1#!/usr/bin/env texlua
2
3if not modules then modules = { } end modules ['mtxrun'] = {
4 version = 1.001,
5 comment = "runner, lua replacement for texmfstart.rb",
6 author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
7 copyright = "PRAGMA ADE / ConTeXt Development Team",
8 license = "see context related readme files"
9}
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62do
63
64package.loaded["l-bit32"] = package.loaded["l-bit32"] or true
65
66
67
68if not modules then modules={} end modules ['l-bit32']={
69 version=1.001,
70 license="the same as regular Lua",
71 source="bitwise.lua, v 1.24 2014/12/26 17:20:53 roberto",
72 comment="drop-in for bit32, adapted a bit by Hans Hagen",
73}
74if bit32 then
75elseif utf8 then
76 load ([[
77local select = select -- instead of: arg = { ... }
78bit32 = {
79 bnot = function (a)
80 return ~a & 0xFFFFFFFF
81 end,
82 band = function (x, y, z, ...)
83 if not z then
84 return ((x or -1) & (y or -1)) & 0xFFFFFFFF
85 else
86 local res = x & y & z
87 for i=1,select("#",...) do
88 res = res & select(i,...)
89 end
90 return res & 0xFFFFFFFF
91 end
92 end,
93 bor = function (x, y, z, ...)
94 if not z then
95 return ((x or 0) | (y or 0)) & 0xFFFFFFFF
96 else
97 local res = x | y | z
98 for i=1,select("#",...) do
99 res = res | select(i,...)
100 end
101 return res & 0xFFFFFFFF
102 end
103 end,
104 bxor = function (x, y, z, ...)
105 if not z then
106 return ((x or 0) ~ (y or 0)) & 0xFFFFFFFF
107 else
108 local res = x ~ y ~ z
109 for i=1,select("#",...) do
110 res = res ~ select(i,...)
111 end
112 return res & 0xFFFFFFFF
113 end
114 end,
115 btest = function (x, y, z, ...)
116 if not z then
117 return (((x or -1) & (y or -1)) & 0xFFFFFFFF) ~= 0
118 else
119 local res = x & y & z
120 for i=1,select("#",...) do
121 res = res & select(i,...)
122 end
123 return (res & 0xFFFFFFFF) ~= 0
124 end
125 end,
126 lshift = function (a, b)
127 return ((a & 0xFFFFFFFF) << b) & 0xFFFFFFFF
128 end,
129 rshift = function (a, b)
130 return ((a & 0xFFFFFFFF) >> b) & 0xFFFFFFFF
131 end,
132 arshift = function (a, b)
133 a = a & 0xFFFFFFFF
134 if b <= 0 or (a & 0x80000000) == 0 then
135 return (a >> b) & 0xFFFFFFFF
136 else
137 return ((a >> b) | ~(0xFFFFFFFF >> b)) & 0xFFFFFFFF
138 end
139 end,
140 lrotate = function (a ,b)
141 b = b & 31
142 a = a & 0xFFFFFFFF
143 a = (a << b) | (a >> (32 - b))
144 return a & 0xFFFFFFFF
145 end,
146 rrotate = function (a, b)
147 b = -b & 31
148 a = a & 0xFFFFFFFF
149 a = (a << b) | (a >> (32 - b))
150 return a & 0xFFFFFFFF
151 end,
152 extract = function (a, f, w)
153 return (a >> f) & ~(-1 << (w or 1))
154 end,
155 replace = function (a, v, f, w)
156 local mask = ~(-1 << (w or 1))
157 return ((a & ~(mask << f)) | ((v & mask) << f)) & 0xFFFFFFFF
158 end,
159}
160 ]] ) ()
161elseif bit then
162 load ([[
163local band, bnot, rshift, lshift = bit.band, bit.bnot, bit.rshift, bit.lshift
164bit32 = {
165 arshift = bit.arshift,
166 band = band,
167 bnot = bnot,
168 bor = bit.bor,
169 bxor = bit.bxor,
170 btest = function(...)
171 return band(...) ~= 0
172 end,
173 extract = function(a,f,w)
174 return band(rshift(a,f),2^(w or 1)-1)
175 end,
176 lrotate = bit.rol,
177 lshift = lshift,
178 replace = function(a,v,f,w)
179 local mask = 2^(w or 1)-1
180 return band(a,bnot(lshift(mask,f)))+lshift(band(v,mask),f)
181 end,
182 rrotate = bit.ror,
183 rshift = rshift,
184}
185 ]] ) ()
186else
187 xpcall(function() local _,t=require("bit32") if t then bit32=t end return end,function() end)
188end
189
190
191end
192
193do
194
195package.loaded["l-lua"] = package.loaded["l-lua"] or true
196
197
198
199if not modules then modules={} end modules ['l-lua']={
200 version=1.001,
201 comment="companion to luat-lib.mkiv",
202 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
203 copyright="PRAGMA ADE / ConTeXt Development Team",
204 license="see context related readme files"
205}
206local next,type,tonumber=next,type,tonumber
207LUAMAJORVERSION,LUAMINORVERSION=string.match(_VERSION,"^[^%d]+(%d+)%.(%d+).*$")
208LUAMAJORVERSION=tonumber(LUAMAJORVERSION) or 5
209LUAMINORVERSION=tonumber(LUAMINORVERSION) or 1
210LUAVERSION=LUAMAJORVERSION+LUAMINORVERSION/10
211if LUAVERSION<5.2 and jit then
212 MINORVERSION=2
213 LUAVERSION=5.2
214end
215if not lpeg then
216 lpeg=require("lpeg")
217end
218if loadstring then
219 local loadnormal=load
220 function load(first,...)
221 if type(first)=="string" then
222 return loadstring(first,...)
223 else
224 return loadnormal(first,...)
225 end
226 end
227else
228 loadstring=load
229end
230if not ipairs then
231 local function iterate(a,i)
232 i=i+1
233 local v=a[i]
234 if v~=nil then
235 return i,v
236 end
237 end
238 function ipairs(a)
239 return iterate,a,0
240 end
241end
242if not pairs then
243 function pairs(t)
244 return next,t
245 end
246end
247if not table.unpack then
248 table.unpack=_G.unpack
249elseif not unpack then
250 _G.unpack=table.unpack
251end
252if not package.loaders then
253 package.loaders=package.searchers
254end
255local print,select,tostring=print,select,tostring
256local inspectors={}
257function setinspector(kind,inspector)
258 inspectors[kind]=inspector
259end
260function inspect(...)
261 for s=1,select("#",...) do
262 local value=select(s,...)
263 if value==nil then
264 print("nil")
265 else
266 local done=false
267 local kind=type(value)
268 local inspector=inspectors[kind]
269 if inspector then
270 done=inspector(value)
271 if done then
272 break
273 end
274 end
275 for kind,inspector in next,inspectors do
276 done=inspector(value)
277 if done then
278 break
279 end
280 end
281 if not done then
282 print(tostring(value))
283 end
284 end
285 end
286end
287local dummy=function() end
288function optionalrequire(...)
289 local ok,result=xpcall(require,dummy,...)
290 if ok then
291 return result
292 end
293end
294local flush=io.flush
295if flush then
296 local execute=os.execute if execute then function os.execute(...) flush() return execute(...) end end
297 local exec=os.exec if exec then function os.exec (...) flush() return exec (...) end end
298 local spawn=os.spawn if spawn then function os.spawn (...) flush() return spawn (...) end end
299 local popen=io.popen if popen then function io.popen (...) flush() return popen (...) end end
300end
301FFISUPPORTED=type(ffi)=="table" and ffi.os~="" and ffi.arch~="" and ffi.load
302if not FFISUPPORTED then
303 local okay;okay,ffi=pcall(require,"ffi")
304 FFISUPPORTED=type(ffi)=="table" and ffi.os~="" and ffi.arch~="" and ffi.load
305end
306if not FFISUPPORTED then
307 ffi=nil
308elseif not ffi.number then
309 ffi.number=tonumber
310end
311if LUAVERSION>5.3 then
312end
313if status and os.setenv then
314 os.setenv("engine",string.lower(status.luatex_engine or "unknown"))
315end
316
317
318end
319
320do
321
322package.loaded["l-macro"] = package.loaded["l-macro"] or true
323
324
325
326if not modules then modules={} end modules ['l-macros']={
327 version=1.001,
328 comment="companion to luat-lib.mkiv",
329 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
330 copyright="PRAGMA ADE / ConTeXt Development Team",
331 license="see context related readme files"
332}
333local S,P,R,V,C,Cs,Cc,Ct,Carg=lpeg.S,lpeg.P,lpeg.R,lpeg.V,lpeg.C,lpeg.Cs,lpeg.Cc,lpeg.Ct,lpeg.Carg
334local lpegmatch=lpeg.match
335local concat=table.concat
336local format,sub,match=string.format,string.sub,string.match
337local next,load,type=next,load,type
338local newline=S("\n\r")^1
339local continue=P("\\")*newline
340local whitespace=S(" \t\n\r")
341local spaces=S(" \t")+continue
342local nametoken=R("az","AZ","__","09")
343local name=nametoken^1
344local body=((continue/""+1)-newline)^1
345local lparent=P("(")
346local rparent=P(")")
347local noparent=1-(lparent+rparent)
348local nested=P { lparent*(noparent+V(1))^0*rparent }
349local escaped=P("\\")*P(1)
350local squote=P("'")
351local dquote=P('"')
352local quoted=dquote*(escaped+(1-dquote))^0*dquote+squote*(escaped+(1-squote))^0*squote
353local arguments=lparent*Ct((Cs((nested+(quoted+1-S("),")))^1)+S(", "))^0)*rparent
354local macros=lua.macros or {}
355lua.macros=macros
356local patterns={}
357local definitions={}
358local resolve
359local subparser
360local report_lua=function(...)
361 if logs and logs.reporter then
362 report_lua=logs.reporter("system","lua")
363 report_lua(...)
364 else
365 print(format(...))
366 end
367end
368local safeguard=P("local")*whitespace^1*name*(whitespace+P("="))
369resolve=safeguard+C(C(name)*(arguments^-1))/function(raw,s,a)
370 local d=definitions[s]
371 if d then
372 if a then
373 local n=#a
374 local p=patterns[s][n]
375 if p then
376 local d=d[n]
377 for i=1,n do
378 a[i]=lpegmatch(subparser,a[i]) or a[i]
379 end
380 return lpegmatch(p,d,1,a) or d
381 else
382 return raw
383 end
384 else
385 return d[0] or raw
386 end
387 elseif a then
388 for i=1,#a do
389 a[i]=lpegmatch(subparser,a[i]) or a[i]
390 end
391 return s.."("..concat(a,",")..")"
392 else
393 return raw
394 end
395end
396subparser=Cs((resolve+P(1))^1)
397local enddefine=P("#enddefine")/""
398local beginregister=(C(name)*(arguments+Cc(false))*C((1-enddefine)^1)*enddefine)/function(k,a,v)
399 local n=0
400 if a then
401 n=#a
402 local pattern=P(false)
403 for i=1,n do
404 pattern=pattern+(P(a[i])*Carg(1))/function(t) return t[i] end
405 end
406 pattern=Cs((pattern+P(1))^1)
407 local p=patterns[k]
408 if not p then
409 p={ [0]=false,false,false,false,false,false,false,false,false }
410 patterns[k]=p
411 end
412 p[n]=pattern
413 end
414 local d=definitions[k]
415 if not d then
416 d={ a=a,[0]=false,false,false,false,false,false,false,false,false }
417 definitions[k]=d
418 end
419 d[n]=lpegmatch(subparser,v) or v
420 return ""
421end
422local register=(Cs(name)*(arguments+Cc(false))*spaces^0*Cs(body))/function(k,a,v)
423 local n=0
424 if a then
425 n=#a
426 local pattern=P(false)
427 for i=1,n do
428 pattern=pattern+(P(a[i])*Carg(1))/function(t) return t[i] end
429 end
430 pattern=Cs((pattern+P(1))^1)
431 local p=patterns[k]
432 if not p then
433 p={ [0]=false,false,false,false,false,false,false,false,false }
434 patterns[k]=p
435 end
436 p[n]=pattern
437 end
438 local d=definitions[k]
439 if not d then
440 d={ a=a,[0]=false,false,false,false,false,false,false,false,false }
441 definitions[k]=d
442 end
443 d[n]=lpegmatch(subparser,v) or v
444 return ""
445end
446local unregister=(C(name)*spaces^0*(arguments+Cc(false)))/function(k,a)
447 local n=0
448 if a then
449 n=#a
450 local p=patterns[k]
451 if p then
452 p[n]=false
453 end
454 end
455 local d=definitions[k]
456 if d then
457 d[n]=false
458 end
459 return ""
460end
461local begindefine=(P("begindefine")*spaces^0/"")*beginregister
462local define=(P("define" )*spaces^0/"")*register
463local undefine=(P("undefine" )*spaces^0/"")*unregister
464local parser=Cs((((P("#")/"")*(define+begindefine+undefine)*(newline^0/"") )+resolve+P(1) )^0 )
465function macros.reset()
466 definitions={}
467 patterns={}
468end
469function macros.showdefinitions()
470 for name,list in table.sortedhash(definitions) do
471 local arguments=list.a
472 if arguments then
473 arguments="("..concat(arguments,",")..")"
474 else
475 arguments=""
476 end
477 print("macro: "..name..arguments)
478 for i=0,#list do
479 local l=list[i]
480 if l then
481 print(" "..l)
482 end
483 end
484 end
485end
486function macros.resolvestring(str)
487 return lpegmatch(parser,str) or str
488end
489function macros.resolving()
490 return next(patterns)
491end
492local function reload(path,name,data)
493 local only=match(name,".-([^/]+)%.lua")
494 if only and only~="" then
495 local name=path.."/"..only
496 local f=io.open(name,"wb")
497 f:write(data)
498 f:close()
499 local f=loadfile(name)
500 os.remove(name)
501 return f
502 end
503end
504local function reload(path,name,data)
505 if path and path~="" then
506 local only=string.match(name,".-([^/]+)%.lua")
507 if only and only~="" then
508 local name=path.."/"..only.."-macro.lua"
509 local f=io.open(name,"wb")
510 if f then
511 f:write(data)
512 f:close()
513 local l=loadfile(name)
514 os.remove(name)
515 return l
516 end
517 end
518 end
519 return load(data,name)
520end
521local function loaded(name,trace,detail)
522 local f=io.open(name,"rb")
523 if not f then
524 return false,format("file '%s' not found",name)
525 end
526 local c=f:read("*a")
527 if not c then
528 return false,format("file '%s' is invalid",name)
529 end
530 f:close()
531 local n=lpegmatch(parser,c)
532 if trace then
533 if #n~=#c then
534 report_lua("macros expanded in '%s' (%i => %i bytes)",name,#c,#n)
535 if detail then
536 report_lua()
537 report_lua(n)
538 report_lua()
539 end
540 elseif detail then
541 report_lua("no macros expanded in '%s'",name)
542 end
543 end
544 return reload(lfs and lfs.currentdir(),name,n)
545end
546macros.loaded=loaded
547function required(name,trace)
548 local filename=file.addsuffix(name,"lua")
549 local fullname=resolvers and resolvers.findfile(filename) or filename
550 if not fullname or fullname=="" then
551 return false
552 end
553 local codeblob=package.loaded[fullname]
554 if codeblob then
555 return codeblob
556 end
557 local code,message=loaded(fullname,macros,trace,trace)
558 if type(code)=="function" then
559 code=code()
560 else
561 report_lua("error when loading '%s'",fullname)
562 return false,message
563 end
564 if code==nil then
565 code=false
566 end
567 package.loaded[fullname]=code
568 return code
569end
570macros.required=required
571
572
573end
574
575do
576
577package.loaded["l-sandbox"] = package.loaded["l-sandbox"] or true
578
579
580
581if not modules then modules={} end modules ['l-sandbox']={
582 version=1.001,
583 comment="companion to luat-lib.mkiv",
584 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
585 copyright="PRAGMA ADE / ConTeXt Development Team",
586 license="see context related readme files"
587}
588local global=_G
589local next=next
590local unpack=unpack or table.unpack
591local type=type
592local tprint=texio and texio.write_nl or print
593local tostring=tostring
594local format=string.format
595local concat=table.concat
596local sort=table.sort
597local gmatch=string.gmatch
598local gsub=string.gsub
599local requiem=require
600sandbox={}
601local sandboxed=false
602local overloads={}
603local skiploads={}
604local initializers={}
605local finalizers={}
606local originals={}
607local comments={}
608local trace=false
609local logger=false
610local blocked={}
611local function report(...)
612 tprint("sandbox ! "..format(...))
613end
614sandbox.report=report
615function sandbox.setreporter(r)
616 report=r
617 sandbox.report=r
618end
619function sandbox.settrace(v)
620 trace=v
621end
622function sandbox.setlogger(l)
623 logger=type(l)=="function" and l or false
624end
625local function register(func,overload,comment)
626 if type(func)=="function" then
627 if type(overload)=="string" then
628 comment=overload
629 overload=nil
630 end
631 local function f(...)
632 if sandboxed then
633 local overload=overloads[f]
634 if overload then
635 if logger then
636 local result={ overload(func,...) }
637 logger {
638 comment=comments[f] or tostring(f),
639 arguments={... },
640 result=result[1] and true or false,
641 }
642 return unpack(result)
643 else
644 return overload(func,...)
645 end
646 else
647 end
648 else
649 return func(...)
650 end
651 end
652 if comment then
653 comments[f]=comment
654 if trace then
655 report("registering function: %s",comment)
656 end
657 end
658 overloads[f]=overload or false
659 originals[f]=func
660 return f
661 end
662end
663local function redefine(func,comment)
664 if type(func)=="function" then
665 skiploads[func]=comment or comments[func] or "unknown"
666 if overloads[func]==false then
667 overloads[func]=nil
668 end
669 end
670end
671sandbox.register=register
672sandbox.redefine=redefine
673function sandbox.original(func)
674 return originals and originals[func] or func
675end
676function sandbox.overload(func,overload,comment)
677 comment=comment or comments[func] or "?"
678 if type(func)~="function" then
679 if trace then
680 report("overloading unknown function: %s",comment)
681 end
682 elseif type(overload)~="function" then
683 if trace then
684 report("overloading function with bad overload: %s",comment)
685 end
686 elseif overloads[func]==nil then
687 if trace then
688 report("function is not registered: %s",comment)
689 end
690 elseif skiploads[func] then
691 if trace then
692 report("function is not skipped: %s",comment)
693 end
694 else
695 if trace then
696 report("overloading function: %s",comment)
697 end
698 overloads[func]=overload
699 end
700 return func
701end
702local function whatever(specification,what,target)
703 if type(specification)~="table" then
704 report("%s needs a specification",what)
705 elseif type(specification.category)~="string" or type(specification.action)~="function" then
706 report("%s needs a category and action",what)
707 elseif not sandboxed then
708 target[#target+1]=specification
709 elseif trace then
710 report("already enabled, discarding %s",what)
711 end
712end
713function sandbox.initializer(specification)
714 whatever(specification,"initializer",initializers)
715end
716function sandbox.finalizer(specification)
717 whatever(specification,"finalizer",finalizers)
718end
719function require(name)
720 local n=gsub(name,"^.*[\\/]","")
721 local n=gsub(n,"[%.].*$","")
722 local b=blocked[n]
723 if b==false then
724 return nil
725 elseif b then
726 if trace then
727 report("using blocked: %s",n)
728 end
729 return b
730 else
731 if trace then
732 report("requiring: %s",name)
733 end
734 return requiem(name)
735 end
736end
737function blockrequire(name,lib)
738 if trace then
739 report("preventing reload of: %s",name)
740 end
741 blocked[name]=lib or _G[name] or false
742end
743function sandbox.enable()
744 if not sandboxed then
745 debug={
746 traceback=debug.traceback,
747 }
748 for i=1,#initializers do
749 initializers[i].action()
750 end
751 for i=1,#finalizers do
752 finalizers[i].action()
753 end
754 local nnot=0
755 local nyes=0
756 local cnot={}
757 local cyes={}
758 local skip={}
759 for k,v in next,overloads do
760 local c=comments[k]
761 if v then
762 if c then
763 cyes[#cyes+1]=c
764 else
765 nyes=nyes+1
766 end
767 else
768 if c then
769 cnot[#cnot+1]=c
770 else
771 nnot=nnot+1
772 end
773 end
774 end
775 for k,v in next,skiploads do
776 skip[#skip+1]=v
777 end
778 if #cyes>0 then
779 sort(cyes)
780 report("overloaded known: %s",concat(cyes," | "))
781 end
782 if nyes>0 then
783 report("overloaded unknown: %s",nyes)
784 end
785 if #cnot>0 then
786 sort(cnot)
787 report("not overloaded known: %s",concat(cnot," | "))
788 end
789 if nnot>0 then
790 report("not overloaded unknown: %s",nnot)
791 end
792 if #skip>0 then
793 sort(skip)
794 report("not overloaded redefined: %s",concat(skip," | "))
795 end
796 initializers=nil
797 finalizers=nil
798 originals=nil
799 sandboxed=true
800 end
801end
802blockrequire("lfs",lfs)
803blockrequire("io",io)
804blockrequire("os",os)
805blockrequire("ffi",ffi)
806local function supported(library)
807 local l=_G[library]
808 return l
809end
810loadfile=register(loadfile,"loadfile")
811if supported("lua") then
812 lua.openfile=register(lua.openfile,"lua.openfile")
813end
814if supported("io") then
815 io.open=register(io.open,"io.open")
816 io.popen=register(io.popen,"io.popen")
817 io.lines=register(io.lines,"io.lines")
818 io.output=register(io.output,"io.output")
819 io.input=register(io.input,"io.input")
820end
821if supported("os") then
822 os.execute=register(os.execute,"os.execute")
823 os.spawn=register(os.spawn,"os.spawn")
824 os.exec=register(os.exec,"os.exec")
825 os.rename=register(os.rename,"os.rename")
826 os.remove=register(os.remove,"os.remove")
827end
828if supported("lfs") then
829 lfs.chdir=register(lfs.chdir,"lfs.chdir")
830 lfs.mkdir=register(lfs.mkdir,"lfs.mkdir")
831 lfs.rmdir=register(lfs.rmdir,"lfs.rmdir")
832 lfs.isfile=register(lfs.isfile,"lfs.isfile")
833 lfs.isdir=register(lfs.isdir,"lfs.isdir")
834 lfs.attributes=register(lfs.attributes,"lfs.attributes")
835 lfs.dir=register(lfs.dir,"lfs.dir")
836 lfs.lock_dir=register(lfs.lock_dir,"lfs.lock_dir")
837 lfs.touch=register(lfs.touch,"lfs.touch")
838 lfs.link=register(lfs.link,"lfs.link")
839 lfs.setmode=register(lfs.setmode,"lfs.setmode")
840 lfs.readlink=register(lfs.readlink,"lfs.readlink")
841 lfs.shortname=register(lfs.shortname,"lfs.shortname")
842 lfs.symlinkattributes=register(lfs.symlinkattributes,"lfs.symlinkattributes")
843end
844
845
846end
847
848do
849
850package.loaded["l-package"] = package.loaded["l-package"] or true
851
852
853
854if not modules then modules={} end modules ['l-package']={
855 version=1.001,
856 comment="companion to luat-lib.mkiv",
857 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
858 copyright="PRAGMA ADE / ConTeXt Development Team",
859 license="see context related readme files"
860}
861local type,unpack=type,unpack
862local gsub,format,find=string.gsub,string.format,string.find
863local insert,remove=table.insert,table.remove
864local P,S,Cs,lpegmatch=lpeg.P,lpeg.S,lpeg.Cs,lpeg.match
865local package=package
866local searchers=package.searchers or package.loaders
867local filejoin=file and file.join or function(path,name) return path.."/"..name end
868local isreadable=file and file.is_readable or function(name) local f=io.open(name) if f then f:close() return true end end
869local addsuffix=file and file.addsuffix or function(name,suffix) return name.."."..suffix end
870local function cleanpath(path)
871 return path
872end
873local pattern=Cs((((1-S("\\/"))^0*(S("\\/")^1/"/"))^0*(P(".")^1/"/"+P(1))^1)*-1)
874local function lualibfile(name)
875 return lpegmatch(pattern,name) or name
876end
877local offset=luarocks and 1 or 0
878local helpers=package.helpers or {
879 cleanpath=cleanpath,
880 lualibfile=lualibfile,
881 trace=false,
882 report=function(...) print(format(...)) end,
883 builtin={
884 ["preload table"]=searchers[1+offset],
885 ["path specification"]=searchers[2+offset],
886 ["cpath specification"]=searchers[3+offset],
887 ["all in one fallback"]=searchers[4+offset],
888 },
889 methods={},
890 sequence={
891 "reset loaded",
892 "already loaded",
893 "preload table",
894 "qualified path",
895 "lua extra list",
896 "lib extra list",
897 "path specification",
898 "cpath specification",
899 "all in one fallback",
900 "not loaded",
901 }
902}
903package.helpers=helpers
904local methods=helpers.methods
905local builtin=helpers.builtin
906local extraluapaths={}
907local extralibpaths={}
908local checkedfiles={}
909local luapaths=nil
910local libpaths=nil
911local oldluapath=nil
912local oldlibpath=nil
913local nofextralua=-1
914local nofextralib=-1
915local nofpathlua=-1
916local nofpathlib=-1
917local function listpaths(what,paths)
918 local nofpaths=#paths
919 if nofpaths>0 then
920 for i=1,nofpaths do
921 helpers.report("using %s path %i: %s",what,i,paths[i])
922 end
923 else
924 helpers.report("no %s paths defined",what)
925 end
926 return nofpaths
927end
928local function getextraluapaths()
929 if helpers.trace and #extraluapaths~=nofextralua then
930 nofextralua=listpaths("extra lua",extraluapaths)
931 end
932 return extraluapaths
933end
934local function getextralibpaths()
935 if helpers.trace and #extralibpaths~=nofextralib then
936 nofextralib=listpaths("extra lib",extralibpaths)
937 end
938 return extralibpaths
939end
940local function getluapaths()
941 local luapath=package.path or ""
942 if oldluapath~=luapath then
943 luapaths=file.splitpath(luapath,";")
944 oldluapath=luapath
945 nofpathlua=-1
946 end
947 if helpers.trace and #luapaths~=nofpathlua then
948 nofpathlua=listpaths("builtin lua",luapaths)
949 end
950 return luapaths
951end
952local function getlibpaths()
953 local libpath=package.cpath or ""
954 if oldlibpath~=libpath then
955 libpaths=file.splitpath(libpath,";")
956 oldlibpath=libpath
957 nofpathlib=-1
958 end
959 if helpers.trace and #libpaths~=nofpathlib then
960 nofpathlib=listpaths("builtin lib",libpaths)
961 end
962 return libpaths
963end
964package.luapaths=getluapaths
965package.libpaths=getlibpaths
966package.extraluapaths=getextraluapaths
967package.extralibpaths=getextralibpaths
968local hashes={
969 lua={},
970 lib={},
971}
972local function registerpath(tag,what,target,...)
973 local pathlist={... }
974 local cleanpath=helpers.cleanpath
975 local trace=helpers.trace
976 local report=helpers.report
977 local hash=hashes[what]
978 local function add(path)
979 local path=cleanpath(path)
980 if not hash[path] then
981 target[#target+1]=path
982 hash[path]=true
983 if trace then
984 report("registered %s path %s: %s",tag,#target,path)
985 end
986 else
987 if trace then
988 report("duplicate %s path: %s",tag,path)
989 end
990 end
991 end
992 for p=1,#pathlist do
993 local path=pathlist[p]
994 if type(path)=="table" then
995 for i=1,#path do
996 add(path[i])
997 end
998 else
999 add(path)
1000 end
1001 end
1002end
1003local function pushpath(tag,what,target,path)
1004 local path=helpers.cleanpath(path)
1005 insert(target,1,path)
1006 if helpers.trace then
1007 helpers.report("pushing %s path in front: %s",tag,path)
1008 end
1009end
1010local function poppath(tag,what,target)
1011 local path=remove(target,1)
1012 if helpers.trace then
1013 if path then
1014 helpers.report("popping %s path from front: %s",tag,path)
1015 else
1016 helpers.report("no %s path to pop",tag)
1017 end
1018 end
1019end
1020helpers.registerpath=registerpath
1021function package.extraluapath(...)
1022 registerpath("extra lua","lua",extraluapaths,...)
1023end
1024function package.pushluapath(path)
1025 pushpath("extra lua","lua",extraluapaths,path)
1026end
1027function package.popluapath()
1028 poppath("extra lua","lua",extraluapaths)
1029end
1030function package.extralibpath(...)
1031 registerpath("extra lib","lib",extralibpaths,...)
1032end
1033function package.pushlibpath(path)
1034 pushpath("extra lib","lib",extralibpaths,path)
1035end
1036function package.poplibpath()
1037 poppath("extra lib","lua",extralibpaths)
1038end
1039local function loadedaslib(resolved,rawname)
1040 local base=gsub(rawname,"%.","_")
1041 local init="luaopen_"..gsub(base,"%.","_")
1042 local data={ resolved,init,"" }
1043 checkedfiles[#checkedfiles+1]=data
1044 if helpers.trace then
1045 helpers.report("calling loadlib with '%s' with init '%s'",resolved,init)
1046 end
1047 local a,b,c=package.loadlib(resolved,init)
1048 if not a and type(b)=="string" then
1049 data[3]=string.fullstrip(b or "unknown error")
1050 end
1051 return a,b,c
1052end
1053helpers.loadedaslib=loadedaslib
1054local function loadedbypath(name,rawname,paths,islib,what)
1055 local trace=helpers.trace
1056 for p=1,#paths do
1057 local path=paths[p]
1058 local resolved=filejoin(path,name)
1059 if trace then
1060 helpers.report("%s path, identifying '%s' on '%s'",what,name,path)
1061 end
1062 if isreadable(resolved) then
1063 if trace then
1064 helpers.report("%s path, '%s' found on '%s'",what,name,resolved)
1065 end
1066 if islib then
1067 return loadedaslib(resolved,rawname)
1068 else
1069 return loadfile(resolved)
1070 end
1071 end
1072 end
1073end
1074helpers.loadedbypath=loadedbypath
1075local function loadedbyname(name,rawname)
1076 if find(name,"^/") or find(name,"^[a-zA-Z]:/") then
1077 local trace=helpers.trace
1078 if trace then
1079 helpers.report("qualified name, identifying '%s'",what,name)
1080 end
1081 if isreadable(name) then
1082 if trace then
1083 helpers.report("qualified name, '%s' found",what,name)
1084 end
1085 return loadfile(name)
1086 end
1087 end
1088end
1089helpers.loadedbyname=loadedbyname
1090methods["reset loaded"]=function(name)
1091 checkedfiles={}
1092 return false
1093end
1094methods["already loaded"]=function(name)
1095 return package.loaded[name]
1096end
1097methods["preload table"]=function(name)
1098 local f=builtin["preload table"]
1099 if f then
1100 return f(name)
1101 end
1102end
1103methods["qualified path"]=function(name)
1104 return loadedbyname(addsuffix(lualibfile(name),"lua"),name)
1105end
1106methods["lua extra list"]=function(name)
1107 return loadedbypath(addsuffix(lualibfile(name),"lua"),name,getextraluapaths(),false,"lua")
1108end
1109methods["lib extra list"]=function(name)
1110 return loadedbypath(addsuffix(lualibfile(name),os.libsuffix),name,getextralibpaths(),true,"lib")
1111end
1112methods["path specification"]=function(name)
1113 local f=builtin["path specification"]
1114 if f then
1115 getluapaths()
1116 return f(name)
1117 end
1118end
1119methods["cpath specification"]=function(name)
1120 local f=builtin["cpath specification"]
1121 if f then
1122 getlibpaths()
1123 return f(name)
1124 end
1125end
1126methods["all in one fallback"]=function(name)
1127 local f=builtin["all in one fallback"]
1128 if f then
1129 return f(name)
1130 end
1131end
1132methods["not loaded"]=function(name)
1133 if helpers.trace then
1134 helpers.report("unable to locate '%s'",name or "?")
1135 for i=1,#checkedfiles do
1136 helpers.report("checked file '%s', initializer '%s', message '%s'",unpack(checkedfiles[i]))
1137 end
1138 end
1139 return nil
1140end
1141local level=0
1142local used={}
1143helpers.traceused=false
1144function helpers.loaded(name)
1145 local sequence=helpers.sequence
1146 level=level+1
1147 for i=1,#sequence do
1148 local method=sequence[i]
1149 local lookup=method and methods[method]
1150 if type(lookup)=="function" then
1151 if helpers.trace then
1152 helpers.report("%s, level '%s', method '%s', name '%s'","locating",level,method,name)
1153 end
1154 local result,rest=lookup(name)
1155 if type(result)=="function" then
1156 if helpers.trace then
1157 helpers.report("%s, level '%s', method '%s', name '%s'","found",level,method,name)
1158 end
1159 if helpers.traceused then
1160 used[#used+1]={ level=level,name=name }
1161 end
1162 level=level-1
1163 return result,rest
1164 end
1165 end
1166 end
1167 level=level-1
1168 return nil
1169end
1170function helpers.showused()
1171 local n=#used
1172 if n>0 then
1173 helpers.report("%s libraries loaded:",n)
1174 helpers.report()
1175 for i=1,n do
1176 local u=used[i]
1177 helpers.report("%i %a",u.level,u.name)
1178 end
1179 helpers.report()
1180 end
1181end
1182function helpers.unload(name)
1183 if helpers.trace then
1184 if package.loaded[name] then
1185 helpers.report("unloading, name '%s', %s",name,"done")
1186 else
1187 helpers.report("unloading, name '%s', %s",name,"not loaded")
1188 end
1189 end
1190 package.loaded[name]=nil
1191end
1192table.insert(searchers,1,helpers.loaded)
1193if context then
1194 package.path=""
1195end
1196
1197
1198end
1199
1200do
1201
1202package.loaded["l-lpeg"] = package.loaded["l-lpeg"] or true
1203
1204
1205
1206if not modules then modules={} end modules ['l-lpeg']={
1207 version=1.001,
1208 comment="companion to luat-lib.mkiv",
1209 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
1210 copyright="PRAGMA ADE / ConTeXt Development Team",
1211 license="see context related readme files"
1212}
1213lpeg=require("lpeg")
1214local lpeg=lpeg
1215if not lpeg.print then function lpeg.print(...) print(lpeg.pcode(...)) end end
1216local type,next,tostring=type,next,tostring
1217local byte,char,gmatch,format=string.byte,string.char,string.gmatch,string.format
1218local floor=math.floor
1219local P,R,S,V,Ct,C,Cs,Cc,Cp,Cmt=lpeg.P,lpeg.R,lpeg.S,lpeg.V,lpeg.Ct,lpeg.C,lpeg.Cs,lpeg.Cc,lpeg.Cp,lpeg.Cmt
1220local lpegtype,lpegmatch,lpegprint=lpeg.type,lpeg.match,lpeg.print
1221if setinspector then
1222 setinspector("lpeg",function(v) if lpegtype(v) then lpegprint(v) return true end end)
1223end
1224lpeg.patterns=lpeg.patterns or {}
1225local patterns=lpeg.patterns
1226local anything=P(1)
1227local endofstring=P(-1)
1228local alwaysmatched=P(true)
1229patterns.anything=anything
1230patterns.endofstring=endofstring
1231patterns.beginofstring=alwaysmatched
1232patterns.alwaysmatched=alwaysmatched
1233local sign=S('+-')
1234local zero=P('0')
1235local digit=R('09')
1236local digits=digit^1
1237local octdigit=R("07")
1238local octdigits=octdigit^1
1239local lowercase=R("az")
1240local uppercase=R("AZ")
1241local underscore=P("_")
1242local hexdigit=digit+lowercase+uppercase
1243local hexdigits=hexdigit^1
1244local cr,lf,crlf=P("\r"),P("\n"),P("\r\n")
1245local newline=P("\r")*(P("\n")+P(true))+P("\n")
1246local escaped=P("\\")*anything
1247local squote=P("'")
1248local dquote=P('"')
1249local space=P(" ")
1250local period=P(".")
1251local comma=P(",")
1252local utfbom_32_be=P('\000\000\254\255')
1253local utfbom_32_le=P('\255\254\000\000')
1254local utfbom_16_be=P('\254\255')
1255local utfbom_16_le=P('\255\254')
1256local utfbom_8=P('\239\187\191')
1257local utfbom=utfbom_32_be+utfbom_32_le+utfbom_16_be+utfbom_16_le+utfbom_8
1258local utftype=utfbom_32_be*Cc("utf-32-be")+utfbom_32_le*Cc("utf-32-le")+utfbom_16_be*Cc("utf-16-be")+utfbom_16_le*Cc("utf-16-le")+utfbom_8*Cc("utf-8")+alwaysmatched*Cc("utf-8")
1259local utfstricttype=utfbom_32_be*Cc("utf-32-be")+utfbom_32_le*Cc("utf-32-le")+utfbom_16_be*Cc("utf-16-be")+utfbom_16_le*Cc("utf-16-le")+utfbom_8*Cc("utf-8")
1260local utfoffset=utfbom_32_be*Cc(4)+utfbom_32_le*Cc(4)+utfbom_16_be*Cc(2)+utfbom_16_le*Cc(2)+utfbom_8*Cc(3)+Cc(0)
1261local utf8next=R("\128\191")
1262patterns.utfbom_32_be=utfbom_32_be
1263patterns.utfbom_32_le=utfbom_32_le
1264patterns.utfbom_16_be=utfbom_16_be
1265patterns.utfbom_16_le=utfbom_16_le
1266patterns.utfbom_8=utfbom_8
1267patterns.utf_16_be_nl=P("\000\r\000\n")+P("\000\r")+P("\000\n")
1268patterns.utf_16_le_nl=P("\r\000\n\000")+P("\r\000")+P("\n\000")
1269patterns.utf_32_be_nl=P("\000\000\000\r\000\000\000\n")+P("\000\000\000\r")+P("\000\000\000\n")
1270patterns.utf_32_le_nl=P("\r\000\000\000\n\000\000\000")+P("\r\000\000\000")+P("\n\000\000\000")
1271patterns.utf8one=R("\000\127")
1272patterns.utf8two=R("\194\223")*utf8next
1273patterns.utf8three=R("\224\239")*utf8next*utf8next
1274patterns.utf8four=R("\240\244")*utf8next*utf8next*utf8next
1275patterns.utfbom=utfbom
1276patterns.utftype=utftype
1277patterns.utfstricttype=utfstricttype
1278patterns.utfoffset=utfoffset
1279local utf8char=patterns.utf8one+patterns.utf8two+patterns.utf8three+patterns.utf8four
1280local validutf8char=utf8char^0*endofstring*Cc(true)+Cc(false)
1281local utf8character=P(1)*R("\128\191")^0
1282patterns.utf8=utf8char
1283patterns.utf8char=utf8char
1284patterns.utf8character=utf8character
1285patterns.validutf8=validutf8char
1286patterns.validutf8char=validutf8char
1287local eol=S("\n\r")
1288local spacer=S(" \t\f\v")
1289local whitespace=eol+spacer
1290local nonspacer=1-spacer
1291local nonwhitespace=1-whitespace
1292patterns.eol=eol
1293patterns.spacer=spacer
1294patterns.whitespace=whitespace
1295patterns.nonspacer=nonspacer
1296patterns.nonwhitespace=nonwhitespace
1297local stripper=spacer^0*C((spacer^0*nonspacer^1)^0)
1298local fullstripper=whitespace^0*C((whitespace^0*nonwhitespace^1)^0)
1299local collapser=Cs(spacer^0/""*nonspacer^0*((spacer^0/" "*nonspacer^1)^0))
1300local nospacer=Cs((whitespace^1/""+nonwhitespace^1)^0)
1301local b_collapser=Cs(whitespace^0/""*(nonwhitespace^1+whitespace^1/" ")^0)
1302local m_collapser=Cs((nonwhitespace^1+whitespace^1/" ")^0)
1303local e_collapser=Cs((whitespace^1*endofstring/""+nonwhitespace^1+whitespace^1/" ")^0)
1304local x_collapser=Cs((nonwhitespace^1+whitespace^1/"" )^0)
1305local b_stripper=Cs(spacer^0/""*(nonspacer^1+spacer^1/" ")^0)
1306local m_stripper=Cs((nonspacer^1+spacer^1/" ")^0)
1307local e_stripper=Cs((spacer^1*endofstring/""+nonspacer^1+spacer^1/" ")^0)
1308local x_stripper=Cs((nonspacer^1+spacer^1/"" )^0)
1309patterns.stripper=stripper
1310patterns.fullstripper=fullstripper
1311patterns.collapser=collapser
1312patterns.nospacer=nospacer
1313patterns.b_collapser=b_collapser
1314patterns.m_collapser=m_collapser
1315patterns.e_collapser=e_collapser
1316patterns.x_collapser=x_collapser
1317patterns.b_stripper=b_stripper
1318patterns.m_stripper=m_stripper
1319patterns.e_stripper=e_stripper
1320patterns.x_stripper=x_stripper
1321patterns.lowercase=lowercase
1322patterns.uppercase=uppercase
1323patterns.letter=patterns.lowercase+patterns.uppercase
1324patterns.space=space
1325patterns.tab=P("\t")
1326patterns.spaceortab=patterns.space+patterns.tab
1327patterns.newline=newline
1328patterns.emptyline=newline^1
1329patterns.equal=P("=")
1330patterns.comma=comma
1331patterns.commaspacer=comma*spacer^0
1332patterns.period=period
1333patterns.colon=P(":")
1334patterns.semicolon=P(";")
1335patterns.underscore=underscore
1336patterns.escaped=escaped
1337patterns.squote=squote
1338patterns.dquote=dquote
1339patterns.nosquote=(escaped+(1-squote))^0
1340patterns.nodquote=(escaped+(1-dquote))^0
1341patterns.unsingle=(squote/"")*patterns.nosquote*(squote/"")
1342patterns.undouble=(dquote/"")*patterns.nodquote*(dquote/"")
1343patterns.unquoted=patterns.undouble+patterns.unsingle
1344patterns.unspacer=((patterns.spacer^1)/"")^0
1345patterns.singlequoted=squote*patterns.nosquote*squote
1346patterns.doublequoted=dquote*patterns.nodquote*dquote
1347patterns.quoted=patterns.doublequoted+patterns.singlequoted
1348patterns.digit=digit
1349patterns.digits=digits
1350patterns.octdigit=octdigit
1351patterns.octdigits=octdigits
1352patterns.hexdigit=hexdigit
1353patterns.hexdigits=hexdigits
1354patterns.sign=sign
1355patterns.cardinal=digits
1356patterns.integer=sign^-1*digits
1357patterns.unsigned=digit^0*period*digits
1358patterns.float=sign^-1*patterns.unsigned
1359patterns.cunsigned=digit^0*comma*digits
1360patterns.cpunsigned=digit^0*(period+comma)*digits
1361patterns.cfloat=sign^-1*patterns.cunsigned
1362patterns.cpfloat=sign^-1*patterns.cpunsigned
1363patterns.number=patterns.float+patterns.integer
1364patterns.cnumber=patterns.cfloat+patterns.integer
1365patterns.cpnumber=patterns.cpfloat+patterns.integer
1366patterns.oct=zero*octdigits
1367patterns.octal=patterns.oct
1368patterns.HEX=zero*P("X")*(digit+uppercase)^1
1369patterns.hex=zero*P("x")*(digit+lowercase)^1
1370patterns.hexadecimal=zero*S("xX")*hexdigits
1371patterns.hexafloat=sign^-1*zero*S("xX")*(hexdigit^0*period*hexdigits+hexdigits*period*hexdigit^0+hexdigits)*(S("pP")*sign^-1*hexdigits)^-1
1372patterns.decafloat=sign^-1*(digit^0*period*digits+digits*period*digit^0+digits)*S("eE")*sign^-1*digits
1373patterns.propername=(uppercase+lowercase+underscore)*(uppercase+lowercase+underscore+digit)^0*endofstring
1374patterns.somecontent=(anything-newline-space)^1
1375patterns.beginline=#(1-newline)
1376patterns.longtostring=Cs(whitespace^0/""*((patterns.quoted+nonwhitespace^1+whitespace^1/""*(endofstring+Cc(" ")))^0))
1377local function anywhere(pattern)
1378 return (1-P(pattern))^0*P(pattern)
1379end
1380lpeg.anywhere=anywhere
1381function lpeg.instringchecker(p)
1382 p=anywhere(p)
1383 return function(str)
1384 return lpegmatch(p,str) and true or false
1385 end
1386end
1387function lpeg.splitter(pattern,action)
1388 if action then
1389 return (((1-P(pattern))^1)/action+1)^0
1390 else
1391 return (Cs((1-P(pattern))^1)+1)^0
1392 end
1393end
1394function lpeg.tsplitter(pattern,action)
1395 if action then
1396 return Ct((((1-P(pattern))^1)/action+1)^0)
1397 else
1398 return Ct((Cs((1-P(pattern))^1)+1)^0)
1399 end
1400end
1401local splitters_s,splitters_m,splitters_t={},{},{}
1402local function splitat(separator,single)
1403 local splitter=(single and splitters_s[separator]) or splitters_m[separator]
1404 if not splitter then
1405 separator=P(separator)
1406 local other=C((1-separator)^0)
1407 if single then
1408 local any=anything
1409 splitter=other*(separator*C(any^0)+"")
1410 splitters_s[separator]=splitter
1411 else
1412 splitter=other*(separator*other)^0
1413 splitters_m[separator]=splitter
1414 end
1415 end
1416 return splitter
1417end
1418local function tsplitat(separator)
1419 local splitter=splitters_t[separator]
1420 if not splitter then
1421 splitter=Ct(splitat(separator))
1422 splitters_t[separator]=splitter
1423 end
1424 return splitter
1425end
1426lpeg.splitat=splitat
1427lpeg.tsplitat=tsplitat
1428function string.splitup(str,separator)
1429 if not separator then
1430 separator=","
1431 end
1432 return lpegmatch(splitters_m[separator] or splitat(separator),str)
1433end
1434local cache={}
1435function lpeg.split(separator,str)
1436 local c=cache[separator]
1437 if not c then
1438 c=tsplitat(separator)
1439 cache[separator]=c
1440 end
1441 return lpegmatch(c,str)
1442end
1443function string.split(str,separator)
1444 if separator then
1445 local c=cache[separator]
1446 if not c then
1447 c=tsplitat(separator)
1448 cache[separator]=c
1449 end
1450 return lpegmatch(c,str)
1451 else
1452 return { str }
1453 end
1454end
1455local spacing=patterns.spacer^0*newline
1456local empty=spacing*Cc("")
1457local nonempty=Cs((1-spacing)^1)*spacing^-1
1458local content=(empty+nonempty)^1
1459patterns.textline=content
1460local linesplitter=tsplitat(newline)
1461patterns.linesplitter=linesplitter
1462function string.splitlines(str)
1463 return lpegmatch(linesplitter,str)
1464end
1465local cache={}
1466function lpeg.checkedsplit(separator,str)
1467 local c=cache[separator]
1468 if not c then
1469 separator=P(separator)
1470 local other=C((1-separator)^1)
1471 c=Ct(separator^0*other*(separator^1*other)^0)
1472 cache[separator]=c
1473 end
1474 return lpegmatch(c,str)
1475end
1476function string.checkedsplit(str,separator)
1477 local c=cache[separator]
1478 if not c then
1479 separator=P(separator)
1480 local other=C((1-separator)^1)
1481 c=Ct(separator^0*other*(separator^1*other)^0)
1482 cache[separator]=c
1483 end
1484 return lpegmatch(c,str)
1485end
1486local function f2(s) local c1,c2=byte(s,1,2) return c1*64+c2-12416 end
1487local function f3(s) local c1,c2,c3=byte(s,1,3) return (c1*64+c2)*64+c3-925824 end
1488local function f4(s) local c1,c2,c3,c4=byte(s,1,4) return ((c1*64+c2)*64+c3)*64+c4-63447168 end
1489local utf8byte=patterns.utf8one/byte+patterns.utf8two/f2+patterns.utf8three/f3+patterns.utf8four/f4
1490patterns.utf8byte=utf8byte
1491local cache={}
1492function lpeg.stripper(str)
1493 if type(str)=="string" then
1494 local s=cache[str]
1495 if not s then
1496 s=Cs(((S(str)^1)/""+1)^0)
1497 cache[str]=s
1498 end
1499 return s
1500 else
1501 return Cs(((str^1)/""+1)^0)
1502 end
1503end
1504local cache={}
1505function lpeg.keeper(str)
1506 if type(str)=="string" then
1507 local s=cache[str]
1508 if not s then
1509 s=Cs((((1-S(str))^1)/""+1)^0)
1510 cache[str]=s
1511 end
1512 return s
1513 else
1514 return Cs((((1-str)^1)/""+1)^0)
1515 end
1516end
1517function lpeg.frontstripper(str)
1518 return (P(str)+P(true))*Cs(anything^0)
1519end
1520function lpeg.endstripper(str)
1521 return Cs((1-P(str)*endofstring)^0)
1522end
1523function lpeg.replacer(one,two,makefunction,isutf)
1524 local pattern
1525 local u=isutf and utf8char or 1
1526 if type(one)=="table" then
1527 local no=#one
1528 local p=P(false)
1529 if no==0 then
1530 for k,v in next,one do
1531 p=p+P(k)/v
1532 end
1533 pattern=Cs((p+u)^0)
1534 elseif no==1 then
1535 local o=one[1]
1536 one,two=P(o[1]),o[2]
1537 pattern=Cs((one/two+u)^0)
1538 else
1539 for i=1,no do
1540 local o=one[i]
1541 p=p+P(o[1])/o[2]
1542 end
1543 pattern=Cs((p+u)^0)
1544 end
1545 else
1546 pattern=Cs((P(one)/(two or "")+u)^0)
1547 end
1548 if makefunction then
1549 return function(str)
1550 return lpegmatch(pattern,str)
1551 end
1552 else
1553 return pattern
1554 end
1555end
1556function lpeg.finder(lst,makefunction,isutf)
1557 local pattern
1558 if type(lst)=="table" then
1559 pattern=P(false)
1560 if #lst==0 then
1561 for k,v in next,lst do
1562 pattern=pattern+P(k)
1563 end
1564 else
1565 for i=1,#lst do
1566 pattern=pattern+P(lst[i])
1567 end
1568 end
1569 else
1570 pattern=P(lst)
1571 end
1572 if isutf then
1573 pattern=((utf8char or 1)-pattern)^0*pattern
1574 else
1575 pattern=(1-pattern)^0*pattern
1576 end
1577 if makefunction then
1578 return function(str)
1579 return lpegmatch(pattern,str)
1580 end
1581 else
1582 return pattern
1583 end
1584end
1585local splitters_f,splitters_s={},{}
1586function lpeg.firstofsplit(separator)
1587 local splitter=splitters_f[separator]
1588 if not splitter then
1589 local pattern=P(separator)
1590 splitter=C((1-pattern)^0)
1591 splitters_f[separator]=splitter
1592 end
1593 return splitter
1594end
1595function lpeg.secondofsplit(separator)
1596 local splitter=splitters_s[separator]
1597 if not splitter then
1598 local pattern=P(separator)
1599 splitter=(1-pattern)^0*pattern*C(anything^0)
1600 splitters_s[separator]=splitter
1601 end
1602 return splitter
1603end
1604local splitters_s,splitters_p={},{}
1605function lpeg.beforesuffix(separator)
1606 local splitter=splitters_s[separator]
1607 if not splitter then
1608 local pattern=P(separator)
1609 splitter=C((1-pattern)^0)*pattern*endofstring
1610 splitters_s[separator]=splitter
1611 end
1612 return splitter
1613end
1614function lpeg.afterprefix(separator)
1615 local splitter=splitters_p[separator]
1616 if not splitter then
1617 local pattern=P(separator)
1618 splitter=pattern*C(anything^0)
1619 splitters_p[separator]=splitter
1620 end
1621 return splitter
1622end
1623function lpeg.balancer(left,right)
1624 left,right=P(left),P(right)
1625 return P { left*((1-left-right)+V(1))^0*right }
1626end
1627function lpeg.counter(pattern,action)
1628 local n=0
1629 local pattern=(P(pattern)/function() n=n+1 end+anything)^0
1630 if action then
1631 return function(str) n=0;lpegmatch(pattern,str);action(n) end
1632 else
1633 return function(str) n=0;lpegmatch(pattern,str);return n end
1634 end
1635end
1636function lpeg.is_lpeg(p)
1637 return p and lpegtype(p)=="pattern"
1638end
1639function lpeg.oneof(list,...)
1640 if type(list)~="table" then
1641 list={ list,... }
1642 end
1643 local p=P(list[1])
1644 for l=2,#list do
1645 p=p+P(list[l])
1646 end
1647 return p
1648end
1649local sort=table.sort
1650local function copyindexed(old)
1651 local new={}
1652 for i=1,#old do
1653 new[i]=old
1654 end
1655 return new
1656end
1657local function sortedkeys(tab)
1658 local keys,s={},0
1659 for key,_ in next,tab do
1660 s=s+1
1661 keys[s]=key
1662 end
1663 sort(keys)
1664 return keys
1665end
1666function lpeg.append(list,pp,delayed,checked)
1667 local p=pp
1668 if #list>0 then
1669 local keys=copyindexed(list)
1670 sort(keys)
1671 for i=#keys,1,-1 do
1672 local k=keys[i]
1673 if p then
1674 p=P(k)+p
1675 else
1676 p=P(k)
1677 end
1678 end
1679 elseif delayed then
1680 local keys=sortedkeys(list)
1681 if p then
1682 for i=1,#keys,1 do
1683 local k=keys[i]
1684 local v=list[k]
1685 p=P(k)/list+p
1686 end
1687 else
1688 for i=1,#keys do
1689 local k=keys[i]
1690 local v=list[k]
1691 if p then
1692 p=P(k)+p
1693 else
1694 p=P(k)
1695 end
1696 end
1697 if p then
1698 p=p/list
1699 end
1700 end
1701 elseif checked then
1702 local keys=sortedkeys(list)
1703 for i=1,#keys do
1704 local k=keys[i]
1705 local v=list[k]
1706 if p then
1707 if k==v then
1708 p=P(k)+p
1709 else
1710 p=P(k)/v+p
1711 end
1712 else
1713 if k==v then
1714 p=P(k)
1715 else
1716 p=P(k)/v
1717 end
1718 end
1719 end
1720 else
1721 local keys=sortedkeys(list)
1722 for i=1,#keys do
1723 local k=keys[i]
1724 local v=list[k]
1725 if p then
1726 p=P(k)/v+p
1727 else
1728 p=P(k)/v
1729 end
1730 end
1731 end
1732 return p
1733end
1734local p_false=P(false)
1735local p_true=P(true)
1736local lower=utf and utf.lower or string.lower
1737local upper=utf and utf.upper or string.upper
1738function lpeg.setutfcasers(l,u)
1739 lower=l or lower
1740 upper=u or upper
1741end
1742local function make1(t,rest)
1743 local p=p_false
1744 local keys=sortedkeys(t)
1745 for i=1,#keys do
1746 local k=keys[i]
1747 if k~="" then
1748 local v=t[k]
1749 if v==true then
1750 p=p+P(k)*p_true
1751 elseif v==false then
1752 else
1753 p=p+P(k)*make1(v,v[""])
1754 end
1755 end
1756 end
1757 if rest then
1758 p=p+p_true
1759 end
1760 return p
1761end
1762local function make2(t,rest)
1763 local p=p_false
1764 local keys=sortedkeys(t)
1765 for i=1,#keys do
1766 local k=keys[i]
1767 if k~="" then
1768 local v=t[k]
1769 if v==true then
1770 p=p+(P(lower(k))+P(upper(k)))*p_true
1771 elseif v==false then
1772 else
1773 p=p+(P(lower(k))+P(upper(k)))*make2(v,v[""])
1774 end
1775 end
1776 end
1777 if rest then
1778 p=p+p_true
1779 end
1780 return p
1781end
1782local function utfchartabletopattern(list,insensitive)
1783 local tree={}
1784 local n=#list
1785 if n==0 then
1786 for s in next,list do
1787 local t=tree
1788 local p,pk
1789 for c in gmatch(s,".") do
1790 if t==true then
1791 t={ [c]=true,[""]=true }
1792 p[pk]=t
1793 p=t
1794 t=false
1795 elseif t==false then
1796 t={ [c]=false }
1797 p[pk]=t
1798 p=t
1799 t=false
1800 else
1801 local tc=t[c]
1802 if not tc then
1803 tc=false
1804 t[c]=false
1805 end
1806 p=t
1807 t=tc
1808 end
1809 pk=c
1810 end
1811 if t==false then
1812 p[pk]=true
1813 elseif t==true then
1814 else
1815 t[""]=true
1816 end
1817 end
1818 else
1819 for i=1,n do
1820 local s=list[i]
1821 local t=tree
1822 local p,pk
1823 for c in gmatch(s,".") do
1824 if t==true then
1825 t={ [c]=true,[""]=true }
1826 p[pk]=t
1827 p=t
1828 t=false
1829 elseif t==false then
1830 t={ [c]=false }
1831 p[pk]=t
1832 p=t
1833 t=false
1834 else
1835 local tc=t[c]
1836 if not tc then
1837 tc=false
1838 t[c]=false
1839 end
1840 p=t
1841 t=tc
1842 end
1843 pk=c
1844 end
1845 if t==false then
1846 p[pk]=true
1847 elseif t==true then
1848 else
1849 t[""]=true
1850 end
1851 end
1852 end
1853 return (insensitive and make2 or make1)(tree)
1854end
1855lpeg.utfchartabletopattern=utfchartabletopattern
1856function lpeg.utfreplacer(list,insensitive)
1857 local pattern=Cs((utfchartabletopattern(list,insensitive)/list+utf8character)^0)
1858 return function(str)
1859 return lpegmatch(pattern,str) or str
1860 end
1861end
1862patterns.containseol=lpeg.finder(eol)
1863local function nextstep(n,step,result)
1864 local m=n%step
1865 local d=floor(n/step)
1866 if d>0 then
1867 local v=V(tostring(step))
1868 local s=result.start
1869 for i=1,d do
1870 if s then
1871 s=v*s
1872 else
1873 s=v
1874 end
1875 end
1876 result.start=s
1877 end
1878 if step>1 and result.start then
1879 local v=V(tostring(step/2))
1880 result[tostring(step)]=v*v
1881 end
1882 if step>0 then
1883 return nextstep(m,step/2,result)
1884 else
1885 return result
1886 end
1887end
1888function lpeg.times(pattern,n)
1889 return P(nextstep(n,2^16,{ "start",["1"]=pattern }))
1890end
1891do
1892 local trailingzeros=zero^0*-digit
1893 local stripper=Cs((
1894 digits*(
1895 period*trailingzeros/""+period*(digit-trailingzeros)^1*(trailingzeros/"")
1896 )+1
1897 )^0)
1898 lpeg.patterns.stripzeros=stripper
1899 local nonzero=digit-zero
1900 local trailingzeros=zero^1*endofstring
1901 local stripper=Cs((1-period)^0*(
1902 period*trailingzeros/""+period*(nonzero^1+(trailingzeros/"")+zero^1)^0+endofstring
1903 ))
1904 lpeg.patterns.stripzero=stripper
1905end
1906local byte_to_HEX={}
1907local byte_to_hex={}
1908local byte_to_dec={}
1909local hex_to_byte={}
1910for i=0,255 do
1911 local H=format("%02X",i)
1912 local h=format("%02x",i)
1913 local d=format("%03i",i)
1914 local c=char(i)
1915 byte_to_HEX[c]=H
1916 byte_to_hex[c]=h
1917 byte_to_dec[c]=d
1918 hex_to_byte[h]=c
1919 hex_to_byte[H]=c
1920end
1921local hextobyte=P(2)/hex_to_byte
1922local bytetoHEX=P(1)/byte_to_HEX
1923local bytetohex=P(1)/byte_to_hex
1924local bytetodec=P(1)/byte_to_dec
1925local hextobytes=Cs(hextobyte^0)
1926local bytestoHEX=Cs(bytetoHEX^0)
1927local bytestohex=Cs(bytetohex^0)
1928local bytestodec=Cs(bytetodec^0)
1929patterns.hextobyte=hextobyte
1930patterns.bytetoHEX=bytetoHEX
1931patterns.bytetohex=bytetohex
1932patterns.bytetodec=bytetodec
1933patterns.hextobytes=hextobytes
1934patterns.bytestoHEX=bytestoHEX
1935patterns.bytestohex=bytestohex
1936patterns.bytestodec=bytestodec
1937function string.toHEX(s)
1938 if not s or s=="" then
1939 return s
1940 else
1941 return lpegmatch(bytestoHEX,s)
1942 end
1943end
1944function string.tohex(s)
1945 if not s or s=="" then
1946 return s
1947 else
1948 return lpegmatch(bytestohex,s)
1949 end
1950end
1951function string.todec(s)
1952 if not s or s=="" then
1953 return s
1954 else
1955 return lpegmatch(bytestodec,s)
1956 end
1957end
1958function string.tobytes(s)
1959 if not s or s=="" then
1960 return s
1961 else
1962 return lpegmatch(hextobytes,s)
1963 end
1964end
1965local patterns={}
1966local function containsws(what)
1967 local p=patterns[what]
1968 if not p then
1969 local p1=P(what)*(whitespace+endofstring)*Cc(true)
1970 local p2=whitespace*P(p1)
1971 p=P(p1)+P(1-p2)^0*p2+Cc(false)
1972 patterns[what]=p
1973 end
1974 return p
1975end
1976lpeg.containsws=containsws
1977function string.containsws(str,what)
1978 return lpegmatch(patterns[what] or containsws(what),str)
1979end
1980
1981
1982end
1983
1984do
1985
1986package.loaded["l-function"] = package.loaded["l-function"] or true
1987
1988
1989
1990if not modules then modules={} end modules ['l-functions']={
1991 version=1.001,
1992 comment="companion to luat-lib.mkiv",
1993 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
1994 copyright="PRAGMA ADE / ConTeXt Development Team",
1995 license="see context related readme files"
1996}
1997functions=functions or {}
1998function functions.dummy() end
1999
2000
2001end
2002
2003do
2004
2005package.loaded["l-string"] = package.loaded["l-string"] or true
2006
2007
2008
2009if not modules then modules={} end modules ['l-string']={
2010 version=1.001,
2011 comment="companion to luat-lib.mkiv",
2012 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
2013 copyright="PRAGMA ADE / ConTeXt Development Team",
2014 license="see context related readme files"
2015}
2016local string=string
2017local sub,gmatch,format,char,byte,rep,lower,find=string.sub,string.gmatch,string.format,string.char,string.byte,string.rep,string.lower,string.find
2018local lpegmatch,patterns=lpeg.match,lpeg.patterns
2019local P,S,C,Ct,Cc,Cs=lpeg.P,lpeg.S,lpeg.C,lpeg.Ct,lpeg.Cc,lpeg.Cs
2020local unquoted=patterns.squote*C(patterns.nosquote)*patterns.squote+patterns.dquote*C(patterns.nodquote)*patterns.dquote
2021function string.unquoted(str)
2022 return lpegmatch(unquoted,str) or str
2023end
2024function string.quoted(str)
2025 return format("%q",str)
2026end
2027function string.count(str,pattern)
2028 local n=0
2029 local i=1
2030 local l=#pattern
2031 while true do
2032 i=find(str,pattern,i)
2033 if i then
2034 n=n+1
2035 i=i+l
2036 else
2037 break
2038 end
2039 end
2040 return n
2041end
2042function string.limit(str,n,sentinel)
2043 if #str>n then
2044 sentinel=sentinel or "..."
2045 return sub(str,1,(n-#sentinel))..sentinel
2046 else
2047 return str
2048 end
2049end
2050local stripper=patterns.stripper
2051local fullstripper=patterns.fullstripper
2052local collapser=patterns.collapser
2053local nospacer=patterns.nospacer
2054local longtostring=patterns.longtostring
2055function string.strip(str)
2056 return str and lpegmatch(stripper,str) or ""
2057end
2058function string.fullstrip(str)
2059 return str and lpegmatch(fullstripper,str) or ""
2060end
2061function string.collapsespaces(str)
2062 return str and lpegmatch(collapser,str) or ""
2063end
2064function string.nospaces(str)
2065 return str and lpegmatch(nospacer,str) or ""
2066end
2067function string.longtostring(str)
2068 return str and lpegmatch(longtostring,str) or ""
2069end
2070local pattern=P(" ")^0*P(-1)
2071function string.is_empty(str)
2072 if not str or str=="" then
2073 return true
2074 else
2075 return lpegmatch(pattern,str) and true or false
2076 end
2077end
2078local anything=patterns.anything
2079local moreescapes=Cc("%")*S(".-+%?()[]*$^{}")
2080local allescapes=Cc("%")*S(".-+%?()[]*")
2081local someescapes=Cc("%")*S(".-+%()[]")
2082local matchescapes=Cc(".")*S("*?")
2083local pattern_m=Cs ((moreescapes+anything )^0 )
2084local pattern_a=Cs ((allescapes+anything )^0 )
2085local pattern_b=Cs ((someescapes+matchescapes+anything )^0 )
2086local pattern_c=Cs (Cc("^")*(someescapes+matchescapes+anything )^0*Cc("$") )
2087function string.escapedpattern(str,simple)
2088 return lpegmatch(simple and pattern_b or pattern_a,str)
2089end
2090function string.topattern(str,lowercase,strict)
2091 if str=="" or type(str)~="string" then
2092 return ".*"
2093 elseif strict=="all" then
2094 str=lpegmatch(pattern_m,str)
2095 elseif strict then
2096 str=lpegmatch(pattern_c,str)
2097 else
2098 str=lpegmatch(pattern_b,str)
2099 end
2100 if lowercase then
2101 return lower(str)
2102 else
2103 return str
2104 end
2105end
2106function string.valid(str,default)
2107 return (type(str)=="string" and str~="" and str) or default or nil
2108end
2109string.itself=function(s) return s end
2110local pattern_c=Ct(C(1)^0)
2111local pattern_b=Ct((C(1)/byte)^0)
2112function string.totable(str,bytes)
2113 return lpegmatch(bytes and pattern_b or pattern_c,str)
2114end
2115local replacer=lpeg.replacer("@","%%")
2116function string.tformat(fmt,...)
2117 return format(lpegmatch(replacer,fmt),...)
2118end
2119string.quote=string.quoted
2120string.unquote=string.unquoted
2121if not string.bytetable then
2122 local limit=5000
2123 function string.bytetable(str)
2124 local n=#str
2125 if n>limit then
2126 local t={ byte(str,1,limit) }
2127 for i=limit+1,n do
2128 t[i]=byte(str,i)
2129 end
2130 return t
2131 else
2132 return { byte(str,1,n) }
2133 end
2134 end
2135end
2136
2137
2138end
2139
2140do
2141
2142package.loaded["l-table"] = package.loaded["l-table"] or true
2143
2144
2145
2146if not modules then modules={} end modules ['l-table']={
2147 version=1.001,
2148 comment="companion to luat-lib.mkiv",
2149 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
2150 copyright="PRAGMA ADE / ConTeXt Development Team",
2151 license="see context related readme files"
2152}
2153local type,next,tostring,tonumber,select,rawget=type,next,tostring,tonumber,select,rawget
2154local table,string=table,string
2155local concat,sort=table.concat,table.sort
2156local format,lower,dump=string.format,string.lower,string.dump
2157local getmetatable,setmetatable=getmetatable,setmetatable
2158local lpegmatch,patterns=lpeg.match,lpeg.patterns
2159local floor=math.floor
2160local stripper=patterns.stripper
2161function table.getn(t)
2162 return t and #t
2163end
2164function table.strip(tab)
2165 local lst={}
2166 local l=0
2167 for i=1,#tab do
2168 local s=lpegmatch(stripper,tab[i]) or ""
2169 if s=="" then
2170 else
2171 l=l+1
2172 lst[l]=s
2173 end
2174 end
2175 return lst
2176end
2177function table.keys(t)
2178 if t then
2179 local keys={}
2180 local k=0
2181 for key in next,t do
2182 k=k+1
2183 keys[k]=key
2184 end
2185 return keys
2186 else
2187 return {}
2188 end
2189end
2190local function compare(a,b)
2191 local ta=type(a)
2192 if ta=="number" then
2193 local tb=type(b)
2194 if ta==tb then
2195 return a<b
2196 elseif tb=="string" then
2197 return tostring(a)<b
2198 end
2199 elseif ta=="string" then
2200 local tb=type(b)
2201 if ta==tb then
2202 return a<b
2203 else
2204 return a<tostring(b)
2205 end
2206 end
2207 return tostring(a)<tostring(b)
2208end
2209local function sortedkeys(tab)
2210 if tab then
2211 local srt={}
2212 local category=0
2213 local s=0
2214 for key in next,tab do
2215 s=s+1
2216 srt[s]=key
2217 if category~=3 then
2218 local tkey=type(key)
2219 if category==1 then
2220 if tkey~="string" then
2221 category=3
2222 end
2223 elseif category==2 then
2224 if tkey~="number" then
2225 category=3
2226 end
2227 else
2228 if tkey=="string" then
2229 category=1
2230 elseif tkey=="number" then
2231 category=2
2232 else
2233 category=3
2234 end
2235 end
2236 end
2237 end
2238 if s<2 then
2239 elseif category==3 then
2240 sort(srt,compare)
2241 else
2242 sort(srt)
2243 end
2244 return srt
2245 else
2246 return {}
2247 end
2248end
2249local function sortedhashonly(tab)
2250 if tab then
2251 local srt={}
2252 local s=0
2253 for key in next,tab do
2254 if type(key)=="string" then
2255 s=s+1
2256 srt[s]=key
2257 end
2258 end
2259 if s>1 then
2260 sort(srt)
2261 end
2262 return srt
2263 else
2264 return {}
2265 end
2266end
2267local function sortedindexonly(tab)
2268 if tab then
2269 local srt={}
2270 local s=0
2271 for key in next,tab do
2272 if type(key)=="number" then
2273 s=s+1
2274 srt[s]=key
2275 end
2276 end
2277 if s>1 then
2278 sort(srt)
2279 end
2280 return srt
2281 else
2282 return {}
2283 end
2284end
2285local function sortedhashkeys(tab,cmp)
2286 if tab then
2287 local srt={}
2288 local s=0
2289 for key in next,tab do
2290 if key then
2291 s=s+1
2292 srt[s]=key
2293 end
2294 end
2295 if s>1 then
2296 sort(srt,cmp)
2297 end
2298 return srt
2299 else
2300 return {}
2301 end
2302end
2303function table.allkeys(t)
2304 local keys={}
2305 for k,v in next,t do
2306 for k in next,v do
2307 keys[k]=true
2308 end
2309 end
2310 return sortedkeys(keys)
2311end
2312table.sortedkeys=sortedkeys
2313table.sortedhashonly=sortedhashonly
2314table.sortedindexonly=sortedindexonly
2315table.sortedhashkeys=sortedhashkeys
2316local function nothing() end
2317local function sortedhash(t,cmp)
2318 if t then
2319 local s
2320 if cmp then
2321 s=sortedhashkeys(t,function(a,b) return cmp(t,a,b) end)
2322 else
2323 s=sortedkeys(t)
2324 end
2325 local m=#s
2326 if m==1 then
2327 return next,t
2328 elseif m>0 then
2329 local n=0
2330 return function()
2331 if n<m then
2332 n=n+1
2333 local k=s[n]
2334 return k,t[k]
2335 end
2336 end
2337 end
2338 end
2339 return nothing
2340end
2341table.sortedhash=sortedhash
2342table.sortedpairs=sortedhash
2343function table.append(t,list)
2344 local n=#t
2345 for i=1,#list do
2346 n=n+1
2347 t[n]=list[i]
2348 end
2349 return t
2350end
2351function table.prepend(t,list)
2352 local nl=#list
2353 local nt=nl+#t
2354 for i=#t,1,-1 do
2355 t[nt]=t[i]
2356 nt=nt-1
2357 end
2358 for i=1,#list do
2359 t[i]=list[i]
2360 end
2361 return t
2362end
2363function table.merge(t,...)
2364 if not t then
2365 t={}
2366 end
2367 for i=1,select("#",...) do
2368 for k,v in next,(select(i,...)) do
2369 t[k]=v
2370 end
2371 end
2372 return t
2373end
2374function table.merged(...)
2375 local t={}
2376 for i=1,select("#",...) do
2377 for k,v in next,(select(i,...)) do
2378 t[k]=v
2379 end
2380 end
2381 return t
2382end
2383function table.imerge(t,...)
2384 local nt=#t
2385 for i=1,select("#",...) do
2386 local nst=select(i,...)
2387 for j=1,#nst do
2388 nt=nt+1
2389 t[nt]=nst[j]
2390 end
2391 end
2392 return t
2393end
2394function table.imerged(...)
2395 local tmp={}
2396 local ntmp=0
2397 for i=1,select("#",...) do
2398 local nst=select(i,...)
2399 for j=1,#nst do
2400 ntmp=ntmp+1
2401 tmp[ntmp]=nst[j]
2402 end
2403 end
2404 return tmp
2405end
2406local function fastcopy(old,metatabletoo)
2407 if old then
2408 local new={}
2409 for k,v in next,old do
2410 if type(v)=="table" then
2411 new[k]=fastcopy(v,metatabletoo)
2412 else
2413 new[k]=v
2414 end
2415 end
2416 if metatabletoo then
2417 local mt=getmetatable(old)
2418 if mt then
2419 setmetatable(new,mt)
2420 end
2421 end
2422 return new
2423 else
2424 return {}
2425 end
2426end
2427local function copy(t,tables)
2428 if not tables then
2429 tables={}
2430 end
2431 local tcopy={}
2432 if not tables[t] then
2433 tables[t]=tcopy
2434 end
2435 for i,v in next,t do
2436 if type(i)=="table" then
2437 if tables[i] then
2438 i=tables[i]
2439 else
2440 i=copy(i,tables)
2441 end
2442 end
2443 if type(v)~="table" then
2444 tcopy[i]=v
2445 elseif tables[v] then
2446 tcopy[i]=tables[v]
2447 else
2448 tcopy[i]=copy(v,tables)
2449 end
2450 end
2451 local mt=getmetatable(t)
2452 if mt then
2453 setmetatable(tcopy,mt)
2454 end
2455 return tcopy
2456end
2457table.fastcopy=fastcopy
2458table.copy=copy
2459function table.derive(parent)
2460 local child={}
2461 if parent then
2462 setmetatable(child,{ __index=parent })
2463 end
2464 return child
2465end
2466function table.tohash(t,value)
2467 local h={}
2468 if t then
2469 if value==nil then value=true end
2470 for _,v in next,t do
2471 h[v]=value
2472 end
2473 end
2474 return h
2475end
2476function table.fromhash(t)
2477 local hsh={}
2478 local h=0
2479 for k,v in next,t do
2480 if v then
2481 h=h+1
2482 hsh[h]=k
2483 end
2484 end
2485 return hsh
2486end
2487local noquotes,hexify,handle,compact,inline,functions,metacheck,accurate
2488local reserved=table.tohash {
2489 'and','break','do','else','elseif','end','false','for','function','if',
2490 'in','local','nil','not','or','repeat','return','then','true','until','while',
2491 'NaN','goto','const',
2492}
2493local function is_simple_table(t,hexify,accurate)
2494 local nt=#t
2495 if nt>0 then
2496 local n=0
2497 for _,v in next,t do
2498 n=n+1
2499 if type(v)=="table" then
2500 return nil
2501 end
2502 end
2503 local haszero=rawget(t,0)
2504 if n==nt then
2505 local tt={}
2506 for i=1,nt do
2507 local v=t[i]
2508 local tv=type(v)
2509 if tv=="number" then
2510 if hexify then
2511 tt[i]=format("0x%X",v)
2512 elseif accurate then
2513 tt[i]=format("%q",v)
2514 else
2515 tt[i]=v
2516 end
2517 elseif tv=="string" then
2518 tt[i]=format("%q",v)
2519 elseif tv=="boolean" then
2520 tt[i]=v and "true" or "false"
2521 else
2522 return nil
2523 end
2524 end
2525 return tt
2526 elseif haszero and (n==nt+1) then
2527 local tt={}
2528 for i=0,nt do
2529 local v=t[i]
2530 local tv=type(v)
2531 if tv=="number" then
2532 if hexify then
2533 tt[i+1]=format("0x%X",v)
2534 elseif accurate then
2535 tt[i+1]=format("%q",v)
2536 else
2537 tt[i+1]=v
2538 end
2539 elseif tv=="string" then
2540 tt[i+1]=format("%q",v)
2541 elseif tv=="boolean" then
2542 tt[i+1]=v and "true" or "false"
2543 else
2544 return nil
2545 end
2546 end
2547 tt[1]="[0] = "..tt[1]
2548 return tt
2549 end
2550 end
2551 return nil
2552end
2553table.is_simple_table=is_simple_table
2554local propername=patterns.propername
2555local function dummy() end
2556local function do_serialize(root,name,depth,level,indexed)
2557 if level>0 then
2558 depth=depth.." "
2559 if indexed then
2560 handle(format("%s{",depth))
2561 else
2562 local tn=type(name)
2563 if tn=="number" then
2564 if hexify then
2565 handle(format("%s[0x%X]={",depth,name))
2566 else
2567 handle(format("%s[%s]={",depth,name))
2568 end
2569 elseif tn=="string" then
2570 if noquotes and not reserved[name] and lpegmatch(propername,name) then
2571 handle(format("%s%s={",depth,name))
2572 else
2573 handle(format("%s[%q]={",depth,name))
2574 end
2575 elseif tn=="boolean" then
2576 handle(format("%s[%s]={",depth,name and "true" or "false"))
2577 else
2578 handle(format("%s{",depth))
2579 end
2580 end
2581 end
2582 if root and next(root)~=nil then
2583 local first=nil
2584 local last=0
2585 if compact then
2586 last=#root
2587 for k=1,last do
2588 if rawget(root,k)==nil then
2589 last=k-1
2590 break
2591 end
2592 end
2593 if last>0 then
2594 first=1
2595 end
2596 end
2597 local sk=sortedkeys(root)
2598 for i=1,#sk do
2599 local k=sk[i]
2600 local v=root[k]
2601 local tv=type(v)
2602 local tk=type(k)
2603 if compact and first and tk=="number" and k>=first and k<=last then
2604 if tv=="number" then
2605 if hexify then
2606 handle(format("%s 0x%X,",depth,v))
2607 elseif accurate then
2608 handle(format("%s %q,",depth,v))
2609 else
2610 handle(format("%s %s,",depth,v))
2611 end
2612 elseif tv=="string" then
2613 handle(format("%s %q,",depth,v))
2614 elseif tv=="table" then
2615 if next(v)==nil then
2616 handle(format("%s {},",depth))
2617 elseif inline then
2618 local st=is_simple_table(v,hexify,accurate)
2619 if st then
2620 handle(format("%s { %s },",depth,concat(st,", ")))
2621 else
2622 do_serialize(v,k,depth,level+1,true)
2623 end
2624 else
2625 do_serialize(v,k,depth,level+1,true)
2626 end
2627 elseif tv=="boolean" then
2628 handle(format("%s %s,",depth,v and "true" or "false"))
2629 elseif tv=="function" then
2630 if functions then
2631 handle(format('%s load(%q),',depth,dump(v)))
2632 else
2633 handle(format('%s "function",',depth))
2634 end
2635 else
2636 handle(format("%s %q,",depth,tostring(v)))
2637 end
2638 elseif k=="__p__" then
2639 if false then
2640 handle(format("%s __p__=nil,",depth))
2641 end
2642 elseif tv=="number" then
2643 if tk=="number" then
2644 if hexify then
2645 if accurate then
2646 handle(format("%s [0x%X]=%q,",depth,k,v))
2647 else
2648 handle(format("%s [0x%X]=%s,",depth,k,v))
2649 end
2650 elseif accurate then
2651 handle(format("%s [%s]=%q,",depth,k,v))
2652 else
2653 handle(format("%s [%s]=%s,",depth,k,v))
2654 end
2655 elseif tk=="boolean" then
2656 if hexify then
2657 if accurate then
2658 handle(format("%s [%s]=%q,",depth,k and "true" or "false",v))
2659 else
2660 handle(format("%s [%s]=%s,",depth,k and "true" or "false",v))
2661 end
2662 elseif accurate then
2663 handle(format("%s [%s]=%q,",depth,k and "true" or "false",v))
2664 else
2665 handle(format("%s [%s]=%s,",depth,k and "true" or "false",v))
2666 end
2667 elseif tk~="string" then
2668 elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
2669 if hexify then
2670 if accurate then
2671 handle(format("%s %s=%q,",depth,k,v))
2672 else
2673 handle(format("%s %s=0x%X,",depth,k,v))
2674 end
2675 elseif accurate then
2676 handle(format("%s %s=%q,",depth,k,v))
2677 else
2678 handle(format("%s %s=%s,",depth,k,v))
2679 end
2680 else
2681 if hexify then
2682 if accurate then
2683 handle(format("%s [%q]=%q,",depth,k,v))
2684 else
2685 handle(format("%s [%q]=0x%X,",depth,k,v))
2686 end
2687 elseif accurate then
2688 handle(format("%s [%q]=%q,",depth,k,v))
2689 else
2690 handle(format("%s [%q]=%s,",depth,k,v))
2691 end
2692 end
2693 elseif tv=="string" then
2694 if tk=="number" then
2695 if hexify then
2696 handle(format("%s [0x%X]=%q,",depth,k,v))
2697 elseif accurate then
2698 handle(format("%s [%q]=%q,",depth,k,v))
2699 else
2700 handle(format("%s [%s]=%q,",depth,k,v))
2701 end
2702 elseif tk=="boolean" then
2703 handle(format("%s [%s]=%q,",depth,k and "true" or "false",v))
2704 elseif tk~="string" then
2705 elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
2706 handle(format("%s %s=%q,",depth,k,v))
2707 else
2708 handle(format("%s [%q]=%q,",depth,k,v))
2709 end
2710 elseif tv=="table" then
2711 if next(v)==nil then
2712 if tk=="number" then
2713 if hexify then
2714 handle(format("%s [0x%X]={},",depth,k))
2715 elseif accurate then
2716 handle(format("%s [%q]={},",depth,k))
2717 else
2718 handle(format("%s [%s]={},",depth,k))
2719 end
2720 elseif tk=="boolean" then
2721 handle(format("%s [%s]={},",depth,k and "true" or "false"))
2722 elseif tk~="string" then
2723 elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
2724 handle(format("%s %s={},",depth,k))
2725 else
2726 handle(format("%s [%q]={},",depth,k))
2727 end
2728 elseif inline then
2729 local st=is_simple_table(v,hexify,accurate)
2730 if st then
2731 if tk=="number" then
2732 if hexify then
2733 handle(format("%s [0x%X]={ %s },",depth,k,concat(st,", ")))
2734 elseif accurate then
2735 handle(format("%s [%q]={ %s },",depth,k,concat(st,", ")))
2736 else
2737 handle(format("%s [%s]={ %s },",depth,k,concat(st,", ")))
2738 end
2739 elseif tk=="boolean" then
2740 handle(format("%s [%s]={ %s },",depth,k and "true" or "false",concat(st,", ")))
2741 elseif tk~="string" then
2742 elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
2743 handle(format("%s %s={ %s },",depth,k,concat(st,", ")))
2744 else
2745 handle(format("%s [%q]={ %s },",depth,k,concat(st,", ")))
2746 end
2747 else
2748 do_serialize(v,k,depth,level+1)
2749 end
2750 else
2751 do_serialize(v,k,depth,level+1)
2752 end
2753 elseif tv=="boolean" then
2754 if tk=="number" then
2755 if hexify then
2756 handle(format("%s [0x%X]=%s,",depth,k,v and "true" or "false"))
2757 elseif accurate then
2758 handle(format("%s [%q]=%s,",depth,k,v and "true" or "false"))
2759 else
2760 handle(format("%s [%s]=%s,",depth,k,v and "true" or "false"))
2761 end
2762 elseif tk=="boolean" then
2763 handle(format("%s [%s]=%s,",depth,tostring(k),v and "true" or "false"))
2764 elseif tk~="string" then
2765 elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
2766 handle(format("%s %s=%s,",depth,k,v and "true" or "false"))
2767 else
2768 handle(format("%s [%q]=%s,",depth,k,v and "true" or "false"))
2769 end
2770 elseif tv=="function" then
2771 if functions then
2772 local getinfo=debug and debug.getinfo
2773 if getinfo then
2774 local f=getinfo(v).what=="C" and dump(dummy) or dump(v)
2775 if tk=="number" then
2776 if hexify then
2777 handle(format("%s [0x%X]=load(%q),",depth,k,f))
2778 elseif accurate then
2779 handle(format("%s [%q]=load(%q),",depth,k,f))
2780 else
2781 handle(format("%s [%s]=load(%q),",depth,k,f))
2782 end
2783 elseif tk=="boolean" then
2784 handle(format("%s [%s]=load(%q),",depth,k and "true" or "false",f))
2785 elseif tk~="string" then
2786 elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
2787 handle(format("%s %s=load(%q),",depth,k,f))
2788 else
2789 handle(format("%s [%q]=load(%q),",depth,k,f))
2790 end
2791 end
2792 end
2793 else
2794 if tk=="number" then
2795 if hexify then
2796 handle(format("%s [0x%X]=%q,",depth,k,tostring(v)))
2797 elseif accurate then
2798 handle(format("%s [%q]=%q,",depth,k,tostring(v)))
2799 else
2800 handle(format("%s [%s]=%q,",depth,k,tostring(v)))
2801 end
2802 elseif tk=="boolean" then
2803 handle(format("%s [%s]=%q,",depth,k and "true" or "false",tostring(v)))
2804 elseif tk~="string" then
2805 elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
2806 handle(format("%s %s=%q,",depth,k,tostring(v)))
2807 else
2808 handle(format("%s [%q]=%q,",depth,k,tostring(v)))
2809 end
2810 end
2811 end
2812 end
2813 if level>0 then
2814 handle(format("%s},",depth))
2815 end
2816end
2817local function serialize(_handle,root,name,specification)
2818 local tname=type(name)
2819 if type(specification)=="table" then
2820 noquotes=specification.noquotes
2821 hexify=specification.hexify
2822 accurate=specification.accurate
2823 handle=_handle or specification.handle or print
2824 functions=specification.functions
2825 compact=specification.compact
2826 inline=specification.inline and compact
2827 metacheck=specification.metacheck
2828 if functions==nil then
2829 functions=true
2830 end
2831 if compact==nil then
2832 compact=true
2833 end
2834 if inline==nil then
2835 inline=compact
2836 end
2837 if metacheck==nil then
2838 metacheck=true
2839 end
2840 else
2841 noquotes=false
2842 hexify=false
2843 handle=_handle or print
2844 compact=true
2845 inline=true
2846 functions=true
2847 metacheck=true
2848 end
2849 if tname=="string" then
2850 if name=="return" then
2851 handle("return {")
2852 else
2853 handle(name.."={")
2854 end
2855 elseif tname=="number" then
2856 if hexify then
2857 handle(format("[0x%X]={",name))
2858 else
2859 handle("["..name.."]={")
2860 end
2861 elseif tname=="boolean" then
2862 if name then
2863 handle("return {")
2864 else
2865 handle("{")
2866 end
2867 else
2868 handle("t={")
2869 end
2870 if root then
2871 if metacheck and getmetatable(root) then
2872 local dummy=root._w_h_a_t_e_v_e_r_
2873 root._w_h_a_t_e_v_e_r_=nil
2874 end
2875 if next(root)~=nil then
2876 do_serialize(root,name,"",0)
2877 end
2878 end
2879 handle("}")
2880end
2881function table.serialize(root,name,specification)
2882 local t={}
2883 local n=0
2884 local function flush(s)
2885 n=n+1
2886 t[n]=s
2887 end
2888 serialize(flush,root,name,specification)
2889 return concat(t,"\n")
2890end
2891table.tohandle=serialize
2892local maxtab=2*1024
2893function table.tofile(filename,root,name,specification)
2894 local f=io.open(filename,'w')
2895 if f then
2896 if maxtab>1 then
2897 local t={}
2898 local n=0
2899 local function flush(s)
2900 n=n+1
2901 t[n]=s
2902 if n>maxtab then
2903 f:write(concat(t,"\n"),"\n")
2904 t={}
2905 n=0
2906 end
2907 end
2908 serialize(flush,root,name,specification)
2909 f:write(concat(t,"\n"),"\n")
2910 else
2911 local function flush(s)
2912 f:write(s,"\n")
2913 end
2914 serialize(flush,root,name,specification)
2915 end
2916 f:close()
2917 io.flush()
2918 end
2919end
2920local function flattened(t,f,depth)
2921 if f==nil then
2922 f={}
2923 depth=0xFFFF
2924 elseif tonumber(f) then
2925 depth=f
2926 f={}
2927 elseif not depth then
2928 depth=0xFFFF
2929 end
2930 for k,v in next,t do
2931 if type(k)~="number" then
2932 if depth>0 and type(v)=="table" then
2933 flattened(v,f,depth-1)
2934 else
2935 f[#f+1]=v
2936 end
2937 end
2938 end
2939 for k=1,#t do
2940 local v=t[k]
2941 if depth>0 and type(v)=="table" then
2942 flattened(v,f,depth-1)
2943 else
2944 f[#f+1]=v
2945 end
2946 end
2947 return f
2948end
2949table.flattened=flattened
2950local function collapsed(t,f,h)
2951 if f==nil then
2952 f={}
2953 h={}
2954 end
2955 for k=1,#t do
2956 local v=t[k]
2957 if type(v)=="table" then
2958 collapsed(v,f,h)
2959 elseif not h[v] then
2960 f[#f+1]=v
2961 h[v]=true
2962 end
2963 end
2964 return f
2965end
2966local function collapsedhash(t,h)
2967 if h==nil then
2968 h={}
2969 end
2970 for k=1,#t do
2971 local v=t[k]
2972 if type(v)=="table" then
2973 collapsedhash(v,h)
2974 else
2975 h[v]=true
2976 end
2977 end
2978 return h
2979end
2980table.collapsed=collapsed
2981table.collapsedhash=collapsedhash
2982local function unnest(t,f)
2983 if not f then
2984 f={}
2985 end
2986 for i=1,#t do
2987 local v=t[i]
2988 if type(v)=="table" then
2989 if type(v[1])=="table" then
2990 unnest(v,f)
2991 else
2992 f[#f+1]=v
2993 end
2994 else
2995 f[#f+1]=v
2996 end
2997 end
2998 return f
2999end
3000function table.unnest(t)
3001 return unnest(t)
3002end
3003local function are_equal(a,b,n,m)
3004 if a==b then
3005 return true
3006 elseif a and b and #a==#b then
3007 if not n then
3008 n=1
3009 end
3010 if not m then
3011 m=#a
3012 end
3013 for i=n,m do
3014 local ai,bi=a[i],b[i]
3015 if ai==bi then
3016 elseif type(ai)=="table" and type(bi)=="table" then
3017 if not are_equal(ai,bi) then
3018 return false
3019 end
3020 else
3021 return false
3022 end
3023 end
3024 return true
3025 else
3026 return false
3027 end
3028end
3029local function identical(a,b)
3030 if a~=b then
3031 for ka,va in next,a do
3032 local vb=b[ka]
3033 if va==vb then
3034 elseif type(va)=="table" and type(vb)=="table" then
3035 if not identical(va,vb) then
3036 return false
3037 end
3038 else
3039 return false
3040 end
3041 end
3042 end
3043 return true
3044end
3045table.identical=identical
3046table.are_equal=are_equal
3047local function sparse(old,nest,keeptables)
3048 local new={}
3049 for k,v in next,old do
3050 if not (v=="" or v==false) then
3051 if nest and type(v)=="table" then
3052 v=sparse(v,nest)
3053 if keeptables or next(v)~=nil then
3054 new[k]=v
3055 end
3056 else
3057 new[k]=v
3058 end
3059 end
3060 end
3061 return new
3062end
3063table.sparse=sparse
3064function table.compact(t)
3065 return sparse(t,true,true)
3066end
3067function table.contains(t,v)
3068 if t then
3069 for i=1,#t do
3070 if t[i]==v then
3071 return i
3072 end
3073 end
3074 end
3075 return false
3076end
3077function table.count(t)
3078 local n=0
3079 for k,v in next,t do
3080 n=n+1
3081 end
3082 return n
3083end
3084function table.swapped(t,s)
3085 local n={}
3086 if s then
3087 for k,v in next,s do
3088 n[k]=v
3089 end
3090 end
3091 for k,v in next,t do
3092 n[v]=k
3093 end
3094 return n
3095end
3096function table.hashed(t)
3097 for i=1,#t do
3098 t[t[i]]=i
3099 end
3100 return t
3101end
3102function table.mirrored(t)
3103 local n={}
3104 for k,v in next,t do
3105 n[v]=k
3106 n[k]=v
3107 end
3108 return n
3109end
3110function table.reversed(t)
3111 if t then
3112 local tt={}
3113 local tn=#t
3114 if tn>0 then
3115 local ttn=0
3116 for i=tn,1,-1 do
3117 ttn=ttn+1
3118 tt[ttn]=t[i]
3119 end
3120 end
3121 return tt
3122 end
3123end
3124function table.reverse(t)
3125 if t then
3126 local n=#t
3127 local m=n+1
3128 for i=1,floor(n/2) do
3129 local j=m-i
3130 t[i],t[j]=t[j],t[i]
3131 end
3132 return t
3133 end
3134end
3135local function sequenced(t,sep,simple)
3136 if not t then
3137 return ""
3138 elseif type(t)~="table" then
3139 return t
3140 end
3141 local n=#t
3142 local s={}
3143 if n>0 then
3144 for i=1,n do
3145 local v=t[i]
3146 if type(v)=="table" then
3147 s[i]="{"..sequenced(v,sep,simple).."}"
3148 else
3149 s[i]=tostring(t[i])
3150 end
3151 end
3152 else
3153 n=0
3154 for k,v in sortedhash(t) do
3155 if simple then
3156 if v==true then
3157 n=n+1
3158 s[n]=k
3159 elseif v and v~="" then
3160 n=n+1
3161 if type(v)=="table" then
3162 s[n]=k.."={"..sequenced(v,sep,simple).."}"
3163 else
3164 s[n]=k.."="..tostring(v)
3165 end
3166 end
3167 else
3168 n=n+1
3169 if type(v)=="table" then
3170 s[n]=k.."={"..sequenced(v,sep,simple).."}"
3171 else
3172 s[n]=k.."="..tostring(v)
3173 end
3174 end
3175 end
3176 end
3177 if sep==true then
3178 return "{ "..concat(s,", ").." }"
3179 else
3180 return concat(s,sep or " | ")
3181 end
3182end
3183table.sequenced=sequenced
3184function table.print(t,...)
3185 if type(t)~="table" then
3186 print(tostring(t))
3187 else
3188 serialize(print,t,...)
3189 end
3190end
3191if setinspector then
3192 setinspector("table",function(v) if type(v)=="table" then serialize(print,v,"table") return true end end)
3193end
3194function table.sub(t,i,j)
3195 return { unpack(t,i,j) }
3196end
3197function table.is_empty(t)
3198 return not t or next(t)==nil
3199end
3200function table.has_one_entry(t)
3201 return t and next(t,next(t))==nil
3202end
3203function table.loweredkeys(t)
3204 local l={}
3205 for k,v in next,t do
3206 l[lower(k)]=v
3207 end
3208 return l
3209end
3210function table.unique(old)
3211 local hash={}
3212 local new={}
3213 local n=0
3214 for i=1,#old do
3215 local oi=old[i]
3216 if not hash[oi] then
3217 n=n+1
3218 new[n]=oi
3219 hash[oi]=true
3220 end
3221 end
3222 return new
3223end
3224function table.sorted(t,...)
3225 sort(t,...)
3226 return t
3227end
3228function table.values(t,s)
3229 if t then
3230 local values={}
3231 local keys={}
3232 local v=0
3233 for key,value in next,t do
3234 if not keys[value] then
3235 v=v+1
3236 values[v]=value
3237 keys[k]=key
3238 end
3239 end
3240 if s then
3241 sort(values)
3242 end
3243 return values
3244 else
3245 return {}
3246 end
3247end
3248function table.filtered(t,pattern,sort,cmp)
3249 if t and type(pattern)=="string" then
3250 if sort then
3251 local s
3252 if cmp then
3253 s=sortedhashkeys(t,function(a,b) return cmp(t,a,b) end)
3254 else
3255 s=sortedkeys(t)
3256 end
3257 local n=0
3258 local m=#s
3259 local function kv(s)
3260 while n<m do
3261 n=n+1
3262 local k=s[n]
3263 if find(k,pattern) then
3264 return k,t[k]
3265 end
3266 end
3267 end
3268 return kv,s
3269 else
3270 local n=next(t)
3271 local function iterator()
3272 while n~=nil do
3273 local k=n
3274 n=next(t,k)
3275 if find(k,pattern) then
3276 return k,t[k]
3277 end
3278 end
3279 end
3280 return iterator,t
3281 end
3282 else
3283 return nothing
3284 end
3285end
3286if not table.move then
3287 function table.move(a1,f,e,t,a2)
3288 if a2 and a1~=a2 then
3289 for i=f,e do
3290 a2[t]=a1[i]
3291 t=t+1
3292 end
3293 return a2
3294 else
3295 t=t+e-f
3296 for i=e,f,-1 do
3297 a1[t]=a1[i]
3298 t=t-1
3299 end
3300 return a1
3301 end
3302 end
3303end
3304
3305
3306end
3307
3308do
3309
3310package.loaded["l-io"] = package.loaded["l-io"] or true
3311
3312
3313
3314if not modules then modules={} end modules ['l-io']={
3315 version=1.001,
3316 comment="companion to luat-lib.mkiv",
3317 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
3318 copyright="PRAGMA ADE / ConTeXt Development Team",
3319 license="see context related readme files"
3320}
3321local io=io
3322local open,flush,write,read=io.open,io.flush,io.write,io.read
3323local byte,find,gsub,format=string.byte,string.find,string.gsub,string.format
3324local concat=table.concat
3325local type=type
3326if string.find(os.getenv("PATH") or "",";",1,true) then
3327 io.fileseparator,io.pathseparator="\\",";"
3328else
3329 io.fileseparator,io.pathseparator="/",":"
3330end
3331local large=0x01000000
3332local medium=0x00100000
3333local small=0x00020000
3334local function readall(f)
3335 local size=f:seek("end")
3336 if size>0 then
3337 f:seek("set",0)
3338 return f:read(size)
3339 else
3340 return ""
3341 end
3342end
3343io.readall=readall
3344function io.loaddata(filename,textmode)
3345 local f=open(filename,(textmode and 'r') or 'rb')
3346 if f then
3347 local size=f:seek("end")
3348 local data=nil
3349 if size>0 then
3350 f:seek("set",0)
3351 data=f:read(size)
3352 end
3353 f:close()
3354 return data
3355 end
3356end
3357function io.copydata(source,target,action)
3358 local f=open(source,"rb")
3359 if f then
3360 local g=open(target,"wb")
3361 if g then
3362 local size=f:seek("end")
3363 if size>0 then
3364 f:seek("set",0)
3365 local data=f:read(size)
3366 if action then
3367 data=action(data)
3368 end
3369 if data then
3370 g:write(data)
3371 end
3372 end
3373 g:close()
3374 end
3375 f:close()
3376 flush()
3377 end
3378end
3379function io.savedata(filename,data,joiner,append)
3380 local f=open(filename,append and "ab" or "wb")
3381 if f then
3382 if append and joiner and f:seek("end")>0 then
3383 f:write(joiner)
3384 end
3385 if type(data)=="table" then
3386 f:write(concat(data,joiner or ""))
3387 elseif type(data)=="function" then
3388 data(f)
3389 else
3390 f:write(data or "")
3391 end
3392 f:close()
3393 flush()
3394 return true
3395 else
3396 return false
3397 end
3398end
3399if fio and fio.readline then
3400 local readline=fio.readline
3401 function io.loadlines(filename,n)
3402 local f=open(filename,'r')
3403 if not f then
3404 elseif n then
3405 local lines={}
3406 for i=1,n do
3407 local line=readline(f)
3408 if line then
3409 lines[i]=line
3410 else
3411 break
3412 end
3413 end
3414 f:close()
3415 lines=concat(lines,"\n")
3416 if #lines>0 then
3417 return lines
3418 end
3419 else
3420 local line=readline(f)
3421 f:close()
3422 if line and #line>0 then
3423 return line
3424 end
3425 end
3426 end
3427else
3428 function io.loadlines(filename,n)
3429 local f=open(filename,'r')
3430 if not f then
3431 elseif n then
3432 local lines={}
3433 for i=1,n do
3434 local line=f:read("*lines")
3435 if line then
3436 lines[i]=line
3437 else
3438 break
3439 end
3440 end
3441 f:close()
3442 lines=concat(lines,"\n")
3443 if #lines>0 then
3444 return lines
3445 end
3446 else
3447 local line=f:read("*line") or ""
3448 f:close()
3449 if #line>0 then
3450 return line
3451 end
3452 end
3453 end
3454end
3455function io.loadchunk(filename,n)
3456 local f=open(filename,'rb')
3457 if f then
3458 local data=f:read(n or 1024)
3459 f:close()
3460 if #data>0 then
3461 return data
3462 end
3463 end
3464end
3465function io.exists(filename)
3466 local f=open(filename)
3467 if f==nil then
3468 return false
3469 else
3470 f:close()
3471 return true
3472 end
3473end
3474function io.size(filename)
3475 local f=open(filename)
3476 if f==nil then
3477 return 0
3478 else
3479 local s=f:seek("end")
3480 f:close()
3481 return s
3482 end
3483end
3484local function noflines(f)
3485 if type(f)=="string" then
3486 local f=open(filename)
3487 if f then
3488 local n=f and noflines(f) or 0
3489 f:close()
3490 return n
3491 else
3492 return 0
3493 end
3494 else
3495 local n=0
3496 for _ in f:lines() do
3497 n=n+1
3498 end
3499 f:seek('set',0)
3500 return n
3501 end
3502end
3503io.noflines=noflines
3504local nextchar={
3505 [ 4]=function(f)
3506 return f:read(1,1,1,1)
3507 end,
3508 [ 2]=function(f)
3509 return f:read(1,1)
3510 end,
3511 [ 1]=function(f)
3512 return f:read(1)
3513 end,
3514 [-2]=function(f)
3515 local a,b=f:read(1,1)
3516 return b,a
3517 end,
3518 [-4]=function(f)
3519 local a,b,c,d=f:read(1,1,1,1)
3520 return d,c,b,a
3521 end
3522}
3523function io.characters(f,n)
3524 if f then
3525 return nextchar[n or 1],f
3526 end
3527end
3528local nextbyte={
3529 [4]=function(f)
3530 local a,b,c,d=f:read(1,1,1,1)
3531 if d then
3532 return byte(a),byte(b),byte(c),byte(d)
3533 end
3534 end,
3535 [3]=function(f)
3536 local a,b,c=f:read(1,1,1)
3537 if b then
3538 return byte(a),byte(b),byte(c)
3539 end
3540 end,
3541 [2]=function(f)
3542 local a,b=f:read(1,1)
3543 if b then
3544 return byte(a),byte(b)
3545 end
3546 end,
3547 [1]=function (f)
3548 local a=f:read(1)
3549 if a then
3550 return byte(a)
3551 end
3552 end,
3553 [-2]=function (f)
3554 local a,b=f:read(1,1)
3555 if b then
3556 return byte(b),byte(a)
3557 end
3558 end,
3559 [-3]=function(f)
3560 local a,b,c=f:read(1,1,1)
3561 if b then
3562 return byte(c),byte(b),byte(a)
3563 end
3564 end,
3565 [-4]=function(f)
3566 local a,b,c,d=f:read(1,1,1,1)
3567 if d then
3568 return byte(d),byte(c),byte(b),byte(a)
3569 end
3570 end
3571}
3572function io.bytes(f,n)
3573 if f then
3574 return nextbyte[n or 1],f
3575 else
3576 return nil,nil
3577 end
3578end
3579function io.ask(question,default,options)
3580 while true do
3581 write(question)
3582 if options then
3583 write(format(" [%s]",concat(options,"|")))
3584 end
3585 if default then
3586 write(format(" [%s]",default))
3587 end
3588 write(format(" "))
3589 flush()
3590 local answer=read()
3591 answer=gsub(answer,"^%s*(.*)%s*$","%1")
3592 if answer=="" and default then
3593 return default
3594 elseif not options then
3595 return answer
3596 else
3597 for k=1,#options do
3598 if options[k]==answer then
3599 return answer
3600 end
3601 end
3602 local pattern="^"..answer
3603 for k=1,#options do
3604 local v=options[k]
3605 if find(v,pattern) then
3606 return v
3607 end
3608 end
3609 end
3610 end
3611end
3612local function readnumber(f,n,m)
3613 if m then
3614 f:seek("set",n)
3615 n=m
3616 end
3617 if n==1 then
3618 return byte(f:read(1))
3619 elseif n==2 then
3620 local a,b=byte(f:read(2),1,2)
3621 return 0x100*a+b
3622 elseif n==3 then
3623 local a,b,c=byte(f:read(3),1,3)
3624 return 0x10000*a+0x100*b+c
3625 elseif n==4 then
3626 local a,b,c,d=byte(f:read(4),1,4)
3627 return 0x1000000*a+0x10000*b+0x100*c+d
3628 elseif n==8 then
3629 local a,b=readnumber(f,4),readnumber(f,4)
3630 return 0x100*a+b
3631 elseif n==12 then
3632 local a,b,c=readnumber(f,4),readnumber(f,4),readnumber(f,4)
3633 return 0x10000*a+0x100*b+c
3634 elseif n==-2 then
3635 local b,a=byte(f:read(2),1,2)
3636 return 0x100*a+b
3637 elseif n==-3 then
3638 local c,b,a=byte(f:read(3),1,3)
3639 return 0x10000*a+0x100*b+c
3640 elseif n==-4 then
3641 local d,c,b,a=byte(f:read(4),1,4)
3642 return 0x1000000*a+0x10000*b+0x100*c+d
3643 elseif n==-8 then
3644 local h,g,f,e,d,c,b,a=byte(f:read(8),1,8)
3645 return 0x100000000000000*a+0x1000000000000*b+0x10000000000*c+0x100000000*d+0x1000000*e+0x10000*f+0x100*g+h
3646 else
3647 return 0
3648 end
3649end
3650io.readnumber=readnumber
3651function io.readstring(f,n,m)
3652 if m then
3653 f:seek("set",n)
3654 n=m
3655 end
3656 local str=gsub(f:read(n),"\000","")
3657 return str
3658end
3659
3660
3661end
3662
3663do
3664
3665package.loaded["l-number"] = package.loaded["l-number"] or true
3666
3667
3668
3669if not modules then modules={} end modules ['l-number']={
3670 version=1.001,
3671 comment="companion to luat-lib.mkxl",
3672 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
3673 copyright="PRAGMA ADE / ConTeXt Development Team",
3674 license="see context related readme files"
3675}
3676local tostring,tonumber=tostring,tonumber
3677local format,match,rep=string.format,string.match,string.rep
3678local concat,insert=table.concat,table.insert
3679local lpegmatch=lpeg.match
3680local floor=math.floor
3681number=number or {}
3682local number=number
3683if bit32 then
3684 local bextract=bit32.extract
3685 local t={
3686 "0","0","0","0","0","0","0","0",
3687 "0","0","0","0","0","0","0","0",
3688 "0","0","0","0","0","0","0","0",
3689 "0","0","0","0","0","0","0","0",
3690 }
3691 function number.tobitstring(b,m,w)
3692 if not w then
3693 w=32
3694 end
3695 local n=w
3696 for i=0,w-1 do
3697 local v=bextract(b,i)
3698 local k=w-i
3699 if v==1 then
3700 n=k
3701 t[k]="1"
3702 else
3703 t[k]="0"
3704 end
3705 end
3706 if w then
3707 return concat(t,"",1,w)
3708 elseif m then
3709 m=33-m*8
3710 if m<1 then
3711 m=1
3712 end
3713 return concat(t,"",1,m)
3714 elseif n<8 then
3715 return concat(t)
3716 elseif n<16 then
3717 return concat(t,"",9)
3718 elseif n<24 then
3719 return concat(t,"",17)
3720 else
3721 return concat(t,"",25)
3722 end
3723 end
3724else
3725 function number.tobitstring(n,m)
3726 if n>0 then
3727 local t={}
3728 while n>0 do
3729 insert(t,1,n%2>0 and 1 or 0)
3730 n=floor(n/2)
3731 end
3732 local nn=8-#t%8
3733 if nn>0 and nn<8 then
3734 for i=1,nn do
3735 insert(t,1,0)
3736 end
3737 end
3738 if m then
3739 m=m*8-#t
3740 if m>0 then
3741 insert(t,1,rep("0",m))
3742 end
3743 end
3744 return concat(t)
3745 elseif m then
3746 rep("00000000",m)
3747 else
3748 return "00000000"
3749 end
3750 end
3751end
3752function number.valid(str,default)
3753 return tonumber(str) or default or nil
3754end
3755function number.toevenhex(n)
3756 local s=format("%X",n)
3757 if #s%2==0 then
3758 return s
3759 else
3760 return "0"..s
3761 end
3762end
3763function number.bytetodecimal(b)
3764 local d=floor(b*100/255+0.5)
3765 if d>100 then
3766 return 100
3767 elseif d<-100 then
3768 return -100
3769 else
3770 return d
3771 end
3772end
3773function number.decimaltobyte(d)
3774 local b=floor(d*255/100+0.5)
3775 if b>255 then
3776 return 255
3777 elseif b<-255 then
3778 return -255
3779 else
3780 return b
3781 end
3782end
3783function number.idiv(i,d)
3784 return floor(i/d)
3785end
3786
3787
3788end
3789
3790do
3791
3792package.loaded["l-set"] = package.loaded["l-set"] or true
3793
3794
3795
3796if not modules then modules={} end modules ['l-set']={
3797 version=1.001,
3798 comment="companion to luat-lib.mkiv",
3799 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
3800 copyright="PRAGMA ADE / ConTeXt Development Team",
3801 license="see context related readme files"
3802}
3803set=set or {}
3804local nums={}
3805local tabs={}
3806local concat=table.concat
3807local next,type=next,type
3808set.create=table.tohash
3809function set.tonumber(t)
3810 if next(t) then
3811 local s=""
3812 for k,v in next,t do
3813 if v then
3814 s=s.." "..k
3815 end
3816 end
3817 local n=nums[s]
3818 if not n then
3819 n=#tabs+1
3820 tabs[n]=t
3821 nums[s]=n
3822 end
3823 return n
3824 else
3825 return 0
3826 end
3827end
3828function set.totable(n)
3829 if n==0 then
3830 return {}
3831 else
3832 return tabs[n] or {}
3833 end
3834end
3835function set.tolist(n)
3836 if n==0 or not tabs[n] then
3837 return ""
3838 else
3839 local t,n={},0
3840 for k,v in next,tabs[n] do
3841 if v then
3842 n=n+1
3843 t[n]=k
3844 end
3845 end
3846 return concat(t," ")
3847 end
3848end
3849function set.contains(n,s)
3850 if type(n)=="table" then
3851 return n[s]
3852 elseif n==0 then
3853 return false
3854 else
3855 local t=tabs[n]
3856 return t and t[s]
3857 end
3858end
3859
3860
3861end
3862
3863do
3864
3865package.loaded["l-os"] = package.loaded["l-os"] or true
3866
3867
3868
3869if not modules then modules={} end modules ['l-os']={
3870 version=1.001,
3871 comment="companion to luat-lib.mkiv",
3872 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
3873 copyright="PRAGMA ADE / ConTeXt Development Team",
3874 license="see context related readme files"
3875}
3876local os=os
3877local date,time,difftime=os.date,os.time,os.difftime
3878local find,format,gsub,upper,gmatch=string.find,string.format,string.gsub,string.upper,string.gmatch
3879local concat=table.concat
3880local random,ceil,randomseed,modf=math.random,math.ceil,math.randomseed,math.modf
3881local type,setmetatable,tonumber,tostring=type,setmetatable,tonumber,tostring
3882do
3883 local selfdir=os.selfdir
3884 if selfdir=="" then
3885 selfdir=nil
3886 end
3887 if not selfdir then
3888 if arg then
3889 for i=1,#arg do
3890 local a=arg[i]
3891 if find(a,"^%-%-[c:]*texmfbinpath=") then
3892 selfdir=gsub(a,"^.-=","")
3893 break
3894 end
3895 end
3896 end
3897 if not selfdir then
3898 selfdir=os.selfbin or "luatex"
3899 if find(selfdir,"[/\\]") then
3900 selfdir=gsub(selfdir,"[/\\][^/\\]*$","")
3901 elseif os.getenv then
3902 local path=os.getenv("PATH")
3903 local name=gsub(selfdir,"^.*[/\\][^/\\]","")
3904 local patt="[^:]+"
3905 if os.type=="windows" then
3906 patt="[^;]+"
3907 name=name..".exe"
3908 end
3909 local isfile
3910 if lfs then
3911 local attributes=lfs.attributes
3912 isfile=function(name)
3913 local a=attributes(name,"mode")
3914 return a=="file" or a=="link" or nil
3915 end
3916 else
3917 local open=io.open
3918 isfile=function(name)
3919 local f=open(name)
3920 if f then
3921 f:close()
3922 return true
3923 end
3924 end
3925 end
3926 for p in gmatch(path,patt) do
3927 if isfile(p.."/"..name) then
3928 selfdir=p
3929 break
3930 end
3931 end
3932 end
3933 end
3934 os.selfdir=selfdir or "."
3935 end
3936end
3937math.initialseed=tonumber(string.sub(string.reverse(tostring(ceil(socket and socket.gettime()*10000 or time()))),1,6))
3938randomseed(math.initialseed)
3939if not os.__getenv__ then
3940 os.__getenv__=os.getenv
3941 os.__setenv__=os.setenv
3942 if os.env then
3943 local osgetenv=os.getenv
3944 local ossetenv=os.setenv
3945 local osenv=os.env local _=osenv.PATH
3946 function os.setenv(k,v)
3947 if v==nil then
3948 v=""
3949 end
3950 local K=upper(k)
3951 osenv[K]=v
3952 if type(v)=="table" then
3953 v=concat(v,";")
3954 end
3955 ossetenv(K,v)
3956 end
3957 function os.getenv(k)
3958 local K=upper(k)
3959 local v=osenv[K] or osenv[k] or osgetenv(K) or osgetenv(k)
3960 if v=="" then
3961 return nil
3962 else
3963 return v
3964 end
3965 end
3966 else
3967 local ossetenv=os.setenv
3968 local osgetenv=os.getenv
3969 local osenv={}
3970 function os.setenv(k,v)
3971 if v==nil then
3972 v=""
3973 end
3974 local K=upper(k)
3975 osenv[K]=v
3976 end
3977 function os.getenv(k)
3978 local K=upper(k)
3979 local v=osenv[K] or osgetenv(K) or osgetenv(k)
3980 if v=="" then
3981 return nil
3982 else
3983 return v
3984 end
3985 end
3986 local function __index(t,k)
3987 return os.getenv(k)
3988 end
3989 local function __newindex(t,k,v)
3990 os.setenv(k,v)
3991 end
3992 os.env={}
3993 setmetatable(os.env,{ __index=__index,__newindex=__newindex } )
3994 end
3995end
3996if not io.fileseparator then
3997 if find(os.getenv("PATH"),";",1,true) then
3998 io.fileseparator,io.pathseparator,os.type="\\",";",os.type or "windows"
3999 else
4000 io.fileseparator,io.pathseparator,os.type="/",":",os.type or "unix"
4001 end
4002end
4003os.type=os.type or (io.pathseparator==";" and "windows") or "unix"
4004os.name=os.name or (os.type=="windows" and "mswin" ) or "linux"
4005if os.type=="windows" then
4006 os.libsuffix,os.binsuffix,os.binsuffixes='dll','exe',{ 'exe','cmd','bat' }
4007elseif os.name=="macosx" then
4008 os.libsuffix,os.binsuffix,os.binsuffixes='dylib','',{ '' }
4009else
4010 os.libsuffix,os.binsuffix,os.binsuffixes='so','',{ '' }
4011end
4012do
4013 local execute=os.execute
4014 local iopopen=io.popen
4015 local ostype=os.type
4016 local function resultof(command)
4017 local handle=iopopen(command,ostype=="windows" and "rb" or "r")
4018 if handle then
4019 local result=handle:read("*all") or ""
4020 handle:close()
4021 return result
4022 else
4023 return ""
4024 end
4025 end
4026 os.resultof=resultof
4027 function os.pipeto(command)
4028 return iopopen(command,"w")
4029 end
4030 local launchers={
4031 windows="start %s",
4032 macosx="open %s",
4033 unix="xdg-open %s &> /dev/null &",
4034 }
4035 function os.launch(str)
4036 local command=format(launchers[os.name] or launchers.unix,str)
4037 execute(command)
4038 end
4039end
4040do
4041 local gettimeofday=os.gettimeofday or os.clock
4042 os.gettimeofday=gettimeofday
4043 local startuptime=gettimeofday()
4044 function os.runtime()
4045 return gettimeofday()-startuptime
4046 end
4047end
4048do
4049 local name=os.name or "linux"
4050 local platform=os.getenv("MTX_PLATFORM") or ""
4051 local architecture=os.uname and os.uname().machine
4052 local bits=os.getenv("MTX_BITS") or find(platform,"64") and 64 or 32
4053 if platform~="" then
4054 elseif os.type=="windows" then
4055 architecture=string.lower(architecture or os.getenv("PROCESSOR_ARCHITECTURE") or "")
4056 if architecture=="x86_64" then
4057 bits,platform=64,"win64"
4058 elseif find(architecture,"amd64") then
4059 bits,platform=64,"win64"
4060 elseif find(architecture,"arm64") then
4061 bits,platform=64,"windows-arm64"
4062 elseif find(architecture,"arm32") then
4063 bits,platform=32,"windows-arm32"
4064 else
4065 bits,platform=32,"mswin"
4066 end
4067 elseif name=="linux" then
4068 architecture=architecture or os.getenv("HOSTTYPE") or resultof("uname -m") or ""
4069 local musl=find(os.selfdir or "","linuxmusl")
4070 if find(architecture,"x86_64") then
4071 bits,platform=64,musl and "linuxmusl" or "linux-64"
4072 elseif find(architecture,"ppc") then
4073 bits,platform=32,"linux-ppc"
4074 else
4075 bits,platform=32,musl and "linuxmusl" or "linux"
4076 end
4077 elseif name=="macosx" then
4078 architecture=architecture or resultof("echo $HOSTTYPE") or ""
4079 if architecture=="" then
4080 bits,platform=64,"osx-intel"
4081 elseif find(architecture,"i386") then
4082 bits,platform=64,"osx-intel"
4083 elseif find(architecture,"x86_64") then
4084 bits,platform=64,"osx-64"
4085 elseif find(architecture,"arm64") then
4086 bits,platform=64,"osx-arm"
4087 else
4088 bits,platform=32,"osx-ppc"
4089 end
4090 elseif name=="sunos" then
4091 architecture=architecture or resultof("uname -m") or ""
4092 if find(architecture,"sparc") then
4093 bits,platform=32,"solaris-sparc"
4094 else
4095 bits,platform=32,"solaris-intel"
4096 end
4097 elseif name=="freebsd" then
4098 architecture=architecture or os.getenv("MACHTYPE") or resultof("uname -m") or ""
4099 if find(architecture,"amd64") or find(architecture,"AMD64") then
4100 bits,platform=64,"freebsd-amd64"
4101 else
4102 bits,platform=32,"freebsd"
4103 end
4104 elseif name=="kfreebsd" then
4105 architecture=architecture or os.getenv("HOSTTYPE") or resultof("uname -m") or ""
4106 if architecture=="x86_64" then
4107 bits,platform=64,"kfreebsd-amd64"
4108 else
4109 bits,platform=32,"kfreebsd-i386"
4110 end
4111 else
4112 architecture=architecture or resultof("uname -m") or ""
4113 if find(architecture,"aarch64") then
4114 bits,platform="linux-aarch64"
4115 elseif find(architecture,"armv7l") then
4116 bits,platform=32,"linux-armhf"
4117 elseif find(architecture,"mips64") or find(architecture,"mips64el") then
4118 bits,platform=64,"linux-mipsel"
4119 elseif find(architecture,"mipsel") or find(architecture,"mips") then
4120 bits,platform=32,"linux-mipsel"
4121 else
4122 bits,platform=64,"linux-64"
4123 end
4124 end
4125 os.setenv("MTX_PLATFORM",platform)
4126 os.setenv("MTX_BITS",bits)
4127 os.platform=platform
4128 os.bits=bits
4129 os.newline=name=="windows" and "\013\010" or "\010"
4130end
4131do
4132 local t={ 8,9,"a","b" }
4133 function os.uuid()
4134 return format("%04x%04x-4%03x-%s%03x-%04x-%04x%04x%04x",
4135 random(0xFFFF),random(0xFFFF),
4136 random(0x0FFF),
4137 t[ceil(random(4))] or 8,random(0x0FFF),
4138 random(0xFFFF),
4139 random(0xFFFF),random(0xFFFF),random(0xFFFF)
4140 )
4141 end
4142end
4143do
4144 local hour,min
4145 function os.timezone(difference)
4146 if not hour then
4147 local current=time()
4148 local utcdate=date("!*t",current)
4149 local localdate=date("*t",current)
4150 localdate.isdst=false
4151 local timediff=difftime(time(localdate),time(utcdate))
4152 hour,min=modf(timediff/3600)
4153 min=min*60
4154 end
4155 if difference then
4156 return hour,min
4157 else
4158 return format("%+03d:%02d",hour,min)
4159 end
4160 end
4161 local timeformat=format("%%s%s",os.timezone())
4162 local dateformat="%Y-%m-%d %H:%M:%S"
4163 local lasttime=nil
4164 local lastdate=nil
4165 function os.fulltime(t,default)
4166 t=t and tonumber(t) or 0
4167 if t>0 then
4168 elseif default then
4169 return default
4170 else
4171 t=time()
4172 end
4173 if t~=lasttime then
4174 lasttime=t
4175 lastdate=format(timeformat,date(dateformat))
4176 end
4177 return lastdate
4178 end
4179 local dateformat="%Y-%m-%d %H:%M:%S"
4180 local lasttime=nil
4181 local lastdate=nil
4182 function os.localtime(t,default)
4183 t=t and tonumber(t) or 0
4184 if t>0 then
4185 elseif default then
4186 return default
4187 else
4188 t=time()
4189 end
4190 if t~=lasttime then
4191 lasttime=t
4192 lastdate=date(dateformat,t)
4193 end
4194 return lastdate
4195 end
4196 function os.converttime(t,default)
4197 local t=tonumber(t)
4198 if t and t>0 then
4199 return date(dateformat,t)
4200 else
4201 return default or "-"
4202 end
4203 end
4204 function os.today()
4205 return date("!*t")
4206 end
4207 function os.now()
4208 return date("!%Y-%m-%d %H:%M:%S")
4209 end
4210end
4211do
4212 local cache={}
4213 local function which(filename)
4214 local fullname=cache[filename]
4215 if fullname==nil then
4216 local suffix=file.suffix(filename)
4217 local suffixes=suffix=="" and os.binsuffixes or { suffix }
4218 for directory in gmatch(os.getenv("PATH"),"[^"..io.pathseparator.."]+") do
4219 local df=file.join(directory,filename)
4220 for i=1,#suffixes do
4221 local dfs=file.addsuffix(df,suffixes[i])
4222 if io.exists(dfs) then
4223 fullname=dfs
4224 break
4225 end
4226 end
4227 end
4228 if not fullname then
4229 fullname=false
4230 end
4231 cache[filename]=fullname
4232 end
4233 return fullname
4234 end
4235 os.which=which
4236 os.where=which
4237end
4238if not os.sleep then
4239 local socket=socket
4240 function os.sleep(n)
4241 if not socket then
4242 socket=require("socket")
4243 end
4244 socket.sleep(n)
4245 end
4246end
4247do
4248 local function isleapyear(year)
4249 return (year%4==0) and (year%100~=0 or year%400==0)
4250 end
4251 os.isleapyear=isleapyear
4252 local days={ 31,28,31,30,31,30,31,31,30,31,30,31 }
4253 local function nofdays(year,month,day)
4254 if not month then
4255 return isleapyear(year) and 365 or 364
4256 elseif not day then
4257 return month==2 and isleapyear(year) and 29 or days[month]
4258 else
4259 for i=1,month-1 do
4260 day=day+days[i]
4261 end
4262 if month>2 and isleapyear(year) then
4263 day=day+1
4264 end
4265 return day
4266 end
4267 end
4268 os.nofdays=nofdays
4269 function os.weekday(day,month,year)
4270 return date("%w",time { year=year,month=month,day=day })+1
4271 end
4272 function os.validdate(year,month,day)
4273 if month<1 then
4274 month=1
4275 elseif month>12 then
4276 month=12
4277 end
4278 if day<1 then
4279 day=1
4280 else
4281 local max=nofdays(year,month)
4282 if day>max then
4283 day=max
4284 end
4285 end
4286 return year,month,day
4287 end
4288 function os.date(fmt,...)
4289 if not fmt then
4290 fmt="%Y-%m-%d %H:%M"
4291 end
4292 return date(fmt,...)
4293 end
4294end
4295do
4296 local osexit=os.exit
4297 local exitcode=nil
4298 function os.setexitcode(code)
4299 exitcode=code
4300 end
4301 function os.exit(c)
4302 if exitcode~=nil then
4303 return osexit(exitcode)
4304 end
4305 if c~=nil then
4306 return osexit(c)
4307 end
4308 return osexit()
4309 end
4310end
4311
4312
4313end
4314
4315do
4316
4317package.loaded["l-file"] = package.loaded["l-file"] or true
4318
4319
4320
4321if not modules then modules={} end modules ['l-file']={
4322 version=1.001,
4323 comment="companion to luat-lib.mkiv",
4324 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
4325 copyright="PRAGMA ADE / ConTeXt Development Team",
4326 license="see context related readme files"
4327}
4328file=file or {}
4329local file=file
4330if not lfs then
4331 lfs=optionalrequire("lfs")
4332end
4333local insert,concat=table.insert,table.concat
4334local match,find,gmatch=string.match,string.find,string.gmatch
4335local lpegmatch=lpeg.match
4336local getcurrentdir,attributes=lfs.currentdir,lfs.attributes
4337local checkedsplit=string.checkedsplit
4338local P,R,S,C,Cs,Cp,Cc,Ct=lpeg.P,lpeg.R,lpeg.S,lpeg.C,lpeg.Cs,lpeg.Cp,lpeg.Cc,lpeg.Ct
4339local attributes=lfs.attributes
4340function lfs.isdir(name)
4341 if name then
4342 return attributes(name,"mode")=="directory"
4343 end
4344end
4345function lfs.isfile(name)
4346 if name then
4347 local a=attributes(name,"mode")
4348 return a=="file" or a=="link" or nil
4349 end
4350end
4351function lfs.isfound(name)
4352 if name then
4353 local a=attributes(name,"mode")
4354 return (a=="file" or a=="link") and name or nil
4355 end
4356end
4357function lfs.modification(name)
4358 return name and attributes(name,"modification") or nil
4359end
4360if sandbox then
4361 sandbox.redefine(lfs.isfile,"lfs.isfile")
4362 sandbox.redefine(lfs.isdir,"lfs.isdir")
4363 sandbox.redefine(lfs.isfound,"lfs.isfound")
4364end
4365local colon=P(":")
4366local period=P(".")
4367local periods=P("..")
4368local fwslash=P("/")
4369local bwslash=P("\\")
4370local slashes=S("\\/")
4371local noperiod=1-period
4372local noslashes=1-slashes
4373local name=noperiod^1
4374local suffix=period/""*(1-period-slashes)^1*-1
4375local pattern=C((1-(slashes^1*noslashes^1*-1))^1)*P(1)
4376local function pathpart(name,default)
4377 return name and lpegmatch(pattern,name) or default or ""
4378end
4379local pattern=(noslashes^0*slashes)^1*C(noslashes^1)*-1
4380local function basename(name)
4381 return name and lpegmatch(pattern,name) or name
4382end
4383local pattern=(noslashes^0*slashes^1)^0*Cs((1-suffix)^1)*suffix^0
4384local function nameonly(name)
4385 return name and lpegmatch(pattern,name) or name
4386end
4387local pattern=(noslashes^0*slashes)^0*(noperiod^1*period)^1*C(noperiod^1)*-1
4388local function suffixonly(name)
4389 return name and lpegmatch(pattern,name) or ""
4390end
4391local pattern=(noslashes^0*slashes)^0*noperiod^1*((period*C(noperiod^1))^1)*-1+Cc("")
4392local function suffixesonly(name)
4393 if name then
4394 return lpegmatch(pattern,name)
4395 else
4396 return ""
4397 end
4398end
4399file.pathpart=pathpart
4400file.basename=basename
4401file.nameonly=nameonly
4402file.suffixonly=suffixonly
4403file.suffix=suffixonly
4404file.suffixesonly=suffixesonly
4405file.suffixes=suffixesonly
4406file.dirname=pathpart
4407file.extname=suffixonly
4408local drive=C(R("az","AZ"))*colon
4409local path=C((noslashes^0*slashes)^0)
4410local suffix=period*C(P(1-period)^0*P(-1))
4411local base=C((1-suffix)^0)
4412local rest=C(P(1)^0)
4413drive=drive+Cc("")
4414path=path+Cc("")
4415base=base+Cc("")
4416suffix=suffix+Cc("")
4417local pattern_a=drive*path*base*suffix
4418local pattern_b=path*base*suffix
4419local pattern_c=C(drive*path)*C(base*suffix)
4420local pattern_d=path*rest
4421function file.splitname(str,splitdrive)
4422 if not str then
4423 elseif splitdrive then
4424 return lpegmatch(pattern_a,str)
4425 else
4426 return lpegmatch(pattern_b,str)
4427 end
4428end
4429function file.splitbase(str)
4430 if str then
4431 return lpegmatch(pattern_d,str)
4432 else
4433 return "",str
4434 end
4435end
4436function file.nametotable(str,splitdrive)
4437 if str then
4438 local path,drive,subpath,name,base,suffix=lpegmatch(pattern_c,str)
4439 if splitdrive then
4440 return {
4441 path=path,
4442 drive=drive,
4443 subpath=subpath,
4444 name=name,
4445 base=base,
4446 suffix=suffix,
4447 }
4448 else
4449 return {
4450 path=path,
4451 name=name,
4452 base=base,
4453 suffix=suffix,
4454 }
4455 end
4456 end
4457end
4458local pattern=Cs(((period*(1-period-slashes)^1*-1)/""+1)^1)
4459function file.removesuffix(name)
4460 return name and lpegmatch(pattern,name)
4461end
4462local suffix=period/""*(1-period-slashes)^1*-1
4463local pattern=Cs((noslashes^0*slashes^1)^0*((1-suffix)^1))*Cs(suffix)
4464function file.addsuffix(filename,suffix,criterium)
4465 if not filename or not suffix or suffix=="" then
4466 return filename
4467 elseif criterium==true then
4468 return filename.."."..suffix
4469 elseif not criterium then
4470 local n,s=lpegmatch(pattern,filename)
4471 if not s or s=="" then
4472 return filename.."."..suffix
4473 else
4474 return filename
4475 end
4476 else
4477 local n,s=lpegmatch(pattern,filename)
4478 if s and s~="" then
4479 local t=type(criterium)
4480 if t=="table" then
4481 for i=1,#criterium do
4482 if s==criterium[i] then
4483 return filename
4484 end
4485 end
4486 elseif t=="string" then
4487 if s==criterium then
4488 return filename
4489 end
4490 end
4491 end
4492 return (n or filename).."."..suffix
4493 end
4494end
4495local suffix=period*(1-period-slashes)^1*-1
4496local pattern=Cs((1-suffix)^0)
4497function file.replacesuffix(name,suffix)
4498 if name and suffix and suffix~="" then
4499 return lpegmatch(pattern,name).."."..suffix
4500 else
4501 return name
4502 end
4503end
4504local reslasher=lpeg.replacer(P("\\"),"/")
4505function file.reslash(str)
4506 return str and lpegmatch(reslasher,str)
4507end
4508if lfs.isreadablefile and lfs.iswritablefile then
4509 file.is_readable=lfs.isreadablefile
4510 file.is_writable=lfs.iswritablefile
4511else
4512 function file.is_writable(name)
4513 if not name then
4514 elseif lfs.isdir(name) then
4515 name=name.."/m_t_x_t_e_s_t.tmp"
4516 local f=io.open(name,"wb")
4517 if f then
4518 f:close()
4519 os.remove(name)
4520 return true
4521 end
4522 elseif lfs.isfile(name) then
4523 local f=io.open(name,"ab")
4524 if f then
4525 f:close()
4526 return true
4527 end
4528 else
4529 local f=io.open(name,"ab")
4530 if f then
4531 f:close()
4532 os.remove(name)
4533 return true
4534 end
4535 end
4536 return false
4537 end
4538 local readable=P("r")*Cc(true)
4539 function file.is_readable(name)
4540 if name then
4541 local a=attributes(name)
4542 return a and lpegmatch(readable,a.permissions) or false
4543 else
4544 return false
4545 end
4546 end
4547end
4548file.isreadable=file.is_readable
4549file.iswritable=file.is_writable
4550function file.size(name)
4551 if name then
4552 local a=attributes(name)
4553 return a and a.size or 0
4554 else
4555 return 0
4556 end
4557end
4558function file.splitpath(str,separator)
4559 return str and checkedsplit(lpegmatch(reslasher,str),separator or io.pathseparator)
4560end
4561function file.joinpath(tab,separator)
4562 return tab and concat(tab,separator or io.pathseparator)
4563end
4564local someslash=S("\\/")
4565local stripper=Cs(P(fwslash)^0/""*reslasher)
4566local isnetwork=someslash*someslash*(1-someslash)+(1-fwslash-colon)^1*colon
4567local isroot=fwslash^1*-1
4568local hasroot=fwslash^1
4569local reslasher=lpeg.replacer(S("\\/"),"/")
4570local deslasher=lpeg.replacer(S("\\/")^1,"/")
4571function file.join(one,two,three,...)
4572 if not two then
4573 return one=="" and one or lpegmatch(reslasher,one)
4574 end
4575 if not one or one=="" then
4576 return lpegmatch(stripper,three and concat({ two,three,... },"/") or two)
4577 end
4578 if lpegmatch(isnetwork,one) then
4579 local one=lpegmatch(reslasher,one)
4580 local two=lpegmatch(deslasher,three and concat({ two,three,... },"/") or two)
4581 if lpegmatch(hasroot,two) then
4582 return one..two
4583 else
4584 return one.."/"..two
4585 end
4586 elseif lpegmatch(isroot,one) then
4587 local two=lpegmatch(deslasher,three and concat({ two,three,... },"/") or two)
4588 if lpegmatch(hasroot,two) then
4589 return two
4590 else
4591 return "/"..two
4592 end
4593 else
4594 return lpegmatch(deslasher,concat({ one,two,three,... },"/"))
4595 end
4596end
4597local drivespec=R("az","AZ")^1*colon
4598local anchors=fwslash+drivespec
4599local untouched=periods+(1-period)^1*P(-1)
4600local mswindrive=Cs(drivespec*(bwslash/"/"+fwslash)^0)
4601local mswinuncpath=(bwslash+fwslash)*(bwslash+fwslash)*Cc("//")
4602local splitstarter=(mswindrive+mswinuncpath+Cc(false))*Ct(lpeg.splitat(S("/\\")^1))
4603local absolute=fwslash
4604function file.collapsepath(str,anchor)
4605 if not str then
4606 return
4607 end
4608 if anchor==true and not lpegmatch(anchors,str) then
4609 str=getcurrentdir().."/"..str
4610 end
4611 if str=="" or str=="." then
4612 return "."
4613 elseif lpegmatch(untouched,str) then
4614 return lpegmatch(reslasher,str)
4615 end
4616 local starter,oldelements=lpegmatch(splitstarter,str)
4617 local newelements={}
4618 local i=#oldelements
4619 while i>0 do
4620 local element=oldelements[i]
4621 if element=='.' then
4622 elseif element=='..' then
4623 local n=i-1
4624 while n>0 do
4625 local element=oldelements[n]
4626 if element~='..' and element~='.' then
4627 oldelements[n]='.'
4628 break
4629 else
4630 n=n-1
4631 end
4632 end
4633 if n<1 then
4634 insert(newelements,1,'..')
4635 end
4636 elseif element~="" then
4637 insert(newelements,1,element)
4638 end
4639 i=i-1
4640 end
4641 if #newelements==0 then
4642 return starter or "."
4643 elseif starter then
4644 return starter..concat(newelements,'/')
4645 elseif lpegmatch(absolute,str) then
4646 return "/"..concat(newelements,'/')
4647 else
4648 newelements=concat(newelements,'/')
4649 if anchor=="." and find(str,"^%./") then
4650 return "./"..newelements
4651 else
4652 return newelements
4653 end
4654 end
4655end
4656local validchars=R("az","09","AZ","--","..")
4657local pattern_a=lpeg.replacer(1-validchars)
4658local pattern_a=Cs((validchars+P(1)/"-")^1)
4659local whatever=P("-")^0/""
4660local pattern_b=Cs(whatever*(1-whatever*-1)^1)
4661function file.robustname(str,strict)
4662 if str then
4663 str=lpegmatch(pattern_a,str) or str
4664 if strict then
4665 return lpegmatch(pattern_b,str) or str
4666 else
4667 return str
4668 end
4669 end
4670end
4671local loaddata=io.loaddata
4672local savedata=io.savedata
4673file.readdata=loaddata
4674file.savedata=savedata
4675function file.copy(oldname,newname)
4676 if oldname and newname then
4677 local data=loaddata(oldname)
4678 if data and data~="" then
4679 savedata(newname,data)
4680 end
4681 end
4682end
4683local letter=R("az","AZ")+S("_-+")
4684local separator=P("://")
4685local qualified=period^0*fwslash+letter*colon+letter^1*separator+letter^1*fwslash
4686local rootbased=fwslash+letter*colon
4687lpeg.patterns.qualified=qualified
4688lpeg.patterns.rootbased=rootbased
4689function file.is_qualified_path(filename)
4690 return filename and lpegmatch(qualified,filename)~=nil
4691end
4692function file.is_rootbased_path(filename)
4693 return filename and lpegmatch(rootbased,filename)~=nil
4694end
4695function file.strip(name,dir)
4696 if name then
4697 local b,a=match(name,"^(.-)"..dir.."(.*)$")
4698 return a~="" and a or name
4699 end
4700end
4701function lfs.mkdirs(path)
4702 local full=""
4703 for sub in gmatch(path,"(/*[^\\/]+)") do
4704 full=full..sub
4705 lfs.mkdir(full)
4706 end
4707end
4708function file.withinbase(path)
4709 local l=0
4710 if not find(path,"^/") then
4711 path="/"..path
4712 end
4713 for dir in gmatch(path,"/([^/]+)") do
4714 if dir==".." then
4715 l=l-1
4716 elseif dir~="." then
4717 l=l+1
4718 end
4719 if l<0 then
4720 return false
4721 end
4722 end
4723 return true
4724end
4725do
4726 local symlinktarget=lfs.symlinktarget
4727 local symlinkattributes=lfs.symlinkattributes
4728 if symlinktarget then
4729 function lfs.readlink(name)
4730 local target=symlinktarget(name)
4731 return name~=target and name or nil
4732 end
4733 elseif symlinkattributes then
4734 function lfs.readlink(name)
4735 return symlinkattributes(name,"target") or nil
4736 end
4737 else
4738 function lfs.readlink(name)
4739 return nil
4740 end
4741 end
4742end
4743
4744
4745end
4746
4747do
4748
4749package.loaded["l-gzip"] = package.loaded["l-gzip"] or true
4750
4751
4752
4753if not modules then modules={} end modules ['l-gzip']={
4754 version=1.001,
4755 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
4756 copyright="PRAGMA ADE / ConTeXt Development Team",
4757 license="see context related readme files"
4758}
4759
4760
4761end
4762
4763do
4764
4765package.loaded["l-md5"] = package.loaded["l-md5"] or true
4766
4767
4768
4769if not modules then modules={} end modules ['l-md5']={
4770 version=1.001,
4771 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
4772 copyright="PRAGMA ADE / ConTeXt Development Team",
4773 license="see context related readme files"
4774}
4775if not md5 then
4776 md5=optionalrequire("md5")
4777end
4778if not md5 then
4779 md5={
4780 sum=function(str) print("error: md5 is not loaded (sum ignored)") return str end,
4781 sumhexa=function(str) print("error: md5 is not loaded (sumhexa ignored)") return str end,
4782 }
4783end
4784local md5,file=md5,file
4785local gsub=string.gsub
4786local modification,isfile,touch=lfs.modification,lfs.isfile,lfs.touch
4787local loaddata,savedata=io.loaddata,io.savedata
4788do
4789 local patterns=lpeg and lpeg.patterns
4790 if patterns then
4791 local bytestoHEX=patterns.bytestoHEX
4792 local bytestohex=patterns.bytestohex
4793 local bytestodec=patterns.bytestodec
4794 local lpegmatch=lpeg.match
4795 local md5sum=md5.sum
4796 if not md5.HEX then function md5.HEX(str) if str then return lpegmatch(bytestoHEX,md5sum(str)) end end end
4797 if not md5.hex then function md5.hex(str) if str then return lpegmatch(bytestohex,md5sum(str)) end end end
4798 if not md5.dec then function md5.dec(str) if str then return lpegmatch(bytestodec,md5sum(str)) end end end
4799 md5.sumhexa=md5.hex
4800 md5.sumHEXA=md5.HEX
4801 end
4802end
4803local md5HEX=md5.HEX
4804function file.needsupdating(oldname,newname,threshold)
4805 local oldtime=modification(oldname)
4806 if oldtime then
4807 local newtime=modification(newname)
4808 if not newtime then
4809 return true
4810 elseif newtime>=oldtime then
4811 return false
4812 elseif oldtime-newtime<(threshold or 1) then
4813 return false
4814 else
4815 return true
4816 end
4817 else
4818 return false
4819 end
4820end
4821file.needs_updating=file.needsupdating
4822function file.syncmtimes(oldname,newname)
4823 local oldtime=modification(oldname)
4824 if oldtime and isfile(newname) then
4825 touch(newname,oldtime,oldtime)
4826 end
4827end
4828local function checksum(name)
4829 if md5 then
4830 local data=loaddata(name)
4831 if data then
4832 return md5HEX(data)
4833 end
4834 end
4835 return nil
4836end
4837file.checksum=checksum
4838function file.loadchecksum(name)
4839 if md5 then
4840 local data=loaddata(name..".md5")
4841 return data and (gsub(data,"%s",""))
4842 end
4843 return nil
4844end
4845function file.savechecksum(name,checksum)
4846 if not checksum then checksum=checksum(name) end
4847 if checksum then
4848 savedata(name..".md5",checksum)
4849 return checksum
4850 end
4851 return nil
4852end
4853
4854
4855end
4856
4857do
4858
4859package.loaded["l-sha"] = package.loaded["l-sha"] or true
4860
4861
4862
4863if not modules then modules={} end modules ['l-sha']={
4864 version=1.001,
4865 comment="companion to luat-lib.mkiv",
4866 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
4867 copyright="PRAGMA ADE / ConTeXt Development Team",
4868 license="see context related readme files"
4869}
4870if sha2 then
4871 local lpegmatch=lpeg.match
4872 local lpegpatterns=lpeg.patterns
4873 local bytestohex=lpegpatterns.bytestohex
4874 local bytestoHEX=lpegpatterns.bytestoHEX
4875 local digest256=sha2.digest256
4876 local digest384=sha2.digest384
4877 local digest512=sha2.digest512
4878 sha2.hash256=function(str) return lpegmatch(bytestohex,digest256(str)) end
4879 sha2.hash384=function(str) return lpegmatch(bytestohex,digest384(str)) end
4880 sha2.hash512=function(str) return lpegmatch(bytestohex,digest512(str)) end
4881 sha2.HASH256=function(str) return lpegmatch(bytestoHEX,digest256(str)) end
4882 sha2.HASH384=function(str) return lpegmatch(bytestoHEX,digest384(str)) end
4883 sha2.HASH512=function(str) return lpegmatch(bytestoHEX,digest512(str)) end
4884end
4885
4886
4887end
4888
4889do
4890
4891package.loaded["l-url"] = package.loaded["l-url"] or true
4892
4893
4894
4895if not modules then modules={} end modules ['l-url']={
4896 version=1.001,
4897 comment="companion to luat-lib.mkiv",
4898 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
4899 copyright="PRAGMA ADE / ConTeXt Development Team",
4900 license="see context related readme files"
4901}
4902local char,format,byte=string.char,string.format,string.byte
4903local concat=table.concat
4904local tonumber,type,next=tonumber,type,next
4905local P,C,R,S,Cs,Cc,Ct,Cf,Cg,V=lpeg.P,lpeg.C,lpeg.R,lpeg.S,lpeg.Cs,lpeg.Cc,lpeg.Ct,lpeg.Cf,lpeg.Cg,lpeg.V
4906local lpegmatch,lpegpatterns,replacer=lpeg.match,lpeg.patterns,lpeg.replacer
4907local sortedhash=table.sortedhash
4908url=url or {}
4909local url=url
4910local unescapes={}
4911local escapes={}
4912setmetatable(unescapes,{ __index=function(t,k)
4913 local v=char(tonumber(k,16))
4914 t[k]=v
4915 return v
4916end })
4917setmetatable(escapes,{ __index=function(t,k)
4918 local v=format("%%%02X",byte(k))
4919 t[k]=v
4920 return v
4921end })
4922local colon=P(":")
4923local qmark=P("?")
4924local hash=P("#")
4925local slash=P("/")
4926local atsign=P("@")
4927local percent=P("%")
4928local endofstring=P(-1)
4929local hexdigit=R("09","AF","af")
4930local plus=P("+")
4931local nothing=Cc("")
4932local okay=R("09","AZ","az")+S("-_.,:=+*~!'()@&$")
4933local escapedchar=(percent*C(hexdigit*hexdigit))/unescapes
4934local unescapedchar=P(1)/escapes
4935local escaped=(plus/" ")+escapedchar
4936local noslash=P("/")/""
4937local plustospace=P("+")/" "
4938local decoder=Cs((
4939 plustospace+escapedchar+P("\r\n")/"\n"+P(1)
4940 )^0 )
4941local encoder=Cs((
4942 R("09","AZ","az")^1+S("-./_")^1+P(" ")/"+"+P("\n")/"\r\n"+unescapedchar
4943 )^0 )
4944lpegpatterns.urldecoder=decoder
4945lpegpatterns.urlencoder=encoder
4946function url.decode (str) return str and lpegmatch(decoder,str) or str end
4947function url.encode (str) return str and lpegmatch(encoder,str) or str end
4948function url.unescape(str) return str and lpegmatch(unescaper,str) or str end
4949local schemestr=Cs((escaped+(1-colon-slash-qmark-hash))^2)
4950local authoritystr=Cs((escaped+(1- slash-qmark-hash))^0)
4951local pathstr=Cs((escaped+(1- qmark-hash))^0)
4952local querystr=Cs(((1- hash))^0)
4953local fragmentstr=Cs((escaped+(1- endofstring))^0)
4954local scheme=schemestr*colon+nothing
4955local authority=slash*slash*authoritystr+nothing
4956local path=slash*pathstr+nothing
4957local query=qmark*querystr+nothing
4958local fragment=hash*fragmentstr+nothing
4959local validurl=scheme*authority*path*query*fragment
4960local parser=Ct(validurl)
4961lpegpatterns.url=validurl
4962lpegpatterns.urlsplitter=parser
4963local escaper=Cs((R("09","AZ","az")^1+P(" ")/"%%20"+S("-./_:")^1+P(1)/escapes)^0)
4964local unescaper=Cs((escapedchar+1)^0)
4965local getcleaner=Cs((P("+++")/"%%2B"+P("+")/"%%20"+P(1))^1)
4966lpegpatterns.urlunescaped=escapedchar
4967lpegpatterns.urlescaper=escaper
4968lpegpatterns.urlunescaper=unescaper
4969lpegpatterns.urlgetcleaner=getcleaner
4970function url.unescapeget(str)
4971 return lpegmatch(getcleaner,str)
4972end
4973local function split(str)
4974 return (type(str)=="string" and lpegmatch(parser,str)) or str
4975end
4976local isscheme=schemestr*colon*slash*slash
4977local function hasscheme(str)
4978 if str then
4979 local scheme=lpegmatch(isscheme,str)
4980 return scheme~="" and scheme or false
4981 else
4982 return false
4983 end
4984end
4985local rootletter=R("az","AZ")+S("_-+")
4986local separator=P("://")
4987local qualified=P(".")^0*P("/")+rootletter*P(":")+rootletter^1*separator+rootletter^1*P("/")
4988local rootbased=P("/")+rootletter*P(":")
4989local barswapper=replacer("|",":")
4990local backslashswapper=replacer("\\","/")
4991local equal=P("=")
4992local amp=P("&")
4993local key=Cs(((plustospace+escapedchar+1)-equal )^0)
4994local value=Cs(((plustospace+escapedchar+1)-amp-endofstring)^0)
4995local splitquery=Cf (Ct("")*P { "sequence",
4996 sequence=V("pair")*(amp*V("pair"))^0,
4997 pair=Cg(key*equal*value),
4998},rawset)
4999local userpart=(1-atsign-colon)^1
5000local serverpart=(1-colon)^1
5001local splitauthority=((Cs(userpart)*colon*Cs(userpart)+Cs(userpart)*Cc(nil))*atsign+Cc(nil)*Cc(nil))*Cs(serverpart)*(colon*(serverpart/tonumber)+Cc(nil))
5002local function hashed(str)
5003 if not str or str=="" then
5004 return {
5005 scheme="invalid",
5006 original=str,
5007 }
5008 end
5009 local detailed=split(str)
5010 local rawscheme=""
5011 local rawquery=""
5012 local somescheme=false
5013 local somequery=false
5014 if detailed then
5015 rawscheme=detailed[1]
5016 rawquery=detailed[4]
5017 somescheme=rawscheme~=""
5018 somequery=rawquery~=""
5019 end
5020 if not somescheme and not somequery then
5021 return {
5022 scheme="file",
5023 authority="",
5024 path=str,
5025 query="",
5026 fragment="",
5027 original=str,
5028 noscheme=true,
5029 filename=str,
5030 }
5031 end
5032 local authority=detailed[2]
5033 local path=detailed[3]
5034 local filename
5035 local username
5036 local password
5037 local host
5038 local port
5039 if authority~="" then
5040 username,password,host,port=lpegmatch(splitauthority,authority)
5041 end
5042 if authority=="" then
5043 filename=path
5044 elseif path=="" then
5045 filename=""
5046 else
5047 filename=authority.."/"..path
5048 end
5049 return {
5050 scheme=rawscheme,
5051 authority=authority,
5052 path=path,
5053 query=lpegmatch(unescaper,rawquery),
5054 queries=lpegmatch(splitquery,rawquery),
5055 fragment=detailed[5],
5056 original=str,
5057 noscheme=false,
5058 filename=filename,
5059 host=host,
5060 port=port,
5061 }
5062end
5063url.split=split
5064url.hasscheme=hasscheme
5065url.hashed=hashed
5066function url.addscheme(str,scheme)
5067 if hasscheme(str) then
5068 return str
5069 elseif not scheme then
5070 return "file:///"..str
5071 else
5072 return scheme..":///"..str
5073 end
5074end
5075function url.construct(hash)
5076 local result,r={},0
5077 local scheme=hash.scheme
5078 local authority=hash.authority
5079 local path=hash.path
5080 local queries=hash.queries
5081 local fragment=hash.fragment
5082 if scheme and scheme~="" then
5083 r=r+1;result[r]=lpegmatch(escaper,scheme)
5084 r=r+1;result[r]="://"
5085 end
5086 if authority and authority~="" then
5087 r=r+1;result[r]=lpegmatch(escaper,authority)
5088 end
5089 if path and path~="" then
5090 r=r+1;result[r]="/"
5091 r=r+1;result[r]=lpegmatch(escaper,path)
5092 end
5093 if queries then
5094 local done=false
5095 for k,v in sortedhash(queries) do
5096 r=r+1;result[r]=done and "&" or "?"
5097 r=r+1;result[r]=lpegmatch(escaper,k)
5098 r=r+1;result[r]="="
5099 r=r+1;result[r]=lpegmatch(escaper,v)
5100 done=true
5101 end
5102 end
5103 if fragment and fragment~="" then
5104 r=r+1;result[r]="#"
5105 r=r+1;result[r]=lpegmatch(escaper,fragment)
5106 end
5107 return concat(result)
5108end
5109local pattern=Cs(slash^-1/""*R("az","AZ")*((S(":|")/":")+P(":"))*slash*P(1)^0)
5110function url.filename(filename)
5111 local spec=hashed(filename)
5112 local path=spec.path
5113 return (spec.scheme=="file" and path and lpegmatch(pattern,path)) or filename
5114end
5115local function escapestring(str)
5116 return lpegmatch(escaper,str)
5117end
5118url.escape=escapestring
5119function url.query(str)
5120 if type(str)=="string" then
5121 return lpegmatch(splitquery,str) or ""
5122 else
5123 return str
5124 end
5125end
5126function url.toquery(data)
5127 local td=type(data)
5128 if td=="string" then
5129 return #str and escape(data) or nil
5130 elseif td=="table" then
5131 if next(data) then
5132 local t={}
5133 for k,v in next,data do
5134 t[#t+1]=format("%s=%s",k,escapestring(v))
5135 end
5136 return concat(t,"&")
5137 end
5138 else
5139 end
5140end
5141local pattern=Cs(noslash^0*(1-noslash*P(-1))^0)
5142function url.barepath(path)
5143 if not path or path=="" then
5144 return ""
5145 else
5146 return lpegmatch(pattern,path)
5147 end
5148end
5149
5150
5151end
5152
5153do
5154
5155package.loaded["l-dir"] = package.loaded["l-dir"] or true
5156
5157
5158
5159if not modules then modules={} end modules ['l-dir']={
5160 version=1.001,
5161 comment="companion to luat-lib.mkiv",
5162 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
5163 copyright="PRAGMA ADE / ConTeXt Development Team",
5164 license="see context related readme files"
5165}
5166local type,select=type,select
5167local find,gmatch,match,gsub,sub=string.find,string.gmatch,string.match,string.gsub,string.sub
5168local concat,insert,remove,unpack=table.concat,table.insert,table.remove,table.unpack
5169local lpegmatch=lpeg.match
5170local P,S,R,C,Cc,Cs,Ct,Cv,V=lpeg.P,lpeg.S,lpeg.R,lpeg.C,lpeg.Cc,lpeg.Cs,lpeg.Ct,lpeg.Cv,lpeg.V
5171dir=dir or {}
5172local dir=dir
5173local lfs=lfs
5174local attributes=lfs.attributes
5175local scandir=lfs.dir
5176local isdir=lfs.isdir
5177local isfile=lfs.isfile
5178local currentdir=lfs.currentdir
5179local chdir=lfs.chdir
5180local mkdir=lfs.mkdir
5181local onwindows=os.type=="windows" or find(os.getenv("PATH"),";",1,true)
5182if onwindows then
5183 local tricky=S("/\\")*P(-1)
5184 isdir=function(name)
5185 if lpegmatch(tricky,name) then
5186 return attributes(name,"mode")=="directory"
5187 else
5188 return attributes(name.."/.","mode")=="directory"
5189 end
5190 end
5191 isfile=function(name)
5192 return attributes(name,"mode")=="file"
5193 end
5194 lfs.isdir=isdir
5195 lfs.isfile=isfile
5196else
5197 isdir=function(name)
5198 return attributes(name,"mode")=="directory"
5199 end
5200 isfile=function(name)
5201 return attributes(name,"mode")=="file"
5202 end
5203 lfs.isdir=isdir
5204 lfs.isfile=isfile
5205end
5206local isreadable=file.isreadable
5207local walkdir=function(p,...)
5208 if isreadable(p.."/.") then
5209 return scandir(p,...)
5210 else
5211 return function() end
5212 end
5213end
5214lfs.walkdir=walkdir
5215function dir.current()
5216 return (gsub(currentdir(),"\\","/"))
5217end
5218local function glob_pattern_function(path,patt,recurse,action)
5219 if isdir(path) then
5220 local usedpath
5221 if path=="/" then
5222 usedpath="/."
5223 elseif not find(path,"/$") then
5224 usedpath=path.."/."
5225 path=path.."/"
5226 else
5227 usedpath=path
5228 end
5229 local dirs
5230 local nofdirs=0
5231 for name,mode,size,time in walkdir(usedpath) do
5232 if name~="." and name~=".." then
5233 local full=path..name
5234 if mode==nil then
5235 mode=attributes(full,'mode')
5236 end
5237 if mode=='file' then
5238 if not patt or find(full,patt) then
5239 action(full,size,time)
5240 end
5241 elseif recurse and mode=="directory" then
5242 if dirs then
5243 nofdirs=nofdirs+1
5244 dirs[nofdirs]=full
5245 else
5246 nofdirs=1
5247 dirs={ full }
5248 end
5249 end
5250 end
5251 end
5252 if dirs then
5253 for i=1,nofdirs do
5254 glob_pattern_function(dirs[i],patt,recurse,action)
5255 end
5256 end
5257 end
5258end
5259local function glob_pattern_table(path,patt,recurse,result)
5260 if not result then
5261 result={}
5262 end
5263 local usedpath
5264 if path=="/" then
5265 usedpath="/."
5266 elseif not find(path,"/$") then
5267 usedpath=path.."/."
5268 path=path.."/"
5269 else
5270 usedpath=path
5271 end
5272 local dirs
5273 local nofdirs=0
5274 local noffiles=#result
5275 for name,mode in walkdir(usedpath) do
5276 if name~="." and name~=".." then
5277 local full=path..name
5278 if mode==nil then
5279 mode=attributes(full,'mode')
5280 end
5281 if mode=='file' then
5282 if not patt or find(full,patt) then
5283 noffiles=noffiles+1
5284 result[noffiles]=full
5285 end
5286 elseif recurse and mode=="directory" then
5287 if dirs then
5288 nofdirs=nofdirs+1
5289 dirs[nofdirs]=full
5290 else
5291 nofdirs=1
5292 dirs={ full }
5293 end
5294 end
5295 end
5296 end
5297 if dirs then
5298 for i=1,nofdirs do
5299 glob_pattern_table(dirs[i],patt,recurse,result)
5300 end
5301 end
5302 return result
5303end
5304local function globpattern(path,patt,recurse,method)
5305 local kind=type(method)
5306 if patt and sub(patt,1,-3)==path then
5307 patt=false
5308 end
5309 local okay=isdir(path)
5310 if kind=="function" then
5311 return okay and glob_pattern_function(path,patt,recurse,method) or {}
5312 elseif kind=="table" then
5313 return okay and glob_pattern_table(path,patt,recurse,method) or method
5314 else
5315 return okay and glob_pattern_table(path,patt,recurse,{}) or {}
5316 end
5317end
5318dir.globpattern=globpattern
5319local function collectpattern(path,patt,recurse,result)
5320 local ok,scanner
5321 result=result or {}
5322 if path=="/" then
5323 ok,scanner,first=xpcall(function() return walkdir(path..".") end,function() end)
5324 else
5325 ok,scanner,first=xpcall(function() return walkdir(path) end,function() end)
5326 end
5327 if ok and type(scanner)=="function" then
5328 if not find(path,"/$") then
5329 path=path..'/'
5330 end
5331 for name in scanner,first do
5332 if name=="." then
5333 elseif name==".." then
5334 else
5335 local full=path..name
5336 local attr=attributes(full)
5337 local mode=attr.mode
5338 if mode=='file' then
5339 if find(full,patt) then
5340 result[name]=attr
5341 end
5342 elseif recurse and mode=="directory" then
5343 attr.list=collectpattern(full,patt,recurse)
5344 result[name]=attr
5345 end
5346 end
5347 end
5348 end
5349 return result
5350end
5351dir.collectpattern=collectpattern
5352local separator,pattern
5353if onwindows then
5354 local slash=S("/\\")/"/"
5355 pattern={
5356 (Cs(P(".")+slash^1)+Cs(R("az","AZ")*P(":")*slash^0)+Cc("./"))*V(2)*V(3),
5357 Cs(((1-S("*?/\\"))^0*slash)^0),
5358 Cs(P(1)^0)
5359 }
5360else
5361 pattern={
5362 (C(P(".")+P("/")^1)+Cc("./"))*V(2)*V(3),
5363 C(((1-S("*?/"))^0*P("/"))^0),
5364 C(P(1)^0)
5365 }
5366end
5367local filter=Cs ((
5368 P("**")/".*"+P("*")/"[^/]*"+P("?")/"[^/]"+P(".")/"%%."+P("+")/"%%+"+P("-")/"%%-"+P(1)
5369)^0 )
5370local function glob(str,t)
5371 if type(t)=="function" then
5372 if type(str)=="table" then
5373 for s=1,#str do
5374 glob(str[s],t)
5375 end
5376 elseif isfile(str) then
5377 t(str)
5378 else
5379 local root,path,base=lpegmatch(pattern,str)
5380 if root and path and base then
5381 local recurse=find(base,"**",1,true)
5382 local start=root..path
5383 local result=lpegmatch(filter,start..base)
5384 globpattern(start,result,recurse,t)
5385 end
5386 end
5387 else
5388 if type(str)=="table" then
5389 local t=t or {}
5390 for s=1,#str do
5391 glob(str[s],t)
5392 end
5393 return t
5394 elseif isfile(str) then
5395 if t then
5396 t[#t+1]=str
5397 return t
5398 else
5399 return { str }
5400 end
5401 else
5402 local root,path,base=lpegmatch(pattern,str)
5403 if root and path and base then
5404 local recurse=find(base,"**",1,true)
5405 local start=root..path
5406 local result=lpegmatch(filter,start..base)
5407 return globpattern(start,result,recurse,t)
5408 else
5409 return {}
5410 end
5411 end
5412 end
5413end
5414dir.glob=glob
5415local function globfiles(path,recurse,func,files)
5416 if type(func)=="string" then
5417 local s=func
5418 func=function(name) return find(name,s) end
5419 end
5420 files=files or {}
5421 local noffiles=#files
5422 for name,mode in walkdir(path) do
5423 if find(name,"^%.") then
5424 else
5425 if mode==nil then
5426 mode=attributes(name,'mode')
5427 end
5428 if mode=="directory" then
5429 if recurse then
5430 globfiles(path.."/"..name,recurse,func,files)
5431 end
5432 elseif mode=="file" then
5433 if not func or func(name) then
5434 noffiles=noffiles+1
5435 files[noffiles]=path.."/"..name
5436 end
5437 end
5438 end
5439 end
5440 return files
5441end
5442dir.globfiles=globfiles
5443local function globdirs(path,recurse,func,files)
5444 if type(func)=="string" then
5445 local s=func
5446 func=function(name) return find(name,s) end
5447 end
5448 files=files or {}
5449 local noffiles=#files
5450 for name,mode in walkdir(path) do
5451 if find(name,"^%.") then
5452 else
5453 if mode==nil then
5454 mode=attributes(name,'mode')
5455 end
5456 if mode=="directory" then
5457 if not func or func(name) then
5458 noffiles=noffiles+1
5459 files[noffiles]=path.."/"..name
5460 if recurse then
5461 globdirs(path.."/"..name,recurse,func,files)
5462 end
5463 end
5464 end
5465 end
5466 end
5467 return files
5468end
5469dir.globdirs=globdirs
5470function dir.ls(pattern)
5471 return concat(glob(pattern),"\n")
5472end
5473local make_indeed=true
5474if onwindows then
5475 function dir.mkdirs(...)
5476 local n=select("#",...)
5477 local str
5478 if n==1 then
5479 str=select(1,...)
5480 if isdir(str) then
5481 return str,true
5482 end
5483 else
5484 str=""
5485 for i=1,n do
5486 local s=select(i,...)
5487 if s=="" then
5488 elseif str=="" then
5489 str=s
5490 else
5491 str=str.."/"..s
5492 end
5493 end
5494 end
5495 local pth=""
5496 local drive=false
5497 local first,middle,last=match(str,"^(//)(//*)(.*)$")
5498 if first then
5499 else
5500 first,last=match(str,"^(//)/*(.-)$")
5501 if first then
5502 middle,last=match(str,"([^/]+)/+(.-)$")
5503 if middle then
5504 pth="//"..middle
5505 else
5506 pth="//"..last
5507 last=""
5508 end
5509 else
5510 first,middle,last=match(str,"^([a-zA-Z]:)(/*)(.-)$")
5511 if first then
5512 pth,drive=first..middle,true
5513 else
5514 middle,last=match(str,"^(/*)(.-)$")
5515 if not middle then
5516 last=str
5517 end
5518 end
5519 end
5520 end
5521 for s in gmatch(last,"[^/]+") do
5522 if pth=="" then
5523 pth=s
5524 elseif drive then
5525 pth,drive=pth..s,false
5526 else
5527 pth=pth.."/"..s
5528 end
5529 if make_indeed and not isdir(pth) then
5530 mkdir(pth)
5531 end
5532 end
5533 return pth,(isdir(pth)==true)
5534 end
5535else
5536 function dir.mkdirs(...)
5537 local n=select("#",...)
5538 local str,pth
5539 if n==1 then
5540 str=select(1,...)
5541 if isdir(str) then
5542 return str,true
5543 end
5544 else
5545 str=""
5546 for i=1,n do
5547 local s=select(i,...)
5548 if s and s~="" then
5549 if str~="" then
5550 str=str.."/"..s
5551 else
5552 str=s
5553 end
5554 end
5555 end
5556 end
5557 str=gsub(str,"/+","/")
5558 if find(str,"^/") then
5559 pth="/"
5560 for s in gmatch(str,"[^/]+") do
5561 local first=(pth=="/")
5562 if first then
5563 pth=pth..s
5564 else
5565 pth=pth.."/"..s
5566 end
5567 if make_indeed and not first and not isdir(pth) then
5568 mkdir(pth)
5569 end
5570 end
5571 else
5572 pth="."
5573 for s in gmatch(str,"[^/]+") do
5574 pth=pth.."/"..s
5575 if make_indeed and not isdir(pth) then
5576 mkdir(pth)
5577 end
5578 end
5579 end
5580 return pth,(isdir(pth)==true)
5581 end
5582end
5583dir.makedirs=dir.mkdirs
5584do
5585 local chdir=sandbox and sandbox.original(chdir) or chdir
5586 if onwindows then
5587 local xcurrentdir=dir.current
5588 function dir.expandname(str)
5589 local first,nothing,last=match(str,"^(//)(//*)(.*)$")
5590 if first then
5591 first=xcurrentdir().."/"
5592 end
5593 if not first then
5594 first,last=match(str,"^(//)/*(.*)$")
5595 end
5596 if not first then
5597 first,last=match(str,"^([a-zA-Z]:)(.*)$")
5598 if first and not find(last,"^/") then
5599 local d=currentdir()
5600 if chdir(first) then
5601 first=xcurrentdir()
5602 end
5603 chdir(d)
5604 end
5605 end
5606 if not first then
5607 first,last=xcurrentdir(),str
5608 end
5609 last=gsub(last,"//","/")
5610 last=gsub(last,"/%./","/")
5611 last=gsub(last,"^/*","")
5612 first=gsub(first,"/*$","")
5613 if last=="" or last=="." then
5614 return first
5615 else
5616 return first.."/"..last
5617 end
5618 end
5619 else
5620 function dir.expandname(str)
5621 if not find(str,"^/") then
5622 str=currentdir().."/"..str
5623 end
5624 str=gsub(str,"//","/")
5625 str=gsub(str,"/%./","/")
5626 str=gsub(str,"(.)/%.$","%1")
5627 return str
5628 end
5629 end
5630 function dir.expandlink(dir,report)
5631 local curdir=currentdir()
5632 local trace=type(report)=="function"
5633 if chdir(dir) then
5634 local newdir=currentdir()
5635 if newdir~=dir and trace then
5636 report("following symlink %a to %a",dir,newdir)
5637 end
5638 chdir(curdir)
5639 return newdir
5640 else
5641 if trace then
5642 report("unable to check path %a",dir)
5643 end
5644 return dir
5645 end
5646 end
5647end
5648file.expandname=dir.expandname
5649local stack={}
5650function dir.push(newdir)
5651 local curdir=currentdir()
5652 insert(stack,curdir)
5653 if newdir and newdir~="" and chdir(newdir) then
5654 return newdir
5655 else
5656 return curdir
5657 end
5658end
5659function dir.pop()
5660 local d=remove(stack)
5661 if d then
5662 chdir(d)
5663 end
5664 return d
5665end
5666local function found(...)
5667 for i=1,select("#",...) do
5668 local path=select(i,...)
5669 local kind=type(path)
5670 if kind=="string" then
5671 if isdir(path) then
5672 return path
5673 end
5674 elseif kind=="table" then
5675 local path=found(unpack(path))
5676 if path then
5677 return path
5678 end
5679 end
5680 end
5681end
5682dir.found=found
5683
5684
5685end
5686
5687do
5688
5689package.loaded["l-boolean"] = package.loaded["l-boolean"] or true
5690
5691
5692
5693if not modules then modules={} end modules ['l-boolean']={
5694 version=1.001,
5695 comment="companion to luat-lib.mkiv",
5696 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
5697 copyright="PRAGMA ADE / ConTeXt Development Team",
5698 license="see context related readme files"
5699}
5700local type,tonumber=type,tonumber
5701boolean=boolean or {}
5702local boolean=boolean
5703function boolean.tonumber(b)
5704 if b then return 1 else return 0 end
5705end
5706function toboolean(str,tolerant)
5707 if str==nil then
5708 return false
5709 elseif str==false then
5710 return false
5711 elseif str==true then
5712 return true
5713 elseif str=="true" then
5714 return true
5715 elseif str=="false" then
5716 return false
5717 elseif not tolerant then
5718 return false
5719 elseif str==0 then
5720 return false
5721 elseif (tonumber(str) or 0)>0 then
5722 return true
5723 else
5724 return str=="yes" or str=="on" or str=="t"
5725 end
5726end
5727string.toboolean=toboolean
5728function string.booleanstring(str)
5729 if str=="0" then
5730 return false
5731 elseif str=="1" then
5732 return true
5733 elseif str=="" then
5734 return false
5735 elseif str=="false" then
5736 return false
5737 elseif str=="true" then
5738 return true
5739 elseif (tonumber(str) or 0)>0 then
5740 return true
5741 else
5742 return str=="yes" or str=="on" or str=="t"
5743 end
5744end
5745function string.is_boolean(str,default,strict)
5746 if type(str)=="string" then
5747 if str=="true" or str=="yes" or str=="on" or str=="t" or (not strict and str=="1") then
5748 return true
5749 elseif str=="false" or str=="no" or str=="off" or str=="f" or (not strict and str=="0") then
5750 return false
5751 end
5752 end
5753 return default
5754end
5755
5756
5757end
5758
5759do
5760
5761package.loaded["l-unicode"] = package.loaded["l-unicode"] or true
5762
5763
5764
5765if not modules then modules={} end modules ['l-unicode']={
5766 version=1.001,
5767 optimize=true,
5768 comment="companion to luat-lib.mkiv",
5769 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
5770 copyright="PRAGMA ADE / ConTeXt Development Team",
5771 license="see context related readme files"
5772}
5773utf=utf or {}
5774unicode=nil
5775if not string.utfcharacters then
5776 local gmatch=string.gmatch
5777 function string.characters(str)
5778 return gmatch(str,".[\128-\191]*")
5779 end
5780end
5781utf.characters=string.utfcharacters
5782local type=type
5783local char,byte,format,sub,gmatch=string.char,string.byte,string.format,string.sub,string.gmatch
5784local concat=table.concat
5785local P,C,R,Cs,Ct,Cmt,Cc,Carg,Cp=lpeg.P,lpeg.C,lpeg.R,lpeg.Cs,lpeg.Ct,lpeg.Cmt,lpeg.Cc,lpeg.Carg,lpeg.Cp
5786local lpegmatch=lpeg.match
5787local patterns=lpeg.patterns
5788local tabletopattern=lpeg.utfchartabletopattern
5789local bytepairs=string.bytepairs
5790local finder=lpeg.finder
5791local replacer=lpeg.replacer
5792local p_utftype=patterns.utftype
5793local p_utfstricttype=patterns.utfstricttype
5794local p_utfoffset=patterns.utfoffset
5795local p_utf8character=patterns.utf8character
5796local p_utf8char=patterns.utf8char
5797local p_utf8byte=patterns.utf8byte
5798local p_utfbom=patterns.utfbom
5799local p_newline=patterns.newline
5800local p_whitespace=patterns.whitespace
5801if not utf.char then
5802 utf.char=string.utfcharacter or (utf8 and utf8.char)
5803 if not utf.char then
5804 local char=string.char
5805 if bit32 then
5806 local rshift=bit32.rshift
5807 function utf.char(n)
5808 if n<0x80 then
5809 return char(n)
5810 elseif n<0x800 then
5811 return char(
5812 0xC0+rshift(n,6),
5813 0x80+(n%0x40)
5814 )
5815 elseif n<0x10000 then
5816 return char(
5817 0xE0+rshift(n,12),
5818 0x80+(rshift(n,6)%0x40),
5819 0x80+(n%0x40)
5820 )
5821 elseif n<0x200000 then
5822 return char(
5823 0xF0+rshift(n,18),
5824 0x80+(rshift(n,12)%0x40),
5825 0x80+(rshift(n,6)%0x40),
5826 0x80+(n%0x40)
5827 )
5828 else
5829 return ""
5830 end
5831 end
5832 else
5833 local floor=math.floor
5834 function utf.char(n)
5835 if n<0x80 then
5836 return char(n)
5837 elseif n<0x800 then
5838 return char(
5839 0xC0+floor(n/0x40),
5840 0x80+(n%0x40)
5841 )
5842 elseif n<0x10000 then
5843 return char(
5844 0xE0+floor(n/0x1000),
5845 0x80+(floor(n/0x40)%0x40),
5846 0x80+(n%0x40)
5847 )
5848 elseif n<0x200000 then
5849 return char(
5850 0xF0+floor(n/0x40000),
5851 0x80+(floor(n/0x1000)%0x40),
5852 0x80+(floor(n/0x40)%0x40),
5853 0x80+(n%0x40)
5854 )
5855 else
5856 return ""
5857 end
5858 end
5859 end
5860 end
5861end
5862if not utf.byte then
5863 utf.byte=string.utfvalue or (utf8 and utf8.codepoint)
5864 if not utf.byte then
5865 function utf.byte(c)
5866 return lpegmatch(p_utf8byte,c)
5867 end
5868 end
5869end
5870local utfchar,utfbyte=utf.char,utf.byte
5871function utf.filetype(data)
5872 return data and lpegmatch(p_utftype,data) or "unknown"
5873end
5874local toentities=Cs (
5875 (
5876 patterns.utf8one+(
5877 patterns.utf8two+patterns.utf8three+patterns.utf8four
5878 )/function(s) local b=utfbyte(s) if b<127 then return s else return format("&#%X;",b) end end
5879 )^0
5880)
5881patterns.toentities=toentities
5882function utf.toentities(str)
5883 return lpegmatch(toentities,str)
5884end
5885local one=P(1)
5886local two=C(1)*C(1)
5887local four=C(R(utfchar(0xD8),utfchar(0xFF)))*C(1)*C(1)*C(1)
5888local pattern=P("\254\255")*Cs((
5889 four/function(a,b,c,d)
5890 local ab=0xFF*byte(a)+byte(b)
5891 local cd=0xFF*byte(c)+byte(d)
5892 return utfchar((ab-0xD800)*0x400+(cd-0xDC00)+0x10000)
5893 end+two/function(a,b)
5894 return utfchar(byte(a)*256+byte(b))
5895 end+one
5896 )^1 )+P("\255\254")*Cs((
5897 four/function(b,a,d,c)
5898 local ab=0xFF*byte(a)+byte(b)
5899 local cd=0xFF*byte(c)+byte(d)
5900 return utfchar((ab-0xD800)*0x400+(cd-0xDC00)+0x10000)
5901 end+two/function(b,a)
5902 return utfchar(byte(a)*256+byte(b))
5903 end+one
5904 )^1 )
5905function string.toutf(s)
5906 return lpegmatch(pattern,s) or s
5907end
5908local validatedutf=Cs (
5909 (
5910 patterns.utf8one+patterns.utf8two+patterns.utf8three+patterns.utf8four+P(1)/"�"
5911 )^0
5912)
5913patterns.validatedutf=validatedutf
5914function utf.is_valid(str)
5915 return type(str)=="string" and lpegmatch(validatedutf,str) or false
5916end
5917if not utf.len then
5918 utf.len=string.utflength or (utf8 and utf8.len)
5919 if not utf.len then
5920 local n,f=0,1
5921 local utfcharcounter=patterns.utfbom^-1*Cmt (
5922 Cc(1)*patterns.utf8one^1+Cc(2)*patterns.utf8two^1+Cc(3)*patterns.utf8three^1+Cc(4)*patterns.utf8four^1,
5923 function(_,t,d)
5924 n=n+(t-f)/d
5925 f=t
5926 return true
5927 end
5928 )^0
5929 function utf.len(str)
5930 n,f=0,1
5931 lpegmatch(utfcharcounter,str or "")
5932 return n
5933 end
5934 end
5935end
5936utf.length=utf.len
5937if not utf.sub then
5938 local utflength=utf.length
5939 local b,e,n,first,last=0,0,0,0,0
5940 local function slide_zero(s,p)
5941 n=n+1
5942 if n>=last then
5943 e=p-1
5944 else
5945 return p
5946 end
5947 end
5948 local function slide_one(s,p)
5949 n=n+1
5950 if n==first then
5951 b=p
5952 end
5953 if n>=last then
5954 e=p-1
5955 else
5956 return p
5957 end
5958 end
5959 local function slide_two(s,p)
5960 n=n+1
5961 if n==first then
5962 b=p
5963 else
5964 return true
5965 end
5966 end
5967 local pattern_zero=Cmt(p_utf8character,slide_zero)^0
5968 local pattern_one=Cmt(p_utf8character,slide_one )^0
5969 local pattern_two=Cmt(p_utf8character,slide_two )^0
5970 local pattern_first=C(p_utf8character)
5971 function utf.sub(str,start,stop)
5972 if not start then
5973 return str
5974 end
5975 if start==0 then
5976 start=1
5977 end
5978 if not stop then
5979 if start<0 then
5980 local l=utflength(str)
5981 start=l+start
5982 else
5983 start=start-1
5984 end
5985 b,n,first=0,0,start
5986 lpegmatch(pattern_two,str)
5987 if n>=first then
5988 return sub(str,b)
5989 else
5990 return ""
5991 end
5992 end
5993 if start<0 or stop<0 then
5994 local l=utf.length(str)
5995 if start<0 then
5996 start=l+start
5997 if start<=0 then
5998 start=1
5999 else
6000 start=start+1
6001 end
6002 end
6003 if stop<0 then
6004 stop=l+stop
6005 if stop==0 then
6006 stop=1
6007 else
6008 stop=stop+1
6009 end
6010 end
6011 end
6012 if start==1 and stop==1 then
6013 return lpegmatch(pattern_first,str) or ""
6014 elseif start>stop then
6015 return ""
6016 elseif start>1 then
6017 b,e,n,first,last=0,0,0,start-1,stop
6018 lpegmatch(pattern_one,str)
6019 if n>=first and e==0 then
6020 e=#str
6021 end
6022 return sub(str,b,e)
6023 else
6024 b,e,n,last=1,0,0,stop
6025 lpegmatch(pattern_zero,str)
6026 if e==0 then
6027 e=#str
6028 end
6029 return sub(str,b,e)
6030 end
6031 end
6032end
6033function utf.remapper(mapping,option,action)
6034 local variant=type(mapping)
6035 if variant=="table" then
6036 action=action or mapping
6037 if option=="dynamic" then
6038 local pattern=false
6039 table.setmetatablenewindex(mapping,function(t,k,v) rawset(t,k,v) pattern=false end)
6040 return function(str)
6041 if not str or str=="" then
6042 return ""
6043 else
6044 if not pattern then
6045 pattern=Cs((tabletopattern(mapping)/action+p_utf8character)^0)
6046 end
6047 return lpegmatch(pattern,str)
6048 end
6049 end
6050 elseif option=="pattern" then
6051 return Cs((tabletopattern(mapping)/action+p_utf8character)^0)
6052 else
6053 local pattern=Cs((tabletopattern(mapping)/action+p_utf8character)^0)
6054 return function(str)
6055 if not str or str=="" then
6056 return ""
6057 else
6058 return lpegmatch(pattern,str)
6059 end
6060 end,pattern
6061 end
6062 elseif variant=="function" then
6063 if option=="pattern" then
6064 return Cs((p_utf8character/mapping+p_utf8character)^0)
6065 else
6066 local pattern=Cs((p_utf8character/mapping+p_utf8character)^0)
6067 return function(str)
6068 if not str or str=="" then
6069 return ""
6070 else
6071 return lpegmatch(pattern,str)
6072 end
6073 end,pattern
6074 end
6075 else
6076 return function(str)
6077 return str or ""
6078 end
6079 end
6080end
6081function utf.replacer(t)
6082 local r=replacer(t,false,false,true)
6083 return function(str)
6084 return lpegmatch(r,str)
6085 end
6086end
6087function utf.subtituter(t)
6088 local f=finder (t)
6089 local r=replacer(t,false,false,true)
6090 return function(str)
6091 local i=lpegmatch(f,str)
6092 if not i then
6093 return str
6094 elseif i>#str then
6095 return str
6096 else
6097 return lpegmatch(r,str)
6098 end
6099 end
6100end
6101local utflinesplitter=p_utfbom^-1*lpeg.tsplitat(p_newline)
6102local utfcharsplitter_ows=p_utfbom^-1*Ct(C(p_utf8character)^0)
6103local utfcharsplitter_iws=p_utfbom^-1*Ct((p_whitespace^1+C(p_utf8character))^0)
6104local utfcharsplitter_raw=Ct(C(p_utf8character)^0)
6105patterns.utflinesplitter=utflinesplitter
6106function utf.splitlines(str)
6107 return lpegmatch(utflinesplitter,str or "")
6108end
6109function utf.split(str,ignorewhitespace)
6110 if ignorewhitespace then
6111 return lpegmatch(utfcharsplitter_iws,str or "")
6112 else
6113 return lpegmatch(utfcharsplitter_ows,str or "")
6114 end
6115end
6116function utf.totable(str)
6117 return lpegmatch(utfcharsplitter_raw,str)
6118end
6119function utf.magic(f)
6120 local str=f:read(4) or ""
6121 local off=lpegmatch(p_utfoffset,str)
6122 if off<4 then
6123 f:seek('set',off)
6124 end
6125 return lpegmatch(p_utftype,str)
6126end
6127local utf16_to_utf8_be,utf16_to_utf8_le
6128local utf32_to_utf8_be,utf32_to_utf8_le
6129local utf_16_be_getbom=patterns.utfbom_16_be^-1
6130local utf_16_le_getbom=patterns.utfbom_16_le^-1
6131local utf_32_be_getbom=patterns.utfbom_32_be^-1
6132local utf_32_le_getbom=patterns.utfbom_32_le^-1
6133local utf_16_be_linesplitter=utf_16_be_getbom*lpeg.tsplitat(patterns.utf_16_be_nl)
6134local utf_16_le_linesplitter=utf_16_le_getbom*lpeg.tsplitat(patterns.utf_16_le_nl)
6135local utf_32_be_linesplitter=utf_32_be_getbom*lpeg.tsplitat(patterns.utf_32_be_nl)
6136local utf_32_le_linesplitter=utf_32_le_getbom*lpeg.tsplitat(patterns.utf_32_le_nl)
6137local more=0
6138local p_utf16_to_utf8_be=C(1)*C(1)/function(left,right)
6139 local now=256*byte(left)+byte(right)
6140 if more>0 then
6141 now=(more-0xD800)*0x400+(now-0xDC00)+0x10000
6142 more=0
6143 return utfchar(now)
6144 elseif now>=0xD800 and now<=0xDBFF then
6145 more=now
6146 return ""
6147 else
6148 return utfchar(now)
6149 end
6150end
6151local p_utf16_to_utf8_le=C(1)*C(1)/function(right,left)
6152 local now=256*byte(left)+byte(right)
6153 if more>0 then
6154 now=(more-0xD800)*0x400+(now-0xDC00)+0x10000
6155 more=0
6156 return utfchar(now)
6157 elseif now>=0xD800 and now<=0xDBFF then
6158 more=now
6159 return ""
6160 else
6161 return utfchar(now)
6162 end
6163end
6164local p_utf32_to_utf8_be=C(1)*C(1)*C(1)*C(1)/function(a,b,c,d)
6165 return utfchar(256*256*256*byte(a)+256*256*byte(b)+256*byte(c)+byte(d))
6166end
6167local p_utf32_to_utf8_le=C(1)*C(1)*C(1)*C(1)/function(a,b,c,d)
6168 return utfchar(256*256*256*byte(d)+256*256*byte(c)+256*byte(b)+byte(a))
6169end
6170p_utf16_to_utf8_be=P(true)/function() more=0 end*utf_16_be_getbom*Cs(p_utf16_to_utf8_be^0)
6171p_utf16_to_utf8_le=P(true)/function() more=0 end*utf_16_le_getbom*Cs(p_utf16_to_utf8_le^0)
6172p_utf32_to_utf8_be=P(true)/function() more=0 end*utf_32_be_getbom*Cs(p_utf32_to_utf8_be^0)
6173p_utf32_to_utf8_le=P(true)/function() more=0 end*utf_32_le_getbom*Cs(p_utf32_to_utf8_le^0)
6174patterns.utf16_to_utf8_be=p_utf16_to_utf8_be
6175patterns.utf16_to_utf8_le=p_utf16_to_utf8_le
6176patterns.utf32_to_utf8_be=p_utf32_to_utf8_be
6177patterns.utf32_to_utf8_le=p_utf32_to_utf8_le
6178utf16_to_utf8_be=function(s)
6179 if s and s~="" then
6180 return lpegmatch(p_utf16_to_utf8_be,s)
6181 else
6182 return s
6183 end
6184end
6185local utf16_to_utf8_be_t=function(t)
6186 if not t then
6187 return nil
6188 elseif type(t)=="string" then
6189 t=lpegmatch(utf_16_be_linesplitter,t)
6190 end
6191 for i=1,#t do
6192 local s=t[i]
6193 if s~="" then
6194 t[i]=lpegmatch(p_utf16_to_utf8_be,s)
6195 end
6196 end
6197 return t
6198end
6199utf16_to_utf8_le=function(s)
6200 if s and s~="" then
6201 return lpegmatch(p_utf16_to_utf8_le,s)
6202 else
6203 return s
6204 end
6205end
6206local utf16_to_utf8_le_t=function(t)
6207 if not t then
6208 return nil
6209 elseif type(t)=="string" then
6210 t=lpegmatch(utf_16_le_linesplitter,t)
6211 end
6212 for i=1,#t do
6213 local s=t[i]
6214 if s~="" then
6215 t[i]=lpegmatch(p_utf16_to_utf8_le,s)
6216 end
6217 end
6218 return t
6219end
6220utf32_to_utf8_be=function(s)
6221 if s and s~="" then
6222 return lpegmatch(p_utf32_to_utf8_be,s)
6223 else
6224 return s
6225 end
6226end
6227local utf32_to_utf8_be_t=function(t)
6228 if not t then
6229 return nil
6230 elseif type(t)=="string" then
6231 t=lpegmatch(utf_32_be_linesplitter,t)
6232 end
6233 for i=1,#t do
6234 local s=t[i]
6235 if s~="" then
6236 t[i]=lpegmatch(p_utf32_to_utf8_be,s)
6237 end
6238 end
6239 return t
6240end
6241utf32_to_utf8_le=function(s)
6242 if s and s~="" then
6243 return lpegmatch(p_utf32_to_utf8_le,s)
6244 else
6245 return s
6246 end
6247end
6248local utf32_to_utf8_le_t=function(t)
6249 if not t then
6250 return nil
6251 elseif type(t)=="string" then
6252 t=lpegmatch(utf_32_le_linesplitter,t)
6253 end
6254 for i=1,#t do
6255 local s=t[i]
6256 if s~="" then
6257 t[i]=lpegmatch(p_utf32_to_utf8_le,s)
6258 end
6259 end
6260 return t
6261end
6262utf.utf16_to_utf8_le_t=utf16_to_utf8_le_t
6263utf.utf16_to_utf8_be_t=utf16_to_utf8_be_t
6264utf.utf32_to_utf8_le_t=utf32_to_utf8_le_t
6265utf.utf32_to_utf8_be_t=utf32_to_utf8_be_t
6266utf.utf16_to_utf8_le=utf16_to_utf8_le
6267utf.utf16_to_utf8_be=utf16_to_utf8_be
6268utf.utf32_to_utf8_le=utf32_to_utf8_le
6269utf.utf32_to_utf8_be=utf32_to_utf8_be
6270function utf.utf8_to_utf8_t(t)
6271 return type(t)=="string" and lpegmatch(utflinesplitter,t) or t
6272end
6273function utf.utf16_to_utf8_t(t,endian)
6274 return endian and utf16_to_utf8_be_t(t) or utf16_to_utf8_le_t(t) or t
6275end
6276function utf.utf32_to_utf8_t(t,endian)
6277 return endian and utf32_to_utf8_be_t(t) or utf32_to_utf8_le_t(t) or t
6278end
6279if bit32 then
6280 local rshift=bit32.rshift
6281 local function little(b)
6282 if b<0x10000 then
6283 return char(b%256,rshift(b,8))
6284 else
6285 b=b-0x10000
6286 local b1=rshift(b,10)+0xD800
6287 local b2=b%1024+0xDC00
6288 return char(b1%256,rshift(b1,8),b2%256,rshift(b2,8))
6289 end
6290 end
6291 local function big(b)
6292 if b<0x10000 then
6293 return char(rshift(b,8),b%256)
6294 else
6295 b=b-0x10000
6296 local b1=rshift(b,10)+0xD800
6297 local b2=b%1024+0xDC00
6298 return char(rshift(b1,8),b1%256,rshift(b2,8),b2%256)
6299 end
6300 end
6301 local l_remap=Cs((p_utf8byte/little+P(1)/"")^0)
6302 local b_remap=Cs((p_utf8byte/big+P(1)/"")^0)
6303 local function utf8_to_utf16_be(str,nobom)
6304 if nobom then
6305 return lpegmatch(b_remap,str)
6306 else
6307 return char(254,255)..lpegmatch(b_remap,str)
6308 end
6309 end
6310 local function utf8_to_utf16_le(str,nobom)
6311 if nobom then
6312 return lpegmatch(l_remap,str)
6313 else
6314 return char(255,254)..lpegmatch(l_remap,str)
6315 end
6316 end
6317 utf.utf8_to_utf16_be=utf8_to_utf16_be
6318 utf.utf8_to_utf16_le=utf8_to_utf16_le
6319 function utf.utf8_to_utf16(str,littleendian,nobom)
6320 if littleendian then
6321 return utf8_to_utf16_le(str,nobom)
6322 else
6323 return utf8_to_utf16_be(str,nobom)
6324 end
6325 end
6326end
6327local pattern=Cs (
6328 (p_utf8byte/function(unicode ) return format("0x%04X",unicode) end)*(p_utf8byte*Carg(1)/function(unicode,separator) return format("%s0x%04X",separator,unicode) end)^0
6329)
6330function utf.tocodes(str,separator)
6331 return lpegmatch(pattern,str,1,separator or " ")
6332end
6333function utf.ustring(s)
6334 return format("U+%05X",type(s)=="number" and s or utfbyte(s))
6335end
6336function utf.xstring(s)
6337 return format("0x%05X",type(s)=="number" and s or utfbyte(s))
6338end
6339function utf.toeight(str)
6340 if not str or str=="" then
6341 return nil
6342 end
6343 local utftype=lpegmatch(p_utfstricttype,str)
6344 if utftype=="utf-8" then
6345 return sub(str,4)
6346 elseif utftype=="utf-16-be" then
6347 return utf16_to_utf8_be(str)
6348 elseif utftype=="utf-16-le" then
6349 return utf16_to_utf8_le(str)
6350 else
6351 return str
6352 end
6353end
6354do
6355 local p_nany=p_utf8character/""
6356 local cache={}
6357 function utf.count(str,what)
6358 if type(what)=="string" then
6359 local p=cache[what]
6360 if not p then
6361 p=Cs((P(what)/" "+p_nany)^0)
6362 cache[p]=p
6363 end
6364 return #lpegmatch(p,str)
6365 else
6366 return #lpegmatch(Cs((P(what)/" "+p_nany)^0),str)
6367 end
6368 end
6369end
6370if not string.utfvalues then
6371 local find=string.find
6372 local dummy=function()
6373 end
6374 function string.utfvalues(str)
6375 local n=#str
6376 if n==0 then
6377 return dummy
6378 elseif n==1 then
6379 return function() return utfbyte(str) end
6380 else
6381 local p=1
6382 return function()
6383 local b,e=find(str,".[\128-\191]*",p)
6384 if b then
6385 p=e+1
6386 return utfbyte(sub(str,b,e))
6387 end
6388 end
6389 end
6390 end
6391end
6392utf.values=string.utfvalues
6393function utf.chrlen(u)
6394 return
6395 (u<0x80 and 1) or
6396 (u<0xE0 and 2) or
6397 (u<0xF0 and 3) or
6398 (u<0xF8 and 4) or
6399 (u<0xFC and 5) or
6400 (u<0xFE and 6) or 0
6401end
6402if bit32 then
6403 local extract=bit32.extract
6404 local char=string.char
6405 function utf.toutf32string(n)
6406 if n<=0xFF then
6407 return
6408 char(n).."\000\000\000"
6409 elseif n<=0xFFFF then
6410 return
6411 char(extract(n,0,8))..char(extract(n,8,8)).."\000\000"
6412 elseif n<=0xFFFFFF then
6413 return
6414 char(extract(n,0,8))..char(extract(n,8,8))..char(extract(n,16,8)).."\000"
6415 else
6416 return
6417 char(extract(n,0,8))..char(extract(n,8,8))..char(extract(n,16,8))..char(extract(n,24,8))
6418 end
6419 end
6420end
6421local len=utf.len
6422local rep=rep
6423function string.utfpadd(s,n)
6424 if n and n~=0 then
6425 local l=len(s)
6426 if n>0 then
6427 local d=n-l
6428 if d>0 then
6429 return rep(c or " ",d)..s
6430 end
6431 else
6432 local d=- n-l
6433 if d>0 then
6434 return s..rep(c or " ",d)
6435 end
6436 end
6437 end
6438 return s
6439end
6440do
6441 local utfcharacters=utf.characters or string.utfcharacters
6442 local utfchar=utf.char or string.utfcharacter
6443 lpeg.UP=P
6444 if utfcharacters then
6445 function lpeg.US(str)
6446 local p=P(false)
6447 for uc in utfcharacters(str) do
6448 p=p+P(uc)
6449 end
6450 return p
6451 end
6452 else
6453 function lpeg.US(str)
6454 local p=P(false)
6455 local f=function(uc)
6456 p=p+P(uc)
6457 end
6458 lpegmatch((p_utf8char/f)^0,str)
6459 return p
6460 end
6461 end
6462 local range=p_utf8byte*p_utf8byte+Cc(false)
6463 function lpeg.UR(str,more)
6464 local first,last
6465 if type(str)=="number" then
6466 first=str
6467 last=more or first
6468 else
6469 first,last=lpegmatch(range,str)
6470 if not last then
6471 return P(str)
6472 end
6473 end
6474 if first==last then
6475 return P(str)
6476 end
6477 if not utfchar then
6478 utfchar=utf.char
6479 end
6480 if utfchar and (last-first<8) then
6481 local p=P(false)
6482 for i=first,last do
6483 p=p+P(utfchar(i))
6484 end
6485 return p
6486 else
6487 local f=function(b)
6488 return b>=first and b<=last
6489 end
6490 return p_utf8byte/f
6491 end
6492 end
6493end
6494
6495
6496end
6497
6498do
6499
6500package.loaded["l-math"] = package.loaded["l-math"] or true
6501
6502
6503
6504if not modules then modules={} end modules ['l-math']={
6505 version=1.001,
6506 comment="companion to luat-lib.mkiv",
6507 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
6508 copyright="PRAGMA ADE / ConTeXt Development Team",
6509 license="see context related readme files"
6510}
6511if not math.ceiling then
6512 math.ceiling=math.ceil
6513end
6514if not math.round then
6515 if xmath then
6516 math.round=xmath.round
6517 else
6518 local floor=math.floor
6519 function math.round(x)
6520 return x<0 and -floor(-x+0.5) or floor(x+0.5)
6521 end
6522 end
6523end
6524if not math.div then
6525 local floor=math.floor
6526 function math.div(n,m) return floor(n/m) end
6527end
6528if not math.mod then
6529 function math.mod(n,m) return n%m end
6530end
6531if not math.sind then
6532 local sin,cos,tan=math.sin,math.cos,math.tan
6533 local pipi=2*math.pi/360
6534 function math.sind(d) return sin(d*pipi) end
6535 function math.cosd(d) return cos(d*pipi) end
6536 function math.tand(d) return tan(d*pipi) end
6537end
6538if not math.odd then
6539 function math.odd (n) return n%2~=0 end
6540 function math.even(n) return n%2==0 end
6541end
6542if not math.cosh then
6543 local exp=math.exp
6544 function math.cosh(x)
6545 local xx=exp(x)
6546 return (xx+1/xx)/2
6547 end
6548 function math.sinh(x)
6549 local xx=exp(x)
6550 return (xx-1/xx)/2
6551 end
6552 function math.tanh(x)
6553 local xx=exp(x)
6554 return (xx-1/xx)/(xx+1/xx)
6555 end
6556end
6557if not math.pow then
6558 function math.pow(x,y)
6559 return x^y
6560 end
6561end
6562if not math.atan2 then
6563 math.atan2=math.atan
6564end
6565if not math.ldexp then
6566 function math.ldexp(x,e)
6567 return x*2.0^e
6568 end
6569end
6570if not math.log10 then
6571 local log=math.log
6572 function math.log10(x)
6573 return log(x,10)
6574 end
6575end
6576if not math.type then
6577 function math.type()
6578 return "float"
6579 end
6580end
6581if not math.tointeger then
6582 math.mininteger=-0x4FFFFFFFFFFF
6583 math.maxinteger=0x4FFFFFFFFFFF
6584 local floor=math.floor
6585 function math.tointeger(n)
6586 local f=floor(n)
6587 return f==n and f or nil
6588 end
6589end
6590if not math.ult then
6591 local floor=math.floor
6592 function math.ult(m,n)
6593 return floor(m)<floor(n)
6594 end
6595end
6596
6597
6598end
6599
6600do
6601
6602package.loaded["util-str"] = package.loaded["util-str"] or true
6603
6604
6605
6606if not modules then modules={} end modules ['util-str']={
6607 version=1.001,
6608 comment="companion to luat-lib.mkiv",
6609 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
6610 copyright="PRAGMA ADE / ConTeXt Development Team",
6611 license="see context related readme files"
6612}
6613utilities=utilities or {}
6614utilities.strings=utilities.strings or {}
6615local strings=utilities.strings
6616local format,gsub,rep,sub,find,char=string.format,string.gsub,string.rep,string.sub,string.find,string.char
6617local load,dump=load,string.dump
6618local tonumber,type,tostring,next,setmetatable=tonumber,type,tostring,next,setmetatable
6619local unpack,concat=table.unpack,table.concat
6620local P,V,C,S,R,Ct,Cs,Cp,Carg,Cc=lpeg.P,lpeg.V,lpeg.C,lpeg.S,lpeg.R,lpeg.Ct,lpeg.Cs,lpeg.Cp,lpeg.Carg,lpeg.Cc
6621local patterns,lpegmatch=lpeg.patterns,lpeg.match
6622local tsplitat=lpeg.tsplitat
6623local utfchar,utfbyte,utflen=utf.char,utf.byte,utf.len
6624local loadstripped=function(str,shortcuts)
6625 if shortcuts then
6626 return load(dump(load(str),true),nil,nil,shortcuts)
6627 else
6628 return load(dump(load(str),true))
6629 end
6630end
6631if not number then number={} end
6632local stripzero=patterns.stripzero
6633local stripzeros=patterns.stripzeros
6634local newline=patterns.newline
6635local endofstring=patterns.endofstring
6636local anything=patterns.anything
6637local whitespace=patterns.whitespace
6638local space=patterns.space
6639local spacer=patterns.spacer
6640local spaceortab=patterns.spaceortab
6641local digit=patterns.digit
6642local sign=patterns.sign
6643local period=patterns.period
6644local ptf=1/65536
6645local bpf=(7200/7227)/65536
6646local function points(n)
6647 if n==0 then
6648 return "0pt"
6649 end
6650 n=tonumber(n)
6651 if not n or n==0 then
6652 return "0pt"
6653 end
6654 n=n*ptf
6655 if n%1==0 then
6656 return format("%ipt",n)
6657 else
6658 return lpegmatch(stripzeros,format("%.5fpt",n))
6659 end
6660end
6661local function nupoints(n)
6662 if n==0 then
6663 return "0"
6664 end
6665 n=tonumber(n)
6666 if not n or n==0 then
6667 return "0"
6668 end
6669 n=n*ptf
6670 if n%1==0 then
6671 return format("%i",n)
6672 else
6673 return format("%.5f",n)
6674 end
6675end
6676local function basepoints(n)
6677 if n==0 then
6678 return "0bp"
6679 end
6680 n=tonumber(n)
6681 if not n or n==0 then
6682 return "0bp"
6683 end
6684 n=n*bpf
6685 if n%1==0 then
6686 return format("%ibp",n)
6687 else
6688 return lpegmatch(stripzeros,format("%.5fbp",n))
6689 end
6690end
6691local function nubasepoints(n)
6692 if n==0 then
6693 return "0"
6694 end
6695 n=tonumber(n)
6696 if not n or n==0 then
6697 return "0"
6698 end
6699 n=n*bpf
6700 if n%1==0 then
6701 return format("%i",n)
6702 else
6703 return format("%.5f",n)
6704 end
6705end
6706number.points=points
6707number.nupoints=nupoints
6708number.basepoints=basepoints
6709number.nubasepoints=nubasepoints
6710local rubish=spaceortab^0*newline
6711local anyrubish=spaceortab+newline
6712local stripped=(spaceortab^1/"")*newline
6713local leading=rubish^0/""
6714local trailing=(anyrubish^1*endofstring)/""
6715local redundant=rubish^3/"\n"
6716local pattern=Cs(leading*(trailing+redundant+stripped+anything)^0)
6717function strings.collapsecrlf(str)
6718 return lpegmatch(pattern,str)
6719end
6720local repeaters={}
6721function strings.newrepeater(str,offset)
6722 offset=offset or 0
6723 local s=repeaters[str]
6724 if not s then
6725 s={}
6726 repeaters[str]=s
6727 end
6728 local t=s[offset]
6729 if t then
6730 return t
6731 end
6732 t={}
6733 setmetatable(t,{ __index=function(t,k)
6734 if not k then
6735 return ""
6736 end
6737 local n=k+offset
6738 local s=n>0 and rep(str,n) or ""
6739 t[k]=s
6740 return s
6741 end })
6742 s[offset]=t
6743 return t
6744end
6745local extra,tab,start=0,0,4,0
6746local nspaces=strings.newrepeater(" ")
6747string.nspaces=nspaces
6748local pattern=Carg(1)/function(t)
6749 extra,tab,start=0,t or 7,1
6750 end*Cs((
6751 Cp()*patterns.tab/function(position)
6752 local current=(position-start+1)+extra
6753 local spaces=tab-(current-1)%tab
6754 if spaces>0 then
6755 extra=extra+spaces-1
6756 return nspaces[spaces]
6757 else
6758 return ""
6759 end
6760 end+newline*Cp()/function(position)
6761 extra,start=0,position
6762 end+anything
6763 )^1)
6764function strings.tabtospace(str,tab)
6765 return lpegmatch(pattern,str,1,tab or 7)
6766end
6767function string.utfpadding(s,n)
6768 if not n or n==0 then
6769 return ""
6770 end
6771 local l=utflen(s)
6772 if n>0 then
6773 return nspaces[n-l]
6774 else
6775 return nspaces[-n-l]
6776 end
6777end
6778local optionalspace=spacer^0
6779local nospace=optionalspace/""
6780local endofline=nospace*newline
6781local stripend=(whitespace^1*endofstring)/""
6782local normalline=(nospace*((1-optionalspace*(newline+endofstring))^1)*nospace)
6783local stripempty=endofline^1/""
6784local normalempty=endofline^1
6785local singleempty=endofline*(endofline^0/"")
6786local doubleempty=endofline*endofline^-1*(endofline^0/"")
6787local stripstart=stripempty^0
6788local intospace=whitespace^1/" "
6789local noleading=whitespace^1/""
6790local notrailing=noleading*endofstring
6791local p_prune_normal=Cs (stripstart*(stripend+normalline+normalempty )^0 )
6792local p_prune_collapse=Cs (stripstart*(stripend+normalline+doubleempty )^0 )
6793local p_prune_noempty=Cs (stripstart*(stripend+normalline+singleempty )^0 )
6794local p_prune_intospace=Cs (noleading*(notrailing+intospace+1 )^0 )
6795local p_retain_normal=Cs ((normalline+normalempty )^0 )
6796local p_retain_collapse=Cs ((normalline+doubleempty )^0 )
6797local p_retain_noempty=Cs ((normalline+singleempty )^0 )
6798local p_collapse_all=Cs (stripstart*(stripend+((whitespace+newline)^1/" ")+1)^0 )
6799local striplinepatterns={
6800 ["prune"]=p_prune_normal,
6801 ["prune and collapse"]=p_prune_collapse,
6802 ["prune and no empty"]=p_prune_noempty,
6803 ["prune and to space"]=p_prune_intospace,
6804 ["retain"]=p_retain_normal,
6805 ["retain and collapse"]=p_retain_collapse,
6806 ["retain and no empty"]=p_retain_noempty,
6807 ["collapse all"]=p_collapse_all,
6808 ["collapse"]=patterns.collapser,
6809}
6810setmetatable(striplinepatterns,{ __index=function(t,k) return p_prune_collapse end })
6811strings.striplinepatterns=striplinepatterns
6812function strings.striplines(str,how)
6813 return str and lpegmatch(striplinepatterns[how],str) or str
6814end
6815function strings.collapse(str)
6816 return str and lpegmatch(p_prune_intospace,str) or str
6817end
6818strings.striplong=strings.striplines
6819function strings.nice(str)
6820 str=gsub(str,"[:%-+_]+"," ")
6821 return str
6822end
6823local n=0
6824local sequenced=table.sequenced
6825function string.autodouble(s,sep)
6826 if s==nil then
6827 return '""'
6828 end
6829 local t=type(s)
6830 if t=="number" then
6831 return tostring(s)
6832 end
6833 if t=="table" then
6834 return ('"'..sequenced(s,sep or ",")..'"')
6835 end
6836 return ('"'..tostring(s)..'"')
6837end
6838function string.autosingle(s,sep)
6839 if s==nil then
6840 return "''"
6841 end
6842 local t=type(s)
6843 if t=="number" then
6844 return tostring(s)
6845 end
6846 if t=="table" then
6847 return ("'"..sequenced(s,sep or ",").."'")
6848 end
6849 return ("'"..tostring(s).."'")
6850end
6851local tracedchars={ [0]=
6852 "[null]","[soh]","[stx]","[etx]","[eot]","[enq]","[ack]","[bel]",
6853 "[bs]","[ht]","[lf]","[vt]","[ff]","[cr]","[so]","[si]",
6854 "[dle]","[dc1]","[dc2]","[dc3]","[dc4]","[nak]","[syn]","[etb]",
6855 "[can]","[em]","[sub]","[esc]","[fs]","[gs]","[rs]","[us]",
6856 "[space]",
6857}
6858string.tracedchars=tracedchars
6859strings.tracers=tracedchars
6860function string.tracedchar(b)
6861 if type(b)=="number" then
6862 return tracedchars[b] or (utfchar(b).." (U+"..format("%05X",b)..")")
6863 else
6864 local c=utfbyte(b)
6865 return tracedchars[c] or (b.." (U+"..(c and format("%05X",c) or "?????")..")")
6866 end
6867end
6868function number.signed(i)
6869 if i>0 then
6870 return "+",i
6871 else
6872 return "-",-i
6873 end
6874end
6875local two=digit*digit
6876local three=two*digit
6877local prefix=(Carg(1)*three)^1
6878local splitter=Cs (
6879 (((1-(three^1*period))^1+C(three))*prefix+C((1-period)^1))*(anything/""*Carg(2))*C(2)
6880)
6881local splitter3=Cs (
6882 three*prefix*endofstring+two*prefix*endofstring+digit*prefix*endofstring+three+two+digit
6883)
6884patterns.formattednumber=splitter
6885function number.formatted(n,sep1,sep2)
6886 if sep1==false then
6887 if type(n)=="number" then
6888 n=tostring(n)
6889 end
6890 return lpegmatch(splitter3,n,1,sep2 or ".")
6891 else
6892 if type(n)=="number" then
6893 n=format("%0.2f",n)
6894 end
6895 if sep1==true then
6896 return lpegmatch(splitter,n,1,".",",")
6897 elseif sep1=="." then
6898 return lpegmatch(splitter,n,1,sep1,sep2 or ",")
6899 elseif sep1=="," then
6900 return lpegmatch(splitter,n,1,sep1,sep2 or ".")
6901 else
6902 return lpegmatch(splitter,n,1,sep1 or ",",sep2 or ".")
6903 end
6904 end
6905end
6906local p=Cs(
6907 P("-")^0*(P("0")^1/"")^0*(1-period)^0*(period*P("0")^1*endofstring/""+period^0)*P(1-P("0")^1*endofstring)^0
6908 )
6909function number.compactfloat(n,fmt)
6910 if n==0 then
6911 return "0"
6912 elseif n==1 then
6913 return "1"
6914 end
6915 n=lpegmatch(p,format(fmt or "%0.3f",n))
6916 if n=="." or n=="" or n=="-" then
6917 return "0"
6918 end
6919 return n
6920end
6921local zero=P("0")^1/""
6922local plus=P("+")/""
6923local minus=P("-")
6924local separator=period
6925local trailing=zero^1*#S("eE")
6926local exponent=(S("eE")*(plus+Cs((minus*zero^0*endofstring)/"")+minus)*zero^0*(endofstring*Cc("0")+anything^1))
6927local pattern_a=Cs(minus^0*digit^1*(separator/""*trailing+separator*(trailing+digit)^0)*exponent)
6928local pattern_b=Cs((exponent+anything)^0)
6929function number.sparseexponent(f,n)
6930 if not n then
6931 n=f
6932 f="%e"
6933 end
6934 local tn=type(n)
6935 if tn=="string" then
6936 local m=tonumber(n)
6937 if m then
6938 return lpegmatch((f=="%e" or f=="%E") and pattern_a or pattern_b,format(f,m))
6939 end
6940 elseif tn=="number" then
6941 return lpegmatch((f=="%e" or f=="%E") and pattern_a or pattern_b,format(f,n))
6942 end
6943 return tostring(n)
6944end
6945local hf={}
6946local hs={}
6947setmetatable(hf,{ __index=function(t,k)
6948 local v="%."..k.."f"
6949 t[k]=v
6950 return v
6951end } )
6952setmetatable(hs,{ __index=function(t,k)
6953 local v="%"..k.."s"
6954 t[k]=v
6955 return v
6956end } )
6957function number.formattedfloat(n,b,a)
6958 local s=format(hf[a],n)
6959 local l=(b or 0)+(a or 0)+1
6960 if #s<l then
6961 return format(hs[l],s)
6962 else
6963 return s
6964 end
6965end
6966local template=[[
6967%s
6968%s
6969return function(%s) return %s end
6970]]
6971local pattern=Cs(Cc('"')*(
6972 (1-S('"\\\n\r'))^1+P('"')/'\\"'+P('\\')/'\\\\'+P('\n')/'\\n'+P('\r')/'\\r'
6973)^0*Cc('"'))
6974patterns.escapedquotes=pattern
6975function string.escapedquotes(s)
6976 return lpegmatch(pattern,s)
6977end
6978local pattern=(1-P("\\"))^1;pattern=Cs (
6979 pattern*((P("\\")/""*(digit^-3/function(s) return char(tonumber(s)) end))+pattern )^1
6980)
6981patterns.unescapedquotes=pattern
6982function string.unescapedquotes(s)
6983 return lpegmatch(pattern,s) or s
6984end
6985string.texnewlines=lpeg.replacer(patterns.newline,"\r",true)
6986local preamble=""
6987local environment={
6988 global=global or _G,
6989 lpeg=lpeg,
6990 type=type,
6991 tostring=tostring,
6992 tonumber=tonumber,
6993 format=string.format,
6994 concat=table.concat,
6995 signed=number.signed,
6996 points=number.points,
6997 nupoints=number.nupoints,
6998 basepoints=number.basepoints,
6999 nubasepoints=number.nubasepoints,
7000 utfchar=utf.char,
7001 utfbyte=utf.byte,
7002 lpegmatch=lpeg.match,
7003 nspaces=string.nspaces,
7004 utfpadding=string.utfpadding,
7005 tracedchar=string.tracedchar,
7006 autosingle=string.autosingle,
7007 autodouble=string.autodouble,
7008 sequenced=table.sequenced,
7009 formattednumber=number.formatted,
7010 sparseexponent=number.sparseexponent,
7011 formattedfloat=number.formattedfloat,
7012 stripzero=patterns.stripzero,
7013 stripzeros=patterns.stripzeros,
7014 escapedquotes=string.escapedquotes,
7015 FORMAT=string.f6,
7016}
7017local arguments={ "a1" }
7018setmetatable(arguments,{ __index=function(t,k)
7019 local v=t[k-1]..",a"..k
7020 t[k]=v
7021 return v
7022 end
7023})
7024local prefix_any=C((sign+space+period+digit)^0)
7025local prefix_sub=(C((sign+digit)^0)+Cc(0))*period*(C((sign+digit)^0)+Cc(0))
7026local prefix_tab=P("{")*C((1-P("}"))^0)*P("}")+C((1-R("az","AZ","09","%%"))^0)
7027local format_s=function(f)
7028 n=n+1
7029 if f and f~="" then
7030 return format("format('%%%ss',a%s)",f,n)
7031 else
7032 return format("(a%s or '')",n)
7033 end
7034end
7035local format_S=function(f)
7036 n=n+1
7037 if f and f~="" then
7038 return format("format('%%%ss',tostring(a%s))",f,n)
7039 else
7040 return format("tostring(a%s)",n)
7041 end
7042end
7043local format_right=function(f)
7044 n=n+1
7045 f=tonumber(f)
7046 if not f or f==0 then
7047 return format("(a%s or '')",n)
7048 elseif f>0 then
7049 return format("utfpadding(a%s,%i)..a%s",n,f,n)
7050 else
7051 return format("a%s..utfpadding(a%s,%i)",n,n,f)
7052 end
7053end
7054local format_left=function(f)
7055 n=n+1
7056 f=tonumber(f)
7057 if not f or f==0 then
7058 return format("(a%s or '')",n)
7059 end
7060 if f<0 then
7061 return format("utfpadding(a%s,%i)..a%s",n,-f,n)
7062 else
7063 return format("a%s..utfpadding(a%s,%i)",n,n,-f)
7064 end
7065end
7066local format_q=JITSUPPORTED and function()
7067 n=n+1
7068 return format("(a%s ~= nil and format('%%q',tostring(a%s)) or '')",n,n)
7069end or function()
7070 n=n+1
7071 return format("(a%s ~= nil and format('%%q',a%s) or '')",n,n)
7072end
7073local format_Q=function()
7074 n=n+1
7075 return format("escapedquotes(tostring(a%s))",n)
7076end
7077local format_i=function(f)
7078 n=n+1
7079 if f and f~="" then
7080 return format("format('%%%si',a%s)",f,n)
7081 else
7082 return format("format('%%i',a%s)",n)
7083 end
7084end
7085local format_d=format_i
7086local format_I=function(f)
7087 n=n+1
7088 return format("format('%%s%%%si',signed(a%s))",f,n)
7089end
7090local format_f=function(f)
7091 n=n+1
7092 return format("format('%%%sf',a%s)",f,n)
7093end
7094local format_F=function(f)
7095 n=n+1
7096 if not f or f=="" then
7097 return format("(((a%s > -0.0000000005 and a%s < 0.0000000005) and '0') or format((a%s %% 1 == 0) and '%%i' or '%%.9f',a%s))",n,n,n,n)
7098 else
7099 return format("format((a%s %% 1 == 0) and '%%i' or '%%%sf',a%s)",n,f,n)
7100 end
7101end
7102local format_k=function(b,a)
7103 n=n+1
7104 return format("formattedfloat(a%s,%s,%s)",n,b or 0,a or 0)
7105end
7106local format_g=function(f)
7107 n=n+1
7108 return format("format('%%%sg',a%s)",f,n)
7109end
7110local format_G=function(f)
7111 n=n+1
7112 return format("format('%%%sG',a%s)",f,n)
7113end
7114local format_e=function(f)
7115 n=n+1
7116 return format("format('%%%se',a%s)",f,n)
7117end
7118local format_E=function(f)
7119 n=n+1
7120 return format("format('%%%sE',a%s)",f,n)
7121end
7122local format_j=function(f)
7123 n=n+1
7124 return format("sparseexponent('%%%se',a%s)",f,n)
7125end
7126local format_J=function(f)
7127 n=n+1
7128 return format("sparseexponent('%%%sE',a%s)",f,n)
7129end
7130local format_x=function(f)
7131 n=n+1
7132 return format("format('%%%sx',a%s)",f,n)
7133end
7134local format_X=function(f)
7135 n=n+1
7136 return format("format('%%%sX',a%s)",f,n)
7137end
7138local format_o=function(f)
7139 n=n+1
7140 return format("format('%%%so',a%s)",f,n)
7141end
7142local format_c=function()
7143 n=n+1
7144 return format("utfchar(a%s)",n)
7145end
7146local format_C=function()
7147 n=n+1
7148 return format("tracedchar(a%s)",n)
7149end
7150local format_r=function(f)
7151 n=n+1
7152 return format("format('%%%s.0f',a%s)",f,n)
7153end
7154local format_h=function(f)
7155 n=n+1
7156 if f=="-" then
7157 f=sub(f,2)
7158 return format("format('%%%sx',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n)
7159 else
7160 return format("format('0x%%%sx',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n)
7161 end
7162end
7163local format_H=function(f)
7164 n=n+1
7165 if f=="-" then
7166 f=sub(f,2)
7167 return format("format('%%%sX',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n)
7168 else
7169 return format("format('0x%%%sX',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n)
7170 end
7171end
7172local format_u=function(f)
7173 n=n+1
7174 if f=="-" then
7175 f=sub(f,2)
7176 return format("format('%%%sx',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n)
7177 else
7178 return format("format('u+%%%sx',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n)
7179 end
7180end
7181local format_U=function(f)
7182 n=n+1
7183 if f=="-" then
7184 f=sub(f,2)
7185 return format("format('%%%sX',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n)
7186 else
7187 return format("format('U+%%%sX',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n)
7188 end
7189end
7190local format_p=function()
7191 n=n+1
7192 return format("points(a%s)",n)
7193end
7194local format_P=function()
7195 n=n+1
7196 return format("nupoints(a%s)",n)
7197end
7198local format_b=function()
7199 n=n+1
7200 return format("basepoints(a%s)",n)
7201end
7202local format_B=function()
7203 n=n+1
7204 return format("nubasepoints(a%s)",n)
7205end
7206local format_t=function(f)
7207 n=n+1
7208 if f and f~="" then
7209 return format("concat(a%s,%q)",n,f)
7210 else
7211 return format("concat(a%s)",n)
7212 end
7213end
7214local format_T=function(f)
7215 n=n+1
7216 if f and f~="" then
7217 return format("sequenced(a%s,%q)",n,f)
7218 else
7219 return format("sequenced(a%s)",n)
7220 end
7221end
7222local format_l=function()
7223 n=n+1
7224 return format("(a%s and 'true' or 'false')",n)
7225end
7226local format_L=function()
7227 n=n+1
7228 return format("(a%s and 'TRUE' or 'FALSE')",n)
7229end
7230local format_n=function()
7231 n=n+1
7232 return format("((a%s %% 1 == 0) and format('%%i',a%s) or tostring(a%s))",n,n,n)
7233end
7234local format_N if environment.FORMAT then
7235 format_N=function(f)
7236 n=n+1
7237 if not f or f=="" then
7238 return format("FORMAT(a%s,'%%.9f')",n)
7239 elseif f==".6" or f=="0.6" then
7240 return format("FORMAT(a%s)",n)
7241 else
7242 return format("FORMAT(a%s,'%%%sf')",n,f)
7243 end
7244 end
7245else
7246 format_N=function(f)
7247 n=n+1
7248 if not f or f=="" then
7249 f=".9"
7250 end
7251 return format("(((a%s %% 1 == 0) and format('%%i',a%s)) or lpegmatch(stripzero,format('%%%sf',a%s)))",n,n,f,n)
7252 end
7253end
7254local format_a=function(f)
7255 n=n+1
7256 if f and f~="" then
7257 return format("autosingle(a%s,%q)",n,f)
7258 else
7259 return format("autosingle(a%s)",n)
7260 end
7261end
7262local format_A=function(f)
7263 n=n+1
7264 if f and f~="" then
7265 return format("autodouble(a%s,%q)",n,f)
7266 else
7267 return format("autodouble(a%s)",n)
7268 end
7269end
7270local format_w=function(f)
7271 n=n+1
7272 f=tonumber(f)
7273 if f then
7274 return format("nspaces[%s+a%s]",f,n)
7275 else
7276 return format("nspaces[a%s]",n)
7277 end
7278end
7279local format_W=function(f)
7280 return format("nspaces[%s]",tonumber(f) or 0)
7281end
7282local format_m=function(f)
7283 n=n+1
7284 if not f or f=="" then
7285 f=","
7286 end
7287 if f=="0" then
7288 return format([[formattednumber(a%s,false)]],n)
7289 else
7290 return format([[formattednumber(a%s,%q,".")]],n,f)
7291 end
7292end
7293local format_M=function(f)
7294 n=n+1
7295 if not f or f=="" then
7296 f="."
7297 end
7298 if f=="0" then
7299 return format([[formattednumber(a%s,false)]],n)
7300 else
7301 return format([[formattednumber(a%s,%q,",")]],n,f)
7302 end
7303end
7304local format_z=function(f)
7305 n=n+(tonumber(f) or 1)
7306 return "''"
7307end
7308local format_rest=function(s)
7309 return format("%q",s)
7310end
7311local format_extension=function(extensions,f,name)
7312 local extension=extensions[name] or "tostring(%s)"
7313 local f=tonumber(f) or 1
7314 local w=find(extension,"%.%.%.")
7315 if f==0 then
7316 if w then
7317 extension=gsub(extension,"%.%.%.","")
7318 end
7319 return extension
7320 elseif f==1 then
7321 if w then
7322 extension=gsub(extension,"%.%.%.","%%s")
7323 end
7324 n=n+1
7325 local a="a"..n
7326 return format(extension,a,a)
7327 elseif f<0 then
7328 if w then
7329 extension=gsub(extension,"%.%.%.","")
7330 return extension
7331 else
7332 local a="a"..(n+f+1)
7333 return format(extension,a,a)
7334 end
7335 else
7336 if w then
7337 extension=gsub(extension,"%.%.%.",rep("%%s,",f-1).."%%s")
7338 end
7339 local t={}
7340 for i=1,f do
7341 n=n+1
7342 t[i]="a"..n
7343 end
7344 return format(extension,unpack(t))
7345 end
7346end
7347local builder=Cs { "start",
7348 start=(
7349 (
7350 P("%")/""*(
7351 V("!")
7352+V("s")+V("q")+V("i")+V("d")+V("f")+V("F")+V("g")+V("G")+V("e")+V("E")+V("x")+V("X")+V("o")
7353+V("c")+V("C")+V("S")
7354+V("Q")
7355+V("n")
7356+V("N")
7357+V("k")
7358+V("r")+V("h")+V("H")+V("u")+V("U")+V("p")+V("P")+V("b")+V("B")+V("t")+V("T")+V("l")+V("L")+V("I")+V("w")
7359+V("W")
7360+V("a")
7361+V("A")
7362+V("j")+V("J")
7363+V("m")+V("M")
7364+V("z")
7365+V(">")
7366+V("<")
7367 )+V("*")
7368 )*(endofstring+Carg(1))
7369 )^0,
7370 ["s"]=(prefix_any*P("s"))/format_s,
7371 ["q"]=(prefix_any*P("q"))/format_q,
7372 ["i"]=(prefix_any*P("i"))/format_i,
7373 ["d"]=(prefix_any*P("d"))/format_d,
7374 ["f"]=(prefix_any*P("f"))/format_f,
7375 ["F"]=(prefix_any*P("F"))/format_F,
7376 ["g"]=(prefix_any*P("g"))/format_g,
7377 ["G"]=(prefix_any*P("G"))/format_G,
7378 ["e"]=(prefix_any*P("e"))/format_e,
7379 ["E"]=(prefix_any*P("E"))/format_E,
7380 ["x"]=(prefix_any*P("x"))/format_x,
7381 ["X"]=(prefix_any*P("X"))/format_X,
7382 ["o"]=(prefix_any*P("o"))/format_o,
7383 ["S"]=(prefix_any*P("S"))/format_S,
7384 ["Q"]=(prefix_any*P("Q"))/format_Q,
7385 ["n"]=(prefix_any*P("n"))/format_n,
7386 ["N"]=(prefix_any*P("N"))/format_N,
7387 ["k"]=(prefix_sub*P("k"))/format_k,
7388 ["c"]=(prefix_any*P("c"))/format_c,
7389 ["C"]=(prefix_any*P("C"))/format_C,
7390 ["r"]=(prefix_any*P("r"))/format_r,
7391 ["h"]=(prefix_any*P("h"))/format_h,
7392 ["H"]=(prefix_any*P("H"))/format_H,
7393 ["u"]=(prefix_any*P("u"))/format_u,
7394 ["U"]=(prefix_any*P("U"))/format_U,
7395 ["p"]=(prefix_any*P("p"))/format_p,
7396 ["P"]=(prefix_any*P("P"))/format_P,
7397 ["b"]=(prefix_any*P("b"))/format_b,
7398 ["B"]=(prefix_any*P("B"))/format_B,
7399 ["t"]=(prefix_tab*P("t"))/format_t,
7400 ["T"]=(prefix_tab*P("T"))/format_T,
7401 ["l"]=(prefix_any*P("l"))/format_l,
7402 ["L"]=(prefix_any*P("L"))/format_L,
7403 ["I"]=(prefix_any*P("I"))/format_I,
7404 ["w"]=(prefix_any*P("w"))/format_w,
7405 ["W"]=(prefix_any*P("W"))/format_W,
7406 ["j"]=(prefix_any*P("j"))/format_j,
7407 ["J"]=(prefix_any*P("J"))/format_J,
7408 ["m"]=(prefix_any*P("m"))/format_m,
7409 ["M"]=(prefix_any*P("M"))/format_M,
7410 ["z"]=(prefix_any*P("z"))/format_z,
7411 ["a"]=(prefix_any*P("a"))/format_a,
7412 ["A"]=(prefix_any*P("A"))/format_A,
7413 ["<"]=(prefix_any*P("<"))/format_left,
7414 [">"]=(prefix_any*P(">"))/format_right,
7415 ["*"]=Cs(((1-P("%"))^1+P("%%")/"%%")^1)/format_rest,
7416 ["?"]=Cs(((1-P("%"))^1 )^1)/format_rest,
7417 ["!"]=Carg(2)*prefix_any*P("!")*C((1-P("!"))^1)*P("!")/format_extension,
7418}
7419local xx=setmetatable({},{ __index=function(t,k) local v=format("%02x",k) t[k]=v return v end })
7420local XX=setmetatable({},{ __index=function(t,k) local v=format("%02X",k) t[k]=v return v end })
7421local preset={
7422 ["%02x"]=function(n) return xx[n] end,
7423 ["%02X"]=function(n) return XX[n] end,
7424}
7425local direct=P("%")*(sign+space+period+digit)^0*S("sqidfgGeExXo")*endofstring/[[local format = string.format return function(str) return format("%0",str) end]]
7426local function make(t,str)
7427 local f=preset[str]
7428 if f then
7429 return f
7430 end
7431 local p=lpegmatch(direct,str)
7432 if p then
7433 f=loadstripped(p)()
7434 else
7435 n=0
7436 p=lpegmatch(builder,str,1,t._connector_,t._extensions_)
7437 if n>0 then
7438 p=format(template,preamble,t._preamble_,arguments[n],p)
7439 f=loadstripped(p,t._environment_)()
7440 else
7441 f=function() return str end
7442 end
7443 end
7444 t[str]=f
7445 return f
7446end
7447local function use(t,fmt,...)
7448 return t[fmt](...)
7449end
7450strings.formatters={}
7451function strings.formatters.new(noconcat)
7452 local e={}
7453 for k,v in next,environment do
7454 e[k]=v
7455 end
7456 local t={
7457 _type_="formatter",
7458 _connector_=noconcat and "," or "..",
7459 _extensions_={},
7460 _preamble_="",
7461 _environment_=e,
7462 }
7463 setmetatable(t,{ __index=make,__call=use })
7464 return t
7465end
7466local formatters=strings.formatters.new()
7467string.formatters=formatters
7468string.formatter=function(str,...) return formatters[str](...) end
7469local function add(t,name,template,preamble)
7470 if type(t)=="table" and t._type_=="formatter" then
7471 t._extensions_[name]=template or "%s"
7472 if type(preamble)=="string" then
7473 t._preamble_=preamble.."\n"..t._preamble_
7474 elseif type(preamble)=="table" then
7475 for k,v in next,preamble do
7476 t._environment_[k]=v
7477 end
7478 end
7479 end
7480end
7481strings.formatters.add=add
7482patterns.xmlescape=Cs((P("<")/"<"+P(">")/">"+P("&")/"&"+P('"')/"""+anything)^0)
7483patterns.texescape=Cs((C(S("#$%\\{}"))/"\\%1"+anything)^0)
7484patterns.luaescape=Cs(((1-S('"\n'))^1+P('"')/'\\"'+P('\n')/'\\n"')^0)
7485patterns.luaquoted=Cs(Cc('"')*((1-S('"\n'))^1+P('"')/'\\"'+P('\n')/'\\n"')^0*Cc('"'))
7486add(formatters,"xml",[[lpegmatch(xmlescape,%s)]],{ xmlescape=patterns.xmlescape })
7487add(formatters,"tex",[[lpegmatch(texescape,%s)]],{ texescape=patterns.texescape })
7488add(formatters,"lua",[[lpegmatch(luaescape,%s)]],{ luaescape=patterns.luaescape })
7489local dquote=patterns.dquote
7490local equote=patterns.escaped+dquote/'\\"'+1
7491local cquote=Cc('"')
7492local pattern=Cs(dquote*(equote-P(-2))^0*dquote)
7493+Cs(cquote*(equote-space)^0*space*equote^0*cquote)
7494function string.optionalquoted(str)
7495 return lpegmatch(pattern,str) or str
7496end
7497local pattern=Cs((newline/(os.newline or "\r")+1)^0)
7498function string.replacenewlines(str)
7499 return lpegmatch(pattern,str)
7500end
7501function strings.newcollector()
7502 local result,r={},0
7503 return
7504 function(fmt,str,...)
7505 r=r+1
7506 result[r]=str==nil and fmt or formatters[fmt](str,...)
7507 end,
7508 function(connector)
7509 if result then
7510 local str=concat(result,connector)
7511 result,r={},0
7512 return str
7513 end
7514 end
7515end
7516local f_16_16=formatters["%0.5N"]
7517function number.to16dot16(n)
7518 return f_16_16(n/65536.0)
7519end
7520if not string.explode then
7521 local p_utf=patterns.utf8character
7522 local p_check=C(p_utf)*(P("+")*Cc(true))^0
7523 local p_split=Ct(C(p_utf)^0)
7524 local p_space=Ct((C(1-P(" ")^1)+P(" ")^1)^0)
7525 function string.explode(str,symbol)
7526 if symbol=="" then
7527 return lpegmatch(p_split,str)
7528 elseif symbol then
7529 local a,b=lpegmatch(p_check,symbol)
7530 if b then
7531 return lpegmatch(tsplitat(P(a)^1),str)
7532 else
7533 return lpegmatch(tsplitat(a),str)
7534 end
7535 else
7536 return lpegmatch(p_space,str)
7537 end
7538 end
7539end
7540do
7541 local p_whitespace=patterns.whitespace^1
7542 local cache=setmetatable({},{ __index=function(t,k)
7543 local p=tsplitat(p_whitespace*P(k)*p_whitespace)
7544 local v=function(s)
7545 return lpegmatch(p,s)
7546 end
7547 t[k]=v
7548 return v
7549 end })
7550 function string.wordsplitter(s)
7551 return cache[s]
7552 end
7553end
7554if CONTEXTLMTXMODE and CONTEXTLMTXMODE>0 then
7555 local t={
7556 ["#"]="#H",
7557 ["\n"]="#L",
7558 ['"']="#Q",
7559 ["\r"]="#R",
7560 [" "]="#S",
7561 ["\t"]="#T",
7562 ["\\"]="#X",
7563 }
7564 function string.texhashed(s)
7565 return (gsub(s,".",t))
7566 end
7567end
7568
7569
7570end
7571
7572do
7573
7574package.loaded["util-tab"] = package.loaded["util-tab"] or true
7575
7576
7577
7578if not modules then modules={} end modules ['util-tab']={
7579 version=1.001,
7580 comment="companion to luat-lib.mkiv",
7581 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
7582 copyright="PRAGMA ADE / ConTeXt Development Team",
7583 license="see context related readme files"
7584}
7585utilities=utilities or {}
7586utilities.tables=utilities.tables or {}
7587local tables=utilities.tables
7588local format,gmatch,gsub,sub=string.format,string.gmatch,string.gsub,string.sub
7589local concat,insert,remove,sort=table.concat,table.insert,table.remove,table.sort
7590local setmetatable,getmetatable,tonumber,tostring,rawget=setmetatable,getmetatable,tonumber,tostring,rawget
7591local type,next,rawset,tonumber,tostring,load,select=type,next,rawset,tonumber,tostring,load,select
7592local lpegmatch,P,Cs,Cc=lpeg.match,lpeg.P,lpeg.Cs,lpeg.Cc
7593local sortedkeys,sortedpairs=table.sortedkeys,table.sortedpairs
7594local formatters=string.formatters
7595local utftoeight=utf.toeight
7596local splitter=lpeg.tsplitat(".")
7597function utilities.tables.definetable(target,nofirst,nolast)
7598 local composed=nil
7599 local t={}
7600 local snippets=lpegmatch(splitter,target)
7601 for i=1,#snippets-(nolast and 1 or 0) do
7602 local name=snippets[i]
7603 if composed then
7604 composed=composed.."."..name
7605 t[#t+1]=formatters["if not %s then %s = { } end"](composed,composed)
7606 else
7607 composed=name
7608 if not nofirst then
7609 t[#t+1]=formatters["%s = %s or { }"](composed,composed)
7610 end
7611 end
7612 end
7613 if composed then
7614 if nolast then
7615 composed=composed.."."..snippets[#snippets]
7616 end
7617 return concat(t,"\n"),composed
7618 else
7619 return "",target
7620 end
7621end
7622function tables.definedtable(...)
7623 local t=_G
7624 for i=1,select("#",...) do
7625 local li=select(i,...)
7626 local tl=t[li]
7627 if not tl then
7628 tl={}
7629 t[li]=tl
7630 end
7631 t=tl
7632 end
7633 return t
7634end
7635function tables.accesstable(target,root)
7636 local t=root or _G
7637 for name in gmatch(target,"([^%.]+)") do
7638 t=t[name]
7639 if not t then
7640 return
7641 end
7642 end
7643 return t
7644end
7645function tables.migratetable(target,v,root)
7646 local t=root or _G
7647 local names=lpegmatch(splitter,target)
7648 for i=1,#names-1 do
7649 local name=names[i]
7650 t[name]=t[name] or {}
7651 t=t[name]
7652 if not t then
7653 return
7654 end
7655 end
7656 t[names[#names]]=v
7657end
7658function tables.removevalue(t,value)
7659 if value then
7660 for i=1,#t do
7661 if t[i]==value then
7662 remove(t,i)
7663 end
7664 end
7665 end
7666end
7667function tables.replacevalue(t,oldvalue,newvalue)
7668 if oldvalue and newvalue then
7669 for i=1,#t do
7670 if t[i]==oldvalue then
7671 t[i]=newvalue
7672 end
7673 end
7674 end
7675end
7676function tables.insertbeforevalue(t,value,extra)
7677 for i=1,#t do
7678 if t[i]==extra then
7679 remove(t,i)
7680 end
7681 end
7682 for i=1,#t do
7683 if t[i]==value then
7684 insert(t,i,extra)
7685 return
7686 end
7687 end
7688 insert(t,1,extra)
7689end
7690function tables.insertaftervalue(t,value,extra)
7691 for i=1,#t do
7692 if t[i]==extra then
7693 remove(t,i)
7694 end
7695 end
7696 for i=1,#t do
7697 if t[i]==value then
7698 insert(t,i+1,extra)
7699 return
7700 end
7701 end
7702 insert(t,#t+1,extra)
7703end
7704local escape=Cs(Cc('"')*((P('"')/'""'+P(1))^0)*Cc('"'))
7705function table.tocsv(t,specification)
7706 if t and #t>0 then
7707 local result={}
7708 local r={}
7709 specification=specification or {}
7710 local fields=specification.fields
7711 if type(fields)~="string" then
7712 fields=sortedkeys(t[1])
7713 end
7714 local separator=specification.separator or ","
7715 local noffields=#fields
7716 if specification.preamble==true then
7717 for f=1,noffields do
7718 r[f]=lpegmatch(escape,tostring(fields[f]))
7719 end
7720 result[1]=concat(r,separator)
7721 end
7722 for i=1,#t do
7723 local ti=t[i]
7724 for f=1,noffields do
7725 local field=ti[fields[f]]
7726 if type(field)=="string" then
7727 r[f]=lpegmatch(escape,field)
7728 else
7729 r[f]=tostring(field)
7730 end
7731 end
7732 result[i+1]=concat(r,separator)
7733 end
7734 return concat(result,"\n")
7735 else
7736 return ""
7737 end
7738end
7739local nspaces=utilities.strings.newrepeater(" ")
7740local function toxml(t,d,result,step)
7741 local r=#result
7742 for k,v in sortedpairs(t) do
7743 local s=nspaces[d]
7744 local tk=type(k)
7745 local tv=type(v)
7746 if tv=="table" then
7747 if tk=="number" then
7748 r=r+1 result[r]=formatters["%s<entry n='%s'>"](s,k)
7749 toxml(v,d+step,result,step)
7750 r=r+1 result[r]=formatters["%s</entry>"](s,k)
7751 else
7752 r=r+1 result[r]=formatters["%s<%s>"](s,k)
7753 toxml(v,d+step,result,step)
7754 r=r+1 result[r]=formatters["%s</%s>"](s,k)
7755 end
7756 elseif tv=="string" then
7757 if tk=="number" then
7758 r=r+1 result[r]=formatters["%s<entry n='%s'>%!xml!</entry>"](s,k,v,k)
7759 else
7760 r=r+1 result[r]=formatters["%s<%s>%!xml!</%s>"](s,k,v,k)
7761 end
7762 elseif tk=="number" then
7763 r=r+1 result[r]=formatters["%s<entry n='%s'>%S</entry>"](s,k,v,k)
7764 else
7765 r=r+1 result[r]=formatters["%s<%s>%S</%s>"](s,k,v,k)
7766 end
7767 end
7768end
7769function table.toxml(t,specification)
7770 specification=specification or {}
7771 local name=specification.name
7772 local noroot=name==false
7773 local result=(specification.nobanner or noroot) and {} or { "<?xml version='1.0' standalone='yes' ?>" }
7774 local indent=specification.indent or 0
7775 local spaces=specification.spaces or 1
7776 if noroot then
7777 toxml(t,indent,result,spaces)
7778 else
7779 toxml({ [name or "data"]=t },indent,result,spaces)
7780 end
7781 return concat(result,"\n")
7782end
7783function tables.encapsulate(core,capsule,protect)
7784 if type(capsule)~="table" then
7785 protect=true
7786 capsule={}
7787 end
7788 for key,value in next,core do
7789 if capsule[key] then
7790 print(formatters["\ninvalid %s %a in %a"]("inheritance",key,core))
7791 os.exit()
7792 else
7793 capsule[key]=value
7794 end
7795 end
7796 if protect then
7797 for key,value in next,core do
7798 core[key]=nil
7799 end
7800 setmetatable(core,{
7801 __index=capsule,
7802 __newindex=function(t,key,value)
7803 if capsule[key] then
7804 print(formatters["\ninvalid %s %a' in %a"]("overload",key,core))
7805 os.exit()
7806 else
7807 rawset(t,key,value)
7808 end
7809 end
7810 } )
7811 end
7812end
7813if JITSUPPORTED then
7814 local f_hashed_string=formatters["[%Q]=%Q,"]
7815 local f_hashed_number=formatters["[%Q]=%s,"]
7816 local f_hashed_boolean=formatters["[%Q]=%l,"]
7817 local f_hashed_table=formatters["[%Q]="]
7818 local f_indexed_string=formatters["[%s]=%Q,"]
7819 local f_indexed_number=formatters["[%s]=%s,"]
7820 local f_indexed_boolean=formatters["[%s]=%l,"]
7821 local f_indexed_table=formatters["[%s]="]
7822 local f_ordered_string=formatters["%Q,"]
7823 local f_ordered_number=formatters["%s,"]
7824 local f_ordered_boolean=formatters["%l,"]
7825 function table.fastserialize(t,prefix)
7826 local r={ type(prefix)=="string" and prefix or "return" }
7827 local m=1
7828 local function fastserialize(t,outer)
7829 local n=#t
7830 m=m+1
7831 r[m]="{"
7832 if n>0 then
7833 local v=t[0]
7834 if v then
7835 local tv=type(v)
7836 if tv=="string" then
7837 m=m+1 r[m]=f_indexed_string(0,v)
7838 elseif tv=="number" then
7839 m=m+1 r[m]=f_indexed_number(0,v)
7840 elseif tv=="table" then
7841 m=m+1 r[m]=f_indexed_table(0)
7842 fastserialize(v)
7843 m=m+1 r[m]=f_indexed_table(0)
7844 elseif tv=="boolean" then
7845 m=m+1 r[m]=f_indexed_boolean(0,v)
7846 end
7847 end
7848 for i=1,n do
7849 local v=t[i]
7850 local tv=type(v)
7851 if tv=="string" then
7852 m=m+1 r[m]=f_ordered_string(v)
7853 elseif tv=="number" then
7854 m=m+1 r[m]=f_ordered_number(v)
7855 elseif tv=="table" then
7856 fastserialize(v)
7857 elseif tv=="boolean" then
7858 m=m+1 r[m]=f_ordered_boolean(v)
7859 end
7860 end
7861 end
7862 for k,v in next,t do
7863 local tk=type(k)
7864 if tk=="number" then
7865 if k>n or k<0 then
7866 local tv=type(v)
7867 if tv=="string" then
7868 m=m+1 r[m]=f_indexed_string(k,v)
7869 elseif tv=="number" then
7870 m=m+1 r[m]=f_indexed_number(k,v)
7871 elseif tv=="table" then
7872 m=m+1 r[m]=f_indexed_table(k)
7873 fastserialize(v)
7874 elseif tv=="boolean" then
7875 m=m+1 r[m]=f_indexed_boolean(k,v)
7876 end
7877 end
7878 else
7879 local tv=type(v)
7880 if tv=="string" then
7881 m=m+1 r[m]=f_hashed_string(k,v)
7882 elseif tv=="number" then
7883 m=m+1 r[m]=f_hashed_number(k,v)
7884 elseif tv=="table" then
7885 m=m+1 r[m]=f_hashed_table(k)
7886 fastserialize(v)
7887 elseif tv=="boolean" then
7888 m=m+1 r[m]=f_hashed_boolean(k,v)
7889 end
7890 end
7891 end
7892 m=m+1
7893 if outer then
7894 r[m]="}"
7895 else
7896 r[m]="},"
7897 end
7898 return r
7899 end
7900 return concat(fastserialize(t,true))
7901 end
7902else
7903 function table.fastserialize(t,prefix)
7904 local r={ type(prefix)=="string" and prefix or "return" }
7905 local m=1
7906 local function fastserialize(t,outer)
7907 local n=#t
7908 m=m+1
7909 r[m]="{"
7910 if n>0 then
7911 local v=t[0]
7912 if v then
7913 m=m+1
7914 r[m]="[0]="
7915 if type(v)=="table" then
7916 fastserialize(v)
7917 else
7918 r[m]=format("%q,",v)
7919 end
7920 end
7921 for i=1,n do
7922 local v=t[i]
7923 m=m+1
7924 if type(v)=="table" then
7925 r[m]=format("[%i]=",i)
7926 fastserialize(v)
7927 else
7928 r[m]=format("[%i]=%q,",i,v)
7929 end
7930 end
7931 end
7932 for k,v in next,t do
7933 local tk=type(k)
7934 if tk=="number" then
7935 if k>n or k<0 then
7936 m=m+1
7937 if type(v)=="table" then
7938 r[m]=format("[%i]=",k)
7939 fastserialize(v)
7940 else
7941 r[m]=format("[%i]=%q,",k,v)
7942 end
7943 end
7944 else
7945 m=m+1
7946 if type(v)=="table" then
7947 r[m]=format("[%q]=",k)
7948 fastserialize(v)
7949 else
7950 r[m]=format("[%q]=%q,",k,v)
7951 end
7952 end
7953 end
7954 m=m+1
7955 if outer then
7956 r[m]="}"
7957 else
7958 r[m]="},"
7959 end
7960 return r
7961 end
7962 return concat(fastserialize(t,true))
7963 end
7964end
7965function table.deserialize(str)
7966 if not str or str=="" then
7967 return
7968 end
7969 local code=load(str)
7970 if not code then
7971 return
7972 end
7973 code=code()
7974 if not code then
7975 return
7976 end
7977 return code
7978end
7979function table.load(filename,loader)
7980 if filename then
7981 local t=(loader or io.loaddata)(filename)
7982 if t and t~="" then
7983 local t=utftoeight(t)
7984 t=load(t)
7985 if type(t)=="function" then
7986 t=t()
7987 if type(t)=="table" then
7988 return t
7989 end
7990 end
7991 end
7992 end
7993end
7994function table.save(filename,t,n,...)
7995 io.savedata(filename,table.serialize(t,n==nil and true or n,...))
7996end
7997local f_key_value=formatters["%s=%q"]
7998local f_add_table=formatters[" {%t},\n"]
7999local f_return_table=formatters["return {\n%t}"]
8000local function slowdrop(t)
8001 local r={}
8002 local l={}
8003 for i=1,#t do
8004 local ti=t[i]
8005 local j=0
8006 for k,v in next,ti do
8007 j=j+1
8008 l[j]=f_key_value(k,v)
8009 end
8010 r[i]=f_add_table(l)
8011 end
8012 return f_return_table(r)
8013end
8014local function fastdrop(t)
8015 local r={ "return {\n" }
8016 local m=1
8017 for i=1,#t do
8018 local ti=t[i]
8019 m=m+1 r[m]=" {"
8020 for k,v in next,ti do
8021 m=m+1 r[m]=f_key_value(k,v)
8022 end
8023 m=m+1 r[m]="},\n"
8024 end
8025 m=m+1
8026 r[m]="}"
8027 return concat(r)
8028end
8029function table.drop(t,slow)
8030 if #t==0 then
8031 return "return { }"
8032 elseif slow==true then
8033 return slowdrop(t)
8034 else
8035 return fastdrop(t)
8036 end
8037end
8038local selfmapper={ __index=function(t,k) t[k]=k return k end }
8039function table.twowaymapper(t)
8040 if not t then
8041 t={}
8042 else
8043 local zero=rawget(t,0)
8044 for i=zero and 0 or 1,#t do
8045 local ti=t[i]
8046 if ti then
8047 local i=tostring(i)
8048 t[i]=ti
8049 t[ti]=i
8050 end
8051 end
8052 end
8053 setmetatable(t,selfmapper)
8054 return t
8055end
8056local f_start_key_idx=formatters["%w{"]
8057local f_start_key_num=JITSUPPORTED and formatters["%w[%s]={"] or formatters["%w[%q]={"]
8058local f_start_key_str=formatters["%w[%q]={"]
8059local f_start_key_boo=formatters["%w[%l]={"]
8060local f_start_key_nop=formatters["%w{"]
8061local f_stop=formatters["%w},"]
8062local f_key_num_value_num=JITSUPPORTED and formatters["%w[%s]=%s,"] or formatters["%w[%s]=%q,"]
8063local f_key_str_value_num=JITSUPPORTED and formatters["%w[%Q]=%s,"] or formatters["%w[%Q]=%q,"]
8064local f_key_boo_value_num=JITSUPPORTED and formatters["%w[%l]=%s,"] or formatters["%w[%l]=%q,"]
8065local f_key_num_value_str=JITSUPPORTED and formatters["%w[%s]=%Q,"] or formatters["%w[%q]=%Q,"]
8066local f_key_str_value_str=formatters["%w[%Q]=%Q,"]
8067local f_key_boo_value_str=formatters["%w[%l]=%Q,"]
8068local f_key_num_value_boo=JITSUPPORTED and formatters["%w[%s]=%l,"] or formatters["%w[%q]=%l,"]
8069local f_key_str_value_boo=formatters["%w[%Q]=%l,"]
8070local f_key_boo_value_boo=formatters["%w[%l]=%l,"]
8071local f_key_num_value_not=JITSUPPORTED and formatters["%w[%s]={},"] or formatters["%w[%q]={},"]
8072local f_key_str_value_not=formatters["%w[%Q]={},"]
8073local f_key_boo_value_not=formatters["%w[%l]={},"]
8074local f_key_num_value_seq=JITSUPPORTED and formatters["%w[%s]={ %, t },"] or formatters["%w[%q]={ %, t },"]
8075local f_key_str_value_seq=formatters["%w[%Q]={ %, t },"]
8076local f_key_boo_value_seq=formatters["%w[%l]={ %, t },"]
8077local f_val_num=JITSUPPORTED and formatters["%w%s,"] or formatters["%w%q,"]
8078local f_val_str=formatters["%w%Q,"]
8079local f_val_boo=formatters["%w%l,"]
8080local f_val_not=formatters["%w{},"]
8081local f_val_seq=formatters["%w{ %, t },"]
8082local f_fin_seq=formatters[" %, t }"]
8083local f_table_return=formatters["return {"]
8084local f_table_name=formatters["%s={"]
8085local f_table_direct=formatters["{"]
8086local f_table_entry=formatters["[%Q]={"]
8087local f_table_finish=formatters["}"]
8088local spaces=utilities.strings.newrepeater(" ")
8089local original_serialize=table.serialize
8090local is_simple_table=table.is_simple_table
8091local function serialize(root,name,specification)
8092 if type(specification)=="table" then
8093 return original_serialize(root,name,specification)
8094 end
8095 local t
8096 local n=1
8097 local unknown=false
8098 local function do_serialize(root,name,depth,level,indexed)
8099 if level>0 then
8100 n=n+1
8101 if indexed then
8102 t[n]=f_start_key_idx(depth)
8103 else
8104 local tn=type(name)
8105 if tn=="number" then
8106 t[n]=f_start_key_num(depth,name)
8107 elseif tn=="string" then
8108 t[n]=f_start_key_str(depth,name)
8109 elseif tn=="boolean" then
8110 t[n]=f_start_key_boo(depth,name)
8111 else
8112 t[n]=f_start_key_nop(depth)
8113 end
8114 end
8115 depth=depth+1
8116 end
8117 if root and next(root)~=nil then
8118 local first=nil
8119 local last=#root
8120 if last>0 then
8121 for k=1,last do
8122 if rawget(root,k)==nil then
8123 last=k-1
8124 break
8125 end
8126 end
8127 if last>0 then
8128 first=1
8129 end
8130 end
8131 local sk=sortedkeys(root)
8132 for i=1,#sk do
8133 local k=sk[i]
8134 local v=root[k]
8135 local tv=type(v)
8136 local tk=type(k)
8137 if first and tk=="number" and k<=last and k>=first then
8138 if tv=="number" then
8139 n=n+1 t[n]=f_val_num(depth,v)
8140 elseif tv=="string" then
8141 n=n+1 t[n]=f_val_str(depth,v)
8142 elseif tv=="table" then
8143 if next(v)==nil then
8144 n=n+1 t[n]=f_val_not(depth)
8145 else
8146 local st=is_simple_table(v)
8147 if st then
8148 n=n+1 t[n]=f_val_seq(depth,st)
8149 else
8150 do_serialize(v,k,depth,level+1,true)
8151 end
8152 end
8153 elseif tv=="boolean" then
8154 n=n+1 t[n]=f_val_boo(depth,v)
8155 elseif unknown then
8156 n=n+1 t[n]=f_val_str(depth,tostring(v))
8157 end
8158 elseif tv=="number" then
8159 if tk=="number" then
8160 n=n+1 t[n]=f_key_num_value_num(depth,k,v)
8161 elseif tk=="string" then
8162 n=n+1 t[n]=f_key_str_value_num(depth,k,v)
8163 elseif tk=="boolean" then
8164 n=n+1 t[n]=f_key_boo_value_num(depth,k,v)
8165 elseif unknown then
8166 n=n+1 t[n]=f_key_str_value_num(depth,tostring(k),v)
8167 end
8168 elseif tv=="string" then
8169 if tk=="number" then
8170 n=n+1 t[n]=f_key_num_value_str(depth,k,v)
8171 elseif tk=="string" then
8172 n=n+1 t[n]=f_key_str_value_str(depth,k,v)
8173 elseif tk=="boolean" then
8174 n=n+1 t[n]=f_key_boo_value_str(depth,k,v)
8175 elseif unknown then
8176 n=n+1 t[n]=f_key_str_value_str(depth,tostring(k),v)
8177 end
8178 elseif tv=="table" then
8179 if next(v)==nil then
8180 if tk=="number" then
8181 n=n+1 t[n]=f_key_num_value_not(depth,k)
8182 elseif tk=="string" then
8183 n=n+1 t[n]=f_key_str_value_not(depth,k)
8184 elseif tk=="boolean" then
8185 n=n+1 t[n]=f_key_boo_value_not(depth,k)
8186 elseif unknown then
8187 n=n+1 t[n]=f_key_str_value_not(depth,tostring(k))
8188 end
8189 else
8190 local st=is_simple_table(v)
8191 if not st then
8192 do_serialize(v,k,depth,level+1)
8193 elseif tk=="number" then
8194 n=n+1 t[n]=f_key_num_value_seq(depth,k,st)
8195 elseif tk=="string" then
8196 n=n+1 t[n]=f_key_str_value_seq(depth,k,st)
8197 elseif tk=="boolean" then
8198 n=n+1 t[n]=f_key_boo_value_seq(depth,k,st)
8199 elseif unknown then
8200 n=n+1 t[n]=f_key_str_value_seq(depth,tostring(k),st)
8201 end
8202 end
8203 elseif tv=="boolean" then
8204 if tk=="number" then
8205 n=n+1 t[n]=f_key_num_value_boo(depth,k,v)
8206 elseif tk=="string" then
8207 n=n+1 t[n]=f_key_str_value_boo(depth,k,v)
8208 elseif tk=="boolean" then
8209 n=n+1 t[n]=f_key_boo_value_boo(depth,k,v)
8210 elseif unknown then
8211 n=n+1 t[n]=f_key_str_value_boo(depth,tostring(k),v)
8212 end
8213 else
8214 if tk=="number" then
8215 n=n+1 t[n]=f_key_num_value_str(depth,k,tostring(v))
8216 elseif tk=="string" then
8217 n=n+1 t[n]=f_key_str_value_str(depth,k,tostring(v))
8218 elseif tk=="boolean" then
8219 n=n+1 t[n]=f_key_boo_value_str(depth,k,tostring(v))
8220 elseif unknown then
8221 n=n+1 t[n]=f_key_str_value_str(depth,tostring(k),tostring(v))
8222 end
8223 end
8224 end
8225 end
8226 if level>0 then
8227 n=n+1 t[n]=f_stop(depth-1)
8228 end
8229 end
8230 local tname=type(name)
8231 if tname=="string" then
8232 if name=="return" then
8233 t={ f_table_return() }
8234 else
8235 t={ f_table_name(name) }
8236 end
8237 elseif tname=="number" then
8238 t={ f_table_entry(name) }
8239 elseif tname=="boolean" then
8240 if name then
8241 t={ f_table_return() }
8242 else
8243 t={ f_table_direct() }
8244 end
8245 else
8246 t={ f_table_name("t") }
8247 end
8248 if root then
8249 if getmetatable(root) then
8250 local dummy=root._w_h_a_t_e_v_e_r_
8251 root._w_h_a_t_e_v_e_r_=nil
8252 end
8253 if next(root)~=nil then
8254 local st=is_simple_table(root)
8255 if st then
8256 return t[1]..f_fin_seq(st)
8257 else
8258 do_serialize(root,name,1,0)
8259 end
8260 end
8261 end
8262 n=n+1
8263 t[n]=f_table_finish()
8264 return concat(t,"\n")
8265end
8266table.serialize=serialize
8267if setinspector then
8268 setinspector("table",function(v)
8269 if type(v)=="table" then
8270 print(serialize(v,"table",{ metacheck=false }))
8271 return true
8272 end
8273 end)
8274end
8275local mt={
8276 __newindex=function(t,k,v)
8277 local n=t.last+1
8278 t.last=n
8279 t.list[n]=k
8280 t.hash[k]=v
8281 end,
8282 __index=function(t,k)
8283 return t.hash[k]
8284 end,
8285 __len=function(t)
8286 return t.last
8287 end,
8288}
8289function table.orderedhash()
8290 return setmetatable({ list={},hash={},last=0 },mt)
8291end
8292function table.ordered(t)
8293 local n=t.last
8294 if n>0 then
8295 local l=t.list
8296 local i=1
8297 local h=t.hash
8298 local f=function()
8299 if i<=n then
8300 local k=i
8301 local v=h[l[k]]
8302 i=i+1
8303 return k,v
8304 end
8305 end
8306 return f,1,h[l[1]]
8307 else
8308 return function() end
8309 end
8310end
8311function combine(target,source)
8312 if target then
8313 for k,v in next,source do
8314 if type(v)=="table" then
8315 target[k]=combine(target[k],source[k])
8316 else
8317 target[k]=v
8318 end
8319 end
8320 return target
8321 else
8322 return source
8323 end
8324end
8325table.combine=combine
8326
8327
8328end
8329
8330do
8331
8332package.loaded["util-fil"] = package.loaded["util-fil"] or true
8333
8334
8335
8336if not modules then modules={} end modules ['util-fil']={
8337 version=1.001,
8338 optimize=true,
8339 comment="companion to luat-lib.mkiv",
8340 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
8341 copyright="PRAGMA ADE / ConTeXt Development Team",
8342 license="see context related readme files"
8343}
8344local tonumber=tonumber
8345local byte=string.byte
8346local char=string.char
8347utilities=utilities or {}
8348local files={}
8349utilities.files=files
8350local zerobased={}
8351function files.open(filename,zb)
8352 local f=io.open(filename,"rb")
8353 if f then
8354 zerobased[f]=zb or false
8355 end
8356 return f
8357end
8358function files.close(f)
8359 zerobased[f]=nil
8360 f:close()
8361end
8362function files.size(f)
8363 local current=f:seek()
8364 local size=f:seek("end")
8365 f:seek("set",current)
8366 return size
8367end
8368files.getsize=files.size
8369function files.setposition(f,n)
8370 if zerobased[f] then
8371 f:seek("set",n)
8372 else
8373 f:seek("set",n-1)
8374 end
8375end
8376function files.getposition(f)
8377 if zerobased[f] then
8378 return f:seek()
8379 else
8380 return f:seek()+1
8381 end
8382end
8383function files.look(f,n,chars)
8384 local p=f:seek()
8385 local s=f:read(n)
8386 f:seek("set",p)
8387 if chars then
8388 return s
8389 else
8390 return byte(s,1,#s)
8391 end
8392end
8393function files.skip(f,n)
8394 if n==1 then
8395 f:read(n)
8396 else
8397 f:seek("set",f:seek()+n)
8398 end
8399end
8400function files.readbyte(f)
8401 return byte(f:read(1))
8402end
8403function files.readbytes(f,n)
8404 return byte(f:read(n),1,n)
8405end
8406function files.readbytetable(f,n)
8407 local s=f:read(n or 1)
8408 return { byte(s,1,#s) }
8409end
8410function files.readchar(f)
8411 return f:read(1)
8412end
8413function files.readstring(f,n)
8414 return f:read(n or 1)
8415end
8416function files.readinteger1(f)
8417 local n=byte(f:read(1))
8418 if n>=0x80 then
8419 return n-0x100
8420 else
8421 return n
8422 end
8423end
8424files.readcardinal1=files.readbyte
8425files.readcardinal=files.readcardinal1
8426files.readinteger=files.readinteger1
8427files.readsignedbyte=files.readinteger1
8428function files.readcardinal2(f)
8429 local a,b=byte(f:read(2),1,2)
8430 return 0x100*a+b
8431end
8432function files.readcardinal2le(f)
8433 local b,a=byte(f:read(2),1,2)
8434 return 0x100*a+b
8435end
8436function files.readinteger2(f)
8437 local a,b=byte(f:read(2),1,2)
8438 if a>=0x80 then
8439 return 0x100*a+b-0x10000
8440 else
8441 return 0x100*a+b
8442 end
8443end
8444function files.readinteger2le(f)
8445 local b,a=byte(f:read(2),1,2)
8446 if a>=0x80 then
8447 return 0x100*a+b-0x10000
8448 else
8449 return 0x100*a+b
8450 end
8451end
8452function files.readcardinal3(f)
8453 local a,b,c=byte(f:read(3),1,3)
8454 return 0x10000*a+0x100*b+c
8455end
8456function files.readcardinal3le(f)
8457 local c,b,a=byte(f:read(3),1,3)
8458 return 0x10000*a+0x100*b+c
8459end
8460function files.readinteger3(f)
8461 local a,b,c=byte(f:read(3),1,3)
8462 if a>=0x80 then
8463 return 0x10000*a+0x100*b+c-0x1000000
8464 else
8465 return 0x10000*a+0x100*b+c
8466 end
8467end
8468function files.readinteger3le(f)
8469 local c,b,a=byte(f:read(3),1,3)
8470 if a>=0x80 then
8471 return 0x10000*a+0x100*b+c-0x1000000
8472 else
8473 return 0x10000*a+0x100*b+c
8474 end
8475end
8476function files.readcardinal4(f)
8477 local a,b,c,d=byte(f:read(4),1,4)
8478 return 0x1000000*a+0x10000*b+0x100*c+d
8479end
8480function files.readcardinal4le(f)
8481 local d,c,b,a=byte(f:read(4),1,4)
8482 return 0x1000000*a+0x10000*b+0x100*c+d
8483end
8484function files.readinteger4(f)
8485 local a,b,c,d=byte(f:read(4),1,4)
8486 if a>=0x80 then
8487 return 0x1000000*a+0x10000*b+0x100*c+d-0x100000000
8488 else
8489 return 0x1000000*a+0x10000*b+0x100*c+d
8490 end
8491end
8492function files.readinteger4le(f)
8493 local d,c,b,a=byte(f:read(4),1,4)
8494 if a>=0x80 then
8495 return 0x1000000*a+0x10000*b+0x100*c+d-0x100000000
8496 else
8497 return 0x1000000*a+0x10000*b+0x100*c+d
8498 end
8499end
8500function files.readfixed2(f)
8501 local n1,n2=byte(f:read(2),1,2)
8502 if n1>=0x80 then
8503 n1=n1-0x100
8504 end
8505 return n1+n2/0xFF
8506end
8507function files.readfixed4(f)
8508 local a,b,c,d=byte(f:read(4),1,4)
8509 local n1=0x100*a+b
8510 local n2=0x100*c+d
8511 if n1>=0x8000 then
8512 n1=n1-0x10000
8513 end
8514 return n1+n2/0xFFFF
8515end
8516if bit32 then
8517 local extract=bit32.extract
8518 local band=bit32.band
8519 function files.read2dot14(f)
8520 local a,b=byte(f:read(2),1,2)
8521 if a>=0x80 then
8522 local n=-(0x100*a+b)
8523 return-(extract(n,14,2)+(band(n,0x3FFF)/16384.0))
8524 else
8525 local n=0x100*a+b
8526 return (extract(n,14,2)+(band(n,0x3FFF)/16384.0))
8527 end
8528 end
8529end
8530function files.skipshort(f,n)
8531 f:read(2*(n or 1))
8532end
8533function files.skiplong(f,n)
8534 f:read(4*(n or 1))
8535end
8536if bit32 then
8537 local rshift=bit32.rshift
8538 function files.writecardinal2(f,n)
8539 local a=char(n%256)
8540 n=rshift(n,8)
8541 local b=char(n%256)
8542 f:write(b,a)
8543 end
8544 function files.writecardinal4(f,n)
8545 local a=char(n%256)
8546 n=rshift(n,8)
8547 local b=char(n%256)
8548 n=rshift(n,8)
8549 local c=char(n%256)
8550 n=rshift(n,8)
8551 local d=char(n%256)
8552 f:write(d,c,b,a)
8553 end
8554 function files.writecardinal2le(f,n)
8555 local a=char(n%256)
8556 n=rshift(n,8)
8557 local b=char(n%256)
8558 f:write(a,b)
8559 end
8560 function files.writecardinal4le(f,n)
8561 local a=char(n%256)
8562 n=rshift(n,8)
8563 local b=char(n%256)
8564 n=rshift(n,8)
8565 local c=char(n%256)
8566 n=rshift(n,8)
8567 local d=char(n%256)
8568 f:write(a,b,c,d)
8569 end
8570else
8571 local floor=math.floor
8572 function files.writecardinal2(f,n)
8573 local a=char(n%256)
8574 n=floor(n/256)
8575 local b=char(n%256)
8576 f:write(b,a)
8577 end
8578 function files.writecardinal4(f,n)
8579 local a=char(n%256)
8580 n=floor(n/256)
8581 local b=char(n%256)
8582 n=floor(n/256)
8583 local c=char(n%256)
8584 n=floor(n/256)
8585 local d=char(n%256)
8586 f:write(d,c,b,a)
8587 end
8588 function files.writecardinal2le(f,n)
8589 local a=char(n%256)
8590 n=floor(n/256)
8591 local b=char(n%256)
8592 f:write(a,b)
8593 end
8594 function files.writecardinal4le(f,n)
8595 local a=char(n%256)
8596 n=floor(n/256)
8597 local b=char(n%256)
8598 n=floor(n/256)
8599 local c=char(n%256)
8600 n=floor(n/256)
8601 local d=char(n%256)
8602 f:write(a,b,c,d)
8603 end
8604end
8605function files.writestring(f,s)
8606 f:write(char(byte(s,1,#s)))
8607end
8608function files.writebyte(f,b)
8609 f:write(char(b))
8610end
8611if fio and fio.readcardinal1 then
8612 files.readcardinal1=fio.readcardinal1
8613 files.readcardinal2=fio.readcardinal2
8614 files.readcardinal3=fio.readcardinal3
8615 files.readcardinal4=fio.readcardinal4
8616 files.readcardinal1le=fio.readcardinal1le or files.readcardinal1le
8617 files.readcardinal2le=fio.readcardinal2le or files.readcardinal2le
8618 files.readcardinal3le=fio.readcardinal3le or files.readcardinal3le
8619 files.readcardinal4le=fio.readcardinal4le or files.readcardinal4le
8620 files.readinteger1=fio.readinteger1
8621 files.readinteger2=fio.readinteger2
8622 files.readinteger3=fio.readinteger3
8623 files.readinteger4=fio.readinteger4
8624 files.readinteger1le=fio.readinteger1le or files.readinteger1le
8625 files.readinteger2le=fio.readinteger2le or files.readinteger2le
8626 files.readinteger3le=fio.readinteger3le or files.readinteger3le
8627 files.readinteger4le=fio.readinteger4le or files.readinteger4le
8628 files.readfixed2=fio.readfixed2
8629 files.readfixed4=fio.readfixed4
8630 files.read2dot14=fio.read2dot14
8631 files.setposition=fio.setposition
8632 files.getposition=fio.getposition
8633 files.readbyte=files.readcardinal1
8634 files.readsignedbyte=files.readinteger1
8635 files.readcardinal=files.readcardinal1
8636 files.readinteger=files.readinteger1
8637 local skipposition=fio.skipposition
8638 files.skipposition=skipposition
8639 files.readbytes=fio.readbytes
8640 files.readbytetable=fio.readbytetable
8641 function files.skipshort(f,n)
8642 skipposition(f,2*(n or 1))
8643 end
8644 function files.skiplong(f,n)
8645 skipposition(f,4*(n or 1))
8646 end
8647end
8648if fio and fio.writecardinal1 then
8649 files.writecardinal1=fio.writecardinal1
8650 files.writecardinal2=fio.writecardinal2
8651 files.writecardinal3=fio.writecardinal3
8652 files.writecardinal4=fio.writecardinal4
8653 files.writecardinal1le=fio.writecardinal1le
8654 files.writecardinal2le=fio.writecardinal2le
8655 files.writecardinal3le=fio.writecardinal3le
8656 files.writecardinal4le=fio.writecardinal4le
8657 files.writeinteger1=fio.writeinteger1 or fio.writecardinal1
8658 files.writeinteger2=fio.writeinteger2 or fio.writecardinal2
8659 files.writeinteger3=fio.writeinteger3 or fio.writecardinal3
8660 files.writeinteger4=fio.writeinteger4 or fio.writecardinal4
8661 files.writeinteger1le=files.writeinteger1le or fio.writecardinal1le
8662 files.writeinteger2le=files.writeinteger2le or fio.writecardinal2le
8663 files.writeinteger3le=files.writeinteger3le or fio.writecardinal3le
8664 files.writeinteger4le=files.writeinteger4le or fio.writecardinal4le
8665end
8666if fio and fio.readcardinaltable then
8667 files.readcardinaltable=fio.readcardinaltable
8668 files.readintegertable=fio.readintegertable
8669else
8670 local readcardinal1=files.readcardinal1
8671 local readcardinal2=files.readcardinal2
8672 local readcardinal3=files.readcardinal3
8673 local readcardinal4=files.readcardinal4
8674 function files.readcardinaltable(f,n,b)
8675 local t={}
8676 if b==1 then for i=1,n do t[i]=readcardinal1(f) end
8677 elseif b==2 then for i=1,n do t[i]=readcardinal2(f) end
8678 elseif b==3 then for i=1,n do t[i]=readcardinal3(f) end
8679 elseif b==4 then for i=1,n do t[i]=readcardinal4(f) end end
8680 return t
8681 end
8682 local readinteger1=files.readinteger1
8683 local readinteger2=files.readinteger2
8684 local readinteger3=files.readinteger3
8685 local readinteger4=files.readinteger4
8686 function files.readintegertable(f,n,b)
8687 local t={}
8688 if b==1 then for i=1,n do t[i]=readinteger1(f) end
8689 elseif b==2 then for i=1,n do t[i]=readinteger2(f) end
8690 elseif b==3 then for i=1,n do t[i]=readinteger3(f) end
8691 elseif b==4 then for i=1,n do t[i]=readinteger4(f) end end
8692 return t
8693 end
8694end
8695
8696
8697end
8698
8699do
8700
8701package.loaded["util-sac"] = package.loaded["util-sac"] or true
8702
8703
8704
8705if not modules then modules={} end modules ['util-sac']={
8706 version=1.001,
8707 optimize=true,
8708 comment="companion to luat-lib.mkiv",
8709 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
8710 copyright="PRAGMA ADE / ConTeXt Development Team",
8711 license="see context related readme files"
8712}
8713local byte,sub=string.byte,string.sub
8714local tonumber=tonumber
8715utilities=utilities or {}
8716local streams={}
8717utilities.streams=streams
8718function streams.open(filename,zerobased)
8719 local f=filename and io.loaddata(filename)
8720 if f then
8721 return { f,1,#f,zerobased or false }
8722 end
8723end
8724function streams.openstring(f,zerobased)
8725 if f then
8726 return { f,1,#f,zerobased or false }
8727 end
8728end
8729function streams.getstring(f)
8730 if f then
8731 return f[1]
8732 end
8733end
8734function streams.close()
8735end
8736function streams.size(f)
8737 return f and f[3] or 0
8738end
8739streams.getsize=streams.size
8740function streams.setposition(f,i)
8741 if f[4] then
8742 if i<=0 then
8743 f[2]=1
8744 else
8745 f[2]=i+1
8746 end
8747 else
8748 if i<=1 then
8749 f[2]=1
8750 else
8751 f[2]=i
8752 end
8753 end
8754end
8755function streams.getposition(f)
8756 if f[4] then
8757 return f[2]-1
8758 else
8759 return f[2]
8760 end
8761end
8762function streams.look(f,n,chars)
8763 local b=f[2]
8764 local e=b+n-1
8765 if chars then
8766 return sub(f[1],b,e)
8767 else
8768 return byte(f[1],b,e)
8769 end
8770end
8771function streams.skip(f,n)
8772 f[2]=f[2]+n
8773end
8774function streams.readbyte(f)
8775 local i=f[2]
8776 f[2]=i+1
8777 return byte(f[1],i)
8778end
8779function streams.readbytes(f,n)
8780 local i=f[2]
8781 local j=i+n
8782 f[2]=j
8783 return byte(f[1],i,j-1)
8784end
8785function streams.readbytetable(f,n)
8786 local i=f[2]
8787 local j=i+n
8788 f[2]=j
8789 return { byte(f[1],i,j-1) }
8790end
8791function streams.skipbytes(f,n)
8792 f[2]=f[2]+n
8793end
8794function streams.readchar(f)
8795 local i=f[2]
8796 f[2]=i+1
8797 return sub(f[1],i,i)
8798end
8799function streams.readstring(f,n)
8800 local i=f[2]
8801 local j=i+n
8802 f[2]=j
8803 return sub(f[1],i,j-1)
8804end
8805function streams.readinteger1(f)
8806 local i=f[2]
8807 f[2]=i+1
8808 local n=byte(f[1],i)
8809 if n>=0x80 then
8810 return n-0x100
8811 else
8812 return n
8813 end
8814end
8815streams.readcardinal1=streams.readbyte
8816streams.readcardinal=streams.readcardinal1
8817streams.readinteger=streams.readinteger1
8818function streams.readcardinal2(f)
8819 local i=f[2]
8820 local j=i+1
8821 f[2]=j+1
8822 local a,b=byte(f[1],i,j)
8823 return 0x100*a+b
8824end
8825function streams.readcardinal2le(f)
8826 local i=f[2]
8827 local j=i+1
8828 f[2]=j+1
8829 local b,a=byte(f[1],i,j)
8830 return 0x100*a+b
8831end
8832function streams.readinteger2(f)
8833 local i=f[2]
8834 local j=i+1
8835 f[2]=j+1
8836 local a,b=byte(f[1],i,j)
8837 if a>=0x80 then
8838 return 0x100*a+b-0x10000
8839 else
8840 return 0x100*a+b
8841 end
8842end
8843function streams.readinteger2le(f)
8844 local i=f[2]
8845 local j=i+1
8846 f[2]=j+1
8847 local b,a=byte(f[1],i,j)
8848 if a>=0x80 then
8849 return 0x100*a+b-0x10000
8850 else
8851 return 0x100*a+b
8852 end
8853end
8854function streams.readcardinal3(f)
8855 local i=f[2]
8856 local j=i+2
8857 f[2]=j+1
8858 local a,b,c=byte(f[1],i,j)
8859 return 0x10000*a+0x100*b+c
8860end
8861function streams.readcardinal3le(f)
8862 local i=f[2]
8863 local j=i+2
8864 f[2]=j+1
8865 local c,b,a=byte(f[1],i,j)
8866 return 0x10000*a+0x100*b+c
8867end
8868function streams.readinteger3(f)
8869 local i=f[2]
8870 local j=i+3
8871 f[2]=j+1
8872 local a,b,c=byte(f[1],i,j)
8873 if a>=0x80 then
8874 return 0x10000*a+0x100*b+c-0x1000000
8875 else
8876 return 0x10000*a+0x100*b+c
8877 end
8878end
8879function streams.readinteger3le(f)
8880 local i=f[2]
8881 local j=i+3
8882 f[2]=j+1
8883 local c,b,a=byte(f[1],i,j)
8884 if a>=0x80 then
8885 return 0x10000*a+0x100*b+c-0x1000000
8886 else
8887 return 0x10000*a+0x100*b+c
8888 end
8889end
8890function streams.readcardinal4(f)
8891 local i=f[2]
8892 local j=i+3
8893 f[2]=j+1
8894 local a,b,c,d=byte(f[1],i,j)
8895 return 0x1000000*a+0x10000*b+0x100*c+d
8896end
8897function streams.readcardinal4le(f)
8898 local i=f[2]
8899 local j=i+3
8900 f[2]=j+1
8901 local d,c,b,a=byte(f[1],i,j)
8902 return 0x1000000*a+0x10000*b+0x100*c+d
8903end
8904function streams.readinteger4(f)
8905 local i=f[2]
8906 local j=i+3
8907 f[2]=j+1
8908 local a,b,c,d=byte(f[1],i,j)
8909 if a>=0x80 then
8910 return 0x1000000*a+0x10000*b+0x100*c+d-0x100000000
8911 else
8912 return 0x1000000*a+0x10000*b+0x100*c+d
8913 end
8914end
8915function streams.readinteger4le(f)
8916 local i=f[2]
8917 local j=i+3
8918 f[2]=j+1
8919 local d,c,b,a=byte(f[1],i,j)
8920 if a>=0x80 then
8921 return 0x1000000*a+0x10000*b+0x100*c+d-0x100000000
8922 else
8923 return 0x1000000*a+0x10000*b+0x100*c+d
8924 end
8925end
8926function streams.readfixed2(f)
8927 local i=f[2]
8928 local j=i+1
8929 f[2]=j+1
8930 local n1,n2=byte(f[1],i,j)
8931 if n1>=0x80 then
8932 n1=n1-0x100
8933 end
8934 return n1+n2/0xFF
8935end
8936function streams.readfixed4(f)
8937 local i=f[2]
8938 local j=i+3
8939 f[2]=j+1
8940 local a,b,c,d=byte(f[1],i,j)
8941 local n1=0x100*a+b
8942 local n2=0x100*c+d
8943 if n1>=0x8000 then
8944 n1=n1-0x10000
8945 end
8946 return n1+n2/0xFFFF
8947end
8948if bit32 then
8949 local extract=bit32.extract
8950 local band=bit32.band
8951 function streams.read2dot14(f)
8952 local i=f[2]
8953 local j=i+1
8954 f[2]=j+1
8955 local a,b=byte(f[1],i,j)
8956 if a>=0x80 then
8957 local n=-(0x100*a+b)
8958 return-(extract(n,14,2)+(band(n,0x3FFF)/16384.0))
8959 else
8960 local n=0x100*a+b
8961 return (extract(n,14,2)+(band(n,0x3FFF)/16384.0))
8962 end
8963 end
8964end
8965function streams.skipshort(f,n)
8966 f[2]=f[2]+2*(n or 1)
8967end
8968function streams.skiplong(f,n)
8969 f[2]=f[2]+4*(n or 1)
8970end
8971if sio and sio.readcardinal2 then
8972 local readcardinal1=sio.readcardinal1
8973 local readcardinal2=sio.readcardinal2
8974 local readcardinal3=sio.readcardinal3
8975 local readcardinal4=sio.readcardinal4
8976 local readinteger1=sio.readinteger1
8977 local readinteger2=sio.readinteger2
8978 local readinteger3=sio.readinteger3
8979 local readinteger4=sio.readinteger4
8980 local readfixed2=sio.readfixed2
8981 local readfixed4=sio.readfixed4
8982 local read2dot14=sio.read2dot14
8983 local readbytes=sio.readbytes
8984 local readbytetable=sio.readbytetable
8985 function streams.readcardinal1(f)
8986 local i=f[2]
8987 f[2]=i+1
8988 return readcardinal1(f[1],i)
8989 end
8990 function streams.readcardinal2(f)
8991 local i=f[2]
8992 f[2]=i+2
8993 return readcardinal2(f[1],i)
8994 end
8995 function streams.readcardinal3(f)
8996 local i=f[2]
8997 f[2]=i+3
8998 return readcardinal3(f[1],i)
8999 end
9000 function streams.readcardinal4(f)
9001 local i=f[2]
9002 f[2]=i+4
9003 return readcardinal4(f[1],i)
9004 end
9005 function streams.readinteger1(f)
9006 local i=f[2]
9007 f[2]=i+1
9008 return readinteger1(f[1],i)
9009 end
9010 function streams.readinteger2(f)
9011 local i=f[2]
9012 f[2]=i+2
9013 return readinteger2(f[1],i)
9014 end
9015 function streams.readinteger3(f)
9016 local i=f[2]
9017 f[2]=i+3
9018 return readinteger3(f[1],i)
9019 end
9020 function streams.readinteger4(f)
9021 local i=f[2]
9022 f[2]=i+4
9023 return readinteger4(f[1],i)
9024 end
9025 function streams.readfixed2(f)
9026 local i=f[2]
9027 f[2]=i+2
9028 return readfixed2(f[1],i)
9029 end
9030 function streams.readfixed4(f)
9031 local i=f[2]
9032 f[2]=i+4
9033 return readfixed4(f[1],i)
9034 end
9035 function streams.read2dot14(f)
9036 local i=f[2]
9037 f[2]=i+2
9038 return read2dot14(f[1],i)
9039 end
9040 function streams.readbytes(f,n)
9041 local i=f[2]
9042 local s=f[3]
9043 local p=i+n
9044 if p>s then
9045 f[2]=s+1
9046 else
9047 f[2]=p
9048 end
9049 return readbytes(f[1],i,n)
9050 end
9051 function streams.readbytetable(f,n)
9052 local i=f[2]
9053 local s=f[3]
9054 local p=i+n
9055 if p>s then
9056 f[2]=s+1
9057 else
9058 f[2]=p
9059 end
9060 return readbytetable(f[1],i,n)
9061 end
9062 streams.readbyte=streams.readcardinal1
9063 streams.readsignedbyte=streams.readinteger1
9064 streams.readcardinal=streams.readcardinal1
9065 streams.readinteger=streams.readinteger1
9066end
9067if sio and sio.readcardinaltable then
9068 local readcardinaltable=sio.readcardinaltable
9069 local readintegertable=sio.readintegertable
9070 function utilities.streams.readcardinaltable(f,n,b)
9071 local i=f[2]
9072 local s=f[3]
9073 local p=i+n*b
9074 if p>s then
9075 f[2]=s+1
9076 else
9077 f[2]=p
9078 end
9079 return readcardinaltable(f[1],i,n,b)
9080 end
9081 function utilities.streams.readintegertable(f,n,b)
9082 local i=f[2]
9083 local s=f[3]
9084 local p=i+n*b
9085 if p>s then
9086 f[2]=s+1
9087 else
9088 f[2]=p
9089 end
9090 return readintegertable(f[1],i,n,b)
9091 end
9092else
9093 local readcardinal1=streams.readcardinal1
9094 local readcardinal2=streams.readcardinal2
9095 local readcardinal3=streams.readcardinal3
9096 local readcardinal4=streams.readcardinal4
9097 function streams.readcardinaltable(f,n,b)
9098 local i=f[2]
9099 local s=f[3]
9100 local p=i+n*b
9101 if p>s then
9102 f[2]=s+1
9103 else
9104 f[2]=p
9105 end
9106 local t={}
9107 if b==1 then for i=1,n do t[i]=readcardinal1(f[1],i) end
9108 elseif b==2 then for i=1,n do t[i]=readcardinal2(f[1],i) end
9109 elseif b==3 then for i=1,n do t[i]=readcardinal3(f[1],i) end
9110 elseif b==4 then for i=1,n do t[i]=readcardinal4(f[1],i) end end
9111 return t
9112 end
9113 local readinteger1=streams.readinteger1
9114 local readinteger2=streams.readinteger2
9115 local readinteger3=streams.readinteger3
9116 local readinteger4=streams.readinteger4
9117 function streams.readintegertable(f,n,b)
9118 local i=f[2]
9119 local s=f[3]
9120 local p=i+n*b
9121 if p>s then
9122 f[2]=s+1
9123 else
9124 f[2]=p
9125 end
9126 local t={}
9127 if b==1 then for i=1,n do t[i]=readinteger1(f[1],i) end
9128 elseif b==2 then for i=1,n do t[i]=readinteger2(f[1],i) end
9129 elseif b==3 then for i=1,n do t[i]=readinteger3(f[1],i) end
9130 elseif b==4 then for i=1,n do t[i]=readinteger4(f[1],i) end end
9131 return t
9132 end
9133end
9134do
9135 local files=utilities.files
9136 if files then
9137 local openfile=files.open
9138 local openstream=streams.open
9139 local openstring=streams.openstring
9140 local setmetatable=setmetatable
9141 function io.newreader(str,method)
9142 local f,m
9143 if method=="string" then
9144 f=openstring(str,true)
9145 m=streams
9146 elseif method=="stream" then
9147 f=openstream(str,true)
9148 m=streams
9149 else
9150 f=openfile(str,"rb")
9151 m=files
9152 end
9153 if f then
9154 local t={}
9155 setmetatable(t,{
9156 __index=function(t,k)
9157 local r=m[k]
9158 if k=="close" then
9159 if f then
9160 m.close(f)
9161 f=nil
9162 end
9163 return function() end
9164 elseif r then
9165 local v=function(_,a,b) return r(f,a,b) end
9166 t[k]=v
9167 return v
9168 else
9169 print("unknown key",k)
9170 end
9171 end
9172 } )
9173 return t
9174 end
9175 end
9176 end
9177end
9178if bit32 and not streams.tocardinal1 then
9179 local extract=bit32.extract
9180 local char=string.char
9181 streams.tocardinal1=char
9182 function streams.tocardinal2(n) return char(extract(n,8,8),extract(n,0,8)) end
9183 function streams.tocardinal3(n) return char(extract(n,16,8),extract(n,8,8),extract(n,0,8)) end
9184 function streams.tocardinal4(n) return char(extract(n,24,8),extract(n,16,8),extract(n,8,8),extract(n,0,8)) end
9185 streams.tocardinal1le=char
9186 function streams.tocardinal2le(n) return char(extract(n,0,8),extract(n,8,8)) end
9187 function streams.tocardinal3le(n) return char(extract(n,0,8),extract(n,8,8),extract(n,16,8)) end
9188 function streams.tocardinal4le(n) return char(extract(n,0,8),extract(n,8,8),extract(n,16,8),extract(n,24,8)) end
9189end
9190if not streams.readcstring then
9191 local readchar=streams.readchar
9192 local concat=table.concat
9193 function streams.readcstring(f)
9194 local t={}
9195 while true do
9196 local c=readchar(f)
9197 if c and c~="\0" then
9198 t[#t+1]=c
9199 else
9200 return concat(t)
9201 end
9202 end
9203 end
9204end
9205
9206
9207end
9208
9209do
9210
9211package.loaded["util-sto"] = package.loaded["util-sto"] or true
9212
9213
9214
9215if not modules then modules={} end modules ['util-sto']={
9216 version=1.001,
9217 comment="companion to luat-lib.mkiv",
9218 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
9219 copyright="PRAGMA ADE / ConTeXt Development Team",
9220 license="see context related readme files"
9221}
9222local setmetatable,getmetatable,rawset,type=setmetatable,getmetatable,rawset,type
9223utilities=utilities or {}
9224utilities.storage=utilities.storage or {}
9225local storage=utilities.storage
9226function storage.mark(t)
9227 if not t then
9228 print("\nfatal error: storage cannot be marked\n")
9229 os.exit()
9230 return
9231 end
9232 local m=getmetatable(t)
9233 if not m then
9234 m={}
9235 setmetatable(t,m)
9236 end
9237 m.__storage__=true
9238 return t
9239end
9240function storage.allocate(t)
9241 t=t or {}
9242 local m=getmetatable(t)
9243 if not m then
9244 m={}
9245 setmetatable(t,m)
9246 end
9247 m.__storage__=true
9248 return t
9249end
9250function storage.marked(t)
9251 local m=getmetatable(t)
9252 return m and m.__storage__
9253end
9254function storage.checked(t)
9255 if not t then
9256 report("\nfatal error: storage has not been allocated\n")
9257 os.exit()
9258 return
9259 end
9260 return t
9261end
9262function storage.setinitializer(data,initialize)
9263 local m=getmetatable(data) or {}
9264 m.__index=function(data,k)
9265 m.__index=nil
9266 initialize()
9267 return data[k]
9268 end
9269 setmetatable(data,m)
9270end
9271local keyisvalue={ __index=function(t,k)
9272 t[k]=k
9273 return k
9274end }
9275function storage.sparse(t)
9276 t=t or {}
9277 setmetatable(t,keyisvalue)
9278 return t
9279end
9280local function f_empty () return "" end
9281local function f_self (t,k) t[k]=k return k end
9282local function f_table (t,k) local v={} t[k]=v return v end
9283local function f_number(t,k) t[k]=0 return 0 end
9284local function f_ignore() end
9285local f_index={
9286 ["empty"]=f_empty,
9287 ["self"]=f_self,
9288 ["table"]=f_table,
9289 ["number"]=f_number,
9290}
9291function table.setmetatableindex(t,f)
9292 if type(t)~="table" then
9293 f,t=t,{}
9294 end
9295 local m=getmetatable(t)
9296 local i=f_index[f] or f
9297 if m then
9298 m.__index=i
9299 else
9300 setmetatable(t,{ __index=i })
9301 end
9302 return t
9303end
9304local f_index={
9305 ["ignore"]=f_ignore,
9306}
9307function table.setmetatablenewindex(t,f)
9308 if type(t)~="table" then
9309 f,t=t,{}
9310 end
9311 local m=getmetatable(t)
9312 local i=f_index[f] or f
9313 if m then
9314 m.__newindex=i
9315 else
9316 setmetatable(t,{ __newindex=i })
9317 end
9318 return t
9319end
9320function table.setmetatablecall(t,f)
9321 if type(t)~="table" then
9322 f,t=t,{}
9323 end
9324 local m=getmetatable(t)
9325 if m then
9326 m.__call=f
9327 else
9328 setmetatable(t,{ __call=f })
9329 end
9330 return t
9331end
9332function table.setmetatableindices(t,f,n,c)
9333 if type(t)~="table" then
9334 f,t=t,{}
9335 end
9336 local m=getmetatable(t)
9337 local i=f_index[f] or f
9338 if m then
9339 m.__index=i
9340 m.__newindex=n
9341 m.__call=c
9342 else
9343 setmetatable(t,{
9344 __index=i,
9345 __newindex=n,
9346 __call=c,
9347 })
9348 end
9349 return t
9350end
9351function table.setmetatablekey(t,key,value)
9352 local m=getmetatable(t)
9353 if not m then
9354 m={}
9355 setmetatable(t,m)
9356 end
9357 m[key]=value
9358 return t
9359end
9360function table.getmetatablekey(t,key,value)
9361 local m=getmetatable(t)
9362 return m and m[key]
9363end
9364function table.makeweak(t)
9365 if not t then
9366 t={}
9367 end
9368 local m=getmetatable(t)
9369 if m then
9370 m.__mode="v"
9371 else
9372 setmetatable(t,{ __mode="v" })
9373 end
9374 return t
9375end
9376
9377
9378end
9379
9380do
9381
9382package.loaded["util-prs"] = package.loaded["util-prs"] or true
9383
9384
9385
9386if not modules then modules={} end modules ['util-prs']={
9387 version=1.001,
9388 comment="companion to luat-lib.mkiv",
9389 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
9390 copyright="PRAGMA ADE / ConTeXt Development Team",
9391 license="see context related readme files"
9392}
9393local lpeg,table,string=lpeg,table,string
9394local P,R,V,S,C,Ct,Cs,Carg,Cc,Cg,Cf,Cp=lpeg.P,lpeg.R,lpeg.V,lpeg.S,lpeg.C,lpeg.Ct,lpeg.Cs,lpeg.Carg,lpeg.Cc,lpeg.Cg,lpeg.Cf,lpeg.Cp
9395local lpegmatch,lpegpatterns=lpeg.match,lpeg.patterns
9396local concat,gmatch,find=table.concat,string.gmatch,string.find
9397local tonumber,tostring,type,next,rawset=tonumber,tostring,type,next,rawset
9398local mod,div=math.mod,math.div
9399utilities=utilities or {}
9400local parsers=utilities.parsers or {}
9401utilities.parsers=parsers
9402local patterns=parsers.patterns or {}
9403parsers.patterns=patterns
9404local setmetatableindex=table.setmetatableindex
9405local sortedhash=table.sortedhash
9406local sortedkeys=table.sortedkeys
9407local tohash=table.tohash
9408local hashes={}
9409parsers.hashes=hashes
9410local digit=R("09")
9411local space=P(' ')
9412local equal=P("=")
9413local colon=P(":")
9414local comma=P(",")
9415local lbrace=P("{")
9416local rbrace=P("}")
9417local lparent=P("(")
9418local rparent=P(")")
9419local lbracket=P("[")
9420local rbracket=P("]")
9421local period=S(".")
9422local punctuation=S(".,:;")
9423local spacer=lpegpatterns.spacer
9424local whitespace=lpegpatterns.whitespace
9425local newline=lpegpatterns.newline
9426local anything=lpegpatterns.anything
9427local endofstring=lpegpatterns.endofstring
9428local nobrace=1-(lbrace+rbrace )
9429local noparent=1-(lparent+rparent)
9430local nobracket=1-(lbracket+rbracket)
9431local escape,left,right=P("\\"),P('{'),P('}')
9432lpegpatterns.balanced=P {
9433 ((escape*(left+right))+(1-(left+right))+V(2))^0,
9434 left*V(1)*right
9435}
9436local nestedbraces=P { lbrace*(nobrace+V(1))^0*rbrace }
9437local nestedparents=P { lparent*(noparent+V(1))^0*rparent }
9438local nestedbrackets=P { lbracket*(nobracket+V(1))^0*rbracket }
9439local spaces=space^0
9440local argument=Cs((lbrace/"")*((nobrace+nestedbraces)^0)*(rbrace/""))
9441local content=(1-endofstring)^0
9442lpegpatterns.nestedbraces=nestedbraces
9443lpegpatterns.nestedparents=nestedparents
9444lpegpatterns.nestedbrackets=nestedbrackets
9445lpegpatterns.nested=nestedbraces
9446lpegpatterns.argument=argument
9447lpegpatterns.content=content
9448local value=lbrace*C((nobrace+nestedbraces)^0)*rbrace+C((nestedbraces+(1-comma))^0)
9449local key=C((1-equal-comma)^1)
9450local pattern_a=(space+comma)^0*(key*equal*value+key*C(""))
9451local pattern_c=(space+comma)^0*(key*equal*value)
9452local pattern_d=(space+comma)^0*(key*(equal+colon)*value+key*C(""))
9453local key=C((1-space-equal-comma)^1)
9454local pattern_b=spaces*comma^0*spaces*(key*((spaces*equal*spaces*value)+C("")))
9455local hash={}
9456local function set(key,value)
9457 hash[key]=value
9458end
9459local pattern_a_s=(pattern_a/set)^1
9460local pattern_b_s=(pattern_b/set)^1
9461local pattern_c_s=(pattern_c/set)^1
9462local pattern_d_s=(pattern_d/set)^1
9463patterns.settings_to_hash_a=pattern_a_s
9464patterns.settings_to_hash_b=pattern_b_s
9465patterns.settings_to_hash_c=pattern_c_s
9466patterns.settings_to_hash_d=pattern_d_s
9467function parsers.make_settings_to_hash_pattern(set,how)
9468 if how=="strict" then
9469 return (pattern_c/set)^1
9470 elseif how=="tolerant" then
9471 return (pattern_b/set)^1
9472 else
9473 return (pattern_a/set)^1
9474 end
9475end
9476function parsers.settings_to_hash(str,existing)
9477 if not str or str=="" then
9478 return {}
9479 elseif type(str)=="table" then
9480 if existing then
9481 for k,v in next,str do
9482 existing[k]=v
9483 end
9484 return exiting
9485 else
9486 return str
9487 end
9488 else
9489 hash=existing or {}
9490 lpegmatch(pattern_a_s,str)
9491 return hash
9492 end
9493end
9494function parsers.settings_to_hash_colon_too(str)
9495 if not str or str=="" then
9496 return {}
9497 elseif type(str)=="table" then
9498 return str
9499 else
9500 hash={}
9501 lpegmatch(pattern_d_s,str)
9502 return hash
9503 end
9504end
9505function parsers.settings_to_hash_tolerant(str,existing)
9506 if not str or str=="" then
9507 return {}
9508 elseif type(str)=="table" then
9509 if existing then
9510 for k,v in next,str do
9511 existing[k]=v
9512 end
9513 return exiting
9514 else
9515 return str
9516 end
9517 else
9518 hash=existing or {}
9519 lpegmatch(pattern_b_s,str)
9520 return hash
9521 end
9522end
9523function parsers.settings_to_hash_strict(str,existing)
9524 if not str or str=="" then
9525 return nil
9526 elseif type(str)=="table" then
9527 if existing then
9528 for k,v in next,str do
9529 existing[k]=v
9530 end
9531 return exiting
9532 else
9533 return str
9534 end
9535 elseif str and str~="" then
9536 hash=existing or {}
9537 lpegmatch(pattern_c_s,str)
9538 return next(hash) and hash
9539 end
9540end
9541local separator=comma*space^0
9542local value=lbrace*C((nobrace+nestedbraces)^0)*rbrace+C((nestedbraces+(1-comma))^0)
9543local pattern=spaces*Ct(value*(separator*value)^0)
9544patterns.settings_to_array=pattern
9545function parsers.settings_to_array(str,strict)
9546 if not str or str=="" then
9547 return {}
9548 elseif type(str)=="table" then
9549 return str
9550 elseif strict then
9551 if find(str,"{",1,true) then
9552 return lpegmatch(pattern,str)
9553 else
9554 return { str }
9555 end
9556 elseif find(str,",",1,true) then
9557 return lpegmatch(pattern,str)
9558 else
9559 return { str }
9560 end
9561end
9562function parsers.settings_to_numbers(str)
9563 if not str or str=="" then
9564 return {}
9565 end
9566 if type(str)=="table" then
9567 elseif find(str,",",1,true) then
9568 str=lpegmatch(pattern,str)
9569 else
9570 return { tonumber(str) }
9571 end
9572 for i=1,#str do
9573 str[i]=tonumber(str[i])
9574 end
9575 return str
9576end
9577local value=lbrace*C((nobrace+nestedbraces)^0)*rbrace+C((nestedbraces+nestedbrackets+nestedparents+(1-comma))^0)
9578local pattern=spaces*Ct(value*(separator*value)^0)
9579function parsers.settings_to_array_obey_fences(str)
9580 return lpegmatch(pattern,str)
9581end
9582local cache_a={}
9583local cache_b={}
9584function parsers.groupedsplitat(symbol,withaction)
9585 if not symbol then
9586 symbol=","
9587 end
9588 local pattern=(withaction and cache_b or cache_a)[symbol]
9589 if not pattern then
9590 local symbols=S(symbol)
9591 local separator=space^0*symbols*space^0
9592 local value=lbrace*C((nobrace+nestedbraces)^0)
9593*(rbrace*(#symbols+P(-1)))
9594+C((nestedbraces+(1-(space^0*(symbols+P(-1)))))^0)
9595 if withaction then
9596 local withvalue=Carg(1)*value/function(f,s) return f(s) end
9597 pattern=spaces*withvalue*(separator*withvalue)^0
9598 cache_b[symbol]=pattern
9599 else
9600 pattern=spaces*Ct(value*(separator*value)^0)
9601 cache_a[symbol]=pattern
9602 end
9603 end
9604 return pattern
9605end
9606local pattern_a=parsers.groupedsplitat(",",false)
9607local pattern_b=parsers.groupedsplitat(",",true)
9608function parsers.stripped_settings_to_array(str)
9609 if not str or str=="" then
9610 return {}
9611 else
9612 return lpegmatch(pattern_a,str)
9613 end
9614end
9615function parsers.process_stripped_settings(str,action)
9616 if not str or str=="" then
9617 return {}
9618 else
9619 return lpegmatch(pattern_b,str,1,action)
9620 end
9621end
9622local function set(t,v)
9623 t[#t+1]=v
9624end
9625local value=P(Carg(1)*value)/set
9626local pattern=value*(separator*value)^0*Carg(1)
9627function parsers.add_settings_to_array(t,str)
9628 return lpegmatch(pattern,str,nil,t)
9629end
9630function parsers.hash_to_string(h,separator,yes,no,strict,omit)
9631 if h then
9632 local t={}
9633 local tn=0
9634 local s=sortedkeys(h)
9635 omit=omit and tohash(omit)
9636 for i=1,#s do
9637 local key=s[i]
9638 if not omit or not omit[key] then
9639 local value=h[key]
9640 if type(value)=="boolean" then
9641 if yes and no then
9642 if value then
9643 tn=tn+1
9644 t[tn]=key..'='..yes
9645 elseif not strict then
9646 tn=tn+1
9647 t[tn]=key..'='..no
9648 end
9649 elseif value or not strict then
9650 tn=tn+1
9651 t[tn]=key..'='..tostring(value)
9652 end
9653 else
9654 tn=tn+1
9655 t[tn]=key..'='..value
9656 end
9657 end
9658 end
9659 return concat(t,separator or ",")
9660 else
9661 return ""
9662 end
9663end
9664function parsers.array_to_string(a,separator)
9665 if a then
9666 return concat(a,separator or ",")
9667 else
9668 return ""
9669 end
9670end
9671local pattern=Cf(Ct("")*Cg(C((1-S(", "))^1)*S(", ")^0*Cc(true))^1,rawset)
9672function parsers.settings_to_set(str)
9673 return str and lpegmatch(pattern,str) or {}
9674end
9675hashes.settings_to_set=table.setmetatableindex(function(t,k)
9676 local v=k and lpegmatch(pattern,k) or {}
9677 t[k]=v
9678 return v
9679end)
9680function parsers.settings_to_set(str)
9681 return str and lpegmatch(pattern,str) or {}
9682end
9683local pattern=Ct((C((1-S(", "))^1)*S(", ")^0)^1)
9684hashes.settings_to_list=table.setmetatableindex(function(t,k)
9685 local v=k and lpegmatch(pattern,k) or {}
9686 t[k]=v
9687 return v
9688end)
9689getmetatable(hashes.settings_to_set ).__mode="kv"
9690getmetatable(hashes.settings_to_list).__mode="kv"
9691function parsers.simple_hash_to_string(h,separator)
9692 local t={}
9693 local tn=0
9694 for k,v in sortedhash(h) do
9695 if v then
9696 tn=tn+1
9697 t[tn]=k
9698 end
9699 end
9700 return concat(t,separator or ",")
9701end
9702local str=Cs(lpegpatterns.unquoted)+C((1-whitespace-equal)^1)
9703local setting=Cf(Carg(1)*(whitespace^0*Cg(str*whitespace^0*(equal*whitespace^0*str+Cc(""))))^1,rawset)
9704local splitter=setting^1
9705function parsers.options_to_hash(str,target)
9706 return str and lpegmatch(splitter,str,1,target or {}) or {}
9707end
9708local splitter=lpeg.tsplitat(" ")
9709function parsers.options_to_array(str)
9710 return str and lpegmatch(splitter,str) or {}
9711end
9712local value=P(lbrace*C((nobrace+nestedbraces)^0)*rbrace)+C(digit^1*lparent*(noparent+nestedparents)^1*rparent)+C((nestedbraces+(1-comma))^1)+Cc("")
9713local pattern_a=spaces*Ct(value*(separator*value)^0)
9714local function repeater(n,str)
9715 if not n then
9716 return str
9717 else
9718 local s=lpegmatch(pattern_a,str)
9719 if n==1 then
9720 return unpack(s)
9721 else
9722 local t={}
9723 local tn=0
9724 for i=1,n do
9725 for j=1,#s do
9726 tn=tn+1
9727 t[tn]=s[j]
9728 end
9729 end
9730 return unpack(t)
9731 end
9732 end
9733end
9734local value=P(lbrace*C((nobrace+nestedbraces)^0)*rbrace)+(C(digit^1)/tonumber*lparent*Cs((noparent+nestedparents)^1)*rparent)/repeater+C((nestedbraces+(1-comma))^1)+Cc("")
9735local pattern_b=spaces*Ct(value*(separator*value)^0)
9736function parsers.settings_to_array_with_repeat(str,expand)
9737 if expand then
9738 return lpegmatch(pattern_b,str) or {}
9739 else
9740 return lpegmatch(pattern_a,str) or {}
9741 end
9742end
9743local value=lbrace*C((nobrace+nestedbraces)^0)*rbrace
9744local pattern=Ct((space+value)^0)
9745function parsers.arguments_to_table(str)
9746 return lpegmatch(pattern,str)
9747end
9748function parsers.getparameters(self,class,parentclass,settings)
9749 local sc=self[class]
9750 if not sc then
9751 sc={}
9752 self[class]=sc
9753 if parentclass then
9754 local sp=self[parentclass]
9755 if not sp then
9756 sp={}
9757 self[parentclass]=sp
9758 end
9759 setmetatableindex(sc,sp)
9760 end
9761 end
9762 parsers.settings_to_hash(settings,sc)
9763end
9764function parsers.listitem(str)
9765 return gmatch(str,"[^, ]+")
9766end
9767local pattern=Cs { "start",
9768 start=V("one")+V("two")+V("three"),
9769 rest=(Cc(",")*V("thousand"))^0*(P(".")+endofstring)*anything^0,
9770 thousand=digit*digit*digit,
9771 one=digit*V("rest"),
9772 two=digit*digit*V("rest"),
9773 three=V("thousand")*V("rest"),
9774}
9775lpegpatterns.splitthousands=pattern
9776function parsers.splitthousands(str)
9777 return lpegmatch(pattern,str) or str
9778end
9779local optionalwhitespace=whitespace^0
9780lpegpatterns.words=Ct((Cs((1-punctuation-whitespace)^1)+anything)^1)
9781lpegpatterns.sentences=Ct((optionalwhitespace*Cs((1-period)^0*period))^1)
9782lpegpatterns.paragraphs=Ct((optionalwhitespace*Cs((whitespace^1*endofstring/""+1-(spacer^0*newline*newline))^1))^1)
9783local dquote=P('"')
9784local equal=P('=')
9785local escape=P('\\')
9786local separator=S(' ,')
9787local key=C((1-equal)^1)
9788local value=dquote*C((1-dquote-escape*dquote)^0)*dquote
9789local pattern=Cf(Ct("")*(Cg(key*equal*value)*separator^0)^1,rawset)^0*P(-1)
9790function parsers.keq_to_hash(str)
9791 if str and str~="" then
9792 return lpegmatch(pattern,str)
9793 else
9794 return {}
9795 end
9796end
9797local defaultspecification={ separator=",",quote='"' }
9798function parsers.csvsplitter(specification)
9799 specification=specification and setmetatableindex(specification,defaultspecification) or defaultspecification
9800 local separator=specification.separator
9801 local quotechar=specification.quote
9802 local numbers=specification.numbers
9803 local separator=S(separator~="" and separator or ",")
9804 local whatever=C((1-separator-newline)^0)
9805 if quotechar and quotechar~="" then
9806 local quotedata=nil
9807 for chr in gmatch(quotechar,".") do
9808 local quotechar=P(chr)
9809 local quoteitem=(1-quotechar)^0
9810 local quoteword=quotechar*(numbers and (quoteitem/tonumber) or C(quoteitem))*quotechar
9811 if quotedata then
9812 quotedata=quotedata+quoteword
9813 else
9814 quotedata=quoteword
9815 end
9816 end
9817 whatever=quotedata+whatever
9818 end
9819 local parser=Ct((Ct(whatever*(separator*whatever)^0)*S("\n\r")^1)^0 )
9820 return function(data)
9821 return lpegmatch(parser,data)
9822 end
9823end
9824function parsers.rfc4180splitter(specification)
9825 specification=specification and setmetatableindex(specification,defaultspecification) or defaultspecification
9826 local numbers=specification.numbers
9827 local separator=specification.separator
9828 local quotechar=P(specification.quote)
9829 local dquotechar=quotechar*quotechar
9830/specification.quote
9831 local separator=S(separator~="" and separator or ",")
9832 local whatever=(dquotechar+(1-quotechar))^0
9833 local escaped=quotechar*(numbers and (whatever/tonumber) or Cs(whatever))*quotechar
9834 local non_escaped=C((1-quotechar-newline-separator)^1)
9835 local field=escaped+non_escaped+Cc("")
9836 local record=Ct(field*(separator*field)^1)
9837 local headerline=record*Cp()
9838 local morerecords=(newline^(specification.strict and -1 or 1)*record)^0
9839 local headeryes=Ct(morerecords)
9840 local headernop=Ct(record*morerecords)
9841 return function(data,getheader)
9842 if getheader then
9843 local header,position=lpegmatch(headerline,data)
9844 local data=lpegmatch(headeryes,data,position)
9845 return data,header
9846 else
9847 return lpegmatch(headernop,data)
9848 end
9849 end
9850end
9851local function ranger(first,last,n,action)
9852 if not first then
9853 elseif last==true then
9854 for i=first,n or first do
9855 action(i)
9856 end
9857 elseif last then
9858 for i=first,last do
9859 action(i)
9860 end
9861 else
9862 action(first)
9863 end
9864end
9865local cardinal=(lpegpatterns.hexadecimal+lpegpatterns.cardinal)/tonumber
9866local spacers=lpegpatterns.spacer^0
9867local endofstring=lpegpatterns.endofstring
9868local stepper=spacers*(cardinal*(spacers*S(":-")*spacers*(cardinal+Cc(true) )+Cc(false) )*Carg(1)*Carg(2)/ranger*S(", ")^0 )^1
9869local stepper=spacers*(cardinal*(spacers*S(":-")*spacers*(cardinal+(P("*")+endofstring)*Cc(true) )+Cc(false) )*Carg(1)*Carg(2)/ranger*S(", ")^0 )^1*endofstring
9870function parsers.stepper(str,n,action)
9871 local ts=type(str)
9872 if type(n)=="function" then
9873 if ts=="number" then
9874 n(str)
9875 elseif ts=="table" then
9876 for i=1,#str do
9877 n(str[i])
9878 end
9879 else
9880 lpegmatch(stepper,str,1,false,n or print)
9881 end
9882 elseif ts=="string" then
9883 lpegmatch(stepper,str,1,n,action or print)
9884 end
9885end
9886local pattern_math=Cs((P("%")/"\\percent "+P("^")*Cc("{")*lpegpatterns.integer*Cc("}")+anything)^0)
9887local pattern_text=Cs((P("%")/"\\percent "+(P("^")/"\\high")*Cc("{")*lpegpatterns.integer*Cc("}")+anything)^0)
9888patterns.unittotex=pattern
9889function parsers.unittotex(str,textmode)
9890 return lpegmatch(textmode and pattern_text or pattern_math,str)
9891end
9892local pattern=Cs((P("^")/"<sup>"*lpegpatterns.integer*Cc("</sup>")+anything)^0)
9893function parsers.unittoxml(str)
9894 return lpegmatch(pattern,str)
9895end
9896local cache={}
9897local spaces=lpegpatterns.space^0
9898local dummy=function() end
9899setmetatableindex(cache,function(t,k)
9900 local separator=S(k)
9901 local value=(1-separator)^0
9902 local pattern=spaces*C(value)*separator^0*Cp()
9903 t[k]=pattern
9904 return pattern
9905end)
9906local commalistiterator=cache[","]
9907function parsers.iterator(str,separator)
9908 local n=#str
9909 if n==0 then
9910 return dummy
9911 else
9912 local pattern=separator and cache[separator] or commalistiterator
9913 local p=1
9914 return function()
9915 if p<=n then
9916 local s,e=lpegmatch(pattern,str,p)
9917 if e then
9918 p=e
9919 return s
9920 end
9921 end
9922 end
9923 end
9924end
9925local function initialize(t,name)
9926 local source=t[name]
9927 if source then
9928 local result={}
9929 for k,v in next,t[name] do
9930 result[k]=v
9931 end
9932 return result
9933 else
9934 return {}
9935 end
9936end
9937local function fetch(t,name)
9938 return t[name] or {}
9939end
9940local function process(result,more)
9941 for k,v in next,more do
9942 result[k]=v
9943 end
9944 return result
9945end
9946local name=C((1-S(", "))^1)
9947local parser=(Carg(1)*name/initialize)*(S(", ")^1*(Carg(1)*name/fetch))^0
9948local merge=Cf(parser,process)
9949function parsers.mergehashes(hash,list)
9950 return lpegmatch(merge,list,1,hash)
9951end
9952function parsers.runtime(time)
9953 if not time then
9954 time=os.runtime()
9955 end
9956 local days=div(time,24*60*60)
9957 time=mod(time,24*60*60)
9958 local hours=div(time,60*60)
9959 time=mod(time,60*60)
9960 local minutes=div(time,60)
9961 local seconds=mod(time,60)
9962 return days,hours,minutes,seconds
9963end
9964local spacing=whitespace^0
9965local apply=P("->")
9966local method=C((1-apply)^1)
9967local token=lbrace*C((1-rbrace)^1)*rbrace+C(anything^1)
9968local pattern=spacing*(method*spacing*apply+Carg(1))*spacing*token
9969function parsers.splitmethod(str,default)
9970 if str then
9971 return lpegmatch(pattern,str,1,default or false)
9972 else
9973 return default or false,""
9974 end
9975end
9976local p_year=lpegpatterns.digit^4/tonumber
9977local pattern=Cf(Ct("")*(
9978 (Cg(Cc("year")*p_year)*S("-/")*Cg(Cc("month")*cardinal)*S("-/")*Cg(Cc("day")*cardinal)
9979 )+(Cg(Cc("day")*cardinal)*S("-/")*Cg(Cc("month")*cardinal)*S("-/")*Cg(Cc("year")*p_year)
9980 )+(Cg(Cc("year")*p_year)*S("-/")*Cg(Cc("month")*cardinal)
9981 )+(Cg(Cc("month")*cardinal)*S("-/")*Cg(Cc("year")*p_year)
9982 )
9983 )*(
9984 P(" ")*Cg(Cc("hour")*cardinal)*P(":")*Cg(Cc("min")*cardinal)*(P(":")*Cg(Cc("sec")*cardinal))^-1+P(-1) )
9985,rawset)
9986lpegpatterns.splittime=pattern
9987function parsers.totime(str)
9988 return lpegmatch(pattern,str)
9989end
9990
9991
9992end
9993
9994do
9995
9996package.loaded["util-fmt"] = package.loaded["util-fmt"] or true
9997
9998
9999
10000if not modules then modules={} end modules ['util-fmt']={
10001 version=1.001,
10002 comment="companion to luat-lib.mkiv",
10003 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
10004 copyright="PRAGMA ADE / ConTeXt Development Team",
10005 license="see context related readme files"
10006}
10007utilities=utilities or {}
10008utilities.formatters=utilities.formatters or {}
10009local formatters=utilities.formatters
10010local concat,format=table.concat,string.format
10011local tostring,type,unpack=tostring,type,unpack
10012local strip=string.strip
10013local lpegmatch=lpeg.match
10014local stripper=lpeg.patterns.stripzeros
10015function formatters.stripzeros(str)
10016 return lpegmatch(stripper,str)
10017end
10018function formatters.formatcolumns(result,between,header)
10019 if result and #result>0 then
10020 local widths={}
10021 local numbers={}
10022 local templates={}
10023 local first=result[1]
10024 local n=#first
10025 between=between or " "
10026 for i=1,n do
10027 widths[i]=0
10028 end
10029 for i=1,#result do
10030 local r=result[i]
10031 for j=1,n do
10032 local rj=r[j]
10033 local tj=type(rj)
10034 if tj=="number" then
10035 numbers[j]=true
10036 rj=tostring(rj)
10037 elseif tj~="string" then
10038 rj=tostring(rj)
10039 r[j]=rj
10040 end
10041 local w=#rj
10042 if w>widths[j] then
10043 widths[j]=w
10044 end
10045 end
10046 end
10047 if header then
10048 for i=1,#header do
10049 local h=header[i]
10050 for j=1,n do
10051 local hj=tostring(h[j])
10052 h[j]=hj
10053 local w=#hj
10054 if w>widths[j] then
10055 widths[j]=w
10056 end
10057 end
10058 end
10059 end
10060 for i=1,n do
10061 local w=widths[i]
10062 if numbers[i] then
10063 if w>80 then
10064 templates[i]="%s"..between
10065 else
10066 templates[i]="% "..w.."i"..between
10067 end
10068 else
10069 if w>80 then
10070 templates[i]="%s"..between
10071 elseif w>0 then
10072 templates[i]="%-"..w.."s"..between
10073 else
10074 templates[i]="%s"
10075 end
10076 end
10077 end
10078 local template=strip(concat(templates))
10079 for i=1,#result do
10080 local str=format(template,unpack(result[i]))
10081 result[i]=strip(str)
10082 end
10083 if header then
10084 for i=1,n do
10085 local w=widths[i]
10086 if w>80 then
10087 templates[i]="%s"..between
10088 elseif w>0 then
10089 templates[i]="%-"..w.."s"..between
10090 else
10091 templates[i]="%s"
10092 end
10093 end
10094 local template=strip(concat(templates))
10095 for i=1,#header do
10096 local str=format(template,unpack(header[i]))
10097 header[i]=strip(str)
10098 end
10099 end
10100 end
10101 return result,header
10102end
10103
10104
10105end
10106
10107do
10108
10109package.loaded["util-soc-imp-reset"] = package.loaded["util-soc-imp-reset"] or true
10110
10111
10112
10113local loaded=package.loaded
10114loaded["socket"]=nil
10115loaded["copas"]=nil
10116loaded["ltn12"]=nil
10117loaded["mbox"]=nil
10118loaded["mime"]=nil
10119loaded["socket.url"]=nil
10120loaded["socket.headers"]=nil
10121loaded["socket.tp"]=nil
10122loaded["socket.http"]=nil
10123loaded["socket.ftp"]=nil
10124loaded["socket.smtp"]=nil
10125
10126
10127end
10128
10129do
10130
10131package.loaded["util-soc-imp-socket"] = package.loaded["util-soc-imp-socket"] or true
10132
10133
10134
10135
10136local type,tostring,setmetatable=type,tostring,setmetatable
10137local min=math.min
10138local format=string.format
10139local socket=socket or package.loaded.socket or require("socket.core")
10140local connect=socket.connect
10141local tcp4=socket.tcp4
10142local tcp6=socket.tcp6
10143local getaddrinfo=socket.dns.getaddrinfo
10144local defaulthost="0.0.0.0"
10145local function report(fmt,first,...)
10146 if logs then
10147 report=logs and logs.reporter("socket")
10148 report(fmt,first,...)
10149 elseif fmt then
10150 fmt="socket: "..fmt
10151 if first then
10152 print(format(fmt,first,...))
10153 else
10154 print(fmt)
10155 end
10156 end
10157end
10158socket.report=report
10159function socket.connect4(address,port,laddress,lport)
10160 return connect(address,port,laddress,lport,"inet")
10161end
10162function socket.connect6(address,port,laddress,lport)
10163 return connect(address,port,laddress,lport,"inet6")
10164end
10165function socket.bind(host,port,backlog)
10166 if host=="*" or host=="" then
10167 host=defaulthost
10168 end
10169 local addrinfo,err=getaddrinfo(host)
10170 if not addrinfo then
10171 return nil,err
10172 end
10173 for i=1,#addrinfo do
10174 local alt=addrinfo[i]
10175 local sock,err=(alt.family=="inet" and tcp4 or tcp6)()
10176 if not sock then
10177 return nil,err or "unknown error"
10178 end
10179 sock:setoption("reuseaddr",true)
10180 local res,err=sock:bind(alt.addr,port)
10181 if res then
10182 res,err=sock:listen(backlog)
10183 if res then
10184 return sock
10185 else
10186 sock:close()
10187 end
10188 else
10189 sock:close()
10190 end
10191 end
10192 return nil,"invalid address"
10193end
10194socket.try=socket.newtry()
10195function socket.choose(list)
10196 return function(name,opt1,opt2)
10197 if type(name)~="string" then
10198 name,opt1,opt2="default",name,opt1
10199 end
10200 local f=list[name or "nil"]
10201 if f then
10202 return f(opt1,opt2)
10203 else
10204 report("error: unknown key '%s'",tostring(name))
10205 end
10206 end
10207end
10208local sourcet={}
10209local sinkt={}
10210socket.sourcet=sourcet
10211socket.sinkt=sinkt
10212socket.BLOCKSIZE=2048
10213sinkt["close-when-done"]=function(sock)
10214 return setmetatable (
10215 {
10216 getfd=function() return sock:getfd() end,
10217 dirty=function() return sock:dirty() end,
10218 },
10219 {
10220 __call=function(self,chunk,err)
10221 if chunk then
10222 return sock:send(chunk)
10223 else
10224 sock:close()
10225 return 1
10226 end
10227 end
10228 }
10229 )
10230end
10231sinkt["keep-open"]=function(sock)
10232 return setmetatable (
10233 {
10234 getfd=function() return sock:getfd() end,
10235 dirty=function() return sock:dirty() end,
10236 },{
10237 __call=function(self,chunk,err)
10238 if chunk then
10239 return sock:send(chunk)
10240 else
10241 return 1
10242 end
10243 end
10244 }
10245 )
10246end
10247sinkt["default"]=sinkt["keep-open"]
10248socket.sink=socket.choose(sinkt)
10249sourcet["by-length"]=function(sock,length)
10250 local blocksize=socket.BLOCKSIZE
10251 return setmetatable (
10252 {
10253 getfd=function() return sock:getfd() end,
10254 dirty=function() return sock:dirty() end,
10255 },
10256 {
10257 __call=function()
10258 if length<=0 then
10259 return nil
10260 end
10261 local chunk,err=sock:receive(min(blocksize,length))
10262 if err then
10263 return nil,err
10264 end
10265 length=length-#chunk
10266 return chunk
10267 end
10268 }
10269 )
10270end
10271sourcet["until-closed"]=function(sock)
10272 local blocksize=socket.BLOCKSIZE
10273 local done=false
10274 return setmetatable (
10275 {
10276 getfd=function() return sock:getfd() end,
10277 dirty=function() return sock:dirty() end,
10278 },{
10279 __call=function()
10280 if done then
10281 return nil
10282 end
10283 local chunk,status,partial=sock:receive(blocksize)
10284 if not status then
10285 return chunk
10286 elseif status=="closed" then
10287 sock:close()
10288 done=true
10289 return partial
10290 else
10291 return nil,status
10292 end
10293 end
10294 }
10295 )
10296end
10297sourcet["default"]=sourcet["until-closed"]
10298socket.source=socket.choose(sourcet)
10299_G.socket=socket
10300package.loaded["socket"]=socket
10301
10302
10303end
10304
10305do
10306
10307package.loaded["util-soc-imp-copas"] = package.loaded["util-soc-imp-copas"] or true
10308
10309
10310
10311
10312local socket=socket or require("socket")
10313local ssl=ssl or nil
10314local WATCH_DOG_TIMEOUT=120
10315local UDP_DATAGRAM_MAX=8192
10316local type,next,pcall,getmetatable,tostring=type,next,pcall,getmetatable,tostring
10317local min,max,random=math.min,math.max,math.random
10318local find=string.find
10319local insert,remove=table.insert,table.remove
10320local gettime=socket.gettime
10321local selectsocket=socket.select
10322local createcoroutine=coroutine.create
10323local resumecoroutine=coroutine.resume
10324local yieldcoroutine=coroutine.yield
10325local runningcoroutine=coroutine.running
10326local function report(fmt,first,...)
10327 if logs then
10328 report=logs and logs.reporter("copas")
10329 report(fmt,first,...)
10330 elseif fmt then
10331 fmt="copas: "..fmt
10332 if first then
10333 print(format(fmt,first,...))
10334 else
10335 print(fmt)
10336 end
10337 end
10338end
10339local copas={
10340 _COPYRIGHT="Copyright (C) 2005-2016 Kepler Project",
10341 _DESCRIPTION="Coroutine Oriented Portable Asynchronous Services",
10342 _VERSION="Copas 2.0.1",
10343 autoclose=true,
10344 running=false,
10345 report=report,
10346 trace=false,
10347}
10348local function statushandler(status,...)
10349 if status then
10350 return...
10351 end
10352 local err=(...)
10353 if type(err)=="table" then
10354 err=err[1]
10355 end
10356 if copas.trace then
10357 report("error: %s",tostring(err))
10358 end
10359 return nil,err
10360end
10361function socket.protect(func)
10362 return function(...)
10363 return statushandler(pcall(func,...))
10364 end
10365end
10366function socket.newtry(finalizer)
10367 return function (...)
10368 local status=(...)
10369 if not status then
10370 local detail=select(2,...)
10371 pcall(finalizer,detail)
10372 if copas.trace then
10373 report("error: %s",tostring(detail))
10374 end
10375 return
10376 end
10377 return...
10378 end
10379end
10380local function newset()
10381 local reverse={}
10382 local set={}
10383 local queue={}
10384 setmetatable(set,{
10385 __index={
10386 insert=function(set,value)
10387 if not reverse[value] then
10388 local n=#set+1
10389 set[n]=value
10390 reverse[value]=n
10391 end
10392 end,
10393 remove=function(set,value)
10394 local index=reverse[value]
10395 if index then
10396 reverse[value]=nil
10397 local n=#set
10398 local top=set[n]
10399 set[n]=nil
10400 if top~=value then
10401 reverse[top]=index
10402 set[index]=top
10403 end
10404 end
10405 end,
10406 push=function (set,key,itm)
10407 local entry=queue[key]
10408 if entry==nil then
10409 queue[key]={ itm }
10410 else
10411 entry[#entry+1]=itm
10412 end
10413 end,
10414 pop=function (set,key)
10415 local top=queue[key]
10416 if top~=nil then
10417 local ret=remove(top,1)
10418 if top[1]==nil then
10419 queue[key]=nil
10420 end
10421 return ret
10422 end
10423 end
10424 }
10425 } )
10426 return set
10427end
10428local _sleeping={
10429 times={},
10430 cos={},
10431 lethargy={},
10432 insert=function()
10433 end,
10434 remove=function()
10435 end,
10436 push=function(self,sleeptime,co)
10437 if not co then
10438 return
10439 end
10440 if sleeptime<0 then
10441 self.lethargy[co]=true
10442 return
10443 else
10444 sleeptime=gettime()+sleeptime
10445 end
10446 local t=self.times
10447 local c=self.cos
10448 local i=1
10449 local n=#t
10450 while i<=n and t[i]<=sleeptime do
10451 i=i+1
10452 end
10453 insert(t,i,sleeptime)
10454 insert(c,i,co)
10455 end,
10456 getnext=
10457 function(self)
10458 local t=self.times
10459 local delay=t[1] and t[1]-gettime() or nil
10460 return delay and max(delay,0) or nil
10461 end,
10462 pop=
10463 function(self,time)
10464 local t=self.times
10465 local c=self.cos
10466 if #t==0 or time<t[1] then
10467 return
10468 end
10469 local co=c[1]
10470 remove(t,1)
10471 remove(c,1)
10472 return co
10473 end,
10474 wakeup=function(self,co)
10475 local let=self.lethargy
10476 if let[co] then
10477 self:push(0,co)
10478 let[co]=nil
10479 else
10480 local c=self.cos
10481 local t=self.times
10482 for i=1,#c do
10483 if c[i]==co then
10484 remove(c,i)
10485 remove(t,i)
10486 self:push(0,co)
10487 return
10488 end
10489 end
10490 end
10491 end
10492}
10493local _servers=newset()
10494local _reading=newset()
10495local _writing=newset()
10496local _reading_log={}
10497local _writing_log={}
10498local _is_timeout={
10499 timeout=true,
10500 wantread=true,
10501 wantwrite=true,
10502}
10503local function isTCP(socket)
10504 return not find(tostring(socket),"^udp")
10505end
10506local function copasreceive(client,pattern,part)
10507 if not pattern or pattern=="" then
10508 pattern="*l"
10509 end
10510 local current_log=_reading_log
10511 local s,err
10512 repeat
10513 s,err,part=client:receive(pattern,part)
10514 if s or (not _is_timeout[err]) then
10515 current_log[client]=nil
10516 return s,err,part
10517 end
10518 if err=="wantwrite" then
10519 current_log=_writing_log
10520 current_log[client]=gettime()
10521 yieldcoroutine(client,_writing)
10522 else
10523 current_log=_reading_log
10524 current_log[client]=gettime()
10525 yieldcoroutine(client,_reading)
10526 end
10527 until false
10528end
10529local function copasreceivefrom(client,size)
10530 local s,err,port
10531 if not size or size==0 then
10532 size=UDP_DATAGRAM_MAX
10533 end
10534 repeat
10535 s,err,port=client:receivefrom(size)
10536 if s or err~="timeout" then
10537 _reading_log[client]=nil
10538 return s,err,port
10539 end
10540 _reading_log[client]=gettime()
10541 yieldcoroutine(client,_reading)
10542 until false
10543end
10544local function copasreceivepartial(client,pattern,part)
10545 if not pattern or pattern=="" then
10546 pattern="*l"
10547 end
10548 local logger=_reading_log
10549 local queue=_reading
10550 local s,err
10551 repeat
10552 s,err,part=client:receive(pattern,part)
10553 if s or (type(pattern)=="number" and part~="" and part) or not _is_timeout[err] then
10554 logger[client]=nil
10555 return s,err,part
10556 end
10557 if err=="wantwrite" then
10558 logger=_writing_log
10559 queue=_writing
10560 else
10561 logger=_reading_log
10562 queue=_reading
10563 end
10564 logger[client]=gettime()
10565 yieldcoroutine(client,queue)
10566 until false
10567end
10568local function copassend(client,data,from,to)
10569 if not from then
10570 from=1
10571 end
10572 local lastIndex=from-1
10573 local logger=_writing_log
10574 local queue=_writing
10575 local s,err
10576 repeat
10577 s,err,lastIndex=client:send(data,lastIndex+1,to)
10578 if random(100)>90 then
10579 logger[client]=gettime()
10580 yieldcoroutine(client,queue)
10581 end
10582 if s or not _is_timeout[err] then
10583 logger[client]=nil
10584 return s,err,lastIndex
10585 end
10586 if err=="wantread" then
10587 logger=_reading_log
10588 queue=_reading
10589 else
10590 logger=_writing_log
10591 queue=_writing
10592 end
10593 logger[client]=gettime()
10594 yieldcoroutine(client,queue)
10595 until false
10596end
10597local function copassendto(client,data,ip,port)
10598 repeat
10599 local s,err=client:sendto(data,ip,port)
10600 if random(100)>90 then
10601 _writing_log[client]=gettime()
10602 yieldcoroutine(client,_writing)
10603 end
10604 if s or err~="timeout" then
10605 _writing_log[client]=nil
10606 return s,err
10607 end
10608 _writing_log[client]=gettime()
10609 yieldcoroutine(client,_writing)
10610 until false
10611end
10612local function copasconnect(skt,host,port)
10613 skt:settimeout(0)
10614 local ret,err,tried_more_than_once
10615 repeat
10616 ret,err=skt:connect (host,port)
10617 if ret or (err~="timeout" and err~="Operation already in progress") then
10618 if not ret and err=="already connected" and tried_more_than_once then
10619 ret=1
10620 err=nil
10621 end
10622 _writing_log[skt]=nil
10623 return ret,err
10624 end
10625 tried_more_than_once=tried_more_than_once or true
10626 _writing_log[skt]=gettime()
10627 yieldcoroutine(skt,_writing)
10628 until false
10629end
10630local function copasdohandshake(skt,sslt)
10631 if not ssl then
10632 ssl=require("ssl")
10633 end
10634 if not ssl then
10635 report("error: no ssl library")
10636 return
10637 end
10638 local nskt,err=ssl.wrap(skt,sslt)
10639 if not nskt then
10640 report("error: %s",tostring(err))
10641 return
10642 end
10643 nskt:settimeout(0)
10644 local queue
10645 repeat
10646 local success,err=nskt:dohandshake()
10647 if success then
10648 return nskt
10649 elseif err=="wantwrite" then
10650 queue=_writing
10651 elseif err=="wantread" then
10652 queue=_reading
10653 else
10654 report("error: %s",tostring(err))
10655 return
10656 end
10657 yieldcoroutine(nskt,queue)
10658 until false
10659end
10660local function copasflush(client)
10661end
10662copas.connect=copassconnect
10663copas.send=copassend
10664copas.sendto=copassendto
10665copas.receive=copasreceive
10666copas.receivefrom=copasreceivefrom
10667copas.copasreceivepartial=copasreceivepartial
10668copas.copasreceivePartial=copasreceivepartial
10669copas.dohandshake=copasdohandshake
10670copas.flush=copasflush
10671local function _skt_mt_tostring(self)
10672 return tostring(self.socket).." (copas wrapped)"
10673end
10674local _skt_mt_tcp_index={
10675 send=function(self,data,from,to)
10676 return copassend (self.socket,data,from,to)
10677 end,
10678 receive=function (self,pattern,prefix)
10679 if self.timeout==0 then
10680 return copasreceivePartial(self.socket,pattern,prefix)
10681 else
10682 return copasreceive(self.socket,pattern,prefix)
10683 end
10684 end,
10685 flush=function (self)
10686 return copasflush(self.socket)
10687 end,
10688 settimeout=function (self,time)
10689 self.timeout=time
10690 return true
10691 end,
10692 connect=function(self,...)
10693 local res,err=copasconnect(self.socket,...)
10694 if res and self.ssl_params then
10695 res,err=self:dohandshake()
10696 end
10697 return res,err
10698 end,
10699 close=function(self,...)
10700 return self.socket:close(...)
10701 end,
10702 bind=function(self,...)
10703 return self.socket:bind(...)
10704 end,
10705 getsockname=function(self,...)
10706 return self.socket:getsockname(...)
10707 end,
10708 getstats=function(self,...)
10709 return self.socket:getstats(...)
10710 end,
10711 setstats=function(self,...)
10712 return self.socket:setstats(...)
10713 end,
10714 listen=function(self,...)
10715 return self.socket:listen(...)
10716 end,
10717 accept=function(self,...)
10718 return self.socket:accept(...)
10719 end,
10720 setoption=function(self,...)
10721 return self.socket:setoption(...)
10722 end,
10723 getpeername=function(self,...)
10724 return self.socket:getpeername(...)
10725 end,
10726 shutdown=function(self,...)
10727 return self.socket:shutdown(...)
10728 end,
10729 dohandshake=function(self,sslt)
10730 self.ssl_params=sslt or self.ssl_params
10731 local nskt,err=copasdohandshake(self.socket,self.ssl_params)
10732 if not nskt then
10733 return nskt,err
10734 end
10735 self.socket=nskt
10736 return self
10737 end,
10738}
10739local _skt_mt_tcp={
10740 __tostring=_skt_mt_tostring,
10741 __index=_skt_mt_tcp_index,
10742}
10743local _skt_mt_udp_index={
10744 sendto=function (self,...)
10745 return copassendto(self.socket,...)
10746 end,
10747 receive=function (self,size)
10748 return copasreceive(self.socket,size or UDP_DATAGRAM_MAX)
10749 end,
10750 receivefrom=function (self,size)
10751 return copasreceivefrom(self.socket,size or UDP_DATAGRAM_MAX)
10752 end,
10753 setpeername=function(self,...)
10754 return self.socket:getpeername(...)
10755 end,
10756 setsockname=function(self,...)
10757 return self.socket:setsockname(...)
10758 end,
10759 close=function(self,...)
10760 return true
10761 end
10762}
10763local _skt_mt_udp={
10764 __tostring=_skt_mt_tostring,
10765 __index=_skt_mt_udp_index,
10766}
10767for k,v in next,_skt_mt_tcp_index do
10768 if not _skt_mt_udp_index[k] then
10769 _skt_mt_udp_index[k]=v
10770 end
10771end
10772local function wrap(skt,sslt)
10773 if getmetatable(skt)==_skt_mt_tcp or getmetatable(skt)==_skt_mt_udp then
10774 return skt
10775 end
10776 skt:settimeout(0)
10777 if isTCP(skt) then
10778 return setmetatable ({ socket=skt,ssl_params=sslt },_skt_mt_tcp)
10779 else
10780 return setmetatable ({ socket=skt },_skt_mt_udp)
10781 end
10782end
10783copas.wrap=wrap
10784function copas.handler(handler,sslparams)
10785 return function (skt,...)
10786 skt=wrap(skt)
10787 if sslparams then
10788 skt:dohandshake(sslparams)
10789 end
10790 return handler(skt,...)
10791 end
10792end
10793local _errhandlers={}
10794function copas.setErrorHandler(err)
10795 local co=runningcoroutine()
10796 if co then
10797 _errhandlers[co]=err
10798 end
10799end
10800local function _deferror (msg,co,skt)
10801 report("%s (%s) (%s)",msg,tostring(co),tostring(skt))
10802end
10803local function _doTick (co,skt,...)
10804 if not co then
10805 return
10806 end
10807 local ok,res,new_q=resumecoroutine(co,skt,...)
10808 if ok and res and new_q then
10809 new_q:insert(res)
10810 new_q:push(res,co)
10811 else
10812 if not ok then
10813 pcall(_errhandlers[co] or _deferror,res,co,skt)
10814 end
10815 if skt and copas.autoclose and isTCP(skt) then
10816 skt:close()
10817 end
10818 _errhandlers[co]=nil
10819 end
10820end
10821local function _accept(input,handler)
10822 local client=input:accept()
10823 if client then
10824 client:settimeout(0)
10825 local co=createcoroutine(handler)
10826 _doTick (co,client)
10827 end
10828 return client
10829end
10830local function _tickRead(skt)
10831 _doTick(_reading:pop(skt),skt)
10832end
10833local function _tickWrite(skt)
10834 _doTick(_writing:pop(skt),skt)
10835end
10836local function addTCPserver(server,handler,timeout)
10837 server:settimeout(timeout or 0)
10838 _servers[server]=handler
10839 _reading:insert(server)
10840end
10841local function addUDPserver(server,handler,timeout)
10842 server:settimeout(timeout or 0)
10843 local co=createcoroutine(handler)
10844 _reading:insert(server)
10845 _doTick(co,server)
10846end
10847function copas.addserver(server,handler,timeout)
10848 if isTCP(server) then
10849 addTCPserver(server,handler,timeout)
10850 else
10851 addUDPserver(server,handler,timeout)
10852 end
10853end
10854function copas.removeserver(server,keep_open)
10855 local s=server
10856 local mt=getmetatable(server)
10857 if mt==_skt_mt_tcp or mt==_skt_mt_udp then
10858 s=server.socket
10859 end
10860 _servers[s]=nil
10861 _reading:remove(s)
10862 if keep_open then
10863 return true
10864 end
10865 return server:close()
10866end
10867function copas.addthread(handler,...)
10868 local thread=createcoroutine(function(_,...) return handler(...) end)
10869 _doTick(thread,nil,...)
10870 return thread
10871end
10872local _tasks={}
10873local function addtaskRead(task)
10874 task.def_tick=_tickRead
10875 _tasks[task]=true
10876end
10877local function addtaskWrite(task)
10878 task.def_tick=_tickWrite
10879 _tasks[task]=true
10880end
10881local function tasks()
10882 return next,_tasks
10883end
10884local _readable_t={
10885 events=function(self)
10886 local i=0
10887 return function ()
10888 i=i+1
10889 return self._evs[i]
10890 end
10891 end,
10892 tick=function(self,input)
10893 local handler=_servers[input]
10894 if handler then
10895 input=_accept(input,handler)
10896 else
10897 _reading:remove(input)
10898 self.def_tick(input)
10899 end
10900 end
10901}
10902addtaskRead(_readable_t)
10903local _writable_t={
10904 events=function(self)
10905 local i=0
10906 return function()
10907 i=i+1
10908 return self._evs[i]
10909 end
10910 end,
10911 tick=function(self,output)
10912 _writing:remove(output)
10913 self.def_tick(output)
10914 end
10915}
10916addtaskWrite(_writable_t)
10917local _sleeping_t={
10918 tick=function(self,time,...)
10919 _doTick(_sleeping:pop(time),...)
10920 end
10921}
10922function copas.sleep(sleeptime)
10923 yieldcoroutine((sleeptime or 0),_sleeping)
10924end
10925function copas.wakeup(co)
10926 _sleeping:wakeup(co)
10927end
10928local last_cleansing=0
10929local function _select(timeout)
10930 local now=gettime()
10931 local r_evs,w_evs,err=selectsocket(_reading,_writing,timeout)
10932 _readable_t._evs=r_evs
10933 _writable_t._evs=w_evs
10934 if (last_cleansing-now)>WATCH_DOG_TIMEOUT then
10935 last_cleansing=now
10936 for skt,time in next,_reading_log do
10937 if not r_evs[skt] and (time-now)>WATCH_DOG_TIMEOUT then
10938 local n=#r_evs+1
10939 _reading_log[skt]=nil
10940 r_evs[n]=skt
10941 r_evs[skt]=n
10942 end
10943 end
10944 for skt,time in next,_writing_log do
10945 if not w_evs[skt] and (time-now)>WATCH_DOG_TIMEOUT then
10946 local n=#w_evs+1
10947 _writing_log[skt]=nil
10948 w_evs[n]=skt
10949 w_evs[skt]=n
10950 end
10951 end
10952 end
10953 if err=="timeout" and #r_evs+#w_evs>0 then
10954 return nil
10955 else
10956 return err
10957 end
10958end
10959local function copasfinished()
10960 return not (next(_reading) or next(_writing) or _sleeping:getnext())
10961end
10962local function copasstep(timeout)
10963 _sleeping_t:tick(gettime())
10964 local nextwait=_sleeping:getnext()
10965 if nextwait then
10966 timeout=timeout and min(nextwait,timeout) or nextwait
10967 elseif copasfinished() then
10968 return false
10969 end
10970 local err=_select(timeout)
10971 if err then
10972 if err=="timeout" then
10973 return false
10974 end
10975 return nil,err
10976 end
10977 for task in tasks() do
10978 for event in task:events() do
10979 task:tick(event)
10980 end
10981 end
10982 return true
10983end
10984copas.finished=copasfinished
10985copas.step=copasstep
10986function copas.loop(timeout)
10987 copas.running=true
10988 while not copasfinished() do
10989 copasstep(timeout)
10990 end
10991 copas.running=false
10992end
10993package.loaded["copas"]=copas
10994
10995
10996end
10997
10998do
10999
11000package.loaded["util-soc-imp-ltn12"] = package.loaded["util-soc-imp-ltn12"] or true
11001
11002
11003
11004
11005local select,unpack=select,unpack
11006local insert,remove=table.insert,table.remove
11007local sub=string.sub
11008local function report(fmt,first,...)
11009 if logs then
11010 report=logs and logs.reporter("ltn12")
11011 report(fmt,first,...)
11012 elseif fmt then
11013 fmt="ltn12: "..fmt
11014 if first then
11015 print(format(fmt,first,...))
11016 else
11017 print(fmt)
11018 end
11019 end
11020end
11021local filter={}
11022local source={}
11023local sink={}
11024local pump={}
11025local ltn12={
11026 _VERSION="LTN12 1.0.3",
11027 BLOCKSIZE=2048,
11028 filter=filter,
11029 source=source,
11030 sink=sink,
11031 pump=pump,
11032 report=report,
11033}
11034function filter.cycle(low,ctx,extra)
11035 if low then
11036 return function(chunk)
11037 return (low(ctx,chunk,extra))
11038 end
11039 end
11040end
11041function filter.chain(...)
11042 local arg={... }
11043 local n=select('#',...)
11044 local top=1
11045 local index=1
11046 local retry=""
11047 return function(chunk)
11048 retry=chunk and retry
11049 while true do
11050 local action=arg[index]
11051 if index==top then
11052 chunk=action(chunk)
11053 if chunk=="" or top==n then
11054 return chunk
11055 elseif chunk then
11056 index=index+1
11057 else
11058 top=top+1
11059 index=top
11060 end
11061 else
11062 chunk=action(chunk or "")
11063 if chunk=="" then
11064 index=index-1
11065 chunk=retry
11066 elseif chunk then
11067 if index==n then
11068 return chunk
11069 else
11070 index=index+1
11071 end
11072 else
11073 report("error: filter returned inappropriate 'nil'")
11074 return
11075 end
11076 end
11077 end
11078 end
11079end
11080local function empty()
11081 return nil
11082end
11083function source.empty()
11084 return empty
11085end
11086local function sourceerror(err)
11087 return function()
11088 return nil,err
11089 end
11090end
11091source.error=sourceerror
11092function source.file(handle,io_err)
11093 if handle then
11094 local blocksize=ltn12.BLOCKSIZE
11095 return function()
11096 local chunk=handle:read(blocksize)
11097 if not chunk then
11098 handle:close()
11099 end
11100 return chunk
11101 end
11102 else
11103 return sourceerror(io_err or "unable to open file")
11104 end
11105end
11106function source.simplify(src)
11107 return function()
11108 local chunk,err_or_new=src()
11109 if err_or_new then
11110 src=err_or_new
11111 end
11112 if chunk then
11113 return chunk
11114 else
11115 return nil,err_or_new
11116 end
11117 end
11118end
11119function source.string(s)
11120 if s then
11121 local blocksize=ltn12.BLOCKSIZE
11122 local i=1
11123 return function()
11124 local nexti=i+blocksize
11125 local chunk=sub(s,i,nexti-1)
11126 i=nexti
11127 if chunk~="" then
11128 return chunk
11129 else
11130 return nil
11131 end
11132 end
11133 else return source.empty() end
11134end
11135function source.rewind(src)
11136 local t={}
11137 return function(chunk)
11138 if chunk then
11139 insert(t,chunk)
11140 else
11141 chunk=remove(t)
11142 if chunk then
11143 return chunk
11144 else
11145 return src()
11146 end
11147 end
11148 end
11149end
11150function source.chain(src,f,...)
11151 if... then
11152 f=filter.chain(f,...)
11153 end
11154 local last_in=""
11155 local last_out=""
11156 local state="feeding"
11157 local err
11158 return function()
11159 if not last_out then
11160 report("error: source is empty")
11161 return
11162 end
11163 while true do
11164 if state=="feeding" then
11165 last_in,err=src()
11166 if err then
11167 return nil,err
11168 end
11169 last_out=f(last_in)
11170 if not last_out then
11171 if last_in then
11172 report("error: filter returned inappropriate 'nil'")
11173 end
11174 return nil
11175 elseif last_out~="" then
11176 state="eating"
11177 if last_in then
11178 last_in=""
11179 end
11180 return last_out
11181 end
11182 else
11183 last_out=f(last_in)
11184 if last_out=="" then
11185 if last_in=="" then
11186 state="feeding"
11187 else
11188 report("error: filter returned nothing")
11189 return
11190 end
11191 elseif not last_out then
11192 if last_in then
11193 report("filter returned inappropriate 'nil'")
11194 end
11195 return nil
11196 else
11197 return last_out
11198 end
11199 end
11200 end
11201 end
11202end
11203function source.cat(...)
11204 local arg={... }
11205 local src=remove(arg,1)
11206 return function()
11207 while src do
11208 local chunk,err=src()
11209 if chunk then
11210 return chunk
11211 end
11212 if err then
11213 return nil,err
11214 end
11215 src=remove(arg,1)
11216 end
11217 end
11218end
11219function sink.table(t)
11220 if not t then
11221 t={}
11222 end
11223 local f=function(chunk,err)
11224 if chunk then
11225 insert(t,chunk)
11226 end
11227 return 1
11228 end
11229 return f,t
11230end
11231function sink.simplify(snk)
11232 return function(chunk,err)
11233 local ret,err_or_new=snk(chunk,err)
11234 if not ret then
11235 return nil,err_or_new
11236 end
11237 if err_or_new then
11238 snk=err_or_new
11239 end
11240 return 1
11241 end
11242end
11243local function null()
11244 return 1
11245end
11246function sink.null()
11247 return null
11248end
11249local function sinkerror(err)
11250 return function()
11251 return nil,err
11252 end
11253end
11254sink.error=sinkerror
11255function sink.file(handle,io_err)
11256 if handle then
11257 return function(chunk,err)
11258 if not chunk then
11259 handle:close()
11260 return 1
11261 else
11262 return handle:write(chunk)
11263 end
11264 end
11265 else
11266 return sinkerror(io_err or "unable to open file")
11267 end
11268end
11269function sink.chain(f,snk,...)
11270 if... then
11271 local args={ f,snk,... }
11272 snk=remove(args,#args)
11273 f=filter.chain(unpack(args))
11274 end
11275 return function(chunk,err)
11276 if chunk~="" then
11277 local filtered=f(chunk)
11278 local done=chunk and ""
11279 while true do
11280 local ret,snkerr=snk(filtered,err)
11281 if not ret then
11282 return nil,snkerr
11283 end
11284 if filtered==done then
11285 return 1
11286 end
11287 filtered=f(done)
11288 end
11289 else
11290 return 1
11291 end
11292 end
11293end
11294function pump.step(src,snk)
11295 local chunk,src_err=src()
11296 local ret,snk_err=snk(chunk,src_err)
11297 if chunk and ret then
11298 return 1
11299 else
11300 return nil,src_err or snk_err
11301 end
11302end
11303function pump.all(src,snk,step)
11304 if not step then
11305 step=pump.step
11306 end
11307 while true do
11308 local ret,err=step(src,snk)
11309 if not ret then
11310 if err then
11311 return nil,err
11312 else
11313 return 1
11314 end
11315 end
11316 end
11317end
11318package.loaded["ltn12"]=ltn12
11319
11320
11321end
11322
11323do
11324
11325package.loaded["util-soc-imp-mime"] = package.loaded["util-soc-imp-mime"] or true
11326
11327
11328
11329
11330local type,tostring=type,tostring
11331local mime=mime or package.loaded.mime or require("mime.core")
11332local ltn12=ltn12 or package.loaded.ltn12 or require("ltn12")
11333local filtercycle=ltn12.filter.cycle
11334local function report(fmt,first,...)
11335 if logs then
11336 report=logs and logs.reporter("mime")
11337 report(fmt,first,...)
11338 elseif fmt then
11339 fmt="mime: "..fmt
11340 if first then
11341 print(format(fmt,first,...))
11342 else
11343 print(fmt)
11344 end
11345 end
11346end
11347mime.report=report
11348local encodet={}
11349local decodet={}
11350local wrapt={}
11351mime.encodet=encodet
11352mime.decodet=decodet
11353mime.wrapt=wrapt
11354local mime_b64=mime.b64
11355local mime_qp=mime.qp
11356local mime_unb64=mime.unb64
11357local mime_unqp=mime.unqp
11358local mime_wrp=mime.wrp
11359local mime_qpwrp=mime.qpwrp
11360local mime_eol=mime_eol
11361local mime_dot=mime_dot
11362encodet['base64']=function()
11363 return filtercycle(mime_b64,"")
11364end
11365encodet['quoted-printable']=function(mode)
11366 return filtercycle(mime_qp,"",mode=="binary" and "=0D=0A" or "\r\n")
11367end
11368decodet['base64']=function()
11369 return filtercycle(mime_unb64,"")
11370end
11371decodet['quoted-printable']=function()
11372 return filtercycle(mime_unqp,"")
11373end
11374local wraptext=function(length)
11375 if not length then
11376 length=76
11377 end
11378 return filtercycle(mime_wrp,length,length)
11379end
11380local wrapquoted=function()
11381 return filtercycle(mime_qpwrp,76,76)
11382end
11383wrapt['text']=wraptext
11384wrapt['base64']=wraptext
11385wrapt['default']=wraptext
11386wrapt['quoted-printable']=wrapquoted
11387function mime.normalize(marker)
11388 return filtercycle(mime_eol,0,marker)
11389end
11390function mime.stuff()
11391 return filtercycle(mime_dot,2)
11392end
11393local function choose(list)
11394 return function(name,opt1,opt2)
11395 if type(name)~="string" then
11396 name,opt1,opt2="default",name,opt1
11397 end
11398 local filter=list[name or "nil"]
11399 if filter then
11400 return filter(opt1,opt2)
11401 else
11402 report("error: unknown key '%s'",tostring(name))
11403 end
11404 end
11405end
11406mime.encode=choose(encodet)
11407mime.decode=choose(decodet)
11408mime.wrap=choose(wrapt)
11409package.loaded["mime"]=mime
11410
11411
11412end
11413
11414do
11415
11416package.loaded["util-soc-imp-url"] = package.loaded["util-soc-imp-url"] or true
11417
11418
11419
11420
11421local tonumber,tostring,type=tonumber,tostring,type
11422local gsub,sub,match,find,format,byte,char=string.gsub,string.sub,string.match,string.find,string.format,string.byte,string.char
11423local insert=table.insert
11424local socket=socket or require("socket")
11425local url={
11426 _VERSION="URL 1.0.3",
11427}
11428socket.url=url
11429function url.escape(s)
11430 return (gsub(s,"([^A-Za-z0-9_])",function(c)
11431 return format("%%%02x",byte(c))
11432 end))
11433end
11434local function make_set(t)
11435 local s={}
11436 for i=1,#t do
11437 s[t[i]]=true
11438 end
11439 return s
11440end
11441local segment_set=make_set {
11442 "-","_",".","!","~","*","'","(",
11443 ")",":","@","&","=","+","$",",",
11444}
11445local function protect_segment(s)
11446 return gsub(s,"([^A-Za-z0-9_])",function(c)
11447 if segment_set[c] then
11448 return c
11449 else
11450 return format("%%%02X",byte(c))
11451 end
11452 end)
11453end
11454function url.unescape(s)
11455 return (gsub(s,"%%(%x%x)",function(hex)
11456 return char(tonumber(hex,16))
11457 end))
11458end
11459local function absolute_path(base_path,relative_path)
11460 if find(relative_path,"^/") then
11461 return relative_path
11462 end
11463 local path=gsub(base_path,"[^/]*$","")
11464 path=path..relative_path
11465 path=gsub(path,"([^/]*%./)",function (s)
11466 if s~="./" then
11467 return s
11468 else
11469 return ""
11470 end
11471 end)
11472 path=gsub(path,"/%.$","/")
11473 local reduced
11474 while reduced~=path do
11475 reduced=path
11476 path=gsub(reduced,"([^/]*/%.%./)",function (s)
11477 if s~="../../" then
11478 return ""
11479 else
11480 return s
11481 end
11482 end)
11483 end
11484 path=gsub(reduced,"([^/]*/%.%.)$",function (s)
11485 if s~="../.." then
11486 return ""
11487 else
11488 return s
11489 end
11490 end)
11491 return path
11492end
11493function url.parse(url,default)
11494 local parsed={}
11495 for k,v in next,default or parsed do
11496 parsed[k]=v
11497 end
11498 if not url or url=="" then
11499 return nil,"invalid url"
11500 end
11501 url=gsub(url,"#(.*)$",function(f)
11502 parsed.fragment=f
11503 return ""
11504 end)
11505 url=gsub(url,"^([%w][%w%+%-%.]*)%:",function(s)
11506 parsed.scheme=s
11507 return ""
11508 end)
11509 url=gsub(url,"^//([^/]*)",function(n)
11510 parsed.authority=n
11511 return ""
11512 end)
11513 url=gsub(url,"%?(.*)",function(q)
11514 parsed.query=q
11515 return ""
11516 end)
11517 url=gsub(url,"%;(.*)",function(p)
11518 parsed.params=p
11519 return ""
11520 end)
11521 if url~="" then
11522 parsed.path=url
11523 end
11524 local authority=parsed.authority
11525 if not authority then
11526 return parsed
11527 end
11528 authority=gsub(authority,"^([^@]*)@",function(u)
11529 parsed.userinfo=u
11530 return ""
11531 end)
11532 authority=gsub(authority,":([^:%]]*)$",function(p)
11533 parsed.port=p
11534 return ""
11535 end)
11536 if authority~="" then
11537 parsed.host=match(authority,"^%[(.+)%]$") or authority
11538 end
11539 local userinfo=parsed.userinfo
11540 if not userinfo then
11541 return parsed
11542 end
11543 userinfo=gsub(userinfo,":([^:]*)$",function(p)
11544 parsed.password=p
11545 return ""
11546 end)
11547 parsed.user=userinfo
11548 return parsed
11549end
11550function url.build(parsed)
11551 local url=parsed.path or ""
11552 if parsed.params then
11553 url=url..";"..parsed.params
11554 end
11555 if parsed.query then
11556 url=url.."?"..parsed.query
11557 end
11558 local authority=parsed.authority
11559 if parsed.host then
11560 authority=parsed.host
11561 if find(authority,":") then
11562 authority="["..authority.."]"
11563 end
11564 if parsed.port then
11565 authority=authority..":"..tostring(parsed.port)
11566 end
11567 local userinfo=parsed.userinfo
11568 if parsed.user then
11569 userinfo=parsed.user
11570 if parsed.password then
11571 userinfo=userinfo..":"..parsed.password
11572 end
11573 end
11574 if userinfo then authority=userinfo.."@"..authority end
11575 end
11576 if authority then
11577 url="//"..authority..url
11578 end
11579 if parsed.scheme then
11580 url=parsed.scheme..":"..url
11581 end
11582 if parsed.fragment then
11583 url=url.."#"..parsed.fragment
11584 end
11585 return url
11586end
11587function url.absolute(base_url,relative_url)
11588 local base_parsed
11589 if type(base_url)=="table" then
11590 base_parsed=base_url
11591 base_url=url.build(base_parsed)
11592 else
11593 base_parsed=url.parse(base_url)
11594 end
11595 local relative_parsed=url.parse(relative_url)
11596 if not base_parsed then
11597 return relative_url
11598 elseif not relative_parsed then
11599 return base_url
11600 elseif relative_parsed.scheme then
11601 return relative_url
11602 else
11603 relative_parsed.scheme=base_parsed.scheme
11604 if not relative_parsed.authority then
11605 relative_parsed.authority=base_parsed.authority
11606 if not relative_parsed.path then
11607 relative_parsed.path=base_parsed.path
11608 if not relative_parsed.params then
11609 relative_parsed.params=base_parsed.params
11610 if not relative_parsed.query then
11611 relative_parsed.query=base_parsed.query
11612 end
11613 end
11614 else
11615 relative_parsed.path=absolute_path(base_parsed.path or "",relative_parsed.path)
11616 end
11617 end
11618 return url.build(relative_parsed)
11619 end
11620end
11621function url.parse_path(path)
11622 local parsed={}
11623 path=path or ""
11624 gsub(path,"([^/]+)",function (s)
11625 insert(parsed,s)
11626 end)
11627 for i=1,#parsed do
11628 parsed[i]=url.unescape(parsed[i])
11629 end
11630 if sub(path,1,1)=="/" then
11631 parsed.is_absolute=1
11632 end
11633 if sub(path,-1,-1)=="/" then
11634 parsed.is_directory=1
11635 end
11636 return parsed
11637end
11638function url.build_path(parsed,unsafe)
11639 local path=""
11640 local n=#parsed
11641 if unsafe then
11642 for i=1,n-1 do
11643 path=path..parsed[i].."/"
11644 end
11645 if n>0 then
11646 path=path..parsed[n]
11647 if parsed.is_directory then
11648 path=path.."/"
11649 end
11650 end
11651 else
11652 for i=1,n-1 do
11653 path=path..protect_segment(parsed[i]).."/"
11654 end
11655 if n>0 then
11656 path=path..protect_segment(parsed[n])
11657 if parsed.is_directory then
11658 path=path.."/"
11659 end
11660 end
11661 end
11662 if parsed.is_absolute then
11663 path="/"..path
11664 end
11665 return path
11666end
11667package.loaded["socket.url"]=url
11668
11669
11670end
11671
11672do
11673
11674package.loaded["util-soc-imp-headers"] = package.loaded["util-soc-imp-headers"] or true
11675
11676
11677
11678
11679local next=next
11680local lower=string.lower
11681local concat=table.concat
11682local socket=socket or require("socket")
11683local headers={}
11684socket.headers=headers
11685local canonic={
11686 ["accept"]="Accept",
11687 ["accept-charset"]="Accept-Charset",
11688 ["accept-encoding"]="Accept-Encoding",
11689 ["accept-language"]="Accept-Language",
11690 ["accept-ranges"]="Accept-Ranges",
11691 ["action"]="Action",
11692 ["alternate-recipient"]="Alternate-Recipient",
11693 ["age"]="Age",
11694 ["allow"]="Allow",
11695 ["arrival-date"]="Arrival-Date",
11696 ["authorization"]="Authorization",
11697 ["bcc"]="Bcc",
11698 ["cache-control"]="Cache-Control",
11699 ["cc"]="Cc",
11700 ["comments"]="Comments",
11701 ["connection"]="Connection",
11702 ["content-description"]="Content-Description",
11703 ["content-disposition"]="Content-Disposition",
11704 ["content-encoding"]="Content-Encoding",
11705 ["content-id"]="Content-ID",
11706 ["content-language"]="Content-Language",
11707 ["content-length"]="Content-Length",
11708 ["content-location"]="Content-Location",
11709 ["content-md5"]="Content-MD5",
11710 ["content-range"]="Content-Range",
11711 ["content-transfer-encoding"]="Content-Transfer-Encoding",
11712 ["content-type"]="Content-Type",
11713 ["cookie"]="Cookie",
11714 ["date"]="Date",
11715 ["diagnostic-code"]="Diagnostic-Code",
11716 ["dsn-gateway"]="DSN-Gateway",
11717 ["etag"]="ETag",
11718 ["expect"]="Expect",
11719 ["expires"]="Expires",
11720 ["final-log-id"]="Final-Log-ID",
11721 ["final-recipient"]="Final-Recipient",
11722 ["from"]="From",
11723 ["host"]="Host",
11724 ["if-match"]="If-Match",
11725 ["if-modified-since"]="If-Modified-Since",
11726 ["if-none-match"]="If-None-Match",
11727 ["if-range"]="If-Range",
11728 ["if-unmodified-since"]="If-Unmodified-Since",
11729 ["in-reply-to"]="In-Reply-To",
11730 ["keywords"]="Keywords",
11731 ["last-attempt-date"]="Last-Attempt-Date",
11732 ["last-modified"]="Last-Modified",
11733 ["location"]="Location",
11734 ["max-forwards"]="Max-Forwards",
11735 ["message-id"]="Message-ID",
11736 ["mime-version"]="MIME-Version",
11737 ["original-envelope-id"]="Original-Envelope-ID",
11738 ["original-recipient"]="Original-Recipient",
11739 ["pragma"]="Pragma",
11740 ["proxy-authenticate"]="Proxy-Authenticate",
11741 ["proxy-authorization"]="Proxy-Authorization",
11742 ["range"]="Range",
11743 ["received"]="Received",
11744 ["received-from-mta"]="Received-From-MTA",
11745 ["references"]="References",
11746 ["referer"]="Referer",
11747 ["remote-mta"]="Remote-MTA",
11748 ["reply-to"]="Reply-To",
11749 ["reporting-mta"]="Reporting-MTA",
11750 ["resent-bcc"]="Resent-Bcc",
11751 ["resent-cc"]="Resent-Cc",
11752 ["resent-date"]="Resent-Date",
11753 ["resent-from"]="Resent-From",
11754 ["resent-message-id"]="Resent-Message-ID",
11755 ["resent-reply-to"]="Resent-Reply-To",
11756 ["resent-sender"]="Resent-Sender",
11757 ["resent-to"]="Resent-To",
11758 ["retry-after"]="Retry-After",
11759 ["return-path"]="Return-Path",
11760 ["sender"]="Sender",
11761 ["server"]="Server",
11762 ["smtp-remote-recipient"]="SMTP-Remote-Recipient",
11763 ["status"]="Status",
11764 ["subject"]="Subject",
11765 ["te"]="TE",
11766 ["to"]="To",
11767 ["trailer"]="Trailer",
11768 ["transfer-encoding"]="Transfer-Encoding",
11769 ["upgrade"]="Upgrade",
11770 ["user-agent"]="User-Agent",
11771 ["vary"]="Vary",
11772 ["via"]="Via",
11773 ["warning"]="Warning",
11774 ["will-retry-until"]="Will-Retry-Until",
11775 ["www-authenticate"]="WWW-Authenticate",
11776 ["x-mailer"]="X-Mailer",
11777}
11778headers.canonic=setmetatable(canonic,{
11779 __index=function(t,k)
11780 socket.report("invalid header: %s",k)
11781 t[k]=k
11782 return k
11783 end
11784})
11785function headers.normalize(headers)
11786 if not headers then
11787 return {}
11788 end
11789 local normalized={}
11790 for k,v in next,headers do
11791 normalized[#normalized+1]=canonic[k]..": "..v
11792 end
11793 normalized[#normalized+1]=""
11794 normalized[#normalized+1]=""
11795 return concat(normalized,"\r\n")
11796end
11797function headers.lower(lowered,headers)
11798 if not lowered then
11799 return {}
11800 end
11801 if not headers then
11802 lowered,headers={},lowered
11803 end
11804 for k,v in next,headers do
11805 lowered[lower(k)]=v
11806 end
11807 return lowered
11808end
11809socket.headers=headers
11810package.loaded["socket.headers"]=headers
11811
11812
11813end
11814
11815do
11816
11817package.loaded["util-soc-imp-tp"] = package.loaded["util-soc-imp-tp"] or true
11818
11819
11820
11821
11822local setmetatable,next,type,tonumber=setmetatable,next,type,tonumber
11823local find,upper=string.find,string.upper
11824local socket=socket or require("socket")
11825local ltn12=ltn12 or require("ltn12")
11826local skipsocket=socket.skip
11827local sinksocket=socket.sink
11828local tcpsocket=socket.tcp
11829local ltn12pump=ltn12.pump
11830local pumpall=ltn12pump.all
11831local pumpstep=ltn12pump.step
11832local tp={
11833 TIMEOUT=60,
11834}
11835socket.tp=tp
11836local function get_reply(c)
11837 local line,err=c:receive()
11838 local reply=line
11839 if err then return
11840 nil,err
11841 end
11842 local code,sep=skipsocket(2,find(line,"^(%d%d%d)(.?)"))
11843 if not code then
11844 return nil,"invalid server reply"
11845 end
11846 if sep=="-" then
11847 local current
11848 repeat
11849 line,err=c:receive()
11850 if err then
11851 return nil,err
11852 end
11853 current,sep=skipsocket(2,find(line,"^(%d%d%d)(.?)"))
11854 reply=reply.."\n"..line
11855 until code==current and sep==" "
11856 end
11857 return code,reply
11858end
11859local methods={}
11860local mt={ __index=methods }
11861function methods.getpeername(self)
11862 return self.c:getpeername()
11863end
11864function methods.getsockname(self)
11865 return self.c:getpeername()
11866end
11867function methods.check(self,ok)
11868 local code,reply=get_reply(self.c)
11869 if not code then
11870 return nil,reply
11871 end
11872 local c=tonumber(code)
11873 local t=type(ok)
11874 if t=="function" then
11875 return ok(c,reply)
11876 elseif t=="table" then
11877 for i=1,#ok do
11878 if find(code,ok[i]) then
11879 return c,reply
11880 end
11881 end
11882 return nil,reply
11883 elseif find(code,ok) then
11884 return c,reply
11885 else
11886 return nil,reply
11887 end
11888end
11889function methods.command(self,cmd,arg)
11890 cmd=upper(cmd)
11891 if arg then
11892 cmd=cmd.." "..arg.."\r\n"
11893 else
11894 cmd=cmd.."\r\n"
11895 end
11896 return self.c:send(cmd)
11897end
11898function methods.sink(self,snk,pat)
11899 local chunk,err=self.c:receive(pat)
11900 return snk(chunk,err)
11901end
11902function methods.send(self,data)
11903 return self.c:send(data)
11904end
11905function methods.receive(self,pat)
11906 return self.c:receive(pat)
11907end
11908function methods.getfd(self)
11909 return self.c:getfd()
11910end
11911function methods.dirty(self)
11912 return self.c:dirty()
11913end
11914function methods.getcontrol(self)
11915 return self.c
11916end
11917function methods.source(self,source,step)
11918 local sink=sinksocket("keep-open",self.c)
11919 local ret,err=pumpall(source,sink,step or pumpstep)
11920 return ret,err
11921end
11922function methods.close(self)
11923 self.c:close()
11924 return 1
11925end
11926function tp.connect(host,port,timeout,create)
11927 local c,e=(create or tcpsocket)()
11928 if not c then
11929 return nil,e
11930 end
11931 c:settimeout(timeout or tp.TIMEOUT)
11932 local r,e=c:connect(host,port)
11933 if not r then
11934 c:close()
11935 return nil,e
11936 end
11937 return setmetatable({ c=c },mt)
11938end
11939package.loaded["socket.tp"]=tp
11940
11941
11942end
11943
11944do
11945
11946package.loaded["util-soc-imp-http"] = package.loaded["util-soc-imp-http"] or true
11947
11948
11949
11950
11951local tostring,tonumber,setmetatable,next,type=tostring,tonumber,setmetatable,next,type
11952local find,lower,format,gsub,match=string.find,string.lower,string.format,string.gsub,string.match
11953local concat=table.concat
11954local socket=socket or require("socket")
11955local url=socket.url or require("socket.url")
11956local ltn12=ltn12 or require("ltn12")
11957local mime=mime or require("mime")
11958local headers=socket.headers or require("socket.headers")
11959local normalizeheaders=headers.normalize
11960local parseurl=url.parse
11961local buildurl=url.build
11962local absoluteurl=url.absolute
11963local unescapeurl=url.unescape
11964local skipsocket=socket.skip
11965local sinksocket=socket.sink
11966local sourcesocket=socket.source
11967local trysocket=socket.try
11968local tcpsocket=socket.tcp
11969local newtrysocket=socket.newtry
11970local protectsocket=socket.protect
11971local emptysource=ltn12.source.empty
11972local stringsource=ltn12.source.string
11973local rewindsource=ltn12.source.rewind
11974local pumpstep=ltn12.pump.step
11975local pumpall=ltn12.pump.all
11976local sinknull=ltn12.sink.null
11977local sinktable=ltn12.sink.table
11978local lowerheaders=headers.lower
11979local mimeb64=mime.b64
11980local http={
11981 TIMEOUT=60,
11982 USERAGENT=socket._VERSION,
11983}
11984socket.http=http
11985local PORT=80
11986local SCHEMES={
11987 http=true,
11988}
11989local function receiveheaders(sock,headers)
11990 if not headers then
11991 headers={}
11992 end
11993 local line,err=sock:receive("*l")
11994 if err then
11995 return nil,err
11996 end
11997 while line~="" do
11998 local name,value=skipsocket(2,find(line,"^(.-):%s*(.*)"))
11999 if not (name and value) then
12000 return nil,"malformed reponse headers"
12001 end
12002 name=lower(name)
12003 line,err=sock:receive("*l")
12004 if err then
12005 return nil,err
12006 end
12007 while find(line,"^%s") do
12008 value=value..line
12009 line=sock:receive("*l")
12010 if err then
12011 return nil,err
12012 end
12013 end
12014 local found=headers[name]
12015 if found then
12016 value=found..", "..value
12017 end
12018 headers[name]=value
12019 end
12020 return headers
12021end
12022socket.sourcet["http-chunked"]=function(sock,headers)
12023 return setmetatable (
12024 {
12025 getfd=function() return sock:getfd() end,
12026 dirty=function() return sock:dirty() end,
12027 },{
12028 __call=function()
12029 local line,err=sock:receive("*l")
12030 if err then
12031 return nil,err
12032 end
12033 local size=tonumber(gsub(line,";.*",""),16)
12034 if not size then
12035 return nil,"invalid chunk size"
12036 end
12037 if size>0 then
12038 local chunk,err,part=sock:receive(size)
12039 if chunk then
12040 sock:receive("*a")
12041 end
12042 return chunk,err
12043 else
12044 headers,err=receiveheaders(sock,headers)
12045 if not headers then
12046 return nil,err
12047 end
12048 end
12049 end
12050 }
12051 )
12052end
12053socket.sinkt["http-chunked"]=function(sock)
12054 return setmetatable(
12055 {
12056 getfd=function() return sock:getfd() end,
12057 dirty=function() return sock:dirty() end,
12058 },
12059 {
12060 __call=function(self,chunk,err)
12061 if not chunk then
12062 chunk=""
12063 end
12064 return sock:send(format("%X\r\n%s\r\n",#chunk,chunk))
12065 end
12066 })
12067end
12068local methods={}
12069local mt={ __index=methods }
12070local function openhttp(host,port,create)
12071 local c=trysocket((create or tcpsocket)())
12072 local h=setmetatable({ c=c },mt)
12073 local try=newtrysocket(function() h:close() end)
12074 h.try=try
12075 try(c:settimeout(http.TIMEOUT))
12076 try(c:connect(host,port or PORT))
12077 return h
12078end
12079http.open=openhttp
12080function methods.sendrequestline(self,method,uri)
12081 local requestline=format("%s %s HTTP/1.1\r\n",method or "GET",uri)
12082 return self.try(self.c:send(requestline))
12083end
12084function methods.sendheaders(self,headers)
12085 self.try(self.c:send(normalizeheaders(headers)))
12086 return 1
12087end
12088function methods.sendbody(self,headers,source,step)
12089 if not source then
12090 source=emptysource()
12091 end
12092 if not step then
12093 step=pumpstep
12094 end
12095 local mode="http-chunked"
12096 if headers["content-length"] then
12097 mode="keep-open"
12098 end
12099 return self.try(pumpall(source,sinksocket(mode,self.c),step))
12100end
12101function methods.receivestatusline(self)
12102 local try=self.try
12103 local status=try(self.c:receive(5))
12104 if status~="HTTP/" then
12105 return nil,status
12106 end
12107 status=try(self.c:receive("*l",status))
12108 local code=skipsocket(2,find(status,"HTTP/%d*%.%d* (%d%d%d)"))
12109 return try(tonumber(code),status)
12110end
12111function methods.receiveheaders(self)
12112 return self.try(receiveheaders(self.c))
12113end
12114function methods.receivebody(self,headers,sink,step)
12115 if not sink then
12116 sink=sinknull()
12117 end
12118 if not step then
12119 step=pumpstep
12120 end
12121 local length=tonumber(headers["content-length"])
12122 local encoding=headers["transfer-encoding"]
12123 local mode="default"
12124 if encoding and encoding~="identity" then
12125 mode="http-chunked"
12126 elseif length then
12127 mode="by-length"
12128 end
12129 return self.try(pumpall(sourcesocket(mode,self.c,length),sink,step))
12130end
12131function methods.receive09body(self,status,sink,step)
12132 local source=rewindsource(sourcesocket("until-closed",self.c))
12133 source(status)
12134 return self.try(pumpall(source,sink,step))
12135end
12136function methods.close(self)
12137 return self.c:close()
12138end
12139local function adjusturi(request)
12140 if not request.proxy and not http.PROXY then
12141 request={
12142 path=trysocket(request.path,"invalid path 'nil'"),
12143 params=request.params,
12144 query=request.query,
12145 fragment=request.fragment,
12146 }
12147 end
12148 return buildurl(request)
12149end
12150local function adjustheaders(request)
12151 local headers={
12152 ["user-agent"]=http.USERAGENT,
12153 ["host"]=gsub(request.authority,"^.-@",""),
12154 ["connection"]="close, TE",
12155 ["te"]="trailers"
12156 }
12157 local username=request.user
12158 local password=request.password
12159 if username and password then
12160 headers["authorization"]="Basic "..(mimeb64(username..":"..unescapeurl(password)))
12161 end
12162 local proxy=request.proxy or http.PROXY
12163 if proxy then
12164 proxy=parseurl(proxy)
12165 local username=proxy.user
12166 local password=proxy.password
12167 if username and password then
12168 headers["proxy-authorization"]="Basic "..(mimeb64(username..":"..password))
12169 end
12170 end
12171 local requestheaders=request.headers
12172 if requestheaders then
12173 headers=lowerheaders(headers,requestheaders)
12174 end
12175 return headers
12176end
12177local default={
12178 host="",
12179 port=PORT,
12180 path="/",
12181 scheme="http"
12182}
12183local function adjustrequest(originalrequest)
12184 local url=originalrequest.url
12185 local request=url and parseurl(url,default) or {}
12186 for k,v in next,originalrequest do
12187 request[k]=v
12188 end
12189 local host=request.host
12190 local port=request.port
12191 local uri=request.uri
12192 if not host or host=="" then
12193 trysocket(nil,"invalid host '"..tostring(host).."'")
12194 end
12195 if port=="" then
12196 request.port=PORT
12197 end
12198 if not uri or uri=="" then
12199 request.uri=adjusturi(request)
12200 end
12201 request.headers=adjustheaders(request)
12202 local proxy=request.proxy or http.PROXY
12203 if proxy then
12204 proxy=parseurl(proxy)
12205 request.host=proxy.host
12206 request.port=proxy.port or 3128
12207 end
12208 return request
12209end
12210local maxredericts=4
12211local validredirects={ [301]=true,[302]=true,[303]=true,[307]=true }
12212local validmethods={ [false]=true,GET=true,HEAD=true }
12213local function shouldredirect(request,code,headers)
12214 local location=headers.location
12215 if not location then
12216 return false
12217 end
12218 location=gsub(location,"%s","")
12219 if location=="" then
12220 return false
12221 end
12222 local scheme=match(location,"^([%w][%w%+%-%.]*)%:")
12223 if scheme and not SCHEMES[scheme] then
12224 return false
12225 end
12226 local method=request.method
12227 local redirect=request.redirect
12228 local redirects=request.nredirects or 0
12229 return redirect and validredirects[code] and validmethods[method] and redirects<=maxredericts
12230end
12231local function shouldreceivebody(request,code)
12232 if request.method=="HEAD" then
12233 return nil
12234 end
12235 if code==204 or code==304 then
12236 return nil
12237 end
12238 if code>=100 and code<200 then
12239 return nil
12240 end
12241 return 1
12242end
12243local tredirect,trequest,srequest
12244tredirect=function(request,location)
12245 local result,code,headers,status=trequest {
12246 url=absoluteurl(request.url,location),
12247 source=request.source,
12248 sink=request.sink,
12249 headers=request.headers,
12250 proxy=request.proxy,
12251 nredirects=(request.nredirects or 0)+1,
12252 create=request.create,
12253 }
12254 if not headers then
12255 headers={}
12256 end
12257 if not headers.location then
12258 headers.location=location
12259 end
12260 return result,code,headers,status
12261end
12262trequest=function(originalrequest)
12263 local request=adjustrequest(originalrequest)
12264 local connection=openhttp(request.host,request.port,request.create)
12265 local headers=request.headers
12266 connection:sendrequestline(request.method,request.uri)
12267 connection:sendheaders(headers)
12268 if request.source then
12269 connection:sendbody(headers,request.source,request.step)
12270 end
12271 local code,status=connection:receivestatusline()
12272 if not code then
12273 connection:receive09body(status,request.sink,request.step)
12274 connection:close()
12275 return 1,200
12276 end
12277 while code==100 do
12278 headers=connection:receiveheaders()
12279 code,status=connection:receivestatusline()
12280 end
12281 headers=connection:receiveheaders()
12282 if shouldredirect(request,code,headers) and not request.source then
12283 connection:close()
12284 return tredirect(originalrequest,headers.location)
12285 end
12286 if shouldreceivebody(request,code) then
12287 connection:receivebody(headers,request.sink,request.step)
12288 end
12289 connection:close()
12290 return 1,code,headers,status
12291end
12292local function genericform(url,body)
12293 local buffer={}
12294 local request={
12295 url=url,
12296 sink=sinktable(buffer),
12297 target=buffer,
12298 }
12299 if body then
12300 request.source=stringsource(body)
12301 request.method="POST"
12302 request.headers={
12303 ["content-length"]=#body,
12304 ["content-type"]="application/x-www-form-urlencoded"
12305 }
12306 end
12307 return request
12308end
12309http.genericform=genericform
12310srequest=function(url,body)
12311 local request=genericform(url,body)
12312 local _,code,headers,status=trequest(request)
12313 return concat(request.target),code,headers,status
12314end
12315http.request=protectsocket(function(request,body)
12316 if type(request)=="string" then
12317 return srequest(request,body)
12318 else
12319 return trequest(request)
12320 end
12321end)
12322package.loaded["socket.http"]=http
12323
12324
12325end
12326
12327do
12328
12329package.loaded["util-soc-imp-ftp"] = package.loaded["util-soc-imp-ftp"] or true
12330
12331
12332
12333
12334local setmetatable,type,next=setmetatable,type,next
12335local find,format,gsub,match=string.find,string.format,string.gsub,string.match
12336local concat=table.concat
12337local mod=math.mod
12338local socket=socket or require("socket")
12339local url=socket.url or require("socket.url")
12340local tp=socket.tp or require("socket.tp")
12341local ltn12=ltn12 or require("ltn12")
12342local tcpsocket=socket.tcp
12343local trysocket=socket.try
12344local skipsocket=socket.skip
12345local sinksocket=socket.sink
12346local selectsocket=socket.select
12347local bindsocket=socket.bind
12348local newtrysocket=socket.newtry
12349local sourcesocket=socket.source
12350local protectsocket=socket.protect
12351local parseurl=url.parse
12352local unescapeurl=url.unescape
12353local pumpall=ltn12.pump.all
12354local pumpstep=ltn12.pump.step
12355local sourcestring=ltn12.source.string
12356local sinktable=ltn12.sink.table
12357local ftp={
12358 TIMEOUT=60,
12359 USER="ftp",
12360 PASSWORD="anonymous@anonymous.org",
12361}
12362socket.ftp=ftp
12363local PORT=21
12364local methods={}
12365local mt={ __index=methods }
12366function ftp.open(server,port,create)
12367 local tp=trysocket(tp.connect(server,port or PORT,ftp.TIMEOUT,create))
12368 local f=setmetatable({ tp=tp },metat)
12369 f.try=newtrysocket(function() f:close() end)
12370 return f
12371end
12372function methods.portconnect(self)
12373 local try=self.try
12374 local server=self.server
12375 try(server:settimeout(ftp.TIMEOUT))
12376 self.data=try(server:accept())
12377 try(self.data:settimeout(ftp.TIMEOUT))
12378end
12379function methods.pasvconnect(self)
12380 local try=self.try
12381 self.data=try(tcpsocket())
12382 self(self.data:settimeout(ftp.TIMEOUT))
12383 self(self.data:connect(self.pasvt.address,self.pasvt.port))
12384end
12385function methods.login(self,user,password)
12386 local try=self.try
12387 local tp=self.tp
12388 try(tp:command("user",user or ftp.USER))
12389 local code,reply=try(tp:check{"2..",331})
12390 if code==331 then
12391 try(tp:command("pass",password or ftp.PASSWORD))
12392 try(tp:check("2.."))
12393 end
12394 return 1
12395end
12396function methods.pasv(self)
12397 local try=self.try
12398 local tp=self.tp
12399 try(tp:command("pasv"))
12400 local code,reply=try(self.tp:check("2.."))
12401 local pattern="(%d+)%D(%d+)%D(%d+)%D(%d+)%D(%d+)%D(%d+)"
12402 local a,b,c,d,p1,p2=skipsocket(2,find(reply,pattern))
12403 try(a and b and c and d and p1 and p2,reply)
12404 local address=format("%d.%d.%d.%d",a,b,c,d)
12405 local port=p1*256+p2
12406 local server=self.server
12407 self.pasvt={
12408 address=address,
12409 port=port,
12410 }
12411 if server then
12412 server:close()
12413 self.server=nil
12414 end
12415 return address,port
12416end
12417function methods.epsv(self)
12418 local try=self.try
12419 local tp=self.tp
12420 try(tp:command("epsv"))
12421 local code,reply=try(tp:check("229"))
12422 local pattern="%((.)(.-)%1(.-)%1(.-)%1%)"
12423 local d,prt,address,port=match(reply,pattern)
12424 try(port,"invalid epsv response")
12425 local address=tp:getpeername()
12426 local server=self.server
12427 self.pasvt={
12428 address=address,
12429 port=port,
12430 }
12431 if self.server then
12432 server:close()
12433 self.server=nil
12434 end
12435 return address,port
12436end
12437function methods.port(self,address,port)
12438 local try=self.try
12439 local tp=self.tp
12440 self.pasvt=nil
12441 if not address then
12442 address,port=try(tp:getsockname())
12443 self.server=try(bindsocket(address,0))
12444 address,port=try(self.server:getsockname())
12445 try(self.server:settimeout(ftp.TIMEOUT))
12446 end
12447 local pl=mod(port,256)
12448 local ph=(port-pl)/256
12449 local arg=gsub(format("%s,%d,%d",address,ph,pl),"%.",",")
12450 try(tp:command("port",arg))
12451 try(tp:check("2.."))
12452 return 1
12453end
12454function methods.eprt(self,family,address,port)
12455 local try=self.try
12456 local tp=self.tp
12457 self.pasvt=nil
12458 if not address then
12459 address,port=try(tp:getsockname())
12460 self.server=try(bindsocket(address,0))
12461 address,port=try(self.server:getsockname())
12462 try(self.server:settimeout(ftp.TIMEOUT))
12463 end
12464 local arg=format("|%s|%s|%d|",family,address,port)
12465 try(tp:command("eprt",arg))
12466 try(tp:check("2.."))
12467 return 1
12468end
12469function methods.send(self,sendt)
12470 local try=self.try
12471 local tp=self.tp
12472 try(self.pasvt or self.server,"need port or pasv first")
12473 if self.pasvt then
12474 self:pasvconnect()
12475 end
12476 local argument=sendt.argument or unescapeurl(gsub(sendt.path or "","^[/\\]",""))
12477 if argument=="" then
12478 argument=nil
12479 end
12480 local command=sendt.command or "stor"
12481 try(tp:command(command,argument))
12482 local code,reply=try(tp:check{"2..","1.."})
12483 if not self.pasvt then
12484 self:portconnect()
12485 end
12486 local step=sendt.step or pumpstep
12487 local readt={ tp }
12488 local checkstep=function(src,snk)
12489 local readyt=selectsocket(readt,nil,0)
12490 if readyt[tp] then
12491 code=try(tp:check("2.."))
12492 end
12493 return step(src,snk)
12494 end
12495 local sink=sinksocket("close-when-done",self.data)
12496 try(pumpall(sendt.source,sink,checkstep))
12497 if find(code,"1..") then
12498 try(tp:check("2.."))
12499 end
12500 self.data:close()
12501 local sent=skipsocket(1,self.data:getstats())
12502 self.data=nil
12503 return sent
12504end
12505function methods.receive(self,recvt)
12506 local try=self.try
12507 local tp=self.tp
12508 try(self.pasvt or self.server,"need port or pasv first")
12509 if self.pasvt then self:pasvconnect() end
12510 local argument=recvt.argument or unescapeurl(gsub(recvt.path or "","^[/\\]",""))
12511 if argument=="" then
12512 argument=nil
12513 end
12514 local command=recvt.command or "retr"
12515 try(tp:command(command,argument))
12516 local code,reply=try(tp:check{"1..","2.."})
12517 if code>=200 and code<=299 then
12518 recvt.sink(reply)
12519 return 1
12520 end
12521 if not self.pasvt then
12522 self:portconnect()
12523 end
12524 local source=sourcesocket("until-closed",self.data)
12525 local step=recvt.step or pumpstep
12526 try(pumpall(source,recvt.sink,step))
12527 if find(code,"1..") then
12528 try(tp:check("2.."))
12529 end
12530 self.data:close()
12531 self.data=nil
12532 return 1
12533end
12534function methods.cwd(self,dir)
12535 local try=self.try
12536 local tp=self.tp
12537 try(tp:command("cwd",dir))
12538 try(tp:check(250))
12539 return 1
12540end
12541function methods.type(self,typ)
12542 local try=self.try
12543 local tp=self.tp
12544 try(tp:command("type",typ))
12545 try(tp:check(200))
12546 return 1
12547end
12548function methods.greet(self)
12549 local try=self.try
12550 local tp=self.tp
12551 local code=try(tp:check{"1..","2.."})
12552 if find(code,"1..") then
12553 try(tp:check("2.."))
12554 end
12555 return 1
12556end
12557function methods.quit(self)
12558 local try=self.try
12559 try(self.tp:command("quit"))
12560 try(self.tp:check("2.."))
12561 return 1
12562end
12563function methods.close(self)
12564 local data=self.data
12565 if data then
12566 data:close()
12567 end
12568 local server=self.server
12569 if server then
12570 server:close()
12571 end
12572 local tp=self.tp
12573 if tp then
12574 tp:close()
12575 end
12576end
12577local function override(t)
12578 if t.url then
12579 local u=parseurl(t.url)
12580 for k,v in next,t do
12581 u[k]=v
12582 end
12583 return u
12584 else
12585 return t
12586 end
12587end
12588local function tput(putt)
12589 putt=override(putt)
12590 local host=putt.host
12591 trysocket(host,"missing hostname")
12592 local f=ftp.open(host,putt.port,putt.create)
12593 f:greet()
12594 f:login(putt.user,putt.password)
12595 local typ=putt.type
12596 if typ then
12597 f:type(typ)
12598 end
12599 f:epsv()
12600 local sent=f:send(putt)
12601 f:quit()
12602 f:close()
12603 return sent
12604end
12605local default={
12606 path="/",
12607 scheme="ftp",
12608}
12609local function genericform(u)
12610 local t=trysocket(parseurl(u,default))
12611 trysocket(t.scheme=="ftp","wrong scheme '"..t.scheme.."'")
12612 trysocket(t.host,"missing hostname")
12613 local pat="^type=(.)$"
12614 if t.params then
12615 local typ=skipsocket(2,find(t.params,pat))
12616 t.type=typ
12617 trysocket(typ=="a" or typ=="i","invalid type '"..typ.."'")
12618 end
12619 return t
12620end
12621ftp.genericform=genericform
12622local function sput(u,body)
12623 local putt=genericform(u)
12624 putt.source=sourcestring(body)
12625 return tput(putt)
12626end
12627ftp.put=protectsocket(function(putt,body)
12628 if type(putt)=="string" then
12629 return sput(putt,body)
12630 else
12631 return tput(putt)
12632 end
12633end)
12634local function tget(gett)
12635 gett=override(gett)
12636 local host=gett.host
12637 trysocket(host,"missing hostname")
12638 local f=ftp.open(host,gett.port,gett.create)
12639 f:greet()
12640 f:login(gett.user,gett.password)
12641 if gett.type then
12642 f:type(gett.type)
12643 end
12644 f:epsv()
12645 f:receive(gett)
12646 f:quit()
12647 return f:close()
12648end
12649local function sget(u)
12650 local gett=genericform(u)
12651 local t={}
12652 gett.sink=sinktable(t)
12653 tget(gett)
12654 return concat(t)
12655end
12656ftp.command=protectsocket(function(cmdt)
12657 cmdt=override(cmdt)
12658 local command=cmdt.command
12659 local argument=cmdt.argument
12660 local check=cmdt.check
12661 local host=cmdt.host
12662 trysocket(host,"missing hostname")
12663 trysocket(command,"missing command")
12664 local f=ftp.open(host,cmdt.port,cmdt.create)
12665 local try=f.try
12666 local tp=f.tp
12667 f:greet()
12668 f:login(cmdt.user,cmdt.password)
12669 if type(command)=="table" then
12670 local argument=argument or {}
12671 for i=1,#command do
12672 local cmd=command[i]
12673 try(tp:command(cmd,argument[i]))
12674 if check and check[i] then
12675 try(tp:check(check[i]))
12676 end
12677 end
12678 else
12679 try(tp:command(command,argument))
12680 if check then
12681 try(tp:check(check))
12682 end
12683 end
12684 f:quit()
12685 return f:close()
12686end)
12687ftp.get=protectsocket(function(gett)
12688 if type(gett)=="string" then
12689 return sget(gett)
12690 else
12691 return tget(gett)
12692 end
12693end)
12694package.loaded["socket.ftp"]=ftp
12695
12696
12697end
12698
12699do
12700
12701package.loaded["util-soc-imp-smtp"] = package.loaded["util-soc-imp-smtp"] or true
12702
12703
12704
12705
12706local type,setmetatable,next=type,setmetatable,next
12707local find,lower,format=string.find,string.lower,string.format
12708local osdate,osgetenv=os.date,os.getenv
12709local random=math.random
12710local socket=socket or require("socket")
12711local headers=socket.headers or require("socket.headers")
12712local ltn12=ltn12 or require("ltn12")
12713local tp=socket.tp or require("socket.tp")
12714local mime=mime or require("mime")
12715local mimeb64=mime.b64
12716local mimestuff=mime.stuff
12717local skipsocket=socket.skip
12718local trysocket=socket.try
12719local newtrysocket=socket.newtry
12720local protectsocket=socket.protect
12721local normalizeheaders=headers.normalize
12722local lowerheaders=headers.lower
12723local createcoroutine=coroutine.create
12724local resumecoroutine=coroutine.resume
12725local yieldcoroutine=coroutine.resume
12726local smtp={
12727 TIMEOUT=60,
12728 SERVER="localhost",
12729 PORT=25,
12730 DOMAIN=osgetenv("SERVER_NAME") or "localhost",
12731 ZONE="-0000",
12732}
12733socket.smtp=smtp
12734local methods={}
12735local mt={ __index=methods }
12736function methods.greet(self,domain)
12737 local try=self.try
12738 local tp=self.tp
12739 try(tp:check("2.."))
12740 try(tp:command("EHLO",domain or _M.DOMAIN))
12741 return skipsocket(1,try(tp:check("2..")))
12742end
12743function methods.mail(self,from)
12744 local try=self.try
12745 local tp=self.tp
12746 try(tp:command("MAIL","FROM:"..from))
12747 return try(tp:check("2.."))
12748end
12749function methods.rcpt(self,to)
12750 local try=self.try
12751 local tp=self.tp
12752 try(tp:command("RCPT","TO:"..to))
12753 return try(tp:check("2.."))
12754end
12755function methods.data(self,src,step)
12756 local try=self.try
12757 local tp=self.tp
12758 try(tp:command("DATA"))
12759 try(tp:check("3.."))
12760 try(tp:source(src,step))
12761 try(tp:send("\r\n.\r\n"))
12762 return try(tp:check("2.."))
12763end
12764function methods.quit(self)
12765 local try=self.try
12766 local tp=self.tp
12767 try(tp:command("QUIT"))
12768 return try(tp:check("2.."))
12769end
12770function methods.close(self)
12771 return self.tp:close()
12772end
12773function methods.login(self,user,password)
12774 local try=self.try
12775 local tp=self.tp
12776 try(tp:command("AUTH","LOGIN"))
12777 try(tp:check("3.."))
12778 try(tp:send(mimeb64(user).."\r\n"))
12779 try(tp:check("3.."))
12780 try(tp:send(mimeb64(password).."\r\n"))
12781 return try(tp:check("2.."))
12782end
12783function methods.plain(self,user,password)
12784 local try=self.try
12785 local tp=self.tp
12786 local auth="PLAIN "..mimeb64("\0"..user.."\0"..password)
12787 try(tp:command("AUTH",auth))
12788 return try(tp:check("2.."))
12789end
12790function methods.auth(self,user,password,ext)
12791 if not user or not password then
12792 return 1
12793 end
12794 local try=self.try
12795 if find(ext,"AUTH[^\n]+LOGIN") then
12796 return self:login(user,password)
12797 elseif find(ext,"AUTH[^\n]+PLAIN") then
12798 return self:plain(user,password)
12799 else
12800 try(nil,"authentication not supported")
12801 end
12802end
12803function methods.send(self,mail)
12804 self:mail(mail.from)
12805 local receipt=mail.rcpt
12806 if type(receipt)=="table" then
12807 for i=1,#receipt do
12808 self:rcpt(receipt[i])
12809 end
12810 elseif receipt then
12811 self:rcpt(receipt)
12812 end
12813 self:data(ltn12.source.chain(mail.source,mimestuff()),mail.step)
12814end
12815local function opensmtp(self,server,port,create)
12816 if not server or server=="" then
12817 server=smtp.SERVER
12818 end
12819 if not port or port=="" then
12820 port=smtp.PORT
12821 end
12822 local s={
12823 tp=trysocket(tp.connect(server,port,smtp.TIMEOUT,create)),
12824 try=newtrysocket(function()
12825 s:close()
12826 end),
12827 }
12828 setmetatable(s,mt)
12829 return s
12830end
12831smtp.open=opensmtp
12832local nofboundaries=0
12833local function newboundary()
12834 nofboundaries=nofboundaries+1
12835 return format('%s%05d==%05u',osdate('%d%m%Y%H%M%S'),random(0,99999),nofboundaries)
12836end
12837local send_message
12838local function send_headers(headers)
12839 yieldcoroutine(normalizeheaders(headers))
12840end
12841local function send_multipart(message)
12842 local boundary=newboundary()
12843 local headers=lowerheaders(message.headers)
12844 local body=message.body
12845 local preamble=body.preamble
12846 local epilogue=body.epilogue
12847 local content=headers['content-type'] or 'multipart/mixed'
12848 headers['content-type']=content..'; boundary="'..boundary..'"'
12849 send_headers(headers)
12850 if preamble then
12851 yieldcoroutine(preamble)
12852 yieldcoroutine("\r\n")
12853 end
12854 for i=1,#body do
12855 yieldcoroutine("\r\n--"..boundary.."\r\n")
12856 send_message(body[i])
12857 end
12858 yieldcoroutine("\r\n--"..boundary.."--\r\n\r\n")
12859 if epilogue then
12860 yieldcoroutine(epilogue)
12861 yieldcoroutine("\r\n")
12862 end
12863end
12864local default_content_type='text/plain; charset="UTF-8"'
12865local function send_source(message)
12866 local headers=lowerheaders(message.headers)
12867 if not headers['content-type'] then
12868 headers['content-type']=default_content_type
12869 end
12870 send_headers(headers)
12871 local getchunk=message.body
12872 while true do
12873 local chunk,err=getchunk()
12874 if err then
12875 yieldcoroutine(nil,err)
12876 elseif chunk then
12877 yieldcoroutine(chunk)
12878 else
12879 break
12880 end
12881 end
12882end
12883local function send_string(message)
12884 local headers=lowerheaders(message.headers)
12885 if not headers['content-type'] then
12886 headers['content-type']=default_content_type
12887 end
12888 send_headers(headers)
12889 yieldcoroutine(message.body)
12890end
12891function send_message(message)
12892 local body=message.body
12893 if type(body)=="table" then
12894 send_multipart(message)
12895 elseif type(body)=="function" then
12896 send_source(message)
12897 else
12898 send_string(message)
12899 end
12900end
12901local function adjust_headers(message)
12902 local headers=lowerheaders(message.headers)
12903 if not headers["date"] then
12904 headers["date"]=osdate("!%a, %d %b %Y %H:%M:%S ")..(message.zone or smtp.ZONE)
12905 end
12906 if not headers["x-mailer"] then
12907 headers["x-mailer"]=socket._VERSION
12908 end
12909 headers["mime-version"]="1.0"
12910 return headers
12911end
12912function smtp.message(message)
12913 message.headers=adjust_headers(message)
12914 local action=createcoroutine(function()
12915 send_message(message)
12916 end)
12917 return function()
12918 local ret,a,b=resumecoroutine(action)
12919 if ret then
12920 return a,b
12921 else
12922 return nil,a
12923 end
12924 end
12925end
12926smtp.send=protectsocket(function(mail)
12927 local snd=opensmtp(smtp,mail.server,mail.port,mail.create)
12928 local ext=snd:greet(mail.domain)
12929 snd:auth(mail.user,mail.password,ext)
12930 snd:send(mail)
12931 snd:quit()
12932 return snd:close()
12933end)
12934package.loaded["socket.smtp"]=smtp
12935
12936
12937end
12938
12939do
12940
12941package.loaded["trac-set"] = package.loaded["trac-set"] or true
12942
12943
12944
12945if not modules then modules={} end modules ['trac-set']={
12946 version=1.001,
12947 comment="companion to luat-lib.mkiv",
12948 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
12949 copyright="PRAGMA ADE / ConTeXt Development Team",
12950 license="see context related readme files"
12951}
12952local type,next,tostring,tonumber=type,next,tostring,tonumber
12953local print=print
12954local concat,sortedhash=table.concat,table.sortedhash
12955local formatters,find,lower,gsub,topattern=string.formatters,string.find,string.lower,string.gsub,string.topattern
12956local is_boolean=string.is_boolean
12957local settings_to_hash=utilities.parsers.settings_to_hash
12958local allocate=utilities.storage.allocate
12959utilities=utilities or {}
12960local utilities=utilities
12961local setters=utilities.setters or {}
12962utilities.setters=setters
12963local data={}
12964local trace_initialize=false
12965local frozen=true
12966local function initialize_setter(filename,name,values)
12967 local setter=data[name]
12968 if setter then
12969 local data=setter.data
12970 if data then
12971 for key,newvalue in sortedhash(values) do
12972 local newvalue=is_boolean(newvalue,newvalue,true)
12973 local functions=data[key]
12974 if functions then
12975 local oldvalue=functions.value
12976 if functions.frozen then
12977 if trace_initialize then
12978 setter.report("%s: %a is %s to %a",filename,key,"frozen",oldvalue)
12979 end
12980 elseif #functions>0 and not oldvalue then
12981 if trace_initialize then
12982 setter.report("%s: %a is %s to %a",filename,key,"set",newvalue)
12983 end
12984 for i=1,#functions do
12985 functions[i](newvalue)
12986 end
12987 functions.value=newvalue
12988 functions.frozen=functions.frozen or frozen
12989 else
12990 if trace_initialize then
12991 setter.report("%s: %a is %s as %a",filename,key,"kept",oldvalue)
12992 end
12993 end
12994 else
12995 functions={ default=newvalue,frozen=frozen }
12996 data[key]=functions
12997 if trace_initialize then
12998 setter.report("%s: %a is %s to %a",filename,key,"defaulted",newvalue)
12999 end
13000 end
13001 end
13002 return true
13003 end
13004 end
13005end
13006local function set(t,what,newvalue)
13007 local data=t.data
13008 if data and not data.frozen then
13009 local done=t.done
13010 if type(what)=="string" then
13011 what=settings_to_hash(what)
13012 end
13013 if type(what)~="table" then
13014 return
13015 end
13016 if not done then
13017 done={}
13018 t.done=done
13019 end
13020 for w,value in sortedhash(what) do
13021 if value=="" then
13022 value=newvalue
13023 elseif not value then
13024 value=false
13025 else
13026 value=is_boolean(value,value,true)
13027 end
13028 w=topattern(w,true,true)
13029 for name,functions in sortedhash(data) do
13030 if done[name] then
13031 elseif find(name,w) then
13032 done[name]=true
13033 for i=1,#functions do
13034 functions[i](value)
13035 end
13036 functions.value=value
13037 end
13038 end
13039 end
13040 end
13041end
13042local function reset(t)
13043 local data=t.data
13044 if data and not data.frozen then
13045 for name,functions in sortedthash(data) do
13046 for i=1,#functions do
13047 functions[i](false)
13048 end
13049 functions.value=false
13050 end
13051 end
13052end
13053local function enable(t,what)
13054 set(t,what,true)
13055end
13056local function disable(t,what)
13057 local data=t.data
13058 if not what or what=="" then
13059 t.done={}
13060 reset(t)
13061 else
13062 set(t,what,false)
13063 end
13064end
13065local function register_setter(t,what,...)
13066 local data=t.data
13067 what=lower(what)
13068 local functions=data[what]
13069 if not functions then
13070 functions={}
13071 data[what]=functions
13072 if trace_initialize then
13073 t.report("defining %a",what)
13074 end
13075 end
13076 local default=functions.default
13077 for i=1,select("#",...) do
13078 local fnc=select(i,...)
13079 local typ=type(fnc)
13080 if typ=="string" then
13081 if trace_initialize then
13082 t.report("coupling %a to %a",what,fnc)
13083 end
13084 local s=fnc
13085 fnc=function(value) set(t,s,value) end
13086 elseif typ=="table" then
13087 functions.values=fnc
13088 fnc=nil
13089 elseif typ~="function" then
13090 fnc=nil
13091 end
13092 if fnc then
13093 functions[#functions+1]=fnc
13094 local value=functions.value or default
13095 if value~=nil then
13096 fnc(value)
13097 functions.value=value
13098 end
13099 end
13100 end
13101 return false
13102end
13103local function enable_setter(t,what)
13104 local e=t.enable
13105 t.enable,t.done=enable,{}
13106 set(t,what,true)
13107 enable(t,what)
13108 t.enable,t.done=e,{}
13109end
13110local function disable_setter(t,what)
13111 local e=t.disable
13112 t.disable,t.done=disable,{}
13113 disable(t,what)
13114 t.disable,t.done=e,{}
13115end
13116local function reset_setter(t)
13117 t.done={}
13118 reset(t)
13119end
13120local function list_setter(t)
13121 local list=table.sortedkeys(t.data)
13122 local user,system={},{}
13123 for l=1,#list do
13124 local what=list[l]
13125 if find(what,"^%*") then
13126 system[#system+1]=what
13127 else
13128 user[#user+1]=what
13129 end
13130 end
13131 return user,system
13132end
13133local function show_setter(t,pattern)
13134 local list=list_setter(t)
13135 t.report()
13136 for k=1,#list do
13137 local name=list[k]
13138 if not pattern or find(name,pattern) then
13139 local functions=t.data[name]
13140 if functions then
13141 local value=functions.value
13142 local default=functions.default
13143 local values=functions.values
13144 local modules=#functions
13145 if default==nil then
13146 default="unset"
13147 elseif type(default)=="table" then
13148 default=concat(default,"|")
13149 else
13150 default=tostring(default)
13151 end
13152 if value==nil then
13153 value="unset"
13154 elseif type(value)=="table" then
13155 value=concat(value,"|")
13156 else
13157 value=tostring(value)
13158 end
13159 t.report(name)
13160 t.report(" modules : %i",modules)
13161 t.report(" default : %s",default)
13162 t.report(" value : %s",value)
13163 if values then
13164 local v={} for i=1,#values do v[i]=tostring(values[i]) end
13165 t.report(" values : % t",v)
13166 end
13167 t.report()
13168 end
13169 end
13170 end
13171end
13172function setters.report(setter,fmt,...)
13173 if fmt then
13174 print(formatters["%-15s : %s"](setter.name,formatters[fmt](...)))
13175 else
13176 print("")
13177 end
13178end
13179local function setter_default(setter,name)
13180 local d=setter.data[name]
13181 return d and d.default
13182end
13183local function setter_value(setter,name)
13184 local d=setter.data[name]
13185 return d and (d.value or d.default)
13186end
13187local function setter_values(setter,name)
13188 local d=setter.data[name]
13189 return d and d.values
13190end
13191local function new_setter(name)
13192 local setter
13193 setter={
13194 data=allocate(),
13195 name=name,
13196 report=function(...) setters.report (setter,...) end,
13197 enable=function(...) enable_setter (setter,...) end,
13198 disable=function(...) disable_setter (setter,...) end,
13199 reset=function(...) reset_setter (setter,...) end,
13200 register=function(...) register_setter(setter,...) end,
13201 list=function(...) return list_setter (setter,...) end,
13202 show=function(...) show_setter (setter,...) end,
13203 default=function(...) return setter_default (setter,...) end,
13204 value=function(...) return setter_value (setter,...) end,
13205 values=function(...) return setter_values (setter,...) end,
13206 }
13207 data[name]=setter
13208 return setter
13209end
13210setters.enable=enable_setter
13211setters.disable=disable_setter
13212setters.register=register_setter
13213setters.list=list_setter
13214setters.show=show_setter
13215setters.reset=reset_setter
13216setters.new=new_setter
13217setters.initialize=initialize_setter
13218trackers=new_setter("trackers")
13219directives=new_setter("directives")
13220experiments=new_setter("experiments")
13221local t_enable,t_disable=trackers .enable,trackers .disable
13222local d_enable,d_disable=directives .enable,directives .disable
13223local e_enable,e_disable=experiments.enable,experiments.disable
13224local trace_directives=false local trace_directives=false trackers.register("system.directives",function(v) trace_directives=v end)
13225local trace_experiments=false local trace_experiments=false trackers.register("system.experiments",function(v) trace_experiments=v end)
13226function directives.enable(...)
13227 if trace_directives then
13228 directives.report("enabling: % t",{...})
13229 end
13230 d_enable(...)
13231end
13232function directives.disable(...)
13233 if trace_directives then
13234 directives.report("disabling: % t",{...})
13235 end
13236 d_disable(...)
13237end
13238function experiments.enable(...)
13239 if trace_experiments then
13240 experiments.report("enabling: % t",{...})
13241 end
13242 e_enable(...)
13243end
13244function experiments.disable(...)
13245 if trace_experiments then
13246 experiments.report("disabling: % t",{...})
13247 end
13248 e_disable(...)
13249end
13250directives.register("system.nostatistics",function(v)
13251 if statistics then
13252 statistics.enable=not v
13253 else
13254 end
13255end)
13256directives.register("system.nolibraries",function(v)
13257 if libraries then
13258 libraries=nil
13259 else
13260 end
13261end)
13262if environment then
13263 local engineflags=environment.engineflags
13264 if engineflags then
13265 local list=engineflags["c:trackers"] or engineflags["trackers"]
13266 if type(list)=="string" then
13267 initialize_setter("commandline flags","trackers",settings_to_hash(list))
13268 end
13269 local list=engineflags["c:directives"] or engineflags["directives"]
13270 if type(list)=="string" then
13271 initialize_setter("commandline flags","directives",settings_to_hash(list))
13272 end
13273 end
13274end
13275if texconfig then
13276 local function set(k,v)
13277 local v=tonumber(v)
13278 if v then
13279 texconfig[k]=v
13280 end
13281 end
13282 directives.register("luatex.expanddepth",function(v) set("expand_depth",v) end)
13283 directives.register("luatex.hashextra",function(v) set("hash_extra",v) end)
13284 directives.register("luatex.nestsize",function(v) set("nest_size",v) end)
13285 directives.register("luatex.maxinopen",function(v) set("max_in_open",v) end)
13286 directives.register("luatex.maxprintline",function(v) set("max_print_line",v) end)
13287 directives.register("luatex.maxstrings",function(v) set("max_strings",v) end)
13288 directives.register("luatex.paramsize",function(v) set("param_size",v) end)
13289 directives.register("luatex.savesize",function(v) set("save_size",v) end)
13290 directives.register("luatex.stacksize",function(v) set("stack_size",v) end)
13291end
13292local data=table.setmetatableindex("table")
13293updaters={
13294 register=function(what,f)
13295 local d=data[what]
13296 d[#d+1]=f
13297 end,
13298 apply=function(what,...)
13299 local d=data[what]
13300 for i=1,#d do
13301 d[i](...)
13302 end
13303 end,
13304}
13305
13306
13307end
13308
13309do
13310
13311package.loaded["trac-log"] = package.loaded["trac-log"] or true
13312
13313
13314
13315if not modules then modules={} end modules ['trac-log']={
13316 version=1.001,
13317 comment="companion to trac-log.mkiv",
13318 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
13319 copyright="PRAGMA ADE / ConTeXt Development Team",
13320 license="see context related readme files"
13321}
13322local next,type,select,print=next,type,select,print
13323local format,gmatch,find=string.format,string.gmatch,string.find
13324local concat,insert,remove=table.concat,table.insert,table.remove
13325local topattern=string.topattern
13326local utfchar=utf.char
13327local datetime=os.date
13328local sleep=os.sleep
13329local openfile=io.open
13330local write_nl=print
13331local write=io.write
13332local setmetatableindex=table.setmetatableindex
13333local formatters=string.formatters
13334local settings_to_hash=utilities.parsers.settings_to_hash
13335local sortedkeys=table.sortedkeys
13336local variant="default"
13337logs=logs or {}
13338local logs=logs
13339local moreinfo=[[
13340More information about ConTeXt and the tools that come with it can be found at:
13341]].."\n"..[[
13342maillist : ntg-context@ntg.nl / http://www.ntg.nl/mailman/listinfo/ntg-context
13343webpage : http://www.pragma-ade.nl / http://tex.aanhet.net
13344wiki : http://contextgarden.net
13345]]
13346formatters.add (
13347 formatters,"unichr",
13348 [["U+" .. format("%%05X",%s) .. " (" .. utfchar(%s) .. ")"]]
13349)
13350formatters.add (
13351 formatters,"chruni",
13352 [[utfchar(%s) .. " (U+" .. format("%%05X",%s) .. ")"]]
13353)
13354local function ignore() end
13355setmetatableindex(logs,function(t,k) t[k]=ignore;return ignore end)
13356local report,subreport,status,settarget,setformats,settranslations
13357local direct,subdirect,writer,pushtarget,poptarget,setlogfile,settimedlog,setprocessor,setformatters,newline
13358local function ansisupported(specification)
13359 if specification~="ansi" and specification~="ansilog" then
13360 return false
13361 elseif os and os.enableansi then
13362 return os.enableansi()
13363 else
13364 return false
13365 end
13366end
13367do
13368 local report_yes,subreport_yes,status_yes
13369 local report_nop,subreport_nop,status_nop
13370 local variants={
13371 default={
13372 formats={
13373 report_yes=formatters["%-15s | %s"],
13374 report_nop=formatters["%-15s |"],
13375 subreport_yes=formatters["%-15s | %s | %s"],
13376 subreport_nop=formatters["%-15s | %s |"],
13377 status_yes=formatters["%-15s : %s\n"],
13378 status_nop=formatters["%-15s :\n"],
13379 },
13380 },
13381 ansi={
13382 formats={
13383 report_yes=formatters["[0;32m%-15s [0;1m|[0m %s"],
13384 report_nop=formatters["[0;32m%-15s [0;1m|[0m"],
13385 subreport_yes=formatters["[0;32m%-15s [0;1m|[0;31m %s [0;1m|[0m %s"],
13386 subreport_nop=formatters["[0;32m%-15s [0;1m|[0;31m %s [0;1m|[0m"],
13387 status_yes=formatters["[0;32m%-15s [0;1m:[0m %s\n"],
13388 status_nop=formatters["[0;32m%-15s [0;1m:[0m\n"],
13389 },
13390 },
13391 }
13392 logs.flush=ignore
13393 writer=function(s)
13394 write_nl(s)
13395 end
13396 newline=function()
13397 write_nl("\n")
13398 end
13399 report=function(a,b,c,...)
13400 if c then
13401 write_nl(report_yes(a,formatters[b](c,...)))
13402 elseif b then
13403 write_nl(report_yes(a,b))
13404 elseif a then
13405 write_nl(report_nop(a))
13406 else
13407 write_nl("")
13408 end
13409 end
13410 subreport=function(a,sub,b,c,...)
13411 if c then
13412 write_nl(subreport_yes(a,sub,formatters[b](c,...)))
13413 elseif b then
13414 write_nl(subreport_yes(a,sub,b))
13415 elseif a then
13416 write_nl(subreport_nop(a,sub))
13417 else
13418 write_nl("")
13419 end
13420 end
13421 status=function(a,b,c,...)
13422 if c then
13423 write_nl(status_yes(a,formatters[b](c,...)))
13424 elseif b then
13425 write_nl(status_yes(a,b))
13426 elseif a then
13427 write_nl(status_nop(a))
13428 else
13429 write_nl("\n")
13430 end
13431 end
13432 direct=ignore
13433 subdirect=ignore
13434 settarget=ignore
13435 pushtarget=ignore
13436 poptarget=ignore
13437 setformats=ignore
13438 settranslations=ignore
13439 setprocessor=function(f)
13440 local writeline=write_nl
13441 write_nl=function(s)
13442 writeline(f(s))
13443 end
13444 end
13445 setformatters=function(specification)
13446 local f=nil
13447 local d=variants.default
13448 if specification then
13449 if type(specification)=="table" then
13450 f=specification.formats or specification
13451 else
13452 if not ansisupported(specification) then
13453 specification="default"
13454 end
13455 local v=variants[specification]
13456 if v then
13457 f=v.formats
13458 end
13459 end
13460 end
13461 if f then
13462 d=d.formats
13463 else
13464 f=d.formats
13465 d=f
13466 end
13467 setmetatableindex(f,d)
13468 report_yes=f.report_yes
13469 report_nop=f.report_nop
13470 subreport_yes=f.subreport_yes
13471 subreport_nop=f.subreport_nop
13472 status_yes=f.status_yes
13473 status_nop=f.status_nop
13474 end
13475 setformatters(variant)
13476 setlogfile=function(name,keepopen)
13477 if name and name~="" then
13478 local localtime=os.localtime
13479 local writeline=write_nl
13480 if keepopen then
13481 local f=io.open(name,"ab")
13482 write_nl=function(s)
13483 writeline(s)
13484 f:write(localtime()," | ",s,"\n")
13485 end
13486 else
13487 write_nl=function(s)
13488 writeline(s)
13489 local f=io.open(name,"ab")
13490 f:write(localtime()," | ",s,"\n")
13491 f:close()
13492 end
13493 end
13494 end
13495 setlogfile=ignore
13496 end
13497 settimedlog=function()
13498 local localtime=os.localtime
13499 local writeline=write_nl
13500 write_nl=function(s)
13501 writeline(localtime().." | "..s)
13502 end
13503 settimedlog=ignore
13504 end
13505end
13506logs.report=report
13507logs.subreport=subreport
13508logs.status=status
13509logs.settarget=settarget
13510logs.pushtarget=pushtarget
13511logs.poptarget=poptarget
13512logs.setformats=setformats
13513logs.settranslations=settranslations
13514logs.setlogfile=setlogfile
13515logs.settimedlog=settimedlog
13516logs.setprocessor=setprocessor
13517logs.setformatters=setformatters
13518logs.direct=direct
13519logs.subdirect=subdirect
13520logs.writer=writer
13521logs.newline=newline
13522local data={}
13523local states=nil
13524local force=false
13525function logs.reporter(category,subcategory)
13526 local logger=data[category]
13527 if not logger then
13528 local state=states==true
13529 if not state and type(states)=="table" then
13530 for c,_ in next,states do
13531 if find(category,c) then
13532 state=true
13533 break
13534 end
13535 end
13536 end
13537 logger={
13538 reporters={},
13539 state=state,
13540 }
13541 data[category]=logger
13542 end
13543 local reporter=logger.reporters[subcategory or "default"]
13544 if not reporter then
13545 if subcategory then
13546 reporter=function(...)
13547 if force or not logger.state then
13548 subreport(category,subcategory,...)
13549 end
13550 end
13551 logger.reporters[subcategory]=reporter
13552 else
13553 local tag=category
13554 reporter=function(...)
13555 if force or not logger.state then
13556 report(category,...)
13557 end
13558 end
13559 logger.reporters.default=reporter
13560 end
13561 end
13562 return reporter
13563end
13564logs.new=logs.reporter
13565local ctxreport=logs.writer
13566function logs.setmessenger(m)
13567 ctxreport=m
13568end
13569function logs.messenger(category,subcategory)
13570 if subcategory then
13571 return function(...)
13572 ctxreport(subdirect(category,subcategory,...))
13573 end
13574 else
13575 return function(...)
13576 ctxreport(direct(category,...))
13577 end
13578 end
13579end
13580local function setblocked(category,value)
13581 if category==true or category=="all" then
13582 category,value="*",true
13583 elseif category==false then
13584 category,value="*",false
13585 elseif value==nil then
13586 value=true
13587 end
13588 if category=="*" then
13589 states=value
13590 for k,v in next,data do
13591 v.state=value
13592 end
13593 else
13594 alllocked=false
13595 states=settings_to_hash(category,type(states)=="table" and states or nil)
13596 for c in next,states do
13597 local v=data[c]
13598 if v then
13599 v.state=value
13600 else
13601 c=topattern(c,true,true)
13602 for k,v in next,data do
13603 if find(k,c) then
13604 v.state=value
13605 end
13606 end
13607 end
13608 end
13609 end
13610end
13611function logs.disable(category,value)
13612 setblocked(category,value==nil and true or value)
13613end
13614function logs.enable(category)
13615 setblocked(category,false)
13616end
13617function logs.categories()
13618 return sortedkeys(data)
13619end
13620function logs.show()
13621 local n,c,s,max=0,0,0,0
13622 for category,v in table.sortedpairs(data) do
13623 n=n+1
13624 local state=v.state
13625 local reporters=v.reporters
13626 local nc=#category
13627 if nc>c then
13628 c=nc
13629 end
13630 for subcategory,_ in next,reporters do
13631 local ns=#subcategory
13632 if ns>c then
13633 s=ns
13634 end
13635 local m=nc+ns
13636 if m>max then
13637 max=m
13638 end
13639 end
13640 local subcategories=concat(sortedkeys(reporters),", ")
13641 if state==true then
13642 state="disabled"
13643 elseif state==false then
13644 state="enabled"
13645 else
13646 state="unknown"
13647 end
13648 report("logging","category %a, subcategories %a, state %a",category,subcategories,state)
13649 end
13650 report("logging","categories: %s, max category: %s, max subcategory: %s, max combined: %s",n,c,s,max)
13651end
13652local delayed_reporters={}
13653setmetatableindex(delayed_reporters,function(t,k)
13654 local v=logs.reporter(k.name)
13655 t[k]=v
13656 return v
13657end)
13658function utilities.setters.report(setter,...)
13659 delayed_reporters[setter](...)
13660end
13661directives.register("logs.blocked",function(v)
13662 setblocked(v,true)
13663end)
13664directives.register("logs.target",function(v)
13665 settarget(v)
13666end)
13667local nesting=0
13668local verbose=false
13669local hasscheme=url.hasscheme
13670local simple=logs.reporter("comment")
13671logs.simple=simple
13672logs.simpleline=simple
13673logs.setprogram=ignore
13674logs.extendbanner=ignore
13675logs.reportlines=ignore
13676logs.reportbanner=ignore
13677logs.reportline=ignore
13678logs.simplelines=ignore
13679logs.help=ignore
13680local Carg,C,lpegmatch=lpeg.Carg,lpeg.C,lpeg.match
13681local p_newline=lpeg.patterns.newline
13682local linewise=(
13683 Carg(1)*C((1-p_newline)^1)/function(t,s) t.report(s) end+Carg(1)*p_newline^2/function(t) t.report() end+p_newline
13684)^1
13685local function reportlines(t,str)
13686 if str then
13687 lpegmatch(linewise,str,1,t)
13688 end
13689end
13690local function reportbanner(t)
13691 local banner=t.banner
13692 if banner then
13693 t.report(banner)
13694 t.report()
13695 end
13696end
13697local function reportversion(t)
13698 local banner=t.banner
13699 if banner then
13700 t.report(banner)
13701 end
13702end
13703local function reporthelp(t,...)
13704 local helpinfo=t.helpinfo
13705 if type(helpinfo)=="string" then
13706 reportlines(t,helpinfo)
13707 elseif type(helpinfo)=="table" then
13708 for i=1,select("#",...) do
13709 reportlines(t,t.helpinfo[select(i,...)])
13710 if i<n then
13711 t.report()
13712 end
13713 end
13714 end
13715end
13716local function reportinfo(t)
13717 t.report()
13718 reportlines(t,t.moreinfo)
13719end
13720local function reportexport(t,method)
13721 report(t.helpinfo)
13722end
13723local reporters={
13724 lines=reportlines,
13725 banner=reportbanner,
13726 version=reportversion,
13727 help=reporthelp,
13728 info=reportinfo,
13729 export=reportexport,
13730}
13731local exporters={
13732}
13733logs.reporters=reporters
13734logs.exporters=exporters
13735function logs.application(t)
13736 local arguments=environment and environment.arguments
13737 if arguments then
13738 local ansi=arguments.ansi or arguments.ansilog
13739 if ansi then
13740 logs.setformatters(arguments.ansi and "ansi" or "ansilog")
13741 end
13742 end
13743 t.name=t.name or "unknown"
13744 t.banner=t.banner
13745 t.moreinfo=moreinfo
13746 t.report=logs.reporter(t.name)
13747 t.help=function(...)
13748 reporters.banner(t)
13749 reporters.help(t,...)
13750 reporters.info(t)
13751 end
13752 t.export=function(...)
13753 reporters.export(t,...)
13754 end
13755 t.identify=function()
13756 reporters.banner(t)
13757 end
13758 t.version=function()
13759 reporters.version(t)
13760 end
13761 return t
13762end
13763local f_syslog=formatters["%s %s => %s => %s => %s\r"]
13764function logs.system(whereto,process,jobname,category,fmt,arg,...)
13765 local message=f_syslog(datetime("%d/%m/%y %H:%m:%S"),process,jobname,category,arg==nil and fmt or format(fmt,arg,...))
13766 for i=1,10 do
13767 local f=openfile(whereto,"a")
13768 if f then
13769 f:write(message)
13770 f:close()
13771 break
13772 else
13773 sleep(0.1)
13774 end
13775 end
13776end
13777local report_system=logs.reporter("system","logs")
13778if utilities then
13779 utilities.report=report_system
13780end
13781if package.helpers.report then
13782 package.helpers.report=logs.reporter("package loader")
13783end
13784
13785
13786end
13787
13788do
13789
13790package.loaded["trac-inf"] = package.loaded["trac-inf"] or true
13791
13792
13793
13794if not modules then modules={} end modules ['trac-inf']={
13795 version=1.001,
13796 comment="companion to trac-inf.mkiv",
13797 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
13798 copyright="PRAGMA ADE / ConTeXt Development Team",
13799 license="see context related readme files"
13800}
13801local type,tonumber,select=type,tonumber,select
13802local format,lower,find=string.format,string.lower,string.find
13803local concat=table.concat
13804local clock=os.gettimeofday or os.clock
13805local setmetatableindex=table.setmetatableindex
13806local serialize=table.serialize
13807local formatters=string.formatters
13808statistics=statistics or {}
13809local statistics=statistics
13810statistics.enable=true
13811statistics.threshold=0.01
13812local statusinfo,n,registered,timers={},0,{},{}
13813setmetatableindex(timers,function(t,k)
13814 local v={ timing=0,loadtime=0,offset=0 }
13815 t[k]=v
13816 return v
13817end)
13818local function hastiming(instance)
13819 return instance and timers[instance]
13820end
13821local function resettiming(instance)
13822 timers[instance or "notimer"]={ timing=0,loadtime=0,offset=0 }
13823end
13824local ticks=clock
13825local seconds=function(n) return n or 0 end
13826if os.type~="windows" then
13827elseif lua.getpreciseticks then
13828 ticks=lua.getpreciseticks
13829 seconds=lua.getpreciseseconds
13830elseif FFISUPPORTED then
13831 local okay,kernel=pcall(ffi.load,"kernel32")
13832 if kernel then
13833 local tonumber=ffi.number or tonumber
13834 ffi.cdef[[
13835 int QueryPerformanceFrequency(int64_t *lpFrequency);
13836 int QueryPerformanceCounter(int64_t *lpPerformanceCount);
13837 ]]
13838 local target=ffi.new("__int64[1]")
13839 ticks=function()
13840 if kernel.QueryPerformanceCounter(target)==1 then
13841 return tonumber(target[0])
13842 else
13843 return 0
13844 end
13845 end
13846 local target=ffi.new("__int64[1]")
13847 seconds=function(ticks)
13848 if kernel.QueryPerformanceFrequency(target)==1 then
13849 return ticks/tonumber(target[0])
13850 else
13851 return 0
13852 end
13853 end
13854 end
13855else
13856end
13857local function starttiming(instance,reset)
13858 local timer=timers[instance or "notimer"]
13859 local it=timer.timing
13860 if reset then
13861 it=0
13862 timer.loadtime=0
13863 end
13864 if it==0 then
13865 timer.starttime=ticks()
13866 if not timer.loadtime then
13867 timer.loadtime=0
13868 end
13869 end
13870 timer.timing=it+1
13871end
13872local function stoptiming(instance)
13873 local timer=timers[instance or "notimer"]
13874 local it=timer.timing
13875 if it>1 then
13876 timer.timing=it-1
13877 else
13878 local starttime=timer.starttime
13879 if starttime and starttime>0 then
13880 local stoptime=ticks()
13881 local loadtime=stoptime-starttime
13882 timer.stoptime=stoptime
13883 timer.loadtime=timer.loadtime+loadtime
13884 timer.timing=0
13885 timer.starttime=0
13886 end
13887 end
13888end
13889local function benchmarktimer(instance)
13890 local timer=timers[instance or "notimer"]
13891 local it=timer.timing
13892 if it>1 then
13893 timer.timing=it-1
13894 else
13895 local starttime=timer.starttime
13896 if starttime and starttime>0 then
13897 timer.offset=ticks()-starttime
13898 else
13899 timer.offset=0
13900 end
13901 end
13902end
13903local function elapsed(instance)
13904 if type(instance)=="number" then
13905 return instance
13906 else
13907 local timer=timers[instance or "notimer"]
13908 return timer and seconds(timer.loadtime-2*(timer.offset or 0)) or 0
13909 end
13910end
13911local function currenttime(instance)
13912 if type(instance)=="number" then
13913 return instance
13914 else
13915 local timer=timers[instance or "notimer"]
13916 local it=timer.timing
13917 if it>1 then
13918 else
13919 local starttime=timer.starttime
13920 if starttime and starttime>0 then
13921 return seconds(timer.loadtime+ticks()-starttime-2*(timer.offset or 0))
13922 end
13923 end
13924 return 0
13925 end
13926end
13927local function elapsedtime(instance)
13928 return format("%0.3f",elapsed(instance))
13929end
13930local function elapsedindeed(instance)
13931 return elapsed(instance)>statistics.threshold
13932end
13933local function elapsedseconds(instance,rest)
13934 if elapsedindeed(instance) then
13935 return format("%0.3f seconds %s",elapsed(instance),rest or "")
13936 end
13937end
13938statistics.hastiming=hastiming
13939statistics.resettiming=resettiming
13940statistics.starttiming=starttiming
13941statistics.stoptiming=stoptiming
13942statistics.currenttime=currenttime
13943statistics.elapsed=elapsed
13944statistics.elapsedtime=elapsedtime
13945statistics.elapsedindeed=elapsedindeed
13946statistics.elapsedseconds=elapsedseconds
13947statistics.benchmarktimer=benchmarktimer
13948function statistics.register(tag,fnc)
13949 if statistics.enable and type(fnc)=="function" then
13950 local rt=registered[tag] or (#statusinfo+1)
13951 statusinfo[rt]={ tag,fnc }
13952 registered[tag]=rt
13953 if #tag>n then n=#tag end
13954 end
13955end
13956local report=logs.reporter("mkiv lua stats")
13957function statistics.show()
13958 if statistics.enable then
13959 local register=statistics.register
13960 register("used platform",function()
13961 return format("%s, type: %s, binary subtree: %s",
13962 os.platform or "unknown",os.type or "unknown",environment.texos or "unknown")
13963 end)
13964 register("used engine",function()
13965 return format("%s version: %s, functionality level: %s, banner: %s",
13966 LUATEXENGINE,LUATEXVERSION,LUATEXFUNCTIONALITY,lower(status.banner))
13967 end)
13968 register("used hash slots",function()
13969 return format("%s of %s + %s",status.cs_count,status.hash_size,status.hash_extra)
13970 end)
13971 register("callbacks",statistics.callbacks)
13972 if JITSUPPORTED then
13973 local jitstatus=jit.status
13974 if jitstatus then
13975 local jitstatus={ jitstatus() }
13976 if jitstatus[1] then
13977 register("luajit options",concat(jitstatus," ",2))
13978 end
13979 end
13980 end
13981 register("lua properties",function()
13982 local hash=2^status.luatex_hashchars
13983 local mask=load([[τεχ = 1]]) and "utf" or "ascii"
13984 return format("engine: %s %s, used memory: %s, hash chars: min(%i,40), symbol mask: %s (%s)",
13985 jit and "luajit" or "lua",LUAVERSION,statistics.memused(),hash,mask,mask=="utf" and "τεχ" or "tex")
13986 end)
13987 register("runtime",statistics.runtime)
13988 logs.newline()
13989 for i=1,#statusinfo do
13990 local s=statusinfo[i]
13991 local r=s[2]()
13992 if r then
13993 report("%s: %s",s[1],r)
13994 end
13995 end
13996 statistics.enable=false
13997 end
13998end
13999function statistics.memused()
14000 local round=math.round or math.floor
14001 return format("%s MB, ctx: %s MB, max: %s MB",
14002 round(collectgarbage("count")/1000),
14003 round(status.luastate_bytes/1000000),
14004 status.luastate_bytes_max and round(status.luastate_bytes_max/1000000) or "unknown"
14005 )
14006end
14007starttiming(statistics)
14008function statistics.formatruntime(runtime)
14009 return format("%s seconds",runtime)
14010end
14011function statistics.runtime()
14012 stoptiming(statistics)
14013 local runtime=lua.getruntime and lua.getruntime() or elapsedtime(statistics)
14014 return statistics.formatruntime(runtime)
14015end
14016local report=logs.reporter("system")
14017function statistics.timed(action,all)
14018 starttiming("run")
14019 action()
14020 stoptiming("run")
14021 local runtime=tonumber(elapsedtime("run"))
14022 if all then
14023 local alltime=tonumber(lua.getruntime and lua.getruntime() or elapsedtime(statistics))
14024 if alltime and alltime>0 then
14025 report("total runtime: %0.3f seconds of %0.3f seconds",runtime,alltime)
14026 return
14027 end
14028 end
14029 report("total runtime: %0.3f seconds",runtime)
14030end
14031function statistics.tracefunction(base,tag,...)
14032 for i=1,select("#",...) do
14033 local name=select(i,...)
14034 local stat={}
14035 local func=base[name]
14036 setmetatableindex(stat,function(t,k) t[k]=0 return 0 end)
14037 base[name]=function(n,k,v) stat[k]=stat[k]+1 return func(n,k,v) end
14038 statistics.register(formatters["%s.%s"](tag,name),function() return serialize(stat,"calls") end)
14039 end
14040end
14041function status.getreadstate()
14042 return {
14043 filename=status.filename or "?",
14044 linenumber=status.linenumber or 0,
14045 iocode=status.inputid or 0,
14046 }
14047end
14048
14049
14050end
14051
14052do
14053
14054package.loaded["trac-pro"] = package.loaded["trac-pro"] or true
14055
14056
14057
14058if not modules then modules={} end modules ['trac-pro']={
14059 version=1.001,
14060 comment="companion to luat-lib.mkiv",
14061 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
14062 copyright="PRAGMA ADE / ConTeXt Development Team",
14063 license="see context related readme files"
14064}
14065local getmetatable,setmetatable,rawset,type,next=getmetatable,setmetatable,rawset,type,next
14066local trace_namespaces=false trackers.register("system.namespaces",function(v) trace_namespaces=v end)
14067local report_system=logs.reporter("system","protection")
14068namespaces=namespaces or {}
14069local namespaces=namespaces
14070local registered={}
14071local function report_index(k,name)
14072 if trace_namespaces then
14073 report_system("reference to %a in protected namespace %a: %s",k,name)
14074 debugger.showtraceback(report_system)
14075 else
14076 report_system("reference to %a in protected namespace %a",k,name)
14077 end
14078end
14079local function report_newindex(k,name)
14080 if trace_namespaces then
14081 report_system("assignment to %a in protected namespace %a: %s",k,name)
14082 debugger.showtraceback(report_system)
14083 else
14084 report_system("assignment to %a in protected namespace %a",k,name)
14085 end
14086end
14087local function register(name)
14088 local data=name=="global" and _G or _G[name]
14089 if not data then
14090 return
14091 end
14092 registered[name]=data
14093 local m=getmetatable(data)
14094 if not m then
14095 m={}
14096 setmetatable(data,m)
14097 end
14098 local index,newindex={},{}
14099 m.__saved__index=m.__index
14100 m.__no__index=function(t,k)
14101 if not index[k] then
14102 index[k]=true
14103 report_index(k,name)
14104 end
14105 return nil
14106 end
14107 m.__saved__newindex=m.__newindex
14108 m.__no__newindex=function(t,k,v)
14109 if not newindex[k] then
14110 newindex[k]=true
14111 report_newindex(k,name)
14112 end
14113 rawset(t,k,v)
14114 end
14115 m.__protection__depth=0
14116end
14117local function private(name)
14118 local data=registered[name]
14119 if not data then
14120 data=_G[name]
14121 if not data then
14122 data={}
14123 _G[name]=data
14124 end
14125 register(name)
14126 end
14127 return data
14128end
14129local function protect(name)
14130 local data=registered[name]
14131 if not data then
14132 return
14133 end
14134 local m=getmetatable(data)
14135 local pd=m.__protection__depth
14136 if pd>0 then
14137 m.__protection__depth=pd+1
14138 else
14139 m.__save_d_index,m.__saved__newindex=m.__index,m.__newindex
14140 m.__index,m.__newindex=m.__no__index,m.__no__newindex
14141 m.__protection__depth=1
14142 end
14143end
14144local function unprotect(name)
14145 local data=registered[name]
14146 if not data then
14147 return
14148 end
14149 local m=getmetatable(data)
14150 local pd=m.__protection__depth
14151 if pd>1 then
14152 m.__protection__depth=pd-1
14153 else
14154 m.__index,m.__newindex=m.__saved__index,m.__saved__newindex
14155 m.__protection__depth=0
14156 end
14157end
14158local function protectall()
14159 for name,_ in next,registered do
14160 if name~="global" then
14161 protect(name)
14162 end
14163 end
14164end
14165local function unprotectall()
14166 for name,_ in next,registered do
14167 if name~="global" then
14168 unprotect(name)
14169 end
14170 end
14171end
14172namespaces.register=register
14173namespaces.private=private
14174namespaces.protect=protect
14175namespaces.unprotect=unprotect
14176namespaces.protectall=protectall
14177namespaces.unprotectall=unprotectall
14178namespaces.private("namespaces") registered={} register("global")
14179directives.register("system.protect",function(v)
14180 if v then
14181 protectall()
14182 else
14183 unprotectall()
14184 end
14185end)
14186directives.register("system.checkglobals",function(v)
14187 if v then
14188 report_system("enabling global namespace guard")
14189 protect("global")
14190 else
14191 report_system("disabling global namespace guard")
14192 unprotect("global")
14193 end
14194end)
14195
14196
14197end
14198
14199do
14200
14201package.loaded["util-lua"] = package.loaded["util-lua"] or true
14202
14203
14204
14205if not modules then modules={} end modules ['util-lua']={
14206 version=1.001,
14207 comment="companion to luat-lib.mkiv",
14208 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
14209 comment="the strip code is written by Peter Cawley",
14210 copyright="PRAGMA ADE / ConTeXt Development Team",
14211 license="see context related readme files"
14212}
14213local rep,sub,byte,dump,format=string.rep,string.sub,string.byte,string.dump,string.format
14214local load,loadfile,type,collectgarbage=load,loadfile,type,collectgarbage
14215utilities=utilities or {}
14216utilities.lua=utilities.lua or {}
14217local luautilities=utilities.lua
14218local report_lua=logs.reporter("system","lua")
14219local report_mem=logs.reporter("system","lua memory")
14220local tracestripping=false
14221local tracememory=false
14222luautilities.stripcode=true
14223luautilities.alwaysstripcode=false
14224luautilities.nofstrippedchunks=0
14225luautilities.nofstrippedbytes=0
14226local strippedchunks={}
14227luautilities.strippedchunks=strippedchunks
14228if not LUATEXENGINE then
14229 LUATEXENGINE=status.luatex_engine and string.lower(status.luatex_engine)
14230 JITSUPPORTED=LUATEXENGINE=="luajittex" or jit
14231 CONTEXTLMTXMODE=CONTEXTLMTXMODE or (LUATEXENGINE=="luametatex" and 1) or 0
14232end
14233luautilities.suffixes={
14234 tma="tma",
14235 tmc=(CONTEXTLMTXMODE and CONTEXTLMTXMODE>0 and "tmd") or (jit and "tmb") or "tmc",
14236 lua="lua",
14237 lmt="lmt",
14238 luc=(CONTEXTLMTXMODE and CONTEXTLMTXMODE>0 and "lud") or (jit and "lub") or "luc",
14239 lui="lui",
14240 luv="luv",
14241 luj="luj",
14242 tua="tua",
14243 tuc=(CONTEXTLMTXMODE and CONTEXTLMTXMODE>0 and "tud") or (jit and "tub") or "tuc",
14244}
14245local function register(name)
14246 if tracestripping then
14247 report_lua("stripped bytecode from %a",name or "unknown")
14248 end
14249 strippedchunks[#strippedchunks+1]=name
14250 luautilities.nofstrippedchunks=luautilities.nofstrippedchunks+1
14251end
14252local function stupidcompile(luafile,lucfile,strip)
14253 local code=io.loaddata(luafile)
14254 if code and code~="" then
14255 code=load(code)
14256 if code then
14257 code=dump(code,strip and luautilities.stripcode or luautilities.alwaysstripcode)
14258 if code and code~="" then
14259 register(name)
14260 io.savedata(lucfile,code)
14261 return true,0
14262 end
14263 else
14264 report_lua("fatal error %a in file %a",1,luafile)
14265 end
14266 else
14267 report_lua("fatal error %a in file %a",2,luafile)
14268 end
14269 return false,0
14270end
14271function luautilities.loadedluacode(fullname,forcestrip,name,macros)
14272 name=name or fullname
14273 if macros then
14274 macros=lua.macros
14275 end
14276 local code,message
14277 if macros then
14278 code,message=macros.loaded(fullname,true,false)
14279 else
14280 code,message=loadfile(fullname)
14281 end
14282 if code then
14283 code()
14284 else
14285 report_lua("loading of file %a failed:\n\t%s",fullname,message or "no message")
14286 code,message=loadfile(fullname)
14287 end
14288 if forcestrip and luautilities.stripcode then
14289 if type(forcestrip)=="function" then
14290 forcestrip=forcestrip(fullname)
14291 end
14292 if forcestrip or luautilities.alwaysstripcode then
14293 register(name)
14294 return load(dump(code,true)),0
14295 else
14296 return code,0
14297 end
14298 elseif luautilities.alwaysstripcode then
14299 register(name)
14300 return load(dump(code,true)),0
14301 else
14302 return code,0
14303 end
14304end
14305function luautilities.strippedloadstring(code,name,forcestrip)
14306 local code,message=load(code)
14307 if not code then
14308 report_lua("loading of file %a failed:\n\t%s",name,message or "no message")
14309 end
14310 if forcestrip and luautilities.stripcode or luautilities.alwaysstripcode then
14311 register(name)
14312 return load(dump(code,true)),0
14313 else
14314 return code,0
14315 end
14316end
14317function luautilities.loadstring(code,name)
14318 local code,message=load(code)
14319 if not code then
14320 report_lua("loading of file %a failed:\n\t%s",name,message or "no message")
14321 end
14322 return code,0
14323end
14324function luautilities.compile(luafile,lucfile,cleanup,strip,fallback)
14325 report_lua("compiling %a into %a",luafile,lucfile)
14326 os.remove(lucfile)
14327 local done=stupidcompile(luafile,lucfile,strip~=false)
14328 if done then
14329 report_lua("dumping %a into %a stripped",luafile,lucfile)
14330 if cleanup==true and lfs.isfile(lucfile) and lfs.isfile(luafile) then
14331 report_lua("removing %a",luafile)
14332 os.remove(luafile)
14333 end
14334 end
14335 return done
14336end
14337function luautilities.loadstripped(...)
14338 local l=load(...)
14339 if l then
14340 return load(dump(l,true))
14341 end
14342end
14343local finalizers={}
14344setmetatable(finalizers,{
14345 __gc=function(t)
14346 for i=1,#t do
14347 pcall(t[i])
14348 end
14349 end
14350} )
14351function luautilities.registerfinalizer(f)
14352 finalizers[#finalizers+1]=f
14353end
14354function luautilities.checkmemory(previous,threshold,trace)
14355 local current=collectgarbage("count")
14356 if previous then
14357 local checked=(threshold or 64)*1024
14358 local delta=current-previous
14359 if current-previous>checked then
14360 collectgarbage("collect")
14361 local afterwards=collectgarbage("count")
14362 if trace or tracememory then
14363 report_mem("previous %r MB, current %r MB, delta %r MB, threshold %r MB, afterwards %r MB",
14364 previous/1024,current/1024,delta/1024,threshold,afterwards)
14365 end
14366 return afterwards
14367 elseif trace or tracememory then
14368 report_mem("previous %r MB, current %r MB, delta %r MB, threshold %r MB",
14369 previous/1024,current/1024,delta/1024,threshold)
14370 end
14371 end
14372 return current
14373end
14374
14375
14376end
14377
14378do
14379
14380package.loaded["util-deb"] = package.loaded["util-deb"] or true
14381
14382
14383
14384if not modules then modules={} end modules ['util-deb']={
14385 version=1.001,
14386 comment="companion to luat-lib.mkiv",
14387 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
14388 copyright="PRAGMA ADE / ConTeXt Development Team",
14389 license="see context related readme files"
14390}
14391local type,next,tostring,tonumber=type,next,tostring,tonumber
14392local format,find,sub,gsub=string.format,string.find,string.sub,string.gsub
14393local insert,remove,sort=table.insert,table.remove,table.sort
14394local setmetatableindex=table.setmetatableindex
14395utilities=utilities or {}
14396local debugger=utilities.debugger or {}
14397utilities.debugger=debugger
14398local report=logs.reporter("debugger")
14399local ticks=os.gettimeofday or os.clock
14400local seconds=function(n) return n or 0 end
14401local overhead=0
14402local dummycalls=10*1000
14403local nesting=0
14404local names={}
14405local initialize=false
14406if lua.getpreciseticks then
14407 initialize=function()
14408 ticks=lua.getpreciseticks
14409 seconds=lua.getpreciseseconds
14410 initialize=false
14411 end
14412elseif not (FFISUPPORTED and ffi) then
14413elseif os.type=="windows" then
14414 initialize=function()
14415 local kernel=ffilib("kernel32","system")
14416 if kernel then
14417 local tonumber=ffi.number or tonumber
14418 ffi.cdef[[
14419 int QueryPerformanceFrequency(int64_t *lpFrequency);
14420 int QueryPerformanceCounter(int64_t *lpPerformanceCount);
14421 ]]
14422 local target=ffi.new("__int64[1]")
14423 ticks=function()
14424 if kernel.QueryPerformanceCounter(target)==1 then
14425 return tonumber(target[0])
14426 else
14427 return 0
14428 end
14429 end
14430 local target=ffi.new("__int64[1]")
14431 seconds=function(ticks)
14432 if kernel.QueryPerformanceFrequency(target)==1 then
14433 return ticks/tonumber(target[0])
14434 else
14435 return 0
14436 end
14437 end
14438 end
14439 initialize=false
14440 end
14441elseif os.type=="unix" then
14442 initialize=function()
14443 local C=ffi.C
14444 local tonumber=ffi.number or tonumber
14445 ffi.cdef [[
14446 /* what a mess */
14447 typedef int clk_id_t;
14448 typedef enum { CLOCK_REALTIME, CLOCK_MONOTONIC, CLOCK_PROCESS_CPUTIME_ID } clk_id;
14449 typedef struct timespec { long sec; long nsec; } ctx_timespec;
14450 int clock_gettime(clk_id_t timerid, struct timespec *t);
14451 ]]
14452 local target=ffi.new("ctx_timespec[?]",1)
14453 local clock=C.CLOCK_PROCESS_CPUTIME_ID
14454 ticks=function ()
14455 C.clock_gettime(clock,target)
14456 return tonumber(target[0].sec*1000000000+target[0].nsec)
14457 end
14458 seconds=function(ticks)
14459 return ticks/1000000000
14460 end
14461 initialize=false
14462 end
14463end
14464setmetatableindex(names,function(t,name)
14465 local v=setmetatableindex(function(t,source)
14466 local v=setmetatableindex(function(t,line)
14467 local v={ total=0,count=0,nesting=0 }
14468 t[line]=v
14469 return v
14470 end)
14471 t[source]=v
14472 return v
14473 end)
14474 t[name]=v
14475 return v
14476end)
14477local getinfo=nil
14478local sethook=nil
14479local function hook(where)
14480 local f=getinfo(2,"nSl")
14481 if f then
14482 local source=f.short_src
14483 if not source then
14484 return
14485 end
14486 local line=f.linedefined or 0
14487 local name=f.name
14488 if not name then
14489 local what=f.what
14490 if what=="C" then
14491 name="<anonymous>"
14492 else
14493 name=f.namewhat or what or "<unknown>"
14494 end
14495 end
14496 local data=names[name][source][line]
14497 if where=="call" then
14498 local nesting=data.nesting
14499 if nesting==0 then
14500 data.count=data.count+1
14501 insert(data,ticks())
14502 data.nesting=1
14503 else
14504 data.nesting=nesting+1
14505 end
14506 elseif where=="return" then
14507 local nesting=data.nesting
14508 if nesting==1 then
14509 local t=remove(data)
14510 if t then
14511 data.total=data.total+ticks()-t
14512 end
14513 data.nesting=0
14514 else
14515 data.nesting=nesting-1
14516 end
14517 end
14518 end
14519end
14520function debugger.showstats(printer,threshold)
14521 local printer=printer or report
14522 local calls=0
14523 local functions=0
14524 local dataset={}
14525 local length=0
14526 local realtime=0
14527 local totaltime=0
14528 local threshold=threshold or 0
14529 for name,sources in next,names do
14530 for source,lines in next,sources do
14531 for line,data in next,lines do
14532 local count=data.count
14533 if count>threshold then
14534 if #name>length then
14535 length=#name
14536 end
14537 local total=data.total
14538 local real=total
14539 if real>0 then
14540 real=total-(count*overhead/dummycalls)
14541 if real<0 then
14542 real=0
14543 end
14544 realtime=realtime+real
14545 end
14546 totaltime=totaltime+total
14547 if line<0 then
14548 line=0
14549 end
14550 dataset[#dataset+1]={ real,total,count,name,source,line }
14551 end
14552 end
14553 end
14554 end
14555 sort(dataset,function(a,b)
14556 if a[1]==b[1] then
14557 if a[2]==b[2] then
14558 if a[3]==b[3] then
14559 if a[4]==b[4] then
14560 if a[5]==b[5] then
14561 return a[6]<b[6]
14562 else
14563 return a[5]<b[5]
14564 end
14565 else
14566 return a[4]<b[4]
14567 end
14568 else
14569 return b[3]<a[3]
14570 end
14571 else
14572 return b[2]<a[2]
14573 end
14574 else
14575 return b[1]<a[1]
14576 end
14577 end)
14578 if length>50 then
14579 length=50
14580 end
14581 local fmt=string.formatters["%4.9k s %3.3k %% %4.9k s %3.3k %% %8i # %-"..length.."s %4i %s"]
14582 for i=1,#dataset do
14583 local data=dataset[i]
14584 local real=data[1]
14585 local total=data[2]
14586 local count=data[3]
14587 local name=data[4]
14588 local source=data[5]
14589 local line=data[6]
14590 calls=calls+count
14591 functions=functions+1
14592 name=gsub(name,"%s+"," ")
14593 if #name>length then
14594 name=sub(name,1,length)
14595 end
14596 printer(fmt(seconds(total),100*total/totaltime,seconds(real),100*real/realtime,count,name,line,source))
14597 end
14598 printer("")
14599 printer(format("functions : %i",functions))
14600 printer(format("calls : %i",calls))
14601 printer(format("overhead : %f",seconds(overhead/1000)))
14602end
14603local function getdebug()
14604 if sethook and getinfo then
14605 return
14606 end
14607 if not debug then
14608 local okay
14609 okay,debug=pcall(require,"debug")
14610 end
14611 if type(debug)~="table" then
14612 return
14613 end
14614 getinfo=debug.getinfo
14615 sethook=debug.sethook
14616 if type(getinfo)~="function" then
14617 getinfo=nil
14618 end
14619 if type(sethook)~="function" then
14620 sethook=nil
14621 end
14622end
14623function debugger.savestats(filename,threshold)
14624 local f=io.open(filename,'w')
14625 if f then
14626 debugger.showstats(function(str) f:write(str,"\n") end,threshold)
14627 f:close()
14628 end
14629end
14630function debugger.enable()
14631 getdebug()
14632 if sethook and getinfo and nesting==0 then
14633 running=true
14634 if initialize then
14635 initialize()
14636 end
14637 sethook(hook,"cr")
14638 local function dummy() end
14639 local t=ticks()
14640 for i=1,dummycalls do
14641 dummy()
14642 end
14643 overhead=ticks()-t
14644 end
14645 if nesting>0 then
14646 nesting=nesting+1
14647 end
14648end
14649function debugger.disable()
14650 if nesting>0 then
14651 nesting=nesting-1
14652 end
14653 if sethook and getinfo and nesting==0 then
14654 sethook()
14655 end
14656end
14657local function showtraceback(rep)
14658 getdebug()
14659 if getinfo then
14660 local level=2
14661 local reporter=rep or report
14662 while true do
14663 local info=getinfo(level,"Sl")
14664 if not info then
14665 break
14666 elseif info.what=="C" then
14667 reporter("%2i : %s",level-1,"C function")
14668 else
14669 reporter("%2i : %s : %s",level-1,info.short_src,info.currentline)
14670 end
14671 level=level+1
14672 end
14673 end
14674end
14675debugger.showtraceback=showtraceback
14676if luac then
14677 local show,dump=luac.print,string.dump
14678 function luac.inspect(v)
14679 if type(v)=="function" then
14680 local ok,str=xpcall(dump,function() end,v)
14681 if ok then
14682 v=str
14683 end
14684 end
14685 if type(v)=="string" then
14686 show(v,true)
14687 else
14688 print(v)
14689 end
14690 end
14691end
14692
14693
14694end
14695
14696do
14697
14698package.loaded["util-tpl"] = package.loaded["util-tpl"] or true
14699
14700
14701
14702if not modules then modules={} end modules ['util-tpl']={
14703 version=1.001,
14704 comment="companion to luat-lib.mkiv",
14705 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
14706 copyright="PRAGMA ADE / ConTeXt Development Team",
14707 license="see context related readme files"
14708}
14709utilities.templates=utilities.templates or {}
14710local templates=utilities.templates
14711local trace_template=false trackers.register("templates.trace",function(v) trace_template=v end)
14712local report_template=logs.reporter("template")
14713local tostring,next=tostring,next
14714local format,sub,byte=string.format,string.sub,string.byte
14715local P,C,R,Cs,Cc,Carg,lpegmatch,lpegpatterns=lpeg.P,lpeg.C,lpeg.R,lpeg.Cs,lpeg.Cc,lpeg.Carg,lpeg.match,lpeg.patterns
14716local formatters=string.formatters
14717local replacer
14718local function replacekey(k,t,how,recursive)
14719 local v=t[k]
14720 if not v then
14721 if trace_template then
14722 report_template("unknown key %a",k)
14723 end
14724 return ""
14725 else
14726 v=tostring(v)
14727 if trace_template then
14728 report_template("setting key %a to value %a",k,v)
14729 end
14730 if recursive then
14731 return lpegmatch(replacer,v,1,t,how,recursive)
14732 else
14733 return v
14734 end
14735 end
14736end
14737local sqlescape=lpeg.replacer {
14738 { "'","''" },
14739 { "\\","\\\\" },
14740 { "\r\n","\\n" },
14741 { "\r","\\n" },
14742}
14743local sqlquoted=Cs(Cc("'")*sqlescape*Cc("'"))
14744lpegpatterns.sqlescape=sqlescape
14745lpegpatterns.sqlquoted=sqlquoted
14746local luaescape=lpegpatterns.luaescape
14747local escapers={
14748 lua=function(s)
14749 return lpegmatch(luaescape,s)
14750 end,
14751 sql=function(s)
14752 return lpegmatch(sqlescape,s)
14753 end,
14754}
14755local quotedescapers={
14756 lua=function(s)
14757 return format("%q",s)
14758 end,
14759 sql=function(s)
14760 return lpegmatch(sqlquoted,s)
14761 end,
14762}
14763local luaescaper=escapers.lua
14764local quotedluaescaper=quotedescapers.lua
14765local function replacekeyunquoted(s,t,how,recurse)
14766 if how==false then
14767 return replacekey(s,t,how,recurse)
14768 else
14769 local escaper=how and escapers[how] or luaescaper
14770 return escaper(replacekey(s,t,how,recurse))
14771 end
14772end
14773local function replacekeyquoted(s,t,how,recurse)
14774 if how==false then
14775 return replacekey(s,t,how,recurse)
14776 else
14777 local escaper=how and quotedescapers[how] or quotedluaescaper
14778 return escaper(replacekey(s,t,how,recurse))
14779 end
14780end
14781local function replaceoptional(l,m,r,t,how,recurse)
14782 local v=t[l]
14783 return v and v~="" and lpegmatch(replacer,r,1,t,how or "lua",recurse or false) or ""
14784end
14785local function replaceformatted(l,m,r,t,how,recurse)
14786 local v=t[r]
14787 return v and formatters[l](v)
14788end
14789local single=P("%")
14790local double=P("%%")
14791local lquoted=P("%[")
14792local rquoted=P("]%")
14793local lquotedq=P("%(")
14794local rquotedq=P(")%")
14795local escape=double/'%%'
14796local nosingle=single/''
14797local nodouble=double/''
14798local nolquoted=lquoted/''
14799local norquoted=rquoted/''
14800local nolquotedq=lquotedq/''
14801local norquotedq=rquotedq/''
14802local nolformatted=P(":")/"%%"
14803local norformatted=P(":")/""
14804local noloptional=P("%?")/''
14805local noroptional=P("?%")/''
14806local nomoptional=P(":")/''
14807local args=Carg(1)*Carg(2)*Carg(3)
14808local key=nosingle*((C((1-nosingle)^1)*args)/replacekey)*nosingle
14809local quoted=nolquotedq*((C((1-norquotedq)^1)*args)/replacekeyquoted)*norquotedq
14810local unquoted=nolquoted*((C((1-norquoted)^1)*args)/replacekeyunquoted)*norquoted
14811local optional=noloptional*((C((1-nomoptional)^1)*nomoptional*C((1-noroptional)^1)*args)/replaceoptional)*noroptional
14812local formatted=nosingle*((Cs(nolformatted*(1-norformatted )^1)*norformatted*C((1-nosingle)^1)*args)/replaceformatted)*nosingle
14813local any=P(1)
14814 replacer=Cs((unquoted+quoted+formatted+escape+optional+key+any)^0)
14815local function replace(str,mapping,how,recurse)
14816 if mapping and str then
14817 return lpegmatch(replacer,str,1,mapping,how or "lua",recurse or false) or str
14818 else
14819 return str
14820 end
14821end
14822templates.replace=replace
14823function templates.replacer(str,how,recurse)
14824 return function(mapping)
14825 return lpegmatch(replacer,str,1,mapping,how or "lua",recurse or false) or str
14826 end
14827end
14828function templates.load(filename,mapping,how,recurse)
14829 local data=io.loaddata(filename) or ""
14830 if mapping and next(mapping) then
14831 return replace(data,mapping,how,recurse)
14832 else
14833 return data
14834 end
14835end
14836function templates.resolve(t,mapping,how,recurse)
14837 if not mapping then
14838 mapping=t
14839 end
14840 for k,v in next,t do
14841 t[k]=replace(v,mapping,how,recurse)
14842 end
14843 return t
14844end
14845
14846
14847end
14848
14849do
14850
14851package.loaded["util-sbx"] = package.loaded["util-sbx"] or true
14852
14853
14854
14855if not modules then modules={} end modules ['util-sbx']={
14856 version=1.001,
14857 comment="companion to luat-lib.mkiv",
14858 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
14859 copyright="PRAGMA ADE / ConTeXt Development Team",
14860 license="see context related readme files"
14861}
14862if not sandbox then require("l-sandbox") end
14863local next,type=next,type
14864local replace=utilities.templates.replace
14865local collapsepath=file.collapsepath
14866local expandname=dir.expandname
14867local sortedhash=table.sortedhash
14868local lpegmatch=lpeg.match
14869local platform=os.type
14870local P,S,C=lpeg.P,lpeg.S,lpeg.C
14871local gsub=string.gsub
14872local lower=string.lower
14873local find=string.find
14874local concat=string.concat
14875local unquoted=string.unquoted
14876local optionalquoted=string.optionalquoted
14877local basename=file.basename
14878local nameonly=file.nameonly
14879local sandbox=sandbox
14880local validroots={}
14881local validrunners={}
14882local validbinaries=true
14883local validlibraries=true
14884local validators={}
14885local finalized=nil
14886local trace=false
14887local p_validroot=nil
14888local p_split=lpeg.firstofsplit(" ")
14889local report=logs.reporter("sandbox")
14890trackers.register("sandbox",function(v) trace=v end)
14891sandbox.setreporter(report)
14892sandbox.finalizer {
14893 category="files",
14894 action=function()
14895 finalized=true
14896 end
14897}
14898local function registerroot(root,what)
14899 if finalized then
14900 report("roots are already finalized")
14901 else
14902 if type(root)=="table" then
14903 root,what=root[1],root[2]
14904 end
14905 if type(root)=="string" and root~="" then
14906 root=collapsepath(expandname(root))
14907 if what=="r" or what=="ro" or what=="readable" then
14908 what="read"
14909 elseif what=="w" or what=="wo" or what=="writable" then
14910 what="write"
14911 end
14912 validroots[root]=what=="write" or false
14913 end
14914 end
14915end
14916sandbox.finalizer {
14917 category="files",
14918 action=function()
14919 if p_validroot then
14920 report("roots are already initialized")
14921 else
14922 sandbox.registerroot(".","write")
14923 for name in sortedhash(validroots) do
14924 if p_validroot then
14925 p_validroot=P(name)+p_validroot
14926 else
14927 p_validroot=P(name)
14928 end
14929 end
14930 p_validroot=p_validroot/validroots
14931 end
14932 end
14933}
14934local function registerbinary(name)
14935 if finalized then
14936 report("binaries are already finalized")
14937 elseif type(name)=="string" and name~="" then
14938 if not validbinaries then
14939 return
14940 end
14941 if validbinaries==true then
14942 validbinaries={ [name]=true }
14943 else
14944 validbinaries[name]=true
14945 end
14946 elseif name==true then
14947 validbinaries={}
14948 end
14949end
14950local function registerlibrary(name)
14951 if finalized then
14952 report("libraries are already finalized")
14953 elseif type(name)=="string" and name~="" then
14954 if not validlibraries then
14955 return
14956 end
14957 if validlibraries==true then
14958 validlibraries={ [nameonly(name)]=true }
14959 else
14960 validlibraries[nameonly(name)]=true
14961 end
14962 elseif name==true then
14963 validlibraries={}
14964 end
14965end
14966local p_write=S("wa") p_write=(1-p_write)^0*p_write
14967local p_path=S("\\/~$%:") p_path=(1-p_path )^0*p_path
14968local function normalized(name)
14969 if platform=="windows" then
14970 name=gsub(name,"/","\\")
14971 end
14972 return name
14973end
14974function sandbox.possiblepath(name)
14975 return lpegmatch(p_path,name) and true or false
14976end
14977local filenamelogger=false
14978function sandbox.setfilenamelogger(l)
14979 filenamelogger=type(l)=="function" and l or false
14980end
14981local function validfilename(name,what)
14982 if p_validroot and type(name)=="string" and lpegmatch(p_path,name) then
14983 local asked=collapsepath(expandname(name))
14984 local okay=lpegmatch(p_validroot,asked)
14985 if okay==true then
14986 if filenamelogger then
14987 filenamelogger(name,"w",asked,true)
14988 end
14989 return name
14990 elseif okay==false then
14991 if not what then
14992 if filenamelogger then
14993 filenamelogger(name,"r",asked,true)
14994 end
14995 return name
14996 elseif lpegmatch(p_write,what) then
14997 if filenamelogger then
14998 filenamelogger(name,"w",asked,false)
14999 end
15000 return
15001 else
15002 if filenamelogger then
15003 filenamelogger(name,"r",asked,true)
15004 end
15005 return name
15006 end
15007 elseif filenamelogger then
15008 filenamelogger(name,"*",name,false)
15009 end
15010 else
15011 return name
15012 end
15013end
15014local function readable(name,finalized)
15015 return validfilename(name,"r")
15016end
15017local function normalizedreadable(name,finalized)
15018 local valid=validfilename(name,"r")
15019 if valid then
15020 return normalized(valid)
15021 end
15022end
15023local function writeable(name,finalized)
15024 return validfilename(name,"w")
15025end
15026local function normalizedwriteable(name,finalized)
15027 local valid=validfilename(name,"w")
15028 if valid then
15029 return normalized(valid)
15030 end
15031end
15032validators.readable=readable
15033validators.writeable=normalizedwriteable
15034validators.normalizedreadable=normalizedreadable
15035validators.normalizedwriteable=writeable
15036validators.filename=readable
15037table.setmetatableindex(validators,function(t,k)
15038 if k then
15039 t[k]=readable
15040 end
15041 return readable
15042end)
15043function validators.string(s,finalized)
15044 if finalized and suspicious(s) then
15045 return ""
15046 else
15047 return s
15048 end
15049end
15050function validators.cache(s)
15051 if finalized then
15052 return basename(s)
15053 else
15054 return s
15055 end
15056end
15057function validators.url(s)
15058 if finalized and find("^file:") then
15059 return ""
15060 else
15061 return s
15062 end
15063end
15064local function filehandlerone(action,one,...)
15065 local checkedone=validfilename(one)
15066 if checkedone then
15067 return action(one,...)
15068 else
15069 end
15070end
15071local function filehandlertwo(action,one,two,...)
15072 local checkedone=validfilename(one)
15073 if checkedone then
15074 local checkedtwo=validfilename(two)
15075 if checkedtwo then
15076 return action(one,two,...)
15077 else
15078 end
15079 else
15080 end
15081end
15082local function iohandler(action,one,...)
15083 if type(one)=="string" then
15084 local checkedone=validfilename(one)
15085 if checkedone then
15086 return action(one,...)
15087 end
15088 elseif one then
15089 return action(one,...)
15090 else
15091 return action()
15092 end
15093end
15094local osexecute=sandbox.original(os.execute)
15095local iopopen=sandbox.original(io.popen)
15096local reported={}
15097local function validcommand(name,program,template,checkers,defaults,variables,reporter,strict)
15098 if validbinaries~=false and (validbinaries==true or validbinaries[program]) then
15099 local binpath=nil
15100 if variables then
15101 for variable,value in next,variables do
15102 local chktype=checkers[variable]
15103 if chktype=="verbose" then
15104 else
15105 local checker=validators[chktype]
15106 if checker and type(value)=="string" then
15107 value=checker(unquoted(value),strict)
15108 if value then
15109 variables[variable]=optionalquoted(value)
15110 else
15111 report("variable %a with value %a fails the check",variable,value)
15112 return
15113 end
15114 else
15115 report("variable %a has no checker",variable)
15116 return
15117 end
15118 end
15119 end
15120 for variable,default in next,defaults do
15121 local value=variables[variable]
15122 if not value or value=="" then
15123 local chktype=checkers[variable]
15124 if chktype=="verbose" then
15125 elseif type(default)=="string" then
15126 local checker=validators[chktype]
15127 if checker then
15128 default=checker(unquoted(default),strict)
15129 if default then
15130 variables[variable]=optionalquoted(default)
15131 else
15132 report("variable %a with default %a fails the check",variable,default)
15133 return
15134 end
15135 end
15136 end
15137 end
15138 end
15139 binpath=variables.binarypath
15140 end
15141 if type(binpath)=="string" and binpath~="" then
15142 program=binpath.."/"..program
15143 end
15144 local command=program.." "..replace(template,variables)
15145 if reporter then
15146 reporter("executing runner %a: %s",name,command)
15147 elseif trace then
15148 report("executing runner %a: %s",name,command)
15149 end
15150 return command
15151 elseif not reported[name] then
15152 report("executing program %a of runner %a is not permitted",program,name)
15153 reported[name]=true
15154 end
15155end
15156local runners={
15157 resultof=function(...)
15158 local command=validcommand(...)
15159 if command then
15160 if trace then
15161 report("resultof: %s",command)
15162 end
15163 local handle=iopopen(command,"rb")
15164 if handle then
15165 local result=handle:read("*all") or ""
15166 handle:close()
15167 return result
15168 end
15169 end
15170 end,
15171 execute=function(...)
15172 local command=validcommand(...)
15173 if command then
15174 if trace then
15175 report("execute: %s",command)
15176 end
15177 local okay=osexecute(command)
15178 return okay
15179 end
15180 end,
15181 pipeto=function(...)
15182 local command=validcommand(...)
15183 if command then
15184 if trace then
15185 report("pipeto: %s",command)
15186 end
15187 return iopopen(command,"w")
15188 end
15189 end,
15190}
15191function sandbox.registerrunner(specification)
15192 if type(specification)=="string" then
15193 local wrapped=validrunners[specification]
15194 inspect(table.sortedkeys(validrunners))
15195 if wrapped then
15196 return wrapped
15197 else
15198 report("unknown predefined runner %a",specification)
15199 return
15200 end
15201 end
15202 if type(specification)~="table" then
15203 report("specification should be a table (or string)")
15204 return
15205 end
15206 local name=specification.name
15207 if type(name)~="string" then
15208 report("invalid name, string expected",name)
15209 return
15210 end
15211 if validrunners[name] then
15212 report("invalid name, runner %a already defined",name)
15213 return
15214 end
15215 local program=specification.program
15216 if type(program)=="string" then
15217 elseif type(program)=="table" then
15218 program=program[platform] or program.default or program.unix
15219 end
15220 if type(program)~="string" or program=="" then
15221 report("invalid runner %a specified for platform %a",name,platform)
15222 return
15223 end
15224 local template=specification.template
15225 if not template then
15226 report("missing template for runner %a",name)
15227 return
15228 end
15229 local method=specification.method or "execute"
15230 local checkers=specification.checkers or {}
15231 local defaults=specification.defaults or {}
15232 local runner=runners[method]
15233 if runner then
15234 local finalized=finalized
15235 local wrapped=function(variables)
15236 return runner(name,program,template,checkers,defaults,variables,specification.reporter,finalized)
15237 end
15238 validrunners[name]=wrapped
15239 return wrapped
15240 else
15241 validrunners[name]=nil
15242 report("invalid method for runner %a",name)
15243 end
15244end
15245function sandbox.getrunner(name)
15246 return name and validrunners[name]
15247end
15248local function suspicious(str)
15249 return (find(str,"[/\\]") or find(command,"..",1,true)) and true or false
15250end
15251local function binaryrunner(action,command,...)
15252 if validbinaries==false then
15253 report("no binaries permitted, ignoring command: %s",command)
15254 return
15255 end
15256 if type(command)~="string" then
15257 report("command should be a string")
15258 return
15259 end
15260 local program=lpegmatch(p_split,command)
15261 if not program or program=="" then
15262 report("unable to filter binary from command: %s",command)
15263 return
15264 end
15265 if validbinaries==true then
15266 elseif not validbinaries[program] then
15267 report("binary not permitted, ignoring command: %s",command)
15268 return
15269 elseif suspicious(command) then
15270 report("/ \\ or .. found, ignoring command (use sandbox.registerrunner): %s",command)
15271 return
15272 end
15273 return action(command,...)
15274end
15275local function dummyrunner(action,command,...)
15276 if type(command)=="table" then
15277 command=concat(command," ",command[0] and 0 or 1)
15278 end
15279 report("ignoring command: %s",command)
15280end
15281sandbox.filehandlerone=filehandlerone
15282sandbox.filehandlertwo=filehandlertwo
15283sandbox.iohandler=iohandler
15284function sandbox.disablerunners()
15285 validbinaries=false
15286end
15287function sandbox.disablelibraries()
15288 validlibraries=false
15289end
15290if FFISUPPORTED and ffi then
15291 function sandbox.disablelibraries()
15292 validlibraries=false
15293 for k,v in next,ffi do
15294 if k~="gc" then
15295 ffi[k]=nil
15296 end
15297 end
15298 end
15299 local fiiload=ffi.load
15300 if fiiload then
15301 local reported={}
15302 function ffi.load(name,...)
15303 if validlibraries==false then
15304 elseif validlibraries==true then
15305 return fiiload(name,...)
15306 elseif validlibraries[nameonly(name)] then
15307 return fiiload(name,...)
15308 else
15309 end
15310 if not reported[name] then
15311 report("using library %a is not permitted",name)
15312 reported[name]=true
15313 end
15314 return nil
15315 end
15316 end
15317end
15318local overload=sandbox.overload
15319local register=sandbox.register
15320 overload(loadfile,filehandlerone,"loadfile")
15321if io then
15322 overload(io.open,filehandlerone,"io.open")
15323 overload(io.popen,binaryrunner,"io.popen")
15324 overload(io.input,iohandler,"io.input")
15325 overload(io.output,iohandler,"io.output")
15326 overload(io.lines,filehandlerone,"io.lines")
15327end
15328if os then
15329 overload(os.execute,binaryrunner,"os.execute")
15330 overload(os.spawn,dummyrunner,"os.spawn")
15331 overload(os.exec,dummyrunner,"os.exec")
15332 overload(os.resultof,binaryrunner,"os.resultof")
15333 overload(os.pipeto,binaryrunner,"os.pipeto")
15334 overload(os.rename,filehandlertwo,"os.rename")
15335 overload(os.remove,filehandlerone,"os.remove")
15336end
15337if lfs then
15338 overload(lfs.chdir,filehandlerone,"lfs.chdir")
15339 overload(lfs.mkdir,filehandlerone,"lfs.mkdir")
15340 overload(lfs.rmdir,filehandlerone,"lfs.rmdir")
15341 overload(lfs.isfile,filehandlerone,"lfs.isfile")
15342 overload(lfs.isdir,filehandlerone,"lfs.isdir")
15343 overload(lfs.attributes,filehandlerone,"lfs.attributes")
15344 overload(lfs.dir,filehandlerone,"lfs.dir")
15345 overload(lfs.lock_dir,filehandlerone,"lfs.lock_dir")
15346 overload(lfs.touch,filehandlerone,"lfs.touch")
15347 overload(lfs.link,filehandlertwo,"lfs.link")
15348 overload(lfs.setmode,filehandlerone,"lfs.setmode")
15349 overload(lfs.readlink,filehandlerone,"lfs.readlink")
15350 overload(lfs.shortname,filehandlerone,"lfs.shortname")
15351 overload(lfs.symlinkattributes,filehandlerone,"lfs.symlinkattributes")
15352end
15353if zip then
15354 zip.open=register(zip.open,filehandlerone,"zip.open")
15355end
15356sandbox.registerroot=registerroot
15357sandbox.registerbinary=registerbinary
15358sandbox.registerlibrary=registerlibrary
15359sandbox.validfilename=validfilename
15360
15361
15362end
15363
15364do
15365
15366package.loaded["util-mrg"] = package.loaded["util-mrg"] or true
15367
15368
15369
15370if not modules then modules={} end modules ['util-mrg']={
15371 version=1.001,
15372 comment="companion to luat-lib.mkiv",
15373 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
15374 copyright="PRAGMA ADE / ConTeXt Development Team",
15375 license="see context related readme files"
15376}
15377local gsub,format=string.gsub,string.format
15378local concat=table.concat
15379local type,next=type,next
15380local P,R,S,V,Ct,C,Cs,Cc,Cp,Cmt,Cb,Cg=lpeg.P,lpeg.R,lpeg.S,lpeg.V,lpeg.Ct,lpeg.C,lpeg.Cs,lpeg.Cc,lpeg.Cp,lpeg.Cmt,lpeg.Cb,lpeg.Cg
15381local lpegmatch,patterns=lpeg.match,lpeg.patterns
15382utilities=utilities or {}
15383local merger=utilities.merger or {}
15384utilities.merger=merger
15385merger.strip_comment=true
15386local report=logs.reporter("system","merge")
15387utilities.report=report
15388local m_begin_merge="begin library merge"
15389local m_end_merge="end library merge"
15390local m_begin_closure="do -- create closure to overcome 200 locals limit"
15391local m_end_closure="end -- of closure"
15392local m_pattern="%c+".."%-%-%s+"..m_begin_merge.."%c+(.-)%c+".."%-%-%s+"..m_end_merge.."%c+"
15393local m_format="\n\n-- "..m_begin_merge.."\n%s\n".."-- "..m_end_merge.."\n\n"
15394local m_faked="-- ".."created merged file".."\n\n".."-- "..m_begin_merge.."\n\n".."-- "..m_end_merge.."\n\n"
15395local m_report=[[
15396-- used libraries : %s
15397-- skipped libraries : %s
15398-- original bytes : %s
15399-- stripped bytes : %s
15400]]
15401local m_preloaded=[[package.loaded[%q] = package.loaded[%q] or true]]
15402local function self_fake()
15403 return m_faked
15404end
15405local function self_nothing()
15406 return ""
15407end
15408local function self_load(name)
15409 local data=io.loaddata(name) or ""
15410 if data=="" then
15411 report("unknown file %a",name)
15412 else
15413 report("inserting file %a",name)
15414 end
15415 return data or ""
15416end
15417local space=patterns.space
15418local eol=patterns.newline
15419local equals=P("=")^0
15420local open=P("[")*Cg(equals,"init")*P("[")*P("\n")^-1
15421local close=P("]")*C(equals)*P("]")
15422local closeeq=Cmt(close*Cb("init"),function(s,i,a,b) return a==b end)
15423local longstring=open*(1-closeeq)^0*close
15424local quoted=patterns.quoted
15425local digit=patterns.digit
15426local emptyline=space^0*eol
15427local operator1=P("<=")+P(">=")+P("~=")+P("..")+S("/^<>=*+%%")
15428local operator2=S("*+/")
15429local operator3=S("-")
15430local operator4=P("..")
15431local separator=S(",;")
15432local ignore=(P("]")*space^1*P("=")*space^1*P("]"))/"]=["+(P("=")*space^1*P("{"))/"={"+(P("(")*space^1)/"("+(P("{")*(space+eol)^1*P("}"))/"{}"
15433local strings=quoted
15434local longcmt=(emptyline^0*P("--")*longstring*emptyline^0)/""
15435local longstr=longstring
15436local comment=emptyline^0*P("--")*P("-")^0*(1-eol)^0*emptyline^1/"\n"
15437local optionalspaces=space^0/""
15438local mandatespaces=space^1/""
15439local optionalspacing=(eol+space)^0/""
15440local mandatespacing=(eol+space)^1/""
15441local pack=digit*space^1*operator4*optionalspacing+optionalspacing*operator1*optionalspacing+optionalspacing*operator2*optionalspaces+mandatespacing*operator3*mandatespaces+optionalspaces*separator*optionalspaces
15442local lines=emptyline^2/"\n"
15443local spaces=(space*space)/" "
15444local spaces=(space*space*space*space)/" "
15445local compact=Cs ((
15446 ignore+strings+longcmt+longstr+comment+pack+lines+spaces+1
15447)^1 )
15448local strip=Cs((emptyline^2/"\n"+1)^0)
15449local stripreturn=Cs((1-P("return")*space^1*P(1-space-eol)^1*(space+eol)^0*P(-1))^1)
15450function merger.compact(data)
15451 return lpegmatch(strip,lpegmatch(compact,data))
15452end
15453local function self_compact(data)
15454 local delta=0
15455 if merger.strip_comment then
15456 local before=#data
15457 data=lpegmatch(compact,data)
15458 data=lpegmatch(strip,data)
15459 local after=#data
15460 delta=before-after
15461 report("original size %s, compacted to %s, stripped %s",before,after,delta)
15462 data=format("-- original size: %s, stripped down to: %s\n\n%s",before,after,data)
15463 end
15464 return lpegmatch(stripreturn,data) or data,delta
15465end
15466local function self_save(name,data)
15467 if data~="" then
15468 io.savedata(name,data)
15469 report("saving %s with size %s",name,#data)
15470 end
15471end
15472local function self_swap(data,code)
15473 return data~="" and (gsub(data,m_pattern,function() return format(m_format,code) end,1)) or ""
15474end
15475local function self_libs(libs,list)
15476 local result,f,frozen,foundpath={},nil,false,nil
15477 result[#result+1]="\n"
15478 if type(libs)=='string' then libs={ libs } end
15479 if type(list)=='string' then list={ list } end
15480 for i=1,#libs do
15481 local lib=libs[i]
15482 for j=1,#list do
15483 local pth=gsub(list[j],"\\","/")
15484 report("checking library path %a",pth)
15485 local name=pth.."/"..lib
15486 if lfs.isfile(name) then
15487 foundpath=pth
15488 end
15489 end
15490 if foundpath then break end
15491 end
15492 if foundpath then
15493 report("using library path %a",foundpath)
15494 local right,wrong,original,stripped={},{},0,0
15495 for i=1,#libs do
15496 local lib=libs[i]
15497 local fullname=foundpath.."/"..lib
15498 if lfs.isfile(fullname) then
15499 report("using library %a",fullname)
15500 local preloaded=file.nameonly(lib)
15501 local data=io.loaddata(fullname,true)
15502 original=original+#data
15503 local data,delta=self_compact(data)
15504 right[#right+1]=lib
15505 result[#result+1]=m_begin_closure
15506 result[#result+1]=format(m_preloaded,preloaded,preloaded)
15507 result[#result+1]=data
15508 result[#result+1]=m_end_closure
15509 stripped=stripped+delta
15510 else
15511 report("skipping library %a",fullname)
15512 wrong[#wrong+1]=lib
15513 end
15514 end
15515 right=#right>0 and concat(right," ") or "-"
15516 wrong=#wrong>0 and concat(wrong," ") or "-"
15517 report("used libraries: %a",right)
15518 report("skipped libraries: %a",wrong)
15519 report("original bytes: %a",original)
15520 report("stripped bytes: %a",stripped)
15521 result[#result+1]=format(m_report,right,wrong,original,stripped)
15522 else
15523 report("no valid library path found")
15524 end
15525 return concat(result,"\n\n")
15526end
15527function merger.selfcreate(libs,list,target)
15528 if target then
15529 self_save(target,self_swap(self_fake(),self_libs(libs,list)))
15530 end
15531end
15532function merger.selfmerge(name,libs,list,target)
15533 self_save(target or name,self_swap(self_load(name),self_libs(libs,list)))
15534end
15535function merger.selfclean(name)
15536 self_save(name,self_swap(self_load(name),self_nothing()))
15537end
15538
15539
15540end
15541
15542do
15543
15544package.loaded["util-env"] = package.loaded["util-env"] or true
15545
15546
15547
15548if not modules then modules={} end modules ['util-env']={
15549 version=1.001,
15550 comment="companion to luat-lib.mkiv",
15551 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
15552 copyright="PRAGMA ADE / ConTeXt Development Team",
15553 license="see context related readme files"
15554}
15555local allocate,mark=utilities.storage.allocate,utilities.storage.mark
15556local format,sub,match,gsub,find=string.format,string.sub,string.match,string.gsub,string.find
15557local unquoted,quoted,optionalquoted=string.unquoted,string.quoted,string.optionalquoted
15558local concat,insert,remove=table.concat,table.insert,table.remove
15559environment=environment or {}
15560local environment=environment
15561local setlocale=os.setlocale
15562setlocale(nil,nil)
15563local report=logs.reporter("system")
15564function os.setlocale(a,b)
15565 if a or b then
15566 if report then
15567 report()
15568 report("You're messing with os.locale in a supposedly locale neutral enviroment. From")
15569 report("now on are on your own and without support. Crashes or unexpected side effects")
15570 report("can happen but don't bother the luatex and context developer team with it.")
15571 report()
15572 report=nil
15573 end
15574 setlocale(a,b)
15575 end
15576end
15577local validengines=allocate {
15578 ["luatex"]=true,
15579 ["luajittex"]=true,
15580}
15581local basicengines=allocate {
15582 ["luatex"]="luatex",
15583 ["texlua"]="luatex",
15584 ["texluac"]="luatex",
15585 ["luajittex"]="luajittex",
15586 ["texluajit"]="luajittex",
15587}
15588local luaengines=allocate {
15589 ["lua"]=true,
15590 ["luajit"]=true,
15591}
15592environment.validengines=validengines
15593environment.basicengines=basicengines
15594if not arg then
15595 environment.used_as_library=true
15596elseif luaengines[file.removesuffix(arg[-1])] then
15597elseif validengines[file.removesuffix(arg[0])] then
15598 if arg[1]=="--luaonly" then
15599 arg[-1]=arg[0]
15600 arg[ 0]=arg[2]
15601 for k=3,#arg do
15602 arg[k-2]=arg[k]
15603 end
15604 remove(arg)
15605 remove(arg)
15606 else
15607 end
15608 local originalzero=file.basename(arg[0])
15609 local specialmapping={ luatools=="base" }
15610 if originalzero~="mtxrun" and originalzero~="mtxrun.lua" then
15611 arg[0]=specialmapping[originalzero] or originalzero
15612 insert(arg,0,"--script")
15613 insert(arg,0,"mtxrun")
15614 end
15615end
15616environment.arguments=allocate()
15617environment.files=allocate()
15618environment.sortedflags=nil
15619function environment.initializearguments(arg)
15620 local arguments={}
15621 local files={}
15622 environment.arguments=arguments
15623 environment.files=files
15624 environment.sortedflags=nil
15625 for index=1,#arg do
15626 local argument=arg[index]
15627 if index>0 then
15628 local flag,value=match(argument,"^%-+(.-)=(.-)$")
15629 if flag then
15630 flag=gsub(flag,"^c:","")
15631 arguments[flag]=unquoted(value or "")
15632 else
15633 flag=match(argument,"^%-+(.+)")
15634 if flag then
15635 flag=gsub(flag,"^c:","")
15636 arguments[flag]=true
15637 else
15638 files[#files+1]=argument
15639 end
15640 end
15641 end
15642 end
15643 if not environment.ownname then
15644 if os.selfpath and os.selfname then
15645 environment.ownname=file.addsuffix(file.join(os.selfpath,os.selfname),"lua")
15646 end
15647 end
15648 environment.ownname=file.reslash(environment.ownname or arg[0] or 'unknown.lua')
15649end
15650function environment.setargument(name,value)
15651 environment.arguments[name]=value
15652end
15653function environment.getargument(name,partial)
15654 local arguments=environment.arguments
15655 local sortedflags=environment.sortedflags
15656 if arguments[name] then
15657 return arguments[name]
15658 elseif partial then
15659 if not sortedflags then
15660 sortedflags=allocate(table.sortedkeys(arguments))
15661 for k=1,#sortedflags do
15662 sortedflags[k]="^"..sortedflags[k]
15663 end
15664 environment.sortedflags=sortedflags
15665 end
15666 for k=1,#sortedflags do
15667 local v=sortedflags[k]
15668 if find(name,v) then
15669 return arguments[sub(v,2,#v)]
15670 end
15671 end
15672 end
15673 return nil
15674end
15675environment.argument=environment.getargument
15676function environment.splitarguments(separator)
15677 local done,before,after=false,{},{}
15678 local originalarguments=environment.originalarguments
15679 for k=1,#originalarguments do
15680 local v=originalarguments[k]
15681 if not done and v==separator then
15682 done=true
15683 elseif done then
15684 after[#after+1]=v
15685 else
15686 before[#before+1]=v
15687 end
15688 end
15689 return before,after
15690end
15691function environment.reconstructcommandline(arg,noquote)
15692 local resolveprefix=resolvers.resolve
15693 arg=arg or environment.originalarguments
15694 if noquote and #arg==1 then
15695 return unquoted(resolveprefix and resolveprefix(arg[1]) or arg[1])
15696 elseif #arg>0 then
15697 local result={}
15698 for i=1,#arg do
15699 result[i]=optionalquoted(resolveprefix and resolveprefix(arg[i]) or resolveprefix)
15700 end
15701 return concat(result," ")
15702 else
15703 return ""
15704 end
15705end
15706function environment.relativepath(path,root)
15707 if not path then
15708 path=""
15709 end
15710 if not file.is_rootbased_path(path) then
15711 if not root then
15712 root=file.pathpart(environment.ownscript or environment.ownname or ".")
15713 end
15714 if root=="" then
15715 root="."
15716 end
15717 path=root.."/"..path
15718 end
15719 return file.collapsepath(path,true)
15720end
15721if arg then
15722 local newarg,instring={},false
15723 for index=1,#arg do
15724 local argument=arg[index]
15725 if find(argument,"^\"") then
15726 if find(argument,"\"$") then
15727 newarg[#newarg+1]=gsub(argument,"^\"(.-)\"$","%1")
15728 instring=false
15729 else
15730 newarg[#newarg+1]=gsub(argument,"^\"","")
15731 instring=true
15732 end
15733 elseif find(argument,"\"$") then
15734 if instring then
15735 newarg[#newarg]=newarg[#newarg].." "..gsub(argument,"\"$","")
15736 instring=false
15737 else
15738 newarg[#newarg+1]=argument
15739 end
15740 elseif instring then
15741 newarg[#newarg]=newarg[#newarg].." "..argument
15742 else
15743 newarg[#newarg+1]=argument
15744 end
15745 end
15746 for i=1,-5,-1 do
15747 newarg[i]=arg[i]
15748 end
15749 environment.initializearguments(newarg)
15750 environment.originalarguments=mark(newarg)
15751 environment.rawarguments=mark(arg)
15752 arg={}
15753end
15754
15755
15756end
15757
15758do
15759
15760package.loaded["luat-env"] = package.loaded["luat-env"] or true
15761
15762
15763
15764 if not modules then modules={} end modules ['luat-env']={
15765 version=1.001,
15766 comment="companion to luat-lib.mkiv",
15767 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
15768 copyright="PRAGMA ADE / ConTeXt Development Team",
15769 license="see context related readme files"
15770}
15771local rawset,loadfile=rawset,loadfile
15772local gsub=string.gsub
15773local trace_locating=false trackers.register("resolvers.locating",function(v) trace_locating=v end)
15774local report_lua=logs.reporter("resolvers","lua")
15775local luautilities=utilities.lua
15776local luasuffixes=luautilities.suffixes
15777local texgettoks=tex and tex.gettoks
15778environment=environment or {}
15779local environment=environment
15780local mt={
15781 __index=function(_,k)
15782 if k=="version" then
15783 local version=texgettoks and texgettoks("contextversiontoks")
15784 if version and version~="" then
15785 rawset(environment,"version",version)
15786 return version
15787 else
15788 return "unknown"
15789 end
15790 elseif k=="jobname" or k=="formatname" then
15791 local name=tex and tex[k]
15792 if name or name=="" then
15793 rawset(environment,k,name)
15794 return name
15795 else
15796 return "unknown"
15797 end
15798 elseif k=="outputfilename" then
15799 local name=environment.jobname
15800 rawset(environment,k,name)
15801 return name
15802 end
15803 end
15804}
15805setmetatable(environment,mt)
15806function environment.texfile(filename)
15807 return resolvers.findfile(filename,'tex')
15808end
15809function environment.luafile(filename)
15810 if CONTEXTLMTXMODE and CONTEXTLMTXMODE>0 and file.suffix(filename)=="lua" then
15811 local resolved=resolvers.findfile(file.replacesuffix(filename,"lmt")) or ""
15812 if resolved~="" then
15813 return resolved
15814 end
15815 end
15816 local resolved=resolvers.findfile(filename,'tex') or ""
15817 if resolved~="" then
15818 return resolved
15819 end
15820 resolved=resolvers.findfile(filename,'texmfscripts') or ""
15821 if resolved~="" then
15822 return resolved
15823 end
15824 return resolvers.findfile(filename,'luatexlibs') or ""
15825end
15826local stripindeed=false directives.register("system.compile.strip",function(v) stripindeed=v end)
15827local function strippable(filename)
15828 if stripindeed then
15829 local modu=modules[file.nameonly(filename)]
15830 return modu and modu.dataonly
15831 else
15832 return false
15833 end
15834end
15835function environment.luafilechunk(filename,silent,macros,optional)
15836 filename=file.replacesuffix(filename,"lua")
15837 local fullname=environment.luafile(filename)
15838 if fullname and fullname~="" then
15839 local data=luautilities.loadedluacode(fullname,strippable,filename,macros)
15840 if not silent then
15841 report_lua("loading file %a %s",fullname,not data and "failed" or "succeeded")
15842 end
15843 return data
15844 else
15845 if not optional and not silent then
15846 report_lua("unknown file %a",filename)
15847 end
15848 return nil
15849 end
15850end
15851function environment.loadluafile(filename,version)
15852 local lucname,luaname,chunk
15853 local basename=file.removesuffix(filename)
15854 if basename==filename then
15855 luaname=file.addsuffix(basename,luasuffixes.lua)
15856 lucname=file.addsuffix(basename,luasuffixes.luc)
15857 else
15858 luaname=filename
15859 lucname=nil
15860 end
15861 local fullname=(lucname and environment.luafile(lucname)) or ""
15862 if fullname~="" then
15863 if trace_locating then
15864 report_lua("loading %a",fullname)
15865 end
15866 chunk=loadfile(fullname)
15867 end
15868 if chunk then
15869 chunk()
15870 if version then
15871 local v=version
15872 if modules and modules[filename] then
15873 v=modules[filename].version
15874 elseif versions and versions[filename] then
15875 v=versions[filename]
15876 end
15877 if v==version then
15878 return true
15879 else
15880 if trace_locating then
15881 report_lua("version mismatch for %a, lua version %a, luc version %a",filename,v,version)
15882 end
15883 environment.loadluafile(filename)
15884 end
15885 else
15886 return true
15887 end
15888 end
15889 fullname=(luaname and environment.luafile(luaname)) or ""
15890 if fullname~="" then
15891 if trace_locating then
15892 report_lua("loading %a",fullname)
15893 end
15894 chunk=loadfile(fullname)
15895 if not chunk then
15896 if trace_locating then
15897 report_lua("unknown file %a",filename)
15898 end
15899 else
15900 chunk()
15901 return true
15902 end
15903 end
15904 return false
15905end
15906environment.filenames=setmetatable({},{
15907 __index=function(t,k)
15908 local v=environment.files[k]
15909 if v then
15910 return (gsub(v,"%.+$",""))
15911 end
15912 end,
15913 __newindex=function(t,k)
15914 end,
15915 __len=function(t)
15916 return #environment.files
15917 end,
15918} )
15919
15920
15921end
15922
15923do
15924
15925package.loaded["util-zip"] = package.loaded["util-zip"] or true
15926
15927
15928
15929if not modules then modules={} end modules ['util-zip']={
15930 version=1.001,
15931 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
15932 copyright="PRAGMA ADE / ConTeXt Development Team",
15933 license="see context related readme files"
15934}
15935local type,tostring,tonumber=type,tostring,tonumber
15936local sort,concat=table.sort,table.concat
15937local find,format,sub,gsub=string.find,string.format,string.sub,string.gsub
15938local osdate,ostime,osclock=os.date,os.time,os.clock
15939local ioopen=io.open
15940local loaddata,savedata=io.loaddata,io.savedata
15941local filejoin,isdir,dirname,mkdirs=file.join,lfs.isdir,file.dirname,dir.mkdirs
15942local suffix,suffixes=file.suffix,file.suffixes
15943local openfile=io.open
15944gzip=gzip or {}
15945if not zlib then
15946 zlib=xzip
15947elseif not xzip then
15948 xzip=zlib
15949end
15950local files=utilities.files
15951local openfile=files.open
15952local closefile=files.close
15953local getsize=files.size
15954local readstring=files.readstring
15955local readcardinal2=files.readcardinal2le
15956local readcardinal4=files.readcardinal4le
15957local setposition=files.setposition
15958local getposition=files.getposition
15959local skipbytes=files.skip
15960local band=bit32.band
15961local rshift=bit32.rshift
15962local lshift=bit32.lshift
15963local zlibdecompress=zlib.decompress
15964local zlibdecompresssize=zlib.decompresssize
15965local zlibchecksum=zlib.crc32
15966if not CONTEXTLMTXMODE or CONTEXTLMTXMODE==0 then
15967 local cs=zlibchecksum
15968 zlibchecksum=function(str,n) return cs(n or 0,str) end
15969end
15970local decompress=function(source) return zlibdecompress (source,-15) end
15971local decompresssize=function(source,targetsize) return zlibdecompresssize(source,targetsize,-15) end
15972local calculatecrc=function(buffer,initial) return zlibchecksum (initial or 0,buffer) end
15973local zipfiles={}
15974utilities.zipfiles=zipfiles
15975local openzipfile,closezipfile,unzipfile,foundzipfile,getziphash,getziplist do
15976 function openzipfile(name)
15977 return {
15978 name=name,
15979 handle=openfile(name,0),
15980 }
15981 end
15982 local function update(handle,data)
15983 position=data.offset
15984 setposition(handle,position)
15985 local signature=readstring(handle,4)
15986 if signature=="PK\3\4" then
15987 local version=readcardinal2(handle)
15988 local flag=readcardinal2(handle)
15989 local method=readcardinal2(handle)
15990 skipbytes(handle,4)
15991 local crc32=readcardinal4(handle)
15992 local compressed=readcardinal4(handle)
15993 local uncompressed=readcardinal4(handle)
15994 local namelength=readcardinal2(handle)
15995 local extralength=readcardinal2(handle)
15996 local filename=readstring(handle,namelength)
15997 local descriptor=band(flag,8)~=0
15998 local encrypted=band(flag,1)~=0
15999 local acceptable=method==0 or method==8
16000 local skipped=0
16001 local size=0
16002 if encrypted then
16003 size=readcardinal2(handle)
16004 skipbytes(handle,size)
16005 skipped=skipped+size+2
16006 skipbytes(8)
16007 skipped=skipped+8
16008 size=readcardinal2(handle)
16009 skipbytes(handle,size)
16010 skipped=skipped+size+2
16011 size=readcardinal4(handle)
16012 skipbytes(handle,size)
16013 skipped=skipped+size+4
16014 size=readcardinal2(handle)
16015 skipbytes(handle,size)
16016 skipped=skipped+size+2
16017 end
16018 if acceptable then
16019 if filename~=data.filename then
16020 else
16021 position=position+30+namelength+extralength+skipped
16022 data.position=position
16023 return position
16024 end
16025 else
16026 end
16027 end
16028 data.position=false
16029 return false
16030 end
16031 local function collect(z)
16032 if not z.list then
16033 local list={}
16034 local hash={}
16035 local position=0
16036 local index=0
16037 local handle=z.handle
16038 local size=getsize(handle)
16039 for i=size-4,size-64*1024,-1 do
16040 setposition(handle,i)
16041 local enddirsignature=readcardinal4(handle)
16042 if enddirsignature==0x06054B50 then
16043 local thisdisknumber=readcardinal2(handle)
16044 local centraldisknumber=readcardinal2(handle)
16045 local thisnofentries=readcardinal2(handle)
16046 local totalnofentries=readcardinal2(handle)
16047 local centralsize=readcardinal4(handle)
16048 local centraloffset=readcardinal4(handle)
16049 local commentlength=readcardinal2(handle)
16050 local comment=readstring(handle,length)
16051 if size-i>=22 then
16052 if thisdisknumber==centraldisknumber then
16053 setposition(handle,centraloffset)
16054 while true do
16055 if readcardinal4(handle)==0x02014B50 then
16056 skipbytes(handle,4)
16057 local flag=readcardinal2(handle)
16058 local method=readcardinal2(handle)
16059 skipbytes(handle,4)
16060 local crc32=readcardinal4(handle)
16061 local compressed=readcardinal4(handle)
16062 local uncompressed=readcardinal4(handle)
16063 local namelength=readcardinal2(handle)
16064 local extralength=readcardinal2(handle)
16065 local commentlength=readcardinal2(handle)
16066 skipbytes(handle,8)
16067 local headeroffset=readcardinal4(handle)
16068 local filename=readstring(handle,namelength)
16069 skipbytes(handle,extralength+commentlength)
16070 local descriptor=band(flag,8)~=0
16071 local encrypted=band(flag,1)~=0
16072 local acceptable=method==0 or method==8
16073 if acceptable then
16074 index=index+1
16075 local data={
16076 filename=filename,
16077 index=index,
16078 position=nil,
16079 method=method,
16080 compressed=compressed,
16081 uncompressed=uncompressed,
16082 crc32=crc32,
16083 encrypted=encrypted,
16084 offset=headeroffset,
16085 }
16086 hash[filename]=data
16087 list[index]=data
16088 end
16089 else
16090 break
16091 end
16092 end
16093 end
16094 break
16095 end
16096 end
16097 end
16098 z.list=list
16099 z.hash=hash
16100 end
16101 end
16102 function getziplist(z)
16103 local list=z.list
16104 if not list then
16105 collect(z)
16106 end
16107 return z.list
16108 end
16109 function getziphash(z)
16110 local hash=z.hash
16111 if not hash then
16112 collect(z)
16113 end
16114 return z.hash
16115 end
16116 function foundzipfile(z,name)
16117 return getziphash(z)[name]
16118 end
16119 function closezipfile(z)
16120 local f=z.handle
16121 if f then
16122 closefile(f)
16123 z.handle=nil
16124 end
16125 end
16126 function unzipfile(z,filename,check)
16127 local hash=z.hash
16128 if not hash then
16129 hash=zipfiles.hash(z)
16130 end
16131 local data=hash[filename]
16132 if not data then
16133 end
16134 if data then
16135 local handle=z.handle
16136 local position=data.position
16137 local compressed=data.compressed
16138 if position==nil then
16139 position=update(handle,data)
16140 end
16141 if position and compressed>0 then
16142 setposition(handle,position)
16143 local result=readstring(handle,compressed)
16144 if data.method==8 then
16145 if decompresssize then
16146 result=decompresssize(result,data.uncompressed)
16147 else
16148 result=decompress(result)
16149 end
16150 end
16151 if check and data.crc32~=calculatecrc(result) then
16152 print("checksum mismatch")
16153 return ""
16154 end
16155 return result
16156 else
16157 return ""
16158 end
16159 end
16160 end
16161 zipfiles.open=openzipfile
16162 zipfiles.close=closezipfile
16163 zipfiles.unzip=unzipfile
16164 zipfiles.hash=getziphash
16165 zipfiles.list=getziplist
16166 zipfiles.found=foundzipfile
16167end
16168if xzip then
16169 local writecardinal1=files.writebyte
16170 local writecardinal2=files.writecardinal2le
16171 local writecardinal4=files.writecardinal4le
16172 local logwriter=logs.writer
16173 local globpattern=dir.globpattern
16174 local compress=xzip.compress
16175 local checksum=xzip.crc32
16176 local function fromdostime(dostime,dosdate)
16177 return ostime {
16178 year=rshift(dosdate,9)+1980,
16179 month=band(rshift(dosdate,5),0x0F),
16180 day=band((dosdate ),0x1F),
16181 hour=band(rshift(dostime,11) ),
16182 min=band(rshift(dostime,5),0x3F),
16183 sec=band((dostime ),0x1F),
16184 }
16185 end
16186 local function todostime(time)
16187 local t=osdate("*t",time)
16188 return
16189 lshift(t.year-1980,9)+lshift(t.month,5)+t.day,
16190 lshift(t.hour,11)+lshift(t.min,5)+rshift(t.sec,1)
16191 end
16192 local function openzip(filename,level,comment,verbose)
16193 local f=ioopen(filename,"wb")
16194 if f then
16195 return {
16196 filename=filename,
16197 handle=f,
16198 list={},
16199 level=tonumber(level) or 3,
16200 comment=tostring(comment),
16201 verbose=verbose,
16202 uncompressed=0,
16203 compressed=0,
16204 }
16205 end
16206 end
16207 local function writezip(z,name,data,level,time)
16208 local f=z.handle
16209 local list=z.list
16210 local level=tonumber(level) or z.level or 3
16211 local method=8
16212 local zipped=compress(data,level)
16213 local checksum=checksum(data)
16214 local verbose=z.verbose
16215 if not zipped then
16216 method=0
16217 zipped=data
16218 end
16219 local start=f:seek()
16220 local compressed=#zipped
16221 local uncompressed=#data
16222 z.compressed=z.compressed+compressed
16223 z.uncompressed=z.uncompressed+uncompressed
16224 if verbose then
16225 local pct=100*compressed/uncompressed
16226 if pct>=100 then
16227 logwriter(format("%10i %s",uncompressed,name))
16228 else
16229 logwriter(format("%10i %02.1f %s",uncompressed,pct,name))
16230 end
16231 end
16232 f:write("\x50\x4b\x03\x04")
16233 writecardinal2(f,0)
16234 writecardinal2(f,0)
16235 writecardinal2(f,method)
16236 writecardinal2(f,0)
16237 writecardinal2(f,0)
16238 writecardinal4(f,checksum)
16239 writecardinal4(f,compressed)
16240 writecardinal4(f,uncompressed)
16241 writecardinal2(f,#name)
16242 writecardinal2(f,0)
16243 f:write(name)
16244 f:write(zipped)
16245 list[#list+1]={ #zipped,#data,name,checksum,start,time or 0 }
16246 end
16247 local function closezip(z)
16248 local f=z.handle
16249 local list=z.list
16250 local comment=z.comment
16251 local verbose=z.verbose
16252 local count=#list
16253 local start=f:seek()
16254 for i=1,count do
16255 local l=list[i]
16256 local compressed=l[1]
16257 local uncompressed=l[2]
16258 local name=l[3]
16259 local checksum=l[4]
16260 local start=l[5]
16261 local time=l[6]
16262 local date,time=todostime(time)
16263 f:write('\x50\x4b\x01\x02')
16264 writecardinal2(f,0)
16265 writecardinal2(f,0)
16266 writecardinal2(f,0)
16267 writecardinal2(f,8)
16268 writecardinal2(f,time)
16269 writecardinal2(f,date)
16270 writecardinal4(f,checksum)
16271 writecardinal4(f,compressed)
16272 writecardinal4(f,uncompressed)
16273 writecardinal2(f,#name)
16274 writecardinal2(f,0)
16275 writecardinal2(f,0)
16276 writecardinal2(f,0)
16277 writecardinal2(f,0)
16278 writecardinal4(f,0)
16279 writecardinal4(f,start)
16280 f:write(name)
16281 end
16282 local stop=f:seek()
16283 local size=stop-start
16284 f:write('\x50\x4b\x05\x06')
16285 writecardinal2(f,0)
16286 writecardinal2(f,0)
16287 writecardinal2(f,count)
16288 writecardinal2(f,count)
16289 writecardinal4(f,size)
16290 writecardinal4(f,start)
16291 if type(comment)=="string" and comment~="" then
16292 writecardinal2(f,#comment)
16293 f:write(comment)
16294 else
16295 writecardinal2(f,0)
16296 end
16297 if verbose then
16298 local compressed=z.compressed
16299 local uncompressed=z.uncompressed
16300 local filename=z.filename
16301 local pct=100*compressed/uncompressed
16302 logwriter("")
16303 if pct>=100 then
16304 logwriter(format("%10i %s",uncompressed,filename))
16305 else
16306 logwriter(format("%10i %02.1f %s",uncompressed,pct,filename))
16307 end
16308 end
16309 f:close()
16310 end
16311 local function zipdir(zipname,path,level,verbose)
16312 if type(zipname)=="table" then
16313 verbose=zipname.verbose
16314 level=zipname.level
16315 path=zipname.path
16316 zipname=zipname.zipname
16317 end
16318 if not zipname or zipname=="" then
16319 return
16320 end
16321 if not path or path=="" then
16322 path="."
16323 end
16324 if not isdir(path) then
16325 return
16326 end
16327 path=gsub(path,"\\+","/")
16328 path=gsub(path,"/+","/")
16329 local list={}
16330 local count=0
16331 globpattern(path,"",true,function(name,size,time)
16332 count=count+1
16333 list[count]={ name,time }
16334 end)
16335 sort(list,function(a,b)
16336 return a[1]<b[1]
16337 end)
16338 local zipf=openzip(zipname,level,comment,verbose)
16339 if zipf then
16340 local p=#path+2
16341 for i=1,count do
16342 local li=list[i]
16343 local name=li[1]
16344 local time=li[2]
16345 local data=loaddata(name)
16346 local name=sub(name,p,#name)
16347 writezip(zipf,name,data,level,time,verbose)
16348 end
16349 closezip(zipf)
16350 end
16351 end
16352 local function unzipdir(zipname,path,verbose,collect,validate)
16353 if type(zipname)=="table" then
16354 validate=zipname.validate
16355 collect=zipname.collect
16356 verbose=zipname.verbose
16357 path=zipname.path
16358 zipname=zipname.zipname
16359 end
16360 if not zipname or zipname=="" then
16361 return
16362 end
16363 if not path or path=="" then
16364 path="."
16365 end
16366 local z=openzipfile(zipname)
16367 if z then
16368 local list=getziplist(z)
16369 if list then
16370 local total=0
16371 local count=#list
16372 local step=number.idiv(count,10)
16373 local done=0
16374 local steps=verbose=="steps"
16375 local time=steps and osclock()
16376 if collect then
16377 collect={}
16378 else
16379 collect=false
16380 end
16381 for i=1,count do
16382 local l=list[i]
16383 local n=l.filename
16384 if not validate or validate(n) then
16385 local d=unzipfile(z,n)
16386 if d then
16387 local p=filejoin(path,n)
16388 if mkdirs(dirname(p)) then
16389 if steps then
16390 total=total+#d
16391 done=done+1
16392 if done>=step then
16393 done=0
16394 logwriter(format("%4i files of %4i done, %10i bytes, %0.3f seconds",i,count,total,osclock()-time))
16395 end
16396 elseif verbose then
16397 logwriter(n)
16398 end
16399 savedata(p,d)
16400 if collect then
16401 collect[#collect+1]=p
16402 end
16403 end
16404 else
16405 logwriter(format("problem with file %s",n))
16406 end
16407 else
16408 end
16409 end
16410 if steps then
16411 logwriter(format("%4i files of %4i done, %10i bytes, %0.3f seconds",count,count,total,osclock()-time))
16412 end
16413 closezipfile(z)
16414 if collect then
16415 return collect
16416 end
16417 else
16418 closezipfile(z)
16419 end
16420 end
16421 end
16422 zipfiles.zipdir=zipdir
16423 zipfiles.unzipdir=unzipdir
16424end
16425local pattern="^\x1F\x8B\x08"
16426local gziplevel=3
16427function gzip.suffix(filename)
16428 local suffix,extra=suffixes(filename)
16429 local gzipped=extra=="gz"
16430 return suffix,gzipped
16431end
16432function gzip.compressed(s)
16433 return s and find(s,pattern)
16434end
16435local getdecompressed
16436local putcompressed
16437if gzip.compress then
16438 local gzipwindow=15+16
16439 local compress=zlib.compress
16440 local decompress=zlib.decompress
16441 getdecompressed=function(str)
16442 return decompress(str,gzipwindow)
16443 end
16444 putcompressed=function(str,level)
16445 return compress(str,level or gziplevel,nil,gzipwindow)
16446 end
16447else
16448 local gzipwindow=-15
16449 local identifier="\x1F\x8B"
16450 local compress=zlib.compress
16451 local decompress=zlib.decompress
16452 local zlibchecksum=zlib.crc32
16453 if not CONTEXTLMTXMODE or CONTEXTLMTXMODE==0 then
16454 local cs=zlibchecksum
16455 zlibchecksum=function(str,n) return cs(n or 0,str) end
16456 end
16457 local streams=utilities.streams
16458 local openstream=streams.openstring
16459 local closestream=streams.close
16460 local getposition=streams.getposition
16461 local readbyte=streams.readbyte
16462 local readcardinal4=streams.readcardinal4le
16463 local readcardinal2=streams.readcardinal2le
16464 local readstring=streams.readstring
16465 local readcstring=streams.readcstring
16466 local skipbytes=streams.skip
16467 local tocardinal1=streams.tocardinal1
16468 local tocardinal4=streams.tocardinal4le
16469 getdecompressed=function(str)
16470 local s=openstream(str)
16471 local identifier=readstring(s,2)
16472 local method=readbyte(s,1)
16473 local flags=readbyte(s,1)
16474 local timestamp=readcardinal4(s)
16475 local compression=readbyte(s,1)
16476 local operating=readbyte(s,1)
16477 local isjusttext=band(flags,0x01)~=0 and true or false
16478 local extrasize=band(flags,0x04)~=0 and readcardinal2(s) or 0
16479 local filename=band(flags,0x08)~=0 and readcstring(s) or ""
16480 local comment=band(flags,0x10)~=0 and readcstring(s) or ""
16481 local checksum=band(flags,0x02)~=0 and readcardinal2(s) or 0
16482 local compressed=readstring(s,#str)
16483 local data=decompress(compressed,gzipwindow)
16484 return data
16485 end
16486 putcompressed=function(str,level,originalname)
16487 return concat {
16488 identifier,
16489 tocardinal1(0x08),
16490 tocardinal1(0x08),
16491 tocardinal4(os.time()),
16492 tocardinal1(0x02),
16493 tocardinal1(0xFF),
16494 (originalname or "unknownname").."\0",
16495 compress(str,level,nil,gzipwindow),
16496 tocardinal4(zlibchecksum(str)),
16497 tocardinal4(#str),
16498 }
16499 end
16500end
16501function gzip.load(filename)
16502 local f=openfile(filename,"rb")
16503 if not f then
16504 else
16505 local data=f:read("*all")
16506 f:close()
16507 if data and data~="" then
16508 if suffix(filename)=="gz" then
16509 data=getdecompressed(data)
16510 end
16511 return data
16512 end
16513 end
16514end
16515function gzip.save(filename,data,level,originalname)
16516 if suffix(filename)~="gz" then
16517 filename=filename..".gz"
16518 end
16519 local f=openfile(filename,"wb")
16520 if f then
16521 data=putcompressed(data or "",level or gziplevel,originalname)
16522 f:write(data)
16523 f:close()
16524 return #data
16525 end
16526end
16527function gzip.compress(s,level)
16528 if s and not find(s,pattern) then
16529 if not level then
16530 level=gziplevel
16531 elseif level<=0 then
16532 return s
16533 elseif level>9 then
16534 level=9
16535 end
16536 return putcompressed(s,level or gziplevel) or s
16537 end
16538end
16539function gzip.decompress(s)
16540 if s and find(s,pattern) then
16541 return getdecompressed(s)
16542 else
16543 return s
16544 end
16545end
16546
16547
16548end
16549
16550do
16551
16552package.loaded["lxml-tab"] = package.loaded["lxml-tab"] or true
16553
16554
16555
16556if not modules then modules={} end modules ['lxml-tab']={
16557 version=1.001,
16558 comment="this module is the basis for the lxml-* ones",
16559 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
16560 copyright="PRAGMA ADE / ConTeXt Development Team",
16561 license="see context related readme files"
16562}
16563local trace_entities=false trackers.register("xml.entities",function(v) trace_entities=v end)
16564local report_xml=logs and logs.reporter("xml","core") or function(...) print(string.format(...)) end
16565if lpeg.setmaxstack then lpeg.setmaxstack(1000) end
16566xml=xml or {}
16567local xml=xml
16568local concat,remove,insert=table.concat,table.remove,table.insert
16569local type,next,setmetatable,getmetatable,tonumber,rawset,select=type,next,setmetatable,getmetatable,tonumber,rawset,select
16570local lower,find,match,gsub=string.lower,string.find,string.match,string.gsub
16571local sort=table.sort
16572local utfchar=utf.char
16573local lpegmatch,lpegpatterns=lpeg.match,lpeg.patterns
16574local P,S,R,C,V,C,Cs=lpeg.P,lpeg.S,lpeg.R,lpeg.C,lpeg.V,lpeg.C,lpeg.Cs
16575local formatters=string.formatters
16576do
16577xml.xmlns=xml.xmlns or {}
16578local check=P(false)
16579local parse=check
16580function xml.registerns(namespace,pattern)
16581 check=check+C(P(lower(pattern)))/namespace
16582 parse=P { P(check)+1*V(1) }
16583end
16584function xml.checkns(namespace,url)
16585 local ns=lpegmatch(parse,lower(url))
16586 if ns and namespace~=ns then
16587 xml.xmlns[namespace]=ns
16588 end
16589end
16590function xml.resolvens(url)
16591 return lpegmatch(parse,lower(url)) or ""
16592end
16593end
16594local nsremap,resolvens=xml.xmlns,xml.resolvens
16595local stack,level,top,at,xmlnms,errorstr
16596local entities,parameters
16597local strip,utfize,resolve,cleanup,resolve_predefined,unify_predefined
16598local dcache,hcache,acache
16599local mt,dt,nt
16600local currentfilename,currentline,linenumbers
16601local grammar_parsed_text_one
16602local grammar_parsed_text_two
16603local grammar_unparsed_text
16604local handle_hex_entity
16605local handle_dec_entity
16606local handle_any_entity_dtd
16607local handle_any_entity_text
16608local function preparexmlstate(settings)
16609 if settings then
16610 linenumbers=settings.linenumbers
16611 stack={}
16612 level=0
16613 top={}
16614 at={}
16615 mt={}
16616 dt={}
16617 nt=0
16618 xmlns={}
16619 errorstr=nil
16620 strip=settings.strip_cm_and_dt
16621 utfize=settings.utfize_entities
16622 resolve=settings.resolve_entities
16623 resolve_predefined=settings.resolve_predefined_entities
16624 unify_predefined=settings.unify_predefined_entities
16625 cleanup=settings.text_cleanup
16626 entities=settings.entities or {}
16627 currentfilename=settings.currentresource
16628 currentline=1
16629 parameters={}
16630 reported_at_errors={}
16631 dcache={}
16632 hcache={}
16633 acache={}
16634 if utfize==nil then
16635 settings.utfize_entities=true
16636 utfize=true
16637 end
16638 if resolve_predefined==nil then
16639 settings.resolve_predefined_entities=true
16640 resolve_predefined=true
16641 end
16642 else
16643 linenumbers=false
16644 stack=nil
16645 level=nil
16646 top=nil
16647 at=nil
16648 mt=nil
16649 dt=nil
16650 nt=nil
16651 xmlns=nil
16652 errorstr=nil
16653 strip=nil
16654 utfize=nil
16655 resolve=nil
16656 resolve_predefined=nil
16657 unify_predefined=nil
16658 cleanup=nil
16659 entities=nil
16660 parameters=nil
16661 reported_at_errors=nil
16662 dcache=nil
16663 hcache=nil
16664 acache=nil
16665 currentfilename=nil
16666 currentline=1
16667 end
16668end
16669local function initialize_mt(root)
16670 mt={ __index=root }
16671end
16672function xml.setproperty(root,k,v)
16673 getmetatable(root).__index[k]=v
16674end
16675function xml.checkerror(top,toclose)
16676 return ""
16677end
16678local checkns=xml.checkns
16679local function add_attribute(namespace,tag,value)
16680 if cleanup and value~="" then
16681 value=cleanup(value)
16682 end
16683 if tag=="xmlns" then
16684 xmlns[#xmlns+1]=resolvens(value)
16685 at[tag]=value
16686 elseif namespace=="" then
16687 at[tag]=value
16688 elseif namespace=="xmlns" then
16689 checkns(tag,value)
16690 at["xmlns:"..tag]=value
16691 else
16692 at[namespace..":"..tag]=value
16693 end
16694end
16695local function add_empty(spacing,namespace,tag)
16696 if spacing~="" then
16697 nt=nt+1
16698 dt[nt]=spacing
16699 end
16700 local resolved=namespace=="" and xmlns[#xmlns] or nsremap[namespace] or namespace
16701 top=stack[level]
16702 dt=top.dt
16703 nt=#dt+1
16704 local t=linenumbers and {
16705 ns=namespace or "",
16706 rn=resolved,
16707 tg=tag,
16708 at=at,
16709 dt={},
16710 ni=nt,
16711 cf=currentfilename,
16712 cl=currentline,
16713 __p__=top,
16714 } or {
16715 ns=namespace or "",
16716 rn=resolved,
16717 tg=tag,
16718 at=at,
16719 dt={},
16720 ni=nt,
16721 __p__=top,
16722 }
16723 dt[nt]=t
16724 setmetatable(t,mt)
16725 if at.xmlns then
16726 remove(xmlns)
16727 end
16728 at={}
16729end
16730local function add_begin(spacing,namespace,tag)
16731 if spacing~="" then
16732 nt=nt+1
16733 dt[nt]=spacing
16734 end
16735 local resolved=namespace=="" and xmlns[#xmlns] or nsremap[namespace] or namespace
16736 dt={}
16737 top=linenumbers and {
16738 ns=namespace or "",
16739 rn=resolved,
16740 tg=tag,
16741 at=at,
16742 dt=dt,
16743 ni=nil,
16744 cf=currentfilename,
16745 cl=currentline,
16746 __p__=stack[level],
16747 } or {
16748 ns=namespace or "",
16749 rn=resolved,
16750 tg=tag,
16751 at=at,
16752 dt=dt,
16753 ni=nil,
16754 __p__=stack[level],
16755 }
16756 setmetatable(top,mt)
16757 nt=0
16758 level=level+1
16759 stack[level]=top
16760 at={}
16761end
16762local function add_end(spacing,namespace,tag)
16763 if spacing~="" then
16764 nt=nt+1
16765 dt[nt]=spacing
16766 end
16767 local toclose=stack[level]
16768 level=level-1
16769 top=stack[level]
16770 if level<1 then
16771 errorstr=formatters["unable to close %s %s"](tag,xml.checkerror(top,toclose) or "")
16772 report_xml(errorstr)
16773 elseif toclose.tg~=tag then
16774 errorstr=formatters["unable to close %s with %s %s"](toclose.tg,tag,xml.checkerror(top,toclose) or "")
16775 report_xml(errorstr)
16776 end
16777 dt=top.dt
16778 nt=#dt+1
16779 dt[nt]=toclose
16780 toclose.ni=nt
16781 if toclose.at.xmlns then
16782 remove(xmlns)
16783 end
16784end
16785local function add_text(text)
16786 if text=="" then
16787 return
16788 elseif cleanup then
16789 if nt>0 then
16790 local s=dt[nt]
16791 if type(s)=="string" then
16792 dt[nt]=s..cleanup(text)
16793 else
16794 nt=nt+1
16795 dt[nt]=cleanup(text)
16796 end
16797 else
16798 nt=1
16799 dt[1]=cleanup(text)
16800 end
16801 else
16802 if nt>0 then
16803 local s=dt[nt]
16804 if type(s)=="string" then
16805 dt[nt]=s..text
16806 else
16807 nt=nt+1
16808 dt[nt]=text
16809 end
16810 else
16811 nt=1
16812 dt[1]=text
16813 end
16814 end
16815end
16816local function add_special(what,spacing,text)
16817 if spacing~="" then
16818 nt=nt+1
16819 dt[nt]=spacing
16820 end
16821 if strip and (what=="@cm@" or what=="@dt@") then
16822 else
16823 nt=nt+1
16824 dt[nt]=linenumbers and {
16825 special=true,
16826 ns="",
16827 tg=what,
16828 ni=nil,
16829 dt={ text },
16830 cf=currentfilename,
16831 cl=currentline,
16832 } or {
16833 special=true,
16834 ns="",
16835 tg=what,
16836 ni=nil,
16837 dt={ text },
16838 }
16839 end
16840end
16841local function set_message(txt)
16842 errorstr="garbage at the end of the file: "..gsub(txt,"([ \n\r\t]*)","")
16843end
16844local function attribute_value_error(str)
16845 if not reported_at_errors[str] then
16846 report_xml("invalid attribute value %a",str)
16847 reported_at_errors[str]=true
16848 at._error_=str
16849 end
16850 return str
16851end
16852local function attribute_specification_error(str)
16853 if not reported_at_errors[str] then
16854 report_xml("invalid attribute specification %a",str)
16855 reported_at_errors[str]=true
16856 at._error_=str
16857 end
16858 return str
16859end
16860do
16861 local badentity="&"
16862 xml.placeholders={
16863 unknown_dec_entity=function(str) return str=="" and badentity or formatters["&%s;"](str) end,
16864 unknown_hex_entity=function(str) return formatters["&#x%s;"](str) end,
16865 unknown_any_entity=function(str) return formatters["&#x%s;"](str) end,
16866 }
16867 local function fromhex(s)
16868 local n=tonumber(s,16)
16869 if n then
16870 return utfchar(n)
16871 else
16872 return formatters["h:%s"](s),true
16873 end
16874 end
16875 local function fromdec(s)
16876 local n=tonumber(s)
16877 if n then
16878 return utfchar(n)
16879 else
16880 return formatters["d:%s"](s),true
16881 end
16882 end
16883 local p_rest=(1-P(";"))^0
16884 local p_many=P(1)^0
16885 local parsedentity=P("&#")*(P("x")*(p_rest/fromhex)+(p_rest/fromdec))*P(";")*P(-1)+P ("#")*(P("x")*(p_many/fromhex)+(p_many/fromdec))
16886 xml.parsedentitylpeg=parsedentity
16887 local predefined_unified={
16888 [38]="&",
16889 [42]=""",
16890 [47]="'",
16891 [74]="<",
16892 [76]=">",
16893 }
16894 local predefined_simplified={
16895 [38]="&",amp="&",
16896 [42]='"',quot='"',
16897 [47]="'",apos="'",
16898 [74]="<",lt="<",
16899 [76]=">",gt=">",
16900 }
16901 local nofprivates=0xF0000
16902 local privates_u={
16903 [ [[&]] ]="&",
16904 [ [["]] ]=""",
16905 [ [[']] ]="'",
16906 [ [[<]] ]="<",
16907 [ [[>]] ]=">",
16908 }
16909 local privates_p={
16910 }
16911 local privates_s={
16912 [ [["]] ]="&U+22;",
16913 [ [[#]] ]="&U+23;",
16914 [ [[$]] ]="&U+24;",
16915 [ [[%]] ]="&U+25;",
16916 [ [[&]] ]="&U+26;",
16917 [ [[']] ]="&U+27;",
16918 [ [[<]] ]="&U+3C;",
16919 [ [[>]] ]="&U+3E;",
16920 [ [[\]] ]="&U+5C;",
16921 [ [[{]] ]="&U+7B;",
16922 [ [[|]] ]="&U+7C;",
16923 [ [[}]] ]="&U+7D;",
16924 [ [[~]] ]="&U+7E;",
16925 }
16926 local privates_x={
16927 [ [["]] ]="&U+22;",
16928 [ [[#]] ]="&U+23;",
16929 [ [[$]] ]="&U+24;",
16930 [ [[%]] ]="&U+25;",
16931 [ [[']] ]="&U+27;",
16932 [ [[\]] ]="&U+5C;",
16933 [ [[{]] ]="&U+7B;",
16934 [ [[|]] ]="&U+7C;",
16935 [ [[}]] ]="&U+7D;",
16936 [ [[~]] ]="&U+7E;",
16937 }
16938 local privates_n={
16939 }
16940 utilities.storage.mark(privates_u)
16941 utilities.storage.mark(privates_p)
16942 utilities.storage.mark(privates_s)
16943 utilities.storage.mark(privates_x)
16944 utilities.storage.mark(privates_n)
16945 local escaped=utf.remapper(privates_u,"dynamic")
16946 local unprivatized=utf.remapper(privates_p,"dynamic")
16947 local unspecialized=utf.remapper(privates_s,"dynamic")
16948 local despecialized=utf.remapper(privates_x,"dynamic")
16949 xml.unprivatized=unprivatized
16950 xml.unspecialized=unspecialized
16951 xml.despecialized=despecialized
16952 xml.escaped=escaped
16953 local function unescaped(s)
16954 local p=privates_n[s]
16955 if not p then
16956 nofprivates=nofprivates+1
16957 p=utfchar(nofprivates)
16958 privates_n[s]=p
16959 s="&"..s..";"
16960 privates_u[p]=s
16961 privates_p[p]=s
16962 privates_s[p]=s
16963 end
16964 return p
16965 end
16966 xml.privatetoken=unescaped
16967 xml.privatecodes=privates_n
16968 xml.specialcodes=privates_s
16969 function xml.addspecialcode(key,value)
16970 privates_s[key]=value or "&"..s..";"
16971 end
16972 handle_hex_entity=function(str)
16973 local h=hcache[str]
16974 if not h then
16975 local n=tonumber(str,16)
16976 h=unify_predefined and predefined_unified[n]
16977 if h then
16978 if trace_entities then
16979 report_xml("utfize, converting hex entity &#x%s; into %a",str,h)
16980 end
16981 elseif utfize then
16982 h=(n and utfchar(n)) or xml.unknown_hex_entity(str) or ""
16983 if not n then
16984 report_xml("utfize, ignoring hex entity &#x%s;",str)
16985 elseif trace_entities then
16986 report_xml("utfize, converting hex entity &#x%s; into %a",str,h)
16987 end
16988 else
16989 if trace_entities then
16990 report_xml("found entity &#x%s;",str)
16991 end
16992 h="&#x"..str..";"
16993 end
16994 hcache[str]=h
16995 end
16996 return h
16997 end
16998 handle_dec_entity=function(str)
16999 local d=dcache[str]
17000 if not d then
17001 local n=tonumber(str)
17002 d=unify_predefined and predefined_unified[n]
17003 if d then
17004 if trace_entities then
17005 report_xml("utfize, converting dec entity &#%s; into %a",str,d)
17006 end
17007 elseif utfize then
17008 d=(n and utfchar(n)) or placeholders.unknown_dec_entity(str) or ""
17009 if not n then
17010 report_xml("utfize, ignoring dec entity &#%s;",str)
17011 elseif trace_entities then
17012 report_xml("utfize, converting dec entity &#%s; into %a",str,d)
17013 end
17014 else
17015 if trace_entities then
17016 report_xml("found entity &#%s;",str)
17017 end
17018 d="&#"..str..";"
17019 end
17020 dcache[str]=d
17021 end
17022 return d
17023 end
17024 handle_any_entity_dtd=function(str)
17025 if resolve then
17026 local a=resolve_predefined and predefined_simplified[str]
17027 if a then
17028 if trace_entities then
17029 report_xml("resolving entity &%s; to predefined %a",str,a)
17030 end
17031 else
17032 if type(resolve)=="function" then
17033 a=resolve(str,entities) or entities[str]
17034 else
17035 a=entities[str]
17036 end
17037 if a then
17038 if type(a)=="function" then
17039 if trace_entities then
17040 report_xml("expanding entity &%s; to function call",str)
17041 end
17042 a=a(str) or ""
17043 end
17044 a=lpegmatch(parsedentity,a) or a
17045 if trace_entities then
17046 report_xml("resolving entity &%s; to internal %a",str,a)
17047 end
17048 else
17049 local unknown_any_entity=placeholders.unknown_any_entity
17050 if unknown_any_entity then
17051 a=unknown_any_entity(str) or ""
17052 end
17053 if a then
17054 if trace_entities then
17055 report_xml("resolving entity &%s; to external %s",str,a)
17056 end
17057 else
17058 if trace_entities then
17059 report_xml("keeping entity &%s;",str)
17060 end
17061 if str=="" then
17062 a=badentity
17063 else
17064 a="&"..str..";"
17065 end
17066 end
17067 end
17068 end
17069 return a
17070 else
17071 local a=acache[str]
17072 if not a then
17073 a=resolve_predefined and predefined_simplified[str]
17074 if a then
17075 acache[str]=a
17076 if trace_entities then
17077 report_xml("entity &%s; becomes %a",str,a)
17078 end
17079 elseif str=="" then
17080 if trace_entities then
17081 report_xml("invalid entity &%s;",str)
17082 end
17083 a=badentity
17084 acache[str]=a
17085 else
17086 if trace_entities then
17087 report_xml("entity &%s; is made private",str)
17088 end
17089 a=unescaped(str)
17090 acache[str]=a
17091 end
17092 end
17093 return a
17094 end
17095 end
17096 handle_any_entity_text=function(str)
17097 if resolve then
17098 local a=resolve_predefined and predefined_simplified[str]
17099 if a then
17100 if trace_entities then
17101 report_xml("resolving entity &%s; to predefined %a",str,a)
17102 end
17103 else
17104 if type(resolve)=="function" then
17105 a=resolve(str,entities) or entities[str]
17106 else
17107 a=entities[str]
17108 end
17109 if a then
17110 if type(a)=="function" then
17111 if trace_entities then
17112 report_xml("expanding entity &%s; to function call",str)
17113 end
17114 a=a(str) or ""
17115 end
17116 a=lpegmatch(grammar_parsed_text_two,a) or a
17117 if type(a)=="number" then
17118 return ""
17119 else
17120 a=lpegmatch(parsedentity,a) or a
17121 if trace_entities then
17122 report_xml("resolving entity &%s; to internal %a",str,a)
17123 end
17124 end
17125 if trace_entities then
17126 report_xml("resolving entity &%s; to internal %a",str,a)
17127 end
17128 else
17129 local unknown_any_entity=placeholders.unknown_any_entity
17130 if unknown_any_entity then
17131 a=unknown_any_entity(str) or ""
17132 end
17133 if a then
17134 if trace_entities then
17135 report_xml("resolving entity &%s; to external %s",str,a)
17136 end
17137 else
17138 if trace_entities then
17139 report_xml("keeping entity &%s;",str)
17140 end
17141 if str=="" then
17142 a=badentity
17143 else
17144 a="&"..str..";"
17145 end
17146 end
17147 end
17148 end
17149 return a
17150 else
17151 local a=acache[str]
17152 if not a then
17153 a=resolve_predefined and predefined_simplified[str]
17154 if a then
17155 acache[str]=a
17156 if trace_entities then
17157 report_xml("entity &%s; becomes %a",str,a)
17158 end
17159 elseif str=="" then
17160 if trace_entities then
17161 report_xml("invalid entity &%s;",str)
17162 end
17163 a=badentity
17164 acache[str]=a
17165 else
17166 if trace_entities then
17167 report_xml("entity &%s; is made private",str)
17168 end
17169 a=unescaped(str)
17170 acache[str]=a
17171 end
17172 end
17173 return a
17174 end
17175 end
17176 local p_rest=(1-P(";"))^1
17177 local spec={
17178 [0x23]="\\Ux{23}",
17179 [0x24]="\\Ux{24}",
17180 [0x25]="\\Ux{25}",
17181 [0x5C]="\\Ux{5C}",
17182 [0x7B]="\\Ux{7B}",
17183 [0x7C]="\\Ux{7C}",
17184 [0x7D]="\\Ux{7D}",
17185 [0x7E]="\\Ux{7E}",
17186 }
17187 local hash=table.setmetatableindex(spec,function(t,k)
17188 local v=utfchar(k)
17189 t[k]=v
17190 return v
17191 end)
17192 local function fromuni(s)
17193 local n=tonumber(s,16)
17194 if n then
17195 return hash[n]
17196 else
17197 return formatters["u:%s"](s),true
17198 end
17199 end
17200 local function fromhex(s)
17201 local n=tonumber(s,16)
17202 if n then
17203 return hash[n]
17204 else
17205 return formatters["h:%s"](s),true
17206 end
17207 end
17208 local function fromdec(s)
17209 local n=tonumber(s)
17210 if n then
17211 return hash[n]
17212 else
17213 return formatters["d:%s"](s),true
17214 end
17215 end
17216 local reparsedentity=P("U+")*(p_rest/fromuni)+P("#")*(
17217 P("x")*(p_rest/fromhex)+p_rest/fromdec
17218 )
17219 local hash=table.setmetatableindex(function(t,k)
17220 local v=utfchar(k)
17221 t[k]=v
17222 return v
17223 end)
17224 local function fromuni(s)
17225 local n=tonumber(s,16)
17226 if n then
17227 return hash[n]
17228 else
17229 return formatters["u:%s"](s),true
17230 end
17231 end
17232 local function fromhex(s)
17233 local n=tonumber(s,16)
17234 if n then
17235 return hash[n]
17236 else
17237 return formatters["h:%s"](s),true
17238 end
17239 end
17240 local function fromdec(s)
17241 local n=tonumber(s)
17242 if n then
17243 return hash[n]
17244 else
17245 return formatters["d:%s"](s),true
17246 end
17247 end
17248 local unescapedentity=P("U+")*(p_rest/fromuni)+P("#")*(
17249 P("x")*(p_rest/fromhex)+p_rest/fromdec
17250 )
17251 xml.reparsedentitylpeg=reparsedentity
17252 xml.unescapedentitylpeg=unescapedentity
17253end
17254local escaped=xml.escaped
17255local unescaped=xml.unescaped
17256local placeholders=xml.placeholders
17257local function handle_end_entity(str)
17258 report_xml("error in entity, %a found without ending %a",str,";")
17259 return str
17260end
17261local function handle_crap_error(chr)
17262 report_xml("error in parsing, unexpected %a found ",chr)
17263 add_text(chr)
17264 return chr
17265end
17266local function handlenewline()
17267 currentline=currentline+1
17268end
17269local spacetab=S(' \t')
17270local space=S(' \r\n\t')
17271local newline=lpegpatterns.newline/handlenewline
17272local anything=P(1)
17273local open=P('<')
17274local close=P('>')
17275local squote=S("'")
17276local dquote=S('"')
17277local equal=P('=')
17278local slash=P('/')
17279local colon=P(':')
17280local semicolon=P(';')
17281local ampersand=P('&')
17282local valid_0=R("\128\255")
17283local valid_1=R('az','AZ')+S('_')+valid_0
17284local valid_2=valid_1+R('09')+S('-.')
17285local valid=valid_1*valid_2^0
17286local name_yes=C(valid^1)*colon*C(valid^1)
17287local name_nop=C(P(true))*C(valid^1)
17288local name=name_yes+name_nop
17289local utfbom=lpegpatterns.utfbom
17290local spacing=C(space^0)
17291local space_nl=spacetab+newline
17292local spacing_nl=Cs((space_nl)^0)
17293local anything_nl=newline+P(1)
17294local function weirdentity(k,v)
17295 if trace_entities then
17296 report_xml("registering %s entity %a as %a","weird",k,v)
17297 end
17298 parameters[k]=v
17299end
17300local function normalentity(k,v)
17301 if trace_entities then
17302 report_xml("registering %s entity %a as %a","normal",k,v)
17303 end
17304 entities[k]=v
17305end
17306local function systementity(k,v,n)
17307 if trace_entities then
17308 report_xml("registering %s entity %a as %a","system",k,v)
17309 end
17310 entities[k]=v
17311end
17312local function publicentity(k,v,n)
17313 if trace_entities then
17314 report_xml("registering %s entity %a as %a","public",k,v)
17315 end
17316 entities[k]=v
17317end
17318local function entityfile(pattern,k,v,n)
17319 if n then
17320 local okay,data
17321 local loadbinfile=resolvers and resolvers.loadbinfile
17322 if loadbinfile then
17323 okay,data=loadbinfile(n)
17324 else
17325 data=io.loaddata(n)
17326 okay=data and data~=""
17327 end
17328 if okay then
17329 if trace_entities then
17330 report_xml("loading public entities %a as %a from %a",k,v,n)
17331 end
17332 lpegmatch(pattern,data)
17333 return
17334 end
17335 end
17336 report_xml("ignoring public entities %a as %a from %a",k,v,n)
17337end
17338local function install(spacenewline,spacing,anything)
17339 local anyentitycontent=(1-open-semicolon-space-close-ampersand)^0
17340 local hexentitycontent=R("AF","af","09")^1
17341 local decentitycontent=R("09")^1
17342 local parsedentity=P("#")/""*(
17343 P("x")/""*(hexentitycontent/handle_hex_entity)+(decentitycontent/handle_dec_entity)
17344 )+(anyentitycontent/handle_any_entity_dtd)
17345 local parsedentity_text=P("#")/""*(
17346 P("x")/""*(hexentitycontent/handle_hex_entity)+(decentitycontent/handle_dec_entity)
17347 )+(anyentitycontent/handle_any_entity_text)
17348 local entity=(ampersand/"")*parsedentity*(semicolon/"")+ampersand*(anyentitycontent/handle_end_entity)
17349 local entity_text=(ampersand/"")*parsedentity_text*(semicolon/"")+ampersand*(anyentitycontent/handle_end_entity)
17350 local text_unparsed=Cs((anything-open)^1)
17351 local text_parsed=(Cs((anything-open-ampersand)^1)/add_text+Cs(entity_text)/add_text)^1
17352 local somespace=(spacenewline)^1
17353 local optionalspace=(spacenewline)^0
17354 local value=(squote*Cs((entity+(anything-squote))^0)*squote)+(dquote*Cs((entity+(anything-dquote))^0)*dquote)
17355 local endofattributes=slash*close+close
17356 local whatever=space*name*optionalspace*equal
17357 local wrongvalue=Cs(P(entity+(1-space-endofattributes))^1)/attribute_value_error
17358 local attributevalue=value+wrongvalue
17359 local attribute=(somespace*name*optionalspace*equal*optionalspace*attributevalue)/add_attribute
17360 local attributes=(attribute+somespace^-1*(((anything-endofattributes)^1)/attribute_specification_error))^0
17361 local parsedtext=text_parsed
17362 local unparsedtext=text_unparsed/add_text
17363 local balanced=P { "["*((anything-S"[]")+V(1))^0*"]" }
17364 local emptyelement=(spacing*open*name*attributes*optionalspace*slash*close)/add_empty
17365 local beginelement=(spacing*open*name*attributes*optionalspace*close)/add_begin
17366 local endelement=(spacing*open*slash*name*optionalspace*close)/add_end
17367 local begincomment=open*P("!--")
17368 local endcomment=P("--")*close
17369 local begininstruction=open*P("?")
17370 local endinstruction=P("?")*close
17371 local begincdata=open*P("![CDATA[")
17372 local endcdata=P("]]")*close
17373 local someinstruction=C((anything-endinstruction)^0)
17374 local somecomment=C((anything-endcomment )^0)
17375 local somecdata=C((anything-endcdata )^0)
17376 local begindoctype=open*P("!DOCTYPE")
17377 local enddoctype=close
17378 local beginset=P("[")
17379 local endset=P("]")
17380 local wrdtypename=C((anything-somespace-P(";"))^1)
17381 local doctypename=C((anything-somespace-close)^0)
17382 local elementdoctype=optionalspace*P("<!ELEMENT")*(anything-close)^0*close
17383 local basiccomment=begincomment*((anything-endcomment)^0)*endcomment
17384 local weirdentitytype=P("%")*(somespace*doctypename*somespace*value)/weirdentity
17385 local normalentitytype=(doctypename*somespace*value)/normalentity
17386 local publicentitytype=(doctypename*somespace*P("PUBLIC")*somespace*value)/publicentity
17387 local systementitytype=(doctypename*somespace*P("SYSTEM")*somespace*value*somespace*P("NDATA")*somespace*doctypename)/systementity
17388 local entitydoctype=optionalspace*P("<!ENTITY")*somespace*(systementitytype+publicentitytype+normalentitytype+weirdentitytype)*optionalspace*close
17389 local publicentityfile=(doctypename*somespace*P("PUBLIC")*somespace*value*(somespace*value)^0)/function(...)
17390 entityfile(entitydoctype,...)
17391 end
17392 local function weirdresolve(s)
17393 lpegmatch(entitydoctype,parameters[s])
17394 end
17395 local function normalresolve(s)
17396 lpegmatch(entitydoctype,entities[s])
17397 end
17398 local entityresolve=P("%")*(wrdtypename/weirdresolve )*P(";")+P("&")*(wrdtypename/normalresolve)*P(";")
17399 entitydoctype=entitydoctype+entityresolve
17400 local doctypeset=beginset*optionalspace*P(elementdoctype+entitydoctype+entityresolve+basiccomment+space)^0*optionalspace*endset
17401 local definitiondoctype=doctypename*somespace*doctypeset
17402 local publicdoctype=doctypename*somespace*P("PUBLIC")*somespace*value*somespace*value*somespace*doctypeset
17403 local systemdoctype=doctypename*somespace*P("SYSTEM")*somespace*value*somespace*doctypeset
17404 local simpledoctype=(anything-close)^1
17405 local somedoctype=C((somespace*(publicentityfile+publicdoctype+systemdoctype+definitiondoctype+simpledoctype)*optionalspace)^0)
17406 local instruction=(spacing*begininstruction*someinstruction*endinstruction)/function(...) add_special("@pi@",...) end
17407 local comment=(spacing*begincomment*somecomment*endcomment )/function(...) add_special("@cm@",...) end
17408 local cdata=(spacing*begincdata*somecdata*endcdata )/function(...) add_special("@cd@",...) end
17409 local doctype=(spacing*begindoctype*somedoctype*enddoctype )/function(...) add_special("@dt@",...) end
17410 local crap_parsed=anything-beginelement-endelement-emptyelement-begininstruction-begincomment-begincdata-ampersand
17411 local crap_unparsed=anything-beginelement-endelement-emptyelement-begininstruction-begincomment-begincdata
17412 local parsedcrap=Cs((crap_parsed^1+entity_text)^1)/handle_crap_error
17413 local parsedcrap=Cs((crap_parsed^1+entity_text)^1)/handle_crap_error
17414 local unparsedcrap=Cs((crap_unparsed )^1)/handle_crap_error
17415 local trailer=space^0*(text_unparsed/set_message)^0
17416 local grammar_parsed_text_one=P { "preamble",
17417 preamble=utfbom^0*instruction^0*(doctype+comment+instruction)^0,
17418 }
17419 local grammar_parsed_text_two=P { "followup",
17420 followup=V("parent")*trailer,
17421 parent=beginelement*V("children")^0*endelement,
17422 children=parsedtext+V("parent")+emptyelement+comment+cdata+instruction+parsedcrap,
17423 }
17424 local grammar_unparsed_text=P { "preamble",
17425 preamble=utfbom^0*instruction^0*(doctype+comment+instruction)^0*V("parent")*trailer,
17426 parent=beginelement*V("children")^0*endelement,
17427 children=unparsedtext+V("parent")+emptyelement+comment+cdata+instruction+unparsedcrap,
17428 }
17429 return grammar_parsed_text_one,grammar_parsed_text_two,grammar_unparsed_text
17430end
17431local
17432 grammar_parsed_text_one_nop,
17433 grammar_parsed_text_two_nop,
17434 grammar_unparsed_text_nop=install(space,spacing,anything)
17435local
17436 grammar_parsed_text_one_yes,
17437 grammar_parsed_text_two_yes,
17438 grammar_unparsed_text_yes=install(space_nl,spacing_nl,anything_nl)
17439local function _xmlconvert_(data,settings,detail)
17440 settings=settings or {}
17441 preparexmlstate(settings)
17442 if settings.linenumbers then
17443 grammar_parsed_text_one=grammar_parsed_text_one_yes
17444 grammar_parsed_text_two=grammar_parsed_text_two_yes
17445 grammar_unparsed_text=grammar_unparsed_text_yes
17446 else
17447 grammar_parsed_text_one=grammar_parsed_text_one_nop
17448 grammar_parsed_text_two=grammar_parsed_text_two_nop
17449 grammar_unparsed_text=grammar_unparsed_text_nop
17450 end
17451 local preprocessor=settings.preprocessor
17452 if data and data~="" and type(preprocessor)=="function" then
17453 data=preprocessor(data,settings) or data
17454 end
17455 if settings.parent_root then
17456 mt=getmetatable(settings.parent_root)
17457 else
17458 initialize_mt(top)
17459 end
17460 level=level+1
17461 stack[level]=top
17462 top.dt={}
17463 dt=top.dt
17464 nt=0
17465 if not data or data=="" then
17466 errorstr="empty xml file"
17467 elseif data==true then
17468 errorstr=detail or "problematic xml file"
17469 elseif utfize or resolve then
17470 local m=lpegmatch(grammar_parsed_text_one,data)
17471 if m then
17472 m=lpegmatch(grammar_parsed_text_two,data,m)
17473 end
17474 if m then
17475 else
17476 errorstr="invalid xml file - parsed text"
17477 end
17478 elseif type(data)=="string" then
17479 if lpegmatch(grammar_unparsed_text,data) then
17480 errorstr=""
17481 else
17482 errorstr="invalid xml file - unparsed text"
17483 end
17484 else
17485 errorstr="invalid xml file - no text at all"
17486 end
17487 local result
17488 if errorstr and errorstr~="" then
17489 result={ dt={ { ns="",tg="error",dt={ errorstr },at={},er=true } } }
17490 setmetatable(result,mt)
17491 setmetatable(result.dt[1],mt)
17492 setmetatable(stack,mt)
17493 local errorhandler=settings.error_handler
17494 if errorhandler==false then
17495 else
17496 errorhandler=errorhandler or xml.errorhandler
17497 if errorhandler then
17498 local currentresource=settings.currentresource
17499 if currentresource and currentresource~="" then
17500 xml.errorhandler(formatters["load error in [%s]: %s"](currentresource,errorstr),currentresource)
17501 else
17502 xml.errorhandler(formatters["load error: %s"](errorstr))
17503 end
17504 end
17505 end
17506 else
17507 result=stack[1]
17508 end
17509 if not settings.no_root then
17510 result={ special=true,ns="",tg='@rt@',dt=result.dt,at={},entities=entities,settings=settings }
17511 setmetatable(result,mt)
17512 local rdt=result.dt
17513 for k=1,#rdt do
17514 local v=rdt[k]
17515 if type(v)=="table" and not v.special then
17516 result.ri=k
17517 v.__p__=result
17518 break
17519 end
17520 end
17521 end
17522 if errorstr and errorstr~="" then
17523 result.error=true
17524 else
17525 errorstr=nil
17526 end
17527 result.statistics={
17528 errormessage=errorstr,
17529 entities={
17530 decimals=dcache,
17531 hexadecimals=hcache,
17532 names=acache,
17533 intermediates=parameters,
17534 }
17535 }
17536 preparexmlstate()
17537 return result
17538end
17539local function xmlconvert(data,settings)
17540 local ok,result=pcall(function() return _xmlconvert_(data,settings) end)
17541 if ok then
17542 return result
17543 elseif type(result)=="string" then
17544 return _xmlconvert_(true,settings,result)
17545 else
17546 return _xmlconvert_(true,settings)
17547 end
17548end
17549xml.convert=xmlconvert
17550function xml.inheritedconvert(data,xmldata,cleanup)
17551 local settings=xmldata.settings
17552 if settings then
17553 settings.parent_root=xmldata
17554 end
17555 local xc=xmlconvert(data,settings)
17556 if cleanup then
17557 local x=xc.dt
17558 if x then
17559 x=x[1]
17560 if x and x.tg=="@pi@" then
17561 local dt=x.dt
17562 local pi=dt and dt[1]
17563 if type(pi)=="string" and find(pi,"^xml") then
17564 remove(dt,1)
17565 end
17566 end
17567 end
17568 end
17569 return xc
17570end
17571function xml.is_valid(root)
17572 return root and root.dt and root.dt[1] and type(root.dt[1])=="table" and not root.dt[1].er
17573end
17574function xml.package(tag,attributes,data)
17575 local ns,tg=match(tag,"^(.-):?([^:]+)$")
17576 local t={ ns=ns,tg=tg,dt=data or "",at=attributes or {} }
17577 setmetatable(t,mt)
17578 return t
17579end
17580function xml.is_valid(root)
17581 return root and not root.error
17582end
17583xml.errorhandler=report_xml
17584function xml.load(filename,settings)
17585 local data=""
17586 if type(filename)=="string" then
17587 local f=io.open(filename,'r')
17588 if f then
17589 data=f:read("*all")
17590 f:close()
17591 end
17592 elseif filename then
17593 data=filename:read("*all")
17594 end
17595 if settings then
17596 settings.currentresource=filename
17597 local result=xmlconvert(data,settings)
17598 settings.currentresource=nil
17599 return result
17600 else
17601 return xmlconvert(data,{ currentresource=filename })
17602 end
17603end
17604local no_root={ no_root=true }
17605function xml.toxml(data)
17606 if type(data)=="string" then
17607 local root={ xmlconvert(data,no_root) }
17608 return (#root>1 and root) or root[1]
17609 else
17610 return data
17611 end
17612end
17613local function copy(old,p)
17614 if old then
17615 local new={}
17616 for k,v in next,old do
17617 local t=type(v)=="table"
17618 if k=="at" then
17619 local t={}
17620 for k,v in next,v do
17621 t[k]=v
17622 end
17623 new[k]=t
17624 elseif k=="dt" then
17625 v.__p__=nil
17626 local t={}
17627 for i=1,#v do
17628 local vi=v[i]
17629 if type(vi)=="table" then
17630 t[i]=copy(vi,new)
17631 else
17632 t[i]=vi
17633 end
17634 end
17635 new[k]=t
17636 t.__p__=p
17637 else
17638 new[k]=v
17639 end
17640 end
17641 local mt=getmetatable(old)
17642 if mt then
17643 setmetatable(new,mt)
17644 end
17645 return new
17646 else
17647 return {}
17648 end
17649end
17650xml.copy=copy
17651function xml.checkbom(root)
17652 if root.ri then
17653 local dt=root.dt
17654 for k=1,#dt do
17655 local v=dt[k]
17656 if type(v)=="table" and v.special and v.tg=="@pi@" and find(v.dt[1],"xml.*version=") then
17657 return
17658 end
17659 end
17660 insert(dt,1,{ special=true,ns="",tg="@pi@",dt={ "xml version='1.0' standalone='yes'" } } )
17661 insert(dt,2,"\n" )
17662 end
17663end
17664local f_attribute=formatters['%s=%q']
17665local function verbose_element(e,handlers,escape)
17666 local handle=handlers.handle
17667 local serialize=handlers.serialize
17668 local ens,etg,eat,edt,ern=e.ns,e.tg,e.at,e.dt,e.rn
17669 local ats=eat and next(eat) and {}
17670 if ats then
17671 local n=0
17672 for k in next,eat do
17673 n=n+1
17674 ats[n]=k
17675 end
17676 if n==1 then
17677 local k=ats[1]
17678 ats=f_attribute(k,escaped(eat[k]))
17679 else
17680 sort(ats)
17681 for i=1,n do
17682 local k=ats[i]
17683 ats[i]=f_attribute(k,escaped(eat[k]))
17684 end
17685 ats=concat(ats," ")
17686 end
17687 end
17688 if ern and trace_entities and ern~=ens then
17689 ens=ern
17690 end
17691 local n=edt and #edt
17692 if ens~="" then
17693 if n and n>0 then
17694 if ats then
17695 handle("<",ens,":",etg," ",ats,">")
17696 else
17697 handle("<",ens,":",etg,">")
17698 end
17699 for i=1,n do
17700 local e=edt[i]
17701 if type(e)=="string" then
17702 handle(escaped(e))
17703 else
17704 serialize(e,handlers)
17705 end
17706 end
17707 handle("</",ens,":",etg,">")
17708 else
17709 if ats then
17710 handle("<",ens,":",etg," ",ats,"/>")
17711 else
17712 handle("<",ens,":",etg,"/>")
17713 end
17714 end
17715 else
17716 if n and n>0 then
17717 if ats then
17718 handle("<",etg," ",ats,">")
17719 else
17720 handle("<",etg,">")
17721 end
17722 for i=1,n do
17723 local e=edt[i]
17724 if type(e)=="string" then
17725 handle(escaped(e))
17726 else
17727 serialize(e,handlers)
17728 end
17729 end
17730 handle("</",etg,">")
17731 else
17732 if ats then
17733 handle("<",etg," ",ats,"/>")
17734 else
17735 handle("<",etg,"/>")
17736 end
17737 end
17738 end
17739end
17740local function verbose_pi(e,handlers)
17741 handlers.handle("<?",e.dt[1],"?>")
17742end
17743local function verbose_comment(e,handlers)
17744 handlers.handle("<!--",e.dt[1],"-->")
17745end
17746local function verbose_cdata(e,handlers)
17747 handlers.handle("<![CDATA[",e.dt[1],"]]>")
17748end
17749local function verbose_doctype(e,handlers)
17750 handlers.handle("<!DOCTYPE",e.dt[1],">")
17751end
17752local function verbose_root(e,handlers)
17753 handlers.serialize(e.dt,handlers)
17754end
17755local function verbose_text(e,handlers)
17756 handlers.handle(escaped(e))
17757end
17758local function verbose_document(e,handlers)
17759 local serialize=handlers.serialize
17760 local functions=handlers.functions
17761 for i=1,#e do
17762 local ei=e[i]
17763 if type(ei)=="string" then
17764 functions["@tx@"](ei,handlers)
17765 else
17766 serialize(ei,handlers)
17767 end
17768 end
17769end
17770local function serialize(e,handlers,...)
17771 if e then
17772 local initialize=handlers.initialize
17773 local finalize=handlers.finalize
17774 local functions=handlers.functions
17775 if initialize then
17776 local state=initialize(...)
17777 if not state==true then
17778 return state
17779 end
17780 end
17781 local etg=e.tg
17782 if etg then
17783 (functions[etg] or functions["@el@"])(e,handlers)
17784 else
17785 functions["@dc@"](e,handlers)
17786 end
17787 if finalize then
17788 return finalize()
17789 end
17790 end
17791end
17792local function xserialize(e,handlers)
17793 if e then
17794 local functions=handlers.functions
17795 local etg=e.tg
17796 if etg then
17797 (functions[etg] or functions["@el@"])(e,handlers)
17798 else
17799 functions["@dc@"](e,handlers)
17800 end
17801 end
17802end
17803local handlers={}
17804local function newhandlers(settings)
17805 local t=table.copy(handlers[settings and settings.parent or "verbose"] or {})
17806 if settings then
17807 for k,v in next,settings do
17808 if type(v)=="table" then
17809 local tk=t[k] if not tk then tk={} t[k]=tk end
17810 for kk,vv in next,v do
17811 tk[kk]=vv
17812 end
17813 else
17814 t[k]=v
17815 end
17816 end
17817 if settings.name then
17818 handlers[settings.name]=t
17819 end
17820 end
17821 utilities.storage.mark(t)
17822 return t
17823end
17824local nofunction=function() end
17825function xml.sethandlersfunction(handler,name,fnc)
17826 handler.functions[name]=fnc or nofunction
17827end
17828function xml.gethandlersfunction(handler,name)
17829 return handler.functions[name]
17830end
17831function xml.gethandlers(name)
17832 return handlers[name]
17833end
17834newhandlers {
17835 name="verbose",
17836 initialize=false,
17837 finalize=false,
17838 serialize=xserialize,
17839 handle=print,
17840 functions={
17841 ["@dc@"]=verbose_document,
17842 ["@dt@"]=verbose_doctype,
17843 ["@rt@"]=verbose_root,
17844 ["@el@"]=verbose_element,
17845 ["@pi@"]=verbose_pi,
17846 ["@cm@"]=verbose_comment,
17847 ["@cd@"]=verbose_cdata,
17848 ["@tx@"]=verbose_text,
17849 }
17850}
17851local result
17852local xmlfilehandler=newhandlers {
17853 name="file",
17854 initialize=function(name)
17855 result=io.open(name,"wb")
17856 return result
17857 end,
17858 finalize=function()
17859 result:close()
17860 return true
17861 end,
17862 handle=function(...)
17863 result:write(...)
17864 end,
17865}
17866function xml.save(root,name)
17867 serialize(root,xmlfilehandler,name)
17868end
17869local result,r,threshold={},0,512
17870local xmlstringhandler=newhandlers {
17871 name="string",
17872 initialize=function()
17873 r=0
17874 return result
17875 end,
17876 finalize=function()
17877 local done=concat(result,"",1,r)
17878 r=0
17879 if r>threshold then
17880 result={}
17881 end
17882 return done
17883 end,
17884 handle=function(...)
17885 for i=1,select("#",...) do
17886 r=r+1
17887 result[r]=select(i,...)
17888 end
17889 end,
17890}
17891local function xmltostring(root)
17892 if not root then
17893 return ""
17894 elseif type(root)=="string" then
17895 return root
17896 else
17897 return serialize(root,xmlstringhandler) or ""
17898 end
17899end
17900local function __tostring(root)
17901 return (root and xmltostring(root)) or ""
17902end
17903initialize_mt=function(root)
17904 mt={ __tostring=__tostring,__index=root }
17905end
17906xml.defaulthandlers=handlers
17907xml.newhandlers=newhandlers
17908xml.serialize=serialize
17909xml.tostring=xmltostring
17910local function xmlstring(e,handle)
17911 if not handle or (e.special and e.tg~="@rt@") then
17912 elseif e.tg then
17913 local edt=e.dt
17914 if edt then
17915 for i=1,#edt do
17916 xmlstring(edt[i],handle)
17917 end
17918 end
17919 else
17920 handle(e)
17921 end
17922end
17923xml.string=xmlstring
17924function xml.settings(e)
17925 while e do
17926 local s=e.settings
17927 if s then
17928 return s
17929 else
17930 e=e.__p__
17931 end
17932 end
17933 return nil
17934end
17935function xml.root(e)
17936 local r=e
17937 while e do
17938 e=e.__p__
17939 if e then
17940 r=e
17941 end
17942 end
17943 return r
17944end
17945function xml.parent(root)
17946 return root.__p__
17947end
17948function xml.body(root)
17949 return root.ri and root.dt[root.ri] or root
17950end
17951function xml.name(root)
17952 if not root then
17953 return ""
17954 end
17955 local ns=root.ns
17956 local tg=root.tg
17957 if ns=="" then
17958 return tg
17959 else
17960 return ns..":"..tg
17961 end
17962end
17963function xml.erase(dt,k)
17964 if dt then
17965 if k then
17966 dt[k]=""
17967 else for k=1,#dt do
17968 dt[1]={ "" }
17969 end end
17970 end
17971end
17972function xml.assign(dt,k,root)
17973 if dt and k then
17974 dt[k]=type(root)=="table" and xml.body(root) or root
17975 return dt[k]
17976 else
17977 return xml.body(root)
17978 end
17979end
17980function xml.tocdata(e,wrapper)
17981 local whatever=type(e)=="table" and xmltostring(e.dt) or e or ""
17982 if wrapper then
17983 whatever=formatters["<%s>%s</%s>"](wrapper,whatever,wrapper)
17984 end
17985 local t={ special=true,ns="",tg="@cd@",at={},rn="",dt={ whatever },__p__=e }
17986 setmetatable(t,getmetatable(e))
17987 e.dt={ t }
17988end
17989function xml.makestandalone(root)
17990 if root.ri then
17991 local dt=root.dt
17992 for k=1,#dt do
17993 local v=dt[k]
17994 if type(v)=="table" and v.special and v.tg=="@pi@" then
17995 local txt=v.dt[1]
17996 if find(txt,"xml.*version=") then
17997 v.dt[1]=txt.." standalone='yes'"
17998 break
17999 end
18000 end
18001 end
18002 end
18003 return root
18004end
18005function xml.kind(e)
18006 local dt=e and e.dt
18007 if dt then
18008 local n=#dt
18009 if n==1 then
18010 local d=dt[1]
18011 if d.special then
18012 local tg=d.tg
18013 if tg=="@cd@" then
18014 return "cdata"
18015 elseif tg=="@cm@" then
18016 return "comment"
18017 elseif tg=="@pi@" then
18018 return "instruction"
18019 elseif tg=="@dt@" then
18020 return "declaration"
18021 end
18022 elseif type(d)=="string" then
18023 return "text"
18024 end
18025 return "element"
18026 elseif n>0 then
18027 return "mixed"
18028 end
18029 end
18030 return "empty"
18031end
18032
18033
18034end
18035
18036do
18037
18038package.loaded["lxml-lpt"] = package.loaded["lxml-lpt"] or true
18039
18040
18041
18042if not modules then modules={} end modules ['lxml-lpt']={
18043 version=1.001,
18044 comment="this module is the basis for the lxml-* ones",
18045 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
18046 copyright="PRAGMA ADE / ConTeXt Development Team",
18047 license="see context related readme files"
18048}
18049local concat,remove,insert=table.concat,table.remove,table.insert
18050local type,next,tonumber,tostring,setmetatable,load,select=type,next,tonumber,tostring,setmetatable,load,select
18051local format,upper,lower,gmatch,gsub,find,rep=string.format,string.upper,string.lower,string.gmatch,string.gsub,string.find,string.rep
18052local lpegmatch,lpegpatterns=lpeg.match,lpeg.patterns
18053local setmetatableindex=table.setmetatableindex
18054local formatters=string.formatters
18055local trace_lpath=false
18056local trace_lparse=false
18057local trace_lprofile=false
18058local report_lpath=logs.reporter("xml","lpath")
18059if trackers then
18060 trackers.register("xml.path",function(v)
18061 trace_lpath=v
18062 end)
18063 trackers.register("xml.parse",function(v)
18064 trace_lparse=v
18065 end)
18066 trackers.register("xml.profile",function(v)
18067 trace_lpath=v
18068 trace_lparse=v
18069 trace_lprofile=v
18070 end)
18071end
18072local xml=xml
18073local lpathcalls=0 function xml.lpathcalls () return lpathcalls end
18074local lpathcached=0 function xml.lpathcached() return lpathcached end
18075xml.functions=xml.functions or {}
18076local functions=xml.functions
18077xml.expressions=xml.expressions or {}
18078local expressions=xml.expressions
18079xml.finalizers=xml.finalizers or {}
18080local finalizers=xml.finalizers
18081xml.specialhandler=xml.specialhandler or {}
18082local specialhandler=xml.specialhandler
18083lpegpatterns.xml=lpegpatterns.xml or {}
18084local xmlpatterns=lpegpatterns.xml
18085finalizers.xml=finalizers.xml or {}
18086finalizers.tex=finalizers.tex or {}
18087local function fallback (t,name)
18088 local fn=finalizers[name]
18089 if fn then
18090 t[name]=fn
18091 else
18092 report_lpath("unknown sub finalizer %a",name)
18093 fn=function() end
18094 end
18095 return fn
18096end
18097setmetatableindex(finalizers.xml,fallback)
18098setmetatableindex(finalizers.tex,fallback)
18099xml.defaultprotocol="xml"
18100local apply_axis={}
18101apply_axis['root']=function(list)
18102 local collected={}
18103 for l=1,#list do
18104 local ll=list[l]
18105 local rt=ll
18106 while ll do
18107 ll=ll.__p__
18108 if ll then
18109 rt=ll
18110 end
18111 end
18112 collected[l]=rt
18113 end
18114 return collected
18115end
18116apply_axis['self']=function(list)
18117 return list
18118end
18119apply_axis['child']=function(list)
18120 local collected={}
18121 local c=0
18122 for l=1,#list do
18123 local ll=list[l]
18124 local dt=ll.dt
18125 if dt then
18126 local n=#dt
18127 if n==0 then
18128 ll.en=0
18129 elseif n==1 then
18130 local dk=dt[1]
18131 if dk.tg then
18132 c=c+1
18133 collected[c]=dk
18134 dk.ni=1
18135 dk.ei=1
18136 ll.en=1
18137 end
18138 else
18139 local en=0
18140 for k=1,#dt do
18141 local dk=dt[k]
18142 if dk.tg then
18143 c=c+1
18144 en=en+1
18145 collected[c]=dk
18146 dk.ni=k
18147 dk.ei=en
18148 end
18149 end
18150 ll.en=en
18151 end
18152 end
18153 end
18154 return collected
18155end
18156local function collect(list,collected,c)
18157 local dt=list.dt
18158 if dt then
18159 local n=#dt
18160 if n==0 then
18161 list.en=0
18162 elseif n==1 then
18163 local dk=dt[1]
18164 if dk.tg then
18165 c=c+1
18166 collected[c]=dk
18167 dk.ni=1
18168 dk.ei=1
18169 c=collect(dk,collected,c)
18170 list.en=1
18171 else
18172 list.en=0
18173 end
18174 else
18175 local en=0
18176 for k=1,n do
18177 local dk=dt[k]
18178 if dk.tg then
18179 c=c+1
18180 en=en+1
18181 collected[c]=dk
18182 dk.ni=k
18183 dk.ei=en
18184 c=collect(dk,collected,c)
18185 end
18186 end
18187 list.en=en
18188 end
18189 end
18190 return c
18191end
18192apply_axis['descendant']=function(list)
18193 local collected={}
18194 local c=0
18195 for l=1,#list do
18196 c=collect(list[l],collected,c)
18197 end
18198 return collected
18199end
18200local function collect(list,collected,c)
18201 local dt=list.dt
18202 if dt then
18203 local n=#dt
18204 if n==0 then
18205 list.en=0
18206 elseif n==1 then
18207 local dk=dt[1]
18208 if dk.tg then
18209 c=c+1
18210 collected[c]=dk
18211 dk.ni=1
18212 dk.ei=1
18213 c=collect(dk,collected,c)
18214 list.en=1
18215 end
18216 else
18217 local en=0
18218 for k=1,#dt do
18219 local dk=dt[k]
18220 if dk.tg then
18221 c=c+1
18222 en=en+1
18223 collected[c]=dk
18224 dk.ni=k
18225 dk.ei=en
18226 c=collect(dk,collected,c)
18227 end
18228 end
18229 list.en=en
18230 end
18231 end
18232 return c
18233end
18234apply_axis['descendant-or-self']=function(list)
18235 local collected={}
18236 local c=0
18237 for l=1,#list do
18238 local ll=list[l]
18239 if ll.special~=true then
18240 c=c+1
18241 collected[c]=ll
18242 end
18243 c=collect(ll,collected,c)
18244 end
18245 return collected
18246end
18247apply_axis['ancestor']=function(list)
18248 local collected={}
18249 local c=0
18250 for l=1,#list do
18251 local ll=list[l]
18252 while ll do
18253 ll=ll.__p__
18254 if ll then
18255 c=c+1
18256 collected[c]=ll
18257 end
18258 end
18259 end
18260 return collected
18261end
18262apply_axis['ancestor-or-self']=function(list)
18263 local collected={}
18264 local c=0
18265 for l=1,#list do
18266 local ll=list[l]
18267 c=c+1
18268 collected[c]=ll
18269 while ll do
18270 ll=ll.__p__
18271 if ll then
18272 c=c+1
18273 collected[c]=ll
18274 end
18275 end
18276 end
18277 return collected
18278end
18279apply_axis['parent']=function(list)
18280 local collected={}
18281 local c=0
18282 for l=1,#list do
18283 local pl=list[l].__p__
18284 if pl then
18285 c=c+1
18286 collected[c]=pl
18287 end
18288 end
18289 return collected
18290end
18291apply_axis['attribute']=function(list)
18292 return {}
18293end
18294apply_axis['namespace']=function(list)
18295 return {}
18296end
18297apply_axis['following']=function(list)
18298 return {}
18299end
18300apply_axis['preceding']=function(list)
18301 return {}
18302end
18303apply_axis['following-sibling']=function(list)
18304 local collected={}
18305 local c=0
18306 for l=1,#list do
18307 local ll=list[l]
18308 local p=ll.__p__
18309 local d=p.dt
18310 for i=ll.ni+1,#d do
18311 local di=d[i]
18312 if type(di)=="table" then
18313 c=c+1
18314 collected[c]=di
18315 end
18316 end
18317 end
18318 return collected
18319end
18320apply_axis['preceding-sibling']=function(list)
18321 local collected={}
18322 local c=0
18323 for l=1,#list do
18324 local ll=list[l]
18325 local p=ll.__p__
18326 local d=p.dt
18327 for i=1,ll.ni-1 do
18328 local di=d[i]
18329 if type(di)=="table" then
18330 c=c+1
18331 collected[c]=di
18332 end
18333 end
18334 end
18335 return collected
18336end
18337apply_axis['reverse-sibling']=function(list)
18338 local collected={}
18339 local c=0
18340 for l=1,#list do
18341 local ll=list[l]
18342 local p=ll.__p__
18343 local d=p.dt
18344 for i=ll.ni-1,1,-1 do
18345 local di=d[i]
18346 if type(di)=="table" then
18347 c=c+1
18348 collected[c]=di
18349 end
18350 end
18351 end
18352 return collected
18353end
18354apply_axis['auto-descendant-or-self']=apply_axis['descendant-or-self']
18355apply_axis['auto-descendant']=apply_axis['descendant']
18356apply_axis['auto-child']=apply_axis['child']
18357apply_axis['auto-self']=apply_axis['self']
18358apply_axis['initial-child']=apply_axis['child']
18359local function apply_nodes(list,directive,nodes)
18360 local maxn=#nodes
18361 if maxn==3 then
18362 local nns=nodes[2]
18363 local ntg=nodes[3]
18364 if not nns and not ntg then
18365 if directive then
18366 return list
18367 else
18368 return {}
18369 end
18370 else
18371 local collected={}
18372 local c=0
18373 local m=0
18374 local p=nil
18375 if not nns then
18376 for l=1,#list do
18377 local ll=list[l]
18378 local ltg=ll.tg
18379 if ltg then
18380 if directive then
18381 if ntg==ltg then
18382 local llp=ll.__p__;if llp~=p then p=llp;m=1 else m=m+1 end
18383 c=c+1
18384 collected[c]=ll
18385 ll.mi=m
18386 end
18387 elseif ntg~=ltg then
18388 local llp=ll.__p__;if llp~=p then p=llp;m=1 else m=m+1 end
18389 c=c+1
18390 collected[c]=ll
18391 ll.mi=m
18392 end
18393 end
18394 end
18395 elseif not ntg then
18396 for l=1,#list do
18397 local ll=list[l]
18398 local lns=ll.rn or ll.ns
18399 if lns then
18400 if directive then
18401 if lns==nns then
18402 local llp=ll.__p__;if llp~=p then p=llp;m=1 else m=m+1 end
18403 c=c+1
18404 collected[c]=ll
18405 ll.mi=m
18406 end
18407 elseif lns~=nns then
18408 local llp=ll.__p__;if llp~=p then p=llp;m=1 else m=m+1 end
18409 c=c+1
18410 collected[c]=ll
18411 ll.mi=m
18412 end
18413 end
18414 end
18415 else
18416 for l=1,#list do
18417 local ll=list[l]
18418 local ltg=ll.tg
18419 if ltg then
18420 local lns=ll.rn or ll.ns
18421 local ok=ltg==ntg and lns==nns
18422 if directive then
18423 if ok then
18424 local llp=ll.__p__;if llp~=p then p=llp;m=1 else m=m+1 end
18425 c=c+1
18426 collected[c]=ll
18427 ll.mi=m
18428 end
18429 elseif not ok then
18430 local llp=ll.__p__;if llp~=p then p=llp;m=1 else m=m+1 end
18431 c=c+1
18432 collected[c]=ll
18433 ll.mi=m
18434 end
18435 end
18436 end
18437 end
18438 return collected
18439 end
18440 else
18441 local collected={}
18442 local c=0
18443 local m=0
18444 local p=nil
18445 for l=1,#list do
18446 local ll=list[l]
18447 local ltg=ll.tg
18448 if ltg then
18449 local lns=ll.rn or ll.ns
18450 local ok=false
18451 for n=1,maxn,3 do
18452 local nns=nodes[n+1]
18453 local ntg=nodes[n+2]
18454 ok=(not ntg or ltg==ntg) and (not nns or lns==nns)
18455 if ok then
18456 break
18457 end
18458 end
18459 if directive then
18460 if ok then
18461 local llp=ll.__p__;if llp~=p then p=llp;m=1 else m=m+1 end
18462 c=c+1
18463 collected[c]=ll
18464 ll.mi=m
18465 end
18466 elseif not ok then
18467 local llp=ll.__p__;if llp~=p then p=llp;m=1 else m=m+1 end
18468 c=c+1
18469 collected[c]=ll
18470 ll.mi=m
18471 end
18472 end
18473 end
18474 return collected
18475 end
18476end
18477local quit_expression=false
18478local function apply_expression(list,expression,order)
18479 local collected={}
18480 local c=0
18481 quit_expression=false
18482 for l=1,#list do
18483 local ll=list[l]
18484 if expression(list,ll,l,order) then
18485 c=c+1
18486 collected[c]=ll
18487 end
18488 if quit_expression then
18489 break
18490 end
18491 end
18492 return collected
18493end
18494local function apply_selector(list,specification)
18495 if xml.applyselector then
18496 apply_selector=xml.applyselector
18497 return apply_selector(list,specification)
18498 else
18499 return list
18500 end
18501end
18502local P,V,C,Cs,Cc,Ct,R,S,Cg,Cb=lpeg.P,lpeg.V,lpeg.C,lpeg.Cs,lpeg.Cc,lpeg.Ct,lpeg.R,lpeg.S,lpeg.Cg,lpeg.Cb
18503local spaces=S(" \n\r\t\f")^0
18504local lp_space=S(" \n\r\t\f")
18505local lp_any=P(1)
18506local lp_noequal=P("!=")/"~="+P("<=")+P(">=")+P("==")
18507local lp_doequal=P("=")/"=="
18508local lp_or=P("|")/" or "
18509local lp_and=P("&")/" and "
18510local builtin={
18511 text="(ll.dt[1] or '')",
18512 content="ll.dt",
18513 name="((ll.ns~='' and ll.ns..':'..ll.tg) or ll.tg)",
18514 tag="ll.tg",
18515 position="l",
18516 firstindex="1",
18517 firstelement="1",
18518 first="1",
18519 lastindex="(#ll.__p__.dt or 1)",
18520 lastelement="(ll.__p__.en or 1)",
18521 last="#list",
18522 list="list",
18523 self="ll",
18524 rootposition="order",
18525 order="order",
18526 element="(ll.ei or 1)",
18527 index="(ll.ni or 1)",
18528 match="(ll.mi or 1)",
18529 namespace="ll.ns",
18530 ns="ll.ns",
18531}
18532local lp_builtin=lpeg.utfchartabletopattern(builtin)/builtin*((spaces*P("(")*spaces*P(")"))/"")
18533local lp_attribute=(P("@")+P("attribute::"))/""*Cc("(ll.at and ll.at['")*((R("az","AZ")+S("-_:"))^1)*Cc("'])")
18534local lp_fastpos_p=P("+")^0*R("09")^1*P(-1)/"l==%0"
18535local lp_fastpos_n=P("-")*R("09")^1*P(-1)/"(%0<0 and (#list+%0+1==l))"
18536local lp_fastpos=lp_fastpos_n+lp_fastpos_p
18537local lp_reserved=C("and")+C("or")+C("not")+C("div")+C("mod")+C("true")+C("false")
18538local lp_lua_function=Cs((R("az","AZ","__")^1*(P(".")*R("az","AZ","__")^1)^1)*("("))/"%0"
18539local lp_function=C(R("az","AZ","__")^1)*P("(")/function(t)
18540 if expressions[t] then
18541 return "expr."..t.."("
18542 else
18543 return "expr.error("
18544 end
18545end
18546local lparent=P("(")
18547local rparent=P(")")
18548local noparent=1-(lparent+rparent)
18549local nested=P{lparent*(noparent+V(1))^0*rparent}
18550local value=P(lparent*C((noparent+nested)^0)*rparent)
18551local lp_child=Cc("expr.child(ll,'")*R("az","AZ")*R("az","AZ","--","__")^0*Cc("')")
18552local lp_number=S("+-")*R("09")^1
18553local lp_string=Cc("'")*R("az","AZ","--","__")^1*Cc("'")
18554local lp_content=(P("'")*(1-P("'"))^0*P("'")+P('"')*(1-P('"'))^0*P('"'))
18555local cleaner
18556local lp_special=(C(P("name")+P("text")+P("tag")+P("count")+P("child")))*value/function(t,s)
18557 if expressions[t] then
18558 s=s and s~="" and lpegmatch(cleaner,s)
18559 if s and s~="" then
18560 return "expr."..t.."(ll,"..s..")"
18561 else
18562 return "expr."..t.."(ll)"
18563 end
18564 else
18565 return "expr.error("..t..")"
18566 end
18567end
18568local content=lp_builtin+lp_attribute+lp_special+lp_noequal+lp_doequal+lp_or+lp_and+lp_reserved+lp_lua_function+lp_function+lp_content+
18569 lp_child+lp_any
18570local converter=Cs (
18571 lp_fastpos+(P { lparent*(V(1))^0*rparent+content } )^0
18572)
18573cleaner=Cs ((
18574 lp_reserved+lp_number+lp_string+1 )^1 )
18575local template_e=[[
18576 local expr = xml.expressions
18577 return function(list,ll,l,order)
18578 return %s
18579 end
18580]]
18581local template_f_y=[[
18582 local finalizer = xml.finalizers['%s']['%s']
18583 return function(collection)
18584 return finalizer(collection,%s)
18585 end
18586]]
18587local template_f_n=[[
18588 return xml.finalizers['%s']['%s']
18589]]
18590local register_last_match={ kind="axis",axis="last-match" }
18591local register_self={ kind="axis",axis="self" }
18592local register_parent={ kind="axis",axis="parent" }
18593local register_descendant={ kind="axis",axis="descendant" }
18594local register_child={ kind="axis",axis="child" }
18595local register_descendant_or_self={ kind="axis",axis="descendant-or-self" }
18596local register_root={ kind="axis",axis="root" }
18597local register_ancestor={ kind="axis",axis="ancestor" }
18598local register_ancestor_or_self={ kind="axis",axis="ancestor-or-self" }
18599local register_attribute={ kind="axis",axis="attribute" }
18600local register_namespace={ kind="axis",axis="namespace" }
18601local register_following={ kind="axis",axis="following" }
18602local register_following_sibling={ kind="axis",axis="following-sibling" }
18603local register_preceding={ kind="axis",axis="preceding" }
18604local register_preceding_sibling={ kind="axis",axis="preceding-sibling" }
18605local register_reverse_sibling={ kind="axis",axis="reverse-sibling" }
18606local register_auto_descendant_or_self={ kind="axis",axis="auto-descendant-or-self" }
18607local register_auto_descendant={ kind="axis",axis="auto-descendant" }
18608local register_auto_self={ kind="axis",axis="auto-self" }
18609local register_auto_child={ kind="axis",axis="auto-child" }
18610local register_initial_child={ kind="axis",axis="initial-child" }
18611local register_all_nodes={ kind="nodes",nodetest=true,nodes={ true,false,false } }
18612local skip={}
18613local function errorrunner_e(str,cnv)
18614 if not skip[str] then
18615 report_lpath("error in expression: %s => %s",str,cnv)
18616 skip[str]=cnv or str
18617 end
18618 return false
18619end
18620local function errorrunner_f(str,arg)
18621 report_lpath("error in finalizer: %s(%s)",str,arg or "")
18622 return false
18623end
18624local function register_nodes(nodetest,nodes)
18625 return { kind="nodes",nodetest=nodetest,nodes=nodes }
18626end
18627local function register_selector(specification)
18628 return { kind="selector",specification=specification }
18629end
18630local function register_expression(expression)
18631 local converted=lpegmatch(converter,expression)
18632 local wrapped=format(template_e,converted)
18633 local runner=load(wrapped)
18634 runner=(runner and runner()) or function() errorrunner_e(expression,converted) end
18635 return { kind="expression",expression=expression,converted=converted,evaluator=runner }
18636end
18637local function register_finalizer(protocol,name,arguments)
18638 local runner
18639 if arguments and arguments~="" then
18640 runner=load(format(template_f_y,protocol or xml.defaultprotocol,name,arguments))
18641 else
18642 runner=load(format(template_f_n,protocol or xml.defaultprotocol,name))
18643 end
18644 runner=(runner and runner()) or function() errorrunner_f(name,arguments) end
18645 return { kind="finalizer",name=name,arguments=arguments,finalizer=runner }
18646end
18647local expression=P { "ex",
18648 ex="["*C((V("sq")+V("dq")+(1-S("[]"))+V("ex"))^0)*"]",
18649 sq="'"*(1-S("'"))^0*"'",
18650 dq='"'*(1-S('"'))^0*'"',
18651}
18652local arguments=P { "ar",
18653 ar="("*Cs((V("sq")+V("dq")+V("nq")+P(1-P(")")))^0)*")",
18654 nq=((1-S("),'\""))^1)/function(s) return format("%q",s) end,
18655 sq=P("'")*(1-P("'"))^0*P("'"),
18656 dq=P('"')*(1-P('"'))^0*P('"'),
18657}
18658local function register_error(str)
18659 return { kind="error",error=format("unparsed: %s",str) }
18660end
18661local special_1=P("*")*Cc(register_auto_descendant)*Cc(register_all_nodes)
18662local special_2=P("/")*Cc(register_auto_self)
18663local special_3=P("")*Cc(register_auto_self)
18664local no_nextcolon=P(-1)+#(1-P(":"))
18665local no_nextlparent=P(-1)+#(1-P("("))
18666local pathparser=Ct { "patterns",
18667 patterns=spaces*V("protocol")*spaces*(
18668 (V("special")*spaces*P(-1) )+(V("initial")*spaces*V("step")*spaces*(P("/")*spaces*V("step")*spaces)^0 )
18669 ),
18670 protocol=Cg(V("letters"),"protocol")*P("://")+Cg(Cc(nil),"protocol"),
18671 step=((V("shortcuts")+V("selector")+P("/")+V("axis"))*spaces*V("nodes")^0+V("error"))*spaces*V("expressions")^0*spaces*V("finalizer")^0,
18672 axis=V("last_match")+V("descendant")+V("child")+V("parent")+V("self")+V("root")+V("ancestor")+V("descendant_or_self")+V("following_sibling")+V("following")+V("reverse_sibling")+V("preceding_sibling")+V("preceding")+V("ancestor_or_self")+#(1-P(-1))*Cc(register_auto_child),
18673 special=special_1+special_2+special_3,
18674 initial=(P("/")*spaces*Cc(register_initial_child))^-1,
18675 error=(P(1)^1)/register_error,
18676 shortcuts_a=V("s_descendant_or_self")+V("s_descendant")+V("s_child")+V("s_parent")+V("s_self")+V("s_root")+V("s_ancestor")+V("s_lastmatch"),
18677 shortcuts=V("shortcuts_a")*(spaces*"/"*spaces*V("shortcuts_a"))^0,
18678 s_descendant_or_self=(P("***/")+P("/"))*Cc(register_descendant_or_self),
18679 s_descendant=P("**")*Cc(register_descendant),
18680 s_child=P("*")*no_nextcolon*Cc(register_child),
18681 s_parent=P("..")*Cc(register_parent),
18682 s_self=P("." )*Cc(register_self),
18683 s_root=P("^^")*Cc(register_root),
18684 s_ancestor=P("^")*Cc(register_ancestor),
18685 s_lastmatch=P("=")*Cc(register_last_match),
18686 descendant=P("descendant::")*Cc(register_descendant),
18687 child=P("child::")*Cc(register_child),
18688 parent=P("parent::")*Cc(register_parent),
18689 self=P("self::")*Cc(register_self),
18690 root=P('root::')*Cc(register_root),
18691 ancestor=P('ancestor::')*Cc(register_ancestor),
18692 descendant_or_self=P('descendant-or-self::')*Cc(register_descendant_or_self),
18693 ancestor_or_self=P('ancestor-or-self::')*Cc(register_ancestor_or_self),
18694 following=P('following::')*Cc(register_following),
18695 following_sibling=P('following-sibling::')*Cc(register_following_sibling),
18696 preceding=P('preceding::')*Cc(register_preceding),
18697 preceding_sibling=P('preceding-sibling::')*Cc(register_preceding_sibling),
18698 reverse_sibling=P('reverse-sibling::')*Cc(register_reverse_sibling),
18699 last_match=P('last-match::')*Cc(register_last_match),
18700 selector=P("{")*C((1-P("}"))^1)*P("}")/register_selector,
18701 nodes=(V("nodefunction")*spaces*P("(")*V("nodeset")*P(")")+V("nodetest")*V("nodeset"))/register_nodes,
18702 expressions=expression/register_expression,
18703 letters=R("az")^1,
18704 name=(1-S("/[]()|:*!"))^1,
18705 negate=P("!")*Cc(false),
18706 nodefunction=V("negate")+P("not")*Cc(false)+Cc(true),
18707 nodetest=V("negate")+Cc(true),
18708 nodename=(V("negate")+Cc(true))*spaces*((V("wildnodename")*P(":")*V("wildnodename"))+(Cc(false)*V("wildnodename"))),
18709 wildnodename=(C(V("name"))+P("*")*Cc(false))*no_nextlparent,
18710 nodeset=spaces*Ct(V("nodename")*(spaces*P("|")*spaces*V("nodename"))^0)*spaces,
18711 finalizer=(Cb("protocol")*P("/")^-1*C(V("name"))*arguments*P(-1))/register_finalizer,
18712}
18713xmlpatterns.pathparser=pathparser
18714local cache={}
18715local function nodesettostring(set,nodetest)
18716 local t={}
18717 for i=1,#set,3 do
18718 local directive,ns,tg=set[i],set[i+1],set[i+2]
18719 if not ns or ns=="" then ns="*" end
18720 if not tg or tg=="" then tg="*" end
18721 tg=(tg=="@rt@" and "[root]") or format("%s:%s",ns,tg)
18722 t[#t+1]=(directive and tg) or format("not(%s)",tg)
18723 end
18724 if nodetest==false then
18725 return format("not(%s)",concat(t,"|"))
18726 else
18727 return concat(t,"|")
18728 end
18729end
18730local function tagstostring(list)
18731 if #list==0 then
18732 return "no elements"
18733 else
18734 local t={}
18735 for i=1,#list do
18736 local li=list[i]
18737 local ns=li.ns
18738 local tg=li.tg
18739 if not ns or ns=="" then ns="*" end
18740 if not tg or tg=="" then tg="*" end
18741 t[i]=(tg=="@rt@" and "[root]") or format("%s:%s",ns,tg)
18742 end
18743 return concat(t," ")
18744 end
18745end
18746xml.nodesettostring=nodesettostring
18747local lpath
18748local function lshow(parsed)
18749 if type(parsed)=="string" then
18750 parsed=lpath(parsed)
18751 end
18752 report_lpath("%s://%s => %s",parsed.protocol or xml.defaultprotocol,parsed.pattern,
18753 table.serialize(parsed,false))
18754end
18755xml.lshow=lshow
18756local function add_comment(p,str)
18757 local pc=p.comment
18758 if not pc then
18759 p.comment={ str }
18760 else
18761 pc[#pc+1]=str
18762 end
18763end
18764lpath=function (pattern)
18765 lpathcalls=lpathcalls+1
18766 if type(pattern)=="table" then
18767 return pattern
18768 else
18769 local parsed=cache[pattern]
18770 if parsed then
18771 lpathcached=lpathcached+1
18772 else
18773 parsed=lpegmatch(pathparser,pattern)
18774 if parsed then
18775 parsed.pattern=pattern
18776 local np=#parsed
18777 if np==0 then
18778 parsed={ pattern=pattern,register_self,state="parsing error" }
18779 report_lpath("parsing error in pattern: %s",pattern)
18780 lshow(parsed)
18781 else
18782 local pi=parsed[1]
18783 if pi.axis=="auto-child" then
18784 if false then
18785 add_comment(parsed,"auto-child replaced by auto-descendant-or-self")
18786 parsed[1]=register_auto_descendant_or_self
18787 else
18788 add_comment(parsed,"auto-child replaced by auto-descendant")
18789 parsed[1]=register_auto_descendant
18790 end
18791 elseif pi.axis=="initial-child" and np>1 and parsed[2].axis then
18792 add_comment(parsed,"initial-child removed")
18793 remove(parsed,1)
18794 end
18795 local np=#parsed
18796 if np>1 then
18797 local pnp=parsed[np]
18798 if pnp.kind=="nodes" and pnp.nodetest==true then
18799 local nodes=pnp.nodes
18800 if nodes[1]==true and nodes[2]==false and nodes[3]==false then
18801 add_comment(parsed,"redundant final wildcard filter removed")
18802 remove(parsed,np)
18803 end
18804 end
18805 end
18806 end
18807 else
18808 parsed={ pattern=pattern }
18809 end
18810 cache[pattern]=parsed
18811 if trace_lparse and not trace_lprofile then
18812 lshow(parsed)
18813 end
18814 end
18815 return parsed
18816 end
18817end
18818xml.lpath=lpath
18819do
18820 local profiled={}
18821 xml.profiled=profiled
18822 local lastmatch=nil
18823 local keepmatch=nil
18824 if directives then
18825 directives.register("xml.path.keeplastmatch",function(v)
18826 keepmatch=v
18827 lastmatch=nil
18828 end)
18829 end
18830 apply_axis["last-match"]=function()
18831 return lastmatch or {}
18832 end
18833 local function profiled_apply(list,parsed,nofparsed,order)
18834 local p=profiled[parsed.pattern]
18835 if p then
18836 p.tested=p.tested+1
18837 else
18838 p={ tested=1,matched=0,finalized=0 }
18839 profiled[parsed.pattern]=p
18840 end
18841 local collected=list
18842 for i=1,nofparsed do
18843 local pi=parsed[i]
18844 local kind=pi.kind
18845 if kind=="axis" then
18846 collected=apply_axis[pi.axis](collected)
18847 elseif kind=="nodes" then
18848 collected=apply_nodes(collected,pi.nodetest,pi.nodes)
18849 elseif kind=="expression" then
18850 collected=apply_expression(collected,pi.evaluator,order)
18851 elseif kind=="selector" then
18852 collected=apply_selector(collected,pi.specification)
18853 elseif kind=="finalizer" then
18854 collected=pi.finalizer(collected)
18855 p.matched=p.matched+1
18856 p.finalized=p.finalized+1
18857 return collected
18858 end
18859 if not collected or #collected==0 then
18860 local pn=i<nofparsed and parsed[nofparsed]
18861 if pn and pn.kind=="finalizer" then
18862 collected=pn.finalizer(collected)
18863 p.finalized=p.finalized+1
18864 return collected
18865 end
18866 return nil
18867 end
18868 end
18869 if collected then
18870 p.matched=p.matched+1
18871 end
18872 return collected
18873 end
18874 local function traced_apply(list,parsed,nofparsed,order)
18875 if trace_lparse then
18876 lshow(parsed)
18877 end
18878 report_lpath("collecting: %s",parsed.pattern)
18879 report_lpath("root tags : %s",tagstostring(list))
18880 report_lpath("order : %s",order or "unset")
18881 local collected=list
18882 for i=1,nofparsed do
18883 local pi=parsed[i]
18884 local kind=pi.kind
18885 if kind=="axis" then
18886 collected=apply_axis[pi.axis](collected)
18887 report_lpath("% 10i : ax : %s",(collected and #collected) or 0,pi.axis)
18888 elseif kind=="nodes" then
18889 collected=apply_nodes(collected,pi.nodetest,pi.nodes)
18890 report_lpath("% 10i : ns : %s",(collected and #collected) or 0,nodesettostring(pi.nodes,pi.nodetest))
18891 elseif kind=="expression" then
18892 collected=apply_expression(collected,pi.evaluator,order)
18893 report_lpath("% 10i : ex : %s -> %s",(collected and #collected) or 0,pi.expression,pi.converted)
18894 elseif kind=="selector" then
18895 collected=apply_selector(collected,pi.specification)
18896 report_lpath("% 10i : se : %s ",(collected and #collected) or 0,pi.specification)
18897 elseif kind=="finalizer" then
18898 collected=pi.finalizer(collected)
18899 report_lpath("% 10i : fi : %s : %s(%s)",(type(collected)=="table" and #collected) or 0,parsed.protocol or xml.defaultprotocol,pi.name,pi.arguments or "")
18900 return collected
18901 end
18902 if not collected or #collected==0 then
18903 local pn=i<nofparsed and parsed[nofparsed]
18904 if pn and pn.kind=="finalizer" then
18905 collected=pn.finalizer(collected)
18906 report_lpath("% 10i : fi : %s : %s(%s)",(type(collected)=="table" and #collected) or 0,parsed.protocol or xml.defaultprotocol,pn.name,pn.arguments or "")
18907 return collected
18908 end
18909 return nil
18910 end
18911 end
18912 return collected
18913 end
18914 local function normal_apply(list,parsed,nofparsed,order)
18915 local collected=list
18916 for i=1,nofparsed do
18917 local pi=parsed[i]
18918 local kind=pi.kind
18919 if kind=="axis" then
18920 local axis=pi.axis
18921 if axis~="self" then
18922 collected=apply_axis[axis](collected)
18923 end
18924 elseif kind=="nodes" then
18925 collected=apply_nodes(collected,pi.nodetest,pi.nodes)
18926 elseif kind=="expression" then
18927 collected=apply_expression(collected,pi.evaluator,order)
18928 elseif kind=="selector" then
18929 collected=apply_selector(collected,pi.specification)
18930 elseif kind=="finalizer" then
18931 return pi.finalizer(collected)
18932 end
18933 if not collected or #collected==0 then
18934 local pf=i<nofparsed and parsed[nofparsed].finalizer
18935 if pf then
18936 return pf(collected)
18937 end
18938 return nil
18939 end
18940 end
18941 return collected
18942 end
18943 local apply=normal_apply
18944 if trackers then
18945 trackers.register("xml.path,xml.parse,xml.profile",function()
18946 if trace_lprofile then
18947 apply=profiled_apply
18948 elseif trace_lpath then
18949 apply=traced_apply
18950 else
18951 apply=normal_apply
18952 end
18953 end)
18954 end
18955 function xml.applylpath(list,pattern)
18956 if not list then
18957 lastmatch=nil
18958 return
18959 end
18960 local parsed=cache[pattern]
18961 if parsed then
18962 lpathcalls=lpathcalls+1
18963 lpathcached=lpathcached+1
18964 elseif type(pattern)=="table" then
18965 lpathcalls=lpathcalls+1
18966 parsed=pattern
18967 else
18968 parsed=lpath(pattern) or pattern
18969 end
18970 if not parsed then
18971 lastmatch=nil
18972 return
18973 end
18974 local nofparsed=#parsed
18975 if nofparsed==0 then
18976 lastmatch=nil
18977 return
18978 end
18979 local collected=apply({ list },parsed,nofparsed,list.mi)
18980 lastmatch=keepmatch and collected or nil
18981 return collected
18982 end
18983 function xml.lastmatch()
18984 return lastmatch
18985 end
18986 local stack={}
18987 function xml.pushmatch()
18988 insert(stack,lastmatch)
18989 end
18990 function xml.popmatch()
18991 lastmatch=remove(stack)
18992 end
18993end
18994local applylpath=xml.applylpath
18995function xml.filter(root,pattern)
18996 return applylpath(root,pattern)
18997end
18998expressions.child=function(e,pattern)
18999 return applylpath(e,pattern)
19000end
19001expressions.count=function(e,pattern)
19002 local collected=applylpath(e,pattern)
19003 return pattern and (collected and #collected) or 0
19004end
19005expressions.attribute=function(e,name,value)
19006 if type(e)=="table" and name then
19007 local a=e.at
19008 if a then
19009 local v=a[name]
19010 if value then
19011 return v==value
19012 else
19013 return v
19014 end
19015 end
19016 end
19017 return nil
19018end
19019expressions.oneof=function(s,...)
19020 for i=1,select("#",...) do
19021 if s==select(i,...) then
19022 return true
19023 end
19024 end
19025 return false
19026end
19027expressions.error=function(str)
19028 xml.errorhandler(format("unknown function in lpath expression: %s",tostring(str or "?")))
19029 return false
19030end
19031expressions.undefined=function(s)
19032 return s==nil
19033end
19034expressions.quit=function(s)
19035 if s or s==nil then
19036 quit_expression=true
19037 end
19038 return true
19039end
19040expressions.print=function(...)
19041 print(...)
19042 return true
19043end
19044expressions.find=function(str,...)
19045 return str and find(str,...)
19046end
19047expressions.upper=function(str) return str and upper(str) or "" end
19048expressions.lower=function(str) return str and lower(str) or "" end
19049expressions.number=tonumber
19050expressions.boolean=toboolean
19051function expressions.contains(str,pattern)
19052 local t=type(str)
19053 if t=="string" then
19054 if find(str,pattern) then
19055 return true
19056 end
19057 elseif t=="table" then
19058 for i=1,#str do
19059 local d=str[i]
19060 if type(d)=="string" and find(d,pattern) then
19061 return true
19062 end
19063 end
19064 end
19065 return false
19066end
19067function expressions.idstring(str)
19068 return type(str)=="string" and gsub(str,"^#","") or ""
19069end
19070local function traverse(root,pattern,handle)
19071 local collected=applylpath(root,pattern)
19072 if collected then
19073 for c=1,#collected do
19074 local e=collected[c]
19075 local r=e.__p__
19076 handle(r,r.dt,e.ni)
19077 end
19078 end
19079end
19080local function selection(root,pattern,handle)
19081 local collected=applylpath(root,pattern)
19082 if collected then
19083 if handle then
19084 for c=1,#collected do
19085 handle(collected[c])
19086 end
19087 else
19088 return collected
19089 end
19090 end
19091end
19092xml.traverse=traverse
19093xml.selection=selection
19094local function dofunction(collected,fnc,...)
19095 if collected then
19096 local f=functions[fnc]
19097 if f then
19098 for c=1,#collected do
19099 f(collected[c],...)
19100 end
19101 else
19102 report_lpath("unknown function %a",fnc)
19103 end
19104 end
19105end
19106finalizers.xml["function"]=dofunction
19107finalizers.tex["function"]=dofunction
19108expressions.text=function(e,n)
19109 local rdt=e.__p__.dt
19110 return rdt and rdt[n] or ""
19111end
19112expressions.name=function(e,n)
19113 local found=false
19114 n=tonumber(n) or 0
19115 if n==0 then
19116 found=type(e)=="table" and e
19117 elseif n<0 then
19118 local d=e.__p__.dt
19119 local k=e.ni
19120 for i=k-1,1,-1 do
19121 local di=d[i]
19122 if type(di)=="table" then
19123 if n==-1 then
19124 found=di
19125 break
19126 else
19127 n=n+1
19128 end
19129 end
19130 end
19131 else
19132 local d=e.__p__.dt
19133 local k=e.ni
19134 for i=k+1,#d,1 do
19135 local di=d[i]
19136 if type(di)=="table" then
19137 if n==1 then
19138 found=di
19139 break
19140 else
19141 n=n-1
19142 end
19143 end
19144 end
19145 end
19146 if found then
19147 local ns=found.rn or found.ns or ""
19148 local tg=found.tg
19149 if ns~="" then
19150 return ns..":"..tg
19151 else
19152 return tg
19153 end
19154 else
19155 return ""
19156 end
19157end
19158expressions.tag=function(e,n)
19159 if not e then
19160 return ""
19161 else
19162 local found=false
19163 n=tonumber(n) or 0
19164 if n==0 then
19165 found=(type(e)=="table") and e
19166 elseif n<0 then
19167 local d=e.__p__.dt
19168 local k=e.ni
19169 for i=k-1,1,-1 do
19170 local di=d[i]
19171 if type(di)=="table" then
19172 if n==-1 then
19173 found=di
19174 break
19175 else
19176 n=n+1
19177 end
19178 end
19179 end
19180 else
19181 local d=e.__p__.dt
19182 local k=e.ni
19183 for i=k+1,#d,1 do
19184 local di=d[i]
19185 if type(di)=="table" then
19186 if n==1 then
19187 found=di
19188 break
19189 else
19190 n=n-1
19191 end
19192 end
19193 end
19194 end
19195 return (found and found.tg) or ""
19196 end
19197end
19198local dummy=function() end
19199function xml.elements(root,pattern,reverse)
19200 local collected=applylpath(root,pattern)
19201 if not collected then
19202 return dummy
19203 end
19204 local n=#collected
19205 if n==0 then
19206 return dummy
19207 end
19208 if reverse then
19209 local c=n+1
19210 return function()
19211 if c>1 then
19212 c=c-1
19213 local e=collected[c]
19214 local r=e.__p__
19215 return r,r.dt,e.ni
19216 end
19217 end
19218 else
19219 local c=0
19220 return function()
19221 if c<n then
19222 c=c+1
19223 local e=collected[c]
19224 local r=e.__p__
19225 return r,r.dt,e.ni
19226 end
19227 end
19228 end
19229end
19230function xml.collected(root,pattern,reverse)
19231 local collected=applylpath(root,pattern)
19232 if not collected then
19233 return dummy
19234 end
19235 local n=#collected
19236 if n==0 then
19237 return dummy
19238 end
19239 if reverse then
19240 local c=n+1
19241 return function()
19242 if c>1 then
19243 c=c-1
19244 return collected[c]
19245 end
19246 end
19247 else
19248 local c=0
19249 return function()
19250 if c<n then
19251 c=c+1
19252 return collected[c]
19253 end
19254 end
19255 end
19256end
19257function xml.inspect(collection,pattern)
19258 pattern=pattern or "."
19259 for e in xml.collected(collection,pattern or ".") do
19260 report_lpath("pattern: %s\n\n%s\n",pattern,xml.tostring(e))
19261 end
19262end
19263local function split(e)
19264 local dt=e.dt
19265 if dt then
19266 for i=1,#dt do
19267 local dti=dt[i]
19268 if type(dti)=="string" then
19269 dti=gsub(dti,"^[\n\r]*(.-)[\n\r]*","%1")
19270 dti=gsub(dti,"[\n\r]+","\n\n")
19271 dt[i]=dti
19272 else
19273 split(dti)
19274 end
19275 end
19276 end
19277 return e
19278end
19279function xml.finalizers.paragraphs(c)
19280 for i=1,#c do
19281 split(c[i])
19282 end
19283 return c
19284end
19285
19286
19287end
19288
19289do
19290
19291package.loaded["lxml-mis"] = package.loaded["lxml-mis"] or true
19292
19293
19294
19295if not modules then modules={} end modules ['lxml-mis']={
19296 version=1.001,
19297 comment="this module is the basis for the lxml-* ones",
19298 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
19299 copyright="PRAGMA ADE / ConTeXt Development Team",
19300 license="see context related readme files"
19301}
19302local xml,lpeg,string=xml,lpeg,string
19303local type=type
19304local concat=table.concat
19305local format,gsub,match=string.format,string.gsub,string.match
19306local lpegmatch,lpegpatterns=lpeg.match,lpeg.patterns
19307local P,S,R,C,V,Cc,Cs=lpeg.P,lpeg.S,lpeg.R,lpeg.C,lpeg.V,lpeg.Cc,lpeg.Cs
19308lpegpatterns.xml=lpegpatterns.xml or {}
19309local xmlpatterns=lpegpatterns.xml
19310local function xmlgsub(t,old,new)
19311 local dt=t.dt
19312 if dt then
19313 for k=1,#dt do
19314 local v=dt[k]
19315 if type(v)=="string" then
19316 dt[k]=gsub(v,old,new)
19317 else
19318 xmlgsub(v,old,new)
19319 end
19320 end
19321 end
19322end
19323function xml.stripleadingspaces(dk,d,k)
19324 if d and k then
19325 local dkm=d[k-1]
19326 if dkm and type(dkm)=="string" then
19327 local s=match(dkm,"\n(%s+)")
19328 xmlgsub(dk,"\n"..rep(" ",#s),"\n")
19329 end
19330 end
19331end
19332local normal=(1-S("<&>"))^0
19333local special=P("<")/"<"+P(">")/">"+P("&")/"&"
19334local escaped=Cs(normal*(special*normal)^0)
19335local normal=(1-S"&")^0
19336local special=P("<")/"<"+P(">")/">"+P("&")/"&"
19337local unescaped=Cs(normal*(special*normal)^0)
19338local cleansed=Cs(((P("<")*(1-P(">"))^0*P(">"))/""+1)^0)
19339xmlpatterns.escaped=escaped
19340xmlpatterns.unescaped=unescaped
19341xmlpatterns.cleansed=cleansed
19342function xml.escaped (str) return lpegmatch(escaped,str) end
19343function xml.unescaped(str) return lpegmatch(unescaped,str) end
19344function xml.cleansed (str) return lpegmatch(cleansed,str) end
19345function xml.fillin(root,pattern,str,check)
19346 local e=xml.first(root,pattern)
19347 if e then
19348 local n=#e.dt
19349 if not check or n==0 or (n==1 and e.dt[1]=="") then
19350 e.dt={ str }
19351 end
19352 end
19353end
19354
19355
19356end
19357
19358do
19359
19360package.loaded["lxml-aux"] = package.loaded["lxml-aux"] or true
19361
19362
19363
19364if not modules then modules={} end modules ['lxml-aux']={
19365 version=1.001,
19366 comment="this module is the basis for the lxml-* ones",
19367 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
19368 copyright="PRAGMA ADE / ConTeXt Development Team",
19369 license="see context related readme files"
19370}
19371local trace_manipulations=false trackers.register("lxml.manipulations",function(v) trace_manipulations=v end)
19372local trace_inclusions=false trackers.register("lxml.inclusions",function(v) trace_inclusions=v end)
19373local report_xml=logs.reporter("xml")
19374local xml=xml
19375local xmlcopy,xmlname=xml.copy,xml.name
19376local xmlinheritedconvert=xml.inheritedconvert
19377local xmlapplylpath=xml.applylpath
19378local type,next,setmetatable,getmetatable=type,next,setmetatable,getmetatable
19379local insert,remove,fastcopy,concat=table.insert,table.remove,table.fastcopy,table.concat
19380local gmatch,gsub,format,find,strip,match=string.gmatch,string.gsub,string.format,string.find,string.strip,string.match
19381local utfbyte=utf.byte
19382local lpegmatch,lpegpatterns=lpeg.match,lpeg.patterns
19383local striplinepatterns=utilities.strings.striplinepatterns
19384local function report(what,pattern,c,e)
19385 report_xml("%s element %a, root %a, position %a, index %a, pattern %a",what,xmlname(e),xmlname(e.__p__),c,e.ni,pattern)
19386end
19387local function withelements(e,handle,depth)
19388 if e and handle then
19389 local edt=e.dt
19390 if edt then
19391 depth=depth or 0
19392 for i=1,#edt do
19393 local e=edt[i]
19394 if type(e)=="table" then
19395 handle(e,depth)
19396 withelements(e,handle,depth+1)
19397 end
19398 end
19399 end
19400 end
19401end
19402xml.withelements=withelements
19403function xml.withelement(e,n,handle)
19404 if e and n~=0 and handle then
19405 local edt=e.dt
19406 if edt then
19407 if n>0 then
19408 for i=1,#edt do
19409 local ei=edt[i]
19410 if type(ei)=="table" then
19411 if n==1 then
19412 handle(ei)
19413 return
19414 else
19415 n=n-1
19416 end
19417 end
19418 end
19419 elseif n<0 then
19420 for i=#edt,1,-1 do
19421 local ei=edt[i]
19422 if type(ei)=="table" then
19423 if n==-1 then
19424 handle(ei)
19425 return
19426 else
19427 n=n+1
19428 end
19429 end
19430 end
19431 end
19432 end
19433 end
19434end
19435function xml.each(root,pattern,handle,reverse)
19436 local collected=xmlapplylpath(root,pattern)
19437 if collected then
19438 if handle then
19439 if reverse then
19440 for c=#collected,1,-1 do
19441 handle(collected[c])
19442 end
19443 else
19444 for c=1,#collected do
19445 handle(collected[c])
19446 end
19447 end
19448 end
19449 return collected
19450 end
19451end
19452function xml.processattributes(root,pattern,handle)
19453 local collected=xmlapplylpath(root,pattern)
19454 if collected and handle then
19455 for c=1,#collected do
19456 handle(collected[c].at)
19457 end
19458 end
19459 return collected
19460end
19461function xml.collect(root,pattern)
19462 return xmlapplylpath(root,pattern)
19463end
19464function xml.collecttexts(root,pattern,flatten)
19465 local collected=xmlapplylpath(root,pattern)
19466 if collected and flatten then
19467 local xmltostring=xml.tostring
19468 for c=1,#collected do
19469 collected[c]=xmltostring(collected[c].dt)
19470 end
19471 end
19472 return collected or {}
19473end
19474function xml.collect_tags(root,pattern,nonamespace)
19475 local collected=xmlapplylpath(root,pattern)
19476 if collected then
19477 local t={}
19478 local n=0
19479 for c=1,#collected do
19480 local e=collected[c]
19481 local ns=e.ns
19482 local tg=e.tg
19483 n=n+1
19484 if nonamespace then
19485 t[n]=tg
19486 elseif ns=="" then
19487 t[n]=tg
19488 else
19489 t[n]=ns..":"..tg
19490 end
19491 end
19492 return t
19493 end
19494end
19495local no_root={ no_root=true }
19496local function redo_ni(d)
19497 for k=1,#d do
19498 local dk=d[k]
19499 if type(dk)=="table" then
19500 dk.ni=k
19501 end
19502 end
19503end
19504xml.reindex=redo_ni
19505local function xmltoelement(whatever,root)
19506 if not whatever then
19507 return nil
19508 end
19509 local element
19510 if type(whatever)=="string" then
19511 element=xmlinheritedconvert(whatever,root,true)
19512 else
19513 element=whatever
19514 end
19515 if element.error then
19516 return whatever
19517 end
19518 if element then
19519 end
19520 return element
19521end
19522xml.toelement=xmltoelement
19523local function copiedelement(element,newparent)
19524 if type(element)~="string" then
19525 element=xmlcopy(element).dt
19526 if newparent and type(element)=="table" then
19527 for i=1,#element do
19528 local e=element[i]
19529 if type(e)=="table" then
19530 e.__p__=newparent
19531 end
19532 end
19533 end
19534 end
19535 return element
19536end
19537function xml.delete(root,pattern)
19538 if not pattern or pattern=="" then
19539 local p=root.__p__
19540 if p then
19541 if trace_manipulations then
19542 report('deleting',"--",c,root)
19543 end
19544 local d=p.dt
19545 remove(d,root.ni)
19546 redo_ni(d)
19547 end
19548 else
19549 local collected=xmlapplylpath(root,pattern)
19550 if collected then
19551 for c=1,#collected do
19552 local e=collected[c]
19553 local p=e.__p__
19554 if p then
19555 if trace_manipulations then
19556 report('deleting',pattern,c,e)
19557 end
19558 local d=p.dt
19559 local ni=e.ni
19560 if ni<=#d then
19561 if false then
19562 p.dt[ni]=""
19563 else
19564 remove(d,ni)
19565 redo_ni(d)
19566 end
19567 else
19568 end
19569 end
19570 end
19571 end
19572 end
19573end
19574function xml.wipe(root,pattern)
19575 local collected=xmlapplylpath(root,pattern)
19576 if collected then
19577 for c=1,#collected do
19578 local e=collected[c]
19579 local p=e.__p__
19580 if p then
19581 local d=p.dt
19582 local ni=e.ni
19583 if ni<=#d then
19584 local dt=e.dt
19585 if #dt==1 then
19586 local d1=dt[1]
19587 if type(d1)=="string" and match(d1,"^%s*$") then
19588 if trace_manipulations then
19589 report('wiping',pattern,c,e)
19590 end
19591 remove(d,ni)
19592 redo_ni(d)
19593 end
19594 end
19595 end
19596 end
19597 end
19598 end
19599end
19600function xml.replace(root,pattern,whatever)
19601 local element=root and xmltoelement(whatever,root)
19602 local collected=element and xmlapplylpath(root,pattern)
19603 if collected then
19604 for c=1,#collected do
19605 local e=collected[c]
19606 local p=e.__p__
19607 if p then
19608 if trace_manipulations then
19609 report('replacing',pattern,c,e)
19610 end
19611 local d=p.dt
19612 local n=e.ni
19613 local t=copiedelement(element,p)
19614 if type(t)=="table" then
19615 d[n]=t[1]
19616 for i=2,#t do
19617 n=n+1
19618 insert(d,n,t[i])
19619 end
19620 else
19621 d[n]=t
19622 end
19623 redo_ni(d)
19624 end
19625 end
19626 end
19627end
19628function xml.expand(root,pattern,whatever)
19629 local collected=root and xmlapplylpath(root,pattern)
19630 if collected then
19631 for c=1,#collected do
19632 local e=collected[c]
19633 local p=e.__p__
19634 if p then
19635 if trace_manipulations then
19636 report('expanding',pattern,c,e)
19637 end
19638 local d=p.dt
19639 local n=e.ni
19640 local t=whatever(e,p)
19641 if t then
19642 if type(t)=="table" then
19643 t=xmlcopy(t)
19644 d[n]=t[1]
19645 for i=2,#t do
19646 n=n+1
19647 insert(d,n,t[i])
19648 end
19649 else
19650 d[n]=t
19651 end
19652 redo_ni(d)
19653 end
19654 end
19655 end
19656 end
19657end
19658local function wrap(e,wrapper)
19659 local t={
19660 rn=e.rn,
19661 tg=e.tg,
19662 ns=e.ns,
19663 at=e.at,
19664 dt=e.dt,
19665 __p__=e,
19666 }
19667 setmetatable(t,getmetatable(e))
19668 e.rn=wrapper.rn or e.rn or ""
19669 e.tg=wrapper.tg or e.tg or ""
19670 e.ns=wrapper.ns or e.ns or ""
19671 e.at=fastcopy(wrapper.at)
19672 e.dt={ t }
19673end
19674function xml.wrap(root,pattern,whatever)
19675 if whatever then
19676 local wrapper=xmltoelement(whatever,root)
19677 local collected=xmlapplylpath(root,pattern)
19678 if collected then
19679 for c=1,#collected do
19680 local e=collected[c]
19681 if trace_manipulations then
19682 report('wrapping',pattern,c,e)
19683 end
19684 wrap(e,wrapper)
19685 end
19686 end
19687 else
19688 wrap(root,xmltoelement(pattern))
19689 end
19690end
19691local function inject_element(root,pattern,whatever,prepend)
19692 local element=root and xmltoelement(whatever,root)
19693 local collected=element and xmlapplylpath(root,pattern)
19694 local function inject_e(e)
19695 local r=e.__p__
19696 local d=r.dt
19697 local k=e.ni
19698 local rri=r.ri
19699 local edt=(rri and d[rri].dt) or (d and d[k] and d[k].dt)
19700 if edt then
19701 local be,af
19702 local cp=copiedelement(element,e)
19703 if prepend then
19704 be,af=cp,edt
19705 else
19706 be,af=edt,cp
19707 end
19708 local bn=#be
19709 for i=1,#af do
19710 bn=bn+1
19711 be[bn]=af[i]
19712 end
19713 if rri then
19714 r.dt[rri].dt=be
19715 else
19716 d[k].dt=be
19717 end
19718 redo_ni(d)
19719 end
19720 end
19721 if not collected then
19722 elseif collected.tg then
19723 inject_e(collected)
19724 else
19725 for c=1,#collected do
19726 inject_e(collected[c])
19727 end
19728 end
19729end
19730local function insert_element(root,pattern,whatever,before)
19731 local element=root and xmltoelement(whatever,root)
19732 local collected=element and xmlapplylpath(root,pattern)
19733 local function insert_e(e)
19734 local r=e.__p__
19735 local d=r.dt
19736 local k=e.ni
19737 if not before then
19738 k=k+1
19739 end
19740 insert(d,k,copiedelement(element,r))
19741 redo_ni(d)
19742 end
19743 if not collected then
19744 elseif collected.tg then
19745 insert_e(collected)
19746 else
19747 for c=1,#collected do
19748 insert_e(collected[c])
19749 end
19750 end
19751end
19752xml.insert_element=insert_element
19753xml.insertafter=insert_element
19754xml.insertbefore=function(r,p,e) insert_element(r,p,e,true) end
19755xml.injectafter=inject_element
19756xml.injectbefore=function(r,p,e) inject_element(r,p,e,true) end
19757local function include(xmldata,pattern,attribute,recursive,loaddata,level)
19758 pattern=pattern or 'include'
19759 loaddata=loaddata or io.loaddata
19760 local collected=xmlapplylpath(xmldata,pattern)
19761 if collected then
19762 if not level then
19763 level=1
19764 end
19765 for c=1,#collected do
19766 local ek=collected[c]
19767 local name=nil
19768 local ekdt=ek.dt
19769 if ekdt then
19770 local ekat=ek.at
19771 local ekrt=ek.__p__
19772 if ekrt then
19773 local epdt=ekrt.dt
19774 if not attribute or attribute=="" then
19775 name=(type(ekdt)=="table" and ekdt[1]) or ekdt
19776 end
19777 if not name then
19778 for a in gmatch(attribute or "href","([^|]+)") do
19779 name=ekat[a]
19780 if name then
19781 break
19782 end
19783 end
19784 end
19785 local data=nil
19786 if name and name~="" then
19787 local d,n=loaddata(name)
19788 data=d or ""
19789 name=n or name
19790 if trace_inclusions then
19791 report_xml("including %s bytes from %a at level %s by pattern %a and attribute %a (%srecursing)",#data,name,level,pattern,attribute or "",recursive and "" or "not ")
19792 end
19793 end
19794 if not data or data=="" then
19795 epdt[ek.ni]=""
19796 elseif ekat["parse"]=="text" then
19797 epdt[ek.ni]=xml.escaped(data)
19798 else
19799 local settings=xmldata.settings
19800 local savedresource=settings.currentresource
19801 settings.currentresource=name
19802 local xi=xmlinheritedconvert(data,xmldata,true)
19803 if not xi then
19804 epdt[ek.ni]=""
19805 else
19806 if recursive then
19807 include(xi,pattern,attribute,recursive,loaddata,level+1)
19808 end
19809 local child=xml.body(xi)
19810 child.__p__=ekrt
19811 child.__f__=name
19812 child.cf=name
19813 epdt[ek.ni]=child
19814 local settings=xmldata.settings
19815 local inclusions=settings and settings.inclusions
19816 if inclusions then
19817 inclusions[#inclusions+1]=name
19818 elseif settings then
19819 settings.inclusions={ name }
19820 else
19821 settings={ inclusions={ name } }
19822 xmldata.settings=settings
19823 end
19824 if child.er then
19825 local badinclusions=settings.badinclusions
19826 if badinclusions then
19827 badinclusions[#badinclusions+1]=name
19828 else
19829 settings.badinclusions={ name }
19830 end
19831 end
19832 end
19833settings.currentresource=savedresource
19834 end
19835 end
19836 end
19837 end
19838 end
19839end
19840xml.include=include
19841function xml.inclusion(e,default)
19842 while e do
19843 local f=e.__f__
19844 if f then
19845 return f
19846 else
19847 e=e.__p__
19848 end
19849 end
19850 return default
19851end
19852local function getinclusions(key,e,sorted)
19853 while e do
19854 local settings=e.settings
19855 if settings then
19856 local inclusions=settings[key]
19857 if inclusions then
19858 inclusions=table.unique(inclusions)
19859 if sorted then
19860 table.sort(inclusions)
19861 end
19862 return inclusions
19863 else
19864 e=e.__p__
19865 end
19866 else
19867 e=e.__p__
19868 end
19869 end
19870end
19871function xml.inclusions(e,sorted)
19872 return getinclusions("inclusions",e,sorted)
19873end
19874function xml.badinclusions(e,sorted)
19875 return getinclusions("badinclusions",e,sorted)
19876end
19877local b_collapser=lpegpatterns.b_collapser
19878local m_collapser=lpegpatterns.m_collapser
19879local e_collapser=lpegpatterns.e_collapser
19880local x_collapser=lpegpatterns.x_collapser
19881local b_stripper=lpegpatterns.b_stripper
19882local m_stripper=lpegpatterns.m_stripper
19883local e_stripper=lpegpatterns.e_stripper
19884local x_stripper=lpegpatterns.x_stripper
19885local function stripelement(e,nolines,anywhere,everything)
19886 local edt=e.dt
19887 if edt then
19888 local n=#edt
19889 if n==0 then
19890 return e
19891 elseif everything then
19892 local t={}
19893 local m=0
19894 for i=1,n do
19895 local str=edt[i]
19896 if type(str)~="string" then
19897 m=m+1
19898 t[m]=str
19899 elseif str~="" then
19900 str=lpegmatch(x_collapser,str)
19901 if str~="" then
19902 m=m+1
19903 t[m]=str
19904 end
19905 end
19906 end
19907 e.dt=t
19908 elseif anywhere then
19909 local t={}
19910 local m=0
19911 for i=1,n do
19912 local str=edt[i]
19913 if type(str)~="string" then
19914 m=m+1
19915 t[m]=str
19916 elseif str~="" then
19917 if nolines then
19918 str=lpegmatch((i==1 and b_collapser) or (i==m and e_collapser) or m_collapser,str)
19919 else
19920 str=lpegmatch((i==1 and b_stripper) or (i==m and e_stripper) or m_stripper,str)
19921 end
19922 if str~="" then
19923 m=m+1
19924 t[m]=str
19925 end
19926 end
19927 end
19928 e.dt=t
19929 else
19930 local str=edt[1]
19931 if type(str)=="string" then
19932 if str~="" then
19933 str=lpegmatch(nolines and b_collapser or b_stripper,str)
19934 end
19935 if str=="" then
19936 remove(edt,1)
19937 n=n-1
19938 else
19939 edt[1]=str
19940 end
19941 end
19942 if n>0 then
19943 str=edt[n]
19944 if type(str)=="string" then
19945 if str=="" then
19946 remove(edt)
19947 else
19948 str=lpegmatch(nolines and e_collapser or e_stripper,str)
19949 if str=="" then
19950 remove(edt)
19951 else
19952 edt[n]=str
19953 end
19954 end
19955 end
19956 end
19957 end
19958 end
19959 return e
19960end
19961xml.stripelement=stripelement
19962function xml.strip(root,pattern,nolines,anywhere,everything)
19963 local collected=xmlapplylpath(root,pattern)
19964 if collected then
19965 for i=1,#collected do
19966 stripelement(collected[i],nolines,anywhere,everything)
19967 end
19968 end
19969end
19970local function compactelement(e)
19971 local edt=e.dt
19972 if edt then
19973 for e=1,#edt do
19974 local str=edt[e]
19975 if type(str)=="string" and not find(str,"%S") then
19976 edt[e]=""
19977 end
19978 end
19979 end
19980 return e
19981end
19982xml.compactelement=compactelement
19983local function renamespace(root,oldspace,newspace)
19984 local ndt=#root.dt
19985 for i=1,ndt or 0 do
19986 local e=root[i]
19987 if type(e)=="table" then
19988 if e.ns==oldspace then
19989 e.ns=newspace
19990 if e.rn then
19991 e.rn=newspace
19992 end
19993 end
19994 local edt=e.dt
19995 if edt then
19996 renamespace(edt,oldspace,newspace)
19997 end
19998 end
19999 end
20000end
20001xml.renamespace=renamespace
20002function xml.remaptag(root,pattern,newtg)
20003 local collected=xmlapplylpath(root,pattern)
20004 if collected then
20005 for c=1,#collected do
20006 collected[c].tg=newtg
20007 end
20008 end
20009end
20010function xml.remapnamespace(root,pattern,newns)
20011 local collected=xmlapplylpath(root,pattern)
20012 if collected then
20013 for c=1,#collected do
20014 collected[c].ns=newns
20015 end
20016 end
20017end
20018function xml.checknamespace(root,pattern,newns)
20019 local collected=xmlapplylpath(root,pattern)
20020 if collected then
20021 for c=1,#collected do
20022 local e=collected[c]
20023 if (not e.rn or e.rn=="") and e.ns=="" then
20024 e.rn=newns
20025 end
20026 end
20027 end
20028end
20029function xml.remapname(root,pattern,newtg,newns,newrn)
20030 local collected=xmlapplylpath(root,pattern)
20031 if collected then
20032 for c=1,#collected do
20033 local e=collected[c]
20034 e.tg,e.ns,e.rn=newtg,newns,newrn
20035 end
20036 end
20037end
20038function xml.cdatatotext(e)
20039 local dt=e.dt
20040 if #dt==1 then
20041 local first=dt[1]
20042 if first.tg=="@cd@" then
20043 e.dt=first.dt
20044 end
20045 else
20046 end
20047end
20048function xml.texttocdata(e)
20049 local dt=e.dt
20050 local s=xml.tostring(dt)
20051 e.tg="@cd@"
20052 e.special=true
20053 e.ns=""
20054 e.rn=""
20055 e.dt={ s }
20056 e.at=nil
20057end
20058function xml.elementtocdata(e)
20059 local dt=e.dt
20060 local s=xml.tostring(e)
20061 e.tg="@cd@"
20062 e.special=true
20063 e.ns=""
20064 e.rn=""
20065 e.dt={ s }
20066 e.at=nil
20067end
20068xml.builtinentities=table.tohash { "amp","quot","apos","lt","gt" }
20069local entities=characters and characters.entities or nil
20070local builtinentities=xml.builtinentities
20071function xml.addentitiesdoctype(root,option)
20072 if not entities then
20073 require("char-ent")
20074 entities=characters.entities
20075 end
20076 if entities and root and root.tg=="@rt@" and root.statistics then
20077 local list={}
20078 local hexify=option=="hexadecimal"
20079 for k,v in table.sortedhash(root.statistics.entities.names) do
20080 if not builtinentities[k] then
20081 local e=entities[k]
20082 if not e then
20083 e=format("[%s]",k)
20084 elseif hexify then
20085 e=format("&#%05X;",utfbyte(k))
20086 end
20087 list[#list+1]=format(" <!ENTITY %s %q >",k,e)
20088 end
20089 end
20090 local dt=root.dt
20091 local n=dt[1].tg=="@pi@" and 2 or 1
20092 if #list>0 then
20093 insert(dt,n,{ "\n" })
20094 insert(dt,n,{
20095 tg="@dt@",
20096 dt={ format("Something [\n%s\n] ",concat(list)) },
20097 ns="",
20098 special=true,
20099 })
20100 insert(dt,n,{ "\n\n" })
20101 else
20102 end
20103 end
20104end
20105xml.all=xml.each
20106xml.insert=xml.insertafter
20107xml.inject=xml.injectafter
20108xml.after=xml.insertafter
20109xml.before=xml.insertbefore
20110xml.process=xml.each
20111xml.obsolete=xml.obsolete or {}
20112local obsolete=xml.obsolete
20113xml.strip_whitespace=xml.strip obsolete.strip_whitespace=xml.strip
20114xml.collect_elements=xml.collect obsolete.collect_elements=xml.collect
20115xml.delete_element=xml.delete obsolete.delete_element=xml.delete
20116xml.replace_element=xml.replace obsolete.replace_element=xml.replace
20117xml.each_element=xml.each obsolete.each_element=xml.each
20118xml.process_elements=xml.process obsolete.process_elements=xml.process
20119xml.insert_element_after=xml.insertafter obsolete.insert_element_after=xml.insertafter
20120xml.insert_element_before=xml.insertbefore obsolete.insert_element_before=xml.insertbefore
20121xml.inject_element_after=xml.injectafter obsolete.inject_element_after=xml.injectafter
20122xml.inject_element_before=xml.injectbefore obsolete.inject_element_before=xml.injectbefore
20123xml.process_attributes=xml.processattributes obsolete.process_attributes=xml.processattributes
20124xml.collect_texts=xml.collecttexts obsolete.collect_texts=xml.collecttexts
20125xml.inject_element=xml.inject obsolete.inject_element=xml.inject
20126xml.remap_tag=xml.remaptag obsolete.remap_tag=xml.remaptag
20127xml.remap_name=xml.remapname obsolete.remap_name=xml.remapname
20128xml.remap_namespace=xml.remapnamespace obsolete.remap_namespace=xml.remapnamespace
20129function xml.cdata(e)
20130 if e then
20131 local dt=e.dt
20132 if dt and #dt==1 then
20133 local first=dt[1]
20134 return first.tg=="@cd@" and first.dt[1] or ""
20135 end
20136 end
20137 return ""
20138end
20139function xml.finalizers.xml.cdata(collected)
20140 if collected then
20141 local e=collected[1]
20142 if e then
20143 local dt=e.dt
20144 if dt and #dt==1 then
20145 local first=dt[1]
20146 return first.tg=="@cd@" and first.dt[1] or ""
20147 end
20148 end
20149 end
20150 return ""
20151end
20152function xml.insertcomment(e,str,n)
20153 insert(e.dt,n or 1,{
20154 tg="@cm@",
20155 ns="",
20156 special=true,
20157 at={},
20158 dt={ str },
20159 })
20160end
20161function xml.insertcdata(e,str,n)
20162 insert(e.dt,n or 1,{
20163 tg="@cd@",
20164 ns="",
20165 special=true,
20166 at={},
20167 dt={ str },
20168 })
20169end
20170function xml.setcomment(e,str,n)
20171 e.dt={ {
20172 tg="@cm@",
20173 ns="",
20174 special=true,
20175 at={},
20176 dt={ str },
20177 } }
20178end
20179function xml.setcdata(e,str)
20180 e.dt={ {
20181 tg="@cd@",
20182 ns="",
20183 special=true,
20184 at={},
20185 dt={ str },
20186 } }
20187end
20188function xml.separate(x,pattern)
20189 local collected=xmlapplylpath(x,pattern)
20190 if collected then
20191 for c=1,#collected do
20192 local e=collected[c]
20193 local d=e.dt
20194 if d==x then
20195 report_xml("warning: xml.separate changes root")
20196 x=d
20197 end
20198 local t={ "\n" }
20199 local n=1
20200 local i=1
20201 local nd=#d
20202 while i<=nd do
20203 while i<=nd do
20204 local di=d[i]
20205 if type(di)=="string" then
20206 if di=="\n" or find(di,"^%s+$") then
20207 i=i+1
20208 else
20209 d[i]=strip(di)
20210 break
20211 end
20212 else
20213 break
20214 end
20215 end
20216 if i>nd then
20217 break
20218 end
20219 t[n+1]="\n"
20220 t[n+2]=d[i]
20221 t[n+3]="\n"
20222 n=n+3
20223 i=i+1
20224 end
20225 t[n+1]="\n"
20226 setmetatable(t,getmetatable(d))
20227 e.dt=t
20228 end
20229 end
20230 return x
20231end
20232local helpers=xml.helpers or {}
20233xml.helpers=helpers
20234local function normal(e,action)
20235 local edt=e.dt
20236 if edt then
20237 for i=1,#edt do
20238 local str=edt[i]
20239 if type(str)=="string" and str~="" then
20240 edt[i]=action(str)
20241 end
20242 end
20243 end
20244end
20245local function recurse(e,action)
20246 local edt=e.dt
20247 if edt then
20248 for i=1,#edt do
20249 local str=edt[i]
20250 if type(str)~="string" then
20251 recurse(str,action)
20252 elseif str~="" then
20253 edt[i]=action(str)
20254 end
20255 end
20256 end
20257end
20258function helpers.recursetext(collected,action,recursive)
20259 if recursive then
20260 for i=1,#collected do
20261 recurse(collected[i],action)
20262 end
20263 else
20264 for i=1,#collected do
20265 normal(collected[i],action)
20266 end
20267 end
20268end
20269local specials={
20270 ["@rt@"]="root",
20271 ["@pi@"]="instruction",
20272 ["@cm@"]="comment",
20273 ["@dt@"]="declaration",
20274 ["@cd@"]="cdata",
20275}
20276local function convert(x,strip,flat)
20277 local ns=x.ns
20278 local tg=x.tg
20279 local at=x.at
20280 local dt=x.dt
20281 local node=flat and {
20282 [0]=(not x.special and (ns~="" and ns..":"..tg or tg)) or nil,
20283 } or {
20284 _namespace=ns~="" and ns or nil,
20285 _tag=not x.special and tg or nil,
20286 _type=specials[tg] or "_element",
20287 }
20288 if at then
20289 for k,v in next,at do
20290 node[k]=v
20291 end
20292 end
20293 local n=0
20294 for i=1,#dt do
20295 local di=dt[i]
20296 if type(di)=="table" then
20297 if flat and di.special then
20298 else
20299 di=convert(di,strip,flat)
20300 if di then
20301 n=n+1
20302 node[n]=di
20303 end
20304 end
20305 elseif strip then
20306 di=lpegmatch(strip,di)
20307 if di~="" then
20308 n=n+1
20309 node[n]=di
20310 end
20311 else
20312 n=n+1
20313 node[n]=di
20314 end
20315 end
20316 if next(node) then
20317 return node
20318 end
20319end
20320function xml.totable(x,strip,flat)
20321 if type(x)=="table" then
20322 if strip then
20323 strip=striplinepatterns[strip]
20324 end
20325 return convert(x,strip,flat)
20326 end
20327end
20328function xml.rename(e,namespace,name,attributes)
20329 if type(e)~="table" or not e.tg then
20330 return
20331 end
20332 if type(name)=="table" then
20333 attributes=name
20334 name=namespace
20335 namespace=""
20336 elseif type(name)~="string" then
20337 attributes={}
20338 name=namespace
20339 namespace=""
20340 end
20341 if type(attributes)~="table" then
20342 attributes={}
20343 end
20344 e.ns=namespace
20345 e.rn=namespace
20346 e.tg=name
20347 e.at=attributes
20348end
20349
20350
20351end
20352
20353do
20354
20355package.loaded["lxml-xml"] = package.loaded["lxml-xml"] or true
20356
20357
20358
20359if not modules then modules={} end modules ['lxml-xml']={
20360 version=1.001,
20361 comment="this module is the basis for the lxml-* ones",
20362 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
20363 copyright="PRAGMA ADE / ConTeXt Development Team",
20364 license="see context related readme files"
20365}
20366local tonumber,next=tonumber,next
20367local concat=table.concat
20368local find,lower,upper=string.find,string.lower,string.upper
20369local xml=xml
20370local finalizers=xml.finalizers.xml
20371local xmlfilter=xml.filter
20372local xmltostring=xml.tostring
20373local xmlserialize=xml.serialize
20374local xmlcollected=xml.collected
20375local xmlnewhandlers=xml.newhandlers
20376local reparsedentity=xml.reparsedentitylpeg
20377local unescapedentity=xml.unescapedentitylpeg
20378local parsedentity=reparsedentity
20379local function first(collected)
20380 return collected and collected[1]
20381end
20382local function last(collected)
20383 return collected and collected[#collected]
20384end
20385local function all(collected)
20386 return collected
20387end
20388local reverse=table.reversed
20389local function attribute(collected,name)
20390 if collected and #collected>0 then
20391 local at=collected[1].at
20392 return at and at[name]
20393 end
20394end
20395local function att(id,name)
20396 local at=id.at
20397 return at and at[name]
20398end
20399local function count(collected)
20400 return collected and #collected or 0
20401end
20402local function position(collected,n)
20403 if not collected then
20404 return 0
20405 end
20406 local nc=#collected
20407 if nc==0 then
20408 return 0
20409 end
20410 n=tonumber(n) or 0
20411 if n<0 then
20412 return collected[nc+n+1]
20413 elseif n>0 then
20414 return collected[n]
20415 else
20416 return collected[1].mi or 0
20417 end
20418end
20419local function match(collected)
20420 return collected and #collected>0 and collected[1].mi or 0
20421end
20422local function index(collected)
20423 return collected and #collected>0 and collected[1].ni or 0
20424end
20425local function attributes(collected,arguments)
20426 if collected and #collected>0 then
20427 local at=collected[1].at
20428 if arguments then
20429 return at[arguments]
20430 elseif next(at) then
20431 return at
20432 end
20433 end
20434end
20435local function chainattribute(collected,arguments)
20436 if collected and #collected>0 then
20437 local e=collected[1]
20438 while e do
20439 local at=e.at
20440 if at then
20441 local a=at[arguments]
20442 if a then
20443 return a
20444 end
20445 else
20446 break
20447 end
20448 e=e.__p__
20449 end
20450 end
20451 return ""
20452end
20453local function raw(collected)
20454 if collected and #collected>0 then
20455 local e=collected[1] or collected
20456 return e and xmltostring(e) or ""
20457 else
20458 return ""
20459 end
20460end
20461local xmltexthandler=xmlnewhandlers {
20462 name="string",
20463 initialize=function()
20464 result={}
20465 return result
20466 end,
20467 finalize=function()
20468 return concat(result)
20469 end,
20470 handle=function(...)
20471 result[#result+1]=concat {... }
20472 end,
20473 escape=false,
20474}
20475local function xmltotext(root)
20476 local dt=root.dt
20477 if not dt then
20478 return ""
20479 end
20480 local nt=#dt
20481 if nt==0 then
20482 return ""
20483 elseif nt==1 and type(dt[1])=="string" then
20484 return dt[1]
20485 else
20486 return xmlserialize(root,xmltexthandler) or ""
20487 end
20488end
20489function xml.serializetotext(root)
20490 return root and xmlserialize(root,xmltexthandler) or ""
20491end
20492local function text(collected)
20493 if collected then
20494 local e=collected[1] or collected
20495 return e and xmltotext(e) or ""
20496 else
20497 return ""
20498 end
20499end
20500local function texts(collected)
20501 if not collected then
20502 return {}
20503 end
20504 local nc=#collected
20505 if nc==0 then
20506 return {}
20507 end
20508 local t,n={},0
20509 for c=1,nc do
20510 local e=collected[c]
20511 if e and e.dt then
20512 n=n+1
20513 t[n]=e.dt
20514 end
20515 end
20516 return t
20517end
20518local function tag(collected,n)
20519 if not collected then
20520 return
20521 end
20522 local nc=#collected
20523 if nc==0 then
20524 return
20525 end
20526 local c
20527 if n==0 or not n then
20528 c=collected[1]
20529 elseif n>1 then
20530 c=collected[n]
20531 else
20532 c=collected[nc-n+1]
20533 end
20534 return c and c.tg
20535end
20536local function name(collected,n)
20537 if not collected then
20538 return
20539 end
20540 local nc=#collected
20541 if nc==0 then
20542 return
20543 end
20544 local c
20545 if n==0 or not n then
20546 c=collected[1]
20547 elseif n>1 then
20548 c=collected[n]
20549 else
20550 c=collected[nc-n+1]
20551 end
20552 if not c then
20553 elseif c.ns=="" then
20554 return c.tg
20555 else
20556 return c.ns..":"..c.tg
20557 end
20558end
20559local function tags(collected,nonamespace)
20560 if not collected then
20561 return
20562 end
20563 local nc=#collected
20564 if nc==0 then
20565 return
20566 end
20567 local t,n={},0
20568 for c=1,nc do
20569 local e=collected[c]
20570 local ns,tg=e.ns,e.tg
20571 n=n+1
20572 if nonamespace or ns=="" then
20573 t[n]=tg
20574 else
20575 t[n]=ns..":"..tg
20576 end
20577 end
20578 return t
20579end
20580local function empty(collected,spacesonly)
20581 if not collected then
20582 return true
20583 end
20584 local nc=#collected
20585 if nc==0 then
20586 return true
20587 end
20588 for c=1,nc do
20589 local e=collected[c]
20590 if e then
20591 local edt=e.dt
20592 if edt then
20593 local n=#edt
20594 if n==1 then
20595 local edk=edt[1]
20596 local typ=type(edk)
20597 if typ=="table" then
20598 return false
20599 elseif edk~="" then
20600 return false
20601 elseif spacesonly and not find(edk,"%S") then
20602 return false
20603 end
20604 elseif n>1 then
20605 return false
20606 end
20607 end
20608 end
20609 end
20610 return true
20611end
20612finalizers.first=first
20613finalizers.last=last
20614finalizers.all=all
20615finalizers.reverse=reverse
20616finalizers.elements=all
20617finalizers.default=all
20618finalizers.attribute=attribute
20619finalizers.att=att
20620finalizers.count=count
20621finalizers.position=position
20622finalizers.match=match
20623finalizers.index=index
20624finalizers.attributes=attributes
20625finalizers.chainattribute=chainattribute
20626finalizers.text=text
20627finalizers.texts=texts
20628finalizers.tag=tag
20629finalizers.name=name
20630finalizers.tags=tags
20631finalizers.empty=empty
20632function xml.first(id,pattern)
20633 return first(xmlfilter(id,pattern))
20634end
20635function xml.last(id,pattern)
20636 return last(xmlfilter(id,pattern))
20637end
20638function xml.count(id,pattern)
20639 return count(xmlfilter(id,pattern))
20640end
20641function xml.attribute(id,pattern,a,default)
20642 return attribute(xmlfilter(id,pattern),a,default)
20643end
20644function xml.raw(id,pattern)
20645 if pattern then
20646 return raw(xmlfilter(id,pattern))
20647 else
20648 return raw(id)
20649 end
20650end
20651function xml.text(id,pattern)
20652 if pattern then
20653 local collected=xmlfilter(id,pattern)
20654 return collected and #collected>0 and xmltotext(collected[1]) or ""
20655 elseif id then
20656 return xmltotext(id) or ""
20657 else
20658 return ""
20659 end
20660end
20661function xml.pure(id,pattern)
20662 if pattern then
20663 local collected=xmlfilter(id,pattern)
20664 if collected and #collected>0 then
20665 parsedentity=unescapedentity
20666 local s=collected and #collected>0 and xmltotext(collected[1]) or ""
20667 parsedentity=reparsedentity
20668 return s
20669 else
20670 return ""
20671 end
20672 else
20673 parsedentity=unescapedentity
20674 local s=xmltotext(id) or ""
20675 parsedentity=reparsedentity
20676 return s
20677 end
20678end
20679xml.content=text
20680function xml.position(id,pattern,n)
20681 return position(xmlfilter(id,pattern),n)
20682end
20683function xml.match(id,pattern)
20684 return match(xmlfilter(id,pattern))
20685end
20686function xml.empty(id,pattern,spacesonly)
20687 return empty(xmlfilter(id,pattern),spacesonly)
20688end
20689xml.all=xml.filter
20690xml.index=xml.position
20691xml.found=xml.filter
20692local function totable(x)
20693 local t={}
20694 for e in xmlcollected(x[1] or x,"/*") do
20695 t[e.tg]=xmltostring(e.dt) or ""
20696 end
20697 return next(t) and t or nil
20698end
20699xml.table=totable
20700finalizers.table=totable
20701local function textonly(e,t)
20702 if e then
20703 local edt=e.dt
20704 if edt then
20705 for i=1,#edt do
20706 local e=edt[i]
20707 if type(e)=="table" then
20708 textonly(e,t)
20709 else
20710 t[#t+1]=e
20711 end
20712 end
20713 end
20714 end
20715 return t
20716end
20717function xml.textonly(e)
20718 return concat(textonly(e,{}))
20719end
20720function finalizers.lowerall(collected)
20721 for c=1,#collected do
20722 local e=collected[c]
20723 if not e.special then
20724 e.tg=lower(e.tg)
20725 local eat=e.at
20726 if eat then
20727 local t={}
20728 for k,v in next,eat do
20729 t[lower(k)]=v
20730 end
20731 e.at=t
20732 end
20733 end
20734 end
20735end
20736function finalizers.upperall(collected)
20737 for c=1,#collected do
20738 local e=collected[c]
20739 if not e.special then
20740 e.tg=upper(e.tg)
20741 local eat=e.at
20742 if eat then
20743 local t={}
20744 for k,v in next,eat do
20745 t[upper(k)]=v
20746 end
20747 e.at=t
20748 end
20749 end
20750 end
20751end
20752
20753
20754end
20755
20756do
20757
20758package.loaded["trac-xml"] = package.loaded["trac-xml"] or true
20759
20760
20761
20762if not modules then modules={} end modules ['trac-xml']={
20763 version=1.001,
20764 comment="companion to trac-log.mkiv",
20765 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
20766 copyright="PRAGMA ADE / ConTeXt Development Team",
20767 license="see context related readme files"
20768}
20769local formatters=string.formatters
20770local reporters=logs.reporters
20771local xmlserialize=xml.serialize
20772local xmlcollected=xml.collected
20773local xmltext=xml.text
20774local xmlfirst=xml.first
20775local function showhelp(specification,...)
20776 local root=xml.convert(specification.helpinfo or "")
20777 if not root then
20778 return
20779 end
20780 local xs=xml.gethandlers("string")
20781 xml.sethandlersfunction(xs,"short",function(e,handler) xmlserialize(e.dt,handler) end)
20782 xml.sethandlersfunction(xs,"ref",function(e,handler) handler.handle("--"..e.at.name) end)
20783 local wantedcategories=select("#",...)==0 and true or table.tohash {... }
20784 local nofcategories=xml.count(root,"/application/flags/category")
20785 local report=specification.report
20786 for category in xmlcollected(root,"/application/flags/category") do
20787 local categoryname=category.at.name or ""
20788 if wantedcategories==true or wantedcategories[categoryname] then
20789 if nofcategories>1 then
20790 report("%s options:",categoryname)
20791 report()
20792 end
20793 for subcategory in xmlcollected(category,"/subcategory") do
20794 for flag in xmlcollected(subcategory,"/flag") do
20795 local name=flag.at.name
20796 local value=flag.at.value
20797 local short=xmltext(xmlfirst(flag,"/short"))
20798 if value then
20799 report("--%-20s %s",formatters["%s=%s"](name,value),short)
20800 else
20801 report("--%-20s %s",name,short)
20802 end
20803 end
20804 report()
20805 end
20806 end
20807 end
20808 for category in xmlcollected(root,"/application/examples/category") do
20809 local title=xmltext(xmlfirst(category,"/title"))
20810 if title and title~="" then
20811 report()
20812 report(title)
20813 report()
20814 end
20815 for subcategory in xmlcollected(category,"/subcategory") do
20816 for example in xmlcollected(subcategory,"/example") do
20817 local command=xmltext(xmlfirst(example,"/command"))
20818 local comment=xmltext(xmlfirst(example,"/comment"))
20819 report(command)
20820 end
20821 report()
20822 end
20823 end
20824 for comment in xmlcollected(root,"/application/comments/comment") do
20825 local comment=xmltext(comment)
20826 report()
20827 report(comment)
20828 report()
20829 end
20830end
20831local reporthelp=reporters.help
20832local exporthelp=reporters.export
20833local function xmlfound(t)
20834 local helpinfo=t.helpinfo
20835 if type(helpinfo)=="table" then
20836 return false
20837 end
20838 if type(helpinfo)~="string" then
20839 helpinfo="Warning: no helpinfo found."
20840 t.helpinfo=helpinfo
20841 return false
20842 end
20843 if string.find(helpinfo,".xml$") then
20844 local ownscript=environment.ownscript
20845 local helpdata=false
20846 if ownscript then
20847 local helpfile=file.join(file.pathpart(ownscript),helpinfo)
20848 helpdata=io.loaddata(helpfile)
20849 if helpdata=="" then
20850 helpdata=false
20851 end
20852 end
20853 if not helpdata then
20854 local helpfile=resolvers.findfile(helpinfo,"tex")
20855 helpdata=helpfile and io.loaddata(helpfile)
20856 end
20857 if helpdata and helpdata~="" then
20858 helpinfo=helpdata
20859 else
20860 helpinfo=formatters["Warning: help file %a is not found."](helpinfo)
20861 end
20862 end
20863 t.helpinfo=helpinfo
20864 return string.find(t.helpinfo,"^<%?xml") and true or false
20865end
20866function reporters.help(t,...)
20867 if xmlfound(t) then
20868 showhelp(t,...)
20869 else
20870 reporthelp(t,...)
20871 end
20872end
20873function reporters.export(t,methods,filename)
20874 if not xmlfound(t) then
20875 return exporthelp(t)
20876 end
20877 if not methods or methods=="" then
20878 methods=environment.arguments["exporthelp"]
20879 end
20880 if not filename or filename=="" then
20881 filename=environment.files[1]
20882 end
20883 dofile(resolvers.findfile("trac-exp.lua","tex"))
20884 local exporters=logs.exporters
20885 if not exporters or not methods then
20886 return exporthelp(t)
20887 end
20888 if methods=="all" then
20889 methods=table.keys(exporters)
20890 elseif type(methods)=="string" then
20891 methods=utilities.parsers.settings_to_array(methods)
20892 else
20893 return exporthelp(t)
20894 end
20895 if type(filename)~="string" or filename=="" then
20896 filename=false
20897 elseif file.pathpart(filename)=="" then
20898 t.report("export file %a will not be saved on the current path (safeguard)",filename)
20899 return
20900 end
20901 for i=1,#methods do
20902 local method=methods[i]
20903 local exporter=exporters[method]
20904 if exporter then
20905 local result=exporter(t,method)
20906 if result and result~="" then
20907 if filename then
20908 local fullname=file.replacesuffix(filename,method)
20909 t.report("saving export in %a",fullname)
20910 dir.mkdirs(file.pathpart(fullname))
20911 io.savedata(fullname,result)
20912 else
20913 reporters.lines(t,result)
20914 end
20915 else
20916 t.report("no output from exporter %a",method)
20917 end
20918 else
20919 t.report("unknown exporter %a",method)
20920 end
20921 end
20922end
20923
20924
20925end
20926
20927do
20928
20929package.loaded["data-ini"] = package.loaded["data-ini"] or true
20930
20931
20932
20933if not modules then modules={} end modules ['data-ini']={
20934 version=1.001,
20935 comment="companion to luat-lib.mkiv",
20936 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
20937 copyright="PRAGMA ADE / ConTeXt Development Team",
20938 license="see context related readme files",
20939}
20940local next,type,getmetatable,rawset=next,type,getmetatable,rawset
20941local gsub,find,gmatch,char=string.gsub,string.find,string.gmatch,string.char
20942local filedirname,filebasename,filejoin,replacesuffix=file.dirname,file.basename,file.join,file.replacesuffix
20943local ostype,osname,osuname,ossetenv,osgetenv=os.type,os.name,os.uname,os.setenv,os.getenv
20944local sortedpairs=table.sortedpairs
20945local isfile,currentdir=lfs.isfile,lfs.currentdir
20946local expandlink=dir.expandlink
20947local P,S,R,C,Cs,Cc,lpegmatch=lpeg.P,lpeg.S,lpeg.R,lpeg.C,lpeg.Cs,lpeg.Cc,lpeg.match
20948local trace_locating=false trackers.register("resolvers.locating",function(v) trace_locating=v end)
20949local trace_expansions=false trackers.register("resolvers.expansions",function(v) trace_expansions=v end)
20950local report_initialization=logs.reporter("resolvers","initialization")
20951resolvers=resolvers or {}
20952local resolvers=resolvers
20953texconfig.kpse_init=false
20954texconfig.shell_escape='t'
20955if not (environment and environment.default_texmfcnf) and kpse and kpse.default_texmfcnf then
20956 local default_texmfcnf=kpse.default_texmfcnf()
20957 default_texmfcnf=gsub(default_texmfcnf,"$SELFAUTOLOC","selfautoloc:")
20958 default_texmfcnf=gsub(default_texmfcnf,"$SELFAUTODIR","selfautodir:")
20959 default_texmfcnf=gsub(default_texmfcnf,"$SELFAUTOPARENT","selfautoparent:")
20960 default_texmfcnf=gsub(default_texmfcnf,"$HOME","home:")
20961 environment.default_texmfcnf=default_texmfcnf
20962end
20963kpse={ original=kpse }
20964setmetatable(kpse,{
20965 __index=function(kp,name)
20966 report_initialization("fatal error: kpse library is accessed (key: %s)",name)
20967 os.exit()
20968 end
20969} )
20970do
20971 local osfontdir=osgetenv("OSFONTDIR")
20972 if osfontdir and osfontdir~="" then
20973 elseif osname=="windows" then
20974 ossetenv("OSFONTDIR","c:/windows/fonts//")
20975 elseif osname=="macosx" then
20976 ossetenv("OSFONTDIR","$HOME/Library/Fonts//;/Library/Fonts//;/System/Library/Fonts//")
20977 end
20978end
20979if not environment.homedir then
20980 local oldhome=osgetenv('HOME')
20981 local homedir=osgetenv(ostype=="windows" and 'USERPROFILE' or 'HOME') or ''
20982 if not homedir or homedir=="" then
20983 homedir=char(127)
20984 end
20985 homedir=file.collapsepath(homedir)
20986 ossetenv("HOME",homedir)
20987 ossetenv("USERPROFILE",homedir)
20988 environment.oldhome=oldhome
20989 environment.homedir=homedir
20990end
20991do
20992 local args=environment.originalarguments or arg
20993 if not environment.ownmain then
20994 environment.ownmain=status and string.match(string.lower(status.banner),"this is ([%a]+)") or "luatex"
20995 end
20996 local ownbin=environment.ownbin or os.selfbin or args[-2] or arg[-2] or args[-1] or arg[-1] or arg[0] or "luametatex"
20997 local ownpath=environment.ownpath or os.selfdir
20998 ownbin=file.collapsepath(ownbin)
20999 ownpath=file.collapsepath(ownpath)
21000 ownpath=expandlink(ownpath,trace_locating and report_initialization)
21001 if not ownpath or ownpath=="" or ownpath=="unset" then
21002 ownpath=args[-1] or arg[-1]
21003 ownpath=ownpath and filedirname(gsub(ownpath,"\\","/"))
21004 if not ownpath or ownpath=="" then
21005 ownpath=args[-0] or arg[-0]
21006 ownpath=ownpath and filedirname(gsub(ownpath,"\\","/"))
21007 end
21008 local binary=ownbin
21009 if not ownpath or ownpath=="" then
21010 ownpath=ownpath and filedirname(binary)
21011 end
21012 if not ownpath or ownpath=="" then
21013 if os.binsuffix~="" then
21014 binary=replacesuffix(binary,os.binsuffix)
21015 end
21016 local path=osgetenv("PATH")
21017 if path then
21018 for p in gmatch(path,"[^"..io.pathseparator.."]+") do
21019 local b=filejoin(p,binary)
21020 if isfile(b) then
21021 ownpath=expandlink(p,trace_locating and report_initialization)
21022 break
21023 end
21024 end
21025 end
21026 end
21027 if not ownpath or ownpath=="" then
21028 ownpath="."
21029 report_initialization("forcing fallback to ownpath %a",ownpath)
21030 elseif trace_locating then
21031 report_initialization("using ownpath %a",ownpath)
21032 end
21033 end
21034 environment.ownbin=ownbin
21035 environment.ownpath=ownpath
21036end
21037resolvers.ownpath=environment.ownpath
21038function resolvers.getownpath()
21039 return environment.ownpath
21040end
21041do
21042 local ownpath=environment.ownpath or dir.current()
21043 if ownpath then
21044 ossetenv('SELFAUTOLOC',file.collapsepath(ownpath))
21045 ossetenv('SELFAUTODIR',file.collapsepath(ownpath.."/.."))
21046 ossetenv('SELFAUTOPARENT',file.collapsepath(ownpath.."/../.."))
21047 else
21048 report_initialization("error: unable to locate ownpath")
21049 os.exit()
21050 end
21051end
21052local texos=environment.texos or osgetenv("TEXOS")
21053local texmfos=environment.texmfos or osgetenv('SELFAUTODIR')
21054if not texos or texos=="" then
21055 texos=file.basename(texmfos)
21056end
21057ossetenv('TEXMFOS',texmfos)
21058ossetenv('TEXOS',texos)
21059ossetenv('SELFAUTOSYSTEM',os.platform)
21060environment.texos=texos
21061environment.texmfos=texmfos
21062local texroot=environment.texroot or osgetenv("TEXROOT")
21063if not texroot or texroot=="" then
21064 texroot=osgetenv('SELFAUTOPARENT')
21065 ossetenv('TEXROOT',texroot)
21066end
21067environment.texroot=file.collapsepath(texroot)
21068local prefixes=utilities.storage.allocate()
21069resolvers.prefixes=prefixes
21070local resolved={}
21071local abstract={}
21072local dynamic={}
21073function resolvers.resetresolve(str)
21074 resolved,abstract={},{}
21075end
21076function resolvers.allprefixes(separator)
21077 local all=table.sortedkeys(prefixes)
21078 if separator then
21079 for i=1,#all do
21080 all[i]=all[i]..":"
21081 end
21082 end
21083 return all
21084end
21085local function _resolve_(method,target)
21086 local action=prefixes[method]
21087 if action then
21088 return action(target)
21089 else
21090 return method..":"..target
21091 end
21092end
21093function resolvers.unresolve(str)
21094 return abstract[str] or str
21095end
21096function resolvers.setdynamic(str)
21097 dynamic[str]=true
21098end
21099local pattern=Cs((C(R("az")^2)*P(":")*C((1-S(" \"\';,"))^1)/_resolve_+P(1))^0)
21100local prefix=C(R("az")^2)*P(":")
21101local target=C((1-S(" \"\';,"))^1)
21102local notarget=(#S(";,")+P(-1))*Cc("")
21103local p_resolve=Cs(((prefix*(target+notarget))/_resolve_+P(1))^0)
21104local p_simple=prefix*P(-1)
21105local function resolve(str)
21106 if type(str)=="table" then
21107 local res={}
21108 for i=1,#str do
21109 res[i]=resolve(str[i])
21110 end
21111 return res
21112 end
21113 local res=resolved[str]
21114 if res then
21115 return res
21116 end
21117 local simple=lpegmatch(p_simple,str)
21118 local action=prefixes[simple]
21119 if action then
21120 local res=action(res)
21121 if not dynamic[simple] then
21122 resolved[simple]=res
21123 abstract[res]=simple
21124 end
21125 return res
21126 end
21127 res=lpegmatch(p_resolve,str)
21128 resolved[str]=res
21129 abstract[res]=str
21130 return res
21131end
21132resolvers.resolve=resolve
21133if type(osuname)=="function" then
21134 for k,v in next,osuname() do
21135 if not prefixes[k] then
21136 prefixes[k]=function() return v end
21137 end
21138 end
21139end
21140if ostype=="unix" then
21141 local pattern
21142 local function makepattern(t,k,v)
21143 if t then
21144 rawset(t,k,v)
21145 end
21146 local colon=P(":")
21147 for k,v in sortedpairs(prefixes) do
21148 if p then
21149 p=P(k)+p
21150 else
21151 p=P(k)
21152 end
21153 end
21154 pattern=Cs((p*colon+colon/";"+P(1))^0)
21155 end
21156 makepattern()
21157 table.setmetatablenewindex(prefixes,makepattern)
21158 function resolvers.repath(str)
21159 return lpegmatch(pattern,str)
21160 end
21161else
21162 function resolvers.repath(str)
21163 return str
21164 end
21165end
21166
21167
21168end
21169
21170do
21171
21172package.loaded["data-exp"] = package.loaded["data-exp"] or true
21173
21174
21175
21176if not modules then modules={} end modules ['data-exp']={
21177 version=1.001,
21178 comment="companion to luat-lib.mkiv",
21179 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
21180 copyright="PRAGMA ADE / ConTeXt Development Team",
21181 license="see context related readme files",
21182}
21183local format,find,gmatch,lower,char,sub=string.format,string.find,string.gmatch,string.lower,string.char,string.sub
21184local concat,sort=table.concat,table.sort
21185local sortedkeys=table.sortedkeys
21186local lpegmatch,lpegpatterns=lpeg.match,lpeg.patterns
21187local Ct,Cs,Cc,Carg,P,C,S=lpeg.Ct,lpeg.Cs,lpeg.Cc,lpeg.Carg,lpeg.P,lpeg.C,lpeg.S
21188local type,next=type,next
21189local isdir=lfs.isdir
21190local collapsepath,joinpath,basename=file.collapsepath,file.join,file.basename
21191local trace_locating=false trackers.register("resolvers.locating",function(v) trace_locating=v end)
21192local trace_expansions=false trackers.register("resolvers.expansions",function(v) trace_expansions=v end)
21193local trace_globbing=true trackers.register("resolvers.globbing",function(v) trace_globbing=v end)
21194local report_expansions=logs.reporter("resolvers","expansions")
21195local report_globbing=logs.reporter("resolvers","globbing")
21196local resolvers=resolvers
21197local resolveprefix=resolvers.resolve
21198local function f_both(a,b)
21199 local t,n={},0
21200 for sb in gmatch(b,"[^,]+") do
21201 for sa in gmatch(a,"[^,]+") do
21202 n=n+1;t[n]=sa..sb
21203 end
21204 end
21205 return concat(t,",")
21206end
21207local comma=P(",")
21208local nocomma=(1-comma)^1
21209local docomma=comma^1/","
21210local before=Cs((nocomma*Carg(1)+docomma)^0)
21211local after=Cs((Carg(1)*nocomma+docomma)^0)
21212local both=Cs(((C(nocomma)*Carg(1))/function(a,b) return lpegmatch(before,b,1,a) end+docomma)^0)
21213local function f_first (a,b) return lpegmatch(after,b,1,a) end
21214local function f_second(a,b) return lpegmatch(before,a,1,b) end
21215local function f_both (a,b) return lpegmatch(both,b,1,a) end
21216local left=P("{")
21217local right=P("}")
21218local var=P((1-S("{}" ))^0)
21219local set=P((1-S("{},"))^0)
21220local other=P(1)
21221local l_first=Cs((Cc("{")*(C(set)*left*C(var)*right/f_first)*Cc("}")+other )^0 )
21222local l_second=Cs((Cc("{")*(left*C(var)*right*C(set)/f_second)*Cc("}")+other )^0 )
21223local l_both=Cs((Cc("{")*(left*C(var)*right*left*C(var)*right/f_both)*Cc("}")+other )^0 )
21224local l_rest=Cs((left*var*(left/"")*var*(right/"")*var*right+other )^0 )
21225local stripper_1=lpeg.stripper ("{}@")
21226local replacer_1=lpeg.replacer { { ",}",",@}" },{ "{,","{@," },}
21227local function splitpathexpr(str,newlist,validate)
21228 if trace_expansions then
21229 report_expansions("expanding variable %a",str)
21230 end
21231 local t,ok,done=newlist or {},false,false
21232 local n=#t
21233 str=lpegmatch(replacer_1,str)
21234 repeat
21235 local old=str
21236 repeat
21237 local old=str
21238 str=lpegmatch(l_first,str)
21239 until old==str
21240 repeat
21241 local old=str
21242 str=lpegmatch(l_second,str)
21243 until old==str
21244 repeat
21245 local old=str
21246 str=lpegmatch(l_both,str)
21247 until old==str
21248 repeat
21249 local old=str
21250 str=lpegmatch(l_rest,str)
21251 until old==str
21252 until old==str
21253 str=lpegmatch(stripper_1,str)
21254 if validate then
21255 for s in gmatch(str,"[^,]+") do
21256 s=validate(s)
21257 if s then
21258 n=n+1
21259 t[n]=s
21260 end
21261 end
21262 else
21263 for s in gmatch(str,"[^,]+") do
21264 n=n+1
21265 t[n]=s
21266 end
21267 end
21268 if trace_expansions then
21269 for k=1,#t do
21270 report_expansions("% 4i: %s",k,t[k])
21271 end
21272 end
21273 return t
21274end
21275local function validate(s)
21276 s=collapsepath(s)
21277 return s~="" and not find(s,"^!*unset/*$") and s
21278end
21279resolvers.validatedpath=validate
21280function resolvers.expandedpathfromlist(pathlist)
21281 local newlist={}
21282 for k=1,#pathlist do
21283 splitpathexpr(pathlist[k],newlist,validate)
21284 end
21285 return newlist
21286end
21287local usedhomedir=nil
21288local donegation=(P("!")/"" )^0
21289local doslashes=(P("\\")/"/"+1)^0
21290local function expandedhome()
21291 if not usedhomedir then
21292 usedhomedir=lpegmatch(Cs(donegation*doslashes),environment.homedir or "")
21293 if usedhomedir=="~" or usedhomedir=="" or not isdir(usedhomedir) then
21294 if trace_expansions then
21295 report_expansions("no home dir set, ignoring dependent path using current path")
21296 end
21297 usedhomedir="."
21298 end
21299 end
21300 return usedhomedir
21301end
21302local dohome=((P("~")+P("$HOME")+P("%HOME%"))/expandedhome)^0
21303local cleanup=Cs(donegation*dohome*doslashes)
21304resolvers.cleanpath=function(str)
21305 return str and lpegmatch(cleanup,str) or ""
21306end
21307local expandhome=P("~")/"$HOME"
21308local dodouble=P('"')/""*(expandhome+(1-P('"')))^0*P('"')/""
21309local dosingle=P("'")/""*(expandhome+(1-P("'")))^0*P("'")/""
21310local dostring=(expandhome+1 )^0
21311local stripper=Cs(
21312 lpegpatterns.unspacer*(dosingle+dodouble+dostring)*lpegpatterns.unspacer
21313)
21314function resolvers.checkedvariable(str)
21315 return type(str)=="string" and lpegmatch(stripper,str) or str
21316end
21317local cache={}
21318local splitter=lpeg.tsplitat(";")
21319local backslashswapper=lpeg.replacer("\\","/")
21320local function splitconfigurationpath(str)
21321 if str then
21322 local found=cache[str]
21323 if not found then
21324 if str=="" then
21325 found={}
21326 else
21327 local split=lpegmatch(splitter,lpegmatch(backslashswapper,str))
21328 found={}
21329 local noffound=0
21330 for i=1,#split do
21331 local s=split[i]
21332 if not find(s,"^{*unset}*") then
21333 noffound=noffound+1
21334 found[noffound]=s
21335 end
21336 end
21337 if trace_expansions then
21338 report_expansions("splitting path specification %a",str)
21339 for k=1,noffound do
21340 report_expansions("% 4i: %s",k,found[k])
21341 end
21342 end
21343 cache[str]=found
21344 end
21345 end
21346 return found
21347 end
21348end
21349resolvers.splitconfigurationpath=splitconfigurationpath
21350function resolvers.splitpath(str)
21351 if type(str)=='table' then
21352 return str
21353 else
21354 return splitconfigurationpath(str)
21355 end
21356end
21357function resolvers.joinpath(str)
21358 if type(str)=='table' then
21359 return joinpath(str)
21360 else
21361 return str
21362 end
21363end
21364local attributes,directory=lfs.attributes,lfs.dir
21365local weird=P(".")^1+lpeg.anywhere(S("~`!#$%^&*()={}[]:;\"\'||<>,?\n\r\t"))
21366local lessweird=P(".")^1+lpeg.anywhere(S("~`#$%^&*:;\"\'||<>,?\n\r\t"))
21367local timer={}
21368local scanned={}
21369local nofscans=0
21370local scancache={}
21371local fullcache={}
21372local nofsharedscans=0
21373local addcasecraptoo=true
21374local function scan(files,remap,spec,path,n,m,r,onlyone,tolerant)
21375 local full=path=="" and spec or (spec..path..'/')
21376 local dirlist={}
21377 local nofdirs=0
21378 local pattern=tolerant and lessweird or weird
21379 local filelist={}
21380 local noffiles=0
21381 for name,mode in directory(full) do
21382 if not lpegmatch(pattern,name) then
21383 if not mode then
21384 mode=attributes(full..name,"mode")
21385 end
21386 if mode=="file" then
21387 n=n+1
21388 noffiles=noffiles+1
21389 filelist[noffiles]=name
21390 elseif mode=="directory" then
21391 m=m+1
21392 nofdirs=nofdirs+1
21393 if path~="" then
21394 dirlist[nofdirs]=path.."/"..name
21395 else
21396 dirlist[nofdirs]=name
21397 end
21398 end
21399 end
21400 end
21401 if noffiles>0 then
21402 sort(filelist)
21403 for i=1,noffiles do
21404 local name=filelist[i]
21405 local lower=lower(name)
21406 local paths=files[lower]
21407 if paths then
21408 if onlyone then
21409 else
21410 if name~=lower then
21411 local rl=remap[lower]
21412 if not rl then
21413 remap[lower]=name
21414 r=r+1
21415 elseif trace_globbing and rl~=name then
21416 report_globbing("confusing filename, name: %a, lower: %a, already: %a",name,lower,rl)
21417 end
21418 if addcasecraptoo then
21419 local paths=files[name]
21420 if not paths then
21421 files[name]=path
21422 elseif type(paths)=="string" then
21423 files[name]={ paths,path }
21424 else
21425 paths[#paths+1]=path
21426 end
21427 end
21428 end
21429 if type(paths)=="string" then
21430 files[lower]={ paths,path }
21431 else
21432 paths[#paths+1]=path
21433 end
21434 end
21435 else
21436 files[lower]=path
21437 if name~=lower then
21438 local rl=remap[lower]
21439 if not rl then
21440 remap[lower]=name
21441 r=r+1
21442 elseif trace_globbing and rl~=name then
21443 report_globbing("confusing filename, name: %a, lower: %a, already: %a",name,lower,rl)
21444 end
21445 end
21446 end
21447 end
21448 end
21449 if nofdirs>0 then
21450 sort(dirlist)
21451 for i=1,nofdirs do
21452 files,remap,n,m,r=scan(files,remap,spec,dirlist[i],n,m,r,onlyonce,tolerant)
21453 end
21454 end
21455 scancache[sub(full,1,-2)]=files
21456 return files,remap,n,m,r
21457end
21458local function scanfiles(path,branch,usecache,onlyonce,tolerant)
21459 local realpath=resolveprefix(path)
21460 if usecache then
21461 local content=fullcache[realpath]
21462 if content then
21463 if trace_locating then
21464 report_expansions("using cached scan of path %a, branch %a",path,branch or path)
21465 end
21466 nofsharedscans=nofsharedscans+1
21467 return content
21468 end
21469 end
21470 statistics.starttiming(timer)
21471 if trace_locating then
21472 report_expansions("scanning path %a, branch %a",path,branch or path)
21473 end
21474 local content
21475 if isdir(realpath) then
21476 local files,remap,n,m,r=scan({},{},realpath..'/',"",0,0,0,onlyonce,tolerant)
21477 content={
21478 metadata={
21479 path=path,
21480 files=n,
21481 directories=m,
21482 remappings=r,
21483 },
21484 files=files,
21485 remap=remap,
21486 }
21487 if trace_locating then
21488 report_expansions("%s files found on %s directories with %s uppercase remappings",n,m,r)
21489 end
21490 else
21491 content={
21492 metadata={
21493 path=path,
21494 files=0,
21495 directories=0,
21496 remappings=0,
21497 },
21498 files={},
21499 remap={},
21500 }
21501 if trace_locating then
21502 report_expansions("invalid path %a",realpath)
21503 end
21504 end
21505 if usecache then
21506 scanned[#scanned+1]=realpath
21507 fullcache[realpath]=content
21508 end
21509 nofscans=nofscans+1
21510 statistics.stoptiming(timer)
21511 return content
21512end
21513resolvers.scanfiles=scanfiles
21514function resolvers.simplescanfiles(path,branch,usecache)
21515 return scanfiles(path,branch,usecache,true,true)
21516end
21517function resolvers.scandata()
21518 table.sort(scanned)
21519 return {
21520 n=nofscans,
21521 shared=nofsharedscans,
21522 time=statistics.elapsedtime(timer),
21523 paths=scanned,
21524 }
21525end
21526function resolvers.get_from_content(content,path,name)
21527 if not content then
21528 return
21529 end
21530 local files=content.files
21531 if not files then
21532 return
21533 end
21534 local remap=content.remap
21535 if not remap then
21536 return
21537 end
21538 if name then
21539 local used=lower(name)
21540 return path,remap[used] or used
21541 else
21542 local name=path
21543 local used=lower(name)
21544 local path=files[used]
21545 if path then
21546 return path,remap[used] or used
21547 end
21548 end
21549end
21550local nothing=function() end
21551function resolvers.filtered_from_content(content,pattern)
21552 if content and type(pattern)=="string" then
21553 local pattern=lower(pattern)
21554 local files=content.files
21555 local remap=content.remap
21556 if files and remap then
21557 local f=sortedkeys(files)
21558 local n=#f
21559 local i=0
21560 local function iterator()
21561 while i<n do
21562 i=i+1
21563 local k=f[i]
21564 if find(k,pattern) then
21565 return files[k],remap and remap[k] or k
21566 end
21567 end
21568 end
21569 return iterator
21570 end
21571 end
21572 return nothing
21573end
21574
21575
21576end
21577
21578do
21579
21580package.loaded["data-env"] = package.loaded["data-env"] or true
21581
21582
21583
21584if not modules then modules={} end modules ['data-env']={
21585 version=1.001,
21586 comment="companion to luat-lib.mkiv",
21587 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
21588 copyright="PRAGMA ADE / ConTeXt Development Team",
21589 license="see context related readme files",
21590}
21591local lower,gsub=string.lower,string.gsub
21592local next,rawget=next,rawget
21593local resolvers=resolvers
21594local allocate=utilities.storage.allocate
21595local setmetatableindex=table.setmetatableindex
21596local sortedhash=table.sortedhash
21597local suffixonly=file.suffixonly
21598local formats=allocate()
21599local suffixes=allocate()
21600local dangerous=allocate()
21601local suffixmap=allocate()
21602local usertypes=allocate()
21603resolvers.formats=formats
21604resolvers.suffixes=suffixes
21605resolvers.dangerous=dangerous
21606resolvers.suffixmap=suffixmap
21607resolvers.usertypes=usertypes
21608local luasuffixes=utilities.lua.suffixes
21609local relations=allocate {
21610 core={
21611 ofm={
21612 names={ "ofm","omega font metric","omega font metrics" },
21613 variable='OFMFONTS',
21614 suffixes={ 'ofm','tfm' },
21615 },
21616 ovf={
21617 names={ "ovf","omega virtual font","omega virtual fonts" },
21618 variable='OVFFONTS',
21619 suffixes={ 'ovf','vf' },
21620 },
21621 tfm={
21622 names={ "tfm","tex font metric","tex font metrics" },
21623 variable='TFMFONTS',
21624 suffixes={ 'tfm' },
21625 },
21626 vf={
21627 names={ "vf","virtual font","virtual fonts" },
21628 variable='VFFONTS',
21629 suffixes={ 'vf' },
21630 },
21631 otf={
21632 names={ "otf","opentype","opentype font","opentype fonts"},
21633 variable='OPENTYPEFONTS',
21634 suffixes={ 'otf' },
21635 },
21636 ttf={
21637 names={ "ttf","truetype","truetype font","truetype fonts","truetype collection","truetype collections","truetype dictionary","truetype dictionaries" },
21638 variable='TTFONTS',
21639 suffixes={ 'ttf','ttc','dfont' },
21640 },
21641 afm={
21642 names={ "afm","adobe font metric","adobe font metrics" },
21643 variable="AFMFONTS",
21644 suffixes={ "afm" },
21645 },
21646 pfb={
21647 names={ "pfb","type1","type 1","type1 font","type 1 font","type1 fonts","type 1 fonts" },
21648 variable='T1FONTS',
21649 suffixes={ 'pfb','pfa' },
21650 },
21651 fea={
21652 names={ "fea","font feature","font features","font feature file","font feature files" },
21653 variable='FONTFEATURES',
21654 suffixes={ 'fea' },
21655 },
21656 cid={
21657 names={ "cid","cid map","cid maps","cid file","cid files" },
21658 variable='FONTCIDMAPS',
21659 suffixes={ 'cid','cidmap' },
21660 },
21661 fmt={
21662 names={ "fmt","format","tex format" },
21663 variable='TEXFORMATS',
21664 suffixes={ 'fmt' },
21665 },
21666 mem={
21667 names={ 'mem',"metapost format" },
21668 variable='MPMEMS',
21669 suffixes={ 'mem' },
21670 },
21671 mp={
21672 names={ "mp" },
21673 variable='MPINPUTS',
21674 suffixes=CONTEXTLMTXMODE>0
21675 and { 'mpxl','mpvi','mpiv','mpii','mp' }
21676 or { 'mpvi','mpiv','mpii','mp' },
21677 usertype=true,
21678 },
21679 tex={
21680 names={ "tex" },
21681 variable='TEXINPUTS',
21682 suffixes={ "tex","mkiv","mkvi","mkxl","mklx","mkii","cld","lfg","xml" },
21683 usertype=true,
21684 },
21685 icc={
21686 names={ "icc","icc profile","icc profiles" },
21687 variable='ICCPROFILES',
21688 suffixes={ 'icc' },
21689 },
21690 texmfscripts={
21691 names={ "texmfscript","texmfscripts","script","scripts" },
21692 variable='TEXMFSCRIPTS',
21693 suffixes={ 'lua','rb','pl','py' },
21694 },
21695 lua={
21696 names={ "lua" },
21697 variable='LUAINPUTS',
21698 suffixes={ luasuffixes.lmt,luasuffixes.lua,luasuffixes.luc,luasuffixes.tma,luasuffixes.tmc },
21699 usertype=true,
21700 },
21701 lib={
21702 names={ "lib" },
21703 variable='CLUAINPUTS',
21704 suffixes=os.libsuffix and { os.libsuffix } or { 'dll','so' },
21705 },
21706 bib={
21707 names={ 'bib' },
21708 variable='BIBINPUTS',
21709 suffixes={ 'bib' },
21710 usertype=true,
21711 },
21712 bst={
21713 names={ 'bst' },
21714 variable='BSTINPUTS',
21715 suffixes={ 'bst' },
21716 usertype=true,
21717 },
21718 fontconfig={
21719 names={ 'fontconfig','fontconfig file','fontconfig files' },
21720 variable='FONTCONFIG_PATH',
21721 },
21722 pk={
21723 names={ "pk" },
21724 variable='PKFONTS',
21725 suffixes={ 'pk' },
21726 },
21727 },
21728 obsolete={
21729 enc={
21730 names={ "enc","enc files","enc file","encoding files","encoding file" },
21731 variable='ENCFONTS',
21732 suffixes={ 'enc' },
21733 },
21734 map={
21735 names={ "map","map files","map file" },
21736 variable='TEXFONTMAPS',
21737 suffixes={ 'map' },
21738 },
21739 lig={
21740 names={ "lig files","lig file","ligature file","ligature files" },
21741 variable='LIGFONTS',
21742 suffixes={ 'lig' },
21743 },
21744 opl={
21745 names={ "opl" },
21746 variable='OPLFONTS',
21747 suffixes={ 'opl' },
21748 },
21749 ovp={
21750 names={ "ovp" },
21751 variable='OVPFONTS',
21752 suffixes={ 'ovp' },
21753 },
21754 },
21755 kpse={
21756 base={
21757 names={ 'base',"metafont format" },
21758 variable='MFBASES',
21759 suffixes={ 'base','bas' },
21760 },
21761 cmap={
21762 names={ 'cmap','cmap files','cmap file' },
21763 variable='CMAPFONTS',
21764 suffixes={ 'cmap' },
21765 },
21766 cnf={
21767 names={ 'cnf' },
21768 suffixes={ 'cnf' },
21769 },
21770 web={
21771 names={ 'web' },
21772 suffixes={ 'web','ch' }
21773 },
21774 cweb={
21775 names={ 'cweb' },
21776 suffixes={ 'w','web','ch' },
21777 },
21778 gf={
21779 names={ 'gf' },
21780 suffixes={ '<resolution>gf' },
21781 },
21782 mf={
21783 names={ 'mf' },
21784 variable='MFINPUTS',
21785 suffixes={ 'mf' },
21786 },
21787 mft={
21788 names={ 'mft' },
21789 suffixes={ 'mft' },
21790 },
21791 pk={
21792 names={ 'pk' },
21793 suffixes={ '<resolution>pk' },
21794 },
21795 },
21796}
21797resolvers.relations=relations
21798function resolvers.updaterelations()
21799 for category,categories in sortedhash(relations) do
21800 for name,relation in sortedhash(categories) do
21801 local rn=relation.names
21802 local rv=relation.variable
21803 if rn and rv then
21804 local rs=relation.suffixes
21805 local ru=relation.usertype
21806 for i=1,#rn do
21807 local rni=lower(gsub(rn[i]," ",""))
21808 formats[rni]=rv
21809 if rs then
21810 suffixes[rni]=rs
21811 for i=1,#rs do
21812 local rsi=rs[i]
21813 if not suffixmap[rsi] then
21814 suffixmap[rsi]=rni
21815 end
21816 end
21817 end
21818 end
21819 if ru then
21820 usertypes[name]=true
21821 end
21822 end
21823 end
21824 end
21825end
21826resolvers.updaterelations()
21827local function simplified(t,k)
21828 return k and rawget(t,lower(gsub(k," ",""))) or nil
21829end
21830setmetatableindex(formats,simplified)
21831setmetatableindex(suffixes,simplified)
21832setmetatableindex(suffixmap,simplified)
21833function resolvers.suffixofformat(str)
21834 local s=suffixes[str]
21835 return s and s[1] or ""
21836end
21837function resolvers.suffixofformat(str)
21838 return suffixes[str] or {}
21839end
21840for name,format in next,formats do
21841 dangerous[name]=true
21842end
21843dangerous.tex=nil
21844function resolvers.formatofvariable(str)
21845 return formats[str] or ''
21846end
21847function resolvers.formatofsuffix(str)
21848 return suffixmap[suffixonly(str)] or 'tex'
21849end
21850function resolvers.variableofformat(str)
21851 return formats[str] or ''
21852end
21853function resolvers.variableofformatorsuffix(str)
21854 local v=formats[str]
21855 if v then
21856 return v
21857 end
21858 v=suffixmap[suffixonly(str)]
21859 if v then
21860 return formats[v]
21861 end
21862 return ''
21863end
21864
21865
21866end
21867
21868do
21869
21870package.loaded["data-tmp"] = package.loaded["data-tmp"] or true
21871
21872
21873
21874if not modules then modules={} end modules ['data-tmp']={
21875 version=1.100,
21876 comment="companion to luat-lib.mkiv",
21877 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
21878 copyright="PRAGMA ADE / ConTeXt Development Team",
21879 license="see context related readme files"
21880}
21881local next,type=next,type
21882local pcall,loadfile,collectgarbage=pcall,loadfile,collectgarbage
21883local format,lower,gsub=string.format,string.lower,string.gsub
21884local concat,serialize,fastserialize,serializetofile=table.concat,table.serialize,table.fastserialize,table.tofile
21885local mkdirs,expanddirname,isdir,isfile=dir.mkdirs,dir.expandname,lfs.isdir,lfs.isfile
21886local is_writable,is_readable=file.is_writable,file.is_readable
21887local collapsepath,joinfile,addsuffix,dirname=file.collapsepath,file.join,file.addsuffix,file.dirname
21888local savedata=file.savedata
21889local formatters=string.formatters
21890local osexit,osdate,osuuid=os.exit,os.date,os.uuid
21891local removefile=os.remove
21892local md5hex=md5.hex
21893local trace_locating=false trackers.register("resolvers.locating",function(v) trace_locating=v end)
21894local trace_cache=false trackers.register("resolvers.cache",function(v) trace_cache=v end)
21895local report_caches=logs.reporter("resolvers","caches")
21896local report_resolvers=logs.reporter("resolvers","caching")
21897local resolvers=resolvers
21898local cleanpath=resolvers.cleanpath
21899local resolvepath=resolvers.resolve
21900local luautilities=utilities.lua
21901do
21902 local directive_cleanup=false directives.register("system.compile.cleanup",function(v) directive_cleanup=v end)
21903 local directive_strip=false directives.register("system.compile.strip",function(v) directive_strip=v end)
21904 local compilelua=luautilities.compile
21905 function luautilities.compile(luafile,lucfile,cleanup,strip)
21906 if cleanup==nil then cleanup=directive_cleanup end
21907 if strip==nil then strip=directive_strip end
21908 return compilelua(luafile,lucfile,cleanup,strip)
21909 end
21910end
21911caches=caches or {}
21912local caches=caches
21913local writable=nil
21914local readables={}
21915local usedreadables={}
21916local compilelua=luautilities.compile
21917local luasuffixes=luautilities.suffixes
21918caches.base=caches.base or (LUATEXENGINE and LUATEXENGINE.."-cache") or "luatex-cache"
21919caches.more=caches.more or "context"
21920caches.defaults={ "TMPDIR","TEMPDIR","TMP","TEMP","HOME","HOMEPATH" }
21921local direct_cache=false
21922local fast_cache=false
21923local cache_tree=false
21924directives.register("system.caches.direct",function(v) direct_cache=true end)
21925directives.register("system.caches.fast",function(v) fast_cache=true end)
21926local function configfiles()
21927 return concat(resolvers.configurationfiles(),";")
21928end
21929local function hashed(tree)
21930 tree=gsub(tree,"[\\/]+$","")
21931 tree=lower(tree)
21932 local hash=md5hex(tree)
21933 if trace_cache or trace_locating then
21934 report_caches("hashing tree %a, hash %a",tree,hash)
21935 end
21936 return hash
21937end
21938local function treehash()
21939 local tree=configfiles()
21940 if not tree or tree=="" then
21941 return false
21942 else
21943 return hashed(tree)
21944 end
21945end
21946caches.hashed=hashed
21947caches.treehash=treehash
21948caches.configfiles=configfiles
21949local function identify()
21950 local texmfcaches=resolvers.cleanpathlist("TEXMFCACHE")
21951 if texmfcaches then
21952 for k=1,#texmfcaches do
21953 local cachepath=texmfcaches[k]
21954 if cachepath~="" then
21955 cachepath=resolvepath(cachepath)
21956 cachepath=cleanpath(cachepath)
21957 cachepath=collapsepath(cachepath)
21958 local valid=isdir(cachepath)
21959 if valid then
21960 if is_readable(cachepath) then
21961 readables[#readables+1]=cachepath
21962 if not writable and is_writable(cachepath) then
21963 writable=cachepath
21964 end
21965 end
21966 elseif not writable then
21967 local cacheparent=dirname(cachepath)
21968 if is_writable(cacheparent) then
21969 mkdirs(cachepath)
21970 if isdir(cachepath) and is_writable(cachepath) then
21971 report_caches("path %a created",cachepath)
21972 writable=cachepath
21973 readables[#readables+1]=cachepath
21974 end
21975 end
21976 end
21977 end
21978 end
21979 end
21980 local texmfcaches=caches.defaults
21981 if texmfcaches then
21982 for k=1,#texmfcaches do
21983 local cachepath=texmfcaches[k]
21984 cachepath=resolvers.expansion(cachepath)
21985 if cachepath~="" then
21986 cachepath=resolvepath(cachepath)
21987 cachepath=cleanpath(cachepath)
21988 local valid=isdir(cachepath)
21989 if valid and is_readable(cachepath) then
21990 if not writable and is_writable(cachepath) then
21991 readables[#readables+1]=cachepath
21992 writable=cachepath
21993 break
21994 end
21995 end
21996 end
21997 end
21998 end
21999 if not writable then
22000 report_caches("fatal error: there is no valid writable cache path defined")
22001 osexit()
22002 elseif #readables==0 then
22003 report_caches("fatal error: there is no valid readable cache path defined")
22004 osexit()
22005 end
22006 writable=expanddirname(cleanpath(writable))
22007 local base=caches.base
22008 local more=caches.more
22009 local tree=cache_tree or treehash()
22010 if tree then
22011 cache_tree=tree
22012 writable=mkdirs(writable,base,more,tree)
22013 for i=1,#readables do
22014 readables[i]=joinfile(readables[i],base,more,tree)
22015 end
22016 else
22017 writable=mkdirs(writable,base,more)
22018 for i=1,#readables do
22019 readables[i]=joinfile(readables[i],base,more)
22020 end
22021 end
22022 if trace_cache then
22023 for i=1,#readables do
22024 report_caches("using readable path %a (order %s)",readables[i],i)
22025 end
22026 report_caches("using writable path %a",writable)
22027 end
22028 identify=function()
22029 return writable,readables
22030 end
22031 return writable,readables
22032end
22033function caches.usedpaths(separator)
22034 local writable,readables=identify()
22035 if #readables>1 then
22036 local result={}
22037 local done={}
22038 for i=1,#readables do
22039 local readable=readables[i]
22040 if readable==writable then
22041 done[readable]=true
22042 result[#result+1]=formatters["readable+writable: %a"](readable)
22043 elseif usedreadables[i] then
22044 done[readable]=true
22045 result[#result+1]=formatters["readable: %a"](readable)
22046 end
22047 end
22048 if not done[writable] then
22049 result[#result+1]=formatters["writable: %a"](writable)
22050 end
22051 return concat(result,separator or " | ")
22052 else
22053 return writable or "?"
22054 end
22055end
22056local r_cache={}
22057local w_cache={}
22058local function getreadablepaths(...)
22059 local tags={... }
22060 local hash=concat(tags,"/")
22061 local done=r_cache[hash]
22062 if not done then
22063 local writable,readables=identify()
22064 if #tags>0 then
22065 done={}
22066 for i=1,#readables do
22067 done[i]=joinfile(readables[i],...)
22068 end
22069 else
22070 done=readables
22071 end
22072 r_cache[hash]=done
22073 end
22074 return done
22075end
22076local function getwritablepath(...)
22077 local tags={... }
22078 local hash=concat(tags,"/")
22079 local done=w_cache[hash]
22080 if not done then
22081 local writable,readables=identify()
22082 if #tags>0 then
22083 done=mkdirs(writable,...)
22084 else
22085 done=writable
22086 end
22087 w_cache[hash]=done
22088 end
22089 return done
22090end
22091local function setfirstwritablefile(filename,...)
22092 local wr=getwritablepath(...)
22093 local fullname=joinfile(wr,filename)
22094 return fullname,wr
22095end
22096local function setluanames(path,name)
22097 return
22098 format("%s/%s.%s",path,name,luasuffixes.tma),
22099 format("%s/%s.%s",path,name,luasuffixes.tmc)
22100end
22101local function getfirstreadablefile(filename,...)
22102 local fullname,path=setfirstwritablefile(filename,...)
22103 if is_readable(fullname) then
22104 return fullname,path
22105 end
22106 local rd=getreadablepaths(...)
22107 for i=1,#rd do
22108 local path=rd[i]
22109 local fullname=joinfile(path,filename)
22110 if is_readable(fullname) then
22111 usedreadables[i]=true
22112 return fullname,path
22113 end
22114 end
22115 return fullname,path
22116end
22117caches.getreadablepaths=getreadablepaths
22118caches.getwritablepath=getwritablepath
22119caches.setfirstwritablefile=setfirstwritablefile
22120caches.getfirstreadablefile=getfirstreadablefile
22121caches.setluanames=setluanames
22122local checkmemory=utilities and utilities.lua and utilities.lua.checkmemory
22123local threshold=100
22124function caches.loaddata(readables,name,writable)
22125 local used=checkmemory and checkmemory()
22126 if type(readables)=="string" then
22127 readables={ readables }
22128 end
22129 for i=1,#readables do
22130 local path=readables[i]
22131 local loader=false
22132 local state=false
22133 local tmaname,tmcname=setluanames(path,name)
22134 if isfile(tmcname) then
22135 state,loader=pcall(loadfile,tmcname)
22136 end
22137 if not loader and isfile(tmaname) then
22138 local tmacrap,tmcname=setluanames(writable,name)
22139 if isfile(tmcname) then
22140 state,loader=pcall(loadfile,tmcname)
22141 end
22142 compilelua(tmaname,tmcname)
22143 if isfile(tmcname) then
22144 state,loader=pcall(loadfile,tmcname)
22145 end
22146 if not loader then
22147 state,loader=pcall(loadfile,tmaname)
22148 end
22149 end
22150 if loader then
22151 loader=loader()
22152 if checkmemory then
22153 checkmemory(used,threshold)
22154 else
22155 collectgarbage("step")
22156 end
22157 return loader
22158 end
22159 end
22160 return false
22161end
22162function caches.is_writable(filepath,filename)
22163 local tmaname,tmcname=setluanames(filepath,filename)
22164 return is_writable(tmaname)
22165end
22166local saveoptions={ compact=true,accurate=not JITSUPPORTED }
22167function caches.savedata(filepath,filename,data,fast)
22168 local tmaname,tmcname=setluanames(filepath,filename)
22169 data.cache_uuid=osuuid()
22170 if fast or fast_cache then
22171 savedata(tmaname,fastserialize(data,true))
22172 elseif direct_cache then
22173 savedata(tmaname,serialize(data,true,saveoptions))
22174 else
22175 serializetofile(tmaname,data,true,saveoptions)
22176 end
22177 compilelua(tmaname,tmcname)
22178end
22179local content_state={}
22180function caches.contentstate()
22181 return content_state or {}
22182end
22183function caches.loadcontent(cachename,dataname,filename)
22184 if not filename then
22185 local name=hashed(cachename)
22186 local full,path=getfirstreadablefile(addsuffix(name,luasuffixes.lua),"trees")
22187 filename=joinfile(path,name)
22188 end
22189 local state,blob=pcall(loadfile,addsuffix(filename,luasuffixes.luc))
22190 if not blob then
22191 state,blob=pcall(loadfile,addsuffix(filename,luasuffixes.lua))
22192 end
22193 if blob then
22194 local data=blob()
22195 if data and data.content then
22196 if data.type==dataname then
22197 if data.version==resolvers.cacheversion then
22198 content_state[#content_state+1]=data.uuid
22199 if trace_locating then
22200 report_resolvers("loading %a for %a from %a",dataname,cachename,filename)
22201 end
22202 return data.content
22203 else
22204 report_resolvers("skipping %a for %a from %a (version mismatch)",dataname,cachename,filename)
22205 end
22206 else
22207 report_resolvers("skipping %a for %a from %a (datatype mismatch)",dataname,cachename,filename)
22208 end
22209 elseif trace_locating then
22210 report_resolvers("skipping %a for %a from %a (no content)",dataname,cachename,filename)
22211 end
22212 elseif trace_locating then
22213 report_resolvers("skipping %a for %a from %a (invalid file)",dataname,cachename,filename)
22214 end
22215end
22216function caches.collapsecontent(content)
22217 for k,v in next,content do
22218 if type(v)=="table" and #v==1 then
22219 content[k]=v[1]
22220 end
22221 end
22222end
22223function caches.savecontent(cachename,dataname,content,filename)
22224 if not filename then
22225 local name=hashed(cachename)
22226 local full,path=setfirstwritablefile(addsuffix(name,luasuffixes.lua),"trees")
22227 filename=joinfile(path,name)
22228 end
22229 local luaname=addsuffix(filename,luasuffixes.lua)
22230 local lucname=addsuffix(filename,luasuffixes.luc)
22231 if trace_locating then
22232 report_resolvers("preparing %a for %a",dataname,cachename)
22233 end
22234 local data={
22235 type=dataname,
22236 root=cachename,
22237 version=resolvers.cacheversion,
22238 date=osdate("%Y-%m-%d"),
22239 time=osdate("%H:%M:%S"),
22240 content=content,
22241 uuid=osuuid(),
22242 }
22243 local ok=savedata(luaname,serialize(data,true))
22244 if ok then
22245 if trace_locating then
22246 report_resolvers("category %a, cachename %a saved in %a",dataname,cachename,luaname)
22247 end
22248 if compilelua(luaname,lucname) then
22249 if trace_locating then
22250 report_resolvers("%a compiled to %a",dataname,lucname)
22251 end
22252 return true
22253 else
22254 if trace_locating then
22255 report_resolvers("compiling failed for %a, deleting file %a",dataname,lucname)
22256 end
22257 removefile(lucname)
22258 end
22259 elseif trace_locating then
22260 report_resolvers("unable to save %a in %a (access error)",dataname,luaname)
22261 end
22262end
22263
22264
22265end
22266
22267do
22268
22269package.loaded["data-met"] = package.loaded["data-met"] or true
22270
22271
22272
22273if not modules then modules={} end modules ['data-met']={
22274 version=1.100,
22275 comment="companion to luat-lib.mkiv",
22276 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
22277 copyright="PRAGMA ADE / ConTeXt Development Team",
22278 license="see context related readme files"
22279}
22280local type=type
22281local find=string.find
22282local addurlscheme,urlhashed=url.addscheme,url.hashed
22283local collapsepath,joinfile=file.collapsepath,file.join
22284local report_methods=logs.reporter("resolvers","methods")
22285local trace_locating=false
22286local trace_methods=false
22287trackers.register("resolvers.locating",function(v) trace_methods=v end)
22288trackers.register("resolvers.methods",function(v) trace_methods=v end)
22289local allocate=utilities.storage.allocate
22290local resolvers=resolvers
22291local registered={}
22292local function splitmethod(filename)
22293 if not filename then
22294 return {
22295 scheme="unknown",
22296 original=filename,
22297 }
22298 end
22299 if type(filename)=="table" then
22300 return filename
22301 end
22302 filename=collapsepath(filename,".")
22303 if not find(filename,"://",1,true) then
22304 return {
22305 scheme="file",
22306 path=filename,
22307 original=filename,
22308 filename=filename,
22309 }
22310 end
22311 local specification=urlhashed(filename)
22312 if not specification.scheme or specification.scheme=="" then
22313 return {
22314 scheme="file",
22315 path=filename,
22316 original=filename,
22317 filename=filename,
22318 }
22319 else
22320 return specification
22321 end
22322end
22323resolvers.splitmethod=splitmethod
22324local function methodhandler(what,first,...)
22325 local method=registered[what]
22326 if method then
22327 local how=method.how
22328 local namespace=method.namespace
22329 if how=="uri" or how=="url" then
22330 local specification=splitmethod(first)
22331 local scheme=specification.scheme
22332 local resolver=namespace and namespace[scheme]
22333 if resolver then
22334 if trace_methods then
22335 report_methods("resolving, method %a, how %a, handler %a, argument %a",what,how,scheme,first)
22336 end
22337 return resolver(specification,...)
22338 else
22339 resolver=namespace.default or namespace.file
22340 if resolver then
22341 if trace_methods then
22342 report_methods("resolving, method %a, how %a, handler %a, argument %a",what,how,"default",first)
22343 end
22344 return resolver(specification,...)
22345 elseif trace_methods then
22346 report_methods("resolving, method %a, how %a, handler %a, argument %a",what,how,"unset")
22347 end
22348 end
22349 elseif how=="tag" then
22350 local resolver=namespace and namespace[first]
22351 if resolver then
22352 if trace_methods then
22353 report_methods("resolving, method %a, how %a, tag %a",what,how,first)
22354 end
22355 return resolver(...)
22356 else
22357 resolver=namespace.default or namespace.file
22358 if resolver then
22359 if trace_methods then
22360 report_methods("resolving, method %a, how %a, tag %a",what,how,"default")
22361 end
22362 return resolver(...)
22363 elseif trace_methods then
22364 report_methods("resolving, method %a, how %a, tag %a",what,how,"unset")
22365 end
22366 end
22367 end
22368 else
22369 report_methods("resolving, invalid method %a")
22370 end
22371end
22372resolvers.methodhandler=methodhandler
22373function resolvers.registermethod(name,namespace,how)
22374 registered[name]={
22375 how=how or "tag",
22376 namespace=namespace
22377 }
22378 namespace["byscheme"]=function(scheme,filename,...)
22379 if scheme=="file" then
22380 return methodhandler(name,filename,...)
22381 else
22382 return methodhandler(name,addurlscheme(filename,scheme),...)
22383 end
22384 end
22385end
22386local concatinators=allocate { notfound=joinfile }
22387local locators=allocate { notfound=function() end }
22388local hashers=allocate { notfound=function() end }
22389local generators=allocate { notfound=function() end }
22390resolvers.concatinators=concatinators
22391resolvers.locators=locators
22392resolvers.hashers=hashers
22393resolvers.generators=generators
22394local registermethod=resolvers.registermethod
22395registermethod("concatinators",concatinators,"tag")
22396registermethod("locators",locators,"uri")
22397registermethod("hashers",hashers,"uri")
22398registermethod("generators",generators,"uri")
22399
22400
22401end
22402
22403do
22404
22405package.loaded["data-res"] = package.loaded["data-res"] or true
22406
22407
22408
22409if not modules then modules={} end modules ['data-res']={
22410 version=1.001,
22411 comment="companion to luat-lib.mkiv",
22412 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
22413 copyright="PRAGMA ADE / ConTeXt Development Team",
22414 license="see context related readme files",
22415}
22416local gsub,find,lower,upper,match,gmatch=string.gsub,string.find,string.lower,string.upper,string.match,string.gmatch
22417local concat,insert,remove=table.concat,table.insert,table.remove
22418local next,type,rawget,loadfile=next,type,rawget,loadfile
22419local mergedtable=table.merged
22420local os=os
22421local P,S,R,C,Cc,Cs,Ct,Carg=lpeg.P,lpeg.S,lpeg.R,lpeg.C,lpeg.Cc,lpeg.Cs,lpeg.Ct,lpeg.Carg
22422local lpegmatch,lpegpatterns=lpeg.match,lpeg.patterns
22423local formatters=string.formatters
22424local filedirname=file.dirname
22425local filebasename=file.basename
22426local suffixonly=file.suffixonly
22427local addsuffix=file.addsuffix
22428local removesuffix=file.removesuffix
22429local filejoin=file.join
22430local collapsepath=file.collapsepath
22431local joinpath=file.joinpath
22432local is_qualified_path=file.is_qualified_path
22433local allocate=utilities.storage.allocate
22434local settings_to_array=utilities.parsers.settings_to_array
22435local urlhasscheme=url.hasscheme
22436local getcurrentdir=lfs.currentdir
22437local isfile=lfs.isfile
22438local isdir=lfs.isdir
22439local setmetatableindex=table.setmetatableindex
22440local luasuffixes=utilities.lua.suffixes
22441local trace_locating=false trackers .register("resolvers.locating",function(v) trace_locating=v end)
22442local trace_details=false trackers .register("resolvers.details",function(v) trace_details=v end)
22443local trace_expansions=false trackers .register("resolvers.expansions",function(v) trace_expansions=v end)
22444local trace_paths=false trackers .register("resolvers.paths",function(v) trace_paths=v end)
22445local resolve_otherwise=true directives.register("resolvers.otherwise",function(v) resolve_otherwise=v end)
22446local report_resolving=logs.reporter("resolvers","resolving")
22447local resolvers=resolvers
22448local expandedpathfromlist=resolvers.expandedpathfromlist
22449local checkedvariable=resolvers.checkedvariable
22450local splitconfigurationpath=resolvers.splitconfigurationpath
22451local methodhandler=resolvers.methodhandler
22452local filtered=resolvers.filtered_from_content
22453local lookup=resolvers.get_from_content
22454local cleanpath=resolvers.cleanpath
22455local resolveprefix=resolvers.resolve
22456local initializesetter=utilities.setters.initialize
22457local ostype,osname,osenv,ossetenv,osgetenv=os.type,os.name,os.env,os.setenv,os.getenv
22458resolvers.cacheversion="1.100"
22459resolvers.configbanner=""
22460resolvers.homedir=environment.homedir
22461resolvers.luacnfname="texmfcnf.lua"
22462resolvers.luacnffallback="contextcnf.lua"
22463resolvers.luacnfstate="unknown"
22464local criticalvars={
22465 "SELFAUTOLOC",
22466 "SELFAUTODIR",
22467 "SELFAUTOPARENT",
22468 "TEXMFCNF",
22469 "TEXMF",
22470 "TEXOS",
22471}
22472if environment.default_texmfcnf then
22473 resolvers.luacnfspec="home:texmf/web2c;"..environment.default_texmfcnf
22474else
22475 local texroot=environment.texroot
22476 resolvers.luacnfspec="home:texmf/web2c;selfautoparent:/texmf-local/web2c;selfautoparent:/texmf-context/web2c;selfautoparent:/texmf/web2c"
22477 if texroot and isdir(texroot.."/texmf-context") then
22478 elseif texroot and isdir(texroot.."/texmf-dist") then
22479 resolvers.luacnfspec="home:texmf/web2c;selfautoparent:/texmf-local/web2c;selfautoparent:/texmf-dist/web2c;selfautoparent:/texmf/web2c"
22480 elseif ostype~="windows" and isdir("/etc/texmf/web2c") then
22481 resolvers.luacnfspec="home:texmf/web2c;/etc/texmf/web2c;selfautodir:/share/texmf/web2c"
22482 else
22483 end
22484end
22485local unset_variable="unset"
22486local formats=resolvers.formats
22487local suffixes=resolvers.suffixes
22488local usertypes=resolvers.usertypes
22489local dangerous=resolvers.dangerous
22490local suffixmap=resolvers.suffixmap
22491resolvers.defaultsuffixes={ "tex" }
22492local instance=nil
22493local variable
22494local expansion
22495local setenv
22496local getenv
22497local formatofsuffix=resolvers.formatofsuffix
22498local splitpath=resolvers.splitpath
22499local splitmethod=resolvers.splitmethod
22500setenv=function(key,value,raw)
22501 if instance then
22502 instance.environment[key]=value
22503 ossetenv(key,raw and value or resolveprefix(value))
22504 end
22505end
22506getenv=function(key)
22507 local value=rawget(instance.environment,key)
22508 if value and value~="" then
22509 return value
22510 else
22511 local e=osgetenv(key)
22512 return e~=nil and e~="" and checkedvariable(e) or ""
22513 end
22514end
22515resolvers.getenv=getenv
22516resolvers.setenv=setenv
22517local dollarstripper=lpeg.stripper("$")
22518local inhibitstripper=P("!")^0*Cs(P(1)^0)
22519local expandedvariable,resolvedvariable do
22520 local function resolveinstancevariable(k)
22521 return instance.expansions[k]
22522 end
22523 local p_variable=P("$")/""
22524 local p_key=C(R("az","AZ","09","__","--")^1)
22525 local p_whatever=P(";")*((1-S("!{}/\\"))^1*P(";")/"")+P(";")*(P(";")/"")+P(1)
22526 local variableexpander=Cs((p_variable*(p_key/resolveinstancevariable)+p_whatever)^1 )
22527 local p_cleaner=P("\\")/"/"+P(";")*S("!{}/\\")^0*P(";")^1/";"
22528 local variablecleaner=Cs((p_cleaner+P(1))^0)
22529 local p_variable=R("az","AZ","09","__","--")^1/resolveinstancevariable
22530 local p_variable=(P("$")/"")*(p_variable+(P("{")/"")*p_variable*(P("}")/""))
22531 local variableresolver=Cs((p_variable+P(1))^0)
22532 expandedvariable=function(var)
22533 return lpegmatch(variableexpander,var) or var
22534 end
22535 function resolvers.reset()
22536 if trace_locating then
22537 report_resolving("creating instance")
22538 end
22539 local environment={}
22540 local variables={}
22541 local expansions={}
22542 local order={}
22543 instance={
22544 environment=environment,
22545 variables=variables,
22546 expansions=expansions,
22547 order=order,
22548 files={},
22549 setups={},
22550 found={},
22551 foundintrees={},
22552 hashes={},
22553 hashed={},
22554 pathlists=false,
22555 specification={},
22556 lists={},
22557 data={},
22558 fakepaths={},
22559 remember=true,
22560 diskcache=true,
22561 renewcache=false,
22562 renewtree=false,
22563 loaderror=false,
22564 savelists=true,
22565 pattern=nil,
22566 force_suffixes=true,
22567 pathstack={},
22568 }
22569 setmetatableindex(variables,function(t,k)
22570 local v
22571 for i=1,#order do
22572 v=order[i][k]
22573 if v~=nil then
22574 t[k]=v
22575 return v
22576 end
22577 end
22578 if v==nil then
22579 v=""
22580 end
22581 t[k]=v
22582 return v
22583 end)
22584 local repath=resolvers.repath
22585 setmetatableindex(environment,function(t,k)
22586 local v=osgetenv(k)
22587 if v==nil then
22588 v=variables[k]
22589 end
22590 if v~=nil then
22591 v=checkedvariable(v) or ""
22592 end
22593 v=repath(v)
22594 t[k]=v
22595 return v
22596 end)
22597 setmetatableindex(expansions,function(t,k)
22598 local v=environment[k]
22599 if type(v)=="string" then
22600 v=lpegmatch(variableresolver,v)
22601 v=lpegmatch(variablecleaner,v)
22602 end
22603 t[k]=v
22604 return v
22605 end)
22606 end
22607end
22608function resolvers.initialized()
22609 return instance~=nil
22610end
22611local function reset_hashes()
22612 instance.lists={}
22613 instance.pathlists=false
22614 instance.found={}
22615end
22616local function reset_caches()
22617 instance.lists={}
22618 instance.pathlists=false
22619end
22620local makepathexpression do
22621 local slash=P("/")
22622 local pathexpressionpattern=Cs (
22623 Cc("^")*(
22624 Cc("%")*S(".-")+slash^2*P(-1)/"/.*"
22625+slash^2/"/"+(1-slash)*P(-1)*Cc("/")+P(1)
22626 )^1*Cc("$")
22627 )
22628 local cache={}
22629 makepathexpression=function(str)
22630 if str=="." then
22631 return "^%./$"
22632 else
22633 local c=cache[str]
22634 if not c then
22635 c=lpegmatch(pathexpressionpattern,str)
22636 cache[str]=c
22637 end
22638 return c
22639 end
22640 end
22641end
22642local function reportcriticalvariables(cnfspec)
22643 if trace_locating then
22644 for i=1,#criticalvars do
22645 local k=criticalvars[i]
22646 local v=getenv(k) or "unknown"
22647 report_resolving("variable %a set to %a",k,v)
22648 end
22649 report_resolving()
22650 if cnfspec then
22651 report_resolving("using configuration specification %a",type(cnfspec)=="table" and concat(cnfspec,",") or cnfspec)
22652 end
22653 report_resolving()
22654 end
22655 reportcriticalvariables=function() end
22656end
22657local function identify_configuration_files()
22658 local specification=instance.specification
22659 if #specification==0 then
22660 local cnfspec=getenv("TEXMFCNF")
22661 if cnfspec=="" then
22662 cnfspec=resolvers.luacnfspec
22663 resolvers.luacnfstate="default"
22664 else
22665 resolvers.luacnfstate="environment"
22666 end
22667 reportcriticalvariables(cnfspec)
22668 local cnfpaths=expandedpathfromlist(splitpath(cnfspec))
22669 local function locatecnf(luacnfname,kind)
22670 for i=1,#cnfpaths do
22671 local filepath=cnfpaths[i]
22672 local filename=collapsepath(filejoin(filepath,luacnfname))
22673 local realname=resolveprefix(filename)
22674 if trace_locating then
22675 local fullpath=gsub(resolveprefix(collapsepath(filepath)),"//","/")
22676 local weirdpath=find(fullpath,"/texmf.+/texmf") or not find(fullpath,"/web2c",1,true)
22677 report_resolving("looking for %s %a on %s path %a from specification %a",
22678 kind,luacnfname,weirdpath and "weird" or "given",fullpath,filepath)
22679 end
22680 if isfile(realname) then
22681 specification[#specification+1]=filename
22682 if trace_locating then
22683 report_resolving("found %s configuration file %a",kind,realname)
22684 end
22685 end
22686 end
22687 end
22688 locatecnf(resolvers.luacnfname,"regular")
22689 if #specification==0 then
22690 locatecnf(resolvers.luacnffallback,"fallback")
22691 end
22692 if trace_locating then
22693 report_resolving()
22694 end
22695 elseif trace_locating then
22696 report_resolving("configuration files already identified")
22697 end
22698end
22699local function load_configuration_files()
22700 local specification=instance.specification
22701 local setups=instance.setups
22702 local order=instance.order
22703 if #specification>0 then
22704 local luacnfname=resolvers.luacnfname
22705 for i=1,#specification do
22706 local filename=specification[i]
22707 local pathname=filedirname(filename)
22708 local filename=filejoin(pathname,luacnfname)
22709 local realname=resolveprefix(filename)
22710 local blob=loadfile(realname)
22711 if blob then
22712 local data=blob()
22713 local parent=data and data.parent
22714 if parent then
22715 local filename=filejoin(pathname,parent)
22716 local realname=resolveprefix(filename)
22717 local blob=loadfile(realname)
22718 if blob then
22719 local parentdata=blob()
22720 if parentdata then
22721 report_resolving("loading configuration file %a",filename)
22722 data=mergedtable(parentdata,data)
22723 end
22724 end
22725 end
22726 data=data and data.content
22727 if data then
22728 if trace_locating then
22729 report_resolving("loading configuration file %a",filename)
22730 report_resolving()
22731 end
22732 local variables=data.variables or {}
22733 local warning=false
22734 for k,v in next,data do
22735 local variant=type(v)
22736 if variant=="table" then
22737 initializesetter(filename,k,v)
22738 elseif variables[k]==nil then
22739 if trace_locating and not warning then
22740 report_resolving("variables like %a in configuration file %a should move to the 'variables' subtable",
22741 k,resolveprefix(filename))
22742 warning=true
22743 end
22744 variables[k]=v
22745 end
22746 end
22747 setups[pathname]=variables
22748 if resolvers.luacnfstate=="default" then
22749 local cnfspec=variables["TEXMFCNF"]
22750 if cnfspec then
22751 if trace_locating then
22752 report_resolving("reloading configuration due to TEXMF redefinition")
22753 end
22754 setenv("TEXMFCNF",cnfspec)
22755 instance.specification={}
22756 identify_configuration_files()
22757 load_configuration_files()
22758 resolvers.luacnfstate="configuration"
22759 break
22760 end
22761 end
22762 else
22763 if trace_locating then
22764 report_resolving("skipping configuration file %a (no content)",filename)
22765 end
22766 setups[pathname]={}
22767 instance.loaderror=true
22768 end
22769 elseif trace_locating then
22770 report_resolving("skipping configuration file %a (no valid format)",filename)
22771 end
22772 order[#order+1]=setups[pathname]
22773 if instance.loaderror then
22774 break
22775 end
22776 end
22777 elseif trace_locating then
22778 report_resolving("warning: no lua configuration files found")
22779 end
22780end
22781local expandedpathlist
22782local unexpandedpathlist
22783function resolvers.configurationfiles()
22784 return instance.specification or {}
22785end
22786local function load_file_databases()
22787 instance.loaderror=false
22788 instance.files={}
22789 if not instance.renewcache then
22790 local hashes=instance.hashes
22791 for k=1,#hashes do
22792 local hash=hashes[k]
22793 resolvers.hashers.byscheme(hash.type,hash.name)
22794 if instance.loaderror then break end
22795 end
22796 end
22797end
22798local function locate_file_databases()
22799 local texmfpaths=expandedpathlist("TEXMF")
22800 if #texmfpaths>0 then
22801 for i=1,#texmfpaths do
22802 local path=collapsepath(texmfpaths[i])
22803 path=gsub(path,"/+$","")
22804 local stripped=lpegmatch(inhibitstripper,path)
22805 if stripped~="" then
22806 local runtime=stripped==path
22807 path=cleanpath(path)
22808 local spec=splitmethod(stripped)
22809 if runtime and (spec.noscheme or spec.scheme=="file") then
22810 stripped="tree:///"..stripped
22811 elseif spec.scheme=="cache" or spec.scheme=="file" then
22812 stripped=spec.path
22813 end
22814 if trace_locating then
22815 if runtime then
22816 report_resolving("locating list of %a (runtime) (%s)",path,stripped)
22817 else
22818 report_resolving("locating list of %a (cached)",path)
22819 end
22820 end
22821 methodhandler('locators',stripped)
22822 end
22823 end
22824 if trace_locating then
22825 report_resolving()
22826 end
22827 elseif trace_locating then
22828 report_resolving("no texmf paths are defined (using TEXMF)")
22829 end
22830end
22831local function generate_file_databases()
22832 local hashes=instance.hashes
22833 for k=1,#hashes do
22834 local hash=hashes[k]
22835 methodhandler('generators',hash.name)
22836 end
22837 if trace_locating then
22838 report_resolving()
22839 end
22840end
22841local function save_file_databases()
22842 local hashes=instance.hashes
22843 local files=instance.files
22844 for i=1,#hashes do
22845 local hash=hashes[i]
22846 local cachename=hash.name
22847 if hash.cache then
22848 local content=files[cachename]
22849 caches.collapsecontent(content)
22850 if trace_locating then
22851 report_resolving("saving tree %a",cachename)
22852 end
22853 caches.savecontent(cachename,"files",content)
22854 elseif trace_locating then
22855 report_resolving("not saving runtime tree %a",cachename)
22856 end
22857 end
22858end
22859function resolvers.renew(hashname)
22860 local files=instance.files
22861 if hashname and hashname~="" then
22862 local expanded=expansion(hashname) or ""
22863 if expanded~="" then
22864 if trace_locating then
22865 report_resolving("identifying tree %a from %a",expanded,hashname)
22866 end
22867 hashname=expanded
22868 else
22869 if trace_locating then
22870 report_resolving("identifying tree %a",hashname)
22871 end
22872 end
22873 local realpath=resolveprefix(hashname)
22874 if isdir(realpath) then
22875 if trace_locating then
22876 report_resolving("using path %a",realpath)
22877 end
22878 methodhandler('generators',hashname)
22879 local content=files[hashname]
22880 caches.collapsecontent(content)
22881 if trace_locating then
22882 report_resolving("saving tree %a",hashname)
22883 end
22884 caches.savecontent(hashname,"files",content)
22885 else
22886 report_resolving("invalid path %a",realpath)
22887 end
22888 end
22889end
22890local function load_databases()
22891 locate_file_databases()
22892 if instance.diskcache and not instance.renewcache then
22893 load_file_databases()
22894 if instance.loaderror then
22895 generate_file_databases()
22896 save_file_databases()
22897 end
22898 else
22899 generate_file_databases()
22900 if instance.renewcache then
22901 save_file_databases()
22902 end
22903 end
22904end
22905function resolvers.appendhash(type,name,cache)
22906 local hashed=instance.hashed
22907 local hashes=instance.hashes
22908 if hashed[name] then
22909 else
22910 if trace_locating then
22911 report_resolving("hash %a appended",name)
22912 end
22913 insert(hashes,{ type=type,name=name,cache=cache } )
22914 hashed[name]=cache
22915 end
22916end
22917function resolvers.prependhash(type,name,cache)
22918 local hashed=instance.hashed
22919 local hashes=instance.hashes
22920 if hashed[name] then
22921 else
22922 if trace_locating then
22923 report_resolving("hash %a prepended",name)
22924 end
22925 insert(hashes,1,{ type=type,name=name,cache=cache } )
22926 hashed[name]=cache
22927 end
22928end
22929function resolvers.extendtexmfvariable(specification)
22930 local environment=instance.environment
22931 local variables=instance.variables
22932 local texmftrees=splitpath(getenv("TEXMF"))
22933 insert(texmftrees,1,specification)
22934 texmftrees=concat(texmftrees,",")
22935 if environment["TEXMF"] then
22936 environment["TEXMF"]=texmftrees
22937 elseif variables["TEXMF"] then
22938 variables["TEXMF"]=texmftrees
22939 else
22940 end
22941 reset_hashes()
22942end
22943function resolvers.splitexpansions()
22944 local expansions=instance.expansions
22945 for k,v in next,expansions do
22946 local t,tn,h,p={},0,{},splitconfigurationpath(v)
22947 for kk=1,#p do
22948 local vv=p[kk]
22949 if vv~="" and not h[vv] then
22950 tn=tn+1
22951 t[tn]=vv
22952 h[vv]=true
22953 end
22954 end
22955 if tn>1 then
22956 expansions[k]=t
22957 else
22958 expansions[k]=t[1]
22959 end
22960 end
22961end
22962function resolvers.datastate()
22963 return caches.contentstate()
22964end
22965variable=function(name)
22966 local variables=instance.variables
22967 local name=name and lpegmatch(dollarstripper,name)
22968 local result=name and variables[name]
22969 return result~=nil and result or ""
22970end
22971expansion=function(name)
22972 local expansions=instance.expansions
22973 local name=name and lpegmatch(dollarstripper,name)
22974 local result=name and expansions[name]
22975 return result~=nil and result or ""
22976end
22977resolvers.variable=variable
22978resolvers.expansion=expansion
22979unexpandedpathlist=function(str)
22980 local pth=variable(str)
22981 local lst=splitpath(pth)
22982 return expandedpathfromlist(lst)
22983end
22984function resolvers.unexpandedpath(str)
22985 return joinpath(unexpandedpathlist(str))
22986end
22987function resolvers.pushpath(name)
22988 local pathstack=instance.pathstack
22989 local lastpath=pathstack[#pathstack]
22990 local pluspath=filedirname(name)
22991 if lastpath then
22992 lastpath=collapsepath(filejoin(lastpath,pluspath))
22993 else
22994 lastpath=collapsepath(pluspath)
22995 end
22996 insert(pathstack,lastpath)
22997 if trace_paths then
22998 report_resolving("pushing path %a",lastpath)
22999 end
23000end
23001function resolvers.poppath()
23002 local pathstack=instance.pathstack
23003 if trace_paths and #pathstack>0 then
23004 report_resolving("popping path %a",pathstack[#pathstack])
23005 end
23006 remove(pathstack)
23007end
23008function resolvers.stackpath()
23009 local pathstack=instance.pathstack
23010 local currentpath=pathstack[#pathstack]
23011 return currentpath~="" and currentpath or nil
23012end
23013local done={}
23014function resolvers.resetextrapaths()
23015 local extra_paths=instance.extra_paths
23016 if not extra_paths then
23017 done={}
23018 instance.extra_paths={}
23019 elseif #ep>0 then
23020 done={}
23021 reset_caches()
23022 end
23023end
23024function resolvers.getextrapaths()
23025 return instance.extra_paths or {}
23026end
23027function resolvers.registerextrapath(paths,subpaths)
23028 if not subpaths or subpaths=="" then
23029 if not paths or path=="" then
23030 return
23031 elseif done[paths] then
23032 return
23033 end
23034 end
23035 local paths=settings_to_array(paths)
23036 local subpaths=settings_to_array(subpaths)
23037 local extra_paths=instance.extra_paths or {}
23038 local oldn=#extra_paths
23039 local newn=oldn
23040 local nofpaths=#paths
23041 local nofsubpaths=#subpaths
23042 if nofpaths>0 then
23043 if nofsubpaths>0 then
23044 for i=1,nofpaths do
23045 local p=paths[i]
23046 for j=1,nofsubpaths do
23047 local s=subpaths[j]
23048 local ps=p.."/"..s
23049 if not done[ps] then
23050 newn=newn+1
23051 extra_paths[newn]=cleanpath(ps)
23052 done[ps]=true
23053 end
23054 end
23055 end
23056 else
23057 for i=1,nofpaths do
23058 local p=paths[i]
23059 if not done[p] then
23060 newn=newn+1
23061 extra_paths[newn]=cleanpath(p)
23062 done[p]=true
23063 end
23064 end
23065 end
23066 elseif nofsubpaths>0 then
23067 for i=1,oldn do
23068 for j=1,nofsubpaths do
23069 local s=subpaths[j]
23070 local ps=extra_paths[i].."/"..s
23071 if not done[ps] then
23072 newn=newn+1
23073 extra_paths[newn]=cleanpath(ps)
23074 done[ps]=true
23075 end
23076 end
23077 end
23078 end
23079 if newn>0 then
23080 instance.extra_paths=extra_paths
23081 end
23082 if newn~=oldn then
23083 reset_caches()
23084 end
23085end
23086function resolvers.pushextrapath(path)
23087 local paths=settings_to_array(path)
23088 local extra_stack=instance.extra_stack
23089 if extra_stack then
23090 insert(extra_stack,1,paths)
23091 else
23092 instance.extra_stack={ paths }
23093 end
23094 reset_caches()
23095end
23096function resolvers.popextrapath()
23097 local extra_stack=instance.extra_stack
23098 if extra_stack then
23099 reset_caches()
23100 return remove(extra_stack,1)
23101 end
23102end
23103local function made_list(instance,list,extra_too)
23104 local done={}
23105 local new={}
23106 local newn=0
23107 local function add(p)
23108 for k=1,#p do
23109 local v=p[k]
23110 if not done[v] then
23111 done[v]=true
23112 newn=newn+1
23113 new[newn]=v
23114 end
23115 end
23116 end
23117 for k=1,#list do
23118 local v=list[k]
23119 if done[v] then
23120 elseif find(v,"^[%.%/]$") then
23121 done[v]=true
23122 newn=newn+1
23123 new[newn]=v
23124 else
23125 break
23126 end
23127 end
23128 if extra_too then
23129 local extra_stack=instance.extra_stack
23130 local extra_paths=instance.extra_paths
23131 if extra_stack and #extra_stack>0 then
23132 for k=1,#extra_stack do
23133 add(extra_stack[k])
23134 end
23135 end
23136 if extra_paths and #extra_paths>0 then
23137 add(extra_paths)
23138 end
23139 end
23140 add(list)
23141 return new
23142end
23143expandedpathlist=function(str,extra_too)
23144 if not str then
23145 return {}
23146 elseif instance.savelists then
23147 str=lpegmatch(dollarstripper,str)
23148 local lists=instance.lists
23149 local lst=lists[str]
23150 if not lst then
23151 local l=made_list(instance,splitpath(expansion(str)),extra_too)
23152 lst=expandedpathfromlist(l)
23153 lists[str]=lst
23154 end
23155 return lst
23156 else
23157 local lst=splitpath(expansion(str))
23158 return made_list(instance,expandedpathfromlist(lst),extra_too)
23159 end
23160end
23161resolvers.expandedpathlist=expandedpathlist
23162resolvers.unexpandedpathlist=unexpandedpathlist
23163function resolvers.cleanpathlist(str)
23164 local t=expandedpathlist(str)
23165 if t then
23166 for i=1,#t do
23167 t[i]=collapsepath(cleanpath(t[i]))
23168 end
23169 end
23170 return t
23171end
23172function resolvers.expandpath(str)
23173 return joinpath(expandedpathlist(str))
23174end
23175local function expandedpathlistfromvariable(str)
23176 str=lpegmatch(dollarstripper,str)
23177 local tmp=resolvers.variableofformatorsuffix(str)
23178 return expandedpathlist(tmp~="" and tmp or str)
23179end
23180function resolvers.expandpathfromvariable(str)
23181 return joinpath(expandedpathlistfromvariable(str))
23182end
23183resolvers.expandedpathlistfromvariable=expandedpathlistfromvariable
23184function resolvers.cleanedpathlist(v)
23185 local t=expandedpathlist(v)
23186 for i=1,#t do
23187 t[i]=resolveprefix(cleanpath(t[i]))
23188 end
23189 return t
23190end
23191function resolvers.expandbraces(str)
23192 local pth=expandedpathfromlist(splitpath(str))
23193 return joinpath(pth)
23194end
23195function resolvers.registerfilehash(name,content,someerror)
23196 local files=instance.files
23197 if content then
23198 files[name]=content
23199 else
23200 files[name]={}
23201 if somerror==true then
23202 instance.loaderror=someerror
23203 end
23204 end
23205end
23206function resolvers.getfilehashes()
23207 return instance and instance.files or {}
23208end
23209function resolvers.gethashes()
23210 return instance and instance.hashes or {}
23211end
23212function resolvers.renewcache()
23213 if instance then
23214 instance.renewcache=true
23215 end
23216end
23217local function isreadable(name)
23218 local readable=isfile(name)
23219 if trace_details then
23220 if readable then
23221 report_resolving("file %a is readable",name)
23222 else
23223 report_resolving("file %a is not readable",name)
23224 end
23225 end
23226 return readable
23227end
23228local function collect_files(names)
23229 local filelist={}
23230 local noffiles=0
23231 local function check(hash,root,pathname,path,basename,name)
23232 if not pathname or find(path,pathname) then
23233 local variant=hash.type
23234 local search=filejoin(root,path,name)
23235 local result=methodhandler('concatinators',variant,root,path,name)
23236 if trace_details then
23237 report_resolving("match: variant %a, search %a, result %a",variant,search,result)
23238 end
23239 noffiles=noffiles+1
23240 filelist[noffiles]={ variant,search,result }
23241 end
23242 end
23243 for k=1,#names do
23244 local filename=names[k]
23245 if trace_details then
23246 report_resolving("checking name %a",filename)
23247 end
23248 local basename=filebasename(filename)
23249 local pathname=filedirname(filename)
23250 if pathname=="" or find(pathname,"^%.") then
23251 pathname=false
23252 else
23253 pathname=gsub(pathname,"%*",".*")
23254 pathname="/"..pathname.."$"
23255 end
23256 local hashes=instance.hashes
23257 local files=instance.files
23258 for h=1,#hashes do
23259 local hash=hashes[h]
23260 local hashname=hash.name
23261 local content=hashname and files[hashname]
23262 if content then
23263 if trace_details then
23264 report_resolving("deep checking %a, base %a, pattern %a",hashname,basename,pathname)
23265 end
23266 local path,name=lookup(content,basename)
23267 if path then
23268 local metadata=content.metadata
23269 local realroot=metadata and metadata.path or hashname
23270 if type(path)=="string" then
23271 check(hash,realroot,pathname,path,basename,name)
23272 else
23273 for i=1,#path do
23274 check(hash,realroot,pathname,path[i],basename,name)
23275 end
23276 end
23277 end
23278 elseif trace_locating then
23279 report_resolving("no match in %a (%s)",hashname,basename)
23280 end
23281 end
23282 end
23283 return noffiles>0 and filelist or nil
23284end
23285local fit={}
23286function resolvers.registerintrees(filename,format,filetype,usedmethod,foundname)
23287 local foundintrees=instance.foundintrees
23288 if usedmethod=="direct" and filename==foundname and fit[foundname] then
23289 else
23290 local collapsed=collapsepath(foundname,true)
23291 local t={
23292 filename=filename,
23293 format=format~="" and format or nil,
23294 filetype=filetype~="" and filetype or nil,
23295 usedmethod=usedmethod,
23296 foundname=foundname,
23297 fullname=collapsed,
23298 }
23299 fit[foundname]=t
23300 foundintrees[#foundintrees+1]=t
23301 end
23302end
23303function resolvers.foundintrees()
23304 return instance.foundintrees or {}
23305end
23306function resolvers.foundintree(fullname)
23307 local f=fit[fullname]
23308 return f and f.usedmethod=="database"
23309end
23310local function can_be_dir(name)
23311 local fakepaths=instance.fakepaths
23312 if not fakepaths[name] then
23313 if isdir(name) then
23314 fakepaths[name]=1
23315 else
23316 fakepaths[name]=2
23317 end
23318 end
23319 return fakepaths[name]==1
23320end
23321local preparetreepattern=Cs((P(".")/"%%."+P("-")/"%%-"+P(1))^0*Cc("$"))
23322local collect_instance_files
23323local function find_analyze(filename,askedformat,allresults)
23324 local filetype=""
23325 local filesuffix=suffixonly(filename)
23326 local wantedfiles={}
23327 wantedfiles[#wantedfiles+1]=filename
23328 if askedformat=="" then
23329 if filesuffix=="" or not suffixmap[filesuffix] then
23330 local defaultsuffixes=resolvers.defaultsuffixes
23331 for i=1,#defaultsuffixes do
23332 local forcedname=filename.."."..defaultsuffixes[i]
23333 wantedfiles[#wantedfiles+1]=forcedname
23334 filetype=formatofsuffix(forcedname)
23335 if trace_locating then
23336 report_resolving("forcing filetype %a",filetype)
23337 end
23338 end
23339 else
23340 filetype=formatofsuffix(filename)
23341 if trace_locating then
23342 report_resolving("using suffix based filetype %a",filetype)
23343 end
23344 end
23345 else
23346 if filesuffix=="" or not suffixmap[filesuffix] then
23347 local format_suffixes=suffixes[askedformat]
23348 if format_suffixes then
23349 for i=1,#format_suffixes do
23350 wantedfiles[#wantedfiles+1]=filename.."."..format_suffixes[i]
23351 end
23352 end
23353 end
23354 filetype=askedformat
23355 if trace_locating then
23356 report_resolving("using given filetype %a",filetype)
23357 end
23358 end
23359 return filetype,wantedfiles
23360end
23361local function find_direct(filename,allresults)
23362 if not dangerous[askedformat] and isreadable(filename) then
23363 if trace_details then
23364 report_resolving("file %a found directly",filename)
23365 end
23366 return "direct",{ filename }
23367 end
23368end
23369local function find_wildcard(filename,allresults)
23370 if find(filename,'*',1,true) then
23371 if trace_locating then
23372 report_resolving("checking wildcard %a",filename)
23373 end
23374 local result=resolvers.findwildcardfiles(filename)
23375 if result then
23376 return "wildcard",result
23377 end
23378 end
23379end
23380local function find_qualified(filename,allresults,askedformat,alsostripped)
23381 if not is_qualified_path(filename) then
23382 return
23383 end
23384 if trace_locating then
23385 report_resolving("checking qualified name %a",filename)
23386 end
23387 if isreadable(filename) then
23388 if trace_details then
23389 report_resolving("qualified file %a found",filename)
23390 end
23391 return "qualified",{ filename }
23392 end
23393 if trace_details then
23394 report_resolving("locating qualified file %a",filename)
23395 end
23396 local forcedname,suffix="",suffixonly(filename)
23397 if suffix=="" then
23398 local format_suffixes=askedformat=="" and resolvers.defaultsuffixes or suffixes[askedformat]
23399 if format_suffixes then
23400 for i=1,#format_suffixes do
23401 local suffix=format_suffixes[i]
23402 forcedname=filename.."."..suffix
23403 if isreadable(forcedname) then
23404 if trace_locating then
23405 report_resolving("no suffix, forcing format filetype %a",suffix)
23406 end
23407 return "qualified",{ forcedname }
23408 end
23409 end
23410 end
23411 end
23412 if alsostripped and suffix and suffix~="" then
23413 local basename=filebasename(filename)
23414 local pattern=lpegmatch(preparetreepattern,filename)
23415 local savedformat=askedformat
23416 local format=savedformat or ""
23417 if format=="" then
23418 askedformat=formatofsuffix(suffix)
23419 end
23420 if not format then
23421 askedformat="othertextfiles"
23422 end
23423 if basename~=filename then
23424 local resolved=collect_instance_files(basename,askedformat,allresults)
23425 if #resolved==0 then
23426 local lowered=lower(basename)
23427 if filename~=lowered then
23428 resolved=collect_instance_files(lowered,askedformat,allresults)
23429 end
23430 end
23431 resolvers.format=savedformat
23432 if #resolved>0 then
23433 local result={}
23434 for r=1,#resolved do
23435 local rr=resolved[r]
23436 if find(rr,pattern) then
23437 result[#result+1]=rr
23438 end
23439 end
23440 if #result>0 then
23441 return "qualified",result
23442 end
23443 end
23444 end
23445 end
23446end
23447local function check_subpath(fname)
23448 if isreadable(fname) then
23449 if trace_details then
23450 report_resolving("found %a by deep scanning",fname)
23451 end
23452 return fname
23453 end
23454end
23455local function makepathlist(list,filetype)
23456 local typespec=resolvers.variableofformat(filetype)
23457 local pathlist=expandedpathlist(typespec,filetype and usertypes[filetype])
23458 local entry={}
23459 if pathlist and #pathlist>0 then
23460 for k=1,#pathlist do
23461 local path=pathlist[k]
23462 local prescanned=find(path,'^!!')
23463 local resursive=find(path,'//$')
23464 local pathname=lpegmatch(inhibitstripper,path)
23465 local expression=makepathexpression(pathname)
23466 local barename=gsub(pathname,"/+$","")
23467 barename=resolveprefix(barename)
23468 local scheme=urlhasscheme(barename)
23469 local schemename=gsub(barename,"%.%*$",'')
23470 entry[k]={
23471 path=path,
23472 pathname=pathname,
23473 prescanned=prescanned,
23474 recursive=recursive,
23475 expression=expression,
23476 barename=barename,
23477 scheme=scheme,
23478 schemename=schemename,
23479 }
23480 end
23481 entry.typespec=typespec
23482 list[filetype]=entry
23483 else
23484 list[filetype]=false
23485 end
23486 return entry
23487end
23488local function find_intree(filename,filetype,wantedfiles,allresults)
23489 local pathlists=instance.pathlists
23490 if not pathlists then
23491 pathlists=setmetatableindex({},makepathlist)
23492 instance.pathlists=pathlists
23493 end
23494 local pathlist=pathlists[filetype]
23495 if pathlist then
23496 local method="intree"
23497 local filelist=collect_files(wantedfiles)
23498 local dirlist={}
23499 local result={}
23500 if filelist then
23501 for i=1,#filelist do
23502 dirlist[i]=filedirname(filelist[i][3]).."/"
23503 end
23504 end
23505 if trace_details then
23506 report_resolving("checking filename %a in tree",filename)
23507 end
23508 for k=1,#pathlist do
23509 local entry=pathlist[k]
23510 local path=entry.path
23511 local pathname=entry.pathname
23512 local done=false
23513 if filelist then
23514 local expression=entry.expression
23515 if trace_details then
23516 report_resolving("using pattern %a for path %a",expression,pathname)
23517 end
23518 for k=1,#filelist do
23519 local fl=filelist[k]
23520 local f=fl[2]
23521 local d=dirlist[k]
23522 if find(d,expression) or find(resolveprefix(d),expression) then
23523 result[#result+1]=resolveprefix(fl[3])
23524 done=true
23525 if allresults then
23526 if trace_details then
23527 report_resolving("match to %a in hash for file %a and path %a, continue scanning",expression,f,d)
23528 end
23529 else
23530 if trace_details then
23531 report_resolving("match to %a in hash for file %a and path %a, quit scanning",expression,f,d)
23532 end
23533 break
23534 end
23535 elseif trace_details then
23536 report_resolving("no match to %a in hash for file %a and path %a",expression,f,d)
23537 end
23538 end
23539 end
23540 if done then
23541 method="database"
23542 else
23543 method="filesystem"
23544 local scheme=entry.scheme
23545 if not scheme or scheme=="file" then
23546 local pname=entry.schemename
23547 if not find(pname,"*",1,true) then
23548 if can_be_dir(pname) then
23549 if not done and not entry.prescanned then
23550 if trace_details then
23551 report_resolving("quick root scan for %a",pname)
23552 end
23553 for k=1,#wantedfiles do
23554 local w=wantedfiles[k]
23555 local fname=check_subpath(filejoin(pname,w))
23556 if fname then
23557 result[#result+1]=fname
23558 done=true
23559 if not allresults then
23560 break
23561 end
23562 end
23563 end
23564 if not done and entry.recursive then
23565 if trace_details then
23566 report_resolving("scanning filesystem for %a",pname)
23567 end
23568 local files=resolvers.simplescanfiles(pname,false,true)
23569 for k=1,#wantedfiles do
23570 local w=wantedfiles[k]
23571 local subpath=files[w]
23572 if not subpath or subpath=="" then
23573 elseif type(subpath)=="string" then
23574 local fname=check_subpath(filejoin(pname,subpath,w))
23575 if fname then
23576 result[#result+1]=fname
23577 done=true
23578 if not allresults then
23579 break
23580 end
23581 end
23582 else
23583 for i=1,#subpath do
23584 local sp=subpath[i]
23585 if sp=="" then
23586 else
23587 local fname=check_subpath(filejoin(pname,sp,w))
23588 if fname then
23589 result[#result+1]=fname
23590 done=true
23591 if not allresults then
23592 break
23593 end
23594 end
23595 end
23596 end
23597 if done and not allresults then
23598 break
23599 end
23600 end
23601 end
23602 end
23603 end
23604 end
23605 else
23606 end
23607 else
23608 for k=1,#wantedfiles do
23609 local pname=entry.barename
23610 local fname=methodhandler('finders',pname.."/"..wantedfiles[k])
23611 if fname then
23612 result[#result+1]=fname
23613 done=true
23614 if not allresults then
23615 break
23616 end
23617 end
23618 end
23619 end
23620 end
23621 if done and not allresults then
23622 break
23623 end
23624 end
23625 if #result>0 then
23626 return method,result
23627 end
23628 end
23629end
23630local function find_onpath(filename,filetype,wantedfiles,allresults)
23631 if trace_details then
23632 report_resolving("checking filename %a, filetype %a, wanted files %a",filename,filetype,concat(wantedfiles," | "))
23633 end
23634 local result={}
23635 for k=1,#wantedfiles do
23636 local fname=wantedfiles[k]
23637 if fname and isreadable(fname) then
23638 filename=fname
23639 result[#result+1]=filejoin('.',fname)
23640 if not allresults then
23641 break
23642 end
23643 end
23644 end
23645 if #result>0 then
23646 return "onpath",result
23647 end
23648end
23649local function find_otherwise(filename,filetype,wantedfiles,allresults)
23650 local filelist=collect_files(wantedfiles)
23651 local fl=filelist and filelist[1]
23652 if fl then
23653 return "otherwise",{ resolveprefix(fl[3]) }
23654 end
23655end
23656collect_instance_files=function(filename,askedformat,allresults)
23657 if not filename or filename=="" then
23658 return {}
23659 end
23660 askedformat=askedformat or ""
23661 filename=collapsepath(filename,".")
23662 filename=gsub(filename,"^%./",getcurrentdir().."/")
23663 if allresults then
23664 local filetype,wantedfiles=find_analyze(filename,askedformat)
23665 local results={
23666 { find_direct (filename,true) },
23667 { find_wildcard (filename,true) },
23668 { find_qualified(filename,true,askedformat) },
23669 { find_intree (filename,filetype,wantedfiles,true) },
23670 { find_onpath (filename,filetype,wantedfiles,true) },
23671 { find_otherwise(filename,filetype,wantedfiles,true) },
23672 }
23673 local result={}
23674 local status={}
23675 local done={}
23676 for k=1,#results do
23677 local r=results[k]
23678 local method,list=r[1],r[2]
23679 if method and list then
23680 for i=1,#list do
23681 local c=collapsepath(list[i])
23682 if not done[c] then
23683 result[#result+1]=c
23684 done[c]=true
23685 end
23686 status[#status+1]=formatters["%-10s: %s"](method,c)
23687 end
23688 end
23689 end
23690 if trace_details then
23691 report_resolving("lookup status: %s",table.serialize(status,filename))
23692 end
23693 return result,status
23694 else
23695 local method,result,stamp,filetype,wantedfiles
23696 if instance.remember then
23697 if askedformat=="" then
23698 stamp=formatters["%s::%s"](suffixonly(filename),filename)
23699 else
23700 stamp=formatters["%s::%s"](askedformat,filename)
23701 end
23702 result=stamp and instance.found[stamp]
23703 if result then
23704 if trace_locating then
23705 report_resolving("remembered file %a",filename)
23706 end
23707 return result
23708 end
23709 end
23710 method,result=find_direct(filename)
23711 if not result then
23712 method,result=find_wildcard(filename)
23713 if not result then
23714 method,result=find_qualified(filename,false,askedformat)
23715 if not result then
23716 filetype,wantedfiles=find_analyze(filename,askedformat)
23717 method,result=find_intree(filename,filetype,wantedfiles)
23718 if not result then
23719 method,result=find_onpath(filename,filetype,wantedfiles)
23720 if resolve_otherwise and not result then
23721 method,result=find_otherwise(filename,filetype,wantedfiles)
23722 end
23723 end
23724 end
23725 end
23726 end
23727 if result and #result>0 then
23728 local foundname=collapsepath(result[1])
23729 resolvers.registerintrees(filename,askedformat,filetype,method,foundname)
23730 result={ foundname }
23731 else
23732 result={}
23733 end
23734 if stamp then
23735 if trace_locating then
23736 report_resolving("remembering file %a using hash %a",filename,stamp)
23737 end
23738 instance.found[stamp]=result
23739 end
23740 return result
23741 end
23742end
23743local function findfiles(filename,filetype,allresults)
23744 if not filename or filename=="" then
23745 return {}
23746 end
23747 if allresults==nil then
23748 allresults=true
23749 end
23750 local result,status=collect_instance_files(filename,filetype or "",allresults)
23751 if not result or #result==0 then
23752 local lowered=lower(filename)
23753 if filename~=lowered then
23754 result,status=collect_instance_files(lowered,filetype or "",allresults)
23755 end
23756 end
23757 return result or {},status
23758end
23759local function findfile(filename,filetype)
23760 if not filename or filename=="" then
23761 return ""
23762 else
23763 return findfiles(filename,filetype,false)[1] or ""
23764 end
23765end
23766resolvers.findfiles=findfiles
23767resolvers.findfile=findfile
23768resolvers.find_file=findfile
23769resolvers.find_files=findfiles
23770function resolvers.findpath(filename,filetype)
23771 return filedirname(findfiles(filename,filetype,false)[1] or "")
23772end
23773local function findgivenfiles(filename,allresults)
23774 local hashes=instance.hashes
23775 local files=instance.files
23776 local base=filebasename(filename)
23777 local result={}
23778 local function okay(hash,path,name)
23779 local found=methodhandler('concatinators',hash.type,hash.name,path,name)
23780 if found and found~="" then
23781 result[#result+1]=resolveprefix(found)
23782 return not allresults
23783 end
23784 end
23785 for k=1,#hashes do
23786 local hash=hashes[k]
23787 local content=files[hash.name]
23788 if content then
23789 local path,name=lookup(content,base)
23790 if not path then
23791 elseif type(path)=="string" then
23792 if okay(hash,path,name) then
23793 return result
23794 end
23795 else
23796 for i=1,#path do
23797 if okay(hash,path[i],name) then
23798 return result
23799 end
23800 end
23801 end
23802 end
23803 end
23804 return result
23805end
23806function resolvers.findgivenfiles(filename)
23807 return findgivenfiles(filename,true)
23808end
23809function resolvers.findgivenfile(filename)
23810 return findgivenfiles(filename,false)[1] or ""
23811end
23812local makewildcard=Cs(
23813 (P("^")^0*P("/")*P(-1)+P(-1))/".*"+(P("^")^0*P("/")/"")^0*(P("*")/".*"+P("-")/"%%-"+P(".")/"%%."+P("?")/"."+P("\\")/"/"+P(1))^0
23814)
23815function resolvers.wildcardpattern(pattern)
23816 return lpegmatch(makewildcard,pattern) or pattern
23817end
23818local function findwildcardfiles(filename,allresults,result)
23819 local files=instance.files
23820 local hashes=instance.hashes
23821 local result=result or {}
23822 local base=filebasename(filename)
23823 local dirn=filedirname(filename)
23824 local path=lower(lpegmatch(makewildcard,dirn) or dirn)
23825 local name=lower(lpegmatch(makewildcard,base) or base)
23826 if find(name,"*",1,true) then
23827 local function okay(found,path,base,hashname,hashtype)
23828 if find(found,path) then
23829 local full=methodhandler('concatinators',hashtype,hashname,found,base)
23830 if full and full~="" then
23831 result[#result+1]=resolveprefix(full)
23832 return not allresults
23833 end
23834 end
23835 end
23836 for k=1,#hashes do
23837 local hash=hashes[k]
23838 local hashname=hash.name
23839 local hashtype=hash.type
23840 if hashname and hashtype then
23841 for found,base in filtered(files[hashname],name) do
23842 if type(found)=='string' then
23843 if okay(found,path,base,hashname,hashtype) then
23844 break
23845 end
23846 else
23847 for i=1,#found do
23848 if okay(found[i],path,base,hashname,hashtype) then
23849 break
23850 end
23851 end
23852 end
23853 end
23854 end
23855 end
23856 else
23857 local function okayokay(found,path,base,hashname,hashtype)
23858 if find(found,path) then
23859 local full=methodhandler('concatinators',hashtype,hashname,found,base)
23860 if full and full~="" then
23861 result[#result+1]=resolveprefix(full)
23862 return not allresults
23863 end
23864 end
23865 end
23866 for k=1,#hashes do
23867 local hash=hashes[k]
23868 local hashname=hash.name
23869 local hashtype=hash.type
23870 if hashname and hashtype then
23871 local found,base=lookup(content,base)
23872 if not found then
23873 elseif type(found)=='string' then
23874 if okay(found,path,base,hashname,hashtype) then
23875 break
23876 end
23877 else
23878 for i=1,#found do
23879 if okay(found[i],path,base,hashname,hashtype) then
23880 break
23881 end
23882 end
23883 end
23884 end
23885 end
23886 end
23887 return result
23888end
23889function resolvers.findwildcardfiles(filename,result)
23890 return findwildcardfiles(filename,true,result)
23891end
23892function resolvers.findwildcardfile(filename)
23893 return findwildcardfiles(filename,false)[1] or ""
23894end
23895do
23896 local starttiming=statistics.starttiming
23897 local stoptiming=statistics.stoptiming
23898 local elapsedtime=statistics.elapsedtime
23899 function resolvers.starttiming()
23900 starttiming(instance)
23901 end
23902 function resolvers.stoptiming()
23903 stoptiming(instance)
23904 end
23905 function resolvers.loadtime()
23906 return elapsedtime(instance)
23907 end
23908end
23909function resolvers.automount()
23910end
23911function resolvers.load(option)
23912 resolvers.starttiming()
23913 identify_configuration_files()
23914 load_configuration_files()
23915 if option~="nofiles" then
23916 load_databases()
23917 resolvers.automount()
23918 end
23919 resolvers.stoptiming()
23920 local files=instance.files
23921 return files and next(files) and true
23922end
23923local function report(str)
23924 if trace_locating then
23925 report_resolving(str)
23926 else
23927 print(str)
23928 end
23929end
23930function resolvers.dowithfilesandreport(command,files,...)
23931 if files and #files>0 then
23932 if trace_locating then
23933 report('')
23934 end
23935 if type(files)=="string" then
23936 files={ files }
23937 end
23938 for f=1,#files do
23939 local file=files[f]
23940 local result=command(file,...)
23941 if type(result)=='string' then
23942 report(result)
23943 else
23944 for i=1,#result do
23945 report(result[i])
23946 end
23947 end
23948 end
23949 end
23950end
23951function resolvers.showpath(str)
23952 return joinpath(expandedpathlist(resolvers.formatofvariable(str)))
23953end
23954function resolvers.registerfile(files,name,path)
23955 if files[name] then
23956 if type(files[name])=='string' then
23957 files[name]={ files[name],path }
23958 else
23959 files[name]=path
23960 end
23961 else
23962 files[name]=path
23963 end
23964end
23965function resolvers.dowithpath(name,func)
23966 local pathlist=expandedpathlist(name)
23967 for i=1,#pathlist do
23968 func("^"..cleanpath(pathlist[i]))
23969 end
23970end
23971function resolvers.dowithvariable(name,func)
23972 func(expandedvariable(name))
23973end
23974function resolvers.locateformat(name)
23975 local engine=environment.ownmain or "luatex"
23976 local barename=removesuffix(file.basename(name))
23977 local fullname=addsuffix(barename,"fmt")
23978 local fmtname=caches.getfirstreadablefile(fullname,"formats",engine) or ""
23979 if fmtname=="" then
23980 fmtname=findfile(fullname)
23981 fmtname=cleanpath(fmtname)
23982 end
23983 if fmtname~="" then
23984 local barename=removesuffix(fmtname)
23985 local luaname=addsuffix(barename,luasuffixes.lua)
23986 local lucname=addsuffix(barename,luasuffixes.luc)
23987 local luiname=addsuffix(barename,luasuffixes.lui)
23988 if isfile(luiname) then
23989 return fmtname,luiname
23990 elseif isfile(lucname) then
23991 return fmtname,lucname
23992 elseif isfile(luaname) then
23993 return fmtname,luaname
23994 end
23995 end
23996 return nil,nil
23997end
23998function resolvers.booleanvariable(str,default)
23999 local b=expansion(str)
24000 if b=="" then
24001 return default
24002 else
24003 b=toboolean(b)
24004 return (b==nil and default) or b
24005 end
24006end
24007function resolvers.dowithfilesintree(pattern,handle,before,after)
24008 local hashes=instance.hashes
24009 local files=instance.files
24010 for i=1,#hashes do
24011 local hash=hashes[i]
24012 local blobtype=hash.type
24013 local blobpath=hash.name
24014 if blobtype and blobpath then
24015 local total=0
24016 local checked=0
24017 local done=0
24018 if before then
24019 before(blobtype,blobpath,pattern)
24020 end
24021 for path,name in filtered(files[blobpath],pattern) do
24022 if type(path)=="string" then
24023 checked=checked+1
24024 if handle(blobtype,blobpath,path,name) then
24025 done=done+1
24026 end
24027 else
24028 checked=checked+#path
24029 for i=1,#path do
24030 if handle(blobtype,blobpath,path[i],name) then
24031 done=done+1
24032 end
24033 end
24034 end
24035 end
24036 if after then
24037 after(blobtype,blobpath,pattern,checked,done)
24038 end
24039 end
24040 end
24041end
24042function resolvers.knownvariables(pattern)
24043 if instance then
24044 local environment=instance.environment
24045 local variables=instance.variables
24046 local expansions=instance.expansions
24047 local order=instance.order
24048 local pattern=upper(pattern or "")
24049 local result={}
24050 for i=1,#order do
24051 for key in next,order[i] do
24052 if result[key]==nil and key~="" and (pattern=="" or find(upper(key),pattern)) then
24053 result[key]={
24054 environment=rawget(environment,key),
24055 variable=key,
24056 expansion=expansions[key],
24057 resolved=resolveprefix(expansions[key]),
24058 }
24059 end
24060 end
24061 end
24062 return result
24063 else
24064 return {}
24065 end
24066end
24067
24068
24069end
24070
24071do
24072
24073package.loaded["data-pre"] = package.loaded["data-pre"] or true
24074
24075
24076
24077if not modules then modules={} end modules ['data-pre']={
24078 version=1.001,
24079 comment="companion to luat-lib.mkiv",
24080 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
24081 copyright="PRAGMA ADE / ConTeXt Development Team",
24082 license="see context related readme files"
24083}
24084local ipairs=ipairs
24085local insert,remove=table.insert,table.remove
24086local resolvers=resolvers
24087local prefixes=resolvers.prefixes
24088local cleanpath=resolvers.cleanpath
24089local findgivenfile=resolvers.findgivenfile
24090local expansion=resolvers.expansion
24091local getenv=resolvers.getenv
24092local basename=file.basename
24093local dirname=file.dirname
24094local joinpath=file.join
24095local isfile=lfs.isfile
24096local isdir=lfs.isdir
24097prefixes.environment=function(str)
24098 return cleanpath(expansion(str))
24099end
24100local function relative(str,n)
24101 if not isfile(str) then
24102 local pstr="./"..str
24103 if isfile(pstr) then
24104 str=pstr
24105 else
24106 local p="../"
24107 for i=1,n or 2 do
24108 local pstr=p..str
24109 if isfile(pstr) then
24110 str=pstr
24111 break
24112 else
24113 p=p.."../"
24114 end
24115 end
24116 end
24117 end
24118 return cleanpath(str)
24119end
24120local function locate(str)
24121 local fullname=findgivenfile(str) or ""
24122 return cleanpath(fullname~="" and fullname or str)
24123end
24124prefixes.relative=relative
24125prefixes.locate=locate
24126prefixes.auto=function(str)
24127 local fullname=relative(str)
24128 if not isfile(fullname) then
24129 fullname=locate(str)
24130 end
24131 return fullname
24132end
24133prefixes.filename=function(str)
24134 local fullname=findgivenfile(str) or ""
24135 return cleanpath(basename((fullname~="" and fullname) or str))
24136end
24137prefixes.pathname=function(str)
24138 local fullname=findgivenfile(str) or ""
24139 return cleanpath(dirname((fullname~="" and fullname) or str))
24140end
24141prefixes.selfautoloc=function(str)
24142 local pth=getenv('SELFAUTOLOC')
24143 return cleanpath(str and joinpath(pth,str) or pth)
24144end
24145prefixes.selfautoparent=function(str)
24146 local pth=getenv('SELFAUTOPARENT')
24147 return cleanpath(str and joinpath(pth,str) or pth)
24148end
24149prefixes.selfautodir=function(str)
24150 local pth=getenv('SELFAUTODIR')
24151 return cleanpath(str and joinpath(pth,str) or pth)
24152end
24153prefixes.home=function(str)
24154 local pth=getenv('HOME')
24155 return cleanpath(str and joinpath(pth,str) or pth)
24156end
24157do
24158 local tmppth
24159 prefixes.temp=function(str)
24160 if not tmppth then
24161 for _,s in ipairs { "TMP","TEMP","TMPDIR","TEMPDIR" } do
24162 tmppth=getenv(s)
24163 if tmppth~="" and isdir(tmppth) then
24164 break
24165 end
24166 end
24167 if not tmppth or tmppth=="" then
24168 tmppth="."
24169 end
24170 end
24171 return cleanpath(str and joinpath(tmppth,str) or tmppth)
24172 end
24173 prefixes.texruns=function(str)
24174 local pth=getenv('TEXRUNS')
24175 if pth=="" then
24176 pth=tmppth
24177 end
24178 return cleanpath(str and joinpath(pth,str) or pth)
24179 end
24180end
24181prefixes.env=prefixes.environment
24182prefixes.rel=prefixes.relative
24183prefixes.loc=prefixes.locate
24184prefixes.kpse=prefixes.locate
24185prefixes.full=prefixes.locate
24186prefixes.file=prefixes.filename
24187prefixes.path=prefixes.pathname
24188local inputstack={}
24189local stackpath=resolvers.stackpath
24190local function toppath()
24191 if not inputstack then
24192 return "."
24193 end
24194 local pathname=dirname(inputstack[#inputstack] or "")
24195 if pathname=="" then
24196 return "."
24197 else
24198 return pathname
24199 end
24200end
24201local function jobpath()
24202 local path=stackpath()
24203 if not path or path=="" then
24204 return "."
24205 else
24206 return path
24207 end
24208end
24209local function pushinputname(name)
24210 insert(inputstack,name)
24211end
24212local function popinputname(name)
24213 return remove(inputstack)
24214end
24215resolvers.toppath=toppath
24216resolvers.jobpath=jobpath
24217resolvers.pushinputname=pushinputname
24218resolvers.popinputname=popinputname
24219prefixes.toppath=function(str) return cleanpath(joinpath(toppath(),str)) end
24220prefixes.jobpath=function(str) return cleanpath(joinpath(jobpath(),str)) end
24221resolvers.setdynamic("toppath")
24222resolvers.setdynamic("jobpath")
24223
24224
24225end
24226
24227do
24228
24229package.loaded["data-inp"] = package.loaded["data-inp"] or true
24230
24231
24232
24233if not modules then modules={} end modules ['data-inp']={
24234 version=1.001,
24235 comment="companion to luat-lib.mkiv",
24236 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
24237 copyright="PRAGMA ADE / ConTeXt Development Team",
24238 license="see context related readme files"
24239}
24240local allocate=utilities.storage.allocate
24241local resolvers=resolvers
24242local methodhandler=resolvers.methodhandler
24243local registermethod=resolvers.registermethod
24244local finders=allocate { helpers={},notfound=function() end }
24245local openers=allocate { helpers={},notfound=function() end }
24246local loaders=allocate { helpers={},notfound=function() return false,nil,0 end }
24247local tracers=allocate { helpers={},notfound=function() end }
24248registermethod("finders",finders,"uri")
24249registermethod("openers",openers,"uri")
24250registermethod("loaders",loaders,"uri")
24251registermethod("tracers",tracers,"uri")
24252resolvers.finders=finders
24253resolvers.openers=openers
24254resolvers.loaders=loaders
24255resolvers.tracers=tracers
24256
24257
24258end
24259
24260do
24261
24262package.loaded["data-out"] = package.loaded["data-out"] or true
24263
24264
24265
24266if not modules then modules={} end modules ['data-out']={
24267 version=1.001,
24268 comment="companion to luat-lib.mkiv",
24269 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
24270 copyright="PRAGMA ADE / ConTeXt Development Team",
24271 license="see context related readme files"
24272}
24273local allocate=utilities.storage.allocate
24274local resolvers=resolvers
24275local registermethod=resolvers.registermethod
24276local savers=allocate { helpers={} }
24277resolvers.savers=savers
24278local cleaners=allocate { helpers={} }
24279resolvers.cleaners=cleaners
24280registermethod("savers",savers,"uri")
24281registermethod("cleaners",cleaners,"uri")
24282
24283
24284end
24285
24286do
24287
24288package.loaded["data-fil"] = package.loaded["data-fil"] or true
24289
24290
24291
24292if not modules then modules={} end modules ['data-fil']={
24293 version=1.001,
24294 comment="companion to luat-lib.mkiv",
24295 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
24296 copyright="PRAGMA ADE / ConTeXt Development Team",
24297 license="see context related readme files"
24298}
24299local ioopen=io.open
24300local isdir=lfs.isdir
24301local trace_locating=false trackers.register("resolvers.locating",function(v) trace_locating=v end)
24302local report_files=logs.reporter("resolvers","files")
24303local resolvers=resolvers
24304local resolveprefix=resolvers.resolve
24305local findfile=resolvers.findfile
24306local scanfiles=resolvers.scanfiles
24307local registerfilehash=resolvers.registerfilehash
24308local appendhash=resolvers.appendhash
24309local loadcachecontent=caches.loadcontent
24310function resolvers.locators.file(specification)
24311 local filename=specification.filename
24312 local realname=resolveprefix(filename)
24313 if realname and realname~='' and isdir(realname) then
24314 if trace_locating then
24315 report_files("file locator %a found as %a",filename,realname)
24316 end
24317 appendhash('file',filename,true)
24318 elseif trace_locating then
24319 report_files("file locator %a not found",filename)
24320 end
24321end
24322function resolvers.hashers.file(specification)
24323 local pathname=specification.filename
24324 local content=loadcachecontent(pathname,'files')
24325 registerfilehash(pathname,content,content==nil)
24326end
24327function resolvers.generators.file(specification)
24328 local pathname=specification.filename
24329 local content=scanfiles(pathname,false,true)
24330 registerfilehash(pathname,content,true)
24331end
24332resolvers.concatinators.file=file.join
24333local finders=resolvers.finders
24334local notfound=finders.notfound
24335function finders.file(specification,filetype)
24336 local filename=specification.filename
24337 local foundname=findfile(filename,filetype)
24338 if foundname and foundname~="" then
24339 if trace_locating then
24340 report_files("file finder: %a found",filename)
24341 end
24342 return foundname
24343 else
24344 if trace_locating then
24345 report_files("file finder: %a not found",filename)
24346 end
24347 return notfound()
24348 end
24349end
24350local openers=resolvers.openers
24351local notfound=openers.notfound
24352local overloaded=false
24353local function textopener(tag,filename,f)
24354 return {
24355 reader=function() return f:read () end,
24356 close=function() return f:close() end,
24357 }
24358end
24359function openers.helpers.textopener(...)
24360 return textopener(...)
24361end
24362function openers.helpers.settextopener(opener)
24363 if overloaded then
24364 report_files("file opener: %s overloaded","already")
24365 else
24366 if trace_locating then
24367 report_files("file opener: %s overloaded","once")
24368 end
24369 overloaded=true
24370 textopener=opener
24371 end
24372end
24373function openers.file(specification,filetype)
24374 local filename=specification.filename
24375 if filename and filename~="" then
24376 local f=ioopen(filename,"r")
24377 if f then
24378 if trace_locating then
24379 report_files("file opener: %a opened",filename)
24380 end
24381 return textopener("file",filename,f)
24382 end
24383 end
24384 if trace_locating then
24385 report_files("file opener: %a not found",filename)
24386 end
24387 return notfound()
24388end
24389local loaders=resolvers.loaders
24390local notfound=loaders.notfound
24391function loaders.file(specification,filetype)
24392 local filename=specification.filename
24393 if filename and filename~="" then
24394 local f=ioopen(filename,"rb")
24395 if f then
24396 if trace_locating then
24397 report_files("file loader: %a loaded",filename)
24398 end
24399 local s=f:read("*a")
24400 f:close()
24401 if s then
24402 return true,s,#s
24403 end
24404 end
24405 end
24406 if trace_locating then
24407 report_files("file loader: %a not found",filename)
24408 end
24409 return notfound()
24410end
24411
24412
24413end
24414
24415do
24416
24417package.loaded["data-con"] = package.loaded["data-con"] or true
24418
24419
24420
24421if not modules then modules={} end modules ['data-con']={
24422 version=1.100,
24423 comment="companion to luat-lib.mkiv",
24424 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
24425 copyright="PRAGMA ADE / ConTeXt Development Team",
24426 license="see context related readme files"
24427}
24428local setmetatable=setmetatable
24429local format,lower,gsub=string.format,string.lower,string.gsub
24430local trace_cache=false trackers.register("resolvers.cache",function(v) trace_cache=v end)
24431local trace_containers=false trackers.register("resolvers.containers",function(v) trace_containers=v end)
24432local trace_storage=false trackers.register("resolvers.storage",function(v) trace_storage=v end)
24433containers=containers or {}
24434local containers=containers
24435containers.usecache=true
24436local getwritablepath=caches.getwritablepath
24437local getreadablepaths=caches.getreadablepaths
24438local cacheiswritable=caches.is_writable
24439local loaddatafromcache=caches.loaddata
24440local savedataincache=caches.savedata
24441local report_containers=logs.reporter("resolvers","containers")
24442local allocated={}
24443local mt={
24444 __index=function(t,k)
24445 if k=="writable" then
24446 local writable=getwritablepath(t.category,t.subcategory) or { "." }
24447 t.writable=writable
24448 return writable
24449 elseif k=="readables" then
24450 local readables=getreadablepaths(t.category,t.subcategory) or { "." }
24451 t.readables=readables
24452 return readables
24453 end
24454 end,
24455 __storage__=true
24456}
24457function containers.define(category,subcategory,version,enabled,reload)
24458 if category and subcategory then
24459 local c=allocated[category]
24460 if not c then
24461 c={}
24462 allocated[category]=c
24463 end
24464 local s=c[subcategory]
24465 if not s then
24466 s={
24467 category=category,
24468 subcategory=subcategory,
24469 storage={},
24470 enabled=enabled,
24471 reload=reload,
24472 version=version or math.pi,
24473 trace=false,
24474 }
24475 setmetatable(s,mt)
24476 c[subcategory]=s
24477 end
24478 return s
24479 end
24480end
24481function containers.is_usable(container,name)
24482 return container.enabled and caches and cacheiswritable(container.writable,name)
24483end
24484function containers.is_valid(container,name)
24485 if name and name~="" then
24486 local storage=container.storage[name]
24487 return storage and storage.cache_version==container.version
24488 else
24489 return false
24490 end
24491end
24492function containers.read(container,name)
24493 local storage=container.storage
24494 local reload=container.reload
24495 local stored=not reload and storage[name]
24496 if not stored and container.enabled and caches and containers.usecache then
24497 stored=loaddatafromcache(container.readables,name,container.writable)
24498 if stored and stored.cache_version==container.version then
24499 if trace_cache or trace_containers then
24500 report_containers("action %a, category %a, name %a","load",container.subcategory,name)
24501 end
24502 else
24503 stored=nil
24504 end
24505 storage[name]=stored
24506 elseif stored then
24507 if trace_cache or trace_containers then
24508 report_containers("action %a, category %a, name %a","reuse",container.subcategory,name)
24509 end
24510 end
24511 return stored
24512end
24513function containers.write(container,name,data,fast)
24514 if data then
24515 data.cache_version=container.version
24516 if container.enabled and caches then
24517 local unique=data.unique
24518 local shared=data.shared
24519 data.unique=nil
24520 data.shared=nil
24521 savedataincache(container.writable,name,data,fast)
24522 if trace_cache or trace_containers then
24523 report_containers("action %a, category %a, name %a","save",container.subcategory,name)
24524 end
24525 data.unique=unique
24526 data.shared=shared
24527 end
24528 if trace_cache or trace_containers then
24529 report_containers("action %a, category %a, name %a","store",container.subcategory,name)
24530 end
24531 container.storage[name]=data
24532 end
24533 return data
24534end
24535function containers.content(container,name)
24536 return container.storage[name]
24537end
24538function containers.cleanname(name)
24539 return (gsub(lower(name),"[^%w\128-\255]+","-"))
24540end
24541
24542
24543end
24544
24545do
24546
24547package.loaded["data-use"] = package.loaded["data-use"] or true
24548
24549
24550
24551if not modules then modules={} end modules ['data-use']={
24552 version=1.001,
24553 comment="companion to luat-lib.mkiv",
24554 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
24555 copyright="PRAGMA ADE / ConTeXt Development Team",
24556 license="see context related readme files"
24557}
24558local format=string.format
24559local trace_locating=false trackers.register("resolvers.locating",function(v) trace_locating=v end)
24560local report_mounts=logs.reporter("resolvers","mounts")
24561local resolvers=resolvers
24562local findfile=resolvers.findfile
24563statistics.register("used config file",function() return caches.configfiles() end)
24564statistics.register("used cache path",function() return caches.usedpaths() end)
24565function statistics.savefmtstatus(texname,formatbanner,sourcefile,banner)
24566 local enginebanner=status.banner
24567 if formatbanner and enginebanner and sourcefile then
24568 local luvname=file.replacesuffix(texname,"luv")
24569 local luvdata={
24570 enginebanner=enginebanner,
24571 formatbanner=formatbanner,
24572 sourcehash=md5.hex(io.loaddata(findfile(sourcefile)) or "unknown"),
24573 sourcefile=sourcefile,
24574 luaversion=LUAVERSION,
24575 formatid=LUATEXFORMATID,
24576 functionality=LUATEXFUNCTIONALITY,
24577 }
24578 io.savedata(luvname,table.serialize(luvdata,true))
24579 lua.registerinitexfinalizer(function()
24580 if jit then
24581 logs.report("format banner","%s lua: %s jit",banner,LUAVERSION)
24582 else
24583 logs.report("format banner","%s lua: %s",banner,LUAVERSION)
24584 end
24585 logs.newline()
24586 end,"show banner")
24587 end
24588end
24589function statistics.checkfmtstatus(texname)
24590 local enginebanner=status.banner
24591 if enginebanner and texname then
24592 local luvname=file.replacesuffix(texname,"luv")
24593 if lfs.isfile(luvname) then
24594 local luv=dofile(luvname)
24595 if luv and luv.sourcefile then
24596 local sourcehash=md5.hex(io.loaddata(findfile(luv.sourcefile)) or "unknown")
24597 local luvbanner=luv.enginebanner or "?"
24598 if luvbanner~=enginebanner then
24599 return format("engine mismatch (luv: %s <> bin: %s)",luvbanner,enginebanner)
24600 end
24601 local luvhash=luv.sourcehash or "?"
24602 if luvhash~=sourcehash then
24603 return format("source mismatch (luv: %s <> bin: %s)",luvhash,sourcehash)
24604 end
24605 local luvluaversion=luv.luaversion or 0
24606 local engluaversion=LUAVERSION or 0
24607 if luvluaversion~=engluaversion then
24608 return format("lua mismatch (luv: %s <> bin: %s)",luvluaversion,engluaversion)
24609 end
24610 local luvfunctionality=luv.functionality or 0
24611 local engfunctionality=status.development_id or 0
24612 if luvfunctionality~=engfunctionality then
24613 return format("functionality mismatch (luv: %s <> bin: %s)",luvfunctionality,engfunctionality)
24614 end
24615 local luvformatid=luv.formatid or 0
24616 local engformatid=status.format_id or 0
24617 if luvformatid~=engformatid then
24618 return format("formatid mismatch (luv: %s <> bin: %s)",luvformatid,engformatid)
24619 end
24620 else
24621 return "invalid status file"
24622 end
24623 else
24624 return "missing status file"
24625 end
24626 end
24627 return true
24628end
24629
24630
24631end
24632
24633do
24634
24635package.loaded["data-zip"] = package.loaded["data-zip"] or true
24636
24637
24638
24639if not modules then modules={} end modules ['data-zip']={
24640 version=1.001,
24641 comment="companion to luat-lib.mkiv",
24642 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
24643 copyright="PRAGMA ADE / ConTeXt Development Team",
24644 license="see context related readme files"
24645}
24646local format,find,match=string.format,string.find,string.match
24647local trace_locating=false trackers.register("resolvers.locating",function(v) trace_locating=v end)
24648local report_zip=logs.reporter("resolvers","zip")
24649local resolvers=resolvers
24650local findfile=resolvers.findfile
24651local registerfile=resolvers.registerfile
24652local splitmethod=resolvers.splitmethod
24653local prependhash=resolvers.prependhash
24654local starttiming=resolvers.starttiming
24655local extendtexmf=resolvers.extendtexmfvariable
24656local stoptiming=resolvers.stoptiming
24657local urlquery=url.query
24658zip=zip or {}
24659local zip=zip
24660local archives=zip.archives or {}
24661zip.archives=archives
24662local registeredfiles=zip.registeredfiles or {}
24663zip.registeredfiles=registeredfiles
24664local zipfiles=utilities.zipfiles
24665local openzip,closezip,validfile,wholefile,filehandle,traversezip
24666if zipfiles then
24667 local ipairs=ipairs
24668 openzip=zipfiles.open
24669 closezip=zipfiles.close
24670 validfile=zipfiles.found
24671 wholefile=zipfiles.unzip
24672 local listzip=zipfiles.list
24673 traversezip=function(zfile)
24674 return ipairs(listzip(zfile))
24675 end
24676 local streams=utilities.streams
24677 local openstream=streams.open
24678 local readstring=streams.readstring
24679 local streamsize=streams.size
24680 local metatable={
24681 close=streams.close,
24682 read=function(stream,n)
24683 readstring(stream,n=="*a" and streamsize(stream) or n)
24684 end
24685 }
24686 filehandle=function(zfile,queryname)
24687 local data=wholefile(zfile,queryname)
24688 if data then
24689 local stream=openstream(data)
24690 if stream then
24691 return setmetatableindex(stream,metatable)
24692 end
24693 end
24694 end
24695else
24696 openzip=zip.open
24697 closezip=zip.close
24698 validfile=function(zfile,queryname)
24699 local dfile=zfile:open(queryname)
24700 if dfile then
24701 dfile:close()
24702 return true
24703 end
24704 return false
24705 end
24706 traversezip=function(zfile)
24707 return z:files()
24708 end
24709 wholefile=function(zfile,queryname)
24710 local dfile=zfile:open(queryname)
24711 if dfile then
24712 local s=dfile:read("*all")
24713 dfile:close()
24714 return s
24715 end
24716 end
24717 filehandle=function(zfile,queryname)
24718 local dfile=zfile:open(queryname)
24719 if dfile then
24720 return dfile
24721 end
24722 end
24723end
24724local function validzip(str)
24725 if not find(str,"^zip://") then
24726 return "zip:///"..str
24727 else
24728 return str
24729 end
24730end
24731local function openarchive(name)
24732 if not name or name=="" then
24733 return nil
24734 else
24735 local arch=archives[name]
24736 if not arch then
24737 local full=findfile(name) or ""
24738 arch=full~="" and openzip(full) or false
24739 archives[name]=arch
24740 end
24741 return arch
24742 end
24743end
24744local function closearchive(name)
24745 if not name or (name=="" and archives[name]) then
24746 closezip(archives[name])
24747 archives[name]=nil
24748 end
24749end
24750zip.openarchive=openarchive
24751zip.closearchive=closearchive
24752function resolvers.locators.zip(specification)
24753 local archive=specification.filename
24754 local zipfile=archive and archive~="" and openarchive(archive)
24755 if trace_locating then
24756 if zipfile then
24757 report_zip("locator: archive %a found",archive)
24758 else
24759 report_zip("locator: archive %a not found",archive)
24760 end
24761 end
24762end
24763function resolvers.concatinators.zip(zipfile,path,name)
24764 if not path or path=="" then
24765 return format('%s?name=%s',zipfile,name)
24766 else
24767 return format('%s?name=%s/%s',zipfile,path,name)
24768 end
24769end
24770local finders=resolvers.finders
24771local notfound=finders.notfound
24772function finders.zip(specification)
24773 local original=specification.original
24774 local archive=specification.filename
24775 if archive then
24776 local query=urlquery(specification.query)
24777 local queryname=query.name
24778 if queryname then
24779 local zfile=openarchive(archive)
24780 if zfile then
24781 if trace_locating then
24782 report_zip("finder: archive %a found",archive)
24783 end
24784 if validfile(zfile,queryname) then
24785 if trace_locating then
24786 report_zip("finder: file %a found",queryname)
24787 end
24788 return specification.original
24789 elseif trace_locating then
24790 report_zip("finder: file %a not found",queryname)
24791 end
24792 elseif trace_locating then
24793 report_zip("finder: unknown archive %a",archive)
24794 end
24795 end
24796 end
24797 if trace_locating then
24798 report_zip("finder: %a not found",original)
24799 end
24800 return notfound()
24801end
24802local openers=resolvers.openers
24803local notfound=openers.notfound
24804local textopener=openers.helpers.textopener
24805function openers.zip(specification)
24806 local original=specification.original
24807 local archive=specification.filename
24808 if archive then
24809 local query=urlquery(specification.query)
24810 local queryname=query.name
24811 if queryname then
24812 local zfile=openarchive(archive)
24813 if zfile then
24814 if trace_locating then
24815 report_zip("opener; archive %a opened",archive)
24816 end
24817 local handle=filehandle(zfile,queryname)
24818 if handle then
24819 if trace_locating then
24820 report_zip("opener: file %a found",queryname)
24821 end
24822 return textopener('zip',original,handle)
24823 elseif trace_locating then
24824 report_zip("opener: file %a not found",queryname)
24825 end
24826 elseif trace_locating then
24827 report_zip("opener: unknown archive %a",archive)
24828 end
24829 end
24830 end
24831 if trace_locating then
24832 report_zip("opener: %a not found",original)
24833 end
24834 return notfound()
24835end
24836local loaders=resolvers.loaders
24837local notfound=loaders.notfound
24838function loaders.zip(specification)
24839 local original=specification.original
24840 local archive=specification.filename
24841 if archive then
24842 local query=urlquery(specification.query)
24843 local queryname=query.name
24844 if queryname then
24845 local zfile=openarchive(archive)
24846 if zfile then
24847 if trace_locating then
24848 report_zip("loader: archive %a opened",archive)
24849 end
24850 local data=wholefile(zfile,queryname)
24851 if data then
24852 if trace_locating then
24853 report_zip("loader; file %a loaded",original)
24854 end
24855 return true,data,#data
24856 elseif trace_locating then
24857 report_zip("loader: file %a not found",queryname)
24858 end
24859 elseif trace_locating then
24860 report_zip("loader; unknown archive %a",archive)
24861 end
24862 end
24863 end
24864 if trace_locating then
24865 report_zip("loader: %a not found",original)
24866 end
24867 return notfound()
24868end
24869local function registerzipfile(z,tree)
24870 local names={}
24871 local files={}
24872 local remap={}
24873 local n=0
24874 local filter=tree=="" and "^(.+)/(.-)$" or format("^%s/(.+)/(.-)$",tree)
24875 if trace_locating then
24876 report_zip("registering: using filter %a",filter)
24877 end
24878 starttiming()
24879 for i in traversezip(z) do
24880 local filename=i.filename
24881 local path,name=match(filename,filter)
24882 if not path then
24883 n=n+1
24884 registerfile(names,filename,"")
24885 local usedname=lower(filename)
24886 files[usedname]=""
24887 if usedname~=filename then
24888 remap[usedname]=filename
24889 end
24890 elseif name and name~="" then
24891 n=n+1
24892 register(names,name,path)
24893 local usedname=lower(name)
24894 files[usedname]=path
24895 if usedname~=name then
24896 remap[usedname]=name
24897 end
24898 else
24899 end
24900 end
24901 stoptiming()
24902 report_zip("registering: %s files registered",n)
24903 return {
24904 files=files,
24905 remap=remap,
24906 }
24907end
24908local function usezipfile(archive)
24909 local specification=splitmethod(archive)
24910 local archive=specification.filename
24911 if archive and not registeredfiles[archive] then
24912 local z=openarchive(archive)
24913 if z then
24914 local tree=urlquery(specification.query).tree or ""
24915 if trace_locating then
24916 report_zip("registering: archive %a",archive)
24917 end
24918 prependhash('zip',archive)
24919 extendtexmf(archive)
24920 registeredfiles[archive]=z
24921 registerfilehash(archive,registerzipfile(z,tree))
24922 elseif trace_locating then
24923 report_zip("registering: unknown archive %a",archive)
24924 end
24925 elseif trace_locating then
24926 report_zip("registering: archive %a not found",archive)
24927 end
24928end
24929resolvers.usezipfile=usezipfile
24930resolvers.registerzipfile=registerzipfile
24931function resolvers.hashers.zip(specification)
24932 local archive=specification.filename
24933 if trace_locating then
24934 report_zip("loading file %a",archive)
24935 end
24936 usezipfile(specification.original)
24937end
24938
24939
24940end
24941
24942do
24943
24944package.loaded["data-tre"] = package.loaded["data-tre"] or true
24945
24946
24947
24948if not modules then modules={} end modules ['data-tre']={
24949 version=1.001,
24950 comment="companion to luat-lib.mkiv",
24951 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
24952 copyright="PRAGMA ADE / ConTeXt Development Team",
24953 license="see context related readme files"
24954}
24955local type=type
24956local find,gsub,lower=string.find,string.gsub,string.lower
24957local basename,dirname,joinname=file.basename,file.dirname,file.join
24958local globdir,isdir,isfile=dir.glob,lfs.isdir,lfs.isfile
24959local P,lpegmatch=lpeg.P,lpeg.match
24960local trace_locating=false trackers.register("resolvers.locating",function(v) trace_locating=v end)
24961local report_trees=logs.reporter("resolvers","trees")
24962local resolvers=resolvers
24963local finders=resolvers.finders
24964local openers=resolvers.openers
24965local loaders=resolvers.loaders
24966local locators=resolvers.locators
24967local hashers=resolvers.hashers
24968local generators=resolvers.generators
24969do
24970 local collectors={}
24971 local found={}
24972 local notfound=finders.notfound
24973 function finders.tree(specification)
24974 local spec=specification.filename
24975 local okay=found[spec]
24976 if okay==nil then
24977 if spec~="" then
24978 local path=dirname(spec)
24979 local name=basename(spec)
24980 if path=="" then
24981 path="."
24982 end
24983 local names=collectors[path]
24984 if not names then
24985 local pattern=find(path,"/%*+$") and path or (path.."/*")
24986 names=globdir(pattern)
24987 collectors[path]=names
24988 end
24989 local pattern="/"..gsub(name,"([%.%-%+])","%%%1").."$"
24990 for i=1,#names do
24991 local fullname=names[i]
24992 if find(fullname,pattern) then
24993 found[spec]=fullname
24994 return fullname
24995 end
24996 end
24997 local pattern=lower(pattern)
24998 for i=1,#names do
24999 local fullname=lower(names[i])
25000 if find(fullname,pattern) then
25001 if isfile(fullname) then
25002 found[spec]=fullname
25003 return fullname
25004 else
25005 break
25006 end
25007 end
25008 end
25009 end
25010 okay=notfound()
25011 found[spec]=okay
25012 end
25013 return okay
25014 end
25015end
25016do
25017 local resolveprefix=resolvers.resolve
25018 local appendhash=resolvers.appendhash
25019 local function dolocate(specification)
25020 local name=specification.filename
25021 local realname=resolveprefix(name)
25022 if realname and realname~='' and isdir(realname) then
25023 if trace_locating then
25024 report_trees("locator %a found",realname)
25025 end
25026 appendhash('tree',name,false)
25027 elseif trace_locating then
25028 report_trees("locator %a not found",name)
25029 end
25030 end
25031 locators.tree=dolocate
25032 locators.dirlist=dolocate
25033 locators.dirfile=dolocate
25034end
25035do
25036 local filegenerator=generators.file
25037 generators.dirlist=filegenerator
25038 generators.dirfile=filegenerator
25039end
25040do
25041 local filegenerator=generators.file
25042 local methodhandler=resolvers.methodhandler
25043 local function dohash(specification)
25044 local name=specification.filename
25045 if trace_locating then
25046 report_trees("analyzing %a",name)
25047 end
25048 methodhandler("hashers",name)
25049 filegenerator(specification)
25050 end
25051 hashers.tree=dohash
25052 hashers.dirlist=dohash
25053 hashers.dirfile=dohash
25054end
25055local resolve do
25056 local collectors={}
25057 local splitter=lpeg.splitat("/**/")
25058 local stripper=lpeg.replacer { [P("/")*P("*")^1*P(-1)]="" }
25059 local loadcontent=caches.loadcontent
25060 local savecontent=caches.savecontent
25061 local notfound=finders.notfound
25062 local scanfiles=resolvers.scanfiles
25063 local lookup=resolvers.get_from_content
25064 table.setmetatableindex(collectors,function(t,k)
25065 local rootname=lpegmatch(stripper,k)
25066 local dataname=joinname(rootname,"dirlist")
25067 local content=loadcontent(dataname,"files",dataname)
25068 if not content then
25069 content=scanfiles(rootname,nil,nil,false,true)
25070 savecontent(dataname,"files",content,dataname)
25071 end
25072 t[k]=content
25073 return content
25074 end)
25075 local function checked(root,p,n)
25076 if p then
25077 if type(p)=="table" then
25078 for i=1,#p do
25079 local fullname=joinname(root,p[i],n)
25080 if isfile(fullname) then
25081 return fullname
25082 end
25083 end
25084 else
25085 local fullname=joinname(root,p,n)
25086 if isfile(fullname) then
25087 return fullname
25088 end
25089 end
25090 end
25091 return notfound()
25092 end
25093 resolve=function(specification)
25094 local filename=specification.filename
25095 if filename~="" then
25096 local root,rest=lpegmatch(splitter,filename)
25097 if root and rest then
25098 local path,name=dirname(rest),basename(rest)
25099 if name~=rest then
25100 local content=collectors[root]
25101 local p,n=lookup(content,name)
25102 if not p then
25103 return notfound()
25104 end
25105 local pattern=".*/"..path.."$"
25106 local istable=type(p)=="table"
25107 if istable then
25108 for i=1,#p do
25109 local pi=p[i]
25110 if pi==path or find(pi,pattern) then
25111 local fullname=joinname(root,pi,n)
25112 if isfile(fullname) then
25113 return fullname
25114 end
25115 end
25116 end
25117 elseif p==path or find(p,pattern) then
25118 local fullname=joinname(root,p,n)
25119 if isfile(fullname) then
25120 return fullname
25121 end
25122 end
25123 local queries=specification.queries
25124 if queries and queries.option=="fileonly" then
25125 return checked(root,p,n)
25126 else
25127 return notfound()
25128 end
25129 end
25130 end
25131 local path=dirname(filename)
25132 local name=basename(filename)
25133 local root=lpegmatch(stripper,path)
25134 local content=collectors[path]
25135 local p,n=lookup(content,name)
25136 if p then
25137 return checked(root,p,n)
25138 end
25139 end
25140 return notfound()
25141 end
25142 finders.dirlist=resolve
25143 function finders.dirfile(specification)
25144 local queries=specification.queries
25145 if queries then
25146 queries.option="fileonly"
25147 else
25148 specification.queries={ option="fileonly" }
25149 end
25150 return resolve(specification)
25151 end
25152end
25153do
25154 local fileopener=openers.file
25155 local fileloader=loaders.file
25156 openers.dirlist=fileopener
25157 loaders.dirlist=fileloader
25158 openers.dirfile=fileopener
25159 loaders.dirfile=fileloader
25160end
25161do
25162 local hashfile="dirhash.lua"
25163 local kind="HASH256"
25164 local version=1.0
25165 local loadtable=table.load
25166 local savetable=table.save
25167 local loaddata=io.loaddata
25168 function resolvers.dirstatus(patterns)
25169 local t=type(patterns)
25170 if t=="string" then
25171 patterns={ patterns }
25172 elseif t~="table" then
25173 return false
25174 end
25175 local status=loadtable(hashfile)
25176 if not status or status.version~=version or status.kind~=kind then
25177 status={
25178 version=1.0,
25179 kind=kind,
25180 hashes={},
25181 }
25182 end
25183 local hashes=status.hashes
25184 local changed={}
25185 local action=sha2[kind]
25186 local update={}
25187 for i=1,#patterns do
25188 local pattern=patterns[i]
25189 local files=globdir(pattern)
25190 for i=1,#files do
25191 local name=files[i]
25192 local hash=action(loaddata(name))
25193 if hashes[name]~=hash then
25194 changed[#changed+1]=name
25195 end
25196 update[name]=hash
25197 end
25198 end
25199 status.hashes=update
25200 savetable(hashfile,status)
25201 return #changed>0 and changed or false
25202 end
25203end
25204
25205
25206end
25207
25208do
25209
25210package.loaded["data-sch"] = package.loaded["data-sch"] or true
25211
25212
25213
25214if not modules then modules={} end modules ['data-sch']={
25215 version=1.001,
25216 comment="companion to luat-lib.mkiv",
25217 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
25218 copyright="PRAGMA ADE / ConTeXt Development Team",
25219 license="see context related readme files"
25220}
25221local load,tonumber=load,tonumber
25222local gsub,format=string.gsub,string.format
25223local sortedhash,concat=table.sortedhash,table.concat
25224local finders,openers,loaders=resolvers.finders,resolvers.openers,resolvers.loaders
25225local addsuffix,suffix,splitbase=file.addsuffix,file.suffix,file.splitbase
25226local md5hex=md5.hex
25227local trace_schemes=false trackers.register("resolvers.schemes",function(v) trace_schemes=v end)
25228local report_schemes=logs.reporter("resolvers","schemes")
25229local http=require("socket.http")
25230local ltn12=require("ltn12")
25231if mbox then mbox=nil end
25232local resolvers=resolvers
25233local schemes=resolvers.schemes or {}
25234resolvers.schemes=schemes
25235local cleaners={}
25236schemes.cleaners=cleaners
25237local threshold=24*60*60
25238directives.register("schemes.threshold",function(v) threshold=tonumber(v) or threshold end)
25239function cleaners.none(specification)
25240 return specification.original
25241end
25242function cleaners.strip(specification)
25243 local path,name=splitbase(specification.original)
25244 if path=="" then
25245 return (gsub(name,"[^%a%d%.]+","-"))
25246 else
25247 return (gsub((gsub(path,"%.","-").."-"..name),"[^%a%d%.]+","-"))
25248 end
25249end
25250function cleaners.md5(specification)
25251 return addsuffix(md5hex(specification.original),suffix(specification.path))
25252end
25253local cleaner=cleaners.strip
25254directives.register("schemes.cleanmethod",function(v) cleaner=cleaners[v] or cleaners.strip end)
25255function resolvers.schemes.cleanname(specification)
25256 local hash=cleaner(specification)
25257 if trace_schemes then
25258 report_schemes("hashing %a to %a",specification.original,hash)
25259 end
25260 return hash
25261end
25262local cached={}
25263local loaded={}
25264local reused={}
25265local thresholds={}
25266local handlers={}
25267local runner=sandbox.registerrunner {
25268 name="curl resolver",
25269 method="execute",
25270 program="curl",
25271 template='--silent --insecure --create-dirs --output "%cachename%" "%original%"',
25272 checkers={
25273 cachename="cache",
25274 original="url",
25275 }
25276}
25277local function fetch(specification)
25278 local original=specification.original
25279 local scheme=specification.scheme
25280 local cleanname=schemes.cleanname(specification)
25281 local cachename=caches.setfirstwritablefile(cleanname,"schemes")
25282 if not cached[original] then
25283 statistics.starttiming(schemes)
25284 if not io.exists(cachename) or (os.difftime(os.time(),lfs.attributes(cachename).modification)>(thresholds[protocol] or threshold)) then
25285 cached[original]=cachename
25286 local handler=handlers[scheme]
25287 if handler then
25288 if trace_schemes then
25289 report_schemes("fetching %a, protocol %a, method %a",original,scheme,"built-in")
25290 end
25291 logs.flush()
25292 handler(specification,cachename)
25293 else
25294 if trace_schemes then
25295 report_schemes("fetching %a, protocol %a, method %a",original,scheme,"curl")
25296 end
25297 logs.flush()
25298 runner {
25299 original=original,
25300 cachename=cachename,
25301 }
25302 end
25303 end
25304 if io.exists(cachename) then
25305 cached[original]=cachename
25306 if trace_schemes then
25307 report_schemes("using cached %a, protocol %a, cachename %a",original,scheme,cachename)
25308 end
25309 else
25310 cached[original]=""
25311 if trace_schemes then
25312 report_schemes("using missing %a, protocol %a",original,scheme)
25313 end
25314 end
25315 loaded[scheme]=loaded[scheme]+1
25316 statistics.stoptiming(schemes)
25317 else
25318 if trace_schemes then
25319 report_schemes("reusing %a, protocol %a",original,scheme)
25320 end
25321 reused[scheme]=reused[scheme]+1
25322 end
25323 return cached[original]
25324end
25325local function finder(specification,filetype)
25326 return resolvers.methodhandler("finders",fetch(specification),filetype)
25327end
25328local opener=openers.file
25329local loader=loaders.file
25330local function install(scheme,handler,newthreshold)
25331 handlers [scheme]=handler
25332 loaded [scheme]=0
25333 reused [scheme]=0
25334 finders [scheme]=finder
25335 openers [scheme]=opener
25336 loaders [scheme]=loader
25337 thresholds[scheme]=newthreshold or threshold
25338end
25339schemes.install=install
25340local function http_handler(specification,cachename)
25341 local tempname=cachename..".tmp"
25342 local handle=io.open(tempname,"wb")
25343 local status,message=http.request {
25344 url=specification.original,
25345 sink=ltn12.sink.file(handle)
25346 }
25347 if not status then
25348 os.remove(tempname)
25349 else
25350 os.remove(cachename)
25351 os.rename(tempname,cachename)
25352 end
25353 return cachename
25354end
25355install('http',http_handler)
25356install('https')
25357install('ftp')
25358statistics.register("scheme handling time",function()
25359 local l,r,nl,nr={},{},0,0
25360 for k,v in sortedhash(loaded) do
25361 if v>0 then
25362 nl=nl+1
25363 l[nl]=k..":"..v
25364 end
25365 end
25366 for k,v in sortedhash(reused) do
25367 if v>0 then
25368 nr=nr+1
25369 r[nr]=k..":"..v
25370 end
25371 end
25372 local n=nl+nr
25373 if n>0 then
25374 if nl==0 then l={ "none" } end
25375 if nr==0 then r={ "none" } end
25376 return format("%s seconds, %s processed, threshold %s seconds, loaded: %s, reused: %s",
25377 statistics.elapsedtime(schemes),n,threshold,concat(l," "),concat(l," "))
25378 else
25379 return nil
25380 end
25381end)
25382local httprequest=http.request
25383local toquery=url.toquery
25384local function fetchstring(url,data)
25385 local q=data and toquery(data)
25386 if q then
25387 url=url.."?"..q
25388 end
25389 local reply=httprequest(url)
25390 return reply
25391end
25392schemes.fetchstring=fetchstring
25393function schemes.fetchtable(url,data)
25394 local reply=fetchstring(url,data)
25395 if reply then
25396 local s=load("return "..reply)
25397 if s then
25398 return s()
25399 end
25400 end
25401end
25402
25403
25404end
25405
25406do
25407
25408package.loaded["data-lua"] = package.loaded["data-lua"] or true
25409
25410
25411
25412if not modules then modules={} end modules ['data-lua']={
25413 version=1.001,
25414 comment="companion to luat-lib.mkiv",
25415 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
25416 copyright="PRAGMA ADE / ConTeXt Development Team",
25417 license="see context related readme files"
25418}
25419local package,lpeg=package,lpeg
25420local loadfile=loadfile
25421local addsuffix=file.addsuffix
25422local P,S,Cs,lpegmatch=lpeg.P,lpeg.S,lpeg.Cs,lpeg.match
25423local luasuffixes={ 'tex','lua' }
25424local libsuffixes={ 'lib' }
25425local luaformats={ 'TEXINPUTS','LUAINPUTS' }
25426local libformats={ 'CLUAINPUTS' }
25427local helpers=package.helpers or {}
25428local methods=helpers.methods or {}
25429local resolvers=resolvers
25430local resolveprefix=resolvers.resolve
25431local expandedpaths=resolvers.expandedpathlistfromvariable
25432local findfile=resolvers.findfile
25433helpers.report=logs.reporter("resolvers","libraries")
25434trackers.register("resolvers.libraries",function(v) helpers.trace=v end)
25435trackers.register("resolvers.locating",function(v) helpers.trace=v end)
25436helpers.sequence={
25437 "already loaded",
25438 "preload table",
25439 "lua variable format",
25440 "lib variable format",
25441 "lua extra list",
25442 "lib extra list",
25443 "path specification",
25444 "cpath specification",
25445 "all in one fallback",
25446 "not loaded",
25447}
25448local pattern=Cs(P("!")^0/""*(P("/")*P(-1)/"/"+P("/")^1/"/"+1)^0)
25449function helpers.cleanpath(path)
25450 return resolveprefix(lpegmatch(pattern,path))
25451end
25452local loadedaslib=helpers.loadedaslib
25453local registerpath=helpers.registerpath
25454local lualibfile=helpers.lualibfile
25455local luaformatpaths
25456local libformatpaths
25457local function getluaformatpaths()
25458 if not luaformatpaths then
25459 luaformatpaths={}
25460 for i=1,#luaformats do
25461 registerpath("lua format","lua",luaformatpaths,expandedpaths(luaformats[i]))
25462 end
25463 end
25464 return luaformatpaths
25465end
25466local function getlibformatpaths()
25467 if not libformatpaths then
25468 libformatpaths={}
25469 for i=1,#libformats do
25470 registerpath("lib format","lib",libformatpaths,expandedpaths(libformats[i]))
25471 end
25472 end
25473 return libformatpaths
25474end
25475local function loadedbyformat(name,rawname,suffixes,islib,what)
25476 local trace=helpers.trace
25477 local report=helpers.report
25478 for i=1,#suffixes do
25479 local format=suffixes[i]
25480 local resolved=findfile(name,format) or ""
25481 if trace then
25482 report("%s format, identifying %a using format %a",what,name,format)
25483 end
25484 if resolved~="" then
25485 if trace then
25486 report("%s format, %a found on %a",what,name,resolved)
25487 end
25488 if islib then
25489 return loadedaslib(resolved,rawname)
25490 else
25491 return loadfile(resolved)
25492 end
25493 end
25494 end
25495end
25496helpers.loadedbyformat=loadedbyformat
25497methods["lua variable format"]=function(name)
25498 if helpers.trace then
25499 helpers.report("%s format, checking %s paths","lua",#getluaformatpaths())
25500 end
25501 return loadedbyformat(addsuffix(lualibfile(name),"lua"),name,luasuffixes,false,"lua")
25502end
25503methods["lib variable format"]=function(name)
25504 if helpers.trace then
25505 helpers.report("%s format, checking %s paths","lib",#getlibformatpaths())
25506 end
25507 return loadedbyformat(addsuffix(lualibfile(name),os.libsuffix),name,libsuffixes,true,"lib")
25508end
25509resolvers.loadlualib=require
25510
25511
25512end
25513
25514do
25515
25516package.loaded["data-aux"] = package.loaded["data-aux"] or true
25517
25518
25519
25520if not modules then modules={} end modules ['data-aux']={
25521 version=1.001,
25522 comment="companion to luat-lib.mkiv",
25523 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
25524 copyright="PRAGMA ADE / ConTeXt Development Team",
25525 license="see context related readme files"
25526}
25527local find=string.find
25528local type,next=type,next
25529local addsuffix,removesuffix=file.addsuffix,file.removesuffix
25530local loaddata,savedata=io.loaddata,io.savedata
25531local trace_locating=false trackers.register("resolvers.locating",function(v) trace_locating=v end)
25532local resolvers=resolvers
25533local cleanpath=resolvers.cleanpath
25534local findfiles=resolvers.findfiles
25535local report_scripts=logs.reporter("resolvers","scripts")
25536function resolvers.updatescript(oldname,newname)
25537 local scriptpath="context/lua"
25538 local oldscript=cleanpath(oldname)
25539 local newname=addsuffix(newname,"lua")
25540 local newscripts=findfiles(newname) or {}
25541 if trace_locating then
25542 report_scripts("to be replaced old script %a",oldscript)
25543 end
25544 if #newscripts==0 then
25545 if trace_locating then
25546 report_scripts("unable to locate new script")
25547 end
25548 else
25549 for i=1,#newscripts do
25550 local newscript=cleanpath(newscripts[i])
25551 if trace_locating then
25552 report_scripts("checking new script %a",newscript)
25553 end
25554 if oldscript==newscript then
25555 if trace_locating then
25556 report_scripts("old and new script are the same")
25557 end
25558 elseif not find(newscript,scriptpath,1,true) then
25559 if trace_locating then
25560 report_scripts("new script should come from %a",scriptpath)
25561 end
25562 elseif not (find(oldscript,removesuffix(newname).."$") or find(oldscript,newname.."$")) then
25563 if trace_locating then
25564 report_scripts("invalid new script name")
25565 end
25566 else
25567 local newdata=loaddata(newscript)
25568 if newdata then
25569 if trace_locating then
25570 report_scripts("old script content replaced by new content: %s",oldscript)
25571 end
25572 savedata(oldscript,newdata)
25573 break
25574 elseif trace_locating then
25575 report_scripts("unable to load new script")
25576 end
25577 end
25578 end
25579 end
25580end
25581
25582
25583end
25584
25585do
25586
25587package.loaded["data-tmf"] = package.loaded["data-tmf"] or true
25588
25589
25590
25591if not modules then modules={} end modules ['data-tmf']={
25592 version=1.001,
25593 comment="companion to luat-lib.mkiv",
25594 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
25595 copyright="PRAGMA ADE / ConTeXt Development Team",
25596 license="see context related readme files"
25597}
25598local resolvers=resolvers
25599local report_tds=logs.reporter("resolvers","tds")
25600function resolvers.load_tree(tree,resolve)
25601 if type(tree)=="string" and tree~="" then
25602 local getenv,setenv=resolvers.getenv,resolvers.setenv
25603 local texos="texmf-"..os.platform
25604 local oldroot=environment.texroot
25605 local newroot=file.collapsepath(tree)
25606 local newtree=file.join(newroot,texos)
25607 local newpath=file.join(newtree,"bin")
25608 if not lfs.isdir(newtree) then
25609 report_tds("no %a under tree %a",texos,tree)
25610 os.exit()
25611 end
25612 if not lfs.isdir(newpath) then
25613 report_tds("no '%s/bin' under tree %a",texos,tree)
25614 os.exit()
25615 end
25616 local texmfos=newtree
25617 environment.texroot=newroot
25618 environment.texos=texos
25619 environment.texmfos=texmfos
25620 if resolve then
25621 resolvers.luacnfspec=resolvers.resolve(resolvers.luacnfspec)
25622 end
25623 setenv('SELFAUTOPARENT',newroot)
25624 setenv('SELFAUTODIR',newtree)
25625 setenv('SELFAUTOLOC',newpath)
25626 setenv('TEXROOT',newroot)
25627 setenv('TEXOS',texos)
25628 setenv('TEXMFOS',texmfos)
25629 setenv('TEXMFCNF',resolvers.luacnfspec,true)
25630 setenv('PATH',newpath..io.pathseparator..getenv('PATH'))
25631 report_tds("changing from root %a to %a",oldroot,newroot)
25632 report_tds("prepending %a to PATH",newpath)
25633 report_tds("setting TEXMFCNF to %a",resolvers.luacnfspec)
25634 report_tds()
25635 end
25636end
25637
25638
25639end
25640
25641do
25642
25643package.loaded["data-lst"] = package.loaded["data-lst"] or true
25644
25645
25646
25647if not modules then modules={} end modules ['data-lst']={
25648 version=1.001,
25649 comment="companion to luat-lib.mkiv",
25650 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
25651 copyright="PRAGMA ADE / ConTeXt Development Team",
25652 license="see context related readme files"
25653}
25654local type=type
25655local sortedhash=table.sortedhash
25656local isdir=lfs.isdir
25657local resolvers=resolvers
25658local listers=resolvers.listers or {}
25659resolvers.listers=listers
25660local resolveprefix=resolvers.resolve
25661local configurationfiles=resolvers.configurationfiles
25662local expandedpathfromlist=resolvers.expandedpathfromlist
25663local splitpath=resolvers.splitpath
25664local knownvariables=resolvers.knownvariables
25665local report_lists=logs.reporter("resolvers","lists")
25666local report_resolved=logs.reporter("system","resolved")
25667local function tabstr(str)
25668 if not str then
25669 return "unset"
25670 elseif type(str)=='table' then
25671 return concat(str," | ")
25672 else
25673 return str
25674 end
25675end
25676function listers.variables(pattern)
25677 local result=resolvers.knownvariables(pattern)
25678 for key,value in sortedhash(result) do
25679 report_lists(key)
25680 report_lists(" env: %s",tabstr(value.environment))
25681 report_lists(" var: %s",tabstr(value.variable))
25682 report_lists(" exp: %s",tabstr(value.expansion))
25683 report_lists(" res: %s",tabstr(value.resolved))
25684 end
25685end
25686function listers.configurations()
25687 local configurations=configurationfiles()
25688 for i=1,#configurations do
25689 report_resolved("file : %s",resolveprefix(configurations[i]))
25690 end
25691 report_resolved("")
25692 local list=expandedpathfromlist(splitpath(resolvers.luacnfspec))
25693 for i=1,#list do
25694 local li=resolveprefix(list[i])
25695 if isdir(li) then
25696 report_resolved("path - %s",li)
25697 else
25698 report_resolved("path + %s",li)
25699 end
25700 end
25701end
25702
25703
25704end
25705
25706do
25707
25708package.loaded["libs-ini"] = package.loaded["libs-ini"] or true
25709
25710
25711
25712if not modules then modules={} end modules ['libs-ini']={
25713 version=1.001,
25714 comment="companion to luat-lib.mkiv",
25715 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
25716 copyright="PRAGMA ADE / ConTeXt Development Team",
25717 license="see context related readme files"
25718}
25719local type,unpack=type,unpack
25720local find=string.find
25721local nameonly=file.nameonly
25722local joinfile=file.join
25723local addsuffix=file.addsuffix
25724local qualifiedpath=file.is_qualified_path
25725local isfile=lfs.isfile
25726local findfile=resolvers.findfile
25727local expandpaths=resolvers.expandedpathlistfromvariable
25728local report=logs.reporter("resolvers","libraries")
25729local trace=false
25730local silent=false
25731trackers.register("resolvers.lib",function(v) trace=v end)
25732trackers.register("resolvers.lib.silent",function(v) silent=v end)
25733local function findlib(required)
25734 local suffix=os.libsuffix or "so"
25735 if not qualifiedpath(required) then
25736 local list=directives.value("system.librarynames" )
25737 local only=nameonly(required)
25738 if type(list)=="table" then
25739 list=list[only]
25740 if type(list)~="table" then
25741 list={ only }
25742 end
25743 else
25744 list={ only }
25745 end
25746 if trace then
25747 report("using lookup list for library %a: % | t",only,list)
25748 end
25749 for i=1,#list do
25750 local name=list[i]
25751 local found=findfile(name,"lib")
25752 if not found or found=="" then
25753 found=findfile(addsuffix(name,suffix),"lib")
25754 end
25755 if found and found~="" then
25756 if trace then
25757 report("library %a resolved via %a path to %a",name,"tds lib",found)
25758 end
25759 return found
25760 end
25761 end
25762 if expandpaths then
25763 local list=expandpaths("PATH")
25764 local base=addsuffix(only,suffix)
25765 for i=1,#list do
25766 local full=joinfile(list[i],base)
25767 local found=isfile(full) and full
25768 if found and found~="" then
25769 if trace then
25770 report("library %a resolved via %a path to %a",full,"system",found)
25771 end
25772 return found
25773 end
25774 end
25775 end
25776 elseif isfile(addsuffix(required,suffix)) then
25777 if trace then
25778 report("library with qualified name %a %sfound",required,"")
25779 end
25780 return required
25781 else
25782 if trace then
25783 report("library with qualified name %a %sfound",required,"not ")
25784 end
25785 end
25786 return false
25787end
25788local foundlibraries=table.setmetatableindex(function(t,k)
25789 local v=findlib(k)
25790 t[k]=v
25791 return v
25792end)
25793function resolvers.findlib(required)
25794 return foundlibraries[required]
25795end
25796local libraries={}
25797resolvers.libraries=libraries
25798local report=logs.reporter("optional")
25799if optional then optional.loaded={} end
25800function libraries.validoptional(name)
25801 local thelib=optional and optional[name]
25802 if not thelib then
25803 elseif thelib.initialize then
25804 return thelib
25805 else
25806 report("invalid optional library %a",libname)
25807 end
25808end
25809function libraries.optionalloaded(name,libnames)
25810 local thelib=optional and optional[name]
25811 if not thelib then
25812 report("no optional %a library found",name)
25813 else
25814 local thelib_initialize=thelib.initialize
25815 if not thelib_initialize then
25816 report("invalid optional library %a",name)
25817 else
25818 if type(libnames)=="string" then
25819 libnames={ libnames }
25820 end
25821 if type(libnames)=="table" then
25822 for i=1,#libnames do
25823 local libname=libnames[i]
25824 local filename=foundlibraries[libname]
25825 if filename and filename~="" then
25826 libnames[i]=filename
25827 else
25828 report("unable to locate library %a",libname)
25829 return
25830 end
25831 end
25832 local initialized=thelib_initialize(unpack(libnames))
25833 if not initialized then
25834 report("unable to initialize library '% + t'",libnames)
25835 elseif not silent then
25836 report("using library '% + t'",libnames)
25837 end
25838 return initialized
25839 end
25840 end
25841 end
25842end
25843if FFISUPPORTED and ffi and ffi.load then
25844 local ffiload=ffi.load
25845 function ffi.load(name)
25846 local full=name and foundlibraries[name]
25847 if full then
25848 return ffiload(full)
25849 else
25850 return ffiload(name)
25851 end
25852 end
25853end
25854local dofile=dofile
25855local savedrequire=require
25856function require(name,version)
25857 if find(name,"%.lua$") or find(name,"%.lmt$") then
25858 local m=dofile(findfile(name))
25859 if m then
25860 package.loaded[name]=m
25861 return m
25862 end
25863 else
25864 return savedrequire(name)
25865 end
25866end
25867
25868
25869end
25870
25871do
25872
25873package.loaded["luat-sta"] = package.loaded["luat-sta"] or true
25874
25875
25876
25877if not modules then modules={} end modules ['luat-sta']={
25878 version=1.001,
25879 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
25880 copyright="PRAGMA ADE / ConTeXt Development Team",
25881 license="see context related readme files"
25882}
25883local gmatch,match=string.gmatch,string.match
25884local type=type
25885states=states or {}
25886local states=states
25887states.data=states.data or {}
25888local data=states.data
25889states.hash=states.hash or {}
25890local hash=states.hash
25891states.tag=states.tag or ""
25892states.filename=states.filename or ""
25893function states.save(filename,tag)
25894 tag=tag or states.tag
25895 filename=file.addsuffix(filename or states.filename,'lus')
25896 io.savedata(filename,
25897 "-- generator : luat-sta.lua\n".."-- state tag : "..tag.."\n\n"..table.serialize(data[tag or states.tag] or {},true)
25898 )
25899end
25900function states.load(filename,tag)
25901 states.filename=filename
25902 states.tag=tag or "whatever"
25903 states.filename=file.addsuffix(states.filename,'lus')
25904 data[states.tag],hash[states.tag]=(io.exists(filename) and dofile(filename)) or {},{}
25905end
25906local function set_by_tag(tag,key,value,default,persistent)
25907 local d,h=data[tag],hash[tag]
25908 if d then
25909 if type(d)=="table" then
25910 local dkey,hkey=key,key
25911 local pre,post=match(key,"(.+)%.([^%.]+)$")
25912 if pre and post then
25913 for k in gmatch(pre,"[^%.]+") do
25914 local dk=d[k]
25915 if not dk then
25916 dk={}
25917 d[k]=dk
25918 elseif type(dk)=="string" then
25919 break
25920 end
25921 d=dk
25922 end
25923 dkey,hkey=post,key
25924 end
25925 if value==nil then
25926 value=default
25927 elseif value==false then
25928 elseif persistent then
25929 value=value or d[dkey] or default
25930 else
25931 value=value or default
25932 end
25933 d[dkey],h[hkey]=value,value
25934 elseif type(d)=="string" then
25935 data[tag],hash[tag]=value,value
25936 end
25937 end
25938end
25939local function get_by_tag(tag,key,default)
25940 local h=hash[tag]
25941 if h and h[key] then
25942 return h[key]
25943 else
25944 local d=data[tag]
25945 if d then
25946 for k in gmatch(key,"[^%.]+") do
25947 local dk=d[k]
25948 if dk~=nil then
25949 d=dk
25950 else
25951 return default
25952 end
25953 end
25954 if d==false then
25955 return false
25956 else
25957 return d or default
25958 end
25959 end
25960 end
25961end
25962states.set_by_tag=set_by_tag
25963states.get_by_tag=get_by_tag
25964function states.set(key,value,default,persistent)
25965 set_by_tag(states.tag,key,value,default,persistent)
25966end
25967function states.get(key,default)
25968 return get_by_tag(states.tag,key,default)
25969end
25970
25971
25972end
25973
25974do
25975
25976package.loaded["luat-fmt"] = package.loaded["luat-fmt"] or true
25977
25978
25979
25980if not modules then modules={} end modules ['luat-fmt']={
25981 version=1.001,
25982 comment="companion to mtxrun",
25983 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
25984 copyright="PRAGMA ADE / ConTeXt Development Team",
25985 license="see context related readme files"
25986}
25987local format=string.format
25988local concat=table.concat
25989local quoted=string.quoted
25990local luasuffixes=utilities.lua.suffixes
25991local report_format=logs.reporter("resolvers","formats")
25992local function primaryflags(arguments)
25993 local flags={}
25994 if arguments.silent then
25995 flags[#flags+1]="--interaction=batchmode"
25996 end
25997 return concat(flags," ")
25998end
25999local function secondaryflags(arguments)
26000 local trackers=arguments.trackers
26001 local directives=arguments.directives
26002 local flags={}
26003 if trackers and trackers~="" then
26004 flags[#flags+1]="--c:trackers="..quoted(trackers)
26005 end
26006 if directives and directives~="" then
26007 flags[#flags+1]="--c:directives="..quoted(directives)
26008 end
26009 if arguments.silent then
26010 flags[#flags+1]="--c:silent"
26011 end
26012 if arguments.errors then
26013 flags[#flags+1]="--c:errors"
26014 end
26015 if arguments.ansi then
26016 flags[#flags+1]="--c:ansi"
26017 end
26018 if arguments.ansilog then
26019 flags[#flags+1]="--c:ansilog"
26020 end
26021 if arguments.strip then
26022 flags[#flags+1]="--c:strip"
26023 end
26024 if arguments.lmtx then
26025 flags[#flags+1]="--c:lmtx"
26026 end
26027 return concat(flags," ")
26028end
26029local template=[[--ini %primaryflags% --socket --shell-escape --lua=%luafile% %texfile% %secondaryflags% %redirect%]]
26030local checkers={
26031 primaryflags="verbose",
26032 secondaryflags="verbose",
26033 luafile="readable",
26034 texfile="readable",
26035 redirect="string",
26036 binarypath="string",
26037}
26038local runners={
26039 luametatex=sandbox.registerrunner {
26040 name="make luametatex format",
26041 program="luametatex",
26042 template=template,
26043 checkers=checkers,
26044 reporter=report_format,
26045 },
26046 luatex=sandbox.registerrunner {
26047 name="make luatex format",
26048 program="luatex",
26049 template=template,
26050 checkers=checkers,
26051 reporter=report_format,
26052 },
26053 luajittex=sandbox.registerrunner {
26054 name="make luajittex format",
26055 program="luajittex",
26056 template=template,
26057 checkers=checkers,
26058 reporter=report_format,
26059 },
26060}
26061local stubfiles={
26062 luametatex="luat-cod.lmt",
26063 luatex="luat-cod.lua",
26064 luajittex="luat-cod.lua",
26065}
26066local suffixes={
26067 luametatex="mkxl",
26068 luatex="mkiv",
26069 luajittex="mkiv",
26070}
26071local function validbinarypath()
26072 if not environment.arguments.nobinarypath then
26073 local path=environment.ownpath or file.dirname(environment.ownname)
26074 if path and path~="" then
26075 path=dir.expandname(path)
26076 if path~="" and lfs.isdir(path) then
26077 return path
26078 end
26079 end
26080 end
26081end
26082local function fatalerror(startupdir,...)
26083 report_format(...)
26084 lfs.chdir(startupdir)
26085end
26086function environment.make_format(formatname)
26087 local arguments=environment.arguments
26088 local engine=environment.ownmain or "luatex"
26089 local silent=arguments.silent
26090 local errors=arguments.errors
26091 local runner=runners[engine]
26092 local startupdir=dir.current()
26093 if not runner then
26094 return fatalerror(startupdir,"the format %a cannot be generated, no runner available for engine %a",name,engine)
26095 end
26096 local luasourcename=stubfiles[engine]
26097 if not luasourcename then
26098 return fatalerror(startupdir,"no lua stub file specified for %a",engine)
26099 end
26100 local texsourcename=file.addsuffix(formatname,suffixes[engine])
26101 local fulltexsourcename=resolvers.findfile(texsourcename,"tex") or ""
26102 if fulltexsourcename=="" then
26103 return fatalerror(startupdir,"no tex source file with name %a (mkiv or tex)",formatname)
26104 end
26105 local fulltexsourcename=dir.expandname(fulltexsourcename)
26106 local texsourcepath=file.dirname(fulltexsourcename)
26107 if lfs.isfile(fulltexsourcename) then
26108 report_format("using tex source file %a",fulltexsourcename)
26109 else
26110 return fatalerror(startupdir,"no accessible tex source file with name %a",fulltexsourcename)
26111 end
26112 local fullluasourcename=dir.expandname(file.join(texsourcepath,luasourcename) or "")
26113 if lfs.isfile(fullluasourcename) then
26114 report_format("using lua stub file %a",fullluasourcename)
26115 else
26116 return fatalerror(startupdir,"no accessible lua stub file with name %a",fulltexsourcename)
26117 end
26118 local validformatpath=caches.getwritablepath("formats",engine) or ""
26119 if validformatpath=="" then
26120 return fatalerror(startupdir,"invalid format path, insufficient write access")
26121 end
26122 local binarypath=validbinarypath()
26123 report_format("changing to format path %a",validformatpath)
26124 if not lfs.chdir(validformatpath) then
26125 return fatalerror(startupdir,"unable to change to format path %a",validformatpath)
26126 end
26127 local primaryflags=primaryflags(arguments)
26128 local secondaryflags=secondaryflags(arguments)
26129 local specification={
26130 binarypath=binarypath,
26131 primaryflags=primaryflags,
26132 secondaryflags=secondaryflags,
26133 luafile=quoted(fullluasourcename),
26134 texfile=quoted(fulltexsourcename),
26135 }
26136 if silent then
26137 specification.redirect="> temp.log"
26138 end
26139 statistics.starttiming("format")
26140 local result=runner(specification)
26141 statistics.stoptiming("format")
26142 if silent then
26143 os.remove("temp.log")
26144 end
26145 report_format()
26146 if binarypath and binarypath~="" then
26147 report_format("binary path : %s",binarypath or "?")
26148 end
26149 report_format("format path : %s",validformatpath)
26150 report_format("luatex engine : %s",engine)
26151 report_format("lua startup file : %s",fullluasourcename)
26152 if primaryflags~="" then
26153 report_format("primary flags : %s",primaryflags)
26154 end
26155 if secondaryflags~="" then
26156 report_format("secondary flags : %s",secondaryflags)
26157 end
26158 report_format("context file : %s",fulltexsourcename)
26159 report_format("run time : %.3f seconds",statistics.elapsed("format"))
26160 report_format("return value : %s",result==0 and "okay" or "error")
26161 report_format()
26162 lfs.chdir(startupdir)
26163end
26164local template=[[%primaryflags% --socket --shell-escape --fmt=%fmtfile% --lua=%luafile% %texfile% %secondaryflags%]]
26165local checkers={
26166 primaryflags="verbose",
26167 secondaryflags="verbose",
26168 fmtfile="readable",
26169 luafile="readable",
26170 texfile="readable",
26171}
26172local runners={
26173 luatex=sandbox.registerrunner {
26174 name="run luatex format",
26175 program="luatex",
26176 template=template,
26177 checkers=checkers,
26178 reporter=report_format,
26179 },
26180 luametatex=sandbox.registerrunner {
26181 name="run luametatex format",
26182 program="luametatex",
26183 template=template,
26184 checkers=checkers,
26185 reporter=report_format,
26186 },
26187 luajittex=sandbox.registerrunner {
26188 name="run luajittex format",
26189 program="luajittex",
26190 template=template,
26191 checkers=checkers,
26192 reporter=report_format,
26193 },
26194}
26195function environment.run_format(formatname,scriptname,filename,primaryflags,secondaryflags,verbose)
26196 local engine=environment.ownmain or "luatex"
26197 if not formatname or formatname=="" then
26198 report_format("missing format name")
26199 return
26200 end
26201 if not scriptname or scriptname=="" then
26202 report_format("missing script name")
26203 return
26204 end
26205 if not lfs.isfile(formatname) or not lfs.isfile(scriptname) then
26206 formatname,scriptname=resolvers.locateformat(formatname)
26207 end
26208 if not formatname or formatname=="" then
26209 report_format("invalid format name")
26210 return
26211 end
26212 if not scriptname or scriptname=="" then
26213 report_format("invalid script name")
26214 return
26215 end
26216 local runner=runners[engine]
26217 if not runner then
26218 report_format("format %a cannot be run, no runner available for engine %a",file.nameonly(name),engine)
26219 return
26220 end
26221 if not filename then
26222 filename ""
26223 end
26224 local binarypath=validbinarypath()
26225 local specification={
26226 binarypath=binarypath,
26227 primaryflags=primaryflags or "",
26228 secondaryflags=secondaryflags or "",
26229 fmtfile=quoted(formatname),
26230 luafile=quoted(scriptname),
26231 texfile=filename~="" and quoted(filename) or "",
26232 }
26233 statistics.starttiming("make format")
26234 local result=runner(specification)
26235 statistics.stoptiming("make format")
26236 if verbose then
26237 report_format()
26238 if binarypath and binarypath~="" then
26239 report_format("binary path : %s",binarypath)
26240 end
26241 report_format("luatex engine : %s",engine)
26242 report_format("lua startup file : %s",scriptname)
26243 report_format("tex format file : %s",formatname)
26244 if filename~="" then
26245 report_format("tex input file : %s",filename)
26246 end
26247 if primaryflags~="" then
26248 report_format("primary flags : %s",primaryflags)
26249 end
26250 if secondaryflags~="" then
26251 report_format("secondary flags : %s",secondaryflags)
26252 end
26253 report_format("run time : %0.3f seconds",statistics.elapsed("make format"))
26254 report_format("return value : %s",result==0 and "okay" or "error")
26255 report_format()
26256 end
26257 return result
26258end
26259
26260
26261end
26262
26263
26264
26265
26266
26267
26268
26269
26270
26271
26272
26273
26274if arg and (arg[0] == 'luatex' or arg[0] == 'luatex.exe') and arg[1] == "--luaonly" then
26275 arg[-1]=arg[0] arg[0]=arg[2] for k=3,#arg do arg[k-2]=arg[k] end arg[#arg]=nil arg[#arg]=nil
26276end
26277
26278
26279
26280local format, gsub, gmatch, match, find = string.format, string.gsub, string.gmatch, string.match, string.find
26281local concat = table.concat
26282
26283local ownname = environment and environment.ownname or arg[0] or 'mtxrun.lua'
26284local ownpath = gsub(match(ownname,"^(.+)[\\/].-$") or ".","\\","/")
26285local owntree = environment and environment.ownpath or ownpath
26286
26287local ownlibs = {
26288
26289 'l-bit32.lua',
26290 'l-lua.lua',
26291 'l-macro.lua',
26292 'l-sandbox.lua',
26293 'l-package.lua',
26294 'l-lpeg.lua',
26295 'l-function.lua',
26296 'l-string.lua',
26297 'l-table.lua',
26298 'l-io.lua',
26299 'l-number.lua',
26300 'l-set.lua',
26301 'l-os.lua',
26302 'l-file.lua',
26303 'l-gzip.lua',
26304 'l-md5.lua',
26305 'l-sha.lua',
26306 'l-url.lua',
26307 'l-dir.lua',
26308 'l-boolean.lua',
26309 'l-unicode.lua',
26310 'l-math.lua',
26311
26312 'util-str.lua',
26313 'util-tab.lua',
26314 'util-fil.lua',
26315 'util-sac.lua',
26316 'util-sto.lua',
26317 'util-prs.lua',
26318 'util-fmt.lua',
26319
26320 'util-soc-imp-reset.lua',
26321 'util-soc-imp-socket.lua',
26322 'util-soc-imp-copas.lua',
26323 'util-soc-imp-ltn12.lua',
26324
26325 'util-soc-imp-mime.lua',
26326 'util-soc-imp-url.lua',
26327 'util-soc-imp-headers.lua',
26328 'util-soc-imp-tp.lua',
26329 'util-soc-imp-http.lua',
26330 'util-soc-imp-ftp.lua',
26331 'util-soc-imp-smtp.lua',
26332
26333 'trac-set.lua',
26334 'trac-log.lua',
26335 'trac-inf.lua',
26336 'trac-pro.lua',
26337 'util-lua.lua',
26338 'util-deb.lua',
26339
26340 'util-tpl.lua',
26341 'util-sbx.lua',
26342 'util-mrg.lua',
26343
26344 'util-env.lua',
26345 'luat-env.lua',
26346
26347 'util-zip.lua',
26348
26349 'lxml-tab.lua',
26350 'lxml-lpt.lua',
26351
26352 'lxml-mis.lua',
26353 'lxml-aux.lua',
26354 'lxml-xml.lua',
26355
26356 'trac-xml.lua',
26357
26358 'data-ini.lua',
26359 'data-exp.lua',
26360 'data-env.lua',
26361 'data-tmp.lua',
26362 'data-met.lua',
26363 'data-res.lua',
26364 'data-pre.lua',
26365 'data-inp.lua',
26366 'data-out.lua',
26367 'data-fil.lua',
26368 'data-con.lua',
26369 'data-use.lua',
26370
26371
26372 'data-zip.lua',
26373 'data-tre.lua',
26374 'data-sch.lua',
26375 'data-lua.lua',
26376 'data-aux.lua',
26377 'data-tmf.lua',
26378 'data-lst.lua',
26379
26380 'libs-ini.lua',
26381
26382 'luat-sta.lua',
26383 'luat-fmt.lua',
26384
26385}
26386
26387
26388
26389
26390local ownlist = {
26391
26392
26393 owntree .. "/../../../../context/sources",
26394
26395 owntree .. "/../../texmf-local/tex/context/base/mkiv",
26396 owntree .. "/../../texmf-context/tex/context/base/mkiv",
26397 owntree .. "/../../texmf/tex/context/base/mkiv",
26398 owntree .. "/../../../texmf-local/tex/context/base/mkiv",
26399 owntree .. "/../../../texmf-context/tex/context/base/mkiv",
26400 owntree .. "/../../../texmf/tex/context/base/mkiv",
26401
26402 owntree .. "/../../texmf-local/tex/context/base",
26403 owntree .. "/../../texmf-context/tex/context/base",
26404 owntree .. "/../../texmf/tex/context/base",
26405 owntree .. "/../../../texmf-local/tex/context/base",
26406 owntree .. "/../../../texmf-context/tex/context/base",
26407 owntree .. "/../../../texmf/tex/context/base",
26408}
26409
26410if ownpath == "." then table.remove(ownlist,1) end
26411
26412own = {
26413 name = ownname,
26414 path = ownpath,
26415 tree = owntree,
26416 list = ownlist,
26417 libs = ownlibs,
26418}
26419
26420local function locate_libs()
26421 for l=1,#ownlibs do
26422 local lib = ownlibs[l]
26423 for p =1,#ownlist do
26424 local pth = ownlist[p]
26425 local filename = pth .. "/" .. lib
26426 local found = lfs.isfile(filename)
26427 if found then
26428 package.path = package.path .. ";" .. pth .. "/?.lua"
26429 return pth
26430 end
26431 end
26432 end
26433end
26434
26435local function load_libs()
26436 local found = locate_libs()
26437 if found then
26438 for l=1,#ownlibs do
26439 local filename = found .. "/" .. ownlibs[l]
26440 local codeblob = loadfile(filename)
26441 if codeblob then
26442 codeblob()
26443 end
26444 end
26445 else
26446 resolvers = nil
26447 end
26448end
26449
26450if not resolvers then
26451 load_libs()
26452end
26453
26454if not resolvers then
26455 print("")
26456 print("Mtxrun is unable to start up due to lack of libraries. You may")
26457 print("try to run 'lua mtxrun.lua --selfmerge' in the path where this")
26458 print("script is located (normally under ..../scripts/context/lua) which")
26459 print("will make this script library independent.")
26460 os.exit()
26461end
26462
26463
26464
26465
26466
26467local e_verbose = false
26468
26469
26470
26471local e_silent = environment.argument("silent")
26472local e_errors = environment.argument("errors")
26473local e_noconsole = environment.argument("noconsole")
26474
26475local e_trackers = environment.argument("trackers")
26476local e_directives = environment.argument("directives")
26477local e_experiments = environment.argument("experiments")
26478
26479local t = { }
26480
26481if type(e_directives) == "string" then
26482 t[#t+1] = e_directives
26483end
26484
26485if type(e_silent) == "string" then
26486 t[#t+1] = format("logs.blocked={%s}",e_silent)
26487elseif e_silent == true then
26488 t[#t+1] = "logs.blocked"
26489end
26490
26491if type(e_errors) == "string" then
26492 t[#t+1] = format("logs.errors={%s}",e_errors)
26493elseif e_errors == true then
26494 t[#t+1] = "logs.errors"
26495end
26496
26497if e_noconsole then
26498 t[#t+1] = format("logs.target=file")
26499end
26500
26501if #t > 0 then
26502 e_directives = concat(t,",")
26503end
26504
26505if e_trackers then trackers .enable(e_trackers) end
26506if e_directives then directives .enable(e_directives) end
26507if e_experiments then experiments.enable(e_experiments) end
26508
26509if not environment.trackers then environment.trackers = e_trackers end
26510if not environment.directives then environment.directives = e_directives end
26511if not environment.experiments then environment.experiments = e_experiments end
26512
26513
26514
26515resolvers.reset()
26516
26517local helpinfo = [[
26518<?xml version="1.0" ?>
26519<application>
26520 <metadata>
26521 <entry name="name">mtxrun</entry>
26522 <entry name="detail">ConTeXt TDS Runner Tool</entry>
26523 <entry name="version">1.33</entry>
26524 </metadata>
26525 <flags>
26526 <category name="basic">
26527 <subcategory>
26528 <flag name="script"><short>run an mtx script (lua prefered method) (<ref name="noquotes"/>), no script gives list</short></flag>
26529 <flag name="evaluate"><short>run code passed on the commandline (between quotes) (=loop) (exit|quit aborts)</short></flag>
26530 <flag name="execute"><short>run a script or program (texmfstart method) (<ref name="noquotes"/>)</short></flag>
26531 <flag name="resolve"><short>resolve prefixed arguments</short></flag>
26532 <flag name="ctxlua"><short>run internally (using preloaded libs)</short></flag>
26533 <flag name="internal"><short>run script using built in libraries (same as <ref name="ctxlua"/>)</short></flag>
26534 <flag name="locate"><short>locate given filename in database (default) or system (<ref name="first"/> <ref name="all"/> <ref name="detail"/>)</short></flag>
26535 </subcategory>
26536 <subcategory>
26537 <flag name="tree" value="pathtotree"><short>use given texmf tree (default file: setuptex.tmf)</short></flag>
26538 <flag name="path" value="runpath"><short>go to given path before execution</short></flag>
26539 <flag name="ifchanged" value="filename"><short>only execute when given file has changed (md checksum)</short></flag>
26540 <flag name="iftouched" value="old,new"><short>only execute when given file has changed (time stamp)</short></flag>
26541 </subcategory>
26542 <subcategory>
26543 <flag name="makestubs"><short>create stubs for (context related) scripts</short></flag>
26544 <flag name="removestubs"><short>remove stubs (context related) scripts</short></flag>
26545 <flag name="stubpath" value="binpath"><short>paths where stubs wil be written</short></flag>
26546 <flag name="windows"><short>create windows (mswin) stubs</short></flag>
26547 <flag name="unix"><short>create unix (linux) stubs</short></flag>
26548 <flag name="addbinarypath"><short>prepend the (found) binarypath to runners</short></flag>
26549 </subcategory>
26550 <subcategory>
26551 <flag name="verbose"><short>give a bit more info</short></flag>
26552 <flag name="trackers" value="list"><short>enable given trackers</short></flag>
26553 <flag name="progname" value="str"><short>format or backend</short></flag>
26554 <flag name="systeminfo" value="str"><short>show current operating system, processor, etc</short></flag>
26555 </subcategory>
26556 <subcategory>
26557 <flag name="edit"><short>launch editor with found file</short></flag>
26558 <flag name="launch"><short>launch files like manuals, assumes os support (<ref name="all"/>,<ref name="list"/>)</short></flag>
26559 </subcategory>
26560 <subcategory>
26561 <flag name="timedrun"><short>run a script and time its run</short></flag>
26562 <flag name="autogenerate"><short>regenerate databases if needed (handy when used to run context in an editor)</short></flag>
26563 </subcategory>
26564 <subcategory>
26565 <flag name="usekpse"><short>use kpse as fallback (when no mkiv and cache installed, often slower)</short></flag>
26566 <flag name="forcekpse"><short>force using kpse (handy when no mkiv and cache installed but less functionality)</short></flag>
26567 </subcategory>
26568 <subcategory>
26569 <flag name="prefixes"><short>show supported prefixes</short></flag>
26570 </subcategory>
26571 <subcategory>
26572 <flag name="generate"><short>generate file database</short></flag>
26573 </subcategory>
26574 <subcategory>
26575 <flag name="variables"><short>show configuration variables</short></flag>
26576 <flag name="configurations"><short>show configuration order</short></flag>
26577 </subcategory>
26578 <subcategory>
26579 <flag name="directives"><short>show (known) directives</short></flag>
26580 <flag name="trackers"><short>show (known) trackers</short></flag>
26581 <flag name="experiments"><short>show (known) experiments</short></flag>
26582 </subcategory>
26583 <subcategory>
26584 <flag name="expand-braces"><short>expand complex variable</short></flag>
26585 <flag name="resolve-path"><short>expand variable (completely resolve paths)</short></flag>
26586 <flag name="expand-path"><short>expand variable (resolve paths)</short></flag>
26587 <flag name="expand-var"><short>expand variable (resolve references)</short></flag>
26588 <flag name="show-path"><short>show path expansion of ...</short></flag>
26589 <flag name="var-value"><short>report value of variable</short></flag>
26590 <flag name="find-file"><short>report file location</short></flag>
26591 <flag name="find-path"><short>report path of file</short></flag>
26592 </subcategory>
26593 <subcategory>
26594 <flag name="pattern" value="string"><short>filter variables</short></flag>
26595 </subcategory>
26596 </category>
26597 </flags>
26598</application>
26599]]
26600
26601local application = logs.application {
26602 name = "mtxrun",
26603 banner = "ConTeXt TDS Runner Tool 1.32",
26604 helpinfo = helpinfo,
26605}
26606
26607local report = application.report
26608
26609messages = messages or { }
26610
26611runners = runners or { }
26612
26613runners.applications = {
26614 ["lua"] = "luatex --luaonly --socket",
26615 ["luc"] = "luatex --luaonly --socket",
26616 ["pl"] = "perl",
26617 ["py"] = "python",
26618 ["rb"] = "ruby",
26619}
26620
26621runners.suffixes = {
26622 'rb', 'lua', 'py', 'pl'
26623}
26624
26625runners.registered = {
26626 texexec = { 'texexec.rb', false },
26627 texutil = { 'texutil.rb', true },
26628 texfont = { 'texfont.pl', true },
26629 texfind = { 'texfind.pl', false },
26630 texshow = { 'texshow.pl', false },
26631
26632 makempy = { 'makempy.pl', true },
26633 mptopdf = { 'mptopdf.pl', true },
26634 pstopdf = { 'pstopdf.rb', true },
26635
26636 concheck = { 'concheck.rb', false },
26637 runtools = { 'runtools.rb', true },
26638 textools = { 'textools.rb', true },
26639 tmftools = { 'tmftools.rb', true },
26640 ctxtools = { 'ctxtools.rb', true },
26641 rlxtools = { 'rlxtools.rb', true },
26642 pdftools = { 'pdftools.rb', true },
26643 mpstools = { 'mpstools.rb', true },
26644
26645 xmltools = { 'xmltools.rb', true },
26646
26647 mtxtools = { 'mtxtools.rb', true },
26648 pdftrimwhite = { 'pdftrimwhite.pl', false },
26649}
26650
26651runners.launchers = {
26652 windows = { },
26653 unix = { },
26654}
26655
26656
26657
26658function runners.libpath(...)
26659 package.prepend_libpath(file.dirname(environment.ownscript),...)
26660 package.prepend_libpath(file.dirname(environment.ownname) ,...)
26661end
26662
26663function runners.prepare()
26664 local checkname = environment.argument("ifchanged")
26665 if type(checkname) == "string" and checkname ~= "" then
26666 local oldchecksum = file.loadchecksum(checkname)
26667 local newchecksum = file.checksum(checkname)
26668 if oldchecksum == newchecksum then
26669 if e_verbose then
26670 report("file '%s' is unchanged",checkname)
26671 end
26672 return "skip"
26673 elseif e_verbose then
26674 report("file '%s' is changed, processing started",checkname)
26675 end
26676 file.savechecksum(checkname)
26677 end
26678 local touchname = environment.argument("iftouched")
26679 if type(touchname) == "string" and touchname ~= "" then
26680 local oldname, newname = string.splitup(touchname, ",")
26681 if oldname and newname and oldname ~= "" and newname ~= "" then
26682 if not file.needs_updating(oldname,newname) then
26683 if e_verbose then
26684 report("file '%s' and '%s' have same age",oldname,newname)
26685 end
26686 return "skip"
26687 elseif e_verbose then
26688 report("file '%s' is older than '%s'",oldname,newname)
26689 end
26690 end
26691 end
26692 local runpath = environment.argument("path")
26693 if type(runpath) == "string" and not lfs.chdir(runpath) then
26694 report("unable to change to path '%s'",runpath)
26695 return "error"
26696 end
26697 runners.prepare = function() end
26698 return "run"
26699end
26700
26701function runners.execute_script(fullname,internal,nosplit)
26702 local noquote = environment.argument("noquotes")
26703 if fullname and fullname ~= "" then
26704 local state = runners.prepare()
26705 if state == 'error' then
26706 return false
26707 elseif state == 'skip' then
26708 return true
26709 elseif state == "run" then
26710 local path, name, suffix = file.splitname(fullname)
26711 local result = ""
26712 if path ~= "" then
26713 result = fullname
26714 elseif name then
26715 name = gsub(name,"^int[%a]*:",function()
26716 internal = true
26717 return ""
26718 end )
26719 name = gsub(name,"^script:","")
26720 if suffix == "" and runners.registered[name] and runners.registered[name][1] then
26721 name = runners.registered[name][1]
26722 suffix = file.suffix(name)
26723 end
26724 if suffix == "" then
26725
26726 for _,s in pairs(runners.suffixes) do
26727 result = resolvers.findfile(name .. "." .. s, 'texmfscripts')
26728 if result ~= "" then
26729 break
26730 end
26731 end
26732 elseif runners.applications[suffix] then
26733 result = resolvers.findfile(name, 'texmfscripts')
26734 else
26735
26736 result = resolvers.findfile(name, 'other text files')
26737 end
26738 end
26739 if result and result ~= "" then
26740 if not no_split then
26741 local before, after = environment.splitarguments(fullname)
26742 environment.arguments_before, environment.arguments_after = before, after
26743 end
26744 if internal then
26745 arg = { } for _,v in pairs(environment.arguments_after) do arg[#arg+1] = v end
26746 environment.ownscript = result
26747 dofile(result)
26748 else
26749local texmfcnf = resolvers.getenv("TEXMFCNF")
26750if not texmfcnf or texmfcnf == "" then
26751 texmfcnf = resolvers.expandedpathfromlist(resolvers.splitpath(resolvers.resolve(resolvers.luacnfspec)))
26752 resolvers.setenv("TEXMFCNF",table.concat(texmfcnf,";"))
26753end
26754 local binary = runners.applications[file.suffix(result)]
26755 result = string.quoted(string.unquoted(result))
26756
26757
26758
26759 if binary and binary ~= "" then
26760 result = binary .. " " .. result
26761 end
26762 local command = result .. " " .. environment.reconstructcommandline(environment.arguments_after,noquote)
26763 if e_verbose then
26764 report()
26765 report("executing: %s",command)
26766 report()
26767 report()
26768 io.flush()
26769 end
26770 local code = os.execute(command)
26771 if code == 0 then
26772 return true
26773 else
26774 if binary then
26775 binary = file.addsuffix(binary,os.binsuffix)
26776 for p in gmatch(os.getenv("PATH"),"[^"..io.pathseparator.."]+") do
26777 if lfs.isfile(file.join(p,binary)) then
26778 return false
26779 end
26780 end
26781 report()
26782 report("This script needs '%s' which seems not to be installed.",binary)
26783 report()
26784 end
26785 return false
26786 end
26787 end
26788 end
26789 end
26790 end
26791 return false
26792end
26793
26794function runners.execute_program(fullname)
26795 local noquote = environment.argument("noquotes")
26796 if fullname and fullname ~= "" then
26797 local state = runners.prepare()
26798 if state == 'error' then
26799 return false
26800 elseif state == 'skip' then
26801 return true
26802 elseif state == "run" then
26803 local before, after = environment.splitarguments(fullname)
26804 for k=1,#after do after[k] = resolvers.resolve(after[k]) end
26805 environment.initializearguments(after)
26806 fullname = gsub(fullname,"^bin:","")
26807 local command = fullname .. " " .. (environment.reconstructcommandline(after or "",noquote) or "")
26808 report()
26809 report("executing: %s",command)
26810 report()
26811 report()
26812 io.flush()
26813 local code = os.execute(command)
26814 return code == 0
26815 end
26816 end
26817 return false
26818end
26819
26820
26821
26822local windows_stub = '@echo off\013\010setlocal\013\010set ownpath=%%~dp0%%\013\010texlua "%%ownpath%%mtxrun.lua" --usekpse --execute %s %%*\013\010endlocal\013\010'
26823local unix_stub = '#!/bin/sh\010mtxrun --usekpse --execute %s \"$@\"\010'
26824
26825function runners.handle_stubs(create)
26826 local stubpath = environment.argument('stubpath') or '.'
26827 local windows = environment.argument('windows') or environment.argument('mswin') or false
26828 local unix = environment.argument('unix') or environment.argument('linux') or false
26829 if not windows and not unix then
26830 if os.platform == "unix" then
26831 unix = true
26832 else
26833 windows = true
26834 end
26835 end
26836 for _,v in pairs(runners.registered) do
26837 local name, doit = v[1], v[2]
26838 if doit then
26839 local base = gsub(file.basename(name), "%.(.-)$", "")
26840 if create then
26841 if windows then
26842 io.savedata(file.join(stubpath,base..".bat"),format(windows_stub,name))
26843 report("windows stub for '%s' created",base)
26844 end
26845 if unix then
26846 io.savedata(file.join(stubpath,base),format(unix_stub,name))
26847 report("unix stub for '%s' created",base)
26848 end
26849 else
26850 if windows and (os.remove(file.join(stubpath,base..'.bat')) or os.remove(file.join(stubpath,base..'.cmd'))) then
26851 report("windows stub for '%s' removed", base)
26852 end
26853 if unix and (os.remove(file.join(stubpath,base)) or os.remove(file.join(stubpath,base..'.sh'))) then
26854 report("unix stub for '%s' removed",base)
26855 end
26856 end
26857 end
26858 end
26859end
26860
26861function runners.resolve_string(filename)
26862 if filename and filename ~= "" then
26863 runners.report_location(resolvers.resolve(filename))
26864 end
26865end
26866
26867
26868
26869function runners.locate_file(filename)
26870 if filename and filename ~= "" then
26871 if environment.argument("first") then
26872 runners.report_location(resolvers.findfile(filename))
26873
26874 elseif environment.argument("all") then
26875 local result, status = resolvers.findfiles(filename)
26876 if status and environment.argument("detail") then
26877 runners.report_location(status)
26878 else
26879 runners.report_location(result)
26880 end
26881 else
26882 runners.report_location(resolvers.findgivenfile(filename))
26883
26884 end
26885 end
26886end
26887
26888function runners.locate_platform()
26889 runners.report_location(os.platform)
26890end
26891
26892function runners.report_location(result)
26893 if type(result) == "table" then
26894 for i=1,#result do
26895 if i > 1 then
26896 io.write("\n")
26897 end
26898 io.write(result[i])
26899 end
26900 else
26901 io.write(result)
26902 end
26903end
26904
26905function runners.edit_script(filename)
26906 local editor = os.getenv("MTXRUN_EDITOR") or os.getenv("TEXMFSTART_EDITOR") or os.getenv("EDITOR") or 'gvim'
26907 local rest = resolvers.resolve(filename)
26908 if rest ~= "" then
26909 local command = editor .. " " .. rest
26910 if e_verbose then
26911 report()
26912 report("starting editor: %s",command)
26913 report()
26914 report()
26915 end
26916 os.launch(command)
26917 end
26918end
26919
26920function runners.save_script_session(filename, list)
26921 if type(list) == "table" then
26922 local t = { }
26923 for i=1,#list do
26924 local key = list[i]
26925 t[key] = environment.arguments[key]
26926 end
26927 io.savedata(filename,table.serialize(t,true))
26928 end
26929end
26930
26931function runners.load_script_session(filename)
26932 if lfs.isfile(filename) then
26933 local t = io.loaddata(filename)
26934 if t then
26935 t = loadstring(t)
26936 if t then t = t() end
26937 for key, value in pairs(t) do
26938 environment.arguments[key] = value
26939 end
26940 end
26941 end
26942end
26943
26944function resolvers.launch(str)
26945
26946
26947 local launchers = runners.launchers[os.platform] if launchers then
26948 local suffix = file.suffix(str) if suffix then
26949 local runner = launchers[suffix] if runner then
26950 str = runner .. " " .. str
26951 end
26952 end
26953 end
26954 os.launch(str)
26955end
26956
26957function runners.launch_file(filename)
26958 local allresults = environment.arguments["all"]
26959 local pattern = environment.arguments["pattern"]
26960 local listonly = environment.arguments["list"]
26961 if not pattern or pattern == "" then
26962 pattern = filename
26963 end
26964 if not pattern or pattern == "" then
26965 report("provide name or --pattern=")
26966 else
26967 local t = resolvers.findfiles(pattern,nil,allresults)
26968 if not t or #t == 0 then
26969 t = resolvers.findfiles("*/" .. pattern,nil,allresults)
26970 end
26971 if not t or #t == 0 then
26972 t = resolvers.findfiles("*/" .. pattern .. "*",nil,allresults)
26973 end
26974 if t and #t > 0 then
26975 for i=1,#t do
26976 local name = t[i]
26977 if listonly then
26978 report("% 3i: %-30s %s",i,file.basename(name),file.dirname(name))
26979 else
26980 report("launching: %s",name)
26981 resolvers.launch(name)
26982 if not allresults then
26983 break
26984 end
26985 end
26986 end
26987 if listonly then
26988 io.write("\n")
26989 io.write("\n[select number]\n\n>> ")
26990 local answer = tonumber(io.read())
26991 if answer then
26992 io.write("\n")
26993 local name = t[answer]
26994 if name then
26995 report("launching: %s",name)
26996 resolvers.launch(name)
26997 else
26998 report("invalid number")
26999 end
27000 end
27001 end
27002 else
27003 report("no match for %s", pattern)
27004 end
27005 end
27006end
27007
27008local mtxprefixes = {
27009 { "^mtx%-", "mtx-" },
27010 { "^mtx%-t%-", "mtx-t-" },
27011}
27012
27013function runners.find_mtx_script(filename)
27014 local function found(name)
27015 local path = file.dirname(name)
27016 if path and path ~= "" then
27017 return false
27018 else
27019 local fullname = own and own.path and file.join(own.path,name)
27020 return io.exists(fullname) and fullname
27021 end
27022 end
27023 filename = file.addsuffix(filename,"lua")
27024 local basename = file.removesuffix(file.basename(filename))
27025 local suffix = file.suffix(filename)
27026
27027 local fullname = file.is_qualified_path(filename) and io.exists(filename) and filename
27028 if fullname and fullname ~= "" then
27029 return fullname
27030 end
27031
27032 fullname = "./" .. filename
27033 fullname = io.exists(fullname) and fullname
27034 if fullname and fullname ~= "" then
27035 return fullname
27036 end
27037
27038 for i=1,#mtxprefixes do
27039 local mtxprefix = mtxprefixes[i]
27040 mtxprefix = find(filename,mtxprefix[1]) and "" or mtxprefix[2]
27041
27042 fullname = mtxprefix .. filename
27043 fullname = found(fullname) or resolvers.findfile(fullname)
27044 if fullname and fullname ~= "" then
27045 return fullname
27046 end
27047
27048 fullname = mtxprefix .. basename .. "s" .. "." .. suffix
27049 fullname = found(fullname) or resolvers.findfile(fullname)
27050 if fullname and fullname ~= "" then
27051 return fullname
27052 end
27053
27054 fullname = mtxprefix .. gsub(basename,"s$","") .. "." .. suffix
27055 fullname = found(fullname) or resolvers.findfile(fullname)
27056 if fullname and fullname ~= "" then
27057 return fullname
27058 end
27059 end
27060
27061 fullname = resolvers.findfile(filename)
27062 return fullname
27063end
27064
27065function runners.register_arguments(...)
27066 local arguments = environment.arguments_after
27067 local passedon = { ... }
27068 for i=#passedon,1,-1 do
27069 local pi = passedon[i]
27070 if pi then
27071 table.insert(arguments,1,pi)
27072 end
27073 end
27074end
27075
27076function runners.execute_ctx_script(filename,...)
27077 runners.register_arguments(...)
27078 local arguments = environment.arguments_after
27079 local fullname = runners.find_mtx_script(filename) or ""
27080 if file.suffix(fullname) == "cld" then
27081
27082 report("running cld script: %s",filename)
27083 table.insert(arguments,1,fullname)
27084 table.insert(arguments,"--autopdf")
27085 fullname = runners.find_mtx_script("context") or ""
27086 end
27087
27088 if fullname == "" and environment.argument("autogenerate") then
27089 resolvers.renewcache()
27090 trackers.enable("resolvers.locating")
27091 resolvers.load()
27092
27093 fullname = runners.find_mtx_script(filename) or ""
27094 end
27095
27096 if fullname ~= "" then
27097 local state = runners.prepare()
27098 if state == 'error' then
27099 return false
27100 elseif state == 'skip' then
27101 return true
27102 elseif state == "run" then
27103
27104 arg = { } for _,v in pairs(arguments) do arg[#arg+1] = resolvers.resolve(v) end
27105 environment.initializearguments(arg)
27106 local loadname = environment.arguments['load']
27107 if loadname then
27108 if type(loadname) ~= "string" then loadname = file.basename(fullname) end
27109 loadname = file.replacesuffix(loadname,"cfg")
27110 runners.load_script_session(loadname)
27111 end
27112 filename = environment.files[1]
27113 if e_verbose then
27114 report("using script: %s (if --path is used don't run on path where mtxrun lives)\n",fullname)
27115 end
27116 environment.ownscript = fullname
27117 dofile(fullname)
27118 local savename = environment.arguments['save']
27119 if savename then
27120 if type(savename) ~= "string" then savename = file.basename(fullname) end
27121 savename = file.replacesuffix(savename,"cfg")
27122 runners.save_script_session(savename,save_list)
27123 end
27124 return true
27125 end
27126 else
27127 if filename == "" or filename == "help" then
27128 local context = resolvers.findfile("mtx-context.lua")
27129 trackers.enable("resolvers.locating")
27130 if context ~= "" then
27131 local result = dir.glob((gsub(context,"mtx%-context","mtx-*")))
27132 local valid = { }
27133 table.sort(result)
27134 for i=1,#result do
27135 local scriptname = result[i]
27136 local scriptbase = match(scriptname,".*mtx%-([^%-]-)%.lua")
27137 if scriptbase then
27138 local data = io.loaddata(scriptname)
27139 local application = match(data,"local application.-=.-(%{.-%})")
27140 if application then
27141 application = loadstring("return " .. application)
27142 if application then
27143 application = application()
27144 local banner = application.banner
27145 if banner then
27146 local description, version = match(banner,"^(.-) ([%d.]+)$")
27147 if description then
27148 valid[#valid+1] = { scriptbase, version, description }
27149 else
27150 valid[#valid+1] = { scriptbase, "", banner }
27151 end
27152 end
27153 end
27154 end
27155 end
27156 end
27157 if #valid > 0 then
27158 application.identify()
27159 report("no script name given, known scripts:")
27160 report()
27161 for k=1,#valid do
27162 local v = valid[k]
27163 report("%-12s %4s %s",v[1],v[2],v[3])
27164 end
27165 end
27166 else
27167 report("no script name given")
27168 end
27169 else
27170 filename = file.addsuffix(filename,"lua")
27171 if file.is_qualified_path(filename) then
27172 report("unknown script '%s'",filename)
27173 else
27174 report("unknown script '%s' or 'mtx-%s'",filename,filename)
27175 end
27176 end
27177 return false
27178 end
27179end
27180
27181function runners.prefixes()
27182 application.identify()
27183 report()
27184 report(concat(resolvers.allprefixes(true)," "))
27185end
27186
27187function runners.timedrun(filename)
27188 if filename and filename ~= "" then
27189 runners.timed(function() os.execute(filename) end)
27190 end
27191end
27192
27193function runners.timed(action)
27194 statistics.timed(action,true)
27195end
27196
27197function runners.associate(filename)
27198 os.launch(filename)
27199end
27200
27201function runners.evaluate(code,filename)
27202 local environment = table.setmetatableindex(_G)
27203 if code == "loop" then
27204 while true do
27205 io.write("lua > ")
27206 local code = io.read()
27207 if code == "quit" or code == "exit" then
27208 break
27209 elseif code ~= "" then
27210 local temp = string.match(code,"^= (.*)$")
27211 if temp then
27212 code = "inspect("..temp..")"
27213 end
27214 local compiled, message = load(code,"console","t",environment)
27215 if type(compiled) ~= "function" then
27216 compiled = load("inspect("..code..")","console","t",environment)
27217 end
27218 if type(compiled) ~= "function" then
27219 io.write("! " .. (message or code).."\n")
27220 else
27221 io.write(compiled())
27222 end
27223 end
27224 end
27225 else
27226 if type(code) ~= "string" or code == "" then
27227 code = filename
27228 end
27229 if code ~= "" then
27230 local compiled, message = load(code,"console","t",environment)
27231 if type(compiled) ~= "function" then
27232 compiled = load("inspect("..code..")","console","t",environment)
27233 end
27234 if type(compiled) ~= "function" then
27235 io.write("invalid lua code: " .. (message or code))
27236 return
27237 end
27238 io.write(compiled())
27239 end
27240 end
27241end
27242
27243function runners.gethelp(filename)
27244 local url = environment.argument("url")
27245 if url and url ~= "" then
27246 local command = string.gsub(environment.argument("command") or "unknown","^%s*\\*(.-)%s*$","%1")
27247 url = utilities.templates.replace(url,{ command = command })
27248 os.launch(url)
27249 else
27250 report("no --url given")
27251 end
27252end
27253
27254function runners.systeminfo()
27255 report("architecture : %s",os.platform or "<unset>")
27256 report("operating system : %s",os.name or "<unset>")
27257 report("file architecture : %s",os.type or "<unset>")
27258 report("binary path : %s",os.selfdir or "<unset>")
27259 report("binary suffix : %s",os.binsuffix or "<unset>")
27260 report("library suffix : %s",os.libsuffix or "<unset>")
27261end
27262
27263
27264
27265
27266
27267local filename = environment.files[1] or ""
27268local ok = true
27269
27270local before, after = environment.splitarguments(filename)
27271environment.arguments_before, environment.arguments_after = before, after
27272environment.initializearguments(before)
27273
27274e_verbose = environment.arguments["verbose"]
27275
27276if e_verbose then
27277 trackers.enable("resolvers.locating")
27278end
27279
27280
27281
27282local is_mkii_stub = runners.registered[file.removesuffix(file.basename(filename))]
27283
27284local e_argument = environment.argument
27285
27286if e_argument("timedlog") then
27287 logs.settimedlog()
27288end
27289
27290if e_argument("usekpse") or e_argument("forcekpse") or is_mkii_stub then
27291
27292 resolvers.load_tree(e_argument('tree'),true)
27293
27294 os.setenv("engine","")
27295 os.setenv("progname","")
27296
27297 local remapper = {
27298 otf = "opentype fonts",
27299 ttf = "truetype fonts",
27300 ttc = "truetype fonts",
27301 pfb = "type1 fonts",
27302 other = "other text files",
27303 }
27304
27305 local progname = e_argument("progname") or 'context'
27306
27307 local function kpse_initialized()
27308 texconfig.kpse_init = true
27309 local t = os.clock()
27310 local k = kpse.original.new("luatex",progname)
27311 local dummy = k:find_file("mtxrun.lua")
27312 report("kpse fallback with progname '%s' initialized in %s seconds",progname,os.clock()-t)
27313 kpse_initialized = function() return k end
27314 return k
27315 end
27316
27317 local findfile = resolvers.findfile
27318 local showpath = resolvers.showpath
27319
27320 if e_argument("forcekpse") then
27321
27322 function resolvers.findfile(name,kind)
27323 return (kpse_initialized():find_file(resolvers.cleanpath(name),(kind ~= "" and (remapper[kind] or kind)) or "tex") or "") or ""
27324 end
27325 function resolvers.showpath(name)
27326 return (kpse_initialized():show_path(name)) or ""
27327 end
27328
27329 elseif e_argument("usekpse") or is_mkii_stub then
27330
27331 resolvers.load()
27332
27333 function resolvers.findfile(name,kind)
27334 local found = findfile(name,kind) or ""
27335 if found ~= "" then
27336 return found
27337 else
27338 return (kpse_initialized():find_file(resolvers.cleanpath(name),(kind ~= "" and (remapper[kind] or kind)) or "tex") or "") or ""
27339 end
27340 end
27341 function resolvers.showpath(name)
27342 local found = showpath(name) or ""
27343 if found ~= "" then
27344 return found
27345 else
27346 return (kpse_initialized():show_path(name)) or ""
27347 end
27348 end
27349
27350 end
27351
27352 function runners.loadbase()
27353 end
27354
27355else
27356
27357 function runners.loadbase(...)
27358 if not resolvers.load(...) then
27359 report("forcing cache reload")
27360 resolvers.renewcache()
27361 trackers.enable("resolvers.locating")
27362 if not resolvers.load(...) then
27363 report("the resolver databases are not present or outdated")
27364 end
27365 end
27366 end
27367
27368 resolvers.load_tree(e_argument('tree'),e_argument("resolve"))
27369
27370end
27371
27372
27373
27374do
27375
27376 local a_locale = e_argument("locale")
27377
27378 if a_locale then
27379
27380
27381
27382
27383
27384
27385 report()
27386 report(what == "force" and "forcing locale:" or "original locale:")
27387 report()
27388 report(" collate : %s",status.lc_collate or "<unset>")
27389 report(" ctype : %s",status.lc_ctype or "<unset>")
27390 report(" monetary : %s",status.lc_monetary or "<unset>")
27391 report(" numeric : %s",status.lc_numeric or "<unset>")
27392 report(" time : %s",status.lc_time or "<unset>")
27393 report()
27394
27395 end
27396
27397 if a_locale == "force" then
27398 os.setlocale(status.lc_collate ,"collate")
27399 os.setlocale(status.lc_ctype ,"ctype")
27400 os.setlocale(status.lc_monetary,"monetary")
27401 os.setlocale(status.lc_numeric ,"numeric")
27402 os.setlocale(status.lc_time ,"time")
27403 else
27404 function os.setlocale()
27405 end
27406 end
27407
27408end
27409
27410
27411
27412
27413
27414
27415
27416
27417
27418
27419
27420
27421
27422if e_argument("script") or e_argument("scripts") then
27423
27424
27425
27426 if e_argument("nofiledatabase") then
27427
27428 else
27429 runners.loadbase()
27430 end
27431 if is_mkii_stub then
27432 ok = runners.execute_script(filename,false,true)
27433 else
27434 ok = runners.execute_ctx_script(filename)
27435 end
27436
27437elseif e_argument("evaluate") then
27438
27439 runners.evaluate(e_argument("evaluate"),filename)
27440
27441elseif e_argument("selfmerge") then
27442
27443
27444
27445 runners.loadbase()
27446 local found = locate_libs()
27447
27448 if found then
27449 local mtxrun = resolvers.findfile("mtxrun.lua")
27450 if lfs.isfile(mtxrun) then
27451 utilities.merger.selfmerge(mtxrun,own.libs,{ found })
27452 application.report("runner updated on resolved path: %s",mtxrun)
27453 else
27454 utilities.merger.selfmerge(own.name,own.libs,{ found })
27455 application.report("runner updated on relative path: %s",own.name)
27456 end
27457 end
27458
27459elseif e_argument("selfclean") then
27460
27461
27462
27463 runners.loadbase()
27464
27465 local mtxrun = resolvers.findfile("mtxrun.lua")
27466 if lfs.isfile(mtxrun) then
27467 utilities.merger.selfclean(mtxrun)
27468 application.report("runner cleaned on resolved path: %s",mtxrun)
27469 else
27470 utilities.merger.selfclean(own.name)
27471 application.report("runner cleaned on relative path: %s",own.name)
27472 end
27473
27474elseif e_argument("selfupdate") then
27475
27476 runners.loadbase()
27477 trackers.enable("resolvers.locating")
27478 resolvers.updatescript(own.name,"mtxrun")
27479
27480elseif e_argument("ctxlua") or e_argument("internal") then
27481
27482
27483
27484 runners.loadbase()
27485 ok = runners.execute_script(filename,true)
27486
27487elseif e_argument("execute") then
27488
27489
27490
27491 runners.loadbase()
27492 ok = runners.execute_script(filename)
27493
27494elseif e_argument("direct") then
27495
27496
27497
27498 runners.loadbase()
27499 ok = runners.execute_program(filename)
27500
27501elseif e_argument("edit") then
27502
27503
27504
27505 runners.loadbase()
27506 runners.edit_script(filename)
27507
27508elseif e_argument("launch") then
27509
27510 runners.loadbase()
27511 runners.launch_file(filename)
27512
27513elseif e_argument("associate") then
27514
27515 runners.associate(filename)
27516
27517elseif e_argument("gethelp") then
27518
27519 runners.gethelp()
27520
27521elseif e_argument("makestubs") then
27522
27523
27524
27525 runners.handle_stubs(true)
27526
27527elseif e_argument("removestubs") then
27528
27529
27530
27531 runners.loadbase()
27532 runners.handle_stubs(false)
27533
27534elseif e_argument("resolve") then
27535
27536
27537
27538 runners.loadbase()
27539 runners.resolve_string(filename)
27540
27541elseif e_argument("locate") then
27542
27543
27544
27545 runners.loadbase()
27546 runners.locate_file(filename)
27547
27548elseif e_argument("platform") or e_argument("show-platform") then
27549
27550
27551
27552 runners.loadbase()
27553 runners.locate_platform()
27554
27555elseif e_argument("prefixes") then
27556
27557 runners.loadbase()
27558 runners.prefixes()
27559
27560elseif e_argument("timedrun") then
27561
27562
27563
27564 runners.loadbase()
27565 runners.timedrun(filename)
27566
27567elseif e_argument("variables") or e_argument("show-variables") or e_argument("expansions") or e_argument("show-expansions") then
27568
27569
27570
27571 resolvers.load("nofiles")
27572 resolvers.listers.variables(e_argument("pattern"))
27573
27574elseif e_argument("configurations") or e_argument("show-configurations") then
27575
27576
27577
27578 resolvers.load("nofiles")
27579 resolvers.listers.configurations()
27580
27581elseif e_argument("find-file") then
27582
27583
27584
27585 resolvers.load()
27586 local e_all = e_argument("all")
27587 local e_pattern = e_argument("pattern")
27588 local e_format = e_argument("format")
27589 local finder = e_all and resolvers.findfiles or resolvers.findfile
27590 if not e_pattern then
27591 runners.register_arguments(filename)
27592 environment.initializearguments(environment.arguments_after)
27593 resolvers.dowithfilesandreport(finder,environment.files,e_format)
27594 elseif type(e_pattern) == "string" then
27595 resolvers.dowithfilesandreport(finder,{ e_pattern },e_format)
27596 end
27597
27598elseif e_argument("find-path") then
27599
27600
27601
27602 resolvers.load()
27603 local path = resolvers.findpath(filename)
27604 if e_verbose then
27605 report(path)
27606 else
27607 print(path)
27608 end
27609
27610elseif e_argument("expand-braces") then
27611
27612
27613
27614 resolvers.load("nofiles")
27615 runners.register_arguments(filename)
27616 environment.initializearguments(environment.arguments_after)
27617 resolvers.dowithfilesandreport(resolvers.expandbraces, environment.files)
27618
27619elseif e_argument("expand-path") then
27620
27621
27622
27623 resolvers.load("nofiles")
27624 runners.register_arguments(filename)
27625 environment.initializearguments(environment.arguments_after)
27626 resolvers.dowithfilesandreport(resolvers.expandpath, environment.files)
27627
27628elseif e_argument("resolve-path") then
27629
27630 resolvers.load("nofiles")
27631 runners.register_arguments(filename)
27632 environment.initializearguments(environment.arguments_after)
27633 resolvers.dowithfilesandreport(resolvers.cleanedpathlist, environment.files)
27634
27635elseif e_argument("expand-var") or e_argument("expand-variable") then
27636
27637
27638
27639 resolvers.load("nofiles")
27640 runners.register_arguments(filename)
27641 environment.initializearguments(environment.arguments_after)
27642 resolvers.dowithfilesandreport(resolvers.expansion, environment.files)
27643
27644elseif e_argument("show-path") or e_argument("path-value") then
27645
27646
27647
27648 resolvers.load("nofiles")
27649 runners.register_arguments(filename)
27650 environment.initializearguments(environment.arguments_after)
27651 resolvers.dowithfilesandreport(resolvers.showpath, environment.files)
27652
27653elseif e_argument("var-value") or e_argument("show-value") then
27654
27655
27656
27657 resolvers.load("nofiles")
27658 runners.register_arguments(filename)
27659 environment.initializearguments(environment.arguments_after)
27660 resolvers.dowithfilesandreport(resolvers.variable,environment.files)
27661
27662elseif e_argument("format-path") then
27663
27664
27665
27666 resolvers.load()
27667 report(caches.getwritablepath("format"))
27668
27669
27670
27671
27672
27673
27674
27675elseif e_argument("generate") then
27676
27677
27678
27679 if filename and filename ~= "" then
27680 resolvers.load("nofiles")
27681 trackers.enable("resolvers.locating")
27682 resolvers.renew(filename)
27683 else
27684 resolvers.renewcache()
27685 trackers.enable("resolvers.locating")
27686 resolvers.load()
27687 end
27688
27689 e_verbose = true
27690
27691elseif e_argument("make") or e_argument("ini") or e_argument("compile") then
27692
27693
27694
27695 resolvers.load()
27696 trackers.enable("resolvers.locating")
27697 environment.make_format(filename)
27698
27699elseif e_argument("run") then
27700
27701
27702
27703 runners.execute_ctx_script("mtx-base","--run",filename)
27704
27705elseif e_argument("fmt") then
27706
27707
27708
27709 runners.execute_ctx_script("mtx-base","--fmt",filename)
27710
27711elseif e_argument("help") and filename=='base' then
27712
27713
27714
27715 runners.execute_ctx_script("mtx-base","--help")
27716
27717elseif e_argument("version") then
27718
27719 application.version()
27720
27721 application.report("source path",environment.ownbin)
27722
27723elseif e_argument("directives") then
27724
27725 directives.show()
27726
27727elseif e_argument("trackers") then
27728
27729 trackers.show()
27730
27731elseif e_argument("experiments") then
27732
27733 experiments.show()
27734
27735elseif e_argument("exporthelp") then
27736
27737 runners.loadbase()
27738 application.export(e_argument("exporthelp"),filename)
27739
27740elseif e_argument("systeminfo") then
27741
27742 runners.systeminfo()
27743
27744elseif e_argument("locale") then
27745
27746
27747
27748elseif e_argument("help") or filename=='help' or filename == "" then
27749
27750 application.help()
27751
27752elseif find(filename,"^bin:") then
27753
27754 runners.loadbase()
27755 ok = runners.execute_program(filename)
27756
27757elseif is_mkii_stub then
27758
27759
27760
27761 runners.loadbase()
27762 ok = runners.execute_script(filename,false,true)
27763
27764elseif false then
27765
27766 runners.loadbase()
27767 ok = runners.execute_ctx_script(filename)
27768 if not ok then
27769 ok = runners.execute_script(filename)
27770 end
27771
27772elseif environment.files[1] == 'texmfcnf.lua' then
27773
27774 resolvers.load("nofiles")
27775 resolvers.listers.configurations()
27776
27777else
27778 runners.loadbase()
27779 runners.execute_ctx_script("mtx-base",filename)
27780
27781end
27782
27783if e_verbose then
27784 report()
27785 report("elapsed lua time: %0.3f seconds",os.runtime())
27786end
27787
27788if os.type ~= "windows" then
27789 texio.write("\n")
27790end
27791
27792if ok == false then ok = 1 elseif ok == true or ok == nil then ok = 0 end
27793
27794if lua and lua.getexitcode then
27795 ok = lua.getexitcode()
27796end
27797
27798
27799
27800os.exit(ok)
27801 |