summaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'scripts')
-rw-r--r--scripts/context/lua/mtxrun.lua1345
-rw-r--r--scripts/context/stubs/mswin/mtxrun.lua1345
-rw-r--r--scripts/context/stubs/unix/mtxrun1345
3 files changed, 2292 insertions, 1743 deletions
diff --git a/scripts/context/lua/mtxrun.lua b/scripts/context/lua/mtxrun.lua
index 3be305bed..6b74022ae 100644
--- a/scripts/context/lua/mtxrun.lua
+++ b/scripts/context/lua/mtxrun.lua
@@ -119,7 +119,7 @@ local patterns_escapes = {
["."] = "%.",
["+"] = "%+", ["-"] = "%-", ["*"] = "%*",
["["] = "%[", ["]"] = "%]",
- ["("] = "%)", [")"] = "%)",
+ ["("] = "%(", [")"] = "%)",
-- ["{"] = "%{", ["}"] = "%}"
-- ["^"] = "%^", ["$"] = "%$",
}
@@ -185,6 +185,7 @@ local patterns = lpeg.patterns
local P, R, S, V, match = lpeg.P, lpeg.R, lpeg.S, lpeg.V, lpeg.match
local Ct, C, Cs, Cc, Cf, Cg = lpeg.Ct, lpeg.C, lpeg.Cs, lpeg.Cc, lpeg.Cf, lpeg.Cg
+local lpegtype = lpeg.type
local utfcharacters = string.utfcharacters
local utfgmatch = unicode and unicode.utf8.gmatch
@@ -201,7 +202,6 @@ patterns.alwaysmatched = alwaysmatched
local digit, sign = R('09'), S('+-')
local cr, lf, crlf = P("\r"), P("\n"), P("\r\n")
local newline = crlf + cr + lf
-local utf8next = R("\128\191")
local escaped = P("\\") * anything
local squote = P("'")
local dquote = P('"')
@@ -222,6 +222,8 @@ local utftype = utfbom_32_be / "utf-32-be" + utfbom_32_le / "utf-32-le
+ utfbom_16_be / "utf-16-be" + utfbom_16_le / "utf-16-le"
+ utfbom_8 / "utf-8" + alwaysmatched / "unknown"
+local utf8next = R("\128\191")
+
patterns.utf8one = R("\000\127")
patterns.utf8two = R("\194\223") * utf8next
patterns.utf8three = R("\224\239") * utf8next * utf8next
@@ -432,19 +434,25 @@ end
-- Just for fun I looked at the used bytecode and
-- p = (p and p + pp) or pp gets one more (testset).
-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]
- if p then
- p = p + pp
- else
- p = pp
+function lpeg.replacer(one,two)
+ if type(one) == "table" then
+ local no = #one
+ if no > 0 then
+ local p
+ for i=1,no do
+ local o = one[i]
+ local pp = P(o[1]) / o[2]
+ if p then
+ p = p + pp
+ else
+ p = pp
+ end
end
+ return Cs((p + 1)^0)
end
- return Cs((p + 1)^0)
+ else
+ two = two or ""
+ return Cs((P(one)/two + 1)^0)
end
end
@@ -646,6 +654,10 @@ function lpeg.oneof(list,...) -- lpeg.oneof("elseif","else","if","then")
return p
end
+function lpeg.is_lpeg(p)
+ return p and lpegtype(p) == "pattern"
+end
+
end -- of closure
@@ -2558,6 +2570,9 @@ local separator = P("://")
local qualified = P(".")^0 * P("/") + letter*P(":") + letter^1*separator + letter^1 * P("/")
local rootbased = P("/") + letter*P(":")
+lpeg.patterns.qualified = qualified
+lpeg.patterns.rootbased = rootbased
+
-- ./name ../name /name c: :// name/name
function file.is_qualified_path(filename)
@@ -2678,72 +2693,95 @@ if not modules then modules = { } end modules ['l-url'] = {
license = "see context related readme files"
}
-local char, gmatch, gsub, format, byte = string.char, string.gmatch, string.gsub, string.format, string.byte
+local char, gmatch, gsub, format, byte, find = string.char, string.gmatch, string.gsub, string.format, string.byte, string.find
local concat = table.concat
local tonumber, type = tonumber, type
-local lpegmatch, lpegP, lpegC, lpegR, lpegS, lpegCs, lpegCc = lpeg.match, lpeg.P, lpeg.C, lpeg.R, lpeg.S, lpeg.Cs, lpeg.Cc
+local P, C, R, S, Cs, Cc, Ct = lpeg.P, lpeg.C, lpeg.R, lpeg.S, lpeg.Cs, lpeg.Cc, lpeg.Ct
+local lpegmatch, lpegpatterns, replacer = lpeg.match, lpeg.patterns, lpeg.replacer
--- from the spec (on the web):
+-- from wikipedia:
--
--- foo://example.com:8042/over/there?name=ferret#nose
--- \_/ \______________/\_________/ \_________/ \__/
--- | | | | |
--- scheme authority path query fragment
--- | _____________________|__
--- / \ / \
--- urn:example:animal:ferret:nose
+-- foo://username:password@example.com:8042/over/there/index.dtb?type=animal;name=narwhal#nose
+-- \_/ \_______________/ \_________/ \__/ \___/ \_/ \______________________/ \__/
+-- | | | | | | | |
+-- | userinfo hostname port | | query fragment
+-- | \________________________________/\_____________|____|/
+-- scheme | | | |
+-- | authority path | |
+-- | | |
+-- | path interpretable as filename
+-- | ___________|____________ |
+-- / \ / \ |
+-- urn:example:animal:ferret:nose interpretable as extension
url = url or { }
local url = url
-local function tochar(s)
- return char(tonumber(s,16))
-end
+local tochar = function(s) return char(tonumber(s,16)) end
-local colon, qmark, hash, slash, percent, endofstring = lpegP(":"), lpegP("?"), lpegP("#"), lpegP("/"), lpegP("%"), lpegP(-1)
+local colon = P(":")
+local qmark = P("?")
+local hash = P("#")
+local slash = P("/")
+local percent = P("%")
+local endofstring = P(-1)
-local hexdigit = lpegR("09","AF","af")
-local plus = lpegP("+")
-local nothing = lpegCc("")
-local escaped = (plus / " ") + (percent * lpegC(hexdigit * hexdigit) / tochar)
+local hexdigit = R("09","AF","af")
+local plus = P("+")
+local nothing = Cc("")
+local escaped = (plus / " ") + (percent * C(hexdigit * hexdigit) / tochar)
-- we assume schemes with more than 1 character (in order to avoid problems with windows disks)
-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)
+local scheme = Cs((escaped+(1-colon-slash-qmark-hash))^2) * colon + nothing
+local authority = slash * slash * Cs((escaped+(1- slash-qmark-hash))^0) + nothing
+local path = slash * Cs((escaped+(1- qmark-hash))^0) + nothing
+local query = qmark * Cs((escaped+(1- hash))^0) + nothing
+local fragment = hash * Cs((escaped+(1- endofstring))^0) + nothing
-lpeg.patterns.urlsplitter = parser
+local parser = Ct(scheme * authority * path * query * fragment)
-local escapes = { }
+lpegpatterns.urlsplitter = parser
-for i=0,255 do
- escapes[i] = format("%%%02X",i)
-end
+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)
+local escaper = Cs((R("09","AZ","az") + S("-./_") + P(1) / escapes)^0)
-lpeg.patterns.urlescaper = escaper
+lpegpatterns.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
-function url.split(str)
+local function split(str)
return (type(str) == "string" and lpegmatch(parser,str)) or str
end
+local function hasscheme(str)
+ local scheme = lpegmatch(scheme,str) -- at least one character
+ return scheme and scheme ~= ""
+end
+
-- todo: cache them
-function url.hashed(str) -- not yet ok (/test?test)
- local s = url.split(str)
+local rootletter = R("az","AZ")
+ + S("_-+")
+local separator = P("://")
+local qualified = P(".")^0 * P("/")
+ + rootletter * P(":")
+ + rootletter^1 * separator
+ + rootletter^1 * P("/")
+local rootbased = P("/")
+ + rootletter * P(":")
+
+local barswapper = replacer("|",":")
+local backslashswapper = replacer("\\","/")
+
+local function hashed(str) -- not yet ok (/test?test)
+ local s = split(str)
local somescheme = s[1] ~= ""
local somequery = s[4] ~= ""
if not somescheme and not somequery then
- return {
+ s = {
scheme = "file",
authority = "",
path = str,
@@ -2751,52 +2789,73 @@ function url.hashed(str) -- not yet ok (/test?test)
fragment = "",
original = str,
noscheme = true,
+ filename = str,
}
- else
- return {
+ else -- not always a filename but handy anyway
+ local authority, path, filename = s[2], s[3]
+ if authority == "" then
+ filename = path
+ else
+ filename = authority .. "/" .. path
+ end
+ s = {
scheme = s[1],
- authority = s[2],
- path = s[3],
+ authority = authority,
+ path = path,
query = s[4],
fragment = s[5],
original = str,
noscheme = false,
+ filename = filename,
}
end
+ return s
end
+-- Here we assume:
+--
+-- files: /// = relative
+-- files: //// = absolute (!)
+
-function url.hasscheme(str)
- return url.split(str)[1] ~= ""
-end
-function url.addscheme(str,scheme)
- return (url.hasscheme(str) and str) or ((scheme or "file:///") .. str)
+url.split = split
+url.hasscheme = hasscheme
+url.hashed = hashed
+
+function url.addscheme(str,scheme) -- no authority
+ if hasscheme(str) then
+ return str
+ elseif not scheme then
+ return "file:///" .. str
+ else
+ return scheme .. ":///" .. str
+ end
end
function url.construct(hash) -- dodo: we need to escape !
- local fullurl = { }
+ local fullurl, f = { }, 0
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 .. "://"
+ f = f + 1 ; fullurl[f] = scheme .. "://"
end
if authority and authority ~= "" then
- fullurl[#fullurl+1] = authority
+ f = f + 1 ; fullurl[f] = authority
end
if path and path ~= "" then
- fullurl[#fullurl+1] = "/" .. path
+ f = f + 1 ; fullurl[f] = "/" .. path
end
if query and query ~= "" then
- fullurl[#fullurl+1] = "?".. query
+ f = f + 1 ; fullurl[f] = "?".. query
end
if fragment and fragment ~= "" then
- fullurl[#fullurl+1] = "#".. fragment
+ f = f + 1 ; fullurl[f] = "#".. fragment
end
return lpegmatch(escaper,concat(fullurl))
end
function url.filename(filename)
- local t = url.hashed(filename)
+ local t = hashed(filename)
return (t.scheme == "file" and (gsub(t.path,"^/([a-zA-Z])([:|])/)","%1:"))) or filename
end
@@ -2820,6 +2879,7 @@ end
+
end -- of closure
do -- create closure to overcome 200 locals limit
@@ -2861,25 +2921,22 @@ end
-- optimizing for no find (*) does not save time
+
local function globpattern(path,patt,recurse,action)
- local ok, scanner
if path == "/" then
- ok, scanner = xpcall(function() return walkdir(path..".") end, function() end) -- kepler safe
- else
- ok, scanner = xpcall(function() return walkdir(path) end, function() end) -- kepler safe
+ path = path .. "."
+ elseif not find(path,"/$") then
+ path = path .. '/'
end
- if ok and type(scanner) == "function" then
- if not find(path,"/$") then path = path .. '/' end
- for name in scanner do
- local full = path .. name
- local mode = attributes(full,'mode')
- if mode == 'file' then
- if find(full,patt) then
- action(full)
- end
- elseif recurse and (mode == "directory") and (name ~= '.') and (name ~= "..") then
- globpattern(full,patt,recurse,action)
+ for name in walkdir(path) do
+ local full = path .. name
+ local mode = attributes(full,'mode')
+ if mode == 'file' then
+ if find(full,patt) then
+ action(full)
end
+ elseif recurse and (mode == "directory") and (name ~= '.') and (name ~= "..") then
+ globpattern(full,patt,recurse,action)
end
end
end
@@ -9363,10 +9420,10 @@ if not modules then modules = { } end modules ['data-exp'] = {
license = "see context related readme files",
}
-local format, gsub, find, gmatch, lower = string.format, string.gsub, string.find, string.gmatch, string.lower
+local format, find, gmatch, lower = string.format, 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 Ct, Cs, Cc, P, C, S = lpeg.Ct, lpeg.Cs, lpeg.Cc, lpeg.P, lpeg.C, lpeg.S
local type, next = type, next
local ostype = os.type
@@ -9381,7 +9438,7 @@ local resolvers = resolvers
-- 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.
+-- past this every time. See data-obs.lua for the gsub variant.
-- {a,b,c,d}
-- a,b,c/{p,q,r},d
@@ -9396,95 +9453,70 @@ local resolvers = resolvers
-- {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 function do_first(a,b)
+local function f_first(a,b)
local t, n = { }, 0
for s in gmatch(b,"[^,]+") do
- n = n + 1
- t[n] = a .. s
+ n = n + 1 ; t[n] = a .. s
end
- return "{" .. concat(t,",") .. "}"
+ return concat(t,",")
end
-local function do_second(a,b)
+local function f_second(a,b)
local t, n = { }, 0
for s in gmatch(a,"[^,]+") do
- n = n + 1
- t[n] = s .. b
+ n = n + 1 ; t[n] = s .. b
end
- return "{" .. concat(t,",") .. "}"
+ return concat(t,",")
end
-local function do_both(a,b)
+local function f_both(a,b)
local t, n = { }, 0
for sa in gmatch(a,"[^,]+") do
for sb in gmatch(b,"[^,]+") do
- n = n + 1
- t[n] = sa .. sb
+ n = n + 1 ; t[n] = sa .. sb
end
end
- return "{" .. concat(t,",") .. "}"
+ return concat(t,",")
end
-local function do_three(a,b,c)
- return a .. b.. c
-end
+local left = P("{")
+local right = P("}")
+local var = P((1 - S("{}" ))^0)
+local set = P((1 - S("{},"))^0)
+local other = P(1)
-local stripper_1 = lpeg.stripper("{}@")
+local l_first = Cs( ( Cc("{") * (C(set) * left * C(var) * right / f_first) * Cc("}") + other )^0 )
+local l_second = Cs( ( Cc("{") * (left * C(var) * right * C(set) / f_second) * Cc("}") + other )^0 )
+local l_both = Cs( ( Cc("{") * (left * C(var) * right * left * C(var) * right / f_both) * Cc("}") + other )^0 )
+local l_rest = Cs( ( left * var * (left/"") * var * (right/"") * var * right + other )^0 )
-local replacer_1 = lpeg.replacer {
- { ",}", ",@}" },
- { "{,", "{@," },
-}
+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
+local function splitpathexpr(str, newlist, validate) -- I couldn't resist lpegging it (nice exercise).
if trace_expansions then
report_resolvers("expanding variable '%s'",str)
end
local t, ok, done = newlist or { }, false, false
local n = #t
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
+ repeat local old = str
+ repeat local old = str ; str = lpegmatch(l_first, str) until old == str
+ repeat local old = str ; str = lpegmatch(l_second,str) until old == str
+ repeat local old = str ; str = lpegmatch(l_both, str) until old == str
+ repeat local old = str ; str = lpegmatch(l_rest, str) until old == str
+ until old == str -- or not find(str,"{")
str = lpegmatch(stripper_1,str)
if validate then
for s in gmatch(str,"[^,]+") do
s = validate(s)
if s then
- n = n + 1
- t[n] = s
+ n = n + 1 ; t[n] = s
end
end
else
for s in gmatch(str,"[^,]+") do
- n = n + 1
- t[n] = s
+ n = n + 1 ; t[n] = s
end
end
if trace_expansions then
@@ -9495,50 +9527,23 @@ local function splitpathexpr(str, newlist, validate)
return t
end
+-- We could make the previous one public.
+
local function validate(s)
- local isrecursive = find(s,"//$")
- s = collapsepath(s)
- if isrecursive then
- s = s .. "//"
- end
- return s ~= "" and not find(s,dummy_path_expr) and s
+ s = collapsepath(s) -- already keeps the //
+ return s ~= "" and not find(s,"^!*unset/*$") and s
end
resolvers.validatedpath = validate -- keeps the trailing //
-function resolvers.expandedpathfromlist(pathlist) -- maybe not a list, just a path
- -- a previous version fed back into pathlist
- local newlist, ok = { }, false
+function resolvers.expandedpathfromlist(pathlist)
+ local newlist = { }
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
- local n = 0
- for k=1,#pathlist do
- for p in gmatch(pathlist[k],"([^,]+)") do
- p = validate(p)
- if p ~= "" then
- n = n + 1
- newlist[n] = p
- end
- end
- end
+ splitpathexpr(pathlist[k],newlist,validate)
end
return newlist
end
--- We also put some cleanup code here.
-
-
-
-
local cleanup = lpeg.replacer {
{ "!" , "" },
{ "\\" , "/" },
@@ -9576,14 +9581,13 @@ end
-- This one strips quotes and funny tokens.
+local expandhome = P("~") / "$HOME" -- environment.homedir
-local expandhome = lpegP("~") / "$HOME" -- environment.homedir
+local dodouble = P('"')/"" * (expandhome + (1 - P('"')))^0 * P('"')/""
+local dosingle = P("'")/"" * (expandhome + (1 - P("'")))^0 * P("'")/""
+local dostring = (expandhome + 1 )^0
-local dodouble = lpegP('"')/"" * (expandhome + (1 - lpegP('"')))^0 * lpegP('"')/""
-local dosingle = lpegP("'")/"" * (expandhome + (1 - lpegP("'")))^0 * lpegP("'")/""
-local dostring = (expandhome + 1 )^0
-
-local stripper = lpegCs(
+local stripper = Cs(
lpegpatterns.unspacer * (dosingle + dodouble + dostring) * lpegpatterns.unspacer
)
@@ -9599,7 +9603,9 @@ end
local cache = { }
-local splitter = lpegCt(lpeg.splitat(lpegS(ostype == "windows" and ";" or ":;"))) -- maybe add ,
+local splitter = Ct(lpeg.splitat(S(ostype == "windows" and ";" or ":;"))) -- maybe add ,
+
+local backslashswapper = lpeg.replacer("\\","/")
local function splitconfigurationpath(str) -- beware, this can be either a path or a { specification }
if str then
@@ -9608,8 +9614,7 @@ local function splitconfigurationpath(str) -- beware, this can be either a path
if str == "" then
found = { }
else
- str = gsub(str,"\\","/")
- local split = lpegmatch(splitter,str)
+ local split = lpegmatch(splitter,lpegmatch(backslashswapper,str)) -- can be combined
found = { }
local noffound = 0
for i=1,#split do
@@ -9658,57 +9663,62 @@ end
-local weird = lpegP(".")^1 + lpeg.anywhere(lpegS("~`!#$%^&*()={}[]:;\"\'||<>,?\n\r\t"))
+local weird = P(".")^1 + lpeg.anywhere(S("~`!#$%^&*()={}[]:;\"\'||<>,?\n\r\t"))
-function resolvers.scanfiles(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
+local attributes, directory = lfs.attributes, lfs.dir
+
+local function scan(files,spec,path,n,m,r)
+ local full = (path == "" and spec) or (spec .. path .. '/')
+ local dirs, nofdirs = { }, 0
+ 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
- dirs[#dirs+1] = name
+ 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
+ nofdirs = nofdirs + 1
+ if path ~= "" then
+ dirs[nofdirs] = path..'/'..name
+ else
+ dirs[nofdirs] = name
+ end
end
end
- if #dirs > 0 then
- sort(dirs)
- for i=1,#dirs do
- scan(spec,dirs[i])
- end
+ end
+ if nofdirs > 0 then
+ sort(dirs)
+ for i=1,nofdirs do
+ files, n, m, r = scan(files,spec,dirs[i],n,m,r)
end
end
- scan(specification .. '/',"")
- files.__files__, files.__directories__, files.__remappings__ = n, m, r
+ return files, n, m, r
+end
+
+function resolvers.scanfiles(path)
+ if trace_locating then
+ report_resolvers("scanning path '%s'",path)
+ end
+ local files, n, m, r = scan({ },path .. '/',"",0,0,0)
+ files.__path__ = path
+ files.__files__ = n
+ files.__directories__ = m
+ files.__remappings__ = r
if trace_locating then
report_resolvers("%s files found on %s directories with %s uppercase remappings",n,m,r)
end
@@ -10399,9 +10409,15 @@ if not modules then modules = { } end modules ['data-met'] = {
license = "see context related readme files"
}
-local find = string.find
+local find, format = string.find, string.format
+local sequenced = table.sequenced
+local addurlscheme, urlhashed = url.addscheme, url.hashed
+
+local trace_locating = false
+
+trackers.register("resolvers.locating", function(v) trace_methods = v end)
+trackers.register("resolvers.methods", function(v) trace_methods = v end)
-local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
local report_resolvers = logs.new("resolvers")
@@ -10409,41 +10425,109 @@ local allocate = utilities.storage.allocate
local resolvers = resolvers
-resolvers.concatinators = allocate ()
-resolvers.locators = allocate { notfound = { nil } } -- locate databases
-resolvers.hashers = allocate { notfound = { nil } } -- load databases
-resolvers.generators = allocate { notfound = { nil } } -- generate databases
+local registered = { }
-function resolvers.splitmethod(filename) -- todo: trigger by suffix
+local function splitmethod(filename) -- todo: filetype in specification
if not filename then
- return { } -- safeguard
- elseif type(filename) == "table" then
+ return { scheme = "unknown", original = filename }
+ end
+ if type(filename) == "table" then
return filename -- already split
- elseif not find(filename,"://") then
- return { scheme="file", path = filename, original = filename } -- quick hack
+ end
+ filename = file.collapsepath(filename)
+ if not find(filename,"://") then
+ return { scheme = "file", path = filename, original = filename, filename = filename }
+ end
+ local specification = url.hashed(filename)
+ if not specification.scheme or specification.scheme == "" then
+ return { scheme = "file", path = filename, original = filename, filename = filename }
else
- return url.hashed(filename)
+ return specification
end
end
-function resolvers.methodhandler(what, filename, filetype) -- ...
- filename = file.collapsepath(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("using special handler for '%s' -> '%s' -> '%s'",specification.original,what,table.sequenced(specification))
+resolvers.splitmethod = splitmethod -- bad name but ok
+
+-- the second argument is always analyzed (saves time later on) and the original
+-- gets passed as original but also as argument
+
+local function methodhandler(what,first,...) -- filename can be nil or false
+ local method = registered[what]
+ if method then
+ local how, namespace = method.how, method.namespace
+ if how == "uri" or how == "url" then
+ local specification = splitmethod(first)
+ local scheme = specification.scheme
+ local resolver = namespace and namespace[scheme]
+ if resolver then
+ if trace_methods then
+ report_resolvers("resolver: method=%s, how=%s, scheme=%s, argument=%s",what,how,scheme,first)
+ end
+ return resolver(specification,...)
+ else
+ resolver = namespace.default or namespace.file
+ if resolver then
+ if trace_methods then
+ report_resolvers("resolver: method=%s, how=%s, default, argument=%s",what,how,first)
+ end
+ return resolver(specification,...)
+ elseif trace_methods then
+ report_resolvers("resolver: method=%s, how=%s, no handler",what,how)
+ end
+ end
+ elseif how == "tag" then
+ local resolver = namespace and namespace[first]
+ if resolver then
+ if trace_methods then
+ report_resolvers("resolver: method=%s, how=%s, tag=%s",what,how,first)
+ end
+ return resolver(...)
+ else
+ resolver = namespace.default or namespace.file
+ if resolver then
+ if trace_methods then
+ report_resolvers("resolver: method=%s, how=%s, default",what,how)
+ end
+ return resolver(...)
+ elseif trace_methods then
+ report_resolvers("resolver: method=%s, how=%s, unknown",what,how)
+ end
+ end
end
- return resolver[scheme](filename,filetype,specification) -- todo: query
else
- if trace_locating then
- report_resolvers("no handler for '%s' -> '%s' -> '%s'",specification.original,what,table.sequenced(specification))
+ report_resolvers("resolver: method=%s, unknown",what)
+ end
+end
+
+resolvers.methodhandler = methodhandler
+
+function resolvers.registermethod(name,namespace,how)
+ registered[name] = { how = how or "tag", namespace = namespace }
+ namespace["byscheme"] = function(scheme,filename,...)
+ if scheme == "file" then
+ return methodhandler(name,filename,...)
+ else
+ return methodhandler(name,addurlscheme(filename,scheme),...)
end
- return resolver.tex(filename,filetype) -- todo: specification
end
end
+local concatinators = allocate { notfound = file.join } -- concatinate paths
+local locators = allocate { notfound = function() end } -- locate databases
+local hashers = allocate { notfound = function() end } -- load databases
+local generators = allocate { notfound = function() end } -- generate databases
+
+resolvers.concatinators = concatinators
+resolvers.locators = locators
+resolvers.hashers = hashers
+resolvers.generators = generators
+
+local registermethod = resolvers.registermethod
+
+registermethod("concatinators",concatinators,"tag")
+registermethod("locators", locators, "uri")
+registermethod("hashers", hashers, "uri")
+registermethod("generators", generators, "uri")
end -- of closure
@@ -10471,11 +10555,11 @@ local concat, insert, sortedkeys = table.concat, table.insert, table.sortedkeys
local next, type = next, type
local os = os
-local lpegP, lpegS, lpegR, lpegC, lpegCc, lpegCs, lpegCt = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.Cc, lpeg.Cs, lpeg.Ct
+local P, S, R, C, Cc, Cs, Ct, Carg = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.Cc, lpeg.Cs, lpeg.Ct, lpeg.Carg
local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns
local filedirname, filebasename, fileextname, filejoin = file.dirname, file.basename, file.extname, file.join
-local collapsepath = file.collapsepath
+local collapsepath, joinpath = file.collapsepath, file.joinpath
local allocate = utilities.storage.allocate
local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
@@ -10489,6 +10573,7 @@ local resolvers = resolvers
local expandedpathfromlist = resolvers.expandedpathfromlist
local checkedvariable = resolvers.checkedvariable
local splitconfigurationpath = resolvers.splitconfigurationpath
+local methodhandler = resolvers.methodhandler
local initializesetter = utilities.setters.initialize
@@ -10502,12 +10587,12 @@ resolvers.luacnfspec = '{$SELFAUTODIR,$SELFAUTOPARENT}{,{/share,}/texmf{-local
resolvers.luacnfname = 'texmfcnf.lua'
resolvers.luacnfstate = "unknown"
-local unset_variable = "unset"
+local unset_variable = "unset"
-local formats = resolvers.formats
-local suffixes = resolvers.suffixes
-local dangerous = resolvers.dangerous
-local suffixmap = resolvers.suffixmap
+local formats = resolvers.formats
+local suffixes = resolvers.suffixes
+local dangerous = resolvers.dangerous
+local suffixmap = resolvers.suffixmap
resolvers.defaultsuffixes = { "tex" } -- "mkiv", "cld" -- too tricky
@@ -10552,7 +10637,7 @@ function resolvers.newinstance()
end
-function resolvers.setinstance(someinstance)
+function resolvers.setinstance(someinstance) -- only one instance is active
instance = someinstance
resolvers.instance = someinstance
return someinstance
@@ -10574,7 +10659,7 @@ function resolvers.setenv(key,value)
end
end
-function resolvers.getenv(key)
+local function getenv(key)
local value = instance.environment[key]
if value and value ~= "" then
return value
@@ -10584,23 +10669,55 @@ function resolvers.getenv(key)
end
end
-resolvers.env = resolvers.getenv
+resolvers.getenv = getenv
+resolvers.env = getenv
+
+local function resolve(key)
+ local value = instance.variables[key] or ""
+ return (value ~= "" and value) or getenv(key) or ""
+end
+
+local dollarstripper = lpeg.stripper("$")
+local inhibitstripper = P("!")^0 * Cs(P(1)^0)
+local backslashswapper = lpeg.replacer("\\","/")
+
+local somevariable = P("$") / ""
+local somekey = C(R("az","AZ","09","__","--")^1)
+local somethingelse = P(";") * ((1-S("!{}/\\"))^1 * P(";") / "")
+ + P(";") * (P(";") / "")
+ + P(1)
+
+local pattern = Cs( (somevariable * (somekey/resolve) + somethingelse)^1 )
local function expandvars(lst) -- simple vars
- local variables, getenv = instance.variables, resolvers.getenv
- local function resolve(a)
- local va = variables[a] or ""
- return (va ~= "" and va) or getenv(a) or ""
- end
for k=1,#lst do
- local var = lst[k]
- var = gsub(var,"%$([%a%d%_%-]+)",resolve)
- var = gsub(var,";+",";")
- var = gsub(var,";[!{}/\\]+;",";")
- lst[k] = var
+ local lk = lst[k]
+ lst[k] = lpegmatch(pattern,lk) or lk
+ end
+end
+
+
+local slash = P("/")
+
+local pattern = Cs (
+ Cc("^") * (
+ Cc("%") * S(".-")
+ + slash^2 * P(-1) / "/.*"
+ + slash^2 / "/.-/"
+ + (1-slash) * P(-1) * Cc("/")
+ + P(1)
+ )^1 * Cc("$")
+)
+
+local function makepathexpression(str)
+ if str == "." then
+ return "^%./$"
+ else
+ return lpegmatch(pattern,str)
end
end
+
local function resolve(key)
local value = instance.variables[key]
if value and value ~= "" then
@@ -10614,22 +10731,21 @@ local function resolve(key)
return e ~= nil and e ~= "" and checkedvariable(e) or ""
end
+local pattern = Cs( (somevariable * (somekey/resolve) + somethingelse)^1 )
+
local function expandedvariable(var) -- simple vars
- var = gsub(var,"%$([%a%d%_%-]+)",resolve)
- var = gsub(var,";+",";")
- var = gsub(var,";[!{}/\\]+;",";")
- return var
+ return lpegmatch(pattern,var) or var
end
+
local function entry(entries,name)
if name and name ~= "" then
- name = gsub(name,'%$','')
- -- local result = entries[name..'.'..instance.progname] or entries[name]
+ name = lpegmatch(dollarstripper,name)
local result = entries[instance.progname .. '.' .. name] or entries[name]
if result then
return result
else
- result = resolvers.getenv(name)
+ result = getenv(name)
if result then
instance.variables[name] = result
resolvers.expandvariables()
@@ -10642,8 +10758,7 @@ end
local function is_entry(entries,name)
if name and name ~= "" then
- name = gsub(name,'%$','')
- -- return (entries[name..'.'..instance.progname] or entries[name]) ~= nil
+ name = lpegmatch(dollarstripper,name)
return (entries[instance.progname .. '.' .. name] or entries[name]) ~= nil
else
return false
@@ -10654,7 +10769,7 @@ local function reportcriticalvariables()
if trace_locating then
for i=1,#resolvers.criticalvars do
local v = resolvers.criticalvars[i]
- report_resolvers("variable '%s' set to '%s'",v,resolvers.getenv(v) or "unknown")
+ report_resolvers("variable '%s' set to '%s'",v,getenv(v) or "unknown")
end
report_resolvers()
end
@@ -10664,7 +10779,7 @@ end
local function identify_configuration_files()
local specification = instance.specification
if #specification == 0 then
- local cnfspec = resolvers.getenv('TEXMFCNF')
+ local cnfspec = getenv('TEXMFCNF')
if cnfspec == "" then
cnfspec = resolvers.luacnfspec
resolvers.luacnfstate = "default"
@@ -10736,7 +10851,6 @@ local function load_configuration_files()
end
end
setups[pathname] = t
-
if resolvers.luacnfstate == "default" then
-- the following code is not tested
local cnfspec = t["TEXMFCNF"]
@@ -10798,63 +10912,30 @@ end
-- database loading
--- locators
-
-function resolvers.locatedatabase(specification)
- return resolvers.methodhandler('locators', specification)
-end
-
-function resolvers.locators.tex(specification)
- if specification and specification ~= '' and lfs.isdir(specification) then
- if trace_locating then
- report_resolvers("tex locator '%s' found",specification)
- end
- resolvers.appendhash('file',specification,filename,true) -- cache
- elseif trace_locating then
- report_resolvers("tex locator '%s' not found",specification)
- end
-end
-
--- hashers
-
-function resolvers.hashdatabase(tag,name)
- return resolvers.methodhandler('hashers',tag,name)
-end
-
local function load_file_databases()
instance.loaderror, instance.files = false, allocate()
if not instance.renewcache then
local hashes = instance.hashes
for k=1,#hashes do
local hash = hashes[k]
- resolvers.hashdatabase(hash.tag,hash.name)
+ resolvers.hashers.byscheme(hash.type,hash.name)
if instance.loaderror then break end
end
end
end
-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
-
local function locate_file_databases()
-- todo: cache:// and tree:// (runtime)
local texmfpaths = resolvers.expandedpathlist('TEXMF')
for i=1,#texmfpaths do
local path = collapsepath(texmfpaths[i])
- local stripped = gsub(path,"^!!","")
- local runtime = stripped == path
- path = resolvers.cleanpath(path)
+ local stripped = lpegmatch(inhibitstripper,path)
if stripped ~= "" then
+ local runtime = stripped == path
+ path = resolvers.cleanpath(path)
if lfs.isdir(path) then
local spec = resolvers.splitmethod(stripped)
- if spec.scheme == "cache" then
+ if spec.scheme == "cache" or spec.scheme == "file" then
stripped = spec.path
elseif runtime and (spec.noscheme or spec.scheme == "file") then
stripped = "tree:///" .. stripped
@@ -10866,7 +10947,7 @@ local function locate_file_databases()
report_resolvers("locating list of '%s' (cached)",path)
end
end
- resolvers.locatedatabase(stripped) -- nothing done with result
+ methodhandler('locators',stripped) -- nothing done with result
else
if trace_locating then
if runtime then
@@ -10885,8 +10966,9 @@ end
local function generate_file_databases()
local hashes = instance.hashes
- for i=1,#hashes do
- resolvers.methodhandler('generators',hashes[i].tag)
+ for k=1,#hashes do
+ local hash = hashes[k]
+ methodhandler('generators',hash.name)
end
if trace_locating then
report_resolvers()
@@ -10896,10 +10978,13 @@ end
local function save_file_databases() -- will become cachers
for i=1,#instance.hashes do
local hash = instance.hashes[i]
- local cachename = hash.tag
+ local cachename = hash.name
if hash.cache then
local content = instance.files[cachename]
caches.collapsecontent(content)
+ if trace_locating then
+ report_resolvers("saving tree '%s'",cachename)
+ end
caches.savecontent(cachename,"files",content)
elseif trace_locating then
report_resolvers("not saving runtime tree '%s'",cachename)
@@ -10923,23 +11008,22 @@ local function load_databases()
end
end
-function resolvers.appendhash(type,tag,name,cache)
+function resolvers.appendhash(type,name,cache)
if trace_locating then
- report_resolvers("hash '%s' appended",tag)
+ report_resolvers("hash '%s' appended",name)
end
- insert(instance.hashes, { type = type, tag = tag, name = name, cache = cache } )
+ insert(instance.hashes, { type = type, name = name, cache = cache } )
end
-function resolvers.prependhash(type,tag,name,cache)
+function resolvers.prependhash(type,name,cache)
if trace_locating then
- report_resolvers("hash '%s' prepended",tag)
+ report_resolvers("hash '%s' prepended",name)
end
- insert(instance.hashes, 1, { type = type, tag = tag, name = name, cache = cache } )
+ insert(instance.hashes, 1, { type = type, name = name, cache = cache } )
end
function resolvers.extendtexmfvariable(specification) -- crap, we could better prepend the hash
--- local t = resolvers.expandedpathlist('TEXMF') -- full expansion
- local t = resolvers.splitpath(resolvers.getenv('TEXMF'))
+ local t = resolvers.splitpath(getenv('TEXMF'))
insert(t,1,specification)
local newspec = concat(t,";")
if instance.environment["TEXMF"] then
@@ -10953,10 +11037,6 @@ function resolvers.extendtexmfvariable(specification) -- crap, we could better p
reset_hashes()
end
-function resolvers.generators.tex(specification,tag)
- instance.files[tag or specification] = resolvers.scanfiles(specification)
-end
-
function resolvers.splitexpansions()
local ie = instance.expansions
for k,v in next, ie do
@@ -10986,9 +11066,20 @@ function resolvers.datastate()
return caches.contentstate()
end
+local function resolve(a)
+ return instance.expansions[a] or getenv(a)
+end
+
+local cleaner = P("\\") / "/" + P(";") * S("!{}/\\")^0 * P(";")^1 / ";"
+
+local variable = R("az","AZ","09","__","--")^1 / resolve
+ variable = (P("$")/"") * (variable + (P("{")/"") * variable * (P("}")/""))
+
+ cleaner = Cs((cleaner + P(1))^0)
+ variable = Cs((variable + P(1))^0)
+
function resolvers.expandvariables()
local expansions, environment, variables = allocate(), instance.environment, instance.variables
- local getenv = resolvers.getenv
instance.expansions = expansions
local engine, progname = instance.engine, instance.progname
if type(engine) ~= "string" then instance.engine, engine = "", "" end
@@ -10996,12 +11087,7 @@ function resolvers.expandvariables()
if engine ~= "" then environment['engine'] = engine end
if progname ~= "" then environment['progname'] = progname end
for k,v in next, environment do
- -- local a, b = match(k,"^(%a+)%_(.*)%s*$") -- too many vars have an _ in the name
- -- if a and b then -- so let's forget about it; it was a
- -- expansions[a..'.'..b] = v -- hack anyway for linux and not needed
- -- else -- anymore as we now have directives
- expansions[k] = v
- -- end
+ expansions[k] = v
end
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
@@ -11009,26 +11095,19 @@ function resolvers.expandvariables()
for k,v in next, variables do -- move variables to expansions
if not expansions[k] then expansions[k] = v end
end
- local busy = false
- local function resolve(a)
- busy = true
- return expansions[a] or getenv(a)
- end
- while true do
- busy = false
+ repeat
+ local busy = false
for k,v in next, expansions do
- 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
+ local s = lpegmatch(variable,v)
+ if s ~= v then
+ busy = true
+ expansions[k] = s
end
end
- if not busy then break end
- end
+ until not busy
+
for k,v in next, expansions do
- expansions[k] = gsub(v,"\\", '/')
+ expansions[k] = lpegmatch(cleaner,v)
end
end
@@ -11055,7 +11134,7 @@ function resolvers.unexpandedpathlist(str)
end
function resolvers.unexpandedpath(str)
- return file.joinpath(resolvers.unexpandedpathlist(str))
+ return joinpath(resolvers.unexpandedpathlist(str))
end
local done = { }
@@ -11169,7 +11248,7 @@ function resolvers.cleanpathlist(str)
end
function resolvers.expandpath(str)
- return file.joinpath(resolvers.expandedpathlist(str))
+ return joinpath(resolvers.expandedpathlist(str))
end
function resolvers.expandedpathlist(str)
@@ -11177,7 +11256,7 @@ function resolvers.expandedpathlist(str)
return ep or { } -- ep ?
elseif instance.savelists then
-- engine+progname hash
- str = gsub(str,"%$","")
+ str = lpegmatch(dollarstripper,str)
if not instance.lists[str] then -- cached
local lst = made_list(instance,resolvers.splitpath(resolvers.expansion(str)))
instance.lists[str] = expandedpathfromlist(lst)
@@ -11190,28 +11269,34 @@ function resolvers.expandedpathlist(str)
end
function resolvers.expandedpathlistfromvariable(str) -- brrr
- local tmp = resolvers.variableofformatorsuffix(gsub(str,"%$",""))
- if tmp ~= "" then
- return resolvers.expandedpathlist(tmp)
- else
- return resolvers.expandedpathlist(str)
- end
+ str = lpegmatch(dollarstripper,str)
+ local tmp = resolvers.variableofformatorsuffix(str)
+ return resolvers.expandedpathlist(tmp ~= "" and tmp or str)
end
function resolvers.expandpathfromvariable(str)
- return file.joinpath(resolvers.expandedpathlistfromvariable(str))
+ return joinpath(resolvers.expandedpathlistfromvariable(str))
end
function resolvers.expandbraces(str) -- output variable and brace expansion of STRING
local ori = resolvers.variable(str)
local pth = expandedpathfromlist(resolvers.splitpath(ori))
- return file.joinpath(pth)
+ return joinpath(pth)
end
-resolvers.isreadable = { }
+function resolvers.registerfilehash(name,content,someerror)
+ if content then
+ instance.files[name] = content
+ else
+ instance.files[name] = { }
+ if somerror == true then -- can be unset
+ instance.loaderror = someerror
+ end
+ end
+end
-function resolvers.isreadable.file(name)
- local readable = lfs.isfile(name) -- brrr
+function isreadable(name)
+ local readable = file.is_readable(name)
if trace_detail then
if readable then
report_resolvers("file '%s' is readable",name)
@@ -11222,8 +11307,6 @@ function resolvers.isreadable.file(name)
return readable
end
-resolvers.isreadable.tex = resolvers.isreadable.file
-
-- name
-- name/name
@@ -11244,7 +11327,7 @@ local function collect_files(names)
local hashes = instance.hashes
for h=1,#hashes do
local hash = hashes[h]
- local blobpath = hash.tag
+ local blobpath = hash.name
local files = blobpath and instance.files[blobpath]
if files then
if trace_detail then
@@ -11265,7 +11348,7 @@ local function collect_files(names)
if not dname or find(blobfile,dname) then
local kind = hash.type
local search = filejoin(blobpath,blobfile,bname)
- local result = resolvers.concatinators[hash.type](blobroot,blobfile,bname)
+ local result = methodhandler('concatinators',hash.type,blobroot,blobfile,bname)
if trace_detail then
report_resolvers("match: kind '%s', search '%s', result '%s'",kind,search,result)
end
@@ -11278,7 +11361,7 @@ local function collect_files(names)
if not dname or find(vv,dname) then
local kind = hash.type
local search = filejoin(blobpath,vv,bname)
- local result = resolvers.concatinators[hash.type](blobroot,vv,bname)
+ local result = methodhandler('concatinators',hash.type,blobroot,vv,bname)
if trace_detail then
report_resolvers("match: kind '%s', search '%s', result '%s'",kind,search,result)
end
@@ -11316,6 +11399,8 @@ local function can_be_dir(name) -- can become local
return fakepaths[name] == 1
end
+local preparetreepattern = Cs((P(".")/"%%." + P("-")/"%%-" + P(1))^0 * Cc("$"))
+
local function collect_instance_files(filename,askedformat,allresults) -- todo : plugin (scanners, checkers etc)
local result = { }
local stamp = nil
@@ -11333,7 +11418,7 @@ local function collect_instance_files(filename,askedformat,allresults) -- todo :
end
end
if not dangerous[askedformat] then
- if resolvers.isreadable.file(filename) then
+ if isreadable(filename) then
if trace_detail then
report_resolvers("file '%s' found directly",filename)
end
@@ -11349,7 +11434,7 @@ local function collect_instance_files(filename,askedformat,allresults) -- todo :
end
result = resolvers.findwildcardfiles(filename) -- we can use th elocal
elseif file.is_qualified_path(filename) then
- if resolvers.isreadable.file(filename) then
+ if isreadable(filename) then
if trace_locating then
report_resolvers("qualified name '%s'", filename)
end
@@ -11362,7 +11447,7 @@ local function collect_instance_files(filename,askedformat,allresults) -- todo :
for i=1,#format_suffixes do
local s = format_suffixes[i]
forcedname = filename .. "." .. s
- if resolvers.isreadable.file(forcedname) then
+ if isreadable(forcedname) then
if trace_locating then
report_resolvers("no suffix, forcing format filetype '%s'", s)
end
@@ -11376,7 +11461,7 @@ local function collect_instance_files(filename,askedformat,allresults) -- todo :
-- try to find in tree (no suffix manipulation), here we search for the
-- matching last part of the name
local basename = filebasename(filename)
- local pattern = gsub(filename .. "$","([%.%-])","%%%1")
+ local pattern = lpegmatch(preparetreepattern,filename)
-- messy .. to be sorted out
local savedformat = askedformat
local format = savedformat or ""
@@ -11471,7 +11556,7 @@ local function collect_instance_files(filename,askedformat,allresults) -- todo :
end
for k=1,#wantedfiles do
local fname = wantedfiles[k]
- if fname and resolvers.isreadable.file(fname) then
+ if fname and isreadable(fname) then
filename, done = fname, true
result[#result+1] = filejoin('.',fname)
break
@@ -11497,26 +11582,15 @@ local function collect_instance_files(filename,askedformat,allresults) -- todo :
if trace_detail then
report_resolvers("checking filename '%s'",filename)
end
- -- a bit messy ... esp the doscan setting here
- local doscan
for k=1,#pathlist do
local path = pathlist[k]
- if find(path,"^!!") then doscan = false else doscan = true end
- local pathname = gsub(path,"^!+", '')
+ local pathname = lpegmatch(inhibitstripper,path)
+ local doscan = path == pathname -- no ^!!
done = false
-- using file list
if filelist then
- local expression
-- compare list entries with permitted pattern -- /xx /xx//
- if not find(pathname,"/$") then
- expression = pathname .. "/"
- else
- expression = pathname
- end
- expression = gsub(expression,"([%-%.])","%%%1") -- this also influences
- expression = gsub(expression,"//+$", '/.*') -- later usage of pathname
- expression = gsub(expression,"//", '/.-/') -- not ok for /// but harmless
- expression = "^" .. expression .. "$"
+ local expression = makepathexpression(pathname)
if trace_detail then
report_resolvers("using pattern '%s' for path '%s'",expression,pathname)
end
@@ -11545,7 +11619,8 @@ local function collect_instance_files(filename,askedformat,allresults) -- todo :
end
if not done and doscan then
-- check if on disk / unchecked / does not work at all / also zips
- if resolvers.splitmethod(pathname).scheme == 'file' then -- ?
+ local scheme = url.hasscheme(pathname)
+ if not scheme or scheme == "file" then
local pname = gsub(pathname,"%.%*$",'')
if not find(pname,"%*") then
local ppname = gsub(pname,"/+$","")
@@ -11553,7 +11628,7 @@ local function collect_instance_files(filename,askedformat,allresults) -- todo :
for k=1,#wantedfiles do
local w = wantedfiles[k]
local fname = filejoin(ppname,w)
- if resolvers.isreadable.file(fname) then
+ if isreadable(fname) then
if trace_detail then
report_resolvers("found '%s' by scanning",fname)
end
@@ -11586,9 +11661,6 @@ local function collect_instance_files(filename,askedformat,allresults) -- todo :
return result
end
-resolvers.concatinators.tex = filejoin
-resolvers.concatinators.file = resolvers.concatinators.tex
-
local function findfiles(filename,filetype,allresults)
local result = collect_instance_files(filename,filetype or "",allresults)
if #result == 0 then
@@ -11609,7 +11681,7 @@ function resolvers.findfile(filename,filetype)
end
function resolvers.findpath(filename,filetype)
- return file.dirname(findfiles(filename,filetype,false)[1] or "")
+ return filedirname(findfiles(filename,filetype,false)[1] or "")
end
local function findgivenfiles(filename,allresults)
@@ -11617,7 +11689,7 @@ local function findgivenfiles(filename,allresults)
local hashes = instance.hashes
for k=1,#hashes do
local hash = hashes[k]
- local files = instance.files[hash.tag] or { }
+ local files = instance.files[hash.name] or { }
local blist = files[bname]
if not blist then
local rname = "remap:"..bname
@@ -11629,12 +11701,12 @@ local function findgivenfiles(filename,allresults)
end
if blist then
if type(blist) == 'string' then
- result[#result+1] = resolvers.concatinators[hash.type](hash.tag,blist,bname) or ""
+ result[#result+1] = methodhandler('concatinators',hash.type,hash.name,blist,bname) or ""
if not allresults then break end
else
for kk=1,#blist do
local vv = blist[kk]
- result[#result+1] = resolvers.concatinators[hash.type](hash.tag,vv,bname) or ""
+ result[#result+1] = methodhandler('concatinators',hash.type,hash.name,vv,bname) or ""
if not allresults then break end
end
end
@@ -11657,14 +11729,14 @@ local function doit(path,blist,bname,tag,kind,result,allresults)
if type(blist) == 'string' then
-- make function and share code
if find(lower(blist),path) then
- result[#result+1] = resolvers.concatinators[kind](tag,blist,bname) or ""
+ result[#result+1] = methodhandler('concatinators',kind,tag,blist,bname) or ""
done = true
end
else
for kk=1,#blist do
local vv = blist[kk]
if find(lower(vv),path) then
- result[#result+1] = resolvers.concatinators[kind](tag,vv,bname) or ""
+ result[#result+1] = methodhandler('concatinators',kind,tag,vv,bname) or ""
done = true
if not allresults then break end
end
@@ -11674,30 +11746,25 @@ local function doit(path,blist,bname,tag,kind,result,allresults)
return done
end
+local makewildcard = Cs(
+ (P("^")^0 * P("/") * P(-1) + P(-1)) /".*"
+ + (P("^")^0 * P("/") / "") * (P("*")/".*" + P("-")/"%%-" + P("?")/"."+ P("\\")/"/" + P(1))^0
+)
+
local function findwildcardfiles(filename,allresults) -- todo: remap: and lpeg
local result = { }
- local bname, dname = filebasename(filename), filedirname(filename)
- local path = gsub(dname,"^*/","")
- path = gsub(path,"*",".*")
- path = gsub(path,"-","%%-")
- if dname == "" then
- path = ".*"
- end
- local name = bname
- name = gsub(name,"*",".*")
- name = gsub(name,"-","%%-")
- path = lower(path)
- name = lower(name)
+ local path = lower(lpegmatch(makewildcard,filedirname (filename)))
+ local name = lower(lpegmatch(makewildcard,filebasename(filename)))
local files, done = instance.files, false
if find(name,"%*") then
local hashes = instance.hashes
for k=1,#hashes do
local hash = hashes[k]
- local tag, kind = hash.tag, hash.type
- for kk, hh in next, files[hash.tag] do
+ local hashname, hashtype = hash.name, hash.type
+ for kk, hh in next, files[hashname] do
if not find(kk,"^remap:") then
if find(lower(kk),name) then
- if doit(path,hh,kk,tag,kind,result,allresults) then done = true end
+ if doit(path,hh,kk,hashname,hashtype,result,allresults) then done = true end
if done and not allresults then break end
end
end
@@ -11707,8 +11774,8 @@ local function findwildcardfiles(filename,allresults) -- todo: remap: and lpeg
local hashes = instance.hashes
for k=1,#hashes do
local hash = hashes[k]
- local tag, kind = hash.tag, hash.type
- if doit(path,files[tag][bname],bname,tag,kind,result,allresults) then done = true end
+ local hashname, hashtype = hash.name, hash.type
+ if doit(path,files[hashname][bname],bname,hashname,hashtype,result,allresults) then done = true end
if done and not allresults then break end
end
end
@@ -11779,12 +11846,9 @@ end
-- resolvers.expandvar = resolvers.expansion -- output variable expansion of STRING.
function resolvers.showpath(str) -- output search path for file type NAME
- return file.joinpath(resolvers.expandedpathlist(resolvers.formatofvariable(str)))
+ return joinpath(resolvers.expandedpathlist(resolvers.formatofvariable(str)))
end
--- resolvers.findfile(filename)
--- resolvers.findfile(filename, f.iletype)
-
function resolvers.registerfile(files, name, path)
if files[name] then
if type(files[name]) == 'string' then
@@ -11809,7 +11873,7 @@ function resolvers.dowithvariable(name,func)
end
function resolvers.locateformat(name)
- local barename = gsub(name,"%.%a+$","")
+ local barename = file.removesuffix(name) -- gsub(name,"%.%a+$","")
local fmtname = caches.getfirstreadablefile(barename..".fmt","formats") or ""
if fmtname == "" then
fmtname = resolvers.findfile(barename..".fmt")
@@ -11845,7 +11909,7 @@ function resolvers.dowithfilesintree(pattern,handle,before,after) -- can be a ni
for i=1,#hashes do
local hash = hashes[i]
local blobtype = hash.type
- local blobpath = hash.tag
+ local blobpath = hash.name
if blobpath then
if before then
before(blobtype,blobpath,pattern)
@@ -12020,13 +12084,23 @@ if not modules then modules = { } end modules ['data-inp'] = {
license = "see context related readme files"
}
-local allocate = utilities.storage.allocate
-
+local allocate = utilities.storage.allocate
local resolvers = resolvers
-resolvers.finders = allocate { notfound = { nil } }
-resolvers.openers = allocate { notfound = { nil } }
-resolvers.loaders = allocate { notfound = { false, nil, 0 } }
+local methodhandler = resolvers.methodhandler
+local registermethod = resolvers.registermethod
+
+local finders = allocate { helpers = { }, notfound = function() end }
+local openers = allocate { helpers = { }, notfound = function() end }
+local loaders = allocate { helpers = { }, notfound = function() return false, nil, 0 end }
+
+registermethod("finders", finders, "uri")
+registermethod("openers", openers, "uri")
+registermethod("loaders", loaders, "uri")
+
+resolvers.finders = finders
+resolvers.openers = openers
+resolvers.loaders = loaders
end -- of closure
@@ -12041,8 +12115,134 @@ if not modules then modules = { } end modules ['data-out'] = {
license = "see context related readme files"
}
-resolvers.savers = utilities.storage.allocate { }
+local allocate = utilities.storage.allocate
+local resolvers = resolvers
+
+local registermethod = resolvers.registermethod
+
+local savers = allocate { helpers = { } }
+resolvers.savers = savers
+
+registermethod("savers", savers, "uri")
+
+
+end -- of closure
+
+do -- create closure to overcome 200 locals limit
+
+if not modules then modules = { } end modules ['data-fil'] = {
+ version = 1.001,
+ comment = "companion to luat-lib.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
+
+local report_resolvers = logs.new("resolvers")
+
+local resolvers = resolvers
+
+local finders, openers, loaders, savers = resolvers.finders, resolvers.openers, resolvers.loaders, resolvers.savers
+local locators, hashers, generators, concatinators = resolvers.locators, resolvers.hashers, resolvers.generators, resolvers.concatinators
+
+local checkgarbage = utilities.garbagecollector and utilities.garbagecollector.check
+
+function locators.file(specification)
+ local name = specification.filename
+ if name and name ~= '' and lfs.isdir(name) then
+ if trace_locating then
+ report_resolvers("file locator '%s' found",name)
+ end
+ resolvers.appendhash('file',name,true) -- cache
+ elseif trace_locating then
+ report_resolvers("file locator '%s' not found",name)
+ end
+end
+
+function hashers.file(specification)
+ local name = specification.filename
+ local content = caches.loadcontent(name,'files')
+ resolvers.registerfilehash(name,content,content==nil)
+end
+
+function generators.file(specification)
+ local name = specification.filename
+ local content = resolvers.scanfiles(name)
+ resolvers.registerfilehash(name,content,true)
+end
+
+concatinators.file = file.join
+
+function finders.file(specification,filetype)
+ local filename = specification.filename
+ local foundname = resolvers.findfile(filename,filetype)
+ if foundname and foundname ~= "" then
+ if trace_locating then
+ report_resolvers("file finder: '%s' found",filename)
+ end
+ return foundname
+ else
+ if trace_locating then
+ report_resolvers("file finder: %s' not found",filename)
+ end
+ return finders.notfound()
+ end
+end
+
+-- The default textopener will be overloaded later on.
+
+function openers.helpers.textopener(tag,filename,f)
+ return {
+ reader = function() return f:read () end,
+ close = function() return f:close() end,
+ }
+end
+
+function openers.file(specification,filetype)
+ local filename = specification.filename
+ if filename and filename ~= "" then
+ local f = io.open(filename,"r")
+ if f then
+ logs.show_open(filename) -- todo
+ if trace_locating then
+ report_resolvers("file opener, '%s' opened",filename)
+ end
+ return openers.helpers.textopener("file",filename,f)
+ end
+ end
+ if trace_locating then
+ report_resolvers("file opener, '%s' not found",filename)
+ end
+ return openers.notfound()
+end
+
+function loaders.file(specification,filetype)
+ local filename = specification.filename
+ if filename and filename ~= "" then
+ local f = io.open(filename,"rb")
+ if f then
+ logs.show_load(filename)
+ if trace_locating then
+ report_resolvers("file loader, '%s' loaded",filename)
+ end
+ local s = f:read("*a")
+ if checkgarbage then
+ checkgarbage(#s)
+ end
+ f:close()
+ if s then
+ return true, s, #s
+ end
+ end
+ end
+ if trace_locating then
+ report_resolvers("file loader, '%s' not found",filename)
+ end
+ return loaders.notfound()
+end
end -- of closure
@@ -12301,10 +12501,9 @@ if not modules then modules = { } end modules ['data-zip'] = {
license = "see context related readme files"
}
--- to be redone using the more recent schemes mechanism
+-- partly redone .. needs testing
local format, find, match = string.format, string.find, string.match
-local unpack = unpack or table.unpack
local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
@@ -12327,9 +12526,6 @@ local archives = zip.archives
zip.registeredfiles = zip.registeredfiles or { }
local registeredfiles = zip.registeredfiles
-local finders, openers, loaders = resolvers.finders, resolvers.openers, resolvers.loaders
-local locators, hashers, concatinators = resolvers.locators, resolvers.hashers, resolvers.concatinators
-
local function validzip(str) -- todo: use url splitter
if not find(str,"^zip://") then
return "zip:///" .. str
@@ -12359,159 +12555,159 @@ function zip.closearchive(name)
end
end
-function locators.zip(specification) -- where is this used? startup zips (untested)
- specification = resolvers.splitmethod(specification)
- local zipfile = specification.path
- local zfile = zip.openarchive(name) -- tricky, could be in to be initialized tree
+function resolvers.locators.zip(specification)
+ local archive = specification.filename
+ local zipfile = archive and archive ~= "" and zip.openarchive(archive) -- tricky, could be in to be initialized tree
if trace_locating then
- if zfile then
- report_resolvers("zip locator, archive '%s' found",specification.original)
+ if zipfile then
+ report_resolvers("zip locator, archive '%s' found",archive)
else
- report_resolvers("zip locator, archive '%s' not found",specification.original)
+ report_resolvers("zip locator, archive '%s' not found",archive)
end
end
end
-function hashers.zip(tag,name)
+function resolvers.hashers.zip(specification)
+ local archive = specification.filename
if trace_locating then
- report_resolvers("loading zip file '%s' as '%s'",name,tag)
+ report_resolvers("loading zip file '%s'",archive)
end
- resolvers.usezipfile(format("%s?tree=%s",tag,name))
+ resolvers.usezipfile(specification.original)
end
-function concatinators.zip(tag,path,name)
+function resolvers.concatinators.zip(zipfile,path,name) -- ok ?
if not path or path == "" then
- return format('%s?name=%s',tag,name)
+ return format('%s?name=%s',zipfile,name)
else
- return format('%s?name=%s/%s',tag,path,name)
+ return format('%s?name=%s/%s',zipfile,path,name)
end
end
-function resolvers.isreadable.zip(name)
- return true
-end
-
-function finders.zip(specification,filetype)
- specification = resolvers.splitmethod(specification)
- if specification.path then
- local q = url.query(specification.query)
- if q.name then
- local zfile = zip.openarchive(specification.path)
+function resolvers.finders.zip(specification)
+ local original = specification.original
+ local archive = specification.filename
+ if archive then
+ local query = url.query(specification.query)
+ local queryname = query.name
+ if queryname then
+ local zfile = zip.openarchive(archive)
if zfile then
if trace_locating then
- report_resolvers("zip finder, archive '%s' found",specification.path)
+ report_resolvers("zip finder, archive '%s' found",archive)
end
- local dfile = zfile:open(q.name)
+ local dfile = zfile:open(queryname)
if dfile then
dfile = zfile:close()
if trace_locating then
- report_resolvers("zip finder, file '%s' found",q.name)
+ report_resolvers("zip finder, file '%s' found",queryname)
end
return specification.original
elseif trace_locating then
- report_resolvers("zip finder, file '%s' not found",q.name)
+ report_resolvers("zip finder, file '%s' not found",queryname)
end
elseif trace_locating then
- report_resolvers("zip finder, unknown archive '%s'",specification.path)
+ report_resolvers("zip finder, unknown archive '%s'",archive)
end
end
end
if trace_locating then
- report_resolvers("zip finder, '%s' not found",filename)
+ report_resolvers("zip finder, '%s' not found",original)
end
- return unpack(finders.notfound)
+ return resolvers.finders.notfound()
end
-function openers.zip(specification)
- local zipspecification = resolvers.splitmethod(specification)
- if zipspecification.path then
- local q = url.query(zipspecification.query)
- if q.name then
- local zfile = zip.openarchive(zipspecification.path)
+function resolvers.openers.zip(specification)
+ local original = specification.original
+ local archive = specification.filename
+ if archive then
+ local query = url.query(specification.query)
+ local queryname = query.name
+ if queryname then
+ local zfile = zip.openarchive(archive)
if zfile then
if trace_locating then
- report_resolvers("zip opener, archive '%s' opened",zipspecification.path)
+ report_resolvers("zip opener, archive '%s' opened",archive)
end
- local dfile = zfile:open(q.name)
+ local dfile = zfile:open(queryname)
if dfile then
- logs.show_open(specification)
+ logs.show_open(original)
if trace_locating then
- report_resolvers("zip opener, file '%s' found",q.name)
+ report_resolvers("zip opener, file '%s' found",queryname)
end
- return openers.textopener('zip',specification,dfile)
+ return resolvers.openers.helpers.textopener('zip',original,dfile)
elseif trace_locating then
- report_resolvers("zip opener, file '%s' not found",q.name)
+ report_resolvers("zip opener, file '%s' not found",queryname)
end
elseif trace_locating then
- report_resolvers("zip opener, unknown archive '%s'",zipspecification.path)
+ report_resolvers("zip opener, unknown archive '%s'",archive)
end
end
end
if trace_locating then
- report_resolvers("zip opener, '%s' not found",filename)
+ report_resolvers("zip opener, '%s' not found",original)
end
- return unpack(openers.notfound)
+ return resolvers.openers.notfound()
end
-function loaders.zip(specification)
- specification = resolvers.splitmethod(specification)
- if specification.path then
- local q = url.query(specification.query)
- if q.name then
- local zfile = zip.openarchive(specification.path)
+function resolvers.loaders.zip(specification)
+ local original = specification.original
+ local archive = specification.filename
+ if archive then
+ local query = url.query(specification.query)
+ local queryname = query.name
+ if queryname then
+ local zfile = zip.openarchive(archive)
if zfile then
if trace_locating then
- report_resolvers("zip loader, archive '%s' opened",specification.path)
+ report_resolvers("zip loader, archive '%s' opened",archive)
end
- local dfile = zfile:open(q.name)
+ local dfile = zfile:open(queryname)
if dfile then
- logs.show_load(filename)
+ logs.show_load(original)
if trace_locating then
- report_resolvers("zip loader, file '%s' loaded",filename)
+ report_resolvers("zip loader, file '%s' loaded",original)
end
local s = dfile:read("*all")
dfile:close()
return true, s, #s
elseif trace_locating then
- report_resolvers("zip loader, file '%s' not found",q.name)
+ report_resolvers("zip loader, file '%s' not found",queryname)
end
elseif trace_locating then
- report_resolvers("zip loader, unknown archive '%s'",specification.path)
+ report_resolvers("zip loader, unknown archive '%s'",archive)
end
end
end
if trace_locating then
- report_resolvers("zip loader, '%s' not found",filename)
+ report_resolvers("zip loader, '%s' not found",original)
end
- return unpack(openers.notfound)
+ return resolvers.openers.notfound()
end
-- zip:///somefile.zip
-- zip:///somefile.zip?tree=texmf-local -> mount
-function resolvers.usezipfile(zipname)
- zipname = validzip(zipname)
- local specification = resolvers.splitmethod(zipname)
- local zipfile = specification.path
- if zipfile and not registeredfiles[zipname] then
- local tree = url.query(specification.query).tree or ""
- local z = zip.openarchive(zipfile)
+function resolvers.usezipfile(archive)
+ local specification = resolvers.splitmethod(archive) -- to be sure
+ local archive = specification.filename
+ if archive and not registeredfiles[archive] then
+ local z = zip.openarchive(archive)
if z then
- local instance = resolvers.instance
+ local tree = url.query(specification.query).tree or ""
if trace_locating then
- report_resolvers("zip registering, registering archive '%s'",zipname)
- end
- statistics.starttiming(instance)
- resolvers.prependhash('zip',zipname,zipfile)
- resolvers.extendtexmfvariable(zipname) -- resets hashes too
- registeredfiles[zipname] = z
- instance.files[zipname] = resolvers.registerzipfile(z,tree or "")
- statistics.stoptiming(instance)
+ report_resolvers("zip registering, registering archive '%s'",archive)
+ end
+ statistics.starttiming(resolvers.instance)
+ resolvers.prependhash('zip',archive)
+ resolvers.extendtexmfvariable(archive) -- resets hashes too
+ registeredfiles[archive] = z
+ instance.files[archive] = resolvers.registerzipfile(z,tree)
+ statistics.stoptiming(resolvers.instance)
elseif trace_locating then
- report_resolvers("zip registering, unknown archive '%s'",zipname)
+ report_resolvers("zip registering, unknown archive '%s'",archive)
end
elseif trace_locating then
- report_resolvers("zip registering, '%s' not found",zipname)
+ report_resolvers("zip registering, '%s' not found",archive)
end
end
@@ -12560,7 +12756,8 @@ if not modules then modules = { } end modules ['data-tre'] = {
-- \input tree://oeps1/**/oeps.tex
local find, gsub, format = string.find, string.gsub, string.format
-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")
@@ -12568,10 +12765,10 @@ local resolvers = 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 ""
+function resolvers.finders.tree(specification)
+ local spec = specification.filename
+ local fnd = found[spec]
+ if fnd == nil then
if spec ~= "" then
local path, name = file.dirname(spec), file.basename(spec)
if path == "" then path = "." end
@@ -12585,53 +12782,41 @@ function resolvers.finders.tree(specification,filetype)
for k=1,#hash do
local v = hash[k]
if find(v,pattern) then
- found[specification] = v
+ found[spec] = v
return v
end
end
end
- fnd = unpack(notfound) -- unpack ? why not just notfound[1]
- found[specification] = fnd
+ fnd = notfound() -- false
+ found[spec] = 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
+ local name = specification.filename
+ if name ~= '' and lfs.isdir(name) then
if trace_locating then
- report_resolvers("tree locator '%s' found (%s)",path,specification)
+ report_resolvers("tree locator '%s' found",name)
end
- resolvers.appendhash('tree',specification,path,false) -- don't cache
+ resolvers.appendhash('tree',name,false) -- don't cache
elseif trace_locating then
- report_resolvers("tree locator '%s' not found",path)
+ report_resolvers("tree locator '%s' not found",name)
end
end
-function resolvers.hashers.tree(tag,name)
+function resolvers.hashers.tree(specification)
+ local name = specification.filename
if trace_locating then
- report_resolvers("analysing tree '%s' as '%s'",name,tag)
+ report_resolvers("analysing tree '%s'",name)
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
+ resolvers.methodhandler("hashers",name)
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
+resolvers.concatinators.tree = resolvers.concatinators.file
+resolvers.generators.tree = resolvers.generators.file
+resolvers.openers.tree = resolvers.openers.file
+resolvers.loaders.tree = resolvers.loaders.file
end -- of closure
@@ -12654,53 +12839,51 @@ local resolvers = resolvers
local finders, openers, loaders = resolvers.finders, resolvers.openers, resolvers.loaders
-curl = curl or { }
-local curl = curl
+resolvers.curl = resolvers.curl or { }
+local curl = resolvers.curl
local cached = { }
-function curl.fetch(protocol, name) -- todo: use socket library
- local cleanname = gsub(name,"[^%a%d%.]+","-")
+local function runcurl(specification)
+ local original = specification.original
+ -- local scheme = specification.scheme
+ local cleanname = gsub(original,"[^%a%d%.]+","-")
local cachename = caches.setfirstwritablefile(cleanname,"curl")
- if not cached[name] then
+ if not cached[original] then
if not io.exists(cachename) then
- cached[name] = cachename
- local command = "curl --silent --create-dirs --output " .. cachename .. " " .. name -- no protocol .. "://"
+ cached[original] = cachename
+ local command = "curl --silent --create-dirs --output " .. cachename .. " " .. original
os.spawn(command)
end
if io.exists(cachename) then
- cached[name] = cachename
+ cached[original] = cachename
else
- cached[name] = ""
+ cached[original] = ""
end
end
- return cached[name]
+ return cached[original]
end
-function finders.curl(protocol,filename)
- local foundname = curl.fetch(protocol, filename)
- return finders.generic(protocol,foundname,filetype)
-end
+-- old code: we could be cleaner using specification (see schemes)
-function openers.curl(protocol,filename)
- return openers.generic(protocol,filename)
+local function finder(specification,filetype)
+ return resolvers.methodhandler("finders",runcurl(specification),filetype)
end
-function loaders.curl(protocol,filename)
- return loaders.generic(protocol,filename)
-end
-
--- todo: metamethod
+local opener = openers.file
+local loader = loaders.file
-function curl.install(protocol)
- finders[protocol] = function (filename,filetype) return finders.curl(protocol,filename) end
- openers[protocol] = function (filename) return openers.curl(protocol,filename) end
- loaders[protocol] = function (filename) return loaders.curl(protocol,filename) end
+local function install(scheme)
+ finders[scheme] = finder
+ openers[scheme] = opener
+ loaders[scheme] = loader
end
-curl.install('http')
-curl.install('https')
-curl.install('ftp')
+resolvers.curl.install = install
+
+install('http')
+install('https')
+install('ftp')
end -- of closure
@@ -12777,7 +12960,7 @@ local function loaded(libpaths,name,simple)
if trace_locating then -- more detail
report_resolvers("! checking for '%s' on 'package.path': '%s' => '%s'",simple,libpath,resolved)
end
- if resolvers.isreadable.file(resolved) then
+ if file.is_readable(resolved) then
if trace_locating then
report_resolvers("! lib '%s' located via 'package.path': '%s'",name,resolved)
end
@@ -12786,7 +12969,6 @@ local function loaded(libpaths,name,simple)
end
end
-
package.loaders[2] = function(name) -- was [#package.loaders+1]
if trace_locating then -- mode detail
report_resolvers("! locating '%s'",name)
@@ -12824,7 +13006,7 @@ package.loaders[2] = function(name) -- was [#package.loaders+1]
if trace_locating then -- mode detail
report_resolvers("! checking for '%s' using 'clibformat path': '%s'",libname,path)
end
- if resolvers.isreadable.file(resolved) then
+ if file.is_readable(resolved) then
if trace_locating then
report_resolvers("! lib '%s' located via 'clibformat': '%s'",libname,resolved)
end
@@ -12838,7 +13020,7 @@ package.loaders[2] = function(name) -- was [#package.loaders+1]
if trace_locating then -- more detail
report_resolvers("! checking for '%s' on 'package.cpath': '%s'",simple,libpath)
end
- if resolvers.isreadable.file(resolved) then
+ if file.is_readable(resolved) then
if trace_locating then
report_resolvers("! lib '%s' located via 'package.cpath': '%s'",name,resolved)
end
@@ -13375,6 +13557,7 @@ own.libs = { -- order can be made better
'data-pre.lua',
'data-inp.lua',
'data-out.lua',
+ 'data-fil.lua',
'data-con.lua',
'data-use.lua',
-- 'data-tex.lua',
diff --git a/scripts/context/stubs/mswin/mtxrun.lua b/scripts/context/stubs/mswin/mtxrun.lua
index 3be305bed..6b74022ae 100644
--- a/scripts/context/stubs/mswin/mtxrun.lua
+++ b/scripts/context/stubs/mswin/mtxrun.lua
@@ -119,7 +119,7 @@ local patterns_escapes = {
["."] = "%.",
["+"] = "%+", ["-"] = "%-", ["*"] = "%*",
["["] = "%[", ["]"] = "%]",
- ["("] = "%)", [")"] = "%)",
+ ["("] = "%(", [")"] = "%)",
-- ["{"] = "%{", ["}"] = "%}"
-- ["^"] = "%^", ["$"] = "%$",
}
@@ -185,6 +185,7 @@ local patterns = lpeg.patterns
local P, R, S, V, match = lpeg.P, lpeg.R, lpeg.S, lpeg.V, lpeg.match
local Ct, C, Cs, Cc, Cf, Cg = lpeg.Ct, lpeg.C, lpeg.Cs, lpeg.Cc, lpeg.Cf, lpeg.Cg
+local lpegtype = lpeg.type
local utfcharacters = string.utfcharacters
local utfgmatch = unicode and unicode.utf8.gmatch
@@ -201,7 +202,6 @@ patterns.alwaysmatched = alwaysmatched
local digit, sign = R('09'), S('+-')
local cr, lf, crlf = P("\r"), P("\n"), P("\r\n")
local newline = crlf + cr + lf
-local utf8next = R("\128\191")
local escaped = P("\\") * anything
local squote = P("'")
local dquote = P('"')
@@ -222,6 +222,8 @@ local utftype = utfbom_32_be / "utf-32-be" + utfbom_32_le / "utf-32-le
+ utfbom_16_be / "utf-16-be" + utfbom_16_le / "utf-16-le"
+ utfbom_8 / "utf-8" + alwaysmatched / "unknown"
+local utf8next = R("\128\191")
+
patterns.utf8one = R("\000\127")
patterns.utf8two = R("\194\223") * utf8next
patterns.utf8three = R("\224\239") * utf8next * utf8next
@@ -432,19 +434,25 @@ end
-- Just for fun I looked at the used bytecode and
-- p = (p and p + pp) or pp gets one more (testset).
-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]
- if p then
- p = p + pp
- else
- p = pp
+function lpeg.replacer(one,two)
+ if type(one) == "table" then
+ local no = #one
+ if no > 0 then
+ local p
+ for i=1,no do
+ local o = one[i]
+ local pp = P(o[1]) / o[2]
+ if p then
+ p = p + pp
+ else
+ p = pp
+ end
end
+ return Cs((p + 1)^0)
end
- return Cs((p + 1)^0)
+ else
+ two = two or ""
+ return Cs((P(one)/two + 1)^0)
end
end
@@ -646,6 +654,10 @@ function lpeg.oneof(list,...) -- lpeg.oneof("elseif","else","if","then")
return p
end
+function lpeg.is_lpeg(p)
+ return p and lpegtype(p) == "pattern"
+end
+
end -- of closure
@@ -2558,6 +2570,9 @@ local separator = P("://")
local qualified = P(".")^0 * P("/") + letter*P(":") + letter^1*separator + letter^1 * P("/")
local rootbased = P("/") + letter*P(":")
+lpeg.patterns.qualified = qualified
+lpeg.patterns.rootbased = rootbased
+
-- ./name ../name /name c: :// name/name
function file.is_qualified_path(filename)
@@ -2678,72 +2693,95 @@ if not modules then modules = { } end modules ['l-url'] = {
license = "see context related readme files"
}
-local char, gmatch, gsub, format, byte = string.char, string.gmatch, string.gsub, string.format, string.byte
+local char, gmatch, gsub, format, byte, find = string.char, string.gmatch, string.gsub, string.format, string.byte, string.find
local concat = table.concat
local tonumber, type = tonumber, type
-local lpegmatch, lpegP, lpegC, lpegR, lpegS, lpegCs, lpegCc = lpeg.match, lpeg.P, lpeg.C, lpeg.R, lpeg.S, lpeg.Cs, lpeg.Cc
+local P, C, R, S, Cs, Cc, Ct = lpeg.P, lpeg.C, lpeg.R, lpeg.S, lpeg.Cs, lpeg.Cc, lpeg.Ct
+local lpegmatch, lpegpatterns, replacer = lpeg.match, lpeg.patterns, lpeg.replacer
--- from the spec (on the web):
+-- from wikipedia:
--
--- foo://example.com:8042/over/there?name=ferret#nose
--- \_/ \______________/\_________/ \_________/ \__/
--- | | | | |
--- scheme authority path query fragment
--- | _____________________|__
--- / \ / \
--- urn:example:animal:ferret:nose
+-- foo://username:password@example.com:8042/over/there/index.dtb?type=animal;name=narwhal#nose
+-- \_/ \_______________/ \_________/ \__/ \___/ \_/ \______________________/ \__/
+-- | | | | | | | |
+-- | userinfo hostname port | | query fragment
+-- | \________________________________/\_____________|____|/
+-- scheme | | | |
+-- | authority path | |
+-- | | |
+-- | path interpretable as filename
+-- | ___________|____________ |
+-- / \ / \ |
+-- urn:example:animal:ferret:nose interpretable as extension
url = url or { }
local url = url
-local function tochar(s)
- return char(tonumber(s,16))
-end
+local tochar = function(s) return char(tonumber(s,16)) end
-local colon, qmark, hash, slash, percent, endofstring = lpegP(":"), lpegP("?"), lpegP("#"), lpegP("/"), lpegP("%"), lpegP(-1)
+local colon = P(":")
+local qmark = P("?")
+local hash = P("#")
+local slash = P("/")
+local percent = P("%")
+local endofstring = P(-1)
-local hexdigit = lpegR("09","AF","af")
-local plus = lpegP("+")
-local nothing = lpegCc("")
-local escaped = (plus / " ") + (percent * lpegC(hexdigit * hexdigit) / tochar)
+local hexdigit = R("09","AF","af")
+local plus = P("+")
+local nothing = Cc("")
+local escaped = (plus / " ") + (percent * C(hexdigit * hexdigit) / tochar)
-- we assume schemes with more than 1 character (in order to avoid problems with windows disks)
-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)
+local scheme = Cs((escaped+(1-colon-slash-qmark-hash))^2) * colon + nothing
+local authority = slash * slash * Cs((escaped+(1- slash-qmark-hash))^0) + nothing
+local path = slash * Cs((escaped+(1- qmark-hash))^0) + nothing
+local query = qmark * Cs((escaped+(1- hash))^0) + nothing
+local fragment = hash * Cs((escaped+(1- endofstring))^0) + nothing
-lpeg.patterns.urlsplitter = parser
+local parser = Ct(scheme * authority * path * query * fragment)
-local escapes = { }
+lpegpatterns.urlsplitter = parser
-for i=0,255 do
- escapes[i] = format("%%%02X",i)
-end
+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)
+local escaper = Cs((R("09","AZ","az") + S("-./_") + P(1) / escapes)^0)
-lpeg.patterns.urlescaper = escaper
+lpegpatterns.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
-function url.split(str)
+local function split(str)
return (type(str) == "string" and lpegmatch(parser,str)) or str
end
+local function hasscheme(str)
+ local scheme = lpegmatch(scheme,str) -- at least one character
+ return scheme and scheme ~= ""
+end
+
-- todo: cache them
-function url.hashed(str) -- not yet ok (/test?test)
- local s = url.split(str)
+local rootletter = R("az","AZ")
+ + S("_-+")
+local separator = P("://")
+local qualified = P(".")^0 * P("/")
+ + rootletter * P(":")
+ + rootletter^1 * separator
+ + rootletter^1 * P("/")
+local rootbased = P("/")
+ + rootletter * P(":")
+
+local barswapper = replacer("|",":")
+local backslashswapper = replacer("\\","/")
+
+local function hashed(str) -- not yet ok (/test?test)
+ local s = split(str)
local somescheme = s[1] ~= ""
local somequery = s[4] ~= ""
if not somescheme and not somequery then
- return {
+ s = {
scheme = "file",
authority = "",
path = str,
@@ -2751,52 +2789,73 @@ function url.hashed(str) -- not yet ok (/test?test)
fragment = "",
original = str,
noscheme = true,
+ filename = str,
}
- else
- return {
+ else -- not always a filename but handy anyway
+ local authority, path, filename = s[2], s[3]
+ if authority == "" then
+ filename = path
+ else
+ filename = authority .. "/" .. path
+ end
+ s = {
scheme = s[1],
- authority = s[2],
- path = s[3],
+ authority = authority,
+ path = path,
query = s[4],
fragment = s[5],
original = str,
noscheme = false,
+ filename = filename,
}
end
+ return s
end
+-- Here we assume:
+--
+-- files: /// = relative
+-- files: //// = absolute (!)
+
-function url.hasscheme(str)
- return url.split(str)[1] ~= ""
-end
-function url.addscheme(str,scheme)
- return (url.hasscheme(str) and str) or ((scheme or "file:///") .. str)
+url.split = split
+url.hasscheme = hasscheme
+url.hashed = hashed
+
+function url.addscheme(str,scheme) -- no authority
+ if hasscheme(str) then
+ return str
+ elseif not scheme then
+ return "file:///" .. str
+ else
+ return scheme .. ":///" .. str
+ end
end
function url.construct(hash) -- dodo: we need to escape !
- local fullurl = { }
+ local fullurl, f = { }, 0
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 .. "://"
+ f = f + 1 ; fullurl[f] = scheme .. "://"
end
if authority and authority ~= "" then
- fullurl[#fullurl+1] = authority
+ f = f + 1 ; fullurl[f] = authority
end
if path and path ~= "" then
- fullurl[#fullurl+1] = "/" .. path
+ f = f + 1 ; fullurl[f] = "/" .. path
end
if query and query ~= "" then
- fullurl[#fullurl+1] = "?".. query
+ f = f + 1 ; fullurl[f] = "?".. query
end
if fragment and fragment ~= "" then
- fullurl[#fullurl+1] = "#".. fragment
+ f = f + 1 ; fullurl[f] = "#".. fragment
end
return lpegmatch(escaper,concat(fullurl))
end
function url.filename(filename)
- local t = url.hashed(filename)
+ local t = hashed(filename)
return (t.scheme == "file" and (gsub(t.path,"^/([a-zA-Z])([:|])/)","%1:"))) or filename
end
@@ -2820,6 +2879,7 @@ end
+
end -- of closure
do -- create closure to overcome 200 locals limit
@@ -2861,25 +2921,22 @@ end
-- optimizing for no find (*) does not save time
+
local function globpattern(path,patt,recurse,action)
- local ok, scanner
if path == "/" then
- ok, scanner = xpcall(function() return walkdir(path..".") end, function() end) -- kepler safe
- else
- ok, scanner = xpcall(function() return walkdir(path) end, function() end) -- kepler safe
+ path = path .. "."
+ elseif not find(path,"/$") then
+ path = path .. '/'
end
- if ok and type(scanner) == "function" then
- if not find(path,"/$") then path = path .. '/' end
- for name in scanner do
- local full = path .. name
- local mode = attributes(full,'mode')
- if mode == 'file' then
- if find(full,patt) then
- action(full)
- end
- elseif recurse and (mode == "directory") and (name ~= '.') and (name ~= "..") then
- globpattern(full,patt,recurse,action)
+ for name in walkdir(path) do
+ local full = path .. name
+ local mode = attributes(full,'mode')
+ if mode == 'file' then
+ if find(full,patt) then
+ action(full)
end
+ elseif recurse and (mode == "directory") and (name ~= '.') and (name ~= "..") then
+ globpattern(full,patt,recurse,action)
end
end
end
@@ -9363,10 +9420,10 @@ if not modules then modules = { } end modules ['data-exp'] = {
license = "see context related readme files",
}
-local format, gsub, find, gmatch, lower = string.format, string.gsub, string.find, string.gmatch, string.lower
+local format, find, gmatch, lower = string.format, 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 Ct, Cs, Cc, P, C, S = lpeg.Ct, lpeg.Cs, lpeg.Cc, lpeg.P, lpeg.C, lpeg.S
local type, next = type, next
local ostype = os.type
@@ -9381,7 +9438,7 @@ local resolvers = resolvers
-- 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.
+-- past this every time. See data-obs.lua for the gsub variant.
-- {a,b,c,d}
-- a,b,c/{p,q,r},d
@@ -9396,95 +9453,70 @@ local resolvers = resolvers
-- {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 function do_first(a,b)
+local function f_first(a,b)
local t, n = { }, 0
for s in gmatch(b,"[^,]+") do
- n = n + 1
- t[n] = a .. s
+ n = n + 1 ; t[n] = a .. s
end
- return "{" .. concat(t,",") .. "}"
+ return concat(t,",")
end
-local function do_second(a,b)
+local function f_second(a,b)
local t, n = { }, 0
for s in gmatch(a,"[^,]+") do
- n = n + 1
- t[n] = s .. b
+ n = n + 1 ; t[n] = s .. b
end
- return "{" .. concat(t,",") .. "}"
+ return concat(t,",")
end
-local function do_both(a,b)
+local function f_both(a,b)
local t, n = { }, 0
for sa in gmatch(a,"[^,]+") do
for sb in gmatch(b,"[^,]+") do
- n = n + 1
- t[n] = sa .. sb
+ n = n + 1 ; t[n] = sa .. sb
end
end
- return "{" .. concat(t,",") .. "}"
+ return concat(t,",")
end
-local function do_three(a,b,c)
- return a .. b.. c
-end
+local left = P("{")
+local right = P("}")
+local var = P((1 - S("{}" ))^0)
+local set = P((1 - S("{},"))^0)
+local other = P(1)
-local stripper_1 = lpeg.stripper("{}@")
+local l_first = Cs( ( Cc("{") * (C(set) * left * C(var) * right / f_first) * Cc("}") + other )^0 )
+local l_second = Cs( ( Cc("{") * (left * C(var) * right * C(set) / f_second) * Cc("}") + other )^0 )
+local l_both = Cs( ( Cc("{") * (left * C(var) * right * left * C(var) * right / f_both) * Cc("}") + other )^0 )
+local l_rest = Cs( ( left * var * (left/"") * var * (right/"") * var * right + other )^0 )
-local replacer_1 = lpeg.replacer {
- { ",}", ",@}" },
- { "{,", "{@," },
-}
+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
+local function splitpathexpr(str, newlist, validate) -- I couldn't resist lpegging it (nice exercise).
if trace_expansions then
report_resolvers("expanding variable '%s'",str)
end
local t, ok, done = newlist or { }, false, false
local n = #t
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
+ repeat local old = str
+ repeat local old = str ; str = lpegmatch(l_first, str) until old == str
+ repeat local old = str ; str = lpegmatch(l_second,str) until old == str
+ repeat local old = str ; str = lpegmatch(l_both, str) until old == str
+ repeat local old = str ; str = lpegmatch(l_rest, str) until old == str
+ until old == str -- or not find(str,"{")
str = lpegmatch(stripper_1,str)
if validate then
for s in gmatch(str,"[^,]+") do
s = validate(s)
if s then
- n = n + 1
- t[n] = s
+ n = n + 1 ; t[n] = s
end
end
else
for s in gmatch(str,"[^,]+") do
- n = n + 1
- t[n] = s
+ n = n + 1 ; t[n] = s
end
end
if trace_expansions then
@@ -9495,50 +9527,23 @@ local function splitpathexpr(str, newlist, validate)
return t
end
+-- We could make the previous one public.
+
local function validate(s)
- local isrecursive = find(s,"//$")
- s = collapsepath(s)
- if isrecursive then
- s = s .. "//"
- end
- return s ~= "" and not find(s,dummy_path_expr) and s
+ s = collapsepath(s) -- already keeps the //
+ return s ~= "" and not find(s,"^!*unset/*$") and s
end
resolvers.validatedpath = validate -- keeps the trailing //
-function resolvers.expandedpathfromlist(pathlist) -- maybe not a list, just a path
- -- a previous version fed back into pathlist
- local newlist, ok = { }, false
+function resolvers.expandedpathfromlist(pathlist)
+ local newlist = { }
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
- local n = 0
- for k=1,#pathlist do
- for p in gmatch(pathlist[k],"([^,]+)") do
- p = validate(p)
- if p ~= "" then
- n = n + 1
- newlist[n] = p
- end
- end
- end
+ splitpathexpr(pathlist[k],newlist,validate)
end
return newlist
end
--- We also put some cleanup code here.
-
-
-
-
local cleanup = lpeg.replacer {
{ "!" , "" },
{ "\\" , "/" },
@@ -9576,14 +9581,13 @@ end
-- This one strips quotes and funny tokens.
+local expandhome = P("~") / "$HOME" -- environment.homedir
-local expandhome = lpegP("~") / "$HOME" -- environment.homedir
+local dodouble = P('"')/"" * (expandhome + (1 - P('"')))^0 * P('"')/""
+local dosingle = P("'")/"" * (expandhome + (1 - P("'")))^0 * P("'")/""
+local dostring = (expandhome + 1 )^0
-local dodouble = lpegP('"')/"" * (expandhome + (1 - lpegP('"')))^0 * lpegP('"')/""
-local dosingle = lpegP("'")/"" * (expandhome + (1 - lpegP("'")))^0 * lpegP("'")/""
-local dostring = (expandhome + 1 )^0
-
-local stripper = lpegCs(
+local stripper = Cs(
lpegpatterns.unspacer * (dosingle + dodouble + dostring) * lpegpatterns.unspacer
)
@@ -9599,7 +9603,9 @@ end
local cache = { }
-local splitter = lpegCt(lpeg.splitat(lpegS(ostype == "windows" and ";" or ":;"))) -- maybe add ,
+local splitter = Ct(lpeg.splitat(S(ostype == "windows" and ";" or ":;"))) -- maybe add ,
+
+local backslashswapper = lpeg.replacer("\\","/")
local function splitconfigurationpath(str) -- beware, this can be either a path or a { specification }
if str then
@@ -9608,8 +9614,7 @@ local function splitconfigurationpath(str) -- beware, this can be either a path
if str == "" then
found = { }
else
- str = gsub(str,"\\","/")
- local split = lpegmatch(splitter,str)
+ local split = lpegmatch(splitter,lpegmatch(backslashswapper,str)) -- can be combined
found = { }
local noffound = 0
for i=1,#split do
@@ -9658,57 +9663,62 @@ end
-local weird = lpegP(".")^1 + lpeg.anywhere(lpegS("~`!#$%^&*()={}[]:;\"\'||<>,?\n\r\t"))
+local weird = P(".")^1 + lpeg.anywhere(S("~`!#$%^&*()={}[]:;\"\'||<>,?\n\r\t"))
-function resolvers.scanfiles(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
+local attributes, directory = lfs.attributes, lfs.dir
+
+local function scan(files,spec,path,n,m,r)
+ local full = (path == "" and spec) or (spec .. path .. '/')
+ local dirs, nofdirs = { }, 0
+ 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
- dirs[#dirs+1] = name
+ 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
+ nofdirs = nofdirs + 1
+ if path ~= "" then
+ dirs[nofdirs] = path..'/'..name
+ else
+ dirs[nofdirs] = name
+ end
end
end
- if #dirs > 0 then
- sort(dirs)
- for i=1,#dirs do
- scan(spec,dirs[i])
- end
+ end
+ if nofdirs > 0 then
+ sort(dirs)
+ for i=1,nofdirs do
+ files, n, m, r = scan(files,spec,dirs[i],n,m,r)
end
end
- scan(specification .. '/',"")
- files.__files__, files.__directories__, files.__remappings__ = n, m, r
+ return files, n, m, r
+end
+
+function resolvers.scanfiles(path)
+ if trace_locating then
+ report_resolvers("scanning path '%s'",path)
+ end
+ local files, n, m, r = scan({ },path .. '/',"",0,0,0)
+ files.__path__ = path
+ files.__files__ = n
+ files.__directories__ = m
+ files.__remappings__ = r
if trace_locating then
report_resolvers("%s files found on %s directories with %s uppercase remappings",n,m,r)
end
@@ -10399,9 +10409,15 @@ if not modules then modules = { } end modules ['data-met'] = {
license = "see context related readme files"
}
-local find = string.find
+local find, format = string.find, string.format
+local sequenced = table.sequenced
+local addurlscheme, urlhashed = url.addscheme, url.hashed
+
+local trace_locating = false
+
+trackers.register("resolvers.locating", function(v) trace_methods = v end)
+trackers.register("resolvers.methods", function(v) trace_methods = v end)
-local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
local report_resolvers = logs.new("resolvers")
@@ -10409,41 +10425,109 @@ local allocate = utilities.storage.allocate
local resolvers = resolvers
-resolvers.concatinators = allocate ()
-resolvers.locators = allocate { notfound = { nil } } -- locate databases
-resolvers.hashers = allocate { notfound = { nil } } -- load databases
-resolvers.generators = allocate { notfound = { nil } } -- generate databases
+local registered = { }
-function resolvers.splitmethod(filename) -- todo: trigger by suffix
+local function splitmethod(filename) -- todo: filetype in specification
if not filename then
- return { } -- safeguard
- elseif type(filename) == "table" then
+ return { scheme = "unknown", original = filename }
+ end
+ if type(filename) == "table" then
return filename -- already split
- elseif not find(filename,"://") then
- return { scheme="file", path = filename, original = filename } -- quick hack
+ end
+ filename = file.collapsepath(filename)
+ if not find(filename,"://") then
+ return { scheme = "file", path = filename, original = filename, filename = filename }
+ end
+ local specification = url.hashed(filename)
+ if not specification.scheme or specification.scheme == "" then
+ return { scheme = "file", path = filename, original = filename, filename = filename }
else
- return url.hashed(filename)
+ return specification
end
end
-function resolvers.methodhandler(what, filename, filetype) -- ...
- filename = file.collapsepath(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("using special handler for '%s' -> '%s' -> '%s'",specification.original,what,table.sequenced(specification))
+resolvers.splitmethod = splitmethod -- bad name but ok
+
+-- the second argument is always analyzed (saves time later on) and the original
+-- gets passed as original but also as argument
+
+local function methodhandler(what,first,...) -- filename can be nil or false
+ local method = registered[what]
+ if method then
+ local how, namespace = method.how, method.namespace
+ if how == "uri" or how == "url" then
+ local specification = splitmethod(first)
+ local scheme = specification.scheme
+ local resolver = namespace and namespace[scheme]
+ if resolver then
+ if trace_methods then
+ report_resolvers("resolver: method=%s, how=%s, scheme=%s, argument=%s",what,how,scheme,first)
+ end
+ return resolver(specification,...)
+ else
+ resolver = namespace.default or namespace.file
+ if resolver then
+ if trace_methods then
+ report_resolvers("resolver: method=%s, how=%s, default, argument=%s",what,how,first)
+ end
+ return resolver(specification,...)
+ elseif trace_methods then
+ report_resolvers("resolver: method=%s, how=%s, no handler",what,how)
+ end
+ end
+ elseif how == "tag" then
+ local resolver = namespace and namespace[first]
+ if resolver then
+ if trace_methods then
+ report_resolvers("resolver: method=%s, how=%s, tag=%s",what,how,first)
+ end
+ return resolver(...)
+ else
+ resolver = namespace.default or namespace.file
+ if resolver then
+ if trace_methods then
+ report_resolvers("resolver: method=%s, how=%s, default",what,how)
+ end
+ return resolver(...)
+ elseif trace_methods then
+ report_resolvers("resolver: method=%s, how=%s, unknown",what,how)
+ end
+ end
end
- return resolver[scheme](filename,filetype,specification) -- todo: query
else
- if trace_locating then
- report_resolvers("no handler for '%s' -> '%s' -> '%s'",specification.original,what,table.sequenced(specification))
+ report_resolvers("resolver: method=%s, unknown",what)
+ end
+end
+
+resolvers.methodhandler = methodhandler
+
+function resolvers.registermethod(name,namespace,how)
+ registered[name] = { how = how or "tag", namespace = namespace }
+ namespace["byscheme"] = function(scheme,filename,...)
+ if scheme == "file" then
+ return methodhandler(name,filename,...)
+ else
+ return methodhandler(name,addurlscheme(filename,scheme),...)
end
- return resolver.tex(filename,filetype) -- todo: specification
end
end
+local concatinators = allocate { notfound = file.join } -- concatinate paths
+local locators = allocate { notfound = function() end } -- locate databases
+local hashers = allocate { notfound = function() end } -- load databases
+local generators = allocate { notfound = function() end } -- generate databases
+
+resolvers.concatinators = concatinators
+resolvers.locators = locators
+resolvers.hashers = hashers
+resolvers.generators = generators
+
+local registermethod = resolvers.registermethod
+
+registermethod("concatinators",concatinators,"tag")
+registermethod("locators", locators, "uri")
+registermethod("hashers", hashers, "uri")
+registermethod("generators", generators, "uri")
end -- of closure
@@ -10471,11 +10555,11 @@ local concat, insert, sortedkeys = table.concat, table.insert, table.sortedkeys
local next, type = next, type
local os = os
-local lpegP, lpegS, lpegR, lpegC, lpegCc, lpegCs, lpegCt = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.Cc, lpeg.Cs, lpeg.Ct
+local P, S, R, C, Cc, Cs, Ct, Carg = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.Cc, lpeg.Cs, lpeg.Ct, lpeg.Carg
local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns
local filedirname, filebasename, fileextname, filejoin = file.dirname, file.basename, file.extname, file.join
-local collapsepath = file.collapsepath
+local collapsepath, joinpath = file.collapsepath, file.joinpath
local allocate = utilities.storage.allocate
local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
@@ -10489,6 +10573,7 @@ local resolvers = resolvers
local expandedpathfromlist = resolvers.expandedpathfromlist
local checkedvariable = resolvers.checkedvariable
local splitconfigurationpath = resolvers.splitconfigurationpath
+local methodhandler = resolvers.methodhandler
local initializesetter = utilities.setters.initialize
@@ -10502,12 +10587,12 @@ resolvers.luacnfspec = '{$SELFAUTODIR,$SELFAUTOPARENT}{,{/share,}/texmf{-local
resolvers.luacnfname = 'texmfcnf.lua'
resolvers.luacnfstate = "unknown"
-local unset_variable = "unset"
+local unset_variable = "unset"
-local formats = resolvers.formats
-local suffixes = resolvers.suffixes
-local dangerous = resolvers.dangerous
-local suffixmap = resolvers.suffixmap
+local formats = resolvers.formats
+local suffixes = resolvers.suffixes
+local dangerous = resolvers.dangerous
+local suffixmap = resolvers.suffixmap
resolvers.defaultsuffixes = { "tex" } -- "mkiv", "cld" -- too tricky
@@ -10552,7 +10637,7 @@ function resolvers.newinstance()
end
-function resolvers.setinstance(someinstance)
+function resolvers.setinstance(someinstance) -- only one instance is active
instance = someinstance
resolvers.instance = someinstance
return someinstance
@@ -10574,7 +10659,7 @@ function resolvers.setenv(key,value)
end
end
-function resolvers.getenv(key)
+local function getenv(key)
local value = instance.environment[key]
if value and value ~= "" then
return value
@@ -10584,23 +10669,55 @@ function resolvers.getenv(key)
end
end
-resolvers.env = resolvers.getenv
+resolvers.getenv = getenv
+resolvers.env = getenv
+
+local function resolve(key)
+ local value = instance.variables[key] or ""
+ return (value ~= "" and value) or getenv(key) or ""
+end
+
+local dollarstripper = lpeg.stripper("$")
+local inhibitstripper = P("!")^0 * Cs(P(1)^0)
+local backslashswapper = lpeg.replacer("\\","/")
+
+local somevariable = P("$") / ""
+local somekey = C(R("az","AZ","09","__","--")^1)
+local somethingelse = P(";") * ((1-S("!{}/\\"))^1 * P(";") / "")
+ + P(";") * (P(";") / "")
+ + P(1)
+
+local pattern = Cs( (somevariable * (somekey/resolve) + somethingelse)^1 )
local function expandvars(lst) -- simple vars
- local variables, getenv = instance.variables, resolvers.getenv
- local function resolve(a)
- local va = variables[a] or ""
- return (va ~= "" and va) or getenv(a) or ""
- end
for k=1,#lst do
- local var = lst[k]
- var = gsub(var,"%$([%a%d%_%-]+)",resolve)
- var = gsub(var,";+",";")
- var = gsub(var,";[!{}/\\]+;",";")
- lst[k] = var
+ local lk = lst[k]
+ lst[k] = lpegmatch(pattern,lk) or lk
+ end
+end
+
+
+local slash = P("/")
+
+local pattern = Cs (
+ Cc("^") * (
+ Cc("%") * S(".-")
+ + slash^2 * P(-1) / "/.*"
+ + slash^2 / "/.-/"
+ + (1-slash) * P(-1) * Cc("/")
+ + P(1)
+ )^1 * Cc("$")
+)
+
+local function makepathexpression(str)
+ if str == "." then
+ return "^%./$"
+ else
+ return lpegmatch(pattern,str)
end
end
+
local function resolve(key)
local value = instance.variables[key]
if value and value ~= "" then
@@ -10614,22 +10731,21 @@ local function resolve(key)
return e ~= nil and e ~= "" and checkedvariable(e) or ""
end
+local pattern = Cs( (somevariable * (somekey/resolve) + somethingelse)^1 )
+
local function expandedvariable(var) -- simple vars
- var = gsub(var,"%$([%a%d%_%-]+)",resolve)
- var = gsub(var,";+",";")
- var = gsub(var,";[!{}/\\]+;",";")
- return var
+ return lpegmatch(pattern,var) or var
end
+
local function entry(entries,name)
if name and name ~= "" then
- name = gsub(name,'%$','')
- -- local result = entries[name..'.'..instance.progname] or entries[name]
+ name = lpegmatch(dollarstripper,name)
local result = entries[instance.progname .. '.' .. name] or entries[name]
if result then
return result
else
- result = resolvers.getenv(name)
+ result = getenv(name)
if result then
instance.variables[name] = result
resolvers.expandvariables()
@@ -10642,8 +10758,7 @@ end
local function is_entry(entries,name)
if name and name ~= "" then
- name = gsub(name,'%$','')
- -- return (entries[name..'.'..instance.progname] or entries[name]) ~= nil
+ name = lpegmatch(dollarstripper,name)
return (entries[instance.progname .. '.' .. name] or entries[name]) ~= nil
else
return false
@@ -10654,7 +10769,7 @@ local function reportcriticalvariables()
if trace_locating then
for i=1,#resolvers.criticalvars do
local v = resolvers.criticalvars[i]
- report_resolvers("variable '%s' set to '%s'",v,resolvers.getenv(v) or "unknown")
+ report_resolvers("variable '%s' set to '%s'",v,getenv(v) or "unknown")
end
report_resolvers()
end
@@ -10664,7 +10779,7 @@ end
local function identify_configuration_files()
local specification = instance.specification
if #specification == 0 then
- local cnfspec = resolvers.getenv('TEXMFCNF')
+ local cnfspec = getenv('TEXMFCNF')
if cnfspec == "" then
cnfspec = resolvers.luacnfspec
resolvers.luacnfstate = "default"
@@ -10736,7 +10851,6 @@ local function load_configuration_files()
end
end
setups[pathname] = t
-
if resolvers.luacnfstate == "default" then
-- the following code is not tested
local cnfspec = t["TEXMFCNF"]
@@ -10798,63 +10912,30 @@ end
-- database loading
--- locators
-
-function resolvers.locatedatabase(specification)
- return resolvers.methodhandler('locators', specification)
-end
-
-function resolvers.locators.tex(specification)
- if specification and specification ~= '' and lfs.isdir(specification) then
- if trace_locating then
- report_resolvers("tex locator '%s' found",specification)
- end
- resolvers.appendhash('file',specification,filename,true) -- cache
- elseif trace_locating then
- report_resolvers("tex locator '%s' not found",specification)
- end
-end
-
--- hashers
-
-function resolvers.hashdatabase(tag,name)
- return resolvers.methodhandler('hashers',tag,name)
-end
-
local function load_file_databases()
instance.loaderror, instance.files = false, allocate()
if not instance.renewcache then
local hashes = instance.hashes
for k=1,#hashes do
local hash = hashes[k]
- resolvers.hashdatabase(hash.tag,hash.name)
+ resolvers.hashers.byscheme(hash.type,hash.name)
if instance.loaderror then break end
end
end
end
-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
-
local function locate_file_databases()
-- todo: cache:// and tree:// (runtime)
local texmfpaths = resolvers.expandedpathlist('TEXMF')
for i=1,#texmfpaths do
local path = collapsepath(texmfpaths[i])
- local stripped = gsub(path,"^!!","")
- local runtime = stripped == path
- path = resolvers.cleanpath(path)
+ local stripped = lpegmatch(inhibitstripper,path)
if stripped ~= "" then
+ local runtime = stripped == path
+ path = resolvers.cleanpath(path)
if lfs.isdir(path) then
local spec = resolvers.splitmethod(stripped)
- if spec.scheme == "cache" then
+ if spec.scheme == "cache" or spec.scheme == "file" then
stripped = spec.path
elseif runtime and (spec.noscheme or spec.scheme == "file") then
stripped = "tree:///" .. stripped
@@ -10866,7 +10947,7 @@ local function locate_file_databases()
report_resolvers("locating list of '%s' (cached)",path)
end
end
- resolvers.locatedatabase(stripped) -- nothing done with result
+ methodhandler('locators',stripped) -- nothing done with result
else
if trace_locating then
if runtime then
@@ -10885,8 +10966,9 @@ end
local function generate_file_databases()
local hashes = instance.hashes
- for i=1,#hashes do
- resolvers.methodhandler('generators',hashes[i].tag)
+ for k=1,#hashes do
+ local hash = hashes[k]
+ methodhandler('generators',hash.name)
end
if trace_locating then
report_resolvers()
@@ -10896,10 +10978,13 @@ end
local function save_file_databases() -- will become cachers
for i=1,#instance.hashes do
local hash = instance.hashes[i]
- local cachename = hash.tag
+ local cachename = hash.name
if hash.cache then
local content = instance.files[cachename]
caches.collapsecontent(content)
+ if trace_locating then
+ report_resolvers("saving tree '%s'",cachename)
+ end
caches.savecontent(cachename,"files",content)
elseif trace_locating then
report_resolvers("not saving runtime tree '%s'",cachename)
@@ -10923,23 +11008,22 @@ local function load_databases()
end
end
-function resolvers.appendhash(type,tag,name,cache)
+function resolvers.appendhash(type,name,cache)
if trace_locating then
- report_resolvers("hash '%s' appended",tag)
+ report_resolvers("hash '%s' appended",name)
end
- insert(instance.hashes, { type = type, tag = tag, name = name, cache = cache } )
+ insert(instance.hashes, { type = type, name = name, cache = cache } )
end
-function resolvers.prependhash(type,tag,name,cache)
+function resolvers.prependhash(type,name,cache)
if trace_locating then
- report_resolvers("hash '%s' prepended",tag)
+ report_resolvers("hash '%s' prepended",name)
end
- insert(instance.hashes, 1, { type = type, tag = tag, name = name, cache = cache } )
+ insert(instance.hashes, 1, { type = type, name = name, cache = cache } )
end
function resolvers.extendtexmfvariable(specification) -- crap, we could better prepend the hash
--- local t = resolvers.expandedpathlist('TEXMF') -- full expansion
- local t = resolvers.splitpath(resolvers.getenv('TEXMF'))
+ local t = resolvers.splitpath(getenv('TEXMF'))
insert(t,1,specification)
local newspec = concat(t,";")
if instance.environment["TEXMF"] then
@@ -10953,10 +11037,6 @@ function resolvers.extendtexmfvariable(specification) -- crap, we could better p
reset_hashes()
end
-function resolvers.generators.tex(specification,tag)
- instance.files[tag or specification] = resolvers.scanfiles(specification)
-end
-
function resolvers.splitexpansions()
local ie = instance.expansions
for k,v in next, ie do
@@ -10986,9 +11066,20 @@ function resolvers.datastate()
return caches.contentstate()
end
+local function resolve(a)
+ return instance.expansions[a] or getenv(a)
+end
+
+local cleaner = P("\\") / "/" + P(";") * S("!{}/\\")^0 * P(";")^1 / ";"
+
+local variable = R("az","AZ","09","__","--")^1 / resolve
+ variable = (P("$")/"") * (variable + (P("{")/"") * variable * (P("}")/""))
+
+ cleaner = Cs((cleaner + P(1))^0)
+ variable = Cs((variable + P(1))^0)
+
function resolvers.expandvariables()
local expansions, environment, variables = allocate(), instance.environment, instance.variables
- local getenv = resolvers.getenv
instance.expansions = expansions
local engine, progname = instance.engine, instance.progname
if type(engine) ~= "string" then instance.engine, engine = "", "" end
@@ -10996,12 +11087,7 @@ function resolvers.expandvariables()
if engine ~= "" then environment['engine'] = engine end
if progname ~= "" then environment['progname'] = progname end
for k,v in next, environment do
- -- local a, b = match(k,"^(%a+)%_(.*)%s*$") -- too many vars have an _ in the name
- -- if a and b then -- so let's forget about it; it was a
- -- expansions[a..'.'..b] = v -- hack anyway for linux and not needed
- -- else -- anymore as we now have directives
- expansions[k] = v
- -- end
+ expansions[k] = v
end
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
@@ -11009,26 +11095,19 @@ function resolvers.expandvariables()
for k,v in next, variables do -- move variables to expansions
if not expansions[k] then expansions[k] = v end
end
- local busy = false
- local function resolve(a)
- busy = true
- return expansions[a] or getenv(a)
- end
- while true do
- busy = false
+ repeat
+ local busy = false
for k,v in next, expansions do
- 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
+ local s = lpegmatch(variable,v)
+ if s ~= v then
+ busy = true
+ expansions[k] = s
end
end
- if not busy then break end
- end
+ until not busy
+
for k,v in next, expansions do
- expansions[k] = gsub(v,"\\", '/')
+ expansions[k] = lpegmatch(cleaner,v)
end
end
@@ -11055,7 +11134,7 @@ function resolvers.unexpandedpathlist(str)
end
function resolvers.unexpandedpath(str)
- return file.joinpath(resolvers.unexpandedpathlist(str))
+ return joinpath(resolvers.unexpandedpathlist(str))
end
local done = { }
@@ -11169,7 +11248,7 @@ function resolvers.cleanpathlist(str)
end
function resolvers.expandpath(str)
- return file.joinpath(resolvers.expandedpathlist(str))
+ return joinpath(resolvers.expandedpathlist(str))
end
function resolvers.expandedpathlist(str)
@@ -11177,7 +11256,7 @@ function resolvers.expandedpathlist(str)
return ep or { } -- ep ?
elseif instance.savelists then
-- engine+progname hash
- str = gsub(str,"%$","")
+ str = lpegmatch(dollarstripper,str)
if not instance.lists[str] then -- cached
local lst = made_list(instance,resolvers.splitpath(resolvers.expansion(str)))
instance.lists[str] = expandedpathfromlist(lst)
@@ -11190,28 +11269,34 @@ function resolvers.expandedpathlist(str)
end
function resolvers.expandedpathlistfromvariable(str) -- brrr
- local tmp = resolvers.variableofformatorsuffix(gsub(str,"%$",""))
- if tmp ~= "" then
- return resolvers.expandedpathlist(tmp)
- else
- return resolvers.expandedpathlist(str)
- end
+ str = lpegmatch(dollarstripper,str)
+ local tmp = resolvers.variableofformatorsuffix(str)
+ return resolvers.expandedpathlist(tmp ~= "" and tmp or str)
end
function resolvers.expandpathfromvariable(str)
- return file.joinpath(resolvers.expandedpathlistfromvariable(str))
+ return joinpath(resolvers.expandedpathlistfromvariable(str))
end
function resolvers.expandbraces(str) -- output variable and brace expansion of STRING
local ori = resolvers.variable(str)
local pth = expandedpathfromlist(resolvers.splitpath(ori))
- return file.joinpath(pth)
+ return joinpath(pth)
end
-resolvers.isreadable = { }
+function resolvers.registerfilehash(name,content,someerror)
+ if content then
+ instance.files[name] = content
+ else
+ instance.files[name] = { }
+ if somerror == true then -- can be unset
+ instance.loaderror = someerror
+ end
+ end
+end
-function resolvers.isreadable.file(name)
- local readable = lfs.isfile(name) -- brrr
+function isreadable(name)
+ local readable = file.is_readable(name)
if trace_detail then
if readable then
report_resolvers("file '%s' is readable",name)
@@ -11222,8 +11307,6 @@ function resolvers.isreadable.file(name)
return readable
end
-resolvers.isreadable.tex = resolvers.isreadable.file
-
-- name
-- name/name
@@ -11244,7 +11327,7 @@ local function collect_files(names)
local hashes = instance.hashes
for h=1,#hashes do
local hash = hashes[h]
- local blobpath = hash.tag
+ local blobpath = hash.name
local files = blobpath and instance.files[blobpath]
if files then
if trace_detail then
@@ -11265,7 +11348,7 @@ local function collect_files(names)
if not dname or find(blobfile,dname) then
local kind = hash.type
local search = filejoin(blobpath,blobfile,bname)
- local result = resolvers.concatinators[hash.type](blobroot,blobfile,bname)
+ local result = methodhandler('concatinators',hash.type,blobroot,blobfile,bname)
if trace_detail then
report_resolvers("match: kind '%s', search '%s', result '%s'",kind,search,result)
end
@@ -11278,7 +11361,7 @@ local function collect_files(names)
if not dname or find(vv,dname) then
local kind = hash.type
local search = filejoin(blobpath,vv,bname)
- local result = resolvers.concatinators[hash.type](blobroot,vv,bname)
+ local result = methodhandler('concatinators',hash.type,blobroot,vv,bname)
if trace_detail then
report_resolvers("match: kind '%s', search '%s', result '%s'",kind,search,result)
end
@@ -11316,6 +11399,8 @@ local function can_be_dir(name) -- can become local
return fakepaths[name] == 1
end
+local preparetreepattern = Cs((P(".")/"%%." + P("-")/"%%-" + P(1))^0 * Cc("$"))
+
local function collect_instance_files(filename,askedformat,allresults) -- todo : plugin (scanners, checkers etc)
local result = { }
local stamp = nil
@@ -11333,7 +11418,7 @@ local function collect_instance_files(filename,askedformat,allresults) -- todo :
end
end
if not dangerous[askedformat] then
- if resolvers.isreadable.file(filename) then
+ if isreadable(filename) then
if trace_detail then
report_resolvers("file '%s' found directly",filename)
end
@@ -11349,7 +11434,7 @@ local function collect_instance_files(filename,askedformat,allresults) -- todo :
end
result = resolvers.findwildcardfiles(filename) -- we can use th elocal
elseif file.is_qualified_path(filename) then
- if resolvers.isreadable.file(filename) then
+ if isreadable(filename) then
if trace_locating then
report_resolvers("qualified name '%s'", filename)
end
@@ -11362,7 +11447,7 @@ local function collect_instance_files(filename,askedformat,allresults) -- todo :
for i=1,#format_suffixes do
local s = format_suffixes[i]
forcedname = filename .. "." .. s
- if resolvers.isreadable.file(forcedname) then
+ if isreadable(forcedname) then
if trace_locating then
report_resolvers("no suffix, forcing format filetype '%s'", s)
end
@@ -11376,7 +11461,7 @@ local function collect_instance_files(filename,askedformat,allresults) -- todo :
-- try to find in tree (no suffix manipulation), here we search for the
-- matching last part of the name
local basename = filebasename(filename)
- local pattern = gsub(filename .. "$","([%.%-])","%%%1")
+ local pattern = lpegmatch(preparetreepattern,filename)
-- messy .. to be sorted out
local savedformat = askedformat
local format = savedformat or ""
@@ -11471,7 +11556,7 @@ local function collect_instance_files(filename,askedformat,allresults) -- todo :
end
for k=1,#wantedfiles do
local fname = wantedfiles[k]
- if fname and resolvers.isreadable.file(fname) then
+ if fname and isreadable(fname) then
filename, done = fname, true
result[#result+1] = filejoin('.',fname)
break
@@ -11497,26 +11582,15 @@ local function collect_instance_files(filename,askedformat,allresults) -- todo :
if trace_detail then
report_resolvers("checking filename '%s'",filename)
end
- -- a bit messy ... esp the doscan setting here
- local doscan
for k=1,#pathlist do
local path = pathlist[k]
- if find(path,"^!!") then doscan = false else doscan = true end
- local pathname = gsub(path,"^!+", '')
+ local pathname = lpegmatch(inhibitstripper,path)
+ local doscan = path == pathname -- no ^!!
done = false
-- using file list
if filelist then
- local expression
-- compare list entries with permitted pattern -- /xx /xx//
- if not find(pathname,"/$") then
- expression = pathname .. "/"
- else
- expression = pathname
- end
- expression = gsub(expression,"([%-%.])","%%%1") -- this also influences
- expression = gsub(expression,"//+$", '/.*') -- later usage of pathname
- expression = gsub(expression,"//", '/.-/') -- not ok for /// but harmless
- expression = "^" .. expression .. "$"
+ local expression = makepathexpression(pathname)
if trace_detail then
report_resolvers("using pattern '%s' for path '%s'",expression,pathname)
end
@@ -11545,7 +11619,8 @@ local function collect_instance_files(filename,askedformat,allresults) -- todo :
end
if not done and doscan then
-- check if on disk / unchecked / does not work at all / also zips
- if resolvers.splitmethod(pathname).scheme == 'file' then -- ?
+ local scheme = url.hasscheme(pathname)
+ if not scheme or scheme == "file" then
local pname = gsub(pathname,"%.%*$",'')
if not find(pname,"%*") then
local ppname = gsub(pname,"/+$","")
@@ -11553,7 +11628,7 @@ local function collect_instance_files(filename,askedformat,allresults) -- todo :
for k=1,#wantedfiles do
local w = wantedfiles[k]
local fname = filejoin(ppname,w)
- if resolvers.isreadable.file(fname) then
+ if isreadable(fname) then
if trace_detail then
report_resolvers("found '%s' by scanning",fname)
end
@@ -11586,9 +11661,6 @@ local function collect_instance_files(filename,askedformat,allresults) -- todo :
return result
end
-resolvers.concatinators.tex = filejoin
-resolvers.concatinators.file = resolvers.concatinators.tex
-
local function findfiles(filename,filetype,allresults)
local result = collect_instance_files(filename,filetype or "",allresults)
if #result == 0 then
@@ -11609,7 +11681,7 @@ function resolvers.findfile(filename,filetype)
end
function resolvers.findpath(filename,filetype)
- return file.dirname(findfiles(filename,filetype,false)[1] or "")
+ return filedirname(findfiles(filename,filetype,false)[1] or "")
end
local function findgivenfiles(filename,allresults)
@@ -11617,7 +11689,7 @@ local function findgivenfiles(filename,allresults)
local hashes = instance.hashes
for k=1,#hashes do
local hash = hashes[k]
- local files = instance.files[hash.tag] or { }
+ local files = instance.files[hash.name] or { }
local blist = files[bname]
if not blist then
local rname = "remap:"..bname
@@ -11629,12 +11701,12 @@ local function findgivenfiles(filename,allresults)
end
if blist then
if type(blist) == 'string' then
- result[#result+1] = resolvers.concatinators[hash.type](hash.tag,blist,bname) or ""
+ result[#result+1] = methodhandler('concatinators',hash.type,hash.name,blist,bname) or ""
if not allresults then break end
else
for kk=1,#blist do
local vv = blist[kk]
- result[#result+1] = resolvers.concatinators[hash.type](hash.tag,vv,bname) or ""
+ result[#result+1] = methodhandler('concatinators',hash.type,hash.name,vv,bname) or ""
if not allresults then break end
end
end
@@ -11657,14 +11729,14 @@ local function doit(path,blist,bname,tag,kind,result,allresults)
if type(blist) == 'string' then
-- make function and share code
if find(lower(blist),path) then
- result[#result+1] = resolvers.concatinators[kind](tag,blist,bname) or ""
+ result[#result+1] = methodhandler('concatinators',kind,tag,blist,bname) or ""
done = true
end
else
for kk=1,#blist do
local vv = blist[kk]
if find(lower(vv),path) then
- result[#result+1] = resolvers.concatinators[kind](tag,vv,bname) or ""
+ result[#result+1] = methodhandler('concatinators',kind,tag,vv,bname) or ""
done = true
if not allresults then break end
end
@@ -11674,30 +11746,25 @@ local function doit(path,blist,bname,tag,kind,result,allresults)
return done
end
+local makewildcard = Cs(
+ (P("^")^0 * P("/") * P(-1) + P(-1)) /".*"
+ + (P("^")^0 * P("/") / "") * (P("*")/".*" + P("-")/"%%-" + P("?")/"."+ P("\\")/"/" + P(1))^0
+)
+
local function findwildcardfiles(filename,allresults) -- todo: remap: and lpeg
local result = { }
- local bname, dname = filebasename(filename), filedirname(filename)
- local path = gsub(dname,"^*/","")
- path = gsub(path,"*",".*")
- path = gsub(path,"-","%%-")
- if dname == "" then
- path = ".*"
- end
- local name = bname
- name = gsub(name,"*",".*")
- name = gsub(name,"-","%%-")
- path = lower(path)
- name = lower(name)
+ local path = lower(lpegmatch(makewildcard,filedirname (filename)))
+ local name = lower(lpegmatch(makewildcard,filebasename(filename)))
local files, done = instance.files, false
if find(name,"%*") then
local hashes = instance.hashes
for k=1,#hashes do
local hash = hashes[k]
- local tag, kind = hash.tag, hash.type
- for kk, hh in next, files[hash.tag] do
+ local hashname, hashtype = hash.name, hash.type
+ for kk, hh in next, files[hashname] do
if not find(kk,"^remap:") then
if find(lower(kk),name) then
- if doit(path,hh,kk,tag,kind,result,allresults) then done = true end
+ if doit(path,hh,kk,hashname,hashtype,result,allresults) then done = true end
if done and not allresults then break end
end
end
@@ -11707,8 +11774,8 @@ local function findwildcardfiles(filename,allresults) -- todo: remap: and lpeg
local hashes = instance.hashes
for k=1,#hashes do
local hash = hashes[k]
- local tag, kind = hash.tag, hash.type
- if doit(path,files[tag][bname],bname,tag,kind,result,allresults) then done = true end
+ local hashname, hashtype = hash.name, hash.type
+ if doit(path,files[hashname][bname],bname,hashname,hashtype,result,allresults) then done = true end
if done and not allresults then break end
end
end
@@ -11779,12 +11846,9 @@ end
-- resolvers.expandvar = resolvers.expansion -- output variable expansion of STRING.
function resolvers.showpath(str) -- output search path for file type NAME
- return file.joinpath(resolvers.expandedpathlist(resolvers.formatofvariable(str)))
+ return joinpath(resolvers.expandedpathlist(resolvers.formatofvariable(str)))
end
--- resolvers.findfile(filename)
--- resolvers.findfile(filename, f.iletype)
-
function resolvers.registerfile(files, name, path)
if files[name] then
if type(files[name]) == 'string' then
@@ -11809,7 +11873,7 @@ function resolvers.dowithvariable(name,func)
end
function resolvers.locateformat(name)
- local barename = gsub(name,"%.%a+$","")
+ local barename = file.removesuffix(name) -- gsub(name,"%.%a+$","")
local fmtname = caches.getfirstreadablefile(barename..".fmt","formats") or ""
if fmtname == "" then
fmtname = resolvers.findfile(barename..".fmt")
@@ -11845,7 +11909,7 @@ function resolvers.dowithfilesintree(pattern,handle,before,after) -- can be a ni
for i=1,#hashes do
local hash = hashes[i]
local blobtype = hash.type
- local blobpath = hash.tag
+ local blobpath = hash.name
if blobpath then
if before then
before(blobtype,blobpath,pattern)
@@ -12020,13 +12084,23 @@ if not modules then modules = { } end modules ['data-inp'] = {
license = "see context related readme files"
}
-local allocate = utilities.storage.allocate
-
+local allocate = utilities.storage.allocate
local resolvers = resolvers
-resolvers.finders = allocate { notfound = { nil } }
-resolvers.openers = allocate { notfound = { nil } }
-resolvers.loaders = allocate { notfound = { false, nil, 0 } }
+local methodhandler = resolvers.methodhandler
+local registermethod = resolvers.registermethod
+
+local finders = allocate { helpers = { }, notfound = function() end }
+local openers = allocate { helpers = { }, notfound = function() end }
+local loaders = allocate { helpers = { }, notfound = function() return false, nil, 0 end }
+
+registermethod("finders", finders, "uri")
+registermethod("openers", openers, "uri")
+registermethod("loaders", loaders, "uri")
+
+resolvers.finders = finders
+resolvers.openers = openers
+resolvers.loaders = loaders
end -- of closure
@@ -12041,8 +12115,134 @@ if not modules then modules = { } end modules ['data-out'] = {
license = "see context related readme files"
}
-resolvers.savers = utilities.storage.allocate { }
+local allocate = utilities.storage.allocate
+local resolvers = resolvers
+
+local registermethod = resolvers.registermethod
+
+local savers = allocate { helpers = { } }
+resolvers.savers = savers
+
+registermethod("savers", savers, "uri")
+
+
+end -- of closure
+
+do -- create closure to overcome 200 locals limit
+
+if not modules then modules = { } end modules ['data-fil'] = {
+ version = 1.001,
+ comment = "companion to luat-lib.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
+
+local report_resolvers = logs.new("resolvers")
+
+local resolvers = resolvers
+
+local finders, openers, loaders, savers = resolvers.finders, resolvers.openers, resolvers.loaders, resolvers.savers
+local locators, hashers, generators, concatinators = resolvers.locators, resolvers.hashers, resolvers.generators, resolvers.concatinators
+
+local checkgarbage = utilities.garbagecollector and utilities.garbagecollector.check
+
+function locators.file(specification)
+ local name = specification.filename
+ if name and name ~= '' and lfs.isdir(name) then
+ if trace_locating then
+ report_resolvers("file locator '%s' found",name)
+ end
+ resolvers.appendhash('file',name,true) -- cache
+ elseif trace_locating then
+ report_resolvers("file locator '%s' not found",name)
+ end
+end
+
+function hashers.file(specification)
+ local name = specification.filename
+ local content = caches.loadcontent(name,'files')
+ resolvers.registerfilehash(name,content,content==nil)
+end
+
+function generators.file(specification)
+ local name = specification.filename
+ local content = resolvers.scanfiles(name)
+ resolvers.registerfilehash(name,content,true)
+end
+
+concatinators.file = file.join
+
+function finders.file(specification,filetype)
+ local filename = specification.filename
+ local foundname = resolvers.findfile(filename,filetype)
+ if foundname and foundname ~= "" then
+ if trace_locating then
+ report_resolvers("file finder: '%s' found",filename)
+ end
+ return foundname
+ else
+ if trace_locating then
+ report_resolvers("file finder: %s' not found",filename)
+ end
+ return finders.notfound()
+ end
+end
+
+-- The default textopener will be overloaded later on.
+
+function openers.helpers.textopener(tag,filename,f)
+ return {
+ reader = function() return f:read () end,
+ close = function() return f:close() end,
+ }
+end
+
+function openers.file(specification,filetype)
+ local filename = specification.filename
+ if filename and filename ~= "" then
+ local f = io.open(filename,"r")
+ if f then
+ logs.show_open(filename) -- todo
+ if trace_locating then
+ report_resolvers("file opener, '%s' opened",filename)
+ end
+ return openers.helpers.textopener("file",filename,f)
+ end
+ end
+ if trace_locating then
+ report_resolvers("file opener, '%s' not found",filename)
+ end
+ return openers.notfound()
+end
+
+function loaders.file(specification,filetype)
+ local filename = specification.filename
+ if filename and filename ~= "" then
+ local f = io.open(filename,"rb")
+ if f then
+ logs.show_load(filename)
+ if trace_locating then
+ report_resolvers("file loader, '%s' loaded",filename)
+ end
+ local s = f:read("*a")
+ if checkgarbage then
+ checkgarbage(#s)
+ end
+ f:close()
+ if s then
+ return true, s, #s
+ end
+ end
+ end
+ if trace_locating then
+ report_resolvers("file loader, '%s' not found",filename)
+ end
+ return loaders.notfound()
+end
end -- of closure
@@ -12301,10 +12501,9 @@ if not modules then modules = { } end modules ['data-zip'] = {
license = "see context related readme files"
}
--- to be redone using the more recent schemes mechanism
+-- partly redone .. needs testing
local format, find, match = string.format, string.find, string.match
-local unpack = unpack or table.unpack
local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
@@ -12327,9 +12526,6 @@ local archives = zip.archives
zip.registeredfiles = zip.registeredfiles or { }
local registeredfiles = zip.registeredfiles
-local finders, openers, loaders = resolvers.finders, resolvers.openers, resolvers.loaders
-local locators, hashers, concatinators = resolvers.locators, resolvers.hashers, resolvers.concatinators
-
local function validzip(str) -- todo: use url splitter
if not find(str,"^zip://") then
return "zip:///" .. str
@@ -12359,159 +12555,159 @@ function zip.closearchive(name)
end
end
-function locators.zip(specification) -- where is this used? startup zips (untested)
- specification = resolvers.splitmethod(specification)
- local zipfile = specification.path
- local zfile = zip.openarchive(name) -- tricky, could be in to be initialized tree
+function resolvers.locators.zip(specification)
+ local archive = specification.filename
+ local zipfile = archive and archive ~= "" and zip.openarchive(archive) -- tricky, could be in to be initialized tree
if trace_locating then
- if zfile then
- report_resolvers("zip locator, archive '%s' found",specification.original)
+ if zipfile then
+ report_resolvers("zip locator, archive '%s' found",archive)
else
- report_resolvers("zip locator, archive '%s' not found",specification.original)
+ report_resolvers("zip locator, archive '%s' not found",archive)
end
end
end
-function hashers.zip(tag,name)
+function resolvers.hashers.zip(specification)
+ local archive = specification.filename
if trace_locating then
- report_resolvers("loading zip file '%s' as '%s'",name,tag)
+ report_resolvers("loading zip file '%s'",archive)
end
- resolvers.usezipfile(format("%s?tree=%s",tag,name))
+ resolvers.usezipfile(specification.original)
end
-function concatinators.zip(tag,path,name)
+function resolvers.concatinators.zip(zipfile,path,name) -- ok ?
if not path or path == "" then
- return format('%s?name=%s',tag,name)
+ return format('%s?name=%s',zipfile,name)
else
- return format('%s?name=%s/%s',tag,path,name)
+ return format('%s?name=%s/%s',zipfile,path,name)
end
end
-function resolvers.isreadable.zip(name)
- return true
-end
-
-function finders.zip(specification,filetype)
- specification = resolvers.splitmethod(specification)
- if specification.path then
- local q = url.query(specification.query)
- if q.name then
- local zfile = zip.openarchive(specification.path)
+function resolvers.finders.zip(specification)
+ local original = specification.original
+ local archive = specification.filename
+ if archive then
+ local query = url.query(specification.query)
+ local queryname = query.name
+ if queryname then
+ local zfile = zip.openarchive(archive)
if zfile then
if trace_locating then
- report_resolvers("zip finder, archive '%s' found",specification.path)
+ report_resolvers("zip finder, archive '%s' found",archive)
end
- local dfile = zfile:open(q.name)
+ local dfile = zfile:open(queryname)
if dfile then
dfile = zfile:close()
if trace_locating then
- report_resolvers("zip finder, file '%s' found",q.name)
+ report_resolvers("zip finder, file '%s' found",queryname)
end
return specification.original
elseif trace_locating then
- report_resolvers("zip finder, file '%s' not found",q.name)
+ report_resolvers("zip finder, file '%s' not found",queryname)
end
elseif trace_locating then
- report_resolvers("zip finder, unknown archive '%s'",specification.path)
+ report_resolvers("zip finder, unknown archive '%s'",archive)
end
end
end
if trace_locating then
- report_resolvers("zip finder, '%s' not found",filename)
+ report_resolvers("zip finder, '%s' not found",original)
end
- return unpack(finders.notfound)
+ return resolvers.finders.notfound()
end
-function openers.zip(specification)
- local zipspecification = resolvers.splitmethod(specification)
- if zipspecification.path then
- local q = url.query(zipspecification.query)
- if q.name then
- local zfile = zip.openarchive(zipspecification.path)
+function resolvers.openers.zip(specification)
+ local original = specification.original
+ local archive = specification.filename
+ if archive then
+ local query = url.query(specification.query)
+ local queryname = query.name
+ if queryname then
+ local zfile = zip.openarchive(archive)
if zfile then
if trace_locating then
- report_resolvers("zip opener, archive '%s' opened",zipspecification.path)
+ report_resolvers("zip opener, archive '%s' opened",archive)
end
- local dfile = zfile:open(q.name)
+ local dfile = zfile:open(queryname)
if dfile then
- logs.show_open(specification)
+ logs.show_open(original)
if trace_locating then
- report_resolvers("zip opener, file '%s' found",q.name)
+ report_resolvers("zip opener, file '%s' found",queryname)
end
- return openers.textopener('zip',specification,dfile)
+ return resolvers.openers.helpers.textopener('zip',original,dfile)
elseif trace_locating then
- report_resolvers("zip opener, file '%s' not found",q.name)
+ report_resolvers("zip opener, file '%s' not found",queryname)
end
elseif trace_locating then
- report_resolvers("zip opener, unknown archive '%s'",zipspecification.path)
+ report_resolvers("zip opener, unknown archive '%s'",archive)
end
end
end
if trace_locating then
- report_resolvers("zip opener, '%s' not found",filename)
+ report_resolvers("zip opener, '%s' not found",original)
end
- return unpack(openers.notfound)
+ return resolvers.openers.notfound()
end
-function loaders.zip(specification)
- specification = resolvers.splitmethod(specification)
- if specification.path then
- local q = url.query(specification.query)
- if q.name then
- local zfile = zip.openarchive(specification.path)
+function resolvers.loaders.zip(specification)
+ local original = specification.original
+ local archive = specification.filename
+ if archive then
+ local query = url.query(specification.query)
+ local queryname = query.name
+ if queryname then
+ local zfile = zip.openarchive(archive)
if zfile then
if trace_locating then
- report_resolvers("zip loader, archive '%s' opened",specification.path)
+ report_resolvers("zip loader, archive '%s' opened",archive)
end
- local dfile = zfile:open(q.name)
+ local dfile = zfile:open(queryname)
if dfile then
- logs.show_load(filename)
+ logs.show_load(original)
if trace_locating then
- report_resolvers("zip loader, file '%s' loaded",filename)
+ report_resolvers("zip loader, file '%s' loaded",original)
end
local s = dfile:read("*all")
dfile:close()
return true, s, #s
elseif trace_locating then
- report_resolvers("zip loader, file '%s' not found",q.name)
+ report_resolvers("zip loader, file '%s' not found",queryname)
end
elseif trace_locating then
- report_resolvers("zip loader, unknown archive '%s'",specification.path)
+ report_resolvers("zip loader, unknown archive '%s'",archive)
end
end
end
if trace_locating then
- report_resolvers("zip loader, '%s' not found",filename)
+ report_resolvers("zip loader, '%s' not found",original)
end
- return unpack(openers.notfound)
+ return resolvers.openers.notfound()
end
-- zip:///somefile.zip
-- zip:///somefile.zip?tree=texmf-local -> mount
-function resolvers.usezipfile(zipname)
- zipname = validzip(zipname)
- local specification = resolvers.splitmethod(zipname)
- local zipfile = specification.path
- if zipfile and not registeredfiles[zipname] then
- local tree = url.query(specification.query).tree or ""
- local z = zip.openarchive(zipfile)
+function resolvers.usezipfile(archive)
+ local specification = resolvers.splitmethod(archive) -- to be sure
+ local archive = specification.filename
+ if archive and not registeredfiles[archive] then
+ local z = zip.openarchive(archive)
if z then
- local instance = resolvers.instance
+ local tree = url.query(specification.query).tree or ""
if trace_locating then
- report_resolvers("zip registering, registering archive '%s'",zipname)
- end
- statistics.starttiming(instance)
- resolvers.prependhash('zip',zipname,zipfile)
- resolvers.extendtexmfvariable(zipname) -- resets hashes too
- registeredfiles[zipname] = z
- instance.files[zipname] = resolvers.registerzipfile(z,tree or "")
- statistics.stoptiming(instance)
+ report_resolvers("zip registering, registering archive '%s'",archive)
+ end
+ statistics.starttiming(resolvers.instance)
+ resolvers.prependhash('zip',archive)
+ resolvers.extendtexmfvariable(archive) -- resets hashes too
+ registeredfiles[archive] = z
+ instance.files[archive] = resolvers.registerzipfile(z,tree)
+ statistics.stoptiming(resolvers.instance)
elseif trace_locating then
- report_resolvers("zip registering, unknown archive '%s'",zipname)
+ report_resolvers("zip registering, unknown archive '%s'",archive)
end
elseif trace_locating then
- report_resolvers("zip registering, '%s' not found",zipname)
+ report_resolvers("zip registering, '%s' not found",archive)
end
end
@@ -12560,7 +12756,8 @@ if not modules then modules = { } end modules ['data-tre'] = {
-- \input tree://oeps1/**/oeps.tex
local find, gsub, format = string.find, string.gsub, string.format
-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")
@@ -12568,10 +12765,10 @@ local resolvers = 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 ""
+function resolvers.finders.tree(specification)
+ local spec = specification.filename
+ local fnd = found[spec]
+ if fnd == nil then
if spec ~= "" then
local path, name = file.dirname(spec), file.basename(spec)
if path == "" then path = "." end
@@ -12585,53 +12782,41 @@ function resolvers.finders.tree(specification,filetype)
for k=1,#hash do
local v = hash[k]
if find(v,pattern) then
- found[specification] = v
+ found[spec] = v
return v
end
end
end
- fnd = unpack(notfound) -- unpack ? why not just notfound[1]
- found[specification] = fnd
+ fnd = notfound() -- false
+ found[spec] = 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
+ local name = specification.filename
+ if name ~= '' and lfs.isdir(name) then
if trace_locating then
- report_resolvers("tree locator '%s' found (%s)",path,specification)
+ report_resolvers("tree locator '%s' found",name)
end
- resolvers.appendhash('tree',specification,path,false) -- don't cache
+ resolvers.appendhash('tree',name,false) -- don't cache
elseif trace_locating then
- report_resolvers("tree locator '%s' not found",path)
+ report_resolvers("tree locator '%s' not found",name)
end
end
-function resolvers.hashers.tree(tag,name)
+function resolvers.hashers.tree(specification)
+ local name = specification.filename
if trace_locating then
- report_resolvers("analysing tree '%s' as '%s'",name,tag)
+ report_resolvers("analysing tree '%s'",name)
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
+ resolvers.methodhandler("hashers",name)
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
+resolvers.concatinators.tree = resolvers.concatinators.file
+resolvers.generators.tree = resolvers.generators.file
+resolvers.openers.tree = resolvers.openers.file
+resolvers.loaders.tree = resolvers.loaders.file
end -- of closure
@@ -12654,53 +12839,51 @@ local resolvers = resolvers
local finders, openers, loaders = resolvers.finders, resolvers.openers, resolvers.loaders
-curl = curl or { }
-local curl = curl
+resolvers.curl = resolvers.curl or { }
+local curl = resolvers.curl
local cached = { }
-function curl.fetch(protocol, name) -- todo: use socket library
- local cleanname = gsub(name,"[^%a%d%.]+","-")
+local function runcurl(specification)
+ local original = specification.original
+ -- local scheme = specification.scheme
+ local cleanname = gsub(original,"[^%a%d%.]+","-")
local cachename = caches.setfirstwritablefile(cleanname,"curl")
- if not cached[name] then
+ if not cached[original] then
if not io.exists(cachename) then
- cached[name] = cachename
- local command = "curl --silent --create-dirs --output " .. cachename .. " " .. name -- no protocol .. "://"
+ cached[original] = cachename
+ local command = "curl --silent --create-dirs --output " .. cachename .. " " .. original
os.spawn(command)
end
if io.exists(cachename) then
- cached[name] = cachename
+ cached[original] = cachename
else
- cached[name] = ""
+ cached[original] = ""
end
end
- return cached[name]
+ return cached[original]
end
-function finders.curl(protocol,filename)
- local foundname = curl.fetch(protocol, filename)
- return finders.generic(protocol,foundname,filetype)
-end
+-- old code: we could be cleaner using specification (see schemes)
-function openers.curl(protocol,filename)
- return openers.generic(protocol,filename)
+local function finder(specification,filetype)
+ return resolvers.methodhandler("finders",runcurl(specification),filetype)
end
-function loaders.curl(protocol,filename)
- return loaders.generic(protocol,filename)
-end
-
--- todo: metamethod
+local opener = openers.file
+local loader = loaders.file
-function curl.install(protocol)
- finders[protocol] = function (filename,filetype) return finders.curl(protocol,filename) end
- openers[protocol] = function (filename) return openers.curl(protocol,filename) end
- loaders[protocol] = function (filename) return loaders.curl(protocol,filename) end
+local function install(scheme)
+ finders[scheme] = finder
+ openers[scheme] = opener
+ loaders[scheme] = loader
end
-curl.install('http')
-curl.install('https')
-curl.install('ftp')
+resolvers.curl.install = install
+
+install('http')
+install('https')
+install('ftp')
end -- of closure
@@ -12777,7 +12960,7 @@ local function loaded(libpaths,name,simple)
if trace_locating then -- more detail
report_resolvers("! checking for '%s' on 'package.path': '%s' => '%s'",simple,libpath,resolved)
end
- if resolvers.isreadable.file(resolved) then
+ if file.is_readable(resolved) then
if trace_locating then
report_resolvers("! lib '%s' located via 'package.path': '%s'",name,resolved)
end
@@ -12786,7 +12969,6 @@ local function loaded(libpaths,name,simple)
end
end
-
package.loaders[2] = function(name) -- was [#package.loaders+1]
if trace_locating then -- mode detail
report_resolvers("! locating '%s'",name)
@@ -12824,7 +13006,7 @@ package.loaders[2] = function(name) -- was [#package.loaders+1]
if trace_locating then -- mode detail
report_resolvers("! checking for '%s' using 'clibformat path': '%s'",libname,path)
end
- if resolvers.isreadable.file(resolved) then
+ if file.is_readable(resolved) then
if trace_locating then
report_resolvers("! lib '%s' located via 'clibformat': '%s'",libname,resolved)
end
@@ -12838,7 +13020,7 @@ package.loaders[2] = function(name) -- was [#package.loaders+1]
if trace_locating then -- more detail
report_resolvers("! checking for '%s' on 'package.cpath': '%s'",simple,libpath)
end
- if resolvers.isreadable.file(resolved) then
+ if file.is_readable(resolved) then
if trace_locating then
report_resolvers("! lib '%s' located via 'package.cpath': '%s'",name,resolved)
end
@@ -13375,6 +13557,7 @@ own.libs = { -- order can be made better
'data-pre.lua',
'data-inp.lua',
'data-out.lua',
+ 'data-fil.lua',
'data-con.lua',
'data-use.lua',
-- 'data-tex.lua',
diff --git a/scripts/context/stubs/unix/mtxrun b/scripts/context/stubs/unix/mtxrun
index 3be305bed..6b74022ae 100644
--- a/scripts/context/stubs/unix/mtxrun
+++ b/scripts/context/stubs/unix/mtxrun
@@ -119,7 +119,7 @@ local patterns_escapes = {
["."] = "%.",
["+"] = "%+", ["-"] = "%-", ["*"] = "%*",
["["] = "%[", ["]"] = "%]",
- ["("] = "%)", [")"] = "%)",
+ ["("] = "%(", [")"] = "%)",
-- ["{"] = "%{", ["}"] = "%}"
-- ["^"] = "%^", ["$"] = "%$",
}
@@ -185,6 +185,7 @@ local patterns = lpeg.patterns
local P, R, S, V, match = lpeg.P, lpeg.R, lpeg.S, lpeg.V, lpeg.match
local Ct, C, Cs, Cc, Cf, Cg = lpeg.Ct, lpeg.C, lpeg.Cs, lpeg.Cc, lpeg.Cf, lpeg.Cg
+local lpegtype = lpeg.type
local utfcharacters = string.utfcharacters
local utfgmatch = unicode and unicode.utf8.gmatch
@@ -201,7 +202,6 @@ patterns.alwaysmatched = alwaysmatched
local digit, sign = R('09'), S('+-')
local cr, lf, crlf = P("\r"), P("\n"), P("\r\n")
local newline = crlf + cr + lf
-local utf8next = R("\128\191")
local escaped = P("\\") * anything
local squote = P("'")
local dquote = P('"')
@@ -222,6 +222,8 @@ local utftype = utfbom_32_be / "utf-32-be" + utfbom_32_le / "utf-32-le
+ utfbom_16_be / "utf-16-be" + utfbom_16_le / "utf-16-le"
+ utfbom_8 / "utf-8" + alwaysmatched / "unknown"
+local utf8next = R("\128\191")
+
patterns.utf8one = R("\000\127")
patterns.utf8two = R("\194\223") * utf8next
patterns.utf8three = R("\224\239") * utf8next * utf8next
@@ -432,19 +434,25 @@ end
-- Just for fun I looked at the used bytecode and
-- p = (p and p + pp) or pp gets one more (testset).
-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]
- if p then
- p = p + pp
- else
- p = pp
+function lpeg.replacer(one,two)
+ if type(one) == "table" then
+ local no = #one
+ if no > 0 then
+ local p
+ for i=1,no do
+ local o = one[i]
+ local pp = P(o[1]) / o[2]
+ if p then
+ p = p + pp
+ else
+ p = pp
+ end
end
+ return Cs((p + 1)^0)
end
- return Cs((p + 1)^0)
+ else
+ two = two or ""
+ return Cs((P(one)/two + 1)^0)
end
end
@@ -646,6 +654,10 @@ function lpeg.oneof(list,...) -- lpeg.oneof("elseif","else","if","then")
return p
end
+function lpeg.is_lpeg(p)
+ return p and lpegtype(p) == "pattern"
+end
+
end -- of closure
@@ -2558,6 +2570,9 @@ local separator = P("://")
local qualified = P(".")^0 * P("/") + letter*P(":") + letter^1*separator + letter^1 * P("/")
local rootbased = P("/") + letter*P(":")
+lpeg.patterns.qualified = qualified
+lpeg.patterns.rootbased = rootbased
+
-- ./name ../name /name c: :// name/name
function file.is_qualified_path(filename)
@@ -2678,72 +2693,95 @@ if not modules then modules = { } end modules ['l-url'] = {
license = "see context related readme files"
}
-local char, gmatch, gsub, format, byte = string.char, string.gmatch, string.gsub, string.format, string.byte
+local char, gmatch, gsub, format, byte, find = string.char, string.gmatch, string.gsub, string.format, string.byte, string.find
local concat = table.concat
local tonumber, type = tonumber, type
-local lpegmatch, lpegP, lpegC, lpegR, lpegS, lpegCs, lpegCc = lpeg.match, lpeg.P, lpeg.C, lpeg.R, lpeg.S, lpeg.Cs, lpeg.Cc
+local P, C, R, S, Cs, Cc, Ct = lpeg.P, lpeg.C, lpeg.R, lpeg.S, lpeg.Cs, lpeg.Cc, lpeg.Ct
+local lpegmatch, lpegpatterns, replacer = lpeg.match, lpeg.patterns, lpeg.replacer
--- from the spec (on the web):
+-- from wikipedia:
--
--- foo://example.com:8042/over/there?name=ferret#nose
--- \_/ \______________/\_________/ \_________/ \__/
--- | | | | |
--- scheme authority path query fragment
--- | _____________________|__
--- / \ / \
--- urn:example:animal:ferret:nose
+-- foo://username:password@example.com:8042/over/there/index.dtb?type=animal;name=narwhal#nose
+-- \_/ \_______________/ \_________/ \__/ \___/ \_/ \______________________/ \__/
+-- | | | | | | | |
+-- | userinfo hostname port | | query fragment
+-- | \________________________________/\_____________|____|/
+-- scheme | | | |
+-- | authority path | |
+-- | | |
+-- | path interpretable as filename
+-- | ___________|____________ |
+-- / \ / \ |
+-- urn:example:animal:ferret:nose interpretable as extension
url = url or { }
local url = url
-local function tochar(s)
- return char(tonumber(s,16))
-end
+local tochar = function(s) return char(tonumber(s,16)) end
-local colon, qmark, hash, slash, percent, endofstring = lpegP(":"), lpegP("?"), lpegP("#"), lpegP("/"), lpegP("%"), lpegP(-1)
+local colon = P(":")
+local qmark = P("?")
+local hash = P("#")
+local slash = P("/")
+local percent = P("%")
+local endofstring = P(-1)
-local hexdigit = lpegR("09","AF","af")
-local plus = lpegP("+")
-local nothing = lpegCc("")
-local escaped = (plus / " ") + (percent * lpegC(hexdigit * hexdigit) / tochar)
+local hexdigit = R("09","AF","af")
+local plus = P("+")
+local nothing = Cc("")
+local escaped = (plus / " ") + (percent * C(hexdigit * hexdigit) / tochar)
-- we assume schemes with more than 1 character (in order to avoid problems with windows disks)
-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)
+local scheme = Cs((escaped+(1-colon-slash-qmark-hash))^2) * colon + nothing
+local authority = slash * slash * Cs((escaped+(1- slash-qmark-hash))^0) + nothing
+local path = slash * Cs((escaped+(1- qmark-hash))^0) + nothing
+local query = qmark * Cs((escaped+(1- hash))^0) + nothing
+local fragment = hash * Cs((escaped+(1- endofstring))^0) + nothing
-lpeg.patterns.urlsplitter = parser
+local parser = Ct(scheme * authority * path * query * fragment)
-local escapes = { }
+lpegpatterns.urlsplitter = parser
-for i=0,255 do
- escapes[i] = format("%%%02X",i)
-end
+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)
+local escaper = Cs((R("09","AZ","az") + S("-./_") + P(1) / escapes)^0)
-lpeg.patterns.urlescaper = escaper
+lpegpatterns.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
-function url.split(str)
+local function split(str)
return (type(str) == "string" and lpegmatch(parser,str)) or str
end
+local function hasscheme(str)
+ local scheme = lpegmatch(scheme,str) -- at least one character
+ return scheme and scheme ~= ""
+end
+
-- todo: cache them
-function url.hashed(str) -- not yet ok (/test?test)
- local s = url.split(str)
+local rootletter = R("az","AZ")
+ + S("_-+")
+local separator = P("://")
+local qualified = P(".")^0 * P("/")
+ + rootletter * P(":")
+ + rootletter^1 * separator
+ + rootletter^1 * P("/")
+local rootbased = P("/")
+ + rootletter * P(":")
+
+local barswapper = replacer("|",":")
+local backslashswapper = replacer("\\","/")
+
+local function hashed(str) -- not yet ok (/test?test)
+ local s = split(str)
local somescheme = s[1] ~= ""
local somequery = s[4] ~= ""
if not somescheme and not somequery then
- return {
+ s = {
scheme = "file",
authority = "",
path = str,
@@ -2751,52 +2789,73 @@ function url.hashed(str) -- not yet ok (/test?test)
fragment = "",
original = str,
noscheme = true,
+ filename = str,
}
- else
- return {
+ else -- not always a filename but handy anyway
+ local authority, path, filename = s[2], s[3]
+ if authority == "" then
+ filename = path
+ else
+ filename = authority .. "/" .. path
+ end
+ s = {
scheme = s[1],
- authority = s[2],
- path = s[3],
+ authority = authority,
+ path = path,
query = s[4],
fragment = s[5],
original = str,
noscheme = false,
+ filename = filename,
}
end
+ return s
end
+-- Here we assume:
+--
+-- files: /// = relative
+-- files: //// = absolute (!)
+
-function url.hasscheme(str)
- return url.split(str)[1] ~= ""
-end
-function url.addscheme(str,scheme)
- return (url.hasscheme(str) and str) or ((scheme or "file:///") .. str)
+url.split = split
+url.hasscheme = hasscheme
+url.hashed = hashed
+
+function url.addscheme(str,scheme) -- no authority
+ if hasscheme(str) then
+ return str
+ elseif not scheme then
+ return "file:///" .. str
+ else
+ return scheme .. ":///" .. str
+ end
end
function url.construct(hash) -- dodo: we need to escape !
- local fullurl = { }
+ local fullurl, f = { }, 0
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 .. "://"
+ f = f + 1 ; fullurl[f] = scheme .. "://"
end
if authority and authority ~= "" then
- fullurl[#fullurl+1] = authority
+ f = f + 1 ; fullurl[f] = authority
end
if path and path ~= "" then
- fullurl[#fullurl+1] = "/" .. path
+ f = f + 1 ; fullurl[f] = "/" .. path
end
if query and query ~= "" then
- fullurl[#fullurl+1] = "?".. query
+ f = f + 1 ; fullurl[f] = "?".. query
end
if fragment and fragment ~= "" then
- fullurl[#fullurl+1] = "#".. fragment
+ f = f + 1 ; fullurl[f] = "#".. fragment
end
return lpegmatch(escaper,concat(fullurl))
end
function url.filename(filename)
- local t = url.hashed(filename)
+ local t = hashed(filename)
return (t.scheme == "file" and (gsub(t.path,"^/([a-zA-Z])([:|])/)","%1:"))) or filename
end
@@ -2820,6 +2879,7 @@ end
+
end -- of closure
do -- create closure to overcome 200 locals limit
@@ -2861,25 +2921,22 @@ end
-- optimizing for no find (*) does not save time
+
local function globpattern(path,patt,recurse,action)
- local ok, scanner
if path == "/" then
- ok, scanner = xpcall(function() return walkdir(path..".") end, function() end) -- kepler safe
- else
- ok, scanner = xpcall(function() return walkdir(path) end, function() end) -- kepler safe
+ path = path .. "."
+ elseif not find(path,"/$") then
+ path = path .. '/'
end
- if ok and type(scanner) == "function" then
- if not find(path,"/$") then path = path .. '/' end
- for name in scanner do
- local full = path .. name
- local mode = attributes(full,'mode')
- if mode == 'file' then
- if find(full,patt) then
- action(full)
- end
- elseif recurse and (mode == "directory") and (name ~= '.') and (name ~= "..") then
- globpattern(full,patt,recurse,action)
+ for name in walkdir(path) do
+ local full = path .. name
+ local mode = attributes(full,'mode')
+ if mode == 'file' then
+ if find(full,patt) then
+ action(full)
end
+ elseif recurse and (mode == "directory") and (name ~= '.') and (name ~= "..") then
+ globpattern(full,patt,recurse,action)
end
end
end
@@ -9363,10 +9420,10 @@ if not modules then modules = { } end modules ['data-exp'] = {
license = "see context related readme files",
}
-local format, gsub, find, gmatch, lower = string.format, string.gsub, string.find, string.gmatch, string.lower
+local format, find, gmatch, lower = string.format, 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 Ct, Cs, Cc, P, C, S = lpeg.Ct, lpeg.Cs, lpeg.Cc, lpeg.P, lpeg.C, lpeg.S
local type, next = type, next
local ostype = os.type
@@ -9381,7 +9438,7 @@ local resolvers = resolvers
-- 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.
+-- past this every time. See data-obs.lua for the gsub variant.
-- {a,b,c,d}
-- a,b,c/{p,q,r},d
@@ -9396,95 +9453,70 @@ local resolvers = resolvers
-- {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 function do_first(a,b)
+local function f_first(a,b)
local t, n = { }, 0
for s in gmatch(b,"[^,]+") do
- n = n + 1
- t[n] = a .. s
+ n = n + 1 ; t[n] = a .. s
end
- return "{" .. concat(t,",") .. "}"
+ return concat(t,",")
end
-local function do_second(a,b)
+local function f_second(a,b)
local t, n = { }, 0
for s in gmatch(a,"[^,]+") do
- n = n + 1
- t[n] = s .. b
+ n = n + 1 ; t[n] = s .. b
end
- return "{" .. concat(t,",") .. "}"
+ return concat(t,",")
end
-local function do_both(a,b)
+local function f_both(a,b)
local t, n = { }, 0
for sa in gmatch(a,"[^,]+") do
for sb in gmatch(b,"[^,]+") do
- n = n + 1
- t[n] = sa .. sb
+ n = n + 1 ; t[n] = sa .. sb
end
end
- return "{" .. concat(t,",") .. "}"
+ return concat(t,",")
end
-local function do_three(a,b,c)
- return a .. b.. c
-end
+local left = P("{")
+local right = P("}")
+local var = P((1 - S("{}" ))^0)
+local set = P((1 - S("{},"))^0)
+local other = P(1)
-local stripper_1 = lpeg.stripper("{}@")
+local l_first = Cs( ( Cc("{") * (C(set) * left * C(var) * right / f_first) * Cc("}") + other )^0 )
+local l_second = Cs( ( Cc("{") * (left * C(var) * right * C(set) / f_second) * Cc("}") + other )^0 )
+local l_both = Cs( ( Cc("{") * (left * C(var) * right * left * C(var) * right / f_both) * Cc("}") + other )^0 )
+local l_rest = Cs( ( left * var * (left/"") * var * (right/"") * var * right + other )^0 )
-local replacer_1 = lpeg.replacer {
- { ",}", ",@}" },
- { "{,", "{@," },
-}
+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
+local function splitpathexpr(str, newlist, validate) -- I couldn't resist lpegging it (nice exercise).
if trace_expansions then
report_resolvers("expanding variable '%s'",str)
end
local t, ok, done = newlist or { }, false, false
local n = #t
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
+ repeat local old = str
+ repeat local old = str ; str = lpegmatch(l_first, str) until old == str
+ repeat local old = str ; str = lpegmatch(l_second,str) until old == str
+ repeat local old = str ; str = lpegmatch(l_both, str) until old == str
+ repeat local old = str ; str = lpegmatch(l_rest, str) until old == str
+ until old == str -- or not find(str,"{")
str = lpegmatch(stripper_1,str)
if validate then
for s in gmatch(str,"[^,]+") do
s = validate(s)
if s then
- n = n + 1
- t[n] = s
+ n = n + 1 ; t[n] = s
end
end
else
for s in gmatch(str,"[^,]+") do
- n = n + 1
- t[n] = s
+ n = n + 1 ; t[n] = s
end
end
if trace_expansions then
@@ -9495,50 +9527,23 @@ local function splitpathexpr(str, newlist, validate)
return t
end
+-- We could make the previous one public.
+
local function validate(s)
- local isrecursive = find(s,"//$")
- s = collapsepath(s)
- if isrecursive then
- s = s .. "//"
- end
- return s ~= "" and not find(s,dummy_path_expr) and s
+ s = collapsepath(s) -- already keeps the //
+ return s ~= "" and not find(s,"^!*unset/*$") and s
end
resolvers.validatedpath = validate -- keeps the trailing //
-function resolvers.expandedpathfromlist(pathlist) -- maybe not a list, just a path
- -- a previous version fed back into pathlist
- local newlist, ok = { }, false
+function resolvers.expandedpathfromlist(pathlist)
+ local newlist = { }
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
- local n = 0
- for k=1,#pathlist do
- for p in gmatch(pathlist[k],"([^,]+)") do
- p = validate(p)
- if p ~= "" then
- n = n + 1
- newlist[n] = p
- end
- end
- end
+ splitpathexpr(pathlist[k],newlist,validate)
end
return newlist
end
--- We also put some cleanup code here.
-
-
-
-
local cleanup = lpeg.replacer {
{ "!" , "" },
{ "\\" , "/" },
@@ -9576,14 +9581,13 @@ end
-- This one strips quotes and funny tokens.
+local expandhome = P("~") / "$HOME" -- environment.homedir
-local expandhome = lpegP("~") / "$HOME" -- environment.homedir
+local dodouble = P('"')/"" * (expandhome + (1 - P('"')))^0 * P('"')/""
+local dosingle = P("'")/"" * (expandhome + (1 - P("'")))^0 * P("'")/""
+local dostring = (expandhome + 1 )^0
-local dodouble = lpegP('"')/"" * (expandhome + (1 - lpegP('"')))^0 * lpegP('"')/""
-local dosingle = lpegP("'")/"" * (expandhome + (1 - lpegP("'")))^0 * lpegP("'")/""
-local dostring = (expandhome + 1 )^0
-
-local stripper = lpegCs(
+local stripper = Cs(
lpegpatterns.unspacer * (dosingle + dodouble + dostring) * lpegpatterns.unspacer
)
@@ -9599,7 +9603,9 @@ end
local cache = { }
-local splitter = lpegCt(lpeg.splitat(lpegS(ostype == "windows" and ";" or ":;"))) -- maybe add ,
+local splitter = Ct(lpeg.splitat(S(ostype == "windows" and ";" or ":;"))) -- maybe add ,
+
+local backslashswapper = lpeg.replacer("\\","/")
local function splitconfigurationpath(str) -- beware, this can be either a path or a { specification }
if str then
@@ -9608,8 +9614,7 @@ local function splitconfigurationpath(str) -- beware, this can be either a path
if str == "" then
found = { }
else
- str = gsub(str,"\\","/")
- local split = lpegmatch(splitter,str)
+ local split = lpegmatch(splitter,lpegmatch(backslashswapper,str)) -- can be combined
found = { }
local noffound = 0
for i=1,#split do
@@ -9658,57 +9663,62 @@ end
-local weird = lpegP(".")^1 + lpeg.anywhere(lpegS("~`!#$%^&*()={}[]:;\"\'||<>,?\n\r\t"))
+local weird = P(".")^1 + lpeg.anywhere(S("~`!#$%^&*()={}[]:;\"\'||<>,?\n\r\t"))
-function resolvers.scanfiles(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
+local attributes, directory = lfs.attributes, lfs.dir
+
+local function scan(files,spec,path,n,m,r)
+ local full = (path == "" and spec) or (spec .. path .. '/')
+ local dirs, nofdirs = { }, 0
+ 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
- dirs[#dirs+1] = name
+ 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
+ nofdirs = nofdirs + 1
+ if path ~= "" then
+ dirs[nofdirs] = path..'/'..name
+ else
+ dirs[nofdirs] = name
+ end
end
end
- if #dirs > 0 then
- sort(dirs)
- for i=1,#dirs do
- scan(spec,dirs[i])
- end
+ end
+ if nofdirs > 0 then
+ sort(dirs)
+ for i=1,nofdirs do
+ files, n, m, r = scan(files,spec,dirs[i],n,m,r)
end
end
- scan(specification .. '/',"")
- files.__files__, files.__directories__, files.__remappings__ = n, m, r
+ return files, n, m, r
+end
+
+function resolvers.scanfiles(path)
+ if trace_locating then
+ report_resolvers("scanning path '%s'",path)
+ end
+ local files, n, m, r = scan({ },path .. '/',"",0,0,0)
+ files.__path__ = path
+ files.__files__ = n
+ files.__directories__ = m
+ files.__remappings__ = r
if trace_locating then
report_resolvers("%s files found on %s directories with %s uppercase remappings",n,m,r)
end
@@ -10399,9 +10409,15 @@ if not modules then modules = { } end modules ['data-met'] = {
license = "see context related readme files"
}
-local find = string.find
+local find, format = string.find, string.format
+local sequenced = table.sequenced
+local addurlscheme, urlhashed = url.addscheme, url.hashed
+
+local trace_locating = false
+
+trackers.register("resolvers.locating", function(v) trace_methods = v end)
+trackers.register("resolvers.methods", function(v) trace_methods = v end)
-local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
local report_resolvers = logs.new("resolvers")
@@ -10409,41 +10425,109 @@ local allocate = utilities.storage.allocate
local resolvers = resolvers
-resolvers.concatinators = allocate ()
-resolvers.locators = allocate { notfound = { nil } } -- locate databases
-resolvers.hashers = allocate { notfound = { nil } } -- load databases
-resolvers.generators = allocate { notfound = { nil } } -- generate databases
+local registered = { }
-function resolvers.splitmethod(filename) -- todo: trigger by suffix
+local function splitmethod(filename) -- todo: filetype in specification
if not filename then
- return { } -- safeguard
- elseif type(filename) == "table" then
+ return { scheme = "unknown", original = filename }
+ end
+ if type(filename) == "table" then
return filename -- already split
- elseif not find(filename,"://") then
- return { scheme="file", path = filename, original = filename } -- quick hack
+ end
+ filename = file.collapsepath(filename)
+ if not find(filename,"://") then
+ return { scheme = "file", path = filename, original = filename, filename = filename }
+ end
+ local specification = url.hashed(filename)
+ if not specification.scheme or specification.scheme == "" then
+ return { scheme = "file", path = filename, original = filename, filename = filename }
else
- return url.hashed(filename)
+ return specification
end
end
-function resolvers.methodhandler(what, filename, filetype) -- ...
- filename = file.collapsepath(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("using special handler for '%s' -> '%s' -> '%s'",specification.original,what,table.sequenced(specification))
+resolvers.splitmethod = splitmethod -- bad name but ok
+
+-- the second argument is always analyzed (saves time later on) and the original
+-- gets passed as original but also as argument
+
+local function methodhandler(what,first,...) -- filename can be nil or false
+ local method = registered[what]
+ if method then
+ local how, namespace = method.how, method.namespace
+ if how == "uri" or how == "url" then
+ local specification = splitmethod(first)
+ local scheme = specification.scheme
+ local resolver = namespace and namespace[scheme]
+ if resolver then
+ if trace_methods then
+ report_resolvers("resolver: method=%s, how=%s, scheme=%s, argument=%s",what,how,scheme,first)
+ end
+ return resolver(specification,...)
+ else
+ resolver = namespace.default or namespace.file
+ if resolver then
+ if trace_methods then
+ report_resolvers("resolver: method=%s, how=%s, default, argument=%s",what,how,first)
+ end
+ return resolver(specification,...)
+ elseif trace_methods then
+ report_resolvers("resolver: method=%s, how=%s, no handler",what,how)
+ end
+ end
+ elseif how == "tag" then
+ local resolver = namespace and namespace[first]
+ if resolver then
+ if trace_methods then
+ report_resolvers("resolver: method=%s, how=%s, tag=%s",what,how,first)
+ end
+ return resolver(...)
+ else
+ resolver = namespace.default or namespace.file
+ if resolver then
+ if trace_methods then
+ report_resolvers("resolver: method=%s, how=%s, default",what,how)
+ end
+ return resolver(...)
+ elseif trace_methods then
+ report_resolvers("resolver: method=%s, how=%s, unknown",what,how)
+ end
+ end
end
- return resolver[scheme](filename,filetype,specification) -- todo: query
else
- if trace_locating then
- report_resolvers("no handler for '%s' -> '%s' -> '%s'",specification.original,what,table.sequenced(specification))
+ report_resolvers("resolver: method=%s, unknown",what)
+ end
+end
+
+resolvers.methodhandler = methodhandler
+
+function resolvers.registermethod(name,namespace,how)
+ registered[name] = { how = how or "tag", namespace = namespace }
+ namespace["byscheme"] = function(scheme,filename,...)
+ if scheme == "file" then
+ return methodhandler(name,filename,...)
+ else
+ return methodhandler(name,addurlscheme(filename,scheme),...)
end
- return resolver.tex(filename,filetype) -- todo: specification
end
end
+local concatinators = allocate { notfound = file.join } -- concatinate paths
+local locators = allocate { notfound = function() end } -- locate databases
+local hashers = allocate { notfound = function() end } -- load databases
+local generators = allocate { notfound = function() end } -- generate databases
+
+resolvers.concatinators = concatinators
+resolvers.locators = locators
+resolvers.hashers = hashers
+resolvers.generators = generators
+
+local registermethod = resolvers.registermethod
+
+registermethod("concatinators",concatinators,"tag")
+registermethod("locators", locators, "uri")
+registermethod("hashers", hashers, "uri")
+registermethod("generators", generators, "uri")
end -- of closure
@@ -10471,11 +10555,11 @@ local concat, insert, sortedkeys = table.concat, table.insert, table.sortedkeys
local next, type = next, type
local os = os
-local lpegP, lpegS, lpegR, lpegC, lpegCc, lpegCs, lpegCt = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.Cc, lpeg.Cs, lpeg.Ct
+local P, S, R, C, Cc, Cs, Ct, Carg = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.Cc, lpeg.Cs, lpeg.Ct, lpeg.Carg
local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns
local filedirname, filebasename, fileextname, filejoin = file.dirname, file.basename, file.extname, file.join
-local collapsepath = file.collapsepath
+local collapsepath, joinpath = file.collapsepath, file.joinpath
local allocate = utilities.storage.allocate
local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
@@ -10489,6 +10573,7 @@ local resolvers = resolvers
local expandedpathfromlist = resolvers.expandedpathfromlist
local checkedvariable = resolvers.checkedvariable
local splitconfigurationpath = resolvers.splitconfigurationpath
+local methodhandler = resolvers.methodhandler
local initializesetter = utilities.setters.initialize
@@ -10502,12 +10587,12 @@ resolvers.luacnfspec = '{$SELFAUTODIR,$SELFAUTOPARENT}{,{/share,}/texmf{-local
resolvers.luacnfname = 'texmfcnf.lua'
resolvers.luacnfstate = "unknown"
-local unset_variable = "unset"
+local unset_variable = "unset"
-local formats = resolvers.formats
-local suffixes = resolvers.suffixes
-local dangerous = resolvers.dangerous
-local suffixmap = resolvers.suffixmap
+local formats = resolvers.formats
+local suffixes = resolvers.suffixes
+local dangerous = resolvers.dangerous
+local suffixmap = resolvers.suffixmap
resolvers.defaultsuffixes = { "tex" } -- "mkiv", "cld" -- too tricky
@@ -10552,7 +10637,7 @@ function resolvers.newinstance()
end
-function resolvers.setinstance(someinstance)
+function resolvers.setinstance(someinstance) -- only one instance is active
instance = someinstance
resolvers.instance = someinstance
return someinstance
@@ -10574,7 +10659,7 @@ function resolvers.setenv(key,value)
end
end
-function resolvers.getenv(key)
+local function getenv(key)
local value = instance.environment[key]
if value and value ~= "" then
return value
@@ -10584,23 +10669,55 @@ function resolvers.getenv(key)
end
end
-resolvers.env = resolvers.getenv
+resolvers.getenv = getenv
+resolvers.env = getenv
+
+local function resolve(key)
+ local value = instance.variables[key] or ""
+ return (value ~= "" and value) or getenv(key) or ""
+end
+
+local dollarstripper = lpeg.stripper("$")
+local inhibitstripper = P("!")^0 * Cs(P(1)^0)
+local backslashswapper = lpeg.replacer("\\","/")
+
+local somevariable = P("$") / ""
+local somekey = C(R("az","AZ","09","__","--")^1)
+local somethingelse = P(";") * ((1-S("!{}/\\"))^1 * P(";") / "")
+ + P(";") * (P(";") / "")
+ + P(1)
+
+local pattern = Cs( (somevariable * (somekey/resolve) + somethingelse)^1 )
local function expandvars(lst) -- simple vars
- local variables, getenv = instance.variables, resolvers.getenv
- local function resolve(a)
- local va = variables[a] or ""
- return (va ~= "" and va) or getenv(a) or ""
- end
for k=1,#lst do
- local var = lst[k]
- var = gsub(var,"%$([%a%d%_%-]+)",resolve)
- var = gsub(var,";+",";")
- var = gsub(var,";[!{}/\\]+;",";")
- lst[k] = var
+ local lk = lst[k]
+ lst[k] = lpegmatch(pattern,lk) or lk
+ end
+end
+
+
+local slash = P("/")
+
+local pattern = Cs (
+ Cc("^") * (
+ Cc("%") * S(".-")
+ + slash^2 * P(-1) / "/.*"
+ + slash^2 / "/.-/"
+ + (1-slash) * P(-1) * Cc("/")
+ + P(1)
+ )^1 * Cc("$")
+)
+
+local function makepathexpression(str)
+ if str == "." then
+ return "^%./$"
+ else
+ return lpegmatch(pattern,str)
end
end
+
local function resolve(key)
local value = instance.variables[key]
if value and value ~= "" then
@@ -10614,22 +10731,21 @@ local function resolve(key)
return e ~= nil and e ~= "" and checkedvariable(e) or ""
end
+local pattern = Cs( (somevariable * (somekey/resolve) + somethingelse)^1 )
+
local function expandedvariable(var) -- simple vars
- var = gsub(var,"%$([%a%d%_%-]+)",resolve)
- var = gsub(var,";+",";")
- var = gsub(var,";[!{}/\\]+;",";")
- return var
+ return lpegmatch(pattern,var) or var
end
+
local function entry(entries,name)
if name and name ~= "" then
- name = gsub(name,'%$','')
- -- local result = entries[name..'.'..instance.progname] or entries[name]
+ name = lpegmatch(dollarstripper,name)
local result = entries[instance.progname .. '.' .. name] or entries[name]
if result then
return result
else
- result = resolvers.getenv(name)
+ result = getenv(name)
if result then
instance.variables[name] = result
resolvers.expandvariables()
@@ -10642,8 +10758,7 @@ end
local function is_entry(entries,name)
if name and name ~= "" then
- name = gsub(name,'%$','')
- -- return (entries[name..'.'..instance.progname] or entries[name]) ~= nil
+ name = lpegmatch(dollarstripper,name)
return (entries[instance.progname .. '.' .. name] or entries[name]) ~= nil
else
return false
@@ -10654,7 +10769,7 @@ local function reportcriticalvariables()
if trace_locating then
for i=1,#resolvers.criticalvars do
local v = resolvers.criticalvars[i]
- report_resolvers("variable '%s' set to '%s'",v,resolvers.getenv(v) or "unknown")
+ report_resolvers("variable '%s' set to '%s'",v,getenv(v) or "unknown")
end
report_resolvers()
end
@@ -10664,7 +10779,7 @@ end
local function identify_configuration_files()
local specification = instance.specification
if #specification == 0 then
- local cnfspec = resolvers.getenv('TEXMFCNF')
+ local cnfspec = getenv('TEXMFCNF')
if cnfspec == "" then
cnfspec = resolvers.luacnfspec
resolvers.luacnfstate = "default"
@@ -10736,7 +10851,6 @@ local function load_configuration_files()
end
end
setups[pathname] = t
-
if resolvers.luacnfstate == "default" then
-- the following code is not tested
local cnfspec = t["TEXMFCNF"]
@@ -10798,63 +10912,30 @@ end
-- database loading
--- locators
-
-function resolvers.locatedatabase(specification)
- return resolvers.methodhandler('locators', specification)
-end
-
-function resolvers.locators.tex(specification)
- if specification and specification ~= '' and lfs.isdir(specification) then
- if trace_locating then
- report_resolvers("tex locator '%s' found",specification)
- end
- resolvers.appendhash('file',specification,filename,true) -- cache
- elseif trace_locating then
- report_resolvers("tex locator '%s' not found",specification)
- end
-end
-
--- hashers
-
-function resolvers.hashdatabase(tag,name)
- return resolvers.methodhandler('hashers',tag,name)
-end
-
local function load_file_databases()
instance.loaderror, instance.files = false, allocate()
if not instance.renewcache then
local hashes = instance.hashes
for k=1,#hashes do
local hash = hashes[k]
- resolvers.hashdatabase(hash.tag,hash.name)
+ resolvers.hashers.byscheme(hash.type,hash.name)
if instance.loaderror then break end
end
end
end
-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
-
local function locate_file_databases()
-- todo: cache:// and tree:// (runtime)
local texmfpaths = resolvers.expandedpathlist('TEXMF')
for i=1,#texmfpaths do
local path = collapsepath(texmfpaths[i])
- local stripped = gsub(path,"^!!","")
- local runtime = stripped == path
- path = resolvers.cleanpath(path)
+ local stripped = lpegmatch(inhibitstripper,path)
if stripped ~= "" then
+ local runtime = stripped == path
+ path = resolvers.cleanpath(path)
if lfs.isdir(path) then
local spec = resolvers.splitmethod(stripped)
- if spec.scheme == "cache" then
+ if spec.scheme == "cache" or spec.scheme == "file" then
stripped = spec.path
elseif runtime and (spec.noscheme or spec.scheme == "file") then
stripped = "tree:///" .. stripped
@@ -10866,7 +10947,7 @@ local function locate_file_databases()
report_resolvers("locating list of '%s' (cached)",path)
end
end
- resolvers.locatedatabase(stripped) -- nothing done with result
+ methodhandler('locators',stripped) -- nothing done with result
else
if trace_locating then
if runtime then
@@ -10885,8 +10966,9 @@ end
local function generate_file_databases()
local hashes = instance.hashes
- for i=1,#hashes do
- resolvers.methodhandler('generators',hashes[i].tag)
+ for k=1,#hashes do
+ local hash = hashes[k]
+ methodhandler('generators',hash.name)
end
if trace_locating then
report_resolvers()
@@ -10896,10 +10978,13 @@ end
local function save_file_databases() -- will become cachers
for i=1,#instance.hashes do
local hash = instance.hashes[i]
- local cachename = hash.tag
+ local cachename = hash.name
if hash.cache then
local content = instance.files[cachename]
caches.collapsecontent(content)
+ if trace_locating then
+ report_resolvers("saving tree '%s'",cachename)
+ end
caches.savecontent(cachename,"files",content)
elseif trace_locating then
report_resolvers("not saving runtime tree '%s'",cachename)
@@ -10923,23 +11008,22 @@ local function load_databases()
end
end
-function resolvers.appendhash(type,tag,name,cache)
+function resolvers.appendhash(type,name,cache)
if trace_locating then
- report_resolvers("hash '%s' appended",tag)
+ report_resolvers("hash '%s' appended",name)
end
- insert(instance.hashes, { type = type, tag = tag, name = name, cache = cache } )
+ insert(instance.hashes, { type = type, name = name, cache = cache } )
end
-function resolvers.prependhash(type,tag,name,cache)
+function resolvers.prependhash(type,name,cache)
if trace_locating then
- report_resolvers("hash '%s' prepended",tag)
+ report_resolvers("hash '%s' prepended",name)
end
- insert(instance.hashes, 1, { type = type, tag = tag, name = name, cache = cache } )
+ insert(instance.hashes, 1, { type = type, name = name, cache = cache } )
end
function resolvers.extendtexmfvariable(specification) -- crap, we could better prepend the hash
--- local t = resolvers.expandedpathlist('TEXMF') -- full expansion
- local t = resolvers.splitpath(resolvers.getenv('TEXMF'))
+ local t = resolvers.splitpath(getenv('TEXMF'))
insert(t,1,specification)
local newspec = concat(t,";")
if instance.environment["TEXMF"] then
@@ -10953,10 +11037,6 @@ function resolvers.extendtexmfvariable(specification) -- crap, we could better p
reset_hashes()
end
-function resolvers.generators.tex(specification,tag)
- instance.files[tag or specification] = resolvers.scanfiles(specification)
-end
-
function resolvers.splitexpansions()
local ie = instance.expansions
for k,v in next, ie do
@@ -10986,9 +11066,20 @@ function resolvers.datastate()
return caches.contentstate()
end
+local function resolve(a)
+ return instance.expansions[a] or getenv(a)
+end
+
+local cleaner = P("\\") / "/" + P(";") * S("!{}/\\")^0 * P(";")^1 / ";"
+
+local variable = R("az","AZ","09","__","--")^1 / resolve
+ variable = (P("$")/"") * (variable + (P("{")/"") * variable * (P("}")/""))
+
+ cleaner = Cs((cleaner + P(1))^0)
+ variable = Cs((variable + P(1))^0)
+
function resolvers.expandvariables()
local expansions, environment, variables = allocate(), instance.environment, instance.variables
- local getenv = resolvers.getenv
instance.expansions = expansions
local engine, progname = instance.engine, instance.progname
if type(engine) ~= "string" then instance.engine, engine = "", "" end
@@ -10996,12 +11087,7 @@ function resolvers.expandvariables()
if engine ~= "" then environment['engine'] = engine end
if progname ~= "" then environment['progname'] = progname end
for k,v in next, environment do
- -- local a, b = match(k,"^(%a+)%_(.*)%s*$") -- too many vars have an _ in the name
- -- if a and b then -- so let's forget about it; it was a
- -- expansions[a..'.'..b] = v -- hack anyway for linux and not needed
- -- else -- anymore as we now have directives
- expansions[k] = v
- -- end
+ expansions[k] = v
end
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
@@ -11009,26 +11095,19 @@ function resolvers.expandvariables()
for k,v in next, variables do -- move variables to expansions
if not expansions[k] then expansions[k] = v end
end
- local busy = false
- local function resolve(a)
- busy = true
- return expansions[a] or getenv(a)
- end
- while true do
- busy = false
+ repeat
+ local busy = false
for k,v in next, expansions do
- 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
+ local s = lpegmatch(variable,v)
+ if s ~= v then
+ busy = true
+ expansions[k] = s
end
end
- if not busy then break end
- end
+ until not busy
+
for k,v in next, expansions do
- expansions[k] = gsub(v,"\\", '/')
+ expansions[k] = lpegmatch(cleaner,v)
end
end
@@ -11055,7 +11134,7 @@ function resolvers.unexpandedpathlist(str)
end
function resolvers.unexpandedpath(str)
- return file.joinpath(resolvers.unexpandedpathlist(str))
+ return joinpath(resolvers.unexpandedpathlist(str))
end
local done = { }
@@ -11169,7 +11248,7 @@ function resolvers.cleanpathlist(str)
end
function resolvers.expandpath(str)
- return file.joinpath(resolvers.expandedpathlist(str))
+ return joinpath(resolvers.expandedpathlist(str))
end
function resolvers.expandedpathlist(str)
@@ -11177,7 +11256,7 @@ function resolvers.expandedpathlist(str)
return ep or { } -- ep ?
elseif instance.savelists then
-- engine+progname hash
- str = gsub(str,"%$","")
+ str = lpegmatch(dollarstripper,str)
if not instance.lists[str] then -- cached
local lst = made_list(instance,resolvers.splitpath(resolvers.expansion(str)))
instance.lists[str] = expandedpathfromlist(lst)
@@ -11190,28 +11269,34 @@ function resolvers.expandedpathlist(str)
end
function resolvers.expandedpathlistfromvariable(str) -- brrr
- local tmp = resolvers.variableofformatorsuffix(gsub(str,"%$",""))
- if tmp ~= "" then
- return resolvers.expandedpathlist(tmp)
- else
- return resolvers.expandedpathlist(str)
- end
+ str = lpegmatch(dollarstripper,str)
+ local tmp = resolvers.variableofformatorsuffix(str)
+ return resolvers.expandedpathlist(tmp ~= "" and tmp or str)
end
function resolvers.expandpathfromvariable(str)
- return file.joinpath(resolvers.expandedpathlistfromvariable(str))
+ return joinpath(resolvers.expandedpathlistfromvariable(str))
end
function resolvers.expandbraces(str) -- output variable and brace expansion of STRING
local ori = resolvers.variable(str)
local pth = expandedpathfromlist(resolvers.splitpath(ori))
- return file.joinpath(pth)
+ return joinpath(pth)
end
-resolvers.isreadable = { }
+function resolvers.registerfilehash(name,content,someerror)
+ if content then
+ instance.files[name] = content
+ else
+ instance.files[name] = { }
+ if somerror == true then -- can be unset
+ instance.loaderror = someerror
+ end
+ end
+end
-function resolvers.isreadable.file(name)
- local readable = lfs.isfile(name) -- brrr
+function isreadable(name)
+ local readable = file.is_readable(name)
if trace_detail then
if readable then
report_resolvers("file '%s' is readable",name)
@@ -11222,8 +11307,6 @@ function resolvers.isreadable.file(name)
return readable
end
-resolvers.isreadable.tex = resolvers.isreadable.file
-
-- name
-- name/name
@@ -11244,7 +11327,7 @@ local function collect_files(names)
local hashes = instance.hashes
for h=1,#hashes do
local hash = hashes[h]
- local blobpath = hash.tag
+ local blobpath = hash.name
local files = blobpath and instance.files[blobpath]
if files then
if trace_detail then
@@ -11265,7 +11348,7 @@ local function collect_files(names)
if not dname or find(blobfile,dname) then
local kind = hash.type
local search = filejoin(blobpath,blobfile,bname)
- local result = resolvers.concatinators[hash.type](blobroot,blobfile,bname)
+ local result = methodhandler('concatinators',hash.type,blobroot,blobfile,bname)
if trace_detail then
report_resolvers("match: kind '%s', search '%s', result '%s'",kind,search,result)
end
@@ -11278,7 +11361,7 @@ local function collect_files(names)
if not dname or find(vv,dname) then
local kind = hash.type
local search = filejoin(blobpath,vv,bname)
- local result = resolvers.concatinators[hash.type](blobroot,vv,bname)
+ local result = methodhandler('concatinators',hash.type,blobroot,vv,bname)
if trace_detail then
report_resolvers("match: kind '%s', search '%s', result '%s'",kind,search,result)
end
@@ -11316,6 +11399,8 @@ local function can_be_dir(name) -- can become local
return fakepaths[name] == 1
end
+local preparetreepattern = Cs((P(".")/"%%." + P("-")/"%%-" + P(1))^0 * Cc("$"))
+
local function collect_instance_files(filename,askedformat,allresults) -- todo : plugin (scanners, checkers etc)
local result = { }
local stamp = nil
@@ -11333,7 +11418,7 @@ local function collect_instance_files(filename,askedformat,allresults) -- todo :
end
end
if not dangerous[askedformat] then
- if resolvers.isreadable.file(filename) then
+ if isreadable(filename) then
if trace_detail then
report_resolvers("file '%s' found directly",filename)
end
@@ -11349,7 +11434,7 @@ local function collect_instance_files(filename,askedformat,allresults) -- todo :
end
result = resolvers.findwildcardfiles(filename) -- we can use th elocal
elseif file.is_qualified_path(filename) then
- if resolvers.isreadable.file(filename) then
+ if isreadable(filename) then
if trace_locating then
report_resolvers("qualified name '%s'", filename)
end
@@ -11362,7 +11447,7 @@ local function collect_instance_files(filename,askedformat,allresults) -- todo :
for i=1,#format_suffixes do
local s = format_suffixes[i]
forcedname = filename .. "." .. s
- if resolvers.isreadable.file(forcedname) then
+ if isreadable(forcedname) then
if trace_locating then
report_resolvers("no suffix, forcing format filetype '%s'", s)
end
@@ -11376,7 +11461,7 @@ local function collect_instance_files(filename,askedformat,allresults) -- todo :
-- try to find in tree (no suffix manipulation), here we search for the
-- matching last part of the name
local basename = filebasename(filename)
- local pattern = gsub(filename .. "$","([%.%-])","%%%1")
+ local pattern = lpegmatch(preparetreepattern,filename)
-- messy .. to be sorted out
local savedformat = askedformat
local format = savedformat or ""
@@ -11471,7 +11556,7 @@ local function collect_instance_files(filename,askedformat,allresults) -- todo :
end
for k=1,#wantedfiles do
local fname = wantedfiles[k]
- if fname and resolvers.isreadable.file(fname) then
+ if fname and isreadable(fname) then
filename, done = fname, true
result[#result+1] = filejoin('.',fname)
break
@@ -11497,26 +11582,15 @@ local function collect_instance_files(filename,askedformat,allresults) -- todo :
if trace_detail then
report_resolvers("checking filename '%s'",filename)
end
- -- a bit messy ... esp the doscan setting here
- local doscan
for k=1,#pathlist do
local path = pathlist[k]
- if find(path,"^!!") then doscan = false else doscan = true end
- local pathname = gsub(path,"^!+", '')
+ local pathname = lpegmatch(inhibitstripper,path)
+ local doscan = path == pathname -- no ^!!
done = false
-- using file list
if filelist then
- local expression
-- compare list entries with permitted pattern -- /xx /xx//
- if not find(pathname,"/$") then
- expression = pathname .. "/"
- else
- expression = pathname
- end
- expression = gsub(expression,"([%-%.])","%%%1") -- this also influences
- expression = gsub(expression,"//+$", '/.*') -- later usage of pathname
- expression = gsub(expression,"//", '/.-/') -- not ok for /// but harmless
- expression = "^" .. expression .. "$"
+ local expression = makepathexpression(pathname)
if trace_detail then
report_resolvers("using pattern '%s' for path '%s'",expression,pathname)
end
@@ -11545,7 +11619,8 @@ local function collect_instance_files(filename,askedformat,allresults) -- todo :
end
if not done and doscan then
-- check if on disk / unchecked / does not work at all / also zips
- if resolvers.splitmethod(pathname).scheme == 'file' then -- ?
+ local scheme = url.hasscheme(pathname)
+ if not scheme or scheme == "file" then
local pname = gsub(pathname,"%.%*$",'')
if not find(pname,"%*") then
local ppname = gsub(pname,"/+$","")
@@ -11553,7 +11628,7 @@ local function collect_instance_files(filename,askedformat,allresults) -- todo :
for k=1,#wantedfiles do
local w = wantedfiles[k]
local fname = filejoin(ppname,w)
- if resolvers.isreadable.file(fname) then
+ if isreadable(fname) then
if trace_detail then
report_resolvers("found '%s' by scanning",fname)
end
@@ -11586,9 +11661,6 @@ local function collect_instance_files(filename,askedformat,allresults) -- todo :
return result
end
-resolvers.concatinators.tex = filejoin
-resolvers.concatinators.file = resolvers.concatinators.tex
-
local function findfiles(filename,filetype,allresults)
local result = collect_instance_files(filename,filetype or "",allresults)
if #result == 0 then
@@ -11609,7 +11681,7 @@ function resolvers.findfile(filename,filetype)
end
function resolvers.findpath(filename,filetype)
- return file.dirname(findfiles(filename,filetype,false)[1] or "")
+ return filedirname(findfiles(filename,filetype,false)[1] or "")
end
local function findgivenfiles(filename,allresults)
@@ -11617,7 +11689,7 @@ local function findgivenfiles(filename,allresults)
local hashes = instance.hashes
for k=1,#hashes do
local hash = hashes[k]
- local files = instance.files[hash.tag] or { }
+ local files = instance.files[hash.name] or { }
local blist = files[bname]
if not blist then
local rname = "remap:"..bname
@@ -11629,12 +11701,12 @@ local function findgivenfiles(filename,allresults)
end
if blist then
if type(blist) == 'string' then
- result[#result+1] = resolvers.concatinators[hash.type](hash.tag,blist,bname) or ""
+ result[#result+1] = methodhandler('concatinators',hash.type,hash.name,blist,bname) or ""
if not allresults then break end
else
for kk=1,#blist do
local vv = blist[kk]
- result[#result+1] = resolvers.concatinators[hash.type](hash.tag,vv,bname) or ""
+ result[#result+1] = methodhandler('concatinators',hash.type,hash.name,vv,bname) or ""
if not allresults then break end
end
end
@@ -11657,14 +11729,14 @@ local function doit(path,blist,bname,tag,kind,result,allresults)
if type(blist) == 'string' then
-- make function and share code
if find(lower(blist),path) then
- result[#result+1] = resolvers.concatinators[kind](tag,blist,bname) or ""
+ result[#result+1] = methodhandler('concatinators',kind,tag,blist,bname) or ""
done = true
end
else
for kk=1,#blist do
local vv = blist[kk]
if find(lower(vv),path) then
- result[#result+1] = resolvers.concatinators[kind](tag,vv,bname) or ""
+ result[#result+1] = methodhandler('concatinators',kind,tag,vv,bname) or ""
done = true
if not allresults then break end
end
@@ -11674,30 +11746,25 @@ local function doit(path,blist,bname,tag,kind,result,allresults)
return done
end
+local makewildcard = Cs(
+ (P("^")^0 * P("/") * P(-1) + P(-1)) /".*"
+ + (P("^")^0 * P("/") / "") * (P("*")/".*" + P("-")/"%%-" + P("?")/"."+ P("\\")/"/" + P(1))^0
+)
+
local function findwildcardfiles(filename,allresults) -- todo: remap: and lpeg
local result = { }
- local bname, dname = filebasename(filename), filedirname(filename)
- local path = gsub(dname,"^*/","")
- path = gsub(path,"*",".*")
- path = gsub(path,"-","%%-")
- if dname == "" then
- path = ".*"
- end
- local name = bname
- name = gsub(name,"*",".*")
- name = gsub(name,"-","%%-")
- path = lower(path)
- name = lower(name)
+ local path = lower(lpegmatch(makewildcard,filedirname (filename)))
+ local name = lower(lpegmatch(makewildcard,filebasename(filename)))
local files, done = instance.files, false
if find(name,"%*") then
local hashes = instance.hashes
for k=1,#hashes do
local hash = hashes[k]
- local tag, kind = hash.tag, hash.type
- for kk, hh in next, files[hash.tag] do
+ local hashname, hashtype = hash.name, hash.type
+ for kk, hh in next, files[hashname] do
if not find(kk,"^remap:") then
if find(lower(kk),name) then
- if doit(path,hh,kk,tag,kind,result,allresults) then done = true end
+ if doit(path,hh,kk,hashname,hashtype,result,allresults) then done = true end
if done and not allresults then break end
end
end
@@ -11707,8 +11774,8 @@ local function findwildcardfiles(filename,allresults) -- todo: remap: and lpeg
local hashes = instance.hashes
for k=1,#hashes do
local hash = hashes[k]
- local tag, kind = hash.tag, hash.type
- if doit(path,files[tag][bname],bname,tag,kind,result,allresults) then done = true end
+ local hashname, hashtype = hash.name, hash.type
+ if doit(path,files[hashname][bname],bname,hashname,hashtype,result,allresults) then done = true end
if done and not allresults then break end
end
end
@@ -11779,12 +11846,9 @@ end
-- resolvers.expandvar = resolvers.expansion -- output variable expansion of STRING.
function resolvers.showpath(str) -- output search path for file type NAME
- return file.joinpath(resolvers.expandedpathlist(resolvers.formatofvariable(str)))
+ return joinpath(resolvers.expandedpathlist(resolvers.formatofvariable(str)))
end
--- resolvers.findfile(filename)
--- resolvers.findfile(filename, f.iletype)
-
function resolvers.registerfile(files, name, path)
if files[name] then
if type(files[name]) == 'string' then
@@ -11809,7 +11873,7 @@ function resolvers.dowithvariable(name,func)
end
function resolvers.locateformat(name)
- local barename = gsub(name,"%.%a+$","")
+ local barename = file.removesuffix(name) -- gsub(name,"%.%a+$","")
local fmtname = caches.getfirstreadablefile(barename..".fmt","formats") or ""
if fmtname == "" then
fmtname = resolvers.findfile(barename..".fmt")
@@ -11845,7 +11909,7 @@ function resolvers.dowithfilesintree(pattern,handle,before,after) -- can be a ni
for i=1,#hashes do
local hash = hashes[i]
local blobtype = hash.type
- local blobpath = hash.tag
+ local blobpath = hash.name
if blobpath then
if before then
before(blobtype,blobpath,pattern)
@@ -12020,13 +12084,23 @@ if not modules then modules = { } end modules ['data-inp'] = {
license = "see context related readme files"
}
-local allocate = utilities.storage.allocate
-
+local allocate = utilities.storage.allocate
local resolvers = resolvers
-resolvers.finders = allocate { notfound = { nil } }
-resolvers.openers = allocate { notfound = { nil } }
-resolvers.loaders = allocate { notfound = { false, nil, 0 } }
+local methodhandler = resolvers.methodhandler
+local registermethod = resolvers.registermethod
+
+local finders = allocate { helpers = { }, notfound = function() end }
+local openers = allocate { helpers = { }, notfound = function() end }
+local loaders = allocate { helpers = { }, notfound = function() return false, nil, 0 end }
+
+registermethod("finders", finders, "uri")
+registermethod("openers", openers, "uri")
+registermethod("loaders", loaders, "uri")
+
+resolvers.finders = finders
+resolvers.openers = openers
+resolvers.loaders = loaders
end -- of closure
@@ -12041,8 +12115,134 @@ if not modules then modules = { } end modules ['data-out'] = {
license = "see context related readme files"
}
-resolvers.savers = utilities.storage.allocate { }
+local allocate = utilities.storage.allocate
+local resolvers = resolvers
+
+local registermethod = resolvers.registermethod
+
+local savers = allocate { helpers = { } }
+resolvers.savers = savers
+
+registermethod("savers", savers, "uri")
+
+
+end -- of closure
+
+do -- create closure to overcome 200 locals limit
+
+if not modules then modules = { } end modules ['data-fil'] = {
+ version = 1.001,
+ comment = "companion to luat-lib.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
+
+local report_resolvers = logs.new("resolvers")
+
+local resolvers = resolvers
+
+local finders, openers, loaders, savers = resolvers.finders, resolvers.openers, resolvers.loaders, resolvers.savers
+local locators, hashers, generators, concatinators = resolvers.locators, resolvers.hashers, resolvers.generators, resolvers.concatinators
+
+local checkgarbage = utilities.garbagecollector and utilities.garbagecollector.check
+
+function locators.file(specification)
+ local name = specification.filename
+ if name and name ~= '' and lfs.isdir(name) then
+ if trace_locating then
+ report_resolvers("file locator '%s' found",name)
+ end
+ resolvers.appendhash('file',name,true) -- cache
+ elseif trace_locating then
+ report_resolvers("file locator '%s' not found",name)
+ end
+end
+
+function hashers.file(specification)
+ local name = specification.filename
+ local content = caches.loadcontent(name,'files')
+ resolvers.registerfilehash(name,content,content==nil)
+end
+
+function generators.file(specification)
+ local name = specification.filename
+ local content = resolvers.scanfiles(name)
+ resolvers.registerfilehash(name,content,true)
+end
+
+concatinators.file = file.join
+
+function finders.file(specification,filetype)
+ local filename = specification.filename
+ local foundname = resolvers.findfile(filename,filetype)
+ if foundname and foundname ~= "" then
+ if trace_locating then
+ report_resolvers("file finder: '%s' found",filename)
+ end
+ return foundname
+ else
+ if trace_locating then
+ report_resolvers("file finder: %s' not found",filename)
+ end
+ return finders.notfound()
+ end
+end
+
+-- The default textopener will be overloaded later on.
+
+function openers.helpers.textopener(tag,filename,f)
+ return {
+ reader = function() return f:read () end,
+ close = function() return f:close() end,
+ }
+end
+
+function openers.file(specification,filetype)
+ local filename = specification.filename
+ if filename and filename ~= "" then
+ local f = io.open(filename,"r")
+ if f then
+ logs.show_open(filename) -- todo
+ if trace_locating then
+ report_resolvers("file opener, '%s' opened",filename)
+ end
+ return openers.helpers.textopener("file",filename,f)
+ end
+ end
+ if trace_locating then
+ report_resolvers("file opener, '%s' not found",filename)
+ end
+ return openers.notfound()
+end
+
+function loaders.file(specification,filetype)
+ local filename = specification.filename
+ if filename and filename ~= "" then
+ local f = io.open(filename,"rb")
+ if f then
+ logs.show_load(filename)
+ if trace_locating then
+ report_resolvers("file loader, '%s' loaded",filename)
+ end
+ local s = f:read("*a")
+ if checkgarbage then
+ checkgarbage(#s)
+ end
+ f:close()
+ if s then
+ return true, s, #s
+ end
+ end
+ end
+ if trace_locating then
+ report_resolvers("file loader, '%s' not found",filename)
+ end
+ return loaders.notfound()
+end
end -- of closure
@@ -12301,10 +12501,9 @@ if not modules then modules = { } end modules ['data-zip'] = {
license = "see context related readme files"
}
--- to be redone using the more recent schemes mechanism
+-- partly redone .. needs testing
local format, find, match = string.format, string.find, string.match
-local unpack = unpack or table.unpack
local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
@@ -12327,9 +12526,6 @@ local archives = zip.archives
zip.registeredfiles = zip.registeredfiles or { }
local registeredfiles = zip.registeredfiles
-local finders, openers, loaders = resolvers.finders, resolvers.openers, resolvers.loaders
-local locators, hashers, concatinators = resolvers.locators, resolvers.hashers, resolvers.concatinators
-
local function validzip(str) -- todo: use url splitter
if not find(str,"^zip://") then
return "zip:///" .. str
@@ -12359,159 +12555,159 @@ function zip.closearchive(name)
end
end
-function locators.zip(specification) -- where is this used? startup zips (untested)
- specification = resolvers.splitmethod(specification)
- local zipfile = specification.path
- local zfile = zip.openarchive(name) -- tricky, could be in to be initialized tree
+function resolvers.locators.zip(specification)
+ local archive = specification.filename
+ local zipfile = archive and archive ~= "" and zip.openarchive(archive) -- tricky, could be in to be initialized tree
if trace_locating then
- if zfile then
- report_resolvers("zip locator, archive '%s' found",specification.original)
+ if zipfile then
+ report_resolvers("zip locator, archive '%s' found",archive)
else
- report_resolvers("zip locator, archive '%s' not found",specification.original)
+ report_resolvers("zip locator, archive '%s' not found",archive)
end
end
end
-function hashers.zip(tag,name)
+function resolvers.hashers.zip(specification)
+ local archive = specification.filename
if trace_locating then
- report_resolvers("loading zip file '%s' as '%s'",name,tag)
+ report_resolvers("loading zip file '%s'",archive)
end
- resolvers.usezipfile(format("%s?tree=%s",tag,name))
+ resolvers.usezipfile(specification.original)
end
-function concatinators.zip(tag,path,name)
+function resolvers.concatinators.zip(zipfile,path,name) -- ok ?
if not path or path == "" then
- return format('%s?name=%s',tag,name)
+ return format('%s?name=%s',zipfile,name)
else
- return format('%s?name=%s/%s',tag,path,name)
+ return format('%s?name=%s/%s',zipfile,path,name)
end
end
-function resolvers.isreadable.zip(name)
- return true
-end
-
-function finders.zip(specification,filetype)
- specification = resolvers.splitmethod(specification)
- if specification.path then
- local q = url.query(specification.query)
- if q.name then
- local zfile = zip.openarchive(specification.path)
+function resolvers.finders.zip(specification)
+ local original = specification.original
+ local archive = specification.filename
+ if archive then
+ local query = url.query(specification.query)
+ local queryname = query.name
+ if queryname then
+ local zfile = zip.openarchive(archive)
if zfile then
if trace_locating then
- report_resolvers("zip finder, archive '%s' found",specification.path)
+ report_resolvers("zip finder, archive '%s' found",archive)
end
- local dfile = zfile:open(q.name)
+ local dfile = zfile:open(queryname)
if dfile then
dfile = zfile:close()
if trace_locating then
- report_resolvers("zip finder, file '%s' found",q.name)
+ report_resolvers("zip finder, file '%s' found",queryname)
end
return specification.original
elseif trace_locating then
- report_resolvers("zip finder, file '%s' not found",q.name)
+ report_resolvers("zip finder, file '%s' not found",queryname)
end
elseif trace_locating then
- report_resolvers("zip finder, unknown archive '%s'",specification.path)
+ report_resolvers("zip finder, unknown archive '%s'",archive)
end
end
end
if trace_locating then
- report_resolvers("zip finder, '%s' not found",filename)
+ report_resolvers("zip finder, '%s' not found",original)
end
- return unpack(finders.notfound)
+ return resolvers.finders.notfound()
end
-function openers.zip(specification)
- local zipspecification = resolvers.splitmethod(specification)
- if zipspecification.path then
- local q = url.query(zipspecification.query)
- if q.name then
- local zfile = zip.openarchive(zipspecification.path)
+function resolvers.openers.zip(specification)
+ local original = specification.original
+ local archive = specification.filename
+ if archive then
+ local query = url.query(specification.query)
+ local queryname = query.name
+ if queryname then
+ local zfile = zip.openarchive(archive)
if zfile then
if trace_locating then
- report_resolvers("zip opener, archive '%s' opened",zipspecification.path)
+ report_resolvers("zip opener, archive '%s' opened",archive)
end
- local dfile = zfile:open(q.name)
+ local dfile = zfile:open(queryname)
if dfile then
- logs.show_open(specification)
+ logs.show_open(original)
if trace_locating then
- report_resolvers("zip opener, file '%s' found",q.name)
+ report_resolvers("zip opener, file '%s' found",queryname)
end
- return openers.textopener('zip',specification,dfile)
+ return resolvers.openers.helpers.textopener('zip',original,dfile)
elseif trace_locating then
- report_resolvers("zip opener, file '%s' not found",q.name)
+ report_resolvers("zip opener, file '%s' not found",queryname)
end
elseif trace_locating then
- report_resolvers("zip opener, unknown archive '%s'",zipspecification.path)
+ report_resolvers("zip opener, unknown archive '%s'",archive)
end
end
end
if trace_locating then
- report_resolvers("zip opener, '%s' not found",filename)
+ report_resolvers("zip opener, '%s' not found",original)
end
- return unpack(openers.notfound)
+ return resolvers.openers.notfound()
end
-function loaders.zip(specification)
- specification = resolvers.splitmethod(specification)
- if specification.path then
- local q = url.query(specification.query)
- if q.name then
- local zfile = zip.openarchive(specification.path)
+function resolvers.loaders.zip(specification)
+ local original = specification.original
+ local archive = specification.filename
+ if archive then
+ local query = url.query(specification.query)
+ local queryname = query.name
+ if queryname then
+ local zfile = zip.openarchive(archive)
if zfile then
if trace_locating then
- report_resolvers("zip loader, archive '%s' opened",specification.path)
+ report_resolvers("zip loader, archive '%s' opened",archive)
end
- local dfile = zfile:open(q.name)
+ local dfile = zfile:open(queryname)
if dfile then
- logs.show_load(filename)
+ logs.show_load(original)
if trace_locating then
- report_resolvers("zip loader, file '%s' loaded",filename)
+ report_resolvers("zip loader, file '%s' loaded",original)
end
local s = dfile:read("*all")
dfile:close()
return true, s, #s
elseif trace_locating then
- report_resolvers("zip loader, file '%s' not found",q.name)
+ report_resolvers("zip loader, file '%s' not found",queryname)
end
elseif trace_locating then
- report_resolvers("zip loader, unknown archive '%s'",specification.path)
+ report_resolvers("zip loader, unknown archive '%s'",archive)
end
end
end
if trace_locating then
- report_resolvers("zip loader, '%s' not found",filename)
+ report_resolvers("zip loader, '%s' not found",original)
end
- return unpack(openers.notfound)
+ return resolvers.openers.notfound()
end
-- zip:///somefile.zip
-- zip:///somefile.zip?tree=texmf-local -> mount
-function resolvers.usezipfile(zipname)
- zipname = validzip(zipname)
- local specification = resolvers.splitmethod(zipname)
- local zipfile = specification.path
- if zipfile and not registeredfiles[zipname] then
- local tree = url.query(specification.query).tree or ""
- local z = zip.openarchive(zipfile)
+function resolvers.usezipfile(archive)
+ local specification = resolvers.splitmethod(archive) -- to be sure
+ local archive = specification.filename
+ if archive and not registeredfiles[archive] then
+ local z = zip.openarchive(archive)
if z then
- local instance = resolvers.instance
+ local tree = url.query(specification.query).tree or ""
if trace_locating then
- report_resolvers("zip registering, registering archive '%s'",zipname)
- end
- statistics.starttiming(instance)
- resolvers.prependhash('zip',zipname,zipfile)
- resolvers.extendtexmfvariable(zipname) -- resets hashes too
- registeredfiles[zipname] = z
- instance.files[zipname] = resolvers.registerzipfile(z,tree or "")
- statistics.stoptiming(instance)
+ report_resolvers("zip registering, registering archive '%s'",archive)
+ end
+ statistics.starttiming(resolvers.instance)
+ resolvers.prependhash('zip',archive)
+ resolvers.extendtexmfvariable(archive) -- resets hashes too
+ registeredfiles[archive] = z
+ instance.files[archive] = resolvers.registerzipfile(z,tree)
+ statistics.stoptiming(resolvers.instance)
elseif trace_locating then
- report_resolvers("zip registering, unknown archive '%s'",zipname)
+ report_resolvers("zip registering, unknown archive '%s'",archive)
end
elseif trace_locating then
- report_resolvers("zip registering, '%s' not found",zipname)
+ report_resolvers("zip registering, '%s' not found",archive)
end
end
@@ -12560,7 +12756,8 @@ if not modules then modules = { } end modules ['data-tre'] = {
-- \input tree://oeps1/**/oeps.tex
local find, gsub, format = string.find, string.gsub, string.format
-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")
@@ -12568,10 +12765,10 @@ local resolvers = 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 ""
+function resolvers.finders.tree(specification)
+ local spec = specification.filename
+ local fnd = found[spec]
+ if fnd == nil then
if spec ~= "" then
local path, name = file.dirname(spec), file.basename(spec)
if path == "" then path = "." end
@@ -12585,53 +12782,41 @@ function resolvers.finders.tree(specification,filetype)
for k=1,#hash do
local v = hash[k]
if find(v,pattern) then
- found[specification] = v
+ found[spec] = v
return v
end
end
end
- fnd = unpack(notfound) -- unpack ? why not just notfound[1]
- found[specification] = fnd
+ fnd = notfound() -- false
+ found[spec] = 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
+ local name = specification.filename
+ if name ~= '' and lfs.isdir(name) then
if trace_locating then
- report_resolvers("tree locator '%s' found (%s)",path,specification)
+ report_resolvers("tree locator '%s' found",name)
end
- resolvers.appendhash('tree',specification,path,false) -- don't cache
+ resolvers.appendhash('tree',name,false) -- don't cache
elseif trace_locating then
- report_resolvers("tree locator '%s' not found",path)
+ report_resolvers("tree locator '%s' not found",name)
end
end
-function resolvers.hashers.tree(tag,name)
+function resolvers.hashers.tree(specification)
+ local name = specification.filename
if trace_locating then
- report_resolvers("analysing tree '%s' as '%s'",name,tag)
+ report_resolvers("analysing tree '%s'",name)
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
+ resolvers.methodhandler("hashers",name)
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
+resolvers.concatinators.tree = resolvers.concatinators.file
+resolvers.generators.tree = resolvers.generators.file
+resolvers.openers.tree = resolvers.openers.file
+resolvers.loaders.tree = resolvers.loaders.file
end -- of closure
@@ -12654,53 +12839,51 @@ local resolvers = resolvers
local finders, openers, loaders = resolvers.finders, resolvers.openers, resolvers.loaders
-curl = curl or { }
-local curl = curl
+resolvers.curl = resolvers.curl or { }
+local curl = resolvers.curl
local cached = { }
-function curl.fetch(protocol, name) -- todo: use socket library
- local cleanname = gsub(name,"[^%a%d%.]+","-")
+local function runcurl(specification)
+ local original = specification.original
+ -- local scheme = specification.scheme
+ local cleanname = gsub(original,"[^%a%d%.]+","-")
local cachename = caches.setfirstwritablefile(cleanname,"curl")
- if not cached[name] then
+ if not cached[original] then
if not io.exists(cachename) then
- cached[name] = cachename
- local command = "curl --silent --create-dirs --output " .. cachename .. " " .. name -- no protocol .. "://"
+ cached[original] = cachename
+ local command = "curl --silent --create-dirs --output " .. cachename .. " " .. original
os.spawn(command)
end
if io.exists(cachename) then
- cached[name] = cachename
+ cached[original] = cachename
else
- cached[name] = ""
+ cached[original] = ""
end
end
- return cached[name]
+ return cached[original]
end
-function finders.curl(protocol,filename)
- local foundname = curl.fetch(protocol, filename)
- return finders.generic(protocol,foundname,filetype)
-end
+-- old code: we could be cleaner using specification (see schemes)
-function openers.curl(protocol,filename)
- return openers.generic(protocol,filename)
+local function finder(specification,filetype)
+ return resolvers.methodhandler("finders",runcurl(specification),filetype)
end
-function loaders.curl(protocol,filename)
- return loaders.generic(protocol,filename)
-end
-
--- todo: metamethod
+local opener = openers.file
+local loader = loaders.file
-function curl.install(protocol)
- finders[protocol] = function (filename,filetype) return finders.curl(protocol,filename) end
- openers[protocol] = function (filename) return openers.curl(protocol,filename) end
- loaders[protocol] = function (filename) return loaders.curl(protocol,filename) end
+local function install(scheme)
+ finders[scheme] = finder
+ openers[scheme] = opener
+ loaders[scheme] = loader
end
-curl.install('http')
-curl.install('https')
-curl.install('ftp')
+resolvers.curl.install = install
+
+install('http')
+install('https')
+install('ftp')
end -- of closure
@@ -12777,7 +12960,7 @@ local function loaded(libpaths,name,simple)
if trace_locating then -- more detail
report_resolvers("! checking for '%s' on 'package.path': '%s' => '%s'",simple,libpath,resolved)
end
- if resolvers.isreadable.file(resolved) then
+ if file.is_readable(resolved) then
if trace_locating then
report_resolvers("! lib '%s' located via 'package.path': '%s'",name,resolved)
end
@@ -12786,7 +12969,6 @@ local function loaded(libpaths,name,simple)
end
end
-
package.loaders[2] = function(name) -- was [#package.loaders+1]
if trace_locating then -- mode detail
report_resolvers("! locating '%s'",name)
@@ -12824,7 +13006,7 @@ package.loaders[2] = function(name) -- was [#package.loaders+1]
if trace_locating then -- mode detail
report_resolvers("! checking for '%s' using 'clibformat path': '%s'",libname,path)
end
- if resolvers.isreadable.file(resolved) then
+ if file.is_readable(resolved) then
if trace_locating then
report_resolvers("! lib '%s' located via 'clibformat': '%s'",libname,resolved)
end
@@ -12838,7 +13020,7 @@ package.loaders[2] = function(name) -- was [#package.loaders+1]
if trace_locating then -- more detail
report_resolvers("! checking for '%s' on 'package.cpath': '%s'",simple,libpath)
end
- if resolvers.isreadable.file(resolved) then
+ if file.is_readable(resolved) then
if trace_locating then
report_resolvers("! lib '%s' located via 'package.cpath': '%s'",name,resolved)
end
@@ -13375,6 +13557,7 @@ own.libs = { -- order can be made better
'data-pre.lua',
'data-inp.lua',
'data-out.lua',
+ 'data-fil.lua',
'data-con.lua',
'data-use.lua',
-- 'data-tex.lua',