summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/scripts/context/lua
diff options
context:
space:
mode:
authorTaco Hoekwater <taco@elvenkind.com>2011-06-01 08:54:21 +0000
committerTaco Hoekwater <taco@elvenkind.com>2011-06-01 08:54:21 +0000
commitd7ccb42582f85acf30568913610ccf4d602023fb (patch)
tree7292e3545a420676878e7451b68892d360c62cb6 /Master/texmf-dist/scripts/context/lua
parent2d62a6fe9b80def59c392268022f1f9a2d6e358f (diff)
commit context 2011.05.18
git-svn-id: svn://tug.org/texlive/trunk@22719 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/scripts/context/lua')
-rw-r--r--Master/texmf-dist/scripts/context/lua/mtx-babel.lua36
-rw-r--r--Master/texmf-dist/scripts/context/lua/mtx-base.lua118
-rw-r--r--Master/texmf-dist/scripts/context/lua/mtx-cache.lua141
-rw-r--r--Master/texmf-dist/scripts/context/lua/mtx-chars.lua50
-rw-r--r--Master/texmf-dist/scripts/context/lua/mtx-check.lua168
-rw-r--r--Master/texmf-dist/scripts/context/lua/mtx-colors.lua57
-rw-r--r--Master/texmf-dist/scripts/context/lua/mtx-context.lua1385
-rw-r--r--Master/texmf-dist/scripts/context/lua/mtx-convert.lua33
-rw-r--r--Master/texmf-dist/scripts/context/lua/mtx-epub.lua177
-rw-r--r--Master/texmf-dist/scripts/context/lua/mtx-flac.lua216
-rw-r--r--Master/texmf-dist/scripts/context/lua/mtx-fonts.lua231
-rw-r--r--Master/texmf-dist/scripts/context/lua/mtx-grep.lua28
-rw-r--r--Master/texmf-dist/scripts/context/lua/mtx-interface.lua423
-rw-r--r--Master/texmf-dist/scripts/context/lua/mtx-metapost.lua177
-rw-r--r--Master/texmf-dist/scripts/context/lua/mtx-metatex.lua43
-rw-r--r--Master/texmf-dist/scripts/context/lua/mtx-modules.lua35
-rw-r--r--Master/texmf-dist/scripts/context/lua/mtx-mptopdf.lua127
-rw-r--r--Master/texmf-dist/scripts/context/lua/mtx-package.lua36
-rw-r--r--Master/texmf-dist/scripts/context/lua/mtx-patterns.lua660
-rw-r--r--Master/texmf-dist/scripts/context/lua/mtx-profile.lua38
-rw-r--r--Master/texmf-dist/scripts/context/lua/mtx-scite.lua56
-rw-r--r--Master/texmf-dist/scripts/context/lua/mtx-server-ctx-fonttest.lua56
-rw-r--r--Master/texmf-dist/scripts/context/lua/mtx-server-ctx-help.lua18
-rw-r--r--Master/texmf-dist/scripts/context/lua/mtx-server-ctx-startup.lua4
-rw-r--r--Master/texmf-dist/scripts/context/lua/mtx-server.lua83
-rw-r--r--Master/texmf-dist/scripts/context/lua/mtx-texworks.lua48
-rw-r--r--Master/texmf-dist/scripts/context/lua/mtx-timing.lua40
-rw-r--r--Master/texmf-dist/scripts/context/lua/mtx-tools.lua68
-rw-r--r--Master/texmf-dist/scripts/context/lua/mtx-unzip.lua41
-rw-r--r--Master/texmf-dist/scripts/context/lua/mtx-update.lua152
-rw-r--r--Master/texmf-dist/scripts/context/lua/mtx-watch.lua64
-rwxr-xr-xMaster/texmf-dist/scripts/context/lua/mtxrun.lua12366
-rw-r--r--Master/texmf-dist/scripts/context/lua/x-ldx.lua322
33 files changed, 10544 insertions, 6953 deletions
diff --git a/Master/texmf-dist/scripts/context/lua/mtx-babel.lua b/Master/texmf-dist/scripts/context/lua/mtx-babel.lua
index 01e2ba4b223..120e490929e 100644
--- a/Master/texmf-dist/scripts/context/lua/mtx-babel.lua
+++ b/Master/texmf-dist/scripts/context/lua/mtx-babel.lua
@@ -8,6 +8,20 @@ if not modules then modules = { } end modules ['mtx-babel'] = {
-- data tables by Thomas A. Schmitz
+local helpinfo = [[
+--language=string conversion language (e.g. greek)
+--structure=string obey given structure (e.g. 'document', default: 'context')
+--convert convert babel codes into utf
+]]
+
+local application = logs.application {
+ name = "mtx-babel",
+ banner = "Babel Input To UTF Conversion 1.20",
+ helpinfo = helpinfo,
+}
+
+local report = application.report
+
scripts = scripts or { }
scripts.babel = scripts.babel or { }
@@ -387,22 +401,22 @@ do
local structure = environment.argument("structure") or "document"
converter = converter[structure]
if converter then
- logs.simple("converting '%s' using language '%s' with structure '%s'", filename, language, structure)
+ report("converting '%s' using language '%s' with structure '%s'", filename, language, structure)
data = converter:match(data)
local newfilename = filename .. ".utf"
io.savedata(newfilename, data)
- logs.simple("converted data saved in '%s'", newfilename)
+ report("converted data saved in '%s'", newfilename)
else
- logs.simple("unknown structure '%s' language '%s'", structure, language)
+ report("unknown structure '%s' language '%s'", structure, language)
end
else
- logs.simple("no converter for language '%s'", language)
+ report("no converter for language '%s'", language)
end
else
- logs.simple("provide language")
+ report("provide language")
end
else
- logs.simple("no data in '%s'",filename)
+ report("no data in '%s'",filename)
end
end
end
@@ -415,16 +429,8 @@ do
end
-logs.extendbanner("Babel Input To UTF Conversion 1.20",true)
-
-messages.help = [[
---language=string conversion language (e.g. greek)
---structure=string obey given structure (e.g. 'document', default: 'context')
---convert convert babel codes into utf
-]]
-
if environment.argument("convert") then
scripts.babel.convert(environment.files[1] or "")
else
- logs.help(messages.help)
+ application.help()
end
diff --git a/Master/texmf-dist/scripts/context/lua/mtx-base.lua b/Master/texmf-dist/scripts/context/lua/mtx-base.lua
new file mode 100644
index 00000000000..ab5bfe18a9b
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/lua/mtx-base.lua
@@ -0,0 +1,118 @@
+if not modules then modules = { } end modules ['mtx-base'] = {
+ version = 1.001,
+ comment = "formerly known as luatools",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local helpinfo = [[
+--generate generate file database
+--variables show configuration variables
+--configurations show configuration order
+--expand-braces expand complex variable
+--expand-path expand variable (resolve paths)
+--expand-var expand variable (resolve references)
+--show-path show path expansion of ...
+--var-value report value of variable
+--find-file report file location
+--find-path report path of file
+--make or --ini make luatex format
+--run or --fmt= run luatex format
+--compile assemble and compile lua inifile
+--verbose give a bit more info
+--all show all found files
+--format=str filter cf format specification (default 'tex', use 'any' for any match)
+--pattern=str filter variables
+--trackers=list enable given trackers
+]]
+
+local application = logs.application {
+ name = "mtx-base",
+ banner = "ConTeXt TDS Management Tool 1.35 (aka luatools)",
+ helpinfo = helpinfo,
+}
+
+local report = application.report
+
+-- private option --noluc for testing errors in the stub
+
+local instance = resolvers.instance
+
+local pattern = environment.arguments["pattern"] or nil
+local fileformat = environment.arguments["format"] or "" -- nil ?
+local allresults = environment.arguments["all"] or false
+local trace = environment.arguments["trace"]
+
+if type(pattern) == 'boolean' then
+ report("invalid pattern specification")
+ pattern = nil
+end
+
+if trace then
+ resolvers.settrace(trace) -- move to mtxrun ?
+end
+
+if environment.arguments["find-file"] then
+ resolvers.load()
+ if pattern then
+ resolvers.dowithfilesandreport(resolvers.findfiles, { pattern }, fileformat, allresults)
+ else
+ resolvers.dowithfilesandreport(resolvers.findfiles, environment.files, fileformat, allresults)
+ end
+elseif environment.arguments["find-path"] then
+ resolvers.load()
+ local path = resolvers.findpath(environment.files[1], fileformat)
+ print(path) -- quite basic, wil become function in logs
+elseif environment.arguments["run"] then
+ resolvers.load("nofiles") -- ! no need for loading databases
+ trackers.enable("resolvers.locating")
+ environment.run_format(environment.files[1] or "",environment.files[2] or "",environment.files[3] or "")
+elseif environment.arguments["fmt"] then
+ resolvers.load("nofiles") -- ! no need for loading databases
+ trackers.enable("resolvers.locating")
+ environment.run_format(environment.arguments["fmt"], environment.files[1] or "",environment.files[2] or "")
+elseif environment.arguments["expand-braces"] then
+ resolvers.load("nofiles")
+ resolvers.dowithfilesandreport(resolvers.expandbraces, environment.files)
+elseif environment.arguments["expand-path"] then
+ resolvers.load("nofiles")
+ resolvers.dowithfilesandreport(resolvers.expandpath, environment.files)
+elseif environment.arguments["expand-var"] or environment.arguments["expand-variable"] then
+ resolvers.load("nofiles")
+ resolvers.dowithfilesandreport(resolvers.expansion, environment.files)
+elseif environment.arguments["show-path"] or environment.arguments["path-value"] then
+ resolvers.load("nofiles")
+ resolvers.dowithfilesandreport(resolvers.showpath, environment.files)
+elseif environment.arguments["var-value"] or environment.arguments["show-value"] then
+ resolvers.load("nofiles")
+ resolvers.dowithfilesandreport(resolvers.variable, environment.files)
+elseif environment.arguments["format-path"] then
+ resolvers.load()
+ report(caches.getwritablepath("format"))
+elseif pattern then -- brrr
+ resolvers.load()
+ resolvers.dowithfilesandreport(resolvers.findfiles, { pattern }, fileformat, allresults)
+elseif environment.arguments["generate"] then
+ instance.renewcache = true
+ trackers.enable("resolvers.locating")
+ resolvers.load()
+elseif environment.arguments["make"] or environment.arguments["ini"] or environment.arguments["compile"] then
+ resolvers.load()
+ trackers.enable("resolvers.locating")
+ environment.make_format(environment.files[1] or "")
+elseif environment.arguments["variables"] or environment.arguments["show-variables"] or environment.arguments["expansions"] or environment.arguments["show-expansions"] then
+ resolvers.load("nofiles")
+ resolvers.listers.variables(pattern)
+elseif environment.arguments["configurations"] or environment.arguments["show-configurations"] then
+ resolvers.load("nofiles")
+ resolvers.listers.configurations()
+elseif environment.arguments["help"] or (environment.files[1]=='help') or (#environment.files==0) then
+ application.help()
+elseif environment.files[1] == 'texmfcnf.lua' then
+ resolvers.load("nofiles")
+ resolvers.listers.configurations()
+else
+ resolvers.load()
+ resolvers.dowithfilesandreport(resolvers.findfiles, environment.files, fileformat, allresults)
+end
diff --git a/Master/texmf-dist/scripts/context/lua/mtx-cache.lua b/Master/texmf-dist/scripts/context/lua/mtx-cache.lua
index c2a0db00d89..08202bbf816 100644
--- a/Master/texmf-dist/scripts/context/lua/mtx-cache.lua
+++ b/Master/texmf-dist/scripts/context/lua/mtx-cache.lua
@@ -6,91 +6,114 @@ if not modules then modules = { } end modules ['mtx-cache'] = {
license = "see context related readme files"
}
-scripts = scripts or { }
-scripts.cache = scripts.cache or { }
+local helpinfo = [[
+--purge remove not used files
+--erase completely remove cache
+--list show cache
-function scripts.cache.collect_one(...)
- local path = caches.setpath(...)
- local tmas = dir.glob(path .. "/*.tma")
- local tmcs = dir.glob(path .. "/*.tmc")
- return path, tmas, tmcs
-end
+--all all (not yet implemented)
+]]
-function scripts.cache.collect_two(...)
- local path = caches.setpath(...)
- local rest = dir.glob(path .. "/**/*")
- return path, rest
-end
+local application = logs.application {
+ name = "mtx-cache",
+ banner = "ConTeXt & MetaTeX Cache Management 0.10",
+ helpinfo = helpinfo,
+}
-local suffixes = { "afm", "tfm", "def", "enc", "otf", "mp", "data" }
+local report = application.report
+
+scripts = scripts or { }
+scripts.cache = scripts.cache or { }
-function scripts.cache.process_one(action)
- for i=1,#suffixes do
- action("fonts", suffixes[i])
+local function collect(path)
+ local all = dir.glob(path .. "/**/*")
+ local tmas, tmcs, rest = { }, { }, { }
+ for i=1,#all do
+ local name = all[i]
+ local suffix = file.suffix(name)
+ if suffix == "tma" then
+ tmas[#tmas+1] = name
+ elseif suffix == "tmc" then
+ tmcs[#tmcs+1] = name
+ else
+ rest[#rest+1] = name
+ end
end
+ return tmas, tmcs, rest, all
end
-function scripts.cache.process_two(action)
- action("curl")
+local function list(banner,path,tmas,tmcs,rest)
+ report("%s: %s",banner,path)
+ report()
+ report("tma : %4i",#tmas)
+ report("tmc : %4i",#tmcs)
+ report("rest : %4i",#rest)
+ report("total : %4i",#tmas+#tmcs+#rest)
+ report()
end
--- todo: recursive delete of paths
-
-function scripts.cache.remove(list,keep)
- local n, keepsuffixes = 0, table.tohash(keep or { })
+local function purge(banner,path,list,all)
+ report("%s: %s",banner,path)
+ report()
+ local n = 0
for i=1,#list do
local filename = list[i]
if string.find(filename,"luatex%-cache") then -- safeguard
- if not keepsuffixes[file.extname(filename) or ""] then
+ if all then
os.remove(filename)
n = n + 1
+ else
+ local suffix = file.suffix(filename)
+ if suffix == "tma" then
+ local checkname = file.replacesuffix(filename,"tma","tmc")
+ if lfs.isfile(checkname) then
+ os.remove(filename)
+ n = n + 1
+ end
+ end
end
end
end
- return n
+ report("removed tma files : %i",n)
+ report()
end
-function scripts.cache.delete(all,keep)
- scripts.cache.process_one(function(...)
- local path, rest = scripts.cache.collect_one(...)
- local n = scripts.cache.remove(rest,keep)
- logs.report("cache path",string.format("%4i files out of %4i deleted on %s",n,#rest,path))
- end)
- scripts.cache.process_two(function(...)
- local path, rest = scripts.cache.collect_two(...)
- local n = scripts.cache.remove(rest,keep)
- logs.report("cache path",string.format("%4i files out of %4i deleted on %s",n,#rest,path))
- end)
+function scripts.cache.purge()
+ local writable = caches.getwritablepath()
+ local tmas, tmcs, rest = collect(writable)
+ list("writable path",writable,tmas,tmcs,rest)
+ local n = purge("writable path",writable,tmas)
+ list("writable path",writable,tmas,tmcs,rest)
end
-function scripts.cache.list(all)
- scripts.cache.process_one(function(...)
- local path, tmas, tmcs = scripts.cache.collect_one(...)
- logs.report("cache path",string.format("%4i (tma:%4i, tmc:%4i) %s",#tmas+#tmcs,#tmas,#tmcs,path))
- logs.report("cache path",string.format("%4i (tma:%4i, tmc:%4i) %s",#tmas+#tmcs,#tmas,#tmcs,path))
- end)
- scripts.cache.process_two(function(...)
- local path, rest = scripts.cache.collect_two("curl")
- logs.report("cache path",string.format("%4i %s",#rest,path))
- end)
+function scripts.cache.erase()
+ local writable = caches.getwritablepath()
+ local tmas, tmcs, rest, all = collect(writable)
+ list("writable path",writable,tmas,tmcs,rest)
+ local n = purge("writable path",writable,all,true)
+ list("writable path",writable,tmas,tmcs,rest)
end
-logs.extendbanner("ConTeXt & MetaTeX Cache Management 0.10")
-
-messages.help = [[
---purge remove not used files
---erase completely remove cache
---list show cache
-
---all all (not yet implemented)
-]]
+function scripts.cache.list()
+ local readables = caches.getreadablepaths()
+ local writable = caches.getwritablepath()
+ local tmas, tmcs, rest = collect(writable)
+ list("writable path",writable,tmas,tmcs,rest)
+ for i=1,#readables do
+ local readable = readables[i]
+ if readable ~= writable then
+ local tmas, tmcs = collect(readable)
+ list("readable path",readable,tmas,tmcs,rest)
+ end
+ end
+end
if environment.argument("purge") then
- scripts.cache.delete(environment.argument("all"),{"tmc"})
+ scripts.cache.purge()
elseif environment.argument("erase") then
- scripts.cache.delete(environment.argument("all"))
+ scripts.cache.erase()
elseif environment.argument("list") then
- scripts.cache.list(environment.argument("all"))
+ scripts.cache.list()
else
- logs.help(messages.help)
+ application.help()
end
diff --git a/Master/texmf-dist/scripts/context/lua/mtx-chars.lua b/Master/texmf-dist/scripts/context/lua/mtx-chars.lua
index 6acacfbd22e..dc760c4e7e6 100644
--- a/Master/texmf-dist/scripts/context/lua/mtx-chars.lua
+++ b/Master/texmf-dist/scripts/context/lua/mtx-chars.lua
@@ -6,6 +6,20 @@ if not modules then modules = { } end modules ['mtx-chars'] = {
license = "see context related readme files"
}
+local helpinfo = [[
+--stix convert stix table to math table
+--xtx generate xetx-*.tex (used by xetex)
+--pdf generate pdfr-def.tex (used by pdftex)
+]]
+
+local application = logs.application {
+ name = "mtx-chars",
+ banner = "MkII Character Table Generators 0.10",
+ helpinfo = helpinfo,
+}
+
+local report = application.report
+
local format, concat, utfchar, upper = string.format, table.concat, unicode.utf8.char, string.upper
scripts = scripts or { }
@@ -24,13 +38,13 @@ scripts.chars = scripts.chars or { }
--~
--~ function scripts.chars.stixtomkiv(inname,outname)
--~ if inname == "" then
---~ logs.report("aquiring math data","invalid datafilename")
+--~ report("aquiring math data, invalid datafilename")
--~ end
--~ local f = io.open(inname)
--~ if not f then
---~ logs.report("aquiring math data","invalid datafile")
+--~ report("aquiring math data, invalid datafile")
--~ else
---~ logs.report("aquiring math data","processing " .. inname)
+--~ report("aquiring math data, processing %s",inname)
--~ if not outname or outname == "" then
--~ outname = "char-mth.lua"
--~ end
@@ -72,9 +86,9 @@ scripts.chars = scripts.chars or { }
--~ end
--~ if not valid then
--~ g:write("\t-- The data file is corrupt, invalid or maybe the format has changed.\n")
---~ logs.report("aquiring math data","problems with data table")
+--~ report("aquiring math data, problems with data table")
--~ else
---~ logs.report("aquiring math data","table saved in " .. outname)
+--~ report("aquiring math data, table saved in %s",outname)
--~ end
--~ g:write("}\n")
--~ g:close()
@@ -83,7 +97,7 @@ scripts.chars = scripts.chars or { }
--~ end
function scripts.chars.stixtomkiv(inname,outname)
- logs.report("we no longer use this options but use our own tables instead")
+ report("we no longer use this options but use our own tables instead")
end
local banner_pdf_1 = [[
@@ -101,7 +115,7 @@ local banner_pdf_2 = [[
]]
function scripts.chars.makepdfr()
- local chartable = resolvers.find_file("char-def.lua") or ""
+ local chartable = resolvers.findfile("char-def.lua") or ""
if chartable ~= "" then
dofile(chartable)
if characters and characters.data then
@@ -143,6 +157,11 @@ local banner_utf_patch = [[
\setXTXcharcodes "201C "201C "201C
\setXTXcharcodes "201D "201D "201D
+
+% patch needed for french
+
+\setXTXcharcodes "2019 "2019 "2019
+
]]
local banner_utf_names = [[
@@ -174,13 +193,13 @@ local xtxclasses = {
}
function scripts.chars.makeencoutf()
- local chartable = resolvers.find_file("char-def.lua") or ""
+ local chartable = resolvers.findfile("char-def.lua") or ""
if chartable ~= "" then
dofile(chartable)
local function open(name,banner)
local f = io.open(name,'w')
if f then
- logs.simple("writing '%s'",name)
+ report("writing '%s'",name)
f:write(format(banner_utf_module,name))
f:write(banner)
f:write()
@@ -234,13 +253,14 @@ function scripts.chars.makeencoutf()
end
end
end
+ local template = "\\def\\%-".. length .. "s{\\char\"%05X } %% %s: %s\n"
for i=1,#list do
local code = list[i]
if code > 0x5B and code <= 0xFFFF then
local chr = data[code]
if chr and chr.contextname then
local ch = utfchar(code)
- f:write(format("\\def\\%s{\\char\"%05X } %% %s: %s\n", chr.contextname:rpadd(length," "), code, chr.description, ch))
+ f:write(format(template, chr.contextname, code, chr.description, ch))
end
end
end
@@ -301,14 +321,6 @@ function scripts.chars.makeencoutf()
end
end
-logs.extendbanner("MkII Character Table Generators 0.10")
-
-messages.help = [[
---stix convert stix table to math table
---xtx generate xetx-*.tex (used by xetex)
---pdf generate pdfr-def.tex (used by pdftex)
-]]
-
if environment.argument("stix") then
local inname = environment.files[1] or ""
local outname = environment.files[2] or ""
@@ -318,5 +330,5 @@ elseif environment.argument("xtx") then
elseif environment.argument("pdf") then
scripts.chars.makepdfr()
else
- logs.help(messages.help)
+ application.help()
end
diff --git a/Master/texmf-dist/scripts/context/lua/mtx-check.lua b/Master/texmf-dist/scripts/context/lua/mtx-check.lua
index 4266ddf0d46..e0ab3b88276 100644
--- a/Master/texmf-dist/scripts/context/lua/mtx-check.lua
+++ b/Master/texmf-dist/scripts/context/lua/mtx-check.lua
@@ -6,91 +6,99 @@ if not modules then modules = { } end modules ['mtx-check'] = {
license = "see context related readme files"
}
+local helpinfo = [[
+--convert check tex file for errors
+]]
+
+local application = logs.application {
+ name = "mtx-check",
+ banner = "Basic ConTeXt Syntax Checking 0.10",
+ helpinfo = helpinfo,
+}
+
+local report = application.report
+
scripts = scripts or { }
scripts.checker = scripts.checker or { }
local validator = { }
-do
-
- validator.n = 1
- validator.errors = { }
- validator.trace = false
- validator.direct = false
+validator.n = 1
+validator.errors = { }
+validator.trace = false
+validator.direct = false
- validator.printer = print
- validator.tracer = print
+validator.printer = print
+validator.tracer = print
- local message = function(position, kind)
- local ve = validator.errors
- ve[#ve+1] = { kind, position, validator.n }
- if validator.direct then
- validator.printer(string.format("%s error at position %s (line %s)", kind, position, validator.n))
- end
+local message = function(position, kind)
+ local ve = validator.errors
+ ve[#ve+1] = { kind, position, validator.n }
+ if validator.direct then
+ validator.printer(string.format("%s error at position %s (line %s)", kind, position, validator.n))
end
- local progress = function(position, data, kind)
- if validator.trace then
- validator.tracer(string.format("%s at position %s: %s", kind, position, data or ""))
- end
+end
+local progress = function(position, data, kind)
+ if validator.trace then
+ validator.tracer(string.format("%s at position %s: %s", kind, position, data or ""))
end
+end
- local P, R, S, V, C, CP, CC = lpeg.P, lpeg.R, lpeg.S, lpeg.V, lpeg.C, lpeg.Cp, lpeg.Cc
-
- local i_m, d_m = P("$"), P("$$")
- local l_s, r_s = P("["), P("]")
- local l_g, r_g = P("{"), P("}")
-
- local okay = lpeg.P("{[}") + lpeg.P("{]}")
-
- local esc = P("\\")
- local cr = P("\r")
- local lf = P("\n")
- local crlf = P("\r\n")
- local space = S(" \t\f\v")
- local newline = crlf + cr + lf
-
- local line = newline / function() validator.n = validator.n + 1 end
-
- -- local grammar = P { "tokens",
- -- ["tokens"] = (V("whatever") + V("grouped") + V("setup") + V("display") + V("inline") + V("errors") + 1)^0,
- -- ["whatever"] = line + esc * 1 + C(P("%") * (1-line)^0),
- -- ["grouped"] = CP() * C(l_g * (V("whatever") + V("grouped") + V("setup") + V("display") + V("inline") + (1 - l_g - r_g))^0 * r_g) * CC("group") / progress,
- -- ["setup"] = CP() * C(l_s * (V("whatever") + V("grouped") + V("setup") + V("display") + V("inline") + (1 - l_s - r_s))^0 * r_s) * CC("setup") / progress,
- -- ["display"] = CP() * C(d_m * (V("whatever") + V("grouped") + (1 - d_m))^0 * d_m) * CC("display") / progress,
- -- ["inline"] = CP() * C(i_m * (V("whatever") + V("grouped") + (1 - i_m))^0 * i_m) * CC("inline") / progress,
- -- ["errors"] = (V("gerror") + V("serror") + V("derror") + V("ierror")) * true,
- -- ["gerror"] = CP() * (l_g + r_g) * CC("grouping") / message,
- -- ["serror"] = CP() * (l_s + r_g) * CC("setup error") / message,
- -- ["derror"] = CP() * d_m * CC("display math error") / message,
- -- ["ierror"] = CP() * i_m * CC("inline math error") / message,
- -- }
-
- local startluacode = P("\\startluacode")
- local stopluacode = P("\\stopluacode")
-
- local somecode = startluacode * (1-stopluacode)^1 * stopluacode
-
- local grammar = P { "tokens",
- ["tokens"] = (V("ignore") + V("whatever") + V("grouped") + V("setup") + V("display") + V("inline") + V("errors") + 1)^0,
- ["whatever"] = line + esc * 1 + C(P("%") * (1-line)^0),
- ["grouped"] = l_g * (V("whatever") + V("grouped") + V("setup") + V("display") + V("inline") + (1 - l_g - r_g))^0 * r_g,
- ["setup"] = l_s * (okay + V("whatever") + V("grouped") + V("setup") + V("display") + V("inline") + (1 - l_s - r_s))^0 * r_s,
- ["display"] = d_m * (V("whatever") + V("grouped") + (1 - d_m))^0 * d_m,
- ["inline"] = i_m * (V("whatever") + V("grouped") + (1 - i_m))^0 * i_m,
- ["errors"] = (V("gerror")+ V("serror") + V("derror") + V("ierror")),
- ["gerror"] = CP() * (l_g + r_g) * CC("grouping") / message,
- ["serror"] = CP() * (l_s + r_g) * CC("setup error") / message,
- ["derror"] = CP() * d_m * CC("display math error") / message,
- ["ierror"] = CP() * i_m * CC("inline math error") / message,
- ["ignore"] = somecode,
- }
-
- function validator.check(str)
- validator.n = 1
- validator.errors = { }
- grammar:match(str)
- end
+local P, R, S, V, C, CP, CC = lpeg.P, lpeg.R, lpeg.S, lpeg.V, lpeg.C, lpeg.Cp, lpeg.Cc
+
+local i_m, d_m = P("$"), P("$$")
+local l_s, r_s = P("["), P("]")
+local l_g, r_g = P("{"), P("}")
+
+local okay = lpeg.P("{[}") + lpeg.P("{]}")
+
+local esc = P("\\")
+local cr = P("\r")
+local lf = P("\n")
+local crlf = P("\r\n")
+local space = S(" \t\f\v")
+local newline = crlf + cr + lf
+
+local line = newline / function() validator.n = validator.n + 1 end
+
+-- local grammar = P { "tokens",
+-- ["tokens"] = (V("whatever") + V("grouped") + V("setup") + V("display") + V("inline") + V("errors") + 1)^0,
+-- ["whatever"] = line + esc * 1 + C(P("%") * (1-line)^0),
+-- ["grouped"] = CP() * C(l_g * (V("whatever") + V("grouped") + V("setup") + V("display") + V("inline") + (1 - l_g - r_g))^0 * r_g) * CC("group") / progress,
+-- ["setup"] = CP() * C(l_s * (V("whatever") + V("grouped") + V("setup") + V("display") + V("inline") + (1 - l_s - r_s))^0 * r_s) * CC("setup") / progress,
+-- ["display"] = CP() * C(d_m * (V("whatever") + V("grouped") + (1 - d_m))^0 * d_m) * CC("display") / progress,
+-- ["inline"] = CP() * C(i_m * (V("whatever") + V("grouped") + (1 - i_m))^0 * i_m) * CC("inline") / progress,
+-- ["errors"] = (V("gerror") + V("serror") + V("derror") + V("ierror")) * true,
+-- ["gerror"] = CP() * (l_g + r_g) * CC("grouping") / message,
+-- ["serror"] = CP() * (l_s + r_g) * CC("setup error") / message,
+-- ["derror"] = CP() * d_m * CC("display math error") / message,
+-- ["ierror"] = CP() * i_m * CC("inline math error") / message,
+-- }
+
+local startluacode = P("\\startluacode")
+local stopluacode = P("\\stopluacode")
+
+local somecode = startluacode * (1-stopluacode)^1 * stopluacode
+
+local grammar = P { "tokens",
+ ["tokens"] = (V("ignore") + V("whatever") + V("grouped") + V("setup") + V("display") + V("inline") + V("errors") + 1)^0,
+ ["whatever"] = line + esc * 1 + C(P("%") * (1-line)^0),
+ ["grouped"] = l_g * (V("whatever") + V("grouped") + V("setup") + V("display") + V("inline") + (1 - l_g - r_g))^0 * r_g,
+ ["setup"] = l_s * (okay + V("whatever") + V("grouped") + V("setup") + V("display") + V("inline") + (1 - l_s - r_s))^0 * r_s,
+ ["display"] = d_m * (V("whatever") + V("grouped") + (1 - d_m))^0 * d_m,
+ ["inline"] = i_m * (V("whatever") + V("grouped") + (1 - i_m))^0 * i_m,
+ ["errors"] = (V("gerror")+ V("serror") + V("derror") + V("ierror")),
+ ["gerror"] = CP() * (l_g + r_g) * CC("grouping") / message,
+ ["serror"] = CP() * (l_s + r_g) * CC("setup error") / message,
+ ["derror"] = CP() * d_m * CC("display math error") / message,
+ ["ierror"] = CP() * i_m * CC("inline math error") / message,
+ ["ignore"] = somecode,
+}
+function validator.check(str)
+ validator.n = 1
+ validator.errors = { }
+ grammar:match(str)
end
--~ str = [[
@@ -117,7 +125,7 @@ function scripts.checker.check(filename)
["\t"] = " <tab> ",
})
data = data:gsub("^ *","")
- print(string.format("% 5i %s %s", line,string.rpadd(kind,10," "),data))
+ print(string.format("% 5i %-10s %s", line, kind, data))
end
else
print("no error")
@@ -127,17 +135,13 @@ function scripts.checker.check(filename)
end
end
-logs.extendbanner("Basic ConTeXt Syntax Checking 0.10",true)
-
-messages.help = [[
---convert check tex file for errors
-]]
-
if environment.argument("check") then
scripts.checker.check(environment.files[1])
elseif environment.argument("help") then
- logs.help(messages.help)
+ application.help()
elseif environment.files[1] then
scripts.checker.check(environment.files[1])
+else
+ application.help()
end
diff --git a/Master/texmf-dist/scripts/context/lua/mtx-colors.lua b/Master/texmf-dist/scripts/context/lua/mtx-colors.lua
new file mode 100644
index 00000000000..2a51d50990e
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/lua/mtx-colors.lua
@@ -0,0 +1,57 @@
+if not modules then modules = { } end modules ['mtx-colors'] = {
+ version = 1.001,
+ comment = "companion to mtxrun.lua",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- todo: fc-cache -v en check dirs, or better is: fc-cat -v | grep Directory
+
+local helpinfo = [[
+--table show icc table
+
+example:
+
+mtxrun --script color --table somename
+]]
+
+local application = logs.application {
+ name = "mtx-colors",
+ banner = "ConTeXt Color Management 0.10",
+ helpinfo = helpinfo,
+}
+
+local report = application.report
+
+if not fontloader then fontloader = fontforge end
+
+dofile(resolvers.findfile("colo-icc.lua","tex"))
+
+scripts = scripts or { }
+scripts.colors = scripts.colors or { }
+
+function scripts.colors.table()
+ local files = environment.files
+ if #files > 0 then
+ for i=1,#files do
+ local profile, okay, message = colors.iccprofile(files[i])
+ if not okay then
+ report(message)
+ else
+ report(table.serialize(profile,"profile"))
+ end
+ end
+ else
+ report("no file(s) given" )
+ end
+end
+
+--~ local track = environment.argument("track")
+--~ if track then trackers.enable(track) end
+
+if environment.argument("table") then
+ scripts.colors.table()
+else
+ application.help()
+end
diff --git a/Master/texmf-dist/scripts/context/lua/mtx-context.lua b/Master/texmf-dist/scripts/context/lua/mtx-context.lua
index 79e74e407ff..5645e476050 100644
--- a/Master/texmf-dist/scripts/context/lua/mtx-context.lua
+++ b/Master/texmf-dist/scripts/context/lua/mtx-context.lua
@@ -6,6 +6,93 @@ if not modules then modules = { } end modules ['mtx-context'] = {
license = "see context related readme files"
}
+local format, gmatch, match, gsub, find = string.format, string.gmatch, string.match, string.gsub, string.find
+local quote = string.quote
+local concat = table.concat
+
+local basicinfo = [[
+--run process (one or more) files (default action)
+--make create context formats
+
+--ctx=name use ctx file (process management specification)
+--interface use specified user interface (default: en)
+
+--autopdf close pdf file in viewer and start pdf viewer afterwards
+--purge(all) purge files either or not after a run (--pattern=...)
+
+--usemodule=list load the given module or style, normally part o fthe distribution
+--environment=list load the given environment file first (document styles)
+--mode=list enable given the modes (conditional processing in styles)
+--path=list also consult the given paths when files are looked for
+--arguments=list set variables that can be consulted during a run (key/value pairs)
+--randomseed=number set the randomseed
+--result=name rename the resulting output to the given name
+--trackers=list set tracker variables (show list with --showtrackers)
+--directives=list set directive variables (show list with --showdirectives)
+--silent=list disable logcatgories (show list with --showlogcategories)
+--noconsole disable logging to the console (logfile only)
+--purgeresult purge result file before run
+
+--forcexml force xml stub (optional flag: --mkii)
+--forcecld force cld (context lua document) stub
+
+--arrange run extra imposition pass, given that the style sets up imposition
+--noarrange ignore imposition specifications in the style
+
+--once only run once (no multipass data file is produced)
+--batchmode run without stopping and don't show messages on the console
+--nonstopmode run without stopping
+
+--generate generate file database etc. (as luatools does)
+--paranoid don't descend to .. and ../..
+--version report installed context version
+
+--expert expert options
+]]
+
+-- filter=list is kind of obsolete
+-- color is obsolete for mkiv, always on
+-- separation is obsolete for mkiv, no longer available
+-- output is currently obsolete for mkiv
+-- setuppath=list must check
+-- modefile=name must check
+-- input=name load the given inputfile (must check)
+
+local expertinfo = [[
+expert options:
+
+--touch update context version number (remake needed afterwards, also provide --expert)
+--nostats omit runtime statistics at the end of the run
+--update update context from website (not to be confused with contextgarden)
+--profile profile job (use: mtxrun --script profile --analyze)
+--timing generate timing and statistics overview
+--tracefiles show some extra info when locating files (at the tex end)
+
+--extra=name process extra (mtx-context-<name> in distribution)
+--extras show extras
+]]
+
+local specialinfo = [[
+special options:
+
+--pdftex process file with texexec using pdftex
+--xetex process file with texexec using xetex
+
+--pipe don't check for file and enter scroll mode (--dummyfile=whatever.tmp)
+]]
+
+local application = logs.application {
+ name = "mtx-context",
+ banner = "ConTeXt Process Management 0.52",
+ helpinfo = {
+ basic = basicinfo,
+ extra = extrainfo,
+ expert = expertinfo,
+ }
+}
+
+local report = application.report
+
scripts = scripts or { }
scripts.context = scripts.context or { }
@@ -30,334 +117,334 @@ function io.copydata(fromfile,tofile)
io.savedata(tofile,io.loaddata(fromfile) or "")
end
--- ctx
-
-ctxrunner = { }
-
-do
-
- function ctxrunner.filtered(str,method)
- str = tostring(str)
- if method == 'name' then str = file.removesuffix(file.basename(str))
- elseif method == 'path' then str = file.dirname(str)
- elseif method == 'suffix' then str = file.extname(str)
- elseif method == 'nosuffix' then str = file.removesuffix(str)
- elseif method == 'nopath' then str = file.basename(str)
- elseif method == 'base' then str = file.basename(str)
- -- elseif method == 'full' then
- -- elseif method == 'complete' then
- -- elseif method == 'expand' then -- str = file.expand_path(str)
- end
- return str:gsub("\\","/")
+-- ctx (will become util-ctx)
+
+local ctxrunner = { }
+
+function ctxrunner.filtered(str,method)
+ str = tostring(str)
+ if method == 'name' then str = file.removesuffix(file.basename(str))
+ elseif method == 'path' then str = file.dirname(str)
+ elseif method == 'suffix' then str = file.extname(str)
+ elseif method == 'nosuffix' then str = file.removesuffix(str)
+ elseif method == 'nopath' then str = file.basename(str)
+ elseif method == 'base' then str = file.basename(str)
+-- elseif method == 'full' then
+-- elseif method == 'complete' then
+-- elseif method == 'expand' then -- str = file.expandpath(str)
end
+ return str:gsub("\\","/")
+end
- function ctxrunner.substitute(e,str)
- local attributes = e.at
- if str and attributes then
- if attributes['method'] then
- str = ctxrunner.filtered(str,attributes['method'])
- end
- if str == "" and attributes['default'] then
- str = attributes['default']
- end
+function ctxrunner.substitute(e,str)
+ local attributes = e.at
+ if str and attributes then
+ if attributes['method'] then
+ str = ctxrunner.filtered(str,attributes['method'])
+ end
+ if str == "" and attributes['default'] then
+ str = attributes['default']
end
- return str
end
+ return str
+end
- function ctxrunner.reflag(flags)
- local t = { }
- for _, flag in next, flags do
- local key, value = flag:match("^(.-)=(.+)$")
- if key and value then
- t[key] = value
- else
- t[flag] = true
- end
+function ctxrunner.reflag(flags)
+ local t = { }
+ for _, flag in next, flags do
+ local key, value = match(flag,"^(.-)=(.+)$")
+ if key and value then
+ t[key] = value
+ else
+ t[flag] = true
end
- return t
end
+ return t
+end
- function ctxrunner.substitute(str)
- return str
- end
+function ctxrunner.substitute(str)
+ return str
+end
- function ctxrunner.justtext(str)
- str = xml.unescaped(tostring(str))
- str = xml.cleansed(str)
- str = str:gsub("\\+",'/')
- str = str:gsub("%s+",' ')
- return str
- end
+function ctxrunner.justtext(str)
+ str = xml.unescaped(tostring(str))
+ str = xml.cleansed(str)
+ str = str:gsub("\\+",'/')
+ str = str:gsub("%s+",' ')
+ return str
+end
- function ctxrunner.new()
- return {
- ctxname = "",
- jobname = "",
- xmldata = nil,
- suffix = "prep",
- locations = { '..', '../..' },
- variables = { },
- messages = { },
- environments = { },
- modules = { },
- filters = { },
- flags = { },
- modes = { },
- prepfiles = { },
- paths = { },
- }
- end
+function ctxrunner.new()
+ return {
+ ctxname = "",
+ jobname = "",
+ xmldata = nil,
+ suffix = "prep",
+ locations = { '..', '../..' },
+ variables = { },
+ messages = { },
+ environments = { },
+ modules = { },
+ filters = { },
+ flags = { },
+ modes = { },
+ prepfiles = { },
+ paths = { },
+ }
+end
- function ctxrunner.savelog(ctxdata,ctlname)
- local function yn(b)
- if b then return 'yes' else return 'no' end
- end
- if not ctlname or ctlname == "" or ctlname == ctxdata.jobname then
- if ctxdata.jobname then
- ctlname = file.replacesuffix(ctxdata.jobname,'ctl')
- elseif ctxdata.ctxname then
- ctlname = file.replacesuffix(ctxdata.ctxname,'ctl')
- else
- logs.simple("invalid ctl name: %s",ctlname or "?")
- return
- end
+function ctxrunner.savelog(ctxdata,ctlname)
+ local function yn(b)
+ if b then return 'yes' else return 'no' end
+ end
+ if not ctlname or ctlname == "" or ctlname == ctxdata.jobname then
+ if ctxdata.jobname then
+ ctlname = file.replacesuffix(ctxdata.jobname,'ctl')
+ elseif ctxdata.ctxname then
+ ctlname = file.replacesuffix(ctxdata.ctxname,'ctl')
+ else
+ report("invalid ctl name: %s",ctlname or "?")
+ return
end
- local prepfiles = ctxdata.prepfiles
- if prepfiles and next(prepfiles) then
- logs.simple("saving logdata in: %s",ctlname)
- f = io.open(ctlname,'w')
- if f then
- f:write("<?xml version='1.0' standalone='yes'?>\n\n")
- f:write(string.format("<ctx:preplist local='%s'>\n",yn(ctxdata.runlocal)))
- local sorted = table.sortedkeys(prepfiles)
- for i=1,#sorted do
- local name = sorted[i]
- f:write(string.format("\t<ctx:prepfile done='%s'>%s</ctx:prepfile>\n",yn(prepfiles[name]),name))
- end
- f:write("</ctx:preplist>\n")
- f:close()
+ end
+ local prepfiles = ctxdata.prepfiles
+ if prepfiles and next(prepfiles) then
+ report("saving logdata in: %s",ctlname)
+ f = io.open(ctlname,'w')
+ if f then
+ f:write("<?xml version='1.0' standalone='yes'?>\n\n")
+ f:write(format("<ctx:preplist local='%s'>\n",yn(ctxdata.runlocal)))
+ local sorted = table.sortedkeys(prepfiles)
+ for i=1,#sorted do
+ local name = sorted[i]
+ f:write(format("\t<ctx:prepfile done='%s'>%s</ctx:prepfile>\n",yn(prepfiles[name]),name))
end
- else
- logs.simple("nothing prepared, no ctl file saved")
- os.remove(ctlname)
+ f:write("</ctx:preplist>\n")
+ f:close()
end
+ else
+ report("nothing prepared, no ctl file saved")
+ os.remove(ctlname)
end
+end
- function ctxrunner.register_path(ctxdata,path)
- -- test if exists
- ctxdata.paths[ctxdata.paths+1] = path
- end
+function ctxrunner.register_path(ctxdata,path)
+ -- test if exists
+ ctxdata.paths[ctxdata.paths+1] = path
+end
- function ctxrunner.trace(ctxdata)
- print(table.serialize(ctxdata.messages))
- print(table.serialize(ctxdata.flags))
- print(table.serialize(ctxdata.environments))
- print(table.serialize(ctxdata.modules))
- print(table.serialize(ctxdata.filters))
- print(table.serialize(ctxdata.modes))
- print(xml.tostring(ctxdata.xmldata))
- end
+function ctxrunner.trace(ctxdata)
+ print(table.serialize(ctxdata.messages))
+ print(table.serialize(ctxdata.flags))
+ print(table.serialize(ctxdata.environments))
+ print(table.serialize(ctxdata.modules))
+ print(table.serialize(ctxdata.filters))
+ print(table.serialize(ctxdata.modes))
+ print(xml.tostring(ctxdata.xmldata))
+end
- function ctxrunner.manipulate(ctxdata,ctxname,defaultname)
+function ctxrunner.manipulate(ctxdata,ctxname,defaultname)
- if not ctxdata.jobname or ctxdata.jobname == "" then
- return
- end
+ if not ctxdata.jobname or ctxdata.jobname == "" then
+ return
+ end
- ctxdata.ctxname = ctxname or file.removesuffix(ctxdata.jobname) or ""
+ ctxdata.ctxname = ctxname or file.removesuffix(ctxdata.jobname) or ""
- if ctxdata.ctxname == "" then
- return
- end
+ if ctxdata.ctxname == "" then
+ return
+ end
- ctxdata.jobname = file.addsuffix(ctxdata.jobname,'tex')
- ctxdata.ctxname = file.addsuffix(ctxdata.ctxname,'ctx')
+ ctxdata.jobname = file.addsuffix(ctxdata.jobname,'tex')
+ ctxdata.ctxname = file.addsuffix(ctxdata.ctxname,'ctx')
- logs.simple("jobname: %s",ctxdata.jobname)
- logs.simple("ctxname: %s",ctxdata.ctxname)
+ report("jobname: %s",ctxdata.jobname)
+ report("ctxname: %s",ctxdata.ctxname)
- -- mtxrun should resolve kpse: and file:
+ -- mtxrun should resolve kpse: and file:
- local usedname = ctxdata.ctxname
- local found = lfs.isfile(usedname)
+ local usedname = ctxdata.ctxname
+ local found = lfs.isfile(usedname)
- if not found then
- for _, path in next, ctxdata.locations do
- local fullname = file.join(path,ctxdata.ctxname)
- if lfs.isfile(fullname) then
- usedname, found = fullname, true
- break
- end
+ -- no futher test if qualified path
+
+ if not found then
+ for _, path in next, ctxdata.locations do
+ local fullname = file.join(path,ctxdata.ctxname)
+ if lfs.isfile(fullname) then
+ usedname, found = fullname, true
+ break
end
end
+ end
- usedname = resolvers.find_file(ctxdata.ctxname,"tex")
+ if not found then
+ usedname = resolvers.findfile(ctxdata.ctxname,"tex")
found = usedname ~= ""
+ end
- if not found and defaultname and defaultname ~= "" and lfs.isfile(defaultname) then
- usedname, found = defaultname, true
- end
+ if not found and defaultname and defaultname ~= "" and lfs.isfile(defaultname) then
+ usedname, found = defaultname, true
+ end
- if not found then
- return
- end
+ if not found then
+ return
+ end
- ctxdata.xmldata = xml.load(usedname)
+ ctxdata.xmldata = xml.load(usedname)
- if not ctxdata.xmldata then
- return
- else
- -- test for valid, can be text file
- end
+ if not ctxdata.xmldata then
+ return
+ else
+ -- test for valid, can be text file
+ end
- xml.include(ctxdata.xmldata,'ctx:include','name', table.append({'.', file.dirname(ctxdata.ctxname)},ctxdata.locations))
+ xml.include(ctxdata.xmldata,'ctx:include','name', table.append({'.', file.dirname(ctxdata.ctxname)},ctxdata.locations))
- ctxdata.variables['job'] = ctxdata.jobname
+ ctxdata.variables['job'] = ctxdata.jobname
- ctxdata.flags = xml.collect_texts(ctxdata.xmldata,"/ctx:job/ctx:flags/ctx:flag",true)
- ctxdata.environments = xml.collect_texts(ctxdata.xmldata,"/ctx:job/ctx:process/ctx:resources/ctx:environment",true)
- ctxdata.modules = xml.collect_texts(ctxdata.xmldata,"/ctx:job/ctx:process/ctx:resources/ctx:module",true)
- ctxdata.filters = xml.collect_texts(ctxdata.xmldata,"/ctx:job/ctx:process/ctx:resources/ctx:filter",true)
- ctxdata.modes = xml.collect_texts(ctxdata.xmldata,"/ctx:job/ctx:process/ctx:resources/ctx:mode",true)
- ctxdata.messages = xml.collect_texts(ctxdata.xmldata,"ctx:message",true)
+ ctxdata.flags = xml.collect_texts(ctxdata.xmldata,"/ctx:job/ctx:flags/ctx:flag",true)
+ ctxdata.environments = xml.collect_texts(ctxdata.xmldata,"/ctx:job/ctx:process/ctx:resources/ctx:environment",true)
+ ctxdata.modules = xml.collect_texts(ctxdata.xmldata,"/ctx:job/ctx:process/ctx:resources/ctx:module",true)
+ ctxdata.filters = xml.collect_texts(ctxdata.xmldata,"/ctx:job/ctx:process/ctx:resources/ctx:filter",true)
+ ctxdata.modes = xml.collect_texts(ctxdata.xmldata,"/ctx:job/ctx:process/ctx:resources/ctx:mode",true)
+ ctxdata.messages = xml.collect_texts(ctxdata.xmldata,"ctx:message",true)
- ctxdata.flags = ctxrunner.reflag(ctxdata.flags)
+ ctxdata.flags = ctxrunner.reflag(ctxdata.flags)
- local messages = ctxdata.messages
- for i=1,#messages do
- logs.simple("ctx comment: %s", xml.tostring(messages[i]))
- end
+ local messages = ctxdata.messages
+ for i=1,#messages do
+ report("ctx comment: %s", xml.tostring(messages[i]))
+ end
- for r, d, k in xml.elements(ctxdata.xmldata,"ctx:value[@name='job']") do
- d[k] = ctxdata.variables['job'] or ""
- end
+ for r, d, k in xml.elements(ctxdata.xmldata,"ctx:value[@name='job']") do
+ d[k] = ctxdata.variables['job'] or ""
+ end
- local commands = { }
- for e in xml.collected(ctxdata.xmldata,"/ctx:job/ctx:preprocess/ctx:processors/ctx:processor") do
- commands[e.at and e.at['name'] or "unknown"] = e
- end
+ local commands = { }
+ for e in xml.collected(ctxdata.xmldata,"/ctx:job/ctx:preprocess/ctx:processors/ctx:processor") do
+ commands[e.at and e.at['name'] or "unknown"] = e
+ end
- local suffix = xml.filter(ctxdata.xmldata,"/ctx:job/ctx:preprocess/attribute('suffix')") or ctxdata.suffix
- local runlocal = xml.filter(ctxdata.xmldata,"/ctx:job/ctx:preprocess/ctx:processors/attribute('local')")
+ local suffix = xml.filter(ctxdata.xmldata,"/ctx:job/ctx:preprocess/attribute('suffix')") or ctxdata.suffix
+ local runlocal = xml.filter(ctxdata.xmldata,"/ctx:job/ctx:preprocess/ctx:processors/attribute('local')")
- runlocal = toboolean(runlocal)
+ runlocal = toboolean(runlocal)
- for files in xml.collected(ctxdata.xmldata,"/ctx:job/ctx:preprocess/ctx:files") do
- for pattern in xml.collected(files,"ctx:file") do
+ for files in xml.collected(ctxdata.xmldata,"/ctx:job/ctx:preprocess/ctx:files") do
+ for pattern in xml.collected(files,"ctx:file") do
- preprocessor = pattern.at['processor'] or ""
+ preprocessor = pattern.at['processor'] or ""
- if preprocessor ~= "" then
+ if preprocessor ~= "" then
- ctxdata.variables['old'] = ctxdata.jobname
- for r, d, k in xml.elements(ctxdata.xmldata,"ctx:value") do
- local ek = d[k]
- local ekat = ek.at['name']
- if ekat == 'old' then
- d[k] = ctxrunner.substitute(ctxdata.variables[ekat] or "")
- end
+ ctxdata.variables['old'] = ctxdata.jobname
+ for r, d, k in xml.elements(ctxdata.xmldata,"ctx:value") do
+ local ek = d[k]
+ local ekat = ek.at['name']
+ if ekat == 'old' then
+ d[k] = ctxrunner.substitute(ctxdata.variables[ekat] or "")
end
+ end
- pattern = ctxrunner.justtext(xml.tostring(pattern))
+ pattern = ctxrunner.justtext(xml.tostring(pattern))
- local oldfiles = dir.glob(pattern)
+ local oldfiles = dir.glob(pattern)
- local pluspath = false
- if #oldfiles == 0 then
- -- message: no files match pattern
- local paths = ctxdata.paths
- for i=1,#paths do
- local p = paths[i]
- local oldfiles = dir.glob(path.join(p,pattern))
- if #oldfiles > 0 then
- pluspath = true
- break
- end
+ local pluspath = false
+ if #oldfiles == 0 then
+ -- message: no files match pattern
+ local paths = ctxdata.paths
+ for i=1,#paths do
+ local p = paths[i]
+ local oldfiles = dir.glob(path.join(p,pattern))
+ if #oldfiles > 0 then
+ pluspath = true
+ break
end
end
- if #oldfiles == 0 then
- -- message: no old files
- else
- for i=1,#oldfiles do
- local oldfile = oldfiles[i]
- local newfile = oldfile .. "." .. suffix -- addsuffix will add one only
- if ctxdata.runlocal then
- newfile = file.basename(newfile)
- end
- if oldfile ~= newfile and file.needsupdate(oldfile,newfile) then
- -- message: oldfile needs preprocessing
- -- os.remove(newfile)
- local splitted = preprocessor:split(',')
- for i=1,#splitted do
- local pp = splitted[i]
- local command = commands[pp]
- if command then
- command = xml.copy(command)
- local suf = (command.at and command.at['suffix']) or ctxdata.suffix
- if suf then
- newfile = oldfile .. "." .. suf
- end
- if ctxdata.runlocal then
- newfile = file.basename(newfile)
- end
- for r, d, k in xml.elements(command,"ctx:old") do
- d[k] = ctxrunner.substitute(oldfile)
- end
- for r, d, k in xml.elements(command,"ctx:new") do
- d[k] = ctxrunner.substitute(newfile)
- end
- ctxdata.variables['old'] = oldfile
- ctxdata.variables['new'] = newfile
- for r, d, k in xml.elements(command,"ctx:value") do
- local ek = d[k]
- local ekat = ek.at and ek.at['name']
- if ekat then
- d[k] = ctxrunner.substitute(ctxdata.variables[ekat] or "")
- end
- end
- -- potential optimization: when mtxrun run internal
- command = xml.content(command)
- command = ctxrunner.justtext(command)
- logs.simple("command: %s",command)
- local result = os.spawn(command) or 0
- -- somehow we get the wrong return value
- if result > 0 then
- logs.simple("error, return code: %s",result)
- end
- if ctxdata.runlocal then
- oldfile = file.basename(oldfile)
+ end
+ if #oldfiles == 0 then
+ -- message: no old files
+ else
+ for i=1,#oldfiles do
+ local oldfile = oldfiles[i]
+ local newfile = oldfile .. "." .. suffix -- addsuffix will add one only
+ if ctxdata.runlocal then
+ newfile = file.basename(newfile)
+ end
+ if oldfile ~= newfile and file.needsupdate(oldfile,newfile) then
+ -- message: oldfile needs preprocessing
+ -- os.remove(newfile)
+ local splitted = preprocessor:split(',')
+ for i=1,#splitted do
+ local pp = splitted[i]
+ local command = commands[pp]
+ if command then
+ command = xml.copy(command)
+ local suf = (command.at and command.at['suffix']) or ctxdata.suffix
+ if suf then
+ newfile = oldfile .. "." .. suf
+ end
+ if ctxdata.runlocal then
+ newfile = file.basename(newfile)
+ end
+ for r, d, k in xml.elements(command,"ctx:old") do
+ d[k] = ctxrunner.substitute(oldfile)
+ end
+ for r, d, k in xml.elements(command,"ctx:new") do
+ d[k] = ctxrunner.substitute(newfile)
+ end
+ ctxdata.variables['old'] = oldfile
+ ctxdata.variables['new'] = newfile
+ for r, d, k in xml.elements(command,"ctx:value") do
+ local ek = d[k]
+ local ekat = ek.at and ek.at['name']
+ if ekat then
+ d[k] = ctxrunner.substitute(ctxdata.variables[ekat] or "")
end
end
+ -- potential optimization: when mtxrun run internal
+ command = xml.content(command)
+ command = ctxrunner.justtext(command)
+ report("command: %s",command)
+ local result = os.spawn(command) or 0
+ -- somehow we get the wrong return value
+ if result > 0 then
+ report("error, return code: %s",result)
+ end
+ if ctxdata.runlocal then
+ oldfile = file.basename(oldfile)
+ end
end
- if lfs.isfile(newfile) then
- file.syncmtimes(oldfile,newfile)
- ctxdata.prepfiles[oldfile] = true
- else
- logs.simple("error, check target location of new file: %s", newfile)
- ctxdata.prepfiles[oldfile] = false
- end
+ end
+ if lfs.isfile(newfile) then
+ file.syncmtimes(oldfile,newfile)
+ ctxdata.prepfiles[oldfile] = true
else
- logs.simple("old file needs no preprocessing")
- ctxdata.prepfiles[oldfile] = lfs.isfile(newfile)
+ report("error, check target location of new file: %s", newfile)
+ ctxdata.prepfiles[oldfile] = false
end
+ else
+ report("old file needs no preprocessing")
+ ctxdata.prepfiles[oldfile] = lfs.isfile(newfile)
end
end
end
end
end
+ end
- ctxrunner.savelog(ctxdata)
+ ctxrunner.savelog(ctxdata)
- end
+end
- function ctxrunner.preppedfile(ctxdata,filename)
- if ctxdata.prepfiles[file.basename(filename)] then
- return filename .. ".prep"
- else
- return filename
- end
+function ctxrunner.preppedfile(ctxdata,filename)
+ if ctxdata.prepfiles[file.basename(filename)] then
+ return filename .. ".prep"
+ else
+ return filename
end
-
end
-- rest
@@ -388,14 +475,6 @@ function scripts.context.multipass.changed(oldhash, newhash)
return false
end
-scripts.context.backends = {
- pdftex = 'pdftex',
- luatex = 'pdftex',
- pdf = 'pdftex',
- dvi = 'dvipdfmx',
- dvips = 'dvips'
-}
-
function scripts.context.multipass.makeoptionfile(jobname,ctxdata,kindofrun,currentrun,finalrun)
-- take jobname from ctx
jobname = file.removesuffix(jobname)
@@ -404,62 +483,51 @@ function scripts.context.multipass.makeoptionfile(jobname,ctxdata,kindofrun,curr
local function someflag(flag)
return (ctxdata and ctxdata.flags[flag]) or environment.argument(flag)
end
- local function setvalue(flag,format,hash,default)
+ local function setvalue(flag,template,hash,default)
local a = someflag(flag) or default
if a and a ~= "" then
if hash then
if hash[a] then
- f:write(format:format(a),"\n")
+ f:write(format(template,a),"\n")
end
else
- f:write(format:format(a),"\n")
+ f:write(format(template,a),"\n")
end
end
end
- local function setvalues(flag,format,plural)
+ local function setvalues(flag,template,plural)
if type(flag) == "table" then
for k, v in next, flag do
- f:write(format:format(v),"\n")
+ f:write(format(template,v),"\n")
end
else
local a = someflag(flag) or (plural and someflag(flag.."s"))
if a and a ~= "" then
- for v in a:gmatch("%s*([^,]+)") do
- f:write(format:format(v),"\n")
+ for v in gmatch(a,"%s*([^,]+)") do
+ f:write(format(template,v),"\n")
end
end
end
end
- local function setfixed(flag,format,...)
+ local function setfixed(flag,template,...)
if someflag(flag) then
- f:write(format:format(...),"\n")
+ f:write(format(template,...),"\n")
end
end
- local function setalways(format,...)
- f:write(format:format(...),"\n")
+ local function setalways(template,...)
+ f:write(format(template,...),"\n")
end
--
+ -- This might change ... we can just pass the relevant flags directly.
+ --
setalways("%% runtime options files (command line driven)")
--
setalways("\\unprotect")
--
- setalways("%% special commands, mostly for the ctx development team")
- --
- if environment.argument("dumpdelta") then
- setalways("\\tracersdumpdelta")
- elseif environment.argument("dumphash") then
- setalways("\\tracersdumphash")
- end
setalways("%% feedback and basic job control")
- if type(environment.argument("track")) == "string" then
- setvalue ("track" , "\\enabletrackers[%s]")
- end
- if type(environment.argument("trackers")) == "string" then
- setvalue ("trackers" , "\\enabletrackers[%s]")
- end
- if type(environment.argument("directives")) == "string" then
- setvalue ("directives", "\\enabledirectives[%s]")
- end
+ --
+ -- Option file, we can pass more on the commandline some day soon.
+ --
setfixed ("timing" , "\\usemodule[timing]")
setfixed ("batchmode" , "\\batchmode")
setfixed ("batch" , "\\batchmode")
@@ -479,11 +547,9 @@ function scripts.context.multipass.makeoptionfile(jobname,ctxdata,kindofrun,curr
--
setalways("%% process info")
--
- -- setvalue ("inputfile" , "\\setupsystem[inputfile=%s]")
setalways( "\\setupsystem[inputfile=%s]",environment.argument("input") or environment.files[1] or "\\jobname")
setvalue ("result" , "\\setupsystem[file=%s]")
setalways( "\\setupsystem[\\c!n=%s,\\c!m=%s]", kindofrun or 0, currentrun or 0)
- -- setalways( "\\setupsystem[\\c!type=%s]",os.type) -- windows or unix
setvalues("path" , "\\usepath[%s]")
setvalue ("setuppath" , "\\setupsystem[\\c!directory={%s}]")
setvalue ("randomseed" , "\\setupsystem[\\c!random=%s]")
@@ -498,7 +564,6 @@ function scripts.context.multipass.makeoptionfile(jobname,ctxdata,kindofrun,curr
setalways("%% options (not that important)")
--
setalways("\\startsetups *runtime:options")
- setvalue ('output' , "\\setupoutput[%s]", scripts.context.backends, 'pdftex')
setfixed ("color" , "\\setupcolors[\\c!state=\\v!start]")
setvalue ("separation" , "\\setupcolors[\\c!split=%s]")
setfixed ("noarrange" , "\\setuparranging[\\v!disable]")
@@ -510,7 +575,6 @@ function scripts.context.multipass.makeoptionfile(jobname,ctxdata,kindofrun,curr
setalways("%% styles and modules")
--
setalways("\\startsetups *runtime:modules")
- setvalues("filter" , "\\useXMLfilter[%s]", true)
setvalues("usemodule" , "\\usemodule[%s]", true)
setvalues("environment" , "\\environment %s ", true)
if ctxdata then
@@ -527,7 +591,6 @@ function scripts.context.multipass.makeoptionfile(jobname,ctxdata,kindofrun,curr
end
function scripts.context.multipass.copyluafile(jobname)
--- io.savedata(jobname..".tuc",io.loaddata(jobname..".tua") or "")
local tuaname, tucname = jobname..".tua", jobname..".tuc"
if lfs.isfile(tuaname) then
os.remove(tucname)
@@ -535,28 +598,6 @@ function scripts.context.multipass.copyluafile(jobname)
end
end
--- obsolete:
---
--- function scripts.context.multipass.copytuifile(jobname)
--- local tuiname, tuoname = jobname .. ".tui", jobname .. ".tuo"
--- if lfs.isfile(tuiname) then
--- local f, g = io.open(tuiname), io.open(tuoname,'w')
--- if f and g then
--- g:write("% traditional utility file, only commands written by mtxrun/context\n%\n")
--- for line in f:lines() do
--- if line:find("^c ") then
--- g:write((line:gsub("^c ","")),"%\n")
--- end
--- end
--- g:write("\\endinput\n")
--- f:close()
--- g:close()
--- end
--- else
--- -- os.remove(tuoname)
--- end
--- end
-
scripts.context.cldsuffixes = table.tohash {
"cld",
}
@@ -591,20 +632,20 @@ scripts.context.interfaces = {
scripts.context.defaultformats = {
"cont-en",
"cont-nl",
- "mptopdf",
--- "metatex",
- "metafun",
+-- "mptopdf", -- todo: mak emkiv variant
+-- "metatex", -- will show up soon
+-- "metafun", -- todo: mp formats
-- "plain"
}
-local function analyze(filename)
+local function analyze(filename) -- only files on current path
local f = io.open(file.addsuffix(filename,"tex"))
if f then
local t = { }
local line = f:read("*line") or ""
- local preamble = line:match("[\254\255]*%%%s+(.+)$") -- there can be an utf bomb in front
+ local preamble = match(line,"[\254\255]*%%%s+(.+)$") -- there can be an utf bomb in front
if preamble then
- for key, value in preamble:gmatch("(%S+)=(%S+)") do
+ for key, value in gmatch(preamble,"(%S+)=(%S+)") do
t[key] = value
end
t.type = "tex"
@@ -623,13 +664,17 @@ local function analyze(filename)
return nil
end
-local function makestub(format,filename,prepname)
+local function makestub(wrap,template,filename,prepname)
local stubname = file.replacesuffix(file.basename(filename),'run')
local f = io.open(stubname,'w')
if f then
- f:write("\\starttext\n")
- f:write(string.format(format,prepname or filename),"\n")
- f:write("\\stoptext\n")
+ if wrap then
+ f:write("\\starttext\n")
+ end
+ f:write(format(template,prepname or filename),"\n")
+ if wrap then
+ f:write("\\stoptext\n")
+ end
f:close()
filename = stubname
end
@@ -637,22 +682,24 @@ local function makestub(format,filename,prepname)
end
--~ function scripts.context.openpdf(name)
---~ os.spawn(string.format('pdfopen --file "%s" 2>&1', file.replacesuffix(name,"pdf")))
+--~ os.spawn(format('pdfopen --file "%s" 2>&1', file.replacesuffix(name,"pdf")))
--~ end
--~ function scripts.context.closepdf(name)
---~ os.spawn(string.format('pdfclose --file "%s" 2>&1', file.replacesuffix(name,"pdf")))
+--~ os.spawn(format('pdfclose --file "%s" 2>&1', file.replacesuffix(name,"pdf")))
--~ end
local pdfview -- delayed loading
-function scripts.context.openpdf(name)
- pdfview = pdfview or dofile(resolvers.find_file("l-pdfview.lua","tex"))
- logs.simple("pdfview methods: %s, current method: %s, MTX_PDFVIEW_METHOD=%s",pdfview.methods(),pdfview.method,os.getenv(pdfview.METHOD) or "<unset>")
+function scripts.context.openpdf(name,method)
+ pdfview = pdfview or dofile(resolvers.findfile("l-pdfview.lua","tex"))
+ pdfview.setmethod(method)
+ report(pdfview.status())
pdfview.open(file.replacesuffix(name,"pdf"))
end
-function scripts.context.closepdf(name)
- pdfview = pdfview or dofile(resolvers.find_file("l-pdfview.lua","tex"))
+function scripts.context.closepdf(name,method)
+ pdfview = pdfview or dofile(resolvers.findfile("l-pdfview.lua","tex"))
+ pdfview.setmethod(method)
pdfview.close(file.replacesuffix(name,"pdf"))
end
@@ -672,13 +719,12 @@ function scripts.context.run(ctxdata,filename)
interface = (type(interface) == "string" and interface) or "en"
--
local formatname = scripts.context.interfaces[interface] or "cont-en"
- local formatfile, scriptfile = resolvers.locate_format(formatname)
+ local formatfile, scriptfile = resolvers.locateformat(formatname)
-- this catches the command line
if not formatfile or not scriptfile then
- logs.simple("warning: no format found, forcing remake (commandline driven)")
- scripts.context.generate()
+ report("warning: no format found, forcing remake (commandline driven)")
scripts.context.make(formatname)
- formatfile, scriptfile = resolvers.locate_format(formatname)
+ formatfile, scriptfile = resolvers.locateformat(formatname)
end
--
if formatfile and scriptfile then
@@ -686,7 +732,7 @@ function scripts.context.run(ctxdata,filename)
local filename = files[i]
local basename, pathname = file.basename(filename), file.dirname(filename)
local jobname = file.removesuffix(basename)
- if pathname == "" then
+ if pathname == "" and not environment.argument("global") then
filename = "./" .. filename
end
-- look at the first line
@@ -698,24 +744,34 @@ function scripts.context.run(ctxdata,filename)
--
-- require "mtx-texutil.lua"
else
- local texexec = resolvers.find_file("texexec.rb") or ""
+ local texexec = resolvers.findfile("texexec.rb") or ""
if texexec ~= "" then
os.setenv("RUBYOPT","")
- local command = string.format("ruby %s %s",texexec,environment.reconstruct_commandline(environment.arguments_after))
- os.exec(command)
+ local options = environment.reconstructcommandline(environment.arguments_after)
+ options = gsub(options,"--purge","")
+ options = gsub(options,"--purgeall","")
+ local command = format("ruby %s %s",texexec,options)
+ if environment.argument("purge") then
+ os.execute(command)
+ scripts.context.purge_job(filename,false,true)
+ elseif environment.argument("purgeall") then
+ os.execute(command)
+ scripts.context.purge_job(filename,true,true)
+ else
+ os.exec(command)
+ end
end
end
else
if a and a.interface and a.interface ~= interface then
formatname = scripts.context.interfaces[a.interface] or formatname
- formatfile, scriptfile = resolvers.locate_format(formatname)
+ formatfile, scriptfile = resolvers.locateformat(formatname)
end
-- this catches the command line
if not formatfile or not scriptfile then
- logs.simple("warning: no format found, forcing remake (source driven)")
- scripts.context.generate()
+ report("warning: no format found, forcing remake (source driven)")
scripts.context.make(formatname)
- formatfile, scriptfile = resolvers.locate_format(formatname)
+ formatfile, scriptfile = resolvers.locateformat(formatname)
end
if formatfile and scriptfile then
-- we default to mkiv xml !
@@ -723,17 +779,18 @@ function scripts.context.run(ctxdata,filename)
local suffix = file.extname(filename) or "?"
if scripts.context.xmlsuffixes[suffix] or environment.argument("forcexml") then
if environment.argument("mkii") then
- filename = makestub("\\processXMLfilegrouped{%s}",filename)
+ filename = makestub(true,"\\processXMLfilegrouped{%s}",filename)
else
- filename = makestub("\\xmlprocess{\\xmldocument}{%s}{}",filename)
+ filename = makestub(true,"\\xmlprocess{\\xmldocument}{%s}{}",filename)
end
elseif scripts.context.cldsuffixes[suffix] or environment.argument("forcecld") then
- filename = makestub("\\ctxlua{context.runfile('%s')}",filename)
+ -- self contained cld files need to have a starttext/stoptext (less fontloading)
+ filename = makestub(false,"\\ctxlua{context.runfile('%s')}",filename)
elseif scripts.context.luasuffixes[suffix] or environment.argument("forcelua") then
- filename = makestub("\\ctxlua{dofile('%s')}",filename)
+ filename = makestub(true,"\\ctxlua{dofile('%s')}",filename)
elseif environment.argument("prep") then
-- we need to keep the original jobname
- filename = makestub("\\readfile{%s}{}{}",filename,ctxrunner.preppedfile(ctxdata,filename))
+ filename = makestub(true,"\\readfile{%s}{}{}",filename,ctxrunner.preppedfile(ctxdata,filename))
end
--
-- todo: also other stubs
@@ -747,14 +804,23 @@ function scripts.context.run(ctxdata,filename)
oldbase = file.removesuffix(jobname)
newbase = file.removesuffix(resultname)
if oldbase ~= newbase then
- for _, suffix in next, scripts.context.beforesuffixes do
- local oldname = file.addsuffix(oldbase,suffix)
- local newname = file.addsuffix(newbase,suffix)
- local tmpname = "keep-"..oldname
- os.remove(tmpname)
- os.rename(oldname,tmpname)
- os.remove(oldname)
- os.rename(newname,oldname)
+ if environment.argument("purgeresult") then
+ for _, suffix in next, scripts.context.aftersuffixes do
+ local oldname = file.addsuffix(oldbase,suffix)
+ local newname = file.addsuffix(newbase,suffix)
+ os.remove(newname)
+ os.remove(oldname)
+ end
+ else
+ for _, suffix in next, scripts.context.beforesuffixes do
+ local oldname = file.addsuffix(oldbase,suffix)
+ local newname = file.addsuffix(newbase,suffix)
+ local tmpname = "keep-"..oldname
+ os.remove(tmpname)
+ os.rename(oldname,tmpname)
+ os.remove(oldname)
+ os.rename(newname,oldname)
+ end
end
else
resultname = nil
@@ -763,17 +829,17 @@ function scripts.context.run(ctxdata,filename)
resultname = nil
end
--
- if environment.argument("autopdf") then
- scripts.context.closepdf(filename)
+ local pdfview = environment.argument("autopdf") or environment.argument("closepdf")
+ if pdfview then
+ scripts.context.closepdf(filename,pdfview)
if resultname then
- scripts.context.closepdf(resultname)
+ scripts.context.closepdf(resultname,pdfview)
end
end
--
- local okay = statistics.check_fmt_status(formatfile)
+ local okay = statistics.checkfmtstatus(formatfile)
if okay ~= true then
- logs.simple("warning: %s, forcing remake",tostring(okay))
- scripts.context.generate()
+ report("warning: %s, forcing remake",tostring(okay))
scripts.context.make(formatname)
end
--
@@ -782,13 +848,57 @@ function scripts.context.run(ctxdata,filename)
flags[#flags+1] = "--interaction=batchmode"
end
if environment.argument("synctex") then
- logs.simple("warning: syntex is enabled") -- can add upto 5% runtime
+ -- this should become a directive
+ report("warning: synctex is enabled") -- can add upto 5% runtime
flags[#flags+1] = "--synctex=1"
end
- flags[#flags+1] = "--fmt=" .. string.quote(formatfile)
- flags[#flags+1] = "--lua=" .. string.quote(scriptfile)
- flags[#flags+1] = "--backend=pdf"
- local command = string.format("luatex %s %s", table.concat(flags," "), string.quote(filename))
+ flags[#flags+1] = "--fmt=" .. quote(formatfile)
+ flags[#flags+1] = "--lua=" .. quote(scriptfile)
+ --
+ -- We pass these directly.
+ --
+
+--~ local silent = environment.argument("silent")
+--~ local noconsole = environment.argument("noconsole")
+--~ local directives = environment.argument("directives")
+--~ local trackers = environment.argument("trackers")
+--~ if silent == true then
+--~ silent = "*"
+--~ end
+--~ if type(silent) == "string" then
+--~ if type(directives) == "string" then
+--~ directives = format("%s,logs.blocked={%s}",directives,silent)
+--~ else
+--~ directives = format("logs.blocked={%s}",silent)
+--~ end
+--~ end
+--~ if noconsole then
+--~ if type(directives) == "string" then
+--~ directives = format("%s,logs.target=file",directives)
+--~ else
+--~ directives = format("logs.target=file")
+--~ end
+--~ end
+
+ local directives = environment.directives
+ local trackers = environment.trackers
+ local experiments = environment.experiments
+
+ --
+ if type(directives) == "string" then
+ flags[#flags+1] = format('--directives="%s"',directives)
+ end
+ if type(trackers) == "string" then
+ flags[#flags+1] = format('--trackers="%s"',trackers)
+ end
+ --
+ local backend = environment.argument("backend")
+ if type(backend) ~= "string" then
+ backend = "pdf"
+ end
+ flags[#flags+1] = format('--backend="%s"',backend)
+ --
+ local command = format("luatex %s %s", concat(flags," "), quote(filename))
local oldhash, newhash = scripts.context.multipass.hashfiles(jobname), { }
local once = environment.argument("once")
local maxnofruns = (once and 1) or scripts.context.multipass.nofruns
@@ -797,23 +907,24 @@ function scripts.context.run(ctxdata,filename)
-- 1:first run, 2:successive run, 3:once, 4:last of maxruns
local kindofrun = (once and 3) or (i==1 and 1) or (i==maxnofruns and 4) or 2
scripts.context.multipass.makeoptionfile(jobname,ctxdata,kindofrun,i,false) -- kindofrun, currentrun, final
- logs.simple("run %s: %s",i,command)
+ report("run %s: %s",i,command)
+--~ print("\n") -- cleaner, else continuation on same line
+ print("") -- cleaner, else continuation on same line
local returncode, errorstring = os.spawn(command)
--~ if returncode == 3 then
- --~ scripts.context.generate()
--~ scripts.context.make(formatname)
--~ returncode, errorstring = os.spawn(command)
--~ if returncode == 3 then
- --~ logs.simple("ks: return code 3, message: %s",errorstring or "?")
+ --~ report("ks: return code 3, message: %s",errorstring or "?")
--~ os.exit(1)
--~ end
--~ end
if not returncode then
- logs.simple("fatal error: no return code, message: %s",errorstring or "?")
+ report("fatal error: no return code, message: %s",errorstring or "?")
os.exit(1)
break
elseif returncode > 0 then
- logs.simple("fatal error: return code: %s",returncode or "?")
+ report("fatal error: return code: %s",returncode or "?")
os.exit(returncode)
break
else
@@ -831,13 +942,13 @@ function scripts.context.run(ctxdata,filename)
if arrange then
local kindofrun = 3
scripts.context.multipass.makeoptionfile(jobname,ctxdata,kindofrun,i,true) -- kindofrun, currentrun, final
- logs.simple("arrange run: %s",command)
+ report("arrange run: %s",command)
local returncode, errorstring = os.spawn(command)
if not returncode then
- logs.simple("fatal error: no return code, message: %s",errorstring or "?")
+ report("fatal error: no return code, message: %s",errorstring or "?")
os.exit(1)
elseif returncode > 0 then
- logs.simple("fatal error: return code: %s",returncode or "?")
+ report("fatal error: return code: %s",returncode or "?")
os.exit(returncode)
end
end
@@ -851,15 +962,26 @@ function scripts.context.run(ctxdata,filename)
os.remove(jobname..".top")
--
if resultname then
- for _, suffix in next, scripts.context.aftersuffixes do
- local oldname = file.addsuffix(oldbase,suffix)
- local newname = file.addsuffix(newbase,suffix)
- local tmpname = "keep-"..oldname
- os.remove(newname)
- os.rename(oldname,newname)
- os.rename(tmpname,oldname)
+ if environment.argument("purgeresult") then
+ -- so, if there is no result then we don't get the old one, but
+ -- related files (log etc) are still there for tracing purposes
+ for _, suffix in next, scripts.context.aftersuffixes do
+ local oldname = file.addsuffix(oldbase,suffix)
+ local newname = file.addsuffix(newbase,suffix)
+ os.remove(newname) -- to be sure
+ os.rename(oldname,newname)
+ end
+ else
+ for _, suffix in next, scripts.context.aftersuffixes do
+ local oldname = file.addsuffix(oldbase,suffix)
+ local newname = file.addsuffix(newbase,suffix)
+ local tmpname = "keep-"..oldname
+ os.remove(newname)
+ os.rename(oldname,newname)
+ os.rename(tmpname,oldname)
+ end
end
- logs.simple("result renamed to: %s",newbase)
+ report("result renamed to: %s",newbase)
end
--
if environment.argument("purge") then
@@ -868,23 +990,24 @@ function scripts.context.run(ctxdata,filename)
scripts.context.purge_job(resultname,true)
end
--
- if environment.argument("autopdf") then
- scripts.context.openpdf(resultname or filename)
+ local pdfview = environment.argument("autopdf")
+ if pdfview then
+ scripts.context.openpdf(resultname or filename,pdfview)
end
--
if environment.argument("timing") then
- logs.line()
- logs.simple("you can process (timing) statistics with:",jobname)
- logs.line()
- logs.simple("context --extra=timing '%s'",jobname)
- logs.simple("mtxrun --script timing --xhtml [--launch --remove] '%s'",jobname)
- logs.line()
+ report()
+ report("you can process (timing) statistics with:",jobname)
+ report()
+ report("context --extra=timing '%s'",jobname)
+ report("mtxrun --script timing --xhtml [--launch --remove] '%s'",jobname)
+ report()
end
else
if formatname then
- logs.simple("error, no format found with name: %s, skipping",formatname)
+ report("error, no format found with name: %s, skipping",formatname)
else
- logs.simple("error, no format found (provide formatname or interface)")
+ report("error, no format found (provide formatname or interface)")
end
break
end
@@ -892,9 +1015,9 @@ function scripts.context.run(ctxdata,filename)
end
else
if formatname then
- logs.simple("error, no format found with name: %s, aborting",formatname)
+ report("error, no format found with name: %s, aborting",formatname)
else
- logs.simple("error, no format found (provide formatname or interface)")
+ report("error, no format found (provide formatname or interface)")
end
end
end
@@ -906,37 +1029,35 @@ function scripts.context.pipe()
local interface = environment.argument("interface")
interface = (type(interface) == "string" and interface) or "en"
local formatname = scripts.context.interfaces[interface] or "cont-en"
- local formatfile, scriptfile = resolvers.locate_format(formatname)
+ local formatfile, scriptfile = resolvers.locateformat(formatname)
if not formatfile or not scriptfile then
- logs.simple("warning: no format found, forcing remake (commandline driven)")
- scripts.context.generate()
+ report("warning: no format found, forcing remake (commandline driven)")
scripts.context.make(formatname)
- formatfile, scriptfile = resolvers.locate_format(formatname)
+ formatfile, scriptfile = resolvers.locateformat(formatname)
end
if formatfile and scriptfile then
- local okay = statistics.check_fmt_status(formatfile)
+ local okay = statistics.checkfmtstatus(formatfile)
if okay ~= true then
- logs.simple("warning: %s, forcing remake",tostring(okay))
- scripts.context.generate()
+ report("warning: %s, forcing remake",tostring(okay))
scripts.context.make(formatname)
end
local flags = {
"--interaction=scrollmode",
- "--fmt=" .. string.quote(formatfile),
- "--lua=" .. string.quote(scriptfile),
+ "--fmt=" .. quote(formatfile),
+ "--lua=" .. quote(scriptfile),
"--backend=pdf",
}
local filename = environment.argument("dummyfile") or ""
if filename == "" then
filename = "\\relax"
- logs.simple("entering scrollmode, end job with \\end")
+ report("entering scrollmode, end job with \\end")
else
filename = file.addsuffix(filename,"tmp")
io.savedata(filename,"\\relax")
scripts.context.multipass.makeoptionfile(filename,{ flags = flags },3,1,false) -- kindofrun, currentrun, final
- logs.simple("entering scrollmode using '%s' with optionfile, end job with \\end",filename)
+ report("entering scrollmode using '%s' with optionfile, end job with \\end",filename)
end
- local command = string.format("luatex %s %s", table.concat(flags," "), string.quote(filename))
+ local command = format("luatex %s %s", concat(flags," "), quote(filename))
os.spawn(command)
if environment.argument("purge") then
scripts.context.purge_job(filename)
@@ -946,41 +1067,45 @@ function scripts.context.pipe()
end
else
if formatname then
- logs.simple("error, no format found with name: %s, aborting",formatname)
+ report("error, no format found with name: %s, aborting",formatname)
else
- logs.simple("error, no format found (provide formatname or interface)")
+ report("error, no format found (provide formatname or interface)")
end
end
end
+local make_mkiv_format = environment.make_format
+
+local function make_mkii_format(name,engine)
+ if environment.argument(engine) then
+ local command = format("mtxrun texexec.rb --make --%s %s",name,engine)
+ report("running command: %s",command)
+ os.spawn(command)
+ end
+end
+
+function scripts.context.generate()
+ resolvers.instance.renewcache = true
+ trackers.enable("resolvers.locating")
+ resolvers.load()
+end
+
function scripts.context.make(name)
- local runners = {
- "luatools --make --compile ",
- (environment.argument("pdftex") and "mtxrun texexec.rb --make --pdftex ") or false,
- (environment.argument("xetex") and "mtxrun texexec.rb --make --xetex " ) or false,
- }
+ if not environment.argument("fast") then -- as in texexec
+ scripts.context.generate()
+ end
local list = (name and { name }) or (environment.files[1] and environment.files) or scripts.context.defaultformats
for i=1,#list do
local name = list[i]
- name = scripts.context.interfaces[name] or name
- for i=1,#runners do
- local runner = runners[i]
- if runner then
- local command = runner .. name
- logs.simple("running command: %s",command)
- os.spawn(command)
- end
+ name = scripts.context.interfaces[name] or name or ""
+ if name ~= "" then
+ make_mkiv_format(name)
+ make_mkii_format(name,"pdftex")
+ make_mkii_format(name,"xetex")
end
end
end
-function scripts.context.generate()
- -- hack, should also be a shared function
- local command = "luatools --generate "
- logs.simple("running command: %s",command)
- os.spawn(command)
-end
-
function scripts.context.ctx()
local ctxdata = ctxrunner.new()
ctxdata.jobname = environment.files[1]
@@ -997,7 +1122,7 @@ function scripts.context.autoctx()
if f then
local chunk = f:read(512) or ""
f:close()
- local ctxname = string.match(chunk,"<%?context%-directive%s+job%s+ctxfile%s+([^ ]-)%s*?>")
+ local ctxname = match(chunk,"<%?context%-directive%s+job%s+ctxfile%s+([^ ]-)%s*?>")
if ctxname then
ctxdata = ctxrunner.new()
ctxdata.jobname = firstfile
@@ -1023,13 +1148,13 @@ local loaded = false
function scripts.context.metapost()
local filename = environment.files[1] or ""
if not loaded then
- dofile(resolvers.find_file("mlib-run.lua"))
+ dofile(resolvers.findfile("mlib-run.lua"))
loaded = true
commands = commands or { }
- commands.writestatus = logs.report
+ commands.writestatus = report -- no longer needed
end
local formatname = environment.argument("format") or "metafun"
- if formatname == "" or type(format) == "boolean" then
+ if formatname == "" or type(formatname) == "boolean" then
formatname = "metafun"
end
if environment.argument("pdf") then
@@ -1037,7 +1162,7 @@ function scripts.context.metapost()
local resultname = environment.argument("result") or basename
local jobname = "mtx-context-metapost"
local tempname = file.addsuffix(jobname,"tex")
- io.savedata(tempname,string.format(template,"metafun",filename))
+ io.savedata(tempname,format(template,"metafun",filename))
environment.files[1] = tempname
environment.setargument("result",resultname)
environment.setargument("once",true)
@@ -1052,22 +1177,22 @@ function scripts.context.metapost()
end
function scripts.context.version()
- local name = resolvers.find_file("context.tex")
+ local name = resolvers.findfile("context.mkiv")
if name ~= "" then
- logs.simple("main context file: %s",name)
+ report("main context file: %s",name)
local data = io.loaddata(name)
if data then
- local version = data:match("\\edef\\contextversion{(.-)}")
+ local version = match(data,"\\edef\\contextversion{(.-)}")
if version then
- logs.simple("current version: %s",version)
+ report("current version: %s",version)
else
- logs.simple("context version: unknown, no timestamp found")
+ report("context version: unknown, no timestamp found")
end
else
- logs.simple("context version: unknown, load error")
+ report("context version: unknown, load error")
end
else
- logs.simple("main context file: unknown, 'context.tex' not found")
+ report("main context file: unknown, 'context.mkiv' not found")
end
end
@@ -1093,6 +1218,10 @@ local persistent_runfiles = {
"tuo", "tub", "top", "tuc"
}
+local special_runfiles = {
+ "-mpgraph*", "-mprun*", "-temp-*"
+}
+
local function purge_file(dfile,cfile)
if cfile and lfs.isfile(cfile) then
if os.remove(dfile) then
@@ -1105,31 +1234,38 @@ local function purge_file(dfile,cfile)
end
end
-function scripts.context.purge_job(jobname,all)
+local function remove_special_files(pattern)
+end
+
+function scripts.context.purge_job(jobname,all,mkiitoo)
if jobname and jobname ~= "" then
jobname = file.basename(jobname)
local filebase = file.removesuffix(jobname)
- local deleted = { }
- for i=1,#obsolete_results do
- deleted[#deleted+1] = purge_file(filebase.."."..obsolete_results[i],filebase..".pdf")
- end
- for i=1,#temporary_runfiles do
- deleted[#deleted+1] = purge_file(filebase.."."..temporary_runfiles[i])
- end
- if all then
- for i=1,#persistent_runfiles do
- deleted[#deleted+1] = purge_file(filebase.."."..persistent_runfiles[i])
+ if mkiitoo then
+ scripts.context.purge(all,filebase,true) -- leading "./"
+ else
+ local deleted = { }
+ for i=1,#obsolete_results do
+ deleted[#deleted+1] = purge_file(filebase.."."..obsolete_results[i],filebase..".pdf")
+ end
+ for i=1,#temporary_runfiles do
+ deleted[#deleted+1] = purge_file(filebase.."."..temporary_runfiles[i])
+ end
+ if all then
+ for i=1,#persistent_runfiles do
+ deleted[#deleted+1] = purge_file(filebase.."."..persistent_runfiles[i])
+ end
+ end
+ if #deleted > 0 then
+ report("purged files: %s", concat(deleted,", "))
end
- end
- if #deleted > 0 then
- logs.simple("purged files: %s", table.join(deleted,", "))
end
end
end
-function scripts.context.purge(all)
+function scripts.context.purge(all,pattern,mkiitoo)
local all = all or environment.argument("all")
- local pattern = environment.argument("pattern") or "*.*"
+ local pattern = environment.argument("pattern") or (pattern and (pattern.."*")) or "*.*"
local files = dir.glob(pattern)
local obsolete = table.tohash(obsolete_results)
local temporary = table.tohash(temporary_runfiles)
@@ -1142,18 +1278,21 @@ function scripts.context.purge(all)
local basename = file.basename(name)
if obsolete[suffix] or temporary[suffix] or persistent[suffix] or generic[basename] then
deleted[#deleted+1] = purge_file(name)
+ elseif mkiitoo then
+ for i=1,#special_runfiles do
+ if find(name,special_runfiles[i]) then
+ deleted[#deleted+1] = purge_file(name)
+ end
+ end
end
end
if #deleted > 0 then
- logs.simple("purged files: %s", table.join(deleted,", "))
+ report("purged files: %s", concat(deleted,", "))
end
end
---~ purge_for_files("test",true)
---~ purge_all_files()
-
local function touch(name,pattern)
- local name = resolvers.find_file(name)
+ local name = resolvers.findfile(name)
local olddata = io.loaddata(name)
if olddata then
local oldversion, newversion = "", os.date("%Y.%m.%d %H:%M")
@@ -1173,20 +1312,78 @@ local function touch(name,pattern)
end
end
+local function touchfiles(suffix)
+ local done, oldversion, newversion, foundname = touch(file.addsuffix("context",suffix),"(\\edef\\contextversion{)(.-)(})")
+ if done then
+ report("old version : %s", oldversion)
+ report("new version : %s", newversion)
+ report("touched file: %s", foundname)
+ local ok, _, _, foundname = touch(file.addsuffix("cont-new",suffix), "(\\newcontextversion{)(.-)(})")
+ if ok then
+ report("touched file: %s", foundname)
+ end
+ end
+end
+
function scripts.context.touch()
if environment.argument("expert") then
- local done, oldversion, newversion, foundname = touch("context.tex", "(\\edef\\contextversion{)(.-)(})")
- if done then
- logs.simple("old version : %s", oldversion)
- logs.simple("new version : %s", newversion)
- logs.simple("touched file: %s", foundname)
- local ok, _, _, foundname = touch("cont-new.tex", "(\\newcontextversion{)(.-)(})")
- if ok then
- logs.simple("touched file: %s", foundname)
- end
- local ok, _, _, foundname = touch("cont-xp.tex", "(\\edef\\contextversion{)(.-)(})")
- if ok then
- logs.simple("touched file: %s", foundname)
+ touchfiles("mkii")
+ touchfiles("mkiv")
+ touchfiles("mkvi")
+ end
+end
+
+-- modules
+
+local labels = { "title", "comment", "status" }
+local cards = { "*.mkvi", "*.mkiv", "*.tex" }
+
+function scripts.context.modules(pattern)
+ local list = { }
+ local found = resolvers.findfile("context.mkiv")
+ if not pattern or pattern == "" then
+ -- official files in the tree
+ for _, card in ipairs(cards) do
+ resolvers.findwildcardfiles(card,list)
+ end
+ -- my dev path
+ for _, card in ipairs(cards) do
+ dir.glob(file.join(file.dirname(found),card),list)
+ end
+ else
+ resolvers.findwildcardfiles(pattern,list)
+ dir.glob(file.join(file.dirname(found,pattern)),list)
+ end
+ local done = { } -- todo : sort
+ for i=1,#list do
+ local v = list[i]
+ local base = file.basename(v)
+ if not done[base] then
+ done[base] = true
+ local suffix = file.suffix(base)
+ if suffix == "tex" or suffix == "mkiv" or suffix == "mkvi" then
+ local prefix = match(base,"^([xmst])%-")
+ if prefix then
+ v = resolvers.findfile(base) -- so that files on my dev path are seen
+ local data = io.loaddata(v) or ""
+ data = match(data,"%% begin info(.-)%% end info")
+ if data then
+ local info = { }
+ for label, text in gmatch(data,"%% +([^ ]+) *: *(.-)[\n\r]") do
+ info[label] = text
+ end
+ report()
+ report("%-7s : %s","module",base)
+ report()
+ for i=1,#labels do
+ local l = labels[i]
+ if info[l] then
+ report("%-7s : %s",l,info[l])
+ end
+ end
+ report()
+ end
+ end
end
end
end
@@ -1195,28 +1392,22 @@ end
-- extras
function scripts.context.extras(pattern)
- local found = resolvers.find_file("context.tex")
- if found == "" then
- logs.simple("unknown extra: %s", extra)
- else
- pattern = file.join(dir.expand_name(file.dirname(found)),string.format("mtx-context-%s.tex",pattern or "*"))
+ -- only in base path, i.e. only official ones
+ local found = resolvers.findfile("context.mkiv")
+ if found ~= "" then
+ pattern = file.join(dir.expandname(file.dirname(found)),format("mtx-context-%s.tex",pattern or "*"))
local list = dir.glob(pattern)
- if not extra or extra == "" then
- logs.extendbanner("extras")
- else
- logs.extendbanner(extra)
- end
for i=1,#list do
local v = list[i]
local data = io.loaddata(v) or ""
- data = string.match(data,"begin help(.-)end help")
+ data = match(data,"%% begin help(.-)%% end help")
if data then
- local h = { string.format("extra: %s (%s)",string.gsub(v,"^.*mtx%-context%-(.-)%.tex$","%1"),v) }
- for s in string.gmatch(data,"%% *(.-)[\n\r]") do
- h[#h+1] = s
+ report()
+ report("extra: %s (%s)",(gsub(v,"^.*mtx%-context%-(.-)%.tex$","%1")),v)
+ for s in gmatch(data,"%% *(.-)[\n\r]") do
+ report(s)
end
- h[#h+1] = ""
- logs.help(table.concat(h,"\n"),"nomoreinfo")
+ report()
end
end
end
@@ -1229,15 +1420,15 @@ function scripts.context.extra()
scripts.context.extras(extra)
else
local fullextra = extra
- if not string.find(fullextra,"mtx%-context%-") then
+ if not find(fullextra,"mtx%-context%-") then
fullextra = "mtx-context-" .. extra
end
- local foundextra = resolvers.find_file(fullextra)
+ local foundextra = resolvers.findfile(fullextra)
if foundextra == "" then
scripts.context.extras()
return
else
- logs.simple("processing extra: %s", foundextra)
+ report("processing extra: %s", foundextra)
end
environment.setargument("purgeall",true)
local result = environment.setargument("result") or ""
@@ -1254,26 +1445,34 @@ end
-- todo: we need to do a dummy run
function scripts.context.trackers()
- environment.files = { resolvers.find_file("m-trackers.tex") }
+ environment.files = { resolvers.findfile("m-trackers.mkiv") }
scripts.context.multipass.nofruns = 1
+ environment.setargument("purgeall",true)
scripts.context.run()
- -- maybe filter from log
end
function scripts.context.directives()
- environment.files = { resolvers.find_file("m-directives.tex") }
+ environment.files = { resolvers.findfile("m-directives.mkiv") }
scripts.context.multipass.nofruns = 1
+ environment.setargument("purgeall",true)
+ scripts.context.run()
+end
+
+function scripts.context.logcategories()
+ environment.files = { resolvers.findfile("m-logcategories.mkiv") }
+ scripts.context.multipass.nofruns = 1
+ environment.setargument("purgeall",true)
scripts.context.run()
- -- maybe filter from log
end
function scripts.context.timed(action)
statistics.timed(action)
end
-local zipname = "cont-tmf.zip"
-local mainzip = "http://www.pragma-ade.com/context/latest/" .. zipname
-local validtrees = { "texmf-local", "texmf-context" }
+local zipname = "cont-tmf.zip"
+local mainzip = "http://www.pragma-ade.com/context/latest/" .. zipname
+local validtrees = { "texmf-local", "texmf-context" }
+local selfscripts = { "mtxrun.lua" } -- was: { "luatools.lua", "mtxrun.lua" }
function zip.loaddata(zipfile,filename) -- should be in zip lib
local f = zipfile:open(filename)
@@ -1289,19 +1488,19 @@ function scripts.context.update()
local force = environment.argument("force")
local socket = require("socket")
local http = require("socket.http")
- local basepath = resolvers.find_file("context.tex") or ""
+ local basepath = resolvers.findfile("context.mkiv") or ""
if basepath == "" then
- logs.simple("quiting, no 'context.tex' found")
+ report("quiting, no 'context.mkiv' found")
return
end
- local basetree = basepath.match(basepath,"^(.-)tex/context/base/context.tex$") or ""
+ local basetree = basepath.match(basepath,"^(.-)tex/context/base/context.mkiv$") or ""
if basetree == "" then
- logs.simple("quiting, no proper tds structure (%s)",basepath)
+ report("quiting, no proper tds structure (%s)",basepath)
return
end
local function is_okay(basetree)
for _, tree in next, validtrees do
- local pattern = string.gsub(tree,"%-","%%-")
+ local pattern = gsub(tree,"%-","%%-")
if basetree:find(pattern) then
return tree
end
@@ -1310,19 +1509,19 @@ function scripts.context.update()
end
local okay = is_okay(basetree)
if not okay then
- logs.simple("quiting, tree '%s' is protected",okay)
+ report("quiting, tree '%s' is protected",okay)
return
else
- logs.simple("updating tree '%s'",okay)
+ report("updating tree '%s'",okay)
end
if not lfs.chdir(basetree) then
- logs.simple("quiting, unable to change to '%s'",okay)
+ report("quiting, unable to change to '%s'",okay)
return
end
- logs.simple("fetching '%s'",mainzip)
+ report("fetching '%s'",mainzip)
local latest = http.request(mainzip)
if not latest then
- logs.simple("context tree '%s' can be updated, use --force",okay)
+ report("context tree '%s' can be updated, use --force",okay)
return
end
io.savedata("cont-tmf.zip",latest)
@@ -1333,37 +1532,37 @@ function scripts.context.update()
-- variant 2
local zipfile = zip.open(zipname)
if not zipfile then
- logs.simple("quiting, unable to open '%s'",zipname)
+ report("quiting, unable to open '%s'",zipname)
return
end
- local newfile = zip.loaddata(zipfile,"tex/context/base/context.tex")
+ local newfile = zip.loaddata(zipfile,"tex/context/base/context.mkiv")
if not newfile then
- logs.simple("quiting, unable to open '%s'","context.tex")
+ report("quiting, unable to open '%s'","context.mkiv")
return
end
- local oldfile = io.loaddata(resolvers.find_file("context.tex")) or ""
+ local oldfile = io.loaddata(resolvers.findfile("context.mkiv")) or ""
local function versiontonumber(what,str)
- local version = str:match("\\edef\\contextversion{(.-)}") or ""
- local year, month, day, hour, minute = str:match("\\edef\\contextversion{(%d+)%.(%d+)%.(%d+) *(%d+)%:(%d+)}")
+ local version = match(str,"\\edef\\contextversion{(.-)}") or ""
+ local year, month, day, hour, minute = match(str,"\\edef\\contextversion{(%d+)%.(%d+)%.(%d+) *(%d+)%:(%d+)}")
if year and minute then
local time = os.time { year=year,month=month,day=day,hour=hour,minute=minute}
- logs.simple("%s version: %s (%s)",what,version,time)
+ report("%s version: %s (%s)",what,version,time)
return time
else
- logs.simple("%s version: %s (unknown)",what,version)
+ report("%s version: %s (unknown)",what,version)
return nil
end
end
local oldversion = versiontonumber("old",oldfile)
local newversion = versiontonumber("new",newfile)
if not oldversion or not newversion then
- logs.simple("quiting, version cannot be determined")
+ report("quiting, version cannot be determined")
return
elseif oldversion == newversion then
- logs.simple("quiting, your current version is up-to-date")
+ report("quiting, your current version is up-to-date")
return
elseif oldversion > newversion then
- logs.simple("quiting, your current version is newer")
+ report("quiting, your current version is newer")
return
end
for k in zipfile:files() do
@@ -1376,113 +1575,46 @@ function scripts.context.update()
if force then
io.savedata(filename,data)
end
- logs.simple(filename)
+ report(filename)
end
end
end
- for _, scriptname in next, { "luatools.lua", "mtxrun.lua" } do
- local oldscript = resolvers.find_file(scriptname) or ""
+ for _, scriptname in next, selfscripts do
+ local oldscript = resolvers.findfile(scriptname) or ""
if oldscript ~= "" and is_okay(oldscript) then
local newscript = "./scripts/context/lua/" .. scriptname
local data = io.loaddata(newscript) or ""
if data ~= "" then
- logs.simple("replacing script '%s' by '%s'",oldscript,newscript)
+ report("replacing script '%s' by '%s'",oldscript,newscript)
if force then
io.savedata(oldscript,data)
end
end
else
- logs.simple("keeping script '%s'",oldscript)
+ report("keeping script '%s'",oldscript)
end
end
if force then
- os.execute("context --generate")
- os.execute("context --make")
+ scripts.context.make()
end
end
if force then
- logs.simple("context tree '%s' has been updated",okay)
+ report("context tree '%s' has been updated",okay)
else
- logs.simple("context tree '%s' can been updated (use --force)",okay)
+ report("context tree '%s' can been updated (use --force)",okay)
end
end
-logs.extendbanner("ConTeXt Process Management 0.51",true)
-
-messages.help = [[
---run process (one or more) files (default action)
---make create context formats
-
---ctx=name use ctx file (process management specification)
---interface use specified user interface (default: en)
-
---autopdf close pdf file in viewer and start pdf viewer afterwards
---purge(all) purge files either or not after a run (--pattern=...)
-
---usemodule=list load the given module or style, normally part o fthe distribution
---environment=list load the given environment file first (document styles)
---mode=list enable given the modes (conditional processing in styles)
---path=list also consult the given paths when files are looked for
---arguments=list set variables that can be consulted during a run (key/value pairs)
---randomseed=number set the randomseed
---result=name rename the resulting output to the given name
---trackers=list show/set tracker variables
---directives=list show/set directive variables
-
---forcexml force xml stub (optional flag: --mkii)
---forcecld force cld (context lua document) stub
-
---arrange run extra imposition pass, given that the style sets up imposition
---noarrange ignore imposition specifications in the style
-
---once only run once (no multipass data file is produced)
---batchmode run without stopping and don't show messages on the console
---nonstopmode run without stopping
-
---generate generate file database etc. (as luatools does)
---paranoid don't descend to .. and ../..
---version report installed context version
-
---expert expert options
-]]
-
--- filter=list is kind of obsolete
--- color is obsolete for mkiv, always on
--- separation is obsolete for mkiv, no longer available
--- output is currently obsolete for mkiv
--- setuppath=list must check
--- modefile=name must check
--- input=name load the given inputfile (must check)
-
-messages.expert = [[
-expert options:
-
---touch update context version number (remake needed afterwards, also provide --expert)
---nostats omit runtime statistics at the end of the run
---update update context from website (not to be confused with contextgarden)
---profile profile job (use: mtxrun --script profile --analyse)
---timing generate timing and statistics overview
---tracefiles show some extra info when locating files (at the tex end)
-
---extra=name process extra (mtx-context-<name> in distribution)
---extras show extras
-]]
-
-messages.private = [[
-private options:
-
---dumphash dump hash table afterwards
---dumpdelta dump hash table afterwards (only new entries)
-]]
-
-messages.special = [[
-special options:
+do
---pdftex process file with texexec using pdftex
---xetex process file with texexec using xetex
+ local silent = environment.argument("silent")
+ if type(silent) == "string" then
+ directives.enable(format("logs.blocked={%s}",silent))
+ elseif silent then
+ directives.enable("logs.blocked")
+ end
---pipe don't check for file and enter scroll mode (--dummyfile=whatever.tmp)
-]]
+end
if environment.argument("once") then
scripts.context.multipass.nofruns = 1
@@ -1497,15 +1629,10 @@ end
if environment.argument("run") then
-- scripts.context.timed(scripts.context.run)
scripts.context.timed(scripts.context.autoctx)
-elseif environment.argument("make") or environment.argument("generate") then
- scripts.context.timed(function()
- if environment.argument("generate") then
- scripts.context.generate()
- end
- if environment.argument("make") then
- scripts.context.make()
- end
- end)
+elseif environment.argument("make") then
+ scripts.context.timed(function() scripts.context.make() end)
+elseif environment.argument("generate") then
+ scripts.context.timed(function() scripts.context.generate() end)
elseif environment.argument("ctx") then
scripts.context.timed(scripts.context.ctx)
elseif environment.argument("mp") or environment.argument("metapost") then
@@ -1517,7 +1644,9 @@ elseif environment.argument("touch") then
elseif environment.argument("update") then
scripts.context.update()
elseif environment.argument("expert") then
- logs.help(table.join({ messages.expert, messages.private, messages.special },"\n"))
+ application.help("expert", "special")
+elseif environment.argument("modules") then
+ scripts.context.modules()
elseif environment.argument("extras") then
scripts.context.extras()
elseif environment.argument("extra") then
@@ -1526,12 +1655,14 @@ elseif environment.argument("help") then
if environment.files[1] == "extras" then
scripts.context.extras()
else
- logs.help(messages.help)
+ application.help("basic")
end
-elseif environment.argument("trackers") and type(environment.argument("trackers")) == "boolean" then
+elseif environment.argument("showtrackers") or environment.argument("trackers") == true then
scripts.context.trackers()
-elseif environment.argument("directives") and type(environment.argument("directives")) == "boolean" then
+elseif environment.argument("showdirectives") or environment.argument("directives") == true then
scripts.context.directives()
+elseif environment.argument("showlogcategories") then
+ scripts.context.logcategories()
elseif environment.argument("track") and type(environment.argument("track")) == "boolean" then -- for old times sake, will go
scripts.context.trackers()
elseif environment.files[1] then
@@ -1546,7 +1677,7 @@ elseif environment.argument("purgeall") then
-- only when no filename given, supports --pattern
scripts.context.purge(true)
else
- logs.help(messages.help)
+ application.help("basic")
end
if environment.argument("profile") then
diff --git a/Master/texmf-dist/scripts/context/lua/mtx-convert.lua b/Master/texmf-dist/scripts/context/lua/mtx-convert.lua
index 62198a62188..0c5c01bbfbe 100644
--- a/Master/texmf-dist/scripts/context/lua/mtx-convert.lua
+++ b/Master/texmf-dist/scripts/context/lua/mtx-convert.lua
@@ -8,6 +8,23 @@ if not modules then modules = { } end modules ['mtx-convert'] = {
-- todo: eps and svg
+local helpinfo = [[
+--convertall convert all graphics on path
+--inputpath=string original graphics path
+--outputpath=string converted graphics path
+--watch watch folders
+--force force conversion (even if older)
+--delay time between sweeps
+]]
+
+local application = logs.application {
+ name = "mtx-convert",
+ banner = "ConTeXT Graphic Conversion Helpers 0.10",
+ helpinfo = helpinfo,
+}
+
+local report = application.report
+
graphics = graphics or { }
graphics.converters = graphics.converters or { }
@@ -40,7 +57,7 @@ local function convert(kind,oldname,newname)
if graphics.converters[kind] then -- extra test
local tmpname = file.replacesuffix(newname,"tmp")
local command = graphics.converters[kind](oldname,tmpname)
- logs.simple("command: %s",command)
+ report("command: %s",command)
io.flush()
os.spawn(command)
os.remove(newname)
@@ -118,22 +135,10 @@ function scripts.convert.convertgiven()
end
end
-
-logs.extendbanner("ConTeXT Graphic Conversion Helpers 0.10",true)
-
-messages.help = [[
---convertall convert all graphics on path
---inputpath=string original graphics path
---outputpath=string converted graphics path
---watch watch folders
---force force conversion (even if older)
---delay time between sweeps
-]]
-
if environment.argument("convertall") then
scripts.convert.convertall()
elseif environment.files[1] then
scripts.convert.convertgiven()
else
- logs.help(messages.help)
+ application.help()
end
diff --git a/Master/texmf-dist/scripts/context/lua/mtx-epub.lua b/Master/texmf-dist/scripts/context/lua/mtx-epub.lua
new file mode 100644
index 00000000000..70ee7828cee
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/lua/mtx-epub.lua
@@ -0,0 +1,177 @@
+if not modules then modules = { } end modules ['mtx-epub'] = {
+ version = 1.001,
+ comment = "companion to mtxrun.lua",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- The epub specification is far from beautiful. Especially the id related
+-- part is messy and devices/programs react differently on them (so an id is not
+-- really an id but has some special property). Then there is this ncx suffix
+-- thing. Somehow it give the impression of a reversed engineered application
+-- format so it will probably take a few cycles to let it become a real
+-- clean standard. Thanks to Adam Reviczky for helping to figure out all these
+-- puzzling details.
+
+-- This is preliminary code. At some point we will deal with images as well but
+-- first we need a decent strategy to export them. More information will be
+-- available on the wiki.
+
+local format = string.format
+local concat = table.concat
+
+local helpinfo = [[
+--make create epub zip file
+
+example:
+
+mtxrun --script epub --make mydocument
+]]
+
+local application = logs.application {
+ name = "mtx-epub",
+ banner = "ConTeXt EPUB Helpers 0.11",
+ helpinfo = helpinfo,
+}
+
+-- script code
+
+scripts = scripts or { }
+scripts.epub = scripts.epub or { }
+
+local mimetype = "application/epub+zip"
+
+local container = [[
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container">
+ <rootfiles>
+ <rootfile full-path="OPS/%s" media-type="application/oebps-package+xml"/>
+ </rootfiles>
+</container>
+]]
+
+local package = [[
+<?xml version="1.0"?>
+
+<package version="2.0" xmlns="http://www.idpf.org/2007/opf" unique-identifier="%s">
+
+ <metadata xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:opf="http://www.idpf.org/2007/opf">
+ <dc:title>My Title</dc:title>
+ <dc:language>en</dc:language>
+ <dc:identifier id="%s" />
+ <dc:creator opf:file-as="Self, My" opf:role="aut">MySelf</dc:creator>
+ </metadata>
+
+ <manifest>
+ %s
+ </manifest>
+
+ <spine toc="ncx">
+ <itemref idref="%s" />
+ </spine>
+
+</package>
+]]
+
+-- We need to figure out what is permitted; numbers only seem to give
+-- problems is some applications as do names with dashes.
+
+local function dumbid(filename)
+ -- return (string.gsub(os.uuid(),"%-%","")) -- to be tested
+ return file.nameonly(filename)
+end
+
+local mimetypes = {
+ xhtml = "application/xhtml+xml",
+ css = "text/css",
+ -- default = "text/plain",
+}
+
+local idmakers = {
+ ncx = function(filename) return "ncx" end,
+ -- css = function(filename) return "stylesheet" end,
+ default = function(filename) return dubmid(filename) end,
+}
+
+-- specification = {
+-- name = "document",
+-- identifier = "123",
+-- root = "a.xhtml",
+-- files = {
+-- "a.xhtml",
+-- "b.css",
+-- "c.png",
+-- }
+-- }
+
+function scripts.epub.make()
+
+ local filename = environment.files[1]
+
+ if filename and filename ~= "" then
+
+ filename = file.basename(filename)
+ local specfile = file.replacesuffix(filename,"specification")
+ local specification = lfs.isfile(specfile) and dofile(specfile) or { }
+
+ -- inspect(specification)
+
+ local name = specification.name or file.removesuffix(filename)
+ local identifier = specification.identifier or os.uuid()
+ local files = specification.files or { file.addsuffix(filename,"xhtml") }
+ local root = specification.root or files[1]
+
+ local epubname = name
+ local epubpath = file.replacesuffix(name,"tree")
+ local epubfile = file.replacesuffix(name,"epub")
+ local epubroot = file.replacesuffix(name,"opf")
+
+ lfs.mkdir(epubpath)
+ lfs.mkdir(file.join(epubpath,"META-INF"))
+ lfs.mkdir(file.join(epubpath,"OPS"))
+
+ local used = { }
+
+ for i=1,#files do
+ local filename = files[i]
+ local suffix = file.suffix(filename)
+ local mime = mimetypes[suffix]
+ if mime then
+ local idmaker = idmakers[suffix] or idmakers.default
+ file.copy(filename,file.join(epubpath,"OPS",filename))
+ used[#used+1] = format("<item id='%s' href='%s' media-type='%s'/>",idmaker(filename),filename,mime)
+ end
+ end
+
+ container = format(container,epubroot)
+ package = format(package,identifier,identifier,concat(used,"\n"),file.removesuffix(root))
+
+ io.savedata(file.join(epubpath,"mimetype"),mimetype)
+ io.savedata(file.join(epubpath,"META-INF","container.xml"),container)
+ io.savedata(file.join(epubpath,"OPS",epubroot),package)
+
+ lfs.chdir(epubpath)
+
+ os.remove(epubfile)
+
+ os.execute(format("zip %s -0 %s",epubfile,"mimetype"))
+ os.execute(format("zip %s -r %s",epubfile,"META-INF"))
+ os.execute(format("zip %s -r %s",epubfile,"OPS"))
+
+ lfs.chdir("..")
+
+ application.report("epub archive: %s",file.join(epubpath,epubfile))
+
+ end
+
+end
+
+--
+
+if environment.argument("make") then
+ scripts.epub.make()
+else
+ application.help()
+end
diff --git a/Master/texmf-dist/scripts/context/lua/mtx-flac.lua b/Master/texmf-dist/scripts/context/lua/mtx-flac.lua
new file mode 100644
index 00000000000..37f985654a3
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/lua/mtx-flac.lua
@@ -0,0 +1,216 @@
+if not modules then modules = { } end modules ['mtx-flac'] = {
+ version = 1.001,
+ comment = "companion to mtxrun.lua",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- Written with Within Temptation's "The Unforgiven" in loopmode on
+-- the speakers. The following code is also used for my occasional music
+-- repository cleanup session using the code below.
+
+-- this can become l-flac.lua
+
+local sub, match, byte, lower = string.sub, string.match, string.byte, string.lower
+local readstring, readnumber = io.readstring, io.readnumber
+local concat = table.concat
+local tonumber = tonumber
+local tobitstring = number.tobitstring
+
+flac = flac or { }
+
+flac.report = string.format
+
+local splitter = lpeg.splitat("=")
+local readers = { }
+
+readers[0] = function(f,size,target) -- not yet ok
+ local info = { }
+ target.info = info
+ info.minimum_block_size = readnumber(f,-2)
+ info.maximum_block_size = readnumber(f,-2)
+ info.minimum_frame_size = readnumber(f,-3)
+ info.maximum_frame_size = readnumber(f,-3)
+ local buffer = { }
+ for i=1,8 do
+ buffer[i] = tobitstring(readnumber(f,1))
+ end
+ local bytes = concat(buffer)
+ info.sample_rate_in_hz = tonumber(sub(bytes, 1,20),2) -- 20
+ info.number_of_channels = tonumber(sub(bytes,21,23),2) -- 3
+ info.bits_per_sample = tonumber(sub(bytes,24,28),2) -- 5
+ info.samples_in_stream = tonumber(sub(bytes,29,64),2) -- 36
+ info.md5_signature = readstring(f,16) -- 128
+end
+
+readers[4] = function(f,size,target,banner)
+ local tags = { }
+ target.tags = tags
+ target.vendor = readstring(f,readnumber(f,-4))
+ for i=1,readnumber(f,-4) do
+ local key, value = lpeg.match(splitter,readstring(f,readnumber(f,-4)))
+ tags[lower(key)] = value
+ end
+end
+
+readers.default = function(f,size,target)
+ f:seek("cur",size)
+end
+
+function flac.getmetadata(filename)
+ local f = io.open(filename, "rb")
+ if f then
+ local banner = readstring(f,4)
+ if banner == "fLaC" then
+ local data = {
+ banner = banner,
+ filename = filename,
+ filesize = lfs.attributes(filename,"size"),
+ }
+ while true do
+ local flag = readnumber(f,1)
+ local size = readnumber(f,3)
+ local last = flag > 127
+ if last then
+ flag = flag - 128
+ end
+ local reader = readers[flag] or readers.default
+ reader(f,size,data,banner)
+ if last then
+ return data
+ end
+ end
+ else
+ flac.report("no flac file: %s (%s)",filename,banner)
+ end
+ f:close()
+ else
+ flac.report("no file: %s",filename)
+ end
+end
+
+function flac.savecollection(pattern,filename)
+ pattern = (pattern ~= "" and pattern) or "**/*.flac"
+ filename = (filename ~= "" and filename) or "music-collection.xml"
+ flac.report("identifying files using pattern %q" ,pattern)
+ local files = dir.glob(pattern)
+ flac.report("%s files found, analyzing files",#files)
+ local music = { }
+ for i=1,#files do
+ local data = flac.getmetadata(files[i])
+ if data then
+ local tags = data.tags
+ local info = data.info
+ local artist = tags.artist
+ local album = tags.album
+ local albums = music[artist]
+ if not albums then
+ albums = { }
+ music[artist] = albums
+ end
+ local albumx = albums[album]
+ if not albumx then
+ albumx = {
+ year = tags.date,
+ tracks = { },
+ }
+ albums[album] = albumx
+ end
+ albumx.tracks[tonumber(tags.tracknumber) or 0] = {
+ title = tags.title,
+ length = math.round((info.samples_in_stream/info.sample_rate_in_hz)),
+ }
+ end
+ end
+ -- inspect(music)
+ local nofartists, nofalbums, noftracks, noferrors = 0, 0, 0, 0
+ local f = io.open(filename,"wb")
+ if f then
+ f:write("<?xml version='1.0' standalone='yes'?>\n\n")
+ f:write("<collection>\n")
+ for artist, albums in table.sortedpairs(music) do
+ nofartists = nofartists + 1
+ f:write("\t<artist>\n")
+ f:write("\t\t<name>" .. artist .. "</name>\n")
+ f:write("\t\t<albums>\n")
+ for album, data in table.sortedpairs(albums) do
+ nofalbums = nofalbums + 1
+ f:write("\t\t\t<album year='" .. (data.year or 0) .. "'>\n")
+ f:write("\t\t\t\t<name>" .. album .. "</name>\n")
+ f:write("\t\t\t\t<tracks>\n")
+ local tracks = data.tracks
+ for i=1,#tracks do
+ local track = tracks[i]
+ if track then
+ noftracks = noftracks + 1
+ f:write("\t\t\t\t\t<track length='" .. track.length .. "'>" .. track.title .. "</track>\n")
+ else
+ noferrors = noferrors + 1
+ flac.report("error in album: %q of artist",album,artist)
+ f:write("\t\t\t\t\t<error track='" .. i .. "'/>\n")
+ end
+ end
+ f:write("\t\t\t\t</tracks>\n")
+ f:write("\t\t\t</album>\n")
+ end
+ f:write("\t\t</albums>\n")
+ f:write("\t</artist>\n")
+ end
+ f:write("</collection>\n")
+ end
+ f:close()
+ flac.report("%s tracks of %s albums of %s artists saved in %q (%s errors)",noftracks,nofalbums,nofartists,filename,noferrors)
+end
+
+--
+
+local helpinfo = [[
+--collect collect albums in xml file
+
+example:
+
+mtxrun --script flac --collect somename.flac
+mtxrun --script flac --collect --pattern="m:/music/**")
+]]
+
+local application = logs.application {
+ name = "mtx-flac",
+ banner = "ConTeXt Flac Helpers 0.10",
+ helpinfo = helpinfo,
+}
+
+flac.report = application.report
+
+-- script code
+
+scripts = scripts or { }
+scripts.flac = scripts.flac or { }
+
+function scripts.flac.collect()
+ local files = environment.files
+ local pattern = environment.arguments.pattern
+ if #files > 0 then
+ for i=1,#files do
+ local filename = files[1]
+ if file.suffix(filename) == "flac" then
+ flac.savecollection(filename,file.replacesuffix(filename,"xml"))
+ elseif lfs.isdir(filename) then
+ local pattern = filename .. "/**.flac"
+ flac.savecollection(pattern,file.addsuffix(file.basename(filename),"xml"))
+ else
+ flac.savecollection(file.replacesuffix(filename,"flac"),file.replacesuffix(filename,"xml"))
+ end
+ end
+ elseif pattern then
+ flac.savecollection(file.addsuffix(pattern,"flac"),"music-collection.xml")
+ else
+ flac.report("no file(s) or pattern given" )
+ end
+end
+
+if environment.argument("collect") then
+ scripts.flac.collect()
+else
+ application.help()
+end
diff --git a/Master/texmf-dist/scripts/context/lua/mtx-fonts.lua b/Master/texmf-dist/scripts/context/lua/mtx-fonts.lua
index 74012ae38d3..8c8f47a70ab 100644
--- a/Master/texmf-dist/scripts/context/lua/mtx-fonts.lua
+++ b/Master/texmf-dist/scripts/context/lua/mtx-fonts.lua
@@ -6,22 +6,107 @@ if not modules then modules = { } end modules ['mtx-fonts'] = {
license = "see context related readme files"
}
+local helpinfo = [[
+--save save open type font in raw table
+
+--reload generate new font database
+--reload --simple generate 'luatex-fonts-names.lua' (not for context!)
+
+--list --name list installed fonts, filter by name [--pattern]
+--list --spec list installed fonts, filter by spec [--filter]
+--list --file list installed fonts, filter by file [--pattern]
+
+--pattern=str filter files using pattern
+--filter=list key-value pairs
+--all show all found instances
+--info give more details
+--track=list enable trackers
+--statistics some info about the database
+
+examples of searches:
+
+mtxrun --script font --list somename (== --pattern=*somename*)
+
+mtxrun --script font --list --name somename
+mtxrun --script font --list --name --pattern=*somename*
+
+mtxrun --script font --list --spec somename
+mtxrun --script font --list --spec somename-bold-italic
+mtxrun --script font --list --spec --pattern=*somename*
+mtxrun --script font --list --spec --filter="fontname=somename"
+mtxrun --script font --list --spec --filter="familyname=somename,weight=bold,style=italic,width=condensed"
+mtxrun --script font --list --spec --filter="familyname=crap*,weight=bold,style=italic"
+
+mtxrun --script font --list --file somename
+mtxrun --script font --list --file --pattern=*somename*
+]]
+
+local application = logs.application {
+ name = "mtx-fonts",
+ banner = "ConTeXt Font Database Management 0.21",
+ helpinfo = helpinfo,
+}
+
+local report = application.report
+
+-- todo: fc-cache -v en check dirs, or better is: fc-cat -v | grep Directory
+
if not fontloader then fontloader = fontforge end
-dofile(resolvers.find_file("font-otp.lua","tex"))
-dofile(resolvers.find_file("font-syn.lua","tex"))
-dofile(resolvers.find_file("font-mis.lua","tex"))
+dofile(resolvers.findfile("font-otp.lua","tex")) -- we need to unpack the font for analysis
+dofile(resolvers.findfile("font-syn.lua","tex"))
+dofile(resolvers.findfile("font-mis.lua","tex"))
scripts = scripts or { }
scripts.fonts = scripts.fonts or { }
+function fonts.names.statistics()
+ fonts.names.load()
+
+ local data = fonts.names.data
+ local statistics = data.statistics
+
+ local function counted(t)
+ local n = { }
+ for k, v in next, t do
+ n[k] = table.count(v)
+ end
+ return table.sequenced(n)
+ end
+
+ report("cache uuid : %s", data.cache_uuid)
+ report("cache version : %s", data.cache_version)
+ report("number of trees : %s", #data.datastate)
+ report()
+ report("number of fonts : %s", statistics.fonts or 0)
+ report("used files : %s", statistics.readfiles or 0)
+ report("skipped files : %s", statistics.skippedfiles or 0)
+ report("duplicate files : %s", statistics.duplicatefiles or 0)
+ report("specifications : %s", #data.specifications)
+ report("families : %s", table.count(data.families))
+ report()
+ report("mappings : %s", counted(data.mappings))
+ report("fallbacks : %s", counted(data.fallbacks))
+ report()
+ report("used styles : %s", table.sequenced(statistics.used_styles))
+ report("used variants : %s", table.sequenced(statistics.used_variants))
+ report("used weights : %s", table.sequenced(statistics.used_weights))
+ report("used widths : %s", table.sequenced(statistics.used_widths))
+ report()
+ report("found styles : %s", table.sequenced(statistics.styles))
+ report("found variants : %s", table.sequenced(statistics.variants))
+ report("found weights : %s", table.sequenced(statistics.weights))
+ report("found widths : %s", table.sequenced(statistics.widths))
+
+end
+
function fonts.names.simple()
local simpleversion = 1.001
local simplelist = { "ttf", "otf", "ttc", "dfont" }
local name = "luatex-fonts-names.lua"
fonts.names.filters.list = simplelist
fonts.names.version = simpleversion -- this number is the same as in font-dum.lua
- logs.report("fontnames","generating font database for 'luatex-fonts' version %s",fonts.names.version)
+ report("generating font database for 'luatex-fonts' version %s",fonts.names.version)
fonts.names.identify(true)
local data = fonts.names.data
if data then
@@ -38,12 +123,12 @@ function fonts.names.simple()
simplemappings[tag] = { s.rawname, s.filename, s.subfont }
end
end
- logs.report("fontnames","saving names in '%s'",name)
+ report("saving names in '%s'",name)
io.savedata(name,table.serialize(simplified,true))
- local data = io.loaddata(resolvers.find_file("font-dum.lua","tex"))
+ local data = io.loaddata(resolvers.findfile("font-dum.lua","tex"))
local dummy = string.match(data,"fonts%.names%.version%s*=%s*([%d%.]+)")
if tonumber(dummy) ~= simpleversion then
- logs.report("fontnames","warning: version number %s in 'font-dum' does not match database version number %s",dummy or "?",simpleversion)
+ report("warning: version number %s in 'font-dum' does not match database version number %s",dummy or "?",simpleversion)
end
elseif lfs.isfile(name) then
os.remove(name)
@@ -75,28 +160,29 @@ local function fontweight(fw)
end
local function showfeatures(tag,specification)
- logs.simple("mapping : %s",tag)
- logs.simple("fontname: %s",specification.fontname)
- logs.simple("fullname: %s",specification.fullname)
- logs.simple("filename: %s",specification.filename)
- logs.simple("family : %s",specification.familyname or "<nofamily>")
- logs.simple("weight : %s",specification.weight or "<noweight>")
- logs.simple("style : %s",specification.style or "<nostyle>")
- logs.simple("width : %s",specification.width or "<nowidth>")
- logs.simple("variant : %s",specification.variant or "<novariant>")
- logs.simple("subfont : %s",subfont(specification.subfont))
- logs.simple("fweight : %s",fontweight(specification.fontweight))
+ report()
+ report("mapping : %s",tag)
+ report("fontname: %s",specification.fontname)
+ report("fullname: %s",specification.fullname)
+ report("filename: %s",specification.filename)
+ report("family : %s",specification.familyname or "<nofamily>")
+ report("weight : %s",specification.weight or "<noweight>")
+ report("style : %s",specification.style or "<nostyle>")
+ report("width : %s",specification.width or "<nowidth>")
+ report("variant : %s",specification.variant or "<novariant>")
+ report("subfont : %s",subfont(specification.subfont))
+ report("fweight : %s",fontweight(specification.fontweight))
-- maybe more
- local features = fonts.get_features(specification.filename,specification.format)
+ local features = fonts.helpers.getfeatures(specification.filename,specification.format)
if features then
for what, v in table.sortedhash(features) do
local data = features[what]
if data and next(data) then
- logs.simple()
- logs.simple("%s features:",what)
- logs.simple()
- logs.simple("feature script languages")
- logs.simple()
+ report()
+ report("%s features:",what)
+ report()
+ report("feature script languages")
+ report()
for f,ff in table.sortedhash(data) do
local done = false
for s, ss in table.sortedhash(ff) do
@@ -107,24 +193,22 @@ local function showfeatures(tag,specification)
else
done = true
end
- logs.simple("% -8s % -8s % -8s",f,s,table.concat(table.sortedkeys(ss), " "))
+ report("% -8s % -8s % -8s",f,s,table.concat(table.sortedkeys(ss), " ")) -- todo: padd 4
end
end
end
end
else
- logs.simple()
- logs.simple("no features")
- logs.simple()
+ report("no features")
end
- logs.reportline()
+ report()
end
local function reloadbase(reload)
if reload then
- logs.simple("fontnames, reloading font database")
+ report("fontnames, reloading font database")
names.load(true)
- logs.simple("fontnames, done\n\n")
+ report("fontnames, done\n\n")
end
end
@@ -151,12 +235,10 @@ local function list_specifications(t,info)
subfont(entry.subfont),
fontweight(entry.fontweight),
}
- e[k] = entry
end
- table.formatcolumns(s)
+ utilities.formatters.formatcolumns(s)
for k=1,#s do
- local v = s[k]
- texio.write_nl(v)
+ texio.write_nl(s[k])
end
end
end
@@ -181,7 +263,7 @@ local function list_matches(t,info)
subfont(entry.subfont)
}
end
- table.formatcolumns(s)
+ utilities.formatters.formatcolumns(s)
for k=1,#s do
texio.write_nl(s[k])
end
@@ -205,17 +287,17 @@ function scripts.fonts.list()
--~ mtxrun --script font --list --name --pattern=*somename*
list_matches(fonts.names.list(string.topattern(pattern,true),reload,all),info)
elseif filter then
- logs.report("fontnames","not supported: --list --name --filter",name)
+ report("not supported: --list --name --filter",name)
elseif given then
--~ mtxrun --script font --list --name somename
list_matches(fonts.names.list(given,reload,all),info)
else
- logs.report("fontnames","not supported: --list --name <no specification>",name)
+ report("not supported: --list --name <no specification>",name)
end
elseif environment.argument("spec") then
if pattern then
--~ mtxrun --script font --list --spec --pattern=*somename*
- logs.report("fontnames","not supported: --list --spec --pattern",name)
+ report("not supported: --list --spec --pattern",name)
elseif filter then
--~ mtxrun --script font --list --spec --filter="fontname=somename"
list_specifications(fonts.names.getlookups(filter),info)
@@ -223,19 +305,19 @@ function scripts.fonts.list()
--~ mtxrun --script font --list --spec somename
list_specifications(fonts.names.collectspec(given,reload,all),info)
else
- logs.report("fontnames","not supported: --list --spec <no specification>",name)
+ report("not supported: --list --spec <no specification>",name)
end
elseif environment.argument("file") then
if pattern then
--~ mtxrun --script font --list --file --pattern=*somename*
list_specifications(fonts.names.collectfiles(string.topattern(pattern,true),reload,all),info)
elseif filter then
- logs.report("fontnames","not supported: --list --spec",name)
+ report("not supported: --list --spec",name)
elseif given then
--~ mtxrun --script font --list --file somename
list_specifications(fonts.names.collectfiles(given,reload,all),info)
else
- logs.report("fontnames","not supported: --list --file <no specification>",name)
+ report("not supported: --list --file <no specification>",name)
end
elseif pattern then
--~ mtxrun --script font --list --pattern=*somename*
@@ -243,8 +325,11 @@ function scripts.fonts.list()
elseif given then
--~ mtxrun --script font --list somename
list_matches(fonts.names.list(given,reload,all),info)
+ elseif all then
+ pattern = "*"
+ list_matches(fonts.names.list(string.topattern(pattern,true),reload,all),info)
else
- logs.report("fontnames","not supported: --list <no specification>",name)
+ report("not supported: --list <no specification>",name)
end
end
@@ -255,19 +340,20 @@ function scripts.fonts.save()
local function save(savename,fontblob)
if fontblob then
savename = savename:lower() .. ".lua"
- logs.simple("fontsave, saving data in %s",savename)
+ report("fontsave, saving data in %s",savename)
+-- fontloader.apply_featurefile(fontblob, "./ts/test.fea")
table.tofile(savename,fontloader.to_table(fontblob),"return")
fontloader.close(fontblob)
end
end
if name and name ~= "" then
- local filename = resolvers.find_file(name) -- maybe also search for opentype
+ local filename = resolvers.findfile(name) -- maybe also search for opentype
if filename and filename ~= "" then
- local suffix = file.extname(filename)
+ local suffix = string.lower(file.extname(filename))
if suffix == 'ttf' or suffix == 'otf' or suffix == 'ttc' or suffix == "dfont" then
local fontinfo = fontloader.info(filename)
if fontinfo then
- logs.simple("font: %s located as %s",name,filename)
+ report("font: %s located as %s",name,filename)
if fontinfo[1] then
for k=1,#fontinfo do
local v = fontinfo[k]
@@ -277,58 +363,19 @@ function scripts.fonts.save()
save(fontinfo.fullname,fontloader.open(filename))
end
else
- logs.simple("font: %s cannot be read",filename)
+ report("font: %s cannot be read",filename)
end
else
- logs.simple("font: %s not saved",filename)
+ report("font: %s not saved",filename)
end
else
- logs.simple("font: %s not found",name)
+ report("font: %s not found",name)
end
else
- logs.simple("font: no name given")
+ report("font: no name given")
end
end
-logs.extendbanner("ConTeXt Font Database Management 0.21",true)
-
-messages.help = [[
---save save open type font in raw table
-
---reload generate new font database
---reload --simple generate 'luatex-fonts-names.lua' (not for context!)
-
---list --name list installed fonts, filter by name [--pattern]
---list --spec list installed fonts, filter by spec [--filter]
---list --file list installed fonts, filter by file [--pattern]
-
---pattern=str filter files using pattern
---filter=list key-value pairs
---all show all found instances
---info give more details
---track=list enable trackers
-
-examples of searches:
-
-mtxrun --script font --list somename (== --pattern=*somename*)
-
-mtxrun --script font --list --name somename
-mtxrun --script font --list --name --pattern=*somename*
-
-mtxrun --script font --list --spec somename
-mtxrun --script font --list --spec somename-bold-italic
-mtxrun --script font --list --spec --pattern=*somename*
-mtxrun --script font --list --spec --filter="fontname=somename"
-mtxrun --script font --list --spec --filter="familyname=somename,weight=bold,style=italic,width=condensed"
-
-mtxrun --script font --list --file somename
-mtxrun --script font --list --file --pattern=*somename*
-]]
-
-local track = environment.argument("track")
-
-if track then trackers.enable(track) end
-
if environment.argument("names") then
environment.setargument("reload",true)
environment.setargument("simple",true)
@@ -340,6 +387,8 @@ elseif environment.argument("reload") then
scripts.fonts.reload()
elseif environment.argument("save") then
scripts.fonts.save()
+elseif environment.argument("statistics") then
+ fonts.names.statistics()
else
- logs.help(messages.help)
+ application.help()
end
diff --git a/Master/texmf-dist/scripts/context/lua/mtx-grep.lua b/Master/texmf-dist/scripts/context/lua/mtx-grep.lua
index 9604bc9f85d..963422c68ec 100644
--- a/Master/texmf-dist/scripts/context/lua/mtx-grep.lua
+++ b/Master/texmf-dist/scripts/context/lua/mtx-grep.lua
@@ -6,11 +6,25 @@ if not modules then modules = { } end modules ['mtx-babel'] = {
license = "see context related readme files"
}
+local helpinfo = [[
+--pattern search for pattern (optional)
+--count count matches only
+--nocomment skip lines that start with %% or #
+
+patterns are lua patterns and need to be escaped accordingly
+]]
+
+local application = logs.application {
+ name = "mtx-grep",
+ banner = "Simple Grepper 0.10",
+ helpinfo = helpinfo,
+}
+
+local report = application.report
+
scripts = scripts or { }
scripts.grep = scripts.grep or { }
-logs.extendbanner("Simple Grepper 0.10",true)
-
local find, format = string.find, string.format
local cr = lpeg.P("\r")
@@ -94,14 +108,6 @@ function scripts.grep.find(pattern, files, offset)
end
end
-messages.help = [[
---pattern search for pattern (optional)
---count count matches only
---nocomment skip lines that start with %% or #
-
-patterns are lua patterns and need to be escaped accordingly
-]]
-
local pattern = environment.argument("pattern")
local files = environment.files and #environment.files > 0 and environment.files
@@ -110,5 +116,5 @@ if pattern and files then
elseif files then
scripts.grep.find(files[1], files, 2)
else
- logs.help(messages.help)
+ application.help()
end
diff --git a/Master/texmf-dist/scripts/context/lua/mtx-interface.lua b/Master/texmf-dist/scripts/context/lua/mtx-interface.lua
index 730a030d930..954cb449870 100644
--- a/Master/texmf-dist/scripts/context/lua/mtx-interface.lua
+++ b/Master/texmf-dist/scripts/context/lua/mtx-interface.lua
@@ -6,7 +6,39 @@ if not modules then modules = { } end modules ['mtx-cache'] = {
license = "see context related readme files"
}
-local format = string.format
+local concat, sort, insert = table.concat, table.sort, table.insert
+local gsub, format, gmatch, find = string.gsub, string.format, string.gmatch, string.find
+local utfchar, utfgsub = utf.char, utf.gsub
+
+local helpinfo = [[
+--interfaces generate context interface files
+--messages generate context message files
+--labels generate context label files
+
+--context equals --interfaces --messages --languages
+
+--scite generate scite interface
+--bbedit generate bbedit interface files
+--jedit generate jedit interface files
+--textpad generate textpad interface files
+--text create text files for commands and environments
+--raw report commands to the console
+--check generate check file
+
+--toutf replace named characters by utf
+--preprocess preprocess mkvi files to tex files [force,suffix]
+
+--suffix use given suffix for output files
+--force force action even when in doubt
+]]
+
+local application = logs.application {
+ name = "mtx-interface",
+ banner = "ConTeXt Interface Related Goodies 0.13",
+ helpinfo = helpinfo,
+}
+
+local report = application.report
scripts = scripts or { }
scripts.interface = scripts.interface or { }
@@ -15,11 +47,11 @@ local flushers = { }
local userinterfaces = { 'en','cs','de','it','nl','ro','fr','pe' }
local messageinterfaces = { 'en','cs','de','it','nl','ro','fr','pe','no' }
-function flushers.scite(interface,collection)
+function flushers.scite(interface,commands)
local result, i = {}, 0
result[#result+1] = format("keywordclass.macros.context.%s=",interface)
- for i=1,#collection do
- local command = collection[i]
+ for i=1,#commands do
+ local command = commands[i]
if i==0 then
result[#result+1] = "\\\n"
i = 5
@@ -28,64 +60,139 @@ function flushers.scite(interface,collection)
end
result[#result+1] = format("%s ",command)
end
- io.savedata(format("cont-%s-scite.properties",interface), table.concat(result),"\n")
- io.savedata(format("cont-%s-scite.lua",interface), table.serialize(collection,true))
+ io.savedata(format("cont-%s-scite.properties",interface), concat(result),"\n")
+ io.savedata(format("cont-%s-scite.lua",interface), table.serialize(commands,true))
end
-function flushers.jedit(interface,collection)
+function flushers.jedit(interface,commands)
local result = {}
result[#result+1] = "<?xml version='1.0'?>"
result[#result+1] = "<!DOCTYPE MODE SYSTEM 'xmode.dtd'>\n"
result[#result+1] = "<MODE>"
result[#result+1] = "\t<RULES>"
result[#result+1] = "\t\t<KEYWORDS>"
- for i=1,#collection do
- local command = collection[i]
- result[#result+1] = format("\t\t\t<KEYWORD2>%s</KEYWORD2>",command)
+ for i=1,#commands do
+ result[#result+1] = format("\t\t\t<KEYWORD2>%s</KEYWORD2>",commands[i])
end
result[#result+1] = "\t\t</KEYWORDS>"
result[#result+1] = "\t</RULES>"
result[#result+1] = "</MODE>"
- io.savedata(format("context-jedit-%s.xml",interface), table.concat(result),"\n")
+ io.savedata(format("context-jedit-%s.xml",interface), concat(result),"\n")
end
-function flushers.bbedit(interface,collection)
+function flushers.bbedit(interface,commands)
local result = {}
result[#result+1] = "<?xml version='1.0'?>"
result[#result+1] = "<key>BBLMKeywordList</key>"
result[#result+1] = "<array>"
- for i=1,#collection do
- local command = collection[i]
- result[#result+1] = format("\t<string>\\%s</string>",command)
+ for i=1,#commands do
+ result[#result+1] = format("\t<string>\\%s</string>",commands[i])
end
result[#result+1] = "</array>"
- io.savedata(format("context-bbedit-%s.xml",interface), table.concat(result),"\n")
+ io.savedata(format("context-bbedit-%s.xml",interface), concat(result),"\n")
+end
+
+function flushers.raw(interface,commands)
+ for i=1,#commands do
+ report(commands[i])
+ end
+end
+
+local textpadcreator = "mtx-interface-textpad.lua"
+
+function flushers.text(interface,commands,environments)
+ local c, cname = { }, format("context-commands-%s.txt",interface)
+ local e, ename = { }, format("context-environments-%s.txt",interface)
+ report("saving '%s'",cname)
+ for i=1,#commands do
+ c[#c+1] = format("\\%s",commands[i])
+ end
+ io.savedata(cname,concat(c,"\n"))
+ report("saving '%s'",ename)
+ for i=1,#environments do
+ e[#e+1] = format("\\start%s",environments[i])
+ e[#e+1] = format("\\stop%s", environments[i])
+ end
+ io.savedata(format("context-environments-%s.txt",interface),concat(e,"\n"))
end
-function flushers.raw(interface,collection)
- for i=1,#collection do
- local command = collection[i]
- logs.simple(command)
+function flushers.textpad(interface,commands,environments)
+ flushers.text(interface,commands,environments)
+ --
+ -- plugin, this is a rewrite of a file provided by Lukas Prochazka
+ --
+ local function merge(templatedata,destinationdata,categories)
+ report("loading '%s'",templatedata)
+ local data = io.loaddata(templatedata)
+ local done = 0
+ for i=1,#categories do
+ local category = categories[i]
+ local cpattern = ";%s*category:%s*(" .. category .. ")%s*[\n\r]+"
+ local fpattern = ";%s*filename:%s*(" .. "%S+" .. ")%s*[\n\r]+"
+ data = gsub(data,cpattern..fpattern,function(category,filename)
+ local found = resolvers.findfile(filename) or ""
+ local blob = found ~= "" and io.loaddata(found) or ""
+ if blob == "" then
+ report("category: %s, filename: %s, not found",category,filename)
+ else
+ done = done + 1
+ report("category: %s, filename: %s, merged",category,filename)
+ end
+ return format("; category: %s\n; filename: %s\n%s\n\n",category,filename,blob)
+ end)
+ end
+ if done > 0 then
+ report("saving '%s' (%s files merged)",destinationdata,done)
+ io.savedata(destinationdata,data)
+ else
+ report("skipping '%s' (no files merged)",destinationdata)
+ end
+ end
+ local templatename = "textpad-context-template.txt"
+ local templatedata = resolvers.findfile(templatename) or ""
+ if templatedata == "" then
+ report("unable to locate template '%s'",templatename)
+ else
+ merge(templatedata, "context.syn", { "tex commands","context commands" })
+ if environment.argument("textpad") == "latex" then
+ merge(templatedata, "context-latex.syn", { "tex commands","context commands", "latex commands" })
+ end
end
+ local r = { }
+ local c = io.loaddata("context-commands-en.txt") or "" -- sits on the same path
+ local e = io.loaddata("context-environments-en.txt") or "" -- sits on the same path
+ for s in gmatch(c,"\\(.-)%s") do
+ r[#r+1] = format("\n!TEXT=%s\n\\%s\n!",s,s)
+ end
+ for s in gmatch(e,"\\start(.-)%s+\\stop(.-)") do
+ r[#r+1] = format("\n!TEXT=%s (start/stop)\n\\start%s \\^\\stop%s\n!",s,s,s)
+ end
+ sort(r)
+ insert(r,1,"!TCL=597,\n!TITLE=ConTeXt\n!SORT=N\n!CHARSET=DEFAULT")
+ io.savedata("context.tcl",concat(r,"\n"))
+ -- cleanup
+ os.remove("context-commands-en.txt")
+ os.remove("context-environments-en.txt")
end
-function scripts.interface.editor(editor)
- local interfaces= environment.files
+function scripts.interface.editor(editor,split,forcedinterfaces)
+ local interfaces= forcedinterfaces or environment.files
if #interfaces == 0 then
interfaces= userinterfaces
end
- local xmlfile = resolvers.find_file("cont-en.xml") or ""
+ local xmlfile = resolvers.findfile("cont-en.xml") or ""
if xmlfile == "" then
- logs.simple("unable to locate cont-en.xml")
+ report("unable to locate cont-en.xml")
end
for i=1,#interfaces do
local interface = interfaces[i]
- local keyfile = resolvers.find_file(format("keys-%s.xml",interface)) or ""
+ local keyfile = resolvers.findfile(format("keys-%s.xml",interface)) or ""
if keyfile == "" then
- logs.simple("unable to locate keys-*.xml")
+ report("unable to locate keys-*.xml")
else
- local collection = { }
- local mappings = { }
+ local commands = { }
+ local mappings = { }
+ local environments = { }
local x = xml.load(keyfile)
for e, d, k in xml.elements(x,"cd:command") do
local at = d[k].at
@@ -101,23 +208,28 @@ function scripts.interface.editor(editor)
if name and name ~= "" then
local remapped = mappings[name] or name
if type == "environment" then
- collection[#collection+1] = "start" .. remapped
- collection[#collection+1] = "stop" .. remapped
+ if split then
+ environments[#environments+1] = remapped
+ else
+ commands[#commands+1] = "start" .. remapped
+ commands[#commands+1] = "stop" .. remapped
+ end
else
- collection[#collection+1] = remapped
+ commands[#commands+1] = remapped
end
end
end
- if #collection > 0 then
- table.sort(collection)
- flushers[editor](interface,collection)
+ if #commands > 0 then
+ sort(commands)
+ sort(environments)
+ flushers[editor](interface,commands,environments)
end
end
end
end
function scripts.interface.check()
- local xmlfile = resolvers.find_file("cont-en.xml") or ""
+ local xmlfile = resolvers.findfile("cont-en.xml") or ""
if xmlfile ~= "" then
local f = io.open("cont-en-check.tex","w")
if f then
@@ -142,13 +254,13 @@ function scripts.interface.check()
end
end
-function scripts.interface.context()
- local filename = resolvers.find_file(environment.files[1] or "mult-def.lua") or ""
+function scripts.interface.interfaces()
+ local filename = resolvers.findfile(environment.files[1] or "mult-def.lua") or ""
if filename ~= "" then
local interface = dofile(filename)
if interface and next(interface) then
local variables, constants, commands, elements = interface.variables, interface.constants, interface.commands, interface.elements
- local filename = resolvers.find_file("cont-en.xml") or ""
+ local filename = resolvers.findfile("cont-en.xml") or ""
local xmldata = filename ~= "" and (io.loaddata(filename) or "")
local function flush(texresult,xmlresult,language,what,tag)
local t = interface[what]
@@ -161,7 +273,7 @@ function scripts.interface.context()
local v = t[key]
local value = v[language] or v["en"]
if not value then
- logs.simple(format("warning, no value for key '%s' for language '%s'",key,language))
+ report("warning, no value for key '%s' for language '%s'",key,language)
else
local value = t[key][language] or t[key].en
texresult[#texresult+1] = format("\\setinterface%s{%s}{%s}",tag,key,value)
@@ -196,12 +308,12 @@ function scripts.interface.context()
flush(texresult,xmlresult,language,"commands", "command")
texresult[#texresult+1] = format("%%\n\\endinput")
xmlresult[#xmlresult+1] = format("</cd:interface>")
- local texfilename = format("mult-%s.tex",language)
+ local texfilename = format("mult-%s.mkii",language)
local xmlfilename = format("keys-%s.xml",language)
- io.savedata(texfilename,table.concat(texresult,"\n"))
- logs.simple(format("saving interface definitions '%s'",texfilename))
- io.savedata(xmlfilename,table.concat(xmlresult,"\n"))
- logs.simple(format("saving interface translations '%s'",xmlfilename))
+ io.savedata(texfilename,concat(texresult,"\n"))
+ report("saving interface definitions '%s'",texfilename)
+ io.savedata(xmlfilename,concat(xmlresult,"\n"))
+ report("saving interface translations '%s'",xmlfilename)
if language ~= "en" and xmldata ~= "" then
local newdata = xmldata:gsub("(<cd:interface.*language=.)en(.)","%1"..language.."%2",1)
newdata = replace(newdata, 'cd:string', 'value', interface.commands, interface.elements, language)
@@ -212,63 +324,208 @@ function scripts.interface.context()
newdata = replace(newdata, 'cd:inherit', 'name', interface.commands, interface.elements, language)
local xmlfilename = format("cont-%s.xml",language)
io.savedata(xmlfilename,newdata)
- logs.simple(format("saving interface specification '%s'",xmlfilename))
+ report("saving interface specification '%s'",xmlfilename)
end
end
end
end
end
-function scripts.interface.messages()
- local filename = resolvers.find_file(environment.files[1] or "mult-mes.lua") or ""
- if filename ~= "" then
- local messages = dofile(filename)
- for i=1,#messageinterfaces do
- local interface = messageinterfaces[i]
- local texresult = { }
- for category, data in next, messages do
- for tag, message in next, data do
- if tag ~= "files" then
- local msg = message[interface] or message["all"] or message["en"]
- if msg then
- texresult[#texresult+1] = format("\\setinterfacemessage{%s}{%s}{%s}",category,tag,msg)
- end
- end
- end
+function scripts.interface.preprocess()
+ require("luat-mac.lua")
+ local newsuffix = environment.argument("suffix") or "log"
+ local force = environment.argument("force")
+ for i=1,#environment.files do
+ local oldname = environment.files[i]
+ local newname = file.replacesuffix(oldname,newsuffix)
+ if oldname == newname then
+ report("skipping '%s' because old and new name are the same",oldname)
+ elseif io.exists(newname) and not force then
+ report("skipping '%s' because new file exists, use --force",oldname)
+ else
+ report("processing '%s' into '%s'",oldname,newname)
+ io.savedata(newname,resolvers.macros.preprocessed(io.loaddata(oldname)))
+ end
+ end
+end
+
+-- function scripts.interface.messages()
+-- local filename = resolvers.findfile(environment.files[1] or "mult-mes.lua") or ""
+-- if filename ~= "" then
+-- local messages = dofile(filename)
+-- report("messages for * loaded from '%s'",filename)
+-- report()
+-- for i=1,#messageinterfaces do
+-- local interface = messageinterfaces[i]
+-- local texresult = { }
+-- for category, data in next, messages do
+-- for tag, message in next, data do
+-- if tag ~= "files" then
+-- local msg = message[interface] or message["all"] or message["en"]
+-- if msg then
+-- texresult[#texresult+1] = format("\\setinterfacemessage{%s}{%s}{%s}",category,tag,msg)
+-- end
+-- end
+-- end
+-- end
+-- texresult[#texresult+1] = format("%%\n\\endinput")
+-- local interfacefile = format("mult-m%s.mkii",interface)
+-- io.savedata(interfacefile,concat(texresult,"\n"))
+-- report("messages for '%s' saved in '%s'",interface,interfacefile)
+-- end
+-- end
+-- end
+
+function scripts.interface.toutf()
+ local filename = environment.files[1]
+ if filename then
+ require("char-def.lua")
+ local contextnames = { }
+ for unicode, data in next, characters.data do
+ local contextname = data.contextname
+ if contextname then
+ contextnames[contextname] = utf.char(unicode)
end
- texresult[#texresult+1] = format("%%\n\\endinput")
- io.savedata(format("mult-m%s.tex",interface),table.concat(texresult,"\n"))
+ contextnames.uumlaut = contextnames.udiaeresis
+ contextnames.Uumlaut = contextnames.Udiaeresis
+ contextnames.oumlaut = contextnames.odiaeresis
+ contextnames.Oumlaut = contextnames.Odiaeresis
+ contextnames.aumlaut = contextnames.adiaeresis
+ contextnames.Aumlaut = contextnames.Adiaeresis
end
+ report("loading '%s'",filename)
+ local str = io.loaddata(filename) or ""
+ local done = { }
+ str = gsub(str,"(\\)([a-zA-Z][a-zA-Z][a-zA-Z]+)(%s*)", function(b,s,a)
+ local cn = contextnames[s]
+ if cn then
+ done[s] = (done[s] or 0) + 1
+ return cn
+ else
+ done[s] = (done[s] or 0) - 1
+ return b .. s .. a
+ end
+ end)
+ for k, v in table.sortedpairs(done) do
+ if v > 0 then
+ report("+ %5i : %s => %s",v,k,contextnames[k])
+ else
+ report("- %5i : %s",-v,k,contextnames[k])
+ end
+ end
+ filename = filename .. ".toutf"
+ report("saving '%s'",filename)
+ io.savedata(filename,str)
end
end
-logs.extendbanner("ConTeXt Interface Related Goodies 0.11",true)
+-- function scripts.interface.labels()
+-- require("char-def.lua")
+-- require("lang-txt.lua")
+-- local interfaces = require("mult-def.lua")
+-- local variables = interfaces.variables
+-- local contextnames = { }
+-- for unicode, data in next, characters.data do
+-- local contextname = data.contextname
+-- if contextname then
+-- contextnames[utfchar(unicode)] = "\\" .. contextname .. " "
+-- end
+-- end
+-- contextnames["i"] = nil
+-- contextnames["'"] = nil
+-- contextnames["\\"] = nil
+-- local function flush(f,kind,what,expand,namespace,prefix)
+-- local whatdata = languages.data.labels[what]
+-- f:write("\n")
+-- f:write(format("%% %s => %s\n",what,kind))
+-- for tag, data in table.sortedpairs(whatdata) do
+-- if not data.hidden then
+-- f:write("\n")
+-- for language, text in table.sortedpairs(data.labels) do
+-- if text ~= "" then
+-- if expand then
+-- text = utfgsub(text,".",contextnames)
+-- text = gsub(text," ", "\ ")
+-- end
+-- if namespace and namespace[tag] then
+-- tag = prefix .. tag
+-- end
+-- if find(text,",") then
+-- text = "{" .. text .. "}"
+-- end
+-- if text == "" then
+-- -- skip
+-- else
+-- if type(text) == "table" then
+-- f:write(format("\\setup%stext[\\s!%s][%s={{%s},}]\n",kind,language,tag,text))
+-- else
+-- f:write(format("\\setup%stext[\\s!%s][%s={{%s},{%s}}]\n",kind,language,tag,text[1],text[2]))
+-- end
+-- end
+-- end
+-- end
+-- end
+-- end
+-- end
+-- function flushall(txtname,expand)
+-- local f = io.open(txtname,"w")
+-- if f then
+-- report("saving '%s'",txtname)
+-- f:write("% this file is auto-generated, don't edit this file\n")
+-- flush(f,"head","titles",expand,variables,"\\v!")
+-- flush(f,"label","texts",expand,variables,"\\v!")
+-- flush(f,"mathlabel","functions",expand)
+-- flush(f,"taglabel","tags",expand)
+-- f:write("\n")
+-- f:write("\\endinput\n")
+-- f:close()
+-- end
+-- end
+-- flushall("lang-txt.mkii",true)
+-- flushall("lang-txt.mkiv",false)
+-- end
-messages.help = [[
---scite generate scite interface
---bbedit generate scite interface
---jedit generate scite interface
---check generate check file
---context generate context definition files
---messages generate context message files
-]]
+local ea = environment.argument
-if environment.argument("context") then
- scripts.interface.context()
-elseif environment.argument("messages") then
- scripts.interface.messages()
-elseif environment.argument("scite") or environment.argument("bbedit") or environment.argument("jedit") then
- if environment.argument("scite") then
+if ea("context") then
+ scripts.interface.interfaces()
+ -- scripts.interface.messages()
+ -- scripts.interface.labels()
+elseif ea("interfaces") or ea("messages") or ea("labels") then
+ if ea("interfaces") then
+ scripts.interface.interfaces()
+ end
+ -- if ea("messages") then
+ -- scripts.interface.messages()
+ -- end
+ -- if ea("labels") then
+ -- scripts.interface.labels()
+ -- end
+elseif ea("preprocess") then
+ scripts.interface.preprocess()
+elseif ea("toutf") then
+ scripts.interface.toutf()
+elseif ea("check") then
+ scripts.interface.check()
+elseif ea("scite") or ea("bbedit") or ea("jedit") or ea("textpad") or ea("text") or ea("raw") then
+ if ea("scite") then
scripts.interface.editor("scite")
end
- if environment.argument("bbedit") then
+ if ea("bbedit") then
scripts.interface.editor("bbedit")
end
- if environment.argument("jedit") then
+ if ea("jedit") then
scripts.interface.editor("jedit")
end
-elseif environment.argument("check") then
- scripts.interface.check()
+ if ea("textpad") then
+ scripts.interface.editor("textpad",true, { "en" })
+ end
+ if ea("text") then
+ scripts.interface.editor("text")
+ end
+ if ea("raw") then
+ scripts.interface.editor("raw")
+ end
else
- logs.help(messages.help)
+ application.help()
end
diff --git a/Master/texmf-dist/scripts/context/lua/mtx-metapost.lua b/Master/texmf-dist/scripts/context/lua/mtx-metapost.lua
new file mode 100644
index 00000000000..3b9ed6ff17e
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/lua/mtx-metapost.lua
@@ -0,0 +1,177 @@
+if not modules then modules = { } end modules ['mtx-metapost'] = { -- this was mtx-mptopdf
+ version = 0.100,
+ comment = "companion to mtxrun.lua",
+ author = "Taco Hoekwater & Hans Hagen",
+ copyright = "ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local helpinfo = [[
+--rawmp raw metapost run
+--metafun use metafun instead of plain
+--latex force --tex=latex
+--texexec force texexec usage (mkii)
+--split split single result file into pages
+
+intended usage:
+
+mtxrun --script metapost yourfile.mp
+mtxrun --script metapost --split yourfile.mp
+mtxrun --script metapost yourfile.123 myfile.mps
+
+other usage resembles mptopdf.pl
+]]
+
+local application = logs.application {
+ name = "mtx-metapost",
+ banner = "MetaPost to PDF processor 0.10",
+ helpinfo = helpinfo,
+}
+
+local report = application.report
+
+scripts = scripts or { }
+scripts.mptopdf = scripts.mptopdf or { }
+scripts.mptopdf.aux = scripts.mptopdf.aux or { }
+
+local format, find, gsub = string.format, string.find, string.gsub
+
+local function assumes_latex(filename)
+ local d = io.loaddata(filename) or ""
+ return find(d,"\\documentstyle") or find(d,"\\documentclass") or find(d,"\\begin{document}")
+end
+
+local template = "\\startTEXpage\n\\convertMPtoPDF{%s}{1}{1}\n\\stopTEXpage"
+local texified = "\\starttext\n%s\n\\stoptext"
+local splitter = "\\startTEXpage\\externalfigure[%s][page=%s]\\stopTEXpage"
+local tempname = "mptopdf-temp.tex"
+
+local function do_convert(filename)
+ if find(filename,".%d+$") or find(filename,"%.mps$") then
+ io.savedata(tempname,format(template,filename))
+ local resultname = format("%s-%s.pdf",file.nameonly(filename),file.extname(filename))
+ local result = os.execute(format([[context --once --batch --purge --result=%s "%s"]],resultname,tempname))
+ return lfs.isfile(resultname) and resultname
+ end
+end
+
+local function do_split(filename,numbers)
+ local name = file.nameonly(filename)
+ for i=1,#numbers do
+ io.savedata(tempname,format(splitter,file.addsuffix(name,"pdf"),i))
+ local resultname = format("%s-%s.pdf",name,numbers[i])
+ local result = os.execute(format([[context --once --batch --purge --result=%s "%s"]],resultname,tempname))
+ end
+end
+
+local function do_texify(str)
+ -- This only works for flat mp files i.e. outer beginfigs. Normally a
+ -- context user will directly make a tex file. Of course we can make
+ -- this script more clever, but why should we as better methods exist.
+ local numbers = { }
+ str = "\\startMPinclusions\n".. str .. "\n\\stopMPinclusions"
+ str = gsub(str,"beginfig%s*%(%s*(.-)%s*%)%s*;%s*",function(s)
+ numbers[#numbers+1] = tonumber(s) or 0
+ return "\n\\stopMPinclusions\n\\startMPpage\n"
+ end)
+ str = gsub(str,"%s*endfig%s*;%s*","\n\\stopMPpage\n\\startMPinclusions\n")
+ str = gsub(str,"\\startMPinclusions%s*\\stopMPinclusions","")
+ str = gsub(str,"[\n\r]+","\n")
+ return format(texified,str), numbers
+end
+
+local function do_convert_all(filename)
+ local results = dir.glob(file.nameonly(filename) .. ".*") -- reset
+ local report = { }
+ for i=1,#results do
+ local filename = results[i]
+ local resultname = do_convert(filename)
+ if resultname then
+ report[#report+1] = { filename, resultname }
+ end
+ end
+ if #report > 0 then
+ report("number of converted files: %i", #report)
+ report()
+ for i=1,#report do
+ local r = report[i]
+ report("%s => %s", r[1], r[2])
+ end
+ else
+ report("no files are converted for '%s'",filename)
+ end
+end
+
+local function do_convert_one(filename)
+ local resultname = do_convert(filename)
+ if resultname then
+ report("%s => %s", filename,resultname)
+ else
+ report("no result for '%s'",filename)
+ end
+end
+
+function scripts.mptopdf.convertall()
+ local rawmp = environment.arguments.rawmp or false
+ local metafun = environment.arguments.metafun or false
+ local latex = environment.arguments.latex or false
+ local pattern = environment.arguments.pattern or false
+ local split = environment.arguments.split or false
+ local files
+ if pattern then
+ files = dir.glob(file.nameonly(filename))
+ else
+ files = environment.files
+ end
+ if #files > 0 then
+ for i=1,#files do
+ local filename = files[i]
+ if file.suffix(filename) == "mp" then
+ local command, convert, texdata, numbers
+ if rawmp then
+ if metafun then
+ command, convert = format("mpost --progname=mpost --mem=metafun %s",filename), true
+ else
+ command, convert = format("mpost --mem=mpost %s",filename), true
+ end
+ else
+ if latex or assumes_latex(filename) then
+ command, convert = format("mpost --mem=mpost --tex=latex %s",filename), true
+ elseif texexec then
+ command, convert = format("texexec --mptex %s",filename), true
+ else
+ texdata, numbers = do_texify(io.loaddata(filename) or "")
+ io.savedata(tempname,texdata)
+ command, convert = format("context --result=%s --purge --once %s",file.nameonly(filename),tempname), false
+ end
+ end
+ report("running: %s",command)
+ local done = os.execute(command)
+ if done then
+ if convert then
+ do_convert_all(filename)
+ elseif split then
+ do_split(filename,numbers)
+ -- already pdf, maybe optionally split
+ end
+ else
+ report("error while processing mp file '%s'", filename)
+ end
+ else
+ do_convert_one(filename)
+ end
+ end
+ else
+ report("no files match to process")
+ end
+end
+
+if environment.files[1] then
+ scripts.mptopdf.convertall()
+else
+ if not environment.arguments.help then
+ report("provide MP output file (or pattern)")
+ report()
+ end
+ application.help()
+end
diff --git a/Master/texmf-dist/scripts/context/lua/mtx-metatex.lua b/Master/texmf-dist/scripts/context/lua/mtx-metatex.lua
index 4453e2ccb47..0b66d59ef3a 100644
--- a/Master/texmf-dist/scripts/context/lua/mtx-metatex.lua
+++ b/Master/texmf-dist/scripts/context/lua/mtx-metatex.lua
@@ -8,39 +8,41 @@ if not modules then modules = { } end modules ['mtx-metatex'] = {
-- future versions will deal with specific variants of metatex
+local helpinfo = [[
+--run process (one or more) files (default action)
+--make create metatex format(s)
+]]
+
+local application = logs.application {
+ name = "mtx-metatex",
+ banner = "MetaTeX Process Management 0.10",
+ helpinfo = helpinfo,
+}
+
+local report = application.report
+
scripts = scripts or { }
scripts.metatex = scripts.metatex or { }
-- metatex
function scripts.metatex.make()
- local command = "luatools --make --compile metatex"
- logs.simple("running command: %s",command)
- os.spawn(command)
+ environment.make_format("metatex")
end
---~ function scripts.metatex.run()
---~ local name = environment.files[1] or ""
---~ if name ~= "" then
---~ local command = "luatools --fmt=metatex " .. name
---~ logs.simple("running command: %s",command)
---~ os.spawn(command)
---~ end
---~ end
-
function scripts.metatex.run(ctxdata,filename)
local filename = environment.files[1] or ""
if filename ~= "" then
- local formatfile, scriptfile = resolvers.locate_format("metatex")
+ local formatfile, scriptfile = resolvers.locateformat("metatex")
if formatfile and scriptfile then
local command = string.format("luatex --fmt=%s --lua=%s %s",
string.quote(formatfile), string.quote(scriptfile), string.quote(filename))
- logs.simple("running command: %s",command)
+ report("running command: %s",command)
os.spawn(command)
elseif formatname then
- logs.simple("error, no format found with name: %s",formatname)
+ report("error, no format found with name: %s",formatname)
else
- logs.simple("error, no format found (provide formatname or interface)")
+ report("error, no format found (provide formatname or interface)")
end
end
end
@@ -49,13 +51,6 @@ function scripts.metatex.timed(action)
statistics.timed(action)
end
-logs.extendbanner("MetaTeX Process Management 0.10",true)
-
-messages.help = [[
---run process (one or more) files (default action)
---make create metatex format(s)
-]]
-
if environment.argument("run") then
scripts.metatex.timed(scripts.metatex.run)
elseif environment.argument("make") then
@@ -65,5 +60,5 @@ elseif environment.argument("help") then
elseif environment.files[1] then
scripts.metatex.timed(scripts.metatex.run)
else
- logs.help(messages.help,false)
+ application.help()
end
diff --git a/Master/texmf-dist/scripts/context/lua/mtx-modules.lua b/Master/texmf-dist/scripts/context/lua/mtx-modules.lua
index 3a348593f8e..8f3ab89cfb6 100644
--- a/Master/texmf-dist/scripts/context/lua/mtx-modules.lua
+++ b/Master/texmf-dist/scripts/context/lua/mtx-modules.lua
@@ -9,6 +9,20 @@ if not modules then modules = { } end modules ['mtx-modules'] = {
scripts = scripts or { }
scripts.modules = scripts.modules or { }
+local helpinfo = [[
+--convert convert source files (tex, mkii, mkiv, mp) to 'ted' files
+--process process source files (tex, mkii, mkiv, mp) to 'pdf' files
+--prep use original name with suffix 'prep' appended
+]]
+
+local application = logs.application {
+ name = "mtx-modules",
+ banner = "ConTeXt Module Documentation Generators 1.00",
+ helpinfo = helpinfo,
+}
+
+local report = application.report
+
-- Documentation can be woven into a source file. This script can generates
-- a file with the documentation and source fragments properly tagged. The
-- documentation is included as comment:
@@ -39,15 +53,15 @@ local find, format, sub, is_empty, strip = string.find, string.format, string.su
local function source_to_ted(inpname,outname,filetype)
local inp = io.open(inpname)
if not inp then
- logs.simple("unable to open '%s'",inpname)
+ report("unable to open '%s'",inpname)
return
end
local out = io.open(outname,"w")
if not out then
- logs.simple("unable to open '%s'",outname)
+ report("unable to open '%s'",outname)
return
end
- logs.simple("converting '%s' to '%s'",inpname,outname)
+ report("converting '%s' to '%s'",inpname,outname)
local skiplevel, indocument, indefinition = 0, false, false
out:write(format("\\startmodule[type=%s]\n",filetype or file.suffix(inpname)))
for line in inp:lines() do
@@ -118,7 +132,7 @@ local function source_to_ted(inpname,outname,filetype)
return true
end
-local suffixes = table.tohash { 'tex','mkii','mkiv','mp' }
+local suffixes = table.tohash { 'tex','mkii','mkiv', 'mkvi', 'mp' }
function scripts.modules.process(runtex)
local processed = { }
@@ -143,25 +157,16 @@ function scripts.modules.process(runtex)
end
for i=1,#processed do
local name = processed[i]
- logs.simple("modules","processed: %s",name)
+ report("modules","processed: %s",name)
end
end
-- context --ctx=m-modules.ctx xxx.mkiv
-
-logs.extendbanner("ConTeXt Module Documentation Generators 1.00",true)
-
-messages.help = [[
---convert convert source files (tex, mkii, mkiv, mp) to 'ted' files
---process process source files (tex, mkii, mkiv, mp) to 'pdf' files
---prep use original name with suffix 'prep' appended
-]]
-
if environment.argument("process") then
scripts.modules.process(true)
elseif environment.argument("convert") then
scripts.modules.process(false)
else
- logs.help(messages.help)
+ application.help()
end
diff --git a/Master/texmf-dist/scripts/context/lua/mtx-mptopdf.lua b/Master/texmf-dist/scripts/context/lua/mtx-mptopdf.lua
deleted file mode 100644
index 342ff1c284a..00000000000
--- a/Master/texmf-dist/scripts/context/lua/mtx-mptopdf.lua
+++ /dev/null
@@ -1,127 +0,0 @@
-if not modules then modules = { } end modules ['mtx-mptopdf'] = {
- version = 1.303,
- comment = "companion to mtxrun.lua, patched by HH so errors are his",
- author = "Taco Hoekwater, Elvenkind BV, Dordrecht NL",
- copyright = "Elvenkind BV / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-scripts = scripts or { }
-scripts.mptopdf = scripts.mptopdf or { }
-scripts.mptopdf.aux = scripts.mptopdf.aux or { }
-
-local dosish = os.type == 'windows'
-local miktex = dosish and environment.TEXSYSTEM and environment.TEXSYSTEM:find("miktex")
-local escapeshell = environment.SHELL and environment.SHELL:find("sh") and true
-
-function scripts.mptopdf.aux.find_latex(fname)
- local d = io.loaddata(fname) or ""
- return d:find("\\documentstyle") or d:find("\\documentclass") or d:find("\\begin{document}")
-end
-
-function scripts.mptopdf.aux.do_convert (fname)
- local command, done, pdfdest = "", 0, ""
- if fname:find(".%d+$") or fname:find("%.mps$") then
- if miktex then
- command = "pdftex -undump=mptopdf"
- else
- command = "pdftex -fmt=mptopdf -progname=context"
- end
- if dosish then
- command = string.format('%s \\relax "%s"',command,fname)
- else
- command = string.format('%s \\\\relax "%s"',command,fname)
- end
- local result = os.execute(command)
- if result == 0 then
- local name, suffix = file.nameonly(fname), file.extname(fname)
- local pdfsrc = name .. ".pdf"
- if lfs.isfile(pdfsrc) then
- pdfdest = name .. "-" .. suffix .. ".pdf"
- os.rename(pdfsrc, pdfdest)
- if lfs.isfile(pdfsrc) then -- rename failed
- file.copy(pdfsrc, pdfdest)
- end
- done = 1
- end
- end
- end
- return done, pdfdest
-end
-
-function scripts.mptopdf.aux.make_mps(fn,latex,rawmp,metafun)
- local rest, mpbin = latex and " --tex=latex " or " ", ""
- if rawmp then
- if metafun then
- mpbin = "mpost --progname=mpost --mem=metafun"
- else
- mpbin = "mpost --mem=mpost"
- end
- else
- if latex then
- mpbin = "mpost --mem=mpost"
- else
- mpbin = "texexec --mptex"
- end
- end
- local runner = mpbin .. rest .. fn
- logs.simple("running: %s\n", runner)
- return (os.execute(runner))
-end
-
-function scripts.mptopdf.convertall()
- local rawmp = environment.arguments.rawmp or false
- local metafun = environment.arguments.metafun or false
- local latex = environment.arguments.latex or false
- local files = dir.glob(environment.files)
- if #files > 0 then
- local fn = files[1]
- if #files == 1 and fn:find("%.mp$") then
- latex = scripts.mptopdf.aux.find_latex(fn) or latex
- end
- if scripts.mptopdf.aux.make_mps(fn,latex,rawmp,metafun) then
- files = dir.glob(file.nameonly(fn) .. ".*") -- reset
- else
- logs.simple("error while processing mp file '%s'", fn)
- exit(1)
- end
- local report = { }
- for i=1,#files do
- local fn = files[i]
- local success, name = scripts.mptopdf.aux.do_convert(fn)
- if success > 0 then
- report[#report+1] = { fn, name }
- end
- end
- if #report > 0 then
- logs.simple("number of converted files: %i", #report)
- logs.simple("")
- for i=1,#report do
- local r = report[i]
- logs.simple("%s => %s", r[1], r[2])
- end
- else
- logs.simple("no files are converted")
- end
- else
- logs.simple("no files match %s", table.concat(environment.files,' '))
- end
-end
-
-logs.extendbanner("MetaPost to PDF Converter 0.51",true)
-
-messages.help = [[
---rawmp raw metapost run
---metafun use metafun instead of plain
---latex force --tex=latex
-]]
-
-if environment.files[1] then
- scripts.mptopdf.convertall()
-else
- if not environment.arguments.help then
- logs.simple("provide MP output file (or pattern)")
- logs.simple("")
- end
- logs.help(messages.help)
-end
diff --git a/Master/texmf-dist/scripts/context/lua/mtx-package.lua b/Master/texmf-dist/scripts/context/lua/mtx-package.lua
index b36fc0ed89c..294ef4f285a 100644
--- a/Master/texmf-dist/scripts/context/lua/mtx-package.lua
+++ b/Master/texmf-dist/scripts/context/lua/mtx-package.lua
@@ -8,22 +8,34 @@ if not modules then modules = { } end modules ['mtx-package'] = {
local format, gsub, gmatch = string.format, string.gsub, string.gmatch
+local helpinfo = [[
+--merge merge 'loadmodule' into merge file
+]]
+
+local application = logs.application {
+ name = "mtx-package",
+ banner = "Distribution Related Goodies 0.10",
+ helpinfo = helpinfo,
+}
+
+local report = application.report
+
scripts = scripts or { }
messages = messages or { }
scripts.package = scripts.package or { }
function scripts.package.merge_luatex_files(name,strip)
- local oldname = resolvers.find_file(name) or ""
+ local oldname = resolvers.findfile(name) or ""
oldname = file.replacesuffix(oldname,"lua")
if oldname == "" then
- logs.simple("missing '%s'",name)
+ report("missing '%s'",name)
else
local newname = file.removesuffix(oldname) .. "-merged.lua"
local data = io.loaddata(oldname) or ""
if data == "" then
- logs.simple("missing '%s'",newname)
+ report("missing '%s'",newname)
else
- logs.simple("loading '%s'",oldname)
+ report("loading '%s'",oldname)
local collected = { }
collected[#collected+1] = format("-- merged file : %s\n",newname)
collected[#collected+1] = format("-- parent file : %s\n",oldname)
@@ -31,11 +43,11 @@ function scripts.package.merge_luatex_files(name,strip)
-- loadmodule can have extra arguments
for lib in gmatch(data,"loadmodule *%([\'\"](.-)[\'\"]") do
if file.basename(lib) ~= file.basename(newname) then
- local fullname = resolvers.find_file(lib) or ""
+ local fullname = resolvers.findfile(lib) or ""
if fullname == "" then
- logs.simple("missing '%s'",lib)
+ report("missing '%s'",lib)
else
- logs.simple("fetching '%s'",fullname)
+ report("fetching '%s'",fullname)
local data = io.loaddata(fullname)
if strip then
data = gsub(data,"%-%-%[%[ldx%-%-.-%-%-%ldx%]%]%-%-[\n\r]*","")
@@ -49,20 +61,14 @@ function scripts.package.merge_luatex_files(name,strip)
end
end
end
- logs.simple("saving '%s'",newname)
+ report("saving '%s'",newname)
io.savedata(newname,table.concat(collected))
end
end
end
-logs.extendbanner("Distribution Related Goodies 0.10",true)
-
-messages.help = [[
---merge merge 'loadmodule' into merge file
-]]
-
if environment.argument("merge") then
scripts.package.merge_luatex_files(environment.files[1] or "")
else
- logs.help(messages.help)
+ application.help()
end
diff --git a/Master/texmf-dist/scripts/context/lua/mtx-patterns.lua b/Master/texmf-dist/scripts/context/lua/mtx-patterns.lua
index 293016991ff..bbf47513354 100644
--- a/Master/texmf-dist/scripts/context/lua/mtx-patterns.lua
+++ b/Master/texmf-dist/scripts/context/lua/mtx-patterns.lua
@@ -6,312 +6,453 @@ if not modules then modules = { } end modules ['mtx-patterns'] = {
license = "see context related readme files"
}
-local format = string.format
+local format, find, concat, gsub, match, gmatch = string.format, string.find, table.concat, string.gsub, string.match, string.gmatch
+local byte, char = utf.byte, utf.char
+local addsuffix = file.addsuffix
+local lpegmatch, validutf8 = lpeg.match, lpeg.patterns.validutf8
+
+local helpinfo = [[
+--convert generate context language files (mnemonic driven, if not given then all)
+--check check pattern file (or those used by context when no file given)
+--path source path where hyph-foo.tex files are stored
+--destination destination path
+
+examples of usage:
+
+mtxrun --script pattern --check hyph-*.tex
+mtxrun --script pattern --check --path=c:/data/develop/svn-hyphen/trunk/hyph-utf8/tex/generic/hyph-utf8/patterns
+mtxrun --script pattern --convert --path=c:/data/develop/svn-hyphen/trunk/hyph-utf8/tex/generic/hyph-utf8/patterns/tex --destination=e:/tmp/patterns
+mtxrun --script pattern --convert --path=c:/data/develop/svn-hyphen/trunk/hyph-utf8/tex/generic/hyph-utf8/patterns/txt --destination=e:/tmp/patterns
+]]
+
+local application = logs.application {
+ name = "mtx-patterns",
+ banner = "ConTeXt Pattern File Management 0.20",
+ helpinfo = helpinfo,
+}
+
+local report = application.report
scripts = scripts or { }
scripts.patterns = scripts.patterns or { }
-scripts.patterns.list = {
- { "??", "hyph-ar.tex", "arabic" },
- { "bg", "hyph-bg.tex", "bulgarian" },
- { "ca", "hyph-ca.tex", "catalan" },
- { "??", "hyph-cop.tex", "coptic" },
- { "cs", "hyph-cs.tex", "czech" },
- { "cy", "hyph-cy.tex", "welsh" },
- { "da", "hyph-da.tex", "danish" },
- { "deo", "hyph-de-1901.tex", "german, old spelling" },
- { "de", "hyph-de-1996.tex", "german, new spelling" },
---~ { "??", "hyph-el-monoton.tex", "" },
---~ { "??", "hyph-el-polyton.tex", "" },
- { "agr", "hyph-grc", "ancient greek" },
---~ { "???", "hyph-x-ibycus", "ancient greek in ibycus encoding" },
---~ { "gr", "", "" },
- { "eo", "hyph-eo.tex", "esperanto" },
- { "gb", "hyph-en-gb.tex", "british english" },
- { "us", "hyph-en-us.tex", "american english" },
- { "es", "hyph-es.tex", "spanish" },
- { "et", "hyph-et.tex", "estonian" },
- { "eu", "hyph-eu.tex", "basque" }, -- ba is Bashkir!
- { "fa", "hyph-fa.tex", "farsi" },
- { "fi", "hyph-fi.tex", "finnish" },
- { "fr", "hyph-fr.tex", "french" },
--- { "??", "hyph-ga.tex", "" },
--- { "??", "hyph-gl.tex", "" },
--- { "??", "hyph-grc.tex", "" },
- { "hr", "hyph-hr.tex", "croatian" },
- { "??", "hyph-hsb.tex", "upper sorbian" },
- { "hu", "hyph-hu.tex", "hungarian" },
- { "??", "hyph-ia.tex", "interlingua" },
- { "??", "hyph-id.tex", "indonesian" },
- { "is", "hyph-is.tex", "icelandic" },
- { "it", "hyph-it.tex", "italian" },
- { "la", "hyph-la.tex", "latin" },
- { "lt", "hyph-lt.tex", "lithuanian" },
- { "mn", "hyph-mn-cyrl.tex", "mongolian, cyrillic script" },
- { "nb", "hyph-nb.tex", "norwegian bokmål" },
- { "nl", "hyph-nl.tex", "dutch" },
- { "nn", "hyph-nn.tex", "norwegian nynorsk" },
- { "pl", "hyph-pl.tex", "polish" },
- { "pt", "hyph-pt.tex", "portuguese" },
- { "ro", "hyph-ro.tex", "romanian" },
- { "ru", "hyph-ru.tex", "russian" },
- { "sk", "hyph-sk.tex", "slovak" },
- { "sl", "hyph-sl.tex", "slovenian" },
- { "sr", "hyph-sr-cyrl.tex", "serbian" },
- { "sv", "hyph-sv.tex", "swedish" },
- { "tr", "hyph-tr.tex", "turkish" },
- { "tk", "hyph-tk.tex", "turkman" },
- { "uk", "hyph-uk.tex", "ukrainian" },
- { "zh", "hyph-zh-latn.tex", "zh-latn, chinese Pinyin" },
+local permitted_characters = table.tohash {
+ 0x0009, -- tab
+ 0x0027, -- apostrofe
+ 0x02BC, -- modifier apostrofe (used in greek)
+ 0x002D, -- hyphen
+ 0x200C, -- zwnj
+ 0x2019, -- quote right
+ 0x1FBD, -- greek, but no letter: symbol modifier
+ 0x1FBF, -- greek, but no letter: symbol modifier
}
+local ignored_ancient_greek = table.tohash {
+ 0x1FD3, -- greekiotadialytikatonos (also 0x0390)
+ 0x1FE3, -- greekupsilondialytikatonos (also 0x03B0)
+ 0x1FBD, -- greek, but no letter: symbol modifier
+ 0x1FBF, -- greek, but no letter: symbol modifier
+ 0x03F2, -- greeksigmalunate
+ 0x02BC, -- modifier apostrofe)
+}
--- stripped down from lpeg example:
+local ignored_french = table.tohash {
+ 0x02BC, -- modifier apostrofe
+}
-local utf = unicode.utf8
+local replaced_whatever = {
+ [char(0x2019)] = char(0x0027)
+}
-local cont = lpeg.R("\128\191") -- continuation byte
+scripts.patterns.list = {
+ { "af", "hyph-af", "afrikaans" },
+ -- { "ar", "hyph-ar", "arabic" },
+ -- { "as", "hyph-as", "assamese" },
+ { "bg", "hyph-bg", "bulgarian" },
+ -- { "bn", "hyph-bn", "bengali" },
+ { "ca", "hyph-ca", "catalan" },
+ -- { "??", "hyph-cop", "coptic" },
+ { "cs", "hyph-cs", "czech" },
+ { "cy", "hyph-cy", "welsh" },
+ { "da", "hyph-da", "danish" },
+ { "deo", "hyph-de-1901", "german, old spelling" },
+ { "de", "hyph-de-1996", "german, new spelling" },
+ -- { "??", "hyph-de-ch-1901", "swiss german" },
+ -- { "??", "hyph-el-monoton", "greek" },
+ -- { "gr", "hyph-el-polyton", "greek" },
+ { "agr", "hyph-grc", "ancient greek", ignored_ancient_greek },
+ { "gb", "hyph-en-gb", "british english" },
+ { "us", "hyph-en-us", "american english" },
+ -- { "eo", "hyph-eo", "esperanto" },
+ { "es", "hyph-es", "spanish" },
+ { "et", "hyph-et", "estonian" },
+ { "eu", "hyph-eu", "basque" },
+ -- { "fa", "hyph-fa", "farsi" },
+ { "fi", "hyph-fi", "finnish" },
+ { "fr", "hyph-fr", "french", ignored_french },
+ -- { "??", "hyph-ga", "irish" },
+ -- { "??", "hyph-gl", "galician" },
+ -- { "gu", "hyph-gu", "gujarati" },
+ -- { "hi", "hyph-hi", "hindi" },
+ { "hr", "hyph-hr", "croatian" },
+ -- { "??", "hyph-hsb", "upper sorbian" },
+ { "hu", "hyph-hu", "hungarian" },
+ -- { "hy", "hyph-hy", "armenian" },
+ -- { "??", "hyph-ia", "interlingua" },
+ -- { "??", "hyph-id", "indonesian" },
+ { "is", "hyph-is", "icelandic" },
+ { "it", "hyph-it", "italian" },
+ -- { "??", "hyph-kmr", "kurmanji" },
+ -- { "kn", "hyph-kn", "kannada" },
+ { "la", "hyph-la", "latin" },
+ -- { "lo", "hyph-lo", "lao" },
+ { "lt", "hyph-lt", "lithuanian" },
+ { "lv", "hyph-lv", "latvian" },
+ -- { "ml", "hyph-ml", "..." },
+ { "mn", "hyph-mn-cyrl", "mongolian, cyrillic script" },
+ -- { "mr", "hyph-mr", "..." },
+ { "nb", "hyph-nb", "norwegian bokmål" },
+ { "nl", "hyph-nl", "dutch" },
+ { "nn", "hyph-nn", "norwegian nynorsk" },
+ -- { "or", "hyph-or", "oriya" },
+ -- { "pa", "hyph-pa", "panjabi" },
+ -- { "", "hyph-", "" },
+ { "pl", "hyph-pl", "polish" },
+ { "pt", "hyph-pt", "portuguese" },
+ { "ro", "hyph-ro", "romanian" },
+ { "ru", "hyph-ru", "russian" },
+ -- { "sa", "hyph-sa", "sanskrit" },
+ { "sk", "hyph-sk", "slovak" },
+ { "sl", "hyph-sl", "slovenian" },
+ { "sr", "hyph-sr-cyrl", "serbian" },
+ -- { "sr", "hyph-sr-latn", "serbian" },
+ { "sv", "hyph-sv", "swedish" },
+ -- { "ta", "hyph-ta", "tamil" },
+ -- { "te", "hyph-te", "telugu" },
+ { "tk", "hyph-tk", "turkmen" },
+ { "tr", "hyph-tr", "turkish" },
+ { "uk", "hyph-uk", "ukrainian" },
+ { "zh", "hyph-zh-latn", "zh-latn, chinese pinyin" },
+}
-local utf8 = lpeg.R("\0\127")
- + lpeg.R("\194\223") * cont
- + lpeg.R("\224\239") * cont * cont
- + lpeg.R("\240\244") * cont * cont * cont
+-- stripped down from lpeg example:
-local validutf = (utf8^0/function() return true end) * (lpeg.P(-1)/function() return false end)
+local utf = unicode.utf8
function utf.check(str)
- return lpeg.match(validutf,str)
+ return lpeg.match(lpeg.patterns.validutf8,str)
end
-local permitted_commands = table.tohash {
- "message",
- "endinput"
-}
+-- *.tex
+-- *.hyp.txt *.pat.txt *.lic.txt *.chr.txt
-local permitted_characters = table.tohash {
- 0x0009, -- tab
- 0x0027, -- apostrofe
- 0x002D, -- hyphen
- 0x200C, --
-}
-
-function scripts.patterns.load(path,name,mnemonic,fullcheck)
+function scripts.patterns.load(path,name,mnemonic,ignored)
local fullname = file.join(path,name)
- local data = io.loaddata(fullname) or ""
- local byte, char = utf.byte, utf.char
- if data ~= "" then
- data = data:gsub("([\n\r])\\input ([^ \n\r]+)", function(previous,subname)
- local subname = file.addsuffix(subname,"tex")
- local subfull = file.join(file.dirname(fullname),subname)
- local subdata = io.loaddata(subfull) or ""
- if subdata == "" then
- if mnemonic then
- logs.simple("no subfile %s for language %s",subname,mnemonic)
- else
- logs.simple("no subfile %s",name)
+ local texfile = addsuffix(fullname,"tex")
+ local hypfile = addsuffix(fullname,"hyp.txt")
+ local patfile = addsuffix(fullname,"pat.txt")
+ local licfile = addsuffix(fullname,"lic.txt")
+ -- local chrfile = addsuffix(fullname,"chr.txt")
+ local okay = true
+ local hyphenations, patterns, comment, stripset = "", "", "", ""
+ local splitpatternsnew, splithyphenationsnew = { }, { }
+ local splitpatternsold, splithyphenationsold = { }, { }
+ local usedpatterncharacters, usedhyphenationcharacters = { }, { }
+ if lfs.isfile(patfile) then
+ report("using txt files %s.[hyp|pat|lic].txt",name)
+ comment, patterns, hyphenations = io.loaddata(licfile) or "", io.loaddata(patfile) or "", io.loaddata(hypfile) or ""
+ hypfile, patfile, licfile = hypfile, patfile, licfile
+ elseif lfs.isfile(texfile) then
+ report("using tex file %s.txt",name)
+ local data = io.loaddata(texfile) or ""
+ if data ~= "" then
+ data = gsub(data,"([\n\r])\\input ([^ \n\r]+)", function(previous,subname)
+ local subname = addsuffix(subname,"tex")
+ local subfull = file.join(file.dirname(texfile),subname)
+ local subdata = io.loaddata(subfull) or ""
+ if subdata == "" then
+ report("no subfile %s",subname)
end
- end
- return previous .. subdata
- end)
- local comment = data:match("^(.-)[\n\r]\\patterns") or ""
- local n, okay = 0, true
- local cd = characters.data
- for line in data:gmatch("[^ \n\r]+") do
- local ok = utf.check(line)
- n = n + 1
- if not ok then
- okay = false
- line = line:gsub("%%","%%%%")
- if fullcheck then
- if mnemonic then
- logs.simple("invalid utf in language %s, file %s, line %s: %s",mnemonic,name,n,line)
- else
- logs.simple("invalid utf in file %s, line %s: %s",name,n,line)
- end
- else
- if mnemonic then
- logs.simple("file %s for %s contains invalid utf",name,mnemonic)
- else
- logs.simple("file %s contains invalid utf",name)
+ return previous .. subdata
+ end)
+ data = gsub(data,"%%.-[\n\r]","")
+ data = gsub(data," *[\n\r]+","\n")
+ patterns = match(data,"\\patterns[%s]*{[%s]*(.-)[%s]*}") or ""
+ hyphenations = match(data,"\\hyphenation[%s]*{[%s]*(.-)[%s]*}") or ""
+ comment = match(data,"^(.-)[\n\r]\\patterns") or ""
+ else
+ okay = false
+ end
+ else
+ okay = false
+ end
+ if okay then
+ -- split into lines
+ local how = lpeg.patterns.whitespace^1
+ splitpatternsnew = lpeg.split(how,patterns)
+ splithyphenationsnew = lpeg.split(how,hyphenations)
+ end
+ if okay then
+ -- remove comments
+ local function check(data,splitdata,name)
+ if find(data,"%%") then
+ for i=1,#splitdata do
+ local line = splitdata[i]
+ if find(line,"%%") then
+ splitdata[i] = gsub(line,"%%.*$","")
+ report("removing comment: %s",line)
end
- break
end
end
end
- local c, h = { }, { }
- for line in data:gmatch("[^\n\r]+") do
- local txt, cmt = line:match("^(.-)%%(.*)$")
- if not txt then
- txt, cmt = line, ""
- end
- for s in txt:gmatch("\\([a-zA-Z]+)") do
- h[s] = (h[s] or 0) + 1
- end
- for s in cmt:gmatch("\\([a-zA-Z]+)") do
- c[s] = (c[s] or 0) + 1
- end
- end
- h.patterns = nil
- h.hyphenation = nil
- for k, v in next, h do
- if not permitted_commands[k] then okay = false end
- if mnemonic then
- logs.simple("command \\%s found in language %s, file %s, n=%s",k,mnemonic,name,v)
- else
- logs.simple("command \\%s found in file %s, n=%s",k,name,v)
+ check(patterns,splitpatternsnew,patfile)
+ check(hyphenations,splithyphenationsnew,hypfile)
+ end
+ if okay then
+ -- remove lines with commands
+ local function check(data,splitdata,name)
+ if find(data,"\\") then
+ for i=1,#splitdata do
+ local line = splitdata[i]
+ if find(line,"\\") then
+ splitdata[i] = ""
+ report("removing line with command: %s",line)
+ end
+ end
end
end
- if not environment.argument("fast") then
- for k, v in next, c do
- if mnemonic then
- logs.simple("command \\%s found in comment of language %s, file %s, n=%s",k,mnemonic,name,v)
- else
- logs.simple("command \\%s found in comment of file %s, n=%s",k,name,v)
+ check(patterns,splitpatternsnew,patfile)
+ check(hyphenations,splithyphenationsnew,hypfile)
+ end
+ if okay then
+ -- check for valid utf
+ local function check(data,splitdata,name)
+ for i=1,#splitdata do
+ local line = splitdata[i]
+ local ok = lpegmatch(validutf8,line)
+ if not ok then
+ splitdata[i] = ""
+ report("removing line with invalid utf: %s",line)
end
end
+ -- check for commands being used in comments
end
- data = data:gsub("%%.-[\n\r]","")
- data = data:gsub(" *[\n\r]+","\n")
- local patterns = data:match("\\patterns[%s]*{[%s]*(.-)[%s]*}") or ""
- local hyphenations = data:match("\\hyphenation[%s]*{[%s]*(.-)[%s]*}") or ""
- patterns = patterns:gsub(" +","\n")
- hyphenations = hyphenations:gsub(" +","\n")
- local p, h = { }, { }
- local pats, hyps = { } , { }
- local pused, hused = { } , { }
- local period = byte(".")
- for line in patterns:gmatch("[^ \n\r]+") do
- local ok = true
- for b in line:utfvalues() do
- if b == period then
- -- ok
- else
- local ct = cd[b].category
- if ct == "lu" or ct == "ll" then
- pused[char(b)] = true
- elseif ct == "nd" then
- -- ok
+ check(patterns,splitpatternsnew,patfile)
+ check(hyphenations,splithyphenationsnew,hypfile)
+ end
+ if okay then
+ -- remove funny lines
+ local cd = characters.data
+ local stripped = { }
+ local function check(splitdata,special,name)
+ local used = { }
+ for i=1,#splitdata do
+ local line = splitdata[i]
+ for b in line:utfvalues() do -- could be an lpeg
+ if b == special then
+ -- not registered
+ elseif permitted_characters[b] then
+ used[char(b)] = true
else
- p[b] = (p[b] or 0) + 1
- ok = false
+ local cdb = cd[b]
+ if not cdb then
+ report("no entry in chardata for character %s (0x%04X)",char(b),b)
+ else
+ local ct = cd[b].category
+ if ct == "lu" or ct == "ll" then
+ used[char(b)] = true
+ elseif ct == "nd" then
+ -- number
+ else
+ report("removing line with suspected utf character %s (0x%04X), category %s: %s",char(b),b,ct,line)
+ splitdata[i] = ""
+ break
+ end
+ end
end
end
end
- if ok then
- pats[#pats+1] = line
- end
+ return used
end
- local hyphen = byte("-")
- for line in hyphenations:gmatch("[^ \n\r]+") do
- local ok = true
- for b in line:utfvalues() do
- if b == hyphen then
- -- ok
- else
- local ct = cd[b].category
- if ct == "lu" or ct == "ll" then
- hused[char(b)] = true
+ usedpatterncharacters = check(splitpatternsnew,byte("."))
+ usedhyphenationcharacters = check(splithyphenationsnew,byte("-"))
+ for k, v in next, stripped do
+ report("entries that contain character %s (0x%04X) have been omitted",char(k),k)
+ end
+ end
+ if okay then
+ local function stripped(what,ignored)
+ -- ignored (per language)
+ local p = nil
+ if ignored then
+ for k, v in next, ignored do
+ if p then
+ p = p + lpeg.P(char(k))
else
- h[b] = (h[b] or 0) + 1
- ok = false
+ p = lpeg.P(char(k))
end
end
+ p = lpeg.P{ p + 1 * lpeg.V(1) } -- anywhere
end
- if ok then
- hyps[#hyps+1] = line
- end
- end
- local stripped = { }
- for k, v in next, p do
- if mnemonic then
- logs.simple("invalid character %s (0x%04X) in patterns of language %s, file %s, n=%s",char(k),k,mnemonic,name,v)
- else
- logs.simple("invalid character %s (0x%04X) in patterns of file %s, n=%s",char(k),k,name,v)
+ -- replaced (all languages)
+ local r = nil
+ for k, v in next, replaced_whatever do
+ if r then
+ r = r + lpeg.P(k)/v
+ else
+ r = lpeg.P(k)/v
+ end
end
- if not permitted_characters[k] then
- okay = false
- else
- stripped[k] = true
+ r = lpeg.Cs((r + 1)^0)
+ local result = { }
+ for i=1,#what do
+ local line = what[i]
+ if p and lpegmatch(p,line) then
+ report("discarding conflicting pattern: %s",line)
+ else -- we can speed this up by testing for replacements in the string
+ local l = lpegmatch(r,line)
+ if l ~= line then
+ report("sanitizing pattern: %s -> %s (for old patterns)",line,l)
+ end
+ result[#result+1] = l
+ end
end
+ return result
end
- for k, v in next, h do
- if mnemonic then
- logs.simple("invalid character %s (0x%04X) in exceptions of language %s, file %s, n=%s",char(k),k,mnemonic,name,v)
- else
- logs.simple("invalid character %s (0x%04X) in exceptions of file %s, n=%s",char(k),k,name,v)
- end
- if not permitted_characters[k] then
- okay = false
- else
- stripped[k] = true
+
+ splitpatternsold = stripped(splitpatternsnew,ignored)
+ splithyphenationsold = stripped(splithyphenationsnew,ignored)
+
+ end
+ if okay then
+ -- discarding duplicates
+ local function check(data,splitdata,name)
+ local used, collected = { }, { }
+ for i=1,#splitdata do
+ local line = splitdata[i]
+ if line == "" then
+ -- discard
+ elseif used[line] then
+ -- discard
+ repo("discarding duplicate pattern: %s",line)
+ else
+ used[line] = true
+ collected[#collected+1] = line
+ end
end
+ return collected
end
- local stripset = ""
- for k, v in next, stripped do
- logs.simple("entries that contain character %s will be omitted",char(k))
- stripset = stripset .. "%" .. char(k)
- end
- return okay, pats, hyps, comment, stripset, pused, hused
- else
- if mnemonic then
- logs.simple("no file %s for language %s",fullname,mnemonic)
- else
- logs.simple("no file %s",fullname)
- end
- return false, { }, { }, "", "", { }, { }
+ splitpatternsnew = check(patterns,splitpatternsnew,patfile)
+ splithyphenationsnew = check(hyphenations,splithyphenationsnew,hypfile)
+ splitpatternsold = check(patterns,splitpatternsold,patfile)
+ splithyphenationsold = check(hyphenations,splithyphenationsold,hypfile)
+ end
+ if not okay then
+ report("no valid file %s.*",name)
end
+ return okay, splitpatternsnew, splithyphenationsnew, splitpatternsold, splithyphenationsold, comment, stripset, usedpatterncharacters, usedhyphenationcharacters
end
-function scripts.patterns.save(destination,mnemonic,patterns,hyphenations,comment,stripped,pused,hused)
- local nofpatterns = #patterns
- local nofhyphenations = #hyphenations
- local pu = table.concat(table.sortedkeys(pused), " ")
- local hu = table.concat(table.sortedkeys(hused), " ")
- logs.simple("language %s has %s patterns and %s exceptions",mnemonic,nofpatterns,nofhyphenations)
+function scripts.patterns.save(destination,mnemonic,name,patternsnew,hyphenationsnew,patternsold,hyphenationsold,comment,stripped,pused,hused,ignored)
+ local nofpatternsnew, nofhyphenationsnew = #patternsnew, #hyphenationsnew
+ local nofpatternsold, nofhyphenationsold = #patternsold, #hyphenationsold
+ report("language %s has %s old and %s new patterns and %s old and %s new exceptions",mnemonic,nofpatternsold,nofpatternsnew,nofhyphenationsold,nofhyphenationsnew)
if mnemonic ~= "??" then
+ local pu = concat(table.sortedkeys(pused), " ")
+ local hu = concat(table.sortedkeys(hused), " ")
+
local rmefile = file.join(destination,"lang-"..mnemonic..".rme")
local patfile = file.join(destination,"lang-"..mnemonic..".pat")
local hypfile = file.join(destination,"lang-"..mnemonic..".hyp")
+ local luafile = file.join(destination,"lang-"..mnemonic..".lua") -- suffix might change to llg
+
local topline = "% generated by mtxrun --script pattern --convert"
local banner = "% for comment and copyright, see " .. rmefile
- logs.simple("saving language data for %s",mnemonic)
+ report("saving language data for %s",mnemonic)
if not comment or comment == "" then comment = "% no comment" end
if not type(destination) == "string" then destination = "." end
+
+ local lines = string.splitlines(comment)
+ for i=1,#lines do
+ if not find(lines[i],"^%%") then
+ lines[i] = "% " .. lines[i]
+ end
+ end
+
+ local metadata = {
+ -- texcomment = comment,
+ texcomment = concat(lines,"\n"),
+ source = name,
+ mnemonic = mnemonic,
+ }
+
+ local patterndata, hyphenationdata
+ if nofpatternsnew > 0 then
+ patterndata = {
+ n = nofpatternsnew,
+ data = concat(patternsnew," ") or nil,
+ characters = concat(table.sortedkeys(pused),""),
+ minhyphenmin = 1, -- determined by pattern author
+ minhyphenmax = 1, -- determined by pattern author
+ }
+ else
+ patterndata = {
+ n = 0,
+ }
+ end
+ if nofhyphenationsnew > 0 then
+ hyphenationdata = {
+ n = nofhyphenationsnew,
+ data = concat(hyphenationsnew," "),
+ characters = concat(table.sortedkeys(hused),""),
+ }
+ else
+ hyphenationdata = {
+ n = 0,
+ }
+ end
+ local data = {
+ -- a prelude to language goodies, like we have font goodies and in
+ -- mkiv we can use this file directly
+ version = "1.001",
+ comment = topline,
+ metadata = metadata,
+ patterns = patterndata,
+ exceptions = hyphenationdata,
+ }
+
os.remove(rmefile)
os.remove(patfile)
os.remove(hypfile)
+ os.remove(luafile)
+
io.savedata(rmefile,format("%s\n\n%s",topline,comment))
- io.savedata(patfile,format("%s\n\n%s\n\n%% used: %s\n\n\\patterns{\n%s}",topline,banner,pu,table.concat(patterns,"\n")))
- io.savedata(hypfile,format("%s\n\n%s\n\n%% used: %s\n\n\\hyphenation{\n%s}",topline,banner,hu,table.concat(hyphenations,"\n")))
+ io.savedata(patfile,format("%s\n\n%s\n\n%% used: %s\n\n\\patterns{\n%s}",topline,banner,pu,concat(patternsold,"\n")))
+ io.savedata(hypfile,format("%s\n\n%s\n\n%% used: %s\n\n\\hyphenation{\n%s}",topline,banner,hu,concat(hyphenationsold,"\n")))
+ io.savedata(luafile,table.serialize(data,true))
end
end
function scripts.patterns.prepare()
- dofile(resolvers.find_file("char-def.lua"))
+ dofile(resolvers.findfile("char-def.lua"))
end
function scripts.patterns.check()
local path = environment.argument("path") or "."
- local found = false
local files = environment.files
+ local only = false
if #files > 0 then
- for i=1,#files do
- local name = files[i]
- logs.simple("checking language file %s", name)
- local okay = scripts.patterns.load(path,name,nil,not environment.argument("fast"))
- if #environment.files > 1 then
- logs.simple("")
- end
- end
- else
- for k, v in next, scripts.patterns.list do
- local mnemonic, name = v[1], v[2]
- logs.simple("checking language %s, file %s", mnemonic, name)
- local okay = scripts.patterns.load(path,name,mnemonic,not environment.argument("fast"))
+ only = table.tohash(files)
+ end
+ for k, v in next, scripts.patterns.list do
+ local mnemonic, name, ignored = v[1], v[2], v[4]
+ if not only or only[mnemonic] then
+ report("checking language %s, file %s", mnemonic, name)
+ local okay = scripts.patterns.load(path,name,mnemonic,ignored)
if not okay then
- logs.simple("there are errors that need to be fixed")
+ report("there are errors that need to be fixed")
end
- logs.simple("")
+ report()
end
end
end
@@ -319,36 +460,34 @@ end
function scripts.patterns.convert()
local path = environment.argument("path") or "."
if path == "" then
- logs.simple("provide sourcepath using --path ")
+ report("provide sourcepath using --path ")
else
local destination = environment.argument("destination") or "."
if path == destination then
- logs.simple("source path and destination path should differ (use --path and/or --destination)")
+ resport("source path and destination path should differ (use --path and/or --destination)")
else
+ local files = environment.files
+ local only = false
+ if #files > 0 then
+ only = table.tohash(files)
+ end
for k, v in next, scripts.patterns.list do
- local mnemonic, name = v[1], v[2]
- logs.simple("converting language %s, file %s", mnemonic, name)
- local okay, patterns, hyphenations, comment, stripped, pused, hused = scripts.patterns.load(path,name,false)
- if okay then
- scripts.patterns.save(destination,mnemonic,patterns,hyphenations,comment,stripped,pused,hused)
- else
- logs.simple("convertion aborted due to error(s)")
+ local mnemonic, name, ignored = v[1], v[2], v[4]
+ if not only or only[mnemonic] then
+ report("converting language %s, file %s", mnemonic, name)
+ local okay, patternsnew, hyphenationsnew, patternsold, hyphenationsold, comment, stripped, pused, hused = scripts.patterns.load(path,name,mnemonic,ignored)
+ if okay then
+ scripts.patterns.save(destination,mnemonic,name,patternsnew,hyphenationsnew,patternsold,hyphenationsold,comment,stripped,pused,hused,ignored)
+ else
+ report("convertion aborted due to error(s)")
+ end
+ report()
end
- logs.simple("")
end
end
end
end
-logs.extendbanner("ConTeXt Pattern File Management 0.20",true)
-
-messages.help = [[
---convert generate context language files (mnemonic driven, if not given then all)
---check check pattern file (or those used by context when no file given)
-
---fast only report filenames, no lines
-]]
-
if environment.argument("check") then
scripts.patterns.prepare()
scripts.patterns.check()
@@ -356,11 +495,10 @@ elseif environment.argument("convert") then
scripts.patterns.prepare()
scripts.patterns.convert()
else
- logs.help(messages.help)
+ application.help()
end
-- mtxrun --script pattern --check hyph-*.tex
-- mtxrun --script pattern --check --path=c:/data/develop/svn-hyphen/trunk/hyph-utf8/tex/generic/hyph-utf8/patterns
--- mtxrun --script pattern --check --fast --path=c:/data/develop/svn-hyphen/trunk/hyph-utf8/tex/generic/hyph-utf8/patterns
--- mtxrun --script pattern --convert --path=c:/data/develop/svn-hyphen/trunk/hyph-utf8/tex/generic/hyph-utf8/patterns --destination=e:/tmp/patterns
--- mtxrun --script pattern --convert --path=c:/data/develop/svn-hyphen/branches/luatex/hyph-utf8/tex/generic/hyph-utf8/patterns/tex --destination=e:/tmp/patterns
+-- mtxrun --script pattern --convert --path=c:/data/develop/svn-hyphen/trunk/hyph-utf8/tex/generic/hyph-utf8/patterns/tex --destination=e:/tmp/patterns
+-- mtxrun --script pattern --convert --path=c:/data/develop/svn-hyphen/trunk/hyph-utf8/tex/generic/hyph-utf8/patterns/txt --destination=e:/tmp/patterns
diff --git a/Master/texmf-dist/scripts/context/lua/mtx-profile.lua b/Master/texmf-dist/scripts/context/lua/mtx-profile.lua
index 11d48d0399b..74e1d153845 100644
--- a/Master/texmf-dist/scripts/context/lua/mtx-profile.lua
+++ b/Master/texmf-dist/scripts/context/lua/mtx-profile.lua
@@ -11,6 +11,19 @@ if not modules then modules = { } end modules ['mtx-profile'] = {
local match, format, find = string.match, string.format, string.find
+local helpinfo = [[
+--analyze analyze lua calls
+--trace analyze tex calls
+]]
+
+local application = logs.application {
+ name = "mtx-cache",
+ banner = "ConTeXt MkIV LuaTeX Profiler 1.00",
+ helpinfo = helpinfo,
+}
+
+local report = application.report
+
scripts = scripts or { }
scripts.profiler = scripts.profiler or { }
@@ -23,7 +36,7 @@ local calltemplate = "%9i %s"
local totaltemplate = "%i internal calls, %i function calls taking %3.4f seconds"
local thresholdtemplate = "thresholds: %i internal calls, %i function calls, %i seconds"
-function scripts.profiler.analyse(filename)
+function scripts.profiler.analyze(filename)
local f = io.open(filename)
if f then
local times, counts, calls = { }, { }, { }
@@ -54,7 +67,7 @@ function scripts.profiler.analyse(filename)
f:close()
print("")
local loaded = { }
- local sortedtable.sortedkeys(times)
+ sortedtable.sortedkeys(times)
for i=1,#sorted do
local filename = sorted[i]
local functions = times[filename]
@@ -101,7 +114,7 @@ function scripts.profiler.analyse(filename)
end
end
-function scripts.profiler.x_analyse(filename)
+function scripts.profiler.x_analyze(filename)
local f = io.open(filename)
local calls = { }
local lines = 0
@@ -151,20 +164,13 @@ local criterium = 100
end
end
---~ scripts.profiler.analyse("t:/manuals/mk/mk-fonts-profile.lua")
---~ scripts.profiler.analyse("t:/manuals/mk/mk-introduction-profile.lua")
-
-logs.extendbanner("ConTeXt MkIV LuaTeX Profiler 1.00",true)
-
-messages.help = [[
---analyse analyse lua calls
---trace analyse tex calls
-]]
+--~ scripts.profiler.analyze("t:/manuals/mk/mk-fonts-profile.lua")
+--~ scripts.profiler.analyze("t:/manuals/mk/mk-introduction-profile.lua")
-if environment.argument("analyse") then
- scripts.profiler.analyse(environment.files[1] or "luatex-profile.log")
+if environment.argument("analyze") then
+ scripts.profiler.analyze(environment.files[1] or "luatex-profile.log")
elseif environment.argument("trace") then
- scripts.profiler.analyse(environment.files[1] or "temp.log")
+ scripts.profiler.analyze(environment.files[1] or "temp.log")
else
- logs.help(messages.help)
+ application.help()
end
diff --git a/Master/texmf-dist/scripts/context/lua/mtx-scite.lua b/Master/texmf-dist/scripts/context/lua/mtx-scite.lua
index d5f0a5344e5..0ac09b01702 100644
--- a/Master/texmf-dist/scripts/context/lua/mtx-scite.lua
+++ b/Master/texmf-dist/scripts/context/lua/mtx-scite.lua
@@ -9,6 +9,19 @@ if not modules then modules = { } end modules ['mtx-scite'] = {
-- todo: append to global properties else order of loading problem
-- linux problem ... files are under root protection so we need --install
+local helpinfo = [[
+--start [--verbose] start scite
+--test report what will happen
+]]
+
+local application = logs.application {
+ name = "mtx-scite",
+ banner = "Scite Startup Script 1.00",
+ helpinfo = helpinfo,
+}
+
+local report = application.report
+
scripts = scripts or { }
scripts.scite = scripts.scite or { }
@@ -30,11 +43,11 @@ function scripts.scite.start(indeed)
local binpaths = file.split_path(os.getenv("PATH")) or file.split_path(os.getenv("path"))
for i=1,#scitesignals do
local scitesignal = scitesignals[i]
- local scitepath = resolvers.find_file(scitesignal,"other text files") or ""
+ local scitepath = resolvers.findfile(scitesignal,"other text files") or ""
if scitepath ~= "" then
scitepath = file.dirname(scitepath) -- data
if scitepath == "" then
- scitepath = resolvers.clean_path(lfs.currentdir())
+ scitepath = resolvers.cleanpath(lfs.currentdir())
else
usedsignal, datapath = scitesignal, scitepath
break
@@ -42,11 +55,11 @@ function scripts.scite.start(indeed)
end
end
if not datapath or datapath == "" then
- logs.simple("invalid datapath, maybe you need to regenerate the file database")
+ report("invalid datapath, maybe you need to regenerate the file database")
return false
end
if not binpaths or #binpaths == 0 then
- logs.simple("invalid binpath")
+ report("invalid binpath")
return false
end
for i=1,#binpaths do
@@ -57,15 +70,15 @@ function scripts.scite.start(indeed)
end
end
if not fullname then
- logs.simple("unable to locate %s",workname)
+ report("unable to locate %s",workname)
return false
end
local properties = dir.glob(file.join(datapath,"*.properties"))
local luafiles = dir.glob(file.join(datapath,"*.lua"))
- local extrafont = resolvers.find_file(screenfont,"truetype font") or ""
+ local extrafont = resolvers.findfile(screenfont,"truetype font") or ""
local pragmafound = dir.glob(file.join(datapath,"pragma.properties"))
if userpath == "" then
- logs.simple("unable to figure out userpath")
+ report("unable to figure out userpath")
return false
end
local verbose = environment.argument("verbose")
@@ -120,47 +133,40 @@ function scripts.scite.start(indeed)
end
end
if not indeed or verbose then
- logs.simple("used signal: %s", usedsignal)
- logs.simple("data path : %s", datapath)
- logs.simple("full name : %s", fullname)
- logs.simple("user path : %s", userpath)
- logs.simple("extra font : %s", extrafont)
+ report("used signal: %s", usedsignal)
+ report("data path : %s", datapath)
+ report("full name : %s", fullname)
+ report("user path : %s", userpath)
+ report("extra font : %s", extrafont)
end
if #logdata > 0 then
- logs.simple("")
+ report("")
for k=1,#logdata do
local v = logdata[k]
- logs.simple(v[1],v[2])
+ report(v[1],v[2])
end
end
if indeed then
if #tobecopied > 0 then
- logs.simple("warning : copying updated files")
+ report("warning : copying updated files")
for i=1,#tobecopied do
local what = tobecopied[i]
- logs.simple("copying : '%s' => '%s'",what[1],what[2])
+ report("copying : '%s' => '%s'",what[1],what[2])
file.copy(what[1],what[2])
end
end
if propfiledone then
- logs.simple("saving : '%s'",userpropfile)
+ report("saving : '%s'",userpropfile)
io.savedata(fullpropfile,userpropdata)
end
os.launch(fullname)
end
end
-logs.extendbanner("Scite Startup Script 1.00",true)
-
-messages.help = [[
---start [--verbose] start scite
---test report what will happen
-]]
-
if environment.argument("start") then
scripts.scite.start(true)
elseif environment.argument("test") then
scripts.scite.start()
else
- logs.help(messages.help)
+ application.help()
end
diff --git a/Master/texmf-dist/scripts/context/lua/mtx-server-ctx-fonttest.lua b/Master/texmf-dist/scripts/context/lua/mtx-server-ctx-fonttest.lua
index b2a993bf836..b30cf017525 100644
--- a/Master/texmf-dist/scripts/context/lua/mtx-server-ctx-fonttest.lua
+++ b/Master/texmf-dist/scripts/context/lua/mtx-server-ctx-fonttest.lua
@@ -6,18 +6,20 @@ if not modules then modules = { } end modules ['mtx-server-ctx-fonttest'] = {
license = "see context related readme files"
}
---~ dofile(resolvers.find_file("l-aux.lua","tex"))
-dofile(resolvers.find_file("trac-lmx.lua","tex"))
-dofile(resolvers.find_file("font-ott.lua","tex"))
-dofile(resolvers.find_file("font-syn.lua","tex"))
-dofile(resolvers.find_file("font-mis.lua","tex"))
-dofile(resolvers.find_file("font-otp.lua","tex"))
+--~ dofile(resolvers.findfile("l-aux.lua","tex"))
+dofile(resolvers.findfile("trac-lmx.lua","tex"))
+dofile(resolvers.findfile("font-ott.lua","tex"))
+dofile(resolvers.findfile("font-syn.lua","tex"))
+dofile(resolvers.findfile("font-mis.lua","tex"))
+--~ dofile(resolvers.findfile("font-otp.lua","tex"))
local format, gsub, concat, match, find = string.format, string.gsub, table.concat, string.match, string.find
+local report = logs.reporter("ctx-fonttest")
+
local sample_line = "This is a sample line!"
local tempname = "mtx-server-ctx-fonttest-temp"
-local temppath = caches.setpath("temp","mtx-server-ctx-fonttest")
+local temppath = caches.setfirstwritablefile("temp","mtx-server-ctx-fonttest")
local basename = "mtx-server-ctx-fonttest-data.lua"
local basepath = temppath
@@ -33,7 +35,7 @@ local process_templates = { }
process_templates.default = [[
\starttext
\setcharactermirroring[1]
- \definefontfeature[sample][analyse=yes,%s]
+ \definefontfeature[sample][analyze=yes,%s]
\definedfont[name:%s*sample]
\startTEXpage[offset=3pt]
\detokenize{%s}
@@ -150,20 +152,20 @@ local cache = { }
local function showfeatures(f)
if f then
- logs.simple("processing font '%s'",f)
+ report("processing font '%s'",f)
local features = cache[f]
if features == nil then
- features = fonts.get_features(resolvers.find_file(f))
+ features = fonts.helpers.getfeatures(resolvers.findfile(f))
if not features then
- logs.simple("building cache for '%s'",f)
+ report("building cache for '%s'",f)
io.savedata(file.join(temppath,file.addsuffix(tempname,"tex")),format(process_templates.cache,f,f))
os.execute(format("mtxrun --path=%s --script context --once --batchmode %s",temppath,tempname))
- features = fonts.get_features(f)
+ features = fonts.helpers.getfeatures(f)
end
cache[f] = features or false
- logs.simple("caching info of '%s'",f)
+ report("caching info of '%s'",f)
else
- logs.simple("using cached info of '%s'",f)
+ report("using cached info of '%s'",f)
end
if features then
local scr, lan, fea, rev = { }, { }, { }, { }
@@ -294,7 +296,7 @@ local function get_specification(name)
end
local function edit_font(currentfont,detail,tempname)
- logs.simple("entering edit mode for '%s'",currentfont)
+ report("entering edit mode for '%s'",currentfont)
local specification = get_specification(currentfont)
if specification then
local htmldata = showfeatures(specification.filename)
@@ -303,7 +305,7 @@ local function edit_font(currentfont,detail,tempname)
local sorted = table.sortedkeys(htmldata.scripts)
for k=1,#sorted do
local v = sorted[k]
- local s = fonts.otf.tables.scripts[v] or v
+ local s = fonts.handlers.otf.tables.scripts[v] or v
if detail and v == detail.script then
scripts[#scripts+1] = format("<input title='%s' id='s-%s' type='radio' name='script' value='%s' onclick='check_script()' checked='checked'/>&nbsp;<span id='t-s-%s'>%s</span>",s,v,v,v,v)
else
@@ -313,7 +315,7 @@ local function edit_font(currentfont,detail,tempname)
local sorted = table.sortedkeys(htmldata.languages)
for k=1,#sorted do
local v = sorted[k]
- local l = fonts.otf.tables.languages[v] or v
+ local l = fonts.handlers.otf.tables.languages[v] or v
if detail and v == detail.language then
languages[#languages+1] = format("<input title='%s' id='l-%s' type='radio' name='language' value='%s' onclick='check_language()' checked='checked'/>&nbsp;<span id='t-l-%s'>%s</span>",l,v,v,v,v)
else
@@ -323,7 +325,7 @@ local function edit_font(currentfont,detail,tempname)
local sorted = table.sortedkeys(htmldata.features)
for k=1,#sorted do
local v = sorted[k]
- local f = fonts.otf.tables.features[v] or v
+ local f = fonts.handlers.otf.tables.features[v] or v
if detail and detail["f-"..v] then
features[#features+1] = format("<input title='%s' id='f-%s' type='checkbox' name='f-%s' onclick='check_feature()' checked='checked'/>&nbsp;<span id='t-f-%s'>%s</span>",f,v,v,v,v)
else
@@ -374,7 +376,7 @@ local function process_font(currentfont,detail) -- maybe just fontname
end
local sample = string.strip(detail.sampletext or "")
if sample == "" then sample = sample_line end
- logs.simple("sample text: %s",sample)
+ report("sample text: %s",sample)
io.savedata(file.join(temppath,file.addsuffix(tempname,"tex")),format(variant,concat(features,","),currentfont,sample))
os.execute(format("mtxrun --path=%s --script context --once --batchmode %s",temppath,tempname))
return edit_font(currentfont,detail,tempname)
@@ -406,7 +408,7 @@ end
local function show_font(currentfont,detail)
local specification = get_specification(currentfont)
- local features = fonts.get_features(specification.filename)
+ local features = fonts.helpers.getfeatures(specification.filename)
local result = { }
result[#result+1] = format("<h1>names</h1>",what)
result[#result+1] = "<table>"
@@ -444,7 +446,7 @@ local function show_font(currentfont,detail)
else
done = true
end
- local title = fonts.otf.tables.features[f] or ""
+ local title = fonts.handlers.otf.tables.features[f] or ""
result[#result+1] = format("<tr><td width='50%%'>%s&nbsp;&nbsp;</td><td><tt>%s&nbsp;&nbsp;</tt></td><td><tt>%s&nbsp;&nbsp;</tt></td><td><tt>%s&nbsp;&nbsp;</tt></td></tr>",title,f,s,concat(table.sortedkeys(ss)," "))
end
end
@@ -497,7 +499,7 @@ local function loadbase()
if storage == "" then
storage = { }
else
- logs.simple("loading '%s'",datafile)
+ report("loading '%s'",datafile)
storage = loadstring(storage)
storage = (storage and storage()) or { }
end
@@ -527,14 +529,14 @@ end
local function savebase(storage,name)
local datafile = file.join(basepath,basename)
- logs.simple("saving '%s' in '%s'",name or "data",datafile)
+ report("saving '%s' in '%s'",name or "data",datafile)
io.savedata(datafile,table.serialize(storage,true))
end
local function deletestored(detail,currentfont,name)
local storage = loadbase()
if storage and name and storage[name] then
- logs.simple("deleting '%s' from base",name)
+ report("deleting '%s' from base",name)
storage[name] = nil
savebase(storage)
end
@@ -610,7 +612,7 @@ end
function extras.reload()
local command = "mtxrun --script font --reload"
- logs.simple("run command: %s",command)
+ report("run command: %s",command)
os.execute(command)
return do_extras()
end
@@ -679,7 +681,7 @@ function doit(configuration,filename,hashed)
variables.javascripts = ""
variables.javascriptinit = ""
- logs.simple("action: %s",action or "no action")
+ report("action: %s",action or "no action")
local result
@@ -722,7 +724,7 @@ function doit(configuration,filename,hashed)
result = { content = lmx.convert('context-fonttest.lmx',false,variables) }
- logs.simple("time spent on page: %0.03f seconds",os.clock()-start)
+ report("time spent on page: %0.03f seconds",os.clock()-start)
return result
diff --git a/Master/texmf-dist/scripts/context/lua/mtx-server-ctx-help.lua b/Master/texmf-dist/scripts/context/lua/mtx-server-ctx-help.lua
index 2f072f97747..15f393853c1 100644
--- a/Master/texmf-dist/scripts/context/lua/mtx-server-ctx-help.lua
+++ b/Master/texmf-dist/scripts/context/lua/mtx-server-ctx-help.lua
@@ -8,15 +8,17 @@ if not modules then modules = { } end modules ['mtx-server-ctx-help'] = {
-- todo in lua interface: noargument, oneargument, twoarguments, threearguments
---~ dofile(resolvers.find_file("l-aux.lua","tex"))
---~ dofile(resolvers.find_file("l-url.lua","tex"))
-dofile(resolvers.find_file("trac-lmx.lua","tex"))
+--~ dofile(resolvers.findfile("l-aux.lua","tex"))
+--~ dofile(resolvers.findfile("l-url.lua","tex"))
+dofile(resolvers.findfile("trac-lmx.lua","tex"))
-- problem ... serialize parent stack
local format = string.format
local concat = table.concat
+local report = logs.reporter("ctx-help")
+
-- -- -- make this a module: cont-xx.lua
document = document or { }
@@ -329,7 +331,7 @@ document.setups.showsources = true
document.setups.mode = 1
function document.setups.load(filename)
- filename = resolvers.find_file(filename) or ""
+ filename = resolvers.findfile(filename) or ""
if filename ~= "" then
local current = document.setups.loaded[filename]
if not current then
@@ -574,7 +576,7 @@ local variables = {
}
--~ function lmx.loadedfile(filename)
---~ return io.loaddata(resolvers.find_file(filename)) -- return resolvers.texdatablob(filename)
+--~ return io.loaddata(resolvers.findfile(filename)) -- return resolvers.texdatablob(filename)
--~ end
local function doit(configuration,filename,hashed)
@@ -591,7 +593,7 @@ local function doit(configuration,filename,hashed)
lastmode = tonumber(detail.mode or lastmode) or 1
if lastinterface then
- logs.simple("checking interface: %s",lastinterface)
+ report("checking interface: %s",lastinterface)
document.setups.load(format("cont-%s.xml",lastinterface))
end
@@ -632,7 +634,7 @@ local function doit(configuration,filename,hashed)
if document.setups.showsources and lastsource and lastsource ~= "" then
-- todo: mkii, mkiv, tex (can be different)
- local data = io.loaddata(resolvers.find_file(lastsource))
+ local data = io.loaddata(resolvers.findfile(lastsource))
variables.maintitle = lastsource
variables.maintext = formats.listing:format(data)
lastsource = ""
@@ -657,7 +659,7 @@ local function doit(configuration,filename,hashed)
local content = lmx.convert('context-help.lmx',false,variables)
- logs.simple("time spent on page: %0.03f seconds",os.clock()-start)
+ report("time spent on page: %0.03f seconds",os.clock()-start)
return { content = content }
end
diff --git a/Master/texmf-dist/scripts/context/lua/mtx-server-ctx-startup.lua b/Master/texmf-dist/scripts/context/lua/mtx-server-ctx-startup.lua
index 59536c36cdb..55680511314 100644
--- a/Master/texmf-dist/scripts/context/lua/mtx-server-ctx-startup.lua
+++ b/Master/texmf-dist/scripts/context/lua/mtx-server-ctx-startup.lua
@@ -6,12 +6,12 @@ if not modules then modules = { } end modules ['mtx-server-ctx-startup'] = {
license = "see context related readme files"
}
-dofile(resolvers.find_file("trac-lmx.lua","tex"))
+dofile(resolvers.findfile("trac-lmx.lua","tex"))
function doit(configuration,filename,hashed)
local list = { }
- local root = file.dirname(resolvers.find_file("mtx-server.lua") or ".")
+ local root = file.dirname(resolvers.findfile("mtx-server.lua") or ".")
if root == "" then root = "." end
local pattern = root .. "/mtx-server-ctx-*.lua"
local files = dir.glob(pattern)
diff --git a/Master/texmf-dist/scripts/context/lua/mtx-server.lua b/Master/texmf-dist/scripts/context/lua/mtx-server.lua
index dc0befcaa2f..4547877b5ef 100644
--- a/Master/texmf-dist/scripts/context/lua/mtx-server.lua
+++ b/Master/texmf-dist/scripts/context/lua/mtx-server.lua
@@ -6,11 +6,28 @@ if not modules then modules = { } end modules ['mtx-server'] = {
license = "see context related readme files"
}
+local helpinfo = [[
+--start start server
+--port port to listen to
+--root server root
+--scripts scripts sub path
+--index index file
+--auto start on own path
+]]
+
+local application = logs.application {
+ name = "mtx-server",
+ banner = "Simple Webserver For Helpers 0.10",
+ helpinfo = helpinfo,
+}
+
+local report = application.report
+
scripts = scripts or { }
scripts.webserver = scripts.webserver or { }
-dofile(resolvers.find_file("l-url.lua","tex"))
-dofile(resolvers.find_file("luat-soc.lua","tex"))
+dofile(resolvers.findfile("l-url.lua","tex"))
+dofile(resolvers.findfile("luat-soc.lua","tex"))
local socket = socket or require("socket") -- redundant in future version
local format = string.format
@@ -127,7 +144,7 @@ local handlers = { }
local function errormessage(client,configuration,n)
local data = format("<head><title>%s %s</title></head><html><h2>%s %s</h2></html>",n,messages[n],n,messages[n])
- logs.simple("handling error %s: %s",n,messages[n])
+ report("handling error %s: %s",n,messages[n])
handlers.generic(client,configuration,data,nil,true)
end
@@ -136,7 +153,7 @@ local validpaths, registered = { }, { }
function scripts.webserver.registerpath(name)
if not registered[name] then
local cleanname = string.gsub(name,"%.%.","deleted-parent")
- logs.simple("registering path '%s'",cleanname)
+ report("registering path '%s'",cleanname)
validpaths[#validpaths+1] = cleanname
registered[name] = true
end
@@ -145,7 +162,7 @@ end
function handlers.generic(client,configuration,data,suffix,iscontent)
if not iscontent then
local name = data
- logs.simple("requested file '%s'",name)
+ report("requested file '%s'",name)
local fullname = file.join(configuration.root,name)
data = io.loaddata(fullname) or ""
if data == "" then
@@ -153,12 +170,12 @@ function handlers.generic(client,configuration,data,suffix,iscontent)
local fullname = file.join(validpaths[n],name)
data = io.loaddata(fullname) or ""
if data ~= "" then
- logs.simple("sending generic file '%s'",fullname)
+ report("sending generic file '%s'",fullname)
break
end
end
else
- logs.simple("sending generic file '%s'",fullname)
+ report("sending generic file '%s'",fullname)
end
end
if data and data ~= "" then
@@ -192,18 +209,18 @@ function handlers.lua(client,configuration,filename,suffix,iscontent,hashed) --
-- todo: split url in components, see l-url; rather trivial
local result, keep = loaded[filename], false
if result then
- logs.simple("reusing script: %s",filename)
+ report("reusing script: %s",filename)
else
- logs.simple("locating script: %s",filename)
+ report("locating script: %s",filename)
if lfs.isfile(filename) then
- logs.simple("loading script: %s",filename)
+ report("loading script: %s",filename)
result = loadfile(filename)
- logs.simple("return type: %s",type(result))
+ report("return type: %s",type(result))
if result and type(result) == "function" then
-- result() should return a table { [type=,] [length=,] content= }, function or string
result, keep = result()
if keep then
- logs.simple("saving script: %s",type(result))
+ report("saving script: %s",type(result))
loaded[filename] = result
end
end
@@ -262,7 +279,7 @@ function scripts.webserver.run(configuration)
end
end
end
- configuration.root = dir.expand_name(configuration.root)
+ configuration.root = dir.expandname(configuration.root)
if not configuration.index then
for i=1,#indices do
local name = indices[i]
@@ -274,14 +291,14 @@ function scripts.webserver.run(configuration)
configuration.index = configuration.index or "unknown"
end
if not configuration.scripts or configuration.scripts == "" then
- configuration.scripts = dir.expand_name(file.join(configuration.root or ".",configuration.scripts or "."))
+ configuration.scripts = dir.expandname(file.join(configuration.root or ".",configuration.scripts or "."))
end
-- so far for checks
- logs.simple("running at port: %s",configuration.port)
- logs.simple("document root: %s",configuration.root or resolvers.ownpath)
- logs.simple("main index file: %s",configuration.index)
- logs.simple("scripts subpath: %s",configuration.scripts)
- logs.simple("context services: http://localhost:%s/mtx-server-ctx-startup.lua",configuration.port)
+ report("running at port: %s",configuration.port)
+ report("document root: %s",configuration.root or resolvers.ownpath)
+ report("main index file: %s",configuration.index)
+ report("scripts subpath: %s",configuration.scripts)
+ report("context services: http://localhost:%s/mtx-server-ctx-startup.lua",configuration.port)
local server = assert(socket.bind("*", configuration.port))
--~ local reading = { server }
while true do -- no multiple clients
@@ -295,26 +312,27 @@ function scripts.webserver.run(configuration)
errormessage(client,configuration,404)
else
local from = client:getpeername()
- logs.simple("request from: %s",tostring(from))
+ report("request from: %s",tostring(from))
local fullurl = request:match("GET (.+) HTTP/.*$") -- todo: more clever
fullurl = socket.url.unescape(fullurl)
local hashed = url.hashed(fullurl)
local query = url.query(hashed.query)
local filename = hashed.path
+--~ table.print(hashed)
if filename then
filename = socket.url.unescape(filename)
- logs.simple("requested action: %s",filename)
+ report("requested action: %s",filename)
if filename:find("%.%.") then
filename = nil -- invalid path
end
if filename == nil or filename == "" or filename == "/" then
filename = configuration.index
- logs.simple("invalid filename, forcing: %s",filename)
+ report("invalid filename, forcing: %s",filename)
end
local suffix = file.extname(filename)
local action = handlers[suffix] or handlers.generic
if action then
- logs.simple("performing action: %s",filename)
+ report("performing action: %s",filename)
action(client,configuration,filename,suffix,false,hashed) -- filename and no content
else
errormessage(client,configuration,404)
@@ -324,23 +342,12 @@ function scripts.webserver.run(configuration)
end
end
client:close()
- logs.simple("time spent with client: %0.03f seconds",os.clock()-start)
+ report("time spent with client: %0.03f seconds",os.clock()-start)
end
end
-logs.extendbanner("Simple Webserver For Helpers 0.10")
-
-messages.help = [[
---start start server
---port port to listen to
---root server root
---scripts scripts sub path
---index index file
---auto start on own path
-]]
-
if environment.argument("auto") then
- local path = resolvers.find_file("mtx-server.lua") or "."
+ local path = resolvers.findfile("mtx-server.lua") or "."
scripts.webserver.run {
port = environment.argument("port"),
root = environment.argument("root") or file.dirname(path) or ".",
@@ -354,8 +361,8 @@ elseif environment.argument("start") then
scripts = environment.argument("scripts"),
}
else
- logs.help(messages.help)
+ application.help()
end
--- mtxrun --script server --start => http://localhost:8080/mtx-server-ctx-help.lua
+-- mtxrun --script server --start => http://localhost:31415/mtx-server-ctx-startup.lua
diff --git a/Master/texmf-dist/scripts/context/lua/mtx-texworks.lua b/Master/texmf-dist/scripts/context/lua/mtx-texworks.lua
index 73ab846cd7f..33e56df468a 100644
--- a/Master/texmf-dist/scripts/context/lua/mtx-texworks.lua
+++ b/Master/texmf-dist/scripts/context/lua/mtx-texworks.lua
@@ -6,6 +6,19 @@ if not modules then modules = { } end modules ['mtx-texworks'] = {
license = "see context related readme files"
}
+local helpinfo = [[
+--start [--verbose] start texworks
+--test report what will happen
+]]
+
+local application = logs.application {
+ name = "mtx-texworks",
+ banner = "TeXworks Startup Script 1.00",
+ helpinfo = helpinfo,
+}
+
+local report = application.report
+
scripts = scripts or { }
scripts.texworks = scripts.texworks or { }
@@ -25,35 +38,35 @@ local texworkininame = "texworks.ini"
function scripts.texworks.start(indeed)
local workname = (os.type == "windows" and "texworks.exe") or "texworks"
local fullname = nil
- local binpaths = file.split_path(os.getenv("PATH")) or file.split_path(os.getenv("path"))
+ local binpaths = file.splitpath(os.getenv("PATH")) or file.splitpath(os.getenv("path"))
local usedsignal = texworkssignal
- local datapath = resolvers.find_file(usedsignal,"other text files") or ""
+ local datapath = resolvers.findfile(usedsignal,"other text files") or ""
if datapath ~= "" then
datapath = file.dirname(datapath) -- data
if datapath == "" then
- datapath = resolvers.clean_path(lfs.currentdir())
+ datapath = resolvers.cleanpath(lfs.currentdir())
end
else
usedsignal = texworkininame
- datapath = resolvers.find_file(usedsignal,"other text files") or ""
+ datapath = resolvers.findfile(usedsignal,"other text files") or ""
if datapath == "" then
usedsignal = string.lower(usedsignal)
- datapath = resolvers.find_file(usedsignal,"other text files") or ""
+ datapath = resolvers.findfile(usedsignal,"other text files") or ""
end
if datapath ~= "" and lfs.isfile(datapath) then
datapath = file.dirname(datapath) -- TUG
datapath = file.dirname(datapath) -- data
if datapath == "" then
- datapath = resolvers.clean_path(lfs.currentdir())
+ datapath = resolvers.cleanpath(lfs.currentdir())
end
end
end
if datapath == "" then
- logs.simple("invalid datapath, maybe you need to regenerate the file database")
+ report("invalid datapath, maybe you need to regenerate the file database")
return false
end
if not binpaths or #binpaths == 0 then
- logs.simple("invalid binpath")
+ report("invalid binpath")
return false
end
for i=1,#binpaths do
@@ -64,7 +77,7 @@ function scripts.texworks.start(indeed)
end
end
if not fullname then
- logs.simple("unable to locate %s",workname)
+ report("unable to locate %s",workname)
return false
end
for i=1,#texworkspaths do
@@ -73,27 +86,20 @@ function scripts.texworks.start(indeed)
os.setenv("TW_INIPATH",datapath)
os.setenv("TW_LIBPATH",datapath)
if not indeed or environment.argument("verbose") then
- logs.simple("used signal: %s", usedsignal)
- logs.simple("data path : %s", datapath)
- logs.simple("full name : %s", fullname)
- logs.simple("set paths : TW_INIPATH TW_LIBPATH")
+ report("used signal: %s", usedsignal)
+ report("data path : %s", datapath)
+ report("full name : %s", fullname)
+ report("set paths : TW_INIPATH TW_LIBPATH")
end
if indeed then
os.launch(fullname)
end
end
-logs.extendbanner("TeXworks Startup Script 1.00",true)
-
-messages.help = [[
---start [--verbose] start texworks
---test report what will happen
-]]
-
if environment.argument("start") then
scripts.texworks.start(true)
elseif environment.argument("test") then
scripts.texworks.start()
else
- logs.help(messages.help)
+ application.help()
end
diff --git a/Master/texmf-dist/scripts/context/lua/mtx-timing.lua b/Master/texmf-dist/scripts/context/lua/mtx-timing.lua
index 40e33cdaef0..03100e991af 100644
--- a/Master/texmf-dist/scripts/context/lua/mtx-timing.lua
+++ b/Master/texmf-dist/scripts/context/lua/mtx-timing.lua
@@ -8,8 +8,22 @@ if not modules then modules = { } end modules ['mtx-timing'] = {
local format, gsub, concat = string.format, string.gsub, table.concat
-dofile(resolvers.find_file("trac-tim.lua","tex"))
-dofile(resolvers.find_file("trac-lmx.lua","tex"))
+local helpinfo = [[
+--xhtml make xhtml file
+--launch launch after conversion
+--remove remove after launching
+]]
+
+local application = logs.application {
+ name = "mtx-timing",
+ banner = "ConTeXt Timing Tools 0.10",
+ helpinfo = helpinfo,
+}
+
+local report = application.report
+
+dofile(resolvers.findfile("trac-tim.lua","tex"))
+dofile(resolvers.findfile("trac-lmx.lua","tex"))
local meta = [[
beginfig(%s) ;
@@ -57,6 +71,8 @@ local directrun = true
local what = { "parameters", "nodes" }
+plugins = plugins or { } -- brrr, will become moduledata as well
+
function plugins.progress.make_svg(filename,other)
local metadata, menudata, c = { }, { }, 0
metadata[#metadata+1] = 'outputformat := "svg" ;'
@@ -74,9 +90,9 @@ function plugins.progress.make_svg(filename,other)
metadata[#metadata+1] = "end ."
metadata = concat(metadata,"\n\n")
if directrun then
- dofile(resolvers.find_file("mlib-run.lua","tex"))
+ dofile(resolvers.findfile("mlib-run.lua","tex"))
commands = commands or { }
- commands.writestatus = logs.report
+ commands.writestatus = report
local result = metapost.directrun("metafun","timing data","svg",true,metadata)
return menudata, result
else
@@ -138,7 +154,7 @@ function plugins.progress.make_lmx_page(name,launch,remove)
local htmldata = plugins.progress.makehtml(filename,other,menudata,metadata)
lmx.htmfile = function(name) return name .. "-timing.xhtml" end
- lmx.lmxfile = function(name) return resolvers.find_file(name,'tex') end
+ lmx.lmxfile = function(name) return resolvers.findfile(name,'tex') end
local variables = {
['title-default'] = 'ConTeXt Timing Information',
@@ -167,9 +183,9 @@ scripts.timings = scripts.timings or { }
function scripts.timings.xhtml(filename)
if filename == "" then
- logs.simple("provide filename")
+ report("provide filename")
elseif not plugins.progress.valid_file(filename) then
- logs.simple("first run context again with the --timing option")
+ report("first run context again with the --timing option")
else
local basename = file.removesuffix(filename)
local launch = environment.argument("launch")
@@ -178,16 +194,8 @@ function scripts.timings.xhtml(filename)
end
end
-logs.extendbanner("ConTeXt Timing Tools 0.10",true)
-
-messages.help = [[
---xhtml make xhtml file
---launch launch after conversion
---remove remove after launching
-]]
-
if environment.argument("xhtml") then
scripts.timings.xhtml(environment.files[1] or "")
else
- logs.help(messages.help)
+ application.help()
end
diff --git a/Master/texmf-dist/scripts/context/lua/mtx-tools.lua b/Master/texmf-dist/scripts/context/lua/mtx-tools.lua
index bf4add16840..905b3aa8583 100644
--- a/Master/texmf-dist/scripts/context/lua/mtx-tools.lua
+++ b/Master/texmf-dist/scripts/context/lua/mtx-tools.lua
@@ -8,6 +8,33 @@ if not modules then modules = { } end modules ['mtx-tools'] = {
local find, format, sub, rep, gsub, lower = string.find, string.format, string.sub, string.rep, string.gsub, string.lower
+local helpinfo = [[
+--disarmutfbomb remove utf bomb if present
+ --force remove indeed
+
+--dirtoxml glob directory into xml
+ --pattern glob pattern (default: *)
+ --url url attribute (no processing)
+ --root the root of the globbed path (default: .)
+ --output output filename (console by default)
+ --recurse recurse into subdirecories
+ --stripname take pathpart of given pattern
+ --longname set name attributes to full path name
+
+--downcase
+ --pattern glob pattern (default: *)
+ --recurse recurse into subdirecories
+ --force downcase indeed
+]]
+
+local application = logs.application {
+ name = "mtx-tools",
+ banner = "Some File Related Goodies 1.01",
+ helpinfo = helpinfo,
+}
+
+local report = application.report
+
scripts = scripts or { }
scripts.tools = scripts.tools or { }
@@ -23,24 +50,24 @@ function scripts.tools.disarmutfbomb()
if not data then
-- just skip
elseif find(data,bomb_1) then
- logs.simple("file '%s' has a 2 character utf bomb",name)
+ report("file '%s' has a 2 character utf bomb",name)
if force then
io.savedata(name,(gsub(data,bomb_1,"")))
end
done = true
elseif find(data,bomb_2) then
- logs.simple("file '%s' has a 3 character utf bomb",name)
+ report("file '%s' has a 3 character utf bomb",name)
if force then
io.savedata(name,(gsub(data,bomb_2,"")))
end
done = true
else
- -- logs.simple("file '%s' has no utf bomb",name)
+ -- report("file '%s' has no utf bomb",name)
end
end
end
if done and not force then
- logs.simple("use --force to do a real disarming")
+ report("use --force to do a real disarming")
end
end
@@ -63,12 +90,12 @@ function scripts.tools.downcase()
end)
if n > 0 then
if force then
- logs.simple("%s files renamed",n)
+ report("%s files renamed",n)
else
- logs.simple("use --force to do a real rename (%s files involved)",n)
+ report("use --force to do a real rename (%s files involved)",n)
end
else
- logs.simple("nothing to do")
+ report("nothing to do")
end
end
@@ -111,7 +138,7 @@ function scripts.tools.dirtoxml()
end
if not pattern or pattern == "" then
- logs.report('provide --pattern=')
+ report('provide --pattern=')
return
end
@@ -123,7 +150,7 @@ function scripts.tools.dirtoxml()
lfs.chdir(root)
- local list = dir.collect_pattern(root,luapattern,recurse)
+ local list = dir.collectpattern(root,luapattern,recurse)
if list[outputfile] then
list[outputfile] = nil
@@ -144,27 +171,6 @@ function scripts.tools.dirtoxml()
end
-logs.extendbanner("Some File Related Goodies 1.01",true)
-
-messages.help = [[
---disarmutfbomb remove utf bomb if present
- --force remove indeed
-
---dirtoxml glob directory into xml
- --pattern glob pattern (default: *)
- --url url attribute (no processing)
- --root the root of the globbed path (default: .)
- --output output filename (console by default)
- --recurse recurse into subdirecories
- --stripname take pathpart of given pattern
- --longname set name attributes to full path name
-
---downcase
- --pattern glob pattern (default: *)
- --recurse recurse into subdirecories
- --force downcase indeed
-]]
-
if environment.argument("disarmutfbomb") then
scripts.tools.disarmutfbomb()
elseif environment.argument("dirtoxml") then
@@ -172,5 +178,5 @@ elseif environment.argument("dirtoxml") then
elseif environment.argument("downcase") then
scripts.tools.downcase()
else
- logs.help(messages.help)
+ application.help()
end
diff --git a/Master/texmf-dist/scripts/context/lua/mtx-unzip.lua b/Master/texmf-dist/scripts/context/lua/mtx-unzip.lua
index f990f421032..645863426db 100644
--- a/Master/texmf-dist/scripts/context/lua/mtx-unzip.lua
+++ b/Master/texmf-dist/scripts/context/lua/mtx-unzip.lua
@@ -1,20 +1,32 @@
+if not modules then modules = { } end modules ['mtx-unzip'] = {
+ version = 1.001,
+ comment = "companion to mtxrun.lua",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
-- maybe --pattern
-logs.extendbanner("Simple Unzipper 0.10")
+local format = string.format
-messages.help = [[
+local helpinfo = [[
--list list files in archive
--junk flatten unzipped directory structure
--extract extract files
]]
+local application = logs.application {
+ name = "mtx-unzip",
+ banner = "Simple Unzipper 0.10",
+ helpinfo = helpinfo,
+}
+
+local report = application.report
+
scripts = scripts or { }
scripts.unzipper = scripts.unzipper or { }
-function scripts.unzipper.help()
- logs.help(messages.help)
-end
-
function scripts.unzipper.opened()
local filename = environment.files[1]
if filename and filename ~= "" then
@@ -24,7 +36,7 @@ function scripts.unzipper.opened()
return zipfile
end
end
- logs.report("unzip", "no zip file: " .. filename)
+ report("no zip file: %s",filename)
return false
end
@@ -36,10 +48,13 @@ function scripts.unzipper.list()
if #k.filename > n then n = #k.filename end
end
local files, paths, compressed, uncompressed = 0, 0, 0, 0
+ local template_a = "%-"..n.."s"
+ local template_b = "%-"..n.."s % 9i % 9i"
+ local template_c = "\n%-"..n.."s % 9i % 9i"
for k in zipfile:files() do
if k.filename:find("/$") then
paths = paths + 1
- print(string.format("%s", k.filename:rpadd(n," ")))
+ print(format(template_a, k.filename))
else
files = files + 1
local cs, us = k.compressed_size, k.uncompressed_size
@@ -49,10 +64,10 @@ function scripts.unzipper.list()
if us > uncompressed then
uncompressed = us
end
- print(string.format("%s % 9i % 9i", k.filename:rpadd(n," "),cs,us))
+ print(format(template_b,k.filename,cs,us))
end
- end
- print(string.format("\n%s % 9i % 9i", (files .. " files, " .. paths .. " directories"):rpadd(n," "),compressed,uncompressed))
+ end -- check following pattern, n is not enough
+ print(format(template_c,files .. " files, " .. paths .. " directories",compressed,uncompressed))
end
end
@@ -91,11 +106,11 @@ function scripts.unzipper.extract()
end
if environment.arguments["h"] or environment.arguments["help"] then
- scripts.unzipper.help()
+ application.help()
elseif environment.arguments["l"] or environment.arguments["list"] then
scripts.unzipper.list(zipfile)
elseif environment.files[1] then -- implicit --extract
scripts.unzipper.extract(zipfile)
else
- scripts.unzipper.help()
+ application.help()
end
diff --git a/Master/texmf-dist/scripts/context/lua/mtx-update.lua b/Master/texmf-dist/scripts/context/lua/mtx-update.lua
index b56083d3809..750c7488692 100644
--- a/Master/texmf-dist/scripts/context/lua/mtx-update.lua
+++ b/Master/texmf-dist/scripts/context/lua/mtx-update.lua
@@ -11,7 +11,33 @@ if not modules then modules = { } end modules ['mtx-update'] = {
-- Together with Arthur Reutenauer she made sure that it worked well on all
-- platforms that matter.
-local format, concat, gmatch = string.format, table.concat, string.gmatch
+local helpinfo = [[
+--platform=string platform (windows, linux, linux-64, osx-intel, osx-ppc, linux-ppc)
+--server=string repository url (rsync://contextgarden.net)
+--module=string repository url (minimals)
+--repository=string specify version (current, experimental)
+--context=string specify version (current, latest, beta, yyyy.mm.dd)
+--rsync=string rsync binary (rsync)
+--texroot=string installation directory (not guessed for the moment)
+--engine=string tex engine (luatex, pdftex, xetex)
+--extras=string extra modules (can be list or 'all')
+--goodies=string extra binaries (like scite and texworks)
+--force instead of a dryrun, do the real thing
+--update update minimal tree
+--make also make formats and generate file databases
+--keep don't delete unused or obsolete files
+--state update tree using saved state
+]]
+
+local application = logs.application {
+ name = "mtx-update",
+ banner = "ConTeXt Minimals Updater 0.22",
+ helpinfo = helpinfo,
+}
+
+local report = application.report
+
+local format, concat, gmatch, gsub = string.format, table.concat, string.gmatch, string.gsub
scripts = scripts or { }
scripts.update = scripts.update or { }
@@ -144,6 +170,11 @@ scripts.update.platforms = {
["solaris"] = "solaris-sparc",
}
+scripts.update.selfscripts = {
+ "mtxrun",
+ -- "luatools",
+}
+
-- the list is filled up later (when we know what modules to download)
scripts.update.modules = {
@@ -154,10 +185,10 @@ function scripts.update.run(str)
-- (texlua sets luatex as the engine, we need to reset that or to fix texexec :)
os.setenv("engine",nil)
if environment.argument("force") then
- logs.report("run", str)
+ report("run, %s",str)
os.execute(str)
else
- logs.report("dry run", str)
+ report("dry run, %s",str)
end
end
@@ -171,7 +202,7 @@ end
function scripts.update.synchronize()
- logs.report("update","start")
+ report("update, start")
local texroot = scripts.update.fullpath(states.get("paths.root"))
local engines = states.get('engines') or { }
@@ -184,7 +215,7 @@ function scripts.update.synchronize()
local goodies = states.get("goodies") -- goodies (like editors)
local force = environment.argument("force")
- bin = string.gsub(bin,"\\","/")
+ bin = gsub(bin,"\\","/")
if not url:find("::$") then url = url .. "::" end
local ok = lfs.attributes(texroot,"mode") == "directory"
@@ -197,6 +228,7 @@ function scripts.update.synchronize()
dir.mkdirs(format("%s/%s", texroot, "texmf-cache"))
dir.mkdirs(format("%s/%s", texroot, "texmf-local"))
dir.mkdirs(format("%s/%s", texroot, "texmf-project"))
+ dir.mkdirs(format("%s/%s", texroot, "texmf-fonts"))
end
if ok or not force then
@@ -209,8 +241,8 @@ function scripts.update.synchronize()
local archives = {}
for i=1,#collection do
local archive = collection[i][1]
- archive = archive:gsub("<platform>", platform)
- archive = archive:gsub("<version>", version)
+ archive = gsub(archive,"<platform>",platform)
+ archive = gsub(archive,"<version>",version)
archives[#archives+1] = archive
end
return archives
@@ -281,12 +313,12 @@ function scripts.update.synchronize()
if platform then
for i=1,#collection do
local c = collection[i]
- local archive = c[1]:gsub("<platform>", platform)
- local destination = format("%s/%s", texroot, c[2]:gsub("<platform>", platform))
- destination = destination:gsub("\\","/")
- archive = archive:gsub("<version>",version)
+ local archive = gsub(c[1],"<platform>",platform)
+ local destination = format("%s/%s", texroot, gsub(c[2],"<platform>", platform))
+ destination = gsub(destination,"\\","/")
+ archive = gsub(archive,"<version>",version)
if osplatform == "windows" or osplatform == "mswin" then
- destination = destination:gsub("([a-zA-Z]):/", "/cygdrive/%1/")
+ destination = gsub(destination,"([a-zA-Z]):/", "/cygdrive/%1/")
end
individual[#individual+1] = { archive, destination }
end
@@ -330,23 +362,12 @@ function scripts.update.synchronize()
end
end
end
- if logs.verbose then
- for k, v in next, combined do
- logs.report("update", k)
- for i=1,#v do
- logs.report("update", " <= " .. v[i])
- end
- end
- end
for destination, archive in next, combined do
local archives, command = concat(archive," "), ""
- -- local normalflags, deleteflags = states.get("rsync.flags.normal"), states.get("rsync.flags.delete")
- -- if environment.argument("keep") or destination:find("%.$") then
- -- command = format("%s %s %s'%s' '%s'", bin, normalflags, url, archives, destination)
- -- else
- -- command = format("%s %s %s %s'%s' '%s'", bin, normalflags, deleteflags, url, archives, destination)
- -- end
local normalflags, deleteflags = states.get("rsync.flags.normal"), ""
+ if os.name == "windows" then
+ normalflags = normalflags .. " -L" -- no symlinks
+ end
local dryrunflags = ""
if not environment.argument("force") then
dryrunflags = "--dry-run"
@@ -355,7 +376,7 @@ function scripts.update.synchronize()
deleteflags = states.get("rsync.flags.delete")
end
command = format("%s %s %s %s %s'%s' '%s'", bin, normalflags, deleteflags, dryrunflags, url, archives, destination)
- --logs.report("mtx update", format("running command: %s",command))
+ -- report("running command: %s",command)
if not fetched[command] then
scripts.update.run(command,true)
fetched[command] = command
@@ -363,43 +384,44 @@ function scripts.update.synchronize()
end
local function update_script(script, platform)
- local bin = bin:gsub("\\","/")
- local texroot = texroot:gsub("\\","/")
+ local bin = gsub(bin,"\\","/")
+ local texroot = gsub(texroot,"\\","/")
platform = scripts.update.platforms[platform]
if platform then
local command
if platform == 'mswin' then
- bin = bin:gsub("([a-zA-Z]):/", "/cygdrive/%1/")
- texroot = texroot:gsub("([a-zA-Z]):/", "/cygdrive/%1/")
- command = string.format("%s -t %s/texmf-context/scripts/context/lua/%s.lua %s/texmf-mswin/bin/", bin, texroot, script, texroot)
+ bin = gsub(bin,"([a-zA-Z]):/", "/cygdrive/%1/")
+ texroot = gsub(texroot,"([a-zA-Z]):/", "/cygdrive/%1/")
+ command = format("%s -t %s/texmf-context/scripts/context/lua/%s.lua %s/texmf-mswin/bin/", bin, texroot, script, texroot)
else
- command = string.format("%s -tgo --chmod=a+x %s/texmf-context/scripts/context/lua/%s.lua %s/texmf-%s/bin/%s", bin, texroot, script, texroot, platform, script)
+ command = format("%s -tgo --chmod=a+x %s/texmf-context/scripts/context/lua/%s.lua %s/texmf-%s/bin/%s", bin, texroot, script, texroot, platform, script)
end
- logs.report("mtx update", format("updating %s for %s: %s", script, platform, command))
+ report("updating %s for %s: %s", script, platform, command)
scripts.update.run(command)
end
end
for platform, _ in next, platforms do
- update_script('luatools',platform)
- update_script('mtxrun',platform)
+ for i=1, #scripts.update.selfscripts do
+ update_script(scripts.update.selfscripts[i],platform)
+ end
end
else
- logs.report("mtx update", format("no valid texroot: %s",texroot))
+ report("no valid texroot: %s",texroot)
end
if not force then
- logs.report("update", "use --force to really update files")
+ report("use --force to really update files")
end
resolvers.load_tree(texroot) -- else we operate in the wrong tree
-- update filename database for pdftex/xetex
- scripts.update.run("mktexlsr")
+ scripts.update.run(format('mtxrun --tree="%s" --direct mktexlsr',texroot))
-- update filename database for luatex
- scripts.update.run("luatools --generate")
+ scripts.update.run(format('mtxrun --tree="%s" --generate',texroot))
- logs.report("update","done")
+ report("update, done")
end
function table.fromhash(t)
@@ -413,7 +435,7 @@ end
-- make the ConTeXt formats
function scripts.update.make()
- logs.report("make","start")
+ report("make, start")
local force = environment.argument("force")
local texroot = scripts.update.fullpath(states.get("paths.root"))
@@ -424,8 +446,8 @@ function scripts.update.make()
resolvers.load_tree(texroot)
- scripts.update.run("mktexlsr")
- scripts.update.run("luatools --generate")
+ scripts.update.run(format('mtxrun --tree="%s" --direct mktexlsr',texroot))
+ scripts.update.run(format('mtxrun --tree="%s" --generate',texroot))
local askedformats = formats
local texformats = table.tohash(scripts.update.texformats)
@@ -444,45 +466,24 @@ function scripts.update.make()
if formatlist ~= "" then
for engine in next, engines do
if engine == "luatex" then
- scripts.update.run(format("context --make")) -- maybe also formatlist
+ scripts.update.run(format('mtxrun --tree="%s" --script context --autogenerate --make',texroot))
else
- -- todo: just handle make here or in mtxrun --script context --make
- scripts.update.run(format("texexec --make --all --fast --%s %s",engine,formatlist))
+ scripts.update.run(format('mtxrun --tree="%s" --script texexec --make --all --%s %s',texroot,engine,formatlist))
end
end
end
local formatlist = concat(table.fromhash(mpformats), " ")
if formatlist ~= "" then
- scripts.update.run(format("texexec --make --all --fast %s",formatlist))
+ scripts.update.run(format('mtxrun --tree="%s" --script texexec --make --all %s',texroot,formatlist))
end
if not force then
- logs.report("make", "use --force to really make formats")
+ report("make, use --force to really make formats")
end
- scripts.update.run("mktexlsr")
- scripts.update.run("luatools --generate")
- logs.report("make","done")
+ scripts.update.run(format('mtxrun --tree="%s" --direct mktexlsr',texroot))
+ scripts.update.run(format('mtxrun --tree="%s" --generate',texroot))
+ report("make, done")
end
-logs.extendbanner("ConTeXt Minimals Updater 0.21",true)
-
-messages.help = [[
---platform=string platform (windows, linux, linux-64, osx-intel, osx-ppc, linux-ppc)
---server=string repository url (rsync://contextgarden.net)
---module=string repository url (minimals)
---repository=string specify version (current, experimental)
---context=string specify version (current, latest, yyyy.mm.dd)
---rsync=string rsync binary (rsync)
---texroot=string installation directory (not guessed for the moment)
---engine=string tex engine (luatex, pdftex, xetex)
---extras=string extra modules (can be list or 'all')
---goodies=string extra binaries (like scite and texworks)
---force instead of a dryrun, do the real thing
---update update minimal tree
---make also make formats and generate file databases
---keep don't delete unused or obsolete files
---state update tree using saved state
-]]
-
scripts.savestate = true
if scripts.savestate then
@@ -549,7 +550,8 @@ if scripts.savestate then
states.set("goodies." .. r, true)
end
- logs.report("state","loaded")
+ report("state, loaded")
+ report()
end
@@ -567,7 +569,7 @@ if environment.argument("update") then
elseif environment.argument("make") then
scripts.update.make()
else
- logs.help(messages.help)
+ application.help()
end
if scripts.savestate then
@@ -575,6 +577,6 @@ if scripts.savestate then
states.set("info.runtime",tonumber(statistics.elapsedtime(states)))
if environment.argument("force") then
states.save()
- logs.report("state","saved")
+ report("state","saved")
end
end
diff --git a/Master/texmf-dist/scripts/context/lua/mtx-watch.lua b/Master/texmf-dist/scripts/context/lua/mtx-watch.lua
index 10f01cf8698..5bd0ad5f496 100644
--- a/Master/texmf-dist/scripts/context/lua/mtx-watch.lua
+++ b/Master/texmf-dist/scripts/context/lua/mtx-watch.lua
@@ -6,6 +6,25 @@ if not modules then modules = { } end modules ['mtx-watch'] = {
license = "see context related readme files"
}
+local helpinfo = [[
+--logpath optional path for log files
+--watch watch given path [--delay]
+--pipe use pipe instead of execute
+--delay delay between sweeps
+--automachine replace /machine/ in path /<servername>/
+--collect condense log files
+--cleanup=delay remove files in given path [--force]
+--showlog show log data
+]]
+
+local application = logs.application {
+ name = "mtx-watch",
+ banner = "ConTeXt Request Watchdog 1.00",
+ helpinfo = helpinfo,
+}
+
+local report = application.report
+
scripts = scripts or { }
scripts.watch = scripts.watch or { }
@@ -92,7 +111,7 @@ function scripts.watch.watch()
end
end
for i=1,#paths do
- logs.report("watch", "watching path ".. paths[i])
+ report("watching path %s",paths[i])
end
local function process()
local done = false
@@ -107,7 +126,7 @@ function scripts.watch.watch()
local ok, joblog = pcall(dofile,name)
if ok and joblog then
if joblog.status == "processing" then
- logs.report("watch",format("aborted job, %s added to queue",name))
+ report("aborted job, %s added to queue",name)
joblog.status = "queued"
io.savedata(name, table.serialize(joblog,true))
elseif joblog.status == "queued" then
@@ -124,7 +143,7 @@ function scripts.watch.watch()
joblog.status = "processing"
joblog.runtime = clock()
io.savedata(name, table.serialize(joblog,true))
- logs.report("watch",format("running: %s", command))
+ report("running: %s", command)
local newpath = file.dirname(name)
io.flush()
local result = ""
@@ -141,7 +160,7 @@ function scripts.watch.watch()
scripts.watch.save_exa_modes(joblog,ctmname)
if pipe then result = os.resultof(command) else result = os.spawn(command) end
end
- logs.report("watch",format("return value: %s", result))
+ report("return value: %s", result)
done = true
local path, base = replacements.outputpath, file.basename(replacements.filename)
joblog.runtime = clock() - joblog.runtime
@@ -162,7 +181,7 @@ function scripts.watch.watch()
if logpath and logpath ~= "" then
local name = file.join(logpath,os.uuid() .. ".lua")
io.savedata(name, table.serialize(joblog,true))
- logs.report("watch", "saving joblog in " .. name)
+ report("saving joblog in %s",name)
end
end
end
@@ -175,7 +194,7 @@ function scripts.watch.watch()
--~ if not done then
--~ n = n + 1
--~ if n >= 10 then
---~ logs.report("watch", format("run time: %i seconds, memory usage: %0.3g MB", difftime(time(),start), (status.luastate_bytes/1024)/1000))
+--~ report("run time: %i seconds, memory usage: %0.3g MB", difftime(time(),start), (status.luastate_bytes/1024)/1000)
--~ n = 0
--~ end
--~ os.sleep(delay)
@@ -187,7 +206,7 @@ function scripts.watch.watch()
if not done then
n = n + 1
if n >= 10 then
- logs.report("watch", format("run time: %i seconds, memory usage: %0.3g MB", difftime(time(),start), (status.luastate_bytes/1024)/1000))
+ report("run time: %i seconds, memory usage: %0.3g MB", difftime(time(),start), (status.luastate_bytes/1024)/1000)
n = 0
end
local ttime = 0
@@ -222,7 +241,7 @@ function scripts.watch.watch()
local filetime = lfs.attributes(name,"modification")
local delta = difftime(currenttime,filetime)
if delta > cleanupdelay then
- -- logs.report("watch",format("cleaning up '%s'",name))
+ -- report("cleaning up '%s'",name)
os.remove(name)
end
end
@@ -248,7 +267,7 @@ function scripts.watch.watch()
end
end
else
- logs.report("watch", "no paths to watch")
+ report("no paths to watch")
end
end
@@ -282,7 +301,7 @@ function scripts.watch.save_logs(collection,path) -- play safe
local check = dofile(filename)
for k,v in next, check do
if not collection[k] then
- logs.error("watch", "error in saving file")
+ report("error in saving file")
os.remove(filename)
return false
end
@@ -326,7 +345,7 @@ function scripts.watch.show_logs(path) -- removes duplicates
local v = sorted[k]
local c = collection[v]
local f, s, r, n = c.filename or "?", c.status or "?", c.runtime or 0, c.size or 0
- logs.report("watch", format("%s %s %3i %8i %s",string.padd(f,max," "),string.padd(s,10," "),r,n,v))
+ report("%s %s %3i %8i %s",string.padd(f,max," "),string.padd(s,10," "),r,n,v)
end
end
@@ -335,11 +354,11 @@ function scripts.watch.cleanup_stale_files() -- removes duplicates
local delay = tonumber(environment.argument("cleanup"))
local force = environment.argument("force")
if not path or path == "." then
- logs.report("watch","provide qualified path")
+ report("provide qualified path")
elseif not delay then
- logs.report("watch","missing --cleanup=delay")
+ report("missing --cleanup=delay")
else
- logs.report("watch","dryrun, use --force for real cleanup")
+ report("dryrun, use --force for real cleanup")
local files = dir.glob(file.join(path,"*"))
local rtime = time()
for i=1,#files do
@@ -347,7 +366,7 @@ function scripts.watch.cleanup_stale_files() -- removes duplicates
local mtime = lfs.attributes(name,"modification")
local delta = difftime(rtime,mtime)
if delta > delay then
- logs.report("watch",format("cleaning up '%s'",name))
+ report("cleaning up '%s'",name)
if force then
os.remove(name)
end
@@ -356,19 +375,6 @@ function scripts.watch.cleanup_stale_files() -- removes duplicates
end
end
-logs.extendbanner("ConTeXt Request Watchdog 1.00",true)
-
-messages.help = [[
---logpath optional path for log files
---watch watch given path [--delay]
---pipe use pipe instead of execute
---delay delay between sweeps
---automachine replace /machine/ in path /<servername>/
---collect condense log files
---cleanup=delay remove files in given path [--force]
---showlog show log data
-]]
-
if environment.argument("watch") then
scripts.watch.watch()
elseif environment.argument("collect") then
@@ -378,5 +384,5 @@ elseif environment.argument("cleanup") then
elseif environment.argument("showlog") then
scripts.watch.show_logs()
else
- logs.help(messages.help)
+ application.help()
end
diff --git a/Master/texmf-dist/scripts/context/lua/mtxrun.lua b/Master/texmf-dist/scripts/context/lua/mtxrun.lua
index 2bdd1b8f020..cc5ebb478ac 100755
--- a/Master/texmf-dist/scripts/context/lua/mtxrun.lua
+++ b/Master/texmf-dist/scripts/context/lua/mtxrun.lua
@@ -8,7 +8,6 @@ if not modules then modules = { } end modules ['mtxrun'] = {
license = "see context related readme files"
}
-
-- one can make a stub:
--
-- #!/bin/sh
@@ -38,8 +37,6 @@ if not modules then modules = { } end modules ['mtxrun'] = {
-- remember for subruns: _CTX_K_S_#{original}_
-- remember for subruns: TEXMFSTART.#{original} [tex.rb texmfstart.rb]
-texlua = true
-
-- begin library merge
@@ -54,8 +51,9 @@ if not modules then modules = { } end modules ['l-string'] = {
license = "see context related readme files"
}
+local string = string
local sub, gsub, find, match, gmatch, format, char, byte, rep, lower = string.sub, string.gsub, string.find, string.match, string.gmatch, string.format, string.char, string.byte, string.rep, string.lower
-local lpegmatch = lpeg.match
+local lpegmatch, S, C, Ct = lpeg.match, lpeg.S, lpeg.C, lpeg.Ct
-- some functions may disappear as they are not used anywhere
@@ -63,196 +61,68 @@ if not string.split then
-- this will be overloaded by a faster lpeg variant
- function string:split(pattern)
- if #self > 0 then
- local t = { }
- for s in gmatch(self..pattern,"(.-)"..pattern) do
- t[#t+1] = s
+ function string.split(str,pattern)
+ local t = { }
+ if #str > 0 then
+ local n = 1
+ for s in gmatch(str..pattern,"(.-)"..pattern) do
+ t[n] = s
+ n = n + 1
end
- return t
- else
- return { }
end
+ return t
end
end
-local chr_to_esc = {
- ["%"] = "%%",
- ["."] = "%.",
- ["+"] = "%+", ["-"] = "%-", ["*"] = "%*",
- ["^"] = "%^", ["$"] = "%$",
- ["["] = "%[", ["]"] = "%]",
- ["("] = "%(", [")"] = "%)",
- ["{"] = "%{", ["}"] = "%}"
-}
-
-string.chr_to_esc = chr_to_esc
-
-function string:esc() -- variant 2
- return (gsub(self,"(.)",chr_to_esc))
-end
-
-function string:unquote()
- return (gsub(self,"^([\"\'])(.*)%1$","%2"))
+function string.unquoted(str)
+ return (gsub(str,"^([\"\'])(.*)%1$","%2"))
end
---~ function string:unquote()
---~ if find(self,"^[\'\"]") then
---~ return sub(self,2,-2)
---~ else
---~ return self
---~ end
---~ end
-function string:quote() -- we could use format("%q")
- return format("%q",self)
+function string.quoted(str)
+ return format("%q",str) -- always "
end
-function string:count(pattern) -- variant 3
+function string.count(str,pattern) -- variant 3
local n = 0
- for _ in gmatch(self,pattern) do
+ for _ in gmatch(str,pattern) do -- not for utf
n = n + 1
end
return n
end
-function string:limit(n,sentinel)
- if #self > n then
- sentinel = sentinel or " ..."
- return sub(self,1,(n-#sentinel)) .. sentinel
- else
- return self
- end
-end
-
---~ function string:strip() -- the .- is quite efficient
---~ -- return match(self,"^%s*(.-)%s*$") or ""
---~ -- return match(self,'^%s*(.*%S)') or '' -- posted on lua list
---~ return find(s,'^%s*$') and '' or match(s,'^%s*(.*%S)')
---~ end
-
-do -- roberto's variant:
- local space = lpeg.S(" \t\v\n")
- local nospace = 1 - space
- local stripper = space^0 * lpeg.C((space^0 * nospace^1)^0)
- function string.strip(str)
- return lpegmatch(stripper,str) or ""
- end
-end
-
-function string:is_empty()
- return not find(self,"%S")
-end
-
-function string:enhance(pattern,action)
- local ok, n = true, 0
- while ok do
- ok = false
- self = gsub(self,pattern, function(...)
- ok, n = true, n + 1
- return action(...)
- end)
- end
- return self, n
-end
-
-local chr_to_hex, hex_to_chr = { }, { }
-
-for i=0,255 do
- local c, h = char(i), format("%02X",i)
- chr_to_hex[c], hex_to_chr[h] = h, c
-end
-
-function string:to_hex()
- return (gsub(self or "","(.)",chr_to_hex))
-end
-
-function string:from_hex()
- return (gsub(self or "","(..)",hex_to_chr))
-end
-
-if not string.characters then
-
- local function nextchar(str, index)
- index = index + 1
- return (index <= #str) and index or nil, sub(str,index,index)
- end
- function string:characters()
- return nextchar, self, 0
- end
- local function nextbyte(str, index)
- index = index + 1
- return (index <= #str) and index or nil, byte(sub(str,index,index))
- end
- function string:bytes()
- return nextbyte, self, 0
- end
-
-end
-
--- we can use format for this (neg n)
-
-function string:rpadd(n,chr)
- local m = n-#self
- if m > 0 then
- return self .. rep(chr or " ",m)
+function string.limit(str,n,sentinel) -- not utf proof
+ if #str > n then
+ sentinel = sentinel or "..."
+ return sub(str,1,(n-#sentinel)) .. sentinel
else
- return self
- end
-end
-
-function string:lpadd(n,chr)
- local m = n-#self
- if m > 0 then
- return rep(chr or " ",m) .. self
- else
- return self
+ return str
end
end
-string.padd = string.rpadd
+local space = S(" \t\v\n")
+local nospace = 1 - space
+local stripper = space^0 * C((space^0 * nospace^1)^0) -- roberto's code
-function is_number(str) -- tonumber
- return find(str,"^[%-%+]?[%d]-%.?[%d+]$") == 1
+function string.strip(str)
+ return lpegmatch(stripper,str) or ""
end
---~ print(is_number("1"))
---~ print(is_number("1.1"))
---~ print(is_number(".1"))
---~ print(is_number("-0.1"))
---~ print(is_number("+0.1"))
---~ print(is_number("-.1"))
---~ print(is_number("+.1"))
-
-function string:split_settings() -- no {} handling, see l-aux for lpeg variant
- if find(self,"=") then
- local t = { }
- for k,v in gmatch(self,"(%a+)=([^%,]*)") do
- t[k] = v
- end
- return t
- else
- return nil
- end
+function string.is_empty(str)
+ return not find(str,"%S")
end
local patterns_escapes = {
- ["-"] = "%-",
- ["."] = "%.",
- ["+"] = "%+",
- ["*"] = "%*",
["%"] = "%%",
- ["("] = "%)",
- [")"] = "%)",
- ["["] = "%[",
- ["]"] = "%]",
+ ["."] = "%.",
+ ["+"] = "%+", ["-"] = "%-", ["*"] = "%*",
+ ["["] = "%[", ["]"] = "%]",
+ ["("] = "%(", [")"] = "%)",
+ -- ["{"] = "%{", ["}"] = "%}"
+ -- ["^"] = "%^", ["$"] = "%$",
}
-function string:pattesc()
- return (gsub(self,".",patterns_escapes))
-end
-
local simple_escapes = {
["-"] = "%-",
["."] = "%.",
@@ -260,80 +130,30 @@ local simple_escapes = {
["*"] = ".*",
}
-function string:simpleesc()
- return (gsub(self,".",simple_escapes))
-end
-
-function string:tohash()
- local t = { }
- for s in gmatch(self,"([^, ]+)") do -- lpeg
- t[s] = true
- end
- return t
-end
-
-local pattern = lpeg.Ct(lpeg.C(1)^0)
-
-function string:totable()
- return lpegmatch(pattern,self)
+function string.escapedpattern(str,simple)
+ return (gsub(str,".",simple and simple_escapes or patterns_escapes))
end
---~ local t = {
---~ "1234567123456712345671234567",
---~ "a\tb\tc",
---~ "aa\tbb\tcc",
---~ "aaa\tbbb\tccc",
---~ "aaaa\tbbbb\tcccc",
---~ "aaaaa\tbbbbb\tccccc",
---~ "aaaaaa\tbbbbbb\tcccccc",
---~ }
---~ for k,v do
---~ print(string.tabtospace(t[k]))
---~ end
-
-function string.tabtospace(str,tab)
- -- we don't handle embedded newlines
- while true do
- local s = find(str,"\t")
- if s then
- if not tab then tab = 7 end -- only when found
- local d = tab-(s-1) % tab
- if d > 0 then
- str = gsub(str,"\t",rep(" ",d),1)
- else
- str = gsub(str,"\t","",1)
- end
+function string.topattern(str,lowercase,strict)
+ if str == "" then
+ return ".*"
+ else
+ str = gsub(str,".",simple_escapes)
+ if lowercase then
+ str = lower(str)
+ end
+ if strict then
+ return "^" .. str .. "$"
else
- break
+ return str
end
end
- return str
end
-function string:compactlong() -- strips newlines and leading spaces
- self = gsub(self,"[\n\r]+ *","")
- self = gsub(self,"^ *","")
- return self
-end
-
-function string:striplong() -- strips newlines and leading spaces
- self = gsub(self,"^%s*","")
- self = gsub(self,"[\n\r]+ *","\n")
- return self
-end
+-- obsolete names:
-function string:topattern(lowercase,strict)
- if lowercase then
- self = lower(self)
- end
- self = gsub(self,".",simple_escapes)
- if self == "" then
- self = ".*"
- elseif strict then
- self = "^" .. self .. "$"
- end
- return self
-end
+string.quote = string.quoted
+string.unquote = string.unquoted
end -- of closure
@@ -350,28 +170,75 @@ if not modules then modules = { } end modules ['l-lpeg'] = {
local lpeg = require("lpeg")
+local type = type
+
+-- Beware, we predefine a bunch of patterns here and one reason for doing so
+-- is that we get consistent behaviour in some of the visualizers.
+
lpeg.patterns = lpeg.patterns or { } -- so that we can share
local patterns = lpeg.patterns
-local P, R, S, Ct, C, Cs, Cc, V = lpeg.P, lpeg.R, lpeg.S, lpeg.Ct, lpeg.C, lpeg.Cs, lpeg.Cc, lpeg.V
-local match = lpeg.match
+local P, R, S, V, match = lpeg.P, lpeg.R, lpeg.S, lpeg.V, lpeg.match
+local Ct, C, Cs, Cc = lpeg.Ct, lpeg.C, lpeg.Cs, lpeg.Cc
+local lpegtype = lpeg.type
+
+local utfcharacters = string.utfcharacters
+local utfgmatch = unicode and unicode.utf8.gmatch
+
+local anything = P(1)
+local endofstring = P(-1)
+local alwaysmatched = P(true)
+
+patterns.anything = anything
+patterns.endofstring = endofstring
+patterns.beginofstring = alwaysmatched
+patterns.alwaysmatched = alwaysmatched
local digit, sign = R('09'), S('+-')
local cr, lf, crlf = P("\r"), P("\n"), P("\r\n")
-local utf8byte = R("\128\191")
+local newline = crlf + cr + lf
+local escaped = P("\\") * anything
+local squote = P("'")
+local dquote = P('"')
+local space = P(" ")
+
+local utfbom_32_be = P('\000\000\254\255')
+local utfbom_32_le = P('\255\254\000\000')
+local utfbom_16_be = P('\255\254')
+local utfbom_16_le = P('\254\255')
+local utfbom_8 = P('\239\187\191')
+local utfbom = utfbom_32_be + utfbom_32_le
+ + utfbom_16_be + utfbom_16_le
+ + utfbom_8
+local utftype = utfbom_32_be / "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.utf8byte = utf8byte
patterns.utf8one = R("\000\127")
-patterns.utf8two = R("\194\223") * utf8byte
-patterns.utf8three = R("\224\239") * utf8byte * utf8byte
-patterns.utf8four = R("\240\244") * utf8byte * utf8byte * utf8byte
+patterns.utf8two = R("\194\223") * utf8next
+patterns.utf8three = R("\224\239") * utf8next * utf8next
+patterns.utf8four = R("\240\244") * utf8next * utf8next * utf8next
+patterns.utfbom = utfbom
+patterns.utftype = utftype
+
+local utf8char = patterns.utf8one + patterns.utf8two + patterns.utf8three + patterns.utf8four
+local validutf8char = utf8char^0 * endofstring * Cc(true) + Cc(false)
+
+patterns.utf8 = utf8char
+patterns.utf8char = utf8char
+patterns.validutf8 = validutf8char
+patterns.validutf8char = validutf8char
patterns.digit = digit
patterns.sign = sign
patterns.cardinal = sign^0 * digit^1
patterns.integer = sign^0 * digit^1
patterns.float = sign^0 * digit^0 * P('.') * digit^1
+patterns.cfloat = sign^0 * digit^0 * P(',') * digit^1
patterns.number = patterns.float + patterns.integer
+patterns.cnumber = patterns.cfloat + patterns.integer
patterns.oct = P("0") * R("07")^1
patterns.octal = patterns.oct
patterns.HEX = P("0x") * R("09","AF")^1
@@ -380,16 +247,42 @@ patterns.hexadecimal = P("0x") * R("09","AF","af")^1
patterns.lowercase = R("az")
patterns.uppercase = R("AZ")
patterns.letter = patterns.lowercase + patterns.uppercase
-patterns.space = S(" ")
+patterns.space = space
+patterns.tab = P("\t")
+patterns.spaceortab = patterns.space + patterns.tab
patterns.eol = S("\n\r")
patterns.spacer = S(" \t\f\v") -- + string.char(0xc2, 0xa0) if we want utf (cf mail roberto)
-patterns.newline = crlf + cr + lf
-patterns.nonspace = 1 - patterns.space
+patterns.newline = newline
+patterns.emptyline = newline^1
patterns.nonspacer = 1 - patterns.spacer
patterns.whitespace = patterns.eol + patterns.spacer
patterns.nonwhitespace = 1 - patterns.whitespace
-patterns.utf8 = patterns.utf8one + patterns.utf8two + patterns.utf8three + patterns.utf8four
-patterns.utfbom = P('\000\000\254\255') + P('\255\254\000\000') + P('\255\254') + P('\254\255') + P('\239\187\191')
+patterns.equal = P("=")
+patterns.comma = P(",")
+patterns.commaspacer = P(",") * patterns.spacer^0
+patterns.period = P(".")
+patterns.colon = P(":")
+patterns.semicolon = P(";")
+patterns.underscore = P("_")
+patterns.escaped = escaped
+patterns.squote = squote
+patterns.dquote = dquote
+patterns.nosquote = (escaped + (1-squote))^0
+patterns.nodquote = (escaped + (1-dquote))^0
+patterns.unsingle = (squote/"") * patterns.nosquote * (squote/"")
+patterns.undouble = (dquote/"") * patterns.nodquote * (dquote/"")
+patterns.unquoted = patterns.undouble + patterns.unsingle -- more often undouble
+patterns.unspacer = ((patterns.spacer^1)/"")^0
+
+patterns.somecontent = (anything - newline - space)^1 -- (utf8char - newline - space)^1
+patterns.beginline = #(1-newline)
+
+local unquoted = Cs(patterns.unquoted * endofstring) -- not C
+
+function string.unquoted(str)
+ return match(unquoted,str) or str
+end
+
function lpeg.anywhere(pattern) --slightly adapted from website
return P { P(pattern) + 1 * V(1) } -- why so complex?
@@ -399,36 +292,18 @@ function lpeg.splitter(pattern, action)
return (((1-P(pattern))^1)/action+1)^0
end
-local spacing = patterns.spacer^0 * patterns.newline -- sort of strip
-local empty = spacing * Cc("")
-local nonempty = Cs((1-spacing)^1) * spacing^-1
-local content = (empty + nonempty)^1
-
-local capture = Ct(content^0)
-
-function string:splitlines()
- return match(capture,self)
-end
-
-patterns.textline = content
-
---~ local p = lpeg.splitat("->",false) print(match(p,"oeps->what->more")) -- oeps what more
---~ local p = lpeg.splitat("->",true) print(match(p,"oeps->what->more")) -- oeps what->more
---~ local p = lpeg.splitat("->",false) print(match(p,"oeps")) -- oeps
---~ local p = lpeg.splitat("->",true) print(match(p,"oeps")) -- oeps
-
local splitters_s, splitters_m = { }, { }
local function splitat(separator,single)
local splitter = (single and splitters_s[separator]) or splitters_m[separator]
if not splitter then
separator = P(separator)
+ local other = C((1 - separator)^0)
if single then
- local other, any = C((1 - separator)^0), P(1)
+ local any = anything
splitter = other * (separator * C(any^0) + "") -- ?
splitters_s[separator] = splitter
else
- local other = C((1 - separator)^0)
splitter = other * (separator * other)^0
splitters_m[separator] = splitter
end
@@ -438,6 +313,7 @@ end
lpeg.splitat = splitat
+
local cache = { }
function lpeg.split(separator,str)
@@ -449,16 +325,39 @@ function lpeg.split(separator,str)
return match(c,str)
end
-function string:split(separator)
+function string.split(str,separator)
local c = cache[separator]
if not c then
c = Ct(splitat(separator))
cache[separator] = c
end
- return match(c,self)
+ return match(c,str)
+end
+
+local spacing = patterns.spacer^0 * newline -- sort of strip
+local empty = spacing * Cc("")
+local nonempty = Cs((1-spacing)^1) * spacing^-1
+local content = (empty + nonempty)^1
+
+patterns.textline = content
+
+
+local linesplitter = Ct(splitat(newline))
+
+patterns.linesplitter = linesplitter
+
+function string.splitlines(str)
+ return match(linesplitter,str)
+end
+
+local utflinesplitter = utfbom^-1 * Ct(splitat(newline))
+
+patterns.utflinesplitter = utflinesplitter
+
+function string.utfsplitlines(str)
+ return match(utflinesplitter,str)
end
-lpeg.splitters = cache
local cache = { }
@@ -466,37 +365,24 @@ function lpeg.checkedsplit(separator,str)
local c = cache[separator]
if not c then
separator = P(separator)
- local other = C((1 - separator)^0)
+ local other = C((1 - separator)^1)
c = Ct(separator^0 * other * (separator^1 * other)^0)
cache[separator] = c
end
return match(c,str)
end
-function string:checkedsplit(separator)
+function string.checkedsplit(str,separator)
local c = cache[separator]
if not c then
separator = P(separator)
- local other = C((1 - separator)^0)
+ local other = C((1 - separator)^1)
c = Ct(separator^0 * other * (separator^1 * other)^0)
cache[separator] = c
end
- return match(c,self)
+ return match(c,str)
end
---~ function lpeg.append(list,pp)
---~ local p = pp
---~ for l=1,#list do
---~ if p then
---~ p = p + P(list[l])
---~ else
---~ p = P(list[l])
---~ end
---~ end
---~ return p
---~ end
-
---~ from roberto's site:
local f1 = string.byte
@@ -504,7 +390,273 @@ local function f2(s) local c1, c2 = f1(s,1,2) return c1 * 64 + c2
local function f3(s) local c1, c2, c3 = f1(s,1,3) return (c1 * 64 + c2) * 64 + c3 - 925824 end
local function f4(s) local c1, c2, c3, c4 = f1(s,1,4) return ((c1 * 64 + c2) * 64 + c3) * 64 + c4 - 63447168 end
-patterns.utf8byte = patterns.utf8one/f1 + patterns.utf8two/f2 + patterns.utf8three/f3 + patterns.utf8four/f4
+local utf8byte = patterns.utf8one/f1 + patterns.utf8two/f2 + patterns.utf8three/f3 + patterns.utf8four/f4
+
+patterns.utf8byte = utf8byte
+
+
+
+local cache = { }
+
+function lpeg.stripper(str)
+ if type(str) == "string" then
+ local s = cache[str]
+ if not s then
+ s = Cs(((S(str)^1)/"" + 1)^0)
+ cache[str] = s
+ end
+ return s
+ else
+ return Cs(((str^1)/"" + 1)^0)
+ end
+end
+
+local cache = { }
+
+function lpeg.keeper(str)
+ if type(str) == "string" then
+ local s = cache[str]
+ if not s then
+ s = Cs((((1-S(str))^1)/"" + 1)^0)
+ cache[str] = s
+ end
+ return s
+ else
+ return Cs((((1-str)^1)/"" + 1)^0)
+ end
+end
+
+function lpeg.frontstripper(str) -- or pattern (yet undocumented)
+ return (P(str) + P(true)) * Cs(P(1)^0)
+end
+
+function lpeg.endstripper(str) -- or pattern (yet undocumented)
+ return Cs((1 - P(str) * P(-1))^0)
+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(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
+ else
+ two = two or ""
+ return Cs((P(one)/two + 1)^0)
+ end
+end
+
+local splitters_f, splitters_s = { }, { }
+
+function lpeg.firstofsplit(separator) -- always return value
+ local splitter = splitters_f[separator]
+ if not splitter then
+ separator = P(separator)
+ splitter = C((1 - separator)^0)
+ splitters_f[separator] = splitter
+ end
+ return splitter
+end
+
+function lpeg.secondofsplit(separator) -- nil if not split
+ local splitter = splitters_s[separator]
+ if not splitter then
+ separator = P(separator)
+ splitter = (1 - separator)^0 * separator * C(anything^0)
+ splitters_s[separator] = splitter
+ end
+ return splitter
+end
+
+function lpeg.balancer(left,right)
+ left, right = P(left), P(right)
+ return P { left * ((1 - left - right) + V(1))^0 * right }
+end
+
+
+
+local nany = utf8char/""
+
+function lpeg.counter(pattern)
+ pattern = Cs((P(pattern)/" " + nany)^0)
+ return function(str)
+ return #match(pattern,str)
+ end
+end
+
+if utfgmatch then
+
+ function lpeg.count(str,what) -- replaces string.count
+ if type(what) == "string" then
+ local n = 0
+ for _ in utfgmatch(str,what) do
+ n = n + 1
+ end
+ return n
+ else -- 4 times slower but still faster than / function
+ return #match(Cs((P(what)/" " + nany)^0),str)
+ end
+ end
+
+else
+
+ local cache = { }
+
+ function lpeg.count(str,what) -- replaces string.count
+ if type(what) == "string" then
+ local p = cache[what]
+ if not p then
+ p = Cs((P(what)/" " + nany)^0)
+ cache[p] = p
+ end
+ return #match(p,str)
+ else -- 4 times slower but still faster than / function
+ return #match(Cs((P(what)/" " + nany)^0),str)
+ end
+ end
+
+end
+
+local patterns_escapes = { -- also defines in l-string
+ ["%"] = "%%",
+ ["."] = "%.",
+ ["+"] = "%+", ["-"] = "%-", ["*"] = "%*",
+ ["["] = "%[", ["]"] = "%]",
+ ["("] = "%)", [")"] = "%)",
+ -- ["{"] = "%{", ["}"] = "%}"
+ -- ["^"] = "%^", ["$"] = "%$",
+}
+
+local simple_escapes = { -- also defines in l-string
+ ["-"] = "%-",
+ ["."] = "%.",
+ ["?"] = ".",
+ ["*"] = ".*",
+}
+
+local p = Cs((S("-.+*%()[]") / patterns_escapes + anything)^0)
+local s = Cs((S("-.+*%()[]") / simple_escapes + anything)^0)
+
+function string.escapedpattern(str,simple)
+ return match(simple and s or p,str)
+end
+
+-- utf extensies
+
+lpeg.UP = lpeg.P
+
+if utfcharacters then
+
+ function lpeg.US(str)
+ local p
+ for uc in utfcharacters(str) do
+ if p then
+ p = p + P(uc)
+ else
+ p = P(uc)
+ end
+ end
+ return p
+ end
+
+
+elseif utfgmatch then
+
+ function lpeg.US(str)
+ local p
+ for uc in utfgmatch(str,".") do
+ if p then
+ p = p + P(uc)
+ else
+ p = P(uc)
+ end
+ end
+ return p
+ end
+
+else
+
+ function lpeg.US(str)
+ local p
+ local f = function(uc)
+ if p then
+ p = p + P(uc)
+ else
+ p = P(uc)
+ end
+ end
+ match((utf8char/f)^0,str)
+ return p
+ end
+
+end
+
+local range = Cs(utf8byte) * (Cs(utf8byte) + Cc(false))
+
+local utfchar = unicode and unicode.utf8 and unicode.utf8.char
+
+function lpeg.UR(str,more)
+ local first, last
+ if type(str) == "number" then
+ first = str
+ last = more or first
+ else
+ first, last = match(range,str)
+ if not last then
+ return P(str)
+ end
+ end
+ if first == last then
+ return P(str)
+ elseif utfchar and last - first < 8 then -- a somewhat arbitrary criterium
+ local p
+ for i=first,last do
+ if p then
+ p = p + P(utfchar(i))
+ else
+ p = P(utfchar(i))
+ end
+ end
+ return p -- nil when invalid range
+ else
+ local f = function(b)
+ return b >= first and b <= last
+ end
+ return utf8byte / f -- nil when invalid range
+ end
+end
+
+
+
+function lpeg.oneof(list,...) -- lpeg.oneof("elseif","else","if","then")
+ if type(list) ~= "table" then
+ list = { list, ... }
+ end
+ -- sort(list) -- longest match first
+ local p = P(list[1])
+ for l=2,#list do
+ p = p + P(list[l])
+ end
+ return p
+end
+
+function lpeg.is_lpeg(p)
+ return p and lpegtype(p) == "pattern"
+end
+
end -- of closure
@@ -519,12 +671,10 @@ if not modules then modules = { } end modules ['l-table'] = {
license = "see context related readme files"
}
-table.join = table.concat
-
+local type, next, tostring, tonumber, ipairs, table, string = type, next, tostring, tonumber, ipairs, table, string
local concat, sort, insert, remove = table.concat, table.sort, table.insert, table.remove
local format, find, gsub, lower, dump, match = string.format, string.find, string.gsub, string.lower, string.dump, string.match
local getmetatable, setmetatable = getmetatable, setmetatable
-local type, next, tostring, tonumber, ipairs = type, next, tostring, tonumber, ipairs
-- Starting with version 5.2 Lua no longer provide ipairs, which makes
-- sense. As we already used the for loop and # in most places the
@@ -576,50 +726,56 @@ end
-- extra functions, some might go (when not used)
function table.strip(tab)
- local lst = { }
+ local lst, l = { }, 0
for i=1,#tab do
local s = gsub(tab[i],"^%s*(.-)%s*$","%1")
if s == "" then
-- skip this one
else
- lst[#lst+1] = s
+ l = l + 1
+ lst[l] = s
end
end
return lst
end
function table.keys(t)
- local k = { }
+ local keys, k = { }, 0
for key, _ in next, t do
- k[#k+1] = key
+ k = k + 1
+ keys[k] = key
end
- return k
+ return keys
end
local function compare(a,b)
- return (tostring(a) < tostring(b))
+ local ta, tb = type(a), type(b) -- needed, else 11 < 2
+ if ta == tb then
+ return a < b
+ else
+ return tostring(a) < tostring(b)
+ end
end
local function sortedkeys(tab)
- local srt, kind = { }, 0 -- 0=unknown 1=string, 2=number 3=mixed
+ local srt, category, s = { }, 0, 0 -- 0=unknown 1=string, 2=number 3=mixed
for key,_ in next, tab do
- srt[#srt+1] = key
- if kind == 3 then
+ s = s + 1
+ srt[s] = key
+ if category == 3 then
-- no further check
else
local tkey = type(key)
if tkey == "string" then
- -- if kind == 2 then kind = 3 else kind = 1 end
- kind = (kind == 2 and 3) or 1
+ category = (category == 2 and 3) or 1
elseif tkey == "number" then
- -- if kind == 1 then kind = 3 else kind = 2 end
- kind = (kind == 1 and 3) or 2
+ category = (category == 1 and 3) or 2
else
- kind = 3
+ category = 3
end
end
end
- if kind == 0 or kind == 3 then
+ if category == 0 or category == 3 then
sort(srt,compare)
else
sort(srt)
@@ -628,9 +784,12 @@ local function sortedkeys(tab)
end
local function sortedhashkeys(tab) -- fast one
- local srt = { }
+ local srt, s = { }, 0
for key,_ in next, tab do
- srt[#srt+1] = key
+ if key then
+ s= s + 1
+ srt[s] = key
+ end
end
sort(srt)
return srt
@@ -639,34 +798,50 @@ end
table.sortedkeys = sortedkeys
table.sortedhashkeys = sortedhashkeys
-function table.sortedhash(t)
- local s = sortedhashkeys(t) -- maybe just sortedkeys
- local n = 0
- local function kv(s)
- n = n + 1
- local k = s[n]
- return k, t[k]
+local function nothing() end
+
+local function sortedhash(t)
+ if t then
+ local n, s = 0, sortedkeys(t) -- the robust one
+ local function kv(s)
+ n = n + 1
+ local k = s[n]
+ return k, t[k]
+ end
+ return kv, s
+ else
+ return nothing
end
- return kv, s
end
-table.sortedpairs = table.sortedhash
+table.sortedhash = sortedhash
+table.sortedpairs = sortedhash
function table.append(t, list)
- for _,v in next, list do
- insert(t,v)
+ local n = #t
+ for i=1,#list do
+ n = n + 1
+ t[n] = list[i]
end
+ return t
end
function table.prepend(t, list)
- for k,v in next, list do
- insert(t,k,v)
+ local nl = #list
+ local nt = nl + #t
+ for i=#t,1,-1 do
+ t[nt] = t[i]
+ nt = nt - 1
+ end
+ for i=1,#list do
+ t[i] = list[i]
end
+ return t
end
function table.merge(t, ...) -- first one is target
- t = t or {}
- local lst = {...}
+ t = t or { }
+ local lst = { ... }
for i=1,#lst do
for k, v in next, lst[i] do
t[k] = v
@@ -676,7 +851,7 @@ function table.merge(t, ...) -- first one is target
end
function table.merged(...)
- local tmp, lst = { }, {...}
+ local tmp, lst = { }, { ... }
for i=1,#lst do
for k, v in next, lst[i] do
tmp[k] = v
@@ -686,41 +861,45 @@ function table.merged(...)
end
function table.imerge(t, ...)
- local lst = {...}
+ local lst, nt = { ... }, #t
for i=1,#lst do
local nst = lst[i]
for j=1,#nst do
- t[#t+1] = nst[j]
+ nt = nt + 1
+ t[nt] = nst[j]
end
end
return t
end
function table.imerged(...)
- local tmp, lst = { }, {...}
+ local tmp, ntmp, lst = { }, 0, {...}
for i=1,#lst do
local nst = lst[i]
for j=1,#nst do
- tmp[#tmp+1] = nst[j]
+ ntmp = ntmp + 1
+ tmp[ntmp] = nst[j]
end
end
return tmp
end
-local function fastcopy(old) -- fast one
+local function fastcopy(old,metatabletoo) -- fast one
if old then
local new = { }
for k,v in next, old do
if type(v) == "table" then
- new[k] = fastcopy(v) -- was just table.copy
+ new[k] = fastcopy(v,metatabletoo) -- was just table.copy
else
new[k] = v
end
end
- -- optional second arg
- local mt = getmetatable(old)
- if mt then
- setmetatable(new,mt)
+ if metatabletoo then
+ -- optional second arg
+ local mt = getmetatable(old)
+ if mt then
+ setmetatable(new,mt)
+ end
end
return new
else
@@ -728,6 +907,8 @@ local function fastcopy(old) -- fast one
end
end
+-- todo : copy without metatable
+
local function copy(t, tables) -- taken from lua wiki, slightly adapted
tables = tables or { }
local tcopy = {}
@@ -760,33 +941,14 @@ end
table.fastcopy = fastcopy
table.copy = copy
--- roughly: copy-loop : unpack : sub == 0.9 : 0.4 : 0.45 (so in critical apps, use unpack)
-
-function table.sub(t,i,j)
- return { unpack(t,i,j) }
-end
-
-function table.replace(a,b)
- for k,v in next, b do
- a[k] = v
+function table.derive(parent)
+ local child = { }
+ if parent then
+ setmetatable(child,{ __index = parent })
end
+ return child
end
--- slower than #t on indexed tables (#t only returns the size of the numerically indexed slice)
-
-function table.is_empty(t) -- obolete, use inline code instead
- return not t or not next(t)
-end
-
-function table.one_entry(t) -- obolete, use inline code instead
- local n = next(t)
- return n and not next(t,n)
-end
-
---~ function table.starts_at(t) -- obsolete, not nice anyway
---~ return ipairs(t,1)(t,0)
---~ end
-
function table.tohash(t,value)
local h = { }
if t then
@@ -799,27 +961,19 @@ function table.tohash(t,value)
end
function table.fromhash(t)
- local h = { }
+ local hsh, h = { }, 0
for k, v in next, t do -- no ipairs here
- if v then h[#h+1] = k end
+ if v then
+ h = h + 1
+ hsh[h] = k
+ end
end
- return h
+ return hsh
end
---~ print(table.serialize(t), "\n")
---~ print(table.serialize(t,"name"), "\n")
---~ print(table.serialize(t,false), "\n")
---~ print(table.serialize(t,true), "\n")
---~ print(table.serialize(t,"name",true), "\n")
---~ print(table.serialize(t,"name",true,true), "\n")
-
-table.serialize_functions = true
-table.serialize_compact = true
-table.serialize_inline = true
-
local noquotes, hexify, handle, reduce, compact, inline, functions
-local reserved = table.tohash { -- intercept a language flaw, no reserved words as key
+local reserved = table.tohash { -- intercept a language inconvenience: no reserved words as key
'and', 'break', 'do', 'else', 'elseif', 'end', 'false', 'for', 'function', 'if',
'in', 'local', 'nil', 'not', 'or', 'repeat', 'return', 'then', 'true', 'until', 'while',
}
@@ -831,20 +985,23 @@ local function simple_table(t)
n = n + 1
end
if n == #t then
- local tt = { }
+ local tt, nt = { }, 0
for i=1,#t do
local v = t[i]
local tv = type(v)
if tv == "number" then
+ nt = nt + 1
if hexify then
- tt[#tt+1] = format("0x%04X",v)
+ tt[nt] = format("0x%04X",v)
else
- tt[#tt+1] = tostring(v) -- tostring not needed
+ tt[nt] = tostring(v) -- tostring not needed
end
elseif tv == "boolean" then
- tt[#tt+1] = tostring(v)
+ nt = nt + 1
+ tt[nt] = tostring(v)
elseif tv == "string" then
- tt[#tt+1] = format("%q",v)
+ nt = nt + 1
+ tt[nt] = format("%q",v)
else
tt = nil
break
@@ -870,21 +1027,25 @@ local function do_serialize(root,name,depth,level,indexed)
depth = depth .. " "
if indexed then
handle(format("%s{",depth))
- elseif name then
- --~ handle(format("%s%s={",depth,key(name)))
- if type(name) == "number" then -- or find(k,"^%d+$") then
+ else
+ local tn = type(name)
+ if tn == "number" then -- or find(k,"^%d+$") then
if hexify then
handle(format("%s[0x%04X]={",depth,name))
else
handle(format("%s[%s]={",depth,name))
end
- elseif noquotes and not reserved[name] and find(name,"^%a[%w%_]*$") then
- handle(format("%s%s={",depth,name))
+ elseif tn == "string" then
+ if noquotes and not reserved[name] and find(name,"^%a[%w%_]*$") then
+ handle(format("%s%s={",depth,name))
+ else
+ handle(format("%s[%q]={",depth,name))
+ end
+ elseif tn == "boolean" then
+ handle(format("%s[%s]={",depth,tostring(name)))
else
- handle(format("%s[%q]={",depth,name))
+ handle(format("%s{",depth))
end
- else
- handle(format("%s{",depth))
end
end
-- we could check for k (index) being number (cardinal)
@@ -901,11 +1062,9 @@ local function do_serialize(root,name,depth,level,indexed)
for i=1,#sk do
local k = sk[i]
local v = root[k]
- --~ if v == root then
- -- circular
- --~ else
- local t = type(v)
- if compact and first and type(k) == "number" and k >= first and k <= last then
+ -- circular
+ local t, tk = type(v), type(k)
+ if compact and first and tk == "number" and k >= first and k <= last then
if t == "number" then
if hexify then
handle(format("%s 0x%04X,",depth,v))
@@ -947,17 +1106,18 @@ local function do_serialize(root,name,depth,level,indexed)
handle(format("%s __p__=nil,",depth))
end
elseif t == "number" then
- --~ if hexify then
- --~ handle(format("%s %s=0x%04X,",depth,key(k),v))
- --~ else
- --~ handle(format("%s %s=%s,",depth,key(k),v)) -- %.99g
- --~ end
- if type(k) == "number" then -- or find(k,"^%d+$") then
+ if tk == "number" then -- or find(k,"^%d+$") then
if hexify then
handle(format("%s [0x%04X]=0x%04X,",depth,k,v))
else
handle(format("%s [%s]=%s,",depth,k,v)) -- %.99g
end
+ elseif tk == "boolean" then
+ if hexify then
+ handle(format("%s [%s]=0x%04X,",depth,tostring(k),v))
+ else
+ handle(format("%s [%s]=%s,",depth,tostring(k),v)) -- %.99g
+ end
elseif noquotes and not reserved[k] and find(k,"^%a[%w%_]*$") then
if hexify then
handle(format("%s %s=0x%04X,",depth,k,v))
@@ -973,26 +1133,28 @@ local function do_serialize(root,name,depth,level,indexed)
end
elseif t == "string" then
if reduce and tonumber(v) then
- --~ handle(format("%s %s=%s,",depth,key(k),v))
- if type(k) == "number" then -- or find(k,"^%d+$") then
+ if tk == "number" then -- or find(k,"^%d+$") then
if hexify then
handle(format("%s [0x%04X]=%s,",depth,k,v))
else
handle(format("%s [%s]=%s,",depth,k,v))
end
+ elseif tk == "boolean" then
+ handle(format("%s [%s]=%s,",depth,tostring(k),v))
elseif noquotes and not reserved[k] and find(k,"^%a[%w%_]*$") then
handle(format("%s %s=%s,",depth,k,v))
else
handle(format("%s [%q]=%s,",depth,k,v))
end
else
- --~ handle(format("%s %s=%q,",depth,key(k),v))
- if type(k) == "number" then -- or find(k,"^%d+$") then
+ if tk == "number" then -- or find(k,"^%d+$") then
if hexify then
handle(format("%s [0x%04X]=%q,",depth,k,v))
else
handle(format("%s [%s]=%q,",depth,k,v))
end
+ elseif tk == "boolean" then
+ handle(format("%s [%s]=%q,",depth,tostring(k),v))
elseif noquotes and not reserved[k] and find(k,"^%a[%w%_]*$") then
handle(format("%s %s=%q,",depth,k,v))
else
@@ -1001,13 +1163,14 @@ local function do_serialize(root,name,depth,level,indexed)
end
elseif t == "table" then
if not next(v) then
- --~ handle(format("%s %s={},",depth,key(k)))
- if type(k) == "number" then -- or find(k,"^%d+$") then
+ if tk == "number" then -- or find(k,"^%d+$") then
if hexify then
handle(format("%s [0x%04X]={},",depth,k))
else
handle(format("%s [%s]={},",depth,k))
end
+ elseif tk == "boolean" then
+ handle(format("%s [%s]={},",depth,tostring(k)))
elseif noquotes and not reserved[k] and find(k,"^%a[%w%_]*$") then
handle(format("%s %s={},",depth,k))
else
@@ -1016,13 +1179,14 @@ local function do_serialize(root,name,depth,level,indexed)
elseif inline then
local st = simple_table(v)
if st then
- --~ handle(format("%s %s={ %s },",depth,key(k),concat(st,", ")))
- if type(k) == "number" then -- or find(k,"^%d+$") then
+ if tk == "number" then -- or find(k,"^%d+$") then
if hexify then
handle(format("%s [0x%04X]={ %s },",depth,k,concat(st,", ")))
else
handle(format("%s [%s]={ %s },",depth,k,concat(st,", ")))
end
+ elseif tk == "boolean" then -- or find(k,"^%d+$") then
+ handle(format("%s [%s]={ %s },",depth,tostring(k),concat(st,", ")))
elseif noquotes and not reserved[k] and find(k,"^%a[%w%_]*$") then
handle(format("%s %s={ %s },",depth,k,concat(st,", ")))
else
@@ -1035,13 +1199,14 @@ local function do_serialize(root,name,depth,level,indexed)
do_serialize(v,k,depth,level+1)
end
elseif t == "boolean" then
- --~ handle(format("%s %s=%s,",depth,key(k),tostring(v)))
- if type(k) == "number" then -- or find(k,"^%d+$") then
+ if tk == "number" then -- or find(k,"^%d+$") then
if hexify then
handle(format("%s [0x%04X]=%s,",depth,k,tostring(v)))
else
handle(format("%s [%s]=%s,",depth,k,tostring(v)))
end
+ elseif tk == "boolean" then -- or find(k,"^%d+$") then
+ handle(format("%s [%s]=%s,",depth,tostring(k),tostring(v)))
elseif noquotes and not reserved[k] and find(k,"^%a[%w%_]*$") then
handle(format("%s %s=%s,",depth,k,tostring(v)))
else
@@ -1049,35 +1214,37 @@ local function do_serialize(root,name,depth,level,indexed)
end
elseif t == "function" then
if functions then
- --~ handle(format('%s %s=loadstring(%q),',depth,key(k),dump(v)))
- if type(k) == "number" then -- or find(k,"^%d+$") then
+ if tk == "number" then -- or find(k,"^%d+$") then
if hexify then
handle(format("%s [0x%04X]=loadstring(%q),",depth,k,dump(v)))
else
handle(format("%s [%s]=loadstring(%q),",depth,k,dump(v)))
end
+ elseif tk == "boolean" then
+ handle(format("%s [%s]=loadstring(%q),",depth,tostring(k),dump(v)))
elseif noquotes and not reserved[k] and find(k,"^%a[%w%_]*$") then
handle(format("%s %s=loadstring(%q),",depth,k,dump(v)))
else
- handle(format("%s [%q]=loadstring(%q),",depth,k,dump(v)))
+ -- handle(format("%s [%q]=loadstring(%q),",depth,k,dump(v)))
+ handle(format("%s [%q]=loadstring(%q),",depth,k,debug.getinfo(v).what == "C" and "C code" or dump(v)))
end
end
else
- --~ handle(format("%s %s=%q,",depth,key(k),tostring(v)))
- if type(k) == "number" then -- or find(k,"^%d+$") then
+ if tk == "number" then -- or find(k,"^%d+$") then
if hexify then
handle(format("%s [0x%04X]=%q,",depth,k,tostring(v)))
else
handle(format("%s [%s]=%q,",depth,k,tostring(v)))
end
+ elseif tk == "boolean" then -- or find(k,"^%d+$") then
+ handle(format("%s [%s]=%q,",depth,tostring(k),tostring(v)))
elseif noquotes and not reserved[k] and find(k,"^%a[%w%_]*$") then
handle(format("%s %s=%q,",depth,k,tostring(v)))
else
handle(format("%s [%q]=%q,",depth,k,tostring(v)))
end
end
- --~ end
- end
+ end
end
if level > 0 then
handle(format("%s},",depth))
@@ -1087,15 +1254,34 @@ end
-- replacing handle by a direct t[#t+1] = ... (plus test) is not much
-- faster (0.03 on 1.00 for zapfino.tma)
-local function serialize(root,name,_handle,_reduce,_noquotes,_hexify)
- noquotes = _noquotes
- hexify = _hexify
- handle = _handle or print
- reduce = _reduce or false
- compact = table.serialize_compact
- inline = compact and table.serialize_inline
- functions = table.serialize_functions
+local function serialize(_handle,root,name,specification) -- handle wins
local tname = type(name)
+ if type(specification) == "table" then
+ noquotes = specification.noquotes
+ hexify = specification.hexify
+ handle = _handle or specification.handle or print
+ reduce = specification.reduce or false
+ functions = specification.functions
+ compact = specification.compact
+ inline = specification.inline and compact
+ if functions == nil then
+ functions = true
+ end
+ if compact == nil then
+ compact = true
+ end
+ if inline == nil then
+ inline = compact
+ end
+ else
+ noquotes = false
+ hexify = false
+ handle = _handle or print
+ reduce = false
+ compact = true
+ inline = true
+ functions = true
+ end
if tname == "string" then
if name == "return" then
handle("return {")
@@ -1117,33 +1303,33 @@ local function serialize(root,name,_handle,_reduce,_noquotes,_hexify)
else
handle("t={")
end
- if root and next(root) then
- do_serialize(root,name,"",0,indexed)
+ if root then
+ -- The dummy access will initialize a table that has a delayed initialization
+ -- using a metatable. (maybe explicitly test for metatable)
+ if getmetatable(root) then -- todo: make this an option, maybe even per subtable
+ local dummy = root._w_h_a_t_e_v_e_r_
+ root._w_h_a_t_e_v_e_r_ = nil
+ end
+ -- Let's forget about empty tables.
+ if next(root) then
+ do_serialize(root,name,"",0)
+ end
end
handle("}")
end
---~ name:
---~
---~ true : return { }
---~ false : { }
---~ nil : t = { }
---~ string : string = { }
---~ 'return' : return { }
---~ number : [number] = { }
-function table.serialize(root,name,reduce,noquotes,hexify)
- local t = { }
+function table.serialize(root,name,specification)
+ local t, n = { }, 0
local function flush(s)
- t[#t+1] = s
+ n = n + 1
+ t[n] = s
end
- serialize(root,name,flush,reduce,noquotes,hexify)
+ serialize(flush,root,name,specification)
return concat(t,"\n")
end
-function table.tohandle(handle,root,name,reduce,noquotes,hexify)
- serialize(root,name,handle,reduce,noquotes,hexify)
-end
+table.tohandle = serialize
-- sometimes tables are real use (zapfino extra pro is some 85M) in which
-- case a stepwise serialization is nice; actually, we could consider:
@@ -1154,73 +1340,63 @@ end
--
-- so this is on the todo list
-table.tofile_maxtab = 2*1024
+local maxtab = 2*1024
-function table.tofile(filename,root,name,reduce,noquotes,hexify)
+function table.tofile(filename,root,name,specification)
local f = io.open(filename,'w')
if f then
- local maxtab = table.tofile_maxtab
if maxtab > 1 then
- local t = { }
+ local t, n = { }, 0
local function flush(s)
- t[#t+1] = s
- if #t > maxtab then
+ n = n + 1
+ t[n] = s
+ if n > maxtab then
f:write(concat(t,"\n"),"\n") -- hm, write(sometable) should be nice
- t = { }
+ t, n = { }, 0 -- we could recycle t if needed
end
end
- serialize(root,name,flush,reduce,noquotes,hexify)
+ serialize(flush,root,name,specification)
f:write(concat(t,"\n"),"\n")
else
local function flush(s)
f:write(s,"\n")
end
- serialize(root,name,flush,reduce,noquotes,hexify)
+ serialize(flush,root,name,specification)
end
f:close()
+ io.flush()
end
end
-local function flatten(t,f,complete) -- is this used? meybe a variant with next, ...
- for i=1,#t do
- local v = t[i]
- if type(v) == "table" then
- if complete or type(v[1]) == "table" then
- flatten(v,f,complete)
+local function flattened(t,f,depth)
+ if f == nil then
+ f = { }
+ depth = 0xFFFF
+ elseif tonumber(f) then
+ -- assume then only two arguments are given
+ depth = f
+ f = { }
+ elseif not depth then
+ depth = 0xFFFF
+ end
+ for k, v in next, t do
+ if type(k) ~= "number" then
+ if depth > 0 and type(v) == "table" then
+ flattened(v,f,depth-1)
else
- f[#f+1] = v
+ f[k] = v
end
- else
- f[#f+1] = v
end
end
-end
-
-function table.flatten(t)
- local f = { }
- flatten(t,f,true)
- return f
-end
-
-function table.unnest(t) -- bad name
- local f = { }
- flatten(t,f,false)
- return f
-end
-
-table.flatten_one_level = table.unnest
-
--- a better one:
-
-local function flattened(t,f)
- if not f then
- f = { }
- end
- for k, v in next, t do
- if type(v) == "table" then
- flattened(v,f)
+ local n = #f
+ for k=1,#t do
+ local v = t[k]
+ if depth > 0 and type(v) == "table" then
+ flattened(v,f,depth-1)
+ n = #f
else
- f[k] = v
+ n = n + 1
+ f[n] = v
end
end
return f
@@ -1228,49 +1404,27 @@ end
table.flattened = flattened
--- the next three may disappear
-
-function table.remove_value(t,value) -- todo: n
- if value then
- for i=1,#t do
- if t[i] == value then
- remove(t,i)
- -- remove all, so no: return
- end
- end
+local function unnest(t,f) -- only used in mk, for old times sake
+ if not f then -- and only relevant for token lists
+ f = { }
end
-end
-
-function table.insert_before_value(t,value,str)
- if str then
- if value then
- for i=1,#t do
- if t[i] == value then
- insert(t,i,str)
- return
- end
+ for i=1,#t do
+ local v = t[i]
+ if type(v) == "table" then
+ if type(v[1]) == "table" then
+ unnest(v,f)
+ else
+ f[#f+1] = v
end
+ else
+ f[#f+1] = v
end
- insert(t,1,str)
- elseif value then
- insert(t,1,value)
end
+ return f
end
-function table.insert_after_value(t,value,str)
- if str then
- if value then
- for i=1,#t do
- if t[i] == value then
- insert(t,i+1,str)
- return
- end
- end
- end
- t[#t+1] = str
- elseif value then
- t[#t+1] = value
- end
+function table.unnest(t) -- bad name
+ return unnest(t)
end
local function are_equal(a,b,n,m) -- indexed
@@ -1297,7 +1451,7 @@ end
local function identical(a,b) -- assumes same structure
for ka, va in next, a do
- local vb = b[k]
+ local vb = b[ka]
if va == vb then
-- same
elseif type(va) == "table" and type(vb) == "table" then
@@ -1311,8 +1465,8 @@ local function identical(a,b) -- assumes same structure
return true
end
-table.are_equal = are_equal
table.identical = identical
+table.are_equal = are_equal
-- maybe also make a combined one
@@ -1338,89 +1492,84 @@ function table.contains(t, v)
end
function table.count(t)
- local n, e = 0, next(t)
- while e do
- n, e = n + 1, next(t,e)
+ local n = 0
+ for k, v in next, t do
+ n = n + 1
end
return n
end
-function table.swapped(t)
- local s = { }
+function table.swapped(t,s) -- hash
+ local n = { }
+ if s then
+ for k, v in next, s do
+ n[k] = v
+ end
+ end
for k, v in next, t do
- s[v] = k
+ n[v] = k
end
- return s
+ return n
end
---~ function table.are_equal(a,b)
---~ return table.serialize(a) == table.serialize(b)
---~ end
-
-function table.clone(t,p) -- t is optional or nil or table
- if not p then
- t, p = { }, t or { }
- elseif not t then
- t = { }
+function table.reversed(t)
+ if t then
+ local tt, tn = { }, #t
+ if tn > 0 then
+ local ttn = 0
+ for i=tn,1,-1 do
+ ttn = ttn + 1
+ tt[ttn] = t[i]
+ end
+ end
+ return tt
end
- setmetatable(t, { __index = function(_,key) return p[key] end }) -- why not __index = p ?
- return t
-end
-
-function table.hexed(t,seperator)
- local tt = { }
- for i=1,#t do tt[i] = format("0x%04X",t[i]) end
- return concat(tt,seperator or " ")
end
-function table.reverse_hash(h)
- local r = { }
- for k,v in next, h do
- r[v] = lower(gsub(k," ",""))
+function table.sequenced(t,sep,simple) -- hash only
+ local s, n = { }, 0
+ for k, v in sortedhash(t) do
+ if simple then
+ if v == true then
+ n = n + 1
+ s[n] = k
+ elseif v and v~= "" then
+ n = n + 1
+ s[n] = k .. "=" .. tostring(v)
+ end
+ else
+ n = n + 1
+ s[n] = k .. "=" .. tostring(v)
+ end
end
- return r
+ return concat(s, sep or " | ")
end
-function table.reverse(t)
- local tt = { }
- if #t > 0 then
- for i=#t,1,-1 do
- tt[#tt+1] = t[i]
- end
+function table.print(t,...)
+ if type(t) ~= "table" then
+ print(tostring(t))
+ else
+ table.tohandle(print,t,...)
end
- return tt
end
-function table.insert_before_value(t,value,extra)
- for i=1,#t do
- if t[i] == extra then
- remove(t,i)
- end
- end
- for i=1,#t do
- if t[i] == value then
- insert(t,i,extra)
- return
- end
- end
- insert(t,1,extra)
+-- -- -- obsolete but we keep them for a while and might comment them later -- -- --
+
+-- roughly: copy-loop : unpack : sub == 0.9 : 0.4 : 0.45 (so in critical apps, use unpack)
+
+function table.sub(t,i,j)
+ return { unpack(t,i,j) }
end
-function table.insert_after_value(t,value,extra)
- for i=1,#t do
- if t[i] == extra then
- remove(t,i)
- end
- end
- for i=1,#t do
- if t[i] == value then
- insert(t,i+1,extra)
- return
- end
- end
- insert(t,#t+1,extra)
+-- slower than #t on indexed tables (#t only returns the size of the numerically indexed slice)
+
+function table.is_empty(t)
+ return not t or not next(t)
end
+function table.has_one_entry(t)
+ return t and not next(t,next(t))
+end
end -- of closure
@@ -1435,7 +1584,10 @@ if not modules then modules = { } end modules ['l-io'] = {
license = "see context related readme files"
}
-local byte, find, gsub = string.byte, string.find, string.gsub
+local io = io
+local byte, find, gsub, format = string.byte, string.find, string.gsub, string.format
+local concat = table.concat
+local type = type
if string.find(os.getenv("PATH"),";") then
io.fileseparator, io.pathseparator = "\\", ";"
@@ -1446,9 +1598,7 @@ end
function io.loaddata(filename,textmode)
local f = io.open(filename,(textmode and 'r') or 'rb')
if f then
- -- collectgarbage("step") -- sometimes makes a big difference in mem consumption
local data = f:read('*all')
- -- garbagecollector.check(data)
f:close()
return data
else
@@ -1460,13 +1610,14 @@ function io.savedata(filename,data,joiner)
local f = io.open(filename,"wb")
if f then
if type(data) == "table" then
- f:write(table.join(data,joiner or ""))
+ f:write(concat(data,joiner or ""))
elseif type(data) == "function" then
data(f)
else
f:write(data or "")
end
f:close()
+ io.flush()
return true
else
return false
@@ -1495,12 +1646,19 @@ function io.size(filename)
end
function io.noflines(f)
- local n = 0
- for _ in f:lines() do
- n = n + 1
+ if type(f) == "string" then
+ local f = io.open(filename)
+ local n = f and io.noflines(f) or 0
+ assert(f:close())
+ return n
+ else
+ local n = 0
+ for _ in f:lines() do
+ n = n + 1
+ end
+ f:seek('set',0)
+ return n
end
- f:seek('set',0)
- return n
end
local nextchar = {
@@ -1526,8 +1684,6 @@ local nextchar = {
function io.characters(f,n)
if f then
return nextchar[n or 1], f
- else
- return nil, nil
end
end
@@ -1536,40 +1692,42 @@ local nextbyte = {
local a, b, c, d = f:read(1,1,1,1)
if d then
return byte(a), byte(b), byte(c), byte(d)
- else
- return nil, nil, nil, nil
+ end
+ end,
+ [3] = function(f)
+ local a, b, c = f:read(1,1,1)
+ if b then
+ return byte(a), byte(b), byte(c)
end
end,
[2] = function(f)
local a, b = f:read(1,1)
if b then
return byte(a), byte(b)
- else
- return nil, nil
end
end,
[1] = function (f)
local a = f:read(1)
if a then
return byte(a)
- else
- return nil
end
end,
[-2] = function (f)
local a, b = f:read(1,1)
if b then
return byte(b), byte(a)
- else
- return nil, nil
+ end
+ end,
+ [-3] = function(f)
+ local a, b, c = f:read(1,1,1)
+ if b then
+ return byte(c), byte(b), byte(a)
end
end,
[-4] = function(f)
local a, b, c, d = f:read(1,1,1,1)
if d then
return byte(d), byte(c), byte(b), byte(a)
- else
- return nil, nil, nil, nil
end
end
}
@@ -1586,12 +1744,13 @@ function io.ask(question,default,options)
while true do
io.write(question)
if options then
- io.write(string.format(" [%s]",table.concat(options,"|")))
+ io.write(format(" [%s]",concat(options,"|")))
end
if default then
- io.write(string.format(" [%s]",default))
+ io.write(format(" [%s]",default))
end
- io.write(string.format(" "))
+ io.write(format(" "))
+ io.flush()
local answer = io.read()
answer = gsub(answer,"^%s*(.*)%s*$","%1")
if answer == "" and default then
@@ -1615,6 +1774,63 @@ function io.ask(question,default,options)
end
end
+local function readnumber(f,n,m)
+ if m then
+ f:seek("set",n)
+ n = m
+ end
+ if n == 1 then
+ return byte(f:read(1))
+ elseif n == 2 then
+ local a, b = byte(f:read(2),1,2)
+ return 256 * a + b
+ elseif n == 3 then
+ local a, b, c = byte(f:read(3),1,3)
+ return 256*256 * a + 256 * b + c
+ elseif n == 4 then
+ local a, b, c, d = byte(f:read(4),1,4)
+ return 256*256*256 * a + 256*256 * b + 256 * c + d
+ elseif n == 8 then
+ local a, b = readnumber(f,4), readnumber(f,4)
+ return 256 * a + b
+ elseif n == 12 then
+ local a, b, c = readnumber(f,4), readnumber(f,4), readnumber(f,4)
+ return 256*256 * a + 256 * b + c
+ elseif n == -2 then
+ local b, a = byte(f:read(2),1,2)
+ return 256*a + b
+ elseif n == -3 then
+ local c, b, a = byte(f:read(3),1,3)
+ return 256*256 * a + 256 * b + c
+ elseif n == -4 then
+ local d, c, b, a = byte(f:read(4),1,4)
+ return 256*256*256 * a + 256*256 * b + 256*c + d
+ elseif n == -8 then
+ local h, g, f, e, d, c, b, a = byte(f:read(8),1,8)
+ return 256*256*256*256*256*256*256 * a +
+ 256*256*256*256*256*256 * b +
+ 256*256*256*256*256 * c +
+ 256*256*256*256 * d +
+ 256*256*256 * e +
+ 256*256 * f +
+ 256 * g +
+ h
+ else
+ return 0
+ end
+end
+
+io.readnumber = readnumber
+
+function io.readstring(f,n,m)
+ if m then
+ f:seek("set",n)
+ n = m
+ end
+ local str = gsub(f:read(n),"%z","")
+ return str
+end
+
end -- of closure
@@ -1628,11 +1844,15 @@ if not modules then modules = { } end modules ['l-number'] = {
license = "see context related readme files"
}
+-- this module will be replaced when we have the bit library
+
local tostring = tostring
-local format, floor, insert, match = string.format, math.floor, table.insert, string.match
+local format, floor, match, rep = string.format, math.floor, string.match, string.rep
+local concat, insert = table.concat, table.insert
local lpegmatch = lpeg.match
-number = number or { }
+number = number or { }
+local number = number
-- a,b,c,d,e,f = number.toset(100101)
@@ -1680,6 +1900,54 @@ function number.bits(n,zero)
end
+function number.bit(p)
+ return 2 ^ (p - 1) -- 1-based indexing
+end
+
+function number.hasbit(x, p) -- typical call: if hasbit(x, bit(3)) then ...
+ return x % (p + p) >= p
+end
+
+function number.setbit(x, p)
+ return hasbit(x, p) and x or x + p
+end
+
+function number.clearbit(x, p)
+ return hasbit(x, p) and x - p or x
+end
+
+
+function number.tobitstring(n,m)
+ if n == 0 then
+ if m then
+ rep("00000000",m)
+ else
+ return "00000000"
+ end
+ else
+ local t = { }
+ while n > 0 do
+ insert(t,1,n % 2 > 0 and 1 or 0)
+ n = floor(n/2)
+ end
+ local nn = 8 - #t % 8
+ if nn > 0 and nn < 8 then
+ for i=1,nn do
+ insert(t,1,0)
+ end
+ end
+ if m then
+ m = m * 8 - #t
+ if m > 0 then
+ insert(t,1,rep("0",m))
+ end
+ end
+ return concat(t)
+ end
+end
+
+
+
end -- of closure
do -- create closure to overcome 200 locals limit
@@ -1692,6 +1960,8 @@ if not modules then modules = { } end modules ['l-set'] = {
license = "see context related readme files"
}
+-- This will become obsolete when we have the bitset library embedded.
+
set = set or { }
local nums = { }
@@ -1735,10 +2005,11 @@ function set.tolist(n)
if n == 0 or not tabs[n] then
return ""
else
- local t = { }
+ local t, n = { }, 0
for k, v in next, tabs[n] do
if v then
- t[#t+1] = k
+ n = n + 1
+ t[n] = k
end
end
return concat(t," ")
@@ -1756,17 +2027,6 @@ function set.contains(n,s)
end
end
---~ local c = set.create{'aap','noot','mies'}
---~ local s = set.tonumber(c)
---~ local t = set.totable(s)
---~ print(t['aap'])
---~ local c = set.create{'zus','wim','jet'}
---~ local s = set.tonumber(c)
---~ local t = set.totable(s)
---~ print(t['aap'])
---~ print(t['jet'])
---~ print(set.contains(t,'jet'))
---~ print(set.contains(t,'aap'))
@@ -1782,32 +2042,119 @@ if not modules then modules = { } end modules ['l-os'] = {
license = "see context related readme files"
}
+-- This file deals with some operating system issues. Please don't bother me
+-- with the pros and cons of operating systems as they all have their flaws
+-- and benefits. Bashing one of them won't help solving problems and fixing
+-- bugs faster and is a waste of time and energy.
+--
+-- path separators: / or \ ... we can use / everywhere
+-- suffixes : dll so exe <none> ... no big deal
+-- quotes : we can use "" in most cases
+-- expansion : unless "" are used * might give side effects
+-- piping/threads : somewhat different for each os
+-- locations : specific user file locations and settings can change over time
+--
+-- os.type : windows | unix (new, we already guessed os.platform)
+-- os.name : windows | msdos | linux | macosx | solaris | .. | generic (new)
+-- os.platform : extended os.name with architecture
+
-- maybe build io.flush in os.execute
-local find, format, gsub = string.find, string.format, string.gsub
+local os = os
+local find, format, gsub, upper = string.find, string.format, string.gsub, string.upper
+local concat = table.concat
local random, ceil = math.random, math.ceil
+local rawget, rawset, type, getmetatable, setmetatable, tonumber = rawget, rawset, type, getmetatable, setmetatable, tonumber
+
+-- The following code permits traversing the environment table, at least
+-- in luatex. Internally all environment names are uppercase.
+
+if not os.__getenv__ then
+
+ os.__getenv__ = os.getenv
+ os.__setenv__ = os.setenv
+
+ if os.env then
+
+ local osgetenv = os.getenv
+ local ossetenv = os.setenv
+ local osenv = os.env local _ = osenv.PATH -- initialize the table
+
+ function os.setenv(k,v)
+ if v == nil then
+ v = ""
+ end
+ local K = upper(k)
+ osenv[K] = v
+ if type(v) == "table" then
+ v = concat(v,";") -- path
+ end
+ ossetenv(K,v)
+ end
+
+ function os.getenv(k)
+ local K = upper(k)
+ local v = osenv[K] or osenv[k] or osgetenv(K) or osgetenv(k)
+ if v == "" then
+ return nil
+ else
+ return v
+ end
+ end
+
+ else
+
+ local ossetenv = os.setenv
+ local osgetenv = os.getenv
+ local osenv = { }
+
+ function os.setenv(k,v)
+ if v == nil then
+ v = ""
+ end
+ local K = upper(k)
+ osenv[K] = v
+ end
-local execute, spawn, exec, ioflush = os.execute, os.spawn or os.execute, os.exec or os.execute, io.flush
+ function os.getenv(k)
+ local K = upper(k)
+ local v = osenv[K] or osgetenv(K) or osgetenv(k)
+ if v == "" then
+ return nil
+ else
+ return v
+ end
+ end
+
+ local function __index(t,k)
+ return os.getenv(k)
+ end
+ local function __newindex(t,k,v)
+ os.setenv(k,v)
+ end
+
+ os.env = { }
+
+ setmetatable(os.env, { __index = __index, __newindex = __newindex } )
+
+ end
+
+end
+
+-- end of environment hack
+
+local execute, spawn, exec, iopopen, ioflush = os.execute, os.spawn or os.execute, os.exec or os.execute, io.popen, io.flush
function os.execute(...) ioflush() return execute(...) end
function os.spawn (...) ioflush() return spawn (...) end
function os.exec (...) ioflush() return exec (...) end
+function io.popen (...) ioflush() return iopopen(...) end
function os.resultof(command)
- ioflush() -- else messed up logging
local handle = io.popen(command,"r")
- if not handle then
- -- print("unknown command '".. command .. "' in os.resultof")
- return ""
- else
- return handle:read("*all") or ""
- end
+ return handle and handle:read("*all") or ""
end
---~ os.type : windows | unix (new, we already guessed os.platform)
---~ os.name : windows | msdos | linux | macosx | solaris | .. | generic (new)
---~ os.platform : extended os.name with architecture
-
if not io.fileseparator then
if find(os.getenv("PATH"),";") then
io.fileseparator, io.pathseparator, os.type = "\\", ";", os.type or "mswin"
@@ -1856,16 +2203,11 @@ function os.runtime()
return os.gettimeofday() - startuptime
end
---~ print(os.gettimeofday()-os.time())
---~ os.sleep(1.234)
---~ print (">>",os.runtime())
---~ print(os.date("%H:%M:%S",os.gettimeofday()))
---~ print(os.date("%H:%M:%S",os.time()))
-- no need for function anymore as we have more clever code and helpers now
-- this metatable trickery might as well disappear
-os.resolvers = os.resolvers or { }
+os.resolvers = os.resolvers or { } -- will become private
local resolvers = os.resolvers
@@ -1878,24 +2220,6 @@ end
setmetatable(os,osmt)
-if not os.setenv then
-
- -- we still store them but they won't be seen in
- -- child processes although we might pass them some day
- -- using command concatination
-
- local env, getenv = { }, os.getenv
-
- function os.setenv(k,v)
- env[k] = v
- end
-
- function os.getenv(k)
- return env[k] or getenv(k)
- end
-
-end
-
-- we can use HOSTTYPE on some platforms
local name, platform = os.name or "linux", os.getenv("MTX_PLATFORM") or ""
@@ -1935,7 +2259,7 @@ elseif os.type == "windows" then
elseif name == "linux" then
function os.resolvers.platform(t,k)
- -- we sometims have HOSTTYPE set so let's check that first
+ -- we sometimes have HOSTTYPE set so let's check that first
local platform, architecture = "", os.getenv("HOSTTYPE") or os.resultof("uname -m") or ""
if find(architecture,"x86_64") then
platform = "linux-64"
@@ -2013,10 +2337,10 @@ elseif name == "freebsd" then
elseif name == "kfreebsd" then
function os.resolvers.platform(t,k)
- -- we sometims have HOSTTYPE set so let's check that first
+ -- we sometimes have HOSTTYPE set so let's check that first
local platform, architecture = "", os.getenv("HOSTTYPE") or os.resultof("uname -m") or ""
if find(architecture,"x86_64") then
- platform = "kfreebsd-64"
+ platform = "kfreebsd-amd64"
else
platform = "kfreebsd-i386"
end
@@ -2091,61 +2415,86 @@ if not modules then modules = { } end modules ['l-file'] = {
-- needs a cleanup
-file = file or { }
+file = file or { }
+local file = file
-local concat = table.concat
-local find, gmatch, match, gsub, sub, char = string.find, string.gmatch, string.match, string.gsub, string.sub, string.char
+local insert, concat = table.insert, table.concat
+local find, gmatch, match, gsub, sub, char, lower = string.find, string.gmatch, string.match, string.gsub, string.sub, string.char, string.lower
local lpegmatch = lpeg.match
+local getcurrentdir, attributes = lfs.currentdir, lfs.attributes
-function file.removesuffix(filename)
- return (gsub(filename,"%.[%a%d]+$",""))
+local P, R, S, C, Cs, Cp, Cc = lpeg.P, lpeg.R, lpeg.S, lpeg.C, lpeg.Cs, lpeg.Cp, lpeg.Cc
+
+local function dirname(name,default)
+ return match(name,"^(.+)[/\\].-$") or (default or "")
end
-function file.addsuffix(filename, suffix)
- if not suffix or suffix == "" then
- return filename
- elseif not find(filename,"%.[%a%d]+$") then
- return filename .. "." .. suffix
- else
- return filename
- end
+local function basename(name)
+ return match(name,"^.+[/\\](.-)$") or name
end
-function file.replacesuffix(filename, suffix)
- return (gsub(filename,"%.[%a%d]+$","")) .. "." .. suffix
+local function nameonly(name)
+ return (gsub(match(name,"^.+[/\\](.-)$") or name,"%..*$",""))
end
-function file.dirname(name,default)
- return match(name,"^(.+)[/\\].-$") or (default or "")
+local function extname(name,default)
+ return match(name,"^.+%.([^/\\]-)$") or default or ""
end
-function file.basename(name)
- return match(name,"^.+[/\\](.-)$") or name
+local function splitname(name)
+ local n, s = match(name,"^(.+)%.([^/\\]-)$")
+ return n or name, s or ""
end
-function file.nameonly(name)
- return (gsub(match(name,"^.+[/\\](.-)$") or name,"%..*$",""))
+file.basename = basename
+file.dirname = dirname
+file.nameonly = nameonly
+file.extname = extname
+file.suffix = extname
+
+function file.removesuffix(filename)
+ return (gsub(filename,"%.[%a%d]+$",""))
end
-function file.extname(name,default)
- return match(name,"^.+%.([^/\\]-)$") or default or ""
+function file.addsuffix(filename, suffix, criterium)
+ if not suffix or suffix == "" then
+ return filename
+ elseif criterium == true then
+ return filename .. "." .. suffix
+ elseif not criterium then
+ local n, s = splitname(filename)
+ if not s or s == "" then
+ return filename .. "." .. suffix
+ else
+ return filename
+ end
+ else
+ local n, s = splitname(filename)
+ if s and s ~= "" then
+ local t = type(criterium)
+ if t == "table" then
+ -- keep if in criterium
+ for i=1,#criterium do
+ if s == criterium[i] then
+ return filename
+ end
+ end
+ elseif t == "string" then
+ -- keep if criterium
+ if s == criterium then
+ return filename
+ end
+ end
+ end
+ return n .. "." .. suffix
+ end
end
-file.suffix = file.extname
---~ function file.join(...)
---~ local pth = concat({...},"/")
---~ pth = gsub(pth,"\\","/")
---~ local a, b = match(pth,"^(.*://)(.*)$")
---~ if a and b then
---~ return a .. gsub(b,"//+","/")
---~ end
---~ a, b = match(pth,"^(//)(.*)$")
---~ if a and b then
---~ return a .. gsub(b,"//+","/")
---~ end
---~ return (gsub(pth,"//+","/"))
---~ end
+function file.replacesuffix(filename, suffix)
+ return (gsub(filename,"%.[%a%d]+$","")) .. "." .. suffix
+end
+
local trick_1 = char(1)
local trick_2 = "^" .. trick_1 .. "/+"
@@ -2173,60 +2522,37 @@ function file.join(...)
return (gsub(pth,"//+","/"))
end
---~ print(file.join("//","/y"))
---~ print(file.join("/","/y"))
---~ print(file.join("","/y"))
---~ print(file.join("/x/","/y"))
---~ print(file.join("x/","/y"))
---~ print(file.join("http://","/y"))
---~ print(file.join("http://a","/y"))
---~ print(file.join("http:///a","/y"))
---~ print(file.join("//nas-1","/y"))
-function file.iswritable(name)
- local a = lfs.attributes(name) or lfs.attributes(file.dirname(name,"."))
+function file.is_writable(name)
+ local a = attributes(name) or attributes(dirname(name,"."))
return a and sub(a.permissions,2,2) == "w"
end
-function file.isreadable(name)
- local a = lfs.attributes(name)
+function file.is_readable(name)
+ local a = attributes(name)
return a and sub(a.permissions,1,1) == "r"
end
-file.is_readable = file.isreadable
-file.is_writable = file.iswritable
+file.isreadable = file.is_readable -- depricated
+file.iswritable = file.is_writable -- depricated
--- todo: lpeg
-
---~ function file.split_path(str)
---~ local t = { }
---~ str = gsub(str,"\\", "/")
---~ str = gsub(str,"(%a):([;/])", "%1\001%2")
---~ for name in gmatch(str,"([^;:]+)") do
---~ if name ~= "" then
---~ t[#t+1] = gsub(name,"\001",":")
---~ end
---~ end
---~ return t
---~ end
+-- todo: lpeg \\ / .. does not save much
local checkedsplit = string.checkedsplit
-function file.split_path(str,separator)
+function file.splitpath(str,separator) -- string
str = gsub(str,"\\","/")
return checkedsplit(str,separator or io.pathseparator)
end
-function file.join_path(tab)
- return concat(tab,io.pathseparator) -- can have trailing //
+function file.joinpath(tab,separator) -- table
+ return concat(tab,separator or io.pathseparator) -- can have trailing //
end
-- we can hash them weakly
-local insert = table.insert
-
-function file.collapse_path(str,anchor)
+function file.collapsepath(str,anchor)
if anchor and not find(str,"^/") and not find(str,"^%a:") then
str = getcurrentdir() .. "/" .. str
end
@@ -2280,62 +2606,15 @@ function file.collapse_path(str,anchor)
return concat(newelements, '/')
end
end
-function file.xcollapse_path(str)
- print('collapse', str)
- if str == "" or str =="." then
- return "."
- elseif find(str,"^%.%.") then
- return str
- elseif not find(str,"%.") then
- return str
- end
- str = gsub(str,"^%./",lfs.currentdir() .. "/") -- ./xx in qualified
- str = gsub(str,"\\","/")
- local oldelements = checkedsplit(str,"/")
- local newelements = { }
- local i = #oldelements
- while i > 0 do
- local element = oldelements[i]
- if element == '.' then
- -- do nothing
- elseif element == '..' then
- local n = i -1
- while n > 0 do
- local element = oldelements[n]
- if element ~= '..' and element ~= '.' then
- oldelements[n] = '.'
- break
- else
- n = n - 1
- end
- end
- if n<1 then
- insert(newelements,1,'..')
- end
- elseif element ~= "" then
- insert(newelements,1,element)
- end
- i = i - 1
- end
- if #newelements == 0 then
- return "."
- elseif find(str,"^/") then
- return "/" .. concat(newelements,'/')
- else
- return concat(newelements, '/')
- end
-end
---~ print(file.collapse_path("/a"))
---~ print(file.collapse_path("a/./b/.."))
---~ print(file.collapse_path("a/aa/../b/bb"))
---~ print(file.collapse_path("a/../.."))
---~ print(file.collapse_path("a/.././././b/.."))
---~ print(file.collapse_path("a/./././b/.."))
---~ print(file.collapse_path("a/b/c/../.."))
-function file.robustname(str)
- return (gsub(str,"[^%a%d%/%-%.\\]+","-"))
+function file.robustname(str,strict)
+ str = gsub(str,"[^%a%d%/%-%.\\]+","-")
+ if strict then
+ return lower(gsub(str,"^%-*(.-)%-*$","%1"))
+ else
+ return str
+ end
end
file.readdata = io.loaddata
@@ -2347,100 +2626,34 @@ end
-- lpeg variants, slightly faster, not always
---~ local period = lpeg.P(".")
---~ local slashes = lpeg.S("\\/")
---~ local noperiod = 1-period
---~ local noslashes = 1-slashes
---~ local name = noperiod^1
-
---~ local pattern = (noslashes^0 * slashes)^0 * (noperiod^1 * period)^1 * lpeg.C(noperiod^1) * -1
-
---~ function file.extname(name)
---~ return lpegmatch(pattern,name) or ""
---~ end
-
---~ local pattern = lpeg.Cs(((period * noperiod^1 * -1)/"" + 1)^1)
-
---~ function file.removesuffix(name)
---~ return lpegmatch(pattern,name)
---~ end
-
---~ local pattern = (noslashes^0 * slashes)^1 * lpeg.C(noslashes^1) * -1
-
---~ function file.basename(name)
---~ return lpegmatch(pattern,name) or name
---~ end
-
---~ local pattern = (noslashes^0 * slashes)^1 * lpeg.Cp() * noslashes^1 * -1
-
---~ function file.dirname(name)
---~ local p = lpegmatch(pattern,name)
---~ if p then
---~ return sub(name,1,p-2)
---~ else
---~ return ""
---~ end
---~ end
-
---~ local pattern = (noslashes^0 * slashes)^0 * (noperiod^1 * period)^1 * lpeg.Cp() * noperiod^1 * -1
-
---~ function file.addsuffix(name, suffix)
---~ local p = lpegmatch(pattern,name)
---~ if p then
---~ return name
---~ else
---~ return name .. "." .. suffix
---~ end
---~ end
-
---~ local pattern = (noslashes^0 * slashes)^0 * (noperiod^1 * period)^1 * lpeg.Cp() * noperiod^1 * -1
-
---~ function file.replacesuffix(name,suffix)
---~ local p = lpegmatch(pattern,name)
---~ if p then
---~ return sub(name,1,p-2) .. "." .. suffix
---~ else
---~ return name .. "." .. suffix
---~ end
---~ end
-
---~ local pattern = (noslashes^0 * slashes)^0 * lpeg.Cp() * ((noperiod^1 * period)^1 * lpeg.Cp() + lpeg.P(true)) * noperiod^1 * -1
-
---~ function file.nameonly(name)
---~ local a, b = lpegmatch(pattern,name)
---~ if b then
---~ return sub(name,a,b-2)
---~ elseif a then
---~ return sub(name,a)
---~ else
---~ return name
---~ end
---~ end
-
---~ local test = file.extname
---~ local test = file.basename
---~ local test = file.dirname
---~ local test = file.addsuffix
---~ local test = file.replacesuffix
---~ local test = file.nameonly
-
---~ print(1,test("./a/b/c/abd.def.xxx","!!!"))
---~ print(2,test("./../b/c/abd.def.xxx","!!!"))
---~ print(3,test("a/b/c/abd.def.xxx","!!!"))
---~ print(4,test("a/b/c/def.xxx","!!!"))
---~ print(5,test("a/b/c/def","!!!"))
---~ print(6,test("def","!!!"))
---~ print(7,test("def.xxx","!!!"))
-
---~ local tim = os.clock() for i=1,250000 do local ext = test("abd.def.xxx","!!!") end print(os.clock()-tim)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-- also rewrite previous
-local letter = lpeg.R("az","AZ") + lpeg.S("_-+")
-local separator = lpeg.P("://")
+local letter = R("az","AZ") + S("_-+")
+local separator = P("://")
+
+local qualified = P(".")^0 * P("/") + letter*P(":") + letter^1*separator + letter^1 * P("/")
+local rootbased = P("/") + letter*P(":")
-local qualified = lpeg.P(".")^0 * lpeg.P("/") + letter*lpeg.P(":") + letter^1*separator + letter^1 * lpeg.P("/")
-local rootbased = lpeg.P("/") + letter*lpeg.P(":")
+lpeg.patterns.qualified = qualified
+lpeg.patterns.rootbased = rootbased
-- ./name ../name /name c: :// name/name
@@ -2452,14 +2665,16 @@ function file.is_rootbased_path(filename)
return lpegmatch(rootbased,filename) ~= nil
end
-local slash = lpeg.S("\\/")
-local period = lpeg.P(".")
-local drive = lpeg.C(lpeg.R("az","AZ")) * lpeg.P(":")
-local path = lpeg.C(((1-slash)^0 * slash)^0)
-local suffix = period * lpeg.C(lpeg.P(1-period)^0 * lpeg.P(-1))
-local base = lpeg.C((1-suffix)^0)
+-- actually these are schemes
-local pattern = (drive + lpeg.Cc("")) * (path + lpeg.Cc("")) * (base + lpeg.Cc("")) * (suffix + lpeg.Cc(""))
+local slash = S("\\/")
+local period = P(".")
+local drive = C(R("az","AZ")) * P(":")
+local path = C(((1-slash)^0 * slash)^0)
+local suffix = period * C(P(1-period)^0 * P(-1))
+local base = C((1-suffix)^0)
+
+local pattern = (drive + Cc("")) * (path + Cc("")) * (base + Cc("")) * (suffix + Cc(""))
function file.splitname(str) -- returns drive, path, base, suffix
return lpegmatch(pattern,str)
@@ -2472,14 +2687,13 @@ end
-- test { "/aa", "/aa/bb", "/aa/bb/cc", "/aa/bb/cc.dd", "/aa/bb/cc.dd.ee" }
-- test { "aa", "aa/bb", "aa/bb/cc", "aa/bb/cc.dd", "aa/bb/cc.dd.ee" }
---~ -- todo:
---~
---~ if os.type == "windows" then
---~ local currentdir = lfs.currentdir
---~ function lfs.currentdir()
---~ return (gsub(currentdir(),"\\","/"))
---~ end
---~ end
+
+-- for myself:
+
+function file.strip(name,dir)
+ local b, a = match(name,"^(.-)" .. dir .. "(.*)$")
+ return a ~= "" and a or name
+end
end -- of closure
@@ -2495,6 +2709,7 @@ if not modules then modules = { } end modules ['l-md5'] = {
-- This also provides file checksums and checkers.
+local md5, file = md5, file
local gsub, format, byte = string.gsub, string.format, string.byte
local function convert(str,fmt)
@@ -2505,27 +2720,13 @@ if not md5.HEX then function md5.HEX(str) return convert(str,"%02X") end end
if not md5.hex then function md5.hex(str) return convert(str,"%02x") end end
if not md5.dec then function md5.dec(str) return convert(str,"%03i") end end
---~ if not md5.HEX then
---~ local function remap(chr) return format("%02X",byte(chr)) end
---~ function md5.HEX(str) return (gsub(md5.sum(str),".",remap)) end
---~ end
---~ if not md5.hex then
---~ local function remap(chr) return format("%02x",byte(chr)) end
---~ function md5.hex(str) return (gsub(md5.sum(str),".",remap)) end
---~ end
---~ if not md5.dec then
---~ local function remap(chr) return format("%03i",byte(chr)) end
---~ function md5.dec(str) return (gsub(md5.sum(str),".",remap)) end
---~ end
-
-file.needs_updating_threshold = 1
-
-function file.needs_updating(oldname,newname) -- size modification access change
+
+function file.needs_updating(oldname,newname,threshold) -- size modification access change
local oldtime = lfs.attributes(oldname, modification)
local newtime = lfs.attributes(newname, modification)
if newtime >= oldtime then
return false
- elseif oldtime - newtime < file.needs_updating_threshold then
+ elseif oldtime - newtime < (threshold or 1) then
return false
else
return true
@@ -2572,86 +2773,171 @@ if not modules then modules = { } end modules ['l-url'] = {
license = "see context related readme files"
}
-local char, gmatch, gsub = string.char, string.gmatch, string.gsub
+local char, gmatch, gsub, format, byte, find = string.char, string.gmatch, string.gsub, string.format, string.byte, string.find
+local concat = table.concat
local tonumber, type = tonumber, type
-local lpegmatch = lpeg.match
+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 tochar = function(s) return char(tonumber(s,16)) end
+
+local colon = P(":")
+local qmark = P("?")
+local hash = P("#")
+local slash = P("/")
+local percent = P("%")
+local endofstring = P(-1)
+
+local hexdigit = R("09","AF","af")
+local plus = P("+")
+local nothing = Cc("")
+local escaped = (plus / " ") + (percent * C(hexdigit * hexdigit) / tochar)
-url = url or { }
+-- we assume schemes with more than 1 character (in order to avoid problems with windows disks)
-local function tochar(s)
- return char(tonumber(s,16))
-end
+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
-local colon, qmark, hash, slash, percent, endofstring = lpeg.P(":"), lpeg.P("?"), lpeg.P("#"), lpeg.P("/"), lpeg.P("%"), lpeg.P(-1)
+local validurl = scheme * authority * path * query * fragment
+local parser = Ct(validurl)
-local hexdigit = lpeg.R("09","AF","af")
-local plus = lpeg.P("+")
-local escaped = (plus / " ") + (percent * lpeg.C(hexdigit * hexdigit) / tochar)
+lpegpatterns.url = validurl
+lpegpatterns.urlsplitter = parser
--- we assume schemes with more than 1 character (in order to avoid problems with windows disks)
+local escapes = { } ; for i=0,255 do escapes[i] = format("%%%02X",i) end
-local scheme = lpeg.Cs((escaped+(1-colon-slash-qmark-hash))^2) * colon + lpeg.Cc("")
-local authority = slash * slash * lpeg.Cs((escaped+(1- slash-qmark-hash))^0) + lpeg.Cc("")
-local path = slash * lpeg.Cs((escaped+(1- qmark-hash))^0) + lpeg.Cc("")
-local query = qmark * lpeg.Cs((escaped+(1- hash))^0) + lpeg.Cc("")
-local fragment = hash * lpeg.Cs((escaped+(1- endofstring))^0) + lpeg.Cc("")
+local escaper = Cs((R("09","AZ","az") + S("-./_") + P(1) / escapes)^0)
-local parser = lpeg.Ct(scheme * authority * path * query * fragment)
+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)
- 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] ~= ""
- return {
- scheme = (somescheme and s[1]) or "file",
- authority = s[2],
- path = s[3],
- query = s[4],
- fragment = s[5],
- original = str,
- noscheme = not somescheme,
- }
+ local somequery = s[4] ~= ""
+ if not somescheme and not somequery then
+ s = {
+ scheme = "file",
+ authority = "",
+ path = str,
+ query = "",
+ fragment = "",
+ original = str,
+ noscheme = true,
+ filename = str,
+ }
+ 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 = authority,
+ path = path,
+ query = s[4],
+ fragment = s[5],
+ original = str,
+ noscheme = false,
+ filename = filename,
+ }
+ end
+ return s
end
-function url.hasscheme(str)
- return url.split(str)[1] ~= ""
-end
+-- Here we assume:
+--
+-- files: /// = relative
+-- files: //// = absolute (!)
+
+
+
+url.split = split
+url.hasscheme = hasscheme
+url.hashed = hashed
-function url.addscheme(str,scheme)
- return (url.hasscheme(str) and str) or ((scheme or "file:///") .. str)
+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)
- local fullurl = hash.sheme .. "://".. hash.authority .. hash.path
- if hash.query then
- fullurl = fullurl .. "?".. hash.query
+function url.construct(hash) -- dodo: we need to escape !
+ local fullurl, f = { }, 0
+ local scheme, authority, path, query, fragment = hash.scheme, hash.authority, hash.path, hash.query, hash.fragment
+ if scheme and scheme ~= "" then
+ f = f + 1 ; fullurl[f] = scheme .. "://"
+ end
+ if authority and authority ~= "" then
+ f = f + 1 ; fullurl[f] = authority
end
- if hash.fragment then
- fullurl = fullurl .. "?".. hash.fragment
+ if path and path ~= "" then
+ f = f + 1 ; fullurl[f] = "/" .. path
end
- return fullurl
+ if query and query ~= "" then
+ f = f + 1 ; fullurl[f] = "?".. query
+ end
+ if fragment and fragment ~= "" then
+ 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
@@ -2667,37 +2953,13 @@ function url.query(str)
end
end
---~ print(url.filename("file:///c:/oeps.txt"))
---~ print(url.filename("c:/oeps.txt"))
---~ print(url.filename("file:///oeps.txt"))
---~ print(url.filename("file:///etc/test.txt"))
---~ print(url.filename("/oeps.txt"))
-
---~ from the spec on the web (sort of):
---~
---~ function test(str)
---~ print(table.serialize(url.hashed(str)))
---~ end
---~
---~ test("%56pass%20words")
---~ test("file:///c:/oeps.txt")
---~ test("file:///c|/oeps.txt")
---~ test("file:///etc/oeps.txt")
---~ test("file://./etc/oeps.txt")
---~ test("file:////etc/oeps.txt")
---~ test("ftp://ftp.is.co.za/rfc/rfc1808.txt")
---~ test("http://www.ietf.org/rfc/rfc2396.txt")
---~ test("ldap://[2001:db8::7]/c=GB?objectClass?one#what")
---~ test("mailto:John.Doe@example.com")
---~ test("news:comp.infosystems.www.servers.unix")
---~ test("tel:+1-816-555-1212")
---~ test("telnet://192.0.2.16:80/")
---~ test("urn:oasis:names:specification:docbook:dtd:xml:4.1.2")
---~ test("/etc/passwords")
---~ test("http://www.pragma-ade.com/spaced%20name")
-
---~ test("zip:///oeps/oeps.zip#bla/bla.tex")
---~ test("zip:///oeps/oeps.zip?bla/bla.tex")
+
+
+
+
+
+
+
end -- of closure
@@ -2712,35 +2974,53 @@ if not modules then modules = { } end modules ['l-dir'] = {
license = "see context related readme files"
}
--- dir.expand_name will be merged with cleanpath and collapsepath
+-- dir.expandname will be merged with cleanpath and collapsepath
local type = type
local find, gmatch, match, gsub = string.find, string.gmatch, string.match, string.gsub
+local concat, insert, remove = table.concat, table.insert, table.remove
local lpegmatch = lpeg.match
+local P, S, R, C, Cc, Cs, Ct, Cv, V = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.Cc, lpeg.Cs, lpeg.Ct, lpeg.Cv, lpeg.V
+
dir = dir or { }
+local dir = dir
+local lfs = lfs
+
+local attributes = lfs.attributes
+local walkdir = lfs.dir
+local isdir = lfs.isdir
+local isfile = lfs.isfile
+local mkdir = lfs.mkdir
+local chdir = lfs.chdir
+local currentdir = lfs.currentdir
-- handy
function dir.current()
- return (gsub(lfs.currentdir(),"\\","/"))
+ return (gsub(currentdir(),"\\","/"))
end
--- optimizing for no string.find (*) does not save time
+-- optimizing for no find (*) does not save time
-local attributes = lfs.attributes
-local walkdir = lfs.dir
-local function glob_pattern(path,patt,recurse,action)
- local ok, scanner
+local lfsisdir = isdir
+
+local function isdir(path)
+ path = gsub(path,"[/\\]+$","")
+ return lfsisdir(path)
+end
+
+lfs.isdir = isdir
+
+local function globpattern(path,patt,recurse,action)
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
+ if isdir(path) then -- lfs.isdir does not like trailing /
+ for name in walkdir(path) do -- lfs.dir accepts trailing /
local full = path .. name
local mode = attributes(full,'mode')
if mode == 'file' then
@@ -2748,15 +3028,15 @@ local function glob_pattern(path,patt,recurse,action)
action(full)
end
elseif recurse and (mode == "directory") and (name ~= '.') and (name ~= "..") then
- glob_pattern(full,patt,recurse,action)
+ globpattern(full,patt,recurse,action)
end
end
end
end
-dir.glob_pattern = glob_pattern
+dir.globpattern = globpattern
-local function collect_pattern(path,patt,recurse,result)
+local function collectpattern(path,patt,recurse,result)
local ok, scanner
result = result or { }
if path == "/" then
@@ -2775,7 +3055,7 @@ local function collect_pattern(path,patt,recurse,result)
result[name] = attr
end
elseif recurse and (mode == "directory") and (name ~= '.') and (name ~= "..") then
- attr.list = collect_pattern(full,patt,recurse)
+ attr.list = collectpattern(full,patt,recurse)
result[name] = attr
end
end
@@ -2783,9 +3063,7 @@ local function collect_pattern(path,patt,recurse,result)
return result
end
-dir.collect_pattern = collect_pattern
-
-local P, S, R, C, Cc, Cs, Ct, Cv, V = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.Cc, lpeg.Cs, lpeg.Ct, lpeg.Cv, lpeg.V
+dir.collectpattern = collectpattern
local pattern = Ct {
[1] = (C(P(".") + P("/")^1) + C(R("az","AZ") * P(":") * P("/")^0) + Cc("./")) * V(2) * V(3),
@@ -2809,16 +3087,16 @@ local function glob(str,t)
for s=1,#str do
glob(str[s],t)
end
- elseif lfs.isfile(str) then
+ elseif isfile(str) then
t(str)
else
- local split = lpegmatch(pattern,str)
+ local split = lpegmatch(pattern,str) -- we could use the file splitter
if split then
local root, path, base = split[1], split[2], split[3]
local recurse = find(base,"%*%*")
local start = root .. path
local result = lpegmatch(filter,start .. base)
- glob_pattern(start,result,recurse,t)
+ globpattern(start,result,recurse,t)
end
end
else
@@ -2828,12 +3106,15 @@ local function glob(str,t)
glob(str[s],t)
end
return t
- elseif lfs.isfile(str) then
- local t = t or { }
- t[#t+1] = str
- return t
+ elseif isfile(str) then
+ if t then
+ t[#t+1] = str
+ return t
+ else
+ return { str }
+ end
else
- local split = lpegmatch(pattern,str)
+ local split = lpegmatch(pattern,str) -- we could use the file splitter
if split then
local t = t or { }
local action = action or function(name) t[#t+1] = name end
@@ -2841,7 +3122,7 @@ local function glob(str,t)
local recurse = find(base,"%*%*")
local start = root .. path
local result = lpegmatch(filter,start .. base)
- glob_pattern(start,result,recurse,action)
+ globpattern(start,result,recurse,action)
return t
else
return { }
@@ -2852,18 +3133,14 @@ end
dir.glob = glob
---~ list = dir.glob("**/*.tif")
---~ list = dir.glob("/**/*.tif")
---~ list = dir.glob("./**/*.tif")
---~ list = dir.glob("oeps/**/*.tif")
---~ list = dir.glob("/oeps/**/*.tif")
local function globfiles(path,recurse,func,files) -- func == pattern or function
if type(func) == "string" then
- local s = func -- alas, we need this indirect way
+ local s = func
func = function(name) return find(name,s) end
end
files = files or { }
+ local noffiles = #files
for name in walkdir(path) do
if find(name,"^%.") then
--- skip
@@ -2874,12 +3151,9 @@ local function globfiles(path,recurse,func,files) -- func == pattern or function
globfiles(path .. "/" .. name,recurse,func,files)
end
elseif mode == "file" then
- if func then
- if func(name) then
- files[#files+1] = path .. "/" .. name
- end
- else
- files[#files+1] = path .. "/" .. name
+ if not func or func(name) then
+ noffiles = noffiles + 1
+ files[noffiles] = path .. "/" .. name
end
end
end
@@ -2897,17 +3171,15 @@ dir.globfiles = globfiles
-- print(dir.ls("*.tex"))
function dir.ls(pattern)
- return table.concat(glob(pattern),"\n")
+ return concat(glob(pattern),"\n")
end
---~ mkdirs("temp")
---~ mkdirs("a/b/c")
---~ mkdirs(".","/a/b/c")
---~ mkdirs("a","b","c")
local make_indeed = true -- false
-if string.find(os.getenv("PATH"),";") then -- os.type == "windows"
+local onwindows = os.type == "windows" or find(os.getenv("PATH"),";")
+
+if onwindows then
function dir.mkdirs(...)
local str, pth, t = "", "", { ... }
@@ -2956,64 +3228,21 @@ if string.find(os.getenv("PATH"),";") then -- os.type == "windows"
else
pth = pth .. "/" .. s
end
- if make_indeed and not lfs.isdir(pth) then
- lfs.mkdir(pth)
+ if make_indeed and not isdir(pth) then
+ mkdir(pth)
end
end
- return pth, (lfs.isdir(pth) == true)
- end
-
---~ print(dir.mkdirs("","","a","c"))
---~ print(dir.mkdirs("a"))
---~ print(dir.mkdirs("a:"))
---~ print(dir.mkdirs("a:/b/c"))
---~ print(dir.mkdirs("a:b/c"))
---~ print(dir.mkdirs("a:/bbb/c"))
---~ print(dir.mkdirs("/a/b/c"))
---~ print(dir.mkdirs("/aaa/b/c"))
---~ print(dir.mkdirs("//a/b/c"))
---~ print(dir.mkdirs("///a/b/c"))
---~ print(dir.mkdirs("a/bbb//ccc/"))
-
- function dir.expand_name(str) -- will be merged with cleanpath and collapsepath
- local first, nothing, last = match(str,"^(//)(//*)(.*)$")
- if first then
- first = dir.current() .. "/"
- end
- if not first then
- first, last = match(str,"^(//)/*(.*)$")
- end
- if not first then
- first, last = match(str,"^([a-zA-Z]:)(.*)$")
- if first and not find(last,"^/") then
- local d = lfs.currentdir()
- if lfs.chdir(first) then
- first = dir.current()
- end
- lfs.chdir(d)
- end
- end
- if not first then
- first, last = dir.current(), str
- end
- last = gsub(last,"//","/")
- last = gsub(last,"/%./","/")
- last = gsub(last,"^/*","")
- first = gsub(first,"/*$","")
- if last == "" then
- return first
- else
- return first .. "/" .. last
- end
+ return pth, (isdir(pth) == true)
end
+
else
function dir.mkdirs(...)
local str, pth, t = "", "", { ... }
for i=1,#t do
local s = t[i]
- if s ~= "" then
+ if s and s ~= "" then -- we catch nil and false
if str ~= "" then
str = str .. "/" .. s
else
@@ -3031,42 +3260,92 @@ else
else
pth = pth .. "/" .. s
end
- if make_indeed and not first and not lfs.isdir(pth) then
- lfs.mkdir(pth)
+ if make_indeed and not first and not isdir(pth) then
+ mkdir(pth)
end
end
else
pth = "."
for s in gmatch(str,"[^/]+") do
pth = pth .. "/" .. s
- if make_indeed and not lfs.isdir(pth) then
- lfs.mkdir(pth)
+ if make_indeed and not isdir(pth) then
+ mkdir(pth)
+ end
+ end
+ end
+ return pth, (isdir(pth) == true)
+ end
+
+
+end
+
+dir.makedirs = dir.mkdirs
+
+-- we can only define it here as it uses dir.current
+
+if onwindows then
+
+ function dir.expandname(str) -- will be merged with cleanpath and collapsepath
+ local first, nothing, last = match(str,"^(//)(//*)(.*)$")
+ if first then
+ first = dir.current() .. "/"
+ end
+ if not first then
+ first, last = match(str,"^(//)/*(.*)$")
+ end
+ if not first then
+ first, last = match(str,"^([a-zA-Z]:)(.*)$")
+ if first and not find(last,"^/") then
+ local d = currentdir()
+ if chdir(first) then
+ first = dir.current()
end
+ chdir(d)
end
end
- return pth, (lfs.isdir(pth) == true)
+ if not first then
+ first, last = dir.current(), str
+ end
+ last = gsub(last,"//","/")
+ last = gsub(last,"/%./","/")
+ last = gsub(last,"^/*","")
+ first = gsub(first,"/*$","")
+ if last == "" or last == "." then
+ return first
+ else
+ return first .. "/" .. last
+ end
end
---~ print(dir.mkdirs("","","a","c"))
---~ print(dir.mkdirs("a"))
---~ print(dir.mkdirs("/a/b/c"))
---~ print(dir.mkdirs("/aaa/b/c"))
---~ print(dir.mkdirs("//a/b/c"))
---~ print(dir.mkdirs("///a/b/c"))
---~ print(dir.mkdirs("a/bbb//ccc/"))
+else
- function dir.expand_name(str) -- will be merged with cleanpath and collapsepath
+ function dir.expandname(str) -- will be merged with cleanpath and collapsepath
if not find(str,"^/") then
- str = lfs.currentdir() .. "/" .. str
+ str = currentdir() .. "/" .. str
end
str = gsub(str,"//","/")
str = gsub(str,"/%./","/")
+ str = gsub(str,"(.)/%.$","%1")
return str
end
end
-dir.makedirs = dir.mkdirs
+file.expandname = dir.expandname -- for convenience
+
+local stack = { }
+
+function dir.push(newdir)
+ insert(stack,lfs.currentdir())
+end
+
+function dir.pop()
+ local d = remove(stack)
+ if d then
+ lfs.chdir(d)
+ end
+ return d
+end
end -- of closure
@@ -3081,12 +3360,17 @@ if not modules then modules = { } end modules ['l-boolean'] = {
license = "see context related readme files"
}
+local type, tonumber = type, tonumber
+
boolean = boolean or { }
+local boolean = boolean
-local type, tonumber = type, tonumber
+-- function boolean.tonumber(b)
+-- return b and 1 or 0 -- test and test and return or return
+-- end
function boolean.tonumber(b)
- if b then return 1 else return 0 end
+ if b then return 1 else return 0 end -- test and return or return
end
function toboolean(str,tolerant)
@@ -3110,7 +3394,9 @@ function toboolean(str,tolerant)
end
end
-function string.is_boolean(str)
+string.toboolean = toboolean
+
+function string.is_boolean(str,default)
if type(str) == "string" then
if str == "true" or str == "yes" or str == "on" or str == "t" then
return true
@@ -3118,18 +3404,301 @@ function string.is_boolean(str)
return false
end
end
- return nil
+ return default
end
-function boolean.alwaystrue()
- return true
+
+end -- of closure
+
+do -- create closure to overcome 200 locals limit
+
+if not modules then modules = { } end modules ['l-unicode'] = {
+ version = 1.001,
+ comment = "companion to luat-lib.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+if not unicode then
+
+ unicode = { utf8 = { } }
+
+ local floor, char = math.floor, string.char
+
+ function unicode.utf8.utfchar(n)
+ if n < 0x80 then
+ return char(n)
+ elseif n < 0x800 then
+ return char(
+ 0xC0 + floor(n/0x40),
+ 0x80 + (n % 0x40)
+ )
+ elseif n < 0x10000 then
+ return char(
+ 0xE0 + floor(n/0x1000),
+ 0x80 + (floor(n/0x40) % 0x40),
+ 0x80 + (n % 0x40)
+ )
+ elseif n < 0x40000 then
+ return char(
+ 0xF0 + floor(n/0x40000),
+ 0x80 + floor(n/0x1000),
+ 0x80 + (floor(n/0x40) % 0x40),
+ 0x80 + (n % 0x40)
+ )
+ else
+ -- return char(
+ -- 0xF1 + floor(n/0x1000000),
+ -- 0x80 + floor(n/0x40000),
+ -- 0x80 + floor(n/0x1000),
+ -- 0x80 + (floor(n/0x40) % 0x40),
+ -- 0x80 + (n % 0x40)
+ -- )
+ return "?"
+ end
+ end
+
end
-function boolean.falsetrue()
- return false
+local unicode = unicode
+
+utf = utf or unicode.utf8
+
+local concat = table.concat
+local utfchar, utfbyte, utfgsub = utf.char, utf.byte, utf.gsub
+local char, byte, find, bytepairs, utfvalues, format = string.char, string.byte, string.find, string.bytepairs, string.utfvalues, string.format
+local type = type
+
+local utfsplitlines = string.utfsplitlines
+
+-- 0 EF BB BF UTF-8
+-- 1 FF FE UTF-16-little-endian
+-- 2 FE FF UTF-16-big-endian
+-- 3 FF FE 00 00 UTF-32-little-endian
+-- 4 00 00 FE FF UTF-32-big-endian
+
+unicode.utfname = {
+ [0] = 'utf-8',
+ [1] = 'utf-16-le',
+ [2] = 'utf-16-be',
+ [3] = 'utf-32-le',
+ [4] = 'utf-32-be'
+}
+
+-- \000 fails in <= 5.0 but is valid in >=5.1 where %z is depricated
+
+function unicode.utftype(f)
+ local str = f:read(4)
+ if not str then
+ f:seek('set')
+ return 0
+ -- elseif find(str,"^%z%z\254\255") then -- depricated
+ -- elseif find(str,"^\000\000\254\255") then -- not permitted and bugged
+ elseif find(str,"\000\000\254\255",1,true) then -- seems to work okay (TH)
+ return 4
+ -- elseif find(str,"^\255\254%z%z") then -- depricated
+ -- elseif find(str,"^\255\254\000\000") then -- not permitted and bugged
+ elseif find(str,"\255\254\000\000",1,true) then -- seems to work okay (TH)
+ return 3
+ elseif find(str,"^\254\255") then
+ f:seek('set',2)
+ return 2
+ elseif find(str,"^\255\254") then
+ f:seek('set',2)
+ return 1
+ elseif find(str,"^\239\187\191") then
+ f:seek('set',3)
+ return 0
+ else
+ f:seek('set')
+ return 0
+ end
+end
+
+
+
+local function utf16_to_utf8_be(t)
+ if type(t) == "string" then
+ t = utfsplitlines(str)
+ end
+ local result = { } -- we reuse result
+ for i=1,#t do
+ local r, more = 0, 0
+ for left, right in bytepairs(t[i]) do
+ if right then
+ local now = 256*left + right
+ if more > 0 then
+ now = (more-0xD800)*0x400 + (now-0xDC00) + 0x10000
+ more = 0
+ r = r + 1
+ result[r] = utfchar(now)
+ elseif now >= 0xD800 and now <= 0xDBFF then
+ more = now
+ else
+ r = r + 1
+ result[r] = utfchar(now)
+ end
+ end
+ end
+ t[i] = concat(result,"",1,r) -- we reused tmp, hence t
+ end
+ return t
+end
+
+local function utf16_to_utf8_le(t)
+ if type(t) == "string" then
+ t = utfsplitlines(str)
+ end
+ local result = { } -- we reuse result
+ for i=1,#t do
+ local r, more = 0, 0
+ for left, right in bytepairs(t[i]) do
+ if right then
+ local now = 256*right + left
+ if more > 0 then
+ now = (more-0xD800)*0x400 + (now-0xDC00) + 0x10000
+ more = 0
+ r = r + 1
+ result[r] = utfchar(now)
+ elseif now >= 0xD800 and now <= 0xDBFF then
+ more = now
+ else
+ r = r + 1
+ result[r] = utfchar(now)
+ end
+ end
+ end
+ t[i] = concat(result,"",1,r) -- we reused tmp, hence t
+ end
+ return t
+end
+
+local function utf32_to_utf8_be(str)
+ if type(t) == "string" then
+ t = utfsplitlines(str)
+ end
+ local result = { } -- we reuse result
+ for i=1,#t do
+ local r, more = 0, -1
+ for a,b in bytepairs(str) do
+ if a and b then
+ if more < 0 then
+ more = 256*256*256*a + 256*256*b
+ else
+ r = r + 1
+ result[t] = utfchar(more + 256*a + b)
+ more = -1
+ end
+ else
+ break
+ end
+ end
+ t[i] = concat(result,"",1,r)
+ end
+ return result
+end
+
+local function utf32_to_utf8_le(str)
+ if type(t) == "string" then
+ t = utfsplitlines(str)
+ end
+ local result = { } -- we reuse result
+ for i=1,#t do
+ local r, more = 0, -1
+ for a,b in bytepairs(str) do
+ if a and b then
+ if more < 0 then
+ more = 256*b + a
+ else
+ r = r + 1
+ result[t] = utfchar(more + 256*256*256*b + 256*256*a)
+ more = -1
+ end
+ else
+ break
+ end
+ end
+ t[i] = concat(result,"",1,r)
+ end
+ return result
+end
+
+unicode.utf32_to_utf8_be = utf32_to_utf8_be
+unicode.utf32_to_utf8_le = utf32_to_utf8_le
+unicode.utf16_to_utf8_be = utf16_to_utf8_be
+unicode.utf16_to_utf8_le = utf16_to_utf8_le
+
+function unicode.utf8_to_utf8(t)
+ return type(t) == "string" and utfsplitlines(t) or t
+end
+
+function unicode.utf16_to_utf8(t,endian)
+ return endian and utf16_to_utf8_be(t) or utf16_to_utf8_le(t) or t
+end
+
+function unicode.utf32_to_utf8(t,endian)
+ return endian and utf32_to_utf8_be(t) or utf32_to_utf8_le(t) or t
+end
+
+local function little(c)
+ local b = byte(c)
+ if b < 0x10000 then
+ return char(b%256,b/256)
+ else
+ b = b - 0x10000
+ local b1, b2 = b/1024 + 0xD800, b%1024 + 0xDC00
+ return char(b1%256,b1/256,b2%256,b2/256)
+ end
+end
+
+local function big(c)
+ local b = byte(c)
+ if b < 0x10000 then
+ return char(b/256,b%256)
+ else
+ b = b - 0x10000
+ local b1, b2 = b/1024 + 0xD800, b%1024 + 0xDC00
+ return char(b1/256,b1%256,b2/256,b2%256)
+ end
+end
+
+function unicode.utf8_to_utf16(str,littleendian)
+ if littleendian then
+ return char(255,254) .. utfgsub(str,".",little)
+ else
+ return char(254,255) .. utfgsub(str,".",big)
+ end
+end
+
+function unicode.utfcodes(str)
+ local t, n = { }, 0
+ for u in utfvalues(str) do
+ n = n + 1
+ t[n] = format("0x%04X",u)
+ end
+ return concat(t,separator or " ")
+end
+
+function unicode.ustring(s)
+ return format("U+%05X",type(s) == "number" and s or utfbyte(s))
+end
+
+function unicode.xstring(s)
+ return format("0x%05X",type(s) == "number" and s or utfbyte(s))
+end
+
+
+local lpegmatch = lpeg.match
+local utftype = lpeg.patterns.utftype
+
+function unicode.filetype(data)
+ return data and lpegmatch(utftype,data) or "unknown"
end
+
+
end -- of closure
do -- create closure to overcome 200 locals limit
@@ -3145,35 +3714,289 @@ if not modules then modules = { } end modules ['l-math'] = {
local floor, sin, cos, tan = math.floor, math.sin, math.cos, math.tan
if not math.round then
- function math.round(x)
- return floor(x + 0.5)
- end
+ function math.round(x) return floor(x + 0.5) end
end
if not math.div then
- function math.div(n,m)
- return floor(n/m)
- end
+ function math.div(n,m) return floor(n/m) end
end
if not math.mod then
- function math.mod(n,m)
- return n % m
- end
+ function math.mod(n,m) return n % m end
end
local pipi = 2*math.pi/360
-function math.sind(d)
- return sin(d*pipi)
+if not math.sind then
+ function math.sind(d) return sin(d*pipi) end
+ function math.cosd(d) return cos(d*pipi) end
+ function math.tand(d) return tan(d*pipi) end
end
-function math.cosd(d)
- return cos(d*pipi)
+if not math.odd then
+ function math.odd (n) return n % 2 == 0 end
+ function math.even(n) return n % 2 ~= 0 end
end
-function math.tand(d)
- return tan(d*pipi)
+
+end -- of closure
+
+do -- create closure to overcome 200 locals limit
+
+if not modules then modules = { } end modules ['util-tab'] = {
+ version = 1.001,
+ comment = "companion to luat-lib.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+utilities = utilities or {}
+utilities.tables = utilities.tables or { }
+local tables = utilities.tables
+
+local format, gmatch = string.format, string.gmatch
+local concat, insert, remove = table.concat, table.insert, table.remove
+local setmetatable, getmetatable, tonumber, tostring = setmetatable, getmetatable, tonumber, tostring
+
+function tables.definetable(target) -- defines undefined tables
+ local composed, t, n = nil, { }, 0
+ for name in gmatch(target,"([^%.]+)") do
+ n = n + 1
+ if composed then
+ composed = composed .. "." .. name
+ else
+ composed = name
+ end
+ t[n] = format("%s = %s or { }",composed,composed)
+ end
+ return concat(t,"\n")
+end
+
+function tables.accesstable(target)
+ local t = _G
+ for name in gmatch(target,"([^%.]+)") do
+ t = t[name]
+ end
+ return t
+end
+
+function tables.removevalue(t,value) -- todo: n
+ if value then
+ for i=1,#t do
+ if t[i] == value then
+ remove(t,i)
+ -- remove all, so no: return
+ end
+ end
+ end
+end
+
+function tables.insertbeforevalue(t,value,extra)
+ for i=1,#t do
+ if t[i] == extra then
+ remove(t,i)
+ end
+ end
+ for i=1,#t do
+ if t[i] == value then
+ insert(t,i,extra)
+ return
+ end
+ end
+ insert(t,1,extra)
+end
+
+function tables.insertaftervalue(t,value,extra)
+ for i=1,#t do
+ if t[i] == extra then
+ remove(t,i)
+ end
+ end
+ for i=1,#t do
+ if t[i] == value then
+ insert(t,i+1,extra)
+ return
+ end
+ end
+ insert(t,#t+1,extra)
+end
+
+-- experimental
+
+local function toxml(t,d,result)
+ for k, v in table.sortedpairs(t) do
+ if type(v) == "table" then
+ result[#result+1] = format("%s<%s>",d,k)
+ toxml(v,d.." ",result)
+ result[#result+1] = format("%s</%s>",d,k)
+ elseif tonumber(k) then
+ result[#result+1] = format("%s<entry n='%s'>%s</entry>",d,k,v,k)
+ else
+ result[#result+1] = format("%s<%s>%s</%s>",d,k,tostring(v),k)
+ end
+ end
+end
+
+function table.toxml(t,name,nobanner)
+ local noroot = name == false
+ local result = (nobanner or noroot) and { } or { "<?xml version='1.0' standalone='yes' ?>" }
+ if noroot then
+ toxml( t, "", result)
+ else
+ toxml( { [name or "root"] = t }, "", result)
+ end
+ return concat(result,"\n")
+end
+
+
+end -- of closure
+
+do -- create closure to overcome 200 locals limit
+
+if not modules then modules = { } end modules ['util-sto'] = {
+ 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 setmetatable, getmetatable = setmetatable, getmetatable
+
+utilities = utilities or { }
+utilities.storage = utilities.storage or { }
+local storage = utilities.storage
+
+function storage.mark(t)
+ if not t then
+ texio.write_nl("fatal error: storage '%s' cannot be marked",t)
+ os.exit()
+ end
+ local m = getmetatable(t)
+ if not m then
+ m = { }
+ setmetatable(t,m)
+ end
+ m.__storage__ = true
+ return t
+end
+
+function storage.allocate(t)
+ t = t or { }
+ local m = getmetatable(t)
+ if not m then
+ m = { }
+ setmetatable(t,m)
+ end
+ m.__storage__ = true
+ return t
+end
+
+function storage.marked(t)
+ local m = getmetatable(t)
+ return m and m.__storage__
+end
+
+function storage.checked(t)
+ if not t then
+ texio.write_nl("fatal error: storage '%s' has not been allocated",t)
+ os.exit()
+ end
+ return t
+end
+
+
+function storage.setinitializer(data,initialize)
+ local m = getmetatable(data) or { }
+ m.__index = function(data,k)
+ m.__index = nil -- so that we can access the entries during initializing
+ initialize()
+ return data[k]
+ end
+ setmetatable(data, m)
+end
+
+local keyisvalue = { __index = function(t,k)
+ t[k] = k
+ return k
+end }
+
+function storage.sparse(t)
+ t = t or { }
+ setmetatable(t,keyisvalue)
+ return t
+end
+
+-- table namespace ?
+
+local function f_empty () return "" end -- t,k
+local function f_self (t,k) t[k] = k return k end
+local function f_ignore() end -- t,k,v
+
+local t_empty = { __index = empty }
+local t_self = { __index = self }
+local t_ignore = { __newindex = ignore }
+
+function table.setmetatableindex(t,f)
+ local m = getmetatable(t)
+ if m then
+ if f == "empty" then
+ m.__index = f_empty
+ elseif f == "key" then
+ m.__index = f_self
+ else
+ m.__index = f
+ end
+ else
+ if f == "empty" then
+ setmetatable(t, t_empty)
+ elseif f == "key" then
+ setmetatable(t, t_self)
+ else
+ setmetatable(t,{ __index = f })
+ end
+ end
+end
+
+function table.setmetatablenewindex(t,f)
+ local m = getmetatable(t)
+ if m then
+ if f == "ignore" then
+ m.__newindex = f_ignore
+ else
+ m.__newindex = f
+ end
+ else
+ if f == "ignore" then
+ setmetatable(t, t_ignore)
+ else
+ setmetatable(t,{ __newindex = f })
+ end
+ end
+end
+
+function table.setmetatablecall(t,f)
+ local m = getmetatable(t)
+ if m then
+ m.__call = f
+ else
+ setmetatable(t,{ __call = f })
+ end
+end
+
+function table.setmetatablekey(t,key,value)
+ local m = getmetatable(t)
+ if not m then
+ m = { }
+ setmetatable(t,m)
+ end
+ m[key] = value
+end
+
+function table.getmetatablekey(t,key,value)
+ local m = getmetatable(t)
+ return m and m[key]
end
@@ -3181,7 +4004,7 @@ end -- of closure
do -- create closure to overcome 200 locals limit
-if not modules then modules = { } end modules ['l-utils'] = {
+if not modules then modules = { } end modules ['util-mrg'] = {
version = 1.001,
comment = "companion to luat-lib.mkiv",
author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
@@ -3191,89 +4014,85 @@ if not modules then modules = { } end modules ['l-utils'] = {
-- hm, quite unreadable
-local gsub = string.gsub
+local gsub, format = string.gsub, string.format
local concat = table.concat
local type, next = type, next
-if not utils then utils = { } end
-if not utils.merger then utils.merger = { } end
-if not utils.lua then utils.lua = { } end
+utilities = utilities or {}
+utilities.merger = utilities.merger or { } -- maybe mergers
+utilities.report = logs and logs.reporter("system") or print
+
+local merger = utilities.merger
+
+merger.strip_comment = true
+
+local m_begin_merge = "begin library merge"
+local m_end_merge = "end library merge"
+local m_begin_closure = "do -- create closure to overcome 200 locals limit"
+local m_end_closure = "end -- of closure"
-utils.merger.m_begin = "begin library merge"
-utils.merger.m_end = "end library merge"
-utils.merger.pattern =
+local m_pattern =
"%c+" ..
- "%-%-%s+" .. utils.merger.m_begin ..
+ "%-%-%s+" .. m_begin_merge ..
"%c+(.-)%c+" ..
- "%-%-%s+" .. utils.merger.m_end ..
+ "%-%-%s+" .. m_end_merge ..
"%c+"
-function utils.merger._self_fake_()
- return
- "-- " .. "created merged file" .. "\n\n" ..
- "-- " .. utils.merger.m_begin .. "\n\n" ..
- "-- " .. utils.merger.m_end .. "\n\n"
-end
+local m_format =
+ "\n\n-- " .. m_begin_merge ..
+ "\n%s\n" ..
+ "-- " .. m_end_merge .. "\n\n"
-function utils.report(...)
- print(...)
+local m_faked =
+ "-- " .. "created merged file" .. "\n\n" ..
+ "-- " .. m_begin_merge .. "\n\n" ..
+ "-- " .. m_end_merge .. "\n\n"
+
+local function self_fake()
+ return m_faked
end
-utils.merger.strip_comment = true
+local function self_nothing()
+ return ""
+end
-function utils.merger._self_load_(name)
- local f, data = io.open(name), ""
- if f then
- utils.report("reading merge from %s",name)
- data = f:read("*all")
- f:close()
+local function self_load(name)
+ local data = io.loaddata(name) or ""
+ if data == "" then
+ utilities.report("merge: unknown file %s",name)
else
- utils.report("unknown file to merge %s",name)
- end
- if data and utils.merger.strip_comment then
- -- saves some 20K
- data = gsub(data,"%-%-~[^\n\r]*[\r\n]", "")
+ utilities.report("merge: inserting %s",name)
end
return data or ""
end
-function utils.merger._self_save_(name, data)
+local function self_save(name, data)
if data ~= "" then
- local f = io.open(name,'w')
- if f then
- utils.report("saving merge from %s",name)
- f:write(data)
- f:close()
+ if merger.strip_comment then
+ -- saves some 20K
+ local n = #data
+ data = gsub(data,"%-%-~[^\n\r]*[\r\n]","")
+ utilities.report("merge: %s bytes of comment stripped, %s bytes of code left",n-#data,#data)
end
+ io.savedata(name,data)
+ utilities.report("merge: saving %s",name)
end
end
-function utils.merger._self_swap_(data,code)
- if data ~= "" then
- return (gsub(data,utils.merger.pattern, function(s)
- return "\n\n" .. "-- "..utils.merger.m_begin .. "\n" .. code .. "\n" .. "-- "..utils.merger.m_end .. "\n\n"
- end, 1))
- else
- return ""
- end
+local function self_swap(data,code)
+ return data ~= "" and (gsub(data,m_pattern, function() return format(m_format,code) end, 1)) or ""
end
---~ stripper:
---~
---~ data = gsub(data,"%-%-~[^\n]*\n","")
---~ data = gsub(data,"\n\n+","\n")
-
-function utils.merger._self_libs_(libs,list)
- local result, f, frozen = { }, nil, false
+local function self_libs(libs,list)
+ local result, f, frozen, foundpath = { }, nil, false, nil
result[#result+1] = "\n"
if type(libs) == 'string' then libs = { libs } end
if type(list) == 'string' then list = { list } end
- local foundpath = nil
for i=1,#libs do
local lib = libs[i]
for j=1,#list do
local pth = gsub(list[j],"\\","/") -- file.clean_path
- utils.report("checking library path %s",pth)
+ utilities.report("merge: checking library path %s",pth)
local name = pth .. "/" .. lib
if lfs.isfile(name) then
foundpath = pth
@@ -3282,76 +4101,75 @@ function utils.merger._self_libs_(libs,list)
if foundpath then break end
end
if foundpath then
- utils.report("using library path %s",foundpath)
+ utilities.report("merge: using library path %s",foundpath)
local right, wrong = { }, { }
for i=1,#libs do
local lib = libs[i]
local fullname = foundpath .. "/" .. lib
if lfs.isfile(fullname) then
- -- right[#right+1] = lib
- utils.report("merging library %s",fullname)
- result[#result+1] = "do -- create closure to overcome 200 locals limit"
+ utilities.report("merge: using library %s",fullname)
+ right[#right+1] = lib
+ result[#result+1] = m_begin_closure
result[#result+1] = io.loaddata(fullname,true)
- result[#result+1] = "end -- of closure"
+ result[#result+1] = m_end_closure
else
- -- wrong[#wrong+1] = lib
- utils.report("no library %s",fullname)
+ utilities.report("merge: skipping library %s",fullname)
+ wrong[#wrong+1] = lib
end
end
if #right > 0 then
- utils.report("merged libraries: %s",concat(right," "))
+ utilities.report("merge: used libraries: %s",concat(right," "))
end
if #wrong > 0 then
- utils.report("skipped libraries: %s",concat(wrong," "))
+ utilities.report("merge: skipped libraries: %s",concat(wrong," "))
end
else
- utils.report("no valid library path found")
+ utilities.report("merge: no valid library path found")
end
return concat(result, "\n\n")
end
-function utils.merger.selfcreate(libs,list,target)
+function merger.selfcreate(libs,list,target)
if target then
- utils.merger._self_save_(
- target,
- utils.merger._self_swap_(
- utils.merger._self_fake_(),
- utils.merger._self_libs_(libs,list)
- )
- )
+ self_save(target,self_swap(self_fake(),self_libs(libs,list)))
end
end
-function utils.merger.selfmerge(name,libs,list,target)
- utils.merger._self_save_(
- target or name,
- utils.merger._self_swap_(
- utils.merger._self_load_(name),
- utils.merger._self_libs_(libs,list)
- )
- )
+function merger.selfmerge(name,libs,list,target)
+ self_save(target or name,self_swap(self_load(name),self_libs(libs,list)))
end
-function utils.merger.selfclean(name)
- utils.merger._self_save_(
- name,
- utils.merger._self_swap_(
- utils.merger._self_load_(name),
- ""
- )
- )
+function merger.selfclean(name)
+ self_save(name,self_swap(self_load(name),self_nothing()))
end
-function utils.lua.compile(luafile, lucfile, cleanup, strip) -- defaults: cleanup=false strip=true
- -- utils.report("compiling",luafile,"into",lucfile)
+
+end -- of closure
+
+do -- create closure to overcome 200 locals limit
+
+if not modules then modules = { } end modules ['util-lua'] = {
+ version = 1.001,
+ comment = "companion to luat-lib.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+utilities = utilities or {}
+utilities.lua = utilities.lua or { }
+utilities.report = logs and logs.reporter("system") or print
+
+function utilities.lua.compile(luafile,lucfile,cleanup,strip) -- defaults: cleanup=false strip=true
+ utilities.report("lua: compiling %s into %s",luafile,lucfile)
os.remove(lucfile)
- local command = "-o " .. string.quote(lucfile) .. " " .. string.quote(luafile)
+ local command = "-o " .. string.quoted(lucfile) .. " " .. string.quoted(luafile)
if strip ~= false then
command = "-s " .. command
end
- local done = (os.spawn("texluac " .. command) == 0) or (os.spawn("luac " .. command) == 0)
+ local done = os.spawn("texluac " .. command) == 0 or os.spawn("luac " .. command) == 0
if done and cleanup == true and lfs.isfile(lucfile) and lfs.isfile(luafile) then
- -- utils.report("removing",luafile)
+ utilities.report("lua: removing %s",luafile)
os.remove(luafile)
end
return done
@@ -3359,11 +4177,16 @@ end
+
+
+
+
+
end -- of closure
do -- create closure to overcome 200 locals limit
-if not modules then modules = { } end modules ['l-aux'] = {
+if not modules then modules = { } end modules ['util-prs'] = {
version = 1.001,
comment = "companion to luat-lib.mkiv",
author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
@@ -3371,15 +4194,20 @@ if not modules then modules = { } end modules ['l-aux'] = {
license = "see context related readme files"
}
--- for inline, no store split : for s in string.gmatch(str,",* *([^,]+)") do .. end
+local P, R, V, C, Ct, Carg = lpeg.P, lpeg.R, lpeg.V, lpeg.C, lpeg.Ct, lpeg.Carg
+local lpegmatch = lpeg.match
+local concat, format, gmatch = table.concat, string.format, string.gmatch
+local tostring, type, next = tostring, type, next
-aux = aux or { }
+utilities = utilities or {}
+utilities.parsers = utilities.parsers or { }
+local parsers = utilities.parsers
+parsers.patterns = parsers.patterns or { }
-local concat, format, gmatch = table.concat, string.format, string.gmatch
-local tostring, type = tostring, type
-local lpegmatch = lpeg.match
+local setmetatableindex = table.setmetatableindex
+local sortedhash = table.sortedhash
-local P, R, V = lpeg.P, lpeg.R, lpeg.V
+-- we could use a Cf Cg construct
local escape, left, right = P("\\"), P('{'), P('}')
@@ -3388,29 +4216,35 @@ lpeg.patterns.balanced = P {
[2] = left * V(1) * right
}
-local space = lpeg.P(' ')
-local equal = lpeg.P("=")
-local comma = lpeg.P(",")
-local lbrace = lpeg.P("{")
-local rbrace = lpeg.P("}")
+local space = P(' ')
+local equal = P("=")
+local comma = P(",")
+local lbrace = P("{")
+local rbrace = P("}")
local nobrace = 1 - (lbrace+rbrace)
-local nested = lpeg.P { lbrace * (nobrace + lpeg.V(1))^0 * rbrace }
+local nested = P { lbrace * (nobrace + V(1))^0 * rbrace }
local spaces = space^0
-local value = lpeg.P(lbrace * lpeg.C((nobrace + nested)^0) * rbrace) + lpeg.C((nested + (1-comma))^0)
+lpeg.patterns.nested = nested
+
+local value = P(lbrace * C((nobrace + nested)^0) * rbrace) + C((nested + (1-comma))^0)
-local key = lpeg.C((1-equal-comma)^1)
-local pattern_a = (space+comma)^0 * (key * equal * value + key * lpeg.C(""))
+local key = C((1-equal-comma)^1)
+local pattern_a = (space+comma)^0 * (key * equal * value + key * C(""))
local pattern_c = (space+comma)^0 * (key * equal * value)
-local key = lpeg.C((1-space-equal-comma)^1)
-local pattern_b = spaces * comma^0 * spaces * (key * ((spaces * equal * spaces * value) + lpeg.C("")))
+local key = C((1-space-equal-comma)^1)
+local pattern_b = spaces * comma^0 * spaces * (key * ((spaces * equal * spaces * value) + C("")))
-- "a=1, b=2, c=3, d={a{b,c}d}, e=12345, f=xx{a{b,c}d}xx, g={}" : outer {} removes, leading spaces ignored
local hash = { }
-local function set(key,value) -- using Carg is slower here
+local function set(key,value)
+ hash[key] = value
+end
+
+local function set(key,value)
hash[key] = value
end
@@ -3418,11 +4252,11 @@ local pattern_a_s = (pattern_a/set)^1
local pattern_b_s = (pattern_b/set)^1
local pattern_c_s = (pattern_c/set)^1
-aux.settings_to_hash_pattern_a = pattern_a_s
-aux.settings_to_hash_pattern_b = pattern_b_s
-aux.settings_to_hash_pattern_c = pattern_c_s
+parsers.patterns.settings_to_hash_a = pattern_a_s
+parsers.patterns.settings_to_hash_b = pattern_b_s
+parsers.patterns.settings_to_hash_c = pattern_c_s
-function aux.make_settings_to_hash_pattern(set,how)
+function parsers.make_settings_to_hash_pattern(set,how)
if how == "strict" then
return (pattern_c/set)^1
elseif how == "tolerant" then
@@ -3432,21 +4266,17 @@ function aux.make_settings_to_hash_pattern(set,how)
end
end
-function aux.settings_to_hash(str,existing)
+function parsers.settings_to_hash(str,existing)
if str and str ~= "" then
hash = existing or { }
- if moretolerant then
- lpegmatch(pattern_b_s,str)
- else
- lpegmatch(pattern_a_s,str)
- end
+ lpegmatch(pattern_a_s,str)
return hash
else
return { }
end
end
-function aux.settings_to_hash_tolerant(str,existing)
+function parsers.settings_to_hash_tolerant(str,existing)
if str and str ~= "" then
hash = existing or { }
lpegmatch(pattern_b_s,str)
@@ -3456,7 +4286,7 @@ function aux.settings_to_hash_tolerant(str,existing)
end
end
-function aux.settings_to_hash_strict(str,existing)
+function parsers.settings_to_hash_strict(str,existing)
if str and str ~= "" then
hash = existing or { }
lpegmatch(pattern_c_s,str)
@@ -3467,16 +4297,16 @@ function aux.settings_to_hash_strict(str,existing)
end
local separator = comma * space^0
-local value = lpeg.P(lbrace * lpeg.C((nobrace + nested)^0) * rbrace) + lpeg.C((nested + (1-comma))^0)
-local pattern = lpeg.Ct(value*(separator*value)^0)
+local value = P(lbrace * C((nobrace + nested)^0) * rbrace) + C((nested + (1-comma))^0)
+local pattern = Ct(value*(separator*value)^0)
-- "aap, {noot}, mies" : outer {} removes, leading spaces ignored
-aux.settings_to_array_pattern = pattern
+parsers.patterns.settings_to_array = pattern
-- we could use a weak table as cache
-function aux.settings_to_array(str)
+function parsers.settings_to_array(str)
if not str or str == "" then
return { }
else
@@ -3488,16 +4318,16 @@ local function set(t,v)
t[#t+1] = v
end
-local value = lpeg.P(lpeg.Carg(1)*value) / set
-local pattern = value*(separator*value)^0 * lpeg.Carg(1)
+local value = P(Carg(1)*value) / set
+local pattern = value*(separator*value)^0 * Carg(1)
-function aux.add_settings_to_array(t,str)
+function parsers.add_settings_to_array(t,str)
return lpegmatch(pattern,str,nil,t)
end
-function aux.hash_to_string(h,separator,yes,no,strict,omit)
+function parsers.hash_to_string(h,separator,yes,no,strict,omit)
if h then
- local t, s = { }, table.sortedkeys(h)
+ local t, tn, s = { }, 0, table.sortedkeys(h)
omit = omit and table.tohash(omit)
for i=1,#s do
local key = s[i]
@@ -3506,15 +4336,19 @@ function aux.hash_to_string(h,separator,yes,no,strict,omit)
if type(value) == "boolean" then
if yes and no then
if value then
- t[#t+1] = key .. '=' .. yes
+ tn = tn + 1
+ t[tn] = key .. '=' .. yes
elseif not strict then
- t[#t+1] = key .. '=' .. no
+ tn = tn + 1
+ t[tn] = key .. '=' .. no
end
elseif value or not strict then
- t[#t+1] = key .. '=' .. tostring(value)
+ tn = tn + 1
+ t[tn] = key .. '=' .. tostring(value)
end
else
- t[#t+1] = key .. '=' .. value
+ tn = tn + 1
+ t[tn] = key .. '=' .. value
end
end
end
@@ -3524,7 +4358,7 @@ function aux.hash_to_string(h,separator,yes,no,strict,omit)
end
end
-function aux.array_to_string(a,separator)
+function parsers.array_to_string(a,separator)
if a then
return concat(a,separator or ",")
else
@@ -3532,107 +4366,160 @@ function aux.array_to_string(a,separator)
end
end
-function aux.settings_to_set(str,t)
+function parsers.settings_to_set(str,t) -- tohash? -- todo: lpeg -- duplicate anyway
t = t or { }
- for s in gmatch(str,"%s*([^,]+)") do
+-- for s in gmatch(str,"%s*([^, ]+)") do -- space added
+ for s in gmatch(str,"[^, ]+") do -- space added
t[s] = true
end
return t
end
-local value = lbrace * lpeg.C((nobrace + nested)^0) * rbrace
-local pattern = lpeg.Ct((space + value)^0)
+function parsers.simple_hash_to_string(h, separator)
+ local t, tn = { }, 0
+ for k, v in sortedhash(h) do
+ if v then
+ tn = tn + 1
+ t[tn] = k
+ end
+ end
+ return concat(t,separator or ",")
+end
+
+local value = lbrace * C((nobrace + nested)^0) * rbrace
+local pattern = Ct((space + value)^0)
-function aux.arguments_to_table(str)
+function parsers.arguments_to_table(str)
return lpegmatch(pattern,str)
end
--- temporary here
+-- temporary here (unoptimized)
-function aux.getparameters(self,class,parentclass,settings)
+function parsers.getparameters(self,class,parentclass,settings)
local sc = self[class]
if not sc then
- sc = table.clone(self[parent])
+ sc = { }
self[class] = sc
+ if parentclass then
+ local sp = self[parentclass]
+ if not sp then
+ sp = { }
+ self[parentclass] = sp
+ end
+ setmetatableindex(sc,sp)
+ end
end
- aux.settings_to_hash(settings,sc)
+ parsers.settings_to_hash(settings,sc)
+end
+
+function parsers.listitem(str)
+ return gmatch(str,"[^, ]+")
end
+
+end -- of closure
+
+do -- create closure to overcome 200 locals limit
+
+if not modules then modules = { } end modules ['util-fmt'] = {
+ version = 1.001,
+ comment = "companion to luat-lib.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+utilities = utilities or { }
+utilities.formatters = utilities.formatters or { }
+local formatters = utilities.formatters
+
+local concat, format = table.concat, string.format
+local tostring, type = tostring, type
+local strip = string.strip
+
+local P, R, Cs = lpeg.P, lpeg.R, lpeg.Cs
+local lpegmatch = lpeg.match
+
-- temporary here
-local digit = lpeg.R("09")
-local period = lpeg.P(".")
-local zero = lpeg.P("0")
+local digit = R("09")
+local period = P(".")
+local zero = P("0")
local trailingzeros = zero^0 * -digit -- suggested by Roberto R
local case_1 = period * trailingzeros / ""
local case_2 = period * (digit - trailingzeros)^1 * (trailingzeros / "")
local number = digit^1 * (case_1 + case_2)
-local stripper = lpeg.Cs((number + 1)^0)
+local stripper = Cs((number + 1)^0)
---~ local sample = "bla 11.00 bla 11 bla 0.1100 bla 1.00100 bla 0.00 bla 0.001 bla 1.1100 bla 0.100100100 bla 0.00100100100"
---~ collectgarbage("collect")
---~ str = string.rep(sample,10000)
---~ local ts = os.clock()
---~ lpegmatch(stripper,str)
---~ print(#str, os.clock()-ts, lpegmatch(stripper,sample))
-lpeg.patterns.strip_zeros = stripper
+lpeg.patterns.stripzeros = stripper
-function aux.strip_zeros(str)
+function formatters.stripzeros(str)
return lpegmatch(stripper,str)
end
-function aux.definetable(target) -- defines undefined tables
- local composed, t = nil, { }
- for name in gmatch(target,"([^%.]+)") do
- if composed then
- composed = composed .. "." .. name
- else
- composed = name
+function formatters.formatcolumns(result,between)
+ if result and #result > 0 then
+ between = between or " "
+ local widths, numbers = { }, { }
+ local first = result[1]
+ local n = #first
+ for i=1,n do
+ widths[i] = 0
+ end
+ for i=1,#result do
+ local r = result[i]
+ for j=1,n do
+ local rj = r[j]
+ local tj = type(rj)
+ if tj == "number" then
+ numbers[j] = true
+ end
+ if tj ~= "string" then
+ rj = tostring(rj)
+ r[j] = rj
+ end
+ local w = #rj
+ if w > widths[j] then
+ widths[j] = w
+ end
+ end
+ end
+ for i=1,n do
+ local w = widths[i]
+ if numbers[i] then
+ if w > 80 then
+ widths[i] = "%s" .. between
+ else
+ widths[i] = "%0" .. w .. "i" .. between
+ end
+ else
+ if w > 80 then
+ widths[i] = "%s" .. between
+ elseif w > 0 then
+ widths[i] = "%-" .. w .. "s" .. between
+ else
+ widths[i] = "%s"
+ end
+ end
+ end
+ local template = strip(concat(widths))
+ for i=1,#result do
+ local str = format(template,unpack(result[i]))
+ result[i] = strip(str)
end
- t[#t+1] = format("%s = %s or { }",composed,composed)
- end
- return concat(t,"\n")
-end
-
-function aux.accesstable(target)
- local t = _G
- for name in gmatch(target,"([^%.]+)") do
- t = t[name]
end
- return t
+ return result
end
---~ function string.commaseparated(str)
---~ return gmatch(str,"([^,%s]+)")
---~ end
-
--- as we use this a lot ...
-
---~ function aux.cachefunction(action,weak)
---~ local cache = { }
---~ if weak then
---~ setmetatable(cache, { __mode = "kv" } )
---~ end
---~ local function reminder(str)
---~ local found = cache[str]
---~ if not found then
---~ found = action(str)
---~ cache[str] = found
---~ end
---~ return found
---~ end
---~ return reminder, cache
---~ end
-
end -- of closure
do -- create closure to overcome 200 locals limit
-if not modules then modules = { } end modules ['trac-tra'] = {
+if not modules then modules = { } end modules ['util.deb'] = {
version = 1.001,
- comment = "companion to trac-tra.mkiv",
+ comment = "companion to luat-lib.mkiv",
author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright = "PRAGMA ADE / ConTeXt Development Team",
license = "see context related readme files"
@@ -3645,14 +4532,16 @@ if not modules then modules = { } end modules ['trac-tra'] = {
local debug = require "debug"
local getinfo = debug.getinfo
-local type, next = type, next
-local concat = table.concat
-local format, find, lower, gmatch, gsub = string.format, string.find, string.lower, string.gmatch, string.gsub
+local type, next, tostring = type, next, tostring
+local format, find = string.format, string.find
+local is_boolean = string.is_boolean
-debugger = debugger or { }
+utilities = utilities or { }
+utilities.debugger = utilities.debugger or { }
+local debugger = utilities.debugger
local counters = { }
-local names = { }
+local names = { }
-- one
@@ -3670,6 +4559,7 @@ local function hook()
end
end
end
+
local function getname(func)
local n = names[func]
if n then
@@ -3685,6 +4575,7 @@ local function getname(func)
return "unknown"
end
end
+
function debugger.showstats(printer,threshold)
printer = printer or texio.write or print
threshold = threshold or 0
@@ -3707,35 +4598,6 @@ end
-- two
---~ local function hook()
---~ local n = getinfo(2)
---~ if n.what=="C" and not n.name then
---~ local f = tostring(debug.traceback())
---~ local cf = counters[f]
---~ if cf == nil then
---~ counters[f] = 1
---~ names[f] = n
---~ else
---~ counters[f] = cf + 1
---~ end
---~ end
---~ end
---~ function debugger.showstats(printer,threshold)
---~ printer = printer or texio.write or print
---~ threshold = threshold or 0
---~ local total, grandtotal, functions = 0, 0, 0
---~ printer("\n") -- ugly but ok
---~ -- table.sort(counters)
---~ for func, count in next, counters do
---~ if count > threshold then
---~ printer(format("%8i %s", count, func))
---~ total = total + count
---~ end
---~ grandtotal = grandtotal + count
---~ functions = functions + 1
---~ end
---~ printer(format("functions: %s, total: %s, grand total: %s, threshold: %s\n", functions, total, grandtotal, threshold))
---~ end
-- rest
@@ -3753,85 +4615,335 @@ end
function debugger.disable()
debug.sethook()
---~ counters[debug.getinfo(2,"f").func] = nil
end
-function debugger.tracing()
- local n = tonumber(os.env['MTX.TRACE.CALLS']) or tonumber(os.env['MTX_TRACE_CALLS']) or 0
- if n > 0 then
- function debugger.tracing() return true end ; return true
+
+
+
+
+local is_node = node and node.is_node
+
+function inspect(i) -- global function
+ local ti = type(i)
+ if ti == "table" then
+ table.print(i,"table")
+ elseif is_node and is_node(i) then
+ table.print(nodes.astable(i),tostring(i))
else
- function debugger.tracing() return false end ; return false
+ print(tostring(i))
end
end
---~ debugger.enable()
---~ print(math.sin(1*.5))
---~ print(math.sin(1*.5))
---~ print(math.sin(1*.5))
---~ print(math.sin(1*.5))
---~ print(math.sin(1*.5))
+end -- of closure
---~ debugger.disable()
+do -- create closure to overcome 200 locals limit
---~ print("")
---~ debugger.showstats()
---~ print("")
---~ debugger.showstats(print,3)
+if not modules then modules = { } end modules ['trac-inf'] = {
+ version = 1.001,
+ comment = "companion to trac-inf.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- As we want to protect the global tables, we no longer store the timing
+-- in the tables themselves but in a hidden timers table so that we don't
+-- get warnings about assignments. This is more efficient than using rawset
+-- and rawget.
+
+local format = string.format
+local clock = os.gettimeofday or os.clock -- should go in environment
+local write_nl = texio.write_nl
+
+statistics = statistics or { }
+local statistics = statistics
+
+statistics.enable = true
+statistics.threshold = 0.05
+
+local statusinfo, n, registered, timers = { }, 0, { }, { }
+
+local function hastiming(instance)
+ return instance and timers[instance]
+end
+
+local function resettiming(instance)
+ timers[instance or "notimer"] = { timing = 0, loadtime = 0 }
+end
-setters = setters or { }
-setters.data = setters.data or { }
+local function starttiming(instance)
+ local timer = timers[instance or "notimer"]
+ if not timer then
+ timer = { }
+ timers[instance or "notimer"] = timer
+ end
+ local it = timer.timing
+ if not it then
+ it = 0
+ end
+ if it == 0 then
+ timer.starttime = clock()
+ if not timer.loadtime then
+ timer.loadtime = 0
+ end
+ end
+ timer.timing = it + 1
+end
---~ local function set(t,what,value)
---~ local data, done = t.data, t.done
---~ if type(what) == "string" then
---~ what = aux.settings_to_array(what) -- inefficient but ok
---~ end
---~ for i=1,#what do
---~ local w = what[i]
---~ for d, f in next, data do
---~ if done[d] then
---~ -- prevent recursion due to wildcards
---~ elseif find(d,w) then
---~ done[d] = true
---~ for i=1,#f do
---~ f[i](value)
---~ end
---~ end
---~ end
---~ end
---~ end
+local function stoptiming(instance, report)
+ local timer = timers[instance or "notimer"]
+ local it = timer.timing
+ if it > 1 then
+ timer.timing = it - 1
+ else
+ local starttime = timer.starttime
+ if starttime then
+ local stoptime = clock()
+ local loadtime = stoptime - starttime
+ timer.stoptime = stoptime
+ timer.loadtime = timer.loadtime + loadtime
+ if report then
+ statistics.report("load time %0.3f",loadtime)
+ end
+ timer.timing = 0
+ return loadtime
+ end
+ end
+ return 0
+end
-local function set(t,what,value)
+local function elapsedtime(instance)
+ local timer = timers[instance or "notimer"]
+ return format("%0.3f",timer and timer.loadtime or 0)
+end
+
+local function elapsedindeed(instance)
+ local timer = timers[instance or "notimer"]
+ return (timer and timer.loadtime or 0) > statistics.threshold
+end
+
+local function elapsedseconds(instance,rest) -- returns nil if 0 seconds
+ if elapsedindeed(instance) then
+ return format("%s seconds %s", elapsedtime(instance),rest or "")
+ end
+end
+
+statistics.hastiming = hastiming
+statistics.resettiming = resettiming
+statistics.starttiming = starttiming
+statistics.stoptiming = stoptiming
+statistics.elapsedtime = elapsedtime
+statistics.elapsedindeed = elapsedindeed
+statistics.elapsedseconds = elapsedseconds
+
+-- general function
+
+function statistics.register(tag,fnc)
+ if statistics.enable and type(fnc) == "function" then
+ local rt = registered[tag] or (#statusinfo + 1)
+ statusinfo[rt] = { tag, fnc }
+ registered[tag] = rt
+ if #tag > n then n = #tag end
+ end
+end
+
+function statistics.show(reporter)
+ if statistics.enable then
+ if not reporter then reporter = function(tag,data,n) write_nl(tag .. " " .. data) end end
+ -- this code will move
+ local register = statistics.register
+ register("luatex banner", function()
+ return string.lower(status.banner)
+ end)
+ register("control sequences", function()
+ return format("%s of %s", status.cs_count, status.hash_size+status.hash_extra)
+ end)
+ register("callbacks", function()
+ local total, indirect = status.callbacks or 0, status.indirect_callbacks or 0
+ return format("%s direct, %s indirect, %s total", total-indirect, indirect, total)
+ end)
+ collectgarbage("collect")
+ register("current memory usage", statistics.memused)
+ register("runtime",statistics.runtime)
+ for i=1,#statusinfo do
+ local s = statusinfo[i]
+ local r = s[2]()
+ if r then
+ reporter(s[1],r,n)
+ end
+ end
+ write_nl("") -- final newline
+ statistics.enable = false
+ end
+end
+
+local template, report_statistics, nn = nil, nil, 0 -- we only calcute it once
+
+function statistics.showjobstat(tag,data,n)
+ if not logs then
+ -- sorry
+ elseif type(data) == "table" then
+ for i=1,#data do
+ statistics.showjobstat(tag,data[i],n)
+ end
+ else
+ if not template or n > nn then
+ template, n = format("%%-%ss - %%s",n), nn
+ report_statistics = logs.reporter("mkiv lua stats")
+ end
+ report_statistics(format(template,tag,data))
+ end
+end
+
+function statistics.memused() -- no math.round yet -)
+ local round = math.round or math.floor
+ return format("%s MB (ctx: %s MB)",round(collectgarbage("count")/1000), round(status.luastate_bytes/1000000))
+end
+
+starttiming(statistics)
+
+function statistics.formatruntime(runtime) -- indirect so it can be overloaded and
+ return format("%s seconds", runtime) -- indeed that happens in cure-uti.lua
+end
+
+function statistics.runtime()
+ stoptiming(statistics)
+ return statistics.formatruntime(elapsedtime(statistics))
+end
+
+function statistics.timed(action,report)
+ report = report or logs.reporter("system")
+ starttiming("run")
+ action()
+ stoptiming("run")
+ report("total runtime: %s",elapsedtime("run"))
+end
+
+-- where, not really the best spot for this:
+
+commands = commands or { }
+
+function commands.resettimer(name)
+ resettiming(name or "whatever")
+ starttiming(name or "whatever")
+end
+
+function commands.elapsedtime(name)
+ stoptiming(name or "whatever")
+ tex.sprint(elapsedtime(name or "whatever"))
+end
+
+
+end -- of closure
+
+do -- create closure to overcome 200 locals limit
+
+if not modules then modules = { } end modules ['trac-set'] = { -- might become util-set.lua
+ version = 1.001,
+ comment = "companion to luat-lib.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local type, next, tostring = type, next, tostring
+local concat = table.concat
+local format, find, lower, gsub, escapedpattern = string.format, string.find, string.lower, string.gsub, string.escapedpattern
+local is_boolean = string.is_boolean
+local settings_to_hash = utilities.parsers.settings_to_hash
+local allocate = utilities.storage.allocate
+
+utilities = utilities or { }
+local utilities = utilities
+utilities.setters = utilities.setters or { }
+local setters = utilities.setters
+
+local data = { } -- maybe just local
+
+-- We can initialize from the cnf file. This is sort of tricky as
+-- later defined setters also need to be initialized then. If set
+-- this way, we need to ensure that they are not reset later on.
+
+local trace_initialize = false -- only for testing during development
+
+function setters.initialize(filename,name,values) -- filename only for diagnostics
+ local setter = data[name]
+ if setter then
+ local data = setter.data
+ if data then
+ for key, value in next, values do
+ -- key = gsub(key,"_",".")
+ value = is_boolean(value,value)
+ local functions = data[key]
+ if functions then
+ if #functions > 0 and not functions.value then
+ if trace_initialize then
+ setter.report("executing %s (%s -> %s)",key,filename,tostring(value))
+ end
+ for i=1,#functions do
+ functions[i](value)
+ end
+ functions.value = value
+ else
+ if trace_initialize then
+ setter.report("skipping %s (%s -> %s)",key,filename,tostring(value))
+ end
+ end
+ else
+ -- we do a simple preregistration i.e. not in the
+ -- list as it might be an obsolete entry
+ functions = { default = value }
+ data[key] = functions
+ if trace_initialize then
+ setter.report("storing %s (%s -> %s)",key,filename,tostring(value))
+ end
+ end
+ end
+ return true
+ end
+ end
+end
+
+-- user interface code
+
+local function set(t,what,newvalue)
local data, done = t.data, t.done
if type(what) == "string" then
- what = aux.settings_to_hash(what) -- inefficient but ok
+ what = settings_to_hash(what) -- inefficient but ok
+ end
+ if type(what) ~= "table" then
+ return
end
- for w, v in next, what do
- if v == "" then
- v = value
+ for w, value in next, what do
+ if value == "" then
+ value = newvalue
+ elseif not value then
+ value = false -- catch nil
else
- v = toboolean(v)
+ value = is_boolean(value,value)
end
- for d, f in next, data do
- if done[d] then
+ w = "^" .. escapedpattern(w,true) .. "$" -- new: anchored
+ for name, functions in next, data do
+ if done[name] then
-- prevent recursion due to wildcards
- elseif find(d,w) then
- done[d] = true
- for i=1,#f do
- f[i](v)
+ elseif find(name,w) then
+ done[name] = true
+ for i=1,#functions do
+ functions[i](value)
end
+ functions.value = value
end
end
end
end
local function reset(t)
- for d, f in next, t.data do
- for i=1,#f do
- f[i](false)
+ for name, functions in next, t.data do
+ for i=1,#functions do
+ functions[i](false)
end
+ functions.value = false
end
end
@@ -3852,32 +4964,51 @@ end
function setters.register(t,what,...)
local data = t.data
what = lower(what)
- local w = data[what]
- if not w then
- w = { }
- data[what] = w
+ local functions = data[what]
+ if not functions then
+ functions = { }
+ data[what] = functions
+ if trace_initialize then
+ t.report("defining %s",what)
+ end
end
+ local default = functions.default -- can be set from cnf file
for _, fnc in next, { ... } do
local typ = type(fnc)
- if typ == "function" then
- w[#w+1] = fnc
- elseif typ == "string" then
- w[#w+1] = function(value) set(t,fnc,value,nesting) end
+ if typ == "string" then
+ if trace_initialize then
+ t.report("coupling %s to %s",what,fnc)
+ end
+ local s = fnc -- else wrong reference
+ fnc = function(value) set(t,s,value) end
+ elseif typ ~= "function" then
+ fnc = nil
+ end
+ if fnc then
+ functions[#functions+1] = fnc
+ -- default: set at command line or in cnf file
+ -- value : set in tex run (needed when loading runtime)
+ local value = functions.value or default
+ if value ~= nil then
+ fnc(value)
+ functions.value = value
+ end
end
end
+ return false -- so we can use it in an assignment
end
function setters.enable(t,what)
local e = t.enable
t.enable, t.done = enable, { }
- enable(t,string.simpleesc(tostring(what)))
+ enable(t,what)
t.enable, t.done = e, { }
end
function setters.disable(t,what)
local e = t.disable
t.disable, t.done = disable, { }
- disable(t,string.simpleesc(tostring(what)))
+ disable(t,what)
t.disable, t.done = e, { }
end
@@ -3901,68 +5032,94 @@ function setters.list(t) -- pattern
end
function setters.show(t)
- commands.writestatus("","")
+ local category = t.name
local list = setters.list(t)
+ t.report()
for k=1,#list do
- commands.writestatus(t.name,list[k])
+ local name = list[k]
+ local functions = t.data[name]
+ if functions then
+ local value, default, modules = functions.value, functions.default, #functions
+ value = value == nil and "unset" or tostring(value)
+ default = default == nil and "unset" or tostring(default)
+ t.report("%-30s modules: %2i default: %6s value: %6s",name,modules,default,value)
+ end
end
- commands.writestatus("","")
+ t.report()
end
-- we could have used a bit of oo and the trackers:enable syntax but
-- there is already a lot of code around using the singular tracker
--- we could make this into a module
+-- we could make this into a module but we also want the rest avaliable
+
+local enable, disable, register, list, show = setters.enable, setters.disable, setters.register, setters.list, setters.show
+
+local function report(setter,...)
+ local report = logs and logs.report
+ if report then
+ report(setter.name,...)
+ else -- fallback, as this module is loaded before the logger
+ write_nl(format("%-15s : %s\n",setter.name,format(...)))
+ end
+end
function setters.new(name)
- local t
- t = {
- data = { },
+ local setter -- we need to access it in setter itself
+ setter = {
+ data = allocate(), -- indexed, but also default and value fields
name = name,
- enable = function(...) setters.enable (t,...) end,
- disable = function(...) setters.disable (t,...) end,
- register = function(...) setters.register(t,...) end,
- list = function(...) setters.list (t,...) end,
- show = function(...) setters.show (t,...) end,
+ report = function(...) report (setter,...) end,
+ enable = function(...) enable (setter,...) end,
+ disable = function(...) disable (setter,...) end,
+ register = function(...) register(setter,...) end,
+ list = function(...) list (setter,...) end,
+ show = function(...) show (setter,...) end,
}
- setters.data[name] = t
- return t
+ data[name] = setter
+ return setter
end
trackers = setters.new("trackers")
directives = setters.new("directives")
experiments = setters.new("experiments")
+local t_enable, t_disable, t_report = trackers .enable, trackers .disable, trackers .report
+local d_enable, d_disable, d_report = directives .enable, directives .disable, directives .report
+local e_enable, e_disable, e_report = experiments.enable, experiments.disable, experiments.report
+
-- nice trick: we overload two of the directives related functions with variants that
-- do tracing (itself using a tracker) .. proof of concept
local trace_directives = false local trace_directives = false trackers.register("system.directives", function(v) trace_directives = v end)
local trace_experiments = false local trace_experiments = false trackers.register("system.experiments", function(v) trace_experiments = v end)
-local e = directives.enable
-local d = directives.disable
-
function directives.enable(...)
- commands.writestatus("directives","enabling: %s",concat({...}," "))
- e(...)
+ if trace_directives then
+ d_report("enabling: %s",concat({...}," "))
+ end
+ d_enable(...)
end
function directives.disable(...)
- commands.writestatus("directives","disabling: %s",concat({...}," "))
- d(...)
+ if trace_directives then
+ d_report("disabling: %s",concat({...}," "))
+ end
+ d_disable(...)
end
-local e = experiments.enable
-local d = experiments.disable
-
function experiments.enable(...)
- commands.writestatus("experiments","enabling: %s",concat({...}," "))
- e(...)
+ if trace_experiments then
+ e_report("enabling: %s",concat({...}," "))
+ end
+ e_enable(...)
end
function experiments.disable(...)
- commands.writestatus("experiments","disabling: %s",concat({...}," "))
- d(...)
+ if trace_experiments then
+ e_report("disabling: %s",concat({...}," "))
+ end
+ e_disable(...)
end
-- a useful example
@@ -3971,6 +5128,1121 @@ directives.register("system.nostatistics", function(v)
statistics.enable = not v
end)
+directives.register("system.nolibraries", function(v)
+ libraries = nil -- we discard this tracing for security
+end)
+
+-- experiment
+
+local flags = environment and environment.engineflags
+
+if flags then
+ if trackers and flags.trackers then
+ setters.initialize("flags","trackers", settings_to_hash(flags.trackers))
+ -- t_enable(flags.trackers)
+ end
+ if directives and flags.directives then
+ setters.initialize("flags","directives", settings_to_hash(flags.directives))
+ -- d_enable(flags.directives)
+ end
+end
+
+-- here
+
+if texconfig then
+
+ local function set(k,v)
+ v = tonumber(v)
+ if v then
+ texconfig[k] = v
+ end
+ end
+
+ directives.register("luatex.expanddepth", function(v) set("expand_depth",v) end)
+ directives.register("luatex.hashextra", function(v) set("hash_extra",v) end)
+ directives.register("luatex.nestsize", function(v) set("nest_size",v) end)
+ directives.register("luatex.maxinopen", function(v) set("max_in_open",v) end)
+ directives.register("luatex.maxprintline", function(v) set("max_print_line",v) end)
+ directives.register("luatex.maxstrings", function(v) set("max_strings",v) end)
+ directives.register("luatex.paramsize", function(v) set("param_size",v) end)
+ directives.register("luatex.savesize", function(v) set("save_size",v) end)
+ directives.register("luatex.stacksize", function(v) set("stack_size",v) end)
+
+end
+
+
+end -- of closure
+
+do -- create closure to overcome 200 locals limit
+
+if not modules then modules = { } end modules ['trac-log'] = {
+ version = 1.001,
+ comment = "companion to trac-log.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- todo: less categories, more subcategories (e.g. nodes)
+
+
+local write_nl, write = texio and texio.write_nl or print, texio and texio.write or io.write
+local format, gmatch, find = string.format, string.gmatch, string.find
+local concat, insert, remove = table.concat, table.insert, table.remove
+local escapedpattern = string.escapedpattern
+local texcount = tex and tex.count
+local next, type = next, type
+
+local setmetatableindex = table.setmetatableindex
+
+--[[ldx--
+<p>This is a prelude to a more extensive logging module. We no longer
+provide <l n='xml'/> based logging a sparsing is relatively easy anyway.</p>
+--ldx]]--
+
+logs = logs or { }
+local logs = logs
+
+local moreinfo = [[
+More information about ConTeXt and the tools that come with it can be found at:
+
+maillist : ntg-context@ntg.nl / http://www.ntg.nl/mailman/listinfo/ntg-context
+webpage : http://www.pragma-ade.nl / http://tex.aanhet.net
+wiki : http://contextgarden.net
+]]
+
+-- basic loggers
+
+local function ignore() end
+
+setmetatableindex(logs, function(t,k) t[k] = ignore ; return ignore end)
+
+local report, subreport, status, settarget, setformats, settranslations
+
+local direct, subdirect, writer, pushtarget, poptarget
+
+if tex and tex.jobname or tex.formatname then
+
+ local valueiskey = { __index = function(t,k) t[k] = k return k end } -- will be helper
+
+ local target = "term and log"
+
+ logs.flush = io.flush
+
+ local formats = { } setmetatable(formats, valueiskey)
+ local translations = { } setmetatable(translations,valueiskey)
+
+ writer = function(...)
+ write_nl(target,...)
+ end
+
+ report = function(a,b,c,...)
+ if c then
+ write_nl(target,format("%-15s > %s\n",translations[a],format(formats[b],c,...)))
+ elseif b then
+ write_nl(target,format("%-15s > %s\n",translations[a],formats[b]))
+ elseif a then
+ write_nl(target,format("%-15s >\n", translations[a]))
+ else
+ write_nl(target,"\n")
+ end
+ end
+
+ direct = function(a,b,c,...)
+ if c then
+ return format("%-15s > %s",translations[a],format(formats[b],c,...))
+ elseif b then
+ return format("%-15s > %s",translations[a],formats[b])
+ elseif a then
+ return format("%-15s >", translations[a])
+ else
+ return ""
+ end
+ end
+
+ subreport = function(a,s,b,c,...)
+ if c then
+ write_nl(target,format("%-15s > %s > %s\n",translations[a],translations[s],format(formats[b],c,...)))
+ elseif b then
+ write_nl(target,format("%-15s > %s > %s\n",translations[a],translations[s],formats[b]))
+ elseif a then
+ write_nl(target,format("%-15s > %s >\n", translations[a],translations[s]))
+ else
+ write_nl(target,"\n")
+ end
+ end
+
+ subdirect = function(a,s,b,c,...)
+ if c then
+ return format("%-15s > %s > %s",translations[a],translations[s],format(formats[b],c,...))
+ elseif b then
+ return format("%-15s > %s > %s",translations[a],translations[s],formats[b])
+ elseif a then
+ return format("%-15s > %s >", translations[a],translations[s])
+ else
+ return ""
+ end
+ end
+
+ status = function(a,b,c,...)
+ if c then
+ write_nl(target,format("%-15s : %s\n",translations[a],format(formats[b],c,...)))
+ elseif b then
+ write_nl(target,format("%-15s : %s\n",translations[a],formats[b]))
+ elseif a then
+ write_nl(target,format("%-15s :\n", translations[a]))
+ else
+ write_nl(target,"\n")
+ end
+ end
+
+ local targets = {
+ logfile = "log",
+ log = "log",
+ file = "log",
+ console = "term",
+ terminal = "term",
+ both = "term and log",
+ }
+
+ settarget = function(whereto)
+ target = targets[whereto or "both"] or targets.both
+ if target == "term" or target == "term and log" then
+ logs.flush = io.flush
+ else
+ logs.flush = ignore
+ end
+ end
+
+ local stack = { }
+
+ pushtarget = function(newtarget)
+ insert(stack,target)
+ settarget(newtarget)
+ end
+
+ poptarget = function()
+ if #stack > 0 then
+ settarget(remove(stack))
+ end
+ end
+
+ setformats = function(f)
+ formats = f
+ end
+
+ settranslations = function(t)
+ translations = t
+ end
+
+else
+
+ logs.flush = ignore
+
+ writer = write_nl
+
+ report = function(a,b,c,...)
+ if c then
+ write_nl(format("%-15s | %s",a,format(b,c,...)))
+ elseif b then
+ write_nl(format("%-15s | %s",a,b))
+ elseif a then
+ write_nl(format("%-15s |", a))
+ else
+ write_nl("")
+ end
+ end
+
+ subreport = function(a,sub,b,c,...)
+ if c then
+ write_nl(format("%-15s | %s | %s",a,sub,format(b,c,...)))
+ elseif b then
+ write_nl(format("%-15s | %s | %s",a,sub,b))
+ elseif a then
+ write_nl(format("%-15s | %s |", a,sub))
+ else
+ write_nl("")
+ end
+ end
+
+ status = function(a,b,c,...) -- not to be used in lua anyway
+ if c then
+ write_nl(format("%-15s : %s\n",a,format(b,c,...)))
+ elseif b then
+ write_nl(format("%-15s : %s\n",a,b)) -- b can have %'s
+ elseif a then
+ write_nl(format("%-15s :\n", a))
+ else
+ write_nl("\n")
+ end
+ end
+
+ direct = ignore
+ subdirect = ignore
+
+ settarget = ignore
+ pushtarget = ignore
+ poptarget = ignore
+ setformats = ignore
+ settranslations = ignore
+
+end
+
+logs.report = report
+logs.subreport = subreport
+logs.status = status
+logs.settarget = settarget
+logs.pushtarget = pushtarget
+logs.poptarget = poptarget
+logs.setformats = setformats
+logs.settranslations = settranslations
+
+logs.direct = direct
+logs.subdirect = subdirect
+logs.writer = writer
+
+-- installer
+
+-- todo: renew (un) locks when a new one is added and wildcard
+
+local data, states = { }, nil
+
+function logs.reporter(category,subcategory)
+ local logger = data[category]
+ if not logger then
+ local state = false
+ if states == true then
+ state = true
+ elseif type(states) == "table" then
+ for c, _ in next, states do
+ if find(category,c) then
+ state = true
+ break
+ end
+ end
+ end
+ logger = {
+ reporters = { },
+ state = state,
+ }
+ data[category] = logger
+ end
+ local reporter = logger.reporters[subcategory or "default"]
+ if not reporter then
+ if subcategory then
+ reporter = function(...)
+ if not logger.state then
+ subreport(category,subcategory,...)
+ end
+ end
+ logger.reporters[subcategory] = reporter
+ else
+ local tag = category
+ reporter = function(...)
+ if not logger.state then
+ report(category,...)
+ end
+ end
+ logger.reporters.default = reporter
+ end
+ end
+ return reporter
+end
+
+logs.new = logs.reporter -- for old times sake
+
+-- context specicific: this ends up in the macro stream
+
+local ctxreport = logs.writer
+
+function logs.setmessenger(m)
+ ctxreport = m
+end
+
+function logs.messenger(category,subcategory)
+ -- we need to avoid catcode mess (todo: fast context)
+ if subcategory then
+ return function(...)
+ ctxreport(subdirect(category,subcategory,...))
+ end
+ else
+ return function(...)
+ ctxreport(direct(category,...))
+ end
+ end
+end
+
+-- so far
+
+local function setblocked(category,value)
+ if category == true then
+ -- lock all
+ category, value = "*", true
+ elseif category == false then
+ -- unlock all
+ category, value = "*", false
+ elseif value == nil then
+ -- lock selective
+ value = true
+ end
+ if category == "*" then
+ states = value
+ for k, v in next, data do
+ v.state = value
+ end
+ else
+ states = utilities.parsers.settings_to_hash(category)
+ for c, _ in next, states do
+ if data[c] then
+ v.state = value
+ else
+ c = escapedpattern(c,true)
+ for k, v in next, data do
+ if find(k,c) then
+ v.state = value
+ end
+ end
+ end
+ end
+ end
+end
+
+function logs.disable(category,value)
+ setblocked(category,value == nil and true or value)
+end
+
+function logs.enable(category)
+ setblocked(category,false)
+end
+
+function logs.categories()
+ return table.sortedkeys(data)
+end
+
+function logs.show()
+ local n, c, s, max = 0, 0, 0, 0
+ for category, v in table.sortedpairs(data) do
+ n = n + 1
+ local state = v.state
+ local reporters = v.reporters
+ local nc = #category
+ if nc > c then
+ c = nc
+ end
+ for subcategory, _ in next, reporters do
+ local ns = #subcategory
+ if ns > c then
+ s = ns
+ end
+ local m = nc + ns
+ if m > max then
+ max = m
+ end
+ end
+ local subcategories = concat(table.sortedkeys(reporters),", ")
+ if state == true then
+ state = "disabled"
+ elseif state == false then
+ state = "enabled"
+ else
+ state = "unknown"
+ end
+ -- no new here
+ report("logging","category: '%s', subcategories: '%s', state: '%s'",category,subcategories,state)
+ end
+ report("logging","categories: %s, max category: %s, max subcategory: %s, max combined: %s",n,c,s,max)
+end
+
+directives.register("logs.blocked", function(v)
+ setblocked(v,true)
+end)
+
+directives.register("logs.target", function(v)
+ settarget(v)
+end)
+
+-- tex specific loggers (might move elsewhere)
+
+local report_pages = logs.reporter("pages") -- not needed but saves checking when we grep for it
+
+local real, user, sub
+
+function logs.start_page_number()
+ real, user, sub = texcount.realpageno, texcount.userpageno, texcount.subpageno
+end
+
+function logs.stop_page_number()
+ if real > 0 then
+ if user > 0 then
+ if sub > 0 then
+ report_pages("flushing realpage %s, userpage %s, subpage %s",real,user,sub)
+ else
+ report_pages("flushing realpage %s, userpage %s",real,user)
+ end
+ else
+ report_pages("flushing realpage %s",real)
+ end
+ else
+ report_pages("flushing page")
+ end
+ logs.flush()
+end
+
+logs.report_job_stat = statistics and statistics.showjobstat
+
+local report_files = logs.reporter("files")
+
+local nesting = 0
+local verbose = false
+local hasscheme = url.hasscheme
+
+-- we don't have show_open and show_close callbacks yet
+
+function logs.show_open(name)
+ -- if hasscheme(name) ~= "virtual" then
+ -- if verbose then
+ -- nesting = nesting + 1
+ -- report_files("level %s, opening %s",nesting,name)
+ -- else
+ -- write(format("(%s",name)) -- tex adds a space
+ -- end
+ -- end
+end
+
+function logs.show_close(name)
+ -- if hasscheme(name) ~= "virtual" then
+ -- if verbose then
+ -- report_files("level %s, closing %s",nesting,name)
+ -- nesting = nesting - 1
+ -- else
+ -- write(")") -- tex adds a space
+ -- end
+ -- end
+end
+
+function logs.show_load(name)
+ -- if hasscheme(name) ~= "virtual" then
+ -- if verbose then
+ -- report_files("level %s, loading %s",nesting+1,name)
+ -- else
+ -- write(format("(%s)",name))
+ -- end
+ -- end
+end
+
+-- there may be scripts out there using this:
+
+local simple = logs.reporter("comment")
+
+logs.simple = simple
+logs.simpleline = simple
+
+-- obsolete
+
+function logs.setprogram () end -- obsolete
+function logs.extendbanner() end -- obsolete
+function logs.reportlines () end -- obsolete
+function logs.reportbanner() end -- obsolete
+function logs.reportline () end -- obsolete
+function logs.simplelines () end -- obsolete
+function logs.help () end -- obsolete
+
+-- applications
+
+local function reportlines(t,str)
+ if str then
+ for line in gmatch(str,"(.-)[\n\r]") do
+ t.report(line)
+ end
+ end
+end
+
+local function reportbanner(t)
+ local banner = t.banner
+ if banner then
+ t.report(banner)
+ t.report()
+ end
+end
+
+local function reporthelp(t,...)
+ local helpinfo = t.helpinfo
+ if type(helpinfo) == "string" then
+ reportlines(t,helpinfo)
+ elseif type(helpinfo) == "table" then
+ local tags = { ... }
+ for i=1,#tags do
+ reportlines(t,t.helpinfo[tags[i]])
+ if i < #tags then
+ t.report()
+ end
+ end
+ end
+end
+
+local function reportinfo(t)
+ t.report()
+ reportlines(t,moreinfo)
+end
+
+function logs.application(t)
+ t.name = t.name or "unknown"
+ t.banner = t.banner
+ t.report = logs.reporter(t.name)
+ t.help = function(...) reportbanner(t) ; reporthelp(t,...) ; reportinfo(t) end
+ t.identify = function() reportbanner(t) end
+ return t
+end
+
+-- somewhat special
+
+-- logging to a file
+
+
+function logs.system(whereto,process,jobname,category,...)
+ local message = format("%s %s => %s => %s => %s\r",os.date("%d/%m/%y %H:%m:%S"),process,jobname,category,format(...))
+ for i=1,10 do
+ local f = io.open(whereto,"a") -- we can consider keepint the file open
+ if f then
+ f:write(message)
+ f:close()
+ break
+ else
+ sleep(0.1)
+ end
+ end
+end
+
+local report_system = logs.reporter("system","logs")
+
+function logs.obsolete(old,new)
+ local o = loadstring("return " .. new)()
+ if type(o) == "function" then
+ return function(...)
+ report_system("function %s is obsolete, use %s",old,new)
+ loadstring(old .. "=" .. new .. " return ".. old)()(...)
+ end
+ elseif type(o) == "table" then
+ local t, m = { }, { }
+ m.__index = function(t,k)
+ report_system("table %s is obsolete, use %s",old,new)
+ m.__index, m.__newindex = o, o
+ return o[k]
+ end
+ m.__newindex = function(t,k,v)
+ report_system("table %s is obsolete, use %s",old,new)
+ m.__index, m.__newindex = o, o
+ o[k] = v
+ end
+ if libraries then
+ libraries.obsolete[old] = t -- true
+ end
+ setmetatable(t,m)
+ return t
+ end
+end
+
+if utilities then
+ utilities.report = report_system
+end
+
+if tex and tex.error then
+ function logs.texerrormessage(...) -- for the moment we put this function here
+ tex.error(format(...), { })
+ end
+else
+ function logs.texerrormessage(...)
+ print(format(...))
+ end
+end
+
+
+end -- of closure
+
+do -- create closure to overcome 200 locals limit
+
+if not modules then modules = { } end modules ['trac-pro'] = {
+ version = 1.001,
+ comment = "companion to luat-lib.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local getmetatable, setmetatable, rawset, type = getmetatable, setmetatable, rawset, type
+
+-- The protection implemented here is probably not that tight but good enough to catch
+-- problems due to naive usage.
+--
+-- There's a more extensive version (trac-xxx.lua) that supports nesting.
+--
+-- This will change when we have _ENV in lua 5.2+
+
+local trace_namespaces = false trackers.register("system.namespaces", function(v) trace_namespaces = v end)
+
+local report_system = logs.reporter("system","protection")
+
+namespaces = namespaces or { }
+local namespaces = namespaces
+
+local registered = { }
+
+local function report_index(k,name)
+ if trace_namespaces then
+ report_system("reference to '%s' in protected namespace '%s', %s",k,name,debug.traceback())
+ else
+ report_system("reference to '%s' in protected namespace '%s'",k,name)
+ end
+end
+
+local function report_newindex(k,name)
+ if trace_namespaces then
+ report_system("assignment to '%s' in protected namespace '%s', %s",k,name,debug.traceback())
+ else
+ report_system("assignment to '%s' in protected namespace '%s'",k,name)
+ end
+end
+
+local function register(name)
+ local data = name == "global" and _G or _G[name]
+ if not data then
+ return -- error
+ end
+ registered[name] = data
+ local m = getmetatable(data)
+ if not m then
+ m = { }
+ setmetatable(data,m)
+ end
+ local index, newindex = { }, { }
+ m.__saved__index = m.__index
+ m.__no__index = function(t,k)
+ if not index[k] then
+ index[k] = true
+ report_index(k,name)
+ end
+ return nil
+ end
+ m.__saved__newindex = m.__newindex
+ m.__no__newindex = function(t,k,v)
+ if not newindex[k] then
+ newindex[k] = true
+ report_newindex(k,name)
+ end
+ rawset(t,k,v)
+ end
+ m.__protection__depth = 0
+end
+
+local function private(name) -- maybe save name
+ local data = registered[name]
+ if not data then
+ data = _G[name]
+ if not data then
+ data = { }
+ _G[name] = data
+ end
+ register(name)
+ end
+ return data
+end
+
+local function protect(name)
+ local data = registered[name]
+ if not data then
+ return
+ end
+ local m = getmetatable(data)
+ local pd = m.__protection__depth
+ if pd > 0 then
+ m.__protection__depth = pd + 1
+ else
+ m.__save_d_index, m.__saved__newindex = m.__index, m.__newindex
+ m.__index, m.__newindex = m.__no__index, m.__no__newindex
+ m.__protection__depth = 1
+ end
+end
+
+local function unprotect(name)
+ local data = registered[name]
+ if not data then
+ return
+ end
+ local m = getmetatable(data)
+ local pd = m.__protection__depth
+ if pd > 1 then
+ m.__protection__depth = pd - 1
+ else
+ m.__index, m.__newindex = m.__saved__index, m.__saved__newindex
+ m.__protection__depth = 0
+ end
+end
+
+local function protectall()
+ for name, _ in next, registered do
+ if name ~= "global" then
+ protect(name)
+ end
+ end
+end
+
+local function unprotectall()
+ for name, _ in next, registered do
+ if name ~= "global" then
+ unprotect(name)
+ end
+ end
+end
+
+namespaces.register = register -- register when defined
+namespaces.private = private -- allocate and register if needed
+namespaces.protect = protect
+namespaces.unprotect = unprotect
+namespaces.protectall = protectall
+namespaces.unprotectall = unprotectall
+
+namespaces.private("namespaces") registered = { } register("global") -- unreachable
+
+directives.register("system.protect", function(v)
+ if v then
+ protectall()
+ else
+ unprotectall()
+ end
+end)
+
+directives.register("system.checkglobals", function(v)
+ if v then
+ report_system("enabling global namespace guard")
+ protect("global")
+ else
+ report_system("disabling global namespace guard")
+ unprotect("global")
+ end
+end)
+
+-- dummy section (will go to luat-dum.lua)
+
+
+
+
+
+
+
+end -- of closure
+
+do -- create closure to overcome 200 locals limit
+
+if not modules then modules = { } end modules ['luat-env'] = {
+ version = 1.001,
+ comment = "companion to luat-lib.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- A former version provided functionality for non embeded core
+-- scripts i.e. runtime library loading. Given the amount of
+-- Lua code we use now, this no longer makes sense. Much of this
+-- evolved before bytecode arrays were available and so a lot of
+-- code has disappeared already.
+
+local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
+
+local report_lua = logs.reporter("resolvers","lua")
+
+local allocate, mark = utilities.storage.allocate, utilities.storage.mark
+
+local format, sub, match, gsub, find = string.format, string.sub, string.match, string.gsub, string.find
+local unquoted, quoted = string.unquoted, string.quoted
+local concat = table.concat
+
+-- precautions
+
+os.setlocale(nil,nil) -- useless feature and even dangerous in luatex
+
+function os.setlocale()
+ -- no way you can mess with it
+end
+
+-- dirty tricks
+
+if arg and (arg[0] == 'luatex' or arg[0] == 'luatex.exe') and arg[1] == "--luaonly" then
+ arg[-1] = arg[0]
+ arg[ 0] = arg[2]
+ for k=3,#arg do
+ arg[k-2] = arg[k]
+ end
+ arg[#arg] = nil -- last
+ arg[#arg] = nil -- pre-last
+end
+
+-- environment
+
+environment = environment or { }
+local environment = environment
+
+environment.arguments = allocate()
+environment.files = allocate()
+environment.sortedflags = nil
+
+local mt = {
+ __index = function(_,k)
+ if k == "version" then
+ local version = tex.toks and tex.toks.contextversiontoks
+ if version and version ~= "" then
+ rawset(environment,"version",version)
+ return version
+ else
+ return "unknown"
+ end
+ elseif k == "jobname" or k == "formatname" then
+ local name = tex and tex[k]
+ if name or name== "" then
+ rawset(environment,k,name)
+ return name
+ else
+ return "unknown"
+ end
+ elseif k == "outputfilename" then
+ local name = environment.jobname
+ rawset(environment,k,name)
+ return name
+ end
+ end
+}
+
+setmetatable(environment,mt)
+
+function environment.initializearguments(arg)
+ local arguments, files = { }, { }
+ environment.arguments, environment.files, environment.sortedflags = arguments, files, nil
+ for index=1,#arg do
+ local argument = arg[index]
+ if index > 0 then
+ local flag, value = match(argument,"^%-+(.-)=(.-)$")
+ if flag then
+ arguments[flag] = unquoted(value or "")
+ else
+ flag = match(argument,"^%-+(.+)")
+ if flag then
+ arguments[flag] = true
+ else
+ files[#files+1] = argument
+ end
+ end
+ end
+ end
+ environment.ownname = environment.ownname or arg[0] or 'unknown.lua'
+end
+
+function environment.setargument(name,value)
+ environment.arguments[name] = value
+end
+
+-- todo: defaults, better checks e.g on type (boolean versus string)
+--
+-- tricky: too many hits when we support partials unless we add
+-- a registration of arguments so from now on we have 'partial'
+
+function environment.argument(name,partial)
+ local arguments, sortedflags = environment.arguments, environment.sortedflags
+ if arguments[name] then
+ return arguments[name]
+ elseif partial then
+ if not sortedflags then
+ sortedflags = allocate(table.sortedkeys(arguments))
+ for k=1,#sortedflags do
+ sortedflags[k] = "^" .. sortedflags[k]
+ end
+ environment.sortedflags = sortedflags
+ end
+ -- example of potential clash: ^mode ^modefile
+ for k=1,#sortedflags do
+ local v = sortedflags[k]
+ if find(name,v) then
+ return arguments[sub(v,2,#v)]
+ end
+ end
+ end
+ return nil
+end
+
+function environment.splitarguments(separator) -- rather special, cut-off before separator
+ local done, before, after = false, { }, { }
+ local originalarguments = environment.originalarguments
+ for k=1,#originalarguments do
+ local v = originalarguments[k]
+ if not done and v == separator then
+ done = true
+ elseif done then
+ after[#after+1] = v
+ else
+ before[#before+1] = v
+ end
+ end
+ return before, after
+end
+
+function environment.reconstructcommandline(arg,noquote)
+ arg = arg or environment.originalarguments
+ if noquote and #arg == 1 then
+ -- we could just do: return unquoted(resolvers.resolve(arg[i]))
+ local a = arg[1]
+ a = resolvers.resolve(a)
+ a = unquoted(a)
+ return a
+ elseif #arg > 0 then
+ local result = { }
+ for i=1,#arg do
+ -- we could just do: result[#result+1] = format("%q",unquoted(resolvers.resolve(arg[i])))
+ local a = arg[i]
+ a = resolvers.resolve(a)
+ a = unquoted(a)
+ a = gsub(a,'"','\\"') -- tricky
+ if find(a," ") then
+ result[#result+1] = quoted(a)
+ else
+ result[#result+1] = a
+ end
+ end
+ return concat(result," ")
+ else
+ return ""
+ end
+end
+
+
+if arg then
+
+ -- new, reconstruct quoted snippets (maybe better just remove the " then and add them later)
+ local newarg, instring = { }, false
+
+ for index=1,#arg do
+ local argument = arg[index]
+ if find(argument,"^\"") then
+ newarg[#newarg+1] = gsub(argument,"^\"","")
+ if not find(argument,"\"$") then
+ instring = true
+ end
+ elseif find(argument,"\"$") then
+ newarg[#newarg] = newarg[#newarg] .. " " .. gsub(argument,"\"$","")
+ instring = false
+ elseif instring then
+ newarg[#newarg] = newarg[#newarg] .. " " .. argument
+ else
+ newarg[#newarg+1] = argument
+ end
+ end
+ for i=1,-5,-1 do
+ newarg[i] = arg[i]
+ end
+
+ environment.initializearguments(newarg)
+
+ environment.originalarguments = mark(newarg)
+ environment.rawarguments = mark(arg)
+
+ arg = { } -- prevent duplicate handling
+
+end
+
+-- weird place ... depends on a not yet loaded module
+
+function environment.texfile(filename)
+ return resolvers.findfile(filename,'tex')
+end
+
+function environment.luafile(filename)
+ local resolved = resolvers.findfile(filename,'tex') or ""
+ if resolved ~= "" then
+ return resolved
+ end
+ resolved = resolvers.findfile(filename,'texmfscripts') or ""
+ if resolved ~= "" then
+ return resolved
+ end
+ return resolvers.findfile(filename,'luatexlibs') or ""
+end
+
+environment.loadedluacode = loadfile -- can be overloaded
+
+function environment.luafilechunk(filename,silent) -- used for loading lua bytecode in the format
+ filename = file.replacesuffix(filename, "lua")
+ local fullname = environment.luafile(filename)
+ if fullname and fullname ~= "" then
+ local data = environment.loadedluacode(fullname)
+ if trace_locating then
+ report_lua("loading file %s%s", fullname, not data and " failed" or "")
+ elseif not silent then
+ texio.write("<",data and "+ " or "- ",fullname,">")
+ end
+ return data
+ else
+ if trace_locating then
+ report_lua("unknown file %s", filename)
+ end
+ return nil
+ end
+end
+
+-- the next ones can use the previous ones / combine
+
+function environment.loadluafile(filename, version)
+ local lucname, luaname, chunk
+ local basename = file.removesuffix(filename)
+ if basename == filename then
+ lucname, luaname = basename .. ".luc", basename .. ".lua"
+ else
+ lucname, luaname = nil, basename -- forced suffix
+ end
+ -- when not overloaded by explicit suffix we look for a luc file first
+ local fullname = (lucname and environment.luafile(lucname)) or ""
+ if fullname ~= "" then
+ if trace_locating then
+ report_lua("loading %s", fullname)
+ end
+ chunk = loadfile(fullname) -- this way we don't need a file exists check
+ end
+ if chunk then
+ assert(chunk)()
+ if version then
+ -- we check of the version number of this chunk matches
+ local v = version -- can be nil
+ if modules and modules[filename] then
+ v = modules[filename].version -- new method
+ elseif versions and versions[filename] then
+ v = versions[filename] -- old method
+ end
+ if v == version then
+ return true
+ else
+ if trace_locating then
+ report_lua("version mismatch for %s: lua=%s, luc=%s", filename, v, version)
+ end
+ environment.loadluafile(filename)
+ end
+ else
+ return true
+ end
+ end
+ fullname = (luaname and environment.luafile(luaname)) or ""
+ if fullname ~= "" then
+ if trace_locating then
+ report_lua("loading %s", fullname)
+ end
+ chunk = loadfile(fullname) -- this way we don't need a file exists check
+ if not chunk then
+ if trace_locating then
+ report_lua("unknown file %s", filename)
+ end
+ else
+ assert(chunk)()
+ return true
+ end
+ end
+ return false
+end
end -- of closure
@@ -3991,6 +6263,8 @@ if not modules then modules = { } end modules ['lxml-tab'] = {
local trace_entities = false trackers.register("xml.entities", function(v) trace_entities = v end)
+local report_xml = logs and logs.reporter("xml","core") or function(...) print(format(...)) end
+
--[[ldx--
<p>The parser used here is inspired by the variant discussed in the lua book, but
handles comment and processing instructions, has a different structure, provides
@@ -4001,11 +6275,15 @@ The find based parser can be found in l-xml-edu.lua along with other older code.
<p>Beware, the interface may change. For instance at, ns, tg, dt may get more
verbose names. Once the code is stable we will also remove some tracing and
optimize the code.</p>
+
+<p>I might even decide to reimplement the parser using the latest <l n='lpeg'/> trickery
+as the current variant was written when <l n='lpeg'/> showed up and it's easier now to
+build tables in one go.</p>
--ldx]]--
xml = xml or { }
+local xml = xml
---~ local xml = xml
local concat, remove, insert = table.concat, table.remove, table.insert
local type, next, setmetatable, getmetatable, tonumber = type, next, setmetatable, getmetatable, tonumber
@@ -4129,7 +6407,7 @@ local dcache, hcache, acache = { }, { }, { }
local mt = { }
-function initialize_mt(root)
+local function initialize_mt(root)
mt = { __index = root } -- will be redefined later
end
@@ -4137,8 +6415,8 @@ function xml.setproperty(root,k,v)
getmetatable(root).__index[k] = v
end
-function xml.check_error(top,toclose)
- return ""
+function xml.checkerror(top,toclose)
+ return "" -- can be set
end
local function add_attribute(namespace,tag,value)
@@ -4194,9 +6472,9 @@ local function add_end(spacing, namespace, tag)
local toclose = remove(stack)
top = stack[#stack]
if #stack < 1 then
- errorstr = format("nothing to close with %s %s", tag, xml.check_error(top,toclose) or "")
+ errorstr = format("nothing to close with %s %s", tag, xml.checkerror(top,toclose) or "")
elseif toclose.tg ~= tag then -- no namespace check
- errorstr = format("unable to close %s with %s %s", toclose.tg, tag, xml.check_error(top,toclose) or "")
+ errorstr = format("unable to close %s with %s %s", toclose.tg, tag, xml.checkerror(top,toclose) or "")
end
dt = top.dt
dt[#dt+1] = toclose
@@ -4233,24 +6511,29 @@ local reported_attribute_errors = { }
local function attribute_value_error(str)
if not reported_attribute_errors[str] then
- logs.report("xml","invalid attribute value: %q",str)
+ report_xml("invalid attribute value: %q",str)
reported_attribute_errors[str] = true
at._error_ = str
end
return str
end
+
local function attribute_specification_error(str)
if not reported_attribute_errors[str] then
- logs.report("xml","invalid attribute specification: %q",str)
+ report_xml("invalid attribute specification: %q",str)
reported_attribute_errors[str] = true
at._error_ = str
end
return str
end
-function xml.unknown_dec_entity_format(str) return (str == "" and "&error;") or format("&%s;",str) end
-function xml.unknown_hex_entity_format(str) return format("&#x%s;",str) end
-function xml.unknown_any_entity_format(str) return format("&#x%s;",str) end
+xml.placeholders = {
+ unknown_dec_entity = function(str) return (str == "" and "&error;") or format("&%s;",str) end,
+ unknown_hex_entity = function(str) return format("&#x%s;",str) end,
+ unknown_any_entity = function(str) return format("&#x%s;",str) end,
+}
+
+local placeholders = xml.placeholders
local function fromhex(s)
local n = tonumber(s,16)
@@ -4304,18 +6587,18 @@ local function handle_hex_entity(str)
h = unify_predefined and predefined_unified[n]
if h then
if trace_entities then
- logs.report("xml","utfize, converting hex entity &#x%s; into %s",str,h)
+ report_xml("utfize, converting hex entity &#x%s; into %s",str,h)
end
elseif utfize then
- h = (n and utfchar(n)) or xml.unknown_hex_entity_format(str) or ""
+ h = (n and utfchar(n)) or xml.unknown_hex_entity(str) or ""
if not n then
- logs.report("xml","utfize, ignoring hex entity &#x%s;",str)
+ report_xml("utfize, ignoring hex entity &#x%s;",str)
elseif trace_entities then
- logs.report("xml","utfize, converting hex entity &#x%s; into %s",str,h)
+ report_xml("utfize, converting hex entity &#x%s; into %s",str,h)
end
else
if trace_entities then
- logs.report("xml","found entity &#x%s;",str)
+ report_xml("found entity &#x%s;",str)
end
h = "&#x" .. str .. ";"
end
@@ -4331,18 +6614,18 @@ local function handle_dec_entity(str)
d = unify_predefined and predefined_unified[n]
if d then
if trace_entities then
- logs.report("xml","utfize, converting dec entity &#%s; into %s",str,d)
+ report_xml("utfize, converting dec entity &#%s; into %s",str,d)
end
elseif utfize then
- d = (n and utfchar(n)) or xml.unknown_dec_entity_format(str) or ""
+ d = (n and utfchar(n)) or placeholders.unknown_dec_entity(str) or ""
if not n then
- logs.report("xml","utfize, ignoring dec entity &#%s;",str)
+ report_xml("utfize, ignoring dec entity &#%s;",str)
elseif trace_entities then
- logs.report("xml","utfize, converting dec entity &#%s; into %s",str,h)
+ report_xml("utfize, converting dec entity &#%s; into %s",str,h)
end
else
if trace_entities then
- logs.report("xml","found entity &#%s;",str)
+ report_xml("found entity &#%s;",str)
end
d = "&#" .. str .. ";"
end
@@ -4367,20 +6650,21 @@ local function handle_any_entity(str)
end
if a then
if trace_entities then
- logs.report("xml","resolved entity &%s; -> %s (internal)",str,a)
+ report_xml("resolved entity &%s; -> %s (internal)",str,a)
end
a = lpegmatch(parsedentity,a) or a
else
- if xml.unknown_any_entity_format then
- a = xml.unknown_any_entity_format(str) or ""
+ local unknown_any_entity = placeholders.unknown_any_entity
+ if unknown_any_entity then
+ a = unknown_any_entity(str) or ""
end
if a then
if trace_entities then
- logs.report("xml","resolved entity &%s; -> %s (external)",str,a)
+ report_xml("resolved entity &%s; -> %s (external)",str,a)
end
else
if trace_entities then
- logs.report("xml","keeping entity &%s;",str)
+ report_xml("keeping entity &%s;",str)
end
if str == "" then
a = "&error;"
@@ -4392,7 +6676,7 @@ local function handle_any_entity(str)
acache[str] = a
elseif trace_entities then
if not acache[str] then
- logs.report("xml","converting entity &%s; into %s",str,a)
+ report_xml("converting entity &%s; into %s",str,a)
acache[str] = a
end
end
@@ -4401,7 +6685,7 @@ local function handle_any_entity(str)
local a = acache[str]
if not a then
if trace_entities then
- logs.report("xml","found entity &%s;",str)
+ report_xml("found entity &%s;",str)
end
a = resolve_predefined and predefined_simplified[str]
if a then
@@ -4420,7 +6704,7 @@ local function handle_any_entity(str)
end
local function handle_end_entity(chr)
- logs.report("xml","error in entity, %q found instead of ';'",chr)
+ report_xml("error in entity, %q found instead of ';'",chr)
end
local space = S(' \r\n\t')
@@ -4555,7 +6839,7 @@ local function xmlconvert(data, settings)
resolve_predefined = settings.resolve_predefined_entities -- in case we have escaped entities
unify_predefined = settings.unify_predefined_entities -- &#038; -> &amp;
cleanup = settings.text_cleanup
- stack, top, at, xmlns, errorstr, result, entities = { }, { }, { }, { }, nil, nil, settings.entities or { }
+ stack, top, at, xmlns, errorstr, entities = { }, { }, { }, { }, nil, settings.entities or { }
acache, hcache, dcache = { }, { }, { } -- not stored
reported_attribute_errors = { }
if settings.parent_root then
@@ -4583,16 +6867,17 @@ local function xmlconvert(data, settings)
else
errorstr = "invalid xml file - no text at all"
end
+ local result
if errorstr and errorstr ~= "" then
result = { dt = { { ns = "", tg = "error", dt = { errorstr }, at={ }, er = true } } }
setmetatable(stack, mt)
- local error_handler = settings.error_handler
- if error_handler == false then
+ local errorhandler = settings.error_handler
+ if errorhandler == false then
-- no error message
else
- error_handler = error_handler or xml.error_handler
- if error_handler then
- xml.error_handler("load",errorstr)
+ errorhandler = errorhandler or xml.errorhandler
+ if errorhandler then
+ xml.errorhandler("load",errorstr)
end
end
else
@@ -4652,7 +6937,7 @@ function xml.is_valid(root)
return root and not root.error
end
-xml.error_handler = (logs and logs.report) or (input and logs.report) or print
+xml.errorhandler = report
--[[ldx--
<p>We cannot load an <l n='lpeg'/> from a filehandle so we need to load
@@ -4763,7 +7048,7 @@ local function verbose_element(e,handlers)
ats[#ats+1] = format('%s=%q',k,v)
end
end
- if ern and trace_remap and ern ~= ens then
+ if ern and trace_entities and ern ~= ens then
ens = ern
end
if ens ~= "" then
@@ -4868,7 +7153,7 @@ local function serialize(e,handlers,...)
-- elseif type(e) == "string" then
-- functions["@tx@"](e,handlers)
else
- functions["@dc@"](e,handlers)
+ functions["@dc@"](e,handlers) -- dc ?
end
if finalize then
return finalize()
@@ -4894,7 +7179,7 @@ local function newhandlers(settings)
if settings then
for k,v in next, settings do
if type(v) == "table" then
- tk = t[k] if not tk then tk = { } t[k] = tk end
+ local tk = t[k] if not tk then tk = { } t[k] = tk end
for kk,vv in next, v do
tk[kk] = vv
end
@@ -4906,6 +7191,7 @@ local function newhandlers(settings)
handlers[settings.name] = t
end
end
+ utilities.storage.mark(t)
return t
end
@@ -5005,7 +7291,7 @@ local function xmltext(root) -- inline
return (root and xmltostring(root)) or ""
end
-function initialize_mt(root)
+initialize_mt = function(root) -- redefinition
mt = { __tostring = xmltext, __index = root }
end
@@ -5040,7 +7326,6 @@ xml.string = xmlstring
<p>A few helpers:</p>
--ldx]]--
---~ xmlsetproperty(root,"settings",settings)
function xml.settings(e)
while e do
@@ -5150,6 +7435,7 @@ function xml.makestandalone(root)
end
end
end
+ return root
end
@@ -5166,11 +7452,14 @@ if not modules then modules = { } end modules ['lxml-pth'] = {
}
-- e.ni is only valid after a filter run
+-- todo: B/C/[get first match]
local concat, remove, insert = table.concat, table.remove, table.insert
local type, next, tonumber, tostring, setmetatable, loadstring = type, next, tonumber, tostring, setmetatable, loadstring
local format, upper, lower, gmatch, gsub, find, rep = string.format, string.upper, string.lower, string.gmatch, string.gsub, string.find, string.rep
-local lpegmatch = lpeg.match
+local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns
+
+local setmetatableindex = table.setmetatableindex
-- beware, this is not xpath ... e.g. position is different (currently) and
-- we have reverse-sibling as reversed preceding sibling
@@ -5202,23 +7491,33 @@ local trace_lpath = false if trackers then trackers.register("xml.path",
local trace_lparse = false if trackers then trackers.register("xml.parse", function(v) trace_lparse = v end) end
local trace_lprofile = false if trackers then trackers.register("xml.profile", function(v) trace_lpath = v trace_lparse = v trace_lprofile = v end) end
+local report_lpath = logs.reporter("xml","lpath")
+
--[[ldx--
<p>We've now arrived at an interesting part: accessing the tree using a subset
of <l n='xpath'/> and since we're not compatible we call it <l n='lpath'/>. We
will explain more about its usage in other documents.</p>
--ldx]]--
+local xml = xml
+
local lpathcalls = 0 function xml.lpathcalls () return lpathcalls end
local lpathcached = 0 function xml.lpathcached() return lpathcached end
-xml.functions = xml.functions or { } -- internal
-xml.expressions = xml.expressions or { } -- in expressions
-xml.finalizers = xml.finalizers or { } -- fast do-with ... (with return value other than collection)
-xml.specialhandler = xml.specialhandler or { }
+xml.functions = xml.functions or { } -- internal
+local functions = xml.functions
+
+xml.expressions = xml.expressions or { } -- in expressions
+local expressions = xml.expressions
+
+xml.finalizers = xml.finalizers or { } -- fast do-with ... (with return value other than collection)
+local finalizers = xml.finalizers
-local functions = xml.functions
-local expressions = xml.expressions
-local finalizers = xml.finalizers
+xml.specialhandler = xml.specialhandler or { }
+local specialhandler = xml.specialhandler
+
+lpegpatterns.xml = lpegpatterns.xml or { }
+local xmlpatterns = lpegpatterns.xml
finalizers.xml = finalizers.xml or { }
finalizers.tex = finalizers.tex or { }
@@ -5228,14 +7527,14 @@ local function fallback (t, name)
if fn then
t[name] = fn
else
- logs.report("xml","unknown sub finalizer '%s'",tostring(name))
+ report_lpath("unknown sub finalizer '%s'",tostring(name))
fn = function() end
end
return fn
end
-setmetatable(finalizers.xml, { __index = fallback })
-setmetatable(finalizers.tex, { __index = fallback })
+setmetatableindex(finalizers.xml, fallback)
+setmetatableindex(finalizers.tex, fallback)
xml.defaultprotocol = "xml"
@@ -5283,102 +7582,106 @@ apply_axis['root'] = function(list)
rt = ll
end
end
- collected[#collected+1] = rt
+ collected[l] = rt
end
return collected
end
apply_axis['self'] = function(list)
---~ local collected = { }
---~ for l=1,#list do
---~ collected[#collected+1] = list[l]
---~ end
---~ return collected
return list
end
apply_axis['child'] = function(list)
- local collected = { }
+ local collected, c = { }, 0
for l=1,#list do
local ll = list[l]
local dt = ll.dt
- local en = 0
- for k=1,#dt do
- local dk = dt[k]
- if dk.tg then
- collected[#collected+1] = dk
- dk.ni = k -- refresh
- en = en + 1
- dk.ei = en
+ if dt then -- weird that this is needed
+ local en = 0
+ for k=1,#dt do
+ local dk = dt[k]
+ if dk.tg then
+ c = c + 1
+ collected[c] = dk
+ dk.ni = k -- refresh
+ en = en + 1
+ dk.ei = en
+ end
end
+ ll.en = en
end
- ll.en = en
end
return collected
end
-local function collect(list,collected)
+local function collect(list,collected,c)
local dt = list.dt
if dt then
local en = 0
for k=1,#dt do
local dk = dt[k]
if dk.tg then
- collected[#collected+1] = dk
+ c = c + 1
+ collected[c] = dk
dk.ni = k -- refresh
en = en + 1
dk.ei = en
- collect(dk,collected)
+ c = collect(dk,collected,c)
end
end
list.en = en
end
+ return c
end
apply_axis['descendant'] = function(list)
- local collected = { }
+ local collected, c = { }, 0
for l=1,#list do
- collect(list[l],collected)
+ c = collect(list[l],collected,c)
end
return collected
end
-local function collect(list,collected)
+local function collect(list,collected,c)
local dt = list.dt
if dt then
local en = 0
for k=1,#dt do
local dk = dt[k]
if dk.tg then
- collected[#collected+1] = dk
+ c = c + 1
+ collected[c] = dk
dk.ni = k -- refresh
en = en + 1
dk.ei = en
- collect(dk,collected)
+ c = collect(dk,collected,c)
end
end
list.en = en
end
+ return c
end
apply_axis['descendant-or-self'] = function(list)
- local collected = { }
+ local collected, c = { }, 0
for l=1,#list do
local ll = list[l]
if ll.special ~= true then -- catch double root
- collected[#collected+1] = ll
+ c = c + 1
+ collected[c] = ll
end
- collect(ll,collected)
+ c = collect(ll,collected,c)
end
return collected
end
apply_axis['ancestor'] = function(list)
- local collected = { }
+ local collected, c = { }, 0
for l=1,#list do
local ll = list[l]
while ll do
ll = ll.__p__
if ll then
- collected[#collected+1] = ll
+ c = c + 1
+ collected[c] = ll
end
end
end
@@ -5386,14 +7689,16 @@ apply_axis['ancestor'] = function(list)
end
apply_axis['ancestor-or-self'] = function(list)
- local collected = { }
+ local collected, c = { }, 0
for l=1,#list do
local ll = list[l]
- collected[#collected+1] = ll
+ c = c + 1
+ collected[c] = ll
while ll do
ll = ll.__p__
if ll then
- collected[#collected+1] = ll
+ c = c + 1
+ collected[c] = ll
end
end
end
@@ -5401,11 +7706,12 @@ apply_axis['ancestor-or-self'] = function(list)
end
apply_axis['parent'] = function(list)
- local collected = { }
+ local collected, c = { }, 0
for l=1,#list do
local pl = list[l].__p__
if pl then
- collected[#collected+1] = pl
+ c = c + 1
+ collected[c] = pl
end
end
return collected
@@ -5420,43 +7726,15 @@ apply_axis['namespace'] = function(list)
end
apply_axis['following'] = function(list) -- incomplete
---~ local collected = { }
---~ for l=1,#list do
---~ local ll = list[l]
---~ local p = ll.__p__
---~ local d = p.dt
---~ for i=ll.ni+1,#d do
---~ local di = d[i]
---~ if type(di) == "table" then
---~ collected[#collected+1] = di
---~ break
---~ end
---~ end
---~ end
---~ return collected
return { }
end
apply_axis['preceding'] = function(list) -- incomplete
---~ local collected = { }
---~ for l=1,#list do
---~ local ll = list[l]
---~ local p = ll.__p__
---~ local d = p.dt
---~ for i=ll.ni-1,1,-1 do
---~ local di = d[i]
---~ if type(di) == "table" then
---~ collected[#collected+1] = di
---~ break
---~ end
---~ end
---~ end
---~ return collected
return { }
end
apply_axis['following-sibling'] = function(list)
- local collected = { }
+ local collected, c = { }, 0
for l=1,#list do
local ll = list[l]
local p = ll.__p__
@@ -5464,7 +7742,8 @@ apply_axis['following-sibling'] = function(list)
for i=ll.ni+1,#d do
local di = d[i]
if type(di) == "table" then
- collected[#collected+1] = di
+ c = c + 1
+ collected[c] = di
end
end
end
@@ -5472,7 +7751,7 @@ apply_axis['following-sibling'] = function(list)
end
apply_axis['preceding-sibling'] = function(list)
- local collected = { }
+ local collected, c = { }, 0
for l=1,#list do
local ll = list[l]
local p = ll.__p__
@@ -5480,7 +7759,8 @@ apply_axis['preceding-sibling'] = function(list)
for i=1,ll.ni-1 do
local di = d[i]
if type(di) == "table" then
- collected[#collected+1] = di
+ c = c + 1
+ collected[c] = di
end
end
end
@@ -5488,7 +7768,7 @@ apply_axis['preceding-sibling'] = function(list)
end
apply_axis['reverse-sibling'] = function(list) -- reverse preceding
- local collected = { }
+ local collected, c = { }, 0
for l=1,#list do
local ll = list[l]
local p = ll.__p__
@@ -5496,7 +7776,8 @@ apply_axis['reverse-sibling'] = function(list) -- reverse preceding
for i=ll.ni-1,1,-1 do
local di = d[i]
if type(di) == "table" then
- collected[#collected+1] = di
+ c = c + 1
+ collected[c] = di
end
end
end
@@ -5522,7 +7803,7 @@ local function apply_nodes(list,directive,nodes)
return { }
end
else
- local collected, m, p = { }, 0, nil
+ local collected, c, m, p = { }, 0, 0, nil
if not nns then -- only check tag
for l=1,#list do
local ll = list[l]
@@ -5531,11 +7812,13 @@ local function apply_nodes(list,directive,nodes)
if directive then
if ntg == ltg then
local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end
- collected[#collected+1], ll.mi = ll, m
+ c = c + 1
+ collected[c], ll.mi = ll, m
end
elseif ntg ~= ltg then
local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end
- collected[#collected+1], ll.mi = ll, m
+ c = c + 1
+ collected[c], ll.mi = ll, m
end
end
end
@@ -5547,11 +7830,13 @@ local function apply_nodes(list,directive,nodes)
if directive then
if lns == nns then
local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end
- collected[#collected+1], ll.mi = ll, m
+ c = c + 1
+ collected[c], ll.mi = ll, m
end
elseif lns ~= nns then
local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end
- collected[#collected+1], ll.mi = ll, m
+ c = c + 1
+ collected[c], ll.mi = ll, m
end
end
end
@@ -5565,11 +7850,13 @@ local function apply_nodes(list,directive,nodes)
if directive then
if ok then
local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end
- collected[#collected+1], ll.mi = ll, m
+ c = c + 1
+ collected[c], ll.mi = ll, m
end
elseif not ok then
local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end
- collected[#collected+1], ll.mi = ll, m
+ c = c + 1
+ collected[c], ll.mi = ll, m
end
end
end
@@ -5577,7 +7864,7 @@ local function apply_nodes(list,directive,nodes)
return collected
end
else
- local collected, m, p = { }, 0, nil
+ local collected, c, m, p = { }, 0, 0, nil
for l=1,#list do
local ll = list[l]
local ltg = ll.tg
@@ -5594,11 +7881,13 @@ local function apply_nodes(list,directive,nodes)
if directive then
if ok then
local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end
- collected[#collected+1], ll.mi = ll, m
+ c = c + 1
+ collected[c], ll.mi = ll, m
end
elseif not ok then
local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end
- collected[#collected+1], ll.mi = ll, m
+ c = c + 1
+ collected[c], ll.mi = ll, m
end
end
end
@@ -5609,12 +7898,13 @@ end
local quit_expression = false
local function apply_expression(list,expression,order)
- local collected = { }
+ local collected, c = { }, 0
quit_expression = false
for l=1,#list do
local ll = list[l]
if expression(list,ll,l,order) then -- nasty, order alleen valid als n=1
- collected[#collected+1] = ll
+ c = c + 1
+ collected[c] = ll
end
if quit_expression then
break
@@ -5653,7 +7943,9 @@ local lp_builtin = P (
P("ns") / "ll.ns"
) * ((spaces * P("(") * spaces * P(")"))/"")
-local lp_attribute = (P("@") + P("attribute::")) / "" * Cc("(ll.at and ll.at['") * R("az","AZ","--","__")^1 * Cc("'])")
+-- for the moment we keep namespaces with attributes
+
+local lp_attribute = (P("@") + P("attribute::")) / "" * Cc("(ll.at and ll.at['") * ((R("az","AZ") + S("-_:"))^1) * Cc("'])")
local lp_fastpos_p = ((P("+")^0 * R("09")^1 * P(-1)) / function(s) return "l==" .. s end)
local lp_fastpos_n = ((P("-") * R("09")^1 * P(-1)) / function(s) return "(" .. s .. "<0 and (#list+".. s .. "==l))" end)
local lp_fastpos = lp_fastpos_n + lp_fastpos_p
@@ -5714,14 +8006,12 @@ local converter = Cs (
)
cleaner = Cs ( (
---~ lp_fastpos +
lp_reserved +
lp_number +
lp_string +
1 )^1 )
---~ expr
local template_e = [[
local expr = xml.expressions
@@ -5772,13 +8062,13 @@ local skip = { }
local function errorrunner_e(str,cnv)
if not skip[str] then
- logs.report("lpath","error in expression: %s => %s",str,cnv)
+ report_lpath("error in expression: %s => %s",str,cnv)
skip[str] = cnv or str
end
return false
end
local function errorrunner_f(str,arg)
- logs.report("lpath","error in finalizer: %s(%s)",str,arg or "")
+ report_lpath("error in finalizer: %s(%s)",str,arg or "")
return false
end
@@ -5829,7 +8119,7 @@ local special_1 = P("*") * Cc(register_auto_descendant) * Cc(register_all_nodes
local special_2 = P("/") * Cc(register_auto_self)
local special_3 = P("") * Cc(register_auto_self)
-local parser = Ct { "patterns", -- can be made a bit faster by moving pattern outside
+local pathparser = Ct { "patterns", -- can be made a bit faster by moving pattern outside
patterns = spaces * V("protocol") * spaces * (
( V("special") * spaces * P(-1) ) +
@@ -5901,6 +8191,8 @@ local parser = Ct { "patterns", -- can be made a bit faster by moving pattern ou
}
+xmlpatterns.pathparser = pathparser
+
local cache = { }
local function nodesettostring(set,nodetest)
@@ -5910,7 +8202,7 @@ local function nodesettostring(set,nodetest)
if not ns or ns == "" then ns = "*" end
if not tg or tg == "" then tg = "*" end
tg = (tg == "@rt@" and "[root]") or format("%s:%s",ns,tg)
- t[#t+1] = (directive and tg) or format("not(%s)",tg)
+ t[i] = (directive and tg) or format("not(%s)",tg)
end
if nodetest == false then
return format("not(%s)",concat(t,"|"))
@@ -5929,7 +8221,7 @@ local function tagstostring(list)
local ns, tg = li.ns, li.tg
if not ns or ns == "" then ns = "*" end
if not tg or tg == "" then tg = "*" end
- t[#t+1] = (tg == "@rt@" and "[root]") or format("%s:%s",ns,tg)
+ t[i] = (tg == "@rt@" and "[root]") or format("%s:%s",ns,tg)
end
return concat(t," ")
end
@@ -5937,16 +8229,16 @@ end
xml.nodesettostring = nodesettostring
-local parse_pattern -- we have a harmless kind of circular reference
+local lpath -- we have a harmless kind of circular reference
+
+local lshowoptions = { functions = false }
local function lshow(parsed)
if type(parsed) == "string" then
- parsed = parse_pattern(parsed)
+ parsed = lpath(parsed)
end
- local s = table.serialize_functions -- ugly
- table.serialize_functions = false -- ugly
- logs.report("lpath","%s://%s => %s",parsed.protocol or xml.defaultprotocol,parsed.pattern,table.serialize(parsed,false))
- table.serialize_functions = s -- ugly
+ report_lpath("%s://%s => %s",parsed.protocol or xml.defaultprotocol,parsed.pattern,
+ table.serialize(parsed,false,lshowoptions))
end
xml.lshow = lshow
@@ -5960,7 +8252,7 @@ local function add_comment(p,str)
end
end
-parse_pattern = function (pattern) -- the gain of caching is rather minimal
+lpath = function (pattern) -- the gain of caching is rather minimal
lpathcalls = lpathcalls + 1
if type(pattern) == "table" then
return pattern
@@ -5969,13 +8261,13 @@ parse_pattern = function (pattern) -- the gain of caching is rather minimal
if parsed then
lpathcached = lpathcached + 1
else
- parsed = lpegmatch(parser,pattern)
+ parsed = lpegmatch(pathparser,pattern)
if parsed then
parsed.pattern = pattern
local np = #parsed
if np == 0 then
parsed = { pattern = pattern, register_self, state = "parsing error" }
- logs.report("lpath","parsing error in '%s'",pattern)
+ report_lpath("parsing error in '%s'",pattern)
lshow(parsed)
else
-- we could have done this with a more complex parser but this
@@ -6017,6 +8309,8 @@ parse_pattern = function (pattern) -- the gain of caching is rather minimal
end
end
+xml.lpath = lpath
+
-- we can move all calls inline and then merge the trace back
-- technically we can combine axis and the next nodes which is
-- what we did before but this a bit cleaner (but slower too)
@@ -6079,32 +8373,32 @@ local function traced_apply(list,parsed,nofparsed,order)
if trace_lparse then
lshow(parsed)
end
- logs.report("lpath", "collecting : %s",parsed.pattern)
- logs.report("lpath", " root tags : %s",tagstostring(list))
- logs.report("lpath", " order : %s",order or "unset")
+ report_lpath("collecting: %s",parsed.pattern)
+ report_lpath("root tags : %s",tagstostring(list))
+ report_lpath("order : %s",order or "unset")
local collected = list
for i=1,nofparsed do
local pi = parsed[i]
local kind = pi.kind
if kind == "axis" then
collected = apply_axis[pi.axis](collected)
- logs.report("lpath", "% 10i : ax : %s",(collected and #collected) or 0,pi.axis)
+ report_lpath("% 10i : ax : %s",(collected and #collected) or 0,pi.axis)
elseif kind == "nodes" then
collected = apply_nodes(collected,pi.nodetest,pi.nodes)
- logs.report("lpath", "% 10i : ns : %s",(collected and #collected) or 0,nodesettostring(pi.nodes,pi.nodetest))
+ report_lpath("% 10i : ns : %s",(collected and #collected) or 0,nodesettostring(pi.nodes,pi.nodetest))
elseif kind == "expression" then
collected = apply_expression(collected,pi.evaluator,order)
- logs.report("lpath", "% 10i : ex : %s -> %s",(collected and #collected) or 0,pi.expression,pi.converted)
+ report_lpath("% 10i : ex : %s -> %s",(collected and #collected) or 0,pi.expression,pi.converted)
elseif kind == "finalizer" then
collected = pi.finalizer(collected)
- logs.report("lpath", "% 10i : fi : %s : %s(%s)",(type(collected) == "table" and #collected) or 0,parsed.protocol or xml.defaultprotocol,pi.name,pi.arguments or "")
+ report_lpath("% 10i : fi : %s : %s(%s)",(type(collected) == "table" and #collected) or 0,parsed.protocol or xml.defaultprotocol,pi.name,pi.arguments or "")
return collected
end
if not collected or #collected == 0 then
local pn = i < nofparsed and parsed[nofparsed]
if pn and pn.kind == "finalizer" then
collected = pn.finalizer(collected)
- logs.report("lpath", "% 10i : fi : %s : %s(%s)",(type(collected) == "table" and #collected) or 0,parsed.protocol or xml.defaultprotocol,pn.name,pn.arguments or "")
+ report_lpath("% 10i : fi : %s : %s(%s)",(type(collected) == "table" and #collected) or 0,parsed.protocol or xml.defaultprotocol,pn.name,pn.arguments or "")
return collected
end
return nil
@@ -6141,8 +8435,11 @@ local function normal_apply(list,parsed,nofparsed,order)
return collected
end
-local function parse_apply(list,pattern)
- -- we avoid an extra call
+
+local function applylpath(list,pattern)
+ if not list then
+ return
+ end
local parsed = cache[pattern]
if parsed then
lpathcalls = lpathcalls + 1
@@ -6151,7 +8448,7 @@ local function parse_apply(list,pattern)
lpathcalls = lpathcalls + 1
parsed = pattern
else
- parsed = parse_pattern(pattern) or pattern
+ parsed = lpath(pattern) or pattern
end
if not parsed then
return
@@ -6160,25 +8457,32 @@ local function parse_apply(list,pattern)
if nofparsed == 0 then
return -- something is wrong
end
- local one = list[1]
- if not one then
- return -- something is wrong
- elseif not trace_lpath then
- return normal_apply(list,parsed,nofparsed,one.mi)
+ if not trace_lpath then
+ return normal_apply ({ list },parsed,nofparsed,list.mi)
elseif trace_lprofile then
- return profiled_apply(list,parsed,nofparsed,one.mi)
+ return profiled_apply({ list },parsed,nofparsed,list.mi)
else
- return traced_apply(list,parsed,nofparsed,one.mi)
+ return traced_apply ({ list },parsed,nofparsed,list.mi)
end
end
+xml.applylpath = applylpath -- takes a table as first argment, which is what xml.filter will do
+
+--[[ldx--
+<p>This is the main filter function. It returns whatever is asked for.</p>
+--ldx]]--
+
+function xml.filter(root,pattern) -- no longer funny attribute handling here
+ return applylpath(root,pattern)
+end
+
-- internal (parsed)
expressions.child = function(e,pattern)
- return parse_apply({ e },pattern) -- todo: cache
+ return applylpath(e,pattern) -- todo: cache
end
expressions.count = function(e,pattern)
- local collected = parse_apply({ e },pattern) -- todo: cache
+ local collected = applylpath(e,pattern) -- todo: cache
return (collected and #collected) or 0
end
@@ -6188,7 +8492,7 @@ expressions.oneof = function(s,...) -- slow
local t = {...} for i=1,#t do if s == t[i] then return true end end return false
end
expressions.error = function(str)
- xml.error_handler("unknown function in lpath expression",tostring(str or "?"))
+ xml.errorhandler("unknown function in lpath expression",tostring(str or "?"))
return false
end
expressions.undefined = function(s)
@@ -6217,8 +8521,8 @@ expressions.boolean = toboolean
-- user interface
local function traverse(root,pattern,handle)
- logs.report("xml","use 'xml.selection' instead for '%s'",pattern)
- local collected = parse_apply({ root },pattern)
+ report_lpath("use 'xml.selection' instead for '%s'",pattern)
+ local collected = applylpath(root,pattern)
if collected then
for c=1,#collected do
local e = collected[c]
@@ -6229,7 +8533,7 @@ local function traverse(root,pattern,handle)
end
local function selection(root,pattern,handle)
- local collected = parse_apply({ root },pattern)
+ local collected = applylpath(root,pattern)
if collected then
if handle then
for c=1,#collected do
@@ -6241,19 +8545,9 @@ local function selection(root,pattern,handle)
end
end
-xml.parse_parser = parser
-xml.parse_pattern = parse_pattern
-xml.parse_apply = parse_apply
xml.traverse = traverse -- old method, r, d, k
xml.selection = selection -- new method, simple handle
-local lpath = parse_pattern
-
-xml.lpath = lpath
-
-function xml.cached_patterns()
- return cache
-end
-- generic function finalizer (independant namespace)
@@ -6265,13 +8559,13 @@ local function dofunction(collected,fnc)
f(collected[c])
end
else
- logs.report("xml","unknown function '%s'",fnc)
+ report_lpath("unknown function '%s'",fnc)
end
end
end
-xml.finalizers.xml["function"] = dofunction
-xml.finalizers.tex["function"] = dofunction
+finalizers.xml["function"] = dofunction
+finalizers.tex["function"] = dofunction
-- functions
@@ -6364,14 +8658,6 @@ expressions.tag = function(e,n) -- only tg
end
--[[ldx--
-<p>This is the main filter function. It returns whatever is asked for.</p>
---ldx]]--
-
-function xml.filter(root,pattern) -- no longer funny attribute handling here
- return parse_apply({ root },pattern)
-end
-
---[[ldx--
<p>Often using an iterators looks nicer in the code than passing handler
functions. The <l n='lua'/> book describes how to use coroutines for that
purpose (<url href='http://www.lua.org/pil/9.3.html'/>). This permits
@@ -6390,7 +8676,7 @@ end
local wrap, yield = coroutine.wrap, coroutine.yield
function xml.elements(root,pattern,reverse) -- r, d, k
- local collected = parse_apply({ root },pattern)
+ local collected = applylpath(root,pattern)
if collected then
if reverse then
return wrap(function() for c=#collected,1,-1 do
@@ -6406,7 +8692,7 @@ function xml.elements(root,pattern,reverse) -- r, d, k
end
function xml.collected(root,pattern,reverse) -- e
- local collected = parse_apply({ root },pattern)
+ local collected = applylpath(root,pattern)
if collected then
if reverse then
return wrap(function() for c=#collected,1,-1 do yield(collected[c]) end end)
@@ -6417,6 +8703,15 @@ function xml.collected(root,pattern,reverse) -- e
return wrap(function() end)
end
+-- handy
+
+function xml.inspect(collection,pattern)
+ pattern = pattern or "."
+ for e in xml.collected(collection,pattern or ".") do
+ report_lpath("pattern %q\n\n%s\n",pattern,xml.tostring(e))
+ end
+end
+
end -- of closure
@@ -6430,10 +8725,16 @@ if not modules then modules = { } end modules ['lxml-mis'] = {
license = "see context related readme files"
}
+local xml, lpeg, string = xml, lpeg, string
+
local concat = table.concat
local type, next, tonumber, tostring, setmetatable, loadstring = type, next, tonumber, tostring, setmetatable, loadstring
local format, gsub, match = string.format, string.gsub, string.match
-local lpegmatch = lpeg.match
+local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns
+local P, S, R, C, V, Cc, Cs = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.V, lpeg.Cc, lpeg.Cs
+
+lpegpatterns.xml = lpegpatterns.xml or { }
+local xmlpatterns = lpegpatterns.xml
--[[ldx--
<p>The following helper functions best belong to the <t>lxml-ini</t>
@@ -6457,9 +8758,8 @@ local function xmlgsub(t,old,new) -- will be replaced
end
end
---~ xml.gsub = xmlgsub
-function xml.strip_leading_spaces(dk,d,k) -- cosmetic, for manual
+function xml.stripleadingspaces(dk,d,k) -- cosmetic, for manual
if d and k then
local dkm = d[k-1]
if dkm and type(dkm) == "string" then
@@ -6469,14 +8769,7 @@ function xml.strip_leading_spaces(dk,d,k) -- cosmetic, for manual
end
end
---~ xml.escapes = { ['&'] = '&amp;', ['<'] = '&lt;', ['>'] = '&gt;', ['"'] = '&quot;' }
---~ xml.unescapes = { } for k,v in next, xml.escapes do xml.unescapes[v] = k end
---~ function xml.escaped (str) return (gsub(str,"(.)" , xml.escapes )) end
---~ function xml.unescaped(str) return (gsub(str,"(&.-;)", xml.unescapes)) end
---~ function xml.cleansed (str) return (gsub(str,"<.->" , '' )) end -- "%b<>"
-
-local P, S, R, C, V, Cc, Cs = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.V, lpeg.Cc, lpeg.Cs
-- 100 * 2500 * "oeps< oeps> oeps&" : gsub:lpeg|lpeg|lpeg
--
@@ -6502,9 +8795,9 @@ local unescaped = Cs(normal * (special * normal)^0)
local cleansed = Cs(((P("<") * (1-P(">"))^0 * P(">"))/"" + 1)^0)
-xml.escaped_pattern = escaped
-xml.unescaped_pattern = unescaped
-xml.cleansed_pattern = cleansed
+xmlpatterns.escaped = escaped
+xmlpatterns.unescaped = unescaped
+xmlpatterns.cleansed = cleansed
function xml.escaped (str) return lpegmatch(escaped,str) end
function xml.unescaped(str) return lpegmatch(unescaped,str) end
@@ -6540,15 +8833,20 @@ if not modules then modules = { } end modules ['lxml-aux'] = {
local trace_manipulations = false trackers.register("lxml.manipulations", function(v) trace_manipulations = v end)
-local xmlparseapply, xmlconvert, xmlcopy, xmlname = xml.parse_apply, xml.convert, xml.copy, xml.name
+local report_xml = logs.reporter("xml")
+
+local xml = xml
+
+local xmlconvert, xmlcopy, xmlname = xml.convert, xml.copy, xml.name
local xmlinheritedconvert = xml.inheritedconvert
+local xmlapplylpath = xml.applylpath
-local type = type
-local insert, remove = table.insert, table.remove
+local type, setmetatable, getmetatable = type, setmetatable, getmetatable
+local insert, remove, fastcopy = table.insert, table.remove, table.fastcopy
local gmatch, gsub = string.gmatch, string.gsub
local function report(what,pattern,c,e)
- logs.report("xml","%s element '%s' (root: '%s', position: %s, index: %s, pattern: %s)",what,xmlname(e),xmlname(e.__p__),c,e.ni,pattern)
+ report_xml("%s element '%s' (root: '%s', position: %s, index: %s, pattern: %s)",what,xmlname(e),xmlname(e.__p__),c,e.ni,pattern)
end
local function withelements(e,handle,depth)
@@ -6602,10 +8900,8 @@ function xml.withelement(e,n,handle) -- slow
end
end
-xml.elements_only = xml.collected
-
-function xml.each_element(root,pattern,handle,reverse)
- local collected = xmlparseapply({ root },pattern)
+function xml.each(root,pattern,handle,reverse)
+ local collected = xmlapplylpath(root,pattern)
if collected then
if reverse then
for c=#collected,1,-1 do
@@ -6620,10 +8916,8 @@ function xml.each_element(root,pattern,handle,reverse)
end
end
-xml.process_elements = xml.each_element
-
-function xml.process_attributes(root,pattern,handle)
- local collected = xmlparseapply({ root },pattern)
+function xml.processattributes(root,pattern,handle)
+ local collected = xmlapplylpath(root,pattern)
if collected and handle then
for c=1,#collected do
handle(collected[c].at)
@@ -6638,12 +8932,12 @@ end
-- are these still needed -> lxml-cmp.lua
-function xml.collect_elements(root, pattern)
- return xmlparseapply({ root },pattern)
+function xml.collect(root, pattern)
+ return xmlapplylpath(root,pattern)
end
-function xml.collect_texts(root, pattern, flatten) -- todo: variant with handle
- local collected = xmlparseapply({ root },pattern)
+function xml.collecttexts(root, pattern, flatten) -- todo: variant with handle
+ local collected = xmlapplylpath(root,pattern)
if collected and flatten then
local xmltostring = xml.tostring
for c=1,#collected do
@@ -6654,18 +8948,19 @@ function xml.collect_texts(root, pattern, flatten) -- todo: variant with handle
end
function xml.collect_tags(root, pattern, nonamespace)
- local collected = xmlparseapply({ root },pattern)
+ local collected = xmlapplylpath(root,pattern)
if collected then
- local t = { }
+ local t, n = { }, 0
for c=1,#collected do
local e = collected[c]
local ns, tg = e.ns, e.tg
+ n = n + 1
if nonamespace then
- t[#t+1] = tg
+ t[n] = tg
elseif ns == "" then
- t[#t+1] = tg
+ t[n] = tg
else
- t[#t+1] = ns .. ":" .. tg
+ t[n] = ns .. ":" .. tg
end
end
return t
@@ -6678,7 +8973,7 @@ end
local no_root = { no_root = true }
-function xml.redo_ni(d)
+local function redo_ni(d)
for k=1,#d do
local dk = d[k]
if type(dk) == "table" then
@@ -6701,12 +8996,7 @@ local function xmltoelement(whatever,root)
return whatever -- string
end
if element then
- --~ if element.ri then
- --~ element = element.dt[element.ri].dt
- --~ else
- --~ element = element.dt
- --~ end
- end
+ end
return element
end
@@ -6724,8 +9014,8 @@ local function copiedelement(element,newparent)
end
end
-function xml.delete_element(root,pattern)
- local collected = xmlparseapply({ root },pattern)
+function xml.delete(root,pattern)
+ local collected = xmlapplylpath(root,pattern)
if collected then
for c=1,#collected do
local e = collected[c]
@@ -6736,15 +9026,15 @@ function xml.delete_element(root,pattern)
end
local d = p.dt
remove(d,e.ni)
- xml.redo_ni(d) -- can be made faster and inlined
+ redo_ni(d) -- can be made faster and inlined
end
end
end
end
-function xml.replace_element(root,pattern,whatever)
+function xml.replace(root,pattern,whatever)
local element = root and xmltoelement(whatever,root)
- local collected = element and xmlparseapply({ root },pattern)
+ local collected = element and xmlapplylpath(root,pattern)
if collected then
for c=1,#collected do
local e = collected[c]
@@ -6755,15 +9045,50 @@ function xml.replace_element(root,pattern,whatever)
end
local d = p.dt
d[e.ni] = copiedelement(element,p)
- xml.redo_ni(d) -- probably not needed
+ redo_ni(d) -- probably not needed
+ end
+ end
+ end
+end
+
+local function wrap(e,wrapper)
+ local t = {
+ rn = e.rn,
+ tg = e.tg,
+ ns = e.ns,
+ at = e.at,
+ dt = e.dt,
+ __p__ = e,
+ }
+ setmetatable(t,getmetatable(e))
+ e.rn = wrapper.rn or e.rn or ""
+ e.tg = wrapper.tg or e.tg or ""
+ e.ns = wrapper.ns or e.ns or ""
+ e.at = fastcopy(wrapper.at)
+ e.dt = { t }
+end
+
+function xml.wrap(root,pattern,whatever)
+ if whatever then
+ local wrapper = xmltoelement(whatever,root)
+ local collected = xmlapplylpath(root,pattern)
+ if collected then
+ for c=1,#collected do
+ local e = collected[c]
+ if trace_manipulations then
+ report('wrapping',pattern,c,e)
+ end
+ wrap(e,wrapper)
end
end
+ else
+ wrap(root,xmltoelement(pattern))
end
end
local function inject_element(root,pattern,whatever,prepend)
local element = root and xmltoelement(whatever,root)
- local collected = element and xmlparseapply({ root },pattern)
+ local collected = element and xmlapplylpath(root,pattern)
if collected then
for c=1,#collected do
local e = collected[c]
@@ -6778,15 +9103,17 @@ local function inject_element(root,pattern,whatever,prepend)
else
be, af = edt, cp
end
+ local bn = #be
for i=1,#af do
- be[#be+1] = af[i]
+ bn = bn + 1
+ be[bn] = af[i]
end
if rri then
r.dt[rri].dt = be
else
d[k].dt = be
end
- xml.redo_ni(d)
+ redo_ni(d)
end
end
end
@@ -6794,7 +9121,7 @@ end
local function insert_element(root,pattern,whatever,before) -- todo: element als functie
local element = root and xmltoelement(whatever,root)
- local collected = element and xmlparseapply({ root },pattern)
+ local collected = element and xmlapplylpath(root,pattern)
if collected then
for c=1,#collected do
local e = collected[c]
@@ -6804,24 +9131,23 @@ local function insert_element(root,pattern,whatever,before) -- todo: element als
k = k + 1
end
insert(d,k,copiedelement(element,r))
- xml.redo_ni(d)
+ redo_ni(d)
end
end
end
-xml.insert_element = insert_element
-xml.insert_element_after = insert_element
-xml.insert_element_before = function(r,p,e) insert_element(r,p,e,true) end
-xml.inject_element = inject_element
-xml.inject_element_after = inject_element
-xml.inject_element_before = function(r,p,e) inject_element(r,p,e,true) end
+xml.insert_element = insert_element
+xml.insertafter = insert_element
+xml.insertbefore = function(r,p,e) insert_element(r,p,e,true) end
+xml.injectafter = inject_element
+xml.injectbefore = function(r,p,e) inject_element(r,p,e,true) end
local function include(xmldata,pattern,attribute,recursive,loaddata)
-- parse="text" (default: xml), encoding="" (todo)
-- attribute = attribute or 'href'
pattern = pattern or 'include'
loaddata = loaddata or io.loaddata
- local collected = xmlparseapply({ xmldata },pattern)
+ local collected = xmlapplylpath(xmldata,pattern)
if collected then
for c=1,#collected do
local ek = collected[c]
@@ -6830,7 +9156,7 @@ local function include(xmldata,pattern,attribute,recursive,loaddata)
local ekat = ek.at
local epdt = ek.__p__.dt
if not attribute or attribute == "" then
- name = (type(ekdt) == "table" and ekdt[1]) or ekdt -- ckeck, probably always tab or str
+ name = (type(ekdt) == "table" and ekdt[1]) or ekdt -- check, probably always tab or str
end
if not name then
for a in gmatch(attribute or "href","([^|]+)") do
@@ -6845,9 +9171,6 @@ local function include(xmldata,pattern,attribute,recursive,loaddata)
-- for the moment hard coded
epdt[ek.ni] = xml.escaped(data) -- d[k] = xml.escaped(data)
else
---~ local settings = xmldata.settings
---~ settings.parent_root = xmldata -- to be tested
---~ local xi = xmlconvert(data,settings)
local xi = xmlinheritedconvert(data,xmldata)
if not xi then
epdt[ek.ni] = "" -- xml.empty(d,k)
@@ -6864,136 +9187,88 @@ end
xml.include = include
---~ local function manipulate(xmldata,pattern,manipulator) -- untested and might go away
---~ local collected = xmlparseapply({ xmldata },pattern)
---~ if collected then
---~ local xmltostring = xml.tostring
---~ for c=1,#collected do
---~ local e = collected[c]
---~ local data = manipulator(xmltostring(e))
---~ if data == "" then
---~ epdt[e.ni] = ""
---~ else
---~ local xi = xmlinheritedconvert(data,xmldata)
---~ if not xi then
---~ epdt[e.ni] = ""
---~ else
---~ epdt[e.ni] = xml.body(xi) -- xml.assign(d,k,xi)
---~ end
---~ end
---~ end
---~ end
---~ end
-
---~ xml.manipulate = manipulate
-
-function xml.strip_whitespace(root, pattern, nolines) -- strips all leading and trailing space !
- local collected = xmlparseapply({ root },pattern)
- if collected then
- for i=1,#collected do
- local e = collected[i]
- local edt = e.dt
- if edt then
- local t = { }
- for i=1,#edt do
- local str = edt[i]
- if type(str) == "string" then
- if str == "" then
- -- stripped
- else
- if nolines then
- str = gsub(str,"[ \n\r\t]+"," ")
- end
- if str == "" then
- -- stripped
- else
- t[#t+1] = str
- end
- end
+local function stripelement(e,nolines,anywhere)
+ local edt = e.dt
+ if edt then
+ if anywhere then
+ local t, n = { }, 0
+ for e=1,#edt do
+ local str = edt[e]
+ if type(str) ~= "string" then
+ n = n + 1
+ t[n] = str
+ elseif str ~= "" then
+ -- todo: lpeg for each case
+ if nolines then
+ str = gsub(str,"%s+"," ")
+ end
+ str = gsub(str,"^%s*(.-)%s*$","%1")
+ if str ~= "" then
+ n = n + 1
+ t[n] = str
+ end
+ end
+ end
+ e.dt = t
+ else
+ -- we can assume a regular sparse xml table with no successive strings
+ -- otherwise we should use a while loop
+ if #edt > 0 then
+ -- strip front
+ local str = edt[1]
+ if type(str) ~= "string" then
+ -- nothing
+ elseif str == "" then
+ remove(edt,1)
+ else
+ if nolines then
+ str = gsub(str,"%s+"," ")
+ end
+ str = gsub(str,"^%s+","")
+ if str == "" then
+ remove(edt,1)
+ else
+ edt[1] = str
+ end
+ end
+ end
+ local nedt = #edt
+ if nedt > 0 then
+ -- strip end
+ local str = edt[nedt]
+ if type(str) ~= "string" then
+ -- nothing
+ elseif str == "" then
+ remove(edt)
+ else
+ if nolines then
+ str = gsub(str,"%s+"," ")
+ end
+ str = gsub(str,"%s+$","")
+ if str == "" then
+ remove(edt)
else
- --~ str.ni = i
- t[#t+1] = str
+ edt[nedt] = str
end
end
- e.dt = t
end
end
end
+ return e -- convenient
end
-function xml.strip_whitespace(root, pattern, nolines, anywhere) -- strips all leading and trailing spacing
- local collected = xmlparseapply({ root },pattern) -- beware, indices no longer are valid now
+xml.stripelement = stripelement
+
+function xml.strip(root,pattern,nolines,anywhere) -- strips all leading and trailing spacing
+ local collected = xmlapplylpath(root,pattern) -- beware, indices no longer are valid now
if collected then
for i=1,#collected do
- local e = collected[i]
- local edt = e.dt
- if edt then
- if anywhere then
- local t = { }
- for e=1,#edt do
- local str = edt[e]
- if type(str) ~= "string" then
- t[#t+1] = str
- elseif str ~= "" then
- -- todo: lpeg for each case
- if nolines then
- str = gsub(str,"%s+"," ")
- end
- str = gsub(str,"^%s*(.-)%s*$","%1")
- if str ~= "" then
- t[#t+1] = str
- end
- end
- end
- e.dt = t
- else
- -- we can assume a regular sparse xml table with no successive strings
- -- otherwise we should use a while loop
- if #edt > 0 then
- -- strip front
- local str = edt[1]
- if type(str) ~= "string" then
- -- nothing
- elseif str == "" then
- remove(edt,1)
- else
- if nolines then
- str = gsub(str,"%s+"," ")
- end
- str = gsub(str,"^%s+","")
- if str == "" then
- remove(edt,1)
- else
- edt[1] = str
- end
- end
- end
- if #edt > 1 then
- -- strip end
- local str = edt[#edt]
- if type(str) ~= "string" then
- -- nothing
- elseif str == "" then
- remove(edt)
- else
- if nolines then
- str = gsub(str,"%s+"," ")
- end
- str = gsub(str,"%s+$","")
- if str == "" then
- remove(edt)
- else
- edt[#edt] = str
- end
- end
- end
- end
- end
+ stripelement(collected[i],nolines,anywhere)
end
end
end
-local function rename_space(root, oldspace, newspace) -- fast variant
+local function renamespace(root, oldspace, newspace) -- fast variant
local ndt = #root.dt
for i=1,ndt or 0 do
local e = root[i]
@@ -7006,16 +9281,16 @@ local function rename_space(root, oldspace, newspace) -- fast variant
end
local edt = e.dt
if edt then
- rename_space(edt, oldspace, newspace)
+ renamespace(edt, oldspace, newspace)
end
end
end
end
-xml.rename_space = rename_space
+xml.renamespace = renamespace
-function xml.remap_tag(root, pattern, newtg)
- local collected = xmlparseapply({ root },pattern)
+function xml.remaptag(root, pattern, newtg)
+ local collected = xmlapplylpath(root,pattern)
if collected then
for c=1,#collected do
collected[c].tg = newtg
@@ -7023,8 +9298,8 @@ function xml.remap_tag(root, pattern, newtg)
end
end
-function xml.remap_namespace(root, pattern, newns)
- local collected = xmlparseapply({ root },pattern)
+function xml.remapnamespace(root, pattern, newns)
+ local collected = xmlapplylpath(root,pattern)
if collected then
for c=1,#collected do
collected[c].ns = newns
@@ -7032,8 +9307,8 @@ function xml.remap_namespace(root, pattern, newns)
end
end
-function xml.check_namespace(root, pattern, newns)
- local collected = xmlparseapply({ root },pattern)
+function xml.checknamespace(root, pattern, newns)
+ local collected = xmlapplylpath(root,pattern)
if collected then
for c=1,#collected do
local e = collected[c]
@@ -7044,8 +9319,8 @@ function xml.check_namespace(root, pattern, newns)
end
end
-function xml.remap_name(root, pattern, newtg, newns, newrn)
- local collected = xmlparseapply({ root },pattern)
+function xml.remapname(root, pattern, newtg, newns, newrn)
+ local collected = xmlapplylpath(root,pattern)
if collected then
for c=1,#collected do
local e = collected[c]
@@ -7058,18 +9333,34 @@ end
<p>Here are a few synonyms.</p>
--ldx]]--
-xml.each = xml.each_element
-xml.process = xml.process_element
-xml.strip = xml.strip_whitespace
-xml.collect = xml.collect_elements
-xml.all = xml.collect_elements
-
-xml.insert = xml.insert_element_after
-xml.inject = xml.inject_element_after
-xml.after = xml.insert_element_after
-xml.before = xml.insert_element_before
-xml.delete = xml.delete_element
-xml.replace = xml.replace_element
+xml.all = xml.each
+xml.insert = xml.insertafter
+xml.inject = xml.injectafter
+xml.after = xml.insertafter
+xml.before = xml.insertbefore
+xml.process = xml.each
+
+-- obsolete
+
+xml.obsolete = xml.obsolete or { }
+local obsolete = xml.obsolete
+
+xml.strip_whitespace = xml.strip obsolete.strip_whitespace = xml.strip
+xml.collect_elements = xml.collect obsolete.collect_elements = xml.collect
+xml.delete_element = xml.delete obsolete.delete_element = xml.delete
+xml.replace_element = xml.replace obsolete.replace_element = xml.replacet
+xml.each_element = xml.each obsolete.each_element = xml.each
+xml.process_elements = xml.process obsolete.process_elements = xml.process
+xml.insert_element_after = xml.insertafter obsolete.insert_element_after = xml.insertafter
+xml.insert_element_before = xml.insertbefore obsolete.insert_element_before = xml.insertbefore
+xml.inject_element_after = xml.injectafter obsolete.inject_element_after = xml.injectafter
+xml.inject_element_before = xml.injectbefore obsolete.inject_element_before = xml.injectbefore
+xml.process_attributes = xml.processattributes obsolete.process_attributes = xml.processattributes
+xml.collect_texts = xml.collecttexts obsolete.collect_texts = xml.collecttexts
+xml.inject_element = xml.inject obsolete.inject_element = xml.inject
+xml.remap_tag = xml.remaptag obsolete.remap_tag = xml.remaptag
+xml.remap_name = xml.remapname obsolete.remap_name = xml.remapname
+xml.remap_namespace = xml.remapnamespace obsolete.remap_namespace = xml.remapnamespace
end -- of closure
@@ -7084,10 +9375,15 @@ if not modules then modules = { } end modules ['lxml-xml'] = {
license = "see context related readme files"
}
+local concat = string.concat
+
+local xml = xml
+
local finalizers = xml.finalizers.xml
local xmlfilter = xml.filter -- we could inline this one for speed
local xmltostring = xml.tostring
local xmlserialize = xml.serialize
+local xmlcollected = xml.collected
local function first(collected) -- wrong ?
return collected and collected[1]
@@ -7101,15 +9397,8 @@ local function all(collected)
return collected
end
-local function reverse(collected)
- if collected then
- local reversed = { }
- for c=#collected,1,-1 do
- reversed[#reversed+1] = collected[c]
- end
- return reversed
- end
-end
+
+local reverse = table.reversed
local function attribute(collected,name)
if collected and #collected > 0 then
@@ -7200,11 +9489,12 @@ end
local function texts(collected)
if collected then
- local t = { }
+ local t, n = { }, 0
for c=1,#collected do
- local e = collection[c]
+ local e = collected[c]
if e and e.dt then
- t[#t+1] = e.dt
+ n = n + 1
+ t[n] = e.dt
end
end
return t
@@ -7247,14 +9537,15 @@ end
local function tags(collected,nonamespace)
if collected then
- local t = { }
+ local t, n = { }, 0
for c=1,#collected do
local e = collected[c]
local ns, tg = e.ns, e.tg
+ n = n + 1
if nonamespace or ns == "" then
- t[#t+1] = tg
+ t[n] = tg
else
- t[#t+1] = ns .. ":" .. tg
+ t[n] = ns .. ":" .. tg
end
end
return t
@@ -7365,293 +9656,273 @@ xml.all = xml.filter
xml.index = xml.position
xml.found = xml.filter
+-- a nice one:
+
+local function totable(x)
+ local t = { }
+ for e in xmlcollected(x[1] or x,"/*") do
+ t[e.tg] = xmltostring(e.dt) or ""
+ end
+ return next(t) and t or nil
+end
+
+xml.table = totable
+finalizers.table = totable
+
+local function textonly(e,t)
+ if e then
+ local edt = e.dt
+ if edt then
+ for i=1,#edt do
+ local e = edt[i]
+ if type(e) == "table" then
+ textonly(e,t)
+ else
+ t[#t+1] = e
+ end
+ end
+ end
+ end
+ return t
+end
+
+function xml.textonly(e) -- no pattern
+ return concat(textonly(e,{}))
+end
+
end -- of closure
do -- create closure to overcome 200 locals limit
-if not modules then modules = { } end modules ['luat-env'] = {
+if not modules then modules = { } end modules ['data-ini'] = {
version = 1.001,
comment = "companion to luat-lib.mkiv",
author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
+ license = "see context related readme files",
}
--- A former version provided functionality for non embeded core
--- scripts i.e. runtime library loading. Given the amount of
--- Lua code we use now, this no longer makes sense. Much of this
--- evolved before bytecode arrays were available and so a lot of
--- code has disappeared already.
+local gsub, find, gmatch = string.gsub, string.find, string.gmatch
+local concat = table.concat
+local next, type = next, type
-local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
+local filedirname, filebasename, fileextname, filejoin = file.dirname, file.basename, file.extname, file.join
-local format, sub, match, gsub, find = string.format, string.sub, string.match, string.gsub, string.find
-local unquote, quote = string.unquote, string.quote
+local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
+local trace_detail = false trackers.register("resolvers.details", function(v) trace_detail = v end)
+local trace_expansions = false trackers.register("resolvers.expansions", function(v) trace_expansions = v end)
--- precautions
+local report_initialization = logs.reporter("resolvers","initialization")
-os.setlocale(nil,nil) -- useless feature and even dangerous in luatex
+local ostype, osname, ossetenv, osgetenv = os.type, os.name, os.setenv, os.getenv
-function os.setlocale()
- -- no way you can mess with it
-end
+-- The code here used to be part of a data-res but for convenience
+-- we now split it over multiple files. As this file is now the
+-- starting point we introduce resolvers here.
--- dirty tricks
+resolvers = resolvers or { }
+local resolvers = resolvers
-if arg and (arg[0] == 'luatex' or arg[0] == 'luatex.exe') and arg[1] == "--luaonly" then
- arg[-1]=arg[0] arg[0]=arg[2] for k=3,#arg do arg[k-2]=arg[k] end arg[#arg]=nil arg[#arg]=nil
-end
+-- We don't want the kpse library to kick in. Also, we want to be able to
+-- execute programs. Control over execution is implemented later.
-if profiler and os.env["MTX_PROFILE_RUN"] == "YES" then
- profiler.start("luatex-profile.log")
-end
+texconfig.kpse_init = false
+texconfig.shell_escape = 't'
--- environment
+kpse = { original = kpse }
-environment = environment or { }
-environment.arguments = { }
-environment.files = { }
-environment.sortedflags = nil
+setmetatable(kpse, {
+ __index = function(kp,name)
+ report_initialization("fatal error: kpse library is accessed (key: %s)",name)
+ os.exit()
+ end
+} )
-if not environment.jobname or environment.jobname == "" then if tex then environment.jobname = tex.jobname end end
-if not environment.version or environment.version == "" then environment.version = "unknown" end
-if not environment.jobname then environment.jobname = "unknown" end
+-- First we check a couple of environment variables. Some might be
+-- set already but we need then later on. We start with the system
+-- font path.
-function environment.initialize_arguments(arg)
- local arguments, files = { }, { }
- environment.arguments, environment.files, environment.sortedflags = arguments, files, nil
- for index=1,#arg do
- local argument = arg[index]
- if index > 0 then
- local flag, value = match(argument,"^%-+(.-)=(.-)$")
- if flag then
- arguments[flag] = unquote(value or "")
- else
- flag = match(argument,"^%-+(.+)")
- if flag then
- arguments[flag] = true
- else
- files[#files+1] = argument
- end
- end
- end
+do
+
+ local osfontdir = osgetenv("OSFONTDIR")
+
+ if osfontdir and osfontdir ~= "" then
+ -- ok
+ elseif osname == "windows" then
+ ossetenv("OSFONTDIR","c:/windows/fonts//")
+ elseif osname == "macosx" then
+ ossetenv("OSFONTDIR","$HOME/Library/Fonts//;/Library/Fonts//;/System/Library/Fonts//")
end
- environment.ownname = environment.ownname or arg[0] or 'unknown.lua'
+
end
-function environment.setargument(name,value)
- environment.arguments[name] = value
+-- Next comes the user's home path. We need this as later on we have
+-- to replace ~ with its value.
+
+do
+
+ local homedir = osgetenv(ostype == "windows" and 'USERPROFILE' or 'HOME') or ''
+
+ if not homedir or homedir == "" then
+ homedir = string.char(127) -- we need a value, later we wil trigger on it
+ end
+
+ homedir = file.collapsepath(homedir)
+
+ ossetenv("HOME", homedir) -- can be used in unix cnf files
+ ossetenv("USERPROFILE",homedir) -- can be used in windows cnf files
+
+ environment.homedir = homedir
+
end
--- todo: defaults, better checks e.g on type (boolean versus string)
---
--- tricky: too many hits when we support partials unless we add
--- a registration of arguments so from now on we have 'partial'
+-- The following code sets the name of the own binary and its
+-- path. This is fallback code as we have os.selfdir now.
-function environment.argument(name,partial)
- local arguments, sortedflags = environment.arguments, environment.sortedflags
- if arguments[name] then
- return arguments[name]
- elseif partial then
- if not sortedflags then
- sortedflags = table.sortedkeys(arguments)
- for k=1,#sortedflags do
- sortedflags[k] = "^" .. sortedflags[k]
- end
- environment.sortedflags = sortedflags
+do
+
+ local args = environment.originalarguments or arg -- this needs a cleanup
+
+ local ownbin = environment.ownbin or args[-2] or arg[-2] or args[-1] or arg[-1] or arg[0] or "luatex"
+ local ownpath = environment.ownpath or os.selfdir
+
+ ownbin = file.collapsepath(ownbin)
+ ownpath = file.collapsepath(ownpath)
+
+ if not ownpath or ownpath == "" or ownpath == "unset" then
+ ownpath = args[-1] or arg[-1]
+ ownpath = ownpath and filedirname(gsub(ownpath,"\\","/"))
+ if not ownpath or ownpath == "" then
+ ownpath = args[-0] or arg[-0]
+ ownpath = ownpath and filedirname(gsub(ownpath,"\\","/"))
end
- -- example of potential clash: ^mode ^modefile
- for k=1,#sortedflags do
- local v = sortedflags[k]
- if find(name,v) then
- return arguments[sub(v,2,#v)]
+ local binary = ownbin
+ if not ownpath or ownpath == "" then
+ ownpath = ownpath and filedirname(binary)
+ end
+ if not ownpath or ownpath == "" then
+ if os.binsuffix ~= "" then
+ binary = file.replacesuffix(binary,os.binsuffix)
+ end
+ local path = osgetenv("PATH")
+ if path then
+ for p in gmatch(path,"[^"..io.pathseparator.."]+") do
+ local b = filejoin(p,binary)
+ if lfs.isfile(b) then
+ -- we assume that after changing to the path the currentdir function
+ -- resolves to the real location and use this side effect here; this
+ -- trick is needed because on the mac installations use symlinks in the
+ -- path instead of real locations
+ local olddir = lfs.currentdir()
+ if lfs.chdir(p) then
+ local pp = lfs.currentdir()
+ if trace_locating and p ~= pp then
+ report_initialization("following symlink '%s' to '%s'",p,pp)
+ end
+ ownpath = pp
+ lfs.chdir(olddir)
+ else
+ if trace_locating then
+ report_initialization("unable to check path '%s'",p)
+ end
+ ownpath = p
+ end
+ break
+ end
+ end
end
end
+ if not ownpath or ownpath == "" then
+ ownpath = "."
+ report_initialization("forcing fallback ownpath .")
+ elseif trace_locating then
+ report_initialization("using ownpath '%s'",ownpath)
+ end
end
- return nil
+
+ environment.ownbin = ownbin
+ environment.ownpath = ownpath
+
end
-environment.argument("x",true)
+resolvers.ownpath = environment.ownpath
-function environment.split_arguments(separator) -- rather special, cut-off before separator
- local done, before, after = false, { }, { }
- local original_arguments = environment.original_arguments
- for k=1,#original_arguments do
- local v = original_arguments[k]
- if not done and v == separator then
- done = true
- elseif done then
- after[#after+1] = v
- else
- before[#before+1] = v
- end
- end
- return before, after
+function resolvers.getownpath()
+ return environment.ownpath
end
-function environment.reconstruct_commandline(arg,noquote)
- arg = arg or environment.original_arguments
- if noquote and #arg == 1 then
- local a = arg[1]
- a = resolvers.resolve(a)
- a = unquote(a)
- return a
- elseif #arg > 0 then
- local result = { }
- for i=1,#arg do
- local a = arg[i]
- a = resolvers.resolve(a)
- a = unquote(a)
- a = gsub(a,'"','\\"') -- tricky
- if find(a," ") then
- result[#result+1] = quote(a)
- else
- result[#result+1] = a
- end
- end
- return table.join(result," ")
+-- The self variables permit us to use only a few (or even no)
+-- environment variables.
+
+do
+
+ local ownpath = environment.ownpath or dir.current()
+
+ if ownpath then
+ ossetenv('SELFAUTOLOC', file.collapsepath(ownpath))
+ ossetenv('SELFAUTODIR', file.collapsepath(ownpath .. "/.."))
+ ossetenv('SELFAUTOPARENT', file.collapsepath(ownpath .. "/../.."))
else
- return ""
+ report_initialization("error: unable to locate ownpath")
+ os.exit()
end
+
end
-if arg then
+-- The running os:
- -- new, reconstruct quoted snippets (maybe better just remove the " then and add them later)
- local newarg, instring = { }, false
+-- todo: check is context sits here os.platform is more trustworthy
+-- that the bin check as mtx-update runs from another path
- for index=1,#arg do
- local argument = arg[index]
- if find(argument,"^\"") then
- newarg[#newarg+1] = gsub(argument,"^\"","")
- if not find(argument,"\"$") then
- instring = true
- end
- elseif find(argument,"\"$") then
- newarg[#newarg] = newarg[#newarg] .. " " .. gsub(argument,"\"$","")
- instring = false
- elseif instring then
- newarg[#newarg] = newarg[#newarg] .. " " .. argument
- else
- newarg[#newarg+1] = argument
- end
- end
- for i=1,-5,-1 do
- newarg[i] = arg[i]
- end
+local texos = environment.texos or osgetenv("TEXOS")
+local texmfos = environment.texmfos or osgetenv('SELFAUTODIR')
- environment.initialize_arguments(newarg)
- environment.original_arguments = newarg
- environment.raw_arguments = arg
+if not texos or texos == "" then
+ texos = file.basename(texmfos)
+end
- arg = { } -- prevent duplicate handling
+ossetenv('TEXMFOS', texmfos) -- full bin path
+ossetenv('TEXOS', texos) -- partial bin parent
+ossetenv('SELFAUTOSYSTEM',os.platform) -- bonus
-end
+environment.texos = texos
+environment.texmfos = texmfos
--- weird place ... depends on a not yet loaded module
+-- The current root:
-function environment.texfile(filename)
- return resolvers.find_file(filename,'tex')
-end
+local texroot = environment.texroot or osgetenv("TEXROOT")
-function environment.luafile(filename)
- local resolved = resolvers.find_file(filename,'tex') or ""
- if resolved ~= "" then
- return resolved
- end
- resolved = resolvers.find_file(filename,'texmfscripts') or ""
- if resolved ~= "" then
- return resolved
- end
- return resolvers.find_file(filename,'luatexlibs') or ""
+if not texroot or texroot == "" then
+ texroot = osgetenv('SELFAUTOPARENT')
+ ossetenv('TEXROOT',texroot)
end
-environment.loadedluacode = loadfile -- can be overloaded
+environment.texroot = file.collapsepath(texroot)
---~ function environment.loadedluacode(name)
---~ if os.spawn("texluac -s -o texluac.luc " .. name) == 0 then
---~ local chunk = loadstring(io.loaddata("texluac.luc"))
---~ os.remove("texluac.luc")
---~ return chunk
---~ else
---~ environment.loadedluacode = loadfile -- can be overloaded
---~ return loadfile(name)
---~ end
---~ end
-
-function environment.luafilechunk(filename) -- used for loading lua bytecode in the format
- filename = file.replacesuffix(filename, "lua")
- local fullname = environment.luafile(filename)
- if fullname and fullname ~= "" then
- if trace_locating then
- logs.report("fileio","loading file %s", fullname)
- end
- return environment.loadedluacode(fullname)
- else
- if trace_locating then
- logs.report("fileio","unknown file %s", filename)
- end
- return nil
+-- Tracing. Todo ...
+
+function resolvers.settrace(n) -- no longer number but: 'locating' or 'detail'
+ if n then
+ trackers.disable("resolvers.*")
+ trackers.enable("resolvers."..n)
end
end
--- the next ones can use the previous ones / combine
+resolvers.settrace(osgetenv("MTX_INPUT_TRACE"))
-function environment.loadluafile(filename, version)
- local lucname, luaname, chunk
- local basename = file.removesuffix(filename)
- if basename == filename then
- lucname, luaname = basename .. ".luc", basename .. ".lua"
- else
- lucname, luaname = nil, basename -- forced suffix
- end
- -- when not overloaded by explicit suffix we look for a luc file first
- local fullname = (lucname and environment.luafile(lucname)) or ""
- if fullname ~= "" then
- if trace_locating then
- logs.report("fileio","loading %s", fullname)
- end
- chunk = loadfile(fullname) -- this way we don't need a file exists check
- end
- if chunk then
- assert(chunk)()
- if version then
- -- we check of the version number of this chunk matches
- local v = version -- can be nil
- if modules and modules[filename] then
- v = modules[filename].version -- new method
- elseif versions and versions[filename] then
- v = versions[filename] -- old method
- end
- if v == version then
- return true
- else
- if trace_locating then
- logs.report("fileio","version mismatch for %s: lua=%s, luc=%s", filename, v, version)
- end
- environment.loadluafile(filename)
- end
- else
- return true
- end
- end
- fullname = (luaname and environment.luafile(luaname)) or ""
- if fullname ~= "" then
- if trace_locating then
- logs.report("fileio","loading %s", fullname)
- end
- chunk = loadfile(fullname) -- this way we don't need a file exists check
- if not chunk then
- if trace_locating then
- logs.report("fileio","unknown file %s", filename)
- end
- else
- assert(chunk)()
- return true
- end
- end
- return false
+-- todo:
+
+-- if profiler and osgetenv("MTX_PROFILE_RUN") == "YES" then
+-- profiler.start("luatex-profile.log")
+-- end
+
+-- a forward definition
+
+if not resolvers.resolve then
+ function resolvers.resolve (s) return s end
+ function resolvers.unresolve(s) return s end
+ function resolvers.repath (s) return s end
end
@@ -7659,536 +9930,1152 @@ end -- of closure
do -- create closure to overcome 200 locals limit
-if not modules then modules = { } end modules ['trac-inf'] = {
+if not modules then modules = { } end modules ['data-exp'] = {
version = 1.001,
- comment = "companion to trac-inf.mkiv",
+ comment = "companion to luat-lib.mkiv",
author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
+ license = "see context related readme files",
}
-local format = string.format
+local format, 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 Ct, Cs, Cc, P, C, S = lpeg.Ct, lpeg.Cs, lpeg.Cc, lpeg.P, lpeg.C, lpeg.S
+local type, next = type, next
-local statusinfo, n, registered = { }, 0, { }
+local ostype = os.type
+local collapsepath = file.collapsepath
-statistics = statistics or { }
+local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
+local trace_expansions = false trackers.register("resolvers.expansions", function(v) trace_expansions = v end)
-statistics.enable = true
-statistics.threshold = 0.05
+local report_expansions = logs.reporter("resolvers","expansions")
--- timing functions
+local resolvers = resolvers
-local clock = os.gettimeofday or os.clock
+-- 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. See data-obs.lua for the gsub variant.
-local notimer
+-- {a,b,c,d}
+-- a,b,c/{p,q,r},d
+-- a,b,c/{p,q,r}/d/{x,y,z}//
+-- a,b,c/{p,q/{x,y,z},r},d/{p,q,r}
+-- a,b,c/{p,q/{x,y,z},r},d/{p,q,r}
+-- a{b,c}{d,e}f
+-- {a,b,c,d}
+-- {a,b,c/{p,q,r},d}
+-- {a,b,c/{p,q,r}/d/{x,y,z}//}
+-- {a,b,c/{p,q/{x,y,z}},d/{p,q,r}}
+-- {a,b,c/{p,q/{x,y,z},w}v,d/{p,q,r}}
+-- {$SELFAUTODIR,$SELFAUTOPARENT}{,{/share,}/texmf{-local,.local,}/web2c}
-function statistics.hastimer(instance)
- return instance and instance.starttime
+local function f_first(a,b)
+ local t, n = { }, 0
+ for s in gmatch(b,"[^,]+") do
+ n = n + 1 ; t[n] = a .. s
+ end
+ return concat(t,",")
end
-function statistics.resettiming(instance)
- if not instance then
- notimer = { timing = 0, loadtime = 0 }
- else
- instance.timing, instance.loadtime = 0, 0
+local function f_second(a,b)
+ local t, n = { }, 0
+ for s in gmatch(a,"[^,]+") do
+ n = n + 1 ; t[n] = s .. b
end
+ return concat(t,",")
end
-function statistics.starttiming(instance)
- if not instance then
- notimer = { }
- instance = notimer
- end
- local it = instance.timing
- if not it then
- it = 0
- end
- if it == 0 then
- instance.starttime = clock()
- if not instance.loadtime then
- instance.loadtime = 0
+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
end
- else
---~ logs.report("system","nested timing (%s)",tostring(instance))
end
- instance.timing = it + 1
+ return concat(t,",")
end
-function statistics.stoptiming(instance, report)
- if not instance then
- instance = notimer
- end
- if instance then
- local it = instance.timing
- if it > 1 then
- instance.timing = it - 1
- else
- local starttime = instance.starttime
- if starttime then
- local stoptime = clock()
- local loadtime = stoptime - starttime
- instance.stoptime = stoptime
- instance.loadtime = instance.loadtime + loadtime
- if report then
- statistics.report("load time %0.3f",loadtime)
- end
- instance.timing = 0
- return loadtime
+local left = P("{")
+local right = P("}")
+local var = P((1 - S("{}" ))^0)
+local set = P((1 - S("{},"))^0)
+local other = P(1)
+
+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 stripper_1 = lpeg.stripper ("{}@")
+local replacer_1 = lpeg.replacer { { ",}", ",@}" }, { "{,", "{@," }, }
+
+local function splitpathexpr(str, newlist, validate) -- I couldn't resist lpegging it (nice exercise).
+ if trace_expansions then
+ report_expansions("expanding variable '%s'",str)
+ end
+ local t, ok, done = newlist or { }, false, false
+ local n = #t
+ str = lpegmatch(replacer_1,str)
+ 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
end
end
+ else
+ for s in gmatch(str,"[^,]+") do
+ n = n + 1 ; t[n] = s
+ end
end
- return 0
+ if trace_expansions then
+ for k=1,#t do
+ report_expansions("% 4i: %s",k,t[k])
+ end
+ end
+ return t
end
-function statistics.elapsedtime(instance)
- if not instance then
- instance = notimer
- end
- return format("%0.3f",(instance and instance.loadtime) or 0)
+-- We could make the previous one public.
+
+local function validate(s)
+ s = collapsepath(s) -- already keeps the //
+ return s ~= "" and not find(s,"^!*unset/*$") and s
end
-function statistics.elapsedindeed(instance)
- if not instance then
- instance = notimer
+resolvers.validatedpath = validate -- keeps the trailing //
+
+function resolvers.expandedpathfromlist(pathlist)
+ local newlist = { }
+ for k=1,#pathlist do
+ splitpathexpr(pathlist[k],newlist,validate)
end
- local t = (instance and instance.loadtime) or 0
- return t > statistics.threshold
+ return newlist
end
-function statistics.elapsedseconds(instance,rest) -- returns nil if 0 seconds
- if statistics.elapsedindeed(instance) then
- return format("%s seconds %s", statistics.elapsedtime(instance),rest or "")
+local cleanup = lpeg.replacer {
+ { "!" , "" },
+ { "\\" , "/" },
+}
+
+local homedir
+
+function resolvers.cleanpath(str)
+ if not homedir then
+ homedir = lpegmatch(cleanup,environment.homedir or "")
+ if homedir == string.char(127) or homedir == "" or not lfs.isdir(homedir) then
+ if trace_expansions then
+ report_expansions("no home dir set, ignoring dependent paths")
+ end
+ function resolvers.cleanpath(str)
+ if find(str,"~") then
+ return "" -- special case
+ else
+ return str and lpegmatch(cleanup,str)
+ end
+ end
+ else
+ cleanup = lpeg.replacer {
+ { "!" , "" },
+ { "\\" , "/" },
+ { "~" , homedir },
+ }
+ function resolvers.cleanpath(str)
+ return str and lpegmatch(cleanup,str)
+ end
+ end
end
+ return resolvers.cleanpath(str)
end
--- general function
+-- This one strips quotes and funny tokens.
-function statistics.register(tag,fnc)
- if statistics.enable and type(fnc) == "function" then
- local rt = registered[tag] or (#statusinfo + 1)
- statusinfo[rt] = { tag, fnc }
- registered[tag] = rt
- if #tag > n then n = #tag end
- end
+local expandhome = P("~") / "$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 stripper = Cs(
+ lpegpatterns.unspacer * (dosingle + dodouble + dostring) * lpegpatterns.unspacer
+)
+
+function resolvers.checkedvariable(str) -- assumes str is a string
+ return lpegmatch(stripper,str) or str
end
-function statistics.show(reporter)
- if statistics.enable then
- if not reporter then reporter = function(tag,data,n) texio.write_nl(tag .. " " .. data) end end
- -- this code will move
- local register = statistics.register
- register("luatex banner", function()
- return string.lower(status.banner)
- end)
- register("control sequences", function()
- return format("%s of %s", status.cs_count, status.hash_size+status.hash_extra)
- end)
- register("callbacks", function()
- local total, indirect = status.callbacks or 0, status.indirect_callbacks or 0
- return format("direct: %s, indirect: %s, total: %s", total-indirect, indirect, total)
- end)
- register("current memory usage", statistics.memused)
- register("runtime",statistics.runtime)
--- --
- for i=1,#statusinfo do
- local s = statusinfo[i]
- local r = s[2]()
- if r then
- reporter(s[1],r,n)
+-- The path splitter:
+
+-- A config (optionally) has the paths split in tables. Internally
+-- we join them and split them after the expansion has taken place. This
+-- is more convenient.
+
+local cache = { }
+
+---- splitter = Ct(lpeg.splitat(S(ostype == "windows" and ";" or ":;"))) -- maybe add ,
+local splitter = Ct(lpeg.splitat(";")) -- as we move towards urls, prefixes and use tables we no longer do :
+
+local backslashswapper = lpeg.replacer("\\","/")
+
+local function splitconfigurationpath(str) -- beware, this can be either a path or a { specification }
+ if str then
+ local found = cache[str]
+ if not found then
+ if str == "" then
+ found = { }
+ else
+ local split = lpegmatch(splitter,lpegmatch(backslashswapper,str)) -- can be combined
+ found = { }
+ local noffound = 0
+ for i=1,#split do
+ local s = split[i]
+ if not find(s,"^{*unset}*") then
+ noffound = noffound + 1
+ found[noffound] = s
+ end
+ end
+ if trace_expansions then
+ report_expansions("splitting path specification '%s'",str)
+ for k=1,noffound do
+ report_expansions("% 4i: %s",k,found[k])
+ end
+ end
+ cache[str] = found
end
end
- texio.write_nl("") -- final newline
- statistics.enable = false
+ return found
end
end
-function statistics.show_job_stat(tag,data,n)
- texio.write_nl(format("%-15s: %s - %s","mkiv lua stats",tag:rpadd(n," "),data))
-end
+resolvers.splitconfigurationpath = splitconfigurationpath
-function statistics.memused() -- no math.round yet -)
- local round = math.round or math.floor
- return format("%s MB (ctx: %s MB)",round(collectgarbage("count")/1000), round(status.luastate_bytes/1000000))
+function resolvers.splitpath(str)
+ if type(str) == 'table' then
+ return str
+ else
+ return splitconfigurationpath(str)
+ end
end
-if statistics.runtime then
- -- already loaded and set
-elseif luatex and luatex.starttime then
- statistics.starttime = luatex.starttime
- statistics.loadtime = 0
- statistics.timing = 0
-else
- statistics.starttiming(statistics)
+function resolvers.joinpath(str)
+ if type(str) == 'table' then
+ return file.joinpath(str)
+ else
+ return str
+ end
end
-function statistics.runtime()
- statistics.stoptiming(statistics)
- return statistics.formatruntime(statistics.elapsedtime(statistics))
-end
+-- The next function scans directories and returns a hash where the
+-- entries are either strings or tables.
-function statistics.formatruntime(runtime)
- return format("%s seconds", statistics.elapsedtime(statistics))
-end
+-- starting with . or .. etc or funny char
-function statistics.timed(action,report)
- local timer = { }
- report = report or logs.simple
- statistics.starttiming(timer)
- action()
- statistics.stoptiming(timer)
- report("total runtime: %s",statistics.elapsedtime(timer))
-end
--- where, not really the best spot for this:
-commands = commands or { }
-local timer
+local weird = P(".")^1 + lpeg.anywhere(S("~`!#$%^&*()={}[]:;\"\'||<>,?\n\r\t"))
-function commands.resettimer()
- statistics.resettiming(timer)
- statistics.starttiming(timer)
+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
+ 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
+ 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
+ return files, n, m, r
end
-function commands.elapsedtime()
- statistics.stoptiming(timer)
- tex.sprint(statistics.elapsedtime(timer))
+function resolvers.scanfiles(path,branch)
+ if trace_locating then
+ report_expansions("scanning path '%s', branch '%s'",path, branch or path)
+ end
+ local realpath = resolvers.resolve(path) -- no shortcut
+ local files, n, m, r = scan({ },realpath .. '/',"",0,0,0)
+ files.__path__ = path -- can be selfautoparent:texmf-whatever
+ files.__files__ = n
+ files.__directories__ = m
+ files.__remappings__ = r
+ if trace_locating then
+ report_expansions("%s files found on %s directories with %s uppercase remappings",n,m,r)
+ end
+ return files
end
-commands.resettimer()
end -- of closure
do -- create closure to overcome 200 locals limit
-if not modules then modules = { } end modules ['trac-log'] = {
+if not modules then modules = { } end modules ['data-env'] = {
version = 1.001,
- comment = "companion to trac-log.mkiv",
+ comment = "companion to luat-lib.mkiv",
author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
+ license = "see context related readme files",
}
--- this is old code that needs an overhaul
+local lower, gsub = string.lower, string.gsub
---~ io.stdout:setvbuf("no")
---~ io.stderr:setvbuf("no")
+local resolvers = resolvers
-local write_nl, write = texio.write_nl or print, texio.write or io.write
-local format, gmatch = string.format, string.gmatch
-local texcount = tex and tex.count
+local allocate = utilities.storage.allocate
+local setmetatableindex = table.setmetatableindex
+local fileextname = file.extname
+
+local formats = allocate()
+local suffixes = allocate()
+local dangerous = allocate()
+local suffixmap = allocate()
+
+resolvers.formats = formats
+resolvers.suffixes = suffixes
+resolvers.dangerous = dangerous
+resolvers.suffixmap = suffixmap
+
+local relations = allocate { -- todo: handlers also here
+ core = {
+ ofm = { -- will become obsolete
+ names = { "ofm", "omega font metric", "omega font metrics" },
+ variable = 'OFMFONTS',
+ suffixes = { 'ofm', 'tfm' },
+ },
+ ovf = { -- will become obsolete
+ names = { "ovf", "omega virtual font", "omega virtual fonts" },
+ variable = 'OVFFONTS',
+ suffixes = { 'ovf', 'vf' },
+ },
+ tfm = {
+ names = { "tfm", "tex font metric", "tex font metrics" },
+ variable = 'TFMFONTS',
+ suffixes = { 'tfm' },
+ },
+ vf = {
+ names = { "vf", "virtual font", "virtual fonts" },
+ variable = 'VFFONTS',
+ suffixes = { 'vf' },
+ },
+ otf = {
+ names = { "otf", "opentype", "opentype font", "opentype fonts"},
+ variable = 'OPENTYPEFONTS',
+ suffixes = { 'otf' },
+ },
+ ttf = {
+ names = { "ttf", "truetype", "truetype font", "truetype fonts", "truetype collection", "truetype collections", "truetype dictionary", "truetype dictionaries" },
+ variable = 'TTFONTS',
+ suffixes = { 'ttf', 'ttc', 'dfont' },
+ },
+ afm = {
+ names = { "afm", "adobe font metric", "adobe font metrics" },
+ variable = "AFMFONTS",
+ suffixes = { "afm" },
+ },
+ pfb = {
+ names = { "pfb", "type1", "type 1", "type1 font", "type 1 font", "type1 fonts", "type 1 fonts" },
+ variable = 'T1FONTS',
+ suffixes = { 'pfb', 'pfa' },
+ },
+ fea = {
+ names = { "fea", "font feature", "font features", "font feature file", "font feature files" },
+ variable = 'FONTFEATURES',
+ suffixes = { 'fea' },
+ },
+ cid = {
+ names = { "cid", "cid map", "cid maps", "cid file", "cid files" },
+ variable = 'FONTCIDMAPS',
+ suffixes = { 'cid', 'cidmap' },
+ },
+ fmt = {
+ names = { "fmt", "format", "tex format" },
+ variable = 'TEXFORMATS',
+ suffixes = { 'fmt' },
+ },
+ mem = { -- will become obsolete
+ names = { 'mem', "metapost format" },
+ variable = 'MPMEMS',
+ suffixes = { 'mem' },
+ },
+ mp = {
+ names = { "mp" },
+ variable = 'MPINPUTS',
+ suffixes = { 'mp' },
+ },
+ tex = {
+ names = { "tex" },
+ variable = 'TEXINPUTS',
+ suffixes = { 'tex', "mkiv", "mkiv", "mkii" },
+ },
+ icc = {
+ names = { "icc", "icc profile", "icc profiles" },
+ variable = 'ICCPROFILES',
+ suffixes = { 'icc' },
+ },
+ texmfscripts = {
+ names = { "texmfscript", "texmfscripts", "script", "scripts" },
+ variable = 'TEXMFSCRIPTS',
+ suffixes = { 'rb', 'pl', 'py' },
+ },
+ lua = {
+ names = { "lua" },
+ variable = 'LUAINPUTS',
+ suffixes = { 'lua', 'luc', 'tma', 'tmc' },
+ },
+ lib = {
+ names = { "lib" },
+ variable = 'CLUAINPUTS',
+ suffixes = os.libsuffix and { os.libsuffix } or { 'dll', 'so' },
+ },
+ bib = {
+ names = { 'bib' },
+ suffixes = { 'bib' },
+ },
+ bst = {
+ names = { 'bst' },
+ suffixes = { 'bst' },
+ },
+ fontconfig = {
+ names = { 'fontconfig', 'fontconfig file', 'fontconfig files' },
+ variable = 'FONTCONFIG_PATH',
+ },
+ },
+ obsolete = {
+ enc = {
+ names = { "enc", "enc files", "enc file", "encoding files", "encoding file" },
+ variable = 'ENCFONTS',
+ suffixes = { 'enc' },
+ },
+ map = {
+ names = { "map", "map files", "map file" },
+ variable = 'TEXFONTMAPS',
+ suffixes = { 'map' },
+ },
+ lig = {
+ names = { "lig files", "lig file", "ligature file", "ligature files" },
+ variable = 'LIGFONTS',
+ suffixes = { 'lig' },
+ },
+ opl = {
+ names = { "opl" },
+ variable = 'OPLFONTS',
+ suffixes = { 'opl' },
+ },
+ ovp = {
+ names = { "ovp" },
+ variable = 'OVPFONTS',
+ suffixes = { 'ovp' },
+ },
+ },
+ kpse = { -- subset
+ base = {
+ names = { 'base', "metafont format" },
+ variable = 'MFBASES',
+ suffixes = { 'base', 'bas' },
+ },
+ cmap = {
+ names = { 'cmap', 'cmap files', 'cmap file' },
+ variable = 'CMAPFONTS',
+ suffixes = { 'cmap' },
+ },
+ cnf = {
+ names = { 'cnf' },
+ suffixes = { 'cnf' },
+ },
+ web = {
+ names = { 'web' },
+ suffixes = { 'web', 'ch' }
+ },
+ cweb = {
+ names = { 'cweb' },
+ suffixes = { 'w', 'web', 'ch' },
+ },
+ gf = {
+ names = { 'gf' },
+ suffixes = { '<resolution>gf' },
+ },
+ mf = {
+ names = { 'mf' },
+ variable = 'MFINPUTS',
+ suffixes = { 'mf' },
+ },
+ mft = {
+ names = { 'mft' },
+ suffixes = { 'mft' },
+ },
+ pk = {
+ names = { 'pk' },
+ suffixes = { '<resolution>pk' },
+ },
+ },
+}
-if texlua then
- write_nl = print
- write = io.write
+resolvers.relations = relations
+
+-- formats: maps a format onto a variable
+
+function resolvers.updaterelations()
+ for category, categories in next, relations do
+ for name, relation in next, categories do
+ local rn = relation.names
+ local rv = relation.variable
+ local rs = relation.suffixes
+ if rn and rv then
+ for i=1,#rn do
+ local rni = lower(gsub(rn[i]," ",""))
+ formats[rni] = rv
+ if rs then
+ suffixes[rni] = rs
+ for i=1,#rs do
+ local rsi = rs[i]
+ suffixmap[rsi] = rni
+ end
+ end
+ end
+ end
+ if rs then
+ end
+ end
+ end
end
---[[ldx--
-<p>This is a prelude to a more extensive logging module. For the sake
-of parsing log files, in addition to the standard logging we will
-provide an <l n='xml'/> structured file. Actually, any logging that
-is hooked into callbacks will be \XML\ by default.</p>
---ldx]]--
+resolvers.updaterelations() -- push this in the metatable -> newindex
+
+local function simplified(t,k)
+ return rawget(t,lower(gsub(k," ","")))
+end
-logs = logs or { }
-logs.xml = logs.xml or { }
-logs.tex = logs.tex or { }
+setmetatableindex(formats, simplified)
+setmetatableindex(suffixes, simplified)
+setmetatableindex(suffixmap, simplified)
---[[ldx--
-<p>This looks pretty ugly but we need to speed things up a bit.</p>
---ldx]]--
+-- A few accessors, mostly for command line tool.
-logs.moreinfo = [[
-more information about ConTeXt and the tools that come with it can be found at:
+function resolvers.suffixofformat(str)
+ local s = suffixes[str]
+ return s and s[1] or ""
+end
-maillist : ntg-context@ntg.nl / http://www.ntg.nl/mailman/listinfo/ntg-context
-webpage : http://www.pragma-ade.nl / http://tex.aanhet.net
-wiki : http://contextgarden.net
-]]
+function resolvers.suffixofformat(str)
+ return suffixes[str] or { }
+end
-logs.levels = {
- ['error'] = 1,
- ['warning'] = 2,
- ['info'] = 3,
- ['debug'] = 4,
-}
+for name, format in next, formats do
+ dangerous[name] = true -- still needed ?
+end
-logs.functions = {
- 'report', 'start', 'stop', 'push', 'pop', 'line', 'direct',
- 'start_run', 'stop_run',
- 'start_page_number', 'stop_page_number',
- 'report_output_pages', 'report_output_log',
- 'report_tex_stat', 'report_job_stat',
- 'show_open', 'show_close', 'show_load',
-}
+-- because vf searching is somewhat dangerous, we want to prevent
+-- too liberal searching esp because we do a lookup on the current
+-- path anyway; only tex (or any) is safe
-logs.tracers = {
-}
+dangerous.tex = nil
-logs.level = 0
-logs.mode = string.lower((os.getenv("MTX.LOG.MODE") or os.getenv("MTX_LOG_MODE") or "tex"))
-function logs.set_level(level)
- logs.level = logs.levels[level] or level
-end
+-- more helpers
-function logs.set_method(method)
- for _, v in next, logs.functions do
- logs[v] = logs[method][v] or function() end
- end
+function resolvers.formatofvariable(str)
+ return formats[str] or ''
end
--- tex logging
+function resolvers.formatofsuffix(str) -- of file
+ return suffixmap[fileextname(str)] or 'tex' -- so many map onto tex (like mkiv, cld etc)
+end
-function logs.tex.report(category,fmt,...) -- new
- if fmt then
- write_nl(category .. " | " .. format(fmt,...))
- else
- write_nl(category .. " |")
- end
+function resolvers.variableofformat(str)
+ return formats[str] or ''
end
-function logs.tex.line(fmt,...) -- new
- if fmt then
- write_nl(format(fmt,...))
- else
- write_nl("")
+function resolvers.variableofformatorsuffix(str)
+ local v = formats[str]
+ if v then
+ return v
+ end
+ v = suffixmap[fileextname(str)]
+ if v then
+ return formats[v]
end
+ return ''
end
---~ function logs.tex.start_page_number()
---~ local real, user, sub = texcount.realpageno, texcount.userpageno, texcount.subpageno
---~ if real > 0 then
---~ if user > 0 then
---~ if sub > 0 then
---~ write(format("[%s.%s.%s",real,user,sub))
---~ else
---~ write(format("[%s.%s",real,user))
---~ end
---~ else
---~ write(format("[%s",real))
---~ end
---~ else
---~ write("[-")
---~ end
---~ end
---~ function logs.tex.stop_page_number()
---~ write("]")
---~ end
-local real, user, sub
+end -- of closure
-function logs.tex.start_page_number()
- real, user, sub = texcount.realpageno, texcount.userpageno, texcount.subpageno
+do -- create closure to overcome 200 locals limit
+
+if not modules then modules = { } end modules ['data-tmp'] = {
+ version = 1.100,
+ comment = "companion to luat-lib.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+--[[ldx--
+<p>This module deals with caching data. It sets up the paths and
+implements loaders and savers for tables. Best is to set the
+following variable. When not set, the usual paths will be
+checked. Personally I prefer the (users) temporary path.</p>
+
+</code>
+TEXMFCACHE=$TMP;$TEMP;$TMPDIR;$TEMPDIR;$HOME;$TEXMFVAR;$VARTEXMF;.
+</code>
+
+<p>Currently we do no locking when we write files. This is no real
+problem because most caching involves fonts and the chance of them
+being written at the same time is small. We also need to extend
+luatools with a recache feature.</p>
+--ldx]]--
+
+local format, lower, gsub, concat = string.format, string.lower, string.gsub, table.concat
+local mkdirs, isdir = dir.mkdirs, lfs.isdir
+
+local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
+local trace_cache = false trackers.register("resolvers.cache", function(v) trace_cache = v end)
+
+local report_caches = logs.reporter("resolvers","caches")
+local report_resolvers = logs.reporter("resolvers","caching")
+
+local resolvers = resolvers
+
+-- intermezzo
+
+local directive_cleanup = false directives.register("system.compile.cleanup", function(v) directive_cleanup = v end)
+local directive_strip = true directives.register("system.compile.strip", function(v) directive_strip = v end)
+
+local compile = utilities.lua.compile
+
+function utilities.lua.compile(luafile,lucfile,cleanup,strip)
+ if cleanup == nil then cleanup = directive_cleanup end
+ if strip == nil then strip = directive_strip end
+ return compile(luafile,lucfile,cleanup,strip)
end
-function logs.tex.stop_page_number()
- if real > 0 then
- if user > 0 then
- if sub > 0 then
- logs.report("pages", "flushing realpage %s, userpage %s, subpage %s",real,user,sub)
- else
- logs.report("pages", "flushing realpage %s, userpage %s",real,user)
+-- end of intermezzo
+
+caches = caches or { }
+local caches = caches
+
+caches.base = caches.base or "luatex-cache"
+caches.more = caches.more or "context"
+caches.direct = false -- true is faster but may need huge amounts of memory
+caches.tree = false
+caches.force = true
+caches.ask = false
+caches.relocate = false
+caches.defaults = { "TMPDIR", "TEMPDIR", "TMP", "TEMP", "HOME", "HOMEPATH" }
+
+local writable, readables, usedreadables = nil, { }, { }
+
+-- we could use a metatable for writable and readable but not yet
+
+local function identify()
+ -- Combining the loops makes it messy. First we check the format cache path
+ -- and when the last component is not present we try to create it.
+ local texmfcaches = resolvers.cleanpathlist("TEXMFCACHE")
+ if texmfcaches then
+ for k=1,#texmfcaches do
+ local cachepath = texmfcaches[k]
+ if cachepath ~= "" then
+ cachepath = resolvers.resolve(cachepath)
+ cachepath = resolvers.cleanpath(cachepath)
+ cachepath = file.collapsepath(cachepath)
+ local valid = isdir(cachepath)
+ if valid then
+ if file.is_readable(cachepath) then
+ readables[#readables+1] = cachepath
+ if not writable and file.is_writable(cachepath) then
+ writable = cachepath
+ end
+ end
+ elseif not writable and caches.force then
+ local cacheparent = file.dirname(cachepath)
+ if file.is_writable(cacheparent) then
+ if not caches.ask or io.ask(format("\nShould I create the cache path %s?",cachepath), "no", { "yes", "no" }) == "yes" then
+ mkdirs(cachepath)
+ if isdir(cachepath) and file.is_writable(cachepath) then
+ report_caches("created: %s",cachepath)
+ writable = cachepath
+ readables[#readables+1] = cachepath
+ end
+ end
+ end
+ end
end
- else
- logs.report("pages", "flushing realpage %s",real)
+ end
+ end
+ -- As a last resort we check some temporary paths but this time we don't
+ -- create them.
+ local texmfcaches = caches.defaults
+ if texmfcaches then
+ for k=1,#texmfcaches do
+ local cachepath = texmfcaches[k]
+ cachepath = resolvers.expansion(cachepath) -- was getenv
+ if cachepath ~= "" then
+ cachepath = resolvers.resolve(cachepath)
+ cachepath = resolvers.cleanpath(cachepath)
+ local valid = isdir(cachepath)
+ if valid and file.is_readable(cachepath) then
+ if not writable and file.is_writable(cachepath) then
+ readables[#readables+1] = cachepath
+ writable = cachepath
+ break
+ end
+ end
+ end
+ end
+ end
+ -- Some extra checking. If we have no writable or readable path then we simply
+ -- quit.
+ if not writable then
+ report_caches("fatal error: there is no valid writable cache path defined")
+ os.exit()
+ elseif #readables == 0 then
+ report_caches("fatal error: there is no valid readable cache path defined")
+ os.exit()
+ end
+ -- why here
+ writable = dir.expandname(resolvers.cleanpath(writable)) -- just in case
+ -- moved here
+ local base, more, tree = caches.base, caches.more, caches.tree or caches.treehash() -- we have only one writable tree
+ if tree then
+ caches.tree = tree
+ writable = mkdirs(writable,base,more,tree)
+ for i=1,#readables do
+ readables[i] = file.join(readables[i],base,more,tree)
end
else
- logs.report("pages", "flushing page")
+ writable = mkdirs(writable,base,more)
+ for i=1,#readables do
+ readables[i] = file.join(readables[i],base,more)
+ end
+ end
+ -- end
+ if trace_cache then
+ for i=1,#readables do
+ report_caches("using readable path '%s' (order %s)",readables[i],i)
+ end
+ report_caches("using writable path '%s'",writable)
+ end
+ identify = function()
+ return writable, readables
end
- io.flush()
+ return writable, readables
end
-logs.tex.report_job_stat = statistics.show_job_stat
+function caches.usedpaths()
+ local writable, readables = identify()
+ if #readables > 1 then
+ local result = { }
+ for i=1,#readables do
+ local readable = readables[i]
+ if usedreadables[i] or readable == writable then
+ result[#result+1] = format("readable: '%s' (order %s)",readable,i)
+ end
+ end
+ result[#result+1] = format("writable: '%s'",writable)
+ return result
+ else
+ return writable
+ end
+end
--- xml logging
+function caches.configfiles()
+ return table.concat(resolvers.instance.specification,";")
+end
-function logs.xml.report(category,fmt,...) -- new
- if fmt then
- write_nl(format("<r category='%s'>%s</r>",category,format(fmt,...)))
- else
- write_nl(format("<r category='%s'/>",category))
+function caches.hashed(tree)
+ tree = gsub(tree,"\\$","/")
+ tree = gsub(tree,"/+$","")
+ tree = lower(tree)
+ local hash = md5.hex(tree)
+ if trace_cache or trace_locating then
+ report_caches("hashing tree %s, hash %s",tree,hash)
end
+ return hash
end
-function logs.xml.line(fmt,...) -- new
- if fmt then
- write_nl(format("<r>%s</r>",format(fmt,...)))
+
+function caches.treehash()
+ local tree = caches.configfiles()
+ if not tree or tree == "" then
+ return false
else
- write_nl("<r/>")
+ return caches.hashed(tree)
end
end
-function logs.xml.start() if logs.level > 0 then tw("<%s>" ) end end
-function logs.xml.stop () if logs.level > 0 then tw("</%s>") end end
-function logs.xml.push () if logs.level > 0 then tw("<!-- ") end end
-function logs.xml.pop () if logs.level > 0 then tw(" -->" ) end end
+local r_cache, w_cache = { }, { } -- normally w in in r but who cares
-function logs.xml.start_run()
- write_nl("<?xml version='1.0' standalone='yes'?>")
- write_nl("<job>") -- xmlns='www.pragma-ade.com/luatex/schemas/context-job.rng'
- write_nl("")
-end
-
-function logs.xml.stop_run()
- write_nl("</job>")
+local function getreadablepaths(...) -- we can optimize this as we have at most 2 tags
+ local tags = { ... }
+ local hash = concat(tags,"/")
+ local done = r_cache[hash]
+ if not done then
+ local writable, readables = identify() -- exit if not found
+ if #tags > 0 then
+ done = { }
+ for i=1,#readables do
+ done[i] = file.join(readables[i],...)
+ end
+ else
+ done = readables
+ end
+ r_cache[hash] = done
+ end
+ return done
end
-function logs.xml.start_page_number()
- write_nl(format("<p real='%s' page='%s' sub='%s'", texcount.realpageno, texcount.userpageno, texcount.subpageno))
+local function getwritablepath(...)
+ local tags = { ... }
+ local hash = concat(tags,"/")
+ local done = w_cache[hash]
+ if not done then
+ local writable, readables = identify() -- exit if not found
+ if #tags > 0 then
+ done = mkdirs(writable,...)
+ else
+ done = writable
+ end
+ w_cache[hash] = done
+ end
+ return done
end
-function logs.xml.stop_page_number()
- write("/>")
- write_nl("")
-end
+caches.getreadablepaths = getreadablepaths
+caches.getwritablepath = getwritablepath
-function logs.xml.report_output_pages(p,b)
- write_nl(format("<v k='pages' v='%s'/>", p))
- write_nl(format("<v k='bytes' v='%s'/>", b))
- write_nl("")
+function caches.getfirstreadablefile(filename,...)
+ local rd = getreadablepaths(...)
+ for i=1,#rd do
+ local path = rd[i]
+ local fullname = file.join(path,filename)
+ if file.is_readable(fullname) then
+ usedreadables[i] = true
+ return fullname, path
+ end
+ end
+ return caches.setfirstwritablefile(filename,...)
end
-function logs.xml.report_output_log()
+function caches.setfirstwritablefile(filename,...)
+ local wr = getwritablepath(...)
+ local fullname = file.join(wr,filename)
+ return fullname, wr
end
-function logs.xml.report_tex_stat(k,v)
- texiowrite_nl("log","<v k='"..k.."'>"..tostring(v).."</v>")
+function caches.define(category,subcategory) -- for old times sake
+ return function()
+ return getwritablepath(category,subcategory)
+ end
end
-local level = 0
-
-function logs.xml.show_open(name)
- level = level + 1
- texiowrite_nl(format("<f l='%s' n='%s'>",level,name))
+function caches.setluanames(path,name)
+ return path .. "/" .. name .. ".tma", path .. "/" .. name .. ".tmc"
end
-function logs.xml.show_close(name)
- texiowrite("</f> ")
- level = level - 1
+function caches.loaddata(readables,name)
+ if type(readables) == "string" then
+ readables = { readables }
+ end
+ for i=1,#readables do
+ local path = readables[i]
+ local tmaname, tmcname = caches.setluanames(path,name)
+ local loader = loadfile(tmcname) or loadfile(tmaname)
+ if loader then
+ loader = loader()
+ collectgarbage("step")
+ return loader
+ end
+ end
+ return false
end
-function logs.xml.show_load(name)
- texiowrite_nl(format("<f l='%s' n='%s'/>",level+1,name))
+function caches.is_writable(filepath,filename)
+ local tmaname, tmcname = caches.setluanames(filepath,filename)
+ return file.is_writable(tmaname)
end
---
-
-local name, banner = 'report', 'context'
+local saveoptions = { compact = true }
-local function report(category,fmt,...)
- if fmt then
- write_nl(format("%s | %s: %s",name,category,format(fmt,...)))
- elseif category then
- write_nl(format("%s | %s",name,category))
+function caches.savedata(filepath,filename,data,raw)
+ local tmaname, tmcname = caches.setluanames(filepath,filename)
+ local reduce, simplify = true, true
+ if raw then
+ reduce, simplify = false, false
+ end
+ data.cache_uuid = os.uuid()
+ if caches.direct then
+ file.savedata(tmaname,table.serialize(data,true,saveoptions))
else
- write_nl(format("%s |",name))
+ table.tofile(tmaname,data,true,saveoptions)
end
+ utilities.lua.compile(tmaname,tmcname)
end
-local function simple(fmt,...)
- if fmt then
- write_nl(format("%s | %s",name,format(fmt,...)))
- else
- write_nl(format("%s |",name))
- end
+-- moved from data-res:
+
+local content_state = { }
+
+function caches.contentstate()
+ return content_state or { }
end
-function logs.setprogram(_name_,_banner_,_verbose_)
- name, banner = _name_, _banner_
- if _verbose_ then
- trackers.enable("resolvers.locating")
- end
- logs.set_method("tex")
- logs.report = report -- also used in libraries
- logs.simple = simple -- only used in scripts !
- if utils then
- utils.report = simple
+function caches.loadcontent(cachename,dataname)
+ local name = caches.hashed(cachename)
+ local full, path = caches.getfirstreadablefile(name ..".lua","trees")
+ local filename = file.join(path,name)
+ local blob = loadfile(filename .. ".luc") or loadfile(filename .. ".lua")
+ if blob then
+ local data = blob()
+ if data and data.content then
+ if data.type == dataname then
+ if data.version == resolvers.cacheversion then
+ content_state[#content_state+1] = data.uuid
+ if trace_locating then
+ report_resolvers("loading '%s' for '%s' from '%s'",dataname,cachename,filename)
+ end
+ return data.content
+ else
+ report_resolvers("skipping '%s' for '%s' from '%s' (version mismatch)",dataname,cachename,filename)
+ end
+ else
+ report_resolvers("skipping '%s' for '%s' from '%s' (datatype mismatch)",dataname,cachename,filename)
+ end
+ elseif trace_locating then
+ report_resolvers("skipping '%s' for '%s' from '%s' (no content)",dataname,cachename,filename)
+ end
+ elseif trace_locating then
+ report_resolvers("skipping '%s' for '%s' from '%s' (invalid file)",dataname,cachename,filename)
end
- logs.verbose = _verbose_
end
-function logs.setverbose(what)
- if what then
- trackers.enable("resolvers.locating")
- else
- trackers.disable("resolvers.locating")
+function caches.collapsecontent(content)
+ for k, v in next, content do
+ if type(v) == "table" and #v == 1 then
+ content[k] = v[1]
+ end
end
- logs.verbose = what or false
end
-function logs.extendbanner(_banner_,_verbose_)
- banner = banner .. " | ".. _banner_
- if _verbose_ ~= nil then
- logs.setverbose(what)
+function caches.savecontent(cachename,dataname,content)
+ local name = caches.hashed(cachename)
+ local full, path = caches.setfirstwritablefile(name ..".lua","trees")
+ local filename = file.join(path,name) -- is full
+ local luaname, lucname = filename .. ".lua", filename .. ".luc"
+ if trace_locating then
+ report_resolvers("preparing '%s' for '%s'",dataname,cachename)
+ end
+ local data = {
+ type = dataname,
+ root = cachename,
+ version = resolvers.cacheversion,
+ date = os.date("%Y-%m-%d"),
+ time = os.date("%H:%M:%S"),
+ content = content,
+ uuid = os.uuid(),
+ }
+ local ok = io.savedata(luaname,table.serialize(data,true))
+ if ok then
+ if trace_locating then
+ report_resolvers("category '%s', cachename '%s' saved in '%s'",dataname,cachename,luaname)
+ end
+ if utilities.lua.compile(luaname,lucname) then
+ if trace_locating then
+ report_resolvers("'%s' compiled to '%s'",dataname,lucname)
+ end
+ return true
+ else
+ if trace_locating then
+ report_resolvers("compiling failed for '%s', deleting file '%s'",dataname,lucname)
+ end
+ os.remove(lucname)
+ end
+ elseif trace_locating then
+ report_resolvers("unable to save '%s' in '%s' (access error)",dataname,luaname)
end
end
-logs.verbose = false
-logs.report = logs.tex.report
-logs.simple = logs.tex.report
-function logs.reportlines(str) -- todo: <lines></lines>
- for line in gmatch(str,"(.-)[\n\r]") do
- logs.report(line)
- end
-end
+end -- of closure
-function logs.reportline() -- for scripts too
- logs.report()
-end
+do -- create closure to overcome 200 locals limit
+
+if not modules then modules = { } end modules ['data-met'] = {
+ version = 1.100,
+ comment = "companion to luat-lib.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+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 report_methods = logs.reporter("resolvers","methods")
-logs.simpleline = logs.reportline
+local allocate = utilities.storage.allocate
-function logs.reportbanner() -- for scripts too
- logs.report(banner)
+local resolvers = resolvers
+
+local registered = { }
+
+local function splitmethod(filename) -- todo: filetype in specification
+ if not filename then
+ return { scheme = "unknown", original = filename }
+ end
+ if type(filename) == "table" then
+ return filename -- already split
+ 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 specification
+ end
end
-function logs.help(message,option)
- logs.reportbanner()
- logs.reportline()
- logs.reportlines(message)
- local moreinfo = logs.moreinfo or ""
- if moreinfo ~= "" and option ~= "nomoreinfo" then
- logs.reportline()
- logs.reportlines(moreinfo)
+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_methods("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_methods("resolver: method=%s, how=%s, default, argument=%s",what,how,first)
+ end
+ return resolver(specification,...)
+ elseif trace_methods then
+ report_methods("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_methods("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_methods("resolver: method=%s, how=%s, default",what,how)
+ end
+ return resolver(...)
+ elseif trace_methods then
+ report_methods("resolver: method=%s, how=%s, unknown",what,how)
+ end
+ end
+ end
+ else
+ report_methods("resolver: method=%s, unknown",what)
end
end
-logs.set_level('error')
-logs.set_method('tex')
+resolvers.methodhandler = methodhandler
-function logs.system(whereto,process,jobname,category,...)
- for i=1,10 do
- local f = io.open(whereto,"a")
- if f then
- f:write(format("%s %s => %s => %s => %s\r",os.date("%d/%m/%y %H:%m:%S"),process,jobname,category,format(...)))
- f:close()
- break
+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
- sleep(0.1)
+ return methodhandler(name,addurlscheme(filename,scheme),...)
end
end
end
---~ local syslogname = "oeps.xxx"
---~
---~ for i=1,10 do
---~ logs.system(syslogname,"context","test","fonts","font %s recached due to newer version (%s)","blabla","123")
---~ end
+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
-function logs.fatal(where,...)
- logs.report(where,"fatal error: %s, aborting now",format(...))
- os.exit()
-end
+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
do -- create closure to overcome 200 locals limit
-if not modules then modules = { } end modules ['data-inp'] = {
+if not modules then modules = { } end modules ['data-res'] = {
version = 1.001,
comment = "companion to luat-lib.mkiv",
author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
@@ -8196,981 +11083,510 @@ if not modules then modules = { } end modules ['data-inp'] = {
license = "see context related readme files",
}
--- After a few years using the code the large luat-inp.lua file
--- has been split up a bit. In the process some functionality was
--- dropped:
---
--- * support for reading lsr files
--- * selective scanning (subtrees)
--- * some public auxiliary functions were made private
---
--- TODO: os.getenv -> os.env[]
--- TODO: instances.[hashes,cnffiles,configurations,522]
--- TODO: check escaping in find etc, too much, too slow
-
--- This lib is multi-purpose and can be loaded again later on so that
--- additional functionality becomes available. We will split thislogs.report("fileio",
--- module in components once we're done with prototyping. This is the
--- first code I wrote for LuaTeX, so it needs some cleanup. Before changing
--- something in this module one can best check with Taco or Hans first; there
--- is some nasty trickery going on that relates to traditional kpse support.
-
--- To be considered: hash key lowercase, first entry in table filename
--- (any case), rest paths (so no need for optimization). Or maybe a
--- separate table that matches lowercase names to mixed case when
--- present. In that case the lower() cases can go away. I will do that
--- only when we run into problems with names ... well ... Iwona-Regular.
+-- In practice we will work within one tds tree, but i want to keep
+-- the option open to build tools that look at multiple trees, which is
+-- why we keep the tree specific data in a table. We used to pass the
+-- instance but for practical purposes we now avoid this and use a
+-- instance variable. We always have one instance active (sort of global).
--- Beware, loading and saving is overloaded in luat-tmp!
+-- todo: cache:/// home:///
local format, gsub, find, lower, upper, match, gmatch = string.format, string.gsub, string.find, string.lower, string.upper, string.match, string.gmatch
local concat, insert, sortedkeys = table.concat, table.insert, table.sortedkeys
-local next, type = next, type
-local lpegmatch = lpeg.match
+local next, type, rawget = next, type, rawget
+local os = os
-local trace_locating, trace_detail, trace_expansions = false, false, false
+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
-trackers.register("resolvers.locating", function(v) trace_locating = v end)
-trackers.register("resolvers.details", function(v) trace_detail = v end)
-trackers.register("resolvers.expansions", function(v) trace_expansions = v end) -- todo
+local filedirname = file.dirname
+local filebasename = file.basename
+local fileextname = file.extname
+local filejoin = file.join
+local collapsepath = file.collapsepath
+local joinpath = file.joinpath
+local allocate = utilities.storage.allocate
+local setmetatableindex = table.setmetatableindex
-if not resolvers then
- resolvers = {
- suffixes = { },
- formats = { },
- dangerous = { },
- suffixmap = { },
- alternatives = { },
- locators = { }, -- locate databases
- hashers = { }, -- load databases
- generators = { }, -- generate databases
- }
-end
+local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
+local trace_detail = false trackers.register("resolvers.details", function(v) trace_detail = v end)
+local trace_expansions = false trackers.register("resolvers.expansions", function(v) trace_expansions = v end)
-local resolvers = resolvers
+local report_resolving = logs.reporter("resolvers","resolving")
-resolvers.locators .notfound = { nil }
-resolvers.hashers .notfound = { nil }
-resolvers.generators.notfound = { nil }
-
-resolvers.cacheversion = '1.0.1'
-resolvers.cnfname = 'texmf.cnf'
-resolvers.luaname = 'texmfcnf.lua'
-resolvers.homedir = os.env[os.type == "windows" and 'USERPROFILE'] or os.env['HOME'] or '~'
-resolvers.cnfdefault = '{$SELFAUTODIR,$SELFAUTOPARENT}{,{/share,}/texmf{-local,.local,}/web2c}'
-
-local dummy_path_expr = "^!*unset/*$"
-
-local formats = resolvers.formats
-local suffixes = resolvers.suffixes
-local dangerous = resolvers.dangerous
-local suffixmap = resolvers.suffixmap
-local alternatives = resolvers.alternatives
-
-formats['afm'] = 'AFMFONTS' suffixes['afm'] = { 'afm' }
-formats['enc'] = 'ENCFONTS' suffixes['enc'] = { 'enc' }
-formats['fmt'] = 'TEXFORMATS' suffixes['fmt'] = { 'fmt' }
-formats['map'] = 'TEXFONTMAPS' suffixes['map'] = { 'map' }
-formats['mp'] = 'MPINPUTS' suffixes['mp'] = { 'mp' }
-formats['ocp'] = 'OCPINPUTS' suffixes['ocp'] = { 'ocp' }
-formats['ofm'] = 'OFMFONTS' suffixes['ofm'] = { 'ofm', 'tfm' }
-formats['otf'] = 'OPENTYPEFONTS' suffixes['otf'] = { 'otf' } -- 'ttf'
-formats['opl'] = 'OPLFONTS' suffixes['opl'] = { 'opl' }
-formats['otp'] = 'OTPINPUTS' suffixes['otp'] = { 'otp' }
-formats['ovf'] = 'OVFFONTS' suffixes['ovf'] = { 'ovf', 'vf' }
-formats['ovp'] = 'OVPFONTS' suffixes['ovp'] = { 'ovp' }
-formats['tex'] = 'TEXINPUTS' suffixes['tex'] = { 'tex' }
-formats['tfm'] = 'TFMFONTS' suffixes['tfm'] = { 'tfm' }
-formats['ttf'] = 'TTFONTS' suffixes['ttf'] = { 'ttf', 'ttc', 'dfont' }
-formats['pfb'] = 'T1FONTS' suffixes['pfb'] = { 'pfb', 'pfa' }
-formats['vf'] = 'VFFONTS' suffixes['vf'] = { 'vf' }
-
-formats['fea'] = 'FONTFEATURES' suffixes['fea'] = { 'fea' }
-formats['cid'] = 'FONTCIDMAPS' suffixes['cid'] = { 'cid', 'cidmap' }
-
-formats ['texmfscripts'] = 'TEXMFSCRIPTS' -- new
-suffixes['texmfscripts'] = { 'rb', 'pl', 'py' } -- 'lua'
-
-formats ['lua'] = 'LUAINPUTS' -- new
-suffixes['lua'] = { 'lua', 'luc', 'tma', 'tmc' }
-
--- backward compatible ones
-
-alternatives['map files'] = 'map'
-alternatives['enc files'] = 'enc'
-alternatives['cid maps'] = 'cid' -- great, why no cid files
-alternatives['font feature files'] = 'fea' -- and fea files here
-alternatives['opentype fonts'] = 'otf'
-alternatives['truetype fonts'] = 'ttf'
-alternatives['truetype collections'] = 'ttc'
-alternatives['truetype dictionary'] = 'dfont'
-alternatives['type1 fonts'] = 'pfb'
-
--- obscure ones
-
-formats ['misc fonts'] = ''
-suffixes['misc fonts'] = { }
-
-formats ['sfd'] = 'SFDFONTS'
-suffixes ['sfd'] = { 'sfd' }
-alternatives['subfont definition files'] = 'sfd'
-
--- lib paths
-
-formats ['lib'] = 'CLUAINPUTS' -- new (needs checking)
-suffixes['lib'] = (os.libsuffix and { os.libsuffix }) or { 'dll', 'so' }
+local resolvers = resolvers
--- In practice we will work within one tds tree, but i want to keep
--- the option open to build tools that look at multiple trees, which is
--- why we keep the tree specific data in a table. We used to pass the
--- instance but for practical pusposes we now avoid this and use a
--- instance variable.
+local expandedpathfromlist = resolvers.expandedpathfromlist
+local checkedvariable = resolvers.checkedvariable
+local splitconfigurationpath = resolvers.splitconfigurationpath
+local methodhandler = resolvers.methodhandler
--- here we catch a few new thingies (todo: add these paths to context.tmf)
---
--- FONTFEATURES = .;$TEXMF/fonts/fea//
--- FONTCIDMAPS = .;$TEXMF/fonts/cid//
+local initializesetter = utilities.setters.initialize
--- we always have one instance active
+local ostype, osname, osenv, ossetenv, osgetenv = os.type, os.name, os.env, os.setenv, os.getenv
-resolvers.instance = resolvers.instance or nil -- the current one (slow access)
-local instance = resolvers.instance or nil -- the current one (fast access)
+resolvers.cacheversion = '1.0.1'
+resolvers.configbanner = ''
+resolvers.homedir = environment.homedir
+resolvers.criticalvars = allocate { "SELFAUTOLOC", "SELFAUTODIR", "SELFAUTOPARENT", "TEXMFCNF", "TEXMF", "TEXOS" }
+resolvers.luacnfname = 'texmfcnf.lua'
+resolvers.luacnfstate = "unknown"
-function resolvers.newinstance()
+-- resolvers.luacnfspec = '{$SELFAUTODIR,$SELFAUTOPARENT}{,{/share,}/texmf{-local,}/web2c}' -- what a rubish path
+resolvers.luacnfspec = 'selfautoparent:{/texmf{-local,}{,/web2c},}}'
- -- store once, freeze and faster (once reset we can best use
- -- instance.environment) maybe better have a register suffix
- -- function
- for k, v in next, suffixes do
- for i=1,#v do
- local vi = v[i]
- if vi then
- suffixmap[vi] = k
- end
- end
- end
- -- because vf searching is somewhat dangerous, we want to prevent
- -- too liberal searching esp because we do a lookup on the current
- -- path anyway; only tex (or any) is safe
+local unset_variable = "unset"
- for k, v in next, formats do
- dangerous[k] = true
- end
- dangerous.tex = nil
+local formats = resolvers.formats
+local suffixes = resolvers.suffixes
+local dangerous = resolvers.dangerous
+local suffixmap = resolvers.suffixmap
- -- the instance
+resolvers.defaultsuffixes = { "tex" } -- "mkiv", "cld" -- too tricky
- local newinstance = {
- rootpath = '',
- treepath = '',
- progname = 'context',
- engine = 'luatex',
- format = '',
- environment = { },
- variables = { },
- expansions = { },
- files = { },
- remap = { },
- configuration = { },
- setup = { },
- order = { },
- found = { },
- foundintrees = { },
- kpsevars = { },
- hashes = { },
- cnffiles = { },
- luafiles = { },
- lists = { },
- remember = true,
- diskcache = true,
- renewcache = false,
- scandisk = true,
- cachepath = nil,
- loaderror = false,
- sortdata = false,
- savelists = true,
- cleanuppaths = true,
- allresults = false,
- pattern = nil, -- lists
- data = { }, -- only for loading
- force_suffixes = true,
- fakepaths = { },
- }
+resolvers.instance = resolvers.instance or nil -- the current one (slow access)
+local instance = resolvers.instance or nil -- the current one (fast access)
- local ne = newinstance.environment
+-- An instance has an environment (coming from the outside, kept raw), variables
+-- (coming from the configuration file), and expansions (variables with nested
+-- variables replaced). One can push something into the outer environment and
+-- its internal copy, but only the later one will be the raw unprefixed variant.
- for k,v in next, os.env do
- ne[k] = resolvers.bare_variable(v)
+function resolvers.setenv(key,value,raw)
+ if instance then
+ -- this one will be consulted first when we stay inside
+ -- the current environment
+ instance.environment[key] = value
+ -- we feed back into the environment, and as this is used
+ -- by other applications (via os.execute) we need to make
+ -- sure that prefixes are resolve
+ ossetenv(key,raw and value or resolvers.resolve(value))
end
+end
- return newinstance
+-- Beware we don't want empty here as this one can be called early on
+-- and therefore we use rawget.
+local function getenv(key)
+ local value = rawget(instance.environment,key)
+ if value and value ~= "" then
+ return value
+ else
+ local e = osgetenv(key)
+ return e ~= nil and e ~= "" and checkedvariable(e) or ""
+ end
end
-function resolvers.setinstance(someinstance)
- instance = someinstance
- resolvers.instance = someinstance
- return someinstance
-end
+resolvers.getenv = getenv
+resolvers.env = getenv
-function resolvers.reset()
- return resolvers.setinstance(resolvers.newinstance())
-end
+-- We are going to use some metatable trickery where we backtrack from
+-- expansion to variable to environment.
-local function reset_hashes()
- instance.lists = { }
- instance.found = { }
+local function resolve(k)
+ return instance.expansions[k]
end
-local function check_configuration() -- not yet ok, no time for debugging now
- if os.env["OSFONTDIR"] then
- -- ok
- elseif os.type == "windows" then
- os.setenv("OSFONTDIR","c:/windows/fonts//")
- elseif os.type == "macosx" then
- os.setenv("OSFONTDIR","$HOME/Library/Fonts//;/Library/Fonts//;/System/Library/Fonts//")
- end
-end
+local dollarstripper = lpeg.stripper("$")
+local inhibitstripper = P("!")^0 * Cs(P(1)^0)
+local backslashswapper = lpeg.replacer("\\","/")
-function resolvers.bare_variable(str) -- assumes str is a string
- return (gsub(str,"\s*([\"\']?)(.+)%1\s*", "%2"))
-end
+local somevariable = P("$") / ""
+local somekey = C(R("az","AZ","09","__","--")^1)
+local somethingelse = P(";") * ((1-S("!{}/\\"))^1 * P(";") / "")
+ + P(";") * (P(";") / "")
+ + P(1)
+local variableexpander = Cs( (somevariable * (somekey/resolve) + somethingelse)^1 )
-function resolvers.settrace(n) -- no longer number but: 'locating' or 'detail'
- if n then
- trackers.disable("resolvers.*")
- trackers.enable("resolvers."..n)
- end
-end
+local cleaner = P("\\") / "/" + P(";") * S("!{}/\\")^0 * P(";")^1 / ";"
+local variablecleaner = Cs((cleaner + P(1))^0)
-resolvers.settrace(os.getenv("MTX_INPUT_TRACE"))
+local somevariable = R("az","AZ","09","__","--")^1 / resolve
+local variable = (P("$")/"") * (somevariable + (P("{")/"") * somevariable * (P("}")/""))
+local variableresolver = Cs((variable + P(1))^0)
-function resolvers.osenv(key)
- local ie = instance.environment
- local value = ie[key]
- if value == nil then
- -- local e = os.getenv(key)
- local e = os.env[key]
- if e == nil then
- -- value = "" -- false
- else
- value = resolvers.bare_variable(e)
- end
- ie[key] = value
- end
- return value or ""
+local function expandedvariable(var)
+ return lpegmatch(variableexpander,var) or var
end
-function resolvers.env(key)
- return instance.environment[key] or resolvers.osenv(key)
-end
+function resolvers.newinstance() -- todo: all vars will become lowercase and alphanum only
---
+ if trace_locating then
+ report_resolving("creating instance")
+ end
-local function expand_vars(lst) -- simple vars
- local variables, env = instance.variables, resolvers.env
- local function resolve(a)
- return variables[a] or env(a)
- end
- for k=1,#lst do
- lst[k] = gsub(lst[k],"%$([%a%d%_%-]+)",resolve)
- end
-end
+ local environment, variables, expansions, order = allocate(), allocate(), allocate(), allocate()
-local function expanded_var(var) -- simple vars
- local function resolve(a)
- return instance.variables[a] or resolvers.env(a)
- end
- return (gsub(var,"%$([%a%d%_%-]+)",resolve))
-end
+ local newinstance = {
+ environment = environment,
+ variables = variables,
+ expansions = expansions,
+ order = order,
+ files = allocate(),
+ setups = allocate(),
+ found = allocate(),
+ foundintrees = allocate(),
+ hashes = allocate(),
+ hashed = allocate(),
+ specification = allocate(),
+ lists = allocate(),
+ data = allocate(), -- only for loading
+ fakepaths = allocate(),
+ remember = true,
+ diskcache = true,
+ renewcache = false,
+ loaderror = false,
+ savelists = true,
+ pattern = nil, -- lists
+ force_suffixes = true,
+ }
-local function entry(entries,name)
- if name and (name ~= "") then
- name = gsub(name,'%$','')
- local result = entries[name..'.'..instance.progname] or entries[name]
- if result then
- return result
- else
- result = resolvers.env(name)
- if result then
- instance.variables[name] = result
- resolvers.expand_variables()
- return instance.expansions[name] or ""
+ setmetatableindex(variables,function(t,k)
+ local v
+ for i=1,#order do
+ v = order[i][k]
+ if v ~= nil then
+ t[k] = v
+ return v
end
end
- end
- return ""
-end
-
-local function is_entry(entries,name)
- if name and name ~= "" then
- name = gsub(name,'%$','')
- return (entries[name..'.'..instance.progname] or entries[name]) ~= nil
- else
- return false
- end
-end
-
--- {a,b,c,d}
--- a,b,c/{p,q,r},d
--- a,b,c/{p,q,r}/d/{x,y,z}//
--- a,b,c/{p,q/{x,y,z},r},d/{p,q,r}
--- a,b,c/{p,q/{x,y,z},r},d/{p,q,r}
--- a{b,c}{d,e}f
--- {a,b,c,d}
--- {a,b,c/{p,q,r},d}
--- {a,b,c/{p,q,r}/d/{x,y,z}//}
--- {a,b,c/{p,q/{x,y,z}},d/{p,q,r}}
--- {a,b,c/{p,q/{x,y,z},w}v,d/{p,q,r}}
--- {$SELFAUTODIR,$SELFAUTOPARENT}{,{/share,}/texmf{-local,.local,}/web2c}
+ if v == nil then
+ v = ""
+ end
+ t[k] = v
+ return v
+ end)
--- 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
+ setmetatableindex(environment, function(t,k)
+ local v = osgetenv(k)
+ if v == nil then
+ v = variables[k]
+ end
+ if v ~= nil then
+ v = checkedvariable(v) or ""
+ end
+ v = resolvers.repath(v) -- for taco who has a : separated osfontdir
+ t[k] = v
+ return v
+ end)
-local function do_first(a,b)
- local t = { }
- for s in gmatch(b,"[^,]+") do t[#t+1] = a .. s end
- return "{" .. concat(t,",") .. "}"
-end
+ setmetatableindex(expansions, function(t,k)
+ local v = environment[k]
+ if type(v) == "string" then
+ v = lpegmatch(variableresolver,v)
+ v = lpegmatch(variablecleaner,v)
+ end
+ t[k] = v
+ return v
+ end)
-local function do_second(a,b)
- local t = { }
- for s in gmatch(a,"[^,]+") do t[#t+1] = s .. b end
- return "{" .. concat(t,",") .. "}"
-end
+ return newinstance
-local function do_both(a,b)
- local t = { }
- for sa in gmatch(a,"[^,]+") do
- for sb in gmatch(b,"[^,]+") do
- t[#t+1] = sa .. sb
- end
- end
- return "{" .. concat(t,",") .. "}"
end
-local function do_three(a,b,c)
- return a .. b.. c
+function resolvers.setinstance(someinstance) -- only one instance is active
+ instance = someinstance
+ resolvers.instance = someinstance
+ return someinstance
end
-local function splitpathexpr(str, t, validate)
- -- no need for further optimization as it is only called a
- -- few times, we can use lpeg for the sub
- if trace_expansions then
- logs.report("fileio","expanding variable '%s'",str)
- end
- t = t or { }
- str = gsub(str,",}",",@}")
- str = gsub(str,"{,","{@,")
- -- str = "@" .. str .. "@"
- local ok, done
- while true do
- done = false
- while true do
- str, ok = gsub(str,"([^{},]+){([^{}]+)}",do_first)
- if ok > 0 then done = true else break end
- end
- while true do
- str, ok = gsub(str,"{([^{}]+)}([^{},]+)",do_second)
- if ok > 0 then done = true else break end
- end
- while true do
- str, ok = gsub(str,"{([^{}]+)}{([^{}]+)}",do_both)
- if ok > 0 then done = true else break end
- end
- str, ok = gsub(str,"({[^{}]*){([^{}]+)}([^{}]*})",do_three)
- if ok > 0 then done = true end
- if not done then break end
- end
- str = gsub(str,"[{}]", "")
- str = gsub(str,"@","")
- if validate then
- for s in gmatch(str,"[^,]+") do
- s = validate(s)
- if s then t[#t+1] = s end
- end
- else
- for s in gmatch(str,"[^,]+") do
- t[#t+1] = s
- end
- end
- if trace_expansions then
- for k=1,#t do
- logs.report("fileio","% 4i: %s",k,t[k])
- end
- end
- return t
+function resolvers.reset()
+ return resolvers.setinstance(resolvers.newinstance())
end
-local function expanded_path_from_list(pathlist) -- maybe not a list, just a path
- -- a previous version fed back into pathlist
- local newlist, ok = { }, false
- for k=1,#pathlist do
- if find(pathlist[k],"[{}]") then
- ok = true
- break
- end
- end
- if ok then
- local function validate(s)
- s = file.collapse_path(s)
- return s ~= "" and not find(s,dummy_path_expr) and s
- end
- for k=1,#pathlist do
- splitpathexpr(pathlist[k],newlist,validate)
- end
- else
- for k=1,#pathlist do
- for p in gmatch(pathlist[k],"([^,]+)") do
- p = file.collapse_path(p)
- if p ~= "" then newlist[#newlist+1] = p end
- end
- end
- end
- return newlist
+local function reset_hashes()
+ instance.lists = { }
+ instance.found = { }
end
--- we follow a rather traditional approach:
---
--- (1) texmf.cnf given in TEXMFCNF
--- (2) texmf.cnf searched in default variable
---
--- also we now follow the stupid route: if not set then just assume *one*
--- cnf file under texmf (i.e. distribution)
+local slash = P("/")
-local args = environment and environment.original_arguments or arg -- this needs a cleanup
+local pathexpressionpattern = Cs (
+ Cc("^") * (
+ Cc("%") * S(".-")
+ + slash^2 * P(-1) / "/.*"
+ + slash^2 / "/.-/"
+ + (1-slash) * P(-1) * Cc("/")
+ + P(1)
+ )^1 * Cc("$") -- yes or no $
+)
-resolvers.ownbin = resolvers.ownbin or args[-2] or arg[-2] or args[-1] or arg[-1] or arg[0] or "luatex"
-resolvers.ownbin = gsub(resolvers.ownbin,"\\","/")
+local cache = { }
-function resolvers.getownpath()
- local ownpath = resolvers.ownpath or os.selfdir
- if not ownpath or ownpath == "" or ownpath == "unset" then
- ownpath = args[-1] or arg[-1]
- ownpath = ownpath and file.dirname(gsub(ownpath,"\\","/"))
- if not ownpath or ownpath == "" then
- ownpath = args[-0] or arg[-0]
- ownpath = ownpath and file.dirname(gsub(ownpath,"\\","/"))
- end
- local binary = resolvers.ownbin
- if not ownpath or ownpath == "" then
- ownpath = ownpath and file.dirname(binary)
- end
- if not ownpath or ownpath == "" then
- if os.binsuffix ~= "" then
- binary = file.replacesuffix(binary,os.binsuffix)
- end
- for p in gmatch(os.getenv("PATH"),"[^"..io.pathseparator.."]+") do
- local b = file.join(p,binary)
- if lfs.isfile(b) then
- -- we assume that after changing to the path the currentdir function
- -- resolves to the real location and use this side effect here; this
- -- trick is needed because on the mac installations use symlinks in the
- -- path instead of real locations
- local olddir = lfs.currentdir()
- if lfs.chdir(p) then
- local pp = lfs.currentdir()
- if trace_locating and p ~= pp then
- logs.report("fileio","following symlink '%s' to '%s'",p,pp)
- end
- ownpath = pp
- lfs.chdir(olddir)
- else
- if trace_locating then
- logs.report("fileio","unable to check path '%s'",p)
- end
- ownpath = p
- end
- break
- end
- end
- end
- if not ownpath or ownpath == "" then
- ownpath = "."
- logs.report("fileio","forcing fallback ownpath .")
- elseif trace_locating then
- logs.report("fileio","using ownpath '%s'",ownpath)
+local function makepathexpression(str)
+ if str == "." then
+ return "^%./$"
+ else
+ local c = cache[str]
+ if not c then
+ c = lpegmatch(pathexpressionpattern,str)
+ cache[str] = c
end
+ return c
end
- resolvers.ownpath = ownpath
- function resolvers.getownpath()
- return resolvers.ownpath
- end
- return ownpath
end
-local own_places = { "SELFAUTOLOC", "SELFAUTODIR", "SELFAUTOPARENT", "TEXMFCNF" }
-
-local function identify_own()
- local ownpath = resolvers.getownpath() or dir.current()
- local ie = instance.environment
- if ownpath then
- if resolvers.env('SELFAUTOLOC') == "" then os.env['SELFAUTOLOC'] = file.collapse_path(ownpath) end
- if resolvers.env('SELFAUTODIR') == "" then os.env['SELFAUTODIR'] = file.collapse_path(ownpath .. "/..") end
- if resolvers.env('SELFAUTOPARENT') == "" then os.env['SELFAUTOPARENT'] = file.collapse_path(ownpath .. "/../..") end
- else
- logs.report("fileio","error: unable to locate ownpath")
- os.exit()
- end
- if resolvers.env('TEXMFCNF') == "" then os.env['TEXMFCNF'] = resolvers.cnfdefault end
- if resolvers.env('TEXOS') == "" then os.env['TEXOS'] = resolvers.env('SELFAUTODIR') end
- if resolvers.env('TEXROOT') == "" then os.env['TEXROOT'] = resolvers.env('SELFAUTOPARENT') end
+local function reportcriticalvariables()
if trace_locating then
- for i=1,#own_places do
- local v = own_places[i]
- logs.report("fileio","variable '%s' set to '%s'",v,resolvers.env(v) or "unknown")
+ for i=1,#resolvers.criticalvars do
+ local k = resolvers.criticalvars[i]
+ local v = resolvers.getenv(k) or "unknown" -- this one will not resolve !
+ report_resolving("variable '%s' set to '%s'",k,v)
end
+ report_resolving()
end
- identify_own = function() end
+ reportcriticalvariables = function() end
end
-function resolvers.identify_cnf()
- if #instance.cnffiles == 0 then
- -- fallback
- identify_own()
- -- the real search
- resolvers.expand_variables()
- local t = resolvers.split_path(resolvers.env('TEXMFCNF'))
- t = expanded_path_from_list(t)
- expand_vars(t) -- redundant
- local function locate(filename,list)
- for i=1,#t do
- local ti = t[i]
- local texmfcnf = file.collapse_path(file.join(ti,filename))
- if lfs.isfile(texmfcnf) then
- list[#list+1] = texmfcnf
+local function identify_configuration_files()
+ local specification = instance.specification
+ if #specification == 0 then
+ local cnfspec = getenv('TEXMFCNF')
+ if cnfspec == "" then
+ cnfspec = resolvers.luacnfspec
+ resolvers.luacnfstate = "default"
+ else
+ resolvers.luacnfstate = "environment"
+ end
+ reportcriticalvariables()
+ local cnfpaths = expandedpathfromlist(resolvers.splitpath(cnfspec))
+ local luacnfname = resolvers.luacnfname
+ for i=1,#cnfpaths do
+ local filename = collapsepath(filejoin(cnfpaths[i],luacnfname))
+ local realname = resolvers.resolve(filename)
+ if lfs.isfile(realname) then
+ specification[#specification+1] = filename
+ if trace_locating then
+ report_resolving("found configuration file '%s'",realname)
end
+ elseif trace_locating then
+ report_resolving("unknown configuration file '%s'",realname)
end
end
- locate(resolvers.luaname,instance.luafiles)
- locate(resolvers.cnfname,instance.cnffiles)
- end
-end
-
-local function load_cnf_file(fname)
- fname = resolvers.clean_path(fname)
- local lname = file.replacesuffix(fname,'lua')
- if lfs.isfile(lname) then
- local dname = file.dirname(fname) -- fname ?
- if not instance.configuration[dname] then
- resolvers.load_data(dname,'configuration',lname and file.basename(lname))
- instance.order[#instance.order+1] = instance.configuration[dname]
+ if trace_locating then
+ report_resolving()
end
- else
- f = io.open(fname)
- if f then
- if trace_locating then
- logs.report("fileio","loading configuration file %s", fname)
- end
- local line, data, n, k, v
- local dname = file.dirname(fname)
- if not instance.configuration[dname] then
- instance.configuration[dname] = { }
- instance.order[#instance.order+1] = instance.configuration[dname]
- end
- local data = instance.configuration[dname]
- while true do
- local line, n = f:read(), 0
- if line then
- while true do -- join lines
- line, n = gsub(line,"\\%s*$", "")
- if n > 0 then
- line = line .. f:read()
- else
- break
+ elseif trace_locating then
+ report_resolving("configuration files already identified")
+ end
+end
+
+local function load_configuration_files()
+ local specification = instance.specification
+ if #specification > 0 then
+ local luacnfname = resolvers.luacnfname
+ for i=1,#specification do
+ local filename = specification[i]
+ local pathname = filedirname(filename)
+ local filename = filejoin(pathname,luacnfname)
+ local realname = resolvers.resolve(filename) -- no shortcut
+ local blob = loadfile(realname)
+ if blob then
+ local setups = instance.setups
+ local data = blob()
+ data = data and data.content
+ if data then
+ if trace_locating then
+ report_resolving("loading configuration file '%s'",filename)
+ report_resolving()
+ end
+ local variables = data.variables or { }
+ local warning = false
+ for k, v in next, data do
+ local variant = type(v)
+ if variant == "table" then
+ initializesetter(filename,k,v)
+ elseif variables[k] == nil then
+ if trace_locating and not warning then
+ report_resolving("variables like '%s' in configuration file '%s' should move to the 'variables' subtable",
+ k,resolvers.resolve(filename))
+ warning = true
+ end
+ variables[k] = v
end
end
- if not find(line,"^[%%#]") then
- local l = gsub(line,"%s*%%.*$","")
- local k, v = match(l,"%s*(.-)%s*=%s*(.-)%s*$")
- if k and v and not data[k] then
- v = gsub(v,"[%%#].*",'')
- data[k] = gsub(v,"~","$HOME")
- instance.kpsevars[k] = true
+ setups[pathname] = variables
+ if resolvers.luacnfstate == "default" then
+ -- the following code is not tested
+ local cnfspec = variables["TEXMFCNF"]
+ if cnfspec then
+ if trace_locating then
+ report_resolving("reloading configuration due to TEXMF redefinition")
+ end
+ -- we push the value into the main environment (osenv) so
+ -- that it takes precedence over the default one and therefore
+ -- also over following definitions
+ resolvers.setenv('TEXMFCNF',cnfspec) -- resolves prefixes
+ -- we now identify and load the specified configuration files
+ instance.specification = { }
+ identify_configuration_files()
+ load_configuration_files()
+ -- we prevent further overload of the configuration variable
+ resolvers.luacnfstate = "configuration"
+ -- we quit the outer loop
+ break
end
end
- else
- break
- end
- end
- f:close()
- elseif trace_locating then
- logs.report("fileio","skipping configuration file '%s'", fname)
- end
- end
-end
-local function collapse_cnf_data() -- potential optimization: pass start index (setup and configuration are shared)
- local order = instance.order
- for i=1,#order do
- local c = order[i]
- for k,v in next, c do
- if not instance.variables[k] then
- if instance.environment[k] then
- instance.variables[k] = instance.environment[k]
else
- instance.kpsevars[k] = true
- instance.variables[k] = resolvers.bare_variable(v)
+ if trace_locating then
+ report_resolving("skipping configuration file '%s' (no content)",filename)
+ end
+ setups[pathname] = { }
+ instance.loaderror = true
end
+ elseif trace_locating then
+ report_resolving("skipping configuration file '%s' (no file)",filename)
end
- end
- end
-end
-
-function resolvers.load_cnf()
- local function loadoldconfigdata()
- local cnffiles = instance.cnffiles
- for i=1,#cnffiles do
- load_cnf_file(cnffiles[i])
- end
- end
- -- instance.cnffiles contain complete names now !
- -- we still use a funny mix of cnf and new but soon
- -- we will switch to lua exclusively as we only use
- -- the file to collect the tree roots
- if #instance.cnffiles == 0 then
- if trace_locating then
- logs.report("fileio","no cnf files found (TEXMFCNF may not be set/known)")
- end
- else
- local cnffiles = instance.cnffiles
- instance.rootpath = cnffiles[1]
- for k=1,#cnffiles do
- instance.cnffiles[k] = file.collapse_path(cnffiles[k])
- end
- for i=1,3 do
- instance.rootpath = file.dirname(instance.rootpath)
- end
- instance.rootpath = file.collapse_path(instance.rootpath)
- if instance.diskcache and not instance.renewcache then
- resolvers.loadoldconfig(instance.cnffiles)
+ instance.order[#instance.order+1] = instance.setups[pathname]
if instance.loaderror then
- loadoldconfigdata()
- resolvers.saveoldconfig()
- end
- else
- loadoldconfigdata()
- if instance.renewcache then
- resolvers.saveoldconfig()
+ break
end
end
- collapse_cnf_data()
- end
- check_configuration()
-end
-
-function resolvers.load_lua()
- if #instance.luafiles == 0 then
- -- yet harmless
- else
- instance.rootpath = instance.luafiles[1]
- local luafiles = instance.luafiles
- for k=1,#luafiles do
- instance.luafiles[k] = file.collapse_path(luafiles[k])
- end
- for i=1,3 do
- instance.rootpath = file.dirname(instance.rootpath)
- end
- instance.rootpath = file.collapse_path(instance.rootpath)
- resolvers.loadnewconfig()
- collapse_cnf_data()
- end
- check_configuration()
-end
-
--- database loading
-
-function resolvers.load_hash()
- resolvers.locatelists()
- if instance.diskcache and not instance.renewcache then
- resolvers.loadfiles()
- if instance.loaderror then
- resolvers.loadlists()
- resolvers.savefiles()
- end
- else
- resolvers.loadlists()
- if instance.renewcache then
- resolvers.savefiles()
- end
- end
-end
-
-function resolvers.append_hash(type,tag,name)
- if trace_locating then
- logs.report("fileio","hash '%s' appended",tag)
- end
- insert(instance.hashes, { ['type']=type, ['tag']=tag, ['name']=name } )
-end
-
-function resolvers.prepend_hash(type,tag,name)
- if trace_locating then
- logs.report("fileio","hash '%s' prepended",tag)
- end
- insert(instance.hashes, 1, { ['type']=type, ['tag']=tag, ['name']=name } )
-end
-
-function resolvers.extend_texmf_var(specification) -- crap, we could better prepend the hash
--- local t = resolvers.expanded_path_list('TEXMF') -- full expansion
- local t = resolvers.split_path(resolvers.env('TEXMF'))
- insert(t,1,specification)
- local newspec = concat(t,";")
- if instance.environment["TEXMF"] then
- instance.environment["TEXMF"] = newspec
- elseif instance.variables["TEXMF"] then
- instance.variables["TEXMF"] = newspec
- else
- -- weird
- end
- resolvers.expand_variables()
- reset_hashes()
-end
-
--- locators
-
-function resolvers.locatelists()
- local texmfpaths = resolvers.clean_path_list('TEXMF')
- for i=1,#texmfpaths do
- local path = texmfpaths[i]
- if trace_locating then
- logs.report("fileio","locating list of '%s'",path)
- end
- resolvers.locatedatabase(file.collapse_path(path))
- end
-end
-
-function resolvers.locatedatabase(specification)
- return resolvers.methodhandler('locators', specification)
-end
-
-function resolvers.locators.tex(specification)
- if specification and specification ~= '' and lfs.isdir(specification) then
- if trace_locating then
- logs.report("fileio","tex locator '%s' found",specification)
- end
- resolvers.append_hash('file',specification,filename)
elseif trace_locating then
- logs.report("fileio","tex locator '%s' not found",specification)
+ report_resolving("warning: no lua configuration files found")
end
end
--- hashers
-
-function resolvers.hashdatabase(tag,name)
- return resolvers.methodhandler('hashers',tag,name)
-end
+-- scheme magic ... database loading
-function resolvers.loadfiles()
- instance.loaderror = false
- instance.files = { }
+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)
- resolvers.load_data(tag,'files')
-end
-
--- generators:
-
-function resolvers.loadlists()
- local hashes = instance.hashes
- for i=1,#hashes do
- resolvers.generatedatabase(hashes[i].tag)
- end
-end
-
-function resolvers.generatedatabase(specification)
- return resolvers.methodhandler('generators', specification)
-end
-
--- starting with . or .. etc or funny char
-
-local weird = lpeg.P(".")^1 + lpeg.anywhere(lpeg.S("~`!#$%^&*()={}[]:;\"\'||<>,?\n\r\t"))
-
---~ local l_forbidden = lpeg.S("~`!#$%^&*()={}[]:;\"\'||\\/<>,?\n\r\t")
---~ local l_confusing = lpeg.P(" ")
---~ local l_character = lpeg.patterns.utf8
---~ local l_dangerous = lpeg.P(".")
-
---~ local l_normal = (l_character - l_forbidden - l_confusing - l_dangerous) * (l_character - l_forbidden - l_confusing^2)^0 * lpeg.P(-1)
---~ ----- l_normal = l_normal * lpeg.Cc(true) + lpeg.Cc(false)
-
---~ local function test(str)
---~ print(str,lpeg.match(l_normal,str))
---~ end
---~ test("ヒラギノ明朝 Pro W3")
---~ test("..ヒラギノ明朝 Pro W3")
---~ test(":ヒラギノ明朝 Pro W3;")
---~ test("ヒラギノ明朝 /Pro W3;")
---~ test("ヒラギノ明朝 Pro W3")
-
-function resolvers.generators.tex(specification)
- local tag = specification
- if trace_locating then
- logs.report("fileio","scanning path '%s'",specification)
- end
- instance.files[tag] = { }
- local files = instance.files[tag]
- local n, m, r = 0, 0, 0
- local spec = specification .. '/'
- local attributes = lfs.attributes
- local directory = lfs.dir
- local function action(path)
- local full
- if path then
- full = spec .. path .. '/'
- else
- full = spec
- end
- for name in directory(full) do
- if not lpegmatch(weird,name) then
- -- if lpegmatch(l_normal,name) then
- local mode = attributes(full..name,'mode')
- if mode == 'file' then
- if path then
- n = n + 1
- local f = files[name]
- if f then
- if type(f) == 'string' then
- files[name] = { f, path }
- else
- f[#f+1] = path
- end
- else -- probably unique anyway
- files[name] = path
- local lower = lower(name)
- if name ~= lower then
- files["remap:"..lower] = name
- r = r + 1
- end
- end
- end
- elseif mode == 'directory' then
- m = m + 1
- if path then
- action(path..'/'..name)
+local function locate_file_databases()
+ -- todo: cache:// and tree:// (runtime)
+ local texmfpaths = resolvers.expandedpathlist('TEXMF')
+ if #texmfpaths > 0 then
+ for i=1,#texmfpaths do
+ local path = collapsepath(texmfpaths[i])
+ local stripped = lpegmatch(inhibitstripper,path) -- the !! thing
+ if stripped ~= "" then
+ local runtime = stripped == path
+ path = resolvers.cleanpath(path)
+ local spec = resolvers.splitmethod(stripped)
+ 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
+ end
+ if trace_locating then
+ if runtime then
+ report_resolving("locating list of '%s' (runtime)",path)
else
- action(name)
+ report_resolving("locating list of '%s' (cached)",path)
end
end
+ methodhandler('locators',stripped)
end
end
- end
- action()
- if trace_locating then
- logs.report("fileio","%s files found on %s directories with %s uppercase remappings",n,m,r)
+ if trace_locating then
+ report_resolving()
+ end
+ elseif trace_locating then
+ report_resolving("no texmf paths are defined (using TEXMF)")
end
end
--- savers, todo
-
-function resolvers.savefiles()
- resolvers.save_data('files')
+local function generate_file_databases()
+ local hashes = instance.hashes
+ for k=1,#hashes do
+ local hash = hashes[k]
+ methodhandler('generators',hash.name)
+ end
+ if trace_locating then
+ report_resolving()
+ end
end
--- A config (optionally) has the paths split in tables. Internally
--- we join them and split them after the expansion has taken place. This
--- is more convenient.
-
---~ local checkedsplit = string.checkedsplit
-
-local cache = { }
-
-local splitter = lpeg.Ct(lpeg.splitat(lpeg.S(os.type == "windows" and ";" or ":;")))
-
-local function split_kpse_path(str) -- beware, this can be either a path or a {specification}
- local found = cache[str]
- if not found then
- if str == "" then
- found = { }
- else
- str = gsub(str,"\\","/")
---~ local split = (find(str,";") and checkedsplit(str,";")) or checkedsplit(str,io.pathseparator)
-local split = lpegmatch(splitter,str)
- found = { }
- for i=1,#split do
- local s = split[i]
- if not find(s,"^{*unset}*") then
- found[#found+1] = s
- end
- end
- if trace_expansions then
- logs.report("fileio","splitting path specification '%s'",str)
- for k=1,#found do
- logs.report("fileio","% 4i: %s",k,found[k])
- end
+local function save_file_databases() -- will become cachers
+ for i=1,#instance.hashes do
+ local hash = instance.hashes[i]
+ local cachename = hash.name
+ if hash.cache then
+ local content = instance.files[cachename]
+ caches.collapsecontent(content)
+ if trace_locating then
+ report_resolving("saving tree '%s'",cachename)
end
- cache[str] = found
+ caches.savecontent(cachename,"files",content)
+ elseif trace_locating then
+ report_resolving("not saving runtime tree '%s'",cachename)
end
end
- return found
end
-resolvers.split_kpse_path = split_kpse_path
-
-function resolvers.splitconfig()
- for i=1,#instance do
- local c = instance[i]
- for k,v in next, c do
- if type(v) == 'string' then
- local t = split_kpse_path(v)
- if #t > 1 then
- c[k] = t
- end
- end
+local function load_databases()
+ locate_file_databases()
+ if instance.diskcache and not instance.renewcache then
+ load_file_databases()
+ if instance.loaderror then
+ generate_file_databases()
+ save_file_databases()
+ end
+ else
+ generate_file_databases()
+ if instance.renewcache then
+ save_file_databases()
end
end
end
-function resolvers.joinconfig()
- local order = instance.order
- for i=1,#order do
- local c = order[i]
- for k,v in next, c do -- indexed?
- if type(v) == 'table' then
- c[k] = file.join_path(v)
- end
+function resolvers.appendhash(type,name,cache)
+ -- safeguard ... tricky as it's actually a bug when seen twice
+ if not instance.hashed[name] then
+ if trace_locating then
+ report_resolving("hash '%s' appended",name)
end
+ insert(instance.hashes, { type = type, name = name, cache = cache } )
+ instance.hashed[name] = cache
end
end
-function resolvers.split_path(str)
- if type(str) == 'table' then
- return str
- else
- return split_kpse_path(str)
+function resolvers.prependhash(type,name,cache)
+ -- safeguard ... tricky as it's actually a bug when seen twice
+ if not instance.hashed[name] then
+ if trace_locating then
+ report_resolving("hash '%s' prepended",name)
+ end
+ insert(instance.hashes, 1, { type = type, name = name, cache = cache } )
+ instance.hashed[name] = cache
end
end
-function resolvers.join_path(str)
- if type(str) == 'table' then
- return file.join_path(str)
+function resolvers.extendtexmfvariable(specification) -- crap, we could better prepend the hash
+ local t = resolvers.splitpath(getenv('TEXMF'))
+ insert(t,1,specification)
+ local newspec = concat(t,";")
+ if instance.environment["TEXMF"] then
+ instance.environment["TEXMF"] = newspec
+ elseif instance.variables["TEXMF"] then
+ instance.variables["TEXMF"] = newspec
else
- return str
+ -- weird
end
+ reset_hashes()
end
function resolvers.splitexpansions()
local ie = instance.expansions
for k,v in next, ie do
- local t, h, p = { }, { }, split_kpse_path(v)
+ local t, tn, h, p = { }, 0, { }, splitconfigurationpath(v)
for kk=1,#p do
local vv = p[kk]
if vv ~= "" and not h[vv] then
- t[#t+1] = vv
+ tn = tn + 1
+ t[tn] = vv
h[vv] = true
end
end
@@ -9184,343 +11600,92 @@ end
-- end of split/join code
-function resolvers.saveoldconfig()
- resolvers.splitconfig()
- resolvers.save_data('configuration')
- resolvers.joinconfig()
-end
-
-resolvers.configbanner = [[
--- This is a Luatex configuration file created by 'luatools.lua' or
--- 'luatex.exe' directly. For comment, suggestions and questions you can
--- contact the ConTeXt Development Team. This configuration file is
--- not copyrighted. [HH & TH]
-]]
-
-function resolvers.serialize(files)
- -- This version is somewhat optimized for the kind of
- -- tables that we deal with, so it's much faster than
- -- the generic serializer. This makes sense because
- -- luatools and mtxtools are called frequently. Okay,
- -- we pay a small price for properly tabbed tables.
- local t = { }
- local function dump(k,v,m) -- could be moved inline
- if type(v) == 'string' then
- return m .. "['" .. k .. "']='" .. v .. "',"
- elseif #v == 1 then
- return m .. "['" .. k .. "']='" .. v[1] .. "',"
- else
- return m .. "['" .. k .. "']={'" .. concat(v,"','").. "'},"
- end
- end
- t[#t+1] = "return {"
- if instance.sortdata then
- local sortedfiles = sortedkeys(files)
- for i=1,#sortedfiles do
- local k = sortedfiles[i]
- local fk = files[k]
- if type(fk) == 'table' then
- t[#t+1] = "\t['" .. k .. "']={"
- local sortedfk = sortedkeys(fk)
- for j=1,#sortedfk do
- local kk = sortedfk[j]
- t[#t+1] = dump(kk,fk[kk],"\t\t")
- end
- t[#t+1] = "\t},"
- else
- t[#t+1] = dump(k,fk,"\t")
- end
- end
- else
- for k, v in next, files do
- if type(v) == 'table' then
- t[#t+1] = "\t['" .. k .. "']={"
- for kk,vv in next, v do
- t[#t+1] = dump(kk,vv,"\t\t")
- end
- t[#t+1] = "\t},"
- else
- t[#t+1] = dump(k,v,"\t")
- end
- end
- end
- t[#t+1] = "}"
- return concat(t,"\n")
-end
-
-local data_state = { }
+-- we used to have 'files' and 'configurations' so therefore the following
+-- shared function
-function resolvers.data_state()
- return data_state or { }
-end
-
-function resolvers.save_data(dataname, makename) -- untested without cache overload
- for cachename, files in next, instance[dataname] do
- local name = (makename or file.join)(cachename,dataname)
- local luaname, lucname = name .. ".lua", name .. ".luc"
- if trace_locating then
- logs.report("fileio","preparing '%s' for '%s'",dataname,cachename)
- end
- for k, v in next, files do
- if type(v) == "table" and #v == 1 then
- files[k] = v[1]
- end
- end
- local data = {
- type = dataname,
- root = cachename,
- version = resolvers.cacheversion,
- date = os.date("%Y-%m-%d"),
- time = os.date("%H:%M:%S"),
- content = files,
- uuid = os.uuid(),
- }
- local ok = io.savedata(luaname,resolvers.serialize(data))
- if ok then
- if trace_locating then
- logs.report("fileio","'%s' saved in '%s'",dataname,luaname)
- end
- if utils.lua.compile(luaname,lucname,false,true) then -- no cleanup but strip
- if trace_locating then
- logs.report("fileio","'%s' compiled to '%s'",dataname,lucname)
- end
- else
- if trace_locating then
- logs.report("fileio","compiling failed for '%s', deleting file '%s'",dataname,lucname)
- end
- os.remove(lucname)
- end
- elseif trace_locating then
- logs.report("fileio","unable to save '%s' in '%s' (access error)",dataname,luaname)
- end
- end
-end
-
-function resolvers.load_data(pathname,dataname,filename,makename) -- untested without cache overload
- filename = ((not filename or (filename == "")) and dataname) or filename
- filename = (makename and makename(dataname,filename)) or file.join(pathname,filename)
- local blob = loadfile(filename .. ".luc") or loadfile(filename .. ".lua")
- if blob then
- local data = blob()
- if data and data.content and data.type == dataname and data.version == resolvers.cacheversion then
- data_state[#data_state+1] = data.uuid
- if trace_locating then
- logs.report("fileio","loading '%s' for '%s' from '%s'",dataname,pathname,filename)
- end
- instance[dataname][pathname] = data.content
- else
- if trace_locating then
- logs.report("fileio","skipping '%s' for '%s' from '%s'",dataname,pathname,filename)
- end
- instance[dataname][pathname] = { }
- instance.loaderror = true
- end
- elseif trace_locating then
- logs.report("fileio","skipping '%s' for '%s' from '%s'",dataname,pathname,filename)
- end
-end
-
--- some day i'll use the nested approach, but not yet (actually we even drop
--- engine/progname support since we have only luatex now)
---
--- first texmfcnf.lua files are located, next the cached texmf.cnf files
---
--- return {
--- TEXMFBOGUS = 'effe checken of dit werkt',
--- }
-
-function resolvers.resetconfig()
- identify_own()
- instance.configuration, instance.setup, instance.order, instance.loaderror = { }, { }, { }, false
-end
-
-function resolvers.loadnewconfig()
- local luafiles = instance.luafiles
- for i=1,#luafiles do
- local cnf = luafiles[i]
- local pathname = file.dirname(cnf)
- local filename = file.join(pathname,resolvers.luaname)
- local blob = loadfile(filename)
- if blob then
- local data = blob()
- if data then
- if trace_locating then
- logs.report("fileio","loading configuration file '%s'",filename)
- end
- if true then
- -- flatten to variable.progname
- local t = { }
- for k, v in next, data do -- v = progname
- if type(v) == "string" then
- t[k] = v
- else
- for kk, vv in next, v do -- vv = variable
- if type(vv) == "string" then
- t[vv.."."..v] = kk
- end
- end
- end
- end
- instance['setup'][pathname] = t
- else
- instance['setup'][pathname] = data
- end
- else
- if trace_locating then
- logs.report("fileio","skipping configuration file '%s'",filename)
- end
- instance['setup'][pathname] = { }
- instance.loaderror = true
- end
- elseif trace_locating then
- logs.report("fileio","skipping configuration file '%s'",filename)
- end
- instance.order[#instance.order+1] = instance.setup[pathname]
- if instance.loaderror then break end
- end
-end
-
-function resolvers.loadoldconfig()
- if not instance.renewcache then
- local cnffiles = instance.cnffiles
- for i=1,#cnffiles do
- local cnf = cnffiles[i]
- local dname = file.dirname(cnf)
- resolvers.load_data(dname,'configuration')
- instance.order[#instance.order+1] = instance.configuration[dname]
- if instance.loaderror then break end
- end
- end
- resolvers.joinconfig()
-end
-
-function resolvers.expand_variables()
- local expansions, environment, variables = { }, instance.environment, instance.variables
- local env = resolvers.env
- instance.expansions = expansions
- local engine, progname = instance.engine, instance.progname
- if type(engine) ~= "string" then instance.engine, engine = "", "" end
- if type(progname) ~= "string" then instance.progname, progname = "", "" end
- 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*$")
- if a and b then
- expansions[a..'.'..b] = v
- else
- expansions[k] = v
- end
- end
- for k,v in next, environment do -- move environment to expansions
- if not expansions[k] then expansions[k] = v end
- end
- 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 env(a)
- end
- while true do
- 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
- expansions[k]= s
- end
- end
- if not busy then break end
- end
- for k,v in next, expansions do
- expansions[k] = gsub(v,"\\", '/')
- end
+function resolvers.datastate()
+ return caches.contentstate()
end
function resolvers.variable(name)
- return entry(instance.variables,name)
+ local name = name and lpegmatch(dollarstripper,name)
+ local result = name and instance.variables[name]
+ return result ~= nil and result or ""
end
function resolvers.expansion(name)
- return entry(instance.expansions,name)
-end
-
-function resolvers.is_variable(name)
- return is_entry(instance.variables,name)
-end
-
-function resolvers.is_expansion(name)
- return is_entry(instance.expansions,name)
+ local name = name and lpegmatch(dollarstripper,name)
+ local result = name and instance.expansions[name]
+ return result ~= nil and result or ""
end
-function resolvers.unexpanded_path_list(str)
+function resolvers.unexpandedpathlist(str)
local pth = resolvers.variable(str)
- local lst = resolvers.split_path(pth)
- return expanded_path_from_list(lst)
+ local lst = resolvers.splitpath(pth)
+ return expandedpathfromlist(lst)
end
-function resolvers.unexpanded_path(str)
- return file.join_path(resolvers.unexpanded_path_list(str))
+function resolvers.unexpandedpath(str)
+ return joinpath(resolvers.unexpandedpathlist(str))
end
-do -- no longer needed
-
- local done = { }
+local done = { }
- function resolvers.reset_extra_path()
- local ep = instance.extra_paths
- if not ep then
- ep, done = { }, { }
- instance.extra_paths = ep
- elseif #ep > 0 then
- instance.lists, done = { }, { }
- end
+function resolvers.resetextrapath()
+ local ep = instance.extra_paths
+ if not ep then
+ ep, done = { }, { }
+ instance.extra_paths = ep
+ elseif #ep > 0 then
+ instance.lists, done = { }, { }
end
+end
- function resolvers.register_extra_path(paths,subpaths)
- local ep = instance.extra_paths or { }
- local n = #ep
- if paths and paths ~= "" then
- if subpaths and subpaths ~= "" then
- for p in gmatch(paths,"[^,]+") do
- -- we gmatch each step again, not that fast, but used seldom
- for s in gmatch(subpaths,"[^,]+") do
- local ps = p .. "/" .. s
- if not done[ps] then
- ep[#ep+1] = resolvers.clean_path(ps)
- done[ps] = true
- end
- end
- end
- else
- for p in gmatch(paths,"[^,]+") do
- if not done[p] then
- ep[#ep+1] = resolvers.clean_path(p)
- done[p] = true
- end
- end
- end
- elseif subpaths and subpaths ~= "" then
- for i=1,n do
+function resolvers.registerextrapath(paths,subpaths)
+ local ep = instance.extra_paths or { }
+ local oldn = #ep
+ local newn = oldn
+ if paths and paths ~= "" then
+ if subpaths and subpaths ~= "" then
+ for p in gmatch(paths,"[^,]+") do
-- we gmatch each step again, not that fast, but used seldom
for s in gmatch(subpaths,"[^,]+") do
- local ps = ep[i] .. "/" .. s
+ local ps = p .. "/" .. s
if not done[ps] then
- ep[#ep+1] = resolvers.clean_path(ps)
+ newn = newn + 1
+ ep[newn] = resolvers.cleanpath(ps)
done[ps] = true
end
end
end
+ else
+ for p in gmatch(paths,"[^,]+") do
+ if not done[p] then
+ newn = newn + 1
+ ep[newn] = resolvers.cleanpath(p)
+ done[p] = true
+ end
+ end
end
- if #ep > 0 then
- instance.extra_paths = ep -- register paths
- end
- if #ep > n then
- instance.lists = { } -- erase the cache
+ elseif subpaths and subpaths ~= "" then
+ for i=1,oldn do
+ -- we gmatch each step again, not that fast, but used seldom
+ for s in gmatch(subpaths,"[^,]+") do
+ local ps = ep[i] .. "/" .. s
+ if not done[ps] then
+ newn = newn + 1
+ ep[newn] = resolvers.cleanpath(ps)
+ done[ps] = true
+ end
+ end
end
end
-
+ if newn > 0 then
+ instance.extra_paths = ep -- register paths
+ end
+ if newn > oldn then
+ instance.lists = { } -- erase the cache
+ end
end
local function made_list(instance,list)
@@ -9528,14 +11693,15 @@ local function made_list(instance,list)
if not ep or #ep == 0 then
return list
else
- local done, new = { }, { }
+ local done, new, newn = { }, { }, 0
-- honour . .. ../.. but only when at the start
for k=1,#list do
local v = list[k]
if not done[v] then
if find(v,"^[%.%/]$") then
done[v] = true
- new[#new+1] = v
+ newn = newn + 1
+ new[newn] = v
else
break
end
@@ -9546,7 +11712,8 @@ local function made_list(instance,list)
local v = ep[k]
if not done[v] then
done[v] = true
- new[#new+1] = v
+ newn = newn + 1
+ new[newn] = v
end
end
-- next the formal paths
@@ -9554,118 +11721,95 @@ local function made_list(instance,list)
local v = list[k]
if not done[v] then
done[v] = true
- new[#new+1] = v
+ newn = newn + 1
+ new[newn] = v
end
end
return new
end
end
-function resolvers.clean_path_list(str)
- local t = resolvers.expanded_path_list(str)
+function resolvers.cleanpathlist(str)
+ local t = resolvers.expandedpathlist(str)
if t then
for i=1,#t do
- t[i] = file.collapse_path(resolvers.clean_path(t[i]))
+ t[i] = collapsepath(resolvers.cleanpath(t[i]))
end
end
return t
end
-function resolvers.expand_path(str)
- return file.join_path(resolvers.expanded_path_list(str))
+function resolvers.expandpath(str)
+ return joinpath(resolvers.expandedpathlist(str))
end
-function resolvers.expanded_path_list(str)
+function resolvers.expandedpathlist(str)
if not str then
- return ep or { } -- ep ?
+ return { }
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.split_path(resolvers.expansion(str)))
- instance.lists[str] = expanded_path_from_list(lst)
+ local lst = made_list(instance,resolvers.splitpath(resolvers.expansion(str)))
+ instance.lists[str] = expandedpathfromlist(lst)
end
return instance.lists[str]
else
- local lst = resolvers.split_path(resolvers.expansion(str))
- return made_list(instance,expanded_path_from_list(lst))
- end
-end
-
-function resolvers.expanded_path_list_from_var(str) -- brrr
- local tmp = resolvers.var_of_format_or_suffix(gsub(str,"%$",""))
- if tmp ~= "" then
- return resolvers.expanded_path_list(tmp)
- else
- return resolvers.expanded_path_list(str)
+ local lst = resolvers.splitpath(resolvers.expansion(str))
+ return made_list(instance,expandedpathfromlist(lst))
end
end
-function resolvers.expand_path_from_var(str)
- return file.join_path(resolvers.expanded_path_list_from_var(str))
+function resolvers.expandedpathlistfromvariable(str) -- brrr
+ str = lpegmatch(dollarstripper,str)
+ local tmp = resolvers.variableofformatorsuffix(str)
+ return resolvers.expandedpathlist(tmp ~= "" and tmp or str)
end
-function resolvers.format_of_var(str)
- return formats[str] or formats[alternatives[str]] or ''
-end
-function resolvers.format_of_suffix(str)
- return suffixmap[file.extname(str)] or 'tex'
+function resolvers.expandpathfromvariable(str)
+ return joinpath(resolvers.expandedpathlistfromvariable(str))
end
-function resolvers.variable_of_format(str)
- return formats[str] or formats[alternatives[str]] or ''
+function resolvers.expandbraces(str) -- output variable and brace expansion of STRING
+ local ori = resolvers.variable(str)
+ local pth = expandedpathfromlist(resolvers.splitpath(ori))
+ return joinpath(pth)
end
-function resolvers.var_of_format_or_suffix(str)
- local v = formats[str]
- if v then
- return v
- end
- v = formats[alternatives[str]]
- if v then
- return v
- end
- v = suffixmap[file.extname(str)]
- if v then
- return formats[isf]
+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
- return ''
-end
-
-function resolvers.expand_braces(str) -- output variable and brace expansion of STRING
- local ori = resolvers.variable(str)
- local pth = expanded_path_from_list(resolvers.split_path(ori))
- return file.join_path(pth)
end
-resolvers.isreadable = { }
-
-function resolvers.isreadable.file(name)
- local readable = lfs.isfile(name) -- brrr
+local function isreadable(name)
+ local readable = lfs.isfile(name) -- not file.is_readable(name) asit can be a dir
if trace_detail then
if readable then
- logs.report("fileio","file '%s' is readable",name)
+ report_resolving("file '%s' is readable",name)
else
- logs.report("fileio","file '%s' is not readable", name)
+ report_resolving("file '%s' is not readable", name)
end
end
return readable
end
-resolvers.isreadable.tex = resolvers.isreadable.file
-
-- name
-- name/name
local function collect_files(names)
- local filelist = { }
+ local filelist, noffiles = { }, 0
for k=1,#names do
local fname = names[k]
if trace_detail then
- logs.report("fileio","checking name '%s'",fname)
+ report_resolving("checking name '%s'",fname)
end
- local bname = file.basename(fname)
- local dname = file.dirname(fname)
+ local bname = filebasename(fname)
+ local dname = filedirname(fname)
if dname == "" or find(dname,"^%.") then
dname = false
else
@@ -9674,11 +11818,11 @@ 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
- logs.report("fileio","deep checking '%s' (%s)",blobpath,bname)
+ report_resolving("deep checking '%s' (%s)",blobpath,bname)
end
local blobfile = files[bname]
if not blobfile then
@@ -9690,62 +11834,51 @@ local function collect_files(names)
end
end
if blobfile then
+ local blobroot = files.__path__ or blobpath
if type(blobfile) == 'string' then
if not dname or find(blobfile,dname) then
- filelist[#filelist+1] = {
- hash.type,
- file.join(blobpath,blobfile,bname), -- search
- resolvers.concatinators[hash.type](blobpath,blobfile,bname) -- result
- }
+ local variant = hash.type
+ -- local search = filejoin(blobpath,blobfile,bname)
+ local search = filejoin(blobroot,blobfile,bname)
+ local result = methodhandler('concatinators',hash.type,blobroot,blobfile,bname)
+ if trace_detail then
+ report_resolving("match: variant '%s', search '%s', result '%s'",variant,search,result)
+ end
+ noffiles = noffiles + 1
+ filelist[noffiles] = { variant, search, result }
end
else
for kk=1,#blobfile do
local vv = blobfile[kk]
if not dname or find(vv,dname) then
- filelist[#filelist+1] = {
- hash.type,
- file.join(blobpath,vv,bname), -- search
- resolvers.concatinators[hash.type](blobpath,vv,bname) -- result
- }
+ local variant = hash.type
+ -- local search = filejoin(blobpath,vv,bname)
+ local search = filejoin(blobroot,vv,bname)
+ local result = methodhandler('concatinators',hash.type,blobroot,vv,bname)
+ if trace_detail then
+ report_resolving("match: variant '%s', search '%s', result '%s'",variant,search,result)
+ end
+ noffiles = noffiles + 1
+ filelist[noffiles] = { variant, search, result }
end
end
end
end
elseif trace_locating then
- logs.report("fileio","no match in '%s' (%s)",blobpath,bname)
+ report_resolving("no match in '%s' (%s)",blobpath,bname)
end
end
end
- if #filelist > 0 then
- return filelist
- else
- return nil
- end
+ return noffiles > 0 and filelist or nil
end
-function resolvers.suffix_of_format(str)
- if suffixes[str] then
- return suffixes[str][1]
- else
- return ""
- end
-end
-
-function resolvers.suffixes_of_format(str)
- if suffixes[str] then
- return suffixes[str]
- else
- return {}
- end
-end
-
-function resolvers.register_in_trees(name)
+function resolvers.registerintrees(name)
if not find(name,"^%.") then
instance.foundintrees[name] = (instance.foundintrees[name] or 0) + 1 -- maybe only one
end
end
--- split the next one up for readability (bu this module needs a cleanup anyway)
+-- split the next one up for readability (but this module needs a cleanup anyway)
local function can_be_dir(name) -- can become local
local fakepaths = instance.fakepaths
@@ -9756,61 +11889,62 @@ local function can_be_dir(name) -- can become local
fakepaths[name] = 2 -- no directory
end
end
- return (fakepaths[name] == 1)
+ return fakepaths[name] == 1
end
-local function collect_instance_files(filename,collected) -- todo : plugin (scanners, checkers etc)
- local result = collected or { }
+local preparetreepattern = Cs((P(".")/"%%." + P("-")/"%%-" + P(1))^0 * Cc("$"))
+
+-- this one is split in smaller functions but it needs testing
+
+local function collect_instance_files(filename,askedformat,allresults) -- todo : plugin (scanners, checkers etc)
+ local result = { }
local stamp = nil
- filename = file.collapse_path(filename)
+ askedformat = askedformat or ""
+ filename = collapsepath(filename)
-- speed up / beware: format problem
- if instance.remember then
- stamp = filename .. "--" .. instance.engine .. "--" .. instance.progname .. "--" .. instance.format
+ if instance.remember and not allresults then
+ stamp = filename .. "--" .. askedformat
if instance.found[stamp] then
if trace_locating then
- logs.report("fileio","remembering file '%s'",filename)
+ report_resolving("remembered file '%s'",filename)
end
+ resolvers.registerintrees(filename) -- for tracing used files
return instance.found[stamp]
end
end
- if not dangerous[instance.format or "?"] then
- if resolvers.isreadable.file(filename) then
+ if not dangerous[askedformat] then
+ if isreadable(filename) then
if trace_detail then
- logs.report("fileio","file '%s' found directly",filename)
+ report_resolving("file '%s' found directly",filename)
+ end
+ if stamp then
+ instance.found[stamp] = { filename }
end
- instance.found[stamp] = { filename }
return { filename }
end
end
if find(filename,'%*') then
if trace_locating then
- logs.report("fileio","checking wildcard '%s'", filename)
+ report_resolving("checking wildcard '%s'", filename)
end
- result = resolvers.find_wildcard_files(filename)
+ 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
- logs.report("fileio","qualified name '%s'", filename)
+ report_resolving("qualified name '%s'", filename)
end
result = { filename }
else
- local forcedname, ok, suffix = "", false, file.extname(filename)
+ local forcedname, ok, suffix = "", false, fileextname(filename)
if suffix == "" then -- why
- if instance.format == "" then
- forcedname = filename .. ".tex"
- if resolvers.isreadable.file(forcedname) then
- if trace_locating then
- logs.report("fileio","no suffix, forcing standard filetype 'tex'")
- end
- result, ok = { forcedname }, true
- end
- else
- local suffixes = resolvers.suffixes_of_format(instance.format)
- for _, s in next, suffixes do
+ local format_suffixes = askedformat == "" and resolvers.defaultsuffixes or suffixes[askedformat]
+ if format_suffixes then
+ for i=1,#format_suffixes do
+ local s = format_suffixes[i]
forcedname = filename .. "." .. s
- if resolvers.isreadable.file(forcedname) then
+ if isreadable(forcedname) then
if trace_locating then
- logs.report("fileio","no suffix, forcing format filetype '%s'", s)
+ report_resolving("no suffix, forcing format filetype '%s'", s)
end
result, ok = { forcedname }, true
break
@@ -9821,23 +11955,24 @@ local function collect_instance_files(filename,collected) -- todo : plugin (scan
if not ok and suffix ~= "" then
-- try to find in tree (no suffix manipulation), here we search for the
-- matching last part of the name
- local basename = file.basename(filename)
- local pattern = gsub(filename .. "$","([%.%-])","%%%1")
- local savedformat = instance.format
+ local basename = filebasename(filename)
+ local pattern = lpegmatch(preparetreepattern,filename)
+ -- messy .. to be sorted out
+ local savedformat = askedformat
local format = savedformat or ""
if format == "" then
- instance.format = resolvers.format_of_suffix(suffix)
+ askedformat = resolvers.formatofsuffix(suffix)
end
if not format then
- instance.format = "othertextfiles" -- kind of everything, maybe texinput is better
+ askedformat = "othertextfiles" -- kind of everything, maybe texinput is better
end
--
if basename ~= filename then
- local resolved = collect_instance_files(basename)
- if #result == 0 then
+ local resolved = collect_instance_files(basename,askedformat,allresults)
+ if #result == 0 then -- shouldn't this be resolved ?
local lowered = lower(basename)
if filename ~= lowered then
- resolved = collect_instance_files(lowered)
+ resolved = collect_instance_files(lowered,askedformat,allresults)
end
end
resolvers.format = savedformat
@@ -9862,57 +11997,60 @@ local function collect_instance_files(filename,collected) -- todo : plugin (scan
-- end
end
if not ok and trace_locating then
- logs.report("fileio","qualified name '%s'", filename)
+ report_resolving("qualified name '%s'", filename)
end
end
else
-- search spec
- local filetype, extra, done, wantedfiles, ext = '', nil, false, { }, file.extname(filename)
- if ext == "" then
- if not instance.force_suffixes then
- wantedfiles[#wantedfiles+1] = filename
- end
- else
- wantedfiles[#wantedfiles+1] = filename
- end
- if instance.format == "" then
- if ext == "" then
- local forcedname = filename .. '.tex'
- wantedfiles[#wantedfiles+1] = forcedname
- filetype = resolvers.format_of_suffix(forcedname)
- if trace_locating then
- logs.report("fileio","forcing filetype '%s'",filetype)
+ local filetype, done, wantedfiles, ext = '', false, { }, fileextname(filename)
+ -- -- tricky as filename can be bla.1.2.3
+ -- if not suffixmap[ext] then --- probably needs to be done elsewhere too
+ -- wantedfiles[#wantedfiles+1] = filename
+ -- end
+ wantedfiles[#wantedfiles+1] = filename
+ if askedformat == "" then
+ if ext == "" or not suffixmap[ext] then
+ local defaultsuffixes = resolvers.defaultsuffixes
+ for i=1,#defaultsuffixes do
+ local forcedname = filename .. '.' .. defaultsuffixes[i]
+ wantedfiles[#wantedfiles+1] = forcedname
+ filetype = resolvers.formatofsuffix(forcedname)
+ if trace_locating then
+ report_resolving("forcing filetype '%s'",filetype)
+ end
end
else
- filetype = resolvers.format_of_suffix(filename)
+ filetype = resolvers.formatofsuffix(filename)
if trace_locating then
- logs.report("fileio","using suffix based filetype '%s'",filetype)
+ report_resolving("using suffix based filetype '%s'",filetype)
end
end
else
- if ext == "" then
- local suffixes = resolvers.suffixes_of_format(instance.format)
- for _, s in next, suffixes do
- wantedfiles[#wantedfiles+1] = filename .. "." .. s
+ if ext == "" or not suffixmap[ext] then
+ local format_suffixes = suffixes[askedformat]
+ if format_suffixes then
+ for i=1,#format_suffixes do
+ wantedfiles[#wantedfiles+1] = filename .. "." .. format_suffixes[i]
+ end
end
end
- filetype = instance.format
+ filetype = askedformat
if trace_locating then
- logs.report("fileio","using given filetype '%s'",filetype)
+ report_resolving("using given filetype '%s'",filetype)
end
end
- local typespec = resolvers.variable_of_format(filetype)
- local pathlist = resolvers.expanded_path_list(typespec)
+ local typespec = resolvers.variableofformat(filetype)
+ local pathlist = resolvers.expandedpathlist(typespec)
if not pathlist or #pathlist == 0 then
-- no pathlist, access check only / todo == wildcard
if trace_detail then
- logs.report("fileio","checking filename '%s', filetype '%s', wanted files '%s'",filename, filetype or '?',concat(wantedfiles," | "))
+ report_resolving("checking filename '%s', filetype '%s', wanted files '%s'",filename, filetype or '?',concat(wantedfiles," | "))
end
for k=1,#wantedfiles do
local fname = wantedfiles[k]
- if fname and resolvers.isreadable.file(fname) then
+ if fname and isreadable(fname) then
filename, done = fname, true
- result[#result+1] = file.join('.',fname)
+ result[#result+1] = filejoin('.',fname)
break
end
end
@@ -9920,8 +12058,8 @@ local function collect_instance_files(filename,collected) -- todo : plugin (scan
local filelist = collect_files(wantedfiles)
local fl = filelist and filelist[1]
if fl then
- filename = fl[3]
- result[#result+1] = filename
+ filename = fl[3] -- not local?
+ result[#result+1] = resolvers.resolve(filename)
done = true
end
else
@@ -9930,76 +12068,65 @@ local function collect_instance_files(filename,collected) -- todo : plugin (scan
local dirlist = { }
if filelist then
for i=1,#filelist do
- dirlist[i] = file.dirname(filelist[i][2]) .. "/"
+ dirlist[i] = filedirname(filelist[i][3]) .. "/" -- was [2] .. gamble
end
end
if trace_detail then
- logs.report("fileio","checking filename '%s'",filename)
+ report_resolving("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
- logs.report("fileio","using pattern '%s' for path '%s'",expression,pathname)
+ report_resolving("using pattern '%s' for path '%s'",expression,pathname)
end
for k=1,#filelist do
local fl = filelist[k]
local f = fl[2]
local d = dirlist[k]
if find(d,expression) then
- --- todo, test for readable
- result[#result+1] = fl[3]
- resolvers.register_in_trees(f) -- for tracing used files
+ -- todo, test for readable
+ result[#result+1] = resolvers.resolve(fl[3]) -- no shortcut
done = true
- if instance.allresults then
+ if allresults then
if trace_detail then
- logs.report("fileio","match in hash for file '%s' on path '%s', continue scanning",f,d)
+ report_resolving("match to '%s' in hash for file '%s' and path '%s', continue scanning",expression,f,d)
end
else
if trace_detail then
- logs.report("fileio","match in hash for file '%s' on path '%s', quit scanning",f,d)
+ report_resolving("match to '%s' in hash for file '%s' and path '%s', quit scanning",expression,f,d)
end
break
end
elseif trace_detail then
- logs.report("fileio","no match in hash for file '%s' on path '%s'",f,d)
+ report_resolving("no match to '%s' in hash for file '%s' and path '%s'",expression,f,d)
end
end
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,"/+$","")
if can_be_dir(ppname) then
for k=1,#wantedfiles do
local w = wantedfiles[k]
- local fname = file.join(ppname,w)
- if resolvers.isreadable.file(fname) then
+ local fname = filejoin(ppname,w)
+ if isreadable(fname) then
if trace_detail then
- logs.report("fileio","found '%s' by scanning",fname)
+ report_resolving("found '%s' by scanning",fname)
end
result[#result+1] = fname
done = true
- if not instance.allresults then break end
+ if not allresults then break end
end
end
else
@@ -10009,56 +12136,66 @@ local function collect_instance_files(filename,collected) -- todo : plugin (scan
end
end
if not done and doscan then
- -- todo: slow path scanning
+ -- todo: slow path scanning ... although we now have tree:// supported in $TEXMF
end
- if done and not instance.allresults then break end
+ if done and not allresults then break end
end
end
end
for k=1,#result do
- result[k] = file.collapse_path(result[k])
+ local rk = collapsepath(result[k])
+ result[k] = rk
+ resolvers.registerintrees(rk) -- for tracing used files
end
- if instance.remember then
+ if stamp then
instance.found[stamp] = result
end
return result
end
-if not resolvers.concatinators then resolvers.concatinators = { } end
+-- -- -- begin of main file search routing -- -- --
-resolvers.concatinators.tex = file.join
-resolvers.concatinators.file = resolvers.concatinators.tex
-function resolvers.find_files(filename,filetype,mustexist)
- if type(mustexist) == boolean then
- -- all set
- elseif type(filetype) == 'boolean' then
- filetype, mustexist = nil, false
- elseif type(filetype) ~= 'string' then
- filetype, mustexist = nil, false
- end
- instance.format = filetype or ''
- local result = collect_instance_files(filename)
+
+
+
+
+
+
+
+
+-- -- -- end of main file search routing -- -- --
+
+local function findfiles(filename,filetype,allresults)
+ local result = collect_instance_files(filename,filetype or "",allresults)
if #result == 0 then
local lowered = lower(filename)
if filename ~= lowered then
- return collect_instance_files(lowered)
+ return collect_instance_files(lowered,filetype or "",allresults)
end
end
- instance.format = ''
return result
end
-function resolvers.find_file(filename,filetype,mustexist)
- return (resolvers.find_files(filename,filetype,mustexist)[1] or "")
+function resolvers.findfiles(filename,filetype)
+ return findfiles(filename,filetype,true)
+end
+
+function resolvers.findfile(filename,filetype)
+ return findfiles(filename,filetype,false)[1] or ""
end
-function resolvers.find_given_files(filename)
- local bname, result = file.basename(filename), { }
+function resolvers.findpath(filename,filetype)
+ return filedirname(findfiles(filename,filetype,false)[1] or "")
+end
+
+local function findgivenfiles(filename,allresults)
+ local bname, result = filebasename(filename), { }
local hashes = instance.hashes
+ local noffound = 0
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
@@ -10070,13 +12207,21 @@ function resolvers.find_given_files(filename)
end
if blist then
if type(blist) == 'string' then
- result[#result+1] = resolvers.concatinators[hash.type](hash.tag,blist,bname) or ""
- if not instance.allresults then break end
+ local found = methodhandler('concatinators',hash.type,hash.name,blist,bname) or ""
+ if found ~= "" then
+ noffound = noffound + 1
+ result[noffound] = resolvers.resolve(found)
+ if not allresults then break end
+ end
else
for kk=1,#blist do
local vv = blist[kk]
- result[#result+1] = resolvers.concatinators[hash.type](hash.tag,vv,bname) or ""
- if not instance.allresults then break end
+ local found = methodhandler('concatinators',hash.type,hash.name,vv,bname) or ""
+ if found ~= "" then
+ noffound = noffound + 1
+ result[noffound] = resolvers.resolve(found)
+ if not allresults then break end
+ end
end
end
end
@@ -10084,24 +12229,31 @@ function resolvers.find_given_files(filename)
return result
end
-function resolvers.find_given_file(filename)
- return (resolvers.find_given_files(filename)[1] or "")
+function resolvers.findgivenfiles(filename)
+ return findgivenfiles(filename,true)
+end
+
+function resolvers.findgivenfile(filename)
+ return findgivenfiles(filename,false)[1] or ""
end
-local function doit(path,blist,bname,tag,kind,result,allresults)
+local function doit(path,blist,bname,tag,variant,result,allresults)
local done = false
- if blist and kind then
+ if blist and variant then
+ local resolve = resolvers.resolve -- added
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 ""
+ local full = methodhandler('concatinators',variant,tag,blist,bname) or ""
+ result[#result+1] = resolve(full)
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 ""
+ local full = methodhandler('concatinators',variant,tag,vv,bname) or ""
+ result[#result+1] = resolve(full)
done = true
if not allresults then break end
end
@@ -10111,30 +12263,32 @@ local function doit(path,blist,bname,tag,kind,result,allresults)
return done
end
-function resolvers.find_wildcard_files(filename) -- todo: remap:
- local result = { }
- local bname, dname = file.basename(filename), file.dirname(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 files, allresults, done = instance.files, instance.allresults, false
+
+local makewildcard = Cs(
+ (P("^")^0 * P("/") * P(-1) + P(-1)) /".*"
+ + (P("^")^0 * P("/") / "")^0 * (P("*")/".*" + P("-")/"%%-" + P(".")/"%%." + P("?")/"."+ P("\\")/"/" + P(1))^0
+)
+
+function resolvers.wildcardpattern(pattern)
+ return lpegmatch(makewildcard,pattern) or pattern
+end
+
+local function findwildcardfiles(filename,allresults,result) -- todo: remap: and lpeg
+ result = result or { }
+ local base = filebasename(filename)
+ local dirn = filedirname(filename)
+ local path = lower(lpegmatch(makewildcard,dirn) or dirn)
+ local name = lower(lpegmatch(makewildcard,base) or base)
+ 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
@@ -10144,8 +12298,8 @@ function resolvers.find_wildcard_files(filename) -- todo: remap:
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
@@ -10154,8 +12308,12 @@ function resolvers.find_wildcard_files(filename) -- todo: remap:
return result
end
-function resolvers.find_wildcard_file(filename)
- return (resolvers.find_wildcard_files(filename)[1] or "")
+function resolvers.findwildcardfiles(filename,result)
+ return findwildcardfiles(filename,true,result)
+end
+
+function resolvers.findwildcardfile(filename)
+ return findwildcardfiles(filename,false)[1] or ""
end
-- main user functions
@@ -10166,34 +12324,33 @@ end
function resolvers.load(option)
statistics.starttiming(instance)
- resolvers.resetconfig()
- resolvers.identify_cnf()
- resolvers.load_lua() -- will become the new method
- resolvers.expand_variables()
- resolvers.load_cnf() -- will be skipped when we have a lua file
- resolvers.expand_variables()
+ identify_configuration_files()
+ load_configuration_files()
if option ~= "nofiles" then
- resolvers.load_hash()
+ load_databases()
resolvers.automount()
end
statistics.stoptiming(instance)
+ local files = instance.files
+ return files and next(files) and true
+end
+
+local function report(str)
+ if trace_locating then
+ report_resolving(str) -- has already verbose
+ else
+ print(str)
+ end
end
-function resolvers.for_files(command, files, filetype, mustexist)
+function resolvers.dowithfilesandreport(command, files, ...) -- will move
if files and #files > 0 then
- local function report(str)
- if trace_locating then
- logs.report("fileio",str) -- has already verbose
- else
- print(str)
- end
- end
if trace_locating then
report('') -- ?
end
for f=1,#files do
local file = files[f]
- local result = command(file,filetype,mustexist)
+ local result = command(file,...)
if type(result) == 'string' then
report(result)
else
@@ -10205,21 +12362,16 @@ function resolvers.for_files(command, files, filetype, mustexist)
end
end
--- strtab
+-- obsolete
-resolvers.var_value = resolvers.variable -- output the value of variable $STRING.
-resolvers.expand_var = resolvers.expansion -- output variable expansion of STRING.
+-- resolvers.varvalue = resolvers.variable -- output the value of variable $STRING.
+-- resolvers.expandvar = resolvers.expansion -- output variable expansion of STRING.
-function resolvers.show_path(str) -- output search path for file type NAME
- return file.join_path(resolvers.expanded_path_list(resolvers.format_of_var(str)))
+function resolvers.showpath(str) -- output search path for file type NAME
+ return joinpath(resolvers.expandedpathlist(resolvers.formatofvariable(str)))
end
--- resolvers.find_file(filename)
--- resolvers.find_file(filename, filetype, mustexist)
--- resolvers.find_file(filename, mustexist)
--- resolvers.find_file(filename, filetype)
-
-function resolvers.register_file(files, name, path)
+function resolvers.registerfile(files, name, path)
if files[name] then
if type(files[name]) == 'string' then
files[name] = { files[name], path }
@@ -10231,101 +12383,24 @@ function resolvers.register_file(files, name, path)
end
end
-function resolvers.splitmethod(filename)
- if not filename then
- return { } -- safeguard
- elseif type(filename) == "table" then
- return filename -- already split
- elseif not find(filename,"://") then
- return { scheme="file", path = filename, original=filename } -- quick hack
- else
- return url.hashed(filename)
- end
-end
-
-function table.sequenced(t,sep) -- temp here
- local s = { }
- for k, v in next, t do -- indexed?
- s[#s+1] = k .. "=" .. tostring(v)
- end
- return concat(s, sep or " | ")
-end
-
-function resolvers.methodhandler(what, filename, filetype) -- ...
- filename = file.collapse_path(filename)
- local specification = (type(filename) == "string" and resolvers.splitmethod(filename)) or filename -- no or { }, let it bomb
- local scheme = specification.scheme
- if resolvers[what][scheme] then
- if trace_locating then
- logs.report("fileio","handler '%s' -> '%s' -> '%s'",specification.original,what,table.sequenced(specification))
- end
- return resolvers[what][scheme](filename,filetype) -- todo: specification
- else
- return resolvers[what].tex(filename,filetype) -- todo: specification
- end
-end
-
-function resolvers.clean_path(str)
- if str then
- str = gsub(str,"\\","/")
- str = gsub(str,"^!+","")
- str = gsub(str,"^~",resolvers.homedir)
- return str
- else
- return nil
- end
-end
-
-function resolvers.do_with_path(name,func)
- local pathlist = resolvers.expanded_path_list(name)
+function resolvers.dowithpath(name,func)
+ local pathlist = resolvers.expandedpathlist(name)
for i=1,#pathlist do
- func("^"..resolvers.clean_path(pathlist[i]))
+ func("^"..resolvers.cleanpath(pathlist[i]))
end
end
-function resolvers.do_with_var(name,func)
- func(expanded_var(name))
+function resolvers.dowithvariable(name,func)
+ func(expandedvariable(name))
end
-function resolvers.with_files(pattern,handle)
- local hashes = instance.hashes
- for i=1,#hashes do
- local hash = hashes[i]
- local blobpath = hash.tag
- local blobtype = hash.type
- if blobpath then
- local files = instance.files[blobpath]
- if files then
- for k,v in next, files do
- if find(k,"^remap:") then
- k = files[k]
- v = files[k] -- chained
- end
- if find(k,pattern) then
- if type(v) == "string" then
- handle(blobtype,blobpath,v,k)
- else
- for _,vv in next, v do -- indexed
- handle(blobtype,blobpath,vv,k)
- end
- end
- end
- end
- end
- end
- end
-end
-
-function resolvers.locate_format(name)
- local barename, fmtname = gsub(name,"%.%a+$",""), ""
- if resolvers.usecache then
- local path = file.join(caches.setpath("formats")) -- maybe platform
- fmtname = file.join(path,barename..".fmt") or ""
- end
+function resolvers.locateformat(name)
+ local barename = file.removesuffix(name) -- gsub(name,"%.%a+$","")
+ local fmtname = caches.getfirstreadablefile(barename..".fmt","formats") or ""
if fmtname == "" then
- fmtname = resolvers.find_files(barename..".fmt")[1] or ""
+ fmtname = resolvers.findfile(barename..".fmt")
+ fmtname = resolvers.cleanpath(fmtname)
end
- fmtname = resolvers.clean_path(fmtname)
if fmtname ~= "" then
local barename = file.removesuffix(fmtname)
local luaname, lucname, luiname = barename .. ".lua", barename .. ".luc", barename .. ".lui"
@@ -10340,7 +12415,7 @@ function resolvers.locate_format(name)
return nil, nil
end
-function resolvers.boolean_variable(str,default)
+function resolvers.booleanvariable(str,default)
local b = resolvers.expansion(str)
if b == "" then
return default
@@ -10350,204 +12425,62 @@ function resolvers.boolean_variable(str,default)
end
end
-texconfig.kpse_init = false
-
-kpse = { original = kpse } setmetatable(kpse, { __index = function(k,v) return resolvers[v] end } )
-
--- for a while
-
-input = resolvers
-
-
-end -- of closure
-
-do -- create closure to overcome 200 locals limit
-
-if not modules then modules = { } end modules ['data-tmp'] = {
- version = 1.001,
- comment = "companion to luat-lib.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
---[[ldx--
-<p>This module deals with caching data. It sets up the paths and
-implements loaders and savers for tables. Best is to set the
-following variable. When not set, the usual paths will be
-checked. Personally I prefer the (users) temporary path.</p>
-
-</code>
-TEXMFCACHE=$TMP;$TEMP;$TMPDIR;$TEMPDIR;$HOME;$TEXMFVAR;$VARTEXMF;.
-</code>
-
-<p>Currently we do no locking when we write files. This is no real
-problem because most caching involves fonts and the chance of them
-being written at the same time is small. We also need to extend
-luatools with a recache feature.</p>
---ldx]]--
-
-local format, lower, gsub = string.format, string.lower, string.gsub
-
-local trace_cache = false trackers.register("resolvers.cache", function(v) trace_cache = v end) -- not used yet
-
-caches = caches or { }
-
-caches.path = caches.path or nil
-caches.base = caches.base or "luatex-cache"
-caches.more = caches.more or "context"
-caches.direct = false -- true is faster but may need huge amounts of memory
-caches.tree = false
-caches.paths = caches.paths or nil
-caches.force = true -- changed for TEXLIVE by TH
-caches.defaults = { "TEXMFCACHE", "TMPDIR", "TEMPDIR", "TMP", "TEMP", "HOME", "HOMEPATH" }
-
-function caches.temp()
- local cachepath = nil
- local function check(list,isenv)
- if not cachepath then
- for k=1,#list do
- local v = list[k]
- cachepath = (isenv and (os.env[v] or "")) or v or ""
- if cachepath == "" then
- -- next
- else
- cachepath = resolvers.clean_path(cachepath)
- if lfs.isdir(cachepath) and file.iswritable(cachepath) then -- lfs.attributes(cachepath,"mode") == "directory"
- break
- elseif caches.force or io.ask(format("\nShould I create the cache path %s?",cachepath), "no", { "yes", "no" }) == "yes" then
- dir.mkdirs(cachepath)
- if lfs.isdir(cachepath) and file.iswritable(cachepath) then
- break
+function resolvers.dowithfilesintree(pattern,handle,before,after) -- will move, can be a nice iterator instead
+ local instance = resolvers.instance
+ local hashes = instance.hashes
+ for i=1,#hashes do
+ local hash = hashes[i]
+ local blobtype = hash.type
+ local blobpath = hash.name
+ if blobpath then
+ if before then
+ before(blobtype,blobpath,pattern)
+ end
+ local files = instance.files[blobpath]
+ local total, checked, done = 0, 0, 0
+ if files then
+ for k,v in next, files do
+ total = total + 1
+ if find(k,"^remap:") then
+ k = files[k]
+ v = k -- files[k] -- chained
+ end
+ if find(k,pattern) then
+ if type(v) == "string" then
+ checked = checked + 1
+ if handle(blobtype,blobpath,v,k) then
+ done = done + 1
+ end
+ else
+ checked = checked + #v
+ for i=1,#v do
+ if handle(blobtype,blobpath,v[i],k) then
+ done = done + 1
+ end
+ end
end
end
end
- cachepath = nil
+ end
+ if after then
+ after(blobtype,blobpath,pattern,total,checked,done)
end
end
end
- check(resolvers.clean_path_list("TEXMFCACHE") or { })
- check(caches.defaults,true)
- if not cachepath then
- print("\nfatal error: there is no valid (writable) cache path defined\n")
- os.exit()
- elseif not lfs.isdir(cachepath) then -- lfs.attributes(cachepath,"mode") ~= "directory"
- print(format("\nfatal error: cache path %s is not a directory\n",cachepath))
- os.exit()
- end
- cachepath = file.collapse_path(cachepath)
- function caches.temp()
- return cachepath
- end
- return cachepath
-end
-
-function caches.configpath()
- return table.concat(resolvers.instance.cnffiles,";")
-end
-
-function caches.hashed(tree)
- return md5.hex(gsub(lower(tree),"[\\\/]+","/"))
-end
-
-function caches.treehash()
- local tree = caches.configpath()
- if not tree or tree == "" then
- return false
- else
- return caches.hashed(tree)
- end
-end
-
-function caches.setpath(...)
- if not caches.path then
- if not caches.path then
- caches.path = caches.temp()
- end
- caches.path = resolvers.clean_path(caches.path) -- to be sure
- caches.tree = caches.tree or caches.treehash()
- if caches.tree then
- caches.path = dir.mkdirs(caches.path,caches.base,caches.more,caches.tree)
- else
- caches.path = dir.mkdirs(caches.path,caches.base,caches.more)
- end
- end
- if not caches.path then
- caches.path = '.'
- end
- caches.path = resolvers.clean_path(caches.path)
- local dirs = { ... }
- if #dirs > 0 then
- local pth = dir.mkdirs(caches.path,...)
- return pth
- end
- caches.path = dir.expand_name(caches.path)
- return caches.path
-end
-
-function caches.definepath(category,subcategory)
- return function()
- return caches.setpath(category,subcategory)
- end
-end
-
-function caches.setluanames(path,name)
- return path .. "/" .. name .. ".tma", path .. "/" .. name .. ".tmc"
end
-function caches.loaddata(path,name)
- local tmaname, tmcname = caches.setluanames(path,name)
- local loader = loadfile(tmcname) or loadfile(tmaname)
- if loader then
- loader = loader()
- collectgarbage("step")
- return loader
- else
- return false
- end
-end
+resolvers.obsolete = resolvers.obsolete or { }
+local obsolete = resolvers.obsolete
---~ function caches.loaddata(path,name)
---~ local tmaname, tmcname = caches.setluanames(path,name)
---~ return dofile(tmcname) or dofile(tmaname)
---~ end
-
-function caches.iswritable(filepath,filename)
- local tmaname, tmcname = caches.setluanames(filepath,filename)
- return file.iswritable(tmaname)
-end
-
-function caches.savedata(filepath,filename,data,raw)
- local tmaname, tmcname = caches.setluanames(filepath,filename)
- local reduce, simplify = true, true
- if raw then
- reduce, simplify = false, false
- end
- data.cache_uuid = os.uuid()
- if caches.direct then
- file.savedata(tmaname, table.serialize(data,'return',false,true,false)) -- no hex
- else
- table.tofile(tmaname, data,'return',false,true,false) -- maybe not the last true
- end
- local cleanup = resolvers.boolean_variable("PURGECACHE", false)
- local strip = resolvers.boolean_variable("LUACSTRIP", true)
- utils.lua.compile(tmaname, tmcname, cleanup, strip)
-end
-
--- here we use the cache for format loading (texconfig.[formatname|jobname])
-
---~ if tex and texconfig and texconfig.formatname and texconfig.formatname == "" then
-if tex and texconfig and (not texconfig.formatname or texconfig.formatname == "") and input and resolvers.instance then
- if not texconfig.luaname then texconfig.luaname = "cont-en.lua" end -- or luc
- texconfig.formatname = caches.setpath("formats") .. "/" .. gsub(texconfig.luaname,"%.lu.$",".fmt")
-end
+resolvers.find_file = resolvers.findfile obsolete.find_file = resolvers.findfile
+resolvers.find_files = resolvers.findfiles obsolete.find_files = resolvers.findfiles
end -- of closure
do -- create closure to overcome 200 locals limit
-if not modules then modules = { } end modules ['data-res'] = {
+if not modules then modules = { } end modules ['data-pre'] = {
version = 1.001,
comment = "companion to luat-lib.mkiv",
author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
@@ -10555,17 +12488,28 @@ if not modules then modules = { } end modules ['data-res'] = {
license = "see context related readme files"
}
---~ print(resolvers.resolve("abc env:tmp file:cont-en.tex path:cont-en.tex full:cont-en.tex rel:zapf/one/p-chars.tex"))
+-- It could be interesting to hook the resolver in the file
+-- opener so that unresolved prefixes travel around and we
+-- get more abstraction.
+
+-- As we use this beforehand we will move this up in the chain
+-- of loading.
+
-local upper, lower, gsub = string.upper, string.lower, string.gsub
+local resolvers = resolvers
+local prefixes = utilities.storage.allocate()
+resolvers.prefixes = prefixes
-local prefixes = { }
+local gsub = string.gsub
+local cleanpath, findgivenfile, expansion = resolvers.cleanpath, resolvers.findgivenfile, resolvers.expansion
+local getenv = resolvers.getenv -- we can probably also use resolvers.expansion
+local P, Cs, lpegmatch = lpeg.P, lpeg.Cs, lpeg.match
prefixes.environment = function(str)
- return resolvers.clean_path(os.getenv(str) or os.getenv(upper(str)) or os.getenv(lower(str)) or "")
+ return cleanpath(expansion(str))
end
-prefixes.relative = function(str,n)
+prefixes.relative = function(str,n) -- lfs.isfile
if io.exists(str) then
-- nothing
elseif io.exists("./" .. str) then
@@ -10581,7 +12525,7 @@ prefixes.relative = function(str,n)
end
end
end
- return resolvers.clean_path(str)
+ return cleanpath(str)
end
prefixes.auto = function(str)
@@ -10593,18 +12537,34 @@ prefixes.auto = function(str)
end
prefixes.locate = function(str)
- local fullname = resolvers.find_given_file(str) or ""
- return resolvers.clean_path((fullname ~= "" and fullname) or str)
+ local fullname = findgivenfile(str) or ""
+ return cleanpath((fullname ~= "" and fullname) or str)
end
prefixes.filename = function(str)
- local fullname = resolvers.find_given_file(str) or ""
- return resolvers.clean_path(file.basename((fullname ~= "" and fullname) or str))
+ local fullname = findgivenfile(str) or ""
+ return cleanpath(file.basename((fullname ~= "" and fullname) or str))
end
prefixes.pathname = function(str)
- local fullname = resolvers.find_given_file(str) or ""
- return resolvers.clean_path(file.dirname((fullname ~= "" and fullname) or str))
+ local fullname = findgivenfile(str) or ""
+ return cleanpath(file.dirname((fullname ~= "" and fullname) or str))
+end
+
+prefixes.selfautoloc = function(str)
+ return cleanpath(file.join(getenv('SELFAUTOLOC'),str))
+end
+
+prefixes.selfautoparent = function(str)
+ return cleanpath(file.join(getenv('SELFAUTOPARENT'),str))
+end
+
+prefixes.selfautodir = function(str)
+ return cleanpath(file.join(getenv('SELFAUTODIR'),str))
+end
+
+prefixes.home = function(str)
+ return cleanpath(file.join(getenv('HOME'),str))
end
prefixes.env = prefixes.environment
@@ -10633,19 +12593,28 @@ local function _resolve_(method,target)
end
end
-local function resolve(str)
- if type(str) == "table" then
- for k=1,#str do
- local v = str[k]
- str[k] = resolve(v) or v
- end
- elseif str and str ~= "" then
- str = gsub(str,"([a-z]+):([^ \"\']*)",_resolve_)
+local resolved, abstract = { }, { }
+
+function resolvers.resetresolve(str)
+ resolved, abstract = { }, { }
+end
+
+local function resolve(str) -- use schemes, this one is then for the commandline only
+ local res = resolved[str]
+ if not res then
+ res = gsub(str,"([a-z][a-z]+):([^ \"\']*)",_resolve_)
+ resolved[str] = res
+ abstract[res] = str
end
- return str
+ return res
+end
+
+local function unresolve(str)
+ return abstract[str] or str
end
-resolvers.resolve = resolve
+resolvers.resolve = resolve
+resolvers.unresolve = unresolve
if os.uname then
@@ -10657,6 +12626,42 @@ if os.uname then
end
+if os.type == "unix" then
+
+ local pattern
+
+ local function makepattern(t,k,v)
+ local colon = P(":")
+ local p
+ for k, v in table.sortedpairs(prefixes) do
+ if p then
+ p = P(k) + p
+ else
+ p = P(k)
+ end
+ end
+ pattern = Cs((p * colon + colon/";" + P(1))^0)
+ if t then
+ t[k] = v
+ end
+ end
+
+ makepattern()
+
+ getmetatable(prefixes).__newindex = makepattern
+
+ function resolvers.repath(str)
+ return lpegmatch(pattern,str)
+ end
+
+else -- already the default:
+
+ function resolvers.repath(str)
+ return str
+ end
+
+end
+
end -- of closure
@@ -10670,13 +12675,23 @@ if not modules then modules = { } end modules ['data-inp'] = {
license = "see context related readme files"
}
-resolvers.finders = resolvers.finders or { }
-resolvers.openers = resolvers.openers or { }
-resolvers.loaders = resolvers.loaders or { }
+local allocate = utilities.storage.allocate
+local resolvers = resolvers
+
+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 }
-resolvers.finders.notfound = { nil }
-resolvers.openers.notfound = { nil }
-resolvers.loaders.notfound = { false, nil, 0 }
+registermethod("finders", finders, "uri")
+registermethod("openers", openers, "uri")
+registermethod("loaders", loaders, "uri")
+
+resolvers.finders = finders
+resolvers.openers = openers
+resolvers.loaders = loaders
end -- of closure
@@ -10691,15 +12706,23 @@ if not modules then modules = { } end modules ['data-out'] = {
license = "see context related readme files"
}
-outputs = outputs or { }
+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-con'] = {
+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",
@@ -10707,6 +12730,124 @@ if not modules then modules = { } end modules ['data-con'] = {
license = "see context related readme files"
}
+local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
+
+local report_files = logs.reporter("resolvers","files")
+
+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
+ local realname = resolvers.resolve(name) -- no shortcut
+ if realname and realname ~= '' and lfs.isdir(realname) then
+ if trace_locating then
+ report_files("file locator '%s' found as '%s'",name,realname)
+ end
+ resolvers.appendhash('file',name,true) -- cache
+ elseif trace_locating then
+ report_files("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 path = specification.filename
+ local content = resolvers.scanfiles(path)
+ resolvers.registerfilehash(path,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_files("file finder: '%s' found",filename)
+ end
+ return foundname
+ else
+ if trace_locating then
+ report_files("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() logs.show_close(filename) 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
+ if trace_locating then
+ report_files("file opener, '%s' opened",filename)
+ end
+ return openers.helpers.textopener("file",filename,f)
+ end
+ end
+ if trace_locating then
+ report_files("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_files("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_files("file loader, '%s' not found",filename)
+ end
+ return loaders.notfound()
+end
+
+
+end -- of closure
+
+do -- create closure to overcome 200 locals limit
+
+if not modules then modules = { } end modules ['data-con'] = {
+ version = 1.100,
+ comment = "companion to luat-lib.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
local format, lower, gsub = string.format, string.lower, string.gsub
local trace_cache = false trackers.register("resolvers.cache", function(v) trace_cache = v end)
@@ -10726,50 +12867,63 @@ table structures without bothering about the disk cache.</p>
<p>Examples of usage can be found in the font related code.</p>
--ldx]]--
-containers = containers or { }
-
+containers = containers or { }
+local containers = containers
containers.usecache = true
+local report_containers = logs.reporter("resolvers","containers")
+
local function report(container,tag,name)
if trace_cache or trace_containers then
- logs.report(format("%s cache",container.subcategory),"%s: %s",tag,name or 'invalid')
+ report_containers("container: %s, tag: %s, name: %s",container.subcategory,tag,name or 'invalid')
end
end
local allocated = { }
--- tracing
+local mt = {
+ __index = function(t,k)
+ if k == "writable" then
+ local writable = caches.getwritablepath(t.category,t.subcategory) or { "." }
+ t.writable = writable
+ return writable
+ elseif k == "readables" then
+ local readables = caches.getreadablepaths(t.category,t.subcategory) or { "." }
+ t.readables = readables
+ return readables
+ end
+ end,
+ __storage__ = true
+}
function containers.define(category, subcategory, version, enabled)
- return function()
- if category and subcategory then
- local c = allocated[category]
- if not c then
- c = { }
- allocated[category] = c
- end
- local s = c[subcategory]
- if not s then
- s = {
- category = category,
- subcategory = subcategory,
- storage = { },
- enabled = enabled,
- version = version or 1.000,
- trace = false,
- path = caches and caches.setpath and caches.setpath(category,subcategory),
- }
- c[subcategory] = s
- end
- return s
- else
- return nil
+ if category and subcategory then
+ local c = allocated[category]
+ if not c then
+ c = { }
+ allocated[category] = c
+ end
+ local s = c[subcategory]
+ if not s then
+ s = {
+ category = category,
+ subcategory = subcategory,
+ storage = { },
+ enabled = enabled,
+ version = version or math.pi, -- after all, this is TeX
+ trace = false,
+ -- writable = caches.getwritablepath and caches.getwritablepath (category,subcategory) or { "." },
+ -- readables = caches.getreadablepaths and caches.getreadablepaths(category,subcategory) or { "." },
+ }
+ setmetatable(s,mt)
+ c[subcategory] = s
end
+ return s
end
end
function containers.is_usable(container, name)
- return container.enabled and caches and caches.iswritable(container.path, name)
+ return container.enabled and caches and caches.is_writable(container.writable, name)
end
function containers.is_valid(container, name)
@@ -10782,18 +12936,20 @@ function containers.is_valid(container, name)
end
function containers.read(container,name)
- if container.enabled and caches and not container.storage[name] and containers.usecache then
- container.storage[name] = caches.loaddata(container.path,name)
- if containers.is_valid(container,name) then
+ local storage = container.storage
+ local stored = storage[name]
+ if not stored and container.enabled and caches and containers.usecache then
+ stored = caches.loaddata(container.readables,name)
+ if stored and stored.cache_version == container.version then
report(container,"loaded",name)
else
- container.storage[name] = nil
+ stored = nil
end
- end
- if container.storage[name] then
+ storage[name] = stored
+ elseif stored then
report(container,"reusing",name)
end
- return container.storage[name]
+ return stored
end
function containers.write(container, name, data)
@@ -10802,7 +12958,7 @@ function containers.write(container, name, data)
if container.enabled and caches then
local unique, shared = data.unique, data.shared
data.unique, data.shared = nil, nil
- caches.savedata(container.path, name, data)
+ caches.savedata(container.writable, name, data)
report(container,"saved",name)
data.unique, data.shared = unique, shared
end
@@ -10837,50 +12993,18 @@ local format, lower, gsub, find = string.format, string.lower, string.gsub, stri
local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
--- since we want to use the cache instead of the tree, we will now
--- reimplement the saver.
-
-local save_data = resolvers.save_data
-local load_data = resolvers.load_data
+local report_mounts = logs.reporter("resolvers","mounts")
-resolvers.cachepath = nil -- public, for tracing
-resolvers.usecache = true -- public, for tracing
-
-function resolvers.save_data(dataname)
- save_data(dataname, function(cachename,dataname)
- resolvers.usecache = not toboolean(resolvers.expansion("CACHEINTDS") or "false",true)
- if resolvers.usecache then
- resolvers.cachepath = resolvers.cachepath or caches.definepath("trees")
- return file.join(resolvers.cachepath(),caches.hashed(cachename))
- else
- return file.join(cachename,dataname)
- end
- end)
-end
-
-function resolvers.load_data(pathname,dataname,filename)
- load_data(pathname,dataname,filename,function(dataname,filename)
- resolvers.usecache = not toboolean(resolvers.expansion("CACHEINTDS") or "false",true)
- if resolvers.usecache then
- resolvers.cachepath = resolvers.cachepath or caches.definepath("trees")
- return file.join(resolvers.cachepath(),caches.hashed(pathname))
- else
- if not filename or (filename == "") then
- filename = dataname
- end
- return file.join(pathname,filename)
- end
- end)
-end
+local resolvers = resolvers
-- we will make a better format, maybe something xml or just text or lua
resolvers.automounted = resolvers.automounted or { }
function resolvers.automount(usecache)
- local mountpaths = resolvers.clean_path_list(resolvers.expansion('TEXMFMOUNT'))
+ local mountpaths = resolvers.cleanpathlist(resolvers.expansion('TEXMFMOUNT'))
if (not mountpaths or #mountpaths == 0) and usecache then
- mountpaths = { caches.setpath("mount") }
+ mountpaths = caches.getreadablepaths("mount")
end
if mountpaths and #mountpaths > 0 then
statistics.starttiming(resolvers.instance)
@@ -10894,7 +13018,7 @@ function resolvers.automount(usecache)
-- skip
elseif find(line,"^zip://") then
if trace_locating then
- logs.report("fileio","mounting %s",line)
+ report_mounts("mounting %s",line)
end
table.insert(resolvers.automounted,line)
resolvers.usezipfile(line)
@@ -10910,40 +13034,40 @@ end
-- status info
-statistics.register("used config path", function() return caches.configpath() end)
-statistics.register("used cache path", function() return caches.temp() or "?" end)
+statistics.register("used config file", function() return caches.configfiles() end)
+statistics.register("used cache path", function() return caches.usedpaths() end)
-- experiment (code will move)
-function statistics.save_fmt_status(texname,formatbanner,sourcefile) -- texname == formatname
+function statistics.savefmtstatus(texname,formatbanner,sourcefile) -- texname == formatname
local enginebanner = status.list().banner
if formatbanner and enginebanner and sourcefile then
local luvname = file.replacesuffix(texname,"luv")
local luvdata = {
enginebanner = enginebanner,
formatbanner = formatbanner,
- sourcehash = md5.hex(io.loaddata(resolvers.find_file(sourcefile)) or "unknown"),
+ sourcehash = md5.hex(io.loaddata(resolvers.findfile(sourcefile)) or "unknown"),
sourcefile = sourcefile,
}
io.savedata(luvname,table.serialize(luvdata,true))
end
end
-function statistics.check_fmt_status(texname)
+function statistics.checkfmtstatus(texname)
local enginebanner = status.list().banner
if enginebanner and texname then
local luvname = file.replacesuffix(texname,"luv")
if lfs.isfile(luvname) then
local luv = dofile(luvname)
if luv and luv.sourcefile then
- local sourcehash = md5.hex(io.loaddata(resolvers.find_file(luv.sourcefile)) or "unknown")
+ local sourcehash = md5.hex(io.loaddata(resolvers.findfile(luv.sourcefile)) or "unknown")
local luvbanner = luv.enginebanner or "?"
if luvbanner ~= enginebanner then
- return string.format("engine mismatch (luv:%s <> bin:%s)",luvbanner,enginebanner)
+ return format("engine mismatch (luv: %s <> bin: %s)",luvbanner,enginebanner)
end
local luvhash = luv.sourcehash or "?"
if luvhash ~= sourcehash then
- return string.format("source mismatch (luv:%s <> bin:%s)",luvhash,sourcehash)
+ return format("source mismatch (luv: %s <> bin: %s)",luvhash,sourcehash)
end
else
return "invalid status file"
@@ -10968,25 +13092,30 @@ if not modules then modules = { } end modules ['data-zip'] = {
license = "see context related readme files"
}
+-- 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)
+local report_zip = logs.reporter("resolvers","zip")
+
-- 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
-zip = zip or { }
-zip.archives = zip.archives or { }
-zip.registeredfiles = zip.registeredfiles or { }
+local resolvers = resolvers
+
+zip = zip or { }
+local zip = zip
-local finders, openers, loaders = resolvers.finders, resolvers.openers, resolvers.loaders
-local locators, hashers, concatinators = resolvers.locators, resolvers.hashers, resolvers.concatinators
+zip.archives = zip.archives or { }
+local archives = zip.archives
-local archives = zip.archives
+zip.registeredfiles = zip.registeredfiles or { }
+local registeredfiles = zip.registeredfiles
local function validzip(str) -- todo: use url splitter
if not find(str,"^zip://") then
@@ -11002,7 +13131,7 @@ function zip.openarchive(name)
else
local arch = archives[name]
if not arch then
- local full = resolvers.find_file(name) or ""
+ local full = resolvers.findfile(name) or ""
arch = (full ~= "" and zip.open(full)) or false
archives[name] = arch
end
@@ -11017,163 +13146,162 @@ 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
- logs.report("fileio","zip locator, archive '%s' found",specification.original)
+ if zipfile then
+ report_zip("locator, archive '%s' found",archive)
else
- logs.report("fileio","zip locator, archive '%s' not found",specification.original)
+ report_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
- logs.report("fileio","loading zip file '%s' as '%s'",name,tag)
+ report_zip("loading 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
- logs.report("fileio","zip finder, archive '%s' found",specification.path)
+ report_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
- logs.report("fileio","zip finder, file '%s' found",q.name)
+ report_zip("finder, file '%s' found",queryname)
end
return specification.original
elseif trace_locating then
- logs.report("fileio","zip finder, file '%s' not found",q.name)
+ report_zip("finder, file '%s' not found",queryname)
end
elseif trace_locating then
- logs.report("fileio","zip finder, unknown archive '%s'",specification.path)
+ report_zip("finder, unknown archive '%s'",archive)
end
end
end
if trace_locating then
- logs.report("fileio","zip finder, '%s' not found",filename)
+ report_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
- logs.report("fileio","zip opener, archive '%s' opened",zipspecification.path)
+ report_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)
if trace_locating then
- logs.report("fileio","zip opener, file '%s' found",q.name)
+ report_zip("opener, file '%s' found",queryname)
end
- return openers.text_opener(specification,dfile,'zip')
+ return resolvers.openers.helpers.textopener('zip',original,dfile)
elseif trace_locating then
- logs.report("fileio","zip opener, file '%s' not found",q.name)
+ report_zip("opener, file '%s' not found",queryname)
end
elseif trace_locating then
- logs.report("fileio","zip opener, unknown archive '%s'",zipspecification.path)
+ report_zip("opener, unknown archive '%s'",archive)
end
end
end
if trace_locating then
- logs.report("fileio","zip opener, '%s' not found",filename)
+ report_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
- logs.report("fileio","zip loader, archive '%s' opened",specification.path)
+ report_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
- logs.report("fileio","zip loader, file '%s' loaded",filename)
+ report_zip("loader, file '%s' loaded",original)
end
local s = dfile:read("*all")
dfile:close()
return true, s, #s
elseif trace_locating then
- logs.report("fileio","zip loader, file '%s' not found",q.name)
+ report_zip("loader, file '%s' not found",queryname)
end
elseif trace_locating then
- logs.report("fileio","zip loader, unknown archive '%s'",specification.path)
+ report_zip("loader, unknown archive '%s'",archive)
end
end
end
if trace_locating then
- logs.report("fileio","zip loader, '%s' not found",filename)
+ report_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 zip.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
- logs.report("fileio","zip registering, registering archive '%s'",zipname)
- end
- statistics.starttiming(instance)
- resolvers.prepend_hash('zip',zipname,zipfile)
- resolvers.extend_texmf_var(zipname) -- resets hashes too
- zip.registeredfiles[zipname] = z
- instance.files[zipname] = resolvers.register_zip_file(z,tree or "")
- statistics.stoptiming(instance)
+ report_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
- logs.report("fileio","zip registering, unknown archive '%s'",zipname)
+ report_zip("registering, unknown archive '%s'",archive)
end
elseif trace_locating then
- logs.report("fileio","zip registering, '%s' not found",zipname)
+ report_zip("registering, '%s' not found",archive)
end
end
-function resolvers.register_zip_file(z,tree)
+function resolvers.registerzipfile(z,tree)
local files, filter = { }, ""
if tree == "" then
filter = "^(.+)/(.-)$"
@@ -11181,9 +13309,9 @@ function resolvers.register_zip_file(z,tree)
filter = format("^%s/(.+)/(.-)$",tree)
end
if trace_locating then
- logs.report("fileio","zip registering, using filter '%s'",filter)
+ report_zip("registering, using filter '%s'",filter)
end
- local register, n = resolvers.register_file, 0
+ local register, n = resolvers.registerfile, 0
for i in z:files() do
local path, name = match(i.filename,filter)
if path then
@@ -11198,7 +13326,7 @@ function resolvers.register_zip_file(z,tree)
n = n + 1
end
end
- logs.report("fileio","zip registering, %s files registered",n)
+ report_zip("registering, %s files registered",n)
return files
end
@@ -11207,6 +13335,84 @@ end -- of closure
do -- create closure to overcome 200 locals limit
+if not modules then modules = { } end modules ['data-tre'] = {
+ version = 1.001,
+ comment = "companion to luat-lib.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- \input tree://oeps1/**/oeps.tex
+
+local find, gsub, format = string.find, string.gsub, string.format
+
+local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
+
+local report_trees = logs.reporter("resolvers","trees")
+
+local resolvers = resolvers
+
+local done, found, notfound = { }, { }, resolvers.finders.notfound
+
+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
+ local hash = done[path]
+ if not hash then
+ local pattern = path .. "/*" -- we will use the proper splitter
+ hash = dir.glob(pattern)
+ done[path] = hash
+ end
+ local pattern = "/" .. gsub(name,"([%.%-%+])", "%%%1") .. "$"
+ for k=1,#hash do
+ local v = hash[k]
+ if find(v,pattern) then
+ found[spec] = v
+ return v
+ end
+ end
+ end
+ fnd = notfound() -- false
+ found[spec] = fnd
+ end
+ return fnd
+end
+
+function resolvers.locators.tree(specification)
+ local name = specification.filename
+ if name ~= '' and lfs.isdir(name) then
+ if trace_locating then
+ report_trees("locator '%s' found",name)
+ end
+ resolvers.appendhash('tree',name,false) -- don't cache
+ elseif trace_locating then
+ report_trees("locator '%s' not found",name)
+ end
+end
+
+function resolvers.hashers.tree(specification)
+ local name = specification.filename
+ if trace_locating then
+ report_trees("analysing '%s'",name)
+ end
+ resolvers.methodhandler("hashers",name)
+end
+
+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
+
+do -- create closure to overcome 200 locals limit
+
if not modules then modules = { } end modules ['data-crl'] = {
version = 1.001,
comment = "companion to luat-lib.mkiv",
@@ -11215,58 +13421,59 @@ if not modules then modules = { } end modules ['data-crl'] = {
license = "see context related readme files"
}
-local gsub = string.gsub
+-- this one is replaced by data-sch.lua --
-curl = curl or { }
+local gsub = string.gsub
-curl.cached = { }
-curl.cachepath = caches.definepath("curl")
+local resolvers = resolvers
local finders, openers, loaders = resolvers.finders, resolvers.openers, resolvers.loaders
-function curl.fetch(protocol, name)
- local cachename = curl.cachepath() .. "/" .. gsub(name,"[^%a%d%.]+","-")
--- cachename = gsub(cachename,"[\\/]", io.fileseparator)
- cachename = gsub(cachename,"[\\]", "/") -- cleanup
- if not curl.cached[name] then
+resolvers.curl = resolvers.curl or { }
+local curl = resolvers.curl
+
+local cached = { }
+
+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[original] then
if not io.exists(cachename) then
- curl.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
- curl.cached[name] = cachename
+ cached[original] = cachename
else
- curl.cached[name] = ""
+ cached[original] = ""
end
end
- return curl.cached[name]
+ return cached[original]
end
-function finders.curl(protocol,filename)
- local foundname = curl.fetch(protocol, filename)
- return finders.generic(protocol,foundname,filetype)
-end
-
-function openers.curl(protocol,filename)
- return openers.generic(protocol,filename)
-end
+-- old code: we could be cleaner using specification (see schemes)
-function loaders.curl(protocol,filename)
- return loaders.generic(protocol,filename)
+local function finder(specification,filetype)
+ return resolvers.methodhandler("finders",runcurl(specification),filetype)
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
@@ -11287,9 +13494,13 @@ if not modules then modules = { } end modules ['data-lua'] = {
local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
+local report_libraries = logs.reporter("resolvers","libraries")
+
local gsub, insert = string.gsub, table.insert
local unpack = unpack or table.unpack
+local resolvers, package = resolvers, package
+
local libformats = { 'luatexlibs', 'tex', 'texmfscripts', 'othertextfiles' } -- 'luainputs'
local clibformats = { 'lib' }
@@ -11298,7 +13509,7 @@ local _path_, libpaths, _cpath_, clibpaths
function package.libpaths()
if not _path_ or package.path ~= _path_ then
_path_ = package.path
- libpaths = file.split_path(_path_,";")
+ libpaths = file.splitpath(_path_,";")
end
return libpaths
end
@@ -11306,7 +13517,7 @@ end
function package.clibpaths()
if not _cpath_ or package.cpath ~= _cpath_ then
_cpath_ = package.cpath
- clibpaths = file.split_path(_cpath_,";")
+ clibpaths = file.splitpath(_cpath_,";")
end
return clibpaths
end
@@ -11315,18 +13526,18 @@ local function thepath(...)
local t = { ... } t[#t+1] = "?.lua"
local path = file.join(unpack(t))
if trace_locating then
- logs.report("fileio","! appending '%s' to 'package.path'",path)
+ report_libraries("! appending '%s' to 'package.path'",path)
end
return path
end
local p_libpaths, a_libpaths = { }, { }
-function package.append_libpath(...)
+function package.appendtolibpath(...)
insert(a_libpath,thepath(...))
end
-function package.prepend_libpath(...)
+function package.prependtolibpath(...)
insert(p_libpaths,1,thepath(...))
end
@@ -11337,31 +13548,30 @@ local function loaded(libpaths,name,simple)
local libpath = libpaths[i]
local resolved = gsub(libpath,"%?",simple)
if trace_locating then -- more detail
- logs.report("fileio","! checking for '%s' on 'package.path': '%s' => '%s'",simple,libpath,resolved)
+ report_libraries("! 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
- logs.report("fileio","! lib '%s' located via 'package.path': '%s'",name,resolved)
+ report_libraries("! lib '%s' located via 'package.path': '%s'",name,resolved)
end
return loadfile(resolved)
end
end
end
-
package.loaders[2] = function(name) -- was [#package.loaders+1]
if trace_locating then -- mode detail
- logs.report("fileio","! locating '%s'",name)
+ report_libraries("! locating '%s'",name)
end
for i=1,#libformats do
local format = libformats[i]
- local resolved = resolvers.find_file(name,format) or ""
+ local resolved = resolvers.findfile(name,format) or ""
if trace_locating then -- mode detail
- logs.report("fileio","! checking for '%s' using 'libformat path': '%s'",name,format)
+ report_libraries("! checking for '%s' using 'libformat path': '%s'",name,format)
end
if resolved ~= "" then
if trace_locating then
- logs.report("fileio","! lib '%s' located via environment: '%s'",name,resolved)
+ report_libraries("! lib '%s' located via environment: '%s'",name,resolved)
end
return loadfile(resolved)
end
@@ -11379,16 +13589,16 @@ package.loaders[2] = function(name) -- was [#package.loaders+1]
for i=1,#clibformats do
-- better have a dedicated loop
local format = clibformats[i]
- local paths = resolvers.expanded_path_list_from_var(format)
+ local paths = resolvers.expandedpathlistfromvariable(format)
for p=1,#paths do
local path = paths[p]
local resolved = file.join(path,libname)
if trace_locating then -- mode detail
- logs.report("fileio","! checking for '%s' using 'clibformat path': '%s'",libname,path)
+ report_libraries("! 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
- logs.report("fileio","! lib '%s' located via 'clibformat': '%s'",libname,resolved)
+ report_libraries("! lib '%s' located via 'clibformat': '%s'",libname,resolved)
end
return package.loadlib(resolved,name)
end
@@ -11398,140 +13608,43 @@ package.loaders[2] = function(name) -- was [#package.loaders+1]
local libpath = clibpaths[i]
local resolved = gsub(libpath,"?",simple)
if trace_locating then -- more detail
- logs.report("fileio","! checking for '%s' on 'package.cpath': '%s'",simple,libpath)
+ report_libraries("! 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
- logs.report("fileio","! lib '%s' located via 'package.cpath': '%s'",name,resolved)
+ report_libraries("! lib '%s' located via 'package.cpath': '%s'",name,resolved)
end
return package.loadlib(resolved,name)
end
end
-- just in case the distribution is messed up
if trace_loading then -- more detail
- logs.report("fileio","! checking for '%s' using 'luatexlibs': '%s'",name)
+ report_libraries("! checking for '%s' using 'luatexlibs': '%s'",name)
end
- local resolved = resolvers.find_file(file.basename(name),'luatexlibs') or ""
+ local resolved = resolvers.findfile(file.basename(name),'luatexlibs') or ""
if resolved ~= "" then
if trace_locating then
- logs.report("fileio","! lib '%s' located by basename via environment: '%s'",name,resolved)
+ report_libraries("! lib '%s' located by basename via environment: '%s'",name,resolved)
end
return loadfile(resolved)
end
if trace_locating then
- logs.report("fileio",'? unable to locate lib: %s',name)
+ report_libraries('? unable to locate lib: %s',name)
end
-- return "unable to locate " .. name
end
resolvers.loadlualib = require
+-- -- -- --
-end -- of closure
-
-do -- create closure to overcome 200 locals limit
-
-if not modules then modules = { } end modules ['luat-kps'] = {
- version = 1.001,
- comment = "companion to luatools.lua",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
---[[ldx--
-<p>This file is used when we want the input handlers to behave like
-<type>kpsewhich</type>. What to do with the following:</p>
-
-<typing>
-{$SELFAUTOLOC,$SELFAUTODIR,$SELFAUTOPARENT}{,{/share,}/texmf{-local,}/web2c}
-$SELFAUTOLOC : /usr/tex/bin/platform
-$SELFAUTODIR : /usr/tex/bin
-$SELFAUTOPARENT : /usr/tex
-</typing>
+package.obsolete = package.obsolete or { }
-<p>How about just forgetting about them?</p>
---ldx]]--
+package.append_libpath = appendtolibpath -- will become obsolete
+package.prepend_libpath = prependtolibpath -- will become obsolete
-local suffixes = resolvers.suffixes
-local formats = resolvers.formats
-
-suffixes['gf'] = { '<resolution>gf' }
-suffixes['pk'] = { '<resolution>pk' }
-suffixes['base'] = { 'base' }
-suffixes['bib'] = { 'bib' }
-suffixes['bst'] = { 'bst' }
-suffixes['cnf'] = { 'cnf' }
-suffixes['mem'] = { 'mem' }
-suffixes['mf'] = { 'mf' }
-suffixes['mfpool'] = { 'pool' }
-suffixes['mft'] = { 'mft' }
-suffixes['mppool'] = { 'pool' }
-suffixes['graphic/figure'] = { 'eps', 'epsi' }
-suffixes['texpool'] = { 'pool' }
-suffixes['PostScript header'] = { 'pro' }
-suffixes['ist'] = { 'ist' }
-suffixes['web'] = { 'web', 'ch' }
-suffixes['cweb'] = { 'w', 'web', 'ch' }
-suffixes['cmap files'] = { 'cmap' }
-suffixes['lig files'] = { 'lig' }
-suffixes['bitmap font'] = { }
-suffixes['MetaPost support'] = { }
-suffixes['TeX system documentation'] = { }
-suffixes['TeX system sources'] = { }
-suffixes['dvips config'] = { }
-suffixes['type42 fonts'] = { }
-suffixes['web2c files'] = { }
-suffixes['other text files'] = { }
-suffixes['other binary files'] = { }
-suffixes['opentype fonts'] = { 'otf' }
-
-suffixes['fmt'] = { 'fmt' }
-suffixes['texmfscripts'] = { 'rb','lua','py','pl' }
-
-suffixes['pdftex config'] = { }
-suffixes['Troff fonts'] = { }
-
-suffixes['ls-R'] = { }
-
---[[ldx--
-<p>If you wondered abou tsome of the previous mappings, how about
-the next bunch:</p>
---ldx]]--
-
-formats['bib'] = ''
-formats['bst'] = ''
-formats['mft'] = ''
-formats['ist'] = ''
-formats['web'] = ''
-formats['cweb'] = ''
-formats['MetaPost support'] = ''
-formats['TeX system documentation'] = ''
-formats['TeX system sources'] = ''
-formats['Troff fonts'] = ''
-formats['dvips config'] = ''
-formats['graphic/figure'] = ''
-formats['ls-R'] = ''
-formats['other text files'] = ''
-formats['other binary files'] = ''
-
-formats['gf'] = ''
-formats['pk'] = ''
-formats['base'] = 'MFBASES'
-formats['cnf'] = ''
-formats['mem'] = 'MPMEMS'
-formats['mf'] = 'MFINPUTS'
-formats['mfpool'] = 'MFPOOL'
-formats['mppool'] = 'MPPOOL'
-formats['texpool'] = 'TEXPOOL'
-formats['PostScript header'] = 'TEXPSHEADERS'
-formats['cmap files'] = 'CMAPFONTS'
-formats['type42 fonts'] = 'T42FONTS'
-formats['web2c files'] = 'WEB2C'
-formats['pdftex config'] = 'PDFTEXCONFIG'
-formats['texmfscripts'] = 'TEXMFSCRIPTS'
-formats['bitmap font'] = ''
-formats['lig files'] = 'LIGFONTS'
+package.obsolete.append_libpath = appendtolibpath -- will become obsolete
+package.obsolete.prepend_libpath = prependtolibpath -- will become obsolete
end -- of closure
@@ -11547,49 +13660,54 @@ if not modules then modules = { } end modules ['data-aux'] = {
}
local find = string.find
+local type, next = type, next
local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
-function resolvers.update_script(oldname,newname) -- oldname -> own.name, not per se a suffix
+local resolvers = resolvers
+
+local report_scripts = logs.reporter("resolvers","scripts")
+
+function resolvers.updatescript(oldname,newname) -- oldname -> own.name, not per se a suffix
local scriptpath = "scripts/context/lua"
newname = file.addsuffix(newname,"lua")
- local oldscript = resolvers.clean_path(oldname)
+ local oldscript = resolvers.cleanpath(oldname)
if trace_locating then
- logs.report("fileio","to be replaced old script %s", oldscript)
+ report_scripts("to be replaced old script %s", oldscript)
end
- local newscripts = resolvers.find_files(newname) or { }
+ local newscripts = resolvers.findfiles(newname) or { }
if #newscripts == 0 then
if trace_locating then
- logs.report("fileio","unable to locate new script")
+ report_scripts("unable to locate new script")
end
else
for i=1,#newscripts do
- local newscript = resolvers.clean_path(newscripts[i])
+ local newscript = resolvers.cleanpath(newscripts[i])
if trace_locating then
- logs.report("fileio","checking new script %s", newscript)
+ report_scripts("checking new script %s", newscript)
end
if oldscript == newscript then
if trace_locating then
- logs.report("fileio","old and new script are the same")
+ report_scripts("old and new script are the same")
end
elseif not find(newscript,scriptpath) then
if trace_locating then
- logs.report("fileio","new script should come from %s",scriptpath)
+ report_scripts("new script should come from %s",scriptpath)
end
elseif not (find(oldscript,file.removesuffix(newname).."$") or find(oldscript,newname.."$")) then
if trace_locating then
- logs.report("fileio","invalid new script name")
+ report_scripts("invalid new script name")
end
else
local newdata = io.loaddata(newscript)
if newdata then
if trace_locating then
- logs.report("fileio","old script content replaced by new content")
+ report_scripts("old script content replaced by new content")
end
io.savedata(oldscript,newdata)
break
elseif trace_locating then
- logs.report("fileio","unable to load new script")
+ report_scripts("unable to load new script")
end
end
end
@@ -11609,72 +13727,133 @@ if not modules then modules = { } end modules ['data-tmf'] = {
license = "see context related readme files"
}
-local find, gsub, match = string.find, string.gsub, string.match
-local getenv, setenv = os.getenv, os.setenv
+local resolvers = resolvers
--- loads *.tmf files in minimal tree roots (to be optimized and documented)
+local report_tds = logs.reporter("resolvers","tds")
-function resolvers.check_environment(tree)
- logs.simpleline()
- setenv('TMP', getenv('TMP') or getenv('TEMP') or getenv('TMPDIR') or getenv('HOME'))
- setenv('TEXOS', getenv('TEXOS') or ("texmf-" .. os.platform))
- setenv('TEXPATH', gsub(tree or "tex","\/+$",''))
- setenv('TEXMFOS', getenv('TEXPATH') .. "/" .. getenv('TEXOS'))
- logs.simpleline()
- logs.simple("preset : TEXPATH => %s", getenv('TEXPATH'))
- logs.simple("preset : TEXOS => %s", getenv('TEXOS'))
- logs.simple("preset : TEXMFOS => %s", getenv('TEXMFOS'))
- logs.simple("preset : TMP => %s", getenv('TMP'))
- logs.simple('')
-end
+-- = <<
+-- ? ??
+-- < +=
+-- > =+
-function resolvers.load_environment(name) -- todo: key=value as well as lua
- local f = io.open(name)
- if f then
- for line in f:lines() do
- if find(line,"^[%%%#]") then
- -- skip comment
- else
- local key, how, value = match(line,"^(.-)%s*([<=>%?]+)%s*(.*)%s*$")
- if how then
- value = gsub(value,"%%(.-)%%", function(v) return getenv(v) or "" end)
- if how == "=" or how == "<<" then
- setenv(key,value)
- elseif how == "?" or how == "??" then
- setenv(key,getenv(key) or value)
- elseif how == "<" or how == "+=" then
- if getenv(key) then
- setenv(key,getenv(key) .. io.fileseparator .. value)
- else
- setenv(key,value)
- end
- elseif how == ">" or how == "=+" then
- if getenv(key) then
- setenv(key,value .. io.pathseparator .. getenv(key))
- else
- setenv(key,value)
- end
- end
- end
- end
+function resolvers.load_tree(tree,resolve)
+ if type(tree) == "string" and tree ~= "" then
+
+ local getenv, setenv = resolvers.getenv, resolvers.setenv
+
+ -- later might listen to the raw osenv var as well
+ local texos = "texmf-" .. os.platform
+
+ local oldroot = environment.texroot
+ local newroot = file.collapsepath(tree)
+
+ local newtree = file.join(newroot,texos)
+ local newpath = file.join(newtree,"bin")
+
+ if not lfs.isdir(newtree) then
+ report_tds("no '%s' under tree %s",texos,tree)
+ os.exit()
end
- f:close()
+ if not lfs.isdir(newpath) then
+ report_tds("no '%s/bin' under tree %s",texos,tree)
+ os.exit()
+ end
+
+ local texmfos = newtree
+
+ environment.texroot = newroot
+ environment.texos = texos
+ environment.texmfos = texmfos
+
+ -- Beware, we need to obey the relocatable autoparent so we
+ -- set TEXMFCNF to its raw value. This is somewhat tricky when
+ -- we run a mkii job from within. Therefore, in mtxrun, there
+ -- is a resolve applied when we're in mkii/kpse mode.
+
+ setenv('SELFAUTOPARENT', newroot)
+ setenv('SELFAUTODIR', newtree)
+ setenv('SELFAUTOLOC', newpath)
+ setenv('TEXROOT', newroot)
+ setenv('TEXOS', texos)
+ setenv('TEXMFOS', texmfos)
+ setenv('TEXMFCNF', resolvers.luacnfspec, not resolve)
+ setenv("PATH", newpath .. io.pathseparator .. getenv("PATH"))
+
+ report_tds("changing from root '%s' to '%s'",oldroot,newroot)
+ report_tds("prepending '%s' to binary path",newpath)
+ report_tds()
end
end
-function resolvers.load_tree(tree)
- if tree and tree ~= "" then
- local setuptex = 'setuptex.tmf'
- if lfs.attributes(tree, "mode") == "directory" then -- check if not nil
- setuptex = tree .. "/" .. setuptex
- else
- setuptex = tree
+
+end -- of closure
+
+do -- create closure to overcome 200 locals limit
+
+if not modules then modules = { } end modules ['data-lst'] = {
+ version = 1.001,
+ comment = "companion to luat-lib.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- used in mtxrun, can be loaded later .. todo
+
+local find, concat, upper, format = string.find, table.concat, string.upper, string.format
+
+resolvers.listers = resolvers.listers or { }
+
+local resolvers = resolvers
+
+local report_lists = logs.reporter("resolvers","lists")
+
+local function tabstr(str)
+ if type(str) == 'table' then
+ return concat(str," | ")
+ else
+ return str
+ end
+end
+
+function resolvers.listers.variables(pattern)
+ local instance = resolvers.instance
+ local environment = instance.environment
+ local variables = instance.variables
+ local expansions = instance.expansions
+ local pattern = upper(pattern or "")
+ local configured = { }
+ local order = instance.order
+ for i=1,#order do
+ for k, v in next, order[i] do
+ if v ~= nil and configured[k] == nil then
+ configured[k] = v
+ end
end
- if io.exists(setuptex) then
- resolvers.check_environment(tree)
- resolvers.load_environment(setuptex)
+ end
+ local env = table.fastcopy(environment)
+ local var = table.fastcopy(variables)
+ local exp = table.fastcopy(expansions)
+ for key, value in table.sortedpairs(configured) do
+ if key ~= "" and (pattern == "" or find(upper(key),pattern)) then
+ report_lists(key)
+ report_lists(" env: %s",tabstr(rawget(environment,key)) or "unset")
+ report_lists(" var: %s",tabstr(configured[key]) or "unset")
+ report_lists(" exp: %s",tabstr(expansions[key]) or "unset")
+ report_lists(" res: %s",resolvers.resolve(expansions[key]) or "unset")
end
end
+ instance.environment = table.fastcopy(env)
+ instance.variables = table.fastcopy(var)
+ instance.expansions = table.fastcopy(exp)
+end
+
+function resolvers.listers.configurations(report)
+ local configurations = resolvers.instance.specification
+ local report = report or texio.write_nl
+ for i=1,#configurations do
+ report(resolvers.resolve(configurations[i]))
+ end
end
@@ -11694,9 +13873,15 @@ if not modules then modules = { } end modules ['luat-sta'] = {
local gmatch, match = string.gmatch, string.match
local type = type
-states = states or { }
-states.data = states.data or { }
-states.hash = states.hash or { }
+states = states or { }
+local states = states
+
+states.data = states.data or { }
+local data = states.data
+
+states.hash = states.hash or { }
+local hash = states.hash
+
states.tag = states.tag or ""
states.filename = states.filename or ""
@@ -11706,7 +13891,7 @@ function states.save(filename,tag)
io.savedata(filename,
"-- generator : luat-sta.lua\n" ..
"-- state tag : " .. tag .. "\n\n" ..
- table.serialize(states.data[tag or states.tag] or {},true)
+ table.serialize(data[tag or states.tag] or {},true)
)
end
@@ -11714,11 +13899,11 @@ function states.load(filename,tag)
states.filename = filename
states.tag = tag or "whatever"
states.filename = file.addsuffix(states.filename,'lus')
- states.data[states.tag], states.hash[states.tag] = (io.exists(filename) and dofile(filename)) or { }, { }
+ data[states.tag], hash[states.tag] = (io.exists(filename) and dofile(filename)) or { }, { }
end
-function states.set_by_tag(tag,key,value,default,persistent)
- local d, h = states.data[tag], states.hash[tag]
+local function set_by_tag(tag,key,value,default,persistent)
+ local d, h = data[tag], hash[tag]
if d then
if type(d) == "table" then
local dkey, hkey = key, key
@@ -11738,8 +13923,10 @@ function states.set_by_tag(tag,key,value,default,persistent)
end
dkey, hkey = post, key
end
- if type(value) == nil then
- value = value or default
+ if value == nil then
+ value = default
+ elseif value == false then
+ -- special case
elseif persistent then
value = value or d[dkey] or default
else
@@ -11748,144 +13935,202 @@ function states.set_by_tag(tag,key,value,default,persistent)
d[dkey], h[hkey] = value, value
elseif type(d) == "string" then
-- weird
- states.data[tag], states.hash[tag] = value, value
+ data[tag], hash[tag] = value, value
end
end
end
-function states.get_by_tag(tag,key,default)
- local h = states.hash[tag]
+local function get_by_tag(tag,key,default)
+ local h = hash[tag]
if h and h[key] then
return h[key]
else
- local d = states.data[tag]
+ local d = data[tag]
if d then
for k in gmatch(key,"[^%.]+") do
local dk = d[k]
- if dk then
+ if dk ~= nil then
d = dk
else
return default
end
end
- return d or default
+ if d == false then
+ return false
+ else
+ return d or default
+ end
end
end
end
+states.set_by_tag = set_by_tag
+states.get_by_tag = get_by_tag
+
function states.set(key,value,default,persistent)
- states.set_by_tag(states.tag,key,value,default,persistent)
+ set_by_tag(states.tag,key,value,default,persistent)
end
function states.get(key,default)
- return states.get_by_tag(states.tag,key,default)
-end
-
---~ states.data.update = {
---~ ["version"] = {
---~ ["major"] = 0,
---~ ["minor"] = 1,
---~ },
---~ ["rsync"] = {
---~ ["server"] = "contextgarden.net",
---~ ["module"] = "minimals",
---~ ["repository"] = "current",
---~ ["flags"] = "-rpztlv --stats",
---~ },
---~ ["tasks"] = {
---~ ["update"] = true,
---~ ["make"] = true,
---~ ["delete"] = false,
---~ },
---~ ["platform"] = {
---~ ["host"] = true,
---~ ["other"] = {
---~ ["mswin"] = false,
---~ ["linux"] = false,
---~ ["linux-64"] = false,
---~ ["osx-intel"] = false,
---~ ["osx-ppc"] = false,
---~ ["sun"] = false,
---~ },
---~ },
---~ ["context"] = {
---~ ["available"] = {"current", "beta", "alpha", "experimental"},
---~ ["selected"] = "current",
---~ },
---~ ["formats"] = {
---~ ["cont-en"] = true,
---~ ["cont-nl"] = true,
---~ ["cont-de"] = false,
---~ ["cont-cz"] = false,
---~ ["cont-fr"] = false,
---~ ["cont-ro"] = false,
---~ },
---~ ["engine"] = {
---~ ["pdftex"] = {
---~ ["install"] = true,
---~ ["formats"] = {
---~ ["pdftex"] = true,
---~ },
---~ },
---~ ["luatex"] = {
---~ ["install"] = true,
---~ ["formats"] = {
---~ },
---~ },
---~ ["xetex"] = {
---~ ["install"] = true,
---~ ["formats"] = {
---~ ["xetex"] = false,
---~ },
---~ },
---~ ["metapost"] = {
---~ ["install"] = true,
---~ ["formats"] = {
---~ ["mpost"] = true,
---~ ["metafun"] = true,
---~ },
---~ },
---~ },
---~ ["fonts"] = {
---~ },
---~ ["doc"] = {
---~ },
---~ ["modules"] = {
---~ ["f-urwgaramond"] = false,
---~ ["f-urwgothic"] = false,
---~ ["t-bnf"] = false,
---~ ["t-chromato"] = false,
---~ ["t-cmscbf"] = false,
---~ ["t-cmttbf"] = false,
---~ ["t-construction-plan"] = false,
---~ ["t-degrade"] = false,
---~ ["t-french"] = false,
---~ ["t-lettrine"] = false,
---~ ["t-lilypond"] = false,
---~ ["t-mathsets"] = false,
---~ ["t-tikz"] = false,
---~ ["t-typearea"] = false,
---~ ["t-vim"] = false,
---~ },
---~ }
-
---~ states.save("teststate", "update")
---~ states.load("teststate", "update")
-
---~ print(states.get_by_tag("update","rsync.server","unknown"))
---~ states.set_by_tag("update","rsync.server","oeps")
---~ print(states.get_by_tag("update","rsync.server","unknown"))
---~ states.save("teststate", "update")
---~ states.load("teststate", "update")
---~ print(states.get_by_tag("update","rsync.server","unknown"))
+ return get_by_tag(states.tag,key,default)
+end
+
+
+
+
+
+end -- of closure
+
+do -- create closure to overcome 200 locals limit
+
+if not modules then modules = { } end modules ['luat-fmt'] = {
+ version = 1.001,
+ comment = "companion to mtxrun",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+
+local format = string.format
+
+local report_format = logs.reporter("resolvers","formats")
+
+-- helper for mtxrun
+
+local quoted = string.quoted
+
+local function primaryflags() -- not yet ok
+ local trackers = environment.argument("trackers")
+ local directives = environment.argument("directives")
+ local flags = ""
+ if trackers and trackers ~= "" then
+ flags = flags .. "--trackers=" .. quoted(trackers)
+ end
+ if directives and directives ~= "" then
+ flags = flags .. "--directives=" .. quoted(directives)
+ end
+ return flags
+end
+
+function environment.make_format(name)
+ -- change to format path (early as we need expanded paths)
+ local olddir = lfs.currentdir()
+ local path = caches.getwritablepath("formats") or "" -- maybe platform
+ if path ~= "" then
+ lfs.chdir(path)
+ end
+ report_format("format path: %s",lfs.currentdir())
+ -- check source file
+ local texsourcename = file.addsuffix(name,"mkiv")
+ local fulltexsourcename = resolvers.findfile(texsourcename,"tex") or ""
+ if fulltexsourcename == "" then
+ texsourcename = file.addsuffix(name,"tex")
+ fulltexsourcename = resolvers.findfile(texsourcename,"tex") or ""
+ end
+ if fulltexsourcename == "" then
+ report_format("no tex source file with name: %s (mkiv or tex)",name)
+ lfs.chdir(olddir)
+ return
+ else
+ report_format("using tex source file: %s",fulltexsourcename)
+ end
+ local texsourcepath = dir.expandname(file.dirname(fulltexsourcename)) -- really needed
+ -- check specification
+ local specificationname = file.replacesuffix(fulltexsourcename,"lus")
+ local fullspecificationname = resolvers.findfile(specificationname,"tex") or ""
+ if fullspecificationname == "" then
+ specificationname = file.join(texsourcepath,"context.lus")
+ fullspecificationname = resolvers.findfile(specificationname,"tex") or ""
+ end
+ if fullspecificationname == "" then
+ report_format("unknown stub specification: %s",specificationname)
+ lfs.chdir(olddir)
+ return
+ end
+ local specificationpath = file.dirname(fullspecificationname)
+ -- load specification
+ local usedluastub = nil
+ local usedlualibs = dofile(fullspecificationname)
+ if type(usedlualibs) == "string" then
+ usedluastub = file.join(file.dirname(fullspecificationname),usedlualibs)
+ elseif type(usedlualibs) == "table" then
+ report_format("using stub specification: %s",fullspecificationname)
+ local texbasename = file.basename(name)
+ local luastubname = file.addsuffix(texbasename,"lua")
+ local lucstubname = file.addsuffix(texbasename,"luc")
+ -- pack libraries in stub
+ report_format("creating initialization file: %s",luastubname)
+ utilities.merger.selfcreate(usedlualibs,specificationpath,luastubname)
+ -- compile stub file (does not save that much as we don't use this stub at startup any more)
+ local strip = resolvers.booleanvariable("LUACSTRIP", true)
+ if utilities.lua.compile(luastubname,lucstubname) and lfs.isfile(lucstubname) then
+ report_format("using compiled initialization file: %s",lucstubname)
+ usedluastub = lucstubname
+ else
+ report_format("using uncompiled initialization file: %s",luastubname)
+ usedluastub = luastubname
+ end
+ else
+ report_format("invalid stub specification: %s",fullspecificationname)
+ lfs.chdir(olddir)
+ return
+ end
+ -- generate format
+ local command = format("luatex --ini %s --lua=%s %s %sdump",primaryflags(),quoted(usedluastub),quoted(fulltexsourcename),os.platform == "unix" and "\\\\" or "\\")
+ report_format("running command: %s\n",command)
+ os.spawn(command)
+ -- remove related mem files
+ local pattern = file.removesuffix(file.basename(usedluastub)).."-*.mem"
+ -- report_format("removing related mplib format with pattern '%s'", pattern)
+ local mp = dir.glob(pattern)
+ if mp then
+ for i=1,#mp do
+ local name = mp[i]
+ report_format("removing related mplib format %s", file.basename(name))
+ os.remove(name)
+ end
+ end
+ lfs.chdir(olddir)
+end
+
+function environment.run_format(name,data,more)
+ -- hm, rather old code here; we can now use the file.whatever functions
+ if name and name ~= "" then
+ local barename = file.removesuffix(name)
+ local fmtname = caches.getfirstreadablefile(file.addsuffix(barename,"fmt"),"formats")
+ if fmtname == "" then
+ fmtname = resolvers.findfile(file.addsuffix(barename,"fmt")) or ""
+ end
+ fmtname = resolvers.cleanpath(fmtname)
+ if fmtname == "" then
+ report_format("no format with name: %s",name)
+ else
+ local barename = file.removesuffix(name) -- expanded name
+ local luaname = file.addsuffix(barename,"luc")
+ if not lfs.isfile(luaname) then
+ luaname = file.addsuffix(barename,"lua")
+ end
+ if not lfs.isfile(luaname) then
+ report_format("using format name: %s",fmtname)
+ report_format("no luc/lua with name: %s",barename)
+ else
+ local command = format("luatex %s --fmt=%s --lua=%s %s %s",primaryflags(),quoted(barename),quoted(luaname),quoted(data),more ~= "" and quoted(more) or "")
+ report_format("running command: %s",command)
+ os.spawn(command)
+ end
+ end
+ end
+end
end -- of closure
-- end library merge
-own = { } -- not local
+own = { } -- not local, might change
+
+own.libs = { -- order can be made better
-own.libs = { -- todo: check which ones are really needed
'l-string.lua',
'l-lpeg.lua',
'l-table.lua',
@@ -11898,39 +14143,55 @@ own.libs = { -- todo: check which ones are really needed
'l-url.lua',
'l-dir.lua',
'l-boolean.lua',
+ 'l-unicode.lua',
'l-math.lua',
--- 'l-unicode.lua',
--- 'l-tex.lua',
- 'l-utils.lua',
- 'l-aux.lua',
--- 'l-xml.lua',
- 'trac-tra.lua',
+
+ 'util-tab.lua',
+ 'util-sto.lua',
+ 'util-mrg.lua',
+ 'util-lua.lua',
+ 'util-prs.lua',
+ 'util-fmt.lua',
+ 'util-deb.lua',
+
+ 'trac-inf.lua',
+ 'trac-set.lua',
+ 'trac-log.lua',
+ 'trac-pro.lua',
+
+ 'luat-env.lua', -- can come before inf (as in mkiv)
+
'lxml-tab.lua',
'lxml-lpt.lua',
--- 'lxml-ent.lua',
+ -- 'lxml-ent.lua',
'lxml-mis.lua',
'lxml-aux.lua',
'lxml-xml.lua',
- 'luat-env.lua',
- 'trac-inf.lua',
- 'trac-log.lua',
- 'data-res.lua',
+
+ 'data-ini.lua',
+ 'data-exp.lua',
+ 'data-env.lua',
'data-tmp.lua',
+ 'data-met.lua',
+ 'data-res.lua',
'data-pre.lua',
'data-inp.lua',
'data-out.lua',
+ 'data-fil.lua',
'data-con.lua',
'data-use.lua',
-- 'data-tex.lua',
-- 'data-bin.lua',
'data-zip.lua',
+ 'data-tre.lua',
'data-crl.lua',
'data-lua.lua',
- 'data-kps.lua', -- so that we can replace kpsewhich
'data-aux.lua', -- updater
- 'data-tmf.lua', -- tree files
- -- needed ?
- 'luat-sta.lua', -- states
+ 'data-tmf.lua',
+ 'data-lst.lua',
+
+ 'luat-sta.lua',
+ 'luat-fmt.lua',
}
-- We need this hack till luatex is fixed.
@@ -11943,35 +14204,62 @@ end
-- End of hack.
-own.name = (environment and environment.ownname) or arg[0] or 'luatools.lua'
+local format, gsub, gmatch, match = string.format, string.gsub, string.gmatch, string.match
+local concat = table.concat
+own.name = (environment and environment.ownname) or arg[0] or 'mtxrun.lua'
+own.path = gsub(match(own.name,"^(.+)[\\/].-$") or ".","\\","/")
+
+local ownpath, owntree = own.path, environment and environment.ownpath or own.path
+
+own.list = {
+ '.',
+ ownpath ,
+ ownpath .. "/../sources", -- HH's development path
+ owntree .. "/../../texmf-local/tex/context/base",
+ owntree .. "/../../texmf-context/tex/context/base",
+ owntree .. "/../../texmf-dist/tex/context/base",
+ owntree .. "/../../texmf/tex/context/base",
+ owntree .. "/../../../texmf-local/tex/context/base",
+ owntree .. "/../../../texmf-context/tex/context/base",
+ owntree .. "/../../../texmf-dist/tex/context/base",
+ owntree .. "/../../../texmf/tex/context/base",
+}
-own.path = string.match(own.name,"^(.+)[\\/].-$") or "."
-own.list = { '.' }
-if own.path ~= '.' then
- table.insert(own.list,own.path)
-end
-table.insert(own.list,own.path.."/../../texmf-dist/tex/context/base")
-table.insert(own.list,own.path.."/../../../tex/context/base")
-table.insert(own.list,own.path.."/mtx")
-table.insert(own.list,own.path.."/../sources")
+if own.path == "." then table.remove(own.list,1) end
local function locate_libs()
- for _, lib in pairs(own.libs) do
- for _, pth in pairs(own.list) do
- local filename = string.gsub(pth .. "/" .. lib,"\\","/")
+ for l=1,#own.libs do
+ local lib = own.libs[l]
+ for p =1,#own.list do
+ local pth = own.list[p]
+ local filename = pth .. "/" .. lib
+ local found = lfs.isfile(filename)
+ if found then
+ package.path = package.path .. ";" .. pth .. "/?.lua" -- in case l-* does a require
+ return pth
+ end
+ end
+ end
+end
+
+local function load_libs()
+ local found = locate_libs()
+ if found then
+ for l=1,#own.libs do
+ local filename = found .. "/" .. own.libs[l]
local codeblob = loadfile(filename)
if codeblob then
codeblob()
- own.list = { pth } -- speed up te search
- break
end
end
+ else
+ resolvers = nil
end
end
if not resolvers then
- locate_libs()
+ load_libs()
end
if not resolvers then
@@ -11983,20 +14271,56 @@ if not resolvers then
os.exit()
end
-logs.setprogram('MTXrun',"TDS Runner Tool 1.24",environment.arguments["verbose"] or false)
+-- verbosity
-local instance = resolvers.reset()
+local e_verbose = environment.arguments["verbose"]
-local trackspec = environment.argument("trackers") or environment.argument("track")
+if e_verbose then
+ trackers.enable("resolvers.locating")
+end
+
+-- some common flags (also passed through environment)
+
+local e_silent = environment.argument("silent")
+local e_noconsole = environment.argument("noconsole")
+
+local e_trackers = environment.argument("trackers")
+local e_directives = environment.argument("directives")
+local e_experiments = environment.argument("experiments")
+
+if e_silent == true then
+ e_silent = "*"
+end
+
+if type(e_silent) == "string" then
+ if type(e_directives) == "string" then
+ e_directives = format("%s,logs.blocked={%s}",e_directives,e_silent)
+ else
+ e_directives = format("logs.blocked={%s}",e_silent)
+ end
+end
-if trackspec then
- trackers.enable(trackspec)
+if e_noconsole then
+ if type(e_directives) == "string" then
+ e_directives = format("%s,logs.target=file",e_directives)
+ else
+ e_directives = format("logs.target=file")
+ end
end
-runners = runners or { } -- global
-messages = messages or { }
+if e_trackers then trackers .enable(e_trackers) end
+if e_directives then directives .enable(e_directives) end
+if e_experiments then experiments.enable(e_experiments) end
-messages.help = [[
+if not environment.trackers then environment.trackers = e_trackers end
+if not environment.directives then environment.directives = e_directives end
+if not environment.experiments then environment.experiments = e_experiments end
+
+--
+
+local instance = resolvers.reset()
+
+local helpinfo = [[
--script run an mtx script (lua prefered method) (--noquotes), no script gives list
--execute run a script or program (texmfstart method) (--noquotes)
--resolve resolve prefixed arguments
@@ -12011,15 +14335,14 @@ messages.help = [[
--ifchanged=filename only execute when given file has changed (md checksum)
--iftouched=old,new only execute when given file has changed (time stamp)
---make create stubs for (context related) scripts
---remove remove stubs (context related) scripts
+--makestubs create stubs for (context related) scripts
+--removestubs remove stubs (context related) scripts
--stubpath=binpath paths where stubs wil be written
--windows create windows (mswin) stubs
--unix create unix (linux) stubs
--verbose give a bit more info
--trackers=list enable given trackers
---engine=str target engine
--progname=str format or backend
--edit launch editor with found file
@@ -12032,8 +14355,35 @@ messages.help = [[
--forcekpse force using kpse (handy when no mkiv and cache installed but less functionality)
--prefixes show supported prefixes
+
+--generate generate file database
+
+--variables show configuration variables
+--configurations show configuration order
+
+--expand-braces expand complex variable
+--expand-path expand variable (resolve paths)
+--expand-var expand variable (resolve references)
+--show-path show path expansion of ...
+--var-value report value of variable
+--find-file report file location
+--find-path report path of file
+
+--pattern=str filter variables
]]
+local application = logs.application {
+ name = "mtxrun",
+ banner = "ConTeXt TDS Runner Tool 1.30",
+ helpinfo = helpinfo,
+}
+
+local report = application.report
+
+messages = messages or { } -- for the moment
+
+runners = runners or { } -- global (might become local)
+
runners.applications = {
["lua"] = "luatex --luaonly",
["luc"] = "luatex --luaonly",
@@ -12056,7 +14406,7 @@ runners.registered = {
makempy = { 'makempy.pl', true },
mptopdf = { 'mptopdf.pl', true },
pstopdf = { 'pstopdf.rb', true }, -- converts ps (and some more) images, does some cleaning (replaced)
--- examplex = { 'examplex.rb', false },
+ -- examplex = { 'examplex.rb', false },
concheck = { 'concheck.rb', false },
runtools = { 'runtools.rb', true },
textools = { 'textools.rb', true },
@@ -12065,9 +14415,9 @@ runners.registered = {
rlxtools = { 'rlxtools.rb', true },
pdftools = { 'pdftools.rb', true },
mpstools = { 'mpstools.rb', true },
--- exatools = { 'exatools.rb', true },
+ -- exatools = { 'exatools.rb', true },
xmltools = { 'xmltools.rb', true },
--- luatools = { 'luatools.lua', true },
+ -- luatools = { 'luatools.lua', true },
mtxtools = { 'mtxtools.rb', true },
pdftrimwhite = { 'pdftrimwhite.pl', false }
}
@@ -12086,45 +14436,39 @@ end
function runners.prepare()
local checkname = environment.argument("ifchanged")
- if checkname and checkname ~= "" then
+ if type(checkname) == "string" and checkname ~= "" then
local oldchecksum = file.loadchecksum(checkname)
local newchecksum = file.checksum(checkname)
if oldchecksum == newchecksum then
- logs.simple("file '%s' is unchanged",checkname)
+ if e_verbose then
+ report("file '%s' is unchanged",checkname)
+ end
return "skip"
- else
- logs.simple("file '%s' is changed, processing started",checkname)
+ elseif e_verbose then
+ report("file '%s' is changed, processing started",checkname)
end
file.savechecksum(checkname)
end
- local oldname, newname = string.split(environment.argument("iftouched") or "", ",")
- if oldname and newname and oldname ~= "" and newname ~= "" then
- if not file.needs_updating(oldname,newname) then
- logs.simple("file '%s' and '%s' have same age",oldname,newname)
- return "skip"
- else
- logs.simple("file '%s' is older than '%s'",oldname,newname)
- end
- end
- local tree = environment.argument('tree') or ""
- if environment.argument('autotree') then
- tree = os.getenv('TEXMFSTART_TREE') or os.getenv('TEXMFSTARTTREE') or tree
- end
- if tree and tree ~= "" then
- resolvers.load_tree(tree)
- end
- local env = environment.argument('environment') or ""
- if env and env ~= "" then
- for _,e in pairs(string.split(env)) do
- -- maybe force suffix when not given
- resolvers.load_tree(e)
+ local touchname = environment.argument("iftouched")
+ if type(touchname) == "string" and touchname ~= "" then
+ local oldname, newname = string.split(touchname, ",")
+ if oldname and newname and oldname ~= "" and newname ~= "" then
+ if not file.needs_updating(oldname,newname) then
+ if e_verbose then
+ report("file '%s' and '%s' have same age",oldname,newname)
+ end
+ return "skip"
+ elseif e_verbose then
+ report("file '%s' is older than '%s'",oldname,newname)
+ end
end
end
local runpath = environment.argument("path")
- if runpath and not lfs.chdir(runpath) then
- logs.simple("unable to change to path '%s'",runpath)
+ if type(runpath) == "string" and not lfs.chdir(runpath) then
+ report("unable to change to path '%s'",runpath)
return "error"
end
+ runners.prepare = function() end
return "run"
end
@@ -12137,8 +14481,6 @@ function runners.execute_script(fullname,internal,nosplit)
elseif state == 'skip' then
return true
elseif state == "run" then
- instance.progname = environment.argument("progname") or instance.progname
- instance.format = environment.argument("format") or instance.format
local path, name, suffix, result = file.dirname(fullname), file.basename(fullname), file.extname(fullname), ""
if path ~= "" then
result = fullname
@@ -12155,21 +14497,21 @@ function runners.execute_script(fullname,internal,nosplit)
if suffix == "" then
-- loop over known suffixes
for _,s in pairs(runners.suffixes) do
- result = resolvers.find_file(name .. "." .. s, 'texmfscripts')
+ result = resolvers.findfile(name .. "." .. s, 'texmfscripts')
if result ~= "" then
break
end
end
elseif runners.applications[suffix] then
- result = resolvers.find_file(name, 'texmfscripts')
+ result = resolvers.findfile(name, 'texmfscripts')
else
-- maybe look on path
- result = resolvers.find_file(name, 'other text files')
+ result = resolvers.findfile(name, 'other text files')
end
end
if result and result ~= "" then
if not no_split then
- local before, after = environment.split_arguments(fullname) -- already done
+ local before, after = environment.splitarguments(fullname) -- already done
environment.arguments_before, environment.arguments_after = before, after
end
if internal then
@@ -12181,12 +14523,12 @@ function runners.execute_script(fullname,internal,nosplit)
if binary and binary ~= "" then
result = binary .. " " .. result
end
- local command = result .. " " .. environment.reconstruct_commandline(environment.arguments_after,noquote)
- if logs.verbose then
- logs.simpleline()
- logs.simple("executing: %s",command)
- logs.simpleline()
- logs.simpleline()
+ local command = result .. " " .. environment.reconstructcommandline(environment.arguments_after,noquote)
+ if e_verbose then
+ report()
+ report("executing: %s",command)
+ report()
+ report()
io.flush()
end
-- no os.exec because otherwise we get the wrong return value
@@ -12196,14 +14538,14 @@ function runners.execute_script(fullname,internal,nosplit)
else
if binary then
binary = file.addsuffix(binary,os.binsuffix)
- for p in string.gmatch(os.getenv("PATH"),"[^"..io.pathseparator.."]+") do
+ for p in gmatch(os.getenv("PATH"),"[^"..io.pathseparator.."]+") do
if lfs.isfile(file.join(p,binary)) then
return false
end
end
- logs.simpleline()
- logs.simple("This script needs '%s' which seems not to be installed.",binary)
- logs.simpleline()
+ report()
+ report("This script needs '%s' which seems not to be installed.",binary)
+ report()
end
return false
end
@@ -12223,14 +14565,15 @@ function runners.execute_program(fullname)
elseif state == 'skip' then
return true
elseif state == "run" then
- local before, after = environment.split_arguments(fullname)
- environment.initialize_arguments(after)
+ local before, after = environment.splitarguments(fullname)
+ for k=1,#after do after[k] = resolvers.resolve(after[k]) end
+ environment.initializearguments(after)
fullname = fullname:gsub("^bin:","")
- local command = fullname .. " " .. (environment.reconstruct_commandline(after or "",noquote) or "")
- logs.simpleline()
- logs.simple("executing: %s",command)
- logs.simpleline()
- logs.simpleline()
+ local command = fullname .. " " .. (environment.reconstructcommandline(after or "",noquote) or "")
+ report()
+ report("executing: %s",command)
+ report()
+ report()
io.flush()
local code = os.exec(command) -- (fullname,unpack(after)) does not work / maybe spawn
return code == 0
@@ -12239,7 +14582,7 @@ function runners.execute_program(fullname)
return false
end
--- the --usekpse flag will fallback on kpse (hm, we can better update mtx-stubs)
+-- the --usekpse flag will fallback (not default) on kpse (hm, we can better update mtx-stubs)
local windows_stub = '@echo off\013\010setlocal\013\010set ownpath=%%~dp0%%\013\010texlua "%%ownpath%%mtxrun.lua" --usekpse --execute %s %%*\013\010endlocal\013\010'
local unix_stub = '#!/bin/sh\010mtxrun --usekpse --execute %s \"$@\"\010'
@@ -12258,22 +14601,22 @@ function runners.handle_stubs(create)
for _,v in pairs(runners.registered) do
local name, doit = v[1], v[2]
if doit then
- local base = string.gsub(file.basename(name), "%.(.-)$", "")
+ local base = gsub(file.basename(name), "%.(.-)$", "")
if create then
if windows then
- io.savedata(file.join(stubpath,base..".bat"),string.format(windows_stub,name))
- logs.simple("windows stub for '%s' created",base)
+ io.savedata(file.join(stubpath,base..".bat"),format(windows_stub,name))
+ report("windows stub for '%s' created",base)
end
if unix then
- io.savedata(file.join(stubpath,base),string.format(unix_stub,name))
- logs.simple("unix stub for '%s' created",base)
+ io.savedata(file.join(stubpath,base),format(unix_stub,name))
+ report("unix stub for '%s' created",base)
end
else
if windows and (os.remove(file.join(stubpath,base..'.bat')) or os.remove(file.join(stubpath,base..'.cmd'))) then
- logs.simple("windows stub for '%s' removed", base)
+ report("windows stub for '%s' removed", base)
end
if unix and (os.remove(file.join(stubpath,base)) or os.remove(file.join(stubpath,base..'.sh'))) then
- logs.simple("unix stub for '%s' removed",base)
+ report("unix stub for '%s' removed",base)
end
end
end
@@ -12289,7 +14632,7 @@ end
function runners.locate_file(filename)
-- differs from texmfstart where locate appends .com .exe .bat ... todo
if filename and filename ~= "" then
- runners.report_location(resolvers.find_given_file(filename))
+ runners.report_location(resolvers.findgivenfile(filename))
end
end
@@ -12298,28 +14641,28 @@ function runners.locate_platform()
end
function runners.report_location(result)
- if logs.verbose then
- logs.simpleline()
+ if e_verbose then
+ reportline()
if result and result ~= "" then
- logs.simple(result)
+ report(result)
else
- logs.simple("not found")
+ report("not found")
end
else
io.write(result)
end
end
-function runners.edit_script(filename) -- we assume that vim is present on most systems
- local editor = os.getenv("MTXRUN_EDITOR") or os.getenv("TEXMFSTART_EDITOR") or os.getenv("EDITOR") or 'vim'
+function runners.edit_script(filename) -- we assume that gvim is present on most systems (todo: also in cnf file)
+ local editor = os.getenv("MTXRUN_EDITOR") or os.getenv("TEXMFSTART_EDITOR") or os.getenv("EDITOR") or 'gvim'
local rest = resolvers.resolve(filename)
if rest ~= "" then
local command = editor .. " " .. rest
- if logs.verbose then
- logs.simpleline()
- logs.simple("starting editor: %s",command)
- logs.simple_line()
- logs.simple_line()
+ if e_verbose then
+ report()
+ report("starting editor: %s",command)
+ report()
+ report()
end
os.launch(command)
end
@@ -12362,33 +14705,33 @@ end
function runners.launch_file(filename)
instance.allresults = true
- logs.setverbose(true)
+ trackers.enable("resolvers.locating")
local pattern = environment.arguments["pattern"]
if not pattern or pattern == "" then
pattern = filename
end
if not pattern or pattern == "" then
- logs.simple("provide name or --pattern=")
+ report("provide name or --pattern=")
else
- local t = resolvers.find_files(pattern)
+ local t = resolvers.findfiles(pattern)
if not t or #t == 0 then
- t = resolvers.find_files("*/" .. pattern)
+ t = resolvers.findfiles("*/" .. pattern)
end
if not t or #t == 0 then
- t = resolvers.find_files("*/" .. pattern .. "*")
+ t = resolvers.findfiles("*/" .. pattern .. "*")
end
if t and #t > 0 then
if environment.arguments["all"] then
for _, v in pairs(t) do
- logs.simple("launching %s", v)
+ report("launching %s", v)
resolvers.launch(v)
end
else
- logs.simple("launching %s", t[1])
+ report("launching %s", t[1])
resolvers.launch(t[1])
end
else
- logs.simple("no match for %s", pattern)
+ report("no match for %s", pattern)
end
end
end
@@ -12421,33 +14764,45 @@ function runners.find_mtx_script(filename)
local mtxprefix = (filename:find("^mtx%-") and "") or "mtx-"
-- context namespace, mtx-<filename>
fullname = mtxprefix .. filename
- fullname = found(fullname) or resolvers.find_file(fullname)
+ fullname = found(fullname) or resolvers.findfile(fullname)
if fullname and fullname ~= "" then
return fullname
end
-- context namespace, mtx-<filename>s
fullname = mtxprefix .. basename .. "s" .. "." .. suffix
- fullname = found(fullname) or resolvers.find_file(fullname)
+ fullname = found(fullname) or resolvers.findfile(fullname)
if fullname and fullname ~= "" then
return fullname
end
-- context namespace, mtx-<filename minus trailing s>
fullname = mtxprefix .. basename:gsub("s$","") .. "." .. suffix
- fullname = found(fullname) or resolvers.find_file(fullname)
+ fullname = found(fullname) or resolvers.findfile(fullname)
if fullname and fullname ~= "" then
return fullname
end
-- context namespace, just <filename>
- fullname = resolvers.find_file(filename)
+ fullname = resolvers.findfile(filename)
return fullname
end
-function runners.execute_ctx_script(filename)
+function runners.register_arguments(...)
+ local arguments = environment.arguments_after
+ local passedon = { ... }
+ for i=#passedon,1,-1 do
+ local pi = passedon[i]
+ if pi then
+ table.insert(arguments,1,pi)
+ end
+ end
+end
+
+function runners.execute_ctx_script(filename,...)
+ runners.register_arguments(...)
local arguments = environment.arguments_after
local fullname = runners.find_mtx_script(filename) or ""
if file.extname(fullname) == "cld" then
-- handy in editors where we force --autopdf
- logs.simple("running cld script: %s",filename)
+ report("running cld script: %s",filename)
table.insert(arguments,1,fullname)
table.insert(arguments,"--autopdf")
fullname = runners.find_mtx_script("context") or ""
@@ -12455,7 +14810,7 @@ function runners.execute_ctx_script(filename)
-- retry after generate but only if --autogenerate
if fullname == "" and environment.argument("autogenerate") then -- might become the default
instance.renewcache = true
- logs.setverbose(true)
+ trackers.enable("resolvers.locating")
resolvers.load()
--
fullname = runners.find_mtx_script(filename) or ""
@@ -12470,7 +14825,7 @@ function runners.execute_ctx_script(filename)
elseif state == "run" then
-- load and save ... kind of undocumented
arg = { } for _,v in pairs(arguments) do arg[#arg+1] = resolvers.resolve(v) end
- environment.initialize_arguments(arg)
+ environment.initializearguments(arg)
local loadname = environment.arguments['load']
if loadname then
if type(loadname) ~= "string" then loadname = file.basename(fullname) end
@@ -12478,8 +14833,8 @@ function runners.execute_ctx_script(filename)
runners.load_script_session(loadname)
end
filename = environment.files[1]
- if logs.verbose then
- logs.simple("using script: %s\n",fullname)
+ if e_verbose then
+ report("using script: %s\n",fullname)
end
environment.ownscript = fullname
dofile(fullname)
@@ -12495,44 +14850,42 @@ function runners.execute_ctx_script(filename)
return true
end
else
- -- logs.setverbose(true)
if filename == "" or filename == "help" then
- local context = resolvers.find_file("mtx-context.lua")
- logs.setverbose(true)
+ local context = resolvers.findfile("mtx-context.lua")
+ trackers.enable("resolvers.locating")
if context ~= "" then
- local result = dir.glob((string.gsub(context,"mtx%-context","mtx-*"))) -- () needed
+ local result = dir.glob((gsub(context,"mtx%-context","mtx-*"))) -- () needed
local valid = { }
table.sort(result)
for i=1,#result do
local scriptname = result[i]
- local scriptbase = string.match(scriptname,".*mtx%-([^%-]-)%.lua")
+ local scriptbase = match(scriptname,".*mtx%-([^%-]-)%.lua")
if scriptbase then
local data = io.loaddata(scriptname)
- local banner, version = string.match(data,"[\n\r]logs%.extendbanner%s*%(%s*[\"\']([^\n\r]+)%s*(%d+%.%d+)")
+ local banner, version = match(data,"[\n\r]logs%.extendbanner%s*%(%s*[\"\']([^\n\r]+)%s*(%d+%.%d+)")
if banner then
valid[#valid+1] = { scriptbase, version, banner }
end
end
end
if #valid > 0 then
- logs.reportbanner()
- logs.reportline()
- logs.simple("no script name given, known scripts:")
- logs.simple()
+ application.identify()
+ report("no script name given, known scripts:")
+ report()
for k=1,#valid do
local v = valid[k]
- logs.simple("%-12s %4s %s",v[1],v[2],v[3])
+ report("%-12s %4s %s",v[1],v[2],v[3])
end
end
else
- logs.simple("no script name given")
+ report("no script name given")
end
else
filename = file.addsuffix(filename,"lua")
if file.is_qualified_path(filename) then
- logs.simple("unknown script '%s'",filename)
+ report("unknown script '%s'",filename)
else
- logs.simple("unknown script '%s' or 'mtx-%s'",filename,filename)
+ report("unknown script '%s' or 'mtx-%s'",filename,filename)
end
end
return false
@@ -12540,9 +14893,9 @@ function runners.execute_ctx_script(filename)
end
function runners.prefixes()
- logs.reportbanner()
- logs.reportline()
- logs.simple(table.concat(resolvers.allprefixes(true)," "))
+ application.identify()
+ report()
+ report(concat(resolvers.allprefixes(true)," "))
end
function runners.timedrun(filename) -- just for me
@@ -12562,13 +14915,11 @@ end
local filename = environment.files[1] or ""
local ok = true
-local before, after = environment.split_arguments(filename)
+local before, after = environment.splitarguments(filename)
environment.arguments_before, environment.arguments_after = before, after
-environment.initialize_arguments(before)
+environment.initializearguments(before)
-instance.engine = environment.argument("engine") or 'luatex'
-instance.progname = environment.argument("progname") or 'context'
-instance.lsrmode = environment.argument("lsr") or false
+instance.lsrmode = environment.argument("lsr") or false
-- maybe the unset has to go to this level
@@ -12576,36 +14927,40 @@ local is_mkii_stub = runners.registered[file.removesuffix(file.basename(filename
if environment.argument("usekpse") or environment.argument("forcekpse") or is_mkii_stub then
+ resolvers.load_tree(environment.argument('tree'),true) -- force resolve of TEXMFCNF
+
os.setenv("engine","")
os.setenv("progname","")
local remapper = {
- otf = "opentype fonts",
- ttf = "truetype fonts",
- ttc = "truetype fonts",
- pfb = "type1 fonts",
+ otf = "opentype fonts",
+ ttf = "truetype fonts",
+ ttc = "truetype fonts",
+ pfb = "type1 fonts",
other = "other text files",
}
+ local progname = environment.argument("progname") or 'context'
+
local function kpse_initialized()
texconfig.kpse_init = true
local t = os.clock()
- local k = kpse.original.new("luatex",instance.progname)
+ local k = kpse.original.new("luatex",progname)
local dummy = k:find_file("mtxrun.lua") -- so that we're initialized
- logs.simple("kpse fallback with progname '%s' initialized in %s seconds",instance.progname,os.clock()-t)
+ report("kpse fallback with progname '%s' initialized in %s seconds",progname,os.clock()-t)
kpse_initialized = function() return k end
return k
end
- local find_file = resolvers.find_file
- local show_path = resolvers.show_path
+ local findfile = resolvers.findfile
+ local showpath = resolvers.showpath
if environment.argument("forcekpse") then
- function resolvers.find_file(name,kind)
- return (kpse_initialized():find_file(resolvers.clean_path(name),(kind ~= "" and (remapper[kind] or kind)) or "tex") or "") or ""
+ function resolvers.findfile(name,kind)
+ return (kpse_initialized():find_file(resolvers.cleanpath(name),(kind ~= "" and (remapper[kind] or kind)) or "tex") or "") or ""
end
- function resolvers.show_path(name)
+ function resolvers.showpath(name)
return (kpse_initialized():show_path(name)) or ""
end
@@ -12613,16 +14968,16 @@ if environment.argument("usekpse") or environment.argument("forcekpse") or is_mk
resolvers.load()
- function resolvers.find_file(name,kind)
- local found = find_file(name,kind) or ""
+ function resolvers.findfile(name,kind)
+ local found = findfile(name,kind) or ""
if found ~= "" then
return found
else
- return (kpse_initialized():find_file(resolvers.clean_path(name),(kind ~= "" and (remapper[kind] or kind)) or "tex") or "") or ""
+ return (kpse_initialized():find_file(resolvers.cleanpath(name),(kind ~= "" and (remapper[kind] or kind)) or "tex") or "") or ""
end
end
- function resolvers.show_path(name)
- local found = show_path(name) or ""
+ function resolvers.showpath(name)
+ local found = showpath(name) or ""
if found ~= "" then
return found
else
@@ -12632,80 +14987,317 @@ if environment.argument("usekpse") or environment.argument("forcekpse") or is_mk
end
+ function runners.loadbase()
+ end
+
else
- resolvers.load()
+ function runners.loadbase(...)
+ if not resolvers.load(...) then
+ report("forcing cache reload")
+ instance.renewcache = true
+ trackers.enable("resolvers.locating")
+ if not resolvers.load(...) then
+ report("the resolver databases are not present or outdated")
+ end
+ end
+ end
+
+ resolvers.load_tree(environment.argument('tree'))
end
+
if environment.argument("selfmerge") then
+
-- embed used libraries
- utils.merger.selfmerge(own.name,own.libs,own.list)
+
+ runners.loadbase()
+ local found = locate_libs()
+ if found then
+ utilities.merger.selfmerge(own.name,own.libs,{ found })
+ end
+
elseif environment.argument("selfclean") then
+
-- remove embedded libraries
- utils.merger.selfclean(own.name)
+
+ runners.loadbase()
+ utilities.merger.selfclean(own.name)
+
elseif environment.argument("selfupdate") then
- logs.setverbose(true)
- resolvers.update_script(own.name,"mtxrun")
+
+ runners.loadbase()
+ trackers.enable("resolvers.locating")
+ resolvers.updatescript(own.name,"mtxrun")
+
elseif environment.argument("ctxlua") or environment.argument("internal") then
+
-- run a script by loading it (using libs)
+
+ runners.loadbase()
ok = runners.execute_script(filename,true)
+
elseif environment.argument("script") or environment.argument("scripts") then
+
-- run a script by loading it (using libs), pass args
+
+ runners.loadbase()
if is_mkii_stub then
- -- execute mkii script
ok = runners.execute_script(filename,false,true)
else
ok = runners.execute_ctx_script(filename)
end
+
elseif environment.argument("execute") then
+
-- execute script
+
+ runners.loadbase()
ok = runners.execute_script(filename)
+
elseif environment.argument("direct") then
+
-- equals bin:
+
+ runners.loadbase()
ok = runners.execute_program(filename)
+
elseif environment.argument("edit") then
+
-- edit file
+
+ runners.loadbase()
runners.edit_script(filename)
+
elseif environment.argument("launch") then
+
+ runners.loadbase()
runners.launch_file(filename)
-elseif environment.argument("make") then
- -- make stubs
+
+elseif environment.argument("makestubs") then
+
+ -- make stubs (depricated)
+
runners.handle_stubs(true)
-elseif environment.argument("remove") then
- -- remove stub
+
+elseif environment.argument("removestubs") then
+
+ -- remove stub (depricated)
+
+ runners.loadbase()
runners.handle_stubs(false)
+
elseif environment.argument("resolve") then
+
-- resolve string
+
+ runners.loadbase()
runners.resolve_string(filename)
+
elseif environment.argument("locate") then
+
-- locate file
+
+ runners.loadbase()
runners.locate_file(filename)
-elseif environment.argument("platform")then
+
+elseif environment.argument("platform") or environment.argument("show-platform") then
+
-- locate platform
+
+ runners.loadbase()
runners.locate_platform()
+
elseif environment.argument("prefixes") then
+
+ runners.loadbase()
runners.prefixes()
+
elseif environment.argument("timedrun") then
+
-- locate platform
+
+ runners.loadbase()
runners.timedrun(filename)
+
+elseif environment.argument("variables") or environment.argument("show-variables") or environment.argument("expansions") or environment.argument("show-expansions") then
+
+ -- luatools: runners.execute_ctx_script("mtx-base","--expansions",filename)
+
+ resolvers.load("nofiles")
+ resolvers.listers.variables(environment.argument("pattern"))
+
+elseif environment.argument("configurations") or environment.argument("show-configurations") then
+
+ -- luatools: runners.execute_ctx_script("mtx-base","--configurations",filename)
+
+ resolvers.load("nofiles")
+ resolvers.listers.configurations()
+
+elseif environment.argument("find-file") then
+
+ -- luatools: runners.execute_ctx_script("mtx-base","--find-file",filename)
+
+ resolvers.load()
+ local e_pattern = environment.argument("pattern")
+ local e_format = environment.arguments("format")
+ if not e_pattern then
+ runners.register_arguments(filename)
+ environment.initializearguments(environment.arguments_after)
+ resolvers.dowithfilesandreport(resolvers.findfiles,environment.files,e_format)
+ elseif type(e_pattern) == "string" then
+ instance.allresults = true -- brrrr
+ resolvers.dowithfilesandreport(resolvers.findfiles,{ e_pattern }, e_format)
+ end
+
+elseif environment.argument("find-path") then
+
+ -- luatools: runners.execute_ctx_script("mtx-base","--find-path",filename)
+
+ resolvers.load()
+ local path = resolvers.findpath(filename, instance.my_format)
+ if e_verbose then
+ report(path)
+ else
+ print(path)
+ end
+
+elseif environment.argument("expand-braces") then
+
+ -- luatools: runners.execute_ctx_script("mtx-base","--expand-braces",filename)
+
+ resolvers.load("nofiles")
+ runners.register_arguments(filename)
+ environment.initializearguments(environment.arguments_after)
+ resolvers.dowithfilesandreport(resolvers.expandbraces, environment.files)
+
+elseif environment.argument("expand-path") then
+
+ -- luatools: runners.execute_ctx_script("mtx-base","--expand-path",filename)
+
+ resolvers.load("nofiles")
+ runners.register_arguments(filename)
+ environment.initializearguments(environment.arguments_after)
+ resolvers.dowithfilesandreport(resolvers.expandpath, environment.files)
+
+elseif environment.argument("expand-var") or environment.argument("expand-variable") then
+
+ -- luatools: runners.execute_ctx_script("mtx-base","--expand-var",filename)
+
+ resolvers.load("nofiles")
+ runners.register_arguments(filename)
+ environment.initializearguments(environment.arguments_after)
+ resolvers.dowithfilesandreport(resolvers.expansion, environment.files)
+
+elseif environment.argument("show-path") or environment.argument("path-value") then
+
+ -- luatools: runners.execute_ctx_script("mtx-base","--show-path",filename)
+
+ resolvers.load("nofiles")
+ runners.register_arguments(filename)
+ environment.initializearguments(environment.arguments_after)
+ resolvers.dowithfilesandreport(resolvers.showpath, environment.files)
+
+elseif environment.argument("var-value") or environment.argument("show-value") then
+
+ -- luatools: runners.execute_ctx_script("mtx-base","--show-value",filename)
+
+ resolvers.load("nofiles")
+ runners.register_arguments(filename)
+ environment.initializearguments(environment.arguments_after)
+ resolvers.dowithfilesandreport(resolvers.variable,environment.files)
+
+elseif environment.argument("format-path") then
+
+ -- luatools: runners.execute_ctx_script("mtx-base","--format-path",filename)
+
+ resolvers.load()
+ report(caches.getwritablepath("format"))
+
+elseif environment.argument("pattern") then
+
+ -- luatools
+
+ runners.execute_ctx_script("mtx-base","--pattern='" .. environment.argument("pattern") .. "'",filename)
+
+elseif environment.argument("generate") then
+
+ -- luatools
+
+ instance.renewcache = true
+ trackers.enable("resolvers.locating")
+ resolvers.load()
+
+elseif environment.argument("make") or environment.argument("ini") or environment.argument("compile") then
+
+ -- luatools: runners.execute_ctx_script("mtx-base","--make",filename)
+
+ resolvers.load()
+ trackers.enable("resolvers.locating")
+ environment.make_format(filename)
+
+elseif environment.argument("run") then
+
+ -- luatools
+
+ runners.execute_ctx_script("mtx-base","--run",filename)
+
+elseif environment.argument("fmt") then
+
+ -- luatools
+
+ runners.execute_ctx_script("mtx-base","--fmt",filename)
+
+elseif environment.argument("help") and filename=='base' then
+
+ -- luatools
+
+ runners.execute_ctx_script("mtx-base","--help")
+
elseif environment.argument("help") or filename=='help' or filename == "" then
- logs.help(messages.help)
- -- execute script
+
+ application.help()
+
elseif filename:find("^bin:") then
+
+ runners.loadbase()
ok = runners.execute_program(filename)
+
elseif is_mkii_stub then
+
-- execute mkii script
+
+ runners.loadbase()
ok = runners.execute_script(filename,false,true)
-else
+
+elseif false then
+
+ runners.loadbase()
ok = runners.execute_ctx_script(filename)
if not ok then
ok = runners.execute_script(filename)
end
+
+elseif environment.files[1] == 'texmfcnf.lua' then -- so that we don't need to load mtx-base
+
+ resolvers.load("nofiles")
+ resolvers.listers.configurations()
+
+else
+
+ runners.loadbase()
+ runners.execute_ctx_script("mtx-base",filename)
+
+end
+
+if e_verbose then
+ report()
+ report("runtime: %0.3f seconds",os.runtime())
end
-if os.platform == "unix" then
- io.write("\n")
+if os.type ~= "windows" then
+ texio.write("\n") -- is this still valid?
end
if ok == false then ok = 1 elseif ok == true then ok = 0 end
diff --git a/Master/texmf-dist/scripts/context/lua/x-ldx.lua b/Master/texmf-dist/scripts/context/lua/x-ldx.lua
deleted file mode 100644
index e0f21d68cd9..00000000000
--- a/Master/texmf-dist/scripts/context/lua/x-ldx.lua
+++ /dev/null
@@ -1,322 +0,0 @@
---[[ldx--
-<source>Lua Documentation Module</source>
-
-This file is part of the <logo label='context'/> documentation suite and
-itself serves as an example of using <logo label='lua'/> in combination
-with <logo label='tex'/>.
-
-I will rewrite this using lpeg once I have the time to study that nice new
-subsystem. On the other hand, we cannot expect proper <logo label='tex'/>
-ad for educational purposed the syntax migh be wrong.
---ldx]]--
-
-banner = "version 1.0.1 - 2007+ - PRAGMA ADE / CONTEXT"
-
---[[
-This script needs a few libraries. Instead of merging the code here
-we can use
-
-<typing>
-mtxrun --internal x-ldx.lua
-</typing>
-
-That way, the libraries included in the runner will be used.
-]]--
-
--- libraries l-string.lua l-table.lua l-io.lua l-file.lua
-
--- begin library merge
--- end library merge
-
---[[
-Just a demo comment line. We will handle such multiline comments but
-only when they start and end at the beginning of a line. More rich
-comments are tagged differently.
-]]--
-
---[[ldx--
-First we define a proper namespace for this module. The <q>l</q> stands for
-<logo label='lua'/>, the <q>d</q> for documentation and the <q>x</q> for
-<logo label='xml'/>.
---ldx]]--
-
-if not ldx then ldx = { } end
-
---[[ldx--
-We load the lua file into a table. The entries in this table themselves are
-tables and have keys like <t>code</t> and <t>comment</t>.
---ldx]]--
-
-function ldx.load(filename)
- local data = file.readdata(filename)
- local expr = "%s*%-%-%[%[ldx%-*%s*(.-)%s*%-%-ldx%]%]%-*%s*"
- local i, j, t = 0, 0, { }
- while true do
- local comment, ni
- ni, j, comment = data:find(expr, j)
- if not ni then break end
- t[#t+1] = { code = data:sub(i, ni-1) }
- t[#t+1] = { comment = comment }
- i = j + 1
- end
- local str = data:sub(i, #data)
- str = str:gsub("^%s*(.-)%s*$", "%1")
- if #str > 0 then
- t[#t+1] = { code = str }
- end
- return t
-end
-
---[[ldx--
-We will tag keywords so that we can higlight them using a special font
-or color. Users can extend this list when needed.
---ldx]]--
-
-ldx.keywords = { }
-
---[[ldx--
-Here come the reserved words:
---ldx]]--
-
-ldx.keywords.reserved = {
- ["and"] = 1,
- ["break"] = 1,
- ["do"] = 1,
- ["else"] = 1,
- ["elseif"] = 1,
- ["end"] = 1,
- ["false"] = 1,
- ["for"] = 1,
- ["function"] = 1,
- ["if"] = 1,
- ["in"] = 1,
- ["local"] = 1,
- ["nil"] = 1,
- ["not"] = 1,
- ["or"] = 1,
- ["repeat"] = 1,
- ["return"] = 1,
- ["then"] = 1,
- ["true"] = 1,
- ["until"] = 1,
- ["while"] = 1
-}
-
---[[ldx--
-We need to escape a few tokens. We keep the hash local to the
-definition but set it up only once, hence the <key>do</key>
-construction.
---ldx]]--
-
-do
- local e = { [">"] = "&gt;", ["<"] = "&lt;", ["&"] = "&amp;" }
- function ldx.escape(str)
- return (str:gsub("([><&])",e))
- end
-end
-
---[[ldx--
-Enhancing the code is a bit tricky due to the fact that we have to
-deal with strings and escaped quotes within these strings. Before we
-mess around with the code, we hide the strings, and after that we
-insert them again. Single and double quoted strings are tagged so
-that we can use a different font to highlight them.
---ldx]]--
-
-ldx.make_index = true
-
-function ldx.enhance(data) -- i need to use lpeg and then we can properly autoindent -)
- local e = ldx.escape
- for k=1,#data do
- local v = data[k]
- if v.code then
- local dqs, sqs, com, cmt, cod = { }, { }, { }, { }, e(v.code)
- cod = cod:gsub('\\"', "##d##")
- cod = cod:gsub("\\'", "##s##")
- cod = cod:gsub("%-%-%[%[.-%]%]%-%-", function(s)
- cmt[#cmt+1] = s
- return "<l<<<".. #cmt ..">>>l>"
- end)
- cod = cod:gsub("%-%-([^\n]*)", function(s)
- com[#com+1] = s
- return "<c<<<".. #com ..">>>c>"
- end)
- cod = cod:gsub("(%b\"\")", function(s)
- dqs[#dqs+1] = s:sub(2,-2) or ""
- return "<d<<<".. #dqs ..">>>d>"
- end)
- cod = cod:gsub("(%b\'\')", function(s)
- sqs[#sqs+1] = s:sub(2,-2) or ""
- return "<s<<<".. #sqs ..">>>s>"
- end)
- cod = cod:gsub("(%a+)",function(key)
- local class = ldx.keywords.reserved[key]
- if class then
- return "<key class='" .. class .. "'>" .. key .. "</key>"
- else
- return key
- end
- end)
- cod = cod:gsub("<s<<<(%d+)>>>s>", function(s)
- return "<sqs>" .. sqs[tonumber(s)] .. "</sqs>"
- end)
- cod = cod:gsub("<d<<<(%d+)>>>d>", function(s)
- return "<dqs>" .. dqs[tonumber(s)] .. "</dqs>"
- end)
- cod = cod:gsub("<c<<<(%d+)>>>c>", function(s)
- return "<com>" .. com[tonumber(s)] .. "</com>"
- end)
- cod = cod:gsub("<l<<<(%d+)>>>l>", function(s)
- return cmt[tonumber(s)]
- end)
- cod = cod:gsub("##d##", "\\\"")
- cod = cod:gsub("##s##", "\\\'")
- if ldx.make_index then
- local lines = cod:split("\n")
- local f = "(<key class='1'>function</key>)%s+([%w%.]+)%s*%("
- for k=1,#lines do
- local v = lines[k]
- -- functies
- v = v:gsub(f,function(key, str)
- return "<function>" .. str .. "</function>("
- end)
- -- variables
- v = v:gsub("^([%w][%w%,%s]-)(=[^=])",function(str, rest)
- local t = string.split(str, ",%s*")
- for k=1,#t do
- t[k] = "<variable>" .. t[k] .. "</variable>"
- end
- return table.join(t,", ") .. rest
- end)
- -- so far
- lines[k] = v
- end
- v.code = table.concat(lines,"\n")
- else
- v.code = cod
- end
- end
- end
-end
-
---[[ldx--
-We're now ready to save the file in <logo label='xml'/> format. This boils
-down to wrapping the code and comment as well as the whole document. We tag
-lines in the code as such so that we don't need messy <t>CDATA</t> constructs
-and by calculating the indentation we also avoid space troubles. It also makes
-it possible to change the indentation afterwards.
---ldx]]--
-
-function ldx.as_xml(data) -- ldx: not needed
- local t, cmode = { }, false
- t[#t+1] = "<?xml version='1.0' standalone='yes'?>\n"
- t[#t+1] = "\n<document xmlns:ldx='http://www.pragma-ade.com/schemas/ldx.rng' xmlns='http://www.pragma-ade.com/schemas/ldx.rng'>\n"
- for k=1,#data do
- local v = data[k]
- if v.code and not v.code:is_empty() then
- t[#t+1] = "\n<code>\n"
- local split = v.code:split("\n")
- for k=1,#split do -- make this faster
- local v = split[k]
- local a, b = v:find("^(%s+)")
- if v then v = v:gsub("[\n\r ]+$","") end
- if a and b then
- v = v:sub(b+1,#v)
- if cmode then
- t[#t+1] = "<line comment='yes' n='" .. b .. "'>" .. v .. "</line>\n"
- else
- t[#t+1] = "<line n='" .. b .. "'>" .. v .. "</line>\n"
- end
- elseif v:is_empty() then
- if cmode then
- t[#t+1] = "<line comment='yes'/>\n"
- else
- t[#t+1] = "<line/>\n"
- end
- elseif v:find("^%-%-%[%[") then
- t[#t+1] = "<line comment='yes'>" .. v .. "</line>\n"
- cmode= true
- elseif v:find("^%]%]%-%-") then
- t[#t+1] = "<line comment='yes'>" .. v .. "</line>\n"
- cmode= false
- elseif cmode then
- t[#t+1] = "<line comment='yes'>" .. v .. "</line>\n"
- else
- t[#t+1] = "<line>" .. v .. "</line>\n"
- end
- end
- t[#t+1] = "</code>\n"
- elseif v.comment then
- t[#t+1] = "\n<comment>\n" .. v.comment .. "\n</comment>\n"
- else
- -- cannot happen
- end
- end
- t[#t+1] = "\n</document>\n"
- return table.concat(t,"")
-end
-
---[[ldx--
-Saving the result is a trivial effort.
---ldx]]--
-
-function ldx.save(filename,data)
- file.savedata(filename,ldx.as_xml(data))
-end
-
---[[ldx--
-The next function wraps it all in one call:
---ldx]]--
-
-function ldx.convert(luaname,ldxname)
- if not file.isreadable(luaname) then
- luaname = luaname .. ".lua"
- end
- if file.isreadable(luaname) then
- if not ldxname then
- ldxname = file.replacesuffix(luaname,"ldx")
- end
- local data = ldx.load(luaname)
- if data then
- ldx.enhance(data)
- if ldxname ~= luaname then
- ldx.save(ldxname,data)
- end
- end
- end
-end
-
---[[ldx--
-This module can be used directly:
-
-<typing>
-mtxrun --internal x-ldx somefile.lua
-</typing>
-
-will produce an ldx file that can be processed with <logo label='context'/>
-by running:
-
-<typing>
-texexec --use=x-ldx --forcexml somefile.ldx
-</typing>
-
-You can do this in one step by saying:
-
-<typing>
-texmfstart texexec --ctx=x-ldx somefile.lua
-</typing>
-
-This will trigger <logo label='texexec'/> into loading the mentioned
-<logo label='ctx'/> file. That file describes the conversion as well
-as the module to be used.
-
-The main conversion call is:
---ldx]]--
-
--- todo: assume usage of "mtxrun --script x-ldx", maybe make it mtx-ldx
-
-if arg and arg[1] then
- ldx.convert(arg[1],arg[2])
-end
-
---~ exit(1)