Current File : //usr/share/texlive/texmf-dist/tex/luatex/lualibs/lualibs-basic-merged.lua |
-- merged file : lualibs-basic-merged.lua
-- parent file : lualibs-basic.lua
-- merge date : Wed Dec 30 17:55:54 2020
do -- begin closure to overcome local limits and interference
if not modules then modules={} end modules ['l-lua']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local next,type,tonumber=next,type,tonumber
LUAMAJORVERSION,LUAMINORVERSION=string.match(_VERSION,"^[^%d]+(%d+)%.(%d+).*$")
LUAMAJORVERSION=tonumber(LUAMAJORVERSION) or 5
LUAMINORVERSION=tonumber(LUAMINORVERSION) or 1
LUAVERSION=LUAMAJORVERSION+LUAMINORVERSION/10
if LUAVERSION<5.2 and jit then
MINORVERSION=2
LUAVERSION=5.2
end
if not lpeg then
lpeg=require("lpeg")
end
if loadstring then
local loadnormal=load
function load(first,...)
if type(first)=="string" then
return loadstring(first,...)
else
return loadnormal(first,...)
end
end
else
loadstring=load
end
if not ipairs then
local function iterate(a,i)
i=i+1
local v=a[i]
if v~=nil then
return i,v
end
end
function ipairs(a)
return iterate,a,0
end
end
if not pairs then
function pairs(t)
return next,t
end
end
if not table.unpack then
table.unpack=_G.unpack
elseif not unpack then
_G.unpack=table.unpack
end
if not package.loaders then
package.loaders=package.searchers
end
local print,select,tostring=print,select,tostring
local inspectors={}
function setinspector(kind,inspector)
inspectors[kind]=inspector
end
function inspect(...)
for s=1,select("#",...) do
local value=select(s,...)
if value==nil then
print("nil")
else
local done=false
local kind=type(value)
local inspector=inspectors[kind]
if inspector then
done=inspector(value)
if done then
break
end
end
for kind,inspector in next,inspectors do
done=inspector(value)
if done then
break
end
end
if not done then
print(tostring(value))
end
end
end
end
local dummy=function() end
function optionalrequire(...)
local ok,result=xpcall(require,dummy,...)
if ok then
return result
end
end
local flush=io.flush
if flush then
local execute=os.execute if execute then function os.execute(...) flush() return execute(...) end end
local exec=os.exec if exec then function os.exec (...) flush() return exec (...) end end
local spawn=os.spawn if spawn then function os.spawn (...) flush() return spawn (...) end end
local popen=io.popen if popen then function io.popen (...) flush() return popen (...) end end
end
FFISUPPORTED=type(ffi)=="table" and ffi.os~="" and ffi.arch~="" and ffi.load
if not FFISUPPORTED then
local okay;okay,ffi=pcall(require,"ffi")
FFISUPPORTED=type(ffi)=="table" and ffi.os~="" and ffi.arch~="" and ffi.load
end
if not FFISUPPORTED then
ffi=nil
elseif not ffi.number then
ffi.number=tonumber
end
if LUAVERSION>5.3 then
end
if status and os.setenv then
os.setenv("engine",string.lower(status.luatex_engine or "unknown"))
end
end -- closure
do -- begin closure to overcome local limits and interference
if not modules then modules={} end modules ['l-package']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local type=type
local gsub,format,find=string.gsub,string.format,string.find
local insert,remove=table.insert,table.remove
local P,S,Cs,lpegmatch=lpeg.P,lpeg.S,lpeg.Cs,lpeg.match
local package=package
local searchers=package.searchers or package.loaders
local filejoin=file and file.join or function(path,name) return path.."/"..name end
local isreadable=file and file.is_readable or function(name) local f=io.open(name) if f then f:close() return true end end
local addsuffix=file and file.addsuffix or function(name,suffix) return name.."."..suffix end
local function cleanpath(path)
return path
end
local pattern=Cs((((1-S("\\/"))^0*(S("\\/")^1/"/"))^0*(P(".")^1/"/"+P(1))^1)*-1)
local function lualibfile(name)
return lpegmatch(pattern,name) or name
end
local offset=luarocks and 1 or 0
local helpers=package.helpers or {
cleanpath=cleanpath,
lualibfile=lualibfile,
trace=false,
report=function(...) print(format(...)) end,
builtin={
["preload table"]=searchers[1+offset],
["path specification"]=searchers[2+offset],
["cpath specification"]=searchers[3+offset],
["all in one fallback"]=searchers[4+offset],
},
methods={},
sequence={
"already loaded",
"preload table",
"qualified path",
"lua extra list",
"lib extra list",
"path specification",
"cpath specification",
"all in one fallback",
"not loaded",
}
}
package.helpers=helpers
local methods=helpers.methods
local builtin=helpers.builtin
local extraluapaths={}
local extralibpaths={}
local luapaths=nil
local libpaths=nil
local oldluapath=nil
local oldlibpath=nil
local nofextralua=-1
local nofextralib=-1
local nofpathlua=-1
local nofpathlib=-1
local function listpaths(what,paths)
local nofpaths=#paths
if nofpaths>0 then
for i=1,nofpaths do
helpers.report("using %s path %i: %s",what,i,paths[i])
end
else
helpers.report("no %s paths defined",what)
end
return nofpaths
end
local function getextraluapaths()
if helpers.trace and #extraluapaths~=nofextralua then
nofextralua=listpaths("extra lua",extraluapaths)
end
return extraluapaths
end
local function getextralibpaths()
if helpers.trace and #extralibpaths~=nofextralib then
nofextralib=listpaths("extra lib",extralibpaths)
end
return extralibpaths
end
local function getluapaths()
local luapath=package.path or ""
if oldluapath~=luapath then
luapaths=file.splitpath(luapath,";")
oldluapath=luapath
nofpathlua=-1
end
if helpers.trace and #luapaths~=nofpathlua then
nofpathlua=listpaths("builtin lua",luapaths)
end
return luapaths
end
local function getlibpaths()
local libpath=package.cpath or ""
if oldlibpath~=libpath then
libpaths=file.splitpath(libpath,";")
oldlibpath=libpath
nofpathlib=-1
end
if helpers.trace and #libpaths~=nofpathlib then
nofpathlib=listpaths("builtin lib",libpaths)
end
return libpaths
end
package.luapaths=getluapaths
package.libpaths=getlibpaths
package.extraluapaths=getextraluapaths
package.extralibpaths=getextralibpaths
local hashes={
lua={},
lib={},
}
local function registerpath(tag,what,target,...)
local pathlist={... }
local cleanpath=helpers.cleanpath
local trace=helpers.trace
local report=helpers.report
local hash=hashes[what]
local function add(path)
local path=cleanpath(path)
if not hash[path] then
target[#target+1]=path
hash[path]=true
if trace then
report("registered %s path %s: %s",tag,#target,path)
end
else
if trace then
report("duplicate %s path: %s",tag,path)
end
end
end
for p=1,#pathlist do
local path=pathlist[p]
if type(path)=="table" then
for i=1,#path do
add(path[i])
end
else
add(path)
end
end
end
local function pushpath(tag,what,target,path)
local path=helpers.cleanpath(path)
insert(target,1,path)
if helpers.trace then
helpers.report("pushing %s path in front: %s",tag,path)
end
end
local function poppath(tag,what,target)
local path=remove(target,1)
if helpers.trace then
if path then
helpers.report("popping %s path from front: %s",tag,path)
else
helpers.report("no %s path to pop",tag)
end
end
end
helpers.registerpath=registerpath
function package.extraluapath(...)
registerpath("extra lua","lua",extraluapaths,...)
end
function package.pushluapath(path)
pushpath("extra lua","lua",extraluapaths,path)
end
function package.popluapath()
poppath("extra lua","lua",extraluapaths)
end
function package.extralibpath(...)
registerpath("extra lib","lib",extralibpaths,...)
end
function package.pushlibpath(path)
pushpath("extra lib","lib",extralibpaths,path)
end
function package.poplibpath()
poppath("extra lib","lua",extralibpaths)
end
local function loadedaslib(resolved,rawname)
local base=gsub(rawname,"%.","_")
local init="luaopen_"..gsub(base,"%.","_")
if helpers.trace then
helpers.report("calling loadlib with '%s' with init '%s'",resolved,init)
end
return package.loadlib(resolved,init)
end
helpers.loadedaslib=loadedaslib
local function loadedbypath(name,rawname,paths,islib,what)
local trace=helpers.trace
for p=1,#paths do
local path=paths[p]
local resolved=filejoin(path,name)
if trace then
helpers.report("%s path, identifying '%s' on '%s'",what,name,path)
end
if isreadable(resolved) then
if trace then
helpers.report("%s path, '%s' found on '%s'",what,name,resolved)
end
if islib then
return loadedaslib(resolved,rawname)
else
return loadfile(resolved)
end
end
end
end
helpers.loadedbypath=loadedbypath
local function loadedbyname(name,rawname)
if find(name,"^/") or find(name,"^[a-zA-Z]:/") then
local trace=helpers.trace
if trace then
helpers.report("qualified name, identifying '%s'",what,name)
end
if isreadable(name) then
if trace then
helpers.report("qualified name, '%s' found",what,name)
end
return loadfile(name)
end
end
end
helpers.loadedbyname=loadedbyname
methods["already loaded"]=function(name)
return package.loaded[name]
end
methods["preload table"]=function(name)
local f=builtin["preload table"]
if f then
return f(name)
end
end
methods["qualified path"]=function(name)
return loadedbyname(addsuffix(lualibfile(name),"lua"),name)
end
methods["lua extra list"]=function(name)
return loadedbypath(addsuffix(lualibfile(name),"lua"),name,getextraluapaths(),false,"lua")
end
methods["lib extra list"]=function(name)
return loadedbypath(addsuffix(lualibfile(name),os.libsuffix),name,getextralibpaths(),true,"lib")
end
methods["path specification"]=function(name)
local f=builtin["path specification"]
if f then
getluapaths()
return f(name)
end
end
methods["cpath specification"]=function(name)
local f=builtin["cpath specification"]
if f then
getlibpaths()
return f(name)
end
end
methods["all in one fallback"]=function(name)
local f=builtin["all in one fallback"]
if f then
return f(name)
end
end
methods["not loaded"]=function(name)
if helpers.trace then
helpers.report("unable to locate '%s'",name or "?")
end
return nil
end
local level=0
local used={}
helpers.traceused=false
function helpers.loaded(name)
local sequence=helpers.sequence
level=level+1
for i=1,#sequence do
local method=sequence[i]
local lookup=method and methods[method]
if type(lookup)=="function" then
if helpers.trace then
helpers.report("%s, level '%s', method '%s', name '%s'","locating",level,method,name)
end
local result,rest=lookup(name)
if type(result)=="function" then
if helpers.trace then
helpers.report("%s, level '%s', method '%s', name '%s'","found",level,method,name)
end
if helpers.traceused then
used[#used+1]={ level=level,name=name }
end
level=level-1
return result,rest
end
end
end
level=level-1
return nil
end
function helpers.showused()
local n=#used
if n>0 then
helpers.report("%s libraries loaded:",n)
helpers.report()
for i=1,n do
local u=used[i]
helpers.report("%i %a",u.level,u.name)
end
helpers.report()
end
end
function helpers.unload(name)
if helpers.trace then
if package.loaded[name] then
helpers.report("unloading, name '%s', %s",name,"done")
else
helpers.report("unloading, name '%s', %s",name,"not loaded")
end
end
package.loaded[name]=nil
end
table.insert(searchers,1,helpers.loaded)
if context then
package.path=""
end
end -- closure
do -- begin closure to overcome local limits and interference
if not modules then modules={} end modules ['l-lpeg']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
lpeg=require("lpeg")
local lpeg=lpeg
if not lpeg.print then function lpeg.print(...) print(lpeg.pcode(...)) end end
local type,next,tostring=type,next,tostring
local byte,char,gmatch,format=string.byte,string.char,string.gmatch,string.format
local floor=math.floor
local P,R,S,V,Ct,C,Cs,Cc,Cp,Cmt=lpeg.P,lpeg.R,lpeg.S,lpeg.V,lpeg.Ct,lpeg.C,lpeg.Cs,lpeg.Cc,lpeg.Cp,lpeg.Cmt
local lpegtype,lpegmatch,lpegprint=lpeg.type,lpeg.match,lpeg.print
if setinspector then
setinspector("lpeg",function(v) if lpegtype(v) then lpegprint(v) return true end end)
end
lpeg.patterns=lpeg.patterns or {}
local patterns=lpeg.patterns
local anything=P(1)
local endofstring=P(-1)
local alwaysmatched=P(true)
patterns.anything=anything
patterns.endofstring=endofstring
patterns.beginofstring=alwaysmatched
patterns.alwaysmatched=alwaysmatched
local sign=S('+-')
local zero=P('0')
local digit=R('09')
local digits=digit^1
local octdigit=R("07")
local octdigits=octdigit^1
local lowercase=R("az")
local uppercase=R("AZ")
local underscore=P("_")
local hexdigit=digit+lowercase+uppercase
local hexdigits=hexdigit^1
local cr,lf,crlf=P("\r"),P("\n"),P("\r\n")
local newline=P("\r")*(P("\n")+P(true))+P("\n")
local escaped=P("\\")*anything
local squote=P("'")
local dquote=P('"')
local space=P(" ")
local period=P(".")
local comma=P(",")
local utfbom_32_be=P('\000\000\254\255')
local utfbom_32_le=P('\255\254\000\000')
local utfbom_16_be=P('\254\255')
local utfbom_16_le=P('\255\254')
local utfbom_8=P('\239\187\191')
local utfbom=utfbom_32_be+utfbom_32_le+utfbom_16_be+utfbom_16_le+utfbom_8
local utftype=utfbom_32_be*Cc("utf-32-be")+utfbom_32_le*Cc("utf-32-le")+utfbom_16_be*Cc("utf-16-be")+utfbom_16_le*Cc("utf-16-le")+utfbom_8*Cc("utf-8")+alwaysmatched*Cc("utf-8")
local utfstricttype=utfbom_32_be*Cc("utf-32-be")+utfbom_32_le*Cc("utf-32-le")+utfbom_16_be*Cc("utf-16-be")+utfbom_16_le*Cc("utf-16-le")+utfbom_8*Cc("utf-8")
local utfoffset=utfbom_32_be*Cc(4)+utfbom_32_le*Cc(4)+utfbom_16_be*Cc(2)+utfbom_16_le*Cc(2)+utfbom_8*Cc(3)+Cc(0)
local utf8next=R("\128\191")
patterns.utfbom_32_be=utfbom_32_be
patterns.utfbom_32_le=utfbom_32_le
patterns.utfbom_16_be=utfbom_16_be
patterns.utfbom_16_le=utfbom_16_le
patterns.utfbom_8=utfbom_8
patterns.utf_16_be_nl=P("\000\r\000\n")+P("\000\r")+P("\000\n")
patterns.utf_16_le_nl=P("\r\000\n\000")+P("\r\000")+P("\n\000")
patterns.utf_32_be_nl=P("\000\000\000\r\000\000\000\n")+P("\000\000\000\r")+P("\000\000\000\n")
patterns.utf_32_le_nl=P("\r\000\000\000\n\000\000\000")+P("\r\000\000\000")+P("\n\000\000\000")
patterns.utf8one=R("\000\127")
patterns.utf8two=R("\194\223")*utf8next
patterns.utf8three=R("\224\239")*utf8next*utf8next
patterns.utf8four=R("\240\244")*utf8next*utf8next*utf8next
patterns.utfbom=utfbom
patterns.utftype=utftype
patterns.utfstricttype=utfstricttype
patterns.utfoffset=utfoffset
local utf8char=patterns.utf8one+patterns.utf8two+patterns.utf8three+patterns.utf8four
local validutf8char=utf8char^0*endofstring*Cc(true)+Cc(false)
local utf8character=P(1)*R("\128\191")^0
patterns.utf8=utf8char
patterns.utf8char=utf8char
patterns.utf8character=utf8character
patterns.validutf8=validutf8char
patterns.validutf8char=validutf8char
local eol=S("\n\r")
local spacer=S(" \t\f\v")
local whitespace=eol+spacer
local nonspacer=1-spacer
local nonwhitespace=1-whitespace
patterns.eol=eol
patterns.spacer=spacer
patterns.whitespace=whitespace
patterns.nonspacer=nonspacer
patterns.nonwhitespace=nonwhitespace
local stripper=spacer^0*C((spacer^0*nonspacer^1)^0)
local fullstripper=whitespace^0*C((whitespace^0*nonwhitespace^1)^0)
local collapser=Cs(spacer^0/""*nonspacer^0*((spacer^0/" "*nonspacer^1)^0))
local nospacer=Cs((whitespace^1/""+nonwhitespace^1)^0)
local b_collapser=Cs(whitespace^0/""*(nonwhitespace^1+whitespace^1/" ")^0)
local m_collapser=Cs((nonwhitespace^1+whitespace^1/" ")^0)
local e_collapser=Cs((whitespace^1*endofstring/""+nonwhitespace^1+whitespace^1/" ")^0)
local x_collapser=Cs((nonwhitespace^1+whitespace^1/"" )^0)
local b_stripper=Cs(spacer^0/""*(nonspacer^1+spacer^1/" ")^0)
local m_stripper=Cs((nonspacer^1+spacer^1/" ")^0)
local e_stripper=Cs((spacer^1*endofstring/""+nonspacer^1+spacer^1/" ")^0)
local x_stripper=Cs((nonspacer^1+spacer^1/"" )^0)
patterns.stripper=stripper
patterns.fullstripper=fullstripper
patterns.collapser=collapser
patterns.nospacer=nospacer
patterns.b_collapser=b_collapser
patterns.m_collapser=m_collapser
patterns.e_collapser=e_collapser
patterns.x_collapser=x_collapser
patterns.b_stripper=b_stripper
patterns.m_stripper=m_stripper
patterns.e_stripper=e_stripper
patterns.x_stripper=x_stripper
patterns.lowercase=lowercase
patterns.uppercase=uppercase
patterns.letter=patterns.lowercase+patterns.uppercase
patterns.space=space
patterns.tab=P("\t")
patterns.spaceortab=patterns.space+patterns.tab
patterns.newline=newline
patterns.emptyline=newline^1
patterns.equal=P("=")
patterns.comma=comma
patterns.commaspacer=comma*spacer^0
patterns.period=period
patterns.colon=P(":")
patterns.semicolon=P(";")
patterns.underscore=underscore
patterns.escaped=escaped
patterns.squote=squote
patterns.dquote=dquote
patterns.nosquote=(escaped+(1-squote))^0
patterns.nodquote=(escaped+(1-dquote))^0
patterns.unsingle=(squote/"")*patterns.nosquote*(squote/"")
patterns.undouble=(dquote/"")*patterns.nodquote*(dquote/"")
patterns.unquoted=patterns.undouble+patterns.unsingle
patterns.unspacer=((patterns.spacer^1)/"")^0
patterns.singlequoted=squote*patterns.nosquote*squote
patterns.doublequoted=dquote*patterns.nodquote*dquote
patterns.quoted=patterns.doublequoted+patterns.singlequoted
patterns.digit=digit
patterns.digits=digits
patterns.octdigit=octdigit
patterns.octdigits=octdigits
patterns.hexdigit=hexdigit
patterns.hexdigits=hexdigits
patterns.sign=sign
patterns.cardinal=digits
patterns.integer=sign^-1*digits
patterns.unsigned=digit^0*period*digits
patterns.float=sign^-1*patterns.unsigned
patterns.cunsigned=digit^0*comma*digits
patterns.cpunsigned=digit^0*(period+comma)*digits
patterns.cfloat=sign^-1*patterns.cunsigned
patterns.cpfloat=sign^-1*patterns.cpunsigned
patterns.number=patterns.float+patterns.integer
patterns.cnumber=patterns.cfloat+patterns.integer
patterns.cpnumber=patterns.cpfloat+patterns.integer
patterns.oct=zero*octdigits
patterns.octal=patterns.oct
patterns.HEX=zero*P("X")*(digit+uppercase)^1
patterns.hex=zero*P("x")*(digit+lowercase)^1
patterns.hexadecimal=zero*S("xX")*hexdigits
patterns.hexafloat=sign^-1*zero*S("xX")*(hexdigit^0*period*hexdigits+hexdigits*period*hexdigit^0+hexdigits)*(S("pP")*sign^-1*hexdigits)^-1
patterns.decafloat=sign^-1*(digit^0*period*digits+digits*period*digit^0+digits)*S("eE")*sign^-1*digits
patterns.propername=(uppercase+lowercase+underscore)*(uppercase+lowercase+underscore+digit)^0*endofstring
patterns.somecontent=(anything-newline-space)^1
patterns.beginline=#(1-newline)
patterns.longtostring=Cs(whitespace^0/""*((patterns.quoted+nonwhitespace^1+whitespace^1/""*(endofstring+Cc(" ")))^0))
local function anywhere(pattern)
return (1-P(pattern))^0*P(pattern)
end
lpeg.anywhere=anywhere
function lpeg.instringchecker(p)
p=anywhere(p)
return function(str)
return lpegmatch(p,str) and true or false
end
end
function lpeg.splitter(pattern,action)
if action then
return (((1-P(pattern))^1)/action+1)^0
else
return (Cs((1-P(pattern))^1)+1)^0
end
end
function lpeg.tsplitter(pattern,action)
if action then
return Ct((((1-P(pattern))^1)/action+1)^0)
else
return Ct((Cs((1-P(pattern))^1)+1)^0)
end
end
local splitters_s,splitters_m,splitters_t={},{},{}
local function splitat(separator,single)
local splitter=(single and splitters_s[separator]) or splitters_m[separator]
if not splitter then
separator=P(separator)
local other=C((1-separator)^0)
if single then
local any=anything
splitter=other*(separator*C(any^0)+"")
splitters_s[separator]=splitter
else
splitter=other*(separator*other)^0
splitters_m[separator]=splitter
end
end
return splitter
end
local function tsplitat(separator)
local splitter=splitters_t[separator]
if not splitter then
splitter=Ct(splitat(separator))
splitters_t[separator]=splitter
end
return splitter
end
lpeg.splitat=splitat
lpeg.tsplitat=tsplitat
function string.splitup(str,separator)
if not separator then
separator=","
end
return lpegmatch(splitters_m[separator] or splitat(separator),str)
end
local cache={}
function lpeg.split(separator,str)
local c=cache[separator]
if not c then
c=tsplitat(separator)
cache[separator]=c
end
return lpegmatch(c,str)
end
function string.split(str,separator)
if separator then
local c=cache[separator]
if not c then
c=tsplitat(separator)
cache[separator]=c
end
return lpegmatch(c,str)
else
return { str }
end
end
local spacing=patterns.spacer^0*newline
local empty=spacing*Cc("")
local nonempty=Cs((1-spacing)^1)*spacing^-1
local content=(empty+nonempty)^1
patterns.textline=content
local linesplitter=tsplitat(newline)
patterns.linesplitter=linesplitter
function string.splitlines(str)
return lpegmatch(linesplitter,str)
end
local cache={}
function lpeg.checkedsplit(separator,str)
local c=cache[separator]
if not c then
separator=P(separator)
local other=C((1-separator)^1)
c=Ct(separator^0*other*(separator^1*other)^0)
cache[separator]=c
end
return lpegmatch(c,str)
end
function string.checkedsplit(str,separator)
local c=cache[separator]
if not c then
separator=P(separator)
local other=C((1-separator)^1)
c=Ct(separator^0*other*(separator^1*other)^0)
cache[separator]=c
end
return lpegmatch(c,str)
end
local function f2(s) local c1,c2=byte(s,1,2) return c1*64+c2-12416 end
local function f3(s) local c1,c2,c3=byte(s,1,3) return (c1*64+c2)*64+c3-925824 end
local function f4(s) local c1,c2,c3,c4=byte(s,1,4) return ((c1*64+c2)*64+c3)*64+c4-63447168 end
local utf8byte=patterns.utf8one/byte+patterns.utf8two/f2+patterns.utf8three/f3+patterns.utf8four/f4
patterns.utf8byte=utf8byte
local cache={}
function lpeg.stripper(str)
if type(str)=="string" then
local s=cache[str]
if not s then
s=Cs(((S(str)^1)/""+1)^0)
cache[str]=s
end
return s
else
return Cs(((str^1)/""+1)^0)
end
end
local cache={}
function lpeg.keeper(str)
if type(str)=="string" then
local s=cache[str]
if not s then
s=Cs((((1-S(str))^1)/""+1)^0)
cache[str]=s
end
return s
else
return Cs((((1-str)^1)/""+1)^0)
end
end
function lpeg.frontstripper(str)
return (P(str)+P(true))*Cs(anything^0)
end
function lpeg.endstripper(str)
return Cs((1-P(str)*endofstring)^0)
end
function lpeg.replacer(one,two,makefunction,isutf)
local pattern
local u=isutf and utf8char or 1
if type(one)=="table" then
local no=#one
local p=P(false)
if no==0 then
for k,v in next,one do
p=p+P(k)/v
end
pattern=Cs((p+u)^0)
elseif no==1 then
local o=one[1]
one,two=P(o[1]),o[2]
pattern=Cs((one/two+u)^0)
else
for i=1,no do
local o=one[i]
p=p+P(o[1])/o[2]
end
pattern=Cs((p+u)^0)
end
else
pattern=Cs((P(one)/(two or "")+u)^0)
end
if makefunction then
return function(str)
return lpegmatch(pattern,str)
end
else
return pattern
end
end
function lpeg.finder(lst,makefunction,isutf)
local pattern
if type(lst)=="table" then
pattern=P(false)
if #lst==0 then
for k,v in next,lst do
pattern=pattern+P(k)
end
else
for i=1,#lst do
pattern=pattern+P(lst[i])
end
end
else
pattern=P(lst)
end
if isutf then
pattern=((utf8char or 1)-pattern)^0*pattern
else
pattern=(1-pattern)^0*pattern
end
if makefunction then
return function(str)
return lpegmatch(pattern,str)
end
else
return pattern
end
end
local splitters_f,splitters_s={},{}
function lpeg.firstofsplit(separator)
local splitter=splitters_f[separator]
if not splitter then
local pattern=P(separator)
splitter=C((1-pattern)^0)
splitters_f[separator]=splitter
end
return splitter
end
function lpeg.secondofsplit(separator)
local splitter=splitters_s[separator]
if not splitter then
local pattern=P(separator)
splitter=(1-pattern)^0*pattern*C(anything^0)
splitters_s[separator]=splitter
end
return splitter
end
local splitters_s,splitters_p={},{}
function lpeg.beforesuffix(separator)
local splitter=splitters_s[separator]
if not splitter then
local pattern=P(separator)
splitter=C((1-pattern)^0)*pattern*endofstring
splitters_s[separator]=splitter
end
return splitter
end
function lpeg.afterprefix(separator)
local splitter=splitters_p[separator]
if not splitter then
local pattern=P(separator)
splitter=pattern*C(anything^0)
splitters_p[separator]=splitter
end
return splitter
end
function lpeg.balancer(left,right)
left,right=P(left),P(right)
return P { left*((1-left-right)+V(1))^0*right }
end
function lpeg.counter(pattern,action)
local n=0
local pattern=(P(pattern)/function() n=n+1 end+anything)^0
if action then
return function(str) n=0;lpegmatch(pattern,str);action(n) end
else
return function(str) n=0;lpegmatch(pattern,str);return n end
end
end
function lpeg.is_lpeg(p)
return p and lpegtype(p)=="pattern"
end
function lpeg.oneof(list,...)
if type(list)~="table" then
list={ list,... }
end
local p=P(list[1])
for l=2,#list do
p=p+P(list[l])
end
return p
end
local sort=table.sort
local function copyindexed(old)
local new={}
for i=1,#old do
new[i]=old
end
return new
end
local function sortedkeys(tab)
local keys,s={},0
for key,_ in next,tab do
s=s+1
keys[s]=key
end
sort(keys)
return keys
end
function lpeg.append(list,pp,delayed,checked)
local p=pp
if #list>0 then
local keys=copyindexed(list)
sort(keys)
for i=#keys,1,-1 do
local k=keys[i]
if p then
p=P(k)+p
else
p=P(k)
end
end
elseif delayed then
local keys=sortedkeys(list)
if p then
for i=1,#keys,1 do
local k=keys[i]
local v=list[k]
p=P(k)/list+p
end
else
for i=1,#keys do
local k=keys[i]
local v=list[k]
if p then
p=P(k)+p
else
p=P(k)
end
end
if p then
p=p/list
end
end
elseif checked then
local keys=sortedkeys(list)
for i=1,#keys do
local k=keys[i]
local v=list[k]
if p then
if k==v then
p=P(k)+p
else
p=P(k)/v+p
end
else
if k==v then
p=P(k)
else
p=P(k)/v
end
end
end
else
local keys=sortedkeys(list)
for i=1,#keys do
local k=keys[i]
local v=list[k]
if p then
p=P(k)/v+p
else
p=P(k)/v
end
end
end
return p
end
local p_false=P(false)
local p_true=P(true)
local lower=utf and utf.lower or string.lower
local upper=utf and utf.upper or string.upper
function lpeg.setutfcasers(l,u)
lower=l or lower
upper=u or upper
end
local function make1(t,rest)
local p=p_false
local keys=sortedkeys(t)
for i=1,#keys do
local k=keys[i]
if k~="" then
local v=t[k]
if v==true then
p=p+P(k)*p_true
elseif v==false then
else
p=p+P(k)*make1(v,v[""])
end
end
end
if rest then
p=p+p_true
end
return p
end
local function make2(t,rest)
local p=p_false
local keys=sortedkeys(t)
for i=1,#keys do
local k=keys[i]
if k~="" then
local v=t[k]
if v==true then
p=p+(P(lower(k))+P(upper(k)))*p_true
elseif v==false then
else
p=p+(P(lower(k))+P(upper(k)))*make2(v,v[""])
end
end
end
if rest then
p=p+p_true
end
return p
end
local function utfchartabletopattern(list,insensitive)
local tree={}
local n=#list
if n==0 then
for s in next,list do
local t=tree
local p,pk
for c in gmatch(s,".") do
if t==true then
t={ [c]=true,[""]=true }
p[pk]=t
p=t
t=false
elseif t==false then
t={ [c]=false }
p[pk]=t
p=t
t=false
else
local tc=t[c]
if not tc then
tc=false
t[c]=false
end
p=t
t=tc
end
pk=c
end
if t==false then
p[pk]=true
elseif t==true then
else
t[""]=true
end
end
else
for i=1,n do
local s=list[i]
local t=tree
local p,pk
for c in gmatch(s,".") do
if t==true then
t={ [c]=true,[""]=true }
p[pk]=t
p=t
t=false
elseif t==false then
t={ [c]=false }
p[pk]=t
p=t
t=false
else
local tc=t[c]
if not tc then
tc=false
t[c]=false
end
p=t
t=tc
end
pk=c
end
if t==false then
p[pk]=true
elseif t==true then
else
t[""]=true
end
end
end
return (insensitive and make2 or make1)(tree)
end
lpeg.utfchartabletopattern=utfchartabletopattern
function lpeg.utfreplacer(list,insensitive)
local pattern=Cs((utfchartabletopattern(list,insensitive)/list+utf8character)^0)
return function(str)
return lpegmatch(pattern,str) or str
end
end
patterns.containseol=lpeg.finder(eol)
local function nextstep(n,step,result)
local m=n%step
local d=floor(n/step)
if d>0 then
local v=V(tostring(step))
local s=result.start
for i=1,d do
if s then
s=v*s
else
s=v
end
end
result.start=s
end
if step>1 and result.start then
local v=V(tostring(step/2))
result[tostring(step)]=v*v
end
if step>0 then
return nextstep(m,step/2,result)
else
return result
end
end
function lpeg.times(pattern,n)
return P(nextstep(n,2^16,{ "start",["1"]=pattern }))
end
do
local trailingzeros=zero^0*-digit
local stripper=Cs((
digits*(
period*trailingzeros/""+period*(digit-trailingzeros)^1*(trailingzeros/"")
)+1
)^0)
lpeg.patterns.stripzeros=stripper
local nonzero=digit-zero
local trailingzeros=zero^1*endofstring
local stripper=Cs((1-period)^0*(
period*trailingzeros/""+period*(nonzero^1+(trailingzeros/"")+zero^1)^0+endofstring
))
lpeg.patterns.stripzero=stripper
end
local byte_to_HEX={}
local byte_to_hex={}
local byte_to_dec={}
local hex_to_byte={}
for i=0,255 do
local H=format("%02X",i)
local h=format("%02x",i)
local d=format("%03i",i)
local c=char(i)
byte_to_HEX[c]=H
byte_to_hex[c]=h
byte_to_dec[c]=d
hex_to_byte[h]=c
hex_to_byte[H]=c
end
local hextobyte=P(2)/hex_to_byte
local bytetoHEX=P(1)/byte_to_HEX
local bytetohex=P(1)/byte_to_hex
local bytetodec=P(1)/byte_to_dec
local hextobytes=Cs(hextobyte^0)
local bytestoHEX=Cs(bytetoHEX^0)
local bytestohex=Cs(bytetohex^0)
local bytestodec=Cs(bytetodec^0)
patterns.hextobyte=hextobyte
patterns.bytetoHEX=bytetoHEX
patterns.bytetohex=bytetohex
patterns.bytetodec=bytetodec
patterns.hextobytes=hextobytes
patterns.bytestoHEX=bytestoHEX
patterns.bytestohex=bytestohex
patterns.bytestodec=bytestodec
function string.toHEX(s)
if not s or s=="" then
return s
else
return lpegmatch(bytestoHEX,s)
end
end
function string.tohex(s)
if not s or s=="" then
return s
else
return lpegmatch(bytestohex,s)
end
end
function string.todec(s)
if not s or s=="" then
return s
else
return lpegmatch(bytestodec,s)
end
end
function string.tobytes(s)
if not s or s=="" then
return s
else
return lpegmatch(hextobytes,s)
end
end
local patterns={}
local function containsws(what)
local p=patterns[what]
if not p then
local p1=P(what)*(whitespace+endofstring)*Cc(true)
local p2=whitespace*P(p1)
p=P(p1)+P(1-p2)^0*p2+Cc(false)
patterns[what]=p
end
return p
end
lpeg.containsws=containsws
function string.containsws(str,what)
return lpegmatch(patterns[what] or containsws(what),str)
end
end -- closure
do -- begin closure to overcome local limits and interference
if not modules then modules={} end modules ['l-functions']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
functions=functions or {}
function functions.dummy() end
end -- closure
do -- begin closure to overcome local limits and interference
if not modules then modules={} end modules ['l-string']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local string=string
local sub,gmatch,format,char,byte,rep,lower=string.sub,string.gmatch,string.format,string.char,string.byte,string.rep,string.lower
local lpegmatch,patterns=lpeg.match,lpeg.patterns
local P,S,C,Ct,Cc,Cs=lpeg.P,lpeg.S,lpeg.C,lpeg.Ct,lpeg.Cc,lpeg.Cs
local unquoted=patterns.squote*C(patterns.nosquote)*patterns.squote+patterns.dquote*C(patterns.nodquote)*patterns.dquote
function string.unquoted(str)
return lpegmatch(unquoted,str) or str
end
function string.quoted(str)
return format("%q",str)
end
function string.count(str,pattern)
local n=0
for _ in gmatch(str,pattern) do
n=n+1
end
return n
end
function string.limit(str,n,sentinel)
if #str>n then
sentinel=sentinel or "..."
return sub(str,1,(n-#sentinel))..sentinel
else
return str
end
end
local stripper=patterns.stripper
local fullstripper=patterns.fullstripper
local collapser=patterns.collapser
local nospacer=patterns.nospacer
local longtostring=patterns.longtostring
function string.strip(str)
return str and lpegmatch(stripper,str) or ""
end
function string.fullstrip(str)
return str and lpegmatch(fullstripper,str) or ""
end
function string.collapsespaces(str)
return str and lpegmatch(collapser,str) or ""
end
function string.nospaces(str)
return str and lpegmatch(nospacer,str) or ""
end
function string.longtostring(str)
return str and lpegmatch(longtostring,str) or ""
end
local pattern=P(" ")^0*P(-1)
function string.is_empty(str)
if not str or str=="" then
return true
else
return lpegmatch(pattern,str) and true or false
end
end
local anything=patterns.anything
local moreescapes=Cc("%")*S(".-+%?()[]*$^{}")
local allescapes=Cc("%")*S(".-+%?()[]*")
local someescapes=Cc("%")*S(".-+%()[]")
local matchescapes=Cc(".")*S("*?")
local pattern_m=Cs ((moreescapes+anything )^0 )
local pattern_a=Cs ((allescapes+anything )^0 )
local pattern_b=Cs ((someescapes+matchescapes+anything )^0 )
local pattern_c=Cs (Cc("^")*(someescapes+matchescapes+anything )^0*Cc("$") )
function string.escapedpattern(str,simple)
return lpegmatch(simple and pattern_b or pattern_a,str)
end
function string.topattern(str,lowercase,strict)
if str=="" or type(str)~="string" then
return ".*"
elseif strict=="all" then
str=lpegmatch(pattern_m,str)
elseif strict then
str=lpegmatch(pattern_c,str)
else
str=lpegmatch(pattern_b,str)
end
if lowercase then
return lower(str)
else
return str
end
end
function string.valid(str,default)
return (type(str)=="string" and str~="" and str) or default or nil
end
string.itself=function(s) return s end
local pattern_c=Ct(C(1)^0)
local pattern_b=Ct((C(1)/byte)^0)
function string.totable(str,bytes)
return lpegmatch(bytes and pattern_b or pattern_c,str)
end
local replacer=lpeg.replacer("@","%%")
function string.tformat(fmt,...)
return format(lpegmatch(replacer,fmt),...)
end
string.quote=string.quoted
string.unquote=string.unquoted
if not string.bytetable then
local limit=5000
function string.bytetable(str)
local n=#str
if n>limit then
local t={ byte(str,1,limit) }
for i=limit+1,n do
t[i]=byte(str,i)
end
return t
else
return { byte(str,1,n) }
end
end
end
end -- closure
do -- begin closure to overcome local limits and interference
if not modules then modules={} end modules ['l-table']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local type,next,tostring,tonumber,select,rawget=type,next,tostring,tonumber,select,rawget
local table,string=table,string
local concat,sort=table.concat,table.sort
local format,lower,dump=string.format,string.lower,string.dump
local getmetatable,setmetatable=getmetatable,setmetatable
local lpegmatch,patterns=lpeg.match,lpeg.patterns
local floor=math.floor
local stripper=patterns.stripper
function table.getn(t)
return t and #t
end
function table.strip(tab)
local lst={}
local l=0
for i=1,#tab do
local s=lpegmatch(stripper,tab[i]) or ""
if s=="" then
else
l=l+1
lst[l]=s
end
end
return lst
end
function table.keys(t)
if t then
local keys={}
local k=0
for key in next,t do
k=k+1
keys[k]=key
end
return keys
else
return {}
end
end
local function compare(a,b)
local ta=type(a)
if ta=="number" then
local tb=type(b)
if ta==tb then
return a<b
elseif tb=="string" then
return tostring(a)<b
end
elseif ta=="string" then
local tb=type(b)
if ta==tb then
return a<b
else
return a<tostring(b)
end
end
return tostring(a)<tostring(b)
end
local function sortedkeys(tab)
if tab then
local srt={}
local category=0
local s=0
for key in next,tab do
s=s+1
srt[s]=key
if category~=3 then
local tkey=type(key)
if category==1 then
if tkey~="string" then
category=3
end
elseif category==2 then
if tkey~="number" then
category=3
end
else
if tkey=="string" then
category=1
elseif tkey=="number" then
category=2
else
category=3
end
end
end
end
if s<2 then
elseif category==3 then
sort(srt,compare)
else
sort(srt)
end
return srt
else
return {}
end
end
local function sortedhashonly(tab)
if tab then
local srt={}
local s=0
for key in next,tab do
if type(key)=="string" then
s=s+1
srt[s]=key
end
end
if s>1 then
sort(srt)
end
return srt
else
return {}
end
end
local function sortedindexonly(tab)
if tab then
local srt={}
local s=0
for key in next,tab do
if type(key)=="number" then
s=s+1
srt[s]=key
end
end
if s>1 then
sort(srt)
end
return srt
else
return {}
end
end
local function sortedhashkeys(tab,cmp)
if tab then
local srt={}
local s=0
for key in next,tab do
if key then
s=s+1
srt[s]=key
end
end
if s>1 then
sort(srt,cmp)
end
return srt
else
return {}
end
end
function table.allkeys(t)
local keys={}
for k,v in next,t do
for k in next,v do
keys[k]=true
end
end
return sortedkeys(keys)
end
table.sortedkeys=sortedkeys
table.sortedhashonly=sortedhashonly
table.sortedindexonly=sortedindexonly
table.sortedhashkeys=sortedhashkeys
local function nothing() end
local function sortedhash(t,cmp)
if t then
local s
if cmp then
s=sortedhashkeys(t,function(a,b) return cmp(t,a,b) end)
else
s=sortedkeys(t)
end
local m=#s
if m==1 then
return next,t
elseif m>0 then
local n=0
return function()
if n<m then
n=n+1
local k=s[n]
return k,t[k]
end
end
end
end
return nothing
end
table.sortedhash=sortedhash
table.sortedpairs=sortedhash
function table.append(t,list)
local n=#t
for i=1,#list do
n=n+1
t[n]=list[i]
end
return t
end
function table.prepend(t,list)
local nl=#list
local nt=nl+#t
for i=#t,1,-1 do
t[nt]=t[i]
nt=nt-1
end
for i=1,#list do
t[i]=list[i]
end
return t
end
function table.merge(t,...)
if not t then
t={}
end
for i=1,select("#",...) do
for k,v in next,(select(i,...)) do
t[k]=v
end
end
return t
end
function table.merged(...)
local t={}
for i=1,select("#",...) do
for k,v in next,(select(i,...)) do
t[k]=v
end
end
return t
end
function table.imerge(t,...)
local nt=#t
for i=1,select("#",...) do
local nst=select(i,...)
for j=1,#nst do
nt=nt+1
t[nt]=nst[j]
end
end
return t
end
function table.imerged(...)
local tmp={}
local ntmp=0
for i=1,select("#",...) do
local nst=select(i,...)
for j=1,#nst do
ntmp=ntmp+1
tmp[ntmp]=nst[j]
end
end
return tmp
end
local function fastcopy(old,metatabletoo)
if old then
local new={}
for k,v in next,old do
if type(v)=="table" then
new[k]=fastcopy(v,metatabletoo)
else
new[k]=v
end
end
if metatabletoo then
local mt=getmetatable(old)
if mt then
setmetatable(new,mt)
end
end
return new
else
return {}
end
end
local function copy(t,tables)
if not tables then
tables={}
end
local tcopy={}
if not tables[t] then
tables[t]=tcopy
end
for i,v in next,t do
if type(i)=="table" then
if tables[i] then
i=tables[i]
else
i=copy(i,tables)
end
end
if type(v)~="table" then
tcopy[i]=v
elseif tables[v] then
tcopy[i]=tables[v]
else
tcopy[i]=copy(v,tables)
end
end
local mt=getmetatable(t)
if mt then
setmetatable(tcopy,mt)
end
return tcopy
end
table.fastcopy=fastcopy
table.copy=copy
function table.derive(parent)
local child={}
if parent then
setmetatable(child,{ __index=parent })
end
return child
end
function table.tohash(t,value)
local h={}
if t then
if value==nil then value=true end
for _,v in next,t do
h[v]=value
end
end
return h
end
function table.fromhash(t)
local hsh={}
local h=0
for k,v in next,t do
if v then
h=h+1
hsh[h]=k
end
end
return hsh
end
local noquotes,hexify,handle,compact,inline,functions,metacheck,accurate
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','const',
}
local function is_simple_table(t,hexify,accurate)
local nt=#t
if nt>0 then
local n=0
for _,v in next,t do
n=n+1
if type(v)=="table" then
return nil
end
end
local haszero=rawget(t,0)
if n==nt then
local tt={}
for i=1,nt do
local v=t[i]
local tv=type(v)
if tv=="number" then
if hexify then
tt[i]=format("0x%X",v)
elseif accurate then
tt[i]=format("%q",v)
else
tt[i]=v
end
elseif tv=="string" then
tt[i]=format("%q",v)
elseif tv=="boolean" then
tt[i]=v and "true" or "false"
else
return nil
end
end
return tt
elseif haszero and (n==nt+1) then
local tt={}
for i=0,nt do
local v=t[i]
local tv=type(v)
if tv=="number" then
if hexify then
tt[i+1]=format("0x%X",v)
elseif accurate then
tt[i+1]=format("%q",v)
else
tt[i+1]=v
end
elseif tv=="string" then
tt[i+1]=format("%q",v)
elseif tv=="boolean" then
tt[i+1]=v and "true" or "false"
else
return nil
end
end
tt[1]="[0] = "..tt[1]
return tt
end
end
return nil
end
table.is_simple_table=is_simple_table
local propername=patterns.propername
local function dummy() end
local function do_serialize(root,name,depth,level,indexed)
if level>0 then
depth=depth.." "
if indexed then
handle(format("%s{",depth))
else
local tn=type(name)
if tn=="number" then
if hexify then
handle(format("%s[0x%X]={",depth,name))
else
handle(format("%s[%s]={",depth,name))
end
elseif tn=="string" then
if noquotes and not reserved[name] and lpegmatch(propername,name) then
handle(format("%s%s={",depth,name))
else
handle(format("%s[%q]={",depth,name))
end
elseif tn=="boolean" then
handle(format("%s[%s]={",depth,name and "true" or "false"))
else
handle(format("%s{",depth))
end
end
end
if root and next(root)~=nil then
local first=nil
local last=0
if compact then
last=#root
for k=1,last do
if rawget(root,k)==nil then
last=k-1
break
end
end
if last>0 then
first=1
end
end
local sk=sortedkeys(root)
for i=1,#sk do
local k=sk[i]
local v=root[k]
local tv=type(v)
local tk=type(k)
if compact and first and tk=="number" and k>=first and k<=last then
if tv=="number" then
if hexify then
handle(format("%s 0x%X,",depth,v))
elseif accurate then
handle(format("%s %q,",depth,v))
else
handle(format("%s %s,",depth,v))
end
elseif tv=="string" then
handle(format("%s %q,",depth,v))
elseif tv=="table" then
if next(v)==nil then
handle(format("%s {},",depth))
elseif inline then
local st=is_simple_table(v,hexify,accurate)
if st then
handle(format("%s { %s },",depth,concat(st,", ")))
else
do_serialize(v,k,depth,level+1,true)
end
else
do_serialize(v,k,depth,level+1,true)
end
elseif tv=="boolean" then
handle(format("%s %s,",depth,v and "true" or "false"))
elseif tv=="function" then
if functions then
handle(format('%s load(%q),',depth,dump(v)))
else
handle(format('%s "function",',depth))
end
else
handle(format("%s %q,",depth,tostring(v)))
end
elseif k=="__p__" then
if false then
handle(format("%s __p__=nil,",depth))
end
elseif tv=="number" then
if tk=="number" then
if hexify then
handle(format("%s [0x%X]=0x%X,",depth,k,v))
elseif accurate then
handle(format("%s [%s]=%q,",depth,k,v))
else
handle(format("%s [%s]=%s,",depth,k,v))
end
elseif tk=="boolean" then
if hexify then
handle(format("%s [%s]=0x%X,",depth,k and "true" or "false",v))
elseif accurate then
handle(format("%s [%s]=%q,",depth,k and "true" or "false",v))
else
handle(format("%s [%s]=%s,",depth,k and "true" or "false",v))
end
elseif tk~="string" then
elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
if hexify then
handle(format("%s %s=0x%X,",depth,k,v))
elseif accurate then
handle(format("%s %s=%q,",depth,k,v))
else
handle(format("%s %s=%s,",depth,k,v))
end
else
if hexify then
handle(format("%s [%q]=0x%X,",depth,k,v))
elseif accurate then
handle(format("%s [%q]=%q,",depth,k,v))
else
handle(format("%s [%q]=%s,",depth,k,v))
end
end
elseif tv=="string" then
if tk=="number" then
if hexify then
handle(format("%s [0x%X]=%q,",depth,k,v))
elseif accurate then
handle(format("%s [%q]=%q,",depth,k,v))
else
handle(format("%s [%s]=%q,",depth,k,v))
end
elseif tk=="boolean" then
handle(format("%s [%s]=%q,",depth,k and "true" or "false",v))
elseif tk~="string" then
elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
handle(format("%s %s=%q,",depth,k,v))
else
handle(format("%s [%q]=%q,",depth,k,v))
end
elseif tv=="table" then
if next(v)==nil then
if tk=="number" then
if hexify then
handle(format("%s [0x%X]={},",depth,k))
elseif accurate then
handle(format("%s [%q]={},",depth,k))
else
handle(format("%s [%s]={},",depth,k))
end
elseif tk=="boolean" then
handle(format("%s [%s]={},",depth,k and "true" or "false"))
elseif tk~="string" then
elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
handle(format("%s %s={},",depth,k))
else
handle(format("%s [%q]={},",depth,k))
end
elseif inline then
local st=is_simple_table(v,hexify,accurate)
if st then
if tk=="number" then
if hexify then
handle(format("%s [0x%X]={ %s },",depth,k,concat(st,", ")))
elseif accurate then
handle(format("%s [%q]={ %s },",depth,k,concat(st,", ")))
else
handle(format("%s [%s]={ %s },",depth,k,concat(st,", ")))
end
elseif tk=="boolean" then
handle(format("%s [%s]={ %s },",depth,k and "true" or "false",concat(st,", ")))
elseif tk~="string" then
elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
handle(format("%s %s={ %s },",depth,k,concat(st,", ")))
else
handle(format("%s [%q]={ %s },",depth,k,concat(st,", ")))
end
else
do_serialize(v,k,depth,level+1)
end
else
do_serialize(v,k,depth,level+1)
end
elseif tv=="boolean" then
if tk=="number" then
if hexify then
handle(format("%s [0x%X]=%s,",depth,k,v and "true" or "false"))
elseif accurate then
handle(format("%s [%q]=%s,",depth,k,v and "true" or "false"))
else
handle(format("%s [%s]=%s,",depth,k,v and "true" or "false"))
end
elseif tk=="boolean" then
handle(format("%s [%s]=%s,",depth,tostring(k),v and "true" or "false"))
elseif tk~="string" then
elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
handle(format("%s %s=%s,",depth,k,v and "true" or "false"))
else
handle(format("%s [%q]=%s,",depth,k,v and "true" or "false"))
end
elseif tv=="function" then
if functions then
local getinfo=debug and debug.getinfo
if getinfo then
local f=getinfo(v).what=="C" and dump(dummy) or dump(v)
if tk=="number" then
if hexify then
handle(format("%s [0x%X]=load(%q),",depth,k,f))
elseif accurate then
handle(format("%s [%q]=load(%q),",depth,k,f))
else
handle(format("%s [%s]=load(%q),",depth,k,f))
end
elseif tk=="boolean" then
handle(format("%s [%s]=load(%q),",depth,k and "true" or "false",f))
elseif tk~="string" then
elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
handle(format("%s %s=load(%q),",depth,k,f))
else
handle(format("%s [%q]=load(%q),",depth,k,f))
end
end
end
else
if tk=="number" then
if hexify then
handle(format("%s [0x%X]=%q,",depth,k,tostring(v)))
elseif accurate then
handle(format("%s [%q]=%q,",depth,k,tostring(v)))
else
handle(format("%s [%s]=%q,",depth,k,tostring(v)))
end
elseif tk=="boolean" then
handle(format("%s [%s]=%q,",depth,k and "true" or "false",tostring(v)))
elseif tk~="string" then
elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
handle(format("%s %s=%q,",depth,k,tostring(v)))
else
handle(format("%s [%q]=%q,",depth,k,tostring(v)))
end
end
end
end
if level>0 then
handle(format("%s},",depth))
end
end
local function serialize(_handle,root,name,specification)
local tname=type(name)
if type(specification)=="table" then
noquotes=specification.noquotes
hexify=specification.hexify
accurate=specification.accurate
handle=_handle or specification.handle or print
functions=specification.functions
compact=specification.compact
inline=specification.inline and compact
metacheck=specification.metacheck
if functions==nil then
functions=true
end
if compact==nil then
compact=true
end
if inline==nil then
inline=compact
end
if metacheck==nil then
metacheck=true
end
else
noquotes=false
hexify=false
handle=_handle or print
compact=true
inline=true
functions=true
metacheck=true
end
if tname=="string" then
if name=="return" then
handle("return {")
else
handle(name.."={")
end
elseif tname=="number" then
if hexify then
handle(format("[0x%X]={",name))
else
handle("["..name.."]={")
end
elseif tname=="boolean" then
if name then
handle("return {")
else
handle("{")
end
else
handle("t={")
end
if root then
if metacheck and getmetatable(root) then
local dummy=root._w_h_a_t_e_v_e_r_
root._w_h_a_t_e_v_e_r_=nil
end
if next(root)~=nil then
do_serialize(root,name,"",0)
end
end
handle("}")
end
function table.serialize(root,name,specification)
local t={}
local n=0
local function flush(s)
n=n+1
t[n]=s
end
serialize(flush,root,name,specification)
return concat(t,"\n")
end
table.tohandle=serialize
local maxtab=2*1024
function table.tofile(filename,root,name,specification)
local f=io.open(filename,'w')
if f then
if maxtab>1 then
local t={}
local n=0
local function flush(s)
n=n+1
t[n]=s
if n>maxtab then
f:write(concat(t,"\n"),"\n")
t={}
n=0
end
end
serialize(flush,root,name,specification)
f:write(concat(t,"\n"),"\n")
else
local function flush(s)
f:write(s,"\n")
end
serialize(flush,root,name,specification)
end
f:close()
io.flush()
end
end
local function flattened(t,f,depth)
if f==nil then
f={}
depth=0xFFFF
elseif tonumber(f) then
depth=f
f={}
elseif not depth then
depth=0xFFFF
end
for k,v in next,t do
if type(k)~="number" then
if depth>0 and type(v)=="table" then
flattened(v,f,depth-1)
else
f[#f+1]=v
end
end
end
for k=1,#t do
local v=t[k]
if depth>0 and type(v)=="table" then
flattened(v,f,depth-1)
else
f[#f+1]=v
end
end
return f
end
table.flattened=flattened
local function collapsed(t,f,h)
if f==nil then
f={}
h={}
end
for k=1,#t do
local v=t[k]
if type(v)=="table" then
collapsed(v,f,h)
elseif not h[v] then
f[#f+1]=v
h[v]=true
end
end
return f
end
local function collapsedhash(t,h)
if h==nil then
h={}
end
for k=1,#t do
local v=t[k]
if type(v)=="table" then
collapsedhash(v,h)
else
h[v]=true
end
end
return h
end
table.collapsed=collapsed
table.collapsedhash=collapsedhash
local function unnest(t,f)
if not f then
f={}
end
for i=1,#t do
local v=t[i]
if type(v)=="table" then
if type(v[1])=="table" then
unnest(v,f)
else
f[#f+1]=v
end
else
f[#f+1]=v
end
end
return f
end
function table.unnest(t)
return unnest(t)
end
local function are_equal(a,b,n,m)
if a==b then
return true
elseif a and b and #a==#b then
if not n then
n=1
end
if not m then
m=#a
end
for i=n,m do
local ai,bi=a[i],b[i]
if ai==bi then
elseif type(ai)=="table" and type(bi)=="table" then
if not are_equal(ai,bi) then
return false
end
else
return false
end
end
return true
else
return false
end
end
local function identical(a,b)
if a~=b then
for ka,va in next,a do
local vb=b[ka]
if va==vb then
elseif type(va)=="table" and type(vb)=="table" then
if not identical(va,vb) then
return false
end
else
return false
end
end
end
return true
end
table.identical=identical
table.are_equal=are_equal
local function sparse(old,nest,keeptables)
local new={}
for k,v in next,old do
if not (v=="" or v==false) then
if nest and type(v)=="table" then
v=sparse(v,nest)
if keeptables or next(v)~=nil then
new[k]=v
end
else
new[k]=v
end
end
end
return new
end
table.sparse=sparse
function table.compact(t)
return sparse(t,true,true)
end
function table.contains(t,v)
if t then
for i=1,#t do
if t[i]==v then
return i
end
end
end
return false
end
function table.count(t)
local n=0
for k,v in next,t do
n=n+1
end
return n
end
function table.swapped(t,s)
local n={}
if s then
for k,v in next,s do
n[k]=v
end
end
for k,v in next,t do
n[v]=k
end
return n
end
function table.hashed(t)
for i=1,#t do
t[t[i]]=i
end
return t
end
function table.mirrored(t)
local n={}
for k,v in next,t do
n[v]=k
n[k]=v
end
return n
end
function table.reversed(t)
if t then
local tt={}
local tn=#t
if tn>0 then
local ttn=0
for i=tn,1,-1 do
ttn=ttn+1
tt[ttn]=t[i]
end
end
return tt
end
end
function table.reverse(t)
if t then
local n=#t
local m=n+1
for i=1,floor(n/2) do
local j=m-i
t[i],t[j]=t[j],t[i]
end
return t
end
end
local function sequenced(t,sep,simple)
if not t then
return ""
elseif type(t)~="table" then
return t
end
local n=#t
local s={}
if n>0 then
for i=1,n do
local v=t[i]
if type(v)=="table" then
s[i]="{"..sequenced(v,sep,simple).."}"
else
s[i]=tostring(t[i])
end
end
else
n=0
for k,v in sortedhash(t) do
if simple then
if v==true then
n=n+1
s[n]=k
elseif v and v~="" then
n=n+1
if type(v)=="table" then
s[n]=k.."={"..sequenced(v,sep,simple).."}"
else
s[n]=k.."="..tostring(v)
end
end
else
n=n+1
if type(v)=="table" then
s[n]=k.."={"..sequenced(v,sep,simple).."}"
else
s[n]=k.."="..tostring(v)
end
end
end
end
if sep==true then
return "{ "..concat(s,", ").." }"
else
return concat(s,sep or " | ")
end
end
table.sequenced=sequenced
function table.print(t,...)
if type(t)~="table" then
print(tostring(t))
else
serialize(print,t,...)
end
end
if setinspector then
setinspector("table",function(v) if type(v)=="table" then serialize(print,v,"table") return true end end)
end
function table.sub(t,i,j)
return { unpack(t,i,j) }
end
function table.is_empty(t)
return not t or next(t)==nil
end
function table.has_one_entry(t)
return t and next(t,next(t))==nil
end
function table.loweredkeys(t)
local l={}
for k,v in next,t do
l[lower(k)]=v
end
return l
end
function table.unique(old)
local hash={}
local new={}
local n=0
for i=1,#old do
local oi=old[i]
if not hash[oi] then
n=n+1
new[n]=oi
hash[oi]=true
end
end
return new
end
function table.sorted(t,...)
sort(t,...)
return t
end
function table.values(t,s)
if t then
local values={}
local keys={}
local v=0
for key,value in next,t do
if not keys[value] then
v=v+1
values[v]=value
keys[k]=key
end
end
if s then
sort(values)
end
return values
else
return {}
end
end
function table.filtered(t,pattern,sort,cmp)
if t and type(pattern)=="string" then
if sort then
local s
if cmp then
s=sortedhashkeys(t,function(a,b) return cmp(t,a,b) end)
else
s=sortedkeys(t)
end
local n=0
local m=#s
local function kv(s)
while n<m do
n=n+1
local k=s[n]
if find(k,pattern) then
return k,t[k]
end
end
end
return kv,s
else
local n=next(t)
local function iterator()
while n~=nil do
local k=n
n=next(t,k)
if find(k,pattern) then
return k,t[k]
end
end
end
return iterator,t
end
else
return nothing
end
end
if not table.move then
function table.move(a1,f,e,t,a2)
if a2 and a1~=a2 then
for i=f,e do
a2[t]=a1[i]
t=t+1
end
return a2
else
t=t+e-f
for i=e,f,-1 do
a1[t]=a1[i]
t=t-1
end
return a1
end
end
end
end -- closure
do -- begin closure to overcome local limits and interference
if not modules then modules={} end modules ['l-boolean']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local type,tonumber=type,tonumber
boolean=boolean or {}
local boolean=boolean
function boolean.tonumber(b)
if b then return 1 else return 0 end
end
function toboolean(str,tolerant)
if str==nil then
return false
elseif str==false then
return false
elseif str==true then
return true
elseif str=="true" then
return true
elseif str=="false" then
return false
elseif not tolerant then
return false
elseif str==0 then
return false
elseif (tonumber(str) or 0)>0 then
return true
else
return str=="yes" or str=="on" or str=="t"
end
end
string.toboolean=toboolean
function string.booleanstring(str)
if str=="0" then
return false
elseif str=="1" then
return true
elseif str=="" then
return false
elseif str=="false" then
return false
elseif str=="true" then
return true
elseif (tonumber(str) or 0)>0 then
return true
else
return str=="yes" or str=="on" or str=="t"
end
end
function string.is_boolean(str,default,strict)
if type(str)=="string" then
if str=="true" or str=="yes" or str=="on" or str=="t" or (not strict and str=="1") then
return true
elseif str=="false" or str=="no" or str=="off" or str=="f" or (not strict and str=="0") then
return false
end
end
return default
end
end -- closure
do -- begin closure to overcome local limits and interference
if not modules then modules={} end modules ['l-number']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local tostring,tonumber=tostring,tonumber
local format,floor,match,rep=string.format,math.floor,string.match,string.rep
local concat,insert=table.concat,table.insert
local lpegmatch=lpeg.match
local floor=math.floor
number=number or {}
local number=number
if bit32 then
local bextract=bit32.extract
local t={
"0","0","0","0","0","0","0","0",
"0","0","0","0","0","0","0","0",
"0","0","0","0","0","0","0","0",
"0","0","0","0","0","0","0","0",
}
function number.tobitstring(b,m,w)
if not w then
w=32
end
local n=w
for i=0,w-1 do
local v=bextract(b,i)
local k=w-i
if v==1 then
n=k
t[k]="1"
else
t[k]="0"
end
end
if w then
return concat(t,"",1,w)
elseif m then
m=33-m*8
if m<1 then
m=1
end
return concat(t,"",1,m)
elseif n<8 then
return concat(t)
elseif n<16 then
return concat(t,"",9)
elseif n<24 then
return concat(t,"",17)
else
return concat(t,"",25)
end
end
else
function number.tobitstring(n,m)
if n>0 then
local t={}
while n>0 do
insert(t,1,n%2>0 and 1 or 0)
n=floor(n/2)
end
local nn=8-#t%8
if nn>0 and nn<8 then
for i=1,nn do
insert(t,1,0)
end
end
if m then
m=m*8-#t
if m>0 then
insert(t,1,rep("0",m))
end
end
return concat(t)
elseif m then
rep("00000000",m)
else
return "00000000"
end
end
end
function number.valid(str,default)
return tonumber(str) or default or nil
end
function number.toevenhex(n)
local s=format("%X",n)
if #s%2==0 then
return s
else
return "0"..s
end
end
function number.bytetodecimal(b)
local d=floor(b*100/255+0.5)
if d>100 then
return 100
elseif d<-100 then
return -100
else
return d
end
end
function number.decimaltobyte(d)
local b=floor(d*255/100+0.5)
if b>255 then
return 255
elseif b<-255 then
return -255
else
return b
end
end
function number.idiv(i,d)
return floor(i/d)
end
end -- closure
do -- begin closure to overcome local limits and interference
if not modules then modules={} end modules ['l-math']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
if not math.ceiling then
math.ceiling=math.ceil
end
if not math.round then
local floor=math.floor
function math.round(x) return floor(x+0.5) end
end
if not math.div then
local floor=math.floor
function math.div(n,m) return floor(n/m) end
end
if not math.mod then
function math.mod(n,m) return n%m end
end
if not math.sind then
local sin,cos,tan=math.sin,math.cos,math.tan
local pipi=2*math.pi/360
function math.sind(d) return sin(d*pipi) end
function math.cosd(d) return cos(d*pipi) end
function math.tand(d) return tan(d*pipi) end
end
if not math.odd then
function math.odd (n) return n%2~=0 end
function math.even(n) return n%2==0 end
end
if not math.cosh then
local exp=math.exp
function math.cosh(x)
local xx=exp(x)
return (xx+1/xx)/2
end
function math.sinh(x)
local xx=exp(x)
return (xx-1/xx)/2
end
function math.tanh(x)
local xx=exp(x)
return (xx-1/xx)/(xx+1/xx)
end
end
if not math.pow then
function math.pow(x,y)
return x^y
end
end
if not math.atan2 then
math.atan2=math.atan
end
if not math.ldexp then
function math.ldexp(x,e)
return x*2.0^e
end
end
if not math.log10 then
local log=math.log
function math.log10(x)
return log(x,10)
end
end
if not math.type then
function math.type()
return "float"
end
end
if not math.tointeger then
math.mininteger=-0x4FFFFFFFFFFF
math.maxinteger=0x4FFFFFFFFFFF
local floor=math.floor
function math.tointeger(n)
local f=floor(n)
return f==n and f or nil
end
end
if not math.ult then
local floor=math.floor
function math.tointeger(m,n)
return floor(m)<floor(n)
end
end
end -- closure
do -- begin closure to overcome local limits and interference
if not modules then modules={} end modules ['l-io']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local io=io
local open,flush,write,read=io.open,io.flush,io.write,io.read
local byte,find,gsub,format=string.byte,string.find,string.gsub,string.format
local concat=table.concat
local type=type
if string.find(os.getenv("PATH") or "",";",1,true) then
io.fileseparator,io.pathseparator="\\",";"
else
io.fileseparator,io.pathseparator="/",":"
end
local large=0x01000000
local medium=0x00100000
local small=0x00020000
local function readall(f)
local size=f:seek("end")
if size>0 then
f:seek("set",0)
return f:read(size)
else
return ""
end
end
io.readall=readall
function io.loaddata(filename,textmode)
local f=open(filename,(textmode and 'r') or 'rb')
if f then
local size=f:seek("end")
local data=nil
if size>0 then
f:seek("set",0)
data=f:read(size)
end
f:close()
return data
end
end
function io.copydata(source,target,action)
local f=open(source,"rb")
if f then
local g=open(target,"wb")
if g then
local size=f:seek("end")
if size>0 then
f:seek("set",0)
local data=f:read(size)
if action then
data=action(data)
end
if data then
g:write(data)
end
end
g:close()
end
f:close()
flush()
end
end
function io.savedata(filename,data,joiner)
local f=open(filename,"wb")
if f then
if type(data)=="table" then
f:write(concat(data,joiner or ""))
elseif type(data)=="function" then
data(f)
else
f:write(data or "")
end
f:close()
flush()
return true
else
return false
end
end
if fio and fio.readline then
local readline=fio.readline
function io.loadlines(filename,n)
local f=open(filename,'r')
if not f then
elseif n then
local lines={}
for i=1,n do
local line=readline(f)
if line then
lines[i]=line
else
break
end
end
f:close()
lines=concat(lines,"\n")
if #lines>0 then
return lines
end
else
local line=readline(f)
f:close()
if line and #line>0 then
return line
end
end
end
else
function io.loadlines(filename,n)
local f=open(filename,'r')
if not f then
elseif n then
local lines={}
for i=1,n do
local line=f:read("*lines")
if line then
lines[i]=line
else
break
end
end
f:close()
lines=concat(lines,"\n")
if #lines>0 then
return lines
end
else
local line=f:read("*line") or ""
f:close()
if #line>0 then
return line
end
end
end
end
function io.loadchunk(filename,n)
local f=open(filename,'rb')
if f then
local data=f:read(n or 1024)
f:close()
if #data>0 then
return data
end
end
end
function io.exists(filename)
local f=open(filename)
if f==nil then
return false
else
f:close()
return true
end
end
function io.size(filename)
local f=open(filename)
if f==nil then
return 0
else
local s=f:seek("end")
f:close()
return s
end
end
local function noflines(f)
if type(f)=="string" then
local f=open(filename)
if f then
local n=f and noflines(f) or 0
f:close()
return n
else
return 0
end
else
local n=0
for _ in f:lines() do
n=n+1
end
f:seek('set',0)
return n
end
end
io.noflines=noflines
local nextchar={
[ 4]=function(f)
return f:read(1,1,1,1)
end,
[ 2]=function(f)
return f:read(1,1)
end,
[ 1]=function(f)
return f:read(1)
end,
[-2]=function(f)
local a,b=f:read(1,1)
return b,a
end,
[-4]=function(f)
local a,b,c,d=f:read(1,1,1,1)
return d,c,b,a
end
}
function io.characters(f,n)
if f then
return nextchar[n or 1],f
end
end
local nextbyte={
[4]=function(f)
local a,b,c,d=f:read(1,1,1,1)
if d then
return byte(a),byte(b),byte(c),byte(d)
end
end,
[3]=function(f)
local a,b,c=f:read(1,1,1)
if b then
return byte(a),byte(b),byte(c)
end
end,
[2]=function(f)
local a,b=f:read(1,1)
if b then
return byte(a),byte(b)
end
end,
[1]=function (f)
local a=f:read(1)
if a then
return byte(a)
end
end,
[-2]=function (f)
local a,b=f:read(1,1)
if b then
return byte(b),byte(a)
end
end,
[-3]=function(f)
local a,b,c=f:read(1,1,1)
if b then
return byte(c),byte(b),byte(a)
end
end,
[-4]=function(f)
local a,b,c,d=f:read(1,1,1,1)
if d then
return byte(d),byte(c),byte(b),byte(a)
end
end
}
function io.bytes(f,n)
if f then
return nextbyte[n or 1],f
else
return nil,nil
end
end
function io.ask(question,default,options)
while true do
write(question)
if options then
write(format(" [%s]",concat(options,"|")))
end
if default then
write(format(" [%s]",default))
end
write(format(" "))
flush()
local answer=read()
answer=gsub(answer,"^%s*(.*)%s*$","%1")
if answer=="" and default then
return default
elseif not options then
return answer
else
for k=1,#options do
if options[k]==answer then
return answer
end
end
local pattern="^"..answer
for k=1,#options do
local v=options[k]
if find(v,pattern) then
return v
end
end
end
end
end
local function readnumber(f,n,m)
if m then
f:seek("set",n)
n=m
end
if n==1 then
return byte(f:read(1))
elseif n==2 then
local a,b=byte(f:read(2),1,2)
return 0x100*a+b
elseif n==3 then
local a,b,c=byte(f:read(3),1,3)
return 0x10000*a+0x100*b+c
elseif n==4 then
local a,b,c,d=byte(f:read(4),1,4)
return 0x1000000*a+0x10000*b+0x100*c+d
elseif n==8 then
local a,b=readnumber(f,4),readnumber(f,4)
return 0x100*a+b
elseif n==12 then
local a,b,c=readnumber(f,4),readnumber(f,4),readnumber(f,4)
return 0x10000*a+0x100*b+c
elseif n==-2 then
local b,a=byte(f:read(2),1,2)
return 0x100*a+b
elseif n==-3 then
local c,b,a=byte(f:read(3),1,3)
return 0x10000*a+0x100*b+c
elseif n==-4 then
local d,c,b,a=byte(f:read(4),1,4)
return 0x1000000*a+0x10000*b+0x100*c+d
elseif n==-8 then
local h,g,f,e,d,c,b,a=byte(f:read(8),1,8)
return 0x100000000000000*a+0x1000000000000*b+0x10000000000*c+0x100000000*d+0x1000000*e+0x10000*f+0x100*g+h
else
return 0
end
end
io.readnumber=readnumber
function io.readstring(f,n,m)
if m then
f:seek("set",n)
n=m
end
local str=gsub(f:read(n),"\000","")
return str
end
end -- closure
do -- begin closure to overcome local limits and interference
if not modules then modules={} end modules ['l-os']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local os=os
local date,time=os.date,os.time
local find,format,gsub,upper,gmatch=string.find,string.format,string.gsub,string.upper,string.gmatch
local concat=table.concat
local random,ceil,randomseed=math.random,math.ceil,math.randomseed
local type,setmetatable,tonumber,tostring=type,setmetatable,tonumber,tostring
do
local selfdir=os.selfdir
if selfdir=="" then
selfdir=nil
end
if not selfdir then
if arg then
for i=1,#arg do
local a=arg[i]
if find(a,"^%-%-[c:]*texmfbinpath=") then
selfdir=gsub(a,"^.-=","")
break
end
end
end
if not selfdir then
selfdir=os.selfbin or "luatex"
if find(selfdir,"[/\\]") then
selfdir=gsub(selfdir,"[/\\][^/\\]*$","")
elseif os.getenv then
local path=os.getenv("PATH")
local name=gsub(selfdir,"^.*[/\\][^/\\]","")
local patt="[^:]+"
if os.type=="windows" then
patt="[^;]+"
name=name..".exe"
end
local isfile
if lfs then
local attributes=lfs.attributes
isfile=function(name)
local a=attributes(name,"mode")
return a=="file" or a=="link" or nil
end
else
local open=io.open
isfile=function(name)
local f=open(name)
if f then
f:close()
return true
end
end
end
for p in gmatch(path,patt) do
if isfile(p.."/"..name) then
selfdir=p
break
end
end
end
end
os.selfdir=selfdir or "."
end
end
math.initialseed=tonumber(string.sub(string.reverse(tostring(ceil(socket and socket.gettime()*10000 or time()))),1,6))
randomseed(math.initialseed)
if not os.__getenv__ then
os.__getenv__=os.getenv
os.__setenv__=os.setenv
if os.env then
local osgetenv=os.getenv
local ossetenv=os.setenv
local osenv=os.env local _=osenv.PATH
function os.setenv(k,v)
if v==nil then
v=""
end
local K=upper(k)
osenv[K]=v
if type(v)=="table" then
v=concat(v,";")
end
ossetenv(K,v)
end
function os.getenv(k)
local K=upper(k)
local v=osenv[K] or osenv[k] or osgetenv(K) or osgetenv(k)
if v=="" then
return nil
else
return v
end
end
else
local ossetenv=os.setenv
local osgetenv=os.getenv
local osenv={}
function os.setenv(k,v)
if v==nil then
v=""
end
local K=upper(k)
osenv[K]=v
end
function os.getenv(k)
local K=upper(k)
local v=osenv[K] or osgetenv(K) or osgetenv(k)
if v=="" then
return nil
else
return v
end
end
local function __index(t,k)
return os.getenv(k)
end
local function __newindex(t,k,v)
os.setenv(k,v)
end
os.env={}
setmetatable(os.env,{ __index=__index,__newindex=__newindex } )
end
end
local execute=os.execute
local iopopen=io.popen
local function resultof(command)
local handle=iopopen(command,"r")
if handle then
local result=handle:read("*all") or ""
handle:close()
return result
else
return ""
end
end
os.resultof=resultof
function os.pipeto(command)
return iopopen(command,"w")
end
if not io.fileseparator then
if find(os.getenv("PATH"),";",1,true) then
io.fileseparator,io.pathseparator,os.type="\\",";",os.type or "windows"
else
io.fileseparator,io.pathseparator,os.type="/",":",os.type or "unix"
end
end
os.type=os.type or (io.pathseparator==";" and "windows") or "unix"
os.name=os.name or (os.type=="windows" and "mswin" ) or "linux"
if os.type=="windows" then
os.libsuffix,os.binsuffix,os.binsuffixes='dll','exe',{ 'exe','cmd','bat' }
else
os.libsuffix,os.binsuffix,os.binsuffixes='so','',{ '' }
end
local launchers={
windows="start %s",
macosx="open %s",
unix="xdg-open %s &> /dev/null &",
}
function os.launch(str)
local command=format(launchers[os.name] or launchers.unix,str)
execute(command)
end
local gettimeofday=os.gettimeofday or os.clock
os.gettimeofday=gettimeofday
local startuptime=gettimeofday()
function os.runtime()
return gettimeofday()-startuptime
end
local resolvers=os.resolvers or {}
os.resolvers=resolvers
setmetatable(os,{ __index=function(t,k)
local r=resolvers[k]
return r and r(t,k) or nil
end })
local name,platform=os.name or "linux",os.getenv("MTX_PLATFORM") or ""
if platform~="" then
os.platform=platform
elseif os.type=="windows" then
function resolvers.platform(t,k)
local architecture=os.getenv("PROCESSOR_ARCHITECTURE") or ""
local platform=""
if find(architecture,"AMD64",1,true) then
platform="win64"
else
platform="mswin"
end
os.setenv("MTX_PLATFORM",platform)
os.platform=platform
return platform
end
elseif name=="linux" then
function resolvers.platform(t,k)
local architecture=os.getenv("HOSTTYPE") or resultof("uname -m") or ""
local platform=os.getenv("MTX_PLATFORM") or ""
local musl=find(os.selfdir or "","linuxmusl")
if platform~="" then
elseif find(architecture,"x86_64",1,true) then
platform=musl and "linuxmusl" or "linux-64"
elseif find(architecture,"ppc",1,true) then
platform="linux-ppc"
else
platform=musl and "linuxmusl" or "linux"
end
os.setenv("MTX_PLATFORM",platform)
os.platform=platform
return platform
end
elseif name=="macosx" then
function resolvers.platform(t,k)
local architecture=resultof("echo $HOSTTYPE") or ""
local platform=""
if architecture=="" then
platform="osx-intel"
elseif find(architecture,"i386",1,true) then
platform="osx-intel"
elseif find(architecture,"x86_64",1,true) then
platform="osx-64"
elseif find(architecture,"arm64",1,true) then
platform="osx-64"
else
platform="osx-ppc"
end
os.setenv("MTX_PLATFORM",platform)
os.platform=platform
return platform
end
elseif name=="sunos" then
function resolvers.platform(t,k)
local architecture=resultof("uname -m") or ""
local platform=""
if find(architecture,"sparc",1,true) then
platform="solaris-sparc"
else
platform="solaris-intel"
end
os.setenv("MTX_PLATFORM",platform)
os.platform=platform
return platform
end
elseif name=="freebsd" then
function resolvers.platform(t,k)
local architecture=resultof("uname -m") or ""
local platform=""
if find(architecture,"amd64",1,true) then
platform="freebsd-amd64"
else
platform="freebsd"
end
os.setenv("MTX_PLATFORM",platform)
os.platform=platform
return platform
end
elseif name=="kfreebsd" then
function resolvers.platform(t,k)
local architecture=os.getenv("HOSTTYPE") or resultof("uname -m") or ""
local platform=""
if find(architecture,"x86_64",1,true) then
platform="kfreebsd-amd64"
else
platform="kfreebsd-i386"
end
os.setenv("MTX_PLATFORM",platform)
os.platform=platform
return platform
end
else
function resolvers.platform(t,k)
local platform="linux"
os.setenv("MTX_PLATFORM",platform)
os.platform=platform
return platform
end
end
os.newline=name=="windows" and "\013\010" or "\010"
function resolvers.bits(t,k)
local bits=find(os.platform,"64",1,true) and 64 or 32
os.bits=bits
return bits
end
local t={ 8,9,"a","b" }
function os.uuid()
return format("%04x%04x-4%03x-%s%03x-%04x-%04x%04x%04x",
random(0xFFFF),random(0xFFFF),
random(0x0FFF),
t[ceil(random(4))] or 8,random(0x0FFF),
random(0xFFFF),
random(0xFFFF),random(0xFFFF),random(0xFFFF)
)
end
local d
function os.timezone(delta)
d=d or ((tonumber(date("%H")) or 0)-(tonumber(date("!%H")) or 0))
if delta then
if d>0 then
return format("+%02i:00",d)
else
return format("-%02i:00",-d)
end
else
return 1
end
end
local timeformat=format("%%s%s",os.timezone(true))
local dateformat="!%Y-%m-%d %H:%M:%S"
local lasttime=nil
local lastdate=nil
function os.fulltime(t,default)
t=t and tonumber(t) or 0
if t>0 then
elseif default then
return default
else
t=time()
end
if t~=lasttime then
lasttime=t
lastdate=format(timeformat,date(dateformat))
end
return lastdate
end
local dateformat="%Y-%m-%d %H:%M:%S"
local lasttime=nil
local lastdate=nil
function os.localtime(t,default)
t=t and tonumber(t) or 0
if t>0 then
elseif default then
return default
else
t=time()
end
if t~=lasttime then
lasttime=t
lastdate=date(dateformat,t)
end
return lastdate
end
function os.converttime(t,default)
local t=tonumber(t)
if t and t>0 then
return date(dateformat,t)
else
return default or "-"
end
end
local memory={}
local function which(filename)
local fullname=memory[filename]
if fullname==nil then
local suffix=file.suffix(filename)
local suffixes=suffix=="" and os.binsuffixes or { suffix }
for directory in gmatch(os.getenv("PATH"),"[^"..io.pathseparator.."]+") do
local df=file.join(directory,filename)
for i=1,#suffixes do
local dfs=file.addsuffix(df,suffixes[i])
if io.exists(dfs) then
fullname=dfs
break
end
end
end
if not fullname then
fullname=false
end
memory[filename]=fullname
end
return fullname
end
os.which=which
os.where=which
function os.today()
return date("!*t")
end
function os.now()
return date("!%Y-%m-%d %H:%M:%S")
end
if not os.sleep then
local socket=socket
function os.sleep(n)
if not socket then
socket=require("socket")
end
socket.sleep(n)
end
end
local function isleapyear(year)
return (year%4==0) and (year%100~=0 or year%400==0)
end
os.isleapyear=isleapyear
local days={ 31,28,31,30,31,30,31,31,30,31,30,31 }
local function nofdays(year,month,day)
if not month then
return isleapyear(year) and 365 or 364
elseif not day then
return month==2 and isleapyear(year) and 29 or days[month]
else
for i=1,month-1 do
day=day+days[i]
end
if month>2 and isleapyear(year) then
day=day+1
end
return day
end
end
os.nofdays=nofdays
function os.weekday(day,month,year)
return date("%w",time { year=year,month=month,day=day })+1
end
function os.validdate(year,month,day)
if month<1 then
month=1
elseif month>12 then
month=12
end
if day<1 then
day=1
else
local max=nofdays(year,month)
if day>max then
day=max
end
end
return year,month,day
end
function os.date(fmt,...)
if not fmt then
fmt="%Y-%m-%d %H:%M"
end
return date(fmt,...)
end
local osexit=os.exit
local exitcode=nil
function os.setexitcode(code)
exitcode=code
end
function os.exit(c)
if exitcode~=nil then
return osexit(exitcode)
end
if c~=nil then
return osexit(c)
end
return osexit()
end
end -- closure
do -- begin closure to overcome local limits and interference
if not modules then modules={} end modules ['l-file']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
file=file or {}
local file=file
if not lfs then
lfs=optionalrequire("lfs")
end
local insert,concat=table.insert,table.concat
local match,find,gmatch=string.match,string.find,string.gmatch
local lpegmatch=lpeg.match
local getcurrentdir,attributes=lfs.currentdir,lfs.attributes
local checkedsplit=string.checkedsplit
local P,R,S,C,Cs,Cp,Cc,Ct=lpeg.P,lpeg.R,lpeg.S,lpeg.C,lpeg.Cs,lpeg.Cp,lpeg.Cc,lpeg.Ct
local attributes=lfs.attributes
function lfs.isdir(name)
if name then
return attributes(name,"mode")=="directory"
end
end
function lfs.isfile(name)
if name then
local a=attributes(name,"mode")
return a=="file" or a=="link" or nil
end
end
function lfs.isfound(name)
if name then
local a=attributes(name,"mode")
return (a=="file" or a=="link") and name or nil
end
end
function lfs.modification(name)
return name and attributes(name,"modification") or nil
end
if sandbox then
sandbox.redefine(lfs.isfile,"lfs.isfile")
sandbox.redefine(lfs.isdir,"lfs.isdir")
sandbox.redefine(lfs.isfound,"lfs.isfound")
end
local colon=P(":")
local period=P(".")
local periods=P("..")
local fwslash=P("/")
local bwslash=P("\\")
local slashes=S("\\/")
local noperiod=1-period
local noslashes=1-slashes
local name=noperiod^1
local suffix=period/""*(1-period-slashes)^1*-1
local pattern=C((1-(slashes^1*noslashes^1*-1))^1)*P(1)
local function pathpart(name,default)
return name and lpegmatch(pattern,name) or default or ""
end
local pattern=(noslashes^0*slashes)^1*C(noslashes^1)*-1
local function basename(name)
return name and lpegmatch(pattern,name) or name
end
local pattern=(noslashes^0*slashes^1)^0*Cs((1-suffix)^1)*suffix^0
local function nameonly(name)
return name and lpegmatch(pattern,name) or name
end
local pattern=(noslashes^0*slashes)^0*(noperiod^1*period)^1*C(noperiod^1)*-1
local function suffixonly(name)
return name and lpegmatch(pattern,name) or ""
end
local pattern=(noslashes^0*slashes)^0*noperiod^1*((period*C(noperiod^1))^1)*-1+Cc("")
local function suffixesonly(name)
if name then
return lpegmatch(pattern,name)
else
return ""
end
end
file.pathpart=pathpart
file.basename=basename
file.nameonly=nameonly
file.suffixonly=suffixonly
file.suffix=suffixonly
file.suffixesonly=suffixesonly
file.suffixes=suffixesonly
file.dirname=pathpart
file.extname=suffixonly
local drive=C(R("az","AZ"))*colon
local path=C((noslashes^0*slashes)^0)
local suffix=period*C(P(1-period)^0*P(-1))
local base=C((1-suffix)^0)
local rest=C(P(1)^0)
drive=drive+Cc("")
path=path+Cc("")
base=base+Cc("")
suffix=suffix+Cc("")
local pattern_a=drive*path*base*suffix
local pattern_b=path*base*suffix
local pattern_c=C(drive*path)*C(base*suffix)
local pattern_d=path*rest
function file.splitname(str,splitdrive)
if not str then
elseif splitdrive then
return lpegmatch(pattern_a,str)
else
return lpegmatch(pattern_b,str)
end
end
function file.splitbase(str)
if str then
return lpegmatch(pattern_d,str)
else
return "",str
end
end
function file.nametotable(str,splitdrive)
if str then
local path,drive,subpath,name,base,suffix=lpegmatch(pattern_c,str)
if splitdrive then
return {
path=path,
drive=drive,
subpath=subpath,
name=name,
base=base,
suffix=suffix,
}
else
return {
path=path,
name=name,
base=base,
suffix=suffix,
}
end
end
end
local pattern=Cs(((period*(1-period-slashes)^1*-1)/""+1)^1)
function file.removesuffix(name)
return name and lpegmatch(pattern,name)
end
local suffix=period/""*(1-period-slashes)^1*-1
local pattern=Cs((noslashes^0*slashes^1)^0*((1-suffix)^1))*Cs(suffix)
function file.addsuffix(filename,suffix,criterium)
if not filename or not suffix or suffix=="" then
return filename
elseif criterium==true then
return filename.."."..suffix
elseif not criterium then
local n,s=lpegmatch(pattern,filename)
if not s or s=="" then
return filename.."."..suffix
else
return filename
end
else
local n,s=lpegmatch(pattern,filename)
if s and s~="" then
local t=type(criterium)
if t=="table" then
for i=1,#criterium do
if s==criterium[i] then
return filename
end
end
elseif t=="string" then
if s==criterium then
return filename
end
end
end
return (n or filename).."."..suffix
end
end
local suffix=period*(1-period-slashes)^1*-1
local pattern=Cs((1-suffix)^0)
function file.replacesuffix(name,suffix)
if name and suffix and suffix~="" then
return lpegmatch(pattern,name).."."..suffix
else
return name
end
end
local reslasher=lpeg.replacer(P("\\"),"/")
function file.reslash(str)
return str and lpegmatch(reslasher,str)
end
if lfs.isreadablefile and lfs.iswritablefile then
file.is_readable=lfs.isreadablefile
file.is_writable=lfs.iswritablefile
else
function file.is_writable(name)
if not name then
elseif lfs.isdir(name) then
name=name.."/m_t_x_t_e_s_t.tmp"
local f=io.open(name,"wb")
if f then
f:close()
os.remove(name)
return true
end
elseif lfs.isfile(name) then
local f=io.open(name,"ab")
if f then
f:close()
return true
end
else
local f=io.open(name,"ab")
if f then
f:close()
os.remove(name)
return true
end
end
return false
end
local readable=P("r")*Cc(true)
function file.is_readable(name)
if name then
local a=attributes(name)
return a and lpegmatch(readable,a.permissions) or false
else
return false
end
end
end
file.isreadable=file.is_readable
file.iswritable=file.is_writable
function file.size(name)
if name then
local a=attributes(name)
return a and a.size or 0
else
return 0
end
end
function file.splitpath(str,separator)
return str and checkedsplit(lpegmatch(reslasher,str),separator or io.pathseparator)
end
function file.joinpath(tab,separator)
return tab and concat(tab,separator or io.pathseparator)
end
local someslash=S("\\/")
local stripper=Cs(P(fwslash)^0/""*reslasher)
local isnetwork=someslash*someslash*(1-someslash)+(1-fwslash-colon)^1*colon
local isroot=fwslash^1*-1
local hasroot=fwslash^1
local reslasher=lpeg.replacer(S("\\/"),"/")
local deslasher=lpeg.replacer(S("\\/")^1,"/")
function file.join(one,two,three,...)
if not two then
return one=="" and one or lpegmatch(reslasher,one)
end
if one=="" then
return lpegmatch(stripper,three and concat({ two,three,... },"/") or two)
end
if lpegmatch(isnetwork,one) then
local one=lpegmatch(reslasher,one)
local two=lpegmatch(deslasher,three and concat({ two,three,... },"/") or two)
if lpegmatch(hasroot,two) then
return one..two
else
return one.."/"..two
end
elseif lpegmatch(isroot,one) then
local two=lpegmatch(deslasher,three and concat({ two,three,... },"/") or two)
if lpegmatch(hasroot,two) then
return two
else
return "/"..two
end
else
return lpegmatch(deslasher,concat({ one,two,three,... },"/"))
end
end
local drivespec=R("az","AZ")^1*colon
local anchors=fwslash+drivespec
local untouched=periods+(1-period)^1*P(-1)
local mswindrive=Cs(drivespec*(bwslash/"/"+fwslash)^0)
local mswinuncpath=(bwslash+fwslash)*(bwslash+fwslash)*Cc("//")
local splitstarter=(mswindrive+mswinuncpath+Cc(false))*Ct(lpeg.splitat(S("/\\")^1))
local absolute=fwslash
function file.collapsepath(str,anchor)
if not str then
return
end
if anchor==true and not lpegmatch(anchors,str) then
str=getcurrentdir().."/"..str
end
if str=="" or str=="." then
return "."
elseif lpegmatch(untouched,str) then
return lpegmatch(reslasher,str)
end
local starter,oldelements=lpegmatch(splitstarter,str)
local newelements={}
local i=#oldelements
while i>0 do
local element=oldelements[i]
if element=='.' then
elseif element=='..' then
local n=i-1
while n>0 do
local element=oldelements[n]
if element~='..' and element~='.' then
oldelements[n]='.'
break
else
n=n-1
end
end
if n<1 then
insert(newelements,1,'..')
end
elseif element~="" then
insert(newelements,1,element)
end
i=i-1
end
if #newelements==0 then
return starter or "."
elseif starter then
return starter..concat(newelements,'/')
elseif lpegmatch(absolute,str) then
return "/"..concat(newelements,'/')
else
newelements=concat(newelements,'/')
if anchor=="." and find(str,"^%./") then
return "./"..newelements
else
return newelements
end
end
end
local validchars=R("az","09","AZ","--","..")
local pattern_a=lpeg.replacer(1-validchars)
local pattern_a=Cs((validchars+P(1)/"-")^1)
local whatever=P("-")^0/""
local pattern_b=Cs(whatever*(1-whatever*-1)^1)
function file.robustname(str,strict)
if str then
str=lpegmatch(pattern_a,str) or str
if strict then
return lpegmatch(pattern_b,str) or str
else
return str
end
end
end
local loaddata=io.loaddata
local savedata=io.savedata
file.readdata=loaddata
file.savedata=savedata
function file.copy(oldname,newname)
if oldname and newname then
local data=loaddata(oldname)
if data and data~="" then
savedata(newname,data)
end
end
end
local letter=R("az","AZ")+S("_-+")
local separator=P("://")
local qualified=period^0*fwslash+letter*colon+letter^1*separator+letter^1*fwslash
local rootbased=fwslash+letter*colon
lpeg.patterns.qualified=qualified
lpeg.patterns.rootbased=rootbased
function file.is_qualified_path(filename)
return filename and lpegmatch(qualified,filename)~=nil
end
function file.is_rootbased_path(filename)
return filename and lpegmatch(rootbased,filename)~=nil
end
function file.strip(name,dir)
if name then
local b,a=match(name,"^(.-)"..dir.."(.*)$")
return a~="" and a or name
end
end
function lfs.mkdirs(path)
local full=""
for sub in gmatch(path,"(/*[^\\/]+)") do
full=full..sub
lfs.mkdir(full)
end
end
function file.withinbase(path)
local l=0
if not find(path,"^/") then
path="/"..path
end
for dir in gmatch(path,"/([^/]+)") do
if dir==".." then
l=l-1
elseif dir~="." then
l=l+1
end
if l<0 then
return false
end
end
return true
end
local symlinkattributes=lfs.symlinkattributes
function lfs.readlink(name)
return symlinkattributes(name,"target") or nil
end
end -- closure
do -- begin closure to overcome local limits and interference
if not modules then modules={} end modules ['l-gzip']={
version=1.001,
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
gzip=gzip or {}
if not zlib then
zlib=xzip
elseif not xzip then
xzip=zlib
end
if zlib then
local suffix=file.suffix
local suffixes=file.suffixes
local find=string.find
local openfile=io.open
local gzipwindow=15+16
local gziplevel=3
local identifier="^\x1F\x8B\x08"
local compress=zlib.compress
local decompress=zlib.decompress
function gzip.load(filename)
local f=openfile(filename,"rb")
if not f then
else
local data=f:read("*all")
f:close()
if data and data~="" then
if suffix(filename)=="gz" then
data=decompress(data,gzipwindow)
end
return data
end
end
end
function gzip.save(filename,data,level)
if suffix(filename)~="gz" then
filename=filename..".gz"
end
local f=openfile(filename,"wb")
if f then
data=compress(data or "",level or gziplevel,nil,gzipwindow)
f:write(data)
f:close()
return #data
end
end
function gzip.suffix(filename)
local suffix,extra=suffixes(filename)
local gzipped=extra=="gz"
return suffix,gzipped
end
function gzip.compressed(s)
return s and find(s,identifier)
end
function gzip.compress(s,level)
if s and not find(s,identifier) then
if not level then
level=gziplevel
elseif level<=0 then
return s
elseif level>9 then
level=9
end
return compress(s,level or gziplevel,nil,gzipwindow) or s
end
end
function gzip.decompress(s)
if s and find(s,identifier) then
return decompress(s,gzipwindow)
else
return s
end
end
end
end -- closure
do -- begin closure to overcome local limits and interference
if not modules then modules={} end modules ['l-md5']={
version=1.001,
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
if not md5 then
md5=optionalrequire("md5")
end
if not md5 then
md5={
sum=function(str) print("error: md5 is not loaded (sum ignored)") return str end,
sumhexa=function(str) print("error: md5 is not loaded (sumhexa ignored)") return str end,
}
end
local md5,file=md5,file
local gsub=string.gsub
local modification,isfile,touch=lfs.modification,lfs.isfile,lfs.touch
local loaddata,savedata=io.loaddata,io.savedata
do
local patterns=lpeg and lpeg.patterns
if patterns then
local bytestoHEX=patterns.bytestoHEX
local bytestohex=patterns.bytestohex
local bytestodec=patterns.bytestodec
local lpegmatch=lpeg.match
local md5sum=md5.sum
if not md5.HEX then function md5.HEX(str) if str then return lpegmatch(bytestoHEX,md5sum(str)) end end end
if not md5.hex then function md5.hex(str) if str then return lpegmatch(bytestohex,md5sum(str)) end end end
if not md5.dec then function md5.dec(str) if str then return lpegmatch(bytestodec,md5sum(str)) end end end
md5.sumhexa=md5.hex
md5.sumHEXA=md5.HEX
end
end
local md5HEX=md5.HEX
function file.needsupdating(oldname,newname,threshold)
local oldtime=modification(oldname)
if oldtime then
local newtime=modification(newname)
if not newtime then
return true
elseif newtime>=oldtime then
return false
elseif oldtime-newtime<(threshold or 1) then
return false
else
return true
end
else
return false
end
end
file.needs_updating=file.needsupdating
function file.syncmtimes(oldname,newname)
local oldtime=modification(oldname)
if oldtime and isfile(newname) then
touch(newname,oldtime,oldtime)
end
end
local function checksum(name)
if md5 then
local data=loaddata(name)
if data then
return md5HEX(data)
end
end
return nil
end
file.checksum=checksum
function file.loadchecksum(name)
if md5 then
local data=loaddata(name..".md5")
return data and (gsub(data,"%s",""))
end
return nil
end
function file.savechecksum(name,checksum)
if not checksum then checksum=checksum(name) end
if checksum then
savedata(name..".md5",checksum)
return checksum
end
return nil
end
end -- closure
do -- begin closure to overcome local limits and interference
if not modules then modules={} end modules ['l-dir']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local type,select=type,select
local find,gmatch,match,gsub,sub=string.find,string.gmatch,string.match,string.gsub,string.sub
local concat,insert,remove,unpack=table.concat,table.insert,table.remove,table.unpack
local lpegmatch=lpeg.match
local P,S,R,C,Cc,Cs,Ct,Cv,V=lpeg.P,lpeg.S,lpeg.R,lpeg.C,lpeg.Cc,lpeg.Cs,lpeg.Ct,lpeg.Cv,lpeg.V
dir=dir or {}
local dir=dir
local lfs=lfs
local attributes=lfs.attributes
local walkdir=lfs.dir
local isdir=lfs.isdir
local isfile=lfs.isfile
local currentdir=lfs.currentdir
local chdir=lfs.chdir
local mkdir=lfs.mkdir
local onwindows=os.type=="windows" or find(os.getenv("PATH"),";",1,true)
if onwindows then
local tricky=S("/\\")*P(-1)
isdir=function(name)
if lpegmatch(tricky,name) then
return attributes(name,"mode")=="directory"
else
return attributes(name.."/.","mode")=="directory"
end
end
isfile=function(name)
return attributes(name,"mode")=="file"
end
lfs.isdir=isdir
lfs.isfile=isfile
else
isdir=function(name)
return attributes(name,"mode")=="directory"
end
isfile=function(name)
return attributes(name,"mode")=="file"
end
lfs.isdir=isdir
lfs.isfile=isfile
end
function dir.current()
return (gsub(currentdir(),"\\","/"))
end
local function glob_pattern_function(path,patt,recurse,action)
if isdir(path) then
local usedpath
if path=="/" then
usedpath="/."
elseif not find(path,"/$") then
usedpath=path.."/."
path=path.."/"
else
usedpath=path
end
local dirs
local nofdirs=0
for name,mode,size,time in walkdir(usedpath) do
if name~="." and name~=".." then
local full=path..name
if mode==nil then
mode=attributes(full,'mode')
end
if mode=='file' then
if not patt or find(full,patt) then
action(full,size,time)
end
elseif recurse and mode=="directory" then
if dirs then
nofdirs=nofdirs+1
dirs[nofdirs]=full
else
nofdirs=1
dirs={ full }
end
end
end
end
if dirs then
for i=1,nofdirs do
glob_pattern_function(dirs[i],patt,recurse,action)
end
end
end
end
local function glob_pattern_table(path,patt,recurse,result)
if not result then
result={}
end
local usedpath
if path=="/" then
usedpath="/."
elseif not find(path,"/$") then
usedpath=path.."/."
path=path.."/"
else
usedpath=path
end
local dirs
local nofdirs=0
local noffiles=#result
for name,mode in walkdir(usedpath) do
if name~="." and name~=".." then
local full=path..name
if mode==nil then
mode=attributes(full,'mode')
end
if mode=='file' then
if not patt or find(full,patt) then
noffiles=noffiles+1
result[noffiles]=full
end
elseif recurse and mode=="directory" then
if dirs then
nofdirs=nofdirs+1
dirs[nofdirs]=full
else
nofdirs=1
dirs={ full }
end
end
end
end
if dirs then
for i=1,nofdirs do
glob_pattern_table(dirs[i],patt,recurse,result)
end
end
return result
end
local function globpattern(path,patt,recurse,method)
local kind=type(method)
if patt and sub(patt,1,-3)==path then
patt=false
end
local okay=isdir(path)
if kind=="function" then
return okay and glob_pattern_function(path,patt,recurse,method) or {}
elseif kind=="table" then
return okay and glob_pattern_table(path,patt,recurse,method) or method
else
return okay and glob_pattern_table(path,patt,recurse,{}) or {}
end
end
dir.globpattern=globpattern
local function collectpattern(path,patt,recurse,result)
local ok,scanner
result=result or {}
if path=="/" then
ok,scanner,first=xpcall(function() return walkdir(path..".") end,function() end)
else
ok,scanner,first=xpcall(function() return walkdir(path) end,function() end)
end
if ok and type(scanner)=="function" then
if not find(path,"/$") then
path=path..'/'
end
for name in scanner,first do
if name=="." then
elseif name==".." then
else
local full=path..name
local attr=attributes(full)
local mode=attr.mode
if mode=='file' then
if find(full,patt) then
result[name]=attr
end
elseif recurse and mode=="directory" then
attr.list=collectpattern(full,patt,recurse)
result[name]=attr
end
end
end
end
return result
end
dir.collectpattern=collectpattern
local separator,pattern
if onwindows then
local slash=S("/\\")/"/"
pattern={
[1]=(Cs(P(".")+slash^1)+Cs(R("az","AZ")*P(":")*slash^0)+Cc("./"))*V(2)*V(3),
[2]=Cs(((1-S("*?/\\"))^0*slash)^0),
[3]=Cs(P(1)^0)
}
else
pattern={
[1]=(C(P(".")+P("/")^1)+Cc("./"))*V(2)*V(3),
[2]=C(((1-S("*?/"))^0*P("/"))^0),
[3]=C(P(1)^0)
}
end
local filter=Cs ((
P("**")/".*"+P("*")/"[^/]*"+P("?")/"[^/]"+P(".")/"%%."+P("+")/"%%+"+P("-")/"%%-"+P(1)
)^0 )
local function glob(str,t)
if type(t)=="function" then
if type(str)=="table" then
for s=1,#str do
glob(str[s],t)
end
elseif isfile(str) then
t(str)
else
local root,path,base=lpegmatch(pattern,str)
if root and path and base then
local recurse=find(base,"**",1,true)
local start=root..path
local result=lpegmatch(filter,start..base)
globpattern(start,result,recurse,t)
end
end
else
if type(str)=="table" then
local t=t or {}
for s=1,#str do
glob(str[s],t)
end
return t
elseif isfile(str) then
if t then
t[#t+1]=str
return t
else
return { str }
end
else
local root,path,base=lpegmatch(pattern,str)
if root and path and base then
local recurse=find(base,"**",1,true)
local start=root..path
local result=lpegmatch(filter,start..base)
return globpattern(start,result,recurse,t)
else
return {}
end
end
end
end
dir.glob=glob
local function globfiles(path,recurse,func,files)
if type(func)=="string" then
local s=func
func=function(name) return find(name,s) end
end
files=files or {}
local noffiles=#files
for name,mode in walkdir(path) do
if find(name,"^%.") then
else
if mode==nil then
mode=attributes(name,'mode')
end
if mode=="directory" then
if recurse then
globfiles(path.."/"..name,recurse,func,files)
end
elseif mode=="file" then
if not func or func(name) then
noffiles=noffiles+1
files[noffiles]=path.."/"..name
end
end
end
end
return files
end
dir.globfiles=globfiles
local function globdirs(path,recurse,func,files)
if type(func)=="string" then
local s=func
func=function(name) return find(name,s) end
end
files=files or {}
local noffiles=#files
for name,mode in walkdir(path) do
if find(name,"^%.") then
else
if mode==nil then
mode=attributes(name,'mode')
end
if mode=="directory" then
if not func or func(name) then
noffiles=noffiles+1
files[noffiles]=path.."/"..name
if recurse then
globdirs(path.."/"..name,recurse,func,files)
end
end
end
end
end
return files
end
dir.globdirs=globdirs
function dir.ls(pattern)
return concat(glob(pattern),"\n")
end
local make_indeed=true
if onwindows then
function dir.mkdirs(...)
local n=select("#",...)
local str
if n==1 then
str=select(1,...)
if isdir(str) then
return str,true
end
else
str=""
for i=1,n do
local s=select(i,...)
if s=="" then
elseif str=="" then
str=s
else
str=str.."/"..s
end
end
end
local pth=""
local drive=false
local first,middle,last=match(str,"^(//)(//*)(.*)$")
if first then
else
first,last=match(str,"^(//)/*(.-)$")
if first then
middle,last=match(str,"([^/]+)/+(.-)$")
if middle then
pth="//"..middle
else
pth="//"..last
last=""
end
else
first,middle,last=match(str,"^([a-zA-Z]:)(/*)(.-)$")
if first then
pth,drive=first..middle,true
else
middle,last=match(str,"^(/*)(.-)$")
if not middle then
last=str
end
end
end
end
for s in gmatch(last,"[^/]+") do
if pth=="" then
pth=s
elseif drive then
pth,drive=pth..s,false
else
pth=pth.."/"..s
end
if make_indeed and not isdir(pth) then
mkdir(pth)
end
end
return pth,(isdir(pth)==true)
end
else
function dir.mkdirs(...)
local n=select("#",...)
local str,pth
if n==1 then
str=select(1,...)
if isdir(str) then
return str,true
end
else
str=""
for i=1,n do
local s=select(i,...)
if s and s~="" then
if str~="" then
str=str.."/"..s
else
str=s
end
end
end
end
str=gsub(str,"/+","/")
if find(str,"^/") then
pth="/"
for s in gmatch(str,"[^/]+") do
local first=(pth=="/")
if first then
pth=pth..s
else
pth=pth.."/"..s
end
if make_indeed and not first and not isdir(pth) then
mkdir(pth)
end
end
else
pth="."
for s in gmatch(str,"[^/]+") do
pth=pth.."/"..s
if make_indeed and not isdir(pth) then
mkdir(pth)
end
end
end
return pth,(isdir(pth)==true)
end
end
dir.makedirs=dir.mkdirs
do
local chdir=sandbox and sandbox.original(chdir) or chdir
if onwindows then
local xcurrentdir=dir.current
function dir.expandname(str)
local first,nothing,last=match(str,"^(//)(//*)(.*)$")
if first then
first=xcurrentdir().."/"
end
if not first then
first,last=match(str,"^(//)/*(.*)$")
end
if not first then
first,last=match(str,"^([a-zA-Z]:)(.*)$")
if first and not find(last,"^/") then
local d=currentdir()
if chdir(first) then
first=xcurrentdir()
end
chdir(d)
end
end
if not first then
first,last=xcurrentdir(),str
end
last=gsub(last,"//","/")
last=gsub(last,"/%./","/")
last=gsub(last,"^/*","")
first=gsub(first,"/*$","")
if last=="" or last=="." then
return first
else
return first.."/"..last
end
end
else
function dir.expandname(str)
if not find(str,"^/") then
str=currentdir().."/"..str
end
str=gsub(str,"//","/")
str=gsub(str,"/%./","/")
str=gsub(str,"(.)/%.$","%1")
return str
end
end
end
file.expandname=dir.expandname
local stack={}
function dir.push(newdir)
local curdir=currentdir()
insert(stack,curdir)
if newdir and newdir~="" and chdir(newdir) then
return newdir
else
return curdir
end
end
function dir.pop()
local d=remove(stack)
if d then
chdir(d)
end
return d
end
local function found(...)
for i=1,select("#",...) do
local path=select(i,...)
local kind=type(path)
if kind=="string" then
if isdir(path) then
return path
end
elseif kind=="table" then
local path=found(unpack(path))
if path then
return path
end
end
end
end
dir.found=found
end -- closure
do -- begin closure to overcome local limits and interference
if not modules then modules={} end modules ['l-unicode']={
version=1.001,
optimize=true,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
utf=utf or {}
-- unicode=nil
if not string.utfcharacters then
local gmatch=string.gmatch
function string.characters(str)
return gmatch(str,".[\128-\191]*")
end
end
utf.characters=string.utfcharacters
local type=type
local char,byte,format,sub,gmatch=string.char,string.byte,string.format,string.sub,string.gmatch
local concat=table.concat
local P,C,R,Cs,Ct,Cmt,Cc,Carg,Cp=lpeg.P,lpeg.C,lpeg.R,lpeg.Cs,lpeg.Ct,lpeg.Cmt,lpeg.Cc,lpeg.Carg,lpeg.Cp
local lpegmatch=lpeg.match
local patterns=lpeg.patterns
local tabletopattern=lpeg.utfchartabletopattern
local bytepairs=string.bytepairs
local finder=lpeg.finder
local replacer=lpeg.replacer
local p_utftype=patterns.utftype
local p_utfstricttype=patterns.utfstricttype
local p_utfoffset=patterns.utfoffset
local p_utf8character=patterns.utf8character
local p_utf8char=patterns.utf8char
local p_utf8byte=patterns.utf8byte
local p_utfbom=patterns.utfbom
local p_newline=patterns.newline
local p_whitespace=patterns.whitespace
if not utf.char then
utf.char=string.utfcharacter or (utf8 and utf8.char)
if not utf.char then
local char=string.char
if bit32 then
local rshift=bit32.rshift
function utf.char(n)
if n<0x80 then
return char(n)
elseif n<0x800 then
return char(
0xC0+rshift(n,6),
0x80+(n%0x40)
)
elseif n<0x10000 then
return char(
0xE0+rshift(n,12),
0x80+(rshift(n,6)%0x40),
0x80+(n%0x40)
)
elseif n<0x200000 then
return char(
0xF0+rshift(n,18),
0x80+(rshift(n,12)%0x40),
0x80+(rshift(n,6)%0x40),
0x80+(n%0x40)
)
else
return ""
end
end
else
local floor=math.floor
function utf.char(n)
if n<0x80 then
return char(n)
elseif n<0x800 then
return char(
0xC0+floor(n/0x40),
0x80+(n%0x40)
)
elseif n<0x10000 then
return char(
0xE0+floor(n/0x1000),
0x80+(floor(n/0x40)%0x40),
0x80+(n%0x40)
)
elseif n<0x200000 then
return char(
0xF0+floor(n/0x40000),
0x80+(floor(n/0x1000)%0x40),
0x80+(floor(n/0x40)%0x40),
0x80+(n%0x40)
)
else
return ""
end
end
end
end
end
if not utf.byte then
utf.byte=string.utfvalue or (utf8 and utf8.codepoint)
if not utf.byte then
function utf.byte(c)
return lpegmatch(p_utf8byte,c)
end
end
end
local utfchar,utfbyte=utf.char,utf.byte
function utf.filetype(data)
return data and lpegmatch(p_utftype,data) or "unknown"
end
local toentities=Cs (
(
patterns.utf8one+(
patterns.utf8two+patterns.utf8three+patterns.utf8four
)/function(s) local b=utfbyte(s) if b<127 then return s else return format("&#%X;",b) end end
)^0
)
patterns.toentities=toentities
function utf.toentities(str)
return lpegmatch(toentities,str)
end
local one=P(1)
local two=C(1)*C(1)
local four=C(R(utfchar(0xD8),utfchar(0xFF)))*C(1)*C(1)*C(1)
local pattern=P("\254\255")*Cs((
four/function(a,b,c,d)
local ab=0xFF*byte(a)+byte(b)
local cd=0xFF*byte(c)+byte(d)
return utfchar((ab-0xD800)*0x400+(cd-0xDC00)+0x10000)
end+two/function(a,b)
return utfchar(byte(a)*256+byte(b))
end+one
)^1 )+P("\255\254")*Cs((
four/function(b,a,d,c)
local ab=0xFF*byte(a)+byte(b)
local cd=0xFF*byte(c)+byte(d)
return utfchar((ab-0xD800)*0x400+(cd-0xDC00)+0x10000)
end+two/function(b,a)
return utfchar(byte(a)*256+byte(b))
end+one
)^1 )
function string.toutf(s)
return lpegmatch(pattern,s) or s
end
local validatedutf=Cs (
(
patterns.utf8one+patterns.utf8two+patterns.utf8three+patterns.utf8four+P(1)/"�"
)^0
)
patterns.validatedutf=validatedutf
function utf.is_valid(str)
return type(str)=="string" and lpegmatch(validatedutf,str) or false
end
if not utf.len then
utf.len=string.utflength or (utf8 and utf8.len)
if not utf.len then
local n,f=0,1
local utfcharcounter=patterns.utfbom^-1*Cmt (
Cc(1)*patterns.utf8one^1+Cc(2)*patterns.utf8two^1+Cc(3)*patterns.utf8three^1+Cc(4)*patterns.utf8four^1,
function(_,t,d)
n=n+(t-f)/d
f=t
return true
end
)^0
function utf.len(str)
n,f=0,1
lpegmatch(utfcharcounter,str or "")
return n
end
end
end
utf.length=utf.len
if not utf.sub then
local utflength=utf.length
local b,e,n,first,last=0,0,0,0,0
local function slide_zero(s,p)
n=n+1
if n>=last then
e=p-1
else
return p
end
end
local function slide_one(s,p)
n=n+1
if n==first then
b=p
end
if n>=last then
e=p-1
else
return p
end
end
local function slide_two(s,p)
n=n+1
if n==first then
b=p
else
return true
end
end
local pattern_zero=Cmt(p_utf8character,slide_zero)^0
local pattern_one=Cmt(p_utf8character,slide_one )^0
local pattern_two=Cmt(p_utf8character,slide_two )^0
local pattern_first=C(p_utf8character)
function utf.sub(str,start,stop)
if not start then
return str
end
if start==0 then
start=1
end
if not stop then
if start<0 then
local l=utflength(str)
start=l+start
else
start=start-1
end
b,n,first=0,0,start
lpegmatch(pattern_two,str)
if n>=first then
return sub(str,b)
else
return ""
end
end
if start<0 or stop<0 then
local l=utf.length(str)
if start<0 then
start=l+start
if start<=0 then
start=1
else
start=start+1
end
end
if stop<0 then
stop=l+stop
if stop==0 then
stop=1
else
stop=stop+1
end
end
end
if start==1 and stop==1 then
return lpegmatch(pattern_first,str) or ""
elseif start>stop then
return ""
elseif start>1 then
b,e,n,first,last=0,0,0,start-1,stop
lpegmatch(pattern_one,str)
if n>=first and e==0 then
e=#str
end
return sub(str,b,e)
else
b,e,n,last=1,0,0,stop
lpegmatch(pattern_zero,str)
if e==0 then
e=#str
end
return sub(str,b,e)
end
end
end
function utf.remapper(mapping,option,action)
local variant=type(mapping)
if variant=="table" then
action=action or mapping
if option=="dynamic" then
local pattern=false
table.setmetatablenewindex(mapping,function(t,k,v) rawset(t,k,v) pattern=false end)
return function(str)
if not str or str=="" then
return ""
else
if not pattern then
pattern=Cs((tabletopattern(mapping)/action+p_utf8character)^0)
end
return lpegmatch(pattern,str)
end
end
elseif option=="pattern" then
return Cs((tabletopattern(mapping)/action+p_utf8character)^0)
else
local pattern=Cs((tabletopattern(mapping)/action+p_utf8character)^0)
return function(str)
if not str or str=="" then
return ""
else
return lpegmatch(pattern,str)
end
end,pattern
end
elseif variant=="function" then
if option=="pattern" then
return Cs((p_utf8character/mapping+p_utf8character)^0)
else
local pattern=Cs((p_utf8character/mapping+p_utf8character)^0)
return function(str)
if not str or str=="" then
return ""
else
return lpegmatch(pattern,str)
end
end,pattern
end
else
return function(str)
return str or ""
end
end
end
function utf.replacer(t)
local r=replacer(t,false,false,true)
return function(str)
return lpegmatch(r,str)
end
end
function utf.subtituter(t)
local f=finder (t)
local r=replacer(t,false,false,true)
return function(str)
local i=lpegmatch(f,str)
if not i then
return str
elseif i>#str then
return str
else
return lpegmatch(r,str)
end
end
end
local utflinesplitter=p_utfbom^-1*lpeg.tsplitat(p_newline)
local utfcharsplitter_ows=p_utfbom^-1*Ct(C(p_utf8character)^0)
local utfcharsplitter_iws=p_utfbom^-1*Ct((p_whitespace^1+C(p_utf8character))^0)
local utfcharsplitter_raw=Ct(C(p_utf8character)^0)
patterns.utflinesplitter=utflinesplitter
function utf.splitlines(str)
return lpegmatch(utflinesplitter,str or "")
end
function utf.split(str,ignorewhitespace)
if ignorewhitespace then
return lpegmatch(utfcharsplitter_iws,str or "")
else
return lpegmatch(utfcharsplitter_ows,str or "")
end
end
function utf.totable(str)
return lpegmatch(utfcharsplitter_raw,str)
end
function utf.magic(f)
local str=f:read(4) or ""
local off=lpegmatch(p_utfoffset,str)
if off<4 then
f:seek('set',off)
end
return lpegmatch(p_utftype,str)
end
local utf16_to_utf8_be,utf16_to_utf8_le
local utf32_to_utf8_be,utf32_to_utf8_le
local utf_16_be_getbom=patterns.utfbom_16_be^-1
local utf_16_le_getbom=patterns.utfbom_16_le^-1
local utf_32_be_getbom=patterns.utfbom_32_be^-1
local utf_32_le_getbom=patterns.utfbom_32_le^-1
local utf_16_be_linesplitter=utf_16_be_getbom*lpeg.tsplitat(patterns.utf_16_be_nl)
local utf_16_le_linesplitter=utf_16_le_getbom*lpeg.tsplitat(patterns.utf_16_le_nl)
local utf_32_be_linesplitter=utf_32_be_getbom*lpeg.tsplitat(patterns.utf_32_be_nl)
local utf_32_le_linesplitter=utf_32_le_getbom*lpeg.tsplitat(patterns.utf_32_le_nl)
local more=0
local p_utf16_to_utf8_be=C(1)*C(1)/function(left,right)
local now=256*byte(left)+byte(right)
if more>0 then
now=(more-0xD800)*0x400+(now-0xDC00)+0x10000
more=0
return utfchar(now)
elseif now>=0xD800 and now<=0xDBFF then
more=now
return ""
else
return utfchar(now)
end
end
local p_utf16_to_utf8_le=C(1)*C(1)/function(right,left)
local now=256*byte(left)+byte(right)
if more>0 then
now=(more-0xD800)*0x400+(now-0xDC00)+0x10000
more=0
return utfchar(now)
elseif now>=0xD800 and now<=0xDBFF then
more=now
return ""
else
return utfchar(now)
end
end
local p_utf32_to_utf8_be=C(1)*C(1)*C(1)*C(1)/function(a,b,c,d)
return utfchar(256*256*256*byte(a)+256*256*byte(b)+256*byte(c)+byte(d))
end
local p_utf32_to_utf8_le=C(1)*C(1)*C(1)*C(1)/function(a,b,c,d)
return utfchar(256*256*256*byte(d)+256*256*byte(c)+256*byte(b)+byte(a))
end
p_utf16_to_utf8_be=P(true)/function() more=0 end*utf_16_be_getbom*Cs(p_utf16_to_utf8_be^0)
p_utf16_to_utf8_le=P(true)/function() more=0 end*utf_16_le_getbom*Cs(p_utf16_to_utf8_le^0)
p_utf32_to_utf8_be=P(true)/function() more=0 end*utf_32_be_getbom*Cs(p_utf32_to_utf8_be^0)
p_utf32_to_utf8_le=P(true)/function() more=0 end*utf_32_le_getbom*Cs(p_utf32_to_utf8_le^0)
patterns.utf16_to_utf8_be=p_utf16_to_utf8_be
patterns.utf16_to_utf8_le=p_utf16_to_utf8_le
patterns.utf32_to_utf8_be=p_utf32_to_utf8_be
patterns.utf32_to_utf8_le=p_utf32_to_utf8_le
utf16_to_utf8_be=function(s)
if s and s~="" then
return lpegmatch(p_utf16_to_utf8_be,s)
else
return s
end
end
local utf16_to_utf8_be_t=function(t)
if not t then
return nil
elseif type(t)=="string" then
t=lpegmatch(utf_16_be_linesplitter,t)
end
for i=1,#t do
local s=t[i]
if s~="" then
t[i]=lpegmatch(p_utf16_to_utf8_be,s)
end
end
return t
end
utf16_to_utf8_le=function(s)
if s and s~="" then
return lpegmatch(p_utf16_to_utf8_le,s)
else
return s
end
end
local utf16_to_utf8_le_t=function(t)
if not t then
return nil
elseif type(t)=="string" then
t=lpegmatch(utf_16_le_linesplitter,t)
end
for i=1,#t do
local s=t[i]
if s~="" then
t[i]=lpegmatch(p_utf16_to_utf8_le,s)
end
end
return t
end
utf32_to_utf8_be=function(s)
if s and s~="" then
return lpegmatch(p_utf32_to_utf8_be,s)
else
return s
end
end
local utf32_to_utf8_be_t=function(t)
if not t then
return nil
elseif type(t)=="string" then
t=lpegmatch(utf_32_be_linesplitter,t)
end
for i=1,#t do
local s=t[i]
if s~="" then
t[i]=lpegmatch(p_utf32_to_utf8_be,s)
end
end
return t
end
utf32_to_utf8_le=function(s)
if s and s~="" then
return lpegmatch(p_utf32_to_utf8_le,s)
else
return s
end
end
local utf32_to_utf8_le_t=function(t)
if not t then
return nil
elseif type(t)=="string" then
t=lpegmatch(utf_32_le_linesplitter,t)
end
for i=1,#t do
local s=t[i]
if s~="" then
t[i]=lpegmatch(p_utf32_to_utf8_le,s)
end
end
return t
end
utf.utf16_to_utf8_le_t=utf16_to_utf8_le_t
utf.utf16_to_utf8_be_t=utf16_to_utf8_be_t
utf.utf32_to_utf8_le_t=utf32_to_utf8_le_t
utf.utf32_to_utf8_be_t=utf32_to_utf8_be_t
utf.utf16_to_utf8_le=utf16_to_utf8_le
utf.utf16_to_utf8_be=utf16_to_utf8_be
utf.utf32_to_utf8_le=utf32_to_utf8_le
utf.utf32_to_utf8_be=utf32_to_utf8_be
function utf.utf8_to_utf8_t(t)
return type(t)=="string" and lpegmatch(utflinesplitter,t) or t
end
function utf.utf16_to_utf8_t(t,endian)
return endian and utf16_to_utf8_be_t(t) or utf16_to_utf8_le_t(t) or t
end
function utf.utf32_to_utf8_t(t,endian)
return endian and utf32_to_utf8_be_t(t) or utf32_to_utf8_le_t(t) or t
end
if bit32 then
local rshift=bit32.rshift
local function little(b)
if b<0x10000 then
return char(b%256,rshift(b,8))
else
b=b-0x10000
local b1=rshift(b,10)+0xD800
local b2=b%1024+0xDC00
return char(b1%256,rshift(b1,8),b2%256,rshift(b2,8))
end
end
local function big(b)
if b<0x10000 then
return char(rshift(b,8),b%256)
else
b=b-0x10000
local b1=rshift(b,10)+0xD800
local b2=b%1024+0xDC00
return char(rshift(b1,8),b1%256,rshift(b2,8),b2%256)
end
end
local l_remap=Cs((p_utf8byte/little+P(1)/"")^0)
local b_remap=Cs((p_utf8byte/big+P(1)/"")^0)
local function utf8_to_utf16_be(str,nobom)
if nobom then
return lpegmatch(b_remap,str)
else
return char(254,255)..lpegmatch(b_remap,str)
end
end
local function utf8_to_utf16_le(str,nobom)
if nobom then
return lpegmatch(l_remap,str)
else
return char(255,254)..lpegmatch(l_remap,str)
end
end
utf.utf8_to_utf16_be=utf8_to_utf16_be
utf.utf8_to_utf16_le=utf8_to_utf16_le
function utf.utf8_to_utf16(str,littleendian,nobom)
if littleendian then
return utf8_to_utf16_le(str,nobom)
else
return utf8_to_utf16_be(str,nobom)
end
end
end
local pattern=Cs (
(p_utf8byte/function(unicode ) return format("0x%04X",unicode) end)*(p_utf8byte*Carg(1)/function(unicode,separator) return format("%s0x%04X",separator,unicode) end)^0
)
function utf.tocodes(str,separator)
return lpegmatch(pattern,str,1,separator or " ")
end
function utf.ustring(s)
return format("U+%05X",type(s)=="number" and s or utfbyte(s))
end
function utf.xstring(s)
return format("0x%05X",type(s)=="number" and s or utfbyte(s))
end
function utf.toeight(str)
if not str or str=="" then
return nil
end
local utftype=lpegmatch(p_utfstricttype,str)
if utftype=="utf-8" then
return sub(str,4)
elseif utftype=="utf-16-be" then
return utf16_to_utf8_be(str)
elseif utftype=="utf-16-le" then
return utf16_to_utf8_le(str)
else
return str
end
end
do
local p_nany=p_utf8character/""
local cache={}
function utf.count(str,what)
if type(what)=="string" then
local p=cache[what]
if not p then
p=Cs((P(what)/" "+p_nany)^0)
cache[p]=p
end
return #lpegmatch(p,str)
else
return #lpegmatch(Cs((P(what)/" "+p_nany)^0),str)
end
end
end
if not string.utfvalues then
local find=string.find
local dummy=function()
end
function string.utfvalues(str)
local n=#str
if n==0 then
return dummy
elseif n==1 then
return function() return utfbyte(str) end
else
local p=1
return function()
local b,e=find(str,".[\128-\191]*",p)
if b then
p=e+1
return utfbyte(sub(str,b,e))
end
end
end
end
end
utf.values=string.utfvalues
function utf.chrlen(u)
return
(u<0x80 and 1) or
(u<0xE0 and 2) or
(u<0xF0 and 3) or
(u<0xF8 and 4) or
(u<0xFC and 5) or
(u<0xFE and 6) or 0
end
if bit32 then
local extract=bit32.extract
local char=string.char
function utf.toutf32string(n)
if n<=0xFF then
return
char(n).."\000\000\000"
elseif n<=0xFFFF then
return
char(extract(n,0,8))..char(extract(n,8,8)).."\000\000"
elseif n<=0xFFFFFF then
return
char(extract(n,0,8))..char(extract(n,8,8))..char(extract(n,16,8)).."\000"
else
return
char(extract(n,0,8))..char(extract(n,8,8))..char(extract(n,16,8))..char(extract(n,24,8))
end
end
end
local len=utf.len
local rep=rep
function string.utfpadd(s,n)
if n and n~=0 then
local l=len(s)
if n>0 then
local d=n-l
if d>0 then
return rep(c or " ",d)..s
end
else
local d=- n-l
if d>0 then
return s..rep(c or " ",d)
end
end
end
return s
end
do
local utfcharacters=utf.characters or string.utfcharacters
local utfchar=utf.char or string.utfcharacter
lpeg.UP=P
if utfcharacters then
function lpeg.US(str)
local p=P(false)
for uc in utfcharacters(str) do
p=p+P(uc)
end
return p
end
else
function lpeg.US(str)
local p=P(false)
local f=function(uc)
p=p+P(uc)
end
lpegmatch((p_utf8char/f)^0,str)
return p
end
end
local range=p_utf8byte*p_utf8byte+Cc(false)
function lpeg.UR(str,more)
local first,last
if type(str)=="number" then
first=str
last=more or first
else
first,last=lpegmatch(range,str)
if not last then
return P(str)
end
end
if first==last then
return P(str)
end
if not utfchar then
utfchar=utf.char
end
if utfchar and (last-first<8) then
local p=P(false)
for i=first,last do
p=p+P(utfchar(i))
end
return p
else
local f=function(b)
return b>=first and b<=last
end
return p_utf8byte/f
end
end
end
end -- closure
do -- begin closure to overcome local limits and interference
if not modules then modules={} end modules ['l-url']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
local char,format,byte=string.char,string.format,string.byte
local concat=table.concat
local tonumber,type,next=tonumber,type,next
local P,C,R,S,Cs,Cc,Ct,Cf,Cg,V=lpeg.P,lpeg.C,lpeg.R,lpeg.S,lpeg.Cs,lpeg.Cc,lpeg.Ct,lpeg.Cf,lpeg.Cg,lpeg.V
local lpegmatch,lpegpatterns,replacer=lpeg.match,lpeg.patterns,lpeg.replacer
local sortedhash=table.sortedhash
url=url or {}
local url=url
local unescapes={}
local escapes={}
setmetatable(unescapes,{ __index=function(t,k)
local v=char(tonumber(k,16))
t[k]=v
return v
end })
setmetatable(escapes,{ __index=function(t,k)
local v=format("%%%02X",byte(k))
t[k]=v
return v
end })
local colon=P(":")
local qmark=P("?")
local hash=P("#")
local slash=P("/")
local atsign=P("@")
local percent=P("%")
local endofstring=P(-1)
local hexdigit=R("09","AF","af")
local plus=P("+")
local nothing=Cc("")
local okay=R("09","AZ","az")+S("-_.,:=+*~!'()@&$")
local escapedchar=(percent*C(hexdigit*hexdigit))/unescapes
local unescapedchar=P(1)/escapes
local escaped=(plus/" ")+escapedchar
local noslash=P("/")/""
local plustospace=P("+")/" "
local decoder=Cs((
plustospace+escapedchar+P("\r\n")/"\n"+P(1)
)^0 )
local encoder=Cs((
R("09","AZ","az")^1+S("-./_")^1+P(" ")/"+"+P("\n")/"\r\n"+unescapedchar
)^0 )
lpegpatterns.urldecoder=decoder
lpegpatterns.urlencoder=encoder
function url.decode (str) return str and lpegmatch(decoder,str) or str end
function url.encode (str) return str and lpegmatch(encoder,str) or str end
function url.unescape(str) return str and lpegmatch(unescaper,str) or str end
local schemestr=Cs((escaped+(1-colon-slash-qmark-hash))^2)
local authoritystr=Cs((escaped+(1- slash-qmark-hash))^0)
local pathstr=Cs((escaped+(1- qmark-hash))^0)
local querystr=Cs(((1- hash))^0)
local fragmentstr=Cs((escaped+(1- endofstring))^0)
local scheme=schemestr*colon+nothing
local authority=slash*slash*authoritystr+nothing
local path=slash*pathstr+nothing
local query=qmark*querystr+nothing
local fragment=hash*fragmentstr+nothing
local validurl=scheme*authority*path*query*fragment
local parser=Ct(validurl)
lpegpatterns.url=validurl
lpegpatterns.urlsplitter=parser
local escaper=Cs((R("09","AZ","az")^1+P(" ")/"%%20"+S("-./_:")^1+P(1)/escapes)^0)
local unescaper=Cs((escapedchar+1)^0)
local getcleaner=Cs((P("+++")/"%%2B"+P("+")/"%%20"+P(1))^1)
lpegpatterns.urlunescaped=escapedchar
lpegpatterns.urlescaper=escaper
lpegpatterns.urlunescaper=unescaper
lpegpatterns.urlgetcleaner=getcleaner
function url.unescapeget(str)
return lpegmatch(getcleaner,str)
end
local function split(str)
return (type(str)=="string" and lpegmatch(parser,str)) or str
end
local isscheme=schemestr*colon*slash*slash
local function hasscheme(str)
if str then
local scheme=lpegmatch(isscheme,str)
return scheme~="" and scheme or false
else
return false
end
end
local rootletter=R("az","AZ")+S("_-+")
local separator=P("://")
local qualified=P(".")^0*P("/")+rootletter*P(":")+rootletter^1*separator+rootletter^1*P("/")
local rootbased=P("/")+rootletter*P(":")
local barswapper=replacer("|",":")
local backslashswapper=replacer("\\","/")
local equal=P("=")
local amp=P("&")
local key=Cs(((plustospace+escapedchar+1)-equal )^0)
local value=Cs(((plustospace+escapedchar+1)-amp-endofstring)^0)
local splitquery=Cf (Ct("")*P { "sequence",
sequence=V("pair")*(amp*V("pair"))^0,
pair=Cg(key*equal*value),
},rawset)
local userpart=(1-atsign-colon)^1
local serverpart=(1-colon)^1
local splitauthority=((Cs(userpart)*colon*Cs(userpart)+Cs(userpart)*Cc(nil))*atsign+Cc(nil)*Cc(nil))*Cs(serverpart)*(colon*(serverpart/tonumber)+Cc(nil))
local function hashed(str)
if not str or str=="" then
return {
scheme="invalid",
original=str,
}
end
local detailed=split(str)
local rawscheme=""
local rawquery=""
local somescheme=false
local somequery=false
if detailed then
rawscheme=detailed[1]
rawquery=detailed[4]
somescheme=rawscheme~=""
somequery=rawquery~=""
end
if not somescheme and not somequery then
return {
scheme="file",
authority="",
path=str,
query="",
fragment="",
original=str,
noscheme=true,
filename=str,
}
end
local authority=detailed[2]
local path=detailed[3]
local filename
local username
local password
local host
local port
if authority~="" then
username,password,host,port=lpegmatch(splitauthority,authority)
end
if authority=="" then
filename=path
elseif path=="" then
filename=""
else
filename=authority.."/"..path
end
return {
scheme=rawscheme,
authority=authority,
path=path,
query=lpegmatch(unescaper,rawquery),
queries=lpegmatch(splitquery,rawquery),
fragment=detailed[5],
original=str,
noscheme=false,
filename=filename,
host=host,
port=port,
}
end
url.split=split
url.hasscheme=hasscheme
url.hashed=hashed
function url.addscheme(str,scheme)
if hasscheme(str) then
return str
elseif not scheme then
return "file:///"..str
else
return scheme..":///"..str
end
end
function url.construct(hash)
local result,r={},0
local scheme=hash.scheme
local authority=hash.authority
local path=hash.path
local queries=hash.queries
local fragment=hash.fragment
if scheme and scheme~="" then
r=r+1;result[r]=lpegmatch(escaper,scheme)
r=r+1;result[r]="://"
end
if authority and authority~="" then
r=r+1;result[r]=lpegmatch(escaper,authority)
end
if path and path~="" then
r=r+1;result[r]="/"
r=r+1;result[r]=lpegmatch(escaper,path)
end
if queries then
local done=false
for k,v in sortedhash(queries) do
r=r+1;result[r]=done and "&" or "?"
r=r+1;result[r]=lpegmatch(escaper,k)
r=r+1;result[r]="="
r=r+1;result[r]=lpegmatch(escaper,v)
done=true
end
end
if fragment and fragment~="" then
r=r+1;result[r]="#"
r=r+1;result[r]=lpegmatch(escaper,fragment)
end
return concat(result)
end
local pattern=Cs(slash^-1/""*R("az","AZ")*((S(":|")/":")+P(":"))*slash*P(1)^0)
function url.filename(filename)
local spec=hashed(filename)
local path=spec.path
return (spec.scheme=="file" and path and lpegmatch(pattern,path)) or filename
end
local function escapestring(str)
return lpegmatch(escaper,str)
end
url.escape=escapestring
function url.query(str)
if type(str)=="string" then
return lpegmatch(splitquery,str) or ""
else
return str
end
end
function url.toquery(data)
local td=type(data)
if td=="string" then
return #str and escape(data) or nil
elseif td=="table" then
if next(data) then
local t={}
for k,v in next,data do
t[#t+1]=format("%s=%s",k,escapestring(v))
end
return concat(t,"&")
end
else
end
end
local pattern=Cs(noslash^0*(1-noslash*P(-1))^0)
function url.barepath(path)
if not path or path=="" then
return ""
else
return lpegmatch(pattern,path)
end
end
end -- closure
do -- begin closure to overcome local limits and interference
if not modules then modules={} end modules ['l-set']={
version=1.001,
comment="companion to luat-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
set=set or {}
local nums={}
local tabs={}
local concat=table.concat
local next,type=next,type
set.create=table.tohash
function set.tonumber(t)
if next(t) then
local s=""
for k,v in next,t do
if v then
s=s.." "..k
end
end
local n=nums[s]
if not n then
n=#tabs+1
tabs[n]=t
nums[s]=n
end
return n
else
return 0
end
end
function set.totable(n)
if n==0 then
return {}
else
return tabs[n] or {}
end
end
function set.tolist(n)
if n==0 or not tabs[n] then
return ""
else
local t,n={},0
for k,v in next,tabs[n] do
if v then
n=n+1
t[n]=k
end
end
return concat(t," ")
end
end
function set.contains(n,s)
if type(n)=="table" then
return n[s]
elseif n==0 then
return false
else
local t=tabs[n]
return t and t[s]
end
end
end -- closure