if not modules then modules = { } end modules ['data-zip'] = {
version = 1.001,
comment = "companion to luat-lib.mkiv",
author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright = "PRAGMA ADE / ConTeXt Development Team",
license = "see context related readme files"
}
-- partly redone .. needs testing
local format, find, match = string.format, string.find, string.match
local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
local report_zip = logs.reporter("resolvers","zip")
--[[ldx--
We use a url syntax for accessing the zip file itself and file in it:
zip:///oeps.zip?name=bla/bla.tex
zip:///oeps.zip?tree=tex/texmf-local
zip:///texmf.zip?tree=/tex/texmf
zip:///texmf.zip?tree=/tex/texmf-local
zip:///texmf-mine.zip?tree=/tex/texmf-projects
--ldx]]--
local resolvers = resolvers
zip = zip or { }
local zip = zip
zip.archives = zip.archives or { }
local archives = zip.archives
zip.registeredfiles = zip.registeredfiles or { }
local registeredfiles = zip.registeredfiles
local limited = false
directives.register("system.inputmode", function(v)
if not limited then
local i_limiter = io.i_limiter(v)
if i_limiter then
zip.open = i_limiter.protect(zip.open)
limited = true
end
end
end)
local function validzip(str) -- todo: use url splitter
if not find(str,"^zip://") then
return "zip:///" .. str
else
return str
end
end
function zip.openarchive(name)
if not name or name == "" then
return nil
else
local arch = archives[name]
if not arch then
local full = resolvers.findfile(name) or ""
arch = (full ~= "" and zip.open(full)) or false
archives[name] = arch
end
return arch
end
end
function zip.closearchive(name)
if not name or (name == "" and archives[name]) then
zip.close(archives[name])
archives[name] = nil
end
end
function resolvers.locators.zip(specification)
local archive = specification.filename
local zipfile = archive and archive ~= "" and zip.openarchive(archive) -- tricky, could be in to be initialized tree
if trace_locating then
if zipfile then
report_zip("locator: archive %a found",archive)
else
report_zip("locator: archive %a not found",archive)
end
end
end
function resolvers.hashers.zip(specification)
local archive = specification.filename
if trace_locating then
report_zip("loading file %a",archive)
end
resolvers.usezipfile(specification.original)
end
function resolvers.concatinators.zip(zipfile,path,name) -- ok ?
if not path or path == "" then
return format('%s?name=%s',zipfile,name)
else
return format('%s?name=%s/%s',zipfile,path,name)
end
end
function resolvers.finders.zip(specification)
local original = specification.original
local archive = specification.filename
if archive then
local query = url.query(specification.query)
local queryname = query.name
if queryname then
local zfile = zip.openarchive(archive)
if zfile then
if trace_locating then
report_zip("finder: archive %a found",archive)
end
local dfile = zfile:open(queryname)
if dfile then
dfile = zfile:close()
if trace_locating then
report_zip("finder: file %a found",queryname)
end
return specification.original
elseif trace_locating then
report_zip("finder: file %a not found",queryname)
end
elseif trace_locating then
report_zip("finder: unknown archive %a",archive)
end
end
end
if trace_locating then
report_zip("finder: %a not found",original)
end
return resolvers.finders.notfound()
end
function resolvers.openers.zip(specification)
local original = specification.original
local archive = specification.filename
if archive then
local query = url.query(specification.query)
local queryname = query.name
if queryname then
local zfile = zip.openarchive(archive)
if zfile then
if trace_locating then
report_zip("opener; archive %a opened",archive)
end
local dfile = zfile:open(queryname)
if dfile then
if trace_locating then
report_zip("opener: file %a found",queryname)
end
return resolvers.openers.helpers.textopener('zip',original,dfile)
elseif trace_locating then
report_zip("opener: file %a not found",queryname)
end
elseif trace_locating then
report_zip("opener: unknown archive %a",archive)
end
end
end
if trace_locating then
report_zip("opener: %a not found",original)
end
return resolvers.openers.notfound()
end
function resolvers.loaders.zip(specification)
local original = specification.original
local archive = specification.filename
if archive then
local query = url.query(specification.query)
local queryname = query.name
if queryname then
local zfile = zip.openarchive(archive)
if zfile then
if trace_locating then
report_zip("loader: archive %a opened",archive)
end
local dfile = zfile:open(queryname)
if dfile then
logs.show_load(original)
if trace_locating then
report_zip("loader; file %a loaded",original)
end
local s = dfile:read("*all")
dfile:close()
return true, s, #s
elseif trace_locating then
report_zip("loader: file %a not found",queryname)
end
elseif trace_locating then
report_zip("loader; unknown archive %a",archive)
end
end
end
if trace_locating then
report_zip("loader: %a not found",original)
end
return resolvers.openers.notfound()
end
-- zip:///somefile.zip
-- zip:///somefile.zip?tree=texmf-local -> mount
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_zip("registering: archive %a",archive)
end
statistics.starttiming(instance)
resolvers.prependhash('zip',archive)
resolvers.extendtexmfvariable(archive) -- resets hashes too
registeredfiles[archive] = z
instance.files[archive] = resolvers.registerzipfile(z,tree)
statistics.stoptiming(instance)
elseif trace_locating then
report_zip("registering: unknown archive %a",archive)
end
elseif trace_locating then
report_zip("registering: archive %a not found",archive)
end
end
function resolvers.registerzipfile(z,tree)
local files, filter = { }, ""
if tree == "" then
filter = "^(.+)/(.-)$"
else
filter = format("^%s/(.+)/(.-)$",tree)
end
if trace_locating then
report_zip("registering: using filter %a",filter)
end
local register, n = resolvers.registerfile, 0
for i in z:files() do
local path, name = match(i.filename,filter)
if path then
if name and name ~= '' then
register(files, name, path)
n = n + 1
else
-- directory
end
else
register(files, i.filename, '')
n = n + 1
end
end
report_zip("registering: %s files registered",n)
return files
end