
| Current Path : /bin/ |
Linux ift1.ift-informatik.de 5.4.0-216-generic #236-Ubuntu SMP Fri Apr 11 19:53:21 UTC 2025 x86_64 |
| Current File : //bin/mtxrun |
#!/usr/bin/env texlua
if not modules then modules = { } end modules ['mtxrun'] = {
version = 1.001,
comment = "runner, lua replacement for texmfstart.rb",
author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright = "PRAGMA ADE / ConTeXt Development Team",
license = "see context related readme files"
}
-- one can make a stub:
-- mtxrun :
--
-- #!/bin/sh
-- env LUATEXDIR=/....../texmf/scripts/context/lua luatex --luaonly mtxrun.lua "$@"
-- mtxrun.cmd :
--
-- @luatex --luaonly %~d0%~p0mtxrun.lua %*
-- filename : mtxrun.lua
-- comment : companion to context.tex
-- author : Hans Hagen, PRAGMA-ADE, Hasselt NL
-- copyright: PRAGMA ADE / ConTeXt Development Team
-- license : see context related readme files
-- This script is based on texmfstart.rb but does not use kpsewhich to locate files.
-- Although kpse is a library it never came to opening up its interface to other
-- programs (esp scripting languages) and so we do it ourselves. The lua variant
-- evolved out of an experimental ruby one. Interesting is that using a scripting
-- language instead of c does not have a speed penalty. Actually the lua variant is
-- more efficient, especially when multiple calls to kpsewhich are involved. The lua
-- library also gives way more control.
-- When libraries used here are updates you can run
--
-- mtxrun --selfmerge
--
-- to update the embedded code. After that you might need to run
--
-- mtxrun --selfupdate
--
-- to copy the new script (from scripts/context/lua) to location where
-- binaries are expected. If you want to remove the embedded code you can run
--
-- mtxxun --selfclean
-- to be done / considered
--
-- support for --exec or make it default
-- support for jar files (or maybe not, never used, too messy)
-- support for $RUBYINPUTS cum suis (if still needed)
-- remember for subruns: _CTX_K_V_#{original}_
-- remember for subruns: _CTX_K_S_#{original}_
-- remember for subruns: TEXMFSTART.#{original} [tex.rb texmfstart.rb]
-- begin library merge
do -- create closure to overcome 200 locals limit
package.loaded["l-bit32"] = package.loaded["l-bit32"] or true
-- original size: 3607, stripped down to: 3009
if not modules then modules={} end modules ['l-bit32']={
version=1.001,
license="the same as regular Lua",
source="bitwise.lua, v 1.24 2014/12/26 17:20:53 roberto",
comment="drop-in for bit32, adapted a bit by Hans Hagen",
}
if bit32 then
elseif utf8 then
load ([[
local select = select -- instead of: arg = { ... }
bit32 = {
bnot = function (a)
return ~a & 0xFFFFFFFF
end,
band = function (x, y, z, ...)
if not z then
return ((x or -1) & (y or -1)) & 0xFFFFFFFF
else
local res = x & y & z
for i=1,select("#",...) do
res = res & select(i,...)
end
return res & 0xFFFFFFFF
end
end,
bor = function (x, y, z, ...)
if not z then
return ((x or 0) | (y or 0)) & 0xFFFFFFFF
else
local res = x | y | z
for i=1,select("#",...) do
res = res | select(i,...)
end
return res & 0xFFFFFFFF
end
end,
bxor = function (x, y, z, ...)
if not z then
return ((x or 0) ~ (y or 0)) & 0xFFFFFFFF
else
local res = x ~ y ~ z
for i=1,select("#",...) do
res = res ~ select(i,...)
end
return res & 0xFFFFFFFF
end
end,
btest = function (x, y, z, ...)
if not z then
return (((x or -1) & (y or -1)) & 0xFFFFFFFF) ~= 0
else
local res = x & y & z
for i=1,select("#",...) do
res = res & select(i,...)
end
return (res & 0xFFFFFFFF) ~= 0
end
end,
lshift = function (a, b)
return ((a & 0xFFFFFFFF) << b) & 0xFFFFFFFF
end,
rshift = function (a, b)
return ((a & 0xFFFFFFFF) >> b) & 0xFFFFFFFF
end,
arshift = function (a, b)
a = a & 0xFFFFFFFF
if b <= 0 or (a & 0x80000000) == 0 then
return (a >> b) & 0xFFFFFFFF
else
return ((a >> b) | ~(0xFFFFFFFF >> b)) & 0xFFFFFFFF
end
end,
lrotate = function (a ,b)
b = b & 31
a = a & 0xFFFFFFFF
a = (a << b) | (a >> (32 - b))
return a & 0xFFFFFFFF
end,
rrotate = function (a, b)
b = -b & 31
a = a & 0xFFFFFFFF
a = (a << b) | (a >> (32 - b))
return a & 0xFFFFFFFF
end,
extract = function (a, f, w)
return (a >> f) & ~(-1 << (w or 1))
end,
replace = function (a, v, f, w)
local mask = ~(-1 << (w or 1))
return ((a & ~(mask << f)) | ((v & mask) << f)) & 0xFFFFFFFF
end,
}
]] ) ()
elseif bit then
load ([[
local band, bnot, rshift, lshift = bit.band, bit.bnot, bit.rshift, bit.lshift
bit32 = {
arshift = bit.arshift,
band = band,
bnot = bnot,
bor = bit.bor,
bxor = bit.bxor,
btest = function(...)
return band(...) ~= 0
end,
extract = function(a,f,w)
return band(rshift(a,f),2^(w or 1)-1)
end,
lrotate = bit.rol,
lshift = lshift,
replace = function(a,v,f,w)
local mask = 2^(w or 1)-1
return band(a,bnot(lshift(mask,f)))+lshift(band(v,mask),f)
end,
rrotate = bit.ror,
rshift = rshift,
}
]] ) ()
else
xpcall(function() local _,t=require("bit32") if t then bit32=t end return end,function() end)
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["l-lua"] = package.loaded["l-lua"] or true
-- original size: 6330, stripped down to: 2831
if not modules then modules={} end modules ['l-lua']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local next,type,tonumber=next,type,tonumber
LUAMAJORVERSION,LUAMINORVERSION=string.match(_VERSION,"^[^%d]+(%d+)%.(%d+).*$")
LUAMAJORVERSION=tonumber(LUAMAJORVERSION) or 5
LUAMINORVERSION=tonumber(LUAMINORVERSION) or 1
LUAVERSION=LUAMAJORVERSION+LUAMINORVERSION/10
if LUAVERSION<5.2 and jit then
MINORVERSION=2
LUAVERSION=5.2
end
if not lpeg then
lpeg=require("lpeg")
end
if loadstring then
local loadnormal=load
function load(first,...)
if type(first)=="string" then
return loadstring(first,...)
else
return loadnormal(first,...)
end
end
else
loadstring=load
end
if not ipairs then
local function iterate(a,i)
i=i+1
local v=a[i]
if v~=nil then
return i,v
end
end
function ipairs(a)
return iterate,a,0
end
end
if not pairs then
function pairs(t)
return next,t
end
end
if not table.unpack then
table.unpack=_G.unpack
elseif not unpack then
_G.unpack=table.unpack
end
if not package.loaders then
package.loaders=package.searchers
end
local print,select,tostring=print,select,tostring
local inspectors={}
function setinspector(kind,inspector)
inspectors[kind]=inspector
end
function inspect(...)
for s=1,select("#",...) do
local value=select(s,...)
if value==nil then
print("nil")
else
local done=false
local kind=type(value)
local inspector=inspectors[kind]
if inspector then
done=inspector(value)
if done then
break
end
end
for kind,inspector in next,inspectors do
done=inspector(value)
if done then
break
end
end
if not done then
print(tostring(value))
end
end
end
end
local dummy=function() end
function optionalrequire(...)
local ok,result=xpcall(require,dummy,...)
if ok then
return result
end
end
if lua then
lua.mask=load([[τεχ = 1]]) and "utf" or "ascii"
end
local flush=io.flush
if flush then
local execute=os.execute if execute then function os.execute(...) flush() return execute(...) end end
local exec=os.exec if exec then function os.exec (...) flush() return exec (...) end end
local spawn=os.spawn if spawn then function os.spawn (...) flush() return spawn (...) end end
local popen=io.popen if popen then function io.popen (...) flush() return popen (...) end end
end
FFISUPPORTED=type(ffi)=="table" and ffi.os~="" and ffi.arch~="" and ffi.load
if not FFISUPPORTED then
local okay;okay,ffi=pcall(require,"ffi")
FFISUPPORTED=type(ffi)=="table" and ffi.os~="" and ffi.arch~="" and ffi.load
end
if not FFISUPPORTED then
ffi=nil
elseif not ffi.number then
ffi.number=tonumber
end
if LUAVERSION>5.3 then
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["l-macro"] = package.loaded["l-macro"] or true
-- original size: 10131, stripped down to: 5991
if not modules then modules={} end modules ['l-macros']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local 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
local lpegmatch=lpeg.match
local concat=table.concat
local format,sub,match=string.format,string.sub,string.match
local next,load,type=next,load,type
local newline=S("\n\r")^1
local continue=P("\\")*newline
local whitespace=S(" \t\n\r")
local spaces=S(" \t")+continue
local nametoken=R("az","AZ","__","09")
local name=nametoken^1
local body=((continue/""+1)-newline)^1
local lparent=P("(")
local rparent=P(")")
local noparent=1-(lparent+rparent)
local nested=P { lparent*(noparent+V(1))^0*rparent }
local escaped=P("\\")*P(1)
local squote=P("'")
local dquote=P('"')
local quoted=dquote*(escaped+(1-dquote))^0*dquote+squote*(escaped+(1-squote))^0*squote
local arguments=lparent*Ct((Cs((nested+(quoted+1-S("),")))^1)+S(", "))^0)*rparent
local macros=lua.macros or {}
lua.macros=macros
local patterns={}
local definitions={}
local resolve
local subparser
local report_lua=function(...)
if logs and logs.reporter then
report_lua=logs.reporter("system","lua")
report_lua(...)
else
print(format(...))
end
end
local safeguard=P("local")*whitespace^1*name*(whitespace+P("="))
resolve=safeguard+C(C(name)*(arguments^-1))/function(raw,s,a)
local d=definitions[s]
if d then
if a then
local n=#a
local p=patterns[s][n]
if p then
local d=d[n]
for i=1,n do
a[i]=lpegmatch(subparser,a[i]) or a[i]
end
return lpegmatch(p,d,1,a) or d
else
return raw
end
else
return d[0] or raw
end
elseif a then
for i=1,#a do
a[i]=lpegmatch(subparser,a[i]) or a[i]
end
return s.."("..concat(a,",")..")"
else
return raw
end
end
subparser=Cs((resolve+P(1))^1)
local enddefine=P("#enddefine")/""
local beginregister=(C(name)*(arguments+Cc(false))*C((1-enddefine)^1)*enddefine)/function(k,a,v)
local n=0
if a then
n=#a
local pattern=P(false)
for i=1,n do
pattern=pattern+(P(a[i])*Carg(1))/function(t) return t[i] end
end
pattern=Cs((pattern+P(1))^1)
local p=patterns[k]
if not p then
p={ [0]=false,false,false,false,false,false,false,false,false }
patterns[k]=p
end
p[n]=pattern
end
local d=definitions[k]
if not d then
d={ a=a,[0]=false,false,false,false,false,false,false,false,false }
definitions[k]=d
end
d[n]=lpegmatch(subparser,v) or v
return ""
end
local register=(Cs(name)*(arguments+Cc(false))*spaces^0*Cs(body))/function(k,a,v)
local n=0
if a then
n=#a
local pattern=P(false)
for i=1,n do
pattern=pattern+(P(a[i])*Carg(1))/function(t) return t[i] end
end
pattern=Cs((pattern+P(1))^1)
local p=patterns[k]
if not p then
p={ [0]=false,false,false,false,false,false,false,false,false }
patterns[k]=p
end
p[n]=pattern
end
local d=definitions[k]
if not d then
d={ a=a,[0]=false,false,false,false,false,false,false,false,false }
definitions[k]=d
end
d[n]=lpegmatch(subparser,v) or v
return ""
end
local unregister=(C(name)*spaces^0*(arguments+Cc(false)))/function(k,a)
local n=0
if a then
n=#a
local p=patterns[k]
if p then
p[n]=false
end
end
local d=definitions[k]
if d then
d[n]=false
end
return ""
end
local begindefine=(P("begindefine")*spaces^0/"")*beginregister
local define=(P("define" )*spaces^0/"")*register
local undefine=(P("undefine" )*spaces^0/"")*unregister
local parser=Cs((((P("#")/"")*(define+begindefine+undefine)*(newline^0/"") )+resolve+P(1) )^0 )
function macros.reset()
definitions={}
patterns={}
end
function macros.showdefinitions()
for name,list in table.sortedhash(definitions) do
local arguments=list.a
if arguments then
arguments="("..concat(arguments,",")..")"
else
arguments=""
end
print("macro: "..name..arguments)
for i=0,#list do
local l=list[i]
if l then
print(" "..l)
end
end
end
end
function macros.resolvestring(str)
return lpegmatch(parser,str) or str
end
function macros.resolving()
return next(patterns)
end
local function reload(path,name,data)
local only=match(name,".-([^/]+)%.lua")
if only and only~="" then
local name=path.."/"..only
local f=io.open(name,"wb")
f:write(data)
f:close()
local f=loadfile(name)
os.remove(name)
return f
end
end
local function reload(path,name,data)
if path and path~="" then
local only=string.match(name,".-([^/]+)%.lua")
if only and only~="" then
local name=path.."/"..only.."-macro.lua"
local f=io.open(name,"wb")
if f then
f:write(data)
f:close()
local l=loadfile(name)
os.remove(name)
return l
end
end
end
return load(data,name)
end
local function loaded(name,trace,detail)
local f=io.open(name,"rb")
if not f then
return false,format("file '%s' not found",name)
end
local c=f:read("*a")
if not c then
return false,format("file '%s' is invalid",name)
end
f:close()
local n=lpegmatch(parser,c)
if trace then
if #n~=#c then
report_lua("macros expanded in '%s' (%i => %i bytes)",name,#c,#n)
if detail then
report_lua()
report_lua(n)
report_lua()
end
elseif detail then
report_lua("no macros expanded in '%s'",name)
end
end
return reload(lfs and lfs.currentdir(),name,n)
end
macros.loaded=loaded
function required(name,trace)
local filename=file.addsuffix(name,"lua")
local fullname=resolvers and resolvers.find_file(filename) or filename
if not fullname or fullname=="" then
return false
end
local codeblob=package.loaded[fullname]
if codeblob then
return codeblob
end
local code,message=loaded(fullname,macros,trace,trace)
if type(code)=="function" then
code=code()
else
report_lua("error when loading '%s'",fullname)
return false,message
end
if code==nil then
code=false
end
package.loaded[fullname]=code
return code
end
macros.required=required
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["l-sandbox"] = package.loaded["l-sandbox"] or true
-- original size: 9747, stripped down to: 6313
if not modules then modules={} end modules ['l-sandbox']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local global=_G
local next=next
local unpack=unpack or table.unpack
local type=type
local tprint=texio and texio.write_nl or print
local tostring=tostring
local format=string.format
local concat=table.concat
local sort=table.sort
local gmatch=string.gmatch
local gsub=string.gsub
local requiem=require
sandbox={}
local sandboxed=false
local overloads={}
local skiploads={}
local initializers={}
local finalizers={}
local originals={}
local comments={}
local trace=false
local logger=false
local blocked={}
local function report(...)
tprint("sandbox ! "..format(...))
end
sandbox.report=report
function sandbox.setreporter(r)
report=r
sandbox.report=r
end
function sandbox.settrace(v)
trace=v
end
function sandbox.setlogger(l)
logger=type(l)=="function" and l or false
end
local function register(func,overload,comment)
if type(func)=="function" then
if type(overload)=="string" then
comment=overload
overload=nil
end
local function f(...)
if sandboxed then
local overload=overloads[f]
if overload then
if logger then
local result={ overload(func,...) }
logger {
comment=comments[f] or tostring(f),
arguments={... },
result=result[1] and true or false,
}
return unpack(result)
else
return overload(func,...)
end
else
end
else
return func(...)
end
end
if comment then
comments[f]=comment
if trace then
report("registering function: %s",comment)
end
end
overloads[f]=overload or false
originals[f]=func
return f
end
end
local function redefine(func,comment)
if type(func)=="function" then
skiploads[func]=comment or comments[func] or "unknown"
if overloads[func]==false then
overloads[func]=nil
end
end
end
sandbox.register=register
sandbox.redefine=redefine
function sandbox.original(func)
return originals and originals[func] or func
end
function sandbox.overload(func,overload,comment)
comment=comment or comments[func] or "?"
if type(func)~="function" then
if trace then
report("overloading unknown function: %s",comment)
end
elseif type(overload)~="function" then
if trace then
report("overloading function with bad overload: %s",comment)
end
elseif overloads[func]==nil then
if trace then
report("function is not registered: %s",comment)
end
elseif skiploads[func] then
if trace then
report("function is not skipped: %s",comment)
end
else
if trace then
report("overloading function: %s",comment)
end
overloads[func]=overload
end
return func
end
local function whatever(specification,what,target)
if type(specification)~="table" then
report("%s needs a specification",what)
elseif type(specification.category)~="string" or type(specification.action)~="function" then
report("%s needs a category and action",what)
elseif not sandboxed then
target[#target+1]=specification
elseif trace then
report("already enabled, discarding %s",what)
end
end
function sandbox.initializer(specification)
whatever(specification,"initializer",initializers)
end
function sandbox.finalizer(specification)
whatever(specification,"finalizer",finalizers)
end
function require(name)
local n=gsub(name,"^.*[\\/]","")
local n=gsub(n,"[%.].*$","")
local b=blocked[n]
if b==false then
return nil
elseif b then
if trace then
report("using blocked: %s",n)
end
return b
else
if trace then
report("requiring: %s",name)
end
return requiem(name)
end
end
function blockrequire(name,lib)
if trace then
report("preventing reload of: %s",name)
end
blocked[name]=lib or _G[name] or false
end
function sandbox.enable()
if not sandboxed then
debug={
traceback=debug.traceback,
}
for i=1,#initializers do
initializers[i].action()
end
for i=1,#finalizers do
finalizers[i].action()
end
local nnot=0
local nyes=0
local cnot={}
local cyes={}
local skip={}
for k,v in next,overloads do
local c=comments[k]
if v then
if c then
cyes[#cyes+1]=c
else
nyes=nyes+1
end
else
if c then
cnot[#cnot+1]=c
else
nnot=nnot+1
end
end
end
for k,v in next,skiploads do
skip[#skip+1]=v
end
if #cyes>0 then
sort(cyes)
report("overloaded known: %s",concat(cyes," | "))
end
if nyes>0 then
report("overloaded unknown: %s",nyes)
end
if #cnot>0 then
sort(cnot)
report("not overloaded known: %s",concat(cnot," | "))
end
if nnot>0 then
report("not overloaded unknown: %s",nnot)
end
if #skip>0 then
sort(skip)
report("not overloaded redefined: %s",concat(skip," | "))
end
initializers=nil
finalizers=nil
originals=nil
sandboxed=true
end
end
blockrequire("lfs",lfs)
blockrequire("io",io)
blockrequire("os",os)
blockrequire("ffi",ffi)
local function supported(library)
local l=_G[library]
return l
end
loadfile=register(loadfile,"loadfile")
if supported("io") then
io.open=register(io.open,"io.open")
io.popen=register(io.popen,"io.popen")
io.lines=register(io.lines,"io.lines")
io.output=register(io.output,"io.output")
io.input=register(io.input,"io.input")
end
if supported("os") then
os.execute=register(os.execute,"os.execute")
os.spawn=register(os.spawn,"os.spawn")
os.exec=register(os.exec,"os.exec")
os.rename=register(os.rename,"os.rename")
os.remove=register(os.remove,"os.remove")
end
if supported("lfs") then
lfs.chdir=register(lfs.chdir,"lfs.chdir")
lfs.mkdir=register(lfs.mkdir,"lfs.mkdir")
lfs.rmdir=register(lfs.rmdir,"lfs.rmdir")
lfs.isfile=register(lfs.isfile,"lfs.isfile")
lfs.isdir=register(lfs.isdir,"lfs.isdir")
lfs.attributes=register(lfs.attributes,"lfs.attributes")
lfs.dir=register(lfs.dir,"lfs.dir")
lfs.lock_dir=register(lfs.lock_dir,"lfs.lock_dir")
lfs.touch=register(lfs.touch,"lfs.touch")
lfs.link=register(lfs.link,"lfs.link")
lfs.setmode=register(lfs.setmode,"lfs.setmode")
lfs.readlink=register(lfs.readlink,"lfs.readlink")
lfs.shortname=register(lfs.shortname,"lfs.shortname")
lfs.symlinkattributes=register(lfs.symlinkattributes,"lfs.symlinkattributes")
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["l-package"] = package.loaded["l-package"] or true
-- original size: 11605, stripped down to: 8299
if not modules then modules={} end modules ['l-package']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local type=type
local gsub,format,find=string.gsub,string.format,string.find
local insert,remove=table.insert,table.remove
local P,S,Cs,lpegmatch=lpeg.P,lpeg.S,lpeg.Cs,lpeg.match
local package=package
local searchers=package.searchers or package.loaders
local filejoin=file and file.join or function(path,name) return path.."/"..name end
local isreadable=file and file.is_readable or function(name) local f=io.open(name) if f then f:close() return true end end
local addsuffix=file and file.addsuffix or function(name,suffix) return name.."."..suffix end
local function cleanpath(path)
return path
end
local pattern=Cs((((1-S("\\/"))^0*(S("\\/")^1/"/"))^0*(P(".")^1/"/"+P(1))^1)*-1)
local function lualibfile(name)
return lpegmatch(pattern,name) or name
end
local offset=luarocks and 1 or 0
local helpers=package.helpers or {
cleanpath=cleanpath,
lualibfile=lualibfile,
trace=false,
report=function(...) print(format(...)) end,
builtin={
["preload table"]=searchers[1+offset],
["path specification"]=searchers[2+offset],
["cpath specification"]=searchers[3+offset],
["all in one fallback"]=searchers[4+offset],
},
methods={},
sequence={
"already loaded",
"preload table",
"qualified path",
"lua extra list",
"lib extra list",
"path specification",
"cpath specification",
"all in one fallback",
"not loaded",
}
}
package.helpers=helpers
local methods=helpers.methods
local builtin=helpers.builtin
local extraluapaths={}
local extralibpaths={}
local luapaths=nil
local libpaths=nil
local oldluapath=nil
local oldlibpath=nil
local nofextralua=-1
local nofextralib=-1
local nofpathlua=-1
local nofpathlib=-1
local function listpaths(what,paths)
local nofpaths=#paths
if nofpaths>0 then
for i=1,nofpaths do
helpers.report("using %s path %i: %s",what,i,paths[i])
end
else
helpers.report("no %s paths defined",what)
end
return nofpaths
end
local function getextraluapaths()
if helpers.trace and #extraluapaths~=nofextralua then
nofextralua=listpaths("extra lua",extraluapaths)
end
return extraluapaths
end
local function getextralibpaths()
if helpers.trace and #extralibpaths~=nofextralib then
nofextralib=listpaths("extra lib",extralibpaths)
end
return extralibpaths
end
local function getluapaths()
local luapath=package.path or ""
if oldluapath~=luapath then
luapaths=file.splitpath(luapath,";")
oldluapath=luapath
nofpathlua=-1
end
if helpers.trace and #luapaths~=nofpathlua then
nofpathlua=listpaths("builtin lua",luapaths)
end
return luapaths
end
local function getlibpaths()
local libpath=package.cpath or ""
if oldlibpath~=libpath then
libpaths=file.splitpath(libpath,";")
oldlibpath=libpath
nofpathlib=-1
end
if helpers.trace and #libpaths~=nofpathlib then
nofpathlib=listpaths("builtin lib",libpaths)
end
return libpaths
end
package.luapaths=getluapaths
package.libpaths=getlibpaths
package.extraluapaths=getextraluapaths
package.extralibpaths=getextralibpaths
local hashes={
lua={},
lib={},
}
local function registerpath(tag,what,target,...)
local pathlist={... }
local cleanpath=helpers.cleanpath
local trace=helpers.trace
local report=helpers.report
local hash=hashes[what]
local function add(path)
local path=cleanpath(path)
if not hash[path] then
target[#target+1]=path
hash[path]=true
if trace then
report("registered %s path %s: %s",tag,#target,path)
end
else
if trace then
report("duplicate %s path: %s",tag,path)
end
end
end
for p=1,#pathlist do
local path=pathlist[p]
if type(path)=="table" then
for i=1,#path do
add(path[i])
end
else
add(path)
end
end
end
local function pushpath(tag,what,target,path)
local path=helpers.cleanpath(path)
insert(target,1,path)
if helpers.trace then
helpers.report("pushing %s path in front: %s",tag,path)
end
end
local function poppath(tag,what,target)
local path=remove(target,1)
if helpers.trace then
if path then
helpers.report("popping %s path from front: %s",tag,path)
else
helpers.report("no %s path to pop",tag)
end
end
end
helpers.registerpath=registerpath
function package.extraluapath(...)
registerpath("extra lua","lua",extraluapaths,...)
end
function package.pushluapath(path)
pushpath("extra lua","lua",extraluapaths,path)
end
function package.popluapath()
poppath("extra lua","lua",extraluapaths)
end
function package.extralibpath(...)
registerpath("extra lib","lib",extralibpaths,...)
end
function package.pushlibpath(path)
pushpath("extra lib","lib",extralibpaths,path)
end
function package.poplibpath()
poppath("extra lib","lua",extralibpaths)
end
local function loadedaslib(resolved,rawname)
local base=gsub(rawname,"%.","_")
local init="luaopen_"..gsub(base,"%.","_")
if helpers.trace then
helpers.report("calling loadlib with '%s' with init '%s'",resolved,init)
end
return package.loadlib(resolved,init)
end
helpers.loadedaslib=loadedaslib
local function loadedbypath(name,rawname,paths,islib,what)
local trace=helpers.trace
for p=1,#paths do
local path=paths[p]
local resolved=filejoin(path,name)
if trace then
helpers.report("%s path, identifying '%s' on '%s'",what,name,path)
end
if isreadable(resolved) then
if trace then
helpers.report("%s path, '%s' found on '%s'",what,name,resolved)
end
if islib then
return loadedaslib(resolved,rawname)
else
return loadfile(resolved)
end
end
end
end
helpers.loadedbypath=loadedbypath
local function loadedbyname(name,rawname)
if find(name,"^/") or find(name,"^[a-zA-Z]:/") then
local trace=helpers.trace
if trace then
helpers.report("qualified name, identifying '%s'",what,name)
end
if isreadable(name) then
if trace then
helpers.report("qualified name, '%s' found",what,name)
end
return loadfile(name)
end
end
end
helpers.loadedbyname=loadedbyname
methods["already loaded"]=function(name)
return package.loaded[name]
end
methods["preload table"]=function(name)
return builtin["preload table"](name)
end
methods["qualified path"]=function(name)
return loadedbyname(addsuffix(lualibfile(name),"lua"),name)
end
methods["lua extra list"]=function(name)
return loadedbypath(addsuffix(lualibfile(name),"lua"),name,getextraluapaths(),false,"lua")
end
methods["lib extra list"]=function(name)
return loadedbypath(addsuffix(lualibfile(name),os.libsuffix),name,getextralibpaths(),true,"lib")
end
methods["path specification"]=function(name)
getluapaths()
return builtin["path specification"](name)
end
methods["cpath specification"]=function(name)
getlibpaths()
return builtin["cpath specification"](name)
end
methods["all in one fallback"]=function(name)
return builtin["all in one fallback"](name)
end
methods["not loaded"]=function(name)
if helpers.trace then
helpers.report("unable to locate '%s'",name or "?")
end
return nil
end
local level=0
local used={}
helpers.traceused=false
function helpers.loaded(name)
local sequence=helpers.sequence
level=level+1
for i=1,#sequence do
local method=sequence[i]
if helpers.trace then
helpers.report("%s, level '%s', method '%s', name '%s'","locating",level,method,name)
end
local result,rest=methods[method](name)
if type(result)=="function" then
if helpers.trace then
helpers.report("%s, level '%s', method '%s', name '%s'","found",level,method,name)
end
if helpers.traceused then
used[#used+1]={ level=level,name=name }
end
level=level-1
return result,rest
end
end
level=level-1
return nil
end
function helpers.showused()
local n=#used
if n>0 then
helpers.report("%s libraries loaded:",n)
helpers.report()
for i=1,n do
local u=used[i]
helpers.report("%i %a",u.level,u.name)
end
helpers.report()
end
end
function helpers.unload(name)
if helpers.trace then
if package.loaded[name] then
helpers.report("unloading, name '%s', %s",name,"done")
else
helpers.report("unloading, name '%s', %s",name,"not loaded")
end
end
package.loaded[name]=nil
end
table.insert(searchers,1,helpers.loaded)
if context then
package.path=""
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["l-lpeg"] = package.loaded["l-lpeg"] or true
-- original size: 38434, stripped down to: 19310
if not modules then modules={} end modules ['l-lpeg']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
lpeg=require("lpeg")
local lpeg=lpeg
if not lpeg.print then function lpeg.print(...) print(lpeg.pcode(...)) end end
local type,next,tostring=type,next,tostring
local byte,char,gmatch,format=string.byte,string.char,string.gmatch,string.format
local floor=math.floor
local 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
local lpegtype,lpegmatch,lpegprint=lpeg.type,lpeg.match,lpeg.print
if setinspector then
setinspector("lpeg",function(v) if lpegtype(v) then lpegprint(v) return true end end)
end
lpeg.patterns=lpeg.patterns or {}
local patterns=lpeg.patterns
local anything=P(1)
local endofstring=P(-1)
local alwaysmatched=P(true)
patterns.anything=anything
patterns.endofstring=endofstring
patterns.beginofstring=alwaysmatched
patterns.alwaysmatched=alwaysmatched
local sign=S('+-')
local zero=P('0')
local digit=R('09')
local digits=digit^1
local octdigit=R("07")
local octdigits=octdigit^1
local lowercase=R("az")
local uppercase=R("AZ")
local underscore=P("_")
local hexdigit=digit+lowercase+uppercase
local hexdigits=hexdigit^1
local cr,lf,crlf=P("\r"),P("\n"),P("\r\n")
local newline=P("\r")*(P("\n")+P(true))+P("\n")
local escaped=P("\\")*anything
local squote=P("'")
local dquote=P('"')
local space=P(" ")
local period=P(".")
local comma=P(",")
local utfbom_32_be=P('\000\000\254\255')
local utfbom_32_le=P('\255\254\000\000')
local utfbom_16_be=P('\254\255')
local utfbom_16_le=P('\255\254')
local utfbom_8=P('\239\187\191')
local utfbom=utfbom_32_be+utfbom_32_le+utfbom_16_be+utfbom_16_le+utfbom_8
local 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")
local 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")
local 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)
local utf8next=R("\128\191")
patterns.utfbom_32_be=utfbom_32_be
patterns.utfbom_32_le=utfbom_32_le
patterns.utfbom_16_be=utfbom_16_be
patterns.utfbom_16_le=utfbom_16_le
patterns.utfbom_8=utfbom_8
patterns.utf_16_be_nl=P("\000\r\000\n")+P("\000\r")+P("\000\n")
patterns.utf_16_le_nl=P("\r\000\n\000")+P("\r\000")+P("\n\000")
patterns.utf_32_be_nl=P("\000\000\000\r\000\000\000\n")+P("\000\000\000\r")+P("\000\000\000\n")
patterns.utf_32_le_nl=P("\r\000\000\000\n\000\000\000")+P("\r\000\000\000")+P("\n\000\000\000")
patterns.utf8one=R("\000\127")
patterns.utf8two=R("\194\223")*utf8next
patterns.utf8three=R("\224\239")*utf8next*utf8next
patterns.utf8four=R("\240\244")*utf8next*utf8next*utf8next
patterns.utfbom=utfbom
patterns.utftype=utftype
patterns.utfstricttype=utfstricttype
patterns.utfoffset=utfoffset
local utf8char=patterns.utf8one+patterns.utf8two+patterns.utf8three+patterns.utf8four
local validutf8char=utf8char^0*endofstring*Cc(true)+Cc(false)
local utf8character=P(1)*R("\128\191")^0
patterns.utf8=utf8char
patterns.utf8char=utf8char
patterns.utf8character=utf8character
patterns.validutf8=validutf8char
patterns.validutf8char=validutf8char
local eol=S("\n\r")
local spacer=S(" \t\f\v")
local whitespace=eol+spacer
local nonspacer=1-spacer
local nonwhitespace=1-whitespace
patterns.eol=eol
patterns.spacer=spacer
patterns.whitespace=whitespace
patterns.nonspacer=nonspacer
patterns.nonwhitespace=nonwhitespace
local stripper=spacer^0*C((spacer^0*nonspacer^1)^0)
local fullstripper=whitespace^0*C((whitespace^0*nonwhitespace^1)^0)
local collapser=Cs(spacer^0/""*nonspacer^0*((spacer^0/" "*nonspacer^1)^0))
local nospacer=Cs((whitespace^1/""+nonwhitespace^1)^0)
local b_collapser=Cs(whitespace^0/""*(nonwhitespace^1+whitespace^1/" ")^0)
local e_collapser=Cs((whitespace^1*endofstring/""+nonwhitespace^1+whitespace^1/" ")^0)
local m_collapser=Cs((nonwhitespace^1+whitespace^1/" ")^0)
local b_stripper=Cs(spacer^0/""*(nonspacer^1+spacer^1/" ")^0)
local e_stripper=Cs((spacer^1*endofstring/""+nonspacer^1+spacer^1/" ")^0)
local m_stripper=Cs((nonspacer^1+spacer^1/" ")^0)
patterns.stripper=stripper
patterns.fullstripper=fullstripper
patterns.collapser=collapser
patterns.nospacer=nospacer
patterns.b_collapser=b_collapser
patterns.m_collapser=m_collapser
patterns.e_collapser=e_collapser
patterns.b_stripper=b_stripper
patterns.m_stripper=m_stripper
patterns.e_stripper=e_stripper
patterns.lowercase=lowercase
patterns.uppercase=uppercase
patterns.letter=patterns.lowercase+patterns.uppercase
patterns.space=space
patterns.tab=P("\t")
patterns.spaceortab=patterns.space+patterns.tab
patterns.newline=newline
patterns.emptyline=newline^1
patterns.equal=P("=")
patterns.comma=comma
patterns.commaspacer=comma*spacer^0
patterns.period=period
patterns.colon=P(":")
patterns.semicolon=P(";")
patterns.underscore=underscore
patterns.escaped=escaped
patterns.squote=squote
patterns.dquote=dquote
patterns.nosquote=(escaped+(1-squote))^0
patterns.nodquote=(escaped+(1-dquote))^0
patterns.unsingle=(squote/"")*patterns.nosquote*(squote/"")
patterns.undouble=(dquote/"")*patterns.nodquote*(dquote/"")
patterns.unquoted=patterns.undouble+patterns.unsingle
patterns.unspacer=((patterns.spacer^1)/"")^0
patterns.singlequoted=squote*patterns.nosquote*squote
patterns.doublequoted=dquote*patterns.nodquote*dquote
patterns.quoted=patterns.doublequoted+patterns.singlequoted
patterns.digit=digit
patterns.digits=digits
patterns.octdigit=octdigit
patterns.octdigits=octdigits
patterns.hexdigit=hexdigit
patterns.hexdigits=hexdigits
patterns.sign=sign
patterns.cardinal=digits
patterns.integer=sign^-1*digits
patterns.unsigned=digit^0*period*digits
patterns.float=sign^-1*patterns.unsigned
patterns.cunsigned=digit^0*comma*digits
patterns.cpunsigned=digit^0*(period+comma)*digits
patterns.cfloat=sign^-1*patterns.cunsigned
patterns.cpfloat=sign^-1*patterns.cpunsigned
patterns.number=patterns.float+patterns.integer
patterns.cnumber=patterns.cfloat+patterns.integer
patterns.cpnumber=patterns.cpfloat+patterns.integer
patterns.oct=zero*octdigits
patterns.octal=patterns.oct
patterns.HEX=zero*P("X")*(digit+uppercase)^1
patterns.hex=zero*P("x")*(digit+lowercase)^1
patterns.hexadecimal=zero*S("xX")*hexdigits
patterns.hexafloat=sign^-1*zero*S("xX")*(hexdigit^0*period*hexdigits+hexdigits*period*hexdigit^0+hexdigits)*(S("pP")*sign^-1*hexdigits)^-1
patterns.decafloat=sign^-1*(digit^0*period*digits+digits*period*digit^0+digits)*S("eE")*sign^-1*digits
patterns.propername=(uppercase+lowercase+underscore)*(uppercase+lowercase+underscore+digit)^0*endofstring
patterns.somecontent=(anything-newline-space)^1
patterns.beginline=#(1-newline)
patterns.longtostring=Cs(whitespace^0/""*((patterns.quoted+nonwhitespace^1+whitespace^1/""*(endofstring+Cc(" ")))^0))
function anywhere(pattern)
return (1-P(pattern))^0*P(pattern)
end
lpeg.anywhere=anywhere
function lpeg.instringchecker(p)
p=anywhere(p)
return function(str)
return lpegmatch(p,str) and true or false
end
end
function lpeg.splitter(pattern,action)
if action then
return (((1-P(pattern))^1)/action+1)^0
else
return (Cs((1-P(pattern))^1)+1)^0
end
end
function lpeg.tsplitter(pattern,action)
if action then
return Ct((((1-P(pattern))^1)/action+1)^0)
else
return Ct((Cs((1-P(pattern))^1)+1)^0)
end
end
local splitters_s,splitters_m,splitters_t={},{},{}
local function splitat(separator,single)
local splitter=(single and splitters_s[separator]) or splitters_m[separator]
if not splitter then
separator=P(separator)
local other=C((1-separator)^0)
if single then
local any=anything
splitter=other*(separator*C(any^0)+"")
splitters_s[separator]=splitter
else
splitter=other*(separator*other)^0
splitters_m[separator]=splitter
end
end
return splitter
end
local function tsplitat(separator)
local splitter=splitters_t[separator]
if not splitter then
splitter=Ct(splitat(separator))
splitters_t[separator]=splitter
end
return splitter
end
lpeg.splitat=splitat
lpeg.tsplitat=tsplitat
function string.splitup(str,separator)
if not separator then
separator=","
end
return lpegmatch(splitters_m[separator] or splitat(separator),str)
end
local cache={}
function lpeg.split(separator,str)
local c=cache[separator]
if not c then
c=tsplitat(separator)
cache[separator]=c
end
return lpegmatch(c,str)
end
function string.split(str,separator)
if separator then
local c=cache[separator]
if not c then
c=tsplitat(separator)
cache[separator]=c
end
return lpegmatch(c,str)
else
return { str }
end
end
local spacing=patterns.spacer^0*newline
local empty=spacing*Cc("")
local nonempty=Cs((1-spacing)^1)*spacing^-1
local content=(empty+nonempty)^1
patterns.textline=content
local linesplitter=tsplitat(newline)
patterns.linesplitter=linesplitter
function string.splitlines(str)
return lpegmatch(linesplitter,str)
end
local cache={}
function lpeg.checkedsplit(separator,str)
local c=cache[separator]
if not c then
separator=P(separator)
local other=C((1-separator)^1)
c=Ct(separator^0*other*(separator^1*other)^0)
cache[separator]=c
end
return lpegmatch(c,str)
end
function string.checkedsplit(str,separator)
local c=cache[separator]
if not c then
separator=P(separator)
local other=C((1-separator)^1)
c=Ct(separator^0*other*(separator^1*other)^0)
cache[separator]=c
end
return lpegmatch(c,str)
end
local function f2(s) local c1,c2=byte(s,1,2) return c1*64+c2-12416 end
local function f3(s) local c1,c2,c3=byte(s,1,3) return (c1*64+c2)*64+c3-925824 end
local function f4(s) local c1,c2,c3,c4=byte(s,1,4) return ((c1*64+c2)*64+c3)*64+c4-63447168 end
local utf8byte=patterns.utf8one/byte+patterns.utf8two/f2+patterns.utf8three/f3+patterns.utf8four/f4
patterns.utf8byte=utf8byte
local cache={}
function lpeg.stripper(str)
if type(str)=="string" then
local s=cache[str]
if not s then
s=Cs(((S(str)^1)/""+1)^0)
cache[str]=s
end
return s
else
return Cs(((str^1)/""+1)^0)
end
end
local cache={}
function lpeg.keeper(str)
if type(str)=="string" then
local s=cache[str]
if not s then
s=Cs((((1-S(str))^1)/""+1)^0)
cache[str]=s
end
return s
else
return Cs((((1-str)^1)/""+1)^0)
end
end
function lpeg.frontstripper(str)
return (P(str)+P(true))*Cs(anything^0)
end
function lpeg.endstripper(str)
return Cs((1-P(str)*endofstring)^0)
end
function lpeg.replacer(one,two,makefunction,isutf)
local pattern
local u=isutf and utf8char or 1
if type(one)=="table" then
local no=#one
local p=P(false)
if no==0 then
for k,v in next,one do
p=p+P(k)/v
end
pattern=Cs((p+u)^0)
elseif no==1 then
local o=one[1]
one,two=P(o[1]),o[2]
pattern=Cs((one/two+u)^0)
else
for i=1,no do
local o=one[i]
p=p+P(o[1])/o[2]
end
pattern=Cs((p+u)^0)
end
else
pattern=Cs((P(one)/(two or "")+u)^0)
end
if makefunction then
return function(str)
return lpegmatch(pattern,str)
end
else
return pattern
end
end
function lpeg.finder(lst,makefunction,isutf)
local pattern
if type(lst)=="table" then
pattern=P(false)
if #lst==0 then
for k,v in next,lst do
pattern=pattern+P(k)
end
else
for i=1,#lst do
pattern=pattern+P(lst[i])
end
end
else
pattern=P(lst)
end
if isutf then
pattern=((utf8char or 1)-pattern)^0*pattern
else
pattern=(1-pattern)^0*pattern
end
if makefunction then
return function(str)
return lpegmatch(pattern,str)
end
else
return pattern
end
end
local splitters_f,splitters_s={},{}
function lpeg.firstofsplit(separator)
local splitter=splitters_f[separator]
if not splitter then
local pattern=P(separator)
splitter=C((1-pattern)^0)
splitters_f[separator]=splitter
end
return splitter
end
function lpeg.secondofsplit(separator)
local splitter=splitters_s[separator]
if not splitter then
local pattern=P(separator)
splitter=(1-pattern)^0*pattern*C(anything^0)
splitters_s[separator]=splitter
end
return splitter
end
local splitters_s,splitters_p={},{}
function lpeg.beforesuffix(separator)
local splitter=splitters_s[separator]
if not splitter then
local pattern=P(separator)
splitter=C((1-pattern)^0)*pattern*endofstring
splitters_s[separator]=splitter
end
return splitter
end
function lpeg.afterprefix(separator)
local splitter=splitters_p[separator]
if not splitter then
local pattern=P(separator)
splitter=pattern*C(anything^0)
splitters_p[separator]=splitter
end
return splitter
end
function lpeg.balancer(left,right)
left,right=P(left),P(right)
return P { left*((1-left-right)+V(1))^0*right }
end
function lpeg.counter(pattern,action)
local n=0
local pattern=(P(pattern)/function() n=n+1 end+anything)^0
if action then
return function(str) n=0;lpegmatch(pattern,str);action(n) end
else
return function(str) n=0;lpegmatch(pattern,str);return n end
end
end
function lpeg.is_lpeg(p)
return p and lpegtype(p)=="pattern"
end
function lpeg.oneof(list,...)
if type(list)~="table" then
list={ list,... }
end
local p=P(list[1])
for l=2,#list do
p=p+P(list[l])
end
return p
end
local sort=table.sort
local function copyindexed(old)
local new={}
for i=1,#old do
new[i]=old
end
return new
end
local function sortedkeys(tab)
local keys,s={},0
for key,_ in next,tab do
s=s+1
keys[s]=key
end
sort(keys)
return keys
end
function lpeg.append(list,pp,delayed,checked)
local p=pp
if #list>0 then
local keys=copyindexed(list)
sort(keys)
for i=#keys,1,-1 do
local k=keys[i]
if p then
p=P(k)+p
else
p=P(k)
end
end
elseif delayed then
local keys=sortedkeys(list)
if p then
for i=1,#keys,1 do
local k=keys[i]
local v=list[k]
p=P(k)/list+p
end
else
for i=1,#keys do
local k=keys[i]
local v=list[k]
if p then
p=P(k)+p
else
p=P(k)
end
end
if p then
p=p/list
end
end
elseif checked then
local keys=sortedkeys(list)
for i=1,#keys do
local k=keys[i]
local v=list[k]
if p then
if k==v then
p=P(k)+p
else
p=P(k)/v+p
end
else
if k==v then
p=P(k)
else
p=P(k)/v
end
end
end
else
local keys=sortedkeys(list)
for i=1,#keys do
local k=keys[i]
local v=list[k]
if p then
p=P(k)/v+p
else
p=P(k)/v
end
end
end
return p
end
local p_false=P(false)
local p_true=P(true)
local lower=utf and utf.lower or string.lower
local upper=utf and utf.upper or string.upper
function lpeg.setutfcasers(l,u)
lower=l or lower
upper=u or upper
end
local function make1(t,rest)
local p=p_false
local keys=sortedkeys(t)
for i=1,#keys do
local k=keys[i]
if k~="" then
local v=t[k]
if v==true then
p=p+P(k)*p_true
elseif v==false then
else
p=p+P(k)*make1(v,v[""])
end
end
end
if rest then
p=p+p_true
end
return p
end
local function make2(t,rest)
local p=p_false
local keys=sortedkeys(t)
for i=1,#keys do
local k=keys[i]
if k~="" then
local v=t[k]
if v==true then
p=p+(P(lower(k))+P(upper(k)))*p_true
elseif v==false then
else
p=p+(P(lower(k))+P(upper(k)))*make2(v,v[""])
end
end
end
if rest then
p=p+p_true
end
return p
end
local function utfchartabletopattern(list,insensitive)
local tree={}
local n=#list
if n==0 then
for s in next,list do
local t=tree
local p,pk
for c in gmatch(s,".") do
if t==true then
t={ [c]=true,[""]=true }
p[pk]=t
p=t
t=false
elseif t==false then
t={ [c]=false }
p[pk]=t
p=t
t=false
else
local tc=t[c]
if not tc then
tc=false
t[c]=false
end
p=t
t=tc
end
pk=c
end
if t==false then
p[pk]=true
elseif t==true then
else
t[""]=true
end
end
else
for i=1,n do
local s=list[i]
local t=tree
local p,pk
for c in gmatch(s,".") do
if t==true then
t={ [c]=true,[""]=true }
p[pk]=t
p=t
t=false
elseif t==false then
t={ [c]=false }
p[pk]=t
p=t
t=false
else
local tc=t[c]
if not tc then
tc=false
t[c]=false
end
p=t
t=tc
end
pk=c
end
if t==false then
p[pk]=true
elseif t==true then
else
t[""]=true
end
end
end
return (insensitive and make2 or make1)(tree)
end
lpeg.utfchartabletopattern=utfchartabletopattern
function lpeg.utfreplacer(list,insensitive)
local pattern=Cs((utfchartabletopattern(list,insensitive)/list+utf8character)^0)
return function(str)
return lpegmatch(pattern,str) or str
end
end
patterns.containseol=lpeg.finder(eol)
local function nextstep(n,step,result)
local m=n%step
local d=floor(n/step)
if d>0 then
local v=V(tostring(step))
local s=result.start
for i=1,d do
if s then
s=v*s
else
s=v
end
end
result.start=s
end
if step>1 and result.start then
local v=V(tostring(step/2))
result[tostring(step)]=v*v
end
if step>0 then
return nextstep(m,step/2,result)
else
return result
end
end
function lpeg.times(pattern,n)
return P(nextstep(n,2^16,{ "start",["1"]=pattern }))
end
do
local trailingzeros=zero^0*-digit
local stripper=Cs((
digits*(
period*trailingzeros/""+period*(digit-trailingzeros)^1*(trailingzeros/"")
)+1
)^0)
lpeg.patterns.stripzeros=stripper
local nonzero=digit-zero
local trailingzeros=zero^1*endofstring
local stripper=Cs((1-period)^0*(
period*trailingzeros/""+period*(nonzero^1+(trailingzeros/"")+zero^1)^0+endofstring
))
lpeg.patterns.stripzero=stripper
end
local byte_to_HEX={}
local byte_to_hex={}
local byte_to_dec={}
local hex_to_byte={}
for i=0,255 do
local H=format("%02X",i)
local h=format("%02x",i)
local d=format("%03i",i)
local c=char(i)
byte_to_HEX[c]=H
byte_to_hex[c]=h
byte_to_dec[c]=d
hex_to_byte[h]=c
hex_to_byte[H]=c
end
local hextobyte=P(2)/hex_to_byte
local bytetoHEX=P(1)/byte_to_HEX
local bytetohex=P(1)/byte_to_hex
local bytetodec=P(1)/byte_to_dec
local hextobytes=Cs(hextobyte^0)
local bytestoHEX=Cs(bytetoHEX^0)
local bytestohex=Cs(bytetohex^0)
local bytestodec=Cs(bytetodec^0)
patterns.hextobyte=hextobyte
patterns.bytetoHEX=bytetoHEX
patterns.bytetohex=bytetohex
patterns.bytetodec=bytetodec
patterns.hextobytes=hextobytes
patterns.bytestoHEX=bytestoHEX
patterns.bytestohex=bytestohex
patterns.bytestodec=bytestodec
function string.toHEX(s)
if not s or s=="" then
return s
else
return lpegmatch(bytestoHEX,s)
end
end
function string.tohex(s)
if not s or s=="" then
return s
else
return lpegmatch(bytestohex,s)
end
end
function string.todec(s)
if not s or s=="" then
return s
else
return lpegmatch(bytestodec,s)
end
end
function string.tobytes(s)
if not s or s=="" then
return s
else
return lpegmatch(hextobytes,s)
end
end
local patterns={}
local function containsws(what)
local p=patterns[what]
if not p then
local p1=P(what)*(whitespace+endofstring)*Cc(true)
local p2=whitespace*P(p1)
p=P(p1)+P(1-p2)^0*p2+Cc(false)
patterns[what]=p
end
return p
end
lpeg.containsws=containsws
function string.containsws(str,what)
return lpegmatch(patterns[what] or containsws(what),str)
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["l-function"] = package.loaded["l-function"] or true
-- original size: 361, stripped down to: 317
if not modules then modules={} end modules ['l-functions']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
functions=functions or {}
function functions.dummy() end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["l-string"] = package.loaded["l-string"] or true
-- original size: 6461, stripped down to: 3255
if not modules then modules={} end modules ['l-string']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local string=string
local sub,gmatch,format,char,byte,rep,lower=string.sub,string.gmatch,string.format,string.char,string.byte,string.rep,string.lower
local lpegmatch,patterns=lpeg.match,lpeg.patterns
local P,S,C,Ct,Cc,Cs=lpeg.P,lpeg.S,lpeg.C,lpeg.Ct,lpeg.Cc,lpeg.Cs
local unquoted=patterns.squote*C(patterns.nosquote)*patterns.squote+patterns.dquote*C(patterns.nodquote)*patterns.dquote
function string.unquoted(str)
return lpegmatch(unquoted,str) or str
end
function string.quoted(str)
return format("%q",str)
end
function string.count(str,pattern)
local n=0
for _ in gmatch(str,pattern) do
n=n+1
end
return n
end
function string.limit(str,n,sentinel)
if #str>n then
sentinel=sentinel or "..."
return sub(str,1,(n-#sentinel))..sentinel
else
return str
end
end
local stripper=patterns.stripper
local fullstripper=patterns.fullstripper
local collapser=patterns.collapser
local nospacer=patterns.nospacer
local longtostring=patterns.longtostring
function string.strip(str)
return str and lpegmatch(stripper,str) or ""
end
function string.fullstrip(str)
return str and lpegmatch(fullstripper,str) or ""
end
function string.collapsespaces(str)
return str and lpegmatch(collapser,str) or ""
end
function string.nospaces(str)
return str and lpegmatch(nospacer,str) or ""
end
function string.longtostring(str)
return str and lpegmatch(longtostring,str) or ""
end
local pattern=P(" ")^0*P(-1)
function string.is_empty(str)
if not str or str=="" then
return true
else
return lpegmatch(pattern,str) and true or false
end
end
local anything=patterns.anything
local allescapes=Cc("%")*S(".-+%?()[]*")
local someescapes=Cc("%")*S(".-+%()[]")
local matchescapes=Cc(".")*S("*?")
local pattern_a=Cs ((allescapes+anything )^0 )
local pattern_b=Cs ((someescapes+matchescapes+anything )^0 )
local pattern_c=Cs (Cc("^")*(someescapes+matchescapes+anything )^0*Cc("$") )
function string.escapedpattern(str,simple)
return lpegmatch(simple and pattern_b or pattern_a,str)
end
function string.topattern(str,lowercase,strict)
if str=="" or type(str)~="string" then
return ".*"
elseif strict then
str=lpegmatch(pattern_c,str)
else
str=lpegmatch(pattern_b,str)
end
if lowercase then
return lower(str)
else
return str
end
end
function string.valid(str,default)
return (type(str)=="string" and str~="" and str) or default or nil
end
string.itself=function(s) return s end
local pattern_c=Ct(C(1)^0)
local pattern_b=Ct((C(1)/byte)^0)
function string.totable(str,bytes)
return lpegmatch(bytes and pattern_b or pattern_c,str)
end
local replacer=lpeg.replacer("@","%%")
function string.tformat(fmt,...)
return format(lpegmatch(replacer,fmt),...)
end
string.quote=string.quoted
string.unquote=string.unquoted
if not string.bytetable then
local limit=5000
function string.bytetable(str)
local n=#str
if n>limit then
local t={ byte(str,1,limit) }
for i=limit+1,n do
t[i]=byte(str,i)
end
return t
else
return { byte(str,1,n) }
end
end
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["l-table"] = package.loaded["l-table"] or true
-- original size: 41332, stripped down to: 21508
if not modules then modules={} end modules ['l-table']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local type,next,tostring,tonumber,select=type,next,tostring,tonumber,select
local table,string=table,string
local concat,sort=table.concat,table.sort
local format,lower,dump=string.format,string.lower,string.dump
local getmetatable,setmetatable=getmetatable,setmetatable
local lpegmatch,patterns=lpeg.match,lpeg.patterns
local floor=math.floor
local stripper=patterns.stripper
function table.getn(t)
return t and #t
end
function table.strip(tab)
local lst={}
local l=0
for i=1,#tab do
local s=lpegmatch(stripper,tab[i]) or ""
if s=="" then
else
l=l+1
lst[l]=s
end
end
return lst
end
function table.keys(t)
if t then
local keys={}
local k=0
for key in next,t do
k=k+1
keys[k]=key
end
return keys
else
return {}
end
end
local function compare(a,b)
local ta=type(a)
if ta=="number" then
local tb=type(b)
if ta==tb then
return a<b
elseif tb=="string" then
return tostring(a)<b
end
elseif ta=="string" then
local tb=type(b)
if ta==tb then
return a<b
else
return a<tostring(b)
end
end
return tostring(a)<tostring(b)
end
local function sortedkeys(tab)
if tab then
local srt={}
local category=0
local s=0
for key in next,tab do
s=s+1
srt[s]=key
if category~=3 then
local tkey=type(key)
if category==1 then
if tkey~="string" then
category=3
end
elseif category==2 then
if tkey~="number" then
category=3
end
else
if tkey=="string" then
category=1
elseif tkey=="number" then
category=2
else
category=3
end
end
end
end
if s<2 then
elseif category==3 then
sort(srt,compare)
else
sort(srt)
end
return srt
else
return {}
end
end
local function sortedhashonly(tab)
if tab then
local srt={}
local s=0
for key in next,tab do
if type(key)=="string" then
s=s+1
srt[s]=key
end
end
if s>1 then
sort(srt)
end
return srt
else
return {}
end
end
local function sortedindexonly(tab)
if tab then
local srt={}
local s=0
for key in next,tab do
if type(key)=="number" then
s=s+1
srt[s]=key
end
end
if s>1 then
sort(srt)
end
return srt
else
return {}
end
end
local function sortedhashkeys(tab,cmp)
if tab then
local srt={}
local s=0
for key in next,tab do
if key then
s=s+1
srt[s]=key
end
end
if s>1 then
sort(srt,cmp)
end
return srt
else
return {}
end
end
function table.allkeys(t)
local keys={}
for k,v in next,t do
for k in next,v do
keys[k]=true
end
end
return sortedkeys(keys)
end
table.sortedkeys=sortedkeys
table.sortedhashonly=sortedhashonly
table.sortedindexonly=sortedindexonly
table.sortedhashkeys=sortedhashkeys
local function nothing() end
local function sortedhash(t,cmp)
if t then
local s
if cmp then
s=sortedhashkeys(t,function(a,b) return cmp(t,a,b) end)
else
s=sortedkeys(t)
end
local m=#s
if m==1 then
return next,t
elseif m>0 then
local n=0
return function()
if n<m then
n=n+1
local k=s[n]
return k,t[k]
end
end
end
end
return nothing
end
table.sortedhash=sortedhash
table.sortedpairs=sortedhash
function table.append(t,list)
local n=#t
for i=1,#list do
n=n+1
t[n]=list[i]
end
return t
end
function table.prepend(t,list)
local nl=#list
local nt=nl+#t
for i=#t,1,-1 do
t[nt]=t[i]
nt=nt-1
end
for i=1,#list do
t[i]=list[i]
end
return t
end
function table.merge(t,...)
if not t then
t={}
end
for i=1,select("#",...) do
for k,v in next,(select(i,...)) do
t[k]=v
end
end
return t
end
function table.merged(...)
local t={}
for i=1,select("#",...) do
for k,v in next,(select(i,...)) do
t[k]=v
end
end
return t
end
function table.imerge(t,...)
local nt=#t
for i=1,select("#",...) do
local nst=select(i,...)
for j=1,#nst do
nt=nt+1
t[nt]=nst[j]
end
end
return t
end
function table.imerged(...)
local tmp={}
local ntmp=0
for i=1,select("#",...) do
local nst=select(i,...)
for j=1,#nst do
ntmp=ntmp+1
tmp[ntmp]=nst[j]
end
end
return tmp
end
local function fastcopy(old,metatabletoo)
if old then
local new={}
for k,v in next,old do
if type(v)=="table" then
new[k]=fastcopy(v,metatabletoo)
else
new[k]=v
end
end
if metatabletoo then
local mt=getmetatable(old)
if mt then
setmetatable(new,mt)
end
end
return new
else
return {}
end
end
local function copy(t,tables)
if not tables then
tables={}
end
local tcopy={}
if not tables[t] then
tables[t]=tcopy
end
for i,v in next,t do
if type(i)=="table" then
if tables[i] then
i=tables[i]
else
i=copy(i,tables)
end
end
if type(v)~="table" then
tcopy[i]=v
elseif tables[v] then
tcopy[i]=tables[v]
else
tcopy[i]=copy(v,tables)
end
end
local mt=getmetatable(t)
if mt then
setmetatable(tcopy,mt)
end
return tcopy
end
table.fastcopy=fastcopy
table.copy=copy
function table.derive(parent)
local child={}
if parent then
setmetatable(child,{ __index=parent })
end
return child
end
function table.tohash(t,value)
local h={}
if t then
if value==nil then value=true end
for _,v in next,t do
h[v]=value
end
end
return h
end
function table.fromhash(t)
local hsh={}
local h=0
for k,v in next,t do
if v then
h=h+1
hsh[h]=k
end
end
return hsh
end
local noquotes,hexify,handle,compact,inline,functions,metacheck
local reserved=table.tohash {
'and','break','do','else','elseif','end','false','for','function','if',
'in','local','nil','not','or','repeat','return','then','true','until','while',
'NaN','goto',
}
local function is_simple_table(t,hexify)
local nt=#t
if nt>0 then
local n=0
for _,v in next,t do
n=n+1
if type(v)=="table" then
return nil
end
end
local haszero=rawget(t,0)
if n==nt then
local tt={}
for i=1,nt do
local v=t[i]
local tv=type(v)
if tv=="number" then
if hexify then
tt[i]=format("0x%X",v)
else
tt[i]=v
end
elseif tv=="string" then
tt[i]=format("%q",v)
elseif tv=="boolean" then
tt[i]=v and "true" or "false"
else
return nil
end
end
return tt
elseif haszero and (n==nt+1) then
local tt={}
for i=0,nt do
local v=t[i]
local tv=type(v)
if tv=="number" then
if hexify then
tt[i+1]=format("0x%X",v)
else
tt[i+1]=v
end
elseif tv=="string" then
tt[i+1]=format("%q",v)
elseif tv=="boolean" then
tt[i+1]=v and "true" or "false"
else
return nil
end
end
tt[1]="[0] = "..tt[1]
return tt
end
end
return nil
end
table.is_simple_table=is_simple_table
local propername=patterns.propername
local function dummy() end
local function do_serialize(root,name,depth,level,indexed)
if level>0 then
depth=depth.." "
if indexed then
handle(format("%s{",depth))
else
local tn=type(name)
if tn=="number" then
if hexify then
handle(format("%s[0x%X]={",depth,name))
else
handle(format("%s[%s]={",depth,name))
end
elseif tn=="string" then
if noquotes and not reserved[name] and lpegmatch(propername,name) then
handle(format("%s%s={",depth,name))
else
handle(format("%s[%q]={",depth,name))
end
elseif tn=="boolean" then
handle(format("%s[%s]={",depth,name and "true" or "false"))
else
handle(format("%s{",depth))
end
end
end
if root and next(root)~=nil then
local first=nil
local last=0
if compact then
last=#root
for k=1,last do
if rawget(root,k)==nil then
last=k-1
break
end
end
if last>0 then
first=1
end
end
local sk=sortedkeys(root)
for i=1,#sk do
local k=sk[i]
local v=root[k]
local tv=type(v)
local tk=type(k)
if compact and first and tk=="number" and k>=first and k<=last then
if tv=="number" then
if hexify then
handle(format("%s 0x%X,",depth,v))
else
handle(format("%s %s,",depth,v))
end
elseif tv=="string" then
handle(format("%s %q,",depth,v))
elseif tv=="table" then
if next(v)==nil then
handle(format("%s {},",depth))
elseif inline then
local st=is_simple_table(v,hexify)
if st then
handle(format("%s { %s },",depth,concat(st,", ")))
else
do_serialize(v,k,depth,level+1,true)
end
else
do_serialize(v,k,depth,level+1,true)
end
elseif tv=="boolean" then
handle(format("%s %s,",depth,v and "true" or "false"))
elseif tv=="function" then
if functions then
handle(format('%s load(%q),',depth,dump(v)))
else
handle(format('%s "function",',depth))
end
else
handle(format("%s %q,",depth,tostring(v)))
end
elseif k=="__p__" then
if false then
handle(format("%s __p__=nil,",depth))
end
elseif tv=="number" then
if tk=="number" then
if hexify then
handle(format("%s [0x%X]=0x%X,",depth,k,v))
else
handle(format("%s [%s]=%s,",depth,k,v))
end
elseif tk=="boolean" then
if hexify then
handle(format("%s [%s]=0x%X,",depth,k and "true" or "false",v))
else
handle(format("%s [%s]=%s,",depth,k and "true" or "false",v))
end
elseif tk~="string" then
elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
if hexify then
handle(format("%s %s=0x%X,",depth,k,v))
else
handle(format("%s %s=%s,",depth,k,v))
end
else
if hexify then
handle(format("%s [%q]=0x%X,",depth,k,v))
else
handle(format("%s [%q]=%s,",depth,k,v))
end
end
elseif tv=="string" then
if tk=="number" then
if hexify then
handle(format("%s [0x%X]=%q,",depth,k,v))
else
handle(format("%s [%s]=%q,",depth,k,v))
end
elseif tk=="boolean" then
handle(format("%s [%s]=%q,",depth,k and "true" or "false",v))
elseif tk~="string" then
elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
handle(format("%s %s=%q,",depth,k,v))
else
handle(format("%s [%q]=%q,",depth,k,v))
end
elseif tv=="table" then
if next(v)==nil then
if tk=="number" then
if hexify then
handle(format("%s [0x%X]={},",depth,k))
else
handle(format("%s [%s]={},",depth,k))
end
elseif tk=="boolean" then
handle(format("%s [%s]={},",depth,k and "true" or "false"))
elseif tk~="string" then
elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
handle(format("%s %s={},",depth,k))
else
handle(format("%s [%q]={},",depth,k))
end
elseif inline then
local st=is_simple_table(v,hexify)
if st then
if tk=="number" then
if hexify then
handle(format("%s [0x%X]={ %s },",depth,k,concat(st,", ")))
else
handle(format("%s [%s]={ %s },",depth,k,concat(st,", ")))
end
elseif tk=="boolean" then
handle(format("%s [%s]={ %s },",depth,k and "true" or "false",concat(st,", ")))
elseif tk~="string" then
elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
handle(format("%s %s={ %s },",depth,k,concat(st,", ")))
else
handle(format("%s [%q]={ %s },",depth,k,concat(st,", ")))
end
else
do_serialize(v,k,depth,level+1)
end
else
do_serialize(v,k,depth,level+1)
end
elseif tv=="boolean" then
if tk=="number" then
if hexify then
handle(format("%s [0x%X]=%s,",depth,k,v and "true" or "false"))
else
handle(format("%s [%s]=%s,",depth,k,v and "true" or "false"))
end
elseif tk=="boolean" then
handle(format("%s [%s]=%s,",depth,tostring(k),v and "true" or "false"))
elseif tk~="string" then
elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
handle(format("%s %s=%s,",depth,k,v and "true" or "false"))
else
handle(format("%s [%q]=%s,",depth,k,v and "true" or "false"))
end
elseif tv=="function" then
if functions then
local getinfo=debug and debug.getinfo
if getinfo then
local f=getinfo(v).what=="C" and dump(dummy) or dump(v)
if tk=="number" then
if hexify then
handle(format("%s [0x%X]=load(%q),",depth,k,f))
else
handle(format("%s [%s]=load(%q),",depth,k,f))
end
elseif tk=="boolean" then
handle(format("%s [%s]=load(%q),",depth,k and "true" or "false",f))
elseif tk~="string" then
elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
handle(format("%s %s=load(%q),",depth,k,f))
else
handle(format("%s [%q]=load(%q),",depth,k,f))
end
end
end
else
if tk=="number" then
if hexify then
handle(format("%s [0x%X]=%q,",depth,k,tostring(v)))
else
handle(format("%s [%s]=%q,",depth,k,tostring(v)))
end
elseif tk=="boolean" then
handle(format("%s [%s]=%q,",depth,k and "true" or "false",tostring(v)))
elseif tk~="string" then
elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
handle(format("%s %s=%q,",depth,k,tostring(v)))
else
handle(format("%s [%q]=%q,",depth,k,tostring(v)))
end
end
end
end
if level>0 then
handle(format("%s},",depth))
end
end
local function serialize(_handle,root,name,specification)
local tname=type(name)
if type(specification)=="table" then
noquotes=specification.noquotes
hexify=specification.hexify
handle=_handle or specification.handle or print
functions=specification.functions
compact=specification.compact
inline=specification.inline and compact
metacheck=specification.metacheck
if functions==nil then
functions=true
end
if compact==nil then
compact=true
end
if inline==nil then
inline=compact
end
if metacheck==nil then
metacheck=true
end
else
noquotes=false
hexify=false
handle=_handle or print
compact=true
inline=true
functions=true
metacheck=true
end
if tname=="string" then
if name=="return" then
handle("return {")
else
handle(name.."={")
end
elseif tname=="number" then
if hexify then
handle(format("[0x%X]={",name))
else
handle("["..name.."]={")
end
elseif tname=="boolean" then
if name then
handle("return {")
else
handle("{")
end
else
handle("t={")
end
if root then
if metacheck and getmetatable(root) then
local dummy=root._w_h_a_t_e_v_e_r_
root._w_h_a_t_e_v_e_r_=nil
end
if next(root)~=nil then
do_serialize(root,name,"",0)
end
end
handle("}")
end
function table.serialize(root,name,specification)
local t={}
local n=0
local function flush(s)
n=n+1
t[n]=s
end
serialize(flush,root,name,specification)
return concat(t,"\n")
end
table.tohandle=serialize
local maxtab=2*1024
function table.tofile(filename,root,name,specification)
local f=io.open(filename,'w')
if f then
if maxtab>1 then
local t={}
local n=0
local function flush(s)
n=n+1
t[n]=s
if n>maxtab then
f:write(concat(t,"\n"),"\n")
t={}
n=0
end
end
serialize(flush,root,name,specification)
f:write(concat(t,"\n"),"\n")
else
local function flush(s)
f:write(s,"\n")
end
serialize(flush,root,name,specification)
end
f:close()
io.flush()
end
end
local function flattened(t,f,depth)
if f==nil then
f={}
depth=0xFFFF
elseif tonumber(f) then
depth=f
f={}
elseif not depth then
depth=0xFFFF
end
for k,v in next,t do
if type(k)~="number" then
if depth>0 and type(v)=="table" then
flattened(v,f,depth-1)
else
f[#f+1]=v
end
end
end
for k=1,#t do
local v=t[k]
if depth>0 and type(v)=="table" then
flattened(v,f,depth-1)
else
f[#f+1]=v
end
end
return f
end
table.flattened=flattened
local function collapsed(t,f,h)
if f==nil then
f={}
h={}
end
for k=1,#t do
local v=t[k]
if type(v)=="table" then
collapsed(v,f,h)
elseif not h[v] then
f[#f+1]=v
h[v]=true
end
end
return f
end
local function collapsedhash(t,h)
if h==nil then
h={}
end
for k=1,#t do
local v=t[k]
if type(v)=="table" then
collapsedhash(v,h)
else
h[v]=true
end
end
return h
end
table.collapsed=collapsed
table.collapsedhash=collapsedhash
local function unnest(t,f)
if not f then
f={}
end
for i=1,#t do
local v=t[i]
if type(v)=="table" then
if type(v[1])=="table" then
unnest(v,f)
else
f[#f+1]=v
end
else
f[#f+1]=v
end
end
return f
end
function table.unnest(t)
return unnest(t)
end
local function are_equal(a,b,n,m)
if a==b then
return true
elseif a and b and #a==#b then
if not n then
n=1
end
if not m then
m=#a
end
for i=n,m do
local ai,bi=a[i],b[i]
if ai==bi then
elseif type(ai)=="table" and type(bi)=="table" then
if not are_equal(ai,bi) then
return false
end
else
return false
end
end
return true
else
return false
end
end
local function identical(a,b)
if a~=b then
for ka,va in next,a do
local vb=b[ka]
if va==vb then
elseif type(va)=="table" and type(vb)=="table" then
if not identical(va,vb) then
return false
end
else
return false
end
end
end
return true
end
table.identical=identical
table.are_equal=are_equal
local function sparse(old,nest,keeptables)
local new={}
for k,v in next,old do
if not (v=="" or v==false) then
if nest and type(v)=="table" then
v=sparse(v,nest)
if keeptables or next(v)~=nil then
new[k]=v
end
else
new[k]=v
end
end
end
return new
end
table.sparse=sparse
function table.compact(t)
return sparse(t,true,true)
end
function table.contains(t,v)
if t then
for i=1,#t do
if t[i]==v then
return i
end
end
end
return false
end
function table.count(t)
local n=0
for k,v in next,t do
n=n+1
end
return n
end
function table.swapped(t,s)
local n={}
if s then
for k,v in next,s do
n[k]=v
end
end
for k,v in next,t do
n[v]=k
end
return n
end
function table.hashed(t)
for i=1,#t do
t[t[i]]=i
end
return t
end
function table.mirrored(t)
local n={}
for k,v in next,t do
n[v]=k
n[k]=v
end
return n
end
function table.reversed(t)
if t then
local tt={}
local tn=#t
if tn>0 then
local ttn=0
for i=tn,1,-1 do
ttn=ttn+1
tt[ttn]=t[i]
end
end
return tt
end
end
function table.reverse(t)
if t then
local n=#t
local m=n+1
for i=1,floor(n/2) do
local j=m-i
t[i],t[j]=t[j],t[i]
end
return t
end
end
local function sequenced(t,sep,simple)
if not t then
return ""
elseif type(t)=="string" then
return t
end
local n=#t
local s={}
if n>0 then
for i=1,n do
local v=t[i]
if type(v)=="table" then
s[i]="{"..sequenced(v,sep,simple).."}"
else
s[i]=tostring(t[i])
end
end
else
n=0
for k,v in sortedhash(t) do
if simple then
if v==true then
n=n+1
s[n]=k
elseif v and v~="" then
n=n+1
if type(v)=="table" then
s[n]=k.."={"..sequenced(v,sep,simple).."}"
else
s[n]=k.."="..tostring(v)
end
end
else
n=n+1
if type(v)=="table" then
s[n]=k.."={"..sequenced(v,sep,simple).."}"
else
s[n]=k.."="..tostring(v)
end
end
end
end
return concat(s,sep or " | ")
end
table.sequenced=sequenced
function table.print(t,...)
if type(t)~="table" then
print(tostring(t))
else
serialize(print,t,...)
end
end
if setinspector then
setinspector("table",function(v) if type(v)=="table" then serialize(print,v,"table") return true end end)
end
function table.sub(t,i,j)
return { unpack(t,i,j) }
end
function table.is_empty(t)
return not t or next(t)==nil
end
function table.has_one_entry(t)
return t and next(t,next(t))==nil
end
function table.loweredkeys(t)
local l={}
for k,v in next,t do
l[lower(k)]=v
end
return l
end
function table.unique(old)
local hash={}
local new={}
local n=0
for i=1,#old do
local oi=old[i]
if not hash[oi] then
n=n+1
new[n]=oi
hash[oi]=true
end
end
return new
end
function table.sorted(t,...)
sort(t,...)
return t
end
function table.values(t,s)
if t then
local values={}
local keys={}
local v=0
for key,value in next,t do
if not keys[value] then
v=v+1
values[v]=value
keys[k]=key
end
end
if s then
sort(values)
end
return values
else
return {}
end
end
function table.filtered(t,pattern,sort,cmp)
if t and type(pattern)=="string" then
if sort then
local s
if cmp then
s=sortedhashkeys(t,function(a,b) return cmp(t,a,b) end)
else
s=sortedkeys(t)
end
local n=0
local m=#s
local function kv(s)
while n<m do
n=n+1
local k=s[n]
if find(k,pattern) then
return k,t[k]
end
end
end
return kv,s
else
local n=next(t)
local function iterator()
while n~=nil do
local k=n
n=next(t,k)
if find(k,pattern) then
return k,t[k]
end
end
end
return iterator,t
end
else
return nothing
end
end
if not table.move then
function table.move(a1,f,e,t,a2)
if a2 and a1~=a2 then
for i=f,e do
a2[t]=a1[i]
t=t+1
end
return a2
else
t=t+e-f
for i=e,f,-1 do
a1[t]=a1[i]
t=t-1
end
return a1
end
end
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["l-io"] = package.loaded["l-io"] or true
-- original size: 11823, stripped down to: 6325
if not modules then modules={} end modules ['l-io']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local io=io
local open,flush,write,read=io.open,io.flush,io.write,io.read
local byte,find,gsub,format=string.byte,string.find,string.gsub,string.format
local concat=table.concat
local type=type
if string.find(os.getenv("PATH"),";",1,true) then
io.fileseparator,io.pathseparator="\\",";"
else
io.fileseparator,io.pathseparator="/",":"
end
local large=0x01000000
local medium=0x00100000
local small=0x00020000
local function readall(f)
local size=f:seek("end")
if size>0 then
f:seek("set",0)
return f:read(size)
else
return ""
end
end
io.readall=readall
function io.loaddata(filename,textmode)
local f=open(filename,(textmode and 'r') or 'rb')
if f then
local size=f:seek("end")
local data=nil
if size>0 then
f:seek("set",0)
data=f:read(size)
end
f:close()
return data
end
end
function io.copydata(source,target,action)
local f=open(source,"rb")
if f then
local g=open(target,"wb")
if g then
local size=f:seek("end")
if size>0 then
f:seek("set",0)
local data=f:read(size)
if action then
data=action(data)
end
if data then
g:write(data)
end
end
g:close()
end
f:close()
flush()
end
end
function io.savedata(filename,data,joiner)
local f=open(filename,"wb")
if f then
if type(data)=="table" then
f:write(concat(data,joiner or ""))
elseif type(data)=="function" then
data(f)
else
f:write(data or "")
end
f:close()
flush()
return true
else
return false
end
end
if fio and fio.readline then
local readline=fio.readline
function io.loadlines(filename,n)
local f=open(filename,'r')
if not f then
elseif n then
local lines={}
for i=1,n do
local line=readline(f)
if line then
lines[i]=line
else
break
end
end
f:close()
lines=concat(lines,"\n")
if #lines>0 then
return lines
end
else
local line=readline(f)
f:close()
if line and #line>0 then
return line
end
end
end
else
function io.loadlines(filename,n)
local f=open(filename,'r')
if not f then
elseif n then
local lines={}
for i=1,n do
local line=f:read("*lines")
if line then
lines[i]=line
else
break
end
end
f:close()
lines=concat(lines,"\n")
if #lines>0 then
return lines
end
else
local line=f:read("*line") or ""
f:close()
if #line>0 then
return line
end
end
end
end
function io.loadchunk(filename,n)
local f=open(filename,'rb')
if f then
local data=f:read(n or 1024)
f:close()
if #data>0 then
return data
end
end
end
function io.exists(filename)
local f=open(filename)
if f==nil then
return false
else
f:close()
return true
end
end
function io.size(filename)
local f=open(filename)
if f==nil then
return 0
else
local s=f:seek("end")
f:close()
return s
end
end
local function noflines(f)
if type(f)=="string" then
local f=open(filename)
if f then
local n=f and noflines(f) or 0
f:close()
return n
else
return 0
end
else
local n=0
for _ in f:lines() do
n=n+1
end
f:seek('set',0)
return n
end
end
io.noflines=noflines
local nextchar={
[ 4]=function(f)
return f:read(1,1,1,1)
end,
[ 2]=function(f)
return f:read(1,1)
end,
[ 1]=function(f)
return f:read(1)
end,
[-2]=function(f)
local a,b=f:read(1,1)
return b,a
end,
[-4]=function(f)
local a,b,c,d=f:read(1,1,1,1)
return d,c,b,a
end
}
function io.characters(f,n)
if f then
return nextchar[n or 1],f
end
end
local nextbyte={
[4]=function(f)
local a,b,c,d=f:read(1,1,1,1)
if d then
return byte(a),byte(b),byte(c),byte(d)
end
end,
[3]=function(f)
local a,b,c=f:read(1,1,1)
if b then
return byte(a),byte(b),byte(c)
end
end,
[2]=function(f)
local a,b=f:read(1,1)
if b then
return byte(a),byte(b)
end
end,
[1]=function (f)
local a=f:read(1)
if a then
return byte(a)
end
end,
[-2]=function (f)
local a,b=f:read(1,1)
if b then
return byte(b),byte(a)
end
end,
[-3]=function(f)
local a,b,c=f:read(1,1,1)
if b then
return byte(c),byte(b),byte(a)
end
end,
[-4]=function(f)
local a,b,c,d=f:read(1,1,1,1)
if d then
return byte(d),byte(c),byte(b),byte(a)
end
end
}
function io.bytes(f,n)
if f then
return nextbyte[n or 1],f
else
return nil,nil
end
end
function io.ask(question,default,options)
while true do
write(question)
if options then
write(format(" [%s]",concat(options,"|")))
end
if default then
write(format(" [%s]",default))
end
write(format(" "))
flush()
local answer=read()
answer=gsub(answer,"^%s*(.*)%s*$","%1")
if answer=="" and default then
return default
elseif not options then
return answer
else
for k=1,#options do
if options[k]==answer then
return answer
end
end
local pattern="^"..answer
for k=1,#options do
local v=options[k]
if find(v,pattern) then
return v
end
end
end
end
end
local function readnumber(f,n,m)
if m then
f:seek("set",n)
n=m
end
if n==1 then
return byte(f:read(1))
elseif n==2 then
local a,b=byte(f:read(2),1,2)
return 0x100*a+b
elseif n==3 then
local a,b,c=byte(f:read(3),1,3)
return 0x10000*a+0x100*b+c
elseif n==4 then
local a,b,c,d=byte(f:read(4),1,4)
return 0x1000000*a+0x10000*b+0x100*c+d
elseif n==8 then
local a,b=readnumber(f,4),readnumber(f,4)
return 0x100*a+b
elseif n==12 then
local a,b,c=readnumber(f,4),readnumber(f,4),readnumber(f,4)
return 0x10000*a+0x100*b+c
elseif n==-2 then
local b,a=byte(f:read(2),1,2)
return 0x100*a+b
elseif n==-3 then
local c,b,a=byte(f:read(3),1,3)
return 0x10000*a+0x100*b+c
elseif n==-4 then
local d,c,b,a=byte(f:read(4),1,4)
return 0x1000000*a+0x10000*b+0x100*c+d
elseif n==-8 then
local h,g,f,e,d,c,b,a=byte(f:read(8),1,8)
return 0x100000000000000*a+0x1000000000000*b+0x10000000000*c+0x100000000*d+0x1000000*e+0x10000*f+0x100*g+h
else
return 0
end
end
io.readnumber=readnumber
function io.readstring(f,n,m)
if m then
f:seek("set",n)
n=m
end
local str=gsub(f:read(n),"\000","")
return str
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["l-number"] = package.loaded["l-number"] or true
-- original size: 5720, stripped down to: 2176
if not modules then modules={} end modules ['l-number']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local tostring,tonumber=tostring,tonumber
local format,floor,match,rep=string.format,math.floor,string.match,string.rep
local concat,insert=table.concat,table.insert
local lpegmatch=lpeg.match
local floor=math.floor
number=number or {}
local number=number
if bit32 then
local bextract=bit32.extract
local t={
"0","0","0","0","0","0","0","0",
"0","0","0","0","0","0","0","0",
"0","0","0","0","0","0","0","0",
"0","0","0","0","0","0","0","0",
}
function number.tobitstring(b,m,w)
if not w then
w=32
end
local n=w
for i=0,w-1 do
local v=bextract(b,i)
local k=w-i
if v==1 then
n=k
t[k]="1"
else
t[k]="0"
end
end
if w then
return concat(t,"",1,w)
elseif m then
m=33-m*8
if m<1 then
m=1
end
return concat(t,"",1,m)
elseif n<8 then
return concat(t)
elseif n<16 then
return concat(t,"",9)
elseif n<24 then
return concat(t,"",17)
else
return concat(t,"",25)
end
end
else
function number.tobitstring(n,m)
if n>0 then
local t={}
while n>0 do
insert(t,1,n%2>0 and 1 or 0)
n=floor(n/2)
end
local nn=8-#t%8
if nn>0 and nn<8 then
for i=1,nn do
insert(t,1,0)
end
end
if m then
m=m*8-#t
if m>0 then
insert(t,1,rep("0",m))
end
end
return concat(t)
elseif m then
rep("00000000",m)
else
return "00000000"
end
end
end
function number.valid(str,default)
return tonumber(str) or default or nil
end
function number.toevenhex(n)
local s=format("%X",n)
if #s%2==0 then
return s
else
return "0"..s
end
end
function number.bytetodecimal(b)
local d=floor(b*100/255+0.5)
if d>100 then
return 100
elseif d<-100 then
return -100
else
return d
end
end
function number.decimaltobyte(d)
local b=floor(d*255/100+0.5)
if b>255 then
return 255
elseif b<-255 then
return -255
else
return b
end
end
function number.idiv(i,d)
return floor(i/d)
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["l-set"] = package.loaded["l-set"] or true
-- original size: 1923, stripped down to: 1044
if not modules then modules={} end modules ['l-set']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
set=set or {}
local nums={}
local tabs={}
local concat=table.concat
local next,type=next,type
set.create=table.tohash
function set.tonumber(t)
if next(t) then
local s=""
for k,v in next,t do
if v then
s=s.." "..k
end
end
local n=nums[s]
if not n then
n=#tabs+1
tabs[n]=t
nums[s]=n
end
return n
else
return 0
end
end
function set.totable(n)
if n==0 then
return {}
else
return tabs[n] or {}
end
end
function set.tolist(n)
if n==0 or not tabs[n] then
return ""
else
local t,n={},0
for k,v in next,tabs[n] do
if v then
n=n+1
t[n]=k
end
end
return concat(t," ")
end
end
function set.contains(n,s)
if type(n)=="table" then
return n[s]
elseif n==0 then
return false
else
local t=tabs[n]
return t and t[s]
end
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["l-os"] = package.loaded["l-os"] or true
-- original size: 18916, stripped down to: 10126
if not modules then modules={} end modules ['l-os']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local os=os
local date,time=os.date,os.time
local find,format,gsub,upper,gmatch=string.find,string.format,string.gsub,string.upper,string.gmatch
local concat=table.concat
local random,ceil,randomseed=math.random,math.ceil,math.randomseed
local rawget,rawset,type,getmetatable,setmetatable,tonumber,tostring=rawget,rawset,type,getmetatable,setmetatable,tonumber,tostring
do
local selfdir=os.selfdir
if selfdir=="" then
selfdir=nil
end
if not selfdir then
if arg then
for i=1,#arg do
local a=arg[i]
if find(a,"^%-%-[c:]*texmfbinpath=") then
selfdir=gsub(a,"^.-=","")
break
end
end
end
if not selfdir then
selfdir=os.selfbin or "luatex"
if find(selfdir,"[/\\]") then
selfdir=gsub(selfdir,"[/\\][^/\\]*$","")
elseif os.getenv then
local path=os.getenv("PATH")
local name=gsub(selfdir,"^.*[/\\][^/\\]","")
local patt="[^:]+"
if os.type=="windows" then
patt="[^;]+"
name=name..".exe"
end
local isfile
if lfs then
local attributes=lfs.attributes
isfile=function(name)
local a=attributes(name,"mode")
return a=="file" or a=="link" or nil
end
else
local open=io.open
isfile=function(name)
local f=open(name)
if f then
f:close()
return true
end
end
end
for p in gmatch(path,patt) do
if isfile(p.."/"..name) then
selfdir=p
break
end
end
end
end
os.selfdir=selfdir or "."
end
end
math.initialseed=tonumber(string.sub(string.reverse(tostring(ceil(socket and socket.gettime()*10000 or time()))),1,6))
randomseed(math.initialseed)
if not os.__getenv__ then
os.__getenv__=os.getenv
os.__setenv__=os.setenv
if os.env then
local osgetenv=os.getenv
local ossetenv=os.setenv
local osenv=os.env local _=osenv.PATH
function os.setenv(k,v)
if v==nil then
v=""
end
local K=upper(k)
osenv[K]=v
if type(v)=="table" then
v=concat(v,";")
end
ossetenv(K,v)
end
function os.getenv(k)
local K=upper(k)
local v=osenv[K] or osenv[k] or osgetenv(K) or osgetenv(k)
if v=="" then
return nil
else
return v
end
end
else
local ossetenv=os.setenv
local osgetenv=os.getenv
local osenv={}
function os.setenv(k,v)
if v==nil then
v=""
end
local K=upper(k)
osenv[K]=v
end
function os.getenv(k)
local K=upper(k)
local v=osenv[K] or osgetenv(K) or osgetenv(k)
if v=="" then
return nil
else
return v
end
end
local function __index(t,k)
return os.getenv(k)
end
local function __newindex(t,k,v)
os.setenv(k,v)
end
os.env={}
setmetatable(os.env,{ __index=__index,__newindex=__newindex } )
end
end
local execute=os.execute
local iopopen=io.popen
local function resultof(command)
local handle=iopopen(command,"r")
if handle then
local result=handle:read("*all") or ""
handle:close()
return result
else
return ""
end
end
os.resultof=resultof
function os.pipeto(command)
return iopopen(command,"w")
end
if not io.fileseparator then
if find(os.getenv("PATH"),";",1,true) then
io.fileseparator,io.pathseparator,os.type="\\",";",os.type or "windows"
else
io.fileseparator,io.pathseparator,os.type="/",":",os.type or "unix"
end
end
os.type=os.type or (io.pathseparator==";" and "windows") or "unix"
os.name=os.name or (os.type=="windows" and "mswin" ) or "linux"
if os.type=="windows" then
os.libsuffix,os.binsuffix,os.binsuffixes='dll','exe',{ 'exe','cmd','bat' }
else
os.libsuffix,os.binsuffix,os.binsuffixes='so','',{ '' }
end
local launchers={
windows="start %s",
macosx="open %s",
unix="xdg-open %s &> /dev/null &",
}
function os.launch(str)
execute(format(launchers[os.name] or launchers.unix,str))
end
local gettimeofday=os.gettimeofday or os.clock
os.gettimeofday=gettimeofday
local startuptime=gettimeofday()
function os.runtime()
return gettimeofday()-startuptime
end
local resolvers=os.resolvers or {}
os.resolvers=resolvers
setmetatable(os,{ __index=function(t,k)
local r=resolvers[k]
return r and r(t,k) or nil
end })
local name,platform=os.name or "linux",os.getenv("MTX_PLATFORM") or ""
if platform~="" then
os.platform=platform
elseif os.type=="windows" then
function resolvers.platform(t,k)
local architecture=os.getenv("PROCESSOR_ARCHITECTURE") or ""
local platform=""
if find(architecture,"AMD64",1,true) then
platform="win64"
else
platform="mswin"
end
os.setenv("MTX_PLATFORM",platform)
os.platform=platform
return platform
end
elseif name=="linux" then
function resolvers.platform(t,k)
local architecture=os.getenv("HOSTTYPE") or resultof("uname -m") or ""
local platform=os.getenv("MTX_PLATFORM") or ""
local musl=find(os.selfdir or "","linuxmusl")
if platform~="" then
elseif find(architecture,"x86_64",1,true) then
platform=musl and "linuxmusl" or "linux-64"
elseif find(architecture,"ppc",1,true) then
platform="linux-ppc"
else
platform=musl and "linuxmusl" or "linux"
end
os.setenv("MTX_PLATFORM",platform)
os.platform=platform
return platform
end
elseif name=="macosx" then
function resolvers.platform(t,k)
local architecture=resultof("echo $HOSTTYPE") or ""
local platform=""
if architecture=="" then
platform="osx-intel"
elseif find(architecture,"i386",1,true) then
platform="osx-intel"
elseif find(architecture,"x86_64",1,true) then
platform="osx-64"
else
platform="osx-ppc"
end
os.setenv("MTX_PLATFORM",platform)
os.platform=platform
return platform
end
elseif name=="sunos" then
function resolvers.platform(t,k)
local architecture=resultof("uname -m") or ""
local platform=""
if find(architecture,"sparc",1,true) then
platform="solaris-sparc"
else
platform="solaris-intel"
end
os.setenv("MTX_PLATFORM",platform)
os.platform=platform
return platform
end
elseif name=="freebsd" then
function resolvers.platform(t,k)
local architecture=resultof("uname -m") or ""
local platform=""
if find(architecture,"amd64",1,true) then
platform="freebsd-amd64"
else
platform="freebsd"
end
os.setenv("MTX_PLATFORM",platform)
os.platform=platform
return platform
end
elseif name=="kfreebsd" then
function resolvers.platform(t,k)
local architecture=os.getenv("HOSTTYPE") or resultof("uname -m") or ""
local platform=""
if find(architecture,"x86_64",1,true) then
platform="kfreebsd-amd64"
else
platform="kfreebsd-i386"
end
os.setenv("MTX_PLATFORM",platform)
os.platform=platform
return platform
end
else
function resolvers.platform(t,k)
local platform="linux"
os.setenv("MTX_PLATFORM",platform)
os.platform=platform
return platform
end
end
os.newline=name=="windows" and "\013\010" or "\010"
function resolvers.bits(t,k)
local bits=find(os.platform,"64",1,true) and 64 or 32
os.bits=bits
return bits
end
local t={ 8,9,"a","b" }
function os.uuid()
return format("%04x%04x-4%03x-%s%03x-%04x-%04x%04x%04x",
random(0xFFFF),random(0xFFFF),
random(0x0FFF),
t[ceil(random(4))] or 8,random(0x0FFF),
random(0xFFFF),
random(0xFFFF),random(0xFFFF),random(0xFFFF)
)
end
local d
function os.timezone(delta)
d=d or tonumber(tonumber(date("%H")-date("!%H")))
if delta then
if d>0 then
return format("+%02i:00",d)
else
return format("-%02i:00",-d)
end
else
return 1
end
end
local timeformat=format("%%s%s",os.timezone(true))
local dateformat="!%Y-%m-%d %H:%M:%S"
local lasttime=nil
local lastdate=nil
function os.fulltime(t,default)
t=t and tonumber(t) or 0
if t>0 then
elseif default then
return default
else
t=time()
end
if t~=lasttime then
lasttime=t
lastdate=format(timeformat,date(dateformat))
end
return lastdate
end
local dateformat="%Y-%m-%d %H:%M:%S"
local lasttime=nil
local lastdate=nil
function os.localtime(t,default)
t=t and tonumber(t) or 0
if t>0 then
elseif default then
return default
else
t=time()
end
if t~=lasttime then
lasttime=t
lastdate=date(dateformat,t)
end
return lastdate
end
function os.converttime(t,default)
local t=tonumber(t)
if t and t>0 then
return date(dateformat,t)
else
return default or "-"
end
end
local memory={}
local function which(filename)
local fullname=memory[filename]
if fullname==nil then
local suffix=file.suffix(filename)
local suffixes=suffix=="" and os.binsuffixes or { suffix }
for directory in gmatch(os.getenv("PATH"),"[^"..io.pathseparator.."]+") do
local df=file.join(directory,filename)
for i=1,#suffixes do
local dfs=file.addsuffix(df,suffixes[i])
if io.exists(dfs) then
fullname=dfs
break
end
end
end
if not fullname then
fullname=false
end
memory[filename]=fullname
end
return fullname
end
os.which=which
os.where=which
function os.today()
return date("!*t")
end
function os.now()
return date("!%Y-%m-%d %H:%M:%S")
end
if not os.sleep then
local socket=socket
function os.sleep(n)
if not socket then
socket=require("socket")
end
socket.sleep(n)
end
end
local function isleapyear(year)
return (year%4==0) and (year%100~=0 or year%400==0)
end
os.isleapyear=isleapyear
local days={ 31,28,31,30,31,30,31,31,30,31,30,31 }
local function nofdays(year,month)
if not month then
return isleapyear(year) and 365 or 364
else
return month==2 and isleapyear(year) and 29 or days[month]
end
end
os.nofdays=nofdays
function os.weekday(day,month,year)
return date("%w",time { year=year,month=month,day=day })+1
end
function os.validdate(year,month,day)
if month<1 then
month=1
elseif month>12 then
month=12
end
if day<1 then
day=1
else
local max=nofdays(year,month)
if day>max then
day=max
end
end
return year,month,day
end
local osexit=os.exit
local exitcode=nil
function os.setexitcode(code)
exitcode=code
end
function os.exit(c)
if exitcode~=nil then
return osexit(exitcode)
end
if c~=nil then
return osexit(c)
end
return osexit()
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["l-file"] = package.loaded["l-file"] or true
-- original size: 21984, stripped down to: 10148
if not modules then modules={} end modules ['l-file']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
file=file or {}
local file=file
if not lfs then
lfs=optionalrequire("lfs")
end
local insert,concat=table.insert,table.concat
local match,find,gmatch=string.match,string.find,string.gmatch
local lpegmatch=lpeg.match
local getcurrentdir,attributes=lfs.currentdir,lfs.attributes
local checkedsplit=string.checkedsplit
local P,R,S,C,Cs,Cp,Cc,Ct=lpeg.P,lpeg.R,lpeg.S,lpeg.C,lpeg.Cs,lpeg.Cp,lpeg.Cc,lpeg.Ct
local attributes=lfs.attributes
function lfs.isdir(name)
return attributes(name,"mode")=="directory"
end
function lfs.isfile(name)
local a=attributes(name,"mode")
return a=="file" or a=="link" or nil
end
function lfs.isfound(name)
local a=attributes(name,"mode")
return (a=="file" or a=="link") and name or nil
end
if sandbox then
sandbox.redefine(lfs.isfile,"lfs.isfile")
sandbox.redefine(lfs.isdir,"lfs.isdir")
sandbox.redefine(lfs.isfound,"lfs.isfound")
end
local colon=P(":")
local period=P(".")
local periods=P("..")
local fwslash=P("/")
local bwslash=P("\\")
local slashes=S("\\/")
local noperiod=1-period
local noslashes=1-slashes
local name=noperiod^1
local suffix=period/""*(1-period-slashes)^1*-1
local pattern=C((1-(slashes^1*noslashes^1*-1))^1)*P(1)
local function pathpart(name,default)
return name and lpegmatch(pattern,name) or default or ""
end
local pattern=(noslashes^0*slashes)^1*C(noslashes^1)*-1
local function basename(name)
return name and lpegmatch(pattern,name) or name
end
local pattern=(noslashes^0*slashes^1)^0*Cs((1-suffix)^1)*suffix^0
local function nameonly(name)
return name and lpegmatch(pattern,name) or name
end
local pattern=(noslashes^0*slashes)^0*(noperiod^1*period)^1*C(noperiod^1)*-1
local function suffixonly(name)
return name and lpegmatch(pattern,name) or ""
end
local pattern=(noslashes^0*slashes)^0*noperiod^1*((period*C(noperiod^1))^1)*-1+Cc("")
local function suffixesonly(name)
if name then
return lpegmatch(pattern,name)
else
return ""
end
end
file.pathpart=pathpart
file.basename=basename
file.nameonly=nameonly
file.suffixonly=suffixonly
file.suffix=suffixonly
file.suffixesonly=suffixesonly
file.suffixes=suffixesonly
file.dirname=pathpart
file.extname=suffixonly
local drive=C(R("az","AZ"))*colon
local path=C((noslashes^0*slashes)^0)
local suffix=period*C(P(1-period)^0*P(-1))
local base=C((1-suffix)^0)
local rest=C(P(1)^0)
drive=drive+Cc("")
path=path+Cc("")
base=base+Cc("")
suffix=suffix+Cc("")
local pattern_a=drive*path*base*suffix
local pattern_b=path*base*suffix
local pattern_c=C(drive*path)*C(base*suffix)
local pattern_d=path*rest
function file.splitname(str,splitdrive)
if not str then
elseif splitdrive then
return lpegmatch(pattern_a,str)
else
return lpegmatch(pattern_b,str)
end
end
function file.splitbase(str)
if str then
return lpegmatch(pattern_d,str)
else
return "",str
end
end
function file.nametotable(str,splitdrive)
if str then
local path,drive,subpath,name,base,suffix=lpegmatch(pattern_c,str)
if splitdrive then
return {
path=path,
drive=drive,
subpath=subpath,
name=name,
base=base,
suffix=suffix,
}
else
return {
path=path,
name=name,
base=base,
suffix=suffix,
}
end
end
end
local pattern=Cs(((period*(1-period-slashes)^1*-1)/""+1)^1)
function file.removesuffix(name)
return name and lpegmatch(pattern,name)
end
local suffix=period/""*(1-period-slashes)^1*-1
local pattern=Cs((noslashes^0*slashes^1)^0*((1-suffix)^1))*Cs(suffix)
function file.addsuffix(filename,suffix,criterium)
if not filename or not suffix or suffix=="" then
return filename
elseif criterium==true then
return filename.."."..suffix
elseif not criterium then
local n,s=lpegmatch(pattern,filename)
if not s or s=="" then
return filename.."."..suffix
else
return filename
end
else
local n,s=lpegmatch(pattern,filename)
if s and s~="" then
local t=type(criterium)
if t=="table" then
for i=1,#criterium do
if s==criterium[i] then
return filename
end
end
elseif t=="string" then
if s==criterium then
return filename
end
end
end
return (n or filename).."."..suffix
end
end
local suffix=period*(1-period-slashes)^1*-1
local pattern=Cs((1-suffix)^0)
function file.replacesuffix(name,suffix)
if name and suffix and suffix~="" then
return lpegmatch(pattern,name).."."..suffix
else
return name
end
end
local reslasher=lpeg.replacer(P("\\"),"/")
function file.reslash(str)
return str and lpegmatch(reslasher,str)
end
if lfs.isreadablefile and lfs.iswritablefile then
file.is_readable=lfs.isreadablefile
file.is_writable=lfs.iswritablefile
else
function file.is_writable(name)
if not name then
elseif lfs.isdir(name) then
name=name.."/m_t_x_t_e_s_t.tmp"
local f=io.open(name,"wb")
if f then
f:close()
os.remove(name)
return true
end
elseif lfs.isfile(name) then
local f=io.open(name,"ab")
if f then
f:close()
return true
end
else
local f=io.open(name,"ab")
if f then
f:close()
os.remove(name)
return true
end
end
return false
end
local readable=P("r")*Cc(true)
function file.is_readable(name)
if name then
local a=attributes(name)
return a and lpegmatch(readable,a.permissions) or false
else
return false
end
end
end
file.isreadable=file.is_readable
file.iswritable=file.is_writable
function file.size(name)
if name then
local a=attributes(name)
return a and a.size or 0
else
return 0
end
end
function file.splitpath(str,separator)
return str and checkedsplit(lpegmatch(reslasher,str),separator or io.pathseparator)
end
function file.joinpath(tab,separator)
return tab and concat(tab,separator or io.pathseparator)
end
local someslash=S("\\/")
local stripper=Cs(P(fwslash)^0/""*reslasher)
local isnetwork=someslash*someslash*(1-someslash)+(1-fwslash-colon)^1*colon
local isroot=fwslash^1*-1
local hasroot=fwslash^1
local reslasher=lpeg.replacer(S("\\/"),"/")
local deslasher=lpeg.replacer(S("\\/")^1,"/")
function file.join(one,two,three,...)
if not two then
return one=="" and one or lpegmatch(reslasher,one)
end
if one=="" then
return lpegmatch(stripper,three and concat({ two,three,... },"/") or two)
end
if lpegmatch(isnetwork,one) then
local one=lpegmatch(reslasher,one)
local two=lpegmatch(deslasher,three and concat({ two,three,... },"/") or two)
if lpegmatch(hasroot,two) then
return one..two
else
return one.."/"..two
end
elseif lpegmatch(isroot,one) then
local two=lpegmatch(deslasher,three and concat({ two,three,... },"/") or two)
if lpegmatch(hasroot,two) then
return two
else
return "/"..two
end
else
return lpegmatch(deslasher,concat({ one,two,three,... },"/"))
end
end
local drivespec=R("az","AZ")^1*colon
local anchors=fwslash+drivespec
local untouched=periods+(1-period)^1*P(-1)
local mswindrive=Cs(drivespec*(bwslash/"/"+fwslash)^0)
local mswinuncpath=(bwslash+fwslash)*(bwslash+fwslash)*Cc("//")
local splitstarter=(mswindrive+mswinuncpath+Cc(false))*Ct(lpeg.splitat(S("/\\")^1))
local absolute=fwslash
function file.collapsepath(str,anchor)
if not str then
return
end
if anchor==true and not lpegmatch(anchors,str) then
str=getcurrentdir().."/"..str
end
if str=="" or str=="." then
return "."
elseif lpegmatch(untouched,str) then
return lpegmatch(reslasher,str)
end
local starter,oldelements=lpegmatch(splitstarter,str)
local newelements={}
local i=#oldelements
while i>0 do
local element=oldelements[i]
if element=='.' then
elseif element=='..' then
local n=i-1
while n>0 do
local element=oldelements[n]
if element~='..' and element~='.' then
oldelements[n]='.'
break
else
n=n-1
end
end
if n<1 then
insert(newelements,1,'..')
end
elseif element~="" then
insert(newelements,1,element)
end
i=i-1
end
if #newelements==0 then
return starter or "."
elseif starter then
return starter..concat(newelements,'/')
elseif lpegmatch(absolute,str) then
return "/"..concat(newelements,'/')
else
newelements=concat(newelements,'/')
if anchor=="." and find(str,"^%./") then
return "./"..newelements
else
return newelements
end
end
end
local validchars=R("az","09","AZ","--","..")
local pattern_a=lpeg.replacer(1-validchars)
local pattern_a=Cs((validchars+P(1)/"-")^1)
local whatever=P("-")^0/""
local pattern_b=Cs(whatever*(1-whatever*-1)^1)
function file.robustname(str,strict)
if str then
str=lpegmatch(pattern_a,str) or str
if strict then
return lpegmatch(pattern_b,str) or str
else
return str
end
end
end
local loaddata=io.loaddata
local savedata=io.savedata
file.readdata=loaddata
file.savedata=savedata
function file.copy(oldname,newname)
if oldname and newname then
local data=loaddata(oldname)
if data and data~="" then
savedata(newname,data)
end
end
end
local letter=R("az","AZ")+S("_-+")
local separator=P("://")
local qualified=period^0*fwslash+letter*colon+letter^1*separator+letter^1*fwslash
local rootbased=fwslash+letter*colon
lpeg.patterns.qualified=qualified
lpeg.patterns.rootbased=rootbased
function file.is_qualified_path(filename)
return filename and lpegmatch(qualified,filename)~=nil
end
function file.is_rootbased_path(filename)
return filename and lpegmatch(rootbased,filename)~=nil
end
function file.strip(name,dir)
if name then
local b,a=match(name,"^(.-)"..dir.."(.*)$")
return a~="" and a or name
end
end
function lfs.mkdirs(path)
local full=""
for sub in gmatch(path,"(/*[^\\/]+)") do
full=full..sub
lfs.mkdir(full)
end
end
function file.withinbase(path)
local l=0
if not find(path,"^/") then
path="/"..path
end
for dir in gmatch(path,"/([^/]+)") do
if dir==".." then
l=l-1
elseif dir~="." then
l=l+1
end
if l<0 then
return false
end
end
return true
end
local symlinkattributes=lfs.symlinkattributes
function lfs.readlink(name)
return symlinkattributes(name,"target") or nil
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["l-gzip"] = package.loaded["l-gzip"] or true
-- original size: 1211, stripped down to: 951
if not modules then modules={} end modules ['l-gzip']={
version=1.001,
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
if not gzip then
return
end
local suffix,suffixes=file.suffix,file.suffixes
function gzip.load(filename)
local f=io.open(filename,"rb")
if not f then
elseif suffix(filename)=="gz" then
f:close()
local g=gzip.open(filename,"rb")
if g then
local str=g:read("*all")
g:close()
return str
end
else
local str=f:read("*all")
f:close()
return str
end
end
function gzip.save(filename,data)
if suffix(filename)~="gz" then
filename=filename..".gz"
end
local f=io.open(filename,"wb")
if f then
local s=zlib.compress(data or "",9,nil,15+16)
f:write(s)
f:close()
return #s
end
end
function gzip.suffix(filename)
local suffix,extra=suffixes(filename)
local gzipped=extra=="gz"
return suffix,gzipped
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["l-md5"] = package.loaded["l-md5"] or true
-- original size: 3309, stripped down to: 2218
if not modules then modules={} end modules ['l-md5']={
version=1.001,
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
if not md5 then
md5=optionalrequire("md5")
end
if not md5 then
md5={
sum=function(str) print("error: md5 is not loaded (sum ignored)") return str end,
sumhexa=function(str) print("error: md5 is not loaded (sumhexa ignored)") return str end,
}
end
local md5,file=md5,file
local gsub=string.gsub
do
local patterns=lpeg and lpeg.patterns
if patterns then
local bytestoHEX=patterns.bytestoHEX
local bytestohex=patterns.bytestohex
local bytestodec=patterns.bytestodec
local lpegmatch=lpeg.match
local md5sum=md5.sum
if not md5.HEX then function md5.HEX(str) if str then return lpegmatch(bytestoHEX,md5sum(str)) end end end
if not md5.hex then function md5.hex(str) if str then return lpegmatch(bytestohex,md5sum(str)) end end end
if not md5.dec then function md5.dec(str) if str then return lpegmatch(bytestodec,md5sum(str)) end end end
md5.sumhexa=md5.hex
md5.sumHEXA=md5.HEX
end
end
function file.needsupdating(oldname,newname,threshold)
local oldtime=lfs.attributes(oldname,"modification")
if oldtime then
local newtime=lfs.attributes(newname,"modification")
if not newtime then
return true
elseif newtime>=oldtime then
return false
elseif oldtime-newtime<(threshold or 1) then
return false
else
return true
end
else
return false
end
end
file.needs_updating=file.needsupdating
function file.syncmtimes(oldname,newname)
local oldtime=lfs.attributes(oldname,"modification")
if oldtime and lfs.isfile(newname) then
lfs.touch(newname,oldtime,oldtime)
end
end
function file.checksum(name)
if md5 then
local data=io.loaddata(name)
if data then
return md5.HEX(data)
end
end
return nil
end
function file.loadchecksum(name)
if md5 then
local data=io.loaddata(name..".md5")
return data and (gsub(data,"%s",""))
end
return nil
end
function file.savechecksum(name,checksum)
if not checksum then checksum=file.checksum(name) end
if checksum then
io.savedata(name..".md5",checksum)
return checksum
end
return nil
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["l-sha"] = package.loaded["l-sha"] or true
-- original size: 1085, stripped down to: 969
if not modules then modules={} end modules ['l-sha']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
if sha2 then
local lpegmatch=lpeg.match
local lpegpatterns=lpeg.patterns
local bytestohex=lpegpatterns.bytestohex
local bytestoHEX=lpegpatterns.bytestoHEX
local digest256=sha2.digest256
local digest384=sha2.digest384
local digest512=sha2.digest512
sha2.hash256=function(str) return lpegmatch(bytestohex,digest256(str)) end
sha2.hash384=function(str) return lpegmatch(bytestohex,digest384(str)) end
sha2.hash512=function(str) return lpegmatch(bytestohex,digest512(str)) end
sha2.HASH256=function(str) return lpegmatch(bytestoHEX,digest256(str)) end
sha2.HASH384=function(str) return lpegmatch(bytestoHEX,digest384(str)) end
sha2.HASH512=function(str) return lpegmatch(bytestoHEX,digest512(str)) end
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["l-url"] = package.loaded["l-url"] or true
-- original size: 14755, stripped down to: 6981
if not modules then modules={} end modules ['l-url']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local char,format,byte=string.char,string.format,string.byte
local concat=table.concat
local tonumber,type,next=tonumber,type,next
local 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
local lpegmatch,lpegpatterns,replacer=lpeg.match,lpeg.patterns,lpeg.replacer
local sortedhash=table.sortedhash
url=url or {}
local url=url
local unescapes={}
local escapes={}
setmetatable(unescapes,{ __index=function(t,k)
local v=char(tonumber(k,16))
t[k]=v
return v
end })
setmetatable(escapes,{ __index=function(t,k)
local v=format("%%%02X",byte(k))
t[k]=v
return v
end })
local colon=P(":")
local qmark=P("?")
local hash=P("#")
local slash=P("/")
local atsign=P("@")
local percent=P("%")
local endofstring=P(-1)
local hexdigit=R("09","AF","af")
local plus=P("+")
local nothing=Cc("")
local okay=R("09","AZ","az")+S("-_.,:=+*~!'()@&$")
local escapedchar=(percent*C(hexdigit*hexdigit))/unescapes
local unescapedchar=P(1)/escapes
local escaped=(plus/" ")+escapedchar
local noslash=P("/")/""
local plustospace=P("+")/" "
local decoder=Cs((
plustospace+escapedchar+P("\r\n")/"\n"+P(1)
)^0 )
local encoder=Cs((
R("09","AZ","az")^1+S("-./_")^1+P(" ")/"+"+P("\n")/"\r\n"+unescapedchar
)^0 )
lpegpatterns.urldecoder=decoder
lpegpatterns.urlencoder=encoder
function url.decode (str) return str and lpegmatch(decoder,str) or str end
function url.encode (str) return str and lpegmatch(encoder,str) or str end
function url.unescape(str) return str and lpegmatch(unescaper,str) or str end
local schemestr=Cs((escaped+(1-colon-slash-qmark-hash))^2)
local authoritystr=Cs((escaped+(1- slash-qmark-hash))^0)
local pathstr=Cs((escaped+(1- qmark-hash))^0)
local querystr=Cs(((1- hash))^0)
local fragmentstr=Cs((escaped+(1- endofstring))^0)
local scheme=schemestr*colon+nothing
local authority=slash*slash*authoritystr+nothing
local path=slash*pathstr+nothing
local query=qmark*querystr+nothing
local fragment=hash*fragmentstr+nothing
local validurl=scheme*authority*path*query*fragment
local parser=Ct(validurl)
lpegpatterns.url=validurl
lpegpatterns.urlsplitter=parser
local escaper=Cs((R("09","AZ","az")^1+P(" ")/"%%20"+S("-./_:")^1+P(1)/escapes)^0)
local unescaper=Cs((escapedchar+1)^0)
local getcleaner=Cs((P("+++")/"%%2B"+P("+")/"%%20"+P(1))^1)
lpegpatterns.urlunescaped=escapedchar
lpegpatterns.urlescaper=escaper
lpegpatterns.urlunescaper=unescaper
lpegpatterns.urlgetcleaner=getcleaner
function url.unescapeget(str)
return lpegmatch(getcleaner,str)
end
local function split(str)
return (type(str)=="string" and lpegmatch(parser,str)) or str
end
local isscheme=schemestr*colon*slash*slash
local function hasscheme(str)
if str then
local scheme=lpegmatch(isscheme,str)
return scheme~="" and scheme or false
else
return false
end
end
local rootletter=R("az","AZ")+S("_-+")
local separator=P("://")
local qualified=P(".")^0*P("/")+rootletter*P(":")+rootletter^1*separator+rootletter^1*P("/")
local rootbased=P("/")+rootletter*P(":")
local barswapper=replacer("|",":")
local backslashswapper=replacer("\\","/")
local equal=P("=")
local amp=P("&")
local key=Cs(((plustospace+escapedchar+1)-equal )^0)
local value=Cs(((plustospace+escapedchar+1)-amp-endofstring)^0)
local splitquery=Cf (Ct("")*P { "sequence",
sequence=V("pair")*(amp*V("pair"))^0,
pair=Cg(key*equal*value),
},rawset)
local userpart=(1-atsign-colon)^1
local serverpart=(1-colon)^1
local splitauthority=((Cs(userpart)*colon*Cs(userpart)+Cs(userpart)*Cc(nil))*atsign+Cc(nil)*Cc(nil))*Cs(serverpart)*(colon*(serverpart/tonumber)+Cc(nil))
local function hashed(str)
if not str or str=="" then
return {
scheme="invalid",
original=str,
}
end
local detailed=split(str)
local rawscheme=""
local rawquery=""
local somescheme=false
local somequery=false
if detailed then
rawscheme=detailed[1]
rawquery=detailed[4]
somescheme=rawscheme~=""
somequery=rawquery~=""
end
if not somescheme and not somequery then
return {
scheme="file",
authority="",
path=str,
query="",
fragment="",
original=str,
noscheme=true,
filename=str,
}
end
local authority=detailed[2]
local path=detailed[3]
local filename
local username
local password
local host
local port
if authority~="" then
username,password,host,port=lpegmatch(splitauthority,authority)
end
if authority=="" then
filename=path
elseif path=="" then
filename=""
else
filename=authority.."/"..path
end
return {
scheme=rawscheme,
authority=authority,
path=path,
query=lpegmatch(unescaper,rawquery),
queries=lpegmatch(splitquery,rawquery),
fragment=detailed[5],
original=str,
noscheme=false,
filename=filename,
host=host,
port=port,
}
end
url.split=split
url.hasscheme=hasscheme
url.hashed=hashed
function url.addscheme(str,scheme)
if hasscheme(str) then
return str
elseif not scheme then
return "file:///"..str
else
return scheme..":///"..str
end
end
function url.construct(hash)
local result,r={},0
local scheme=hash.scheme
local authority=hash.authority
local path=hash.path
local queries=hash.queries
local fragment=hash.fragment
if scheme and scheme~="" then
r=r+1;result[r]=lpegmatch(escaper,scheme)
r=r+1;result[r]="://"
end
if authority and authority~="" then
r=r+1;result[r]=lpegmatch(escaper,authority)
end
if path and path~="" then
r=r+1;result[r]="/"
r=r+1;result[r]=lpegmatch(escaper,path)
end
if queries then
local done=false
for k,v in sortedhash(queries) do
r=r+1;result[r]=done and "&" or "?"
r=r+1;result[r]=lpegmatch(escaper,k)
r=r+1;result[r]="="
r=r+1;result[r]=lpegmatch(escaper,v)
done=true
end
end
if fragment and fragment~="" then
r=r+1;result[r]="#"
r=r+1;result[r]=lpegmatch(escaper,fragment)
end
return concat(result)
end
local pattern=Cs(slash^-1/""*R("az","AZ")*((S(":|")/":")+P(":"))*slash*P(1)^0)
function url.filename(filename)
local spec=hashed(filename)
local path=spec.path
return (spec.scheme=="file" and path and lpegmatch(pattern,path)) or filename
end
local function escapestring(str)
return lpegmatch(escaper,str)
end
url.escape=escapestring
function url.query(str)
if type(str)=="string" then
return lpegmatch(splitquery,str) or ""
else
return str
end
end
function url.toquery(data)
local td=type(data)
if td=="string" then
return #str and escape(data) or nil
elseif td=="table" then
if next(data) then
local t={}
for k,v in next,data do
t[#t+1]=format("%s=%s",k,escapestring(v))
end
return concat(t,"&")
end
else
end
end
local pattern=Cs(noslash^0*(1-noslash*P(-1))^0)
function url.barepath(path)
if not path or path=="" then
return ""
else
return lpegmatch(pattern,path)
end
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["l-dir"] = package.loaded["l-dir"] or true
-- original size: 18253, stripped down to: 10816
if not modules then modules={} end modules ['l-dir']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local type,select=type,select
local find,gmatch,match,gsub,sub=string.find,string.gmatch,string.match,string.gsub,string.sub
local concat,insert,remove,unpack=table.concat,table.insert,table.remove,table.unpack
local lpegmatch=lpeg.match
local 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
dir=dir or {}
local dir=dir
local lfs=lfs
local attributes=lfs.attributes
local walkdir=lfs.dir
local isdir=lfs.isdir
local isfile=lfs.isfile
local currentdir=lfs.currentdir
local chdir=lfs.chdir
local mkdir=lfs.mkdir
local onwindows=os.type=="windows" or find(os.getenv("PATH"),";",1,true)
if onwindows then
local tricky=S("/\\")*P(-1)
isdir=function(name)
if lpegmatch(tricky,name) then
return attributes(name,"mode")=="directory"
else
return attributes(name.."/.","mode")=="directory"
end
end
isfile=function(name)
return attributes(name,"mode")=="file"
end
lfs.isdir=isdir
lfs.isfile=isfile
else
isdir=function(name)
return attributes(name,"mode")=="directory"
end
isfile=function(name)
return attributes(name,"mode")=="file"
end
lfs.isdir=isdir
lfs.isfile=isfile
end
function dir.current()
return (gsub(currentdir(),"\\","/"))
end
local function glob_pattern_function(path,patt,recurse,action)
if isdir(path) then
local usedpath
if path=="/" then
usedpath="/."
elseif not find(path,"/$") then
usedpath=path.."/."
path=path.."/"
else
usedpath=path
end
local dirs
local nofdirs=0
for name,mode,size,time in walkdir(usedpath) do
if name~="." and name~=".." then
local full=path..name
if mode==nil then
mode=attributes(full,'mode')
end
if mode=='file' then
if not patt or find(full,patt) then
action(full,size,time)
end
elseif recurse and mode=="directory" then
if dirs then
nofdirs=nofdirs+1
dirs[nofdirs]=full
else
nofdirs=1
dirs={ full }
end
end
end
end
if dirs then
for i=1,nofdirs do
glob_pattern_function(dirs[i],patt,recurse,action)
end
end
end
end
local function glob_pattern_table(path,patt,recurse,result)
if not result then
result={}
end
local usedpath
if path=="/" then
usedpath="/."
elseif not find(path,"/$") then
usedpath=path.."/."
path=path.."/"
else
usedpath=path
end
local dirs
local nofdirs=0
local noffiles=#result
for name,mode in walkdir(usedpath) do
if name~="." and name~=".." then
local full=path..name
if mode==nil then
mode=attributes(full,'mode')
end
if mode=='file' then
if not patt or find(full,patt) then
noffiles=noffiles+1
result[noffiles]=full
end
elseif recurse and mode=="directory" then
if dirs then
nofdirs=nofdirs+1
dirs[nofdirs]=full
else
nofdirs=1
dirs={ full }
end
end
end
end
if dirs then
for i=1,nofdirs do
glob_pattern_table(dirs[i],patt,recurse,result)
end
end
return result
end
local function globpattern(path,patt,recurse,method)
local kind=type(method)
if patt and sub(patt,1,-3)==path then
patt=false
end
local okay=isdir(path)
if kind=="function" then
return okay and glob_pattern_function(path,patt,recurse,method) or {}
elseif kind=="table" then
return okay and glob_pattern_table(path,patt,recurse,method) or method
else
return okay and glob_pattern_table(path,patt,recurse,{}) or {}
end
end
dir.globpattern=globpattern
local function collectpattern(path,patt,recurse,result)
local ok,scanner
result=result or {}
if path=="/" then
ok,scanner,first=xpcall(function() return walkdir(path..".") end,function() end)
else
ok,scanner,first=xpcall(function() return walkdir(path) end,function() end)
end
if ok and type(scanner)=="function" then
if not find(path,"/$") then
path=path..'/'
end
for name in scanner,first do
if name=="." then
elseif name==".." then
else
local full=path..name
local attr=attributes(full)
local mode=attr.mode
if mode=='file' then
if find(full,patt) then
result[name]=attr
end
elseif recurse and mode=="directory" then
attr.list=collectpattern(full,patt,recurse)
result[name]=attr
end
end
end
end
return result
end
dir.collectpattern=collectpattern
local separator,pattern
if onwindows then
local slash=S("/\\")/"/"
pattern={
[1]=(Cs(P(".")+slash^1)+Cs(R("az","AZ")*P(":")*slash^0)+Cc("./"))*V(2)*V(3),
[2]=Cs(((1-S("*?/\\"))^0*slash)^0),
[3]=Cs(P(1)^0)
}
else
pattern={
[1]=(C(P(".")+P("/")^1)+Cc("./"))*V(2)*V(3),
[2]=C(((1-S("*?/"))^0*P("/"))^0),
[3]=C(P(1)^0)
}
end
local filter=Cs ((
P("**")/".*"+P("*")/"[^/]*"+P("?")/"[^/]"+P(".")/"%%."+P("+")/"%%+"+P("-")/"%%-"+P(1)
)^0 )
local function glob(str,t)
if type(t)=="function" then
if type(str)=="table" then
for s=1,#str do
glob(str[s],t)
end
elseif isfile(str) then
t(str)
else
local root,path,base=lpegmatch(pattern,str)
if root and path and base then
local recurse=find(base,"**",1,true)
local start=root..path
local result=lpegmatch(filter,start..base)
globpattern(start,result,recurse,t)
end
end
else
if type(str)=="table" then
local t=t or {}
for s=1,#str do
glob(str[s],t)
end
return t
elseif isfile(str) then
if t then
t[#t+1]=str
return t
else
return { str }
end
else
local root,path,base=lpegmatch(pattern,str)
if root and path and base then
local recurse=find(base,"**",1,true)
local start=root..path
local result=lpegmatch(filter,start..base)
return globpattern(start,result,recurse,t)
else
return {}
end
end
end
end
dir.glob=glob
local function globfiles(path,recurse,func,files)
if type(func)=="string" then
local s=func
func=function(name) return find(name,s) end
end
files=files or {}
local noffiles=#files
for name,mode in walkdir(path) do
if find(name,"^%.") then
else
if mode==nil then
mode=attributes(name,'mode')
end
if mode=="directory" then
if recurse then
globfiles(path.."/"..name,recurse,func,files)
end
elseif mode=="file" then
if not func or func(name) then
noffiles=noffiles+1
files[noffiles]=path.."/"..name
end
end
end
end
return files
end
dir.globfiles=globfiles
local function globdirs(path,recurse,func,files)
if type(func)=="string" then
local s=func
func=function(name) return find(name,s) end
end
files=files or {}
local noffiles=#files
for name,mode in walkdir(path) do
if find(name,"^%.") then
else
if mode==nil then
mode=attributes(name,'mode')
end
if mode=="directory" then
if not func or func(name) then
noffiles=noffiles+1
files[noffiles]=path.."/"..name
if recurse then
globdirs(path.."/"..name,recurse,func,files)
end
end
end
end
end
return files
end
dir.globdirs=globdirs
function dir.ls(pattern)
return concat(glob(pattern),"\n")
end
local make_indeed=true
if onwindows then
function dir.mkdirs(...)
local n=select("#",...)
local str
if n==1 then
str=select(1,...)
if isdir(str) then
return str,true
end
else
str=""
for i=1,n do
local s=select(i,...)
if s=="" then
elseif str=="" then
str=s
else
str=str.."/"..s
end
end
end
local pth=""
local drive=false
local first,middle,last=match(str,"^(//)(//*)(.*)$")
if first then
else
first,last=match(str,"^(//)/*(.-)$")
if first then
middle,last=match(str,"([^/]+)/+(.-)$")
if middle then
pth="//"..middle
else
pth="//"..last
last=""
end
else
first,middle,last=match(str,"^([a-zA-Z]:)(/*)(.-)$")
if first then
pth,drive=first..middle,true
else
middle,last=match(str,"^(/*)(.-)$")
if not middle then
last=str
end
end
end
end
for s in gmatch(last,"[^/]+") do
if pth=="" then
pth=s
elseif drive then
pth,drive=pth..s,false
else
pth=pth.."/"..s
end
if make_indeed and not isdir(pth) then
mkdir(pth)
end
end
return pth,(isdir(pth)==true)
end
else
function dir.mkdirs(...)
local n=select("#",...)
local str,pth
if n==1 then
str=select(1,...)
if isdir(str) then
return str,true
end
else
str=""
for i=1,n do
local s=select(i,...)
if s and s~="" then
if str~="" then
str=str.."/"..s
else
str=s
end
end
end
end
str=gsub(str,"/+","/")
if find(str,"^/") then
pth="/"
for s in gmatch(str,"[^/]+") do
local first=(pth=="/")
if first then
pth=pth..s
else
pth=pth.."/"..s
end
if make_indeed and not first and not isdir(pth) then
mkdir(pth)
end
end
else
pth="."
for s in gmatch(str,"[^/]+") do
pth=pth.."/"..s
if make_indeed and not isdir(pth) then
mkdir(pth)
end
end
end
return pth,(isdir(pth)==true)
end
end
dir.makedirs=dir.mkdirs
do
local chdir=sandbox and sandbox.original(chdir) or chdir
if onwindows then
local xcurrentdir=dir.current
function dir.expandname(str)
local first,nothing,last=match(str,"^(//)(//*)(.*)$")
if first then
first=xcurrentdir().."/"
end
if not first then
first,last=match(str,"^(//)/*(.*)$")
end
if not first then
first,last=match(str,"^([a-zA-Z]:)(.*)$")
if first and not find(last,"^/") then
local d=currentdir()
if chdir(first) then
first=xcurrentdir()
end
chdir(d)
end
end
if not first then
first,last=xcurrentdir(),str
end
last=gsub(last,"//","/")
last=gsub(last,"/%./","/")
last=gsub(last,"^/*","")
first=gsub(first,"/*$","")
if last=="" or last=="." then
return first
else
return first.."/"..last
end
end
else
function dir.expandname(str)
if not find(str,"^/") then
str=currentdir().."/"..str
end
str=gsub(str,"//","/")
str=gsub(str,"/%./","/")
str=gsub(str,"(.)/%.$","%1")
return str
end
end
end
file.expandname=dir.expandname
local stack={}
function dir.push(newdir)
local curdir=currentdir()
insert(stack,curdir)
if newdir and newdir~="" and chdir(newdir) then
return newdir
else
return curdir
end
end
function dir.pop()
local d=remove(stack)
if d then
chdir(d)
end
return d
end
local function found(...)
for i=1,select("#",...) do
local path=select(i,...)
local kind=type(path)
if kind=="string" then
if isdir(path) then
return path
end
elseif kind=="table" then
local path=found(unpack(path))
if path then
return path
end
end
end
end
dir.found=found
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["l-boolean"] = package.loaded["l-boolean"] or true
-- original size: 1850, stripped down to: 1498
if not modules then modules={} end modules ['l-boolean']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local type,tonumber=type,tonumber
boolean=boolean or {}
local boolean=boolean
function boolean.tonumber(b)
if b then return 1 else return 0 end
end
function toboolean(str,tolerant)
if str==nil then
return false
elseif str==false then
return false
elseif str==true then
return true
elseif str=="true" then
return true
elseif str=="false" then
return false
elseif not tolerant then
return false
elseif str==0 then
return false
elseif (tonumber(str) or 0)>0 then
return true
else
return str=="yes" or str=="on" or str=="t"
end
end
string.toboolean=toboolean
function string.booleanstring(str)
if str=="0" then
return false
elseif str=="1" then
return true
elseif str=="" then
return false
elseif str=="false" then
return false
elseif str=="true" then
return true
elseif (tonumber(str) or 0)>0 then
return true
else
return str=="yes" or str=="on" or str=="t"
end
end
function string.is_boolean(str,default,strict)
if type(str)=="string" then
if str=="true" or str=="yes" or str=="on" or str=="t" or (not strict and str=="1") then
return true
elseif str=="false" or str=="no" or str=="off" or str=="f" or (not strict and str=="0") then
return false
end
end
return default
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["l-unicode"] = package.loaded["l-unicode"] or true
-- original size: 41047, stripped down to: 17171
if not modules then modules={} end modules ['l-unicode']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
utf=utf or {}
unicode=nil
if not string.utfcharacters then
local gmatch=string.gmatch
function string.characters(str)
return gmatch(str,".[\128-\191]*")
end
end
utf.characters=string.utfcharacters
local type=type
local char,byte,format,sub,gmatch=string.char,string.byte,string.format,string.sub,string.gmatch
local concat=table.concat
local 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
local lpegmatch=lpeg.match
local patterns=lpeg.patterns
local tabletopattern=lpeg.utfchartabletopattern
local bytepairs=string.bytepairs
local finder=lpeg.finder
local replacer=lpeg.replacer
local p_utftype=patterns.utftype
local p_utfstricttype=patterns.utfstricttype
local p_utfoffset=patterns.utfoffset
local p_utf8character=patterns.utf8character
local p_utf8char=patterns.utf8char
local p_utf8byte=patterns.utf8byte
local p_utfbom=patterns.utfbom
local p_newline=patterns.newline
local p_whitespace=patterns.whitespace
if not utf.char then
utf.char=string.utfcharacter or (utf8 and utf8.char)
if not utf.char then
local char=string.char
if bit32 then
local rshift=bit32.rshift
function utf.char(n)
if n<0x80 then
return char(n)
elseif n<0x800 then
return char(
0xC0+rshift(n,6),
0x80+(n%0x40)
)
elseif n<0x10000 then
return char(
0xE0+rshift(n,12),
0x80+(rshift(n,6)%0x40),
0x80+(n%0x40)
)
elseif n<0x200000 then
return char(
0xF0+rshift(n,18),
0x80+(rshift(n,12)%0x40),
0x80+(rshift(n,6)%0x40),
0x80+(n%0x40)
)
else
return ""
end
end
else
local floor=math.floor
function utf.char(n)
if n<0x80 then
return char(n)
elseif n<0x800 then
return char(
0xC0+floor(n/0x40),
0x80+(n%0x40)
)
elseif n<0x10000 then
return char(
0xE0+floor(n/0x1000),
0x80+(floor(n/0x40)%0x40),
0x80+(n%0x40)
)
elseif n<0x200000 then
return char(
0xF0+floor(n/0x40000),
0x80+(floor(n/0x1000)%0x40),
0x80+(floor(n/0x40)%0x40),
0x80+(n%0x40)
)
else
return ""
end
end
end
end
end
if not utf.byte then
utf.byte=string.utfvalue or (utf8 and utf8.codepoint)
if not utf.byte then
function utf.byte(c)
return lpegmatch(p_utf8byte,c)
end
end
end
local utfchar,utfbyte=utf.char,utf.byte
function utf.filetype(data)
return data and lpegmatch(p_utftype,data) or "unknown"
end
local toentities=Cs (
(
patterns.utf8one+(
patterns.utf8two+patterns.utf8three+patterns.utf8four
)/function(s) local b=utfbyte(s) if b<127 then return s else return format("&#%X;",b) end end
)^0
)
patterns.toentities=toentities
function utf.toentities(str)
return lpegmatch(toentities,str)
end
local one=P(1)
local two=C(1)*C(1)
local four=C(R(utfchar(0xD8),utfchar(0xFF)))*C(1)*C(1)*C(1)
local pattern=P("\254\255")*Cs((
four/function(a,b,c,d)
local ab=0xFF*byte(a)+byte(b)
local cd=0xFF*byte(c)+byte(d)
return utfchar((ab-0xD800)*0x400+(cd-0xDC00)+0x10000)
end+two/function(a,b)
return utfchar(byte(a)*256+byte(b))
end+one
)^1 )+P("\255\254")*Cs((
four/function(b,a,d,c)
local ab=0xFF*byte(a)+byte(b)
local cd=0xFF*byte(c)+byte(d)
return utfchar((ab-0xD800)*0x400+(cd-0xDC00)+0x10000)
end+two/function(b,a)
return utfchar(byte(a)*256+byte(b))
end+one
)^1 )
function string.toutf(s)
return lpegmatch(pattern,s) or s
end
local validatedutf=Cs (
(
patterns.utf8one+patterns.utf8two+patterns.utf8three+patterns.utf8four+P(1)/"�"
)^0
)
patterns.validatedutf=validatedutf
function utf.is_valid(str)
return type(str)=="string" and lpegmatch(validatedutf,str) or false
end
if not utf.len then
utf.len=string.utflength or (utf8 and utf8.len)
if not utf.len then
local n,f=0,1
local utfcharcounter=patterns.utfbom^-1*Cmt (
Cc(1)*patterns.utf8one^1+Cc(2)*patterns.utf8two^1+Cc(3)*patterns.utf8three^1+Cc(4)*patterns.utf8four^1,
function(_,t,d)
n=n+(t-f)/d
f=t
return true
end
)^0
function utf.len(str)
n,f=0,1
lpegmatch(utfcharcounter,str or "")
return n
end
end
end
utf.length=utf.len
if not utf.sub then
local utflength=utf.length
local b,e,n,first,last=0,0,0,0,0
local function slide_zero(s,p)
n=n+1
if n>=last then
e=p-1
else
return p
end
end
local function slide_one(s,p)
n=n+1
if n==first then
b=p
end
if n>=last then
e=p-1
else
return p
end
end
local function slide_two(s,p)
n=n+1
if n==first then
b=p
else
return true
end
end
local pattern_zero=Cmt(p_utf8character,slide_zero)^0
local pattern_one=Cmt(p_utf8character,slide_one )^0
local pattern_two=Cmt(p_utf8character,slide_two )^0
local pattern_first=C(p_utf8character)
function utf.sub(str,start,stop)
if not start then
return str
end
if start==0 then
start=1
end
if not stop then
if start<0 then
local l=utflength(str)
start=l+start
else
start=start-1
end
b,n,first=0,0,start
lpegmatch(pattern_two,str)
if n>=first then
return sub(str,b)
else
return ""
end
end
if start<0 or stop<0 then
local l=utf.length(str)
if start<0 then
start=l+start
if start<=0 then
start=1
else
start=start+1
end
end
if stop<0 then
stop=l+stop
if stop==0 then
stop=1
else
stop=stop+1
end
end
end
if start==1 and stop==1 then
return lpegmatch(pattern_first,str) or ""
elseif start>stop then
return ""
elseif start>1 then
b,e,n,first,last=0,0,0,start-1,stop
lpegmatch(pattern_one,str)
if n>=first and e==0 then
e=#str
end
return sub(str,b,e)
else
b,e,n,last=1,0,0,stop
lpegmatch(pattern_zero,str)
if e==0 then
e=#str
end
return sub(str,b,e)
end
end
end
function utf.remapper(mapping,option,action)
local variant=type(mapping)
if variant=="table" then
action=action or mapping
if option=="dynamic" then
local pattern=false
table.setmetatablenewindex(mapping,function(t,k,v) rawset(t,k,v) pattern=false end)
return function(str)
if not str or str=="" then
return ""
else
if not pattern then
pattern=Cs((tabletopattern(mapping)/action+p_utf8character)^0)
end
return lpegmatch(pattern,str)
end
end
elseif option=="pattern" then
return Cs((tabletopattern(mapping)/action+p_utf8character)^0)
else
local pattern=Cs((tabletopattern(mapping)/action+p_utf8character)^0)
return function(str)
if not str or str=="" then
return ""
else
return lpegmatch(pattern,str)
end
end,pattern
end
elseif variant=="function" then
if option=="pattern" then
return Cs((p_utf8character/mapping+p_utf8character)^0)
else
local pattern=Cs((p_utf8character/mapping+p_utf8character)^0)
return function(str)
if not str or str=="" then
return ""
else
return lpegmatch(pattern,str)
end
end,pattern
end
else
return function(str)
return str or ""
end
end
end
function utf.replacer(t)
local r=replacer(t,false,false,true)
return function(str)
return lpegmatch(r,str)
end
end
function utf.subtituter(t)
local f=finder (t)
local r=replacer(t,false,false,true)
return function(str)
local i=lpegmatch(f,str)
if not i then
return str
elseif i>#str then
return str
else
return lpegmatch(r,str)
end
end
end
local utflinesplitter=p_utfbom^-1*lpeg.tsplitat(p_newline)
local utfcharsplitter_ows=p_utfbom^-1*Ct(C(p_utf8character)^0)
local utfcharsplitter_iws=p_utfbom^-1*Ct((p_whitespace^1+C(p_utf8character))^0)
local utfcharsplitter_raw=Ct(C(p_utf8character)^0)
patterns.utflinesplitter=utflinesplitter
function utf.splitlines(str)
return lpegmatch(utflinesplitter,str or "")
end
function utf.split(str,ignorewhitespace)
if ignorewhitespace then
return lpegmatch(utfcharsplitter_iws,str or "")
else
return lpegmatch(utfcharsplitter_ows,str or "")
end
end
function utf.totable(str)
return lpegmatch(utfcharsplitter_raw,str)
end
function utf.magic(f)
local str=f:read(4) or ""
local off=lpegmatch(p_utfoffset,str)
if off<4 then
f:seek('set',off)
end
return lpegmatch(p_utftype,str)
end
local utf16_to_utf8_be,utf16_to_utf8_le
local utf32_to_utf8_be,utf32_to_utf8_le
local utf_16_be_getbom=patterns.utfbom_16_be^-1
local utf_16_le_getbom=patterns.utfbom_16_le^-1
local utf_32_be_getbom=patterns.utfbom_32_be^-1
local utf_32_le_getbom=patterns.utfbom_32_le^-1
local utf_16_be_linesplitter=utf_16_be_getbom*lpeg.tsplitat(patterns.utf_16_be_nl)
local utf_16_le_linesplitter=utf_16_le_getbom*lpeg.tsplitat(patterns.utf_16_le_nl)
local utf_32_be_linesplitter=utf_32_be_getbom*lpeg.tsplitat(patterns.utf_32_be_nl)
local utf_32_le_linesplitter=utf_32_le_getbom*lpeg.tsplitat(patterns.utf_32_le_nl)
local more=0
local p_utf16_to_utf8_be=C(1)*C(1)/function(left,right)
local now=256*byte(left)+byte(right)
if more>0 then
now=(more-0xD800)*0x400+(now-0xDC00)+0x10000
more=0
return utfchar(now)
elseif now>=0xD800 and now<=0xDBFF then
more=now
return ""
else
return utfchar(now)
end
end
local p_utf16_to_utf8_le=C(1)*C(1)/function(right,left)
local now=256*byte(left)+byte(right)
if more>0 then
now=(more-0xD800)*0x400+(now-0xDC00)+0x10000
more=0
return utfchar(now)
elseif now>=0xD800 and now<=0xDBFF then
more=now
return ""
else
return utfchar(now)
end
end
local p_utf32_to_utf8_be=C(1)*C(1)*C(1)*C(1)/function(a,b,c,d)
return utfchar(256*256*256*byte(a)+256*256*byte(b)+256*byte(c)+byte(d))
end
local p_utf32_to_utf8_le=C(1)*C(1)*C(1)*C(1)/function(a,b,c,d)
return utfchar(256*256*256*byte(d)+256*256*byte(c)+256*byte(b)+byte(a))
end
p_utf16_to_utf8_be=P(true)/function() more=0 end*utf_16_be_getbom*Cs(p_utf16_to_utf8_be^0)
p_utf16_to_utf8_le=P(true)/function() more=0 end*utf_16_le_getbom*Cs(p_utf16_to_utf8_le^0)
p_utf32_to_utf8_be=P(true)/function() more=0 end*utf_32_be_getbom*Cs(p_utf32_to_utf8_be^0)
p_utf32_to_utf8_le=P(true)/function() more=0 end*utf_32_le_getbom*Cs(p_utf32_to_utf8_le^0)
patterns.utf16_to_utf8_be=p_utf16_to_utf8_be
patterns.utf16_to_utf8_le=p_utf16_to_utf8_le
patterns.utf32_to_utf8_be=p_utf32_to_utf8_be
patterns.utf32_to_utf8_le=p_utf32_to_utf8_le
utf16_to_utf8_be=function(s)
if s and s~="" then
return lpegmatch(p_utf16_to_utf8_be,s)
else
return s
end
end
local utf16_to_utf8_be_t=function(t)
if not t then
return nil
elseif type(t)=="string" then
t=lpegmatch(utf_16_be_linesplitter,t)
end
for i=1,#t do
local s=t[i]
if s~="" then
t[i]=lpegmatch(p_utf16_to_utf8_be,s)
end
end
return t
end
utf16_to_utf8_le=function(s)
if s and s~="" then
return lpegmatch(p_utf16_to_utf8_le,s)
else
return s
end
end
local utf16_to_utf8_le_t=function(t)
if not t then
return nil
elseif type(t)=="string" then
t=lpegmatch(utf_16_le_linesplitter,t)
end
for i=1,#t do
local s=t[i]
if s~="" then
t[i]=lpegmatch(p_utf16_to_utf8_le,s)
end
end
return t
end
utf32_to_utf8_be=function(s)
if s and s~="" then
return lpegmatch(p_utf32_to_utf8_be,s)
else
return s
end
end
local utf32_to_utf8_be_t=function(t)
if not t then
return nil
elseif type(t)=="string" then
t=lpegmatch(utf_32_be_linesplitter,t)
end
for i=1,#t do
local s=t[i]
if s~="" then
t[i]=lpegmatch(p_utf32_to_utf8_be,s)
end
end
return t
end
utf32_to_utf8_le=function(s)
if s and s~="" then
return lpegmatch(p_utf32_to_utf8_le,s)
else
return s
end
end
local utf32_to_utf8_le_t=function(t)
if not t then
return nil
elseif type(t)=="string" then
t=lpegmatch(utf_32_le_linesplitter,t)
end
for i=1,#t do
local s=t[i]
if s~="" then
t[i]=lpegmatch(p_utf32_to_utf8_le,s)
end
end
return t
end
utf.utf16_to_utf8_le_t=utf16_to_utf8_le_t
utf.utf16_to_utf8_be_t=utf16_to_utf8_be_t
utf.utf32_to_utf8_le_t=utf32_to_utf8_le_t
utf.utf32_to_utf8_be_t=utf32_to_utf8_be_t
utf.utf16_to_utf8_le=utf16_to_utf8_le
utf.utf16_to_utf8_be=utf16_to_utf8_be
utf.utf32_to_utf8_le=utf32_to_utf8_le
utf.utf32_to_utf8_be=utf32_to_utf8_be
function utf.utf8_to_utf8_t(t)
return type(t)=="string" and lpegmatch(utflinesplitter,t) or t
end
function utf.utf16_to_utf8_t(t,endian)
return endian and utf16_to_utf8_be_t(t) or utf16_to_utf8_le_t(t) or t
end
function utf.utf32_to_utf8_t(t,endian)
return endian and utf32_to_utf8_be_t(t) or utf32_to_utf8_le_t(t) or t
end
local function little(b)
if b<0x10000 then
return char(b%256,rshift(b,8))
else
b=b-0x10000
local b1=rshift(b,10)+0xD800
local b2=b%1024+0xDC00
return char(b1%256,rshift(b1,8),b2%256,rshift(b2,8))
end
end
local function big(b)
if b<0x10000 then
return char(rshift(b,8),b%256)
else
b=b-0x10000
local b1=rshift(b,10)+0xD800
local b2=b%1024+0xDC00
return char(rshift(b1,8),b1%256,rshift(b2,8),b2%256)
end
end
local l_remap=Cs((p_utf8byte/little+P(1)/"")^0)
local b_remap=Cs((p_utf8byte/big+P(1)/"")^0)
local function utf8_to_utf16_be(str,nobom)
if nobom then
return lpegmatch(b_remap,str)
else
return char(254,255)..lpegmatch(b_remap,str)
end
end
local function utf8_to_utf16_le(str,nobom)
if nobom then
return lpegmatch(l_remap,str)
else
return char(255,254)..lpegmatch(l_remap,str)
end
end
utf.utf8_to_utf16_be=utf8_to_utf16_be
utf.utf8_to_utf16_le=utf8_to_utf16_le
function utf.utf8_to_utf16(str,littleendian,nobom)
if littleendian then
return utf8_to_utf16_le(str,nobom)
else
return utf8_to_utf16_be(str,nobom)
end
end
local pattern=Cs (
(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
)
function utf.tocodes(str,separator)
return lpegmatch(pattern,str,1,separator or " ")
end
function utf.ustring(s)
return format("U+%05X",type(s)=="number" and s or utfbyte(s))
end
function utf.xstring(s)
return format("0x%05X",type(s)=="number" and s or utfbyte(s))
end
function utf.toeight(str)
if not str or str=="" then
return nil
end
local utftype=lpegmatch(p_utfstricttype,str)
if utftype=="utf-8" then
return sub(str,4)
elseif utftype=="utf-16-be" then
return utf16_to_utf8_be(str)
elseif utftype=="utf-16-le" then
return utf16_to_utf8_le(str)
else
return str
end
end
do
local p_nany=p_utf8character/""
local cache={}
function utf.count(str,what)
if type(what)=="string" then
local p=cache[what]
if not p then
p=Cs((P(what)/" "+p_nany)^0)
cache[p]=p
end
return #lpegmatch(p,str)
else
return #lpegmatch(Cs((P(what)/" "+p_nany)^0),str)
end
end
end
if not string.utfvalues then
local find=string.find
local dummy=function()
end
function string.utfvalues(str)
local n=#str
if n==0 then
return dummy
elseif n==1 then
return function() return utfbyte(str) end
else
local p=1
return function()
local b,e=find(str,".[\128-\191]*",p)
if b then
p=e+1
return utfbyte(sub(str,b,e))
end
end
end
end
end
utf.values=string.utfvalues
function utf.chrlen(u)
return
(u<0x80 and 1) or
(u<0xE0 and 2) or
(u<0xF0 and 3) or
(u<0xF8 and 4) or
(u<0xFC and 5) or
(u<0xFE and 6) or 0
end
if bit32 then
local extract=bit32.extract
local char=string.char
function utf.toutf32string(n)
if n<=0xFF then
return
char(n).."\000\000\000"
elseif n<=0xFFFF then
return
char(extract(n,0,8))..char(extract(n,8,8)).."\000\000"
elseif n<=0xFFFFFF then
return
char(extract(n,0,8))..char(extract(n,8,8))..char(extract(n,16,8)).."\000"
else
return
char(extract(n,0,8))..char(extract(n,8,8))..char(extract(n,16,8))..char(extract(n,24,8))
end
end
end
local len=utf.len
local rep=rep
function string.utfpadd(s,n)
if n and n~=0 then
local l=len(s)
if n>0 then
local d=n-l
if d>0 then
return rep(c or " ",d)..s
end
else
local d=- n-l
if d>0 then
return s..rep(c or " ",d)
end
end
end
return s
end
do
local utfcharacters=utf.characters or string.utfcharacters
local utfchar=utf.char or string.utfcharacter
lpeg.UP=P
if utfcharacters then
function lpeg.US(str)
local p=P(false)
for uc in utfcharacters(str) do
p=p+P(uc)
end
return p
end
else
function lpeg.US(str)
local p=P(false)
local f=function(uc)
p=p+P(uc)
end
lpegmatch((p_utf8char/f)^0,str)
return p
end
end
local range=p_utf8byte*p_utf8byte+Cc(false)
function lpeg.UR(str,more)
local first,last
if type(str)=="number" then
first=str
last=more or first
else
first,last=lpegmatch(range,str)
if not last then
return P(str)
end
end
if first==last then
return P(str)
end
if not utfchar then
utfchar=utf.char
end
if utfchar and (last-first<8) then
local p=P(false)
for i=first,last do
p=p+P(utfchar(i))
end
return p
else
local f=function(b)
return b>=first and b<=last
end
return p_utf8byte/f
end
end
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["l-math"] = package.loaded["l-math"] or true
-- original size: 2555, stripped down to: 1831
if not modules then modules={} end modules ['l-math']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
if not math.ceiling then
math.ceiling=math.ceil
end
if not math.round then
local floor=math.floor
function math.round(x) return floor(x+0.5) end
end
if not math.div then
local floor=math.floor
function math.div(n,m) return floor(n/m) end
end
if not math.mod then
function math.mod(n,m) return n%m end
end
if not math.sind then
local sin,cos,tan=math.sin,math.cos,math.tan
local pipi=2*math.pi/360
function math.sind(d) return sin(d*pipi) end
function math.cosd(d) return cos(d*pipi) end
function math.tand(d) return tan(d*pipi) end
end
if not math.odd then
function math.odd (n) return n%2~=0 end
function math.even(n) return n%2==0 end
end
if not math.cosh then
local exp=math.exp
function math.cosh(x)
local xx=exp(x)
return (xx+1/xx)/2
end
function math.sinh(x)
local xx=exp(x)
return (xx-1/xx)/2
end
function math.tanh(x)
local xx=exp(x)
return (xx-1/xx)/(xx+1/xx)
end
end
if not math.pow then
function math.pow(x,y)
return x^y
end
end
if not math.atan2 then
math.atan2=math.atan
end
if not math.ldexp then
function math.ldexp(x,e)
return x*2.0^e
end
end
if not math.log10 then
local log=math.log
function math.log10(x)
return log(x,10)
end
end
if not math.type then
function math.type()
return "float"
end
end
if not math.tointeger then
math.mininteger=-0x4FFFFFFFFFFF
math.maxinteger=0x4FFFFFFFFFFF
local floor=math.floor
function math.tointeger(n)
local f=floor(n)
return f==n and f or nil
end
end
if not math.ult then
local floor=math.floor
function math.tointeger(m,n)
return floor(m)<floor(n)
end
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["util-str"] = package.loaded["util-str"] or true
-- original size: 43488, stripped down to: 21595
if not modules then modules={} end modules ['util-str']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
utilities=utilities or {}
utilities.strings=utilities.strings or {}
local strings=utilities.strings
local format,gsub,rep,sub,find=string.format,string.gsub,string.rep,string.sub,string.find
local load,dump=load,string.dump
local tonumber,type,tostring,next,setmetatable=tonumber,type,tostring,next,setmetatable
local unpack,concat=table.unpack,table.concat
local 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
local patterns,lpegmatch=lpeg.patterns,lpeg.match
local utfchar,utfbyte,utflen=utf.char,utf.byte,utf.len
local loadstripped=function(str,shortcuts)
if shortcuts then
return load(dump(load(str),true),nil,nil,shortcuts)
else
return load(dump(load(str),true))
end
end
if not number then number={} end
local stripzero=patterns.stripzero
local stripzeros=patterns.stripzeros
local newline=patterns.newline
local endofstring=patterns.endofstring
local anything=patterns.anything
local whitespace=patterns.whitespace
local space=patterns.space
local spacer=patterns.spacer
local spaceortab=patterns.spaceortab
local digit=patterns.digit
local sign=patterns.sign
local period=patterns.period
local ptf=1/65536
local bpf=(7200/7227)/65536
local function points(n)
if n==0 then
return "0pt"
end
n=tonumber(n)
if not n or n==0 then
return "0pt"
end
n=n*ptf
if n%1==0 then
return format("%ipt",n)
end
return lpegmatch(stripzeros,format("%.5fpt",n))
end
local function basepoints(n)
if n==0 then
return "0pt"
end
n=tonumber(n)
if not n or n==0 then
return "0pt"
end
n=n*bpf
if n%1==0 then
return format("%ibp",n)
end
return lpegmatch(stripzeros,format("%.5fbp",n))
end
number.points=points
number.basepoints=basepoints
local rubish=spaceortab^0*newline
local anyrubish=spaceortab+newline
local stripped=(spaceortab^1/"")*newline
local leading=rubish^0/""
local trailing=(anyrubish^1*endofstring)/""
local redundant=rubish^3/"\n"
local pattern=Cs(leading*(trailing+redundant+stripped+anything)^0)
function strings.collapsecrlf(str)
return lpegmatch(pattern,str)
end
local repeaters={}
function strings.newrepeater(str,offset)
offset=offset or 0
local s=repeaters[str]
if not s then
s={}
repeaters[str]=s
end
local t=s[offset]
if t then
return t
end
t={}
setmetatable(t,{ __index=function(t,k)
if not k then
return ""
end
local n=k+offset
local s=n>0 and rep(str,n) or ""
t[k]=s
return s
end })
s[offset]=t
return t
end
local extra,tab,start=0,0,4,0
local nspaces=strings.newrepeater(" ")
string.nspaces=nspaces
local pattern=Carg(1)/function(t)
extra,tab,start=0,t or 7,1
end*Cs((
Cp()*patterns.tab/function(position)
local current=(position-start+1)+extra
local spaces=tab-(current-1)%tab
if spaces>0 then
extra=extra+spaces-1
return nspaces[spaces]
else
return ""
end
end+newline*Cp()/function(position)
extra,start=0,position
end+anything
)^1)
function strings.tabtospace(str,tab)
return lpegmatch(pattern,str,1,tab or 7)
end
function string.utfpadding(s,n)
if not n or n==0 then
return ""
end
local l=utflen(s)
if n>0 then
return nspaces[n-l]
else
return nspaces[-n-l]
end
end
local optionalspace=spacer^0
local nospace=optionalspace/""
local endofline=nospace*newline
local stripend=(whitespace^1*endofstring)/""
local normalline=(nospace*((1-optionalspace*(newline+endofstring))^1)*nospace)
local stripempty=endofline^1/""
local normalempty=endofline^1
local singleempty=endofline*(endofline^0/"")
local doubleempty=endofline*endofline^-1*(endofline^0/"")
local stripstart=stripempty^0
local intospace=whitespace^1/" "
local noleading=whitespace^1/""
local notrailing=noleading*endofstring
local p_prune_normal=Cs (stripstart*(stripend+normalline+normalempty )^0 )
local p_prune_collapse=Cs (stripstart*(stripend+normalline+doubleempty )^0 )
local p_prune_noempty=Cs (stripstart*(stripend+normalline+singleempty )^0 )
local p_prune_intospace=Cs (noleading*(notrailing+intospace+1 )^0 )
local p_retain_normal=Cs ((normalline+normalempty )^0 )
local p_retain_collapse=Cs ((normalline+doubleempty )^0 )
local p_retain_noempty=Cs ((normalline+singleempty )^0 )
local striplinepatterns={
["prune"]=p_prune_normal,
["prune and collapse"]=p_prune_collapse,
["prune and no empty"]=p_prune_noempty,
["prune and to space"]=p_prune_intospace,
["retain"]=p_retain_normal,
["retain and collapse"]=p_retain_collapse,
["retain and no empty"]=p_retain_noempty,
["collapse"]=patterns.collapser,
}
setmetatable(striplinepatterns,{ __index=function(t,k) return p_prune_collapse end })
strings.striplinepatterns=striplinepatterns
function strings.striplines(str,how)
return str and lpegmatch(striplinepatterns[how],str) or str
end
function strings.collapse(str)
return str and lpegmatch(p_prune_intospace,str) or str
end
strings.striplong=strings.striplines
function strings.nice(str)
str=gsub(str,"[:%-+_]+"," ")
return str
end
local n=0
local sequenced=table.sequenced
function string.autodouble(s,sep)
if s==nil then
return '""'
end
local t=type(s)
if t=="number" then
return tostring(s)
end
if t=="table" then
return ('"'..sequenced(s,sep or ",")..'"')
end
return ('"'..tostring(s)..'"')
end
function string.autosingle(s,sep)
if s==nil then
return "''"
end
local t=type(s)
if t=="number" then
return tostring(s)
end
if t=="table" then
return ("'"..sequenced(s,sep or ",").."'")
end
return ("'"..tostring(s).."'")
end
local tracedchars={ [0]=
"[null]","[soh]","[stx]","[etx]","[eot]","[enq]","[ack]","[bel]",
"[bs]","[ht]","[lf]","[vt]","[ff]","[cr]","[so]","[si]",
"[dle]","[dc1]","[dc2]","[dc3]","[dc4]","[nak]","[syn]","[etb]",
"[can]","[em]","[sub]","[esc]","[fs]","[gs]","[rs]","[us]",
"[space]",
}
string.tracedchars=tracedchars
strings.tracers=tracedchars
function string.tracedchar(b)
if type(b)=="number" then
return tracedchars[b] or (utfchar(b).." (U+"..format("%05X",b)..")")
else
local c=utfbyte(b)
return tracedchars[c] or (b.." (U+"..(c and format("%05X",c) or "?????")..")")
end
end
function number.signed(i)
if i>0 then
return "+",i
else
return "-",-i
end
end
local two=digit*digit
local three=two*digit
local prefix=(Carg(1)*three)^1
local splitter=Cs (
(((1-(three^1*period))^1+C(three))*prefix+C((1-period)^1))*(anything/""*Carg(2))*C(2)
)
local splitter3=Cs (
three*prefix*endofstring+two*prefix*endofstring+digit*prefix*endofstring+three+two+digit
)
patterns.formattednumber=splitter
function number.formatted(n,sep1,sep2)
if sep1==false then
if type(n)=="number" then
n=tostring(n)
end
return lpegmatch(splitter3,n,1,sep2 or ".")
else
if type(n)=="number" then
n=format("%0.2f",n)
end
if sep1==true then
return lpegmatch(splitter,n,1,".",",")
elseif sep1=="." then
return lpegmatch(splitter,n,1,sep1,sep2 or ",")
elseif sep1=="," then
return lpegmatch(splitter,n,1,sep1,sep2 or ".")
else
return lpegmatch(splitter,n,1,sep1 or ",",sep2 or ".")
end
end
end
local p=Cs(
P("-")^0*(P("0")^1/"")^0*(1-period)^0*(period*P("0")^1*endofstring/""+period^0)*P(1-P("0")^1*endofstring)^0
)
function number.compactfloat(n,fmt)
if n==0 then
return "0"
elseif n==1 then
return "1"
end
n=lpegmatch(p,format(fmt or "%0.3f",n))
if n=="." or n=="" or n=="-" then
return "0"
end
return n
end
local zero=P("0")^1/""
local plus=P("+")/""
local minus=P("-")
local separator=period
local trailing=zero^1*#S("eE")
local exponent=(S("eE")*(plus+Cs((minus*zero^0*endofstring)/"")+minus)*zero^0*(endofstring*Cc("0")+anything^1))
local pattern_a=Cs(minus^0*digit^1*(separator/""*trailing+separator*(trailing+digit)^0)*exponent)
local pattern_b=Cs((exponent+anything)^0)
function number.sparseexponent(f,n)
if not n then
n=f
f="%e"
end
local tn=type(n)
if tn=="string" then
local m=tonumber(n)
if m then
return lpegmatch((f=="%e" or f=="%E") and pattern_a or pattern_b,format(f,m))
end
elseif tn=="number" then
return lpegmatch((f=="%e" or f=="%E") and pattern_a or pattern_b,format(f,n))
end
return tostring(n)
end
local hf={}
local hs={}
setmetatable(hf,{ __index=function(t,k)
local v="%."..k.."f"
t[k]=v
return v
end } )
setmetatable(hs,{ __index=function(t,k)
local v="%"..k.."s"
t[k]=v
return v
end } )
function number.formattedfloat(n,b,a)
local s=format(hf[a],n)
local l=(b or 0)+(a or 0)+1
if #s<l then
return format(hs[l],s)
else
return s
end
end
local template=[[
%s
%s
return function(%s) return %s end
]]
local preamble=""
local environment={
global=global or _G,
lpeg=lpeg,
type=type,
tostring=tostring,
tonumber=tonumber,
format=string.format,
concat=table.concat,
signed=number.signed,
points=number.points,
basepoints=number.basepoints,
utfchar=utf.char,
utfbyte=utf.byte,
lpegmatch=lpeg.match,
nspaces=string.nspaces,
utfpadding=string.utfpadding,
tracedchar=string.tracedchar,
autosingle=string.autosingle,
autodouble=string.autodouble,
sequenced=table.sequenced,
formattednumber=number.formatted,
sparseexponent=number.sparseexponent,
formattedfloat=number.formattedfloat,
stripzero=lpeg.patterns.stripzero,
stripzeros=lpeg.patterns.stripzeros,
FORMAT=string.f9,
}
local arguments={ "a1" }
setmetatable(arguments,{ __index=function(t,k)
local v=t[k-1]..",a"..k
t[k]=v
return v
end
})
local prefix_any=C((sign+space+period+digit)^0)
local prefix_sub=(C((sign+digit)^0)+Cc(0))*period*(C((sign+digit)^0)+Cc(0))
local prefix_tab=P("{")*C((1-P("}"))^0)*P("}")+C((1-R("az","AZ","09","%%"))^0)
local format_s=function(f)
n=n+1
if f and f~="" then
return format("format('%%%ss',a%s)",f,n)
else
return format("(a%s or '')",n)
end
end
local format_S=function(f)
n=n+1
if f and f~="" then
return format("format('%%%ss',tostring(a%s))",f,n)
else
return format("tostring(a%s)",n)
end
end
local format_right=function(f)
n=n+1
f=tonumber(f)
if not f or f==0 then
return format("(a%s or '')",n)
elseif f>0 then
return format("utfpadding(a%s,%i)..a%s",n,f,n)
else
return format("a%s..utfpadding(a%s,%i)",n,n,f)
end
end
local format_left=function(f)
n=n+1
f=tonumber(f)
if not f or f==0 then
return format("(a%s or '')",n)
end
if f<0 then
return format("utfpadding(a%s,%i)..a%s",n,-f,n)
else
return format("a%s..utfpadding(a%s,%i)",n,n,-f)
end
end
local format_q=function()
n=n+1
return format("(a%s ~= nil and format('%%q',tostring(a%s)) or '')",n,n)
end
local format_Q=function()
n=n+1
return format("format('%%q',tostring(a%s))",n)
end
local format_i=function(f)
n=n+1
if f and f~="" then
return format("format('%%%si',a%s)",f,n)
else
return format("format('%%i',a%s)",n)
end
end
local format_d=format_i
local format_I=function(f)
n=n+1
return format("format('%%s%%%si',signed(a%s))",f,n)
end
local format_f=function(f)
n=n+1
return format("format('%%%sf',a%s)",f,n)
end
local format_F=function(f)
n=n+1
if not f or f=="" then
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)
else
return format("format((a%s %% 1 == 0) and '%%i' or '%%%sf',a%s)",n,f,n)
end
end
local format_k=function(b,a)
n=n+1
return format("formattedfloat(a%s,%s,%s)",n,b or 0,a or 0)
end
local format_g=function(f)
n=n+1
return format("format('%%%sg',a%s)",f,n)
end
local format_G=function(f)
n=n+1
return format("format('%%%sG',a%s)",f,n)
end
local format_e=function(f)
n=n+1
return format("format('%%%se',a%s)",f,n)
end
local format_E=function(f)
n=n+1
return format("format('%%%sE',a%s)",f,n)
end
local format_j=function(f)
n=n+1
return format("sparseexponent('%%%se',a%s)",f,n)
end
local format_J=function(f)
n=n+1
return format("sparseexponent('%%%sE',a%s)",f,n)
end
local format_x=function(f)
n=n+1
return format("format('%%%sx',a%s)",f,n)
end
local format_X=function(f)
n=n+1
return format("format('%%%sX',a%s)",f,n)
end
local format_o=function(f)
n=n+1
return format("format('%%%so',a%s)",f,n)
end
local format_c=function()
n=n+1
return format("utfchar(a%s)",n)
end
local format_C=function()
n=n+1
return format("tracedchar(a%s)",n)
end
local format_r=function(f)
n=n+1
return format("format('%%%s.0f',a%s)",f,n)
end
local format_h=function(f)
n=n+1
if f=="-" then
f=sub(f,2)
return format("format('%%%sx',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n)
else
return format("format('0x%%%sx',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n)
end
end
local format_H=function(f)
n=n+1
if f=="-" then
f=sub(f,2)
return format("format('%%%sX',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n)
else
return format("format('0x%%%sX',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n)
end
end
local format_u=function(f)
n=n+1
if f=="-" then
f=sub(f,2)
return format("format('%%%sx',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n)
else
return format("format('u+%%%sx',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n)
end
end
local format_U=function(f)
n=n+1
if f=="-" then
f=sub(f,2)
return format("format('%%%sX',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n)
else
return format("format('U+%%%sX',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n)
end
end
local format_p=function()
n=n+1
return format("points(a%s)",n)
end
local format_b=function()
n=n+1
return format("basepoints(a%s)",n)
end
local format_t=function(f)
n=n+1
if f and f~="" then
return format("concat(a%s,%q)",n,f)
else
return format("concat(a%s)",n)
end
end
local format_T=function(f)
n=n+1
if f and f~="" then
return format("sequenced(a%s,%q)",n,f)
else
return format("sequenced(a%s)",n)
end
end
local format_l=function()
n=n+1
return format("(a%s and 'true' or 'false')",n)
end
local format_L=function()
n=n+1
return format("(a%s and 'TRUE' or 'FALSE')",n)
end
local format_n=function()
n=n+1
return format("((a%s %% 1 == 0) and format('%%i',a%s) or tostring(a%s))",n,n,n)
end
local format_N=function(f)
n=n+1
if not f or f=="" then
f=".9"
end
return format("(((a%s %% 1 == 0) and format('%%i',a%s)) or lpegmatch(stripzero,format('%%%sf',a%s)))",n,n,f,n)
end
local format_a=function(f)
n=n+1
if f and f~="" then
return format("autosingle(a%s,%q)",n,f)
else
return format("autosingle(a%s)",n)
end
end
local format_A=function(f)
n=n+1
if f and f~="" then
return format("autodouble(a%s,%q)",n,f)
else
return format("autodouble(a%s)",n)
end
end
local format_w=function(f)
n=n+1
f=tonumber(f)
if f then
return format("nspaces[%s+a%s]",f,n)
else
return format("nspaces[a%s]",n)
end
end
local format_W=function(f)
return format("nspaces[%s]",tonumber(f) or 0)
end
local format_m=function(f)
n=n+1
if not f or f=="" then
f=","
end
if f=="0" then
return format([[formattednumber(a%s,false)]],n)
else
return format([[formattednumber(a%s,%q,".")]],n,f)
end
end
local format_M=function(f)
n=n+1
if not f or f=="" then
f="."
end
if f=="0" then
return format([[formattednumber(a%s,false)]],n)
else
return format([[formattednumber(a%s,%q,",")]],n,f)
end
end
local format_z=function(f)
n=n+(tonumber(f) or 1)
return "''"
end
local format_rest=function(s)
return format("%q",s)
end
local format_extension=function(extensions,f,name)
local extension=extensions[name] or "tostring(%s)"
local f=tonumber(f) or 1
local w=find(extension,"%.%.%.")
if w then
if f==0 then
extension=gsub(extension,"%.%.%.","")
return extension
elseif f==1 then
extension=gsub(extension,"%.%.%.","%%s")
n=n+1
local a="a"..n
return format(extension,a,a)
elseif f<0 then
local a="a"..(n+f+1)
return format(extension,a,a)
else
extension=gsub(extension,"%.%.%.",rep("%%s,",f-1).."%%s")
local t={}
for i=1,f do
n=n+1
t[i]="a"..n
end
return format(extension,unpack(t))
end
else
extension=gsub(extension,"%%s",function()
n=n+1
return "a"..n
end)
return extension
end
end
local builder=Cs { "start",
start=(
(
P("%")/""*(
V("!")
+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")
+V("c")+V("C")+V("S")
+V("Q")
+V("n")
+V("N")
+V("k")
+V("r")+V("h")+V("H")+V("u")+V("U")+V("p")+V("b")+V("t")+V("T")+V("l")+V("L")+V("I")+V("w")
+V("W")
+V("a")
+V("A")
+V("j")+V("J")
+V("m")+V("M")
+V("z")
+V(">")
+V("<")
)+V("*")
)*(endofstring+Carg(1))
)^0,
["s"]=(prefix_any*P("s"))/format_s,
["q"]=(prefix_any*P("q"))/format_q,
["i"]=(prefix_any*P("i"))/format_i,
["d"]=(prefix_any*P("d"))/format_d,
["f"]=(prefix_any*P("f"))/format_f,
["F"]=(prefix_any*P("F"))/format_F,
["g"]=(prefix_any*P("g"))/format_g,
["G"]=(prefix_any*P("G"))/format_G,
["e"]=(prefix_any*P("e"))/format_e,
["E"]=(prefix_any*P("E"))/format_E,
["x"]=(prefix_any*P("x"))/format_x,
["X"]=(prefix_any*P("X"))/format_X,
["o"]=(prefix_any*P("o"))/format_o,
["S"]=(prefix_any*P("S"))/format_S,
["Q"]=(prefix_any*P("Q"))/format_Q,
["n"]=(prefix_any*P("n"))/format_n,
["N"]=(prefix_any*P("N"))/format_N,
["k"]=(prefix_sub*P("k"))/format_k,
["c"]=(prefix_any*P("c"))/format_c,
["C"]=(prefix_any*P("C"))/format_C,
["r"]=(prefix_any*P("r"))/format_r,
["h"]=(prefix_any*P("h"))/format_h,
["H"]=(prefix_any*P("H"))/format_H,
["u"]=(prefix_any*P("u"))/format_u,
["U"]=(prefix_any*P("U"))/format_U,
["p"]=(prefix_any*P("p"))/format_p,
["b"]=(prefix_any*P("b"))/format_b,
["t"]=(prefix_tab*P("t"))/format_t,
["T"]=(prefix_tab*P("T"))/format_T,
["l"]=(prefix_any*P("l"))/format_l,
["L"]=(prefix_any*P("L"))/format_L,
["I"]=(prefix_any*P("I"))/format_I,
["w"]=(prefix_any*P("w"))/format_w,
["W"]=(prefix_any*P("W"))/format_W,
["j"]=(prefix_any*P("j"))/format_j,
["J"]=(prefix_any*P("J"))/format_J,
["m"]=(prefix_any*P("m"))/format_m,
["M"]=(prefix_any*P("M"))/format_M,
["z"]=(prefix_any*P("z"))/format_z,
["a"]=(prefix_any*P("a"))/format_a,
["A"]=(prefix_any*P("A"))/format_A,
["<"]=(prefix_any*P("<"))/format_left,
[">"]=(prefix_any*P(">"))/format_right,
["*"]=Cs(((1-P("%"))^1+P("%%")/"%%")^1)/format_rest,
["?"]=Cs(((1-P("%"))^1 )^1)/format_rest,
["!"]=Carg(2)*prefix_any*P("!")*C((1-P("!"))^1)*P("!")/format_extension,
}
local xx=setmetatable({},{ __index=function(t,k) local v=format("%02x",k) t[k]=v return v end })
local XX=setmetatable({},{ __index=function(t,k) local v=format("%02X",k) t[k]=v return v end })
local preset={
["%02x"]=function(n) return xx[n] end,
["%02X"]=function(n) return XX[n] end,
}
local direct=P("%")*(sign+space+period+digit)^0*S("sqidfgGeExXo")*endofstring/[[local format = string.format return function(str) return format("%0",str) end]]
local function make(t,str)
local f=preset[str]
if f then
return f
end
local p=lpegmatch(direct,str)
if p then
f=loadstripped(p)()
else
n=0
p=lpegmatch(builder,str,1,t._connector_,t._extensions_)
if n>0 then
p=format(template,preamble,t._preamble_,arguments[n],p)
f=loadstripped(p,t._environment_)()
else
f=function() return str end
end
end
t[str]=f
return f
end
local function use(t,fmt,...)
return t[fmt](...)
end
strings.formatters={}
function strings.formatters.new(noconcat)
local e={}
for k,v in next,environment do
e[k]=v
end
local t={
_type_="formatter",
_connector_=noconcat and "," or "..",
_extensions_={},
_preamble_="",
_environment_=e,
}
setmetatable(t,{ __index=make,__call=use })
return t
end
local formatters=strings.formatters.new()
string.formatters=formatters
string.formatter=function(str,...) return formatters[str](...) end
local function add(t,name,template,preamble)
if type(t)=="table" and t._type_=="formatter" then
t._extensions_[name]=template or "%s"
if type(preamble)=="string" then
t._preamble_=preamble.."\n"..t._preamble_
elseif type(preamble)=="table" then
for k,v in next,preamble do
t._environment_[k]=v
end
end
end
end
strings.formatters.add=add
patterns.xmlescape=Cs((P("<")/"<"+P(">")/">"+P("&")/"&"+P('"')/"""+anything)^0)
patterns.texescape=Cs((C(S("#$%\\{}"))/"\\%1"+anything)^0)
patterns.luaescape=Cs(((1-S('"\n'))^1+P('"')/'\\"'+P('\n')/'\\n"')^0)
patterns.luaquoted=Cs(Cc('"')*((1-S('"\n'))^1+P('"')/'\\"'+P('\n')/'\\n"')^0*Cc('"'))
add(formatters,"xml",[[lpegmatch(xmlescape,%s)]],{ xmlescape=lpeg.patterns.xmlescape })
add(formatters,"tex",[[lpegmatch(texescape,%s)]],{ texescape=lpeg.patterns.texescape })
add(formatters,"lua",[[lpegmatch(luaescape,%s)]],{ luaescape=lpeg.patterns.luaescape })
local dquote=patterns.dquote
local equote=patterns.escaped+dquote/'\\"'+1
local cquote=Cc('"')
local pattern=Cs(dquote*(equote-P(-2))^0*dquote)
+Cs(cquote*(equote-space)^0*space*equote^0*cquote)
function string.optionalquoted(str)
return lpegmatch(pattern,str) or str
end
local pattern=Cs((newline/(os.newline or "\r")+1)^0)
function string.replacenewlines(str)
return lpegmatch(pattern,str)
end
function strings.newcollector()
local result,r={},0
return
function(fmt,str,...)
r=r+1
result[r]=str==nil and fmt or formatters[fmt](str,...)
end,
function(connector)
if result then
local str=concat(result,connector)
result,r={},0
return str
end
end
end
local f_16_16=formatters["%0.5N"]
function number.to16dot16(n)
return f_16_16(n/65536.0)
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["util-tab"] = package.loaded["util-tab"] or true
-- original size: 28866, stripped down to: 16134
if not modules then modules={} end modules ['util-tab']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
utilities=utilities or {}
utilities.tables=utilities.tables or {}
local tables=utilities.tables
local format,gmatch,gsub,sub=string.format,string.gmatch,string.gsub,string.sub
local concat,insert,remove,sort=table.concat,table.insert,table.remove,table.sort
local setmetatable,getmetatable,tonumber,tostring,rawget=setmetatable,getmetatable,tonumber,tostring,rawget
local type,next,rawset,tonumber,tostring,load,select=type,next,rawset,tonumber,tostring,load,select
local lpegmatch,P,Cs,Cc=lpeg.match,lpeg.P,lpeg.Cs,lpeg.Cc
local sortedkeys,sortedpairs=table.sortedkeys,table.sortedpairs
local formatters=string.formatters
local utftoeight=utf.toeight
local splitter=lpeg.tsplitat(".")
function utilities.tables.definetable(target,nofirst,nolast)
local composed=nil
local t={}
local snippets=lpegmatch(splitter,target)
for i=1,#snippets-(nolast and 1 or 0) do
local name=snippets[i]
if composed then
composed=composed.."."..name
t[#t+1]=formatters["if not %s then %s = { } end"](composed,composed)
else
composed=name
if not nofirst then
t[#t+1]=formatters["%s = %s or { }"](composed,composed)
end
end
end
if composed then
if nolast then
composed=composed.."."..snippets[#snippets]
end
return concat(t,"\n"),composed
else
return "",target
end
end
function tables.definedtable(...)
local t=_G
for i=1,select("#",...) do
local li=select(i,...)
local tl=t[li]
if not tl then
tl={}
t[li]=tl
end
t=tl
end
return t
end
function tables.accesstable(target,root)
local t=root or _G
for name in gmatch(target,"([^%.]+)") do
t=t[name]
if not t then
return
end
end
return t
end
function tables.migratetable(target,v,root)
local t=root or _G
local names=lpegmatch(splitter,target)
for i=1,#names-1 do
local name=names[i]
t[name]=t[name] or {}
t=t[name]
if not t then
return
end
end
t[names[#names]]=v
end
function tables.removevalue(t,value)
if value then
for i=1,#t do
if t[i]==value then
remove(t,i)
end
end
end
end
function tables.replacevalue(t,oldvalue,newvalue)
if oldvalue and newvalue then
for i=1,#t do
if t[i]==oldvalue then
t[i]=newvalue
end
end
end
end
function tables.insertbeforevalue(t,value,extra)
for i=1,#t do
if t[i]==extra then
remove(t,i)
end
end
for i=1,#t do
if t[i]==value then
insert(t,i,extra)
return
end
end
insert(t,1,extra)
end
function tables.insertaftervalue(t,value,extra)
for i=1,#t do
if t[i]==extra then
remove(t,i)
end
end
for i=1,#t do
if t[i]==value then
insert(t,i+1,extra)
return
end
end
insert(t,#t+1,extra)
end
local escape=Cs(Cc('"')*((P('"')/'""'+P(1))^0)*Cc('"'))
function table.tocsv(t,specification)
if t and #t>0 then
local result={}
local r={}
specification=specification or {}
local fields=specification.fields
if type(fields)~="string" then
fields=sortedkeys(t[1])
end
local separator=specification.separator or ","
local noffields=#fields
if specification.preamble==true then
for f=1,noffields do
r[f]=lpegmatch(escape,tostring(fields[f]))
end
result[1]=concat(r,separator)
end
for i=1,#t do
local ti=t[i]
for f=1,noffields do
local field=ti[fields[f]]
if type(field)=="string" then
r[f]=lpegmatch(escape,field)
else
r[f]=tostring(field)
end
end
result[i+1]=concat(r,separator)
end
return concat(result,"\n")
else
return ""
end
end
local nspaces=utilities.strings.newrepeater(" ")
local function toxml(t,d,result,step)
local r=#result
for k,v in sortedpairs(t) do
local s=nspaces[d]
local tk=type(k)
local tv=type(v)
if tv=="table" then
if tk=="number" then
r=r+1 result[r]=formatters["%s<entry n='%s'>"](s,k)
toxml(v,d+step,result,step)
r=r+1 result[r]=formatters["%s</entry>"](s,k)
else
r=r+1 result[r]=formatters["%s<%s>"](s,k)
toxml(v,d+step,result,step)
r=r+1 result[r]=formatters["%s</%s>"](s,k)
end
elseif tv=="string" then
if tk=="number" then
r=r+1 result[r]=formatters["%s<entry n='%s'>%!xml!</entry>"](s,k,v,k)
else
r=r+1 result[r]=formatters["%s<%s>%!xml!</%s>"](s,k,v,k)
end
elseif tk=="number" then
r=r+1 result[r]=formatters["%s<entry n='%s'>%S</entry>"](s,k,v,k)
else
r=r+1 result[r]=formatters["%s<%s>%S</%s>"](s,k,v,k)
end
end
end
function table.toxml(t,specification)
specification=specification or {}
local name=specification.name
local noroot=name==false
local result=(specification.nobanner or noroot) and {} or { "<?xml version='1.0' standalone='yes' ?>" }
local indent=specification.indent or 0
local spaces=specification.spaces or 1
if noroot then
toxml(t,indent,result,spaces)
else
toxml({ [name or "data"]=t },indent,result,spaces)
end
return concat(result,"\n")
end
function tables.encapsulate(core,capsule,protect)
if type(capsule)~="table" then
protect=true
capsule={}
end
for key,value in next,core do
if capsule[key] then
print(formatters["\ninvalid %s %a in %a"]("inheritance",key,core))
os.exit()
else
capsule[key]=value
end
end
if protect then
for key,value in next,core do
core[key]=nil
end
setmetatable(core,{
__index=capsule,
__newindex=function(t,key,value)
if capsule[key] then
print(formatters["\ninvalid %s %a' in %a"]("overload",key,core))
os.exit()
else
rawset(t,key,value)
end
end
} )
end
end
local f_hashed_string=formatters["[%q]=%q,"]
local f_hashed_number=formatters["[%q]=%s,"]
local f_hashed_boolean=formatters["[%q]=%l,"]
local f_hashed_table=formatters["[%q]="]
local f_indexed_string=formatters["[%s]=%q,"]
local f_indexed_number=formatters["[%s]=%s,"]
local f_indexed_boolean=formatters["[%s]=%l,"]
local f_indexed_table=formatters["[%s]="]
local f_ordered_string=formatters["%q,"]
local f_ordered_number=formatters["%s,"]
local f_ordered_boolean=formatters["%l,"]
function table.fastserialize(t,prefix)
local r={ type(prefix)=="string" and prefix or "return" }
local m=1
local function fastserialize(t,outer)
local n=#t
m=m+1
r[m]="{"
if n>0 then
for i=0,n do
local v=t[i]
local tv=type(v)
if tv=="string" then
m=m+1 r[m]=f_ordered_string(v)
elseif tv=="number" then
m=m+1 r[m]=f_ordered_number(v)
elseif tv=="table" then
fastserialize(v)
elseif tv=="boolean" then
m=m+1 r[m]=f_ordered_boolean(v)
end
end
end
for k,v in next,t do
local tk=type(k)
if tk=="number" then
if k>n or k<0 then
local tv=type(v)
if tv=="string" then
m=m+1 r[m]=f_indexed_string(k,v)
elseif tv=="number" then
m=m+1 r[m]=f_indexed_number(k,v)
elseif tv=="table" then
m=m+1 r[m]=f_indexed_table(k)
fastserialize(v)
elseif tv=="boolean" then
m=m+1 r[m]=f_indexed_boolean(k,v)
end
end
else
local tv=type(v)
if tv=="string" then
m=m+1 r[m]=f_hashed_string(k,v)
elseif tv=="number" then
m=m+1 r[m]=f_hashed_number(k,v)
elseif tv=="table" then
m=m+1 r[m]=f_hashed_table(k)
fastserialize(v)
elseif tv=="boolean" then
m=m+1 r[m]=f_hashed_boolean(k,v)
end
end
end
m=m+1
if outer then
r[m]="}"
else
r[m]="},"
end
return r
end
return concat(fastserialize(t,true))
end
function table.deserialize(str)
if not str or str=="" then
return
end
local code=load(str)
if not code then
return
end
code=code()
if not code then
return
end
return code
end
function table.load(filename,loader)
if filename then
local t=(loader or io.loaddata)(filename)
if t and t~="" then
local t=utftoeight(t)
t=load(t)
if type(t)=="function" then
t=t()
if type(t)=="table" then
return t
end
end
end
end
end
function table.save(filename,t,n,...)
io.savedata(filename,table.serialize(t,n==nil and true or n,...))
end
local f_key_value=formatters["%s=%q"]
local f_add_table=formatters[" {%t},\n"]
local f_return_table=formatters["return {\n%t}"]
local function slowdrop(t)
local r={}
local l={}
for i=1,#t do
local ti=t[i]
local j=0
for k,v in next,ti do
j=j+1
l[j]=f_key_value(k,v)
end
r[i]=f_add_table(l)
end
return f_return_table(r)
end
local function fastdrop(t)
local r={ "return {\n" }
local m=1
for i=1,#t do
local ti=t[i]
m=m+1 r[m]=" {"
for k,v in next,ti do
m=m+1 r[m]=f_key_value(k,v)
end
m=m+1 r[m]="},\n"
end
m=m+1
r[m]="}"
return concat(r)
end
function table.drop(t,slow)
if #t==0 then
return "return { }"
elseif slow==true then
return slowdrop(t)
else
return fastdrop(t)
end
end
local selfmapper={ __index=function(t,k) t[k]=k return k end }
function table.twowaymapper(t)
if not t then
t={}
else
local zero=rawget(t,0)
for i=zero and 0 or 1,#t do
local ti=t[i]
if ti then
local i=tostring(i)
t[i]=ti
t[ti]=i
end
end
end
setmetatable(t,selfmapper)
return t
end
local f_start_key_idx=formatters["%w{"]
local f_start_key_num=formatters["%w[%s]={"]
local f_start_key_str=formatters["%w[%q]={"]
local f_start_key_boo=formatters["%w[%l]={"]
local f_start_key_nop=formatters["%w{"]
local f_stop=formatters["%w},"]
local f_key_num_value_num=formatters["%w[%s]=%s,"]
local f_key_str_value_num=formatters["%w[%q]=%s,"]
local f_key_boo_value_num=formatters["%w[%l]=%s,"]
local f_key_num_value_str=formatters["%w[%s]=%q,"]
local f_key_str_value_str=formatters["%w[%q]=%q,"]
local f_key_boo_value_str=formatters["%w[%l]=%q,"]
local f_key_num_value_boo=formatters["%w[%s]=%l,"]
local f_key_str_value_boo=formatters["%w[%q]=%l,"]
local f_key_boo_value_boo=formatters["%w[%l]=%l,"]
local f_key_num_value_not=formatters["%w[%s]={},"]
local f_key_str_value_not=formatters["%w[%q]={},"]
local f_key_boo_value_not=formatters["%w[%l]={},"]
local f_key_num_value_seq=formatters["%w[%s]={ %, t },"]
local f_key_str_value_seq=formatters["%w[%q]={ %, t },"]
local f_key_boo_value_seq=formatters["%w[%l]={ %, t },"]
local f_val_num=formatters["%w%s,"]
local f_val_str=formatters["%w%q,"]
local f_val_boo=formatters["%w%l,"]
local f_val_not=formatters["%w{},"]
local f_val_seq=formatters["%w{ %, t },"]
local f_fin_seq=formatters[" %, t }"]
local f_table_return=formatters["return {"]
local f_table_name=formatters["%s={"]
local f_table_direct=formatters["{"]
local f_table_entry=formatters["[%q]={"]
local f_table_finish=formatters["}"]
local spaces=utilities.strings.newrepeater(" ")
local original_serialize=table.serialize
local is_simple_table=table.is_simple_table
local function serialize(root,name,specification)
if type(specification)=="table" then
return original_serialize(root,name,specification)
end
local t
local n=1
local unknown=false
local function do_serialize(root,name,depth,level,indexed)
if level>0 then
n=n+1
if indexed then
t[n]=f_start_key_idx(depth)
else
local tn=type(name)
if tn=="number" then
t[n]=f_start_key_num(depth,name)
elseif tn=="string" then
t[n]=f_start_key_str(depth,name)
elseif tn=="boolean" then
t[n]=f_start_key_boo(depth,name)
else
t[n]=f_start_key_nop(depth)
end
end
depth=depth+1
end
if root and next(root)~=nil then
local first=nil
local last=#root
if last>0 then
for k=1,last do
if rawget(root,k)==nil then
last=k-1
break
end
end
if last>0 then
first=1
end
end
local sk=sortedkeys(root)
for i=1,#sk do
local k=sk[i]
local v=root[k]
local tv=type(v)
local tk=type(k)
if first and tk=="number" and k<=last and k>=first then
if tv=="number" then
n=n+1 t[n]=f_val_num(depth,v)
elseif tv=="string" then
n=n+1 t[n]=f_val_str(depth,v)
elseif tv=="table" then
if next(v)==nil then
n=n+1 t[n]=f_val_not(depth)
else
local st=is_simple_table(v)
if st then
n=n+1 t[n]=f_val_seq(depth,st)
else
do_serialize(v,k,depth,level+1,true)
end
end
elseif tv=="boolean" then
n=n+1 t[n]=f_val_boo(depth,v)
elseif unknown then
n=n+1 t[n]=f_val_str(depth,tostring(v))
end
elseif tv=="number" then
if tk=="number" then
n=n+1 t[n]=f_key_num_value_num(depth,k,v)
elseif tk=="string" then
n=n+1 t[n]=f_key_str_value_num(depth,k,v)
elseif tk=="boolean" then
n=n+1 t[n]=f_key_boo_value_num(depth,k,v)
elseif unknown then
n=n+1 t[n]=f_key_str_value_num(depth,tostring(k),v)
end
elseif tv=="string" then
if tk=="number" then
n=n+1 t[n]=f_key_num_value_str(depth,k,v)
elseif tk=="string" then
n=n+1 t[n]=f_key_str_value_str(depth,k,v)
elseif tk=="boolean" then
n=n+1 t[n]=f_key_boo_value_str(depth,k,v)
elseif unknown then
n=n+1 t[n]=f_key_str_value_str(depth,tostring(k),v)
end
elseif tv=="table" then
if next(v)==nil then
if tk=="number" then
n=n+1 t[n]=f_key_num_value_not(depth,k)
elseif tk=="string" then
n=n+1 t[n]=f_key_str_value_not(depth,k)
elseif tk=="boolean" then
n=n+1 t[n]=f_key_boo_value_not(depth,k)
elseif unknown then
n=n+1 t[n]=f_key_str_value_not(depth,tostring(k))
end
else
local st=is_simple_table(v)
if not st then
do_serialize(v,k,depth,level+1)
elseif tk=="number" then
n=n+1 t[n]=f_key_num_value_seq(depth,k,st)
elseif tk=="string" then
n=n+1 t[n]=f_key_str_value_seq(depth,k,st)
elseif tk=="boolean" then
n=n+1 t[n]=f_key_boo_value_seq(depth,k,st)
elseif unknown then
n=n+1 t[n]=f_key_str_value_seq(depth,tostring(k),st)
end
end
elseif tv=="boolean" then
if tk=="number" then
n=n+1 t[n]=f_key_num_value_boo(depth,k,v)
elseif tk=="string" then
n=n+1 t[n]=f_key_str_value_boo(depth,k,v)
elseif tk=="boolean" then
n=n+1 t[n]=f_key_boo_value_boo(depth,k,v)
elseif unknown then
n=n+1 t[n]=f_key_str_value_boo(depth,tostring(k),v)
end
else
if tk=="number" then
n=n+1 t[n]=f_key_num_value_str(depth,k,tostring(v))
elseif tk=="string" then
n=n+1 t[n]=f_key_str_value_str(depth,k,tostring(v))
elseif tk=="boolean" then
n=n+1 t[n]=f_key_boo_value_str(depth,k,tostring(v))
elseif unknown then
n=n+1 t[n]=f_key_str_value_str(depth,tostring(k),tostring(v))
end
end
end
end
if level>0 then
n=n+1 t[n]=f_stop(depth-1)
end
end
local tname=type(name)
if tname=="string" then
if name=="return" then
t={ f_table_return() }
else
t={ f_table_name(name) }
end
elseif tname=="number" then
t={ f_table_entry(name) }
elseif tname=="boolean" then
if name then
t={ f_table_return() }
else
t={ f_table_direct() }
end
else
t={ f_table_name("t") }
end
if root then
if getmetatable(root) then
local dummy=root._w_h_a_t_e_v_e_r_
root._w_h_a_t_e_v_e_r_=nil
end
if next(root)~=nil then
local st=is_simple_table(root)
if st then
return t[1]..f_fin_seq(st)
else
do_serialize(root,name,1,0)
end
end
end
n=n+1
t[n]=f_table_finish()
return concat(t,"\n")
end
table.serialize=serialize
if setinspector then
setinspector("table",function(v)
if type(v)=="table" then
print(serialize(v,"table",{ metacheck=false }))
return true
end
end)
end
local mt={
__newindex=function(t,k,v)
local n=t.last+1
t.last=n
t.list[n]=k
t.hash[k]=v
end,
__index=function(t,k)
return t.hash[k]
end,
__len=function(t)
return t.last
end,
}
function table.orderedhash()
return setmetatable({ list={},hash={},last=0 },mt)
end
function table.ordered(t)
local n=t.last
if n>0 then
local l=t.list
local i=1
local h=t.hash
local f=function()
if i<=n then
local k=i
local v=h[l[k]]
i=i+1
return k,v
end
end
return f,1,h[l[1]]
else
return function() end
end
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["util-fil"] = package.loaded["util-fil"] or true
-- original size: 11530, stripped down to: 9007
if not modules then modules={} end modules ['util-fil']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local tonumber=tonumber
local byte=string.byte
local char=string.char
utilities=utilities or {}
local files={}
utilities.files=files
local zerobased={}
function files.open(filename,zb)
local f=io.open(filename,"rb")
if f then
zerobased[f]=zb or false
end
return f
end
function files.close(f)
zerobased[f]=nil
f:close()
end
function files.size(f)
local current=f:seek()
local size=f:seek("end")
f:seek("set",current)
return size
end
files.getsize=files.size
function files.setposition(f,n)
if zerobased[f] then
f:seek("set",n)
else
f:seek("set",n-1)
end
end
function files.getposition(f)
if zerobased[f] then
return f:seek()
else
return f:seek()+1
end
end
function files.look(f,n,chars)
local p=f:seek()
local s=f:read(n)
f:seek("set",p)
if chars then
return s
else
return byte(s,1,#s)
end
end
function files.skip(f,n)
if n==1 then
f:read(n)
else
f:seek("set",f:seek()+n)
end
end
function files.readbyte(f)
return byte(f:read(1))
end
function files.readbytes(f,n)
return byte(f:read(n),1,n)
end
function files.readbytetable(f,n)
local s=f:read(n or 1)
return { byte(s,1,#s) }
end
function files.readchar(f)
return f:read(1)
end
function files.readstring(f,n)
return f:read(n or 1)
end
function files.readinteger1(f)
local n=byte(f:read(1))
if n>=0x80 then
return n-0x100
else
return n
end
end
files.readcardinal1=files.readbyte
files.readcardinal=files.readcardinal1
files.readinteger=files.readinteger1
files.readsignedbyte=files.readinteger1
function files.readcardinal2(f)
local a,b=byte(f:read(2),1,2)
return 0x100*a+b
end
function files.readcardinal2le(f)
local b,a=byte(f:read(2),1,2)
return 0x100*a+b
end
function files.readinteger2(f)
local a,b=byte(f:read(2),1,2)
if a>=0x80 then
return 0x100*a+b-0x10000
else
return 0x100*a+b
end
end
function files.readinteger2le(f)
local b,a=byte(f:read(2),1,2)
if a>=0x80 then
return 0x100*a+b-0x10000
else
return 0x100*a+b
end
end
function files.readcardinal3(f)
local a,b,c=byte(f:read(3),1,3)
return 0x10000*a+0x100*b+c
end
function files.readcardinal3le(f)
local c,b,a=byte(f:read(3),1,3)
return 0x10000*a+0x100*b+c
end
function files.readinteger3(f)
local a,b,c=byte(f:read(3),1,3)
if a>=0x80 then
return 0x10000*a+0x100*b+c-0x1000000
else
return 0x10000*a+0x100*b+c
end
end
function files.readinteger3le(f)
local c,b,a=byte(f:read(3),1,3)
if a>=0x80 then
return 0x10000*a+0x100*b+c-0x1000000
else
return 0x10000*a+0x100*b+c
end
end
function files.readcardinal4(f)
local a,b,c,d=byte(f:read(4),1,4)
return 0x1000000*a+0x10000*b+0x100*c+d
end
function files.readcardinal4le(f)
local d,c,b,a=byte(f:read(4),1,4)
return 0x1000000*a+0x10000*b+0x100*c+d
end
function files.readinteger4(f)
local a,b,c,d=byte(f:read(4),1,4)
if a>=0x80 then
return 0x1000000*a+0x10000*b+0x100*c+d-0x100000000
else
return 0x1000000*a+0x10000*b+0x100*c+d
end
end
function files.readinteger4le(f)
local d,c,b,a=byte(f:read(4),1,4)
if a>=0x80 then
return 0x1000000*a+0x10000*b+0x100*c+d-0x100000000
else
return 0x1000000*a+0x10000*b+0x100*c+d
end
end
function files.readfixed2(f)
local a,b=byte(f:read(2),1,2)
if a>=0x80 then
tonumber((a-0x100).."."..b)
else
tonumber((a ).."."..b)
end
end
function files.readfixed4(f)
local a,b,c,d=byte(f:read(4),1,4)
if a>=0x80 then
tonumber((0x100*a+b-0x10000).."."..(0x100*c+d))
else
tonumber((0x100*a+b ).."."..(0x100*c+d))
end
end
if bit32 then
local extract=bit32.extract
local band=bit32.band
function files.read2dot14(f)
local a,b=byte(f:read(2),1,2)
if a>=0x80 then
local n=-(0x100*a+b)
return-(extract(n,14,2)+(band(n,0x3FFF)/16384.0))
else
local n=0x100*a+b
return (extract(n,14,2)+(band(n,0x3FFF)/16384.0))
end
end
end
function files.skipshort(f,n)
f:read(2*(n or 1))
end
function files.skiplong(f,n)
f:read(4*(n or 1))
end
if bit32 then
local rshift=bit32.rshift
function files.writecardinal2(f,n)
local a=char(n%256)
n=rshift(n,8)
local b=char(n%256)
f:write(b,a)
end
function files.writecardinal4(f,n)
local a=char(n%256)
n=rshift(n,8)
local b=char(n%256)
n=rshift(n,8)
local c=char(n%256)
n=rshift(n,8)
local d=char(n%256)
f:write(d,c,b,a)
end
function files.writecardinal2le(f,n)
local a=char(n%256)
n=rshift(n,8)
local b=char(n%256)
f:write(a,b)
end
function files.writecardinal4le(f,n)
local a=char(n%256)
n=rshift(n,8)
local b=char(n%256)
n=rshift(n,8)
local c=char(n%256)
n=rshift(n,8)
local d=char(n%256)
f:write(a,b,c,d)
end
else
local floor=math.floor
function files.writecardinal2(f,n)
local a=char(n%256)
n=floor(n/256)
local b=char(n%256)
f:write(b,a)
end
function files.writecardinal4(f,n)
local a=char(n%256)
n=floor(n/256)
local b=char(n%256)
n=floor(n/256)
local c=char(n%256)
n=floor(n/256)
local d=char(n%256)
f:write(d,c,b,a)
end
function files.writecardinal2le(f,n)
local a=char(n%256)
n=floor(n/256)
local b=char(n%256)
f:write(a,b)
end
function files.writecardinal4le(f,n)
local a=char(n%256)
n=floor(n/256)
local b=char(n%256)
n=floor(n/256)
local c=char(n%256)
n=floor(n/256)
local d=char(n%256)
f:write(a,b,c,d)
end
end
function files.writestring(f,s)
f:write(char(byte(s,1,#s)))
end
function files.writebyte(f,b)
f:write(char(b))
end
if fio and fio.readcardinal1 then
files.readcardinal1=fio.readcardinal1
files.readcardinal2=fio.readcardinal2
files.readcardinal3=fio.readcardinal3
files.readcardinal4=fio.readcardinal4
files.readcardinal1le=fio.readcardinal1le or files.readcardinal1le
files.readcardinal2le=fio.readcardinal2le or files.readcardinal2le
files.readcardinal3le=fio.readcardinal3le or files.readcardinal3le
files.readcardinal4le=fio.readcardinal4le or files.readcardinal4le
files.readinteger1=fio.readinteger1
files.readinteger2=fio.readinteger2
files.readinteger3=fio.readinteger3
files.readinteger4=fio.readinteger4
files.readinteger1le=fio.readinteger1le or files.readinteger1le
files.readinteger2le=fio.readinteger2le or files.readinteger2le
files.readinteger3le=fio.readinteger3le or files.readinteger3le
files.readinteger4le=fio.readinteger4le or files.readinteger4le
files.readfixed2=fio.readfixed2
files.readfixed4=fio.readfixed4
files.read2dot14=fio.read2dot14
files.setposition=fio.setposition
files.getposition=fio.getposition
files.readbyte=files.readcardinal1
files.readsignedbyte=files.readinteger1
files.readcardinal=files.readcardinal1
files.readinteger=files.readinteger1
local skipposition=fio.skipposition
files.skipposition=skipposition
files.readbytes=fio.readbytes
files.readbytetable=fio.readbytetable
function files.skipshort(f,n)
skipposition(f,2*(n or 1))
end
function files.skiplong(f,n)
skipposition(f,4*(n or 1))
end
end
if fio and fio.writecardinal1 then
files.writecardinal1=fio.writecardinal1
files.writecardinal2=fio.writecardinal2
files.writecardinal3=fio.writecardinal3
files.writecardinal4=fio.writecardinal4
files.writecardinal1le=fio.writecardinal1le
files.writecardinal2le=fio.writecardinal2le
files.writecardinal3le=fio.writecardinal3le
files.writecardinal4le=fio.writecardinal4le
files.writeinteger1=fio.writeinteger1 or fio.writecardinal1
files.writeinteger2=fio.writeinteger2 or fio.writecardinal2
files.writeinteger3=fio.writeinteger3 or fio.writecardinal3
files.writeinteger4=fio.writeinteger4 or fio.writecardinal4
files.writeinteger1le=files.writeinteger1le or fio.writecardinal1le
files.writeinteger2le=files.writeinteger2le or fio.writecardinal2le
files.writeinteger3le=files.writeinteger3le or fio.writecardinal3le
files.writeinteger4le=files.writeinteger4le or fio.writecardinal4le
end
if fio and fio.readcardinaltable then
files.readcardinaltable=fio.readcardinaltable
files.readintegertable=fio.readintegertable
else
local readcardinal1=files.readcardinal1
local readcardinal2=files.readcardinal2
local readcardinal3=files.readcardinal3
local readcardinal4=files.readcardinal4
function files.readcardinaltable(f,n,b)
local t={}
if b==1 then for i=1,n do t[i]=readcardinal1(f) end
elseif b==2 then for i=1,n do t[i]=readcardinal2(f) end
elseif b==3 then for i=1,n do t[i]=readcardinal3(f) end
elseif b==4 then for i=1,n do t[i]=readcardinal4(f) end end
return t
end
local readinteger1=files.readinteger1
local readinteger2=files.readinteger2
local readinteger3=files.readinteger3
local readinteger4=files.readinteger4
function files.readintegertable(f,n,b)
local t={}
if b==1 then for i=1,n do t[i]=readinteger1(f) end
elseif b==2 then for i=1,n do t[i]=readinteger2(f) end
elseif b==3 then for i=1,n do t[i]=readinteger3(f) end
elseif b==4 then for i=1,n do t[i]=readinteger4(f) end end
return t
end
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["util-sac"] = package.loaded["util-sac"] or true
-- original size: 11332, stripped down to: 8420
if not modules then modules={} end modules ['util-sac']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local byte,sub=string.byte,string.sub
local tonumber=tonumber
utilities=utilities or {}
local streams={}
utilities.streams=streams
function streams.open(filename,zerobased)
local f=filename and io.loaddata(filename)
if f then
return { f,1,#f,zerobased or false }
end
end
function streams.openstring(f,zerobased)
if f then
return { f,1,#f,zerobased or false }
end
end
function streams.getstring(f)
if f then
return f[1]
end
end
function streams.close()
end
function streams.size(f)
return f and f[3] or 0
end
function streams.setposition(f,i)
if f[4] then
if i<=0 then
f[2]=1
else
f[2]=i+1
end
else
if i<=1 then
f[2]=1
else
f[2]=i
end
end
end
function streams.getposition(f)
if f[4] then
return f[2]-1
else
return f[2]
end
end
function streams.look(f,n,chars)
local b=f[2]
local e=b+n-1
if chars then
return sub(f[1],b,e)
else
return byte(f[1],b,e)
end
end
function streams.skip(f,n)
f[2]=f[2]+n
end
function streams.readbyte(f)
local i=f[2]
f[2]=i+1
return byte(f[1],i)
end
function streams.readbytes(f,n)
local i=f[2]
local j=i+n
f[2]=j
return byte(f[1],i,j-1)
end
function streams.readbytetable(f,n)
local i=f[2]
local j=i+n
f[2]=j
return { byte(f[1],i,j-1) }
end
function streams.skipbytes(f,n)
f[2]=f[2]+n
end
function streams.readchar(f)
local i=f[2]
f[2]=i+1
return sub(f[1],i,i)
end
function streams.readstring(f,n)
local i=f[2]
local j=i+n
f[2]=j
return sub(f[1],i,j-1)
end
function streams.readinteger1(f)
local i=f[2]
f[2]=i+1
local n=byte(f[1],i)
if n>=0x80 then
return n-0x100
else
return n
end
end
streams.readcardinal1=streams.readbyte
streams.readcardinal=streams.readcardinal1
streams.readinteger=streams.readinteger1
function streams.readcardinal2(f)
local i=f[2]
local j=i+1
f[2]=j+1
local a,b=byte(f[1],i,j)
return 0x100*a+b
end
function streams.readcardinal2le(f)
local i=f[2]
local j=i+1
f[2]=j+1
local b,a=byte(f[1],i,j)
return 0x100*a+b
end
function streams.readinteger2(f)
local i=f[2]
local j=i+1
f[2]=j+1
local a,b=byte(f[1],i,j)
if a>=0x80 then
return 0x100*a+b-0x10000
else
return 0x100*a+b
end
end
function streams.readinteger2le(f)
local i=f[2]
local j=i+1
f[2]=j+1
local b,a=byte(f[1],i,j)
if a>=0x80 then
return 0x100*a+b-0x10000
else
return 0x100*a+b
end
end
function streams.readcardinal3(f)
local i=f[2]
local j=i+2
f[2]=j+1
local a,b,c=byte(f[1],i,j)
return 0x10000*a+0x100*b+c
end
function streams.readcardinal3le(f)
local i=f[2]
local j=i+2
f[2]=j+1
local c,b,a=byte(f[1],i,j)
return 0x10000*a+0x100*b+c
end
function streams.readinteger3(f)
local i=f[2]
local j=i+3
f[2]=j+1
local a,b,c=byte(f[1],i,j)
if a>=0x80 then
return 0x10000*a+0x100*b+c-0x1000000
else
return 0x10000*a+0x100*b+c
end
end
function streams.readinteger3le(f)
local i=f[2]
local j=i+3
f[2]=j+1
local c,b,a=byte(f[1],i,j)
if a>=0x80 then
return 0x10000*a+0x100*b+c-0x1000000
else
return 0x10000*a+0x100*b+c
end
end
function streams.readcardinal4(f)
local i=f[2]
local j=i+3
f[2]=j+1
local a,b,c,d=byte(f[1],i,j)
return 0x1000000*a+0x10000*b+0x100*c+d
end
function streams.readcardinal4le(f)
local i=f[2]
local j=i+3
f[2]=j+1
local d,c,b,a=byte(f[1],i,j)
return 0x1000000*a+0x10000*b+0x100*c+d
end
function streams.readinteger4(f)
local i=f[2]
local j=i+3
f[2]=j+1
local a,b,c,d=byte(f[1],i,j)
if a>=0x80 then
return 0x1000000*a+0x10000*b+0x100*c+d-0x100000000
else
return 0x1000000*a+0x10000*b+0x100*c+d
end
end
function streams.readinteger4le(f)
local i=f[2]
local j=i+3
f[2]=j+1
local d,c,b,a=byte(f[1],i,j)
if a>=0x80 then
return 0x1000000*a+0x10000*b+0x100*c+d-0x100000000
else
return 0x1000000*a+0x10000*b+0x100*c+d
end
end
function streams.readfixed2(f)
local i=f[2]
local j=i+1
f[2]=j+1
local a,b=byte(f[1],i,j)
if a>=0x80 then
tonumber((a-0x100).."."..b)
else
tonumber((a ).."."..b)
end
end
function streams.readfixed4(f)
local i=f[2]
local j=i+3
f[2]=j+1
local a,b,c,d=byte(f[1],i,j)
if a>=0x80 then
tonumber((0x100*a+b-0x10000).."."..(0x100*c+d))
else
tonumber((0x100*a+b ).."."..(0x100*c+d))
end
end
if bit32 then
local extract=bit32.extract
local band=bit32.band
function streams.read2dot14(f)
local i=f[2]
local j=i+1
f[2]=j+1
local a,b=byte(f[1],i,j)
if a>=0x80 then
local n=-(0x100*a+b)
return-(extract(n,14,2)+(band(n,0x3FFF)/16384.0))
else
local n=0x100*a+b
return (extract(n,14,2)+(band(n,0x3FFF)/16384.0))
end
end
end
function streams.skipshort(f,n)
f[2]=f[2]+2*(n or 1)
end
function streams.skiplong(f,n)
f[2]=f[2]+4*(n or 1)
end
if sio and sio.readcardinal2 then
local readcardinal1=sio.readcardinal1
local readcardinal2=sio.readcardinal2
local readcardinal3=sio.readcardinal3
local readcardinal4=sio.readcardinal4
local readinteger1=sio.readinteger1
local readinteger2=sio.readinteger2
local readinteger3=sio.readinteger3
local readinteger4=sio.readinteger4
local readfixed2=sio.readfixed2
local readfixed4=sio.readfixed4
local read2dot14=sio.read2dot14
local readbytes=sio.readbytes
local readbytetable=sio.readbytetable
function streams.readcardinal1(f)
local i=f[2]
f[2]=i+1
return readcardinal1(f[1],i)
end
function streams.readcardinal2(f)
local i=f[2]
f[2]=i+2
return readcardinal2(f[1],i)
end
function streams.readcardinal3(f)
local i=f[2]
f[2]=i+3
return readcardinal3(f[1],i)
end
function streams.readcardinal4(f)
local i=f[2]
f[2]=i+4
return readcardinal4(f[1],i)
end
function streams.readinteger1(f)
local i=f[2]
f[2]=i+1
return readinteger1(f[1],i)
end
function streams.readinteger2(f)
local i=f[2]
f[2]=i+2
return readinteger2(f[1],i)
end
function streams.readinteger3(f)
local i=f[2]
f[2]=i+3
return readinteger3(f[1],i)
end
function streams.readinteger4(f)
local i=f[2]
f[2]=i+4
return readinteger4(f[1],i)
end
function streams.read2dot4(f)
local i=f[2]
f[2]=i+2
return read2dot4(f[1],i)
end
function streams.readbytes(f,n)
local i=f[2]
local s=f[3]
local p=i+n
if p>s then
f[2]=s+1
else
f[2]=p
end
return readbytes(f[1],i,n)
end
function streams.readbytetable(f,n)
local i=f[2]
local s=f[3]
local p=i+n
if p>s then
f[2]=s+1
else
f[2]=p
end
return readbytetable(f[1],i,n)
end
streams.readbyte=streams.readcardinal1
streams.readsignedbyte=streams.readinteger1
streams.readcardinal=streams.readcardinal1
streams.readinteger=streams.readinteger1
end
if sio and sio.readcardinaltable then
local readcardinaltable=sio.readcardinaltable
local readintegertable=sio.readintegertable
function utilities.streams.readcardinaltable(f,n,b)
local i=f[2]
local s=f[3]
local p=i+n*b
if p>s then
f[2]=s+1
else
f[2]=p
end
return readcardinaltable(f[1],i,n,b)
end
function utilities.streams.readintegertable(f,n,b)
local i=f[2]
local s=f[3]
local p=i+n*b
if p>s then
f[2]=s+1
else
f[2]=p
end
return readintegertable(f[1],i,n,b)
end
else
local readcardinal1=streams.readcardinal1
local readcardinal2=streams.readcardinal2
local readcardinal3=streams.readcardinal3
local readcardinal4=streams.readcardinal4
function streams.readcardinaltable(f,n,b)
local i=f[2]
local s=f[3]
local p=i+n*b
if p>s then
f[2]=s+1
else
f[2]=p
end
local t={}
if b==1 then for i=1,n do t[i]=readcardinal1(f[1],i) end
elseif b==2 then for i=1,n do t[i]=readcardinal2(f[1],i) end
elseif b==3 then for i=1,n do t[i]=readcardinal3(f[1],i) end
elseif b==4 then for i=1,n do t[i]=readcardinal4(f[1],i) end end
return t
end
local readinteger1=streams.readinteger1
local readinteger2=streams.readinteger2
local readinteger3=streams.readinteger3
local readinteger4=streams.readinteger4
function streams.readintegertable(f,n,b)
local i=f[2]
local s=f[3]
local p=i+n*b
if p>s then
f[2]=s+1
else
f[2]=p
end
local t={}
if b==1 then for i=1,n do t[i]=readinteger1(f[1],i) end
elseif b==2 then for i=1,n do t[i]=readinteger2(f[1],i) end
elseif b==3 then for i=1,n do t[i]=readinteger3(f[1],i) end
elseif b==4 then for i=1,n do t[i]=readinteger4(f[1],i) end end
return t
end
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["util-sto"] = package.loaded["util-sto"] or true
-- original size: 6661, stripped down to: 3074
if not modules then modules={} end modules ['util-sto']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local setmetatable,getmetatable,rawset,type=setmetatable,getmetatable,rawset,type
utilities=utilities or {}
utilities.storage=utilities.storage or {}
local storage=utilities.storage
function storage.mark(t)
if not t then
print("\nfatal error: storage cannot be marked\n")
os.exit()
return
end
local m=getmetatable(t)
if not m then
m={}
setmetatable(t,m)
end
m.__storage__=true
return t
end
function storage.allocate(t)
t=t or {}
local m=getmetatable(t)
if not m then
m={}
setmetatable(t,m)
end
m.__storage__=true
return t
end
function storage.marked(t)
local m=getmetatable(t)
return m and m.__storage__
end
function storage.checked(t)
if not t then
report("\nfatal error: storage has not been allocated\n")
os.exit()
return
end
return t
end
function storage.setinitializer(data,initialize)
local m=getmetatable(data) or {}
m.__index=function(data,k)
m.__index=nil
initialize()
return data[k]
end
setmetatable(data,m)
end
local keyisvalue={ __index=function(t,k)
t[k]=k
return k
end }
function storage.sparse(t)
t=t or {}
setmetatable(t,keyisvalue)
return t
end
local function f_empty () return "" end
local function f_self (t,k) t[k]=k return k end
local function f_table (t,k) local v={} t[k]=v return v end
local function f_number(t,k) t[k]=0 return 0 end
local function f_ignore() end
local f_index={
["empty"]=f_empty,
["self"]=f_self,
["table"]=f_table,
["number"]=f_number,
}
function table.setmetatableindex(t,f)
if type(t)~="table" then
f,t=t,{}
end
local m=getmetatable(t)
local i=f_index[f] or f
if m then
m.__index=i
else
setmetatable(t,{ __index=i })
end
return t
end
local f_index={
["ignore"]=f_ignore,
}
function table.setmetatablenewindex(t,f)
if type(t)~="table" then
f,t=t,{}
end
local m=getmetatable(t)
local i=f_index[f] or f
if m then
m.__newindex=i
else
setmetatable(t,{ __newindex=i })
end
return t
end
function table.setmetatablecall(t,f)
if type(t)~="table" then
f,t=t,{}
end
local m=getmetatable(t)
if m then
m.__call=f
else
setmetatable(t,{ __call=f })
end
return t
end
function table.setmetatableindices(t,f,n,c)
if type(t)~="table" then
f,t=t,{}
end
local m=getmetatable(t)
local i=f_index[f] or f
if m then
m.__index=i
m.__newindex=n
m.__call=c
else
setmetatable(t,{
__index=i,
__newindex=n,
__call=c,
})
end
return t
end
function table.setmetatablekey(t,key,value)
local m=getmetatable(t)
if not m then
m={}
setmetatable(t,m)
end
m[key]=value
return t
end
function table.getmetatablekey(t,key,value)
local m=getmetatable(t)
return m and m[key]
end
function table.makeweak(t)
if not t then
t={}
end
local m=getmetatable(t)
if m then
m.__mode="v"
else
setmetatable(t,{ __mode="v" })
end
return t
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["util-prs"] = package.loaded["util-prs"] or true
-- original size: 24132, stripped down to: 16210
if not modules then modules={} end modules ['util-prs']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local lpeg,table,string=lpeg,table,string
local 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
local lpegmatch,lpegpatterns=lpeg.match,lpeg.patterns
local concat,gmatch,find=table.concat,string.gmatch,string.find
local tostring,type,next,rawset=tostring,type,next,rawset
local mod,div=math.mod,math.div
utilities=utilities or {}
local parsers=utilities.parsers or {}
utilities.parsers=parsers
local patterns=parsers.patterns or {}
parsers.patterns=patterns
local setmetatableindex=table.setmetatableindex
local sortedhash=table.sortedhash
local sortedkeys=table.sortedkeys
local tohash=table.tohash
local hashes={}
parsers.hashes=hashes
local digit=R("09")
local space=P(' ')
local equal=P("=")
local colon=P(":")
local comma=P(",")
local lbrace=P("{")
local rbrace=P("}")
local lparent=P("(")
local rparent=P(")")
local lbracket=P("[")
local rbracket=P("]")
local period=S(".")
local punctuation=S(".,:;")
local spacer=lpegpatterns.spacer
local whitespace=lpegpatterns.whitespace
local newline=lpegpatterns.newline
local anything=lpegpatterns.anything
local endofstring=lpegpatterns.endofstring
local nobrace=1-(lbrace+rbrace )
local noparent=1-(lparent+rparent)
local nobracket=1-(lbracket+rbracket)
local escape,left,right=P("\\"),P('{'),P('}')
lpegpatterns.balanced=P {
[1]=((escape*(left+right))+(1-(left+right))+V(2))^0,
[2]=left*V(1)*right
}
local nestedbraces=P { lbrace*(nobrace+V(1))^0*rbrace }
local nestedparents=P { lparent*(noparent+V(1))^0*rparent }
local nestedbrackets=P { lbracket*(nobracket+V(1))^0*rbracket }
local spaces=space^0
local argument=Cs((lbrace/"")*((nobrace+nestedbraces)^0)*(rbrace/""))
local content=(1-endofstring)^0
lpegpatterns.nestedbraces=nestedbraces
lpegpatterns.nestedparents=nestedparents
lpegpatterns.nested=nestedbraces
lpegpatterns.argument=argument
lpegpatterns.content=content
local value=lbrace*C((nobrace+nestedbraces)^0)*rbrace+C((nestedbraces+(1-comma))^0)
local key=C((1-equal-comma)^1)
local pattern_a=(space+comma)^0*(key*equal*value+key*C(""))
local pattern_c=(space+comma)^0*(key*equal*value)
local pattern_d=(space+comma)^0*(key*(equal+colon)*value+key*C(""))
local key=C((1-space-equal-comma)^1)
local pattern_b=spaces*comma^0*spaces*(key*((spaces*equal*spaces*value)+C("")))
local hash={}
local function set(key,value)
hash[key]=value
end
local pattern_a_s=(pattern_a/set)^1
local pattern_b_s=(pattern_b/set)^1
local pattern_c_s=(pattern_c/set)^1
local pattern_d_s=(pattern_d/set)^1
patterns.settings_to_hash_a=pattern_a_s
patterns.settings_to_hash_b=pattern_b_s
patterns.settings_to_hash_c=pattern_c_s
patterns.settings_to_hash_d=pattern_d_s
function parsers.make_settings_to_hash_pattern(set,how)
if how=="strict" then
return (pattern_c/set)^1
elseif how=="tolerant" then
return (pattern_b/set)^1
else
return (pattern_a/set)^1
end
end
function parsers.settings_to_hash(str,existing)
if not str or str=="" then
return {}
elseif type(str)=="table" then
if existing then
for k,v in next,str do
existing[k]=v
end
return exiting
else
return str
end
else
hash=existing or {}
lpegmatch(pattern_a_s,str)
return hash
end
end
function parsers.settings_to_hash_colon_too(str)
if not str or str=="" then
return {}
elseif type(str)=="table" then
return str
else
hash={}
lpegmatch(pattern_d_s,str)
return hash
end
end
function parsers.settings_to_hash_tolerant(str,existing)
if not str or str=="" then
return {}
elseif type(str)=="table" then
if existing then
for k,v in next,str do
existing[k]=v
end
return exiting
else
return str
end
else
hash=existing or {}
lpegmatch(pattern_b_s,str)
return hash
end
end
function parsers.settings_to_hash_strict(str,existing)
if not str or str=="" then
return nil
elseif type(str)=="table" then
if existing then
for k,v in next,str do
existing[k]=v
end
return exiting
else
return str
end
elseif str and str~="" then
hash=existing or {}
lpegmatch(pattern_c_s,str)
return next(hash) and hash
end
end
local separator=comma*space^0
local value=lbrace*C((nobrace+nestedbraces)^0)*rbrace+C((nestedbraces+(1-comma))^0)
local pattern=spaces*Ct(value*(separator*value)^0)
patterns.settings_to_array=pattern
function parsers.settings_to_array(str,strict)
if not str or str=="" then
return {}
elseif type(str)=="table" then
return str
elseif strict then
if find(str,"{",1,true) then
return lpegmatch(pattern,str)
else
return { str }
end
elseif find(str,",",1,true) then
return lpegmatch(pattern,str)
else
return { str }
end
end
function parsers.settings_to_numbers(str)
if not str or str=="" then
return {}
end
if type(str)=="table" then
elseif find(str,",",1,true) then
str=lpegmatch(pattern,str)
else
return { tonumber(str) }
end
for i=1,#str do
str[i]=tonumber(str[i])
end
return str
end
local value=lbrace*C((nobrace+nestedbraces)^0)*rbrace+C((nestedbraces+nestedbrackets+nestedparents+(1-comma))^0)
local pattern=spaces*Ct(value*(separator*value)^0)
function parsers.settings_to_array_obey_fences(str)
return lpegmatch(pattern,str)
end
local cache_a={}
local cache_b={}
function parsers.groupedsplitat(symbol,withaction)
if not symbol then
symbol=","
end
local pattern=(withaction and cache_b or cache_a)[symbol]
if not pattern then
local symbols=S(symbol)
local separator=space^0*symbols*space^0
local value=lbrace*C((nobrace+nestedbraces)^0)*rbrace+C((nestedbraces+(1-(space^0*(symbols+P(-1)))))^0)
if withaction then
local withvalue=Carg(1)*value/function(f,s) return f(s) end
pattern=spaces*withvalue*(separator*withvalue)^0
cache_b[symbol]=pattern
else
pattern=spaces*Ct(value*(separator*value)^0)
cache_a[symbol]=pattern
end
end
return pattern
end
local pattern_a=parsers.groupedsplitat(",",false)
local pattern_b=parsers.groupedsplitat(",",true)
function parsers.stripped_settings_to_array(str)
if not str or str=="" then
return {}
else
return lpegmatch(pattern_a,str)
end
end
function parsers.process_stripped_settings(str,action)
if not str or str=="" then
return {}
else
return lpegmatch(pattern_b,str,1,action)
end
end
local function set(t,v)
t[#t+1]=v
end
local value=P(Carg(1)*value)/set
local pattern=value*(separator*value)^0*Carg(1)
function parsers.add_settings_to_array(t,str)
return lpegmatch(pattern,str,nil,t)
end
function parsers.hash_to_string(h,separator,yes,no,strict,omit)
if h then
local t={}
local tn=0
local s=sortedkeys(h)
omit=omit and tohash(omit)
for i=1,#s do
local key=s[i]
if not omit or not omit[key] then
local value=h[key]
if type(value)=="boolean" then
if yes and no then
if value then
tn=tn+1
t[tn]=key..'='..yes
elseif not strict then
tn=tn+1
t[tn]=key..'='..no
end
elseif value or not strict then
tn=tn+1
t[tn]=key..'='..tostring(value)
end
else
tn=tn+1
t[tn]=key..'='..value
end
end
end
return concat(t,separator or ",")
else
return ""
end
end
function parsers.array_to_string(a,separator)
if a then
return concat(a,separator or ",")
else
return ""
end
end
local pattern=Cf(Ct("")*Cg(C((1-S(", "))^1)*S(", ")^0*Cc(true))^1,rawset)
function parsers.settings_to_set(str)
return str and lpegmatch(pattern,str) or {}
end
hashes.settings_to_set=table.setmetatableindex(function(t,k)
local v=k and lpegmatch(pattern,k) or {}
t[k]=v
return v
end)
getmetatable(hashes.settings_to_set).__mode="kv"
function parsers.simple_hash_to_string(h,separator)
local t={}
local tn=0
for k,v in sortedhash(h) do
if v then
tn=tn+1
t[tn]=k
end
end
return concat(t,separator or ",")
end
local str=Cs(lpegpatterns.unquoted)+C((1-whitespace-equal)^1)
local setting=Cf(Carg(1)*(whitespace^0*Cg(str*whitespace^0*(equal*whitespace^0*str+Cc(""))))^1,rawset)
local splitter=setting^1
function parsers.options_to_hash(str,target)
return str and lpegmatch(splitter,str,1,target or {}) or {}
end
local splitter=lpeg.tsplitat(" ")
function parsers.options_to_array(str)
return str and lpegmatch(splitter,str) or {}
end
local value=P(lbrace*C((nobrace+nestedbraces)^0)*rbrace)+C(digit^1*lparent*(noparent+nestedparents)^1*rparent)+C((nestedbraces+(1-comma))^1)
local pattern_a=spaces*Ct(value*(separator*value)^0)
local function repeater(n,str)
if not n then
return str
else
local s=lpegmatch(pattern_a,str)
if n==1 then
return unpack(s)
else
local t={}
local tn=0
for i=1,n do
for j=1,#s do
tn=tn+1
t[tn]=s[j]
end
end
return unpack(t)
end
end
end
local value=P(lbrace*C((nobrace+nestedbraces)^0)*rbrace)+(C(digit^1)/tonumber*lparent*Cs((noparent+nestedparents)^1)*rparent)/repeater+C((nestedbraces+(1-comma))^1)
local pattern_b=spaces*Ct(value*(separator*value)^0)
function parsers.settings_to_array_with_repeat(str,expand)
if expand then
return lpegmatch(pattern_b,str) or {}
else
return lpegmatch(pattern_a,str) or {}
end
end
local value=lbrace*C((nobrace+nestedbraces)^0)*rbrace
local pattern=Ct((space+value)^0)
function parsers.arguments_to_table(str)
return lpegmatch(pattern,str)
end
function parsers.getparameters(self,class,parentclass,settings)
local sc=self[class]
if not sc then
sc={}
self[class]=sc
if parentclass then
local sp=self[parentclass]
if not sp then
sp={}
self[parentclass]=sp
end
setmetatableindex(sc,sp)
end
end
parsers.settings_to_hash(settings,sc)
end
function parsers.listitem(str)
return gmatch(str,"[^, ]+")
end
local pattern=Cs { "start",
start=V("one")+V("two")+V("three"),
rest=(Cc(",")*V("thousand"))^0*(P(".")+endofstring)*anything^0,
thousand=digit*digit*digit,
one=digit*V("rest"),
two=digit*digit*V("rest"),
three=V("thousand")*V("rest"),
}
lpegpatterns.splitthousands=pattern
function parsers.splitthousands(str)
return lpegmatch(pattern,str) or str
end
local optionalwhitespace=whitespace^0
lpegpatterns.words=Ct((Cs((1-punctuation-whitespace)^1)+anything)^1)
lpegpatterns.sentences=Ct((optionalwhitespace*Cs((1-period)^0*period))^1)
lpegpatterns.paragraphs=Ct((optionalwhitespace*Cs((whitespace^1*endofstring/""+1-(spacer^0*newline*newline))^1))^1)
local dquote=P('"')
local equal=P('=')
local escape=P('\\')
local separator=S(' ,')
local key=C((1-equal)^1)
local value=dquote*C((1-dquote-escape*dquote)^0)*dquote
local pattern=Cf(Ct("")*(Cg(key*equal*value)*separator^0)^1,rawset)^0*P(-1)
function parsers.keq_to_hash(str)
if str and str~="" then
return lpegmatch(pattern,str)
else
return {}
end
end
local defaultspecification={ separator=",",quote='"' }
function parsers.csvsplitter(specification)
specification=specification and setmetatableindex(specification,defaultspecification) or defaultspecification
local separator=specification.separator
local quotechar=specification.quote
local separator=S(separator~="" and separator or ",")
local whatever=C((1-separator-newline)^0)
if quotechar and quotechar~="" then
local quotedata=nil
for chr in gmatch(quotechar,".") do
local quotechar=P(chr)
local quoteword=quotechar*C((1-quotechar)^0)*quotechar
if quotedata then
quotedata=quotedata+quoteword
else
quotedata=quoteword
end
end
whatever=quotedata+whatever
end
local parser=Ct((Ct(whatever*(separator*whatever)^0)*S("\n\r")^1)^0 )
return function(data)
return lpegmatch(parser,data)
end
end
function parsers.rfc4180splitter(specification)
specification=specification and setmetatableindex(specification,defaultspecification) or defaultspecification
local separator=specification.separator
local quotechar=P(specification.quote)
local dquotechar=quotechar*quotechar
/specification.quote
local separator=S(separator~="" and separator or ",")
local escaped=quotechar*Cs((dquotechar+(1-quotechar))^0)*quotechar
local non_escaped=C((1-quotechar-newline-separator)^1)
local field=escaped+non_escaped+Cc("")
local record=Ct(field*(separator*field)^1)
local headerline=record*Cp()
local morerecords=(newline^(specification.strict and -1 or 1)*record)^0
local headeryes=Ct(morerecords)
local headernop=Ct(record*morerecords)
return function(data,getheader)
if getheader then
local header,position=lpegmatch(headerline,data)
local data=lpegmatch(headeryes,data,position)
return data,header
else
return lpegmatch(headernop,data)
end
end
end
local function ranger(first,last,n,action)
if not first then
elseif last==true then
for i=first,n or first do
action(i)
end
elseif last then
for i=first,last do
action(i)
end
else
action(first)
end
end
local cardinal=lpegpatterns.cardinal/tonumber
local spacers=lpegpatterns.spacer^0
local endofstring=lpegpatterns.endofstring
local stepper=spacers*(cardinal*(spacers*S(":-")*spacers*(cardinal+Cc(true) )+Cc(false) )*Carg(1)*Carg(2)/ranger*S(", ")^0 )^1
local stepper=spacers*(cardinal*(spacers*S(":-")*spacers*(cardinal+(P("*")+endofstring)*Cc(true) )+Cc(false) )*Carg(1)*Carg(2)/ranger*S(", ")^0 )^1*endofstring
function parsers.stepper(str,n,action)
if type(n)=="function" then
lpegmatch(stepper,str,1,false,n or print)
else
lpegmatch(stepper,str,1,n,action or print)
end
end
local pattern_math=Cs((P("%")/"\\percent "+P("^")*Cc("{")*lpegpatterns.integer*Cc("}")+anything)^0)
local pattern_text=Cs((P("%")/"\\percent "+(P("^")/"\\high")*Cc("{")*lpegpatterns.integer*Cc("}")+anything)^0)
patterns.unittotex=pattern
function parsers.unittotex(str,textmode)
return lpegmatch(textmode and pattern_text or pattern_math,str)
end
local pattern=Cs((P("^")/"<sup>"*lpegpatterns.integer*Cc("</sup>")+anything)^0)
function parsers.unittoxml(str)
return lpegmatch(pattern,str)
end
local cache={}
local spaces=lpegpatterns.space^0
local dummy=function() end
setmetatableindex(cache,function(t,k)
local separator=P(k)
local value=(1-separator)^0
local pattern=spaces*C(value)*separator^0*Cp()
t[k]=pattern
return pattern
end)
local commalistiterator=cache[","]
function parsers.iterator(str,separator)
local n=#str
if n==0 then
return dummy
else
local pattern=separator and cache[separator] or commalistiterator
local p=1
return function()
if p<=n then
local s,e=lpegmatch(pattern,str,p)
if e then
p=e
return s
end
end
end
end
end
local function initialize(t,name)
local source=t[name]
if source then
local result={}
for k,v in next,t[name] do
result[k]=v
end
return result
else
return {}
end
end
local function fetch(t,name)
return t[name] or {}
end
local function process(result,more)
for k,v in next,more do
result[k]=v
end
return result
end
local name=C((1-S(", "))^1)
local parser=(Carg(1)*name/initialize)*(S(", ")^1*(Carg(1)*name/fetch))^0
local merge=Cf(parser,process)
function parsers.mergehashes(hash,list)
return lpegmatch(merge,list,1,hash)
end
function parsers.runtime(time)
if not time then
time=os.runtime()
end
local days=div(time,24*60*60)
time=mod(time,24*60*60)
local hours=div(time,60*60)
time=mod(time,60*60)
local minutes=div(time,60)
local seconds=mod(time,60)
return days,hours,minutes,seconds
end
local spacing=whitespace^0
local apply=P("->")
local method=C((1-apply)^1)
local token=lbrace*C((1-rbrace)^1)*rbrace+C(anything^1)
local pattern=spacing*(method*spacing*apply+Carg(1))*spacing*token
function parsers.splitmethod(str,default)
if str then
return lpegmatch(pattern,str,1,default or false)
else
return default or false,""
end
end
local p_year=lpegpatterns.digit^4/tonumber
local pattern=Cf(Ct("")*(
(Cg(Cc("year")*p_year)*S("-/")*Cg(Cc("month")*cardinal)*S("-/")*Cg(Cc("day")*cardinal)
)+(Cg(Cc("day")*cardinal)*S("-/")*Cg(Cc("month")*cardinal)*S("-/")*Cg(Cc("year")*p_year)
)
)*P(" ")*Cg(Cc("hour")*cardinal)*P(":")*Cg(Cc("min")*cardinal)*(P(":")*Cg(Cc("sec")*cardinal))^-1
,rawset)
lpegpatterns.splittime=pattern
function parsers.totime(str)
return lpegmatch(pattern,str)
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["util-fmt"] = package.loaded["util-fmt"] or true
-- original size: 2541, stripped down to: 1624
if not modules then modules={} end modules ['util-fmt']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
utilities=utilities or {}
utilities.formatters=utilities.formatters or {}
local formatters=utilities.formatters
local concat,format=table.concat,string.format
local tostring,type=tostring,type
local strip=string.strip
local lpegmatch=lpeg.match
local stripper=lpeg.patterns.stripzeros
function formatters.stripzeros(str)
return lpegmatch(stripper,str)
end
function formatters.formatcolumns(result,between)
if result and #result>0 then
between=between or " "
local widths,numbers={},{}
local first=result[1]
local n=#first
for i=1,n do
widths[i]=0
end
for i=1,#result do
local r=result[i]
for j=1,n do
local rj=r[j]
local tj=type(rj)
if tj=="number" then
numbers[j]=true
rj=tostring(rj)
elseif tj~="string" then
rj=tostring(rj)
r[j]=rj
end
local w=#rj
if w>widths[j] then
widths[j]=w
end
end
end
for i=1,n do
local w=widths[i]
if numbers[i] then
if w>80 then
widths[i]="%s"..between
else
widths[i]="%0"..w.."i"..between
end
else
if w>80 then
widths[i]="%s"..between
elseif w>0 then
widths[i]="%-"..w.."s"..between
else
widths[i]="%s"
end
end
end
local template=strip(concat(widths))
for i=1,#result do
local str=format(template,unpack(result[i]))
result[i]=strip(str)
end
end
return result
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["util-soc-imp-reset"] = package.loaded["util-soc-imp-reset"] or true
-- original size: 374, stripped down to: 282
local loaded=package.loaded
loaded["socket"]=nil
loaded["copas"]=nil
loaded["ltn12"]=nil
loaded["mbox"]=nil
loaded["mime"]=nil
loaded["socket.url"]=nil
loaded["socket.headers"]=nil
loaded["socket.tp"]=nil
loaded["socket.http"]=nil
loaded["socket.ftp"]=nil
loaded["socket.smtp"]=nil
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["util-soc-imp-socket"] = package.loaded["util-soc-imp-socket"] or true
-- original size: 4905, stripped down to: 3562
local type,tostring,setmetatable=type,tostring,setmetatable
local min=math.min
local format=string.format
local socket=socket or package.loaded.socket or require("socket.core")
local connect=socket.connect
local tcp4=socket.tcp4
local tcp6=socket.tcp6
local getaddrinfo=socket.dns.getaddrinfo
local defaulthost="0.0.0.0"
local function report(fmt,first,...)
if logs then
report=logs and logs.reporter("socket")
report(fmt,first,...)
elseif fmt then
fmt="socket: "..fmt
if first then
print(format(fmt,first,...))
else
print(fmt)
end
end
end
socket.report=report
function socket.connect4(address,port,laddress,lport)
return connect(address,port,laddress,lport,"inet")
end
function socket.connect6(address,port,laddress,lport)
return connect(address,port,laddress,lport,"inet6")
end
function socket.bind(host,port,backlog)
if host=="*" or host=="" then
host=defaulthost
end
local addrinfo,err=getaddrinfo(host)
if not addrinfo then
return nil,err
end
for i=1,#addrinfo do
local alt=addrinfo[i]
local sock,err=(alt.family=="inet" and tcp4 or tcp6)()
if not sock then
return nil,err or "unknown error"
end
sock:setoption("reuseaddr",true)
local res,err=sock:bind(alt.addr,port)
if res then
res,err=sock:listen(backlog)
if res then
return sock
else
sock:close()
end
else
sock:close()
end
end
return nil,"invalid address"
end
socket.try=socket.newtry()
function socket.choose(list)
return function(name,opt1,opt2)
if type(name)~="string" then
name,opt1,opt2="default",name,opt1
end
local f=list[name or "nil"]
if f then
return f(opt1,opt2)
else
report("error: unknown key '%s'",tostring(name))
end
end
end
local sourcet={}
local sinkt={}
socket.sourcet=sourcet
socket.sinkt=sinkt
socket.BLOCKSIZE=2048
sinkt["close-when-done"]=function(sock)
return setmetatable (
{
getfd=function() return sock:getfd() end,
dirty=function() return sock:dirty() end,
},
{
__call=function(self,chunk,err)
if chunk then
return sock:send(chunk)
else
sock:close()
return 1
end
end
}
)
end
sinkt["keep-open"]=function(sock)
return setmetatable (
{
getfd=function() return sock:getfd() end,
dirty=function() return sock:dirty() end,
},{
__call=function(self,chunk,err)
if chunk then
return sock:send(chunk)
else
return 1
end
end
}
)
end
sinkt["default"]=sinkt["keep-open"]
socket.sink=socket.choose(sinkt)
sourcet["by-length"]=function(sock,length)
local blocksize=socket.BLOCKSIZE
return setmetatable (
{
getfd=function() return sock:getfd() end,
dirty=function() return sock:dirty() end,
},
{
__call=function()
if length<=0 then
return nil
end
local chunk,err=sock:receive(min(blocksize,length))
if err then
return nil,err
end
length=length-#chunk
return chunk
end
}
)
end
sourcet["until-closed"]=function(sock)
local blocksize=socket.BLOCKSIZE
local done=false
return setmetatable (
{
getfd=function() return sock:getfd() end,
dirty=function() return sock:dirty() end,
},{
__call=function()
if done then
return nil
end
local chunk,status,partial=sock:receive(blocksize)
if not status then
return chunk
elseif status=="closed" then
sock:close()
done=true
return partial
else
return nil,status
end
end
}
)
end
sourcet["default"]=sourcet["until-closed"]
socket.source=socket.choose(sourcet)
_G.socket=socket
package.loaded["socket"]=socket
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["util-soc-imp-copas"] = package.loaded["util-soc-imp-copas"] or true
-- original size: 25844, stripped down to: 14821
local socket=socket or require("socket")
local ssl=ssl or nil
local WATCH_DOG_TIMEOUT=120
local UDP_DATAGRAM_MAX=8192
local type,next,pcall,getmetatable,tostring=type,next,pcall,getmetatable,tostring
local min,max,random=math.min,math.max,math.random
local find=string.find
local insert,remove=table.insert,table.remove
local gettime=socket.gettime
local selectsocket=socket.select
local createcoroutine=coroutine.create
local resumecoroutine=coroutine.resume
local yieldcoroutine=coroutine.yield
local runningcoroutine=coroutine.running
local function report(fmt,first,...)
if logs then
report=logs and logs.reporter("copas")
report(fmt,first,...)
elseif fmt then
fmt="copas: "..fmt
if first then
print(format(fmt,first,...))
else
print(fmt)
end
end
end
local copas={
_COPYRIGHT="Copyright (C) 2005-2016 Kepler Project",
_DESCRIPTION="Coroutine Oriented Portable Asynchronous Services",
_VERSION="Copas 2.0.1",
autoclose=true,
running=false,
report=report,
}
local function statushandler(status,...)
if status then
return...
end
local err=(...)
if type(err)=="table" then
err=err[1]
end
report("error: %s",tostring(err))
return nil,err
end
function socket.protect(func)
return function(...)
return statushandler(pcall(func,...))
end
end
function socket.newtry(finalizer)
return function (...)
local status=(...)
if not status then
local detail=select(2,...)
pcall(finalizer,detail)
report("error: %s",tostring(detail))
return
end
return...
end
end
local function newset()
local reverse={}
local set={}
local queue={}
setmetatable(set,{
__index={
insert=function(set,value)
if not reverse[value] then
local n=#set+1
set[n]=value
reverse[value]=n
end
end,
remove=function(set,value)
local index=reverse[value]
if index then
reverse[value]=nil
local n=#set
local top=set[n]
set[n]=nil
if top~=value then
reverse[top]=index
set[index]=top
end
end
end,
push=function (set,key,itm)
local entry=queue[key]
if entry==nil then
queue[key]={ itm }
else
entry[#entry+1]=itm
end
end,
pop=function (set,key)
local top=queue[key]
if top~=nil then
local ret=remove(top,1)
if top[1]==nil then
queue[key]=nil
end
return ret
end
end
}
} )
return set
end
local _sleeping={
times={},
cos={},
lethargy={},
insert=function()
end,
remove=function()
end,
push=function(self,sleeptime,co)
if not co then
return
end
if sleeptime<0 then
self.lethargy[co]=true
return
else
sleeptime=gettime()+sleeptime
end
local t=self.times
local c=self.cos
local i=1
local n=#t
while i<=n and t[i]<=sleeptime do
i=i+1
end
insert(t,i,sleeptime)
insert(c,i,co)
end,
getnext=
function(self)
local t=self.times
local delay=t[1] and t[1]-gettime() or nil
return delay and max(delay,0) or nil
end,
pop=
function(self,time)
local t=self.times
local c=self.cos
if #t==0 or time<t[1] then
return
end
local co=c[1]
remove(t,1)
remove(c,1)
return co
end,
wakeup=function(self,co)
local let=self.lethargy
if let[co] then
self:push(0,co)
let[co]=nil
else
local c=self.cos
local t=self.times
for i=1,#c do
if c[i]==co then
remove(c,i)
remove(t,i)
self:push(0,co)
return
end
end
end
end
}
local _servers=newset()
local _reading=newset()
local _writing=newset()
local _reading_log={}
local _writing_log={}
local _is_timeout={
timeout=true,
wantread=true,
wantwrite=true,
}
local function isTCP(socket)
return not find(tostring(socket),"^udp")
end
local function copasreceive(client,pattern,part)
if not pattern or pattern=="" then
pattern="*l"
end
local current_log=_reading_log
local s,err
repeat
s,err,part=client:receive(pattern,part)
if s or (not _is_timeout[err]) then
current_log[client]=nil
return s,err,part
end
if err=="wantwrite" then
current_log=_writing_log
current_log[client]=gettime()
yieldcoroutine(client,_writing)
else
current_log=_reading_log
current_log[client]=gettime()
yieldcoroutine(client,_reading)
end
until false
end
local function copasreceivefrom(client,size)
local s,err,port
if not size or size==0 then
size=UDP_DATAGRAM_MAX
end
repeat
s,err,port=client:receivefrom(size)
if s or err~="timeout" then
_reading_log[client]=nil
return s,err,port
end
_reading_log[client]=gettime()
yieldcoroutine(client,_reading)
until false
end
local function copasreceivepartial(client,pattern,part)
if not pattern or pattern=="" then
pattern="*l"
end
local logger=_reading_log
local queue=_reading
local s,err
repeat
s,err,part=client:receive(pattern,part)
if s or (type(pattern)=="number" and part~="" and part) or not _is_timeout[err] then
logger[client]=nil
return s,err,part
end
if err=="wantwrite" then
logger=_writing_log
queue=_writing
else
logger=_reading_log
queue=_reading
end
logger[client]=gettime()
yieldcoroutine(client,queue)
until false
end
local function copassend(client,data,from,to)
if not from then
from=1
end
local lastIndex=from-1
local logger=_writing_log
local queue=_writing
local s,err
repeat
s,err,lastIndex=client:send(data,lastIndex+1,to)
if random(100)>90 then
logger[client]=gettime()
yieldcoroutine(client,queue)
end
if s or not _is_timeout[err] then
logger[client]=nil
return s,err,lastIndex
end
if err=="wantread" then
logger=_reading_log
queue=_reading
else
logger=_writing_log
queue=_writing
end
logger[client]=gettime()
yieldcoroutine(client,queue)
until false
end
local function copassendto(client,data,ip,port)
repeat
local s,err=client:sendto(data,ip,port)
if random(100)>90 then
_writing_log[client]=gettime()
yieldcoroutine(client,_writing)
end
if s or err~="timeout" then
_writing_log[client]=nil
return s,err
end
_writing_log[client]=gettime()
yieldcoroutine(client,_writing)
until false
end
local function copasconnect(skt,host,port)
skt:settimeout(0)
local ret,err,tried_more_than_once
repeat
ret,err=skt:connect (host,port)
if ret or (err~="timeout" and err~="Operation already in progress") then
if not ret and err=="already connected" and tried_more_than_once then
ret=1
err=nil
end
_writing_log[skt]=nil
return ret,err
end
tried_more_than_once=tried_more_than_once or true
_writing_log[skt]=gettime()
yieldcoroutine(skt,_writing)
until false
end
local function copasdohandshake(skt,sslt)
if not ssl then
ssl=require("ssl")
end
if not ssl then
report("error: no ssl library")
return
end
local nskt,err=ssl.wrap(skt,sslt)
if not nskt then
report("error: %s",tostring(err))
return
end
nskt:settimeout(0)
local queue
repeat
local success,err=nskt:dohandshake()
if success then
return nskt
elseif err=="wantwrite" then
queue=_writing
elseif err=="wantread" then
queue=_reading
else
report("error: %s",tostring(err))
return
end
yieldcoroutine(nskt,queue)
until false
end
local function copasflush(client)
end
copas.connect=copassconnect
copas.send=copassend
copas.sendto=copassendto
copas.receive=copasreceive
copas.receivefrom=copasreceivefrom
copas.copasreceivepartial=copasreceivepartial
copas.copasreceivePartial=copasreceivepartial
copas.dohandshake=copasdohandshake
copas.flush=copasflush
local function _skt_mt_tostring(self)
return tostring(self.socket).." (copas wrapped)"
end
local _skt_mt_tcp_index={
send=function(self,data,from,to)
return copassend (self.socket,data,from,to)
end,
receive=function (self,pattern,prefix)
if self.timeout==0 then
return copasreceivePartial(self.socket,pattern,prefix)
else
return copasreceive(self.socket,pattern,prefix)
end
end,
flush=function (self)
return copasflush(self.socket)
end,
settimeout=function (self,time)
self.timeout=time
return true
end,
connect=function(self,...)
local res,err=copasconnect(self.socket,...)
if res and self.ssl_params then
res,err=self:dohandshake()
end
return res,err
end,
close=function(self,...)
return self.socket:close(...)
end,
bind=function(self,...)
return self.socket:bind(...)
end,
getsockname=function(self,...)
return self.socket:getsockname(...)
end,
getstats=function(self,...)
return self.socket:getstats(...)
end,
setstats=function(self,...)
return self.socket:setstats(...)
end,
listen=function(self,...)
return self.socket:listen(...)
end,
accept=function(self,...)
return self.socket:accept(...)
end,
setoption=function(self,...)
return self.socket:setoption(...)
end,
getpeername=function(self,...)
return self.socket:getpeername(...)
end,
shutdown=function(self,...)
return self.socket:shutdown(...)
end,
dohandshake=function(self,sslt)
self.ssl_params=sslt or self.ssl_params
local nskt,err=copasdohandshake(self.socket,self.ssl_params)
if not nskt then
return nskt,err
end
self.socket=nskt
return self
end,
}
local _skt_mt_tcp={
__tostring=_skt_mt_tostring,
__index=_skt_mt_tcp_index,
}
local _skt_mt_udp_index={
sendto=function (self,...)
return copassendto(self.socket,...)
end,
receive=function (self,size)
return copasreceive(self.socket,size or UDP_DATAGRAM_MAX)
end,
receivefrom=function (self,size)
return copasreceivefrom(self.socket,size or UDP_DATAGRAM_MAX)
end,
setpeername=function(self,...)
return self.socket:getpeername(...)
end,
setsockname=function(self,...)
return self.socket:setsockname(...)
end,
close=function(self,...)
return true
end
}
local _skt_mt_udp={
__tostring=_skt_mt_tostring,
__index=_skt_mt_udp_index,
}
for k,v in next,_skt_mt_tcp_index do
if not _skt_mt_udp_index[k] then
_skt_mt_udp_index[k]=v
end
end
local function wrap(skt,sslt)
if getmetatable(skt)==_skt_mt_tcp or getmetatable(skt)==_skt_mt_udp then
return skt
end
skt:settimeout(0)
if isTCP(skt) then
return setmetatable ({ socket=skt,ssl_params=sslt },_skt_mt_tcp)
else
return setmetatable ({ socket=skt },_skt_mt_udp)
end
end
copas.wrap=wrap
function copas.handler(handler,sslparams)
return function (skt,...)
skt=wrap(skt)
if sslparams then
skt:dohandshake(sslparams)
end
return handler(skt,...)
end
end
local _errhandlers={}
function copas.setErrorHandler(err)
local co=runningcoroutine()
if co then
_errhandlers[co]=err
end
end
local function _deferror (msg,co,skt)
report("%s (%s) (%s)",msg,tostring(co),tostring(skt))
end
local function _doTick (co,skt,...)
if not co then
return
end
local ok,res,new_q=resumecoroutine(co,skt,...)
if ok and res and new_q then
new_q:insert(res)
new_q:push(res,co)
else
if not ok then
pcall(_errhandlers[co] or _deferror,res,co,skt)
end
if skt and copas.autoclose and isTCP(skt) then
skt:close()
end
_errhandlers[co]=nil
end
end
local function _accept(input,handler)
local client=input:accept()
if client then
client:settimeout(0)
local co=createcoroutine(handler)
_doTick (co,client)
end
return client
end
local function _tickRead(skt)
_doTick(_reading:pop(skt),skt)
end
local function _tickWrite(skt)
_doTick(_writing:pop(skt),skt)
end
local function addTCPserver(server,handler,timeout)
server:settimeout(timeout or 0)
_servers[server]=handler
_reading:insert(server)
end
local function addUDPserver(server,handler,timeout)
server:settimeout(timeout or 0)
local co=createcoroutine(handler)
_reading:insert(server)
_doTick(co,server)
end
function copas.addserver(server,handler,timeout)
if isTCP(server) then
addTCPserver(server,handler,timeout)
else
addUDPserver(server,handler,timeout)
end
end
function copas.removeserver(server,keep_open)
local s=server
local mt=getmetatable(server)
if mt==_skt_mt_tcp or mt==_skt_mt_udp then
s=server.socket
end
_servers[s]=nil
_reading:remove(s)
if keep_open then
return true
end
return server:close()
end
function copas.addthread(handler,...)
local thread=createcoroutine(function(_,...) return handler(...) end)
_doTick(thread,nil,...)
return thread
end
local _tasks={}
local function addtaskRead(task)
task.def_tick=_tickRead
_tasks[task]=true
end
local function addtaskWrite(task)
task.def_tick=_tickWrite
_tasks[task]=true
end
local function tasks()
return next,_tasks
end
local _readable_t={
events=function(self)
local i=0
return function ()
i=i+1
return self._evs[i]
end
end,
tick=function(self,input)
local handler=_servers[input]
if handler then
input=_accept(input,handler)
else
_reading:remove(input)
self.def_tick(input)
end
end
}
addtaskRead(_readable_t)
local _writable_t={
events=function(self)
local i=0
return function()
i=i+1
return self._evs[i]
end
end,
tick=function(self,output)
_writing:remove(output)
self.def_tick(output)
end
}
addtaskWrite(_writable_t)
local _sleeping_t={
tick=function(self,time,...)
_doTick(_sleeping:pop(time),...)
end
}
function copas.sleep(sleeptime)
yieldcoroutine((sleeptime or 0),_sleeping)
end
function copas.wakeup(co)
_sleeping:wakeup(co)
end
local last_cleansing=0
local function _select(timeout)
local now=gettime()
local r_evs,w_evs,err=selectsocket(_reading,_writing,timeout)
_readable_t._evs=r_evs
_writable_t._evs=w_evs
if (last_cleansing-now)>WATCH_DOG_TIMEOUT then
last_cleansing=now
for skt,time in next,_reading_log do
if not r_evs[skt] and (time-now)>WATCH_DOG_TIMEOUT then
local n=#r_evs+1
_reading_log[skt]=nil
r_evs[n]=skt
r_evs[skt]=n
end
end
for skt,time in next,_writing_log do
if not w_evs[skt] and (time-now)>WATCH_DOG_TIMEOUT then
local n=#w_evs+1
_writing_log[skt]=nil
w_evs[n]=skt
w_evs[skt]=n
end
end
end
if err=="timeout" and #r_evs+#w_evs>0 then
return nil
else
return err
end
end
local function copasfinished()
return not (next(_reading) or next(_writing) or _sleeping:getnext())
end
local function copasstep(timeout)
_sleeping_t:tick(gettime())
local nextwait=_sleeping:getnext()
if nextwait then
timeout=timeout and min(nextwait,timeout) or nextwait
elseif copasfinished() then
return false
end
local err=_select(timeout)
if err then
if err=="timeout" then
return false
end
return nil,err
end
for task in tasks() do
for event in task:events() do
task:tick(event)
end
end
return true
end
copas.finished=copasfinished
copas.step=copasstep
function copas.loop(timeout)
copas.running=true
while not copasfinished() do
copasstep(timeout)
end
copas.running=false
end
package.loaded["copas"]=copas
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["util-soc-imp-ltn12"] = package.loaded["util-soc-imp-ltn12"] or true
-- original size: 8709, stripped down to: 5411
local select,unpack=select,unpack
local insert,remove=table.insert,table.remove
local sub=string.sub
local function report(fmt,first,...)
if logs then
report=logs and logs.reporter("ltn12")
report(fmt,first,...)
elseif fmt then
fmt="ltn12: "..fmt
if first then
print(format(fmt,first,...))
else
print(fmt)
end
end
end
local filter={}
local source={}
local sink={}
local pump={}
local ltn12={
_VERSION="LTN12 1.0.3",
BLOCKSIZE=2048,
filter=filter,
source=source,
sink=sink,
pump=pump,
report=report,
}
function filter.cycle(low,ctx,extra)
if low then
return function(chunk)
return (low(ctx,chunk,extra))
end
end
end
function filter.chain(...)
local arg={... }
local n=select('#',...)
local top=1
local index=1
local retry=""
return function(chunk)
retry=chunk and retry
while true do
local action=arg[index]
if index==top then
chunk=action(chunk)
if chunk=="" or top==n then
return chunk
elseif chunk then
index=index+1
else
top=top+1
index=top
end
else
chunk=action(chunk or "")
if chunk=="" then
index=index-1
chunk=retry
elseif chunk then
if index==n then
return chunk
else
index=index+1
end
else
report("error: filter returned inappropriate 'nil'")
return
end
end
end
end
end
local function empty()
return nil
end
function source.empty()
return empty
end
local function sourceerror(err)
return function()
return nil,err
end
end
source.error=sourceerror
function source.file(handle,io_err)
if handle then
local blocksize=ltn12.BLOCKSIZE
return function()
local chunk=handle:read(blocksize)
if not chunk then
handle:close()
end
return chunk
end
else
return sourceerror(io_err or "unable to open file")
end
end
function source.simplify(src)
return function()
local chunk,err_or_new=src()
if err_or_new then
src=err_or_new
end
if chunk then
return chunk
else
return nil,err_or_new
end
end
end
function source.string(s)
if s then
local blocksize=ltn12.BLOCKSIZE
local i=1
return function()
local nexti=i+blocksize
local chunk=sub(s,i,nexti-1)
i=nexti
if chunk~="" then
return chunk
else
return nil
end
end
else return source.empty() end
end
function source.rewind(src)
local t={}
return function(chunk)
if chunk then
insert(t,chunk)
else
chunk=remove(t)
if chunk then
return chunk
else
return src()
end
end
end
end
function source.chain(src,f,...)
if... then
f=filter.chain(f,...)
end
local last_in=""
local last_out=""
local state="feeding"
local err
return function()
if not last_out then
report("error: source is empty")
return
end
while true do
if state=="feeding" then
last_in,err=src()
if err then
return nil,err
end
last_out=f(last_in)
if not last_out then
if last_in then
report("error: filter returned inappropriate 'nil'")
end
return nil
elseif last_out~="" then
state="eating"
if last_in then
last_in=""
end
return last_out
end
else
last_out=f(last_in)
if last_out=="" then
if last_in=="" then
state="feeding"
else
report("error: filter returned nothing")
return
end
elseif not last_out then
if last_in then
report("filter returned inappropriate 'nil'")
end
return nil
else
return last_out
end
end
end
end
end
function source.cat(...)
local arg={... }
local src=remove(arg,1)
return function()
while src do
local chunk,err=src()
if chunk then
return chunk
end
if err then
return nil,err
end
src=remove(arg,1)
end
end
end
function sink.table(t)
if not t then
t={}
end
local f=function(chunk,err)
if chunk then
insert(t,chunk)
end
return 1
end
return f,t
end
function sink.simplify(snk)
return function(chunk,err)
local ret,err_or_new=snk(chunk,err)
if not ret then
return nil,err_or_new
end
if err_or_new then
snk=err_or_new
end
return 1
end
end
local function null()
return 1
end
function sink.null()
return null
end
local function sinkerror(err)
return function()
return nil,err
end
end
sink.error=sinkerror
function sink.file(handle,io_err)
if handle then
return function(chunk,err)
if not chunk then
handle:close()
return 1
else
return handle:write(chunk)
end
end
else
return sinkerror(io_err or "unable to open file")
end
end
function sink.chain(f,snk,...)
if... then
local args={ f,snk,... }
snk=remove(args,#args)
f=filter.chain(unpack(args))
end
return function(chunk,err)
if chunk~="" then
local filtered=f(chunk)
local done=chunk and ""
while true do
local ret,snkerr=snk(filtered,err)
if not ret then
return nil,snkerr
end
if filtered==done then
return 1
end
filtered=f(done)
end
else
return 1
end
end
end
function pump.step(src,snk)
local chunk,src_err=src()
local ret,snk_err=snk(chunk,src_err)
if chunk and ret then
return 1
else
return nil,src_err or snk_err
end
end
function pump.all(src,snk,step)
if not step then
step=pump.step
end
while true do
local ret,err=step(src,snk)
if not ret then
if err then
return nil,err
else
return 1
end
end
end
end
package.loaded["ltn12"]=ltn12
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["util-soc-imp-mime"] = package.loaded["util-soc-imp-mime"] or true
-- original size: 2373, stripped down to: 1931
local type,tostring=type,tostring
local mime=mime or package.loaded.mime or require("mime.core")
local ltn12=ltn12 or package.loaded.ltn12 or require("ltn12")
local filtercycle=ltn12.filter.cycle
local function report(fmt,first,...)
if logs then
report=logs and logs.reporter("mime")
report(fmt,first,...)
elseif fmt then
fmt="mime: "..fmt
if first then
print(format(fmt,first,...))
else
print(fmt)
end
end
end
mime.report=report
local encodet={}
local decodet={}
local wrapt={}
mime.encodet=encodet
mime.decodet=decodet
mime.wrapt=wrapt
local mime_b64=mime.b64
local mime_qp=mime.qp
local mime_unb64=mime.unb64
local mime_unqp=mime.unqp
local mime_wrp=mime.wrp
local mime_qpwrp=mime.qpwrp
local mime_eol=mime_eol
local mime_dot=mime_dot
encodet['base64']=function()
return filtercycle(mime_b64,"")
end
encodet['quoted-printable']=function(mode)
return filtercycle(mime_qp,"",mode=="binary" and "=0D=0A" or "\r\n")
end
decodet['base64']=function()
return filtercycle(mime_unb64,"")
end
decodet['quoted-printable']=function()
return filtercycle(mime_unqp,"")
end
local wraptext=function(length)
if not length then
length=76
end
return filtercycle(mime_wrp,length,length)
end
local wrapquoted=function()
return filtercycle(mime_qpwrp,76,76)
end
wrapt['text']=wraptext
wrapt['base64']=wraptext
wrapt['default']=wraptext
wrapt['quoted-printable']=wrapquoted
function mime.normalize(marker)
return filtercycle(mime_eol,0,marker)
end
function mime.stuff()
return filtercycle(mime_dot,2)
end
local function choose(list)
return function(name,opt1,opt2)
if type(name)~="string" then
name,opt1,opt2="default",name,opt1
end
local filter=list[name or "nil"]
if filter then
return filter(opt1,opt2)
else
report("error: unknown key '%s'",tostring(name))
end
end
end
mime.encode=choose(encodet)
mime.decode=choose(decodet)
mime.wrap=choose(wrapt)
package.loaded["mime"]=mime
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["util-soc-imp-url"] = package.loaded["util-soc-imp-url"] or true
-- original size: 6863, stripped down to: 5269
local tonumber,tostring,type=tonumber,tostring,type
local gsub,sub,match,find,format,byte,char=string.gsub,string.sub,string.match,string.find,string.format,string.byte,string.char
local insert=table.insert
local socket=socket or require("socket")
local url={
_VERSION="URL 1.0.3",
}
socket.url=url
function url.escape(s)
return (gsub(s,"([^A-Za-z0-9_])",function(c)
return format("%%%02x",byte(c))
end))
end
local function make_set(t)
local s={}
for i=1,#t do
s[t[i]]=true
end
return s
end
local segment_set=make_set {
"-","_",".","!","~","*","'","(",
")",":","@","&","=","+","$",",",
}
local function protect_segment(s)
return gsub(s,"([^A-Za-z0-9_])",function(c)
if segment_set[c] then
return c
else
return format("%%%02X",byte(c))
end
end)
end
function url.unescape(s)
return (gsub(s,"%%(%x%x)",function(hex)
return char(tonumber(hex,16))
end))
end
local function absolute_path(base_path,relative_path)
if find(relative_path,"^/") then
return relative_path
end
local path=gsub(base_path,"[^/]*$","")
path=path..relative_path
path=gsub(path,"([^/]*%./)",function (s)
if s~="./" then
return s
else
return ""
end
end)
path=gsub(path,"/%.$","/")
local reduced
while reduced~=path do
reduced=path
path=gsub(reduced,"([^/]*/%.%./)",function (s)
if s~="../../" then
return ""
else
return s
end
end)
end
path=gsub(reduced,"([^/]*/%.%.)$",function (s)
if s~="../.." then
return ""
else
return s
end
end)
return path
end
function url.parse(url,default)
local parsed={}
for k,v in next,default or parsed do
parsed[k]=v
end
if not url or url=="" then
return nil,"invalid url"
end
url=gsub(url,"#(.*)$",function(f)
parsed.fragment=f
return ""
end)
url=gsub(url,"^([%w][%w%+%-%.]*)%:",function(s)
parsed.scheme=s
return ""
end)
url=gsub(url,"^//([^/]*)",function(n)
parsed.authority=n
return ""
end)
url=gsub(url,"%?(.*)",function(q)
parsed.query=q
return ""
end)
url=gsub(url,"%;(.*)",function(p)
parsed.params=p
return ""
end)
if url~="" then
parsed.path=url
end
local authority=parsed.authority
if not authority then
return parsed
end
authority=gsub(authority,"^([^@]*)@",function(u)
parsed.userinfo=u
return ""
end)
authority=gsub(authority,":([^:%]]*)$",function(p)
parsed.port=p
return ""
end)
if authority~="" then
parsed.host=match(authority,"^%[(.+)%]$") or authority
end
local userinfo=parsed.userinfo
if not userinfo then
return parsed
end
userinfo=gsub(userinfo,":([^:]*)$",function(p)
parsed.password=p
return ""
end)
parsed.user=userinfo
return parsed
end
function url.build(parsed)
local url=parsed.path or ""
if parsed.params then
url=url..";"..parsed.params
end
if parsed.query then
url=url.."?"..parsed.query
end
local authority=parsed.authority
if parsed.host then
authority=parsed.host
if find(authority,":") then
authority="["..authority.."]"
end
if parsed.port then
authority=authority..":"..tostring(parsed.port)
end
local userinfo=parsed.userinfo
if parsed.user then
userinfo=parsed.user
if parsed.password then
userinfo=userinfo..":"..parsed.password
end
end
if userinfo then authority=userinfo.."@"..authority end
end
if authority then
url="//"..authority..url
end
if parsed.scheme then
url=parsed.scheme..":"..url
end
if parsed.fragment then
url=url.."#"..parsed.fragment
end
return url
end
function url.absolute(base_url,relative_url)
local base_parsed
if type(base_url)=="table" then
base_parsed=base_url
base_url=url.build(base_parsed)
else
base_parsed=url.parse(base_url)
end
local relative_parsed=url.parse(relative_url)
if not base_parsed then
return relative_url
elseif not relative_parsed then
return base_url
elseif relative_parsed.scheme then
return relative_url
else
relative_parsed.scheme=base_parsed.scheme
if not relative_parsed.authority then
relative_parsed.authority=base_parsed.authority
if not relative_parsed.path then
relative_parsed.path=base_parsed.path
if not relative_parsed.params then
relative_parsed.params=base_parsed.params
if not relative_parsed.query then
relative_parsed.query=base_parsed.query
end
end
else
relative_parsed.path=absolute_path(base_parsed.path or "",relative_parsed.path)
end
end
return url.build(relative_parsed)
end
end
function url.parse_path(path)
local parsed={}
path=path or ""
gsub(path,"([^/]+)",function (s)
insert(parsed,s)
end)
for i=1,#parsed do
parsed[i]=url.unescape(parsed[i])
end
if sub(path,1,1)=="/" then
parsed.is_absolute=1
end
if sub(path,-1,-1)=="/" then
parsed.is_directory=1
end
return parsed
end
function url.build_path(parsed,unsafe)
local path=""
local n=#parsed
if unsafe then
for i=1,n-1 do
path=path..parsed[i].."/"
end
if n>0 then
path=path..parsed[n]
if parsed.is_directory then
path=path.."/"
end
end
else
for i=1,n-1 do
path=path..protect_segment(parsed[i]).."/"
end
if n>0 then
path=path..protect_segment(parsed[n])
if parsed.is_directory then
path=path.."/"
end
end
end
if parsed.is_absolute then
path="/"..path
end
return path
end
package.loaded["socket.url"]=url
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["util-soc-imp-headers"] = package.loaded["util-soc-imp-headers"] or true
-- original size: 5721, stripped down to: 3754
local next=next
local lower=string.lower
local concat=table.concat
local socket=socket or require("socket")
local headers={}
socket.headers=headers
local canonic={
["accept"]="Accept",
["accept-charset"]="Accept-Charset",
["accept-encoding"]="Accept-Encoding",
["accept-language"]="Accept-Language",
["accept-ranges"]="Accept-Ranges",
["action"]="Action",
["alternate-recipient"]="Alternate-Recipient",
["age"]="Age",
["allow"]="Allow",
["arrival-date"]="Arrival-Date",
["authorization"]="Authorization",
["bcc"]="Bcc",
["cache-control"]="Cache-Control",
["cc"]="Cc",
["comments"]="Comments",
["connection"]="Connection",
["content-description"]="Content-Description",
["content-disposition"]="Content-Disposition",
["content-encoding"]="Content-Encoding",
["content-id"]="Content-ID",
["content-language"]="Content-Language",
["content-length"]="Content-Length",
["content-location"]="Content-Location",
["content-md5"]="Content-MD5",
["content-range"]="Content-Range",
["content-transfer-encoding"]="Content-Transfer-Encoding",
["content-type"]="Content-Type",
["cookie"]="Cookie",
["date"]="Date",
["diagnostic-code"]="Diagnostic-Code",
["dsn-gateway"]="DSN-Gateway",
["etag"]="ETag",
["expect"]="Expect",
["expires"]="Expires",
["final-log-id"]="Final-Log-ID",
["final-recipient"]="Final-Recipient",
["from"]="From",
["host"]="Host",
["if-match"]="If-Match",
["if-modified-since"]="If-Modified-Since",
["if-none-match"]="If-None-Match",
["if-range"]="If-Range",
["if-unmodified-since"]="If-Unmodified-Since",
["in-reply-to"]="In-Reply-To",
["keywords"]="Keywords",
["last-attempt-date"]="Last-Attempt-Date",
["last-modified"]="Last-Modified",
["location"]="Location",
["max-forwards"]="Max-Forwards",
["message-id"]="Message-ID",
["mime-version"]="MIME-Version",
["original-envelope-id"]="Original-Envelope-ID",
["original-recipient"]="Original-Recipient",
["pragma"]="Pragma",
["proxy-authenticate"]="Proxy-Authenticate",
["proxy-authorization"]="Proxy-Authorization",
["range"]="Range",
["received"]="Received",
["received-from-mta"]="Received-From-MTA",
["references"]="References",
["referer"]="Referer",
["remote-mta"]="Remote-MTA",
["reply-to"]="Reply-To",
["reporting-mta"]="Reporting-MTA",
["resent-bcc"]="Resent-Bcc",
["resent-cc"]="Resent-Cc",
["resent-date"]="Resent-Date",
["resent-from"]="Resent-From",
["resent-message-id"]="Resent-Message-ID",
["resent-reply-to"]="Resent-Reply-To",
["resent-sender"]="Resent-Sender",
["resent-to"]="Resent-To",
["retry-after"]="Retry-After",
["return-path"]="Return-Path",
["sender"]="Sender",
["server"]="Server",
["smtp-remote-recipient"]="SMTP-Remote-Recipient",
["status"]="Status",
["subject"]="Subject",
["te"]="TE",
["to"]="To",
["trailer"]="Trailer",
["transfer-encoding"]="Transfer-Encoding",
["upgrade"]="Upgrade",
["user-agent"]="User-Agent",
["vary"]="Vary",
["via"]="Via",
["warning"]="Warning",
["will-retry-until"]="Will-Retry-Until",
["www-authenticate"]="WWW-Authenticate",
["x-mailer"]="X-Mailer",
}
headers.canonic=setmetatable(canonic,{
__index=function(t,k)
socket.report("invalid header: %s",k)
t[k]=k
return k
end
})
function headers.normalize(headers)
if not headers then
return {}
end
local normalized={}
for k,v in next,headers do
normalized[#normalized+1]=canonic[k]..": "..v
end
normalized[#normalized+1]=""
normalized[#normalized+1]=""
return concat(normalized,"\r\n")
end
function headers.lower(lowered,headers)
if not lowered then
return {}
end
if not headers then
lowered,headers={},lowered
end
for k,v in next,headers do
lowered[lower(k)]=v
end
return lowered
end
socket.headers=headers
package.loaded["socket.headers"]=headers
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["util-soc-imp-tp"] = package.loaded["util-soc-imp-tp"] or true
-- original size: 3116, stripped down to: 2533
local setmetatable,next,type,tonumber=setmetatable,next,type,tonumber
local find,upper=string.find,string.upper
local socket=socket or require("socket")
local ltn12=ltn12 or require("ltn12")
local skipsocket=socket.skip
local sinksocket=socket.sink
local tcpsocket=socket.tcp
local ltn12pump=ltn12.pump
local pumpall=ltn12pump.all
local pumpstep=ltn12pump.step
local tp={
TIMEOUT=60,
}
socket.tp=tp
local function get_reply(c)
local line,err=c:receive()
local reply=line
if err then return
nil,err
end
local code,sep=skipsocket(2,find(line,"^(%d%d%d)(.?)"))
if not code then
return nil,"invalid server reply"
end
if sep=="-" then
local current
repeat
line,err=c:receive()
if err then
return nil,err
end
current,sep=skipsocket(2,find(line,"^(%d%d%d)(.?)"))
reply=reply.."\n"..line
until code==current and sep==" "
end
return code,reply
end
local methods={}
local mt={ __index=methods }
function methods.getpeername(self)
return self.c:getpeername()
end
function methods.getsockname(self)
return self.c:getpeername()
end
function methods.check(self,ok)
local code,reply=get_reply(self.c)
if not code then
return nil,reply
end
local c=tonumber(code)
local t=type(ok)
if t=="function" then
return ok(c,reply)
elseif t=="table" then
for i=1,#ok do
if find(code,ok[i]) then
return c,reply
end
end
return nil,reply
elseif find(code,ok) then
return c,reply
else
return nil,reply
end
end
function methods.command(self,cmd,arg)
cmd=upper(cmd)
if arg then
cmd=cmd.." "..arg.."\r\n"
else
cmd=cmd.."\r\n"
end
return self.c:send(cmd)
end
function methods.sink(self,snk,pat)
local chunk,err=self.c:receive(pat)
return snk(chunk,err)
end
function methods.send(self,data)
return self.c:send(data)
end
function methods.receive(self,pat)
return self.c:receive(pat)
end
function methods.getfd(self)
return self.c:getfd()
end
function methods.dirty(self)
return self.c:dirty()
end
function methods.getcontrol(self)
return self.c
end
function methods.source(self,source,step)
local sink=sinksocket("keep-open",self.c)
local ret,err=pumpall(source,sink,step or pumpstep)
return ret,err
end
function methods.close(self)
self.c:close()
return 1
end
function tp.connect(host,port,timeout,create)
local c,e=(create or tcpsocket)()
if not c then
return nil,e
end
c:settimeout(timeout or tp.TIMEOUT)
local r,e=c:connect(host,port)
if not r then
c:close()
return nil,e
end
return setmetatable({ c=c },mt)
end
package.loaded["socket.tp"]=tp
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["util-soc-imp-http"] = package.loaded["util-soc-imp-http"] or true
-- original size: 12577, stripped down to: 9577
local tostring,tonumber,setmetatable,next,type=tostring,tonumber,setmetatable,next,type
local find,lower,format,gsub,match=string.find,string.lower,string.format,string.gsub,string.match
local concat=table.concat
local socket=socket or require("socket")
local url=socket.url or require("socket.url")
local ltn12=ltn12 or require("ltn12")
local mime=mime or require("mime")
local headers=socket.headers or require("socket.headers")
local normalizeheaders=headers.normalize
local parseurl=url.parse
local buildurl=url.build
local absoluteurl=url.absolute
local unescapeurl=url.unescape
local skipsocket=socket.skip
local sinksocket=socket.sink
local sourcesocket=socket.source
local trysocket=socket.try
local tcpsocket=socket.tcp
local newtrysocket=socket.newtry
local protectsocket=socket.protect
local emptysource=ltn12.source.empty
local stringsource=ltn12.source.string
local rewindsource=ltn12.source.rewind
local pumpstep=ltn12.pump.step
local pumpall=ltn12.pump.all
local sinknull=ltn12.sink.null
local sinktable=ltn12.sink.table
local lowerheaders=headers.lower
local mimeb64=mime.b64
local http={
TIMEOUT=60,
USERAGENT=socket._VERSION,
}
socket.http=http
local PORT=80
local SCHEMES={
http=true,
}
local function receiveheaders(sock,headers)
if not headers then
headers={}
end
local line,err=sock:receive()
if err then
return nil,err
end
while line~="" do
local name,value=skipsocket(2,find(line,"^(.-):%s*(.*)"))
if not (name and value) then
return nil,"malformed reponse headers"
end
name=lower(name)
line,err=sock:receive()
if err then
return nil,err
end
while find(line,"^%s") do
value=value..line
line=sock:receive()
if err then
return nil,err
end
end
local found=headers[name]
if found then
value=found..", "..value
end
headers[name]=value
end
return headers
end
socket.sourcet["http-chunked"]=function(sock,headers)
return setmetatable (
{
getfd=function() return sock:getfd() end,
dirty=function() return sock:dirty() end,
},{
__call=function()
local line,err=sock:receive()
if err then
return nil,err
end
local size=tonumber(gsub(line,";.*",""),16)
if not size then
return nil,"invalid chunk size"
end
if size>0 then
local chunk,err,part=sock:receive(size)
if chunk then
sock:receive()
end
return chunk,err
else
headers,err=receiveheaders(sock,headers)
if not headers then
return nil,err
end
end
end
}
)
end
socket.sinkt["http-chunked"]=function(sock)
return setmetatable(
{
getfd=function() return sock:getfd() end,
dirty=function() return sock:dirty() end,
},
{
__call=function(self,chunk,err)
if not chunk then
chunk=""
end
return sock:send(format("%X\r\n%s\r\n",#chunk,chunk))
end
})
end
local methods={}
local mt={ __index=methods }
local function openhttp(host,port,create)
local c=trysocket((create or tcpsocket)())
local h=setmetatable({ c=c },mt)
local try=newtrysocket(function() h:close() end)
h.try=try
try(c:settimeout(http.TIMEOUT))
try(c:connect(host,port or PORT))
return h
end
http.open=openhttp
function methods.sendrequestline(self,method,uri)
local requestline=format("%s %s HTTP/1.1\r\n",method or "GET",uri)
return self.try(self.c:send(requestline))
end
function methods.sendheaders(self,headers)
self.try(self.c:send(normalizeheaders(headers)))
return 1
end
function methods.sendbody(self,headers,source,step)
if not source then
source=emptysource()
end
if not step then
step=pumpstep
end
local mode="http-chunked"
if headers["content-length"] then
mode="keep-open"
end
return self.try(pumpall(source,sinksocket(mode,self.c),step))
end
function methods.receivestatusline(self)
local try=self.try
local status=try(self.c:receive(5))
if status~="HTTP/" then
return nil,status
end
status=try(self.c:receive("*l",status))
local code=skipsocket(2,find(status,"HTTP/%d*%.%d* (%d%d%d)"))
return try(tonumber(code),status)
end
function methods.receiveheaders(self)
return self.try(receiveheaders(self.c))
end
function methods.receivebody(self,headers,sink,step)
if not sink then
sink=sinknull()
end
if not step then
step=pumpstep
end
local length=tonumber(headers["content-length"])
local encoding=headers["transfer-encoding"]
local mode="default"
if encoding and encoding~="identity" then
mode="http-chunked"
elseif length then
mode="by-length"
end
return self.try(pumpall(sourcesocket(mode,self.c,length),sink,step))
end
function methods.receive09body(self,status,sink,step)
local source=rewindsource(sourcesocket("until-closed",self.c))
source(status)
return self.try(pumpall(source,sink,step))
end
function methods.close(self)
return self.c:close()
end
local function adjusturi(request)
if not request.proxy and not http.PROXY then
request={
path=trysocket(request.path,"invalid path 'nil'"),
params=request.params,
query=request.query,
fragment=request.fragment,
}
end
return buildurl(request)
end
local function adjustheaders(request)
local headers={
["user-agent"]=http.USERAGENT,
["host"]=gsub(request.authority,"^.-@",""),
["connection"]="close, TE",
["te"]="trailers"
}
local username=request.user
local password=request.password
if username and password then
headers["authorization"]="Basic "..(mimeb64(username..":"..unescapeurl(password)))
end
local proxy=request.proxy or http.PROXY
if proxy then
proxy=parseurl(proxy)
local username=proxy.user
local password=proxy.password
if username and password then
headers["proxy-authorization"]="Basic "..(mimeb64(username..":"..password))
end
end
local requestheaders=request.headers
if requestheaders then
headers=lowerheaders(headers,requestheaders)
end
return headers
end
local default={
host="",
port=PORT,
path="/",
scheme="http"
}
local function adjustrequest(originalrequest)
local url=originalrequest.url
local request=url and parseurl(url,default) or {}
for k,v in next,originalrequest do
request[k]=v
end
local host=request.host
local port=request.port
local uri=request.uri
if not host or host=="" then
trysocket(nil,"invalid host '"..tostring(host).."'")
end
if port=="" then
request.port=PORT
end
if not uri or uri=="" then
request.uri=adjusturi(request)
end
request.headers=adjustheaders(request)
local proxy=request.proxy or http.PROXY
if proxy then
proxy=parseurl(proxy)
request.host=proxy.host
request.port=proxy.port or 3128
end
return request
end
local maxredericts=4
local validredirects={ [301]=true,[302]=true,[303]=true,[307]=true }
local validmethods={ [false]=true,GET=true,HEAD=true }
local function shouldredirect(request,code,headers)
local location=headers.location
if not location then
return false
end
location=gsub(location,"%s","")
if location=="" then
return false
end
local scheme=match(location,"^([%w][%w%+%-%.]*)%:")
if scheme and not SCHEMES[scheme] then
return false
end
local method=request.method
local redirect=request.redirect
local redirects=request.nredirects or 0
return redirect and validredirects[code] and validmethods[method] and redirects<=maxredericts
end
local function shouldreceivebody(request,code)
if request.method=="HEAD" then
return nil
end
if code==204 or code==304 then
return nil
end
if code>=100 and code<200 then
return nil
end
return 1
end
local tredirect,trequest,srequest
tredirect=function(request,location)
local result,code,headers,status=trequest {
url=absoluteurl(request.url,location),
source=request.source,
sink=request.sink,
headers=request.headers,
proxy=request.proxy,
nredirects=(request.nredirects or 0)+1,
create=request.create,
}
if not headers then
headers={}
end
if not headers.location then
headers.location=location
end
return result,code,headers,status
end
trequest=function(originalrequest)
local request=adjustrequest(originalrequest)
local connection=openhttp(request.host,request.port,request.create)
local headers=request.headers
connection:sendrequestline(request.method,request.uri)
connection:sendheaders(headers)
if request.source then
connection:sendbody(headers,request.source,request.step)
end
local code,status=connection:receivestatusline()
if not code then
connection:receive09body(status,request.sink,request.step)
return 1,200
end
while code==100 do
headers=connection:receiveheaders()
code,status=connection:receivestatusline()
end
headers=connection:receiveheaders()
if shouldredirect(request,code,headers) and not request.source then
connection:close()
return tredirect(originalrequest,headers.location)
end
if shouldreceivebody(request,code) then
connection:receivebody(headers,request.sink,request.step)
end
connection:close()
return 1,code,headers,status
end
local function genericform(url,body)
local buffer={}
local request={
url=url,
sink=sinktable(buffer),
target=buffer,
}
if body then
request.source=stringsource(body)
request.method="POST"
request.headers={
["content-length"]=#body,
["content-type"]="application/x-www-form-urlencoded"
}
end
return request
end
http.genericform=genericform
srequest=function(url,body)
local request=genericform(url,body)
local _,code,headers,status=trequest(request)
return concat(request.target),code,headers,status
end
http.request=protectsocket(function(request,body)
if type(request)=="string" then
return srequest(request,body)
else
return trequest(request)
end
end)
package.loaded["socket.http"]=http
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["util-soc-imp-ftp"] = package.loaded["util-soc-imp-ftp"] or true
-- original size: 10357, stripped down to: 8548
local setmetatable,type,next=setmetatable,type,next
local find,format,gsub,match=string.find,string.format,string.gsub,string.match
local concat=table.concat
local mod=math.mod
local socket=socket or require("socket")
local url=socket.url or require("socket.url")
local tp=socket.tp or require("socket.tp")
local ltn12=ltn12 or require("ltn12")
local tcpsocket=socket.tcp
local trysocket=socket.try
local skipsocket=socket.skip
local sinksocket=socket.sink
local selectsocket=socket.select
local bindsocket=socket.bind
local newtrysocket=socket.newtry
local sourcesocket=socket.source
local protectsocket=socket.protect
local parseurl=url.parse
local unescapeurl=url.unescape
local pumpall=ltn12.pump.all
local pumpstep=ltn12.pump.step
local sourcestring=ltn12.source.string
local sinktable=ltn12.sink.table
local ftp={
TIMEOUT=60,
USER="ftp",
PASSWORD="anonymous@anonymous.org",
}
socket.ftp=ftp
local PORT=21
local methods={}
local mt={ __index=methods }
function ftp.open(server,port,create)
local tp=trysocket(tp.connect(server,port or PORT,ftp.TIMEOUT,create))
local f=setmetatable({ tp=tp },metat)
f.try=newtrysocket(function() f:close() end)
return f
end
function methods.portconnect(self)
local try=self.try
local server=self.server
try(server:settimeout(ftp.TIMEOUT))
self.data=try(server:accept())
try(self.data:settimeout(ftp.TIMEOUT))
end
function methods.pasvconnect(self)
local try=self.try
self.data=try(tcpsocket())
self(self.data:settimeout(ftp.TIMEOUT))
self(self.data:connect(self.pasvt.address,self.pasvt.port))
end
function methods.login(self,user,password)
local try=self.try
local tp=self.tp
try(tp:command("user",user or ftp.USER))
local code,reply=try(tp:check{"2..",331})
if code==331 then
try(tp:command("pass",password or ftp.PASSWORD))
try(tp:check("2.."))
end
return 1
end
function methods.pasv(self)
local try=self.try
local tp=self.tp
try(tp:command("pasv"))
local code,reply=try(self.tp:check("2.."))
local pattern="(%d+)%D(%d+)%D(%d+)%D(%d+)%D(%d+)%D(%d+)"
local a,b,c,d,p1,p2=skipsocket(2,find(reply,pattern))
try(a and b and c and d and p1 and p2,reply)
local address=format("%d.%d.%d.%d",a,b,c,d)
local port=p1*256+p2
local server=self.server
self.pasvt={
address=address,
port=port,
}
if server then
server:close()
self.server=nil
end
return address,port
end
function methods.epsv(self)
local try=self.try
local tp=self.tp
try(tp:command("epsv"))
local code,reply=try(tp:check("229"))
local pattern="%((.)(.-)%1(.-)%1(.-)%1%)"
local d,prt,address,port=match(reply,pattern)
try(port,"invalid epsv response")
local address=tp:getpeername()
local server=self.server
self.pasvt={
address=address,
port=port,
}
if self.server then
server:close()
self.server=nil
end
return address,port
end
function methods.port(self,address,port)
local try=self.try
local tp=self.tp
self.pasvt=nil
if not address then
address,port=try(tp:getsockname())
self.server=try(bindsocket(address,0))
address,port=try(self.server:getsockname())
try(self.server:settimeout(ftp.TIMEOUT))
end
local pl=mod(port,256)
local ph=(port-pl)/256
local arg=gsub(format("%s,%d,%d",address,ph,pl),"%.",",")
try(tp:command("port",arg))
try(tp:check("2.."))
return 1
end
function methods.eprt(self,family,address,port)
local try=self.try
local tp=self.tp
self.pasvt=nil
if not address then
address,port=try(tp:getsockname())
self.server=try(bindsocket(address,0))
address,port=try(self.server:getsockname())
try(self.server:settimeout(ftp.TIMEOUT))
end
local arg=format("|%s|%s|%d|",family,address,port)
try(tp:command("eprt",arg))
try(tp:check("2.."))
return 1
end
function methods.send(self,sendt)
local try=self.try
local tp=self.tp
try(self.pasvt or self.server,"need port or pasv first")
if self.pasvt then
self:pasvconnect()
end
local argument=sendt.argument or unescapeurl(gsub(sendt.path or "","^[/\\]",""))
if argument=="" then
argument=nil
end
local command=sendt.command or "stor"
try(tp:command(command,argument))
local code,reply=try(tp:check{"2..","1.."})
if not self.pasvt then
self:portconnect()
end
local step=sendt.step or pumpstep
local readt={ tp }
local checkstep=function(src,snk)
local readyt=selectsocket(readt,nil,0)
if readyt[tp] then
code=try(tp:check("2.."))
end
return step(src,snk)
end
local sink=sinksocket("close-when-done",self.data)
try(pumpall(sendt.source,sink,checkstep))
if find(code,"1..") then
try(tp:check("2.."))
end
self.data:close()
local sent=skipsocket(1,self.data:getstats())
self.data=nil
return sent
end
function methods.receive(self,recvt)
local try=self.try
local tp=self.tp
try(self.pasvt or self.server,"need port or pasv first")
if self.pasvt then self:pasvconnect() end
local argument=recvt.argument or unescapeurl(gsub(recvt.path or "","^[/\\]",""))
if argument=="" then
argument=nil
end
local command=recvt.command or "retr"
try(tp:command(command,argument))
local code,reply=try(tp:check{"1..","2.."})
if code>=200 and code<=299 then
recvt.sink(reply)
return 1
end
if not self.pasvt then
self:portconnect()
end
local source=sourcesocket("until-closed",self.data)
local step=recvt.step or pumpstep
try(pumpall(source,recvt.sink,step))
if find(code,"1..") then
try(tp:check("2.."))
end
self.data:close()
self.data=nil
return 1
end
function methods.cwd(self,dir)
local try=self.try
local tp=self.tp
try(tp:command("cwd",dir))
try(tp:check(250))
return 1
end
function methods.type(self,typ)
local try=self.try
local tp=self.tp
try(tp:command("type",typ))
try(tp:check(200))
return 1
end
function methods.greet(self)
local try=self.try
local tp=self.tp
local code=try(tp:check{"1..","2.."})
if find(code,"1..") then
try(tp:check("2.."))
end
return 1
end
function methods.quit(self)
local try=self.try
try(self.tp:command("quit"))
try(self.tp:check("2.."))
return 1
end
function methods.close(self)
local data=self.data
if data then
data:close()
end
local server=self.server
if server then
server:close()
end
local tp=self.tp
if tp then
tp:close()
end
end
local function override(t)
if t.url then
local u=parseurl(t.url)
for k,v in next,t do
u[k]=v
end
return u
else
return t
end
end
local function tput(putt)
putt=override(putt)
local host=putt.host
trysocket(host,"missing hostname")
local f=ftp.open(host,putt.port,putt.create)
f:greet()
f:login(putt.user,putt.password)
local typ=putt.type
if typ then
f:type(typ)
end
f:epsv()
local sent=f:send(putt)
f:quit()
f:close()
return sent
end
local default={
path="/",
scheme="ftp",
}
local function genericform(u)
local t=trysocket(parseurl(u,default))
trysocket(t.scheme=="ftp","wrong scheme '"..t.scheme.."'")
trysocket(t.host,"missing hostname")
local pat="^type=(.)$"
if t.params then
local typ=skipsocket(2,find(t.params,pat))
t.type=typ
trysocket(typ=="a" or typ=="i","invalid type '"..typ.."'")
end
return t
end
ftp.genericform=genericform
local function sput(u,body)
local putt=genericform(u)
putt.source=sourcestring(body)
return tput(putt)
end
ftp.put=protectsocket(function(putt,body)
if type(putt)=="string" then
return sput(putt,body)
else
return tput(putt)
end
end)
local function tget(gett)
gett=override(gett)
local host=gett.host
trysocket(host,"missing hostname")
local f=ftp.open(host,gett.port,gett.create)
f:greet()
f:login(gett.user,gett.password)
if gett.type then
f:type(gett.type)
end
f:epsv()
f:receive(gett)
f:quit()
return f:close()
end
local function sget(u)
local gett=genericform(u)
local t={}
gett.sink=sinktable(t)
tget(gett)
return concat(t)
end
ftp.command=protectsocket(function(cmdt)
cmdt=override(cmdt)
local command=cmdt.command
local argument=cmdt.argument
local check=cmdt.check
local host=cmdt.host
trysocket(host,"missing hostname")
trysocket(command,"missing command")
local f=ftp.open(host,cmdt.port,cmdt.create)
local try=f.try
local tp=f.tp
f:greet()
f:login(cmdt.user,cmdt.password)
if type(command)=="table" then
local argument=argument or {}
for i=1,#command do
local cmd=command[i]
try(tp:command(cmd,argument[i]))
if check and check[i] then
try(tp:check(check[i]))
end
end
else
try(tp:command(command,argument))
if check then
try(tp:check(check))
end
end
f:quit()
return f:close()
end)
ftp.get=protectsocket(function(gett)
if type(gett)=="string" then
return sget(gett)
else
return tget(gett)
end
end)
package.loaded["socket.ftp"]=ftp
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["util-soc-imp-smtp"] = package.loaded["util-soc-imp-smtp"] or true
-- original size: 7018, stripped down to: 5883
local type,setmetatable,next=type,setmetatable,next
local find,lower,format=string.find,string.lower,string.format
local osdate,osgetenv=os.date,os.getenv
local random=math.random
local socket=socket or require("socket")
local headers=socket.headers or require("socket.headers")
local ltn12=ltn12 or require("ltn12")
local tp=socket.tp or require("socket.tp")
local mime=mime or require("mime")
local mimeb64=mime.b64
local mimestuff=mime.stuff
local skipsocket=socket.skip
local trysocket=socket.try
local newtrysocket=socket.newtry
local protectsocket=socket.protect
local normalizeheaders=headers.normalize
local lowerheaders=headers.lower
local createcoroutine=coroutine.create
local resumecoroutine=coroutine.resume
local yieldcoroutine=coroutine.resume
local smtp={
TIMEOUT=60,
SERVER="localhost",
PORT=25,
DOMAIN=osgetenv("SERVER_NAME") or "localhost",
ZONE="-0000",
}
socket.smtp=smtp
local methods={}
local mt={ __index=methods }
function methods.greet(self,domain)
local try=self.try
local tp=self.tp
try(tp:check("2.."))
try(tp:command("EHLO",domain or _M.DOMAIN))
return skipsocket(1,try(tp:check("2..")))
end
function methods.mail(self,from)
local try=self.try
local tp=self.tp
try(tp:command("MAIL","FROM:"..from))
return try(tp:check("2.."))
end
function methods.rcpt(self,to)
local try=self.try
local tp=self.tp
try(tp:command("RCPT","TO:"..to))
return try(tp:check("2.."))
end
function methods.data(self,src,step)
local try=self.try
local tp=self.tp
try(tp:command("DATA"))
try(tp:check("3.."))
try(tp:source(src,step))
try(tp:send("\r\n.\r\n"))
return try(tp:check("2.."))
end
function methods.quit(self)
local try=self.try
local tp=self.tp
try(tp:command("QUIT"))
return try(tp:check("2.."))
end
function methods.close(self)
return self.tp:close()
end
function methods.login(self,user,password)
local try=self.try
local tp=self.tp
try(tp:command("AUTH","LOGIN"))
try(tp:check("3.."))
try(tp:send(mimeb64(user).."\r\n"))
try(tp:check("3.."))
try(tp:send(mimeb64(password).."\r\n"))
return try(tp:check("2.."))
end
function methods.plain(self,user,password)
local try=self.try
local tp=self.tp
local auth="PLAIN "..mimeb64("\0"..user.."\0"..password)
try(tp:command("AUTH",auth))
return try(tp:check("2.."))
end
function methods.auth(self,user,password,ext)
if not user or not password then
return 1
end
local try=self.try
if find(ext,"AUTH[^\n]+LOGIN") then
return self:login(user,password)
elseif find(ext,"AUTH[^\n]+PLAIN") then
return self:plain(user,password)
else
try(nil,"authentication not supported")
end
end
function methods.send(self,mail)
self:mail(mail.from)
local receipt=mail.rcpt
if type(receipt)=="table" then
for i=1,#receipt do
self:rcpt(receipt[i])
end
elseif receipt then
self:rcpt(receipt)
end
self:data(ltn12.source.chain(mail.source,mimestuff()),mail.step)
end
local function opensmtp(self,server,port,create)
if not server or server=="" then
server=smtp.SERVER
end
if not port or port=="" then
port=smtp.PORT
end
local s={
tp=trysocket(tp.connect(server,port,smtp.TIMEOUT,create)),
try=newtrysocket(function()
s:close()
end),
}
setmetatable(s,mt)
return s
end
smtp.open=opensmtp
local nofboundaries=0
local function newboundary()
nofboundaries=nofboundaries+1
return format('%s%05d==%05u',osdate('%d%m%Y%H%M%S'),random(0,99999),nofboundaries)
end
local send_message
local function send_headers(headers)
yieldcoroutine(normalizeheaders(headers))
end
local function send_multipart(message)
local boundary=newboundary()
local headers=lowerheaders(message.headers)
local body=message.body
local preamble=body.preamble
local epilogue=body.epilogue
local content=headers['content-type'] or 'multipart/mixed'
headers['content-type']=content..'; boundary="'..boundary..'"'
send_headers(headers)
if preamble then
yieldcoroutine(preamble)
yieldcoroutine("\r\n")
end
for i=1,#body do
yieldcoroutine("\r\n--"..boundary.."\r\n")
send_message(body[i])
end
yieldcoroutine("\r\n--"..boundary.."--\r\n\r\n")
if epilogue then
yieldcoroutine(epilogue)
yieldcoroutine("\r\n")
end
end
local default_content_type='text/plain; charset="UTF-8"'
local function send_source(message)
local headers=lowerheaders(message.headers)
if not headers['content-type'] then
headers['content-type']=default_content_type
end
send_headers(headers)
local getchunk=message.body
while true do
local chunk,err=getchunk()
if err then
yieldcoroutine(nil,err)
elseif chunk then
yieldcoroutine(chunk)
else
break
end
end
end
local function send_string(message)
local headers=lowerheaders(message.headers)
if not headers['content-type'] then
headers['content-type']=default_content_type
end
send_headers(headers)
yieldcoroutine(message.body)
end
function send_message(message)
local body=message.body
if type(body)=="table" then
send_multipart(message)
elseif type(body)=="function" then
send_source(message)
else
send_string(message)
end
end
local function adjust_headers(message)
local headers=lowerheaders(message.headers)
if not headers["date"] then
headers["date"]=osdate("!%a, %d %b %Y %H:%M:%S ")..(message.zone or smtp.ZONE)
end
if not headers["x-mailer"] then
headers["x-mailer"]=socket._VERSION
end
headers["mime-version"]="1.0"
return headers
end
function smtp.message(message)
message.headers=adjust_headers(message)
local action=createcoroutine(function()
send_message(message)
end)
return function()
local ret,a,b=resumecoroutine(action)
if ret then
return a,b
else
return nil,a
end
end
end
smtp.send=protectsocket(function(mail)
local snd=opensmtp(smtp,mail.server,mail.port,mail.create)
local ext=snd:greet(mail.domain)
snd:auth(mail.user,mail.password,ext)
snd:send(mail)
snd:quit()
return snd:close()
end)
package.loaded["socket.smtp"]=smtp
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["trac-set"] = package.loaded["trac-set"] or true
-- original size: 13340, stripped down to: 8826
if not modules then modules={} end modules ['trac-set']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local type,next,tostring,tonumber=type,next,tostring,tonumber
local concat,sortedhash=table.concat,table.sortedhash
local format,find,lower,gsub,topattern=string.format,string.find,string.lower,string.gsub,string.topattern
local is_boolean=string.is_boolean
local settings_to_hash=utilities.parsers.settings_to_hash
local allocate=utilities.storage.allocate
utilities=utilities or {}
local utilities=utilities
local setters=utilities.setters or {}
utilities.setters=setters
local data={}
local trace_initialize=false
function setters.initialize(filename,name,values)
local setter=data[name]
if setter then
frozen=true
local data=setter.data
if data then
for key,newvalue in sortedhash(values) do
local newvalue=is_boolean(newvalue,newvalue,true)
local functions=data[key]
if functions then
local oldvalue=functions.value
if functions.frozen then
if trace_initialize then
setter.report("%s: %a is %s to %a",filename,key,"frozen",oldvalue)
end
elseif #functions>0 and not oldvalue then
if trace_initialize then
setter.report("%s: %a is %s to %a",filename,key,"set",newvalue)
end
for i=1,#functions do
functions[i](newvalue)
end
functions.value=newvalue
functions.frozen=functions.frozen or frozen
else
if trace_initialize then
setter.report("%s: %a is %s as %a",filename,key,"kept",oldvalue)
end
end
else
functions={ default=newvalue,frozen=frozen }
data[key]=functions
if trace_initialize then
setter.report("%s: %a is %s to %a",filename,key,"defaulted",newvalue)
end
end
end
return true
end
end
end
local function set(t,what,newvalue)
local data=t.data
if not data.frozen then
local done=t.done
if type(what)=="string" then
what=settings_to_hash(what)
end
if type(what)~="table" then
return
end
if not done then
done={}
t.done=done
end
for w,value in sortedhash(what) do
if value=="" then
value=newvalue
elseif not value then
value=false
else
value=is_boolean(value,value,true)
end
w=topattern(w,true,true)
for name,functions in sortedhash(data) do
if done[name] then
elseif find(name,w) then
done[name]=true
for i=1,#functions do
functions[i](value)
end
functions.value=value
end
end
end
end
end
local function reset(t)
local data=t.data
if not data.frozen then
for name,functions in sortedthash(data) do
for i=1,#functions do
functions[i](false)
end
functions.value=false
end
end
end
local function enable(t,what)
set(t,what,true)
end
local function disable(t,what)
local data=t.data
if not what or what=="" then
t.done={}
reset(t)
else
set(t,what,false)
end
end
function setters.register(t,what,...)
local data=t.data
what=lower(what)
local functions=data[what]
if not functions then
functions={}
data[what]=functions
if trace_initialize then
t.report("defining %a",what)
end
end
local default=functions.default
for i=1,select("#",...) do
local fnc=select(i,...)
local typ=type(fnc)
if typ=="string" then
if trace_initialize then
t.report("coupling %a to %a",what,fnc)
end
local s=fnc
fnc=function(value) set(t,s,value) end
elseif typ~="function" then
fnc=nil
end
if fnc then
functions[#functions+1]=fnc
local value=functions.value or default
if value~=nil then
fnc(value)
functions.value=value
end
end
end
return false
end
function setters.enable(t,what)
local e=t.enable
t.enable,t.done=enable,{}
enable(t,what)
t.enable,t.done=e,{}
end
function setters.disable(t,what)
local e=t.disable
t.disable,t.done=disable,{}
disable(t,what)
t.disable,t.done=e,{}
end
function setters.reset(t)
t.done={}
reset(t)
end
function setters.list(t)
local list=table.sortedkeys(t.data)
local user,system={},{}
for l=1,#list do
local what=list[l]
if find(what,"^%*") then
system[#system+1]=what
else
user[#user+1]=what
end
end
return user,system
end
function setters.show(t)
local list=setters.list(t)
t.report()
for k=1,#list do
local name=list[k]
local functions=t.data[name]
if functions then
local value=functions.value
local default=functions.default
local modules=#functions
if default==nil then
default="unset"
elseif type(default)=="table" then
default=concat(default,"|")
else
default=tostring(default)
end
if value==nil then
value="unset"
elseif type(value)=="table" then
value=concat(value,"|")
else
value=tostring(value)
end
t.report(name)
t.report(" modules : %i",modules)
t.report(" default : %s",default)
t.report(" value : %s",value)
t.report()
end
end
end
local enable,disable,register,list,show=setters.enable,setters.disable,setters.register,setters.list,setters.show
function setters.report(setter,...)
print(format("%-15s : %s\n",setter.name,format(...)))
end
local function default(setter,name)
local d=setter.data[name]
return d and d.default
end
local function value(setter,name)
local d=setter.data[name]
return d and (d.value or d.default)
end
function setters.new(name)
local setter
setter={
data=allocate(),
name=name,
report=function(...) setters.report (setter,...) end,
enable=function(...) enable (setter,...) end,
disable=function(...) disable (setter,...) end,
reset=function(...) reset (setter,...) end,
register=function(...) register(setter,...) end,
list=function(...) list (setter,...) end,
show=function(...) show (setter,...) end,
default=function(...) return default (setter,...) end,
value=function(...) return value (setter,...) end,
}
data[name]=setter
return setter
end
trackers=setters.new("trackers")
directives=setters.new("directives")
experiments=setters.new("experiments")
local t_enable,t_disable=trackers .enable,trackers .disable
local d_enable,d_disable=directives .enable,directives .disable
local e_enable,e_disable=experiments.enable,experiments.disable
local trace_directives=false local trace_directives=false trackers.register("system.directives",function(v) trace_directives=v end)
local trace_experiments=false local trace_experiments=false trackers.register("system.experiments",function(v) trace_experiments=v end)
function directives.enable(...)
if trace_directives then
directives.report("enabling: % t",{...})
end
d_enable(...)
end
function directives.disable(...)
if trace_directives then
directives.report("disabling: % t",{...})
end
d_disable(...)
end
function experiments.enable(...)
if trace_experiments then
experiments.report("enabling: % t",{...})
end
e_enable(...)
end
function experiments.disable(...)
if trace_experiments then
experiments.report("disabling: % t",{...})
end
e_disable(...)
end
directives.register("system.nostatistics",function(v)
if statistics then
statistics.enable=not v
else
end
end)
directives.register("system.nolibraries",function(v)
if libraries then
libraries=nil
else
end
end)
if environment then
local engineflags=environment.engineflags
if engineflags then
local list=engineflags["c:trackers"] or engineflags["trackers"]
if type(list)=="string" then
setters.initialize("commandline flags","trackers",settings_to_hash(list))
end
local list=engineflags["c:directives"] or engineflags["directives"]
if type(list)=="string" then
setters.initialize("commandline flags","directives",settings_to_hash(list))
end
end
end
if texconfig then
local function set(k,v)
v=tonumber(v)
if v then
texconfig[k]=v
end
end
directives.register("luatex.expanddepth",function(v) set("expand_depth",v) end)
directives.register("luatex.hashextra",function(v) set("hash_extra",v) end)
directives.register("luatex.nestsize",function(v) set("nest_size",v) end)
directives.register("luatex.maxinopen",function(v) set("max_in_open",v) end)
directives.register("luatex.maxprintline",function(v) set("max_print_line",v) end)
directives.register("luatex.maxstrings",function(v) set("max_strings",v) end)
directives.register("luatex.paramsize",function(v) set("param_size",v) end)
directives.register("luatex.savesize",function(v) set("save_size",v) end)
directives.register("luatex.stacksize",function(v) set("stack_size",v) end)
end
local data=table.setmetatableindex("table")
updaters={
register=function(what,f)
local d=data[what]
d[#d+1]=f
end,
apply=function(what,...)
local d=data[what]
for i=1,#d do
d[i](...)
end
end,
}
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["trac-log"] = package.loaded["trac-log"] or true
-- original size: 32618, stripped down to: 20935
if not modules then modules={} end modules ['trac-log']={
version=1.001,
comment="companion to trac-log.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local next,type,select,print=next,type,select,print
local format,gmatch,find=string.format,string.gmatch,string.find
local concat,insert,remove=table.concat,table.insert,table.remove
local topattern=string.topattern
local utfchar=utf.char
local datetime=os.date
local openfile=io.open
local runningtex=tex and (tex.jobname or tex.formatname)
local write_nl=runningtex and texio and texio.write_nl or print
local write=runningtex and texio and texio.write or io.write
local setmetatableindex=table.setmetatableindex
local formatters=string.formatters
local settings_to_hash=utilities.parsers.settings_to_hash
local sortedkeys=table.sortedkeys
local variant="default"
logs=logs or {}
local logs=logs
local moreinfo=[[
More information about ConTeXt and the tools that come with it can be found at:
]].."\n"..[[
maillist : ntg-context@ntg.nl / http://www.ntg.nl/mailman/listinfo/ntg-context
webpage : http://www.pragma-ade.nl / http://tex.aanhet.net
wiki : http://contextgarden.net
]]
formatters.add (
formatters,"unichr",
[["U+" .. format("%%05X",%s) .. " (" .. utfchar(%s) .. ")"]]
)
formatters.add (
formatters,"chruni",
[[utfchar(%s) .. " (U+" .. format("%%05X",%s) .. ")"]]
)
local function ignore() end
setmetatableindex(logs,function(t,k) t[k]=ignore;return ignore end)
local report,subreport,status,settarget,setformats,settranslations
local direct,subdirect,writer,pushtarget,poptarget,setlogfile,settimedlog,setprocessor,setformatters,newline
if runningtex and texio then
if texio.setescape then
texio.setescape(0)
end
if arg then
for k,v in next,arg do
if v=="--ansi" or v=="--c:ansi" then
variant="ansi"
break
end
end
end
local function useluawrites()
local texio_write_nl=texio.write_nl
local texio_write=texio.write
local io_write=io.write
write_nl=function(target,...)
if not io_write then
io_write=io.write
end
if target=="term and log" then
texio_write_nl("log",...)
texio_write_nl("term","")
io_write(...)
elseif target=="log" then
texio_write_nl("log",...)
elseif target=="term" then
texio_write_nl("term","")
io_write(...)
elseif type(target)=="number" then
texio_write_nl(target,...)
elseif target~="none" then
texio_write_nl("log",target,...)
texio_write_nl("term","")
io_write(target,...)
end
end
write=function(target,...)
if not io_write then
io_write=io.write
end
if target=="term and log" then
texio_write("log",...)
io_write(...)
elseif target=="log" then
texio_write("log",...)
elseif target=="term" then
io_write(...)
elseif type(target)=="number" then
texio_write(target,...)
elseif target~="none" then
texio_write("log",target,...)
io_write(target,...)
end
end
texio.write=write
texio.write_nl=write_nl
useluawrites=ignore
end
local whereto="both"
local target=nil
local targets=nil
local formats=table.setmetatableindex("self")
local translations=table.setmetatableindex("self")
local report_yes,subreport_yes,direct_yes,subdirect_yes,status_yes
local report_nop,subreport_nop,direct_nop,subdirect_nop,status_nop
local variants={
default={
formats={
report_yes=formatters["%-15s > %s\n"],
report_nop=formatters["%-15s >\n"],
direct_yes=formatters["%-15s > %s"],
direct_nop=formatters["%-15s >"],
subreport_yes=formatters["%-15s > %s > %s\n"],
subreport_nop=formatters["%-15s > %s >\n"],
subdirect_yes=formatters["%-15s > %s > %s"],
subdirect_nop=formatters["%-15s > %s >"],
status_yes=formatters["%-15s : %s\n"],
status_nop=formatters["%-15s :\n"],
},
targets={
logfile="log",
log="log",
file="log",
console="term",
terminal="term",
both="term and log",
},
},
ansi={
formats={
report_yes=formatters["[0;33m%-15s [0;1m>[0m %s\n"],
report_nop=formatters["[0;33m%-15s [0;1m>[0m\n"],
direct_yes=formatters["[0;33m%-15s [0;1m>[0m %s"],
direct_nop=formatters["[0;33m%-15s [0;1m>[0m"],
subreport_yes=formatters["[0;33m%-15s [0;1m>[0;35m %s [0;1m>[0m %s\n"],
subreport_nop=formatters["[0;33m%-15s [0;1m>[0;35m %s [0;1m>[0m\n"],
subdirect_yes=formatters["[0;33m%-15s [0;1m>[0;35m %s [0;1m>[0m %s"],
subdirect_nop=formatters["[0;33m%-15s [0;1m>[0;35m %s [0;1m>[0m"],
status_yes=formatters["[0;33m%-15s [0;1m:[0m %s\n"],
status_nop=formatters["[0;33m%-15s [0;1m:[0m\n"],
},
targets={
logfile="none",
log="none",
file="none",
console="term",
terminal="term",
both="term",
},
}
}
logs.flush=io.flush
writer=function(...)
write_nl(target,...)
end
newline=function()
write_nl(target,"\n")
end
report=function(a,b,c,...)
if c~=nil then
write_nl(target,report_yes(translations[a],formatters[formats[b]](c,...)))
elseif b then
write_nl(target,report_yes(translations[a],formats[b]))
elseif a then
write_nl(target,report_nop(translations[a]))
else
write_nl(target,"\n")
end
end
direct=function(a,b,c,...)
if c~=nil then
return direct_yes(translations[a],formatters[formats[b]](c,...))
elseif b then
return direct_yes(translations[a],formats[b])
elseif a then
return direct_nop(translations[a])
else
return ""
end
end
subreport=function(a,s,b,c,...)
if c~=nil then
write_nl(target,subreport_yes(translations[a],translations[s],formatters[formats[b]](c,...)))
elseif b then
write_nl(target,subreport_yes(translations[a],translations[s],formats[b]))
elseif a then
write_nl(target,subreport_nop(translations[a],translations[s]))
else
write_nl(target,"\n")
end
end
subdirect=function(a,s,b,c,...)
if c~=nil then
return subdirect_yes(translations[a],translations[s],formatters[formats[b]](c,...))
elseif b then
return subdirect_yes(translations[a],translations[s],formats[b])
elseif a then
return subdirect_nop(translations[a],translations[s])
else
return ""
end
end
status=function(a,b,c,...)
if c~=nil then
write_nl(target,status_yes(translations[a],formatters[formats[b]](c,...)))
elseif b then
write_nl(target,status_yes(translations[a],formats[b]))
elseif a then
write_nl(target,status_nop(translations[a]))
else
write_nl(target,"\n")
end
end
settarget=function(askedwhereto)
whereto=askedwhereto or whereto or "both"
target=targets[whereto]
if not target then
whereto="both"
target=targets[whereto]
end
if target=="term" or target=="term and log" then
logs.flush=io.flush
else
logs.flush=ignore
end
end
local stack={}
pushtarget=function(newtarget)
insert(stack,target)
settarget(newtarget)
end
poptarget=function()
if #stack>0 then
settarget(remove(stack))
end
end
setformats=function(f)
formats=f
end
settranslations=function(t)
translations=t
end
setprocessor=function(f)
local writeline=write_nl
write_nl=function(target,...)
writeline(target,f(...))
end
end
setformatters=function(specification)
local t=nil
local f=nil
local d=variants.default
if not specification then
elseif type(specification)=="table" then
t=specification.targets
f=specification.formats or specification
else
local v=variants[specification]
if v then
t=v.targets
f=v.formats
variant=specification
end
end
targets=t or d.targets
target=targets[whereto] or target
if f then
d=d.formats
else
f=d.formats
d=f
end
setmetatableindex(f,d)
report_yes=f.report_yes
report_nop=f.report_nop
subreport_yes=f.subreport_yes
subreport_nop=f.subreport_nop
direct_yes=f.direct_yes
direct_nop=f.direct_nop
subdirect_yes=f.subdirect_yes
subdirect_nop=f.subdirect_nop
status_yes=f.status_yes
status_nop=f.status_nop
if variant=="ansi" then
useluawrites()
end
settarget(whereto)
end
setformatters(variant)
setlogfile=ignore
settimedlog=ignore
else
local report_yes,subreport_yes,status_yes
local report_nop,subreport_nop,status_nop
local variants={
default={
formats={
report_yes=formatters["%-15s | %s"],
report_nop=formatters["%-15s |"],
subreport_yes=formatters["%-15s | %s | %s"],
subreport_nop=formatters["%-15s | %s |"],
status_yes=formatters["%-15s : %s\n"],
status_nop=formatters["%-15s :\n"],
},
},
ansi={
formats={
report_yes=formatters["[0;32m%-15s [0;1m|[0m %s"],
report_nop=formatters["[0;32m%-15s [0;1m|[0m"],
subreport_yes=formatters["[0;32m%-15s [0;1m|[0;31m %s [0;1m|[0m %s"],
subreport_nop=formatters["[0;32m%-15s [0;1m|[0;31m %s [0;1m|[0m"],
status_yes=formatters["[0;32m%-15s [0;1m:[0m %s\n"],
status_nop=formatters["[0;32m%-15s [0;1m:[0m\n"],
},
},
}
logs.flush=ignore
writer=function(s)
write_nl(s)
end
newline=function()
write_nl("\n")
end
report=function(a,b,c,...)
if c then
write_nl(report_yes(a,formatters[b](c,...)))
elseif b then
write_nl(report_yes(a,b))
elseif a then
write_nl(report_nop(a))
else
write_nl("")
end
end
subreport=function(a,sub,b,c,...)
if c then
write_nl(subreport_yes(a,sub,formatters[b](c,...)))
elseif b then
write_nl(subreport_yes(a,sub,b))
elseif a then
write_nl(subreport_nop(a,sub))
else
write_nl("")
end
end
status=function(a,b,c,...)
if c then
write_nl(status_yes(a,formatters[b](c,...)))
elseif b then
write_nl(status_yes(a,b))
elseif a then
write_nl(status_nop(a))
else
write_nl("\n")
end
end
direct=ignore
subdirect=ignore
settarget=ignore
pushtarget=ignore
poptarget=ignore
setformats=ignore
settranslations=ignore
setprocessor=function(f)
local writeline=write_nl
write_nl=function(s)
writeline(f(s))
end
end
setformatters=function(specification)
local f=nil
local d=variants.default
if specification then
if type(specification)=="table" then
f=specification.formats or specification
else
local v=variants[specification]
if v then
f=v.formats
end
end
end
if f then
d=d.formats
else
f=d.formats
d=f
end
setmetatableindex(f,d)
report_yes=f.report_yes
report_nop=f.report_nop
subreport_yes=f.subreport_yes
subreport_nop=f.subreport_nop
status_yes=f.status_yes
status_nop=f.status_nop
end
setformatters(variant)
setlogfile=function(name,keepopen)
if name and name~="" then
local localtime=os.localtime
local writeline=write_nl
if keepopen then
local f=io.open(name,"ab")
write_nl=function(s)
writeline(s)
f:write(localtime()," | ",s,"\n")
end
else
write_nl=function(s)
writeline(s)
local f=io.open(name,"ab")
f:write(localtime()," | ",s,"\n")
f:close()
end
end
end
setlogfile=ignore
end
settimedlog=function()
local localtime=os.localtime
local writeline=write_nl
write_nl=function(s)
writeline(localtime().." | "..s)
end
settimedlog=ignore
end
end
logs.report=report
logs.subreport=subreport
logs.status=status
logs.settarget=settarget
logs.pushtarget=pushtarget
logs.poptarget=poptarget
logs.setformats=setformats
logs.settranslations=settranslations
logs.setlogfile=setlogfile
logs.settimedlog=settimedlog
logs.setprocessor=setprocessor
logs.setformatters=setformatters
logs.direct=direct
logs.subdirect=subdirect
logs.writer=writer
logs.newline=newline
local data={}
local states=nil
local force=false
function logs.reporter(category,subcategory)
local logger=data[category]
if not logger then
local state=states==true
if not state and type(states)=="table" then
for c,_ in next,states do
if find(category,c) then
state=true
break
end
end
end
logger={
reporters={},
state=state,
}
data[category]=logger
end
local reporter=logger.reporters[subcategory or "default"]
if not reporter then
if subcategory then
reporter=function(...)
if force or not logger.state then
subreport(category,subcategory,...)
end
end
logger.reporters[subcategory]=reporter
else
local tag=category
reporter=function(...)
if force or not logger.state then
report(category,...)
end
end
logger.reporters.default=reporter
end
end
return reporter
end
logs.new=logs.reporter
local ctxreport=logs.writer
function logs.setmessenger(m)
ctxreport=m
end
function logs.messenger(category,subcategory)
if subcategory then
return function(...)
ctxreport(subdirect(category,subcategory,...))
end
else
return function(...)
ctxreport(direct(category,...))
end
end
end
local function setblocked(category,value)
if category==true or category=="all" then
category,value="*",true
elseif category==false then
category,value="*",false
elseif value==nil then
value=true
end
if category=="*" then
states=value
for k,v in next,data do
v.state=value
end
else
alllocked=false
states=settings_to_hash(category,type(states)=="table" and states or nil)
for c in next,states do
local v=data[c]
if v then
v.state=value
else
c=topattern(c,true,true)
for k,v in next,data do
if find(k,c) then
v.state=value
end
end
end
end
end
end
function logs.disable(category,value)
setblocked(category,value==nil and true or value)
end
function logs.enable(category)
setblocked(category,false)
end
function logs.categories()
return sortedkeys(data)
end
function logs.show()
local n,c,s,max=0,0,0,0
for category,v in table.sortedpairs(data) do
n=n+1
local state=v.state
local reporters=v.reporters
local nc=#category
if nc>c then
c=nc
end
for subcategory,_ in next,reporters do
local ns=#subcategory
if ns>c then
s=ns
end
local m=nc+ns
if m>max then
max=m
end
end
local subcategories=concat(sortedkeys(reporters),", ")
if state==true then
state="disabled"
elseif state==false then
state="enabled"
else
state="unknown"
end
report("logging","category %a, subcategories %a, state %a",category,subcategories,state)
end
report("logging","categories: %s, max category: %s, max subcategory: %s, max combined: %s",n,c,s,max)
end
local delayed_reporters={}
setmetatableindex(delayed_reporters,function(t,k)
local v=logs.reporter(k.name)
t[k]=v
return v
end)
function utilities.setters.report(setter,...)
delayed_reporters[setter](...)
end
directives.register("logs.blocked",function(v)
setblocked(v,true)
end)
directives.register("logs.target",function(v)
settarget(v)
end)
if tex then
local report=logs.reporter("pages")
local texgetcount=tex and tex.getcount
local real,user,sub=0,0,0
function logs.start_page_number()
real=texgetcount("realpageno")
user=texgetcount("userpageno")
sub=texgetcount("subpageno")
end
local timing=false
local lasttime=nil
trackers.register("pages.timing",function(v)
timing=""
end)
function logs.stop_page_number()
if timing then
local elapsed=statistics.currenttime(statistics)
local average,page
if not lasttime or real<2 then
average=elapsed
page=elapsed
else
average=elapsed/(real-1)
page=elapsed-lasttime
end
lasttime=elapsed
timing=formatters[", total %0.03f, page %0.03f, average %0.03f"](elapsed,page,average)
end
if real<=0 then
report("flushing page%s",timing)
elseif user<=0 then
report("flushing realpage %s%s",real,timing)
elseif sub<=0 then
report("flushing realpage %s, userpage %s%s",real,user,timing)
else
report("flushing realpage %s, userpage %s, subpage %s%s",real,user,sub,timing)
end
logs.flush()
end
end
local nesting=0
local verbose=false
local hasscheme=url.hasscheme
function logs.show_open(name)
end
function logs.show_close(name)
end
function logs.show_load(name)
end
local simple=logs.reporter("comment")
logs.simple=simple
logs.simpleline=simple
logs.setprogram=ignore
logs.extendbanner=ignore
logs.reportlines=ignore
logs.reportbanner=ignore
logs.reportline=ignore
logs.simplelines=ignore
logs.help=ignore
local Carg,C,lpegmatch=lpeg.Carg,lpeg.C,lpeg.match
local p_newline=lpeg.patterns.newline
local linewise=(
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
)^1
local function reportlines(t,str)
if str then
lpegmatch(linewise,str,1,t)
end
end
local function reportbanner(t)
local banner=t.banner
if banner then
t.report(banner)
t.report()
end
end
local function reportversion(t)
local banner=t.banner
if banner then
t.report(banner)
end
end
local function reporthelp(t,...)
local helpinfo=t.helpinfo
if type(helpinfo)=="string" then
reportlines(t,helpinfo)
elseif type(helpinfo)=="table" then
for i=1,select("#",...) do
reportlines(t,t.helpinfo[select(i,...)])
if i<n then
t.report()
end
end
end
end
local function reportinfo(t)
t.report()
reportlines(t,t.moreinfo)
end
local function reportexport(t,method)
report(t.helpinfo)
end
local reporters={
lines=reportlines,
banner=reportbanner,
version=reportversion,
help=reporthelp,
info=reportinfo,
export=reportexport,
}
local exporters={
}
logs.reporters=reporters
logs.exporters=exporters
function logs.application(t)
t.name=t.name or "unknown"
t.banner=t.banner
t.moreinfo=moreinfo
t.report=logs.reporter(t.name)
t.help=function(...)
reporters.banner(t)
reporters.help(t,...)
reporters.info(t)
end
t.export=function(...)
reporters.export(t,...)
end
t.identify=function()
reporters.banner(t)
end
t.version=function()
reporters.version(t)
end
return t
end
local f_syslog=formatters["%s %s => %s => %s => %s\r"]
function logs.system(whereto,process,jobname,category,fmt,arg,...)
local message=f_syslog(datetime("%d/%m/%y %H:%m:%S"),process,jobname,category,arg==nil and fmt or format(fmt,arg,...))
for i=1,10 do
local f=openfile(whereto,"a")
if f then
f:write(message)
f:close()
break
else
sleep(0.1)
end
end
end
local report_system=logs.reporter("system","logs")
function logs.obsolete(old,new)
local o=loadstring("return "..new)()
if type(o)=="function" then
return function(...)
report_system("function %a is obsolete, use %a",old,new)
loadstring(old.."="..new.." return "..old)()(...)
end
elseif type(o)=="table" then
local t,m={},{}
m.__index=function(t,k)
report_system("table %a is obsolete, use %a",old,new)
m.__index,m.__newindex=o,o
return o[k]
end
m.__newindex=function(t,k,v)
report_system("table %a is obsolete, use %a",old,new)
m.__index,m.__newindex=o,o
o[k]=v
end
if libraries then
libraries.obsolete[old]=t
end
setmetatable(t,m)
return t
end
end
if utilities then
utilities.report=report_system
end
if tex and tex.error then
function logs.texerrormessage(...)
tex.error(format(...))
end
else
function logs.texerrormessage(...)
print(format(...))
end
end
io.stdout:setvbuf('no')
io.stderr:setvbuf('no')
if package.helpers.report then
package.helpers.report=logs.reporter("package loader")
end
if tex then
local finalactions={}
local fatalerrors={}
local possiblefatal={}
local loggingerrors=false
function logs.loggingerrors()
return loggingerrors
end
directives.register("logs.errors",function(v)
loggingerrors=v
if type(v)=="string" then
fatalerrors=settings_to_hash(v)
else
fatalerrors={}
end
end)
function logs.registerfinalactions(...)
insert(finalactions,...)
end
local what=nil
local report=nil
local state=nil
local target=nil
local function startlogging(t,r,w,s)
target=t
state=force
force=true
report=type(r)=="function" and r or logs.reporter(r)
what=w
pushtarget(target)
newline()
if s then
report("start %s: %s",what,s)
else
report("start %s",what)
end
if target=="logfile" then
newline()
end
return report
end
local function stoplogging()
if target=="logfile" then
newline()
end
report("stop %s",what)
if target=="logfile" then
newline()
end
poptarget()
state=oldstate
end
function logs.startfilelogging(...)
return startlogging("logfile",...)
end
logs.stopfilelogging=stoplogging
local done=false
function logs.starterrorlogging(r,w,...)
if not done then
pushtarget("terminal")
newline()
logs.report("error logging","start possible issues")
poptarget()
done=true
end
if fatalerrors[w] then
possiblefatal[w]=true
end
return startlogging("terminal",r,w,...)
end
logs.stoperrorlogging=stoplogging
function logs.finalactions()
if #finalactions>0 then
for i=1,#finalactions do
finalactions[i]()
end
if done then
pushtarget("terminal")
newline()
logs.report("error logging","stop possible issues")
poptarget()
end
return next(possiblefatal) and sortedkeys(possiblefatal) or false
end
end
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["trac-inf"] = package.loaded["trac-inf"] or true
-- original size: 8966, stripped down to: 5972
if not modules then modules={} end modules ['trac-inf']={
version=1.001,
comment="companion to trac-inf.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local type,tonumber,select=type,tonumber,select
local format,lower,find=string.format,string.lower,string.find
local concat=table.concat
local clock=os.gettimeofday or os.clock
local setmetatableindex=table.setmetatableindex
local serialize=table.serialize
local formatters=string.formatters
statistics=statistics or {}
local statistics=statistics
statistics.enable=true
statistics.threshold=0.01
local statusinfo,n,registered,timers={},0,{},{}
setmetatableindex(timers,function(t,k)
local v={ timing=0,loadtime=0 }
t[k]=v
return v
end)
local function hastiming(instance)
return instance and timers[instance]
end
local function resettiming(instance)
timers[instance or "notimer"]={ timing=0,loadtime=0 }
end
local ticks=clock
local seconds=function(n) return n or 0 end
local function starttiming(instance,reset)
local timer=timers[instance or "notimer"]
local it=timer.timing
if reset then
it=0
timer.loadtime=0
end
if it==0 then
timer.starttime=ticks()
if not timer.loadtime then
timer.loadtime=0
end
end
timer.timing=it+1
end
local function stoptiming(instance)
local timer=timers[instance or "notimer"]
local it=timer.timing
if it>1 then
timer.timing=it-1
else
local starttime=timer.starttime
if starttime and starttime>0 then
local stoptime=ticks()
local loadtime=stoptime-starttime
timer.stoptime=stoptime
timer.loadtime=timer.loadtime+loadtime
timer.timing=0
timer.starttime=0
return loadtime
end
end
return 0
end
local function elapsed(instance)
if type(instance)=="number" then
return instance
else
local timer=timers[instance or "notimer"]
return timer and seconds(timer.loadtime) or 0
end
end
local function currenttime(instance)
if type(instance)=="number" then
return instance
else
local timer=timers[instance or "notimer"]
local it=timer.timing
if it>1 then
else
local starttime=timer.starttime
if starttime and starttime>0 then
return seconds(timer.loadtime+ticks()-starttime)
end
end
return 0
end
end
local function elapsedtime(instance)
return format("%0.3f",elapsed(instance))
end
local function elapsedindeed(instance)
return elapsed(instance)>statistics.threshold
end
local function elapsedseconds(instance,rest)
if elapsedindeed(instance) then
return format("%0.3f seconds %s",elapsed(instance),rest or "")
end
end
statistics.hastiming=hastiming
statistics.resettiming=resettiming
statistics.starttiming=starttiming
statistics.stoptiming=stoptiming
statistics.currenttime=currenttime
statistics.elapsed=elapsed
statistics.elapsedtime=elapsedtime
statistics.elapsedindeed=elapsedindeed
statistics.elapsedseconds=elapsedseconds
function statistics.register(tag,fnc)
if statistics.enable and type(fnc)=="function" then
local rt=registered[tag] or (#statusinfo+1)
statusinfo[rt]={ tag,fnc }
registered[tag]=rt
if #tag>n then n=#tag end
end
end
local report=logs.reporter("mkiv lua stats")
function statistics.show()
if statistics.enable then
local register=statistics.register
register("used platform",function()
return format("%s, type: %s, binary subtree: %s",
os.platform or "unknown",os.type or "unknown",environment.texos or "unknown")
end)
register("used engine",function()
return format("%s version %s with functionality level %s, banner: %s",
LUATEXENGINE,LUATEXVERSION,LUATEXFUNCTIONALITY,lower(status.banner))
end)
register("control sequences",function()
return format("%s of %s + %s",status.cs_count,status.hash_size,status.hash_extra)
end)
register("callbacks",statistics.callbacks)
if TEXENGINE=="luajittex" and JITSUPPORTED then
local jitstatus=jit.status
if jitstatus then
local jitstatus={ jitstatus() }
if jitstatus[1] then
register("luajit options",concat(jitstatus," ",2))
end
end
end
register("lua properties",function()
local hashchar=tonumber(status.luatex_hashchars)
local mask=lua.mask or "ascii"
return format("engine: %s %s, used memory: %s, hash chars: min(%i,40), symbol mask: %s (%s)",
jit and "luajit" or "lua",
LUAVERSION,
statistics.memused(),
hashchar and 2^hashchar or "unknown",
mask,
mask=="utf" and "τεχ" or "tex")
end)
register("runtime",statistics.runtime)
logs.newline()
for i=1,#statusinfo do
local s=statusinfo[i]
local r=s[2]()
if r then
report("%s: %s",s[1],r)
end
end
statistics.enable=false
end
end
function statistics.memused()
local round=math.round or math.floor
return format("%s MB (ctx: %s MB)",round(collectgarbage("count")/1000),round(status.luastate_bytes/1000000))
end
starttiming(statistics)
function statistics.formatruntime(runtime)
return format("%s seconds",runtime)
end
function statistics.runtime()
stoptiming(statistics)
local runtime=lua.getruntime and lua.getruntime() or elapsedtime(statistics)
return statistics.formatruntime(runtime)
end
local report=logs.reporter("system")
function statistics.timed(action,all)
starttiming("run")
action()
stoptiming("run")
local runtime=tonumber(elapsedtime("run"))
if all then
local alltime=tonumber(lua.getruntime and lua.getruntime() or elapsedtime(statistics))
if alltime and alltime>0 then
report("total runtime: %0.3f seconds of %0.3f seconds",runtime,alltime)
return
end
end
report("total runtime: %0.3f seconds",runtime)
end
function statistics.tracefunction(base,tag,...)
for i=1,select("#",...) do
local name=select(i,...)
local stat={}
local func=base[name]
setmetatableindex(stat,function(t,k) t[k]=0 return 0 end)
base[name]=function(n,k,v) stat[k]=stat[k]+1 return func(n,k,v) end
statistics.register(formatters["%s.%s"](tag,name),function() return serialize(stat,"calls") end)
end
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["trac-pro"] = package.loaded["trac-pro"] or true
-- original size: 5841, stripped down to: 3352
if not modules then modules={} end modules ['trac-pro']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local getmetatable,setmetatable,rawset,type,next=getmetatable,setmetatable,rawset,type,next
local trace_namespaces=false trackers.register("system.namespaces",function(v) trace_namespaces=v end)
local report_system=logs.reporter("system","protection")
namespaces=namespaces or {}
local namespaces=namespaces
local registered={}
local function report_index(k,name)
if trace_namespaces then
report_system("reference to %a in protected namespace %a: %s",k,name)
debugger.showtraceback(report_system)
else
report_system("reference to %a in protected namespace %a",k,name)
end
end
local function report_newindex(k,name)
if trace_namespaces then
report_system("assignment to %a in protected namespace %a: %s",k,name)
debugger.showtraceback(report_system)
else
report_system("assignment to %a in protected namespace %a",k,name)
end
end
local function register(name)
local data=name=="global" and _G or _G[name]
if not data then
return
end
registered[name]=data
local m=getmetatable(data)
if not m then
m={}
setmetatable(data,m)
end
local index,newindex={},{}
m.__saved__index=m.__index
m.__no__index=function(t,k)
if not index[k] then
index[k]=true
report_index(k,name)
end
return nil
end
m.__saved__newindex=m.__newindex
m.__no__newindex=function(t,k,v)
if not newindex[k] then
newindex[k]=true
report_newindex(k,name)
end
rawset(t,k,v)
end
m.__protection__depth=0
end
local function private(name)
local data=registered[name]
if not data then
data=_G[name]
if not data then
data={}
_G[name]=data
end
register(name)
end
return data
end
local function protect(name)
local data=registered[name]
if not data then
return
end
local m=getmetatable(data)
local pd=m.__protection__depth
if pd>0 then
m.__protection__depth=pd+1
else
m.__save_d_index,m.__saved__newindex=m.__index,m.__newindex
m.__index,m.__newindex=m.__no__index,m.__no__newindex
m.__protection__depth=1
end
end
local function unprotect(name)
local data=registered[name]
if not data then
return
end
local m=getmetatable(data)
local pd=m.__protection__depth
if pd>1 then
m.__protection__depth=pd-1
else
m.__index,m.__newindex=m.__saved__index,m.__saved__newindex
m.__protection__depth=0
end
end
local function protectall()
for name,_ in next,registered do
if name~="global" then
protect(name)
end
end
end
local function unprotectall()
for name,_ in next,registered do
if name~="global" then
unprotect(name)
end
end
end
namespaces.register=register
namespaces.private=private
namespaces.protect=protect
namespaces.unprotect=unprotect
namespaces.protectall=protectall
namespaces.unprotectall=unprotectall
namespaces.private("namespaces") registered={} register("global")
directives.register("system.protect",function(v)
if v then
protectall()
else
unprotectall()
end
end)
directives.register("system.checkglobals",function(v)
if v then
report_system("enabling global namespace guard")
protect("global")
else
report_system("disabling global namespace guard")
unprotect("global")
end
end)
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["util-lua"] = package.loaded["util-lua"] or true
-- original size: 6664, stripped down to: 4589
if not modules then modules={} end modules ['util-lua']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
comment="the strip code is written by Peter Cawley",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local rep,sub,byte,dump,format=string.rep,string.sub,string.byte,string.dump,string.format
local load,loadfile,type,collectgarbage=load,loadfile,type,collectgarbage
utilities=utilities or {}
utilities.lua=utilities.lua or {}
local luautilities=utilities.lua
local report_lua=logs.reporter("system","lua")
local report_mem=logs.reporter("system","lua memory")
local tracestripping=false
local tracememory=false
luautilities.stripcode=true
luautilities.alwaysstripcode=false
luautilities.nofstrippedchunks=0
luautilities.nofstrippedbytes=0
local strippedchunks={}
luautilities.strippedchunks=strippedchunks
luautilities.suffixes={
tma="tma",
tmc=jit and "tmb" or "tmc",
lua="lua",
luc=jit and "lub" or "luc",
lui="lui",
luv="luv",
luj="luj",
tua="tua",
tuc="tuc",
}
local function register(name)
if tracestripping then
report_lua("stripped bytecode from %a",name or "unknown")
end
strippedchunks[#strippedchunks+1]=name
luautilities.nofstrippedchunks=luautilities.nofstrippedchunks+1
end
local function stupidcompile(luafile,lucfile,strip)
local code=io.loaddata(luafile)
if code and code~="" then
code=load(code)
if code then
code=dump(code,strip and luautilities.stripcode or luautilities.alwaysstripcode)
if code and code~="" then
register(name)
io.savedata(lucfile,code)
return true,0
end
else
report_lua("fatal error %a in file %a",1,luafile)
end
else
report_lua("fatal error %a in file %a",2,luafile)
end
return false,0
end
function luautilities.loadedluacode(fullname,forcestrip,name,macros)
name=name or fullname
if macros then
macros=lua.macros
end
local code,message
if macros then
code,message=macros.loaded(fullname,true,false)
else
code,message=loadfile(fullname)
end
if code then
code()
else
report_lua("loading of file %a failed:\n\t%s",fullname,message or "no message")
code,message=loadfile(fullname)
end
if forcestrip and luautilities.stripcode then
if type(forcestrip)=="function" then
forcestrip=forcestrip(fullname)
end
if forcestrip or luautilities.alwaysstripcode then
register(name)
return load(dump(code,true)),0
else
return code,0
end
elseif luautilities.alwaysstripcode then
register(name)
return load(dump(code,true)),0
else
return code,0
end
end
function luautilities.strippedloadstring(code,name,forcestrip)
local code,message=load(code)
if not code then
report_lua("loading of file %a failed:\n\t%s",name,message or "no message")
end
if forcestrip and luautilities.stripcode or luautilities.alwaysstripcode then
register(name)
return load(dump(code,true)),0
else
return code,0
end
end
function luautilities.loadstring(code,name)
local code,message=load(code)
if not code then
report_lua("loading of file %a failed:\n\t%s",name,message or "no message")
end
return code,0
end
function luautilities.compile(luafile,lucfile,cleanup,strip,fallback)
report_lua("compiling %a into %a",luafile,lucfile)
os.remove(lucfile)
local done=stupidcompile(luafile,lucfile,strip~=false)
if done then
report_lua("dumping %a into %a stripped",luafile,lucfile)
if cleanup==true and lfs.isfile(lucfile) and lfs.isfile(luafile) then
report_lua("removing %a",luafile)
os.remove(luafile)
end
end
return done
end
function luautilities.loadstripped(...)
local l=load(...)
if l then
return load(dump(l,true))
end
end
local finalizers={}
setmetatable(finalizers,{
__gc=function(t)
for i=1,#t do
pcall(t[i])
end
end
} )
function luautilities.registerfinalizer(f)
finalizers[#finalizers+1]=f
end
function luautilities.checkmemory(previous,threshold,trace)
local current=collectgarbage("count")
if previous then
local checked=(threshold or 64)*1024
local delta=current-previous
if current-previous>checked then
collectgarbage("collect")
local afterwards=collectgarbage("count")
if trace or tracememory then
report_mem("previous %r MB, current %r MB, delta %r MB, threshold %r MB, afterwards %r MB",
previous/1024,current/1024,delta/1024,threshold,afterwards)
end
return afterwards
elseif trace or tracememory then
report_mem("previous %r MB, current %r MB, delta %r MB, threshold %r MB",
previous/1024,current/1024,delta/1024,threshold)
end
end
return current
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["util-deb"] = package.loaded["util-deb"] or true
-- original size: 9955, stripped down to: 6693
if not modules then modules={} end modules ['util-deb']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local type,next,tostring,tonumber=type,next,tostring,tonumber
local format,find,sub,gsub=string.format,string.find,string.sub,string.gsub
local insert,remove,sort=table.insert,table.remove,table.sort
local setmetatableindex=table.setmetatableindex
utilities=utilities or {}
local debugger=utilities.debugger or {}
utilities.debugger=debugger
local report=logs.reporter("debugger")
local ticks=os.gettimeofday or os.clock
local seconds=function(n) return n or 0 end
local overhead=0
local dummycalls=10*1000
local nesting=0
local names={}
local initialize=false
if not (FFISUPPORTED and ffi) then
elseif os.type=="windows" then
initialize=function()
local kernel=ffilib("kernel32","system")
if kernel then
local tonumber=ffi.number or tonumber
ffi.cdef[[
int QueryPerformanceFrequency(int64_t *lpFrequency);
int QueryPerformanceCounter(int64_t *lpPerformanceCount);
]]
local target=ffi.new("__int64[1]")
ticks=function()
if kernel.QueryPerformanceCounter(target)==1 then
return tonumber(target[0])
else
return 0
end
end
local target=ffi.new("__int64[1]")
seconds=function(ticks)
if kernel.QueryPerformanceFrequency(target)==1 then
return ticks/tonumber(target[0])
else
return 0
end
end
end
initialize=false
end
elseif os.type=="unix" then
initialize=function()
local C=ffi.C
local tonumber=ffi.number or tonumber
ffi.cdef [[
/* what a mess */
typedef int clk_id_t;
typedef enum { CLOCK_REALTIME, CLOCK_MONOTONIC, CLOCK_PROCESS_CPUTIME_ID } clk_id;
typedef struct timespec { long sec; long nsec; } ctx_timespec;
int clock_gettime(clk_id_t timerid, struct timespec *t);
]]
local target=ffi.new("ctx_timespec[?]",1)
local clock=C.CLOCK_PROCESS_CPUTIME_ID
ticks=function ()
C.clock_gettime(clock,target)
return tonumber(target[0].sec*1000000000+target[0].nsec)
end
seconds=function(ticks)
return ticks/1000000000
end
initialize=false
end
end
setmetatableindex(names,function(t,name)
local v=setmetatableindex(function(t,source)
local v=setmetatableindex(function(t,line)
local v={ total=0,count=0,nesting=0 }
t[line]=v
return v
end)
t[source]=v
return v
end)
t[name]=v
return v
end)
local getinfo=nil
local sethook=nil
local function hook(where)
local f=getinfo(2,"nSl")
if f then
local source=f.short_src
if not source then
return
end
local line=f.linedefined or 0
local name=f.name
if not name then
local what=f.what
if what=="C" then
name="<anonymous>"
else
name=f.namewhat or what or "<unknown>"
end
end
local data=names[name][source][line]
if where=="call" then
local nesting=data.nesting
if nesting==0 then
data.count=data.count+1
insert(data,ticks())
data.nesting=1
else
data.nesting=nesting+1
end
elseif where=="return" then
local nesting=data.nesting
if nesting==1 then
local t=remove(data)
if t then
data.total=data.total+ticks()-t
end
data.nesting=0
else
data.nesting=nesting-1
end
end
end
end
function debugger.showstats(printer,threshold)
local printer=printer or report
local calls=0
local functions=0
local dataset={}
local length=0
local realtime=0
local totaltime=0
local threshold=threshold or 0
for name,sources in next,names do
for source,lines in next,sources do
for line,data in next,lines do
local count=data.count
if count>threshold then
if #name>length then
length=#name
end
local total=data.total
local real=total
if real>0 then
real=total-(count*overhead/dummycalls)
if real<0 then
real=0
end
realtime=realtime+real
end
totaltime=totaltime+total
if line<0 then
line=0
end
dataset[#dataset+1]={ real,total,count,name,source,line }
end
end
end
end
sort(dataset,function(a,b)
if a[1]==b[1] then
if a[2]==b[2] then
if a[3]==b[3] then
if a[4]==b[4] then
if a[5]==b[5] then
return a[6]<b[6]
else
return a[5]<b[5]
end
else
return a[4]<b[4]
end
else
return b[3]<a[3]
end
else
return b[2]<a[2]
end
else
return b[1]<a[1]
end
end)
if length>50 then
length=50
end
local fmt=string.formatters["%4.9k s %3.3k %% %4.9k s %3.3k %% %8i # %-"..length.."s %4i %s"]
for i=1,#dataset do
local data=dataset[i]
local real=data[1]
local total=data[2]
local count=data[3]
local name=data[4]
local source=data[5]
local line=data[6]
calls=calls+count
functions=functions+1
name=gsub(name,"%s+"," ")
if #name>length then
name=sub(name,1,length)
end
printer(fmt(seconds(total),100*total/totaltime,seconds(real),100*real/realtime,count,name,line,source))
end
printer("")
printer(format("functions : %i",functions))
printer(format("calls : %i",calls))
printer(format("overhead : %f",seconds(overhead/1000)))
end
local function getdebug()
if sethook and getinfo then
return
end
if not debug then
local okay
okay,debug=pcall(require,"debug")
end
if type(debug)~="table" then
return
end
getinfo=debug.getinfo
sethook=debug.sethook
if type(getinfo)~="function" then
getinfo=nil
end
if type(sethook)~="function" then
sethook=nil
end
end
function debugger.savestats(filename,threshold)
local f=io.open(filename,'w')
if f then
debugger.showstats(function(str) f:write(str,"\n") end,threshold)
f:close()
end
end
function debugger.enable()
getdebug()
if sethook and getinfo and nesting==0 then
running=true
if initialize then
initialize()
end
sethook(hook,"cr")
local function dummy() end
local t=ticks()
for i=1,dummycalls do
dummy()
end
overhead=ticks()-t
end
if nesting>0 then
nesting=nesting+1
end
end
function debugger.disable()
if nesting>0 then
nesting=nesting-1
end
if sethook and getinfo and nesting==0 then
sethook()
end
end
local function showtraceback(rep)
getdebug()
if getinfo then
local level=2
local reporter=rep or report
while true do
local info=getinfo(level,"Sl")
if not info then
break
elseif info.what=="C" then
reporter("%2i : %s",level-1,"C function")
else
reporter("%2i : %s : %s",level-1,info.short_src,info.currentline)
end
level=level+1
end
end
end
debugger.showtraceback=showtraceback
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["util-tpl"] = package.loaded["util-tpl"] or true
-- original size: 7112, stripped down to: 3887
if not modules then modules={} end modules ['util-tpl']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
utilities.templates=utilities.templates or {}
local templates=utilities.templates
local trace_template=false trackers.register("templates.trace",function(v) trace_template=v end)
local report_template=logs.reporter("template")
local tostring,next=tostring,next
local format,sub,byte=string.format,string.sub,string.byte
local P,C,R,Cs,Cc,Carg,lpegmatch,lpegpatterns=lpeg.P,lpeg.C,lpeg.R,lpeg.Cs,lpeg.Cc,lpeg.Carg,lpeg.match,lpeg.patterns
local replacer
local function replacekey(k,t,how,recursive)
local v=t[k]
if not v then
if trace_template then
report_template("unknown key %a",k)
end
return ""
else
v=tostring(v)
if trace_template then
report_template("setting key %a to value %a",k,v)
end
if recursive then
return lpegmatch(replacer,v,1,t,how,recursive)
else
return v
end
end
end
local sqlescape=lpeg.replacer {
{ "'","''" },
{ "\\","\\\\" },
{ "\r\n","\\n" },
{ "\r","\\n" },
}
local sqlquoted=Cs(Cc("'")*sqlescape*Cc("'"))
lpegpatterns.sqlescape=sqlescape
lpegpatterns.sqlquoted=sqlquoted
local luaescape=lpegpatterns.luaescape
local escapers={
lua=function(s)
return lpegmatch(luaescape,s)
end,
sql=function(s)
return lpegmatch(sqlescape,s)
end,
}
local quotedescapers={
lua=function(s)
return format("%q",s)
end,
sql=function(s)
return lpegmatch(sqlquoted,s)
end,
}
local luaescaper=escapers.lua
local quotedluaescaper=quotedescapers.lua
local function replacekeyunquoted(s,t,how,recurse)
if how==false then
return replacekey(s,t,how,recurse)
else
local escaper=how and escapers[how] or luaescaper
return escaper(replacekey(s,t,how,recurse))
end
end
local function replacekeyquoted(s,t,how,recurse)
if how==false then
return replacekey(s,t,how,recurse)
else
local escaper=how and quotedescapers[how] or quotedluaescaper
return escaper(replacekey(s,t,how,recurse))
end
end
local function replaceoptional(l,m,r,t,how,recurse)
local v=t[l]
return v and v~="" and lpegmatch(replacer,r,1,t,how or "lua",recurse or false) or ""
end
local single=P("%")
local double=P("%%")
local lquoted=P("%[")
local rquoted=P("]%")
local lquotedq=P("%(")
local rquotedq=P(")%")
local escape=double/'%%'
local nosingle=single/''
local nodouble=double/''
local nolquoted=lquoted/''
local norquoted=rquoted/''
local nolquotedq=lquotedq/''
local norquotedq=rquotedq/''
local noloptional=P("%?")/''
local noroptional=P("?%")/''
local nomoptional=P(":")/''
local args=Carg(1)*Carg(2)*Carg(3)
local key=nosingle*((C((1-nosingle )^1)*args)/replacekey )*nosingle
local quoted=nolquotedq*((C((1-norquotedq )^1)*args)/replacekeyquoted )*norquotedq
local unquoted=nolquoted*((C((1-norquoted )^1)*args)/replacekeyunquoted)*norquoted
local optional=noloptional*((C((1-nomoptional)^1)*nomoptional*C((1-noroptional)^1)*args)/replaceoptional)*noroptional
local any=P(1)
replacer=Cs((unquoted+quoted+escape+optional+key+any)^0)
local function replace(str,mapping,how,recurse)
if mapping and str then
return lpegmatch(replacer,str,1,mapping,how or "lua",recurse or false) or str
else
return str
end
end
templates.replace=replace
function templates.replacer(str,how,recurse)
return function(mapping)
return lpegmatch(replacer,str,1,mapping,how or "lua",recurse or false) or str
end
end
function templates.load(filename,mapping,how,recurse)
local data=io.loaddata(filename) or ""
if mapping and next(mapping) then
return replace(data,mapping,how,recurse)
else
return data
end
end
function templates.resolve(t,mapping,how,recurse)
if not mapping then
mapping=t
end
for k,v in next,t do
t[k]=replace(v,mapping,how,recurse)
end
return t
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["util-sbx"] = package.loaded["util-sbx"] or true
-- original size: 20393, stripped down to: 13121
if not modules then modules={} end modules ['util-sbx']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
if not sandbox then require("l-sandbox") end
local next,type=next,type
local replace=utilities.templates.replace
local collapsepath=file.collapsepath
local expandname=dir.expandname
local sortedhash=table.sortedhash
local lpegmatch=lpeg.match
local platform=os.type
local P,S,C=lpeg.P,lpeg.S,lpeg.C
local gsub=string.gsub
local lower=string.lower
local find=string.find
local concat=string.concat
local unquoted=string.unquoted
local optionalquoted=string.optionalquoted
local basename=file.basename
local nameonly=file.nameonly
local sandbox=sandbox
local validroots={}
local validrunners={}
local validbinaries=true
local validlibraries=true
local validators={}
local finalized=nil
local trace=false
local p_validroot=nil
local p_split=lpeg.firstofsplit(" ")
local report=logs.reporter("sandbox")
trackers.register("sandbox",function(v) trace=v end)
sandbox.setreporter(report)
sandbox.finalizer {
category="files",
action=function()
finalized=true
end
}
local function registerroot(root,what)
if finalized then
report("roots are already finalized")
else
if type(root)=="table" then
root,what=root[1],root[2]
end
if type(root)=="string" and root~="" then
root=collapsepath(expandname(root))
if what=="r" or what=="ro" or what=="readable" then
what="read"
elseif what=="w" or what=="wo" or what=="writable" then
what="write"
end
validroots[root]=what=="write" or false
end
end
end
sandbox.finalizer {
category="files",
action=function()
if p_validroot then
report("roots are already initialized")
else
sandbox.registerroot(".","write")
for name in sortedhash(validroots) do
if p_validroot then
p_validroot=P(name)+p_validroot
else
p_validroot=P(name)
end
end
p_validroot=p_validroot/validroots
end
end
}
local function registerbinary(name)
if finalized then
report("binaries are already finalized")
elseif type(name)=="string" and name~="" then
if not validbinaries then
return
end
if validbinaries==true then
validbinaries={ [name]=true }
else
validbinaries[name]=true
end
elseif name==true then
validbinaries={}
end
end
local function registerlibrary(name)
if finalized then
report("libraries are already finalized")
elseif type(name)=="string" and name~="" then
if not validlibraries then
return
end
if validlibraries==true then
validlibraries={ [nameonly(name)]=true }
else
validlibraries[nameonly(name)]=true
end
elseif name==true then
validlibraries={}
end
end
local p_write=S("wa") p_write=(1-p_write)^0*p_write
local p_path=S("\\/~$%:") p_path=(1-p_path )^0*p_path
local function normalized(name)
if platform=="windows" then
name=gsub(name,"/","\\")
end
return name
end
function sandbox.possiblepath(name)
return lpegmatch(p_path,name) and true or false
end
local filenamelogger=false
function sandbox.setfilenamelogger(l)
filenamelogger=type(l)=="function" and l or false
end
local function validfilename(name,what)
if p_validroot and type(name)=="string" and lpegmatch(p_path,name) then
local asked=collapsepath(expandname(name))
local okay=lpegmatch(p_validroot,asked)
if okay==true then
if filenamelogger then
filenamelogger(name,"w",asked,true)
end
return name
elseif okay==false then
if not what then
if filenamelogger then
filenamelogger(name,"r",asked,true)
end
return name
elseif lpegmatch(p_write,what) then
if filenamelogger then
filenamelogger(name,"w",asked,false)
end
return
else
if filenamelogger then
filenamelogger(name,"r",asked,true)
end
return name
end
elseif filenamelogger then
filenamelogger(name,"*",name,false)
end
else
return name
end
end
local function readable(name,finalized)
return validfilename(name,"r")
end
local function normalizedreadable(name,finalized)
local valid=validfilename(name,"r")
if valid then
return normalized(valid)
end
end
local function writeable(name,finalized)
return validfilename(name,"w")
end
local function normalizedwriteable(name,finalized)
local valid=validfilename(name,"w")
if valid then
return normalized(valid)
end
end
validators.readable=readable
validators.writeable=normalizedwriteable
validators.normalizedreadable=normalizedreadable
validators.normalizedwriteable=writeable
validators.filename=readable
table.setmetatableindex(validators,function(t,k)
if k then
t[k]=readable
end
return readable
end)
function validators.string(s,finalized)
if finalized and suspicious(s) then
return ""
else
return s
end
end
function validators.cache(s)
if finalized then
return basename(s)
else
return s
end
end
function validators.url(s)
if finalized and find("^file:") then
return ""
else
return s
end
end
local function filehandlerone(action,one,...)
local checkedone=validfilename(one)
if checkedone then
return action(one,...)
else
end
end
local function filehandlertwo(action,one,two,...)
local checkedone=validfilename(one)
if checkedone then
local checkedtwo=validfilename(two)
if checkedtwo then
return action(one,two,...)
else
end
else
end
end
local function iohandler(action,one,...)
if type(one)=="string" then
local checkedone=validfilename(one)
if checkedone then
return action(one,...)
end
elseif one then
return action(one,...)
else
return action()
end
end
local osexecute=sandbox.original(os.execute)
local iopopen=sandbox.original(io.popen)
local reported={}
local function validcommand(name,program,template,checkers,defaults,variables,reporter,strict)
if validbinaries~=false and (validbinaries==true or validbinaries[program]) then
if variables then
for variable,value in next,variables do
local checker=validators[checkers[variable]]
if checker then
value=checker(unquoted(value),strict)
if value then
variables[variable]=optionalquoted(value)
else
report("variable %a with value %a fails the check",variable,value)
return
end
else
report("variable %a has no checker",variable)
return
end
end
for variable,default in next,defaults do
local value=variables[variable]
if not value or value=="" then
local checker=validators[checkers[variable]]
if checker then
default=checker(unquoted(default),strict)
if default then
variables[variable]=optionalquoted(default)
else
report("variable %a with default %a fails the check",variable,default)
return
end
end
end
end
end
local command=program.." "..replace(template,variables)
if reporter then
reporter("executing runner %a: %s",name,command)
elseif trace then
report("executing runner %a: %s",name,command)
end
return command
elseif not reported[name] then
report("executing program %a of runner %a is not permitted",program,name)
reported[name]=true
end
end
local runners={
resultof=function(...)
local command=validcommand(...)
if command then
if trace then
report("resultof: %s",command)
end
local handle=iopopen(command,"r")
if handle then
local result=handle:read("*all") or ""
handle:close()
return result
end
end
end,
execute=function(...)
local command=validcommand(...)
if command then
if trace then
report("execute: %s",command)
end
return osexecute(command)
end
end,
pipeto=function(...)
local command=validcommand(...)
if command then
if trace then
report("pipeto: %s",command)
end
return iopopen(command,"w")
end
end,
}
function sandbox.registerrunner(specification)
if type(specification)=="string" then
local wrapped=validrunners[specification]
inspect(table.sortedkeys(validrunners))
if wrapped then
return wrapped
else
report("unknown predefined runner %a",specification)
return
end
end
if type(specification)~="table" then
report("specification should be a table (or string)")
return
end
local name=specification.name
if type(name)~="string" then
report("invalid name, string expected",name)
return
end
if validrunners[name] then
report("invalid name, runner %a already defined")
return
end
local program=specification.program
if type(program)=="string" then
elseif type(program)=="table" then
program=program[platform] or program.default or program.unix
end
if type(program)~="string" or program=="" then
report("invalid runner %a specified for platform %a",name,platform)
return
end
local template=specification.template
if not template then
report("missing template for runner %a",name)
return
end
local method=specification.method or "execute"
local checkers=specification.checkers or {}
local defaults=specification.defaults or {}
local runner=runners[method]
if runner then
local finalized=finalized
local wrapped=function(variables)
return runner(name,program,template,checkers,defaults,variables,specification.reporter,finalized)
end
validrunners[name]=wrapped
return wrapped
else
validrunners[name]=nil
report("invalid method for runner %a",name)
end
end
function sandbox.getrunner(name)
return name and validrunners[name]
end
local function suspicious(str)
return (find(str,"[/\\]") or find(command,"..",1,true)) and true or false
end
local function binaryrunner(action,command,...)
if validbinaries==false then
report("no binaries permitted, ignoring command: %s",command)
return
end
if type(command)~="string" then
report("command should be a string")
return
end
local program=lpegmatch(p_split,command)
if not program or program=="" then
report("unable to filter binary from command: %s",command)
return
end
if validbinaries==true then
elseif not validbinaries[program] then
report("binary not permitted, ignoring command: %s",command)
return
elseif suspicious(command) then
report("/ \\ or .. found, ignoring command (use sandbox.registerrunner): %s",command)
return
end
return action(command,...)
end
local function dummyrunner(action,command,...)
if type(command)=="table" then
command=concat(command," ",command[0] and 0 or 1)
end
report("ignoring command: %s",command)
end
sandbox.filehandlerone=filehandlerone
sandbox.filehandlertwo=filehandlertwo
sandbox.iohandler=iohandler
function sandbox.disablerunners()
validbinaries=false
end
function sandbox.disablelibraries()
validlibraries=false
end
if FFISUPPORTED and ffi then
function sandbox.disablelibraries()
validlibraries=false
for k,v in next,ffi do
if k~="gc" then
ffi[k]=nil
end
end
end
local fiiload=ffi.load
if fiiload then
local reported={}
function ffi.load(name,...)
if validlibraries==false then
elseif validlibraries==true then
return fiiload(name,...)
elseif validlibraries[nameonly(name)] then
return fiiload(name,...)
else
end
if not reported[name] then
report("using library %a is not permitted",name)
reported[name]=true
end
return nil
end
end
end
local overload=sandbox.overload
local register=sandbox.register
overload(loadfile,filehandlerone,"loadfile")
if io then
overload(io.open,filehandlerone,"io.open")
overload(io.popen,binaryrunner,"io.popen")
overload(io.input,iohandler,"io.input")
overload(io.output,iohandler,"io.output")
overload(io.lines,filehandlerone,"io.lines")
end
if os then
overload(os.execute,binaryrunner,"os.execute")
overload(os.spawn,dummyrunner,"os.spawn")
overload(os.exec,dummyrunner,"os.exec")
overload(os.resultof,binaryrunner,"os.resultof")
overload(os.pipeto,binaryrunner,"os.pipeto")
overload(os.rename,filehandlertwo,"os.rename")
overload(os.remove,filehandlerone,"os.remove")
end
if lfs then
overload(lfs.chdir,filehandlerone,"lfs.chdir")
overload(lfs.mkdir,filehandlerone,"lfs.mkdir")
overload(lfs.rmdir,filehandlerone,"lfs.rmdir")
overload(lfs.isfile,filehandlerone,"lfs.isfile")
overload(lfs.isdir,filehandlerone,"lfs.isdir")
overload(lfs.attributes,filehandlerone,"lfs.attributes")
overload(lfs.dir,filehandlerone,"lfs.dir")
overload(lfs.lock_dir,filehandlerone,"lfs.lock_dir")
overload(lfs.touch,filehandlerone,"lfs.touch")
overload(lfs.link,filehandlertwo,"lfs.link")
overload(lfs.setmode,filehandlerone,"lfs.setmode")
overload(lfs.readlink,filehandlerone,"lfs.readlink")
overload(lfs.shortname,filehandlerone,"lfs.shortname")
overload(lfs.symlinkattributes,filehandlerone,"lfs.symlinkattributes")
end
if zip then
zip.open=register(zip.open,filehandlerone,"zip.open")
end
if fontloader then
fontloader.open=register(fontloader.open,filehandlerone,"fontloader.open")
fontloader.info=register(fontloader.info,filehandlerone,"fontloader.info")
end
if epdf then
epdf.open=register(epdf.open,filehandlerone,"epdf.open")
end
sandbox.registerroot=registerroot
sandbox.registerbinary=registerbinary
sandbox.registerlibrary=registerlibrary
sandbox.validfilename=validfilename
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["util-mrg"] = package.loaded["util-mrg"] or true
-- original size: 7819, stripped down to: 5881
if not modules then modules={} end modules ['util-mrg']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local gsub,format=string.gsub,string.format
local concat=table.concat
local type,next=type,next
local 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
local lpegmatch,patterns=lpeg.match,lpeg.patterns
utilities=utilities or {}
local merger=utilities.merger or {}
utilities.merger=merger
merger.strip_comment=true
local report=logs.reporter("system","merge")
utilities.report=report
local m_begin_merge="begin library merge"
local m_end_merge="end library merge"
local m_begin_closure="do -- create closure to overcome 200 locals limit"
local m_end_closure="end -- of closure"
local m_pattern="%c+".."%-%-%s+"..m_begin_merge.."%c+(.-)%c+".."%-%-%s+"..m_end_merge.."%c+"
local m_format="\n\n-- "..m_begin_merge.."\n%s\n".."-- "..m_end_merge.."\n\n"
local m_faked="-- ".."created merged file".."\n\n".."-- "..m_begin_merge.."\n\n".."-- "..m_end_merge.."\n\n"
local m_report=[[
-- used libraries : %s
-- skipped libraries : %s
-- original bytes : %s
-- stripped bytes : %s
]]
local m_preloaded=[[package.loaded[%q] = package.loaded[%q] or true]]
local function self_fake()
return m_faked
end
local function self_nothing()
return ""
end
local function self_load(name)
local data=io.loaddata(name) or ""
if data=="" then
report("unknown file %a",name)
else
report("inserting file %a",name)
end
return data or ""
end
local space=patterns.space
local eol=patterns.newline
local equals=P("=")^0
local open=P("[")*Cg(equals,"init")*P("[")*P("\n")^-1
local close=P("]")*C(equals)*P("]")
local closeeq=Cmt(close*Cb("init"),function(s,i,a,b) return a==b end)
local longstring=open*(1-closeeq)^0*close
local quoted=patterns.quoted
local digit=patterns.digit
local emptyline=space^0*eol
local operator1=P("<=")+P(">=")+P("~=")+P("..")+S("/^<>=*+%%")
local operator2=S("*+/")
local operator3=S("-")
local operator4=P("..")
local separator=S(",;")
local ignore=(P("]")*space^1*P("=")*space^1*P("]"))/"]=["+(P("=")*space^1*P("{"))/"={"+(P("(")*space^1)/"("+(P("{")*(space+eol)^1*P("}"))/"{}"
local strings=quoted
local longcmt=(emptyline^0*P("--")*longstring*emptyline^0)/""
local longstr=longstring
local comment=emptyline^0*P("--")*P("-")^0*(1-eol)^0*emptyline^1/"\n"
local optionalspaces=space^0/""
local mandatespaces=space^1/""
local optionalspacing=(eol+space)^0/""
local mandatespacing=(eol+space)^1/""
local pack=digit*space^1*operator4*optionalspacing+optionalspacing*operator1*optionalspacing+optionalspacing*operator2*optionalspaces+mandatespacing*operator3*mandatespaces+optionalspaces*separator*optionalspaces
local lines=emptyline^2/"\n"
local spaces=(space*space)/" "
local spaces=(space*space*space*space)/" "
local compact=Cs ((
ignore+strings+longcmt+longstr+comment+pack+lines+spaces+1
)^1 )
local strip=Cs((emptyline^2/"\n"+1)^0)
local stripreturn=Cs((1-P("return")*space^1*P(1-space-eol)^1*(space+eol)^0*P(-1))^1)
function merger.compact(data)
return lpegmatch(strip,lpegmatch(compact,data))
end
local function self_compact(data)
local delta=0
if merger.strip_comment then
local before=#data
data=lpegmatch(compact,data)
data=lpegmatch(strip,data)
local after=#data
delta=before-after
report("original size %s, compacted to %s, stripped %s",before,after,delta)
data=format("-- original size: %s, stripped down to: %s\n\n%s",before,after,data)
end
return lpegmatch(stripreturn,data) or data,delta
end
local function self_save(name,data)
if data~="" then
io.savedata(name,data)
report("saving %s with size %s",name,#data)
end
end
local function self_swap(data,code)
return data~="" and (gsub(data,m_pattern,function() return format(m_format,code) end,1)) or ""
end
local function self_libs(libs,list)
local result,f,frozen,foundpath={},nil,false,nil
result[#result+1]="\n"
if type(libs)=='string' then libs={ libs } end
if type(list)=='string' then list={ list } end
for i=1,#libs do
local lib=libs[i]
for j=1,#list do
local pth=gsub(list[j],"\\","/")
report("checking library path %a",pth)
local name=pth.."/"..lib
if lfs.isfile(name) then
foundpath=pth
end
end
if foundpath then break end
end
if foundpath then
report("using library path %a",foundpath)
local right,wrong,original,stripped={},{},0,0
for i=1,#libs do
local lib=libs[i]
local fullname=foundpath.."/"..lib
if lfs.isfile(fullname) then
report("using library %a",fullname)
local preloaded=file.nameonly(lib)
local data=io.loaddata(fullname,true)
original=original+#data
local data,delta=self_compact(data)
right[#right+1]=lib
result[#result+1]=m_begin_closure
result[#result+1]=format(m_preloaded,preloaded,preloaded)
result[#result+1]=data
result[#result+1]=m_end_closure
stripped=stripped+delta
else
report("skipping library %a",fullname)
wrong[#wrong+1]=lib
end
end
right=#right>0 and concat(right," ") or "-"
wrong=#wrong>0 and concat(wrong," ") or "-"
report("used libraries: %a",right)
report("skipped libraries: %a",wrong)
report("original bytes: %a",original)
report("stripped bytes: %a",stripped)
result[#result+1]=format(m_report,right,wrong,original,stripped)
else
report("no valid library path found")
end
return concat(result,"\n\n")
end
function merger.selfcreate(libs,list,target)
if target then
self_save(target,self_swap(self_fake(),self_libs(libs,list)))
end
end
function merger.selfmerge(name,libs,list,target)
self_save(target or name,self_swap(self_load(name),self_libs(libs,list)))
end
function merger.selfclean(name)
self_save(name,self_swap(self_load(name),self_nothing()))
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["util-env"] = package.loaded["util-env"] or true
-- original size: 9738, stripped down to: 5531
if not modules then modules={} end modules ['util-env']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local allocate,mark=utilities.storage.allocate,utilities.storage.mark
local format,sub,match,gsub,find=string.format,string.sub,string.match,string.gsub,string.find
local unquoted,quoted,optionalquoted=string.unquoted,string.quoted,string.optionalquoted
local concat,insert,remove=table.concat,table.insert,table.remove
environment=environment or {}
local environment=environment
local setlocale=os.setlocale
setlocale(nil,nil)
local report=logs.reporter("system")
function os.setlocale(a,b)
if a or b then
if report then
report()
report("You're messing with os.locale in a supposedly locale neutral enviroment. From")
report("now on are on your own and without support. Crashes or unexpected side effects")
report("can happen but don't bother the luatex and context developer team with it.")
report()
report=nil
end
setlocale(a,b)
end
end
local validengines=allocate {
["luatex"]=true,
["luajittex"]=true,
}
local basicengines=allocate {
["luatex"]="luatex",
["texlua"]="luatex",
["texluac"]="luatex",
["luajittex"]="luajittex",
["texluajit"]="luajittex",
}
local luaengines=allocate {
["lua"]=true,
["luajit"]=true,
}
environment.validengines=validengines
environment.basicengines=basicengines
if not arg then
environment.used_as_library=true
elseif luaengines[file.removesuffix(arg[-1])] then
elseif validengines[file.removesuffix(arg[0])] then
if arg[1]=="--luaonly" then
arg[-1]=arg[0]
arg[ 0]=arg[2]
for k=3,#arg do
arg[k-2]=arg[k]
end
remove(arg)
remove(arg)
else
end
local originalzero=file.basename(arg[0])
local specialmapping={ luatools=="base" }
if originalzero~="mtxrun" and originalzero~="mtxrun.lua" then
arg[0]=specialmapping[originalzero] or originalzero
insert(arg,0,"--script")
insert(arg,0,"mtxrun")
end
end
environment.arguments=allocate()
environment.files=allocate()
environment.sortedflags=nil
function environment.initializearguments(arg)
local arguments={}
local files={}
environment.arguments=arguments
environment.files=files
environment.sortedflags=nil
for index=1,#arg do
local argument=arg[index]
if index>0 then
local flag,value=match(argument,"^%-+(.-)=(.-)$")
if flag then
flag=gsub(flag,"^c:","")
arguments[flag]=unquoted(value or "")
else
flag=match(argument,"^%-+(.+)")
if flag then
flag=gsub(flag,"^c:","")
arguments[flag]=true
else
files[#files+1]=argument
end
end
end
end
if not environment.ownname then
if os.selfpath and os.selfname then
environment.ownname=file.addsuffix(file.join(os.selfpath,os.selfname),"lua")
end
end
environment.ownname=file.reslash(environment.ownname or arg[0] or 'unknown.lua')
end
function environment.setargument(name,value)
environment.arguments[name]=value
end
function environment.getargument(name,partial)
local arguments,sortedflags=environment.arguments,environment.sortedflags
if arguments[name] then
return arguments[name]
elseif partial then
if not sortedflags then
sortedflags=allocate(table.sortedkeys(arguments))
for k=1,#sortedflags do
sortedflags[k]="^"..sortedflags[k]
end
environment.sortedflags=sortedflags
end
for k=1,#sortedflags do
local v=sortedflags[k]
if find(name,v) then
return arguments[sub(v,2,#v)]
end
end
end
return nil
end
environment.argument=environment.getargument
function environment.splitarguments(separator)
local done,before,after=false,{},{}
local originalarguments=environment.originalarguments
for k=1,#originalarguments do
local v=originalarguments[k]
if not done and v==separator then
done=true
elseif done then
after[#after+1]=v
else
before[#before+1]=v
end
end
return before,after
end
function environment.reconstructcommandline(arg,noquote)
local resolveprefix=resolvers.resolve
arg=arg or environment.originalarguments
if noquote and #arg==1 then
return unquoted(resolveprefix and resolveprefix(arg[1]) or arg[1])
elseif #arg>0 then
local result={}
for i=1,#arg do
result[i]=optionalquoted(resolveprefix and resolveprefix(arg[i]) or resolveprefix)
end
return concat(result," ")
else
return ""
end
end
function environment.relativepath(path,root)
if not path then
path=""
end
if not file.is_rootbased_path(path) then
if not root then
root=file.pathpart(environment.ownscript or environment.ownname or ".")
end
if root=="" then
root="."
end
path=root.."/"..path
end
return file.collapsepath(path,true)
end
if arg then
local newarg,instring={},false
for index=1,#arg do
local argument=arg[index]
if find(argument,"^\"") then
if find(argument,"\"$") then
newarg[#newarg+1]=gsub(argument,"^\"(.-)\"$","%1")
instring=false
else
newarg[#newarg+1]=gsub(argument,"^\"","")
instring=true
end
elseif find(argument,"\"$") then
if instring then
newarg[#newarg]=newarg[#newarg].." "..gsub(argument,"\"$","")
instring=false
else
newarg[#newarg+1]=argument
end
elseif instring then
newarg[#newarg]=newarg[#newarg].." "..argument
else
newarg[#newarg+1]=argument
end
end
for i=1,-5,-1 do
newarg[i]=arg[i]
end
environment.initializearguments(newarg)
environment.originalarguments=mark(newarg)
environment.rawarguments=mark(arg)
arg={}
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["luat-env"] = package.loaded["luat-env"] or true
-- original size: 6134, stripped down to: 4118
if not modules then modules={} end modules ['luat-env']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local rawset,rawget,loadfile=rawset,rawget,loadfile
local gsub=string.gsub
local trace_locating=false trackers.register("resolvers.locating",function(v) trace_locating=v end)
local report_lua=logs.reporter("resolvers","lua")
local luautilities=utilities.lua
local luasuffixes=luautilities.suffixes
local texgettoks=tex and tex.gettoks
environment=environment or {}
local environment=environment
local mt={
__index=function(_,k)
if k=="version" then
local version=texgettoks and texgettoks("contextversiontoks")
if version and version~="" then
rawset(environment,"version",version)
return version
else
return "unknown"
end
elseif k=="kind" then
local kind=texgettoks and texgettoks("contextkindtoks")
if kind and kind~="" then
rawset(environment,"kind",kind)
return kind
else
return "unknown"
end
elseif k=="jobname" or k=="formatname" then
local name=tex and tex[k]
if name or name=="" then
rawset(environment,k,name)
return name
else
return "unknown"
end
elseif k=="outputfilename" then
local name=environment.jobname
rawset(environment,k,name)
return name
end
end
}
setmetatable(environment,mt)
function environment.texfile(filename)
return resolvers.findfile(filename,'tex')
end
function environment.luafile(filename)
local resolved=resolvers.findfile(filename,'tex') or ""
if resolved~="" then
return resolved
end
resolved=resolvers.findfile(filename,'texmfscripts') or ""
if resolved~="" then
return resolved
end
return resolvers.findfile(filename,'luatexlibs') or ""
end
local stripindeed=false directives.register("system.compile.strip",function(v) stripindeed=v end)
local function strippable(filename)
if stripindeed then
local modu=modules[file.nameonly(filename)]
return modu and modu.dataonly
else
return false
end
end
function environment.luafilechunk(filename,silent,macros)
filename=file.replacesuffix(filename,"lua")
local fullname=environment.luafile(filename)
if fullname and fullname~="" then
local data=luautilities.loadedluacode(fullname,strippable,filename,macros)
if not silent then
report_lua("loading file %a %s",fullname,not data and "failed" or "succeeded")
end
return data
else
if not silent then
report_lua("unknown file %a",filename)
end
return nil
end
end
function environment.loadluafile(filename,version)
local lucname,luaname,chunk
local basename=file.removesuffix(filename)
if basename==filename then
luaname=file.addsuffix(basename,luasuffixes.lua)
lucname=file.addsuffix(basename,luasuffixes.luc)
else
luaname=filename
lucname=nil
end
local fullname=(lucname and environment.luafile(lucname)) or ""
if fullname~="" then
if trace_locating then
report_lua("loading %a",fullname)
end
chunk=loadfile(fullname)
end
if chunk then
chunk()
if version then
local v=version
if modules and modules[filename] then
v=modules[filename].version
elseif versions and versions[filename] then
v=versions[filename]
end
if v==version then
return true
else
if trace_locating then
report_lua("version mismatch for %a, lua version %a, luc version %a",filename,v,version)
end
environment.loadluafile(filename)
end
else
return true
end
end
fullname=(luaname and environment.luafile(luaname)) or ""
if fullname~="" then
if trace_locating then
report_lua("loading %a",fullname)
end
chunk=loadfile(fullname)
if not chunk then
if trace_locating then
report_lua("unknown file %a",filename)
end
else
chunk()
return true
end
end
return false
end
environment.filenames=setmetatable({},{
__index=function(t,k)
local v=environment.files[k]
if v then
return (gsub(v,"%.+$",""))
end
end,
__newindex=function(t,k)
end,
__len=function(t)
return #environment.files
end,
} )
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["util-zip"] = package.loaded["util-zip"] or true
-- original size: 18645, stripped down to: 11291
if not modules then modules={} end modules ['util-zip']={
version=1.001,
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local type,tostring,tonumber=type,tostring,tonumber
local sort=table.sort
local find,format,sub,gsub=string.find,string.format,string.sub,string.gsub
local osdate,ostime=os.date,os.time
local ioopen=io.open
local loaddata,savedata=io.loaddata,io.savedata
local filejoin,isdir,dirname,mkdirs=file.join,lfs.isdir,file.dirname,dir.mkdirs
local files=utilities.files
local openfile=files.open
local closefile=files.close
local readstring=files.readstring
local readcardinal2=files.readcardinal2le
local readcardinal4=files.readcardinal4le
local setposition=files.setposition
local getposition=files.getposition
local band=bit32.band
local rshift=bit32.rshift
local lshift=bit32.lshift
local decompress,calculatecrc
if flate then
decompress=flate.flate_decompress
calculatecrc=flate.update_crc32
else
local zlibdecompress=zlib.decompress
local zlibchecksum=zlib.crc32
decompress=function(source,targetsize)
local target=zlibdecompress(source,-15)
if target then
return target
else
return false,1
end
end
calculatecrc=function(buffer,initial)
return zlibchecksum(initial or 0,buffer)
end
end
local zipfiles={}
utilities.zipfiles=zipfiles
local openzipfile,closezipfile,unzipfile,foundzipfile,getziphash,getziplist do
function openzipfile(name)
return {
name=name,
handle=openfile(name,0),
}
end
local function collect(z)
if not z.list then
local list={}
local hash={}
local position=0
local index=0
local handle=z.handle
while true do
setposition(handle,position)
local signature=readstring(handle,4)
if signature=="PK\3\4" then
local version=readcardinal2(handle)
local flag=readcardinal2(handle)
local method=readcardinal2(handle)
local filetime=readcardinal2(handle)
local filedate=readcardinal2(handle)
local crc32=readcardinal4(handle)
local compressed=readcardinal4(handle)
local uncompressed=readcardinal4(handle)
local namelength=readcardinal2(handle)
local extralength=readcardinal2(handle)
local filename=readstring(handle,namelength)
local descriptor=band(flag,8)~=0
local encrypted=band(flag,1)~=0
local acceptable=method==0 or method==8
local skipped=0
local size=0
if encrypted then
size=readcardinal2(handle)
skipbytes(size)
skipped=skipped+size+2
skipbytes(8)
skipped=skipped+8
size=readcardinal2(handle)
skipbytes(size)
skipped=skipped+size+2
size=readcardinal4(handle)
skipbytes(size)
skipped=skipped+size+4
size=readcardinal2(handle)
skipbytes(size)
skipped=skipped+size+2
end
position=position+30+namelength+extralength+skipped
if descriptor then
setposition(handle,position+compressed)
crc32=readcardinal4(handle)
compressed=readcardinal4(handle)
uncompressed=readcardinal4(handle)
end
if acceptable then
index=index+1
local data={
filename=filename,
index=index,
position=position,
method=method,
compressed=compressed,
uncompressed=uncompressed,
crc32=crc32,
encrypted=encrypted,
}
hash[filename]=data
list[index]=data
else
end
position=position+compressed
else
break
end
z.list=list
z.hash=hash
end
end
end
function getziplist(z)
local list=z.list
if not list then
collect(z)
end
return z.list
end
function getziphash(z)
local hash=z.hash
if not hash then
collect(z)
end
return z.hash
end
function foundzipfile(z,name)
return getziphash(z)[name]
end
function closezipfile(z)
local f=z.handle
if f then
closefile(f)
z.handle=nil
end
end
function unzipfile(z,filename,check)
local hash=z.hash
if not hash then
hash=zipfiles.hash(z)
end
local data=hash[filename]
if not data then
end
if data then
local handle=z.handle
local position=data.position
local compressed=data.compressed
if compressed>0 then
setposition(handle,position)
local result=readstring(handle,compressed)
if data.method==8 then
result=decompress(result,data.uncompressed)
end
if check and data.crc32~=calculatecrc(result) then
print("checksum mismatch")
return ""
end
return result
else
return ""
end
end
end
zipfiles.open=openzipfile
zipfiles.close=closezipfile
zipfiles.unzip=unzipfile
zipfiles.hash=getziphash
zipfiles.list=getziplist
zipfiles.found=foundzipfile
end
if flate then do
local writecardinal1=files.writebyte
local writecardinal2=files.writecardinal2le
local writecardinal4=files.writecardinal4le
local logwriter=logs.writer
local globpattern=dir.globpattern
local compress=flate.flate_compress
local checksum=flate.update_crc32
local function fromdostime(dostime,dosdate)
return ostime {
year=rshift(dosdate,9)+1980,
month=band(rshift(dosdate,5),0x0F),
day=band((dosdate ),0x1F),
hour=band(rshift(dostime,11) ),
min=band(rshift(dostime,5),0x3F),
sec=band((dostime ),0x1F),
}
end
local function todostime(time)
local t=osdate("*t",time)
return
lshift(t.year-1980,9)+lshift(t.month,5)+t.day,
lshift(t.hour,11)+lshift(t.min,5)+rshift(t.sec,1)
end
local function openzip(filename,level,comment,verbose)
local f=ioopen(filename,"wb")
if f then
return {
filename=filename,
handle=f,
list={},
level=tonumber(level) or 3,
comment=tostring(comment),
verbose=verbose,
uncompressed=0,
compressed=0,
}
end
end
local function writezip(z,name,data,level,time)
local f=z.handle
local list=z.list
local level=tonumber(level) or z.level or 3
local method=8
local zipped=compress(data,level)
local checksum=checksum(data)
local verbose=z.verbose
if not zipped then
method=0
zipped=data
end
local start=f:seek()
local compressed=#zipped
local uncompressed=#data
z.compressed=z.compressed+compressed
z.uncompressed=z.uncompressed+uncompressed
if verbose then
local pct=100*compressed/uncompressed
if pct>=100 then
logwriter(format("%10i %s",uncompressed,name))
else
logwriter(format("%10i %02.1f %s",uncompressed,pct,name))
end
end
f:write("\x50\x4b\x03\x04")
writecardinal2(f,0)
writecardinal2(f,0)
writecardinal2(f,method)
writecardinal2(f,0)
writecardinal2(f,0)
writecardinal4(f,checksum)
writecardinal4(f,compressed)
writecardinal4(f,uncompressed)
writecardinal2(f,#name)
writecardinal2(f,0)
f:write(name)
f:write(zipped)
list[#list+1]={ #zipped,#data,name,checksum,start,time or 0 }
end
local function closezip(z)
local f=z.handle
local list=z.list
local comment=z.comment
local verbose=z.verbose
local count=#list
local start=f:seek()
for i=1,count do
local l=list[i]
local compressed=l[1]
local uncompressed=l[2]
local name=l[3]
local checksum=l[4]
local start=l[5]
local time=l[6]
local date,time=todostime(time)
f:write('\x50\x4b\x01\x02')
writecardinal2(f,0)
writecardinal2(f,0)
writecardinal2(f,0)
writecardinal2(f,8)
writecardinal2(f,time)
writecardinal2(f,date)
writecardinal4(f,checksum)
writecardinal4(f,compressed)
writecardinal4(f,uncompressed)
writecardinal2(f,#name)
writecardinal2(f,0)
writecardinal2(f,0)
writecardinal2(f,0)
writecardinal2(f,0)
writecardinal4(f,0)
writecardinal4(f,start)
f:write(name)
end
local stop=f:seek()
local size=stop-start
f:write('\x50\x4b\x05\x06')
writecardinal2(f,0)
writecardinal2(f,0)
writecardinal2(f,count)
writecardinal2(f,count)
writecardinal4(f,size)
writecardinal4(f,start)
if type(comment)=="string" and comment~="" then
writecardinal2(f,#comment)
f:write(comment)
else
writecardinal2(f,0)
end
if verbose then
local compressed=z.compressed
local uncompressed=z.uncompressed
local filename=z.filename
local pct=100*compressed/uncompressed
logwriter("")
if pct>=100 then
logwriter(format("%10i %s",uncompressed,filename))
else
logwriter(format("%10i %02.1f %s",uncompressed,pct,filename))
end
end
f:close()
end
local function zipdir(zipname,path,level,verbose)
if type(zipname)=="table" then
verbose=zipname.verbose
level=zipname.level
path=zipname.path
zipname=zipname.zipname
end
if not zipname or zipname=="" then
return
end
if not path or path=="" then
path="."
end
if not isdir(path) then
return
end
path=gsub(path,"\\+","/")
path=gsub(path,"/+","/")
local list={}
local count=0
globpattern(path,"",true,function(name,size,time)
count=count+1
list[count]={ name,time }
end)
sort(list,function(a,b)
return a[1]<b[1]
end)
local zipf=openzip(zipname,level,comment,verbose)
if zipf then
local p=#path+2
for i=1,count do
local li=list[i]
local name=li[1]
local time=li[2]
local data=loaddata(name)
local name=sub(name,p,#name)
writezip(zipf,name,data,level,time,verbose)
end
closezip(zipf)
end
end
local function unzipdir(zipname,path,verbose)
if type(zipname)=="table" then
verbose=zipname.verbose
path=zipname.path
zipname=zipname.zipname
end
if not zipname or zipname=="" then
return
end
if not path or path=="" then
path="."
end
local z=openzipfile(zipname)
if z then
local list=getziplist(z)
if list then
local total=0
local count=#list
local step=number.idiv(count,10)
local done=0
for i=1,count do
local l=list[i]
local n=l.filename
local d=unzipfile(z,n)
local p=filejoin(path,n)
if mkdirs(dirname(p)) then
if verbose=="steps" then
total=total+#d
done=done+1
if done>=step then
done=0
logwriter(format("%4i files of %4i done, %10i bytes",i,count,total))
end
elseif verbose then
logwriter(n)
end
savedata(p,d)
end
end
if verbose=="steps" then
logwriter(format("%4i files of %4i done, %10i bytes",count,count,total))
end
closezipfile(z)
return true
else
closezipfile(z)
end
end
end
zipfiles.zipdir=zipdir
zipfiles.unzipdir=unzipdir
end end
if flate then
local streams=utilities.streams
local openfile=streams.open
local closestream=streams.close
local setposition=streams.setposition
local getsize=streams.size
local readcardinal4=streams.readcardinal4le
local getstring=streams.getstring
local decompress=flate.gz_decompress
function zipfiles.gunzipfile(filename)
local strm=openfile(filename)
if strm then
setposition(strm,getsize(strm)-4+1)
local size=readcardinal4(strm)
local data=decompress(getstring(strm),size)
closestream(strm)
return data
end
end
elseif gzip then
local openfile=gzip.open
function zipfiles.gunzipfile(filename)
local g=openfile(filename,"rb")
if g then
local d=g:read("*a")
d:close()
return d
end
end
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["lxml-tab"] = package.loaded["lxml-tab"] or true
-- original size: 60383, stripped down to: 35698
if not modules then modules={} end modules ['lxml-tab']={
version=1.001,
comment="this module is the basis for the lxml-* ones",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local trace_entities=false trackers.register("xml.entities",function(v) trace_entities=v end)
local report_xml=logs and logs.reporter("xml","core") or function(...) print(string.format(...)) end
if lpeg.setmaxstack then lpeg.setmaxstack(1000) end
xml=xml or {}
local xml=xml
local concat,remove,insert=table.concat,table.remove,table.insert
local type,next,setmetatable,getmetatable,tonumber,rawset,select=type,next,setmetatable,getmetatable,tonumber,rawset,select
local lower,find,match,gsub=string.lower,string.find,string.match,string.gsub
local sort=table.sort
local utfchar=utf.char
local lpegmatch,lpegpatterns=lpeg.match,lpeg.patterns
local P,S,R,C,V,C,Cs=lpeg.P,lpeg.S,lpeg.R,lpeg.C,lpeg.V,lpeg.C,lpeg.Cs
local formatters=string.formatters
do
xml.xmlns=xml.xmlns or {}
local check=P(false)
local parse=check
function xml.registerns(namespace,pattern)
check=check+C(P(lower(pattern)))/namespace
parse=P { P(check)+1*V(1) }
end
function xml.checkns(namespace,url)
local ns=lpegmatch(parse,lower(url))
if ns and namespace~=ns then
xml.xmlns[namespace]=ns
end
end
function xml.resolvens(url)
return lpegmatch(parse,lower(url)) or ""
end
end
local nsremap,resolvens=xml.xmlns,xml.resolvens
local stack,level,top,at,xmlnms,errorstr
local entities,parameters
local strip,utfize,resolve,cleanup,resolve_predefined,unify_predefined
local dcache,hcache,acache
local mt,dt,nt
local currentfilename,currentline,linenumbers
local grammar_parsed_text_one
local grammar_parsed_text_two
local grammar_unparsed_text
local handle_hex_entity
local handle_dec_entity
local handle_any_entity_dtd
local handle_any_entity_text
local function preparexmlstate(settings)
if settings then
linenumbers=settings.linenumbers
stack={}
level=0
top={}
at={}
mt={}
dt={}
nt=0
xmlns={}
errorstr=nil
strip=settings.strip_cm_and_dt
utfize=settings.utfize_entities
resolve=settings.resolve_entities
resolve_predefined=settings.resolve_predefined_entities
unify_predefined=settings.unify_predefined_entities
cleanup=settings.text_cleanup
entities=settings.entities or {}
currentfilename=settings.currentresource
currentline=1
parameters={}
reported_at_errors={}
dcache={}
hcache={}
acache={}
if utfize==nil then
settings.utfize_entities=true
utfize=true
end
if resolve_predefined==nil then
settings.resolve_predefined_entities=true
resolve_predefined=true
end
else
linenumbers=false
stack=nil
level=nil
top=nil
at=nil
mt=nil
dt=nil
nt=nil
xmlns=nil
errorstr=nil
strip=nil
utfize=nil
resolve=nil
resolve_predefined=nil
unify_predefined=nil
cleanup=nil
entities=nil
parameters=nil
reported_at_errors=nil
dcache=nil
hcache=nil
acache=nil
currentfilename=nil
currentline=1
end
end
local function initialize_mt(root)
mt={ __index=root }
end
function xml.setproperty(root,k,v)
getmetatable(root).__index[k]=v
end
function xml.checkerror(top,toclose)
return ""
end
local checkns=xml.checkns
local function add_attribute(namespace,tag,value)
if cleanup and value~="" then
value=cleanup(value)
end
if tag=="xmlns" then
xmlns[#xmlns+1]=resolvens(value)
at[tag]=value
elseif namespace=="" then
at[tag]=value
elseif namespace=="xmlns" then
checkns(tag,value)
at["xmlns:"..tag]=value
else
at[namespace..":"..tag]=value
end
end
local function add_empty(spacing,namespace,tag)
if spacing~="" then
nt=nt+1
dt[nt]=spacing
end
local resolved=namespace=="" and xmlns[#xmlns] or nsremap[namespace] or namespace
top=stack[level]
dt=top.dt
nt=#dt+1
local t=linenumbers and {
ns=namespace or "",
rn=resolved,
tg=tag,
at=at,
dt={},
ni=nt,
cf=currentfilename,
cl=currentline,
__p__=top,
} or {
ns=namespace or "",
rn=resolved,
tg=tag,
at=at,
dt={},
ni=nt,
__p__=top,
}
dt[nt]=t
setmetatable(t,mt)
if at.xmlns then
remove(xmlns)
end
at={}
end
local function add_begin(spacing,namespace,tag)
if spacing~="" then
nt=nt+1
dt[nt]=spacing
end
local resolved=namespace=="" and xmlns[#xmlns] or nsremap[namespace] or namespace
dt={}
top=linenumbers and {
ns=namespace or "",
rn=resolved,
tg=tag,
at=at,
dt=dt,
ni=nil,
cf=currentfilename,
cl=currentline,
__p__=stack[level],
} or {
ns=namespace or "",
rn=resolved,
tg=tag,
at=at,
dt=dt,
ni=nil,
__p__=stack[level],
}
setmetatable(top,mt)
nt=0
level=level+1
stack[level]=top
at={}
end
local function add_end(spacing,namespace,tag)
if spacing~="" then
nt=nt+1
dt[nt]=spacing
end
local toclose=stack[level]
level=level-1
top=stack[level]
if level<1 then
errorstr=formatters["unable to close %s %s"](tag,xml.checkerror(top,toclose) or "")
report_xml(errorstr)
elseif toclose.tg~=tag then
errorstr=formatters["unable to close %s with %s %s"](toclose.tg,tag,xml.checkerror(top,toclose) or "")
report_xml(errorstr)
end
dt=top.dt
nt=#dt+1
dt[nt]=toclose
toclose.ni=nt
if toclose.at.xmlns then
remove(xmlns)
end
end
local function add_text(text)
if text=="" then
return
end
if cleanup then
if nt>0 then
local s=dt[nt]
if type(s)=="string" then
dt[nt]=s..cleanup(text)
else
nt=nt+1
dt[nt]=cleanup(text)
end
else
nt=1
dt[1]=cleanup(text)
end
else
if nt>0 then
local s=dt[nt]
if type(s)=="string" then
dt[nt]=s..text
else
nt=nt+1
dt[nt]=text
end
else
nt=1
dt[1]=text
end
end
end
local function add_special(what,spacing,text)
if spacing~="" then
nt=nt+1
dt[nt]=spacing
end
if strip and (what=="@cm@" or what=="@dt@") then
else
nt=nt+1
dt[nt]=linenumbers and {
special=true,
ns="",
tg=what,
ni=nil,
dt={ text },
cf=currentfilename,
cl=currentline,
} or {
special=true,
ns="",
tg=what,
ni=nil,
dt={ text },
}
end
end
local function set_message(txt)
errorstr="garbage at the end of the file: "..gsub(txt,"([ \n\r\t]*)","")
end
local function attribute_value_error(str)
if not reported_at_errors[str] then
report_xml("invalid attribute value %a",str)
reported_at_errors[str]=true
at._error_=str
end
return str
end
local function attribute_specification_error(str)
if not reported_at_errors[str] then
report_xml("invalid attribute specification %a",str)
reported_at_errors[str]=true
at._error_=str
end
return str
end
do
local badentity="&"
xml.placeholders={
unknown_dec_entity=function(str) return str=="" and badentity or formatters["&%s;"](str) end,
unknown_hex_entity=function(str) return formatters["&#x%s;"](str) end,
unknown_any_entity=function(str) return formatters["&#x%s;"](str) end,
}
local function fromhex(s)
local n=tonumber(s,16)
if n then
return utfchar(n)
else
return formatters["h:%s"](s),true
end
end
local function fromdec(s)
local n=tonumber(s)
if n then
return utfchar(n)
else
return formatters["d:%s"](s),true
end
end
local p_rest=(1-P(";"))^0
local p_many=P(1)^0
local parsedentity=P("&#")*(P("x")*(p_rest/fromhex)+(p_rest/fromdec))*P(";")*P(-1)+P ("#")*(P("x")*(p_many/fromhex)+(p_many/fromdec))
xml.parsedentitylpeg=parsedentity
local predefined_unified={
[38]="&",
[42]=""",
[47]="'",
[74]="<",
[76]=">",
}
local predefined_simplified={
[38]="&",amp="&",
[42]='"',quot='"',
[47]="'",apos="'",
[74]="<",lt="<",
[76]=">",gt=">",
}
local nofprivates=0xF0000
local privates_u={
[ [[&]] ]="&",
[ [["]] ]=""",
[ [[']] ]="'",
[ [[<]] ]="<",
[ [[>]] ]=">",
}
local privates_p={
}
local privates_s={
[ [["]] ]="&U+22;",
[ [[#]] ]="&U+23;",
[ [[$]] ]="&U+24;",
[ [[%]] ]="&U+25;",
[ [[&]] ]="&U+26;",
[ [[']] ]="&U+27;",
[ [[<]] ]="&U+3C;",
[ [[>]] ]="&U+3E;",
[ [[\]] ]="&U+5C;",
[ [[{]] ]="&U+7B;",
[ [[|]] ]="&U+7C;",
[ [[}]] ]="&U+7D;",
[ [[~]] ]="&U+7E;",
}
local privates_x={
[ [["]] ]="&U+22;",
[ [[#]] ]="&U+23;",
[ [[$]] ]="&U+24;",
[ [[%]] ]="&U+25;",
[ [[']] ]="&U+27;",
[ [[\]] ]="&U+5C;",
[ [[{]] ]="&U+7B;",
[ [[|]] ]="&U+7C;",
[ [[}]] ]="&U+7D;",
[ [[~]] ]="&U+7E;",
}
local privates_n={
}
local escaped=utf.remapper(privates_u,"dynamic")
local unprivatized=utf.remapper(privates_p,"dynamic")
local unspecialized=utf.remapper(privates_s,"dynamic")
local despecialized=utf.remapper(privates_x,"dynamic")
xml.unprivatized=unprivatized
xml.unspecialized=unspecialized
xml.despecialized=despecialized
xml.escaped=escaped
local function unescaped(s)
local p=privates_n[s]
if not p then
nofprivates=nofprivates+1
p=utfchar(nofprivates)
privates_n[s]=p
s="&"..s..";"
privates_u[p]=s
privates_p[p]=s
privates_s[p]=s
end
return p
end
xml.privatetoken=unescaped
xml.privatecodes=privates_n
xml.specialcodes=privates_s
function xml.addspecialcode(key,value)
privates_s[key]=value or "&"..s..";"
end
handle_hex_entity=function(str)
local h=hcache[str]
if not h then
local n=tonumber(str,16)
h=unify_predefined and predefined_unified[n]
if h then
if trace_entities then
report_xml("utfize, converting hex entity &#x%s; into %a",str,h)
end
elseif utfize then
h=(n and utfchar(n)) or xml.unknown_hex_entity(str) or ""
if not n then
report_xml("utfize, ignoring hex entity &#x%s;",str)
elseif trace_entities then
report_xml("utfize, converting hex entity &#x%s; into %a",str,h)
end
else
if trace_entities then
report_xml("found entity &#x%s;",str)
end
h="&#x"..str..";"
end
hcache[str]=h
end
return h
end
handle_dec_entity=function(str)
local d=dcache[str]
if not d then
local n=tonumber(str)
d=unify_predefined and predefined_unified[n]
if d then
if trace_entities then
report_xml("utfize, converting dec entity &#%s; into %a",str,d)
end
elseif utfize then
d=(n and utfchar(n)) or placeholders.unknown_dec_entity(str) or ""
if not n then
report_xml("utfize, ignoring dec entity &#%s;",str)
elseif trace_entities then
report_xml("utfize, converting dec entity &#%s; into %a",str,d)
end
else
if trace_entities then
report_xml("found entity &#%s;",str)
end
d="&#"..str..";"
end
dcache[str]=d
end
return d
end
handle_any_entity_dtd=function(str)
if resolve then
local a=resolve_predefined and predefined_simplified[str]
if a then
if trace_entities then
report_xml("resolving entity &%s; to predefined %a",str,a)
end
else
if type(resolve)=="function" then
a=resolve(str,entities) or entities[str]
else
a=entities[str]
end
if a then
if type(a)=="function" then
if trace_entities then
report_xml("expanding entity &%s; to function call",str)
end
a=a(str) or ""
end
a=lpegmatch(parsedentity,a) or a
if trace_entities then
report_xml("resolving entity &%s; to internal %a",str,a)
end
else
local unknown_any_entity=placeholders.unknown_any_entity
if unknown_any_entity then
a=unknown_any_entity(str) or ""
end
if a then
if trace_entities then
report_xml("resolving entity &%s; to external %s",str,a)
end
else
if trace_entities then
report_xml("keeping entity &%s;",str)
end
if str=="" then
a=badentity
else
a="&"..str..";"
end
end
end
end
return a
else
local a=acache[str]
if not a then
a=resolve_predefined and predefined_simplified[str]
if a then
acache[str]=a
if trace_entities then
report_xml("entity &%s; becomes %a",str,a)
end
elseif str=="" then
if trace_entities then
report_xml("invalid entity &%s;",str)
end
a=badentity
acache[str]=a
else
if trace_entities then
report_xml("entity &%s; is made private",str)
end
a=unescaped(str)
acache[str]=a
end
end
return a
end
end
handle_any_entity_text=function(str)
if resolve then
local a=resolve_predefined and predefined_simplified[str]
if a then
if trace_entities then
report_xml("resolving entity &%s; to predefined %a",str,a)
end
else
if type(resolve)=="function" then
a=resolve(str,entities) or entities[str]
else
a=entities[str]
end
if a then
if type(a)=="function" then
if trace_entities then
report_xml("expanding entity &%s; to function call",str)
end
a=a(str) or ""
end
a=lpegmatch(grammar_parsed_text_two,a) or a
if type(a)=="number" then
return ""
else
a=lpegmatch(parsedentity,a) or a
if trace_entities then
report_xml("resolving entity &%s; to internal %a",str,a)
end
end
if trace_entities then
report_xml("resolving entity &%s; to internal %a",str,a)
end
else
local unknown_any_entity=placeholders.unknown_any_entity
if unknown_any_entity then
a=unknown_any_entity(str) or ""
end
if a then
if trace_entities then
report_xml("resolving entity &%s; to external %s",str,a)
end
else
if trace_entities then
report_xml("keeping entity &%s;",str)
end
if str=="" then
a=badentity
else
a="&"..str..";"
end
end
end
end
return a
else
local a=acache[str]
if not a then
a=resolve_predefined and predefined_simplified[str]
if a then
acache[str]=a
if trace_entities then
report_xml("entity &%s; becomes %a",str,a)
end
elseif str=="" then
if trace_entities then
report_xml("invalid entity &%s;",str)
end
a=badentity
acache[str]=a
else
if trace_entities then
report_xml("entity &%s; is made private",str)
end
a=unescaped(str)
acache[str]=a
end
end
return a
end
end
local p_rest=(1-P(";"))^1
local spec={
[0x23]="\\Ux{23}",
[0x24]="\\Ux{24}",
[0x25]="\\Ux{25}",
[0x5C]="\\Ux{5C}",
[0x7B]="\\Ux{7B}",
[0x7C]="\\Ux{7C}",
[0x7D]="\\Ux{7D}",
[0x7E]="\\Ux{7E}",
}
local hash=table.setmetatableindex(spec,function(t,k)
local v=utfchar(k)
t[k]=v
return v
end)
local function fromuni(s)
local n=tonumber(s,16)
if n then
return hash[n]
else
return formatters["u:%s"](s),true
end
end
local function fromhex(s)
local n=tonumber(s,16)
if n then
return hash[n]
else
return formatters["h:%s"](s),true
end
end
local function fromdec(s)
local n=tonumber(s)
if n then
return hash[n]
else
return formatters["d:%s"](s),true
end
end
local reparsedentity=P("U+")*(p_rest/fromuni)+P("#")*(
P("x")*(p_rest/fromhex)+p_rest/fromdec
)
local hash=table.setmetatableindex(function(t,k)
local v=utfchar(k)
t[k]=v
return v
end)
local function fromuni(s)
local n=tonumber(s,16)
if n then
return hash[n]
else
return formatters["u:%s"](s),true
end
end
local function fromhex(s)
local n=tonumber(s,16)
if n then
return hash[n]
else
return formatters["h:%s"](s),true
end
end
local function fromdec(s)
local n=tonumber(s)
if n then
return hash[n]
else
return formatters["d:%s"](s),true
end
end
local unescapedentity=P("U+")*(p_rest/fromuni)+P("#")*(
P("x")*(p_rest/fromhex)+p_rest/fromdec
)
xml.reparsedentitylpeg=reparsedentity
xml.unescapedentitylpeg=unescapedentity
end
local escaped=xml.escaped
local unescaped=xml.unescaped
local placeholders=xml.placeholders
local function handle_end_entity(str)
report_xml("error in entity, %a found without ending %a",str,";")
return str
end
local function handle_crap_error(chr)
report_xml("error in parsing, unexpected %a found ",chr)
add_text(chr)
return chr
end
local function handlenewline()
currentline=currentline+1
end
local spacetab=S(' \t')
local space=S(' \r\n\t')
local newline=lpegpatterns.newline/handlenewline
local anything=P(1)
local open=P('<')
local close=P('>')
local squote=S("'")
local dquote=S('"')
local equal=P('=')
local slash=P('/')
local colon=P(':')
local semicolon=P(';')
local ampersand=P('&')
local valid=R('az','AZ','09')+S('_-.')
local name_yes=C(valid^1)*colon*C(valid^1)
local name_nop=C(P(true))*C(valid^1)
local name=name_yes+name_nop
local utfbom=lpegpatterns.utfbom
local spacing=C(space^0)
local space_nl=spacetab+newline
local spacing_nl=Cs((space_nl)^0)
local anything_nl=newline+P(1)
local function weirdentity(k,v)
if trace_entities then
report_xml("registering %s entity %a as %a","weird",k,v)
end
parameters[k]=v
end
local function normalentity(k,v)
if trace_entities then
report_xml("registering %s entity %a as %a","normal",k,v)
end
entities[k]=v
end
local function systementity(k,v,n)
if trace_entities then
report_xml("registering %s entity %a as %a","system",k,v)
end
entities[k]=v
end
local function publicentity(k,v,n)
if trace_entities then
report_xml("registering %s entity %a as %a","public",k,v)
end
entities[k]=v
end
local function entityfile(pattern,k,v,n)
if n then
local okay,data
if resolvers then
okay,data=resolvers.loadbinfile(n)
else
data=io.loaddata(n)
okay=data and data~=""
end
if okay then
if trace_entities then
report_xml("loading public entities %a as %a from %a",k,v,n)
end
lpegmatch(pattern,data)
return
end
end
report_xml("ignoring public entities %a as %a from %a",k,v,n)
end
local function install(spacenewline,spacing,anything)
local anyentitycontent=(1-open-semicolon-space-close-ampersand)^0
local hexentitycontent=R("AF","af","09")^1
local decentitycontent=R("09")^1
local parsedentity=P("#")/""*(
P("x")/""*(hexentitycontent/handle_hex_entity)+(decentitycontent/handle_dec_entity)
)+(anyentitycontent/handle_any_entity_dtd)
local parsedentity_text=P("#")/""*(
P("x")/""*(hexentitycontent/handle_hex_entity)+(decentitycontent/handle_dec_entity)
)+(anyentitycontent/handle_any_entity_text)
local entity=(ampersand/"")*parsedentity*(semicolon/"")+ampersand*(anyentitycontent/handle_end_entity)
local entity_text=(ampersand/"")*parsedentity_text*(semicolon/"")+ampersand*(anyentitycontent/handle_end_entity)
local text_unparsed=Cs((anything-open)^1)
local text_parsed=(Cs((anything-open-ampersand)^1)/add_text+Cs(entity_text)/add_text)^1
local somespace=(spacenewline)^1
local optionalspace=(spacenewline)^0
local value=(squote*Cs((entity+(anything-squote))^0)*squote)+(dquote*Cs((entity+(anything-dquote))^0)*dquote)
local endofattributes=slash*close+close
local whatever=space*name*optionalspace*equal
local wrongvalue=Cs(P(entity+(1-space-endofattributes))^1)/attribute_value_error
local attributevalue=value+wrongvalue
local attribute=(somespace*name*optionalspace*equal*optionalspace*attributevalue)/add_attribute
local attributes=(attribute+somespace^-1*(((anything-endofattributes)^1)/attribute_specification_error))^0
local parsedtext=text_parsed
local unparsedtext=text_unparsed/add_text
local balanced=P { "["*((anything-S"[]")+V(1))^0*"]" }
local emptyelement=(spacing*open*name*attributes*optionalspace*slash*close)/add_empty
local beginelement=(spacing*open*name*attributes*optionalspace*close)/add_begin
local endelement=(spacing*open*slash*name*optionalspace*close)/add_end
local begincomment=open*P("!--")
local endcomment=P("--")*close
local begininstruction=open*P("?")
local endinstruction=P("?")*close
local begincdata=open*P("![CDATA[")
local endcdata=P("]]")*close
local someinstruction=C((anything-endinstruction)^0)
local somecomment=C((anything-endcomment )^0)
local somecdata=C((anything-endcdata )^0)
local begindoctype=open*P("!DOCTYPE")
local enddoctype=close
local beginset=P("[")
local endset=P("]")
local wrdtypename=C((anything-somespace-P(";"))^1)
local doctypename=C((anything-somespace-close)^0)
local elementdoctype=optionalspace*P("<!ELEMENT")*(anything-close)^0*close
local basiccomment=begincomment*((anything-endcomment)^0)*endcomment
local weirdentitytype=P("%")*(somespace*doctypename*somespace*value)/weirdentity
local normalentitytype=(doctypename*somespace*value)/normalentity
local publicentitytype=(doctypename*somespace*P("PUBLIC")*somespace*value)/publicentity
local systementitytype=(doctypename*somespace*P("SYSTEM")*somespace*value*somespace*P("NDATA")*somespace*doctypename)/systementity
local entitydoctype=optionalspace*P("<!ENTITY")*somespace*(systementitytype+publicentitytype+normalentitytype+weirdentitytype)*optionalspace*close
local publicentityfile=(doctypename*somespace*P("PUBLIC")*somespace*value*(somespace*value)^0)/function(...)
entityfile(entitydoctype,...)
end
local function weirdresolve(s)
lpegmatch(entitydoctype,parameters[s])
end
local function normalresolve(s)
lpegmatch(entitydoctype,entities[s])
end
local entityresolve=P("%")*(wrdtypename/weirdresolve )*P(";")+P("&")*(wrdtypename/normalresolve)*P(";")
entitydoctype=entitydoctype+entityresolve
local doctypeset=beginset*optionalspace*P(elementdoctype+entitydoctype+entityresolve+basiccomment+space)^0*optionalspace*endset
local definitiondoctype=doctypename*somespace*doctypeset
local publicdoctype=doctypename*somespace*P("PUBLIC")*somespace*value*somespace*value*somespace*doctypeset
local systemdoctype=doctypename*somespace*P("SYSTEM")*somespace*value*somespace*doctypeset
local simpledoctype=(anything-close)^1
local somedoctype=C((somespace*(
publicentityfile+publicdoctype+systemdoctype+definitiondoctype+simpledoctype)*optionalspace)^0)
local instruction=(spacing*begininstruction*someinstruction*endinstruction)/function(...) add_special("@pi@",...) end
local comment=(spacing*begincomment*somecomment*endcomment )/function(...) add_special("@cm@",...) end
local cdata=(spacing*begincdata*somecdata*endcdata )/function(...) add_special("@cd@",...) end
local doctype=(spacing*begindoctype*somedoctype*enddoctype )/function(...) add_special("@dt@",...) end
local crap_parsed=anything-beginelement-endelement-emptyelement-begininstruction-begincomment-begincdata-ampersand
local crap_unparsed=anything-beginelement-endelement-emptyelement-begininstruction-begincomment-begincdata
local parsedcrap=Cs((crap_parsed^1+entity_text)^1)/handle_crap_error
local parsedcrap=Cs((crap_parsed^1+entity_text)^1)/handle_crap_error
local unparsedcrap=Cs((crap_unparsed )^1)/handle_crap_error
local trailer=space^0*(text_unparsed/set_message)^0
local grammar_parsed_text_one=P { "preamble",
preamble=utfbom^0*instruction^0*(doctype+comment+instruction)^0,
}
local grammar_parsed_text_two=P { "followup",
followup=V("parent")*trailer,
parent=beginelement*V("children")^0*endelement,
children=parsedtext+V("parent")+emptyelement+comment+cdata+instruction+parsedcrap,
}
local grammar_unparsed_text=P { "preamble",
preamble=utfbom^0*instruction^0*(doctype+comment+instruction)^0*V("parent")*trailer,
parent=beginelement*V("children")^0*endelement,
children=unparsedtext+V("parent")+emptyelement+comment+cdata+instruction+unparsedcrap,
}
return grammar_parsed_text_one,grammar_parsed_text_two,grammar_unparsed_text
end
grammar_parsed_text_one_nop,
grammar_parsed_text_two_nop,
grammar_unparsed_text_nop=install(space,spacing,anything)
grammar_parsed_text_one_yes,
grammar_parsed_text_two_yes,
grammar_unparsed_text_yes=install(space_nl,spacing_nl,anything_nl)
local function _xmlconvert_(data,settings,detail)
settings=settings or {}
preparexmlstate(settings)
if settings.linenumbers then
grammar_parsed_text_one=grammar_parsed_text_one_yes
grammar_parsed_text_two=grammar_parsed_text_two_yes
grammar_unparsed_text=grammar_unparsed_text_yes
else
grammar_parsed_text_one=grammar_parsed_text_one_nop
grammar_parsed_text_two=grammar_parsed_text_two_nop
grammar_unparsed_text=grammar_unparsed_text_nop
end
local preprocessor=settings.preprocessor
if data and data~="" and type(preprocessor)=="function" then
data=preprocessor(data,settings) or data
end
if settings.parent_root then
mt=getmetatable(settings.parent_root)
else
initialize_mt(top)
end
level=level+1
stack[level]=top
top.dt={}
dt=top.dt
nt=0
if not data or data=="" then
errorstr="empty xml file"
elseif data==true then
errorstr=detail or "problematic xml file"
elseif utfize or resolve then
local m=lpegmatch(grammar_parsed_text_one,data)
if m then
m=lpegmatch(grammar_parsed_text_two,data,m)
end
if m then
else
errorstr="invalid xml file - parsed text"
end
elseif type(data)=="string" then
if lpegmatch(grammar_unparsed_text,data) then
errorstr=""
else
errorstr="invalid xml file - unparsed text"
end
else
errorstr="invalid xml file - no text at all"
end
local result
if errorstr and errorstr~="" then
result={ dt={ { ns="",tg="error",dt={ errorstr },at={},er=true } } }
setmetatable(result,mt)
setmetatable(result.dt[1],mt)
setmetatable(stack,mt)
local errorhandler=settings.error_handler
if errorhandler==false then
else
errorhandler=errorhandler or xml.errorhandler
if errorhandler then
local currentresource=settings.currentresource
if currentresource and currentresource~="" then
xml.errorhandler(formatters["load error in [%s]: %s"](currentresource,errorstr),currentresource)
else
xml.errorhandler(formatters["load error: %s"](errorstr))
end
end
end
else
result=stack[1]
end
if not settings.no_root then
result={ special=true,ns="",tg='@rt@',dt=result.dt,at={},entities=entities,settings=settings }
setmetatable(result,mt)
local rdt=result.dt
for k=1,#rdt do
local v=rdt[k]
if type(v)=="table" and not v.special then
result.ri=k
v.__p__=result
break
end
end
end
if errorstr and errorstr~="" then
result.error=true
else
errorstr=nil
end
result.statistics={
errormessage=errorstr,
entities={
decimals=dcache,
hexadecimals=hcache,
names=acache,
intermediates=parameters,
}
}
preparexmlstate()
return result
end
local function xmlconvert(data,settings)
local ok,result=pcall(function() return _xmlconvert_(data,settings) end)
if ok then
return result
elseif type(result)=="string" then
return _xmlconvert_(true,settings,result)
else
return _xmlconvert_(true,settings)
end
end
xml.convert=xmlconvert
function xml.inheritedconvert(data,xmldata)
local settings=xmldata.settings
if settings then
settings.parent_root=xmldata
end
local xc=xmlconvert(data,settings)
return xc
end
function xml.is_valid(root)
return root and root.dt and root.dt[1] and type(root.dt[1])=="table" and not root.dt[1].er
end
function xml.package(tag,attributes,data)
local ns,tg=match(tag,"^(.-):?([^:]+)$")
local t={ ns=ns,tg=tg,dt=data or "",at=attributes or {} }
setmetatable(t,mt)
return t
end
function xml.is_valid(root)
return root and not root.error
end
xml.errorhandler=report_xml
function xml.load(filename,settings)
local data=""
if type(filename)=="string" then
local f=io.open(filename,'r')
if f then
data=f:read("*all")
f:close()
end
elseif filename then
data=filename:read("*all")
end
if settings then
settings.currentresource=filename
local result=xmlconvert(data,settings)
settings.currentresource=nil
return result
else
return xmlconvert(data,{ currentresource=filename })
end
end
local no_root={ no_root=true }
function xml.toxml(data)
if type(data)=="string" then
local root={ xmlconvert(data,no_root) }
return (#root>1 and root) or root[1]
else
return data
end
end
local function copy(old,p)
if old then
local new={}
for k,v in next,old do
local t=type(v)=="table"
if k=="at" then
local t={}
for k,v in next,v do
t[k]=v
end
new[k]=t
elseif k=="dt" then
v.__p__=nil
v=copy(v,new)
new[k]=v
v.__p__=p
else
new[k]=v
end
end
local mt=getmetatable(old)
if mt then
setmetatable(new,mt)
end
return new
else
return {}
end
end
xml.copy=copy
function xml.checkbom(root)
if root.ri then
local dt=root.dt
for k=1,#dt do
local v=dt[k]
if type(v)=="table" and v.special and v.tg=="@pi@" and find(v.dt[1],"xml.*version=") then
return
end
end
insert(dt,1,{ special=true,ns="",tg="@pi@",dt={ "xml version='1.0' standalone='yes'" } } )
insert(dt,2,"\n" )
end
end
local f_attribute=formatters['%s=%q']
local function verbose_element(e,handlers,escape)
local handle=handlers.handle
local serialize=handlers.serialize
local ens,etg,eat,edt,ern=e.ns,e.tg,e.at,e.dt,e.rn
local ats=eat and next(eat) and {}
if ats then
local n=0
for k in next,eat do
n=n+1
ats[n]=k
end
if n==1 then
local k=ats[1]
ats=f_attribute(k,escaped(eat[k]))
else
sort(ats)
for i=1,n do
local k=ats[i]
ats[i]=f_attribute(k,escaped(eat[k]))
end
ats=concat(ats," ")
end
end
if ern and trace_entities and ern~=ens then
ens=ern
end
local n=edt and #edt
if ens~="" then
if n and n>0 then
if ats then
handle("<",ens,":",etg," ",ats,">")
else
handle("<",ens,":",etg,">")
end
for i=1,n do
local e=edt[i]
if type(e)=="string" then
handle(escaped(e))
else
serialize(e,handlers)
end
end
handle("</",ens,":",etg,">")
else
if ats then
handle("<",ens,":",etg," ",ats,"/>")
else
handle("<",ens,":",etg,"/>")
end
end
else
if n and n>0 then
if ats then
handle("<",etg," ",ats,">")
else
handle("<",etg,">")
end
for i=1,n do
local e=edt[i]
if type(e)=="string" then
handle(escaped(e))
else
serialize(e,handlers)
end
end
handle("</",etg,">")
else
if ats then
handle("<",etg," ",ats,"/>")
else
handle("<",etg,"/>")
end
end
end
end
local function verbose_pi(e,handlers)
handlers.handle("<?",e.dt[1],"?>")
end
local function verbose_comment(e,handlers)
handlers.handle("<!--",e.dt[1],"-->")
end
local function verbose_cdata(e,handlers)
handlers.handle("<![CDATA[",e.dt[1],"]]>")
end
local function verbose_doctype(e,handlers)
handlers.handle("<!DOCTYPE",e.dt[1],">")
end
local function verbose_root(e,handlers)
handlers.serialize(e.dt,handlers)
end
local function verbose_text(e,handlers)
handlers.handle(escaped(e))
end
local function verbose_document(e,handlers)
local serialize=handlers.serialize
local functions=handlers.functions
for i=1,#e do
local ei=e[i]
if type(ei)=="string" then
functions["@tx@"](ei,handlers)
else
serialize(ei,handlers)
end
end
end
local function serialize(e,handlers,...)
if e then
local initialize=handlers.initialize
local finalize=handlers.finalize
local functions=handlers.functions
if initialize then
local state=initialize(...)
if not state==true then
return state
end
end
local etg=e.tg
if etg then
(functions[etg] or functions["@el@"])(e,handlers)
else
functions["@dc@"](e,handlers)
end
if finalize then
return finalize()
end
end
end
local function xserialize(e,handlers)
if e then
local functions=handlers.functions
local etg=e.tg
if etg then
(functions[etg] or functions["@el@"])(e,handlers)
else
functions["@dc@"](e,handlers)
end
end
end
local handlers={}
local function newhandlers(settings)
local t=table.copy(handlers[settings and settings.parent or "verbose"] or {})
if settings then
for k,v in next,settings do
if type(v)=="table" then
local tk=t[k] if not tk then tk={} t[k]=tk end
for kk,vv in next,v do
tk[kk]=vv
end
else
t[k]=v
end
end
if settings.name then
handlers[settings.name]=t
end
end
utilities.storage.mark(t)
return t
end
local nofunction=function() end
function xml.sethandlersfunction(handler,name,fnc)
handler.functions[name]=fnc or nofunction
end
function xml.gethandlersfunction(handler,name)
return handler.functions[name]
end
function xml.gethandlers(name)
return handlers[name]
end
newhandlers {
name="verbose",
initialize=false,
finalize=false,
serialize=xserialize,
handle=print,
functions={
["@dc@"]=verbose_document,
["@dt@"]=verbose_doctype,
["@rt@"]=verbose_root,
["@el@"]=verbose_element,
["@pi@"]=verbose_pi,
["@cm@"]=verbose_comment,
["@cd@"]=verbose_cdata,
["@tx@"]=verbose_text,
}
}
local result
local xmlfilehandler=newhandlers {
name="file",
initialize=function(name)
result=io.open(name,"wb")
return result
end,
finalize=function()
result:close()
return true
end,
handle=function(...)
result:write(...)
end,
}
function xml.save(root,name)
serialize(root,xmlfilehandler,name)
end
local result,r,threshold={},0,512
local xmlstringhandler=newhandlers {
name="string",
initialize=function()
r=0
return result
end,
finalize=function()
local done=concat(result,"",1,r)
r=0
if r>threshold then
result={}
end
return done
end,
handle=function(...)
for i=1,select("#",...) do
r=r+1
result[r]=select(i,...)
end
end,
}
local function xmltostring(root)
if not root then
return ""
elseif type(root)=="string" then
return root
else
return serialize(root,xmlstringhandler) or ""
end
end
local function __tostring(root)
return (root and xmltostring(root)) or ""
end
initialize_mt=function(root)
mt={ __tostring=__tostring,__index=root }
end
xml.defaulthandlers=handlers
xml.newhandlers=newhandlers
xml.serialize=serialize
xml.tostring=xmltostring
local function xmlstring(e,handle)
if not handle or (e.special and e.tg~="@rt@") then
elseif e.tg then
local edt=e.dt
if edt then
for i=1,#edt do
xmlstring(edt[i],handle)
end
end
else
handle(e)
end
end
xml.string=xmlstring
function xml.settings(e)
while e do
local s=e.settings
if s then
return s
else
e=e.__p__
end
end
return nil
end
function xml.root(e)
local r=e
while e do
e=e.__p__
if e then
r=e
end
end
return r
end
function xml.parent(root)
return root.__p__
end
function xml.body(root)
return root.ri and root.dt[root.ri] or root
end
function xml.name(root)
if not root then
return ""
end
local ns=root.ns
local tg=root.tg
if ns=="" then
return tg
else
return ns..":"..tg
end
end
function xml.erase(dt,k)
if dt then
if k then
dt[k]=""
else for k=1,#dt do
dt[1]={ "" }
end end
end
end
function xml.assign(dt,k,root)
if dt and k then
dt[k]=type(root)=="table" and xml.body(root) or root
return dt[k]
else
return xml.body(root)
end
end
function xml.tocdata(e,wrapper)
local whatever=type(e)=="table" and xmltostring(e.dt) or e or ""
if wrapper then
whatever=formatters["<%s>%s</%s>"](wrapper,whatever,wrapper)
end
local t={ special=true,ns="",tg="@cd@",at={},rn="",dt={ whatever },__p__=e }
setmetatable(t,getmetatable(e))
e.dt={ t }
end
function xml.makestandalone(root)
if root.ri then
local dt=root.dt
for k=1,#dt do
local v=dt[k]
if type(v)=="table" and v.special and v.tg=="@pi@" then
local txt=v.dt[1]
if find(txt,"xml.*version=") then
v.dt[1]=txt.." standalone='yes'"
break
end
end
end
end
return root
end
function xml.kind(e)
local dt=e and e.dt
if dt then
local n=#dt
if n==1 then
local d=dt[1]
if d.special then
local tg=d.tg
if tg=="@cd@" then
return "cdata"
elseif tg=="@cm" then
return "comment"
elseif tg=="@pi@" then
return "instruction"
elseif tg=="@dt@" then
return "declaration"
end
elseif type(d)=="string" then
return "text"
end
return "element"
elseif n>0 then
return "mixed"
end
end
return "empty"
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["lxml-lpt"] = package.loaded["lxml-lpt"] or true
-- original size: 55145, stripped down to: 30992
if not modules then modules={} end modules ['lxml-lpt']={
version=1.001,
comment="this module is the basis for the lxml-* ones",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local concat,remove,insert=table.concat,table.remove,table.insert
local type,next,tonumber,tostring,setmetatable,load,select=type,next,tonumber,tostring,setmetatable,load,select
local format,upper,lower,gmatch,gsub,find,rep=string.format,string.upper,string.lower,string.gmatch,string.gsub,string.find,string.rep
local lpegmatch,lpegpatterns=lpeg.match,lpeg.patterns
local setmetatableindex=table.setmetatableindex
local formatters=string.formatters
local trace_lpath=false
local trace_lparse=false
local trace_lprofile=false
local report_lpath=logs.reporter("xml","lpath")
if trackers then
trackers.register("xml.path",function(v)
trace_lpath=v
end)
trackers.register("xml.parse",function(v)
trace_lparse=v
end)
trackers.register("xml.profile",function(v)
trace_lpath=v
trace_lparse=v
trace_lprofile=v
end)
end
local xml=xml
local lpathcalls=0 function xml.lpathcalls () return lpathcalls end
local lpathcached=0 function xml.lpathcached() return lpathcached end
xml.functions=xml.functions or {}
local functions=xml.functions
xml.expressions=xml.expressions or {}
local expressions=xml.expressions
xml.finalizers=xml.finalizers or {}
local finalizers=xml.finalizers
xml.specialhandler=xml.specialhandler or {}
local specialhandler=xml.specialhandler
lpegpatterns.xml=lpegpatterns.xml or {}
local xmlpatterns=lpegpatterns.xml
finalizers.xml=finalizers.xml or {}
finalizers.tex=finalizers.tex or {}
local function fallback (t,name)
local fn=finalizers[name]
if fn then
t[name]=fn
else
report_lpath("unknown sub finalizer %a",name)
fn=function() end
end
return fn
end
setmetatableindex(finalizers.xml,fallback)
setmetatableindex(finalizers.tex,fallback)
xml.defaultprotocol="xml"
local apply_axis={}
apply_axis['root']=function(list)
local collected={}
for l=1,#list do
local ll=list[l]
local rt=ll
while ll do
ll=ll.__p__
if ll then
rt=ll
end
end
collected[l]=rt
end
return collected
end
apply_axis['self']=function(list)
return list
end
apply_axis['child']=function(list)
local collected={}
local c=0
for l=1,#list do
local ll=list[l]
local dt=ll.dt
if dt then
local n=#dt
if n==0 then
ll.en=0
elseif n==1 then
local dk=dt[1]
if dk.tg then
c=c+1
collected[c]=dk
dk.ni=1
dk.ei=1
ll.en=1
end
else
local en=0
for k=1,#dt do
local dk=dt[k]
if dk.tg then
c=c+1
en=en+1
collected[c]=dk
dk.ni=k
dk.ei=en
end
end
ll.en=en
end
end
end
return collected
end
local function collect(list,collected,c)
local dt=list.dt
if dt then
local n=#dt
if n==0 then
list.en=0
elseif n==1 then
local dk=dt[1]
if dk.tg then
c=c+1
collected[c]=dk
dk.ni=1
dk.ei=1
c=collect(dk,collected,c)
list.en=1
else
list.en=0
end
else
local en=0
for k=1,n do
local dk=dt[k]
if dk.tg then
c=c+1
en=en+1
collected[c]=dk
dk.ni=k
dk.ei=en
c=collect(dk,collected,c)
end
end
list.en=en
end
end
return c
end
apply_axis['descendant']=function(list)
local collected={}
local c=0
for l=1,#list do
c=collect(list[l],collected,c)
end
return collected
end
local function collect(list,collected,c)
local dt=list.dt
if dt then
local n=#dt
if n==0 then
list.en=0
elseif n==1 then
local dk=dt[1]
if dk.tg then
c=c+1
collected[c]=dk
dk.ni=1
dk.ei=1
c=collect(dk,collected,c)
list.en=1
end
else
local en=0
for k=1,#dt do
local dk=dt[k]
if dk.tg then
c=c+1
en=en+1
collected[c]=dk
dk.ni=k
dk.ei=en
c=collect(dk,collected,c)
end
end
list.en=en
end
end
return c
end
apply_axis['descendant-or-self']=function(list)
local collected={}
local c=0
for l=1,#list do
local ll=list[l]
if ll.special~=true then
c=c+1
collected[c]=ll
end
c=collect(ll,collected,c)
end
return collected
end
apply_axis['ancestor']=function(list)
local collected={}
local c=0
for l=1,#list do
local ll=list[l]
while ll do
ll=ll.__p__
if ll then
c=c+1
collected[c]=ll
end
end
end
return collected
end
apply_axis['ancestor-or-self']=function(list)
local collected={}
local c=0
for l=1,#list do
local ll=list[l]
c=c+1
collected[c]=ll
while ll do
ll=ll.__p__
if ll then
c=c+1
collected[c]=ll
end
end
end
return collected
end
apply_axis['parent']=function(list)
local collected={}
local c=0
for l=1,#list do
local pl=list[l].__p__
if pl then
c=c+1
collected[c]=pl
end
end
return collected
end
apply_axis['attribute']=function(list)
return {}
end
apply_axis['namespace']=function(list)
return {}
end
apply_axis['following']=function(list)
return {}
end
apply_axis['preceding']=function(list)
return {}
end
apply_axis['following-sibling']=function(list)
local collected={}
local c=0
for l=1,#list do
local ll=list[l]
local p=ll.__p__
local d=p.dt
for i=ll.ni+1,#d do
local di=d[i]
if type(di)=="table" then
c=c+1
collected[c]=di
end
end
end
return collected
end
apply_axis['preceding-sibling']=function(list)
local collected={}
local c=0
for l=1,#list do
local ll=list[l]
local p=ll.__p__
local d=p.dt
for i=1,ll.ni-1 do
local di=d[i]
if type(di)=="table" then
c=c+1
collected[c]=di
end
end
end
return collected
end
apply_axis['reverse-sibling']=function(list)
local collected={}
local c=0
for l=1,#list do
local ll=list[l]
local p=ll.__p__
local d=p.dt
for i=ll.ni-1,1,-1 do
local di=d[i]
if type(di)=="table" then
c=c+1
collected[c]=di
end
end
end
return collected
end
apply_axis['auto-descendant-or-self']=apply_axis['descendant-or-self']
apply_axis['auto-descendant']=apply_axis['descendant']
apply_axis['auto-child']=apply_axis['child']
apply_axis['auto-self']=apply_axis['self']
apply_axis['initial-child']=apply_axis['child']
local function apply_nodes(list,directive,nodes)
local maxn=#nodes
if maxn==3 then
local nns=nodes[2]
local ntg=nodes[3]
if not nns and not ntg then
if directive then
return list
else
return {}
end
else
local collected={}
local c=0
local m=0
local p=nil
if not nns then
for l=1,#list do
local ll=list[l]
local ltg=ll.tg
if ltg then
if directive then
if ntg==ltg then
local llp=ll.__p__;if llp~=p then p=llp;m=1 else m=m+1 end
c=c+1
collected[c]=ll
ll.mi=m
end
elseif ntg~=ltg then
local llp=ll.__p__;if llp~=p then p=llp;m=1 else m=m+1 end
c=c+1
collected[c]=ll
ll.mi=m
end
end
end
elseif not ntg then
for l=1,#list do
local ll=list[l]
local lns=ll.rn or ll.ns
if lns then
if directive then
if lns==nns then
local llp=ll.__p__;if llp~=p then p=llp;m=1 else m=m+1 end
c=c+1
collected[c]=ll
ll.mi=m
end
elseif lns~=nns then
local llp=ll.__p__;if llp~=p then p=llp;m=1 else m=m+1 end
c=c+1
collected[c]=ll
ll.mi=m
end
end
end
else
for l=1,#list do
local ll=list[l]
local ltg=ll.tg
if ltg then
local lns=ll.rn or ll.ns
local ok=ltg==ntg and lns==nns
if directive then
if ok then
local llp=ll.__p__;if llp~=p then p=llp;m=1 else m=m+1 end
c=c+1
collected[c]=ll
ll.mi=m
end
elseif not ok then
local llp=ll.__p__;if llp~=p then p=llp;m=1 else m=m+1 end
c=c+1
collected[c]=ll
ll.mi=m
end
end
end
end
return collected
end
else
local collected={}
local c=0
local m=0
local p=nil
for l=1,#list do
local ll=list[l]
local ltg=ll.tg
if ltg then
local lns=ll.rn or ll.ns
local ok=false
for n=1,maxn,3 do
local nns=nodes[n+1]
local ntg=nodes[n+2]
ok=(not ntg or ltg==ntg) and (not nns or lns==nns)
if ok then
break
end
end
if directive then
if ok then
local llp=ll.__p__;if llp~=p then p=llp;m=1 else m=m+1 end
c=c+1
collected[c]=ll
ll.mi=m
end
elseif not ok then
local llp=ll.__p__;if llp~=p then p=llp;m=1 else m=m+1 end
c=c+1
collected[c]=ll
ll.mi=m
end
end
end
return collected
end
end
local quit_expression=false
local function apply_expression(list,expression,order)
local collected={}
local c=0
quit_expression=false
for l=1,#list do
local ll=list[l]
if expression(list,ll,l,order) then
c=c+1
collected[c]=ll
end
if quit_expression then
break
end
end
return collected
end
local function apply_selector(list,specification)
if xml.applyselector then
apply_selector=xml.applyselector
return apply_selector(list,specification)
else
return list
end
end
local 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
local spaces=S(" \n\r\t\f")^0
local lp_space=S(" \n\r\t\f")
local lp_any=P(1)
local lp_noequal=P("!=")/"~="+P("<=")+P(">=")+P("==")
local lp_doequal=P("=")/"=="
local lp_or=P("|")/" or "
local lp_and=P("&")/" and "
local builtin={
text="(ll.dt[1] or '')",
content="ll.dt",
name="((ll.ns~='' and ll.ns..':'..ll.tg) or ll.tg)",
tag="ll.tg",
position="l",
firstindex="1",
firstelement="1",
first="1",
lastindex="(#ll.__p__.dt or 1)",
lastelement="(ll.__p__.en or 1)",
last="#list",
rootposition="order",
order="order",
element="(ll.ei or 1)",
index="(ll.ni or 1)",
match="(ll.mi or 1)",
namespace="ll.ns",
ns="ll.ns",
}
local lp_builtin=lpeg.utfchartabletopattern(builtin)/builtin*((spaces*P("(")*spaces*P(")"))/"")
local lp_attribute=(P("@")+P("attribute::"))/""*Cc("(ll.at and ll.at['")*((R("az","AZ")+S("-_:"))^1)*Cc("'])")
local lp_fastpos_p=P("+")^0*R("09")^1*P(-1)/"l==%0"
local lp_fastpos_n=P("-")*R("09")^1*P(-1)/"(%0<0 and (#list+%0==l))"
local lp_fastpos=lp_fastpos_n+lp_fastpos_p
local lp_reserved=C("and")+C("or")+C("not")+C("div")+C("mod")+C("true")+C("false")
local lp_lua_function=Cs((R("az","AZ","__")^1*(P(".")*R("az","AZ","__")^1)^1)*("("))/"%0"
local lp_function=C(R("az","AZ","__")^1)*P("(")/function(t)
if expressions[t] then
return "expr."..t.."("
else
return "expr.error("
end
end
local lparent=P("(")
local rparent=P(")")
local noparent=1-(lparent+rparent)
local nested=P{lparent*(noparent+V(1))^0*rparent}
local value=P(lparent*C((noparent+nested)^0)*rparent)
local lp_child=Cc("expr.child(ll,'")*R("az","AZ")*R("az","AZ","--","__")^0*Cc("')")
local lp_number=S("+-")*R("09")^1
local lp_string=Cc("'")*R("az","AZ","--","__")^1*Cc("'")
local lp_content=(P("'")*(1-P("'"))^0*P("'")+P('"')*(1-P('"'))^0*P('"'))
local cleaner
local lp_special=(C(P("name")+P("text")+P("tag")+P("count")+P("child")))*value/function(t,s)
if expressions[t] then
s=s and s~="" and lpegmatch(cleaner,s)
if s and s~="" then
return "expr."..t.."(ll,"..s..")"
else
return "expr."..t.."(ll)"
end
else
return "expr.error("..t..")"
end
end
local content=lp_builtin+lp_attribute+lp_special+lp_noequal+lp_doequal+lp_or+lp_and+lp_reserved+lp_lua_function+lp_function+lp_content+
lp_child+lp_any
local converter=Cs (
lp_fastpos+(P { lparent*(V(1))^0*rparent+content } )^0
)
cleaner=Cs ((
lp_reserved+lp_number+lp_string+1 )^1 )
local template_e=[[
local expr = xml.expressions
return function(list,ll,l,order)
return %s
end
]]
local template_f_y=[[
local finalizer = xml.finalizers['%s']['%s']
return function(collection)
return finalizer(collection,%s)
end
]]
local template_f_n=[[
return xml.finalizers['%s']['%s']
]]
local register_last_match={ kind="axis",axis="last-match" }
local register_self={ kind="axis",axis="self" }
local register_parent={ kind="axis",axis="parent" }
local register_descendant={ kind="axis",axis="descendant" }
local register_child={ kind="axis",axis="child" }
local register_descendant_or_self={ kind="axis",axis="descendant-or-self" }
local register_root={ kind="axis",axis="root" }
local register_ancestor={ kind="axis",axis="ancestor" }
local register_ancestor_or_self={ kind="axis",axis="ancestor-or-self" }
local register_attribute={ kind="axis",axis="attribute" }
local register_namespace={ kind="axis",axis="namespace" }
local register_following={ kind="axis",axis="following" }
local register_following_sibling={ kind="axis",axis="following-sibling" }
local register_preceding={ kind="axis",axis="preceding" }
local register_preceding_sibling={ kind="axis",axis="preceding-sibling" }
local register_reverse_sibling={ kind="axis",axis="reverse-sibling" }
local register_auto_descendant_or_self={ kind="axis",axis="auto-descendant-or-self" }
local register_auto_descendant={ kind="axis",axis="auto-descendant" }
local register_auto_self={ kind="axis",axis="auto-self" }
local register_auto_child={ kind="axis",axis="auto-child" }
local register_initial_child={ kind="axis",axis="initial-child" }
local register_all_nodes={ kind="nodes",nodetest=true,nodes={ true,false,false } }
local skip={}
local function errorrunner_e(str,cnv)
if not skip[str] then
report_lpath("error in expression: %s => %s",str,cnv)
skip[str]=cnv or str
end
return false
end
local function errorrunner_f(str,arg)
report_lpath("error in finalizer: %s(%s)",str,arg or "")
return false
end
local function register_nodes(nodetest,nodes)
return { kind="nodes",nodetest=nodetest,nodes=nodes }
end
local function register_selector(specification)
return { kind="selector",specification=specification }
end
local function register_expression(expression)
local converted=lpegmatch(converter,expression)
local runner=load(format(template_e,converted))
runner=(runner and runner()) or function() errorrunner_e(expression,converted) end
return { kind="expression",expression=expression,converted=converted,evaluator=runner }
end
local function register_finalizer(protocol,name,arguments)
local runner
if arguments and arguments~="" then
runner=load(format(template_f_y,protocol or xml.defaultprotocol,name,arguments))
else
runner=load(format(template_f_n,protocol or xml.defaultprotocol,name))
end
runner=(runner and runner()) or function() errorrunner_f(name,arguments) end
return { kind="finalizer",name=name,arguments=arguments,finalizer=runner }
end
local expression=P { "ex",
ex="["*C((V("sq")+V("dq")+(1-S("[]"))+V("ex"))^0)*"]",
sq="'"*(1-S("'"))^0*"'",
dq='"'*(1-S('"'))^0*'"',
}
local arguments=P { "ar",
ar="("*Cs((V("sq")+V("dq")+V("nq")+P(1-P(")")))^0)*")",
nq=((1-S("),'\""))^1)/function(s) return format("%q",s) end,
sq=P("'")*(1-P("'"))^0*P("'"),
dq=P('"')*(1-P('"'))^0*P('"'),
}
local function register_error(str)
return { kind="error",error=format("unparsed: %s",str) }
end
local special_1=P("*")*Cc(register_auto_descendant)*Cc(register_all_nodes)
local special_2=P("/")*Cc(register_auto_self)
local special_3=P("")*Cc(register_auto_self)
local no_nextcolon=P(-1)+#(1-P(":"))
local no_nextlparent=P(-1)+#(1-P("("))
local pathparser=Ct { "patterns",
patterns=spaces*V("protocol")*spaces*(
(V("special")*spaces*P(-1) )+(V("initial")*spaces*V("step")*spaces*(P("/")*spaces*V("step")*spaces)^0 )
),
protocol=Cg(V("letters"),"protocol")*P("://")+Cg(Cc(nil),"protocol"),
step=((V("shortcuts")+V("selector")+P("/")+V("axis"))*spaces*V("nodes")^0+V("error"))*spaces*V("expressions")^0*spaces*V("finalizer")^0,
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),
special=special_1+special_2+special_3,
initial=(P("/")*spaces*Cc(register_initial_child))^-1,
error=(P(1)^1)/register_error,
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"),
shortcuts=V("shortcuts_a")*(spaces*"/"*spaces*V("shortcuts_a"))^0,
s_descendant_or_self=(P("***/")+P("/"))*Cc(register_descendant_or_self),
s_descendant=P("**")*Cc(register_descendant),
s_child=P("*")*no_nextcolon*Cc(register_child),
s_parent=P("..")*Cc(register_parent),
s_self=P("." )*Cc(register_self),
s_root=P("^^")*Cc(register_root),
s_ancestor=P("^")*Cc(register_ancestor),
s_lastmatch=P("=")*Cc(register_last_match),
descendant=P("descendant::")*Cc(register_descendant),
child=P("child::")*Cc(register_child),
parent=P("parent::")*Cc(register_parent),
self=P("self::")*Cc(register_self),
root=P('root::')*Cc(register_root),
ancestor=P('ancestor::')*Cc(register_ancestor),
descendant_or_self=P('descendant-or-self::')*Cc(register_descendant_or_self),
ancestor_or_self=P('ancestor-or-self::')*Cc(register_ancestor_or_self),
following=P('following::')*Cc(register_following),
following_sibling=P('following-sibling::')*Cc(register_following_sibling),
preceding=P('preceding::')*Cc(register_preceding),
preceding_sibling=P('preceding-sibling::')*Cc(register_preceding_sibling),
reverse_sibling=P('reverse-sibling::')*Cc(register_reverse_sibling),
last_match=P('last-match::')*Cc(register_last_match),
selector=P("{")*C((1-P("}"))^1)*P("}")/register_selector,
nodes=(V("nodefunction")*spaces*P("(")*V("nodeset")*P(")")+V("nodetest")*V("nodeset"))/register_nodes,
expressions=expression/register_expression,
letters=R("az")^1,
name=(1-S("/[]()|:*!"))^1,
negate=P("!")*Cc(false),
nodefunction=V("negate")+P("not")*Cc(false)+Cc(true),
nodetest=V("negate")+Cc(true),
nodename=(V("negate")+Cc(true))*spaces*((V("wildnodename")*P(":")*V("wildnodename"))+(Cc(false)*V("wildnodename"))),
wildnodename=(C(V("name"))+P("*")*Cc(false))*no_nextlparent,
nodeset=spaces*Ct(V("nodename")*(spaces*P("|")*spaces*V("nodename"))^0)*spaces,
finalizer=(Cb("protocol")*P("/")^-1*C(V("name"))*arguments*P(-1))/register_finalizer,
}
xmlpatterns.pathparser=pathparser
local cache={}
local function nodesettostring(set,nodetest)
local t={}
for i=1,#set,3 do
local directive,ns,tg=set[i],set[i+1],set[i+2]
if not ns or ns=="" then ns="*" end
if not tg or tg=="" then tg="*" end
tg=(tg=="@rt@" and "[root]") or format("%s:%s",ns,tg)
t[#t+1]=(directive and tg) or format("not(%s)",tg)
end
if nodetest==false then
return format("not(%s)",concat(t,"|"))
else
return concat(t,"|")
end
end
local function tagstostring(list)
if #list==0 then
return "no elements"
else
local t={}
for i=1,#list do
local li=list[i]
local ns=li.ns
local tg=li.tg
if not ns or ns=="" then ns="*" end
if not tg or tg=="" then tg="*" end
t[i]=(tg=="@rt@" and "[root]") or format("%s:%s",ns,tg)
end
return concat(t," ")
end
end
xml.nodesettostring=nodesettostring
local lpath
local function lshow(parsed)
if type(parsed)=="string" then
parsed=lpath(parsed)
end
report_lpath("%s://%s => %s",parsed.protocol or xml.defaultprotocol,parsed.pattern,
table.serialize(parsed,false))
end
xml.lshow=lshow
local function add_comment(p,str)
local pc=p.comment
if not pc then
p.comment={ str }
else
pc[#pc+1]=str
end
end
lpath=function (pattern)
lpathcalls=lpathcalls+1
if type(pattern)=="table" then
return pattern
else
local parsed=cache[pattern]
if parsed then
lpathcached=lpathcached+1
else
parsed=lpegmatch(pathparser,pattern)
if parsed then
parsed.pattern=pattern
local np=#parsed
if np==0 then
parsed={ pattern=pattern,register_self,state="parsing error" }
report_lpath("parsing error in pattern: %s",pattern)
lshow(parsed)
else
local pi=parsed[1]
if pi.axis=="auto-child" then
if false then
add_comment(parsed,"auto-child replaced by auto-descendant-or-self")
parsed[1]=register_auto_descendant_or_self
else
add_comment(parsed,"auto-child replaced by auto-descendant")
parsed[1]=register_auto_descendant
end
elseif pi.axis=="initial-child" and np>1 and parsed[2].axis then
add_comment(parsed,"initial-child removed")
remove(parsed,1)
end
local np=#parsed
if np>1 then
local pnp=parsed[np]
if pnp.kind=="nodes" and pnp.nodetest==true then
local nodes=pnp.nodes
if nodes[1]==true and nodes[2]==false and nodes[3]==false then
add_comment(parsed,"redundant final wildcard filter removed")
remove(parsed,np)
end
end
end
end
else
parsed={ pattern=pattern }
end
cache[pattern]=parsed
if trace_lparse and not trace_lprofile then
lshow(parsed)
end
end
return parsed
end
end
xml.lpath=lpath
do
local profiled={}
xml.profiled=profiled
local lastmatch=nil
local keepmatch=nil
if directives then
directives.register("xml.path.keeplastmatch",function(v)
keepmatch=v
lastmatch=nil
end)
end
apply_axis["last-match"]=function()
return lastmatch or {}
end
local function profiled_apply(list,parsed,nofparsed,order)
local p=profiled[parsed.pattern]
if p then
p.tested=p.tested+1
else
p={ tested=1,matched=0,finalized=0 }
profiled[parsed.pattern]=p
end
local collected=list
for i=1,nofparsed do
local pi=parsed[i]
local kind=pi.kind
if kind=="axis" then
collected=apply_axis[pi.axis](collected)
elseif kind=="nodes" then
collected=apply_nodes(collected,pi.nodetest,pi.nodes)
elseif kind=="expression" then
collected=apply_expression(collected,pi.evaluator,order)
elseif kind=="selector" then
collected=apply_selector(collected,pi.specification)
elseif kind=="finalizer" then
collected=pi.finalizer(collected)
p.matched=p.matched+1
p.finalized=p.finalized+1
return collected
end
if not collected or #collected==0 then
local pn=i<nofparsed and parsed[nofparsed]
if pn and pn.kind=="finalizer" then
collected=pn.finalizer(collected)
p.finalized=p.finalized+1
return collected
end
return nil
end
end
if collected then
p.matched=p.matched+1
end
return collected
end
local function traced_apply(list,parsed,nofparsed,order)
if trace_lparse then
lshow(parsed)
end
report_lpath("collecting: %s",parsed.pattern)
report_lpath("root tags : %s",tagstostring(list))
report_lpath("order : %s",order or "unset")
local collected=list
for i=1,nofparsed do
local pi=parsed[i]
local kind=pi.kind
if kind=="axis" then
collected=apply_axis[pi.axis](collected)
report_lpath("% 10i : ax : %s",(collected and #collected) or 0,pi.axis)
elseif kind=="nodes" then
collected=apply_nodes(collected,pi.nodetest,pi.nodes)
report_lpath("% 10i : ns : %s",(collected and #collected) or 0,nodesettostring(pi.nodes,pi.nodetest))
elseif kind=="expression" then
collected=apply_expression(collected,pi.evaluator,order)
report_lpath("% 10i : ex : %s -> %s",(collected and #collected) or 0,pi.expression,pi.converted)
elseif kind=="selector" then
collected=apply_selector(collected,pi.specification)
report_lpath("% 10i : se : %s ",(collected and #collected) or 0,pi.specification)
elseif kind=="finalizer" then
collected=pi.finalizer(collected)
report_lpath("% 10i : fi : %s : %s(%s)",(type(collected)=="table" and #collected) or 0,parsed.protocol or xml.defaultprotocol,pi.name,pi.arguments or "")
return collected
end
if not collected or #collected==0 then
local pn=i<nofparsed and parsed[nofparsed]
if pn and pn.kind=="finalizer" then
collected=pn.finalizer(collected)
report_lpath("% 10i : fi : %s : %s(%s)",(type(collected)=="table" and #collected) or 0,parsed.protocol or xml.defaultprotocol,pn.name,pn.arguments or "")
return collected
end
return nil
end
end
return collected
end
local function normal_apply(list,parsed,nofparsed,order)
local collected=list
for i=1,nofparsed do
local pi=parsed[i]
local kind=pi.kind
if kind=="axis" then
local axis=pi.axis
if axis~="self" then
collected=apply_axis[axis](collected)
end
elseif kind=="nodes" then
collected=apply_nodes(collected,pi.nodetest,pi.nodes)
elseif kind=="expression" then
collected=apply_expression(collected,pi.evaluator,order)
elseif kind=="selector" then
collected=apply_selector(collected,pi.specification)
elseif kind=="finalizer" then
return pi.finalizer(collected)
end
if not collected or #collected==0 then
local pf=i<nofparsed and parsed[nofparsed].finalizer
if pf then
return pf(collected)
end
return nil
end
end
return collected
end
local apply=normal_apply
if trackers then
trackers.register("xml.path,xml.parse,xml.profile",function()
if trace_lprofile then
apply=profiled_apply
elseif trace_lpath then
apply=traced_apply
else
apply=normal_apply
end
end)
end
function xml.applylpath(list,pattern)
if not list then
lastmatch=nil
return
end
local parsed=cache[pattern]
if parsed then
lpathcalls=lpathcalls+1
lpathcached=lpathcached+1
elseif type(pattern)=="table" then
lpathcalls=lpathcalls+1
parsed=pattern
else
parsed=lpath(pattern) or pattern
end
if not parsed then
lastmatch=nil
return
end
local nofparsed=#parsed
if nofparsed==0 then
lastmatch=nil
return
end
local collected=apply({ list },parsed,nofparsed,list.mi)
lastmatch=keepmatch and collected or nil
return collected
end
function xml.lastmatch()
return lastmatch
end
local stack={}
function xml.pushmatch()
insert(stack,lastmatch)
end
function xml.popmatch()
lastmatch=remove(stack)
end
end
local applylpath=xml.applylpath
function xml.filter(root,pattern)
return applylpath(root,pattern)
end
expressions.child=function(e,pattern)
return applylpath(e,pattern)
end
expressions.count=function(e,pattern)
local collected=applylpath(e,pattern)
return pattern and (collected and #collected) or 0
end
expressions.oneof=function(s,...)
for i=1,select("#",...) do
if s==select(i,...) then
return true
end
end
return false
end
expressions.error=function(str)
xml.errorhandler(format("unknown function in lpath expression: %s",tostring(str or "?")))
return false
end
expressions.undefined=function(s)
return s==nil
end
expressions.quit=function(s)
if s or s==nil then
quit_expression=true
end
return true
end
expressions.print=function(...)
print(...)
return true
end
expressions.find=find
expressions.upper=upper
expressions.lower=lower
expressions.number=tonumber
expressions.boolean=toboolean
function expressions.contains(str,pattern)
local t=type(str)
if t=="string" then
if find(str,pattern) then
return true
end
elseif t=="table" then
for i=1,#str do
local d=str[i]
if type(d)=="string" and find(d,pattern) then
return true
end
end
end
return false
end
function xml.expressions.idstring(str)
return type(str)=="string" and gsub(str,"^#","") or ""
end
local function traverse(root,pattern,handle)
local collected=applylpath(root,pattern)
if collected then
for c=1,#collected do
local e=collected[c]
local r=e.__p__
handle(r,r.dt,e.ni)
end
end
end
local function selection(root,pattern,handle)
local collected=applylpath(root,pattern)
if collected then
if handle then
for c=1,#collected do
handle(collected[c])
end
else
return collected
end
end
end
xml.traverse=traverse
xml.selection=selection
local function dofunction(collected,fnc,...)
if collected then
local f=functions[fnc]
if f then
for c=1,#collected do
f(collected[c],...)
end
else
report_lpath("unknown function %a",fnc)
end
end
end
finalizers.xml["function"]=dofunction
finalizers.tex["function"]=dofunction
expressions.text=function(e,n)
local rdt=e.__p__.dt
return rdt and rdt[n] or ""
end
expressions.name=function(e,n)
local found=false
n=tonumber(n) or 0
if n==0 then
found=type(e)=="table" and e
elseif n<0 then
local d=e.__p__.dt
local k=e.ni
for i=k-1,1,-1 do
local di=d[i]
if type(di)=="table" then
if n==-1 then
found=di
break
else
n=n+1
end
end
end
else
local d=e.__p__.dt
local k=e.ni
for i=k+1,#d,1 do
local di=d[i]
if type(di)=="table" then
if n==1 then
found=di
break
else
n=n-1
end
end
end
end
if found then
local ns=found.rn or found.ns or ""
local tg=found.tg
if ns~="" then
return ns..":"..tg
else
return tg
end
else
return ""
end
end
expressions.tag=function(e,n)
if not e then
return ""
else
local found=false
n=tonumber(n) or 0
if n==0 then
found=(type(e)=="table") and e
elseif n<0 then
local d=e.__p__.dt
local k=e.ni
for i=k-1,1,-1 do
local di=d[i]
if type(di)=="table" then
if n==-1 then
found=di
break
else
n=n+1
end
end
end
else
local d=e.__p__.dt
local k=e.ni
for i=k+1,#d,1 do
local di=d[i]
if type(di)=="table" then
if n==1 then
found=di
break
else
n=n-1
end
end
end
end
return (found and found.tg) or ""
end
end
local dummy=function() end
function xml.elements(root,pattern,reverse)
local collected=applylpath(root,pattern)
if not collected then
return dummy
end
local n=#collected
if n==0 then
return dummy
end
if reverse then
local c=n+1
return function()
if c>1 then
c=c-1
local e=collected[c]
local r=e.__p__
return r,r.dt,e.ni
end
end
else
local c=0
return function()
if c<n then
c=c+1
local e=collected[c]
local r=e.__p__
return r,r.dt,e.ni
end
end
end
end
function xml.collected(root,pattern,reverse)
local collected=applylpath(root,pattern)
if not collected then
return dummy
end
local n=#collected
if n==0 then
return dummy
end
if reverse then
local c=n+1
return function()
if c>1 then
c=c-1
return collected[c]
end
end
else
local c=0
return function()
if c<n then
c=c+1
return collected[c]
end
end
end
end
function xml.inspect(collection,pattern)
pattern=pattern or "."
for e in xml.collected(collection,pattern or ".") do
report_lpath("pattern: %s\n\n%s\n",pattern,xml.tostring(e))
end
end
local function split(e)
local dt=e.dt
if dt then
for i=1,#dt do
local dti=dt[i]
if type(dti)=="string" then
dti=gsub(dti,"^[\n\r]*(.-)[\n\r]*","%1")
dti=gsub(dti,"[\n\r]+","\n\n")
dt[i]=dti
else
split(dti)
end
end
end
return e
end
function xml.finalizers.paragraphs(c)
for i=1,#c do
split(c[i])
end
return c
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["lxml-mis"] = package.loaded["lxml-mis"] or true
-- original size: 3574, stripped down to: 1808
if not modules then modules={} end modules ['lxml-mis']={
version=1.001,
comment="this module is the basis for the lxml-* ones",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local xml,lpeg,string=xml,lpeg,string
local type=type
local concat=table.concat
local format,gsub,match=string.format,string.gsub,string.match
local lpegmatch,lpegpatterns=lpeg.match,lpeg.patterns
local P,S,R,C,V,Cc,Cs=lpeg.P,lpeg.S,lpeg.R,lpeg.C,lpeg.V,lpeg.Cc,lpeg.Cs
lpegpatterns.xml=lpegpatterns.xml or {}
local xmlpatterns=lpegpatterns.xml
local function xmlgsub(t,old,new)
local dt=t.dt
if dt then
for k=1,#dt do
local v=dt[k]
if type(v)=="string" then
dt[k]=gsub(v,old,new)
else
xmlgsub(v,old,new)
end
end
end
end
function xml.stripleadingspaces(dk,d,k)
if d and k then
local dkm=d[k-1]
if dkm and type(dkm)=="string" then
local s=match(dkm,"\n(%s+)")
xmlgsub(dk,"\n"..rep(" ",#s),"\n")
end
end
end
local normal=(1-S("<&>"))^0
local special=P("<")/"<"+P(">")/">"+P("&")/"&"
local escaped=Cs(normal*(special*normal)^0)
local normal=(1-S"&")^0
local special=P("<")/"<"+P(">")/">"+P("&")/"&"
local unescaped=Cs(normal*(special*normal)^0)
local cleansed=Cs(((P("<")*(1-P(">"))^0*P(">"))/""+1)^0)
xmlpatterns.escaped=escaped
xmlpatterns.unescaped=unescaped
xmlpatterns.cleansed=cleansed
function xml.escaped (str) return lpegmatch(escaped,str) end
function xml.unescaped(str) return lpegmatch(unescaped,str) end
function xml.cleansed (str) return lpegmatch(cleansed,str) end
function xml.fillin(root,pattern,str,check)
local e=xml.first(root,pattern)
if e then
local n=#e.dt
if not check or n==0 or (n==1 and e.dt[1]=="") then
e.dt={ str }
end
end
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["lxml-aux"] = package.loaded["lxml-aux"] or true
-- original size: 30771, stripped down to: 19680
if not modules then modules={} end modules ['lxml-aux']={
version=1.001,
comment="this module is the basis for the lxml-* ones",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local trace_manipulations=false trackers.register("lxml.manipulations",function(v) trace_manipulations=v end)
local trace_inclusions=false trackers.register("lxml.inclusions",function(v) trace_inclusions=v end)
local report_xml=logs.reporter("xml")
local xml=xml
local xmlcopy,xmlname=xml.copy,xml.name
local xmlinheritedconvert=xml.inheritedconvert
local xmlapplylpath=xml.applylpath
local type,next,setmetatable,getmetatable=type,next,setmetatable,getmetatable
local insert,remove,fastcopy,concat=table.insert,table.remove,table.fastcopy,table.concat
local gmatch,gsub,format,find,strip=string.gmatch,string.gsub,string.format,string.find,string.strip
local utfbyte=utf.byte
local lpegmatch,lpegpatterns=lpeg.match,lpeg.patterns
local striplinepatterns=utilities.strings.striplinepatterns
local function report(what,pattern,c,e)
report_xml("%s element %a, root %a, position %a, index %a, pattern %a",what,xmlname(e),xmlname(e.__p__),c,e.ni,pattern)
end
local function withelements(e,handle,depth)
if e and handle then
local edt=e.dt
if edt then
depth=depth or 0
for i=1,#edt do
local e=edt[i]
if type(e)=="table" then
handle(e,depth)
withelements(e,handle,depth+1)
end
end
end
end
end
xml.withelements=withelements
function xml.withelement(e,n,handle)
if e and n~=0 and handle then
local edt=e.dt
if edt then
if n>0 then
for i=1,#edt do
local ei=edt[i]
if type(ei)=="table" then
if n==1 then
handle(ei)
return
else
n=n-1
end
end
end
elseif n<0 then
for i=#edt,1,-1 do
local ei=edt[i]
if type(ei)=="table" then
if n==-1 then
handle(ei)
return
else
n=n+1
end
end
end
end
end
end
end
function xml.each(root,pattern,handle,reverse)
local collected=xmlapplylpath(root,pattern)
if collected then
if handle then
if reverse then
for c=#collected,1,-1 do
handle(collected[c])
end
else
for c=1,#collected do
handle(collected[c])
end
end
end
return collected
end
end
function xml.processattributes(root,pattern,handle)
local collected=xmlapplylpath(root,pattern)
if collected and handle then
for c=1,#collected do
handle(collected[c].at)
end
end
return collected
end
function xml.collect(root,pattern)
return xmlapplylpath(root,pattern)
end
function xml.collecttexts(root,pattern,flatten)
local collected=xmlapplylpath(root,pattern)
if collected and flatten then
local xmltostring=xml.tostring
for c=1,#collected do
collected[c]=xmltostring(collected[c].dt)
end
end
return collected or {}
end
function xml.collect_tags(root,pattern,nonamespace)
local collected=xmlapplylpath(root,pattern)
if collected then
local t={}
local n=0
for c=1,#collected do
local e=collected[c]
local ns=e.ns
local tg=e.tg
n=n+1
if nonamespace then
t[n]=tg
elseif ns=="" then
t[n]=tg
else
t[n]=ns..":"..tg
end
end
return t
end
end
local no_root={ no_root=true }
local function redo_ni(d)
for k=1,#d do
local dk=d[k]
if type(dk)=="table" then
dk.ni=k
end
end
end
xml.reindex=redo_ni
local function xmltoelement(whatever,root)
if not whatever then
return nil
end
local element
if type(whatever)=="string" then
element=xmlinheritedconvert(whatever,root)
else
element=whatever
end
if element.error then
return whatever
end
if element then
end
return element
end
xml.toelement=xmltoelement
local function copiedelement(element,newparent)
if type(element)=="string" then
return element
else
element=xmlcopy(element).dt
if newparent and type(element)=="table" then
element.__p__=newparent
end
return element
end
end
function xml.delete(root,pattern)
if not pattern or pattern=="" then
local p=root.__p__
if p then
if trace_manipulations then
report('deleting',"--",c,root)
end
local d=p.dt
remove(d,root.ni)
redo_ni(d)
end
else
local collected=xmlapplylpath(root,pattern)
if collected then
for c=1,#collected do
local e=collected[c]
local p=e.__p__
if p then
if trace_manipulations then
report('deleting',pattern,c,e)
end
local d=p.dt
local ni=e.ni
if ni<=#d then
if false then
p.dt[ni]=""
else
remove(d,ni)
redo_ni(d)
end
else
end
end
end
end
end
end
function xml.replace(root,pattern,whatever)
local element=root and xmltoelement(whatever,root)
local collected=element and xmlapplylpath(root,pattern)
if collected then
for c=1,#collected do
local e=collected[c]
local p=e.__p__
if p then
if trace_manipulations then
report('replacing',pattern,c,e)
end
local d=p.dt
local n=e.ni
local t=copiedelement(element,p)
if type(t)=="table" then
d[n]=t[1]
for i=2,#t do
n=n+1
insert(d,n,t[i])
end
else
d[n]=t
end
redo_ni(d)
end
end
end
end
local function wrap(e,wrapper)
local t={
rn=e.rn,
tg=e.tg,
ns=e.ns,
at=e.at,
dt=e.dt,
__p__=e,
}
setmetatable(t,getmetatable(e))
e.rn=wrapper.rn or e.rn or ""
e.tg=wrapper.tg or e.tg or ""
e.ns=wrapper.ns or e.ns or ""
e.at=fastcopy(wrapper.at)
e.dt={ t }
end
function xml.wrap(root,pattern,whatever)
if whatever then
local wrapper=xmltoelement(whatever,root)
local collected=xmlapplylpath(root,pattern)
if collected then
for c=1,#collected do
local e=collected[c]
if trace_manipulations then
report('wrapping',pattern,c,e)
end
wrap(e,wrapper)
end
end
else
wrap(root,xmltoelement(pattern))
end
end
local function inject_element(root,pattern,whatever,prepend)
local element=root and xmltoelement(whatever,root)
local collected=element and xmlapplylpath(root,pattern)
local function inject_e(e)
local r=e.__p__
local d=r.dt
local k=e.ni
local rri=r.ri
local edt=(rri and d[rri].dt) or (d and d[k] and d[k].dt)
if edt then
local be,af
local cp=copiedelement(element,e)
if prepend then
be,af=cp,edt
else
be,af=edt,cp
end
local bn=#be
for i=1,#af do
bn=bn+1
be[bn]=af[i]
end
if rri then
r.dt[rri].dt=be
else
d[k].dt=be
end
redo_ni(d)
end
end
if not collected then
elseif collected.tg then
inject_e(collected)
else
for c=1,#collected do
inject_e(collected[c])
end
end
end
local function insert_element(root,pattern,whatever,before)
local element=root and xmltoelement(whatever,root)
local collected=element and xmlapplylpath(root,pattern)
local function insert_e(e)
local r=e.__p__
local d=r.dt
local k=e.ni
if not before then
k=k+1
end
insert(d,k,copiedelement(element,r))
redo_ni(d)
end
if not collected then
elseif collected.tg then
insert_e(collected)
else
for c=1,#collected do
insert_e(collected[c])
end
end
end
xml.insert_element=insert_element
xml.insertafter=insert_element
xml.insertbefore=function(r,p,e) insert_element(r,p,e,true) end
xml.injectafter=inject_element
xml.injectbefore=function(r,p,e) inject_element(r,p,e,true) end
local function include(xmldata,pattern,attribute,recursive,loaddata,level)
pattern=pattern or 'include'
loaddata=loaddata or io.loaddata
local collected=xmlapplylpath(xmldata,pattern)
if collected then
if not level then
level=1
end
for c=1,#collected do
local ek=collected[c]
local name=nil
local ekdt=ek.dt
if ekdt then
local ekat=ek.at
local ekrt=ek.__p__
if ekrt then
local epdt=ekrt.dt
if not attribute or attribute=="" then
name=(type(ekdt)=="table" and ekdt[1]) or ekdt
end
if not name then
for a in gmatch(attribute or "href","([^|]+)") do
name=ekat[a]
if name then
break
end
end
end
local data=nil
if name and name~="" then
local d,n=loaddata(name)
data=d or ""
name=n or name
if trace_inclusions then
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 ")
end
end
if not data or data=="" then
epdt[ek.ni]=""
elseif ekat["parse"]=="text" then
epdt[ek.ni]=xml.escaped(data)
else
local settings=xmldata.settings
local savedresource=settings.currentresource
settings.currentresource=name
local xi=xmlinheritedconvert(data,xmldata)
if not xi then
epdt[ek.ni]=""
else
if recursive then
include(xi,pattern,attribute,recursive,loaddata,level+1)
end
local child=xml.body(xi)
child.__p__=ekrt
child.__f__=name
child.cf=name
epdt[ek.ni]=child
local settings=xmldata.settings
local inclusions=settings and settings.inclusions
if inclusions then
inclusions[#inclusions+1]=name
elseif settings then
settings.inclusions={ name }
else
settings={ inclusions={ name } }
xmldata.settings=settings
end
if child.er then
local badinclusions=settings.badinclusions
if badinclusions then
badinclusions[#badinclusions+1]=name
else
settings.badinclusions={ name }
end
end
end
settings.currentresource=savedresource
end
end
end
end
end
end
xml.include=include
function xml.inclusion(e,default)
while e do
local f=e.__f__
if f then
return f
else
e=e.__p__
end
end
return default
end
local function getinclusions(key,e,sorted)
while e do
local settings=e.settings
if settings then
local inclusions=settings[key]
if inclusions then
inclusions=table.unique(inclusions)
if sorted then
table.sort(inclusions)
end
return inclusions
else
e=e.__p__
end
else
e=e.__p__
end
end
end
function xml.inclusions(e,sorted)
return getinclusions("inclusions",e,sorted)
end
function xml.badinclusions(e,sorted)
return getinclusions("badinclusions",e,sorted)
end
local b_collapser=lpegpatterns.b_collapser
local m_collapser=lpegpatterns.m_collapser
local e_collapser=lpegpatterns.e_collapser
local b_stripper=lpegpatterns.b_stripper
local m_stripper=lpegpatterns.m_stripper
local e_stripper=lpegpatterns.e_stripper
local function stripelement(e,nolines,anywhere)
local edt=e.dt
if edt then
local n=#edt
if n==0 then
return e
elseif anywhere then
local t={}
local m=0
for e=1,n do
local str=edt[e]
if type(str)~="string" then
m=m+1
t[m]=str
elseif str~="" then
if nolines then
str=lpegmatch((n==1 and b_collapser) or (n==m and e_collapser) or m_collapser,str)
else
str=lpegmatch((n==1 and b_stripper) or (n==m and e_stripper) or m_stripper,str)
end
if str~="" then
m=m+1
t[m]=str
end
end
end
e.dt=t
else
local str=edt[1]
if type(str)=="string" then
if str~="" then
str=lpegmatch(nolines and b_collapser or b_stripper,str)
end
if str=="" then
remove(edt,1)
n=n-1
else
edt[1]=str
end
end
if n>0 then
str=edt[n]
if type(str)=="string" then
if str=="" then
remove(edt)
else
str=lpegmatch(nolines and e_collapser or e_stripper,str)
if str=="" then
remove(edt)
else
edt[n]=str
end
end
end
end
end
end
return e
end
xml.stripelement=stripelement
function xml.strip(root,pattern,nolines,anywhere)
local collected=xmlapplylpath(root,pattern)
if collected then
for i=1,#collected do
stripelement(collected[i],nolines,anywhere)
end
end
end
local function renamespace(root,oldspace,newspace)
local ndt=#root.dt
for i=1,ndt or 0 do
local e=root[i]
if type(e)=="table" then
if e.ns==oldspace then
e.ns=newspace
if e.rn then
e.rn=newspace
end
end
local edt=e.dt
if edt then
renamespace(edt,oldspace,newspace)
end
end
end
end
xml.renamespace=renamespace
function xml.remaptag(root,pattern,newtg)
local collected=xmlapplylpath(root,pattern)
if collected then
for c=1,#collected do
collected[c].tg=newtg
end
end
end
function xml.remapnamespace(root,pattern,newns)
local collected=xmlapplylpath(root,pattern)
if collected then
for c=1,#collected do
collected[c].ns=newns
end
end
end
function xml.checknamespace(root,pattern,newns)
local collected=xmlapplylpath(root,pattern)
if collected then
for c=1,#collected do
local e=collected[c]
if (not e.rn or e.rn=="") and e.ns=="" then
e.rn=newns
end
end
end
end
function xml.remapname(root,pattern,newtg,newns,newrn)
local collected=xmlapplylpath(root,pattern)
if collected then
for c=1,#collected do
local e=collected[c]
e.tg,e.ns,e.rn=newtg,newns,newrn
end
end
end
function xml.cdatatotext(e)
local dt=e.dt
if #dt==1 then
local first=dt[1]
if first.tg=="@cd@" then
e.dt=first.dt
end
else
end
end
function xml.texttocdata(e)
local dt=e.dt
local s=xml.tostring(dt)
e.tg="@cd@"
e.special=true
e.ns=""
e.rn=""
e.dt={ s }
e.at=nil
end
function xml.elementtocdata(e)
local dt=e.dt
local s=xml.tostring(e)
e.tg="@cd@"
e.special=true
e.ns=""
e.rn=""
e.dt={ s }
e.at=nil
end
xml.builtinentities=table.tohash { "amp","quot","apos","lt","gt" }
local entities=characters and characters.entities or nil
local builtinentities=xml.builtinentities
function xml.addentitiesdoctype(root,option)
if not entities then
require("char-ent")
entities=characters.entities
end
if entities and root and root.tg=="@rt@" and root.statistics then
local list={}
local hexify=option=="hexadecimal"
for k,v in table.sortedhash(root.statistics.entities.names) do
if not builtinentities[k] then
local e=entities[k]
if not e then
e=format("[%s]",k)
elseif hexify then
e=format("&#%05X;",utfbyte(k))
end
list[#list+1]=format(" <!ENTITY %s %q >",k,e)
end
end
local dt=root.dt
local n=dt[1].tg=="@pi@" and 2 or 1
if #list>0 then
insert(dt,n,{ "\n" })
insert(dt,n,{
tg="@dt@",
dt={ format("Something [\n%s\n] ",concat(list)) },
ns="",
special=true,
})
insert(dt,n,{ "\n\n" })
else
end
end
end
xml.all=xml.each
xml.insert=xml.insertafter
xml.inject=xml.injectafter
xml.after=xml.insertafter
xml.before=xml.insertbefore
xml.process=xml.each
xml.obsolete=xml.obsolete or {}
local obsolete=xml.obsolete
xml.strip_whitespace=xml.strip obsolete.strip_whitespace=xml.strip
xml.collect_elements=xml.collect obsolete.collect_elements=xml.collect
xml.delete_element=xml.delete obsolete.delete_element=xml.delete
xml.replace_element=xml.replace obsolete.replace_element=xml.replace
xml.each_element=xml.each obsolete.each_element=xml.each
xml.process_elements=xml.process obsolete.process_elements=xml.process
xml.insert_element_after=xml.insertafter obsolete.insert_element_after=xml.insertafter
xml.insert_element_before=xml.insertbefore obsolete.insert_element_before=xml.insertbefore
xml.inject_element_after=xml.injectafter obsolete.inject_element_after=xml.injectafter
xml.inject_element_before=xml.injectbefore obsolete.inject_element_before=xml.injectbefore
xml.process_attributes=xml.processattributes obsolete.process_attributes=xml.processattributes
xml.collect_texts=xml.collecttexts obsolete.collect_texts=xml.collecttexts
xml.inject_element=xml.inject obsolete.inject_element=xml.inject
xml.remap_tag=xml.remaptag obsolete.remap_tag=xml.remaptag
xml.remap_name=xml.remapname obsolete.remap_name=xml.remapname
xml.remap_namespace=xml.remapnamespace obsolete.remap_namespace=xml.remapnamespace
function xml.cdata(e)
if e then
local dt=e.dt
if dt and #dt==1 then
local first=dt[1]
return first.tg=="@cd@" and first.dt[1] or ""
end
end
return ""
end
function xml.finalizers.xml.cdata(collected)
if collected then
local e=collected[1]
if e then
local dt=e.dt
if dt and #dt==1 then
local first=dt[1]
return first.tg=="@cd@" and first.dt[1] or ""
end
end
end
return ""
end
function xml.insertcomment(e,str,n)
insert(e.dt,n or 1,{
tg="@cm@",
ns="",
special=true,
at={},
dt={ str },
})
end
function xml.insertcdata(e,str,n)
insert(e.dt,n or 1,{
tg="@cd@",
ns="",
special=true,
at={},
dt={ str },
})
end
function xml.setcomment(e,str,n)
e.dt={ {
tg="@cm@",
ns="",
special=true,
at={},
dt={ str },
} }
end
function xml.setcdata(e,str)
e.dt={ {
tg="@cd@",
ns="",
special=true,
at={},
dt={ str },
} }
end
function xml.separate(x,pattern)
local collected=xmlapplylpath(x,pattern)
if collected then
for c=1,#collected do
local e=collected[c]
local d=e.dt
if d==x then
report_xml("warning: xml.separate changes root")
x=d
end
local t={ "\n" }
local n=1
local i=1
local nd=#d
while i<=nd do
while i<=nd do
local di=d[i]
if type(di)=="string" then
if di=="\n" or find(di,"^%s+$") then
i=i+1
else
d[i]=strip(di)
break
end
else
break
end
end
if i>nd then
break
end
t[n+1]="\n"
t[n+2]=d[i]
t[n+3]="\n"
n=n+3
i=i+1
end
t[n+1]="\n"
setmetatable(t,getmetatable(d))
e.dt=t
end
end
return x
end
local helpers=xml.helpers or {}
xml.helpers=helpers
local function normal(e,action)
local edt=e.dt
if edt then
for i=1,#edt do
local str=edt[i]
if type(str)=="string" and str~="" then
edt[i]=action(str)
end
end
end
end
local function recurse(e,action)
local edt=e.dt
if edt then
for i=1,#edt do
local str=edt[i]
if type(str)~="string" then
recurse(str,action)
elseif str~="" then
edt[i]=action(str)
end
end
end
end
function helpers.recursetext(collected,action,recursive)
if recursive then
for i=1,#collected do
recurse(collected[i],action)
end
else
for i=1,#collected do
normal(collected[i],action)
end
end
end
local specials={
["@rt@"]="root",
["@pi@"]="instruction",
["@cm@"]="comment",
["@dt@"]="declaration",
["@cd@"]="cdata",
}
local function convert(x,strip,flat)
local ns=x.ns
local tg=x.tg
local at=x.at
local dt=x.dt
local node=flat and {
[0]=(not x.special and (ns~="" and ns..":"..tg or tg)) or nil,
} or {
_namespace=ns~="" and ns or nil,
_tag=not x.special and tg or nil,
_type=specials[tg] or "_element",
}
if at then
for k,v in next,at do
node[k]=v
end
end
local n=0
for i=1,#dt do
local di=dt[i]
if type(di)=="table" then
if flat and di.special then
else
di=convert(di,strip,flat)
if di then
n=n+1
node[n]=di
end
end
elseif strip then
di=lpegmatch(strip,di)
if di~="" then
n=n+1
node[n]=di
end
else
n=n+1
node[n]=di
end
end
if next(node) then
return node
end
end
function xml.totable(x,strip,flat)
if type(x)=="table" then
if strip then
strip=striplinepatterns[strip]
end
return convert(x,strip,flat)
end
end
function xml.rename(e,namespace,name,attributes)
if type(e)~="table" or not e.tg then
return
end
if type(name)=="table" then
attributes=name
name=namespace
namespace=""
elseif type(name)~="string" then
attributes={}
name=namespace
namespace=""
end
if type(attributes)~="table" then
attributes={}
end
e.ns=namespace
e.rn=namespace
e.tg=name
e.at=attributes
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["lxml-xml"] = package.loaded["lxml-xml"] or true
-- original size: 11096, stripped down to: 7702
if not modules then modules={} end modules ['lxml-xml']={
version=1.001,
comment="this module is the basis for the lxml-* ones",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local tonumber,next=tonumber,next
local concat=table.concat
local find,lower,upper=string.find,string.lower,string.upper
local xml=xml
local finalizers=xml.finalizers.xml
local xmlfilter=xml.filter
local xmltostring=xml.tostring
local xmlserialize=xml.serialize
local xmlcollected=xml.collected
local xmlnewhandlers=xml.newhandlers
local reparsedentity=xml.reparsedentitylpeg
local unescapedentity=xml.unescapedentitylpeg
local parsedentity=reparsedentity
local function first(collected)
return collected and collected[1]
end
local function last(collected)
return collected and collected[#collected]
end
local function all(collected)
return collected
end
local reverse=table.reversed
local function attribute(collected,name)
if collected and #collected>0 then
local at=collected[1].at
return at and at[name]
end
end
local function att(id,name)
local at=id.at
return at and at[name]
end
local function count(collected)
return collected and #collected or 0
end
local function position(collected,n)
if not collected then
return 0
end
local nc=#collected
if nc==0 then
return 0
end
n=tonumber(n) or 0
if n<0 then
return collected[nc+n+1]
elseif n>0 then
return collected[n]
else
return collected[1].mi or 0
end
end
local function match(collected)
return collected and #collected>0 and collected[1].mi or 0
end
local function index(collected)
return collected and #collected>0 and collected[1].ni or 0
end
local function attributes(collected,arguments)
if collected and #collected>0 then
local at=collected[1].at
if arguments then
return at[arguments]
elseif next(at) then
return at
end
end
end
local function chainattribute(collected,arguments)
if collected and #collected>0 then
local e=collected[1]
while e do
local at=e.at
if at then
local a=at[arguments]
if a then
return a
end
else
break
end
e=e.__p__
end
end
return ""
end
local function raw(collected)
if collected and #collected>0 then
local e=collected[1] or collected
return e and xmltostring(e) or ""
else
return ""
end
end
local xmltexthandler=xmlnewhandlers {
name="string",
initialize=function()
result={}
return result
end,
finalize=function()
return concat(result)
end,
handle=function(...)
result[#result+1]=concat {... }
end,
escape=false,
}
local function xmltotext(root)
local dt=root.dt
if not dt then
return ""
end
local nt=#dt
if nt==0 then
return ""
elseif nt==1 and type(dt[1])=="string" then
return dt[1]
else
return xmlserialize(root,xmltexthandler) or ""
end
end
function xml.serializetotext(root)
return root and xmlserialize(root,xmltexthandler) or ""
end
local function text(collected)
if collected then
local e=collected[1] or collected
return e and xmltotext(e) or ""
else
return ""
end
end
local function texts(collected)
if not collected then
return {}
end
local nc=#collected
if nc==0 then
return {}
end
local t,n={},0
for c=1,nc do
local e=collected[c]
if e and e.dt then
n=n+1
t[n]=e.dt
end
end
return t
end
local function tag(collected,n)
if not collected then
return
end
local nc=#collected
if nc==0 then
return
end
local c
if n==0 or not n then
c=collected[1]
elseif n>1 then
c=collected[n]
else
c=collected[nc-n+1]
end
return c and c.tg
end
local function name(collected,n)
if not collected then
return
end
local nc=#collected
if nc==0 then
return
end
local c
if n==0 or not n then
c=collected[1]
elseif n>1 then
c=collected[n]
else
c=collected[nc-n+1]
end
if not c then
elseif c.ns=="" then
return c.tg
else
return c.ns..":"..c.tg
end
end
local function tags(collected,nonamespace)
if not collected then
return
end
local nc=#collected
if nc==0 then
return
end
local t,n={},0
for c=1,nc do
local e=collected[c]
local ns,tg=e.ns,e.tg
n=n+1
if nonamespace or ns=="" then
t[n]=tg
else
t[n]=ns..":"..tg
end
end
return t
end
local function empty(collected,spacesonly)
if not collected then
return true
end
local nc=#collected
if nc==0 then
return true
end
for c=1,nc do
local e=collected[c]
if e then
local edt=e.dt
if edt then
local n=#edt
if n==1 then
local edk=edt[1]
local typ=type(edk)
if typ=="table" then
return false
elseif edk~="" then
return false
elseif spacesonly and not find(edk,"%S") then
return false
end
elseif n>1 then
return false
end
end
end
end
return true
end
finalizers.first=first
finalizers.last=last
finalizers.all=all
finalizers.reverse=reverse
finalizers.elements=all
finalizers.default=all
finalizers.attribute=attribute
finalizers.att=att
finalizers.count=count
finalizers.position=position
finalizers.match=match
finalizers.index=index
finalizers.attributes=attributes
finalizers.chainattribute=chainattribute
finalizers.text=text
finalizers.texts=texts
finalizers.tag=tag
finalizers.name=name
finalizers.tags=tags
finalizers.empty=empty
function xml.first(id,pattern)
return first(xmlfilter(id,pattern))
end
function xml.last(id,pattern)
return last(xmlfilter(id,pattern))
end
function xml.count(id,pattern)
return count(xmlfilter(id,pattern))
end
function xml.attribute(id,pattern,a,default)
return attribute(xmlfilter(id,pattern),a,default)
end
function xml.raw(id,pattern)
if pattern then
return raw(xmlfilter(id,pattern))
else
return raw(id)
end
end
function xml.text(id,pattern)
if pattern then
local collected=xmlfilter(id,pattern)
return collected and #collected>0 and xmltotext(collected[1]) or ""
elseif id then
return xmltotext(id) or ""
else
return ""
end
end
function xml.pure(id,pattern)
if pattern then
local collected=xmlfilter(id,pattern)
if collected and #collected>0 then
parsedentity=unescapedentity
local s=collected and #collected>0 and xmltotext(collected[1]) or ""
parsedentity=reparsedentity
return s
else
return ""
end
else
parsedentity=unescapedentity
local s=xmltotext(id) or ""
parsedentity=reparsedentity
return s
end
end
xml.content=text
function xml.position(id,pattern,n)
return position(xmlfilter(id,pattern),n)
end
function xml.match(id,pattern)
return match(xmlfilter(id,pattern))
end
function xml.empty(id,pattern,spacesonly)
return empty(xmlfilter(id,pattern),spacesonly)
end
xml.all=xml.filter
xml.index=xml.position
xml.found=xml.filter
local function totable(x)
local t={}
for e in xmlcollected(x[1] or x,"/*") do
t[e.tg]=xmltostring(e.dt) or ""
end
return next(t) and t or nil
end
xml.table=totable
finalizers.table=totable
local function textonly(e,t)
if e then
local edt=e.dt
if edt then
for i=1,#edt do
local e=edt[i]
if type(e)=="table" then
textonly(e,t)
else
t[#t+1]=e
end
end
end
end
return t
end
function xml.textonly(e)
return concat(textonly(e,{}))
end
function finalizers.lowerall(collected)
for c=1,#collected do
local e=collected[c]
if not e.special then
e.tg=lower(e.tg)
local eat=e.at
if eat then
local t={}
for k,v in next,eat do
t[lower(k)]=v
end
e.at=t
end
end
end
end
function finalizers.upperall(collected)
for c=1,#collected do
local e=collected[c]
if not e.special then
e.tg=upper(e.tg)
local eat=e.at
if eat then
local t={}
for k,v in next,eat do
t[upper(k)]=v
end
e.at=t
end
end
end
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["trac-xml"] = package.loaded["trac-xml"] or true
-- original size: 6407, stripped down to: 4640
if not modules then modules={} end modules ['trac-xml']={
version=1.001,
comment="companion to trac-log.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local formatters=string.formatters
local reporters=logs.reporters
local xmlserialize=xml.serialize
local xmlcollected=xml.collected
local xmltext=xml.text
local xmlfirst=xml.first
local function showhelp(specification,...)
local root=xml.convert(specification.helpinfo or "")
if not root then
return
end
local xs=xml.gethandlers("string")
xml.sethandlersfunction(xs,"short",function(e,handler) xmlserialize(e.dt,handler) end)
xml.sethandlersfunction(xs,"ref",function(e,handler) handler.handle("--"..e.at.name) end)
local wantedcategories=select("#",...)==0 and true or table.tohash {... }
local nofcategories=xml.count(root,"/application/flags/category")
local report=specification.report
for category in xmlcollected(root,"/application/flags/category") do
local categoryname=category.at.name or ""
if wantedcategories==true or wantedcategories[categoryname] then
if nofcategories>1 then
report("%s options:",categoryname)
report()
end
for subcategory in xmlcollected(category,"/subcategory") do
for flag in xmlcollected(subcategory,"/flag") do
local name=flag.at.name
local value=flag.at.value
local short=xmltext(xmlfirst(flag,"/short"))
if value then
report("--%-20s %s",formatters["%s=%s"](name,value),short)
else
report("--%-20s %s",name,short)
end
end
report()
end
end
end
for category in xmlcollected(root,"/application/examples/category") do
local title=xmltext(xmlfirst(category,"/title"))
if title and title~="" then
report()
report(title)
report()
end
for subcategory in xmlcollected(category,"/subcategory") do
for example in xmlcollected(subcategory,"/example") do
local command=xmltext(xmlfirst(example,"/command"))
local comment=xmltext(xmlfirst(example,"/comment"))
report(command)
end
report()
end
end
for comment in xmlcollected(root,"/application/comments/comment") do
local comment=xmltext(comment)
report()
report(comment)
report()
end
end
local reporthelp=reporters.help
local exporthelp=reporters.export
local function xmlfound(t)
local helpinfo=t.helpinfo
if type(helpinfo)=="table" then
return false
end
if type(helpinfo)~="string" then
helpinfo="Warning: no helpinfo found."
t.helpinfo=helpinfo
return false
end
if string.find(helpinfo,".xml$") then
local ownscript=environment.ownscript
local helpdata=false
if ownscript then
local helpfile=file.join(file.pathpart(ownscript),helpinfo)
helpdata=io.loaddata(helpfile)
if helpdata=="" then
helpdata=false
end
end
if not helpdata then
local helpfile=resolvers.findfile(helpinfo,"tex")
helpdata=helpfile and io.loaddata(helpfile)
end
if helpdata and helpdata~="" then
helpinfo=helpdata
else
helpinfo=formatters["Warning: help file %a is not found."](helpinfo)
end
end
t.helpinfo=helpinfo
return string.find(t.helpinfo,"^<%?xml") and true or false
end
function reporters.help(t,...)
if xmlfound(t) then
showhelp(t,...)
else
reporthelp(t,...)
end
end
function reporters.export(t,methods,filename)
if not xmlfound(t) then
return exporthelp(t)
end
if not methods or methods=="" then
methods=environment.arguments["exporthelp"]
end
if not filename or filename=="" then
filename=environment.files[1]
end
dofile(resolvers.findfile("trac-exp.lua","tex"))
local exporters=logs.exporters
if not exporters or not methods then
return exporthelp(t)
end
if methods=="all" then
methods=table.keys(exporters)
elseif type(methods)=="string" then
methods=utilities.parsers.settings_to_array(methods)
else
return exporthelp(t)
end
if type(filename)~="string" or filename=="" then
filename=false
elseif file.pathpart(filename)=="" then
t.report("export file %a will not be saved on the current path (safeguard)",filename)
return
end
for i=1,#methods do
local method=methods[i]
local exporter=exporters[method]
if exporter then
local result=exporter(t,method)
if result and result~="" then
if filename then
local fullname=file.replacesuffix(filename,method)
t.report("saving export in %a",fullname)
dir.mkdirs(file.pathpart(fullname))
io.savedata(fullname,result)
else
reporters.lines(t,result)
end
else
t.report("no output from exporter %a",method)
end
else
t.report("unknown exporter %a",method)
end
end
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["data-ini"] = package.loaded["data-ini"] or true
-- original size: 11099, stripped down to: 7152
if not modules then modules={} end modules ['data-ini']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files",
}
local next,type,getmetatable,rawset=next,type,getmetatable,rawset
local gsub,find,gmatch,char=string.gsub,string.find,string.gmatch,string.char
local filedirname,filebasename,filejoin=file.dirname,file.basename,file.join
local ostype,osname,osuname,ossetenv,osgetenv=os.type,os.name,os.uname,os.setenv,os.getenv
local P,S,R,C,Cs,Cc,lpegmatch=lpeg.P,lpeg.S,lpeg.R,lpeg.C,lpeg.Cs,lpeg.Cc,lpeg.match
local trace_locating=false trackers.register("resolvers.locating",function(v) trace_locating=v end)
local trace_detail=false trackers.register("resolvers.details",function(v) trace_detail=v end)
local trace_expansions=false trackers.register("resolvers.expansions",function(v) trace_expansions=v end)
local report_initialization=logs.reporter("resolvers","initialization")
resolvers=resolvers or {}
local resolvers=resolvers
texconfig.kpse_init=false
texconfig.shell_escape='t'
if not (environment and environment.default_texmfcnf) and kpse and kpse.default_texmfcnf then
local default_texmfcnf=kpse.default_texmfcnf()
default_texmfcnf=gsub(default_texmfcnf,"$SELFAUTOLOC","selfautoloc:")
default_texmfcnf=gsub(default_texmfcnf,"$SELFAUTODIR","selfautodir:")
default_texmfcnf=gsub(default_texmfcnf,"$SELFAUTOPARENT","selfautoparent:")
default_texmfcnf=gsub(default_texmfcnf,"$HOME","home:")
environment.default_texmfcnf=default_texmfcnf
end
kpse={ original=kpse }
setmetatable(kpse,{
__index=function(kp,name)
report_initialization("fatal error: kpse library is accessed (key: %s)",name)
os.exit()
end
} )
do
local osfontdir=osgetenv("OSFONTDIR")
if osfontdir and osfontdir~="" then
elseif osname=="windows" then
ossetenv("OSFONTDIR","c:/windows/fonts//")
elseif osname=="macosx" then
ossetenv("OSFONTDIR","$HOME/Library/Fonts//;/Library/Fonts//;/System/Library/Fonts//")
end
end
do
local homedir=osgetenv(ostype=="windows" and 'USERPROFILE' or 'HOME') or ''
if not homedir or homedir=="" then
homedir=char(127)
end
homedir=file.collapsepath(homedir)
ossetenv("HOME",homedir)
ossetenv("USERPROFILE",homedir)
environment.homedir=homedir
end
do
local args=environment.originalarguments or arg
if not environment.ownmain then
environment.ownmain=status and string.match(string.lower(status.banner),"this is ([%a]+)") or "luatex"
end
local ownbin=environment.ownbin or args[-2] or arg[-2] or args[-1] or arg[-1] or arg[0] or "luatex"
local ownpath=environment.ownpath or os.selfdir
ownbin=file.collapsepath(ownbin)
ownpath=file.collapsepath(ownpath)
if not ownpath or ownpath=="" or ownpath=="unset" then
ownpath=args[-1] or arg[-1]
ownpath=ownpath and filedirname(gsub(ownpath,"\\","/"))
if not ownpath or ownpath=="" then
ownpath=args[-0] or arg[-0]
ownpath=ownpath and filedirname(gsub(ownpath,"\\","/"))
end
local binary=ownbin
if not ownpath or ownpath=="" then
ownpath=ownpath and filedirname(binary)
end
if not ownpath or ownpath=="" then
if os.binsuffix~="" then
binary=file.replacesuffix(binary,os.binsuffix)
end
local path=osgetenv("PATH")
if path then
for p in gmatch(path,"[^"..io.pathseparator.."]+") do
local b=filejoin(p,binary)
if lfs.isfile(b) then
local olddir=lfs.currentdir()
if lfs.chdir(p) then
local pp=lfs.currentdir()
if trace_locating and p~=pp then
report_initialization("following symlink %a to %a",p,pp)
end
ownpath=pp
lfs.chdir(olddir)
else
if trace_locating then
report_initialization("unable to check path %a",p)
end
ownpath=p
end
break
end
end
end
end
if not ownpath or ownpath=="" then
ownpath="."
report_initialization("forcing fallback to ownpath %a",ownpath)
elseif trace_locating then
report_initialization("using ownpath %a",ownpath)
end
end
environment.ownbin=ownbin
environment.ownpath=ownpath
end
resolvers.ownpath=environment.ownpath
function resolvers.getownpath()
return environment.ownpath
end
do
local ownpath=environment.ownpath or dir.current()
if ownpath then
ossetenv('SELFAUTOLOC',file.collapsepath(ownpath))
ossetenv('SELFAUTODIR',file.collapsepath(ownpath.."/.."))
ossetenv('SELFAUTOPARENT',file.collapsepath(ownpath.."/../.."))
else
report_initialization("error: unable to locate ownpath")
os.exit()
end
end
local texos=environment.texos or osgetenv("TEXOS")
local texmfos=environment.texmfos or osgetenv('SELFAUTODIR')
if not texos or texos=="" then
texos=file.basename(texmfos)
end
ossetenv('TEXMFOS',texmfos)
ossetenv('TEXOS',texos)
ossetenv('SELFAUTOSYSTEM',os.platform)
environment.texos=texos
environment.texmfos=texmfos
local texroot=environment.texroot or osgetenv("TEXROOT")
if not texroot or texroot=="" then
texroot=osgetenv('SELFAUTOPARENT')
ossetenv('TEXROOT',texroot)
end
environment.texroot=file.collapsepath(texroot)
local prefixes=utilities.storage.allocate()
resolvers.prefixes=prefixes
local resolved={}
local abstract={}
local dynamic={}
function resolvers.resetresolve(str)
resolved,abstract={},{}
end
function resolvers.allprefixes(separator)
local all=table.sortedkeys(prefixes)
if separator then
for i=1,#all do
all[i]=all[i]..":"
end
end
return all
end
local function _resolve_(method,target)
local action=prefixes[method]
if action then
return action(target)
else
return method..":"..target
end
end
function resolvers.unresolve(str)
return abstract[str] or str
end
function resolvers.setdynamic(str)
dynamic[str]=true
end
local pattern=Cs((C(R("az")^2)*P(":")*C((1-S(" \"\';,"))^1)/_resolve_+P(1))^0)
local prefix=C(R("az")^2)*P(":")
local target=C((1-S(" \"\';,"))^1)
local notarget=(#S(";,")+P(-1))*Cc("")
local p_resolve=Cs(((prefix*(target+notarget))/_resolve_+P(1))^0)
local p_simple=prefix*P(-1)
local function resolve(str)
if type(str)=="table" then
local res={}
for i=1,#str do
res[i]=resolve(str[i])
end
return res
end
local res=resolved[str]
if res then
return res
end
local simple=lpegmatch(p_simple,str)
local action=prefixes[simple]
if action then
local res=action(res)
if not dynamic[simple] then
resolved[simple]=res
abstract[res]=simple
end
return res
end
res=lpegmatch(p_resolve,str)
resolved[str]=res
abstract[res]=str
return res
end
resolvers.resolve=resolve
if type(osuname)=="function" then
for k,v in next,osuname() do
if not prefixes[k] then
prefixes[k]=function() return v end
end
end
end
if ostype=="unix" then
local pattern
local function makepattern(t,k,v)
if t then
rawset(t,k,v)
end
local colon=P(":")
for k,v in table.sortedpairs(prefixes) do
if p then
p=P(k)+p
else
p=P(k)
end
end
pattern=Cs((p*colon+colon/";"+P(1))^0)
end
makepattern()
table.setmetatablenewindex(prefixes,makepattern)
function resolvers.repath(str)
return lpegmatch(pattern,str)
end
else
function resolvers.repath(str)
return str
end
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["data-exp"] = package.loaded["data-exp"] or true
-- original size: 18154, stripped down to: 10416
if not modules then modules={} end modules ['data-exp']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files",
}
local format,find,gmatch,lower,char,sub=string.format,string.find,string.gmatch,string.lower,string.char,string.sub
local concat,sort=table.concat,table.sort
local sortedkeys=table.sortedkeys
local lpegmatch,lpegpatterns=lpeg.match,lpeg.patterns
local Ct,Cs,Cc,Carg,P,C,S=lpeg.Ct,lpeg.Cs,lpeg.Cc,lpeg.Carg,lpeg.P,lpeg.C,lpeg.S
local type,next=type,next
local isdir=lfs.isdir
local collapsepath,joinpath,basename=file.collapsepath,file.join,file.basename
local trace_locating=false trackers.register("resolvers.locating",function(v) trace_locating=v end)
local trace_expansions=false trackers.register("resolvers.expansions",function(v) trace_expansions=v end)
local trace_globbing=true trackers.register("resolvers.globbing",function(v) trace_globbing=v end)
local report_expansions=logs.reporter("resolvers","expansions")
local report_globbing=logs.reporter("resolvers","globbing")
local resolvers=resolvers
local resolveprefix=resolvers.resolve
local function f_both(a,b)
local t,n={},0
for sb in gmatch(b,"[^,]+") do
for sa in gmatch(a,"[^,]+") do
n=n+1;t[n]=sa..sb
end
end
return concat(t,",")
end
local comma=P(",")
local nocomma=(1-comma)^1
local docomma=comma^1/","
local before=Cs((nocomma*Carg(1)+docomma)^0)
local after=Cs((Carg(1)*nocomma+docomma)^0)
local both=Cs(((C(nocomma)*Carg(1))/function(a,b) return lpegmatch(before,b,1,a) end+docomma)^0)
local function f_first (a,b) return lpegmatch(after,b,1,a) end
local function f_second(a,b) return lpegmatch(before,a,1,b) end
local function f_both (a,b) return lpegmatch(both,b,1,a) end
local left=P("{")
local right=P("}")
local var=P((1-S("{}" ))^0)
local set=P((1-S("{},"))^0)
local other=P(1)
local l_first=Cs((Cc("{")*(C(set)*left*C(var)*right/f_first)*Cc("}")+other )^0 )
local l_second=Cs((Cc("{")*(left*C(var)*right*C(set)/f_second)*Cc("}")+other )^0 )
local l_both=Cs((Cc("{")*(left*C(var)*right*left*C(var)*right/f_both)*Cc("}")+other )^0 )
local l_rest=Cs((left*var*(left/"")*var*(right/"")*var*right+other )^0 )
local stripper_1=lpeg.stripper ("{}@")
local replacer_1=lpeg.replacer { { ",}",",@}" },{ "{,","{@," },}
local function splitpathexpr(str,newlist,validate)
if trace_expansions then
report_expansions("expanding variable %a",str)
end
local t,ok,done=newlist or {},false,false
local n=#t
str=lpegmatch(replacer_1,str)
repeat
local old=str
repeat
local old=str
str=lpegmatch(l_first,str)
until old==str
repeat
local old=str
str=lpegmatch(l_second,str)
until old==str
repeat
local old=str
str=lpegmatch(l_both,str)
until old==str
repeat
local old=str
str=lpegmatch(l_rest,str)
until old==str
until old==str
str=lpegmatch(stripper_1,str)
if validate then
for s in gmatch(str,"[^,]+") do
s=validate(s)
if s then
n=n+1
t[n]=s
end
end
else
for s in gmatch(str,"[^,]+") do
n=n+1
t[n]=s
end
end
if trace_expansions then
for k=1,#t do
report_expansions("% 4i: %s",k,t[k])
end
end
return t
end
local function validate(s)
s=collapsepath(s)
return s~="" and not find(s,"^!*unset/*$") and s
end
resolvers.validatedpath=validate
function resolvers.expandedpathfromlist(pathlist)
local newlist={}
for k=1,#pathlist do
splitpathexpr(pathlist[k],newlist,validate)
end
return newlist
end
local usedhomedir=nil
local donegation=(P("!")/"" )^0
local doslashes=(P("\\")/"/"+1)^0
local function expandedhome()
if not usedhomedir then
usedhomedir=lpegmatch(Cs(donegation*doslashes),environment.homedir or "")
if usedhomedir=="~" or usedhomedir=="" or not isdir(usedhomedir) then
if trace_expansions then
report_expansions("no home dir set, ignoring dependent path using current path")
end
usedhomedir="."
end
end
return usedhomedir
end
local dohome=((P("~")+P("$HOME")+P("%HOME%"))/expandedhome)^0
local cleanup=Cs(donegation*dohome*doslashes)
resolvers.cleanpath=function(str)
return str and lpegmatch(cleanup,str) or ""
end
local expandhome=P("~")/"$HOME"
local dodouble=P('"')/""*(expandhome+(1-P('"')))^0*P('"')/""
local dosingle=P("'")/""*(expandhome+(1-P("'")))^0*P("'")/""
local dostring=(expandhome+1 )^0
local stripper=Cs(
lpegpatterns.unspacer*(dosingle+dodouble+dostring)*lpegpatterns.unspacer
)
function resolvers.checkedvariable(str)
return type(str)=="string" and lpegmatch(stripper,str) or str
end
local cache={}
local splitter=lpeg.tsplitat(";")
local backslashswapper=lpeg.replacer("\\","/")
local function splitconfigurationpath(str)
if str then
local found=cache[str]
if not found then
if str=="" then
found={}
else
local split=lpegmatch(splitter,lpegmatch(backslashswapper,str))
found={}
local noffound=0
for i=1,#split do
local s=split[i]
if not find(s,"^{*unset}*") then
noffound=noffound+1
found[noffound]=s
end
end
if trace_expansions then
report_expansions("splitting path specification %a",str)
for k=1,noffound do
report_expansions("% 4i: %s",k,found[k])
end
end
cache[str]=found
end
end
return found
end
end
resolvers.splitconfigurationpath=splitconfigurationpath
function resolvers.splitpath(str)
if type(str)=='table' then
return str
else
return splitconfigurationpath(str)
end
end
function resolvers.joinpath(str)
if type(str)=='table' then
return joinpath(str)
else
return str
end
end
local attributes,directory=lfs.attributes,lfs.dir
local weird=P(".")^1+lpeg.anywhere(S("~`!#$%^&*()={}[]:;\"\'||<>,?\n\r\t"))
local lessweird=P(".")^1+lpeg.anywhere(S("~`#$%^&*:;\"\'||<>,?\n\r\t"))
local timer={}
local scanned={}
local nofscans=0
local scancache={}
local fullcache={}
local nofsharedscans=0
local addcasecraptoo=true
local function scan(files,remap,spec,path,n,m,r,onlyone,tolerant)
local full=path=="" and spec or (spec..path..'/')
local dirlist={}
local nofdirs=0
local pattern=tolerant and lessweird or weird
local filelist={}
local noffiles=0
for name,mode in directory(full) do
if not lpegmatch(pattern,name) then
if not mode then
mode=attributes(full..name,"mode")
end
if mode=="file" then
n=n+1
noffiles=noffiles+1
filelist[noffiles]=name
elseif mode=="directory" then
m=m+1
nofdirs=nofdirs+1
if path~="" then
dirlist[nofdirs]=path.."/"..name
else
dirlist[nofdirs]=name
end
end
end
end
if noffiles>0 then
sort(filelist)
for i=1,noffiles do
local name=filelist[i]
local lower=lower(name)
local paths=files[lower]
if paths then
if onlyone then
else
if name~=lower then
local rl=remap[lower]
if not rl then
remap[lower]=name
r=r+1
elseif trace_globbing and rl~=name then
report_globbing("confusing filename, name: %a, lower: %a, already: %a",name,lower,rl)
end
if addcasecraptoo then
local paths=files[name]
if not paths then
files[name]=path
elseif type(paths)=="string" then
files[name]={ paths,path }
else
paths[#paths+1]=path
end
end
end
if type(paths)=="string" then
files[lower]={ paths,path }
else
paths[#paths+1]=path
end
end
else
files[lower]=path
if name~=lower then
local rl=remap[lower]
if not rl then
remap[lower]=name
r=r+1
elseif trace_globbing and rl~=name then
report_globbing("confusing filename, name: %a, lower: %a, already: %a",name,lower,rl)
end
end
end
end
end
if nofdirs>0 then
sort(dirlist)
for i=1,nofdirs do
files,remap,n,m,r=scan(files,remap,spec,dirlist[i],n,m,r,onlyonce,tolerant)
end
end
scancache[sub(full,1,-2)]=files
return files,remap,n,m,r
end
function resolvers.scanfiles(path,branch,usecache,onlyonce,tolerant)
local realpath=resolveprefix(path)
if usecache then
local content=fullcache[realpath]
if content then
if trace_locating then
report_expansions("using cached scan of path %a, branch %a",path,branch or path)
end
nofsharedscans=nofsharedscans+1
return content
end
end
statistics.starttiming(timer)
if trace_locating then
report_expansions("scanning path %a, branch %a",path,branch or path)
end
local content
if isdir(realpath) then
local files,remap,n,m,r=scan({},{},realpath..'/',"",0,0,0,onlyonce,tolerant)
content={
metadata={
path=path,
files=n,
directories=m,
remappings=r,
},
files=files,
remap=remap,
}
if trace_locating then
report_expansions("%s files found on %s directories with %s uppercase remappings",n,m,r)
end
else
content={
metadata={
path=path,
files=0,
directories=0,
remappings=0,
},
files={},
remap={},
}
if trace_locating then
report_expansions("invalid path %a",realpath)
end
end
if usecache then
scanned[#scanned+1]=realpath
fullcache[realpath]=content
end
nofscans=nofscans+1
statistics.stoptiming(timer)
return content
end
function resolvers.simplescanfiles(path,branch,usecache)
return resolvers.scanfiles(path,branch,usecache,true,true)
end
function resolvers.scandata()
table.sort(scanned)
return {
n=nofscans,
shared=nofsharedscans,
time=statistics.elapsedtime(timer),
paths=scanned,
}
end
function resolvers.get_from_content(content,path,name)
if not content then
return
end
local files=content.files
if not files then
return
end
local remap=content.remap
if not remap then
return
end
if name then
local used=lower(name)
return path,remap[used] or used
else
local name=path
local used=lower(name)
local path=files[used]
if path then
return path,remap[used] or used
end
end
end
local nothing=function() end
function resolvers.filtered_from_content(content,pattern)
if content and type(pattern)=="string" then
local pattern=lower(pattern)
local files=content.files
local remap=content.remap
if files and remap then
local f=sortedkeys(files)
local n=#f
local i=0
local function iterator()
while i<n do
i=i+1
local k=f[i]
if find(k,pattern) then
return files[k],remap and remap[k] or k
end
end
end
return iterator
end
end
return nothing
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["data-env"] = package.loaded["data-env"] or true
-- original size: 9360, stripped down to: 6312
if not modules then modules={} end modules ['data-env']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files",
}
local lower,gsub=string.lower,string.gsub
local next=next
local resolvers=resolvers
local allocate=utilities.storage.allocate
local setmetatableindex=table.setmetatableindex
local suffixonly=file.suffixonly
local formats=allocate()
local suffixes=allocate()
local dangerous=allocate()
local suffixmap=allocate()
local usertypes=allocate()
resolvers.formats=formats
resolvers.suffixes=suffixes
resolvers.dangerous=dangerous
resolvers.suffixmap=suffixmap
resolvers.usertypes=usertypes
local luasuffixes=utilities.lua.suffixes
local relations=allocate {
core={
ofm={
names={ "ofm","omega font metric","omega font metrics" },
variable='OFMFONTS',
suffixes={ 'ofm','tfm' },
},
ovf={
names={ "ovf","omega virtual font","omega virtual fonts" },
variable='OVFFONTS',
suffixes={ 'ovf','vf' },
},
tfm={
names={ "tfm","tex font metric","tex font metrics" },
variable='TFMFONTS',
suffixes={ 'tfm' },
},
vf={
names={ "vf","virtual font","virtual fonts" },
variable='VFFONTS',
suffixes={ 'vf' },
},
otf={
names={ "otf","opentype","opentype font","opentype fonts"},
variable='OPENTYPEFONTS',
suffixes={ 'otf' },
},
ttf={
names={ "ttf","truetype","truetype font","truetype fonts","truetype collection","truetype collections","truetype dictionary","truetype dictionaries" },
variable='TTFONTS',
suffixes={ 'ttf','ttc','dfont' },
},
afm={
names={ "afm","adobe font metric","adobe font metrics" },
variable="AFMFONTS",
suffixes={ "afm" },
},
pfb={
names={ "pfb","type1","type 1","type1 font","type 1 font","type1 fonts","type 1 fonts" },
variable='T1FONTS',
suffixes={ 'pfb','pfa' },
},
fea={
names={ "fea","font feature","font features","font feature file","font feature files" },
variable='FONTFEATURES',
suffixes={ 'fea' },
},
cid={
names={ "cid","cid map","cid maps","cid file","cid files" },
variable='FONTCIDMAPS',
suffixes={ 'cid','cidmap' },
},
fmt={
names={ "fmt","format","tex format" },
variable='TEXFORMATS',
suffixes={ 'fmt' },
},
mem={
names={ 'mem',"metapost format" },
variable='MPMEMS',
suffixes={ 'mem' },
},
mp={
names={ "mp" },
variable='MPINPUTS',
suffixes={ 'mp','mpvi','mpiv','mpii' },
usertype=true,
},
tex={
names={ "tex" },
variable='TEXINPUTS',
suffixes={ "tex","mkvi","mkiv","mkii","cld","lfg","xml" },
usertype=true,
},
icc={
names={ "icc","icc profile","icc profiles" },
variable='ICCPROFILES',
suffixes={ 'icc' },
},
texmfscripts={
names={ "texmfscript","texmfscripts","script","scripts" },
variable='TEXMFSCRIPTS',
suffixes={ 'lua','rb','pl','py' },
},
lua={
names={ "lua" },
variable='LUAINPUTS',
suffixes={ luasuffixes.lua,luasuffixes.luc,luasuffixes.tma,luasuffixes.tmc },
usertype=true,
},
lib={
names={ "lib" },
variable='CLUAINPUTS',
suffixes=os.libsuffix and { os.libsuffix } or { 'dll','so' },
},
bib={
names={ 'bib' },
variable='BIBINPUTS',
suffixes={ 'bib' },
usertype=true,
},
bst={
names={ 'bst' },
variable='BSTINPUTS',
suffixes={ 'bst' },
usertype=true,
},
fontconfig={
names={ 'fontconfig','fontconfig file','fontconfig files' },
variable='FONTCONFIG_PATH',
},
pk={
names={ "pk" },
variable='PKFONTS',
suffixes={ 'pk' },
},
},
obsolete={
enc={
names={ "enc","enc files","enc file","encoding files","encoding file" },
variable='ENCFONTS',
suffixes={ 'enc' },
},
map={
names={ "map","map files","map file" },
variable='TEXFONTMAPS',
suffixes={ 'map' },
},
lig={
names={ "lig files","lig file","ligature file","ligature files" },
variable='LIGFONTS',
suffixes={ 'lig' },
},
opl={
names={ "opl" },
variable='OPLFONTS',
suffixes={ 'opl' },
},
ovp={
names={ "ovp" },
variable='OVPFONTS',
suffixes={ 'ovp' },
},
},
kpse={
base={
names={ 'base',"metafont format" },
variable='MFBASES',
suffixes={ 'base','bas' },
},
cmap={
names={ 'cmap','cmap files','cmap file' },
variable='CMAPFONTS',
suffixes={ 'cmap' },
},
cnf={
names={ 'cnf' },
suffixes={ 'cnf' },
},
web={
names={ 'web' },
suffixes={ 'web','ch' }
},
cweb={
names={ 'cweb' },
suffixes={ 'w','web','ch' },
},
gf={
names={ 'gf' },
suffixes={ '<resolution>gf' },
},
mf={
names={ 'mf' },
variable='MFINPUTS',
suffixes={ 'mf' },
},
mft={
names={ 'mft' },
suffixes={ 'mft' },
},
pk={
names={ 'pk' },
suffixes={ '<resolution>pk' },
},
},
}
resolvers.relations=relations
function resolvers.updaterelations()
for category,categories in next,relations do
for name,relation in next,categories do
local rn=relation.names
local rv=relation.variable
if rn and rv then
local rs=relation.suffixes
local ru=relation.usertype
for i=1,#rn do
local rni=lower(gsub(rn[i]," ",""))
formats[rni]=rv
if rs then
suffixes[rni]=rs
for i=1,#rs do
local rsi=rs[i]
suffixmap[rsi]=rni
end
end
end
if ru then
usertypes[name]=true
end
end
end
end
end
resolvers.updaterelations()
local function simplified(t,k)
return k and rawget(t,lower(gsub(k," ",""))) or nil
end
setmetatableindex(formats,simplified)
setmetatableindex(suffixes,simplified)
setmetatableindex(suffixmap,simplified)
function resolvers.suffixofformat(str)
local s=suffixes[str]
return s and s[1] or ""
end
function resolvers.suffixofformat(str)
return suffixes[str] or {}
end
for name,format in next,formats do
dangerous[name]=true
end
dangerous.tex=nil
function resolvers.formatofvariable(str)
return formats[str] or ''
end
function resolvers.formatofsuffix(str)
return suffixmap[suffixonly(str)] or 'tex'
end
function resolvers.variableofformat(str)
return formats[str] or ''
end
function resolvers.variableofformatorsuffix(str)
local v=formats[str]
if v then
return v
end
v=suffixmap[suffixonly(str)]
if v then
return formats[v]
end
return ''
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["data-tmp"] = package.loaded["data-tmp"] or true
-- original size: 16284, stripped down to: 10938
if not modules then modules={} end modules ['data-tmp']={
version=1.100,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local format,lower,gsub,concat=string.format,string.lower,string.gsub,table.concat
local concat=table.concat
local mkdirs,isdir,isfile=dir.mkdirs,lfs.isdir,lfs.isfile
local addsuffix,is_writable,is_readable=file.addsuffix,file.is_writable,file.is_readable
local formatters=string.formatters
local next,type=next,type
local trace_locating=false trackers.register("resolvers.locating",function(v) trace_locating=v end)
local trace_cache=false trackers.register("resolvers.cache",function(v) trace_cache=v end)
local report_caches=logs.reporter("resolvers","caches")
local report_resolvers=logs.reporter("resolvers","caching")
local resolvers=resolvers
local cleanpath=resolvers.cleanpath
local directive_cleanup=false directives.register("system.compile.cleanup",function(v) directive_cleanup=v end)
local directive_strip=false directives.register("system.compile.strip",function(v) directive_strip=v end)
local compile=utilities.lua.compile
function utilities.lua.compile(luafile,lucfile,cleanup,strip)
if cleanup==nil then cleanup=directive_cleanup end
if strip==nil then strip=directive_strip end
return compile(luafile,lucfile,cleanup,strip)
end
caches=caches or {}
local caches=caches
local luasuffixes=utilities.lua.suffixes
caches.base=caches.base or "luatex-cache"
caches.more=caches.more or "context"
caches.direct=false
caches.tree=false
caches.force=true
caches.ask=false
caches.relocate=false
caches.defaults={ "TMPDIR","TEMPDIR","TMP","TEMP","HOME","HOMEPATH" }
directives.register("system.caches.fast",function(v) caches.fast=true end)
local writable,readables,usedreadables=nil,{},{}
local function identify()
local texmfcaches=resolvers.cleanpathlist("TEXMFCACHE")
if texmfcaches then
for k=1,#texmfcaches do
local cachepath=texmfcaches[k]
if cachepath~="" then
cachepath=resolvers.resolve(cachepath)
cachepath=resolvers.cleanpath(cachepath)
cachepath=file.collapsepath(cachepath)
local valid=isdir(cachepath)
if valid then
if is_readable(cachepath) then
readables[#readables+1]=cachepath
if not writable and is_writable(cachepath) then
writable=cachepath
end
end
elseif not writable and caches.force then
local cacheparent=file.dirname(cachepath)
if is_writable(cacheparent) and true then
if not caches.ask or io.ask(format("\nShould I create the cache path %s?",cachepath),"no",{ "yes","no" })=="yes" then
mkdirs(cachepath)
if isdir(cachepath) and is_writable(cachepath) then
report_caches("path %a created",cachepath)
writable=cachepath
readables[#readables+1]=cachepath
end
end
end
end
end
end
end
local texmfcaches=caches.defaults
if texmfcaches then
for k=1,#texmfcaches do
local cachepath=texmfcaches[k]
cachepath=resolvers.expansion(cachepath)
if cachepath~="" then
cachepath=resolvers.resolve(cachepath)
cachepath=resolvers.cleanpath(cachepath)
local valid=isdir(cachepath)
if valid and is_readable(cachepath) then
if not writable and is_writable(cachepath) then
readables[#readables+1]=cachepath
writable=cachepath
break
end
end
end
end
end
if not writable then
report_caches("fatal error: there is no valid writable cache path defined")
os.exit()
elseif #readables==0 then
report_caches("fatal error: there is no valid readable cache path defined")
os.exit()
end
writable=dir.expandname(resolvers.cleanpath(writable))
local base,more,tree=caches.base,caches.more,caches.tree or caches.treehash()
if tree then
caches.tree=tree
writable=mkdirs(writable,base,more,tree)
for i=1,#readables do
readables[i]=file.join(readables[i],base,more,tree)
end
else
writable=mkdirs(writable,base,more)
for i=1,#readables do
readables[i]=file.join(readables[i],base,more)
end
end
if trace_cache then
for i=1,#readables do
report_caches("using readable path %a (order %s)",readables[i],i)
end
report_caches("using writable path %a",writable)
end
identify=function()
return writable,readables
end
return writable,readables
end
function caches.usedpaths(separator)
local writable,readables=identify()
if #readables>1 then
local result={}
local done={}
for i=1,#readables do
local readable=readables[i]
if readable==writable then
done[readable]=true
result[#result+1]=formatters["readable+writable: %a"](readable)
elseif usedreadables[i] then
done[readable]=true
result[#result+1]=formatters["readable: %a"](readable)
end
end
if not done[writable] then
result[#result+1]=formatters["writable: %a"](writable)
end
return concat(result,separator or " | ")
else
return writable or "?"
end
end
function caches.configfiles()
return concat(resolvers.configurationfiles(),";")
end
function caches.hashed(tree)
tree=gsub(tree,"[\\/]+$","")
tree=lower(tree)
local hash=md5.hex(tree)
if trace_cache or trace_locating then
report_caches("hashing tree %a, hash %a",tree,hash)
end
return hash
end
function caches.treehash()
local tree=caches.configfiles()
if not tree or tree=="" then
return false
else
return caches.hashed(tree)
end
end
local r_cache,w_cache={},{}
local function getreadablepaths(...)
local tags={... }
local hash=concat(tags,"/")
local done=r_cache[hash]
if not done then
local writable,readables=identify()
if #tags>0 then
done={}
for i=1,#readables do
done[i]=file.join(readables[i],...)
end
else
done=readables
end
r_cache[hash]=done
end
return done
end
local function getwritablepath(...)
local tags={... }
local hash=concat(tags,"/")
local done=w_cache[hash]
if not done then
local writable,readables=identify()
if #tags>0 then
done=mkdirs(writable,...)
else
done=writable
end
w_cache[hash]=done
end
return done
end
caches.getreadablepaths=getreadablepaths
caches.getwritablepath=getwritablepath
function caches.getfirstreadablefile(filename,...)
local fullname,path=caches.setfirstwritablefile(filename,...)
if is_readable(fullname) then
return fullname,path
end
local rd=getreadablepaths(...)
for i=1,#rd do
local path=rd[i]
local fullname=file.join(path,filename)
if is_readable(fullname) then
usedreadables[i]=true
return fullname,path
end
end
return fullname,path
end
function caches.setfirstwritablefile(filename,...)
local wr=getwritablepath(...)
local fullname=file.join(wr,filename)
return fullname,wr
end
function caches.define(category,subcategory)
return function()
return getwritablepath(category,subcategory)
end
end
function caches.setluanames(path,name)
return format("%s/%s.%s",path,name,luasuffixes.tma),format("%s/%s.%s",path,name,luasuffixes.tmc)
end
function caches.loaddata(readables,name,writable)
if type(readables)=="string" then
readables={ readables }
end
for i=1,#readables do
local path=readables[i]
local loader=false
local tmaname,tmcname=caches.setluanames(path,name)
if isfile(tmcname) then
loader=loadfile(tmcname)
end
if not loader and isfile(tmaname) then
local tmacrap,tmcname=caches.setluanames(writable,name)
if isfile(tmcname) then
loader=loadfile(tmcname)
end
utilities.lua.compile(tmaname,tmcname)
if isfile(tmcname) then
loader=loadfile(tmcname)
end
if not loader then
loader=loadfile(tmaname)
end
end
if loader then
loader=loader()
collectgarbage("step")
return loader
end
end
return false
end
function caches.is_writable(filepath,filename)
local tmaname,tmcname=caches.setluanames(filepath,filename)
return is_writable(tmaname)
end
local saveoptions={ compact=true }
function caches.savedata(filepath,filename,data,raw)
local tmaname,tmcname=caches.setluanames(filepath,filename)
data.cache_uuid=os.uuid()
if caches.fast then
file.savedata(tmaname,table.fastserialize(data,true))
elseif caches.direct then
file.savedata(tmaname,table.serialize(data,true,saveoptions))
else
table.tofile(tmaname,data,true,saveoptions)
end
utilities.lua.compile(tmaname,tmcname)
end
local content_state={}
function caches.contentstate()
return content_state or {}
end
function caches.loadcontent(cachename,dataname,filename)
if not filename then
local name=caches.hashed(cachename)
local full,path=caches.getfirstreadablefile(addsuffix(name,luasuffixes.lua),"trees")
filename=file.join(path,name)
end
local blob=loadfile(addsuffix(filename,luasuffixes.luc)) or loadfile(addsuffix(filename,luasuffixes.lua))
if blob then
local data=blob()
if data and data.content then
if data.type==dataname then
if data.version==resolvers.cacheversion then
content_state[#content_state+1]=data.uuid
if trace_locating then
report_resolvers("loading %a for %a from %a",dataname,cachename,filename)
end
return data.content
else
report_resolvers("skipping %a for %a from %a (version mismatch)",dataname,cachename,filename)
end
else
report_resolvers("skipping %a for %a from %a (datatype mismatch)",dataname,cachename,filename)
end
elseif trace_locating then
report_resolvers("skipping %a for %a from %a (no content)",dataname,cachename,filename)
end
elseif trace_locating then
report_resolvers("skipping %a for %a from %a (invalid file)",dataname,cachename,filename)
end
end
function caches.collapsecontent(content)
for k,v in next,content do
if type(v)=="table" and #v==1 then
content[k]=v[1]
end
end
end
function caches.savecontent(cachename,dataname,content,filename)
if not filename then
local name=caches.hashed(cachename)
local full,path=caches.setfirstwritablefile(addsuffix(name,luasuffixes.lua),"trees")
filename=file.join(path,name)
end
local luaname=addsuffix(filename,luasuffixes.lua)
local lucname=addsuffix(filename,luasuffixes.luc)
if trace_locating then
report_resolvers("preparing %a for %a",dataname,cachename)
end
local data={
type=dataname,
root=cachename,
version=resolvers.cacheversion,
date=os.date("%Y-%m-%d"),
time=os.date("%H:%M:%S"),
content=content,
uuid=os.uuid(),
}
local ok=io.savedata(luaname,table.serialize(data,true))
if ok then
if trace_locating then
report_resolvers("category %a, cachename %a saved in %a",dataname,cachename,luaname)
end
if utilities.lua.compile(luaname,lucname) then
if trace_locating then
report_resolvers("%a compiled to %a",dataname,lucname)
end
return true
else
if trace_locating then
report_resolvers("compiling failed for %a, deleting file %a",dataname,lucname)
end
os.remove(lucname)
end
elseif trace_locating then
report_resolvers("unable to save %a in %a (access error)",dataname,luaname)
end
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["data-met"] = package.loaded["data-met"] or true
-- original size: 5310, stripped down to: 3784
if not modules then modules={} end modules ['data-met']={
version=1.100,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local find,format=string.find,string.format
local sequenced=table.sequenced
local addurlscheme,urlhashed=url.addscheme,url.hashed
local trace_locating=false
local trace_methods=false
trackers.register("resolvers.locating",function(v) trace_methods=v end)
trackers.register("resolvers.methods",function(v) trace_methods=v end)
local report_methods=logs.reporter("resolvers","methods")
local allocate=utilities.storage.allocate
local resolvers=resolvers
local registered={}
local function splitmethod(filename)
if not filename then
return { scheme="unknown",original=filename }
end
if type(filename)=="table" then
return filename
end
filename=file.collapsepath(filename,".")
if not find(filename,"://",1,true) then
return { scheme="file",path=filename,original=filename,filename=filename }
end
local specification=url.hashed(filename)
if not specification.scheme or specification.scheme=="" then
return { scheme="file",path=filename,original=filename,filename=filename }
else
return specification
end
end
resolvers.splitmethod=splitmethod
local function methodhandler(what,first,...)
local method=registered[what]
if method then
local how,namespace=method.how,method.namespace
if how=="uri" or how=="url" then
local specification=splitmethod(first)
local scheme=specification.scheme
local resolver=namespace and namespace[scheme]
if resolver then
if trace_methods then
report_methods("resolving, method %a, how %a, handler %a, argument %a",what,how,scheme,first)
end
return resolver(specification,...)
else
resolver=namespace.default or namespace.file
if resolver then
if trace_methods then
report_methods("resolving, method %a, how %a, handler %a, argument %a",what,how,"default",first)
end
return resolver(specification,...)
elseif trace_methods then
report_methods("resolving, method %a, how %a, handler %a, argument %a",what,how,"unset")
end
end
elseif how=="tag" then
local resolver=namespace and namespace[first]
if resolver then
if trace_methods then
report_methods("resolving, method %a, how %a, tag %a",what,how,first)
end
return resolver(...)
else
resolver=namespace.default or namespace.file
if resolver then
if trace_methods then
report_methods("resolving, method %a, how %a, tag %a",what,how,"default")
end
return resolver(...)
elseif trace_methods then
report_methods("resolving, method %a, how %a, tag %a",what,how,"unset")
end
end
end
else
report_methods("resolving, invalid method %a")
end
end
resolvers.methodhandler=methodhandler
function resolvers.registermethod(name,namespace,how)
registered[name]={ how=how or "tag",namespace=namespace }
namespace["byscheme"]=function(scheme,filename,...)
if scheme=="file" then
return methodhandler(name,filename,...)
else
return methodhandler(name,addurlscheme(filename,scheme),...)
end
end
end
local concatinators=allocate { notfound=file.join }
local locators=allocate { notfound=function() end }
local hashers=allocate { notfound=function() end }
local generators=allocate { notfound=function() end }
resolvers.concatinators=concatinators
resolvers.locators=locators
resolvers.hashers=hashers
resolvers.generators=generators
local registermethod=resolvers.registermethod
registermethod("concatinators",concatinators,"tag")
registermethod("locators",locators,"uri")
registermethod("hashers",hashers,"uri")
registermethod("generators",generators,"uri")
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["data-res"] = package.loaded["data-res"] or true
-- original size: 68195, stripped down to: 43680
if not modules then modules={} end modules ['data-res']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files",
}
local gsub,find,lower,upper,match,gmatch=string.gsub,string.find,string.lower,string.upper,string.match,string.gmatch
local concat,insert,remove=table.concat,table.insert,table.remove
local next,type,rawget=next,type,rawget
local os=os
local P,S,R,C,Cc,Cs,Ct,Carg=lpeg.P,lpeg.S,lpeg.R,lpeg.C,lpeg.Cc,lpeg.Cs,lpeg.Ct,lpeg.Carg
local lpegmatch,lpegpatterns=lpeg.match,lpeg.patterns
local formatters=string.formatters
local filedirname=file.dirname
local filebasename=file.basename
local suffixonly=file.suffixonly
local addsuffix=file.addsuffix
local removesuffix=file.removesuffix
local filejoin=file.join
local collapsepath=file.collapsepath
local joinpath=file.joinpath
local is_qualified_path=file.is_qualified_path
local allocate=utilities.storage.allocate
local settings_to_array=utilities.parsers.settings_to_array
local getcurrentdir=lfs.currentdir
local isfile=lfs.isfile
local isdir=lfs.isdir
local setmetatableindex=table.setmetatableindex
local luasuffixes=utilities.lua.suffixes
local trace_locating=false trackers .register("resolvers.locating",function(v) trace_locating=v end)
local trace_detail=false trackers .register("resolvers.details",function(v) trace_detail=v end)
local trace_expansions=false trackers .register("resolvers.expansions",function(v) trace_expansions=v end)
local trace_paths=false trackers .register("resolvers.paths",function(v) trace_paths=v end)
local resolve_otherwise=true directives.register("resolvers.otherwise",function(v) resolve_otherwise=v end)
local report_resolving=logs.reporter("resolvers","resolving")
local resolvers=resolvers
local expandedpathfromlist=resolvers.expandedpathfromlist
local checkedvariable=resolvers.checkedvariable
local splitconfigurationpath=resolvers.splitconfigurationpath
local methodhandler=resolvers.methodhandler
local filtered=resolvers.filtered_from_content
local lookup=resolvers.get_from_content
local cleanpath=resolvers.cleanpath
local resolveprefix=resolvers.resolve
local initializesetter=utilities.setters.initialize
local ostype,osname,osenv,ossetenv,osgetenv=os.type,os.name,os.env,os.setenv,os.getenv
resolvers.cacheversion="1.100"
resolvers.configbanner=""
resolvers.homedir=environment.homedir
resolvers.criticalvars=allocate { "SELFAUTOLOC","SELFAUTODIR","SELFAUTOPARENT","TEXMFCNF","TEXMF","TEXOS" }
resolvers.luacnfname="texmfcnf.lua"
resolvers.luacnffallback="contextcnf.lua"
resolvers.luacnfstate="unknown"
if environment.default_texmfcnf then
resolvers.luacnfspec="home:texmf/web2c;"..environment.default_texmfcnf
else
resolvers.luacnfspec=concat ({
"home:texmf/web2c",
"selfautoparent:/texmf-local/web2c",
"selfautoparent:/texmf-context/web2c",
"selfautoparent:/texmf-dist/web2c",
"selfautoparent:/texmf/web2c",
},";")
end
local unset_variable="unset"
local formats=resolvers.formats
local suffixes=resolvers.suffixes
local usertypes=resolvers.usertypes
local dangerous=resolvers.dangerous
local suffixmap=resolvers.suffixmap
resolvers.defaultsuffixes={ "tex" }
local instance=nil
function resolvers.setenv(key,value,raw)
if instance then
instance.environment[key]=value
ossetenv(key,raw and value or resolveprefix(value))
end
end
local function getenv(key)
local value=rawget(instance.environment,key)
if value and value~="" then
return value
else
local e=osgetenv(key)
return e~=nil and e~="" and checkedvariable(e) or ""
end
end
resolvers.getenv=getenv
resolvers.env=getenv
local function resolvevariable(k)
return instance.expansions[k]
end
local dollarstripper=lpeg.stripper("$")
local inhibitstripper=P("!")^0*Cs(P(1)^0)
local somevariable=P("$")/""
local somekey=C(R("az","AZ","09","__","--")^1)
local somethingelse=P(";")*((1-S("!{}/\\"))^1*P(";")/"")+P(";")*(P(";")/"")+P(1)
local variableexpander=Cs((somevariable*(somekey/resolvevariable)+somethingelse)^1 )
local cleaner=P("\\")/"/"+P(";")*S("!{}/\\")^0*P(";")^1/";"
local variablecleaner=Cs((cleaner+P(1))^0)
local somevariable=R("az","AZ","09","__","--")^1/resolvevariable
local variable=(P("$")/"")*(somevariable+(P("{")/"")*somevariable*(P("}")/""))
local variableresolver=Cs((variable+P(1))^0)
local function expandedvariable(var)
return lpegmatch(variableexpander,var) or var
end
function resolvers.reset()
if trace_locating then
report_resolving("creating instance")
end
local environment={}
local variables={}
local expansions={}
local order={}
instance={
environment=environment,
variables=variables,
expansions=expansions,
order=order,
files={},
setups={},
found={},
foundintrees={},
hashes={},
hashed={},
pathlists=false,
specification={},
lists={},
data={},
fakepaths={},
remember=true,
diskcache=true,
renewcache=false,
renewtree=false,
loaderror=false,
savelists=true,
pattern=nil,
force_suffixes=true,
pathstack={},
}
setmetatableindex(variables,function(t,k)
local v
for i=1,#order do
v=order[i][k]
if v~=nil then
t[k]=v
return v
end
end
if v==nil then
v=""
end
t[k]=v
return v
end)
setmetatableindex(environment,function(t,k)
local v=osgetenv(k)
if v==nil then
v=variables[k]
end
if v~=nil then
v=checkedvariable(v) or ""
end
v=resolvers.repath(v)
t[k]=v
return v
end)
setmetatableindex(expansions,function(t,k)
local v=environment[k]
if type(v)=="string" then
v=lpegmatch(variableresolver,v)
v=lpegmatch(variablecleaner,v)
end
t[k]=v
return v
end)
end
function resolvers.initialized()
return instance~=nil
end
local function reset_hashes()
instance.lists={}
instance.pathlists=false
instance.found={}
end
local function reset_caches()
instance.lists={}
instance.pathlists=false
end
local slash=P("/")
local pathexpressionpattern=Cs (
Cc("^")*(
Cc("%")*S(".-")+slash^2*P(-1)/"/.*"
+slash^2/"/"+(1-slash)*P(-1)*Cc("/")+P(1)
)^1*Cc("$")
)
local cache={}
local function makepathexpression(str)
if str=="." then
return "^%./$"
else
local c=cache[str]
if not c then
c=lpegmatch(pathexpressionpattern,str)
cache[str]=c
end
return c
end
end
local function reportcriticalvariables(cnfspec)
if trace_locating then
for i=1,#resolvers.criticalvars do
local k=resolvers.criticalvars[i]
local v=resolvers.getenv(k) or "unknown"
report_resolving("variable %a set to %a",k,v)
end
report_resolving()
if cnfspec then
report_resolving("using configuration specification %a",type(cnfspec)=="table" and concat(cnfspec,",") or cnfspec)
end
report_resolving()
end
reportcriticalvariables=function() end
end
local function identify_configuration_files()
local specification=instance.specification
if #specification==0 then
local cnfspec=getenv("TEXMFCNF")
if cnfspec=="" then
cnfspec=resolvers.luacnfspec
resolvers.luacnfstate="default"
else
resolvers.luacnfstate="environment"
end
reportcriticalvariables(cnfspec)
local cnfpaths=expandedpathfromlist(resolvers.splitpath(cnfspec))
local function locatecnf(luacnfname,kind)
for i=1,#cnfpaths do
local filepath=cnfpaths[i]
local filename=collapsepath(filejoin(filepath,luacnfname))
local realname=resolveprefix(filename)
if trace_locating then
local fullpath=gsub(resolveprefix(collapsepath(filepath)),"//","/")
local weirdpath=find(fullpath,"/texmf.+/texmf") or not find(fullpath,"/web2c",1,true)
report_resolving("looking for %s %a on %s path %a from specification %a",
kind,luacnfname,weirdpath and "weird" or "given",fullpath,filepath)
end
if isfile(realname) then
specification[#specification+1]=filename
if trace_locating then
report_resolving("found %s configuration file %a",kind,realname)
end
end
end
end
locatecnf(resolvers.luacnfname,"regular")
if #specification==0 then
locatecnf(resolvers.luacnffallback,"fallback")
end
if trace_locating then
report_resolving()
end
elseif trace_locating then
report_resolving("configuration files already identified")
end
end
local function load_configuration_files()
local specification=instance.specification
if #specification>0 then
local luacnfname=resolvers.luacnfname
for i=1,#specification do
local filename=specification[i]
local pathname=filedirname(filename)
local filename=filejoin(pathname,luacnfname)
local realname=resolveprefix(filename)
local blob=loadfile(realname)
if blob then
local setups=instance.setups
local data=blob()
local parent=data and data.parent
if parent then
local filename=filejoin(pathname,parent)
local realname=resolveprefix(filename)
local blob=loadfile(realname)
if blob then
local parentdata=blob()
if parentdata then
report_resolving("loading configuration file %a",filename)
data=table.merged(parentdata,data)
end
end
end
data=data and data.content
if data then
if trace_locating then
report_resolving("loading configuration file %a",filename)
report_resolving()
end
local variables=data.variables or {}
local warning=false
for k,v in next,data do
local variant=type(v)
if variant=="table" then
initializesetter(filename,k,v)
elseif variables[k]==nil then
if trace_locating and not warning then
report_resolving("variables like %a in configuration file %a should move to the 'variables' subtable",
k,resolveprefix(filename))
warning=true
end
variables[k]=v
end
end
setups[pathname]=variables
if resolvers.luacnfstate=="default" then
local cnfspec=variables["TEXMFCNF"]
if cnfspec then
if trace_locating then
report_resolving("reloading configuration due to TEXMF redefinition")
end
resolvers.setenv("TEXMFCNF",cnfspec)
instance.specification={}
identify_configuration_files()
load_configuration_files()
resolvers.luacnfstate="configuration"
break
end
end
else
if trace_locating then
report_resolving("skipping configuration file %a (no content)",filename)
end
setups[pathname]={}
instance.loaderror=true
end
elseif trace_locating then
report_resolving("skipping configuration file %a (no valid format)",filename)
end
instance.order[#instance.order+1]=instance.setups[pathname]
if instance.loaderror then
break
end
end
elseif trace_locating then
report_resolving("warning: no lua configuration files found")
end
end
function resolvers.configurationfiles()
return instance.specification or {}
end
local function load_file_databases()
instance.loaderror=false
instance.files={}
if not instance.renewcache then
local hashes=instance.hashes
for k=1,#hashes do
local hash=hashes[k]
resolvers.hashers.byscheme(hash.type,hash.name)
if instance.loaderror then break end
end
end
end
local function locate_file_databases()
local texmfpaths=resolvers.expandedpathlist("TEXMF")
if #texmfpaths>0 then
for i=1,#texmfpaths do
local path=collapsepath(texmfpaths[i])
path=gsub(path,"/+$","")
local stripped=lpegmatch(inhibitstripper,path)
if stripped~="" then
local runtime=stripped==path
path=cleanpath(path)
local spec=resolvers.splitmethod(stripped)
if runtime and (spec.noscheme or spec.scheme=="file") then
stripped="tree:///"..stripped
elseif spec.scheme=="cache" or spec.scheme=="file" then
stripped=spec.path
end
if trace_locating then
if runtime then
report_resolving("locating list of %a (runtime) (%s)",path,stripped)
else
report_resolving("locating list of %a (cached)",path)
end
end
methodhandler('locators',stripped)
end
end
if trace_locating then
report_resolving()
end
elseif trace_locating then
report_resolving("no texmf paths are defined (using TEXMF)")
end
end
local function generate_file_databases()
local hashes=instance.hashes
for k=1,#hashes do
local hash=hashes[k]
methodhandler('generators',hash.name)
end
if trace_locating then
report_resolving()
end
end
local function save_file_databases()
for i=1,#instance.hashes do
local hash=instance.hashes[i]
local cachename=hash.name
if hash.cache then
local content=instance.files[cachename]
caches.collapsecontent(content)
if trace_locating then
report_resolving("saving tree %a",cachename)
end
caches.savecontent(cachename,"files",content)
elseif trace_locating then
report_resolving("not saving runtime tree %a",cachename)
end
end
end
function resolvers.renew(hashname)
if hashname and hashname~="" then
local expanded=resolvers.expansion(hashname) or ""
if expanded~="" then
if trace_locating then
report_resolving("identifying tree %a from %a",expanded,hashname)
end
hashname=expanded
else
if trace_locating then
report_resolving("identifying tree %a",hashname)
end
end
local realpath=resolveprefix(hashname)
if isdir(realpath) then
if trace_locating then
report_resolving("using path %a",realpath)
end
methodhandler('generators',hashname)
local content=instance.files[hashname]
caches.collapsecontent(content)
if trace_locating then
report_resolving("saving tree %a",hashname)
end
caches.savecontent(hashname,"files",content)
else
report_resolving("invalid path %a",realpath)
end
end
end
local function load_databases()
locate_file_databases()
if instance.diskcache and not instance.renewcache then
load_file_databases()
if instance.loaderror then
generate_file_databases()
save_file_databases()
end
else
generate_file_databases()
if instance.renewcache then
save_file_databases()
end
end
end
function resolvers.appendhash(type,name,cache)
if not instance.hashed[name] then
if trace_locating then
report_resolving("hash %a appended",name)
end
insert(instance.hashes,{ type=type,name=name,cache=cache } )
instance.hashed[name]=cache
end
end
function resolvers.prependhash(type,name,cache)
if not instance.hashed[name] then
if trace_locating then
report_resolving("hash %a prepended",name)
end
insert(instance.hashes,1,{ type=type,name=name,cache=cache } )
instance.hashed[name]=cache
end
end
function resolvers.extendtexmfvariable(specification)
local t=resolvers.splitpath(getenv("TEXMF"))
insert(t,1,specification)
local newspec=concat(t,",")
if instance.environment["TEXMF"] then
instance.environment["TEXMF"]=newspec
elseif instance.variables["TEXMF"] then
instance.variables["TEXMF"]=newspec
else
end
reset_hashes()
end
function resolvers.splitexpansions()
local ie=instance.expansions
for k,v in next,ie do
local t,tn,h,p={},0,{},splitconfigurationpath(v)
for kk=1,#p do
local vv=p[kk]
if vv~="" and not h[vv] then
tn=tn+1
t[tn]=vv
h[vv]=true
end
end
if #t>1 then
ie[k]=t
else
ie[k]=t[1]
end
end
end
function resolvers.datastate()
return caches.contentstate()
end
function resolvers.variable(name)
local name=name and lpegmatch(dollarstripper,name)
local result=name and instance.variables[name]
return result~=nil and result or ""
end
function resolvers.expansion(name)
local name=name and lpegmatch(dollarstripper,name)
local result=name and instance.expansions[name]
return result~=nil and result or ""
end
function resolvers.unexpandedpathlist(str)
local pth=resolvers.variable(str)
local lst=resolvers.splitpath(pth)
return expandedpathfromlist(lst)
end
function resolvers.unexpandedpath(str)
return joinpath(resolvers.unexpandedpathlist(str))
end
function resolvers.pushpath(name)
local pathstack=instance.pathstack
local lastpath=pathstack[#pathstack]
local pluspath=filedirname(name)
if lastpath then
lastpath=collapsepath(filejoin(lastpath,pluspath))
else
lastpath=collapsepath(pluspath)
end
insert(pathstack,lastpath)
if trace_paths then
report_resolving("pushing path %a",lastpath)
end
end
function resolvers.poppath()
local pathstack=instance.pathstack
if trace_paths and #pathstack>0 then
report_resolving("popping path %a",pathstack[#pathstack])
end
remove(pathstack)
end
function resolvers.stackpath()
local pathstack=instance.pathstack
local currentpath=pathstack[#pathstack]
return currentpath~="" and currentpath or nil
end
local done={}
function resolvers.resetextrapaths()
local ep=instance.extra_paths
if not ep then
done={}
instance.extra_paths={}
elseif #ep>0 then
done={}
reset_caches()
end
end
function resolvers.getextrapaths()
return instance.extra_paths or {}
end
function resolvers.registerextrapath(paths,subpaths)
if not subpaths or subpaths=="" then
if not paths or path=="" then
return
elseif done[paths] then
return
end
end
local paths=settings_to_array(paths)
local subpaths=settings_to_array(subpaths)
local ep=instance.extra_paths or {}
local oldn=#ep
local newn=oldn
local nofpaths=#paths
local nofsubpaths=#subpaths
if nofpaths>0 then
if nofsubpaths>0 then
for i=1,nofpaths do
local p=paths[i]
for j=1,nofsubpaths do
local s=subpaths[j]
local ps=p.."/"..s
if not done[ps] then
newn=newn+1
ep[newn]=cleanpath(ps)
done[ps]=true
end
end
end
else
for i=1,nofpaths do
local p=paths[i]
if not done[p] then
newn=newn+1
ep[newn]=cleanpath(p)
done[p]=true
end
end
end
elseif nofsubpaths>0 then
for i=1,oldn do
for j=1,nofsubpaths do
local s=subpaths[j]
local ps=ep[i].."/"..s
if not done[ps] then
newn=newn+1
ep[newn]=cleanpath(ps)
done[ps]=true
end
end
end
end
if newn>0 then
instance.extra_paths=ep
end
if newn~=oldn then
reset_caches()
end
end
function resolvers.pushextrapath(path)
local paths=settings_to_array(path)
if instance.extra_stack then
insert(instance.extra_stack,1,paths)
else
instance.extra_stack={ paths }
end
reset_caches()
end
function resolvers.popextrapath()
if instance.extra_stack then
reset_caches()
return remove(instance.extra_stack,1)
end
end
local function made_list(instance,list,extra_too)
local done={}
local new={}
local newn=0
local function add(p)
for k=1,#p do
local v=p[k]
if not done[v] then
done[v]=true
newn=newn+1
new[newn]=v
end
end
end
for k=1,#list do
local v=list[k]
if done[v] then
elseif find(v,"^[%.%/]$") then
done[v]=true
newn=newn+1
new[newn]=v
else
break
end
end
if extra_too then
local es=instance.extra_stack
if es and #es>0 then
for k=1,#es do
add(es[k])
end
end
local ep=instance.extra_paths
if ep and #ep>0 then
add(ep)
end
end
add(list)
return new
end
function resolvers.cleanpathlist(str)
local t=resolvers.expandedpathlist(str)
if t then
for i=1,#t do
t[i]=collapsepath(cleanpath(t[i]))
end
end
return t
end
function resolvers.expandpath(str)
return joinpath(resolvers.expandedpathlist(str))
end
function resolvers.expandedpathlist(str,extra_too)
if not str then
return {}
elseif instance.savelists then
str=lpegmatch(dollarstripper,str)
local lists=instance.lists
local lst=lists[str]
if not lst then
local l=made_list(instance,resolvers.splitpath(resolvers.expansion(str)),extra_too)
lst=expandedpathfromlist(l)
lists[str]=lst
end
return lst
else
local lst=resolvers.splitpath(resolvers.expansion(str))
return made_list(instance,expandedpathfromlist(lst),extra_too)
end
end
function resolvers.expandedpathlistfromvariable(str)
str=lpegmatch(dollarstripper,str)
local tmp=resolvers.variableofformatorsuffix(str)
return resolvers.expandedpathlist(tmp~="" and tmp or str)
end
function resolvers.expandpathfromvariable(str)
return joinpath(resolvers.expandedpathlistfromvariable(str))
end
function resolvers.cleanedpathlist(v)
local t=resolvers.expandedpathlist(v)
for i=1,#t do
t[i]=resolvers.resolve(resolvers.cleanpath(t[i]))
end
return t
end
function resolvers.expandbraces(str)
local pth=expandedpathfromlist(resolvers.splitpath(str))
return joinpath(pth)
end
function resolvers.registerfilehash(name,content,someerror)
if content then
instance.files[name]=content
else
instance.files[name]={}
if somerror==true then
instance.loaderror=someerror
end
end
end
function resolvers.getfilehashes()
return instance and instance.files or {}
end
function resolvers.gethashes()
return instance and instance.hashes or {}
end
function resolvers.renewcache()
if instance then
instance.renewcache=true
end
end
local function isreadable(name)
local readable=isfile(name)
if trace_detail then
if readable then
report_resolving("file %a is readable",name)
else
report_resolving("file %a is not readable",name)
end
end
return readable
end
local function collect_files(names)
local filelist={}
local noffiles=0
local function check(hash,root,pathname,path,basename,name)
if not pathname or find(path,pathname) then
local variant=hash.type
local search=filejoin(root,path,name)
local result=methodhandler('concatinators',variant,root,path,name)
if trace_detail then
report_resolving("match: variant %a, search %a, result %a",variant,search,result)
end
noffiles=noffiles+1
filelist[noffiles]={ variant,search,result }
end
end
for k=1,#names do
local filename=names[k]
if trace_detail then
report_resolving("checking name %a",filename)
end
local basename=filebasename(filename)
local pathname=filedirname(filename)
if pathname=="" or find(pathname,"^%.") then
pathname=false
else
pathname=gsub(pathname,"%*",".*")
pathname="/"..pathname.."$"
end
local hashes=instance.hashes
for h=1,#hashes do
local hash=hashes[h]
local hashname=hash.name
local content=hashname and instance.files[hashname]
if content then
if trace_detail then
report_resolving("deep checking %a, base %a, pattern %a",hashname,basename,pathname)
end
local path,name=lookup(content,basename)
if path then
local metadata=content.metadata
local realroot=metadata and metadata.path or hashname
if type(path)=="string" then
check(hash,realroot,pathname,path,basename,name)
else
for i=1,#path do
check(hash,realroot,pathname,path[i],basename,name)
end
end
end
elseif trace_locating then
report_resolving("no match in %a (%s)",hashname,basename)
end
end
end
return noffiles>0 and filelist or nil
end
local fit={}
function resolvers.registerintrees(filename,format,filetype,usedmethod,foundname)
local foundintrees=instance.foundintrees
if usedmethod=="direct" and filename==foundname and fit[foundname] then
else
local collapsed=collapsepath(foundname,true)
local t={
filename=filename,
format=format~="" and format or nil,
filetype=filetype~="" and filetype or nil,
usedmethod=usedmethod,
foundname=foundname,
fullname=collapsed,
}
fit[foundname]=t
foundintrees[#foundintrees+1]=t
end
end
function resolvers.foundintrees()
return instance.foundintrees or {}
end
function resolvers.foundintree(fullname)
local f=fit[fullname]
return f and f.usedmethod=="database"
end
local function can_be_dir(name)
local fakepaths=instance.fakepaths
if not fakepaths[name] then
if isdir(name) then
fakepaths[name]=1
else
fakepaths[name]=2
end
end
return fakepaths[name]==1
end
local preparetreepattern=Cs((P(".")/"%%."+P("-")/"%%-"+P(1))^0*Cc("$"))
local collect_instance_files
local function find_analyze(filename,askedformat,allresults)
local filetype=''
local filesuffix=suffixonly(filename)
local wantedfiles={}
wantedfiles[#wantedfiles+1]=filename
if askedformat=="" then
if filesuffix=="" or not suffixmap[filesuffix] then
local defaultsuffixes=resolvers.defaultsuffixes
local formatofsuffix=resolvers.formatofsuffix
for i=1,#defaultsuffixes do
local forcedname=filename..'.'..defaultsuffixes[i]
wantedfiles[#wantedfiles+1]=forcedname
filetype=formatofsuffix(forcedname)
if trace_locating then
report_resolving("forcing filetype %a",filetype)
end
end
else
filetype=resolvers.formatofsuffix(filename)
if trace_locating then
report_resolving("using suffix based filetype %a",filetype)
end
end
else
if filesuffix=="" or not suffixmap[filesuffix] then
local format_suffixes=suffixes[askedformat]
if format_suffixes then
for i=1,#format_suffixes do
wantedfiles[#wantedfiles+1]=filename.."."..format_suffixes[i]
end
end
end
filetype=askedformat
if trace_locating then
report_resolving("using given filetype %a",filetype)
end
end
return filetype,wantedfiles
end
local function find_direct(filename,allresults)
if not dangerous[askedformat] and isreadable(filename) then
if trace_detail then
report_resolving("file %a found directly",filename)
end
return "direct",{ filename }
end
end
local function find_wildcard(filename,allresults)
if find(filename,'*',1,true) then
if trace_locating then
report_resolving("checking wildcard %a",filename)
end
local result=resolvers.findwildcardfiles(filename)
if result then
return "wildcard",result
end
end
end
local function find_qualified(filename,allresults,askedformat,alsostripped)
if not is_qualified_path(filename) then
return
end
if trace_locating then
report_resolving("checking qualified name %a",filename)
end
if isreadable(filename) then
if trace_detail then
report_resolving("qualified file %a found",filename)
end
return "qualified",{ filename }
end
if trace_detail then
report_resolving("locating qualified file %a",filename)
end
local forcedname,suffix="",suffixonly(filename)
if suffix=="" then
local format_suffixes=askedformat=="" and resolvers.defaultsuffixes or suffixes[askedformat]
if format_suffixes then
for i=1,#format_suffixes do
local s=format_suffixes[i]
forcedname=filename.."."..s
if isreadable(forcedname) then
if trace_locating then
report_resolving("no suffix, forcing format filetype %a",s)
end
return "qualified",{ forcedname }
end
end
end
end
if alsostripped and suffix and suffix~="" then
local basename=filebasename(filename)
local pattern=lpegmatch(preparetreepattern,filename)
local savedformat=askedformat
local format=savedformat or ""
if format=="" then
askedformat=resolvers.formatofsuffix(suffix)
end
if not format then
askedformat="othertextfiles"
end
if basename~=filename then
local resolved=collect_instance_files(basename,askedformat,allresults)
if #resolved==0 then
local lowered=lower(basename)
if filename~=lowered then
resolved=collect_instance_files(lowered,askedformat,allresults)
end
end
resolvers.format=savedformat
if #resolved>0 then
local result={}
for r=1,#resolved do
local rr=resolved[r]
if find(rr,pattern) then
result[#result+1]=rr
end
end
if #result>0 then
return "qualified",result
end
end
end
end
end
local function check_subpath(fname)
if isreadable(fname) then
if trace_detail then
report_resolving("found %a by deep scanning",fname)
end
return fname
end
end
local function makepathlist(list,filetype)
local typespec=resolvers.variableofformat(filetype)
local pathlist=resolvers.expandedpathlist(typespec,filetype and usertypes[filetype])
local entry={}
if pathlist and #pathlist>0 then
for k=1,#pathlist do
local path=pathlist[k]
local prescanned=find(path,'^!!')
local resursive=find(path,'//$')
local pathname=lpegmatch(inhibitstripper,path)
local expression=makepathexpression(pathname)
local barename=gsub(pathname,"/+$","")
barename=resolveprefix(barename)
local scheme=url.hasscheme(barename)
local schemename=gsub(barename,"%.%*$",'')
entry[k]={
path=path,
pathname=pathname,
prescanned=prescanned,
recursive=recursive,
expression=expression,
barename=barename,
scheme=scheme,
schemename=schemename,
}
end
entry.typespec=typespec
list[filetype]=entry
else
list[filetype]=false
end
return entry
end
local function find_intree(filename,filetype,wantedfiles,allresults)
local pathlists=instance.pathlists
if not pathlists then
pathlists=setmetatableindex({},makepathlist)
instance.pathlists=pathlists
end
local pathlist=pathlists[filetype]
if pathlist then
local method="intree"
local filelist=collect_files(wantedfiles)
local dirlist={}
local result={}
if filelist then
for i=1,#filelist do
dirlist[i]=filedirname(filelist[i][3]).."/"
end
end
if trace_detail then
report_resolving("checking filename %a in tree",filename)
end
for k=1,#pathlist do
local entry=pathlist[k]
local path=entry.path
local pathname=entry.pathname
local done=false
if filelist then
local expression=entry.expression
if trace_detail then
report_resolving("using pattern %a for path %a",expression,pathname)
end
for k=1,#filelist do
local fl=filelist[k]
local f=fl[2]
local d=dirlist[k]
if find(d,expression) or find(resolveprefix(d),expression) then
result[#result+1]=resolveprefix(fl[3])
done=true
if allresults then
if trace_detail then
report_resolving("match to %a in hash for file %a and path %a, continue scanning",expression,f,d)
end
else
if trace_detail then
report_resolving("match to %a in hash for file %a and path %a, quit scanning",expression,f,d)
end
break
end
elseif trace_detail then
report_resolving("no match to %a in hash for file %a and path %a",expression,f,d)
end
end
end
if done then
method="database"
else
method="filesystem"
local scheme=entry.scheme
if not scheme or scheme=="file" then
local pname=entry.schemename
if not find(pname,"*",1,true) then
if can_be_dir(pname) then
if not done and not entry.prescanned then
if trace_detail then
report_resolving("quick root scan for %a",pname)
end
for k=1,#wantedfiles do
local w=wantedfiles[k]
local fname=check_subpath(filejoin(pname,w))
if fname then
result[#result+1]=fname
done=true
if not allresults then
break
end
end
end
if not done and entry.recursive then
if trace_detail then
report_resolving("scanning filesystem for %a",pname)
end
local files=resolvers.simplescanfiles(pname,false,true)
for k=1,#wantedfiles do
local w=wantedfiles[k]
local subpath=files[w]
if not subpath or subpath=="" then
elseif type(subpath)=="string" then
local fname=check_subpath(filejoin(pname,subpath,w))
if fname then
result[#result+1]=fname
done=true
if not allresults then
break
end
end
else
for i=1,#subpath do
local sp=subpath[i]
if sp=="" then
else
local fname=check_subpath(filejoin(pname,sp,w))
if fname then
result[#result+1]=fname
done=true
if not allresults then
break
end
end
end
end
if done and not allresults then
break
end
end
end
end
end
end
else
end
else
for k=1,#wantedfiles do
local pname=entry.barename
local fname=methodhandler('finders',pname.."/"..wantedfiles[k])
if fname then
result[#result+1]=fname
done=true
if not allresults then
break
end
end
end
end
end
if done and not allresults then
break
end
end
if #result>0 then
return method,result
end
end
end
local function find_onpath(filename,filetype,wantedfiles,allresults)
if trace_detail then
report_resolving("checking filename %a, filetype %a, wanted files %a",filename,filetype,concat(wantedfiles," | "))
end
local result={}
for k=1,#wantedfiles do
local fname=wantedfiles[k]
if fname and isreadable(fname) then
filename=fname
result[#result+1]=filejoin('.',fname)
if not allresults then
break
end
end
end
if #result>0 then
return "onpath",result
end
end
local function find_otherwise(filename,filetype,wantedfiles,allresults)
local filelist=collect_files(wantedfiles)
local fl=filelist and filelist[1]
if fl then
return "otherwise",{ resolveprefix(fl[3]) }
end
end
collect_instance_files=function(filename,askedformat,allresults)
if not filename or filename=="" then
return {}
end
askedformat=askedformat or ""
filename=collapsepath(filename,".")
filename=gsub(filename,"^%./",getcurrentdir().."/")
if allresults then
local filetype,wantedfiles=find_analyze(filename,askedformat)
local results={
{ find_direct (filename,true) },
{ find_wildcard (filename,true) },
{ find_qualified(filename,true,askedformat) },
{ find_intree (filename,filetype,wantedfiles,true) },
{ find_onpath (filename,filetype,wantedfiles,true) },
{ find_otherwise(filename,filetype,wantedfiles,true) },
}
local result,status,done={},{},{}
for k,r in next,results do
local method,list=r[1],r[2]
if method and list then
for i=1,#list do
local c=collapsepath(list[i])
if not done[c] then
result[#result+1]=c
done[c]=true
end
status[#status+1]=formatters["%-10s: %s"](method,c)
end
end
end
if trace_detail then
report_resolving("lookup status: %s",table.serialize(status,filename))
end
return result,status
else
local method,result,stamp,filetype,wantedfiles
if instance.remember then
if askedformat=="" then
stamp=formatters["%s::%s"](suffixonly(filename),filename)
else
stamp=formatters["%s::%s"](askedformat,filename)
end
result=stamp and instance.found[stamp]
if result then
if trace_locating then
report_resolving("remembered file %a",filename)
end
return result
end
end
method,result=find_direct(filename)
if not result then
method,result=find_wildcard(filename)
if not result then
method,result=find_qualified(filename,false,askedformat)
if not result then
filetype,wantedfiles=find_analyze(filename,askedformat)
method,result=find_intree(filename,filetype,wantedfiles)
if not result then
method,result=find_onpath(filename,filetype,wantedfiles)
if resolve_otherwise and not result then
method,result=find_otherwise(filename,filetype,wantedfiles)
end
end
end
end
end
if result and #result>0 then
local foundname=collapsepath(result[1])
resolvers.registerintrees(filename,askedformat,filetype,method,foundname)
result={ foundname }
else
result={}
end
if stamp then
if trace_locating then
report_resolving("remembering file %a using hash %a",filename,stamp)
end
instance.found[stamp]=result
end
return result
end
end
local function findfiles(filename,filetype,allresults)
if not filename or filename=="" then
return {}
end
local result,status=collect_instance_files(filename,filetype or "",allresults)
if not result or #result==0 then
local lowered=lower(filename)
if filename~=lowered then
result,status=collect_instance_files(lowered,filetype or "",allresults)
end
end
return result or {},status
end
function resolvers.findfiles(filename,filetype)
if not filename or filename=="" then
return ""
else
return findfiles(filename,filetype,true)
end
end
function resolvers.findfile(filename,filetype)
if not filename or filename=="" then
return ""
else
return findfiles(filename,filetype,false)[1] or ""
end
end
function resolvers.findpath(filename,filetype)
return filedirname(findfiles(filename,filetype,false)[1] or "")
end
local function findgivenfiles(filename,allresults)
local base=filebasename(filename)
local result={}
local hashes=instance.hashes
local function okay(hash,path,name)
local found=methodhandler('concatinators',hash.type,hash.name,path,name)
if found and found~="" then
result[#result+1]=resolveprefix(found)
return not allresults
end
end
for k=1,#hashes do
local hash=hashes[k]
local content=instance.files[hash.name]
if content then
local path,name=lookup(content,base)
if not path then
elseif type(path)=="string" then
if okay(hash,path,name) then
return result
end
else
for i=1,#path do
if okay(hash,path[i],name) then
return result
end
end
end
end
end
return result
end
function resolvers.findgivenfiles(filename)
return findgivenfiles(filename,true)
end
function resolvers.findgivenfile(filename)
return findgivenfiles(filename,false)[1] or ""
end
local makewildcard=Cs(
(P("^")^0*P("/")*P(-1)+P(-1))/".*"+(P("^")^0*P("/")/"")^0*(P("*")/".*"+P("-")/"%%-"+P(".")/"%%."+P("?")/"."+P("\\")/"/"+P(1))^0
)
function resolvers.wildcardpattern(pattern)
return lpegmatch(makewildcard,pattern) or pattern
end
local function findwildcardfiles(filename,allresults,result)
local result=result or {}
local base=filebasename(filename)
local dirn=filedirname(filename)
local path=lower(lpegmatch(makewildcard,dirn) or dirn)
local name=lower(lpegmatch(makewildcard,base) or base)
local files=instance.files
if find(name,"*",1,true) then
local hashes=instance.hashes
local function okay(found,path,base,hashname,hashtype)
if find(found,path) then
local full=methodhandler('concatinators',hashtype,hashname,found,base)
if full and full~="" then
result[#result+1]=resolveprefix(full)
return not allresults
end
end
end
for k=1,#hashes do
local hash=hashes[k]
local hashname=hash.name
local hashtype=hash.type
if hashname and hashtype then
for found,base in filtered(files[hashname],name) do
if type(found)=='string' then
if okay(found,path,base,hashname,hashtype) then
break
end
else
for i=1,#found do
if okay(found[i],path,base,hashname,hashtype) then
break
end
end
end
end
end
end
else
local function okayokay(found,path,base,hashname,hashtype)
if find(found,path) then
local full=methodhandler('concatinators',hashtype,hashname,found,base)
if full and full~="" then
result[#result+1]=resolveprefix(full)
return not allresults
end
end
end
local hashes=instance.hashes
for k=1,#hashes do
local hash=hashes[k]
local hashname=hash.name
local hashtype=hash.type
if hashname and hashtype then
local found,base=lookup(content,base)
if not found then
elseif type(found)=='string' then
if okay(found,path,base,hashname,hashtype) then
break
end
else
for i=1,#found do
if okay(found[i],path,base,hashname,hashtype) then
break
end
end
end
end
end
end
return result
end
function resolvers.findwildcardfiles(filename,result)
return findwildcardfiles(filename,true,result)
end
function resolvers.findwildcardfile(filename)
return findwildcardfiles(filename,false)[1] or ""
end
function resolvers.automount()
end
function resolvers.starttiming()
statistics.starttiming(instance)
end
function resolvers.stoptiming()
statistics.stoptiming(instance)
end
function resolvers.load(option)
resolvers.starttiming()
identify_configuration_files()
load_configuration_files()
if option~="nofiles" then
load_databases()
resolvers.automount()
end
resolvers.stoptiming()
local files=instance.files
return files and next(files) and true
end
function resolvers.loadtime()
return statistics.elapsedtime(instance)
end
local function report(str)
if trace_locating then
report_resolving(str)
else
print(str)
end
end
function resolvers.dowithfilesandreport(command,files,...)
if files and #files>0 then
if trace_locating then
report('')
end
if type(files)=="string" then
files={ files }
end
for f=1,#files do
local file=files[f]
local result=command(file,...)
if type(result)=='string' then
report(result)
else
for i=1,#result do
report(result[i])
end
end
end
end
end
function resolvers.showpath(str)
return joinpath(resolvers.expandedpathlist(resolvers.formatofvariable(str)))
end
function resolvers.registerfile(files,name,path)
if files[name] then
if type(files[name])=='string' then
files[name]={ files[name],path }
else
files[name]=path
end
else
files[name]=path
end
end
function resolvers.dowithpath(name,func)
local pathlist=resolvers.expandedpathlist(name)
for i=1,#pathlist do
func("^"..cleanpath(pathlist[i]))
end
end
function resolvers.dowithvariable(name,func)
func(expandedvariable(name))
end
function resolvers.locateformat(name)
local engine=environment.ownmain or "luatex"
local barename=removesuffix(name)
local fullname=addsuffix(barename,"fmt")
local fmtname=caches.getfirstreadablefile(fullname,"formats",engine) or ""
if fmtname=="" then
fmtname=resolvers.findfile(fullname)
fmtname=cleanpath(fmtname)
end
if fmtname~="" then
local barename=removesuffix(fmtname)
local luaname=addsuffix(barename,luasuffixes.lua)
local lucname=addsuffix(barename,luasuffixes.luc)
local luiname=addsuffix(barename,luasuffixes.lui)
if isfile(luiname) then
return barename,luiname
elseif isfile(lucname) then
return barename,lucname
elseif isfile(luaname) then
return barename,luaname
end
end
return nil,nil
end
function resolvers.booleanvariable(str,default)
local b=resolvers.expansion(str)
if b=="" then
return default
else
b=toboolean(b)
return (b==nil and default) or b
end
end
function resolvers.dowithfilesintree(pattern,handle,before,after)
local hashes=instance.hashes
for i=1,#hashes do
local hash=hashes[i]
local blobtype=hash.type
local blobpath=hash.name
if blobtype and blobpath then
local total=0
local checked=0
local done=0
if before then
before(blobtype,blobpath,pattern)
end
for path,name in filtered(instance.files[blobpath],pattern) do
if type(path)=="string" then
checked=checked+1
if handle(blobtype,blobpath,path,name) then
done=done+1
end
else
checked=checked+#path
for i=1,#path do
if handle(blobtype,blobpath,path[i],name) then
done=done+1
end
end
end
end
if after then
after(blobtype,blobpath,pattern,checked,done)
end
end
end
end
local obsolete=resolvers.obsolete or {}
resolvers.obsolete=obsolete
resolvers.find_file=resolvers.findfile obsolete.find_file=resolvers.findfile
resolvers.find_files=resolvers.findfiles obsolete.find_files=resolvers.findfiles
function resolvers.knownvariables(pattern)
if instance then
local environment=instance.environment
local variables=instance.variables
local expansions=instance.expansions
local order=instance.order
local pattern=upper(pattern or "")
local result={}
for i=1,#order do
for key in next,order[i] do
if result[key]==nil and key~="" and (pattern=="" or find(upper(key),pattern)) then
result[key]={
environment=rawget(environment,key),
variable=key,
expansion=expansions[key],
resolved=resolveprefix(expansions[key]),
}
end
end
end
return result
else
return {}
end
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["data-pre"] = package.loaded["data-pre"] or true
-- original size: 4854, stripped down to: 2889
if not modules then modules={} end modules ['data-pre']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local resolvers=resolvers
local prefixes=resolvers.prefixes
local cleanpath=resolvers.cleanpath
local findgivenfile=resolvers.findgivenfile
local expansion=resolvers.expansion
local getenv=resolvers.getenv
local basename=file.basename
local dirname=file.dirname
local joinpath=file.join
local isfile=lfs.isfile
prefixes.environment=function(str)
return cleanpath(expansion(str))
end
local function relative(str,n)
if not isfile(str) then
local pstr="./"..str
if isfile(pstr) then
str=pstr
else
local p="../"
for i=1,n or 2 do
local pstr=p..str
if isfile(pstr) then
str=pstr
break
else
p=p.."../"
end
end
end
end
return cleanpath(str)
end
local function locate(str)
local fullname=findgivenfile(str) or ""
return cleanpath(fullname~="" and fullname or str)
end
prefixes.relative=relative
prefixes.locate=locate
prefixes.auto=function(str)
local fullname=relative(str)
if not isfile(fullname) then
fullname=locate(str)
end
return fullname
end
prefixes.filename=function(str)
local fullname=findgivenfile(str) or ""
return cleanpath(basename((fullname~="" and fullname) or str))
end
prefixes.pathname=function(str)
local fullname=findgivenfile(str) or ""
return cleanpath(dirname((fullname~="" and fullname) or str))
end
prefixes.selfautoloc=function(str)
local pth=getenv('SELFAUTOLOC')
return cleanpath(str and joinpath(pth,str) or pth)
end
prefixes.selfautoparent=function(str)
local pth=getenv('SELFAUTOPARENT')
return cleanpath(str and joinpath(pth,str) or pth)
end
prefixes.selfautodir=function(str)
local pth=getenv('SELFAUTODIR')
return cleanpath(str and joinpath(pth,str) or pth)
end
prefixes.home=function(str)
local pth=getenv('HOME')
return cleanpath(str and joinpath(pth,str) or pth)
end
prefixes.env=prefixes.environment
prefixes.rel=prefixes.relative
prefixes.loc=prefixes.locate
prefixes.kpse=prefixes.locate
prefixes.full=prefixes.locate
prefixes.file=prefixes.filename
prefixes.path=prefixes.pathname
local function toppath()
local inputstack=resolvers.inputstack
if not inputstack then
return "."
end
local pathname=dirname(inputstack[#inputstack] or "")
if pathname=="" then
return "."
else
return pathname
end
end
local function jobpath()
local path=resolvers.stackpath()
if not path or path=="" then
return "."
else
return path
end
end
resolvers.toppath=toppath
resolvers.jobpath=jobpath
prefixes.toppath=function(str) return cleanpath(joinpath(toppath(),str)) end
prefixes.jobpath=function(str) return cleanpath(joinpath(jobpath(),str)) end
resolvers.setdynamic("toppath")
resolvers.setdynamic("jobpath")
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["data-inp"] = package.loaded["data-inp"] or true
-- original size: 910, stripped down to: 818
if not modules then modules={} end modules ['data-inp']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local allocate=utilities.storage.allocate
local resolvers=resolvers
local methodhandler=resolvers.methodhandler
local registermethod=resolvers.registermethod
local finders=allocate { helpers={},notfound=function() end }
local openers=allocate { helpers={},notfound=function() end }
local loaders=allocate { helpers={},notfound=function() return false,nil,0 end }
registermethod("finders",finders,"uri")
registermethod("openers",openers,"uri")
registermethod("loaders",loaders,"uri")
resolvers.finders=finders
resolvers.openers=openers
resolvers.loaders=loaders
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["data-out"] = package.loaded["data-out"] or true
-- original size: 530, stripped down to: 470
if not modules then modules={} end modules ['data-out']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local allocate=utilities.storage.allocate
local resolvers=resolvers
local registermethod=resolvers.registermethod
local savers=allocate { helpers={} }
resolvers.savers=savers
registermethod("savers",savers,"uri")
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["data-fil"] = package.loaded["data-fil"] or true
-- original size: 3863, stripped down to: 3170
if not modules then modules={} end modules ['data-fil']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local trace_locating=false trackers.register("resolvers.locating",function(v) trace_locating=v end)
local report_files=logs.reporter("resolvers","files")
local resolvers=resolvers
local resolveprefix=resolvers.resolve
local finders,openers,loaders,savers=resolvers.finders,resolvers.openers,resolvers.loaders,resolvers.savers
local locators,hashers,generators,concatinators=resolvers.locators,resolvers.hashers,resolvers.generators,resolvers.concatinators
local checkgarbage=utilities.garbagecollector and utilities.garbagecollector.check
function locators.file(specification)
local filename=specification.filename
local realname=resolveprefix(filename)
if realname and realname~='' and lfs.isdir(realname) then
if trace_locating then
report_files("file locator %a found as %a",filename,realname)
end
resolvers.appendhash('file',filename,true)
elseif trace_locating then
report_files("file locator %a not found",filename)
end
end
function hashers.file(specification)
local pathname=specification.filename
local content=caches.loadcontent(pathname,'files')
resolvers.registerfilehash(pathname,content,content==nil)
end
function generators.file(specification)
local pathname=specification.filename
local content=resolvers.scanfiles(pathname,false,true)
resolvers.registerfilehash(pathname,content,true)
end
concatinators.file=file.join
function finders.file(specification,filetype)
local filename=specification.filename
local foundname=resolvers.findfile(filename,filetype)
if foundname and foundname~="" then
if trace_locating then
report_files("file finder: %a found",filename)
end
return foundname
else
if trace_locating then
report_files("file finder: %a not found",filename)
end
return finders.notfound()
end
end
function openers.helpers.textopener(tag,filename,f)
return {
reader=function() return f:read () end,
close=function() logs.show_close(filename) return f:close() end,
}
end
function openers.file(specification,filetype)
local filename=specification.filename
if filename and filename~="" then
local f=io.open(filename,"r")
if f then
if trace_locating then
report_files("file opener: %a opened",filename)
end
return openers.helpers.textopener("file",filename,f)
end
end
if trace_locating then
report_files("file opener: %a not found",filename)
end
return openers.notfound()
end
function loaders.file(specification,filetype)
local filename=specification.filename
if filename and filename~="" then
local f=io.open(filename,"rb")
if f then
logs.show_load(filename)
if trace_locating then
report_files("file loader: %a loaded",filename)
end
local s=f:read("*a")
if checkgarbage then
checkgarbage(#s)
end
f:close()
if s then
return true,s,#s
end
end
end
if trace_locating then
report_files("file loader: %a not found",filename)
end
return loaders.notfound()
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["data-con"] = package.loaded["data-con"] or true
-- original size: 5029, stripped down to: 3432
if not modules then modules={} end modules ['data-con']={
version=1.100,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local format,lower,gsub=string.format,string.lower,string.gsub
local trace_cache=false trackers.register("resolvers.cache",function(v) trace_cache=v end)
local trace_containers=false trackers.register("resolvers.containers",function(v) trace_containers=v end)
local trace_storage=false trackers.register("resolvers.storage",function(v) trace_storage=v end)
containers=containers or {}
local containers=containers
containers.usecache=true
local report_containers=logs.reporter("resolvers","containers")
local allocated={}
local mt={
__index=function(t,k)
if k=="writable" then
local writable=caches.getwritablepath(t.category,t.subcategory) or { "." }
t.writable=writable
return writable
elseif k=="readables" then
local readables=caches.getreadablepaths(t.category,t.subcategory) or { "." }
t.readables=readables
return readables
end
end,
__storage__=true
}
function containers.define(category,subcategory,version,enabled)
if category and subcategory then
local c=allocated[category]
if not c then
c={}
allocated[category]=c
end
local s=c[subcategory]
if not s then
s={
category=category,
subcategory=subcategory,
storage={},
enabled=enabled,
version=version or math.pi,
trace=false,
}
setmetatable(s,mt)
c[subcategory]=s
end
return s
end
end
function containers.is_usable(container,name)
return container.enabled and caches and caches.is_writable(container.writable,name)
end
function containers.is_valid(container,name)
if name and name~="" then
local storage=container.storage[name]
return storage and storage.cache_version==container.version
else
return false
end
end
function containers.read(container,name)
local storage=container.storage
local stored=storage[name]
if not stored and container.enabled and caches and containers.usecache then
stored=caches.loaddata(container.readables,name,container.writable)
if stored and stored.cache_version==container.version then
if trace_cache or trace_containers then
report_containers("action %a, category %a, name %a","load",container.subcategory,name)
end
else
stored=nil
end
storage[name]=stored
elseif stored then
if trace_cache or trace_containers then
report_containers("action %a, category %a, name %a","reuse",container.subcategory,name)
end
end
return stored
end
function containers.write(container,name,data)
if data then
data.cache_version=container.version
if container.enabled and caches then
local unique,shared=data.unique,data.shared
data.unique,data.shared=nil,nil
caches.savedata(container.writable,name,data)
if trace_cache or trace_containers then
report_containers("action %a, category %a, name %a","save",container.subcategory,name)
end
data.unique,data.shared=unique,shared
end
if trace_cache or trace_containers then
report_containers("action %a, category %a, name %a","store",container.subcategory,name)
end
container.storage[name]=data
end
return data
end
function containers.content(container,name)
return container.storage[name]
end
function containers.cleanname(name)
return (gsub(lower(name),"[^%w\128-\255]+","-"))
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["data-use"] = package.loaded["data-use"] or true
-- original size: 4434, stripped down to: 3180
if not modules then modules={} end modules ['data-use']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local format,lower,gsub,find=string.format,string.lower,string.gsub,string.find
local trace_locating=false trackers.register("resolvers.locating",function(v) trace_locating=v end)
local report_mounts=logs.reporter("resolvers","mounts")
local resolvers=resolvers
resolvers.automounted=resolvers.automounted or {}
function resolvers.automount(usecache)
local mountpaths=resolvers.cleanpathlist(resolvers.expansion('TEXMFMOUNT'))
if (not mountpaths or #mountpaths==0) and usecache then
mountpaths=caches.getreadablepaths("mount")
end
if mountpaths and #mountpaths>0 then
resolvers.starttiming()
for k=1,#mountpaths do
local root=mountpaths[k]
local f=io.open(root.."/url.tmi")
if f then
for line in f:lines() do
if line then
if find(line,"^[%%#%-]") then
elseif find(line,"^zip://") then
if trace_locating then
report_mounts("mounting %a",line)
end
table.insert(resolvers.automounted,line)
resolvers.usezipfile(line)
end
end
end
f:close()
end
end
resolvers.stoptiming()
end
end
statistics.register("used config file",function() return caches.configfiles() end)
statistics.register("used cache path",function() return caches.usedpaths() end)
function statistics.savefmtstatus(texname,formatbanner,sourcefile,kind,banner)
local enginebanner=status.banner
if formatbanner and enginebanner and sourcefile then
local luvname=file.replacesuffix(texname,"luv")
local luvdata={
enginebanner=enginebanner,
formatbanner=formatbanner,
sourcehash=md5.hex(io.loaddata(resolvers.findfile(sourcefile)) or "unknown"),
sourcefile=sourcefile,
luaversion=LUAVERSION,
}
io.savedata(luvname,table.serialize(luvdata,true))
lua.registerfinalizer(function()
if jit then
logs.report("format banner","%s lua: %s jit",banner,LUAVERSION)
else
logs.report("format banner","%s lua: %s",banner,LUAVERSION)
end
logs.newline()
end)
end
end
function statistics.checkfmtstatus(texname)
local enginebanner=status.banner
if enginebanner and texname then
local luvname=file.replacesuffix(texname,"luv")
if lfs.isfile(luvname) then
local luv=dofile(luvname)
if luv and luv.sourcefile then
local sourcehash=md5.hex(io.loaddata(resolvers.findfile(luv.sourcefile)) or "unknown")
local luvbanner=luv.enginebanner or "?"
if luvbanner~=enginebanner then
return format("engine mismatch (luv: %s <> bin: %s)",luvbanner,enginebanner)
end
local luvhash=luv.sourcehash or "?"
if luvhash~=sourcehash then
return format("source mismatch (luv: %s <> bin: %s)",luvhash,sourcehash)
end
local luvluaversion=luv.luaversion or 0
if luvluaversion~=LUAVERSION then
return format("lua mismatch (luv: %s <> bin: %s)",luvluaversion,LUAVERSION)
end
else
return "invalid status file"
end
else
return "missing status file"
end
end
return true
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["data-zip"] = package.loaded["data-zip"] or true
-- original size: 10263, stripped down to: 7556
if not modules then modules={} end modules ['data-zip']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local format,find,match=string.format,string.find,string.match
local trace_locating=false trackers.register("resolvers.locating",function(v) trace_locating=v end)
local report_zip=logs.reporter("resolvers","zip")
local resolvers=resolvers
zip=zip or {}
local zip=zip
local archives=zip.archives or {}
zip.archives=archives
local registeredfiles=zip.registeredfiles or {}
zip.registeredfiles=registeredfiles
local zipfiles=utilities.zipfiles
local openzip,closezip,validfile,wholefile,filehandle,traversezip
if zipfiles then
local ipairs=ipairs
openzip=zipfiles.open
closezip=zipfiles.close
validfile=zipfiles.found
wholefile=zipfiles.unzip
traversezip=function(zfile)
return ipairs(zipfiles.list(zfile))
end
local streams=utilities.streams
local openstream=streams.open
local readstring=streams.readstring
local streamsize=streams.size
local metatable={
close=streams.close,
read=function(stream,n)
readstring(stream,n=="*a" and streamsize(stream) or n)
end
}
filehandle=function(zfile,queryname)
local data=wholefile(zfile,queryname)
if data then
local stream=openstream(data)
if stream then
return setmetatableindex(stream,metatable)
end
end
end
else
openzip=zip.open
closezip=zip.close
validfile=function(zfile,queryname)
local dfile=zfile:open(queryname)
if dfile then
dfile:close()
return true
end
return false
end
traversezip=function(zfile)
return z:files()
end
wholefile=function(zfile,queryname)
local dfile=zfile:open(queryname)
if dfile then
local s=dfile:read("*all")
dfile:close()
return s
end
end
filehandle=function(zfile,queryname)
local dfile=zfile:open(queryname)
if dfile then
return dfile
end
end
end
local function validzip(str)
if not find(str,"^zip://") then
return "zip:///"..str
else
return str
end
end
function zip.openarchive(name)
if not name or name=="" then
return nil
else
local arch=archives[name]
if not arch then
local full=resolvers.findfile(name) or ""
arch=full~="" and openzip(full) or false
archives[name]=arch
end
return arch
end
end
function zip.closearchive(name)
if not name or (name=="" and archives[name]) then
closezip(archives[name])
archives[name]=nil
end
end
function resolvers.locators.zip(specification)
local archive=specification.filename
local zipfile=archive and archive~="" and zip.openarchive(archive)
if trace_locating then
if zipfile then
report_zip("locator: archive %a found",archive)
else
report_zip("locator: archive %a not found",archive)
end
end
end
function resolvers.hashers.zip(specification)
local archive=specification.filename
if trace_locating then
report_zip("loading file %a",archive)
end
resolvers.usezipfile(specification.original)
end
function resolvers.concatinators.zip(zipfile,path,name)
if not path or path=="" then
return format('%s?name=%s',zipfile,name)
else
return format('%s?name=%s/%s',zipfile,path,name)
end
end
function resolvers.finders.zip(specification)
local original=specification.original
local archive=specification.filename
if archive then
local query=url.query(specification.query)
local queryname=query.name
if queryname then
local zfile=zip.openarchive(archive)
if zfile then
if trace_locating then
report_zip("finder: archive %a found",archive)
end
if validfile(zfile,queryname) then
if trace_locating then
report_zip("finder: file %a found",queryname)
end
return specification.original
elseif trace_locating then
report_zip("finder: file %a not found",queryname)
end
elseif trace_locating then
report_zip("finder: unknown archive %a",archive)
end
end
end
if trace_locating then
report_zip("finder: %a not found",original)
end
return resolvers.finders.notfound()
end
function resolvers.openers.zip(specification)
local original=specification.original
local archive=specification.filename
if archive then
local query=url.query(specification.query)
local queryname=query.name
if queryname then
local zfile=zip.openarchive(archive)
if zfile then
if trace_locating then
report_zip("opener; archive %a opened",archive)
end
local handle=filehandle(zfile,queryname)
if handle then
if trace_locating then
report_zip("opener: file %a found",queryname)
end
return resolvers.openers.helpers.textopener('zip',original,handle)
elseif trace_locating then
report_zip("opener: file %a not found",queryname)
end
elseif trace_locating then
report_zip("opener: unknown archive %a",archive)
end
end
end
if trace_locating then
report_zip("opener: %a not found",original)
end
return resolvers.openers.notfound()
end
function resolvers.loaders.zip(specification)
local original=specification.original
local archive=specification.filename
if archive then
local query=url.query(specification.query)
local queryname=query.name
if queryname then
local zfile=zip.openarchive(archive)
if zfile then
if trace_locating then
report_zip("loader: archive %a opened",archive)
end
local data=wholefile(zfile,queryname)
if data then
logs.show_load(original)
if trace_locating then
report_zip("loader; file %a loaded",original)
end
return true,data,#data
elseif trace_locating then
report_zip("loader: file %a not found",queryname)
end
elseif trace_locating then
report_zip("loader; unknown archive %a",archive)
end
end
end
if trace_locating then
report_zip("loader: %a not found",original)
end
return resolvers.openers.notfound()
end
function resolvers.usezipfile(archive)
local specification=resolvers.splitmethod(archive)
local archive=specification.filename
if archive and not registeredfiles[archive] then
local z=zip.openarchive(archive)
if z then
local tree=url.query(specification.query).tree or ""
if trace_locating then
report_zip("registering: archive %a",archive)
end
resolvers.starttiming()
resolvers.prependhash('zip',archive)
resolvers.extendtexmfvariable(archive)
registeredfiles[archive]=z
resolvers.registerfilehash(archive,resolvers.registerzipfile(z,tree))
resolvers.stoptiming()
elseif trace_locating then
report_zip("registering: unknown archive %a",archive)
end
elseif trace_locating then
report_zip("registering: archive %a not found",archive)
end
end
function resolvers.registerzipfile(z,tree)
local names={}
local files={}
local remap={}
local n=0
local filter=tree=="" and "^(.+)/(.-)$" or format("^%s/(.+)/(.-)$",tree)
local register=resolvers.registerfile
if trace_locating then
report_zip("registering: using filter %a",filter)
end
for i in traversezip(z) do
local filename=i.filename
local path,name=match(filename,filter)
if not path then
n=n+1
register(names,filename,"")
local usedname=lower(filename)
files[usedname]=""
if usedname~=filename then
remap[usedname]=filename
end
elseif name and name~="" then
n=n+1
register(names,name,path)
local usedname=lower(name)
files[usedname]=path
if usedname~=name then
remap[usedname]=name
end
else
end
end
report_zip("registering: %s files registered",n)
return {
files=files,
remap=remap,
}
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["data-tre"] = package.loaded["data-tre"] or true
-- original size: 8478, stripped down to: 5223
if not modules then modules={} end modules ['data-tre']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local find,gsub,lower=string.find,string.gsub,string.lower
local basename,dirname,joinname=file.basename,file.dirname,file .join
local globdir,isdir,isfile=dir.glob,lfs.isdir,lfs.isfile
local P,lpegmatch=lpeg.P,lpeg.match
local trace_locating=false trackers.register("resolvers.locating",function(v) trace_locating=v end)
local report_trees=logs.reporter("resolvers","trees")
local resolvers=resolvers
local resolveprefix=resolvers.resolve
local notfound=resolvers.finders.notfound
local lookup=resolvers.get_from_content
local collectors={}
local found={}
function resolvers.finders.tree(specification)
local spec=specification.filename
local okay=found[spec]
if okay==nil then
if spec~="" then
local path=dirname(spec)
local name=basename(spec)
if path=="" then
path="."
end
local names=collectors[path]
if not names then
local pattern=find(path,"/%*+$") and path or (path.."/*")
names=globdir(pattern)
collectors[path]=names
end
local pattern="/"..gsub(name,"([%.%-%+])","%%%1").."$"
for i=1,#names do
local fullname=names[i]
if find(fullname,pattern) then
found[spec]=fullname
return fullname
end
end
local pattern=lower(pattern)
for i=1,#names do
local fullname=lower(names[i])
if find(fullname,pattern) then
if isfile(fullname) then
found[spec]=fullname
return fullname
else
break
end
end
end
end
okay=notfound()
found[spec]=okay
end
return okay
end
function resolvers.locators.tree(specification)
local name=specification.filename
local realname=resolveprefix(name)
if realname and realname~='' and isdir(realname) then
if trace_locating then
report_trees("locator %a found",realname)
end
resolvers.appendhash('tree',name,false)
elseif trace_locating then
report_trees("locator %a not found",name)
end
end
function resolvers.hashers.tree(specification)
local name=specification.filename
if trace_locating then
report_trees("analyzing %a",name)
end
resolvers.methodhandler("hashers",name)
resolvers.generators.file(specification)
end
local collectors={}
local splitter=lpeg.splitat("/**/")
local stripper=lpeg.replacer { [P("/")*P("*")^1*P(-1)]="" }
table.setmetatableindex(collectors,function(t,k)
local rootname=lpegmatch(stripper,k)
local dataname=joinname(rootname,"dirlist")
local content=caches.loadcontent(dataname,"files",dataname)
if not content then
content=resolvers.scanfiles(rootname,nil,nil,false,true)
caches.savecontent(dataname,"files",content,dataname)
end
t[k]=content
return content
end)
local function checked(root,p,n)
if p then
if type(p)=="table" then
for i=1,#p do
local fullname=joinname(root,p[i],n)
if isfile(fullname) then
return fullname
end
end
else
local fullname=joinname(root,p,n)
if isfile(fullname) then
return fullname
end
end
end
return notfound()
end
local function resolve(specification)
local filename=specification.filename
if filename~="" then
local root,rest=lpegmatch(splitter,filename)
if root and rest then
local path,name=dirname(rest),basename(rest)
if name~=rest then
local content=collectors[root]
local p,n=lookup(content,name)
if not p then
return notfound()
end
local pattern=".*/"..path.."$"
local istable=type(p)=="table"
if istable then
for i=1,#p do
local pi=p[i]
if pi==path or find(pi,pattern) then
local fullname=joinname(root,pi,n)
if isfile(fullname) then
return fullname
end
end
end
elseif p==path or find(p,pattern) then
local fullname=joinname(root,p,n)
if isfile(fullname) then
return fullname
end
end
local queries=specification.queries
if queries and queries.option=="fileonly" then
return checked(root,p,n)
else
return notfound()
end
end
end
local path,name=dirname(filename),basename(filename)
local root=lpegmatch(stripper,path)
local content=collectors[path]
local p,n=lookup(content,name)
if p then
return checked(root,p,n)
end
end
return notfound()
end
resolvers.finders .dirlist=resolve
resolvers.locators .dirlist=resolvers.locators .tree
resolvers.hashers .dirlist=resolvers.hashers .tree
resolvers.generators.dirlist=resolvers.generators.file
resolvers.openers .dirlist=resolvers.openers .file
resolvers.loaders .dirlist=resolvers.loaders .file
function resolvers.finders.dirfile(specification)
local queries=specification.queries
if queries then
queries.option="fileonly"
else
specification.queries={ option="fileonly" }
end
return resolve(specification)
end
resolvers.locators .dirfile=resolvers.locators .dirlist
resolvers.hashers .dirfile=resolvers.hashers .dirlist
resolvers.generators.dirfile=resolvers.generators.dirlist
resolvers.openers .dirfile=resolvers.openers .dirlist
resolvers.loaders .dirfile=resolvers.loaders .dirlist
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["data-sch"] = package.loaded["data-sch"] or true
-- original size: 6753, stripped down to: 5268
if not modules then modules={} end modules ['data-sch']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local load,tonumber=load,tonumber
local gsub,concat,format=string.gsub,table.concat,string.format
local finders,openers,loaders=resolvers.finders,resolvers.openers,resolvers.loaders
local trace_schemes=false trackers.register("resolvers.schemes",function(v) trace_schemes=v end)
local report_schemes=logs.reporter("resolvers","schemes")
local http=require("socket.http")
local ltn12=require("ltn12")
if mbox then mbox=nil end
local resolvers=resolvers
local schemes=resolvers.schemes or {}
resolvers.schemes=schemes
local cleaners={}
schemes.cleaners=cleaners
local threshold=24*60*60
directives.register("schemes.threshold",function(v) threshold=tonumber(v) or threshold end)
function cleaners.none(specification)
return specification.original
end
function cleaners.strip(specification)
local path,name=file.splitbase(specification.original)
if path=="" then
return (gsub(name,"[^%a%d%.]+","-"))
else
return (gsub((gsub(path,"%.","-").."-"..name),"[^%a%d%.]+","-"))
end
end
function cleaners.md5(specification)
return file.addsuffix(md5.hex(specification.original),file.suffix(specification.path))
end
local cleaner=cleaners.strip
directives.register("schemes.cleanmethod",function(v) cleaner=cleaners[v] or cleaners.strip end)
function resolvers.schemes.cleanname(specification)
local hash=cleaner(specification)
if trace_schemes then
report_schemes("hashing %a to %a",specification.original,hash)
end
return hash
end
local cached={}
local loaded={}
local reused={}
local thresholds={}
local handlers={}
local runner=sandbox.registerrunner {
name="curl resolver",
method="execute",
program="curl",
template="--silent --insecure --create-dirs --output %cachename% %original%",
checkers={
cachename="cache",
original="url",
}
}
local function fetch(specification)
local original=specification.original
local scheme=specification.scheme
local cleanname=schemes.cleanname(specification)
local cachename=caches.setfirstwritablefile(cleanname,"schemes")
if not cached[original] then
statistics.starttiming(schemes)
if not io.exists(cachename) or (os.difftime(os.time(),lfs.attributes(cachename).modification)>(thresholds[protocol] or threshold)) then
cached[original]=cachename
local handler=handlers[scheme]
if handler then
if trace_schemes then
report_schemes("fetching %a, protocol %a, method %a",original,scheme,"built-in")
end
logs.flush()
handler(specification,cachename)
else
if trace_schemes then
report_schemes("fetching %a, protocol %a, method %a",original,scheme,"curl")
end
logs.flush()
runner {
original=original,
cachename=cachename,
}
end
end
if io.exists(cachename) then
cached[original]=cachename
if trace_schemes then
report_schemes("using cached %a, protocol %a, cachename %a",original,scheme,cachename)
end
else
cached[original]=""
if trace_schemes then
report_schemes("using missing %a, protocol %a",original,scheme)
end
end
loaded[scheme]=loaded[scheme]+1
statistics.stoptiming(schemes)
else
if trace_schemes then
report_schemes("reusing %a, protocol %a",original,scheme)
end
reused[scheme]=reused[scheme]+1
end
return cached[original]
end
local function finder(specification,filetype)
return resolvers.methodhandler("finders",fetch(specification),filetype)
end
local opener=openers.file
local loader=loaders.file
local function install(scheme,handler,newthreshold)
handlers [scheme]=handler
loaded [scheme]=0
reused [scheme]=0
finders [scheme]=finder
openers [scheme]=opener
loaders [scheme]=loader
thresholds[scheme]=newthreshold or threshold
end
schemes.install=install
local function http_handler(specification,cachename)
local tempname=cachename..".tmp"
local f=io.open(tempname,"wb")
local status,message=http.request {
url=specification.original,
sink=ltn12.sink.file(f)
}
if not status then
os.remove(tempname)
else
os.remove(cachename)
os.rename(tempname,cachename)
end
return cachename
end
install('http',http_handler)
install('https')
install('ftp')
statistics.register("scheme handling time",function()
local l,r,nl,nr={},{},0,0
for k,v in table.sortedhash(loaded) do
if v>0 then
nl=nl+1
l[nl]=k..":"..v
end
end
for k,v in table.sortedhash(reused) do
if v>0 then
nr=nr+1
r[nr]=k..":"..v
end
end
local n=nl+nr
if n>0 then
l=nl>0 and concat(l) or "none"
r=nr>0 and concat(r) or "none"
return format("%s seconds, %s processed, threshold %s seconds, loaded: %s, reused: %s",
statistics.elapsedtime(schemes),n,threshold,l,r)
else
return nil
end
end)
local httprequest=http.request
local toquery=url.toquery
local function fetchstring(url,data)
local q=data and toquery(data)
if q then
url=url.."?"..q
end
local reply=httprequest(url)
return reply
end
schemes.fetchstring=fetchstring
function schemes.fetchtable(url,data)
local reply=fetchstring(url,data)
if reply then
local s=load("return "..reply)
if s then
return s()
end
end
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["data-lua"] = package.loaded["data-lua"] or true
-- original size: 4207, stripped down to: 3041
if not modules then modules={} end modules ['data-lua']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local package,lpeg=package,lpeg
local gsub=string.gsub
local concat=table.concat
local addsuffix=file.addsuffix
local P,S,Cs,lpegmatch=lpeg.P,lpeg.S,lpeg.Cs,lpeg.match
local luasuffixes={ 'tex','lua' }
local libsuffixes={ 'lib' }
local luaformats={ 'TEXINPUTS','LUAINPUTS' }
local libformats={ 'CLUAINPUTS' }
local helpers=package.helpers or {}
local methods=helpers.methods or {}
local resolvers=resolvers
local resolveprefix=resolvers.resolve
helpers.report=logs.reporter("resolvers","libraries")
trackers.register("resolvers.libraries",function(v) helpers.trace=v end)
trackers.register("resolvers.locating",function(v) helpers.trace=v end)
helpers.sequence={
"already loaded",
"preload table",
"lua variable format",
"lib variable format",
"lua extra list",
"lib extra list",
"path specification",
"cpath specification",
"all in one fallback",
"not loaded",
}
local pattern=Cs(P("!")^0/""*(P("/")*P(-1)/"/"+P("/")^1/"/"+1)^0)
function helpers.cleanpath(path)
return resolveprefix(lpegmatch(pattern,path))
end
local loadedaslib=helpers.loadedaslib
local registerpath=helpers.registerpath
local lualibfile=helpers.lualibfile
local luaformatpaths
local libformatpaths
local function getluaformatpaths()
if not luaformatpaths then
luaformatpaths={}
for i=1,#luaformats do
registerpath("lua format","lua",luaformatpaths,resolvers.expandedpathlistfromvariable(luaformats[i]))
end
end
return luaformatpaths
end
local function getlibformatpaths()
if not libformatpaths then
libformatpaths={}
for i=1,#libformats do
registerpath("lib format","lib",libformatpaths,resolvers.expandedpathlistfromvariable(libformats[i]))
end
end
return libformatpaths
end
local function loadedbyformat(name,rawname,suffixes,islib,what)
local trace=helpers.trace
local report=helpers.report
for i=1,#suffixes do
local format=suffixes[i]
local resolved=resolvers.findfile(name,format) or ""
if trace then
report("%s format, identifying %a using format %a",what,name,format)
end
if resolved~="" then
if trace then
report("%s format, %a found on %a",what,name,resolved)
end
if islib then
return loadedaslib(resolved,rawname)
else
return loadfile(resolved)
end
end
end
end
helpers.loadedbyformat=loadedbyformat
methods["lua variable format"]=function(name)
if helpers.trace then
helpers.report("%s format, checking %s paths","lua",#getluaformatpaths())
end
return loadedbyformat(addsuffix(lualibfile(name),"lua"),name,luasuffixes,false,"lua")
end
methods["lib variable format"]=function(name)
if helpers.trace then
helpers.report("%s format, checking %s paths","lib",#getlibformatpaths())
end
return loadedbyformat(addsuffix(lualibfile(name),os.libsuffix),name,libsuffixes,true,"lib")
end
resolvers.loadlualib=require
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["data-aux"] = package.loaded["data-aux"] or true
-- original size: 2452, stripped down to: 1877
if not modules then modules={} end modules ['data-aux']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local find=string.find
local type,next=type,next
local trace_locating=false trackers.register("resolvers.locating",function(v) trace_locating=v end)
local resolvers=resolvers
local report_scripts=logs.reporter("resolvers","scripts")
function resolvers.updatescript(oldname,newname)
local scriptpath="context/lua"
newname=file.addsuffix(newname,"lua")
local oldscript=resolvers.cleanpath(oldname)
if trace_locating then
report_scripts("to be replaced old script %a",oldscript)
end
local newscripts=resolvers.findfiles(newname) or {}
if #newscripts==0 then
if trace_locating then
report_scripts("unable to locate new script")
end
else
for i=1,#newscripts do
local newscript=resolvers.cleanpath(newscripts[i])
if trace_locating then
report_scripts("checking new script %a",newscript)
end
if oldscript==newscript then
if trace_locating then
report_scripts("old and new script are the same")
end
elseif not find(newscript,scriptpath,1,true) then
if trace_locating then
report_scripts("new script should come from %a",scriptpath)
end
elseif not (find(oldscript,file.removesuffix(newname).."$") or find(oldscript,newname.."$")) then
if trace_locating then
report_scripts("invalid new script name")
end
else
local newdata=io.loaddata(newscript)
if newdata then
if trace_locating then
report_scripts("old script content replaced by new content: %s",oldscript)
end
io.savedata(oldscript,newdata)
break
elseif trace_locating then
report_scripts("unable to load new script")
end
end
end
end
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["data-tmf"] = package.loaded["data-tmf"] or true
-- original size: 2601, stripped down to: 1549
if not modules then modules={} end modules ['data-tmf']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local resolvers=resolvers
local report_tds=logs.reporter("resolvers","tds")
function resolvers.load_tree(tree,resolve)
if type(tree)=="string" and tree~="" then
local getenv,setenv=resolvers.getenv,resolvers.setenv
local texos="texmf-"..os.platform
local oldroot=environment.texroot
local newroot=file.collapsepath(tree)
local newtree=file.join(newroot,texos)
local newpath=file.join(newtree,"bin")
if not lfs.isdir(newtree) then
report_tds("no %a under tree %a",texos,tree)
os.exit()
end
if not lfs.isdir(newpath) then
report_tds("no '%s/bin' under tree %a",texos,tree)
os.exit()
end
local texmfos=newtree
environment.texroot=newroot
environment.texos=texos
environment.texmfos=texmfos
if resolve then
resolvers.luacnfspec=resolvers.resolve(resolvers.luacnfspec)
end
setenv('SELFAUTOPARENT',newroot)
setenv('SELFAUTODIR',newtree)
setenv('SELFAUTOLOC',newpath)
setenv('TEXROOT',newroot)
setenv('TEXOS',texos)
setenv('TEXMFOS',texmfos)
setenv('TEXMFCNF',resolvers.luacnfspec,true)
setenv('PATH',newpath..io.pathseparator..getenv('PATH'))
report_tds("changing from root %a to %a",oldroot,newroot)
report_tds("prepending %a to PATH",newpath)
report_tds("setting TEXMFCNF to %a",resolvers.luacnfspec)
report_tds()
end
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["data-lst"] = package.loaded["data-lst"] or true
-- original size: 1823, stripped down to: 1542
if not modules then modules={} end modules ['data-lst']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local type=type
local concat,sortedhash=table.concat,table.sortedhash
local resolvers=resolvers
local listers=resolvers.listers or {}
resolvers.listers=listers
local resolveprefix=resolvers.resolve
local report_lists=logs.reporter("resolvers","lists")
local report_resolved=logs.reporter("system","resolved")
local function tabstr(str)
if type(str)=='table' then
return concat(str," | ")
else
return str
end
end
function listers.variables(pattern)
local result=resolvers.knownvariables(pattern)
for key,value in sortedhash(result) do
report_lists(key)
report_lists(" env: %s",tabstr(value.environment or "unset"))
report_lists(" var: %s",tabstr(value.variable or "unset"))
report_lists(" exp: %s",tabstr(value.expansion or "unset"))
report_lists(" res: %s",tabstr(value.resolved or "unset"))
end
end
function listers.configurations()
local configurations=resolvers.configurationfiles()
for i=1,#configurations do
report_resolved("file : %s",resolveprefix(configurations[i]))
end
report_resolved("")
local list=resolvers.expandedpathfromlist(resolvers.splitpath(resolvers.luacnfspec))
for i=1,#list do
local li=resolveprefix(list[i])
if lfs.isdir(li) then
report_resolved("path - %s",li)
else
report_resolved("path + %s",li)
end
end
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["util-lib"] = package.loaded["util-lib"] or true
-- original size: 16094, stripped down to: 8443
if not modules then modules={} end modules ['util-lib']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files",
}
local type=type
local next=next
local pcall=pcall
local gsub=string.gsub
local find=string.find
local sort=table.sort
local pathpart=file.pathpart
local nameonly=file.nameonly
local joinfile=file.join
local removesuffix=file.removesuffix
local addsuffix=file.addsuffix
local findfile=resolvers.findfile
local findfiles=resolvers.findfiles
local expandpaths=resolvers.expandedpathlistfromvariable
local qualifiedpath=file.is_qualified_path
local isfile=lfs.isfile
local done=false
local function locate(required,version,trace,report,action)
if type(required)~="string" then
report("provide a proper library name")
return
end
if trace then
report("requiring library %a with version %a",required,version or "any")
end
local found_library=nil
local required_full=gsub(required,"%.","/")
local required_path=pathpart(required_full)
local required_base=nameonly(required_full)
if qualifiedpath(required) then
if isfile(addsuffix(required,os.libsuffix)) then
if trace then
report("qualified name %a found",required)
end
found_library=required
else
if trace then
report("qualified name %a not found",required)
end
end
else
local required_name=required_base.."."..os.libsuffix
local version=type(version)=="string" and version~="" and version or false
local engine="luatex"
if trace and not done then
local list=expandpaths("lib")
for i=1,#list do
report("tds path %i: %s",i,list[i])
end
end
local function found(locate,asked_library,how,...)
if trace then
report("checking %s: %a",how,asked_library)
end
return locate(asked_library,...)
end
local function check(locate,...)
local found=nil
if version then
local asked_library=joinfile(required_path,version,required_name)
if trace then
report("checking %s: %a","with version",asked_library)
end
found=locate(asked_library,...)
end
if not found or found=="" then
local asked_library=joinfile(required_path,required_name)
if trace then
report("checking %s: %a","with version",asked_library)
end
found=locate(asked_library,...)
end
return found and found~="" and found or false
end
local function attempt(checkpattern)
if trace then
report("checking tds lib paths strictly")
end
local found=findfile and check(findfile,"lib")
if found and (not checkpattern or find(found,checkpattern)) then
return found
end
if trace then
report("checking tds lib paths with wildcard")
end
local asked_library=joinfile(required_path,".*",required_name)
if trace then
report("checking %s: %a","latest version",asked_library)
end
local list=findfiles(asked_library,"lib",true)
if list and #list>0 then
sort(list)
local found=list[#list]
if found and (not checkpattern or find(found,checkpattern)) then
return found
end
end
if trace then
report("checking lib paths")
end
package.extralibpath(environment.ownpath)
local paths=package.libpaths()
local pattern="/[^/]+%."..os.libsuffix.."$"
for i=1,#paths do
required_path=gsub(paths[i],pattern,"")
local found=check(lfs.isfound)
if type(found)=="string" and (not checkpattern or find(found,checkpattern)) then
return found
end
end
return false
end
if engine then
if trace then
report("attemp 1, engine %a",engine)
end
found_library=attempt("/"..engine.."/")
if not found_library then
if trace then
report("attemp 2, no engine",asked_library)
end
found_library=attempt()
end
else
found_library=attempt()
end
end
if not found_library then
if trace then
report("not found: %a",required)
end
library=false
else
if trace then
report("found: %a",found_library)
end
local result,message=action(found_library,required_base)
if result then
library=result
else
library=false
report("load error: message %a, library %a",tostring(message or "unknown"),found_library or "no library")
end
end
if trace then
if not library then
report("unknown library: %a",required)
else
report("stored library: %a",required)
end
end
return library or nil
end
do
local report_swiglib=logs.reporter("swiglib")
local trace_swiglib=false
local savedrequire=require
local loadedlibs={}
local loadlib=package.loadlib
local pushdir=dir.push
local popdir=dir.pop
trackers.register("resolvers.swiglib",function(v) trace_swiglib=v end)
function requireswiglib(required,version)
local library=loadedlibs[library]
if library==nil then
local trace_swiglib=trace_swiglib or package.helpers.trace
library=locate(required,version,trace_swiglib,report_swiglib,function(name,base)
pushdir(pathpart(name))
local opener="luaopen_"..base
if trace_swiglib then
report_swiglib("opening: %a with %a",name,opener)
end
local library,message=loadlib(name,opener)
local libtype=type(library)
if libtype=="function" then
library=library()
else
report_swiglib("load error: %a returns %a, message %a, library %a",opener,libtype,(string.gsub(message or "no message","[%s]+$","")),found_library or "no library")
library=false
end
popdir()
return library
end)
loadedlibs[required]=library or false
end
return library
end
function require(name,version)
if find(name,"^swiglib%.") then
return requireswiglib(name,version)
else
return savedrequire(name)
end
end
local swiglibs={}
local initializer="core"
function swiglib(name,version)
local library=swiglibs[name]
if not library then
statistics.starttiming(swiglibs)
if trace_swiglib then
report_swiglib("loading %a",name)
end
if not find(name,"%."..initializer.."$") then
fullname="swiglib."..name.."."..initializer
else
fullname="swiglib."..name
end
library=requireswiglib(fullname,version)
swiglibs[name]=library
statistics.stoptiming(swiglibs)
end
return library
end
statistics.register("used swiglibs",function()
if next(swiglibs) then
return string.format("%s, initial load time %s seconds",table.concat(table.sortedkeys(swiglibs)," "),statistics.elapsedtime(swiglibs))
end
end)
end
if FFISUPPORTED and ffi and ffi.load then
local report_ffilib=logs.reporter("ffilib")
local trace_ffilib=false
local savedffiload=ffi.load
trackers.register("resolvers.ffilib",function(v) trace_ffilib=v end)
local loaded={}
local function locateindeed(name)
name=removesuffix(name)
local l=loaded[name]
if l==nil then
local state,library=pcall(savedffiload,name)
if type(library)=="userdata" then
l=library
elseif type(state)=="userdata" then
l=state
else
l=false
end
loaded[name]=l
elseif trace_ffilib then
report_ffilib("reusing already loaded %a",name)
end
return l
end
local function getlist(required)
local list=directives.value("system.librarynames" )
if type(list)=="table" then
list=list[required]
if type(list)=="table" then
if trace then
report("using lookup list for library %a: % | t",required,list)
end
return list
end
end
return { required }
end
function ffilib(name,version)
name=removesuffix(name)
local l=loaded[name]
if l~=nil then
if trace_ffilib then
report_ffilib("reusing already loaded %a",name)
end
return l
end
local list=getlist(name)
if version=="system" then
for i=1,#list do
local library=locateindeed(list[i])
if type(library)=="userdata" then
return library
end
end
else
for i=1,#list do
local library=locate(list[i],version,trace_ffilib,report_ffilib,locateindeed)
if type(library)=="userdata" then
return library
end
end
end
end
function ffi.load(name)
local list=getlist(name)
for i=1,#list do
local library=ffilib(list[i])
if type(library)=="userdata" then
return library
end
end
if trace_ffilib then
report_ffilib("trying to load %a using normal loader",name)
end
for i=1,#list do
local state,library=pcall(savedffiload,list[i])
if type(library)=="userdata" then
return library
elseif type(state)=="userdata" then
return library
end
end
end
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["luat-sta"] = package.loaded["luat-sta"] or true
-- original size: 5703, stripped down to: 2321
if not modules then modules={} end modules ['luat-sta']={
version=1.001,
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local gmatch,match=string.gmatch,string.match
local type=type
states=states or {}
local states=states
states.data=states.data or {}
local data=states.data
states.hash=states.hash or {}
local hash=states.hash
states.tag=states.tag or ""
states.filename=states.filename or ""
function states.save(filename,tag)
tag=tag or states.tag
filename=file.addsuffix(filename or states.filename,'lus')
io.savedata(filename,
"-- generator : luat-sta.lua\n".."-- state tag : "..tag.."\n\n"..table.serialize(data[tag or states.tag] or {},true)
)
end
function states.load(filename,tag)
states.filename=filename
states.tag=tag or "whatever"
states.filename=file.addsuffix(states.filename,'lus')
data[states.tag],hash[states.tag]=(io.exists(filename) and dofile(filename)) or {},{}
end
local function set_by_tag(tag,key,value,default,persistent)
local d,h=data[tag],hash[tag]
if d then
if type(d)=="table" then
local dkey,hkey=key,key
local pre,post=match(key,"(.+)%.([^%.]+)$")
if pre and post then
for k in gmatch(pre,"[^%.]+") do
local dk=d[k]
if not dk then
dk={}
d[k]=dk
elseif type(dk)=="string" then
break
end
d=dk
end
dkey,hkey=post,key
end
if value==nil then
value=default
elseif value==false then
elseif persistent then
value=value or d[dkey] or default
else
value=value or default
end
d[dkey],h[hkey]=value,value
elseif type(d)=="string" then
data[tag],hash[tag]=value,value
end
end
end
local function get_by_tag(tag,key,default)
local h=hash[tag]
if h and h[key] then
return h[key]
else
local d=data[tag]
if d then
for k in gmatch(key,"[^%.]+") do
local dk=d[k]
if dk~=nil then
d=dk
else
return default
end
end
if d==false then
return false
else
return d or default
end
end
end
end
states.set_by_tag=set_by_tag
states.get_by_tag=get_by_tag
function states.set(key,value,default,persistent)
set_by_tag(states.tag,key,value,default,persistent)
end
function states.get(key,default)
return get_by_tag(states.tag,key,default)
end
end -- of closure
do -- create closure to overcome 200 locals limit
package.loaded["luat-fmt"] = package.loaded["luat-fmt"] or true
-- original size: 9637, stripped down to: 7253
if not modules then modules={} end modules ['luat-fmt']={
version=1.001,
comment="companion to mtxrun",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local format=string.format
local concat=table.concat
local quoted=string.quoted
local luasuffixes=utilities.lua.suffixes
local report_format=logs.reporter("resolvers","formats")
local function primaryflags()
local arguments=environment.arguments
local flags={}
if arguments.silent then
flags[#flags+1]="--interaction=batchmode"
end
return concat(flags," ")
end
local function secondaryflags()
local arguments=environment.arguments
local trackers=arguments.trackers
local directives=arguments.directives
local flags={}
if trackers and trackers~="" then
flags[#flags+1]="--c:trackers="..quoted(trackers)
end
if directives and directives~="" then
flags[#flags+1]="--c:directives="..quoted(directives)
end
if arguments.silent then
flags[#flags+1]="--c:silent"
end
if arguments.errors then
flags[#flags+1]="--c:errors"
end
if arguments.jit then
flags[#flags+1]="--c:jiton"
end
if arguments.ansi then
flags[#flags+1]="--c:ansi"
end
if arguments.strip then
flags[#flags+1]="--c:strip"
end
if arguments.lmtx then
flags[#flags+1]="--c:lmtx"
end
return concat(flags," ")
end
local template=[[--ini %primaryflags% --lua=%luafile% %texfile% %secondaryflags% %dump% %redirect%]]
local checkers={
primaryflags="string",
secondaryflags="string",
luafile="readable",
texfile="readable",
redirect="string",
dump="string",
}
local runners={
luatex=sandbox.registerrunner {
name="make luatex format",
program="luatex",
template=template,
checkers=checkers,
reporter=report_format,
},
luametatex=sandbox.registerrunner {
name="make luametatex format",
program="luametatex",
template=template,
checkers=checkers,
reporter=report_format,
},
luajittex=sandbox.registerrunner {
name="make luajittex format",
program="luajittex",
template=template,
checkers=checkers,
reporter=report_format,
},
}
function environment.make_format(name,arguments)
local engine=environment.ownmain or "luatex"
local silent=environment.arguments.silent
local errors=environment.arguments.errors
local olddir=dir.current()
local path=caches.getwritablepath("formats",engine) or ""
if path~="" then
lfs.chdir(path)
end
report_format("using format path %a",dir.current())
local texsourcename=file.addsuffix(name,"mkiv")
local fulltexsourcename=resolvers.findfile(texsourcename,"tex") or ""
if fulltexsourcename=="" then
texsourcename=file.addsuffix(name,"tex")
fulltexsourcename=resolvers.findfile(texsourcename,"tex") or ""
end
if fulltexsourcename=="" then
report_format("no tex source file with name %a (mkiv or tex)",name)
lfs.chdir(olddir)
return
else
report_format("using tex source file %a",fulltexsourcename)
end
local texsourcepath=dir.expandname(file.dirname(fulltexsourcename))
local specificationname=file.replacesuffix(fulltexsourcename,"lus")
local fullspecificationname=resolvers.findfile(specificationname,"tex") or ""
if fullspecificationname=="" then
specificationname=file.join(texsourcepath,"context.lus")
fullspecificationname=resolvers.findfile(specificationname,"tex") or ""
end
if fullspecificationname=="" then
report_format("unknown stub specification %a",specificationname)
lfs.chdir(olddir)
return
end
local specificationpath=file.dirname(fullspecificationname)
local usedluastub=nil
local usedlualibs=dofile(fullspecificationname)
if type(usedlualibs)=="string" then
usedluastub=file.join(file.dirname(fullspecificationname),usedlualibs)
elseif type(usedlualibs)=="table" then
report_format("using stub specification %a",fullspecificationname)
local texbasename=file.basename(name)
local luastubname=file.addsuffix(texbasename,luasuffixes.lua)
local lucstubname=file.addsuffix(texbasename,luasuffixes.luc)
report_format("creating initialization file %a",luastubname)
utilities.merger.selfcreate(usedlualibs,specificationpath,luastubname)
if utilities.lua.compile(luastubname,lucstubname) and lfs.isfile(lucstubname) then
report_format("using compiled initialization file %a",lucstubname)
usedluastub=lucstubname
else
report_format("using uncompiled initialization file %a",luastubname)
usedluastub=luastubname
end
else
report_format("invalid stub specification %a",fullspecificationname)
lfs.chdir(olddir)
return
end
local specification={
primaryflags=primaryflags(),
secondaryflags=secondaryflags(),
luafile=quoted(usedluastub),
texfile=quoted(fulltexsourcename),
dump=os.platform=="unix" and "\\\\dump" or "\\dump",
}
local runner=runners[engine]
if not runner then
report_format("format %a cannot be generated, no runner available for engine %a",name,engine)
elseif silent then
statistics.starttiming()
specification.redirect="> temp.log"
local result=runner(specification)
local runtime=statistics.stoptiming()
if result~=0 then
print(format("%s silent make > fatal error when making format %q",engine,name))
else
print(format("%s silent make > format %q made in %.3f seconds",engine,name,runtime))
end
os.remove("temp.log")
else
runner(specification)
end
local pattern=file.removesuffix(file.basename(usedluastub)).."-*.mem"
local mp=dir.glob(pattern)
if mp then
for i=1,#mp do
local name=mp[i]
report_format("removing related mplib format %a",file.basename(name))
os.remove(name)
end
end
lfs.chdir(olddir)
end
local template=[[%flags% --fmt=%fmtfile% --lua=%luafile% %texfile% %more%]]
local checkers={
flags="string",
more="string",
fmtfile="readable",
luafile="readable",
texfile="readable",
}
local runners={
luatex=sandbox.registerrunner {
name="run luatex format",
program="luatex",
template=template,
checkers=checkers,
reporter=report_format,
},
luajittex=sandbox.registerrunner {
name="run luajittex format",
program="luajittex",
template=template,
checkers=checkers,
reporter=report_format,
},
}
function environment.run_format(name,data,more)
if name and name~="" then
local engine=environment.ownmain or "luatex"
local barename=file.removesuffix(name)
local fmtname=caches.getfirstreadablefile(file.addsuffix(barename,"fmt"),"formats",engine)
if fmtname=="" then
fmtname=resolvers.findfile(file.addsuffix(barename,"fmt")) or ""
end
fmtname=resolvers.cleanpath(fmtname)
if fmtname=="" then
report_format("no format with name %a",name)
else
local barename=file.removesuffix(name)
local luaname=file.addsuffix(barename,"luc")
if not lfs.isfile(luaname) then
luaname=file.addsuffix(barename,"lua")
end
if not lfs.isfile(luaname) then
report_format("using format name %a",fmtname)
report_format("no luc/lua file with name %a",barename)
else
local runner=runners[engine]
if not runner then
report_format("format %a cannot be run, no runner available for engine %a",name,engine)
else
runner {
flags=primaryflags(),
fmtfile=quoted(barename),
luafile=quoted(luaname),
texfile=quoted(data),
more=more,
}
end
end
end
end
end
end -- of closure
-- 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 util-lib.lua luat-sta.lua luat-fmt.lua
-- skipped libraries : -
-- original bytes : 1019480
-- stripped bytes : 403728
-- end library merge
-- We need this hack till luatex is fixed.
--
-- for k,v in pairs(arg) do print(k,v) end
if arg and (arg[0] == 'luatex' or arg[0] == 'luatex.exe') and arg[1] == "--luaonly" then
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
end
-- End of hack.
local format, gsub, gmatch, match, find = string.format, string.gsub, string.gmatch, string.match, string.find
local concat = table.concat
local ownname = environment and environment.ownname or arg[0] or 'mtxrun.lua'
local ownpath = gsub(match(ownname,"^(.+)[\\/].-$") or ".","\\","/")
local owntree = environment and environment.ownpath or ownpath
local ownlibs = { -- order can be made better
'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', -- code might move to l-string
'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-mbox.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', -- was before trac-set
'trac-pro.lua', -- not really needed
'util-lua.lua', -- indeed here?
'util-deb.lua',
'util-tpl.lua',
'util-sbx.lua',
'util-mrg.lua',
'util-env.lua',
'luat-env.lua', -- can come before inf (as in mkiv)
'util-zip.lua',
'lxml-tab.lua',
'lxml-lpt.lua',
-- 'lxml-ent.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-tex.lua',
-- 'data-bin.lua',
'data-zip.lua',
'data-tre.lua',
'data-sch.lua',
'data-lua.lua',
'data-aux.lua', -- updater
'data-tmf.lua',
'data-lst.lua',
'util-lib.lua', -- swiglib
'luat-sta.lua',
'luat-fmt.lua',
}
-- c:/data/develop/tex-context/tex/texmf-win64/bin/../../texmf-context/tex/context/base/mkiv/data-tmf.lua
-- c:/data/develop/context/sources/data-tmf.lua
local ownlist = {
-- '.',
-- ownpath ,
owntree .. "/../../../../context/sources", -- HH's development path
--
owntree .. "/../../texmf-local/tex/context/base/mkiv",
owntree .. "/../../texmf-context/tex/context/base/mkiv",
owntree .. "/../../texmf/tex/context/base/mkiv",
owntree .. "/../../../texmf-local/tex/context/base/mkiv",
owntree .. "/../../../texmf-context/tex/context/base/mkiv",
owntree .. "/../../../texmf/tex/context/base/mkiv",
--
owntree .. "/../../texmf-local/tex/context/base",
owntree .. "/../../texmf-context/tex/context/base",
owntree .. "/../../texmf/tex/context/base",
owntree .. "/../../../texmf-local/tex/context/base",
owntree .. "/../../../texmf-context/tex/context/base",
owntree .. "/../../../texmf/tex/context/base",
}
if ownpath == "." then table.remove(ownlist,1) end
own = {
name = ownname,
path = ownpath,
tree = owntree,
list = ownlist,
libs = ownlibs,
}
local function locate_libs()
for l=1,#ownlibs do
local lib = ownlibs[l]
for p =1,#ownlist do
local pth = ownlist[p]
local filename = pth .. "/" .. lib
local found = lfs.isfile(filename)
if found then
package.path = package.path .. ";" .. pth .. "/?.lua" -- in case l-* does a require
return pth
end
end
end
end
local function load_libs()
local found = locate_libs()
if found then
for l=1,#ownlibs do
local filename = found .. "/" .. ownlibs[l]
local codeblob = loadfile(filename)
if codeblob then
codeblob()
end
end
else
resolvers = nil
end
end
if not resolvers then
load_libs()
end
if not resolvers then
print("")
print("Mtxrun is unable to start up due to lack of libraries. You may")
print("try to run 'lua mtxrun.lua --selfmerge' in the path where this")
print("script is located (normally under ..../scripts/context/lua) which")
print("will make this script library independent.")
os.exit()
end
-- verbosity
----- e_verbose = environment.arguments["verbose"]
local e_verbose = false
-- some common flags (also passed through environment)
local e_silent = environment.argument("silent")
local e_errors = environment.argument("errors")
local e_noconsole = environment.argument("noconsole")
local e_trackers = environment.argument("trackers")
local e_directives = environment.argument("directives")
local e_experiments = environment.argument("experiments")
local t = { }
if type(e_directives) == "string" then
t[#t+1] = e_directives
end
if type(e_silent) == "string" then
t[#t+1] = format("logs.blocked={%s}",e_silent)
elseif e_silent == true then
t[#t+1] = "logs.blocked"
end
if type(e_errors) == "string" then
t[#t+1] = format("logs.errors={%s}",e_errors)
elseif e_errors == true then
t[#t+1] = "logs.errors"
end
if e_noconsole then
t[#t+1] = format("logs.target=file")
end
if #t > 0 then
e_directives = concat(t,",")
end
if e_trackers then trackers .enable(e_trackers) end
if e_directives then directives .enable(e_directives) end
if e_experiments then experiments.enable(e_experiments) end
if not environment.trackers then environment.trackers = e_trackers end
if not environment.directives then environment.directives = e_directives end
if not environment.experiments then environment.experiments = e_experiments end
--
resolvers.reset()
local helpinfo = [[
<?xml version="1.0" ?>
<application>
<metadata>
<entry name="name">mtxrun</entry>
<entry name="detail">ConTeXt TDS Runner Tool</entry>
<entry name="version">1.33</entry>
</metadata>
<flags>
<category name="basic">
<subcategory>
<flag name="script"><short>run an mtx script (lua prefered method) (<ref name="noquotes"/>), no script gives list</short></flag>
<flag name="evaluate"><short>run code passed on the commandline (between quotes)</short></flag>
<flag name="execute"><short>run a script or program (texmfstart method) (<ref name="noquotes"/>)</short></flag>
<flag name="resolve"><short>resolve prefixed arguments</short></flag>
<flag name="ctxlua"><short>run internally (using preloaded libs)</short></flag>
<flag name="internal"><short>run script using built in libraries (same as <ref name="ctxlua"/>)</short></flag>
<flag name="locate"><short>locate given filename in database (default) or system (<ref name="first"/> <ref name="all"/> <ref name="detail"/>)</short></flag>
</subcategory>
<subcategory>
<flag name="tree" value="pathtotree"><short>use given texmf tree (default file: setuptex.tmf)</short></flag>
<flag name="path" value="runpath"><short>go to given path before execution</short></flag>
<flag name="ifchanged" value="filename"><short>only execute when given file has changed (md checksum)</short></flag>
<flag name="iftouched" value="old,new"><short>only execute when given file has changed (time stamp)</short></flag>
</subcategory>
<subcategory>
<flag name="makestubs"><short>create stubs for (context related) scripts</short></flag>
<flag name="removestubs"><short>remove stubs (context related) scripts</short></flag>
<flag name="stubpath" value="binpath"><short>paths where stubs wil be written</short></flag>
<flag name="windows"><short>create windows (mswin) stubs</short></flag>
<flag name="unix"><short>create unix (linux) stubs</short></flag>
</subcategory>
<subcategory>
<flag name="verbose"><short>give a bit more info</short></flag>
<flag name="trackers" value="list"><short>enable given trackers</short></flag>
<flag name="progname" value="str"><short>format or backend</short></flag>
<flag name="systeminfo" value="str"><short>show current operating system, processor, etc</short></flag>
</subcategory>
<subcategory>
<flag name="edit"><short>launch editor with found file</short></flag>
<flag name="launch"><short>launch files like manuals, assumes os support (<ref name="all"/>,<ref name="list"/>)</short></flag>
</subcategory>
<subcategory>
<flag name="timedrun"><short>run a script and time its run</short></flag>
<flag name="autogenerate"><short>regenerate databases if needed (handy when used to run context in an editor)</short></flag>
</subcategory>
<subcategory>
<flag name="usekpse"><short>use kpse as fallback (when no mkiv and cache installed, often slower)</short></flag>
<flag name="forcekpse"><short>force using kpse (handy when no mkiv and cache installed but less functionality)</short></flag>
</subcategory>
<subcategory>
<flag name="prefixes"><short>show supported prefixes</short></flag>
</subcategory>
<subcategory>
<flag name="generate"><short>generate file database</short></flag>
</subcategory>
<subcategory>
<flag name="variables"><short>show configuration variables</short></flag>
<flag name="configurations"><short>show configuration order</short></flag>
</subcategory>
<subcategory>
<flag name="directives"><short>show (known) directives</short></flag>
<flag name="trackers"><short>show (known) trackers</short></flag>
<flag name="experiments"><short>show (known) experiments</short></flag>
</subcategory>
<subcategory>
<flag name="expand-braces"><short>expand complex variable</short></flag>
<flag name="resolve-path"><short>expand variable (completely resolve paths)</short></flag>
<flag name="expand-path"><short>expand variable (resolve paths)</short></flag>
<flag name="expand-var"><short>expand variable (resolve references)</short></flag>
<flag name="show-path"><short>show path expansion of ...</short></flag>
<flag name="var-value"><short>report value of variable</short></flag>
<flag name="find-file"><short>report file location</short></flag>
<flag name="find-path"><short>report path of file</short></flag>
</subcategory>
<subcategory>
<flag name="pattern" value="string"><short>filter variables</short></flag>
</subcategory>
</category>
</flags>
</application>
]]
local application = logs.application {
name = "mtxrun",
banner = "ConTeXt TDS Runner Tool 1.32",
helpinfo = helpinfo,
}
local report = application.report
messages = messages or { } -- for the moment
runners = runners or { } -- global (might become local)
runners.applications = {
["lua"] = "luatex --luaonly",
["luc"] = "luatex --luaonly",
["pl"] = "perl",
["py"] = "python",
["rb"] = "ruby",
}
runners.suffixes = {
'rb', 'lua', 'py', 'pl'
}
runners.registered = {
texexec = { 'texexec.rb', false }, -- context mkii runner (only tool not to be luafied)
texutil = { 'texutil.rb', true }, -- old perl based index sorter for mkii (old versions need it)
texfont = { 'texfont.pl', true }, -- perl script that makes mkii font metric files
texfind = { 'texfind.pl', false }, -- perltk based tex searching tool, mostly used at pragma
texshow = { 'texshow.pl', false }, -- perltk based context help system, will be luafied
-- texwork = { 'texwork.pl', false }, -- perltk based editing environment, only used at pragma
makempy = { 'makempy.pl', true },
mptopdf = { 'mptopdf.pl', true },
pstopdf = { 'pstopdf.rb', true }, -- converts ps (and some more) images, does some cleaning (replaced)
-- examplex = { 'examplex.rb', false },
concheck = { 'concheck.rb', false },
runtools = { 'runtools.rb', true },
textools = { 'textools.rb', true },
tmftools = { 'tmftools.rb', true },
ctxtools = { 'ctxtools.rb', true },
rlxtools = { 'rlxtools.rb', true },
pdftools = { 'pdftools.rb', true },
mpstools = { 'mpstools.rb', true },
-- exatools = { 'exatools.rb', true },
xmltools = { 'xmltools.rb', true },
-- luatools = { 'luatools.lua', true },
mtxtools = { 'mtxtools.rb', true },
pdftrimwhite = { 'pdftrimwhite.pl', false },
}
runners.launchers = {
windows = { },
unix = { },
}
-- like runners.libpath("framework"): looks on script's subpath
function runners.libpath(...)
package.prepend_libpath(file.dirname(environment.ownscript),...)
package.prepend_libpath(file.dirname(environment.ownname) ,...)
end
function runners.prepare()
local checkname = environment.argument("ifchanged")
if type(checkname) == "string" and checkname ~= "" then
local oldchecksum = file.loadchecksum(checkname)
local newchecksum = file.checksum(checkname)
if oldchecksum == newchecksum then
if e_verbose then
report("file '%s' is unchanged",checkname)
end
return "skip"
elseif e_verbose then
report("file '%s' is changed, processing started",checkname)
end
file.savechecksum(checkname)
end
local touchname = environment.argument("iftouched")
if type(touchname) == "string" and touchname ~= "" then
local oldname, newname = string.splitup(touchname, ",")
if oldname and newname and oldname ~= "" and newname ~= "" then
if not file.needs_updating(oldname,newname) then
if e_verbose then
report("file '%s' and '%s' have same age",oldname,newname)
end
return "skip"
elseif e_verbose then
report("file '%s' is older than '%s'",oldname,newname)
end
end
end
local runpath = environment.argument("path")
if type(runpath) == "string" and not lfs.chdir(runpath) then
report("unable to change to path '%s'",runpath)
return "error"
end
runners.prepare = function() end
return "run"
end
function runners.execute_script(fullname,internal,nosplit)
local noquote = environment.argument("noquotes")
if fullname and fullname ~= "" then
local state = runners.prepare()
if state == 'error' then
return false
elseif state == 'skip' then
return true
elseif state == "run" then
local path, name, suffix = file.splitname(fullname)
local result = ""
if path ~= "" then
result = fullname
elseif name then
name = gsub(name,"^int[%a]*:",function()
internal = true
return ""
end )
name = gsub(name,"^script:","")
if suffix == "" and runners.registered[name] and runners.registered[name][1] then
name = runners.registered[name][1]
suffix = file.suffix(name)
end
if suffix == "" then
-- loop over known suffixes
for _,s in pairs(runners.suffixes) do
result = resolvers.findfile(name .. "." .. s, 'texmfscripts')
if result ~= "" then
break
end
end
elseif runners.applications[suffix] then
result = resolvers.findfile(name, 'texmfscripts')
else
-- maybe look on path
result = resolvers.findfile(name, 'other text files')
end
end
if result and result ~= "" then
if not no_split then
local before, after = environment.splitarguments(fullname) -- already done
environment.arguments_before, environment.arguments_after = before, after
end
if internal then
arg = { } for _,v in pairs(environment.arguments_after) do arg[#arg+1] = v end
environment.ownscript = result
dofile(result)
else
local texmfcnf = resolvers.getenv("TEXMFCNF")
if not texmfcnf or texmfcnf == "" then
texmfcnf = resolvers.expandedpathfromlist(resolvers.splitpath(resolvers.resolve(resolvers.luacnfspec)))
resolvers.setenv("TEXMFCNF",table.concat(texmfcnf,";")) -- for running texexec etc (after tl change to texmf-dist)
end
local binary = runners.applications[file.suffix(result)]
result = string.quoted(string.unquoted(result))
-- if string.match(result,' ') and not string.match(result,"^\".*\"$") then
-- result = '"' .. result .. '"'
-- end
if binary and binary ~= "" then
result = binary .. " " .. result
end
local command = result .. " " .. environment.reconstructcommandline(environment.arguments_after,noquote)
if e_verbose then
report()
report("executing: %s",command)
report()
report()
io.flush()
end
-- no os.exec because otherwise we get the wrong return value
local code = os.execute(command) -- maybe spawn
if code == 0 then
return true
else
if binary then
binary = file.addsuffix(binary,os.binsuffix)
for p in gmatch(os.getenv("PATH"),"[^"..io.pathseparator.."]+") do
if lfs.isfile(file.join(p,binary)) then
return false
end
end
report()
report("This script needs '%s' which seems not to be installed.",binary)
report()
end
return false
end
end
end
end
end
return false
end
function runners.execute_program(fullname)
local noquote = environment.argument("noquotes")
if fullname and fullname ~= "" then
local state = runners.prepare()
if state == 'error' then
return false
elseif state == 'skip' then
return true
elseif state == "run" then
local before, after = environment.splitarguments(fullname)
for k=1,#after do after[k] = resolvers.resolve(after[k]) end
environment.initializearguments(after)
fullname = gsub(fullname,"^bin:","")
local command = fullname .. " " .. (environment.reconstructcommandline(after or "",noquote) or "")
report()
report("executing: %s",command)
report()
report()
io.flush()
local code = os.exec(command) -- (fullname,unpack(after)) does not work / maybe spawn
return code == 0
end
end
return false
end
-- the --usekpse flag will fallback (not default) on kpse (hm, we can better update mtx-stubs)
local windows_stub = '@echo off\013\010setlocal\013\010set ownpath=%%~dp0%%\013\010texlua "%%ownpath%%mtxrun.lua" --usekpse --execute %s %%*\013\010endlocal\013\010'
local unix_stub = '#!/bin/sh\010mtxrun --usekpse --execute %s \"$@\"\010'
function runners.handle_stubs(create)
local stubpath = environment.argument('stubpath') or '.' -- 'auto' no longer subpathssupported
local windows = environment.argument('windows') or environment.argument('mswin') or false
local unix = environment.argument('unix') or environment.argument('linux') or false
if not windows and not unix then
if os.platform == "unix" then
unix = true
else
windows = true
end
end
for _,v in pairs(runners.registered) do
local name, doit = v[1], v[2]
if doit then
local base = gsub(file.basename(name), "%.(.-)$", "")
if create then
if windows then
io.savedata(file.join(stubpath,base..".bat"),format(windows_stub,name))
report("windows stub for '%s' created",base)
end
if unix then
io.savedata(file.join(stubpath,base),format(unix_stub,name))
report("unix stub for '%s' created",base)
end
else
if windows and (os.remove(file.join(stubpath,base..'.bat')) or os.remove(file.join(stubpath,base..'.cmd'))) then
report("windows stub for '%s' removed", base)
end
if unix and (os.remove(file.join(stubpath,base)) or os.remove(file.join(stubpath,base..'.sh'))) then
report("unix stub for '%s' removed",base)
end
end
end
end
end
function runners.resolve_string(filename)
if filename and filename ~= "" then
runners.report_location(resolvers.resolve(filename))
end
end
-- differs from texmfstart where locate appends .com .exe .bat ... todo
function runners.locate_file(filename) -- was given file but only searches in tree
if filename and filename ~= "" then
if environment.argument("first") then
runners.report_location(resolvers.findfile(filename))
-- resolvers.dowithfilesandreport(resolvers.findfile,filename)
elseif environment.argument("all") then
local result, status = resolvers.findfiles(filename)
if status and environment.argument("detail") then
runners.report_location(status)
else
runners.report_location(result)
end
else
runners.report_location(resolvers.findgivenfile(filename))
-- resolvers.dowithfilesandreport(resolvers.findgivenfile,filename)
end
end
end
function runners.locate_platform()
runners.report_location(os.platform)
end
function runners.report_location(result)
if type(result) == "table" then
for i=1,#result do
if i > 1 then
io.write("\n")
end
io.write(result[i])
end
else
io.write(result)
end
end
function runners.edit_script(filename) -- we assume that gvim is present on most systems (todo: also in cnf file)
local editor = os.getenv("MTXRUN_EDITOR") or os.getenv("TEXMFSTART_EDITOR") or os.getenv("EDITOR") or 'gvim'
local rest = resolvers.resolve(filename)
if rest ~= "" then
local command = editor .. " " .. rest
if e_verbose then
report()
report("starting editor: %s",command)
report()
report()
end
os.launch(command)
end
end
function runners.save_script_session(filename, list)
local t = { }
for i=1,#list do
local key = list[i]
t[key] = environment.arguments[key]
end
io.savedata(filename,table.serialize(t,true))
end
function runners.load_script_session(filename)
if lfs.isfile(filename) then
local t = io.loaddata(filename)
if t then
t = loadstring(t)
if t then t = t() end
for key, value in pairs(t) do
environment.arguments[key] = value
end
end
end
end
function resolvers.launch(str)
-- maybe we also need to test on mtxrun.launcher.suffix environment
-- variable or on windows consult the assoc and ftype vars and such
local launchers = runners.launchers[os.platform] if launchers then
local suffix = file.suffix(str) if suffix then
local runner = launchers[suffix] if runner then
str = runner .. " " .. str
end
end
end
os.launch(str)
end
function runners.launch_file(filename)
local allresults = environment.arguments["all"]
local pattern = environment.arguments["pattern"]
local listonly = environment.arguments["list"]
if not pattern or pattern == "" then
pattern = filename
end
if not pattern or pattern == "" then
report("provide name or --pattern=")
else
local t = resolvers.findfiles(pattern,nil,allresults)
if not t or #t == 0 then
t = resolvers.findfiles("*/" .. pattern,nil,allresults)
end
if not t or #t == 0 then
t = resolvers.findfiles("*/" .. pattern .. "*",nil,allresults)
end
if t and #t > 0 then
for i=1,#t do
local name = t[i]
if listonly then
report("% 3i: %-30s %s",i,file.basename(name),file.dirname(name))
else
report("launching: %s",name)
resolvers.launch(name)
if not allresults then
break
end
end
end
if listonly then
io.write("\n")
io.write("\n[select number]\n\n>> ")
local answer = tonumber(io.read())
if answer then
io.write("\n")
local name = t[answer]
if name then
report("launching: %s",name)
resolvers.launch(name)
else
report("invalid number")
end
end
end
else
report("no match for %s", pattern)
end
end
end
local mtxprefixes = {
{ "^mtx%-", "mtx-" },
{ "^mtx%-t%-", "mtx-t-" },
}
function runners.find_mtx_script(filename)
local function found(name)
local path = file.dirname(name)
if path and path ~= "" then
return false
else
local fullname = own and own.path and file.join(own.path,name)
return io.exists(fullname) and fullname
end
end
filename = file.addsuffix(filename,"lua")
local basename = file.removesuffix(file.basename(filename))
local suffix = file.suffix(filename)
-- qualified path, raw name
local fullname = file.is_qualified_path(filename) and io.exists(filename) and filename
if fullname and fullname ~= "" then
return fullname
end
-- current path, raw name
fullname = "./" .. filename
fullname = io.exists(fullname) and fullname
if fullname and fullname ~= "" then
return fullname
end
-- mtx- prefix checking
for i=1,#mtxprefixes do
local mtxprefix = mtxprefixes[i]
mtxprefix = find(filename,mtxprefix[1]) and "" or mtxprefix[2]
-- context namespace, mtx-<filename>
fullname = mtxprefix .. filename
fullname = found(fullname) or resolvers.findfile(fullname)
if fullname and fullname ~= "" then
return fullname
end
-- context namespace, mtx-<filename>s
fullname = mtxprefix .. basename .. "s" .. "." .. suffix
fullname = found(fullname) or resolvers.findfile(fullname)
if fullname and fullname ~= "" then
return fullname
end
-- context namespace, mtx-<filename minus trailing s>
fullname = mtxprefix .. gsub(basename,"s$","") .. "." .. suffix
fullname = found(fullname) or resolvers.findfile(fullname)
if fullname and fullname ~= "" then
return fullname
end
end
-- context namespace, just <filename>
fullname = resolvers.findfile(filename)
return fullname
end
function runners.register_arguments(...)
local arguments = environment.arguments_after
local passedon = { ... }
for i=#passedon,1,-1 do
local pi = passedon[i]
if pi then
table.insert(arguments,1,pi)
end
end
end
function runners.execute_ctx_script(filename,...)
runners.register_arguments(...)
local arguments = environment.arguments_after
local fullname = runners.find_mtx_script(filename) or ""
if file.suffix(fullname) == "cld" then
-- handy in editors where we force --autopdf
report("running cld script: %s",filename)
table.insert(arguments,1,fullname)
table.insert(arguments,"--autopdf")
fullname = runners.find_mtx_script("context") or ""
end
-- retry after generate but only if --autogenerate
if fullname == "" and environment.argument("autogenerate") then -- might become the default
resolvers.renewcache()
trackers.enable("resolvers.locating")
resolvers.load()
--
fullname = runners.find_mtx_script(filename) or ""
end
-- that should do it
if fullname ~= "" then
local state = runners.prepare()
if state == 'error' then
return false
elseif state == 'skip' then
return true
elseif state == "run" then
-- load and save ... kind of undocumented
arg = { } for _,v in pairs(arguments) do arg[#arg+1] = resolvers.resolve(v) end
environment.initializearguments(arg)
local loadname = environment.arguments['load']
if loadname then
if type(loadname) ~= "string" then loadname = file.basename(fullname) end
loadname = file.replacesuffix(loadname,"cfg")
runners.load_script_session(loadname)
end
filename = environment.files[1]
if e_verbose then
report("using script: %s (if --path is used don't run on path where mtxrun lives)\n",fullname)
end
environment.ownscript = fullname
dofile(fullname)
local savename = environment.arguments['save']
if savename then
if type(savename) ~= "string" then savename = file.basename(fullname) end
savename = file.replacesuffix(savename,"cfg")
runners.save_script_session(savename,save_list)
end
return true
end
else
if filename == "" or filename == "help" then
local context = resolvers.findfile("mtx-context.lua")
trackers.enable("resolvers.locating")
if context ~= "" then
local result = dir.glob((gsub(context,"mtx%-context","mtx-*"))) -- () needed
local valid = { }
table.sort(result)
for i=1,#result do
local scriptname = result[i]
local scriptbase = match(scriptname,".*mtx%-([^%-]-)%.lua")
if scriptbase then
local data = io.loaddata(scriptname)
local application = match(data,"local application.-=.-(%{.-%})")
if application then
application = loadstring("return " .. application)
if application then
application = application()
local banner = application.banner
if banner then
local description, version = match(banner,"^(.-) ([%d.]+)$")
if description then
valid[#valid+1] = { scriptbase, version, description }
else
valid[#valid+1] = { scriptbase, "", banner }
end
end
end
end
end
end
if #valid > 0 then
application.identify()
report("no script name given, known scripts:")
report()
for k=1,#valid do
local v = valid[k]
report("%-12s %4s %s",v[1],v[2],v[3])
end
end
else
report("no script name given")
end
else
filename = file.addsuffix(filename,"lua")
if file.is_qualified_path(filename) then
report("unknown script '%s'",filename)
else
report("unknown script '%s' or 'mtx-%s'",filename,filename)
end
end
return false
end
end
function runners.prefixes()
application.identify()
report()
report(concat(resolvers.allprefixes(true)," "))
end
function runners.timedrun(filename) -- just for me
if filename and filename ~= "" then
runners.timed(function() os.execute(filename) end)
end
end
function runners.timed(action)
statistics.timed(action,true)
end
function runners.associate(filename)
os.launch(filename)
end
function runners.evaluate(code,filename) -- for Luigi
if code == "loop" then
while true do
io.write("> ")
local code = io.read()
if code ~= "" then
local temp = string.match(code,"^= (.*)$")
if temp then
code = "print("..temp..")"
end
local compiled, message = loadstring(code)
if type(compiled) ~= "function" then
io.write("! " .. (message or code).."\n")
else
io.write(compiled())
end
end
end
else
if type(code) ~= "string" or code == "" then
code = filename
end
if code ~= "" then
local compiled, message = loadstring(code)
if type(compiled) ~= "function" then
io.write("invalid lua code: " .. (message or code))
return
end
io.write(compiled())
end
end
end
function runners.gethelp(filename)
local url = environment.argument("url")
if url and url ~= "" then
local command = string.gsub(environment.argument("command") or "unknown","^%s*\\*(.-)%s*$","%1")
url = utilities.templates.replace(url,{ command = command })
os.launch(url)
else
report("no --url given")
end
end
function runners.systeminfo()
report("architecture : %s",os.platform or "<unset>")
report("operating system : %s",os.name or "<unset>")
report("file architecture : %s",os.type or "<unset>")
report("binary path : %s",os.selfdir or "<unset>")
report("binary suffix : %s",os.binsuffix or "<unset>")
report("library suffix : %s",os.libsuffix or "<unset>")
end
-- this is a bit dirty ... first we store the first filename and next we
-- split the arguments so that we only see the ones meant for this script
-- ... later we will use the second half
local filename = environment.files[1] or ""
local ok = true
local before, after = environment.splitarguments(filename)
environment.arguments_before, environment.arguments_after = before, after
environment.initializearguments(before)
e_verbose = environment.arguments["verbose"] -- delayed till here (we need the ones before script)
if e_verbose then
trackers.enable("resolvers.locating")
end
-- maybe the unset has to go to this level
local is_mkii_stub = runners.registered[file.removesuffix(file.basename(filename))]
local e_argument = environment.argument
if e_argument("timedlog") then
logs.settimedlog()
end
if e_argument("usekpse") or e_argument("forcekpse") or is_mkii_stub then
resolvers.load_tree(e_argument('tree'),true) -- force resolve of TEXMFCNF
os.setenv("engine","")
os.setenv("progname","")
local remapper = {
otf = "opentype fonts",
ttf = "truetype fonts",
ttc = "truetype fonts",
pfb = "type1 fonts",
other = "other text files",
}
local progname = e_argument("progname") or 'context'
local function kpse_initialized()
texconfig.kpse_init = true
local t = os.clock()
local k = kpse.original.new("luatex",progname)
local dummy = k:find_file("mtxrun.lua") -- so that we're initialized
report("kpse fallback with progname '%s' initialized in %s seconds",progname,os.clock()-t)
kpse_initialized = function() return k end
return k
end
local findfile = resolvers.findfile
local showpath = resolvers.showpath
if e_argument("forcekpse") then
function resolvers.findfile(name,kind)
return (kpse_initialized():find_file(resolvers.cleanpath(name),(kind ~= "" and (remapper[kind] or kind)) or "tex") or "") or ""
end
function resolvers.showpath(name)
return (kpse_initialized():show_path(name)) or ""
end
elseif e_argument("usekpse") or is_mkii_stub then
resolvers.load()
function resolvers.findfile(name,kind)
local found = findfile(name,kind) or ""
if found ~= "" then
return found
else
return (kpse_initialized():find_file(resolvers.cleanpath(name),(kind ~= "" and (remapper[kind] or kind)) or "tex") or "") or ""
end
end
function resolvers.showpath(name)
local found = showpath(name) or ""
if found ~= "" then
return found
else
return (kpse_initialized():show_path(name)) or ""
end
end
end
function runners.loadbase()
end
else
function runners.loadbase(...)
if not resolvers.load(...) then
report("forcing cache reload")
resolvers.renewcache()
trackers.enable("resolvers.locating")
if not resolvers.load(...) then
report("the resolver databases are not present or outdated")
end
end
end
resolvers.load_tree(e_argument('tree'),e_argument("resolve"))
end
-- joke .. reminds me of messing with gigi terminals
do
local a_locale = e_argument("locale")
if a_locale then
-- I really hate this crap but am too tired of discussing it over and over
-- again so for the sake of usiage outside context we will provide ways to
-- use locales in an otherwise supposed to be locale agnostic system. And
-- forget about support in case of interferences.
report()
report(what == "force" and "forcing locale:" or "original locale:")
report()
report(" collate : %s",status.lc_collate or "<unset>")
report(" ctype : %s",status.lc_ctype or "<unset>")
report(" monetary : %s",status.lc_monetary or "<unset>")
report(" numeric : %s",status.lc_numeric or "<unset>")
report(" time : %s",status.lc_time or "<unset>")
report()
end
if a_locale == "force" then
os.setlocale(status.lc_collate ,"collate")
os.setlocale(status.lc_ctype ,"ctype")
os.setlocale(status.lc_monetary,"monetary")
os.setlocale(status.lc_numeric ,"numeric")
os.setlocale(status.lc_time ,"time")
else
function os.setlocale()
end
end
end
if e_argument("ansi") then
logs.setformatters("ansi")
local script = e_argument("script") or e_argument("scripts")
if type(script) == "string" then
logs.writer("]0;"..script.."") -- for Alan to test
end
end
if e_argument("script") or e_argument("scripts") then
-- run a script by loading it (using libs), pass args
if e_argument("nofiledatabase") then
-- handy for mtx-update
else
runners.loadbase()
end
if is_mkii_stub then
ok = runners.execute_script(filename,false,true)
else
ok = runners.execute_ctx_script(filename)
end
elseif e_argument("evaluate") then
runners.evaluate(e_argument("evaluate"),filename)
elseif e_argument("selfmerge") then
-- embed used libraries
runners.loadbase()
local found = locate_libs()
if found then
local mtxrun = resolvers.findfile("mtxrun.lua") -- includes local name
if lfs.isfile(mtxrun) then
utilities.merger.selfmerge(mtxrun,own.libs,{ found })
application.report("runner updated on resolved path: %s",mtxrun)
else
utilities.merger.selfmerge(own.name,own.libs,{ found })
application.report("runner updated on relative path: %s",own.name)
end
end
elseif e_argument("selfclean") then
-- remove embedded libraries
runners.loadbase()
local mtxrun = resolvers.findfile("mtxrun.lua") -- includes local name
if lfs.isfile(mtxrun) then
utilities.merger.selfclean(mtxrun)
application.report("runner cleaned on resolved path: %s",mtxrun)
else
utilities.merger.selfclean(own.name)
application.report("runner cleaned on relative path: %s",own.name)
end
elseif e_argument("selfupdate") then
runners.loadbase()
trackers.enable("resolvers.locating")
resolvers.updatescript(own.name,"mtxrun")
elseif e_argument("ctxlua") or e_argument("internal") then
-- run a script by loading it (using libs)
runners.loadbase()
ok = runners.execute_script(filename,true)
elseif e_argument("execute") then
-- execute script
runners.loadbase()
ok = runners.execute_script(filename)
elseif e_argument("direct") then
-- equals bin:
runners.loadbase()
ok = runners.execute_program(filename)
elseif e_argument("edit") then
-- edit file
runners.loadbase()
runners.edit_script(filename)
elseif e_argument("launch") then
runners.loadbase()
runners.launch_file(filename)
elseif e_argument("associate") then
runners.associate(filename)
elseif e_argument("gethelp") then
runners.gethelp()
elseif e_argument("makestubs") then
-- make stubs (depricated)
runners.handle_stubs(true)
elseif e_argument("removestubs") then
-- remove stub (depricated)
runners.loadbase()
runners.handle_stubs(false)
elseif e_argument("resolve") then
-- resolve string
runners.loadbase()
runners.resolve_string(filename)
elseif e_argument("locate") then
-- locate file (only database)
runners.loadbase()
runners.locate_file(filename)
elseif e_argument("platform") or e_argument("show-platform") then
-- locate platform
runners.loadbase()
runners.locate_platform()
elseif e_argument("prefixes") then
runners.loadbase()
runners.prefixes()
elseif e_argument("timedrun") then
-- locate platform
runners.loadbase()
runners.timedrun(filename)
elseif e_argument("variables") or e_argument("show-variables") or e_argument("expansions") or e_argument("show-expansions") then
-- luatools: runners.execute_ctx_script("mtx-base","--expansions",filename)
resolvers.load("nofiles")
resolvers.listers.variables(e_argument("pattern"))
elseif e_argument("configurations") or e_argument("show-configurations") then
-- luatools: runners.execute_ctx_script("mtx-base","--configurations",filename)
resolvers.load("nofiles")
resolvers.listers.configurations()
elseif e_argument("find-file") then
-- luatools: runners.execute_ctx_script("mtx-base","--find-file",filename)
resolvers.load()
local e_all = e_argument("all")
local e_pattern = e_argument("pattern")
local e_format = e_argument("format")
local finder = e_all and resolvers.findfiles or resolvers.findfile
if not e_pattern then
runners.register_arguments(filename)
environment.initializearguments(environment.arguments_after)
resolvers.dowithfilesandreport(finder,environment.files,e_format)
elseif type(e_pattern) == "string" then
resolvers.dowithfilesandreport(finder,{ e_pattern },e_format)
end
elseif e_argument("find-path") then
-- luatools: runners.execute_ctx_script("mtx-base","--find-path",filename)
resolvers.load()
local path = resolvers.findpath(filename)
if e_verbose then
report(path)
else
print(path)
end
elseif e_argument("expand-braces") then
-- luatools: runners.execute_ctx_script("mtx-base","--expand-braces",filename)
resolvers.load("nofiles")
runners.register_arguments(filename)
environment.initializearguments(environment.arguments_after)
resolvers.dowithfilesandreport(resolvers.expandbraces, environment.files)
elseif e_argument("expand-path") then
-- luatools: runners.execute_ctx_script("mtx-base","--expand-path",filename)
resolvers.load("nofiles")
runners.register_arguments(filename)
environment.initializearguments(environment.arguments_after)
resolvers.dowithfilesandreport(resolvers.expandpath, environment.files)
elseif e_argument("resolve-path") then
resolvers.load("nofiles")
runners.register_arguments(filename)
environment.initializearguments(environment.arguments_after)
resolvers.dowithfilesandreport(resolvers.cleanedpathlist, environment.files)
elseif e_argument("expand-var") or e_argument("expand-variable") then
-- luatools: runners.execute_ctx_script("mtx-base","--expand-var",filename)
resolvers.load("nofiles")
runners.register_arguments(filename)
environment.initializearguments(environment.arguments_after)
resolvers.dowithfilesandreport(resolvers.expansion, environment.files)
elseif e_argument("show-path") or e_argument("path-value") then
-- luatools: runners.execute_ctx_script("mtx-base","--show-path",filename)
resolvers.load("nofiles")
runners.register_arguments(filename)
environment.initializearguments(environment.arguments_after)
resolvers.dowithfilesandreport(resolvers.showpath, environment.files)
elseif e_argument("var-value") or e_argument("show-value") then
-- luatools: runners.execute_ctx_script("mtx-base","--show-value",filename)
resolvers.load("nofiles")
runners.register_arguments(filename)
environment.initializearguments(environment.arguments_after)
resolvers.dowithfilesandreport(resolvers.variable,environment.files)
elseif e_argument("format-path") then
-- luatools: runners.execute_ctx_script("mtx-base","--format-path",filename)
resolvers.load()
report(caches.getwritablepath("format"))
elseif e_argument("pattern") then
-- luatools
runners.execute_ctx_script("mtx-base","--pattern='" .. e_argument("pattern") .. "'",filename)
elseif e_argument("generate") then
-- luatools
if filename and filename ~= "" then
resolvers.load("nofiles")
trackers.enable("resolvers.locating")
resolvers.renew(filename)
else
resolvers.renewcache()
trackers.enable("resolvers.locating")
resolvers.load()
end
e_verbose = true
elseif e_argument("make") or e_argument("ini") or e_argument("compile") then
-- luatools: runners.execute_ctx_script("mtx-base","--make",filename)
resolvers.load()
trackers.enable("resolvers.locating")
environment.make_format(filename)
elseif e_argument("run") then
-- luatools
runners.execute_ctx_script("mtx-base","--run",filename)
elseif e_argument("fmt") then
-- luatools
runners.execute_ctx_script("mtx-base","--fmt",filename)
elseif e_argument("help") and filename=='base' then
-- luatools
runners.execute_ctx_script("mtx-base","--help")
elseif e_argument("version") then
application.version()
application.report("source path",environment.ownbin)
elseif e_argument("directives") then
directives.show()
elseif e_argument("trackers") then
trackers.show()
elseif e_argument("experiments") then
experiments.show()
elseif e_argument("exporthelp") then
runners.loadbase()
application.export(e_argument("exporthelp"),filename)
elseif e_argument("systeminfo") then
runners.systeminfo()
elseif e_argument("locale") then
-- already done
elseif e_argument("help") or filename=='help' or filename == "" then
application.help()
elseif find(filename,"^bin:") then
runners.loadbase()
ok = runners.execute_program(filename)
elseif is_mkii_stub then
-- execute mkii script
runners.loadbase()
ok = runners.execute_script(filename,false,true)
elseif false then
runners.loadbase()
ok = runners.execute_ctx_script(filename)
if not ok then
ok = runners.execute_script(filename)
end
elseif environment.files[1] == 'texmfcnf.lua' then -- so that we don't need to load mtx-base
resolvers.load("nofiles")
resolvers.listers.configurations()
else
runners.loadbase()
runners.execute_ctx_script("mtx-base",filename)
end
if e_verbose then
report()
report("elapsed lua time: %0.3f seconds",os.runtime())
end
if os.type ~= "windows" then
texio.write("\n") -- is this still valid?
end
if ok == false then ok = 1 elseif ok == true or ok == nil then ok = 0 end
-- os.exit(ok,true) -- true forces a cleanup in 5.2+
os.exit(ok) -- true forces a cleanup in 5.2+ but reports a wrong number then