summaryrefslogtreecommitdiff
path: root/scripts/context/lua/mtxrun.lua
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/context/lua/mtxrun.lua')
-rw-r--r--scripts/context/lua/mtxrun.lua7292
1 files changed, 4007 insertions, 3285 deletions
diff --git a/scripts/context/lua/mtxrun.lua b/scripts/context/lua/mtxrun.lua
index 5a4919bab..46db66493 100644
--- a/scripts/context/lua/mtxrun.lua
+++ b/scripts/context/lua/mtxrun.lua
@@ -38,8 +38,6 @@ if not modules then modules = { } end modules ['mtxrun'] = {
-- remember for subruns: _CTX_K_S_#{original}_
-- remember for subruns: TEXMFSTART.#{original} [tex.rb texmfstart.rb]
-texlua = true
-
-- begin library merge
@@ -97,13 +95,6 @@ function string:unquote()
return (gsub(self,"^([\"\'])(.*)%1$","%2"))
end
---~ function string:unquote()
---~ if find(self,"^[\'\"]") then
---~ return sub(self,2,-2)
---~ else
---~ return self
---~ end
---~ end
function string:quote() -- we could use format("%q")
return format("%q",self)
@@ -126,11 +117,6 @@ function string:limit(n,sentinel)
end
end
---~ function string:strip() -- the .- is quite efficient
---~ -- return match(self,"^%s*(.-)%s*$") or ""
---~ -- return match(self,'^%s*(.*%S)') or '' -- posted on lua list
---~ return find(s,'^%s*$') and '' or match(s,'^%s*(.*%S)')
---~ end
do -- roberto's variant:
local space = lpeg.S(" \t\v\n")
@@ -217,13 +203,6 @@ function is_number(str) -- tonumber
return find(str,"^[%-%+]?[%d]-%.?[%d+]$") == 1
end
---~ print(is_number("1"))
---~ print(is_number("1.1"))
---~ print(is_number(".1"))
---~ print(is_number("-0.1"))
---~ print(is_number("+0.1"))
---~ print(is_number("-.1"))
---~ print(is_number("+.1"))
function string:split_settings() -- no {} handling, see l-aux for lpeg variant
if find(self,"=") then
@@ -278,18 +257,6 @@ function string:totable()
return lpegmatch(pattern,self)
end
---~ local t = {
---~ "1234567123456712345671234567",
---~ "a\tb\tc",
---~ "aa\tbb\tcc",
---~ "aaa\tbbb\tccc",
---~ "aaaa\tbbbb\tcccc",
---~ "aaaaa\tbbbbb\tccccc",
---~ "aaaaaa\tbbbbbb\tcccccc",
---~ }
---~ for k,v do
---~ print(string.tabtospace(t[k]))
---~ end
function string.tabtospace(str,tab)
-- we don't handle embedded newlines
@@ -390,6 +357,11 @@ patterns.whitespace = patterns.eol + patterns.spacer
patterns.nonwhitespace = 1 - patterns.whitespace
patterns.utf8 = patterns.utf8one + patterns.utf8two + patterns.utf8three + patterns.utf8four
patterns.utfbom = P('\000\000\254\255') + P('\255\254\000\000') + P('\255\254') + P('\254\255') + P('\239\187\191')
+patterns.validutf8 = patterns.utf8^0 * P(-1) * Cc(true) + Cc(false)
+
+patterns.undouble = P('"')/"" * (1-P('"'))^0 * P('"')/""
+patterns.unsingle = P("'")/"" * (1-P("'"))^0 * P("'")/""
+patterns.unspacer = ((patterns.spacer^1)/"")^0
function lpeg.anywhere(pattern) --slightly adapted from website
return P { P(pattern) + 1 * V(1) } -- why so complex?
@@ -412,10 +384,6 @@ end
patterns.textline = content
---~ local p = lpeg.splitat("->",false) print(match(p,"oeps->what->more")) -- oeps what more
---~ local p = lpeg.splitat("->",true) print(match(p,"oeps->what->more")) -- oeps what->more
---~ local p = lpeg.splitat("->",false) print(match(p,"oeps")) -- oeps
---~ local p = lpeg.splitat("->",true) print(match(p,"oeps")) -- oeps
local splitters_s, splitters_m = { }, { }
@@ -484,19 +452,7 @@ function string:checkedsplit(separator)
return match(c,self)
end
---~ function lpeg.append(list,pp)
---~ local p = pp
---~ for l=1,#list do
---~ if p then
---~ p = p + P(list[l])
---~ else
---~ p = P(list[l])
---~ end
---~ end
---~ return p
---~ end
---~ from roberto's site:
local f1 = string.byte
@@ -506,6 +462,53 @@ local function f4(s) local c1, c2, c3, c4 = f1(s,1,4) return ((c1 * 64 + c2) * 6
patterns.utf8byte = patterns.utf8one/f1 + patterns.utf8two/f2 + patterns.utf8three/f3 + patterns.utf8four/f4
+local cache = { }
+
+function lpeg.stripper(str)
+ local s = cache[str]
+ if not s then
+ s = Cs(((S(str)^1)/"" + 1)^0)
+ cache[str] = s
+ end
+ return s
+end
+
+function lpeg.replacer(t)
+ if #t > 0 then
+ local p
+ for i=1,#t do
+ local ti= t[i]
+ local pp = P(ti[1]) / ti[2]
+ p = (p and p + pp ) or pp
+ end
+ return Cs((p + 1)^0)
+ end
+end
+
+
+local splitters_f, splitters_s = { }, { }
+
+function lpeg.firstofsplit(separator) -- always return value
+ local splitter = splitters_f[separator]
+ if not splitter then
+ separator = P(separator)
+ splitter = C((1 - separator)^0)
+ splitters_f[separator] = splitter
+ end
+ return splitter
+end
+
+function lpeg.secondofsplit(separator) -- nil if not split
+ local splitter = splitters_s[separator]
+ if not splitter then
+ separator = P(separator)
+ splitter = (1 - separator)^0 * separator * C(P(1)^0)
+ splitters_s[separator] = splitter
+ end
+ return splitter
+end
+
+
end -- of closure
@@ -783,9 +786,6 @@ function table.one_entry(t) -- obolete, use inline code instead
return n and not next(t,n)
end
---~ function table.starts_at(t) -- obsolete, not nice anyway
---~ return ipairs(t,1)(t,0)
---~ end
function table.tohash(t,value)
local h = { }
@@ -806,12 +806,6 @@ function table.fromhash(t)
return h
end
---~ print(table.serialize(t), "\n")
---~ print(table.serialize(t,"name"), "\n")
---~ print(table.serialize(t,false), "\n")
---~ print(table.serialize(t,true), "\n")
---~ print(table.serialize(t,"name",true), "\n")
---~ print(table.serialize(t,"name",true,true), "\n")
table.serialize_functions = true
table.serialize_compact = true
@@ -871,8 +865,7 @@ local function do_serialize(root,name,depth,level,indexed)
if indexed then
handle(format("%s{",depth))
elseif name then
- --~ handle(format("%s%s={",depth,key(name)))
- if type(name) == "number" then -- or find(k,"^%d+$") then
+ if type(name) == "number" then -- or find(k,"^%d+$") then
if hexify then
handle(format("%s[0x%04X]={",depth,name))
else
@@ -901,10 +894,8 @@ local function do_serialize(root,name,depth,level,indexed)
for i=1,#sk do
local k = sk[i]
local v = root[k]
- --~ if v == root then
- -- circular
- --~ else
- local t = type(v)
+ -- circular
+ local t = type(v)
if compact and first and type(k) == "number" and k >= first and k <= last then
if t == "number" then
if hexify then
@@ -947,12 +938,7 @@ local function do_serialize(root,name,depth,level,indexed)
handle(format("%s __p__=nil,",depth))
end
elseif t == "number" then
- --~ if hexify then
- --~ handle(format("%s %s=0x%04X,",depth,key(k),v))
- --~ else
- --~ handle(format("%s %s=%s,",depth,key(k),v)) -- %.99g
- --~ end
- if type(k) == "number" then -- or find(k,"^%d+$") then
+ if type(k) == "number" then -- or find(k,"^%d+$") then
if hexify then
handle(format("%s [0x%04X]=0x%04X,",depth,k,v))
else
@@ -973,8 +959,7 @@ local function do_serialize(root,name,depth,level,indexed)
end
elseif t == "string" then
if reduce and tonumber(v) then
- --~ handle(format("%s %s=%s,",depth,key(k),v))
- if type(k) == "number" then -- or find(k,"^%d+$") then
+ if type(k) == "number" then -- or find(k,"^%d+$") then
if hexify then
handle(format("%s [0x%04X]=%s,",depth,k,v))
else
@@ -986,8 +971,7 @@ local function do_serialize(root,name,depth,level,indexed)
handle(format("%s [%q]=%s,",depth,k,v))
end
else
- --~ handle(format("%s %s=%q,",depth,key(k),v))
- if type(k) == "number" then -- or find(k,"^%d+$") then
+ if type(k) == "number" then -- or find(k,"^%d+$") then
if hexify then
handle(format("%s [0x%04X]=%q,",depth,k,v))
else
@@ -1001,8 +985,7 @@ local function do_serialize(root,name,depth,level,indexed)
end
elseif t == "table" then
if not next(v) then
- --~ handle(format("%s %s={},",depth,key(k)))
- if type(k) == "number" then -- or find(k,"^%d+$") then
+ if type(k) == "number" then -- or find(k,"^%d+$") then
if hexify then
handle(format("%s [0x%04X]={},",depth,k))
else
@@ -1016,8 +999,7 @@ local function do_serialize(root,name,depth,level,indexed)
elseif inline then
local st = simple_table(v)
if st then
- --~ handle(format("%s %s={ %s },",depth,key(k),concat(st,", ")))
- if type(k) == "number" then -- or find(k,"^%d+$") then
+ if type(k) == "number" then -- or find(k,"^%d+$") then
if hexify then
handle(format("%s [0x%04X]={ %s },",depth,k,concat(st,", ")))
else
@@ -1035,8 +1017,7 @@ local function do_serialize(root,name,depth,level,indexed)
do_serialize(v,k,depth,level+1)
end
elseif t == "boolean" then
- --~ handle(format("%s %s=%s,",depth,key(k),tostring(v)))
- if type(k) == "number" then -- or find(k,"^%d+$") then
+ if type(k) == "number" then -- or find(k,"^%d+$") then
if hexify then
handle(format("%s [0x%04X]=%s,",depth,k,tostring(v)))
else
@@ -1049,8 +1030,7 @@ local function do_serialize(root,name,depth,level,indexed)
end
elseif t == "function" then
if functions then
- --~ handle(format('%s %s=loadstring(%q),',depth,key(k),dump(v)))
- if type(k) == "number" then -- or find(k,"^%d+$") then
+ if type(k) == "number" then -- or find(k,"^%d+$") then
if hexify then
handle(format("%s [0x%04X]=loadstring(%q),",depth,k,dump(v)))
else
@@ -1063,8 +1043,7 @@ local function do_serialize(root,name,depth,level,indexed)
end
end
else
- --~ handle(format("%s %s=%q,",depth,key(k),tostring(v)))
- if type(k) == "number" then -- or find(k,"^%d+$") then
+ if type(k) == "number" then -- or find(k,"^%d+$") then
if hexify then
handle(format("%s [0x%04X]=%q,",depth,k,tostring(v)))
else
@@ -1076,8 +1055,7 @@ local function do_serialize(root,name,depth,level,indexed)
handle(format("%s [%q]=%q,",depth,k,tostring(v)))
end
end
- --~ end
- end
+ end
end
if level > 0 then
handle(format("%s},",depth))
@@ -1118,19 +1096,11 @@ local function serialize(root,name,_handle,_reduce,_noquotes,_hexify)
handle("t={")
end
if root and next(root) then
- do_serialize(root,name,"",0,indexed)
+ do_serialize(root,name,"",0)
end
handle("}")
end
---~ name:
---~
---~ true : return { }
---~ false : { }
---~ nil : t = { }
---~ string : string = { }
---~ 'return' : return { }
---~ number : [number] = { }
function table.serialize(root,name,reduce,noquotes,hexify)
local t = { }
@@ -1353,9 +1323,6 @@ function table.swapped(t)
return s
end
---~ function table.are_equal(a,b)
---~ return table.serialize(a) == table.serialize(b)
---~ end
function table.clone(t,p) -- t is optional or nil or table
if not p then
@@ -1421,6 +1388,17 @@ function table.insert_after_value(t,value,extra)
insert(t,#t+1,extra)
end
+function table.sequenced(t,sep)
+ local s = { }
+ for k, v in next, t do -- indexed?
+ s[#s+1] = k .. "=" .. tostring(v)
+ end
+ return concat(s, sep or " | ")
+end
+
+function table.print(...)
+ print(table.serialize(...))
+end
end -- of closure
@@ -1756,17 +1734,6 @@ function set.contains(n,s)
end
end
---~ local c = set.create{'aap','noot','mies'}
---~ local s = set.tonumber(c)
---~ local t = set.totable(s)
---~ print(t['aap'])
---~ local c = set.create{'zus','wim','jet'}
---~ local s = set.tonumber(c)
---~ local t = set.totable(s)
---~ print(t['aap'])
---~ print(t['jet'])
---~ print(set.contains(t,'jet'))
---~ print(set.contains(t,'aap'))
@@ -1784,29 +1751,97 @@ if not modules then modules = { } end modules ['l-os'] = {
-- maybe build io.flush in os.execute
-local find, format, gsub = string.find, string.format, string.gsub
+local find, format, gsub, upper = string.find, string.format, string.gsub, string.upper
local random, ceil = math.random, math.ceil
+local rawget, rawset, type, getmetatable, setmetatable, tonumber = rawget, rawset, type, getmetatable, setmetatable, tonumber
+local rawget, rawset, type, getmetatable, setmetatable, tonumber = rawget, rawset, type, getmetatable, setmetatable, tonumber
+
+-- The following code permits traversing the environment table, at least
+-- in luatex. Internally all environment names are uppercase.
+
+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 -- initialize the table
+
+ function os.setenv(k,v)
+ if v == nil then
+ v = ""
+ end
+ local K = upper(k)
+ osenv[K] = v
+ 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 } )
-local execute, spawn, exec, ioflush = os.execute, os.spawn or os.execute, os.exec or os.execute, io.flush
+ end
+
+end
+
+-- end of environment hack
+
+local execute, spawn, exec, iopopen, ioflush = os.execute, os.spawn or os.execute, os.exec or os.execute, io.popen, io.flush
function os.execute(...) ioflush() return execute(...) end
function os.spawn (...) ioflush() return spawn (...) end
function os.exec (...) ioflush() return exec (...) end
+function io.popen (...) ioflush() return iopopen(...) end
function os.resultof(command)
- ioflush() -- else messed up logging
local handle = io.popen(command,"r")
- if not handle then
- -- print("unknown command '".. command .. "' in os.resultof")
- return ""
- else
- return handle:read("*all") or ""
- end
+ return handle and handle:read("*all") or ""
end
---~ os.type : windows | unix (new, we already guessed os.platform)
---~ os.name : windows | msdos | linux | macosx | solaris | .. | generic (new)
---~ os.platform : extended os.name with architecture
if not io.fileseparator then
if find(os.getenv("PATH"),";") then
@@ -1856,11 +1891,6 @@ function os.runtime()
return os.gettimeofday() - startuptime
end
---~ print(os.gettimeofday()-os.time())
---~ os.sleep(1.234)
---~ print (">>",os.runtime())
---~ print(os.date("%H:%M:%S",os.gettimeofday()))
---~ print(os.date("%H:%M:%S",os.time()))
-- no need for function anymore as we have more clever code and helpers now
-- this metatable trickery might as well disappear
@@ -1878,24 +1908,6 @@ end
setmetatable(os,osmt)
-if not os.setenv then
-
- -- we still store them but they won't be seen in
- -- child processes although we might pass them some day
- -- using command concatination
-
- local env, getenv = { }, os.getenv
-
- function os.setenv(k,v)
- env[k] = v
- end
-
- function os.getenv(k)
- return env[k] or getenv(k)
- end
-
-end
-
-- we can use HOSTTYPE on some platforms
local name, platform = os.name or "linux", os.getenv("MTX_PLATFORM") or ""
@@ -2016,7 +2028,7 @@ elseif name == "kfreebsd" then
-- we sometims have HOSTTYPE set so let's check that first
local platform, architecture = "", os.getenv("HOSTTYPE") or os.resultof("uname -m") or ""
if find(architecture,"x86_64") then
- platform = "kfreebsd-64"
+ platform = "kfreebsd-amd64"
else
platform = "kfreebsd-i386"
end
@@ -2093,9 +2105,10 @@ if not modules then modules = { } end modules ['l-file'] = {
file = file or { }
-local concat = table.concat
+local insert, concat = table.insert, table.concat
local find, gmatch, match, gsub, sub, char = string.find, string.gmatch, string.match, string.gsub, string.sub, string.char
local lpegmatch = lpeg.match
+local getcurrentdir = lfs.currentdir
local function dirname(name,default)
return match(name,"^(.+)[/\\].-$") or (default or "")
@@ -2133,6 +2146,13 @@ function file.addsuffix(filename, suffix, criterium)
return filename
elseif criterium == true then
return filename .. "." .. suffix
+ elseif not criterium then
+ local n, s = splitname(filename)
+ if not s or s == "" then
+ return filename .. "." .. suffix
+ else
+ return filename
+ end
else
local n, s = splitname(filename)
if s and s ~= "" then
@@ -2155,32 +2175,11 @@ function file.addsuffix(filename, suffix, criterium)
end
end
---~ print(file.addsuffix("name","new") .. "-> name.new")
---~ print(file.addsuffix("name.old","new") .. "-> name.old")
---~ print(file.addsuffix("name.old","new",true) .. "-> name.old.new")
---~ print(file.addsuffix("name.old","new","new") .. "-> name.new")
---~ print(file.addsuffix("name.old","new","old") .. "-> name.old")
---~ print(file.addsuffix("name.old","new","foo") .. "-> name.new")
---~ print(file.addsuffix("name.old","new",{"foo","bar"}) .. "-> name.new")
---~ print(file.addsuffix("name.old","new",{"old","bar"}) .. "-> name.old")
function file.replacesuffix(filename, suffix)
return (gsub(filename,"%.[%a%d]+$","")) .. "." .. suffix
end
---~ function file.join(...)
---~ local pth = concat({...},"/")
---~ pth = gsub(pth,"\\","/")
---~ local a, b = match(pth,"^(.*://)(.*)$")
---~ if a and b then
---~ return a .. gsub(b,"//+","/")
---~ end
---~ a, b = match(pth,"^(//)(.*)$")
---~ if a and b then
---~ return a .. gsub(b,"//+","/")
---~ end
---~ return (gsub(pth,"//+","/"))
---~ end
local trick_1 = char(1)
local trick_2 = "^" .. trick_1 .. "/+"
@@ -2208,15 +2207,6 @@ function file.join(...)
return (gsub(pth,"//+","/"))
end
---~ print(file.join("//","/y"))
---~ print(file.join("/","/y"))
---~ print(file.join("","/y"))
---~ print(file.join("/x/","/y"))
---~ print(file.join("x/","/y"))
---~ print(file.join("http://","/y"))
---~ print(file.join("http://a","/y"))
---~ print(file.join("http:///a","/y"))
---~ print(file.join("//nas-1","/y"))
function file.iswritable(name)
local a = lfs.attributes(name) or lfs.attributes(dirname(name,"."))
@@ -2233,17 +2223,6 @@ file.is_writable = file.iswritable
-- todo: lpeg
---~ function file.split_path(str)
---~ local t = { }
---~ str = gsub(str,"\\", "/")
---~ str = gsub(str,"(%a):([;/])", "%1\001%2")
---~ for name in gmatch(str,"([^;:]+)") do
---~ if name ~= "" then
---~ t[#t+1] = gsub(name,"\001",":")
---~ end
---~ end
---~ return t
---~ end
local checkedsplit = string.checkedsplit
@@ -2258,31 +2237,62 @@ end
-- we can hash them weakly
-function file.collapse_path(str)
+
+function file.collapse_path(str,anchor)
+ if anchor and not find(str,"^/") and not find(str,"^%a:") then
+ str = getcurrentdir() .. "/" .. str
+ end
+ if str == "" or str =="." then
+ return "."
+ elseif find(str,"^%.%.") then
+ str = gsub(str,"\\","/")
+ return str
+ elseif not find(str,"%.") then
+ str = gsub(str,"\\","/")
+ return str
+ end
str = gsub(str,"\\","/")
- if find(str,"/") then
- str = gsub(str,"^%./",(gsub(lfs.currentdir(),"\\","/")) .. "/") -- ./xx in qualified
- str = gsub(str,"/%./","/")
- local n, m = 1, 1
- while n > 0 or m > 0 do
- str, n = gsub(str,"[^/%.]+/%.%.$","")
- str, m = gsub(str,"[^/%.]+/%.%./","")
+ local starter, rest = match(str,"^(%a+:/*)(.-)$")
+ if starter then
+ str = rest
+ end
+ local oldelements = checkedsplit(str,"/")
+ local newelements = { }
+ local i = #oldelements
+ while i > 0 do
+ local element = oldelements[i]
+ if element == '.' then
+ -- do nothing
+ 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
- str = gsub(str,"([^/])/$","%1")
- -- str = gsub(str,"^%./","") -- ./xx in qualified
- str = gsub(str,"/%.$","")
+ i = i - 1
+ end
+ if #newelements == 0 then
+ return starter or "."
+ elseif starter then
+ return starter .. concat(newelements, '/')
+ elseif find(str,"^/") then
+ return "/" .. concat(newelements,'/')
+ else
+ return concat(newelements, '/')
end
- if str == "" then str = "." end
- return str
end
---~ print(file.collapse_path("/a"))
---~ print(file.collapse_path("a/./b/.."))
---~ print(file.collapse_path("a/aa/../b/bb"))
---~ print(file.collapse_path("a/../.."))
---~ print(file.collapse_path("a/.././././b/.."))
---~ print(file.collapse_path("a/./././b/.."))
---~ print(file.collapse_path("a/b/c/../.."))
function file.robustname(str)
return (gsub(str,"[^%a%d%/%-%.\\]+","-"))
@@ -2297,92 +2307,23 @@ end
-- lpeg variants, slightly faster, not always
---~ local period = lpeg.P(".")
---~ local slashes = lpeg.S("\\/")
---~ local noperiod = 1-period
---~ local noslashes = 1-slashes
---~ local name = noperiod^1
-
---~ local pattern = (noslashes^0 * slashes)^0 * (noperiod^1 * period)^1 * lpeg.C(noperiod^1) * -1
-
---~ function file.extname(name)
---~ return lpegmatch(pattern,name) or ""
---~ end
-
---~ local pattern = lpeg.Cs(((period * noperiod^1 * -1)/"" + 1)^1)
-
---~ function file.removesuffix(name)
---~ return lpegmatch(pattern,name)
---~ end
-
---~ local pattern = (noslashes^0 * slashes)^1 * lpeg.C(noslashes^1) * -1
-
---~ function file.basename(name)
---~ return lpegmatch(pattern,name) or name
---~ end
-
---~ local pattern = (noslashes^0 * slashes)^1 * lpeg.Cp() * noslashes^1 * -1
-
---~ function file.dirname(name)
---~ local p = lpegmatch(pattern,name)
---~ if p then
---~ return sub(name,1,p-2)
---~ else
---~ return ""
---~ end
---~ end
-
---~ local pattern = (noslashes^0 * slashes)^0 * (noperiod^1 * period)^1 * lpeg.Cp() * noperiod^1 * -1
-
---~ function file.addsuffix(name, suffix)
---~ local p = lpegmatch(pattern,name)
---~ if p then
---~ return name
---~ else
---~ return name .. "." .. suffix
---~ end
---~ end
-
---~ local pattern = (noslashes^0 * slashes)^0 * (noperiod^1 * period)^1 * lpeg.Cp() * noperiod^1 * -1
-
---~ function file.replacesuffix(name,suffix)
---~ local p = lpegmatch(pattern,name)
---~ if p then
---~ return sub(name,1,p-2) .. "." .. suffix
---~ else
---~ return name .. "." .. suffix
---~ end
---~ end
-
---~ local pattern = (noslashes^0 * slashes)^0 * lpeg.Cp() * ((noperiod^1 * period)^1 * lpeg.Cp() + lpeg.P(true)) * noperiod^1 * -1
-
---~ function file.nameonly(name)
---~ local a, b = lpegmatch(pattern,name)
---~ if b then
---~ return sub(name,a,b-2)
---~ elseif a then
---~ return sub(name,a)
---~ else
---~ return name
---~ end
---~ end
-
---~ local test = file.extname
---~ local test = file.basename
---~ local test = file.dirname
---~ local test = file.addsuffix
---~ local test = file.replacesuffix
---~ local test = file.nameonly
-
---~ print(1,test("./a/b/c/abd.def.xxx","!!!"))
---~ print(2,test("./../b/c/abd.def.xxx","!!!"))
---~ print(3,test("a/b/c/abd.def.xxx","!!!"))
---~ print(4,test("a/b/c/def.xxx","!!!"))
---~ print(5,test("a/b/c/def","!!!"))
---~ print(6,test("def","!!!"))
---~ print(7,test("def.xxx","!!!"))
-
---~ local tim = os.clock() for i=1,250000 do local ext = test("abd.def.xxx","!!!") end print(os.clock()-tim)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-- also rewrite previous
@@ -2422,14 +2363,6 @@ end
-- test { "/aa", "/aa/bb", "/aa/bb/cc", "/aa/bb/cc.dd", "/aa/bb/cc.dd.ee" }
-- test { "aa", "aa/bb", "aa/bb/cc", "aa/bb/cc.dd", "aa/bb/cc.dd.ee" }
---~ -- todo:
---~
---~ if os.type == "windows" then
---~ local currentdir = lfs.currentdir
---~ function lfs.currentdir()
---~ return (gsub(currentdir(),"\\","/"))
---~ end
---~ end
end -- of closure
@@ -2455,18 +2388,6 @@ if not md5.HEX then function md5.HEX(str) return convert(str,"%02X") end end
if not md5.hex then function md5.hex(str) return convert(str,"%02x") end end
if not md5.dec then function md5.dec(str) return convert(str,"%03i") end end
---~ if not md5.HEX then
---~ local function remap(chr) return format("%02X",byte(chr)) end
---~ function md5.HEX(str) return (gsub(md5.sum(str),".",remap)) end
---~ end
---~ if not md5.hex then
---~ local function remap(chr) return format("%02x",byte(chr)) end
---~ function md5.hex(str) return (gsub(md5.sum(str),".",remap)) end
---~ end
---~ if not md5.dec then
---~ local function remap(chr) return format("%03i",byte(chr)) end
---~ function md5.dec(str) return (gsub(md5.sum(str),".",remap)) end
---~ end
file.needs_updating_threshold = 1
@@ -2522,9 +2443,10 @@ if not modules then modules = { } end modules ['l-url'] = {
license = "see context related readme files"
}
-local char, gmatch, gsub = string.char, string.gmatch, string.gsub
+local char, gmatch, gsub, format, byte = string.char, string.gmatch, string.gsub, string.format, string.byte
+local concat = table.concat
local tonumber, type = tonumber, type
-local lpegmatch = lpeg.match
+local lpegmatch, lpegP, lpegC, lpegR, lpegS, lpegCs, lpegCc = lpeg.match, lpeg.P, lpeg.C, lpeg.R, lpeg.S, lpeg.Cs, lpeg.Cc
-- from the spec (on the web):
--
@@ -2542,22 +2464,35 @@ local function tochar(s)
return char(tonumber(s,16))
end
-local colon, qmark, hash, slash, percent, endofstring = lpeg.P(":"), lpeg.P("?"), lpeg.P("#"), lpeg.P("/"), lpeg.P("%"), lpeg.P(-1)
+local colon, qmark, hash, slash, percent, endofstring = lpegP(":"), lpegP("?"), lpegP("#"), lpegP("/"), lpegP("%"), lpegP(-1)
-local hexdigit = lpeg.R("09","AF","af")
-local plus = lpeg.P("+")
-local escaped = (plus / " ") + (percent * lpeg.C(hexdigit * hexdigit) / tochar)
+local hexdigit = lpegR("09","AF","af")
+local plus = lpegP("+")
+local nothing = lpegCc("")
+local escaped = (plus / " ") + (percent * lpegC(hexdigit * hexdigit) / tochar)
-- we assume schemes with more than 1 character (in order to avoid problems with windows disks)
-local scheme = lpeg.Cs((escaped+(1-colon-slash-qmark-hash))^2) * colon + lpeg.Cc("")
-local authority = slash * slash * lpeg.Cs((escaped+(1- slash-qmark-hash))^0) + lpeg.Cc("")
-local path = slash * lpeg.Cs((escaped+(1- qmark-hash))^0) + lpeg.Cc("")
-local query = qmark * lpeg.Cs((escaped+(1- hash))^0) + lpeg.Cc("")
-local fragment = hash * lpeg.Cs((escaped+(1- endofstring))^0) + lpeg.Cc("")
+local scheme = lpegCs((escaped+(1-colon-slash-qmark-hash))^2) * colon + nothing
+local authority = slash * slash * lpegCs((escaped+(1- slash-qmark-hash))^0) + nothing
+local path = slash * lpegCs((escaped+(1- qmark-hash))^0) + nothing
+local query = qmark * lpegCs((escaped+(1- hash))^0) + nothing
+local fragment = hash * lpegCs((escaped+(1- endofstring))^0) + nothing
local parser = lpeg.Ct(scheme * authority * path * query * fragment)
+lpeg.patterns.urlsplitter = parser
+
+local escapes = { }
+
+for i=0,255 do
+ escapes[i] = format("%%%02X",i)
+end
+
+local escaper = lpeg.Cs((lpegR("09","AZ","az") + lpegS("-./_") + lpegP(1) / escapes)^0)
+
+lpeg.patterns.urlescaper = escaper
+
-- todo: reconsider Ct as we can as well have five return values (saves a table)
-- so we can have two parsers, one with and one without
@@ -2570,15 +2505,27 @@ end
function url.hashed(str)
local s = url.split(str)
local somescheme = s[1] ~= ""
- return {
- scheme = (somescheme and s[1]) or "file",
- authority = s[2],
- path = s[3],
- query = s[4],
- fragment = s[5],
- original = str,
- noscheme = not somescheme,
- }
+ if not somescheme then
+ return {
+ scheme = "file",
+ authority = "",
+ path = str,
+ query = "",
+ fragment = "",
+ original = str,
+ noscheme = true,
+ }
+ else
+ return {
+ scheme = s[1],
+ authority = s[2],
+ path = s[3],
+ query = s[4],
+ fragment = s[5],
+ original = str,
+ noscheme = false,
+ }
+ end
end
function url.hasscheme(str)
@@ -2589,15 +2536,25 @@ function url.addscheme(str,scheme)
return (url.hasscheme(str) and str) or ((scheme or "file:///") .. str)
end
-function url.construct(hash)
- local fullurl = hash.sheme .. "://".. hash.authority .. hash.path
- if hash.query then
- fullurl = fullurl .. "?".. hash.query
+function url.construct(hash) -- dodo: we need to escape !
+ local fullurl = { }
+ local scheme, authority, path, query, fragment = hash.scheme, hash.authority, hash.path, hash.query, hash.fragment
+ if scheme and scheme ~= "" then
+ fullurl[#fullurl+1] = scheme .. "://"
+ end
+ if authority and authority ~= "" then
+ fullurl[#fullurl+1] = authority
+ end
+ if path and path ~= "" then
+ fullurl[#fullurl+1] = "/" .. path
end
- if hash.fragment then
- fullurl = fullurl .. "?".. hash.fragment
+ if query and query ~= "" then
+ fullurl[#fullurl+1] = "?".. query
end
- return fullurl
+ if fragment and fragment ~= "" then
+ fullurl[#fullurl+1] = "#".. fragment
+ end
+ return lpegmatch(escaper,concat(fullurl))
end
function url.filename(filename)
@@ -2617,37 +2574,12 @@ function url.query(str)
end
end
---~ print(url.filename("file:///c:/oeps.txt"))
---~ print(url.filename("c:/oeps.txt"))
---~ print(url.filename("file:///oeps.txt"))
---~ print(url.filename("file:///etc/test.txt"))
---~ print(url.filename("/oeps.txt"))
-
---~ from the spec on the web (sort of):
---~
---~ function test(str)
---~ print(table.serialize(url.hashed(str)))
---~ end
---~
---~ test("%56pass%20words")
---~ test("file:///c:/oeps.txt")
---~ test("file:///c|/oeps.txt")
---~ test("file:///etc/oeps.txt")
---~ test("file://./etc/oeps.txt")
---~ test("file:////etc/oeps.txt")
---~ test("ftp://ftp.is.co.za/rfc/rfc1808.txt")
---~ test("http://www.ietf.org/rfc/rfc2396.txt")
---~ test("ldap://[2001:db8::7]/c=GB?objectClass?one#what")
---~ test("mailto:John.Doe@example.com")
---~ test("news:comp.infosystems.www.servers.unix")
---~ test("tel:+1-816-555-1212")
---~ test("telnet://192.0.2.16:80/")
---~ test("urn:oasis:names:specification:docbook:dtd:xml:4.1.2")
---~ test("/etc/passwords")
---~ test("http://www.pragma-ade.com/spaced%20name")
-
---~ test("zip:///oeps/oeps.zip#bla/bla.tex")
---~ test("zip:///oeps/oeps.zip?bla/bla.tex")
+
+
+
+
+
+
end -- of closure
@@ -2802,11 +2734,6 @@ end
dir.glob = glob
---~ list = dir.glob("**/*.tif")
---~ list = dir.glob("/**/*.tif")
---~ list = dir.glob("./**/*.tif")
---~ list = dir.glob("oeps/**/*.tif")
---~ list = dir.glob("/oeps/**/*.tif")
local function globfiles(path,recurse,func,files) -- func == pattern or function
if type(func) == "string" then
@@ -2850,10 +2777,6 @@ function dir.ls(pattern)
return table.concat(glob(pattern),"\n")
end
---~ mkdirs("temp")
---~ mkdirs("a/b/c")
---~ mkdirs(".","/a/b/c")
---~ mkdirs("a","b","c")
local make_indeed = true -- false
@@ -2913,17 +2836,6 @@ if string.find(os.getenv("PATH"),";") then -- os.type == "windows"
return pth, (lfs.isdir(pth) == true)
end
---~ print(dir.mkdirs("","","a","c"))
---~ print(dir.mkdirs("a"))
---~ print(dir.mkdirs("a:"))
---~ print(dir.mkdirs("a:/b/c"))
---~ print(dir.mkdirs("a:b/c"))
---~ print(dir.mkdirs("a:/bbb/c"))
---~ print(dir.mkdirs("/a/b/c"))
---~ print(dir.mkdirs("/aaa/b/c"))
---~ print(dir.mkdirs("//a/b/c"))
---~ print(dir.mkdirs("///a/b/c"))
---~ print(dir.mkdirs("a/bbb//ccc/"))
function dir.expand_name(str) -- will be merged with cleanpath and collapsepath
local first, nothing, last = match(str,"^(//)(//*)(.*)$")
@@ -2963,7 +2875,7 @@ else
local str, pth, t = "", "", { ... }
for i=1,#t do
local s = t[i]
- if s ~= "" then
+ if s and s ~= "" then -- we catch nil and false
if str ~= "" then
str = str .. "/" .. s
else
@@ -2997,13 +2909,6 @@ else
return pth, (lfs.isdir(pth) == true)
end
---~ print(dir.mkdirs("","","a","c"))
---~ print(dir.mkdirs("a"))
---~ print(dir.mkdirs("/a/b/c"))
---~ print(dir.mkdirs("/aaa/b/c"))
---~ print(dir.mkdirs("//a/b/c"))
---~ print(dir.mkdirs("///a/b/c"))
---~ print(dir.mkdirs("a/bbb//ccc/"))
function dir.expand_name(str) -- will be merged with cleanpath and collapsepath
if not find(str,"^/") then
@@ -3060,7 +2965,7 @@ function toboolean(str,tolerant)
end
end
-function string.is_boolean(str)
+function string.is_boolean(str,default)
if type(str) == "string" then
if str == "true" or str == "yes" or str == "on" or str == "t" then
return true
@@ -3068,7 +2973,7 @@ function string.is_boolean(str)
return false
end
end
- return nil
+ return default
end
function boolean.alwaystrue()
@@ -3084,6 +2989,211 @@ end -- of closure
do -- create closure to overcome 200 locals limit
+if not modules then modules = { } end modules ['l-unicode'] = {
+ version = 1.001,
+ comment = "companion to luat-lib.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+if not unicode then
+
+ unicode = { utf8 = { } }
+
+ local floor, char = math.floor, string.char
+
+ function unicode.utf8.utfchar(n)
+ if n < 0x80 then
+ return char(n)
+ elseif n < 0x800 then
+ return char(0xC0 + floor(n/0x40)) .. char(0x80 + (n % 0x40))
+ elseif n < 0x10000 then
+ return char(0xE0 + floor(n/0x1000)) .. char(0x80 + (floor(n/0x40) % 0x40)) .. char(0x80 + (n % 0x40))
+ elseif n < 0x40000 then
+ return char(0xF0 + floor(n/0x40000)) .. char(0x80 + floor(n/0x1000)) .. char(0x80 + (floor(n/0x40) % 0x40)) .. char(0x80 + (n % 0x40))
+ else -- wrong:
+ -- return char(0xF1 + floor(n/0x1000000)) .. char(0x80 + floor(n/0x40000)) .. char(0x80 + floor(n/0x1000)) .. char(0x80 + (floor(n/0x40) % 0x40)) .. char(0x80 + (n % 0x40))
+ return "?"
+ end
+ end
+
+end
+
+utf = utf or unicode.utf8
+
+local concat, utfchar, utfgsub = table.concat, utf.char, utf.gsub
+local char, byte, find, bytepairs = string.char, string.byte, string.find, string.bytepairs
+
+-- 0 EF BB BF UTF-8
+-- 1 FF FE UTF-16-little-endian
+-- 2 FE FF UTF-16-big-endian
+-- 3 FF FE 00 00 UTF-32-little-endian
+-- 4 00 00 FE FF UTF-32-big-endian
+
+unicode.utfname = {
+ [0] = 'utf-8',
+ [1] = 'utf-16-le',
+ [2] = 'utf-16-be',
+ [3] = 'utf-32-le',
+ [4] = 'utf-32-be'
+}
+
+-- \000 fails in <= 5.0 but is valid in >=5.1 where %z is depricated
+
+function unicode.utftype(f)
+ local str = f:read(4)
+ if not str then
+ f:seek('set')
+ return 0
+ -- elseif find(str,"^%z%z\254\255") then -- depricated
+ -- elseif find(str,"^\000\000\254\255") then -- not permitted and bugged
+ elseif find(str,"\000\000\254\255",1,true) then -- seems to work okay (TH)
+ return 4
+ -- elseif find(str,"^\255\254%z%z") then -- depricated
+ -- elseif find(str,"^\255\254\000\000") then -- not permitted and bugged
+ elseif find(str,"\255\254\000\000",1,true) then -- seems to work okay (TH)
+ return 3
+ elseif find(str,"^\254\255") then
+ f:seek('set',2)
+ return 2
+ elseif find(str,"^\255\254") then
+ f:seek('set',2)
+ return 1
+ elseif find(str,"^\239\187\191") then
+ f:seek('set',3)
+ return 0
+ else
+ f:seek('set')
+ return 0
+ end
+end
+
+function unicode.utf16_to_utf8(str, endian) -- maybe a gsub is faster or an lpeg
+ local result, tmp, n, m, p = { }, { }, 0, 0, 0
+ -- lf | cr | crlf / (cr:13, lf:10)
+ local function doit()
+ if n == 10 then
+ if p ~= 13 then
+ result[#result+1] = concat(tmp)
+ tmp = { }
+ p = 0
+ end
+ elseif n == 13 then
+ result[#result+1] = concat(tmp)
+ tmp = { }
+ p = n
+ else
+ tmp[#tmp+1] = utfchar(n)
+ p = 0
+ end
+ end
+ for l,r in bytepairs(str) do
+ if r then
+ if endian then
+ n = l*256 + r
+ else
+ n = r*256 + l
+ end
+ if m > 0 then
+ n = (m-0xD800)*0x400 + (n-0xDC00) + 0x10000
+ m = 0
+ doit()
+ elseif n >= 0xD800 and n <= 0xDBFF then
+ m = n
+ else
+ doit()
+ end
+ end
+ end
+ if #tmp > 0 then
+ result[#result+1] = concat(tmp)
+ end
+ return result
+end
+
+function unicode.utf32_to_utf8(str, endian)
+ local result = { }
+ local tmp, n, m, p = { }, 0, -1, 0
+ -- lf | cr | crlf / (cr:13, lf:10)
+ local function doit()
+ if n == 10 then
+ if p ~= 13 then
+ result[#result+1] = concat(tmp)
+ tmp = { }
+ p = 0
+ end
+ elseif n == 13 then
+ result[#result+1] = concat(tmp)
+ tmp = { }
+ p = n
+ else
+ tmp[#tmp+1] = utfchar(n)
+ p = 0
+ end
+ end
+ for a,b in bytepairs(str) do
+ if a and b then
+ if m < 0 then
+ if endian then
+ m = a*256*256*256 + b*256*256
+ else
+ m = b*256 + a
+ end
+ else
+ if endian then
+ n = m + a*256 + b
+ else
+ n = m + b*256*256*256 + a*256*256
+ end
+ m = -1
+ doit()
+ end
+ else
+ break
+ end
+ end
+ if #tmp > 0 then
+ result[#result+1] = concat(tmp)
+ end
+ return result
+end
+
+local function little(c)
+ local b = byte(c) -- b = c:byte()
+ if b < 0x10000 then
+ return char(b%256,b/256)
+ else
+ b = b - 0x10000
+ local b1, b2 = b/1024 + 0xD800, b%1024 + 0xDC00
+ return char(b1%256,b1/256,b2%256,b2/256)
+ end
+end
+
+local function big(c)
+ local b = byte(c)
+ if b < 0x10000 then
+ return char(b/256,b%256)
+ else
+ b = b - 0x10000
+ local b1, b2 = b/1024 + 0xD800, b%1024 + 0xDC00
+ return char(b1/256,b1%256,b2/256,b2%256)
+ end
+end
+
+function unicode.utf8_to_utf16(str,littleendian)
+ if littleendian then
+ return char(255,254) .. utfgsub(str,".",little)
+ else
+ return char(254,255) .. utfgsub(str,".",big)
+ end
+end
+
+
+end -- of closure
+
+do -- create closure to overcome 200 locals limit
+
if not modules then modules = { } end modules ['l-math'] = {
version = 1.001,
comment = "companion to luat-lib.mkiv",
@@ -3141,7 +3251,7 @@ if not modules then modules = { } end modules ['l-utils'] = {
-- hm, quite unreadable
-local gsub = string.gsub
+local gsub, format = string.gsub, string.format
local concat = table.concat
local type, next = type, next
@@ -3149,81 +3259,79 @@ if not utils then utils = { } end
if not utils.merger then utils.merger = { } end
if not utils.lua then utils.lua = { } end
-utils.merger.m_begin = "begin library merge"
-utils.merger.m_end = "end library merge"
-utils.merger.pattern =
+utils.report = utils.report or print
+
+local merger = utils.merger
+
+merger.strip_comment = true
+
+local m_begin_merge = "begin library merge"
+local m_end_merge = "end library merge"
+local m_begin_closure = "do -- create closure to overcome 200 locals limit"
+local m_end_closure = "end -- of closure"
+
+local m_pattern =
"%c+" ..
- "%-%-%s+" .. utils.merger.m_begin ..
+ "%-%-%s+" .. m_begin_merge ..
"%c+(.-)%c+" ..
- "%-%-%s+" .. utils.merger.m_end ..
+ "%-%-%s+" .. m_end_merge ..
"%c+"
-function utils.merger._self_fake_()
- return
- "-- " .. "created merged file" .. "\n\n" ..
- "-- " .. utils.merger.m_begin .. "\n\n" ..
- "-- " .. utils.merger.m_end .. "\n\n"
-end
+local m_format =
+ "\n\n-- " .. m_begin_merge ..
+ "\n%s\n" ..
+ "-- " .. m_end_merge .. "\n\n"
-function utils.report(...)
- print(...)
+local m_faked =
+ "-- " .. "created merged file" .. "\n\n" ..
+ "-- " .. m_begin_merge .. "\n\n" ..
+ "-- " .. m_end_merge .. "\n\n"
+
+local function self_fake()
+ return m_faked
end
-utils.merger.strip_comment = true
+local function self_nothing()
+ return ""
+end
-function utils.merger._self_load_(name)
- local f, data = io.open(name), ""
- if f then
- utils.report("reading merge from %s",name)
- data = f:read("*all")
- f:close()
+local function self_load(name)
+ local data = io.loaddata(name) or ""
+ if data == "" then
+ utils.report("merge: unknown file %s",name)
else
- utils.report("unknown file to merge %s",name)
- end
- if data and utils.merger.strip_comment then
- -- saves some 20K
- data = gsub(data,"%-%-~[^\n\r]*[\r\n]", "")
+ utils.report("merge: inserting %s",name)
end
return data or ""
end
-function utils.merger._self_save_(name, data)
+local function self_save(name, data)
if data ~= "" then
- local f = io.open(name,'w')
- if f then
- utils.report("saving merge from %s",name)
- f:write(data)
- f:close()
+ if merger.strip_comment then
+ -- saves some 20K
+ local n = #data
+ data = gsub(data,"%-%-~[^\n\r]*[\r\n]","")
+ utils.report("merge: %s bytes of comment stripped, %s bytes of code left",n-#data,#data)
end
+ io.savedata(name,data)
+ utils.report("merge: saving %s",name)
end
end
-function utils.merger._self_swap_(data,code)
- if data ~= "" then
- return (gsub(data,utils.merger.pattern, function(s)
- return "\n\n" .. "-- "..utils.merger.m_begin .. "\n" .. code .. "\n" .. "-- "..utils.merger.m_end .. "\n\n"
- end, 1))
- else
- return ""
- end
+local function self_swap(data,code)
+ return data ~= "" and (gsub(data,m_pattern, function() return format(m_format,code) end, 1)) or ""
end
---~ stripper:
---~
---~ data = gsub(data,"%-%-~[^\n]*\n","")
---~ data = gsub(data,"\n\n+","\n")
-
-function utils.merger._self_libs_(libs,list)
- local result, f, frozen = { }, nil, false
+local function self_libs(libs,list)
+ local result, f, frozen, foundpath = { }, nil, false, nil
result[#result+1] = "\n"
if type(libs) == 'string' then libs = { libs } end
if type(list) == 'string' then list = { list } end
- local foundpath = nil
for i=1,#libs do
local lib = libs[i]
for j=1,#list do
local pth = gsub(list[j],"\\","/") -- file.clean_path
- utils.report("checking library path %s",pth)
+ utils.report("merge: checking library path %s",pth)
local name = pth .. "/" .. lib
if lfs.isfile(name) then
foundpath = pth
@@ -3232,76 +3340,58 @@ function utils.merger._self_libs_(libs,list)
if foundpath then break end
end
if foundpath then
- utils.report("using library path %s",foundpath)
+ utils.report("merge: using library path %s",foundpath)
local right, wrong = { }, { }
for i=1,#libs do
local lib = libs[i]
local fullname = foundpath .. "/" .. lib
if lfs.isfile(fullname) then
- -- right[#right+1] = lib
- utils.report("merging library %s",fullname)
- result[#result+1] = "do -- create closure to overcome 200 locals limit"
+ utils.report("merge: using library %s",fullname)
+ right[#right+1] = lib
+ result[#result+1] = m_begin_closure
result[#result+1] = io.loaddata(fullname,true)
- result[#result+1] = "end -- of closure"
+ result[#result+1] = m_end_closure
else
- -- wrong[#wrong+1] = lib
- utils.report("no library %s",fullname)
+ utils.report("merge: skipping library %s",fullname)
+ wrong[#wrong+1] = lib
end
end
if #right > 0 then
- utils.report("merged libraries: %s",concat(right," "))
+ utils.report("merge: used libraries: %s",concat(right," "))
end
if #wrong > 0 then
- utils.report("skipped libraries: %s",concat(wrong," "))
+ utils.report("merge: skipped libraries: %s",concat(wrong," "))
end
else
- utils.report("no valid library path found")
+ utils.report("merge: no valid library path found")
end
return concat(result, "\n\n")
end
-function utils.merger.selfcreate(libs,list,target)
+function merger.selfcreate(libs,list,target)
if target then
- utils.merger._self_save_(
- target,
- utils.merger._self_swap_(
- utils.merger._self_fake_(),
- utils.merger._self_libs_(libs,list)
- )
- )
- end
-end
-
-function utils.merger.selfmerge(name,libs,list,target)
- utils.merger._self_save_(
- target or name,
- utils.merger._self_swap_(
- utils.merger._self_load_(name),
- utils.merger._self_libs_(libs,list)
- )
- )
+ self_save(target,self_swap(self_fake(),self_libs(libs,list)))
+ end
end
-function utils.merger.selfclean(name)
- utils.merger._self_save_(
- name,
- utils.merger._self_swap_(
- utils.merger._self_load_(name),
- ""
- )
- )
+function merger.selfmerge(name,libs,list,target)
+ self_save(target or name,self_swap(self_load(name),self_libs(libs,list)))
+end
+
+function merger.selfclean(name)
+ self_save(name,self_swap(self_load(name),self_nothing()))
end
-function utils.lua.compile(luafile, lucfile, cleanup, strip) -- defaults: cleanup=false strip=true
- -- utils.report("compiling",luafile,"into",lucfile)
+function utils.lua.compile(luafile,lucfile,cleanup,strip) -- defaults: cleanup=false strip=true
+ utils.report("lua: compiling %s into %s",luafile,lucfile)
os.remove(lucfile)
local command = "-o " .. string.quote(lucfile) .. " " .. string.quote(luafile)
if strip ~= false then
command = "-s " .. command
end
- local done = (os.spawn("texluac " .. command) == 0) or (os.spawn("luac " .. command) == 0)
+ local done = os.spawn("texluac " .. command) == 0 or os.spawn("luac " .. command) == 0
if done and cleanup == true and lfs.isfile(lucfile) and lfs.isfile(luafile) then
- -- utils.report("removing",luafile)
+ utils.report("lua: removing %s",luafile)
os.remove(luafile)
end
return done
@@ -3385,11 +3475,7 @@ end
function aux.settings_to_hash(str,existing)
if str and str ~= "" then
hash = existing or { }
- if moretolerant then
- lpegmatch(pattern_b_s,str)
- else
- lpegmatch(pattern_a_s,str)
- end
+ lpegmatch(pattern_a_s,str)
return hash
else
return { }
@@ -3519,12 +3605,6 @@ local case_2 = period * (digit - trailingzeros)^1 * (trailingzeros / "")
local number = digit^1 * (case_1 + case_2)
local stripper = lpeg.Cs((number + 1)^0)
---~ local sample = "bla 11.00 bla 11 bla 0.1100 bla 1.00100 bla 0.00 bla 0.001 bla 1.1100 bla 0.100100100 bla 0.00100100100"
---~ collectgarbage("collect")
---~ str = string.rep(sample,10000)
---~ local ts = os.clock()
---~ lpegmatch(stripper,str)
---~ print(#str, os.clock()-ts, lpegmatch(stripper,sample))
lpeg.patterns.strip_zeros = stripper
@@ -3553,235 +3633,305 @@ function aux.accesstable(target)
return t
end
---~ function string.commaseparated(str)
---~ return gmatch(str,"([^,%s]+)")
---~ end
-- as we use this a lot ...
---~ function aux.cachefunction(action,weak)
---~ local cache = { }
---~ if weak then
---~ setmetatable(cache, { __mode = "kv" } )
---~ end
---~ local function reminder(str)
---~ local found = cache[str]
---~ if not found then
---~ found = action(str)
---~ cache[str] = found
---~ end
---~ return found
---~ end
---~ return reminder, cache
---~ end
end -- of closure
do -- create closure to overcome 200 locals limit
-if not modules then modules = { } end modules ['trac-tra'] = {
+if not modules then modules = { } end modules ['trac-inf'] = {
version = 1.001,
- comment = "companion to trac-tra.mkiv",
+ comment = "companion to trac-inf.mkiv",
author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright = "PRAGMA ADE / ConTeXt Development Team",
license = "see context related readme files"
}
--- the <anonymous> tag is kind of generic and used for functions that are not
--- bound to a variable, like node.new, node.copy etc (contrary to for instance
--- node.has_attribute which is bound to a has_attribute local variable in mkiv)
+-- As we want to protect the global tables, we no longer store the timing
+-- in the tables themselves but in a hidden timers table so that we don't
+-- get warnings about assignments. This is more efficient than using rawset
+-- and rawget.
-local debug = require "debug"
+local format = string.format
+local clock = os.gettimeofday or os.clock -- should go in environment
-local getinfo = debug.getinfo
-local type, next = type, next
-local concat = table.concat
-local format, find, lower, gmatch, gsub = string.format, string.find, string.lower, string.gmatch, string.gsub
+local statusinfo, n, registered = { }, 0, { }
-debugger = debugger or { }
+statistics = statistics or { }
-local counters = { }
-local names = { }
+statistics.enable = true
+statistics.threshold = 0.05
--- one
+local timers = { }
-local function hook()
- local f = getinfo(2,"f").func
- local n = getinfo(2,"Sn")
--- if n.what == "C" and n.name then print (n.namewhat .. ': ' .. n.name) end
- if f then
- local cf = counters[f]
- if cf == nil then
- counters[f] = 1
- names[f] = n
- else
- counters[f] = cf + 1
- end
- end
+local function hastiming(instance)
+ return instance and timers[instance]
end
-local function getname(func)
- local n = names[func]
- if n then
- if n.what == "C" then
- return n.name or '<anonymous>'
- else
- -- source short_src linedefined what name namewhat nups func
- local name = n.name or n.namewhat or n.what
- if not name or name == "" then name = "?" end
- return format("%s : %s : %s", n.short_src or "unknown source", n.linedefined or "--", name)
+
+local function resettiming(instance)
+ timers[instance or "notimer"] = { timing = 0, loadtime = 0 }
+end
+
+local function starttiming(instance)
+ local timer = timers[instance or "notimer"]
+ if not timer then
+ timer = { }
+ timers[instance or "notimer"] = timer
+ end
+ local it = timer.timing
+ if not it then
+ it = 0
+ end
+ if it == 0 then
+ timer.starttime = clock()
+ if not timer.loadtime then
+ timer.loadtime = 0
end
- else
- return "unknown"
end
+ timer.timing = it + 1
end
-function debugger.showstats(printer,threshold)
- printer = printer or texio.write or print
- threshold = threshold or 0
- local total, grandtotal, functions = 0, 0, 0
- printer("\n") -- ugly but ok
- -- table.sort(counters)
- for func, count in next, counters do
- if count > threshold then
- local name = getname(func)
- if not find(name,"for generator") then
- printer(format("%8i %s", count, name))
- total = total + count
+
+local function stoptiming(instance, report)
+ local timer = timers[instance or "notimer"]
+ local it = timer.timing
+ if it > 1 then
+ timer.timing = it - 1
+ else
+ local starttime = timer.starttime
+ if starttime then
+ local stoptime = clock()
+ local loadtime = stoptime - starttime
+ timer.stoptime = stoptime
+ timer.loadtime = timer.loadtime + loadtime
+ if report then
+ statistics.report("load time %0.3f",loadtime)
end
+ timer.timing = 0
+ return loadtime
end
- grandtotal = grandtotal + count
- functions = functions + 1
end
- printer(format("functions: %s, total: %s, grand total: %s, threshold: %s\n", functions, total, grandtotal, threshold))
+ return 0
end
--- two
-
---~ local function hook()
---~ local n = getinfo(2)
---~ if n.what=="C" and not n.name then
---~ local f = tostring(debug.traceback())
---~ local cf = counters[f]
---~ if cf == nil then
---~ counters[f] = 1
---~ names[f] = n
---~ else
---~ counters[f] = cf + 1
---~ end
---~ end
---~ end
---~ function debugger.showstats(printer,threshold)
---~ printer = printer or texio.write or print
---~ threshold = threshold or 0
---~ local total, grandtotal, functions = 0, 0, 0
---~ printer("\n") -- ugly but ok
---~ -- table.sort(counters)
---~ for func, count in next, counters do
---~ if count > threshold then
---~ printer(format("%8i %s", count, func))
---~ total = total + count
---~ end
---~ grandtotal = grandtotal + count
---~ functions = functions + 1
---~ end
---~ printer(format("functions: %s, total: %s, grand total: %s, threshold: %s\n", functions, total, grandtotal, threshold))
---~ end
+local function elapsedtime(instance)
+ local timer = timers[instance or "notimer"]
+ return format("%0.3f",timer and timer.loadtime or 0)
+end
--- rest
+local function elapsedindeed(instance)
+ local timer = timers[instance or "notimer"]
+ return (timer and timer.loadtime or 0) > statistics.threshold
+end
-function debugger.savestats(filename,threshold)
- local f = io.open(filename,'w')
- if f then
- debugger.showstats(function(str) f:write(str) end,threshold)
- f:close()
+local function elapsedseconds(instance,rest) -- returns nil if 0 seconds
+ if elapsedindeed(instance) then
+ return format("%s seconds %s", elapsedtime(instance),rest or "")
end
end
-function debugger.enable()
- debug.sethook(hook,"c")
+statistics.hastiming = hastiming
+statistics.resettiming = resettiming
+statistics.starttiming = starttiming
+statistics.stoptiming = stoptiming
+statistics.elapsedtime = elapsedtime
+statistics.elapsedindeed = elapsedindeed
+statistics.elapsedseconds = elapsedseconds
+
+-- general function
+
+function statistics.register(tag,fnc)
+ if statistics.enable and type(fnc) == "function" then
+ local rt = registered[tag] or (#statusinfo + 1)
+ statusinfo[rt] = { tag, fnc }
+ registered[tag] = rt
+ if #tag > n then n = #tag end
+ end
end
-function debugger.disable()
- debug.sethook()
---~ counters[debug.getinfo(2,"f").func] = nil
+function statistics.show(reporter)
+ if statistics.enable then
+ if not reporter then reporter = function(tag,data,n) texio.write_nl(tag .. " " .. data) end end
+ -- this code will move
+ local register = statistics.register
+ register("luatex banner", function()
+ return string.lower(status.banner)
+ end)
+ register("control sequences", function()
+ return format("%s of %s", status.cs_count, status.hash_size+status.hash_extra)
+ end)
+ register("callbacks", function()
+ local total, indirect = status.callbacks or 0, status.indirect_callbacks or 0
+ return format("direct: %s, indirect: %s, total: %s", total-indirect, indirect, total)
+ end)
+ register("current memory usage", statistics.memused)
+ register("runtime",statistics.runtime)
+ for i=1,#statusinfo do
+ local s = statusinfo[i]
+ local r = s[2]()
+ if r then
+ reporter(s[1],r,n)
+ end
+ end
+ texio.write_nl("") -- final newline
+ statistics.enable = false
+ end
end
-function debugger.tracing()
- local n = tonumber(os.env['MTX.TRACE.CALLS']) or tonumber(os.env['MTX_TRACE_CALLS']) or 0
- if n > 0 then
- function debugger.tracing() return true end ; return true
+function statistics.show_job_stat(tag,data,n)
+ if type(data) == "table" then
+ for i=1,#data do
+ statistics.show_job_stat(tag,data[i],n)
+ end
else
- function debugger.tracing() return false end ; return false
+ texio.write_nl(format("%-15s: %s - %s","mkiv lua stats",tag:rpadd(n," "),data))
end
end
---~ debugger.enable()
+function statistics.memused() -- no math.round yet -)
+ local round = math.round or math.floor
+ return format("%s MB (ctx: %s MB)",round(collectgarbage("count")/1000), round(status.luastate_bytes/1000000))
+end
+
+starttiming(statistics)
+
+function statistics.formatruntime(runtime) -- indirect so it can be overloaded and
+ return format("%s seconds", runtime) -- indeed that happens in cure-uti.lua
+end
+
+function statistics.runtime()
+ stoptiming(statistics)
+ return statistics.formatruntime(elapsedtime(statistics))
+end
+
+function statistics.timed(action,report)
+ report = report or logs.simple
+ starttiming("run")
+ action()
+ stoptiming("run")
+ report("total runtime: %s",elapsedtime("run"))
+end
+
+-- where, not really the best spot for this:
+
+commands = commands or { }
+
+function commands.resettimer(name)
+ resettiming(name or "whatever")
+ starttiming(name or "whatever")
+end
+
+function commands.elapsedtime(name)
+ stoptiming(name or "whatever")
+ tex.sprint(elapsedtime(name or "whatever"))
+end
+
+
+end -- of closure
+
+do -- create closure to overcome 200 locals limit
+
+if not modules then modules = { } end modules ['trac-set'] = {
+ version = 1.001,
+ comment = "companion to luat-lib.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local type, next, tostring = type, next, tostring
+local concat = table.concat
+local format, find, lower, gsub = string.format, string.find, string.lower, string.gsub
+local is_boolean = string.is_boolean
+
+setters = { }
+
+local data = { } -- maybe just local
---~ print(math.sin(1*.5))
---~ print(math.sin(1*.5))
---~ print(math.sin(1*.5))
---~ print(math.sin(1*.5))
---~ print(math.sin(1*.5))
+-- We can initialize from the cnf file. This is sort of tricky as
+-- laster defined setters also need to be initialized then. If set
+-- this way, we need to ensure that they are not reset later on.
---~ debugger.disable()
+local trace_initialize = false
---~ print("")
---~ debugger.showstats()
---~ print("")
---~ debugger.showstats(print,3)
+local function report(what,filename,name,key,value)
+ texio.write_nl(format("%s setter, filename: %s, name: %s, key: %s, value: %s",what,filename,name,key,value))
+end
-setters = setters or { }
-setters.data = setters.data or { }
+function setters.initialize(filename,name,values) -- filename only for diagnostics
+ local data = data[name]
+ if data then
+ data = data.data
+ if data then
+ for key, value in next, values do
+ key = gsub(key,"_",".")
+ value = is_boolean(value,value)
+ local functions = data[key]
+ if functions then
+ if #functions > 0 and not functions.value then
+ if trace_initialize then
+ report("doing",filename,name,key,value)
+ end
+ for i=1,#functions do
+ functions[i](value)
+ end
+ functions.value = value
+ else
+ if trace_initialize then
+ report("skipping",filename,name,key,value)
+ end
+ end
+ else
+ -- we do a simple preregistration i.e. not in the
+ -- list as it might be an obsolete entry
+ functions = { default = value }
+ data[key] = functions
+ if trace_initialize then
+ report("storing",filename,name,key,value)
+ end
+ end
+ end
+ end
+ end
+end
---~ local function set(t,what,value)
---~ local data, done = t.data, t.done
---~ if type(what) == "string" then
---~ what = aux.settings_to_array(what) -- inefficient but ok
---~ end
---~ for i=1,#what do
---~ local w = what[i]
---~ for d, f in next, data do
---~ if done[d] then
---~ -- prevent recursion due to wildcards
---~ elseif find(d,w) then
---~ done[d] = true
---~ for i=1,#f do
---~ f[i](value)
---~ end
---~ end
---~ end
---~ end
---~ end
+-- user interface code
-local function set(t,what,value)
+local function set(t,what,newvalue)
local data, done = t.data, t.done
if type(what) == "string" then
what = aux.settings_to_hash(what) -- inefficient but ok
end
- for w, v in next, what do
- if v == "" then
- v = value
+ for w, value in next, what do
+ if value == "" then
+ value = newvalue
+ elseif not value then
+ value = false -- catch nil
else
- v = toboolean(v)
+ value = is_boolean(value,value)
end
- for d, f in next, data do
- if done[d] then
+ for name, functions in next, data do
+ if done[name] then
-- prevent recursion due to wildcards
- elseif find(d,w) then
- done[d] = true
- for i=1,#f do
- f[i](v)
+ elseif find(name,w) then
+ done[name] = true
+ for i=1,#functions do
+ functions[i](value)
end
+ functions.value = value
end
end
end
end
local function reset(t)
- for d, f in next, t.data do
- for i=1,#f do
- f[i](false)
+ for name, functions in next, t.data do
+ for i=1,#functions do
+ functions[i](false)
end
+ functions.value = false
end
end
@@ -3802,17 +3952,26 @@ end
function setters.register(t,what,...)
local data = t.data
what = lower(what)
- local w = data[what]
- if not w then
- w = { }
- data[what] = w
+ local functions = data[what]
+ if not functions then
+ functions = { }
+ data[what] = functions
end
+ local default = functions.default -- can be set from cnf file
for _, fnc in next, { ... } do
local typ = type(fnc)
- if typ == "function" then
- w[#w+1] = fnc
- elseif typ == "string" then
- w[#w+1] = function(value) set(t,fnc,value,nesting) end
+ if typ == "string" then
+ local s = fnc -- else wrong reference
+ fnc = function(value) set(t,s,value) end
+ elseif typ ~= "function" then
+ fnc = nil
+ end
+ if fnc then
+ functions[#functions+1] = fnc
+ if default then
+ fnc(default)
+ functions.value = default
+ end
end
end
end
@@ -3853,8 +4012,16 @@ end
function setters.show(t)
commands.writestatus("","")
local list = setters.list(t)
+ local category = t.name
for k=1,#list do
- commands.writestatus(t.name,list[k])
+ local name = list[k]
+ local functions = t.data[name]
+ if functions then
+ local value, default, modules = functions.value, functions.default, #functions
+ value = value == nil and "unset" or tostring(value)
+ default = default == nil and "unset" or tostring(default)
+ commands.writestatus(category,format("%-25s modules: %2i default: %5s value: %5s",name,modules,default,value))
+ end
end
commands.writestatus("","")
end
@@ -3867,7 +4034,7 @@ end
function setters.new(name)
local t
t = {
- data = { },
+ data = { }, -- indexed, but also default and value fields
name = name,
enable = function(...) setters.enable (t,...) end,
disable = function(...) setters.disable (t,...) end,
@@ -3875,7 +4042,7 @@ function setters.new(name)
list = function(...) setters.list (t,...) end,
show = function(...) setters.show (t,...) end,
}
- setters.data[name] = t
+ data[name] = t
return t
end
@@ -3893,12 +4060,12 @@ local e = directives.enable
local d = directives.disable
function directives.enable(...)
- commands.writestatus("directives","enabling: %s",concat({...}," "))
+ (commands.writestatus or logs.report)("directives","enabling: %s",concat({...}," "))
e(...)
end
function directives.disable(...)
- commands.writestatus("directives","disabling: %s",concat({...}," "))
+ (commands.writestatus or logs.report)("directives","disabling: %s",concat({...}," "))
d(...)
end
@@ -3906,12 +4073,12 @@ local e = experiments.enable
local d = experiments.disable
function experiments.enable(...)
- commands.writestatus("experiments","enabling: %s",concat({...}," "))
+ (commands.writestatus or logs.report)("experiments","enabling: %s",concat({...}," "))
e(...)
end
function experiments.disable(...)
- commands.writestatus("experiments","disabling: %s",concat({...}," "))
+ (commands.writestatus or logs.report)("experiments","disabling: %s",concat({...}," "))
d(...)
end
@@ -3922,6 +4089,946 @@ directives.register("system.nostatistics", function(v)
end)
+end -- of closure
+
+do -- create closure to overcome 200 locals limit
+
+if not modules then modules = { } end modules ['trac-tra'] = {
+ version = 1.001,
+ comment = "companion to trac-tra.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- the <anonymous> tag is kind of generic and used for functions that are not
+-- bound to a variable, like node.new, node.copy etc (contrary to for instance
+-- node.has_attribute which is bound to a has_attribute local variable in mkiv)
+
+local debug = require "debug"
+
+local getinfo = debug.getinfo
+local type, next = type, next
+local format, find = string.format, string.find
+local is_boolean = string.is_boolean
+
+debugger = debugger or { }
+
+local counters = { }
+local names = { }
+
+-- one
+
+local function hook()
+ local f = getinfo(2,"f").func
+ local n = getinfo(2,"Sn")
+-- if n.what == "C" and n.name then print (n.namewhat .. ': ' .. n.name) end
+ if f then
+ local cf = counters[f]
+ if cf == nil then
+ counters[f] = 1
+ names[f] = n
+ else
+ counters[f] = cf + 1
+ end
+ end
+end
+
+local function getname(func)
+ local n = names[func]
+ if n then
+ if n.what == "C" then
+ return n.name or '<anonymous>'
+ else
+ -- source short_src linedefined what name namewhat nups func
+ local name = n.name or n.namewhat or n.what
+ if not name or name == "" then name = "?" end
+ return format("%s : %s : %s", n.short_src or "unknown source", n.linedefined or "--", name)
+ end
+ else
+ return "unknown"
+ end
+end
+
+function debugger.showstats(printer,threshold)
+ printer = printer or texio.write or print
+ threshold = threshold or 0
+ local total, grandtotal, functions = 0, 0, 0
+ printer("\n") -- ugly but ok
+ -- table.sort(counters)
+ for func, count in next, counters do
+ if count > threshold then
+ local name = getname(func)
+ if not find(name,"for generator") then
+ printer(format("%8i %s", count, name))
+ total = total + count
+ end
+ end
+ grandtotal = grandtotal + count
+ functions = functions + 1
+ end
+ printer(format("functions: %s, total: %s, grand total: %s, threshold: %s\n", functions, total, grandtotal, threshold))
+end
+
+-- two
+
+
+-- rest
+
+function debugger.savestats(filename,threshold)
+ local f = io.open(filename,'w')
+ if f then
+ debugger.showstats(function(str) f:write(str) end,threshold)
+ f:close()
+ end
+end
+
+function debugger.enable()
+ debug.sethook(hook,"c")
+end
+
+function debugger.disable()
+ debug.sethook()
+end
+
+local function trace_calls(n)
+ debugger.enable()
+ luatex.register_stop_actions(function()
+ debugger.disable()
+ debugger.savestats(tex.jobname .. "-luacalls.log",tonumber(n))
+ end)
+ trace_calls = function() end
+end
+
+if directives then
+ directives.register("system.tracecalls", function(n) trace_calls(n) end) -- indirect is needed for nilling
+end
+
+
+
+
+
+
+end -- of closure
+
+do -- create closure to overcome 200 locals limit
+
+if not modules then modules = { } end modules ['trac-log'] = {
+ version = 1.001,
+ comment = "companion to trac-log.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- xml logging is only usefull in normal runs, not in ini mode
+-- it looks like some tex logging (like filenames) is broken (no longer
+-- interceoted at the tex end so the xml variant is not that useable now)
+
+
+local write_nl, write = texio and texio.write_nl or print, texio and texio.write or io.write
+local format, gmatch = string.format, string.gmatch
+local texcount = tex and tex.count
+
+--[[ldx--
+<p>This is a prelude to a more extensive logging module. For the sake
+of parsing log files, in addition to the standard logging we will
+provide an <l n='xml'/> structured file. Actually, any logging that
+is hooked into callbacks will be \XML\ by default.</p>
+--ldx]]--
+
+logs = logs or { }
+
+--[[ldx--
+<p>This looks pretty ugly but we need to speed things up a bit.</p>
+--ldx]]--
+
+local moreinfo = [[
+More information about ConTeXt and the tools that come with it can be found at:
+
+maillist : ntg-context@ntg.nl / http://www.ntg.nl/mailman/listinfo/ntg-context
+webpage : http://www.pragma-ade.nl / http://tex.aanhet.net
+wiki : http://contextgarden.net
+]]
+
+local functions = {
+ 'report', 'status', 'start', 'stop', 'push', 'pop', 'line', 'direct',
+ 'start_run', 'stop_run',
+ 'start_page_number', 'stop_page_number',
+ 'report_output_pages', 'report_output_log',
+ 'report_tex_stat', 'report_job_stat',
+ 'show_open', 'show_close', 'show_load',
+ 'dummy',
+}
+
+local method = "nop"
+
+function logs.set_method(newmethod)
+ method = newmethod
+ -- a direct copy might be faster but let's try this for a while
+ setmetatable(logs, { __index = logs[method] })
+end
+
+function logs.get_method()
+ return method
+end
+
+-- installer
+
+local data = { }
+
+function logs.new(category)
+ local logger = data[category]
+ if not logger then
+ logger = function(...)
+ logs.report(category,...)
+ end
+ data[category] = logger
+ end
+ return logger
+end
+
+
+
+-- nop logging (maybe use __call instead)
+
+local noplog = { } logs.nop = noplog setmetatable(logs, { __index = noplog })
+
+for i=1,#functions do
+ noplog[functions[i]] = function() end
+end
+
+-- tex logging
+
+local texlog = { } logs.tex = texlog setmetatable(texlog, { __index = noplog })
+
+function texlog.report(a,b,c,...)
+ if c then
+ write_nl(format("%-16s> %s\n",a,format(b,c,...)))
+ elseif b then
+ write_nl(format("%-16s> %s\n",a,b))
+ else
+ write_nl(format("%-16s>\n",a))
+ end
+end
+
+function texlog.status(a,b,c,...)
+ if c then
+ write_nl(format("%-16s: %s\n",a,format(b,c,...)))
+ elseif b then
+ write_nl(format("%-16s: %s\n",a,b)) -- b can have %'s
+ else
+ write_nl(format("%-16s:>\n",a))
+ end
+end
+
+function texlog.line(fmt,...) -- new
+ if fmt then
+ write_nl(format(fmt,...))
+ else
+ write_nl("")
+ end
+end
+
+local real, user, sub
+
+function texlog.start_page_number()
+ real, user, sub = texcount.realpageno, texcount.userpageno, texcount.subpageno
+end
+
+local report_pages = logs.new("pages") -- not needed but saves checking when we grep for it
+
+function texlog.stop_page_number()
+ if real > 0 then
+ if user > 0 then
+ if sub > 0 then
+ report_pages("flushing realpage %s, userpage %s, subpage %s",real,user,sub)
+ else
+ report_pages("flushing realpage %s, userpage %s",real,user)
+ end
+ else
+ report_pages("flushing realpage %s",real)
+ end
+ else
+ report_pages("flushing page")
+ end
+ io.flush()
+end
+
+texlog.report_job_stat = statistics and statistics.show_job_stat
+
+-- xml logging
+
+local xmllog = { } logs.xml = xmllog setmetatable(xmllog, { __index = noplog })
+
+function xmllog.report(category,fmt,s,...) -- new
+ if s then
+ write_nl(format("<r category='%s'>%s</r>",category,format(fmt,s,...)))
+ elseif fmt then
+ write_nl(format("<r category='%s'>%s</r>",category,fmt))
+ else
+ write_nl(format("<r category='%s'/>",category))
+ end
+end
+
+function xmllog.status(category,fmt,s,...)
+ if s then
+ write_nl(format("<s category='%s'>%s</r>",category,format(fmt,s,...)))
+ elseif fmt then
+ write_nl(format("<s category='%s'>%s</r>",category,fmt))
+ else
+ write_nl(format("<s category='%s'/>",category))
+ end
+end
+
+function xmllog.line(fmt,...) -- new
+ if fmt then
+ write_nl(format("<r>%s</r>",format(fmt,...)))
+ else
+ write_nl("<r/>")
+ end
+end
+
+function xmllog.start() write_nl("<%s>" ) end
+function xmllog.stop () write_nl("</%s>") end
+function xmllog.push () write_nl("<!-- ") end
+function xmllog.pop () write_nl(" -->" ) end
+
+function xmllog.start_run()
+ write_nl("<?xml version='1.0' standalone='yes'?>")
+ write_nl("<job>") -- xmlns='www.pragma-ade.com/luatex/schemas/context-job.rng'
+ write_nl("")
+end
+
+function xmllog.stop_run()
+ write_nl("</job>")
+end
+
+function xmllog.start_page_number()
+ write_nl(format("<p real='%s' page='%s' sub='%s'", texcount.realpageno, texcount.userpageno, texcount.subpageno))
+end
+
+function xmllog.stop_page_number()
+ write("/>")
+ write_nl("")
+end
+
+function xmllog.report_output_pages(p,b)
+ write_nl(format("<v k='pages' v='%s'/>", p))
+ write_nl(format("<v k='bytes' v='%s'/>", b))
+ write_nl("")
+end
+
+function xmllog.report_output_log()
+ -- nothing
+end
+
+function xmllog.report_tex_stat(k,v)
+ write_nl("log","<v k='"..k.."'>"..tostring(v).."</v>")
+end
+
+local nesting = 0
+
+function xmllog.show_open(name)
+ nesting = nesting + 1
+ write_nl(format("<f l='%s' n='%s'>",nesting,name))
+end
+
+function xmllog.show_close(name)
+ write("</f> ")
+ nesting = nesting - 1
+end
+
+function xmllog.show_load(name)
+ write_nl(format("<f l='%s' n='%s'/>",nesting+1,name))
+end
+
+-- initialization
+
+if tex and (tex.jobname or tex.formatname) then
+ -- todo: this can be set in mtxrun ... or maybe we should just forget about this alternative format
+ if (os.getenv("mtx.directives.logmethod") or os.getenv("mtx_directives_logmethod")) == "xml" then
+ logs.set_method('xml')
+ else
+ logs.set_method('tex')
+ end
+else
+ logs.set_method('nop')
+end
+
+-- logging in runners -> these are actually the nop loggers
+
+local name, banner = 'report', 'context'
+
+function noplog.report(category,fmt,...) -- todo: fmt,s
+ if fmt then
+ write_nl(format("%s | %s: %s",name,category,format(fmt,...)))
+ elseif category then
+ write_nl(format("%s | %s",name,category))
+ else
+ write_nl(format("%s |",name))
+ end
+end
+
+noplog.status = noplog.report -- just to be sure, never used
+
+function noplog.simple(fmt,...) -- todo: fmt,s
+ if fmt then
+ write_nl(format("%s | %s",name,format(fmt,...)))
+ else
+ write_nl(format("%s |",name))
+ end
+end
+
+if utils then
+ utils.report = function(...) logs.simple(...) end
+end
+
+function logs.setprogram(newname,newbanner)
+ name, banner = newname, newbanner
+end
+
+function logs.extendbanner(newbanner)
+ banner = banner .. " | ".. newbanner
+end
+
+function logs.reportlines(str) -- todo: <lines></lines>
+ for line in gmatch(str,"(.-)[\n\r]") do
+ logs.report(line)
+ end
+end
+
+function logs.reportline() -- for scripts too
+ logs.report()
+end
+
+function logs.simpleline()
+ logs.report()
+end
+
+function logs.simplelines(str) -- todo: <lines></lines>
+ for line in gmatch(str,"(.-)[\n\r]") do
+ logs.simple(line)
+ end
+end
+
+function logs.reportbanner() -- for scripts too
+ logs.report(banner)
+end
+
+function logs.help(message,option)
+ logs.reportbanner()
+ logs.reportline()
+ logs.reportlines(message)
+ if option ~= "nomoreinfo" then
+ logs.reportline()
+ logs.reportlines(moreinfo)
+ end
+end
+
+-- logging to a file
+
+
+function logs.system(whereto,process,jobname,category,...)
+ local message = format("%s %s => %s => %s => %s\r",os.date("%d/%m/%y %H:%m:%S"),process,jobname,category,format(...))
+ for i=1,10 do
+ local f = io.open(whereto,"a")
+ if f then
+ f:write(message)
+ f:close()
+ break
+ else
+ sleep(0.1)
+ end
+ end
+end
+
+-- bonus
+
+function logs.fatal(where,...)
+ logs.report(where,"fatal error: %s, aborting now",format(...))
+ os.exit()
+end
+
+
+
+end -- of closure
+
+do -- create closure to overcome 200 locals limit
+
+if not modules then modules = { } end modules ['trac-pro'] = {
+ version = 1.001,
+ comment = "companion to luat-lib.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local getmetatable, setmetatable, rawset, type = getmetatable, setmetatable, rawset, type
+
+-- The protection implemented here is probably not that tight but good enough to catch
+-- problems due to naive usage.
+--
+-- There's a more extensive version (trac-xxx.lua) that supports nesting.
+--
+-- This will change when we have _ENV in lua 5.2+
+
+local trace_namespaces = false trackers.register("system.namespaces", function(v) trace_namespaces = v end)
+
+local report_system = logs.new("system")
+
+namespaces = { }
+
+local registered = { }
+
+local function report_index(k,name)
+ if trace_namespaces then
+ report_system("reference to '%s' in protected namespace '%s', %s",k,name,debug.traceback())
+ else
+ report_system("reference to '%s' in protected namespace '%s'",k,name)
+ end
+end
+
+local function report_newindex(k,name)
+ if trace_namespaces then
+ report_system("assignment to '%s' in protected namespace '%s', %s",k,name,debug.traceback())
+ else
+ report_system("assignment to '%s' in protected namespace '%s'",k,name)
+ end
+end
+
+local function register(name)
+ local data = name == "global" and _G or _G[name]
+ if not data then
+ return -- error
+ end
+ registered[name] = data
+ local m = getmetatable(data)
+ if not m then
+ m = { }
+ setmetatable(data,m)
+ end
+ local index, newindex = { }, { }
+ m.__saved__index = m.__index
+ m.__no__index = function(t,k)
+ if not index[k] then
+ index[k] = true
+ report_index(k,name)
+ end
+ return nil
+ end
+ m.__saved__newindex = m.__newindex
+ m.__no__newindex = function(t,k,v)
+ if not newindex[k] then
+ newindex[k] = true
+ report_newindex(k,name)
+ end
+ rawset(t,k,v)
+ end
+ m.__protection__depth = 0
+end
+
+local function private(name) -- maybe save name
+ local data = registered[name]
+ if not data then
+ data = _G[name]
+ if not data then
+ data = { }
+ _G[name] = data
+ end
+ register(name)
+ end
+ return data
+end
+
+local function protect(name)
+ local data = registered[name]
+ if not data then
+ return
+ end
+ local m = getmetatable(data)
+ local pd = m.__protection__depth
+ if pd > 0 then
+ m.__protection__depth = pd + 1
+ else
+ m.__save_d_index, m.__saved__newindex = m.__index, m.__newindex
+ m.__index, m.__newindex = m.__no__index, m.__no__newindex
+ m.__protection__depth = 1
+ end
+end
+
+local function unprotect(name)
+ local data = registered[name]
+ if not data then
+ return
+ end
+ local m = getmetatable(data)
+ local pd = m.__protection__depth
+ if pd > 1 then
+ m.__protection__depth = pd - 1
+ else
+ m.__index, m.__newindex = m.__saved__index, m.__saved__newindex
+ m.__protection__depth = 0
+ end
+end
+
+local function protectall()
+ for name, _ in next, registered do
+ if name ~= "global" then
+ protect(name)
+ end
+ end
+end
+
+local function unprotectall()
+ for name, _ in next, registered do
+ if name ~= "global" then
+ unprotect(name)
+ end
+ end
+end
+
+namespaces.register = register -- register when defined
+namespaces.private = private -- allocate and register if needed
+namespaces.protect = protect
+namespaces.unprotect = unprotect
+namespaces.protectall = protectall
+namespaces.unprotectall = unprotectall
+
+namespaces.private("namespaces") registered = { } register("global") -- unreachable
+
+directives.register("system.protect", function(v)
+ if v then
+ protectall()
+ else
+ unprotectall()
+ end
+end)
+
+directives.register("system.checkglobals", function(v)
+ if v then
+ report_system("enabling global namespace guard")
+ protect("global")
+ else
+ report_system("disabling global namespace guard")
+ unprotect("global")
+ end
+end)
+
+-- dummy section (will go to luat-dum.lua)
+
+
+
+
+
+
+
+end -- of closure
+
+do -- create closure to overcome 200 locals limit
+
+if not modules then modules = { } end modules ['luat-env'] = {
+ version = 1.001,
+ comment = "companion to luat-lib.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- A former version provided functionality for non embeded core
+-- scripts i.e. runtime library loading. Given the amount of
+-- Lua code we use now, this no longer makes sense. Much of this
+-- evolved before bytecode arrays were available and so a lot of
+-- code has disappeared already.
+
+local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
+
+local report_resolvers = logs.new("resolvers")
+
+local format, sub, match, gsub, find = string.format, string.sub, string.match, string.gsub, string.find
+local unquote, quote = string.unquote, string.quote
+
+-- precautions
+
+os.setlocale(nil,nil) -- useless feature and even dangerous in luatex
+
+function os.setlocale()
+ -- no way you can mess with it
+end
+
+-- dirty tricks
+
+if arg and (arg[0] == 'luatex' or arg[0] == 'luatex.exe') and arg[1] == "--luaonly" then
+ arg[-1] = arg[0]
+ arg[ 0] = arg[2]
+ for k=3,#arg do
+ arg[k-2] = arg[k]
+ end
+ arg[#arg] = nil -- last
+ arg[#arg] = nil -- pre-last
+end
+
+-- environment
+
+environment = environment or { }
+environment.arguments = { }
+environment.files = { }
+environment.sortedflags = nil
+
+local mt = {
+ __index = function(_,k)
+ if k == "version" then
+ local version = tex.toks and tex.toks.contextversiontoks
+ if version and version ~= "" then
+ rawset(environment,"version",version)
+ return version
+ else
+ return "unknown"
+ end
+ elseif k == "jobname" or k == "formatname" then
+ local name = tex and tex[k]
+ if name or name== "" then
+ rawset(environment,k,name)
+ return name
+ else
+ return "unknown"
+ end
+ elseif k == "outputfilename" then
+ local name = environment.jobname
+ rawset(environment,k,name)
+ return name
+ end
+ end
+}
+
+setmetatable(environment,mt)
+
+function environment.initialize_arguments(arg)
+ local arguments, files = { }, { }
+ environment.arguments, environment.files, environment.sortedflags = arguments, files, nil
+ for index=1,#arg do
+ local argument = arg[index]
+ if index > 0 then
+ local flag, value = match(argument,"^%-+(.-)=(.-)$")
+ if flag then
+ arguments[flag] = unquote(value or "")
+ else
+ flag = match(argument,"^%-+(.+)")
+ if flag then
+ arguments[flag] = true
+ else
+ files[#files+1] = argument
+ end
+ end
+ end
+ end
+ environment.ownname = environment.ownname or arg[0] or 'unknown.lua'
+end
+
+function environment.setargument(name,value)
+ environment.arguments[name] = value
+end
+
+-- todo: defaults, better checks e.g on type (boolean versus string)
+--
+-- tricky: too many hits when we support partials unless we add
+-- a registration of arguments so from now on we have 'partial'
+
+function environment.argument(name,partial)
+ local arguments, sortedflags = environment.arguments, environment.sortedflags
+ if arguments[name] then
+ return arguments[name]
+ elseif partial then
+ if not sortedflags then
+ sortedflags = table.sortedkeys(arguments)
+ for k=1,#sortedflags do
+ sortedflags[k] = "^" .. sortedflags[k]
+ end
+ environment.sortedflags = sortedflags
+ end
+ -- example of potential clash: ^mode ^modefile
+ for k=1,#sortedflags do
+ local v = sortedflags[k]
+ if find(name,v) then
+ return arguments[sub(v,2,#v)]
+ end
+ end
+ end
+ return nil
+end
+
+function environment.split_arguments(separator) -- rather special, cut-off before separator
+ local done, before, after = false, { }, { }
+ local original_arguments = environment.original_arguments
+ for k=1,#original_arguments do
+ local v = original_arguments[k]
+ if not done and v == separator then
+ done = true
+ elseif done then
+ after[#after+1] = v
+ else
+ before[#before+1] = v
+ end
+ end
+ return before, after
+end
+
+function environment.reconstruct_commandline(arg,noquote)
+ arg = arg or environment.original_arguments
+ if noquote and #arg == 1 then
+ local a = arg[1]
+ a = resolvers.resolve(a)
+ a = unquote(a)
+ return a
+ elseif #arg > 0 then
+ local result = { }
+ for i=1,#arg do
+ local a = arg[i]
+ a = resolvers.resolve(a)
+ a = unquote(a)
+ a = gsub(a,'"','\\"') -- tricky
+ if find(a," ") then
+ result[#result+1] = quote(a)
+ else
+ result[#result+1] = a
+ end
+ end
+ return table.join(result," ")
+ else
+ return ""
+ end
+end
+
+if arg then
+
+ -- new, reconstruct quoted snippets (maybe better just remove the " then and add them later)
+ local newarg, instring = { }, false
+
+ for index=1,#arg do
+ local argument = arg[index]
+ if find(argument,"^\"") then
+ newarg[#newarg+1] = gsub(argument,"^\"","")
+ if not find(argument,"\"$") then
+ instring = true
+ end
+ elseif find(argument,"\"$") then
+ newarg[#newarg] = newarg[#newarg] .. " " .. gsub(argument,"\"$","")
+ instring = false
+ elseif instring then
+ newarg[#newarg] = newarg[#newarg] .. " " .. argument
+ else
+ newarg[#newarg+1] = argument
+ end
+ end
+ for i=1,-5,-1 do
+ newarg[i] = arg[i]
+ end
+
+ environment.initialize_arguments(newarg)
+ environment.original_arguments = newarg
+ environment.raw_arguments = arg
+
+ arg = { } -- prevent duplicate handling
+
+end
+
+-- weird place ... depends on a not yet loaded module
+
+function environment.texfile(filename)
+ return resolvers.find_file(filename,'tex')
+end
+
+function environment.luafile(filename)
+ local resolved = resolvers.find_file(filename,'tex') or ""
+ if resolved ~= "" then
+ return resolved
+ end
+ resolved = resolvers.find_file(filename,'texmfscripts') or ""
+ if resolved ~= "" then
+ return resolved
+ end
+ return resolvers.find_file(filename,'luatexlibs') or ""
+end
+
+environment.loadedluacode = loadfile -- can be overloaded
+
+function environment.luafilechunk(filename,silent) -- used for loading lua bytecode in the format
+ filename = file.replacesuffix(filename, "lua")
+ local fullname = environment.luafile(filename)
+ if fullname and fullname ~= "" then
+ local data = environment.loadedluacode(fullname)
+ if trace_locating then
+ report_resolvers("loading file %s%s", fullname, not data and " failed" or "")
+ elseif not silent then
+ texio.write("<",data and "+ " or "- ",fullname,">")
+ end
+ return data
+ else
+ if trace_locating then
+ report_resolvers("unknown file %s", filename)
+ end
+ return nil
+ end
+end
+
+-- the next ones can use the previous ones / combine
+
+function environment.loadluafile(filename, version)
+ local lucname, luaname, chunk
+ local basename = file.removesuffix(filename)
+ if basename == filename then
+ lucname, luaname = basename .. ".luc", basename .. ".lua"
+ else
+ lucname, luaname = nil, basename -- forced suffix
+ end
+ -- when not overloaded by explicit suffix we look for a luc file first
+ local fullname = (lucname and environment.luafile(lucname)) or ""
+ if fullname ~= "" then
+ if trace_locating then
+ report_resolvers("loading %s", fullname)
+ end
+ chunk = loadfile(fullname) -- this way we don't need a file exists check
+ end
+ if chunk then
+ assert(chunk)()
+ if version then
+ -- we check of the version number of this chunk matches
+ local v = version -- can be nil
+ if modules and modules[filename] then
+ v = modules[filename].version -- new method
+ elseif versions and versions[filename] then
+ v = versions[filename] -- old method
+ end
+ if v == version then
+ return true
+ else
+ if trace_locating then
+ report_resolvers("version mismatch for %s: lua=%s, luc=%s", filename, v, version)
+ end
+ environment.loadluafile(filename)
+ end
+ else
+ return true
+ end
+ end
+ fullname = (luaname and environment.luafile(luaname)) or ""
+ if fullname ~= "" then
+ if trace_locating then
+ report_resolvers("loading %s", fullname)
+ end
+ chunk = loadfile(fullname) -- this way we don't need a file exists check
+ if not chunk then
+ if trace_locating then
+ report_resolvers("unknown file %s", filename)
+ end
+ else
+ assert(chunk)()
+ return true
+ end
+ end
+ return false
+end
+
end -- of closure
@@ -3941,6 +5048,8 @@ if not modules then modules = { } end modules ['lxml-tab'] = {
local trace_entities = false trackers.register("xml.entities", function(v) trace_entities = v end)
+local report_xml = logs.new("xml")
+
--[[ldx--
<p>The parser used here is inspired by the variant discussed in the lua book, but
handles comment and processing instructions, has a different structure, provides
@@ -3955,7 +5064,6 @@ optimize the code.</p>
xml = xml or { }
---~ local xml = xml
local concat, remove, insert = table.concat, table.remove, table.insert
local type, next, setmetatable, getmetatable, tonumber = type, next, setmetatable, getmetatable, tonumber
@@ -4079,7 +5187,7 @@ local dcache, hcache, acache = { }, { }, { }
local mt = { }
-function initialize_mt(root)
+local function initialize_mt(root)
mt = { __index = root } -- will be redefined later
end
@@ -4183,7 +5291,7 @@ local reported_attribute_errors = { }
local function attribute_value_error(str)
if not reported_attribute_errors[str] then
- logs.report("xml","invalid attribute value: %q",str)
+ report_xml("invalid attribute value: %q",str)
reported_attribute_errors[str] = true
at._error_ = str
end
@@ -4191,7 +5299,7 @@ local function attribute_value_error(str)
end
local function attribute_specification_error(str)
if not reported_attribute_errors[str] then
- logs.report("xml","invalid attribute specification: %q",str)
+ report_xml("invalid attribute specification: %q",str)
reported_attribute_errors[str] = true
at._error_ = str
end
@@ -4254,18 +5362,18 @@ local function handle_hex_entity(str)
h = unify_predefined and predefined_unified[n]
if h then
if trace_entities then
- logs.report("xml","utfize, converting hex entity &#x%s; into %s",str,h)
+ report_xml("utfize, converting hex entity &#x%s; into %s",str,h)
end
elseif utfize then
h = (n and utfchar(n)) or xml.unknown_hex_entity_format(str) or ""
if not n then
- logs.report("xml","utfize, ignoring hex entity &#x%s;",str)
+ report_xml("utfize, ignoring hex entity &#x%s;",str)
elseif trace_entities then
- logs.report("xml","utfize, converting hex entity &#x%s; into %s",str,h)
+ report_xml("utfize, converting hex entity &#x%s; into %s",str,h)
end
else
if trace_entities then
- logs.report("xml","found entity &#x%s;",str)
+ report_xml("found entity &#x%s;",str)
end
h = "&#x" .. str .. ";"
end
@@ -4281,18 +5389,18 @@ local function handle_dec_entity(str)
d = unify_predefined and predefined_unified[n]
if d then
if trace_entities then
- logs.report("xml","utfize, converting dec entity &#%s; into %s",str,d)
+ report_xml("utfize, converting dec entity &#%s; into %s",str,d)
end
elseif utfize then
d = (n and utfchar(n)) or xml.unknown_dec_entity_format(str) or ""
if not n then
- logs.report("xml","utfize, ignoring dec entity &#%s;",str)
+ report_xml("utfize, ignoring dec entity &#%s;",str)
elseif trace_entities then
- logs.report("xml","utfize, converting dec entity &#%s; into %s",str,h)
+ report_xml("utfize, converting dec entity &#%s; into %s",str,h)
end
else
if trace_entities then
- logs.report("xml","found entity &#%s;",str)
+ report_xml("found entity &#%s;",str)
end
d = "&#" .. str .. ";"
end
@@ -4317,7 +5425,7 @@ local function handle_any_entity(str)
end
if a then
if trace_entities then
- logs.report("xml","resolved entity &%s; -> %s (internal)",str,a)
+ report_xml("resolved entity &%s; -> %s (internal)",str,a)
end
a = lpegmatch(parsedentity,a) or a
else
@@ -4326,11 +5434,11 @@ local function handle_any_entity(str)
end
if a then
if trace_entities then
- logs.report("xml","resolved entity &%s; -> %s (external)",str,a)
+ report_xml("resolved entity &%s; -> %s (external)",str,a)
end
else
if trace_entities then
- logs.report("xml","keeping entity &%s;",str)
+ report_xml("keeping entity &%s;",str)
end
if str == "" then
a = "&error;"
@@ -4342,7 +5450,7 @@ local function handle_any_entity(str)
acache[str] = a
elseif trace_entities then
if not acache[str] then
- logs.report("xml","converting entity &%s; into %s",str,a)
+ report_xml("converting entity &%s; into %s",str,a)
acache[str] = a
end
end
@@ -4351,7 +5459,7 @@ local function handle_any_entity(str)
local a = acache[str]
if not a then
if trace_entities then
- logs.report("xml","found entity &%s;",str)
+ report_xml("found entity &%s;",str)
end
a = resolve_predefined and predefined_simplified[str]
if a then
@@ -4370,7 +5478,7 @@ local function handle_any_entity(str)
end
local function handle_end_entity(chr)
- logs.report("xml","error in entity, %q found instead of ';'",chr)
+ report_xml("error in entity, %q found instead of ';'",chr)
end
local space = S(' \r\n\t')
@@ -4505,7 +5613,7 @@ local function xmlconvert(data, settings)
resolve_predefined = settings.resolve_predefined_entities -- in case we have escaped entities
unify_predefined = settings.unify_predefined_entities -- &#038; -> &amp;
cleanup = settings.text_cleanup
- stack, top, at, xmlns, errorstr, result, entities = { }, { }, { }, { }, nil, nil, settings.entities or { }
+ stack, top, at, xmlns, errorstr, entities = { }, { }, { }, { }, nil, settings.entities or { }
acache, hcache, dcache = { }, { }, { } -- not stored
reported_attribute_errors = { }
if settings.parent_root then
@@ -4533,6 +5641,7 @@ local function xmlconvert(data, settings)
else
errorstr = "invalid xml file - no text at all"
end
+ local result
if errorstr and errorstr ~= "" then
result = { dt = { { ns = "", tg = "error", dt = { errorstr }, at={ }, er = true } } }
setmetatable(stack, mt)
@@ -4713,7 +5822,7 @@ local function verbose_element(e,handlers)
ats[#ats+1] = format('%s=%q',k,v)
end
end
- if ern and trace_remap and ern ~= ens then
+ if ern and trace_entities and ern ~= ens then
ens = ern
end
if ens ~= "" then
@@ -4844,7 +5953,7 @@ local function newhandlers(settings)
if settings then
for k,v in next, settings do
if type(v) == "table" then
- tk = t[k] if not tk then tk = { } t[k] = tk end
+ local tk = t[k] if not tk then tk = { } t[k] = tk end
for kk,vv in next, v do
tk[kk] = vv
end
@@ -4955,7 +6064,7 @@ local function xmltext(root) -- inline
return (root and xmltostring(root)) or ""
end
-function initialize_mt(root)
+initialize_mt = function(root) -- redefinition
mt = { __tostring = xmltext, __index = root }
end
@@ -4990,7 +6099,6 @@ xml.string = xmlstring
<p>A few helpers:</p>
--ldx]]--
---~ xmlsetproperty(root,"settings",settings)
function xml.settings(e)
while e do
@@ -5152,6 +6260,8 @@ local trace_lpath = false if trackers then trackers.register("xml.path",
local trace_lparse = false if trackers then trackers.register("xml.parse", function(v) trace_lparse = v end) end
local trace_lprofile = false if trackers then trackers.register("xml.profile", function(v) trace_lpath = v trace_lparse = v trace_lprofile = v end) end
+local report_lpath = logs.new("lpath")
+
--[[ldx--
<p>We've now arrived at an interesting part: accessing the tree using a subset
of <l n='xpath'/> and since we're not compatible we call it <l n='lpath'/>. We
@@ -5178,7 +6288,7 @@ local function fallback (t, name)
if fn then
t[name] = fn
else
- logs.report("xml","unknown sub finalizer '%s'",tostring(name))
+ report_lpath("unknown sub finalizer '%s'",tostring(name))
fn = function() end
end
return fn
@@ -5239,11 +6349,6 @@ apply_axis['root'] = function(list)
end
apply_axis['self'] = function(list)
---~ local collected = { }
---~ for l=1,#list do
---~ collected[#collected+1] = list[l]
---~ end
---~ return collected
return list
end
@@ -5370,38 +6475,10 @@ apply_axis['namespace'] = function(list)
end
apply_axis['following'] = function(list) -- incomplete
---~ local collected = { }
---~ for l=1,#list do
---~ local ll = list[l]
---~ local p = ll.__p__
---~ local d = p.dt
---~ for i=ll.ni+1,#d do
---~ local di = d[i]
---~ if type(di) == "table" then
---~ collected[#collected+1] = di
---~ break
---~ end
---~ end
---~ end
---~ return collected
return { }
end
apply_axis['preceding'] = function(list) -- incomplete
---~ local collected = { }
---~ for l=1,#list do
---~ local ll = list[l]
---~ local p = ll.__p__
---~ local d = p.dt
---~ for i=ll.ni-1,1,-1 do
---~ local di = d[i]
---~ if type(di) == "table" then
---~ collected[#collected+1] = di
---~ break
---~ end
---~ end
---~ end
---~ return collected
return { }
end
@@ -5664,14 +6741,12 @@ local converter = Cs (
)
cleaner = Cs ( (
---~ lp_fastpos +
lp_reserved +
lp_number +
lp_string +
1 )^1 )
---~ expr
local template_e = [[
local expr = xml.expressions
@@ -5722,13 +6797,13 @@ local skip = { }
local function errorrunner_e(str,cnv)
if not skip[str] then
- logs.report("lpath","error in expression: %s => %s",str,cnv)
+ report_lpath("error in expression: %s => %s",str,cnv)
skip[str] = cnv or str
end
return false
end
local function errorrunner_f(str,arg)
- logs.report("lpath","error in finalizer: %s(%s)",str,arg or "")
+ report_lpath("error in finalizer: %s(%s)",str,arg or "")
return false
end
@@ -5895,7 +6970,7 @@ local function lshow(parsed)
end
local s = table.serialize_functions -- ugly
table.serialize_functions = false -- ugly
- logs.report("lpath","%s://%s => %s",parsed.protocol or xml.defaultprotocol,parsed.pattern,table.serialize(parsed,false))
+ report_lpath("%s://%s => %s",parsed.protocol or xml.defaultprotocol,parsed.pattern,table.serialize(parsed,false))
table.serialize_functions = s -- ugly
end
@@ -5925,7 +7000,7 @@ parse_pattern = function (pattern) -- the gain of caching is rather minimal
local np = #parsed
if np == 0 then
parsed = { pattern = pattern, register_self, state = "parsing error" }
- logs.report("lpath","parsing error in '%s'",pattern)
+ report_lpath("parsing error in '%s'",pattern)
lshow(parsed)
else
-- we could have done this with a more complex parser but this
@@ -6029,32 +7104,32 @@ local function traced_apply(list,parsed,nofparsed,order)
if trace_lparse then
lshow(parsed)
end
- logs.report("lpath", "collecting : %s",parsed.pattern)
- logs.report("lpath", " root tags : %s",tagstostring(list))
- logs.report("lpath", " order : %s",order or "unset")
+ report_lpath("collecting : %s",parsed.pattern)
+ report_lpath(" root tags : %s",tagstostring(list))
+ report_lpath(" order : %s",order or "unset")
local collected = list
for i=1,nofparsed do
local pi = parsed[i]
local kind = pi.kind
if kind == "axis" then
collected = apply_axis[pi.axis](collected)
- logs.report("lpath", "% 10i : ax : %s",(collected and #collected) or 0,pi.axis)
+ report_lpath("% 10i : ax : %s",(collected and #collected) or 0,pi.axis)
elseif kind == "nodes" then
collected = apply_nodes(collected,pi.nodetest,pi.nodes)
- logs.report("lpath", "% 10i : ns : %s",(collected and #collected) or 0,nodesettostring(pi.nodes,pi.nodetest))
+ report_lpath("% 10i : ns : %s",(collected and #collected) or 0,nodesettostring(pi.nodes,pi.nodetest))
elseif kind == "expression" then
collected = apply_expression(collected,pi.evaluator,order)
- logs.report("lpath", "% 10i : ex : %s -> %s",(collected and #collected) or 0,pi.expression,pi.converted)
+ report_lpath("% 10i : ex : %s -> %s",(collected and #collected) or 0,pi.expression,pi.converted)
elseif kind == "finalizer" then
collected = pi.finalizer(collected)
- logs.report("lpath", "% 10i : fi : %s : %s(%s)",(type(collected) == "table" and #collected) or 0,parsed.protocol or xml.defaultprotocol,pi.name,pi.arguments or "")
+ report_lpath("% 10i : fi : %s : %s(%s)",(type(collected) == "table" and #collected) or 0,parsed.protocol or xml.defaultprotocol,pi.name,pi.arguments or "")
return collected
end
if not collected or #collected == 0 then
local pn = i < nofparsed and parsed[nofparsed]
if pn and pn.kind == "finalizer" then
collected = pn.finalizer(collected)
- logs.report("lpath", "% 10i : fi : %s : %s(%s)",(type(collected) == "table" and #collected) or 0,parsed.protocol or xml.defaultprotocol,pn.name,pn.arguments or "")
+ report_lpath("% 10i : fi : %s : %s(%s)",(type(collected) == "table" and #collected) or 0,parsed.protocol or xml.defaultprotocol,pn.name,pn.arguments or "")
return collected
end
return nil
@@ -6167,7 +7242,7 @@ expressions.boolean = toboolean
-- user interface
local function traverse(root,pattern,handle)
- logs.report("xml","use 'xml.selection' instead for '%s'",pattern)
+ report_lpath("use 'xml.selection' instead for '%s'",pattern)
local collected = parse_apply({ root },pattern)
if collected then
for c=1,#collected do
@@ -6215,7 +7290,7 @@ local function dofunction(collected,fnc)
f(collected[c])
end
else
- logs.report("xml","unknown function '%s'",fnc)
+ report_lpath("unknown function '%s'",fnc)
end
end
end
@@ -6407,7 +7482,6 @@ local function xmlgsub(t,old,new) -- will be replaced
end
end
---~ xml.gsub = xmlgsub
function xml.strip_leading_spaces(dk,d,k) -- cosmetic, for manual
if d and k then
@@ -6419,12 +7493,7 @@ function xml.strip_leading_spaces(dk,d,k) -- cosmetic, for manual
end
end
---~ xml.escapes = { ['&'] = '&amp;', ['<'] = '&lt;', ['>'] = '&gt;', ['"'] = '&quot;' }
---~ xml.unescapes = { } for k,v in next, xml.escapes do xml.unescapes[v] = k end
---~ function xml.escaped (str) return (gsub(str,"(.)" , xml.escapes )) end
---~ function xml.unescaped(str) return (gsub(str,"(&.-;)", xml.unescapes)) end
---~ function xml.cleansed (str) return (gsub(str,"<.->" , '' )) end -- "%b<>"
local P, S, R, C, V, Cc, Cs = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.V, lpeg.Cc, lpeg.Cs
@@ -6490,6 +7559,8 @@ if not modules then modules = { } end modules ['lxml-aux'] = {
local trace_manipulations = false trackers.register("lxml.manipulations", function(v) trace_manipulations = v end)
+local report_xml = logs.new("xml")
+
local xmlparseapply, xmlconvert, xmlcopy, xmlname = xml.parse_apply, xml.convert, xml.copy, xml.name
local xmlinheritedconvert = xml.inheritedconvert
@@ -6498,7 +7569,7 @@ local insert, remove = table.insert, table.remove
local gmatch, gsub = string.gmatch, string.gsub
local function report(what,pattern,c,e)
- logs.report("xml","%s element '%s' (root: '%s', position: %s, index: %s, pattern: %s)",what,xmlname(e),xmlname(e.__p__),c,e.ni,pattern)
+ report_xml("%s element '%s' (root: '%s', position: %s, index: %s, pattern: %s)",what,xmlname(e),xmlname(e.__p__),c,e.ni,pattern)
end
local function withelements(e,handle,depth)
@@ -6651,12 +7722,7 @@ local function xmltoelement(whatever,root)
return whatever -- string
end
if element then
- --~ if element.ri then
- --~ element = element.dt[element.ri].dt
- --~ else
- --~ element = element.dt
- --~ end
- end
+ end
return element
end
@@ -6795,9 +7861,6 @@ local function include(xmldata,pattern,attribute,recursive,loaddata)
-- for the moment hard coded
epdt[ek.ni] = xml.escaped(data) -- d[k] = xml.escaped(data)
else
---~ local settings = xmldata.settings
---~ settings.parent_root = xmldata -- to be tested
---~ local xi = xmlconvert(data,settings)
local xi = xmlinheritedconvert(data,xmldata)
if not xi then
epdt[ek.ni] = "" -- xml.empty(d,k)
@@ -6814,28 +7877,7 @@ end
xml.include = include
---~ local function manipulate(xmldata,pattern,manipulator) -- untested and might go away
---~ local collected = xmlparseapply({ xmldata },pattern)
---~ if collected then
---~ local xmltostring = xml.tostring
---~ for c=1,#collected do
---~ local e = collected[c]
---~ local data = manipulator(xmltostring(e))
---~ if data == "" then
---~ epdt[e.ni] = ""
---~ else
---~ local xi = xmlinheritedconvert(data,xmldata)
---~ if not xi then
---~ epdt[e.ni] = ""
---~ else
---~ epdt[e.ni] = xml.body(xi) -- xml.assign(d,k,xi)
---~ end
---~ end
---~ end
---~ end
---~ end
-
---~ xml.manipulate = manipulate
+
function xml.strip_whitespace(root, pattern, nolines) -- strips all leading and trailing space !
local collected = xmlparseapply({ root },pattern)
@@ -6861,8 +7903,7 @@ function xml.strip_whitespace(root, pattern, nolines) -- strips all leading and
end
end
else
- --~ str.ni = i
- t[#t+1] = str
+ t[#t+1] = str
end
end
e.dt = t
@@ -7320,825 +8361,1137 @@ end -- of closure
do -- create closure to overcome 200 locals limit
-if not modules then modules = { } end modules ['luat-env'] = {
+if not modules then modules = { } end modules ['data-ini'] = {
version = 1.001,
comment = "companion to luat-lib.mkiv",
author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
+ license = "see context related readme files",
}
--- A former version provided functionality for non embeded core
--- scripts i.e. runtime library loading. Given the amount of
--- Lua code we use now, this no longer makes sense. Much of this
--- evolved before bytecode arrays were available and so a lot of
--- code has disappeared already.
-
-local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
-
-local format, sub, match, gsub, find = string.format, string.sub, string.match, string.gsub, string.find
-local unquote, quote = string.unquote, string.quote
+local gsub, find, gmatch = string.gsub, string.find, string.gmatch
+local concat = table.concat
+local next, type = next, type
--- precautions
+local filedirname, filebasename, fileextname, filejoin = file.dirname, file.basename, file.extname, file.join
-os.setlocale(nil,nil) -- useless feature and even dangerous in luatex
+local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
+local trace_detail = false trackers.register("resolvers.details", function(v) trace_detail = v end)
+local trace_expansions = false trackers.register("resolvers.expansions", function(v) trace_expansions = v end)
-function os.setlocale()
- -- no way you can mess with it
-end
+local report_resolvers = logs.new("resolvers")
--- dirty tricks
+local ostype, osname, ossetenv, osgetenv = os.type, os.name, os.setenv, os.getenv
-if arg and (arg[0] == 'luatex' or arg[0] == 'luatex.exe') and arg[1] == "--luaonly" then
- arg[-1]=arg[0] arg[0]=arg[2] for k=3,#arg do arg[k-2]=arg[k] end arg[#arg]=nil arg[#arg]=nil
-end
+-- The code here used to be part of a data-res but for convenience
+-- we now split it over multiple files. As this file is now the
+-- starting point we introduce resolvers here.
-if profiler and os.env["MTX_PROFILE_RUN"] == "YES" then
- profiler.start("luatex-profile.log")
-end
+resolvers = resolvers or { }
--- environment
+-- We don't want the kpse library to kick in. Also, we want to be able to
+-- execute programs. Control over execution is implemented later.
-environment = environment or { }
-environment.arguments = { }
-environment.files = { }
-environment.sortedflags = nil
+texconfig.kpse_init = false
+texconfig.shell_escape = 't'
-if not environment.jobname or environment.jobname == "" then if tex then environment.jobname = tex.jobname end end
-if not environment.version or environment.version == "" then environment.version = "unknown" end
-if not environment.jobname then environment.jobname = "unknown" end
+kpse = { original = kpse }
-function environment.initialize_arguments(arg)
- local arguments, files = { }, { }
- environment.arguments, environment.files, environment.sortedflags = arguments, files, nil
- for index=1,#arg do
- local argument = arg[index]
- if index > 0 then
- local flag, value = match(argument,"^%-+(.-)=(.-)$")
- if flag then
- arguments[flag] = unquote(value or "")
- else
- flag = match(argument,"^%-+(.+)")
- if flag then
- arguments[flag] = true
- else
- files[#files+1] = argument
- end
+setmetatable(kpse, {
+ __index = function(kp,name)
+ local r = resolvers[name]
+ if not r then
+ r = function (...)
+ report_resolvers("not supported: %s(%s)",name,concat(...))
end
+ rawset(kp,name,r)
end
+ return r
end
- environment.ownname = environment.ownname or arg[0] or 'unknown.lua'
+} )
+
+-- First we check a couple of environment variables. Some might be
+-- set already but we need then later on. We start with the system
+-- font path.
+
+do
+
+ local osfontdir = osgetenv("OSFONTDIR")
+
+ if osfontdir and osfontdir ~= "" then
+ -- ok
+ elseif osname == "windows" then
+ ossetenv("OSFONTDIR","c:/windows/fonts//")
+ elseif osname == "macosx" then
+ ossetenv("OSFONTDIR","$HOME/Library/Fonts//;/Library/Fonts//;/System/Library/Fonts//")
+ end
+
end
-function environment.setargument(name,value)
- environment.arguments[name] = value
+-- Next comes the user's home path. We need this as later on we have
+-- to replace ~ with its value.
+
+do
+
+ local homedir = osgetenv(ostype == "windows" and 'USERPROFILE' or 'HOME') or '~'
+
+ homedir = file.collapse_path(homedir)
+
+ ossetenv("HOME", homedir) -- can be used in unix cnf files
+ ossetenv("USERPROFILE",homedir) -- can be used in windows cnf files
+
+ environment.homedir = homedir
+
end
--- todo: defaults, better checks e.g on type (boolean versus string)
---
--- tricky: too many hits when we support partials unless we add
--- a registration of arguments so from now on we have 'partial'
+-- The following code sets the name of the own binary and its
+-- path. This is fallback code as we have os.selfdir now.
-function environment.argument(name,partial)
- local arguments, sortedflags = environment.arguments, environment.sortedflags
- if arguments[name] then
- return arguments[name]
- elseif partial then
- if not sortedflags then
- sortedflags = table.sortedkeys(arguments)
- for k=1,#sortedflags do
- sortedflags[k] = "^" .. sortedflags[k]
- end
- environment.sortedflags = sortedflags
+do
+
+ local args = environment.original_arguments or arg -- this needs a cleanup
+
+ local ownbin = environment.ownbin or args[-2] or arg[-2] or args[-1] or arg[-1] or arg[0] or "luatex"
+ local ownpath = environment.ownpath or os.selfdir
+
+ ownbin = file.collapse_path(ownbin)
+ ownpath = file.collapse_path(ownpath)
+
+ if not ownpath or ownpath == "" or ownpath == "unset" then
+ ownpath = args[-1] or arg[-1]
+ ownpath = ownpath and filedirname(gsub(ownpath,"\\","/"))
+ if not ownpath or ownpath == "" then
+ ownpath = args[-0] or arg[-0]
+ ownpath = ownpath and filedirname(gsub(ownpath,"\\","/"))
end
- -- example of potential clash: ^mode ^modefile
- for k=1,#sortedflags do
- local v = sortedflags[k]
- if find(name,v) then
- return arguments[sub(v,2,#v)]
+ local binary = ownbin
+ if not ownpath or ownpath == "" then
+ ownpath = ownpath and filedirname(binary)
+ end
+ if not ownpath or ownpath == "" then
+ if os.binsuffix ~= "" then
+ binary = file.replacesuffix(binary,os.binsuffix)
+ end
+ local path = osgetenv("PATH")
+ if path then
+ for p in gmatch(path,"[^"..io.pathseparator.."]+") do
+ local b = filejoin(p,binary)
+ if lfs.isfile(b) then
+ -- we assume that after changing to the path the currentdir function
+ -- resolves to the real location and use this side effect here; this
+ -- trick is needed because on the mac installations use symlinks in the
+ -- path instead of real locations
+ local olddir = lfs.currentdir()
+ if lfs.chdir(p) then
+ local pp = lfs.currentdir()
+ if trace_locating and p ~= pp then
+ report_resolvers("following symlink '%s' to '%s'",p,pp)
+ end
+ ownpath = pp
+ lfs.chdir(olddir)
+ else
+ if trace_locating then
+ report_resolvers("unable to check path '%s'",p)
+ end
+ ownpath = p
+ end
+ break
+ end
+ end
end
end
+ if not ownpath or ownpath == "" then
+ ownpath = "."
+ report_resolvers("forcing fallback ownpath .")
+ elseif trace_locating then
+ report_resolvers("using ownpath '%s'",ownpath)
+ end
end
- return nil
+
+ environment.ownbin = ownbin
+ environment.ownpath = ownpath
+
end
-environment.argument("x",true)
+resolvers.ownpath = environment.ownpath
-function environment.split_arguments(separator) -- rather special, cut-off before separator
- local done, before, after = false, { }, { }
- local original_arguments = environment.original_arguments
- for k=1,#original_arguments do
- local v = original_arguments[k]
- if not done and v == separator then
- done = true
- elseif done then
- after[#after+1] = v
- else
- before[#before+1] = v
- end
- end
- return before, after
+function resolvers.getownpath()
+ return environment.ownpath
end
-function environment.reconstruct_commandline(arg,noquote)
- arg = arg or environment.original_arguments
- if noquote and #arg == 1 then
- local a = arg[1]
- a = resolvers.resolve(a)
- a = unquote(a)
- return a
- elseif #arg > 0 then
- local result = { }
- for i=1,#arg do
- local a = arg[i]
- a = resolvers.resolve(a)
- a = unquote(a)
- a = gsub(a,'"','\\"') -- tricky
- if find(a," ") then
- result[#result+1] = quote(a)
- else
- result[#result+1] = a
- end
- end
- return table.join(result," ")
+-- The self variables permit us to use only a few (or even no)
+-- environment variables.
+
+do
+
+ local ownpath = environment.ownpath or dir.current()
+
+ if ownpath then
+ ossetenv('SELFAUTOLOC', file.collapse_path(ownpath))
+ ossetenv('SELFAUTODIR', file.collapse_path(ownpath .. "/.."))
+ ossetenv('SELFAUTOPARENT', file.collapse_path(ownpath .. "/../.."))
else
- return ""
+ report_resolvers("error: unable to locate ownpath")
+ os.exit()
end
+
end
-if arg then
+-- The running os:
- -- new, reconstruct quoted snippets (maybe better just remove the " then and add them later)
- local newarg, instring = { }, false
+-- todo: check is context sits here os.platform is more trustworthy
+-- that the bin check as mtx-update runs from another path
- for index=1,#arg do
- local argument = arg[index]
- if find(argument,"^\"") then
- newarg[#newarg+1] = gsub(argument,"^\"","")
- if not find(argument,"\"$") then
- instring = true
- end
- elseif find(argument,"\"$") then
- newarg[#newarg] = newarg[#newarg] .. " " .. gsub(argument,"\"$","")
- instring = false
- elseif instring then
- newarg[#newarg] = newarg[#newarg] .. " " .. argument
- else
- newarg[#newarg+1] = argument
- end
- end
- for i=1,-5,-1 do
- newarg[i] = arg[i]
- end
+local texos = environment.texos or osgetenv("TEXOS")
+local texmfos = environment.texmfos or osgetenv('SELFAUTODIR')
- environment.initialize_arguments(newarg)
- environment.original_arguments = newarg
- environment.raw_arguments = arg
+if not texos or texos == "" then
+ texos = file.basename(texmfos)
+end
- arg = { } -- prevent duplicate handling
+ossetenv('TEXMFOS', texmfos) -- full bin path
+ossetenv('TEXOS', texos) -- partial bin parent
+ossetenv('SELFAUTOSYSTEM',os.platform) -- bonus
-end
+environment.texos = texos
+environment.texmfos = texmfos
--- weird place ... depends on a not yet loaded module
+-- The current root:
-function environment.texfile(filename)
- return resolvers.find_file(filename,'tex')
-end
+local texroot = environment.texroot or osgetenv("TEXROOT")
-function environment.luafile(filename)
- local resolved = resolvers.find_file(filename,'tex') or ""
- if resolved ~= "" then
- return resolved
- end
- resolved = resolvers.find_file(filename,'texmfscripts') or ""
- if resolved ~= "" then
- return resolved
- end
- return resolvers.find_file(filename,'luatexlibs') or ""
+if not texroot or texroot == "" then
+ texroot = osgetenv('SELFAUTOPARENT')
+ ossetenv('TEXROOT',texroot)
end
-environment.loadedluacode = loadfile -- can be overloaded
+environment.texroot = file.collapse_path(texroot)
---~ function environment.loadedluacode(name)
---~ if os.spawn("texluac -s -o texluac.luc " .. name) == 0 then
---~ local chunk = loadstring(io.loaddata("texluac.luc"))
---~ os.remove("texluac.luc")
---~ return chunk
---~ else
---~ environment.loadedluacode = loadfile -- can be overloaded
---~ return loadfile(name)
---~ end
---~ end
-
-function environment.luafilechunk(filename) -- used for loading lua bytecode in the format
- filename = file.replacesuffix(filename, "lua")
- local fullname = environment.luafile(filename)
- if fullname and fullname ~= "" then
- if trace_locating then
- logs.report("fileio","loading file %s", fullname)
- end
- return environment.loadedluacode(fullname)
- else
- if trace_locating then
- logs.report("fileio","unknown file %s", filename)
- end
- return nil
+-- Tracing. Todo ...
+
+function resolvers.settrace(n) -- no longer number but: 'locating' or 'detail'
+ if n then
+ trackers.disable("resolvers.*")
+ trackers.enable("resolvers."..n)
end
end
--- the next ones can use the previous ones / combine
+resolvers.settrace(osgetenv("MTX_INPUT_TRACE"))
-function environment.loadluafile(filename, version)
- local lucname, luaname, chunk
- local basename = file.removesuffix(filename)
- if basename == filename then
- lucname, luaname = basename .. ".luc", basename .. ".lua"
- else
- lucname, luaname = nil, basename -- forced suffix
- end
- -- when not overloaded by explicit suffix we look for a luc file first
- local fullname = (lucname and environment.luafile(lucname)) or ""
- if fullname ~= "" then
- if trace_locating then
- logs.report("fileio","loading %s", fullname)
- end
- chunk = loadfile(fullname) -- this way we don't need a file exists check
- end
- if chunk then
- assert(chunk)()
- if version then
- -- we check of the version number of this chunk matches
- local v = version -- can be nil
- if modules and modules[filename] then
- v = modules[filename].version -- new method
- elseif versions and versions[filename] then
- v = versions[filename] -- old method
- end
- if v == version then
- return true
- else
- if trace_locating then
- logs.report("fileio","version mismatch for %s: lua=%s, luc=%s", filename, v, version)
- end
- environment.loadluafile(filename)
- end
- else
- return true
- end
- end
- fullname = (luaname and environment.luafile(luaname)) or ""
- if fullname ~= "" then
- if trace_locating then
- logs.report("fileio","loading %s", fullname)
- end
- chunk = loadfile(fullname) -- this way we don't need a file exists check
- if not chunk then
- if trace_locating then
- logs.report("fileio","unknown file %s", filename)
- end
- else
- assert(chunk)()
- return true
- end
- end
- return false
-end
+-- todo:
+
+-- if profiler and osgetenv("MTX_PROFILE_RUN") == "YES" then
+-- profiler.start("luatex-profile.log")
+-- end
end -- of closure
do -- create closure to overcome 200 locals limit
-if not modules then modules = { } end modules ['trac-inf'] = {
+if not modules then modules = { } end modules ['data-exp'] = {
version = 1.001,
- comment = "companion to trac-inf.mkiv",
+ 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"
+ license = "see context related readme files",
}
-local format = string.format
+local format, gsub, find, gmatch, lower = string.format, string.gsub, string.find, string.gmatch, string.lower
+local concat, sort = table.concat, table.sort
+local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns
+local lpegCt, lpegCs, lpegP, lpegC, lpegS = lpeg.Ct, lpeg.Cs, lpeg.P, lpeg.C, lpeg.S
+local type, next = type, next
-local statusinfo, n, registered = { }, 0, { }
+local ostype = os.type
+local collapse_path = file.collapse_path
-statistics = statistics or { }
+local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
+local trace_expansions = false trackers.register("resolvers.expansions", function(v) trace_expansions = v end)
-statistics.enable = true
-statistics.threshold = 0.05
+local report_resolvers = logs.new("resolvers")
--- timing functions
+-- As this bit of code is somewhat special it gets its own module. After
+-- all, when working on the main resolver code, I don't want to scroll
+-- past this every time.
-local clock = os.gettimeofday or os.clock
+-- {a,b,c,d}
+-- a,b,c/{p,q,r},d
+-- a,b,c/{p,q,r}/d/{x,y,z}//
+-- a,b,c/{p,q/{x,y,z},r},d/{p,q,r}
+-- a,b,c/{p,q/{x,y,z},r},d/{p,q,r}
+-- a{b,c}{d,e}f
+-- {a,b,c,d}
+-- {a,b,c/{p,q,r},d}
+-- {a,b,c/{p,q,r}/d/{x,y,z}//}
+-- {a,b,c/{p,q/{x,y,z}},d/{p,q,r}}
+-- {a,b,c/{p,q/{x,y,z},w}v,d/{p,q,r}}
+-- {$SELFAUTODIR,$SELFAUTOPARENT}{,{/share,}/texmf{-local,.local,}/web2c}
+
+-- this one is better and faster, but it took me a while to realize
+-- that this kind of replacement is cleaner than messy parsing and
+-- fuzzy concatenating we can probably gain a bit with selectively
+-- applying lpeg, but experiments with lpeg parsing this proved not to
+-- work that well; the parsing is ok, but dealing with the resulting
+-- table is a pain because we need to work inside-out recursively
+
+local dummy_path_expr = "^!*unset/*$"
-local notimer
+local function do_first(a,b)
+ local t = { }
+ for s in gmatch(b,"[^,]+") do t[#t+1] = a .. s end
+ return "{" .. concat(t,",") .. "}"
+end
-function statistics.hastimer(instance)
- return instance and instance.starttime
+local function do_second(a,b)
+ local t = { }
+ for s in gmatch(a,"[^,]+") do t[#t+1] = s .. b end
+ return "{" .. concat(t,",") .. "}"
end
-function statistics.resettiming(instance)
- if not instance then
- notimer = { timing = 0, loadtime = 0 }
- else
- instance.timing, instance.loadtime = 0, 0
+local function do_both(a,b)
+ local t = { }
+ for sa in gmatch(a,"[^,]+") do
+ for sb in gmatch(b,"[^,]+") do
+ t[#t+1] = sa .. sb
+ end
end
+ return "{" .. concat(t,",") .. "}"
+end
+
+local function do_three(a,b,c)
+ return a .. b.. c
end
-function statistics.starttiming(instance)
- if not instance then
- notimer = { }
- instance = notimer
+local stripper_1 = lpeg.stripper("{}@")
+
+local replacer_1 = lpeg.replacer {
+ { ",}", ",@}" },
+ { "{,", "{@," },
+}
+
+local function splitpathexpr(str, newlist, validate)
+ -- no need for further optimization as it is only called a
+ -- few times, we can use lpeg for the sub
+ if trace_expansions then
+ report_resolvers("expanding variable '%s'",str)
end
- local it = instance.timing
- if not it then
- it = 0
+ local t, ok, done = newlist or { }, false, false
+ str = lpegmatch(replacer_1,str)
+ while true do
+ done = false
+ while true do
+ str, ok = gsub(str,"([^{},]+){([^{}]+)}",do_first)
+ if ok > 0 then done = true else break end
+ end
+ while true do
+ str, ok = gsub(str,"{([^{}]+)}([^{},]+)",do_second)
+ if ok > 0 then done = true else break end
+ end
+ while true do
+ str, ok = gsub(str,"{([^{}]+)}{([^{}]+)}",do_both)
+ if ok > 0 then done = true else break end
+ end
+ str, ok = gsub(str,"({[^{}]*){([^{}]+)}([^{}]*})",do_three)
+ if ok > 0 then done = true end
+ if not done then break end
end
- if it == 0 then
- instance.starttime = clock()
- if not instance.loadtime then
- instance.loadtime = 0
+ str = lpegmatch(stripper_1,str)
+ if validate then
+ for s in gmatch(str,"[^,]+") do
+ s = validate(s)
+ if s then t[#t+1] = s end
end
else
---~ logs.report("system","nested timing (%s)",tostring(instance))
- end
- instance.timing = it + 1
-end
-
-function statistics.stoptiming(instance, report)
- if not instance then
- instance = notimer
+ for s in gmatch(str,"[^,]+") do
+ t[#t+1] = s
+ end
end
- if instance then
- local it = instance.timing
- if it > 1 then
- instance.timing = it - 1
- else
- local starttime = instance.starttime
- if starttime then
- local stoptime = clock()
- local loadtime = stoptime - starttime
- instance.stoptime = stoptime
- instance.loadtime = instance.loadtime + loadtime
- if report then
- statistics.report("load time %0.3f",loadtime)
- end
- instance.timing = 0
- return loadtime
- end
+ if trace_expansions then
+ for k=1,#t do
+ report_resolvers("% 4i: %s",k,t[k])
end
end
- return 0
+ return t
end
-function statistics.elapsedtime(instance)
- if not instance then
- instance = notimer
+local function validate(s)
+ local isrecursive = find(s,"//$")
+ s = collapse_path(s)
+ if isrecursive then
+ s = s .. "//"
end
- return format("%0.3f",(instance and instance.loadtime) or 0)
+ return s ~= "" and not find(s,dummy_path_expr) and s
end
-function statistics.elapsedindeed(instance)
- if not instance then
- instance = notimer
+resolvers.validated_path = validate -- keeps the trailing //
+
+function resolvers.expanded_path_from_list(pathlist) -- maybe not a list, just a path
+ -- a previous version fed back into pathlist
+ local newlist, ok = { }, false
+ for k=1,#pathlist do
+ if find(pathlist[k],"[{}]") then
+ ok = true
+ break
+ end
+ end
+ if ok then
+ for k=1,#pathlist do
+ splitpathexpr(pathlist[k],newlist,validate)
+ end
+ else
+ for k=1,#pathlist do
+ for p in gmatch(pathlist[k],"([^,]+)") do
+ p = validate(p)
+ if p ~= "" then newlist[#newlist+1] = p end
+ end
+ end
end
- local t = (instance and instance.loadtime) or 0
- return t > statistics.threshold
+ return newlist
end
-function statistics.elapsedseconds(instance,rest) -- returns nil if 0 seconds
- if statistics.elapsedindeed(instance) then
- return format("%s seconds %s", statistics.elapsedtime(instance),rest or "")
- end
+-- We also put some cleanup code here.
+
+local cleanup -- used recursively
+
+cleanup = lpeg.replacer {
+ { "!", "" },
+ { "\\", "/" },
+ { "~" , function() return lpegmatch(cleanup,environment.homedir) end },
+}
+
+function resolvers.clean_path(str)
+ return str and lpegmatch(cleanup,str)
end
--- general function
+-- This one strips quotes and funny tokens.
-function statistics.register(tag,fnc)
- if statistics.enable and type(fnc) == "function" then
- local rt = registered[tag] or (#statusinfo + 1)
- statusinfo[rt] = { tag, fnc }
- registered[tag] = rt
- if #tag > n then n = #tag end
- end
+
+local expandhome = lpegP("~") / "$HOME" -- environment.homedir
+
+local dodouble = lpegP('"')/"" * (expandhome + (1 - lpegP('"')))^0 * lpegP('"')/""
+local dosingle = lpegP("'")/"" * (expandhome + (1 - lpegP("'")))^0 * lpegP("'")/""
+local dostring = (expandhome + 1 )^0
+
+local stripper = lpegCs(
+ lpegpatterns.unspacer * (dosingle + dodouble + dostring) * lpegpatterns.unspacer
+)
+
+function resolvers.checked_variable(str) -- assumes str is a string
+ return lpegmatch(stripper,str) or str
end
-function statistics.show(reporter)
- if statistics.enable then
- if not reporter then reporter = function(tag,data,n) texio.write_nl(tag .. " " .. data) end end
- -- this code will move
- local register = statistics.register
- register("luatex banner", function()
- return string.lower(status.banner)
- end)
- register("control sequences", function()
- return format("%s of %s", status.cs_count, status.hash_size+status.hash_extra)
- end)
- register("callbacks", function()
- local total, indirect = status.callbacks or 0, status.indirect_callbacks or 0
- return format("direct: %s, indirect: %s, total: %s", total-indirect, indirect, total)
- end)
- register("current memory usage", statistics.memused)
- register("runtime",statistics.runtime)
--- --
- for i=1,#statusinfo do
- local s = statusinfo[i]
- local r = s[2]()
- if r then
- reporter(s[1],r,n)
+-- The path splitter:
+
+-- A config (optionally) has the paths split in tables. Internally
+-- we join them and split them after the expansion has taken place. This
+-- is more convenient.
+
+
+local cache = { }
+
+local splitter = lpegCt(lpeg.splitat(lpegS(ostype == "windows" and ";" or ":;"))) -- maybe add ,
+
+local function split_configuration_path(str) -- beware, this can be either a path or a { specification }
+ if str then
+ local found = cache[str]
+ if not found then
+ if str == "" then
+ found = { }
+ else
+ str = gsub(str,"\\","/")
+ local split = lpegmatch(splitter,str)
+ found = { }
+ for i=1,#split do
+ local s = split[i]
+ if not find(s,"^{*unset}*") then
+ found[#found+1] = s
+ end
+ end
+ if trace_expansions then
+ report_resolvers("splitting path specification '%s'",str)
+ for k=1,#found do
+ report_resolvers("% 4i: %s",k,found[k])
+ end
+ end
+ cache[str] = found
end
end
- texio.write_nl("") -- final newline
- statistics.enable = false
+ return found
end
end
-function statistics.show_job_stat(tag,data,n)
- texio.write_nl(format("%-15s: %s - %s","mkiv lua stats",tag:rpadd(n," "),data))
-end
-
-function statistics.memused() -- no math.round yet -)
- local round = math.round or math.floor
- return format("%s MB (ctx: %s MB)",round(collectgarbage("count")/1000), round(status.luastate_bytes/1000000))
-end
+resolvers.split_configuration_path = split_configuration_path
-if statistics.runtime then
- -- already loaded and set
-elseif luatex and luatex.starttime then
- statistics.starttime = luatex.starttime
- statistics.loadtime = 0
- statistics.timing = 0
-else
- statistics.starttiming(statistics)
+function resolvers.split_path(str)
+ if type(str) == 'table' then
+ return str
+ else
+ return split_configuration_path(str)
+ end
end
-function statistics.runtime()
- statistics.stoptiming(statistics)
- return statistics.formatruntime(statistics.elapsedtime(statistics))
+function resolvers.join_path(str)
+ if type(str) == 'table' then
+ return file.join_path(str)
+ else
+ return str
+ end
end
-function statistics.formatruntime(runtime)
- return format("%s seconds", statistics.elapsedtime(statistics))
-end
+-- The next function scans directories and returns a hash where the
+-- entries are either strings or tables.
-function statistics.timed(action,report)
- local timer = { }
- report = report or logs.simple
- statistics.starttiming(timer)
- action()
- statistics.stoptiming(timer)
- report("total runtime: %s",statistics.elapsedtime(timer))
-end
+-- starting with . or .. etc or funny char
--- where, not really the best spot for this:
-commands = commands or { }
-local timer
-function commands.resettimer()
- statistics.resettiming(timer)
- statistics.starttiming(timer)
-end
+local weird = lpegP(".")^1 + lpeg.anywhere(lpegS("~`!#$%^&*()={}[]:;\"\'||<>,?\n\r\t"))
-function commands.elapsedtime()
- statistics.stoptiming(timer)
- tex.sprint(statistics.elapsedtime(timer))
+function resolvers.scan_files(specification)
+ if trace_locating then
+ report_resolvers("scanning path '%s'",specification)
+ end
+ local attributes, directory = lfs.attributes, lfs.dir
+ local files = { __path__ = specification }
+ local n, m, r = 0, 0, 0
+ local function scan(spec,path)
+ local full = (path == "" and spec) or (spec .. path .. '/')
+ local dirs = { }
+ for name in directory(full) do
+ if not lpegmatch(weird,name) then
+ local mode = attributes(full..name,'mode')
+ if mode == 'file' then
+ n = n + 1
+ local f = files[name]
+ if f then
+ if type(f) == 'string' then
+ files[name] = { f, path }
+ else
+ f[#f+1] = path
+ end
+ else -- probably unique anyway
+ files[name] = path
+ local lower = lower(name)
+ if name ~= lower then
+ files["remap:"..lower] = name
+ r = r + 1
+ end
+ end
+ elseif mode == 'directory' then
+ m = m + 1
+ if path ~= "" then
+ dirs[#dirs+1] = path..'/'..name
+ else
+ dirs[#dirs+1] = name
+ end
+ end
+ end
+ end
+ if #dirs > 0 then
+ sort(dirs)
+ for i=1,#dirs do
+ scan(spec,dirs[i])
+ end
+ end
+ end
+ scan(specification .. '/',"")
+ files.__files__, files.__directories__, files.__remappings__ = n, m, r
+ if trace_locating then
+ report_resolvers("%s files found on %s directories with %s uppercase remappings",n,m,r)
+ end
+ return files
end
-commands.resettimer()
end -- of closure
do -- create closure to overcome 200 locals limit
-if not modules then modules = { } end modules ['trac-log'] = {
+if not modules then modules = { } end modules ['data-env'] = {
version = 1.001,
- comment = "companion to trac-log.mkiv",
+ 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"
+ license = "see context related readme files",
}
--- this is old code that needs an overhaul
+local formats = { } resolvers.formats = formats
+local suffixes = { } resolvers.suffixes = suffixes
+local dangerous = { } resolvers.dangerous = dangerous
+local suffixmap = { } resolvers.suffixmap = suffixmap
+local alternatives = { } resolvers.alternatives = alternatives
+
+formats['afm'] = 'AFMFONTS' suffixes['afm'] = { 'afm' }
+formats['enc'] = 'ENCFONTS' suffixes['enc'] = { 'enc' }
+formats['fmt'] = 'TEXFORMATS' suffixes['fmt'] = { 'fmt' }
+formats['map'] = 'TEXFONTMAPS' suffixes['map'] = { 'map' }
+formats['mp'] = 'MPINPUTS' suffixes['mp'] = { 'mp' }
+formats['ocp'] = 'OCPINPUTS' suffixes['ocp'] = { 'ocp' }
+formats['ofm'] = 'OFMFONTS' suffixes['ofm'] = { 'ofm', 'tfm' }
+formats['otf'] = 'OPENTYPEFONTS' suffixes['otf'] = { 'otf' }
+formats['opl'] = 'OPLFONTS' suffixes['opl'] = { 'opl' }
+formats['otp'] = 'OTPINPUTS' suffixes['otp'] = { 'otp' }
+formats['ovf'] = 'OVFFONTS' suffixes['ovf'] = { 'ovf', 'vf' }
+formats['ovp'] = 'OVPFONTS' suffixes['ovp'] = { 'ovp' }
+formats['tex'] = 'TEXINPUTS' suffixes['tex'] = { 'tex' }
+formats['tfm'] = 'TFMFONTS' suffixes['tfm'] = { 'tfm' }
+formats['ttf'] = 'TTFONTS' suffixes['ttf'] = { 'ttf', 'ttc', 'dfont' }
+formats['pfb'] = 'T1FONTS' suffixes['pfb'] = { 'pfb', 'pfa' }
+formats['vf'] = 'VFFONTS' suffixes['vf'] = { 'vf' }
+formats['fea'] = 'FONTFEATURES' suffixes['fea'] = { 'fea' }
+formats['cid'] = 'FONTCIDMAPS' suffixes['cid'] = { 'cid', 'cidmap' }
+formats['texmfscripts'] = 'TEXMFSCRIPTS' suffixes['texmfscripts'] = { 'rb', 'pl', 'py' }
+formats['lua'] = 'LUAINPUTS' suffixes['lua'] = { 'lua', 'luc', 'tma', 'tmc' }
+formats['lib'] = 'CLUAINPUTS' suffixes['lib'] = (os.libsuffix and { os.libsuffix }) or { 'dll', 'so' }
---~ io.stdout:setvbuf("no")
---~ io.stderr:setvbuf("no")
-
-local write_nl, write = texio.write_nl or print, texio.write or io.write
-local format, gmatch = string.format, string.gmatch
-local texcount = tex and tex.count
+-- backward compatible ones
-if texlua then
- write_nl = print
- write = io.write
-end
+alternatives['map files'] = 'map'
+alternatives['enc files'] = 'enc'
+alternatives['cid maps'] = 'cid' -- great, why no cid files
+alternatives['font feature files'] = 'fea' -- and fea files here
+alternatives['opentype fonts'] = 'otf'
+alternatives['truetype fonts'] = 'ttf'
+alternatives['truetype collections'] = 'ttc'
+alternatives['truetype dictionary'] = 'dfont'
+alternatives['type1 fonts'] = 'pfb'
--[[ldx--
-<p>This is a prelude to a more extensive logging module. For the sake
-of parsing log files, in addition to the standard logging we will
-provide an <l n='xml'/> structured file. Actually, any logging that
-is hooked into callbacks will be \XML\ by default.</p>
+<p>If you wondered about some of the previous mappings, how about
+the next bunch:</p>
--ldx]]--
-logs = logs or { }
-logs.xml = logs.xml or { }
-logs.tex = logs.tex or { }
+-- kpse specific ones (a few omitted) .. we only add them for locating
+-- files that we don't use anyway
+
+formats['base'] = 'MFBASES' suffixes['base'] = { 'base', 'bas' }
+formats['bib'] = '' suffixes['bib'] = { 'bib' }
+formats['bitmap font'] = '' suffixes['bitmap font'] = { }
+formats['bst'] = '' suffixes['bst'] = { 'bst' }
+formats['cmap files'] = 'CMAPFONTS' suffixes['cmap files'] = { 'cmap' }
+formats['cnf'] = '' suffixes['cnf'] = { 'cnf' }
+formats['cweb'] = '' suffixes['cweb'] = { 'w', 'web', 'ch' }
+formats['dvips config'] = '' suffixes['dvips config'] = { }
+formats['gf'] = '' suffixes['gf'] = { '<resolution>gf' }
+formats['graphic/figure'] = '' suffixes['graphic/figure'] = { 'eps', 'epsi' }
+formats['ist'] = '' suffixes['ist'] = { 'ist' }
+formats['lig files'] = 'LIGFONTS' suffixes['lig files'] = { 'lig' }
+formats['ls-R'] = '' suffixes['ls-R'] = { }
+formats['mem'] = 'MPMEMS' suffixes['mem'] = { 'mem' }
+formats['MetaPost support'] = '' suffixes['MetaPost support'] = { }
+formats['mf'] = 'MFINPUTS' suffixes['mf'] = { 'mf' }
+formats['mft'] = '' suffixes['mft'] = { 'mft' }
+formats['misc fonts'] = '' suffixes['misc fonts'] = { }
+formats['other text files'] = '' suffixes['other text files'] = { }
+formats['other binary files'] = '' suffixes['other binary files'] = { }
+formats['pdftex config'] = 'PDFTEXCONFIG' suffixes['pdftex config'] = { }
+formats['pk'] = '' suffixes['pk'] = { '<resolution>pk' }
+formats['PostScript header'] = 'TEXPSHEADERS' suffixes['PostScript header'] = { 'pro' }
+formats['sfd'] = 'SFDFONTS' suffixes['sfd'] = { 'sfd' }
+formats['TeX system documentation'] = '' suffixes['TeX system documentation'] = { }
+formats['TeX system sources'] = '' suffixes['TeX system sources'] = { }
+formats['Troff fonts'] = '' suffixes['Troff fonts'] = { }
+formats['type42 fonts'] = 'T42FONTS' suffixes['type42 fonts'] = { }
+formats['web'] = '' suffixes['web'] = { 'web', 'ch' }
+formats['web2c files'] = 'WEB2C' suffixes['web2c files'] = { }
+formats['fontconfig files'] = 'FONTCONFIG_PATH' suffixes['fontconfig files'] = { } -- not unique
---[[ldx--
-<p>This looks pretty ugly but we need to speed things up a bit.</p>
---ldx]]--
+alternatives['subfont definition files'] = 'sfd'
-logs.moreinfo = [[
-more information about ConTeXt and the tools that come with it can be found at:
+-- A few accessors, mostly for command line tool.
-maillist : ntg-context@ntg.nl / http://www.ntg.nl/mailman/listinfo/ntg-context
-webpage : http://www.pragma-ade.nl / http://tex.aanhet.net
-wiki : http://contextgarden.net
-]]
+function resolvers.suffix_of_format(str)
+ local s = suffixes[str]
+ return s and s[1] or ""
+end
-logs.levels = {
- ['error'] = 1,
- ['warning'] = 2,
- ['info'] = 3,
- ['debug'] = 4,
-}
+function resolvers.suffixes_of_format(str)
+ return suffixes[str] or { }
+end
-logs.functions = {
- 'report', 'start', 'stop', 'push', 'pop', 'line', 'direct',
- 'start_run', 'stop_run',
- 'start_page_number', 'stop_page_number',
- 'report_output_pages', 'report_output_log',
- 'report_tex_stat', 'report_job_stat',
- 'show_open', 'show_close', 'show_load',
-}
+-- As we don't register additional suffixes anyway, we can as well
+-- freeze the reverse map here.
-logs.tracers = {
-}
+for name, suffixlist in next, suffixes do
+ for i=1,#suffixlist do
+ suffixmap[suffixlist[i]] = name
+ end
+end
-logs.level = 0
-logs.mode = string.lower((os.getenv("MTX.LOG.MODE") or os.getenv("MTX_LOG_MODE") or "tex"))
+setmetatable(suffixes, { __newindex = function(suffixes,name,suffixlist)
+ rawset(suffixes,name,suffixlist)
+ suffixes[name] = suffixlist
+ for i=1,#suffixlist do
+ suffixmap[suffixlist[i]] = name
+ end
+end } )
-function logs.set_level(level)
- logs.level = logs.levels[level] or level
+for name, format in next, formats do
+ dangerous[name] = true
end
-function logs.set_method(method)
- for _, v in next, logs.functions do
- logs[v] = logs[method][v] or function() end
- end
+-- because vf searching is somewhat dangerous, we want to prevent
+-- too liberal searching esp because we do a lookup on the current
+-- path anyway; only tex (or any) is safe
+
+dangerous.tex = nil
+
+
+-- more helpers
+
+function resolvers.format_of_var(str)
+ return formats[str] or formats[alternatives[str]] or ''
end
--- tex logging
+function resolvers.format_of_suffix(str) -- of file
+ return suffixmap[file.extname(str)] or 'tex'
+end
-function logs.tex.report(category,fmt,...) -- new
- if fmt then
- write_nl(category .. " | " .. format(fmt,...))
- else
- write_nl(category .. " |")
- end
+function resolvers.variable_of_format(str)
+ return formats[str] or formats[alternatives[str]] or ''
end
-function logs.tex.line(fmt,...) -- new
- if fmt then
- write_nl(format(fmt,...))
- else
- write_nl("")
+function resolvers.var_of_format_or_suffix(str)
+ local v = formats[str]
+ if v then
+ return v
+ end
+ v = formats[alternatives[str]]
+ if v then
+ return v
end
+ v = suffixmap[fileextname(str)]
+ if v then
+ return formats[v]
+ end
+ return ''
end
---~ function logs.tex.start_page_number()
---~ local real, user, sub = texcount.realpageno, texcount.userpageno, texcount.subpageno
---~ if real > 0 then
---~ if user > 0 then
---~ if sub > 0 then
---~ write(format("[%s.%s.%s",real,user,sub))
---~ else
---~ write(format("[%s.%s",real,user))
---~ end
---~ else
---~ write(format("[%s",real))
---~ end
---~ else
---~ write("[-")
---~ end
---~ end
-
---~ function logs.tex.stop_page_number()
---~ write("]")
---~ end
-local real, user, sub
-function logs.tex.start_page_number()
- real, user, sub = texcount.realpageno, texcount.userpageno, texcount.subpageno
-end
+end -- of closure
-function logs.tex.stop_page_number()
- if real > 0 then
- if user > 0 then
- if sub > 0 then
- logs.report("pages", "flushing realpage %s, userpage %s, subpage %s",real,user,sub)
- else
- logs.report("pages", "flushing realpage %s, userpage %s",real,user)
+do -- create closure to overcome 200 locals limit
+
+if not modules then modules = { } end modules ['data-tmp'] = {
+ version = 1.100,
+ comment = "companion to luat-lib.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+--[[ldx--
+<p>This module deals with caching data. It sets up the paths and
+implements loaders and savers for tables. Best is to set the
+following variable. When not set, the usual paths will be
+checked. Personally I prefer the (users) temporary path.</p>
+
+</code>
+TEXMFCACHE=$TMP;$TEMP;$TMPDIR;$TEMPDIR;$HOME;$TEXMFVAR;$VARTEXMF;.
+</code>
+
+<p>Currently we do no locking when we write files. This is no real
+problem because most caching involves fonts and the chance of them
+being written at the same time is small. We also need to extend
+luatools with a recache feature.</p>
+--ldx]]--
+
+local format, lower, gsub, concat = string.format, string.lower, string.gsub, table.concat
+local mkdirs, isdir = dir.mkdirs, lfs.isdir
+
+local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
+local trace_cache = false trackers.register("resolvers.cache", function(v) trace_cache = v end)
+
+local report_cache = logs.new("cache")
+
+local report_resolvers = logs.new("resolvers")
+
+caches = caches or { }
+
+caches.base = caches.base or "luatex-cache"
+caches.more = caches.more or "context"
+caches.direct = false -- true is faster but may need huge amounts of memory
+caches.tree = false
+caches.force = true
+caches.ask = false
+caches.defaults = { "TMPDIR", "TEMPDIR", "TMP", "TEMP", "HOME", "HOMEPATH" }
+
+local writable, readables, usedreadables = nil, { }, { }
+
+-- we could use a metatable for writable and readable but not yet
+
+local function identify()
+ -- Combining the loops makes it messy. First we check the format cache path
+ -- and when the last component is not present we try to create it.
+ local texmfcaches = resolvers.clean_path_list("TEXMFCACHE")
+ if texmfcaches then
+ for k=1,#texmfcaches do
+ local cachepath = texmfcaches[k]
+ if cachepath ~= "" then
+ cachepath = resolvers.clean_path(cachepath)
+ cachepath = file.collapse_path(cachepath)
+ local valid = isdir(cachepath)
+ if valid then
+ if file.isreadable(cachepath) then
+ readables[#readables+1] = cachepath
+ if not writable and file.iswritable(cachepath) then
+ writable = cachepath
+ end
+ end
+ elseif not writable and caches.force then
+ local cacheparent = file.dirname(cachepath)
+ if file.iswritable(cacheparent) then
+ if not caches.ask or io.ask(format("\nShould I create the cache path %s?",cachepath), "no", { "yes", "no" }) == "yes" then
+ mkdirs(cachepath)
+ if isdir(cachepath) and file.iswritable(cachepath) then
+ report_cache("created: %s",cachepath)
+ writable = cachepath
+ readables[#readables+1] = cachepath
+ end
+ end
+ end
+ end
end
- else
- logs.report("pages", "flushing realpage %s",real)
+ end
+ end
+ -- As a last resort we check some temporary paths but this time we don't
+ -- create them.
+ local texmfcaches = caches.defaults
+ if texmfcaches then
+ for k=1,#texmfcaches do
+ local cachepath = texmfcaches[k]
+ cachepath = resolvers.getenv(cachepath)
+ if cachepath ~= "" then
+ cachepath = resolvers.clean_path(cachepath)
+ local valid = isdir(cachepath)
+ if valid and file.isreadable(cachepath) then
+ if not writable and file.iswritable(cachepath) then
+ readables[#readables+1] = cachepath
+ writable = cachepath
+ break
+ end
+ end
+ end
+ end
+ end
+ -- Some extra checking. If we have no writable or readable path then we simply
+ -- quit.
+ if not writable then
+ report_cache("fatal error: there is no valid writable cache path defined")
+ os.exit()
+ elseif #readables == 0 then
+ report_cache("fatal error: there is no valid readable cache path defined")
+ os.exit()
+ end
+ -- why here
+ writable = dir.expand_name(resolvers.clean_path(writable)) -- just in case
+ -- moved here
+ local base, more, tree = caches.base, caches.more, caches.tree or caches.treehash() -- we have only one writable tree
+ if tree then
+ caches.tree = tree
+ writable = mkdirs(writable,base,more,tree)
+ for i=1,#readables do
+ readables[i] = file.join(readables[i],base,more,tree)
end
else
- logs.report("pages", "flushing page")
+ writable = mkdirs(writable,base,more)
+ for i=1,#readables do
+ readables[i] = file.join(readables[i],base,more)
+ end
end
- io.flush()
+ -- end
+ if trace_cache then
+ for i=1,#readables do
+ report_cache("using readable path '%s' (order %s)",readables[i],i)
+ end
+ report_cache("using writable path '%s'",writable)
+ end
+ identify = function()
+ return writable, readables
+ end
+ return writable, readables
end
-logs.tex.report_job_stat = statistics.show_job_stat
-
--- xml logging
-
-function logs.xml.report(category,fmt,...) -- new
- if fmt then
- write_nl(format("<r category='%s'>%s</r>",category,format(fmt,...)))
+function caches.usedpaths()
+ local writable, readables = identify()
+ if #readables > 1 then
+ local result = { }
+ for i=1,#readables do
+ local readable = readables[i]
+ if usedreadables[i] or readable == writable then
+ result[#result+1] = format("readable: '%s' (order %s)",readable,i)
+ end
+ end
+ result[#result+1] = format("writable: '%s'",writable)
+ return result
else
- write_nl(format("<r category='%s'/>",category))
+ return writable
end
end
-function logs.xml.line(fmt,...) -- new
- if fmt then
- write_nl(format("<r>%s</r>",format(fmt,...)))
+
+function caches.configfiles()
+ return table.concat(resolvers.instance.specification,";")
+end
+
+function caches.hashed(tree)
+ return md5.hex(gsub(lower(tree),"[\\\/]+","/"))
+end
+
+function caches.treehash()
+ local tree = caches.configfiles()
+ if not tree or tree == "" then
+ return false
else
- write_nl("<r/>")
+ return caches.hashed(tree)
end
end
-function logs.xml.start() if logs.level > 0 then tw("<%s>" ) end end
-function logs.xml.stop () if logs.level > 0 then tw("</%s>") end end
-function logs.xml.push () if logs.level > 0 then tw("<!-- ") end end
-function logs.xml.pop () if logs.level > 0 then tw(" -->" ) end end
+local r_cache, w_cache = { }, { } -- normally w in in r but who cares
-function logs.xml.start_run()
- write_nl("<?xml version='1.0' standalone='yes'?>")
- write_nl("<job>") -- xmlns='www.pragma-ade.com/luatex/schemas/context-job.rng'
- write_nl("")
+local function getreadablepaths(...) -- we can optimize this as we have at most 2 tags
+ local tags = { ... }
+ local hash = concat(tags,"/")
+ local done = r_cache[hash]
+ if not done then
+ local writable, readables = identify() -- exit if not found
+ if #tags > 0 then
+ done = { }
+ for i=1,#readables do
+ done[i] = file.join(readables[i],...)
+ end
+ else
+ done = readables
+ end
+ r_cache[hash] = done
+ end
+ return done
end
-function logs.xml.stop_run()
- write_nl("</job>")
+local function getwritablepath(...)
+ local tags = { ... }
+ local hash = concat(tags,"/")
+ local done = w_cache[hash]
+ if not done then
+ local writable, readables = identify() -- exit if not found
+ if #tags > 0 then
+ done = mkdirs(writable,...)
+ else
+ done = writable
+ end
+ w_cache[hash] = done
+ end
+ return done
end
-function logs.xml.start_page_number()
- write_nl(format("<p real='%s' page='%s' sub='%s'", texcount.realpageno, texcount.userpageno, texcount.subpageno))
-end
+caches.getreadablepaths = getreadablepaths
+caches.getwritablepath = getwritablepath
-function logs.xml.stop_page_number()
- write("/>")
- write_nl("")
+function caches.getfirstreadablefile(filename,...)
+ local rd = getreadablepaths(...)
+ for i=1,#rd do
+ local path = rd[i]
+ local fullname = file.join(path,filename)
+ if file.isreadable(fullname) then
+ usedreadables[i] = true
+ return fullname, path
+ end
+ end
+ return caches.setfirstwritablefile(filename,...)
end
-function logs.xml.report_output_pages(p,b)
- write_nl(format("<v k='pages' v='%s'/>", p))
- write_nl(format("<v k='bytes' v='%s'/>", b))
- write_nl("")
+function caches.setfirstwritablefile(filename,...)
+ local wr = getwritablepath(...)
+ local fullname = file.join(wr,filename)
+ return fullname, wr
end
-function logs.xml.report_output_log()
+function caches.define(category,subcategory) -- for old times sake
+ return function()
+ return getwritablepath(category,subcategory)
+ end
end
-function logs.xml.report_tex_stat(k,v)
- texiowrite_nl("log","<v k='"..k.."'>"..tostring(v).."</v>")
+function caches.setluanames(path,name)
+ return path .. "/" .. name .. ".tma", path .. "/" .. name .. ".tmc"
end
-local level = 0
-
-function logs.xml.show_open(name)
- level = level + 1
- texiowrite_nl(format("<f l='%s' n='%s'>",level,name))
+function caches.loaddata(readables,name)
+ if type(readables) == "string" then
+ readables = { readables }
+ end
+ for i=1,#readables do
+ local path = readables[i]
+ local tmaname, tmcname = caches.setluanames(path,name)
+ local loader = loadfile(tmcname) or loadfile(tmaname)
+ if loader then
+ loader = loader()
+ collectgarbage("step")
+ return loader
+ end
+ end
+ return false
end
-function logs.xml.show_close(name)
- texiowrite("</f> ")
- level = level - 1
+function caches.iswritable(filepath,filename)
+ local tmaname, tmcname = caches.setluanames(filepath,filename)
+ return file.iswritable(tmaname)
end
-function logs.xml.show_load(name)
- texiowrite_nl(format("<f l='%s' n='%s'/>",level+1,name))
+function caches.savedata(filepath,filename,data,raw)
+ local tmaname, tmcname = caches.setluanames(filepath,filename)
+ local reduce, simplify = true, true
+ if raw then
+ reduce, simplify = false, false
+ end
+ data.cache_uuid = os.uuid()
+ if caches.direct then
+ file.savedata(tmaname, table.serialize(data,'return',false,true,false)) -- no hex
+ else
+ table.tofile(tmaname, data,'return',false,true,false) -- maybe not the last true
+ end
+ local cleanup = resolvers.boolean_variable("PURGECACHE", false)
+ local strip = resolvers.boolean_variable("LUACSTRIP", true)
+ utils.lua.compile(tmaname, tmcname, cleanup, strip)
end
---
+-- moved from data-res:
-local name, banner = 'report', 'context'
+local content_state = { }
-local function report(category,fmt,...)
- if fmt then
- write_nl(format("%s | %s: %s",name,category,format(fmt,...)))
- elseif category then
- write_nl(format("%s | %s",name,category))
- else
- write_nl(format("%s |",name))
- end
+function caches.contentstate()
+ return content_state or { }
end
-local function simple(fmt,...)
- if fmt then
- write_nl(format("%s | %s",name,format(fmt,...)))
- else
- write_nl(format("%s |",name))
+function caches.loadcontent(cachename,dataname)
+ local name = caches.hashed(cachename)
+ local full, path = caches.getfirstreadablefile(name ..".lua","trees")
+ local filename = file.join(path,name)
+ local blob = loadfile(filename .. ".luc") or loadfile(filename .. ".lua")
+ if blob then
+ local data = blob()
+ if data and data.content and data.type == dataname and data.version == resolvers.cacheversion then
+ content_state[#content_state+1] = data.uuid
+ if trace_locating then
+ report_resolvers("loading '%s' for '%s' from '%s'",dataname,cachename,filename)
+ end
+ return data.content
+ elseif trace_locating then
+ report_resolvers("skipping '%s' for '%s' from '%s'",dataname,cachename,filename)
+ end
+ elseif trace_locating then
+ report_resolvers("skipping '%s' for '%s' from '%s'",dataname,cachename,filename)
end
end
-function logs.setprogram(_name_,_banner_,_verbose_)
- name, banner = _name_, _banner_
- if _verbose_ then
- trackers.enable("resolvers.locating")
- end
- logs.set_method("tex")
- logs.report = report -- also used in libraries
- logs.simple = simple -- only used in scripts !
- if utils then
- utils.report = simple
+function caches.collapsecontent(content)
+ for k, v in next, content do
+ if type(v) == "table" and #v == 1 then
+ content[k] = v[1]
+ end
end
- logs.verbose = _verbose_
end
-function logs.setverbose(what)
- if what then
- trackers.enable("resolvers.locating")
- else
- trackers.disable("resolvers.locating")
+function caches.savecontent(cachename,dataname,content)
+ local name = caches.hashed(cachename)
+ local full, path = caches.setfirstwritablefile(name ..".lua","trees")
+ local filename = file.join(path,name) -- is full
+ local luaname, lucname = filename .. ".lua", filename .. ".luc"
+ if trace_locating then
+ report_resolvers("preparing '%s' for '%s'",dataname,cachename)
+ end
+ local data = {
+ type = dataname,
+ root = cachename,
+ version = resolvers.cacheversion,
+ date = os.date("%Y-%m-%d"),
+ time = os.date("%H:%M:%S"),
+ content = content,
+ uuid = os.uuid(),
+ }
+ local ok = io.savedata(luaname,table.serialize(data,true))
+ if ok then
+ if trace_locating then
+ report_resolvers("category '%s', cachename '%s' saved in '%s'",dataname,cachename,luaname)
+ end
+ if utils.lua.compile(luaname,lucname,false,true) then -- no cleanup but strip
+ if trace_locating then
+ report_resolvers("'%s' compiled to '%s'",dataname,lucname)
+ end
+ return true
+ else
+ if trace_locating then
+ report_resolvers("compiling failed for '%s', deleting file '%s'",dataname,lucname)
+ end
+ os.remove(lucname)
+ end
+ elseif trace_locating then
+ report_resolvers("unable to save '%s' in '%s' (access error)",dataname,luaname)
end
- logs.verbose = what or false
end
-function logs.extendbanner(_banner_,_verbose_)
- banner = banner .. " | ".. _banner_
- if _verbose_ ~= nil then
- logs.setverbose(what)
- end
-end
-logs.verbose = false
-logs.report = logs.tex.report
-logs.simple = logs.tex.report
-function logs.reportlines(str) -- todo: <lines></lines>
- for line in gmatch(str,"(.-)[\n\r]") do
- logs.report(line)
- end
-end
-function logs.reportline() -- for scripts too
- logs.report()
-end
+end -- of closure
-logs.simpleline = logs.reportline
+do -- create closure to overcome 200 locals limit
-function logs.reportbanner() -- for scripts too
- logs.report(banner)
-end
+if not modules then modules = { } end modules ['data-met'] = {
+ version = 1.100,
+ comment = "companion to luat-lib.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
-function logs.help(message,option)
- logs.reportbanner()
- logs.reportline()
- logs.reportlines(message)
- local moreinfo = logs.moreinfo or ""
- if moreinfo ~= "" and option ~= "nomoreinfo" then
- logs.reportline()
- logs.reportlines(moreinfo)
+local find = string.find
+
+local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
+
+local report_resolvers = logs.new("resolvers")
+
+resolvers.locators = { notfound = { nil } } -- locate databases
+resolvers.hashers = { notfound = { nil } } -- load databases
+resolvers.generators = { notfound = { nil } } -- generate databases
+
+function resolvers.splitmethod(filename)
+ if not filename then
+ return { } -- safeguard
+ elseif type(filename) == "table" then
+ return filename -- already split
+ elseif not find(filename,"://") then
+ return { scheme="file", path = filename, original = filename } -- quick hack
+ else
+ return url.hashed(filename)
end
end
-logs.set_level('error')
-logs.set_method('tex')
-
-function logs.system(whereto,process,jobname,category,...)
- for i=1,10 do
- local f = io.open(whereto,"a")
- if f then
- f:write(format("%s %s => %s => %s => %s\r",os.date("%d/%m/%y %H:%m:%S"),process,jobname,category,format(...)))
- f:close()
- break
- else
- sleep(0.1)
+function resolvers.methodhandler(what, filename, filetype) -- ...
+ filename = file.collapse_path(filename)
+ local specification = (type(filename) == "string" and resolvers.splitmethod(filename)) or filename -- no or { }, let it bomb
+ local scheme = specification.scheme
+ local resolver = resolvers[what]
+ if resolver[scheme] then
+ if trace_locating then
+ report_resolvers("handler '%s' -> '%s' -> '%s'",specification.original,what,table.sequenced(specification))
end
+ return resolver[scheme](filename,filetype)
+ else
+ return resolver.tex(filename,filetype) -- todo: specification
end
end
---~ local syslogname = "oeps.xxx"
---~
---~ for i=1,10 do
---~ logs.system(syslogname,"context","test","fonts","font %s recached due to newer version (%s)","blabla","123")
---~ end
-
-function logs.fatal(where,...)
- logs.report(where,"fatal error: %s, aborting now",format(...))
- os.exit()
-end
end -- of closure
do -- create closure to overcome 200 locals limit
-if not modules then modules = { } end modules ['data-inp'] = {
+if not modules then modules = { } end modules ['data-res'] = {
version = 1.001,
comment = "companion to luat-lib.mkiv",
author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
@@ -8146,70 +9499,45 @@ if not modules then modules = { } end modules ['data-inp'] = {
license = "see context related readme files",
}
--- After a few years using the code the large luat-inp.lua file
--- has been split up a bit. In the process some functionality was
--- dropped:
---
--- * support for reading lsr files
--- * selective scanning (subtrees)
--- * some public auxiliary functions were made private
---
--- TODO: os.getenv -> os.env[]
--- TODO: instances.[hashes,cnffiles,configurations,522]
--- TODO: check escaping in find etc, too much, too slow
-
--- This lib is multi-purpose and can be loaded again later on so that
--- additional functionality becomes available. We will split thislogs.report("fileio",
--- module in components once we're done with prototyping. This is the
--- first code I wrote for LuaTeX, so it needs some cleanup. Before changing
--- something in this module one can best check with Taco or Hans first; there
--- is some nasty trickery going on that relates to traditional kpse support.
-
--- To be considered: hash key lowercase, first entry in table filename
--- (any case), rest paths (so no need for optimization). Or maybe a
--- separate table that matches lowercase names to mixed case when
--- present. In that case the lower() cases can go away. I will do that
--- only when we run into problems with names ... well ... Iwona-Regular.
+-- In practice we will work within one tds tree, but i want to keep
+-- the option open to build tools that look at multiple trees, which is
+-- why we keep the tree specific data in a table. We used to pass the
+-- instance but for practical purposes we now avoid this and use a
+-- instance variable. We always have one instance active (sort of global).
--- Beware, loading and saving is overloaded in luat-tmp!
+-- todo: cache:/// home:///
local format, gsub, find, lower, upper, match, gmatch = string.format, string.gsub, string.find, string.lower, string.upper, string.match, string.gmatch
local concat, insert, sortedkeys = table.concat, table.insert, table.sortedkeys
local next, type = next, type
-local lpegmatch = lpeg.match
-local trace_locating, trace_detail, trace_expansions = false, false, false
+local lpegP, lpegS, lpegR, lpegC, lpegCc, lpegCs, lpegCt = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.Cc, lpeg.Cs, lpeg.Ct
+local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns
-trackers.register("resolvers.locating", function(v) trace_locating = v end)
-trackers.register("resolvers.details", function(v) trace_detail = v end)
-trackers.register("resolvers.expansions", function(v) trace_expansions = v end) -- todo
+local filedirname, filebasename, fileextname, filejoin = file.dirname, file.basename, file.extname, file.join
+local collapse_path = file.collapse_path
-if not resolvers then
- resolvers = {
- suffixes = { },
- formats = { },
- dangerous = { },
- suffixmap = { },
- alternatives = { },
- locators = { }, -- locate databases
- hashers = { }, -- load databases
- generators = { }, -- generate databases
- }
-end
+local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
+local trace_detail = false trackers.register("resolvers.details", function(v) trace_detail = v end)
+local trace_expansions = false trackers.register("resolvers.expansions", function(v) trace_expansions = v end)
-local resolvers = resolvers
+local report_resolvers = logs.new("resolvers")
-resolvers.locators .notfound = { nil }
-resolvers.hashers .notfound = { nil }
-resolvers.generators.notfound = { nil }
+local expanded_path_from_list = resolvers.expanded_path_from_list
+local checked_variable = resolvers.checked_variable
+local split_configuration_path = resolvers.split_configuration_path
+
+local ostype, osname, osenv, ossetenv, osgetenv = os.type, os.name, os.env, os.setenv, os.getenv
resolvers.cacheversion = '1.0.1'
-resolvers.cnfname = 'texmf.cnf'
-resolvers.luaname = 'texmfcnf.lua'
-resolvers.homedir = os.env[os.type == "windows" and 'USERPROFILE'] or os.env['HOME'] or '~'
-resolvers.cnfdefault = '{$SELFAUTODIR,$SELFAUTOPARENT}{,{/share,}/texmf{-local,.local,}/web2c}'
+resolvers.configbanner = ''
+resolvers.homedir = environment.homedir
+resolvers.criticalvars = { "SELFAUTOLOC", "SELFAUTODIR", "SELFAUTOPARENT", "TEXMFCNF", "TEXMF", "TEXOS" }
+resolvers.luacnfspec = '{$SELFAUTODIR,$SELFAUTOPARENT}{,{/share,}/texmf{-local,}/web2c}' -- rubish path
+resolvers.luacnfname = 'texmfcnf.lua'
+resolvers.luacnfstate = "unknown"
-local dummy_path_expr = "^!*unset/*$"
+local unset_variable = "unset"
local formats = resolvers.formats
local suffixes = resolvers.suffixes
@@ -8217,104 +9545,12 @@ local dangerous = resolvers.dangerous
local suffixmap = resolvers.suffixmap
local alternatives = resolvers.alternatives
-formats['afm'] = 'AFMFONTS' suffixes['afm'] = { 'afm' }
-formats['enc'] = 'ENCFONTS' suffixes['enc'] = { 'enc' }
-formats['fmt'] = 'TEXFORMATS' suffixes['fmt'] = { 'fmt' }
-formats['map'] = 'TEXFONTMAPS' suffixes['map'] = { 'map' }
-formats['mp'] = 'MPINPUTS' suffixes['mp'] = { 'mp' }
-formats['ocp'] = 'OCPINPUTS' suffixes['ocp'] = { 'ocp' }
-formats['ofm'] = 'OFMFONTS' suffixes['ofm'] = { 'ofm', 'tfm' }
-formats['otf'] = 'OPENTYPEFONTS' suffixes['otf'] = { 'otf' } -- 'ttf'
-formats['opl'] = 'OPLFONTS' suffixes['opl'] = { 'opl' }
-formats['otp'] = 'OTPINPUTS' suffixes['otp'] = { 'otp' }
-formats['ovf'] = 'OVFFONTS' suffixes['ovf'] = { 'ovf', 'vf' }
-formats['ovp'] = 'OVPFONTS' suffixes['ovp'] = { 'ovp' }
-formats['tex'] = 'TEXINPUTS' suffixes['tex'] = { 'tex' }
-formats['tfm'] = 'TFMFONTS' suffixes['tfm'] = { 'tfm' }
-formats['ttf'] = 'TTFONTS' suffixes['ttf'] = { 'ttf', 'ttc', 'dfont' }
-formats['pfb'] = 'T1FONTS' suffixes['pfb'] = { 'pfb', 'pfa' }
-formats['vf'] = 'VFFONTS' suffixes['vf'] = { 'vf' }
-
-formats['fea'] = 'FONTFEATURES' suffixes['fea'] = { 'fea' }
-formats['cid'] = 'FONTCIDMAPS' suffixes['cid'] = { 'cid', 'cidmap' }
-
-formats ['texmfscripts'] = 'TEXMFSCRIPTS' -- new
-suffixes['texmfscripts'] = { 'rb', 'pl', 'py' } -- 'lua'
-
-formats ['lua'] = 'LUAINPUTS' -- new
-suffixes['lua'] = { 'lua', 'luc', 'tma', 'tmc' }
-
--- backward compatible ones
-
-alternatives['map files'] = 'map'
-alternatives['enc files'] = 'enc'
-alternatives['cid maps'] = 'cid' -- great, why no cid files
-alternatives['font feature files'] = 'fea' -- and fea files here
-alternatives['opentype fonts'] = 'otf'
-alternatives['truetype fonts'] = 'ttf'
-alternatives['truetype collections'] = 'ttc'
-alternatives['truetype dictionary'] = 'dfont'
-alternatives['type1 fonts'] = 'pfb'
-
--- obscure ones
-
-formats ['misc fonts'] = ''
-suffixes['misc fonts'] = { }
-
-formats ['sfd'] = 'SFDFONTS'
-suffixes ['sfd'] = { 'sfd' }
-alternatives['subfont definition files'] = 'sfd'
-
--- lib paths
-
-formats ['lib'] = 'CLUAINPUTS' -- new (needs checking)
-suffixes['lib'] = (os.libsuffix and { os.libsuffix }) or { 'dll', 'so' }
-
--- In practice we will work within one tds tree, but i want to keep
--- the option open to build tools that look at multiple trees, which is
--- why we keep the tree specific data in a table. We used to pass the
--- instance but for practical pusposes we now avoid this and use a
--- instance variable.
-
--- here we catch a few new thingies (todo: add these paths to context.tmf)
---
--- FONTFEATURES = .;$TEXMF/fonts/fea//
--- FONTCIDMAPS = .;$TEXMF/fonts/cid//
-
--- we always have one instance active
-
resolvers.instance = resolvers.instance or nil -- the current one (slow access)
-local instance = resolvers.instance or nil -- the current one (fast access)
+local instance = resolvers.instance or nil -- the current one (fast access)
function resolvers.newinstance()
- -- store once, freeze and faster (once reset we can best use
- -- instance.environment) maybe better have a register suffix
- -- function
-
- for k, v in next, suffixes do
- for i=1,#v do
- local vi = v[i]
- if vi then
- suffixmap[vi] = k
- end
- end
- end
-
- -- because vf searching is somewhat dangerous, we want to prevent
- -- too liberal searching esp because we do a lookup on the current
- -- path anyway; only tex (or any) is safe
-
- for k, v in next, formats do
- dangerous[k] = true
- end
- dangerous.tex = nil
-
- -- the instance
-
local newinstance = {
- rootpath = '',
- treepath = '',
progname = 'context',
engine = 'luatex',
format = '',
@@ -8322,26 +9558,19 @@ function resolvers.newinstance()
variables = { },
expansions = { },
files = { },
- remap = { },
- configuration = { },
- setup = { },
+ setups = { },
order = { },
found = { },
foundintrees = { },
- kpsevars = { },
+ origins = { },
hashes = { },
- cnffiles = { },
- luafiles = { },
+ specification = { },
lists = { },
remember = true,
diskcache = true,
renewcache = false,
- scandisk = true,
- cachepath = nil,
loaderror = false,
- sortdata = false,
savelists = true,
- cleanuppaths = true,
allresults = false,
pattern = nil, -- lists
data = { }, -- only for loading
@@ -8351,8 +9580,8 @@ function resolvers.newinstance()
local ne = newinstance.environment
- for k,v in next, os.env do
- ne[k] = resolvers.bare_variable(v)
+ for k, v in next, osenv do
+ ne[upper(k)] = checked_variable(v)
end
return newinstance
@@ -8374,91 +9603,68 @@ local function reset_hashes()
instance.found = { }
end
-local function check_configuration() -- not yet ok, no time for debugging now
- local ie, iv = instance.environment, instance.variables
- local function fix(varname,default)
- local proname = varname .. "." .. instance.progname or "crap"
- local p, v = ie[proname], ie[varname] or iv[varname]
- if not ((p and p ~= "") or (v and v ~= "")) then
- iv[varname] = default -- or environment?
- end
- end
- local name = os.name
- if name == "windows" then
- fix("OSFONTDIR", "c:/windows/fonts//")
- elseif name == "macosx" then
- fix("OSFONTDIR", "$HOME/Library/Fonts//;/Library/Fonts//;/System/Library/Fonts//")
- else
- -- bad luck
- end
- fix("LUAINPUTS" , ".;$TEXINPUTS;$TEXMFSCRIPTS") -- no progname, hm
- -- this will go away some day
- fix("FONTFEATURES", ".;$TEXMF/fonts/{data,fea}//;$OPENTYPEFONTS;$TTFONTS;$T1FONTS;$AFMFONTS")
- fix("FONTCIDMAPS" , ".;$TEXMF/fonts/{data,cid}//;$OPENTYPEFONTS;$TTFONTS;$T1FONTS;$AFMFONTS")
- --
- fix("LUATEXLIBS" , ".;$TEXMF/luatex/lua//")
-end
-
-function resolvers.bare_variable(str) -- assumes str is a string
- return (gsub(str,"\s*([\"\']?)(.+)%1\s*", "%2"))
-end
-
-function resolvers.settrace(n) -- no longer number but: 'locating' or 'detail'
- if n then
- trackers.disable("resolvers.*")
- trackers.enable("resolvers."..n)
+function resolvers.setenv(key,value)
+ if instance then
+ instance.environment[key] = value
+ ossetenv(key,value)
end
end
-resolvers.settrace(os.getenv("MTX_INPUT_TRACE"))
-
-function resolvers.osenv(key)
- local ie = instance.environment
- local value = ie[key]
- if value == nil then
- -- local e = os.getenv(key)
- local e = os.env[key]
- if e == nil then
- -- value = "" -- false
- else
- value = resolvers.bare_variable(e)
- end
- ie[key] = value
+function resolvers.getenv(key)
+ local value = instance.environment[key]
+ if value and value ~= "" then
+ return value
+ else
+ local e = osgetenv(key)
+ return e ~= nil and e ~= "" and checked_variable(e) or ""
end
- return value or ""
-end
-
-function resolvers.env(key)
- return instance.environment[key] or resolvers.osenv(key)
end
---
+resolvers.env = resolvers.getenv
local function expand_vars(lst) -- simple vars
- local variables, env = instance.variables, resolvers.env
+ local variables, getenv = instance.variables, resolvers.getenv
local function resolve(a)
- return variables[a] or env(a)
+ local va = variables[a] or ""
+ return (va ~= "" and va) or getenv(a) or ""
end
for k=1,#lst do
- lst[k] = gsub(lst[k],"%$([%a%d%_%-]+)",resolve)
+ local var = lst[k]
+ var = gsub(var,"%$([%a%d%_%-]+)",resolve)
+ var = gsub(var,";+",";")
+ var = gsub(var,";[!{}/\\]+;",";")
+ lst[k] = var
end
end
-local function expanded_var(var) -- simple vars
- local function resolve(a)
- return instance.variables[a] or resolvers.env(a)
+local function resolve(key)
+ local value = instance.variables[key]
+ if value and value ~= "" then
+ return value
end
- return (gsub(var,"%$([%a%d%_%-]+)",resolve))
+ local value = instance.environment[key]
+ if value and value ~= "" then
+ return value
+ end
+ local e = osgetenv(key)
+ return e ~= nil and e ~= "" and checked_variable(e) or ""
+end
+
+local function expanded_var(var) -- simple vars
+ var = gsub(var,"%$([%a%d%_%-]+)",resolve)
+ var = gsub(var,";+",";")
+ var = gsub(var,";[!{}/\\]+;",";")
+ return var
end
local function entry(entries,name)
- if name and (name ~= "") then
+ if name and name ~= "" then
name = gsub(name,'%$','')
local result = entries[name..'.'..instance.progname] or entries[name]
if result then
return result
else
- result = resolvers.env(name)
+ result = resolvers.getenv(name)
if result then
instance.variables[name] = result
resolvers.expand_variables()
@@ -8478,438 +9684,147 @@ local function is_entry(entries,name)
end
end
--- {a,b,c,d}
--- a,b,c/{p,q,r},d
--- a,b,c/{p,q,r}/d/{x,y,z}//
--- a,b,c/{p,q/{x,y,z},r},d/{p,q,r}
--- a,b,c/{p,q/{x,y,z},r},d/{p,q,r}
--- a{b,c}{d,e}f
--- {a,b,c,d}
--- {a,b,c/{p,q,r},d}
--- {a,b,c/{p,q,r}/d/{x,y,z}//}
--- {a,b,c/{p,q/{x,y,z}},d/{p,q,r}}
--- {a,b,c/{p,q/{x,y,z},w}v,d/{p,q,r}}
--- {$SELFAUTODIR,$SELFAUTOPARENT}{,{/share,}/texmf{-local,.local,}/web2c}
-
--- this one is better and faster, but it took me a while to realize
--- that this kind of replacement is cleaner than messy parsing and
--- fuzzy concatenating we can probably gain a bit with selectively
--- applying lpeg, but experiments with lpeg parsing this proved not to
--- work that well; the parsing is ok, but dealing with the resulting
--- table is a pain because we need to work inside-out recursively
-
-local function do_first(a,b)
- local t = { }
- for s in gmatch(b,"[^,]+") do t[#t+1] = a .. s end
- return "{" .. concat(t,",") .. "}"
-end
-
-local function do_second(a,b)
- local t = { }
- for s in gmatch(a,"[^,]+") do t[#t+1] = s .. b end
- return "{" .. concat(t,",") .. "}"
-end
-
-local function do_both(a,b)
- local t = { }
- for sa in gmatch(a,"[^,]+") do
- for sb in gmatch(b,"[^,]+") do
- t[#t+1] = sa .. sb
- end
- end
- return "{" .. concat(t,",") .. "}"
-end
-
-local function do_three(a,b,c)
- return a .. b.. c
-end
-
-local function splitpathexpr(str, t, validate)
- -- no need for further optimization as it is only called a
- -- few times, we can use lpeg for the sub
- if trace_expansions then
- logs.report("fileio","expanding variable '%s'",str)
- end
- t = t or { }
- str = gsub(str,",}",",@}")
- str = gsub(str,"{,","{@,")
- -- str = "@" .. str .. "@"
- local ok, done
- while true do
- done = false
- while true do
- str, ok = gsub(str,"([^{},]+){([^{}]+)}",do_first)
- if ok > 0 then done = true else break end
- end
- while true do
- str, ok = gsub(str,"{([^{}]+)}([^{},]+)",do_second)
- if ok > 0 then done = true else break end
- end
- while true do
- str, ok = gsub(str,"{([^{}]+)}{([^{}]+)}",do_both)
- if ok > 0 then done = true else break end
- end
- str, ok = gsub(str,"({[^{}]*){([^{}]+)}([^{}]*})",do_three)
- if ok > 0 then done = true end
- if not done then break end
- end
- str = gsub(str,"[{}]", "")
- str = gsub(str,"@","")
- if validate then
- for s in gmatch(str,"[^,]+") do
- s = validate(s)
- if s then t[#t+1] = s end
- end
- else
- for s in gmatch(str,"[^,]+") do
- t[#t+1] = s
- end
- end
- if trace_expansions then
- for k=1,#t do
- logs.report("fileio","% 4i: %s",k,t[k])
- end
- end
- return t
-end
-
-local function expanded_path_from_list(pathlist) -- maybe not a list, just a path
- -- a previous version fed back into pathlist
- local newlist, ok = { }, false
- for k=1,#pathlist do
- if find(pathlist[k],"[{}]") then
- ok = true
- break
- end
- end
- if ok then
- local function validate(s)
- s = file.collapse_path(s)
- return s ~= "" and not find(s,dummy_path_expr) and s
- end
- for k=1,#pathlist do
- splitpathexpr(pathlist[k],newlist,validate)
- end
- else
- for k=1,#pathlist do
- for p in gmatch(pathlist[k],"([^,]+)") do
- p = file.collapse_path(p)
- if p ~= "" then newlist[#newlist+1] = p end
- end
- end
- end
- return newlist
-end
-
--- we follow a rather traditional approach:
---
--- (1) texmf.cnf given in TEXMFCNF
--- (2) texmf.cnf searched in default variable
---
--- also we now follow the stupid route: if not set then just assume *one*
--- cnf file under texmf (i.e. distribution)
-
-local args = environment and environment.original_arguments or arg -- this needs a cleanup
-
-resolvers.ownbin = resolvers.ownbin or args[-2] or arg[-2] or args[-1] or arg[-1] or arg[0] or "luatex"
-resolvers.ownbin = gsub(resolvers.ownbin,"\\","/")
-
-function resolvers.getownpath()
- local ownpath = resolvers.ownpath or os.selfdir
- if not ownpath or ownpath == "" or ownpath == "unset" then
- ownpath = args[-1] or arg[-1]
- ownpath = ownpath and file.dirname(gsub(ownpath,"\\","/"))
- if not ownpath or ownpath == "" then
- ownpath = args[-0] or arg[-0]
- ownpath = ownpath and file.dirname(gsub(ownpath,"\\","/"))
- end
- local binary = resolvers.ownbin
- if not ownpath or ownpath == "" then
- ownpath = ownpath and file.dirname(binary)
- end
- if not ownpath or ownpath == "" then
- if os.binsuffix ~= "" then
- binary = file.replacesuffix(binary,os.binsuffix)
- end
- for p in gmatch(os.getenv("PATH"),"[^"..io.pathseparator.."]+") do
- local b = file.join(p,binary)
- if lfs.isfile(b) then
- -- we assume that after changing to the path the currentdir function
- -- resolves to the real location and use this side effect here; this
- -- trick is needed because on the mac installations use symlinks in the
- -- path instead of real locations
- local olddir = lfs.currentdir()
- if lfs.chdir(p) then
- local pp = lfs.currentdir()
- if trace_locating and p ~= pp then
- logs.report("fileio","following symlink '%s' to '%s'",p,pp)
- end
- ownpath = pp
- lfs.chdir(olddir)
- else
- if trace_locating then
- logs.report("fileio","unable to check path '%s'",p)
- end
- ownpath = p
- end
- break
- end
- end
- end
- if not ownpath or ownpath == "" then
- ownpath = "."
- logs.report("fileio","forcing fallback ownpath .")
- elseif trace_locating then
- logs.report("fileio","using ownpath '%s'",ownpath)
- end
- end
- resolvers.ownpath = ownpath
- function resolvers.getownpath()
- return resolvers.ownpath
- end
- return ownpath
-end
-
-local own_places = { "SELFAUTOLOC", "SELFAUTODIR", "SELFAUTOPARENT", "TEXMFCNF" }
-
-local function identify_own()
- local ownpath = resolvers.getownpath() or dir.current()
- local ie = instance.environment
- if ownpath then
- if resolvers.env('SELFAUTOLOC') == "" then os.env['SELFAUTOLOC'] = file.collapse_path(ownpath) end
- if resolvers.env('SELFAUTODIR') == "" then os.env['SELFAUTODIR'] = file.collapse_path(ownpath .. "/..") end
- if resolvers.env('SELFAUTOPARENT') == "" then os.env['SELFAUTOPARENT'] = file.collapse_path(ownpath .. "/../..") end
- else
- logs.report("fileio","error: unable to locate ownpath")
- os.exit()
- end
- if resolvers.env('TEXMFCNF') == "" then os.env['TEXMFCNF'] = resolvers.cnfdefault end
- if resolvers.env('TEXOS') == "" then os.env['TEXOS'] = resolvers.env('SELFAUTODIR') end
- if resolvers.env('TEXROOT') == "" then os.env['TEXROOT'] = resolvers.env('SELFAUTOPARENT') end
+function resolvers.report_critical_variables()
if trace_locating then
- for i=1,#own_places do
- local v = own_places[i]
- logs.report("fileio","variable '%s' set to '%s'",v,resolvers.env(v) or "unknown")
+ for i=1,#resolvers.criticalvars do
+ local v = resolvers.criticalvars[i]
+ report_resolvers("variable '%s' set to '%s'",v,resolvers.getenv(v) or "unknown")
end
+ report_resolvers()
end
- identify_own = function() end
+ resolvers.report_critical_variables = function() end
end
-function resolvers.identify_cnf()
- if #instance.cnffiles == 0 then
- -- fallback
- identify_own()
- -- the real search
- resolvers.expand_variables()
- local t = resolvers.split_path(resolvers.env('TEXMFCNF'))
- t = expanded_path_from_list(t)
- expand_vars(t) -- redundant
- local function locate(filename,list)
- for i=1,#t do
- local ti = t[i]
- local texmfcnf = file.collapse_path(file.join(ti,filename))
- if lfs.isfile(texmfcnf) then
- list[#list+1] = texmfcnf
- end
- end
- end
- locate(resolvers.luaname,instance.luafiles)
- locate(resolvers.cnfname,instance.cnffiles)
- end
-end
-
-local function load_cnf_file(fname)
- fname = resolvers.clean_path(fname)
- local lname = file.replacesuffix(fname,'lua')
- if lfs.isfile(lname) then
- local dname = file.dirname(fname) -- fname ?
- if not instance.configuration[dname] then
- resolvers.load_data(dname,'configuration',lname and file.basename(lname))
- instance.order[#instance.order+1] = instance.configuration[dname]
+local function identify_configuration_files()
+ local specification = instance.specification
+ if #specification == 0 then
+ local cnfspec = resolvers.getenv('TEXMFCNF')
+ if cnfspec == "" then
+ cnfspec = resolvers.luacnfspec
+ resolvers.luacnfstate = "default"
+ else
+ resolvers.luacnfstate = "environment"
end
- else
- f = io.open(fname)
- if f then
- if trace_locating then
- logs.report("fileio","loading configuration file %s", fname)
- end
- local line, data, n, k, v
- local dname = file.dirname(fname)
- if not instance.configuration[dname] then
- instance.configuration[dname] = { }
- instance.order[#instance.order+1] = instance.configuration[dname]
- end
- local data = instance.configuration[dname]
- while true do
- local line, n = f:read(), 0
- if line then
- while true do -- join lines
- line, n = gsub(line,"\\%s*$", "")
- if n > 0 then
- line = line .. f:read()
- else
- break
+ resolvers.report_critical_variables()
+ resolvers.expand_variables()
+ local cnfpaths = expanded_path_from_list(resolvers.split_path(cnfspec))
+ expand_vars(cnfpaths) --- hm
+ local luacnfname = resolvers.luacnfname
+ for i=1,#cnfpaths do
+ local filename = collapse_path(filejoin(cnfpaths[i],luacnfname))
+ if lfs.isfile(filename) then
+ specification[#specification+1] = filename
+ end
+ end
+ end
+end
+
+local function load_configuration_files()
+ local specification = instance.specification
+ if #specification > 0 then
+ local luacnfname = resolvers.luacnfname
+ for i=1,#specification do
+ local filename = specification[i]
+ local pathname = filedirname(filename)
+ local filename = filejoin(pathname,luacnfname)
+ local blob = loadfile(filename)
+ if blob then
+ local data = blob()
+ data = data and data.content
+ local setups = instance.setups
+ if data then
+ if trace_locating then
+ report_resolvers("loading configuration file '%s'",filename)
+ report_resolvers()
+ end
+ -- flattening is easier to deal with as we need to collapse
+ local t = { }
+ for k, v in next, data do -- v = progname
+ if v ~= unset_variable then
+ local kind = type(v)
+ if kind == "string" then
+ t[k] = v
+ elseif kind == "table" then
+ -- this operates on the table directly
+ setters.initialize(filename,k,v)
+ -- this doesn't (maybe metatables some day)
+ for kk, vv in next, v do -- vv = variable
+ if vv ~= unset_variable then
+ if type(vv) == "string" then
+ t[kk.."."..k] = vv
+ end
+ end
+ end
+ else
+ -- report_resolvers("strange key '%s' in configuration file '%s'",k,filename)
+ end
end
end
- if not find(line,"^[%%#]") then
- local l = gsub(line,"%s*%%.*$","")
- local k, v = match(l,"%s*(.-)%s*=%s*(.-)%s*$")
- if k and v and not data[k] then
- v = gsub(v,"[%%#].*",'')
- data[k] = gsub(v,"~","$HOME")
- instance.kpsevars[k] = true
+ setups[pathname] = t
+
+ if resolvers.luacnfstate == "default" then
+ -- the following code is not tested
+ local cnfspec = t["TEXMFCNF"]
+ if cnfspec then
+ -- we push the value into the main environment (osenv) so
+ -- that it takes precedence over the default one and therefore
+ -- also over following definitions
+ resolvers.setenv('TEXMFCNF',cnfspec)
+ -- we now identify and load the specified configuration files
+ instance.specification = { }
+ identify_configuration_files()
+ load_configuration_files()
+ -- we prevent further overload of the configuration variable
+ resolvers.luacnfstate = "configuration"
+ -- we quit the outer loop
+ break
end
end
+
else
- break
+ if trace_locating then
+ report_resolvers("skipping configuration file '%s'",filename)
+ end
+ setups[pathname] = { }
+ instance.loaderror = true
end
+ elseif trace_locating then
+ report_resolvers("skipping configuration file '%s'",filename)
+ end
+ instance.order[#instance.order+1] = instance.setups[pathname]
+ if instance.loaderror then
+ break
end
- f:close()
- elseif trace_locating then
- logs.report("fileio","skipping configuration file '%s'", fname)
end
+ elseif trace_locating then
+ report_resolvers("warning: no lua configuration files found")
end
end
-local function collapse_cnf_data() -- potential optimization: pass start index (setup and configuration are shared)
- local order = instance.order
+local function collapse_configuration_data() -- potential optimization: pass start index (setup and configuration are shared)
+ local order, variables, environment, origins = instance.order, instance.variables, instance.environment, instance.origins
for i=1,#order do
local c = order[i]
for k,v in next, c do
- if not instance.variables[k] then
- if instance.environment[k] then
- instance.variables[k] = instance.environment[k]
+ if variables[k] then
+ -- okay
+ else
+ local ek = environment[k]
+ if ek and ek ~= "" then
+ variables[k], origins[k] = ek, "env"
else
- instance.kpsevars[k] = true
- instance.variables[k] = resolvers.bare_variable(v)
+ local bv = checked_variable(v)
+ variables[k], origins[k] = bv, "cnf"
end
end
end
end
end
-function resolvers.load_cnf()
- local function loadoldconfigdata()
- local cnffiles = instance.cnffiles
- for i=1,#cnffiles do
- load_cnf_file(cnffiles[i])
- end
- end
- -- instance.cnffiles contain complete names now !
- -- we still use a funny mix of cnf and new but soon
- -- we will switch to lua exclusively as we only use
- -- the file to collect the tree roots
- if #instance.cnffiles == 0 then
- if trace_locating then
- logs.report("fileio","no cnf files found (TEXMFCNF may not be set/known)")
- end
- else
- local cnffiles = instance.cnffiles
- instance.rootpath = cnffiles[1]
- for k=1,#cnffiles do
- instance.cnffiles[k] = file.collapse_path(cnffiles[k])
- end
- for i=1,3 do
- instance.rootpath = file.dirname(instance.rootpath)
- end
- instance.rootpath = file.collapse_path(instance.rootpath)
- if instance.diskcache and not instance.renewcache then
- resolvers.loadoldconfig(instance.cnffiles)
- if instance.loaderror then
- loadoldconfigdata()
- resolvers.saveoldconfig()
- end
- else
- loadoldconfigdata()
- if instance.renewcache then
- resolvers.saveoldconfig()
- end
- end
- collapse_cnf_data()
- end
- check_configuration()
-end
-
-function resolvers.load_lua()
- if #instance.luafiles == 0 then
- -- yet harmless
- else
- instance.rootpath = instance.luafiles[1]
- local luafiles = instance.luafiles
- for k=1,#luafiles do
- instance.luafiles[k] = file.collapse_path(luafiles[k])
- end
- for i=1,3 do
- instance.rootpath = file.dirname(instance.rootpath)
- end
- instance.rootpath = file.collapse_path(instance.rootpath)
- resolvers.loadnewconfig()
- collapse_cnf_data()
- end
- check_configuration()
-end
-
-- database loading
-function resolvers.load_hash()
- resolvers.locatelists()
- if instance.diskcache and not instance.renewcache then
- resolvers.loadfiles()
- if instance.loaderror then
- resolvers.loadlists()
- resolvers.savefiles()
- end
- else
- resolvers.loadlists()
- if instance.renewcache then
- resolvers.savefiles()
- end
- end
-end
-
-function resolvers.append_hash(type,tag,name)
- if trace_locating then
- logs.report("fileio","hash '%s' appended",tag)
- end
- insert(instance.hashes, { ['type']=type, ['tag']=tag, ['name']=name } )
-end
-
-function resolvers.prepend_hash(type,tag,name)
- if trace_locating then
- logs.report("fileio","hash '%s' prepended",tag)
- end
- insert(instance.hashes, 1, { ['type']=type, ['tag']=tag, ['name']=name } )
-end
-
-function resolvers.extend_texmf_var(specification) -- crap, we could better prepend the hash
--- local t = resolvers.expanded_path_list('TEXMF') -- full expansion
- local t = resolvers.split_path(resolvers.env('TEXMF'))
- insert(t,1,specification)
- local newspec = concat(t,";")
- if instance.environment["TEXMF"] then
- instance.environment["TEXMF"] = newspec
- elseif instance.variables["TEXMF"] then
- instance.variables["TEXMF"] = newspec
- else
- -- weird
- end
- resolvers.expand_variables()
- reset_hashes()
-end
-
-- locators
-function resolvers.locatelists()
- local texmfpaths = resolvers.clean_path_list('TEXMF')
- for i=1,#texmfpaths do
- local path = texmfpaths[i]
- if trace_locating then
- logs.report("fileio","locating list of '%s'",path)
- end
- resolvers.locatedatabase(file.collapse_path(path))
- end
-end
-
function resolvers.locatedatabase(specification)
return resolvers.methodhandler('locators', specification)
end
@@ -8917,11 +9832,11 @@ end
function resolvers.locators.tex(specification)
if specification and specification ~= '' and lfs.isdir(specification) then
if trace_locating then
- logs.report("fileio","tex locator '%s' found",specification)
+ report_resolvers("tex locator '%s' found",specification)
end
- resolvers.append_hash('file',specification,filename)
+ resolvers.append_hash('file',specification,filename,true) -- cache
elseif trace_locating then
- logs.report("fileio","tex locator '%s' not found",specification)
+ report_resolvers("tex locator '%s' not found",specification)
end
end
@@ -8931,9 +9846,8 @@ function resolvers.hashdatabase(tag,name)
return resolvers.methodhandler('hashers',tag,name)
end
-function resolvers.loadfiles()
- instance.loaderror = false
- instance.files = { }
+local function load_file_databases()
+ instance.loaderror, instance.files = false, { }
if not instance.renewcache then
local hashes = instance.hashes
for k=1,#hashes do
@@ -8944,194 +9858,134 @@ function resolvers.loadfiles()
end
end
-function resolvers.hashers.tex(tag,name)
- resolvers.load_data(tag,'files')
-end
-
--- generators:
-
-function resolvers.loadlists()
- local hashes = instance.hashes
- for i=1,#hashes do
- resolvers.generatedatabase(hashes[i].tag)
+function resolvers.hashers.tex(tag,name) -- used where?
+ local content = caches.loadcontent(tag,'files')
+ if content then
+ instance.files[tag] = content
+ else
+ instance.files[tag] = { }
+ instance.loaderror = true
end
end
-function resolvers.generatedatabase(specification)
- return resolvers.methodhandler('generators', specification)
-end
-
--- starting with . or .. etc or funny char
-
-local weird = lpeg.P(".")^1 + lpeg.anywhere(lpeg.S("~`!#$%^&*()={}[]:;\"\'||<>,?\n\r\t"))
-
---~ local l_forbidden = lpeg.S("~`!#$%^&*()={}[]:;\"\'||\\/<>,?\n\r\t")
---~ local l_confusing = lpeg.P(" ")
---~ local l_character = lpeg.patterns.utf8
---~ local l_dangerous = lpeg.P(".")
-
---~ local l_normal = (l_character - l_forbidden - l_confusing - l_dangerous) * (l_character - l_forbidden - l_confusing^2)^0 * lpeg.P(-1)
---~ ----- l_normal = l_normal * lpeg.Cc(true) + lpeg.Cc(false)
-
---~ local function test(str)
---~ print(str,lpeg.match(l_normal,str))
---~ end
---~ test("ヒラギノ明朝 Pro W3")
---~ test("..ヒラギノ明朝 Pro W3")
---~ test(":ヒラギノ明朝 Pro W3;")
---~ test("ヒラギノ明朝 /Pro W3;")
---~ test("ヒラギノ明朝 Pro W3")
-
-function resolvers.generators.tex(specification)
- local tag = specification
- if trace_locating then
- logs.report("fileio","scanning path '%s'",specification)
- end
- instance.files[tag] = { }
- local files = instance.files[tag]
- local n, m, r = 0, 0, 0
- local spec = specification .. '/'
- local attributes = lfs.attributes
- local directory = lfs.dir
- local function action(path)
- local full
- if path then
- full = spec .. path .. '/'
- else
- full = spec
- end
- for name in directory(full) do
- if not lpegmatch(weird,name) then
- -- if lpegmatch(l_normal,name) then
- local mode = attributes(full..name,'mode')
- if mode == 'file' then
- if path then
- n = n + 1
- local f = files[name]
- if f then
- if type(f) == 'string' then
- files[name] = { f, path }
- else
- f[#f+1] = path
- end
- else -- probably unique anyway
- files[name] = path
- local lower = lower(name)
- if name ~= lower then
- files["remap:"..lower] = name
- r = r + 1
- end
- end
+local function locate_file_databases()
+ -- todo: cache:// and tree:// (runtime)
+ local texmfpaths = resolvers.expanded_path_list('TEXMF')
+ for i=1,#texmfpaths do
+ local path = collapse_path(texmfpaths[i])
+ local stripped = gsub(path,"^!!","")
+ local runtime = stripped == path
+ path = resolvers.clean_path(path)
+ if stripped ~= "" then
+ if lfs.isdir(path) then
+ local spec = resolvers.splitmethod(stripped)
+ if spec.scheme == "cache" then
+ stripped = spec.path
+ elseif runtime and (spec.noscheme or spec.scheme == "file") then
+ stripped = "tree:///" .. stripped
+ end
+ if trace_locating then
+ if runtime then
+ report_resolvers("locating list of '%s' (runtime)",path)
+ else
+ report_resolvers("locating list of '%s' (cached)",path)
end
- elseif mode == 'directory' then
- m = m + 1
- if path then
- action(path..'/'..name)
+ end
+ resolvers.locatedatabase(stripped) -- nothing done with result
+ else
+ if trace_locating then
+ if runtime then
+ report_resolvers("skipping list of '%s' (runtime)",path)
else
- action(name)
+ report_resolvers("skipping list of '%s' (cached)",path)
end
end
end
end
end
- action()
if trace_locating then
- logs.report("fileio","%s files found on %s directories with %s uppercase remappings",n,m,r)
+ report_resolvers()
end
end
--- savers, todo
-
-function resolvers.savefiles()
- resolvers.save_data('files')
+local function generate_file_databases()
+ local hashes = instance.hashes
+ for i=1,#hashes do
+ resolvers.methodhandler('generators',hashes[i].tag)
+ end
+ if trace_locating then
+ report_resolvers()
+ end
end
--- A config (optionally) has the paths split in tables. Internally
--- we join them and split them after the expansion has taken place. This
--- is more convenient.
-
---~ local checkedsplit = string.checkedsplit
-
-local cache = { }
-
-local splitter = lpeg.Ct(lpeg.splitat(lpeg.S(os.type == "windows" and ";" or ":;")))
-
-local function split_kpse_path(str) -- beware, this can be either a path or a {specification}
- local found = cache[str]
- if not found then
- if str == "" then
- found = { }
- else
- str = gsub(str,"\\","/")
---~ local split = (find(str,";") and checkedsplit(str,";")) or checkedsplit(str,io.pathseparator)
-local split = lpegmatch(splitter,str)
- found = { }
- for i=1,#split do
- local s = split[i]
- if not find(s,"^{*unset}*") then
- found[#found+1] = s
- end
- end
- if trace_expansions then
- logs.report("fileio","splitting path specification '%s'",str)
- for k=1,#found do
- logs.report("fileio","% 4i: %s",k,found[k])
- end
- end
- cache[str] = found
+local function save_file_databases() -- will become cachers
+ for i=1,#instance.hashes do
+ local hash = instance.hashes[i]
+ local cachename = hash.tag
+ if hash.cache then
+ local content = instance.files[cachename]
+ caches.collapsecontent(content)
+ caches.savecontent(cachename,"files",content)
+ elseif trace_locating then
+ report_resolvers("not saving runtime tree '%s'",cachename)
end
end
- return found
end
-resolvers.split_kpse_path = split_kpse_path
-
-function resolvers.splitconfig()
- for i=1,#instance do
- local c = instance[i]
- for k,v in next, c do
- if type(v) == 'string' then
- local t = split_kpse_path(v)
- if #t > 1 then
- c[k] = t
- end
- end
+local function load_databases()
+ locate_file_databases()
+ if instance.diskcache and not instance.renewcache then
+ load_file_databases()
+ if instance.loaderror then
+ generate_file_databases()
+ save_file_databases()
+ end
+ else
+ generate_file_databases()
+ if instance.renewcache then
+ save_file_databases()
end
end
end
-function resolvers.joinconfig()
- local order = instance.order
- for i=1,#order do
- local c = order[i]
- for k,v in next, c do -- indexed?
- if type(v) == 'table' then
- c[k] = file.join_path(v)
- end
- end
+function resolvers.append_hash(type,tag,name,cache)
+ if trace_locating then
+ report_resolvers("hash '%s' appended",tag)
end
+ insert(instance.hashes, { type = type, tag = tag, name = name, cache = cache } )
end
-function resolvers.split_path(str)
- if type(str) == 'table' then
- return str
- else
- return split_kpse_path(str)
+function resolvers.prepend_hash(type,tag,name,cache)
+ if trace_locating then
+ report_resolvers("hash '%s' prepended",tag)
end
+ insert(instance.hashes, 1, { type = type, tag = tag, name = name, cache = cache } )
end
-function resolvers.join_path(str)
- if type(str) == 'table' then
- return file.join_path(str)
+function resolvers.extend_texmf_var(specification) -- crap, we could better prepend the hash
+-- local t = resolvers.expanded_path_list('TEXMF') -- full expansion
+ local t = resolvers.split_path(resolvers.getenv('TEXMF'))
+ insert(t,1,specification)
+ local newspec = concat(t,";")
+ if instance.environment["TEXMF"] then
+ instance.environment["TEXMF"] = newspec
+ elseif instance.variables["TEXMF"] then
+ instance.variables["TEXMF"] = newspec
else
- return str
+ -- weird
end
+ resolvers.expand_variables()
+ reset_hashes()
+end
+
+function resolvers.generators.tex(specification,tag)
+ instance.files[tag or specification] = resolvers.scan_files(specification)
end
function resolvers.splitexpansions()
local ie = instance.expansions
for k,v in next, ie do
- local t, h, p = { }, { }, split_kpse_path(v)
+ local t, h, p = { }, { }, split_configuration_path(v)
for kk=1,#p do
local vv = p[kk]
if vv ~= "" and not h[vv] then
@@ -9149,219 +10003,16 @@ end
-- end of split/join code
-function resolvers.saveoldconfig()
- resolvers.splitconfig()
- resolvers.save_data('configuration')
- resolvers.joinconfig()
-end
-
-resolvers.configbanner = [[
--- This is a Luatex configuration file created by 'luatools.lua' or
--- 'luatex.exe' directly. For comment, suggestions and questions you can
--- contact the ConTeXt Development Team. This configuration file is
--- not copyrighted. [HH & TH]
-]]
-
-function resolvers.serialize(files)
- -- This version is somewhat optimized for the kind of
- -- tables that we deal with, so it's much faster than
- -- the generic serializer. This makes sense because
- -- luatools and mtxtools are called frequently. Okay,
- -- we pay a small price for properly tabbed tables.
- local t = { }
- local function dump(k,v,m) -- could be moved inline
- if type(v) == 'string' then
- return m .. "['" .. k .. "']='" .. v .. "',"
- elseif #v == 1 then
- return m .. "['" .. k .. "']='" .. v[1] .. "',"
- else
- return m .. "['" .. k .. "']={'" .. concat(v,"','").. "'},"
- end
- end
- t[#t+1] = "return {"
- if instance.sortdata then
- local sortedfiles = sortedkeys(files)
- for i=1,#sortedfiles do
- local k = sortedfiles[i]
- local fk = files[k]
- if type(fk) == 'table' then
- t[#t+1] = "\t['" .. k .. "']={"
- local sortedfk = sortedkeys(fk)
- for j=1,#sortedfk do
- local kk = sortedfk[j]
- t[#t+1] = dump(kk,fk[kk],"\t\t")
- end
- t[#t+1] = "\t},"
- else
- t[#t+1] = dump(k,fk,"\t")
- end
- end
- else
- for k, v in next, files do
- if type(v) == 'table' then
- t[#t+1] = "\t['" .. k .. "']={"
- for kk,vv in next, v do
- t[#t+1] = dump(kk,vv,"\t\t")
- end
- t[#t+1] = "\t},"
- else
- t[#t+1] = dump(k,v,"\t")
- end
- end
- end
- t[#t+1] = "}"
- return concat(t,"\n")
-end
-
-local data_state = { }
+-- we used to have 'files' and 'configurations' so therefore the following
+-- shared function
function resolvers.data_state()
- return data_state or { }
-end
-
-function resolvers.save_data(dataname, makename) -- untested without cache overload
- for cachename, files in next, instance[dataname] do
- local name = (makename or file.join)(cachename,dataname)
- local luaname, lucname = name .. ".lua", name .. ".luc"
- if trace_locating then
- logs.report("fileio","preparing '%s' for '%s'",dataname,cachename)
- end
- for k, v in next, files do
- if type(v) == "table" and #v == 1 then
- files[k] = v[1]
- end
- end
- local data = {
- type = dataname,
- root = cachename,
- version = resolvers.cacheversion,
- date = os.date("%Y-%m-%d"),
- time = os.date("%H:%M:%S"),
- content = files,
- uuid = os.uuid(),
- }
- local ok = io.savedata(luaname,resolvers.serialize(data))
- if ok then
- if trace_locating then
- logs.report("fileio","'%s' saved in '%s'",dataname,luaname)
- end
- if utils.lua.compile(luaname,lucname,false,true) then -- no cleanup but strip
- if trace_locating then
- logs.report("fileio","'%s' compiled to '%s'",dataname,lucname)
- end
- else
- if trace_locating then
- logs.report("fileio","compiling failed for '%s', deleting file '%s'",dataname,lucname)
- end
- os.remove(lucname)
- end
- elseif trace_locating then
- logs.report("fileio","unable to save '%s' in '%s' (access error)",dataname,luaname)
- end
- end
-end
-
-function resolvers.load_data(pathname,dataname,filename,makename) -- untested without cache overload
- filename = ((not filename or (filename == "")) and dataname) or filename
- filename = (makename and makename(dataname,filename)) or file.join(pathname,filename)
- local blob = loadfile(filename .. ".luc") or loadfile(filename .. ".lua")
- if blob then
- local data = blob()
- if data and data.content and data.type == dataname and data.version == resolvers.cacheversion then
- data_state[#data_state+1] = data.uuid
- if trace_locating then
- logs.report("fileio","loading '%s' for '%s' from '%s'",dataname,pathname,filename)
- end
- instance[dataname][pathname] = data.content
- else
- if trace_locating then
- logs.report("fileio","skipping '%s' for '%s' from '%s'",dataname,pathname,filename)
- end
- instance[dataname][pathname] = { }
- instance.loaderror = true
- end
- elseif trace_locating then
- logs.report("fileio","skipping '%s' for '%s' from '%s'",dataname,pathname,filename)
- end
-end
-
--- some day i'll use the nested approach, but not yet (actually we even drop
--- engine/progname support since we have only luatex now)
---
--- first texmfcnf.lua files are located, next the cached texmf.cnf files
---
--- return {
--- TEXMFBOGUS = 'effe checken of dit werkt',
--- }
-
-function resolvers.resetconfig()
- identify_own()
- instance.configuration, instance.setup, instance.order, instance.loaderror = { }, { }, { }, false
-end
-
-function resolvers.loadnewconfig()
- local luafiles = instance.luafiles
- for i=1,#luafiles do
- local cnf = luafiles[i]
- local pathname = file.dirname(cnf)
- local filename = file.join(pathname,resolvers.luaname)
- local blob = loadfile(filename)
- if blob then
- local data = blob()
- if data then
- if trace_locating then
- logs.report("fileio","loading configuration file '%s'",filename)
- end
- if true then
- -- flatten to variable.progname
- local t = { }
- for k, v in next, data do -- v = progname
- if type(v) == "string" then
- t[k] = v
- else
- for kk, vv in next, v do -- vv = variable
- if type(vv) == "string" then
- t[vv.."."..v] = kk
- end
- end
- end
- end
- instance['setup'][pathname] = t
- else
- instance['setup'][pathname] = data
- end
- else
- if trace_locating then
- logs.report("fileio","skipping configuration file '%s'",filename)
- end
- instance['setup'][pathname] = { }
- instance.loaderror = true
- end
- elseif trace_locating then
- logs.report("fileio","skipping configuration file '%s'",filename)
- end
- instance.order[#instance.order+1] = instance.setup[pathname]
- if instance.loaderror then break end
- end
-end
-
-function resolvers.loadoldconfig()
- if not instance.renewcache then
- local cnffiles = instance.cnffiles
- for i=1,#cnffiles do
- local cnf = cnffiles[i]
- local dname = file.dirname(cnf)
- resolvers.load_data(dname,'configuration')
- instance.order[#instance.order+1] = instance.configuration[dname]
- if instance.loaderror then break end
- end
- end
- resolvers.joinconfig()
+ return caches.contentstate()
end
function resolvers.expand_variables()
local expansions, environment, variables = { }, instance.environment, instance.variables
- local env = resolvers.env
+ local getenv = resolvers.getenv
instance.expansions = expansions
local engine, progname = instance.engine, instance.progname
if type(engine) ~= "string" then instance.engine, engine = "", "" end
@@ -9376,7 +10027,7 @@ function resolvers.expand_variables()
expansions[k] = v
end
end
- for k,v in next, environment do -- move environment to expansions
+ for k,v in next, environment do -- move environment to expansions (variables are already in there)
if not expansions[k] then expansions[k] = v end
end
for k,v in next, variables do -- move variables to expansions
@@ -9385,7 +10036,7 @@ function resolvers.expand_variables()
local busy = false
local function resolve(a)
busy = true
- return expansions[a] or env(a)
+ return expansions[a] or getenv(a)
end
while true do
busy = false
@@ -9393,6 +10044,8 @@ function resolvers.expand_variables()
local s, n = gsub(v,"%$([%a%d%_%-]+)",resolve)
local s, m = gsub(s,"%$%{([%a%d%_%-]+)%}",resolve)
if n > 0 or m > 0 then
+ s = gsub(s,";+",";")
+ s = gsub(s,";[!{}/\\]+;",";")
expansions[k]= s
end
end
@@ -9429,63 +10082,59 @@ function resolvers.unexpanded_path(str)
return file.join_path(resolvers.unexpanded_path_list(str))
end
-do -- no longer needed
+local done = { }
- local done = { }
-
- function resolvers.reset_extra_path()
- local ep = instance.extra_paths
- if not ep then
- ep, done = { }, { }
- instance.extra_paths = ep
- elseif #ep > 0 then
- instance.lists, done = { }, { }
- end
+function resolvers.reset_extra_path()
+ local ep = instance.extra_paths
+ if not ep then
+ ep, done = { }, { }
+ instance.extra_paths = ep
+ elseif #ep > 0 then
+ instance.lists, done = { }, { }
end
+end
- function resolvers.register_extra_path(paths,subpaths)
- local ep = instance.extra_paths or { }
- local n = #ep
- if paths and paths ~= "" then
- if subpaths and subpaths ~= "" then
- for p in gmatch(paths,"[^,]+") do
- -- we gmatch each step again, not that fast, but used seldom
- for s in gmatch(subpaths,"[^,]+") do
- local ps = p .. "/" .. s
- if not done[ps] then
- ep[#ep+1] = resolvers.clean_path(ps)
- done[ps] = true
- end
- end
- end
- else
- for p in gmatch(paths,"[^,]+") do
- if not done[p] then
- ep[#ep+1] = resolvers.clean_path(p)
- done[p] = true
- end
- end
- end
- elseif subpaths and subpaths ~= "" then
- for i=1,n do
+function resolvers.register_extra_path(paths,subpaths)
+ local ep = instance.extra_paths or { }
+ local n = #ep
+ if paths and paths ~= "" then
+ if subpaths and subpaths ~= "" then
+ for p in gmatch(paths,"[^,]+") do
-- we gmatch each step again, not that fast, but used seldom
for s in gmatch(subpaths,"[^,]+") do
- local ps = ep[i] .. "/" .. s
+ local ps = p .. "/" .. s
if not done[ps] then
ep[#ep+1] = resolvers.clean_path(ps)
done[ps] = true
end
end
end
+ else
+ for p in gmatch(paths,"[^,]+") do
+ if not done[p] then
+ ep[#ep+1] = resolvers.clean_path(p)
+ done[p] = true
+ end
+ end
end
- if #ep > 0 then
- instance.extra_paths = ep -- register paths
- end
- if #ep > n then
- instance.lists = { } -- erase the cache
+ elseif subpaths and subpaths ~= "" then
+ for i=1,n do
+ -- we gmatch each step again, not that fast, but used seldom
+ for s in gmatch(subpaths,"[^,]+") do
+ local ps = ep[i] .. "/" .. s
+ if not done[ps] then
+ ep[#ep+1] = resolvers.clean_path(ps)
+ done[ps] = true
+ end
+ end
end
end
-
+ if #ep > 0 then
+ instance.extra_paths = ep -- register paths
+ end
+ if #ep > n then
+ instance.lists = { } -- erase the cache
+ end
end
local function made_list(instance,list)
@@ -9530,7 +10179,7 @@ function resolvers.clean_path_list(str)
local t = resolvers.expanded_path_list(str)
if t then
for i=1,#t do
- t[i] = file.collapse_path(resolvers.clean_path(t[i]))
+ t[i] = collapse_path(resolvers.clean_path(t[i]))
end
end
return t
@@ -9570,33 +10219,6 @@ function resolvers.expand_path_from_var(str)
return file.join_path(resolvers.expanded_path_list_from_var(str))
end
-function resolvers.format_of_var(str)
- return formats[str] or formats[alternatives[str]] or ''
-end
-function resolvers.format_of_suffix(str)
- return suffixmap[file.extname(str)] or 'tex'
-end
-
-function resolvers.variable_of_format(str)
- return formats[str] or formats[alternatives[str]] or ''
-end
-
-function resolvers.var_of_format_or_suffix(str)
- local v = formats[str]
- if v then
- return v
- end
- v = formats[alternatives[str]]
- if v then
- return v
- end
- v = suffixmap[file.extname(str)]
- if v then
- return formats[isf]
- end
- return ''
-end
-
function resolvers.expand_braces(str) -- output variable and brace expansion of STRING
local ori = resolvers.variable(str)
local pth = expanded_path_from_list(resolvers.split_path(ori))
@@ -9609,9 +10231,9 @@ function resolvers.isreadable.file(name)
local readable = lfs.isfile(name) -- brrr
if trace_detail then
if readable then
- logs.report("fileio","file '%s' is readable",name)
+ report_resolvers("file '%s' is readable",name)
else
- logs.report("fileio","file '%s' is not readable", name)
+ report_resolvers("file '%s' is not readable", name)
end
end
return readable
@@ -9627,10 +10249,10 @@ local function collect_files(names)
for k=1,#names do
local fname = names[k]
if trace_detail then
- logs.report("fileio","checking name '%s'",fname)
+ report_resolvers("checking name '%s'",fname)
end
- local bname = file.basename(fname)
- local dname = file.dirname(fname)
+ local bname = filebasename(fname)
+ local dname = filedirname(fname)
if dname == "" or find(dname,"^%.") then
dname = false
else
@@ -9643,7 +10265,7 @@ local function collect_files(names)
local files = blobpath and instance.files[blobpath]
if files then
if trace_detail then
- logs.report("fileio","deep checking '%s' (%s)",blobpath,bname)
+ report_resolvers("deep checking '%s' (%s)",blobpath,bname)
end
local blobfile = files[bname]
if not blobfile then
@@ -9655,53 +10277,38 @@ local function collect_files(names)
end
end
if blobfile then
+ local blobroot = files.__path__ or blobpath
if type(blobfile) == 'string' then
if not dname or find(blobfile,dname) then
- filelist[#filelist+1] = {
- hash.type,
- file.join(blobpath,blobfile,bname), -- search
- resolvers.concatinators[hash.type](blobpath,blobfile,bname) -- result
- }
+ local kind = hash.type
+ local search = filejoin(blobpath,blobfile,bname)
+ local result = resolvers.concatinators[hash.type](blobroot,blobfile,bname)
+ if trace_detail then
+ report_resolvers("match: kind '%s', search '%s', result '%s'",kind,search,result)
+ end
+ filelist[#filelist+1] = { kind, search, result }
end
else
for kk=1,#blobfile do
local vv = blobfile[kk]
if not dname or find(vv,dname) then
- filelist[#filelist+1] = {
- hash.type,
- file.join(blobpath,vv,bname), -- search
- resolvers.concatinators[hash.type](blobpath,vv,bname) -- result
- }
+ local kind = hash.type
+ local search = filejoin(blobpath,vv,bname)
+ local result = resolvers.concatinators[hash.type](blobroot,vv,bname)
+ if trace_detail then
+ report_resolvers("match: kind '%s', search '%s', result '%s'",kind,search,result)
+ end
+ filelist[#filelist+1] = { kind, search, result }
end
end
end
end
elseif trace_locating then
- logs.report("fileio","no match in '%s' (%s)",blobpath,bname)
+ report_resolvers("no match in '%s' (%s)",blobpath,bname)
end
end
end
- if #filelist > 0 then
- return filelist
- else
- return nil
- end
-end
-
-function resolvers.suffix_of_format(str)
- if suffixes[str] then
- return suffixes[str][1]
- else
- return ""
- end
-end
-
-function resolvers.suffixes_of_format(str)
- if suffixes[str] then
- return suffixes[str]
- else
- return {}
- end
+ return #filelist > 0 and filelist or nil
end
function resolvers.register_in_trees(name)
@@ -9721,27 +10328,28 @@ local function can_be_dir(name) -- can become local
fakepaths[name] = 2 -- no directory
end
end
- return (fakepaths[name] == 1)
+ return fakepaths[name] == 1
end
local function collect_instance_files(filename,collected) -- todo : plugin (scanners, checkers etc)
local result = collected or { }
local stamp = nil
- filename = file.collapse_path(filename)
+ filename = collapse_path(filename)
-- speed up / beware: format problem
if instance.remember then
stamp = filename .. "--" .. instance.engine .. "--" .. instance.progname .. "--" .. instance.format
if instance.found[stamp] then
if trace_locating then
- logs.report("fileio","remembering file '%s'",filename)
+ report_resolvers("remembering file '%s'",filename)
end
+ resolvers.register_in_trees(filename) -- for tracing used files
return instance.found[stamp]
end
end
if not dangerous[instance.format or "?"] then
if resolvers.isreadable.file(filename) then
if trace_detail then
- logs.report("fileio","file '%s' found directly",filename)
+ report_resolvers("file '%s' found directly",filename)
end
instance.found[stamp] = { filename }
return { filename }
@@ -9749,37 +10357,39 @@ local function collect_instance_files(filename,collected) -- todo : plugin (scan
end
if find(filename,'%*') then
if trace_locating then
- logs.report("fileio","checking wildcard '%s'", filename)
+ report_resolvers("checking wildcard '%s'", filename)
end
result = resolvers.find_wildcard_files(filename)
elseif file.is_qualified_path(filename) then
if resolvers.isreadable.file(filename) then
if trace_locating then
- logs.report("fileio","qualified name '%s'", filename)
+ report_resolvers("qualified name '%s'", filename)
end
result = { filename }
else
- local forcedname, ok, suffix = "", false, file.extname(filename)
+ local forcedname, ok, suffix = "", false, fileextname(filename)
if suffix == "" then -- why
if instance.format == "" then
forcedname = filename .. ".tex"
if resolvers.isreadable.file(forcedname) then
if trace_locating then
- logs.report("fileio","no suffix, forcing standard filetype 'tex'")
+ report_resolvers("no suffix, forcing standard filetype 'tex'")
end
result, ok = { forcedname }, true
end
else
- local suffixes = resolvers.suffixes_of_format(instance.format)
- for i=1,#suffixes do
- local s = suffixes[i]
- forcedname = filename .. "." .. s
- if resolvers.isreadable.file(forcedname) then
- if trace_locating then
- logs.report("fileio","no suffix, forcing format filetype '%s'", s)
+ local format_suffixes = suffixes[instance.format]
+ if format_suffixes then
+ for i=1,#format_suffixes do
+ local s = format_suffixes[i]
+ forcedname = filename .. "." .. s
+ if resolvers.isreadable.file(forcedname) then
+ if trace_locating then
+ report_resolvers("no suffix, forcing format filetype '%s'", s)
+ end
+ result, ok = { forcedname }, true
+ break
end
- result, ok = { forcedname }, true
- break
end
end
end
@@ -9787,7 +10397,7 @@ local function collect_instance_files(filename,collected) -- todo : plugin (scan
if not ok and suffix ~= "" then
-- try to find in tree (no suffix manipulation), here we search for the
-- matching last part of the name
- local basename = file.basename(filename)
+ local basename = filebasename(filename)
local pattern = gsub(filename .. "$","([%.%-])","%%%1")
local savedformat = instance.format
local format = savedformat or ""
@@ -9828,16 +10438,13 @@ local function collect_instance_files(filename,collected) -- todo : plugin (scan
-- end
end
if not ok and trace_locating then
- logs.report("fileio","qualified name '%s'", filename)
+ report_resolvers("qualified name '%s'", filename)
end
end
else
-- search spec
- local filetype, extra, done, wantedfiles, ext = '', nil, false, { }, file.extname(filename)
+ local filetype, extra, done, wantedfiles, ext = '', nil, false, { }, fileextname(filename)
-- tricky as filename can be bla.1.2.3
---~ if not suffixmap[ext] then --- probably needs to be done elsewhere too
---~ wantedfiles[#wantedfiles+1] = filename
---~ end
if ext == "" then
if not instance.force_suffixes then
wantedfiles[#wantedfiles+1] = filename
@@ -9851,24 +10458,26 @@ local function collect_instance_files(filename,collected) -- todo : plugin (scan
wantedfiles[#wantedfiles+1] = forcedname
filetype = resolvers.format_of_suffix(forcedname)
if trace_locating then
- logs.report("fileio","forcing filetype '%s'",filetype)
+ report_resolvers("forcing filetype '%s'",filetype)
end
else
filetype = resolvers.format_of_suffix(filename)
if trace_locating then
- logs.report("fileio","using suffix based filetype '%s'",filetype)
+ report_resolvers("using suffix based filetype '%s'",filetype)
end
end
else
- local suffixes = resolvers.suffixes_of_format(instance.format)
if ext == "" or not suffixmap[ext] then
- for i=1,#suffixes do
- wantedfiles[#wantedfiles+1] = filename .. "." .. suffixes[i]
+ local format_suffixes = suffixes[instance.format]
+ if format_suffixes then
+ for i=1,#format_suffixes do
+ wantedfiles[#wantedfiles+1] = filename .. "." .. format_suffixes[i]
+ end
end
end
filetype = instance.format
if trace_locating then
- logs.report("fileio","using given filetype '%s'",filetype)
+ report_resolvers("using given filetype '%s'",filetype)
end
end
local typespec = resolvers.variable_of_format(filetype)
@@ -9876,13 +10485,13 @@ local function collect_instance_files(filename,collected) -- todo : plugin (scan
if not pathlist or #pathlist == 0 then
-- no pathlist, access check only / todo == wildcard
if trace_detail then
- logs.report("fileio","checking filename '%s', filetype '%s', wanted files '%s'",filename, filetype or '?',concat(wantedfiles," | "))
+ report_resolvers("checking filename '%s', filetype '%s', wanted files '%s'",filename, filetype or '?',concat(wantedfiles," | "))
end
for k=1,#wantedfiles do
local fname = wantedfiles[k]
if fname and resolvers.isreadable.file(fname) then
filename, done = fname, true
- result[#result+1] = file.join('.',fname)
+ result[#result+1] = filejoin('.',fname)
break
end
end
@@ -9900,11 +10509,11 @@ local function collect_instance_files(filename,collected) -- todo : plugin (scan
local dirlist = { }
if filelist then
for i=1,#filelist do
- dirlist[i] = file.dirname(filelist[i][2]) .. "/"
+ dirlist[i] = filedirname(filelist[i][3]) .. "/" -- was [2] .. gamble
end
end
if trace_detail then
- logs.report("fileio","checking filename '%s'",filename)
+ report_resolvers("checking filename '%s'",filename)
end
-- a bit messy ... esp the doscan setting here
local doscan
@@ -9927,7 +10536,7 @@ local function collect_instance_files(filename,collected) -- todo : plugin (scan
expression = gsub(expression,"//", '/.-/') -- not ok for /// but harmless
expression = "^" .. expression .. "$"
if trace_detail then
- logs.report("fileio","using pattern '%s' for path '%s'",expression,pathname)
+ report_resolvers("using pattern '%s' for path '%s'",expression,pathname)
end
for k=1,#filelist do
local fl = filelist[k]
@@ -9936,20 +10545,19 @@ local function collect_instance_files(filename,collected) -- todo : plugin (scan
if find(d,expression) then
--- todo, test for readable
result[#result+1] = fl[3]
- resolvers.register_in_trees(f) -- for tracing used files
done = true
if instance.allresults then
if trace_detail then
- logs.report("fileio","match to '%s' in hash for file '%s' and path '%s', continue scanning",expression,f,d)
+ report_resolvers("match to '%s' in hash for file '%s' and path '%s', continue scanning",expression,f,d)
end
else
if trace_detail then
- logs.report("fileio","match to '%s' in hash for file '%s' and path '%s', quit scanning",expression,f,d)
+ report_resolvers("match to '%s' in hash for file '%s' and path '%s', quit scanning",expression,f,d)
end
break
end
elseif trace_detail then
- logs.report("fileio","no match to '%s' in hash for file '%s' and path '%s'",expression,f,d)
+ report_resolvers("no match to '%s' in hash for file '%s' and path '%s'",expression,f,d)
end
end
end
@@ -9962,10 +10570,10 @@ local function collect_instance_files(filename,collected) -- todo : plugin (scan
if can_be_dir(ppname) then
for k=1,#wantedfiles do
local w = wantedfiles[k]
- local fname = file.join(ppname,w)
+ local fname = filejoin(ppname,w)
if resolvers.isreadable.file(fname) then
if trace_detail then
- logs.report("fileio","found '%s' by scanning",fname)
+ report_resolvers("found '%s' by scanning",fname)
end
result[#result+1] = fname
done = true
@@ -9979,14 +10587,16 @@ local function collect_instance_files(filename,collected) -- todo : plugin (scan
end
end
if not done and doscan then
- -- todo: slow path scanning
+ -- todo: slow path scanning ... although we now have tree:// supported in $TEXMF
end
if done and not instance.allresults then break end
end
end
end
for k=1,#result do
- result[k] = file.collapse_path(result[k])
+ local rk = collapse_path(result[k])
+ result[k] = rk
+ resolvers.register_in_trees(rk) -- for tracing used files
end
if instance.remember then
instance.found[stamp] = result
@@ -9996,7 +10606,7 @@ end
if not resolvers.concatinators then resolvers.concatinators = { } end
-resolvers.concatinators.tex = file.join
+resolvers.concatinators.tex = filejoin
resolvers.concatinators.file = resolvers.concatinators.tex
function resolvers.find_files(filename,filetype,mustexist)
@@ -10023,8 +10633,14 @@ function resolvers.find_file(filename,filetype,mustexist)
return (resolvers.find_files(filename,filetype,mustexist)[1] or "")
end
+function resolvers.find_path(filename,filetype)
+ local path = resolvers.find_files(filename,filetype)[1] or ""
+ -- todo return current path
+ return file.dirname(path)
+end
+
function resolvers.find_given_files(filename)
- local bname, result = file.basename(filename), { }
+ local bname, result = filebasename(filename), { }
local hashes = instance.hashes
for k=1,#hashes do
local hash = hashes[k]
@@ -10081,9 +10697,9 @@ local function doit(path,blist,bname,tag,kind,result,allresults)
return done
end
-function resolvers.find_wildcard_files(filename) -- todo: remap:
+function resolvers.find_wildcard_files(filename) -- todo: remap: and lpeg
local result = { }
- local bname, dname = file.basename(filename), file.dirname(filename)
+ local bname, dname = filebasename(filename), filedirname(filename)
local path = gsub(dname,"^*/","")
path = gsub(path,"*",".*")
path = gsub(path,"-","%%-")
@@ -10136,26 +10752,24 @@ end
function resolvers.load(option)
statistics.starttiming(instance)
- resolvers.resetconfig()
- resolvers.identify_cnf()
- resolvers.load_lua() -- will become the new method
- resolvers.expand_variables()
- resolvers.load_cnf() -- will be skipped when we have a lua file
+ identify_configuration_files()
+ load_configuration_files()
+ collapse_configuration_data()
resolvers.expand_variables()
if option ~= "nofiles" then
- resolvers.load_hash()
+ load_databases()
resolvers.automount()
end
statistics.stoptiming(instance)
local files = instance.files
- return files and next(files)
+ return files and next(files) and true
end
function resolvers.for_files(command, files, filetype, mustexist)
if files and #files > 0 then
local function report(str)
if trace_locating then
- logs.report("fileio",str) -- has already verbose
+ report_resolvers(str) -- has already verbose
else
print(str)
end
@@ -10203,51 +10817,6 @@ function resolvers.register_file(files, name, path)
end
end
-function resolvers.splitmethod(filename)
- if not filename then
- return { } -- safeguard
- elseif type(filename) == "table" then
- return filename -- already split
- elseif not find(filename,"://") then
- return { scheme="file", path = filename, original=filename } -- quick hack
- else
- return url.hashed(filename)
- end
-end
-
-function table.sequenced(t,sep) -- temp here
- local s = { }
- for k, v in next, t do -- indexed?
- s[#s+1] = k .. "=" .. tostring(v)
- end
- return concat(s, sep or " | ")
-end
-
-function resolvers.methodhandler(what, filename, filetype) -- ...
- filename = file.collapse_path(filename)
- local specification = (type(filename) == "string" and resolvers.splitmethod(filename)) or filename -- no or { }, let it bomb
- local scheme = specification.scheme
- if resolvers[what][scheme] then
- if trace_locating then
- logs.report("fileio","handler '%s' -> '%s' -> '%s'",specification.original,what,table.sequenced(specification))
- end
- return resolvers[what][scheme](filename,filetype) -- todo: specification
- else
- return resolvers[what].tex(filename,filetype) -- todo: specification
- end
-end
-
-function resolvers.clean_path(str)
- if str then
- str = gsub(str,"\\","/")
- str = gsub(str,"^!+","")
- str = gsub(str,"^~",resolvers.homedir)
- return str
- else
- return nil
- end
-end
-
function resolvers.do_with_path(name,func)
local pathlist = resolvers.expanded_path_list(name)
for i=1,#pathlist do
@@ -10259,45 +10828,13 @@ function resolvers.do_with_var(name,func)
func(expanded_var(name))
end
-function resolvers.with_files(pattern,handle)
- local hashes = instance.hashes
- for i=1,#hashes do
- local hash = hashes[i]
- local blobpath = hash.tag
- local blobtype = hash.type
- if blobpath then
- local files = instance.files[blobpath]
- if files then
- for k,v in next, files do
- if find(k,"^remap:") then
- k = files[k]
- v = files[k] -- chained
- end
- if find(k,pattern) then
- if type(v) == "string" then
- handle(blobtype,blobpath,v,k)
- else
- for _,vv in next, v do -- indexed
- handle(blobtype,blobpath,vv,k)
- end
- end
- end
- end
- end
- end
- end
-end
-
function resolvers.locate_format(name)
- local barename, fmtname = gsub(name,"%.%a+$",""), ""
- if resolvers.usecache then
- local path = file.join(caches.setpath("formats")) -- maybe platform
- fmtname = file.join(path,barename..".fmt") or ""
- end
+ local barename = gsub(name,"%.%a+$","")
+ local fmtname = caches.getfirstreadablefile(barename..".fmt","formats") or ""
if fmtname == "" then
fmtname = resolvers.find_files(barename..".fmt")[1] or ""
+ fmtname = resolvers.clean_path(fmtname)
end
- fmtname = resolvers.clean_path(fmtname)
if fmtname ~= "" then
local barename = file.removesuffix(fmtname)
local luaname, lucname, luiname = barename .. ".lua", barename .. ".luc", barename .. ".lui"
@@ -10322,196 +10859,48 @@ function resolvers.boolean_variable(str,default)
end
end
-texconfig.kpse_init = false
-
-kpse = { original = kpse } setmetatable(kpse, { __index = function(k,v) return resolvers[v] end } )
-
--- for a while
-
-input = resolvers
-
-
-end -- of closure
-
-do -- create closure to overcome 200 locals limit
-
-if not modules then modules = { } end modules ['data-tmp'] = {
- 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"
-}
-
---[[ldx--
-<p>This module deals with caching data. It sets up the paths and
-implements loaders and savers for tables. Best is to set the
-following variable. When not set, the usual paths will be
-checked. Personally I prefer the (users) temporary path.</p>
-
-</code>
-TEXMFCACHE=$TMP;$TEMP;$TMPDIR;$TEMPDIR;$HOME;$TEXMFVAR;$VARTEXMF;.
-</code>
-
-<p>Currently we do no locking when we write files. This is no real
-problem because most caching involves fonts and the chance of them
-being written at the same time is small. We also need to extend
-luatools with a recache feature.</p>
---ldx]]--
-
-local format, lower, gsub = string.format, string.lower, string.gsub
-
-local trace_cache = false trackers.register("resolvers.cache", function(v) trace_cache = v end) -- not used yet
-
-caches = caches or { }
-
-caches.path = caches.path or nil
-caches.base = caches.base or "luatex-cache"
-caches.more = caches.more or "context"
-caches.direct = false -- true is faster but may need huge amounts of memory
-caches.tree = false
-caches.paths = caches.paths or nil
-caches.force = false
-caches.defaults = { "TEXMFCACHE", "TMPDIR", "TEMPDIR", "TMP", "TEMP", "HOME", "HOMEPATH" }
-
-function caches.temp()
- local cachepath = nil
- local function check(list,isenv)
- if not cachepath then
- for k=1,#list do
- local v = list[k]
- cachepath = (isenv and (os.env[v] or "")) or v or ""
- if cachepath == "" then
- -- next
- else
- cachepath = resolvers.clean_path(cachepath)
- if lfs.isdir(cachepath) and file.iswritable(cachepath) then -- lfs.attributes(cachepath,"mode") == "directory"
- break
- elseif caches.force or io.ask(format("\nShould I create the cache path %s?",cachepath), "no", { "yes", "no" }) == "yes" then
- dir.mkdirs(cachepath)
- if lfs.isdir(cachepath) and file.iswritable(cachepath) then
- break
+function resolvers.with_files(pattern,handle,before,after) -- can be a nice iterator instead
+ local instance = resolvers.instance
+ local hashes = instance.hashes
+ for i=1,#hashes do
+ local hash = hashes[i]
+ local blobtype = hash.type
+ local blobpath = hash.tag
+ if blobpath then
+ if before then
+ before(blobtype,blobpath,pattern)
+ end
+ local files = instance.files[blobpath]
+ local total, checked, done = 0, 0, 0
+ if files then
+ for k,v in next, files do
+ total = total + 1
+ if find(k,"^remap:") then
+ k = files[k]
+ v = k -- files[k] -- chained
+ end
+ if find(k,pattern) then
+ if type(v) == "string" then
+ checked = checked + 1
+ if handle(blobtype,blobpath,v,k) then
+ done = done + 1
+ end
+ else
+ checked = checked + #v
+ for i=1,#v do
+ if handle(blobtype,blobpath,v[i],k) then
+ done = done + 1
+ end
+ end
end
end
end
- cachepath = nil
+ end
+ if after then
+ after(blobtype,blobpath,pattern,total,checked,done)
end
end
end
- check(resolvers.clean_path_list("TEXMFCACHE") or { })
- check(caches.defaults,true)
- if not cachepath then
- print("\nfatal error: there is no valid (writable) cache path defined\n")
- os.exit()
- elseif not lfs.isdir(cachepath) then -- lfs.attributes(cachepath,"mode") ~= "directory"
- print(format("\nfatal error: cache path %s is not a directory\n",cachepath))
- os.exit()
- end
- cachepath = file.collapse_path(cachepath)
- function caches.temp()
- return cachepath
- end
- return cachepath
-end
-
-function caches.configpath()
- return table.concat(resolvers.instance.cnffiles,";")
-end
-
-function caches.hashed(tree)
- return md5.hex(gsub(lower(tree),"[\\\/]+","/"))
-end
-
-function caches.treehash()
- local tree = caches.configpath()
- if not tree or tree == "" then
- return false
- else
- return caches.hashed(tree)
- end
-end
-
-function caches.setpath(...)
- if not caches.path then
- if not caches.path then
- caches.path = caches.temp()
- end
- caches.path = resolvers.clean_path(caches.path) -- to be sure
- caches.tree = caches.tree or caches.treehash()
- if caches.tree then
- caches.path = dir.mkdirs(caches.path,caches.base,caches.more,caches.tree)
- else
- caches.path = dir.mkdirs(caches.path,caches.base,caches.more)
- end
- end
- if not caches.path then
- caches.path = '.'
- end
- caches.path = resolvers.clean_path(caches.path)
- local dirs = { ... }
- if #dirs > 0 then
- local pth = dir.mkdirs(caches.path,...)
- return pth
- end
- caches.path = dir.expand_name(caches.path)
- return caches.path
-end
-
-function caches.definepath(category,subcategory)
- return function()
- return caches.setpath(category,subcategory)
- end
-end
-
-function caches.setluanames(path,name)
- return path .. "/" .. name .. ".tma", path .. "/" .. name .. ".tmc"
-end
-
-function caches.loaddata(path,name)
- local tmaname, tmcname = caches.setluanames(path,name)
- local loader = loadfile(tmcname) or loadfile(tmaname)
- if loader then
- loader = loader()
- collectgarbage("step")
- return loader
- else
- return false
- end
-end
-
---~ function caches.loaddata(path,name)
---~ local tmaname, tmcname = caches.setluanames(path,name)
---~ return dofile(tmcname) or dofile(tmaname)
---~ end
-
-function caches.iswritable(filepath,filename)
- local tmaname, tmcname = caches.setluanames(filepath,filename)
- return file.iswritable(tmaname)
-end
-
-function caches.savedata(filepath,filename,data,raw)
- local tmaname, tmcname = caches.setluanames(filepath,filename)
- local reduce, simplify = true, true
- if raw then
- reduce, simplify = false, false
- end
- data.cache_uuid = os.uuid()
- if caches.direct then
- file.savedata(tmaname, table.serialize(data,'return',false,true,false)) -- no hex
- else
- table.tofile(tmaname, data,'return',false,true,false) -- maybe not the last true
- end
- local cleanup = resolvers.boolean_variable("PURGECACHE", false)
- local strip = resolvers.boolean_variable("LUACSTRIP", true)
- utils.lua.compile(tmaname, tmcname, cleanup, strip)
-end
-
--- here we use the cache for format loading (texconfig.[formatname|jobname])
-
---~ if tex and texconfig and texconfig.formatname and texconfig.formatname == "" then
-if tex and texconfig and (not texconfig.formatname or texconfig.formatname == "") and input and resolvers.instance then
- if not texconfig.luaname then texconfig.luaname = "cont-en.lua" end -- or luc
- texconfig.formatname = caches.setpath("formats") .. "/" .. gsub(texconfig.luaname,"%.lu.$",".fmt")
end
@@ -10519,7 +10908,7 @@ end -- of closure
do -- create closure to overcome 200 locals limit
-if not modules then modules = { } end modules ['data-res'] = {
+if not modules then modules = { } end modules ['data-pre'] = {
version = 1.001,
comment = "companion to luat-lib.mkiv",
author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
@@ -10527,14 +10916,15 @@ if not modules then modules = { } end modules ['data-res'] = {
license = "see context related readme files"
}
---~ print(resolvers.resolve("abc env:tmp file:cont-en.tex path:cont-en.tex full:cont-en.tex rel:zapf/one/p-chars.tex"))
local upper, lower, gsub = string.upper, string.lower, string.gsub
local prefixes = { }
-prefixes.environment = function(str)
- return resolvers.clean_path(os.getenv(str) or os.getenv(upper(str)) or os.getenv(lower(str)) or "")
+local getenv = resolvers.getenv
+
+prefixes.environment = function(str) -- getenv is case insensitive anyway
+ return resolvers.clean_path(getenv(str) or getenv(upper(str)) or getenv(lower(str)) or "")
end
prefixes.relative = function(str,n)
@@ -10672,7 +11062,7 @@ end -- of closure
do -- create closure to overcome 200 locals limit
if not modules then modules = { } end modules ['data-con'] = {
- version = 1.001,
+ version = 1.100,
comment = "companion to luat-lib.mkiv",
author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright = "PRAGMA ADE / ConTeXt Development Team",
@@ -10702,46 +11092,58 @@ containers = containers or { }
containers.usecache = true
+local report_cache = logs.new("cache")
+
local function report(container,tag,name)
if trace_cache or trace_containers then
- logs.report(format("%s cache",container.subcategory),"%s: %s",tag,name or 'invalid')
+ report_cache("container: %s, tag: %s, name: %s",container.subcategory,tag,name or 'invalid')
end
end
local allocated = { }
--- tracing
+local mt = {
+ __index = function(t,k)
+ if k == "writable" then
+ local writable = caches.getwritablepath(t.category,t.subcategory) or { "." }
+ t.writable = writable
+ return writable
+ elseif k == "readables" then
+ local readables = caches.getreadablepaths(t.category,t.subcategory) or { "." }
+ t.readables = readables
+ return readables
+ end
+ end
+}
function containers.define(category, subcategory, version, enabled)
- return function()
- if category and subcategory then
- local c = allocated[category]
- if not c then
- c = { }
- allocated[category] = c
- end
- local s = c[subcategory]
- if not s then
- s = {
- category = category,
- subcategory = subcategory,
- storage = { },
- enabled = enabled,
- version = version or 1.000,
- trace = false,
- path = caches and caches.setpath and caches.setpath(category,subcategory),
- }
- c[subcategory] = s
- end
- return s
- else
- return nil
+ if category and subcategory then
+ local c = allocated[category]
+ if not c then
+ c = { }
+ allocated[category] = c
+ end
+ local s = c[subcategory]
+ if not s then
+ s = {
+ category = category,
+ subcategory = subcategory,
+ storage = { },
+ enabled = enabled,
+ version = version or math.pi, -- after all, this is TeX
+ trace = false,
+ -- writable = caches.getwritablepath and caches.getwritablepath (category,subcategory) or { "." },
+ -- readables = caches.getreadablepaths and caches.getreadablepaths(category,subcategory) or { "." },
+ }
+ setmetatable(s,mt)
+ c[subcategory] = s
end
+ return s
end
end
function containers.is_usable(container, name)
- return container.enabled and caches and caches.iswritable(container.path, name)
+ return container.enabled and caches and caches.iswritable(container.writable, name)
end
function containers.is_valid(container, name)
@@ -10754,18 +11156,20 @@ function containers.is_valid(container, name)
end
function containers.read(container,name)
- if container.enabled and caches and not container.storage[name] and containers.usecache then
- container.storage[name] = caches.loaddata(container.path,name)
- if containers.is_valid(container,name) then
+ local storage = container.storage
+ local stored = storage[name]
+ if not stored and container.enabled and caches and containers.usecache then
+ stored = caches.loaddata(container.readables,name)
+ if stored and stored.cache_version == container.version then
report(container,"loaded",name)
else
- container.storage[name] = nil
+ stored = nil
end
- end
- if container.storage[name] then
+ storage[name] = stored
+ elseif stored then
report(container,"reusing",name)
end
- return container.storage[name]
+ return stored
end
function containers.write(container, name, data)
@@ -10774,7 +11178,7 @@ function containers.write(container, name, data)
if container.enabled and caches then
local unique, shared = data.unique, data.shared
data.unique, data.shared = nil, nil
- caches.savedata(container.path, name, data)
+ caches.savedata(container.writable, name, data)
report(container,"saved",name)
data.unique, data.shared = unique, shared
end
@@ -10809,41 +11213,7 @@ local format, lower, gsub, find = string.format, string.lower, string.gsub, stri
local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
--- since we want to use the cache instead of the tree, we will now
--- reimplement the saver.
-
-local save_data = resolvers.save_data
-local load_data = resolvers.load_data
-
-resolvers.cachepath = nil -- public, for tracing
-resolvers.usecache = true -- public, for tracing
-
-function resolvers.save_data(dataname)
- save_data(dataname, function(cachename,dataname)
- resolvers.usecache = not toboolean(resolvers.expansion("CACHEINTDS") or "false",true)
- if resolvers.usecache then
- resolvers.cachepath = resolvers.cachepath or caches.definepath("trees")
- return file.join(resolvers.cachepath(),caches.hashed(cachename))
- else
- return file.join(cachename,dataname)
- end
- end)
-end
-
-function resolvers.load_data(pathname,dataname,filename)
- load_data(pathname,dataname,filename,function(dataname,filename)
- resolvers.usecache = not toboolean(resolvers.expansion("CACHEINTDS") or "false",true)
- if resolvers.usecache then
- resolvers.cachepath = resolvers.cachepath or caches.definepath("trees")
- return file.join(resolvers.cachepath(),caches.hashed(pathname))
- else
- if not filename or (filename == "") then
- filename = dataname
- end
- return file.join(pathname,filename)
- end
- end)
-end
+local report_resolvers = logs.new("resolvers")
-- we will make a better format, maybe something xml or just text or lua
@@ -10852,7 +11222,7 @@ resolvers.automounted = resolvers.automounted or { }
function resolvers.automount(usecache)
local mountpaths = resolvers.clean_path_list(resolvers.expansion('TEXMFMOUNT'))
if (not mountpaths or #mountpaths == 0) and usecache then
- mountpaths = { caches.setpath("mount") }
+ mountpaths = caches.getreadablepaths("mount")
end
if mountpaths and #mountpaths > 0 then
statistics.starttiming(resolvers.instance)
@@ -10866,7 +11236,7 @@ function resolvers.automount(usecache)
-- skip
elseif find(line,"^zip://") then
if trace_locating then
- logs.report("fileio","mounting %s",line)
+ report_resolvers("mounting %s",line)
end
table.insert(resolvers.automounted,line)
resolvers.usezipfile(line)
@@ -10882,8 +11252,8 @@ end
-- status info
-statistics.register("used config path", function() return caches.configpath() end)
-statistics.register("used cache path", function() return caches.temp() or "?" end)
+statistics.register("used config file", function() return caches.configfiles() end)
+statistics.register("used cache path", function() return caches.usedpaths() end)
-- experiment (code will move)
@@ -10945,6 +11315,8 @@ local unpack = unpack or table.unpack
local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
+local report_resolvers = logs.new("resolvers")
+
-- zip:///oeps.zip?name=bla/bla.tex
-- zip:///oeps.zip?tree=tex/texmf-local
-- zip:///texmf.zip?tree=/tex/texmf
@@ -10995,16 +11367,16 @@ function locators.zip(specification) -- where is this used? startup zips (untest
local zfile = zip.openarchive(name) -- tricky, could be in to be initialized tree
if trace_locating then
if zfile then
- logs.report("fileio","zip locator, archive '%s' found",specification.original)
+ report_resolvers("zip locator, archive '%s' found",specification.original)
else
- logs.report("fileio","zip locator, archive '%s' not found",specification.original)
+ report_resolvers("zip locator, archive '%s' not found",specification.original)
end
end
end
function hashers.zip(tag,name)
if trace_locating then
- logs.report("fileio","loading zip file '%s' as '%s'",name,tag)
+ report_resolvers("loading zip file '%s' as '%s'",name,tag)
end
resolvers.usezipfile(format("%s?tree=%s",tag,name))
end
@@ -11029,25 +11401,25 @@ function finders.zip(specification,filetype)
local zfile = zip.openarchive(specification.path)
if zfile then
if trace_locating then
- logs.report("fileio","zip finder, archive '%s' found",specification.path)
+ report_resolvers("zip finder, archive '%s' found",specification.path)
end
local dfile = zfile:open(q.name)
if dfile then
dfile = zfile:close()
if trace_locating then
- logs.report("fileio","zip finder, file '%s' found",q.name)
+ report_resolvers("zip finder, file '%s' found",q.name)
end
return specification.original
elseif trace_locating then
- logs.report("fileio","zip finder, file '%s' not found",q.name)
+ report_resolvers("zip finder, file '%s' not found",q.name)
end
elseif trace_locating then
- logs.report("fileio","zip finder, unknown archive '%s'",specification.path)
+ report_resolvers("zip finder, unknown archive '%s'",specification.path)
end
end
end
if trace_locating then
- logs.report("fileio","zip finder, '%s' not found",filename)
+ report_resolvers("zip finder, '%s' not found",filename)
end
return unpack(finders.notfound)
end
@@ -11060,25 +11432,25 @@ function openers.zip(specification)
local zfile = zip.openarchive(zipspecification.path)
if zfile then
if trace_locating then
- logs.report("fileio","zip opener, archive '%s' opened",zipspecification.path)
+ report_resolvers("zip opener, archive '%s' opened",zipspecification.path)
end
local dfile = zfile:open(q.name)
if dfile then
logs.show_open(specification)
if trace_locating then
- logs.report("fileio","zip opener, file '%s' found",q.name)
+ report_resolvers("zip opener, file '%s' found",q.name)
end
return openers.text_opener(specification,dfile,'zip')
elseif trace_locating then
- logs.report("fileio","zip opener, file '%s' not found",q.name)
+ report_resolvers("zip opener, file '%s' not found",q.name)
end
elseif trace_locating then
- logs.report("fileio","zip opener, unknown archive '%s'",zipspecification.path)
+ report_resolvers("zip opener, unknown archive '%s'",zipspecification.path)
end
end
end
if trace_locating then
- logs.report("fileio","zip opener, '%s' not found",filename)
+ report_resolvers("zip opener, '%s' not found",filename)
end
return unpack(openers.notfound)
end
@@ -11091,27 +11463,27 @@ function loaders.zip(specification)
local zfile = zip.openarchive(specification.path)
if zfile then
if trace_locating then
- logs.report("fileio","zip loader, archive '%s' opened",specification.path)
+ report_resolvers("zip loader, archive '%s' opened",specification.path)
end
local dfile = zfile:open(q.name)
if dfile then
logs.show_load(filename)
if trace_locating then
- logs.report("fileio","zip loader, file '%s' loaded",filename)
+ report_resolvers("zip loader, file '%s' loaded",filename)
end
local s = dfile:read("*all")
dfile:close()
return true, s, #s
elseif trace_locating then
- logs.report("fileio","zip loader, file '%s' not found",q.name)
+ report_resolvers("zip loader, file '%s' not found",q.name)
end
elseif trace_locating then
- logs.report("fileio","zip loader, unknown archive '%s'",specification.path)
+ report_resolvers("zip loader, unknown archive '%s'",specification.path)
end
end
end
if trace_locating then
- logs.report("fileio","zip loader, '%s' not found",filename)
+ report_resolvers("zip loader, '%s' not found",filename)
end
return unpack(openers.notfound)
end
@@ -11129,7 +11501,7 @@ function resolvers.usezipfile(zipname)
if z then
local instance = resolvers.instance
if trace_locating then
- logs.report("fileio","zip registering, registering archive '%s'",zipname)
+ report_resolvers("zip registering, registering archive '%s'",zipname)
end
statistics.starttiming(instance)
resolvers.prepend_hash('zip',zipname,zipfile)
@@ -11138,10 +11510,10 @@ function resolvers.usezipfile(zipname)
instance.files[zipname] = resolvers.register_zip_file(z,tree or "")
statistics.stoptiming(instance)
elseif trace_locating then
- logs.report("fileio","zip registering, unknown archive '%s'",zipname)
+ report_resolvers("zip registering, unknown archive '%s'",zipname)
end
elseif trace_locating then
- logs.report("fileio","zip registering, '%s' not found",zipname)
+ report_resolvers("zip registering, '%s' not found",zipname)
end
end
@@ -11153,7 +11525,7 @@ function resolvers.register_zip_file(z,tree)
filter = format("^%s/(.+)/(.-)$",tree)
end
if trace_locating then
- logs.report("fileio","zip registering, using filter '%s'",filter)
+ report_resolvers("zip registering, using filter '%s'",filter)
end
local register, n = resolvers.register_file, 0
for i in z:files() do
@@ -11170,7 +11542,7 @@ function resolvers.register_zip_file(z,tree)
n = n + 1
end
end
- logs.report("fileio","zip registering, %s files registered",n)
+ report_resolvers("zip registering, %s files registered",n)
return files
end
@@ -11179,6 +11551,93 @@ end -- of closure
do -- create closure to overcome 200 locals limit
+if not modules then modules = { } end modules ['data-tre'] = {
+ version = 1.001,
+ comment = "companion to luat-lib.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- \input tree://oeps1/**/oeps.tex
+
+local find, gsub, format = string.find, string.gsub, string.format
+local unpack = unpack or table.unpack
+
+local report_resolvers = logs.new("resolvers")
+
+local done, found, notfound = { }, { }, resolvers.finders.notfound
+
+function resolvers.finders.tree(specification,filetype)
+ local fnd = found[specification]
+ if not fnd then
+ local spec = resolvers.splitmethod(specification).path or ""
+ if spec ~= "" then
+ local path, name = file.dirname(spec), file.basename(spec)
+ if path == "" then path = "." end
+ local hash = done[path]
+ if not hash then
+ local pattern = path .. "/*" -- we will use the proper splitter
+ hash = dir.glob(pattern)
+ done[path] = hash
+ end
+ local pattern = "/" .. gsub(name,"([%.%-%+])", "%%%1") .. "$"
+ for k=1,#hash do
+ local v = hash[k]
+ if find(v,pattern) then
+ found[specification] = v
+ return v
+ end
+ end
+ end
+ fnd = unpack(notfound) -- unpack ? why not just notfound[1]
+ found[specification] = fnd
+ end
+ return fnd
+end
+
+function resolvers.locators.tree(specification)
+ local spec = resolvers.splitmethod(specification)
+ local path = spec.path
+ if path ~= '' and lfs.isdir(path) then
+ if trace_locating then
+ report_resolvers("tree locator '%s' found (%s)",path,specification)
+ end
+ resolvers.append_hash('tree',specification,path,false) -- don't cache
+ elseif trace_locating then
+ report_resolvers("tree locator '%s' not found",path)
+ end
+end
+
+function resolvers.hashers.tree(tag,name)
+ if trace_locating then
+ report_resolvers("analysing tree '%s' as '%s'",name,tag)
+ end
+ -- todo: maybe share with done above
+ local spec = resolvers.splitmethod(tag)
+ local path = spec.path
+ resolvers.generators.tex(path,tag) -- we share this with the normal tree analyzer
+end
+
+function resolvers.generators.tree(tag)
+ local spec = resolvers.splitmethod(tag)
+ local path = spec.path
+ resolvers.generators.tex(path,tag) -- we share this with the normal tree analyzer
+end
+
+function resolvers.concatinators.tree(tag,path,name)
+ return file.join(tag,path,name)
+end
+
+resolvers.isreadable.tree = file.isreadable
+resolvers.openers.tree = resolvers.openers.generic
+resolvers.loaders.tree = resolvers.loaders.generic
+
+
+end -- of closure
+
+do -- create closure to overcome 200 locals limit
+
if not modules then modules = { } end modules ['data-crl'] = {
version = 1.001,
comment = "companion to luat-lib.mkiv",
@@ -11187,32 +11646,31 @@ if not modules then modules = { } end modules ['data-crl'] = {
license = "see context related readme files"
}
-local gsub = string.gsub
+-- this one is replaced by data-sch.lua --
curl = curl or { }
-curl.cached = { }
-curl.cachepath = caches.definepath("curl")
-
+local gsub = string.gsub
local finders, openers, loaders = resolvers.finders, resolvers.openers, resolvers.loaders
-function curl.fetch(protocol, name)
- local cachename = curl.cachepath() .. "/" .. gsub(name,"[^%a%d%.]+","-")
--- cachename = gsub(cachename,"[\\/]", io.fileseparator)
- cachename = gsub(cachename,"[\\]", "/") -- cleanup
- if not curl.cached[name] then
+local cached = { }
+
+function curl.fetch(protocol, name) -- todo: use socket library
+ local cleanname = gsub(name,"[^%a%d%.]+","-")
+ local cachename = caches.setfirstwritablefile(cleanname,"curl")
+ if not cached[name] then
if not io.exists(cachename) then
- curl.cached[name] = cachename
+ cached[name] = cachename
local command = "curl --silent --create-dirs --output " .. cachename .. " " .. name -- no protocol .. "://"
os.spawn(command)
end
if io.exists(cachename) then
- curl.cached[name] = cachename
+ cached[name] = cachename
else
- curl.cached[name] = ""
+ cached[name] = ""
end
end
- return curl.cached[name]
+ return cached[name]
end
function finders.curl(protocol,filename)
@@ -11259,6 +11717,8 @@ if not modules then modules = { } end modules ['data-lua'] = {
local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
+local report_resolvers = logs.new("resolvers")
+
local gsub, insert = string.gsub, table.insert
local unpack = unpack or table.unpack
@@ -11287,7 +11747,7 @@ local function thepath(...)
local t = { ... } t[#t+1] = "?.lua"
local path = file.join(unpack(t))
if trace_locating then
- logs.report("fileio","! appending '%s' to 'package.path'",path)
+ report_resolvers("! appending '%s' to 'package.path'",path)
end
return path
end
@@ -11309,11 +11769,11 @@ local function loaded(libpaths,name,simple)
local libpath = libpaths[i]
local resolved = gsub(libpath,"%?",simple)
if trace_locating then -- more detail
- logs.report("fileio","! checking for '%s' on 'package.path': '%s' => '%s'",simple,libpath,resolved)
+ report_resolvers("! checking for '%s' on 'package.path': '%s' => '%s'",simple,libpath,resolved)
end
if resolvers.isreadable.file(resolved) then
if trace_locating then
- logs.report("fileio","! lib '%s' located via 'package.path': '%s'",name,resolved)
+ report_resolvers("! lib '%s' located via 'package.path': '%s'",name,resolved)
end
return loadfile(resolved)
end
@@ -11323,17 +11783,17 @@ end
package.loaders[2] = function(name) -- was [#package.loaders+1]
if trace_locating then -- mode detail
- logs.report("fileio","! locating '%s'",name)
+ report_resolvers("! locating '%s'",name)
end
for i=1,#libformats do
local format = libformats[i]
local resolved = resolvers.find_file(name,format) or ""
if trace_locating then -- mode detail
- logs.report("fileio","! checking for '%s' using 'libformat path': '%s'",name,format)
+ report_resolvers("! checking for '%s' using 'libformat path': '%s'",name,format)
end
if resolved ~= "" then
if trace_locating then
- logs.report("fileio","! lib '%s' located via environment: '%s'",name,resolved)
+ report_resolvers("! lib '%s' located via environment: '%s'",name,resolved)
end
return loadfile(resolved)
end
@@ -11356,11 +11816,11 @@ package.loaders[2] = function(name) -- was [#package.loaders+1]
local path = paths[p]
local resolved = file.join(path,libname)
if trace_locating then -- mode detail
- logs.report("fileio","! checking for '%s' using 'clibformat path': '%s'",libname,path)
+ report_resolvers("! checking for '%s' using 'clibformat path': '%s'",libname,path)
end
if resolvers.isreadable.file(resolved) then
if trace_locating then
- logs.report("fileio","! lib '%s' located via 'clibformat': '%s'",libname,resolved)
+ report_resolvers("! lib '%s' located via 'clibformat': '%s'",libname,resolved)
end
return package.loadlib(resolved,name)
end
@@ -11370,28 +11830,28 @@ package.loaders[2] = function(name) -- was [#package.loaders+1]
local libpath = clibpaths[i]
local resolved = gsub(libpath,"?",simple)
if trace_locating then -- more detail
- logs.report("fileio","! checking for '%s' on 'package.cpath': '%s'",simple,libpath)
+ report_resolvers("! checking for '%s' on 'package.cpath': '%s'",simple,libpath)
end
if resolvers.isreadable.file(resolved) then
if trace_locating then
- logs.report("fileio","! lib '%s' located via 'package.cpath': '%s'",name,resolved)
+ report_resolvers("! lib '%s' located via 'package.cpath': '%s'",name,resolved)
end
return package.loadlib(resolved,name)
end
end
-- just in case the distribution is messed up
if trace_loading then -- more detail
- logs.report("fileio","! checking for '%s' using 'luatexlibs': '%s'",name)
+ report_resolvers("! checking for '%s' using 'luatexlibs': '%s'",name)
end
local resolved = resolvers.find_file(file.basename(name),'luatexlibs') or ""
if resolved ~= "" then
if trace_locating then
- logs.report("fileio","! lib '%s' located by basename via environment: '%s'",name,resolved)
+ report_resolvers("! lib '%s' located by basename via environment: '%s'",name,resolved)
end
return loadfile(resolved)
end
if trace_locating then
- logs.report("fileio",'? unable to locate lib: %s',name)
+ report_resolvers('? unable to locate lib: %s',name)
end
-- return "unable to locate " .. name
end
@@ -11403,113 +11863,6 @@ end -- of closure
do -- create closure to overcome 200 locals limit
-if not modules then modules = { } end modules ['luat-kps'] = {
- version = 1.001,
- comment = "companion to luatools.lua",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
---[[ldx--
-<p>This file is used when we want the input handlers to behave like
-<type>kpsewhich</type>. What to do with the following:</p>
-
-<typing>
-{$SELFAUTOLOC,$SELFAUTODIR,$SELFAUTOPARENT}{,{/share,}/texmf{-local,}/web2c}
-$SELFAUTOLOC : /usr/tex/bin/platform
-$SELFAUTODIR : /usr/tex/bin
-$SELFAUTOPARENT : /usr/tex
-</typing>
-
-<p>How about just forgetting about them?</p>
---ldx]]--
-
-local suffixes = resolvers.suffixes
-local formats = resolvers.formats
-
-suffixes['gf'] = { '<resolution>gf' }
-suffixes['pk'] = { '<resolution>pk' }
-suffixes['base'] = { 'base' }
-suffixes['bib'] = { 'bib' }
-suffixes['bst'] = { 'bst' }
-suffixes['cnf'] = { 'cnf' }
-suffixes['mem'] = { 'mem' }
-suffixes['mf'] = { 'mf' }
-suffixes['mfpool'] = { 'pool' }
-suffixes['mft'] = { 'mft' }
-suffixes['mppool'] = { 'pool' }
-suffixes['graphic/figure'] = { 'eps', 'epsi' }
-suffixes['texpool'] = { 'pool' }
-suffixes['PostScript header'] = { 'pro' }
-suffixes['ist'] = { 'ist' }
-suffixes['web'] = { 'web', 'ch' }
-suffixes['cweb'] = { 'w', 'web', 'ch' }
-suffixes['cmap files'] = { 'cmap' }
-suffixes['lig files'] = { 'lig' }
-suffixes['bitmap font'] = { }
-suffixes['MetaPost support'] = { }
-suffixes['TeX system documentation'] = { }
-suffixes['TeX system sources'] = { }
-suffixes['dvips config'] = { }
-suffixes['type42 fonts'] = { }
-suffixes['web2c files'] = { }
-suffixes['other text files'] = { }
-suffixes['other binary files'] = { }
-suffixes['opentype fonts'] = { 'otf' }
-
-suffixes['fmt'] = { 'fmt' }
-suffixes['texmfscripts'] = { 'rb','lua','py','pl' }
-
-suffixes['pdftex config'] = { }
-suffixes['Troff fonts'] = { }
-
-suffixes['ls-R'] = { }
-
---[[ldx--
-<p>If you wondered abou tsome of the previous mappings, how about
-the next bunch:</p>
---ldx]]--
-
-formats['bib'] = ''
-formats['bst'] = ''
-formats['mft'] = ''
-formats['ist'] = ''
-formats['web'] = ''
-formats['cweb'] = ''
-formats['MetaPost support'] = ''
-formats['TeX system documentation'] = ''
-formats['TeX system sources'] = ''
-formats['Troff fonts'] = ''
-formats['dvips config'] = ''
-formats['graphic/figure'] = ''
-formats['ls-R'] = ''
-formats['other text files'] = ''
-formats['other binary files'] = ''
-
-formats['gf'] = ''
-formats['pk'] = ''
-formats['base'] = 'MFBASES'
-formats['cnf'] = ''
-formats['mem'] = 'MPMEMS'
-formats['mf'] = 'MFINPUTS'
-formats['mfpool'] = 'MFPOOL'
-formats['mppool'] = 'MPPOOL'
-formats['texpool'] = 'TEXPOOL'
-formats['PostScript header'] = 'TEXPSHEADERS'
-formats['cmap files'] = 'CMAPFONTS'
-formats['type42 fonts'] = 'T42FONTS'
-formats['web2c files'] = 'WEB2C'
-formats['pdftex config'] = 'PDFTEXCONFIG'
-formats['texmfscripts'] = 'TEXMFSCRIPTS'
-formats['bitmap font'] = ''
-formats['lig files'] = 'LIGFONTS'
-
-
-end -- of closure
-
-do -- create closure to overcome 200 locals limit
-
if not modules then modules = { } end modules ['data-aux'] = {
version = 1.001,
comment = "companion to luat-lib.mkiv",
@@ -11519,49 +11872,52 @@ if not modules then modules = { } end modules ['data-aux'] = {
}
local find = string.find
+local type, next = type, next
local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
+local report_resolvers = logs.new("resolvers")
+
function resolvers.update_script(oldname,newname) -- oldname -> own.name, not per se a suffix
local scriptpath = "scripts/context/lua"
newname = file.addsuffix(newname,"lua")
local oldscript = resolvers.clean_path(oldname)
if trace_locating then
- logs.report("fileio","to be replaced old script %s", oldscript)
+ report_resolvers("to be replaced old script %s", oldscript)
end
local newscripts = resolvers.find_files(newname) or { }
if #newscripts == 0 then
if trace_locating then
- logs.report("fileio","unable to locate new script")
+ report_resolvers("unable to locate new script")
end
else
for i=1,#newscripts do
local newscript = resolvers.clean_path(newscripts[i])
if trace_locating then
- logs.report("fileio","checking new script %s", newscript)
+ report_resolvers("checking new script %s", newscript)
end
if oldscript == newscript then
if trace_locating then
- logs.report("fileio","old and new script are the same")
+ report_resolvers("old and new script are the same")
end
elseif not find(newscript,scriptpath) then
if trace_locating then
- logs.report("fileio","new script should come from %s",scriptpath)
+ report_resolvers("new script should come from %s",scriptpath)
end
elseif not (find(oldscript,file.removesuffix(newname).."$") or find(oldscript,newname.."$")) then
if trace_locating then
- logs.report("fileio","invalid new script name")
+ report_resolvers("invalid new script name")
end
else
local newdata = io.loaddata(newscript)
if newdata then
if trace_locating then
- logs.report("fileio","old script content replaced by new content")
+ report_resolvers("old script content replaced by new content")
end
io.savedata(oldscript,newdata)
break
elseif trace_locating then
- logs.report("fileio","unable to load new script")
+ report_resolvers("unable to load new script")
end
end
end
@@ -11581,70 +11937,116 @@ if not modules then modules = { } end modules ['data-tmf'] = {
license = "see context related readme files"
}
-local find, gsub, match = string.find, string.gsub, string.match
-local getenv, setenv = os.getenv, os.setenv
+-- = <<
+-- ? ??
+-- < +=
+-- > =+
--- loads *.tmf files in minimal tree roots (to be optimized and documented)
+function resolvers.load_tree(tree)
+ if type(tree) == "string" and tree ~= "" then
-function resolvers.check_environment(tree)
- logs.simpleline()
- setenv('TMP', getenv('TMP') or getenv('TEMP') or getenv('TMPDIR') or getenv('HOME'))
- setenv('TEXOS', getenv('TEXOS') or ("texmf-" .. os.platform))
- setenv('TEXPATH', gsub(tree or "tex","\/+$",''))
- setenv('TEXMFOS', getenv('TEXPATH') .. "/" .. getenv('TEXOS'))
- logs.simpleline()
- logs.simple("preset : TEXPATH => %s", getenv('TEXPATH'))
- logs.simple("preset : TEXOS => %s", getenv('TEXOS'))
- logs.simple("preset : TEXMFOS => %s", getenv('TEXMFOS'))
- logs.simple("preset : TMP => %s", getenv('TMP'))
- logs.simple('')
-end
+ local getenv, setenv = resolvers.getenv, resolvers.setenv
-function resolvers.load_environment(name) -- todo: key=value as well as lua
- local f = io.open(name)
- if f then
- for line in f:lines() do
- if find(line,"^[%%%#]") then
- -- skip comment
- else
- local key, how, value = match(line,"^(.-)%s*([<=>%?]+)%s*(.*)%s*$")
- if how then
- value = gsub(value,"%%(.-)%%", function(v) return getenv(v) or "" end)
- if how == "=" or how == "<<" then
- setenv(key,value)
- elseif how == "?" or how == "??" then
- setenv(key,getenv(key) or value)
- elseif how == "<" or how == "+=" then
- if getenv(key) then
- setenv(key,getenv(key) .. io.fileseparator .. value)
- else
- setenv(key,value)
- end
- elseif how == ">" or how == "=+" then
- if getenv(key) then
- setenv(key,value .. io.pathseparator .. getenv(key))
- else
- setenv(key,value)
- end
- end
- end
- end
+ -- later might listen to the raw osenv var as well
+ local texos = "texmf-" .. os.platform
+
+ local oldroot = environment.texroot
+ local newroot = file.collapse_path(tree)
+
+ local newtree = file.join(newroot,texos)
+ local newpath = file.join(newtree,"bin")
+
+ if not lfs.isdir(newtree) then
+ logs.simple("no '%s' under tree %s",texos,tree)
+ os.exit()
end
- f:close()
+ if not lfs.isdir(newpath) then
+ logs.simple("no '%s/bin' under tree %s",texos,tree)
+ os.exit()
+ end
+
+ local texmfos = newtree
+
+ environment.texroot = newroot
+ environment.texos = texos
+ environment.texmfos = texmfos
+
+ setenv('SELFAUTOPARENT', newroot)
+ setenv('SELFAUTODIR', newtree)
+ setenv('SELFAUTOLOC', newpath)
+ setenv('TEXROOT', newroot)
+ setenv('TEXOS', texos)
+ setenv('TEXMFOS', texmfos)
+ setenv('TEXROOT', newroot)
+ setenv('TEXMFCNF', resolvers.luacnfspec)
+ setenv("PATH", newpath .. io.pathseparator .. getenv("PATH"))
+
+ logs.simple("changing from root '%s' to '%s'",oldroot,newroot)
+ logs.simple("prepending '%s' to binary path",newpath)
+ logs.simple()
end
end
-function resolvers.load_tree(tree)
- if tree and tree ~= "" then
- local setuptex = 'setuptex.tmf'
- if lfs.attributes(tree, "mode") == "directory" then -- check if not nil
- setuptex = tree .. "/" .. setuptex
- else
- setuptex = tree
+
+end -- of closure
+
+do -- create closure to overcome 200 locals limit
+
+if not modules then modules = { } end modules ['data-lst'] = {
+ version = 1.001,
+ comment = "companion to luat-lib.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- used in mtxrun
+
+local find, concat, upper, format = string.find, table.concat, string.upper, string.format
+
+resolvers.listers = resolvers.listers or { }
+
+local function tabstr(str)
+ if type(str) == 'table' then
+ return concat(str," | ")
+ else
+ return str
+ end
+end
+
+local function list(list,report,pattern)
+ pattern = pattern and pattern ~= "" and upper(pattern) or ""
+ local instance = resolvers.instance
+ local report = report or texio.write_nl
+ local sorted = table.sortedkeys(list)
+ for i=1,#sorted do
+ local key = sorted[i]
+ if pattern == "" or find(upper(key),pattern) then
+ report(format('%s %s=%s',instance.origins[key] or "---",key,tabstr(list[key])))
end
- if io.exists(setuptex) then
- resolvers.check_environment(tree)
- resolvers.load_environment(setuptex)
+ end
+end
+
+function resolvers.listers.variables (report,pattern) list(resolvers.instance.variables, report,pattern) end
+function resolvers.listers.expansions(report,pattern) list(resolvers.instance.expansions,report,pattern) end
+
+function resolvers.listers.configurations(report,pattern)
+ pattern = pattern and pattern ~= "" and upper(pattern) or ""
+ local report = report or texio.write_nl
+ local instance = resolvers.instance
+ local sorted = table.sortedkeys(instance.kpsevars)
+ for i=1,#sorted do
+ local key = sorted[i]
+ if pattern == "" or find(upper(key),pattern) then
+ report(format("%s\n",key))
+ local order = instance.order
+ for i=1,#order do
+ local str = order[i][key]
+ if str then
+ report(format("\t%s\t%s",i,str))
+ end
+ end
+ report("")
end
end
end
@@ -11753,111 +12155,140 @@ function states.get(key,default)
return states.get_by_tag(states.tag,key,default)
end
---~ states.data.update = {
---~ ["version"] = {
---~ ["major"] = 0,
---~ ["minor"] = 1,
---~ },
---~ ["rsync"] = {
---~ ["server"] = "contextgarden.net",
---~ ["module"] = "minimals",
---~ ["repository"] = "current",
---~ ["flags"] = "-rpztlv --stats",
---~ },
---~ ["tasks"] = {
---~ ["update"] = true,
---~ ["make"] = true,
---~ ["delete"] = false,
---~ },
---~ ["platform"] = {
---~ ["host"] = true,
---~ ["other"] = {
---~ ["mswin"] = false,
---~ ["linux"] = false,
---~ ["linux-64"] = false,
---~ ["osx-intel"] = false,
---~ ["osx-ppc"] = false,
---~ ["sun"] = false,
---~ },
---~ },
---~ ["context"] = {
---~ ["available"] = {"current", "beta", "alpha", "experimental"},
---~ ["selected"] = "current",
---~ },
---~ ["formats"] = {
---~ ["cont-en"] = true,
---~ ["cont-nl"] = true,
---~ ["cont-de"] = false,
---~ ["cont-cz"] = false,
---~ ["cont-fr"] = false,
---~ ["cont-ro"] = false,
---~ },
---~ ["engine"] = {
---~ ["pdftex"] = {
---~ ["install"] = true,
---~ ["formats"] = {
---~ ["pdftex"] = true,
---~ },
---~ },
---~ ["luatex"] = {
---~ ["install"] = true,
---~ ["formats"] = {
---~ },
---~ },
---~ ["xetex"] = {
---~ ["install"] = true,
---~ ["formats"] = {
---~ ["xetex"] = false,
---~ },
---~ },
---~ ["metapost"] = {
---~ ["install"] = true,
---~ ["formats"] = {
---~ ["mpost"] = true,
---~ ["metafun"] = true,
---~ },
---~ },
---~ },
---~ ["fonts"] = {
---~ },
---~ ["doc"] = {
---~ },
---~ ["modules"] = {
---~ ["f-urwgaramond"] = false,
---~ ["f-urwgothic"] = false,
---~ ["t-bnf"] = false,
---~ ["t-chromato"] = false,
---~ ["t-cmscbf"] = false,
---~ ["t-cmttbf"] = false,
---~ ["t-construction-plan"] = false,
---~ ["t-degrade"] = false,
---~ ["t-french"] = false,
---~ ["t-lettrine"] = false,
---~ ["t-lilypond"] = false,
---~ ["t-mathsets"] = false,
---~ ["t-tikz"] = false,
---~ ["t-typearea"] = false,
---~ ["t-vim"] = false,
---~ },
---~ }
-
---~ states.save("teststate", "update")
---~ states.load("teststate", "update")
-
---~ print(states.get_by_tag("update","rsync.server","unknown"))
---~ states.set_by_tag("update","rsync.server","oeps")
---~ print(states.get_by_tag("update","rsync.server","unknown"))
---~ states.save("teststate", "update")
---~ states.load("teststate", "update")
---~ print(states.get_by_tag("update","rsync.server","unknown"))
+
+
+
+
+end -- of closure
+
+do -- create closure to overcome 200 locals limit
+
+if not modules then modules = { } end modules ['luat-fmt'] = {
+ version = 1.001,
+ comment = "companion to mtxrun",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- helper for mtxrun
+
+function environment.make_format(name)
+ -- change to format path (early as we need expanded paths)
+ local olddir = lfs.currentdir()
+ local path = caches.getwritablepath("formats") or "" -- maybe platform
+ if path ~= "" then
+ lfs.chdir(path)
+ end
+ logs.simple("format path: %s",lfs.currentdir())
+ -- check source file
+ local texsourcename = file.addsuffix(name,"tex")
+ local fulltexsourcename = resolvers.find_file(texsourcename,"tex") or ""
+ if fulltexsourcename == "" then
+ logs.simple("no tex source file with name: %s",texsourcename)
+ lfs.chdir(olddir)
+ return
+ else
+ logs.simple("using tex source file: %s",fulltexsourcename)
+ end
+ local texsourcepath = dir.expand_name(file.dirname(fulltexsourcename)) -- really needed
+ -- check specification
+ local specificationname = file.replacesuffix(fulltexsourcename,"lus")
+ local fullspecificationname = resolvers.find_file(specificationname,"tex") or ""
+ if fullspecificationname == "" then
+ specificationname = file.join(texsourcepath,"context.lus")
+ fullspecificationname = resolvers.find_file(specificationname,"tex") or ""
+ end
+ if fullspecificationname == "" then
+ logs.simple("unknown stub specification: %s",specificationname)
+ lfs.chdir(olddir)
+ return
+ end
+ local specificationpath = file.dirname(fullspecificationname)
+ -- load specification
+ local usedluastub = nil
+ local usedlualibs = dofile(fullspecificationname)
+ if type(usedlualibs) == "string" then
+ usedluastub = file.join(file.dirname(fullspecificationname),usedlualibs)
+ elseif type(usedlualibs) == "table" then
+ logs.simple("using stub specification: %s",fullspecificationname)
+ local texbasename = file.basename(name)
+ local luastubname = file.addsuffix(texbasename,"lua")
+ local lucstubname = file.addsuffix(texbasename,"luc")
+ -- pack libraries in stub
+ logs.simple("creating initialization file: %s",luastubname)
+ utils.merger.selfcreate(usedlualibs,specificationpath,luastubname)
+ -- compile stub file (does not save that much as we don't use this stub at startup any more)
+ local strip = resolvers.boolean_variable("LUACSTRIP", true)
+ if utils.lua.compile(luastubname,lucstubname,false,strip) and lfs.isfile(lucstubname) then
+ logs.simple("using compiled initialization file: %s",lucstubname)
+ usedluastub = lucstubname
+ else
+ logs.simple("using uncompiled initialization file: %s",luastubname)
+ usedluastub = luastubname
+ end
+ else
+ logs.simple("invalid stub specification: %s",fullspecificationname)
+ lfs.chdir(olddir)
+ return
+ end
+ -- generate format
+ local q = string.quote
+ local command = string.format("luatex --ini --lua=%s %s %sdump",q(usedluastub),q(fulltexsourcename),os.platform == "unix" and "\\\\" or "\\")
+ logs.simple("running command: %s\n",command)
+ os.spawn(command)
+ -- remove related mem files
+ local pattern = file.removesuffix(file.basename(usedluastub)).."-*.mem"
+ -- logs.simple("removing related mplib format with pattern '%s'", pattern)
+ local mp = dir.glob(pattern)
+ if mp then
+ for i=1,#mp do
+ local name = mp[i]
+ logs.simple("removing related mplib format %s", file.basename(name))
+ os.remove(name)
+ end
+ end
+ lfs.chdir(olddir)
+end
+
+function environment.run_format(name,data,more)
+ -- hm, rather old code here; we can now use the file.whatever functions
+ if name and name ~= "" then
+ local barename = file.removesuffix(name)
+ local fmtname = caches.getfirstreadablefile(file.addsuffix(barename,"fmt"),"formats")
+ if fmtname == "" then
+ fmtname = resolvers.find_file(file.addsuffix(barename,"fmt")) or ""
+ end
+ fmtname = resolvers.clean_path(fmtname)
+ if fmtname == "" then
+ logs.simple("no format with name: %s",name)
+ else
+ local barename = file.removesuffix(name) -- expanded name
+ local luaname = file.addsuffix(barename,"luc")
+ if not lfs.isfile(luaname) then
+ luaname = file.addsuffix(barename,"lua")
+ end
+ if not lfs.isfile(luaname) then
+ logs.simple("using format name: %s",fmtname)
+ logs.simple("no luc/lua with name: %s",barename)
+ else
+ local q = string.quote
+ local command = string.format("luatex --fmt=%s --lua=%s %s %s",q(barename),q(luaname),q(data),more ~= "" and q(more) or "")
+ logs.simple("running command: %s",command)
+ os.spawn(command)
+ end
+ end
+ end
+end
end -- of closure
-- end library merge
-own = { } -- not local
+own = { } -- not local, might change
+
+own.libs = { -- order can be made better
-own.libs = { -- todo: check which ones are really needed
'l-string.lua',
'l-lpeg.lua',
'l-table.lua',
@@ -11870,24 +12301,32 @@ own.libs = { -- todo: check which ones are really needed
'l-url.lua',
'l-dir.lua',
'l-boolean.lua',
+ 'l-unicode.lua',
'l-math.lua',
--- 'l-unicode.lua',
--- 'l-tex.lua',
'l-utils.lua',
'l-aux.lua',
--- 'l-xml.lua',
+
+ 'trac-inf.lua',
+ 'trac-set.lua',
'trac-tra.lua',
+ 'trac-log.lua',
+ 'trac-pro.lua',
+ 'luat-env.lua', -- can come before inf (as in mkiv)
+
'lxml-tab.lua',
'lxml-lpt.lua',
--- 'lxml-ent.lua',
+ -- 'lxml-ent.lua',
'lxml-mis.lua',
'lxml-aux.lua',
'lxml-xml.lua',
- 'luat-env.lua',
- 'trac-inf.lua',
- 'trac-log.lua',
- 'data-res.lua',
+
+
+ 'data-ini.lua',
+ 'data-exp.lua',
+ 'data-env.lua',
'data-tmp.lua',
+ 'data-met.lua',
+ 'data-res.lua',
'data-pre.lua',
'data-inp.lua',
'data-out.lua',
@@ -11896,13 +12335,15 @@ own.libs = { -- todo: check which ones are really needed
-- 'data-tex.lua',
-- 'data-bin.lua',
'data-zip.lua',
+ 'data-tre.lua',
'data-crl.lua',
'data-lua.lua',
- 'data-kps.lua', -- so that we can replace kpsewhich
'data-aux.lua', -- updater
- 'data-tmf.lua', -- tree files
- -- needed ?
- 'luat-sta.lua', -- states
+ 'data-tmf.lua',
+ 'data-lst.lua',
+
+ 'luat-sta.lua',
+ 'luat-fmt.lua',
}
-- We need this hack till luatex is fixed.
@@ -11915,36 +12356,61 @@ end
-- End of hack.
-own.name = (environment and environment.ownname) or arg[0] or 'luatools.lua'
+own.name = (environment and environment.ownname) or arg[0] or 'mtxrun.lua'
+own.path = string.gsub(string.match(own.name,"^(.+)[\\/].-$") or ".","\\","/")
+
+local ownpath, owntree = own.path, environment and environment.ownpath or own.path
+
+own.list = {
+ '.',
+ ownpath ,
+ ownpath .. "/../sources", -- HH's development path
+ owntree .. "/../../texmf-local/tex/context/base",
+ owntree .. "/../../texmf-context/tex/context/base",
+ owntree .. "/../../texmf-dist/tex/context/base",
+ owntree .. "/../../texmf/tex/context/base",
+ owntree .. "/../../../texmf-local/tex/context/base",
+ owntree .. "/../../../texmf-context/tex/context/base",
+ owntree .. "/../../../texmf-dist/tex/context/base",
+ owntree .. "/../../../texmf/tex/context/base",
+}
+if own.path == "." then table.remove(own.list,1) end
-own.path = string.match(own.name,"^(.+)[\\/].-$") or "."
-own.list = { '.' }
-if own.path ~= '.' then
- table.insert(own.list,own.path)
+local function locate_libs()
+ for l=1,#own.libs do
+ local lib = own.libs[l]
+ for p =1,#own.list do
+ local pth = own.list[p]
+ local filename = pth .. "/" .. lib
+ local found = lfs.isfile(filename)
+ if found then
+ return pth
+ end
+ end
+ end
end
-table.insert(own.list,own.path.."/../../../tex/context/base")
-table.insert(own.list,own.path.."/mtx")
-table.insert(own.list,own.path.."/../sources")
-local function locate_libs()
- for _, lib in pairs(own.libs) do
- for _, pth in pairs(own.list) do
- local filename = string.gsub(pth .. "/" .. lib,"\\","/")
+local function load_libs()
+ local found = locate_libs()
+ if found then
+ for l=1,#own.libs do
+ local filename = found .. "/" .. own.libs[l]
local codeblob = loadfile(filename)
if codeblob then
codeblob()
- own.list = { pth } -- speed up te search
- break
end
end
+ else
+ resolvers = nil
end
end
if not resolvers then
- locate_libs()
+ load_libs()
end
+
if not resolvers then
print("")
print("Mtxrun is unable to start up due to lack of libraries. You may")
@@ -11954,7 +12420,11 @@ if not resolvers then
os.exit()
end
-logs.setprogram('MTXrun',"TDS Runner Tool 1.24",environment.arguments["verbose"] or false)
+logs.setprogram('MTXrun',"TDS Runner Tool 1.26")
+
+if environment.arguments["verbose"] then
+ trackers.enable("resolvers.locating")
+end
local instance = resolvers.reset()
@@ -11982,8 +12452,8 @@ messages.help = [[
--ifchanged=filename only execute when given file has changed (md checksum)
--iftouched=old,new only execute when given file has changed (time stamp)
---make create stubs for (context related) scripts
---remove remove stubs (context related) scripts
+--makestubs create stubs for (context related) scripts
+--removestubs remove stubs (context related) scripts
--stubpath=binpath paths where stubs wil be written
--windows create windows (mswin) stubs
--unix create unix (linux) stubs
@@ -12003,8 +12473,24 @@ messages.help = [[
--forcekpse force using kpse (handy when no mkiv and cache installed but less functionality)
--prefixes show supported prefixes
+
+--generate generate file database
+
+--variables show configuration variables
+--expansions show expanded variables
+--configurations show configuration order
+--expand-braces expand complex variable
+--expand-path expand variable (resolve paths)
+--expand-var expand variable (resolve references)
+--show-path show path expansion of ...
+--var-value report value of variable
+--find-file report file location
+--find-path report path of file
+
+--pattern=str filter variables
]]
+
runners.applications = {
["lua"] = "luatex --luaonly",
["luc"] = "luatex --luaonly",
@@ -12057,45 +12543,40 @@ end
function runners.prepare()
local checkname = environment.argument("ifchanged")
- if checkname and checkname ~= "" then
+ local verbose = environment.argument("verbose")
+ if type(checkname) == "string" and checkname ~= "" then
local oldchecksum = file.loadchecksum(checkname)
local newchecksum = file.checksum(checkname)
if oldchecksum == newchecksum then
- logs.simple("file '%s' is unchanged",checkname)
+ if verbose then
+ logs.simple("file '%s' is unchanged",checkname)
+ end
return "skip"
- else
+ elseif verbose then
logs.simple("file '%s' is changed, processing started",checkname)
end
file.savechecksum(checkname)
end
- local oldname, newname = string.split(environment.argument("iftouched") or "", ",")
- if oldname and newname and oldname ~= "" and newname ~= "" then
- if not file.needs_updating(oldname,newname) then
- logs.simple("file '%s' and '%s' have same age",oldname,newname)
- return "skip"
- else
- logs.simple("file '%s' is older than '%s'",oldname,newname)
- end
- end
- local tree = environment.argument('tree') or ""
- if environment.argument('autotree') then
- tree = os.getenv('TEXMFSTART_TREE') or os.getenv('TEXMFSTARTTREE') or tree
- end
- if tree and tree ~= "" then
- resolvers.load_tree(tree)
- end
- local env = environment.argument('environment') or ""
- if env and env ~= "" then
- for _,e in pairs(string.split(env)) do
- -- maybe force suffix when not given
- resolvers.load_tree(e)
+ local touchname = environment.argument("iftouched")
+ if type(touchname) == "string" and touchname ~= "" then
+ local oldname, newname = string.split(touchname, ",")
+ if oldname and newname and oldname ~= "" and newname ~= "" then
+ if not file.needs_updating(oldname,newname) then
+ if verbose then
+ logs.simple("file '%s' and '%s' have same age",oldname,newname)
+ end
+ return "skip"
+ elseif verbose then
+ logs.simple("file '%s' is older than '%s'",oldname,newname)
+ end
end
end
local runpath = environment.argument("path")
- if runpath and not lfs.chdir(runpath) then
+ if type(runpath) == "string" and not lfs.chdir(runpath) then
logs.simple("unable to change to path '%s'",runpath)
return "error"
end
+ runners.prepare = function() end
return "run"
end
@@ -12333,7 +12814,7 @@ end
function runners.launch_file(filename)
instance.allresults = true
- logs.setverbose(true)
+ trackers.enable("resolvers.locating")
local pattern = environment.arguments["pattern"]
if not pattern or pattern == "" then
pattern = filename
@@ -12413,7 +12894,19 @@ function runners.find_mtx_script(filename)
return fullname
end
-function runners.execute_ctx_script(filename)
+function runners.register_arguments(...)
+ local arguments = environment.arguments_after
+ local passedon = { ... }
+ for i=#passedon,1,-1 do
+ local pi = passedon[i]
+ if pi then
+ table.insert(arguments,1,pi)
+ end
+ end
+end
+
+function runners.execute_ctx_script(filename,...)
+ runners.register_arguments(...)
local arguments = environment.arguments_after
local fullname = runners.find_mtx_script(filename) or ""
if file.extname(fullname) == "cld" then
@@ -12426,7 +12919,7 @@ function runners.execute_ctx_script(filename)
-- retry after generate but only if --autogenerate
if fullname == "" and environment.argument("autogenerate") then -- might become the default
instance.renewcache = true
- logs.setverbose(true)
+ trackers.enable("resolvers.locating")
resolvers.load()
--
fullname = runners.find_mtx_script(filename) or ""
@@ -12466,10 +12959,9 @@ function runners.execute_ctx_script(filename)
return true
end
else
- -- logs.setverbose(true)
if filename == "" or filename == "help" then
local context = resolvers.find_file("mtx-context.lua")
- logs.setverbose(true)
+ trackers.enable("resolvers.locating")
if context ~= "" then
local result = dir.glob((string.gsub(context,"mtx%-context","mtx-*"))) -- () needed
local valid = { }
@@ -12603,87 +13095,317 @@ if environment.argument("usekpse") or environment.argument("forcekpse") or is_mk
end
+ function runners.loadbase()
+ end
+
else
- if not resolvers.load() then
- logs.simple("forcing cache reload")
- instance.renewcache = true
- logs.setverbose(true)
- if not resolvers.load() then
- logs.simple("there is something wrong with your system")
+ function runners.loadbase(...)
+ if not resolvers.load(...) then
+ logs.simple("forcing cache reload")
+ instance.renewcache = true
+ trackers.enable("resolvers.locating")
+ if not resolvers.load(...) then
+ logs.simple("the resolver databases are not present or outdated")
+ end
end
end
end
+resolvers.load_tree(environment.argument('tree'))
+
if environment.argument("selfmerge") then
+
-- embed used libraries
- utils.merger.selfmerge(own.name,own.libs,own.list)
+
+ runners.loadbase()
+ local found = locate_libs()
+ if found then
+ utils.merger.selfmerge(own.name,own.libs,{ found })
+ end
+
elseif environment.argument("selfclean") then
+
-- remove embedded libraries
+
+ runners.loadbase()
utils.merger.selfclean(own.name)
+
elseif environment.argument("selfupdate") then
- logs.setverbose(true)
+
+ runners.loadbase()
+ trackers.enable("resolvers.locating")
resolvers.update_script(own.name,"mtxrun")
+
elseif environment.argument("ctxlua") or environment.argument("internal") then
+
-- run a script by loading it (using libs)
+
+ runners.loadbase()
ok = runners.execute_script(filename,true)
+
elseif environment.argument("script") or environment.argument("scripts") then
+
-- run a script by loading it (using libs), pass args
+
+ runners.loadbase()
if is_mkii_stub then
- -- execute mkii script
ok = runners.execute_script(filename,false,true)
else
ok = runners.execute_ctx_script(filename)
end
+
elseif environment.argument("execute") then
+
-- execute script
+
+ runners.loadbase()
ok = runners.execute_script(filename)
+
elseif environment.argument("direct") then
+
-- equals bin:
+
+ runners.loadbase()
ok = runners.execute_program(filename)
+
elseif environment.argument("edit") then
+
-- edit file
+
+ runners.loadbase()
runners.edit_script(filename)
+
elseif environment.argument("launch") then
+
+ runners.loadbase()
runners.launch_file(filename)
-elseif environment.argument("make") then
- -- make stubs
+
+elseif environment.argument("makestubs") then
+
+ -- make stubs (depricated)
+
runners.handle_stubs(true)
-elseif environment.argument("remove") then
- -- remove stub
+
+elseif environment.argument("removestubs") then
+
+ -- remove stub (depricated)
+
+ runners.loadbase()
runners.handle_stubs(false)
+
elseif environment.argument("resolve") then
+
-- resolve string
+
+ runners.loadbase()
runners.resolve_string(filename)
+
elseif environment.argument("locate") then
+
-- locate file
+
+ runners.loadbase()
runners.locate_file(filename)
-elseif environment.argument("platform")then
+
+elseif environment.argument("platform") or environment.argument("show-platform") then
+
-- locate platform
+
+ runners.loadbase()
runners.locate_platform()
+
elseif environment.argument("prefixes") then
+
+ runners.loadbase()
runners.prefixes()
+
elseif environment.argument("timedrun") then
+
-- locate platform
+
+ runners.loadbase()
runners.timedrun(filename)
+
+elseif environment.argument("variables") or environment.argument("show-variables") then
+
+ -- luatools: runners.execute_ctx_script("mtx-base","--variables",filename)
+
+ resolvers.load("nofiles")
+ resolvers.listers.variables(false,environment.argument("pattern"))
+
+elseif environment.argument("expansions") or environment.argument("show-expansions") then
+
+ -- luatools: runners.execute_ctx_script("mtx-base","--expansions",filename)
+
+ resolvers.load("nofiles")
+ resolvers.listers.expansions(false,environment.argument("pattern"))
+
+elseif environment.argument("configurations") or environment.argument("show-configurations") then
+
+ -- luatools: runners.execute_ctx_script("mtx-base","--configurations",filename)
+
+ resolvers.load("nofiles")
+ resolvers.listers.configurations(false,environment.argument("pattern"))
+
+elseif environment.argument("find-file") then
+
+ -- luatools: runners.execute_ctx_script("mtx-base","--find-file",filename)
+
+ resolvers.load()
+ local pattern = environment.argument("pattern")
+ local format = environment.arguments["format"] or instance.format
+ if not pattern then
+ runners.register_arguments(filename)
+ environment.initialize_arguments(environment.arguments_after)
+ resolvers.for_files(resolvers.find_files,environment.files,format)
+ elseif type(pattern) == "string" then
+ instance.allresults = true -- brrrr
+ resolvers.for_files(resolvers.find_files,{ pattern }, format)
+ end
+
+elseif environment.argument("find-path") then
+
+ -- luatools: runners.execute_ctx_script("mtx-base","--find-path",filename)
+
+ resolvers.load()
+ local path = resolvers.find_path(filename, instance.my_format)
+ if logs.verbose then
+ logs.simple(path)
+ else
+ print(path)
+ end
+
+elseif environment.argument("expand-braces") then
+
+ -- luatools: runners.execute_ctx_script("mtx-base","--expand-braces",filename)
+
+ resolvers.load("nofiles")
+ runners.register_arguments(filename)
+ environment.initialize_arguments(environment.arguments_after)
+ resolvers.for_files(resolvers.expand_braces, environment.files)
+
+elseif environment.argument("expand-path") then
+
+ -- luatools: runners.execute_ctx_script("mtx-base","--expand-path",filename)
+
+ resolvers.load("nofiles")
+ runners.register_arguments(filename)
+ environment.initialize_arguments(environment.arguments_after)
+ resolvers.for_files(resolvers.expand_path, environment.files)
+
+elseif environment.argument("expand-var") or environment.argument("expand-variable") then
+
+ -- luatools: runners.execute_ctx_script("mtx-base","--expand-var",filename)
+
+ resolvers.load("nofiles")
+ runners.register_arguments(filename)
+ environment.initialize_arguments(environment.arguments_after)
+ resolvers.for_files(resolvers.expand_var, environment.files)
+
+elseif environment.argument("show-path") or environment.argument("path-value") then
+
+ -- luatools: runners.execute_ctx_script("mtx-base","--show-path",filename)
+
+ resolvers.load("nofiles")
+ runners.register_arguments(filename)
+ environment.initialize_arguments(environment.arguments_after)
+ resolvers.for_files(resolvers.show_path, environment.files)
+
+elseif environment.argument("var-value") or environment.argument("show-value") then
+
+ -- luatools: runners.execute_ctx_script("mtx-base","--show-value",filename)
+
+ resolvers.load("nofiles")
+ runners.register_arguments(filename)
+ environment.initialize_arguments(environment.arguments_after)
+ resolvers.for_files(resolvers.var_value,environment.files)
+
+elseif environment.argument("format-path") then
+
+ -- luatools: runners.execute_ctx_script("mtx-base","--format-path",filename)
+
+ resolvers.load()
+ logs.simple(caches.getwritablepath("format"))
+
+elseif environment.argument("pattern") then
+
+ -- luatools
+
+ runners.execute_ctx_script("mtx-base","--pattern='" .. environment.argument("pattern") .. "'",filename)
+
+elseif environment.argument("generate") then
+
+ -- luatools
+
+ instance.renewcache = true
+ trackers.enable("resolvers.locating")
+ resolvers.load()
+
+elseif environment.argument("make") or environment.argument("ini") or environment.argument("compile") then
+
+ -- luatools: runners.execute_ctx_script("mtx-base","--make",filename)
+
+ resolvers.load()
+ trackers.enable("resolvers.locating")
+ environment.make_format(filename)
+
+elseif environment.argument("run") then
+
+ -- luatools
+
+ runners.execute_ctx_script("mtx-base","--run",filename)
+
+elseif environment.argument("fmt") then
+
+ -- luatools
+
+ runners.execute_ctx_script("mtx-base","--fmt",filename)
+
+elseif environment.argument("help") and filename=='base' then
+
+ -- luatools
+
+ runners.execute_ctx_script("mtx-base","--help")
+
elseif environment.argument("help") or filename=='help' or filename == "" then
+
logs.help(messages.help)
- -- execute script
+
elseif filename:find("^bin:") then
+
+ runners.loadbase()
ok = runners.execute_program(filename)
+
elseif is_mkii_stub then
+
-- execute mkii script
+
+ runners.loadbase()
ok = runners.execute_script(filename,false,true)
-else
+
+elseif false then
+
+ runners.loadbase()
ok = runners.execute_ctx_script(filename)
if not ok then
ok = runners.execute_script(filename)
end
+
+else
+
+ runners.execute_ctx_script("mtx-base",filename)
+
+end
+
+if logs.verbose then
+ logs.simpleline()
+ logs.simple("runtime: %0.3f seconds",os.runtime())
end
-if os.platform == "unix" then
- io.write("\n")
+if os.type ~= "windows" then
+ texio.write("\n")
end
if ok == false then ok = 1 elseif ok == true then ok = 0 end