mtxrun.lua /size: 685 Kb    last modification: 2024-01-16 09:02
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-- one can make a stub:
12
13-- mtxrun :
14--
15-- #!/bin/sh
16-- env LUATEXDIR=/....../texmf/scripts/context/lua luatex --luaonly mtxrun.lua "$@"
17
18-- mtxrun.cmd :
19--
20-- @luatex --luaonly %~d0%~p0mtxrun.lua %*
21
22-- filename : mtxrun.lua
23-- comment  : companion to context.tex
24-- author   : Hans Hagen, PRAGMA-ADE, Hasselt NL
25-- copyright: PRAGMA ADE / ConTeXt Development Team
26-- license  : see context related readme files
27
28-- This script is based on texmfstart.rb but does not use kpsewhich to locate files.
29-- Although kpse is a library it never came to opening up its interface to other
30-- programs (esp scripting languages) and so we do it ourselves. The lua variant
31-- evolved out of an experimental ruby one. Interesting is that using a scripting
32-- language instead of c does not have a speed penalty. Actually the lua variant is
33-- more efficient, especially when multiple calls to kpsewhich are involved. The lua
34-- library also gives way more control.
35
36-- When libraries used here are updates you can run
37--
38--   mtxrun --selfmerge
39--
40-- to update the embedded code. After that you might need to run
41--
42--   mtxrun --selfupdate
43--
44-- to copy the new script (from scripts/context/lua) to location where
45-- binaries are expected. If you want to remove the embedded code you can run
46--
47--   mtxxun --selfclean
48
49-- to be done / considered
50--
51-- support for --exec or make it default
52-- support for jar files (or maybe not, never used, too messy)
53-- support for $RUBYINPUTS cum suis (if still needed)
54-- remember for subruns: _CTX_K_V_#{original}_
55-- remember for subruns: _CTX_K_S_#{original}_
56-- remember for subruns: TEXMFSTART.#{original} [tex.rb texmfstart.rb]
57
58-- begin library merge
59
60
61
62do -- create closure to overcome 200 locals limit
63
64package.loaded["l-bit32"] = package.loaded["l-bit32"] or true
65
66-- original size: 3607, stripped down to: 3009
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 -- of closure
192
193do -- create closure to overcome 200 locals limit
194
195package.loaded["l-lua"] = package.loaded["l-lua"] or true
196
197-- original size: 6405, stripped down to: 2865
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 -- of closure
319
320do -- create closure to overcome 200 locals limit
321
322package.loaded["l-macro"] = package.loaded["l-macro"] or true
323
324-- original size: 10130, stripped down to: 5990
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 -- of closure
574
575do -- create closure to overcome 200 locals limit
576
577package.loaded["l-sandbox"] = package.loaded["l-sandbox"] or true
578
579-- original size: 9604, stripped down to: 6394
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 -- of closure
847
848do -- create closure to overcome 200 locals limit
849
850package.loaded["l-package"] = package.loaded["l-package"] or true
851
852-- original size: 12566, stripped down to: 8937
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 -- of closure
1199
1200do -- create closure to overcome 200 locals limit
1201
1202package.loaded["l-lpeg"] = package.loaded["l-lpeg"] or true
1203
1204-- original size: 38742, stripped down to: 19489
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 -- of closure
1983
1984do -- create closure to overcome 200 locals limit
1985
1986package.loaded["l-function"] = package.loaded["l-function"] or true
1987
1988-- original size: 361, stripped down to: 317
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 -- of closure
2002
2003do -- create closure to overcome 200 locals limit
2004
2005package.loaded["l-string"] = package.loaded["l-string"] or true
2006
2007-- original size: 6955, stripped down to: 3504
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 -- of closure
2139
2140do -- create closure to overcome 200 locals limit
2141
2142package.loaded["l-table"] = package.loaded["l-table"] or true
2143
2144-- original size: 42596, stripped down to: 23029
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 -- of closure
3307
3308do -- create closure to overcome 200 locals limit
3309
3310package.loaded["l-io"] = package.loaded["l-io"] or true
3311
3312-- original size: 11988, stripped down to: 6430
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 -- of closure
3662
3663do -- create closure to overcome 200 locals limit
3664
3665package.loaded["l-number"] = package.loaded["l-number"] or true
3666
3667-- original size: 4588, stripped down to: 2159
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 -- of closure
3789
3790do -- create closure to overcome 200 locals limit
3791
3792package.loaded["l-set"] = package.loaded["l-set"] or true
3793
3794-- original size: 1923, stripped down to: 1044
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 -- of closure
3862
3863do -- create closure to overcome 200 locals limit
3864
3865package.loaded["l-os"] = package.loaded["l-os"] or true
3866
3867-- original size: 20686, stripped down to: 10791
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 -- of closure
4314
4315do -- create closure to overcome 200 locals limit
4316
4317package.loaded["l-file"] = package.loaded["l-file"] or true
4318
4319-- original size: 22637, stripped down to: 10595
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 -- of closure
4746
4747do -- create closure to overcome 200 locals limit
4748
4749package.loaded["l-gzip"] = package.loaded["l-gzip"] or true
4750
4751-- original size: 268, stripped down to: 216
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 -- of closure
4762
4763do -- create closure to overcome 200 locals limit
4764
4765package.loaded["l-md5"] = package.loaded["l-md5"] or true
4766
4767-- original size: 3414, stripped down to: 2307
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 -- of closure
4856
4857do -- create closure to overcome 200 locals limit
4858
4859package.loaded["l-sha"] = package.loaded["l-sha"] or true
4860
4861-- original size: 1085, stripped down to: 969
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 -- of closure
4888
4889do -- create closure to overcome 200 locals limit
4890
4891package.loaded["l-url"] = package.loaded["l-url"] or true
4892
4893-- original size: 14713, stripped down to: 6981
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 -- of closure
5152
5153do -- create closure to overcome 200 locals limit
5154
5155package.loaded["l-dir"] = package.loaded["l-dir"] or true
5156
5157-- original size: 19139, stripped down to: 11345
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 -- of closure
5686
5687do -- create closure to overcome 200 locals limit
5688
5689package.loaded["l-boolean"] = package.loaded["l-boolean"] or true
5690
5691-- original size: 1850, stripped down to: 1498
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 -- of closure
5758
5759do -- create closure to overcome 200 locals limit
5760
5761package.loaded["l-unicode"] = package.loaded["l-unicode"] or true
5762
5763-- original size: 41303, stripped down to: 17277
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 -- of closure
6497
6498do -- create closure to overcome 200 locals limit
6499
6500package.loaded["l-math"] = package.loaded["l-math"] or true
6501
6502-- original size: 2679, stripped down to: 1909
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 -- of closure
6599
6600do -- create closure to overcome 200 locals limit
6601
6602package.loaded["util-str"] = package.loaded["util-str"] or true
6603
6604-- original size: 46912, stripped down to: 24470
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("<")/"&lt;"+P(">")/"&gt;"+P("&")/"&amp;"+P('"')/"&quot;"+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 -- of closure
7571
7572do -- create closure to overcome 200 locals limit
7573
7574package.loaded["util-tab"] = package.loaded["util-tab"] or true
7575
7576-- original size: 34148, stripped down to: 18419
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 -- of closure
8329
8330do -- create closure to overcome 200 locals limit
8331
8332package.loaded["util-fil"] = package.loaded["util-fil"] or true
8333
8334-- original size: 11474, stripped down to: 8973
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 -- of closure
8698
8699do -- create closure to overcome 200 locals limit
8700
8701package.loaded["util-sac"] = package.loaded["util-sac"] or true
8702
8703-- original size: 14107, stripped down to: 10453
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 -- of closure
9208
9209do -- create closure to overcome 200 locals limit
9210
9211package.loaded["util-sto"] = package.loaded["util-sto"] or true
9212
9213-- original size: 6661, stripped down to: 3074
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 -- of closure
9379
9380do -- create closure to overcome 200 locals limit
9381
9382package.loaded["util-prs"] = package.loaded["util-prs"] or true
9383
9384-- original size: 26298, stripped down to: 17137
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 -- of closure
9993
9994do -- create closure to overcome 200 locals limit
9995
9996package.loaded["util-fmt"] = package.loaded["util-fmt"] or true
9997
9998-- original size: 3379, stripped down to: 2273
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 -- of closure
10106
10107do -- create closure to overcome 200 locals limit
10108
10109package.loaded["util-soc-imp-reset"] = package.loaded["util-soc-imp-reset"] or true
10110
10111-- original size: 374, stripped down to: 282
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 -- of closure
10128
10129do -- create closure to overcome 200 locals limit
10130
10131package.loaded["util-soc-imp-socket"] = package.loaded["util-soc-imp-socket"] or true
10132
10133-- original size: 4905, stripped down to: 3562
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 -- of closure
10304
10305do -- create closure to overcome 200 locals limit
10306
10307package.loaded["util-soc-imp-copas"] = package.loaded["util-soc-imp-copas"] or true
10308
10309-- original size: 26186, stripped down to: 14893
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 -- of closure
10997
10998do -- create closure to overcome 200 locals limit
10999
11000package.loaded["util-soc-imp-ltn12"] = package.loaded["util-soc-imp-ltn12"] or true
11001
11002-- original size: 8709, stripped down to: 5411
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 -- of closure
11322
11323do -- create closure to overcome 200 locals limit
11324
11325package.loaded["util-soc-imp-mime"] = package.loaded["util-soc-imp-mime"] or true
11326
11327-- original size: 2373, stripped down to: 1931
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 -- of closure
11413
11414do -- create closure to overcome 200 locals limit
11415
11416package.loaded["util-soc-imp-url"] = package.loaded["util-soc-imp-url"] or true
11417
11418-- original size: 6863, stripped down to: 5269
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 -- of closure
11671
11672do -- create closure to overcome 200 locals limit
11673
11674package.loaded["util-soc-imp-headers"] = package.loaded["util-soc-imp-headers"] or true
11675
11676-- original size: 5721, stripped down to: 3754
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 -- of closure
11814
11815do -- create closure to overcome 200 locals limit
11816
11817package.loaded["util-soc-imp-tp"] = package.loaded["util-soc-imp-tp"] or true
11818
11819-- original size: 3116, stripped down to: 2533
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 -- of closure
11943
11944do -- create closure to overcome 200 locals limit
11945
11946package.loaded["util-soc-imp-http"] = package.loaded["util-soc-imp-http"] or true
11947
11948-- original size: 12772, stripped down to: 9619
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 -- of closure
12326
12327do -- create closure to overcome 200 locals limit
12328
12329package.loaded["util-soc-imp-ftp"] = package.loaded["util-soc-imp-ftp"] or true
12330
12331-- original size: 10357, stripped down to: 8548
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 -- of closure
12698
12699do -- create closure to overcome 200 locals limit
12700
12701package.loaded["util-soc-imp-smtp"] = package.loaded["util-soc-imp-smtp"] or true
12702
12703-- original size: 7018, stripped down to: 5883
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 -- of closure
12938
12939do -- create closure to overcome 200 locals limit
12940
12941package.loaded["trac-set"] = package.loaded["trac-set"] or true
12942
12943-- original size: 14568, stripped down to: 9644
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 -- of closure
13308
13309do -- create closure to overcome 200 locals limit
13310
13311package.loaded["trac-log"] = package.loaded["trac-log"] or true
13312
13313-- original size: 16040, stripped down to: 11066
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["%-15s | %s"],
13384    report_nop=formatters["%-15s |"],
13385    subreport_yes=formatters["%-15s | %s | %s"],
13386    subreport_nop=formatters["%-15s | %s |"],
13387    status_yes=formatters["%-15s : %s\n"],
13388    status_nop=formatters["%-15s :\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 -- of closure
13787
13788do -- create closure to overcome 200 locals limit
13789
13790package.loaded["trac-inf"] = package.loaded["trac-inf"] or true
13791
13792-- original size: 9740, stripped down to: 7296
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 -- of closure
14051
14052do -- create closure to overcome 200 locals limit
14053
14054package.loaded["trac-pro"] = package.loaded["trac-pro"] or true
14055
14056-- original size: 5841, stripped down to: 3352
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 -- of closure
14198
14199do -- create closure to overcome 200 locals limit
14200
14201package.loaded["util-lua"] = package.loaded["util-lua"] or true
14202
14203-- original size: 7166, stripped down to: 5009
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 -- of closure
14377
14378do -- create closure to overcome 200 locals limit
14379
14380package.loaded["util-deb"] = package.loaded["util-deb"] or true
14381
14382-- original size: 10593, stripped down to: 7102
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 -- of closure
14695
14696do -- create closure to overcome 200 locals limit
14697
14698package.loaded["util-tpl"] = package.loaded["util-tpl"] or true
14699
14700-- original size: 7722, stripped down to: 4212
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 -- of closure
14848
14849do -- create closure to overcome 200 locals limit
14850
14851package.loaded["util-sbx"] = package.loaded["util-sbx"] or true
14852
14853-- original size: 21146, stripped down to: 13272
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 -- of closure
15363
15364do -- create closure to overcome 200 locals limit
15365
15366package.loaded["util-mrg"] = package.loaded["util-mrg"] or true
15367
15368-- original size: 7819, stripped down to: 5881
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 -- of closure
15541
15542do -- create closure to overcome 200 locals limit
15543
15544package.loaded["util-env"] = package.loaded["util-env"] or true
15545
15546-- original size: 9750, stripped down to: 5538
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 -- of closure
15757
15758do -- create closure to overcome 200 locals limit
15759
15760package.loaded["luat-env"] = package.loaded["luat-env"] or true
15761
15762-- original size: 6293, stripped down to: 4141
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 -- of closure
15922
15923do -- create closure to overcome 200 locals limit
15924
15925package.loaded["util-zip"] = package.loaded["util-zip"] or true
15926
15927-- original size: 33051, stripped down to: 16324
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 -- of closure
16549
16550do -- create closure to overcome 200 locals limit
16551
16552package.loaded["lxml-tab"] = package.loaded["lxml-tab"] or true
16553
16554-- original size: 62425, stripped down to: 36404
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]="&amp;",
16889  [42]="&quot;",
16890  [47]="&apos;",
16891  [74]="&lt;",
16892  [76]="&gt;",
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  [ [[&]] ]="&amp;",
16904  [ [["]] ]="&quot;",
16905  [ [[']] ]="&apos;",
16906  [ [[<]] ]="&lt;",
16907  [ [[>]] ]="&gt;",
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 -- of closure
18035
18036do -- create closure to overcome 200 locals limit
18037
18038package.loaded["lxml-lpt"] = package.loaded["lxml-lpt"] or true
18039
18040-- original size: 54738, stripped down to: 31395
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 -- of closure
19288
19289do -- create closure to overcome 200 locals limit
19290
19291package.loaded["lxml-mis"] = package.loaded["lxml-mis"] or true
19292
19293-- original size: 3542, stripped down to: 1808
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("<")/"&lt;"+P(">")/"&gt;"+P("&")/"&amp;"
19334local escaped=Cs(normal*(special*normal)^0)
19335local normal=(1-S"&")^0
19336local special=P("&lt;")/"<"+P("&gt;")/">"+P("&amp;")/"&"
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 -- of closure
19357
19358do -- create closure to overcome 200 locals limit
19359
19360package.loaded["lxml-aux"] = package.loaded["lxml-aux"] or true
19361
19362-- original size: 34522, stripped down to: 21511
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 -- of closure
20352
20353do -- create closure to overcome 200 locals limit
20354
20355package.loaded["lxml-xml"] = package.loaded["lxml-xml"] or true
20356
20357-- original size: 11096, stripped down to: 7702
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 -- of closure
20755
20756do -- create closure to overcome 200 locals limit
20757
20758package.loaded["trac-xml"] = package.loaded["trac-xml"] or true
20759
20760-- original size: 6407, stripped down to: 4640
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 -- of closure
20926
20927do -- create closure to overcome 200 locals limit
20928
20929package.loaded["data-ini"] = package.loaded["data-ini"] or true
20930
20931-- original size: 10636, stripped down to: 7049
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 -- of closure
21169
21170do -- create closure to overcome 200 locals limit
21171
21172package.loaded["data-exp"] = package.loaded["data-exp"] or true
21173
21174-- original size: 18179, stripped down to: 10432
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 -- of closure
21577
21578do -- create closure to overcome 200 locals limit
21579
21580package.loaded["data-env"] = package.loaded["data-env"] or true
21581
21582-- original size: 9758, stripped down to: 6523
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 -- of closure
21867
21868do -- create closure to overcome 200 locals limit
21869
21870package.loaded["data-tmp"] = package.loaded["data-tmp"] or true
21871
21872-- original size: 16433, stripped down to: 11636
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 -- of closure
22266
22267do -- create closure to overcome 200 locals limit
22268
22269package.loaded["data-met"] = package.loaded["data-met"] or true
22270
22271-- original size: 5518, stripped down to: 3854
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 -- of closure
22402
22403do -- create closure to overcome 200 locals limit
22404
22405package.loaded["data-res"] = package.loaded["data-res"] or true
22406
22407-- original size: 70775, stripped down to: 44854
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 -- of closure
24070
24071do -- create closure to overcome 200 locals limit
24072
24073package.loaded["data-pre"] = package.loaded["data-pre"] or true
24074
24075-- original size: 5872, stripped down to: 3691
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 -- of closure
24226
24227do -- create closure to overcome 200 locals limit
24228
24229package.loaded["data-inp"] = package.loaded["data-inp"] or true
24230
24231-- original size: 1050, stripped down to: 946
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 -- of closure
24259
24260do -- create closure to overcome 200 locals limit
24261
24262package.loaded["data-out"] = package.loaded["data-out"] or true
24263
24264-- original size: 682, stripped down to: 579
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 -- of closure
24285
24286do -- create closure to overcome 200 locals limit
24287
24288package.loaded["data-fil"] = package.loaded["data-fil"] or true
24289
24290-- original size: 4365, stripped down to: 3452
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 -- of closure
24414
24415do -- create closure to overcome 200 locals limit
24416
24417package.loaded["data-con"] = package.loaded["data-con"] or true
24418
24419-- original size: 5477, stripped down to: 3757
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 -- of closure
24544
24545do -- create closure to overcome 200 locals limit
24546
24547package.loaded["data-use"] = package.loaded["data-use"] or true
24548
24549-- original size: 5806, stripped down to: 2925
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 -- of closure
24632
24633do -- create closure to overcome 200 locals limit
24634
24635package.loaded["data-zip"] = package.loaded["data-zip"] or true
24636
24637-- original size: 10789, stripped down to: 7951
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 -- of closure
24941
24942do -- create closure to overcome 200 locals limit
24943
24944package.loaded["data-tre"] = package.loaded["data-tre"] or true
24945
24946-- original size: 10802, stripped down to: 6619
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 -- of closure
25207
25208do -- create closure to overcome 200 locals limit
25209
25210package.loaded["data-sch"] = package.loaded["data-sch"] or true
25211
25212-- original size: 6945, stripped down to: 5408
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 -- of closure
25405
25406do -- create closure to overcome 200 locals limit
25407
25408package.loaded["data-lua"] = package.loaded["data-lua"] or true
25409
25410-- original size: 4227, stripped down to: 3049
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 -- of closure
25513
25514do -- create closure to overcome 200 locals limit
25515
25516package.loaded["data-aux"] = package.loaded["data-aux"] or true
25517
25518-- original size: 2610, stripped down to: 2019
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 -- of closure
25584
25585do -- create closure to overcome 200 locals limit
25586
25587package.loaded["data-tmf"] = package.loaded["data-tmf"] or true
25588
25589-- original size: 2601, stripped down to: 1549
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 -- of closure
25640
25641do -- create closure to overcome 200 locals limit
25642
25643package.loaded["data-lst"] = package.loaded["data-lst"] or true
25644
25645-- original size: 2038, stripped down to: 1696
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 -- of closure
25705
25706do -- create closure to overcome 200 locals limit
25707
25708package.loaded["libs-ini"] = package.loaded["libs-ini"] or true
25709
25710-- original size: 6524, stripped down to: 4064
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 -- of closure
25870
25871do -- create closure to overcome 200 locals limit
25872
25873package.loaded["luat-sta"] = package.loaded["luat-sta"] or true
25874
25875-- original size: 5703, stripped down to: 2321
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 -- of closure
25973
25974do -- create closure to overcome 200 locals limit
25975
25976package.loaded["luat-fmt"] = package.loaded["luat-fmt"] or true
25977
25978-- original size: 12056, stripped down to: 8398
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 -- of closure
26262
26263-- used libraries    : l-bit32.lua l-lua.lua l-macro.lua l-sandbox.lua l-package.lua l-lpeg.lua l-function.lua l-string.lua l-table.lua l-io.lua l-number.lua l-set.lua l-os.lua l-file.lua l-gzip.lua l-md5.lua l-sha.lua l-url.lua l-dir.lua l-boolean.lua l-unicode.lua l-math.lua util-str.lua util-tab.lua util-fil.lua util-sac.lua util-sto.lua util-prs.lua util-fmt.lua util-soc-imp-reset.lua util-soc-imp-socket.lua util-soc-imp-copas.lua util-soc-imp-ltn12.lua util-soc-imp-mime.lua util-soc-imp-url.lua util-soc-imp-headers.lua util-soc-imp-tp.lua util-soc-imp-http.lua util-soc-imp-ftp.lua util-soc-imp-smtp.lua trac-set.lua trac-log.lua trac-inf.lua trac-pro.lua util-lua.lua util-deb.lua util-tpl.lua util-sbx.lua util-mrg.lua util-env.lua luat-env.lua util-zip.lua lxml-tab.lua lxml-lpt.lua lxml-mis.lua lxml-aux.lua lxml-xml.lua trac-xml.lua data-ini.lua data-exp.lua data-env.lua data-tmp.lua data-met.lua data-res.lua data-pre.lua data-inp.lua data-out.lua data-fil.lua data-con.lua data-use.lua data-zip.lua data-tre.lua data-sch.lua data-lua.lua data-aux.lua data-tmf.lua data-lst.lua libs-ini.lua luat-sta.lua luat-fmt.lua
26264-- skipped libraries : -
26265-- original bytes    : 1049917
26266-- stripped bytes    : 417309
26267
26268-- end library merge
26269
26270-- We need this hack till luatex is fixed.
26271--
26272-- for k,v in pairs(arg) do print(k,v) end
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-- End of hack.
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 = { -- order can be made better
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', -- code might move to l-string
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 -- 'util-soc-imp-mbox.lua',
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', -- was before trac-set
26336    'trac-pro.lua', -- not really needed
26337    'util-lua.lua', -- indeed here?
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', -- can come before inf (as in mkiv)
26346
26347    'util-zip.lua',
26348
26349    'lxml-tab.lua',
26350    'lxml-lpt.lua',
26351 -- 'lxml-ent.lua',
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--  'data-tex.lua',
26371--  'data-bin.lua',
26372    'data-zip.lua',
26373    'data-tre.lua',
26374    'data-sch.lua',
26375    'data-lua.lua',
26376    'data-aux.lua', -- updater
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-- c:/data/develop/tex-context/tex/texmf-win64/bin/../../texmf-context/tex/context/base/mkiv/data-tmf.lua
26388-- c:/data/develop/context/sources/data-tmf.lua
26389
26390local ownlist = {
26391 -- '.',
26392 -- ownpath ,
26393    owntree .. "/../../../../context/sources", -- HH's development path
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" -- in case l-* does a require
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-- verbosity
26464
26465----- e_verbose = environment.arguments["verbose"]
26466
26467local e_verbose = false
26468
26469-- some common flags (also passed through environment)
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 { } -- for the moment
26610
26611runners = runners  or { } -- global (might become local)
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 },  -- context mkii runner (only tool not to be luafied)
26627    texutil      = { 'texutil.rb',      true  },  -- old perl based index sorter for mkii (old versions need it)
26628    texfont      = { 'texfont.pl',      true  },  -- perl script that makes mkii font metric files
26629    texfind      = { 'texfind.pl',      false },  -- perltk based tex searching tool, mostly used at pragma
26630    texshow      = { 'texshow.pl',      false },  -- perltk based context help system, will be luafied
26631 -- texwork      = { 'texwork.pl',      false },  -- perltk based editing environment, only used at pragma
26632    makempy      = { 'makempy.pl',      true  },
26633    mptopdf      = { 'mptopdf.pl',      true  },
26634    pstopdf      = { 'pstopdf.rb',      true  },  -- converts ps (and some more) images, does some cleaning (replaced)
26635 -- examplex     = { 'examplex.rb',     false },
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 -- exatools     = { 'exatools.rb',     true  },
26645    xmltools     = { 'xmltools.rb',     true  },
26646 -- luatools     = { 'luatools.lua',    true  },
26647    mtxtools     = { 'mtxtools.rb',     true  },
26648    pdftrimwhite = { 'pdftrimwhite.pl', false },
26649}
26650
26651runners.launchers = {
26652    windows = { },
26653    unix    = { },
26654}
26655
26656-- like runners.libpath("framework"): looks on script's subpath
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                    -- loop over known suffixes
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                    -- maybe look on path
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) -- already done
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,";")) -- for running texexec etc (after tl change to texmf-dist)
26753end
26754                    local binary = runners.applications[file.suffix(result)]
26755                    result = string.quoted(string.unquoted(result))
26756                 -- if string.match(result,' ') and not string.match(result,"^\".*\"$") then
26757                 --     result = '"' .. result .. '"'
26758                 -- end
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-- the --usekpse flag will fallback (not default) on kpse (hm, we can better update mtx-stubs)
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 '.' -- 'auto' no longer subpathssupported
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-- differs from texmfstart where locate appends .com .exe .bat ... todo
26868
26869function runners.locate_file(filename) -- was given file but only searches in tree
26870    if filename and filename ~= "" then
26871        if environment.argument("first") then
26872            runners.report_location(resolvers.findfile(filename))
26873         -- resolvers.dowithfilesandreport(resolvers.findfile,filename)
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         -- resolvers.dowithfilesandreport(resolvers.findgivenfile,filename)
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) -- we assume that gvim is present on most systems (todo: also in cnf file)
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    -- maybe we also need to test on mtxrun.launcher.suffix environment
26946    -- variable or on windows consult the assoc and ftype vars and such
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    -- qualified path, raw name
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    -- current path, raw name
27032    fullname = "./" .. filename
27033    fullname = io.exists(fullname) and fullname
27034    if fullname and fullname ~= "" then
27035        return fullname
27036    end
27037    -- mtx- prefix checking
27038    for i=1,#mtxprefixes do
27039        local mtxprefix = mtxprefixes[i]
27040        mtxprefix = find(filename,mtxprefix[1]) and "" or mtxprefix[2]
27041        -- context namespace, mtx-<filename>
27042        fullname = mtxprefix .. filename
27043        fullname = found(fullname) or resolvers.findfile(fullname)
27044        if fullname and fullname ~= "" then
27045            return fullname
27046        end
27047        -- context namespace, mtx-<filename>s
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        -- context namespace, mtx-<filename minus trailing s>
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    -- context namespace, just <filename>
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        -- handy in editors where we force --autopdf
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    -- retry after generate but only if --autogenerate
27088    if fullname == "" and environment.argument("autogenerate") then -- might become the default
27089        resolvers.renewcache()
27090        trackers.enable("resolvers.locating")
27091        resolvers.load()
27092        --
27093        fullname = runners.find_mtx_script(filename) or ""
27094    end
27095    -- that should do it
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            -- load and save ... kind of undocumented
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-*"))) -- () needed
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) -- just for me
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) -- for Luigi
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-- this is a bit dirty ... first we store the first filename and next we
27264-- split the arguments so that we only see the ones meant for this script
27265-- ... later we will use the second half
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"] -- delayed till here (we need the ones before script)
27275
27276if e_verbose then
27277    trackers.enable("resolvers.locating")
27278end
27279
27280-- maybe the unset has to go to this level
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) -- force resolve of TEXMFCNF
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") -- so that we're initialized
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-- joke .. reminds me of messing with gigi terminals
27373
27374do
27375
27376    local a_locale = e_argument("locale")
27377
27378    if a_locale then
27379
27380        -- I really hate this crap but am too tired of discussing it over and over
27381        -- again so for the sake of usiage outside context we will provide ways to
27382        -- use locales in an otherwise supposed to be locale agnostic system. And
27383        -- forget about support in case of interferences.
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-- if e_argument("ansi") or e_argument("ansilog") then
27411
27412--     logs.setformatters(e_argument("ansi") and "ansi" or "ansilog")
27413
27414--  -- local script = e_argument("script") or e_argument("scripts")
27415--  --
27416--  -- if type(script) == "string" then
27417--  --     logs.writer("]0;"..script.."") -- for Alan to test
27418--  -- end
27419
27420-- end
27421
27422if e_argument("script") or e_argument("scripts") then
27423
27424    -- run a script by loading it (using libs), pass args
27425
27426    if e_argument("nofiledatabase") then
27427        -- handy for mtx-update
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    -- embed used libraries
27444
27445    runners.loadbase()
27446    local found = locate_libs()
27447
27448    if found then
27449        local mtxrun = resolvers.findfile("mtxrun.lua") -- includes local name
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    -- remove embedded libraries
27462
27463    runners.loadbase()
27464
27465    local mtxrun = resolvers.findfile("mtxrun.lua") -- includes local name
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    -- run a script by loading it (using libs)
27483
27484    runners.loadbase()
27485    ok = runners.execute_script(filename,true)
27486
27487elseif e_argument("execute") then
27488
27489    -- execute script
27490
27491    runners.loadbase()
27492    ok = runners.execute_script(filename)
27493
27494elseif e_argument("direct") then
27495
27496    -- equals bin:
27497
27498    runners.loadbase()
27499    ok = runners.execute_program(filename)
27500
27501elseif e_argument("edit") then
27502
27503    -- edit file
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    -- make stubs (depricated)
27524
27525    runners.handle_stubs(true)
27526
27527elseif e_argument("removestubs") then
27528
27529    -- remove stub (depricated)
27530
27531    runners.loadbase()
27532    runners.handle_stubs(false)
27533
27534elseif e_argument("resolve") then
27535
27536    -- resolve string
27537
27538    runners.loadbase()
27539    runners.resolve_string(filename)
27540
27541elseif e_argument("locate") then
27542
27543    -- locate file (only database)
27544
27545    runners.loadbase()
27546    runners.locate_file(filename)
27547
27548elseif e_argument("platform") or e_argument("show-platform") then
27549
27550    -- locate platform
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    -- locate platform
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    -- luatools: runners.execute_ctx_script("mtx-base","--expansions",filename)
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    -- luatools: runners.execute_ctx_script("mtx-base","--configurations",filename)
27577
27578    resolvers.load("nofiles")
27579    resolvers.listers.configurations()
27580
27581elseif e_argument("find-file") then
27582
27583    -- luatools: runners.execute_ctx_script("mtx-base","--find-file",filename)
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    -- luatools: runners.execute_ctx_script("mtx-base","--find-path",filename)
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    -- luatools: runners.execute_ctx_script("mtx-base","--expand-braces",filename)
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    -- luatools: runners.execute_ctx_script("mtx-base","--expand-path",filename)
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    -- luatools: runners.execute_ctx_script("mtx-base","--expand-var",filename)
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    -- luatools: runners.execute_ctx_script("mtx-base","--show-path",filename)
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    -- luatools: runners.execute_ctx_script("mtx-base","--show-value",filename)
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    -- luatools: runners.execute_ctx_script("mtx-base","--format-path",filename)
27665
27666    resolvers.load()
27667    report(caches.getwritablepath("format"))
27668
27669-- elseif e_argument("pattern") then
27670--
27671--     -- luatools
27672--
27673--     runners.execute_ctx_script("mtx-base","--pattern='" .. e_argument("pattern") .. "'",filename)
27674
27675elseif e_argument("generate") then
27676
27677    -- luatools
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    -- luatools: runners.execute_ctx_script("mtx-base","--make",filename)
27694
27695    resolvers.load()
27696    trackers.enable("resolvers.locating")
27697    environment.make_format(filename)
27698
27699elseif e_argument("run") then
27700
27701    -- luatools
27702
27703    runners.execute_ctx_script("mtx-base","--run",filename)
27704
27705elseif e_argument("fmt") then
27706
27707    -- luatools
27708
27709    runners.execute_ctx_script("mtx-base","--fmt",filename)
27710
27711elseif e_argument("help") and filename=='base' then
27712
27713    -- luatools
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    -- already done
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    -- execute mkii script
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 -- so that we don't need to load mtx-base
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") -- is this still valid?
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-- os.exit(ok,true) -- true forces a cleanup in 5.2+
27799
27800os.exit(ok)         -- true forces a cleanup in 5.2+ but reports a wrong number then
27801