diff options
Diffstat (limited to 'scripts/context/lua/mtxrun.lua')
-rw-r--r-- | scripts/context/lua/mtxrun.lua | 3122 |
1 files changed, 2399 insertions, 723 deletions
diff --git a/scripts/context/lua/mtxrun.lua b/scripts/context/lua/mtxrun.lua index 7b711a88d..873770cac 100644 --- a/scripts/context/lua/mtxrun.lua +++ b/scripts/context/lua/mtxrun.lua @@ -56,7 +56,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-lua"] = package.loaded["l-lua"] or true --- original size: 4734, stripped down to: 2626 +-- original size: 5125, stripped down to: 2881 if not modules then modules={} end modules ['l-lua']={ version=1.001, @@ -162,6 +162,283 @@ if flush then 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 + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["l-sandbox"] = package.loaded["l-sandbox"] or true + +-- original size: 9667, stripped down to: 6678 + +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.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 + 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 @@ -170,7 +447,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-package"] = package.loaded["l-package"] or true --- original size: 10949, stripped down to: 8037 +-- original size: 10587, stripped down to: 7815 if not modules then modules={} end modules ['l-package']={ version=1.001, @@ -460,7 +737,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-lpeg"] = package.loaded["l-lpeg"] or true --- original size: 38185, stripped down to: 20990 +-- original size: 37748, stripped down to: 20111 if not modules then modules={} end modules ['l-lpeg']={ version=1.001, @@ -552,6 +829,7 @@ 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*P(-1)/""+nonwhitespace^1+whitespace^1/" ")^0) local m_collapser=Cs((nonwhitespace^1+whitespace^1/" ")^0) @@ -561,6 +839,7 @@ 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 @@ -1027,27 +1306,7 @@ function lpeg.append(list,pp,delayed,checked) end local p_false=P(false) local p_true=P(true) -local function make(t) - local function making(t) - 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)*making(v) - end - end - end - if t[""] then - p=p+p_true - end - return p - end +local function make(t,rest) local p=p_false local keys=sortedkeys(t) for i=1,#keys do @@ -1058,10 +1317,13 @@ local function make(t) p=p+P(k)*p_true elseif v==false then else - p=p+P(k)*making(v) + p=p+P(k)*make(v,v[""]) end end end + if rest then + p=p+p_true + end return p end local function collapse(t,x) @@ -1264,7 +1526,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-function"] = package.loaded["l-function"] or true --- original size: 372, stripped down to: 329 +-- original size: 361, stripped down to: 322 if not modules then modules={} end modules ['l-functions']={ version=1.001, @@ -1283,7 +1545,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-string"] = package.loaded["l-string"] or true --- original size: 5983, stripped down to: 2959 +-- original size: 6419, stripped down to: 3339 if not modules then modules={} end modules ['l-string']={ version=1.001, @@ -1321,22 +1583,26 @@ 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 lpegmatch(stripper,str) or "" + return str and lpegmatch(stripper,str) or "" end function string.fullstrip(str) - return lpegmatch(fullstripper,str) or "" + return str and lpegmatch(fullstripper,str) or "" end function string.collapsespaces(str) - return lpegmatch(collapser,str) or "" + 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 lpegmatch(longtostring,str) or "" + return str and lpegmatch(longtostring,str) or "" end local pattern=P(" ")^0*P(-1) function string.is_empty(str) - if str=="" then + if not str or str=="" then return true else return lpegmatch(pattern,str) and true or false @@ -1381,6 +1647,21 @@ function string.tformat(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 @@ -1389,7 +1670,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-table"] = package.loaded["l-table"] or true --- original size: 36997, stripped down to: 22376 +-- original size: 39608, stripped down to: 23165 if not modules then modules={} end modules ['l-table']={ version=1.001, @@ -1716,19 +1997,23 @@ function table.fromhash(t) end return hsh end -local noquotes,hexify,handle,compact,inline,functions +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 simple_table(t) +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 @@ -1738,10 +2023,10 @@ local function simple_table(t) if hexify then tt[i]=format("0x%X",v) else - tt[i]=tostring(v) + tt[i]=v end elseif tv=="string" then - tt[i]=format("%q",v) + tt[i]=format("%q",v) elseif tv=="boolean" then tt[i]=v and "true" or "false" else @@ -1749,10 +2034,32 @@ local function simple_table(t) 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) @@ -1786,7 +2093,7 @@ local function do_serialize(root,name,depth,level,indexed) if compact then last=#root for k=1,last do - if root[k]==nil then + if rawget(root,k)==nil then last=k-1 break end @@ -1814,7 +2121,7 @@ local function do_serialize(root,name,depth,level,indexed) if next(v)==nil then handle(format("%s {},",depth)) elseif inline then - local st=simple_table(v) + local st=is_simple_table(v,hexify) if st then handle(format("%s { %s },",depth,concat(st,", "))) else @@ -1851,6 +2158,7 @@ local function do_serialize(root,name,depth,level,indexed) 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)) @@ -1873,6 +2181,7 @@ local function do_serialize(root,name,depth,level,indexed) 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 @@ -1888,13 +2197,14 @@ local function do_serialize(root,name,depth,level,indexed) 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=simple_table(v) + local st=is_simple_table(v,hexify) if st then if tk=="number" then if hexify then @@ -1904,6 +2214,7 @@ local function do_serialize(root,name,depth,level,indexed) 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 @@ -1924,6 +2235,7 @@ local function do_serialize(root,name,depth,level,indexed) 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 @@ -1940,6 +2252,7 @@ local function do_serialize(root,name,depth,level,indexed) 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 @@ -1955,6 +2268,7 @@ local function do_serialize(root,name,depth,level,indexed) 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 @@ -1976,6 +2290,7 @@ local function serialize(_handle,root,name,specification) functions=specification.functions compact=specification.compact inline=specification.inline and compact + metacheck=specification.metacheck if functions==nil then functions=true end @@ -1985,6 +2300,9 @@ local function serialize(_handle,root,name,specification) if inline==nil then inline=compact end + if metacheck==nil then + metacheck=true + end else noquotes=false hexify=false @@ -1992,6 +2310,7 @@ local function serialize(_handle,root,name,specification) compact=true inline=true functions=true + metacheck=true end if tname=="string" then if name=="return" then @@ -2015,7 +2334,7 @@ local function serialize(_handle,root,name,specification) handle("t={") end if root then - if getmetatable(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 @@ -2091,6 +2410,38 @@ local function flattened(t,f,depth) 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={} @@ -2197,6 +2548,12 @@ function table.swapped(t,s) 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 @@ -2365,7 +2722,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-io"] = package.loaded["l-io"] or true --- original size: 9001, stripped down to: 6512 +-- original size: 11790, stripped down to: 6961 if not modules then modules={} end modules ['l-io']={ version=1.001, @@ -2375,6 +2732,7 @@ if not modules then modules={} end modules ['l-io']={ 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 floor=math.floor @@ -2384,50 +2742,56 @@ if string.find(os.getenv("PATH"),";",1,true) then else io.fileseparator,io.pathseparator="/",":" end -local function readall(f) - return f:read("*all") -end +local large=2^24 +local medium=large/16 +local small=medium/8 local function readall(f) local size=f:seek("end") - if size==0 then - return "" - elseif size<1024*1024 then + if size>0 then f:seek("set",0) - return f:read('*all') - else - local done=f:seek("set",0) - local step - if size<1024*1024 then - step=1024*1024 - elseif size>16*1024*1024 then - step=16*1024*1024 - else - step=floor(size/(1024*1024))*1024*1024/8 - end - local data={} - while true do - local r=f:read(step) - if not r then - return concat(data) - else - data[#data+1]=r - end - end + return f:read(size) + else + return "" end end io.readall=readall function io.loaddata(filename,textmode) - local f=io.open(filename,(textmode and 'r') or 'rb') + local f=open(filename,(textmode and 'r') or 'rb') if f then - local data=readall(f) + local size=f:seek("end") + local data=nil + if size>0 then + f:seek("set",0) + data=f:read(size) + end f:close() - if #data>0 then - return data + 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=io.open(filename,"wb") + local f=open(filename,"wb") if f then if type(data)=="table" then f:write(concat(data,joiner or "")) @@ -2437,40 +2801,70 @@ function io.savedata(filename,data,joiner) f:write(data or "") end f:close() - io.flush() + flush() return true else return false end end -function io.loadlines(filename,n) - local f=io.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[#lines+1]=line - else - break +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 - 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 +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=io.open(filename,'rb') + local f=open(filename,'rb') if f then local data=f:read(n or 1024) f:close() @@ -2480,7 +2874,7 @@ function io.loadchunk(filename,n) end end function io.exists(filename) - local f=io.open(filename) + local f=open(filename) if f==nil then return false else @@ -2489,7 +2883,7 @@ function io.exists(filename) end end function io.size(filename) - local f=io.open(filename) + local f=open(filename) if f==nil then return 0 else @@ -2498,11 +2892,11 @@ function io.size(filename) return s end end -function io.noflines(f) +local function noflines(f) if type(f)=="string" then - local f=io.open(filename) + local f=open(filename) if f then - local n=f and io.noflines(f) or 0 + local n=f and noflines(f) or 0 f:close() return n else @@ -2517,6 +2911,7 @@ function io.noflines(f) return n end end +io.noflines=noflines local nextchar={ [ 4]=function(f) return f:read(1,1,1,1) @@ -2594,16 +2989,16 @@ function io.bytes(f,n) end function io.ask(question,default,options) while true do - io.write(question) + write(question) if options then - io.write(format(" [%s]",concat(options,"|"))) + write(format(" [%s]",concat(options,"|"))) end if default then - io.write(format(" [%s]",default)) + write(format(" [%s]",default)) end - io.write(format(" ")) - io.flush() - local answer=io.read() + write(format(" ")) + flush() + local answer=read() answer=gsub(answer,"^%s*(.*)%s*$","%1") if answer=="" and default then return default @@ -2625,7 +3020,7 @@ function io.ask(question,default,options) end end end -local function readnumber(f,n,m) +local function readnumber(f,n,m) if m then f:seek("set",n) n=m @@ -2634,31 +3029,31 @@ local function readnumber(f,n,m) return byte(f:read(1)) elseif n==2 then local a,b=byte(f:read(2),1,2) - return 256*a+b + return 0x100*a+b elseif n==3 then local a,b,c=byte(f:read(3),1,3) - return 256*256*a+256*b+c + return 0x10000*a+0x100*b+c elseif n==4 then local a,b,c,d=byte(f:read(4),1,4) - return 256*256*256*a+256*256*b+256*c+d + return 0x1000000*a+0x10000*b+0x100*c+d elseif n==8 then local a,b=readnumber(f,4),readnumber(f,4) - return 256*a+b + return 0x100*a+b elseif n==12 then local a,b,c=readnumber(f,4),readnumber(f,4),readnumber(f,4) - return 256*256*a+256*b+c + return 0x10000*a+0x100*b+c elseif n==-2 then local b,a=byte(f:read(2),1,2) - return 256*a+b + return 0x100*a+b elseif n==-3 then local c,b,a=byte(f:read(3),1,3) - return 256*256*a+256*b+c + return 0x10000*a+0x100*b+c elseif n==-4 then local d,c,b,a=byte(f:read(4),1,4) - return 256*256*256*a+256*256*b+256*c+d + 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 256*256*256*256*256*256*256*a+256*256*256*256*256*256*b+256*256*256*256*256*c+256*256*256*256*d+256*256*256*e+256*256*f+256*g+h + return 0x100000000000000*a+0x1000000000000*b+0x10000000000*c+0x100000000*d+0x1000000*e+0x10000*f+0x100*g+h else return 0 end @@ -2680,7 +3075,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-number"] = package.loaded["l-number"] or true --- original size: 5146, stripped down to: 2933 +-- original size: 5358, stripped down to: 3177 if not modules then modules={} end modules ['l-number']={ version=1.001, @@ -2693,6 +3088,7 @@ 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 @@ -2817,6 +3213,26 @@ end function number.bits(n) return { bits(n,1) } 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 end -- of closure @@ -2825,7 +3241,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-set"] = package.loaded["l-set"] or true --- original size: 2010, stripped down to: 1186 +-- original size: 1923, stripped down to: 1133 if not modules then modules={} end modules ['l-set']={ version=1.001, @@ -2898,7 +3314,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-os"] = package.loaded["l-os"] or true --- original size: 16390, stripped down to: 9734 +-- original size: 16268, stripped down to: 9246 if not modules then modules={} end modules ['l-os']={ version=1.001, @@ -2974,7 +3390,7 @@ if not os.__getenv__ then end local execute=os.execute local iopopen=io.popen -function os.resultof(command) +local function resultof(command) local handle=iopopen(command,"r") if handle then local result=handle:read("*all") or "" @@ -2984,9 +3400,13 @@ function os.resultof(command) 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 "mswin" + io.fileseparator,io.pathseparator,os.type="\\",";",os.type or "windows" else io.fileseparator,io.pathseparator,os.type="/",":",os.type or "unix" end @@ -3029,17 +3449,6 @@ setmetatable(os,{ __index=function(t,k) return r and r(t,k) or nil end }) local name,platform=os.name or "linux",os.getenv("MTX_PLATFORM") or "" -local function guess() - local architecture=os.resultof("uname -m") or "" - if architecture~="" then - return architecture - end - architecture=os.getenv("HOSTTYPE") or "" - if architecture~="" then - return architecture - end - return os.resultof("echo $HOSTTYPE") or "" -end if platform~="" then os.platform=platform elseif os.type=="windows" then @@ -3056,7 +3465,7 @@ elseif os.type=="windows" then end elseif name=="linux" then function resolvers.platform(t,k) - local platform,architecture="",os.getenv("HOSTTYPE") or os.resultof("uname -m") or "" + local platform,architecture="",os.getenv("HOSTTYPE") or resultof("uname -m") or "" if find(architecture,"x86_64",1,true) then platform="linux-64" elseif find(architecture,"ppc",1,true) then @@ -3070,7 +3479,7 @@ elseif name=="linux" then end elseif name=="macosx" then function resolvers.platform(t,k) - local platform,architecture="",os.resultof("echo $HOSTTYPE") or "" + local platform,architecture="",resultof("echo $HOSTTYPE") or "" if architecture=="" then platform="osx-intel" elseif find(architecture,"i386",1,true) then @@ -3086,7 +3495,7 @@ elseif name=="macosx" then end elseif name=="sunos" then function resolvers.platform(t,k) - local platform,architecture="",os.resultof("uname -m") or "" + local platform,architecture="",resultof("uname -m") or "" if find(architecture,"sparc",1,true) then platform="solaris-sparc" else @@ -3098,7 +3507,7 @@ elseif name=="sunos" then end elseif name=="freebsd" then function resolvers.platform(t,k) - local platform,architecture="",os.resultof("uname -m") or "" + local platform,architecture="",resultof("uname -m") or "" if find(architecture,"amd64",1,true) then platform="freebsd-amd64" else @@ -3110,7 +3519,7 @@ elseif name=="freebsd" then end elseif name=="kfreebsd" then function resolvers.platform(t,k) - local platform,architecture="",os.getenv("HOSTTYPE") or os.resultof("uname -m") or "" + local platform,architecture="",os.getenv("HOSTTYPE") or resultof("uname -m") or "" if find(architecture,"x86_64",1,true) then platform="kfreebsd-amd64" else @@ -3241,7 +3650,7 @@ if not os.sleep then end end local function isleapyear(year) - return (year%400==0) or ((year%100~=0) and (year%4==0)) + 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 } @@ -3280,7 +3689,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-file"] = package.loaded["l-file"] or true --- original size: 21648, stripped down to: 10238 +-- original size: 20997, stripped down to: 9986 if not modules then modules={} end modules ['l-file']={ version=1.001, @@ -3617,13 +4026,15 @@ function file.robustname(str,strict) end end end -file.readdata=io.loaddata -file.savedata=io.savedata +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=io.loaddata(oldname) + local data=loaddata(oldname) if data and data~="" then - file.savedata(newname,data) + savedata(newname,data) end end end @@ -3660,7 +4071,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-gzip"] = package.loaded["l-gzip"] or true --- original size: 1265, stripped down to: 1038 +-- original size: 1211, stripped down to: 1002 if not modules then modules={} end modules ['l-gzip']={ version=1.001, @@ -3714,7 +4125,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-md5"] = package.loaded["l-md5"] or true --- original size: 3355, stripped down to: 2321 +-- original size: 3309, stripped down to: 2314 if not modules then modules={} end modules ['l-md5']={ version=1.001, @@ -3744,6 +4155,8 @@ do 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) @@ -3802,7 +4215,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-url"] = package.loaded["l-url"] or true --- original size: 12897, stripped down to: 5882 +-- original size: 12531, stripped down to: 5721 if not modules then modules={} end modules ['l-url']={ version=1.001, @@ -4019,7 +4432,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-dir"] = package.loaded["l-dir"] or true --- original size: 17358, stripped down to: 11378 +-- original size: 17703, stripped down to: 11691 if not modules then modules={} end modules ['l-dir']={ version=1.001, @@ -4283,6 +4696,31 @@ local function globfiles(path,recurse,func,files) 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 in walkdir(path) do + if find(name,"^%.") then + else + local mode=attributes(name,'mode') + 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 @@ -4447,9 +4885,13 @@ end file.expandname=dir.expandname local stack={} function dir.push(newdir) - insert(stack,currentdir()) + local curdir=currentdir() + insert(stack,curdir) if newdir and newdir~="" then chdir(newdir) + return newdir + else + return curdir end end function dir.pop() @@ -4484,7 +4926,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-boolean"] = package.loaded["l-boolean"] or true --- original size: 1919, stripped down to: 1621 +-- original size: 1850, stripped down to: 1568 if not modules then modules={} end modules ['l-boolean']={ version=1.001, @@ -4556,7 +4998,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-unicode"] = package.loaded["l-unicode"] or true --- original size: 38699, stripped down to: 16321 +-- original size: 38263, stripped down to: 16330 if not modules then modules={} end modules ['l-unicode']={ version=1.001, @@ -5167,6 +5609,23 @@ function utf.chrlen(u) (u<0xFC and 5) or (u<0xFE and 6) or 0 end +local extract=bit32.extract +local char=string.char +function unicode.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 -- of closure @@ -5175,7 +5634,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-math"] = package.loaded["l-math"] or true --- original size: 1012, stripped down to: 912 +-- original size: 974, stripped down to: 890 if not modules then modules={} end modules ['l-math']={ version=1.001, @@ -5215,7 +5674,7 @@ do -- create closure to overcome 200 locals limit package.loaded["util-str"] = package.loaded["util-str"] or true --- original size: 36053, stripped down to: 19685 +-- original size: 36148, stripped down to: 20179 if not modules then modules={} end modules ['util-str']={ version=1.001, @@ -5227,7 +5686,7 @@ if not modules then modules={} end modules ['util-str']={ utilities=utilities or {} utilities.strings=utilities.strings or {} local strings=utilities.strings -local format,gsub,rep,sub=string.format,string.gsub,string.rep,string.sub +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=tonumber,type,tostring local unpack,concat=table.unpack,table.concat @@ -5410,6 +5869,25 @@ function number.signed(i) return "-",-i end end +local digit=patterns.digit +local period=patterns.period +local three=digit*digit*digit +local splitter=Cs ( + (((1-(three^1*period))^1+C(three))*(Carg(1)*three)^1+C((1-period)^1))*(P(1)/""*Carg(2))*C(2) +) +patterns.formattednumber=splitter +function number.formatted(n,sep1,sep2) + local s=type(s)=="string" and n or format("%0.2f",n) + if sep1==true then + return lpegmatch(splitter,s,1,".",",") + elseif sep1=="." then + return lpegmatch(splitter,s,1,sep1,sep2 or ",") + elseif sep1=="," then + return lpegmatch(splitter,s,1,sep1,sep2 or ".") + else + return lpegmatch(splitter,s,1,sep1 or ",",sep2 or ".") + end +end local zero=P("0")^1/"" local plus=P("+")/"" local minus=P("-") @@ -5435,6 +5913,27 @@ function number.sparseexponent(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 @@ -5462,6 +5961,7 @@ local autodouble=string.autodouble local sequenced=table.sequenced local formattednumber=number.formatted local sparseexponent=number.sparseexponent +local formattedfloat=number.formattedfloat ]] else environment={ @@ -5485,6 +5985,7 @@ else sequenced=table.sequenced, formattednumber=number.formatted, sparseexponent=number.sparseexponent, + formattedfloat=number.formattedfloat } end local arguments={ "a1" } @@ -5495,6 +5996,7 @@ setmetatable(arguments,{ __index=function(t,k) end }) local prefix_any=C((S("+- .")+R("09"))^0) +local prefix_sub=(C((S("+-")+R("09"))^0)+Cc(0))*P(".")*(C((S("+-")+R("09"))^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 @@ -5545,6 +6047,10 @@ local format_F=function(f) 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,%i,%i)",n,b or 0,a or 0) +end local format_g=function(f) n=n+1 return format("format('%%%sg',a%s)",f,n) @@ -5693,25 +6199,6 @@ end local format_W=function(f) return format("nspaces[%s]",tonumber(f) or 0) end -local digit=patterns.digit -local period=patterns.period -local three=digit*digit*digit -local splitter=Cs ( - (((1-(three^1*period))^1+C(three))*(Carg(1)*three)^1+C((1-period)^1))*(P(1)/""*Carg(2))*C(2) -) -patterns.formattednumber=splitter -function number.formatted(n,sep1,sep2) - local s=type(s)=="string" and n or format("%0.2f",n) - if sep1==true then - return lpegmatch(splitter,s,1,".",",") - elseif sep1=="." then - return lpegmatch(splitter,s,1,sep1,sep2 or ",") - elseif sep1=="," then - return lpegmatch(splitter,s,1,sep1,sep2 or ".") - else - return lpegmatch(splitter,s,1,sep1 or ",",sep2 or ".") - end -end local format_m=function(f) n=n+1 if not f or f=="" then @@ -5736,9 +6223,16 @@ 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 f==0 then + if w then + extension=gsub(extension,"%.%.%.","") + end return extension elseif f==1 then + if w then + extension=gsub(extension,"%.%.%.","%%s") + end n=n+1 local a="a"..n return format(extension,a,a) @@ -5746,10 +6240,13 @@ local format_extension=function(extensions,f,name) local a="a"..(n+f+1) return format(extension,a,a) else + if w then + extension=gsub(extension,"%.%.%.",rep("%%s,",f-1).."%%s") + end local t={} for i=1,f do n=n+1 - t[#t+1]="a"..n + t[i]="a"..n end return format(extension,unpack(t)) end @@ -5762,7 +6259,8 @@ local builder=Cs { "start", +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") @@ -5789,6 +6287,7 @@ local builder=Cs { "start", ["S"]=(prefix_any*P("S"))/format_S, ["Q"]=(prefix_any*P("Q"))/format_S, ["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, @@ -5909,7 +6408,7 @@ do -- create closure to overcome 200 locals limit package.loaded["util-tab"] = package.loaded["util-tab"] or true --- original size: 28680, stripped down to: 18636 +-- original size: 27407, stripped down to: 17116 if not modules then modules={} end modules ['util-tab']={ version=1.001, @@ -5923,7 +6422,7 @@ 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=setmetatable,getmetatable,tonumber,tostring +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 @@ -6063,7 +6562,7 @@ function table.tocsv(t,specification) r[f]=tostring(field) end end - result[#result+1]=concat(r,separator) + result[i+1]=concat(r,separator) end return concat(result,"\n") else @@ -6295,11 +6794,12 @@ function table.autokey(t,k) return v end local selfmapper={ __index=function(t,k) t[k]=k return k end } -function table.twowaymapper(t) - if not t then - t={} - else - for i=0,#t do +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) @@ -6307,7 +6807,6 @@ function table.twowaymapper(t) t[ti]=i end end - t[""]=t[0] or "" end setmetatable(t,selfmapper) return t @@ -6346,6 +6845,7 @@ 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) @@ -6353,54 +6853,6 @@ local function serialize(root,name,specification) local t local n=1 local unknown=false - local function simple_table(t) - 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=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 - tt[i]=v - 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 - tt[i+1]=v - 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 local function do_serialize(root,name,depth,level,indexed) if level>0 then n=n+1 @@ -6425,7 +6877,7 @@ local function serialize(root,name,specification) local last=0 last=#root for k=1,last do - if root[k]==nil then + if rawget(root,k)==nil then last=k-1 break end @@ -6448,7 +6900,7 @@ local function serialize(root,name,specification) if next(v)==nil then n=n+1 t[n]=f_val_not(depth) else - local st=simple_table(v) + local st=is_simple_table(v) if st then n=n+1 t[n]=f_val_seq(depth,st) else @@ -6492,7 +6944,7 @@ local function serialize(root,name,specification) n=n+1 t[n]=f_key_str_value_not(depth,tostring(k)) end else - local st=simple_table(v) + local st=is_simple_table(v) if not st then do_serialize(v,k,depth,level+1) elseif tk=="number" then @@ -6552,11 +7004,11 @@ local function serialize(root,name,specification) end if root then if getmetatable(root) then - local dummy=root._w_h_a_t_e_v_e_r_ + 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=simple_table(root) + local st=is_simple_table(root) if st then return t[1]..f_fin_seq(st) else @@ -6570,7 +7022,12 @@ local function serialize(root,name,specification) end table.serialize=serialize if setinspector then - setinspector("table",function(v) if type(v)=="table" then print(serialize(v,"table",{})) return true end end) + setinspector("table",function(v) + if type(v)=="table" then + print(serialize(v,"table",{ metacheck=false })) + return true + end + end) end @@ -6580,7 +7037,7 @@ do -- create closure to overcome 200 locals limit package.loaded["util-fil"] = package.loaded["util-fil"] or true --- original size: 3577, stripped down to: 2870 +-- original size: 7567, stripped down to: 5575 if not modules then modules={} end modules ['util-fil']={ version=1.001, @@ -6590,7 +7047,9 @@ if not modules then modules={} end modules ['util-fil']={ license="see context related readme files" } local byte=string.byte -local extract=bit32.extract +local char=string.char +local extract=bit32 and bit32.extract +local floor=math.floor utilities=utilities or {} local files={} utilities.files=files @@ -6609,6 +7068,7 @@ end function files.size(f) return f:seek("end") end +files.getsize=files.size function files.setposition(f,n) if zerobased[f] then f:seek("set",n) @@ -6646,6 +7106,10 @@ 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 @@ -6655,7 +7119,7 @@ end function files.readinteger1(f) local n=byte(f:read(1)) if n>=0x80 then - return n-0xFF-1 + return n-0x100 else return n end @@ -6663,55 +7127,107 @@ 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) - local n=0x100*a+b - if n>=0x8000 then - return n-0xFFFF-1 + if a>=0x80 then + return 0x100*a+b-0x10000 else - return n + 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) - local n=0x1000000*a+0x10000*b+0x100*c+d - if n>=0x8000000 then - return n-0xFFFFFFFF-1 + if a>=0x80 then + return 0x1000000*a+0x10000*b+0x100*c+d-0x100000000 else - return n + return 0x1000000*a+0x10000*b+0x100*c+d end end -function files.readfixed4(f) - local a,b,c,d=byte(f:read(4),1,4) - local n=0x100*a+b - if n>=0x8000 then - return n-0xFFFF-1+(0x100*c+d)/0xFFFF +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 n+(0x100*c+d)/0xFFFF + return 0x1000000*a+0x10000*b+0x100*c+d end end -function files.read2dot14(f) +function files.readfixed2(f) local a,b=byte(f:read(2),1,2) - local n=0x100*a+b - local m=extract(n,0,30) - if n>0x7FFF then - n=extract(n,30,2) - return m/0x4000-4 + if a>=0x80 then + return (a-0x100)+b/0x100 + else + return (a )+b/0x100 + end +end +function files.readfixed4(f) + local a,b,c,d=byte(f:read(4),1,4) + if a>=0x80 then + return (0x100*a+b-0x10000)+(0x100*c+d)/0x10000 else - n=extract(n,30,2) - return n+m/0x4000 + return (0x100*a+b )+(0x100*c+d)/0x10000 + end +end +if extract 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) @@ -6720,6 +7236,55 @@ end function files.skiplong(f,n) f:read(4*(n or 1)) end +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.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.readinteger1=fio.readinteger1 + files.readinteger2=fio.readinteger2 + files.readinteger3=fio.readinteger3 + files.readinteger4=fio.readinteger4 + 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 end -- of closure @@ -6728,7 +7293,7 @@ do -- create closure to overcome 200 locals limit package.loaded["util-sac"] = package.loaded["util-sac"] or true --- original size: 4264, stripped down to: 3349 +-- original size: 8716, stripped down to: 6754 if not modules then modules={} end modules ['util-sac']={ version=1.001, @@ -6738,7 +7303,7 @@ if not modules then modules={} end modules ['util-sac']={ license="see context related readme files" } local byte,sub=string.byte,string.sub -local extract=bit32.extract +local extract=bit32 and bit32.extract utilities=utilities or {} local streams={} utilities.streams=streams @@ -6796,6 +7361,12 @@ function streams.readbytes(f,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 @@ -6815,7 +7386,7 @@ function streams.readinteger1(f) f[2]=i+1 local n=byte(f[1],i) if n>=0x80 then - return n-0xFF-1 + return n-0x100 else return n end @@ -6830,16 +7401,33 @@ function streams.readcardinal2(f) 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) - local n=0x100*a+b - if n>=0x8000 then - return n-0xFFFF-1 + if a>=0x80 then + return 0x100*a+b-0x10000 else - return n + 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) @@ -6849,6 +7437,35 @@ function streams.readcardinal3(f) 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 @@ -6861,11 +7478,21 @@ function streams.readinteger4(f) local j=i+3 f[2]=j+1 local a,b,c,d=byte(f[1],i,j) - local n=0x1000000*a+0x10000*b+0x100*c+d - if n>=0x8000000 then - return n-0xFFFFFFFF-1 + if a>=0x80 then + return 0x1000000*a+0x10000*b+0x100*c+d-0x100000000 else - return n + 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.readfixed4(f) @@ -6873,26 +7500,38 @@ function streams.readfixed4(f) local j=i+3 f[2]=j+1 local a,b,c,d=byte(f[1],i,j) - local n=0x100*a+b - if n>=0x8000 then - return n-0xFFFF-1+(0x100*c+d)/0xFFFF + if a>=0x80 then + return (0x100*a+b-0x10000)+(0x100*c+d)/0x10000 else - return n+(0x100*c+d)/0xFFFF + return (0x100*a+b )+(0x100*c+d)/0x10000 end end -function streams.read2dot14(f) +function streams.readfixed2(f) local i=f[2] local j=i+1 f[2]=j+1 local a,b=byte(f[1],i,j) - local n=0x100*a+b - local m=extract(n,0,30) - if n>0x7FFF then - n=extract(n,30,2) - return m/0x4000-4 - else - n=extract(n,30,2) - return n+m/0x4000 + if a>=0x80 then + return (a-0x100)+b/0x100 + else + return (a )+b/0x100 + end +end +if extract 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) @@ -6901,6 +7540,92 @@ 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 end -- of closure @@ -6909,7 +7634,7 @@ do -- create closure to overcome 200 locals limit package.loaded["util-sto"] = package.loaded["util-sto"] or true --- original size: 4100, stripped down to: 2852 +-- original size: 3926, stripped down to: 2742 if not modules then modules={} end modules ['util-sto']={ version=1.001, @@ -7049,7 +7774,7 @@ do -- create closure to overcome 200 locals limit package.loaded["util-prs"] = package.loaded["util-prs"] or true --- original size: 23411, stripped down to: 16177 +-- original size: 22883, stripped down to: 16045 if not modules then modules={} end modules ['util-prs']={ version=1.001, @@ -7211,6 +7936,21 @@ function parsers.settings_to_array(str,strict) 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=P(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) @@ -7587,7 +8327,7 @@ do -- create closure to overcome 200 locals limit package.loaded["util-fmt"] = package.loaded["util-fmt"] or true --- original size: 2350, stripped down to: 1847 +-- original size: 2274, stripped down to: 1781 if not modules then modules={} end modules ['util-fmt']={ version=1.001, @@ -7668,7 +8408,7 @@ do -- create closure to overcome 200 locals limit package.loaded["trac-set"] = package.loaded["trac-set"] or true --- original size: 12862, stripped down to: 9104 +-- original size: 12454, stripped down to: 8840 if not modules then modules={} end modules ['trac-set']={ version=1.001, @@ -7854,7 +8594,6 @@ function setters.list(t) return user,system end function setters.show(t) - local category=t.name local list=setters.list(t) t.report() for k=1,#list do @@ -7981,7 +8720,7 @@ do -- create closure to overcome 200 locals limit package.loaded["trac-log"] = package.loaded["trac-log"] or true --- original size: 30767, stripped down to: 21355 +-- original size: 30007, stripped down to: 20818 if not modules then modules={} end modules ['trac-log']={ version=1.001, @@ -8027,6 +8766,14 @@ if tex and (tex.jobname or tex.formatname) 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 @@ -8612,7 +9359,6 @@ function logs.stop_page_number() end logs.flush() end -local report_files=logs.reporter("files") local nesting=0 local verbose=false local hasscheme=url.hasscheme @@ -8774,7 +9520,7 @@ do -- create closure to overcome 200 locals limit package.loaded["trac-inf"] = package.loaded["trac-inf"] or true --- original size: 6917, stripped down to: 5484 +-- original size: 8036, stripped down to: 5567 if not modules then modules={} end modules ['trac-inf']={ version=1.001, @@ -8806,11 +9552,13 @@ 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) local timer=timers[instance or "notimer"] local it=timer.timing or 0 if it==0 then - timer.starttime=clock() + timer.starttime=ticks() if not timer.loadtime then timer.loadtime=0 end @@ -8824,12 +9572,13 @@ local function stoptiming(instance) timer.timing=it-1 else local starttime=timer.starttime - if starttime then - local stoptime=clock() + 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 @@ -8840,7 +9589,7 @@ local function elapsed(instance) return instance or 0 else local timer=timers[instance or "notimer"] - return timer and timer.loadtime or 0 + return timer and seconds(timer.loadtime) or 0 end end local function elapsedtime(instance) @@ -8888,10 +9637,13 @@ function statistics.show() local total,indirect=status.callbacks or 0,status.indirect_callbacks or 0 return format("%s direct, %s indirect, %s total",total-indirect,indirect,total) end) - if jit then - local jitstatus={ jit.status() } - if jitstatus[1] then - register("luajit options",concat(jitstatus," ",2)) + 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() @@ -8955,7 +9707,7 @@ do -- create closure to overcome 200 locals limit package.loaded["trac-pro"] = package.loaded["trac-pro"] or true --- original size: 6039, stripped down to: 3616 +-- original size: 5829, stripped down to: 3501 if not modules then modules={} end modules ['trac-pro']={ version=1.001, @@ -9102,7 +9854,7 @@ do -- create closure to overcome 200 locals limit package.loaded["util-lua"] = package.loaded["util-lua"] or true --- original size: 5142, stripped down to: 3611 +-- original size: 5396, stripped down to: 3708 if not modules then modules={} end modules ['util-lua']={ version=1.001, @@ -9224,6 +9976,17 @@ function luautilities.loadstripped(...) 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 end -- of closure @@ -9232,7 +9995,7 @@ do -- create closure to overcome 200 locals limit package.loaded["util-deb"] = package.loaded["util-deb"] or true --- original size: 4030, stripped down to: 2718 +-- original size: 8911, stripped down to: 6504 if not modules then modules={} end modules ['util-deb']={ version=1.001, @@ -9242,75 +10005,230 @@ if not modules then modules={} end modules ['util-deb']={ license="see context related readme files" } local debug=require "debug" -local getinfo=debug.getinfo -local type,next,tostring=type,next,tostring -local format,find=string.format,string.find -local is_boolean=string.is_boolean +local getinfo,sethook=debug.getinfo,debug.sethook +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 counters={} -local names={} local report=logs.reporter("debugger") -local function hook() - local f=getinfo(2) - if f then - local n="unknown" - if f.what=="C" then - n=f.name or '<anonymous>' - if not names[n] then - names[n]=format("%42s",n) +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 - else - n=f.name or f.namewhat or f.what - if not n or n=="" then - n="?" + 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 } + t[line]=v + return v + end) + t[source]=v + return v + end) + t[name]=v + return v +end) +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 - if not names[n] then - names[n]=format("%42s : % 5i : %s",n,f.linedefined or 0,f.short_src or "unknown source") + end + local data=names[name][source][line] + if where=="call" then + data.count=data.count+1 + insert(data,ticks()) + elseif where=="return" then + local t=remove(data) + if t then + data.total=data.total+ticks()-t end end - counters[n]=(counters[n] or 0)+1 end end -function debugger.showstats(printer,threshold) - printer=printer or report - threshold=threshold or 0 - local total,grandtotal,functions=0,0,0 +function debugger.showstats(printer,threshold) + local printer=printer or report + local calls=0 + local functions=0 local dataset={} - for name,count in next,counters do - dataset[#dataset+1]={ name,count } + local length=0 + local wholetime=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 + wholetime=wholetime+real + end + if line<0 then + line=0 + end + dataset[#dataset+1]={ real,total,count,name,source,line } + end + end + end end - table.sort(dataset,function(a,b) return a[2]==b[2] and b[1]>a[1] or a[2]>b[2] 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 %4.9k %3.3k %8i %-"..length.."s %4i %s"] for i=1,#dataset do - local d=dataset[i] - local name=d[1] - local count=d[2] - if count>threshold and not find(name,"for generator") then - printer(format("%8i %s\n",count,names[name])) - total=total+count - end - grandtotal=grandtotal+count + 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] + local percent=real/wholetime + calls=calls+count functions=functions+1 + name=gsub(name,"%s+"," ") + if #name>length then + name=sub(name,1,length) + end + printer(fmt(seconds(total),seconds(real),percent,count,name,line,source)) end - printer("\n") - printer(format("functions : % 10i\n",functions)) - printer(format("total : % 10i\n",total)) - printer(format("grand total: % 10i\n",grandtotal)) - printer(format("threshold : % 10i\n",threshold)) + printer("") + printer(format("functions : %i",functions)) + printer(format("calls : %i",calls)) + printer(format("overhead : %f",seconds(overhead/1000))) end function debugger.savestats(filename,threshold) local f=io.open(filename,'w') if f then - debugger.showstats(function(str) f:write(str) end,threshold) + debugger.showstats(function(str) f:write(str,"\n") end,threshold) f:close() end end function debugger.enable() - debug.sethook(hook,"c") + if 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() - debug.sethook() + if nesting>0 then + nesting=nesting-1 + end + if nesting==0 then + sethook() + end end local function showtraceback(rep) local level=2 @@ -9334,9 +10252,661 @@ end -- of closure do -- create closure to overcome 200 locals limit +package.loaded["util-tpl"] = package.loaded["util-tpl"] or true + +-- original size: 7100, stripped down to: 3978 + +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=tostring +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: 20309, stripped down to: 13848 + +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 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={ [name]=true } + else + validlibraries[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,"%.%.")) 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 load=ffi.load + if load then + local reported={} + function ffi.load(name,...) + if validlibraries==false then + elseif validlibraries==true then + return load(name,...) + elseif validlibraries[name] then + return load(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: 7985, stripped down to: 6153 +-- original size: 7757, stripped down to: 6015 if not modules then modules={} end modules ['util-mrg']={ version=1.001, @@ -9511,154 +11081,9 @@ end -- of closure do -- create closure to overcome 200 locals limit -package.loaded["util-tpl"] = package.loaded["util-tpl"] or true - --- original size: 7313, stripped down to: 4076 - -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=tostring -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-env"] = package.loaded["util-env"] or true --- original size: 8284, stripped down to: 5176 +-- original size: 9246, stripped down to: 5038 if not modules then modules={} end modules ['util-env']={ version=1.001, @@ -9845,7 +11270,7 @@ do -- create closure to overcome 200 locals limit package.loaded["luat-env"] = package.loaded["luat-env"] or true --- original size: 6358, stripped down to: 4257 +-- original size: 6174, stripped down to: 4141 if not modules then modules={} end modules ['luat-env']={ version=1.001, @@ -9998,7 +11423,7 @@ do -- create closure to overcome 200 locals limit package.loaded["lxml-tab"] = package.loaded["lxml-tab"] or true --- original size: 56973, stripped down to: 35872 +-- original size: 57003, stripped down to: 35696 if not modules then modules={} end modules ['lxml-tab']={ version=1.001, @@ -10013,7 +11438,7 @@ 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=type,next,setmetatable,getmetatable,tonumber,rawset +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 @@ -10140,6 +11565,7 @@ local function add_empty(spacing,namespace,tag) tg=tag, at=at, dt={}, + ni=nt, __p__=top } dt[nt]=t @@ -10161,6 +11587,7 @@ local function add_begin(spacing,namespace,tag) tg=tag, at=at, dt={}, + ni=nil, __p__=stack[level] } setmetatable(top,mt) @@ -10188,6 +11615,7 @@ local function add_end(spacing,namespace,tag) dt=top.dt nt=#dt+1 dt[nt]=toclose + toclose.ni=nt if toclose.at.xmlns then remove(xmlns) end @@ -10232,7 +11660,13 @@ local function add_special(what,spacing,text) if strip and (what=="@cm@" or what=="@dt@") then else nt=nt+1 - dt[nt]={ special=true,ns="",tg=what,dt={ text } } + dt[nt]={ + special=true, + ns="", + tg=what, + ni=nil, + dt={ text }, + } end end local function set_message(txt) @@ -10285,7 +11719,6 @@ do end local p_rest=(1-P(";"))^0 local p_many=P(1)^0 - local p_char=lpegpatterns.utf8character 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={ @@ -10327,13 +11760,27 @@ do [ [[}]] ]="&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] @@ -10778,6 +12225,10 @@ local grammar_unparsed_text=P { "preamble", local function _xmlconvert_(data,settings) settings=settings or {} preparexmlstate(settings) + 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 @@ -10919,14 +12370,24 @@ function xml.toxml(data) return data end end -local function copy(old) +local function copy(old,p) if old then local new={} for k,v in next,old do - if type(v)=="table" then - new[k]=table.copy(v) - else + 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) @@ -11157,18 +12618,26 @@ local xmlfilehandler=newhandlers { function xml.save(root,name) serialize(root,xmlfilehandler,name) end -local result +local result,r,threshold={},0,512 local xmlstringhandler=newhandlers { name="string", initialize=function() - result={} + r=0 return result end, finalize=function() - return concat(result) + local done=concat(result,"",1,r) + r=0 + if r>threshold then + result={} + end + return done end, handle=function(...) - result[#result+1]=concat {... } + for i=1,select("#",...) do + r=r+1 + result[r]=select(i,...) + end end, } local function xmltostring(root) @@ -11320,7 +12789,7 @@ do -- create closure to overcome 200 locals limit package.loaded["lxml-lpt"] = package.loaded["lxml-lpt"] or true --- original size: 53892, stripped down to: 32508 +-- original size: 53301, stripped down to: 32477 if not modules then modules={} end modules ['lxml-lpt']={ version=1.001, @@ -11702,6 +13171,14 @@ local function apply_expression(list,expression,order) 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") @@ -11825,6 +13302,9 @@ 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)) @@ -11865,34 +13345,36 @@ local pathparser=Ct { "patterns", (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")+P("/")+V("axis"))*spaces*V("nodes")^0+V("error"))*spaces*V("expressions")^0*spaces*V("finalizer")^0, + 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"), + 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 ), - 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 ), + 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, @@ -12042,6 +13524,8 @@ do 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 @@ -12083,6 +13567,9 @@ do 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 "") @@ -12114,6 +13601,8 @@ do 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 @@ -12170,6 +13659,13 @@ do 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) @@ -12449,7 +13945,7 @@ do -- create closure to overcome 200 locals limit package.loaded["lxml-mis"] = package.loaded["lxml-mis"] or true --- original size: 3787, stripped down to: 2003 +-- original size: 3684, stripped down to: 1957 if not modules then modules={} end modules ['lxml-mis']={ version=1.001, @@ -12518,7 +14014,7 @@ do -- create closure to overcome 200 locals limit package.loaded["lxml-aux"] = package.loaded["lxml-aux"] or true --- original size: 30566, stripped down to: 21741 +-- original size: 29835, stripped down to: 21174 if not modules then modules={} end modules ['lxml-aux']={ version=1.001, @@ -12534,7 +14030,6 @@ local xml=xml local xmlcopy,xmlname=xml.copy,xml.name local xmlinheritedconvert=xml.inheritedconvert local xmlapplylpath=xml.applylpath -local xmlfilter=xml.filter 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 @@ -12738,7 +14233,17 @@ function xml.replace(root,pattern,whatever) report('replacing',pattern,c,e) end local d=p.dt - d[e.ni]=copiedelement(element,p) + 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 @@ -13161,7 +14666,7 @@ 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.replacet +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 @@ -13379,7 +14884,7 @@ do -- create closure to overcome 200 locals limit package.loaded["lxml-xml"] = package.loaded["lxml-xml"] or true --- original size: 10719, stripped down to: 7841 +-- original size: 10274, stripped down to: 7538 if not modules then modules={} end modules ['lxml-xml']={ version=1.001, @@ -13757,7 +15262,7 @@ do -- create closure to overcome 200 locals limit package.loaded["trac-xml"] = package.loaded["trac-xml"] or true --- original size: 6534, stripped down to: 5072 +-- original size: 6407, stripped down to: 4965 if not modules then modules={} end modules ['trac-xml']={ version=1.001, @@ -13907,6 +15412,7 @@ function reporters.export(t,methods,filename) 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) @@ -13927,7 +15433,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-ini"] = package.loaded["data-ini"] or true --- original size: 11444, stripped down to: 7830 +-- original size: 11099, stripped down to: 7516 if not modules then modules={} end modules ['data-ini']={ version=1.001, @@ -14072,11 +15578,6 @@ if not texroot or texroot=="" then ossetenv('TEXROOT',texroot) end environment.texroot=file.collapsepath(texroot) -if type(profiler)=="table" and not jit then - directives.register("system.profile",function() - profiler.start("luatex-profile.log") - end) -end local prefixes=utilities.storage.allocate() resolvers.prefixes=prefixes local resolved={} @@ -14183,7 +15684,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-exp"] = package.loaded["data-exp"] or true --- original size: 18619, stripped down to: 11042 +-- original size: 17958, stripped down to: 10705 if not modules then modules={} end modules ['data-exp']={ version=1.001, @@ -14199,7 +15700,6 @@ 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 ostype=os.type 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) @@ -14568,7 +16068,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-env"] = package.loaded["data-env"] or true --- original size: 9649, stripped down to: 7131 +-- original size: 9342, stripped down to: 6887 if not modules then modules={} end modules ['data-env']={ version=1.001, @@ -14852,7 +16352,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-tmp"] = package.loaded["data-tmp"] or true --- original size: 16066, stripped down to: 11938 +-- original size: 16088, stripped down to: 11435 if not modules then modules={} end modules ['data-tmp']={ version=1.100, @@ -15056,18 +16556,6 @@ end caches.getreadablepaths=getreadablepaths caches.getwritablepath=getwritablepath function caches.getfirstreadablefile(filename,...) - 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 caches.setfirstwritablefile(filename,...) -end -function caches.getfirstreadablefile_TEST_ME_FIRST(filename,...) local fullname,path=caches.setfirstwritablefile(filename,...) if is_readable(fullname) then return fullname,path @@ -15096,18 +16584,22 @@ 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) +function caches.loaddata(readables,name,writable) if type(readables)=="string" then readables={ readables } end for i=1,#readables do local path=readables[i] - local tmaname,tmcname=caches.setluanames(path,name) 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) @@ -15228,7 +16720,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-met"] = package.loaded["data-met"] or true --- original size: 5488, stripped down to: 4101 +-- original size: 5310, stripped down to: 3980 if not modules then modules={} end modules ['data-met']={ version=1.100, @@ -15240,7 +16732,6 @@ if not modules then modules={} end modules ['data-met']={ local find,format=string.find,string.format local sequenced=table.sequenced local addurlscheme,urlhashed=url.addscheme,url.hashed -local getcurrentdir=lfs.currentdir local trace_locating=false local trace_methods=false trackers.register("resolvers.locating",function(v) trace_methods=v end) @@ -15347,7 +16838,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-res"] = package.loaded["data-res"] or true --- original size: 67241, stripped down to: 46427 +-- original size: 67524, stripped down to: 46632 if not modules then modules={} end modules ['data-res']={ version=1.001, @@ -15401,6 +16892,7 @@ 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 @@ -15444,7 +16936,6 @@ local function resolvevariable(k) end local dollarstripper=lpeg.stripper("$") local inhibitstripper=P("!")^0*Cs(P(1)^0) -local backslashswapper=lpeg.replacer("\\","/") local somevariable=P("$")/"" local somekey=C(R("az","AZ","09","__","--")^1) local somethingelse=P(";")*((1-S("!{}/\\"))^1*P(";")/"")+P(";")*(P(";")/"")+P(1) @@ -15590,23 +17081,29 @@ local function identify_configuration_files() end reportcriticalvariables(cnfspec) local cnfpaths=expandedpathfromlist(resolvers.splitpath(cnfspec)) - local luacnfname=resolvers.luacnfname - 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 %a on %s path %a from specification %a",luacnfname,weirdpath and "weird" or "given",fullpath,filepath) - end - if isfile(realname) then - specification[#specification+1]=filename + 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 - report_resolving("found configuration file %a",realname) + 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 @@ -16903,7 +18400,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-pre"] = package.loaded["data-pre"] or true --- original size: 4236, stripped down to: 3144 +-- original size: 4090, stripped down to: 3059 if not modules then modules={} end modules ['data-pre']={ version=1.001, @@ -17025,7 +18522,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-inp"] = package.loaded["data-inp"] or true --- original size: 935, stripped down to: 838 +-- original size: 910, stripped down to: 823 if not modules then modules={} end modules ['data-inp']={ version=1.001, @@ -17055,7 +18552,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-out"] = package.loaded["data-out"] or true --- original size: 548, stripped down to: 483 +-- original size: 530, stripped down to: 475 if not modules then modules={} end modules ['data-out']={ version=1.001, @@ -17078,7 +18575,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-fil"] = package.loaded["data-fil"] or true --- original size: 3976, stripped down to: 3391 +-- original size: 3863, stripped down to: 3310 if not modules then modules={} end modules ['data-fil']={ version=1.001, @@ -17186,7 +18683,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-con"] = package.loaded["data-con"] or true --- original size: 5148, stripped down to: 3680 +-- original size: 5029, stripped down to: 3607 if not modules then modules={} end modules ['data-con']={ version=1.100, @@ -17256,7 +18753,7 @@ 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) + 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) @@ -17305,7 +18802,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-use"] = package.loaded["data-use"] or true --- original size: 4000, stripped down to: 3052 +-- original size: 4045, stripped down to: 3110 if not modules then modules={} end modules ['data-use']={ version=1.001, @@ -17350,7 +18847,7 @@ function resolvers.automount(usecache) 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) +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") @@ -17361,6 +18858,10 @@ function statistics.savefmtstatus(texname,formatbanner,sourcefile) sourcefile=sourcefile, } io.savedata(luvname,table.serialize(luvdata,true)) + lua.registerfinalizer(function() + logs.report("format banner","%s",banner) + logs.newline() + end) end end function statistics.checkfmtstatus(texname) @@ -17396,7 +18897,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-zip"] = package.loaded["data-zip"] or true --- original size: 9036, stripped down to: 7041 +-- original size: 8772, stripped down to: 6841 if not modules then modules={} end modules ['data-zip']={ version=1.001, @@ -17633,7 +19134,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-tre"] = package.loaded["data-tre"] or true --- original size: 8712, stripped down to: 5726 +-- original size: 8479, stripped down to: 5580 if not modules then modules={} end modules ['data-tre']={ version=1.001, @@ -17822,7 +19323,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-sch"] = package.loaded["data-sch"] or true --- original size: 6779, stripped down to: 5444 +-- original size: 6653, stripped down to: 5467 if not modules then modules={} end modules ['data-sch']={ version=1.001, @@ -17868,11 +19369,21 @@ function resolvers.schemes.cleanname(specification) end return hash end -local cached,loaded,reused,thresholds,handlers={},{},{},{},{} -local function runcurl(name,cachename) - local command="curl --silent --insecure --create-dirs --output "..cachename.." "..name - os.execute(command) -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 @@ -17894,7 +19405,10 @@ local function fetch(specification) report_schemes("fetching %a, protocol %a, method %a",original,scheme,"curl") end logs.flush() - runcurl(original,cachename) + runner { + original=original, + cachename=cachename, + } end end if io.exists(cachename) then @@ -18003,7 +19517,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-lua"] = package.loaded["data-lua"] or true --- original size: 4447, stripped down to: 3302 +-- original size: 4207, stripped down to: 3137 if not modules then modules={} end modules ['data-lua']={ version=1.001, @@ -18045,8 +19559,6 @@ function helpers.cleanpath(path) return resolveprefix(lpegmatch(pattern,path)) end local loadedaslib=helpers.loadedaslib -local getextraluapaths=package.extraluapaths -local getextralibpaths=package.extralibpaths local registerpath=helpers.registerpath local lualibfile=helpers.lualibfile local luaformatpaths @@ -18112,7 +19624,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-aux"] = package.loaded["data-aux"] or true --- original size: 2494, stripped down to: 2047 +-- original size: 2431, stripped down to: 1996 if not modules then modules={} end modules ['data-aux']={ version=1.001, @@ -18179,7 +19691,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-tmf"] = package.loaded["data-tmf"] or true --- original size: 2674, stripped down to: 1658 +-- original size: 2601, stripped down to: 1627 if not modules then modules={} end modules ['data-tmf']={ version=1.001, @@ -18235,7 +19747,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-lst"] = package.loaded["data-lst"] or true --- original size: 2815, stripped down to: 2415 +-- original size: 2734, stripped down to: 2354 if not modules then modules={} end modules ['data-lst']={ version=1.001, @@ -18315,7 +19827,7 @@ do -- create closure to overcome 200 locals limit package.loaded["util-lib"] = package.loaded["util-lib"] or true --- original size: 11846, stripped down to: 6059 +-- original size: 13595, stripped down to: 7500 if not modules then modules={} end modules ['util-lib']={ version=1.001, @@ -18324,35 +19836,51 @@ if not modules then modules={} end modules ['util-lib']={ copyright="PRAGMA ADE / ConTeXt Development Team", license="see context related readme files", } -local gsub,find=string.gsub,string.find -local pathpart,nameonly,joinfile=file.pathpart,file.nameonly,file.join -local findfile,findfiles=resolvers and resolvers.findfile,resolvers and resolvers.findfiles -local loaded=package.loaded -local report_swiglib=logs.reporter("swiglib") -local trace_swiglib=false trackers.register("resolvers.swiglib",function(v) trace_swiglib=v end) +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 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 requireswiglib(required,version) - local trace_swiglib=trace_swiglib or package.helpers.trace - local library=loaded[required] - if library==nil then - if trace_swiglib then - report_swiglib("requiring library %a with version %a",required,version or "any") - end - local required_full=gsub(required,"%.","/") - local required_path=pathpart(required_full) - local required_base=nameonly(required_full) +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(required) then + found_library=required + end + else local required_name=required_base.."."..os.libsuffix local version=type(version)=="string" and version~="" and version or false local engine=environment.ownmain or false - if trace_swiglib and not done then - local list=resolvers.expandedpathlistfromvariable("lib") + if trace and not done then + local list=expandpaths("lib") for i=1,#list do - report_swiglib("tds path %i: %s",i,list[i]) + report("tds path %i: %s",i,list[i]) end end local function found(locate,asked_library,how,...) - if trace_swiglib then - report_swiglib("checking %s: %a",how,asked_library) + if trace then + report("checking %s: %a",how,asked_library) end return locate(asked_library,...) end @@ -18360,45 +19888,45 @@ local function requireswiglib(required,version) local found=nil if version then local asked_library=joinfile(required_path,version,required_name) - if trace_swiglib then - report_swiglib("checking %s: %a","with version",asked_library) + 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_swiglib then - report_swiglib("checking %s: %a","with version",asked_library) + 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_swiglib then - report_swiglib("checking tds lib paths strictly") + 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_swiglib then - report_swiglib("checking tds lib paths with wildcard") + if trace then + report("checking tds lib paths with wildcard") end local asked_library=joinfile(required_path,".*",required_name) - if trace_swiglib then - report_swiglib("checking %s: %a","latest version",asked_library) + 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 - table.sort(list) + sort(list) local found=list[#list] if found and (not checkpattern or find(found,checkpattern)) then return found end end - if trace_swiglib then - report_swiglib("checking lib paths") + if trace then + report("checking lib paths") end package.extralibpath(environment.ownpath) local paths=package.libpaths() @@ -18410,89 +19938,143 @@ local function requireswiglib(required,version) end return false end - local found_library=nil if engine then - if trace_swiglib then - report_swiglib("attemp 1, engine %a",engine) + if trace then + report("attemp 1, engine %a",engine) end found_library=attempt("/"..engine.."/") if not found_library then - if trace_swiglib then - report_swiglib("attemp 2, no engine",asked_library) + if trace then + report("attemp 2, no engine",asked_library) end found_library=attempt() end else found_library=attempt() end - if not found_library then - if trace_swiglib then - report_swiglib("not found: %a",required) - 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 message,result=action(found_library,required_base) + if result then + library=result + else library=false + report("load error: message %a, library %a",tostring(message),found_library or "no library") + end + end + if not library then + report("unknown: %a",required) + elseif trace then + report("stored: %a",required) + end + return library +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() + message=true + 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 message,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 - local path=pathpart(found_library) - local base=nameonly(found_library) - dir.push(path) + 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("found: %a",found_library) - end - local message=nil - local opener="luaopen_"..required_base - library,message=package.loadlib(found_library,opener) - local libtype=type(library) - if libtype=="function" then - library=library() + report_swiglib("loading %a",name) + end + if not find(name,"%."..initializer.."$") then + fullname="swiglib."..name.."."..initializer 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 + fullname="swiglib."..name end - dir.pop() + library=requireswiglib(fullname,version) + swiglibs[name]=library + statistics.stoptiming(swiglibs) end - if not library then - report_swiglib("unknown: %a",required) - elseif trace_swiglib then - report_swiglib("stored: %a",required) - end - loaded[required]=library - else - report_swiglib("reused: %a",required) + return library end - return library + 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 -local savedrequire=require -function require(name,version) - if find(name,"^swiglib%.") then - return requireswiglib(name,version) - else - return savedrequire(name) +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 function locateindeed(name) + local message,library=pcall(savedffiload,removesuffix(name)) + if type(library)=="userdata" then + return library + else + return false + end 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) + function ffilib(required,version) + if version=="system" then + return locateindeed(name) + else + return locate(required,version,trace_ffilib,report_ffilib,locateindeed) end - if not find(name,"%."..initializer.."$") then - fullname="swiglib."..name.."."..initializer + end + function ffi.load(name) + local library=ffilib(name) + if type(library)=="userdata" then + return library else - fullname="swiglib."..name + report_ffilib("trying to load %a using normal loader",name) + return savedffiload(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 -- of closure @@ -18501,7 +20083,7 @@ do -- create closure to overcome 200 locals limit package.loaded["luat-sta"] = package.loaded["luat-sta"] or true --- original size: 5914, stripped down to: 2584 +-- original size: 5703, stripped down to: 2507 if not modules then modules={} end modules ['luat-sta']={ version=1.001, @@ -18604,7 +20186,7 @@ do -- create closure to overcome 200 locals limit package.loaded["luat-fmt"] = package.loaded["luat-fmt"] or true --- original size: 6967, stripped down to: 5631 +-- original size: 9144, stripped down to: 7291 if not modules then modules={} end modules ['luat-fmt']={ version=1.001, @@ -18618,23 +20200,67 @@ local concat=table.concat local quoted=string.quoted local luasuffixes=utilities.lua.suffixes local report_format=logs.reporter("resolvers","formats") -local function primaryflags() - local trackers=environment.argument("trackers") - local directives=environment.argument("directives") +local function primaryflags() + local arguments=environment.arguments + local flags={} + if arguments.silent then + flags[#flags+1]="--interaction=batchmode" + end + if arguments.jit then + flags[#flags+1]="--jiton" + 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={ "--trackers="..quoted(trackers) } + flags[#flags+1]="--c:trackers="..quoted(trackers) end if directives and directives~="" then - flags={ "--directives="..quoted(directives) } + flags[#flags+1]="--c:directives="..quoted(directives) + end + if arguments.silent then + flags[#flags+1]="--c:silent" end - if environment.argument("jit") then - flags={ "--jiton" } + if arguments.jit then + flags[#flags+1]="--c:jiton" + end + if arguments.ansi then + flags[#flags+1]="--c:ansi" end return concat(flags," ") end -function environment.make_format(name,silent) +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, + }, + 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 olddir=dir.current() local path=caches.getwritablepath("formats",engine) or "" if path~="" then @@ -18690,11 +20316,20 @@ function environment.make_format(name,silent) lfs.chdir(olddir) return end - local dump=os.platform=="unix" and "\\\\dump" or "\\dump" - if silent then + 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() - local command=format("%s --ini --interaction=batchmode %s --lua=%s %s %s > temp.log",engine,primaryflags(),quoted(usedluastub),quoted(fulltexsourcename),dump) - local result=os.execute(command) + 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)) @@ -18703,9 +20338,7 @@ function environment.make_format(name,silent) end os.remove("temp.log") else - local command=format("%s --ini %s --lua=%s %s %sdump",engine,primaryflags(),quoted(usedluastub),quoted(fulltexsourcename),dump) - report_format("running command: %s\n",command) - os.execute(command) + runner(specification) end local pattern=file.removesuffix(file.basename(usedluastub)).."-*.mem" local mp=dir.glob(pattern) @@ -18718,6 +20351,30 @@ function environment.make_format(name,silent) 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" @@ -18739,9 +20396,18 @@ function environment.run_format(name,data,more) report_format("using format name %a",fmtname) report_format("no luc/lua file with name %a",barename) else - local command=format("%s %s --fmt=%s --lua=%s %s %s",engine,primaryflags(),quoted(barename),quoted(luaname),quoted(data),more~="" and quoted(more) or "") - report_format("running command: %s",command) - os.execute(command) + 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 @@ -18750,10 +20416,10 @@ end end -- of closure --- used libraries : l-lua.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-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 trac-set.lua trac-log.lua trac-inf.lua trac-pro.lua util-lua.lua util-deb.lua util-mrg.lua util-tpl.lua util-env.lua luat-env.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 +-- used libraries : l-lua.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-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 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 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 : 797557 --- stripped bytes : 289197 +-- original bytes : 842443 +-- stripped bytes : 306317 -- end library merge @@ -18777,6 +20443,7 @@ local owntree = environment and environment.ownpath or ownpath local ownlibs = { -- order can be made better 'l-lua.lua', + 'l-sandbox.lua', 'l-package.lua', 'l-lpeg.lua', 'l-function.lua', @@ -18810,8 +20477,9 @@ local ownlibs = { -- order can be made better 'util-lua.lua', -- indeed here? 'util-deb.lua', - 'util-mrg.lua', 'util-tpl.lua', + 'util-sbx.lua', + 'util-mrg.lua', 'util-env.lua', 'luat-env.lua', -- can come before inf (as in mkiv) @@ -18983,7 +20651,7 @@ local helpinfo = [[ <metadata> <entry name="name">mtxrun</entry> <entry name="detail">ConTeXt TDS Runner Tool</entry> - <entry name="version">1.31</entry> + <entry name="version">1.32</entry> </metadata> <flags> <category name="basic"> @@ -19046,6 +20714,7 @@ local helpinfo = [[ </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> @@ -19063,7 +20732,7 @@ local helpinfo = [[ local application = logs.application { name = "mtxrun", - banner = "ConTeXt TDS Runner Tool 1.31", + banner = "ConTeXt TDS Runner Tool 1.32", helpinfo = helpinfo, } @@ -20029,6 +21698,13 @@ elseif e_argument("expand-path") then 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) |