diff options
Diffstat (limited to 'scripts')
59 files changed, 29966 insertions, 5082 deletions
diff --git a/scripts/context/lua/mtx-bibtex.lua b/scripts/context/lua/mtx-bibtex.lua new file mode 100644 index 000000000..92036e3a5 --- /dev/null +++ b/scripts/context/lua/mtx-bibtex.lua @@ -0,0 +1,152 @@ +if not modules then modules = { } end modules ['mtx-bibtex'] = { + version = 1.002, + comment = "this script is part of publication support", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE", + license = "see context related readme files" +} + +local helpinfo = [[ +<?xml version="1.0"?> +<application> + <metadata> + <entry name="name">mtx-bibtex</entry> + <entry name="detail">bibtex helpers</entry> + <entry name="version">1.00</entry> + </metadata> + <flags> + <category name="basic"> + <subcategory> + <flag name="toxml"><short>convert bibtex database(s) to xml</short></flag> + <flag name="tolua"><short>convert bibtex database(s) to lua</short></flag> + <flag name="search"><short>seatch bibtex database(s)</short></flag> + </subcategory> + </category> + </flags> + <examples> + <category> + <title>Example</title> + <subcategory> + <example><command>mtxrun --script bibtex --tolua bibl-001.bib</command></example> + <example><command>mtxrun --script bibtex --tolua --simple bibl-001.bib</command></example> + <example><command>mtxrun --script bibtex --toxml bibl-001.bib bibl-002.bib bibl-003.bib biblio.xml</command></example> + <example><command>mtxrun --script bibtex --search --list --pattern=match(author:foo) bar.bib</command></example> + </subcategory> + </category> + </examples> +</application> +]] + +local application = logs.application { + name = "mtx-bibtex", + banner = "bibtex helpers", + helpinfo = helpinfo, +} + +local report = application.report + +require("util-seq") +require("publ-dat") +require("publ-fnd") + +scripts = scripts or { } +scripts.bibtex = scripts.bibtex or { } + +function scripts.bibtex.toxml(files) + local instance = bibtex.new() + local target = "mtx-bibtex-output.xml" + for i=1,#files do + local filename = files[i] + local filetype = file.suffix(filename) + if filetype == "xml" then + target = filename + elseif filetype == "bib" then + bibtex.load { dataset = instance, filename = filename } + else + -- not supported + end + end + bibtex.converttoxml(instance,true) + instance.shortcuts = nil + instance.luadata = nil + xml.save(instance.xmldata,target) +end + +function scripts.bibtex.tolua(files) + local instance = bibtex.new() + local target = "mtx-bibtex-output.lua" + for i=1,#files do + local filename = files[i] + local filetype = file.suffix(filename) + if filetype == "lua" then + target = filename + elseif filetype == "bib" then + bibtex.load { dataset = instance, filename = filename } + + else + -- not supported + end + end + instance.shortcuts = nil + instance.xmldata = nil + bibtex.analyze(instance) + if environment.arguments.simple then + table.save(target,instance) + else + table.save(target,instance.luadata) + end +end + +function scripts.bibtex.search(files,pattern,list) + if pattern then + local dataset = publications.datasets["whatever"] + for i=1,#files do + local filename = resolvers.findfile(files[i]) + if filename and filename ~= "" then + publications.load { dataset = "whatever", filename = filename } + end + end + local found = publications.search(dataset,pattern) + local tags = table.sortedkeys(found) + if #tags == 0 then + report("no match") + elseif list then + report("%s matches:",#tags) + local result = { } + local luadata = dataset.luadata + for i=1,#tags do + local tag = tags[i] + local entry = luadata[tag] + result[i] = { + tag, + entry.year, + entry.author, + entry.title, + } + end + utilities.formatters.formatcolumns(result) + logs.newline() + for i=1,#result do + texio.write_nl(result[i]) + end + logs.newline() + else + report("%s matches: % t",#tags,tags) + end + end +end + +if environment.arguments.search then + scripts.bibtex.search(environment.files,environment.arguments.pattern,environment.arguments.list) +elseif environment.arguments.toxml then + scripts.bibtex.toxml(environment.files) +elseif environment.arguments.tolua then + scripts.bibtex.tolua(environment.files) +elseif environment.arguments.exporthelp then + application.export(environment.arguments.exporthelp,environment.files[1]) +else + application.help() +end + +-- scripts.bibtex.toxml { "tugboat.bib" } +-- scripts.bibtex.tolua { "tugboat.bib" } diff --git a/scripts/context/lua/mtx-check.lua b/scripts/context/lua/mtx-check.lua index 9f52509ec..c456b4414 100644 --- a/scripts/context/lua/mtx-check.lua +++ b/scripts/context/lua/mtx-check.lua @@ -21,7 +21,7 @@ local helpinfo = [[ <flags> <category name="basic"> <subcategory> - <flag name="convert"><short>check tex file for errors</short></flag> + <flag name="check"><short>check tex file for errors</short></flag> </subcategory> </category> </flags> @@ -34,17 +34,17 @@ local application = logs.application { helpinfo = helpinfo, } -local report = application.report +local report = application.report -scripts = scripts or { } -scripts.checker = scripts.checker or { } +scripts = scripts or { } +scripts.checker = scripts.checker or { } -local validator = { } +local validator = { } -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 @@ -68,27 +68,24 @@ local progress = function(position, data, kind) end end -local i_m, d_m = P("$"), P("$$") -local l_s, r_s = P("["), P("]") -local l_g, r_g = P("{"), P("}") +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 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 esc = P("\\") +local space = S(" \t\f\v") +local newline = lpeg.patterns.newline -local line = newline / function() validator.n = validator.n + 1 end +local line = newline / function() validator.n = validator.n + 1 end local startluacode = P("\\startluacode") local stopluacode = P("\\stopluacode") -local somecode = startluacode * (1-stopluacode)^1 * stopluacode +local somecode = startluacode * (1-stopluacode)^1 * stopluacode -local stack = { } +local stack = { } local function push(p,s) -- print("start",p,s) @@ -117,7 +114,7 @@ local contextgrammar = P { "tokens", ["start"] = start, ["stop"] = stop, ["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, + ["grouped"] = l_g * (V("whatever") + V("grouped") + V("setup") + V("display") + V("inline") + line + (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, diff --git a/scripts/context/lua/mtx-context.lua b/scripts/context/lua/mtx-context.lua index 4c6672051..d624f6831 100644 --- a/scripts/context/lua/mtx-context.lua +++ b/scripts/context/lua/mtx-context.lua @@ -28,10 +28,13 @@ local fileaddsuffix = file.addsuffix local filenewsuffix = file.replacesuffix local removesuffix = file.removesuffix local validfile = lfs.isfile +local removefile = os.remove +local renamefile = os.rename +local formatters = string.formatters local application = logs.application { name = "mtx-context", - banner = "ConTeXt Process Management 0.60", + banner = "ConTeXt Process Management 0.61", -- helpinfo = helpinfo, -- table with { category_a = text_1, category_b = text_2 } or helpstring or xml_blob helpinfo = "mtx-context.xml", } @@ -87,14 +90,17 @@ scripts.context = scripts.context or { } -- for the moment here -if getargument("jit") or getargument("jiton") then +if jit then -- already luajittex + setargument("engine","luajittex") + setargument("jit",nil) +elseif getargument("jit") or getargument("jiton") then -- relaunch luajittex -- bonus shortcut, we assume than --jit also indicates the engine -- although --jit and --engine=luajittex are independent setargument("engine","luajittex") end -local engine_new = getargument("engine") or directives.value("system.engine") -local engine_old = environment.ownbin +local engine_new = file.nameonly(getargument("engine") or directives.value("system.engine")) +local engine_old = file.nameonly(environment.ownbin) local function restart(engine_old,engine_new) local command = format("%s --luaonly %q %s --redirected",engine_new,environment.ownname,environment.reconstructcommandline()) @@ -253,8 +259,9 @@ end -- multipass control -local multipass_suffixes = { ".tuc" } -local multipass_nofruns = 8 -- or 7 to test oscillation +local multipass_suffixes = { ".tuc" } +local multipass_nofruns = 9 -- better for tracing oscillation +local multipass_forcedruns = false local function multipass_hashfiles(jobname) local hash = { } @@ -275,11 +282,47 @@ local function multipass_changed(oldhash, newhash) return false end -local function multipass_copyluafile(jobname) +local f_tempfile = formatters["%s-%s-%02d.tmp"] + +local function backup(run,kind,filename) + if run == 1 then + for i=1,10 do + local tmpname = f_tempfile(jobname,kind,i) + if validfile(tmpname) then + removefile(tmpname) + report("removing %a",tmpname) + end + end + end + if validfile(filename) then + local tmpname = f_tempfile(jobname,kind,run or 1) + report("copying %a into %a",filename,tmpname) + file.copy(filename,tmpname) + else + report("no file %a, nothing kept",filename) + end +end + +local function multipass_copyluafile(jobname,run) local tuaname, tucname = jobname..".tua", jobname..".tuc" if validfile(tuaname) then - os.remove(tucname) - os.rename(tuaname,tucname) + if run then + backup(run,"tuc",tucname) + report("copying %a into %a",tuaname,tucname) + report() + end + removefile(tucname) + renamefile(tuaname,tucname) + end +end + +local function multipass_copylogfile(jobname,run) + local logname = jobname..".log" + if validfile(logname) then + if run then + backup(run,"log",logname) + report() + end end end @@ -344,8 +387,8 @@ local function result_push_purge(oldbase,newbase) for _, suffix in next, usedsuffixes.after do local oldname = fileaddsuffix(oldbase,suffix) local newname = fileaddsuffix(newbase,suffix) - os.remove(newname) - os.remove(oldname) + removefile(newname) + removefile(oldname) end end @@ -354,10 +397,10 @@ local function result_push_keep(oldbase,newbase) local oldname = fileaddsuffix(oldbase,suffix) local newname = fileaddsuffix(newbase,suffix) local tmpname = "keep-"..oldname - os.remove(tmpname) - os.rename(oldname,tmpname) - os.remove(oldname) - os.rename(newname,oldname) + removefile(tmpname) + renamefile(oldname,tmpname) + removefile(oldname) + renamefile(newname,oldname) end end @@ -365,8 +408,8 @@ local function result_save_error(oldbase,newbase) for _, suffix in next, usedsuffixes.keep do local oldname = fileaddsuffix(oldbase,suffix) local newname = fileaddsuffix(newbase,suffix) - os.remove(newname) -- to be sure - os.rename(oldname,newname) + removefile(newname) -- to be sure + renamefile(oldname,newname) end end @@ -374,8 +417,8 @@ local function result_save_purge(oldbase,newbase) for _, suffix in next, usedsuffixes.after do local oldname = fileaddsuffix(oldbase,suffix) local newname = fileaddsuffix(newbase,suffix) - os.remove(newname) -- to be sure - os.rename(oldname,newname) + removefile(newname) -- to be sure + renamefile(oldname,newname) end end @@ -384,9 +427,9 @@ local function result_save_keep(oldbase,newbase) local oldname = fileaddsuffix(oldbase,suffix) local newname = fileaddsuffix(newbase,suffix) local tmpname = "keep-"..oldname - os.remove(newname) - os.rename(oldname,newname) - os.rename(tmpname,oldname) + removefile(newname) + renamefile(oldname,newname) + renamefile(tmpname,oldname) end end @@ -536,15 +579,30 @@ function scripts.context.run(ctxdata,filename) local a_profile = getargument("profile") local a_batchmode = getargument("batchmode") local a_nonstopmode = getargument("nonstopmode") + local a_scollmode = getargument("scrollmode") local a_once = getargument("once") local a_synctex = getargument("synctex") local a_backend = getargument("backend") local a_arrange = getargument("arrange") local a_noarrange = getargument("noarrange") local a_jiton = getargument("jiton") + local a_jithash = getargument("jithash") local a_texformat = getargument("texformat") + local a_keeptuc = getargument("keeptuc") + local a_keeplog = getargument("keeplog") + + -- the following flag is not officially supported because i cannot forsee + -- side effects (so no bug reports please) .. we provide --sandbox that + -- does similar things but tries to ensure that context works as expected + + local a_safer = getargument("safer") + + if a_safer then + report("warning: using the luatex safer options, processing is not guaranteed") + end + -- - a_batchmode = (a_batchmode and "batchmode") or (a_nonstopmode and "nonstopmode") or nil + a_batchmode = (a_batchmode and "batchmode") or (a_nonstopmode and "nonstopmode") or (a_scrollmode and "scrollmode") or nil a_synctex = check_synctex(a_synctex) -- for i=1,#filelist do @@ -578,13 +636,30 @@ function scripts.context.run(ctxdata,filename) formatfile, scriptfile = resolvers.locateformat(formatname) end -- - a_jiton = (a_jiton or toboolean(analysis.jiton,true)) and true or nil + a_jiton = (a_jiton or toboolean(analysis.jiton,true)) and true or nil + a_jithash = validstring(a_jithash or analysis.jithash) or nil -- if not formatfile or not scriptfile then report("warning: no format found, forcing remake (source driven)") scripts.context.make(formatname,a_engine) formatfile, scriptfile = resolvers.locateformat(formatname) end + -- + local function combine(key) + local flag = validstring(environment[key]) + local plus = analysis[key] + if flag and plus then + return plus .. "," .. flag -- flag wins + else + return flag or plus -- flag wins + end + end + local a_trackers = analysis.trackers + local a_experiments = analysis.experiments + local directives = combine("directives") + local trackers = combine("trackers") + local experiments = combine("experiments") + -- if formatfile and scriptfile then local suffix = validstring(getargument("suffix")) local resultname = validstring(getargument("result")) @@ -632,9 +707,9 @@ function scripts.context.run(ctxdata,filename) local maxnofruns = once and 1 or multipass_nofruns -- local c_flags = { - directives = validstring(environment.directives), -- gets passed via mtxrun - trackers = validstring(environment.trackers), -- gets passed via mtxrun - experiments = validstring(environment.experiments), -- gets passed via mtxrun + directives = directives, -- gets passed via mtxrun + trackers = trackers, -- gets passed via mtxrun + experiments = experiments, -- gets passed via mtxrun -- result = validstring(resultname), input = validstring(getargument("input") or filename), -- alternative input @@ -655,18 +730,16 @@ function scripts.context.run(ctxdata,filename) ["interaction"] = a_batchmode, ["synctex"] = a_synctex, ["no-parse-first-line"] = true, + ["safer"] = a_safer, -- ["no-mktex"] = true, -- ["file-line-error-style"] = true, ["fmt"] = formatfile, ["lua"] = scriptfile, ["jobname"] = jobname, ["jiton"] = a_jiton, + ["jithash"] = a_jithash, } -- - if a_synctex then - report("warning: synctex is enabled") -- can add upto 5% runtime - end - -- if not a_timing then -- okay elseif c_flags.usemodule then @@ -683,6 +756,15 @@ function scripts.context.run(ctxdata,filename) c_flags.directives = "system.profile" end -- + if a_synctex then + report("warning: synctex is enabled") -- can add upto 5% runtime + if c_flags.directives then + c_flags.directives = format("system.synctex=%s,%s",a_synctex,c_flags.directives) + else + c_flags.directives = format("system.synctex=%s",a_synctex) + end + end + -- -- kindofrun: 1:first run, 2:successive run, 3:once, 4:last of maxruns -- for currentrun=1,maxnofruns do @@ -690,14 +772,16 @@ function scripts.context.run(ctxdata,filename) c_flags.final = false c_flags.kindofrun = (a_once and 3) or (currentrun==1 and 1) or (currentrun==maxnofruns and 4) or 2 c_flags.maxnofruns = maxnofruns + c_flags.forcedruns = multipass_forcedruns and multipass_forcedruns > 0 and multipass_forcedruns or nil c_flags.currentrun = currentrun c_flags.noarrange = a_noarrange or a_arrange or nil -- local command = luatex_command(l_flags,c_flags,mainfile,a_engine) -- - report("run %s: %s",i,command) + report("run %s: %s",currentrun,command) print("") -- cleaner, else continuation on same line local returncode, errorstring = os.spawn(command) + -- todo: remake format when no proper format is found if not returncode then report("fatal error: no return code, message: %s",errorstring or "?") if resultname then @@ -706,11 +790,17 @@ function scripts.context.run(ctxdata,filename) os.exit(1) break elseif returncode == 0 then - multipass_copyluafile(jobname) - newhash = multipass_hashfiles(jobname) - if multipass_changed(oldhash,newhash) then - oldhash = newhash - else + multipass_copyluafile(jobname,a_keeptuc and currentrun) + multipass_copylogfile(jobname,a_keeplog and currentrun) + if not multipass_forcedruns then + newhash = multipass_hashfiles(jobname) + if multipass_changed(oldhash,newhash) then + oldhash = newhash + else + break + end + elseif currentrun == multipass_forcedruns then + report("quitting after force %i runs",multipass_forcedruns) break end else @@ -773,6 +863,24 @@ function scripts.context.run(ctxdata,filename) pdf_open(resultname or jobname,pdfview) end -- + local epub = analysis.epub + if epub then + if type(epub) == "string" then + local t = settings_to_array(epub) + for i=1,#t do + t[i] = "--" .. gsub(t[i],"^%-*","") + end + epub = concat(t," ") + else + epub = "--make" + end + local command = "mtxrun --script epub " .. epub .. " " .. jobname + report() + report("making epub file: ",command) + report() + os.execute(command) + end + -- if a_timing then report() report("you can process (timing) statistics with:",jobname) @@ -838,7 +946,7 @@ function scripts.context.pipe() -- still used? scripts.context.purge_job(filename) elseif getargument("purgeall") then scripts.context.purge_job(filename,true) - os.remove(filename) + removefile(filename) end else if formatname then @@ -1032,11 +1140,11 @@ local special_runfiles = { local function purge_file(dfile,cfile) if cfile and validfile(cfile) then - if os.remove(dfile) then + if removefile(dfile) then return filebasename(dfile) end elseif dfile then - if os.remove(dfile) then + if removefile(dfile) then return filebasename(dfile) end end @@ -1130,8 +1238,8 @@ local function touch(path,name,versionpattern,kind,kindpattern) end if newdata ~= "" and (oldversion ~= newversion or oldkind ~= newkind or newdata ~= olddata) then local backup = filenewsuffix(name,"tmp") - os.remove(backup) - os.rename(name,backup) + removefile(backup) + renamefile(name,backup) io.savedata(name,newdata) return name, oldversion, newversion, oldkind, newkind end @@ -1478,9 +1586,12 @@ do end if getargument("once") then - multipass_nofruns = 1 -elseif getargument("runs") then - multipass_nofruns = tonumber(getargument("runs")) or nil + multipass_nofruns = 1 +else + if getargument("runs") then + multipass_nofruns = tonumber(getargument("runs")) or nil + end + multipass_forcedruns = tonumber(getargument("forcedruns")) or nil end if getargument("run") then diff --git a/scripts/context/lua/mtx-context.xml b/scripts/context/lua/mtx-context.xml index a3812288f..c41093289 100644 --- a/scripts/context/lua/mtx-context.xml +++ b/scripts/context/lua/mtx-context.xml @@ -108,6 +108,14 @@ <flag name="once"> <short>only run once (no multipass data file is produced)</short> </flag> + <flag name="runs"> + <short>process at most this many times</short> + </flag> + <flag name="forcedruns"> + <short>process this many times (permits for optimization trial runs)</short> + </flag> + </subcategory> + <subcategory> <flag name="batchmode"> <short>run without stopping and do not show messages on the console</short> </flag> @@ -117,7 +125,7 @@ <flag name="synctex"> <short>run with synctex enabled (optional value: zipped, unzipped, 1, -1)</short> </flag> - </subcategory> + </subcategory> <subcategory> <flag name="generate"> <short>generate file database etc. (as luatools does)</short> @@ -134,7 +142,7 @@ <short>assume given file present elsewhere</short> </flag> <flag name="nofile"> - <short>use dummy file as jobname</short> + <short>use dummy file as jobname</short> </flag> </subcategory> </category> @@ -144,16 +152,22 @@ <short>update context version number (also provide <ref name="expert"/>, optionally provide <ref name="basepath"/>)</short> </flag> <flag name="nostatistics"> - <short>omit runtime statistics at the end of the run</short> + <short>omit runtime statistics at the end of the run</short> </flag> <flag name="update"> - <short>update context from website (not to be confused with contextgarden)</short> + <short>update context from website (not to be confused with contextgarden)</short> </flag> - <flag name="profile"> - <short>profile job (use: mtxrun <ref name="script"/> profile <ref name="analyze"/>)</short> + <flag name="profile"> + <short>profile job (use: mtxrun <ref name="script"/> profile <ref name="analyze"/>)</short> </flag> - <flag name="timing"> - <short>generate timing and statistics overview</short> + <flag name="timing"> + <short>generate timing and statistics overview</short> + </flag> + <flag name="keeptuc"> + <short>keep previous tuc files (jobname-tuc-[run].tmp)</short> + </flag> + <flag name="keeplog"> + <short>keep previous log files (jobname-log-[run].tmp)</short> </flag> </subcategory> <subcategory> @@ -182,6 +196,11 @@ <short>do not check for file and enter scroll mode (<ref name="dummyfile"/>=whatever.tmp)</short> </flag> </subcategory> + <subcategory> + <flag name="sandbox"> + <short>process file in a limited environment</short> + </flag> + </subcategory> </category> </flags> </application> diff --git a/scripts/context/lua/mtx-convert.lua b/scripts/context/lua/mtx-convert.lua index b76b3baaf..d5dba075a 100644 --- a/scripts/context/lua/mtx-convert.lua +++ b/scripts/context/lua/mtx-convert.lua @@ -48,7 +48,7 @@ local convert = scripts.convert convert.converters = convert.converters or { } local converters = convert.converters -local gsprogram = os.type == "windows" and "gswin32c" or "gs" +local gsprogram = (os.type == "windows" and (os.which("gswin64c.exe") or os.which("gswin32c.exe"))) or "gs" local gstemplate_eps = "%s -q -sDEVICE=pdfwrite -dPDFSETTINGS=/prepress -dEPSCrop -dNOPAUSE -dSAFER -dNOCACHE -dBATCH -dAutoRotatePages=/None -dProcessColorModel=/DeviceCMYK -sOutputFile=%s %s -c quit" local gstemplate_ps = "%s -q -sDEVICE=pdfwrite -dPDFSETTINGS=/prepress -dNOPAUSE -dSAFER -dNOCACHE -dBATCH -dAutoRotatePages=/None -dProcessColorModel=/DeviceCMYK -sOutputFile=%s %s -c quit" diff --git a/scripts/context/lua/mtx-epub.lua b/scripts/context/lua/mtx-epub.lua index 11f0a2024..956ce4931 100644 --- a/scripts/context/lua/mtx-epub.lua +++ b/scripts/context/lua/mtx-epub.lua @@ -18,8 +18,89 @@ if not modules then modules = { } end modules ['mtx-epub'] = { -- first we need a decent strategy to export them. More information will be -- available on the wiki. -local format, gsub = string.format, string.gsub -local concat = table.concat +-- META-INF +-- container.xml +-- OEBPS +-- content.opf +-- toc.ncx +-- images +-- styles +-- mimetype + +-- todo: +-- +-- remove m_k_v_i prefixes +-- remap fonts %mono% in css so that we can replace +-- coverpage tests +-- split up + +-- todo: automated cover page: +-- +-- \startMPpage +-- StartPage ; +-- fill Page withcolor .5red ; +-- numeric n ; +-- for i=10 downto 1 : +-- n := i * PaperWidth/40 ; +-- draw +-- lrcorner Page shifted (0,n) +-- % -- lrcorner Page +-- -- lrcorner Page shifted (-n,0) +-- % -- cycle +-- withpen pencircle scaled 1mm +-- withcolor white ; +-- endfor ; +-- picture p ; p := image ( +-- draw +-- anchored.top( +-- textext.bot("\tttf Some Title") +-- xsized .8PaperWidth +-- ,center topboundary Page +-- ) +-- withcolor white ; +-- ) ; +-- picture q ; q := image ( +-- draw +-- anchored.top( +-- textext.bot("\tttf An Author") +-- xsized .4PaperWidth +-- shifted (0,-PaperHeight/40) +-- ,center bottomboundary p +-- ) +-- withcolor white ; +-- ) ; +-- draw p ; +-- draw q ; +-- StopPage ; +-- \stopMPpage + +local format, gsub, find = string.format, string.gsub, string.find +local concat, sortedhash = table.concat, table.sortedhash + +local formatters = string.formatters +local replacetemplate = utilities.templates.replace + +local addsuffix = file.addsuffix +local nameonly = file.nameonly +local basename = file.basename +local pathpart = file.pathpart +local joinfile = file.join +local suffix = file.suffix +local addsuffix = file.addsuffix +local removesuffix = file.removesuffix +local replacesuffix = file.replacesuffix + +local copyfile = file.copy +local removefile = os.remove + +local needsupdating = file.needsupdating + +local isdir = lfs.isdir +local isfile = lfs.isfile +local mkdir = lfs.mkdir + +local pushdir = dir.push +local popdir = dir.pop local helpinfo = [[ <?xml version="1.0"?> @@ -27,12 +108,17 @@ local helpinfo = [[ <metadata> <entry name="name">mtx-epub</entry> <entry name="detail">ConTeXt EPUB Helpers</entry> - <entry name="version">0.12</entry> + <entry name="version">1.10</entry> </metadata> <flags> <category name="basic"> <subcategory> <flag name="make"><short>create epub zip file</short></flag> + <flag name="purge"><short>remove obsolete files</short></flag> + <flag name="rename"><short>rename images to sane names</short></flag> + <flag name="svgmath"><short>convert mathml to svg</short></flag> + <flag name="svgstyle"><short>use given tex style for svg generation (overloads style in specification)</short></flag> + <flag name="all"><short>assume: --purge --rename --svgmath (for fast testing)</short></flag> </subcategory> </category> </flags> @@ -49,10 +135,12 @@ local helpinfo = [[ local application = logs.application { name = "mtx-epub", - banner = "ConTeXt EPUB Helpers 0.12", + banner = "ConTeXt EPUB Helpers 1.10", helpinfo = helpinfo, } +local report = application.report + -- script code scripts = scripts or { } @@ -60,91 +148,142 @@ scripts.epub = scripts.epub or { } local mimetype = "application/epub+zip" -local container = [[ +local t_container = [[ <?xml version="1.0" encoding="UTF-8"?> <container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container"> <rootfiles> - <rootfile full-path="OEBPS/%s" media-type="application/oebps-package+xml"/> + <rootfile full-path="OEBPS/%rootfile%" media-type="application/oebps-package+xml"/> </rootfiles> </container> ]] -local package = [[ +-- urn:uuid: + +-- <dc:identifier id="%identifier%" opf:scheme="UUID">%uuid%</dc:identifier> + +local t_package = [[ <?xml version="1.0" encoding="UTF-8"?> -<package version="2.0" xmlns="http://www.idpf.org/2007/opf" unique-identifier="%s"> +<package xmlns="http://www.idpf.org/2007/opf" unique-identifier="%identifier%" version="3.0"> <metadata xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:opf="http://www.idpf.org/2007/opf"> - <dc:title>%s</dc:title> - <dc:language>%s</dc:language> - <dc:identifier id="%s" opf:scheme="UUID">urn:uuid:%s</dc:identifier> - <dc:creator>%s</dc:creator> - <dc:date>%s</dc:date> - <meta name="cover" content="%s" /> + <dc:title>%title%</dc:title> + <dc:language>%language%</dc:language> + <dc:identifier id="%identifier%">%uuid%</dc:identifier> + <dc:creator>%creator%</dc:creator> + <dc:date>%date%</dc:date> + <!-- + <dc:subject>%subject%</dc:subject> + <dc:description>%description%</dc:description> + <dc:publisher>%publisher%</dc:publisher> + <dc:source>%source%</dc:source> + <dc:relation>%relation%</dc:relation> + <dc:coverage>%coverage%</dc:coverage> + <dc:rights>%rights%</dc:rights> + --> + <meta name="cover" content="%coverpage%" /> + <meta name="generator" content="ConTeXt MkIV" /> + <meta property="dcterms:modified">%date%</meta> </metadata> <manifest> -%s +%manifest% </manifest> <spine toc="ncx"> <itemref idref="cover-xhtml" /> - <itemref idref="%s" /> + <itemref idref="%rootfile%" /> </spine> </package> ]] -local item = [[ <item id="%s" href="%s" media-type="%s"/>]] -local toc = [[ -<?xml version="1.0"?> +local t_item = [[ <item id="%id%" href="%filename%" media-type="%mime%" />]] +local t_prop = [[ <item id="%id%" href="%filename%" media-type="%mime%" properties="%properties%" />]] + +-- <!DOCTYPE ncx PUBLIC "-//NISO//DTD ncx 2005-1//EN" "http://www.daisy.org/z3986/2005/ncx-2005-1.dtd"> + +local t_toc = [[ +<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE ncx PUBLIC "-//NISO//DTD ncx 2005-1//EN" "http://www.daisy.org/z3986/2005/ncx-2005-1.dtd"> +<!-- this is no longer needed in epub 3.0+ --> <ncx xmlns="http://www.daisy.org/z3986/2005/ncx/" version="2005-1"> <head> - <meta name="dtb:uid" content="%s" /> + <meta name="generator" content="ConTeXt MkIV" /> + <meta name="dtb:uid" content="%identifier%" /> <meta name="dtb:depth" content="2" /> <meta name="dtb:totalPgeCount" content="0" /> <meta name="dtb:maxPageNumber" content="0" /> </head> <docTitle> - <text>%s</text> + <text>%title%</text> </docTitle> + <docAuthor> + <text>%author%</text> + </docAuthor> + <navMap> <navPoint id="np-1" playOrder="1"> <navLabel> <text>start</text> </navLabel> - <content src="%s"/> + <content src="%root%"/> </navPoint> </navMap> </ncx> ]] -local coverxhtml = [[ +local t_navtoc = [[ <?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops"> + <head> + <meta charset="utf-8" /> + <title>navtoc</title> + </head> + <body> + <div class="navtoc"> + <!-- <nav epub:type="lot"> --> + <nav epub:type="toc" id="navtoc"> + <ol> + <li><a href="%root%">document</a></li> + </ol> + </nav> + </div> + </body> +</html> +]] + +-- <html xmlns="http://www.w3.org/1999/xhtml"> +-- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> + +local t_coverxhtml = [[ +<?xml version="1.0" encoding="UTF-8"?> <html xmlns="http://www.w3.org/1999/xhtml"> <head> - <title>cover.xhtml</title> + <meta charset="utf-8" /> + <title>cover page</title> </head> <body> - <div> - <img src="%s" alt="The cover image" style="max-width: 100%%;" /> + <div class="coverpage"> + %content% </div> </body> </html> ]] +local t_coverimg = [[ + <img src="%image%" alt="The cover image" style="max-width: 100%%;" /> +]] + -- We need to figure out what is permitted. Numbers only seem to give -- problems is some applications as do names with dashes. Also the -- optional toc is supposed to be there and although id's are by @@ -154,12 +293,13 @@ local coverxhtml = [[ local function dumbid(filename) -- return (string.gsub(os.uuid(),"%-%","")) -- to be tested - return file.nameonly(filename) .. "-" .. file.suffix(filename) + return nameonly(filename) .. "-" .. suffix(filename) end local mimetypes = { xhtml = "application/xhtml+xml", xml = "application/xhtml+xml", + html = "application/html", css = "text/css", svg = "image/svg+xml", png = "image/png", @@ -175,204 +315,601 @@ local idmakers = { default = function(filename) return dumbid(filename) end, } --- specification = { --- name = "document", --- identifier = "123", --- root = "a.xhtml", --- files = { --- "a.xhtml", --- "b.css", --- "c.png", --- } --- } - -local function locateimages(oldname,newname,subpath) +local function relocateimages(imagedata,oldname,newname,subpath,rename) local data = io.loaddata(oldname) - local images = { } - local done = gsub(data,"(background%-image *: * url%()(.-)(%))", function(before,name,after) - if subpath then - name = file.join(subpath,name) + if data then + subpath = joinfile("..",subpath) + report("relocating images") + local n = 0 + local done = gsub(data,[[(id=")(.-)(".-background%-image *: *url%()(.-)(%))]], function(s1,id,s2,name,s3) + local data = imagedata[id] + if data then + local newname = data[id].newname + if newname then + if subpath then + name = joinfile(subpath,basename(newname)) + else + name = basename(newname) + end + -- name = url.addscheme(name) + end + if newname then + n = n + 1 + if rename then + name = joinfile(subpath,addsuffix(id,suffix(name))) + end + return s1 .. id .. s2 .. name .. s3 + end + end + end) + report("%s images relocated in %a",n,newname) + if newname then + io.savedata(newname,done) end - images[#images+1] = name - return before .. name .. after - end) - if newname then - io.savedata(done,newname) end return images end +function reportobsolete(oldfiles,newfiles,purge) + + for i=1,#oldfiles do oldfiles[i] = gsub(oldfiles[i],"^[%./]+","") end + for i=1,#newfiles do newfiles[i] = gsub(newfiles[i],"^[%./]+","") end + + local old = table.tohash(oldfiles) + local new = table.tohash(newfiles) + local done = false + + for name in sortedhash(old) do + if not new[name] then + if not done then + report() + if purge then + report("removing obsolete files:") + else + report("obsolete files:") + end + report() + done = true + end + report(" %s",name) + if purge then + removefile(name) + end + end + end + + if done then + report() + end + + return done + +end + + local zippers = { { name = "zip", + binary = "zip", uncompressed = "zip %s -X -0 %s", compressed = "zip %s -X -9 -r %s", }, { - name = "7zip (7z)", + name = "7z (7zip)", + binary = "7z", uncompressed = "7z a -tzip -mx0 %s %s", compressed = "7z a -tzip %s %s", }, } -function scripts.epub.make() +function scripts.epub.make(purge,rename,svgmath,svgstyle) + + -- one can enter a jobname or jobname-export but the simple jobname is + -- preferred local filename = environment.files[1] - if filename and filename ~= "" and type(filename) == "string" then + if not filename or filename == "" or type(filename) ~= "string" then + report("provide filename") + return + end + + local specpath, specname, specfull - filename = file.basename(filename) - local specfile = file.replacesuffix(filename,"specification") - local specification = lfs.isfile(specfile) and dofile(specfile) or { } + if isdir(filename) then + specpath = filename + specname = addsuffix(specpath,"lua") + specfull = joinfile(specpath,specname) + end + + if not specfull or not isfile(specfull) then + specpath = filename .. "-export" + specname = addsuffix(filename .. "-pub","lua") + specfull = joinfile(specpath,specname) + end + + if not specfull or not isfile(specfull) then + report("unknown specificaton file %a for %a",specfull or "?",filename) + return + end --- inspect(specification) + local specification = dofile(specfull) + + if not specification or not next(specification) then + report("invalid specificaton file %a",specfile) + return + end + + report("using specification file %a",specfull) + + -- images: { ... url = location ... } + + local defaultcoverpage = "cover.xhtml" + + local name = specification.name or nameonly(filename) + local identifier = specification.identifier or "" + local htmlfiles = specification.htmlfiles or { } + local styles = specification.styles or { } + local images = specification.images or { } + local htmlroot = specification.htmlroot or htmlfiles[1] or "" + local language = specification.language or "en" + local creator = specification.creator or "context mkiv" + local author = specification.author or "anonymous" + local title = specification.title or name + local subtitle = specification.subtitle or "" + local imagefile = specification.imagefile or "" + local imagepath = specification.imagepath or "images" + local stylepath = specification.stylepath or "styles" + local coverpage = specification.firstpage or defaultcoverpage + + if type(svgstyle) == "string" and not svgstyle then + svgstyle = specification.svgstyle or "" + end - local name = specification.name or file.removesuffix(filename) - local identifier = specification.identifier or os.uuid(true) - local files = specification.files or { file.addsuffix(filename,"xhtml") } - local images = specification.images or { } - local root = specification.root or files[1] - local language = specification.language or "en" - local creator = specification.author or "My Self" - local title = specification.title or "My Title" - local firstpage = specification.firstpage or "" - local lastpage = specification.lastpage or "" + local obsolete = false - -- identifier = gsub(identifier,"[^a-zA-z0-9]","") + if #htmlfiles == 0 then + report("no html files specified") + return + end + if htmlroot == "" then + report("no html root file specified") + return + end + + if subtitle ~= "" then + title = format("%s, %s",title,subtitle) + end + + local htmlsource = specpath + local imagesource = joinfile(specpath,imagepath) + local stylesource = joinfile(specpath,stylepath) + + -- once we're here we can start moving files to the right spot; first we deal + -- with images + + -- ["image-1"]={ + -- height = "7.056cm", + -- name = "file:///t:/sources/cow.svg", + -- page = "1", + -- width = "9.701cm", + -- } + + -- end of todo + + local pdftosvg = os.which("mudraw") and formatters[ [[mudraw -o "%s" "%s" %s]] ] + + local f_svgpage = formatters["%s-page-%s.svg"] + local f_svgname = formatters["%s.svg"] + + local notupdated = 0 + local updated = 0 + local skipped = 0 + local oldfiles = dir.glob(file.join(imagesource,"*")) + local newfiles = { } + + if not pdftosvg then + report("the %a binary is not present","mudraw") + end + + -- a coverpage file has to be in the root of the export tree + + if not coverpage then + report("no cover page (image) defined") + elseif suffix(coverpage) ~= "xhtml" then + report("using cover page %a",coverpage) + local source = coverpage + local target = joinfile(htmlsource,coverpage) + htmlfiles[#htmlfiles+1 ] = coverpage + report("copying coverpage %a to %a",source,target) + copyfile(source,target) + elseif isfile(coverpage) then + report("using cover page image %a",coverpage) + images.cover = { + height = "100%", + width = "100%", + page = "1", + name = url.filename(coverpage), + used = coverpage, + } + local data = replacetemplate(t_coverxhtml, { + content = replacetemplate(t_coverimg, { + image = coverpage, + }) + }) + coverpage = defaultcoverpage + local target = joinfile(htmlsource,coverpage) + report("saving coverpage to %a",target) + io.savedata(target,data) + htmlfiles[#htmlfiles+1 ] = coverpage + else + report("cover page image %a is not present",coverpage) + coverpage = false + end + + if not coverpage then + local data = replacetemplate(t_coverxhtml, { + content = "no cover page" + }) + coverpage = defaultcoverpage + local target = joinfile(htmlsource,coverpage) + report("saving dummy coverpage to %a",target) + io.savedata(target,data) + htmlfiles[#htmlfiles+1 ] = coverpage + end - if firstpage ~= "" then - images[firstpage] = firstpage + for id, data in sortedhash(images) do + local name = url.filename(data.name) + local used = url.filename(data.used) + local base = basename(used) + local page = tonumber(data.page) or 1 + -- todo : check timestamp and prefix, rename to image-* + if suffix(used) == "pdf" then + -- todo: pass svg name + if page > 1 then + name = f_svgpage(nameonly(name),page) + else + name = f_svgname(nameonly(name)) + end + local source = used + local target = joinfile(imagesource,name) + if needsupdating(source,target) then + if pdftosvg then + local command = pdftosvg(target,source,page) + report("running command %a",command) + os.execute(command) + updated = updated + 1 + else + skipped = skipped + 1 + end + else + notupdated = notupdated + 1 + end + newfiles[#newfiles+1] = target + else + name = basename(used) + local source = used + local target = joinfile(imagesource,name) + if needsupdating(source,target) then + report("copying %a to %a",source,target) + copyfile(source,target) + updated = updated + 1 + else + notupdated = notupdated + 1 + -- no message + end + newfiles[#newfiles+1] = target end - if lastpage ~= "" then - images[lastpage] = lastpage + local target = newfiles[#newfiles] + if suffix(target) == "svg" and isfile(target) then + local data = io.loaddata(target) + if data then + local done = gsub(data,"<!(DOCTYPE.-)>","<!-- %1 -->",1) + if data ~= done then + report("doctype fixed in %a",target) + io.savedata(target,data) + end + end end + data.newname = name -- without path + end + + report("%s images checked, %s updated, %s kept, %s skipped",updated + notupdated + skipped,updated,notupdated,skipped) - identifier = "BookId" -- weird requirement - - local epubname = name - local epubpath = file.replacesuffix(name,"tree") - local epubfile = file.replacesuffix(name,"epub") - local epubroot = file.replacesuffix(name,"opf") - local epubtoc = "toc.ncx" - local epubcover = "cover.xhtml" - - application.report("creating paths in tree %s",epubpath) - lfs.mkdir(epubpath) - lfs.mkdir(file.join(epubpath,"META-INF")) - lfs.mkdir(file.join(epubpath,"OEBPS")) - - local used = { } - - local function copyone(filename) - local suffix = file.suffix(filename) - local mime = mimetypes[suffix] - if mime then - local idmaker = idmakers[suffix] or idmakers.default - local target = file.join(epubpath,"OEBPS",filename) - file.copy(filename,target) - application.report("copying %s to %s",filename,target) - used[#used+1] = format(item,idmaker(filename),filename,mime) + if reportobsolete(oldfiles,newfiles,purge) then + obsolete = true + end + + -- here we can decide not to make an epub + + local uuid = format("urn:uuid:%s",os.uuid(true)) -- os.uuid() + local identifier = "bookid" -- for now + + local epubname = removesuffix(name) + local epubpath = name .. "-epub" + local epubfile = replacesuffix(name,"epub") + local epubroot = replacesuffix(name,"opf") + local epubtoc = "toc.ncx" + local epubmimetypes = "mimetype" + local epubcontainer = "container.xml" + local epubnavigator = "nav.xhtml" + + local metapath = "META-INF" + local datapath = "OEBPS" + + local oldfiles = dir.glob(file.join(epubpath,"**/*")) + local newfiles = { } + + report("creating paths in tree %a",epubpath) + + if not isdir(epubpath) then + mkdir(epubpath) + end + if not isdir(epubpath) then + report("unable to create path %a",epubpath) + return + end + + local metatarget = joinfile(epubpath,metapath) + local htmltarget = joinfile(epubpath,datapath) + local styletarget = joinfile(epubpath,datapath,stylepath) + local imagetarget = joinfile(epubpath,datapath,imagepath) + + mkdir(metatarget) + mkdir(htmltarget) + mkdir(styletarget) + mkdir(imagetarget) + + local used = { } + local notupdated = 0 + local updated = 0 + + local oldimagespecification = joinfile(htmlsource,imagefile) + local newimagespecification = joinfile(htmltarget,imagefile) + + report("removing %a",newimagespecification) + -- removefile(newimagespecification) -- because we update that one + + local function registerone(path,filename,mathml) + local suffix = suffix(filename) + local mime = mimetypes[suffix] + if mime then + local idmaker = idmakers[suffix] or idmakers.default + local fullname = path and joinfile(path,filename) or filename + if mathml then + used[#used+1] = replacetemplate(t_prop, { + id = idmaker(filename), + filename = fullname, + mime = mime, + properties = "mathml", + } ) + else + used[#used+1] = replacetemplate(t_item, { + id = idmaker(filename), + filename = fullname, + mime = mime, + } ) end + return true + end + end + + local function registerandcopyfile(check,path,name,sourcepath,targetpath,newname,image) + + if name == "" then + report("ignoring unknown image") + return + end + + if newname then + newname = replacesuffix(newname,suffix(name)) + else + newname = name end - copyone("cover.xhtml") - copyone("toc.ncx") + local source = joinfile(sourcepath,name) + local target = joinfile(targetpath,newname) + local mathml = false - local function copythem(files) - for i=1,#files do - local filename = files[i] - if type(filename) == "string" then - copyone(filename) + if suffix(source) == "xhtml" then + if find(io.loaddata(source),"MathML") then + mathml = true -- inbelievable: the property is only valid when there is mathml + end + else + report("checking image %a -> %a",source,target) + end + if registerone(path,newname,mathml) then + if not check or needsupdating(source,target) or mathml and svgmath then + report("copying %a to %a",source,target) + copyfile(source,target) + updated = updated + 1 + else + notupdated = notupdated + 1 + end + newfiles[#newfiles+1] = target + if mathml and svgmath then + report() + report("converting mathml into svg in %a",target) + report() + local status, total, unique = moduledata.svgmath.convert(target,svgstyle) + report() + if status then + report("%s formulas converted, %s are unique",total,unique) + else + report("warning: %a in %a",total,target) end + report() end end + end - copythem(files) + -- local nofdummies = 0 + -- local dummyname = formatters["dummy-figure-%03i"] + -- local makedummy = formatters["context --extra=dummies --noconsole --once --result=%s"] + -- + -- local function registerandcopydummy(targetpath,name) + -- nofdummies = nofdummies + 1 + -- local newname = dummyname(nofdummies) + -- local target = joinfile(targetpath,newname) + -- if not isfile(target) then + -- pushdir(targetpath) + -- report("generating dummy %a for %a",newname,name or "unknown") + -- os.execute(makedummy(newname)) + -- popdir() + -- end + -- return newname + -- end + + for image, data in sortedhash(images) do + -- if data.used == "" then + -- data.newname = registerandcopydummy(imagetarget,data.name) + -- end + registerandcopyfile(true,imagepath,data.newname,imagesource,imagetarget,rename and image,true) + end + for i=1,#styles do + registerandcopyfile(false,stylepath,styles[i],stylesource,styletarget) + end + for i=1,#htmlfiles do + registerandcopyfile(false,false,htmlfiles[i],htmlsource,htmltarget) + end - local theimages = { } + relocateimages(images,oldimagespecification,oldimagespecification,imagepath,rename) + relocateimages(images,oldimagespecification,newimagespecification,imagepath,rename) - for k, v in table.sortedpairs(images) do - theimages[#theimages+1] = k - if not lfs.isfile(k) and file.suffix(k) == "svg" and file.suffix(v) == "pdf" then - local command = format("inkscape --export-plain-svg=%s %s",k,v) - application.report("running command '%s'\n\n",command) - os.execute(command) - end + report("%s files registered, %s updated, %s kept",updated + notupdated,updated,notupdated) + + local function saveinfile(what,name,data) + report("saving %s in %a",what,name) + io.savedata(name,data) + newfiles[#newfiles+1] = name + end + + used[#used+1] = replacetemplate(t_prop, { + id = "nav", + filename = epubnavigator, + properties = "nav", + mime = "application/xhtml+xml", + }) + + registerone(false,epubtoc) + + saveinfile("navigation data",joinfile(htmltarget,epubnavigator),replacetemplate(t_navtoc, { -- version 3.0 + root = htmlroot, + } ) ) + + saveinfile("used mimetypes",joinfile(epubpath,epubmimetypes),mimetype) + + saveinfile("version 2.0 container",joinfile(metatarget,epubcontainer),replacetemplate(t_container, { + rootfile = epubroot + } ) ) + + local idmaker = idmakers[suffix(htmlroot)] or idmakers.default + + saveinfile("package specification",joinfile(htmltarget,epubroot),replacetemplate(t_package, { + identifier = identifier, + title = title, + language = language, + uuid = uuid, + creator = creator, + date = os.date("!%Y-%m-%dT%H:%M:%SZ"), + coverpage = idmaker(coverpage), + manifest = concat(used,"\n"), + rootfile = idmaker(htmlroot) + } ) ) + + -- t_toc is replaced by t_navtoc in >= 3 + + saveinfile("table of contents",joinfile(htmltarget,epubtoc), replacetemplate(t_toc, { + identifier = uuid, -- identifier, + title = title, + author = author, + root = htmlroot, + } ) ) + + report("creating archive\n\n") + + pushdir(epubpath) + + removefile(epubfile) + + local usedzipper = false + + local function zipped(zipper) + local ok = os.execute(format(zipper.uncompressed,epubfile,epubmimetypes)) + if ok == 0 then + os.execute(format(zipper.compressed,epubfile,metapath)) + os.execute(format(zipper.compressed,epubfile,datapath)) + usedzipper = zipper.name + return true end + end + + -- nice way + + for i=1,#zippers do + if os.which(zippers[i].binary) and zipped(zippers[i]) then + break + end + end - copythem(theimages) - - local idmaker = idmakers[file.suffix(root)] or idmakers.default - - container = format(container, - epubroot - ) - package = format(package, - identifier, - title, - language, - identifier, - os.uuid(), - creator, - os.date("!%Y-%m-%dT%H:%M:%SZ"), - idmaker(firstpage), - concat(used,"\n"), - idmaker(root) - ) - toc = format(toc, - identifier, - title, - root - ) - coverxhtml = format(coverxhtml, - firstpage - ) - - io.savedata(file.join(epubpath,"mimetype"),mimetype) - io.savedata(file.join(epubpath,"META-INF","container.xml"),container) - io.savedata(file.join(epubpath,"OEBPS",epubroot),package) - io.savedata(file.join(epubpath,"OEBPS",epubtoc),toc) - io.savedata(file.join(epubpath,"OEBPS",epubcover),coverxhtml) - - application.report("creating archive\n\n") - - lfs.chdir(epubpath) - os.remove(epubfile) - - local done = false + -- trial and error + if not usedzipper then for i=1,#zippers do - local zipper = zippers[i] - if os.execute(format(zipper.uncompressed,epubfile,"mimetype")) then - os.execute(format(zipper.compressed,epubfile,"META-INF")) - os.execute(format(zipper.compressed,epubfile,"OEBPS")) - done = zipper.name + if zipped(zippers[i]) then break end end + end - lfs.chdir("..") + popdir() - if done then - application.report("epub archive made using %s: %s",done,file.join(epubpath,epubfile)) - else - local list = { } - for i=1,#zippers do - list[#list+1] = zipper.name - end - application.report("no epub archive made, install one of: %s",concat(list," ")) + if usedzipper then + local treefile = joinfile(epubpath,epubfile) + removefile(epubfile) + copyfile(treefile,epubfile) + if isfile(epubfile) then + removefile(treefile) end + report("epub archive made using %s: %s",usedzipper,epubfile) + else + local list = { } + for i=1,#zippers do + list[#list+1] = zippers[i].name + end + report("no epub archive made, install one of: % | t",list) + end + + if reportobsolete(oldfiles,newfiles,purge) then + obsolete = true + end + if obsolete and not purge then + report("use --purge to remove obsolete files") end end -- -if environment.argument("make") then - scripts.epub.make() -elseif environment.argument("exporthelp") then - application.export(environment.argument("exporthelp"),environment.files[1]) +local a_exporthelp = environment.argument("exporthelp") +local a_make = environment.argument("make") +local a_all = environment.argument("all") +local a_purge = a_all or environment.argument("purge") +local a_rename = a_all or environment.argument("rename") +local a_svgmath = a_all or environment.argument("svgmath") +local a_svgstyle = environment.argument("svgstyle") + +if a_make and a_svgmath then + require("x-math-svg") +end + +if a_make then + scripts.epub.make(a_purge,a_rename,a_svgmath,a_svgstyle) +elseif a_exporthelp then + application.export(a_exporthelp,environment.files[1]) else application.help() end + +-- java -jar d:\epubcheck\epubcheck-3.0.1.jar -v 3.0 -mode xhtml mkiv-publications.tree\mkiv-publications.epub diff --git a/scripts/context/lua/mtx-fcd.lua b/scripts/context/lua/mtx-fcd.lua index 2fcb9a2c7..76087cc37 100644 --- a/scripts/context/lua/mtx-fcd.lua +++ b/scripts/context/lua/mtx-fcd.lua @@ -247,7 +247,7 @@ end local function fcd_find() found = { } - pattern = environment.files[1] or "" + pattern = lower(environment.files[1] or "") if pattern ~= "" then pattern = string.escapedpattern(pattern) local paths = hash.paths @@ -255,7 +255,7 @@ local function fcd_find() local paths = paths[i][2] for i=1,#paths do local path = paths[i] - if find(path,pattern) then + if find(lower(path),pattern) then found[#found+1] = path end end diff --git a/scripts/context/lua/mtx-flac.lua b/scripts/context/lua/mtx-flac.lua index 4e01abc99..116eeda34 100644 --- a/scripts/context/lua/mtx-flac.lua +++ b/scripts/context/lua/mtx-flac.lua @@ -8,7 +8,7 @@ if not modules then modules = { } end modules ['mtx-flac'] = { local sub, match, byte, lower = string.sub, string.match, string.byte, string.lower local readstring, readnumber = io.readstring, io.readnumber -local concat, sortedpairs = table.concat, table.sortedpairs +local concat, sortedpairs, sort, keys = table.concat, table.sortedpairs, table.sort, table.keys local tonumber = tonumber local tobitstring = number.tobitstring local lpegmatch = lpeg.match @@ -105,35 +105,62 @@ function flac.savecollection(pattern,filename) local files = dir.glob(pattern) flac.report("%s files found, analyzing files",#files) local music = { } - table.sort(files) + sort(files) for i=1,#files do - local data = flac.getmetadata(files[i]) + local name = files[i] + local data = flac.getmetadata(name) if data then local tags = data.tags local info = data.info - local artist = tags.artist or "no-artist" - local album = tags.album or "no-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 = { }, + if tags and info then + local artist = tags.artist or "no-artist" + local album = tags.album or "no-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)), } - albums[album] = albumx + else + flac.report("unable to read file",name) 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 nofartists = 0 + local nofalbums = 0 + local noftracks = 0 + local noferrors = 0 + -- + local allalbums + local function compare(a,b) + local ya = allalbums[a].year or 0 + local yb = allalbums[b].year or 0 + if ya == yb then + return a < b + else + return ya < yb + end + end + local function getlist(albums) + allalbums = albums + local list = keys(albums) + sort(list,compare) + return list + end + -- + filename = file.addsuffix(filename,"xml") local f = io.open(filename,"wb") if f then flac.report("saving data in file %q",filename) @@ -144,15 +171,8 @@ function flac.savecollection(pattern,filename) f:write("\t<artist>\n") f:write("\t\t<name>",lpegmatch(p_escaped,artist),"</name>\n") f:write("\t\t<albums>\n") - local list = table.keys(albums) - table.sort(list,function(a,b) - local ya, yb = albums[a].year or 0, albums[b].year or 0 - if ya == yb then - return a < b - else - return ya < yb - end - end) + local list = getlist(albums) + nofalbums = nofalbums + #list for nofalbums=1,#list do local album = list[nofalbums] local data = albums[album] @@ -180,6 +200,40 @@ function flac.savecollection(pattern,filename) f:write("</collection>\n") f:close() flac.report("%s tracks of %s albums of %s artists saved in %q (%s errors)",noftracks,nofalbums,nofartists,filename,noferrors) + -- a secret option for alan braslau + if environment.argument("bibtex") then + filename = file.replacesuffix(filename,"bib") + local f = io.open(filename,"wb") + if f then + local n = 0 + for artist, albums in sortedpairs(music) do + local list = getlist(albums) + for nofalbums=1,#list do + n = n + 1 + local album = list[nofalbums] + local data = albums[album] + local tracks = data.tracks + f:write("@cd{entry-",n,",\n") + f:write("\tartist = {",artist,"},\n") + f:write("\ttitle = {",album or "no title","},\n") + f:write("\tyear = {",data.year or 0,"},\n") + f:write("\ttracks = {",#tracks,"},\n") + for i=1,#tracks do + local track = tracks[i] + if track then + noftracks = noftracks + 1 + f:write("\ttrack:",i," = {",track.title,"},\n") + f:write("\tlength:",i," = {",track.length,"},\n") + end + end + f:write("}\n") + end + end + f:close() + flac.report("additional bibtex file generated: %s",filename) + end + end + -- else flac.report("unable to save data in file %q",filename) end diff --git a/scripts/context/lua/mtx-fonts.lua b/scripts/context/lua/mtx-fonts.lua index 4340cb357..694e6a649 100644 --- a/scripts/context/lua/mtx-fonts.lua +++ b/scripts/context/lua/mtx-fonts.lua @@ -38,7 +38,7 @@ local helpinfo = [[ <flag name="filter" value="list"><short>key-value pairs</short></flag> <flag name="all"><short>show all found instances (combined with other flags)</short></flag> <flag name="info"><short>give more details</short></flag> - <flag name="track" value="list"><short>enable trackers</short></flag> + <flag name="trackers" value="list"><short>enable trackers</short></flag> <flag name="statistics"><short>some info about the database</short></flag> </subcategory> </category> @@ -413,8 +413,12 @@ function scripts.fonts.save() local sub = givenfiles[2] or "" local function save(savename,fontblob) if fontblob then + if fontblob.validation_state and table.contains(fontblob.validation_state,"bad_ps_fontname") then + report("ignoring bad fontname for %a",name) + savename = file.nameonly(name) .. "-bad-ps-name" + end savename = file.addsuffix(string.lower(savename),"lua") - report("fontsave, saving data in %s",savename) + report("fontsave, saving data in %a",savename) table.tofile(savename,fontloader.to_table(fontblob),"return") fontloader.close(fontblob) end @@ -426,7 +430,7 @@ function scripts.fonts.save() if suffix == 'ttf' or suffix == 'otf' or suffix == 'ttc' or suffix == "dfont" then local fontinfo = fontloader.info(filename) if fontinfo then - report("font: %s located as %s",name,filename) + report("font: %a located as %a",name,filename) if #fontinfo > 0 then for k=1,#fontinfo do local v = fontinfo[k] @@ -436,13 +440,13 @@ function scripts.fonts.save() save(fontinfo.fullname,fontloader.open(filename)) end else - report("font: %s cannot be read",filename) + report("font: %a cannot be read",filename) end else - report("font: %s not saved",filename) + report("font: %a not saved",filename) end else - report("font: %s not found",name) + report("font: %a not found",name) end else report("font: no name given") diff --git a/scripts/context/lua/mtx-interface.lua b/scripts/context/lua/mtx-interface.lua index 82cefd638..1640f0891 100644 --- a/scripts/context/lua/mtx-interface.lua +++ b/scripts/context/lua/mtx-interface.lua @@ -248,7 +248,7 @@ function scripts.interface.editor(editor,split,forcedinterfaces) local mappings = { } local environments = { } local x = xml.load(keyfile) - for e, d, k in xml.elements(x,"cd:command") do + for e, d, k in xml.elements(x,"/cd:interface/cd:commands/cd:command") do -- somehow this was variable local at = d[k].at local name, value = at.name, at.value if name and value then @@ -256,7 +256,7 @@ function scripts.interface.editor(editor,split,forcedinterfaces) end end local x = xml.load(xmlfile) - for e, d, k in xml.elements(x,"cd:command") do + for e, d, k in xml.elements(x,"/cd:interface/cd:command") do local at = d[k].at local name, type = at.name, at["type"] if name and name ~= "" then @@ -322,7 +322,7 @@ function scripts.interface.check() if f then f:write("\\starttext\n") local x = xml.load(xmlfile) - for e, d, k in xml.elements(x,"cd:command") do + for e, d, k in xml.elements(x,"/cd:interface/cd:command") do local dk = d[k] local at = dk.at if at then @@ -384,6 +384,7 @@ function scripts.interface.interfaces() return a .. b .. c .. b end) end + -- we could just replace attributes for language, _ in next, commands.setuplayout do local texresult, xmlresult = { }, { } texresult[#texresult+1] = format("%% this file is auto-generated, don't edit this file\n%%") @@ -403,6 +404,7 @@ function scripts.interface.interfaces() 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:command', 'name', interface.commands, interface.elements, language) newdata = replace(newdata, 'cd:string', 'value', interface.commands, interface.elements, language) newdata = replace(newdata, 'cd:variable' , 'value', interface.variables, nil, language) newdata = replace(newdata, 'cd:parameter', 'name', interface.constants, nil, language) diff --git a/scripts/context/lua/mtx-metapost.lua b/scripts/context/lua/mtx-metapost.lua index 08daec978..6306125d4 100644 --- a/scripts/context/lua/mtx-metapost.lua +++ b/scripts/context/lua/mtx-metapost.lua @@ -6,6 +6,8 @@ if not modules then modules = { } end modules ['mtx-metapost'] = { -- this was m license = "see context related readme files" } +-- todo: load map files + local helpinfo = [[ <?xml version="1.0"?> <application> @@ -60,24 +62,42 @@ local function assumes_latex(filename) return find(d,"\\documentstyle") or find(d,"\\documentclass") or find(d,"\\begin{document}") end +local basemaps = "original-base.map,original-ams-base.map,original-ams-euler.map,original-public-lm.map" + +local wrapper = "\\starttext\n%s\n%s\\stoptext" +local loadmap = "\\loadmapfile[%s]\n" 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) +local function do_mapfiles(mapfiles) + local maps = { } + for i=1,#mapfiles do + local mapfile = mapfiles[i] + application.report("using map file %a",mapfile) + maps[i] = format(loadmap,mapfile) + end + return table.concat(maps) +end + +local function do_convert(filename,mapfiles) if find(filename,".%d+$") or find(filename,"%.mps$") then - io.savedata(tempname,format(template,filename)) + local body = format(template,filename) + local maps = do_mapfiles(mapfiles) + io.savedata(tempname,format(wrapper,maps,body)) local resultname = format("%s-%s.pdf",file.nameonly(filename),file.suffix(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 function do_split(filename,numbers,mapfiles) local name = file.nameonly(filename) + local maps = do_mapfiles(mapfiles) for i=1,#numbers do - io.savedata(tempname,format(splitter,file.addsuffix(name,"pdf"),i)) + local body = format(splitter,file.addsuffix(name,"pdf"),i) + io.savedata(tempname,format(wrapper,maps,body)) local resultname = format("%s-%s.pdf",name,numbers[i]) local result = os.execute(format([[context --once --batch --purge --result=%s "%s"]],resultname,tempname)) end @@ -99,12 +119,12 @@ local function do_texify(str) return format(texified,str), numbers end -local function do_convert_all(filename) +local function do_convert_all(filename,mapfiles) local results = dir.glob(file.nameonly(filename) .. ".*") -- reset local report = { } for i=1,#results do local filename = results[i] - local resultname = do_convert(filename) + local resultname = do_convert(filename,mapfiles) if resultname then report[#report+1] = { filename, resultname } end @@ -121,8 +141,8 @@ local function do_convert_all(filename) end end -local function do_convert_one(filename) - local resultname = do_convert(filename) +local function do_convert_one(filename,mapfiles) + local resultname = do_convert(filename,mapfiles) if resultname then report("%s => %s", filename,resultname) else @@ -131,17 +151,13 @@ local function do_convert_one(filename) 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 + 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 = pattern and dir.glob(file.nameonly(filename)) or environment.files + local mapfiles = utilities.parsers.settings_to_array(environment.arguments.mapfiles or basemaps) if #files > 0 then for i=1,#files do local filename = files[i] @@ -168,16 +184,16 @@ function scripts.mptopdf.convertall() local done = os.execute(command) if done then if convert then - do_convert_all(filename) + do_convert_all(filename,mapfiles) elseif split then - do_split(filename,numbers) + do_split(filename,numbers,mapfiles) -- already pdf, maybe optionally split end else report("error while processing mp file '%s'", filename) end else - do_convert_one(filename) + do_convert_one(filename,mapfiles) end end else diff --git a/scripts/context/lua/mtx-mk-help.lua b/scripts/context/lua/mtx-mk-help.lua index 794bbca37..083dbc3ec 100644 --- a/scripts/context/lua/mtx-mk-help.lua +++ b/scripts/context/lua/mtx-mk-help.lua @@ -403,7 +403,7 @@ local helpinfo = [[ <application> <metadata> <entry name="name">mptopdf</entry> - <entry name="detail">convert MetaPost to PDF</entry> + <entry name="detail">convert MetaPost figures to PDF</entry> <entry name="version">1.4.1</entry> </metadata> <flags> diff --git a/scripts/context/lua/mtx-patterns.lua b/scripts/context/lua/mtx-patterns.lua index 5e2b2d902..b7d41e2b2 100644 --- a/scripts/context/lua/mtx-patterns.lua +++ b/scripts/context/lua/mtx-patterns.lua @@ -29,6 +29,7 @@ local helpinfo = [[ <flag name="specification"><short>additional patterns: e.g.: =cy,hyph-cy,welsh</short></flag> <flag name="compress"><short>compress data</short></flag> <flag name="words"><short>update words in given file</short></flag> + <flag name="hyphenate"><short>show hypephenated words</short></flag> </subcategory> </category> </flags> @@ -40,6 +41,7 @@ local helpinfo = [[ <example><command>mtxrun --script pattern --check --path=c:/data/develop/svn-hyphen/trunk/hyph-utf8/tex/generic/hyph-utf8/patterns</command></example> <example><command>mtxrun --script pattern --convert --path=c:/data/develop/svn-hyphen/trunk/hyph-utf8/tex/generic/hyph-utf8/patterns/tex --destination=e:/tmp/patterns</command></example> <example><command>mtxrun --script pattern --convert --path=c:/data/develop/svn-hyphen/trunk/hyph-utf8/tex/generic/hyph-utf8/patterns/txt --destination=e:/tmp/patterns</command></example> + <example><command>mtxrun --script pattern --hyphenate --language=nl --left=3 nogalwiedes inderdaad</command></example> </subcategory> </category> </examples> @@ -625,6 +627,49 @@ function scripts.patterns.words() end end +-- mtxrun --script patterns --hyphenate --language=nl nogalwiedes --left=3 +-- +-- hyphenator | +-- hyphenator | . n o g a l w i e d e s . . n o g a l w i e d e s . +-- hyphenator | .0n4 0 4 0 0 0 0 0 0 0 0 0 0 +-- hyphenator | 0o0g0a4l0 0 4 0 0 4 0 0 0 0 0 0 0 +-- hyphenator | 1g0a0 0 4 1 0 4 0 0 0 0 0 0 0 +-- hyphenator | 0l1w0 0 4 1 0 4 1 0 0 0 0 0 0 +-- hyphenator | 4i0e0 0 4 1 0 4 1 4 0 0 0 0 0 +-- hyphenator | 0i0e3d0e0 0 4 1 0 4 1 4 0 3 0 0 0 +-- hyphenator | 0e1d0 0 4 1 0 4 1 4 0 3 0 0 0 +-- hyphenator | 1d0e0 0 4 1 0 4 1 4 0 3 0 0 0 +-- hyphenator | 0d0e2s0 0 4 1 0 4 1 4 0 3 0 2 0 +-- hyphenator | 4s0. 0 4 1 0 4 1 4 0 3 0 4 0 +-- hyphenator | .0n4o1g0a4l1w4i0e3d0e4s0. . n o-g a l-w i e-d e s . +-- hyphenator | +-- mtx-patterns | nl 3 3 : nogalwiedes : nogal-wie-des + +function scripts.patterns.hyphenate() + require("lang-hyp") + local traditional = languages.hyphenators.traditional + local left = tonumber(environment.arguments.left) or 3 + local right = tonumber(environment.arguments.right) or 3 + local language = environment.arguments.language or "us" + local dictionary = traditional.loadpatterns(language) + local words = environment.files + local specification = { + leftcharmin = left, + rightcharmin = right, + leftchar = false, + rightchar = false, + } + trackers.enable("hyphenator.steps") + for i=1,#words do + local word = words[i] + report("%s %s %s : %s : %s", + language, left, right, + word, + traditional.injecthyphens(dictionary,word,specification) + ) + end +end + if environment.argument("check") then scripts.patterns.prepare() scripts.patterns.check() @@ -633,16 +678,21 @@ elseif environment.argument("convert") then scripts.patterns.convert() elseif environment.argument("words") then scripts.patterns.words() -- for the moment here +elseif environment.argument("hyphenate") then + scripts.patterns.hyphenate() -- for the moment here elseif environment.argument("exporthelp") then application.export(environment.argument("exporthelp"),environment.files[1]) else 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 --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 +-- 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 +-- +-- use this call: +-- +-- mtxrun --script pattern --convert --path=c:/data/develop/svn-hyphen/trunk/hyph-utf8/tex/generic/hyph-utf8/patterns/txt --destination=e:/tmp/patterns -- copy /Y *.hyp e:\tex-context\tex\texmf-context\tex\context\patterns -- copy /Y *.pat e:\tex-context\tex\texmf-context\tex\context\patterns diff --git a/scripts/context/lua/mtx-plain.lua b/scripts/context/lua/mtx-plain.lua index d10c21375..1076572fc 100644 --- a/scripts/context/lua/mtx-plain.lua +++ b/scripts/context/lua/mtx-plain.lua @@ -103,7 +103,11 @@ function scripts.plain.make(texengine,texformat) end function scripts.plain.run(texengine,texformat,filename) - execute('%s --fmt=%s "%s"',texengine,file.removesuffix(texformat),filename) + local t = { } + for k, v in next, environment.arguments do + t[#t+1] = string.format("--mtx:%s=%s",k,v) + end + execute('%s --fmt=%s %s "%s"',texengine,file.removesuffix(texformat),table.concat(t," "),filename) end function scripts.plain.fonts() @@ -114,7 +118,7 @@ local texformat = environment.arguments.texformat or environment.arguments.forma local texengine = environment.arguments.texengine or environment.arguments.engine if type(texengine) ~= "string" or texengine == "" then - texengine = environment.arguments.jit and "luajittex" or"luatex" + texengine = (jit or environment.arguments.jit) and "luajittex" or "luatex" end if type(texformat) ~= "string" or texformat == "" then diff --git a/scripts/context/lua/mtx-scite.lua b/scripts/context/lua/mtx-scite.lua index 972edbfe6..ae8c67387 100644 --- a/scripts/context/lua/mtx-scite.lua +++ b/scripts/context/lua/mtx-scite.lua @@ -6,6 +6,8 @@ if not modules then modules = { } end modules ['mtx-scite'] = { license = "see context related readme files" } +-- mtxrun --script scite --tree --source=t:/texmf/tex/context --target=e:/tmp/context --numbers + local P, R, S, C, Ct, Cf, Cc, Cg = lpeg.P, lpeg.R, lpeg.S, lpeg.C, lpeg.Ct, lpeg.Cf, lpeg.Cc, lpeg.Cg local lpegmatch = lpeg.match local format, lower, gmatch = string.format, string.lower, string.gmatch @@ -22,6 +24,8 @@ local helpinfo = [[ <category name="basic"> <subcategory> <flag name="words"><short>convert spell-*.txt into spell-*.lua</short></flag> + <flag name="tree"><short>converts a tree into an html tree (--source --target --numbers)</short></flag> + <flag name="file"><short>converts a file into an html tree (--source --target --numbers --lexer)</short></flag> </subcategory> </category> </flags> @@ -36,6 +40,8 @@ local application = logs.application { local report = application.report +local scite = require("util-sci") + scripts = scripts or { } scripts.scite = scripts.scite or { } @@ -241,6 +247,51 @@ function scripts.scite.words() report("you need to move the lua files to lexers/data") end +function scripts.scite.tree() + local source = environment.argument("source") + local target = environment.argument("target") + local numbers = environment.argument("numbers") + if not lfs.isdir(source) then + report("you need to pass a valid source path with --source") + return + end + if not lfs.isdir(target) then + report("you need to pass a valid target path with --target") + return + end + if source == target then + report("source and target paths must be different") + return + end + scite.converttree(source,target,numbers) +end + +function scripts.scite.file() + local source = environment.argument("source") + local target = environment.argument("target") + local lexer = environment.argument("lexer") + local numbers = environment.argument("numbers") + if source then + local target = target or file.replacesuffix(source,"html") + if source == target then + report("the source file cannot be the same as the target") + else + scite.filetohtml(source,lexer,target,numbers) + end + + else + for i=1,#environment.files do + local source = environment.files[i] + local target = file.replacesuffix(source,"html") + if source == target then + report("the source file cannot be the same as the target") + else + scite.filetohtml(source,nil,target,numbers) + end + end + end +end + -- if environment.argument("start") then -- scripts.scite.start(true) -- elseif environment.argument("test") then @@ -251,6 +302,10 @@ end if environment.argument("words") then scripts.scite.words() +elseif environment.argument("tree") then + scripts.scite.tree() +elseif environment.argument("file") then + scripts.scite.file() elseif environment.argument("exporthelp") then application.export(environment.argument("exporthelp"),environment.files[1]) else diff --git a/scripts/context/lua/mtx-server.lua b/scripts/context/lua/mtx-server.lua index 5466bfe80..dba07f1d5 100644 --- a/scripts/context/lua/mtx-server.lua +++ b/scripts/context/lua/mtx-server.lua @@ -278,6 +278,20 @@ handlers.html = handlers.htm local indices = { "index.htm", "index.html" } local portnumber = 31415 -- pi suits tex +local newline = lpeg.patterns.newline +local spacer = lpeg.patterns.spacer +local whitespace = lpeg.patterns.whitespace +local method = lpeg.P("GET") + + lpeg.P("POST") +local identify = (1-method)^0 + * lpeg.C(method) + * spacer^1 + * lpeg.C((1-spacer)^1) + * spacer^1 + * lpeg.P("HTTP/") + * (1-whitespace)^0 + * lpeg.C(lpeg.P(1)^0) + function scripts.webserver.run(configuration) -- check configuration configuration.port = tonumber(configuration.port or os.getenv("MTX_SERVER_PORT") or portnumber) or portnumber @@ -329,17 +343,24 @@ function scripts.webserver.run(configuration) local from = client:getpeername() report("request from: %s",tostring(from)) report("request data: %s",tostring(request)) - local fullurl = string.match(request,"GET (.+) HTTP/.*$") or "" -- todo: more clever / post - if fullurl == "" then + -- local fullurl = string.match(request,"(GET) (.+) HTTP/.*$") or "" -- todo: more clever / post + -- if fullurl == "" then +-- print("!!!!",request) + local method, fullurl, body = lpeg.match(identify,request) + if method == "" or fullurl == "" then report("no url") errormessage(client,configuration,404) else + + -- todo: method: POST + fullurl = url.unescapeget(fullurl) report("requested url: %s",fullurl) -- fullurl = socket.url.unescape(fullurl) -- happens later local hashed = url.hashed(fullurl) local query = url.query(hashed.query) local filename = hashed.path -- hm, not query? + hashed.body = body if script then filename = script report("forced script: %s",filename) diff --git a/scripts/context/lua/mtx-update.lua b/scripts/context/lua/mtx-update.lua index c7eb74395..daf4f5b16 100644 --- a/scripts/context/lua/mtx-update.lua +++ b/scripts/context/lua/mtx-update.lua @@ -11,13 +11,15 @@ 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. +-- LuaTeX and LuajitTeX are now always installed together. + local helpinfo = [[ <?xml version="1.0"?> <application> <metadata> <entry name="name">mtx-update</entry> <entry name="detail">ConTeXt Minimals Updater</entry> - <entry name="version">1.01</entry> + <entry name="version">1.02</entry> </metadata> <flags> <category name="basic"> @@ -48,7 +50,7 @@ local helpinfo = [[ local application = logs.application { name = "mtx-update", - banner = "ConTeXt Minimals Updater 1.01", + banner = "ConTeXt Minimals Updater 1.02", helpinfo = helpinfo, } @@ -124,7 +126,7 @@ scripts.update.engines = { ["luatex"] = { { "fonts/new/", "texmf" }, { "bin/luatex/<platform>/", "texmf-<platform>" }, - { "bin/luajittex/<platform>/","texmf-<platform>" }, + -- { "bin/luajittex/<platform>/","texmf-<platform>" }, }, ["xetex"] = { { "base/xetex/", "texmf" }, @@ -142,7 +144,7 @@ scripts.update.engines = { { "fonts/old/", "texmf" }, { "base/xetex/", "texmf" }, { "bin/luatex/<platform>/", "texmf-<platform>" }, - { "bin/luajittex/<platform>/","texmf-<platform>" }, + -- { "bin/luajittex/<platform>/","texmf-<platform>" }, { "bin/xetex/<platform>/", "texmf-<platform>" }, { "bin/pdftex/<platform>/", "texmf-<platform>" }, }, @@ -181,6 +183,8 @@ scripts.update.platforms = { ["linux-64"] = "linux-64", ["linux64"] = "linux-64", -- + ["linux-armhf"] = "linux-armhf", + -- ["freebsd"] = "freebsd", -- ["freebsd-amd64"] = "freebsd-amd64", @@ -561,9 +565,8 @@ function scripts.update.make() local formatlist = concat(table.fromhash(texformats), " ") if formatlist ~= "" then for engine in table.sortedhash(engines) do - if engine == "luatex" then + if engine == "luatex" or engine == "luajittex" then scripts.update.run(format('mtxrun --tree="%s" --script context --autogenerate --make',texroot)) - elseif engine == "luajittex" then scripts.update.run(format('mtxrun --tree="%s" --script context --autogenerate --make --engine=luajittex',texroot)) else scripts.update.run(format('mtxrun --tree="%s" --script texexec --make --all --%s %s',texroot,engine,formatlist)) diff --git a/scripts/context/lua/mtxrun.lua b/scripts/context/lua/mtxrun.lua index 0ff2d2897..edfeba8dd 100644 --- a/scripts/context/lua/mtxrun.lua +++ b/scripts/context/lua/mtxrun.lua @@ -56,7 +56,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-lua"] = package.loaded["l-lua"] or true --- original size: 3123, stripped down to: 1694 +-- original size: 3888, stripped down to: 2197 if not modules then modules={} end modules ['l-lua']={ version=1.001, @@ -136,6 +136,16 @@ function optionalrequire(...) return result end end +if lua then + lua.mask=load([[τεχ = 1]]) and "utf" or "ascii" +end +local flush=io.flush +if flush then + local execute=os.execute if execute then function os.execute(...) flush() return execute(...) end end + local exec=os.exec if exec then function os.exec (...) flush() return exec (...) end end + local spawn=os.spawn if spawn then function os.spawn (...) flush() return spawn (...) end end + local popen=io.popen if popen then function io.popen (...) flush() return popen (...) end end +end end -- of closure @@ -434,7 +444,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-lpeg"] = package.loaded["l-lpeg"] or true --- original size: 29245, stripped down to: 15964 +-- original size: 36977, stripped down to: 20349 if not modules then modules={} end modules ['l-lpeg']={ version=1.001, @@ -450,7 +460,9 @@ local byte,char,gmatch,format=string.byte,string.char,string.gmatch,string.forma local floor=math.floor local P,R,S,V,Ct,C,Cs,Cc,Cp,Cmt=lpeg.P,lpeg.R,lpeg.S,lpeg.V,lpeg.Ct,lpeg.C,lpeg.Cs,lpeg.Cc,lpeg.Cp,lpeg.Cmt local lpegtype,lpegmatch,lpegprint=lpeg.type,lpeg.match,lpeg.print -setinspector(function(v) if lpegtype(v) then lpegprint(v) return true end end) +if setinspector then + setinspector(function(v) if lpegtype(v) then lpegprint(v) return true end end) +end lpeg.patterns=lpeg.patterns or {} local patterns=lpeg.patterns local anything=P(1) @@ -469,7 +481,7 @@ local uppercase=R("AZ") local underscore=P("_") local hexdigit=digit+lowercase+uppercase local cr,lf,crlf=P("\r"),P("\n"),P("\r\n") -local newline=crlf+S("\r\n") +local newline=P("\r")*(P("\n")+P(true))+P("\n") local escaped=P("\\")*anything local squote=P("'") local dquote=P('"') @@ -491,8 +503,10 @@ patterns.utfbom_32_le=utfbom_32_le patterns.utfbom_16_be=utfbom_16_be patterns.utfbom_16_le=utfbom_16_le patterns.utfbom_8=utfbom_8 -patterns.utf_16_be_nl=P("\000\r\000\n")+P("\000\r")+P("\000\n") -patterns.utf_16_le_nl=P("\r\000\n\000")+P("\r\000")+P("\n\000") +patterns.utf_16_be_nl=P("\000\r\000\n")+P("\000\r")+P("\000\n") +patterns.utf_16_le_nl=P("\r\000\n\000")+P("\r\000")+P("\n\000") +patterns.utf_32_be_nl=P("\000\000\000\r\000\000\000\n")+P("\000\000\000\r")+P("\000\000\000\n") +patterns.utf_32_le_nl=P("\r\000\000\000\n\000\000\000")+P("\r\000\000\000")+P("\n\000\000\000") patterns.utf8one=R("\000\127") patterns.utf8two=R("\194\223")*utf8next patterns.utf8three=R("\224\239")*utf8next*utf8next @@ -519,10 +533,24 @@ patterns.spacer=spacer patterns.whitespace=whitespace patterns.nonspacer=nonspacer patterns.nonwhitespace=nonwhitespace -local stripper=spacer^0*C((spacer^0*nonspacer^1)^0) +local stripper=spacer^0*C((spacer^0*nonspacer^1)^0) +local fullstripper=whitespace^0*C((whitespace^0*nonwhitespace^1)^0) local collapser=Cs(spacer^0/""*nonspacer^0*((spacer^0/" "*nonspacer^1)^0)) +local b_collapser=Cs(whitespace^0/""*(nonwhitespace^1+whitespace^1/" ")^0) +local e_collapser=Cs((whitespace^1*P(-1)/""+nonwhitespace^1+whitespace^1/" ")^0) +local m_collapser=Cs((nonwhitespace^1+whitespace^1/" ")^0) +local b_stripper=Cs(spacer^0/""*(nonspacer^1+spacer^1/" ")^0) +local e_stripper=Cs((spacer^1*P(-1)/""+nonspacer^1+spacer^1/" ")^0) +local m_stripper=Cs((nonspacer^1+spacer^1/" ")^0) patterns.stripper=stripper +patterns.fullstripper=fullstripper patterns.collapser=collapser +patterns.b_collapser=b_collapser +patterns.m_collapser=m_collapser +patterns.e_collapser=e_collapser +patterns.b_stripper=b_stripper +patterns.m_stripper=m_stripper +patterns.e_stripper=e_stripper patterns.lowercase=lowercase patterns.uppercase=uppercase patterns.letter=patterns.lowercase+patterns.uppercase @@ -559,9 +587,12 @@ patterns.integer=sign^-1*digit^1 patterns.unsigned=digit^0*period*digit^1 patterns.float=sign^-1*patterns.unsigned patterns.cunsigned=digit^0*comma*digit^1 +patterns.cpunsigned=digit^0*(period+comma)*digit^1 patterns.cfloat=sign^-1*patterns.cunsigned +patterns.cpfloat=sign^-1*patterns.cpunsigned patterns.number=patterns.float+patterns.integer patterns.cnumber=patterns.cfloat+patterns.integer +patterns.cpnumber=patterns.cpfloat+patterns.integer patterns.oct=zero*octdigit^1 patterns.octal=patterns.oct patterns.HEX=zero*P("X")*(digit+uppercase)^1 @@ -744,7 +775,7 @@ function lpeg.replacer(one,two,makefunction,isutf) return pattern end end -function lpeg.finder(lst,makefunction) +function lpeg.finder(lst,makefunction,isutf) local pattern if type(lst)=="table" then pattern=P(false) @@ -760,7 +791,11 @@ function lpeg.finder(lst,makefunction) else pattern=P(lst) end - pattern=(1-pattern)^0*pattern + if isutf then + pattern=((utf8char or 1)-pattern)^0*pattern + else + pattern=(1-pattern)^0*pattern + end if makefunction then return function(str) return lpegmatch(pattern,str) @@ -974,37 +1009,139 @@ function lpeg.append(list,pp,delayed,checked) end return p end +local p_false=P(false) +local p_true=P(true) local function make(t) - local p + local function making(t) + local p=p_false + local keys=sortedkeys(t) + for i=1,#keys do + local k=keys[i] + if k~="" then + local v=t[k] + if v==true then + p=p+P(k)*p_true + elseif v==false then + else + p=p+P(k)*making(v) + end + end + end + if t[""] then + p=p+p_true + end + return p + end + local p=p_false local keys=sortedkeys(t) for i=1,#keys do local k=keys[i] - local v=t[k] - if not p then - if next(v) then - p=P(k)*make(v) + if k~="" then + local v=t[k] + if v==true then + p=p+P(k)*p_true + elseif v==false then else - p=P(k) + p=p+P(k)*making(v) end - else - if next(v) then - p=p+P(k)*make(v) + end + end + return p +end +local function collapse(t,x) + if type(t)~="table" then + return t,x + else + local n=next(t) + if n==nil then + return t,x + elseif next(t,n)==nil then + local k=n + local v=t[k] + if type(v)=="table" then + return collapse(v,x..k) else - p=p+P(k) + return v,x..k + end + else + local tt={} + for k,v in next,t do + local vv,kk=collapse(v,k) + tt[kk]=vv end + return tt,x end end - return p end function lpeg.utfchartabletopattern(list) local tree={} - for i=1,#list do - local t=tree - for c in gmatch(list[i],".") do - if not t[c] then - t[c]={} + local n=#list + if n==0 then + for s in next,list do + local t=tree + local p,pk + for c in gmatch(s,".") do + if t==true then + t={ [c]=true,[""]=true } + p[pk]=t + p=t + t=false + elseif t==false then + t={ [c]=false } + p[pk]=t + p=t + t=false + else + local tc=t[c] + if not tc then + tc=false + t[c]=false + end + p=t + t=tc + end + pk=c + end + if t==false then + p[pk]=true + elseif t==true then + else + t[""]=true + end + end + else + for i=1,n do + local s=list[i] + local t=tree + local p,pk + for c in gmatch(s,".") do + if t==true then + t={ [c]=true,[""]=true } + p[pk]=t + p=t + t=false + elseif t==false then + t={ [c]=false } + p[pk]=t + p=t + t=false + else + local tc=t[c] + if not tc then + tc=false + t[c]=false + end + p=t + t=tc + end + pk=c + end + if t==false then + p[pk]=true + elseif t==true then + else + t[""]=true end - t=t[c] end end return make(tree) @@ -1044,6 +1181,65 @@ local case_2=period*(digit-trailingzeros)^1*(trailingzeros/"") local number=digit^1*(case_1+case_2) local stripper=Cs((number+1)^0) lpeg.patterns.stripzeros=stripper +local byte_to_HEX={} +local byte_to_hex={} +local byte_to_dec={} +local hex_to_byte={} +for i=0,255 do + local H=format("%02X",i) + local h=format("%02x",i) + local d=format("%03i",i) + local c=char(i) + byte_to_HEX[c]=H + byte_to_hex[c]=h + byte_to_dec[c]=d + hex_to_byte[h]=c + hex_to_byte[H]=c +end +local hextobyte=P(2)/hex_to_byte +local bytetoHEX=P(1)/byte_to_HEX +local bytetohex=P(1)/byte_to_hex +local bytetodec=P(1)/byte_to_dec +local hextobytes=Cs(hextobyte^0) +local bytestoHEX=Cs(bytetoHEX^0) +local bytestohex=Cs(bytetohex^0) +local bytestodec=Cs(bytetodec^0) +patterns.hextobyte=hextobyte +patterns.bytetoHEX=bytetoHEX +patterns.bytetohex=bytetohex +patterns.bytetodec=bytetodec +patterns.hextobytes=hextobytes +patterns.bytestoHEX=bytestoHEX +patterns.bytestohex=bytestohex +patterns.bytestodec=bytestodec +function string.toHEX(s) + if not s or s=="" then + return s + else + return lpegmatch(bytestoHEX,s) + end +end +function string.tohex(s) + if not s or s=="" then + return s + else + return lpegmatch(bytestohex,s) + end +end +function string.todec(s) + if not s or s=="" then + return s + else + return lpegmatch(bytestodec,s) + end +end +function string.tobytes(s) + if not s or s=="" then + return s + else + return lpegmatch(hextobytes,s) + end +end end -- of closure @@ -1071,7 +1267,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-string"] = package.loaded["l-string"] or true --- original size: 5547, stripped down to: 2708 +-- original size: 5694, stripped down to: 2827 if not modules then modules={} end modules ['l-string']={ version=1.001, @@ -1107,11 +1303,15 @@ function string.limit(str,n,sentinel) end end local stripper=patterns.stripper +local fullstripper=patterns.fullstripper local collapser=patterns.collapser local longtostring=patterns.longtostring function string.strip(str) return lpegmatch(stripper,str) or "" end +function string.fullstrip(str) + return lpegmatch(fullstripper,str) or "" +end function string.collapsespaces(str) return lpegmatch(collapser,str) or "" end @@ -1172,7 +1372,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-table"] = package.loaded["l-table"] or true --- original size: 31113, stripped down to: 20256 +-- original size: 35724, stripped down to: 21525 if not modules then modules={} end modules ['l-table']={ version=1.001, @@ -1205,7 +1405,7 @@ end function table.keys(t) if t then local keys,k={},0 - for key,_ in next,t do + for key in next,t do k=k+1 keys[k]=key end @@ -1215,32 +1415,52 @@ function table.keys(t) end end local function compare(a,b) - local ta,tb=type(a),type(b) - if ta==tb then - return a<b - else - return tostring(a)<tostring(b) + local ta=type(a) + if ta=="number" then + local tb=type(b) + if ta==tb then + return a<b + elseif tb=="string" then + return tostring(a)<b + end + elseif ta=="string" then + local tb=type(b) + if ta==tb then + return a<b + else + return a<tostring(b) + end end + return tostring(a)<tostring(b) end local function sortedkeys(tab) if tab then local srt,category,s={},0,0 - for key,_ in next,tab do + for key in next,tab do s=s+1 srt[s]=key if category==3 then + elseif category==1 then + if type(key)~="string" then + category=3 + end + elseif category==2 then + if type(key)~="number" then + category=3 + end else local tkey=type(key) if tkey=="string" then - category=(category==2 and 3) or 1 + category=1 elseif tkey=="number" then - category=(category==1 and 3) or 2 + category=2 else category=3 end end end - if category==0 or category==3 then + if s<2 then + elseif category==3 then sort(srt,compare) else sort(srt) @@ -1250,16 +1470,52 @@ local function sortedkeys(tab) return {} end end +local function sortedhashonly(tab) + if tab then + local srt,s={},0 + for key in next,tab do + if type(key)=="string" then + s=s+1 + srt[s]=key + end + end + if s>1 then + sort(srt) + end + return srt + else + return {} + end +end +local function sortedindexonly(tab) + if tab then + local srt,s={},0 + for key in next,tab do + if type(key)=="number" then + s=s+1 + srt[s]=key + end + end + if s>1 then + sort(srt) + end + return srt + else + return {} + end +end local function sortedhashkeys(tab,cmp) if tab then local srt,s={},0 - for key,_ in next,tab do + for key in next,tab do if key then s=s+1 srt[s]=key end end - sort(srt,cmp) + if s>1 then + sort(srt,cmp) + end return srt else return {} @@ -1268,13 +1524,15 @@ end function table.allkeys(t) local keys={} for k,v in next,t do - for k,v in next,v do + for k in next,v do keys[k]=true end end return sortedkeys(keys) end table.sortedkeys=sortedkeys +table.sortedhashonly=sortedhashonly +table.sortedindexonly=sortedindexonly table.sortedhashkeys=sortedhashkeys local function nothing() end local function sortedhash(t,cmp) @@ -1285,19 +1543,21 @@ local function sortedhash(t,cmp) else s=sortedkeys(t) end - local n=0 local m=#s - local function kv(s) - if n<m then - n=n+1 - local k=s[n] - return k,t[k] + if m==1 then + return next,t + elseif m>0 then + local n=0 + return function() + if n<m then + n=n+1 + local k=s[n] + return k,t[k] + end end end - return kv,s - else - return nothing end + return nothing end table.sortedhash=sortedhash table.sortedpairs=sortedhash @@ -1439,39 +1699,36 @@ function table.fromhash(t) end return hsh end -local noquotes,hexify,handle,reduce,compact,inline,functions +local noquotes,hexify,handle,compact,inline,functions local reserved=table.tohash { 'and','break','do','else','elseif','end','false','for','function','if', 'in','local','nil','not','or','repeat','return','then','true','until','while', 'NaN','goto', } local function simple_table(t) - if #t>0 then + local nt=#t + if nt>0 then local n=0 for _,v in next,t do n=n+1 end - if n==#t then - local tt,nt={},0 - for i=1,#t do + if n==nt then + local tt={} + for i=1,nt do local v=t[i] local tv=type(v) if tv=="number" then - nt=nt+1 if hexify then - tt[nt]=format("0x%04X",v) + tt[i]=format("0x%X",v) else - tt[nt]=tostring(v) + tt[i]=tostring(v) end elseif tv=="string" then - nt=nt+1 - tt[nt]=format("%q",v) + tt[i]=format("%q",v) elseif tv=="boolean" then - nt=nt+1 - tt[nt]=v and "true" or "false" + tt[i]=v and "true" or "false" else - tt=nil - break + return nil end end return tt @@ -1490,7 +1747,7 @@ local function do_serialize(root,name,depth,level,indexed) local tn=type(name) if tn=="number" then if hexify then - handle(format("%s[0x%04X]={",depth,name)) + handle(format("%s[0x%X]={",depth,name)) else handle(format("%s[%s]={",depth,name)) end @@ -1507,7 +1764,7 @@ local function do_serialize(root,name,depth,level,indexed) end end end - if root and next(root) then + if root and next(root)~=nil then local first,last=nil,0 if compact then last=#root @@ -1525,22 +1782,19 @@ local function do_serialize(root,name,depth,level,indexed) for i=1,#sk do local k=sk[i] local v=root[k] - local tv,tk=type(v),type(k) + local tv=type(v) + local tk=type(k) if compact and first and tk=="number" and k>=first and k<=last then if tv=="number" then if hexify then - handle(format("%s 0x%04X,",depth,v)) + handle(format("%s 0x%X,",depth,v)) else handle(format("%s %s,",depth,v)) end elseif tv=="string" then - if reduce and tonumber(v) then - handle(format("%s %s,",depth,v)) - else - handle(format("%s %q,",depth,v)) - end + handle(format("%s %q,",depth,v)) elseif tv=="table" then - if not next(v) then + if next(v)==nil then handle(format("%s {},",depth)) elseif inline then local st=simple_table(v) @@ -1570,64 +1824,48 @@ local function do_serialize(root,name,depth,level,indexed) elseif tv=="number" then if tk=="number" then if hexify then - handle(format("%s [0x%04X]=0x%04X,",depth,k,v)) + handle(format("%s [0x%X]=0x%X,",depth,k,v)) else handle(format("%s [%s]=%s,",depth,k,v)) end elseif tk=="boolean" then if hexify then - handle(format("%s [%s]=0x%04X,",depth,k and "true" or "false",v)) + handle(format("%s [%s]=0x%X,",depth,k and "true" or "false",v)) else handle(format("%s [%s]=%s,",depth,k and "true" or "false",v)) end elseif noquotes and not reserved[k] and lpegmatch(propername,k) then if hexify then - handle(format("%s %s=0x%04X,",depth,k,v)) + handle(format("%s %s=0x%X,",depth,k,v)) else handle(format("%s %s=%s,",depth,k,v)) end else if hexify then - handle(format("%s [%q]=0x%04X,",depth,k,v)) + handle(format("%s [%q]=0x%X,",depth,k,v)) else handle(format("%s [%q]=%s,",depth,k,v)) end end elseif tv=="string" then - if reduce and tonumber(v) then - if tk=="number" 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,k and "true" or "false",v)) - elseif noquotes and not reserved[k] and lpegmatch(propername,k) then - handle(format("%s %s=%s,",depth,k,v)) + if tk=="number" then + if hexify then + handle(format("%s [0x%X]=%q,",depth,k,v)) else - handle(format("%s [%q]=%s,",depth,k,v)) + handle(format("%s [%s]=%q,",depth,k,v)) end + elseif tk=="boolean" then + handle(format("%s [%s]=%q,",depth,k and "true" or "false",v)) + elseif noquotes and not reserved[k] and lpegmatch(propername,k) then + handle(format("%s %s=%q,",depth,k,v)) else - if tk=="number" 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,k and "true" or "false",v)) - elseif noquotes and not reserved[k] and lpegmatch(propername,k) then - handle(format("%s %s=%q,",depth,k,v)) - else - handle(format("%s [%q]=%q,",depth,k,v)) - end + handle(format("%s [%q]=%q,",depth,k,v)) end elseif tv=="table" then - if not next(v) then + if next(v)==nil then if tk=="number" then if hexify then - handle(format("%s [0x%04X]={},",depth,k)) + handle(format("%s [0x%X]={},",depth,k)) else handle(format("%s [%s]={},",depth,k)) end @@ -1643,7 +1881,7 @@ local function do_serialize(root,name,depth,level,indexed) if st then if tk=="number" then if hexify then - handle(format("%s [0x%04X]={ %s },",depth,k,concat(st,", "))) + handle(format("%s [0x%X]={ %s },",depth,k,concat(st,", "))) else handle(format("%s [%s]={ %s },",depth,k,concat(st,", "))) end @@ -1663,7 +1901,7 @@ local function do_serialize(root,name,depth,level,indexed) elseif tv=="boolean" then if tk=="number" then if hexify then - handle(format("%s [0x%04X]=%s,",depth,k,v and "true" or "false")) + handle(format("%s [0x%X]=%s,",depth,k,v and "true" or "false")) else handle(format("%s [%s]=%s,",depth,k,v and "true" or "false")) end @@ -1679,7 +1917,7 @@ local function do_serialize(root,name,depth,level,indexed) local f=getinfo(v).what=="C" and dump(dummy) or dump(v) if tk=="number" then if hexify then - handle(format("%s [0x%04X]=load(%q),",depth,k,f)) + handle(format("%s [0x%X]=load(%q),",depth,k,f)) else handle(format("%s [%s]=load(%q),",depth,k,f)) end @@ -1694,7 +1932,7 @@ local function do_serialize(root,name,depth,level,indexed) else if tk=="number" then if hexify then - handle(format("%s [0x%04X]=%q,",depth,k,tostring(v))) + handle(format("%s [0x%X]=%q,",depth,k,tostring(v))) else handle(format("%s [%s]=%q,",depth,k,tostring(v))) end @@ -1718,7 +1956,6 @@ local function serialize(_handle,root,name,specification) 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 @@ -1735,7 +1972,6 @@ local function serialize(_handle,root,name,specification) noquotes=false hexify=false handle=_handle or print - reduce=false compact=true inline=true functions=true @@ -1748,7 +1984,7 @@ local function serialize(_handle,root,name,specification) end elseif tname=="number" then if hexify then - handle(format("[0x%04X]={",name)) + handle(format("[0x%X]={",name)) else handle("["..name.."]={") end @@ -1766,7 +2002,7 @@ local function serialize(_handle,root,name,specification) local dummy=root._w_h_a_t_e_v_e_r_ root._w_h_a_t_e_v_e_r_=nil end - if next(root) then + if next(root)~=nil then do_serialize(root,name,"",0) end end @@ -1895,14 +2131,25 @@ local function identical(a,b) end table.identical=identical table.are_equal=are_equal -function table.compact(t) - if t then - for k,v in next,t do - if not next(v) then - t[k]=nil +local function sparse(old,nest,keeptables) + local new={} + for k,v in next,old do + if not (v=="" or v==false) then + if nest and type(v)=="table" then + v=sparse(v,nest) + if keeptables or next(v)~=nil then + new[k]=v + end + else + new[k]=v end end end + return new +end +table.sparse=sparse +function table.compact(t) + return sparse(t,true,true) end function table.contains(t,v) if t then @@ -2000,15 +2247,17 @@ function table.print(t,...) serialize(print,t,...) end end -setinspector(function(v) if type(v)=="table" then serialize(print,v,"table") return true end end) +if setinspector then + setinspector(function(v) if type(v)=="table" then serialize(print,v,"table") return true end end) +end function table.sub(t,i,j) return { unpack(t,i,j) } end function table.is_empty(t) - return not t or not next(t) + return not t or next(t)==nil end function table.has_one_entry(t) - return t and not next(t,next(t)) + return t and next(t,next(t))==nil end function table.loweredkeys(t) local l={} @@ -2053,6 +2302,44 @@ function table.values(t,s) return {} end end +function table.filtered(t,pattern,sort,cmp) + if t and type(pattern)=="string" then + if sort then + local s + if cmp then + s=sortedhashkeys(t,function(a,b) return cmp(t,a,b) end) + else + s=sortedkeys(t) + end + local n=0 + local m=#s + local function kv(s) + while n<m do + n=n+1 + local k=s[n] + if find(k,pattern) then + return k,t[k] + end + end + end + return kv,s + else + local n=next(t) + local function iterator() + while n~=nil do + local k=n + n=next(t,k) + if find(k,pattern) then + return k,t[k] + end + end + end + return iterator,t + end + else + return nothing + end +end end -- of closure @@ -2061,7 +2348,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-io"] = package.loaded["l-io"] or true --- original size: 8817, stripped down to: 6340 +-- original size: 8643, stripped down to: 6232 if not modules then modules={} end modules ['l-io']={ version=1.001, @@ -2075,7 +2362,7 @@ local byte,find,gsub,format=string.byte,string.find,string.gsub,string.format local concat=table.concat local floor=math.floor local type=type -if string.find(os.getenv("PATH"),";") then +if string.find(os.getenv("PATH"),";",1,true) then io.fileseparator,io.pathseparator="\\",";" else io.fileseparator,io.pathseparator="/",":" @@ -2368,8 +2655,6 @@ function io.readstring(f,n,m) local str=gsub(f:read(n),"\000","") return str end -if not io.i_limiter then function io.i_limiter() end end -if not io.o_limiter then function io.o_limiter() end end end -- of closure @@ -2596,7 +2881,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-os"] = package.loaded["l-os"] or true --- original size: 16023, stripped down to: 9634 +-- original size: 15832, stripped down to: 9456 if not modules then modules={} end modules ['l-os']={ version=1.001, @@ -2670,13 +2955,10 @@ if not os.__getenv__ then setmetatable(os.env,{ __index=__index,__newindex=__newindex } ) end end -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 +local execute=os.execute +local iopopen=io.popen function os.resultof(command) - local handle=io.popen(command,"r") + local handle=iopopen(command,"r") if handle then local result=handle:read("*all") or "" handle:close() @@ -2686,7 +2968,7 @@ function os.resultof(command) end end if not io.fileseparator then - if find(os.getenv("PATH"),";") then + if find(os.getenv("PATH"),";",1,true) then io.fileseparator,io.pathseparator,os.type="\\",";",os.type or "mswin" else io.fileseparator,io.pathseparator,os.type="/",":",os.type or "unix" @@ -2705,7 +2987,7 @@ local launchers={ unix="$BROWSER %s &> /dev/null &", } function os.launch(str) - os.execute(format(launchers[os.name] or launchers.unix,str)) + execute(format(launchers[os.name] or launchers.unix,str)) end if not os.times then function os.times() @@ -2746,7 +3028,7 @@ if platform~="" then elseif os.type=="windows" then function resolvers.platform(t,k) local platform,architecture="",os.getenv("PROCESSOR_ARCHITECTURE") or "" - if find(architecture,"AMD64") then + if find(architecture,"AMD64",1,true) then platform="win64" else platform="mswin" @@ -2758,9 +3040,9 @@ elseif os.type=="windows" then elseif name=="linux" then function resolvers.platform(t,k) local platform,architecture="",os.getenv("HOSTTYPE") or os.resultof("uname -m") or "" - if find(architecture,"x86_64") then + if find(architecture,"x86_64",1,true) then platform="linux-64" - elseif find(architecture,"ppc") then + elseif find(architecture,"ppc",1,true) then platform="linux-ppc" else platform="linux" @@ -2774,9 +3056,9 @@ elseif name=="macosx" then local platform,architecture="",os.resultof("echo $HOSTTYPE") or "" if architecture=="" then platform="osx-intel" - elseif find(architecture,"i386") then + elseif find(architecture,"i386",1,true) then platform="osx-intel" - elseif find(architecture,"x86_64") then + elseif find(architecture,"x86_64",1,true) then platform="osx-64" else platform="osx-ppc" @@ -2788,7 +3070,7 @@ elseif name=="macosx" then elseif name=="sunos" then function resolvers.platform(t,k) local platform,architecture="",os.resultof("uname -m") or "" - if find(architecture,"sparc") then + if find(architecture,"sparc",1,true) then platform="solaris-sparc" else platform="solaris-intel" @@ -2800,7 +3082,7 @@ elseif name=="sunos" then elseif name=="freebsd" then function resolvers.platform(t,k) local platform,architecture="",os.resultof("uname -m") or "" - if find(architecture,"amd64") then + if find(architecture,"amd64",1,true) then platform="freebsd-amd64" else platform="freebsd" @@ -2812,7 +3094,7 @@ elseif name=="freebsd" then elseif name=="kfreebsd" then function resolvers.platform(t,k) local platform,architecture="",os.getenv("HOSTTYPE") or os.resultof("uname -m") or "" - if find(architecture,"x86_64") then + if find(architecture,"x86_64",1,true) then platform="kfreebsd-amd64" else platform="kfreebsd-i386" @@ -2829,8 +3111,9 @@ else return platform end end +os.newline=name=="windows" and "\013\010" or "\010" function resolvers.bits(t,k) - local bits=find(os.platform,"64") and 64 or 32 + local bits=find(os.platform,"64",1,true) and 64 or 32 os.bits=bits return bits end @@ -2980,7 +3263,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-file"] = package.loaded["l-file"] or true --- original size: 18308, stripped down to: 9948 +-- original size: 20949, stripped down to: 9945 if not modules then modules={} end modules ['l-file']={ version=1.001, @@ -2994,41 +3277,28 @@ local file=file if not lfs then lfs=optionalrequire("lfs") end -if not lfs then - lfs={ - getcurrentdir=function() - return "." - end, - attributes=function() - return nil - end, - isfile=function(name) - local f=io.open(name,'rb') - if f then - f:close() - return true - end - end, - isdir=function(name) - print("you need to load lfs") - return false - end - } -elseif not lfs.isfile then - local attributes=lfs.attributes - function lfs.isdir(name) - return attributes(name,"mode")=="directory" - end - function lfs.isfile(name) - return attributes(name,"mode")=="file" - end -end local insert,concat=table.insert,table.concat local match,find,gmatch=string.match,string.find,string.gmatch local lpegmatch=lpeg.match local getcurrentdir,attributes=lfs.currentdir,lfs.attributes local checkedsplit=string.checkedsplit local P,R,S,C,Cs,Cp,Cc,Ct=lpeg.P,lpeg.R,lpeg.S,lpeg.C,lpeg.Cs,lpeg.Cp,lpeg.Cc,lpeg.Ct +local tricky=S("/\\")*P(-1) +local attributes=lfs.attributes +if sandbox then + sandbox.redefine(lfs.isfile,"lfs.isfile") + sandbox.redefine(lfs.isdir,"lfs.isdir") +end +function lfs.isdir(name) + if lpegmatch(tricky,name) then + return attributes(name,"mode")=="directory" + else + return attributes(name.."/.","mode")=="directory" + end +end +function lfs.isfile(name) + return attributes(name,"mode")=="file" +end local colon=P(":") local period=P(".") local periods=P("..") @@ -3230,28 +3500,30 @@ local isroot=fwslash^1*-1 local hasroot=fwslash^1 local reslasher=lpeg.replacer(S("\\/"),"/") local deslasher=lpeg.replacer(S("\\/")^1,"/") -function file.join(...) - local lst={... } - local one=lst[1] +function file.join(one,two,three,...) + if not two then + return one=="" and one or lpegmatch(stripper,one) + end + if one=="" then + return lpegmatch(stripper,three and concat({ two,three,... },"/") or two) + end if lpegmatch(isnetwork,one) then local one=lpegmatch(reslasher,one) - local two=lpegmatch(deslasher,concat(lst,"/",2)) + local two=lpegmatch(deslasher,three and concat({ two,three,... },"/") or two) if lpegmatch(hasroot,two) then return one..two else return one.."/"..two end elseif lpegmatch(isroot,one) then - local two=lpegmatch(deslasher,concat(lst,"/",2)) + local two=lpegmatch(deslasher,three and concat({ two,three,... },"/") or two) if lpegmatch(hasroot,two) then return two else return "/"..two end - elseif one=="" then - return lpegmatch(stripper,concat(lst,"/",2)) else - return lpegmatch(deslasher,concat(lst,"/")) + return lpegmatch(deslasher,concat({ one,two,three,... },"/")) end end local drivespec=R("az","AZ")^1*colon @@ -3425,7 +3697,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-md5"] = package.loaded["l-md5"] or true --- original size: 3760, stripped down to: 2088 +-- original size: 3248, stripped down to: 2266 if not modules then modules={} end modules ['l-md5']={ version=1.001, @@ -3443,14 +3715,20 @@ if not md5 then } end local md5,file=md5,file -local gsub,format,byte=string.gsub,string.format,string.byte -local md5sum=md5.sum -local function convert(str,fmt) - return (gsub(md5sum(str),".",function(chr) return format(fmt,byte(chr)) end)) -end -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 +local gsub=string.gsub +do + local patterns=lpeg and lpeg.patterns + if patterns then + local bytestoHEX=patterns.bytestoHEX + local bytestohex=patterns.bytestohex + local bytestodec=patterns.bytestodec + local lpegmatch=lpeg.match + local md5sum=md5.sum + if not md5.HEX then function md5.HEX(str) if str then return lpegmatch(bytestoHEX,md5sum(str)) end end end + if not md5.hex then function md5.hex(str) if str then return lpegmatch(bytestohex,md5sum(str)) end end end + if not md5.dec then function md5.dec(str) if str then return lpegmatch(bytestodec,md5sum(str)) end end end + end +end function file.needsupdating(oldname,newname,threshold) local oldtime=lfs.attributes(oldname,"modification") if oldtime then @@ -3507,7 +3785,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-url"] = package.loaded["l-url"] or true --- original size: 11993, stripped down to: 5584 +-- original size: 12531, stripped down to: 5721 if not modules then modules={} end modules ['l-url']={ version=1.001, @@ -3534,7 +3812,7 @@ local hexdigit=R("09","AF","af") local plus=P("+") local nothing=Cc("") local escapedchar=(percent*C(hexdigit*hexdigit))/tochar -local escaped=(plus/" ")+escapedchar +local escaped=(plus/" ")+escapedchar local noslash=P("/")/"" local schemestr=Cs((escaped+(1-colon-slash-qmark-hash))^2) local authoritystr=Cs((escaped+(1- slash-qmark-hash))^0) @@ -3593,19 +3871,25 @@ local splitquery=Cf (Ct("")*P { "sequence", pair=Cg(key*equal*value), },rawset) local function hashed(str) - if str=="" then + if not str or str=="" then return { scheme="invalid", original=str, } end - local s=split(str) - local rawscheme=s[1] - local rawquery=s[4] - local somescheme=rawscheme~="" - local somequery=rawquery~="" + local detailed=split(str) + local rawscheme="" + local rawquery="" + local somescheme=false + local somequery=false + if detailed then + rawscheme=detailed[1] + rawquery=detailed[4] + somescheme=rawscheme~="" + somequery=rawquery~="" + end if not somescheme and not somequery then - s={ + return { scheme="file", authority="", path=str, @@ -3615,28 +3899,28 @@ local function hashed(str) noscheme=true, filename=str, } - else - local authority,path,filename=s[2],s[3] - if authority=="" then - filename=path - elseif path=="" then - filename="" - else - filename=authority.."/"..path - end - s={ - scheme=rawscheme, - authority=authority, - path=path, - query=lpegmatch(unescaper,rawquery), - queries=lpegmatch(splitquery,rawquery), - fragment=s[5], - original=str, - noscheme=false, - filename=filename, - } end - return s + local authority=detailed[2] + local path=detailed[3] + local filename=nil + if authority=="" then + filename=path + elseif path=="" then + filename="" + else + filename=authority.."/"..path + end + return { + scheme=rawscheme, + authority=authority, + path=path, + query=lpegmatch(unescaper,rawquery), + queries=lpegmatch(splitquery,rawquery), + fragment=detailed[5], + original=str, + noscheme=false, + filename=filename, + } end url.split=split url.hasscheme=hasscheme @@ -3670,7 +3954,7 @@ function url.construct(hash) end return lpegmatch(escaper,concat(fullurl)) end -local pattern=Cs(noslash*R("az","AZ")*(S(":|")/":")*noslash*P(1)^0) +local pattern=Cs(slash^-1/""*R("az","AZ")*((S(":|")/":")+P(":"))*slash*P(1)^0) function url.filename(filename) local spec=hashed(filename) local path=spec.path @@ -3718,7 +4002,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-dir"] = package.loaded["l-dir"] or true --- original size: 14229, stripped down to: 8740 +-- original size: 16765, stripped down to: 11003 if not modules then modules={} end modules ['l-dir']={ version=1.001, @@ -3728,7 +4012,7 @@ if not modules then modules={} end modules ['l-dir']={ license="see context related readme files" } local type,select=type,select -local find,gmatch,match,gsub=string.find,string.gmatch,string.match,string.gsub +local find,gmatch,match,gsub,sub=string.find,string.gmatch,string.match,string.gsub,string.sub local concat,insert,remove,unpack=table.concat,table.insert,table.remove,table.unpack local lpegmatch=lpeg.match local P,S,R,C,Cc,Cs,Ct,Cv,V=lpeg.P,lpeg.S,lpeg.R,lpeg.C,lpeg.Cc,lpeg.Cs,lpeg.Ct,lpeg.Cv,lpeg.V @@ -3737,53 +4021,127 @@ local dir=dir local lfs=lfs local attributes=lfs.attributes local walkdir=lfs.dir -local isdir=lfs.isdir -local isfile=lfs.isfile +local isdir=lfs.isdir +local isfile=lfs.isfile local currentdir=lfs.currentdir local chdir=lfs.chdir -local onwindows=os.type=="windows" or find(os.getenv("PATH"),";") -if not isdir then - function isdir(name) - local a=attributes(name) - return a and a.mode=="directory" +local mkdir=lfs.mkdir +local onwindows=os.type=="windows" or find(os.getenv("PATH"),";",1,true) +if onwindows then + local tricky=S("/\\")*P(-1) + isdir=function(name) + if lpegmatch(tricky,name) then + return attributes(name,"mode")=="directory" + else + return attributes(name.."/.","mode")=="directory" + end + end + isfile=function(name) + return attributes(name,"mode")=="file" end lfs.isdir=isdir -end -if not isfile then - function isfile(name) - local a=attributes(name) - return a and a.mode=="file" + lfs.isfile=isfile +else + isdir=function(name) + return attributes(name,"mode")=="directory" end + isfile=function(name) + return attributes(name,"mode")=="file" + end + lfs.isdir=isdir lfs.isfile=isfile end function dir.current() return (gsub(currentdir(),"\\","/")) end -local lfsisdir=isdir -local function isdir(path) - path=gsub(path,"[/\\]+$","") - return lfsisdir(path) +local function glob_pattern_function(path,patt,recurse,action) + if isdir(path) then + local usedpath + if path=="/" then + usedpath="/." + elseif not find(path,"/$") then + usedpath=path.."/." + path=path.."/" + else + usedpath=path + end + local dirs + for name in walkdir(usedpath) do + if name~="." and name~=".." then + local full=path..name + local mode=attributes(full,'mode') + if mode=='file' then + if not patt or find(full,patt) then + action(full) + end + elseif recurse and mode=="directory" then + if not dirs then + dirs={ full } + else + dirs[#dirs+1]=full + end + end + end + end + if dirs then + for i=1,#dirs do + glob_pattern_function(dirs[i],patt,recurse,action) + end + end + end end -lfs.isdir=isdir -local function globpattern(path,patt,recurse,action) - if path=="/" then - path=path.."." - elseif not find(path,"/$") then - path=path..'/' - end - if isdir(path) then - for name in walkdir(path) do - local full=path..name - local mode=attributes(full,'mode') - if mode=='file' then - if find(full,patt) then - action(full) +local function glob_pattern_table(path,patt,recurse,result) + if not result then + result={} + end + if isdir(path) then + local usedpath + if path=="/" then + usedpath="/." + elseif not find(path,"/$") then + usedpath=path.."/." + path=path.."/" + else + usedpath=path + end + local dirs + for name in walkdir(usedpath) do + if name~="." and name~=".." then + local full=path..name + local mode=attributes(full,'mode') + if mode=='file' then + if not patt or find(full,patt) then + result[#result+1]=full + end + elseif recurse and mode=="directory" then + if not dirs then + dirs={ full } + else + dirs[#dirs+1]=full + end end - elseif recurse and (mode=="directory") and (name~='.') and (name~="..") then - globpattern(full,patt,recurse,action) + end + end + if dirs then + for i=1,#dirs do + glob_pattern_table(dirs[i],patt,recurse,result) end end end + return result +end +local function globpattern(path,patt,recurse,method) + local kind=type(method) + if patt and sub(patt,1,-3)==path then + patt=false + end + if kind=="function" then + return glob_pattern_function(path,patt,recurse,method) + elseif kind=="table" then + return glob_pattern_table(path,patt,recurse,method) + else + return glob_pattern_table(path,patt,recurse,{}) + end end dir.globpattern=globpattern local function collectpattern(path,patt,recurse,result) @@ -3795,34 +4153,40 @@ local function collectpattern(path,patt,recurse,result) ok,scanner,first=xpcall(function() return walkdir(path) end,function() end) end if ok and type(scanner)=="function" then - if not find(path,"/$") then path=path..'/' end + if not find(path,"/$") then + path=path..'/' + end for name in scanner,first do - local full=path..name - local attr=attributes(full) - local mode=attr.mode - if mode=='file' then - if find(full,patt) then + if name=="." then + elseif name==".." then + else + local full=path..name + local attr=attributes(full) + local mode=attr.mode + if mode=='file' then + if find(full,patt) then + result[name]=attr + end + elseif recurse and mode=="directory" then + attr.list=collectpattern(full,patt,recurse) result[name]=attr end - elseif recurse and (mode=="directory") and (name~='.') and (name~="..") then - attr.list=collectpattern(full,patt,recurse) - result[name]=attr end end end return result end dir.collectpattern=collectpattern -local separator -if onwindows then +local separator,pattern +if onwindows then local slash=S("/\\")/"/" - pattern=Ct { + pattern={ [1]=(Cs(P(".")+slash^1)+Cs(R("az","AZ")*P(":")*slash^0)+Cc("./"))*V(2)*V(3), [2]=Cs(((1-S("*?/\\"))^0*slash)^0), [3]=Cs(P(1)^0) } -else - pattern=Ct { +else + pattern={ [1]=(C(P(".")+P("/")^1)+Cc("./"))*V(2)*V(3), [2]=C(((1-S("*?/"))^0*P("/"))^0), [3]=C(P(1)^0) @@ -3840,10 +4204,9 @@ local function glob(str,t) elseif isfile(str) then t(str) else - local split=lpegmatch(pattern,str) - if split then - local root,path,base=split[1],split[2],split[3] - local recurse=find(base,"%*%*") + local root,path,base=lpegmatch(pattern,str) + if root and path and base then + local recurse=find(base,"**",1,true) local start=root..path local result=lpegmatch(filter,start..base) globpattern(start,result,recurse,t) @@ -3864,16 +4227,12 @@ local function glob(str,t) return { str } end else - local split=lpegmatch(pattern,str) - if split then - local t=t or {} - local action=action or function(name) t[#t+1]=name end - local root,path,base=split[1],split[2],split[3] - local recurse=find(base,"%*%*") + local root,path,base=lpegmatch(pattern,str) + if root and path and base then + local recurse=find(base,"**",1,true) local start=root..path local result=lpegmatch(filter,start..base) - globpattern(start,result,recurse,action) - return t + return globpattern(start,result,recurse,t) else return {} end @@ -3913,16 +4272,26 @@ end local make_indeed=true if onwindows then function dir.mkdirs(...) - local str,pth="","" - for i=1,select("#",...) do - local s=select(i,...) - if s=="" then - elseif str=="" then - str=s - else - str=str.."/"..s + local n=select("#",...) + local str + if n==1 then + str=select(1,...) + if isdir(str) then + return str,true + end + else + str="" + for i=1,n do + local s=select(i,...) + if s=="" then + elseif str=="" then + str=s + else + str=str.."/"..s + end end end + local pth="" local drive=false local first,middle,last=match(str,"^(//)(//*)(.*)$") if first then @@ -3957,21 +4326,30 @@ if onwindows then pth=pth.."/"..s end if make_indeed and not isdir(pth) then - lfs.mkdir(pth) + mkdir(pth) end end return pth,(isdir(pth)==true) end else function dir.mkdirs(...) - local str,pth="","" - for i=1,select("#",...) do - local s=select(i,...) - if s and s~="" then - if str~="" then - str=str.."/"..s - else - str=s + local n=select("#",...) + local str,pth + if n==1 then + str=select(1,...) + if isdir(str) then + return str,true + end + else + str="" + for i=1,n do + local s=select(i,...) + if s and s~="" then + if str~="" then + str=str.."/"..s + else + str=s + end end end end @@ -3986,7 +4364,7 @@ else pth=pth.."/"..s end if make_indeed and not first and not isdir(pth) then - lfs.mkdir(pth) + mkdir(pth) end end else @@ -3994,7 +4372,7 @@ else for s in gmatch(str,"[^/]+") do pth=pth.."/"..s if make_indeed and not isdir(pth) then - lfs.mkdir(pth) + mkdir(pth) end end end @@ -4002,47 +4380,51 @@ else end end dir.makedirs=dir.mkdirs -if onwindows then - function dir.expandname(str) - 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() +do + local chdir=sandbox and sandbox.original(chdir) or chdir + if onwindows then + local xcurrentdir=dir.current + function dir.expandname(str) + local first,nothing,last=match(str,"^(//)(//*)(.*)$") + if first then + first=xcurrentdir().."/" + end + if not first then + first,last=match(str,"^(//)/*(.*)$") + end + if not first then + first,last=match(str,"^([a-zA-Z]:)(.*)$") + if first and not find(last,"^/") then + local d=currentdir() + if chdir(first) then + first=xcurrentdir() + end + chdir(d) end - chdir(d) + end + if not first then + first,last=xcurrentdir(),str + end + last=gsub(last,"//","/") + last=gsub(last,"/%./","/") + last=gsub(last,"^/*","") + first=gsub(first,"/*$","") + if last=="" or last=="." then + return first + else + return first.."/"..last end end - 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 -else - function dir.expandname(str) - if not find(str,"^/") then - str=currentdir().."/"..str + else + function dir.expandname(str) + if not find(str,"^/") then + str=currentdir().."/"..str + end + str=gsub(str,"//","/") + str=gsub(str,"/%./","/") + str=gsub(str,"(.)/%.$","%1") + return str end - str=gsub(str,"//","/") - str=gsub(str,"/%./","/") - str=gsub(str,"(.)/%.$","%1") - return str end end file.expandname=dir.expandname @@ -4085,7 +4467,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-boolean"] = package.loaded["l-boolean"] or true --- original size: 1809, stripped down to: 1527 +-- original size: 1850, stripped down to: 1568 if not modules then modules={} end modules ['l-boolean']={ version=1.001, @@ -4139,11 +4521,11 @@ function string.booleanstring(str) return str=="yes" or str=="on" or str=="t" end end -function string.is_boolean(str,default) +function string.is_boolean(str,default,strict) if type(str)=="string" then - if str=="true" or str=="yes" or str=="on" or str=="t" or str=="1" then + if str=="true" or str=="yes" or str=="on" or str=="t" or (not strict and str=="1") then return true - elseif str=="false" or str=="no" or str=="off" or str=="f" or str=="0" then + elseif str=="false" or str=="no" or str=="off" or str=="f" or (not strict and str=="0") then return false end end @@ -4157,7 +4539,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-unicode"] = package.loaded["l-unicode"] or true --- original size: 33473, stripped down to: 14938 +-- original size: 37388, stripped down to: 15817 if not modules then modules={} end modules ['l-unicode']={ version=1.001, @@ -4173,7 +4555,9 @@ local type=type local char,byte,format,sub,gmatch=string.char,string.byte,string.format,string.sub,string.gmatch local concat=table.concat local P,C,R,Cs,Ct,Cmt,Cc,Carg,Cp=lpeg.P,lpeg.C,lpeg.R,lpeg.Cs,lpeg.Ct,lpeg.Cmt,lpeg.Cc,lpeg.Carg,lpeg.Cp -local lpegmatch,patterns=lpeg.match,lpeg.patterns +local lpegmatch=lpeg.match +local patterns=lpeg.patterns +local tabletopattern=lpeg.utfchartabletopattern local bytepairs=string.bytepairs local finder=lpeg.finder local replacer=lpeg.replacer @@ -4182,7 +4566,7 @@ local utfgmatch=utf.gmatch local p_utftype=patterns.utftype local p_utfstricttype=patterns.utfstricttype local p_utfoffset=patterns.utfoffset -local p_utf8char=patterns.utf8char +local p_utf8char=patterns.utf8character local p_utf8byte=patterns.utf8byte local p_utfbom=patterns.utfbom local p_newline=patterns.newline @@ -4321,6 +4705,7 @@ if not utf.sub then local pattern_zero=Cmt(p_utf8char,slide_zero)^0 local pattern_one=Cmt(p_utf8char,slide_one )^0 local pattern_two=Cmt(p_utf8char,slide_two )^0 + local pattern_first=C(patterns.utf8character) function utf.sub(str,start,stop) if not start then return str @@ -4362,7 +4747,9 @@ if not utf.sub then end end end - if start>stop then + if start==1 and stop==1 then + return lpegmatch(pattern_first,str) or "" + elseif start>stop then return "" elseif start>1 then b,e,n,first,last=0,0,0,start-1,stop @@ -4381,15 +4768,52 @@ if not utf.sub then end end end -function utf.remapper(mapping) - local pattern=Cs((p_utf8char/mapping)^0) - return function(str) - if not str or str=="" then - return "" +function utf.remapper(mapping,option) + local variant=type(mapping) + if variant=="table" then + if option=="dynamic" then + local pattern=false + table.setmetatablenewindex(mapping,function(t,k,v) rawset(t,k,v) pattern=false end) + return function(str) + if not str or str=="" then + return "" + else + if not pattern then + pattern=Cs((tabletopattern(mapping)/mapping+p_utf8char)^0) + end + return lpegmatch(pattern,str) + end + end + elseif option=="pattern" then + return Cs((tabletopattern(mapping)/mapping+p_utf8char)^0) else - return lpegmatch(pattern,str) + local pattern=Cs((tabletopattern(mapping)/mapping+p_utf8char)^0) + return function(str) + if not str or str=="" then + return "" + else + return lpegmatch(pattern,str) + end + end,pattern + end + elseif variant=="function" then + if option=="pattern" then + return Cs((p_utf8char/mapping+p_utf8char)^0) + else + local pattern=Cs((p_utf8char/mapping+p_utf8char)^0) + return function(str) + if not str or str=="" then + return "" + else + return lpegmatch(pattern,str) + end + end,pattern end - end,pattern + else + return function(str) + return str or "" + end + end end function utf.replacer(t) local r=replacer(t,false,false,true) @@ -4439,190 +4863,157 @@ function utf.magic(f) end local utf16_to_utf8_be,utf16_to_utf8_le local utf32_to_utf8_be,utf32_to_utf8_le -local utf_16_be_linesplitter=patterns.utfbom_16_be^-1*lpeg.tsplitat(patterns.utf_16_be_nl) -local utf_16_le_linesplitter=patterns.utfbom_16_le^-1*lpeg.tsplitat(patterns.utf_16_le_nl) -if bytepairs then - utf16_to_utf8_be=function(t) - if type(t)=="string" then - t=lpegmatch(utf_16_be_linesplitter,t) - end - local 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) - end - return t +local utf_16_be_getbom=patterns.utfbom_16_be^-1 +local utf_16_le_getbom=patterns.utfbom_16_le^-1 +local utf_32_be_getbom=patterns.utfbom_32_be^-1 +local utf_32_le_getbom=patterns.utfbom_32_le^-1 +local utf_16_be_linesplitter=utf_16_be_getbom*lpeg.tsplitat(patterns.utf_16_be_nl) +local utf_16_le_linesplitter=utf_16_le_getbom*lpeg.tsplitat(patterns.utf_16_le_nl) +local utf_32_be_linesplitter=utf_32_be_getbom*lpeg.tsplitat(patterns.utf_32_be_nl) +local utf_32_le_linesplitter=utf_32_le_getbom*lpeg.tsplitat(patterns.utf_32_le_nl) +local more=0 +local p_utf16_to_utf8_be=C(1)*C(1)/function(left,right) + local now=256*byte(left)+byte(right) + if more>0 then + now=(more-0xD800)*0x400+(now-0xDC00)+0x10000 + more=0 + return utfchar(now) + elseif now>=0xD800 and now<=0xDBFF then + more=now + return "" + else + return utfchar(now) + end +end +local p_utf16_to_utf8_le=C(1)*C(1)/function(right,left) + local now=256*byte(left)+byte(right) + if more>0 then + now=(more-0xD800)*0x400+(now-0xDC00)+0x10000 + more=0 + return utfchar(now) + elseif now>=0xD800 and now<=0xDBFF then + more=now + return "" + else + return utfchar(now) + end +end +local p_utf32_to_utf8_be=C(1)*C(1)*C(1)*C(1)/function(a,b,c,d) + return utfchar(256*256*256*byte(a)+256*256*byte(b)+256*byte(c)+byte(d)) +end +local p_utf32_to_utf8_le=C(1)*C(1)*C(1)*C(1)/function(a,b,c,d) + return utfchar(256*256*256*byte(d)+256*256*byte(c)+256*byte(b)+byte(a)) +end +p_utf16_to_utf8_be=P(true)/function() more=0 end*utf_16_be_getbom*Cs(p_utf16_to_utf8_be^0) +p_utf16_to_utf8_le=P(true)/function() more=0 end*utf_16_le_getbom*Cs(p_utf16_to_utf8_le^0) +p_utf32_to_utf8_be=P(true)/function() more=0 end*utf_32_be_getbom*Cs(p_utf32_to_utf8_be^0) +p_utf32_to_utf8_le=P(true)/function() more=0 end*utf_32_le_getbom*Cs(p_utf32_to_utf8_le^0) +patterns.utf16_to_utf8_be=p_utf16_to_utf8_be +patterns.utf16_to_utf8_le=p_utf16_to_utf8_le +patterns.utf32_to_utf8_be=p_utf32_to_utf8_be +patterns.utf32_to_utf8_le=p_utf32_to_utf8_le +utf16_to_utf8_be=function(s) + if s and s~="" then + return lpegmatch(p_utf16_to_utf8_be,s) + else + return s end - utf16_to_utf8_le=function(t) - if type(t)=="string" then - t=lpegmatch(utf_16_le_linesplitter,t) - end - local 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) - end - return t +end +local utf16_to_utf8_be_t=function(t) + if not t then + return nil + elseif type(t)=="string" then + t=lpegmatch(utf_16_be_linesplitter,t) end - utf32_to_utf8_be=function(t) - if type(t)=="string" then - t=lpegmatch(utflinesplitter,t) - end - local result={} - for i=1,#t do - local r,more=0,-1 - for a,b in bytepairs(t[i]) 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) + for i=1,#t do + local s=t[i] + if s~="" then + t[i]=lpegmatch(p_utf16_to_utf8_be,s) end - return t end - utf32_to_utf8_le=function(t) - if type(t)=="string" then - t=lpegmatch(utflinesplitter,t) - end - local result={} - for i=1,#t do - local r,more=0,-1 - for a,b in bytepairs(t[i]) 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 t + return t +end +utf16_to_utf8_le=function(s) + if s and s~="" then + return lpegmatch(p_utf16_to_utf8_le,s) + else + return s end -else - utf16_to_utf8_be=function(t) - if type(t)=="string" then - t=lpegmatch(utf_16_be_linesplitter,t) - end - local result={} - for i=1,#t do - local r,more=0,0 - for left,right in gmatch(t[i],"(.)(.)") do - if left=="\000" then - r=r+1 - result[r]=utfchar(byte(right)) - elseif right then - local now=256*byte(left)+byte(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) +end +local utf16_to_utf8_le_t=function(t) + if not t then + return nil + elseif type(t)=="string" then + t=lpegmatch(utf_16_le_linesplitter,t) + end + for i=1,#t do + local s=t[i] + if s~="" then + t[i]=lpegmatch(p_utf16_to_utf8_le,s) end - return t end - utf16_to_utf8_le=function(t) - if type(t)=="string" then - t=lpegmatch(utf_16_le_linesplitter,t) + return t +end +utf32_to_utf8_be=function(s) + if s and s~="" then + return lpegmatch(p_utf32_to_utf8_be,s) + else + return s + end +end +local utf32_to_utf8_be_t=function(t) + if not t then + return nil + elseif type(t)=="string" then + t=lpegmatch(utf_32_be_linesplitter,t) + end + for i=1,#t do + local s=t[i] + if s~="" then + t[i]=lpegmatch(p_utf32_to_utf8_be,s) end - local result={} - for i=1,#t do - local r,more=0,0 - for left,right in gmatch(t[i],"(.)(.)") do - if right=="\000" then - r=r+1 - result[r]=utfchar(byte(left)) - elseif right then - local now=256*byte(right)+byte(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) + end + return t +end +utf32_to_utf8_le=function(s) + if s and s~="" then + return lpegmatch(p_utf32_to_utf8_le,s) + else + return s + end +end +local utf32_to_utf8_le_t=function(t) + if not t then + return nil + elseif type(t)=="string" then + t=lpegmatch(utf_32_le_linesplitter,t) + end + for i=1,#t do + local s=t[i] + if s~="" then + t[i]=lpegmatch(p_utf32_to_utf8_le,s) end - return t end - utf32_to_utf8_le=function() return {} end - utf32_to_utf8_be=function() return {} end + return t end +utf.utf16_to_utf8_le_t=utf16_to_utf8_le_t +utf.utf16_to_utf8_be_t=utf16_to_utf8_be_t +utf.utf32_to_utf8_le_t=utf32_to_utf8_le_t +utf.utf32_to_utf8_be_t=utf32_to_utf8_be_t utf.utf16_to_utf8_le=utf16_to_utf8_le utf.utf16_to_utf8_be=utf16_to_utf8_be utf.utf32_to_utf8_le=utf32_to_utf8_le utf.utf32_to_utf8_be=utf32_to_utf8_be -function utf.utf8_to_utf8(t) +function utf.utf8_to_utf8_t(t) return type(t)=="string" and lpegmatch(utflinesplitter,t) or t end -function utf.utf16_to_utf8(t,endian) - return endian and utf16_to_utf8_be(t) or utf16_to_utf8_le(t) or t +function utf.utf16_to_utf8_t(t,endian) + return endian and utf16_to_utf8_be_t(t) or utf16_to_utf8_le_t(t) or t end -function utf.utf32_to_utf8(t,endian) - return endian and utf32_to_utf8_be(t) or utf32_to_utf8_le(t) or t +function utf.utf32_to_utf8_t(t,endian) + return endian and utf32_to_utf8_be_t(t) or utf32_to_utf8_le_t(t) or t end -local function little(c) - local b=byte(c) +local function little(b) if b<0x10000 then return char(b%256,b/256) else @@ -4631,8 +5022,7 @@ local function little(c) return char(b1%256,b1/256,b2%256,b2/256) end end -local function big(c) - local b=byte(c) +local function big(b) if b<0x10000 then return char(b/256,b%256) else @@ -4641,27 +5031,29 @@ local function big(c) return char(b1/256,b1%256,b2/256,b2%256) end end -local _,l_remap=utf.remapper(little) -local _,b_remap=utf.remapper(big) -function utf.utf8_to_utf16_be(str,nobom) +local l_remap=Cs((p_utf8byte/little+P(1)/"")^0) +local b_remap=Cs((p_utf8byte/big+P(1)/"")^0) +local function utf8_to_utf16_be(str,nobom) if nobom then return lpegmatch(b_remap,str) else return char(254,255)..lpegmatch(b_remap,str) end end -function utf.utf8_to_utf16_le(str,nobom) +local function utf8_to_utf16_le(str,nobom) if nobom then return lpegmatch(l_remap,str) else return char(255,254)..lpegmatch(l_remap,str) end end +utf.utf8_to_utf16_be=utf8_to_utf16_be +utf.utf8_to_utf16_le=utf8_to_utf16_le function utf.utf8_to_utf16(str,littleendian,nobom) if littleendian then - return utf.utf8_to_utf16_le(str,nobom) + return utf8_to_utf16_le(str,nobom) else - return utf.utf8_to_utf16_be(str,nobom) + return utf8_to_utf16_be(str,nobom) end end local pattern=Cs ( @@ -4677,16 +5069,16 @@ function utf.xstring(s) return format("0x%05X",type(s)=="number" and s or utfbyte(s)) end function utf.toeight(str) - if not str then + if not str or str=="" then return nil end local utftype=lpegmatch(p_utfstricttype,str) if utftype=="utf-8" then - return sub(str,4) - elseif utftype=="utf-16-le" then - return utf16_to_utf8_le(str) + return sub(str,4) elseif utftype=="utf-16-be" then - return utf16_to_utf8_ne(str) + return utf16_to_utf8_be(str) + elseif utftype=="utf-16-le" then + return utf16_to_utf8_le(str) else return str end @@ -4765,7 +5157,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-math"] = package.loaded["l-math"] or true --- original size: 915, stripped down to: 836 +-- original size: 974, stripped down to: 890 if not modules then modules={} end modules ['l-math']={ version=1.001, @@ -4775,6 +5167,9 @@ if not modules then modules={} end modules ['l-math']={ license="see context related readme files" } local floor,sin,cos,tan=math.floor,math.sin,math.cos,math.tan +if not math.ceiling then + math.ceiling=math.ceil +end if not math.round then function math.round(x) return floor(x+0.5) end end @@ -4802,7 +5197,7 @@ do -- create closure to overcome 200 locals limit package.loaded["util-str"] = package.loaded["util-str"] or true --- original size: 26857, stripped down to: 15062 +-- original size: 34503, stripped down to: 18933 if not modules then modules={} end modules ['util-str']={ version=1.001, @@ -4821,25 +5216,43 @@ local unpack,concat=table.unpack,table.concat local P,V,C,S,R,Ct,Cs,Cp,Carg,Cc=lpeg.P,lpeg.V,lpeg.C,lpeg.S,lpeg.R,lpeg.Ct,lpeg.Cs,lpeg.Cp,lpeg.Carg,lpeg.Cc local patterns,lpegmatch=lpeg.patterns,lpeg.match local utfchar,utfbyte=utf.char,utf.byte -local loadstripped=_LUAVERSION<5.2 and load or function(str) - return load(dump(load(str),true)) +local loadstripped=nil +if _LUAVERSION<5.2 then + loadstripped=function(str,shortcuts) + return load(str) + end +else + loadstripped=function(str,shortcuts) + if shortcuts then + return load(dump(load(str),true),nil,nil,shortcuts) + else + return load(dump(load(str),true)) + end + end end if not number then number={} end local stripper=patterns.stripzeros +local newline=patterns.newline +local endofstring=patterns.endofstring +local whitespace=patterns.whitespace +local spacer=patterns.spacer +local spaceortab=patterns.spaceortab local function points(n) + n=tonumber(n) return (not n or n==0) and "0pt" or lpegmatch(stripper,format("%.5fpt",n/65536)) end local function basepoints(n) + n=tonumber(n) return (not n or n==0) and "0bp" or lpegmatch(stripper,format("%.5fbp",n*(7200/7227)/65536)) end number.points=points number.basepoints=basepoints -local rubish=patterns.spaceortab^0*patterns.newline -local anyrubish=patterns.spaceortab+patterns.newline +local rubish=spaceortab^0*newline +local anyrubish=spaceortab+newline local anything=patterns.anything -local stripped=(patterns.spaceortab^1/"")*patterns.newline +local stripped=(spaceortab^1/"")*newline local leading=rubish^0/"" -local trailing=(anyrubish^1*patterns.endofstring)/"" +local trailing=(anyrubish^1*endofstring)/"" local redundant=rubish^3/"\n" local pattern=Cs(leading*(trailing+redundant+stripped+anything)^0) function strings.collapsecrlf(str) @@ -4885,18 +5298,44 @@ local pattern=Carg(1)/function(t) else return "" end - end+patterns.newline*Cp()/function(position) + end+newline*Cp()/function(position) extra,start=0,position end+patterns.anything )^1) function strings.tabtospace(str,tab) return lpegmatch(pattern,str,1,tab or 7) end -function strings.striplong(str) - str=gsub(str,"^%s*","") - str=gsub(str,"[\n\r]+ *","\n") - return str +local space=spacer^0 +local nospace=space/"" +local endofline=nospace*newline +local stripend=(whitespace^1*endofstring)/"" +local normalline=(nospace*((1-space*(newline+endofstring))^1)*nospace) +local stripempty=endofline^1/"" +local normalempty=endofline^1 +local singleempty=endofline*(endofline^0/"") +local doubleempty=endofline*endofline^-1*(endofline^0/"") +local stripstart=stripempty^0 +local p_prune_normal=Cs (stripstart*(stripend+normalline+normalempty )^0 ) +local p_prune_collapse=Cs (stripstart*(stripend+normalline+doubleempty )^0 ) +local p_prune_noempty=Cs (stripstart*(stripend+normalline+singleempty )^0 ) +local p_retain_normal=Cs ((normalline+normalempty )^0 ) +local p_retain_collapse=Cs ((normalline+doubleempty )^0 ) +local p_retain_noempty=Cs ((normalline+singleempty )^0 ) +local striplinepatterns={ + ["prune"]=p_prune_normal, + ["prune and collapse"]=p_prune_collapse, + ["prune and no empty"]=p_prune_noempty, + ["retain"]=p_retain_normal, + ["retain and collapse"]=p_retain_collapse, + ["retain and no empty"]=p_retain_noempty, + ["collapse"]=patterns.collapser, +} +setmetatable(striplinepatterns,{ __index=function(t,k) return p_prune_collapse end }) +strings.striplinepatterns=striplinepatterns +function strings.striplines(str,how) + return str and lpegmatch(striplinepatterns[how],str) or str end +strings.striplong=strings.striplines function strings.nice(str) str=gsub(str,"[:%-+_]+"," ") return str @@ -4934,10 +5373,10 @@ string.tracedchars=tracedchars strings.tracers=tracedchars function string.tracedchar(b) if type(b)=="number" then - return tracedchars[b] or (utfchar(b).." (U+"..format('%05X',b)..")") + return tracedchars[b] or (utfchar(b).." (U+"..format("%05X",b)..")") else local c=utfbyte(b) - return tracedchars[c] or (b.." (U+"..format('%05X',c)..")") + return tracedchars[c] or (b.." (U+"..(c and format("%05X",c) or "?????")..")") end end function number.signed(i) @@ -4972,31 +5411,58 @@ function number.sparseexponent(f,n) end return tostring(n) end -local preamble=[[ -local type = type -local tostring = tostring -local tonumber = tonumber -local format = string.format -local concat = table.concat -local signed = number.signed -local points = number.points -local basepoints = number.basepoints -local utfchar = utf.char -local utfbyte = utf.byte -local lpegmatch = lpeg.match -local nspaces = string.nspaces -local tracedchar = string.tracedchar -local autosingle = string.autosingle -local autodouble = string.autodouble -local sequenced = table.sequenced -local formattednumber = number.formatted -local sparseexponent = number.sparseexponent -]] local template=[[ %s %s return function(%s) return %s end ]] +local preamble,environment="",{} +if _LUAVERSION<5.2 then + preamble=[[ +local lpeg=lpeg +local type=type +local tostring=tostring +local tonumber=tonumber +local format=string.format +local concat=table.concat +local signed=number.signed +local points=number.points +local basepoints= number.basepoints +local utfchar=utf.char +local utfbyte=utf.byte +local lpegmatch=lpeg.match +local nspaces=string.nspaces +local tracedchar=string.tracedchar +local autosingle=string.autosingle +local autodouble=string.autodouble +local sequenced=table.sequenced +local formattednumber=number.formatted +local sparseexponent=number.sparseexponent + ]] +else + environment={ + global=global or _G, + lpeg=lpeg, + type=type, + tostring=tostring, + tonumber=tonumber, + format=string.format, + concat=table.concat, + signed=number.signed, + points=number.points, + basepoints=number.basepoints, + utfchar=utf.char, + utfbyte=utf.byte, + lpegmatch=lpeg.match, + nspaces=string.nspaces, + tracedchar=string.tracedchar, + autosingle=string.autosingle, + autodouble=string.autodouble, + sequenced=table.sequenced, + formattednumber=number.formatted, + sparseexponent=number.sparseexponent, + } +end local arguments={ "a1" } setmetatable(arguments,{ __index=function(t,k) local v=t[k-1]..",a"..k @@ -5035,7 +5501,7 @@ local format_i=function(f) if f and f~="" then return format("format('%%%si',a%s)",f,n) else - return format("format('%%i',a%s)",n) + return format("format('%%i',a%s)",n) end end local format_d=format_i @@ -5047,6 +5513,14 @@ local format_f=function(f) n=n+1 return format("format('%%%sf',a%s)",f,n) end +local format_F=function(f) + n=n+1 + if not f or f=="" then + return format("(((a%s > -0.0000000005 and a%s < 0.0000000005) and '0') or format((a%s %% 1 == 0) and '%%i' or '%%.9f',a%s))",n,n,n,n) + else + return format("format((a%s %% 1 == 0) and '%%i' or '%%%sf',a%s)",n,f,n) + end +end local format_g=function(f) n=n+1 return format("format('%%%sg',a%s)",f,n) @@ -5261,7 +5735,7 @@ local builder=Cs { "start", ( P("%")/""*( V("!") -+V("s")+V("q")+V("i")+V("d")+V("f")+V("g")+V("G")+V("e")+V("E")+V("x")+V("X")+V("o") ++V("s")+V("q")+V("i")+V("d")+V("f")+V("F")+V("g")+V("G")+V("e")+V("E")+V("x")+V("X")+V("o") +V("c")+V("C")+V("S") +V("Q") +V("N") @@ -5272,7 +5746,6 @@ local builder=Cs { "start", +V("j")+V("J") +V("m")+V("M") +V("z") -+V("*") )+V("*") )*(P(-1)+Carg(1)) )^0, @@ -5281,6 +5754,7 @@ local builder=Cs { "start", ["i"]=(prefix_any*P("i"))/format_i, ["d"]=(prefix_any*P("d"))/format_d, ["f"]=(prefix_any*P("f"))/format_f, + ["F"]=(prefix_any*P("F"))/format_F, ["g"]=(prefix_any*P("g"))/format_g, ["G"]=(prefix_any*P("G"))/format_G, ["e"]=(prefix_any*P("e"))/format_e, @@ -5315,11 +5789,12 @@ local builder=Cs { "start", ["a"]=(prefix_any*P("a"))/format_a, ["A"]=(prefix_any*P("A"))/format_A, ["*"]=Cs(((1-P("%"))^1+P("%%")/"%%")^1)/format_rest, + ["?"]=Cs(((1-P("%"))^1 )^1)/format_rest, ["!"]=Carg(2)*prefix_any*P("!")*C((1-P("!"))^1)*P("!")/format_extension, } local direct=Cs ( - P("%")/""*Cc([[local format = string.format return function(str) return format("%]])*(S("+- .")+R("09"))^0*S("sqidfgGeExXo")*Cc([[",str) end]])*P(-1) - ) + P("%")*(S("+- .")+R("09"))^0*S("sqidfgGeExXo")*P(-1)/[[local format = string.format return function(str) return format("%0",str) end]] +) local function make(t,str) local f local p @@ -5328,10 +5803,10 @@ local function make(t,str) f=loadstripped(p)() else n=0 - p=lpegmatch(builder,str,1,"..",t._extensions_) + p=lpegmatch(builder,str,1,t._connector_,t._extensions_) if n>0 then p=format(template,preamble,t._preamble_,arguments[n],p) - f=loadstripped(p)() + f=loadstripped(p,t._environment_)() else f=function() return str end end @@ -5343,10 +5818,22 @@ local function use(t,fmt,...) return t[fmt](...) end strings.formatters={} -function strings.formatters.new() - local t={ _extensions_={},_preamble_="",_type_="formatter" } - setmetatable(t,{ __index=make,__call=use }) - return t +if _LUAVERSION<5.2 then + function strings.formatters.new(noconcat) + local t={ _type_="formatter",_connector_=noconcat and "," or "..",_extensions_={},_preamble_=preamble,_environment_={} } + setmetatable(t,{ __index=make,__call=use }) + return t + end +else + function strings.formatters.new(noconcat) + local e={} + for k,v in next,environment do + e[k]=v + end + local t={ _type_="formatter",_connector_=noconcat and "," or "..",_extensions_={},_preamble_="",_environment_=e } + setmetatable(t,{ __index=make,__call=use }) + return t + end end local formatters=strings.formatters.new() string.formatters=formatters @@ -5354,8 +5841,12 @@ string.formatter=function(str,...) return formatters[str](...) end local function add(t,name,template,preamble) if type(t)=="table" and t._type_=="formatter" then t._extensions_[name]=template or "%s" - if preamble then + if type(preamble)=="string" then t._preamble_=preamble.."\n"..t._preamble_ + elseif type(preamble)=="table" then + for k,v in next,preamble do + t._environment_[k]=v + end end end end @@ -5364,9 +5855,28 @@ patterns.xmlescape=Cs((P("<")/"<"+P(">")/">"+P("&")/"&"+P('"')/"" patterns.texescape=Cs((C(S("#$%\\{}"))/"\\%1"+P(1))^0) patterns.luaescape=Cs(((1-S('"\n'))^1+P('"')/'\\"'+P('\n')/'\\n"')^0) patterns.luaquoted=Cs(Cc('"')*((1-S('"\n'))^1+P('"')/'\\"'+P('\n')/'\\n"')^0*Cc('"')) -add(formatters,"xml",[[lpegmatch(xmlescape,%s)]],[[local xmlescape = lpeg.patterns.xmlescape]]) -add(formatters,"tex",[[lpegmatch(texescape,%s)]],[[local texescape = lpeg.patterns.texescape]]) -add(formatters,"lua",[[lpegmatch(luaescape,%s)]],[[local luaescape = lpeg.patterns.luaescape]]) +if _LUAVERSION<5.2 then + add(formatters,"xml",[[lpegmatch(xmlescape,%s)]],"local xmlescape = lpeg.patterns.xmlescape") + add(formatters,"tex",[[lpegmatch(texescape,%s)]],"local texescape = lpeg.patterns.texescape") + add(formatters,"lua",[[lpegmatch(luaescape,%s)]],"local luaescape = lpeg.patterns.luaescape") +else + add(formatters,"xml",[[lpegmatch(xmlescape,%s)]],{ xmlescape=lpeg.patterns.xmlescape }) + add(formatters,"tex",[[lpegmatch(texescape,%s)]],{ texescape=lpeg.patterns.texescape }) + add(formatters,"lua",[[lpegmatch(luaescape,%s)]],{ luaescape=lpeg.patterns.luaescape }) +end +local dquote=patterns.dquote +local equote=patterns.escaped+dquote/'\\"'+1 +local space=patterns.space +local cquote=Cc('"') +local pattern=Cs(dquote*(equote-P(-2))^0*dquote) ++Cs(cquote*(equote-space)^0*space*equote^0*cquote) +function string.optionalquoted(str) + return lpegmatch(pattern,str) or str +end +local pattern=Cs((newline/os.newline+1)^0) +function string.replacenewlines(str) + return lpegmatch(pattern,str) +end end -- of closure @@ -5375,7 +5885,7 @@ do -- create closure to overcome 200 locals limit package.loaded["util-tab"] = package.loaded["util-tab"] or true --- original size: 23952, stripped down to: 16092 +-- original size: 25338, stripped down to: 16247 if not modules then modules={} end modules ['util-tab']={ version=1.001, @@ -5388,7 +5898,7 @@ utilities=utilities or {} utilities.tables=utilities.tables or {} local tables=utilities.tables local format,gmatch,gsub,sub=string.format,string.gmatch,string.gsub,string.sub -local concat,insert,remove=table.concat,table.insert,table.remove +local concat,insert,remove,sort=table.concat,table.insert,table.remove,table.sort local setmetatable,getmetatable,tonumber,tostring=setmetatable,getmetatable,tonumber,tostring local type,next,rawset,tonumber,tostring,load,select=type,next,rawset,tonumber,tostring,load,select local lpegmatch,P,Cs,Cc=lpeg.match,lpeg.P,lpeg.Cs,lpeg.Cc @@ -5396,27 +5906,29 @@ local sortedkeys,sortedpairs=table.sortedkeys,table.sortedpairs local formatters=string.formatters local utftoeight=utf.toeight local splitter=lpeg.tsplitat(".") -function tables.definetable(target,nofirst,nolast) - local composed,shortcut,t=nil,nil,{} +function utilities.tables.definetable(target,nofirst,nolast) + local composed,t=nil,{} local snippets=lpegmatch(splitter,target) for i=1,#snippets-(nolast and 1 or 0) do local name=snippets[i] if composed then - composed=shortcut.."."..name - shortcut=shortcut.."_"..name - t[#t+1]=formatters["local %s = %s if not %s then %s = { } %s = %s end"](shortcut,composed,shortcut,shortcut,composed,shortcut) + composed=composed.."."..name + t[#t+1]=formatters["if not %s then %s = { } end"](composed,composed) else composed=name - shortcut=name if not nofirst then t[#t+1]=formatters["%s = %s or { }"](composed,composed) end end end - if nolast then - composed=shortcut.."."..snippets[#snippets] + if composed then + if nolast then + composed=composed.."."..snippets[#snippets] + end + return concat(t,"\n"),composed + else + return "",target end - return concat(t,"\n"),composed end function tables.definedtable(...) local t=_G @@ -5443,7 +5955,7 @@ function tables.accesstable(target,root) end function tables.migratetable(target,v,root) local t=root or _G - local names=string.split(target,".") + local names=lpegmatch(splitter,target) for i=1,#names-1 do local name=names[i] t[name]=t[name] or {} @@ -5463,6 +5975,15 @@ function tables.removevalue(t,value) end end end +function tables.replacevalue(t,oldvalue,newvalue) + if oldvalue and newvalue then + for i=1,#t do + if t[i]==oldvalue then + t[i]=newvalue + end + end + end +end function tables.insertbeforevalue(t,value,extra) for i=1,#t do if t[i]==extra then @@ -5610,7 +6131,7 @@ local f_ordered_string=formatters["%q,"] local f_ordered_number=formatters["%s,"] local f_ordered_boolean=formatters["%l,"] function table.fastserialize(t,prefix) - local r={ prefix or "return" } + local r={ type(prefix)=="string" and prefix or "return" } local m=1 local function fastserialize(t,outer) local n=#t @@ -5807,7 +6328,8 @@ function table.serialize(root,name,specification) local t local n=1 local function simple_table(t) - if #t>0 then + local nt=#t + if nt>0 then local n=0 for _,v in next,t do n=n+1 @@ -5815,19 +6337,17 @@ function table.serialize(root,name,specification) return nil end end - if n==#t then + if n==nt then local tt={} - local nt=0 - for i=1,#t do + for i=1,nt do local v=t[i] local tv=type(v) - nt=nt+1 if tv=="number" then - tt[nt]=v + tt[i]=v elseif tv=="string" then - tt[nt]=format("%q",v) + tt[i]=format("%q",v) elseif tv=="boolean" then - tt[nt]=v and "true" or "false" + tt[i]=v and "true" or "false" else return nil end @@ -5856,7 +6376,7 @@ function table.serialize(root,name,specification) end depth=depth+1 end - if root and next(root) then + if root and next(root)~=nil then local first=nil local last=0 last=#root @@ -5875,13 +6395,13 @@ function table.serialize(root,name,specification) local v=root[k] local tv=type(v) local tk=type(k) - if first and tk=="number" and k>=first and k<=last then + if first and tk=="number" and k<=last and k>=first then if tv=="number" then n=n+1 t[n]=f_val_num(depth,v) elseif tv=="string" then n=n+1 t[n]=f_val_str(depth,v) elseif tv=="table" then - if not next(v) then + if next(v)==nil then n=n+1 t[n]=f_val_not(depth) else local st=simple_table(v) @@ -5911,13 +6431,13 @@ function table.serialize(root,name,specification) n=n+1 t[n]=f_key_boo_value_str(depth,k,v) end elseif tv=="table" then - if not next(v) then + if next(v)==nil then if tk=="number" then - n=n+1 t[n]=f_key_num_value_not(depth,k,v) + n=n+1 t[n]=f_key_num_value_not(depth,k) elseif tk=="string" then - n=n+1 t[n]=f_key_str_value_not(depth,k,v) + n=n+1 t[n]=f_key_str_value_not(depth,k) elseif tk=="boolean" then - n=n+1 t[n]=f_key_boo_value_not(depth,k,v) + n=n+1 t[n]=f_key_boo_value_not(depth,k) end else local st=simple_table(v) @@ -5969,7 +6489,7 @@ function table.serialize(root,name,specification) local dummy=root._w_h_a_t_e_v_e_r_ root._w_h_a_t_e_v_e_r_=nil end - if next(root) then + if next(root)~=nil then do_serialize(root,name,1,0) end end @@ -6132,7 +6652,7 @@ do -- create closure to overcome 200 locals limit package.loaded["util-prs"] = package.loaded["util-prs"] or true --- original size: 19604, stripped down to: 13998 +-- original size: 21780, stripped down to: 15121 if not modules then modules={} end modules ['util-prs']={ version=1.001, @@ -6154,6 +6674,8 @@ local patterns=parsers.patterns or {} parsers.patterns=patterns local setmetatableindex=table.setmetatableindex local sortedhash=table.sortedhash +local sortedkeys=table.sortedkeys +local tohash=table.tohash local digit=R("09") local space=P(' ') local equal=P("=") @@ -6203,9 +6725,7 @@ patterns.settings_to_hash_a=pattern_a_s patterns.settings_to_hash_b=pattern_b_s patterns.settings_to_hash_c=pattern_c_s function parsers.make_settings_to_hash_pattern(set,how) - if type(str)=="table" then - return set - elseif how=="strict" then + if how=="strict" then return (pattern_c/set)^1 elseif how=="tolerant" then return (pattern_b/set)^1 @@ -6214,7 +6734,9 @@ function parsers.make_settings_to_hash_pattern(set,how) end end function parsers.settings_to_hash(str,existing) - if type(str)=="table" then + if not str or str=="" then + return {} + elseif type(str)=="table" then if existing then for k,v in next,str do existing[k]=v @@ -6223,16 +6745,16 @@ function parsers.settings_to_hash(str,existing) else return str end - elseif str and str~="" then + else hash=existing or {} lpegmatch(pattern_a_s,str) return hash - else - return {} end end function parsers.settings_to_hash_tolerant(str,existing) - if type(str)=="table" then + if not str or str=="" then + return {} + elseif type(str)=="table" then if existing then for k,v in next,str do existing[k]=v @@ -6241,16 +6763,16 @@ function parsers.settings_to_hash_tolerant(str,existing) else return str end - elseif str and str~="" then + else hash=existing or {} lpegmatch(pattern_b_s,str) return hash - else - return {} end end function parsers.settings_to_hash_strict(str,existing) - if type(str)=="table" then + if not str or str=="" then + return nil + elseif type(str)=="table" then if existing then for k,v in next,str do existing[k]=v @@ -6263,8 +6785,6 @@ function parsers.settings_to_hash_strict(str,existing) hash=existing or {} lpegmatch(pattern_c_s,str) return next(hash) and hash - else - return nil end end local separator=comma*space^0 @@ -6272,27 +6792,46 @@ local value=P(lbrace*C((nobrace+nestedbraces)^0)*rbrace)+C((nestedbraces+(1-comm local pattern=spaces*Ct(value*(separator*value)^0) patterns.settings_to_array=pattern function parsers.settings_to_array(str,strict) - if type(str)=="table" then - return str - elseif not str or str=="" then + if not str or str=="" then return {} + elseif type(str)=="table" then + return str elseif strict then - if find(str,"{") then + if find(str,"{",1,true) then return lpegmatch(pattern,str) else return { str } end - elseif find(str,",") then + elseif find(str,",",1,true) then return lpegmatch(pattern,str) else return { str } end end -local separator=space^0*comma*space^0 -local value=P(lbrace*C((nobrace+nestedbraces)^0)*rbrace)+C((nestedbraces+(1-(space^0*(comma+P(-1)))))^0) -local withvalue=Carg(1)*value/function(f,s) return f(s) end -local pattern_a=spaces*Ct(value*(separator*value)^0) -local pattern_b=spaces*withvalue*(separator*withvalue)^0 +local cache_a={} +local cache_b={} +function parsers.groupedsplitat(symbol,withaction) + if not symbol then + symbol="," + end + local pattern=(withaction and cache_b or cache_a)[symbol] + if not pattern then + local symbols=S(symbol) + local separator=space^0*symbols*space^0 + local value=P(lbrace*C((nobrace+nestedbraces)^0)*rbrace)+C((nestedbraces+(1-(space^0*(symbols+P(-1)))))^0) + if withaction then + local withvalue=Carg(1)*value/function(f,s) return f(s) end + pattern=spaces*withvalue*(separator*withvalue)^0 + cache_b[symbol]=pattern + else + pattern=spaces*Ct(value*(separator*value)^0) + cache_a[symbol]=pattern + end + end + return pattern +end +local pattern_a=parsers.groupedsplitat(",",false) +local pattern_b=parsers.groupedsplitat(",",true) function parsers.stripped_settings_to_array(str) if not str or str=="" then return {} @@ -6317,8 +6856,8 @@ function parsers.add_settings_to_array(t,str) end function parsers.hash_to_string(h,separator,yes,no,strict,omit) if h then - local t,tn,s={},0,table.sortedkeys(h) - omit=omit and table.tohash(omit) + local t,tn,s={},0,sortedkeys(h) + omit=omit and tohash(omit) for i=1,#s do local key=s[i] if not omit or not omit[key] then @@ -6354,12 +6893,9 @@ function parsers.array_to_string(a,separator) return "" end end -function parsers.settings_to_set(str,t) - t=t or {} - for s in gmatch(str,"[^, ]+") do - t[s]=true - end - return t +local pattern=Cf(Ct("")*Cg(C((1-S(", "))^1)*S(", ")^0*Cc(true))^1,rawset) +function utilities.parsers.settings_to_set(str,t) + return str and lpegmatch(pattern,str) or {} end function parsers.simple_hash_to_string(h,separator) local t,tn={},0 @@ -6371,12 +6907,16 @@ function parsers.simple_hash_to_string(h,separator) end return concat(t,separator or ",") end -local str=C((1-whitespace-equal)^1) +local str=Cs(lpegpatterns.unquoted)+C((1-whitespace-equal)^1) local setting=Cf(Carg(1)*(whitespace^0*Cg(str*whitespace^0*(equal*whitespace^0*str+Cc(""))))^1,rawset) local splitter=setting^1 function utilities.parsers.options_to_hash(str,target) return str and lpegmatch(splitter,str,1,target or {}) or {} end +local splitter=lpeg.tsplitat(" ") +function utilities.parsers.options_to_array(str) + return str and lpegmatch(splitter,str) or {} +end local value=P(lbrace*C((nobrace+nestedbraces)^0)*rbrace)+C(digit^1*lparent*(noparent+nestedparents)^1*rparent)+C((nestedbraces+(1-comma))^1) local pattern_a=spaces*Ct(value*(separator*value)^0) local function repeater(n,str) @@ -6463,7 +7003,7 @@ function parsers.keq_to_hash(str) end local defaultspecification={ separator=",",quote='"' } function parsers.csvsplitter(specification) - specification=specification and table.setmetatableindex(specification,defaultspecification) or defaultspecification + specification=specification and setmetatableindex(specification,defaultspecification) or defaultspecification local separator=specification.separator local quotechar=specification.quote local separator=S(separator~="" and separator or ",") @@ -6487,7 +7027,7 @@ function parsers.csvsplitter(specification) end end function parsers.rfc4180splitter(specification) - specification=specification and table.setmetatableindex(specification,defaultspecification) or defaultspecification + specification=specification and setmetatableindex(specification,defaultspecification) or defaultspecification local separator=specification.separator local quotechar=P(specification.quote) local dquotechar=quotechar*quotechar @@ -6498,7 +7038,7 @@ function parsers.rfc4180splitter(specification) local field=escaped+non_escaped+Cc("") local record=Ct(field*(separator*field)^1) local headerline=record*Cp() - local wholeblob=Ct((newline^-1*record)^0) + local wholeblob=Ct((newline^(specification.strict and -1 or 1)*record)^0) return function(data,getheader) if getheader then local header,position=lpegmatch(headerline,data) @@ -6535,20 +7075,20 @@ function parsers.stepper(str,n,action) lpegmatch(stepper,str,1,n,action or print) end end -local pattern_math=Cs((P("%")/"\\percent "+P("^")*Cc("{")*lpegpatterns.integer*Cc("}")+P(1))^0) -local pattern_text=Cs((P("%")/"\\percent "+(P("^")/"\\high")*Cc("{")*lpegpatterns.integer*Cc("}")+P(1))^0) +local pattern_math=Cs((P("%")/"\\percent "+P("^")*Cc("{")*lpegpatterns.integer*Cc("}")+anything)^0) +local pattern_text=Cs((P("%")/"\\percent "+(P("^")/"\\high")*Cc("{")*lpegpatterns.integer*Cc("}")+anything)^0) patterns.unittotex=pattern function parsers.unittotex(str,textmode) return lpegmatch(textmode and pattern_text or pattern_math,str) end -local pattern=Cs((P("^")/"<sup>"*lpegpatterns.integer*Cc("</sup>")+P(1))^0) +local pattern=Cs((P("^")/"<sup>"*lpegpatterns.integer*Cc("</sup>")+anything)^0) function parsers.unittoxml(str) return lpegmatch(pattern,str) end local cache={} -local spaces=lpeg.patterns.space^0 +local spaces=lpegpatterns.space^0 local dummy=function() end -table.setmetatableindex(cache,function(t,k) +setmetatableindex(cache,function(t,k) local separator=P(k) local value=(1-separator)^0 local pattern=spaces*C(value)*separator^0*Cp() @@ -6613,6 +7153,18 @@ function utilities.parsers.runtime(time) local seconds=mod(time,60) return days,hours,minutes,seconds end +local spacing=whitespace^0 +local apply=P("->") +local method=C((1-apply)^1) +local token=lbrace*C((1-rbrace)^1)*rbrace+C(anything^1) +local pattern=spacing*(method*spacing*apply+Carg(1))*spacing*token +function utilities.parsers.splitmethod(str,default) + if str then + return lpegmatch(pattern,str,1,default or false) + else + return default or false,"" + end +end end -- of closure @@ -6702,7 +7254,7 @@ do -- create closure to overcome 200 locals limit package.loaded["trac-set"] = package.loaded["trac-set"] or true --- original size: 12365, stripped down to: 8799 +-- original size: 12482, stripped down to: 8864 if not modules then modules={} end modules ['trac-set']={ version=1.001, @@ -6730,7 +7282,7 @@ function setters.initialize(filename,name,values) local data=setter.data if data then for key,newvalue in next,values do - local newvalue=is_boolean(newvalue,newvalue) + local newvalue=is_boolean(newvalue,newvalue,true) local functions=data[key] if functions then local oldvalue=functions.value @@ -6784,7 +7336,7 @@ local function set(t,what,newvalue) elseif not value then value=false else - value=is_boolean(value,value) + value=is_boolean(value,value,true) end w=topattern(w,true,true) for name,functions in next,data do @@ -6923,6 +7475,7 @@ function setters.new(name) report=function(...) setters.report (setter,...) end, enable=function(...) enable (setter,...) end, disable=function(...) disable (setter,...) end, + reset=function(...) reset (setter,...) end, register=function(...) register(setter,...) end, list=function(...) list (setter,...) end, show=function(...) show (setter,...) end, @@ -7014,7 +7567,7 @@ do -- create closure to overcome 200 locals limit package.loaded["trac-log"] = package.loaded["trac-log"] or true --- original size: 25391, stripped down to: 16561 +-- original size: 29359, stripped down to: 20483 if not modules then modules={} end modules ['trac-log']={ version=1.001, @@ -7023,15 +7576,18 @@ if not modules then modules={} end modules ['trac-log']={ copyright="PRAGMA ADE / ConTeXt Development Team", license="see context related readme files" } +local next,type,select,print=next,type,select,print 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 topattern=string.topattern -local next,type,select=next,type,select local utfchar=utf.char +local datetime=os.date +local openfile=io.open local setmetatableindex=table.setmetatableindex local formatters=string.formatters local texgetcount=tex and tex.getcount +local variant="default" logs=logs or {} local logs=logs local moreinfo=[[ @@ -7041,32 +7597,122 @@ 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 ]] -utilities.strings.formatters.add ( +formatters.add ( formatters,"unichr", [["U+" .. format("%%05X",%s) .. " (" .. utfchar(%s) .. ")"]] ) -utilities.strings.formatters.add ( +formatters.add ( formatters,"chruni", [[utfchar(%s) .. " (U+" .. format("%%05X",%s) .. ")"]] ) 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,setlogfile,settimedlog,setprocessor,setformatters +local direct,subdirect,writer,pushtarget,poptarget,setlogfile,settimedlog,setprocessor,setformatters,newline if tex and (tex.jobname or tex.formatname) then - local valueiskey={ __index=function(t,k) t[k]=k return k end } - local target="term and log" + local function useluawrites() + local texio_write_nl=texio.write_nl + local texio_write=texio.write + local io_write=io.write + write_nl=function(target,...) + if not io_write then + io_write=io.write + end + if target=="term and log" then + texio_write_nl("log",...) + texio_write_nl("term","") + io_write(...) + elseif target=="log" then + texio_write_nl("log",...) + elseif target=="term" then + texio_write_nl("term","") + io_write(...) + elseif target~="none" then + texio_write_nl("log",target,...) + texio_write_nl("term","") + io_write(target,...) + end + end + write=function(target,...) + if not io_write then + io_write=io.write + end + if target=="term and log" then + texio_write("log",...) + io_write(...) + elseif target=="log" then + texio_write("log",...) + elseif target=="term" then + io_write(...) + elseif target~="none" then + texio_write("log",target,...) + io_write(target,...) + end + end + texio.write=write + texio.write_nl=write_nl + useluawrites=ignore + end + local whereto="both" + local target=nil + local targets=nil + local formats=table.setmetatableindex("self") + local translations=table.setmetatableindex("self") + local report_yes,subreport_yes,direct_yes,subdirect_yes,status_yes + local report_nop,subreport_nop,direct_nop,subdirect_nop,status_nop + local variants={ + default={ + formats={ + report_yes=formatters["%-15s > %s\n"], + report_nop=formatters["%-15s >\n"], + direct_yes=formatters["%-15s > %s"], + direct_nop=formatters["%-15s >"], + subreport_yes=formatters["%-15s > %s > %s\n"], + subreport_nop=formatters["%-15s > %s >\n"], + subdirect_yes=formatters["%-15s > %s > %s"], + subdirect_nop=formatters["%-15s > %s >"], + status_yes=formatters["%-15s : %s\n"], + status_nop=formatters["%-15s :\n"], + }, + targets={ + logfile="log", + log="log", + file="log", + console="term", + terminal="term", + both="term and log", + }, + }, + ansi={ + formats={ + report_yes=formatters["[0;33m%-15s [0;1m>[0m %s\n"], + report_nop=formatters["[0;33m%-15s [0;1m>[0m\n"], + direct_yes=formatters["[0;33m%-15s [0;1m>[0m %s"], + direct_nop=formatters["[0;33m%-15s [0;1m>[0m"], + subreport_yes=formatters["[0;33m%-15s [0;1m>[0;35m %s [0;1m>[0m %s\n"], + subreport_nop=formatters["[0;33m%-15s [0;1m>[0;35m %s [0;1m>[0m\n"], + subdirect_yes=formatters["[0;33m%-15s [0;1m>[0;35m %s [0;1m>[0m %s"], + subdirect_nop=formatters["[0;33m%-15s [0;1m>[0;35m %s [0;1m>[0m"], + status_yes=formatters["[0;33m%-15s [0;1m:[0m %s\n"], + status_nop=formatters["[0;33m%-15s [0;1m:[0m\n"], + }, + targets={ + logfile="none", + log="none", + file="none", + console="term", + terminal="term", + both="term", + }, + } + } logs.flush=io.flush - local formats={} setmetatable(formats,valueiskey) - local translations={} setmetatable(translations,valueiskey) writer=function(...) write_nl(target,...) end newline=function() write_nl(target,"\n") end - local report_yes=formatters["%-15s > %s\n"] - local report_nop=formatters["%-15s >\n"] report=function(a,b,c,...) if c then write_nl(target,report_yes(translations[a],formatters[formats[b]](c,...))) @@ -7078,8 +7724,6 @@ if tex and (tex.jobname or tex.formatname) then write_nl(target,"\n") end end - local direct_yes=formatters["%-15s > %s"] - local direct_nop=formatters["%-15s >"] direct=function(a,b,c,...) if c then return direct_yes(translations[a],formatters[formats[b]](c,...)) @@ -7091,8 +7735,6 @@ if tex and (tex.jobname or tex.formatname) then return "" end end - local subreport_yes=formatters["%-15s > %s > %s\n"] - local subreport_nop=formatters["%-15s > %s >\n"] subreport=function(a,s,b,c,...) if c then write_nl(target,subreport_yes(translations[a],translations[s],formatters[formats[b]](c,...))) @@ -7104,8 +7746,6 @@ if tex and (tex.jobname or tex.formatname) then write_nl(target,"\n") end end - local subdirect_yes=formatters["%-15s > %s > %s"] - local subdirect_nop=formatters["%-15s > %s >"] subdirect=function(a,s,b,c,...) if c then return subdirect_yes(translations[a],translations[s],formatters[formats[b]](c,...)) @@ -7117,8 +7757,6 @@ if tex and (tex.jobname or tex.formatname) then return "" end end - local status_yes=formatters["%-15s : %s\n"] - local status_nop=formatters["%-15s :\n"] status=function(a,b,c,...) if c then write_nl(target,status_yes(translations[a],formatters[formats[b]](c,...))) @@ -7130,16 +7768,13 @@ if tex and (tex.jobname or tex.formatname) then 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 + settarget=function(askedwhereto) + whereto=askedwhereto or whereto or "both" + target=targets[whereto] + if not target then + whereto="both" + target=targets[whereto] + end if target=="term" or target=="term and log" then logs.flush=io.flush else @@ -7168,21 +7803,74 @@ if tex and (tex.jobname or tex.formatname) then writeline(target,f(...)) end end - setformatters=function(f) - report_yes=f.report_yes or report_yes - report_nop=f.report_nop or report_nop - subreport_yes=f.subreport_yes or subreport_yes - subreport_nop=f.subreport_nop or subreport_nop - direct_yes=f.direct_yes or direct_yes - direct_nop=f.direct_nop or direct_nop - subdirect_yes=f.subdirect_yes or subdirect_yes - subdirect_nop=f.subdirect_nop or subdirect_nop - status_yes=f.status_yes or status_yes - status_nop=f.status_nop or status_nop - end + setformatters=function(specification) + local t=nil + local f=nil + local d=variants.default + if not specification then + elseif type(specification)=="table" then + t=specification.targets + f=specification.formats or specification + else + local v=variants[specification] + if v then + t=v.targets + f=v.formats + variant=specification + end + end + targets=t or d.targets + target=targets[whereto] or target + if f then + d=d.formats + else + f=d.formats + d=f + end + setmetatableindex(f,d) + report_yes=f.report_yes + report_nop=f.report_nop + subreport_yes=f.subreport_yes + subreport_nop=f.subreport_nop + direct_yes=f.direct_yes + direct_nop=f.direct_nop + subdirect_yes=f.subdirect_yes + subdirect_nop=f.subdirect_nop + status_yes=f.status_yes + status_nop=f.status_nop + if variant=="ansi" then + useluawrites() + end + settarget(whereto) + end + setformatters(variant) setlogfile=ignore settimedlog=ignore else + local report_yes,subreport_yes,status_yes + local report_nop,subreport_nop,status_nop + local variants={ + default={ + formats={ + report_yes=formatters["%-15s | %s"], + report_nop=formatters["%-15s |"], + subreport_yes=formatters["%-15s | %s | %s"], + subreport_nop=formatters["%-15s | %s |"], + status_yes=formatters["%-15s : %s\n"], + status_nop=formatters["%-15s :\n"], + }, + }, + ansi={ + formats={ + report_yes=formatters["[0;32m%-15s [0;1m|[0m %s"], + report_nop=formatters["[0;32m%-15s [0;1m|[0m"], + subreport_yes=formatters["[0;32m%-15s [0;1m|[0;31m %s [0;1m|[0m %s"], + subreport_nop=formatters["[0;32m%-15s [0;1m|[0;31m %s [0;1m|[0m"], + status_yes=formatters["[0;32m%-15s [0;1m:[0m %s\n"], + status_nop=formatters["[0;32m%-15s [0;1m:[0m\n"], + }, + }, + } logs.flush=ignore writer=function(s) write_nl(s) @@ -7190,8 +7878,6 @@ else newline=function() write_nl("\n") end - local report_yes=formatters["%-15s | %s"] - local report_nop=formatters["%-15s |"] report=function(a,b,c,...) if c then write_nl(report_yes(a,formatters[b](c,...))) @@ -7203,8 +7889,6 @@ else write_nl("") end end - local subreport_yes=formatters["%-15s | %s | %s"] - local subreport_nop=formatters["%-15s | %s |"] subreport=function(a,sub,b,c,...) if c then write_nl(subreport_yes(a,sub,formatters[b](c,...))) @@ -7216,8 +7900,6 @@ else write_nl("") end end - local status_yes=formatters["%-15s : %s\n"] - local status_nop=formatters["%-15s :\n"] status=function(a,b,c,...) if c then write_nl(status_yes(a,formatters[b](c,...))) @@ -7242,14 +7924,34 @@ else writeline(f(s)) end end - setformatters=function(f) - report_yes=f.report_yes or report_yes - report_nop=f.report_nop or report_nop - subreport_yes=f.subreport_yes or subreport_yes - subreport_nop=f.subreport_nop or subreport_nop - status_yes=f.status_yes or status_yes - status_nop=f.status_nop or status_nop - end + setformatters=function(specification) + local f=nil + local d=variants.default + if specification then + if type(specification)=="table" then + f=specification.formats or specification + else + local v=variants[specification] + if v then + f=v.formats + end + end + end + if f then + d=d.formats + else + f=d.formats + d=f + end + setmetatableindex(f,d) + report_yes=f.report_yes + report_nop=f.report_nop + subreport_yes=f.subreport_yes + subreport_nop=f.subreport_nop + status_yes=f.status_yes + status_nop=f.status_nop + end + setformatters(variant) setlogfile=function(name,keepopen) if name and name~="" then local localtime=os.localtime @@ -7368,9 +8070,10 @@ local function setblocked(category,value) v.state=value end else - states=utilities.parsers.settings_to_hash(category) + states=utilities.parsers.settings_to_hash(category,type(states)=="table" and states or nil) for c,_ in next,states do - if data[c] then + local v=data[c] + if v then v.state=value else c=topattern(c,true,true) @@ -7501,13 +8204,13 @@ end local simple=logs.reporter("comment") logs.simple=simple logs.simpleline=simple -function logs.setprogram () end -function logs.extendbanner() end -function logs.reportlines () end -function logs.reportbanner() end -function logs.reportline () end -function logs.simplelines () end -function logs.help () end +logs.setprogram=ignore +logs.extendbanner=ignore +logs.reportlines=ignore +logs.reportbanner=ignore +logs.reportline=ignore +logs.simplelines=ignore +logs.help=ignore local Carg,C,lpegmatch=lpeg.Carg,lpeg.C,lpeg.match local p_newline=lpeg.patterns.newline local linewise=( @@ -7584,10 +8287,11 @@ function logs.application(t) end return t end -function logs.system(whereto,process,jobname,category,...) - local message=formatters["%s %s => %s => %s => %s\r"](os.date("%d/%m/%y %H:%m:%S"),process,jobname,category,format(...)) +local f_syslog=formatters["%s %s => %s => %s => %s\r"] +function logs.system(whereto,process,jobname,category,fmt,arg,...) + local message=f_syslog(datetime("%d/%m/%y %H:%m:%S"),process,jobname,category,arg==nil and fmt or format(fmt,arg,...)) for i=1,10 do - local f=io.open(whereto,"a") + local f=openfile(whereto,"a") if f then f:write(message) f:close() @@ -7649,7 +8353,7 @@ do -- create closure to overcome 200 locals limit package.loaded["trac-inf"] = package.loaded["trac-inf"] or true --- original size: 6501, stripped down to: 5156 +-- original size: 6704, stripped down to: 5343 if not modules then modules={} end modules ['trac-inf']={ version=1.001, @@ -7659,7 +8363,7 @@ if not modules then modules={} end modules ['trac-inf']={ license="see context related readme files" } local type,tonumber,select=type,tonumber,select -local format,lower=string.format,string.lower +local format,lower,find=string.format,string.lower,string.find local concat=table.concat local clock=os.gettimeofday or os.clock local setmetatableindex=table.setmetatableindex @@ -7750,7 +8454,8 @@ function statistics.show() if statistics.enable then local register=statistics.register register("used platform",function() - return format("%s, type: %s, binary subtree: %s",os.platform or "unknown",os.type or "unknown",environment.texos or "unknown") + return format("%s, type: %s, binary subtree: %s", + os.platform or "unknown",os.type or "unknown",environment.texos or "unknown") end) register("luatex banner",function() return lower(status.banner) @@ -7763,14 +8468,23 @@ function statistics.show() return format("%s direct, %s indirect, %s total",total-indirect,indirect,total) end) if jit then - local status={ jit.status() } - if status[1] then - register("luajit status",function() - return concat(status," ",2) - end) - end - end - register("current memory usage",statistics.memused) + local jitstatus={ jit.status() } + if jitstatus[1] then + register("luajit options",concat(jitstatus," ",2)) + end + end + register("lua properties",function() + local list=status.list() + local hashchar=tonumber(list.luatex_hashchars) + local mask=lua.mask or "ascii" + return format("engine: %s, used memory: %s, hash type: %s, hash chars: min(%s,40), symbol mask: %s (%s)", + jit and "luajit" or "lua", + statistics.memused(), + list.luatex_hashtype or "default", + hashchar and 2^hashchar or "unknown", + mask, + mask=="utf" and "τεχ" or "tex") + end) register("runtime",statistics.runtime) logs.newline() for i=1,#statusinfo do @@ -7812,15 +8526,6 @@ function statistics.tracefunction(base,tag,...) statistics.register(formatters["%s.%s"](tag,name),function() return serialize(stat,"calls") end) end end -commands=commands or {} -function commands.resettimer(name) - resettiming(name or "whatever") - starttiming(name or "whatever") -end -function commands.elapsedtime(name) - stoptiming(name or "whatever") - context(elapsedtime(name or "whatever")) -end end -- of closure @@ -7829,7 +8534,7 @@ do -- create closure to overcome 200 locals limit package.loaded["trac-pro"] = package.loaded["trac-pro"] or true --- original size: 5773, stripped down to: 3453 +-- original size: 5829, stripped down to: 3501 if not modules then modules={} end modules ['trac-pro']={ version=1.001, @@ -7846,14 +8551,16 @@ local namespaces=namespaces local registered={} local function report_index(k,name) if trace_namespaces then - report_system("reference to %a in protected namespace %a: %s",k,name,debug.traceback()) + report_system("reference to %a in protected namespace %a: %s",k,name) + debugger.showtraceback(report_system) else report_system("reference to %a in protected namespace %a",k,name) end end local function report_newindex(k,name) if trace_namespaces then - report_system("assignment to %a in protected namespace %a: %s",k,name,debug.traceback()) + report_system("assignment to %a in protected namespace %a: %s",k,name) + debugger.showtraceback(report_system) else report_system("assignment to %a in protected namespace %a",k,name) end @@ -8104,7 +8811,7 @@ do -- create closure to overcome 200 locals limit package.loaded["util-deb"] = package.loaded["util-deb"] or true --- original size: 3708, stripped down to: 2568 +-- original size: 3898, stripped down to: 2644 if not modules then modules={} end modules ['util-deb']={ version=1.001, @@ -8184,20 +8891,22 @@ end function debugger.disable() debug.sethook() end -function traceback() - local level=1 +local function showtraceback(rep) + local level=2 + local reporter=rep or report while true do - local info=debug.getinfo(level,"Sl") + local info=getinfo(level,"Sl") if not info then break elseif info.what=="C" then - print(format("%3i : C function",level)) + reporter("%2i : %s",level-1,"C function") else - print(format("%3i : [%s]:%d",level,info.short_src,info.currentline)) + reporter("%2i : %s : %s",level-1,info.short_src,info.currentline) end level=level+1 end end +debugger.showtraceback=showtraceback end -- of closure @@ -8383,7 +9092,7 @@ do -- create closure to overcome 200 locals limit package.loaded["util-tpl"] = package.loaded["util-tpl"] or true --- original size: 6251, stripped down to: 3488 +-- original size: 7100, stripped down to: 3978 if not modules then modules={} end modules ['util-tpl']={ version=1.001, @@ -8425,7 +9134,7 @@ local sqlescape=lpeg.replacer { { "\r\n","\\n" }, { "\r","\\n" }, } -local sqlquoted=lpeg.Cs(lpeg.Cc("'")*sqlescape*lpeg.Cc("'")) +local sqlquoted=Cs(Cc("'")*sqlescape*Cc("'")) lpegpatterns.sqlescape=sqlescape lpegpatterns.sqlquoted=sqlquoted local luaescape=lpegpatterns.luaescape @@ -8448,12 +9157,24 @@ local quotedescapers={ local luaescaper=escapers.lua local quotedluaescaper=quotedescapers.lua local function replacekeyunquoted(s,t,how,recurse) - local escaper=how and escapers[how] or luaescaper - return escaper(replacekey(s,t,how,recurse)) + if how==false then + return replacekey(s,t,how,recurse) + else + local escaper=how and escapers[how] or luaescaper + return escaper(replacekey(s,t,how,recurse)) + end end local function replacekeyquoted(s,t,how,recurse) - local escaper=how and quotedescapers[how] or quotedluaescaper - return escaper(replacekey(s,t,how,recurse)) + if how==false then + return replacekey(s,t,how,recurse) + else + local escaper=how and quotedescapers[how] or quotedluaescaper + return escaper(replacekey(s,t,how,recurse)) + end +end +local function replaceoptional(l,m,r,t,how,recurse) + local v=t[l] + return v and v~="" and lpegmatch(replacer,r,1,t,how or "lua",recurse or false) or "" end local single=P("%") local double=P("%%") @@ -8468,11 +9189,16 @@ local nolquoted=lquoted/'' local norquoted=rquoted/'' local nolquotedq=lquotedq/'' local norquotedq=rquotedq/'' -local key=nosingle*((C((1-nosingle )^1)*Carg(1)*Carg(2)*Carg(3))/replacekey )*nosingle -local quoted=nolquotedq*((C((1-norquotedq)^1)*Carg(1)*Carg(2)*Carg(3))/replacekeyquoted )*norquotedq -local unquoted=nolquoted*((C((1-norquoted )^1)*Carg(1)*Carg(2)*Carg(3))/replacekeyunquoted)*norquoted +local noloptional=P("%?")/'' +local noroptional=P("?%")/'' +local nomoptional=P(":")/'' +local args=Carg(1)*Carg(2)*Carg(3) +local key=nosingle*((C((1-nosingle )^1)*args)/replacekey )*nosingle +local quoted=nolquotedq*((C((1-norquotedq )^1)*args)/replacekeyquoted )*norquotedq +local unquoted=nolquoted*((C((1-norquoted )^1)*args)/replacekeyunquoted)*norquoted +local optional=noloptional*((C((1-nomoptional)^1)*nomoptional*C((1-noroptional)^1)*args)/replaceoptional)*noroptional local any=P(1) - replacer=Cs((unquoted+quoted+escape+key+any)^0) + replacer=Cs((unquoted+quoted+escape+optional+key+any)^0) local function replace(str,mapping,how,recurse) if mapping and str then return lpegmatch(replacer,str,1,mapping,how or "lua",recurse or false) or str @@ -8511,7 +9237,7 @@ do -- create closure to overcome 200 locals limit package.loaded["util-env"] = package.loaded["util-env"] or true --- original size: 8807, stripped down to: 5085 +-- original size: 8022, stripped down to: 5038 if not modules then modules={} end modules ['util-env']={ version=1.001, @@ -8522,7 +9248,7 @@ if not modules then modules={} end modules ['util-env']={ } 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 unquoted,quoted,optionalquoted=string.unquoted,string.quoted,string.optionalquoted local concat,insert,remove=table.concat,table.insert,table.remove environment=environment or {} local environment=environment @@ -8635,24 +9361,14 @@ function environment.splitarguments(separator) return before,after end function environment.reconstructcommandline(arg,noquote) + local resolveprefix=resolvers.resolve arg=arg or environment.originalarguments if noquote and #arg==1 then - local a=arg[1] - a=resolvers.resolve(a) - a=unquoted(a) - return a + return unquoted(resolveprefix and resolveprefix(arg[1]) or arg[1]) elseif #arg>0 then local result={} for i=1,#arg do - local a=arg[i] - a=resolvers.resolve(a) - a=unquoted(a) - a=gsub(a,'"','\\"') - if find(a," ") then - result[#result+1]=quoted(a) - else - result[#result+1]=a - end + result[i]=optionalquoted(resolveprefix and resolveprefix(arg[i]) or resolveprefix) end return concat(result," ") else @@ -8708,7 +9424,7 @@ do -- create closure to overcome 200 locals limit package.loaded["luat-env"] = package.loaded["luat-env"] or true --- original size: 5930, stripped down to: 4235 +-- original size: 6174, stripped down to: 4141 if not modules then modules={} end modules ['luat-env']={ version=1.001, @@ -8786,15 +9502,13 @@ function environment.luafilechunk(filename,silent) filename=file.replacesuffix(filename,"lua") local fullname=environment.luafile(filename) if fullname and fullname~="" then - local data=luautilities.loadedluacode(fullname,strippable,filename) - if trace_locating then + local data=luautilities.loadedluacode(fullname,strippable,filename) + if not silent then report_lua("loading file %a %s",fullname,not data and "failed" or "succeeded") - elseif not silent then - texio.write("<",data and "+ " or "- ",fullname,">") end return data else - if trace_locating then + if not silent then report_lua("unknown file %a",filename) end return nil @@ -8863,7 +9577,7 @@ do -- create closure to overcome 200 locals limit package.loaded["lxml-tab"] = package.loaded["lxml-tab"] or true --- original size: 42447, stripped down to: 26589 +-- original size: 45683, stripped down to: 27866 if not modules then modules={} end modules ['lxml-tab']={ version=1.001, @@ -8878,10 +9592,10 @@ if lpeg.setmaxstack then lpeg.setmaxstack(1000) end xml=xml or {} local xml=xml local concat,remove,insert=table.concat,table.remove,table.insert -local type,next,setmetatable,getmetatable,tonumber=type,next,setmetatable,getmetatable,tonumber +local type,next,setmetatable,getmetatable,tonumber,rawset=type,next,setmetatable,getmetatable,tonumber,rawset local lower,find,match,gsub=string.lower,string.find,string.match,string.gsub local utfchar=utf.char -local lpegmatch=lpeg.match +local lpegmatch,lpegpatterns=lpeg.match,lpeg.patterns local P,S,R,C,V,C,Cs=lpeg.P,lpeg.S,lpeg.R,lpeg.C,lpeg.V,lpeg.C,lpeg.Cs local formatters=string.formatters xml.xmlns=xml.xmlns or {} @@ -8976,8 +9690,10 @@ local function add_end(spacing,namespace,tag) top=stack[#stack] if #stack<1 then errorstr=formatters["unable to close %s %s"](tag,xml.checkerror(top,toclose) or "") + report_xml(errorstr) elseif toclose.tg~=tag then errorstr=formatters["unable to close %s with %s %s"](toclose.tg,tag,xml.checkerror(top,toclose) or "") + report_xml(errorstr) end dt=top.dt dt[#dt+1]=toclose @@ -8986,10 +9702,29 @@ local function add_end(spacing,namespace,tag) end end local function add_text(text) + local n=#dt if cleanup and #text>0 then - dt[#dt+1]=cleanup(text) + if n>0 then + local s=dt[n] + if type(s)=="string" then + dt[n]=s..cleanup(text) + else + dt[n+1]=cleanup(text) + end + else + dt[1]=cleanup(text) + end else - dt[#dt+1]=text + if n>0 then + local s=dt[n] + if type(s)=="string" then + dt[n]=s..text + else + dt[n+1]=text + end + else + dt[1]=text + end end end local function add_special(what,spacing,text) @@ -9021,8 +9756,10 @@ local function attribute_specification_error(str) end return str end +local badentity="&error;" +local badentity="&" xml.placeholders={ - unknown_dec_entity=function(str) return str=="" and "&error;" or formatters["&%s;"](str) end, + unknown_dec_entity=function(str) return str=="" and badentity or formatters["&%s;"](str) end, unknown_hex_entity=function(str) return formatters["&#x%s;"](str) end, unknown_any_entity=function(str) return formatters["&#x%s;"](str) end, } @@ -9043,9 +9780,10 @@ local function fromdec(s) return formatters["d:%s"](s),true end end -local rest=(1-P(";"))^0 -local many=P(1)^0 -local parsedentity=P("&")*(P("#x")*(rest/fromhex)+P("#")*(rest/fromdec))*P(";")*P(-1)+(P("#x")*(many/fromhex)+P("#")*(many/fromdec)) +local p_rest=(1-P(";"))^0 +local p_many=P(1)^0 +local p_char=lpegpatterns.utf8character +local parsedentity=P("&")*(P("#x")*(p_rest/fromhex)+P("#")*(p_rest/fromdec))*P(";")*P(-1)+(P("#x")*(p_many/fromhex)+P("#")*(p_many/fromdec)) local predefined_unified={ [38]="&", [42]=""", @@ -9071,7 +9809,9 @@ local privates_u={ local privates_p={} local privates_n={ } -local escaped=utf.remapper(privates_u) +local escaped=utf.remapper(privates_u,"dynamic") +local unprivatized=utf.remapper(privates_p,"dynamic") +xml.unprivatized=unprivatized local function unescaped(s) local p=privates_n[s] if not p then @@ -9084,9 +9824,7 @@ local function unescaped(s) end return p end -local unprivatized=utf.remapper(privates_p) xml.privatetoken=unescaped -xml.unprivatized=unprivatized xml.privatecodes=privates_n local function handle_hex_entity(str) local h=hcache[str] @@ -9181,7 +9919,7 @@ local function handle_any_entity(str) report_xml("keeping entity &%s;",str) end if str=="" then - a="&error;" + a=badentity else a="&"..str..";" end @@ -9209,7 +9947,7 @@ local function handle_any_entity(str) if trace_entities then report_xml("invalid entity &%s;",str) end - a="&error;" + a=badentity acache[str]=a else if trace_entities then @@ -9222,8 +9960,14 @@ local function handle_any_entity(str) return a end end -local function handle_end_entity(chr) - report_xml("error in entity, %a found instead of %a",chr,";") +local function handle_end_entity(str) + report_xml("error in entity, %a found without ending %a",str,";") + return str +end +local function handle_crap_error(chr) + report_xml("error in parsing, unexpected %a found ",chr) + add_text(chr) + return chr end local space=S(' \r\n\t') local open=P('<') @@ -9239,15 +9983,15 @@ local valid=R('az','AZ','09')+S('_-.') local name_yes=C(valid^1)*colon*C(valid^1) local name_nop=C(P(true))*C(valid^1) local name=name_yes+name_nop -local utfbom=lpeg.patterns.utfbom +local utfbom=lpegpatterns.utfbom local spacing=C(space^0) -local anyentitycontent=(1-open-semicolon-space-close)^0 +local anyentitycontent=(1-open-semicolon-space-close-ampersand)^0 local hexentitycontent=R("AF","af","09")^0 local decentitycontent=R("09")^0 local parsedentity=P("#")/""*( P("x")/""*(hexentitycontent/handle_hex_entity)+(decentitycontent/handle_dec_entity) )+(anyentitycontent/handle_any_entity) -local entity=ampersand/""*parsedentity*((semicolon/"")+#(P(1)/handle_end_entity)) +local entity=(ampersand/"")*parsedentity*(semicolon/"")+ampersand*(anyentitycontent/handle_end_entity) local text_unparsed=C((1-open)^1) local text_parsed=Cs(((1-open-ampersand)^1+entity)^1) local somespace=space^1 @@ -9298,16 +10042,20 @@ local instruction=(spacing*begininstruction*someinstruction*endinstruction)/func local comment=(spacing*begincomment*somecomment*endcomment )/function(...) add_special("@cm@",...) end local cdata=(spacing*begincdata*somecdata*endcdata )/function(...) add_special("@cd@",...) end local doctype=(spacing*begindoctype*somedoctype*enddoctype )/function(...) add_special("@dt@",...) end +local crap_parsed=1-beginelement-endelement-emptyelement-begininstruction-begincomment-begincdata-ampersand +local crap_unparsed=1-beginelement-endelement-emptyelement-begininstruction-begincomment-begincdata +local parsedcrap=Cs((crap_parsed^1+entity)^1)/handle_crap_error +local unparsedcrap=Cs((crap_unparsed )^1)/handle_crap_error local trailer=space^0*(text_unparsed/set_message)^0 local grammar_parsed_text=P { "preamble", preamble=utfbom^0*instruction^0*(doctype+comment+instruction)^0*V("parent")*trailer, parent=beginelement*V("children")^0*endelement, - children=parsedtext+V("parent")+emptyelement+comment+cdata+instruction, + children=parsedtext+V("parent")+emptyelement+comment+cdata+instruction+parsedcrap, } local grammar_unparsed_text=P { "preamble", preamble=utfbom^0*instruction^0*(doctype+comment+instruction)^0*V("parent")*trailer, parent=beginelement*V("children")^0*endelement, - children=unparsedtext+V("parent")+emptyelement+comment+cdata+instruction, + children=unparsedtext+V("parent")+emptyelement+comment+cdata+instruction+unparsedcrap, } local function _xmlconvert_(data,settings) settings=settings or {} @@ -9341,7 +10089,6 @@ local function _xmlconvert_(data,settings) errorstr="empty xml file" elseif utfize or resolve then if lpegmatch(grammar_parsed_text,data) then - errorstr="" else errorstr="invalid xml file - parsed text" end @@ -9357,6 +10104,8 @@ local function _xmlconvert_(data,settings) local result if errorstr and errorstr~="" then result={ dt={ { ns="",tg="error",dt={ errorstr },at={},er=true } } } +setmetatable(result,mt) +setmetatable(result.dt[1],mt) setmetatable(stack,mt) local errorhandler=settings.error_handler if errorhandler==false then @@ -9389,8 +10138,11 @@ local function _xmlconvert_(data,settings) end if errorstr and errorstr~="" then result.error=true + else + errorstr=nil end result.statistics={ + errormessage=errorstr, entities={ decimals=dcache, hexadecimals=hcache, @@ -9404,7 +10156,7 @@ local function _xmlconvert_(data,settings) reported_attribute_errors,mt,errorhandler=nil,nil,nil return result end -function xmlconvert(data,settings) +local function xmlconvert(data,settings) local ok,result=pcall(function() return _xmlconvert_(data,settings) end) if ok then return result @@ -9496,14 +10248,17 @@ function xml.checkbom(root) insert(dt,2,"\n" ) end end -local function verbose_element(e,handlers) +local f_attribute=formatters['%s=%q'] +local function verbose_element(e,handlers,escape) local handle=handlers.handle local serialize=handlers.serialize local ens,etg,eat,edt,ern=e.ns,e.tg,e.at,e.dt,e.rn local ats=eat and next(eat) and {} if ats then + local n=0 for k,v in next,eat do - ats[#ats+1]=formatters['%s=%q'](k,escaped(v)) + n=n+1 + ats[n]=f_attribute(k,escaped(v)) end end if ern and trace_entities and ern~=ens then @@ -9588,23 +10343,25 @@ local function verbose_document(e,handlers) end end local function serialize(e,handlers,...) - local initialize=handlers.initialize - local finalize=handlers.finalize - local functions=handlers.functions - if initialize then - local state=initialize(...) - if not state==true then - return state + if e then + local initialize=handlers.initialize + local finalize=handlers.finalize + local functions=handlers.functions + if initialize then + local state=initialize(...) + if not state==true then + return state + end + end + local etg=e.tg + if etg then + (functions[etg] or functions["@el@"])(e,handlers) + else + functions["@dc@"](e,handlers) + end + if finalize then + return finalize() end - end - local etg=e.tg - if etg then - (functions[etg] or functions["@el@"])(e,handlers) - else - functions["@dc@"](e,handlers) - end - if finalize then - return finalize() end end local function xserialize(e,handlers) @@ -9845,7 +10602,7 @@ do -- create closure to overcome 200 locals limit package.loaded["lxml-lpt"] = package.loaded["lxml-lpt"] or true --- original size: 48956, stripped down to: 30516 +-- original size: 48229, stripped down to: 30684 if not modules then modules={} end modules ['lxml-lpt']={ version=1.001, @@ -10230,8 +10987,8 @@ 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")+S("-_:"))^1)*Cc("'])") -lp_fastpos_p=P("+")^0*R("09")^1*P(-1)/"l==%0" -lp_fastpos_n=P("-")*R("09")^1*P(-1)/"(%0<0 and (#list+%0==l))" +local lp_fastpos_p=P("+")^0*R("09")^1*P(-1)/"l==%0" +local lp_fastpos_n=P("-")*R("09")^1*P(-1)/"(%0<0 and (#list+%0==l))" local lp_fastpos=lp_fastpos_n+lp_fastpos_p local lp_reserved=C("and")+C("or")+C("not")+C("div")+C("mod")+C("true")+C("false") local lp_lua_function=Cs((R("az","AZ","__")^1*(P(".")*R("az","AZ","__")^1)^1)*("("))/"%0" @@ -10410,7 +11167,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[i]=(directive and tg) or format("not(%s)",tg) + t[#t+1]=(directive and tg) or format("not(%s)",tg) end if nodetest==false then return format("not(%s)",concat(t,"|")) @@ -10676,7 +11433,6 @@ expressions.print=function(...) print(...) return true end -expressions.contains=find expressions.find=find expressions.upper=upper expressions.lower=lower @@ -10698,6 +11454,9 @@ function expressions.contains(str,pattern) end return false end +function xml.expressions.idstring(str) + return type(str)=="string" and gsub(str,"^#","") or "" +end local function traverse(root,pattern,handle) local collected=applylpath(root,pattern) if collected then @@ -10826,8 +11585,13 @@ function xml.elements(root,pattern,reverse) local collected=applylpath(root,pattern) if not collected then return dummy - elseif reverse then - local c=#collected+1 + end + local n=#collected + if n==0 then + return dummy + end + if reverse then + local c=n+1 return function() if c>1 then c=c-1 @@ -10837,7 +11601,7 @@ function xml.elements(root,pattern,reverse) end end else - local n,c=#collected,0 + local c=0 return function() if c<n then c=c+1 @@ -10852,8 +11616,13 @@ function xml.collected(root,pattern,reverse) local collected=applylpath(root,pattern) if not collected then return dummy - elseif reverse then - local c=#collected+1 + end + local n=#collected + if n==0 then + return dummy + end + if reverse then + local c=n+1 return function() if c>1 then c=c-1 @@ -10861,7 +11630,7 @@ function xml.collected(root,pattern,reverse) end end else - local n,c=#collected,0 + local c=0 return function() if c<n then c=c+1 @@ -10876,7 +11645,7 @@ function xml.inspect(collection,pattern) report_lpath("pattern: %s\n\n%s\n",pattern,xml.tostring(e)) end end -local function split(e) +local function split(e) local dt=e.dt if dt then for i=1,#dt do @@ -10975,7 +11744,7 @@ do -- create closure to overcome 200 locals limit package.loaded["lxml-aux"] = package.loaded["lxml-aux"] or true --- original size: 23804, stripped down to: 16817 +-- original size: 28786, stripped down to: 20578 if not modules then modules={} end modules ['lxml-aux']={ version=1.001, @@ -10985,16 +11754,19 @@ if not modules then modules={} end modules ['lxml-aux']={ license="see context related readme files" } local trace_manipulations=false trackers.register("lxml.manipulations",function(v) trace_manipulations=v end) +local trace_inclusions=false trackers.register("lxml.inclusions",function(v) trace_inclusions=v end) local report_xml=logs.reporter("xml") local xml=xml -local xmlconvert,xmlcopy,xmlname=xml.convert,xml.copy,xml.name +local xmlcopy,xmlname=xml.copy,xml.name local xmlinheritedconvert=xml.inheritedconvert local xmlapplylpath=xml.applylpath local xmlfilter=xml.filter -local type,setmetatable,getmetatable=type,setmetatable,getmetatable +local type,next,setmetatable,getmetatable=type,next,setmetatable,getmetatable local insert,remove,fastcopy,concat=table.insert,table.remove,table.fastcopy,table.concat local gmatch,gsub,format,find,strip=string.gmatch,string.gsub,string.format,string.find,string.strip local utfbyte=utf.byte +local lpegmatch=lpeg.match +local striplinepatterns=utilities.strings.striplinepatterns local function report(what,pattern,c,e) report_xml("%s element %a, root %a, position %a, index %a, pattern %a",what,xmlname(e),xmlname(e.__p__),c,e.ni,pattern) end @@ -11049,13 +11821,15 @@ end function xml.each(root,pattern,handle,reverse) local collected=xmlapplylpath(root,pattern) if collected then - if reverse then - for c=#collected,1,-1 do - handle(collected[c]) - end - else - for c=1,#collected do - handle(collected[c]) + if handle then + if reverse then + for c=#collected,1,-1 do + handle(collected[c]) + end + else + for c=1,#collected do + handle(collected[c]) + end end end return collected @@ -11111,6 +11885,7 @@ local function redo_ni(d) end end end +xml.reindex=redo_ni local function xmltoelement(whatever,root) if not whatever then return nil @@ -11162,8 +11937,16 @@ function xml.delete(root,pattern) report('deleting',pattern,c,e) end local d=p.dt - remove(d,e.ni) - redo_ni(d) + local ni=e.ni + if ni<=#d then + if false then + p.dt[ni]="" + else + remove(d,ni) + redo_ni(d) + end + else + end end end end @@ -11283,28 +12066,40 @@ 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) +local function include(xmldata,pattern,attribute,recursive,loaddata,level) pattern=pattern or 'include' loaddata=loaddata or io.loaddata local collected=xmlapplylpath(xmldata,pattern) if collected then + if not level then + level=1 + end for c=1,#collected do local ek=collected[c] local name=nil local ekdt=ek.dt local ekat=ek.at - local epdt=ek.__p__.dt + local ekrt=ek.__p__ + local epdt=ekrt.dt if not attribute or attribute=="" then name=(type(ekdt)=="table" and ekdt[1]) or ekdt end if not name then for a in gmatch(attribute or "href","([^|]+)") do name=ekat[a] - if name then break end + if name then + break + end + end + end + local data=nil + if name and name~="" then + data=loaddata(name) or "" + if trace_inclusions then + report_xml("including %s bytes from %a at level %s by pattern %a and attribute %a (%srecursing)",#data,name,level,pattern,attribute or "",recursive and "" or "not ") end end - local data=(name and name~="" and loaddata(name)) or "" - if data=="" then + if not data or data=="" then epdt[ek.ni]="" elseif ekat["parse"]=="text" then epdt[ek.ni]=xml.escaped(data) @@ -11314,70 +12109,127 @@ local function include(xmldata,pattern,attribute,recursive,loaddata) epdt[ek.ni]="" else if recursive then - include(xi,pattern,attribute,recursive,loaddata) + include(xi,pattern,attribute,recursive,loaddata,level+1) + end + local child=xml.body(xi) + child.__p__=ekrt + child.__f__=name + epdt[ek.ni]=child + local inclusions=xmldata.settings.inclusions + if inclusions then + inclusions[#inclusions+1]=name + else + xmldata.settings.inclusions={ name } + end + if child.er then + local badinclusions=xmldata.settings.badinclusions + if badinclusions then + badinclusions[#badinclusions+1]=name + else + xmldata.settings.badinclusions={ name } + end end - epdt[ek.ni]=xml.body(xi) end end end end end xml.include=include +function xml.inclusion(e,default) + while e do + local f=e.__f__ + if f then + return f + else + e=e.__p__ + end + end + return default +end +local function getinclusions(key,e,sorted) + while e do + local settings=e.settings + if settings then + local inclusions=settings[key] + if inclusions then + inclusions=table.unique(inclusions) + if sorted then + table.sort(inclusions) + end + return inclusions + else + e=e.__p__ + end + else + e=e.__p__ + end + end +end +function xml.inclusions(e,sorted) + return getinclusions("inclusions",e,sorted) +end +function xml.badinclusions(e,sorted) + return getinclusions("badinclusions",e,sorted) +end +local b_collapser=lpeg.patterns.b_collapser +local m_collapser=lpeg.patterns.m_collapser +local e_collapser=lpeg.patterns.e_collapser +local b_stripper=lpeg.patterns.b_stripper +local m_stripper=lpeg.patterns.m_stripper +local e_stripper=lpeg.patterns.e_stripper +local lpegmatch=lpeg.match 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 n=#edt + if n==0 then + return e + elseif anywhere then + local t={} + local m=0 + for e=1,n do local str=edt[e] if type(str)~="string" then - n=n+1 - t[n]=str + m=m+1 + t[m]=str elseif str~="" then if nolines then - str=gsub(str,"%s+"," ") + str=lpegmatch((n==1 and b_collapser) or (n==m and e_collapser) or m_collapser,str) + else + str=lpegmatch((n==1 and b_stripper) or (n==m and e_stripper) or m_stripper,str) end - str=gsub(str,"^%s*(.-)%s*$","%1") if str~="" then - n=n+1 - t[n]=str + m=m+1 + t[m]=str end end end e.dt=t else - if #edt>0 then - local str=edt[1] - if type(str)~="string" then - elseif str=="" then + local str=edt[1] + if type(str)=="string" then + if str~="" then + str=lpegmatch(nolines and b_collapser or b_stripper,str) + end + if str=="" then remove(edt,1) + n=n-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 + edt[1]=str end end - local nedt=#edt - if nedt>0 then - local str=edt[nedt] - if type(str)~="string" then - elseif str=="" then - remove(edt) - else - if nolines then - str=gsub(str,"%s+"," ") - end - str=gsub(str,"%s+$","") + if n>0 then + str=edt[n] + if type(str)=="string" then if str=="" then remove(edt) else - edt[nedt]=str + str=lpegmatch(nolines and e_collapser or e_stripper,str) + if str=="" then + remove(edt) + else + edt[n]=str + end end end end @@ -11563,8 +12415,8 @@ function xml.finalizers.xml.cdata(collected) end return "" end -function xml.insertcomment(e,str,n) - table.insert(e.dt,n or 1,{ +function xml.insertcomment(e,str,n) + insert(e.dt,n or 1,{ tg="@cm@", ns="", special=true, @@ -11572,7 +12424,25 @@ function xml.insertcomment(e,str,n) dt={ str }, }) end -function xml.setcdata(e,str) +function xml.insertcdata(e,str,n) + insert(e.dt,n or 1,{ + tg="@cd@", + ns="", + special=true, + at={}, + dt={ str }, + }) +end +function xml.setcomment(e,str,n) + e.dt={ { + tg="@cm@", + ns="", + special=true, + at={}, + dt={ str }, + } } +end +function xml.setcdata(e,str) e.dt={ { tg="@cd@", ns="", @@ -11642,7 +12512,7 @@ local function recurse(e,action) for i=1,#edt do local str=edt[i] if type(str)~="string" then - recurse(str,action,recursive) + recurse(str,action) elseif str~="" then edt[i]=action(str) end @@ -11660,6 +12530,65 @@ function helpers.recursetext(collected,action,recursive) end end end +local specials={ + ["@rt@"]="root", + ["@pi@"]="instruction", + ["@cm@"]="comment", + ["@dt@"]="declaration", + ["@cd@"]="cdata", +} +local function convert(x,strip,flat) + local ns=x.ns + local tg=x.tg + local at=x.at + local dt=x.dt + local node=flat and { + [0]=(not x.special and (ns~="" and ns..":"..tg or tg)) or nil, + } or { + _namespace=ns~="" and ns or nil, + _tag=not x.special and tg or nil, + _type=specials[tg] or "_element", + } + if at then + for k,v in next,at do + node[k]=v + end + end + local n=0 + for i=1,#dt do + local di=dt[i] + if type(di)=="table" then + if flat and di.special then + else + di=convert(di,strip,flat) + if di then + n=n+1 + node[n]=di + end + end + elseif strip then + di=lpegmatch(strip,di) + if di~="" then + n=n+1 + node[n]=di + end + else + n=n+1 + node[n]=di + end + end + if next(node) then + return node + end +end +function xml.totable(x,strip,flat) + if type(x)=="table" then + if strip then + strip=striplinepatterns[strip] + end + return convert(x,strip,flat) + end +end end -- of closure @@ -12216,7 +13145,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-ini"] = package.loaded["data-ini"] or true --- original size: 7898, stripped down to: 5501 +-- original size: 11085, stripped down to: 7662 if not modules then modules={} end modules ['data-ini']={ version=1.001, @@ -12225,14 +13154,15 @@ if not modules then modules={} end modules ['data-ini']={ copyright="PRAGMA ADE / ConTeXt Development Team", license="see context related readme files", } +local next,type,getmetatable,rawset=next,type,getmetatable,rawset local gsub,find,gmatch,char=string.gsub,string.find,string.gmatch,string.char -local next,type=next,type local filedirname,filebasename,filejoin=file.dirname,file.basename,file.join +local ostype,osname,osuname,ossetenv,osgetenv=os.type,os.name,os.uname,os.setenv,os.getenv +local P,S,R,C,Cs,Cc,lpegmatch=lpeg.P,lpeg.S,lpeg.R,lpeg.C,lpeg.Cs,lpeg.Cc,lpeg.match 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 report_initialization=logs.reporter("resolvers","initialization") -local ostype,osname,ossetenv,osgetenv=os.type,os.name,os.setenv,os.getenv resolvers=resolvers or {} local resolvers=resolvers texconfig.kpse_init=false @@ -12360,15 +13290,108 @@ if not texroot or texroot=="" then ossetenv('TEXROOT',texroot) end environment.texroot=file.collapsepath(texroot) -if profiler then +if type(profiler)=="table" and not jit then directives.register("system.profile",function() profiler.start("luatex-profile.log") end) end -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 +local prefixes=utilities.storage.allocate() +resolvers.prefixes=prefixes +local resolved={} +local abstract={} +local dynamic={} +function resolvers.resetresolve(str) + resolved,abstract={},{} +end +function resolvers.allprefixes(separator) + local all=table.sortedkeys(prefixes) + if separator then + for i=1,#all do + all[i]=all[i]..":" + end + end + return all +end +local function _resolve_(method,target) + local action=prefixes[method] + if action then + return action(target) + else + return method..":"..target + end +end +function resolvers.unresolve(str) + return abstract[str] or str +end +function resolvers.setdynamic(str) + dynamic[str]=true +end +local pattern=Cs((C(R("az")^2)*P(":")*C((1-S(" \"\';,"))^1)/_resolve_+P(1))^0) +local prefix=C(R("az")^2)*P(":") +local target=C((1-S(" \"\';,"))^1) +local notarget=(#S(";,")+P(-1))*Cc("") +local p_resolve=Cs(((prefix*(target+notarget))/_resolve_+P(1))^0) +local p_simple=prefix*P(-1) +local function resolve(str) + if type(str)=="table" then + local res={} + for i=1,#str do + res[i]=resolve(str[i]) + end + return res + end + local res=resolved[str] + if res then + return res + end + local simple=lpegmatch(p_simple,str) + local action=prefixes[simple] + if action then + local res=action(res) + if not dynamic[simple] then + resolved[simple]=res + abstract[res]=simple + end + return res + end + res=lpegmatch(p_resolve,str) + resolved[str]=res + abstract[res]=str + return res +end +resolvers.resolve=resolve +if type(osuname)=="function" then + for k,v in next,osuname() do + if not prefixes[k] then + prefixes[k]=function() return v end + end + end +end +if ostype=="unix" then + local pattern + local function makepattern(t,k,v) + if t then + rawset(t,k,v) + end + local colon=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) + end + makepattern() + table.setmetatablenewindex(prefixes,makepattern) + function resolvers.repath(str) + return lpegmatch(pattern,str) + end +else + function resolvers.repath(str) + return str + end end @@ -12378,7 +13401,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-exp"] = package.loaded["data-exp"] or true --- original size: 15303, stripped down to: 9716 +-- original size: 17216, stripped down to: 10657 if not modules then modules={} end modules ['data-exp']={ version=1.001, @@ -12392,12 +13415,16 @@ local concat,sort=table.concat,table.sort local lpegmatch,lpegpatterns=lpeg.match,lpeg.patterns local Ct,Cs,Cc,Carg,P,C,S=lpeg.Ct,lpeg.Cs,lpeg.Cc,lpeg.Carg,lpeg.P,lpeg.C,lpeg.S local type,next=type,next +local isdir=lfs.isdir local ostype=os.type -local collapsepath=file.collapsepath +local collapsepath,joinpath,basename=file.collapsepath,file.join,file.basename 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) +local trace_globbing=true trackers.register("resolvers.globbing",function(v) trace_globbing=v end) local report_expansions=logs.reporter("resolvers","expansions") +local report_globbing=logs.reporter("resolvers","globbing") local resolvers=resolvers +local resolveprefix=resolvers.resolve local function f_both(a,b) local t,n={},0 for sb in gmatch(b,"[^,]+") do @@ -12487,35 +13514,27 @@ function resolvers.expandedpathfromlist(pathlist) end return newlist end -local cleanup=lpeg.replacer { - { "!","" }, - { "\\","/" }, -} -function resolvers.cleanpath(str) - local doslashes=(P("\\")/"/"+1)^0 - local donegation=(P("!")/"" )^0 - local homedir=lpegmatch(Cs(donegation*doslashes),environment.homedir or "") - if homedir=="~" 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 not str or find(str,"~") then - return "" - else - return lpegmatch(cleanup,str) +local usedhomedir=nil +local donegation=(P("!")/"" )^0 +local doslashes=(P("\\")/"/"+1)^0 +local function expandedhome() + if not usedhomedir then + usedhomedir=lpegmatch(Cs(donegation*doslashes),environment.homedir or "") + if usedhomedir=="~" or usedhomedir=="" or not isdir(usedhomedir) then + if trace_expansions then + report_expansions("no home dir set, ignoring dependent path using current path") end - end - else - local dohome=((P("~")+P("$HOME"))/homedir)^0 - local cleanup=Cs(donegation*dohome*doslashes) - function resolvers.cleanpath(str) - return str and lpegmatch(cleanup,str) or "" + usedhomedir="." end end - return resolvers.cleanpath(str) + return usedhomedir end -local expandhome=P("~")/"$HOME" +local dohome=((P("~")+P("$HOME")+P("%HOME%"))/expandedhome)^0 +local cleanup=Cs(donegation*dohome*doslashes) +resolvers.cleanpath=function(str) + return str and lpegmatch(cleanup,str) or "" +end +local expandhome=P("~")/"$HOME" local dodouble=P('"')/""*(expandhome+(1-P('"')))^0*P('"')/"" local dosingle=P("'")/""*(expandhome+(1-P("'")))^0*P("'")/"" local dostring=(expandhome+1 )^0 @@ -12567,46 +13586,67 @@ function resolvers.splitpath(str) end function resolvers.joinpath(str) if type(str)=='table' then - return file.joinpath(str) + return joinpath(str) else return str end end local attributes,directory=lfs.attributes,lfs.dir local weird=P(".")^1+lpeg.anywhere(S("~`!#$%^&*()={}[]:;\"\'||<>,?\n\r\t")) +local lessweird=P(".")^1+lpeg.anywhere(S("~`#$%^&*:;\"\'||<>,?\n\r\t")) local timer={} local scanned={} local nofscans=0 local scancache={} -local function scan(files,spec,path,n,m,r) - local full=(path=="" and spec) or (spec..path..'/') +local fullcache={} +local nofsharedscans=0 +local function scan(files,remap,spec,path,n,m,r,onlyone,tolerant) + local full=path=="" and spec or (spec..path..'/') local dirs={} local nofdirs=0 + local pattern=tolerant and lessweird or weird for name in directory(full) do - if not lpegmatch(weird,name) then - local mode=attributes(full..name,'mode') - if mode=='file' then + if not lpegmatch(pattern,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 } + local lower=lower(name) + local paths=files[lower] + if paths then + if onlyone then else - f[#f+1]=path + if type(paths)=="string" then + files[lower]={ paths,path } + else + paths[#paths+1]=path + end + if name~=lower then + local rl=remap[lower] + if not rl then + remap[lower]=name + r=r+1 + elseif trace_globbing and rl~=name then + report_globbing("confusing filename, name: %a, lower: %a, already: %a",name,lower,rl) + end + end end else - files[name]=path - local lower=lower(name) + files[lower]=path if name~=lower then - files["remap:"..lower]=name - r=r+1 + local rl=remap[lower] + if not rl then + remap[lower]=name + r=r+1 + elseif trace_globbing and rl~=name then + report_globbing("confusing filename, name: %a, lower: %a, already: %a",name,lower,rl) + end end end - elseif mode=='directory' then + elseif mode=="directory" then m=m+1 nofdirs=nofdirs+1 if path~="" then - dirs[nofdirs]=path..'/'..name + dirs[nofdirs]=path.."/"..name else dirs[nofdirs]=name end @@ -12616,107 +13656,69 @@ local function scan(files,spec,path,n,m,r) if nofdirs>0 then sort(dirs) for i=1,nofdirs do - files,n,m,r=scan(files,spec,dirs[i],n,m,r) + files,remap,n,m,r=scan(files,remap,spec,dirs[i],n,m,r,onlyonce,tolerant) end end scancache[sub(full,1,-2)]=files - return files,n,m,r + return files,remap,n,m,r end -local fullcache={} -function resolvers.scanfiles(path,branch,usecache) - statistics.starttiming(timer) - local realpath=resolvers.resolve(path) +function resolvers.scanfiles(path,branch,usecache,onlyonce,tolerant) + local realpath=resolveprefix(path) if usecache then - local files=fullcache[realpath] - if files then + local content=fullcache[realpath] + if content then if trace_locating then - report_expansions("using caches scan of path %a, branch %a",path,branch or path) + report_expansions("using cached scan of path %a, branch %a",path,branch or path) end - return files + nofsharedscans=nofsharedscans+1 + return content end end + statistics.starttiming(timer) if trace_locating then report_expansions("scanning path %a, branch %a",path,branch or path) end - local files,n,m,r=scan({},realpath..'/',"",0,0,0) - files.__path__=path - 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 - if usecache then - scanned[#scanned+1]=realpath - fullcache[realpath]=files - end - nofscans=nofscans+1 - statistics.stoptiming(timer) - return files -end -local function simplescan(files,spec,path) - local full=(path=="" and spec) or (spec..path..'/') - local dirs={} - local nofdirs=0 - for name in directory(full) do - if not lpegmatch(weird,name) then - local mode=attributes(full..name,'mode') - if mode=='file' then - if not files[name] then - files[name]=path - end - elseif mode=='directory' then - 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=simplescan(files,spec,dirs[i]) - end - end - return files -end -local simplecache={} -local nofsharedscans=0 -function resolvers.simplescanfiles(path,branch,usecache) - statistics.starttiming(timer) - local realpath=resolvers.resolve(path) - if usecache then - local files=simplecache[realpath] - if not files then - files=scancache[realpath] - if files then - nofsharedscans=nofsharedscans+1 - end + local content + if isdir(realpath) then + local files,remap,n,m,r=scan({},{},realpath..'/',"",0,0,0,onlyonce,tolerant) + content={ + metadata={ + path=path, + files=n, + directories=m, + remappings=r, + }, + files=files, + remap=remap, + } + if trace_locating then + report_expansions("%s files found on %s directories with %s uppercase remappings",n,m,r) end - if files then - if trace_locating then - report_expansions("using caches scan of path %a, branch %a",path,branch or path) - end - return files + else + content={ + metadata={ + path=path, + files=0, + directories=0, + remappings=0, + }, + files={}, + remap={}, + } + if trace_locating then + report_expansions("invalid path %a",realpath) end end - if trace_locating then - report_expansions("scanning path %a, branch %a",path,branch or path) - end - local files=simplescan({},realpath..'/',"") - if trace_locating then - report_expansions("%s files found",table.count(files)) - end if usecache then scanned[#scanned+1]=realpath - simplecache[realpath]=files + fullcache[realpath]=content end nofscans=nofscans+1 statistics.stoptiming(timer) - return files + return content +end +function resolvers.simplescanfiles(path,branch,usecache) + return resolvers.scanfiles(path,branch,usecache,true,true) end function resolvers.scandata() table.sort(scanned) @@ -12727,6 +13729,52 @@ function resolvers.scandata() paths=scanned, } end +function resolvers.get_from_content(content,path,name) + if not content then + return + end + local files=content.files + if not files then + return + end + local remap=content.remap + if not remap then + return + end + if name then + local used=lower(name) + return path,remap[used] or used + else + local name=path + local used=lower(name) + local path=files[used] + if path then + return path,remap[used] or used + end + end +end +local nothing=function() end +function resolvers.filtered_from_content(content,pattern) + if content and type(pattern)=="string" then + local pattern=lower(pattern) + local files=content.files + local remap=content.remap + if files and remap then + local n=next(files) + local function iterator() + while n do + local k=n + n=next(files,k) + if find(k,pattern) then + return files[k],remap and remap[k] or k + end + end + end + return iterator + end + end + return nothing +end end -- of closure @@ -12735,7 +13783,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-env"] = package.loaded["data-env"] or true --- original size: 8769, stripped down to: 6490 +-- original size: 9216, stripped down to: 6798 if not modules then modules={} end modules ['data-env']={ version=1.001, @@ -12753,10 +13801,12 @@ local formats=allocate() local suffixes=allocate() local dangerous=allocate() local suffixmap=allocate() +local usertypes=allocate() resolvers.formats=formats resolvers.suffixes=suffixes resolvers.dangerous=dangerous resolvers.suffixmap=suffixmap +resolvers.usertypes=usertypes local luasuffixes=utilities.lua.suffixes local relations=allocate { core={ @@ -12824,11 +13874,13 @@ local relations=allocate { names={ "mp" }, variable='MPINPUTS', suffixes={ 'mp','mpvi','mpiv','mpii' }, + usertype=true, }, tex={ names={ "tex" }, variable='TEXINPUTS', - suffixes={ 'tex',"mkvi","mkiv","mkii" }, + suffixes={ "tex","mkvi","mkiv","mkii","cld","lfg","xml" }, + usertype=true, }, icc={ names={ "icc","icc profile","icc profiles" }, @@ -12844,6 +13896,7 @@ local relations=allocate { names={ "lua" }, variable='LUAINPUTS', suffixes={ luasuffixes.lua,luasuffixes.luc,luasuffixes.tma,luasuffixes.tmc }, + usertype=true, }, lib={ names={ "lib" }, @@ -12852,11 +13905,15 @@ local relations=allocate { }, bib={ names={ 'bib' }, + variable='BIBINPUTS', suffixes={ 'bib' }, + usertype=true, }, bst={ names={ 'bst' }, + variable='BSTINPUTS', suffixes={ 'bst' }, + usertype=true, }, fontconfig={ names={ 'fontconfig','fontconfig file','fontconfig files' }, @@ -12938,8 +13995,9 @@ function resolvers.updaterelations() for name,relation in next,categories do local rn=relation.names local rv=relation.variable - local rs=relation.suffixes if rn and rv then + local rs=relation.suffixes + local ru=relation.usertype for i=1,#rn do local rni=lower(gsub(rn[i]," ","")) formats[rni]=rv @@ -12951,8 +14009,9 @@ function resolvers.updaterelations() end end end - end - if rs then + if ru then + usertypes[name]=true + end end end end @@ -13003,7 +14062,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-tmp"] = package.loaded["data-tmp"] or true --- original size: 15532, stripped down to: 11648 +-- original size: 15618, stripped down to: 11629 if not modules then modules={} end modules ['data-tmp']={ version=1.100, @@ -13013,7 +14072,7 @@ if not modules then modules={} end modules ['data-tmp']={ license="see context related readme files" } local format,lower,gsub,concat=string.format,string.lower,string.gsub,table.concat -local concat,serialize,serializetofile=table.concat,table.serialize,table.tofile +local concat=table.concat local mkdirs,isdir,isfile=dir.mkdirs,lfs.isdir,lfs.isfile local addsuffix,is_writable,is_readable=file.addsuffix,file.is_writable,file.is_readable local formatters=string.formatters @@ -13022,6 +14081,7 @@ local trace_cache=false trackers.register("resolvers.cache",function(v) trace_ca local report_caches=logs.reporter("resolvers","caches") local report_resolvers=logs.reporter("resolvers","caching") local resolvers=resolvers +local cleanpath=resolvers.cleanpath local directive_cleanup=false directives.register("system.compile.cleanup",function(v) directive_cleanup=v end) local directive_strip=false directives.register("system.compile.strip",function(v) directive_strip=v end) local compile=utilities.lua.compile @@ -13043,7 +14103,7 @@ caches.relocate=false caches.defaults={ "TMPDIR","TEMPDIR","TMP","TEMP","HOME","HOMEPATH" } local writable,readables,usedreadables=nil,{},{} local function identify() - local texmfcaches=resolvers.cleanpathlist("TEXMFCACHE") + local texmfcaches=resolvers.cleanpathlist("TEXMFCACHE") if texmfcaches then for k=1,#texmfcaches do local cachepath=texmfcaches[k] @@ -13281,15 +14341,11 @@ end local saveoptions={ compact=true } 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,serialize(data,true,saveoptions)) + file.savedata(tmaname,table.serialize(data,true,saveoptions)) else - serializetofile(tmaname,data,true,saveoptions) + table.tofile(tmaname,data,true,saveoptions) end utilities.lua.compile(tmaname,tmcname) end @@ -13297,10 +14353,12 @@ local content_state={} function caches.contentstate() return content_state or {} end -function caches.loadcontent(cachename,dataname) - local name=caches.hashed(cachename) - local full,path=caches.getfirstreadablefile(addsuffix(name,luasuffixes.lua),"trees") - local filename=file.join(path,name) +function caches.loadcontent(cachename,dataname,filename) + if not filename then + local name=caches.hashed(cachename) + local full,path=caches.getfirstreadablefile(addsuffix(name,luasuffixes.lua),"trees") + filename=file.join(path,name) + end local blob=loadfile(addsuffix(filename,luasuffixes.luc)) or loadfile(addsuffix(filename,luasuffixes.lua)) if blob then local data=blob() @@ -13332,10 +14390,12 @@ function caches.collapsecontent(content) end end end -function caches.savecontent(cachename,dataname,content) - local name=caches.hashed(cachename) - local full,path=caches.setfirstwritablefile(addsuffix(name,luasuffixes.lua),"trees") - local filename=file.join(path,name) +function caches.savecontent(cachename,dataname,content,filename) + if not filename then + local name=caches.hashed(cachename) + local full,path=caches.setfirstwritablefile(addsuffix(name,luasuffixes.lua),"trees") + filename=file.join(path,name) + end local luaname=addsuffix(filename,luasuffixes.lua) local lucname=addsuffix(filename,luasuffixes.luc) if trace_locating then @@ -13350,7 +14410,7 @@ function caches.savecontent(cachename,dataname,content) content=content, uuid=os.uuid(), } - local ok=io.savedata(luaname,serialize(data,true)) + local ok=io.savedata(luaname,table.serialize(data,true)) if ok then if trace_locating then report_resolvers("category %a, cachename %a saved in %a",dataname,cachename,luaname) @@ -13378,7 +14438,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-met"] = package.loaded["data-met"] or true --- original size: 5453, stripped down to: 4007 +-- original size: 5347, stripped down to: 4015 if not modules then modules={} end modules ['data-met']={ version=1.100, @@ -13406,8 +14466,8 @@ local function splitmethod(filename) if type(filename)=="table" then return filename end - filename=file.collapsepath(filename,".") - if not find(filename,"://") then + filename=file.collapsepath(filename,".") + if not find(filename,"://",1,true) then return { scheme="file",path=filename,original=filename,filename=filename } end local specification=url.hashed(filename) @@ -13497,7 +14557,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-res"] = package.loaded["data-res"] or true --- original size: 61799, stripped down to: 42957 +-- original size: 67003, stripped down to: 46291 if not modules then modules={} end modules ['data-res']={ version=1.001, @@ -13507,7 +14567,7 @@ if not modules then modules={} end modules ['data-res']={ license="see context related readme files", } local gsub,find,lower,upper,match,gmatch=string.gsub,string.find,string.lower,string.upper,string.match,string.gmatch -local concat,insert,sortedkeys=table.concat,table.insert,table.sortedkeys +local concat,insert,remove,sortedkeys,sortedhash=table.concat,table.insert,table.remove,table.sortedkeys,table.sortedhash local next,type,rawget=next,type,rawget local os=os 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 @@ -13516,27 +14576,38 @@ local formatters=string.formatters local filedirname=file.dirname local filebasename=file.basename local suffixonly=file.suffixonly +local addsuffix=file.addsuffix +local removesuffix=file.removesuffix local filejoin=file.join local collapsepath=file.collapsepath local joinpath=file.joinpath +local is_qualified_path=file.is_qualified_path local allocate=utilities.storage.allocate local settings_to_array=utilities.parsers.settings_to_array +local getcurrentdir=lfs.currentdir +local isfile=lfs.isfile +local isdir=lfs.isdir local setmetatableindex=table.setmetatableindex local luasuffixes=utilities.lua.suffixes -local getcurrentdir=lfs.currentdir -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 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 trace_paths=false trackers .register("resolvers.paths",function(v) trace_paths=v end) +local resolve_otherwise=true directives.register("resolvers.otherwise",function(v) resolve_otherwise=v end) local report_resolving=logs.reporter("resolvers","resolving") local resolvers=resolvers local expandedpathfromlist=resolvers.expandedpathfromlist local checkedvariable=resolvers.checkedvariable local splitconfigurationpath=resolvers.splitconfigurationpath local methodhandler=resolvers.methodhandler +local filtered=resolvers.filtered_from_content +local lookup=resolvers.get_from_content +local cleanpath=resolvers.cleanpath +local resolveprefix=resolvers.resolve local initializesetter=utilities.setters.initialize local ostype,osname,osenv,ossetenv,osgetenv=os.type,os.name,os.env,os.setenv,os.getenv -resolvers.cacheversion='1.0.1' -resolvers.configbanner='' +resolvers.cacheversion="1.100" +resolvers.configbanner="" resolvers.homedir=environment.homedir resolvers.criticalvars=allocate { "SELFAUTOLOC","SELFAUTODIR","SELFAUTOPARENT","TEXMFCNF","TEXMF","TEXOS" } resolvers.luacnfname="texmfcnf.lua" @@ -13555,6 +14626,7 @@ end local unset_variable="unset" local formats=resolvers.formats local suffixes=resolvers.suffixes +local usertypes=resolvers.usertypes local dangerous=resolvers.dangerous local suffixmap=resolvers.suffixmap resolvers.defaultsuffixes={ "tex" } @@ -13563,7 +14635,7 @@ local instance=resolvers.instance or nil function resolvers.setenv(key,value,raw) if instance then instance.environment[key]=value - ossetenv(key,raw and value or resolvers.resolve(value)) + ossetenv(key,raw and value or resolveprefix(value)) end end local function getenv(key) @@ -13577,7 +14649,7 @@ local function getenv(key) end resolvers.getenv=getenv resolvers.env=getenv -local function resolve(k) +local function resolvevariable(k) return instance.expansions[k] end local dollarstripper=lpeg.stripper("$") @@ -13586,19 +14658,19 @@ local backslashswapper=lpeg.replacer("\\","/") local somevariable=P("$")/"" local somekey=C(R("az","AZ","09","__","--")^1) local somethingelse=P(";")*((1-S("!{}/\\"))^1*P(";")/"")+P(";")*(P(";")/"")+P(1) -local variableexpander=Cs((somevariable*(somekey/resolve)+somethingelse)^1 ) +local variableexpander=Cs((somevariable*(somekey/resolvevariable)+somethingelse)^1 ) local cleaner=P("\\")/"/"+P(";")*S("!{}/\\")^0*P(";")^1/";" local variablecleaner=Cs((cleaner+P(1))^0) -local somevariable=R("az","AZ","09","__","--")^1/resolve +local somevariable=R("az","AZ","09","__","--")^1/resolvevariable local variable=(P("$")/"")*(somevariable+(P("{")/"")*somevariable*(P("}")/"")) local variableresolver=Cs((variable+P(1))^0) local function expandedvariable(var) return lpegmatch(variableexpander,var) or var end -function resolvers.newinstance() - if trace_locating then +function resolvers.newinstance() + if trace_locating then report_resolving("creating instance") - end + end local environment,variables,expansions,order=allocate(),allocate(),allocate(),allocate() local newinstance={ environment=environment, @@ -13611,6 +14683,7 @@ function resolvers.newinstance() foundintrees=allocate(), hashes=allocate(), hashed=allocate(), + pathlists=false, specification=allocate(), lists=allocate(), data=allocate(), @@ -13623,6 +14696,7 @@ function resolvers.newinstance() savelists=true, pattern=nil, force_suffixes=true, + pathstack={}, } setmetatableindex(variables,function(t,k) local v @@ -13672,8 +14746,13 @@ function resolvers.reset() end local function reset_hashes() instance.lists={} + instance.pathlists=false instance.found={} end +local function reset_caches() + instance.lists={} + instance.pathlists=false +end local slash=P("/") local pathexpressionpattern=Cs ( Cc("^")*( @@ -13725,13 +14804,13 @@ local function identify_configuration_files() for i=1,#cnfpaths do local filepath=cnfpaths[i] local filename=collapsepath(filejoin(filepath,luacnfname)) - local realname=resolvers.resolve(filename) + local realname=resolveprefix(filename) if trace_locating then - local fullpath=gsub(resolvers.resolve(collapsepath(filepath)),"//","/") - local weirdpath=find(fullpath,"/texmf.+/texmf") or not find(fullpath,"/web2c") + local fullpath=gsub(resolveprefix(collapsepath(filepath)),"//","/") + local weirdpath=find(fullpath,"/texmf.+/texmf") or not find(fullpath,"/web2c",1,true) report_resolving("looking for %a on %s path %a from specification %a",luacnfname,weirdpath and "weird" or "given",fullpath,filepath) end - if lfs.isfile(realname) then + if isfile(realname) then specification[#specification+1]=filename if trace_locating then report_resolving("found configuration file %a",realname) @@ -13753,7 +14832,7 @@ local function load_configuration_files() local filename=specification[i] local pathname=filedirname(filename) local filename=filejoin(pathname,luacnfname) - local realname=resolvers.resolve(filename) + local realname=resolveprefix(filename) local blob=loadfile(realname) if blob then local setups=instance.setups @@ -13761,7 +14840,7 @@ local function load_configuration_files() local parent=data and data.parent if parent then local filename=filejoin(pathname,parent) - local realname=resolvers.resolve(filename) + local realname=resolveprefix(filename) local blob=loadfile(realname) if blob then local parentdata=blob() @@ -13786,7 +14865,7 @@ local function load_configuration_files() elseif variables[k]==nil then if trace_locating and not warning then report_resolving("variables like %a in configuration file %a should move to the 'variables' subtable", - k,resolvers.resolve(filename)) + k,resolveprefix(filename)) warning=true end variables[k]=v @@ -13846,7 +14925,7 @@ local function locate_file_databases() local stripped=lpegmatch(inhibitstripper,path) if stripped~="" then local runtime=stripped==path - path=resolvers.cleanpath(path) + path=cleanpath(path) local spec=resolvers.splitmethod(stripped) if runtime and (spec.noscheme or spec.scheme=="file") then stripped="tree:///"..stripped @@ -13909,8 +14988,8 @@ function resolvers.renew(hashname) report_resolving("identifying tree %a",hashname) end end - local realpath=resolvers.resolve(hashname) - if lfs.isdir(realpath) then + local realpath=resolveprefix(hashname) + if isdir(realpath) then if trace_locating then report_resolving("using path %a",realpath) end @@ -14011,19 +15090,53 @@ end function resolvers.unexpandedpath(str) return joinpath(resolvers.unexpandedpathlist(str)) end +function resolvers.pushpath(name) + local pathstack=instance.pathstack + local lastpath=pathstack[#pathstack] + local pluspath=filedirname(name) + if lastpath then + lastpath=collapsepath(filejoin(lastpath,pluspath)) + else + lastpath=collapsepath(pluspath) + end + insert(pathstack,lastpath) + if trace_paths then + report_resolving("pushing path %a",lastpath) + end +end +function resolvers.poppath() + local pathstack=instance.pathstack + if trace_paths and #pathstack>0 then + report_resolving("popping path %a",pathstack[#pathstack]) + end + remove(pathstack) +end +function resolvers.stackpath() + local pathstack=instance.pathstack + local currentpath=pathstack[#pathstack] + return currentpath~="" and currentpath or nil +end local done={} function resolvers.resetextrapath() local ep=instance.extra_paths if not ep then - ep,done={},{} - instance.extra_paths=ep + done={} + instance.extra_paths={} elseif #ep>0 then - instance.lists,done={},{} + done={} + reset_caches() end end function resolvers.registerextrapath(paths,subpaths) - paths=settings_to_array(paths) - subpaths=settings_to_array(subpaths) + if not subpaths or subpaths=="" then + if not paths or path=="" then + return + elseif done[paths] then + return + end + end + local paths=settings_to_array(paths) + local subpaths=settings_to_array(subpaths) local ep=instance.extra_paths or {} local oldn=#ep local newn=oldn @@ -14038,7 +15151,7 @@ function resolvers.registerextrapath(paths,subpaths) local ps=p.."/"..s if not done[ps] then newn=newn+1 - ep[newn]=resolvers.cleanpath(ps) + ep[newn]=cleanpath(ps) done[ps]=true end end @@ -14048,7 +15161,7 @@ function resolvers.registerextrapath(paths,subpaths) local p=paths[i] if not done[p] then newn=newn+1 - ep[newn]=resolvers.cleanpath(p) + ep[newn]=cleanpath(p) done[p]=true end end @@ -14060,7 +15173,7 @@ function resolvers.registerextrapath(paths,subpaths) local ps=ep[i].."/"..s if not done[ps] then newn=newn+1 - ep[newn]=resolvers.cleanpath(ps) + ep[newn]=cleanpath(ps) done[ps]=true end end @@ -14069,52 +15182,70 @@ function resolvers.registerextrapath(paths,subpaths) if newn>0 then instance.extra_paths=ep end - if newn>oldn then - instance.lists={} + if newn~=oldn then + reset_caches() end end -local function made_list(instance,list) - local ep=instance.extra_paths - if not ep or #ep==0 then - return list +function resolvers.pushextrapath(path) + local paths=settings_to_array(path) + if instance.extra_stack then + insert(instance.extra_stack,1,paths) else - local done,new,newn={},{},0 - for k=1,#list do - local v=list[k] - if not done[v] then - if find(v,"^[%.%/]$") then - done[v]=true - newn=newn+1 - new[newn]=v - else - break - end - end - end - for k=1,#ep do - local v=ep[k] + instance.extra_stack={ paths } + end + reset_caches() +end +function resolvers.popextrapath() + if instance.extra_stack then + reset_caches() + return remove(instance.extra_stack,1) + end +end +local function made_list(instance,list,extra_too) + local done={} + local new={} + local newn=0 + local function add(p) + for k=1,#p do + local v=p[k] if not done[v] then done[v]=true newn=newn+1 new[newn]=v end end - for k=1,#list do - local v=list[k] - if not done[v] then - done[v]=true - newn=newn+1 - new[newn]=v + end + for k=1,#list do + local v=list[k] + if done[v] then + elseif find(v,"^[%.%/]$") then + done[v]=true + newn=newn+1 + new[newn]=v + else + break + end + end + if extra_too then + local es=instance.extra_stack + if es and #es>0 then + for k=1,#es do + add(es[k]) end end - return new + local ep=instance.extra_paths + if ep and #ep>0 then + add(ep) + end end + add(list) + return new end function resolvers.cleanpathlist(str) local t=resolvers.expandedpathlist(str) if t then for i=1,#t do - t[i]=collapsepath(resolvers.cleanpath(t[i])) + t[i]=collapsepath(cleanpath(t[i])) end end return t @@ -14122,22 +15253,22 @@ end function resolvers.expandpath(str) return joinpath(resolvers.expandedpathlist(str)) end -function resolvers.expandedpathlist(str) +function resolvers.expandedpathlist(str,extra_too) if not str then return {} - elseif instance.savelists then + elseif instance.savelists then str=lpegmatch(dollarstripper,str) local lists=instance.lists local lst=lists[str] if not lst then - local l=made_list(instance,resolvers.splitpath(resolvers.expansion(str))) + local l=made_list(instance,resolvers.splitpath(resolvers.expansion(str)),extra_too) lst=expandedpathfromlist(l) lists[str]=lst end return lst else local lst=resolvers.splitpath(resolvers.expansion(str)) - return made_list(instance,expandedpathfromlist(lst)) + return made_list(instance,expandedpathfromlist(lst),extra_too) end end function resolvers.expandedpathlistfromvariable(str) @@ -14148,6 +15279,13 @@ end function resolvers.expandpathfromvariable(str) return joinpath(resolvers.expandedpathlistfromvariable(str)) end +function resolvers.cleanedpathlist(v) + local t=resolvers.expandedpathlist(v) + for i=1,#t do + t[i]=resolvers.resolve(resolvers.cleanpath(t[i])) + end + return t +end function resolvers.expandbraces(str) local ori=str local pth=expandedpathfromlist(resolvers.splitpath(ori)) @@ -14164,7 +15302,7 @@ function resolvers.registerfilehash(name,content,someerror) end end local function isreadable(name) - local readable=lfs.isfile(name) + local readable=isfile(name) if trace_detail then if readable then report_resolving("file %a is readable",name) @@ -14174,70 +15312,57 @@ local function isreadable(name) end return readable end -local function collect_files(names) - local filelist,noffiles={},0 +local function collect_files(names) + local filelist={} + local noffiles=0 + local function check(hash,root,pathname,path,name) + if not pathname or find(path,pathname) then + local variant=hash.type + local search=filejoin(root,path,name) + local result=methodhandler('concatinators',variant,root,path,name) + if trace_detail then + report_resolving("match: variant %a, search %a, result %a",variant,search,result) + end + noffiles=noffiles+1 + filelist[noffiles]={ variant,search,result } + end + end for k=1,#names do - local fname=names[k] + local filename=names[k] if trace_detail then - report_resolving("checking name %a",fname) + report_resolving("checking name %a",filename) end - local bname=filebasename(fname) - local dname=filedirname(fname) - if dname=="" or find(dname,"^%.") then - dname=false + local basename=filebasename(filename) + local pathname=filedirname(filename) + if pathname=="" or find(pathname,"^%.") then + pathname=false else - dname=gsub(dname,"%*",".*") - dname="/"..dname.."$" + pathname=gsub(pathname,"%*",".*") + pathname="/"..pathname.."$" end local hashes=instance.hashes for h=1,#hashes do local hash=hashes[h] - local blobpath=hash.name - local files=blobpath and instance.files[blobpath] - if files then + local hashname=hash.name + local content=hashname and instance.files[hashname] + if content then if trace_detail then - report_resolving("deep checking %a, base %a, pattern %a",blobpath,bname,dname) + report_resolving("deep checking %a, base %a, pattern %a",hashname,basename,pathname) end - local blobfile=files[bname] - if not blobfile then - local rname="remap:"..bname - blobfile=files[rname] - if blobfile then - bname=files[rname] - blobfile=files[bname] - end - end - if blobfile then - local blobroot=files.__path__ or blobpath - if type(blobfile)=='string' then - if not dname or find(blobfile,dname) then - local variant=hash.type - local search=filejoin(blobroot,blobfile,bname) - local result=methodhandler('concatinators',hash.type,blobroot,blobfile,bname) - if trace_detail then - report_resolving("match: variant %a, search %a, result %a",variant,search,result) - end - noffiles=noffiles+1 - filelist[noffiles]={ variant,search,result } - end + local path,name=lookup(content,basename) + if path then + local metadata=content.metadata + local realroot=metadata and metadata.path or hashname + if type(path)=="string" then + check(hash,realroot,pathname,path,name) else - for kk=1,#blobfile do - local vv=blobfile[kk] - if not dname or find(vv,dname) then - local variant=hash.type - local search=filejoin(blobroot,vv,bname) - local result=methodhandler('concatinators',hash.type,blobroot,vv,bname) - if trace_detail then - report_resolving("match: variant %a, search %a, result %a",variant,search,result) - end - noffiles=noffiles+1 - filelist[noffiles]={ variant,search,result } - end + for i=1,#path do + check(hash,realroot,pathname,path[i],name) end end end elseif trace_locating then - report_resolving("no match in %a (%s)",blobpath,bname) + report_resolving("no match in %a (%s)",hashname,basename) end end end @@ -14262,7 +15387,7 @@ end local function can_be_dir(name) local fakepaths=instance.fakepaths if not fakepaths[name] then - if lfs.isdir(name) then + if isdir(name) then fakepaths[name]=1 else fakepaths[name]=2 @@ -14278,10 +15403,11 @@ local function find_analyze(filename,askedformat,allresults) if askedformat=="" then if ext=="" or not suffixmap[ext] then local defaultsuffixes=resolvers.defaultsuffixes + local formatofsuffix=resolvers.formatofsuffix for i=1,#defaultsuffixes do local forcedname=filename..'.'..defaultsuffixes[i] wantedfiles[#wantedfiles+1]=forcedname - filetype=resolvers.formatofsuffix(forcedname) + filetype=formatofsuffix(forcedname) if trace_locating then report_resolving("forcing filetype %a",filetype) end @@ -14317,18 +15443,18 @@ local function find_direct(filename,allresults) end end local function find_wildcard(filename,allresults) - if find(filename,'%*') then + if find(filename,'*',1,true) then if trace_locating then report_resolving("checking wildcard %a",filename) end - local method,result=resolvers.findwildcardfiles(filename) + local result=resolvers.findwildcardfiles(filename) if result then return "wildcard",result end end end local function find_qualified(filename,allresults,askedformat,alsostripped) - if not file.is_qualified_path(filename) then + if not is_qualified_path(filename) then return end if trace_locating then @@ -14402,33 +15528,66 @@ local function check_subpath(fname) return fname end end -local function find_intree(filename,filetype,wantedfiles,allresults) +local function makepathlist(list,filetype) local typespec=resolvers.variableofformat(filetype) - local pathlist=resolvers.expandedpathlist(typespec) - local method="intree" + local pathlist=resolvers.expandedpathlist(typespec,filetype and usertypes[filetype]) + local entry={} if pathlist and #pathlist>0 then - local filelist=collect_files(wantedfiles) + for k=1,#pathlist do + local path=pathlist[k] + local prescanned=find(path,'^!!') + local resursive=find(path,'//$') + local pathname=lpegmatch(inhibitstripper,path) + local expression=makepathexpression(pathname) + local barename=gsub(pathname,"/+$","") + barename=resolveprefix(barename) + local scheme=url.hasscheme(barename) + local schemename=gsub(barename,"%.%*$",'') + entry[k]={ + path=path, + pathname=pathname, + prescanned=prescanned, + recursive=recursive, + expression=expression, + barename=barename, + scheme=scheme, + schemename=schemename, + } + end + entry.typespec=typespec + list[filetype]=entry + else + list[filetype]=false + end + return entry +end +local function find_intree(filename,filetype,wantedfiles,allresults) + local pathlists=instance.pathlists + if not pathlists then + pathlists=setmetatableindex(allocate(),makepathlist) + instance.pathlists=pathlists + end + local pathlist=pathlists[filetype] + if pathlist then + local method="intree" + local filelist=collect_files(wantedfiles) local dirlist={} + local result={} if filelist then for i=1,#filelist do dirlist[i]=filedirname(filelist[i][3]).."/" end end if trace_detail then - report_resolving("checking filename %a",filename) + report_resolving("checking filename %a in tree",filename) end - local resolve=resolvers.resolve - local result={} for k=1,#pathlist do - local path=pathlist[k] - local pathname=lpegmatch(inhibitstripper,path) - local doscan=path==pathname - if not find (pathname,'//$') then - doscan=false - end + local entry=pathlist[k] + local path=entry.path + local pathname=entry.pathname local done=false if filelist then - local expression=makepathexpression(pathname) + local expression=entry.expression if trace_detail then report_resolving("using pattern %a for path %a",expression,pathname) end @@ -14436,8 +15595,8 @@ local function find_intree(filename,filetype,wantedfiles,allresults) local fl=filelist[k] local f=fl[2] local d=dirlist[k] - if find(d,expression) or find(resolve(d),expression) then - result[#result+1]=resolve(fl[3]) + if find(d,expression) or find(resolveprefix(d),expression) then + result[#result+1]=resolveprefix(fl[3]) done=true if allresults then if trace_detail then @@ -14458,56 +15617,62 @@ local function find_intree(filename,filetype,wantedfiles,allresults) method="database" else method="filesystem" - pathname=gsub(pathname,"/+$","") - pathname=resolve(pathname) - local scheme=url.hasscheme(pathname) + local scheme=entry.scheme if not scheme or scheme=="file" then - local pname=gsub(pathname,"%.%*$",'') - if not find(pname,"%*") then + local pname=entry.schemename + if not find(pname,"*",1,true) then if can_be_dir(pname) then - for k=1,#wantedfiles do - local w=wantedfiles[k] - local fname=check_subpath(filejoin(pname,w)) - if fname then - result[#result+1]=fname - done=true - if not allresults then - break - end + if not done and not entry.prescanned then + if trace_detail then + report_resolving("quick root scan for %a",pname) end - end - if not done and doscan then - local files=resolvers.simplescanfiles(pname,false,true) for k=1,#wantedfiles do local w=wantedfiles[k] - local subpath=files[w] - if not subpath or subpath=="" then - elseif type(subpath)=="string" then - local fname=check_subpath(filejoin(pname,subpath,w)) - if fname then - result[#result+1]=fname - done=true - if not allresults then - break - end + local fname=check_subpath(filejoin(pname,w)) + if fname then + result[#result+1]=fname + done=true + if not allresults then + break end - else - for i=1,#subpath do - local sp=subpath[i] - if sp=="" then - else - local fname=check_subpath(filejoin(pname,sp,w)) - if fname then - result[#result+1]=fname - done=true - if not allresults then - break + end + end + if not done and entry.recursive then + if trace_detail then + report_resolving("scanning filesystem for %a",pname) + end + local files=resolvers.simplescanfiles(pname,false,true) + for k=1,#wantedfiles do + local w=wantedfiles[k] + local subpath=files[w] + if not subpath or subpath=="" then + elseif type(subpath)=="string" then + local fname=check_subpath(filejoin(pname,subpath,w)) + if fname then + result[#result+1]=fname + done=true + if not allresults then + break + end + end + else + for i=1,#subpath do + local sp=subpath[i] + if sp=="" then + else + local fname=check_subpath(filejoin(pname,sp,w)) + if fname then + result[#result+1]=fname + done=true + if not allresults then + break + end end end end - end - if done and not allresults then - break + if done and not allresults then + break + end end end end @@ -14515,6 +15680,18 @@ local function find_intree(filename,filetype,wantedfiles,allresults) end else end + else + for k=1,#wantedfiles do + local pname=entry.barename + local fname=methodhandler('finders',pname.."/"..wantedfiles[k]) + if fname then + result[#result+1]=fname + done=true + if not allresults then + break + end + end + end end end if done and not allresults then @@ -14549,10 +15726,13 @@ local function find_otherwise(filename,filetype,wantedfiles,allresults) local filelist=collect_files(wantedfiles) local fl=filelist and filelist[1] if fl then - return "otherwise",{ resolvers.resolve(fl[3]) } + return "otherwise",{ resolveprefix(fl[3]) } end end collect_instance_files=function(filename,askedformat,allresults) + if not filename or filename=="" then + return {} + end askedformat=askedformat or "" filename=collapsepath(filename,".") filename=gsub(filename,"^%./",getcurrentdir().."/") @@ -14587,7 +15767,11 @@ collect_instance_files=function(filename,askedformat,allresults) else local method,result,stamp,filetype,wantedfiles if instance.remember then - stamp=formatters["%s--%s"](filename,askedformat) + if askedformat=="" then + stamp=formatters["%s::%s"](suffixonly(filename),filename) + else + stamp=formatters["%s::%s"](askedformat,filename) + end result=stamp and instance.found[stamp] if result then if trace_locating then @@ -14606,7 +15790,7 @@ collect_instance_files=function(filename,askedformat,allresults) method,result=find_intree(filename,filetype,wantedfiles) if not result then method,result=find_onpath(filename,filetype,wantedfiles) - if not result then + if resolve_otherwise and not result then method,result=find_otherwise(filename,filetype,wantedfiles) end end @@ -14622,7 +15806,7 @@ collect_instance_files=function(filename,askedformat,allresults) end if stamp then if trace_locating then - report_resolving("remembering file %a",filename) + report_resolving("remembering file %a using hash %a",filename,stamp) end instance.found[stamp]=result end @@ -14630,6 +15814,9 @@ collect_instance_files=function(filename,askedformat,allresults) end end local function findfiles(filename,filetype,allresults) + if not filename or filename=="" then + return {} + end local result,status=collect_instance_files(filename,filetype or "",allresults) if not result or #result==0 then local lowered=lower(filename) @@ -14649,39 +15836,30 @@ 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 base=filebasename(filename) + local result={} local hashes=instance.hashes - local noffound=0 + local function okay(hash,path,name) + local found=methodhandler('concatinators',hash.type,hash.name,path,name) + if found and found~="" then + result[#result+1]=resolveprefix(found) + return not allresults + end + end for k=1,#hashes do local hash=hashes[k] - local files=instance.files[hash.name] or {} - local blist=files[bname] - if not blist then - local rname="remap:"..bname - blist=files[rname] - if blist then - bname=files[rname] - blist=files[bname] - end - end - if blist then - if type(blist)=='string' then - 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 + local content=instance.files[hash.name] + if content then + local path,name=lookup(content,base) + if not path then + elseif type(path)=="string" then + if okay(hash,path,name) then + return result end else - for kk=1,#blist do - local vv=blist[kk] - 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 + for i=1,#path do + if okay(hash,path[i],name) then + return result end end end @@ -14695,64 +15873,80 @@ end function resolvers.findgivenfile(filename) return findgivenfiles(filename,false)[1] or "" end -local function doit(path,blist,bname,tag,variant,result,allresults) - local done=false - if blist and variant then - local resolve=resolvers.resolve - if type(blist)=='string' then - if find(lower(blist),path) then - 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 - local full=methodhandler('concatinators',variant,tag,vv,bname) or "" - result[#result+1]=resolve(full) - done=true - if not allresults then break end - end - end - end - end - return done -end 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) - result=result or {} +local function findwildcardfiles(filename,allresults,result) + local 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 files=instance.files + if find(name,"*",1,true) then local hashes=instance.hashes + local function okay(found,path,base,hashname,hashtype) + if find(found,path) then + local full=methodhandler('concatinators',hashtype,hashname,found,base) + if full and full~="" then + result[#result+1]=resolveprefix(full) + return not allresults + end + end + end for k=1,#hashes do local hash=hashes[k] - 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,hashname,hashtype,result,allresults) then done=true end - if done and not allresults then break end + local hashname=hash.name + local hashtype=hash.type + if hashname and hashtype then + for found,base in filtered(files[hashname],name) do + if type(found)=='string' then + if okay(found,path,base,hashname,hashtype) then + break + end + else + for i=1,#found do + if okay(found[i],path,base,hashname,hashtype) then + break + end + end end end end end else + local function okayokay(found,path,base,hashname,hashtype) + if find(found,path) then + local full=methodhandler('concatinators',hashtype,hashname,found,base) + if full and full~="" then + result[#result+1]=resolveprefix(full) + return not allresults + end + end + end local hashes=instance.hashes for k=1,#hashes do local hash=hashes[k] - local hashname,hashtype=hash.name,hash.type - if doit(path,files[hashname][base],base,hashname,hashtype,result,allresults) then done=true end - if done and not allresults then break end + local hashname=hash.name + local hashtype=hash.type + if hashname and hashtype then + local found,base=lookup(content,base) + if not found then + elseif type(found)=='string' then + if okay(found,path,base,hashname,hashtype) then + break + end + else + for i=1,#found do + if okay(found[i],path,base,hashname,hashtype) then + break + end + end + end + end end end return result @@ -14825,7 +16019,7 @@ end function resolvers.dowithpath(name,func) local pathlist=resolvers.expandedpathlist(name) for i=1,#pathlist do - func("^"..resolvers.cleanpath(pathlist[i])) + func("^"..cleanpath(pathlist[i])) end end function resolvers.dowithvariable(name,func) @@ -14833,23 +16027,23 @@ function resolvers.dowithvariable(name,func) end function resolvers.locateformat(name) local engine=environment.ownmain or "luatex" - local barename=file.removesuffix(name) - local fullname=file.addsuffix(barename,"fmt") + local barename=removesuffix(name) + local fullname=addsuffix(barename,"fmt") local fmtname=caches.getfirstreadablefile(fullname,"formats",engine) or "" if fmtname=="" then fmtname=resolvers.findfile(fullname) - fmtname=resolvers.cleanpath(fmtname) + fmtname=cleanpath(fmtname) end if fmtname~="" then - local barename=file.removesuffix(fmtname) - local luaname=file.addsuffix(barename,luasuffixes.lua) - local lucname=file.addsuffix(barename,luasuffixes.luc) - local luiname=file.addsuffix(barename,luasuffixes.lui) - if lfs.isfile(luiname) then + local barename=removesuffix(fmtname) + local luaname=addsuffix(barename,luasuffixes.lua) + local lucname=addsuffix(barename,luasuffixes.luc) + local luiname=addsuffix(barename,luasuffixes.lui) + if isfile(luiname) then return barename,luiname - elseif lfs.isfile(lucname) then + elseif isfile(lucname) then return barename,lucname - elseif lfs.isfile(luaname) then + elseif isfile(luaname) then return barename,luaname end end @@ -14871,29 +16065,24 @@ function resolvers.dowithfilesintree(pattern,handle,before,after) local hash=hashes[i] local blobtype=hash.type local blobpath=hash.name - if blobpath then + if blobtype and blobpath then + local total=0 + local checked=0 + local done=0 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 table.sortedhash(files) do - total=total+1 - if find(k,"^remap:") then - elseif 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 + for path,name in filtered(instance.files[blobpath],pattern) do + if type(path)=="string" then + checked=checked+1 + if handle(blobtype,blobpath,path,name) then + done=done+1 + end + else + checked=checked+#path + for i=1,#path do + if handle(blobtype,blobpath,path[i],name) then + done=done+1 end end end @@ -14904,8 +16093,8 @@ function resolvers.dowithfilesintree(pattern,handle,before,after) end end end -resolvers.obsolete=resolvers.obsolete or {} -local obsolete=resolvers.obsolete +local obsolete=resolvers.obsolete or {} +resolvers.obsolete=obsolete resolvers.find_file=resolvers.findfile obsolete.find_file=resolvers.findfile resolvers.find_files=resolvers.findfiles obsolete.find_files=resolvers.findfiles @@ -14916,7 +16105,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-pre"] = package.loaded["data-pre"] or true --- original size: 6643, stripped down to: 4401 +-- original size: 3950, stripped down to: 2935 if not modules then modules={} end modules ['data-pre']={ version=1.001, @@ -14926,44 +16115,51 @@ if not modules then modules={} end modules ['data-pre']={ license="see context related readme files" } local resolvers=resolvers -local prefixes=utilities.storage.allocate() -resolvers.prefixes=prefixes -local cleanpath,findgivenfile,expansion=resolvers.cleanpath,resolvers.findgivenfile,resolvers.expansion +local prefixes=resolvers.prefixes +local cleanpath=resolvers.cleanpath +local findgivenfile=resolvers.findgivenfile +local expansion=resolvers.expansion local getenv=resolvers.getenv -local P,S,R,C,Cs,Cc,lpegmatch=lpeg.P,lpeg.S,lpeg.R,lpeg.C,lpeg.Cs,lpeg.Cc,lpeg.match -local joinpath,basename,dirname=file.join,file.basename,file.dirname -local getmetatable,rawset,type=getmetatable,rawset,type +local basename=file.basename +local dirname=file.dirname +local joinpath=file.join +local isfile=lfs.isfile prefixes.environment=function(str) return cleanpath(expansion(str)) end -prefixes.relative=function(str,n) - if io.exists(str) then - elseif io.exists("./"..str) then - str="./"..str - else - local p="../" - for i=1,n or 2 do - if io.exists(p..str) then - str=p..str - break - else - p=p.."../" +local function relative(str,n) + if not isfile(str) then + local pstr="./"..str + if isfile(pstr) then + str=pstr + else + local p="../" + for i=1,n or 2 do + local pstr=p..str + if isfile(pstr) then + str=pstr + break + else + p=p.."../" + end end end end return cleanpath(str) end +local function locate(str) + local fullname=findgivenfile(str) or "" + return cleanpath(fullname~="" and fullname or str) +end +prefixes.relative=relative +prefixes.locate=locate prefixes.auto=function(str) - local fullname=prefixes.relative(str) - if not lfs.isfile(fullname) then - fullname=prefixes.locate(str) + local fullname=relative(str) + if not isfile(fullname) then + fullname=locate(str) end return fullname end -prefixes.locate=function(str) - local fullname=findgivenfile(str) or "" - return cleanpath((fullname~="" and fullname) or str) -end prefixes.filename=function(str) local fullname=findgivenfile(str) or "" return cleanpath(basename((fullname~="" and fullname) or str)) @@ -14984,6 +16180,13 @@ end prefixes.home=function(str) return cleanpath(joinpath(getenv('HOME'),str)) end +prefixes.env=prefixes.environment +prefixes.rel=prefixes.relative +prefixes.loc=prefixes.locate +prefixes.kpse=prefixes.locate +prefixes.full=prefixes.locate +prefixes.file=prefixes.filename +prefixes.path=prefixes.pathname local function toppath() local inputstack=resolvers.inputstack if not inputstack then @@ -14996,98 +16199,22 @@ local function toppath() return pathname end end -resolvers.toppath=toppath -prefixes.toppath=function(str) - return cleanpath(joinpath(toppath(),str)) -end -prefixes.env=prefixes.environment -prefixes.rel=prefixes.relative -prefixes.loc=prefixes.locate -prefixes.kpse=prefixes.locate -prefixes.full=prefixes.locate -prefixes.file=prefixes.filename -prefixes.path=prefixes.pathname -function resolvers.allprefixes(separator) - local all=table.sortedkeys(prefixes) - if separator then - for i=1,#all do - all[i]=all[i]..":" - end - end - return all -end -local function _resolve_(method,target) - local action=prefixes[method] - if action then - return action(target) - else - return method..":"..target - end -end -local resolved,abstract={},{} -function resolvers.resetresolve(str) - resolved,abstract={},{} -end -local pattern=Cs((C(R("az")^2)*P(":")*C((1-S(" \"\';,"))^1)/_resolve_+P(1))^0) -local prefix=C(R("az")^2)*P(":") -local target=C((1-S(" \"\';,"))^1) -local notarget=(#S(";,")+P(-1))*Cc("") -local pattern=Cs(((prefix*(target+notarget))/_resolve_+P(1))^0) -local function resolve(str) - if type(str)=="table" then - local t={} - for i=1,#str do - t[i]=resolve(str[i]) - end - return t +local function jobpath() + local path=resolvers.stackpath() + if not path or path=="" then + return "." else - local res=resolved[str] - if not res then - res=lpegmatch(pattern,str) - resolved[str]=res - abstract[res]=str - end - return res - end -end -local function unresolve(str) - return abstract[str] or str -end -resolvers.resolve=resolve -resolvers.unresolve=unresolve -if type(os.uname)=="function" then - for k,v in next,os.uname() do - if not prefixes[k] then - prefixes[k]=function() return v end - end - end -end -if os.type=="unix" then - local pattern - local function makepattern(t,k,v) - if t then - rawset(t,k,v) - end - local colon=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) - end - makepattern() - getmetatable(prefixes).__newindex=makepattern - function resolvers.repath(str) - return lpegmatch(pattern,str) - end -else - function resolvers.repath(str) - return str + return path end end +resolvers.toppath=toppath +resolvers.jobpath=jobpath +prefixes.toppath=function(str) return cleanpath(joinpath(toppath(),str)) end +prefixes.jobpath=function(str) return cleanpath(joinpath(jobpath(),str)) end +resolvers.setdynamic("toppath") +resolvers.setdynamic("jobpath") +prefixes.jobfile=prefixes.jobpath +resolvers.setdynamic("jobfile") end -- of closure @@ -15149,7 +16276,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-fil"] = package.loaded["data-fil"] or true --- original size: 3801, stripped down to: 3231 +-- original size: 3863, stripped down to: 3310 if not modules then modules={} end modules ['data-fil']={ version=1.001, @@ -15161,30 +16288,31 @@ if not modules then modules={} end modules ['data-fil']={ 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 resolveprefix=resolvers.resolve 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) + local filename=specification.filename + local realname=resolveprefix(filename) if realname and realname~='' and lfs.isdir(realname) then if trace_locating then - report_files("file locator %a found as %a",name,realname) + report_files("file locator %a found as %a",filename,realname) end - resolvers.appendhash('file',name,true) + resolvers.appendhash('file',filename,true) elseif trace_locating then - report_files("file locator %a not found",name) + report_files("file locator %a not found",filename) end end function hashers.file(specification) - local name=specification.filename - local content=caches.loadcontent(name,'files') - resolvers.registerfilehash(name,content,content==nil) + local pathname=specification.filename + local content=caches.loadcontent(pathname,'files') + resolvers.registerfilehash(pathname,content,content==nil) end function generators.file(specification) - local path=specification.filename - local content=resolvers.scanfiles(path,false,true) - resolvers.registerfilehash(path,content,true) + local pathname=specification.filename + local content=resolvers.scanfiles(pathname,false,true) + resolvers.registerfilehash(pathname,content,true) end concatinators.file=file.join function finders.file(specification,filetype) @@ -15375,7 +16503,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-use"] = package.loaded["data-use"] or true --- original size: 3913, stripped down to: 2998 +-- original size: 3899, stripped down to: 2984 if not modules then modules={} end modules ['data-use']={ version=1.001, @@ -15421,7 +16549,7 @@ end statistics.register("used config file",function() return caches.configfiles() end) statistics.register("used cache path",function() return caches.usedpaths() end) function statistics.savefmtstatus(texname,formatbanner,sourcefile) - local enginebanner=status.list().banner + local enginebanner=status.banner if formatbanner and enginebanner and sourcefile then local luvname=file.replacesuffix(texname,"luv") local luvdata={ @@ -15434,7 +16562,7 @@ function statistics.savefmtstatus(texname,formatbanner,sourcefile) end end function statistics.checkfmtstatus(texname) - local enginebanner=status.list().banner + local enginebanner=status.banner if enginebanner and texname then local luvname=file.replacesuffix(texname,"luv") if lfs.isfile(luvname) then @@ -15466,7 +16594,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-zip"] = package.loaded["data-zip"] or true --- original size: 8489, stripped down to: 6757 +-- original size: 8772, stripped down to: 6841 if not modules then modules={} end modules ['data-zip']={ version=1.001, @@ -15485,16 +16613,6 @@ zip.archives=zip.archives or {} local archives=zip.archives zip.registeredfiles=zip.registeredfiles or {} local registeredfiles=zip.registeredfiles -local limited=false -directives.register("system.inputmode",function(v) - if not limited then - local i_limiter=io.i_limiter(v) - if i_limiter then - zip.open=i_limiter.protect(zip.open) - limited=true - end - end -end) local function validzip(str) if not find(str,"^zip://") then return "zip:///"..str @@ -15509,7 +16627,7 @@ function zip.openarchive(name) local arch=archives[name] if not arch then local full=resolvers.findfile(name) or "" - arch=(full~="" and zip.open(full)) or false + arch=full~="" and zip.open(full) or false archives[name]=arch end return arch @@ -15668,31 +16786,42 @@ function resolvers.usezipfile(archive) end end function resolvers.registerzipfile(z,tree) - local files,filter={},"" - if tree=="" then - filter="^(.+)/(.-)$" - else - filter=format("^%s/(.+)/(.-)$",tree) - end + local names={} + local files={} + local remap={} + local n=0 + local filter=tree=="" and "^(.+)/(.-)$" or format("^%s/(.+)/(.-)$",tree) + local register=resolvers.registerfile if trace_locating then report_zip("registering: using filter %a",filter) end - local register,n=resolvers.registerfile,0 for i in z:files() do - local path,name=match(i.filename,filter) - if path then - if name and name~='' then - register(files,name,path) - n=n+1 - else + local filename=i.filename + local path,name=match(filename,filter) + if not path then + n=n+1 + register(names,filename,"") + local usedname=lower(filename) + files[usedname]="" + if usedname~=filename then + remap[usedname]=filename end - else - register(files,i.filename,'') + elseif name and name~="" then n=n+1 + register(names,name,path) + local usedname=lower(name) + files[usedname]=path + if usedname~=name then + remap[usedname]=name + end + else end end report_zip("registering: %s files registered",n) - return files + return { + files=files, + remap=remap, + } end @@ -15702,7 +16831,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-tre"] = package.loaded["data-tre"] or true --- original size: 2508, stripped down to: 2074 +-- original size: 8479, stripped down to: 5580 if not modules then modules={} end modules ['data-tre']={ version=1.001, @@ -15711,42 +16840,64 @@ if not modules then modules={} end modules ['data-tre']={ copyright="PRAGMA ADE / ConTeXt Development Team", license="see context related readme files" } -local find,gsub,format=string.find,string.gsub,string.format +local find,gsub,lower=string.find,string.gsub,string.lower +local basename,dirname,joinname=file.basename,file.dirname,file .join +local globdir,isdir,isfile=dir.glob,lfs.isdir,lfs.isfile +local P,lpegmatch=lpeg.P,lpeg.match 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 resolveprefix=resolvers.resolve +local notfound=resolvers.finders.notfound +local lookup=resolvers.get_from_content +local collectors={} +local found={} +function resolvers.finders.tree(specification) local spec=specification.filename - local fnd=found[spec] - if fnd==nil then + local okay=found[spec] + if okay==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.."/*" - hash=dir.glob(pattern) - done[path]=hash + local path=dirname(spec) + local name=basename(spec) + if path=="" then + path="." + end + local names=collectors[path] + if not names then + local pattern=find(path,"/%*+$") and path or (path.."/*") + names=globdir(pattern) + collectors[path]=names 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 + for i=1,#names do + local fullname=names[i] + if find(fullname,pattern) then + found[spec]=fullname + return fullname + end + end + local pattern=lower(pattern) + for i=1,#names do + local fullname=lower(names[i]) + if find(fullname,pattern) then + if isfile(fullname) then + found[spec]=fullname + return fullname + else + break + end end end end - fnd=notfound() - found[spec]=fnd + okay=notfound() + found[spec]=okay end - return fnd + return okay end function resolvers.locators.tree(specification) local name=specification.filename - local realname=resolvers.resolve(name) - if realname and realname~='' and lfs.isdir(realname) then + local realname=resolveprefix(name) + if realname and realname~='' and isdir(realname) then if trace_locating then report_trees("locator %a found",realname) end @@ -15757,16 +16908,110 @@ function resolvers.locators.tree(specification) end function resolvers.hashers.tree(specification) local name=specification.filename - if trace_locating then - report_trees("analysing %a",name) - end + report_trees("analyzing %a",name) resolvers.methodhandler("hashers",name) resolvers.generators.file(specification) 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 +local collectors={} +local splitter=lpeg.splitat("/**/") +local stripper=lpeg.replacer { [P("/")*P("*")^1*P(-1)]="" } +table.setmetatableindex(collectors,function(t,k) + local rootname=lpegmatch(stripper,k) + local dataname=joinname(rootname,"dirlist") + local content=caches.loadcontent(dataname,"files",dataname) + if not content then + content=resolvers.scanfiles(rootname,nil,nil,false,true) + caches.savecontent(dataname,"files",content,dataname) + end + t[k]=content + return content +end) +local function checked(root,p,n) + if p then + if type(p)=="table" then + for i=1,#p do + local fullname=joinname(root,p[i],n) + if isfile(fullname) then + return fullname + end + end + else + local fullname=joinname(root,p,n) + if isfile(fullname) then + return fullname + end + end + end + return notfound() +end +local function resolve(specification) + local filename=specification.filename + if filename~="" then + local root,rest=lpegmatch(splitter,filename) + if root and rest then + local path,name=dirname(rest),basename(rest) + if name~=rest then + local content=collectors[root] + local p,n=lookup(content,name) + if not p then + return notfound() + end + local pattern=".*/"..path.."$" + local istable=type(p)=="table" + if istable then + for i=1,#p do + local pi=p[i] + if pi==path or find(pi,pattern) then + local fullname=joinname(root,pi,n) + if isfile(fullname) then + return fullname + end + end + end + elseif p==path or find(p,pattern) then + local fullname=joinname(root,p,n) + if isfile(fullname) then + return fullname + end + end + local queries=specification.queries + if queries and queries.option=="fileonly" then + return checked(root,p,n) + else + return notfound() + end + end + end + local path,name=dirname(filename),basename(filename) + local root=lpegmatch(stripper,path) + local content=collectors[path] + local p,n=lookup(content,name) + if p then + return checked(root,p,n) + end + end + return notfound() +end +resolvers.finders .dirlist=resolve +resolvers.locators .dirlist=resolvers.locators .tree +resolvers.hashers .dirlist=resolvers.hashers .tree +resolvers.generators.dirlist=resolvers.generators.file +resolvers.openers .dirlist=resolvers.openers .file +resolvers.loaders .dirlist=resolvers.loaders .file +function resolvers.finders.dirfile(specification) + local queries=specification.queries + if queries then + queries.option="fileonly" + else + specification.queries={ option="fileonly" } + end + return resolve(specification) +end +resolvers.locators .dirfile=resolvers.locators .dirlist +resolvers.hashers .dirfile=resolvers.hashers .dirlist +resolvers.generators.dirfile=resolvers.generators.dirlist +resolvers.openers .dirfile=resolvers.openers .dirlist +resolvers.loaders .dirfile=resolvers.loaders .dirlist end -- of closure @@ -15775,7 +17020,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-sch"] = package.loaded["data-sch"] or true --- original size: 6202, stripped down to: 5149 +-- original size: 6569, stripped down to: 5304 if not modules then modules={} end modules ['data-sch']={ version=1.001, @@ -15801,8 +17046,13 @@ directives.register("schemes.threshold",function(v) threshold=tonumber(v) or thr function cleaners.none(specification) return specification.original end -function cleaners.strip(specification) - return (gsub(specification.original,"[^%a%d%.]+","-")) +function cleaners.strip(specification) + local path,name=file.splitbase(specification.original) + if path=="" then + return (gsub(name,"[^%a%d%.]+","-")) + else + return (gsub((gsub(path,"%.","-").."-"..name),"[^%a%d%.]+","-")) + end end function cleaners.md5(specification) return file.addsuffix(md5.hex(specification.original),file.suffix(specification.path)) @@ -15818,8 +17068,8 @@ function resolvers.schemes.cleanname(specification) end local cached,loaded,reused,thresholds,handlers={},{},{},{},{} local function runcurl(name,cachename) - local command="curl --silent --create-dirs --output "..cachename.." "..name - os.spawn(command) + local command="curl --silent --insecure --create-dirs --output "..cachename.." "..name + os.execute(command) end local function fetch(specification) local original=specification.original @@ -15951,7 +17201,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-lua"] = package.loaded["data-lua"] or true --- original size: 4237, stripped down to: 3177 +-- original size: 4313, stripped down to: 3227 if not modules then modules={} end modules ['data-lua']={ version=1.001, @@ -15960,7 +17210,7 @@ if not modules then modules={} end modules ['data-lua']={ copyright="PRAGMA ADE / ConTeXt Development Team", license="see context related readme files" } -local resolvers,package=resolvers,package +local package,lpeg=package,lpeg local gsub=string.gsub local concat=table.concat local addsuffix=file.addsuffix @@ -15971,9 +17221,11 @@ local luaformats={ 'TEXINPUTS','LUAINPUTS' } local libformats={ 'CLUAINPUTS' } local helpers=package.helpers or {} local methods=helpers.methods or {} +local resolvers=resolvers +local resolveprefix=resolvers.resolve +helpers.report=logs.reporter("resolvers","libraries") trackers.register("resolvers.libraries",function(v) helpers.trace=v end) trackers.register("resolvers.locating",function(v) helpers.trace=v end) -helpers.report=logs.reporter("resolvers","libraries") helpers.sequence={ "already loaded", "preload table", @@ -15988,7 +17240,7 @@ helpers.sequence={ } local pattern=Cs(P("!")^0/""*(P("/")*P(-1)/"/"+P("/")^1/"/"+1)^0) function helpers.cleanpath(path) - return resolvers.resolve(lpegmatch(pattern,path)) + return resolveprefix(lpegmatch(pattern,path)) end local loadedaslib=helpers.loadedaslib local getextraluapaths=package.extraluapaths @@ -16058,7 +17310,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-aux"] = package.loaded["data-aux"] or true --- original size: 2394, stripped down to: 2005 +-- original size: 2431, stripped down to: 1996 if not modules then modules={} end modules ['data-aux']={ version=1.001, @@ -16072,8 +17324,8 @@ local type,next=type,next local trace_locating=false trackers.register("resolvers.locating",function(v) trace_locating=v end) local resolvers=resolvers local report_scripts=logs.reporter("resolvers","scripts") -function resolvers.updatescript(oldname,newname) - local scriptpath="scripts/context/lua" +function resolvers.updatescript(oldname,newname) + local scriptpath="context/lua" newname=file.addsuffix(newname,"lua") local oldscript=resolvers.cleanpath(oldname) if trace_locating then @@ -16125,7 +17377,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-tmf"] = package.loaded["data-tmf"] or true --- original size: 2600, stripped down to: 1627 +-- original size: 2601, stripped down to: 1627 if not modules then modules={} end modules ['data-tmf']={ version=1.001, @@ -16181,7 +17433,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-lst"] = package.loaded["data-lst"] or true --- original size: 2654, stripped down to: 2301 +-- original size: 2734, stripped down to: 2354 if not modules then modules={} end modules ['data-lst']={ version=1.001, @@ -16190,10 +17442,13 @@ if not modules then modules={} end modules ['data-lst']={ copyright="PRAGMA ADE / ConTeXt Development Team", license="see context related readme files" } -local find,concat,upper,format=string.find,table.concat,string.upper,string.format +local rawget,type,next=rawget,type,next +local find,concat,upper=string.find,table.concat,string.upper local fastcopy,sortedpairs=table.fastcopy,table.sortedpairs -resolvers.listers=resolvers.listers or {} local resolvers=resolvers +local listers=resolvers.listers or {} +resolvers.listers=listers +local resolveprefix=resolvers.resolve local report_lists=logs.reporter("resolvers","lists") local function tabstr(str) if type(str)=='table' then @@ -16202,7 +17457,7 @@ local function tabstr(str) return str end end -function resolvers.listers.variables(pattern) +function listers.variables(pattern) local instance=resolvers.instance local environment=instance.environment local variables=instance.variables @@ -16223,10 +17478,10 @@ function resolvers.listers.variables(pattern) for key,value in 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",tabstr(resolvers.resolve(expansions[key])) or "unset") + 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",tabstr(resolveprefix(expansions[key])) or "unset") end end instance.environment=fastcopy(env) @@ -16234,15 +17489,15 @@ function resolvers.listers.variables(pattern) instance.expansions=fastcopy(exp) end local report_resolved=logs.reporter("system","resolved") -function resolvers.listers.configurations() +function listers.configurations() local configurations=resolvers.instance.specification for i=1,#configurations do - report_resolved("file : %s",resolvers.resolve(configurations[i])) + report_resolved("file : %s",resolveprefix(configurations[i])) end report_resolved("") local list=resolvers.expandedpathfromlist(resolvers.splitpath(resolvers.luacnfspec)) for i=1,#list do - local li=resolvers.resolve(list[i]) + local li=resolveprefix(list[i]) if lfs.isdir(li) then report_resolved("path - %s",li) else @@ -16547,7 +17802,7 @@ do -- create closure to overcome 200 locals limit package.loaded["luat-fmt"] = package.loaded["luat-fmt"] or true --- original size: 5951, stripped down to: 4922 +-- original size: 5955, stripped down to: 4926 if not modules then modules={} end modules ['luat-fmt']={ version=1.001, @@ -16635,7 +17890,7 @@ function environment.make_format(name) end local command=format("%s --ini %s --lua=%s %s %sdump",engine,primaryflags(),quoted(usedluastub),quoted(fulltexsourcename),os.platform=="unix" and "\\\\" or "\\") report_format("running command: %s\n",command) - os.spawn(command) + os.execute(command) local pattern=file.removesuffix(file.basename(usedluastub)).."-*.mem" local mp=dir.glob(pattern) if mp then @@ -16670,7 +17925,7 @@ function environment.run_format(name,data,more) else local command=format("%s %s --fmt=%s --lua=%s %s %s",engine,primaryflags(),quoted(barename),quoted(luaname),quoted(data),more~="" and quoted(more) or "") report_format("running command: %s",command) - os.spawn(command) + os.execute(command) end end end @@ -16681,8 +17936,8 @@ end -- of closure -- used libraries : l-lua.lua l-package.lua l-lpeg.lua l-function.lua l-string.lua l-table.lua l-io.lua l-number.lua l-set.lua l-os.lua l-file.lua l-gzip.lua l-md5.lua l-url.lua l-dir.lua l-boolean.lua l-unicode.lua l-math.lua util-str.lua util-tab.lua util-sto.lua util-prs.lua util-fmt.lua trac-set.lua trac-log.lua trac-inf.lua trac-pro.lua util-lua.lua util-deb.lua util-mrg.lua util-tpl.lua util-env.lua luat-env.lua lxml-tab.lua lxml-lpt.lua lxml-mis.lua lxml-aux.lua lxml-xml.lua trac-xml.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-zip.lua data-tre.lua data-sch.lua data-lua.lua data-aux.lua data-tmf.lua data-lst.lua util-lib.lua luat-sta.lua luat-fmt.lua -- skipped libraries : - --- original bytes : 685064 --- stripped bytes : 242353 +-- original bytes : 745618 +-- stripped bytes : 269191 -- end library merge @@ -16781,17 +18036,18 @@ local ownlibs = { -- order can be made better } +-- c:/data/develop/tex-context/tex/texmf-win64/bin/../../texmf-context/tex/context/base/data-tmf.lua +-- c:/data/develop/context/sources/data-tmf.lua + local ownlist = { - '.', - ownpath , - ownpath .. "/../sources", -- HH's development path + -- '.', + -- ownpath , + owntree .. "/../../../../context/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", } @@ -16907,6 +18163,7 @@ local helpinfo = [[ <category name="basic"> <subcategory> <flag name="script"><short>run an mtx script (lua prefered method) (<ref name="noquotes"/>), no script gives list</short></flag> + <flag name="evaluate"><short>run code passed on the commandline (between quotes)</short></flag> <flag name="execute"><short>run a script or program (texmfstart method) (<ref name="noquotes"/>)</short></flag> <flag name="resolve"><short>resolve prefixed arguments</short></flag> <flag name="ctxlua"><short>run internally (using preloaded libs)</short></flag> @@ -16932,6 +18189,7 @@ local helpinfo = [[ <flag name="verbose"><short>give a bit more info</short></flag> <flag name="trackers" value="list"><short>enable given trackers</short></flag> <flag name="progname" value="str"><short>format or backend</short></flag> + <flag name="systeminfo" value="str"><short>show current operating system, processor, etc</short></flag> </subcategory> <subcategory> <flag name="edit"><short>launch editor with found file</short></flag> @@ -17561,6 +18819,39 @@ function runners.associate(filename) os.launch(filename) end +function runners.evaluate(code,filename) -- for Luigi + if code == "loop" then + while true do + io.write("> ") + local code = io.read() + if code ~= "" then + local temp = string.match(code,"^= (.*)$") + if temp then + code = "print("..temp..")" + end + local compiled, message = loadstring(code) + if type(compiled) ~= "function" then + io.write("! " .. (message or code).."\n") + else + io.write(compiled()) + end + end + end + else + if type(code) ~= "string" or code == "" then + code = filename + end + if code ~= "" then + local compiled, message = loadstring(code) + if type(compiled) ~= "function" then + io.write("invalid lua code: " .. (message or code)) + return + end + io.write(compiled()) + end + end +end + function runners.gethelp(filename) local url = environment.argument("url") if url and url ~= "" then @@ -17572,6 +18863,15 @@ function runners.gethelp(filename) end end +function runners.systeminfo() + report("architecture : %s",os.platform or "<unset>") + report("operating system : %s",os.name or "<unset>") + report("file architecture : %s",os.type or "<unset>") + report("binary path : %s",os.selfdir or "<unset>") + report("binary suffix : %s",os.binsuffix or "<unset>") + report("library suffix : %s",os.libsuffix or "<unset>") +end + -- this is a bit dirty ... first we store the first filename and next we -- split the arguments so that we only see the ones meant for this script -- ... later we will use the second half @@ -17687,16 +18987,13 @@ end if e_argument("ansi") then - local formatters = string.formatters + logs.setformatters("ansi") - logs.setformatters { - report_yes = formatters["[1;32m%-15s [0;1m|[0m %s"], - report_nop = formatters["[1;32m%-15s [0;1m|[0m"], - subreport_yes = formatters["[1;32m%-15s [0;1m|[1;31m %s [0;1m|[0m %s"], - subreport_nop = formatters["[1;32m%-15s [0;1m|[1;31m %s [0;1m|[0m"], - status_yes = formatters["[1;32m%-15s [0;1m:[0m %s\n"], - status_nop = formatters["[1;32m%-15s [0;1m:[0m\n"], - } + local script = e_argument("script") or e_argument("scripts") + + if type(script) == "string" then + logs.writer("]0;"..script.."") -- for Alan to test + end end @@ -17715,14 +19012,26 @@ if e_argument("script") or e_argument("scripts") then ok = runners.execute_ctx_script(filename) end +elseif e_argument("evaluate") then + + runners.evaluate(e_argument("evaluate"),filename) + elseif e_argument("selfmerge") then -- embed used libraries runners.loadbase() local found = locate_libs() + if found then - utilities.merger.selfmerge(own.name,own.libs,{ found }) + local mtxrun = resolvers.findfile("mtxrun.lua") -- includes local name + if lfs.isfile(mtxrun) then + utilities.merger.selfmerge(mtxrun,own.libs,{ found }) + application.report("runner updated on resolved path: %s",mtxrun) + else + utilities.merger.selfmerge(own.name,own.libs,{ found }) + application.report("runner updated on relative path: %s",own.name) + end end elseif e_argument("selfclean") then @@ -17730,7 +19039,15 @@ elseif e_argument("selfclean") then -- remove embedded libraries runners.loadbase() - utilities.merger.selfclean(own.name) + + local mtxrun = resolvers.findfile("mtxrun.lua") -- includes local name + if lfs.isfile(mtxrun) then + utilities.merger.selfclean(mtxrun) + application.report("runner cleaned on resolved path: %s",mtxrun) + else + utilities.merger.selfclean(own.name) + application.report("runner cleaned on relative path: %s",own.name) + end elseif e_argument("selfupdate") then @@ -17972,6 +19289,8 @@ elseif e_argument("version") then application.version() + application.report("source path",environment.ownbin) + elseif e_argument("directives") then directives.show() @@ -17989,6 +19308,10 @@ elseif e_argument("exporthelp") then runners.loadbase() application.export(e_argument("exporthelp"),filename) +elseif e_argument("systeminfo") then + + runners.systeminfo() + elseif e_argument("help") or filename=='help' or filename == "" then application.help() diff --git a/scripts/context/ruby/texexec.rb b/scripts/context/ruby/texexec.rb index c673cb46b..7f8298c09 100644 --- a/scripts/context/ruby/texexec.rb +++ b/scripts/context/ruby/texexec.rb @@ -685,22 +685,22 @@ end # so far for compatibility, will move to tex -@@extrastringvars = [ +extrastringvars = [ 'pages', 'background', 'backspace', 'topspace', 'boxtype', 'tempdir','bannerheight', 'printformat', 'method', 'scale', 'selection', 'combination', 'textwidth', 'addempty', 'logfile', 'startline', 'endline', 'startcolumn', 'endcolumn', 'scale' ] -@@extrabooleanvars = [ +extrabooleanvars = [ 'centerpage', 'noduplex', 'color', 'pretty', 'fullscreen', 'screensaver', 'markings' ] if job = TEX.new(logger) then - job.setextrastringvars(@@extrastringvars) - job.setextrabooleanvars(@@extrabooleanvars) + job.setextrastringvars(extrastringvars) + job.setextrabooleanvars(extrabooleanvars) job.booleanvars.each do |k| commandline.registerflag(k) diff --git a/scripts/context/stubs/mswin/first-setup.bat b/scripts/context/stubs/install/first-setup.bat index f06ad0e6b..f06ad0e6b 100644 --- a/scripts/context/stubs/mswin/first-setup.bat +++ b/scripts/context/stubs/install/first-setup.bat diff --git a/scripts/context/stubs/install/first-setup.sh b/scripts/context/stubs/install/first-setup.sh new file mode 100644 index 000000000..9249fd2e0 --- /dev/null +++ b/scripts/context/stubs/install/first-setup.sh @@ -0,0 +1,120 @@ +#!/bin/sh + +# Takes the same arguments as mtx-update + +# you may change this if you want ... +CONTEXTROOT="$PWD/tex" + +# suggested by Tobias Florek to check for ruby & rsync +if [ ! -x "`which rsync`" ]; then + echo "You need to install rsync first." + exit 1 +fi +if [ ! -x "`which ruby`" ]; then + echo "You might want to install Ruby first if you want to use pdfTeX or XeTeX." +fi + +system=`uname -s` +cpu=`uname -m` + +case "$system" in + # linux + Linux) + case "$cpu" in + i*86) platform="linux" ;; + x86_64|ia64) platform="linux-64" ;; + # a little bit of cheating with ppc64 (won't work on Gentoo) + ppc|ppc64) platform="linux-ppc" ;; + # we currently support just mipsel, but Debian is lying (reports mips64) + # we need more hacks to fix the situation, this is just a temporary solution + mips|mips64|mipsel|mips64el) platform="linux-mipsel" ;; + *) platform="unknown" ;; + esac ;; + # Mac OS X + Darwin) + case "$cpu" in + i*86) platform="osx-intel" ;; + x86_64) platform="osx-64" ;; + ppc*|powerpc|power*|Power*) platform="osx-ppc" ;; + *) platform="unknown" ;; + esac ;; + # FreeBSD + FreeBSD|freebsd) + case "$cpu" in + i*86) platform="freebsd" ;; + x86_64) platform="freebsd" ;; # no special binaries are available yet + amd64) platform="freebsd-amd64" ;; + *) platform="unknown" ;; + esac ;; + # kFreeBSD (debian) + GNU/kFreeBSD) + case "$cpu" in + i*86) platform="kfreebsd-i386" ;; + x86_64|amd64) platform="kfreebsd-amd64" ;; + *) platform="unknown" ;; + esac ;; + # cygwin + CYGWIN*) + case "$cpu" in + i*86) platform="cygwin" ;; + x86_64|ia64) platform="cygwin-64" ;; + *) platform="unknown" ;; + esac ;; + # SunOS/Solaris + SunOS) + case "$cpu" in + sparc) platform="solaris-sparc" ;; + i86pc) platform="solaris-intel" ;; + *) platform="unknown" ;; + esac ;; + *) platform="unknown" +esac + +# temporary patch for 64-bit Leopard with 32-bit kernel +if test "$platform" = "osx-intel"; then + # if running Snow Leopard or later + # better: /usr/bin/sw_vers -productVersion + if test `uname -r|cut -f1 -d"."` -ge 10 ; then + # if working on 64-bit hardware + if test `sysctl -n hw.cpu64bit_capable` = 1; then + # snowleopard32=TRUE + platform="osx-64" + fi + fi +fi + +if test "$platform" = "unknown" ; then + echo "Error: your system \"$system $cpu\" is not supported yet." + echo "Please report to the ConTeXt mailing-list (ntg-context@ntg.nl)" + exit +fi + +# if you want to enforce some specific platform +# (when 'uname' doesn't agree with true architecture), uncomment and modify next line: +# platform=linux + +# download or rsync the latest scripts first +rsync -rlptv rsync://contextgarden.net/minimals/setup/$platform/bin . + +# download or update the distribution +# you may remove the --context=beta switch if you want to use "current" +# you can use --engine=luatex if you want just mkiv +env PATH="$PWD/bin:$CONTEXTROOT/texmf-$platform/bin:$PATH" \ +mtxrun --script ./bin/mtx-update.lua --force --update --make --context=beta --platform=$platform --texroot="$CONTEXTROOT" $@ + +echo +echo "When you want to use context, you need to initialize the tree by typing:" +echo +echo " . $CONTEXTROOT/setuptex" +echo +echo "in your shell or add" +echo " \"$CONTEXTROOT/texmf-$platform/bin\"" +echo "to PATH variable if you want to set it permanently." +echo "This can usually be done in .bashrc, .bash_profile" +echo "(or whatever file is used to initialize your shell)." +echo + +if [ ! -x "`which ruby`" ]; then + echo "You might want to install Ruby first if you want to use pdfTeX or XeTeX." + echo +fi diff --git a/scripts/context/stubs/mswin/context.exe b/scripts/context/stubs/mswin/context.exe Binary files differindex faae5caa7..0e7882cf9 100644 --- a/scripts/context/stubs/mswin/context.exe +++ b/scripts/context/stubs/mswin/context.exe diff --git a/scripts/context/stubs/mswin/ctxtools.exe b/scripts/context/stubs/mswin/ctxtools.exe Binary files differindex faae5caa7..0e7882cf9 100644 --- a/scripts/context/stubs/mswin/ctxtools.exe +++ b/scripts/context/stubs/mswin/ctxtools.exe diff --git a/scripts/context/stubs/mswin/luatools.exe b/scripts/context/stubs/mswin/luatools.exe Binary files differindex faae5caa7..0e7882cf9 100644 --- a/scripts/context/stubs/mswin/luatools.exe +++ b/scripts/context/stubs/mswin/luatools.exe diff --git a/scripts/context/stubs/mswin/metatex.exe b/scripts/context/stubs/mswin/metatex.exe Binary files differindex faae5caa7..0e7882cf9 100644 --- a/scripts/context/stubs/mswin/metatex.exe +++ b/scripts/context/stubs/mswin/metatex.exe diff --git a/scripts/context/stubs/mswin/mptopdf.exe b/scripts/context/stubs/mswin/mptopdf.exe Binary files differindex faae5caa7..0e7882cf9 100644 --- a/scripts/context/stubs/mswin/mptopdf.exe +++ b/scripts/context/stubs/mswin/mptopdf.exe diff --git a/scripts/context/stubs/mswin/mtxrun.dll b/scripts/context/stubs/mswin/mtxrun.dll Binary files differindex 5a79e1bad..3c4481c31 100644 --- a/scripts/context/stubs/mswin/mtxrun.dll +++ b/scripts/context/stubs/mswin/mtxrun.dll diff --git a/scripts/context/stubs/mswin/mtxrun.exe b/scripts/context/stubs/mswin/mtxrun.exe Binary files differindex faae5caa7..0e7882cf9 100644 --- a/scripts/context/stubs/mswin/mtxrun.exe +++ b/scripts/context/stubs/mswin/mtxrun.exe diff --git a/scripts/context/stubs/mswin/mtxrun.lua b/scripts/context/stubs/mswin/mtxrun.lua index 0ff2d2897..edfeba8dd 100644 --- a/scripts/context/stubs/mswin/mtxrun.lua +++ b/scripts/context/stubs/mswin/mtxrun.lua @@ -56,7 +56,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-lua"] = package.loaded["l-lua"] or true --- original size: 3123, stripped down to: 1694 +-- original size: 3888, stripped down to: 2197 if not modules then modules={} end modules ['l-lua']={ version=1.001, @@ -136,6 +136,16 @@ function optionalrequire(...) return result end end +if lua then + lua.mask=load([[τεχ = 1]]) and "utf" or "ascii" +end +local flush=io.flush +if flush then + local execute=os.execute if execute then function os.execute(...) flush() return execute(...) end end + local exec=os.exec if exec then function os.exec (...) flush() return exec (...) end end + local spawn=os.spawn if spawn then function os.spawn (...) flush() return spawn (...) end end + local popen=io.popen if popen then function io.popen (...) flush() return popen (...) end end +end end -- of closure @@ -434,7 +444,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-lpeg"] = package.loaded["l-lpeg"] or true --- original size: 29245, stripped down to: 15964 +-- original size: 36977, stripped down to: 20349 if not modules then modules={} end modules ['l-lpeg']={ version=1.001, @@ -450,7 +460,9 @@ local byte,char,gmatch,format=string.byte,string.char,string.gmatch,string.forma local floor=math.floor local P,R,S,V,Ct,C,Cs,Cc,Cp,Cmt=lpeg.P,lpeg.R,lpeg.S,lpeg.V,lpeg.Ct,lpeg.C,lpeg.Cs,lpeg.Cc,lpeg.Cp,lpeg.Cmt local lpegtype,lpegmatch,lpegprint=lpeg.type,lpeg.match,lpeg.print -setinspector(function(v) if lpegtype(v) then lpegprint(v) return true end end) +if setinspector then + setinspector(function(v) if lpegtype(v) then lpegprint(v) return true end end) +end lpeg.patterns=lpeg.patterns or {} local patterns=lpeg.patterns local anything=P(1) @@ -469,7 +481,7 @@ local uppercase=R("AZ") local underscore=P("_") local hexdigit=digit+lowercase+uppercase local cr,lf,crlf=P("\r"),P("\n"),P("\r\n") -local newline=crlf+S("\r\n") +local newline=P("\r")*(P("\n")+P(true))+P("\n") local escaped=P("\\")*anything local squote=P("'") local dquote=P('"') @@ -491,8 +503,10 @@ patterns.utfbom_32_le=utfbom_32_le patterns.utfbom_16_be=utfbom_16_be patterns.utfbom_16_le=utfbom_16_le patterns.utfbom_8=utfbom_8 -patterns.utf_16_be_nl=P("\000\r\000\n")+P("\000\r")+P("\000\n") -patterns.utf_16_le_nl=P("\r\000\n\000")+P("\r\000")+P("\n\000") +patterns.utf_16_be_nl=P("\000\r\000\n")+P("\000\r")+P("\000\n") +patterns.utf_16_le_nl=P("\r\000\n\000")+P("\r\000")+P("\n\000") +patterns.utf_32_be_nl=P("\000\000\000\r\000\000\000\n")+P("\000\000\000\r")+P("\000\000\000\n") +patterns.utf_32_le_nl=P("\r\000\000\000\n\000\000\000")+P("\r\000\000\000")+P("\n\000\000\000") patterns.utf8one=R("\000\127") patterns.utf8two=R("\194\223")*utf8next patterns.utf8three=R("\224\239")*utf8next*utf8next @@ -519,10 +533,24 @@ patterns.spacer=spacer patterns.whitespace=whitespace patterns.nonspacer=nonspacer patterns.nonwhitespace=nonwhitespace -local stripper=spacer^0*C((spacer^0*nonspacer^1)^0) +local stripper=spacer^0*C((spacer^0*nonspacer^1)^0) +local fullstripper=whitespace^0*C((whitespace^0*nonwhitespace^1)^0) local collapser=Cs(spacer^0/""*nonspacer^0*((spacer^0/" "*nonspacer^1)^0)) +local b_collapser=Cs(whitespace^0/""*(nonwhitespace^1+whitespace^1/" ")^0) +local e_collapser=Cs((whitespace^1*P(-1)/""+nonwhitespace^1+whitespace^1/" ")^0) +local m_collapser=Cs((nonwhitespace^1+whitespace^1/" ")^0) +local b_stripper=Cs(spacer^0/""*(nonspacer^1+spacer^1/" ")^0) +local e_stripper=Cs((spacer^1*P(-1)/""+nonspacer^1+spacer^1/" ")^0) +local m_stripper=Cs((nonspacer^1+spacer^1/" ")^0) patterns.stripper=stripper +patterns.fullstripper=fullstripper patterns.collapser=collapser +patterns.b_collapser=b_collapser +patterns.m_collapser=m_collapser +patterns.e_collapser=e_collapser +patterns.b_stripper=b_stripper +patterns.m_stripper=m_stripper +patterns.e_stripper=e_stripper patterns.lowercase=lowercase patterns.uppercase=uppercase patterns.letter=patterns.lowercase+patterns.uppercase @@ -559,9 +587,12 @@ patterns.integer=sign^-1*digit^1 patterns.unsigned=digit^0*period*digit^1 patterns.float=sign^-1*patterns.unsigned patterns.cunsigned=digit^0*comma*digit^1 +patterns.cpunsigned=digit^0*(period+comma)*digit^1 patterns.cfloat=sign^-1*patterns.cunsigned +patterns.cpfloat=sign^-1*patterns.cpunsigned patterns.number=patterns.float+patterns.integer patterns.cnumber=patterns.cfloat+patterns.integer +patterns.cpnumber=patterns.cpfloat+patterns.integer patterns.oct=zero*octdigit^1 patterns.octal=patterns.oct patterns.HEX=zero*P("X")*(digit+uppercase)^1 @@ -744,7 +775,7 @@ function lpeg.replacer(one,two,makefunction,isutf) return pattern end end -function lpeg.finder(lst,makefunction) +function lpeg.finder(lst,makefunction,isutf) local pattern if type(lst)=="table" then pattern=P(false) @@ -760,7 +791,11 @@ function lpeg.finder(lst,makefunction) else pattern=P(lst) end - pattern=(1-pattern)^0*pattern + if isutf then + pattern=((utf8char or 1)-pattern)^0*pattern + else + pattern=(1-pattern)^0*pattern + end if makefunction then return function(str) return lpegmatch(pattern,str) @@ -974,37 +1009,139 @@ function lpeg.append(list,pp,delayed,checked) end return p end +local p_false=P(false) +local p_true=P(true) local function make(t) - local p + local function making(t) + local p=p_false + local keys=sortedkeys(t) + for i=1,#keys do + local k=keys[i] + if k~="" then + local v=t[k] + if v==true then + p=p+P(k)*p_true + elseif v==false then + else + p=p+P(k)*making(v) + end + end + end + if t[""] then + p=p+p_true + end + return p + end + local p=p_false local keys=sortedkeys(t) for i=1,#keys do local k=keys[i] - local v=t[k] - if not p then - if next(v) then - p=P(k)*make(v) + if k~="" then + local v=t[k] + if v==true then + p=p+P(k)*p_true + elseif v==false then else - p=P(k) + p=p+P(k)*making(v) end - else - if next(v) then - p=p+P(k)*make(v) + end + end + return p +end +local function collapse(t,x) + if type(t)~="table" then + return t,x + else + local n=next(t) + if n==nil then + return t,x + elseif next(t,n)==nil then + local k=n + local v=t[k] + if type(v)=="table" then + return collapse(v,x..k) else - p=p+P(k) + return v,x..k + end + else + local tt={} + for k,v in next,t do + local vv,kk=collapse(v,k) + tt[kk]=vv end + return tt,x end end - return p end function lpeg.utfchartabletopattern(list) local tree={} - for i=1,#list do - local t=tree - for c in gmatch(list[i],".") do - if not t[c] then - t[c]={} + local n=#list + if n==0 then + for s in next,list do + local t=tree + local p,pk + for c in gmatch(s,".") do + if t==true then + t={ [c]=true,[""]=true } + p[pk]=t + p=t + t=false + elseif t==false then + t={ [c]=false } + p[pk]=t + p=t + t=false + else + local tc=t[c] + if not tc then + tc=false + t[c]=false + end + p=t + t=tc + end + pk=c + end + if t==false then + p[pk]=true + elseif t==true then + else + t[""]=true + end + end + else + for i=1,n do + local s=list[i] + local t=tree + local p,pk + for c in gmatch(s,".") do + if t==true then + t={ [c]=true,[""]=true } + p[pk]=t + p=t + t=false + elseif t==false then + t={ [c]=false } + p[pk]=t + p=t + t=false + else + local tc=t[c] + if not tc then + tc=false + t[c]=false + end + p=t + t=tc + end + pk=c + end + if t==false then + p[pk]=true + elseif t==true then + else + t[""]=true end - t=t[c] end end return make(tree) @@ -1044,6 +1181,65 @@ local case_2=period*(digit-trailingzeros)^1*(trailingzeros/"") local number=digit^1*(case_1+case_2) local stripper=Cs((number+1)^0) lpeg.patterns.stripzeros=stripper +local byte_to_HEX={} +local byte_to_hex={} +local byte_to_dec={} +local hex_to_byte={} +for i=0,255 do + local H=format("%02X",i) + local h=format("%02x",i) + local d=format("%03i",i) + local c=char(i) + byte_to_HEX[c]=H + byte_to_hex[c]=h + byte_to_dec[c]=d + hex_to_byte[h]=c + hex_to_byte[H]=c +end +local hextobyte=P(2)/hex_to_byte +local bytetoHEX=P(1)/byte_to_HEX +local bytetohex=P(1)/byte_to_hex +local bytetodec=P(1)/byte_to_dec +local hextobytes=Cs(hextobyte^0) +local bytestoHEX=Cs(bytetoHEX^0) +local bytestohex=Cs(bytetohex^0) +local bytestodec=Cs(bytetodec^0) +patterns.hextobyte=hextobyte +patterns.bytetoHEX=bytetoHEX +patterns.bytetohex=bytetohex +patterns.bytetodec=bytetodec +patterns.hextobytes=hextobytes +patterns.bytestoHEX=bytestoHEX +patterns.bytestohex=bytestohex +patterns.bytestodec=bytestodec +function string.toHEX(s) + if not s or s=="" then + return s + else + return lpegmatch(bytestoHEX,s) + end +end +function string.tohex(s) + if not s or s=="" then + return s + else + return lpegmatch(bytestohex,s) + end +end +function string.todec(s) + if not s or s=="" then + return s + else + return lpegmatch(bytestodec,s) + end +end +function string.tobytes(s) + if not s or s=="" then + return s + else + return lpegmatch(hextobytes,s) + end +end end -- of closure @@ -1071,7 +1267,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-string"] = package.loaded["l-string"] or true --- original size: 5547, stripped down to: 2708 +-- original size: 5694, stripped down to: 2827 if not modules then modules={} end modules ['l-string']={ version=1.001, @@ -1107,11 +1303,15 @@ function string.limit(str,n,sentinel) end end local stripper=patterns.stripper +local fullstripper=patterns.fullstripper local collapser=patterns.collapser local longtostring=patterns.longtostring function string.strip(str) return lpegmatch(stripper,str) or "" end +function string.fullstrip(str) + return lpegmatch(fullstripper,str) or "" +end function string.collapsespaces(str) return lpegmatch(collapser,str) or "" end @@ -1172,7 +1372,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-table"] = package.loaded["l-table"] or true --- original size: 31113, stripped down to: 20256 +-- original size: 35724, stripped down to: 21525 if not modules then modules={} end modules ['l-table']={ version=1.001, @@ -1205,7 +1405,7 @@ end function table.keys(t) if t then local keys,k={},0 - for key,_ in next,t do + for key in next,t do k=k+1 keys[k]=key end @@ -1215,32 +1415,52 @@ function table.keys(t) end end local function compare(a,b) - local ta,tb=type(a),type(b) - if ta==tb then - return a<b - else - return tostring(a)<tostring(b) + local ta=type(a) + if ta=="number" then + local tb=type(b) + if ta==tb then + return a<b + elseif tb=="string" then + return tostring(a)<b + end + elseif ta=="string" then + local tb=type(b) + if ta==tb then + return a<b + else + return a<tostring(b) + end end + return tostring(a)<tostring(b) end local function sortedkeys(tab) if tab then local srt,category,s={},0,0 - for key,_ in next,tab do + for key in next,tab do s=s+1 srt[s]=key if category==3 then + elseif category==1 then + if type(key)~="string" then + category=3 + end + elseif category==2 then + if type(key)~="number" then + category=3 + end else local tkey=type(key) if tkey=="string" then - category=(category==2 and 3) or 1 + category=1 elseif tkey=="number" then - category=(category==1 and 3) or 2 + category=2 else category=3 end end end - if category==0 or category==3 then + if s<2 then + elseif category==3 then sort(srt,compare) else sort(srt) @@ -1250,16 +1470,52 @@ local function sortedkeys(tab) return {} end end +local function sortedhashonly(tab) + if tab then + local srt,s={},0 + for key in next,tab do + if type(key)=="string" then + s=s+1 + srt[s]=key + end + end + if s>1 then + sort(srt) + end + return srt + else + return {} + end +end +local function sortedindexonly(tab) + if tab then + local srt,s={},0 + for key in next,tab do + if type(key)=="number" then + s=s+1 + srt[s]=key + end + end + if s>1 then + sort(srt) + end + return srt + else + return {} + end +end local function sortedhashkeys(tab,cmp) if tab then local srt,s={},0 - for key,_ in next,tab do + for key in next,tab do if key then s=s+1 srt[s]=key end end - sort(srt,cmp) + if s>1 then + sort(srt,cmp) + end return srt else return {} @@ -1268,13 +1524,15 @@ end function table.allkeys(t) local keys={} for k,v in next,t do - for k,v in next,v do + for k in next,v do keys[k]=true end end return sortedkeys(keys) end table.sortedkeys=sortedkeys +table.sortedhashonly=sortedhashonly +table.sortedindexonly=sortedindexonly table.sortedhashkeys=sortedhashkeys local function nothing() end local function sortedhash(t,cmp) @@ -1285,19 +1543,21 @@ local function sortedhash(t,cmp) else s=sortedkeys(t) end - local n=0 local m=#s - local function kv(s) - if n<m then - n=n+1 - local k=s[n] - return k,t[k] + if m==1 then + return next,t + elseif m>0 then + local n=0 + return function() + if n<m then + n=n+1 + local k=s[n] + return k,t[k] + end end end - return kv,s - else - return nothing end + return nothing end table.sortedhash=sortedhash table.sortedpairs=sortedhash @@ -1439,39 +1699,36 @@ function table.fromhash(t) end return hsh end -local noquotes,hexify,handle,reduce,compact,inline,functions +local noquotes,hexify,handle,compact,inline,functions local reserved=table.tohash { 'and','break','do','else','elseif','end','false','for','function','if', 'in','local','nil','not','or','repeat','return','then','true','until','while', 'NaN','goto', } local function simple_table(t) - if #t>0 then + local nt=#t + if nt>0 then local n=0 for _,v in next,t do n=n+1 end - if n==#t then - local tt,nt={},0 - for i=1,#t do + if n==nt then + local tt={} + for i=1,nt do local v=t[i] local tv=type(v) if tv=="number" then - nt=nt+1 if hexify then - tt[nt]=format("0x%04X",v) + tt[i]=format("0x%X",v) else - tt[nt]=tostring(v) + tt[i]=tostring(v) end elseif tv=="string" then - nt=nt+1 - tt[nt]=format("%q",v) + tt[i]=format("%q",v) elseif tv=="boolean" then - nt=nt+1 - tt[nt]=v and "true" or "false" + tt[i]=v and "true" or "false" else - tt=nil - break + return nil end end return tt @@ -1490,7 +1747,7 @@ local function do_serialize(root,name,depth,level,indexed) local tn=type(name) if tn=="number" then if hexify then - handle(format("%s[0x%04X]={",depth,name)) + handle(format("%s[0x%X]={",depth,name)) else handle(format("%s[%s]={",depth,name)) end @@ -1507,7 +1764,7 @@ local function do_serialize(root,name,depth,level,indexed) end end end - if root and next(root) then + if root and next(root)~=nil then local first,last=nil,0 if compact then last=#root @@ -1525,22 +1782,19 @@ local function do_serialize(root,name,depth,level,indexed) for i=1,#sk do local k=sk[i] local v=root[k] - local tv,tk=type(v),type(k) + local tv=type(v) + local tk=type(k) if compact and first and tk=="number" and k>=first and k<=last then if tv=="number" then if hexify then - handle(format("%s 0x%04X,",depth,v)) + handle(format("%s 0x%X,",depth,v)) else handle(format("%s %s,",depth,v)) end elseif tv=="string" then - if reduce and tonumber(v) then - handle(format("%s %s,",depth,v)) - else - handle(format("%s %q,",depth,v)) - end + handle(format("%s %q,",depth,v)) elseif tv=="table" then - if not next(v) then + if next(v)==nil then handle(format("%s {},",depth)) elseif inline then local st=simple_table(v) @@ -1570,64 +1824,48 @@ local function do_serialize(root,name,depth,level,indexed) elseif tv=="number" then if tk=="number" then if hexify then - handle(format("%s [0x%04X]=0x%04X,",depth,k,v)) + handle(format("%s [0x%X]=0x%X,",depth,k,v)) else handle(format("%s [%s]=%s,",depth,k,v)) end elseif tk=="boolean" then if hexify then - handle(format("%s [%s]=0x%04X,",depth,k and "true" or "false",v)) + handle(format("%s [%s]=0x%X,",depth,k and "true" or "false",v)) else handle(format("%s [%s]=%s,",depth,k and "true" or "false",v)) end elseif noquotes and not reserved[k] and lpegmatch(propername,k) then if hexify then - handle(format("%s %s=0x%04X,",depth,k,v)) + handle(format("%s %s=0x%X,",depth,k,v)) else handle(format("%s %s=%s,",depth,k,v)) end else if hexify then - handle(format("%s [%q]=0x%04X,",depth,k,v)) + handle(format("%s [%q]=0x%X,",depth,k,v)) else handle(format("%s [%q]=%s,",depth,k,v)) end end elseif tv=="string" then - if reduce and tonumber(v) then - if tk=="number" 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,k and "true" or "false",v)) - elseif noquotes and not reserved[k] and lpegmatch(propername,k) then - handle(format("%s %s=%s,",depth,k,v)) + if tk=="number" then + if hexify then + handle(format("%s [0x%X]=%q,",depth,k,v)) else - handle(format("%s [%q]=%s,",depth,k,v)) + handle(format("%s [%s]=%q,",depth,k,v)) end + elseif tk=="boolean" then + handle(format("%s [%s]=%q,",depth,k and "true" or "false",v)) + elseif noquotes and not reserved[k] and lpegmatch(propername,k) then + handle(format("%s %s=%q,",depth,k,v)) else - if tk=="number" 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,k and "true" or "false",v)) - elseif noquotes and not reserved[k] and lpegmatch(propername,k) then - handle(format("%s %s=%q,",depth,k,v)) - else - handle(format("%s [%q]=%q,",depth,k,v)) - end + handle(format("%s [%q]=%q,",depth,k,v)) end elseif tv=="table" then - if not next(v) then + if next(v)==nil then if tk=="number" then if hexify then - handle(format("%s [0x%04X]={},",depth,k)) + handle(format("%s [0x%X]={},",depth,k)) else handle(format("%s [%s]={},",depth,k)) end @@ -1643,7 +1881,7 @@ local function do_serialize(root,name,depth,level,indexed) if st then if tk=="number" then if hexify then - handle(format("%s [0x%04X]={ %s },",depth,k,concat(st,", "))) + handle(format("%s [0x%X]={ %s },",depth,k,concat(st,", "))) else handle(format("%s [%s]={ %s },",depth,k,concat(st,", "))) end @@ -1663,7 +1901,7 @@ local function do_serialize(root,name,depth,level,indexed) elseif tv=="boolean" then if tk=="number" then if hexify then - handle(format("%s [0x%04X]=%s,",depth,k,v and "true" or "false")) + handle(format("%s [0x%X]=%s,",depth,k,v and "true" or "false")) else handle(format("%s [%s]=%s,",depth,k,v and "true" or "false")) end @@ -1679,7 +1917,7 @@ local function do_serialize(root,name,depth,level,indexed) local f=getinfo(v).what=="C" and dump(dummy) or dump(v) if tk=="number" then if hexify then - handle(format("%s [0x%04X]=load(%q),",depth,k,f)) + handle(format("%s [0x%X]=load(%q),",depth,k,f)) else handle(format("%s [%s]=load(%q),",depth,k,f)) end @@ -1694,7 +1932,7 @@ local function do_serialize(root,name,depth,level,indexed) else if tk=="number" then if hexify then - handle(format("%s [0x%04X]=%q,",depth,k,tostring(v))) + handle(format("%s [0x%X]=%q,",depth,k,tostring(v))) else handle(format("%s [%s]=%q,",depth,k,tostring(v))) end @@ -1718,7 +1956,6 @@ local function serialize(_handle,root,name,specification) 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 @@ -1735,7 +1972,6 @@ local function serialize(_handle,root,name,specification) noquotes=false hexify=false handle=_handle or print - reduce=false compact=true inline=true functions=true @@ -1748,7 +1984,7 @@ local function serialize(_handle,root,name,specification) end elseif tname=="number" then if hexify then - handle(format("[0x%04X]={",name)) + handle(format("[0x%X]={",name)) else handle("["..name.."]={") end @@ -1766,7 +2002,7 @@ local function serialize(_handle,root,name,specification) local dummy=root._w_h_a_t_e_v_e_r_ root._w_h_a_t_e_v_e_r_=nil end - if next(root) then + if next(root)~=nil then do_serialize(root,name,"",0) end end @@ -1895,14 +2131,25 @@ local function identical(a,b) end table.identical=identical table.are_equal=are_equal -function table.compact(t) - if t then - for k,v in next,t do - if not next(v) then - t[k]=nil +local function sparse(old,nest,keeptables) + local new={} + for k,v in next,old do + if not (v=="" or v==false) then + if nest and type(v)=="table" then + v=sparse(v,nest) + if keeptables or next(v)~=nil then + new[k]=v + end + else + new[k]=v end end end + return new +end +table.sparse=sparse +function table.compact(t) + return sparse(t,true,true) end function table.contains(t,v) if t then @@ -2000,15 +2247,17 @@ function table.print(t,...) serialize(print,t,...) end end -setinspector(function(v) if type(v)=="table" then serialize(print,v,"table") return true end end) +if setinspector then + setinspector(function(v) if type(v)=="table" then serialize(print,v,"table") return true end end) +end function table.sub(t,i,j) return { unpack(t,i,j) } end function table.is_empty(t) - return not t or not next(t) + return not t or next(t)==nil end function table.has_one_entry(t) - return t and not next(t,next(t)) + return t and next(t,next(t))==nil end function table.loweredkeys(t) local l={} @@ -2053,6 +2302,44 @@ function table.values(t,s) return {} end end +function table.filtered(t,pattern,sort,cmp) + if t and type(pattern)=="string" then + if sort then + local s + if cmp then + s=sortedhashkeys(t,function(a,b) return cmp(t,a,b) end) + else + s=sortedkeys(t) + end + local n=0 + local m=#s + local function kv(s) + while n<m do + n=n+1 + local k=s[n] + if find(k,pattern) then + return k,t[k] + end + end + end + return kv,s + else + local n=next(t) + local function iterator() + while n~=nil do + local k=n + n=next(t,k) + if find(k,pattern) then + return k,t[k] + end + end + end + return iterator,t + end + else + return nothing + end +end end -- of closure @@ -2061,7 +2348,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-io"] = package.loaded["l-io"] or true --- original size: 8817, stripped down to: 6340 +-- original size: 8643, stripped down to: 6232 if not modules then modules={} end modules ['l-io']={ version=1.001, @@ -2075,7 +2362,7 @@ local byte,find,gsub,format=string.byte,string.find,string.gsub,string.format local concat=table.concat local floor=math.floor local type=type -if string.find(os.getenv("PATH"),";") then +if string.find(os.getenv("PATH"),";",1,true) then io.fileseparator,io.pathseparator="\\",";" else io.fileseparator,io.pathseparator="/",":" @@ -2368,8 +2655,6 @@ function io.readstring(f,n,m) local str=gsub(f:read(n),"\000","") return str end -if not io.i_limiter then function io.i_limiter() end end -if not io.o_limiter then function io.o_limiter() end end end -- of closure @@ -2596,7 +2881,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-os"] = package.loaded["l-os"] or true --- original size: 16023, stripped down to: 9634 +-- original size: 15832, stripped down to: 9456 if not modules then modules={} end modules ['l-os']={ version=1.001, @@ -2670,13 +2955,10 @@ if not os.__getenv__ then setmetatable(os.env,{ __index=__index,__newindex=__newindex } ) end end -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 +local execute=os.execute +local iopopen=io.popen function os.resultof(command) - local handle=io.popen(command,"r") + local handle=iopopen(command,"r") if handle then local result=handle:read("*all") or "" handle:close() @@ -2686,7 +2968,7 @@ function os.resultof(command) end end if not io.fileseparator then - if find(os.getenv("PATH"),";") then + if find(os.getenv("PATH"),";",1,true) then io.fileseparator,io.pathseparator,os.type="\\",";",os.type or "mswin" else io.fileseparator,io.pathseparator,os.type="/",":",os.type or "unix" @@ -2705,7 +2987,7 @@ local launchers={ unix="$BROWSER %s &> /dev/null &", } function os.launch(str) - os.execute(format(launchers[os.name] or launchers.unix,str)) + execute(format(launchers[os.name] or launchers.unix,str)) end if not os.times then function os.times() @@ -2746,7 +3028,7 @@ if platform~="" then elseif os.type=="windows" then function resolvers.platform(t,k) local platform,architecture="",os.getenv("PROCESSOR_ARCHITECTURE") or "" - if find(architecture,"AMD64") then + if find(architecture,"AMD64",1,true) then platform="win64" else platform="mswin" @@ -2758,9 +3040,9 @@ elseif os.type=="windows" then elseif name=="linux" then function resolvers.platform(t,k) local platform,architecture="",os.getenv("HOSTTYPE") or os.resultof("uname -m") or "" - if find(architecture,"x86_64") then + if find(architecture,"x86_64",1,true) then platform="linux-64" - elseif find(architecture,"ppc") then + elseif find(architecture,"ppc",1,true) then platform="linux-ppc" else platform="linux" @@ -2774,9 +3056,9 @@ elseif name=="macosx" then local platform,architecture="",os.resultof("echo $HOSTTYPE") or "" if architecture=="" then platform="osx-intel" - elseif find(architecture,"i386") then + elseif find(architecture,"i386",1,true) then platform="osx-intel" - elseif find(architecture,"x86_64") then + elseif find(architecture,"x86_64",1,true) then platform="osx-64" else platform="osx-ppc" @@ -2788,7 +3070,7 @@ elseif name=="macosx" then elseif name=="sunos" then function resolvers.platform(t,k) local platform,architecture="",os.resultof("uname -m") or "" - if find(architecture,"sparc") then + if find(architecture,"sparc",1,true) then platform="solaris-sparc" else platform="solaris-intel" @@ -2800,7 +3082,7 @@ elseif name=="sunos" then elseif name=="freebsd" then function resolvers.platform(t,k) local platform,architecture="",os.resultof("uname -m") or "" - if find(architecture,"amd64") then + if find(architecture,"amd64",1,true) then platform="freebsd-amd64" else platform="freebsd" @@ -2812,7 +3094,7 @@ elseif name=="freebsd" then elseif name=="kfreebsd" then function resolvers.platform(t,k) local platform,architecture="",os.getenv("HOSTTYPE") or os.resultof("uname -m") or "" - if find(architecture,"x86_64") then + if find(architecture,"x86_64",1,true) then platform="kfreebsd-amd64" else platform="kfreebsd-i386" @@ -2829,8 +3111,9 @@ else return platform end end +os.newline=name=="windows" and "\013\010" or "\010" function resolvers.bits(t,k) - local bits=find(os.platform,"64") and 64 or 32 + local bits=find(os.platform,"64",1,true) and 64 or 32 os.bits=bits return bits end @@ -2980,7 +3263,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-file"] = package.loaded["l-file"] or true --- original size: 18308, stripped down to: 9948 +-- original size: 20949, stripped down to: 9945 if not modules then modules={} end modules ['l-file']={ version=1.001, @@ -2994,41 +3277,28 @@ local file=file if not lfs then lfs=optionalrequire("lfs") end -if not lfs then - lfs={ - getcurrentdir=function() - return "." - end, - attributes=function() - return nil - end, - isfile=function(name) - local f=io.open(name,'rb') - if f then - f:close() - return true - end - end, - isdir=function(name) - print("you need to load lfs") - return false - end - } -elseif not lfs.isfile then - local attributes=lfs.attributes - function lfs.isdir(name) - return attributes(name,"mode")=="directory" - end - function lfs.isfile(name) - return attributes(name,"mode")=="file" - end -end local insert,concat=table.insert,table.concat local match,find,gmatch=string.match,string.find,string.gmatch local lpegmatch=lpeg.match local getcurrentdir,attributes=lfs.currentdir,lfs.attributes local checkedsplit=string.checkedsplit local P,R,S,C,Cs,Cp,Cc,Ct=lpeg.P,lpeg.R,lpeg.S,lpeg.C,lpeg.Cs,lpeg.Cp,lpeg.Cc,lpeg.Ct +local tricky=S("/\\")*P(-1) +local attributes=lfs.attributes +if sandbox then + sandbox.redefine(lfs.isfile,"lfs.isfile") + sandbox.redefine(lfs.isdir,"lfs.isdir") +end +function lfs.isdir(name) + if lpegmatch(tricky,name) then + return attributes(name,"mode")=="directory" + else + return attributes(name.."/.","mode")=="directory" + end +end +function lfs.isfile(name) + return attributes(name,"mode")=="file" +end local colon=P(":") local period=P(".") local periods=P("..") @@ -3230,28 +3500,30 @@ local isroot=fwslash^1*-1 local hasroot=fwslash^1 local reslasher=lpeg.replacer(S("\\/"),"/") local deslasher=lpeg.replacer(S("\\/")^1,"/") -function file.join(...) - local lst={... } - local one=lst[1] +function file.join(one,two,three,...) + if not two then + return one=="" and one or lpegmatch(stripper,one) + end + if one=="" then + return lpegmatch(stripper,three and concat({ two,three,... },"/") or two) + end if lpegmatch(isnetwork,one) then local one=lpegmatch(reslasher,one) - local two=lpegmatch(deslasher,concat(lst,"/",2)) + local two=lpegmatch(deslasher,three and concat({ two,three,... },"/") or two) if lpegmatch(hasroot,two) then return one..two else return one.."/"..two end elseif lpegmatch(isroot,one) then - local two=lpegmatch(deslasher,concat(lst,"/",2)) + local two=lpegmatch(deslasher,three and concat({ two,three,... },"/") or two) if lpegmatch(hasroot,two) then return two else return "/"..two end - elseif one=="" then - return lpegmatch(stripper,concat(lst,"/",2)) else - return lpegmatch(deslasher,concat(lst,"/")) + return lpegmatch(deslasher,concat({ one,two,three,... },"/")) end end local drivespec=R("az","AZ")^1*colon @@ -3425,7 +3697,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-md5"] = package.loaded["l-md5"] or true --- original size: 3760, stripped down to: 2088 +-- original size: 3248, stripped down to: 2266 if not modules then modules={} end modules ['l-md5']={ version=1.001, @@ -3443,14 +3715,20 @@ if not md5 then } end local md5,file=md5,file -local gsub,format,byte=string.gsub,string.format,string.byte -local md5sum=md5.sum -local function convert(str,fmt) - return (gsub(md5sum(str),".",function(chr) return format(fmt,byte(chr)) end)) -end -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 +local gsub=string.gsub +do + local patterns=lpeg and lpeg.patterns + if patterns then + local bytestoHEX=patterns.bytestoHEX + local bytestohex=patterns.bytestohex + local bytestodec=patterns.bytestodec + local lpegmatch=lpeg.match + local md5sum=md5.sum + if not md5.HEX then function md5.HEX(str) if str then return lpegmatch(bytestoHEX,md5sum(str)) end end end + if not md5.hex then function md5.hex(str) if str then return lpegmatch(bytestohex,md5sum(str)) end end end + if not md5.dec then function md5.dec(str) if str then return lpegmatch(bytestodec,md5sum(str)) end end end + end +end function file.needsupdating(oldname,newname,threshold) local oldtime=lfs.attributes(oldname,"modification") if oldtime then @@ -3507,7 +3785,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-url"] = package.loaded["l-url"] or true --- original size: 11993, stripped down to: 5584 +-- original size: 12531, stripped down to: 5721 if not modules then modules={} end modules ['l-url']={ version=1.001, @@ -3534,7 +3812,7 @@ local hexdigit=R("09","AF","af") local plus=P("+") local nothing=Cc("") local escapedchar=(percent*C(hexdigit*hexdigit))/tochar -local escaped=(plus/" ")+escapedchar +local escaped=(plus/" ")+escapedchar local noslash=P("/")/"" local schemestr=Cs((escaped+(1-colon-slash-qmark-hash))^2) local authoritystr=Cs((escaped+(1- slash-qmark-hash))^0) @@ -3593,19 +3871,25 @@ local splitquery=Cf (Ct("")*P { "sequence", pair=Cg(key*equal*value), },rawset) local function hashed(str) - if str=="" then + if not str or str=="" then return { scheme="invalid", original=str, } end - local s=split(str) - local rawscheme=s[1] - local rawquery=s[4] - local somescheme=rawscheme~="" - local somequery=rawquery~="" + local detailed=split(str) + local rawscheme="" + local rawquery="" + local somescheme=false + local somequery=false + if detailed then + rawscheme=detailed[1] + rawquery=detailed[4] + somescheme=rawscheme~="" + somequery=rawquery~="" + end if not somescheme and not somequery then - s={ + return { scheme="file", authority="", path=str, @@ -3615,28 +3899,28 @@ local function hashed(str) noscheme=true, filename=str, } - else - local authority,path,filename=s[2],s[3] - if authority=="" then - filename=path - elseif path=="" then - filename="" - else - filename=authority.."/"..path - end - s={ - scheme=rawscheme, - authority=authority, - path=path, - query=lpegmatch(unescaper,rawquery), - queries=lpegmatch(splitquery,rawquery), - fragment=s[5], - original=str, - noscheme=false, - filename=filename, - } end - return s + local authority=detailed[2] + local path=detailed[3] + local filename=nil + if authority=="" then + filename=path + elseif path=="" then + filename="" + else + filename=authority.."/"..path + end + return { + scheme=rawscheme, + authority=authority, + path=path, + query=lpegmatch(unescaper,rawquery), + queries=lpegmatch(splitquery,rawquery), + fragment=detailed[5], + original=str, + noscheme=false, + filename=filename, + } end url.split=split url.hasscheme=hasscheme @@ -3670,7 +3954,7 @@ function url.construct(hash) end return lpegmatch(escaper,concat(fullurl)) end -local pattern=Cs(noslash*R("az","AZ")*(S(":|")/":")*noslash*P(1)^0) +local pattern=Cs(slash^-1/""*R("az","AZ")*((S(":|")/":")+P(":"))*slash*P(1)^0) function url.filename(filename) local spec=hashed(filename) local path=spec.path @@ -3718,7 +4002,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-dir"] = package.loaded["l-dir"] or true --- original size: 14229, stripped down to: 8740 +-- original size: 16765, stripped down to: 11003 if not modules then modules={} end modules ['l-dir']={ version=1.001, @@ -3728,7 +4012,7 @@ if not modules then modules={} end modules ['l-dir']={ license="see context related readme files" } local type,select=type,select -local find,gmatch,match,gsub=string.find,string.gmatch,string.match,string.gsub +local find,gmatch,match,gsub,sub=string.find,string.gmatch,string.match,string.gsub,string.sub local concat,insert,remove,unpack=table.concat,table.insert,table.remove,table.unpack local lpegmatch=lpeg.match local P,S,R,C,Cc,Cs,Ct,Cv,V=lpeg.P,lpeg.S,lpeg.R,lpeg.C,lpeg.Cc,lpeg.Cs,lpeg.Ct,lpeg.Cv,lpeg.V @@ -3737,53 +4021,127 @@ local dir=dir local lfs=lfs local attributes=lfs.attributes local walkdir=lfs.dir -local isdir=lfs.isdir -local isfile=lfs.isfile +local isdir=lfs.isdir +local isfile=lfs.isfile local currentdir=lfs.currentdir local chdir=lfs.chdir -local onwindows=os.type=="windows" or find(os.getenv("PATH"),";") -if not isdir then - function isdir(name) - local a=attributes(name) - return a and a.mode=="directory" +local mkdir=lfs.mkdir +local onwindows=os.type=="windows" or find(os.getenv("PATH"),";",1,true) +if onwindows then + local tricky=S("/\\")*P(-1) + isdir=function(name) + if lpegmatch(tricky,name) then + return attributes(name,"mode")=="directory" + else + return attributes(name.."/.","mode")=="directory" + end + end + isfile=function(name) + return attributes(name,"mode")=="file" end lfs.isdir=isdir -end -if not isfile then - function isfile(name) - local a=attributes(name) - return a and a.mode=="file" + lfs.isfile=isfile +else + isdir=function(name) + return attributes(name,"mode")=="directory" end + isfile=function(name) + return attributes(name,"mode")=="file" + end + lfs.isdir=isdir lfs.isfile=isfile end function dir.current() return (gsub(currentdir(),"\\","/")) end -local lfsisdir=isdir -local function isdir(path) - path=gsub(path,"[/\\]+$","") - return lfsisdir(path) +local function glob_pattern_function(path,patt,recurse,action) + if isdir(path) then + local usedpath + if path=="/" then + usedpath="/." + elseif not find(path,"/$") then + usedpath=path.."/." + path=path.."/" + else + usedpath=path + end + local dirs + for name in walkdir(usedpath) do + if name~="." and name~=".." then + local full=path..name + local mode=attributes(full,'mode') + if mode=='file' then + if not patt or find(full,patt) then + action(full) + end + elseif recurse and mode=="directory" then + if not dirs then + dirs={ full } + else + dirs[#dirs+1]=full + end + end + end + end + if dirs then + for i=1,#dirs do + glob_pattern_function(dirs[i],patt,recurse,action) + end + end + end end -lfs.isdir=isdir -local function globpattern(path,patt,recurse,action) - if path=="/" then - path=path.."." - elseif not find(path,"/$") then - path=path..'/' - end - if isdir(path) then - for name in walkdir(path) do - local full=path..name - local mode=attributes(full,'mode') - if mode=='file' then - if find(full,patt) then - action(full) +local function glob_pattern_table(path,patt,recurse,result) + if not result then + result={} + end + if isdir(path) then + local usedpath + if path=="/" then + usedpath="/." + elseif not find(path,"/$") then + usedpath=path.."/." + path=path.."/" + else + usedpath=path + end + local dirs + for name in walkdir(usedpath) do + if name~="." and name~=".." then + local full=path..name + local mode=attributes(full,'mode') + if mode=='file' then + if not patt or find(full,patt) then + result[#result+1]=full + end + elseif recurse and mode=="directory" then + if not dirs then + dirs={ full } + else + dirs[#dirs+1]=full + end end - elseif recurse and (mode=="directory") and (name~='.') and (name~="..") then - globpattern(full,patt,recurse,action) + end + end + if dirs then + for i=1,#dirs do + glob_pattern_table(dirs[i],patt,recurse,result) end end end + return result +end +local function globpattern(path,patt,recurse,method) + local kind=type(method) + if patt and sub(patt,1,-3)==path then + patt=false + end + if kind=="function" then + return glob_pattern_function(path,patt,recurse,method) + elseif kind=="table" then + return glob_pattern_table(path,patt,recurse,method) + else + return glob_pattern_table(path,patt,recurse,{}) + end end dir.globpattern=globpattern local function collectpattern(path,patt,recurse,result) @@ -3795,34 +4153,40 @@ local function collectpattern(path,patt,recurse,result) ok,scanner,first=xpcall(function() return walkdir(path) end,function() end) end if ok and type(scanner)=="function" then - if not find(path,"/$") then path=path..'/' end + if not find(path,"/$") then + path=path..'/' + end for name in scanner,first do - local full=path..name - local attr=attributes(full) - local mode=attr.mode - if mode=='file' then - if find(full,patt) then + if name=="." then + elseif name==".." then + else + local full=path..name + local attr=attributes(full) + local mode=attr.mode + if mode=='file' then + if find(full,patt) then + result[name]=attr + end + elseif recurse and mode=="directory" then + attr.list=collectpattern(full,patt,recurse) result[name]=attr end - elseif recurse and (mode=="directory") and (name~='.') and (name~="..") then - attr.list=collectpattern(full,patt,recurse) - result[name]=attr end end end return result end dir.collectpattern=collectpattern -local separator -if onwindows then +local separator,pattern +if onwindows then local slash=S("/\\")/"/" - pattern=Ct { + pattern={ [1]=(Cs(P(".")+slash^1)+Cs(R("az","AZ")*P(":")*slash^0)+Cc("./"))*V(2)*V(3), [2]=Cs(((1-S("*?/\\"))^0*slash)^0), [3]=Cs(P(1)^0) } -else - pattern=Ct { +else + pattern={ [1]=(C(P(".")+P("/")^1)+Cc("./"))*V(2)*V(3), [2]=C(((1-S("*?/"))^0*P("/"))^0), [3]=C(P(1)^0) @@ -3840,10 +4204,9 @@ local function glob(str,t) elseif isfile(str) then t(str) else - local split=lpegmatch(pattern,str) - if split then - local root,path,base=split[1],split[2],split[3] - local recurse=find(base,"%*%*") + local root,path,base=lpegmatch(pattern,str) + if root and path and base then + local recurse=find(base,"**",1,true) local start=root..path local result=lpegmatch(filter,start..base) globpattern(start,result,recurse,t) @@ -3864,16 +4227,12 @@ local function glob(str,t) return { str } end else - local split=lpegmatch(pattern,str) - if split then - local t=t or {} - local action=action or function(name) t[#t+1]=name end - local root,path,base=split[1],split[2],split[3] - local recurse=find(base,"%*%*") + local root,path,base=lpegmatch(pattern,str) + if root and path and base then + local recurse=find(base,"**",1,true) local start=root..path local result=lpegmatch(filter,start..base) - globpattern(start,result,recurse,action) - return t + return globpattern(start,result,recurse,t) else return {} end @@ -3913,16 +4272,26 @@ end local make_indeed=true if onwindows then function dir.mkdirs(...) - local str,pth="","" - for i=1,select("#",...) do - local s=select(i,...) - if s=="" then - elseif str=="" then - str=s - else - str=str.."/"..s + local n=select("#",...) + local str + if n==1 then + str=select(1,...) + if isdir(str) then + return str,true + end + else + str="" + for i=1,n do + local s=select(i,...) + if s=="" then + elseif str=="" then + str=s + else + str=str.."/"..s + end end end + local pth="" local drive=false local first,middle,last=match(str,"^(//)(//*)(.*)$") if first then @@ -3957,21 +4326,30 @@ if onwindows then pth=pth.."/"..s end if make_indeed and not isdir(pth) then - lfs.mkdir(pth) + mkdir(pth) end end return pth,(isdir(pth)==true) end else function dir.mkdirs(...) - local str,pth="","" - for i=1,select("#",...) do - local s=select(i,...) - if s and s~="" then - if str~="" then - str=str.."/"..s - else - str=s + local n=select("#",...) + local str,pth + if n==1 then + str=select(1,...) + if isdir(str) then + return str,true + end + else + str="" + for i=1,n do + local s=select(i,...) + if s and s~="" then + if str~="" then + str=str.."/"..s + else + str=s + end end end end @@ -3986,7 +4364,7 @@ else pth=pth.."/"..s end if make_indeed and not first and not isdir(pth) then - lfs.mkdir(pth) + mkdir(pth) end end else @@ -3994,7 +4372,7 @@ else for s in gmatch(str,"[^/]+") do pth=pth.."/"..s if make_indeed and not isdir(pth) then - lfs.mkdir(pth) + mkdir(pth) end end end @@ -4002,47 +4380,51 @@ else end end dir.makedirs=dir.mkdirs -if onwindows then - function dir.expandname(str) - 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() +do + local chdir=sandbox and sandbox.original(chdir) or chdir + if onwindows then + local xcurrentdir=dir.current + function dir.expandname(str) + local first,nothing,last=match(str,"^(//)(//*)(.*)$") + if first then + first=xcurrentdir().."/" + end + if not first then + first,last=match(str,"^(//)/*(.*)$") + end + if not first then + first,last=match(str,"^([a-zA-Z]:)(.*)$") + if first and not find(last,"^/") then + local d=currentdir() + if chdir(first) then + first=xcurrentdir() + end + chdir(d) end - chdir(d) + end + if not first then + first,last=xcurrentdir(),str + end + last=gsub(last,"//","/") + last=gsub(last,"/%./","/") + last=gsub(last,"^/*","") + first=gsub(first,"/*$","") + if last=="" or last=="." then + return first + else + return first.."/"..last end end - 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 -else - function dir.expandname(str) - if not find(str,"^/") then - str=currentdir().."/"..str + else + function dir.expandname(str) + if not find(str,"^/") then + str=currentdir().."/"..str + end + str=gsub(str,"//","/") + str=gsub(str,"/%./","/") + str=gsub(str,"(.)/%.$","%1") + return str end - str=gsub(str,"//","/") - str=gsub(str,"/%./","/") - str=gsub(str,"(.)/%.$","%1") - return str end end file.expandname=dir.expandname @@ -4085,7 +4467,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-boolean"] = package.loaded["l-boolean"] or true --- original size: 1809, stripped down to: 1527 +-- original size: 1850, stripped down to: 1568 if not modules then modules={} end modules ['l-boolean']={ version=1.001, @@ -4139,11 +4521,11 @@ function string.booleanstring(str) return str=="yes" or str=="on" or str=="t" end end -function string.is_boolean(str,default) +function string.is_boolean(str,default,strict) if type(str)=="string" then - if str=="true" or str=="yes" or str=="on" or str=="t" or str=="1" then + if str=="true" or str=="yes" or str=="on" or str=="t" or (not strict and str=="1") then return true - elseif str=="false" or str=="no" or str=="off" or str=="f" or str=="0" then + elseif str=="false" or str=="no" or str=="off" or str=="f" or (not strict and str=="0") then return false end end @@ -4157,7 +4539,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-unicode"] = package.loaded["l-unicode"] or true --- original size: 33473, stripped down to: 14938 +-- original size: 37388, stripped down to: 15817 if not modules then modules={} end modules ['l-unicode']={ version=1.001, @@ -4173,7 +4555,9 @@ local type=type local char,byte,format,sub,gmatch=string.char,string.byte,string.format,string.sub,string.gmatch local concat=table.concat local P,C,R,Cs,Ct,Cmt,Cc,Carg,Cp=lpeg.P,lpeg.C,lpeg.R,lpeg.Cs,lpeg.Ct,lpeg.Cmt,lpeg.Cc,lpeg.Carg,lpeg.Cp -local lpegmatch,patterns=lpeg.match,lpeg.patterns +local lpegmatch=lpeg.match +local patterns=lpeg.patterns +local tabletopattern=lpeg.utfchartabletopattern local bytepairs=string.bytepairs local finder=lpeg.finder local replacer=lpeg.replacer @@ -4182,7 +4566,7 @@ local utfgmatch=utf.gmatch local p_utftype=patterns.utftype local p_utfstricttype=patterns.utfstricttype local p_utfoffset=patterns.utfoffset -local p_utf8char=patterns.utf8char +local p_utf8char=patterns.utf8character local p_utf8byte=patterns.utf8byte local p_utfbom=patterns.utfbom local p_newline=patterns.newline @@ -4321,6 +4705,7 @@ if not utf.sub then local pattern_zero=Cmt(p_utf8char,slide_zero)^0 local pattern_one=Cmt(p_utf8char,slide_one )^0 local pattern_two=Cmt(p_utf8char,slide_two )^0 + local pattern_first=C(patterns.utf8character) function utf.sub(str,start,stop) if not start then return str @@ -4362,7 +4747,9 @@ if not utf.sub then end end end - if start>stop then + if start==1 and stop==1 then + return lpegmatch(pattern_first,str) or "" + elseif start>stop then return "" elseif start>1 then b,e,n,first,last=0,0,0,start-1,stop @@ -4381,15 +4768,52 @@ if not utf.sub then end end end -function utf.remapper(mapping) - local pattern=Cs((p_utf8char/mapping)^0) - return function(str) - if not str or str=="" then - return "" +function utf.remapper(mapping,option) + local variant=type(mapping) + if variant=="table" then + if option=="dynamic" then + local pattern=false + table.setmetatablenewindex(mapping,function(t,k,v) rawset(t,k,v) pattern=false end) + return function(str) + if not str or str=="" then + return "" + else + if not pattern then + pattern=Cs((tabletopattern(mapping)/mapping+p_utf8char)^0) + end + return lpegmatch(pattern,str) + end + end + elseif option=="pattern" then + return Cs((tabletopattern(mapping)/mapping+p_utf8char)^0) else - return lpegmatch(pattern,str) + local pattern=Cs((tabletopattern(mapping)/mapping+p_utf8char)^0) + return function(str) + if not str or str=="" then + return "" + else + return lpegmatch(pattern,str) + end + end,pattern + end + elseif variant=="function" then + if option=="pattern" then + return Cs((p_utf8char/mapping+p_utf8char)^0) + else + local pattern=Cs((p_utf8char/mapping+p_utf8char)^0) + return function(str) + if not str or str=="" then + return "" + else + return lpegmatch(pattern,str) + end + end,pattern end - end,pattern + else + return function(str) + return str or "" + end + end end function utf.replacer(t) local r=replacer(t,false,false,true) @@ -4439,190 +4863,157 @@ function utf.magic(f) end local utf16_to_utf8_be,utf16_to_utf8_le local utf32_to_utf8_be,utf32_to_utf8_le -local utf_16_be_linesplitter=patterns.utfbom_16_be^-1*lpeg.tsplitat(patterns.utf_16_be_nl) -local utf_16_le_linesplitter=patterns.utfbom_16_le^-1*lpeg.tsplitat(patterns.utf_16_le_nl) -if bytepairs then - utf16_to_utf8_be=function(t) - if type(t)=="string" then - t=lpegmatch(utf_16_be_linesplitter,t) - end - local 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) - end - return t +local utf_16_be_getbom=patterns.utfbom_16_be^-1 +local utf_16_le_getbom=patterns.utfbom_16_le^-1 +local utf_32_be_getbom=patterns.utfbom_32_be^-1 +local utf_32_le_getbom=patterns.utfbom_32_le^-1 +local utf_16_be_linesplitter=utf_16_be_getbom*lpeg.tsplitat(patterns.utf_16_be_nl) +local utf_16_le_linesplitter=utf_16_le_getbom*lpeg.tsplitat(patterns.utf_16_le_nl) +local utf_32_be_linesplitter=utf_32_be_getbom*lpeg.tsplitat(patterns.utf_32_be_nl) +local utf_32_le_linesplitter=utf_32_le_getbom*lpeg.tsplitat(patterns.utf_32_le_nl) +local more=0 +local p_utf16_to_utf8_be=C(1)*C(1)/function(left,right) + local now=256*byte(left)+byte(right) + if more>0 then + now=(more-0xD800)*0x400+(now-0xDC00)+0x10000 + more=0 + return utfchar(now) + elseif now>=0xD800 and now<=0xDBFF then + more=now + return "" + else + return utfchar(now) + end +end +local p_utf16_to_utf8_le=C(1)*C(1)/function(right,left) + local now=256*byte(left)+byte(right) + if more>0 then + now=(more-0xD800)*0x400+(now-0xDC00)+0x10000 + more=0 + return utfchar(now) + elseif now>=0xD800 and now<=0xDBFF then + more=now + return "" + else + return utfchar(now) + end +end +local p_utf32_to_utf8_be=C(1)*C(1)*C(1)*C(1)/function(a,b,c,d) + return utfchar(256*256*256*byte(a)+256*256*byte(b)+256*byte(c)+byte(d)) +end +local p_utf32_to_utf8_le=C(1)*C(1)*C(1)*C(1)/function(a,b,c,d) + return utfchar(256*256*256*byte(d)+256*256*byte(c)+256*byte(b)+byte(a)) +end +p_utf16_to_utf8_be=P(true)/function() more=0 end*utf_16_be_getbom*Cs(p_utf16_to_utf8_be^0) +p_utf16_to_utf8_le=P(true)/function() more=0 end*utf_16_le_getbom*Cs(p_utf16_to_utf8_le^0) +p_utf32_to_utf8_be=P(true)/function() more=0 end*utf_32_be_getbom*Cs(p_utf32_to_utf8_be^0) +p_utf32_to_utf8_le=P(true)/function() more=0 end*utf_32_le_getbom*Cs(p_utf32_to_utf8_le^0) +patterns.utf16_to_utf8_be=p_utf16_to_utf8_be +patterns.utf16_to_utf8_le=p_utf16_to_utf8_le +patterns.utf32_to_utf8_be=p_utf32_to_utf8_be +patterns.utf32_to_utf8_le=p_utf32_to_utf8_le +utf16_to_utf8_be=function(s) + if s and s~="" then + return lpegmatch(p_utf16_to_utf8_be,s) + else + return s end - utf16_to_utf8_le=function(t) - if type(t)=="string" then - t=lpegmatch(utf_16_le_linesplitter,t) - end - local 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) - end - return t +end +local utf16_to_utf8_be_t=function(t) + if not t then + return nil + elseif type(t)=="string" then + t=lpegmatch(utf_16_be_linesplitter,t) end - utf32_to_utf8_be=function(t) - if type(t)=="string" then - t=lpegmatch(utflinesplitter,t) - end - local result={} - for i=1,#t do - local r,more=0,-1 - for a,b in bytepairs(t[i]) 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) + for i=1,#t do + local s=t[i] + if s~="" then + t[i]=lpegmatch(p_utf16_to_utf8_be,s) end - return t end - utf32_to_utf8_le=function(t) - if type(t)=="string" then - t=lpegmatch(utflinesplitter,t) - end - local result={} - for i=1,#t do - local r,more=0,-1 - for a,b in bytepairs(t[i]) 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 t + return t +end +utf16_to_utf8_le=function(s) + if s and s~="" then + return lpegmatch(p_utf16_to_utf8_le,s) + else + return s end -else - utf16_to_utf8_be=function(t) - if type(t)=="string" then - t=lpegmatch(utf_16_be_linesplitter,t) - end - local result={} - for i=1,#t do - local r,more=0,0 - for left,right in gmatch(t[i],"(.)(.)") do - if left=="\000" then - r=r+1 - result[r]=utfchar(byte(right)) - elseif right then - local now=256*byte(left)+byte(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) +end +local utf16_to_utf8_le_t=function(t) + if not t then + return nil + elseif type(t)=="string" then + t=lpegmatch(utf_16_le_linesplitter,t) + end + for i=1,#t do + local s=t[i] + if s~="" then + t[i]=lpegmatch(p_utf16_to_utf8_le,s) end - return t end - utf16_to_utf8_le=function(t) - if type(t)=="string" then - t=lpegmatch(utf_16_le_linesplitter,t) + return t +end +utf32_to_utf8_be=function(s) + if s and s~="" then + return lpegmatch(p_utf32_to_utf8_be,s) + else + return s + end +end +local utf32_to_utf8_be_t=function(t) + if not t then + return nil + elseif type(t)=="string" then + t=lpegmatch(utf_32_be_linesplitter,t) + end + for i=1,#t do + local s=t[i] + if s~="" then + t[i]=lpegmatch(p_utf32_to_utf8_be,s) end - local result={} - for i=1,#t do - local r,more=0,0 - for left,right in gmatch(t[i],"(.)(.)") do - if right=="\000" then - r=r+1 - result[r]=utfchar(byte(left)) - elseif right then - local now=256*byte(right)+byte(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) + end + return t +end +utf32_to_utf8_le=function(s) + if s and s~="" then + return lpegmatch(p_utf32_to_utf8_le,s) + else + return s + end +end +local utf32_to_utf8_le_t=function(t) + if not t then + return nil + elseif type(t)=="string" then + t=lpegmatch(utf_32_le_linesplitter,t) + end + for i=1,#t do + local s=t[i] + if s~="" then + t[i]=lpegmatch(p_utf32_to_utf8_le,s) end - return t end - utf32_to_utf8_le=function() return {} end - utf32_to_utf8_be=function() return {} end + return t end +utf.utf16_to_utf8_le_t=utf16_to_utf8_le_t +utf.utf16_to_utf8_be_t=utf16_to_utf8_be_t +utf.utf32_to_utf8_le_t=utf32_to_utf8_le_t +utf.utf32_to_utf8_be_t=utf32_to_utf8_be_t utf.utf16_to_utf8_le=utf16_to_utf8_le utf.utf16_to_utf8_be=utf16_to_utf8_be utf.utf32_to_utf8_le=utf32_to_utf8_le utf.utf32_to_utf8_be=utf32_to_utf8_be -function utf.utf8_to_utf8(t) +function utf.utf8_to_utf8_t(t) return type(t)=="string" and lpegmatch(utflinesplitter,t) or t end -function utf.utf16_to_utf8(t,endian) - return endian and utf16_to_utf8_be(t) or utf16_to_utf8_le(t) or t +function utf.utf16_to_utf8_t(t,endian) + return endian and utf16_to_utf8_be_t(t) or utf16_to_utf8_le_t(t) or t end -function utf.utf32_to_utf8(t,endian) - return endian and utf32_to_utf8_be(t) or utf32_to_utf8_le(t) or t +function utf.utf32_to_utf8_t(t,endian) + return endian and utf32_to_utf8_be_t(t) or utf32_to_utf8_le_t(t) or t end -local function little(c) - local b=byte(c) +local function little(b) if b<0x10000 then return char(b%256,b/256) else @@ -4631,8 +5022,7 @@ local function little(c) return char(b1%256,b1/256,b2%256,b2/256) end end -local function big(c) - local b=byte(c) +local function big(b) if b<0x10000 then return char(b/256,b%256) else @@ -4641,27 +5031,29 @@ local function big(c) return char(b1/256,b1%256,b2/256,b2%256) end end -local _,l_remap=utf.remapper(little) -local _,b_remap=utf.remapper(big) -function utf.utf8_to_utf16_be(str,nobom) +local l_remap=Cs((p_utf8byte/little+P(1)/"")^0) +local b_remap=Cs((p_utf8byte/big+P(1)/"")^0) +local function utf8_to_utf16_be(str,nobom) if nobom then return lpegmatch(b_remap,str) else return char(254,255)..lpegmatch(b_remap,str) end end -function utf.utf8_to_utf16_le(str,nobom) +local function utf8_to_utf16_le(str,nobom) if nobom then return lpegmatch(l_remap,str) else return char(255,254)..lpegmatch(l_remap,str) end end +utf.utf8_to_utf16_be=utf8_to_utf16_be +utf.utf8_to_utf16_le=utf8_to_utf16_le function utf.utf8_to_utf16(str,littleendian,nobom) if littleendian then - return utf.utf8_to_utf16_le(str,nobom) + return utf8_to_utf16_le(str,nobom) else - return utf.utf8_to_utf16_be(str,nobom) + return utf8_to_utf16_be(str,nobom) end end local pattern=Cs ( @@ -4677,16 +5069,16 @@ function utf.xstring(s) return format("0x%05X",type(s)=="number" and s or utfbyte(s)) end function utf.toeight(str) - if not str then + if not str or str=="" then return nil end local utftype=lpegmatch(p_utfstricttype,str) if utftype=="utf-8" then - return sub(str,4) - elseif utftype=="utf-16-le" then - return utf16_to_utf8_le(str) + return sub(str,4) elseif utftype=="utf-16-be" then - return utf16_to_utf8_ne(str) + return utf16_to_utf8_be(str) + elseif utftype=="utf-16-le" then + return utf16_to_utf8_le(str) else return str end @@ -4765,7 +5157,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-math"] = package.loaded["l-math"] or true --- original size: 915, stripped down to: 836 +-- original size: 974, stripped down to: 890 if not modules then modules={} end modules ['l-math']={ version=1.001, @@ -4775,6 +5167,9 @@ if not modules then modules={} end modules ['l-math']={ license="see context related readme files" } local floor,sin,cos,tan=math.floor,math.sin,math.cos,math.tan +if not math.ceiling then + math.ceiling=math.ceil +end if not math.round then function math.round(x) return floor(x+0.5) end end @@ -4802,7 +5197,7 @@ do -- create closure to overcome 200 locals limit package.loaded["util-str"] = package.loaded["util-str"] or true --- original size: 26857, stripped down to: 15062 +-- original size: 34503, stripped down to: 18933 if not modules then modules={} end modules ['util-str']={ version=1.001, @@ -4821,25 +5216,43 @@ local unpack,concat=table.unpack,table.concat local P,V,C,S,R,Ct,Cs,Cp,Carg,Cc=lpeg.P,lpeg.V,lpeg.C,lpeg.S,lpeg.R,lpeg.Ct,lpeg.Cs,lpeg.Cp,lpeg.Carg,lpeg.Cc local patterns,lpegmatch=lpeg.patterns,lpeg.match local utfchar,utfbyte=utf.char,utf.byte -local loadstripped=_LUAVERSION<5.2 and load or function(str) - return load(dump(load(str),true)) +local loadstripped=nil +if _LUAVERSION<5.2 then + loadstripped=function(str,shortcuts) + return load(str) + end +else + loadstripped=function(str,shortcuts) + if shortcuts then + return load(dump(load(str),true),nil,nil,shortcuts) + else + return load(dump(load(str),true)) + end + end end if not number then number={} end local stripper=patterns.stripzeros +local newline=patterns.newline +local endofstring=patterns.endofstring +local whitespace=patterns.whitespace +local spacer=patterns.spacer +local spaceortab=patterns.spaceortab local function points(n) + n=tonumber(n) return (not n or n==0) and "0pt" or lpegmatch(stripper,format("%.5fpt",n/65536)) end local function basepoints(n) + n=tonumber(n) return (not n or n==0) and "0bp" or lpegmatch(stripper,format("%.5fbp",n*(7200/7227)/65536)) end number.points=points number.basepoints=basepoints -local rubish=patterns.spaceortab^0*patterns.newline -local anyrubish=patterns.spaceortab+patterns.newline +local rubish=spaceortab^0*newline +local anyrubish=spaceortab+newline local anything=patterns.anything -local stripped=(patterns.spaceortab^1/"")*patterns.newline +local stripped=(spaceortab^1/"")*newline local leading=rubish^0/"" -local trailing=(anyrubish^1*patterns.endofstring)/"" +local trailing=(anyrubish^1*endofstring)/"" local redundant=rubish^3/"\n" local pattern=Cs(leading*(trailing+redundant+stripped+anything)^0) function strings.collapsecrlf(str) @@ -4885,18 +5298,44 @@ local pattern=Carg(1)/function(t) else return "" end - end+patterns.newline*Cp()/function(position) + end+newline*Cp()/function(position) extra,start=0,position end+patterns.anything )^1) function strings.tabtospace(str,tab) return lpegmatch(pattern,str,1,tab or 7) end -function strings.striplong(str) - str=gsub(str,"^%s*","") - str=gsub(str,"[\n\r]+ *","\n") - return str +local space=spacer^0 +local nospace=space/"" +local endofline=nospace*newline +local stripend=(whitespace^1*endofstring)/"" +local normalline=(nospace*((1-space*(newline+endofstring))^1)*nospace) +local stripempty=endofline^1/"" +local normalempty=endofline^1 +local singleempty=endofline*(endofline^0/"") +local doubleempty=endofline*endofline^-1*(endofline^0/"") +local stripstart=stripempty^0 +local p_prune_normal=Cs (stripstart*(stripend+normalline+normalempty )^0 ) +local p_prune_collapse=Cs (stripstart*(stripend+normalline+doubleempty )^0 ) +local p_prune_noempty=Cs (stripstart*(stripend+normalline+singleempty )^0 ) +local p_retain_normal=Cs ((normalline+normalempty )^0 ) +local p_retain_collapse=Cs ((normalline+doubleempty )^0 ) +local p_retain_noempty=Cs ((normalline+singleempty )^0 ) +local striplinepatterns={ + ["prune"]=p_prune_normal, + ["prune and collapse"]=p_prune_collapse, + ["prune and no empty"]=p_prune_noempty, + ["retain"]=p_retain_normal, + ["retain and collapse"]=p_retain_collapse, + ["retain and no empty"]=p_retain_noempty, + ["collapse"]=patterns.collapser, +} +setmetatable(striplinepatterns,{ __index=function(t,k) return p_prune_collapse end }) +strings.striplinepatterns=striplinepatterns +function strings.striplines(str,how) + return str and lpegmatch(striplinepatterns[how],str) or str end +strings.striplong=strings.striplines function strings.nice(str) str=gsub(str,"[:%-+_]+"," ") return str @@ -4934,10 +5373,10 @@ string.tracedchars=tracedchars strings.tracers=tracedchars function string.tracedchar(b) if type(b)=="number" then - return tracedchars[b] or (utfchar(b).." (U+"..format('%05X',b)..")") + return tracedchars[b] or (utfchar(b).." (U+"..format("%05X",b)..")") else local c=utfbyte(b) - return tracedchars[c] or (b.." (U+"..format('%05X',c)..")") + return tracedchars[c] or (b.." (U+"..(c and format("%05X",c) or "?????")..")") end end function number.signed(i) @@ -4972,31 +5411,58 @@ function number.sparseexponent(f,n) end return tostring(n) end -local preamble=[[ -local type = type -local tostring = tostring -local tonumber = tonumber -local format = string.format -local concat = table.concat -local signed = number.signed -local points = number.points -local basepoints = number.basepoints -local utfchar = utf.char -local utfbyte = utf.byte -local lpegmatch = lpeg.match -local nspaces = string.nspaces -local tracedchar = string.tracedchar -local autosingle = string.autosingle -local autodouble = string.autodouble -local sequenced = table.sequenced -local formattednumber = number.formatted -local sparseexponent = number.sparseexponent -]] local template=[[ %s %s return function(%s) return %s end ]] +local preamble,environment="",{} +if _LUAVERSION<5.2 then + preamble=[[ +local lpeg=lpeg +local type=type +local tostring=tostring +local tonumber=tonumber +local format=string.format +local concat=table.concat +local signed=number.signed +local points=number.points +local basepoints= number.basepoints +local utfchar=utf.char +local utfbyte=utf.byte +local lpegmatch=lpeg.match +local nspaces=string.nspaces +local tracedchar=string.tracedchar +local autosingle=string.autosingle +local autodouble=string.autodouble +local sequenced=table.sequenced +local formattednumber=number.formatted +local sparseexponent=number.sparseexponent + ]] +else + environment={ + global=global or _G, + lpeg=lpeg, + type=type, + tostring=tostring, + tonumber=tonumber, + format=string.format, + concat=table.concat, + signed=number.signed, + points=number.points, + basepoints=number.basepoints, + utfchar=utf.char, + utfbyte=utf.byte, + lpegmatch=lpeg.match, + nspaces=string.nspaces, + tracedchar=string.tracedchar, + autosingle=string.autosingle, + autodouble=string.autodouble, + sequenced=table.sequenced, + formattednumber=number.formatted, + sparseexponent=number.sparseexponent, + } +end local arguments={ "a1" } setmetatable(arguments,{ __index=function(t,k) local v=t[k-1]..",a"..k @@ -5035,7 +5501,7 @@ local format_i=function(f) if f and f~="" then return format("format('%%%si',a%s)",f,n) else - return format("format('%%i',a%s)",n) + return format("format('%%i',a%s)",n) end end local format_d=format_i @@ -5047,6 +5513,14 @@ local format_f=function(f) n=n+1 return format("format('%%%sf',a%s)",f,n) end +local format_F=function(f) + n=n+1 + if not f or f=="" then + return format("(((a%s > -0.0000000005 and a%s < 0.0000000005) and '0') or format((a%s %% 1 == 0) and '%%i' or '%%.9f',a%s))",n,n,n,n) + else + return format("format((a%s %% 1 == 0) and '%%i' or '%%%sf',a%s)",n,f,n) + end +end local format_g=function(f) n=n+1 return format("format('%%%sg',a%s)",f,n) @@ -5261,7 +5735,7 @@ local builder=Cs { "start", ( P("%")/""*( V("!") -+V("s")+V("q")+V("i")+V("d")+V("f")+V("g")+V("G")+V("e")+V("E")+V("x")+V("X")+V("o") ++V("s")+V("q")+V("i")+V("d")+V("f")+V("F")+V("g")+V("G")+V("e")+V("E")+V("x")+V("X")+V("o") +V("c")+V("C")+V("S") +V("Q") +V("N") @@ -5272,7 +5746,6 @@ local builder=Cs { "start", +V("j")+V("J") +V("m")+V("M") +V("z") -+V("*") )+V("*") )*(P(-1)+Carg(1)) )^0, @@ -5281,6 +5754,7 @@ local builder=Cs { "start", ["i"]=(prefix_any*P("i"))/format_i, ["d"]=(prefix_any*P("d"))/format_d, ["f"]=(prefix_any*P("f"))/format_f, + ["F"]=(prefix_any*P("F"))/format_F, ["g"]=(prefix_any*P("g"))/format_g, ["G"]=(prefix_any*P("G"))/format_G, ["e"]=(prefix_any*P("e"))/format_e, @@ -5315,11 +5789,12 @@ local builder=Cs { "start", ["a"]=(prefix_any*P("a"))/format_a, ["A"]=(prefix_any*P("A"))/format_A, ["*"]=Cs(((1-P("%"))^1+P("%%")/"%%")^1)/format_rest, + ["?"]=Cs(((1-P("%"))^1 )^1)/format_rest, ["!"]=Carg(2)*prefix_any*P("!")*C((1-P("!"))^1)*P("!")/format_extension, } local direct=Cs ( - P("%")/""*Cc([[local format = string.format return function(str) return format("%]])*(S("+- .")+R("09"))^0*S("sqidfgGeExXo")*Cc([[",str) end]])*P(-1) - ) + P("%")*(S("+- .")+R("09"))^0*S("sqidfgGeExXo")*P(-1)/[[local format = string.format return function(str) return format("%0",str) end]] +) local function make(t,str) local f local p @@ -5328,10 +5803,10 @@ local function make(t,str) f=loadstripped(p)() else n=0 - p=lpegmatch(builder,str,1,"..",t._extensions_) + p=lpegmatch(builder,str,1,t._connector_,t._extensions_) if n>0 then p=format(template,preamble,t._preamble_,arguments[n],p) - f=loadstripped(p)() + f=loadstripped(p,t._environment_)() else f=function() return str end end @@ -5343,10 +5818,22 @@ local function use(t,fmt,...) return t[fmt](...) end strings.formatters={} -function strings.formatters.new() - local t={ _extensions_={},_preamble_="",_type_="formatter" } - setmetatable(t,{ __index=make,__call=use }) - return t +if _LUAVERSION<5.2 then + function strings.formatters.new(noconcat) + local t={ _type_="formatter",_connector_=noconcat and "," or "..",_extensions_={},_preamble_=preamble,_environment_={} } + setmetatable(t,{ __index=make,__call=use }) + return t + end +else + function strings.formatters.new(noconcat) + local e={} + for k,v in next,environment do + e[k]=v + end + local t={ _type_="formatter",_connector_=noconcat and "," or "..",_extensions_={},_preamble_="",_environment_=e } + setmetatable(t,{ __index=make,__call=use }) + return t + end end local formatters=strings.formatters.new() string.formatters=formatters @@ -5354,8 +5841,12 @@ string.formatter=function(str,...) return formatters[str](...) end local function add(t,name,template,preamble) if type(t)=="table" and t._type_=="formatter" then t._extensions_[name]=template or "%s" - if preamble then + if type(preamble)=="string" then t._preamble_=preamble.."\n"..t._preamble_ + elseif type(preamble)=="table" then + for k,v in next,preamble do + t._environment_[k]=v + end end end end @@ -5364,9 +5855,28 @@ patterns.xmlescape=Cs((P("<")/"<"+P(">")/">"+P("&")/"&"+P('"')/"" patterns.texescape=Cs((C(S("#$%\\{}"))/"\\%1"+P(1))^0) patterns.luaescape=Cs(((1-S('"\n'))^1+P('"')/'\\"'+P('\n')/'\\n"')^0) patterns.luaquoted=Cs(Cc('"')*((1-S('"\n'))^1+P('"')/'\\"'+P('\n')/'\\n"')^0*Cc('"')) -add(formatters,"xml",[[lpegmatch(xmlescape,%s)]],[[local xmlescape = lpeg.patterns.xmlescape]]) -add(formatters,"tex",[[lpegmatch(texescape,%s)]],[[local texescape = lpeg.patterns.texescape]]) -add(formatters,"lua",[[lpegmatch(luaescape,%s)]],[[local luaescape = lpeg.patterns.luaescape]]) +if _LUAVERSION<5.2 then + add(formatters,"xml",[[lpegmatch(xmlescape,%s)]],"local xmlescape = lpeg.patterns.xmlescape") + add(formatters,"tex",[[lpegmatch(texescape,%s)]],"local texescape = lpeg.patterns.texescape") + add(formatters,"lua",[[lpegmatch(luaescape,%s)]],"local luaescape = lpeg.patterns.luaescape") +else + add(formatters,"xml",[[lpegmatch(xmlescape,%s)]],{ xmlescape=lpeg.patterns.xmlescape }) + add(formatters,"tex",[[lpegmatch(texescape,%s)]],{ texescape=lpeg.patterns.texescape }) + add(formatters,"lua",[[lpegmatch(luaescape,%s)]],{ luaescape=lpeg.patterns.luaescape }) +end +local dquote=patterns.dquote +local equote=patterns.escaped+dquote/'\\"'+1 +local space=patterns.space +local cquote=Cc('"') +local pattern=Cs(dquote*(equote-P(-2))^0*dquote) ++Cs(cquote*(equote-space)^0*space*equote^0*cquote) +function string.optionalquoted(str) + return lpegmatch(pattern,str) or str +end +local pattern=Cs((newline/os.newline+1)^0) +function string.replacenewlines(str) + return lpegmatch(pattern,str) +end end -- of closure @@ -5375,7 +5885,7 @@ do -- create closure to overcome 200 locals limit package.loaded["util-tab"] = package.loaded["util-tab"] or true --- original size: 23952, stripped down to: 16092 +-- original size: 25338, stripped down to: 16247 if not modules then modules={} end modules ['util-tab']={ version=1.001, @@ -5388,7 +5898,7 @@ utilities=utilities or {} utilities.tables=utilities.tables or {} local tables=utilities.tables local format,gmatch,gsub,sub=string.format,string.gmatch,string.gsub,string.sub -local concat,insert,remove=table.concat,table.insert,table.remove +local concat,insert,remove,sort=table.concat,table.insert,table.remove,table.sort local setmetatable,getmetatable,tonumber,tostring=setmetatable,getmetatable,tonumber,tostring local type,next,rawset,tonumber,tostring,load,select=type,next,rawset,tonumber,tostring,load,select local lpegmatch,P,Cs,Cc=lpeg.match,lpeg.P,lpeg.Cs,lpeg.Cc @@ -5396,27 +5906,29 @@ local sortedkeys,sortedpairs=table.sortedkeys,table.sortedpairs local formatters=string.formatters local utftoeight=utf.toeight local splitter=lpeg.tsplitat(".") -function tables.definetable(target,nofirst,nolast) - local composed,shortcut,t=nil,nil,{} +function utilities.tables.definetable(target,nofirst,nolast) + local composed,t=nil,{} local snippets=lpegmatch(splitter,target) for i=1,#snippets-(nolast and 1 or 0) do local name=snippets[i] if composed then - composed=shortcut.."."..name - shortcut=shortcut.."_"..name - t[#t+1]=formatters["local %s = %s if not %s then %s = { } %s = %s end"](shortcut,composed,shortcut,shortcut,composed,shortcut) + composed=composed.."."..name + t[#t+1]=formatters["if not %s then %s = { } end"](composed,composed) else composed=name - shortcut=name if not nofirst then t[#t+1]=formatters["%s = %s or { }"](composed,composed) end end end - if nolast then - composed=shortcut.."."..snippets[#snippets] + if composed then + if nolast then + composed=composed.."."..snippets[#snippets] + end + return concat(t,"\n"),composed + else + return "",target end - return concat(t,"\n"),composed end function tables.definedtable(...) local t=_G @@ -5443,7 +5955,7 @@ function tables.accesstable(target,root) end function tables.migratetable(target,v,root) local t=root or _G - local names=string.split(target,".") + local names=lpegmatch(splitter,target) for i=1,#names-1 do local name=names[i] t[name]=t[name] or {} @@ -5463,6 +5975,15 @@ function tables.removevalue(t,value) end end end +function tables.replacevalue(t,oldvalue,newvalue) + if oldvalue and newvalue then + for i=1,#t do + if t[i]==oldvalue then + t[i]=newvalue + end + end + end +end function tables.insertbeforevalue(t,value,extra) for i=1,#t do if t[i]==extra then @@ -5610,7 +6131,7 @@ local f_ordered_string=formatters["%q,"] local f_ordered_number=formatters["%s,"] local f_ordered_boolean=formatters["%l,"] function table.fastserialize(t,prefix) - local r={ prefix or "return" } + local r={ type(prefix)=="string" and prefix or "return" } local m=1 local function fastserialize(t,outer) local n=#t @@ -5807,7 +6328,8 @@ function table.serialize(root,name,specification) local t local n=1 local function simple_table(t) - if #t>0 then + local nt=#t + if nt>0 then local n=0 for _,v in next,t do n=n+1 @@ -5815,19 +6337,17 @@ function table.serialize(root,name,specification) return nil end end - if n==#t then + if n==nt then local tt={} - local nt=0 - for i=1,#t do + for i=1,nt do local v=t[i] local tv=type(v) - nt=nt+1 if tv=="number" then - tt[nt]=v + tt[i]=v elseif tv=="string" then - tt[nt]=format("%q",v) + tt[i]=format("%q",v) elseif tv=="boolean" then - tt[nt]=v and "true" or "false" + tt[i]=v and "true" or "false" else return nil end @@ -5856,7 +6376,7 @@ function table.serialize(root,name,specification) end depth=depth+1 end - if root and next(root) then + if root and next(root)~=nil then local first=nil local last=0 last=#root @@ -5875,13 +6395,13 @@ function table.serialize(root,name,specification) local v=root[k] local tv=type(v) local tk=type(k) - if first and tk=="number" and k>=first and k<=last then + if first and tk=="number" and k<=last and k>=first then if tv=="number" then n=n+1 t[n]=f_val_num(depth,v) elseif tv=="string" then n=n+1 t[n]=f_val_str(depth,v) elseif tv=="table" then - if not next(v) then + if next(v)==nil then n=n+1 t[n]=f_val_not(depth) else local st=simple_table(v) @@ -5911,13 +6431,13 @@ function table.serialize(root,name,specification) n=n+1 t[n]=f_key_boo_value_str(depth,k,v) end elseif tv=="table" then - if not next(v) then + if next(v)==nil then if tk=="number" then - n=n+1 t[n]=f_key_num_value_not(depth,k,v) + n=n+1 t[n]=f_key_num_value_not(depth,k) elseif tk=="string" then - n=n+1 t[n]=f_key_str_value_not(depth,k,v) + n=n+1 t[n]=f_key_str_value_not(depth,k) elseif tk=="boolean" then - n=n+1 t[n]=f_key_boo_value_not(depth,k,v) + n=n+1 t[n]=f_key_boo_value_not(depth,k) end else local st=simple_table(v) @@ -5969,7 +6489,7 @@ function table.serialize(root,name,specification) local dummy=root._w_h_a_t_e_v_e_r_ root._w_h_a_t_e_v_e_r_=nil end - if next(root) then + if next(root)~=nil then do_serialize(root,name,1,0) end end @@ -6132,7 +6652,7 @@ do -- create closure to overcome 200 locals limit package.loaded["util-prs"] = package.loaded["util-prs"] or true --- original size: 19604, stripped down to: 13998 +-- original size: 21780, stripped down to: 15121 if not modules then modules={} end modules ['util-prs']={ version=1.001, @@ -6154,6 +6674,8 @@ local patterns=parsers.patterns or {} parsers.patterns=patterns local setmetatableindex=table.setmetatableindex local sortedhash=table.sortedhash +local sortedkeys=table.sortedkeys +local tohash=table.tohash local digit=R("09") local space=P(' ') local equal=P("=") @@ -6203,9 +6725,7 @@ patterns.settings_to_hash_a=pattern_a_s patterns.settings_to_hash_b=pattern_b_s patterns.settings_to_hash_c=pattern_c_s function parsers.make_settings_to_hash_pattern(set,how) - if type(str)=="table" then - return set - elseif how=="strict" then + if how=="strict" then return (pattern_c/set)^1 elseif how=="tolerant" then return (pattern_b/set)^1 @@ -6214,7 +6734,9 @@ function parsers.make_settings_to_hash_pattern(set,how) end end function parsers.settings_to_hash(str,existing) - if type(str)=="table" then + if not str or str=="" then + return {} + elseif type(str)=="table" then if existing then for k,v in next,str do existing[k]=v @@ -6223,16 +6745,16 @@ function parsers.settings_to_hash(str,existing) else return str end - elseif str and str~="" then + else hash=existing or {} lpegmatch(pattern_a_s,str) return hash - else - return {} end end function parsers.settings_to_hash_tolerant(str,existing) - if type(str)=="table" then + if not str or str=="" then + return {} + elseif type(str)=="table" then if existing then for k,v in next,str do existing[k]=v @@ -6241,16 +6763,16 @@ function parsers.settings_to_hash_tolerant(str,existing) else return str end - elseif str and str~="" then + else hash=existing or {} lpegmatch(pattern_b_s,str) return hash - else - return {} end end function parsers.settings_to_hash_strict(str,existing) - if type(str)=="table" then + if not str or str=="" then + return nil + elseif type(str)=="table" then if existing then for k,v in next,str do existing[k]=v @@ -6263,8 +6785,6 @@ function parsers.settings_to_hash_strict(str,existing) hash=existing or {} lpegmatch(pattern_c_s,str) return next(hash) and hash - else - return nil end end local separator=comma*space^0 @@ -6272,27 +6792,46 @@ local value=P(lbrace*C((nobrace+nestedbraces)^0)*rbrace)+C((nestedbraces+(1-comm local pattern=spaces*Ct(value*(separator*value)^0) patterns.settings_to_array=pattern function parsers.settings_to_array(str,strict) - if type(str)=="table" then - return str - elseif not str or str=="" then + if not str or str=="" then return {} + elseif type(str)=="table" then + return str elseif strict then - if find(str,"{") then + if find(str,"{",1,true) then return lpegmatch(pattern,str) else return { str } end - elseif find(str,",") then + elseif find(str,",",1,true) then return lpegmatch(pattern,str) else return { str } end end -local separator=space^0*comma*space^0 -local value=P(lbrace*C((nobrace+nestedbraces)^0)*rbrace)+C((nestedbraces+(1-(space^0*(comma+P(-1)))))^0) -local withvalue=Carg(1)*value/function(f,s) return f(s) end -local pattern_a=spaces*Ct(value*(separator*value)^0) -local pattern_b=spaces*withvalue*(separator*withvalue)^0 +local cache_a={} +local cache_b={} +function parsers.groupedsplitat(symbol,withaction) + if not symbol then + symbol="," + end + local pattern=(withaction and cache_b or cache_a)[symbol] + if not pattern then + local symbols=S(symbol) + local separator=space^0*symbols*space^0 + local value=P(lbrace*C((nobrace+nestedbraces)^0)*rbrace)+C((nestedbraces+(1-(space^0*(symbols+P(-1)))))^0) + if withaction then + local withvalue=Carg(1)*value/function(f,s) return f(s) end + pattern=spaces*withvalue*(separator*withvalue)^0 + cache_b[symbol]=pattern + else + pattern=spaces*Ct(value*(separator*value)^0) + cache_a[symbol]=pattern + end + end + return pattern +end +local pattern_a=parsers.groupedsplitat(",",false) +local pattern_b=parsers.groupedsplitat(",",true) function parsers.stripped_settings_to_array(str) if not str or str=="" then return {} @@ -6317,8 +6856,8 @@ function parsers.add_settings_to_array(t,str) end function parsers.hash_to_string(h,separator,yes,no,strict,omit) if h then - local t,tn,s={},0,table.sortedkeys(h) - omit=omit and table.tohash(omit) + local t,tn,s={},0,sortedkeys(h) + omit=omit and tohash(omit) for i=1,#s do local key=s[i] if not omit or not omit[key] then @@ -6354,12 +6893,9 @@ function parsers.array_to_string(a,separator) return "" end end -function parsers.settings_to_set(str,t) - t=t or {} - for s in gmatch(str,"[^, ]+") do - t[s]=true - end - return t +local pattern=Cf(Ct("")*Cg(C((1-S(", "))^1)*S(", ")^0*Cc(true))^1,rawset) +function utilities.parsers.settings_to_set(str,t) + return str and lpegmatch(pattern,str) or {} end function parsers.simple_hash_to_string(h,separator) local t,tn={},0 @@ -6371,12 +6907,16 @@ function parsers.simple_hash_to_string(h,separator) end return concat(t,separator or ",") end -local str=C((1-whitespace-equal)^1) +local str=Cs(lpegpatterns.unquoted)+C((1-whitespace-equal)^1) local setting=Cf(Carg(1)*(whitespace^0*Cg(str*whitespace^0*(equal*whitespace^0*str+Cc(""))))^1,rawset) local splitter=setting^1 function utilities.parsers.options_to_hash(str,target) return str and lpegmatch(splitter,str,1,target or {}) or {} end +local splitter=lpeg.tsplitat(" ") +function utilities.parsers.options_to_array(str) + return str and lpegmatch(splitter,str) or {} +end local value=P(lbrace*C((nobrace+nestedbraces)^0)*rbrace)+C(digit^1*lparent*(noparent+nestedparents)^1*rparent)+C((nestedbraces+(1-comma))^1) local pattern_a=spaces*Ct(value*(separator*value)^0) local function repeater(n,str) @@ -6463,7 +7003,7 @@ function parsers.keq_to_hash(str) end local defaultspecification={ separator=",",quote='"' } function parsers.csvsplitter(specification) - specification=specification and table.setmetatableindex(specification,defaultspecification) or defaultspecification + specification=specification and setmetatableindex(specification,defaultspecification) or defaultspecification local separator=specification.separator local quotechar=specification.quote local separator=S(separator~="" and separator or ",") @@ -6487,7 +7027,7 @@ function parsers.csvsplitter(specification) end end function parsers.rfc4180splitter(specification) - specification=specification and table.setmetatableindex(specification,defaultspecification) or defaultspecification + specification=specification and setmetatableindex(specification,defaultspecification) or defaultspecification local separator=specification.separator local quotechar=P(specification.quote) local dquotechar=quotechar*quotechar @@ -6498,7 +7038,7 @@ function parsers.rfc4180splitter(specification) local field=escaped+non_escaped+Cc("") local record=Ct(field*(separator*field)^1) local headerline=record*Cp() - local wholeblob=Ct((newline^-1*record)^0) + local wholeblob=Ct((newline^(specification.strict and -1 or 1)*record)^0) return function(data,getheader) if getheader then local header,position=lpegmatch(headerline,data) @@ -6535,20 +7075,20 @@ function parsers.stepper(str,n,action) lpegmatch(stepper,str,1,n,action or print) end end -local pattern_math=Cs((P("%")/"\\percent "+P("^")*Cc("{")*lpegpatterns.integer*Cc("}")+P(1))^0) -local pattern_text=Cs((P("%")/"\\percent "+(P("^")/"\\high")*Cc("{")*lpegpatterns.integer*Cc("}")+P(1))^0) +local pattern_math=Cs((P("%")/"\\percent "+P("^")*Cc("{")*lpegpatterns.integer*Cc("}")+anything)^0) +local pattern_text=Cs((P("%")/"\\percent "+(P("^")/"\\high")*Cc("{")*lpegpatterns.integer*Cc("}")+anything)^0) patterns.unittotex=pattern function parsers.unittotex(str,textmode) return lpegmatch(textmode and pattern_text or pattern_math,str) end -local pattern=Cs((P("^")/"<sup>"*lpegpatterns.integer*Cc("</sup>")+P(1))^0) +local pattern=Cs((P("^")/"<sup>"*lpegpatterns.integer*Cc("</sup>")+anything)^0) function parsers.unittoxml(str) return lpegmatch(pattern,str) end local cache={} -local spaces=lpeg.patterns.space^0 +local spaces=lpegpatterns.space^0 local dummy=function() end -table.setmetatableindex(cache,function(t,k) +setmetatableindex(cache,function(t,k) local separator=P(k) local value=(1-separator)^0 local pattern=spaces*C(value)*separator^0*Cp() @@ -6613,6 +7153,18 @@ function utilities.parsers.runtime(time) local seconds=mod(time,60) return days,hours,minutes,seconds end +local spacing=whitespace^0 +local apply=P("->") +local method=C((1-apply)^1) +local token=lbrace*C((1-rbrace)^1)*rbrace+C(anything^1) +local pattern=spacing*(method*spacing*apply+Carg(1))*spacing*token +function utilities.parsers.splitmethod(str,default) + if str then + return lpegmatch(pattern,str,1,default or false) + else + return default or false,"" + end +end end -- of closure @@ -6702,7 +7254,7 @@ do -- create closure to overcome 200 locals limit package.loaded["trac-set"] = package.loaded["trac-set"] or true --- original size: 12365, stripped down to: 8799 +-- original size: 12482, stripped down to: 8864 if not modules then modules={} end modules ['trac-set']={ version=1.001, @@ -6730,7 +7282,7 @@ function setters.initialize(filename,name,values) local data=setter.data if data then for key,newvalue in next,values do - local newvalue=is_boolean(newvalue,newvalue) + local newvalue=is_boolean(newvalue,newvalue,true) local functions=data[key] if functions then local oldvalue=functions.value @@ -6784,7 +7336,7 @@ local function set(t,what,newvalue) elseif not value then value=false else - value=is_boolean(value,value) + value=is_boolean(value,value,true) end w=topattern(w,true,true) for name,functions in next,data do @@ -6923,6 +7475,7 @@ function setters.new(name) report=function(...) setters.report (setter,...) end, enable=function(...) enable (setter,...) end, disable=function(...) disable (setter,...) end, + reset=function(...) reset (setter,...) end, register=function(...) register(setter,...) end, list=function(...) list (setter,...) end, show=function(...) show (setter,...) end, @@ -7014,7 +7567,7 @@ do -- create closure to overcome 200 locals limit package.loaded["trac-log"] = package.loaded["trac-log"] or true --- original size: 25391, stripped down to: 16561 +-- original size: 29359, stripped down to: 20483 if not modules then modules={} end modules ['trac-log']={ version=1.001, @@ -7023,15 +7576,18 @@ if not modules then modules={} end modules ['trac-log']={ copyright="PRAGMA ADE / ConTeXt Development Team", license="see context related readme files" } +local next,type,select,print=next,type,select,print 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 topattern=string.topattern -local next,type,select=next,type,select local utfchar=utf.char +local datetime=os.date +local openfile=io.open local setmetatableindex=table.setmetatableindex local formatters=string.formatters local texgetcount=tex and tex.getcount +local variant="default" logs=logs or {} local logs=logs local moreinfo=[[ @@ -7041,32 +7597,122 @@ 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 ]] -utilities.strings.formatters.add ( +formatters.add ( formatters,"unichr", [["U+" .. format("%%05X",%s) .. " (" .. utfchar(%s) .. ")"]] ) -utilities.strings.formatters.add ( +formatters.add ( formatters,"chruni", [[utfchar(%s) .. " (U+" .. format("%%05X",%s) .. ")"]] ) 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,setlogfile,settimedlog,setprocessor,setformatters +local direct,subdirect,writer,pushtarget,poptarget,setlogfile,settimedlog,setprocessor,setformatters,newline if tex and (tex.jobname or tex.formatname) then - local valueiskey={ __index=function(t,k) t[k]=k return k end } - local target="term and log" + local function useluawrites() + local texio_write_nl=texio.write_nl + local texio_write=texio.write + local io_write=io.write + write_nl=function(target,...) + if not io_write then + io_write=io.write + end + if target=="term and log" then + texio_write_nl("log",...) + texio_write_nl("term","") + io_write(...) + elseif target=="log" then + texio_write_nl("log",...) + elseif target=="term" then + texio_write_nl("term","") + io_write(...) + elseif target~="none" then + texio_write_nl("log",target,...) + texio_write_nl("term","") + io_write(target,...) + end + end + write=function(target,...) + if not io_write then + io_write=io.write + end + if target=="term and log" then + texio_write("log",...) + io_write(...) + elseif target=="log" then + texio_write("log",...) + elseif target=="term" then + io_write(...) + elseif target~="none" then + texio_write("log",target,...) + io_write(target,...) + end + end + texio.write=write + texio.write_nl=write_nl + useluawrites=ignore + end + local whereto="both" + local target=nil + local targets=nil + local formats=table.setmetatableindex("self") + local translations=table.setmetatableindex("self") + local report_yes,subreport_yes,direct_yes,subdirect_yes,status_yes + local report_nop,subreport_nop,direct_nop,subdirect_nop,status_nop + local variants={ + default={ + formats={ + report_yes=formatters["%-15s > %s\n"], + report_nop=formatters["%-15s >\n"], + direct_yes=formatters["%-15s > %s"], + direct_nop=formatters["%-15s >"], + subreport_yes=formatters["%-15s > %s > %s\n"], + subreport_nop=formatters["%-15s > %s >\n"], + subdirect_yes=formatters["%-15s > %s > %s"], + subdirect_nop=formatters["%-15s > %s >"], + status_yes=formatters["%-15s : %s\n"], + status_nop=formatters["%-15s :\n"], + }, + targets={ + logfile="log", + log="log", + file="log", + console="term", + terminal="term", + both="term and log", + }, + }, + ansi={ + formats={ + report_yes=formatters["[0;33m%-15s [0;1m>[0m %s\n"], + report_nop=formatters["[0;33m%-15s [0;1m>[0m\n"], + direct_yes=formatters["[0;33m%-15s [0;1m>[0m %s"], + direct_nop=formatters["[0;33m%-15s [0;1m>[0m"], + subreport_yes=formatters["[0;33m%-15s [0;1m>[0;35m %s [0;1m>[0m %s\n"], + subreport_nop=formatters["[0;33m%-15s [0;1m>[0;35m %s [0;1m>[0m\n"], + subdirect_yes=formatters["[0;33m%-15s [0;1m>[0;35m %s [0;1m>[0m %s"], + subdirect_nop=formatters["[0;33m%-15s [0;1m>[0;35m %s [0;1m>[0m"], + status_yes=formatters["[0;33m%-15s [0;1m:[0m %s\n"], + status_nop=formatters["[0;33m%-15s [0;1m:[0m\n"], + }, + targets={ + logfile="none", + log="none", + file="none", + console="term", + terminal="term", + both="term", + }, + } + } logs.flush=io.flush - local formats={} setmetatable(formats,valueiskey) - local translations={} setmetatable(translations,valueiskey) writer=function(...) write_nl(target,...) end newline=function() write_nl(target,"\n") end - local report_yes=formatters["%-15s > %s\n"] - local report_nop=formatters["%-15s >\n"] report=function(a,b,c,...) if c then write_nl(target,report_yes(translations[a],formatters[formats[b]](c,...))) @@ -7078,8 +7724,6 @@ if tex and (tex.jobname or tex.formatname) then write_nl(target,"\n") end end - local direct_yes=formatters["%-15s > %s"] - local direct_nop=formatters["%-15s >"] direct=function(a,b,c,...) if c then return direct_yes(translations[a],formatters[formats[b]](c,...)) @@ -7091,8 +7735,6 @@ if tex and (tex.jobname or tex.formatname) then return "" end end - local subreport_yes=formatters["%-15s > %s > %s\n"] - local subreport_nop=formatters["%-15s > %s >\n"] subreport=function(a,s,b,c,...) if c then write_nl(target,subreport_yes(translations[a],translations[s],formatters[formats[b]](c,...))) @@ -7104,8 +7746,6 @@ if tex and (tex.jobname or tex.formatname) then write_nl(target,"\n") end end - local subdirect_yes=formatters["%-15s > %s > %s"] - local subdirect_nop=formatters["%-15s > %s >"] subdirect=function(a,s,b,c,...) if c then return subdirect_yes(translations[a],translations[s],formatters[formats[b]](c,...)) @@ -7117,8 +7757,6 @@ if tex and (tex.jobname or tex.formatname) then return "" end end - local status_yes=formatters["%-15s : %s\n"] - local status_nop=formatters["%-15s :\n"] status=function(a,b,c,...) if c then write_nl(target,status_yes(translations[a],formatters[formats[b]](c,...))) @@ -7130,16 +7768,13 @@ if tex and (tex.jobname or tex.formatname) then 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 + settarget=function(askedwhereto) + whereto=askedwhereto or whereto or "both" + target=targets[whereto] + if not target then + whereto="both" + target=targets[whereto] + end if target=="term" or target=="term and log" then logs.flush=io.flush else @@ -7168,21 +7803,74 @@ if tex and (tex.jobname or tex.formatname) then writeline(target,f(...)) end end - setformatters=function(f) - report_yes=f.report_yes or report_yes - report_nop=f.report_nop or report_nop - subreport_yes=f.subreport_yes or subreport_yes - subreport_nop=f.subreport_nop or subreport_nop - direct_yes=f.direct_yes or direct_yes - direct_nop=f.direct_nop or direct_nop - subdirect_yes=f.subdirect_yes or subdirect_yes - subdirect_nop=f.subdirect_nop or subdirect_nop - status_yes=f.status_yes or status_yes - status_nop=f.status_nop or status_nop - end + setformatters=function(specification) + local t=nil + local f=nil + local d=variants.default + if not specification then + elseif type(specification)=="table" then + t=specification.targets + f=specification.formats or specification + else + local v=variants[specification] + if v then + t=v.targets + f=v.formats + variant=specification + end + end + targets=t or d.targets + target=targets[whereto] or target + if f then + d=d.formats + else + f=d.formats + d=f + end + setmetatableindex(f,d) + report_yes=f.report_yes + report_nop=f.report_nop + subreport_yes=f.subreport_yes + subreport_nop=f.subreport_nop + direct_yes=f.direct_yes + direct_nop=f.direct_nop + subdirect_yes=f.subdirect_yes + subdirect_nop=f.subdirect_nop + status_yes=f.status_yes + status_nop=f.status_nop + if variant=="ansi" then + useluawrites() + end + settarget(whereto) + end + setformatters(variant) setlogfile=ignore settimedlog=ignore else + local report_yes,subreport_yes,status_yes + local report_nop,subreport_nop,status_nop + local variants={ + default={ + formats={ + report_yes=formatters["%-15s | %s"], + report_nop=formatters["%-15s |"], + subreport_yes=formatters["%-15s | %s | %s"], + subreport_nop=formatters["%-15s | %s |"], + status_yes=formatters["%-15s : %s\n"], + status_nop=formatters["%-15s :\n"], + }, + }, + ansi={ + formats={ + report_yes=formatters["[0;32m%-15s [0;1m|[0m %s"], + report_nop=formatters["[0;32m%-15s [0;1m|[0m"], + subreport_yes=formatters["[0;32m%-15s [0;1m|[0;31m %s [0;1m|[0m %s"], + subreport_nop=formatters["[0;32m%-15s [0;1m|[0;31m %s [0;1m|[0m"], + status_yes=formatters["[0;32m%-15s [0;1m:[0m %s\n"], + status_nop=formatters["[0;32m%-15s [0;1m:[0m\n"], + }, + }, + } logs.flush=ignore writer=function(s) write_nl(s) @@ -7190,8 +7878,6 @@ else newline=function() write_nl("\n") end - local report_yes=formatters["%-15s | %s"] - local report_nop=formatters["%-15s |"] report=function(a,b,c,...) if c then write_nl(report_yes(a,formatters[b](c,...))) @@ -7203,8 +7889,6 @@ else write_nl("") end end - local subreport_yes=formatters["%-15s | %s | %s"] - local subreport_nop=formatters["%-15s | %s |"] subreport=function(a,sub,b,c,...) if c then write_nl(subreport_yes(a,sub,formatters[b](c,...))) @@ -7216,8 +7900,6 @@ else write_nl("") end end - local status_yes=formatters["%-15s : %s\n"] - local status_nop=formatters["%-15s :\n"] status=function(a,b,c,...) if c then write_nl(status_yes(a,formatters[b](c,...))) @@ -7242,14 +7924,34 @@ else writeline(f(s)) end end - setformatters=function(f) - report_yes=f.report_yes or report_yes - report_nop=f.report_nop or report_nop - subreport_yes=f.subreport_yes or subreport_yes - subreport_nop=f.subreport_nop or subreport_nop - status_yes=f.status_yes or status_yes - status_nop=f.status_nop or status_nop - end + setformatters=function(specification) + local f=nil + local d=variants.default + if specification then + if type(specification)=="table" then + f=specification.formats or specification + else + local v=variants[specification] + if v then + f=v.formats + end + end + end + if f then + d=d.formats + else + f=d.formats + d=f + end + setmetatableindex(f,d) + report_yes=f.report_yes + report_nop=f.report_nop + subreport_yes=f.subreport_yes + subreport_nop=f.subreport_nop + status_yes=f.status_yes + status_nop=f.status_nop + end + setformatters(variant) setlogfile=function(name,keepopen) if name and name~="" then local localtime=os.localtime @@ -7368,9 +8070,10 @@ local function setblocked(category,value) v.state=value end else - states=utilities.parsers.settings_to_hash(category) + states=utilities.parsers.settings_to_hash(category,type(states)=="table" and states or nil) for c,_ in next,states do - if data[c] then + local v=data[c] + if v then v.state=value else c=topattern(c,true,true) @@ -7501,13 +8204,13 @@ end local simple=logs.reporter("comment") logs.simple=simple logs.simpleline=simple -function logs.setprogram () end -function logs.extendbanner() end -function logs.reportlines () end -function logs.reportbanner() end -function logs.reportline () end -function logs.simplelines () end -function logs.help () end +logs.setprogram=ignore +logs.extendbanner=ignore +logs.reportlines=ignore +logs.reportbanner=ignore +logs.reportline=ignore +logs.simplelines=ignore +logs.help=ignore local Carg,C,lpegmatch=lpeg.Carg,lpeg.C,lpeg.match local p_newline=lpeg.patterns.newline local linewise=( @@ -7584,10 +8287,11 @@ function logs.application(t) end return t end -function logs.system(whereto,process,jobname,category,...) - local message=formatters["%s %s => %s => %s => %s\r"](os.date("%d/%m/%y %H:%m:%S"),process,jobname,category,format(...)) +local f_syslog=formatters["%s %s => %s => %s => %s\r"] +function logs.system(whereto,process,jobname,category,fmt,arg,...) + local message=f_syslog(datetime("%d/%m/%y %H:%m:%S"),process,jobname,category,arg==nil and fmt or format(fmt,arg,...)) for i=1,10 do - local f=io.open(whereto,"a") + local f=openfile(whereto,"a") if f then f:write(message) f:close() @@ -7649,7 +8353,7 @@ do -- create closure to overcome 200 locals limit package.loaded["trac-inf"] = package.loaded["trac-inf"] or true --- original size: 6501, stripped down to: 5156 +-- original size: 6704, stripped down to: 5343 if not modules then modules={} end modules ['trac-inf']={ version=1.001, @@ -7659,7 +8363,7 @@ if not modules then modules={} end modules ['trac-inf']={ license="see context related readme files" } local type,tonumber,select=type,tonumber,select -local format,lower=string.format,string.lower +local format,lower,find=string.format,string.lower,string.find local concat=table.concat local clock=os.gettimeofday or os.clock local setmetatableindex=table.setmetatableindex @@ -7750,7 +8454,8 @@ function statistics.show() if statistics.enable then local register=statistics.register register("used platform",function() - return format("%s, type: %s, binary subtree: %s",os.platform or "unknown",os.type or "unknown",environment.texos or "unknown") + return format("%s, type: %s, binary subtree: %s", + os.platform or "unknown",os.type or "unknown",environment.texos or "unknown") end) register("luatex banner",function() return lower(status.banner) @@ -7763,14 +8468,23 @@ function statistics.show() return format("%s direct, %s indirect, %s total",total-indirect,indirect,total) end) if jit then - local status={ jit.status() } - if status[1] then - register("luajit status",function() - return concat(status," ",2) - end) - end - end - register("current memory usage",statistics.memused) + local jitstatus={ jit.status() } + if jitstatus[1] then + register("luajit options",concat(jitstatus," ",2)) + end + end + register("lua properties",function() + local list=status.list() + local hashchar=tonumber(list.luatex_hashchars) + local mask=lua.mask or "ascii" + return format("engine: %s, used memory: %s, hash type: %s, hash chars: min(%s,40), symbol mask: %s (%s)", + jit and "luajit" or "lua", + statistics.memused(), + list.luatex_hashtype or "default", + hashchar and 2^hashchar or "unknown", + mask, + mask=="utf" and "τεχ" or "tex") + end) register("runtime",statistics.runtime) logs.newline() for i=1,#statusinfo do @@ -7812,15 +8526,6 @@ function statistics.tracefunction(base,tag,...) statistics.register(formatters["%s.%s"](tag,name),function() return serialize(stat,"calls") end) end end -commands=commands or {} -function commands.resettimer(name) - resettiming(name or "whatever") - starttiming(name or "whatever") -end -function commands.elapsedtime(name) - stoptiming(name or "whatever") - context(elapsedtime(name or "whatever")) -end end -- of closure @@ -7829,7 +8534,7 @@ do -- create closure to overcome 200 locals limit package.loaded["trac-pro"] = package.loaded["trac-pro"] or true --- original size: 5773, stripped down to: 3453 +-- original size: 5829, stripped down to: 3501 if not modules then modules={} end modules ['trac-pro']={ version=1.001, @@ -7846,14 +8551,16 @@ local namespaces=namespaces local registered={} local function report_index(k,name) if trace_namespaces then - report_system("reference to %a in protected namespace %a: %s",k,name,debug.traceback()) + report_system("reference to %a in protected namespace %a: %s",k,name) + debugger.showtraceback(report_system) else report_system("reference to %a in protected namespace %a",k,name) end end local function report_newindex(k,name) if trace_namespaces then - report_system("assignment to %a in protected namespace %a: %s",k,name,debug.traceback()) + report_system("assignment to %a in protected namespace %a: %s",k,name) + debugger.showtraceback(report_system) else report_system("assignment to %a in protected namespace %a",k,name) end @@ -8104,7 +8811,7 @@ do -- create closure to overcome 200 locals limit package.loaded["util-deb"] = package.loaded["util-deb"] or true --- original size: 3708, stripped down to: 2568 +-- original size: 3898, stripped down to: 2644 if not modules then modules={} end modules ['util-deb']={ version=1.001, @@ -8184,20 +8891,22 @@ end function debugger.disable() debug.sethook() end -function traceback() - local level=1 +local function showtraceback(rep) + local level=2 + local reporter=rep or report while true do - local info=debug.getinfo(level,"Sl") + local info=getinfo(level,"Sl") if not info then break elseif info.what=="C" then - print(format("%3i : C function",level)) + reporter("%2i : %s",level-1,"C function") else - print(format("%3i : [%s]:%d",level,info.short_src,info.currentline)) + reporter("%2i : %s : %s",level-1,info.short_src,info.currentline) end level=level+1 end end +debugger.showtraceback=showtraceback end -- of closure @@ -8383,7 +9092,7 @@ do -- create closure to overcome 200 locals limit package.loaded["util-tpl"] = package.loaded["util-tpl"] or true --- original size: 6251, stripped down to: 3488 +-- original size: 7100, stripped down to: 3978 if not modules then modules={} end modules ['util-tpl']={ version=1.001, @@ -8425,7 +9134,7 @@ local sqlescape=lpeg.replacer { { "\r\n","\\n" }, { "\r","\\n" }, } -local sqlquoted=lpeg.Cs(lpeg.Cc("'")*sqlescape*lpeg.Cc("'")) +local sqlquoted=Cs(Cc("'")*sqlescape*Cc("'")) lpegpatterns.sqlescape=sqlescape lpegpatterns.sqlquoted=sqlquoted local luaescape=lpegpatterns.luaescape @@ -8448,12 +9157,24 @@ local quotedescapers={ local luaescaper=escapers.lua local quotedluaescaper=quotedescapers.lua local function replacekeyunquoted(s,t,how,recurse) - local escaper=how and escapers[how] or luaescaper - return escaper(replacekey(s,t,how,recurse)) + if how==false then + return replacekey(s,t,how,recurse) + else + local escaper=how and escapers[how] or luaescaper + return escaper(replacekey(s,t,how,recurse)) + end end local function replacekeyquoted(s,t,how,recurse) - local escaper=how and quotedescapers[how] or quotedluaescaper - return escaper(replacekey(s,t,how,recurse)) + if how==false then + return replacekey(s,t,how,recurse) + else + local escaper=how and quotedescapers[how] or quotedluaescaper + return escaper(replacekey(s,t,how,recurse)) + end +end +local function replaceoptional(l,m,r,t,how,recurse) + local v=t[l] + return v and v~="" and lpegmatch(replacer,r,1,t,how or "lua",recurse or false) or "" end local single=P("%") local double=P("%%") @@ -8468,11 +9189,16 @@ local nolquoted=lquoted/'' local norquoted=rquoted/'' local nolquotedq=lquotedq/'' local norquotedq=rquotedq/'' -local key=nosingle*((C((1-nosingle )^1)*Carg(1)*Carg(2)*Carg(3))/replacekey )*nosingle -local quoted=nolquotedq*((C((1-norquotedq)^1)*Carg(1)*Carg(2)*Carg(3))/replacekeyquoted )*norquotedq -local unquoted=nolquoted*((C((1-norquoted )^1)*Carg(1)*Carg(2)*Carg(3))/replacekeyunquoted)*norquoted +local noloptional=P("%?")/'' +local noroptional=P("?%")/'' +local nomoptional=P(":")/'' +local args=Carg(1)*Carg(2)*Carg(3) +local key=nosingle*((C((1-nosingle )^1)*args)/replacekey )*nosingle +local quoted=nolquotedq*((C((1-norquotedq )^1)*args)/replacekeyquoted )*norquotedq +local unquoted=nolquoted*((C((1-norquoted )^1)*args)/replacekeyunquoted)*norquoted +local optional=noloptional*((C((1-nomoptional)^1)*nomoptional*C((1-noroptional)^1)*args)/replaceoptional)*noroptional local any=P(1) - replacer=Cs((unquoted+quoted+escape+key+any)^0) + replacer=Cs((unquoted+quoted+escape+optional+key+any)^0) local function replace(str,mapping,how,recurse) if mapping and str then return lpegmatch(replacer,str,1,mapping,how or "lua",recurse or false) or str @@ -8511,7 +9237,7 @@ do -- create closure to overcome 200 locals limit package.loaded["util-env"] = package.loaded["util-env"] or true --- original size: 8807, stripped down to: 5085 +-- original size: 8022, stripped down to: 5038 if not modules then modules={} end modules ['util-env']={ version=1.001, @@ -8522,7 +9248,7 @@ if not modules then modules={} end modules ['util-env']={ } 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 unquoted,quoted,optionalquoted=string.unquoted,string.quoted,string.optionalquoted local concat,insert,remove=table.concat,table.insert,table.remove environment=environment or {} local environment=environment @@ -8635,24 +9361,14 @@ function environment.splitarguments(separator) return before,after end function environment.reconstructcommandline(arg,noquote) + local resolveprefix=resolvers.resolve arg=arg or environment.originalarguments if noquote and #arg==1 then - local a=arg[1] - a=resolvers.resolve(a) - a=unquoted(a) - return a + return unquoted(resolveprefix and resolveprefix(arg[1]) or arg[1]) elseif #arg>0 then local result={} for i=1,#arg do - local a=arg[i] - a=resolvers.resolve(a) - a=unquoted(a) - a=gsub(a,'"','\\"') - if find(a," ") then - result[#result+1]=quoted(a) - else - result[#result+1]=a - end + result[i]=optionalquoted(resolveprefix and resolveprefix(arg[i]) or resolveprefix) end return concat(result," ") else @@ -8708,7 +9424,7 @@ do -- create closure to overcome 200 locals limit package.loaded["luat-env"] = package.loaded["luat-env"] or true --- original size: 5930, stripped down to: 4235 +-- original size: 6174, stripped down to: 4141 if not modules then modules={} end modules ['luat-env']={ version=1.001, @@ -8786,15 +9502,13 @@ function environment.luafilechunk(filename,silent) filename=file.replacesuffix(filename,"lua") local fullname=environment.luafile(filename) if fullname and fullname~="" then - local data=luautilities.loadedluacode(fullname,strippable,filename) - if trace_locating then + local data=luautilities.loadedluacode(fullname,strippable,filename) + if not silent then report_lua("loading file %a %s",fullname,not data and "failed" or "succeeded") - elseif not silent then - texio.write("<",data and "+ " or "- ",fullname,">") end return data else - if trace_locating then + if not silent then report_lua("unknown file %a",filename) end return nil @@ -8863,7 +9577,7 @@ do -- create closure to overcome 200 locals limit package.loaded["lxml-tab"] = package.loaded["lxml-tab"] or true --- original size: 42447, stripped down to: 26589 +-- original size: 45683, stripped down to: 27866 if not modules then modules={} end modules ['lxml-tab']={ version=1.001, @@ -8878,10 +9592,10 @@ if lpeg.setmaxstack then lpeg.setmaxstack(1000) end xml=xml or {} local xml=xml local concat,remove,insert=table.concat,table.remove,table.insert -local type,next,setmetatable,getmetatable,tonumber=type,next,setmetatable,getmetatable,tonumber +local type,next,setmetatable,getmetatable,tonumber,rawset=type,next,setmetatable,getmetatable,tonumber,rawset local lower,find,match,gsub=string.lower,string.find,string.match,string.gsub local utfchar=utf.char -local lpegmatch=lpeg.match +local lpegmatch,lpegpatterns=lpeg.match,lpeg.patterns local P,S,R,C,V,C,Cs=lpeg.P,lpeg.S,lpeg.R,lpeg.C,lpeg.V,lpeg.C,lpeg.Cs local formatters=string.formatters xml.xmlns=xml.xmlns or {} @@ -8976,8 +9690,10 @@ local function add_end(spacing,namespace,tag) top=stack[#stack] if #stack<1 then errorstr=formatters["unable to close %s %s"](tag,xml.checkerror(top,toclose) or "") + report_xml(errorstr) elseif toclose.tg~=tag then errorstr=formatters["unable to close %s with %s %s"](toclose.tg,tag,xml.checkerror(top,toclose) or "") + report_xml(errorstr) end dt=top.dt dt[#dt+1]=toclose @@ -8986,10 +9702,29 @@ local function add_end(spacing,namespace,tag) end end local function add_text(text) + local n=#dt if cleanup and #text>0 then - dt[#dt+1]=cleanup(text) + if n>0 then + local s=dt[n] + if type(s)=="string" then + dt[n]=s..cleanup(text) + else + dt[n+1]=cleanup(text) + end + else + dt[1]=cleanup(text) + end else - dt[#dt+1]=text + if n>0 then + local s=dt[n] + if type(s)=="string" then + dt[n]=s..text + else + dt[n+1]=text + end + else + dt[1]=text + end end end local function add_special(what,spacing,text) @@ -9021,8 +9756,10 @@ local function attribute_specification_error(str) end return str end +local badentity="&error;" +local badentity="&" xml.placeholders={ - unknown_dec_entity=function(str) return str=="" and "&error;" or formatters["&%s;"](str) end, + unknown_dec_entity=function(str) return str=="" and badentity or formatters["&%s;"](str) end, unknown_hex_entity=function(str) return formatters["&#x%s;"](str) end, unknown_any_entity=function(str) return formatters["&#x%s;"](str) end, } @@ -9043,9 +9780,10 @@ local function fromdec(s) return formatters["d:%s"](s),true end end -local rest=(1-P(";"))^0 -local many=P(1)^0 -local parsedentity=P("&")*(P("#x")*(rest/fromhex)+P("#")*(rest/fromdec))*P(";")*P(-1)+(P("#x")*(many/fromhex)+P("#")*(many/fromdec)) +local p_rest=(1-P(";"))^0 +local p_many=P(1)^0 +local p_char=lpegpatterns.utf8character +local parsedentity=P("&")*(P("#x")*(p_rest/fromhex)+P("#")*(p_rest/fromdec))*P(";")*P(-1)+(P("#x")*(p_many/fromhex)+P("#")*(p_many/fromdec)) local predefined_unified={ [38]="&", [42]=""", @@ -9071,7 +9809,9 @@ local privates_u={ local privates_p={} local privates_n={ } -local escaped=utf.remapper(privates_u) +local escaped=utf.remapper(privates_u,"dynamic") +local unprivatized=utf.remapper(privates_p,"dynamic") +xml.unprivatized=unprivatized local function unescaped(s) local p=privates_n[s] if not p then @@ -9084,9 +9824,7 @@ local function unescaped(s) end return p end -local unprivatized=utf.remapper(privates_p) xml.privatetoken=unescaped -xml.unprivatized=unprivatized xml.privatecodes=privates_n local function handle_hex_entity(str) local h=hcache[str] @@ -9181,7 +9919,7 @@ local function handle_any_entity(str) report_xml("keeping entity &%s;",str) end if str=="" then - a="&error;" + a=badentity else a="&"..str..";" end @@ -9209,7 +9947,7 @@ local function handle_any_entity(str) if trace_entities then report_xml("invalid entity &%s;",str) end - a="&error;" + a=badentity acache[str]=a else if trace_entities then @@ -9222,8 +9960,14 @@ local function handle_any_entity(str) return a end end -local function handle_end_entity(chr) - report_xml("error in entity, %a found instead of %a",chr,";") +local function handle_end_entity(str) + report_xml("error in entity, %a found without ending %a",str,";") + return str +end +local function handle_crap_error(chr) + report_xml("error in parsing, unexpected %a found ",chr) + add_text(chr) + return chr end local space=S(' \r\n\t') local open=P('<') @@ -9239,15 +9983,15 @@ local valid=R('az','AZ','09')+S('_-.') local name_yes=C(valid^1)*colon*C(valid^1) local name_nop=C(P(true))*C(valid^1) local name=name_yes+name_nop -local utfbom=lpeg.patterns.utfbom +local utfbom=lpegpatterns.utfbom local spacing=C(space^0) -local anyentitycontent=(1-open-semicolon-space-close)^0 +local anyentitycontent=(1-open-semicolon-space-close-ampersand)^0 local hexentitycontent=R("AF","af","09")^0 local decentitycontent=R("09")^0 local parsedentity=P("#")/""*( P("x")/""*(hexentitycontent/handle_hex_entity)+(decentitycontent/handle_dec_entity) )+(anyentitycontent/handle_any_entity) -local entity=ampersand/""*parsedentity*((semicolon/"")+#(P(1)/handle_end_entity)) +local entity=(ampersand/"")*parsedentity*(semicolon/"")+ampersand*(anyentitycontent/handle_end_entity) local text_unparsed=C((1-open)^1) local text_parsed=Cs(((1-open-ampersand)^1+entity)^1) local somespace=space^1 @@ -9298,16 +10042,20 @@ local instruction=(spacing*begininstruction*someinstruction*endinstruction)/func local comment=(spacing*begincomment*somecomment*endcomment )/function(...) add_special("@cm@",...) end local cdata=(spacing*begincdata*somecdata*endcdata )/function(...) add_special("@cd@",...) end local doctype=(spacing*begindoctype*somedoctype*enddoctype )/function(...) add_special("@dt@",...) end +local crap_parsed=1-beginelement-endelement-emptyelement-begininstruction-begincomment-begincdata-ampersand +local crap_unparsed=1-beginelement-endelement-emptyelement-begininstruction-begincomment-begincdata +local parsedcrap=Cs((crap_parsed^1+entity)^1)/handle_crap_error +local unparsedcrap=Cs((crap_unparsed )^1)/handle_crap_error local trailer=space^0*(text_unparsed/set_message)^0 local grammar_parsed_text=P { "preamble", preamble=utfbom^0*instruction^0*(doctype+comment+instruction)^0*V("parent")*trailer, parent=beginelement*V("children")^0*endelement, - children=parsedtext+V("parent")+emptyelement+comment+cdata+instruction, + children=parsedtext+V("parent")+emptyelement+comment+cdata+instruction+parsedcrap, } local grammar_unparsed_text=P { "preamble", preamble=utfbom^0*instruction^0*(doctype+comment+instruction)^0*V("parent")*trailer, parent=beginelement*V("children")^0*endelement, - children=unparsedtext+V("parent")+emptyelement+comment+cdata+instruction, + children=unparsedtext+V("parent")+emptyelement+comment+cdata+instruction+unparsedcrap, } local function _xmlconvert_(data,settings) settings=settings or {} @@ -9341,7 +10089,6 @@ local function _xmlconvert_(data,settings) errorstr="empty xml file" elseif utfize or resolve then if lpegmatch(grammar_parsed_text,data) then - errorstr="" else errorstr="invalid xml file - parsed text" end @@ -9357,6 +10104,8 @@ local function _xmlconvert_(data,settings) local result if errorstr and errorstr~="" then result={ dt={ { ns="",tg="error",dt={ errorstr },at={},er=true } } } +setmetatable(result,mt) +setmetatable(result.dt[1],mt) setmetatable(stack,mt) local errorhandler=settings.error_handler if errorhandler==false then @@ -9389,8 +10138,11 @@ local function _xmlconvert_(data,settings) end if errorstr and errorstr~="" then result.error=true + else + errorstr=nil end result.statistics={ + errormessage=errorstr, entities={ decimals=dcache, hexadecimals=hcache, @@ -9404,7 +10156,7 @@ local function _xmlconvert_(data,settings) reported_attribute_errors,mt,errorhandler=nil,nil,nil return result end -function xmlconvert(data,settings) +local function xmlconvert(data,settings) local ok,result=pcall(function() return _xmlconvert_(data,settings) end) if ok then return result @@ -9496,14 +10248,17 @@ function xml.checkbom(root) insert(dt,2,"\n" ) end end -local function verbose_element(e,handlers) +local f_attribute=formatters['%s=%q'] +local function verbose_element(e,handlers,escape) local handle=handlers.handle local serialize=handlers.serialize local ens,etg,eat,edt,ern=e.ns,e.tg,e.at,e.dt,e.rn local ats=eat and next(eat) and {} if ats then + local n=0 for k,v in next,eat do - ats[#ats+1]=formatters['%s=%q'](k,escaped(v)) + n=n+1 + ats[n]=f_attribute(k,escaped(v)) end end if ern and trace_entities and ern~=ens then @@ -9588,23 +10343,25 @@ local function verbose_document(e,handlers) end end local function serialize(e,handlers,...) - local initialize=handlers.initialize - local finalize=handlers.finalize - local functions=handlers.functions - if initialize then - local state=initialize(...) - if not state==true then - return state + if e then + local initialize=handlers.initialize + local finalize=handlers.finalize + local functions=handlers.functions + if initialize then + local state=initialize(...) + if not state==true then + return state + end + end + local etg=e.tg + if etg then + (functions[etg] or functions["@el@"])(e,handlers) + else + functions["@dc@"](e,handlers) + end + if finalize then + return finalize() end - end - local etg=e.tg - if etg then - (functions[etg] or functions["@el@"])(e,handlers) - else - functions["@dc@"](e,handlers) - end - if finalize then - return finalize() end end local function xserialize(e,handlers) @@ -9845,7 +10602,7 @@ do -- create closure to overcome 200 locals limit package.loaded["lxml-lpt"] = package.loaded["lxml-lpt"] or true --- original size: 48956, stripped down to: 30516 +-- original size: 48229, stripped down to: 30684 if not modules then modules={} end modules ['lxml-lpt']={ version=1.001, @@ -10230,8 +10987,8 @@ 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")+S("-_:"))^1)*Cc("'])") -lp_fastpos_p=P("+")^0*R("09")^1*P(-1)/"l==%0" -lp_fastpos_n=P("-")*R("09")^1*P(-1)/"(%0<0 and (#list+%0==l))" +local lp_fastpos_p=P("+")^0*R("09")^1*P(-1)/"l==%0" +local lp_fastpos_n=P("-")*R("09")^1*P(-1)/"(%0<0 and (#list+%0==l))" local lp_fastpos=lp_fastpos_n+lp_fastpos_p local lp_reserved=C("and")+C("or")+C("not")+C("div")+C("mod")+C("true")+C("false") local lp_lua_function=Cs((R("az","AZ","__")^1*(P(".")*R("az","AZ","__")^1)^1)*("("))/"%0" @@ -10410,7 +11167,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[i]=(directive and tg) or format("not(%s)",tg) + t[#t+1]=(directive and tg) or format("not(%s)",tg) end if nodetest==false then return format("not(%s)",concat(t,"|")) @@ -10676,7 +11433,6 @@ expressions.print=function(...) print(...) return true end -expressions.contains=find expressions.find=find expressions.upper=upper expressions.lower=lower @@ -10698,6 +11454,9 @@ function expressions.contains(str,pattern) end return false end +function xml.expressions.idstring(str) + return type(str)=="string" and gsub(str,"^#","") or "" +end local function traverse(root,pattern,handle) local collected=applylpath(root,pattern) if collected then @@ -10826,8 +11585,13 @@ function xml.elements(root,pattern,reverse) local collected=applylpath(root,pattern) if not collected then return dummy - elseif reverse then - local c=#collected+1 + end + local n=#collected + if n==0 then + return dummy + end + if reverse then + local c=n+1 return function() if c>1 then c=c-1 @@ -10837,7 +11601,7 @@ function xml.elements(root,pattern,reverse) end end else - local n,c=#collected,0 + local c=0 return function() if c<n then c=c+1 @@ -10852,8 +11616,13 @@ function xml.collected(root,pattern,reverse) local collected=applylpath(root,pattern) if not collected then return dummy - elseif reverse then - local c=#collected+1 + end + local n=#collected + if n==0 then + return dummy + end + if reverse then + local c=n+1 return function() if c>1 then c=c-1 @@ -10861,7 +11630,7 @@ function xml.collected(root,pattern,reverse) end end else - local n,c=#collected,0 + local c=0 return function() if c<n then c=c+1 @@ -10876,7 +11645,7 @@ function xml.inspect(collection,pattern) report_lpath("pattern: %s\n\n%s\n",pattern,xml.tostring(e)) end end -local function split(e) +local function split(e) local dt=e.dt if dt then for i=1,#dt do @@ -10975,7 +11744,7 @@ do -- create closure to overcome 200 locals limit package.loaded["lxml-aux"] = package.loaded["lxml-aux"] or true --- original size: 23804, stripped down to: 16817 +-- original size: 28786, stripped down to: 20578 if not modules then modules={} end modules ['lxml-aux']={ version=1.001, @@ -10985,16 +11754,19 @@ if not modules then modules={} end modules ['lxml-aux']={ license="see context related readme files" } local trace_manipulations=false trackers.register("lxml.manipulations",function(v) trace_manipulations=v end) +local trace_inclusions=false trackers.register("lxml.inclusions",function(v) trace_inclusions=v end) local report_xml=logs.reporter("xml") local xml=xml -local xmlconvert,xmlcopy,xmlname=xml.convert,xml.copy,xml.name +local xmlcopy,xmlname=xml.copy,xml.name local xmlinheritedconvert=xml.inheritedconvert local xmlapplylpath=xml.applylpath local xmlfilter=xml.filter -local type,setmetatable,getmetatable=type,setmetatable,getmetatable +local type,next,setmetatable,getmetatable=type,next,setmetatable,getmetatable local insert,remove,fastcopy,concat=table.insert,table.remove,table.fastcopy,table.concat local gmatch,gsub,format,find,strip=string.gmatch,string.gsub,string.format,string.find,string.strip local utfbyte=utf.byte +local lpegmatch=lpeg.match +local striplinepatterns=utilities.strings.striplinepatterns local function report(what,pattern,c,e) report_xml("%s element %a, root %a, position %a, index %a, pattern %a",what,xmlname(e),xmlname(e.__p__),c,e.ni,pattern) end @@ -11049,13 +11821,15 @@ end function xml.each(root,pattern,handle,reverse) local collected=xmlapplylpath(root,pattern) if collected then - if reverse then - for c=#collected,1,-1 do - handle(collected[c]) - end - else - for c=1,#collected do - handle(collected[c]) + if handle then + if reverse then + for c=#collected,1,-1 do + handle(collected[c]) + end + else + for c=1,#collected do + handle(collected[c]) + end end end return collected @@ -11111,6 +11885,7 @@ local function redo_ni(d) end end end +xml.reindex=redo_ni local function xmltoelement(whatever,root) if not whatever then return nil @@ -11162,8 +11937,16 @@ function xml.delete(root,pattern) report('deleting',pattern,c,e) end local d=p.dt - remove(d,e.ni) - redo_ni(d) + local ni=e.ni + if ni<=#d then + if false then + p.dt[ni]="" + else + remove(d,ni) + redo_ni(d) + end + else + end end end end @@ -11283,28 +12066,40 @@ 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) +local function include(xmldata,pattern,attribute,recursive,loaddata,level) pattern=pattern or 'include' loaddata=loaddata or io.loaddata local collected=xmlapplylpath(xmldata,pattern) if collected then + if not level then + level=1 + end for c=1,#collected do local ek=collected[c] local name=nil local ekdt=ek.dt local ekat=ek.at - local epdt=ek.__p__.dt + local ekrt=ek.__p__ + local epdt=ekrt.dt if not attribute or attribute=="" then name=(type(ekdt)=="table" and ekdt[1]) or ekdt end if not name then for a in gmatch(attribute or "href","([^|]+)") do name=ekat[a] - if name then break end + if name then + break + end + end + end + local data=nil + if name and name~="" then + data=loaddata(name) or "" + if trace_inclusions then + report_xml("including %s bytes from %a at level %s by pattern %a and attribute %a (%srecursing)",#data,name,level,pattern,attribute or "",recursive and "" or "not ") end end - local data=(name and name~="" and loaddata(name)) or "" - if data=="" then + if not data or data=="" then epdt[ek.ni]="" elseif ekat["parse"]=="text" then epdt[ek.ni]=xml.escaped(data) @@ -11314,70 +12109,127 @@ local function include(xmldata,pattern,attribute,recursive,loaddata) epdt[ek.ni]="" else if recursive then - include(xi,pattern,attribute,recursive,loaddata) + include(xi,pattern,attribute,recursive,loaddata,level+1) + end + local child=xml.body(xi) + child.__p__=ekrt + child.__f__=name + epdt[ek.ni]=child + local inclusions=xmldata.settings.inclusions + if inclusions then + inclusions[#inclusions+1]=name + else + xmldata.settings.inclusions={ name } + end + if child.er then + local badinclusions=xmldata.settings.badinclusions + if badinclusions then + badinclusions[#badinclusions+1]=name + else + xmldata.settings.badinclusions={ name } + end end - epdt[ek.ni]=xml.body(xi) end end end end end xml.include=include +function xml.inclusion(e,default) + while e do + local f=e.__f__ + if f then + return f + else + e=e.__p__ + end + end + return default +end +local function getinclusions(key,e,sorted) + while e do + local settings=e.settings + if settings then + local inclusions=settings[key] + if inclusions then + inclusions=table.unique(inclusions) + if sorted then + table.sort(inclusions) + end + return inclusions + else + e=e.__p__ + end + else + e=e.__p__ + end + end +end +function xml.inclusions(e,sorted) + return getinclusions("inclusions",e,sorted) +end +function xml.badinclusions(e,sorted) + return getinclusions("badinclusions",e,sorted) +end +local b_collapser=lpeg.patterns.b_collapser +local m_collapser=lpeg.patterns.m_collapser +local e_collapser=lpeg.patterns.e_collapser +local b_stripper=lpeg.patterns.b_stripper +local m_stripper=lpeg.patterns.m_stripper +local e_stripper=lpeg.patterns.e_stripper +local lpegmatch=lpeg.match 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 n=#edt + if n==0 then + return e + elseif anywhere then + local t={} + local m=0 + for e=1,n do local str=edt[e] if type(str)~="string" then - n=n+1 - t[n]=str + m=m+1 + t[m]=str elseif str~="" then if nolines then - str=gsub(str,"%s+"," ") + str=lpegmatch((n==1 and b_collapser) or (n==m and e_collapser) or m_collapser,str) + else + str=lpegmatch((n==1 and b_stripper) or (n==m and e_stripper) or m_stripper,str) end - str=gsub(str,"^%s*(.-)%s*$","%1") if str~="" then - n=n+1 - t[n]=str + m=m+1 + t[m]=str end end end e.dt=t else - if #edt>0 then - local str=edt[1] - if type(str)~="string" then - elseif str=="" then + local str=edt[1] + if type(str)=="string" then + if str~="" then + str=lpegmatch(nolines and b_collapser or b_stripper,str) + end + if str=="" then remove(edt,1) + n=n-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 + edt[1]=str end end - local nedt=#edt - if nedt>0 then - local str=edt[nedt] - if type(str)~="string" then - elseif str=="" then - remove(edt) - else - if nolines then - str=gsub(str,"%s+"," ") - end - str=gsub(str,"%s+$","") + if n>0 then + str=edt[n] + if type(str)=="string" then if str=="" then remove(edt) else - edt[nedt]=str + str=lpegmatch(nolines and e_collapser or e_stripper,str) + if str=="" then + remove(edt) + else + edt[n]=str + end end end end @@ -11563,8 +12415,8 @@ function xml.finalizers.xml.cdata(collected) end return "" end -function xml.insertcomment(e,str,n) - table.insert(e.dt,n or 1,{ +function xml.insertcomment(e,str,n) + insert(e.dt,n or 1,{ tg="@cm@", ns="", special=true, @@ -11572,7 +12424,25 @@ function xml.insertcomment(e,str,n) dt={ str }, }) end -function xml.setcdata(e,str) +function xml.insertcdata(e,str,n) + insert(e.dt,n or 1,{ + tg="@cd@", + ns="", + special=true, + at={}, + dt={ str }, + }) +end +function xml.setcomment(e,str,n) + e.dt={ { + tg="@cm@", + ns="", + special=true, + at={}, + dt={ str }, + } } +end +function xml.setcdata(e,str) e.dt={ { tg="@cd@", ns="", @@ -11642,7 +12512,7 @@ local function recurse(e,action) for i=1,#edt do local str=edt[i] if type(str)~="string" then - recurse(str,action,recursive) + recurse(str,action) elseif str~="" then edt[i]=action(str) end @@ -11660,6 +12530,65 @@ function helpers.recursetext(collected,action,recursive) end end end +local specials={ + ["@rt@"]="root", + ["@pi@"]="instruction", + ["@cm@"]="comment", + ["@dt@"]="declaration", + ["@cd@"]="cdata", +} +local function convert(x,strip,flat) + local ns=x.ns + local tg=x.tg + local at=x.at + local dt=x.dt + local node=flat and { + [0]=(not x.special and (ns~="" and ns..":"..tg or tg)) or nil, + } or { + _namespace=ns~="" and ns or nil, + _tag=not x.special and tg or nil, + _type=specials[tg] or "_element", + } + if at then + for k,v in next,at do + node[k]=v + end + end + local n=0 + for i=1,#dt do + local di=dt[i] + if type(di)=="table" then + if flat and di.special then + else + di=convert(di,strip,flat) + if di then + n=n+1 + node[n]=di + end + end + elseif strip then + di=lpegmatch(strip,di) + if di~="" then + n=n+1 + node[n]=di + end + else + n=n+1 + node[n]=di + end + end + if next(node) then + return node + end +end +function xml.totable(x,strip,flat) + if type(x)=="table" then + if strip then + strip=striplinepatterns[strip] + end + return convert(x,strip,flat) + end +end end -- of closure @@ -12216,7 +13145,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-ini"] = package.loaded["data-ini"] or true --- original size: 7898, stripped down to: 5501 +-- original size: 11085, stripped down to: 7662 if not modules then modules={} end modules ['data-ini']={ version=1.001, @@ -12225,14 +13154,15 @@ if not modules then modules={} end modules ['data-ini']={ copyright="PRAGMA ADE / ConTeXt Development Team", license="see context related readme files", } +local next,type,getmetatable,rawset=next,type,getmetatable,rawset local gsub,find,gmatch,char=string.gsub,string.find,string.gmatch,string.char -local next,type=next,type local filedirname,filebasename,filejoin=file.dirname,file.basename,file.join +local ostype,osname,osuname,ossetenv,osgetenv=os.type,os.name,os.uname,os.setenv,os.getenv +local P,S,R,C,Cs,Cc,lpegmatch=lpeg.P,lpeg.S,lpeg.R,lpeg.C,lpeg.Cs,lpeg.Cc,lpeg.match 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 report_initialization=logs.reporter("resolvers","initialization") -local ostype,osname,ossetenv,osgetenv=os.type,os.name,os.setenv,os.getenv resolvers=resolvers or {} local resolvers=resolvers texconfig.kpse_init=false @@ -12360,15 +13290,108 @@ if not texroot or texroot=="" then ossetenv('TEXROOT',texroot) end environment.texroot=file.collapsepath(texroot) -if profiler then +if type(profiler)=="table" and not jit then directives.register("system.profile",function() profiler.start("luatex-profile.log") end) end -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 +local prefixes=utilities.storage.allocate() +resolvers.prefixes=prefixes +local resolved={} +local abstract={} +local dynamic={} +function resolvers.resetresolve(str) + resolved,abstract={},{} +end +function resolvers.allprefixes(separator) + local all=table.sortedkeys(prefixes) + if separator then + for i=1,#all do + all[i]=all[i]..":" + end + end + return all +end +local function _resolve_(method,target) + local action=prefixes[method] + if action then + return action(target) + else + return method..":"..target + end +end +function resolvers.unresolve(str) + return abstract[str] or str +end +function resolvers.setdynamic(str) + dynamic[str]=true +end +local pattern=Cs((C(R("az")^2)*P(":")*C((1-S(" \"\';,"))^1)/_resolve_+P(1))^0) +local prefix=C(R("az")^2)*P(":") +local target=C((1-S(" \"\';,"))^1) +local notarget=(#S(";,")+P(-1))*Cc("") +local p_resolve=Cs(((prefix*(target+notarget))/_resolve_+P(1))^0) +local p_simple=prefix*P(-1) +local function resolve(str) + if type(str)=="table" then + local res={} + for i=1,#str do + res[i]=resolve(str[i]) + end + return res + end + local res=resolved[str] + if res then + return res + end + local simple=lpegmatch(p_simple,str) + local action=prefixes[simple] + if action then + local res=action(res) + if not dynamic[simple] then + resolved[simple]=res + abstract[res]=simple + end + return res + end + res=lpegmatch(p_resolve,str) + resolved[str]=res + abstract[res]=str + return res +end +resolvers.resolve=resolve +if type(osuname)=="function" then + for k,v in next,osuname() do + if not prefixes[k] then + prefixes[k]=function() return v end + end + end +end +if ostype=="unix" then + local pattern + local function makepattern(t,k,v) + if t then + rawset(t,k,v) + end + local colon=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) + end + makepattern() + table.setmetatablenewindex(prefixes,makepattern) + function resolvers.repath(str) + return lpegmatch(pattern,str) + end +else + function resolvers.repath(str) + return str + end end @@ -12378,7 +13401,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-exp"] = package.loaded["data-exp"] or true --- original size: 15303, stripped down to: 9716 +-- original size: 17216, stripped down to: 10657 if not modules then modules={} end modules ['data-exp']={ version=1.001, @@ -12392,12 +13415,16 @@ local concat,sort=table.concat,table.sort local lpegmatch,lpegpatterns=lpeg.match,lpeg.patterns local Ct,Cs,Cc,Carg,P,C,S=lpeg.Ct,lpeg.Cs,lpeg.Cc,lpeg.Carg,lpeg.P,lpeg.C,lpeg.S local type,next=type,next +local isdir=lfs.isdir local ostype=os.type -local collapsepath=file.collapsepath +local collapsepath,joinpath,basename=file.collapsepath,file.join,file.basename 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) +local trace_globbing=true trackers.register("resolvers.globbing",function(v) trace_globbing=v end) local report_expansions=logs.reporter("resolvers","expansions") +local report_globbing=logs.reporter("resolvers","globbing") local resolvers=resolvers +local resolveprefix=resolvers.resolve local function f_both(a,b) local t,n={},0 for sb in gmatch(b,"[^,]+") do @@ -12487,35 +13514,27 @@ function resolvers.expandedpathfromlist(pathlist) end return newlist end -local cleanup=lpeg.replacer { - { "!","" }, - { "\\","/" }, -} -function resolvers.cleanpath(str) - local doslashes=(P("\\")/"/"+1)^0 - local donegation=(P("!")/"" )^0 - local homedir=lpegmatch(Cs(donegation*doslashes),environment.homedir or "") - if homedir=="~" 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 not str or find(str,"~") then - return "" - else - return lpegmatch(cleanup,str) +local usedhomedir=nil +local donegation=(P("!")/"" )^0 +local doslashes=(P("\\")/"/"+1)^0 +local function expandedhome() + if not usedhomedir then + usedhomedir=lpegmatch(Cs(donegation*doslashes),environment.homedir or "") + if usedhomedir=="~" or usedhomedir=="" or not isdir(usedhomedir) then + if trace_expansions then + report_expansions("no home dir set, ignoring dependent path using current path") end - end - else - local dohome=((P("~")+P("$HOME"))/homedir)^0 - local cleanup=Cs(donegation*dohome*doslashes) - function resolvers.cleanpath(str) - return str and lpegmatch(cleanup,str) or "" + usedhomedir="." end end - return resolvers.cleanpath(str) + return usedhomedir end -local expandhome=P("~")/"$HOME" +local dohome=((P("~")+P("$HOME")+P("%HOME%"))/expandedhome)^0 +local cleanup=Cs(donegation*dohome*doslashes) +resolvers.cleanpath=function(str) + return str and lpegmatch(cleanup,str) or "" +end +local expandhome=P("~")/"$HOME" local dodouble=P('"')/""*(expandhome+(1-P('"')))^0*P('"')/"" local dosingle=P("'")/""*(expandhome+(1-P("'")))^0*P("'")/"" local dostring=(expandhome+1 )^0 @@ -12567,46 +13586,67 @@ function resolvers.splitpath(str) end function resolvers.joinpath(str) if type(str)=='table' then - return file.joinpath(str) + return joinpath(str) else return str end end local attributes,directory=lfs.attributes,lfs.dir local weird=P(".")^1+lpeg.anywhere(S("~`!#$%^&*()={}[]:;\"\'||<>,?\n\r\t")) +local lessweird=P(".")^1+lpeg.anywhere(S("~`#$%^&*:;\"\'||<>,?\n\r\t")) local timer={} local scanned={} local nofscans=0 local scancache={} -local function scan(files,spec,path,n,m,r) - local full=(path=="" and spec) or (spec..path..'/') +local fullcache={} +local nofsharedscans=0 +local function scan(files,remap,spec,path,n,m,r,onlyone,tolerant) + local full=path=="" and spec or (spec..path..'/') local dirs={} local nofdirs=0 + local pattern=tolerant and lessweird or weird for name in directory(full) do - if not lpegmatch(weird,name) then - local mode=attributes(full..name,'mode') - if mode=='file' then + if not lpegmatch(pattern,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 } + local lower=lower(name) + local paths=files[lower] + if paths then + if onlyone then else - f[#f+1]=path + if type(paths)=="string" then + files[lower]={ paths,path } + else + paths[#paths+1]=path + end + if name~=lower then + local rl=remap[lower] + if not rl then + remap[lower]=name + r=r+1 + elseif trace_globbing and rl~=name then + report_globbing("confusing filename, name: %a, lower: %a, already: %a",name,lower,rl) + end + end end else - files[name]=path - local lower=lower(name) + files[lower]=path if name~=lower then - files["remap:"..lower]=name - r=r+1 + local rl=remap[lower] + if not rl then + remap[lower]=name + r=r+1 + elseif trace_globbing and rl~=name then + report_globbing("confusing filename, name: %a, lower: %a, already: %a",name,lower,rl) + end end end - elseif mode=='directory' then + elseif mode=="directory" then m=m+1 nofdirs=nofdirs+1 if path~="" then - dirs[nofdirs]=path..'/'..name + dirs[nofdirs]=path.."/"..name else dirs[nofdirs]=name end @@ -12616,107 +13656,69 @@ local function scan(files,spec,path,n,m,r) if nofdirs>0 then sort(dirs) for i=1,nofdirs do - files,n,m,r=scan(files,spec,dirs[i],n,m,r) + files,remap,n,m,r=scan(files,remap,spec,dirs[i],n,m,r,onlyonce,tolerant) end end scancache[sub(full,1,-2)]=files - return files,n,m,r + return files,remap,n,m,r end -local fullcache={} -function resolvers.scanfiles(path,branch,usecache) - statistics.starttiming(timer) - local realpath=resolvers.resolve(path) +function resolvers.scanfiles(path,branch,usecache,onlyonce,tolerant) + local realpath=resolveprefix(path) if usecache then - local files=fullcache[realpath] - if files then + local content=fullcache[realpath] + if content then if trace_locating then - report_expansions("using caches scan of path %a, branch %a",path,branch or path) + report_expansions("using cached scan of path %a, branch %a",path,branch or path) end - return files + nofsharedscans=nofsharedscans+1 + return content end end + statistics.starttiming(timer) if trace_locating then report_expansions("scanning path %a, branch %a",path,branch or path) end - local files,n,m,r=scan({},realpath..'/',"",0,0,0) - files.__path__=path - 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 - if usecache then - scanned[#scanned+1]=realpath - fullcache[realpath]=files - end - nofscans=nofscans+1 - statistics.stoptiming(timer) - return files -end -local function simplescan(files,spec,path) - local full=(path=="" and spec) or (spec..path..'/') - local dirs={} - local nofdirs=0 - for name in directory(full) do - if not lpegmatch(weird,name) then - local mode=attributes(full..name,'mode') - if mode=='file' then - if not files[name] then - files[name]=path - end - elseif mode=='directory' then - 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=simplescan(files,spec,dirs[i]) - end - end - return files -end -local simplecache={} -local nofsharedscans=0 -function resolvers.simplescanfiles(path,branch,usecache) - statistics.starttiming(timer) - local realpath=resolvers.resolve(path) - if usecache then - local files=simplecache[realpath] - if not files then - files=scancache[realpath] - if files then - nofsharedscans=nofsharedscans+1 - end + local content + if isdir(realpath) then + local files,remap,n,m,r=scan({},{},realpath..'/',"",0,0,0,onlyonce,tolerant) + content={ + metadata={ + path=path, + files=n, + directories=m, + remappings=r, + }, + files=files, + remap=remap, + } + if trace_locating then + report_expansions("%s files found on %s directories with %s uppercase remappings",n,m,r) end - if files then - if trace_locating then - report_expansions("using caches scan of path %a, branch %a",path,branch or path) - end - return files + else + content={ + metadata={ + path=path, + files=0, + directories=0, + remappings=0, + }, + files={}, + remap={}, + } + if trace_locating then + report_expansions("invalid path %a",realpath) end end - if trace_locating then - report_expansions("scanning path %a, branch %a",path,branch or path) - end - local files=simplescan({},realpath..'/',"") - if trace_locating then - report_expansions("%s files found",table.count(files)) - end if usecache then scanned[#scanned+1]=realpath - simplecache[realpath]=files + fullcache[realpath]=content end nofscans=nofscans+1 statistics.stoptiming(timer) - return files + return content +end +function resolvers.simplescanfiles(path,branch,usecache) + return resolvers.scanfiles(path,branch,usecache,true,true) end function resolvers.scandata() table.sort(scanned) @@ -12727,6 +13729,52 @@ function resolvers.scandata() paths=scanned, } end +function resolvers.get_from_content(content,path,name) + if not content then + return + end + local files=content.files + if not files then + return + end + local remap=content.remap + if not remap then + return + end + if name then + local used=lower(name) + return path,remap[used] or used + else + local name=path + local used=lower(name) + local path=files[used] + if path then + return path,remap[used] or used + end + end +end +local nothing=function() end +function resolvers.filtered_from_content(content,pattern) + if content and type(pattern)=="string" then + local pattern=lower(pattern) + local files=content.files + local remap=content.remap + if files and remap then + local n=next(files) + local function iterator() + while n do + local k=n + n=next(files,k) + if find(k,pattern) then + return files[k],remap and remap[k] or k + end + end + end + return iterator + end + end + return nothing +end end -- of closure @@ -12735,7 +13783,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-env"] = package.loaded["data-env"] or true --- original size: 8769, stripped down to: 6490 +-- original size: 9216, stripped down to: 6798 if not modules then modules={} end modules ['data-env']={ version=1.001, @@ -12753,10 +13801,12 @@ local formats=allocate() local suffixes=allocate() local dangerous=allocate() local suffixmap=allocate() +local usertypes=allocate() resolvers.formats=formats resolvers.suffixes=suffixes resolvers.dangerous=dangerous resolvers.suffixmap=suffixmap +resolvers.usertypes=usertypes local luasuffixes=utilities.lua.suffixes local relations=allocate { core={ @@ -12824,11 +13874,13 @@ local relations=allocate { names={ "mp" }, variable='MPINPUTS', suffixes={ 'mp','mpvi','mpiv','mpii' }, + usertype=true, }, tex={ names={ "tex" }, variable='TEXINPUTS', - suffixes={ 'tex',"mkvi","mkiv","mkii" }, + suffixes={ "tex","mkvi","mkiv","mkii","cld","lfg","xml" }, + usertype=true, }, icc={ names={ "icc","icc profile","icc profiles" }, @@ -12844,6 +13896,7 @@ local relations=allocate { names={ "lua" }, variable='LUAINPUTS', suffixes={ luasuffixes.lua,luasuffixes.luc,luasuffixes.tma,luasuffixes.tmc }, + usertype=true, }, lib={ names={ "lib" }, @@ -12852,11 +13905,15 @@ local relations=allocate { }, bib={ names={ 'bib' }, + variable='BIBINPUTS', suffixes={ 'bib' }, + usertype=true, }, bst={ names={ 'bst' }, + variable='BSTINPUTS', suffixes={ 'bst' }, + usertype=true, }, fontconfig={ names={ 'fontconfig','fontconfig file','fontconfig files' }, @@ -12938,8 +13995,9 @@ function resolvers.updaterelations() for name,relation in next,categories do local rn=relation.names local rv=relation.variable - local rs=relation.suffixes if rn and rv then + local rs=relation.suffixes + local ru=relation.usertype for i=1,#rn do local rni=lower(gsub(rn[i]," ","")) formats[rni]=rv @@ -12951,8 +14009,9 @@ function resolvers.updaterelations() end end end - end - if rs then + if ru then + usertypes[name]=true + end end end end @@ -13003,7 +14062,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-tmp"] = package.loaded["data-tmp"] or true --- original size: 15532, stripped down to: 11648 +-- original size: 15618, stripped down to: 11629 if not modules then modules={} end modules ['data-tmp']={ version=1.100, @@ -13013,7 +14072,7 @@ if not modules then modules={} end modules ['data-tmp']={ license="see context related readme files" } local format,lower,gsub,concat=string.format,string.lower,string.gsub,table.concat -local concat,serialize,serializetofile=table.concat,table.serialize,table.tofile +local concat=table.concat local mkdirs,isdir,isfile=dir.mkdirs,lfs.isdir,lfs.isfile local addsuffix,is_writable,is_readable=file.addsuffix,file.is_writable,file.is_readable local formatters=string.formatters @@ -13022,6 +14081,7 @@ local trace_cache=false trackers.register("resolvers.cache",function(v) trace_ca local report_caches=logs.reporter("resolvers","caches") local report_resolvers=logs.reporter("resolvers","caching") local resolvers=resolvers +local cleanpath=resolvers.cleanpath local directive_cleanup=false directives.register("system.compile.cleanup",function(v) directive_cleanup=v end) local directive_strip=false directives.register("system.compile.strip",function(v) directive_strip=v end) local compile=utilities.lua.compile @@ -13043,7 +14103,7 @@ caches.relocate=false caches.defaults={ "TMPDIR","TEMPDIR","TMP","TEMP","HOME","HOMEPATH" } local writable,readables,usedreadables=nil,{},{} local function identify() - local texmfcaches=resolvers.cleanpathlist("TEXMFCACHE") + local texmfcaches=resolvers.cleanpathlist("TEXMFCACHE") if texmfcaches then for k=1,#texmfcaches do local cachepath=texmfcaches[k] @@ -13281,15 +14341,11 @@ end local saveoptions={ compact=true } 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,serialize(data,true,saveoptions)) + file.savedata(tmaname,table.serialize(data,true,saveoptions)) else - serializetofile(tmaname,data,true,saveoptions) + table.tofile(tmaname,data,true,saveoptions) end utilities.lua.compile(tmaname,tmcname) end @@ -13297,10 +14353,12 @@ local content_state={} function caches.contentstate() return content_state or {} end -function caches.loadcontent(cachename,dataname) - local name=caches.hashed(cachename) - local full,path=caches.getfirstreadablefile(addsuffix(name,luasuffixes.lua),"trees") - local filename=file.join(path,name) +function caches.loadcontent(cachename,dataname,filename) + if not filename then + local name=caches.hashed(cachename) + local full,path=caches.getfirstreadablefile(addsuffix(name,luasuffixes.lua),"trees") + filename=file.join(path,name) + end local blob=loadfile(addsuffix(filename,luasuffixes.luc)) or loadfile(addsuffix(filename,luasuffixes.lua)) if blob then local data=blob() @@ -13332,10 +14390,12 @@ function caches.collapsecontent(content) end end end -function caches.savecontent(cachename,dataname,content) - local name=caches.hashed(cachename) - local full,path=caches.setfirstwritablefile(addsuffix(name,luasuffixes.lua),"trees") - local filename=file.join(path,name) +function caches.savecontent(cachename,dataname,content,filename) + if not filename then + local name=caches.hashed(cachename) + local full,path=caches.setfirstwritablefile(addsuffix(name,luasuffixes.lua),"trees") + filename=file.join(path,name) + end local luaname=addsuffix(filename,luasuffixes.lua) local lucname=addsuffix(filename,luasuffixes.luc) if trace_locating then @@ -13350,7 +14410,7 @@ function caches.savecontent(cachename,dataname,content) content=content, uuid=os.uuid(), } - local ok=io.savedata(luaname,serialize(data,true)) + local ok=io.savedata(luaname,table.serialize(data,true)) if ok then if trace_locating then report_resolvers("category %a, cachename %a saved in %a",dataname,cachename,luaname) @@ -13378,7 +14438,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-met"] = package.loaded["data-met"] or true --- original size: 5453, stripped down to: 4007 +-- original size: 5347, stripped down to: 4015 if not modules then modules={} end modules ['data-met']={ version=1.100, @@ -13406,8 +14466,8 @@ local function splitmethod(filename) if type(filename)=="table" then return filename end - filename=file.collapsepath(filename,".") - if not find(filename,"://") then + filename=file.collapsepath(filename,".") + if not find(filename,"://",1,true) then return { scheme="file",path=filename,original=filename,filename=filename } end local specification=url.hashed(filename) @@ -13497,7 +14557,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-res"] = package.loaded["data-res"] or true --- original size: 61799, stripped down to: 42957 +-- original size: 67003, stripped down to: 46291 if not modules then modules={} end modules ['data-res']={ version=1.001, @@ -13507,7 +14567,7 @@ if not modules then modules={} end modules ['data-res']={ license="see context related readme files", } local gsub,find,lower,upper,match,gmatch=string.gsub,string.find,string.lower,string.upper,string.match,string.gmatch -local concat,insert,sortedkeys=table.concat,table.insert,table.sortedkeys +local concat,insert,remove,sortedkeys,sortedhash=table.concat,table.insert,table.remove,table.sortedkeys,table.sortedhash local next,type,rawget=next,type,rawget local os=os 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 @@ -13516,27 +14576,38 @@ local formatters=string.formatters local filedirname=file.dirname local filebasename=file.basename local suffixonly=file.suffixonly +local addsuffix=file.addsuffix +local removesuffix=file.removesuffix local filejoin=file.join local collapsepath=file.collapsepath local joinpath=file.joinpath +local is_qualified_path=file.is_qualified_path local allocate=utilities.storage.allocate local settings_to_array=utilities.parsers.settings_to_array +local getcurrentdir=lfs.currentdir +local isfile=lfs.isfile +local isdir=lfs.isdir local setmetatableindex=table.setmetatableindex local luasuffixes=utilities.lua.suffixes -local getcurrentdir=lfs.currentdir -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 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 trace_paths=false trackers .register("resolvers.paths",function(v) trace_paths=v end) +local resolve_otherwise=true directives.register("resolvers.otherwise",function(v) resolve_otherwise=v end) local report_resolving=logs.reporter("resolvers","resolving") local resolvers=resolvers local expandedpathfromlist=resolvers.expandedpathfromlist local checkedvariable=resolvers.checkedvariable local splitconfigurationpath=resolvers.splitconfigurationpath local methodhandler=resolvers.methodhandler +local filtered=resolvers.filtered_from_content +local lookup=resolvers.get_from_content +local cleanpath=resolvers.cleanpath +local resolveprefix=resolvers.resolve local initializesetter=utilities.setters.initialize local ostype,osname,osenv,ossetenv,osgetenv=os.type,os.name,os.env,os.setenv,os.getenv -resolvers.cacheversion='1.0.1' -resolvers.configbanner='' +resolvers.cacheversion="1.100" +resolvers.configbanner="" resolvers.homedir=environment.homedir resolvers.criticalvars=allocate { "SELFAUTOLOC","SELFAUTODIR","SELFAUTOPARENT","TEXMFCNF","TEXMF","TEXOS" } resolvers.luacnfname="texmfcnf.lua" @@ -13555,6 +14626,7 @@ end local unset_variable="unset" local formats=resolvers.formats local suffixes=resolvers.suffixes +local usertypes=resolvers.usertypes local dangerous=resolvers.dangerous local suffixmap=resolvers.suffixmap resolvers.defaultsuffixes={ "tex" } @@ -13563,7 +14635,7 @@ local instance=resolvers.instance or nil function resolvers.setenv(key,value,raw) if instance then instance.environment[key]=value - ossetenv(key,raw and value or resolvers.resolve(value)) + ossetenv(key,raw and value or resolveprefix(value)) end end local function getenv(key) @@ -13577,7 +14649,7 @@ local function getenv(key) end resolvers.getenv=getenv resolvers.env=getenv -local function resolve(k) +local function resolvevariable(k) return instance.expansions[k] end local dollarstripper=lpeg.stripper("$") @@ -13586,19 +14658,19 @@ local backslashswapper=lpeg.replacer("\\","/") local somevariable=P("$")/"" local somekey=C(R("az","AZ","09","__","--")^1) local somethingelse=P(";")*((1-S("!{}/\\"))^1*P(";")/"")+P(";")*(P(";")/"")+P(1) -local variableexpander=Cs((somevariable*(somekey/resolve)+somethingelse)^1 ) +local variableexpander=Cs((somevariable*(somekey/resolvevariable)+somethingelse)^1 ) local cleaner=P("\\")/"/"+P(";")*S("!{}/\\")^0*P(";")^1/";" local variablecleaner=Cs((cleaner+P(1))^0) -local somevariable=R("az","AZ","09","__","--")^1/resolve +local somevariable=R("az","AZ","09","__","--")^1/resolvevariable local variable=(P("$")/"")*(somevariable+(P("{")/"")*somevariable*(P("}")/"")) local variableresolver=Cs((variable+P(1))^0) local function expandedvariable(var) return lpegmatch(variableexpander,var) or var end -function resolvers.newinstance() - if trace_locating then +function resolvers.newinstance() + if trace_locating then report_resolving("creating instance") - end + end local environment,variables,expansions,order=allocate(),allocate(),allocate(),allocate() local newinstance={ environment=environment, @@ -13611,6 +14683,7 @@ function resolvers.newinstance() foundintrees=allocate(), hashes=allocate(), hashed=allocate(), + pathlists=false, specification=allocate(), lists=allocate(), data=allocate(), @@ -13623,6 +14696,7 @@ function resolvers.newinstance() savelists=true, pattern=nil, force_suffixes=true, + pathstack={}, } setmetatableindex(variables,function(t,k) local v @@ -13672,8 +14746,13 @@ function resolvers.reset() end local function reset_hashes() instance.lists={} + instance.pathlists=false instance.found={} end +local function reset_caches() + instance.lists={} + instance.pathlists=false +end local slash=P("/") local pathexpressionpattern=Cs ( Cc("^")*( @@ -13725,13 +14804,13 @@ local function identify_configuration_files() for i=1,#cnfpaths do local filepath=cnfpaths[i] local filename=collapsepath(filejoin(filepath,luacnfname)) - local realname=resolvers.resolve(filename) + local realname=resolveprefix(filename) if trace_locating then - local fullpath=gsub(resolvers.resolve(collapsepath(filepath)),"//","/") - local weirdpath=find(fullpath,"/texmf.+/texmf") or not find(fullpath,"/web2c") + local fullpath=gsub(resolveprefix(collapsepath(filepath)),"//","/") + local weirdpath=find(fullpath,"/texmf.+/texmf") or not find(fullpath,"/web2c",1,true) report_resolving("looking for %a on %s path %a from specification %a",luacnfname,weirdpath and "weird" or "given",fullpath,filepath) end - if lfs.isfile(realname) then + if isfile(realname) then specification[#specification+1]=filename if trace_locating then report_resolving("found configuration file %a",realname) @@ -13753,7 +14832,7 @@ local function load_configuration_files() local filename=specification[i] local pathname=filedirname(filename) local filename=filejoin(pathname,luacnfname) - local realname=resolvers.resolve(filename) + local realname=resolveprefix(filename) local blob=loadfile(realname) if blob then local setups=instance.setups @@ -13761,7 +14840,7 @@ local function load_configuration_files() local parent=data and data.parent if parent then local filename=filejoin(pathname,parent) - local realname=resolvers.resolve(filename) + local realname=resolveprefix(filename) local blob=loadfile(realname) if blob then local parentdata=blob() @@ -13786,7 +14865,7 @@ local function load_configuration_files() elseif variables[k]==nil then if trace_locating and not warning then report_resolving("variables like %a in configuration file %a should move to the 'variables' subtable", - k,resolvers.resolve(filename)) + k,resolveprefix(filename)) warning=true end variables[k]=v @@ -13846,7 +14925,7 @@ local function locate_file_databases() local stripped=lpegmatch(inhibitstripper,path) if stripped~="" then local runtime=stripped==path - path=resolvers.cleanpath(path) + path=cleanpath(path) local spec=resolvers.splitmethod(stripped) if runtime and (spec.noscheme or spec.scheme=="file") then stripped="tree:///"..stripped @@ -13909,8 +14988,8 @@ function resolvers.renew(hashname) report_resolving("identifying tree %a",hashname) end end - local realpath=resolvers.resolve(hashname) - if lfs.isdir(realpath) then + local realpath=resolveprefix(hashname) + if isdir(realpath) then if trace_locating then report_resolving("using path %a",realpath) end @@ -14011,19 +15090,53 @@ end function resolvers.unexpandedpath(str) return joinpath(resolvers.unexpandedpathlist(str)) end +function resolvers.pushpath(name) + local pathstack=instance.pathstack + local lastpath=pathstack[#pathstack] + local pluspath=filedirname(name) + if lastpath then + lastpath=collapsepath(filejoin(lastpath,pluspath)) + else + lastpath=collapsepath(pluspath) + end + insert(pathstack,lastpath) + if trace_paths then + report_resolving("pushing path %a",lastpath) + end +end +function resolvers.poppath() + local pathstack=instance.pathstack + if trace_paths and #pathstack>0 then + report_resolving("popping path %a",pathstack[#pathstack]) + end + remove(pathstack) +end +function resolvers.stackpath() + local pathstack=instance.pathstack + local currentpath=pathstack[#pathstack] + return currentpath~="" and currentpath or nil +end local done={} function resolvers.resetextrapath() local ep=instance.extra_paths if not ep then - ep,done={},{} - instance.extra_paths=ep + done={} + instance.extra_paths={} elseif #ep>0 then - instance.lists,done={},{} + done={} + reset_caches() end end function resolvers.registerextrapath(paths,subpaths) - paths=settings_to_array(paths) - subpaths=settings_to_array(subpaths) + if not subpaths or subpaths=="" then + if not paths or path=="" then + return + elseif done[paths] then + return + end + end + local paths=settings_to_array(paths) + local subpaths=settings_to_array(subpaths) local ep=instance.extra_paths or {} local oldn=#ep local newn=oldn @@ -14038,7 +15151,7 @@ function resolvers.registerextrapath(paths,subpaths) local ps=p.."/"..s if not done[ps] then newn=newn+1 - ep[newn]=resolvers.cleanpath(ps) + ep[newn]=cleanpath(ps) done[ps]=true end end @@ -14048,7 +15161,7 @@ function resolvers.registerextrapath(paths,subpaths) local p=paths[i] if not done[p] then newn=newn+1 - ep[newn]=resolvers.cleanpath(p) + ep[newn]=cleanpath(p) done[p]=true end end @@ -14060,7 +15173,7 @@ function resolvers.registerextrapath(paths,subpaths) local ps=ep[i].."/"..s if not done[ps] then newn=newn+1 - ep[newn]=resolvers.cleanpath(ps) + ep[newn]=cleanpath(ps) done[ps]=true end end @@ -14069,52 +15182,70 @@ function resolvers.registerextrapath(paths,subpaths) if newn>0 then instance.extra_paths=ep end - if newn>oldn then - instance.lists={} + if newn~=oldn then + reset_caches() end end -local function made_list(instance,list) - local ep=instance.extra_paths - if not ep or #ep==0 then - return list +function resolvers.pushextrapath(path) + local paths=settings_to_array(path) + if instance.extra_stack then + insert(instance.extra_stack,1,paths) else - local done,new,newn={},{},0 - for k=1,#list do - local v=list[k] - if not done[v] then - if find(v,"^[%.%/]$") then - done[v]=true - newn=newn+1 - new[newn]=v - else - break - end - end - end - for k=1,#ep do - local v=ep[k] + instance.extra_stack={ paths } + end + reset_caches() +end +function resolvers.popextrapath() + if instance.extra_stack then + reset_caches() + return remove(instance.extra_stack,1) + end +end +local function made_list(instance,list,extra_too) + local done={} + local new={} + local newn=0 + local function add(p) + for k=1,#p do + local v=p[k] if not done[v] then done[v]=true newn=newn+1 new[newn]=v end end - for k=1,#list do - local v=list[k] - if not done[v] then - done[v]=true - newn=newn+1 - new[newn]=v + end + for k=1,#list do + local v=list[k] + if done[v] then + elseif find(v,"^[%.%/]$") then + done[v]=true + newn=newn+1 + new[newn]=v + else + break + end + end + if extra_too then + local es=instance.extra_stack + if es and #es>0 then + for k=1,#es do + add(es[k]) end end - return new + local ep=instance.extra_paths + if ep and #ep>0 then + add(ep) + end end + add(list) + return new end function resolvers.cleanpathlist(str) local t=resolvers.expandedpathlist(str) if t then for i=1,#t do - t[i]=collapsepath(resolvers.cleanpath(t[i])) + t[i]=collapsepath(cleanpath(t[i])) end end return t @@ -14122,22 +15253,22 @@ end function resolvers.expandpath(str) return joinpath(resolvers.expandedpathlist(str)) end -function resolvers.expandedpathlist(str) +function resolvers.expandedpathlist(str,extra_too) if not str then return {} - elseif instance.savelists then + elseif instance.savelists then str=lpegmatch(dollarstripper,str) local lists=instance.lists local lst=lists[str] if not lst then - local l=made_list(instance,resolvers.splitpath(resolvers.expansion(str))) + local l=made_list(instance,resolvers.splitpath(resolvers.expansion(str)),extra_too) lst=expandedpathfromlist(l) lists[str]=lst end return lst else local lst=resolvers.splitpath(resolvers.expansion(str)) - return made_list(instance,expandedpathfromlist(lst)) + return made_list(instance,expandedpathfromlist(lst),extra_too) end end function resolvers.expandedpathlistfromvariable(str) @@ -14148,6 +15279,13 @@ end function resolvers.expandpathfromvariable(str) return joinpath(resolvers.expandedpathlistfromvariable(str)) end +function resolvers.cleanedpathlist(v) + local t=resolvers.expandedpathlist(v) + for i=1,#t do + t[i]=resolvers.resolve(resolvers.cleanpath(t[i])) + end + return t +end function resolvers.expandbraces(str) local ori=str local pth=expandedpathfromlist(resolvers.splitpath(ori)) @@ -14164,7 +15302,7 @@ function resolvers.registerfilehash(name,content,someerror) end end local function isreadable(name) - local readable=lfs.isfile(name) + local readable=isfile(name) if trace_detail then if readable then report_resolving("file %a is readable",name) @@ -14174,70 +15312,57 @@ local function isreadable(name) end return readable end -local function collect_files(names) - local filelist,noffiles={},0 +local function collect_files(names) + local filelist={} + local noffiles=0 + local function check(hash,root,pathname,path,name) + if not pathname or find(path,pathname) then + local variant=hash.type + local search=filejoin(root,path,name) + local result=methodhandler('concatinators',variant,root,path,name) + if trace_detail then + report_resolving("match: variant %a, search %a, result %a",variant,search,result) + end + noffiles=noffiles+1 + filelist[noffiles]={ variant,search,result } + end + end for k=1,#names do - local fname=names[k] + local filename=names[k] if trace_detail then - report_resolving("checking name %a",fname) + report_resolving("checking name %a",filename) end - local bname=filebasename(fname) - local dname=filedirname(fname) - if dname=="" or find(dname,"^%.") then - dname=false + local basename=filebasename(filename) + local pathname=filedirname(filename) + if pathname=="" or find(pathname,"^%.") then + pathname=false else - dname=gsub(dname,"%*",".*") - dname="/"..dname.."$" + pathname=gsub(pathname,"%*",".*") + pathname="/"..pathname.."$" end local hashes=instance.hashes for h=1,#hashes do local hash=hashes[h] - local blobpath=hash.name - local files=blobpath and instance.files[blobpath] - if files then + local hashname=hash.name + local content=hashname and instance.files[hashname] + if content then if trace_detail then - report_resolving("deep checking %a, base %a, pattern %a",blobpath,bname,dname) + report_resolving("deep checking %a, base %a, pattern %a",hashname,basename,pathname) end - local blobfile=files[bname] - if not blobfile then - local rname="remap:"..bname - blobfile=files[rname] - if blobfile then - bname=files[rname] - blobfile=files[bname] - end - end - if blobfile then - local blobroot=files.__path__ or blobpath - if type(blobfile)=='string' then - if not dname or find(blobfile,dname) then - local variant=hash.type - local search=filejoin(blobroot,blobfile,bname) - local result=methodhandler('concatinators',hash.type,blobroot,blobfile,bname) - if trace_detail then - report_resolving("match: variant %a, search %a, result %a",variant,search,result) - end - noffiles=noffiles+1 - filelist[noffiles]={ variant,search,result } - end + local path,name=lookup(content,basename) + if path then + local metadata=content.metadata + local realroot=metadata and metadata.path or hashname + if type(path)=="string" then + check(hash,realroot,pathname,path,name) else - for kk=1,#blobfile do - local vv=blobfile[kk] - if not dname or find(vv,dname) then - local variant=hash.type - local search=filejoin(blobroot,vv,bname) - local result=methodhandler('concatinators',hash.type,blobroot,vv,bname) - if trace_detail then - report_resolving("match: variant %a, search %a, result %a",variant,search,result) - end - noffiles=noffiles+1 - filelist[noffiles]={ variant,search,result } - end + for i=1,#path do + check(hash,realroot,pathname,path[i],name) end end end elseif trace_locating then - report_resolving("no match in %a (%s)",blobpath,bname) + report_resolving("no match in %a (%s)",hashname,basename) end end end @@ -14262,7 +15387,7 @@ end local function can_be_dir(name) local fakepaths=instance.fakepaths if not fakepaths[name] then - if lfs.isdir(name) then + if isdir(name) then fakepaths[name]=1 else fakepaths[name]=2 @@ -14278,10 +15403,11 @@ local function find_analyze(filename,askedformat,allresults) if askedformat=="" then if ext=="" or not suffixmap[ext] then local defaultsuffixes=resolvers.defaultsuffixes + local formatofsuffix=resolvers.formatofsuffix for i=1,#defaultsuffixes do local forcedname=filename..'.'..defaultsuffixes[i] wantedfiles[#wantedfiles+1]=forcedname - filetype=resolvers.formatofsuffix(forcedname) + filetype=formatofsuffix(forcedname) if trace_locating then report_resolving("forcing filetype %a",filetype) end @@ -14317,18 +15443,18 @@ local function find_direct(filename,allresults) end end local function find_wildcard(filename,allresults) - if find(filename,'%*') then + if find(filename,'*',1,true) then if trace_locating then report_resolving("checking wildcard %a",filename) end - local method,result=resolvers.findwildcardfiles(filename) + local result=resolvers.findwildcardfiles(filename) if result then return "wildcard",result end end end local function find_qualified(filename,allresults,askedformat,alsostripped) - if not file.is_qualified_path(filename) then + if not is_qualified_path(filename) then return end if trace_locating then @@ -14402,33 +15528,66 @@ local function check_subpath(fname) return fname end end -local function find_intree(filename,filetype,wantedfiles,allresults) +local function makepathlist(list,filetype) local typespec=resolvers.variableofformat(filetype) - local pathlist=resolvers.expandedpathlist(typespec) - local method="intree" + local pathlist=resolvers.expandedpathlist(typespec,filetype and usertypes[filetype]) + local entry={} if pathlist and #pathlist>0 then - local filelist=collect_files(wantedfiles) + for k=1,#pathlist do + local path=pathlist[k] + local prescanned=find(path,'^!!') + local resursive=find(path,'//$') + local pathname=lpegmatch(inhibitstripper,path) + local expression=makepathexpression(pathname) + local barename=gsub(pathname,"/+$","") + barename=resolveprefix(barename) + local scheme=url.hasscheme(barename) + local schemename=gsub(barename,"%.%*$",'') + entry[k]={ + path=path, + pathname=pathname, + prescanned=prescanned, + recursive=recursive, + expression=expression, + barename=barename, + scheme=scheme, + schemename=schemename, + } + end + entry.typespec=typespec + list[filetype]=entry + else + list[filetype]=false + end + return entry +end +local function find_intree(filename,filetype,wantedfiles,allresults) + local pathlists=instance.pathlists + if not pathlists then + pathlists=setmetatableindex(allocate(),makepathlist) + instance.pathlists=pathlists + end + local pathlist=pathlists[filetype] + if pathlist then + local method="intree" + local filelist=collect_files(wantedfiles) local dirlist={} + local result={} if filelist then for i=1,#filelist do dirlist[i]=filedirname(filelist[i][3]).."/" end end if trace_detail then - report_resolving("checking filename %a",filename) + report_resolving("checking filename %a in tree",filename) end - local resolve=resolvers.resolve - local result={} for k=1,#pathlist do - local path=pathlist[k] - local pathname=lpegmatch(inhibitstripper,path) - local doscan=path==pathname - if not find (pathname,'//$') then - doscan=false - end + local entry=pathlist[k] + local path=entry.path + local pathname=entry.pathname local done=false if filelist then - local expression=makepathexpression(pathname) + local expression=entry.expression if trace_detail then report_resolving("using pattern %a for path %a",expression,pathname) end @@ -14436,8 +15595,8 @@ local function find_intree(filename,filetype,wantedfiles,allresults) local fl=filelist[k] local f=fl[2] local d=dirlist[k] - if find(d,expression) or find(resolve(d),expression) then - result[#result+1]=resolve(fl[3]) + if find(d,expression) or find(resolveprefix(d),expression) then + result[#result+1]=resolveprefix(fl[3]) done=true if allresults then if trace_detail then @@ -14458,56 +15617,62 @@ local function find_intree(filename,filetype,wantedfiles,allresults) method="database" else method="filesystem" - pathname=gsub(pathname,"/+$","") - pathname=resolve(pathname) - local scheme=url.hasscheme(pathname) + local scheme=entry.scheme if not scheme or scheme=="file" then - local pname=gsub(pathname,"%.%*$",'') - if not find(pname,"%*") then + local pname=entry.schemename + if not find(pname,"*",1,true) then if can_be_dir(pname) then - for k=1,#wantedfiles do - local w=wantedfiles[k] - local fname=check_subpath(filejoin(pname,w)) - if fname then - result[#result+1]=fname - done=true - if not allresults then - break - end + if not done and not entry.prescanned then + if trace_detail then + report_resolving("quick root scan for %a",pname) end - end - if not done and doscan then - local files=resolvers.simplescanfiles(pname,false,true) for k=1,#wantedfiles do local w=wantedfiles[k] - local subpath=files[w] - if not subpath or subpath=="" then - elseif type(subpath)=="string" then - local fname=check_subpath(filejoin(pname,subpath,w)) - if fname then - result[#result+1]=fname - done=true - if not allresults then - break - end + local fname=check_subpath(filejoin(pname,w)) + if fname then + result[#result+1]=fname + done=true + if not allresults then + break end - else - for i=1,#subpath do - local sp=subpath[i] - if sp=="" then - else - local fname=check_subpath(filejoin(pname,sp,w)) - if fname then - result[#result+1]=fname - done=true - if not allresults then - break + end + end + if not done and entry.recursive then + if trace_detail then + report_resolving("scanning filesystem for %a",pname) + end + local files=resolvers.simplescanfiles(pname,false,true) + for k=1,#wantedfiles do + local w=wantedfiles[k] + local subpath=files[w] + if not subpath or subpath=="" then + elseif type(subpath)=="string" then + local fname=check_subpath(filejoin(pname,subpath,w)) + if fname then + result[#result+1]=fname + done=true + if not allresults then + break + end + end + else + for i=1,#subpath do + local sp=subpath[i] + if sp=="" then + else + local fname=check_subpath(filejoin(pname,sp,w)) + if fname then + result[#result+1]=fname + done=true + if not allresults then + break + end end end end - end - if done and not allresults then - break + if done and not allresults then + break + end end end end @@ -14515,6 +15680,18 @@ local function find_intree(filename,filetype,wantedfiles,allresults) end else end + else + for k=1,#wantedfiles do + local pname=entry.barename + local fname=methodhandler('finders',pname.."/"..wantedfiles[k]) + if fname then + result[#result+1]=fname + done=true + if not allresults then + break + end + end + end end end if done and not allresults then @@ -14549,10 +15726,13 @@ local function find_otherwise(filename,filetype,wantedfiles,allresults) local filelist=collect_files(wantedfiles) local fl=filelist and filelist[1] if fl then - return "otherwise",{ resolvers.resolve(fl[3]) } + return "otherwise",{ resolveprefix(fl[3]) } end end collect_instance_files=function(filename,askedformat,allresults) + if not filename or filename=="" then + return {} + end askedformat=askedformat or "" filename=collapsepath(filename,".") filename=gsub(filename,"^%./",getcurrentdir().."/") @@ -14587,7 +15767,11 @@ collect_instance_files=function(filename,askedformat,allresults) else local method,result,stamp,filetype,wantedfiles if instance.remember then - stamp=formatters["%s--%s"](filename,askedformat) + if askedformat=="" then + stamp=formatters["%s::%s"](suffixonly(filename),filename) + else + stamp=formatters["%s::%s"](askedformat,filename) + end result=stamp and instance.found[stamp] if result then if trace_locating then @@ -14606,7 +15790,7 @@ collect_instance_files=function(filename,askedformat,allresults) method,result=find_intree(filename,filetype,wantedfiles) if not result then method,result=find_onpath(filename,filetype,wantedfiles) - if not result then + if resolve_otherwise and not result then method,result=find_otherwise(filename,filetype,wantedfiles) end end @@ -14622,7 +15806,7 @@ collect_instance_files=function(filename,askedformat,allresults) end if stamp then if trace_locating then - report_resolving("remembering file %a",filename) + report_resolving("remembering file %a using hash %a",filename,stamp) end instance.found[stamp]=result end @@ -14630,6 +15814,9 @@ collect_instance_files=function(filename,askedformat,allresults) end end local function findfiles(filename,filetype,allresults) + if not filename or filename=="" then + return {} + end local result,status=collect_instance_files(filename,filetype or "",allresults) if not result or #result==0 then local lowered=lower(filename) @@ -14649,39 +15836,30 @@ 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 base=filebasename(filename) + local result={} local hashes=instance.hashes - local noffound=0 + local function okay(hash,path,name) + local found=methodhandler('concatinators',hash.type,hash.name,path,name) + if found and found~="" then + result[#result+1]=resolveprefix(found) + return not allresults + end + end for k=1,#hashes do local hash=hashes[k] - local files=instance.files[hash.name] or {} - local blist=files[bname] - if not blist then - local rname="remap:"..bname - blist=files[rname] - if blist then - bname=files[rname] - blist=files[bname] - end - end - if blist then - if type(blist)=='string' then - 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 + local content=instance.files[hash.name] + if content then + local path,name=lookup(content,base) + if not path then + elseif type(path)=="string" then + if okay(hash,path,name) then + return result end else - for kk=1,#blist do - local vv=blist[kk] - 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 + for i=1,#path do + if okay(hash,path[i],name) then + return result end end end @@ -14695,64 +15873,80 @@ end function resolvers.findgivenfile(filename) return findgivenfiles(filename,false)[1] or "" end -local function doit(path,blist,bname,tag,variant,result,allresults) - local done=false - if blist and variant then - local resolve=resolvers.resolve - if type(blist)=='string' then - if find(lower(blist),path) then - 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 - local full=methodhandler('concatinators',variant,tag,vv,bname) or "" - result[#result+1]=resolve(full) - done=true - if not allresults then break end - end - end - end - end - return done -end 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) - result=result or {} +local function findwildcardfiles(filename,allresults,result) + local 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 files=instance.files + if find(name,"*",1,true) then local hashes=instance.hashes + local function okay(found,path,base,hashname,hashtype) + if find(found,path) then + local full=methodhandler('concatinators',hashtype,hashname,found,base) + if full and full~="" then + result[#result+1]=resolveprefix(full) + return not allresults + end + end + end for k=1,#hashes do local hash=hashes[k] - 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,hashname,hashtype,result,allresults) then done=true end - if done and not allresults then break end + local hashname=hash.name + local hashtype=hash.type + if hashname and hashtype then + for found,base in filtered(files[hashname],name) do + if type(found)=='string' then + if okay(found,path,base,hashname,hashtype) then + break + end + else + for i=1,#found do + if okay(found[i],path,base,hashname,hashtype) then + break + end + end end end end end else + local function okayokay(found,path,base,hashname,hashtype) + if find(found,path) then + local full=methodhandler('concatinators',hashtype,hashname,found,base) + if full and full~="" then + result[#result+1]=resolveprefix(full) + return not allresults + end + end + end local hashes=instance.hashes for k=1,#hashes do local hash=hashes[k] - local hashname,hashtype=hash.name,hash.type - if doit(path,files[hashname][base],base,hashname,hashtype,result,allresults) then done=true end - if done and not allresults then break end + local hashname=hash.name + local hashtype=hash.type + if hashname and hashtype then + local found,base=lookup(content,base) + if not found then + elseif type(found)=='string' then + if okay(found,path,base,hashname,hashtype) then + break + end + else + for i=1,#found do + if okay(found[i],path,base,hashname,hashtype) then + break + end + end + end + end end end return result @@ -14825,7 +16019,7 @@ end function resolvers.dowithpath(name,func) local pathlist=resolvers.expandedpathlist(name) for i=1,#pathlist do - func("^"..resolvers.cleanpath(pathlist[i])) + func("^"..cleanpath(pathlist[i])) end end function resolvers.dowithvariable(name,func) @@ -14833,23 +16027,23 @@ function resolvers.dowithvariable(name,func) end function resolvers.locateformat(name) local engine=environment.ownmain or "luatex" - local barename=file.removesuffix(name) - local fullname=file.addsuffix(barename,"fmt") + local barename=removesuffix(name) + local fullname=addsuffix(barename,"fmt") local fmtname=caches.getfirstreadablefile(fullname,"formats",engine) or "" if fmtname=="" then fmtname=resolvers.findfile(fullname) - fmtname=resolvers.cleanpath(fmtname) + fmtname=cleanpath(fmtname) end if fmtname~="" then - local barename=file.removesuffix(fmtname) - local luaname=file.addsuffix(barename,luasuffixes.lua) - local lucname=file.addsuffix(barename,luasuffixes.luc) - local luiname=file.addsuffix(barename,luasuffixes.lui) - if lfs.isfile(luiname) then + local barename=removesuffix(fmtname) + local luaname=addsuffix(barename,luasuffixes.lua) + local lucname=addsuffix(barename,luasuffixes.luc) + local luiname=addsuffix(barename,luasuffixes.lui) + if isfile(luiname) then return barename,luiname - elseif lfs.isfile(lucname) then + elseif isfile(lucname) then return barename,lucname - elseif lfs.isfile(luaname) then + elseif isfile(luaname) then return barename,luaname end end @@ -14871,29 +16065,24 @@ function resolvers.dowithfilesintree(pattern,handle,before,after) local hash=hashes[i] local blobtype=hash.type local blobpath=hash.name - if blobpath then + if blobtype and blobpath then + local total=0 + local checked=0 + local done=0 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 table.sortedhash(files) do - total=total+1 - if find(k,"^remap:") then - elseif 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 + for path,name in filtered(instance.files[blobpath],pattern) do + if type(path)=="string" then + checked=checked+1 + if handle(blobtype,blobpath,path,name) then + done=done+1 + end + else + checked=checked+#path + for i=1,#path do + if handle(blobtype,blobpath,path[i],name) then + done=done+1 end end end @@ -14904,8 +16093,8 @@ function resolvers.dowithfilesintree(pattern,handle,before,after) end end end -resolvers.obsolete=resolvers.obsolete or {} -local obsolete=resolvers.obsolete +local obsolete=resolvers.obsolete or {} +resolvers.obsolete=obsolete resolvers.find_file=resolvers.findfile obsolete.find_file=resolvers.findfile resolvers.find_files=resolvers.findfiles obsolete.find_files=resolvers.findfiles @@ -14916,7 +16105,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-pre"] = package.loaded["data-pre"] or true --- original size: 6643, stripped down to: 4401 +-- original size: 3950, stripped down to: 2935 if not modules then modules={} end modules ['data-pre']={ version=1.001, @@ -14926,44 +16115,51 @@ if not modules then modules={} end modules ['data-pre']={ license="see context related readme files" } local resolvers=resolvers -local prefixes=utilities.storage.allocate() -resolvers.prefixes=prefixes -local cleanpath,findgivenfile,expansion=resolvers.cleanpath,resolvers.findgivenfile,resolvers.expansion +local prefixes=resolvers.prefixes +local cleanpath=resolvers.cleanpath +local findgivenfile=resolvers.findgivenfile +local expansion=resolvers.expansion local getenv=resolvers.getenv -local P,S,R,C,Cs,Cc,lpegmatch=lpeg.P,lpeg.S,lpeg.R,lpeg.C,lpeg.Cs,lpeg.Cc,lpeg.match -local joinpath,basename,dirname=file.join,file.basename,file.dirname -local getmetatable,rawset,type=getmetatable,rawset,type +local basename=file.basename +local dirname=file.dirname +local joinpath=file.join +local isfile=lfs.isfile prefixes.environment=function(str) return cleanpath(expansion(str)) end -prefixes.relative=function(str,n) - if io.exists(str) then - elseif io.exists("./"..str) then - str="./"..str - else - local p="../" - for i=1,n or 2 do - if io.exists(p..str) then - str=p..str - break - else - p=p.."../" +local function relative(str,n) + if not isfile(str) then + local pstr="./"..str + if isfile(pstr) then + str=pstr + else + local p="../" + for i=1,n or 2 do + local pstr=p..str + if isfile(pstr) then + str=pstr + break + else + p=p.."../" + end end end end return cleanpath(str) end +local function locate(str) + local fullname=findgivenfile(str) or "" + return cleanpath(fullname~="" and fullname or str) +end +prefixes.relative=relative +prefixes.locate=locate prefixes.auto=function(str) - local fullname=prefixes.relative(str) - if not lfs.isfile(fullname) then - fullname=prefixes.locate(str) + local fullname=relative(str) + if not isfile(fullname) then + fullname=locate(str) end return fullname end -prefixes.locate=function(str) - local fullname=findgivenfile(str) or "" - return cleanpath((fullname~="" and fullname) or str) -end prefixes.filename=function(str) local fullname=findgivenfile(str) or "" return cleanpath(basename((fullname~="" and fullname) or str)) @@ -14984,6 +16180,13 @@ end prefixes.home=function(str) return cleanpath(joinpath(getenv('HOME'),str)) end +prefixes.env=prefixes.environment +prefixes.rel=prefixes.relative +prefixes.loc=prefixes.locate +prefixes.kpse=prefixes.locate +prefixes.full=prefixes.locate +prefixes.file=prefixes.filename +prefixes.path=prefixes.pathname local function toppath() local inputstack=resolvers.inputstack if not inputstack then @@ -14996,98 +16199,22 @@ local function toppath() return pathname end end -resolvers.toppath=toppath -prefixes.toppath=function(str) - return cleanpath(joinpath(toppath(),str)) -end -prefixes.env=prefixes.environment -prefixes.rel=prefixes.relative -prefixes.loc=prefixes.locate -prefixes.kpse=prefixes.locate -prefixes.full=prefixes.locate -prefixes.file=prefixes.filename -prefixes.path=prefixes.pathname -function resolvers.allprefixes(separator) - local all=table.sortedkeys(prefixes) - if separator then - for i=1,#all do - all[i]=all[i]..":" - end - end - return all -end -local function _resolve_(method,target) - local action=prefixes[method] - if action then - return action(target) - else - return method..":"..target - end -end -local resolved,abstract={},{} -function resolvers.resetresolve(str) - resolved,abstract={},{} -end -local pattern=Cs((C(R("az")^2)*P(":")*C((1-S(" \"\';,"))^1)/_resolve_+P(1))^0) -local prefix=C(R("az")^2)*P(":") -local target=C((1-S(" \"\';,"))^1) -local notarget=(#S(";,")+P(-1))*Cc("") -local pattern=Cs(((prefix*(target+notarget))/_resolve_+P(1))^0) -local function resolve(str) - if type(str)=="table" then - local t={} - for i=1,#str do - t[i]=resolve(str[i]) - end - return t +local function jobpath() + local path=resolvers.stackpath() + if not path or path=="" then + return "." else - local res=resolved[str] - if not res then - res=lpegmatch(pattern,str) - resolved[str]=res - abstract[res]=str - end - return res - end -end -local function unresolve(str) - return abstract[str] or str -end -resolvers.resolve=resolve -resolvers.unresolve=unresolve -if type(os.uname)=="function" then - for k,v in next,os.uname() do - if not prefixes[k] then - prefixes[k]=function() return v end - end - end -end -if os.type=="unix" then - local pattern - local function makepattern(t,k,v) - if t then - rawset(t,k,v) - end - local colon=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) - end - makepattern() - getmetatable(prefixes).__newindex=makepattern - function resolvers.repath(str) - return lpegmatch(pattern,str) - end -else - function resolvers.repath(str) - return str + return path end end +resolvers.toppath=toppath +resolvers.jobpath=jobpath +prefixes.toppath=function(str) return cleanpath(joinpath(toppath(),str)) end +prefixes.jobpath=function(str) return cleanpath(joinpath(jobpath(),str)) end +resolvers.setdynamic("toppath") +resolvers.setdynamic("jobpath") +prefixes.jobfile=prefixes.jobpath +resolvers.setdynamic("jobfile") end -- of closure @@ -15149,7 +16276,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-fil"] = package.loaded["data-fil"] or true --- original size: 3801, stripped down to: 3231 +-- original size: 3863, stripped down to: 3310 if not modules then modules={} end modules ['data-fil']={ version=1.001, @@ -15161,30 +16288,31 @@ if not modules then modules={} end modules ['data-fil']={ 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 resolveprefix=resolvers.resolve 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) + local filename=specification.filename + local realname=resolveprefix(filename) if realname and realname~='' and lfs.isdir(realname) then if trace_locating then - report_files("file locator %a found as %a",name,realname) + report_files("file locator %a found as %a",filename,realname) end - resolvers.appendhash('file',name,true) + resolvers.appendhash('file',filename,true) elseif trace_locating then - report_files("file locator %a not found",name) + report_files("file locator %a not found",filename) end end function hashers.file(specification) - local name=specification.filename - local content=caches.loadcontent(name,'files') - resolvers.registerfilehash(name,content,content==nil) + local pathname=specification.filename + local content=caches.loadcontent(pathname,'files') + resolvers.registerfilehash(pathname,content,content==nil) end function generators.file(specification) - local path=specification.filename - local content=resolvers.scanfiles(path,false,true) - resolvers.registerfilehash(path,content,true) + local pathname=specification.filename + local content=resolvers.scanfiles(pathname,false,true) + resolvers.registerfilehash(pathname,content,true) end concatinators.file=file.join function finders.file(specification,filetype) @@ -15375,7 +16503,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-use"] = package.loaded["data-use"] or true --- original size: 3913, stripped down to: 2998 +-- original size: 3899, stripped down to: 2984 if not modules then modules={} end modules ['data-use']={ version=1.001, @@ -15421,7 +16549,7 @@ end statistics.register("used config file",function() return caches.configfiles() end) statistics.register("used cache path",function() return caches.usedpaths() end) function statistics.savefmtstatus(texname,formatbanner,sourcefile) - local enginebanner=status.list().banner + local enginebanner=status.banner if formatbanner and enginebanner and sourcefile then local luvname=file.replacesuffix(texname,"luv") local luvdata={ @@ -15434,7 +16562,7 @@ function statistics.savefmtstatus(texname,formatbanner,sourcefile) end end function statistics.checkfmtstatus(texname) - local enginebanner=status.list().banner + local enginebanner=status.banner if enginebanner and texname then local luvname=file.replacesuffix(texname,"luv") if lfs.isfile(luvname) then @@ -15466,7 +16594,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-zip"] = package.loaded["data-zip"] or true --- original size: 8489, stripped down to: 6757 +-- original size: 8772, stripped down to: 6841 if not modules then modules={} end modules ['data-zip']={ version=1.001, @@ -15485,16 +16613,6 @@ zip.archives=zip.archives or {} local archives=zip.archives zip.registeredfiles=zip.registeredfiles or {} local registeredfiles=zip.registeredfiles -local limited=false -directives.register("system.inputmode",function(v) - if not limited then - local i_limiter=io.i_limiter(v) - if i_limiter then - zip.open=i_limiter.protect(zip.open) - limited=true - end - end -end) local function validzip(str) if not find(str,"^zip://") then return "zip:///"..str @@ -15509,7 +16627,7 @@ function zip.openarchive(name) local arch=archives[name] if not arch then local full=resolvers.findfile(name) or "" - arch=(full~="" and zip.open(full)) or false + arch=full~="" and zip.open(full) or false archives[name]=arch end return arch @@ -15668,31 +16786,42 @@ function resolvers.usezipfile(archive) end end function resolvers.registerzipfile(z,tree) - local files,filter={},"" - if tree=="" then - filter="^(.+)/(.-)$" - else - filter=format("^%s/(.+)/(.-)$",tree) - end + local names={} + local files={} + local remap={} + local n=0 + local filter=tree=="" and "^(.+)/(.-)$" or format("^%s/(.+)/(.-)$",tree) + local register=resolvers.registerfile if trace_locating then report_zip("registering: using filter %a",filter) end - local register,n=resolvers.registerfile,0 for i in z:files() do - local path,name=match(i.filename,filter) - if path then - if name and name~='' then - register(files,name,path) - n=n+1 - else + local filename=i.filename + local path,name=match(filename,filter) + if not path then + n=n+1 + register(names,filename,"") + local usedname=lower(filename) + files[usedname]="" + if usedname~=filename then + remap[usedname]=filename end - else - register(files,i.filename,'') + elseif name and name~="" then n=n+1 + register(names,name,path) + local usedname=lower(name) + files[usedname]=path + if usedname~=name then + remap[usedname]=name + end + else end end report_zip("registering: %s files registered",n) - return files + return { + files=files, + remap=remap, + } end @@ -15702,7 +16831,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-tre"] = package.loaded["data-tre"] or true --- original size: 2508, stripped down to: 2074 +-- original size: 8479, stripped down to: 5580 if not modules then modules={} end modules ['data-tre']={ version=1.001, @@ -15711,42 +16840,64 @@ if not modules then modules={} end modules ['data-tre']={ copyright="PRAGMA ADE / ConTeXt Development Team", license="see context related readme files" } -local find,gsub,format=string.find,string.gsub,string.format +local find,gsub,lower=string.find,string.gsub,string.lower +local basename,dirname,joinname=file.basename,file.dirname,file .join +local globdir,isdir,isfile=dir.glob,lfs.isdir,lfs.isfile +local P,lpegmatch=lpeg.P,lpeg.match 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 resolveprefix=resolvers.resolve +local notfound=resolvers.finders.notfound +local lookup=resolvers.get_from_content +local collectors={} +local found={} +function resolvers.finders.tree(specification) local spec=specification.filename - local fnd=found[spec] - if fnd==nil then + local okay=found[spec] + if okay==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.."/*" - hash=dir.glob(pattern) - done[path]=hash + local path=dirname(spec) + local name=basename(spec) + if path=="" then + path="." + end + local names=collectors[path] + if not names then + local pattern=find(path,"/%*+$") and path or (path.."/*") + names=globdir(pattern) + collectors[path]=names 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 + for i=1,#names do + local fullname=names[i] + if find(fullname,pattern) then + found[spec]=fullname + return fullname + end + end + local pattern=lower(pattern) + for i=1,#names do + local fullname=lower(names[i]) + if find(fullname,pattern) then + if isfile(fullname) then + found[spec]=fullname + return fullname + else + break + end end end end - fnd=notfound() - found[spec]=fnd + okay=notfound() + found[spec]=okay end - return fnd + return okay end function resolvers.locators.tree(specification) local name=specification.filename - local realname=resolvers.resolve(name) - if realname and realname~='' and lfs.isdir(realname) then + local realname=resolveprefix(name) + if realname and realname~='' and isdir(realname) then if trace_locating then report_trees("locator %a found",realname) end @@ -15757,16 +16908,110 @@ function resolvers.locators.tree(specification) end function resolvers.hashers.tree(specification) local name=specification.filename - if trace_locating then - report_trees("analysing %a",name) - end + report_trees("analyzing %a",name) resolvers.methodhandler("hashers",name) resolvers.generators.file(specification) 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 +local collectors={} +local splitter=lpeg.splitat("/**/") +local stripper=lpeg.replacer { [P("/")*P("*")^1*P(-1)]="" } +table.setmetatableindex(collectors,function(t,k) + local rootname=lpegmatch(stripper,k) + local dataname=joinname(rootname,"dirlist") + local content=caches.loadcontent(dataname,"files",dataname) + if not content then + content=resolvers.scanfiles(rootname,nil,nil,false,true) + caches.savecontent(dataname,"files",content,dataname) + end + t[k]=content + return content +end) +local function checked(root,p,n) + if p then + if type(p)=="table" then + for i=1,#p do + local fullname=joinname(root,p[i],n) + if isfile(fullname) then + return fullname + end + end + else + local fullname=joinname(root,p,n) + if isfile(fullname) then + return fullname + end + end + end + return notfound() +end +local function resolve(specification) + local filename=specification.filename + if filename~="" then + local root,rest=lpegmatch(splitter,filename) + if root and rest then + local path,name=dirname(rest),basename(rest) + if name~=rest then + local content=collectors[root] + local p,n=lookup(content,name) + if not p then + return notfound() + end + local pattern=".*/"..path.."$" + local istable=type(p)=="table" + if istable then + for i=1,#p do + local pi=p[i] + if pi==path or find(pi,pattern) then + local fullname=joinname(root,pi,n) + if isfile(fullname) then + return fullname + end + end + end + elseif p==path or find(p,pattern) then + local fullname=joinname(root,p,n) + if isfile(fullname) then + return fullname + end + end + local queries=specification.queries + if queries and queries.option=="fileonly" then + return checked(root,p,n) + else + return notfound() + end + end + end + local path,name=dirname(filename),basename(filename) + local root=lpegmatch(stripper,path) + local content=collectors[path] + local p,n=lookup(content,name) + if p then + return checked(root,p,n) + end + end + return notfound() +end +resolvers.finders .dirlist=resolve +resolvers.locators .dirlist=resolvers.locators .tree +resolvers.hashers .dirlist=resolvers.hashers .tree +resolvers.generators.dirlist=resolvers.generators.file +resolvers.openers .dirlist=resolvers.openers .file +resolvers.loaders .dirlist=resolvers.loaders .file +function resolvers.finders.dirfile(specification) + local queries=specification.queries + if queries then + queries.option="fileonly" + else + specification.queries={ option="fileonly" } + end + return resolve(specification) +end +resolvers.locators .dirfile=resolvers.locators .dirlist +resolvers.hashers .dirfile=resolvers.hashers .dirlist +resolvers.generators.dirfile=resolvers.generators.dirlist +resolvers.openers .dirfile=resolvers.openers .dirlist +resolvers.loaders .dirfile=resolvers.loaders .dirlist end -- of closure @@ -15775,7 +17020,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-sch"] = package.loaded["data-sch"] or true --- original size: 6202, stripped down to: 5149 +-- original size: 6569, stripped down to: 5304 if not modules then modules={} end modules ['data-sch']={ version=1.001, @@ -15801,8 +17046,13 @@ directives.register("schemes.threshold",function(v) threshold=tonumber(v) or thr function cleaners.none(specification) return specification.original end -function cleaners.strip(specification) - return (gsub(specification.original,"[^%a%d%.]+","-")) +function cleaners.strip(specification) + local path,name=file.splitbase(specification.original) + if path=="" then + return (gsub(name,"[^%a%d%.]+","-")) + else + return (gsub((gsub(path,"%.","-").."-"..name),"[^%a%d%.]+","-")) + end end function cleaners.md5(specification) return file.addsuffix(md5.hex(specification.original),file.suffix(specification.path)) @@ -15818,8 +17068,8 @@ function resolvers.schemes.cleanname(specification) end local cached,loaded,reused,thresholds,handlers={},{},{},{},{} local function runcurl(name,cachename) - local command="curl --silent --create-dirs --output "..cachename.." "..name - os.spawn(command) + local command="curl --silent --insecure --create-dirs --output "..cachename.." "..name + os.execute(command) end local function fetch(specification) local original=specification.original @@ -15951,7 +17201,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-lua"] = package.loaded["data-lua"] or true --- original size: 4237, stripped down to: 3177 +-- original size: 4313, stripped down to: 3227 if not modules then modules={} end modules ['data-lua']={ version=1.001, @@ -15960,7 +17210,7 @@ if not modules then modules={} end modules ['data-lua']={ copyright="PRAGMA ADE / ConTeXt Development Team", license="see context related readme files" } -local resolvers,package=resolvers,package +local package,lpeg=package,lpeg local gsub=string.gsub local concat=table.concat local addsuffix=file.addsuffix @@ -15971,9 +17221,11 @@ local luaformats={ 'TEXINPUTS','LUAINPUTS' } local libformats={ 'CLUAINPUTS' } local helpers=package.helpers or {} local methods=helpers.methods or {} +local resolvers=resolvers +local resolveprefix=resolvers.resolve +helpers.report=logs.reporter("resolvers","libraries") trackers.register("resolvers.libraries",function(v) helpers.trace=v end) trackers.register("resolvers.locating",function(v) helpers.trace=v end) -helpers.report=logs.reporter("resolvers","libraries") helpers.sequence={ "already loaded", "preload table", @@ -15988,7 +17240,7 @@ helpers.sequence={ } local pattern=Cs(P("!")^0/""*(P("/")*P(-1)/"/"+P("/")^1/"/"+1)^0) function helpers.cleanpath(path) - return resolvers.resolve(lpegmatch(pattern,path)) + return resolveprefix(lpegmatch(pattern,path)) end local loadedaslib=helpers.loadedaslib local getextraluapaths=package.extraluapaths @@ -16058,7 +17310,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-aux"] = package.loaded["data-aux"] or true --- original size: 2394, stripped down to: 2005 +-- original size: 2431, stripped down to: 1996 if not modules then modules={} end modules ['data-aux']={ version=1.001, @@ -16072,8 +17324,8 @@ local type,next=type,next local trace_locating=false trackers.register("resolvers.locating",function(v) trace_locating=v end) local resolvers=resolvers local report_scripts=logs.reporter("resolvers","scripts") -function resolvers.updatescript(oldname,newname) - local scriptpath="scripts/context/lua" +function resolvers.updatescript(oldname,newname) + local scriptpath="context/lua" newname=file.addsuffix(newname,"lua") local oldscript=resolvers.cleanpath(oldname) if trace_locating then @@ -16125,7 +17377,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-tmf"] = package.loaded["data-tmf"] or true --- original size: 2600, stripped down to: 1627 +-- original size: 2601, stripped down to: 1627 if not modules then modules={} end modules ['data-tmf']={ version=1.001, @@ -16181,7 +17433,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-lst"] = package.loaded["data-lst"] or true --- original size: 2654, stripped down to: 2301 +-- original size: 2734, stripped down to: 2354 if not modules then modules={} end modules ['data-lst']={ version=1.001, @@ -16190,10 +17442,13 @@ if not modules then modules={} end modules ['data-lst']={ copyright="PRAGMA ADE / ConTeXt Development Team", license="see context related readme files" } -local find,concat,upper,format=string.find,table.concat,string.upper,string.format +local rawget,type,next=rawget,type,next +local find,concat,upper=string.find,table.concat,string.upper local fastcopy,sortedpairs=table.fastcopy,table.sortedpairs -resolvers.listers=resolvers.listers or {} local resolvers=resolvers +local listers=resolvers.listers or {} +resolvers.listers=listers +local resolveprefix=resolvers.resolve local report_lists=logs.reporter("resolvers","lists") local function tabstr(str) if type(str)=='table' then @@ -16202,7 +17457,7 @@ local function tabstr(str) return str end end -function resolvers.listers.variables(pattern) +function listers.variables(pattern) local instance=resolvers.instance local environment=instance.environment local variables=instance.variables @@ -16223,10 +17478,10 @@ function resolvers.listers.variables(pattern) for key,value in 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",tabstr(resolvers.resolve(expansions[key])) or "unset") + 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",tabstr(resolveprefix(expansions[key])) or "unset") end end instance.environment=fastcopy(env) @@ -16234,15 +17489,15 @@ function resolvers.listers.variables(pattern) instance.expansions=fastcopy(exp) end local report_resolved=logs.reporter("system","resolved") -function resolvers.listers.configurations() +function listers.configurations() local configurations=resolvers.instance.specification for i=1,#configurations do - report_resolved("file : %s",resolvers.resolve(configurations[i])) + report_resolved("file : %s",resolveprefix(configurations[i])) end report_resolved("") local list=resolvers.expandedpathfromlist(resolvers.splitpath(resolvers.luacnfspec)) for i=1,#list do - local li=resolvers.resolve(list[i]) + local li=resolveprefix(list[i]) if lfs.isdir(li) then report_resolved("path - %s",li) else @@ -16547,7 +17802,7 @@ do -- create closure to overcome 200 locals limit package.loaded["luat-fmt"] = package.loaded["luat-fmt"] or true --- original size: 5951, stripped down to: 4922 +-- original size: 5955, stripped down to: 4926 if not modules then modules={} end modules ['luat-fmt']={ version=1.001, @@ -16635,7 +17890,7 @@ function environment.make_format(name) end local command=format("%s --ini %s --lua=%s %s %sdump",engine,primaryflags(),quoted(usedluastub),quoted(fulltexsourcename),os.platform=="unix" and "\\\\" or "\\") report_format("running command: %s\n",command) - os.spawn(command) + os.execute(command) local pattern=file.removesuffix(file.basename(usedluastub)).."-*.mem" local mp=dir.glob(pattern) if mp then @@ -16670,7 +17925,7 @@ function environment.run_format(name,data,more) else local command=format("%s %s --fmt=%s --lua=%s %s %s",engine,primaryflags(),quoted(barename),quoted(luaname),quoted(data),more~="" and quoted(more) or "") report_format("running command: %s",command) - os.spawn(command) + os.execute(command) end end end @@ -16681,8 +17936,8 @@ end -- of closure -- used libraries : l-lua.lua l-package.lua l-lpeg.lua l-function.lua l-string.lua l-table.lua l-io.lua l-number.lua l-set.lua l-os.lua l-file.lua l-gzip.lua l-md5.lua l-url.lua l-dir.lua l-boolean.lua l-unicode.lua l-math.lua util-str.lua util-tab.lua util-sto.lua util-prs.lua util-fmt.lua trac-set.lua trac-log.lua trac-inf.lua trac-pro.lua util-lua.lua util-deb.lua util-mrg.lua util-tpl.lua util-env.lua luat-env.lua lxml-tab.lua lxml-lpt.lua lxml-mis.lua lxml-aux.lua lxml-xml.lua trac-xml.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-zip.lua data-tre.lua data-sch.lua data-lua.lua data-aux.lua data-tmf.lua data-lst.lua util-lib.lua luat-sta.lua luat-fmt.lua -- skipped libraries : - --- original bytes : 685064 --- stripped bytes : 242353 +-- original bytes : 745618 +-- stripped bytes : 269191 -- end library merge @@ -16781,17 +18036,18 @@ local ownlibs = { -- order can be made better } +-- c:/data/develop/tex-context/tex/texmf-win64/bin/../../texmf-context/tex/context/base/data-tmf.lua +-- c:/data/develop/context/sources/data-tmf.lua + local ownlist = { - '.', - ownpath , - ownpath .. "/../sources", -- HH's development path + -- '.', + -- ownpath , + owntree .. "/../../../../context/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", } @@ -16907,6 +18163,7 @@ local helpinfo = [[ <category name="basic"> <subcategory> <flag name="script"><short>run an mtx script (lua prefered method) (<ref name="noquotes"/>), no script gives list</short></flag> + <flag name="evaluate"><short>run code passed on the commandline (between quotes)</short></flag> <flag name="execute"><short>run a script or program (texmfstart method) (<ref name="noquotes"/>)</short></flag> <flag name="resolve"><short>resolve prefixed arguments</short></flag> <flag name="ctxlua"><short>run internally (using preloaded libs)</short></flag> @@ -16932,6 +18189,7 @@ local helpinfo = [[ <flag name="verbose"><short>give a bit more info</short></flag> <flag name="trackers" value="list"><short>enable given trackers</short></flag> <flag name="progname" value="str"><short>format or backend</short></flag> + <flag name="systeminfo" value="str"><short>show current operating system, processor, etc</short></flag> </subcategory> <subcategory> <flag name="edit"><short>launch editor with found file</short></flag> @@ -17561,6 +18819,39 @@ function runners.associate(filename) os.launch(filename) end +function runners.evaluate(code,filename) -- for Luigi + if code == "loop" then + while true do + io.write("> ") + local code = io.read() + if code ~= "" then + local temp = string.match(code,"^= (.*)$") + if temp then + code = "print("..temp..")" + end + local compiled, message = loadstring(code) + if type(compiled) ~= "function" then + io.write("! " .. (message or code).."\n") + else + io.write(compiled()) + end + end + end + else + if type(code) ~= "string" or code == "" then + code = filename + end + if code ~= "" then + local compiled, message = loadstring(code) + if type(compiled) ~= "function" then + io.write("invalid lua code: " .. (message or code)) + return + end + io.write(compiled()) + end + end +end + function runners.gethelp(filename) local url = environment.argument("url") if url and url ~= "" then @@ -17572,6 +18863,15 @@ function runners.gethelp(filename) end end +function runners.systeminfo() + report("architecture : %s",os.platform or "<unset>") + report("operating system : %s",os.name or "<unset>") + report("file architecture : %s",os.type or "<unset>") + report("binary path : %s",os.selfdir or "<unset>") + report("binary suffix : %s",os.binsuffix or "<unset>") + report("library suffix : %s",os.libsuffix or "<unset>") +end + -- this is a bit dirty ... first we store the first filename and next we -- split the arguments so that we only see the ones meant for this script -- ... later we will use the second half @@ -17687,16 +18987,13 @@ end if e_argument("ansi") then - local formatters = string.formatters + logs.setformatters("ansi") - logs.setformatters { - report_yes = formatters["[1;32m%-15s [0;1m|[0m %s"], - report_nop = formatters["[1;32m%-15s [0;1m|[0m"], - subreport_yes = formatters["[1;32m%-15s [0;1m|[1;31m %s [0;1m|[0m %s"], - subreport_nop = formatters["[1;32m%-15s [0;1m|[1;31m %s [0;1m|[0m"], - status_yes = formatters["[1;32m%-15s [0;1m:[0m %s\n"], - status_nop = formatters["[1;32m%-15s [0;1m:[0m\n"], - } + local script = e_argument("script") or e_argument("scripts") + + if type(script) == "string" then + logs.writer("]0;"..script.."") -- for Alan to test + end end @@ -17715,14 +19012,26 @@ if e_argument("script") or e_argument("scripts") then ok = runners.execute_ctx_script(filename) end +elseif e_argument("evaluate") then + + runners.evaluate(e_argument("evaluate"),filename) + elseif e_argument("selfmerge") then -- embed used libraries runners.loadbase() local found = locate_libs() + if found then - utilities.merger.selfmerge(own.name,own.libs,{ found }) + local mtxrun = resolvers.findfile("mtxrun.lua") -- includes local name + if lfs.isfile(mtxrun) then + utilities.merger.selfmerge(mtxrun,own.libs,{ found }) + application.report("runner updated on resolved path: %s",mtxrun) + else + utilities.merger.selfmerge(own.name,own.libs,{ found }) + application.report("runner updated on relative path: %s",own.name) + end end elseif e_argument("selfclean") then @@ -17730,7 +19039,15 @@ elseif e_argument("selfclean") then -- remove embedded libraries runners.loadbase() - utilities.merger.selfclean(own.name) + + local mtxrun = resolvers.findfile("mtxrun.lua") -- includes local name + if lfs.isfile(mtxrun) then + utilities.merger.selfclean(mtxrun) + application.report("runner cleaned on resolved path: %s",mtxrun) + else + utilities.merger.selfclean(own.name) + application.report("runner cleaned on relative path: %s",own.name) + end elseif e_argument("selfupdate") then @@ -17972,6 +19289,8 @@ elseif e_argument("version") then application.version() + application.report("source path",environment.ownbin) + elseif e_argument("directives") then directives.show() @@ -17989,6 +19308,10 @@ elseif e_argument("exporthelp") then runners.loadbase() application.export(e_argument("exporthelp"),filename) +elseif e_argument("systeminfo") then + + runners.systeminfo() + elseif e_argument("help") or filename=='help' or filename == "" then application.help() diff --git a/scripts/context/stubs/mswin/mtxrunjit.exe b/scripts/context/stubs/mswin/mtxrunjit.exe Binary files differnew file mode 100644 index 000000000..0e7882cf9 --- /dev/null +++ b/scripts/context/stubs/mswin/mtxrunjit.exe diff --git a/scripts/context/stubs/mswin/mtxworks.exe b/scripts/context/stubs/mswin/mtxworks.exe Binary files differindex faae5caa7..0e7882cf9 100644 --- a/scripts/context/stubs/mswin/mtxworks.exe +++ b/scripts/context/stubs/mswin/mtxworks.exe diff --git a/scripts/context/stubs/mswin/pstopdf.exe b/scripts/context/stubs/mswin/pstopdf.exe Binary files differindex faae5caa7..0e7882cf9 100644 --- a/scripts/context/stubs/mswin/pstopdf.exe +++ b/scripts/context/stubs/mswin/pstopdf.exe diff --git a/scripts/context/stubs/mswin/texexec.exe b/scripts/context/stubs/mswin/texexec.exe Binary files differindex faae5caa7..0e7882cf9 100644 --- a/scripts/context/stubs/mswin/texexec.exe +++ b/scripts/context/stubs/mswin/texexec.exe diff --git a/scripts/context/stubs/mswin/texmfstart.exe b/scripts/context/stubs/mswin/texmfstart.exe Binary files differindex faae5caa7..0e7882cf9 100644 --- a/scripts/context/stubs/mswin/texmfstart.exe +++ b/scripts/context/stubs/mswin/texmfstart.exe diff --git a/scripts/context/stubs/setup/setuptex b/scripts/context/stubs/setup/setuptex new file mode 100644 index 000000000..d41e36707 --- /dev/null +++ b/scripts/context/stubs/setup/setuptex @@ -0,0 +1,167 @@ +# Example setup file for ConTeXt distribution +# +# Author: Hans Hagen +# Patches: Arthur R. & Mojca M. +# +# Usage: +# . setuptex [texroot] +# +# On the first run also execute: +# mktexlsr +# texexec --make --alone + +# +# PLATFORM +# + +# we will try to guess the platform first +# (needs to be kept in sync with first-setup.sh and mtxrun) +# if yours is missing, let us know + +system=`uname -s` +cpu=`uname -m` + +case "$system" in + # linux + Linux) + case "$cpu" in + i*86) platform="linux" ;; + x86_64|ia64) platform="linux-64" ;; + # a little bit of cheating with ppc64 (won't work on Gentoo) + ppc|ppc64) platform="linux-ppc" ;; + *) platform="unknown" ;; + esac ;; + # Mac OS X + Darwin) + case "$cpu" in + i*86) platform="osx-intel" ;; + x86_64) platform="osx-64" ;; + ppc*|powerpc|power*|Power*) platform="osx-ppc" ;; + *) platform="unknown" ;; + esac ;; + # FreeBSD + FreeBSD|freebsd) + case "$cpu" in + i*86) platform="freebsd" ;; + x86_64) platform="freebsd" ;; + amd64) platform="freebsd-amd64" ;; + *) platform="unknown" ;; + esac ;; + # kFreeBSD (Debian) + GNU/kFreeBSD) + case "$cpu" in + i*86) platform="kfreebsd-i386" ;; + x86_64|amd64) platform="kfreebsd-amd64" ;; + *) platform="unknown" ;; + esac ;; + # cygwin + CYGWIN) + case "$cpu" in + i*86) platform="cygwin" ;; + x86_64|ia64) platform="cygwin-64" ;; + *) platform="unknown" ;; + esac ;; + # SunOS/Solaris + SunOS) + case "$cpu" in + sparc) platform="solaris-sparc" ;; + i86pc) platform="solaris-intel" ;; + *) platform="unknown" ;; + esac ;; + *) platform="unknown" +esac + +# temporary fix for Snow Leopard +if test "$platform" = "osx-intel"; then + # running Snow Leopard or later + if test `uname -r|cut -f1 -d"."` -ge 10 ; then + # working on 64-bit hardware + if test `sysctl -n hw.cpu64bit_capable` = 1; then + platform="osx-64" + fi + fi +fi + +if test "$platform" = "unknown" ; then + echo "Error: your system \"$system $cpu\" is not supported yet." + echo "Please report to the ConTeXt mailing-list (ntg-context@ntg.nl)" +fi + +# +# PATH +# + +# this resolves to path of the setuptex script +# We use $0 for determine the path to the script, except for: +# * bash where $0 always is bash; here we use BASH_SOURCE +# * ksh93 where we use ${.sh.file} +# Thanks to Vasile Gaburici and Alessandro Perucchi for reporting this +# * http://www.ntg.nl/pipermail/ntg-context/2008/033953.html +# * http://www.ntg.nl/pipermail/ntg-context/2012/068658.html +if [ z"$BASH_SOURCE" != z ]; then + SCRIPTPATH="$BASH_SOURCE" +elif [ z"$KSH_VERSION" != z ]; then + SCRIPTPATH="${.sh.file}" +else + SCRIPTPATH="$0" +fi + +OWNPATH=$(cd -P -- "$(dirname -- "$SCRIPTPATH")" && pwd -P) + +# but one can also call +# . setuptex path-to-tree + +TEXROOT="" +# first check if any path has been provided in the argument, and try to use that one +if [ $# -ne 0 ] ; then + # TODO: resolve any errors + ARGPATH=$(cd -P -- "$(dirname -- "$1")" && pwd -P) && ARGPATH=$ARGPATH/$(basename -- "$1") + if test -f "$ARGPATH/texmf/tex/plain/base/plain.tex" ; then + if [ -d "$ARGPATH/texmf-$platform/bin" ]; then + TEXROOT="$ARGPATH" + else + echo "Binaries for platform '$platform' are missing." + echo "(There is no folder \"$ARGPATH/texmf-$platform/bin\")" + fi + else + echo "The argument \"$ARGPATH\" is not a valid TEXROOT path." + echo "(There is no file \"$ARGPATH/texmf/tex/plain/base/plain.tex\")" + + if [ -f "$OWNPATH/texmf/tex/plain/base/plain.tex" ]; then + TEXROOT="$OWNPATH" + fi + fi +else + if [ -f "$OWNPATH/texmf/tex/plain/base/plain.tex" ]; then + if [ -d "$OWNPATH/texmf-$platform/bin" ]; then + TEXROOT="$OWNPATH" + else + echo "Binaries for platform '$platform' are missing." + echo "(There is no folder \"$OWNPATH/texmf-$platform/bin\")" + fi + else + echo "\"$OWNPATH\" is not a valid TEXROOT path." + echo "(There is no file \"$OWNPATH/texmf/tex/plain/base/plain.tex\")" + fi +fi + +if [ "$TEXROOT" != "" ]; then + # for Alan Braslau's server :) + if [ "x$PS1" != "x" ] ; then + echo "Setting \"$TEXROOT\" as ConTeXt root." + fi + +# ConTeXt binaries have to be added to PATH +TEXMFOS=$TEXROOT/texmf-$platform +export PATH=$TEXMFOS/bin:$PATH + +# unset variables that won't be used lately +unset platform cpu system OWNPATH SCRIPTPATH ARGPATH TEXMFOS + +# not sure why this would be needed +# export CTXMINIMAL=yes + +else + echo "provide a proper tex root (like '. setuptex /something/tex')" ; +fi + diff --git a/scripts/context/stubs/mswin/setuptex.bat b/scripts/context/stubs/setup/setuptex.bat index b61fd4494..b61fd4494 100644 --- a/scripts/context/stubs/mswin/setuptex.bat +++ b/scripts/context/stubs/setup/setuptex.bat diff --git a/scripts/context/stubs/setup/setuptex.csh b/scripts/context/stubs/setup/setuptex.csh new file mode 100644 index 000000000..c1160675f --- /dev/null +++ b/scripts/context/stubs/setup/setuptex.csh @@ -0,0 +1,164 @@ +# Example setup file for ConTeXt distribution +# +# Author: Hans Hagen +# Patches: Arthur R. & Mojca M. +# (t)csh version: Alan B. +# +# Usage : +# source setuptex.csh [texroot] +# +# On the first run also execute: +# mktexlsr +# texexec --make --alone + +echo "We are considering removing setuptex.csh in case that nobody uses it." +echo "If you still use this file please drop us some mail at" +echo " gardeners (at) contextgarden (dot) net" +echo "If we don't get any response, we will delete it in near future." + +# +# PLATFORM +# + +# we will try to guess the platform first +# (needs to be kept in sync with first-setup.sh and mtxrun) +# if yours is missing, let us know + +set system=`uname -s` +set cpu=`uname -m` + +switch ( $system ) + # linux + case Linux: + switch ( $cpu ) + case i*86: + set platform="linux" + breaksw + case x86_64: + case ia64: + set platform="linux-64" + breaksw + case ppc: + case ppc64: + set platform="linux-ppc" + breaksw + default: + set platform="unknown" + endsw + breaksw + # Mac OS X + case Darwin: + switch ( $cpu ) + case i*86: + set platform="osx-intel" + breaksw + case x86_64: + set platform="osx-64" + breaksw + case ppc*: + case powerpc: + case power*: + case Power*: + set platform="osx-ppc" + breaksw + default: + set platform="unknown" + endsw + breaksw + # FreeBSD + case FreeBSD: + case freebsd: + switch ( $cpu ) + case i*86: + set platform="freebsd" + breaksw + case x86_64: + set platform="freebsd" + breaksw + case amd64: + set platform="freebsd-amd64" + breaksw + default: + set platform="unknown" + endsw + breaksw + # cygwin + case CYGWIN: + switch ( $cpu ) + case i*86: + set platform="cygwin" + breaksw + case x86_64: + case ia64: + set platform="cygwin-64" + breaksw + default: + set platform="unknown" + endsw + breaksw + # SunOS/Solaris + case SunOS: + switch ( $cpu ) + case sparc: + set platform="solaris-sparc" + breaksw + case i86pc: + set platform="solaris-intel" + default: + set platform="unknown" + endsw + breaksw + # Other + default: + set platform="unknown" +endsw + +if ( $platform == "unknown" ) then + echo Error: your system \"$system $cpu\" is not supported yet. + echo Please report to the ConTeXt mailing-list (ntg-context@ntg.nl). +endif + +# +# PATH +# + +# this resolves to path of the setuptex script +# We use $0 for determine the path to the script, except for bash and (t)csh where $0 +# always is bash or (t)csh. + +# but one can also call +# . setuptex path-to-tex-tree + +# first check if any path has been provided in the argument, and try to use that one +if ( $# > 0 ) then + setenv TEXROOT $1 +else + # $_ should be `history -h 1` but doesn't seem to work... + set cmd=`history -h 1` + if ( $cmd[2]:h == $cmd[2]:t ) then + setenv TEXROOT $cwd + else + setenv TEXROOT $cmd[2]:h + endif + unset cmd +endif +cd $TEXROOT; setenv TEXROOT $cwd; cd - + +if ( -f "$TEXROOT/texmf/tex/plain/base/plain.tex" ) then + echo Setting \"$TEXROOT\" as TEXROOT. +else + echo \"$TEXROOT\" is not a valid TEXROOT path. + echo There is no file \"$TEXROOT/texmf/tex/plain/base/plain.tex\". + echo Please provide a proper tex root (like \"source setuptex /path/tex\") + unsetenv TEXROOT + exit +endif + +unsetenv TEXINPUTS MPINPUTS MFINPUTS + +# ConTeXt binaries have to be added to PATH +setenv TEXMFOS $TEXROOT/texmf-$platform +setenv PATH $TEXMFOS/bin:$PATH +# TODO: we could set OSFONTDIR on Mac for example + +# setenv CTXMINIMAL yes diff --git a/scripts/context/stubs/source/mtxrun_dll.c b/scripts/context/stubs/source/mtxrun_dll.c index 400ed6778..fc2e260f5 100644 --- a/scripts/context/stubs/source/mtxrun_dll.c +++ b/scripts/context/stubs/source/mtxrun_dll.c @@ -55,7 +55,6 @@ return 1; \ } -char texlua_name[] = "texlua"; // just a bare name, luatex strips the rest anyway static char cmdline[MAX_CMD]; static char dirpath[MAX_PATH]; static char progname[MAX_PATH]; @@ -70,12 +69,12 @@ int main( int argc, char *argv[] ) __declspec(dllexport) int dllrunscript( int argc, char *argv[] ) #endif { - char *s, *luatexfname, *argstr, **lua_argv; + char *binary, *s, *luatexfname, *argstr, **lua_argv; int k, quoted, lua_argc; int passprogname = 0; + unsigned char is_jit=0; // directory of this module/executable - HMODULE module_handle = GetModuleHandle( "mtxrun.dll" ); // if ( module_handle == NULL ) exe path will be used, which is OK too k = (int) GetModuleFileName( module_handle, dirpath, MAX_PATH ); @@ -86,13 +85,20 @@ __declspec(dllexport) int dllrunscript( int argc, char *argv[] ) *(++s) = '\0'; //remove file name, leave trailing backslash // program name - k = strlen(argv[0]); while ( k && (argv[0][k-1] != '/') && (argv[0][k-1] != '\\') ) k--; strcpy(progname, &argv[0][k]); s = progname; if ( s = strrchr(s, '.') ) *s = '\0'; // remove file extension part + /* check "jit" : strlen("jit") = 3 */ + if (strncmp(progname + strlen(progname) - 3, "jit", 3) == 0) { + is_jit = 1; + progname[strlen(progname) - 3]='\0'; + } + else + is_jit = 0; + // script path strcpy( scriptpath, dirpath ); @@ -114,44 +120,110 @@ __declspec(dllexport) int dllrunscript( int argc, char *argv[] ) strcat( scriptpath, "mtxrun.lua" ); passprogname = 1; } - if ( GetFileAttributes(scriptpath) == INVALID_FILE_ATTRIBUTES ) DIE( "file not found: %s\n", scriptpath ); - // find texlua.exe - - if ( !SearchPath( - getenv( "PATH" ), // path to search (optional) - "texlua.exe", // file name to search - NULL, // file extension to add (optional) - MAX_PATH, // output buffer size - luatexpath, // output buffer pointer - &luatexfname ) // pointer to a file part in the output buffer (optional) - ) - if ( !SearchPath( - dirpath, // path to search (optional) - "texlua.exe", // file name to search - NULL, // file extension to add (optional) - MAX_PATH, // output buffer size - luatexpath, // output buffer pointer - &luatexfname ) // pointer to a file part in the output buffer (optional) - ) - DIE( "unable to locate texlua.exe on the search path" ); + // find luatex.exe /luajittex.exe + if ( SearchPath( + dirpath, // was getenv( "PATH" ), // path to search (optional) + (is_jit ? "luajittex.exe":"luatex.exe"), // file name to search + NULL, // file extension to add (optional) + MAX_PATH, // output buffer size + luatexpath, // output buffer pointer + &luatexfname ) // pointer to a file part in the output buffer (optional) + ) { + binary = (is_jit ? "luajittex.exe":"luatex.exe"); + } else if ( SearchPath( + dirpath, // was getenv( "PATH" ), // path to search (optional) + (is_jit ? "texluajit.exe":"texlua.exe"), // file name to search + NULL, // file extension to add (optional) + MAX_PATH, // output buffer size + luatexpath, // output buffer pointer + &luatexfname ) // pointer to a file part in the output buffer (optional) + ) { + binary = (is_jit ? "texluajit.exe":"texlua.exe"); + } else if ( SearchPath( + getenv("PATH"), // was dirpath, // path to search (optional) + (is_jit ? "luajittex.exe":"luatex.exe"), // file name to search + NULL, // file extension to add (optional) + MAX_PATH, // output buffer size + luatexpath, // output buffer pointer + &luatexfname ) // pointer to a file part in the output buffer (optional) + ) { + binary = (is_jit ? "luajittex.exe":"luatex.exe"); + } else if ( SearchPath( + getenv("PATH") , // was dirpath, // path to search (optional) + (is_jit ? "texluajit.exe":"texlua.exe"), // file name to search + NULL, // file extension to add (optional) + MAX_PATH, // output buffer size + luatexpath, // output buffer pointer + &luatexfname ) // pointer to a file part in the output buffer (optional) + ) { + binary = (is_jit ? "texluajit.exe":"texlua.exe"); + }else { + DIE( "unable to locate texlua.exe on the search path" ); + } + + /* if ( SearchPath( */ + /* dirpath, // was getenv( "PATH" ), // path to search (optional) */ + /* (is_jit ? "luajittex.exe":"luatex.exe"), // file name to search */ + /* NULL, // file extension to add (optional) */ + /* MAX_PATH, // output buffer size */ + /* luatexpath, // output buffer pointer */ + /* &luatexfname ) // pointer to a file part in the output buffer (optional) */ + /* ) { */ + /* binary = (is_jit ? "luajittex.exe":"luatex.exe"); */ + /* }else if ( SearchPath( */ + /* getenv("PATH"), // was dirpath, // path to search (optional) */ + /* (is_jit ? "luajittex.exe":"luatex.exe"), // file name to search */ + /* NULL, // file extension to add (optional) */ + /* MAX_PATH, // output buffer size */ + /* luatexpath, // output buffer pointer */ + /* &luatexfname ) // pointer to a file part in the output buffer (optional) */ + /* ) { */ + /* binary = (is_jit ? "luajittex.exe":"luatex.exe"); */ + /* }else if ( SearchPath( */ + /* dirpath, // was getenv( "PATH" ), // path to search (optional) */ + /* (is_jit ? "texluajit.exe":"texlua.exe"), // file name to search */ + /* NULL, // file extension to add (optional) */ + /* MAX_PATH, // output buffer size */ + /* luatexpath, // output buffer pointer */ + /* &luatexfname ) // pointer to a file part in the output buffer (optional) */ + /* ) { */ + /* binary = (is_jit ? "texluajit.exe":"texlua.exe"); */ + /* }else if ( SearchPath( */ + /* getenv("PATH") , // was dirpath, // path to search (optional) */ + /* (is_jit ? "texluajit.exe":"texlua.exe"), // file name to search */ + /* NULL, // file extension to add (optional) */ + /* MAX_PATH, // output buffer size */ + /* luatexpath, // output buffer pointer */ + /* &luatexfname ) // pointer to a file part in the output buffer (optional) */ + /* ) { */ + /* binary = (is_jit ? "texluajit.exe":"texlua.exe"); */ + /* }else { */ + /* DIE( "unable to locate texlua.exe on the search path" ); */ + /* } */ + - // link directly with luatex.dll if available in texlua's dir - strcpy( luatexfname, "luatex.dll" ); + + // link directly with luatex.dll if available in texlua's dir + strcpy( luatexfname, (is_jit ? "luajittex.dll":"luatex.dll") ); if ( dllluatex = LoadLibrary(luatexpath) ) { - mainlikeproc dllluatexmain = (mainlikeproc) GetProcAddress( dllluatex, "dllluatexmain" ); + mainlikeproc dllluatexmain = (mainlikeproc) GetProcAddress( dllluatex, (is_jit ? "dllluajittexmain": "dllluatexmain" )); if ( dllluatexmain == NULL ) - DIE( "unable to locate dllluatexmain procedure in luatex.dll" ); + if (is_jit) + DIE( "unable to locate dllluatexmain procedure in luajittex.dll" ) + else + DIE( "unable to locate dllluatexmain procedure in luatex.dll" ); // set up argument list for texlua script - lua_argv = (char **)malloc( (argc + 4) * sizeof(char *) ); + lua_argv = (char **)malloc( (argc + 5) * sizeof(char *) ); if ( lua_argv == NULL ) DIE( "out of memory\n" ); - lua_argv[lua_argc=0] = texlua_name; + lua_argv[lua_argc=0] = luatexfname; + lua_argv[++lua_argc] = "--luaonly"; lua_argv[++lua_argc] = scriptpath; // script to execute if (passprogname) { lua_argv[++lua_argc] = "--script"; @@ -162,15 +234,15 @@ __declspec(dllexport) int dllrunscript( int argc, char *argv[] ) // call texlua interpreter // dllluatexmain never returns, but we pretend that it does - + k = dllluatexmain( lua_argc, lua_argv ); if (lua_argv) free( lua_argv ); return k; } - // we are still here, so no luatex.dll; spawn texlua.exe instead - strcpy( luatexfname, "texlua.exe" ); + strcpy( luatexfname,binary); + strcpy( cmdline, " --luaonly " ); strcpy( cmdline, "\"" ); strcat( cmdline, luatexpath ); strcat( cmdline, "\" \"" ); @@ -180,7 +252,6 @@ __declspec(dllexport) int dllrunscript( int argc, char *argv[] ) strcat( cmdline, " --script " ); strcat( cmdline, progname ); } - argstr = GetCommandLine(); // get the command line of this process if ( argstr == NULL ) DIE( "unable to retrieve the command line string\n" ); @@ -209,7 +280,6 @@ __declspec(dllexport) int dllrunscript( int argc, char *argv[] ) si.hStdOutput = GetStdHandle( STD_OUTPUT_HANDLE ); si.hStdError = GetStdHandle( STD_ERROR_HANDLE ); ZeroMemory( &pi, sizeof(pi) ); - if( !CreateProcess( NULL, // module name (uses command line if NULL) cmdline, // command line @@ -222,7 +292,6 @@ __declspec(dllexport) int dllrunscript( int argc, char *argv[] ) &si, // STARTUPINFO structure &pi ) // PROCESS_INFORMATION structure ) DIE( "command execution failed: %s\n", cmdline ); - DWORD ret = 0; CloseHandle( pi.hThread ); // thread handle is not needed if ( WaitForSingleObject( pi.hProcess, INFINITE ) == WAIT_OBJECT_0 ) { @@ -232,7 +301,6 @@ __declspec(dllexport) int dllrunscript( int argc, char *argv[] ) CloseHandle( pi.hProcess ); // propagate exit code from the child process - return ret; } diff --git a/scripts/context/stubs/source/readme.txt b/scripts/context/stubs/source/readme.txt index 354d85b09..72892ee2f 100644 --- a/scripts/context/stubs/source/readme.txt +++ b/scripts/context/stubs/source/readme.txt @@ -1,36 +1,40 @@ Copyright: -The originally 'runscript' program was written by in 2009 by -T.M.Trzeciak and is public domain. This derived mtxrun program -is an adapted version by Hans Hagen. +The originally 'runscript' program was written by in 2009 by T.M.Trzeciak and is +public domain. This derived mtxrun program is an adapted version by Hans Hagen and +Luigi Scarso. Comment: -In ConTeXt MkIV we have two core scripts: luatools.lua and -mtxrun.lua where the second one is used to launch other scripts. +In ConTeXt MkIV we have two core scripts: luatools.lua and mtxrun.lua where the +second one is used to launch other scripts. The mtxrun.exe program calls luatex.exe. + Normally a user will use a call like: -mtxrun --script font --reload + mtxrun --script font --reload + +Here mtxrun is a lua script. In order to avoid the usage of a cmd file on windows this +runner will start texlua directly. In TeXlive a runner is added for each cmd file but +we don't want that overhead (and extra files). By using an exe we can call these +scripts in batch files without the need for using call. + +The mtxrun.exe file can be copied to a mtxrunjit.exe file in which case luajittex.exe +is called. -Here mtxrun is a lua script. In order to avoid the usage of a cmd -file on windows this runner will start texlua directly. In TeXlive -a runner is added for each cmd file but we don't want that overhead -(and extra files). By using an exe we can call these scripts in -batch files without the need for using call. + mtxrunjit --script font --reload -We also don't want to use other runners, like those that use kpse -to locate the script as this is exactly what mtxrun itself is doing -already. Therefore the runscript program is adapted to a more direct -approach suitable for mtxrun. +We also don't want to use other runners, like those that use kpse to locate the script +as this is exactly what mtxrun itself is doing already. Therefore the runscript program +is adapted to a more direct approach suitable for mtxrun. Compilation: with gcc (size optimized): -gcc -Os -s -shared -o mtxrun.dll mtxrun_dll.c -gcc -Os -s -o mtxrun.exe mtxrun_exe.c -L./ -lmtxrun + gcc -Os -s -shared -o mtxrun.dll mtxrun_dll.c + gcc -Os -s -o mtxrun.exe mtxrun_exe.c -L./ -lmtxrun with tcc (ver. 0.9.24), extra small size -tcc -shared -o runscript.dll runscript_dll.c -tcc -o runscript.exe runscript_exe.c runscript.def + tcc -shared -o runscript.dll runscript_dll.c + tcc -o runscript.exe runscript_exe.c runscript.def diff --git a/scripts/context/stubs/unix/contextjit b/scripts/context/stubs/unix/contextjit new file mode 100644 index 000000000..5ac1947c7 --- /dev/null +++ b/scripts/context/stubs/unix/contextjit @@ -0,0 +1,5 @@ +#!/bin/sh + +luajittex --luaonly $(dirname $0)/mtxrun --script context "$@" + +# luajittex --luaonly ${0%contextjit}mtxrun --script context "$@" diff --git a/scripts/context/stubs/unix/ctxtools b/scripts/context/stubs/unix/ctxtools deleted file mode 100644 index 2e6bd4afa..000000000 --- a/scripts/context/stubs/unix/ctxtools +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -mtxrun --script ctxtools "$@" diff --git a/scripts/context/stubs/unix/mptopdf b/scripts/context/stubs/unix/mptopdf deleted file mode 100644 index 147333740..000000000 --- a/scripts/context/stubs/unix/mptopdf +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -mtxrun --script mptopdf "$@" diff --git a/scripts/context/stubs/unix/mtxrun b/scripts/context/stubs/unix/mtxrun index 0ff2d2897..edfeba8dd 100644 --- a/scripts/context/stubs/unix/mtxrun +++ b/scripts/context/stubs/unix/mtxrun @@ -56,7 +56,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-lua"] = package.loaded["l-lua"] or true --- original size: 3123, stripped down to: 1694 +-- original size: 3888, stripped down to: 2197 if not modules then modules={} end modules ['l-lua']={ version=1.001, @@ -136,6 +136,16 @@ function optionalrequire(...) return result end end +if lua then + lua.mask=load([[τεχ = 1]]) and "utf" or "ascii" +end +local flush=io.flush +if flush then + local execute=os.execute if execute then function os.execute(...) flush() return execute(...) end end + local exec=os.exec if exec then function os.exec (...) flush() return exec (...) end end + local spawn=os.spawn if spawn then function os.spawn (...) flush() return spawn (...) end end + local popen=io.popen if popen then function io.popen (...) flush() return popen (...) end end +end end -- of closure @@ -434,7 +444,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-lpeg"] = package.loaded["l-lpeg"] or true --- original size: 29245, stripped down to: 15964 +-- original size: 36977, stripped down to: 20349 if not modules then modules={} end modules ['l-lpeg']={ version=1.001, @@ -450,7 +460,9 @@ local byte,char,gmatch,format=string.byte,string.char,string.gmatch,string.forma local floor=math.floor local P,R,S,V,Ct,C,Cs,Cc,Cp,Cmt=lpeg.P,lpeg.R,lpeg.S,lpeg.V,lpeg.Ct,lpeg.C,lpeg.Cs,lpeg.Cc,lpeg.Cp,lpeg.Cmt local lpegtype,lpegmatch,lpegprint=lpeg.type,lpeg.match,lpeg.print -setinspector(function(v) if lpegtype(v) then lpegprint(v) return true end end) +if setinspector then + setinspector(function(v) if lpegtype(v) then lpegprint(v) return true end end) +end lpeg.patterns=lpeg.patterns or {} local patterns=lpeg.patterns local anything=P(1) @@ -469,7 +481,7 @@ local uppercase=R("AZ") local underscore=P("_") local hexdigit=digit+lowercase+uppercase local cr,lf,crlf=P("\r"),P("\n"),P("\r\n") -local newline=crlf+S("\r\n") +local newline=P("\r")*(P("\n")+P(true))+P("\n") local escaped=P("\\")*anything local squote=P("'") local dquote=P('"') @@ -491,8 +503,10 @@ patterns.utfbom_32_le=utfbom_32_le patterns.utfbom_16_be=utfbom_16_be patterns.utfbom_16_le=utfbom_16_le patterns.utfbom_8=utfbom_8 -patterns.utf_16_be_nl=P("\000\r\000\n")+P("\000\r")+P("\000\n") -patterns.utf_16_le_nl=P("\r\000\n\000")+P("\r\000")+P("\n\000") +patterns.utf_16_be_nl=P("\000\r\000\n")+P("\000\r")+P("\000\n") +patterns.utf_16_le_nl=P("\r\000\n\000")+P("\r\000")+P("\n\000") +patterns.utf_32_be_nl=P("\000\000\000\r\000\000\000\n")+P("\000\000\000\r")+P("\000\000\000\n") +patterns.utf_32_le_nl=P("\r\000\000\000\n\000\000\000")+P("\r\000\000\000")+P("\n\000\000\000") patterns.utf8one=R("\000\127") patterns.utf8two=R("\194\223")*utf8next patterns.utf8three=R("\224\239")*utf8next*utf8next @@ -519,10 +533,24 @@ patterns.spacer=spacer patterns.whitespace=whitespace patterns.nonspacer=nonspacer patterns.nonwhitespace=nonwhitespace -local stripper=spacer^0*C((spacer^0*nonspacer^1)^0) +local stripper=spacer^0*C((spacer^0*nonspacer^1)^0) +local fullstripper=whitespace^0*C((whitespace^0*nonwhitespace^1)^0) local collapser=Cs(spacer^0/""*nonspacer^0*((spacer^0/" "*nonspacer^1)^0)) +local b_collapser=Cs(whitespace^0/""*(nonwhitespace^1+whitespace^1/" ")^0) +local e_collapser=Cs((whitespace^1*P(-1)/""+nonwhitespace^1+whitespace^1/" ")^0) +local m_collapser=Cs((nonwhitespace^1+whitespace^1/" ")^0) +local b_stripper=Cs(spacer^0/""*(nonspacer^1+spacer^1/" ")^0) +local e_stripper=Cs((spacer^1*P(-1)/""+nonspacer^1+spacer^1/" ")^0) +local m_stripper=Cs((nonspacer^1+spacer^1/" ")^0) patterns.stripper=stripper +patterns.fullstripper=fullstripper patterns.collapser=collapser +patterns.b_collapser=b_collapser +patterns.m_collapser=m_collapser +patterns.e_collapser=e_collapser +patterns.b_stripper=b_stripper +patterns.m_stripper=m_stripper +patterns.e_stripper=e_stripper patterns.lowercase=lowercase patterns.uppercase=uppercase patterns.letter=patterns.lowercase+patterns.uppercase @@ -559,9 +587,12 @@ patterns.integer=sign^-1*digit^1 patterns.unsigned=digit^0*period*digit^1 patterns.float=sign^-1*patterns.unsigned patterns.cunsigned=digit^0*comma*digit^1 +patterns.cpunsigned=digit^0*(period+comma)*digit^1 patterns.cfloat=sign^-1*patterns.cunsigned +patterns.cpfloat=sign^-1*patterns.cpunsigned patterns.number=patterns.float+patterns.integer patterns.cnumber=patterns.cfloat+patterns.integer +patterns.cpnumber=patterns.cpfloat+patterns.integer patterns.oct=zero*octdigit^1 patterns.octal=patterns.oct patterns.HEX=zero*P("X")*(digit+uppercase)^1 @@ -744,7 +775,7 @@ function lpeg.replacer(one,two,makefunction,isutf) return pattern end end -function lpeg.finder(lst,makefunction) +function lpeg.finder(lst,makefunction,isutf) local pattern if type(lst)=="table" then pattern=P(false) @@ -760,7 +791,11 @@ function lpeg.finder(lst,makefunction) else pattern=P(lst) end - pattern=(1-pattern)^0*pattern + if isutf then + pattern=((utf8char or 1)-pattern)^0*pattern + else + pattern=(1-pattern)^0*pattern + end if makefunction then return function(str) return lpegmatch(pattern,str) @@ -974,37 +1009,139 @@ function lpeg.append(list,pp,delayed,checked) end return p end +local p_false=P(false) +local p_true=P(true) local function make(t) - local p + local function making(t) + local p=p_false + local keys=sortedkeys(t) + for i=1,#keys do + local k=keys[i] + if k~="" then + local v=t[k] + if v==true then + p=p+P(k)*p_true + elseif v==false then + else + p=p+P(k)*making(v) + end + end + end + if t[""] then + p=p+p_true + end + return p + end + local p=p_false local keys=sortedkeys(t) for i=1,#keys do local k=keys[i] - local v=t[k] - if not p then - if next(v) then - p=P(k)*make(v) + if k~="" then + local v=t[k] + if v==true then + p=p+P(k)*p_true + elseif v==false then else - p=P(k) + p=p+P(k)*making(v) end - else - if next(v) then - p=p+P(k)*make(v) + end + end + return p +end +local function collapse(t,x) + if type(t)~="table" then + return t,x + else + local n=next(t) + if n==nil then + return t,x + elseif next(t,n)==nil then + local k=n + local v=t[k] + if type(v)=="table" then + return collapse(v,x..k) else - p=p+P(k) + return v,x..k + end + else + local tt={} + for k,v in next,t do + local vv,kk=collapse(v,k) + tt[kk]=vv end + return tt,x end end - return p end function lpeg.utfchartabletopattern(list) local tree={} - for i=1,#list do - local t=tree - for c in gmatch(list[i],".") do - if not t[c] then - t[c]={} + local n=#list + if n==0 then + for s in next,list do + local t=tree + local p,pk + for c in gmatch(s,".") do + if t==true then + t={ [c]=true,[""]=true } + p[pk]=t + p=t + t=false + elseif t==false then + t={ [c]=false } + p[pk]=t + p=t + t=false + else + local tc=t[c] + if not tc then + tc=false + t[c]=false + end + p=t + t=tc + end + pk=c + end + if t==false then + p[pk]=true + elseif t==true then + else + t[""]=true + end + end + else + for i=1,n do + local s=list[i] + local t=tree + local p,pk + for c in gmatch(s,".") do + if t==true then + t={ [c]=true,[""]=true } + p[pk]=t + p=t + t=false + elseif t==false then + t={ [c]=false } + p[pk]=t + p=t + t=false + else + local tc=t[c] + if not tc then + tc=false + t[c]=false + end + p=t + t=tc + end + pk=c + end + if t==false then + p[pk]=true + elseif t==true then + else + t[""]=true end - t=t[c] end end return make(tree) @@ -1044,6 +1181,65 @@ local case_2=period*(digit-trailingzeros)^1*(trailingzeros/"") local number=digit^1*(case_1+case_2) local stripper=Cs((number+1)^0) lpeg.patterns.stripzeros=stripper +local byte_to_HEX={} +local byte_to_hex={} +local byte_to_dec={} +local hex_to_byte={} +for i=0,255 do + local H=format("%02X",i) + local h=format("%02x",i) + local d=format("%03i",i) + local c=char(i) + byte_to_HEX[c]=H + byte_to_hex[c]=h + byte_to_dec[c]=d + hex_to_byte[h]=c + hex_to_byte[H]=c +end +local hextobyte=P(2)/hex_to_byte +local bytetoHEX=P(1)/byte_to_HEX +local bytetohex=P(1)/byte_to_hex +local bytetodec=P(1)/byte_to_dec +local hextobytes=Cs(hextobyte^0) +local bytestoHEX=Cs(bytetoHEX^0) +local bytestohex=Cs(bytetohex^0) +local bytestodec=Cs(bytetodec^0) +patterns.hextobyte=hextobyte +patterns.bytetoHEX=bytetoHEX +patterns.bytetohex=bytetohex +patterns.bytetodec=bytetodec +patterns.hextobytes=hextobytes +patterns.bytestoHEX=bytestoHEX +patterns.bytestohex=bytestohex +patterns.bytestodec=bytestodec +function string.toHEX(s) + if not s or s=="" then + return s + else + return lpegmatch(bytestoHEX,s) + end +end +function string.tohex(s) + if not s or s=="" then + return s + else + return lpegmatch(bytestohex,s) + end +end +function string.todec(s) + if not s or s=="" then + return s + else + return lpegmatch(bytestodec,s) + end +end +function string.tobytes(s) + if not s or s=="" then + return s + else + return lpegmatch(hextobytes,s) + end +end end -- of closure @@ -1071,7 +1267,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-string"] = package.loaded["l-string"] or true --- original size: 5547, stripped down to: 2708 +-- original size: 5694, stripped down to: 2827 if not modules then modules={} end modules ['l-string']={ version=1.001, @@ -1107,11 +1303,15 @@ function string.limit(str,n,sentinel) end end local stripper=patterns.stripper +local fullstripper=patterns.fullstripper local collapser=patterns.collapser local longtostring=patterns.longtostring function string.strip(str) return lpegmatch(stripper,str) or "" end +function string.fullstrip(str) + return lpegmatch(fullstripper,str) or "" +end function string.collapsespaces(str) return lpegmatch(collapser,str) or "" end @@ -1172,7 +1372,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-table"] = package.loaded["l-table"] or true --- original size: 31113, stripped down to: 20256 +-- original size: 35724, stripped down to: 21525 if not modules then modules={} end modules ['l-table']={ version=1.001, @@ -1205,7 +1405,7 @@ end function table.keys(t) if t then local keys,k={},0 - for key,_ in next,t do + for key in next,t do k=k+1 keys[k]=key end @@ -1215,32 +1415,52 @@ function table.keys(t) end end local function compare(a,b) - local ta,tb=type(a),type(b) - if ta==tb then - return a<b - else - return tostring(a)<tostring(b) + local ta=type(a) + if ta=="number" then + local tb=type(b) + if ta==tb then + return a<b + elseif tb=="string" then + return tostring(a)<b + end + elseif ta=="string" then + local tb=type(b) + if ta==tb then + return a<b + else + return a<tostring(b) + end end + return tostring(a)<tostring(b) end local function sortedkeys(tab) if tab then local srt,category,s={},0,0 - for key,_ in next,tab do + for key in next,tab do s=s+1 srt[s]=key if category==3 then + elseif category==1 then + if type(key)~="string" then + category=3 + end + elseif category==2 then + if type(key)~="number" then + category=3 + end else local tkey=type(key) if tkey=="string" then - category=(category==2 and 3) or 1 + category=1 elseif tkey=="number" then - category=(category==1 and 3) or 2 + category=2 else category=3 end end end - if category==0 or category==3 then + if s<2 then + elseif category==3 then sort(srt,compare) else sort(srt) @@ -1250,16 +1470,52 @@ local function sortedkeys(tab) return {} end end +local function sortedhashonly(tab) + if tab then + local srt,s={},0 + for key in next,tab do + if type(key)=="string" then + s=s+1 + srt[s]=key + end + end + if s>1 then + sort(srt) + end + return srt + else + return {} + end +end +local function sortedindexonly(tab) + if tab then + local srt,s={},0 + for key in next,tab do + if type(key)=="number" then + s=s+1 + srt[s]=key + end + end + if s>1 then + sort(srt) + end + return srt + else + return {} + end +end local function sortedhashkeys(tab,cmp) if tab then local srt,s={},0 - for key,_ in next,tab do + for key in next,tab do if key then s=s+1 srt[s]=key end end - sort(srt,cmp) + if s>1 then + sort(srt,cmp) + end return srt else return {} @@ -1268,13 +1524,15 @@ end function table.allkeys(t) local keys={} for k,v in next,t do - for k,v in next,v do + for k in next,v do keys[k]=true end end return sortedkeys(keys) end table.sortedkeys=sortedkeys +table.sortedhashonly=sortedhashonly +table.sortedindexonly=sortedindexonly table.sortedhashkeys=sortedhashkeys local function nothing() end local function sortedhash(t,cmp) @@ -1285,19 +1543,21 @@ local function sortedhash(t,cmp) else s=sortedkeys(t) end - local n=0 local m=#s - local function kv(s) - if n<m then - n=n+1 - local k=s[n] - return k,t[k] + if m==1 then + return next,t + elseif m>0 then + local n=0 + return function() + if n<m then + n=n+1 + local k=s[n] + return k,t[k] + end end end - return kv,s - else - return nothing end + return nothing end table.sortedhash=sortedhash table.sortedpairs=sortedhash @@ -1439,39 +1699,36 @@ function table.fromhash(t) end return hsh end -local noquotes,hexify,handle,reduce,compact,inline,functions +local noquotes,hexify,handle,compact,inline,functions local reserved=table.tohash { 'and','break','do','else','elseif','end','false','for','function','if', 'in','local','nil','not','or','repeat','return','then','true','until','while', 'NaN','goto', } local function simple_table(t) - if #t>0 then + local nt=#t + if nt>0 then local n=0 for _,v in next,t do n=n+1 end - if n==#t then - local tt,nt={},0 - for i=1,#t do + if n==nt then + local tt={} + for i=1,nt do local v=t[i] local tv=type(v) if tv=="number" then - nt=nt+1 if hexify then - tt[nt]=format("0x%04X",v) + tt[i]=format("0x%X",v) else - tt[nt]=tostring(v) + tt[i]=tostring(v) end elseif tv=="string" then - nt=nt+1 - tt[nt]=format("%q",v) + tt[i]=format("%q",v) elseif tv=="boolean" then - nt=nt+1 - tt[nt]=v and "true" or "false" + tt[i]=v and "true" or "false" else - tt=nil - break + return nil end end return tt @@ -1490,7 +1747,7 @@ local function do_serialize(root,name,depth,level,indexed) local tn=type(name) if tn=="number" then if hexify then - handle(format("%s[0x%04X]={",depth,name)) + handle(format("%s[0x%X]={",depth,name)) else handle(format("%s[%s]={",depth,name)) end @@ -1507,7 +1764,7 @@ local function do_serialize(root,name,depth,level,indexed) end end end - if root and next(root) then + if root and next(root)~=nil then local first,last=nil,0 if compact then last=#root @@ -1525,22 +1782,19 @@ local function do_serialize(root,name,depth,level,indexed) for i=1,#sk do local k=sk[i] local v=root[k] - local tv,tk=type(v),type(k) + local tv=type(v) + local tk=type(k) if compact and first and tk=="number" and k>=first and k<=last then if tv=="number" then if hexify then - handle(format("%s 0x%04X,",depth,v)) + handle(format("%s 0x%X,",depth,v)) else handle(format("%s %s,",depth,v)) end elseif tv=="string" then - if reduce and tonumber(v) then - handle(format("%s %s,",depth,v)) - else - handle(format("%s %q,",depth,v)) - end + handle(format("%s %q,",depth,v)) elseif tv=="table" then - if not next(v) then + if next(v)==nil then handle(format("%s {},",depth)) elseif inline then local st=simple_table(v) @@ -1570,64 +1824,48 @@ local function do_serialize(root,name,depth,level,indexed) elseif tv=="number" then if tk=="number" then if hexify then - handle(format("%s [0x%04X]=0x%04X,",depth,k,v)) + handle(format("%s [0x%X]=0x%X,",depth,k,v)) else handle(format("%s [%s]=%s,",depth,k,v)) end elseif tk=="boolean" then if hexify then - handle(format("%s [%s]=0x%04X,",depth,k and "true" or "false",v)) + handle(format("%s [%s]=0x%X,",depth,k and "true" or "false",v)) else handle(format("%s [%s]=%s,",depth,k and "true" or "false",v)) end elseif noquotes and not reserved[k] and lpegmatch(propername,k) then if hexify then - handle(format("%s %s=0x%04X,",depth,k,v)) + handle(format("%s %s=0x%X,",depth,k,v)) else handle(format("%s %s=%s,",depth,k,v)) end else if hexify then - handle(format("%s [%q]=0x%04X,",depth,k,v)) + handle(format("%s [%q]=0x%X,",depth,k,v)) else handle(format("%s [%q]=%s,",depth,k,v)) end end elseif tv=="string" then - if reduce and tonumber(v) then - if tk=="number" 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,k and "true" or "false",v)) - elseif noquotes and not reserved[k] and lpegmatch(propername,k) then - handle(format("%s %s=%s,",depth,k,v)) + if tk=="number" then + if hexify then + handle(format("%s [0x%X]=%q,",depth,k,v)) else - handle(format("%s [%q]=%s,",depth,k,v)) + handle(format("%s [%s]=%q,",depth,k,v)) end + elseif tk=="boolean" then + handle(format("%s [%s]=%q,",depth,k and "true" or "false",v)) + elseif noquotes and not reserved[k] and lpegmatch(propername,k) then + handle(format("%s %s=%q,",depth,k,v)) else - if tk=="number" 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,k and "true" or "false",v)) - elseif noquotes and not reserved[k] and lpegmatch(propername,k) then - handle(format("%s %s=%q,",depth,k,v)) - else - handle(format("%s [%q]=%q,",depth,k,v)) - end + handle(format("%s [%q]=%q,",depth,k,v)) end elseif tv=="table" then - if not next(v) then + if next(v)==nil then if tk=="number" then if hexify then - handle(format("%s [0x%04X]={},",depth,k)) + handle(format("%s [0x%X]={},",depth,k)) else handle(format("%s [%s]={},",depth,k)) end @@ -1643,7 +1881,7 @@ local function do_serialize(root,name,depth,level,indexed) if st then if tk=="number" then if hexify then - handle(format("%s [0x%04X]={ %s },",depth,k,concat(st,", "))) + handle(format("%s [0x%X]={ %s },",depth,k,concat(st,", "))) else handle(format("%s [%s]={ %s },",depth,k,concat(st,", "))) end @@ -1663,7 +1901,7 @@ local function do_serialize(root,name,depth,level,indexed) elseif tv=="boolean" then if tk=="number" then if hexify then - handle(format("%s [0x%04X]=%s,",depth,k,v and "true" or "false")) + handle(format("%s [0x%X]=%s,",depth,k,v and "true" or "false")) else handle(format("%s [%s]=%s,",depth,k,v and "true" or "false")) end @@ -1679,7 +1917,7 @@ local function do_serialize(root,name,depth,level,indexed) local f=getinfo(v).what=="C" and dump(dummy) or dump(v) if tk=="number" then if hexify then - handle(format("%s [0x%04X]=load(%q),",depth,k,f)) + handle(format("%s [0x%X]=load(%q),",depth,k,f)) else handle(format("%s [%s]=load(%q),",depth,k,f)) end @@ -1694,7 +1932,7 @@ local function do_serialize(root,name,depth,level,indexed) else if tk=="number" then if hexify then - handle(format("%s [0x%04X]=%q,",depth,k,tostring(v))) + handle(format("%s [0x%X]=%q,",depth,k,tostring(v))) else handle(format("%s [%s]=%q,",depth,k,tostring(v))) end @@ -1718,7 +1956,6 @@ local function serialize(_handle,root,name,specification) 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 @@ -1735,7 +1972,6 @@ local function serialize(_handle,root,name,specification) noquotes=false hexify=false handle=_handle or print - reduce=false compact=true inline=true functions=true @@ -1748,7 +1984,7 @@ local function serialize(_handle,root,name,specification) end elseif tname=="number" then if hexify then - handle(format("[0x%04X]={",name)) + handle(format("[0x%X]={",name)) else handle("["..name.."]={") end @@ -1766,7 +2002,7 @@ local function serialize(_handle,root,name,specification) local dummy=root._w_h_a_t_e_v_e_r_ root._w_h_a_t_e_v_e_r_=nil end - if next(root) then + if next(root)~=nil then do_serialize(root,name,"",0) end end @@ -1895,14 +2131,25 @@ local function identical(a,b) end table.identical=identical table.are_equal=are_equal -function table.compact(t) - if t then - for k,v in next,t do - if not next(v) then - t[k]=nil +local function sparse(old,nest,keeptables) + local new={} + for k,v in next,old do + if not (v=="" or v==false) then + if nest and type(v)=="table" then + v=sparse(v,nest) + if keeptables or next(v)~=nil then + new[k]=v + end + else + new[k]=v end end end + return new +end +table.sparse=sparse +function table.compact(t) + return sparse(t,true,true) end function table.contains(t,v) if t then @@ -2000,15 +2247,17 @@ function table.print(t,...) serialize(print,t,...) end end -setinspector(function(v) if type(v)=="table" then serialize(print,v,"table") return true end end) +if setinspector then + setinspector(function(v) if type(v)=="table" then serialize(print,v,"table") return true end end) +end function table.sub(t,i,j) return { unpack(t,i,j) } end function table.is_empty(t) - return not t or not next(t) + return not t or next(t)==nil end function table.has_one_entry(t) - return t and not next(t,next(t)) + return t and next(t,next(t))==nil end function table.loweredkeys(t) local l={} @@ -2053,6 +2302,44 @@ function table.values(t,s) return {} end end +function table.filtered(t,pattern,sort,cmp) + if t and type(pattern)=="string" then + if sort then + local s + if cmp then + s=sortedhashkeys(t,function(a,b) return cmp(t,a,b) end) + else + s=sortedkeys(t) + end + local n=0 + local m=#s + local function kv(s) + while n<m do + n=n+1 + local k=s[n] + if find(k,pattern) then + return k,t[k] + end + end + end + return kv,s + else + local n=next(t) + local function iterator() + while n~=nil do + local k=n + n=next(t,k) + if find(k,pattern) then + return k,t[k] + end + end + end + return iterator,t + end + else + return nothing + end +end end -- of closure @@ -2061,7 +2348,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-io"] = package.loaded["l-io"] or true --- original size: 8817, stripped down to: 6340 +-- original size: 8643, stripped down to: 6232 if not modules then modules={} end modules ['l-io']={ version=1.001, @@ -2075,7 +2362,7 @@ local byte,find,gsub,format=string.byte,string.find,string.gsub,string.format local concat=table.concat local floor=math.floor local type=type -if string.find(os.getenv("PATH"),";") then +if string.find(os.getenv("PATH"),";",1,true) then io.fileseparator,io.pathseparator="\\",";" else io.fileseparator,io.pathseparator="/",":" @@ -2368,8 +2655,6 @@ function io.readstring(f,n,m) local str=gsub(f:read(n),"\000","") return str end -if not io.i_limiter then function io.i_limiter() end end -if not io.o_limiter then function io.o_limiter() end end end -- of closure @@ -2596,7 +2881,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-os"] = package.loaded["l-os"] or true --- original size: 16023, stripped down to: 9634 +-- original size: 15832, stripped down to: 9456 if not modules then modules={} end modules ['l-os']={ version=1.001, @@ -2670,13 +2955,10 @@ if not os.__getenv__ then setmetatable(os.env,{ __index=__index,__newindex=__newindex } ) end end -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 +local execute=os.execute +local iopopen=io.popen function os.resultof(command) - local handle=io.popen(command,"r") + local handle=iopopen(command,"r") if handle then local result=handle:read("*all") or "" handle:close() @@ -2686,7 +2968,7 @@ function os.resultof(command) end end if not io.fileseparator then - if find(os.getenv("PATH"),";") then + if find(os.getenv("PATH"),";",1,true) then io.fileseparator,io.pathseparator,os.type="\\",";",os.type or "mswin" else io.fileseparator,io.pathseparator,os.type="/",":",os.type or "unix" @@ -2705,7 +2987,7 @@ local launchers={ unix="$BROWSER %s &> /dev/null &", } function os.launch(str) - os.execute(format(launchers[os.name] or launchers.unix,str)) + execute(format(launchers[os.name] or launchers.unix,str)) end if not os.times then function os.times() @@ -2746,7 +3028,7 @@ if platform~="" then elseif os.type=="windows" then function resolvers.platform(t,k) local platform,architecture="",os.getenv("PROCESSOR_ARCHITECTURE") or "" - if find(architecture,"AMD64") then + if find(architecture,"AMD64",1,true) then platform="win64" else platform="mswin" @@ -2758,9 +3040,9 @@ elseif os.type=="windows" then elseif name=="linux" then function resolvers.platform(t,k) local platform,architecture="",os.getenv("HOSTTYPE") or os.resultof("uname -m") or "" - if find(architecture,"x86_64") then + if find(architecture,"x86_64",1,true) then platform="linux-64" - elseif find(architecture,"ppc") then + elseif find(architecture,"ppc",1,true) then platform="linux-ppc" else platform="linux" @@ -2774,9 +3056,9 @@ elseif name=="macosx" then local platform,architecture="",os.resultof("echo $HOSTTYPE") or "" if architecture=="" then platform="osx-intel" - elseif find(architecture,"i386") then + elseif find(architecture,"i386",1,true) then platform="osx-intel" - elseif find(architecture,"x86_64") then + elseif find(architecture,"x86_64",1,true) then platform="osx-64" else platform="osx-ppc" @@ -2788,7 +3070,7 @@ elseif name=="macosx" then elseif name=="sunos" then function resolvers.platform(t,k) local platform,architecture="",os.resultof("uname -m") or "" - if find(architecture,"sparc") then + if find(architecture,"sparc",1,true) then platform="solaris-sparc" else platform="solaris-intel" @@ -2800,7 +3082,7 @@ elseif name=="sunos" then elseif name=="freebsd" then function resolvers.platform(t,k) local platform,architecture="",os.resultof("uname -m") or "" - if find(architecture,"amd64") then + if find(architecture,"amd64",1,true) then platform="freebsd-amd64" else platform="freebsd" @@ -2812,7 +3094,7 @@ elseif name=="freebsd" then elseif name=="kfreebsd" then function resolvers.platform(t,k) local platform,architecture="",os.getenv("HOSTTYPE") or os.resultof("uname -m") or "" - if find(architecture,"x86_64") then + if find(architecture,"x86_64",1,true) then platform="kfreebsd-amd64" else platform="kfreebsd-i386" @@ -2829,8 +3111,9 @@ else return platform end end +os.newline=name=="windows" and "\013\010" or "\010" function resolvers.bits(t,k) - local bits=find(os.platform,"64") and 64 or 32 + local bits=find(os.platform,"64",1,true) and 64 or 32 os.bits=bits return bits end @@ -2980,7 +3263,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-file"] = package.loaded["l-file"] or true --- original size: 18308, stripped down to: 9948 +-- original size: 20949, stripped down to: 9945 if not modules then modules={} end modules ['l-file']={ version=1.001, @@ -2994,41 +3277,28 @@ local file=file if not lfs then lfs=optionalrequire("lfs") end -if not lfs then - lfs={ - getcurrentdir=function() - return "." - end, - attributes=function() - return nil - end, - isfile=function(name) - local f=io.open(name,'rb') - if f then - f:close() - return true - end - end, - isdir=function(name) - print("you need to load lfs") - return false - end - } -elseif not lfs.isfile then - local attributes=lfs.attributes - function lfs.isdir(name) - return attributes(name,"mode")=="directory" - end - function lfs.isfile(name) - return attributes(name,"mode")=="file" - end -end local insert,concat=table.insert,table.concat local match,find,gmatch=string.match,string.find,string.gmatch local lpegmatch=lpeg.match local getcurrentdir,attributes=lfs.currentdir,lfs.attributes local checkedsplit=string.checkedsplit local P,R,S,C,Cs,Cp,Cc,Ct=lpeg.P,lpeg.R,lpeg.S,lpeg.C,lpeg.Cs,lpeg.Cp,lpeg.Cc,lpeg.Ct +local tricky=S("/\\")*P(-1) +local attributes=lfs.attributes +if sandbox then + sandbox.redefine(lfs.isfile,"lfs.isfile") + sandbox.redefine(lfs.isdir,"lfs.isdir") +end +function lfs.isdir(name) + if lpegmatch(tricky,name) then + return attributes(name,"mode")=="directory" + else + return attributes(name.."/.","mode")=="directory" + end +end +function lfs.isfile(name) + return attributes(name,"mode")=="file" +end local colon=P(":") local period=P(".") local periods=P("..") @@ -3230,28 +3500,30 @@ local isroot=fwslash^1*-1 local hasroot=fwslash^1 local reslasher=lpeg.replacer(S("\\/"),"/") local deslasher=lpeg.replacer(S("\\/")^1,"/") -function file.join(...) - local lst={... } - local one=lst[1] +function file.join(one,two,three,...) + if not two then + return one=="" and one or lpegmatch(stripper,one) + end + if one=="" then + return lpegmatch(stripper,three and concat({ two,three,... },"/") or two) + end if lpegmatch(isnetwork,one) then local one=lpegmatch(reslasher,one) - local two=lpegmatch(deslasher,concat(lst,"/",2)) + local two=lpegmatch(deslasher,three and concat({ two,three,... },"/") or two) if lpegmatch(hasroot,two) then return one..two else return one.."/"..two end elseif lpegmatch(isroot,one) then - local two=lpegmatch(deslasher,concat(lst,"/",2)) + local two=lpegmatch(deslasher,three and concat({ two,three,... },"/") or two) if lpegmatch(hasroot,two) then return two else return "/"..two end - elseif one=="" then - return lpegmatch(stripper,concat(lst,"/",2)) else - return lpegmatch(deslasher,concat(lst,"/")) + return lpegmatch(deslasher,concat({ one,two,three,... },"/")) end end local drivespec=R("az","AZ")^1*colon @@ -3425,7 +3697,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-md5"] = package.loaded["l-md5"] or true --- original size: 3760, stripped down to: 2088 +-- original size: 3248, stripped down to: 2266 if not modules then modules={} end modules ['l-md5']={ version=1.001, @@ -3443,14 +3715,20 @@ if not md5 then } end local md5,file=md5,file -local gsub,format,byte=string.gsub,string.format,string.byte -local md5sum=md5.sum -local function convert(str,fmt) - return (gsub(md5sum(str),".",function(chr) return format(fmt,byte(chr)) end)) -end -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 +local gsub=string.gsub +do + local patterns=lpeg and lpeg.patterns + if patterns then + local bytestoHEX=patterns.bytestoHEX + local bytestohex=patterns.bytestohex + local bytestodec=patterns.bytestodec + local lpegmatch=lpeg.match + local md5sum=md5.sum + if not md5.HEX then function md5.HEX(str) if str then return lpegmatch(bytestoHEX,md5sum(str)) end end end + if not md5.hex then function md5.hex(str) if str then return lpegmatch(bytestohex,md5sum(str)) end end end + if not md5.dec then function md5.dec(str) if str then return lpegmatch(bytestodec,md5sum(str)) end end end + end +end function file.needsupdating(oldname,newname,threshold) local oldtime=lfs.attributes(oldname,"modification") if oldtime then @@ -3507,7 +3785,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-url"] = package.loaded["l-url"] or true --- original size: 11993, stripped down to: 5584 +-- original size: 12531, stripped down to: 5721 if not modules then modules={} end modules ['l-url']={ version=1.001, @@ -3534,7 +3812,7 @@ local hexdigit=R("09","AF","af") local plus=P("+") local nothing=Cc("") local escapedchar=(percent*C(hexdigit*hexdigit))/tochar -local escaped=(plus/" ")+escapedchar +local escaped=(plus/" ")+escapedchar local noslash=P("/")/"" local schemestr=Cs((escaped+(1-colon-slash-qmark-hash))^2) local authoritystr=Cs((escaped+(1- slash-qmark-hash))^0) @@ -3593,19 +3871,25 @@ local splitquery=Cf (Ct("")*P { "sequence", pair=Cg(key*equal*value), },rawset) local function hashed(str) - if str=="" then + if not str or str=="" then return { scheme="invalid", original=str, } end - local s=split(str) - local rawscheme=s[1] - local rawquery=s[4] - local somescheme=rawscheme~="" - local somequery=rawquery~="" + local detailed=split(str) + local rawscheme="" + local rawquery="" + local somescheme=false + local somequery=false + if detailed then + rawscheme=detailed[1] + rawquery=detailed[4] + somescheme=rawscheme~="" + somequery=rawquery~="" + end if not somescheme and not somequery then - s={ + return { scheme="file", authority="", path=str, @@ -3615,28 +3899,28 @@ local function hashed(str) noscheme=true, filename=str, } - else - local authority,path,filename=s[2],s[3] - if authority=="" then - filename=path - elseif path=="" then - filename="" - else - filename=authority.."/"..path - end - s={ - scheme=rawscheme, - authority=authority, - path=path, - query=lpegmatch(unescaper,rawquery), - queries=lpegmatch(splitquery,rawquery), - fragment=s[5], - original=str, - noscheme=false, - filename=filename, - } end - return s + local authority=detailed[2] + local path=detailed[3] + local filename=nil + if authority=="" then + filename=path + elseif path=="" then + filename="" + else + filename=authority.."/"..path + end + return { + scheme=rawscheme, + authority=authority, + path=path, + query=lpegmatch(unescaper,rawquery), + queries=lpegmatch(splitquery,rawquery), + fragment=detailed[5], + original=str, + noscheme=false, + filename=filename, + } end url.split=split url.hasscheme=hasscheme @@ -3670,7 +3954,7 @@ function url.construct(hash) end return lpegmatch(escaper,concat(fullurl)) end -local pattern=Cs(noslash*R("az","AZ")*(S(":|")/":")*noslash*P(1)^0) +local pattern=Cs(slash^-1/""*R("az","AZ")*((S(":|")/":")+P(":"))*slash*P(1)^0) function url.filename(filename) local spec=hashed(filename) local path=spec.path @@ -3718,7 +4002,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-dir"] = package.loaded["l-dir"] or true --- original size: 14229, stripped down to: 8740 +-- original size: 16765, stripped down to: 11003 if not modules then modules={} end modules ['l-dir']={ version=1.001, @@ -3728,7 +4012,7 @@ if not modules then modules={} end modules ['l-dir']={ license="see context related readme files" } local type,select=type,select -local find,gmatch,match,gsub=string.find,string.gmatch,string.match,string.gsub +local find,gmatch,match,gsub,sub=string.find,string.gmatch,string.match,string.gsub,string.sub local concat,insert,remove,unpack=table.concat,table.insert,table.remove,table.unpack local lpegmatch=lpeg.match local P,S,R,C,Cc,Cs,Ct,Cv,V=lpeg.P,lpeg.S,lpeg.R,lpeg.C,lpeg.Cc,lpeg.Cs,lpeg.Ct,lpeg.Cv,lpeg.V @@ -3737,53 +4021,127 @@ local dir=dir local lfs=lfs local attributes=lfs.attributes local walkdir=lfs.dir -local isdir=lfs.isdir -local isfile=lfs.isfile +local isdir=lfs.isdir +local isfile=lfs.isfile local currentdir=lfs.currentdir local chdir=lfs.chdir -local onwindows=os.type=="windows" or find(os.getenv("PATH"),";") -if not isdir then - function isdir(name) - local a=attributes(name) - return a and a.mode=="directory" +local mkdir=lfs.mkdir +local onwindows=os.type=="windows" or find(os.getenv("PATH"),";",1,true) +if onwindows then + local tricky=S("/\\")*P(-1) + isdir=function(name) + if lpegmatch(tricky,name) then + return attributes(name,"mode")=="directory" + else + return attributes(name.."/.","mode")=="directory" + end + end + isfile=function(name) + return attributes(name,"mode")=="file" end lfs.isdir=isdir -end -if not isfile then - function isfile(name) - local a=attributes(name) - return a and a.mode=="file" + lfs.isfile=isfile +else + isdir=function(name) + return attributes(name,"mode")=="directory" end + isfile=function(name) + return attributes(name,"mode")=="file" + end + lfs.isdir=isdir lfs.isfile=isfile end function dir.current() return (gsub(currentdir(),"\\","/")) end -local lfsisdir=isdir -local function isdir(path) - path=gsub(path,"[/\\]+$","") - return lfsisdir(path) +local function glob_pattern_function(path,patt,recurse,action) + if isdir(path) then + local usedpath + if path=="/" then + usedpath="/." + elseif not find(path,"/$") then + usedpath=path.."/." + path=path.."/" + else + usedpath=path + end + local dirs + for name in walkdir(usedpath) do + if name~="." and name~=".." then + local full=path..name + local mode=attributes(full,'mode') + if mode=='file' then + if not patt or find(full,patt) then + action(full) + end + elseif recurse and mode=="directory" then + if not dirs then + dirs={ full } + else + dirs[#dirs+1]=full + end + end + end + end + if dirs then + for i=1,#dirs do + glob_pattern_function(dirs[i],patt,recurse,action) + end + end + end end -lfs.isdir=isdir -local function globpattern(path,patt,recurse,action) - if path=="/" then - path=path.."." - elseif not find(path,"/$") then - path=path..'/' - end - if isdir(path) then - for name in walkdir(path) do - local full=path..name - local mode=attributes(full,'mode') - if mode=='file' then - if find(full,patt) then - action(full) +local function glob_pattern_table(path,patt,recurse,result) + if not result then + result={} + end + if isdir(path) then + local usedpath + if path=="/" then + usedpath="/." + elseif not find(path,"/$") then + usedpath=path.."/." + path=path.."/" + else + usedpath=path + end + local dirs + for name in walkdir(usedpath) do + if name~="." and name~=".." then + local full=path..name + local mode=attributes(full,'mode') + if mode=='file' then + if not patt or find(full,patt) then + result[#result+1]=full + end + elseif recurse and mode=="directory" then + if not dirs then + dirs={ full } + else + dirs[#dirs+1]=full + end end - elseif recurse and (mode=="directory") and (name~='.') and (name~="..") then - globpattern(full,patt,recurse,action) + end + end + if dirs then + for i=1,#dirs do + glob_pattern_table(dirs[i],patt,recurse,result) end end end + return result +end +local function globpattern(path,patt,recurse,method) + local kind=type(method) + if patt and sub(patt,1,-3)==path then + patt=false + end + if kind=="function" then + return glob_pattern_function(path,patt,recurse,method) + elseif kind=="table" then + return glob_pattern_table(path,patt,recurse,method) + else + return glob_pattern_table(path,patt,recurse,{}) + end end dir.globpattern=globpattern local function collectpattern(path,patt,recurse,result) @@ -3795,34 +4153,40 @@ local function collectpattern(path,patt,recurse,result) ok,scanner,first=xpcall(function() return walkdir(path) end,function() end) end if ok and type(scanner)=="function" then - if not find(path,"/$") then path=path..'/' end + if not find(path,"/$") then + path=path..'/' + end for name in scanner,first do - local full=path..name - local attr=attributes(full) - local mode=attr.mode - if mode=='file' then - if find(full,patt) then + if name=="." then + elseif name==".." then + else + local full=path..name + local attr=attributes(full) + local mode=attr.mode + if mode=='file' then + if find(full,patt) then + result[name]=attr + end + elseif recurse and mode=="directory" then + attr.list=collectpattern(full,patt,recurse) result[name]=attr end - elseif recurse and (mode=="directory") and (name~='.') and (name~="..") then - attr.list=collectpattern(full,patt,recurse) - result[name]=attr end end end return result end dir.collectpattern=collectpattern -local separator -if onwindows then +local separator,pattern +if onwindows then local slash=S("/\\")/"/" - pattern=Ct { + pattern={ [1]=(Cs(P(".")+slash^1)+Cs(R("az","AZ")*P(":")*slash^0)+Cc("./"))*V(2)*V(3), [2]=Cs(((1-S("*?/\\"))^0*slash)^0), [3]=Cs(P(1)^0) } -else - pattern=Ct { +else + pattern={ [1]=(C(P(".")+P("/")^1)+Cc("./"))*V(2)*V(3), [2]=C(((1-S("*?/"))^0*P("/"))^0), [3]=C(P(1)^0) @@ -3840,10 +4204,9 @@ local function glob(str,t) elseif isfile(str) then t(str) else - local split=lpegmatch(pattern,str) - if split then - local root,path,base=split[1],split[2],split[3] - local recurse=find(base,"%*%*") + local root,path,base=lpegmatch(pattern,str) + if root and path and base then + local recurse=find(base,"**",1,true) local start=root..path local result=lpegmatch(filter,start..base) globpattern(start,result,recurse,t) @@ -3864,16 +4227,12 @@ local function glob(str,t) return { str } end else - local split=lpegmatch(pattern,str) - if split then - local t=t or {} - local action=action or function(name) t[#t+1]=name end - local root,path,base=split[1],split[2],split[3] - local recurse=find(base,"%*%*") + local root,path,base=lpegmatch(pattern,str) + if root and path and base then + local recurse=find(base,"**",1,true) local start=root..path local result=lpegmatch(filter,start..base) - globpattern(start,result,recurse,action) - return t + return globpattern(start,result,recurse,t) else return {} end @@ -3913,16 +4272,26 @@ end local make_indeed=true if onwindows then function dir.mkdirs(...) - local str,pth="","" - for i=1,select("#",...) do - local s=select(i,...) - if s=="" then - elseif str=="" then - str=s - else - str=str.."/"..s + local n=select("#",...) + local str + if n==1 then + str=select(1,...) + if isdir(str) then + return str,true + end + else + str="" + for i=1,n do + local s=select(i,...) + if s=="" then + elseif str=="" then + str=s + else + str=str.."/"..s + end end end + local pth="" local drive=false local first,middle,last=match(str,"^(//)(//*)(.*)$") if first then @@ -3957,21 +4326,30 @@ if onwindows then pth=pth.."/"..s end if make_indeed and not isdir(pth) then - lfs.mkdir(pth) + mkdir(pth) end end return pth,(isdir(pth)==true) end else function dir.mkdirs(...) - local str,pth="","" - for i=1,select("#",...) do - local s=select(i,...) - if s and s~="" then - if str~="" then - str=str.."/"..s - else - str=s + local n=select("#",...) + local str,pth + if n==1 then + str=select(1,...) + if isdir(str) then + return str,true + end + else + str="" + for i=1,n do + local s=select(i,...) + if s and s~="" then + if str~="" then + str=str.."/"..s + else + str=s + end end end end @@ -3986,7 +4364,7 @@ else pth=pth.."/"..s end if make_indeed and not first and not isdir(pth) then - lfs.mkdir(pth) + mkdir(pth) end end else @@ -3994,7 +4372,7 @@ else for s in gmatch(str,"[^/]+") do pth=pth.."/"..s if make_indeed and not isdir(pth) then - lfs.mkdir(pth) + mkdir(pth) end end end @@ -4002,47 +4380,51 @@ else end end dir.makedirs=dir.mkdirs -if onwindows then - function dir.expandname(str) - 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() +do + local chdir=sandbox and sandbox.original(chdir) or chdir + if onwindows then + local xcurrentdir=dir.current + function dir.expandname(str) + local first,nothing,last=match(str,"^(//)(//*)(.*)$") + if first then + first=xcurrentdir().."/" + end + if not first then + first,last=match(str,"^(//)/*(.*)$") + end + if not first then + first,last=match(str,"^([a-zA-Z]:)(.*)$") + if first and not find(last,"^/") then + local d=currentdir() + if chdir(first) then + first=xcurrentdir() + end + chdir(d) end - chdir(d) + end + if not first then + first,last=xcurrentdir(),str + end + last=gsub(last,"//","/") + last=gsub(last,"/%./","/") + last=gsub(last,"^/*","") + first=gsub(first,"/*$","") + if last=="" or last=="." then + return first + else + return first.."/"..last end end - 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 -else - function dir.expandname(str) - if not find(str,"^/") then - str=currentdir().."/"..str + else + function dir.expandname(str) + if not find(str,"^/") then + str=currentdir().."/"..str + end + str=gsub(str,"//","/") + str=gsub(str,"/%./","/") + str=gsub(str,"(.)/%.$","%1") + return str end - str=gsub(str,"//","/") - str=gsub(str,"/%./","/") - str=gsub(str,"(.)/%.$","%1") - return str end end file.expandname=dir.expandname @@ -4085,7 +4467,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-boolean"] = package.loaded["l-boolean"] or true --- original size: 1809, stripped down to: 1527 +-- original size: 1850, stripped down to: 1568 if not modules then modules={} end modules ['l-boolean']={ version=1.001, @@ -4139,11 +4521,11 @@ function string.booleanstring(str) return str=="yes" or str=="on" or str=="t" end end -function string.is_boolean(str,default) +function string.is_boolean(str,default,strict) if type(str)=="string" then - if str=="true" or str=="yes" or str=="on" or str=="t" or str=="1" then + if str=="true" or str=="yes" or str=="on" or str=="t" or (not strict and str=="1") then return true - elseif str=="false" or str=="no" or str=="off" or str=="f" or str=="0" then + elseif str=="false" or str=="no" or str=="off" or str=="f" or (not strict and str=="0") then return false end end @@ -4157,7 +4539,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-unicode"] = package.loaded["l-unicode"] or true --- original size: 33473, stripped down to: 14938 +-- original size: 37388, stripped down to: 15817 if not modules then modules={} end modules ['l-unicode']={ version=1.001, @@ -4173,7 +4555,9 @@ local type=type local char,byte,format,sub,gmatch=string.char,string.byte,string.format,string.sub,string.gmatch local concat=table.concat local P,C,R,Cs,Ct,Cmt,Cc,Carg,Cp=lpeg.P,lpeg.C,lpeg.R,lpeg.Cs,lpeg.Ct,lpeg.Cmt,lpeg.Cc,lpeg.Carg,lpeg.Cp -local lpegmatch,patterns=lpeg.match,lpeg.patterns +local lpegmatch=lpeg.match +local patterns=lpeg.patterns +local tabletopattern=lpeg.utfchartabletopattern local bytepairs=string.bytepairs local finder=lpeg.finder local replacer=lpeg.replacer @@ -4182,7 +4566,7 @@ local utfgmatch=utf.gmatch local p_utftype=patterns.utftype local p_utfstricttype=patterns.utfstricttype local p_utfoffset=patterns.utfoffset -local p_utf8char=patterns.utf8char +local p_utf8char=patterns.utf8character local p_utf8byte=patterns.utf8byte local p_utfbom=patterns.utfbom local p_newline=patterns.newline @@ -4321,6 +4705,7 @@ if not utf.sub then local pattern_zero=Cmt(p_utf8char,slide_zero)^0 local pattern_one=Cmt(p_utf8char,slide_one )^0 local pattern_two=Cmt(p_utf8char,slide_two )^0 + local pattern_first=C(patterns.utf8character) function utf.sub(str,start,stop) if not start then return str @@ -4362,7 +4747,9 @@ if not utf.sub then end end end - if start>stop then + if start==1 and stop==1 then + return lpegmatch(pattern_first,str) or "" + elseif start>stop then return "" elseif start>1 then b,e,n,first,last=0,0,0,start-1,stop @@ -4381,15 +4768,52 @@ if not utf.sub then end end end -function utf.remapper(mapping) - local pattern=Cs((p_utf8char/mapping)^0) - return function(str) - if not str or str=="" then - return "" +function utf.remapper(mapping,option) + local variant=type(mapping) + if variant=="table" then + if option=="dynamic" then + local pattern=false + table.setmetatablenewindex(mapping,function(t,k,v) rawset(t,k,v) pattern=false end) + return function(str) + if not str or str=="" then + return "" + else + if not pattern then + pattern=Cs((tabletopattern(mapping)/mapping+p_utf8char)^0) + end + return lpegmatch(pattern,str) + end + end + elseif option=="pattern" then + return Cs((tabletopattern(mapping)/mapping+p_utf8char)^0) else - return lpegmatch(pattern,str) + local pattern=Cs((tabletopattern(mapping)/mapping+p_utf8char)^0) + return function(str) + if not str or str=="" then + return "" + else + return lpegmatch(pattern,str) + end + end,pattern + end + elseif variant=="function" then + if option=="pattern" then + return Cs((p_utf8char/mapping+p_utf8char)^0) + else + local pattern=Cs((p_utf8char/mapping+p_utf8char)^0) + return function(str) + if not str or str=="" then + return "" + else + return lpegmatch(pattern,str) + end + end,pattern end - end,pattern + else + return function(str) + return str or "" + end + end end function utf.replacer(t) local r=replacer(t,false,false,true) @@ -4439,190 +4863,157 @@ function utf.magic(f) end local utf16_to_utf8_be,utf16_to_utf8_le local utf32_to_utf8_be,utf32_to_utf8_le -local utf_16_be_linesplitter=patterns.utfbom_16_be^-1*lpeg.tsplitat(patterns.utf_16_be_nl) -local utf_16_le_linesplitter=patterns.utfbom_16_le^-1*lpeg.tsplitat(patterns.utf_16_le_nl) -if bytepairs then - utf16_to_utf8_be=function(t) - if type(t)=="string" then - t=lpegmatch(utf_16_be_linesplitter,t) - end - local 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) - end - return t +local utf_16_be_getbom=patterns.utfbom_16_be^-1 +local utf_16_le_getbom=patterns.utfbom_16_le^-1 +local utf_32_be_getbom=patterns.utfbom_32_be^-1 +local utf_32_le_getbom=patterns.utfbom_32_le^-1 +local utf_16_be_linesplitter=utf_16_be_getbom*lpeg.tsplitat(patterns.utf_16_be_nl) +local utf_16_le_linesplitter=utf_16_le_getbom*lpeg.tsplitat(patterns.utf_16_le_nl) +local utf_32_be_linesplitter=utf_32_be_getbom*lpeg.tsplitat(patterns.utf_32_be_nl) +local utf_32_le_linesplitter=utf_32_le_getbom*lpeg.tsplitat(patterns.utf_32_le_nl) +local more=0 +local p_utf16_to_utf8_be=C(1)*C(1)/function(left,right) + local now=256*byte(left)+byte(right) + if more>0 then + now=(more-0xD800)*0x400+(now-0xDC00)+0x10000 + more=0 + return utfchar(now) + elseif now>=0xD800 and now<=0xDBFF then + more=now + return "" + else + return utfchar(now) + end +end +local p_utf16_to_utf8_le=C(1)*C(1)/function(right,left) + local now=256*byte(left)+byte(right) + if more>0 then + now=(more-0xD800)*0x400+(now-0xDC00)+0x10000 + more=0 + return utfchar(now) + elseif now>=0xD800 and now<=0xDBFF then + more=now + return "" + else + return utfchar(now) + end +end +local p_utf32_to_utf8_be=C(1)*C(1)*C(1)*C(1)/function(a,b,c,d) + return utfchar(256*256*256*byte(a)+256*256*byte(b)+256*byte(c)+byte(d)) +end +local p_utf32_to_utf8_le=C(1)*C(1)*C(1)*C(1)/function(a,b,c,d) + return utfchar(256*256*256*byte(d)+256*256*byte(c)+256*byte(b)+byte(a)) +end +p_utf16_to_utf8_be=P(true)/function() more=0 end*utf_16_be_getbom*Cs(p_utf16_to_utf8_be^0) +p_utf16_to_utf8_le=P(true)/function() more=0 end*utf_16_le_getbom*Cs(p_utf16_to_utf8_le^0) +p_utf32_to_utf8_be=P(true)/function() more=0 end*utf_32_be_getbom*Cs(p_utf32_to_utf8_be^0) +p_utf32_to_utf8_le=P(true)/function() more=0 end*utf_32_le_getbom*Cs(p_utf32_to_utf8_le^0) +patterns.utf16_to_utf8_be=p_utf16_to_utf8_be +patterns.utf16_to_utf8_le=p_utf16_to_utf8_le +patterns.utf32_to_utf8_be=p_utf32_to_utf8_be +patterns.utf32_to_utf8_le=p_utf32_to_utf8_le +utf16_to_utf8_be=function(s) + if s and s~="" then + return lpegmatch(p_utf16_to_utf8_be,s) + else + return s end - utf16_to_utf8_le=function(t) - if type(t)=="string" then - t=lpegmatch(utf_16_le_linesplitter,t) - end - local 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) - end - return t +end +local utf16_to_utf8_be_t=function(t) + if not t then + return nil + elseif type(t)=="string" then + t=lpegmatch(utf_16_be_linesplitter,t) end - utf32_to_utf8_be=function(t) - if type(t)=="string" then - t=lpegmatch(utflinesplitter,t) - end - local result={} - for i=1,#t do - local r,more=0,-1 - for a,b in bytepairs(t[i]) 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) + for i=1,#t do + local s=t[i] + if s~="" then + t[i]=lpegmatch(p_utf16_to_utf8_be,s) end - return t end - utf32_to_utf8_le=function(t) - if type(t)=="string" then - t=lpegmatch(utflinesplitter,t) - end - local result={} - for i=1,#t do - local r,more=0,-1 - for a,b in bytepairs(t[i]) 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 t + return t +end +utf16_to_utf8_le=function(s) + if s and s~="" then + return lpegmatch(p_utf16_to_utf8_le,s) + else + return s end -else - utf16_to_utf8_be=function(t) - if type(t)=="string" then - t=lpegmatch(utf_16_be_linesplitter,t) - end - local result={} - for i=1,#t do - local r,more=0,0 - for left,right in gmatch(t[i],"(.)(.)") do - if left=="\000" then - r=r+1 - result[r]=utfchar(byte(right)) - elseif right then - local now=256*byte(left)+byte(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) +end +local utf16_to_utf8_le_t=function(t) + if not t then + return nil + elseif type(t)=="string" then + t=lpegmatch(utf_16_le_linesplitter,t) + end + for i=1,#t do + local s=t[i] + if s~="" then + t[i]=lpegmatch(p_utf16_to_utf8_le,s) end - return t end - utf16_to_utf8_le=function(t) - if type(t)=="string" then - t=lpegmatch(utf_16_le_linesplitter,t) + return t +end +utf32_to_utf8_be=function(s) + if s and s~="" then + return lpegmatch(p_utf32_to_utf8_be,s) + else + return s + end +end +local utf32_to_utf8_be_t=function(t) + if not t then + return nil + elseif type(t)=="string" then + t=lpegmatch(utf_32_be_linesplitter,t) + end + for i=1,#t do + local s=t[i] + if s~="" then + t[i]=lpegmatch(p_utf32_to_utf8_be,s) end - local result={} - for i=1,#t do - local r,more=0,0 - for left,right in gmatch(t[i],"(.)(.)") do - if right=="\000" then - r=r+1 - result[r]=utfchar(byte(left)) - elseif right then - local now=256*byte(right)+byte(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) + end + return t +end +utf32_to_utf8_le=function(s) + if s and s~="" then + return lpegmatch(p_utf32_to_utf8_le,s) + else + return s + end +end +local utf32_to_utf8_le_t=function(t) + if not t then + return nil + elseif type(t)=="string" then + t=lpegmatch(utf_32_le_linesplitter,t) + end + for i=1,#t do + local s=t[i] + if s~="" then + t[i]=lpegmatch(p_utf32_to_utf8_le,s) end - return t end - utf32_to_utf8_le=function() return {} end - utf32_to_utf8_be=function() return {} end + return t end +utf.utf16_to_utf8_le_t=utf16_to_utf8_le_t +utf.utf16_to_utf8_be_t=utf16_to_utf8_be_t +utf.utf32_to_utf8_le_t=utf32_to_utf8_le_t +utf.utf32_to_utf8_be_t=utf32_to_utf8_be_t utf.utf16_to_utf8_le=utf16_to_utf8_le utf.utf16_to_utf8_be=utf16_to_utf8_be utf.utf32_to_utf8_le=utf32_to_utf8_le utf.utf32_to_utf8_be=utf32_to_utf8_be -function utf.utf8_to_utf8(t) +function utf.utf8_to_utf8_t(t) return type(t)=="string" and lpegmatch(utflinesplitter,t) or t end -function utf.utf16_to_utf8(t,endian) - return endian and utf16_to_utf8_be(t) or utf16_to_utf8_le(t) or t +function utf.utf16_to_utf8_t(t,endian) + return endian and utf16_to_utf8_be_t(t) or utf16_to_utf8_le_t(t) or t end -function utf.utf32_to_utf8(t,endian) - return endian and utf32_to_utf8_be(t) or utf32_to_utf8_le(t) or t +function utf.utf32_to_utf8_t(t,endian) + return endian and utf32_to_utf8_be_t(t) or utf32_to_utf8_le_t(t) or t end -local function little(c) - local b=byte(c) +local function little(b) if b<0x10000 then return char(b%256,b/256) else @@ -4631,8 +5022,7 @@ local function little(c) return char(b1%256,b1/256,b2%256,b2/256) end end -local function big(c) - local b=byte(c) +local function big(b) if b<0x10000 then return char(b/256,b%256) else @@ -4641,27 +5031,29 @@ local function big(c) return char(b1/256,b1%256,b2/256,b2%256) end end -local _,l_remap=utf.remapper(little) -local _,b_remap=utf.remapper(big) -function utf.utf8_to_utf16_be(str,nobom) +local l_remap=Cs((p_utf8byte/little+P(1)/"")^0) +local b_remap=Cs((p_utf8byte/big+P(1)/"")^0) +local function utf8_to_utf16_be(str,nobom) if nobom then return lpegmatch(b_remap,str) else return char(254,255)..lpegmatch(b_remap,str) end end -function utf.utf8_to_utf16_le(str,nobom) +local function utf8_to_utf16_le(str,nobom) if nobom then return lpegmatch(l_remap,str) else return char(255,254)..lpegmatch(l_remap,str) end end +utf.utf8_to_utf16_be=utf8_to_utf16_be +utf.utf8_to_utf16_le=utf8_to_utf16_le function utf.utf8_to_utf16(str,littleendian,nobom) if littleendian then - return utf.utf8_to_utf16_le(str,nobom) + return utf8_to_utf16_le(str,nobom) else - return utf.utf8_to_utf16_be(str,nobom) + return utf8_to_utf16_be(str,nobom) end end local pattern=Cs ( @@ -4677,16 +5069,16 @@ function utf.xstring(s) return format("0x%05X",type(s)=="number" and s or utfbyte(s)) end function utf.toeight(str) - if not str then + if not str or str=="" then return nil end local utftype=lpegmatch(p_utfstricttype,str) if utftype=="utf-8" then - return sub(str,4) - elseif utftype=="utf-16-le" then - return utf16_to_utf8_le(str) + return sub(str,4) elseif utftype=="utf-16-be" then - return utf16_to_utf8_ne(str) + return utf16_to_utf8_be(str) + elseif utftype=="utf-16-le" then + return utf16_to_utf8_le(str) else return str end @@ -4765,7 +5157,7 @@ do -- create closure to overcome 200 locals limit package.loaded["l-math"] = package.loaded["l-math"] or true --- original size: 915, stripped down to: 836 +-- original size: 974, stripped down to: 890 if not modules then modules={} end modules ['l-math']={ version=1.001, @@ -4775,6 +5167,9 @@ if not modules then modules={} end modules ['l-math']={ license="see context related readme files" } local floor,sin,cos,tan=math.floor,math.sin,math.cos,math.tan +if not math.ceiling then + math.ceiling=math.ceil +end if not math.round then function math.round(x) return floor(x+0.5) end end @@ -4802,7 +5197,7 @@ do -- create closure to overcome 200 locals limit package.loaded["util-str"] = package.loaded["util-str"] or true --- original size: 26857, stripped down to: 15062 +-- original size: 34503, stripped down to: 18933 if not modules then modules={} end modules ['util-str']={ version=1.001, @@ -4821,25 +5216,43 @@ local unpack,concat=table.unpack,table.concat local P,V,C,S,R,Ct,Cs,Cp,Carg,Cc=lpeg.P,lpeg.V,lpeg.C,lpeg.S,lpeg.R,lpeg.Ct,lpeg.Cs,lpeg.Cp,lpeg.Carg,lpeg.Cc local patterns,lpegmatch=lpeg.patterns,lpeg.match local utfchar,utfbyte=utf.char,utf.byte -local loadstripped=_LUAVERSION<5.2 and load or function(str) - return load(dump(load(str),true)) +local loadstripped=nil +if _LUAVERSION<5.2 then + loadstripped=function(str,shortcuts) + return load(str) + end +else + loadstripped=function(str,shortcuts) + if shortcuts then + return load(dump(load(str),true),nil,nil,shortcuts) + else + return load(dump(load(str),true)) + end + end end if not number then number={} end local stripper=patterns.stripzeros +local newline=patterns.newline +local endofstring=patterns.endofstring +local whitespace=patterns.whitespace +local spacer=patterns.spacer +local spaceortab=patterns.spaceortab local function points(n) + n=tonumber(n) return (not n or n==0) and "0pt" or lpegmatch(stripper,format("%.5fpt",n/65536)) end local function basepoints(n) + n=tonumber(n) return (not n or n==0) and "0bp" or lpegmatch(stripper,format("%.5fbp",n*(7200/7227)/65536)) end number.points=points number.basepoints=basepoints -local rubish=patterns.spaceortab^0*patterns.newline -local anyrubish=patterns.spaceortab+patterns.newline +local rubish=spaceortab^0*newline +local anyrubish=spaceortab+newline local anything=patterns.anything -local stripped=(patterns.spaceortab^1/"")*patterns.newline +local stripped=(spaceortab^1/"")*newline local leading=rubish^0/"" -local trailing=(anyrubish^1*patterns.endofstring)/"" +local trailing=(anyrubish^1*endofstring)/"" local redundant=rubish^3/"\n" local pattern=Cs(leading*(trailing+redundant+stripped+anything)^0) function strings.collapsecrlf(str) @@ -4885,18 +5298,44 @@ local pattern=Carg(1)/function(t) else return "" end - end+patterns.newline*Cp()/function(position) + end+newline*Cp()/function(position) extra,start=0,position end+patterns.anything )^1) function strings.tabtospace(str,tab) return lpegmatch(pattern,str,1,tab or 7) end -function strings.striplong(str) - str=gsub(str,"^%s*","") - str=gsub(str,"[\n\r]+ *","\n") - return str +local space=spacer^0 +local nospace=space/"" +local endofline=nospace*newline +local stripend=(whitespace^1*endofstring)/"" +local normalline=(nospace*((1-space*(newline+endofstring))^1)*nospace) +local stripempty=endofline^1/"" +local normalempty=endofline^1 +local singleempty=endofline*(endofline^0/"") +local doubleempty=endofline*endofline^-1*(endofline^0/"") +local stripstart=stripempty^0 +local p_prune_normal=Cs (stripstart*(stripend+normalline+normalempty )^0 ) +local p_prune_collapse=Cs (stripstart*(stripend+normalline+doubleempty )^0 ) +local p_prune_noempty=Cs (stripstart*(stripend+normalline+singleempty )^0 ) +local p_retain_normal=Cs ((normalline+normalempty )^0 ) +local p_retain_collapse=Cs ((normalline+doubleempty )^0 ) +local p_retain_noempty=Cs ((normalline+singleempty )^0 ) +local striplinepatterns={ + ["prune"]=p_prune_normal, + ["prune and collapse"]=p_prune_collapse, + ["prune and no empty"]=p_prune_noempty, + ["retain"]=p_retain_normal, + ["retain and collapse"]=p_retain_collapse, + ["retain and no empty"]=p_retain_noempty, + ["collapse"]=patterns.collapser, +} +setmetatable(striplinepatterns,{ __index=function(t,k) return p_prune_collapse end }) +strings.striplinepatterns=striplinepatterns +function strings.striplines(str,how) + return str and lpegmatch(striplinepatterns[how],str) or str end +strings.striplong=strings.striplines function strings.nice(str) str=gsub(str,"[:%-+_]+"," ") return str @@ -4934,10 +5373,10 @@ string.tracedchars=tracedchars strings.tracers=tracedchars function string.tracedchar(b) if type(b)=="number" then - return tracedchars[b] or (utfchar(b).." (U+"..format('%05X',b)..")") + return tracedchars[b] or (utfchar(b).." (U+"..format("%05X",b)..")") else local c=utfbyte(b) - return tracedchars[c] or (b.." (U+"..format('%05X',c)..")") + return tracedchars[c] or (b.." (U+"..(c and format("%05X",c) or "?????")..")") end end function number.signed(i) @@ -4972,31 +5411,58 @@ function number.sparseexponent(f,n) end return tostring(n) end -local preamble=[[ -local type = type -local tostring = tostring -local tonumber = tonumber -local format = string.format -local concat = table.concat -local signed = number.signed -local points = number.points -local basepoints = number.basepoints -local utfchar = utf.char -local utfbyte = utf.byte -local lpegmatch = lpeg.match -local nspaces = string.nspaces -local tracedchar = string.tracedchar -local autosingle = string.autosingle -local autodouble = string.autodouble -local sequenced = table.sequenced -local formattednumber = number.formatted -local sparseexponent = number.sparseexponent -]] local template=[[ %s %s return function(%s) return %s end ]] +local preamble,environment="",{} +if _LUAVERSION<5.2 then + preamble=[[ +local lpeg=lpeg +local type=type +local tostring=tostring +local tonumber=tonumber +local format=string.format +local concat=table.concat +local signed=number.signed +local points=number.points +local basepoints= number.basepoints +local utfchar=utf.char +local utfbyte=utf.byte +local lpegmatch=lpeg.match +local nspaces=string.nspaces +local tracedchar=string.tracedchar +local autosingle=string.autosingle +local autodouble=string.autodouble +local sequenced=table.sequenced +local formattednumber=number.formatted +local sparseexponent=number.sparseexponent + ]] +else + environment={ + global=global or _G, + lpeg=lpeg, + type=type, + tostring=tostring, + tonumber=tonumber, + format=string.format, + concat=table.concat, + signed=number.signed, + points=number.points, + basepoints=number.basepoints, + utfchar=utf.char, + utfbyte=utf.byte, + lpegmatch=lpeg.match, + nspaces=string.nspaces, + tracedchar=string.tracedchar, + autosingle=string.autosingle, + autodouble=string.autodouble, + sequenced=table.sequenced, + formattednumber=number.formatted, + sparseexponent=number.sparseexponent, + } +end local arguments={ "a1" } setmetatable(arguments,{ __index=function(t,k) local v=t[k-1]..",a"..k @@ -5035,7 +5501,7 @@ local format_i=function(f) if f and f~="" then return format("format('%%%si',a%s)",f,n) else - return format("format('%%i',a%s)",n) + return format("format('%%i',a%s)",n) end end local format_d=format_i @@ -5047,6 +5513,14 @@ local format_f=function(f) n=n+1 return format("format('%%%sf',a%s)",f,n) end +local format_F=function(f) + n=n+1 + if not f or f=="" then + return format("(((a%s > -0.0000000005 and a%s < 0.0000000005) and '0') or format((a%s %% 1 == 0) and '%%i' or '%%.9f',a%s))",n,n,n,n) + else + return format("format((a%s %% 1 == 0) and '%%i' or '%%%sf',a%s)",n,f,n) + end +end local format_g=function(f) n=n+1 return format("format('%%%sg',a%s)",f,n) @@ -5261,7 +5735,7 @@ local builder=Cs { "start", ( P("%")/""*( V("!") -+V("s")+V("q")+V("i")+V("d")+V("f")+V("g")+V("G")+V("e")+V("E")+V("x")+V("X")+V("o") ++V("s")+V("q")+V("i")+V("d")+V("f")+V("F")+V("g")+V("G")+V("e")+V("E")+V("x")+V("X")+V("o") +V("c")+V("C")+V("S") +V("Q") +V("N") @@ -5272,7 +5746,6 @@ local builder=Cs { "start", +V("j")+V("J") +V("m")+V("M") +V("z") -+V("*") )+V("*") )*(P(-1)+Carg(1)) )^0, @@ -5281,6 +5754,7 @@ local builder=Cs { "start", ["i"]=(prefix_any*P("i"))/format_i, ["d"]=(prefix_any*P("d"))/format_d, ["f"]=(prefix_any*P("f"))/format_f, + ["F"]=(prefix_any*P("F"))/format_F, ["g"]=(prefix_any*P("g"))/format_g, ["G"]=(prefix_any*P("G"))/format_G, ["e"]=(prefix_any*P("e"))/format_e, @@ -5315,11 +5789,12 @@ local builder=Cs { "start", ["a"]=(prefix_any*P("a"))/format_a, ["A"]=(prefix_any*P("A"))/format_A, ["*"]=Cs(((1-P("%"))^1+P("%%")/"%%")^1)/format_rest, + ["?"]=Cs(((1-P("%"))^1 )^1)/format_rest, ["!"]=Carg(2)*prefix_any*P("!")*C((1-P("!"))^1)*P("!")/format_extension, } local direct=Cs ( - P("%")/""*Cc([[local format = string.format return function(str) return format("%]])*(S("+- .")+R("09"))^0*S("sqidfgGeExXo")*Cc([[",str) end]])*P(-1) - ) + P("%")*(S("+- .")+R("09"))^0*S("sqidfgGeExXo")*P(-1)/[[local format = string.format return function(str) return format("%0",str) end]] +) local function make(t,str) local f local p @@ -5328,10 +5803,10 @@ local function make(t,str) f=loadstripped(p)() else n=0 - p=lpegmatch(builder,str,1,"..",t._extensions_) + p=lpegmatch(builder,str,1,t._connector_,t._extensions_) if n>0 then p=format(template,preamble,t._preamble_,arguments[n],p) - f=loadstripped(p)() + f=loadstripped(p,t._environment_)() else f=function() return str end end @@ -5343,10 +5818,22 @@ local function use(t,fmt,...) return t[fmt](...) end strings.formatters={} -function strings.formatters.new() - local t={ _extensions_={},_preamble_="",_type_="formatter" } - setmetatable(t,{ __index=make,__call=use }) - return t +if _LUAVERSION<5.2 then + function strings.formatters.new(noconcat) + local t={ _type_="formatter",_connector_=noconcat and "," or "..",_extensions_={},_preamble_=preamble,_environment_={} } + setmetatable(t,{ __index=make,__call=use }) + return t + end +else + function strings.formatters.new(noconcat) + local e={} + for k,v in next,environment do + e[k]=v + end + local t={ _type_="formatter",_connector_=noconcat and "," or "..",_extensions_={},_preamble_="",_environment_=e } + setmetatable(t,{ __index=make,__call=use }) + return t + end end local formatters=strings.formatters.new() string.formatters=formatters @@ -5354,8 +5841,12 @@ string.formatter=function(str,...) return formatters[str](...) end local function add(t,name,template,preamble) if type(t)=="table" and t._type_=="formatter" then t._extensions_[name]=template or "%s" - if preamble then + if type(preamble)=="string" then t._preamble_=preamble.."\n"..t._preamble_ + elseif type(preamble)=="table" then + for k,v in next,preamble do + t._environment_[k]=v + end end end end @@ -5364,9 +5855,28 @@ patterns.xmlescape=Cs((P("<")/"<"+P(">")/">"+P("&")/"&"+P('"')/"" patterns.texescape=Cs((C(S("#$%\\{}"))/"\\%1"+P(1))^0) patterns.luaescape=Cs(((1-S('"\n'))^1+P('"')/'\\"'+P('\n')/'\\n"')^0) patterns.luaquoted=Cs(Cc('"')*((1-S('"\n'))^1+P('"')/'\\"'+P('\n')/'\\n"')^0*Cc('"')) -add(formatters,"xml",[[lpegmatch(xmlescape,%s)]],[[local xmlescape = lpeg.patterns.xmlescape]]) -add(formatters,"tex",[[lpegmatch(texescape,%s)]],[[local texescape = lpeg.patterns.texescape]]) -add(formatters,"lua",[[lpegmatch(luaescape,%s)]],[[local luaescape = lpeg.patterns.luaescape]]) +if _LUAVERSION<5.2 then + add(formatters,"xml",[[lpegmatch(xmlescape,%s)]],"local xmlescape = lpeg.patterns.xmlescape") + add(formatters,"tex",[[lpegmatch(texescape,%s)]],"local texescape = lpeg.patterns.texescape") + add(formatters,"lua",[[lpegmatch(luaescape,%s)]],"local luaescape = lpeg.patterns.luaescape") +else + add(formatters,"xml",[[lpegmatch(xmlescape,%s)]],{ xmlescape=lpeg.patterns.xmlescape }) + add(formatters,"tex",[[lpegmatch(texescape,%s)]],{ texescape=lpeg.patterns.texescape }) + add(formatters,"lua",[[lpegmatch(luaescape,%s)]],{ luaescape=lpeg.patterns.luaescape }) +end +local dquote=patterns.dquote +local equote=patterns.escaped+dquote/'\\"'+1 +local space=patterns.space +local cquote=Cc('"') +local pattern=Cs(dquote*(equote-P(-2))^0*dquote) ++Cs(cquote*(equote-space)^0*space*equote^0*cquote) +function string.optionalquoted(str) + return lpegmatch(pattern,str) or str +end +local pattern=Cs((newline/os.newline+1)^0) +function string.replacenewlines(str) + return lpegmatch(pattern,str) +end end -- of closure @@ -5375,7 +5885,7 @@ do -- create closure to overcome 200 locals limit package.loaded["util-tab"] = package.loaded["util-tab"] or true --- original size: 23952, stripped down to: 16092 +-- original size: 25338, stripped down to: 16247 if not modules then modules={} end modules ['util-tab']={ version=1.001, @@ -5388,7 +5898,7 @@ utilities=utilities or {} utilities.tables=utilities.tables or {} local tables=utilities.tables local format,gmatch,gsub,sub=string.format,string.gmatch,string.gsub,string.sub -local concat,insert,remove=table.concat,table.insert,table.remove +local concat,insert,remove,sort=table.concat,table.insert,table.remove,table.sort local setmetatable,getmetatable,tonumber,tostring=setmetatable,getmetatable,tonumber,tostring local type,next,rawset,tonumber,tostring,load,select=type,next,rawset,tonumber,tostring,load,select local lpegmatch,P,Cs,Cc=lpeg.match,lpeg.P,lpeg.Cs,lpeg.Cc @@ -5396,27 +5906,29 @@ local sortedkeys,sortedpairs=table.sortedkeys,table.sortedpairs local formatters=string.formatters local utftoeight=utf.toeight local splitter=lpeg.tsplitat(".") -function tables.definetable(target,nofirst,nolast) - local composed,shortcut,t=nil,nil,{} +function utilities.tables.definetable(target,nofirst,nolast) + local composed,t=nil,{} local snippets=lpegmatch(splitter,target) for i=1,#snippets-(nolast and 1 or 0) do local name=snippets[i] if composed then - composed=shortcut.."."..name - shortcut=shortcut.."_"..name - t[#t+1]=formatters["local %s = %s if not %s then %s = { } %s = %s end"](shortcut,composed,shortcut,shortcut,composed,shortcut) + composed=composed.."."..name + t[#t+1]=formatters["if not %s then %s = { } end"](composed,composed) else composed=name - shortcut=name if not nofirst then t[#t+1]=formatters["%s = %s or { }"](composed,composed) end end end - if nolast then - composed=shortcut.."."..snippets[#snippets] + if composed then + if nolast then + composed=composed.."."..snippets[#snippets] + end + return concat(t,"\n"),composed + else + return "",target end - return concat(t,"\n"),composed end function tables.definedtable(...) local t=_G @@ -5443,7 +5955,7 @@ function tables.accesstable(target,root) end function tables.migratetable(target,v,root) local t=root or _G - local names=string.split(target,".") + local names=lpegmatch(splitter,target) for i=1,#names-1 do local name=names[i] t[name]=t[name] or {} @@ -5463,6 +5975,15 @@ function tables.removevalue(t,value) end end end +function tables.replacevalue(t,oldvalue,newvalue) + if oldvalue and newvalue then + for i=1,#t do + if t[i]==oldvalue then + t[i]=newvalue + end + end + end +end function tables.insertbeforevalue(t,value,extra) for i=1,#t do if t[i]==extra then @@ -5610,7 +6131,7 @@ local f_ordered_string=formatters["%q,"] local f_ordered_number=formatters["%s,"] local f_ordered_boolean=formatters["%l,"] function table.fastserialize(t,prefix) - local r={ prefix or "return" } + local r={ type(prefix)=="string" and prefix or "return" } local m=1 local function fastserialize(t,outer) local n=#t @@ -5807,7 +6328,8 @@ function table.serialize(root,name,specification) local t local n=1 local function simple_table(t) - if #t>0 then + local nt=#t + if nt>0 then local n=0 for _,v in next,t do n=n+1 @@ -5815,19 +6337,17 @@ function table.serialize(root,name,specification) return nil end end - if n==#t then + if n==nt then local tt={} - local nt=0 - for i=1,#t do + for i=1,nt do local v=t[i] local tv=type(v) - nt=nt+1 if tv=="number" then - tt[nt]=v + tt[i]=v elseif tv=="string" then - tt[nt]=format("%q",v) + tt[i]=format("%q",v) elseif tv=="boolean" then - tt[nt]=v and "true" or "false" + tt[i]=v and "true" or "false" else return nil end @@ -5856,7 +6376,7 @@ function table.serialize(root,name,specification) end depth=depth+1 end - if root and next(root) then + if root and next(root)~=nil then local first=nil local last=0 last=#root @@ -5875,13 +6395,13 @@ function table.serialize(root,name,specification) local v=root[k] local tv=type(v) local tk=type(k) - if first and tk=="number" and k>=first and k<=last then + if first and tk=="number" and k<=last and k>=first then if tv=="number" then n=n+1 t[n]=f_val_num(depth,v) elseif tv=="string" then n=n+1 t[n]=f_val_str(depth,v) elseif tv=="table" then - if not next(v) then + if next(v)==nil then n=n+1 t[n]=f_val_not(depth) else local st=simple_table(v) @@ -5911,13 +6431,13 @@ function table.serialize(root,name,specification) n=n+1 t[n]=f_key_boo_value_str(depth,k,v) end elseif tv=="table" then - if not next(v) then + if next(v)==nil then if tk=="number" then - n=n+1 t[n]=f_key_num_value_not(depth,k,v) + n=n+1 t[n]=f_key_num_value_not(depth,k) elseif tk=="string" then - n=n+1 t[n]=f_key_str_value_not(depth,k,v) + n=n+1 t[n]=f_key_str_value_not(depth,k) elseif tk=="boolean" then - n=n+1 t[n]=f_key_boo_value_not(depth,k,v) + n=n+1 t[n]=f_key_boo_value_not(depth,k) end else local st=simple_table(v) @@ -5969,7 +6489,7 @@ function table.serialize(root,name,specification) local dummy=root._w_h_a_t_e_v_e_r_ root._w_h_a_t_e_v_e_r_=nil end - if next(root) then + if next(root)~=nil then do_serialize(root,name,1,0) end end @@ -6132,7 +6652,7 @@ do -- create closure to overcome 200 locals limit package.loaded["util-prs"] = package.loaded["util-prs"] or true --- original size: 19604, stripped down to: 13998 +-- original size: 21780, stripped down to: 15121 if not modules then modules={} end modules ['util-prs']={ version=1.001, @@ -6154,6 +6674,8 @@ local patterns=parsers.patterns or {} parsers.patterns=patterns local setmetatableindex=table.setmetatableindex local sortedhash=table.sortedhash +local sortedkeys=table.sortedkeys +local tohash=table.tohash local digit=R("09") local space=P(' ') local equal=P("=") @@ -6203,9 +6725,7 @@ patterns.settings_to_hash_a=pattern_a_s patterns.settings_to_hash_b=pattern_b_s patterns.settings_to_hash_c=pattern_c_s function parsers.make_settings_to_hash_pattern(set,how) - if type(str)=="table" then - return set - elseif how=="strict" then + if how=="strict" then return (pattern_c/set)^1 elseif how=="tolerant" then return (pattern_b/set)^1 @@ -6214,7 +6734,9 @@ function parsers.make_settings_to_hash_pattern(set,how) end end function parsers.settings_to_hash(str,existing) - if type(str)=="table" then + if not str or str=="" then + return {} + elseif type(str)=="table" then if existing then for k,v in next,str do existing[k]=v @@ -6223,16 +6745,16 @@ function parsers.settings_to_hash(str,existing) else return str end - elseif str and str~="" then + else hash=existing or {} lpegmatch(pattern_a_s,str) return hash - else - return {} end end function parsers.settings_to_hash_tolerant(str,existing) - if type(str)=="table" then + if not str or str=="" then + return {} + elseif type(str)=="table" then if existing then for k,v in next,str do existing[k]=v @@ -6241,16 +6763,16 @@ function parsers.settings_to_hash_tolerant(str,existing) else return str end - elseif str and str~="" then + else hash=existing or {} lpegmatch(pattern_b_s,str) return hash - else - return {} end end function parsers.settings_to_hash_strict(str,existing) - if type(str)=="table" then + if not str or str=="" then + return nil + elseif type(str)=="table" then if existing then for k,v in next,str do existing[k]=v @@ -6263,8 +6785,6 @@ function parsers.settings_to_hash_strict(str,existing) hash=existing or {} lpegmatch(pattern_c_s,str) return next(hash) and hash - else - return nil end end local separator=comma*space^0 @@ -6272,27 +6792,46 @@ local value=P(lbrace*C((nobrace+nestedbraces)^0)*rbrace)+C((nestedbraces+(1-comm local pattern=spaces*Ct(value*(separator*value)^0) patterns.settings_to_array=pattern function parsers.settings_to_array(str,strict) - if type(str)=="table" then - return str - elseif not str or str=="" then + if not str or str=="" then return {} + elseif type(str)=="table" then + return str elseif strict then - if find(str,"{") then + if find(str,"{",1,true) then return lpegmatch(pattern,str) else return { str } end - elseif find(str,",") then + elseif find(str,",",1,true) then return lpegmatch(pattern,str) else return { str } end end -local separator=space^0*comma*space^0 -local value=P(lbrace*C((nobrace+nestedbraces)^0)*rbrace)+C((nestedbraces+(1-(space^0*(comma+P(-1)))))^0) -local withvalue=Carg(1)*value/function(f,s) return f(s) end -local pattern_a=spaces*Ct(value*(separator*value)^0) -local pattern_b=spaces*withvalue*(separator*withvalue)^0 +local cache_a={} +local cache_b={} +function parsers.groupedsplitat(symbol,withaction) + if not symbol then + symbol="," + end + local pattern=(withaction and cache_b or cache_a)[symbol] + if not pattern then + local symbols=S(symbol) + local separator=space^0*symbols*space^0 + local value=P(lbrace*C((nobrace+nestedbraces)^0)*rbrace)+C((nestedbraces+(1-(space^0*(symbols+P(-1)))))^0) + if withaction then + local withvalue=Carg(1)*value/function(f,s) return f(s) end + pattern=spaces*withvalue*(separator*withvalue)^0 + cache_b[symbol]=pattern + else + pattern=spaces*Ct(value*(separator*value)^0) + cache_a[symbol]=pattern + end + end + return pattern +end +local pattern_a=parsers.groupedsplitat(",",false) +local pattern_b=parsers.groupedsplitat(",",true) function parsers.stripped_settings_to_array(str) if not str or str=="" then return {} @@ -6317,8 +6856,8 @@ function parsers.add_settings_to_array(t,str) end function parsers.hash_to_string(h,separator,yes,no,strict,omit) if h then - local t,tn,s={},0,table.sortedkeys(h) - omit=omit and table.tohash(omit) + local t,tn,s={},0,sortedkeys(h) + omit=omit and tohash(omit) for i=1,#s do local key=s[i] if not omit or not omit[key] then @@ -6354,12 +6893,9 @@ function parsers.array_to_string(a,separator) return "" end end -function parsers.settings_to_set(str,t) - t=t or {} - for s in gmatch(str,"[^, ]+") do - t[s]=true - end - return t +local pattern=Cf(Ct("")*Cg(C((1-S(", "))^1)*S(", ")^0*Cc(true))^1,rawset) +function utilities.parsers.settings_to_set(str,t) + return str and lpegmatch(pattern,str) or {} end function parsers.simple_hash_to_string(h,separator) local t,tn={},0 @@ -6371,12 +6907,16 @@ function parsers.simple_hash_to_string(h,separator) end return concat(t,separator or ",") end -local str=C((1-whitespace-equal)^1) +local str=Cs(lpegpatterns.unquoted)+C((1-whitespace-equal)^1) local setting=Cf(Carg(1)*(whitespace^0*Cg(str*whitespace^0*(equal*whitespace^0*str+Cc(""))))^1,rawset) local splitter=setting^1 function utilities.parsers.options_to_hash(str,target) return str and lpegmatch(splitter,str,1,target or {}) or {} end +local splitter=lpeg.tsplitat(" ") +function utilities.parsers.options_to_array(str) + return str and lpegmatch(splitter,str) or {} +end local value=P(lbrace*C((nobrace+nestedbraces)^0)*rbrace)+C(digit^1*lparent*(noparent+nestedparents)^1*rparent)+C((nestedbraces+(1-comma))^1) local pattern_a=spaces*Ct(value*(separator*value)^0) local function repeater(n,str) @@ -6463,7 +7003,7 @@ function parsers.keq_to_hash(str) end local defaultspecification={ separator=",",quote='"' } function parsers.csvsplitter(specification) - specification=specification and table.setmetatableindex(specification,defaultspecification) or defaultspecification + specification=specification and setmetatableindex(specification,defaultspecification) or defaultspecification local separator=specification.separator local quotechar=specification.quote local separator=S(separator~="" and separator or ",") @@ -6487,7 +7027,7 @@ function parsers.csvsplitter(specification) end end function parsers.rfc4180splitter(specification) - specification=specification and table.setmetatableindex(specification,defaultspecification) or defaultspecification + specification=specification and setmetatableindex(specification,defaultspecification) or defaultspecification local separator=specification.separator local quotechar=P(specification.quote) local dquotechar=quotechar*quotechar @@ -6498,7 +7038,7 @@ function parsers.rfc4180splitter(specification) local field=escaped+non_escaped+Cc("") local record=Ct(field*(separator*field)^1) local headerline=record*Cp() - local wholeblob=Ct((newline^-1*record)^0) + local wholeblob=Ct((newline^(specification.strict and -1 or 1)*record)^0) return function(data,getheader) if getheader then local header,position=lpegmatch(headerline,data) @@ -6535,20 +7075,20 @@ function parsers.stepper(str,n,action) lpegmatch(stepper,str,1,n,action or print) end end -local pattern_math=Cs((P("%")/"\\percent "+P("^")*Cc("{")*lpegpatterns.integer*Cc("}")+P(1))^0) -local pattern_text=Cs((P("%")/"\\percent "+(P("^")/"\\high")*Cc("{")*lpegpatterns.integer*Cc("}")+P(1))^0) +local pattern_math=Cs((P("%")/"\\percent "+P("^")*Cc("{")*lpegpatterns.integer*Cc("}")+anything)^0) +local pattern_text=Cs((P("%")/"\\percent "+(P("^")/"\\high")*Cc("{")*lpegpatterns.integer*Cc("}")+anything)^0) patterns.unittotex=pattern function parsers.unittotex(str,textmode) return lpegmatch(textmode and pattern_text or pattern_math,str) end -local pattern=Cs((P("^")/"<sup>"*lpegpatterns.integer*Cc("</sup>")+P(1))^0) +local pattern=Cs((P("^")/"<sup>"*lpegpatterns.integer*Cc("</sup>")+anything)^0) function parsers.unittoxml(str) return lpegmatch(pattern,str) end local cache={} -local spaces=lpeg.patterns.space^0 +local spaces=lpegpatterns.space^0 local dummy=function() end -table.setmetatableindex(cache,function(t,k) +setmetatableindex(cache,function(t,k) local separator=P(k) local value=(1-separator)^0 local pattern=spaces*C(value)*separator^0*Cp() @@ -6613,6 +7153,18 @@ function utilities.parsers.runtime(time) local seconds=mod(time,60) return days,hours,minutes,seconds end +local spacing=whitespace^0 +local apply=P("->") +local method=C((1-apply)^1) +local token=lbrace*C((1-rbrace)^1)*rbrace+C(anything^1) +local pattern=spacing*(method*spacing*apply+Carg(1))*spacing*token +function utilities.parsers.splitmethod(str,default) + if str then + return lpegmatch(pattern,str,1,default or false) + else + return default or false,"" + end +end end -- of closure @@ -6702,7 +7254,7 @@ do -- create closure to overcome 200 locals limit package.loaded["trac-set"] = package.loaded["trac-set"] or true --- original size: 12365, stripped down to: 8799 +-- original size: 12482, stripped down to: 8864 if not modules then modules={} end modules ['trac-set']={ version=1.001, @@ -6730,7 +7282,7 @@ function setters.initialize(filename,name,values) local data=setter.data if data then for key,newvalue in next,values do - local newvalue=is_boolean(newvalue,newvalue) + local newvalue=is_boolean(newvalue,newvalue,true) local functions=data[key] if functions then local oldvalue=functions.value @@ -6784,7 +7336,7 @@ local function set(t,what,newvalue) elseif not value then value=false else - value=is_boolean(value,value) + value=is_boolean(value,value,true) end w=topattern(w,true,true) for name,functions in next,data do @@ -6923,6 +7475,7 @@ function setters.new(name) report=function(...) setters.report (setter,...) end, enable=function(...) enable (setter,...) end, disable=function(...) disable (setter,...) end, + reset=function(...) reset (setter,...) end, register=function(...) register(setter,...) end, list=function(...) list (setter,...) end, show=function(...) show (setter,...) end, @@ -7014,7 +7567,7 @@ do -- create closure to overcome 200 locals limit package.loaded["trac-log"] = package.loaded["trac-log"] or true --- original size: 25391, stripped down to: 16561 +-- original size: 29359, stripped down to: 20483 if not modules then modules={} end modules ['trac-log']={ version=1.001, @@ -7023,15 +7576,18 @@ if not modules then modules={} end modules ['trac-log']={ copyright="PRAGMA ADE / ConTeXt Development Team", license="see context related readme files" } +local next,type,select,print=next,type,select,print 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 topattern=string.topattern -local next,type,select=next,type,select local utfchar=utf.char +local datetime=os.date +local openfile=io.open local setmetatableindex=table.setmetatableindex local formatters=string.formatters local texgetcount=tex and tex.getcount +local variant="default" logs=logs or {} local logs=logs local moreinfo=[[ @@ -7041,32 +7597,122 @@ 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 ]] -utilities.strings.formatters.add ( +formatters.add ( formatters,"unichr", [["U+" .. format("%%05X",%s) .. " (" .. utfchar(%s) .. ")"]] ) -utilities.strings.formatters.add ( +formatters.add ( formatters,"chruni", [[utfchar(%s) .. " (U+" .. format("%%05X",%s) .. ")"]] ) 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,setlogfile,settimedlog,setprocessor,setformatters +local direct,subdirect,writer,pushtarget,poptarget,setlogfile,settimedlog,setprocessor,setformatters,newline if tex and (tex.jobname or tex.formatname) then - local valueiskey={ __index=function(t,k) t[k]=k return k end } - local target="term and log" + local function useluawrites() + local texio_write_nl=texio.write_nl + local texio_write=texio.write + local io_write=io.write + write_nl=function(target,...) + if not io_write then + io_write=io.write + end + if target=="term and log" then + texio_write_nl("log",...) + texio_write_nl("term","") + io_write(...) + elseif target=="log" then + texio_write_nl("log",...) + elseif target=="term" then + texio_write_nl("term","") + io_write(...) + elseif target~="none" then + texio_write_nl("log",target,...) + texio_write_nl("term","") + io_write(target,...) + end + end + write=function(target,...) + if not io_write then + io_write=io.write + end + if target=="term and log" then + texio_write("log",...) + io_write(...) + elseif target=="log" then + texio_write("log",...) + elseif target=="term" then + io_write(...) + elseif target~="none" then + texio_write("log",target,...) + io_write(target,...) + end + end + texio.write=write + texio.write_nl=write_nl + useluawrites=ignore + end + local whereto="both" + local target=nil + local targets=nil + local formats=table.setmetatableindex("self") + local translations=table.setmetatableindex("self") + local report_yes,subreport_yes,direct_yes,subdirect_yes,status_yes + local report_nop,subreport_nop,direct_nop,subdirect_nop,status_nop + local variants={ + default={ + formats={ + report_yes=formatters["%-15s > %s\n"], + report_nop=formatters["%-15s >\n"], + direct_yes=formatters["%-15s > %s"], + direct_nop=formatters["%-15s >"], + subreport_yes=formatters["%-15s > %s > %s\n"], + subreport_nop=formatters["%-15s > %s >\n"], + subdirect_yes=formatters["%-15s > %s > %s"], + subdirect_nop=formatters["%-15s > %s >"], + status_yes=formatters["%-15s : %s\n"], + status_nop=formatters["%-15s :\n"], + }, + targets={ + logfile="log", + log="log", + file="log", + console="term", + terminal="term", + both="term and log", + }, + }, + ansi={ + formats={ + report_yes=formatters["[0;33m%-15s [0;1m>[0m %s\n"], + report_nop=formatters["[0;33m%-15s [0;1m>[0m\n"], + direct_yes=formatters["[0;33m%-15s [0;1m>[0m %s"], + direct_nop=formatters["[0;33m%-15s [0;1m>[0m"], + subreport_yes=formatters["[0;33m%-15s [0;1m>[0;35m %s [0;1m>[0m %s\n"], + subreport_nop=formatters["[0;33m%-15s [0;1m>[0;35m %s [0;1m>[0m\n"], + subdirect_yes=formatters["[0;33m%-15s [0;1m>[0;35m %s [0;1m>[0m %s"], + subdirect_nop=formatters["[0;33m%-15s [0;1m>[0;35m %s [0;1m>[0m"], + status_yes=formatters["[0;33m%-15s [0;1m:[0m %s\n"], + status_nop=formatters["[0;33m%-15s [0;1m:[0m\n"], + }, + targets={ + logfile="none", + log="none", + file="none", + console="term", + terminal="term", + both="term", + }, + } + } logs.flush=io.flush - local formats={} setmetatable(formats,valueiskey) - local translations={} setmetatable(translations,valueiskey) writer=function(...) write_nl(target,...) end newline=function() write_nl(target,"\n") end - local report_yes=formatters["%-15s > %s\n"] - local report_nop=formatters["%-15s >\n"] report=function(a,b,c,...) if c then write_nl(target,report_yes(translations[a],formatters[formats[b]](c,...))) @@ -7078,8 +7724,6 @@ if tex and (tex.jobname or tex.formatname) then write_nl(target,"\n") end end - local direct_yes=formatters["%-15s > %s"] - local direct_nop=formatters["%-15s >"] direct=function(a,b,c,...) if c then return direct_yes(translations[a],formatters[formats[b]](c,...)) @@ -7091,8 +7735,6 @@ if tex and (tex.jobname or tex.formatname) then return "" end end - local subreport_yes=formatters["%-15s > %s > %s\n"] - local subreport_nop=formatters["%-15s > %s >\n"] subreport=function(a,s,b,c,...) if c then write_nl(target,subreport_yes(translations[a],translations[s],formatters[formats[b]](c,...))) @@ -7104,8 +7746,6 @@ if tex and (tex.jobname or tex.formatname) then write_nl(target,"\n") end end - local subdirect_yes=formatters["%-15s > %s > %s"] - local subdirect_nop=formatters["%-15s > %s >"] subdirect=function(a,s,b,c,...) if c then return subdirect_yes(translations[a],translations[s],formatters[formats[b]](c,...)) @@ -7117,8 +7757,6 @@ if tex and (tex.jobname or tex.formatname) then return "" end end - local status_yes=formatters["%-15s : %s\n"] - local status_nop=formatters["%-15s :\n"] status=function(a,b,c,...) if c then write_nl(target,status_yes(translations[a],formatters[formats[b]](c,...))) @@ -7130,16 +7768,13 @@ if tex and (tex.jobname or tex.formatname) then 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 + settarget=function(askedwhereto) + whereto=askedwhereto or whereto or "both" + target=targets[whereto] + if not target then + whereto="both" + target=targets[whereto] + end if target=="term" or target=="term and log" then logs.flush=io.flush else @@ -7168,21 +7803,74 @@ if tex and (tex.jobname or tex.formatname) then writeline(target,f(...)) end end - setformatters=function(f) - report_yes=f.report_yes or report_yes - report_nop=f.report_nop or report_nop - subreport_yes=f.subreport_yes or subreport_yes - subreport_nop=f.subreport_nop or subreport_nop - direct_yes=f.direct_yes or direct_yes - direct_nop=f.direct_nop or direct_nop - subdirect_yes=f.subdirect_yes or subdirect_yes - subdirect_nop=f.subdirect_nop or subdirect_nop - status_yes=f.status_yes or status_yes - status_nop=f.status_nop or status_nop - end + setformatters=function(specification) + local t=nil + local f=nil + local d=variants.default + if not specification then + elseif type(specification)=="table" then + t=specification.targets + f=specification.formats or specification + else + local v=variants[specification] + if v then + t=v.targets + f=v.formats + variant=specification + end + end + targets=t or d.targets + target=targets[whereto] or target + if f then + d=d.formats + else + f=d.formats + d=f + end + setmetatableindex(f,d) + report_yes=f.report_yes + report_nop=f.report_nop + subreport_yes=f.subreport_yes + subreport_nop=f.subreport_nop + direct_yes=f.direct_yes + direct_nop=f.direct_nop + subdirect_yes=f.subdirect_yes + subdirect_nop=f.subdirect_nop + status_yes=f.status_yes + status_nop=f.status_nop + if variant=="ansi" then + useluawrites() + end + settarget(whereto) + end + setformatters(variant) setlogfile=ignore settimedlog=ignore else + local report_yes,subreport_yes,status_yes + local report_nop,subreport_nop,status_nop + local variants={ + default={ + formats={ + report_yes=formatters["%-15s | %s"], + report_nop=formatters["%-15s |"], + subreport_yes=formatters["%-15s | %s | %s"], + subreport_nop=formatters["%-15s | %s |"], + status_yes=formatters["%-15s : %s\n"], + status_nop=formatters["%-15s :\n"], + }, + }, + ansi={ + formats={ + report_yes=formatters["[0;32m%-15s [0;1m|[0m %s"], + report_nop=formatters["[0;32m%-15s [0;1m|[0m"], + subreport_yes=formatters["[0;32m%-15s [0;1m|[0;31m %s [0;1m|[0m %s"], + subreport_nop=formatters["[0;32m%-15s [0;1m|[0;31m %s [0;1m|[0m"], + status_yes=formatters["[0;32m%-15s [0;1m:[0m %s\n"], + status_nop=formatters["[0;32m%-15s [0;1m:[0m\n"], + }, + }, + } logs.flush=ignore writer=function(s) write_nl(s) @@ -7190,8 +7878,6 @@ else newline=function() write_nl("\n") end - local report_yes=formatters["%-15s | %s"] - local report_nop=formatters["%-15s |"] report=function(a,b,c,...) if c then write_nl(report_yes(a,formatters[b](c,...))) @@ -7203,8 +7889,6 @@ else write_nl("") end end - local subreport_yes=formatters["%-15s | %s | %s"] - local subreport_nop=formatters["%-15s | %s |"] subreport=function(a,sub,b,c,...) if c then write_nl(subreport_yes(a,sub,formatters[b](c,...))) @@ -7216,8 +7900,6 @@ else write_nl("") end end - local status_yes=formatters["%-15s : %s\n"] - local status_nop=formatters["%-15s :\n"] status=function(a,b,c,...) if c then write_nl(status_yes(a,formatters[b](c,...))) @@ -7242,14 +7924,34 @@ else writeline(f(s)) end end - setformatters=function(f) - report_yes=f.report_yes or report_yes - report_nop=f.report_nop or report_nop - subreport_yes=f.subreport_yes or subreport_yes - subreport_nop=f.subreport_nop or subreport_nop - status_yes=f.status_yes or status_yes - status_nop=f.status_nop or status_nop - end + setformatters=function(specification) + local f=nil + local d=variants.default + if specification then + if type(specification)=="table" then + f=specification.formats or specification + else + local v=variants[specification] + if v then + f=v.formats + end + end + end + if f then + d=d.formats + else + f=d.formats + d=f + end + setmetatableindex(f,d) + report_yes=f.report_yes + report_nop=f.report_nop + subreport_yes=f.subreport_yes + subreport_nop=f.subreport_nop + status_yes=f.status_yes + status_nop=f.status_nop + end + setformatters(variant) setlogfile=function(name,keepopen) if name and name~="" then local localtime=os.localtime @@ -7368,9 +8070,10 @@ local function setblocked(category,value) v.state=value end else - states=utilities.parsers.settings_to_hash(category) + states=utilities.parsers.settings_to_hash(category,type(states)=="table" and states or nil) for c,_ in next,states do - if data[c] then + local v=data[c] + if v then v.state=value else c=topattern(c,true,true) @@ -7501,13 +8204,13 @@ end local simple=logs.reporter("comment") logs.simple=simple logs.simpleline=simple -function logs.setprogram () end -function logs.extendbanner() end -function logs.reportlines () end -function logs.reportbanner() end -function logs.reportline () end -function logs.simplelines () end -function logs.help () end +logs.setprogram=ignore +logs.extendbanner=ignore +logs.reportlines=ignore +logs.reportbanner=ignore +logs.reportline=ignore +logs.simplelines=ignore +logs.help=ignore local Carg,C,lpegmatch=lpeg.Carg,lpeg.C,lpeg.match local p_newline=lpeg.patterns.newline local linewise=( @@ -7584,10 +8287,11 @@ function logs.application(t) end return t end -function logs.system(whereto,process,jobname,category,...) - local message=formatters["%s %s => %s => %s => %s\r"](os.date("%d/%m/%y %H:%m:%S"),process,jobname,category,format(...)) +local f_syslog=formatters["%s %s => %s => %s => %s\r"] +function logs.system(whereto,process,jobname,category,fmt,arg,...) + local message=f_syslog(datetime("%d/%m/%y %H:%m:%S"),process,jobname,category,arg==nil and fmt or format(fmt,arg,...)) for i=1,10 do - local f=io.open(whereto,"a") + local f=openfile(whereto,"a") if f then f:write(message) f:close() @@ -7649,7 +8353,7 @@ do -- create closure to overcome 200 locals limit package.loaded["trac-inf"] = package.loaded["trac-inf"] or true --- original size: 6501, stripped down to: 5156 +-- original size: 6704, stripped down to: 5343 if not modules then modules={} end modules ['trac-inf']={ version=1.001, @@ -7659,7 +8363,7 @@ if not modules then modules={} end modules ['trac-inf']={ license="see context related readme files" } local type,tonumber,select=type,tonumber,select -local format,lower=string.format,string.lower +local format,lower,find=string.format,string.lower,string.find local concat=table.concat local clock=os.gettimeofday or os.clock local setmetatableindex=table.setmetatableindex @@ -7750,7 +8454,8 @@ function statistics.show() if statistics.enable then local register=statistics.register register("used platform",function() - return format("%s, type: %s, binary subtree: %s",os.platform or "unknown",os.type or "unknown",environment.texos or "unknown") + return format("%s, type: %s, binary subtree: %s", + os.platform or "unknown",os.type or "unknown",environment.texos or "unknown") end) register("luatex banner",function() return lower(status.banner) @@ -7763,14 +8468,23 @@ function statistics.show() return format("%s direct, %s indirect, %s total",total-indirect,indirect,total) end) if jit then - local status={ jit.status() } - if status[1] then - register("luajit status",function() - return concat(status," ",2) - end) - end - end - register("current memory usage",statistics.memused) + local jitstatus={ jit.status() } + if jitstatus[1] then + register("luajit options",concat(jitstatus," ",2)) + end + end + register("lua properties",function() + local list=status.list() + local hashchar=tonumber(list.luatex_hashchars) + local mask=lua.mask or "ascii" + return format("engine: %s, used memory: %s, hash type: %s, hash chars: min(%s,40), symbol mask: %s (%s)", + jit and "luajit" or "lua", + statistics.memused(), + list.luatex_hashtype or "default", + hashchar and 2^hashchar or "unknown", + mask, + mask=="utf" and "τεχ" or "tex") + end) register("runtime",statistics.runtime) logs.newline() for i=1,#statusinfo do @@ -7812,15 +8526,6 @@ function statistics.tracefunction(base,tag,...) statistics.register(formatters["%s.%s"](tag,name),function() return serialize(stat,"calls") end) end end -commands=commands or {} -function commands.resettimer(name) - resettiming(name or "whatever") - starttiming(name or "whatever") -end -function commands.elapsedtime(name) - stoptiming(name or "whatever") - context(elapsedtime(name or "whatever")) -end end -- of closure @@ -7829,7 +8534,7 @@ do -- create closure to overcome 200 locals limit package.loaded["trac-pro"] = package.loaded["trac-pro"] or true --- original size: 5773, stripped down to: 3453 +-- original size: 5829, stripped down to: 3501 if not modules then modules={} end modules ['trac-pro']={ version=1.001, @@ -7846,14 +8551,16 @@ local namespaces=namespaces local registered={} local function report_index(k,name) if trace_namespaces then - report_system("reference to %a in protected namespace %a: %s",k,name,debug.traceback()) + report_system("reference to %a in protected namespace %a: %s",k,name) + debugger.showtraceback(report_system) else report_system("reference to %a in protected namespace %a",k,name) end end local function report_newindex(k,name) if trace_namespaces then - report_system("assignment to %a in protected namespace %a: %s",k,name,debug.traceback()) + report_system("assignment to %a in protected namespace %a: %s",k,name) + debugger.showtraceback(report_system) else report_system("assignment to %a in protected namespace %a",k,name) end @@ -8104,7 +8811,7 @@ do -- create closure to overcome 200 locals limit package.loaded["util-deb"] = package.loaded["util-deb"] or true --- original size: 3708, stripped down to: 2568 +-- original size: 3898, stripped down to: 2644 if not modules then modules={} end modules ['util-deb']={ version=1.001, @@ -8184,20 +8891,22 @@ end function debugger.disable() debug.sethook() end -function traceback() - local level=1 +local function showtraceback(rep) + local level=2 + local reporter=rep or report while true do - local info=debug.getinfo(level,"Sl") + local info=getinfo(level,"Sl") if not info then break elseif info.what=="C" then - print(format("%3i : C function",level)) + reporter("%2i : %s",level-1,"C function") else - print(format("%3i : [%s]:%d",level,info.short_src,info.currentline)) + reporter("%2i : %s : %s",level-1,info.short_src,info.currentline) end level=level+1 end end +debugger.showtraceback=showtraceback end -- of closure @@ -8383,7 +9092,7 @@ do -- create closure to overcome 200 locals limit package.loaded["util-tpl"] = package.loaded["util-tpl"] or true --- original size: 6251, stripped down to: 3488 +-- original size: 7100, stripped down to: 3978 if not modules then modules={} end modules ['util-tpl']={ version=1.001, @@ -8425,7 +9134,7 @@ local sqlescape=lpeg.replacer { { "\r\n","\\n" }, { "\r","\\n" }, } -local sqlquoted=lpeg.Cs(lpeg.Cc("'")*sqlescape*lpeg.Cc("'")) +local sqlquoted=Cs(Cc("'")*sqlescape*Cc("'")) lpegpatterns.sqlescape=sqlescape lpegpatterns.sqlquoted=sqlquoted local luaescape=lpegpatterns.luaescape @@ -8448,12 +9157,24 @@ local quotedescapers={ local luaescaper=escapers.lua local quotedluaescaper=quotedescapers.lua local function replacekeyunquoted(s,t,how,recurse) - local escaper=how and escapers[how] or luaescaper - return escaper(replacekey(s,t,how,recurse)) + if how==false then + return replacekey(s,t,how,recurse) + else + local escaper=how and escapers[how] or luaescaper + return escaper(replacekey(s,t,how,recurse)) + end end local function replacekeyquoted(s,t,how,recurse) - local escaper=how and quotedescapers[how] or quotedluaescaper - return escaper(replacekey(s,t,how,recurse)) + if how==false then + return replacekey(s,t,how,recurse) + else + local escaper=how and quotedescapers[how] or quotedluaescaper + return escaper(replacekey(s,t,how,recurse)) + end +end +local function replaceoptional(l,m,r,t,how,recurse) + local v=t[l] + return v and v~="" and lpegmatch(replacer,r,1,t,how or "lua",recurse or false) or "" end local single=P("%") local double=P("%%") @@ -8468,11 +9189,16 @@ local nolquoted=lquoted/'' local norquoted=rquoted/'' local nolquotedq=lquotedq/'' local norquotedq=rquotedq/'' -local key=nosingle*((C((1-nosingle )^1)*Carg(1)*Carg(2)*Carg(3))/replacekey )*nosingle -local quoted=nolquotedq*((C((1-norquotedq)^1)*Carg(1)*Carg(2)*Carg(3))/replacekeyquoted )*norquotedq -local unquoted=nolquoted*((C((1-norquoted )^1)*Carg(1)*Carg(2)*Carg(3))/replacekeyunquoted)*norquoted +local noloptional=P("%?")/'' +local noroptional=P("?%")/'' +local nomoptional=P(":")/'' +local args=Carg(1)*Carg(2)*Carg(3) +local key=nosingle*((C((1-nosingle )^1)*args)/replacekey )*nosingle +local quoted=nolquotedq*((C((1-norquotedq )^1)*args)/replacekeyquoted )*norquotedq +local unquoted=nolquoted*((C((1-norquoted )^1)*args)/replacekeyunquoted)*norquoted +local optional=noloptional*((C((1-nomoptional)^1)*nomoptional*C((1-noroptional)^1)*args)/replaceoptional)*noroptional local any=P(1) - replacer=Cs((unquoted+quoted+escape+key+any)^0) + replacer=Cs((unquoted+quoted+escape+optional+key+any)^0) local function replace(str,mapping,how,recurse) if mapping and str then return lpegmatch(replacer,str,1,mapping,how or "lua",recurse or false) or str @@ -8511,7 +9237,7 @@ do -- create closure to overcome 200 locals limit package.loaded["util-env"] = package.loaded["util-env"] or true --- original size: 8807, stripped down to: 5085 +-- original size: 8022, stripped down to: 5038 if not modules then modules={} end modules ['util-env']={ version=1.001, @@ -8522,7 +9248,7 @@ if not modules then modules={} end modules ['util-env']={ } 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 unquoted,quoted,optionalquoted=string.unquoted,string.quoted,string.optionalquoted local concat,insert,remove=table.concat,table.insert,table.remove environment=environment or {} local environment=environment @@ -8635,24 +9361,14 @@ function environment.splitarguments(separator) return before,after end function environment.reconstructcommandline(arg,noquote) + local resolveprefix=resolvers.resolve arg=arg or environment.originalarguments if noquote and #arg==1 then - local a=arg[1] - a=resolvers.resolve(a) - a=unquoted(a) - return a + return unquoted(resolveprefix and resolveprefix(arg[1]) or arg[1]) elseif #arg>0 then local result={} for i=1,#arg do - local a=arg[i] - a=resolvers.resolve(a) - a=unquoted(a) - a=gsub(a,'"','\\"') - if find(a," ") then - result[#result+1]=quoted(a) - else - result[#result+1]=a - end + result[i]=optionalquoted(resolveprefix and resolveprefix(arg[i]) or resolveprefix) end return concat(result," ") else @@ -8708,7 +9424,7 @@ do -- create closure to overcome 200 locals limit package.loaded["luat-env"] = package.loaded["luat-env"] or true --- original size: 5930, stripped down to: 4235 +-- original size: 6174, stripped down to: 4141 if not modules then modules={} end modules ['luat-env']={ version=1.001, @@ -8786,15 +9502,13 @@ function environment.luafilechunk(filename,silent) filename=file.replacesuffix(filename,"lua") local fullname=environment.luafile(filename) if fullname and fullname~="" then - local data=luautilities.loadedluacode(fullname,strippable,filename) - if trace_locating then + local data=luautilities.loadedluacode(fullname,strippable,filename) + if not silent then report_lua("loading file %a %s",fullname,not data and "failed" or "succeeded") - elseif not silent then - texio.write("<",data and "+ " or "- ",fullname,">") end return data else - if trace_locating then + if not silent then report_lua("unknown file %a",filename) end return nil @@ -8863,7 +9577,7 @@ do -- create closure to overcome 200 locals limit package.loaded["lxml-tab"] = package.loaded["lxml-tab"] or true --- original size: 42447, stripped down to: 26589 +-- original size: 45683, stripped down to: 27866 if not modules then modules={} end modules ['lxml-tab']={ version=1.001, @@ -8878,10 +9592,10 @@ if lpeg.setmaxstack then lpeg.setmaxstack(1000) end xml=xml or {} local xml=xml local concat,remove,insert=table.concat,table.remove,table.insert -local type,next,setmetatable,getmetatable,tonumber=type,next,setmetatable,getmetatable,tonumber +local type,next,setmetatable,getmetatable,tonumber,rawset=type,next,setmetatable,getmetatable,tonumber,rawset local lower,find,match,gsub=string.lower,string.find,string.match,string.gsub local utfchar=utf.char -local lpegmatch=lpeg.match +local lpegmatch,lpegpatterns=lpeg.match,lpeg.patterns local P,S,R,C,V,C,Cs=lpeg.P,lpeg.S,lpeg.R,lpeg.C,lpeg.V,lpeg.C,lpeg.Cs local formatters=string.formatters xml.xmlns=xml.xmlns or {} @@ -8976,8 +9690,10 @@ local function add_end(spacing,namespace,tag) top=stack[#stack] if #stack<1 then errorstr=formatters["unable to close %s %s"](tag,xml.checkerror(top,toclose) or "") + report_xml(errorstr) elseif toclose.tg~=tag then errorstr=formatters["unable to close %s with %s %s"](toclose.tg,tag,xml.checkerror(top,toclose) or "") + report_xml(errorstr) end dt=top.dt dt[#dt+1]=toclose @@ -8986,10 +9702,29 @@ local function add_end(spacing,namespace,tag) end end local function add_text(text) + local n=#dt if cleanup and #text>0 then - dt[#dt+1]=cleanup(text) + if n>0 then + local s=dt[n] + if type(s)=="string" then + dt[n]=s..cleanup(text) + else + dt[n+1]=cleanup(text) + end + else + dt[1]=cleanup(text) + end else - dt[#dt+1]=text + if n>0 then + local s=dt[n] + if type(s)=="string" then + dt[n]=s..text + else + dt[n+1]=text + end + else + dt[1]=text + end end end local function add_special(what,spacing,text) @@ -9021,8 +9756,10 @@ local function attribute_specification_error(str) end return str end +local badentity="&error;" +local badentity="&" xml.placeholders={ - unknown_dec_entity=function(str) return str=="" and "&error;" or formatters["&%s;"](str) end, + unknown_dec_entity=function(str) return str=="" and badentity or formatters["&%s;"](str) end, unknown_hex_entity=function(str) return formatters["&#x%s;"](str) end, unknown_any_entity=function(str) return formatters["&#x%s;"](str) end, } @@ -9043,9 +9780,10 @@ local function fromdec(s) return formatters["d:%s"](s),true end end -local rest=(1-P(";"))^0 -local many=P(1)^0 -local parsedentity=P("&")*(P("#x")*(rest/fromhex)+P("#")*(rest/fromdec))*P(";")*P(-1)+(P("#x")*(many/fromhex)+P("#")*(many/fromdec)) +local p_rest=(1-P(";"))^0 +local p_many=P(1)^0 +local p_char=lpegpatterns.utf8character +local parsedentity=P("&")*(P("#x")*(p_rest/fromhex)+P("#")*(p_rest/fromdec))*P(";")*P(-1)+(P("#x")*(p_many/fromhex)+P("#")*(p_many/fromdec)) local predefined_unified={ [38]="&", [42]=""", @@ -9071,7 +9809,9 @@ local privates_u={ local privates_p={} local privates_n={ } -local escaped=utf.remapper(privates_u) +local escaped=utf.remapper(privates_u,"dynamic") +local unprivatized=utf.remapper(privates_p,"dynamic") +xml.unprivatized=unprivatized local function unescaped(s) local p=privates_n[s] if not p then @@ -9084,9 +9824,7 @@ local function unescaped(s) end return p end -local unprivatized=utf.remapper(privates_p) xml.privatetoken=unescaped -xml.unprivatized=unprivatized xml.privatecodes=privates_n local function handle_hex_entity(str) local h=hcache[str] @@ -9181,7 +9919,7 @@ local function handle_any_entity(str) report_xml("keeping entity &%s;",str) end if str=="" then - a="&error;" + a=badentity else a="&"..str..";" end @@ -9209,7 +9947,7 @@ local function handle_any_entity(str) if trace_entities then report_xml("invalid entity &%s;",str) end - a="&error;" + a=badentity acache[str]=a else if trace_entities then @@ -9222,8 +9960,14 @@ local function handle_any_entity(str) return a end end -local function handle_end_entity(chr) - report_xml("error in entity, %a found instead of %a",chr,";") +local function handle_end_entity(str) + report_xml("error in entity, %a found without ending %a",str,";") + return str +end +local function handle_crap_error(chr) + report_xml("error in parsing, unexpected %a found ",chr) + add_text(chr) + return chr end local space=S(' \r\n\t') local open=P('<') @@ -9239,15 +9983,15 @@ local valid=R('az','AZ','09')+S('_-.') local name_yes=C(valid^1)*colon*C(valid^1) local name_nop=C(P(true))*C(valid^1) local name=name_yes+name_nop -local utfbom=lpeg.patterns.utfbom +local utfbom=lpegpatterns.utfbom local spacing=C(space^0) -local anyentitycontent=(1-open-semicolon-space-close)^0 +local anyentitycontent=(1-open-semicolon-space-close-ampersand)^0 local hexentitycontent=R("AF","af","09")^0 local decentitycontent=R("09")^0 local parsedentity=P("#")/""*( P("x")/""*(hexentitycontent/handle_hex_entity)+(decentitycontent/handle_dec_entity) )+(anyentitycontent/handle_any_entity) -local entity=ampersand/""*parsedentity*((semicolon/"")+#(P(1)/handle_end_entity)) +local entity=(ampersand/"")*parsedentity*(semicolon/"")+ampersand*(anyentitycontent/handle_end_entity) local text_unparsed=C((1-open)^1) local text_parsed=Cs(((1-open-ampersand)^1+entity)^1) local somespace=space^1 @@ -9298,16 +10042,20 @@ local instruction=(spacing*begininstruction*someinstruction*endinstruction)/func local comment=(spacing*begincomment*somecomment*endcomment )/function(...) add_special("@cm@",...) end local cdata=(spacing*begincdata*somecdata*endcdata )/function(...) add_special("@cd@",...) end local doctype=(spacing*begindoctype*somedoctype*enddoctype )/function(...) add_special("@dt@",...) end +local crap_parsed=1-beginelement-endelement-emptyelement-begininstruction-begincomment-begincdata-ampersand +local crap_unparsed=1-beginelement-endelement-emptyelement-begininstruction-begincomment-begincdata +local parsedcrap=Cs((crap_parsed^1+entity)^1)/handle_crap_error +local unparsedcrap=Cs((crap_unparsed )^1)/handle_crap_error local trailer=space^0*(text_unparsed/set_message)^0 local grammar_parsed_text=P { "preamble", preamble=utfbom^0*instruction^0*(doctype+comment+instruction)^0*V("parent")*trailer, parent=beginelement*V("children")^0*endelement, - children=parsedtext+V("parent")+emptyelement+comment+cdata+instruction, + children=parsedtext+V("parent")+emptyelement+comment+cdata+instruction+parsedcrap, } local grammar_unparsed_text=P { "preamble", preamble=utfbom^0*instruction^0*(doctype+comment+instruction)^0*V("parent")*trailer, parent=beginelement*V("children")^0*endelement, - children=unparsedtext+V("parent")+emptyelement+comment+cdata+instruction, + children=unparsedtext+V("parent")+emptyelement+comment+cdata+instruction+unparsedcrap, } local function _xmlconvert_(data,settings) settings=settings or {} @@ -9341,7 +10089,6 @@ local function _xmlconvert_(data,settings) errorstr="empty xml file" elseif utfize or resolve then if lpegmatch(grammar_parsed_text,data) then - errorstr="" else errorstr="invalid xml file - parsed text" end @@ -9357,6 +10104,8 @@ local function _xmlconvert_(data,settings) local result if errorstr and errorstr~="" then result={ dt={ { ns="",tg="error",dt={ errorstr },at={},er=true } } } +setmetatable(result,mt) +setmetatable(result.dt[1],mt) setmetatable(stack,mt) local errorhandler=settings.error_handler if errorhandler==false then @@ -9389,8 +10138,11 @@ local function _xmlconvert_(data,settings) end if errorstr and errorstr~="" then result.error=true + else + errorstr=nil end result.statistics={ + errormessage=errorstr, entities={ decimals=dcache, hexadecimals=hcache, @@ -9404,7 +10156,7 @@ local function _xmlconvert_(data,settings) reported_attribute_errors,mt,errorhandler=nil,nil,nil return result end -function xmlconvert(data,settings) +local function xmlconvert(data,settings) local ok,result=pcall(function() return _xmlconvert_(data,settings) end) if ok then return result @@ -9496,14 +10248,17 @@ function xml.checkbom(root) insert(dt,2,"\n" ) end end -local function verbose_element(e,handlers) +local f_attribute=formatters['%s=%q'] +local function verbose_element(e,handlers,escape) local handle=handlers.handle local serialize=handlers.serialize local ens,etg,eat,edt,ern=e.ns,e.tg,e.at,e.dt,e.rn local ats=eat and next(eat) and {} if ats then + local n=0 for k,v in next,eat do - ats[#ats+1]=formatters['%s=%q'](k,escaped(v)) + n=n+1 + ats[n]=f_attribute(k,escaped(v)) end end if ern and trace_entities and ern~=ens then @@ -9588,23 +10343,25 @@ local function verbose_document(e,handlers) end end local function serialize(e,handlers,...) - local initialize=handlers.initialize - local finalize=handlers.finalize - local functions=handlers.functions - if initialize then - local state=initialize(...) - if not state==true then - return state + if e then + local initialize=handlers.initialize + local finalize=handlers.finalize + local functions=handlers.functions + if initialize then + local state=initialize(...) + if not state==true then + return state + end + end + local etg=e.tg + if etg then + (functions[etg] or functions["@el@"])(e,handlers) + else + functions["@dc@"](e,handlers) + end + if finalize then + return finalize() end - end - local etg=e.tg - if etg then - (functions[etg] or functions["@el@"])(e,handlers) - else - functions["@dc@"](e,handlers) - end - if finalize then - return finalize() end end local function xserialize(e,handlers) @@ -9845,7 +10602,7 @@ do -- create closure to overcome 200 locals limit package.loaded["lxml-lpt"] = package.loaded["lxml-lpt"] or true --- original size: 48956, stripped down to: 30516 +-- original size: 48229, stripped down to: 30684 if not modules then modules={} end modules ['lxml-lpt']={ version=1.001, @@ -10230,8 +10987,8 @@ 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")+S("-_:"))^1)*Cc("'])") -lp_fastpos_p=P("+")^0*R("09")^1*P(-1)/"l==%0" -lp_fastpos_n=P("-")*R("09")^1*P(-1)/"(%0<0 and (#list+%0==l))" +local lp_fastpos_p=P("+")^0*R("09")^1*P(-1)/"l==%0" +local lp_fastpos_n=P("-")*R("09")^1*P(-1)/"(%0<0 and (#list+%0==l))" local lp_fastpos=lp_fastpos_n+lp_fastpos_p local lp_reserved=C("and")+C("or")+C("not")+C("div")+C("mod")+C("true")+C("false") local lp_lua_function=Cs((R("az","AZ","__")^1*(P(".")*R("az","AZ","__")^1)^1)*("("))/"%0" @@ -10410,7 +11167,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[i]=(directive and tg) or format("not(%s)",tg) + t[#t+1]=(directive and tg) or format("not(%s)",tg) end if nodetest==false then return format("not(%s)",concat(t,"|")) @@ -10676,7 +11433,6 @@ expressions.print=function(...) print(...) return true end -expressions.contains=find expressions.find=find expressions.upper=upper expressions.lower=lower @@ -10698,6 +11454,9 @@ function expressions.contains(str,pattern) end return false end +function xml.expressions.idstring(str) + return type(str)=="string" and gsub(str,"^#","") or "" +end local function traverse(root,pattern,handle) local collected=applylpath(root,pattern) if collected then @@ -10826,8 +11585,13 @@ function xml.elements(root,pattern,reverse) local collected=applylpath(root,pattern) if not collected then return dummy - elseif reverse then - local c=#collected+1 + end + local n=#collected + if n==0 then + return dummy + end + if reverse then + local c=n+1 return function() if c>1 then c=c-1 @@ -10837,7 +11601,7 @@ function xml.elements(root,pattern,reverse) end end else - local n,c=#collected,0 + local c=0 return function() if c<n then c=c+1 @@ -10852,8 +11616,13 @@ function xml.collected(root,pattern,reverse) local collected=applylpath(root,pattern) if not collected then return dummy - elseif reverse then - local c=#collected+1 + end + local n=#collected + if n==0 then + return dummy + end + if reverse then + local c=n+1 return function() if c>1 then c=c-1 @@ -10861,7 +11630,7 @@ function xml.collected(root,pattern,reverse) end end else - local n,c=#collected,0 + local c=0 return function() if c<n then c=c+1 @@ -10876,7 +11645,7 @@ function xml.inspect(collection,pattern) report_lpath("pattern: %s\n\n%s\n",pattern,xml.tostring(e)) end end -local function split(e) +local function split(e) local dt=e.dt if dt then for i=1,#dt do @@ -10975,7 +11744,7 @@ do -- create closure to overcome 200 locals limit package.loaded["lxml-aux"] = package.loaded["lxml-aux"] or true --- original size: 23804, stripped down to: 16817 +-- original size: 28786, stripped down to: 20578 if not modules then modules={} end modules ['lxml-aux']={ version=1.001, @@ -10985,16 +11754,19 @@ if not modules then modules={} end modules ['lxml-aux']={ license="see context related readme files" } local trace_manipulations=false trackers.register("lxml.manipulations",function(v) trace_manipulations=v end) +local trace_inclusions=false trackers.register("lxml.inclusions",function(v) trace_inclusions=v end) local report_xml=logs.reporter("xml") local xml=xml -local xmlconvert,xmlcopy,xmlname=xml.convert,xml.copy,xml.name +local xmlcopy,xmlname=xml.copy,xml.name local xmlinheritedconvert=xml.inheritedconvert local xmlapplylpath=xml.applylpath local xmlfilter=xml.filter -local type,setmetatable,getmetatable=type,setmetatable,getmetatable +local type,next,setmetatable,getmetatable=type,next,setmetatable,getmetatable local insert,remove,fastcopy,concat=table.insert,table.remove,table.fastcopy,table.concat local gmatch,gsub,format,find,strip=string.gmatch,string.gsub,string.format,string.find,string.strip local utfbyte=utf.byte +local lpegmatch=lpeg.match +local striplinepatterns=utilities.strings.striplinepatterns local function report(what,pattern,c,e) report_xml("%s element %a, root %a, position %a, index %a, pattern %a",what,xmlname(e),xmlname(e.__p__),c,e.ni,pattern) end @@ -11049,13 +11821,15 @@ end function xml.each(root,pattern,handle,reverse) local collected=xmlapplylpath(root,pattern) if collected then - if reverse then - for c=#collected,1,-1 do - handle(collected[c]) - end - else - for c=1,#collected do - handle(collected[c]) + if handle then + if reverse then + for c=#collected,1,-1 do + handle(collected[c]) + end + else + for c=1,#collected do + handle(collected[c]) + end end end return collected @@ -11111,6 +11885,7 @@ local function redo_ni(d) end end end +xml.reindex=redo_ni local function xmltoelement(whatever,root) if not whatever then return nil @@ -11162,8 +11937,16 @@ function xml.delete(root,pattern) report('deleting',pattern,c,e) end local d=p.dt - remove(d,e.ni) - redo_ni(d) + local ni=e.ni + if ni<=#d then + if false then + p.dt[ni]="" + else + remove(d,ni) + redo_ni(d) + end + else + end end end end @@ -11283,28 +12066,40 @@ 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) +local function include(xmldata,pattern,attribute,recursive,loaddata,level) pattern=pattern or 'include' loaddata=loaddata or io.loaddata local collected=xmlapplylpath(xmldata,pattern) if collected then + if not level then + level=1 + end for c=1,#collected do local ek=collected[c] local name=nil local ekdt=ek.dt local ekat=ek.at - local epdt=ek.__p__.dt + local ekrt=ek.__p__ + local epdt=ekrt.dt if not attribute or attribute=="" then name=(type(ekdt)=="table" and ekdt[1]) or ekdt end if not name then for a in gmatch(attribute or "href","([^|]+)") do name=ekat[a] - if name then break end + if name then + break + end + end + end + local data=nil + if name and name~="" then + data=loaddata(name) or "" + if trace_inclusions then + report_xml("including %s bytes from %a at level %s by pattern %a and attribute %a (%srecursing)",#data,name,level,pattern,attribute or "",recursive and "" or "not ") end end - local data=(name and name~="" and loaddata(name)) or "" - if data=="" then + if not data or data=="" then epdt[ek.ni]="" elseif ekat["parse"]=="text" then epdt[ek.ni]=xml.escaped(data) @@ -11314,70 +12109,127 @@ local function include(xmldata,pattern,attribute,recursive,loaddata) epdt[ek.ni]="" else if recursive then - include(xi,pattern,attribute,recursive,loaddata) + include(xi,pattern,attribute,recursive,loaddata,level+1) + end + local child=xml.body(xi) + child.__p__=ekrt + child.__f__=name + epdt[ek.ni]=child + local inclusions=xmldata.settings.inclusions + if inclusions then + inclusions[#inclusions+1]=name + else + xmldata.settings.inclusions={ name } + end + if child.er then + local badinclusions=xmldata.settings.badinclusions + if badinclusions then + badinclusions[#badinclusions+1]=name + else + xmldata.settings.badinclusions={ name } + end end - epdt[ek.ni]=xml.body(xi) end end end end end xml.include=include +function xml.inclusion(e,default) + while e do + local f=e.__f__ + if f then + return f + else + e=e.__p__ + end + end + return default +end +local function getinclusions(key,e,sorted) + while e do + local settings=e.settings + if settings then + local inclusions=settings[key] + if inclusions then + inclusions=table.unique(inclusions) + if sorted then + table.sort(inclusions) + end + return inclusions + else + e=e.__p__ + end + else + e=e.__p__ + end + end +end +function xml.inclusions(e,sorted) + return getinclusions("inclusions",e,sorted) +end +function xml.badinclusions(e,sorted) + return getinclusions("badinclusions",e,sorted) +end +local b_collapser=lpeg.patterns.b_collapser +local m_collapser=lpeg.patterns.m_collapser +local e_collapser=lpeg.patterns.e_collapser +local b_stripper=lpeg.patterns.b_stripper +local m_stripper=lpeg.patterns.m_stripper +local e_stripper=lpeg.patterns.e_stripper +local lpegmatch=lpeg.match 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 n=#edt + if n==0 then + return e + elseif anywhere then + local t={} + local m=0 + for e=1,n do local str=edt[e] if type(str)~="string" then - n=n+1 - t[n]=str + m=m+1 + t[m]=str elseif str~="" then if nolines then - str=gsub(str,"%s+"," ") + str=lpegmatch((n==1 and b_collapser) or (n==m and e_collapser) or m_collapser,str) + else + str=lpegmatch((n==1 and b_stripper) or (n==m and e_stripper) or m_stripper,str) end - str=gsub(str,"^%s*(.-)%s*$","%1") if str~="" then - n=n+1 - t[n]=str + m=m+1 + t[m]=str end end end e.dt=t else - if #edt>0 then - local str=edt[1] - if type(str)~="string" then - elseif str=="" then + local str=edt[1] + if type(str)=="string" then + if str~="" then + str=lpegmatch(nolines and b_collapser or b_stripper,str) + end + if str=="" then remove(edt,1) + n=n-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 + edt[1]=str end end - local nedt=#edt - if nedt>0 then - local str=edt[nedt] - if type(str)~="string" then - elseif str=="" then - remove(edt) - else - if nolines then - str=gsub(str,"%s+"," ") - end - str=gsub(str,"%s+$","") + if n>0 then + str=edt[n] + if type(str)=="string" then if str=="" then remove(edt) else - edt[nedt]=str + str=lpegmatch(nolines and e_collapser or e_stripper,str) + if str=="" then + remove(edt) + else + edt[n]=str + end end end end @@ -11563,8 +12415,8 @@ function xml.finalizers.xml.cdata(collected) end return "" end -function xml.insertcomment(e,str,n) - table.insert(e.dt,n or 1,{ +function xml.insertcomment(e,str,n) + insert(e.dt,n or 1,{ tg="@cm@", ns="", special=true, @@ -11572,7 +12424,25 @@ function xml.insertcomment(e,str,n) dt={ str }, }) end -function xml.setcdata(e,str) +function xml.insertcdata(e,str,n) + insert(e.dt,n or 1,{ + tg="@cd@", + ns="", + special=true, + at={}, + dt={ str }, + }) +end +function xml.setcomment(e,str,n) + e.dt={ { + tg="@cm@", + ns="", + special=true, + at={}, + dt={ str }, + } } +end +function xml.setcdata(e,str) e.dt={ { tg="@cd@", ns="", @@ -11642,7 +12512,7 @@ local function recurse(e,action) for i=1,#edt do local str=edt[i] if type(str)~="string" then - recurse(str,action,recursive) + recurse(str,action) elseif str~="" then edt[i]=action(str) end @@ -11660,6 +12530,65 @@ function helpers.recursetext(collected,action,recursive) end end end +local specials={ + ["@rt@"]="root", + ["@pi@"]="instruction", + ["@cm@"]="comment", + ["@dt@"]="declaration", + ["@cd@"]="cdata", +} +local function convert(x,strip,flat) + local ns=x.ns + local tg=x.tg + local at=x.at + local dt=x.dt + local node=flat and { + [0]=(not x.special and (ns~="" and ns..":"..tg or tg)) or nil, + } or { + _namespace=ns~="" and ns or nil, + _tag=not x.special and tg or nil, + _type=specials[tg] or "_element", + } + if at then + for k,v in next,at do + node[k]=v + end + end + local n=0 + for i=1,#dt do + local di=dt[i] + if type(di)=="table" then + if flat and di.special then + else + di=convert(di,strip,flat) + if di then + n=n+1 + node[n]=di + end + end + elseif strip then + di=lpegmatch(strip,di) + if di~="" then + n=n+1 + node[n]=di + end + else + n=n+1 + node[n]=di + end + end + if next(node) then + return node + end +end +function xml.totable(x,strip,flat) + if type(x)=="table" then + if strip then + strip=striplinepatterns[strip] + end + return convert(x,strip,flat) + end +end end -- of closure @@ -12216,7 +13145,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-ini"] = package.loaded["data-ini"] or true --- original size: 7898, stripped down to: 5501 +-- original size: 11085, stripped down to: 7662 if not modules then modules={} end modules ['data-ini']={ version=1.001, @@ -12225,14 +13154,15 @@ if not modules then modules={} end modules ['data-ini']={ copyright="PRAGMA ADE / ConTeXt Development Team", license="see context related readme files", } +local next,type,getmetatable,rawset=next,type,getmetatable,rawset local gsub,find,gmatch,char=string.gsub,string.find,string.gmatch,string.char -local next,type=next,type local filedirname,filebasename,filejoin=file.dirname,file.basename,file.join +local ostype,osname,osuname,ossetenv,osgetenv=os.type,os.name,os.uname,os.setenv,os.getenv +local P,S,R,C,Cs,Cc,lpegmatch=lpeg.P,lpeg.S,lpeg.R,lpeg.C,lpeg.Cs,lpeg.Cc,lpeg.match 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 report_initialization=logs.reporter("resolvers","initialization") -local ostype,osname,ossetenv,osgetenv=os.type,os.name,os.setenv,os.getenv resolvers=resolvers or {} local resolvers=resolvers texconfig.kpse_init=false @@ -12360,15 +13290,108 @@ if not texroot or texroot=="" then ossetenv('TEXROOT',texroot) end environment.texroot=file.collapsepath(texroot) -if profiler then +if type(profiler)=="table" and not jit then directives.register("system.profile",function() profiler.start("luatex-profile.log") end) end -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 +local prefixes=utilities.storage.allocate() +resolvers.prefixes=prefixes +local resolved={} +local abstract={} +local dynamic={} +function resolvers.resetresolve(str) + resolved,abstract={},{} +end +function resolvers.allprefixes(separator) + local all=table.sortedkeys(prefixes) + if separator then + for i=1,#all do + all[i]=all[i]..":" + end + end + return all +end +local function _resolve_(method,target) + local action=prefixes[method] + if action then + return action(target) + else + return method..":"..target + end +end +function resolvers.unresolve(str) + return abstract[str] or str +end +function resolvers.setdynamic(str) + dynamic[str]=true +end +local pattern=Cs((C(R("az")^2)*P(":")*C((1-S(" \"\';,"))^1)/_resolve_+P(1))^0) +local prefix=C(R("az")^2)*P(":") +local target=C((1-S(" \"\';,"))^1) +local notarget=(#S(";,")+P(-1))*Cc("") +local p_resolve=Cs(((prefix*(target+notarget))/_resolve_+P(1))^0) +local p_simple=prefix*P(-1) +local function resolve(str) + if type(str)=="table" then + local res={} + for i=1,#str do + res[i]=resolve(str[i]) + end + return res + end + local res=resolved[str] + if res then + return res + end + local simple=lpegmatch(p_simple,str) + local action=prefixes[simple] + if action then + local res=action(res) + if not dynamic[simple] then + resolved[simple]=res + abstract[res]=simple + end + return res + end + res=lpegmatch(p_resolve,str) + resolved[str]=res + abstract[res]=str + return res +end +resolvers.resolve=resolve +if type(osuname)=="function" then + for k,v in next,osuname() do + if not prefixes[k] then + prefixes[k]=function() return v end + end + end +end +if ostype=="unix" then + local pattern + local function makepattern(t,k,v) + if t then + rawset(t,k,v) + end + local colon=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) + end + makepattern() + table.setmetatablenewindex(prefixes,makepattern) + function resolvers.repath(str) + return lpegmatch(pattern,str) + end +else + function resolvers.repath(str) + return str + end end @@ -12378,7 +13401,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-exp"] = package.loaded["data-exp"] or true --- original size: 15303, stripped down to: 9716 +-- original size: 17216, stripped down to: 10657 if not modules then modules={} end modules ['data-exp']={ version=1.001, @@ -12392,12 +13415,16 @@ local concat,sort=table.concat,table.sort local lpegmatch,lpegpatterns=lpeg.match,lpeg.patterns local Ct,Cs,Cc,Carg,P,C,S=lpeg.Ct,lpeg.Cs,lpeg.Cc,lpeg.Carg,lpeg.P,lpeg.C,lpeg.S local type,next=type,next +local isdir=lfs.isdir local ostype=os.type -local collapsepath=file.collapsepath +local collapsepath,joinpath,basename=file.collapsepath,file.join,file.basename 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) +local trace_globbing=true trackers.register("resolvers.globbing",function(v) trace_globbing=v end) local report_expansions=logs.reporter("resolvers","expansions") +local report_globbing=logs.reporter("resolvers","globbing") local resolvers=resolvers +local resolveprefix=resolvers.resolve local function f_both(a,b) local t,n={},0 for sb in gmatch(b,"[^,]+") do @@ -12487,35 +13514,27 @@ function resolvers.expandedpathfromlist(pathlist) end return newlist end -local cleanup=lpeg.replacer { - { "!","" }, - { "\\","/" }, -} -function resolvers.cleanpath(str) - local doslashes=(P("\\")/"/"+1)^0 - local donegation=(P("!")/"" )^0 - local homedir=lpegmatch(Cs(donegation*doslashes),environment.homedir or "") - if homedir=="~" 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 not str or find(str,"~") then - return "" - else - return lpegmatch(cleanup,str) +local usedhomedir=nil +local donegation=(P("!")/"" )^0 +local doslashes=(P("\\")/"/"+1)^0 +local function expandedhome() + if not usedhomedir then + usedhomedir=lpegmatch(Cs(donegation*doslashes),environment.homedir or "") + if usedhomedir=="~" or usedhomedir=="" or not isdir(usedhomedir) then + if trace_expansions then + report_expansions("no home dir set, ignoring dependent path using current path") end - end - else - local dohome=((P("~")+P("$HOME"))/homedir)^0 - local cleanup=Cs(donegation*dohome*doslashes) - function resolvers.cleanpath(str) - return str and lpegmatch(cleanup,str) or "" + usedhomedir="." end end - return resolvers.cleanpath(str) + return usedhomedir end -local expandhome=P("~")/"$HOME" +local dohome=((P("~")+P("$HOME")+P("%HOME%"))/expandedhome)^0 +local cleanup=Cs(donegation*dohome*doslashes) +resolvers.cleanpath=function(str) + return str and lpegmatch(cleanup,str) or "" +end +local expandhome=P("~")/"$HOME" local dodouble=P('"')/""*(expandhome+(1-P('"')))^0*P('"')/"" local dosingle=P("'")/""*(expandhome+(1-P("'")))^0*P("'")/"" local dostring=(expandhome+1 )^0 @@ -12567,46 +13586,67 @@ function resolvers.splitpath(str) end function resolvers.joinpath(str) if type(str)=='table' then - return file.joinpath(str) + return joinpath(str) else return str end end local attributes,directory=lfs.attributes,lfs.dir local weird=P(".")^1+lpeg.anywhere(S("~`!#$%^&*()={}[]:;\"\'||<>,?\n\r\t")) +local lessweird=P(".")^1+lpeg.anywhere(S("~`#$%^&*:;\"\'||<>,?\n\r\t")) local timer={} local scanned={} local nofscans=0 local scancache={} -local function scan(files,spec,path,n,m,r) - local full=(path=="" and spec) or (spec..path..'/') +local fullcache={} +local nofsharedscans=0 +local function scan(files,remap,spec,path,n,m,r,onlyone,tolerant) + local full=path=="" and spec or (spec..path..'/') local dirs={} local nofdirs=0 + local pattern=tolerant and lessweird or weird for name in directory(full) do - if not lpegmatch(weird,name) then - local mode=attributes(full..name,'mode') - if mode=='file' then + if not lpegmatch(pattern,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 } + local lower=lower(name) + local paths=files[lower] + if paths then + if onlyone then else - f[#f+1]=path + if type(paths)=="string" then + files[lower]={ paths,path } + else + paths[#paths+1]=path + end + if name~=lower then + local rl=remap[lower] + if not rl then + remap[lower]=name + r=r+1 + elseif trace_globbing and rl~=name then + report_globbing("confusing filename, name: %a, lower: %a, already: %a",name,lower,rl) + end + end end else - files[name]=path - local lower=lower(name) + files[lower]=path if name~=lower then - files["remap:"..lower]=name - r=r+1 + local rl=remap[lower] + if not rl then + remap[lower]=name + r=r+1 + elseif trace_globbing and rl~=name then + report_globbing("confusing filename, name: %a, lower: %a, already: %a",name,lower,rl) + end end end - elseif mode=='directory' then + elseif mode=="directory" then m=m+1 nofdirs=nofdirs+1 if path~="" then - dirs[nofdirs]=path..'/'..name + dirs[nofdirs]=path.."/"..name else dirs[nofdirs]=name end @@ -12616,107 +13656,69 @@ local function scan(files,spec,path,n,m,r) if nofdirs>0 then sort(dirs) for i=1,nofdirs do - files,n,m,r=scan(files,spec,dirs[i],n,m,r) + files,remap,n,m,r=scan(files,remap,spec,dirs[i],n,m,r,onlyonce,tolerant) end end scancache[sub(full,1,-2)]=files - return files,n,m,r + return files,remap,n,m,r end -local fullcache={} -function resolvers.scanfiles(path,branch,usecache) - statistics.starttiming(timer) - local realpath=resolvers.resolve(path) +function resolvers.scanfiles(path,branch,usecache,onlyonce,tolerant) + local realpath=resolveprefix(path) if usecache then - local files=fullcache[realpath] - if files then + local content=fullcache[realpath] + if content then if trace_locating then - report_expansions("using caches scan of path %a, branch %a",path,branch or path) + report_expansions("using cached scan of path %a, branch %a",path,branch or path) end - return files + nofsharedscans=nofsharedscans+1 + return content end end + statistics.starttiming(timer) if trace_locating then report_expansions("scanning path %a, branch %a",path,branch or path) end - local files,n,m,r=scan({},realpath..'/',"",0,0,0) - files.__path__=path - 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 - if usecache then - scanned[#scanned+1]=realpath - fullcache[realpath]=files - end - nofscans=nofscans+1 - statistics.stoptiming(timer) - return files -end -local function simplescan(files,spec,path) - local full=(path=="" and spec) or (spec..path..'/') - local dirs={} - local nofdirs=0 - for name in directory(full) do - if not lpegmatch(weird,name) then - local mode=attributes(full..name,'mode') - if mode=='file' then - if not files[name] then - files[name]=path - end - elseif mode=='directory' then - 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=simplescan(files,spec,dirs[i]) - end - end - return files -end -local simplecache={} -local nofsharedscans=0 -function resolvers.simplescanfiles(path,branch,usecache) - statistics.starttiming(timer) - local realpath=resolvers.resolve(path) - if usecache then - local files=simplecache[realpath] - if not files then - files=scancache[realpath] - if files then - nofsharedscans=nofsharedscans+1 - end + local content + if isdir(realpath) then + local files,remap,n,m,r=scan({},{},realpath..'/',"",0,0,0,onlyonce,tolerant) + content={ + metadata={ + path=path, + files=n, + directories=m, + remappings=r, + }, + files=files, + remap=remap, + } + if trace_locating then + report_expansions("%s files found on %s directories with %s uppercase remappings",n,m,r) end - if files then - if trace_locating then - report_expansions("using caches scan of path %a, branch %a",path,branch or path) - end - return files + else + content={ + metadata={ + path=path, + files=0, + directories=0, + remappings=0, + }, + files={}, + remap={}, + } + if trace_locating then + report_expansions("invalid path %a",realpath) end end - if trace_locating then - report_expansions("scanning path %a, branch %a",path,branch or path) - end - local files=simplescan({},realpath..'/',"") - if trace_locating then - report_expansions("%s files found",table.count(files)) - end if usecache then scanned[#scanned+1]=realpath - simplecache[realpath]=files + fullcache[realpath]=content end nofscans=nofscans+1 statistics.stoptiming(timer) - return files + return content +end +function resolvers.simplescanfiles(path,branch,usecache) + return resolvers.scanfiles(path,branch,usecache,true,true) end function resolvers.scandata() table.sort(scanned) @@ -12727,6 +13729,52 @@ function resolvers.scandata() paths=scanned, } end +function resolvers.get_from_content(content,path,name) + if not content then + return + end + local files=content.files + if not files then + return + end + local remap=content.remap + if not remap then + return + end + if name then + local used=lower(name) + return path,remap[used] or used + else + local name=path + local used=lower(name) + local path=files[used] + if path then + return path,remap[used] or used + end + end +end +local nothing=function() end +function resolvers.filtered_from_content(content,pattern) + if content and type(pattern)=="string" then + local pattern=lower(pattern) + local files=content.files + local remap=content.remap + if files and remap then + local n=next(files) + local function iterator() + while n do + local k=n + n=next(files,k) + if find(k,pattern) then + return files[k],remap and remap[k] or k + end + end + end + return iterator + end + end + return nothing +end end -- of closure @@ -12735,7 +13783,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-env"] = package.loaded["data-env"] or true --- original size: 8769, stripped down to: 6490 +-- original size: 9216, stripped down to: 6798 if not modules then modules={} end modules ['data-env']={ version=1.001, @@ -12753,10 +13801,12 @@ local formats=allocate() local suffixes=allocate() local dangerous=allocate() local suffixmap=allocate() +local usertypes=allocate() resolvers.formats=formats resolvers.suffixes=suffixes resolvers.dangerous=dangerous resolvers.suffixmap=suffixmap +resolvers.usertypes=usertypes local luasuffixes=utilities.lua.suffixes local relations=allocate { core={ @@ -12824,11 +13874,13 @@ local relations=allocate { names={ "mp" }, variable='MPINPUTS', suffixes={ 'mp','mpvi','mpiv','mpii' }, + usertype=true, }, tex={ names={ "tex" }, variable='TEXINPUTS', - suffixes={ 'tex',"mkvi","mkiv","mkii" }, + suffixes={ "tex","mkvi","mkiv","mkii","cld","lfg","xml" }, + usertype=true, }, icc={ names={ "icc","icc profile","icc profiles" }, @@ -12844,6 +13896,7 @@ local relations=allocate { names={ "lua" }, variable='LUAINPUTS', suffixes={ luasuffixes.lua,luasuffixes.luc,luasuffixes.tma,luasuffixes.tmc }, + usertype=true, }, lib={ names={ "lib" }, @@ -12852,11 +13905,15 @@ local relations=allocate { }, bib={ names={ 'bib' }, + variable='BIBINPUTS', suffixes={ 'bib' }, + usertype=true, }, bst={ names={ 'bst' }, + variable='BSTINPUTS', suffixes={ 'bst' }, + usertype=true, }, fontconfig={ names={ 'fontconfig','fontconfig file','fontconfig files' }, @@ -12938,8 +13995,9 @@ function resolvers.updaterelations() for name,relation in next,categories do local rn=relation.names local rv=relation.variable - local rs=relation.suffixes if rn and rv then + local rs=relation.suffixes + local ru=relation.usertype for i=1,#rn do local rni=lower(gsub(rn[i]," ","")) formats[rni]=rv @@ -12951,8 +14009,9 @@ function resolvers.updaterelations() end end end - end - if rs then + if ru then + usertypes[name]=true + end end end end @@ -13003,7 +14062,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-tmp"] = package.loaded["data-tmp"] or true --- original size: 15532, stripped down to: 11648 +-- original size: 15618, stripped down to: 11629 if not modules then modules={} end modules ['data-tmp']={ version=1.100, @@ -13013,7 +14072,7 @@ if not modules then modules={} end modules ['data-tmp']={ license="see context related readme files" } local format,lower,gsub,concat=string.format,string.lower,string.gsub,table.concat -local concat,serialize,serializetofile=table.concat,table.serialize,table.tofile +local concat=table.concat local mkdirs,isdir,isfile=dir.mkdirs,lfs.isdir,lfs.isfile local addsuffix,is_writable,is_readable=file.addsuffix,file.is_writable,file.is_readable local formatters=string.formatters @@ -13022,6 +14081,7 @@ local trace_cache=false trackers.register("resolvers.cache",function(v) trace_ca local report_caches=logs.reporter("resolvers","caches") local report_resolvers=logs.reporter("resolvers","caching") local resolvers=resolvers +local cleanpath=resolvers.cleanpath local directive_cleanup=false directives.register("system.compile.cleanup",function(v) directive_cleanup=v end) local directive_strip=false directives.register("system.compile.strip",function(v) directive_strip=v end) local compile=utilities.lua.compile @@ -13043,7 +14103,7 @@ caches.relocate=false caches.defaults={ "TMPDIR","TEMPDIR","TMP","TEMP","HOME","HOMEPATH" } local writable,readables,usedreadables=nil,{},{} local function identify() - local texmfcaches=resolvers.cleanpathlist("TEXMFCACHE") + local texmfcaches=resolvers.cleanpathlist("TEXMFCACHE") if texmfcaches then for k=1,#texmfcaches do local cachepath=texmfcaches[k] @@ -13281,15 +14341,11 @@ end local saveoptions={ compact=true } 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,serialize(data,true,saveoptions)) + file.savedata(tmaname,table.serialize(data,true,saveoptions)) else - serializetofile(tmaname,data,true,saveoptions) + table.tofile(tmaname,data,true,saveoptions) end utilities.lua.compile(tmaname,tmcname) end @@ -13297,10 +14353,12 @@ local content_state={} function caches.contentstate() return content_state or {} end -function caches.loadcontent(cachename,dataname) - local name=caches.hashed(cachename) - local full,path=caches.getfirstreadablefile(addsuffix(name,luasuffixes.lua),"trees") - local filename=file.join(path,name) +function caches.loadcontent(cachename,dataname,filename) + if not filename then + local name=caches.hashed(cachename) + local full,path=caches.getfirstreadablefile(addsuffix(name,luasuffixes.lua),"trees") + filename=file.join(path,name) + end local blob=loadfile(addsuffix(filename,luasuffixes.luc)) or loadfile(addsuffix(filename,luasuffixes.lua)) if blob then local data=blob() @@ -13332,10 +14390,12 @@ function caches.collapsecontent(content) end end end -function caches.savecontent(cachename,dataname,content) - local name=caches.hashed(cachename) - local full,path=caches.setfirstwritablefile(addsuffix(name,luasuffixes.lua),"trees") - local filename=file.join(path,name) +function caches.savecontent(cachename,dataname,content,filename) + if not filename then + local name=caches.hashed(cachename) + local full,path=caches.setfirstwritablefile(addsuffix(name,luasuffixes.lua),"trees") + filename=file.join(path,name) + end local luaname=addsuffix(filename,luasuffixes.lua) local lucname=addsuffix(filename,luasuffixes.luc) if trace_locating then @@ -13350,7 +14410,7 @@ function caches.savecontent(cachename,dataname,content) content=content, uuid=os.uuid(), } - local ok=io.savedata(luaname,serialize(data,true)) + local ok=io.savedata(luaname,table.serialize(data,true)) if ok then if trace_locating then report_resolvers("category %a, cachename %a saved in %a",dataname,cachename,luaname) @@ -13378,7 +14438,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-met"] = package.loaded["data-met"] or true --- original size: 5453, stripped down to: 4007 +-- original size: 5347, stripped down to: 4015 if not modules then modules={} end modules ['data-met']={ version=1.100, @@ -13406,8 +14466,8 @@ local function splitmethod(filename) if type(filename)=="table" then return filename end - filename=file.collapsepath(filename,".") - if not find(filename,"://") then + filename=file.collapsepath(filename,".") + if not find(filename,"://",1,true) then return { scheme="file",path=filename,original=filename,filename=filename } end local specification=url.hashed(filename) @@ -13497,7 +14557,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-res"] = package.loaded["data-res"] or true --- original size: 61799, stripped down to: 42957 +-- original size: 67003, stripped down to: 46291 if not modules then modules={} end modules ['data-res']={ version=1.001, @@ -13507,7 +14567,7 @@ if not modules then modules={} end modules ['data-res']={ license="see context related readme files", } local gsub,find,lower,upper,match,gmatch=string.gsub,string.find,string.lower,string.upper,string.match,string.gmatch -local concat,insert,sortedkeys=table.concat,table.insert,table.sortedkeys +local concat,insert,remove,sortedkeys,sortedhash=table.concat,table.insert,table.remove,table.sortedkeys,table.sortedhash local next,type,rawget=next,type,rawget local os=os 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 @@ -13516,27 +14576,38 @@ local formatters=string.formatters local filedirname=file.dirname local filebasename=file.basename local suffixonly=file.suffixonly +local addsuffix=file.addsuffix +local removesuffix=file.removesuffix local filejoin=file.join local collapsepath=file.collapsepath local joinpath=file.joinpath +local is_qualified_path=file.is_qualified_path local allocate=utilities.storage.allocate local settings_to_array=utilities.parsers.settings_to_array +local getcurrentdir=lfs.currentdir +local isfile=lfs.isfile +local isdir=lfs.isdir local setmetatableindex=table.setmetatableindex local luasuffixes=utilities.lua.suffixes -local getcurrentdir=lfs.currentdir -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 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 trace_paths=false trackers .register("resolvers.paths",function(v) trace_paths=v end) +local resolve_otherwise=true directives.register("resolvers.otherwise",function(v) resolve_otherwise=v end) local report_resolving=logs.reporter("resolvers","resolving") local resolvers=resolvers local expandedpathfromlist=resolvers.expandedpathfromlist local checkedvariable=resolvers.checkedvariable local splitconfigurationpath=resolvers.splitconfigurationpath local methodhandler=resolvers.methodhandler +local filtered=resolvers.filtered_from_content +local lookup=resolvers.get_from_content +local cleanpath=resolvers.cleanpath +local resolveprefix=resolvers.resolve local initializesetter=utilities.setters.initialize local ostype,osname,osenv,ossetenv,osgetenv=os.type,os.name,os.env,os.setenv,os.getenv -resolvers.cacheversion='1.0.1' -resolvers.configbanner='' +resolvers.cacheversion="1.100" +resolvers.configbanner="" resolvers.homedir=environment.homedir resolvers.criticalvars=allocate { "SELFAUTOLOC","SELFAUTODIR","SELFAUTOPARENT","TEXMFCNF","TEXMF","TEXOS" } resolvers.luacnfname="texmfcnf.lua" @@ -13555,6 +14626,7 @@ end local unset_variable="unset" local formats=resolvers.formats local suffixes=resolvers.suffixes +local usertypes=resolvers.usertypes local dangerous=resolvers.dangerous local suffixmap=resolvers.suffixmap resolvers.defaultsuffixes={ "tex" } @@ -13563,7 +14635,7 @@ local instance=resolvers.instance or nil function resolvers.setenv(key,value,raw) if instance then instance.environment[key]=value - ossetenv(key,raw and value or resolvers.resolve(value)) + ossetenv(key,raw and value or resolveprefix(value)) end end local function getenv(key) @@ -13577,7 +14649,7 @@ local function getenv(key) end resolvers.getenv=getenv resolvers.env=getenv -local function resolve(k) +local function resolvevariable(k) return instance.expansions[k] end local dollarstripper=lpeg.stripper("$") @@ -13586,19 +14658,19 @@ local backslashswapper=lpeg.replacer("\\","/") local somevariable=P("$")/"" local somekey=C(R("az","AZ","09","__","--")^1) local somethingelse=P(";")*((1-S("!{}/\\"))^1*P(";")/"")+P(";")*(P(";")/"")+P(1) -local variableexpander=Cs((somevariable*(somekey/resolve)+somethingelse)^1 ) +local variableexpander=Cs((somevariable*(somekey/resolvevariable)+somethingelse)^1 ) local cleaner=P("\\")/"/"+P(";")*S("!{}/\\")^0*P(";")^1/";" local variablecleaner=Cs((cleaner+P(1))^0) -local somevariable=R("az","AZ","09","__","--")^1/resolve +local somevariable=R("az","AZ","09","__","--")^1/resolvevariable local variable=(P("$")/"")*(somevariable+(P("{")/"")*somevariable*(P("}")/"")) local variableresolver=Cs((variable+P(1))^0) local function expandedvariable(var) return lpegmatch(variableexpander,var) or var end -function resolvers.newinstance() - if trace_locating then +function resolvers.newinstance() + if trace_locating then report_resolving("creating instance") - end + end local environment,variables,expansions,order=allocate(),allocate(),allocate(),allocate() local newinstance={ environment=environment, @@ -13611,6 +14683,7 @@ function resolvers.newinstance() foundintrees=allocate(), hashes=allocate(), hashed=allocate(), + pathlists=false, specification=allocate(), lists=allocate(), data=allocate(), @@ -13623,6 +14696,7 @@ function resolvers.newinstance() savelists=true, pattern=nil, force_suffixes=true, + pathstack={}, } setmetatableindex(variables,function(t,k) local v @@ -13672,8 +14746,13 @@ function resolvers.reset() end local function reset_hashes() instance.lists={} + instance.pathlists=false instance.found={} end +local function reset_caches() + instance.lists={} + instance.pathlists=false +end local slash=P("/") local pathexpressionpattern=Cs ( Cc("^")*( @@ -13725,13 +14804,13 @@ local function identify_configuration_files() for i=1,#cnfpaths do local filepath=cnfpaths[i] local filename=collapsepath(filejoin(filepath,luacnfname)) - local realname=resolvers.resolve(filename) + local realname=resolveprefix(filename) if trace_locating then - local fullpath=gsub(resolvers.resolve(collapsepath(filepath)),"//","/") - local weirdpath=find(fullpath,"/texmf.+/texmf") or not find(fullpath,"/web2c") + local fullpath=gsub(resolveprefix(collapsepath(filepath)),"//","/") + local weirdpath=find(fullpath,"/texmf.+/texmf") or not find(fullpath,"/web2c",1,true) report_resolving("looking for %a on %s path %a from specification %a",luacnfname,weirdpath and "weird" or "given",fullpath,filepath) end - if lfs.isfile(realname) then + if isfile(realname) then specification[#specification+1]=filename if trace_locating then report_resolving("found configuration file %a",realname) @@ -13753,7 +14832,7 @@ local function load_configuration_files() local filename=specification[i] local pathname=filedirname(filename) local filename=filejoin(pathname,luacnfname) - local realname=resolvers.resolve(filename) + local realname=resolveprefix(filename) local blob=loadfile(realname) if blob then local setups=instance.setups @@ -13761,7 +14840,7 @@ local function load_configuration_files() local parent=data and data.parent if parent then local filename=filejoin(pathname,parent) - local realname=resolvers.resolve(filename) + local realname=resolveprefix(filename) local blob=loadfile(realname) if blob then local parentdata=blob() @@ -13786,7 +14865,7 @@ local function load_configuration_files() elseif variables[k]==nil then if trace_locating and not warning then report_resolving("variables like %a in configuration file %a should move to the 'variables' subtable", - k,resolvers.resolve(filename)) + k,resolveprefix(filename)) warning=true end variables[k]=v @@ -13846,7 +14925,7 @@ local function locate_file_databases() local stripped=lpegmatch(inhibitstripper,path) if stripped~="" then local runtime=stripped==path - path=resolvers.cleanpath(path) + path=cleanpath(path) local spec=resolvers.splitmethod(stripped) if runtime and (spec.noscheme or spec.scheme=="file") then stripped="tree:///"..stripped @@ -13909,8 +14988,8 @@ function resolvers.renew(hashname) report_resolving("identifying tree %a",hashname) end end - local realpath=resolvers.resolve(hashname) - if lfs.isdir(realpath) then + local realpath=resolveprefix(hashname) + if isdir(realpath) then if trace_locating then report_resolving("using path %a",realpath) end @@ -14011,19 +15090,53 @@ end function resolvers.unexpandedpath(str) return joinpath(resolvers.unexpandedpathlist(str)) end +function resolvers.pushpath(name) + local pathstack=instance.pathstack + local lastpath=pathstack[#pathstack] + local pluspath=filedirname(name) + if lastpath then + lastpath=collapsepath(filejoin(lastpath,pluspath)) + else + lastpath=collapsepath(pluspath) + end + insert(pathstack,lastpath) + if trace_paths then + report_resolving("pushing path %a",lastpath) + end +end +function resolvers.poppath() + local pathstack=instance.pathstack + if trace_paths and #pathstack>0 then + report_resolving("popping path %a",pathstack[#pathstack]) + end + remove(pathstack) +end +function resolvers.stackpath() + local pathstack=instance.pathstack + local currentpath=pathstack[#pathstack] + return currentpath~="" and currentpath or nil +end local done={} function resolvers.resetextrapath() local ep=instance.extra_paths if not ep then - ep,done={},{} - instance.extra_paths=ep + done={} + instance.extra_paths={} elseif #ep>0 then - instance.lists,done={},{} + done={} + reset_caches() end end function resolvers.registerextrapath(paths,subpaths) - paths=settings_to_array(paths) - subpaths=settings_to_array(subpaths) + if not subpaths or subpaths=="" then + if not paths or path=="" then + return + elseif done[paths] then + return + end + end + local paths=settings_to_array(paths) + local subpaths=settings_to_array(subpaths) local ep=instance.extra_paths or {} local oldn=#ep local newn=oldn @@ -14038,7 +15151,7 @@ function resolvers.registerextrapath(paths,subpaths) local ps=p.."/"..s if not done[ps] then newn=newn+1 - ep[newn]=resolvers.cleanpath(ps) + ep[newn]=cleanpath(ps) done[ps]=true end end @@ -14048,7 +15161,7 @@ function resolvers.registerextrapath(paths,subpaths) local p=paths[i] if not done[p] then newn=newn+1 - ep[newn]=resolvers.cleanpath(p) + ep[newn]=cleanpath(p) done[p]=true end end @@ -14060,7 +15173,7 @@ function resolvers.registerextrapath(paths,subpaths) local ps=ep[i].."/"..s if not done[ps] then newn=newn+1 - ep[newn]=resolvers.cleanpath(ps) + ep[newn]=cleanpath(ps) done[ps]=true end end @@ -14069,52 +15182,70 @@ function resolvers.registerextrapath(paths,subpaths) if newn>0 then instance.extra_paths=ep end - if newn>oldn then - instance.lists={} + if newn~=oldn then + reset_caches() end end -local function made_list(instance,list) - local ep=instance.extra_paths - if not ep or #ep==0 then - return list +function resolvers.pushextrapath(path) + local paths=settings_to_array(path) + if instance.extra_stack then + insert(instance.extra_stack,1,paths) else - local done,new,newn={},{},0 - for k=1,#list do - local v=list[k] - if not done[v] then - if find(v,"^[%.%/]$") then - done[v]=true - newn=newn+1 - new[newn]=v - else - break - end - end - end - for k=1,#ep do - local v=ep[k] + instance.extra_stack={ paths } + end + reset_caches() +end +function resolvers.popextrapath() + if instance.extra_stack then + reset_caches() + return remove(instance.extra_stack,1) + end +end +local function made_list(instance,list,extra_too) + local done={} + local new={} + local newn=0 + local function add(p) + for k=1,#p do + local v=p[k] if not done[v] then done[v]=true newn=newn+1 new[newn]=v end end - for k=1,#list do - local v=list[k] - if not done[v] then - done[v]=true - newn=newn+1 - new[newn]=v + end + for k=1,#list do + local v=list[k] + if done[v] then + elseif find(v,"^[%.%/]$") then + done[v]=true + newn=newn+1 + new[newn]=v + else + break + end + end + if extra_too then + local es=instance.extra_stack + if es and #es>0 then + for k=1,#es do + add(es[k]) end end - return new + local ep=instance.extra_paths + if ep and #ep>0 then + add(ep) + end end + add(list) + return new end function resolvers.cleanpathlist(str) local t=resolvers.expandedpathlist(str) if t then for i=1,#t do - t[i]=collapsepath(resolvers.cleanpath(t[i])) + t[i]=collapsepath(cleanpath(t[i])) end end return t @@ -14122,22 +15253,22 @@ end function resolvers.expandpath(str) return joinpath(resolvers.expandedpathlist(str)) end -function resolvers.expandedpathlist(str) +function resolvers.expandedpathlist(str,extra_too) if not str then return {} - elseif instance.savelists then + elseif instance.savelists then str=lpegmatch(dollarstripper,str) local lists=instance.lists local lst=lists[str] if not lst then - local l=made_list(instance,resolvers.splitpath(resolvers.expansion(str))) + local l=made_list(instance,resolvers.splitpath(resolvers.expansion(str)),extra_too) lst=expandedpathfromlist(l) lists[str]=lst end return lst else local lst=resolvers.splitpath(resolvers.expansion(str)) - return made_list(instance,expandedpathfromlist(lst)) + return made_list(instance,expandedpathfromlist(lst),extra_too) end end function resolvers.expandedpathlistfromvariable(str) @@ -14148,6 +15279,13 @@ end function resolvers.expandpathfromvariable(str) return joinpath(resolvers.expandedpathlistfromvariable(str)) end +function resolvers.cleanedpathlist(v) + local t=resolvers.expandedpathlist(v) + for i=1,#t do + t[i]=resolvers.resolve(resolvers.cleanpath(t[i])) + end + return t +end function resolvers.expandbraces(str) local ori=str local pth=expandedpathfromlist(resolvers.splitpath(ori)) @@ -14164,7 +15302,7 @@ function resolvers.registerfilehash(name,content,someerror) end end local function isreadable(name) - local readable=lfs.isfile(name) + local readable=isfile(name) if trace_detail then if readable then report_resolving("file %a is readable",name) @@ -14174,70 +15312,57 @@ local function isreadable(name) end return readable end -local function collect_files(names) - local filelist,noffiles={},0 +local function collect_files(names) + local filelist={} + local noffiles=0 + local function check(hash,root,pathname,path,name) + if not pathname or find(path,pathname) then + local variant=hash.type + local search=filejoin(root,path,name) + local result=methodhandler('concatinators',variant,root,path,name) + if trace_detail then + report_resolving("match: variant %a, search %a, result %a",variant,search,result) + end + noffiles=noffiles+1 + filelist[noffiles]={ variant,search,result } + end + end for k=1,#names do - local fname=names[k] + local filename=names[k] if trace_detail then - report_resolving("checking name %a",fname) + report_resolving("checking name %a",filename) end - local bname=filebasename(fname) - local dname=filedirname(fname) - if dname=="" or find(dname,"^%.") then - dname=false + local basename=filebasename(filename) + local pathname=filedirname(filename) + if pathname=="" or find(pathname,"^%.") then + pathname=false else - dname=gsub(dname,"%*",".*") - dname="/"..dname.."$" + pathname=gsub(pathname,"%*",".*") + pathname="/"..pathname.."$" end local hashes=instance.hashes for h=1,#hashes do local hash=hashes[h] - local blobpath=hash.name - local files=blobpath and instance.files[blobpath] - if files then + local hashname=hash.name + local content=hashname and instance.files[hashname] + if content then if trace_detail then - report_resolving("deep checking %a, base %a, pattern %a",blobpath,bname,dname) + report_resolving("deep checking %a, base %a, pattern %a",hashname,basename,pathname) end - local blobfile=files[bname] - if not blobfile then - local rname="remap:"..bname - blobfile=files[rname] - if blobfile then - bname=files[rname] - blobfile=files[bname] - end - end - if blobfile then - local blobroot=files.__path__ or blobpath - if type(blobfile)=='string' then - if not dname or find(blobfile,dname) then - local variant=hash.type - local search=filejoin(blobroot,blobfile,bname) - local result=methodhandler('concatinators',hash.type,blobroot,blobfile,bname) - if trace_detail then - report_resolving("match: variant %a, search %a, result %a",variant,search,result) - end - noffiles=noffiles+1 - filelist[noffiles]={ variant,search,result } - end + local path,name=lookup(content,basename) + if path then + local metadata=content.metadata + local realroot=metadata and metadata.path or hashname + if type(path)=="string" then + check(hash,realroot,pathname,path,name) else - for kk=1,#blobfile do - local vv=blobfile[kk] - if not dname or find(vv,dname) then - local variant=hash.type - local search=filejoin(blobroot,vv,bname) - local result=methodhandler('concatinators',hash.type,blobroot,vv,bname) - if trace_detail then - report_resolving("match: variant %a, search %a, result %a",variant,search,result) - end - noffiles=noffiles+1 - filelist[noffiles]={ variant,search,result } - end + for i=1,#path do + check(hash,realroot,pathname,path[i],name) end end end elseif trace_locating then - report_resolving("no match in %a (%s)",blobpath,bname) + report_resolving("no match in %a (%s)",hashname,basename) end end end @@ -14262,7 +15387,7 @@ end local function can_be_dir(name) local fakepaths=instance.fakepaths if not fakepaths[name] then - if lfs.isdir(name) then + if isdir(name) then fakepaths[name]=1 else fakepaths[name]=2 @@ -14278,10 +15403,11 @@ local function find_analyze(filename,askedformat,allresults) if askedformat=="" then if ext=="" or not suffixmap[ext] then local defaultsuffixes=resolvers.defaultsuffixes + local formatofsuffix=resolvers.formatofsuffix for i=1,#defaultsuffixes do local forcedname=filename..'.'..defaultsuffixes[i] wantedfiles[#wantedfiles+1]=forcedname - filetype=resolvers.formatofsuffix(forcedname) + filetype=formatofsuffix(forcedname) if trace_locating then report_resolving("forcing filetype %a",filetype) end @@ -14317,18 +15443,18 @@ local function find_direct(filename,allresults) end end local function find_wildcard(filename,allresults) - if find(filename,'%*') then + if find(filename,'*',1,true) then if trace_locating then report_resolving("checking wildcard %a",filename) end - local method,result=resolvers.findwildcardfiles(filename) + local result=resolvers.findwildcardfiles(filename) if result then return "wildcard",result end end end local function find_qualified(filename,allresults,askedformat,alsostripped) - if not file.is_qualified_path(filename) then + if not is_qualified_path(filename) then return end if trace_locating then @@ -14402,33 +15528,66 @@ local function check_subpath(fname) return fname end end -local function find_intree(filename,filetype,wantedfiles,allresults) +local function makepathlist(list,filetype) local typespec=resolvers.variableofformat(filetype) - local pathlist=resolvers.expandedpathlist(typespec) - local method="intree" + local pathlist=resolvers.expandedpathlist(typespec,filetype and usertypes[filetype]) + local entry={} if pathlist and #pathlist>0 then - local filelist=collect_files(wantedfiles) + for k=1,#pathlist do + local path=pathlist[k] + local prescanned=find(path,'^!!') + local resursive=find(path,'//$') + local pathname=lpegmatch(inhibitstripper,path) + local expression=makepathexpression(pathname) + local barename=gsub(pathname,"/+$","") + barename=resolveprefix(barename) + local scheme=url.hasscheme(barename) + local schemename=gsub(barename,"%.%*$",'') + entry[k]={ + path=path, + pathname=pathname, + prescanned=prescanned, + recursive=recursive, + expression=expression, + barename=barename, + scheme=scheme, + schemename=schemename, + } + end + entry.typespec=typespec + list[filetype]=entry + else + list[filetype]=false + end + return entry +end +local function find_intree(filename,filetype,wantedfiles,allresults) + local pathlists=instance.pathlists + if not pathlists then + pathlists=setmetatableindex(allocate(),makepathlist) + instance.pathlists=pathlists + end + local pathlist=pathlists[filetype] + if pathlist then + local method="intree" + local filelist=collect_files(wantedfiles) local dirlist={} + local result={} if filelist then for i=1,#filelist do dirlist[i]=filedirname(filelist[i][3]).."/" end end if trace_detail then - report_resolving("checking filename %a",filename) + report_resolving("checking filename %a in tree",filename) end - local resolve=resolvers.resolve - local result={} for k=1,#pathlist do - local path=pathlist[k] - local pathname=lpegmatch(inhibitstripper,path) - local doscan=path==pathname - if not find (pathname,'//$') then - doscan=false - end + local entry=pathlist[k] + local path=entry.path + local pathname=entry.pathname local done=false if filelist then - local expression=makepathexpression(pathname) + local expression=entry.expression if trace_detail then report_resolving("using pattern %a for path %a",expression,pathname) end @@ -14436,8 +15595,8 @@ local function find_intree(filename,filetype,wantedfiles,allresults) local fl=filelist[k] local f=fl[2] local d=dirlist[k] - if find(d,expression) or find(resolve(d),expression) then - result[#result+1]=resolve(fl[3]) + if find(d,expression) or find(resolveprefix(d),expression) then + result[#result+1]=resolveprefix(fl[3]) done=true if allresults then if trace_detail then @@ -14458,56 +15617,62 @@ local function find_intree(filename,filetype,wantedfiles,allresults) method="database" else method="filesystem" - pathname=gsub(pathname,"/+$","") - pathname=resolve(pathname) - local scheme=url.hasscheme(pathname) + local scheme=entry.scheme if not scheme or scheme=="file" then - local pname=gsub(pathname,"%.%*$",'') - if not find(pname,"%*") then + local pname=entry.schemename + if not find(pname,"*",1,true) then if can_be_dir(pname) then - for k=1,#wantedfiles do - local w=wantedfiles[k] - local fname=check_subpath(filejoin(pname,w)) - if fname then - result[#result+1]=fname - done=true - if not allresults then - break - end + if not done and not entry.prescanned then + if trace_detail then + report_resolving("quick root scan for %a",pname) end - end - if not done and doscan then - local files=resolvers.simplescanfiles(pname,false,true) for k=1,#wantedfiles do local w=wantedfiles[k] - local subpath=files[w] - if not subpath or subpath=="" then - elseif type(subpath)=="string" then - local fname=check_subpath(filejoin(pname,subpath,w)) - if fname then - result[#result+1]=fname - done=true - if not allresults then - break - end + local fname=check_subpath(filejoin(pname,w)) + if fname then + result[#result+1]=fname + done=true + if not allresults then + break end - else - for i=1,#subpath do - local sp=subpath[i] - if sp=="" then - else - local fname=check_subpath(filejoin(pname,sp,w)) - if fname then - result[#result+1]=fname - done=true - if not allresults then - break + end + end + if not done and entry.recursive then + if trace_detail then + report_resolving("scanning filesystem for %a",pname) + end + local files=resolvers.simplescanfiles(pname,false,true) + for k=1,#wantedfiles do + local w=wantedfiles[k] + local subpath=files[w] + if not subpath or subpath=="" then + elseif type(subpath)=="string" then + local fname=check_subpath(filejoin(pname,subpath,w)) + if fname then + result[#result+1]=fname + done=true + if not allresults then + break + end + end + else + for i=1,#subpath do + local sp=subpath[i] + if sp=="" then + else + local fname=check_subpath(filejoin(pname,sp,w)) + if fname then + result[#result+1]=fname + done=true + if not allresults then + break + end end end end - end - if done and not allresults then - break + if done and not allresults then + break + end end end end @@ -14515,6 +15680,18 @@ local function find_intree(filename,filetype,wantedfiles,allresults) end else end + else + for k=1,#wantedfiles do + local pname=entry.barename + local fname=methodhandler('finders',pname.."/"..wantedfiles[k]) + if fname then + result[#result+1]=fname + done=true + if not allresults then + break + end + end + end end end if done and not allresults then @@ -14549,10 +15726,13 @@ local function find_otherwise(filename,filetype,wantedfiles,allresults) local filelist=collect_files(wantedfiles) local fl=filelist and filelist[1] if fl then - return "otherwise",{ resolvers.resolve(fl[3]) } + return "otherwise",{ resolveprefix(fl[3]) } end end collect_instance_files=function(filename,askedformat,allresults) + if not filename or filename=="" then + return {} + end askedformat=askedformat or "" filename=collapsepath(filename,".") filename=gsub(filename,"^%./",getcurrentdir().."/") @@ -14587,7 +15767,11 @@ collect_instance_files=function(filename,askedformat,allresults) else local method,result,stamp,filetype,wantedfiles if instance.remember then - stamp=formatters["%s--%s"](filename,askedformat) + if askedformat=="" then + stamp=formatters["%s::%s"](suffixonly(filename),filename) + else + stamp=formatters["%s::%s"](askedformat,filename) + end result=stamp and instance.found[stamp] if result then if trace_locating then @@ -14606,7 +15790,7 @@ collect_instance_files=function(filename,askedformat,allresults) method,result=find_intree(filename,filetype,wantedfiles) if not result then method,result=find_onpath(filename,filetype,wantedfiles) - if not result then + if resolve_otherwise and not result then method,result=find_otherwise(filename,filetype,wantedfiles) end end @@ -14622,7 +15806,7 @@ collect_instance_files=function(filename,askedformat,allresults) end if stamp then if trace_locating then - report_resolving("remembering file %a",filename) + report_resolving("remembering file %a using hash %a",filename,stamp) end instance.found[stamp]=result end @@ -14630,6 +15814,9 @@ collect_instance_files=function(filename,askedformat,allresults) end end local function findfiles(filename,filetype,allresults) + if not filename or filename=="" then + return {} + end local result,status=collect_instance_files(filename,filetype or "",allresults) if not result or #result==0 then local lowered=lower(filename) @@ -14649,39 +15836,30 @@ 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 base=filebasename(filename) + local result={} local hashes=instance.hashes - local noffound=0 + local function okay(hash,path,name) + local found=methodhandler('concatinators',hash.type,hash.name,path,name) + if found and found~="" then + result[#result+1]=resolveprefix(found) + return not allresults + end + end for k=1,#hashes do local hash=hashes[k] - local files=instance.files[hash.name] or {} - local blist=files[bname] - if not blist then - local rname="remap:"..bname - blist=files[rname] - if blist then - bname=files[rname] - blist=files[bname] - end - end - if blist then - if type(blist)=='string' then - 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 + local content=instance.files[hash.name] + if content then + local path,name=lookup(content,base) + if not path then + elseif type(path)=="string" then + if okay(hash,path,name) then + return result end else - for kk=1,#blist do - local vv=blist[kk] - 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 + for i=1,#path do + if okay(hash,path[i],name) then + return result end end end @@ -14695,64 +15873,80 @@ end function resolvers.findgivenfile(filename) return findgivenfiles(filename,false)[1] or "" end -local function doit(path,blist,bname,tag,variant,result,allresults) - local done=false - if blist and variant then - local resolve=resolvers.resolve - if type(blist)=='string' then - if find(lower(blist),path) then - 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 - local full=methodhandler('concatinators',variant,tag,vv,bname) or "" - result[#result+1]=resolve(full) - done=true - if not allresults then break end - end - end - end - end - return done -end 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) - result=result or {} +local function findwildcardfiles(filename,allresults,result) + local 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 files=instance.files + if find(name,"*",1,true) then local hashes=instance.hashes + local function okay(found,path,base,hashname,hashtype) + if find(found,path) then + local full=methodhandler('concatinators',hashtype,hashname,found,base) + if full and full~="" then + result[#result+1]=resolveprefix(full) + return not allresults + end + end + end for k=1,#hashes do local hash=hashes[k] - 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,hashname,hashtype,result,allresults) then done=true end - if done and not allresults then break end + local hashname=hash.name + local hashtype=hash.type + if hashname and hashtype then + for found,base in filtered(files[hashname],name) do + if type(found)=='string' then + if okay(found,path,base,hashname,hashtype) then + break + end + else + for i=1,#found do + if okay(found[i],path,base,hashname,hashtype) then + break + end + end end end end end else + local function okayokay(found,path,base,hashname,hashtype) + if find(found,path) then + local full=methodhandler('concatinators',hashtype,hashname,found,base) + if full and full~="" then + result[#result+1]=resolveprefix(full) + return not allresults + end + end + end local hashes=instance.hashes for k=1,#hashes do local hash=hashes[k] - local hashname,hashtype=hash.name,hash.type - if doit(path,files[hashname][base],base,hashname,hashtype,result,allresults) then done=true end - if done and not allresults then break end + local hashname=hash.name + local hashtype=hash.type + if hashname and hashtype then + local found,base=lookup(content,base) + if not found then + elseif type(found)=='string' then + if okay(found,path,base,hashname,hashtype) then + break + end + else + for i=1,#found do + if okay(found[i],path,base,hashname,hashtype) then + break + end + end + end + end end end return result @@ -14825,7 +16019,7 @@ end function resolvers.dowithpath(name,func) local pathlist=resolvers.expandedpathlist(name) for i=1,#pathlist do - func("^"..resolvers.cleanpath(pathlist[i])) + func("^"..cleanpath(pathlist[i])) end end function resolvers.dowithvariable(name,func) @@ -14833,23 +16027,23 @@ function resolvers.dowithvariable(name,func) end function resolvers.locateformat(name) local engine=environment.ownmain or "luatex" - local barename=file.removesuffix(name) - local fullname=file.addsuffix(barename,"fmt") + local barename=removesuffix(name) + local fullname=addsuffix(barename,"fmt") local fmtname=caches.getfirstreadablefile(fullname,"formats",engine) or "" if fmtname=="" then fmtname=resolvers.findfile(fullname) - fmtname=resolvers.cleanpath(fmtname) + fmtname=cleanpath(fmtname) end if fmtname~="" then - local barename=file.removesuffix(fmtname) - local luaname=file.addsuffix(barename,luasuffixes.lua) - local lucname=file.addsuffix(barename,luasuffixes.luc) - local luiname=file.addsuffix(barename,luasuffixes.lui) - if lfs.isfile(luiname) then + local barename=removesuffix(fmtname) + local luaname=addsuffix(barename,luasuffixes.lua) + local lucname=addsuffix(barename,luasuffixes.luc) + local luiname=addsuffix(barename,luasuffixes.lui) + if isfile(luiname) then return barename,luiname - elseif lfs.isfile(lucname) then + elseif isfile(lucname) then return barename,lucname - elseif lfs.isfile(luaname) then + elseif isfile(luaname) then return barename,luaname end end @@ -14871,29 +16065,24 @@ function resolvers.dowithfilesintree(pattern,handle,before,after) local hash=hashes[i] local blobtype=hash.type local blobpath=hash.name - if blobpath then + if blobtype and blobpath then + local total=0 + local checked=0 + local done=0 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 table.sortedhash(files) do - total=total+1 - if find(k,"^remap:") then - elseif 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 + for path,name in filtered(instance.files[blobpath],pattern) do + if type(path)=="string" then + checked=checked+1 + if handle(blobtype,blobpath,path,name) then + done=done+1 + end + else + checked=checked+#path + for i=1,#path do + if handle(blobtype,blobpath,path[i],name) then + done=done+1 end end end @@ -14904,8 +16093,8 @@ function resolvers.dowithfilesintree(pattern,handle,before,after) end end end -resolvers.obsolete=resolvers.obsolete or {} -local obsolete=resolvers.obsolete +local obsolete=resolvers.obsolete or {} +resolvers.obsolete=obsolete resolvers.find_file=resolvers.findfile obsolete.find_file=resolvers.findfile resolvers.find_files=resolvers.findfiles obsolete.find_files=resolvers.findfiles @@ -14916,7 +16105,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-pre"] = package.loaded["data-pre"] or true --- original size: 6643, stripped down to: 4401 +-- original size: 3950, stripped down to: 2935 if not modules then modules={} end modules ['data-pre']={ version=1.001, @@ -14926,44 +16115,51 @@ if not modules then modules={} end modules ['data-pre']={ license="see context related readme files" } local resolvers=resolvers -local prefixes=utilities.storage.allocate() -resolvers.prefixes=prefixes -local cleanpath,findgivenfile,expansion=resolvers.cleanpath,resolvers.findgivenfile,resolvers.expansion +local prefixes=resolvers.prefixes +local cleanpath=resolvers.cleanpath +local findgivenfile=resolvers.findgivenfile +local expansion=resolvers.expansion local getenv=resolvers.getenv -local P,S,R,C,Cs,Cc,lpegmatch=lpeg.P,lpeg.S,lpeg.R,lpeg.C,lpeg.Cs,lpeg.Cc,lpeg.match -local joinpath,basename,dirname=file.join,file.basename,file.dirname -local getmetatable,rawset,type=getmetatable,rawset,type +local basename=file.basename +local dirname=file.dirname +local joinpath=file.join +local isfile=lfs.isfile prefixes.environment=function(str) return cleanpath(expansion(str)) end -prefixes.relative=function(str,n) - if io.exists(str) then - elseif io.exists("./"..str) then - str="./"..str - else - local p="../" - for i=1,n or 2 do - if io.exists(p..str) then - str=p..str - break - else - p=p.."../" +local function relative(str,n) + if not isfile(str) then + local pstr="./"..str + if isfile(pstr) then + str=pstr + else + local p="../" + for i=1,n or 2 do + local pstr=p..str + if isfile(pstr) then + str=pstr + break + else + p=p.."../" + end end end end return cleanpath(str) end +local function locate(str) + local fullname=findgivenfile(str) or "" + return cleanpath(fullname~="" and fullname or str) +end +prefixes.relative=relative +prefixes.locate=locate prefixes.auto=function(str) - local fullname=prefixes.relative(str) - if not lfs.isfile(fullname) then - fullname=prefixes.locate(str) + local fullname=relative(str) + if not isfile(fullname) then + fullname=locate(str) end return fullname end -prefixes.locate=function(str) - local fullname=findgivenfile(str) or "" - return cleanpath((fullname~="" and fullname) or str) -end prefixes.filename=function(str) local fullname=findgivenfile(str) or "" return cleanpath(basename((fullname~="" and fullname) or str)) @@ -14984,6 +16180,13 @@ end prefixes.home=function(str) return cleanpath(joinpath(getenv('HOME'),str)) end +prefixes.env=prefixes.environment +prefixes.rel=prefixes.relative +prefixes.loc=prefixes.locate +prefixes.kpse=prefixes.locate +prefixes.full=prefixes.locate +prefixes.file=prefixes.filename +prefixes.path=prefixes.pathname local function toppath() local inputstack=resolvers.inputstack if not inputstack then @@ -14996,98 +16199,22 @@ local function toppath() return pathname end end -resolvers.toppath=toppath -prefixes.toppath=function(str) - return cleanpath(joinpath(toppath(),str)) -end -prefixes.env=prefixes.environment -prefixes.rel=prefixes.relative -prefixes.loc=prefixes.locate -prefixes.kpse=prefixes.locate -prefixes.full=prefixes.locate -prefixes.file=prefixes.filename -prefixes.path=prefixes.pathname -function resolvers.allprefixes(separator) - local all=table.sortedkeys(prefixes) - if separator then - for i=1,#all do - all[i]=all[i]..":" - end - end - return all -end -local function _resolve_(method,target) - local action=prefixes[method] - if action then - return action(target) - else - return method..":"..target - end -end -local resolved,abstract={},{} -function resolvers.resetresolve(str) - resolved,abstract={},{} -end -local pattern=Cs((C(R("az")^2)*P(":")*C((1-S(" \"\';,"))^1)/_resolve_+P(1))^0) -local prefix=C(R("az")^2)*P(":") -local target=C((1-S(" \"\';,"))^1) -local notarget=(#S(";,")+P(-1))*Cc("") -local pattern=Cs(((prefix*(target+notarget))/_resolve_+P(1))^0) -local function resolve(str) - if type(str)=="table" then - local t={} - for i=1,#str do - t[i]=resolve(str[i]) - end - return t +local function jobpath() + local path=resolvers.stackpath() + if not path or path=="" then + return "." else - local res=resolved[str] - if not res then - res=lpegmatch(pattern,str) - resolved[str]=res - abstract[res]=str - end - return res - end -end -local function unresolve(str) - return abstract[str] or str -end -resolvers.resolve=resolve -resolvers.unresolve=unresolve -if type(os.uname)=="function" then - for k,v in next,os.uname() do - if not prefixes[k] then - prefixes[k]=function() return v end - end - end -end -if os.type=="unix" then - local pattern - local function makepattern(t,k,v) - if t then - rawset(t,k,v) - end - local colon=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) - end - makepattern() - getmetatable(prefixes).__newindex=makepattern - function resolvers.repath(str) - return lpegmatch(pattern,str) - end -else - function resolvers.repath(str) - return str + return path end end +resolvers.toppath=toppath +resolvers.jobpath=jobpath +prefixes.toppath=function(str) return cleanpath(joinpath(toppath(),str)) end +prefixes.jobpath=function(str) return cleanpath(joinpath(jobpath(),str)) end +resolvers.setdynamic("toppath") +resolvers.setdynamic("jobpath") +prefixes.jobfile=prefixes.jobpath +resolvers.setdynamic("jobfile") end -- of closure @@ -15149,7 +16276,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-fil"] = package.loaded["data-fil"] or true --- original size: 3801, stripped down to: 3231 +-- original size: 3863, stripped down to: 3310 if not modules then modules={} end modules ['data-fil']={ version=1.001, @@ -15161,30 +16288,31 @@ if not modules then modules={} end modules ['data-fil']={ 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 resolveprefix=resolvers.resolve 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) + local filename=specification.filename + local realname=resolveprefix(filename) if realname and realname~='' and lfs.isdir(realname) then if trace_locating then - report_files("file locator %a found as %a",name,realname) + report_files("file locator %a found as %a",filename,realname) end - resolvers.appendhash('file',name,true) + resolvers.appendhash('file',filename,true) elseif trace_locating then - report_files("file locator %a not found",name) + report_files("file locator %a not found",filename) end end function hashers.file(specification) - local name=specification.filename - local content=caches.loadcontent(name,'files') - resolvers.registerfilehash(name,content,content==nil) + local pathname=specification.filename + local content=caches.loadcontent(pathname,'files') + resolvers.registerfilehash(pathname,content,content==nil) end function generators.file(specification) - local path=specification.filename - local content=resolvers.scanfiles(path,false,true) - resolvers.registerfilehash(path,content,true) + local pathname=specification.filename + local content=resolvers.scanfiles(pathname,false,true) + resolvers.registerfilehash(pathname,content,true) end concatinators.file=file.join function finders.file(specification,filetype) @@ -15375,7 +16503,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-use"] = package.loaded["data-use"] or true --- original size: 3913, stripped down to: 2998 +-- original size: 3899, stripped down to: 2984 if not modules then modules={} end modules ['data-use']={ version=1.001, @@ -15421,7 +16549,7 @@ end statistics.register("used config file",function() return caches.configfiles() end) statistics.register("used cache path",function() return caches.usedpaths() end) function statistics.savefmtstatus(texname,formatbanner,sourcefile) - local enginebanner=status.list().banner + local enginebanner=status.banner if formatbanner and enginebanner and sourcefile then local luvname=file.replacesuffix(texname,"luv") local luvdata={ @@ -15434,7 +16562,7 @@ function statistics.savefmtstatus(texname,formatbanner,sourcefile) end end function statistics.checkfmtstatus(texname) - local enginebanner=status.list().banner + local enginebanner=status.banner if enginebanner and texname then local luvname=file.replacesuffix(texname,"luv") if lfs.isfile(luvname) then @@ -15466,7 +16594,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-zip"] = package.loaded["data-zip"] or true --- original size: 8489, stripped down to: 6757 +-- original size: 8772, stripped down to: 6841 if not modules then modules={} end modules ['data-zip']={ version=1.001, @@ -15485,16 +16613,6 @@ zip.archives=zip.archives or {} local archives=zip.archives zip.registeredfiles=zip.registeredfiles or {} local registeredfiles=zip.registeredfiles -local limited=false -directives.register("system.inputmode",function(v) - if not limited then - local i_limiter=io.i_limiter(v) - if i_limiter then - zip.open=i_limiter.protect(zip.open) - limited=true - end - end -end) local function validzip(str) if not find(str,"^zip://") then return "zip:///"..str @@ -15509,7 +16627,7 @@ function zip.openarchive(name) local arch=archives[name] if not arch then local full=resolvers.findfile(name) or "" - arch=(full~="" and zip.open(full)) or false + arch=full~="" and zip.open(full) or false archives[name]=arch end return arch @@ -15668,31 +16786,42 @@ function resolvers.usezipfile(archive) end end function resolvers.registerzipfile(z,tree) - local files,filter={},"" - if tree=="" then - filter="^(.+)/(.-)$" - else - filter=format("^%s/(.+)/(.-)$",tree) - end + local names={} + local files={} + local remap={} + local n=0 + local filter=tree=="" and "^(.+)/(.-)$" or format("^%s/(.+)/(.-)$",tree) + local register=resolvers.registerfile if trace_locating then report_zip("registering: using filter %a",filter) end - local register,n=resolvers.registerfile,0 for i in z:files() do - local path,name=match(i.filename,filter) - if path then - if name and name~='' then - register(files,name,path) - n=n+1 - else + local filename=i.filename + local path,name=match(filename,filter) + if not path then + n=n+1 + register(names,filename,"") + local usedname=lower(filename) + files[usedname]="" + if usedname~=filename then + remap[usedname]=filename end - else - register(files,i.filename,'') + elseif name and name~="" then n=n+1 + register(names,name,path) + local usedname=lower(name) + files[usedname]=path + if usedname~=name then + remap[usedname]=name + end + else end end report_zip("registering: %s files registered",n) - return files + return { + files=files, + remap=remap, + } end @@ -15702,7 +16831,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-tre"] = package.loaded["data-tre"] or true --- original size: 2508, stripped down to: 2074 +-- original size: 8479, stripped down to: 5580 if not modules then modules={} end modules ['data-tre']={ version=1.001, @@ -15711,42 +16840,64 @@ if not modules then modules={} end modules ['data-tre']={ copyright="PRAGMA ADE / ConTeXt Development Team", license="see context related readme files" } -local find,gsub,format=string.find,string.gsub,string.format +local find,gsub,lower=string.find,string.gsub,string.lower +local basename,dirname,joinname=file.basename,file.dirname,file .join +local globdir,isdir,isfile=dir.glob,lfs.isdir,lfs.isfile +local P,lpegmatch=lpeg.P,lpeg.match 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 resolveprefix=resolvers.resolve +local notfound=resolvers.finders.notfound +local lookup=resolvers.get_from_content +local collectors={} +local found={} +function resolvers.finders.tree(specification) local spec=specification.filename - local fnd=found[spec] - if fnd==nil then + local okay=found[spec] + if okay==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.."/*" - hash=dir.glob(pattern) - done[path]=hash + local path=dirname(spec) + local name=basename(spec) + if path=="" then + path="." + end + local names=collectors[path] + if not names then + local pattern=find(path,"/%*+$") and path or (path.."/*") + names=globdir(pattern) + collectors[path]=names 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 + for i=1,#names do + local fullname=names[i] + if find(fullname,pattern) then + found[spec]=fullname + return fullname + end + end + local pattern=lower(pattern) + for i=1,#names do + local fullname=lower(names[i]) + if find(fullname,pattern) then + if isfile(fullname) then + found[spec]=fullname + return fullname + else + break + end end end end - fnd=notfound() - found[spec]=fnd + okay=notfound() + found[spec]=okay end - return fnd + return okay end function resolvers.locators.tree(specification) local name=specification.filename - local realname=resolvers.resolve(name) - if realname and realname~='' and lfs.isdir(realname) then + local realname=resolveprefix(name) + if realname and realname~='' and isdir(realname) then if trace_locating then report_trees("locator %a found",realname) end @@ -15757,16 +16908,110 @@ function resolvers.locators.tree(specification) end function resolvers.hashers.tree(specification) local name=specification.filename - if trace_locating then - report_trees("analysing %a",name) - end + report_trees("analyzing %a",name) resolvers.methodhandler("hashers",name) resolvers.generators.file(specification) 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 +local collectors={} +local splitter=lpeg.splitat("/**/") +local stripper=lpeg.replacer { [P("/")*P("*")^1*P(-1)]="" } +table.setmetatableindex(collectors,function(t,k) + local rootname=lpegmatch(stripper,k) + local dataname=joinname(rootname,"dirlist") + local content=caches.loadcontent(dataname,"files",dataname) + if not content then + content=resolvers.scanfiles(rootname,nil,nil,false,true) + caches.savecontent(dataname,"files",content,dataname) + end + t[k]=content + return content +end) +local function checked(root,p,n) + if p then + if type(p)=="table" then + for i=1,#p do + local fullname=joinname(root,p[i],n) + if isfile(fullname) then + return fullname + end + end + else + local fullname=joinname(root,p,n) + if isfile(fullname) then + return fullname + end + end + end + return notfound() +end +local function resolve(specification) + local filename=specification.filename + if filename~="" then + local root,rest=lpegmatch(splitter,filename) + if root and rest then + local path,name=dirname(rest),basename(rest) + if name~=rest then + local content=collectors[root] + local p,n=lookup(content,name) + if not p then + return notfound() + end + local pattern=".*/"..path.."$" + local istable=type(p)=="table" + if istable then + for i=1,#p do + local pi=p[i] + if pi==path or find(pi,pattern) then + local fullname=joinname(root,pi,n) + if isfile(fullname) then + return fullname + end + end + end + elseif p==path or find(p,pattern) then + local fullname=joinname(root,p,n) + if isfile(fullname) then + return fullname + end + end + local queries=specification.queries + if queries and queries.option=="fileonly" then + return checked(root,p,n) + else + return notfound() + end + end + end + local path,name=dirname(filename),basename(filename) + local root=lpegmatch(stripper,path) + local content=collectors[path] + local p,n=lookup(content,name) + if p then + return checked(root,p,n) + end + end + return notfound() +end +resolvers.finders .dirlist=resolve +resolvers.locators .dirlist=resolvers.locators .tree +resolvers.hashers .dirlist=resolvers.hashers .tree +resolvers.generators.dirlist=resolvers.generators.file +resolvers.openers .dirlist=resolvers.openers .file +resolvers.loaders .dirlist=resolvers.loaders .file +function resolvers.finders.dirfile(specification) + local queries=specification.queries + if queries then + queries.option="fileonly" + else + specification.queries={ option="fileonly" } + end + return resolve(specification) +end +resolvers.locators .dirfile=resolvers.locators .dirlist +resolvers.hashers .dirfile=resolvers.hashers .dirlist +resolvers.generators.dirfile=resolvers.generators.dirlist +resolvers.openers .dirfile=resolvers.openers .dirlist +resolvers.loaders .dirfile=resolvers.loaders .dirlist end -- of closure @@ -15775,7 +17020,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-sch"] = package.loaded["data-sch"] or true --- original size: 6202, stripped down to: 5149 +-- original size: 6569, stripped down to: 5304 if not modules then modules={} end modules ['data-sch']={ version=1.001, @@ -15801,8 +17046,13 @@ directives.register("schemes.threshold",function(v) threshold=tonumber(v) or thr function cleaners.none(specification) return specification.original end -function cleaners.strip(specification) - return (gsub(specification.original,"[^%a%d%.]+","-")) +function cleaners.strip(specification) + local path,name=file.splitbase(specification.original) + if path=="" then + return (gsub(name,"[^%a%d%.]+","-")) + else + return (gsub((gsub(path,"%.","-").."-"..name),"[^%a%d%.]+","-")) + end end function cleaners.md5(specification) return file.addsuffix(md5.hex(specification.original),file.suffix(specification.path)) @@ -15818,8 +17068,8 @@ function resolvers.schemes.cleanname(specification) end local cached,loaded,reused,thresholds,handlers={},{},{},{},{} local function runcurl(name,cachename) - local command="curl --silent --create-dirs --output "..cachename.." "..name - os.spawn(command) + local command="curl --silent --insecure --create-dirs --output "..cachename.." "..name + os.execute(command) end local function fetch(specification) local original=specification.original @@ -15951,7 +17201,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-lua"] = package.loaded["data-lua"] or true --- original size: 4237, stripped down to: 3177 +-- original size: 4313, stripped down to: 3227 if not modules then modules={} end modules ['data-lua']={ version=1.001, @@ -15960,7 +17210,7 @@ if not modules then modules={} end modules ['data-lua']={ copyright="PRAGMA ADE / ConTeXt Development Team", license="see context related readme files" } -local resolvers,package=resolvers,package +local package,lpeg=package,lpeg local gsub=string.gsub local concat=table.concat local addsuffix=file.addsuffix @@ -15971,9 +17221,11 @@ local luaformats={ 'TEXINPUTS','LUAINPUTS' } local libformats={ 'CLUAINPUTS' } local helpers=package.helpers or {} local methods=helpers.methods or {} +local resolvers=resolvers +local resolveprefix=resolvers.resolve +helpers.report=logs.reporter("resolvers","libraries") trackers.register("resolvers.libraries",function(v) helpers.trace=v end) trackers.register("resolvers.locating",function(v) helpers.trace=v end) -helpers.report=logs.reporter("resolvers","libraries") helpers.sequence={ "already loaded", "preload table", @@ -15988,7 +17240,7 @@ helpers.sequence={ } local pattern=Cs(P("!")^0/""*(P("/")*P(-1)/"/"+P("/")^1/"/"+1)^0) function helpers.cleanpath(path) - return resolvers.resolve(lpegmatch(pattern,path)) + return resolveprefix(lpegmatch(pattern,path)) end local loadedaslib=helpers.loadedaslib local getextraluapaths=package.extraluapaths @@ -16058,7 +17310,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-aux"] = package.loaded["data-aux"] or true --- original size: 2394, stripped down to: 2005 +-- original size: 2431, stripped down to: 1996 if not modules then modules={} end modules ['data-aux']={ version=1.001, @@ -16072,8 +17324,8 @@ local type,next=type,next local trace_locating=false trackers.register("resolvers.locating",function(v) trace_locating=v end) local resolvers=resolvers local report_scripts=logs.reporter("resolvers","scripts") -function resolvers.updatescript(oldname,newname) - local scriptpath="scripts/context/lua" +function resolvers.updatescript(oldname,newname) + local scriptpath="context/lua" newname=file.addsuffix(newname,"lua") local oldscript=resolvers.cleanpath(oldname) if trace_locating then @@ -16125,7 +17377,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-tmf"] = package.loaded["data-tmf"] or true --- original size: 2600, stripped down to: 1627 +-- original size: 2601, stripped down to: 1627 if not modules then modules={} end modules ['data-tmf']={ version=1.001, @@ -16181,7 +17433,7 @@ do -- create closure to overcome 200 locals limit package.loaded["data-lst"] = package.loaded["data-lst"] or true --- original size: 2654, stripped down to: 2301 +-- original size: 2734, stripped down to: 2354 if not modules then modules={} end modules ['data-lst']={ version=1.001, @@ -16190,10 +17442,13 @@ if not modules then modules={} end modules ['data-lst']={ copyright="PRAGMA ADE / ConTeXt Development Team", license="see context related readme files" } -local find,concat,upper,format=string.find,table.concat,string.upper,string.format +local rawget,type,next=rawget,type,next +local find,concat,upper=string.find,table.concat,string.upper local fastcopy,sortedpairs=table.fastcopy,table.sortedpairs -resolvers.listers=resolvers.listers or {} local resolvers=resolvers +local listers=resolvers.listers or {} +resolvers.listers=listers +local resolveprefix=resolvers.resolve local report_lists=logs.reporter("resolvers","lists") local function tabstr(str) if type(str)=='table' then @@ -16202,7 +17457,7 @@ local function tabstr(str) return str end end -function resolvers.listers.variables(pattern) +function listers.variables(pattern) local instance=resolvers.instance local environment=instance.environment local variables=instance.variables @@ -16223,10 +17478,10 @@ function resolvers.listers.variables(pattern) for key,value in 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",tabstr(resolvers.resolve(expansions[key])) or "unset") + 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",tabstr(resolveprefix(expansions[key])) or "unset") end end instance.environment=fastcopy(env) @@ -16234,15 +17489,15 @@ function resolvers.listers.variables(pattern) instance.expansions=fastcopy(exp) end local report_resolved=logs.reporter("system","resolved") -function resolvers.listers.configurations() +function listers.configurations() local configurations=resolvers.instance.specification for i=1,#configurations do - report_resolved("file : %s",resolvers.resolve(configurations[i])) + report_resolved("file : %s",resolveprefix(configurations[i])) end report_resolved("") local list=resolvers.expandedpathfromlist(resolvers.splitpath(resolvers.luacnfspec)) for i=1,#list do - local li=resolvers.resolve(list[i]) + local li=resolveprefix(list[i]) if lfs.isdir(li) then report_resolved("path - %s",li) else @@ -16547,7 +17802,7 @@ do -- create closure to overcome 200 locals limit package.loaded["luat-fmt"] = package.loaded["luat-fmt"] or true --- original size: 5951, stripped down to: 4922 +-- original size: 5955, stripped down to: 4926 if not modules then modules={} end modules ['luat-fmt']={ version=1.001, @@ -16635,7 +17890,7 @@ function environment.make_format(name) end local command=format("%s --ini %s --lua=%s %s %sdump",engine,primaryflags(),quoted(usedluastub),quoted(fulltexsourcename),os.platform=="unix" and "\\\\" or "\\") report_format("running command: %s\n",command) - os.spawn(command) + os.execute(command) local pattern=file.removesuffix(file.basename(usedluastub)).."-*.mem" local mp=dir.glob(pattern) if mp then @@ -16670,7 +17925,7 @@ function environment.run_format(name,data,more) else local command=format("%s %s --fmt=%s --lua=%s %s %s",engine,primaryflags(),quoted(barename),quoted(luaname),quoted(data),more~="" and quoted(more) or "") report_format("running command: %s",command) - os.spawn(command) + os.execute(command) end end end @@ -16681,8 +17936,8 @@ end -- of closure -- used libraries : l-lua.lua l-package.lua l-lpeg.lua l-function.lua l-string.lua l-table.lua l-io.lua l-number.lua l-set.lua l-os.lua l-file.lua l-gzip.lua l-md5.lua l-url.lua l-dir.lua l-boolean.lua l-unicode.lua l-math.lua util-str.lua util-tab.lua util-sto.lua util-prs.lua util-fmt.lua trac-set.lua trac-log.lua trac-inf.lua trac-pro.lua util-lua.lua util-deb.lua util-mrg.lua util-tpl.lua util-env.lua luat-env.lua lxml-tab.lua lxml-lpt.lua lxml-mis.lua lxml-aux.lua lxml-xml.lua trac-xml.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-zip.lua data-tre.lua data-sch.lua data-lua.lua data-aux.lua data-tmf.lua data-lst.lua util-lib.lua luat-sta.lua luat-fmt.lua -- skipped libraries : - --- original bytes : 685064 --- stripped bytes : 242353 +-- original bytes : 745618 +-- stripped bytes : 269191 -- end library merge @@ -16781,17 +18036,18 @@ local ownlibs = { -- order can be made better } +-- c:/data/develop/tex-context/tex/texmf-win64/bin/../../texmf-context/tex/context/base/data-tmf.lua +-- c:/data/develop/context/sources/data-tmf.lua + local ownlist = { - '.', - ownpath , - ownpath .. "/../sources", -- HH's development path + -- '.', + -- ownpath , + owntree .. "/../../../../context/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", } @@ -16907,6 +18163,7 @@ local helpinfo = [[ <category name="basic"> <subcategory> <flag name="script"><short>run an mtx script (lua prefered method) (<ref name="noquotes"/>), no script gives list</short></flag> + <flag name="evaluate"><short>run code passed on the commandline (between quotes)</short></flag> <flag name="execute"><short>run a script or program (texmfstart method) (<ref name="noquotes"/>)</short></flag> <flag name="resolve"><short>resolve prefixed arguments</short></flag> <flag name="ctxlua"><short>run internally (using preloaded libs)</short></flag> @@ -16932,6 +18189,7 @@ local helpinfo = [[ <flag name="verbose"><short>give a bit more info</short></flag> <flag name="trackers" value="list"><short>enable given trackers</short></flag> <flag name="progname" value="str"><short>format or backend</short></flag> + <flag name="systeminfo" value="str"><short>show current operating system, processor, etc</short></flag> </subcategory> <subcategory> <flag name="edit"><short>launch editor with found file</short></flag> @@ -17561,6 +18819,39 @@ function runners.associate(filename) os.launch(filename) end +function runners.evaluate(code,filename) -- for Luigi + if code == "loop" then + while true do + io.write("> ") + local code = io.read() + if code ~= "" then + local temp = string.match(code,"^= (.*)$") + if temp then + code = "print("..temp..")" + end + local compiled, message = loadstring(code) + if type(compiled) ~= "function" then + io.write("! " .. (message or code).."\n") + else + io.write(compiled()) + end + end + end + else + if type(code) ~= "string" or code == "" then + code = filename + end + if code ~= "" then + local compiled, message = loadstring(code) + if type(compiled) ~= "function" then + io.write("invalid lua code: " .. (message or code)) + return + end + io.write(compiled()) + end + end +end + function runners.gethelp(filename) local url = environment.argument("url") if url and url ~= "" then @@ -17572,6 +18863,15 @@ function runners.gethelp(filename) end end +function runners.systeminfo() + report("architecture : %s",os.platform or "<unset>") + report("operating system : %s",os.name or "<unset>") + report("file architecture : %s",os.type or "<unset>") + report("binary path : %s",os.selfdir or "<unset>") + report("binary suffix : %s",os.binsuffix or "<unset>") + report("library suffix : %s",os.libsuffix or "<unset>") +end + -- this is a bit dirty ... first we store the first filename and next we -- split the arguments so that we only see the ones meant for this script -- ... later we will use the second half @@ -17687,16 +18987,13 @@ end if e_argument("ansi") then - local formatters = string.formatters + logs.setformatters("ansi") - logs.setformatters { - report_yes = formatters["[1;32m%-15s [0;1m|[0m %s"], - report_nop = formatters["[1;32m%-15s [0;1m|[0m"], - subreport_yes = formatters["[1;32m%-15s [0;1m|[1;31m %s [0;1m|[0m %s"], - subreport_nop = formatters["[1;32m%-15s [0;1m|[1;31m %s [0;1m|[0m"], - status_yes = formatters["[1;32m%-15s [0;1m:[0m %s\n"], - status_nop = formatters["[1;32m%-15s [0;1m:[0m\n"], - } + local script = e_argument("script") or e_argument("scripts") + + if type(script) == "string" then + logs.writer("]0;"..script.."") -- for Alan to test + end end @@ -17715,14 +19012,26 @@ if e_argument("script") or e_argument("scripts") then ok = runners.execute_ctx_script(filename) end +elseif e_argument("evaluate") then + + runners.evaluate(e_argument("evaluate"),filename) + elseif e_argument("selfmerge") then -- embed used libraries runners.loadbase() local found = locate_libs() + if found then - utilities.merger.selfmerge(own.name,own.libs,{ found }) + local mtxrun = resolvers.findfile("mtxrun.lua") -- includes local name + if lfs.isfile(mtxrun) then + utilities.merger.selfmerge(mtxrun,own.libs,{ found }) + application.report("runner updated on resolved path: %s",mtxrun) + else + utilities.merger.selfmerge(own.name,own.libs,{ found }) + application.report("runner updated on relative path: %s",own.name) + end end elseif e_argument("selfclean") then @@ -17730,7 +19039,15 @@ elseif e_argument("selfclean") then -- remove embedded libraries runners.loadbase() - utilities.merger.selfclean(own.name) + + local mtxrun = resolvers.findfile("mtxrun.lua") -- includes local name + if lfs.isfile(mtxrun) then + utilities.merger.selfclean(mtxrun) + application.report("runner cleaned on resolved path: %s",mtxrun) + else + utilities.merger.selfclean(own.name) + application.report("runner cleaned on relative path: %s",own.name) + end elseif e_argument("selfupdate") then @@ -17972,6 +19289,8 @@ elseif e_argument("version") then application.version() + application.report("source path",environment.ownbin) + elseif e_argument("directives") then directives.show() @@ -17989,6 +19308,10 @@ elseif e_argument("exporthelp") then runners.loadbase() application.export(e_argument("exporthelp"),filename) +elseif e_argument("systeminfo") then + + runners.systeminfo() + elseif e_argument("help") or filename=='help' or filename == "" then application.help() diff --git a/scripts/context/stubs/unix/mtxrunjit b/scripts/context/stubs/unix/mtxrunjit new file mode 100644 index 000000000..117105aa5 --- /dev/null +++ b/scripts/context/stubs/unix/mtxrunjit @@ -0,0 +1,5 @@ +#!/bin/sh + +luajittex --luaonly $(dirname $0)/mtxrun "$@" + +# luajittex --luaonly ${0%jit} "$@" diff --git a/scripts/context/stubs/unix/pstopdf b/scripts/context/stubs/unix/pstopdf deleted file mode 100644 index 116f5f4a3..000000000 --- a/scripts/context/stubs/unix/pstopdf +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -mtxrun --script pstopdf "$@" diff --git a/scripts/context/stubs/win64/context.exe b/scripts/context/stubs/win64/context.exe Binary files differnew file mode 100644 index 000000000..93290a6e0 --- /dev/null +++ b/scripts/context/stubs/win64/context.exe diff --git a/scripts/context/stubs/win64/contextjit.exe b/scripts/context/stubs/win64/contextjit.exe Binary files differnew file mode 100644 index 000000000..93290a6e0 --- /dev/null +++ b/scripts/context/stubs/win64/contextjit.exe diff --git a/scripts/context/stubs/win64/ctxtools.exe b/scripts/context/stubs/win64/ctxtools.exe Binary files differnew file mode 100644 index 000000000..93290a6e0 --- /dev/null +++ b/scripts/context/stubs/win64/ctxtools.exe diff --git a/scripts/context/stubs/win64/luatools.exe b/scripts/context/stubs/win64/luatools.exe Binary files differnew file mode 100644 index 000000000..93290a6e0 --- /dev/null +++ b/scripts/context/stubs/win64/luatools.exe diff --git a/scripts/context/stubs/win64/metatex.exe b/scripts/context/stubs/win64/metatex.exe Binary files differnew file mode 100644 index 000000000..93290a6e0 --- /dev/null +++ b/scripts/context/stubs/win64/metatex.exe diff --git a/scripts/context/stubs/win64/mptopdf.exe b/scripts/context/stubs/win64/mptopdf.exe Binary files differnew file mode 100644 index 000000000..93290a6e0 --- /dev/null +++ b/scripts/context/stubs/win64/mptopdf.exe diff --git a/scripts/context/stubs/win64/mtxrun.dll b/scripts/context/stubs/win64/mtxrun.dll Binary files differnew file mode 100644 index 000000000..910502735 --- /dev/null +++ b/scripts/context/stubs/win64/mtxrun.dll diff --git a/scripts/context/stubs/win64/mtxrun.exe b/scripts/context/stubs/win64/mtxrun.exe Binary files differnew file mode 100644 index 000000000..93290a6e0 --- /dev/null +++ b/scripts/context/stubs/win64/mtxrun.exe diff --git a/scripts/context/stubs/win64/mtxrun.lua b/scripts/context/stubs/win64/mtxrun.lua new file mode 100644 index 000000000..edfeba8dd --- /dev/null +++ b/scripts/context/stubs/win64/mtxrun.lua @@ -0,0 +1,19363 @@ +#!/usr/bin/env texlua + +-- for k, v in next, _G.string do +-- local tv = type(v) +-- if tv == "table" then +-- for kk, vv in next, v do +-- print(k,kk,vv) +-- end +-- else +-- print(tv,k,v) +-- end +-- end + +if not modules then modules = { } end modules ['mtxrun'] = { + version = 1.001, + comment = "runner, lua replacement for texmfstart.rb", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- one can make a stub: +-- +-- #!/bin/sh +-- env LUATEXDIR=/....../texmf/scripts/context/lua luatex --luaonly mtxrun.lua "$@" + +-- filename : mtxrun.lua +-- comment : companion to context.tex +-- author : Hans Hagen, PRAGMA-ADE, Hasselt NL +-- copyright: PRAGMA ADE / ConTeXt Development Team +-- license : see context related readme files + +-- This script is based on texmfstart.rb but does not use kpsewhich to +-- locate files. Although kpse is a library it never came to opening up +-- its interface to other programs (esp scripting languages) and so we +-- do it ourselves. The lua variant evolved out of an experimental ruby +-- one. Interesting is that using a scripting language instead of c does +-- not have a speed penalty. Actually the lua variant is more efficient, +-- especially when multiple calls to kpsewhich are involved. The lua +-- library also gives way more control. + +-- to be done / considered +-- +-- support for --exec or make it default +-- support for jar files (or maybe not, never used, too messy) +-- support for $RUBYINPUTS cum suis (if still needed) +-- remember for subruns: _CTX_K_V_#{original}_ +-- remember for subruns: _CTX_K_S_#{original}_ +-- remember for subruns: TEXMFSTART.#{original} [tex.rb texmfstart.rb] + +-- begin library merge + + + +do -- create closure to overcome 200 locals limit + +package.loaded["l-lua"] = package.loaded["l-lua"] or true + +-- original size: 3888, stripped down to: 2197 + +if not modules then modules={} end modules ['l-lua']={ + version=1.001, + comment="companion to luat-lib.mkiv", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +local major,minor=string.match(_VERSION,"^[^%d]+(%d+)%.(%d+).*$") +_MAJORVERSION=tonumber(major) or 5 +_MINORVERSION=tonumber(minor) or 1 +_LUAVERSION=_MAJORVERSION+_MINORVERSION/10 +if not lpeg then + lpeg=require("lpeg") +end +if loadstring then + local loadnormal=load + function load(first,...) + if type(first)=="string" then + return loadstring(first,...) + else + return loadnormal(first,...) + end + end +else + loadstring=load +end +if not ipairs then + local function iterate(a,i) + i=i+1 + local v=a[i] + if v~=nil then + return i,v + end + end + function ipairs(a) + return iterate,a,0 + end +end +if not pairs then + function pairs(t) + return next,t + end +end +if not table.unpack then + table.unpack=_G.unpack +elseif not unpack then + _G.unpack=table.unpack +end +if not package.loaders then + package.loaders=package.searchers +end +local print,select,tostring=print,select,tostring +local inspectors={} +function setinspector(inspector) + inspectors[#inspectors+1]=inspector +end +function inspect(...) + for s=1,select("#",...) do + local value=select(s,...) + local done=false + for i=1,#inspectors do + done=inspectors[i](value) + if done then + break + end + end + if not done then + print(tostring(value)) + end + end +end +local dummy=function() end +function optionalrequire(...) + local ok,result=xpcall(require,dummy,...) + if ok then + return result + end +end +if lua then + lua.mask=load([[τεχ = 1]]) and "utf" or "ascii" +end +local flush=io.flush +if flush then + local execute=os.execute if execute then function os.execute(...) flush() return execute(...) end end + local exec=os.exec if exec then function os.exec (...) flush() return exec (...) end end + local spawn=os.spawn if spawn then function os.spawn (...) flush() return spawn (...) end end + local popen=io.popen if popen then function io.popen (...) flush() return popen (...) end end +end + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["l-package"] = package.loaded["l-package"] or true + +-- original size: 10587, stripped down to: 7815 + +if not modules then modules={} end modules ['l-package']={ + version=1.001, + comment="companion to luat-lib.mkiv", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +local type=type +local gsub,format,find=string.gsub,string.format,string.find +local P,S,Cs,lpegmatch=lpeg.P,lpeg.S,lpeg.Cs,lpeg.match +local package=package +local searchers=package.searchers or package.loaders +local filejoin=file and file.join or function(path,name) return path.."/"..name end +local isreadable=file and file.is_readable or function(name) local f=io.open(name) if f then f:close() return true end end +local addsuffix=file and file.addsuffix or function(name,suffix) return name.."."..suffix end +local function cleanpath(path) + return path +end +local pattern=Cs((((1-S("\\/"))^0*(S("\\/")^1/"/"))^0*(P(".")^1/"/"+P(1))^1)*-1) +local function lualibfile(name) + return lpegmatch(pattern,name) or name +end +local offset=luarocks and 1 or 0 +local helpers=package.helpers or { + cleanpath=cleanpath, + lualibfile=lualibfile, + trace=false, + report=function(...) print(format(...)) end, + builtin={ + ["preload table"]=searchers[1+offset], + ["path specification"]=searchers[2+offset], + ["cpath specification"]=searchers[3+offset], + ["all in one fallback"]=searchers[4+offset], + }, + methods={}, + sequence={ + "already loaded", + "preload table", + "qualified path", + "lua extra list", + "lib extra list", + "path specification", + "cpath specification", + "all in one fallback", + "not loaded", + } +} +package.helpers=helpers +local methods=helpers.methods +local builtin=helpers.builtin +local extraluapaths={} +local extralibpaths={} +local luapaths=nil +local libpaths=nil +local oldluapath=nil +local oldlibpath=nil +local nofextralua=-1 +local nofextralib=-1 +local nofpathlua=-1 +local nofpathlib=-1 +local function listpaths(what,paths) + local nofpaths=#paths + if nofpaths>0 then + for i=1,nofpaths do + helpers.report("using %s path %i: %s",what,i,paths[i]) + end + else + helpers.report("no %s paths defined",what) + end + return nofpaths +end +local function getextraluapaths() + if helpers.trace and #extraluapaths~=nofextralua then + nofextralua=listpaths("extra lua",extraluapaths) + end + return extraluapaths +end +local function getextralibpaths() + if helpers.trace and #extralibpaths~=nofextralib then + nofextralib=listpaths("extra lib",extralibpaths) + end + return extralibpaths +end +local function getluapaths() + local luapath=package.path or "" + if oldluapath~=luapath then + luapaths=file.splitpath(luapath,";") + oldluapath=luapath + nofpathlua=-1 + end + if helpers.trace and #luapaths~=nofpathlua then + nofpathlua=listpaths("builtin lua",luapaths) + end + return luapaths +end +local function getlibpaths() + local libpath=package.cpath or "" + if oldlibpath~=libpath then + libpaths=file.splitpath(libpath,";") + oldlibpath=libpath + nofpathlib=-1 + end + if helpers.trace and #libpaths~=nofpathlib then + nofpathlib=listpaths("builtin lib",libpaths) + end + return libpaths +end +package.luapaths=getluapaths +package.libpaths=getlibpaths +package.extraluapaths=getextraluapaths +package.extralibpaths=getextralibpaths +local hashes={ + lua={}, + lib={}, +} +local function registerpath(tag,what,target,...) + local pathlist={... } + local cleanpath=helpers.cleanpath + local trace=helpers.trace + local report=helpers.report + local hash=hashes[what] + local function add(path) + local path=cleanpath(path) + if not hash[path] then + target[#target+1]=path + hash[path]=true + if trace then + report("registered %s path %s: %s",tag,#target,path) + end + else + if trace then + report("duplicate %s path: %s",tag,path) + end + end + end + for p=1,#pathlist do + local path=pathlist[p] + if type(path)=="table" then + for i=1,#path do + add(path[i]) + end + else + add(path) + end + end + return paths +end +helpers.registerpath=registerpath +function package.extraluapath(...) + registerpath("extra lua","lua",extraluapaths,...) +end +function package.extralibpath(...) + registerpath("extra lib","lib",extralibpaths,...) +end +local function loadedaslib(resolved,rawname) + local base=gsub(rawname,"%.","_") + local init="luaopen_"..gsub(base,"%.","_") + if helpers.trace then + helpers.report("calling loadlib with '%s' with init '%s'",resolved,init) + end + return package.loadlib(resolved,init) +end +helpers.loadedaslib=loadedaslib +local function loadedbypath(name,rawname,paths,islib,what) + local trace=helpers.trace + for p=1,#paths do + local path=paths[p] + local resolved=filejoin(path,name) + if trace then + helpers.report("%s path, identifying '%s' on '%s'",what,name,path) + end + if isreadable(resolved) then + if trace then + helpers.report("%s path, '%s' found on '%s'",what,name,resolved) + end + if islib then + return loadedaslib(resolved,rawname) + else + return loadfile(resolved) + end + end + end +end +helpers.loadedbypath=loadedbypath +local function loadedbyname(name,rawname) + if find(name,"^/") or find(name,"^[a-zA-Z]:/") then + local trace=helpers.trace + if trace then + helpers.report("qualified name, identifying '%s'",what,name) + end + if isreadable(name) then + if trace then + helpers.report("qualified name, '%s' found",what,name) + end + return loadfile(name) + end + end +end +helpers.loadedbyname=loadedbyname +methods["already loaded"]=function(name) + return package.loaded[name] +end +methods["preload table"]=function(name) + return builtin["preload table"](name) +end +methods["qualified path"]=function(name) + return loadedbyname(addsuffix(lualibfile(name),"lua"),name) +end +methods["lua extra list"]=function(name) + return loadedbypath(addsuffix(lualibfile(name),"lua"),name,getextraluapaths(),false,"lua") +end +methods["lib extra list"]=function(name) + return loadedbypath(addsuffix(lualibfile(name),os.libsuffix),name,getextralibpaths(),true,"lib") +end +methods["path specification"]=function(name) + getluapaths() + return builtin["path specification"](name) +end +methods["cpath specification"]=function(name) + getlibpaths() + return builtin["cpath specification"](name) +end +methods["all in one fallback"]=function(name) + return builtin["all in one fallback"](name) +end +methods["not loaded"]=function(name) + if helpers.trace then + helpers.report("unable to locate '%s'",name or "?") + end + return nil +end +local level=0 +local used={} +helpers.traceused=false +function helpers.loaded(name) + local sequence=helpers.sequence + level=level+1 + for i=1,#sequence do + local method=sequence[i] + if helpers.trace then + helpers.report("%s, level '%s', method '%s', name '%s'","locating",level,method,name) + end + local result,rest=methods[method](name) + if type(result)=="function" then + if helpers.trace then + helpers.report("%s, level '%s', method '%s', name '%s'","found",level,method,name) + end + if helpers.traceused then + used[#used+1]={ level=level,name=name } + end + level=level-1 + return result,rest + end + end + level=level-1 + return nil +end +function helpers.showused() + local n=#used + if n>0 then + helpers.report("%s libraries loaded:",n) + helpers.report() + for i=1,n do + local u=used[i] + helpers.report("%i %a",u.level,u.name) + end + helpers.report() + end +end +function helpers.unload(name) + if helpers.trace then + if package.loaded[name] then + helpers.report("unloading, name '%s', %s",name,"done") + else + helpers.report("unloading, name '%s', %s",name,"not loaded") + end + end + package.loaded[name]=nil +end +table.insert(searchers,1,helpers.loaded) + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["l-lpeg"] = package.loaded["l-lpeg"] or true + +-- original size: 36977, stripped down to: 20349 + +if not modules then modules={} end modules ['l-lpeg']={ + version=1.001, + comment="companion to luat-lib.mkiv", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +lpeg=require("lpeg") +if not lpeg.print then function lpeg.print(...) print(lpeg.pcode(...)) end end +local type,next,tostring=type,next,tostring +local byte,char,gmatch,format=string.byte,string.char,string.gmatch,string.format +local floor=math.floor +local P,R,S,V,Ct,C,Cs,Cc,Cp,Cmt=lpeg.P,lpeg.R,lpeg.S,lpeg.V,lpeg.Ct,lpeg.C,lpeg.Cs,lpeg.Cc,lpeg.Cp,lpeg.Cmt +local lpegtype,lpegmatch,lpegprint=lpeg.type,lpeg.match,lpeg.print +if setinspector then + setinspector(function(v) if lpegtype(v) then lpegprint(v) return true end end) +end +lpeg.patterns=lpeg.patterns or {} +local patterns=lpeg.patterns +local anything=P(1) +local endofstring=P(-1) +local alwaysmatched=P(true) +patterns.anything=anything +patterns.endofstring=endofstring +patterns.beginofstring=alwaysmatched +patterns.alwaysmatched=alwaysmatched +local sign=S('+-') +local zero=P('0') +local digit=R('09') +local octdigit=R("07") +local lowercase=R("az") +local uppercase=R("AZ") +local underscore=P("_") +local hexdigit=digit+lowercase+uppercase +local cr,lf,crlf=P("\r"),P("\n"),P("\r\n") +local newline=P("\r")*(P("\n")+P(true))+P("\n") +local escaped=P("\\")*anything +local squote=P("'") +local dquote=P('"') +local space=P(" ") +local period=P(".") +local comma=P(",") +local utfbom_32_be=P('\000\000\254\255') +local utfbom_32_le=P('\255\254\000\000') +local utfbom_16_be=P('\254\255') +local utfbom_16_le=P('\255\254') +local utfbom_8=P('\239\187\191') +local utfbom=utfbom_32_be+utfbom_32_le+utfbom_16_be+utfbom_16_le+utfbom_8 +local utftype=utfbom_32_be*Cc("utf-32-be")+utfbom_32_le*Cc("utf-32-le")+utfbom_16_be*Cc("utf-16-be")+utfbom_16_le*Cc("utf-16-le")+utfbom_8*Cc("utf-8")+alwaysmatched*Cc("utf-8") +local utfstricttype=utfbom_32_be*Cc("utf-32-be")+utfbom_32_le*Cc("utf-32-le")+utfbom_16_be*Cc("utf-16-be")+utfbom_16_le*Cc("utf-16-le")+utfbom_8*Cc("utf-8") +local utfoffset=utfbom_32_be*Cc(4)+utfbom_32_le*Cc(4)+utfbom_16_be*Cc(2)+utfbom_16_le*Cc(2)+utfbom_8*Cc(3)+Cc(0) +local utf8next=R("\128\191") +patterns.utfbom_32_be=utfbom_32_be +patterns.utfbom_32_le=utfbom_32_le +patterns.utfbom_16_be=utfbom_16_be +patterns.utfbom_16_le=utfbom_16_le +patterns.utfbom_8=utfbom_8 +patterns.utf_16_be_nl=P("\000\r\000\n")+P("\000\r")+P("\000\n") +patterns.utf_16_le_nl=P("\r\000\n\000")+P("\r\000")+P("\n\000") +patterns.utf_32_be_nl=P("\000\000\000\r\000\000\000\n")+P("\000\000\000\r")+P("\000\000\000\n") +patterns.utf_32_le_nl=P("\r\000\000\000\n\000\000\000")+P("\r\000\000\000")+P("\n\000\000\000") +patterns.utf8one=R("\000\127") +patterns.utf8two=R("\194\223")*utf8next +patterns.utf8three=R("\224\239")*utf8next*utf8next +patterns.utf8four=R("\240\244")*utf8next*utf8next*utf8next +patterns.utfbom=utfbom +patterns.utftype=utftype +patterns.utfstricttype=utfstricttype +patterns.utfoffset=utfoffset +local utf8char=patterns.utf8one+patterns.utf8two+patterns.utf8three+patterns.utf8four +local validutf8char=utf8char^0*endofstring*Cc(true)+Cc(false) +local utf8character=P(1)*R("\128\191")^0 +patterns.utf8=utf8char +patterns.utf8char=utf8char +patterns.utf8character=utf8character +patterns.validutf8=validutf8char +patterns.validutf8char=validutf8char +local eol=S("\n\r") +local spacer=S(" \t\f\v") +local whitespace=eol+spacer +local nonspacer=1-spacer +local nonwhitespace=1-whitespace +patterns.eol=eol +patterns.spacer=spacer +patterns.whitespace=whitespace +patterns.nonspacer=nonspacer +patterns.nonwhitespace=nonwhitespace +local stripper=spacer^0*C((spacer^0*nonspacer^1)^0) +local fullstripper=whitespace^0*C((whitespace^0*nonwhitespace^1)^0) +local collapser=Cs(spacer^0/""*nonspacer^0*((spacer^0/" "*nonspacer^1)^0)) +local b_collapser=Cs(whitespace^0/""*(nonwhitespace^1+whitespace^1/" ")^0) +local e_collapser=Cs((whitespace^1*P(-1)/""+nonwhitespace^1+whitespace^1/" ")^0) +local m_collapser=Cs((nonwhitespace^1+whitespace^1/" ")^0) +local b_stripper=Cs(spacer^0/""*(nonspacer^1+spacer^1/" ")^0) +local e_stripper=Cs((spacer^1*P(-1)/""+nonspacer^1+spacer^1/" ")^0) +local m_stripper=Cs((nonspacer^1+spacer^1/" ")^0) +patterns.stripper=stripper +patterns.fullstripper=fullstripper +patterns.collapser=collapser +patterns.b_collapser=b_collapser +patterns.m_collapser=m_collapser +patterns.e_collapser=e_collapser +patterns.b_stripper=b_stripper +patterns.m_stripper=m_stripper +patterns.e_stripper=e_stripper +patterns.lowercase=lowercase +patterns.uppercase=uppercase +patterns.letter=patterns.lowercase+patterns.uppercase +patterns.space=space +patterns.tab=P("\t") +patterns.spaceortab=patterns.space+patterns.tab +patterns.newline=newline +patterns.emptyline=newline^1 +patterns.equal=P("=") +patterns.comma=comma +patterns.commaspacer=comma*spacer^0 +patterns.period=period +patterns.colon=P(":") +patterns.semicolon=P(";") +patterns.underscore=underscore +patterns.escaped=escaped +patterns.squote=squote +patterns.dquote=dquote +patterns.nosquote=(escaped+(1-squote))^0 +patterns.nodquote=(escaped+(1-dquote))^0 +patterns.unsingle=(squote/"")*patterns.nosquote*(squote/"") +patterns.undouble=(dquote/"")*patterns.nodquote*(dquote/"") +patterns.unquoted=patterns.undouble+patterns.unsingle +patterns.unspacer=((patterns.spacer^1)/"")^0 +patterns.singlequoted=squote*patterns.nosquote*squote +patterns.doublequoted=dquote*patterns.nodquote*dquote +patterns.quoted=patterns.doublequoted+patterns.singlequoted +patterns.digit=digit +patterns.octdigit=octdigit +patterns.hexdigit=hexdigit +patterns.sign=sign +patterns.cardinal=digit^1 +patterns.integer=sign^-1*digit^1 +patterns.unsigned=digit^0*period*digit^1 +patterns.float=sign^-1*patterns.unsigned +patterns.cunsigned=digit^0*comma*digit^1 +patterns.cpunsigned=digit^0*(period+comma)*digit^1 +patterns.cfloat=sign^-1*patterns.cunsigned +patterns.cpfloat=sign^-1*patterns.cpunsigned +patterns.number=patterns.float+patterns.integer +patterns.cnumber=patterns.cfloat+patterns.integer +patterns.cpnumber=patterns.cpfloat+patterns.integer +patterns.oct=zero*octdigit^1 +patterns.octal=patterns.oct +patterns.HEX=zero*P("X")*(digit+uppercase)^1 +patterns.hex=zero*P("x")*(digit+lowercase)^1 +patterns.hexadecimal=zero*S("xX")*hexdigit^1 +patterns.hexafloat=sign^-1*zero*S("xX")*(hexdigit^0*period*hexdigit^1+hexdigit^1*period*hexdigit^0+hexdigit^1)*(S("pP")*sign^-1*hexdigit^1)^-1 +patterns.decafloat=sign^-1*(digit^0*period*digit^1+digit^1*period*digit^0+digit^1)*S("eE")*sign^-1*digit^1 +patterns.propername=(uppercase+lowercase+underscore)*(uppercase+lowercase+underscore+digit)^0*endofstring +patterns.somecontent=(anything-newline-space)^1 +patterns.beginline=#(1-newline) +patterns.longtostring=Cs(whitespace^0/""*((patterns.quoted+nonwhitespace^1+whitespace^1/""*(P(-1)+Cc(" ")))^0)) +local function anywhere(pattern) + return P { P(pattern)+1*V(1) } +end +lpeg.anywhere=anywhere +function lpeg.instringchecker(p) + p=anywhere(p) + return function(str) + return lpegmatch(p,str) and true or false + end +end +function lpeg.splitter(pattern,action) + return (((1-P(pattern))^1)/action+1)^0 +end +function lpeg.tsplitter(pattern,action) + return Ct((((1-P(pattern))^1)/action+1)^0) +end +local splitters_s,splitters_m,splitters_t={},{},{} +local function splitat(separator,single) + local splitter=(single and splitters_s[separator]) or splitters_m[separator] + if not splitter then + separator=P(separator) + local other=C((1-separator)^0) + if single then + local any=anything + splitter=other*(separator*C(any^0)+"") + splitters_s[separator]=splitter + else + splitter=other*(separator*other)^0 + splitters_m[separator]=splitter + end + end + return splitter +end +local function tsplitat(separator) + local splitter=splitters_t[separator] + if not splitter then + splitter=Ct(splitat(separator)) + splitters_t[separator]=splitter + end + return splitter +end +lpeg.splitat=splitat +lpeg.tsplitat=tsplitat +function string.splitup(str,separator) + if not separator then + separator="," + end + return lpegmatch(splitters_m[separator] or splitat(separator),str) +end +local cache={} +function lpeg.split(separator,str) + local c=cache[separator] + if not c then + c=tsplitat(separator) + cache[separator]=c + end + return lpegmatch(c,str) +end +function string.split(str,separator) + if separator then + local c=cache[separator] + if not c then + c=tsplitat(separator) + cache[separator]=c + end + return lpegmatch(c,str) + else + return { str } + end +end +local spacing=patterns.spacer^0*newline +local empty=spacing*Cc("") +local nonempty=Cs((1-spacing)^1)*spacing^-1 +local content=(empty+nonempty)^1 +patterns.textline=content +local linesplitter=tsplitat(newline) +patterns.linesplitter=linesplitter +function string.splitlines(str) + return lpegmatch(linesplitter,str) +end +local cache={} +function lpeg.checkedsplit(separator,str) + local c=cache[separator] + if not c then + separator=P(separator) + local other=C((1-separator)^1) + c=Ct(separator^0*other*(separator^1*other)^0) + cache[separator]=c + end + return lpegmatch(c,str) +end +function string.checkedsplit(str,separator) + local c=cache[separator] + if not c then + separator=P(separator) + local other=C((1-separator)^1) + c=Ct(separator^0*other*(separator^1*other)^0) + cache[separator]=c + end + return lpegmatch(c,str) +end +local function f2(s) local c1,c2=byte(s,1,2) return c1*64+c2-12416 end +local function f3(s) local c1,c2,c3=byte(s,1,3) return (c1*64+c2)*64+c3-925824 end +local function f4(s) local c1,c2,c3,c4=byte(s,1,4) return ((c1*64+c2)*64+c3)*64+c4-63447168 end +local utf8byte=patterns.utf8one/byte+patterns.utf8two/f2+patterns.utf8three/f3+patterns.utf8four/f4 +patterns.utf8byte=utf8byte +local cache={} +function lpeg.stripper(str) + if type(str)=="string" then + local s=cache[str] + if not s then + s=Cs(((S(str)^1)/""+1)^0) + cache[str]=s + end + return s + else + return Cs(((str^1)/""+1)^0) + end +end +local cache={} +function lpeg.keeper(str) + if type(str)=="string" then + local s=cache[str] + if not s then + s=Cs((((1-S(str))^1)/""+1)^0) + cache[str]=s + end + return s + else + return Cs((((1-str)^1)/""+1)^0) + end +end +function lpeg.frontstripper(str) + return (P(str)+P(true))*Cs(anything^0) +end +function lpeg.endstripper(str) + return Cs((1-P(str)*endofstring)^0) +end +function lpeg.replacer(one,two,makefunction,isutf) + local pattern + local u=isutf and utf8char or 1 + if type(one)=="table" then + local no=#one + local p=P(false) + if no==0 then + for k,v in next,one do + p=p+P(k)/v + end + pattern=Cs((p+u)^0) + elseif no==1 then + local o=one[1] + one,two=P(o[1]),o[2] + pattern=Cs((one/two+u)^0) + else + for i=1,no do + local o=one[i] + p=p+P(o[1])/o[2] + end + pattern=Cs((p+u)^0) + end + else + pattern=Cs((P(one)/(two or "")+u)^0) + end + if makefunction then + return function(str) + return lpegmatch(pattern,str) + end + else + return pattern + end +end +function lpeg.finder(lst,makefunction,isutf) + local pattern + if type(lst)=="table" then + pattern=P(false) + if #lst==0 then + for k,v in next,lst do + pattern=pattern+P(k) + end + else + for i=1,#lst do + pattern=pattern+P(lst[i]) + end + end + else + pattern=P(lst) + end + if isutf then + pattern=((utf8char or 1)-pattern)^0*pattern + else + pattern=(1-pattern)^0*pattern + end + if makefunction then + return function(str) + return lpegmatch(pattern,str) + end + else + return pattern + end +end +local splitters_f,splitters_s={},{} +function lpeg.firstofsplit(separator) + local splitter=splitters_f[separator] + if not splitter then + local pattern=P(separator) + splitter=C((1-pattern)^0) + splitters_f[separator]=splitter + end + return splitter +end +function lpeg.secondofsplit(separator) + local splitter=splitters_s[separator] + if not splitter then + local pattern=P(separator) + splitter=(1-pattern)^0*pattern*C(anything^0) + splitters_s[separator]=splitter + end + return splitter +end +local splitters_s,splitters_p={},{} +function lpeg.beforesuffix(separator) + local splitter=splitters_s[separator] + if not splitter then + local pattern=P(separator) + splitter=C((1-pattern)^0)*pattern*endofstring + splitters_s[separator]=splitter + end + return splitter +end +function lpeg.afterprefix(separator) + local splitter=splitters_p[separator] + if not splitter then + local pattern=P(separator) + splitter=pattern*C(anything^0) + splitters_p[separator]=splitter + end + return splitter +end +function lpeg.balancer(left,right) + left,right=P(left),P(right) + return P { left*((1-left-right)+V(1))^0*right } +end +local nany=utf8char/"" +function lpeg.counter(pattern) + pattern=Cs((P(pattern)/" "+nany)^0) + return function(str) + return #lpegmatch(pattern,str) + end +end +utf=utf or (unicode and unicode.utf8) or {} +local utfcharacters=utf and utf.characters or string.utfcharacters +local utfgmatch=utf and utf.gmatch +local utfchar=utf and utf.char +lpeg.UP=lpeg.P +if utfcharacters then + function lpeg.US(str) + local p=P(false) + for uc in utfcharacters(str) do + p=p+P(uc) + end + return p + end +elseif utfgmatch then + function lpeg.US(str) + local p=P(false) + for uc in utfgmatch(str,".") do + p=p+P(uc) + end + return p + end +else + function lpeg.US(str) + local p=P(false) + local f=function(uc) + p=p+P(uc) + end + lpegmatch((utf8char/f)^0,str) + return p + end +end +local range=utf8byte*utf8byte+Cc(false) +function lpeg.UR(str,more) + local first,last + if type(str)=="number" then + first=str + last=more or first + else + first,last=lpegmatch(range,str) + if not last then + return P(str) + end + end + if first==last then + return P(str) + elseif utfchar and (last-first<8) then + local p=P(false) + for i=first,last do + p=p+P(utfchar(i)) + end + return p + else + local f=function(b) + return b>=first and b<=last + end + return utf8byte/f + end +end +function lpeg.is_lpeg(p) + return p and lpegtype(p)=="pattern" +end +function lpeg.oneof(list,...) + if type(list)~="table" then + list={ list,... } + end + local p=P(list[1]) + for l=2,#list do + p=p+P(list[l]) + end + return p +end +local sort=table.sort +local function copyindexed(old) + local new={} + for i=1,#old do + new[i]=old + end + return new +end +local function sortedkeys(tab) + local keys,s={},0 + for key,_ in next,tab do + s=s+1 + keys[s]=key + end + sort(keys) + return keys +end +function lpeg.append(list,pp,delayed,checked) + local p=pp + if #list>0 then + local keys=copyindexed(list) + sort(keys) + for i=#keys,1,-1 do + local k=keys[i] + if p then + p=P(k)+p + else + p=P(k) + end + end + elseif delayed then + local keys=sortedkeys(list) + if p then + for i=1,#keys,1 do + local k=keys[i] + local v=list[k] + p=P(k)/list+p + end + else + for i=1,#keys do + local k=keys[i] + local v=list[k] + if p then + p=P(k)+p + else + p=P(k) + end + end + if p then + p=p/list + end + end + elseif checked then + local keys=sortedkeys(list) + for i=1,#keys do + local k=keys[i] + local v=list[k] + if p then + if k==v then + p=P(k)+p + else + p=P(k)/v+p + end + else + if k==v then + p=P(k) + else + p=P(k)/v + end + end + end + else + local keys=sortedkeys(list) + for i=1,#keys do + local k=keys[i] + local v=list[k] + if p then + p=P(k)/v+p + else + p=P(k)/v + end + end + end + return p +end +local p_false=P(false) +local p_true=P(true) +local function make(t) + local function making(t) + local p=p_false + local keys=sortedkeys(t) + for i=1,#keys do + local k=keys[i] + if k~="" then + local v=t[k] + if v==true then + p=p+P(k)*p_true + elseif v==false then + else + p=p+P(k)*making(v) + end + end + end + if t[""] then + p=p+p_true + end + return p + end + local p=p_false + local keys=sortedkeys(t) + for i=1,#keys do + local k=keys[i] + if k~="" then + local v=t[k] + if v==true then + p=p+P(k)*p_true + elseif v==false then + else + p=p+P(k)*making(v) + end + end + end + return p +end +local function collapse(t,x) + if type(t)~="table" then + return t,x + else + local n=next(t) + if n==nil then + return t,x + elseif next(t,n)==nil then + local k=n + local v=t[k] + if type(v)=="table" then + return collapse(v,x..k) + else + return v,x..k + end + else + local tt={} + for k,v in next,t do + local vv,kk=collapse(v,k) + tt[kk]=vv + end + return tt,x + end + end +end +function lpeg.utfchartabletopattern(list) + local tree={} + local n=#list + if n==0 then + for s in next,list do + local t=tree + local p,pk + for c in gmatch(s,".") do + if t==true then + t={ [c]=true,[""]=true } + p[pk]=t + p=t + t=false + elseif t==false then + t={ [c]=false } + p[pk]=t + p=t + t=false + else + local tc=t[c] + if not tc then + tc=false + t[c]=false + end + p=t + t=tc + end + pk=c + end + if t==false then + p[pk]=true + elseif t==true then + else + t[""]=true + end + end + else + for i=1,n do + local s=list[i] + local t=tree + local p,pk + for c in gmatch(s,".") do + if t==true then + t={ [c]=true,[""]=true } + p[pk]=t + p=t + t=false + elseif t==false then + t={ [c]=false } + p[pk]=t + p=t + t=false + else + local tc=t[c] + if not tc then + tc=false + t[c]=false + end + p=t + t=tc + end + pk=c + end + if t==false then + p[pk]=true + elseif t==true then + else + t[""]=true + end + end + end + return make(tree) +end +patterns.containseol=lpeg.finder(eol) +local function nextstep(n,step,result) + local m=n%step + local d=floor(n/step) + if d>0 then + local v=V(tostring(step)) + local s=result.start + for i=1,d do + if s then + s=v*s + else + s=v + end + end + result.start=s + end + if step>1 and result.start then + local v=V(tostring(step/2)) + result[tostring(step)]=v*v + end + if step>0 then + return nextstep(m,step/2,result) + else + return result + end +end +function lpeg.times(pattern,n) + return P(nextstep(n,2^16,{ "start",["1"]=pattern })) +end +local trailingzeros=zero^0*-digit +local case_1=period*trailingzeros/"" +local case_2=period*(digit-trailingzeros)^1*(trailingzeros/"") +local number=digit^1*(case_1+case_2) +local stripper=Cs((number+1)^0) +lpeg.patterns.stripzeros=stripper +local byte_to_HEX={} +local byte_to_hex={} +local byte_to_dec={} +local hex_to_byte={} +for i=0,255 do + local H=format("%02X",i) + local h=format("%02x",i) + local d=format("%03i",i) + local c=char(i) + byte_to_HEX[c]=H + byte_to_hex[c]=h + byte_to_dec[c]=d + hex_to_byte[h]=c + hex_to_byte[H]=c +end +local hextobyte=P(2)/hex_to_byte +local bytetoHEX=P(1)/byte_to_HEX +local bytetohex=P(1)/byte_to_hex +local bytetodec=P(1)/byte_to_dec +local hextobytes=Cs(hextobyte^0) +local bytestoHEX=Cs(bytetoHEX^0) +local bytestohex=Cs(bytetohex^0) +local bytestodec=Cs(bytetodec^0) +patterns.hextobyte=hextobyte +patterns.bytetoHEX=bytetoHEX +patterns.bytetohex=bytetohex +patterns.bytetodec=bytetodec +patterns.hextobytes=hextobytes +patterns.bytestoHEX=bytestoHEX +patterns.bytestohex=bytestohex +patterns.bytestodec=bytestodec +function string.toHEX(s) + if not s or s=="" then + return s + else + return lpegmatch(bytestoHEX,s) + end +end +function string.tohex(s) + if not s or s=="" then + return s + else + return lpegmatch(bytestohex,s) + end +end +function string.todec(s) + if not s or s=="" then + return s + else + return lpegmatch(bytestodec,s) + end +end +function string.tobytes(s) + if not s or s=="" then + return s + else + return lpegmatch(hextobytes,s) + end +end + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["l-function"] = package.loaded["l-function"] or true + +-- original size: 361, stripped down to: 322 + +if not modules then modules={} end modules ['l-functions']={ + version=1.001, + comment="companion to luat-lib.mkiv", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +functions=functions or {} +function functions.dummy() end + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["l-string"] = package.loaded["l-string"] or true + +-- original size: 5694, stripped down to: 2827 + +if not modules then modules={} end modules ['l-string']={ + version=1.001, + comment="companion to luat-lib.mkiv", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +local string=string +local sub,gmatch,format,char,byte,rep,lower=string.sub,string.gmatch,string.format,string.char,string.byte,string.rep,string.lower +local lpegmatch,patterns=lpeg.match,lpeg.patterns +local P,S,C,Ct,Cc,Cs=lpeg.P,lpeg.S,lpeg.C,lpeg.Ct,lpeg.Cc,lpeg.Cs +local unquoted=patterns.squote*C(patterns.nosquote)*patterns.squote+patterns.dquote*C(patterns.nodquote)*patterns.dquote +function string.unquoted(str) + return lpegmatch(unquoted,str) or str +end +function string.quoted(str) + return format("%q",str) +end +function string.count(str,pattern) + local n=0 + for _ in gmatch(str,pattern) do + n=n+1 + end + return n +end +function string.limit(str,n,sentinel) + if #str>n then + sentinel=sentinel or "..." + return sub(str,1,(n-#sentinel))..sentinel + else + return str + end +end +local stripper=patterns.stripper +local fullstripper=patterns.fullstripper +local collapser=patterns.collapser +local longtostring=patterns.longtostring +function string.strip(str) + return lpegmatch(stripper,str) or "" +end +function string.fullstrip(str) + return lpegmatch(fullstripper,str) or "" +end +function string.collapsespaces(str) + return lpegmatch(collapser,str) or "" +end +function string.longtostring(str) + return lpegmatch(longtostring,str) or "" +end +local pattern=P(" ")^0*P(-1) +function string.is_empty(str) + if str=="" then + return true + else + return lpegmatch(pattern,str) and true or false + end +end +local anything=patterns.anything +local allescapes=Cc("%")*S(".-+%?()[]*") +local someescapes=Cc("%")*S(".-+%()[]") +local matchescapes=Cc(".")*S("*?") +local pattern_a=Cs ((allescapes+anything )^0 ) +local pattern_b=Cs ((someescapes+matchescapes+anything )^0 ) +local pattern_c=Cs (Cc("^")*(someescapes+matchescapes+anything )^0*Cc("$") ) +function string.escapedpattern(str,simple) + return lpegmatch(simple and pattern_b or pattern_a,str) +end +function string.topattern(str,lowercase,strict) + if str=="" or type(str)~="string" then + return ".*" + elseif strict then + str=lpegmatch(pattern_c,str) + else + str=lpegmatch(pattern_b,str) + end + if lowercase then + return lower(str) + else + return str + end +end +function string.valid(str,default) + return (type(str)=="string" and str~="" and str) or default or nil +end +string.itself=function(s) return s end +local pattern=Ct(C(1)^0) +function string.totable(str) + return lpegmatch(pattern,str) +end +local replacer=lpeg.replacer("@","%%") +function string.tformat(fmt,...) + return format(lpegmatch(replacer,fmt),...) +end +string.quote=string.quoted +string.unquote=string.unquoted + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["l-table"] = package.loaded["l-table"] or true + +-- original size: 35724, stripped down to: 21525 + +if not modules then modules={} end modules ['l-table']={ + version=1.001, + comment="companion to luat-lib.mkiv", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +local type,next,tostring,tonumber,ipairs,select=type,next,tostring,tonumber,ipairs,select +local table,string=table,string +local concat,sort,insert,remove=table.concat,table.sort,table.insert,table.remove +local format,lower,dump=string.format,string.lower,string.dump +local getmetatable,setmetatable=getmetatable,setmetatable +local getinfo=debug.getinfo +local lpegmatch,patterns=lpeg.match,lpeg.patterns +local floor=math.floor +local stripper=patterns.stripper +function table.strip(tab) + local lst,l={},0 + for i=1,#tab do + local s=lpegmatch(stripper,tab[i]) or "" + if s=="" then + else + l=l+1 + lst[l]=s + end + end + return lst +end +function table.keys(t) + if t then + local keys,k={},0 + for key in next,t do + k=k+1 + keys[k]=key + end + return keys + else + return {} + end +end +local function compare(a,b) + local ta=type(a) + if ta=="number" then + local tb=type(b) + if ta==tb then + return a<b + elseif tb=="string" then + return tostring(a)<b + end + elseif ta=="string" then + local tb=type(b) + if ta==tb then + return a<b + else + return a<tostring(b) + end + end + return tostring(a)<tostring(b) +end +local function sortedkeys(tab) + if tab then + local srt,category,s={},0,0 + for key in next,tab do + s=s+1 + srt[s]=key + if category==3 then + elseif category==1 then + if type(key)~="string" then + category=3 + end + elseif category==2 then + if type(key)~="number" then + category=3 + end + else + local tkey=type(key) + if tkey=="string" then + category=1 + elseif tkey=="number" then + category=2 + else + category=3 + end + end + end + if s<2 then + elseif category==3 then + sort(srt,compare) + else + sort(srt) + end + return srt + else + return {} + end +end +local function sortedhashonly(tab) + if tab then + local srt,s={},0 + for key in next,tab do + if type(key)=="string" then + s=s+1 + srt[s]=key + end + end + if s>1 then + sort(srt) + end + return srt + else + return {} + end +end +local function sortedindexonly(tab) + if tab then + local srt,s={},0 + for key in next,tab do + if type(key)=="number" then + s=s+1 + srt[s]=key + end + end + if s>1 then + sort(srt) + end + return srt + else + return {} + end +end +local function sortedhashkeys(tab,cmp) + if tab then + local srt,s={},0 + for key in next,tab do + if key then + s=s+1 + srt[s]=key + end + end + if s>1 then + sort(srt,cmp) + end + return srt + else + return {} + end +end +function table.allkeys(t) + local keys={} + for k,v in next,t do + for k in next,v do + keys[k]=true + end + end + return sortedkeys(keys) +end +table.sortedkeys=sortedkeys +table.sortedhashonly=sortedhashonly +table.sortedindexonly=sortedindexonly +table.sortedhashkeys=sortedhashkeys +local function nothing() end +local function sortedhash(t,cmp) + if t then + local s + if cmp then + s=sortedhashkeys(t,function(a,b) return cmp(t,a,b) end) + else + s=sortedkeys(t) + end + local m=#s + if m==1 then + return next,t + elseif m>0 then + local n=0 + return function() + if n<m then + n=n+1 + local k=s[n] + return k,t[k] + end + end + end + end + return nothing +end +table.sortedhash=sortedhash +table.sortedpairs=sortedhash +function table.append(t,list) + local n=#t + for i=1,#list do + n=n+1 + t[n]=list[i] + end + return t +end +function table.prepend(t,list) + local nl=#list + local nt=nl+#t + for i=#t,1,-1 do + t[nt]=t[i] + nt=nt-1 + end + for i=1,#list do + t[i]=list[i] + end + return t +end +function table.merge(t,...) + t=t or {} + for i=1,select("#",...) do + for k,v in next,(select(i,...)) do + t[k]=v + end + end + return t +end +function table.merged(...) + local t={} + for i=1,select("#",...) do + for k,v in next,(select(i,...)) do + t[k]=v + end + end + return t +end +function table.imerge(t,...) + local nt=#t + for i=1,select("#",...) do + local nst=select(i,...) + for j=1,#nst do + nt=nt+1 + t[nt]=nst[j] + end + end + return t +end +function table.imerged(...) + local tmp,ntmp={},0 + for i=1,select("#",...) do + local nst=select(i,...) + for j=1,#nst do + ntmp=ntmp+1 + tmp[ntmp]=nst[j] + end + end + return tmp +end +local function fastcopy(old,metatabletoo) + if old then + local new={} + for k,v in next,old do + if type(v)=="table" then + new[k]=fastcopy(v,metatabletoo) + else + new[k]=v + end + end + if metatabletoo then + local mt=getmetatable(old) + if mt then + setmetatable(new,mt) + end + end + return new + else + return {} + end +end +local function copy(t,tables) + tables=tables or {} + local tcopy={} + if not tables[t] then + tables[t]=tcopy + end + for i,v in next,t do + if type(i)=="table" then + if tables[i] then + i=tables[i] + else + i=copy(i,tables) + end + end + if type(v)~="table" then + tcopy[i]=v + elseif tables[v] then + tcopy[i]=tables[v] + else + tcopy[i]=copy(v,tables) + end + end + local mt=getmetatable(t) + if mt then + setmetatable(tcopy,mt) + end + return tcopy +end +table.fastcopy=fastcopy +table.copy=copy +function table.derive(parent) + local child={} + if parent then + setmetatable(child,{ __index=parent }) + end + return child +end +function table.tohash(t,value) + local h={} + if t then + if value==nil then value=true end + for _,v in next,t do + h[v]=value + end + end + return h +end +function table.fromhash(t) + local hsh,h={},0 + for k,v in next,t do + if v then + h=h+1 + hsh[h]=k + end + end + return hsh +end +local noquotes,hexify,handle,compact,inline,functions +local reserved=table.tohash { + 'and','break','do','else','elseif','end','false','for','function','if', + 'in','local','nil','not','or','repeat','return','then','true','until','while', + 'NaN','goto', +} +local function simple_table(t) + local nt=#t + if nt>0 then + local n=0 + for _,v in next,t do + n=n+1 + end + if n==nt then + local tt={} + for i=1,nt do + local v=t[i] + local tv=type(v) + if tv=="number" then + if hexify then + tt[i]=format("0x%X",v) + else + tt[i]=tostring(v) + end + elseif tv=="string" then + tt[i]=format("%q",v) + elseif tv=="boolean" then + tt[i]=v and "true" or "false" + else + return nil + end + end + return tt + end + end + return nil +end +local propername=patterns.propername +local function dummy() end +local function do_serialize(root,name,depth,level,indexed) + if level>0 then + depth=depth.." " + if indexed then + handle(format("%s{",depth)) + else + local tn=type(name) + if tn=="number" then + if hexify then + handle(format("%s[0x%X]={",depth,name)) + else + handle(format("%s[%s]={",depth,name)) + end + elseif tn=="string" then + if noquotes and not reserved[name] and lpegmatch(propername,name) then + handle(format("%s%s={",depth,name)) + else + handle(format("%s[%q]={",depth,name)) + end + elseif tn=="boolean" then + handle(format("%s[%s]={",depth,name and "true" or "false")) + else + handle(format("%s{",depth)) + end + end + end + if root and next(root)~=nil then + local first,last=nil,0 + if compact then + last=#root + for k=1,last do + if root[k]==nil then + last=k-1 + break + end + end + if last>0 then + first=1 + end + end + local sk=sortedkeys(root) + for i=1,#sk do + local k=sk[i] + local v=root[k] + local tv=type(v) + local tk=type(k) + if compact and first and tk=="number" and k>=first and k<=last then + if tv=="number" then + if hexify then + handle(format("%s 0x%X,",depth,v)) + else + handle(format("%s %s,",depth,v)) + end + elseif tv=="string" then + handle(format("%s %q,",depth,v)) + elseif tv=="table" then + if next(v)==nil then + handle(format("%s {},",depth)) + elseif inline then + local st=simple_table(v) + if st then + handle(format("%s { %s },",depth,concat(st,", "))) + else + do_serialize(v,k,depth,level+1,true) + end + else + do_serialize(v,k,depth,level+1,true) + end + elseif tv=="boolean" then + handle(format("%s %s,",depth,v and "true" or "false")) + elseif tv=="function" then + if functions then + handle(format('%s load(%q),',depth,dump(v))) + else + handle(format('%s "function",',depth)) + end + else + handle(format("%s %q,",depth,tostring(v))) + end + elseif k=="__p__" then + if false then + handle(format("%s __p__=nil,",depth)) + end + elseif tv=="number" then + if tk=="number" then + if hexify then + handle(format("%s [0x%X]=0x%X,",depth,k,v)) + else + handle(format("%s [%s]=%s,",depth,k,v)) + end + elseif tk=="boolean" then + if hexify then + handle(format("%s [%s]=0x%X,",depth,k and "true" or "false",v)) + else + handle(format("%s [%s]=%s,",depth,k and "true" or "false",v)) + end + elseif noquotes and not reserved[k] and lpegmatch(propername,k) then + if hexify then + handle(format("%s %s=0x%X,",depth,k,v)) + else + handle(format("%s %s=%s,",depth,k,v)) + end + else + if hexify then + handle(format("%s [%q]=0x%X,",depth,k,v)) + else + handle(format("%s [%q]=%s,",depth,k,v)) + end + end + elseif tv=="string" then + if tk=="number" then + if hexify then + handle(format("%s [0x%X]=%q,",depth,k,v)) + else + handle(format("%s [%s]=%q,",depth,k,v)) + end + elseif tk=="boolean" then + handle(format("%s [%s]=%q,",depth,k and "true" or "false",v)) + elseif noquotes and not reserved[k] and lpegmatch(propername,k) then + handle(format("%s %s=%q,",depth,k,v)) + else + handle(format("%s [%q]=%q,",depth,k,v)) + end + elseif tv=="table" then + if next(v)==nil then + if tk=="number" then + if hexify then + handle(format("%s [0x%X]={},",depth,k)) + else + handle(format("%s [%s]={},",depth,k)) + end + elseif tk=="boolean" then + handle(format("%s [%s]={},",depth,k and "true" or "false")) + elseif noquotes and not reserved[k] and lpegmatch(propername,k) then + handle(format("%s %s={},",depth,k)) + else + handle(format("%s [%q]={},",depth,k)) + end + elseif inline then + local st=simple_table(v) + if st then + if tk=="number" then + if hexify then + handle(format("%s [0x%X]={ %s },",depth,k,concat(st,", "))) + else + handle(format("%s [%s]={ %s },",depth,k,concat(st,", "))) + end + elseif tk=="boolean" then + handle(format("%s [%s]={ %s },",depth,k and "true" or "false",concat(st,", "))) + elseif noquotes and not reserved[k] and lpegmatch(propername,k) then + handle(format("%s %s={ %s },",depth,k,concat(st,", "))) + else + handle(format("%s [%q]={ %s },",depth,k,concat(st,", "))) + end + else + do_serialize(v,k,depth,level+1) + end + else + do_serialize(v,k,depth,level+1) + end + elseif tv=="boolean" then + if tk=="number" then + if hexify then + handle(format("%s [0x%X]=%s,",depth,k,v and "true" or "false")) + else + handle(format("%s [%s]=%s,",depth,k,v and "true" or "false")) + end + elseif tk=="boolean" then + handle(format("%s [%s]=%s,",depth,tostring(k),v and "true" or "false")) + elseif noquotes and not reserved[k] and lpegmatch(propername,k) then + handle(format("%s %s=%s,",depth,k,v and "true" or "false")) + else + handle(format("%s [%q]=%s,",depth,k,v and "true" or "false")) + end + elseif tv=="function" then + if functions then + local f=getinfo(v).what=="C" and dump(dummy) or dump(v) + if tk=="number" then + if hexify then + handle(format("%s [0x%X]=load(%q),",depth,k,f)) + else + handle(format("%s [%s]=load(%q),",depth,k,f)) + end + elseif tk=="boolean" then + handle(format("%s [%s]=load(%q),",depth,k and "true" or "false",f)) + elseif noquotes and not reserved[k] and lpegmatch(propername,k) then + handle(format("%s %s=load(%q),",depth,k,f)) + else + handle(format("%s [%q]=load(%q),",depth,k,f)) + end + end + else + if tk=="number" then + if hexify then + handle(format("%s [0x%X]=%q,",depth,k,tostring(v))) + else + handle(format("%s [%s]=%q,",depth,k,tostring(v))) + end + elseif tk=="boolean" then + handle(format("%s [%s]=%q,",depth,k and "true" or "false",tostring(v))) + elseif noquotes and not reserved[k] and lpegmatch(propername,k) then + handle(format("%s %s=%q,",depth,k,tostring(v))) + else + handle(format("%s [%q]=%q,",depth,k,tostring(v))) + end + end + end + end + if level>0 then + handle(format("%s},",depth)) + end +end +local function serialize(_handle,root,name,specification) + local tname=type(name) + if type(specification)=="table" then + noquotes=specification.noquotes + hexify=specification.hexify + handle=_handle or specification.handle or print + 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 + compact=true + inline=true + functions=true + end + if tname=="string" then + if name=="return" then + handle("return {") + else + handle(name.."={") + end + elseif tname=="number" then + if hexify then + handle(format("[0x%X]={",name)) + else + handle("["..name.."]={") + end + elseif tname=="boolean" then + if name then + handle("return {") + else + handle("{") + end + else + handle("t={") + end + if root then + if getmetatable(root) then + local dummy=root._w_h_a_t_e_v_e_r_ + root._w_h_a_t_e_v_e_r_=nil + end + if next(root)~=nil then + do_serialize(root,name,"",0) + end + end + handle("}") +end +function table.serialize(root,name,specification) + local t,n={},0 + local function flush(s) + n=n+1 + t[n]=s + end + serialize(flush,root,name,specification) + return concat(t,"\n") +end +table.tohandle=serialize +local maxtab=2*1024 +function table.tofile(filename,root,name,specification) + local f=io.open(filename,'w') + if f then + if maxtab>1 then + local t,n={},0 + local function flush(s) + n=n+1 + t[n]=s + if n>maxtab then + f:write(concat(t,"\n"),"\n") + t,n={},0 + end + end + serialize(flush,root,name,specification) + f:write(concat(t,"\n"),"\n") + else + local function flush(s) + f:write(s,"\n") + end + serialize(flush,root,name,specification) + end + f:close() + io.flush() + end +end +local function flattened(t,f,depth) + if f==nil then + f={} + depth=0xFFFF + elseif tonumber(f) then + depth=f + f={} + elseif not depth then + depth=0xFFFF + end + for k,v in next,t do + if type(k)~="number" then + if depth>0 and type(v)=="table" then + flattened(v,f,depth-1) + else + f[#f+1]=v + end + end + end + for k=1,#t do + local v=t[k] + if depth>0 and type(v)=="table" then + flattened(v,f,depth-1) + else + f[#f+1]=v + end + end + return f +end +table.flattened=flattened +local function unnest(t,f) + if not f then + f={} + end + for i=1,#t do + local v=t[i] + if type(v)=="table" then + if type(v[1])=="table" then + unnest(v,f) + else + f[#f+1]=v + end + else + f[#f+1]=v + end + end + return f +end +function table.unnest(t) + return unnest(t) +end +local function are_equal(a,b,n,m) + if a and b and #a==#b then + n=n or 1 + m=m or #a + for i=n,m do + local ai,bi=a[i],b[i] + if ai==bi then + elseif type(ai)=="table" and type(bi)=="table" then + if not are_equal(ai,bi) then + return false + end + else + return false + end + end + return true + else + return false + end +end +local function identical(a,b) + for ka,va in next,a do + local vb=b[ka] + if va==vb then + elseif type(va)=="table" and type(vb)=="table" then + if not identical(va,vb) then + return false + end + else + return false + end + end + return true +end +table.identical=identical +table.are_equal=are_equal +local function sparse(old,nest,keeptables) + local new={} + for k,v in next,old do + if not (v=="" or v==false) then + if nest and type(v)=="table" then + v=sparse(v,nest) + if keeptables or next(v)~=nil then + new[k]=v + end + else + new[k]=v + end + end + end + return new +end +table.sparse=sparse +function table.compact(t) + return sparse(t,true,true) +end +function table.contains(t,v) + if t then + for i=1,#t do + if t[i]==v then + return i + end + end + end + return false +end +function table.count(t) + local n=0 + for k,v in next,t do + n=n+1 + end + return n +end +function table.swapped(t,s) + local n={} + if s then + for k,v in next,s do + n[k]=v + end + end + for k,v in next,t do + n[v]=k + end + return n +end +function table.mirrored(t) + local n={} + for k,v in next,t do + n[v]=k + n[k]=v + end + return n +end +function table.reversed(t) + if t then + local tt,tn={},#t + if tn>0 then + local ttn=0 + for i=tn,1,-1 do + ttn=ttn+1 + tt[ttn]=t[i] + end + end + return tt + end +end +function table.reverse(t) + if t then + local n=#t + for i=1,floor(n/2) do + local j=n-i+1 + t[i],t[j]=t[j],t[i] + end + return t + end +end +function table.sequenced(t,sep,simple) + if not t then + return "" + end + local n=#t + local s={} + if n>0 then + for i=1,n do + s[i]=tostring(t[i]) + end + else + n=0 + for k,v in sortedhash(t) do + if simple then + if v==true then + n=n+1 + s[n]=k + elseif v and v~="" then + n=n+1 + s[n]=k.."="..tostring(v) + end + else + n=n+1 + s[n]=k.."="..tostring(v) + end + end + end + return concat(s,sep or " | ") +end +function table.print(t,...) + if type(t)~="table" then + print(tostring(t)) + else + serialize(print,t,...) + end +end +if setinspector then + setinspector(function(v) if type(v)=="table" then serialize(print,v,"table") return true end end) +end +function table.sub(t,i,j) + return { unpack(t,i,j) } +end +function table.is_empty(t) + return not t or next(t)==nil +end +function table.has_one_entry(t) + return t and next(t,next(t))==nil +end +function table.loweredkeys(t) + local l={} + for k,v in next,t do + l[lower(k)]=v + end + return l +end +function table.unique(old) + local hash={} + local new={} + local n=0 + for i=1,#old do + local oi=old[i] + if not hash[oi] then + n=n+1 + new[n]=oi + hash[oi]=true + end + end + return new +end +function table.sorted(t,...) + sort(t,...) + return t +end +function table.values(t,s) + if t then + local values,keys,v={},{},0 + for key,value in next,t do + if not keys[value] then + v=v+1 + values[v]=value + keys[k]=key + end + end + if s then + sort(values) + end + return values + else + return {} + end +end +function table.filtered(t,pattern,sort,cmp) + if t and type(pattern)=="string" then + if sort then + local s + if cmp then + s=sortedhashkeys(t,function(a,b) return cmp(t,a,b) end) + else + s=sortedkeys(t) + end + local n=0 + local m=#s + local function kv(s) + while n<m do + n=n+1 + local k=s[n] + if find(k,pattern) then + return k,t[k] + end + end + end + return kv,s + else + local n=next(t) + local function iterator() + while n~=nil do + local k=n + n=next(t,k) + if find(k,pattern) then + return k,t[k] + end + end + end + return iterator,t + end + else + return nothing + end +end + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["l-io"] = package.loaded["l-io"] or true + +-- original size: 8643, stripped down to: 6232 + +if not modules then modules={} end modules ['l-io']={ + version=1.001, + comment="companion to luat-lib.mkiv", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +local io=io +local byte,find,gsub,format=string.byte,string.find,string.gsub,string.format +local concat=table.concat +local floor=math.floor +local type=type +if string.find(os.getenv("PATH"),";",1,true) then + io.fileseparator,io.pathseparator="\\",";" +else + io.fileseparator,io.pathseparator="/",":" +end +local function readall(f) + return f:read("*all") +end +local function readall(f) + local size=f:seek("end") + if size==0 then + return "" + elseif size<1024*1024 then + f:seek("set",0) + return f:read('*all') + else + local done=f:seek("set",0) + local step + if size<1024*1024 then + step=1024*1024 + elseif size>16*1024*1024 then + step=16*1024*1024 + else + step=floor(size/(1024*1024))*1024*1024/8 + end + local data={} + while true do + local r=f:read(step) + if not r then + return concat(data) + else + data[#data+1]=r + end + end + end +end +io.readall=readall +function io.loaddata(filename,textmode) + local f=io.open(filename,(textmode and 'r') or 'rb') + if f then + local data=readall(f) + f:close() + if #data>0 then + return data + end + end +end +function io.savedata(filename,data,joiner) + local f=io.open(filename,"wb") + if f then + if type(data)=="table" then + f:write(concat(data,joiner or "")) + elseif type(data)=="function" then + data(f) + else + f:write(data or "") + end + f:close() + io.flush() + return true + else + return false + end +end +function io.loadlines(filename,n) + local f=io.open(filename,'r') + if not f then + elseif n then + local lines={} + for i=1,n do + local line=f:read("*lines") + if line then + lines[#lines+1]=line + else + break + end + end + f:close() + lines=concat(lines,"\n") + if #lines>0 then + return lines + end + else + local line=f:read("*line") or "" + f:close() + if #line>0 then + return line + end + end +end +function io.loadchunk(filename,n) + local f=io.open(filename,'rb') + if f then + local data=f:read(n or 1024) + f:close() + if #data>0 then + return data + end + end +end +function io.exists(filename) + local f=io.open(filename) + if f==nil then + return false + else + f:close() + return true + end +end +function io.size(filename) + local f=io.open(filename) + if f==nil then + return 0 + else + local s=f:seek("end") + f:close() + return s + end +end +function io.noflines(f) + if type(f)=="string" then + local f=io.open(filename) + if f then + local n=f and io.noflines(f) or 0 + f:close() + return n + else + return 0 + end + else + local n=0 + for _ in f:lines() do + n=n+1 + end + f:seek('set',0) + return n + end +end +local nextchar={ + [ 4]=function(f) + return f:read(1,1,1,1) + end, + [ 2]=function(f) + return f:read(1,1) + end, + [ 1]=function(f) + return f:read(1) + end, + [-2]=function(f) + local a,b=f:read(1,1) + return b,a + end, + [-4]=function(f) + local a,b,c,d=f:read(1,1,1,1) + return d,c,b,a + end +} +function io.characters(f,n) + if f then + return nextchar[n or 1],f + end +end +local nextbyte={ + [4]=function(f) + local a,b,c,d=f:read(1,1,1,1) + if d then + return byte(a),byte(b),byte(c),byte(d) + end + end, + [3]=function(f) + local a,b,c=f:read(1,1,1) + if b then + return byte(a),byte(b),byte(c) + end + end, + [2]=function(f) + local a,b=f:read(1,1) + if b then + return byte(a),byte(b) + end + end, + [1]=function (f) + local a=f:read(1) + if a then + return byte(a) + end + end, + [-2]=function (f) + local a,b=f:read(1,1) + if b then + return byte(b),byte(a) + end + end, + [-3]=function(f) + local a,b,c=f:read(1,1,1) + if b then + return byte(c),byte(b),byte(a) + end + end, + [-4]=function(f) + local a,b,c,d=f:read(1,1,1,1) + if d then + return byte(d),byte(c),byte(b),byte(a) + end + end +} +function io.bytes(f,n) + if f then + return nextbyte[n or 1],f + else + return nil,nil + end +end +function io.ask(question,default,options) + while true do + io.write(question) + if options then + io.write(format(" [%s]",concat(options,"|"))) + end + if default then + io.write(format(" [%s]",default)) + end + io.write(format(" ")) + io.flush() + local answer=io.read() + answer=gsub(answer,"^%s*(.*)%s*$","%1") + if answer=="" and default then + return default + elseif not options then + return answer + else + for k=1,#options do + if options[k]==answer then + return answer + end + end + local pattern="^"..answer + for k=1,#options do + local v=options[k] + if find(v,pattern) then + return v + end + end + end + end +end +local function readnumber(f,n,m) + if m then + f:seek("set",n) + n=m + end + if n==1 then + return byte(f:read(1)) + elseif n==2 then + local a,b=byte(f:read(2),1,2) + return 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),"\000","") + return str +end + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["l-number"] = package.loaded["l-number"] or true + +-- original size: 4939, stripped down to: 2830 + +if not modules then modules={} end modules ['l-number']={ + version=1.001, + comment="companion to luat-lib.mkiv", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +local tostring,tonumber=tostring,tonumber +local format,floor,match,rep=string.format,math.floor,string.match,string.rep +local concat,insert=table.concat,table.insert +local lpegmatch=lpeg.match +number=number or {} +local number=number +if bit32 then + local btest,bor=bit32.btest,bit32.bor + function number.bit(p) + return 2^(p-1) + end + number.hasbit=btest + number.setbit=bor + function number.setbit(x,p) + return btest(x,p) and x or x+p + end + function number.clearbit(x,p) + return btest(x,p) and x-p or x + end +else + function number.bit(p) + return 2^(p-1) + end + function number.hasbit(x,p) + return x%(p+p)>=p + end + function number.setbit(x,p) + return (x%(p+p)>=p) and x or x+p + end + function number.clearbit(x,p) + return (x%(p+p)>=p) and x-p or x + end +end +if bit32 then + local bextract=bit32.extract + local t={ + "0","0","0","0","0","0","0","0", + "0","0","0","0","0","0","0","0", + "0","0","0","0","0","0","0","0", + "0","0","0","0","0","0","0","0", + } + function number.tobitstring(b,m) + local n=32 + for i=0,31 do + local v=bextract(b,i) + local k=32-i + if v==1 then + n=k + t[k]="1" + else + t[k]="0" + end + end + if m then + m=33-m*8 + if m<1 then + m=1 + end + return concat(t,"",m) + elseif n<8 then + return concat(t) + elseif n<16 then + return concat(t,"",9) + elseif n<24 then + return concat(t,"",17) + else + return concat(t,"",25) + end + end +else + function number.tobitstring(n,m) + if n>0 then + local t={} + while n>0 do + insert(t,1,n%2>0 and 1 or 0) + n=floor(n/2) + end + local nn=8-#t%8 + if nn>0 and nn<8 then + for i=1,nn do + insert(t,1,0) + end + end + if m then + m=m*8-#t + if m>0 then + insert(t,1,rep("0",m)) + end + end + return concat(t) + elseif m then + rep("00000000",m) + else + return "00000000" + end + end +end +function number.valid(str,default) + return tonumber(str) or default or nil +end +function number.toevenhex(n) + local s=format("%X",n) + if #s%2==0 then + return s + else + return "0"..s + end +end +local one=lpeg.C(1-lpeg.S('')/tonumber)^1 +function number.toset(n) + return lpegmatch(one,tostring(n)) +end +local function bits(n,i,...) + if n>0 then + local m=n%2 + local n=floor(n/2) + if m>0 then + return bits(n,i+1,i,...) + else + return bits(n,i+1,...) + end + else + return... + end +end +function number.bits(n) + return { bits(n,1) } +end + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["l-set"] = package.loaded["l-set"] or true + +-- original size: 1923, stripped down to: 1133 + +if not modules then modules={} end modules ['l-set']={ + version=1.001, + comment="companion to luat-lib.mkiv", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +set=set or {} +local nums={} +local tabs={} +local concat=table.concat +local next,type=next,type +set.create=table.tohash +function set.tonumber(t) + if next(t) then + local s="" + for k,v in next,t do + if v then + s=s.." "..k + end + end + local n=nums[s] + if not n then + n=#tabs+1 + tabs[n]=t + nums[s]=n + end + return n + else + return 0 + end +end +function set.totable(n) + if n==0 then + return {} + else + return tabs[n] or {} + end +end +function set.tolist(n) + if n==0 or not tabs[n] then + return "" + else + local t,n={},0 + for k,v in next,tabs[n] do + if v then + n=n+1 + t[n]=k + end + end + return concat(t," ") + end +end +function set.contains(n,s) + if type(n)=="table" then + return n[s] + elseif n==0 then + return false + else + local t=tabs[n] + return t and t[s] + end +end + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["l-os"] = package.loaded["l-os"] or true + +-- original size: 15832, stripped down to: 9456 + +if not modules then modules={} end modules ['l-os']={ + version=1.001, + comment="companion to luat-lib.mkiv", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +local os=os +local date,time=os.date,os.time +local find,format,gsub,upper,gmatch=string.find,string.format,string.gsub,string.upper,string.gmatch +local concat=table.concat +local random,ceil,randomseed=math.random,math.ceil,math.randomseed +local rawget,rawset,type,getmetatable,setmetatable,tonumber,tostring=rawget,rawset,type,getmetatable,setmetatable,tonumber,tostring +math.initialseed=tonumber(string.sub(string.reverse(tostring(ceil(socket and socket.gettime()*10000 or time()))),1,6)) +randomseed(math.initialseed) +if not os.__getenv__ then + os.__getenv__=os.getenv + os.__setenv__=os.setenv + if os.env then + local osgetenv=os.getenv + local ossetenv=os.setenv + local osenv=os.env local _=osenv.PATH + function os.setenv(k,v) + if v==nil then + v="" + end + local K=upper(k) + osenv[K]=v + if type(v)=="table" then + v=concat(v,";") + end + ossetenv(K,v) + end + function os.getenv(k) + local K=upper(k) + local v=osenv[K] or osenv[k] or osgetenv(K) or osgetenv(k) + if v=="" then + return nil + else + return v + end + end + else + local ossetenv=os.setenv + local osgetenv=os.getenv + local osenv={} + function os.setenv(k,v) + if v==nil then + v="" + end + local K=upper(k) + osenv[K]=v + end + function os.getenv(k) + local K=upper(k) + local v=osenv[K] or osgetenv(K) or osgetenv(k) + if v=="" then + return nil + else + return v + end + end + local function __index(t,k) + return os.getenv(k) + end + local function __newindex(t,k,v) + os.setenv(k,v) + end + os.env={} + setmetatable(os.env,{ __index=__index,__newindex=__newindex } ) + end +end +local execute=os.execute +local iopopen=io.popen +function os.resultof(command) + local handle=iopopen(command,"r") + if handle then + local result=handle:read("*all") or "" + handle:close() + return result + else + return "" + end +end +if not io.fileseparator then + if find(os.getenv("PATH"),";",1,true) then + io.fileseparator,io.pathseparator,os.type="\\",";",os.type or "mswin" + else + io.fileseparator,io.pathseparator,os.type="/",":",os.type or "unix" + end +end +os.type=os.type or (io.pathseparator==";" and "windows") or "unix" +os.name=os.name or (os.type=="windows" and "mswin" ) or "linux" +if os.type=="windows" then + os.libsuffix,os.binsuffix,os.binsuffixes='dll','exe',{ 'exe','cmd','bat' } +else + os.libsuffix,os.binsuffix,os.binsuffixes='so','',{ '' } +end +local launchers={ + windows="start %s", + macosx="open %s", + unix="$BROWSER %s &> /dev/null &", +} +function os.launch(str) + execute(format(launchers[os.name] or launchers.unix,str)) +end +if not os.times then + function os.times() + return { + utime=os.gettimeofday(), + stime=0, + cutime=0, + cstime=0, + } + end +end +local gettimeofday=os.gettimeofday or os.clock +os.gettimeofday=gettimeofday +local startuptime=gettimeofday() +function os.runtime() + return gettimeofday()-startuptime +end +local resolvers=os.resolvers or {} +os.resolvers=resolvers +setmetatable(os,{ __index=function(t,k) + local r=resolvers[k] + return r and r(t,k) or nil +end }) +local name,platform=os.name or "linux",os.getenv("MTX_PLATFORM") or "" +local function guess() + local architecture=os.resultof("uname -m") or "" + if architecture~="" then + return architecture + end + architecture=os.getenv("HOSTTYPE") or "" + if architecture~="" then + return architecture + end + return os.resultof("echo $HOSTTYPE") or "" +end +if platform~="" then + os.platform=platform +elseif os.type=="windows" then + function resolvers.platform(t,k) + local platform,architecture="",os.getenv("PROCESSOR_ARCHITECTURE") or "" + if find(architecture,"AMD64",1,true) then + platform="win64" + else + platform="mswin" + end + os.setenv("MTX_PLATFORM",platform) + os.platform=platform + return platform + end +elseif name=="linux" then + function resolvers.platform(t,k) + local platform,architecture="",os.getenv("HOSTTYPE") or os.resultof("uname -m") or "" + if find(architecture,"x86_64",1,true) then + platform="linux-64" + elseif find(architecture,"ppc",1,true) then + platform="linux-ppc" + else + platform="linux" + end + os.setenv("MTX_PLATFORM",platform) + os.platform=platform + return platform + end +elseif name=="macosx" then + function resolvers.platform(t,k) + local platform,architecture="",os.resultof("echo $HOSTTYPE") or "" + if architecture=="" then + platform="osx-intel" + elseif find(architecture,"i386",1,true) then + platform="osx-intel" + elseif find(architecture,"x86_64",1,true) then + platform="osx-64" + else + platform="osx-ppc" + end + os.setenv("MTX_PLATFORM",platform) + os.platform=platform + return platform + end +elseif name=="sunos" then + function resolvers.platform(t,k) + local platform,architecture="",os.resultof("uname -m") or "" + if find(architecture,"sparc",1,true) then + platform="solaris-sparc" + else + platform="solaris-intel" + end + os.setenv("MTX_PLATFORM",platform) + os.platform=platform + return platform + end +elseif name=="freebsd" then + function resolvers.platform(t,k) + local platform,architecture="",os.resultof("uname -m") or "" + if find(architecture,"amd64",1,true) then + platform="freebsd-amd64" + else + platform="freebsd" + end + os.setenv("MTX_PLATFORM",platform) + os.platform=platform + return platform + end +elseif name=="kfreebsd" then + function resolvers.platform(t,k) + local platform,architecture="",os.getenv("HOSTTYPE") or os.resultof("uname -m") or "" + if find(architecture,"x86_64",1,true) then + platform="kfreebsd-amd64" + else + platform="kfreebsd-i386" + end + os.setenv("MTX_PLATFORM",platform) + os.platform=platform + return platform + end +else + function resolvers.platform(t,k) + local platform="linux" + os.setenv("MTX_PLATFORM",platform) + os.platform=platform + return platform + end +end +os.newline=name=="windows" and "\013\010" or "\010" +function resolvers.bits(t,k) + local bits=find(os.platform,"64",1,true) and 64 or 32 + os.bits=bits + return bits +end +local t={ 8,9,"a","b" } +function os.uuid() + return format("%04x%04x-4%03x-%s%03x-%04x-%04x%04x%04x", + random(0xFFFF),random(0xFFFF), + random(0x0FFF), + t[ceil(random(4))] or 8,random(0x0FFF), + random(0xFFFF), + random(0xFFFF),random(0xFFFF),random(0xFFFF) + ) +end +local d +function os.timezone(delta) + d=d or tonumber(tonumber(date("%H")-date("!%H"))) + if delta then + if d>0 then + return format("+%02i:00",d) + else + return format("-%02i:00",-d) + end + else + return 1 + end +end +local timeformat=format("%%s%s",os.timezone(true)) +local dateformat="!%Y-%m-%d %H:%M:%S" +local lasttime=nil +local lastdate=nil +function os.fulltime(t,default) + t=t and tonumber(t) or 0 + if t>0 then + elseif default then + return default + else + t=time() + end + if t~=lasttime then + lasttime=t + lastdate=format(timeformat,date(dateformat)) + end + return lastdate +end +local dateformat="%Y-%m-%d %H:%M:%S" +local lasttime=nil +local lastdate=nil +function os.localtime(t,default) + t=t and tonumber(t) or 0 + if t>0 then + elseif default then + return default + else + t=time() + end + if t~=lasttime then + lasttime=t + lastdate=date(dateformat,t) + end + return lastdate +end +function os.converttime(t,default) + local t=tonumber(t) + if t and t>0 then + return date(dateformat,t) + else + return default or "-" + end +end +local memory={} +local function which(filename) + local fullname=memory[filename] + if fullname==nil then + local suffix=file.suffix(filename) + local suffixes=suffix=="" and os.binsuffixes or { suffix } + for directory in gmatch(os.getenv("PATH"),"[^"..io.pathseparator.."]+") do + local df=file.join(directory,filename) + for i=1,#suffixes do + local dfs=file.addsuffix(df,suffixes[i]) + if io.exists(dfs) then + fullname=dfs + break + end + end + end + if not fullname then + fullname=false + end + memory[filename]=fullname + end + return fullname +end +os.which=which +os.where=which +function os.today() + return date("!*t") +end +function os.now() + return date("!%Y-%m-%d %H:%M:%S") +end +if not os.sleep then + local socket=socket + function os.sleep(n) + if not socket then + socket=require("socket") + end + socket.sleep(n) + end +end +local function isleapyear(year) + return (year%400==0) or ((year%100~=0) and (year%4==0)) +end +os.isleapyear=isleapyear +local days={ 31,28,31,30,31,30,31,31,30,31,30,31 } +local function nofdays(year,month) + if not month then + return isleapyear(year) and 365 or 364 + else + return month==2 and isleapyear(year) and 29 or days[month] + end +end +os.nofdays=nofdays +function os.weekday(day,month,year) + return date("%w",time { year=year,month=month,day=day })+1 +end +function os.validdate(year,month,day) + if month<1 then + month=1 + elseif month>12 then + month=12 + end + if day<1 then + day=1 + else + local max=nofdays(year,month) + if day>max then + day=max + end + end + return year,month,day +end + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["l-file"] = package.loaded["l-file"] or true + +-- original size: 20949, stripped down to: 9945 + +if not modules then modules={} end modules ['l-file']={ + version=1.001, + comment="companion to luat-lib.mkiv", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +file=file or {} +local file=file +if not lfs then + lfs=optionalrequire("lfs") +end +local insert,concat=table.insert,table.concat +local match,find,gmatch=string.match,string.find,string.gmatch +local lpegmatch=lpeg.match +local getcurrentdir,attributes=lfs.currentdir,lfs.attributes +local checkedsplit=string.checkedsplit +local P,R,S,C,Cs,Cp,Cc,Ct=lpeg.P,lpeg.R,lpeg.S,lpeg.C,lpeg.Cs,lpeg.Cp,lpeg.Cc,lpeg.Ct +local tricky=S("/\\")*P(-1) +local attributes=lfs.attributes +if sandbox then + sandbox.redefine(lfs.isfile,"lfs.isfile") + sandbox.redefine(lfs.isdir,"lfs.isdir") +end +function lfs.isdir(name) + if lpegmatch(tricky,name) then + return attributes(name,"mode")=="directory" + else + return attributes(name.."/.","mode")=="directory" + end +end +function lfs.isfile(name) + return attributes(name,"mode")=="file" +end +local colon=P(":") +local period=P(".") +local periods=P("..") +local fwslash=P("/") +local bwslash=P("\\") +local slashes=S("\\/") +local noperiod=1-period +local noslashes=1-slashes +local name=noperiod^1 +local suffix=period/""*(1-period-slashes)^1*-1 +local pattern=C((1-(slashes^1*noslashes^1*-1))^1)*P(1) +local function pathpart(name,default) + return name and lpegmatch(pattern,name) or default or "" +end +local pattern=(noslashes^0*slashes)^1*C(noslashes^1)*-1 +local function basename(name) + return name and lpegmatch(pattern,name) or name +end +local pattern=(noslashes^0*slashes^1)^0*Cs((1-suffix)^1)*suffix^0 +local function nameonly(name) + return name and lpegmatch(pattern,name) or name +end +local pattern=(noslashes^0*slashes)^0*(noperiod^1*period)^1*C(noperiod^1)*-1 +local function suffixonly(name) + return name and lpegmatch(pattern,name) or "" +end +local pattern=(noslashes^0*slashes)^0*noperiod^1*((period*C(noperiod^1))^1)*-1+Cc("") +local function suffixesonly(name) + if name then + return lpegmatch(pattern,name) + else + return "" + end +end +file.pathpart=pathpart +file.basename=basename +file.nameonly=nameonly +file.suffixonly=suffixonly +file.suffix=suffixonly +file.suffixesonly=suffixesonly +file.suffixes=suffixesonly +file.dirname=pathpart +file.extname=suffixonly +local drive=C(R("az","AZ"))*colon +local path=C((noslashes^0*slashes)^0) +local suffix=period*C(P(1-period)^0*P(-1)) +local base=C((1-suffix)^0) +local rest=C(P(1)^0) +drive=drive+Cc("") +path=path+Cc("") +base=base+Cc("") +suffix=suffix+Cc("") +local pattern_a=drive*path*base*suffix +local pattern_b=path*base*suffix +local pattern_c=C(drive*path)*C(base*suffix) +local pattern_d=path*rest +function file.splitname(str,splitdrive) + if not str then + elseif splitdrive then + return lpegmatch(pattern_a,str) + else + return lpegmatch(pattern_b,str) + end +end +function file.splitbase(str) + if str then + return lpegmatch(pattern_d,str) + else + return "",str + end +end +function file.nametotable(str,splitdrive) + if str then + local path,drive,subpath,name,base,suffix=lpegmatch(pattern_c,str) + if splitdrive then + return { + path=path, + drive=drive, + subpath=subpath, + name=name, + base=base, + suffix=suffix, + } + else + return { + path=path, + name=name, + base=base, + suffix=suffix, + } + end + end +end +local pattern=Cs(((period*(1-period-slashes)^1*-1)/""+1)^1) +function file.removesuffix(name) + return name and lpegmatch(pattern,name) +end +local suffix=period/""*(1-period-slashes)^1*-1 +local pattern=Cs((noslashes^0*slashes^1)^0*((1-suffix)^1))*Cs(suffix) +function file.addsuffix(filename,suffix,criterium) + if not filename or not suffix or suffix=="" then + return filename + elseif criterium==true then + return filename.."."..suffix + elseif not criterium then + local n,s=lpegmatch(pattern,filename) + if not s or s=="" then + return filename.."."..suffix + else + return filename + end + else + local n,s=lpegmatch(pattern,filename) + if s and s~="" then + local t=type(criterium) + if t=="table" then + for i=1,#criterium do + if s==criterium[i] then + return filename + end + end + elseif t=="string" then + if s==criterium then + return filename + end + end + end + return (n or filename).."."..suffix + end +end +local suffix=period*(1-period-slashes)^1*-1 +local pattern=Cs((1-suffix)^0) +function file.replacesuffix(name,suffix) + if name and suffix and suffix~="" then + return lpegmatch(pattern,name).."."..suffix + else + return name + end +end +local reslasher=lpeg.replacer(P("\\"),"/") +function file.reslash(str) + return str and lpegmatch(reslasher,str) +end +function file.is_writable(name) + if not name then + elseif lfs.isdir(name) then + name=name.."/m_t_x_t_e_s_t.tmp" + local f=io.open(name,"wb") + if f then + f:close() + os.remove(name) + return true + end + elseif lfs.isfile(name) then + local f=io.open(name,"ab") + if f then + f:close() + return true + end + else + local f=io.open(name,"ab") + if f then + f:close() + os.remove(name) + return true + end + end + return false +end +local readable=P("r")*Cc(true) +function file.is_readable(name) + if name then + local a=attributes(name) + return a and lpegmatch(readable,a.permissions) or false + else + return false + end +end +file.isreadable=file.is_readable +file.iswritable=file.is_writable +function file.size(name) + if name then + local a=attributes(name) + return a and a.size or 0 + else + return 0 + end +end +function file.splitpath(str,separator) + return str and checkedsplit(lpegmatch(reslasher,str),separator or io.pathseparator) +end +function file.joinpath(tab,separator) + return tab and concat(tab,separator or io.pathseparator) +end +local someslash=S("\\/") +local stripper=Cs(P(fwslash)^0/""*reslasher) +local isnetwork=someslash*someslash*(1-someslash)+(1-fwslash-colon)^1*colon +local isroot=fwslash^1*-1 +local hasroot=fwslash^1 +local reslasher=lpeg.replacer(S("\\/"),"/") +local deslasher=lpeg.replacer(S("\\/")^1,"/") +function file.join(one,two,three,...) + if not two then + return one=="" and one or lpegmatch(stripper,one) + end + if one=="" then + return lpegmatch(stripper,three and concat({ two,three,... },"/") or two) + end + if lpegmatch(isnetwork,one) then + local one=lpegmatch(reslasher,one) + local two=lpegmatch(deslasher,three and concat({ two,three,... },"/") or two) + if lpegmatch(hasroot,two) then + return one..two + else + return one.."/"..two + end + elseif lpegmatch(isroot,one) then + local two=lpegmatch(deslasher,three and concat({ two,three,... },"/") or two) + if lpegmatch(hasroot,two) then + return two + else + return "/"..two + end + else + return lpegmatch(deslasher,concat({ one,two,three,... },"/")) + end +end +local drivespec=R("az","AZ")^1*colon +local anchors=fwslash+drivespec +local untouched=periods+(1-period)^1*P(-1) +local mswindrive=Cs(drivespec*(bwslash/"/"+fwslash)^0) +local mswinuncpath=(bwslash+fwslash)*(bwslash+fwslash)*Cc("//") +local splitstarter=(mswindrive+mswinuncpath+Cc(false))*Ct(lpeg.splitat(S("/\\")^1)) +local absolute=fwslash +function file.collapsepath(str,anchor) + if not str then + return + end + if anchor==true and not lpegmatch(anchors,str) then + str=getcurrentdir().."/"..str + end + if str=="" or str=="." then + return "." + elseif lpegmatch(untouched,str) then + return lpegmatch(reslasher,str) + end + local starter,oldelements=lpegmatch(splitstarter,str) + local newelements={} + local i=#oldelements + while i>0 do + local element=oldelements[i] + if element=='.' then + elseif element=='..' then + local n=i-1 + while n>0 do + local element=oldelements[n] + if element~='..' and element~='.' then + oldelements[n]='.' + break + else + n=n-1 + end + end + if n<1 then + insert(newelements,1,'..') + end + elseif element~="" then + insert(newelements,1,element) + end + i=i-1 + end + if #newelements==0 then + return starter or "." + elseif starter then + return starter..concat(newelements,'/') + elseif lpegmatch(absolute,str) then + return "/"..concat(newelements,'/') + else + newelements=concat(newelements,'/') + if anchor=="." and find(str,"^%./") then + return "./"..newelements + else + return newelements + end + end +end +local validchars=R("az","09","AZ","--","..") +local pattern_a=lpeg.replacer(1-validchars) +local pattern_a=Cs((validchars+P(1)/"-")^1) +local whatever=P("-")^0/"" +local pattern_b=Cs(whatever*(1-whatever*-1)^1) +function file.robustname(str,strict) + if str then + str=lpegmatch(pattern_a,str) or str + if strict then + return lpegmatch(pattern_b,str) or str + else + return str + end + end +end +file.readdata=io.loaddata +file.savedata=io.savedata +function file.copy(oldname,newname) + if oldname and newname then + local data=io.loaddata(oldname) + if data and data~="" then + file.savedata(newname,data) + end + end +end +local letter=R("az","AZ")+S("_-+") +local separator=P("://") +local qualified=period^0*fwslash+letter*colon+letter^1*separator+letter^1*fwslash +local rootbased=fwslash+letter*colon +lpeg.patterns.qualified=qualified +lpeg.patterns.rootbased=rootbased +function file.is_qualified_path(filename) + return filename and lpegmatch(qualified,filename)~=nil +end +function file.is_rootbased_path(filename) + return filename and lpegmatch(rootbased,filename)~=nil +end +function file.strip(name,dir) + if name then + local b,a=match(name,"^(.-)"..dir.."(.*)$") + return a~="" and a or name + end +end +function lfs.mkdirs(path) + local full="" + for sub in gmatch(path,"(/*[^\\/]+)") do + full=full..sub + lfs.mkdir(full) + end +end + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["l-gzip"] = package.loaded["l-gzip"] or true + +-- original size: 1211, stripped down to: 1002 + +if not modules then modules={} end modules ['l-gzip']={ + version=1.001, + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +if not gzip then + return +end +local suffix,suffixes=file.suffix,file.suffixes +function gzip.load(filename) + local f=io.open(filename,"rb") + if not f then + elseif suffix(filename)=="gz" then + f:close() + local g=gzip.open(filename,"rb") + if g then + local str=g:read("*all") + g:close() + return str + end + else + local str=f:read("*all") + f:close() + return str + end +end +function gzip.save(filename,data) + if suffix(filename)~="gz" then + filename=filename..".gz" + end + local f=io.open(filename,"wb") + if f then + local s=zlib.compress(data or "",9,nil,15+16) + f:write(s) + f:close() + return #s + end +end +function gzip.suffix(filename) + local suffix,extra=suffixes(filename) + local gzipped=extra=="gz" + return suffix,gzipped +end + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["l-md5"] = package.loaded["l-md5"] or true + +-- original size: 3248, stripped down to: 2266 + +if not modules then modules={} end modules ['l-md5']={ + version=1.001, + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +if not md5 then + md5=optionalrequire("md5") +end +if not md5 then + md5={ + sum=function(str) print("error: md5 is not loaded (sum ignored)") return str end, + sumhexa=function(str) print("error: md5 is not loaded (sumhexa ignored)") return str end, + } +end +local md5,file=md5,file +local gsub=string.gsub +do + local patterns=lpeg and lpeg.patterns + if patterns then + local bytestoHEX=patterns.bytestoHEX + local bytestohex=patterns.bytestohex + local bytestodec=patterns.bytestodec + local lpegmatch=lpeg.match + local md5sum=md5.sum + if not md5.HEX then function md5.HEX(str) if str then return lpegmatch(bytestoHEX,md5sum(str)) end end end + if not md5.hex then function md5.hex(str) if str then return lpegmatch(bytestohex,md5sum(str)) end end end + if not md5.dec then function md5.dec(str) if str then return lpegmatch(bytestodec,md5sum(str)) end end end + end +end +function file.needsupdating(oldname,newname,threshold) + local oldtime=lfs.attributes(oldname,"modification") + if oldtime then + local newtime=lfs.attributes(newname,"modification") + if not newtime then + return true + elseif newtime>=oldtime then + return false + elseif oldtime-newtime<(threshold or 1) then + return false + else + return true + end + else + return false + end +end +file.needs_updating=file.needsupdating +function file.syncmtimes(oldname,newname) + local oldtime=lfs.attributes(oldname,"modification") + if oldtime and lfs.isfile(newname) then + lfs.touch(newname,oldtime,oldtime) + end +end +function file.checksum(name) + if md5 then + local data=io.loaddata(name) + if data then + return md5.HEX(data) + end + end + return nil +end +function file.loadchecksum(name) + if md5 then + local data=io.loaddata(name..".md5") + return data and (gsub(data,"%s","")) + end + return nil +end +function file.savechecksum(name,checksum) + if not checksum then checksum=file.checksum(name) end + if checksum then + io.savedata(name..".md5",checksum) + return checksum + end + return nil +end + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["l-url"] = package.loaded["l-url"] or true + +-- original size: 12531, stripped down to: 5721 + +if not modules then modules={} end modules ['l-url']={ + version=1.001, + comment="companion to luat-lib.mkiv", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +local char,format,byte=string.char,string.format,string.byte +local concat=table.concat +local tonumber,type=tonumber,type +local P,C,R,S,Cs,Cc,Ct,Cf,Cg,V=lpeg.P,lpeg.C,lpeg.R,lpeg.S,lpeg.Cs,lpeg.Cc,lpeg.Ct,lpeg.Cf,lpeg.Cg,lpeg.V +local lpegmatch,lpegpatterns,replacer=lpeg.match,lpeg.patterns,lpeg.replacer +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 escapedchar=(percent*C(hexdigit*hexdigit))/tochar +local escaped=(plus/" ")+escapedchar +local noslash=P("/")/"" +local schemestr=Cs((escaped+(1-colon-slash-qmark-hash))^2) +local authoritystr=Cs((escaped+(1- slash-qmark-hash))^0) +local pathstr=Cs((escaped+(1- qmark-hash))^0) +local querystr=Cs(((1- hash))^0) +local fragmentstr=Cs((escaped+(1- endofstring))^0) +local scheme=schemestr*colon+nothing +local authority=slash*slash*authoritystr+nothing +local path=slash*pathstr+nothing +local query=qmark*querystr+nothing +local fragment=hash*fragmentstr+nothing +local validurl=scheme*authority*path*query*fragment +local parser=Ct(validurl) +lpegpatterns.url=validurl +lpegpatterns.urlsplitter=parser +local escapes={} +setmetatable(escapes,{ __index=function(t,k) + local v=format("%%%02X",byte(k)) + t[k]=v + return v +end }) +local escaper=Cs((R("09","AZ","az")^1+P(" ")/"%%20"+S("-./_")^1+P(1)/escapes)^0) +local unescaper=Cs((escapedchar+1)^0) +local getcleaner=Cs((P("+++")/"%%2B"+P("+")/"%%20"+P(1))^1) +lpegpatterns.urlunescaped=escapedchar +lpegpatterns.urlescaper=escaper +lpegpatterns.urlunescaper=unescaper +lpegpatterns.urlgetcleaner=getcleaner +function url.unescapeget(str) + return lpegmatch(getcleaner,str) +end +local function split(str) + return (type(str)=="string" and lpegmatch(parser,str)) or str +end +local isscheme=schemestr*colon*slash*slash +local function hasscheme(str) + if str then + local scheme=lpegmatch(isscheme,str) + return scheme~="" and scheme or false + else + return false + end +end +local rootletter=R("az","AZ")+S("_-+") +local separator=P("://") +local qualified=P(".")^0*P("/")+rootletter*P(":")+rootletter^1*separator+rootletter^1*P("/") +local rootbased=P("/")+rootletter*P(":") +local barswapper=replacer("|",":") +local backslashswapper=replacer("\\","/") +local equal=P("=") +local amp=P("&") +local key=Cs(((escapedchar+1)-equal )^0) +local value=Cs(((escapedchar+1)-amp -endofstring)^0) +local splitquery=Cf (Ct("")*P { "sequence", + sequence=V("pair")*(amp*V("pair"))^0, + pair=Cg(key*equal*value), +},rawset) +local function hashed(str) + if not str or str=="" then + return { + scheme="invalid", + original=str, + } + end + local detailed=split(str) + local rawscheme="" + local rawquery="" + local somescheme=false + local somequery=false + if detailed then + rawscheme=detailed[1] + rawquery=detailed[4] + somescheme=rawscheme~="" + somequery=rawquery~="" + end + if not somescheme and not somequery then + return { + scheme="file", + authority="", + path=str, + query="", + fragment="", + original=str, + noscheme=true, + filename=str, + } + end + local authority=detailed[2] + local path=detailed[3] + local filename=nil + if authority=="" then + filename=path + elseif path=="" then + filename="" + else + filename=authority.."/"..path + end + return { + scheme=rawscheme, + authority=authority, + path=path, + query=lpegmatch(unescaper,rawquery), + queries=lpegmatch(splitquery,rawquery), + fragment=detailed[5], + original=str, + noscheme=false, + filename=filename, + } +end +url.split=split +url.hasscheme=hasscheme +url.hashed=hashed +function url.addscheme(str,scheme) + if hasscheme(str) then + return str + elseif not scheme then + return "file:///"..str + else + return scheme..":///"..str + end +end +function url.construct(hash) + local 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 path and path~="" then + f=f+1;fullurl[f]="/"..path + end + 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 +local pattern=Cs(slash^-1/""*R("az","AZ")*((S(":|")/":")+P(":"))*slash*P(1)^0) +function url.filename(filename) + local spec=hashed(filename) + local path=spec.path + return (spec.scheme=="file" and path and lpegmatch(pattern,path)) or filename +end +local function escapestring(str) + return lpegmatch(escaper,str) +end +url.escape=escapestring +function url.query(str) + if type(str)=="string" then + return lpegmatch(splitquery,str) or "" + else + return str + end +end +function url.toquery(data) + local td=type(data) + if td=="string" then + return #str and escape(data) or nil + elseif td=="table" then + if next(data) then + local t={} + for k,v in next,data do + t[#t+1]=format("%s=%s",k,escapestring(v)) + end + return concat(t,"&") + end + else + end +end +local pattern=Cs(noslash^0*(1-noslash*P(-1))^0) +function url.barepath(path) + if not path or path=="" then + return "" + else + return lpegmatch(pattern,path) + end +end + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["l-dir"] = package.loaded["l-dir"] or true + +-- original size: 16765, stripped down to: 11003 + +if not modules then modules={} end modules ['l-dir']={ + version=1.001, + comment="companion to luat-lib.mkiv", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +local type,select=type,select +local find,gmatch,match,gsub,sub=string.find,string.gmatch,string.match,string.gsub,string.sub +local concat,insert,remove,unpack=table.concat,table.insert,table.remove,table.unpack +local lpegmatch=lpeg.match +local P,S,R,C,Cc,Cs,Ct,Cv,V=lpeg.P,lpeg.S,lpeg.R,lpeg.C,lpeg.Cc,lpeg.Cs,lpeg.Ct,lpeg.Cv,lpeg.V +dir=dir or {} +local dir=dir +local lfs=lfs +local attributes=lfs.attributes +local walkdir=lfs.dir +local isdir=lfs.isdir +local isfile=lfs.isfile +local currentdir=lfs.currentdir +local chdir=lfs.chdir +local mkdir=lfs.mkdir +local onwindows=os.type=="windows" or find(os.getenv("PATH"),";",1,true) +if onwindows then + local tricky=S("/\\")*P(-1) + isdir=function(name) + if lpegmatch(tricky,name) then + return attributes(name,"mode")=="directory" + else + return attributes(name.."/.","mode")=="directory" + end + end + isfile=function(name) + return attributes(name,"mode")=="file" + end + lfs.isdir=isdir + lfs.isfile=isfile +else + isdir=function(name) + return attributes(name,"mode")=="directory" + end + isfile=function(name) + return attributes(name,"mode")=="file" + end + lfs.isdir=isdir + lfs.isfile=isfile +end +function dir.current() + return (gsub(currentdir(),"\\","/")) +end +local function glob_pattern_function(path,patt,recurse,action) + if isdir(path) then + local usedpath + if path=="/" then + usedpath="/." + elseif not find(path,"/$") then + usedpath=path.."/." + path=path.."/" + else + usedpath=path + end + local dirs + for name in walkdir(usedpath) do + if name~="." and name~=".." then + local full=path..name + local mode=attributes(full,'mode') + if mode=='file' then + if not patt or find(full,patt) then + action(full) + end + elseif recurse and mode=="directory" then + if not dirs then + dirs={ full } + else + dirs[#dirs+1]=full + end + end + end + end + if dirs then + for i=1,#dirs do + glob_pattern_function(dirs[i],patt,recurse,action) + end + end + end +end +local function glob_pattern_table(path,patt,recurse,result) + if not result then + result={} + end + if isdir(path) then + local usedpath + if path=="/" then + usedpath="/." + elseif not find(path,"/$") then + usedpath=path.."/." + path=path.."/" + else + usedpath=path + end + local dirs + for name in walkdir(usedpath) do + if name~="." and name~=".." then + local full=path..name + local mode=attributes(full,'mode') + if mode=='file' then + if not patt or find(full,patt) then + result[#result+1]=full + end + elseif recurse and mode=="directory" then + if not dirs then + dirs={ full } + else + dirs[#dirs+1]=full + end + end + end + end + if dirs then + for i=1,#dirs do + glob_pattern_table(dirs[i],patt,recurse,result) + end + end + end + return result +end +local function globpattern(path,patt,recurse,method) + local kind=type(method) + if patt and sub(patt,1,-3)==path then + patt=false + end + if kind=="function" then + return glob_pattern_function(path,patt,recurse,method) + elseif kind=="table" then + return glob_pattern_table(path,patt,recurse,method) + else + return glob_pattern_table(path,patt,recurse,{}) + end +end +dir.globpattern=globpattern +local function collectpattern(path,patt,recurse,result) + local ok,scanner + result=result or {} + if path=="/" then + ok,scanner,first=xpcall(function() return walkdir(path..".") end,function() end) + else + ok,scanner,first=xpcall(function() return walkdir(path) end,function() end) + end + if ok and type(scanner)=="function" then + if not find(path,"/$") then + path=path..'/' + end + for name in scanner,first do + if name=="." then + elseif name==".." then + else + local full=path..name + local attr=attributes(full) + local mode=attr.mode + if mode=='file' then + if find(full,patt) then + result[name]=attr + end + elseif recurse and mode=="directory" then + attr.list=collectpattern(full,patt,recurse) + result[name]=attr + end + end + end + end + return result +end +dir.collectpattern=collectpattern +local separator,pattern +if onwindows then + local slash=S("/\\")/"/" + pattern={ + [1]=(Cs(P(".")+slash^1)+Cs(R("az","AZ")*P(":")*slash^0)+Cc("./"))*V(2)*V(3), + [2]=Cs(((1-S("*?/\\"))^0*slash)^0), + [3]=Cs(P(1)^0) + } +else + pattern={ + [1]=(C(P(".")+P("/")^1)+Cc("./"))*V(2)*V(3), + [2]=C(((1-S("*?/"))^0*P("/"))^0), + [3]=C(P(1)^0) + } +end +local filter=Cs (( + P("**")/".*"+P("*")/"[^/]*"+P("?")/"[^/]"+P(".")/"%%."+P("+")/"%%+"+P("-")/"%%-"+P(1) +)^0 ) +local function glob(str,t) + if type(t)=="function" then + if type(str)=="table" then + for s=1,#str do + glob(str[s],t) + end + elseif isfile(str) then + t(str) + else + local root,path,base=lpegmatch(pattern,str) + if root and path and base then + local recurse=find(base,"**",1,true) + local start=root..path + local result=lpegmatch(filter,start..base) + globpattern(start,result,recurse,t) + end + end + else + if type(str)=="table" then + local t=t or {} + for s=1,#str do + glob(str[s],t) + end + return t + elseif isfile(str) then + if t then + t[#t+1]=str + return t + else + return { str } + end + else + local root,path,base=lpegmatch(pattern,str) + if root and path and base then + local recurse=find(base,"**",1,true) + local start=root..path + local result=lpegmatch(filter,start..base) + return globpattern(start,result,recurse,t) + else + return {} + end + end + end +end +dir.glob=glob +local function globfiles(path,recurse,func,files) + if type(func)=="string" then + local s=func + func=function(name) return find(name,s) end + end + files=files or {} + local noffiles=#files + for name in walkdir(path) do + if find(name,"^%.") then + else + local mode=attributes(name,'mode') + if mode=="directory" then + if recurse then + globfiles(path.."/"..name,recurse,func,files) + end + elseif mode=="file" then + if not func or func(name) then + noffiles=noffiles+1 + files[noffiles]=path.."/"..name + end + end + end + end + return files +end +dir.globfiles=globfiles +function dir.ls(pattern) + return concat(glob(pattern),"\n") +end +local make_indeed=true +if onwindows then + function dir.mkdirs(...) + local n=select("#",...) + local str + if n==1 then + str=select(1,...) + if isdir(str) then + return str,true + end + else + str="" + for i=1,n do + local s=select(i,...) + if s=="" then + elseif str=="" then + str=s + else + str=str.."/"..s + end + end + end + local pth="" + local drive=false + local first,middle,last=match(str,"^(//)(//*)(.*)$") + if first then + else + first,last=match(str,"^(//)/*(.-)$") + if first then + middle,last=match(str,"([^/]+)/+(.-)$") + if middle then + pth="//"..middle + else + pth="//"..last + last="" + end + else + first,middle,last=match(str,"^([a-zA-Z]:)(/*)(.-)$") + if first then + pth,drive=first..middle,true + else + middle,last=match(str,"^(/*)(.-)$") + if not middle then + last=str + end + end + end + end + for s in gmatch(last,"[^/]+") do + if pth=="" then + pth=s + elseif drive then + pth,drive=pth..s,false + else + pth=pth.."/"..s + end + if make_indeed and not isdir(pth) then + mkdir(pth) + end + end + return pth,(isdir(pth)==true) + end +else + function dir.mkdirs(...) + local n=select("#",...) + local str,pth + if n==1 then + str=select(1,...) + if isdir(str) then + return str,true + end + else + str="" + for i=1,n do + local s=select(i,...) + if s and s~="" then + if str~="" then + str=str.."/"..s + else + str=s + end + end + end + end + str=gsub(str,"/+","/") + if find(str,"^/") then + pth="/" + for s in gmatch(str,"[^/]+") do + local first=(pth=="/") + if first then + pth=pth..s + else + pth=pth.."/"..s + end + if make_indeed and not first and not isdir(pth) then + mkdir(pth) + end + end + else + pth="." + for s in gmatch(str,"[^/]+") do + pth=pth.."/"..s + if make_indeed and not isdir(pth) then + mkdir(pth) + end + end + end + return pth,(isdir(pth)==true) + end +end +dir.makedirs=dir.mkdirs +do + local chdir=sandbox and sandbox.original(chdir) or chdir + if onwindows then + local xcurrentdir=dir.current + function dir.expandname(str) + local first,nothing,last=match(str,"^(//)(//*)(.*)$") + if first then + first=xcurrentdir().."/" + end + if not first then + first,last=match(str,"^(//)/*(.*)$") + end + if not first then + first,last=match(str,"^([a-zA-Z]:)(.*)$") + if first and not find(last,"^/") then + local d=currentdir() + if chdir(first) then + first=xcurrentdir() + end + chdir(d) + end + end + if not first then + first,last=xcurrentdir(),str + end + last=gsub(last,"//","/") + last=gsub(last,"/%./","/") + last=gsub(last,"^/*","") + first=gsub(first,"/*$","") + if last=="" or last=="." then + return first + else + return first.."/"..last + end + end + else + function dir.expandname(str) + if not find(str,"^/") then + str=currentdir().."/"..str + end + str=gsub(str,"//","/") + str=gsub(str,"/%./","/") + str=gsub(str,"(.)/%.$","%1") + return str + end + end +end +file.expandname=dir.expandname +local stack={} +function dir.push(newdir) + insert(stack,currentdir()) + if newdir and newdir~="" then + chdir(newdir) + end +end +function dir.pop() + local d=remove(stack) + if d then + chdir(d) + end + return d +end +local function found(...) + for i=1,select("#",...) do + local path=select(i,...) + local kind=type(path) + if kind=="string" then + if isdir(path) then + return path + end + elseif kind=="table" then + local path=found(unpack(path)) + if path then + return path + end + end + end +end +dir.found=found + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["l-boolean"] = package.loaded["l-boolean"] or true + +-- original size: 1850, stripped down to: 1568 + +if not modules then modules={} end modules ['l-boolean']={ + version=1.001, + comment="companion to luat-lib.mkiv", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +local type,tonumber=type,tonumber +boolean=boolean or {} +local boolean=boolean +function boolean.tonumber(b) + if b then return 1 else return 0 end +end +function toboolean(str,tolerant) + if str==nil then + return false + elseif str==false then + return false + elseif str==true then + return true + elseif str=="true" then + return true + elseif str=="false" then + return false + elseif not tolerant then + return false + elseif str==0 then + return false + elseif (tonumber(str) or 0)>0 then + return true + else + return str=="yes" or str=="on" or str=="t" + end +end +string.toboolean=toboolean +function string.booleanstring(str) + if str=="0" then + return false + elseif str=="1" then + return true + elseif str=="" then + return false + elseif str=="false" then + return false + elseif str=="true" then + return true + elseif (tonumber(str) or 0)>0 then + return true + else + return str=="yes" or str=="on" or str=="t" + end +end +function string.is_boolean(str,default,strict) + if type(str)=="string" then + if str=="true" or str=="yes" or str=="on" or str=="t" or (not strict and str=="1") then + return true + elseif str=="false" or str=="no" or str=="off" or str=="f" or (not strict and str=="0") then + return false + end + end + return default +end + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["l-unicode"] = package.loaded["l-unicode"] or true + +-- original size: 37388, stripped down to: 15817 + +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" +} +utf=utf or (unicode and unicode.utf8) or {} +utf.characters=utf.characters or string.utfcharacters +utf.values=utf.values or string.utfvalues +local type=type +local char,byte,format,sub,gmatch=string.char,string.byte,string.format,string.sub,string.gmatch +local concat=table.concat +local P,C,R,Cs,Ct,Cmt,Cc,Carg,Cp=lpeg.P,lpeg.C,lpeg.R,lpeg.Cs,lpeg.Ct,lpeg.Cmt,lpeg.Cc,lpeg.Carg,lpeg.Cp +local lpegmatch=lpeg.match +local patterns=lpeg.patterns +local tabletopattern=lpeg.utfchartabletopattern +local bytepairs=string.bytepairs +local finder=lpeg.finder +local replacer=lpeg.replacer +local utfvalues=utf.values +local utfgmatch=utf.gmatch +local p_utftype=patterns.utftype +local p_utfstricttype=patterns.utfstricttype +local p_utfoffset=patterns.utfoffset +local p_utf8char=patterns.utf8character +local p_utf8byte=patterns.utf8byte +local p_utfbom=patterns.utfbom +local p_newline=patterns.newline +local p_whitespace=patterns.whitespace +if not unicode then + unicode={ utf=utf } +end +if not utf.char then + local floor,char=math.floor,string.char + function utf.char(n) + if n<0x80 then + return char(n) + elseif n<0x800 then + return char( + 0xC0+floor(n/0x40), + 0x80+(n%0x40) + ) + elseif n<0x10000 then + return char( + 0xE0+floor(n/0x1000), + 0x80+(floor(n/0x40)%0x40), + 0x80+(n%0x40) + ) + elseif n<0x200000 then + return char( + 0xF0+floor(n/0x40000), + 0x80+(floor(n/0x1000)%0x40), + 0x80+(floor(n/0x40)%0x40), + 0x80+(n%0x40) + ) + else + return "" + end + end +end +if not utf.byte then + local utf8byte=patterns.utf8byte + function utf.byte(c) + return lpegmatch(utf8byte,c) + end +end +local utfchar,utfbyte=utf.char,utf.byte +function utf.filetype(data) + return data and lpegmatch(p_utftype,data) or "unknown" +end +local toentities=Cs ( + ( + patterns.utf8one+( + patterns.utf8two+patterns.utf8three+patterns.utf8four + )/function(s) local b=utfbyte(s) if b<127 then return s else return format("&#%X;",b) end end + )^0 +) +patterns.toentities=toentities +function utf.toentities(str) + return lpegmatch(toentities,str) +end +local one=P(1) +local two=C(1)*C(1) +local four=C(R(utfchar(0xD8),utfchar(0xFF)))*C(1)*C(1)*C(1) +local pattern=P("\254\255")*Cs(( + four/function(a,b,c,d) + local ab=0xFF*byte(a)+byte(b) + local cd=0xFF*byte(c)+byte(d) + return utfchar((ab-0xD800)*0x400+(cd-0xDC00)+0x10000) + end+two/function(a,b) + return utfchar(byte(a)*256+byte(b)) + end+one + )^1 )+P("\255\254")*Cs(( + four/function(b,a,d,c) + local ab=0xFF*byte(a)+byte(b) + local cd=0xFF*byte(c)+byte(d) + return utfchar((ab-0xD800)*0x400+(cd-0xDC00)+0x10000) + end+two/function(b,a) + return utfchar(byte(a)*256+byte(b)) + end+one + )^1 ) +function string.toutf(s) + return lpegmatch(pattern,s) or s +end +local validatedutf=Cs ( + ( + patterns.utf8one+patterns.utf8two+patterns.utf8three+patterns.utf8four+P(1)/"�" + )^0 +) +patterns.validatedutf=validatedutf +function utf.is_valid(str) + return type(str)=="string" and lpegmatch(validatedutf,str) or false +end +if not utf.len then + local n,f=0,1 + local utfcharcounter=patterns.utfbom^-1*Cmt ( + Cc(1)*patterns.utf8one^1+Cc(2)*patterns.utf8two^1+Cc(3)*patterns.utf8three^1+Cc(4)*patterns.utf8four^1, + function(_,t,d) + n=n+(t-f)/d + f=t + return true + end + )^0 + function utf.len(str) + n,f=0,1 + lpegmatch(utfcharcounter,str or "") + return n + end +end +utf.length=utf.len +if not utf.sub then + local utflength=utf.length + local b,e,n,first,last=0,0,0,0,0 + local function slide_zero(s,p) + n=n+1 + if n>=last then + e=p-1 + else + return p + end + end + local function slide_one(s,p) + n=n+1 + if n==first then + b=p + end + if n>=last then + e=p-1 + else + return p + end + end + local function slide_two(s,p) + n=n+1 + if n==first then + b=p + else + return true + end + end + local pattern_zero=Cmt(p_utf8char,slide_zero)^0 + local pattern_one=Cmt(p_utf8char,slide_one )^0 + local pattern_two=Cmt(p_utf8char,slide_two )^0 + local pattern_first=C(patterns.utf8character) + function utf.sub(str,start,stop) + if not start then + return str + end + if start==0 then + start=1 + end + if not stop then + if start<0 then + local l=utflength(str) + start=l+start + else + start=start-1 + end + b,n,first=0,0,start + lpegmatch(pattern_two,str) + if n>=first then + return sub(str,b) + else + return "" + end + end + if start<0 or stop<0 then + local l=utf.length(str) + if start<0 then + start=l+start + if start<=0 then + start=1 + else + start=start+1 + end + end + if stop<0 then + stop=l+stop + if stop==0 then + stop=1 + else + stop=stop+1 + end + end + end + if start==1 and stop==1 then + return lpegmatch(pattern_first,str) or "" + elseif start>stop then + return "" + elseif start>1 then + b,e,n,first,last=0,0,0,start-1,stop + lpegmatch(pattern_one,str) + if n>=first and e==0 then + e=#str + end + return sub(str,b,e) + else + b,e,n,last=1,0,0,stop + lpegmatch(pattern_zero,str) + if e==0 then + e=#str + end + return sub(str,b,e) + end + end +end +function utf.remapper(mapping,option) + local variant=type(mapping) + if variant=="table" then + if option=="dynamic" then + local pattern=false + table.setmetatablenewindex(mapping,function(t,k,v) rawset(t,k,v) pattern=false end) + return function(str) + if not str or str=="" then + return "" + else + if not pattern then + pattern=Cs((tabletopattern(mapping)/mapping+p_utf8char)^0) + end + return lpegmatch(pattern,str) + end + end + elseif option=="pattern" then + return Cs((tabletopattern(mapping)/mapping+p_utf8char)^0) + else + local pattern=Cs((tabletopattern(mapping)/mapping+p_utf8char)^0) + return function(str) + if not str or str=="" then + return "" + else + return lpegmatch(pattern,str) + end + end,pattern + end + elseif variant=="function" then + if option=="pattern" then + return Cs((p_utf8char/mapping+p_utf8char)^0) + else + local pattern=Cs((p_utf8char/mapping+p_utf8char)^0) + return function(str) + if not str or str=="" then + return "" + else + return lpegmatch(pattern,str) + end + end,pattern + end + else + return function(str) + return str or "" + end + end +end +function utf.replacer(t) + local r=replacer(t,false,false,true) + return function(str) + return lpegmatch(r,str) + end +end +function utf.subtituter(t) + local f=finder (t) + local r=replacer(t,false,false,true) + return function(str) + local i=lpegmatch(f,str) + if not i then + return str + elseif i>#str then + return str + else + return lpegmatch(r,str) + end + end +end +local utflinesplitter=p_utfbom^-1*lpeg.tsplitat(p_newline) +local utfcharsplitter_ows=p_utfbom^-1*Ct(C(p_utf8char)^0) +local utfcharsplitter_iws=p_utfbom^-1*Ct((p_whitespace^1+C(p_utf8char))^0) +local utfcharsplitter_raw=Ct(C(p_utf8char)^0) +patterns.utflinesplitter=utflinesplitter +function utf.splitlines(str) + return lpegmatch(utflinesplitter,str or "") +end +function utf.split(str,ignorewhitespace) + if ignorewhitespace then + return lpegmatch(utfcharsplitter_iws,str or "") + else + return lpegmatch(utfcharsplitter_ows,str or "") + end +end +function utf.totable(str) + return lpegmatch(utfcharsplitter_raw,str) +end +function utf.magic(f) + local str=f:read(4) or "" + local off=lpegmatch(p_utfoffset,str) + if off<4 then + f:seek('set',off) + end + return lpegmatch(p_utftype,str) +end +local utf16_to_utf8_be,utf16_to_utf8_le +local utf32_to_utf8_be,utf32_to_utf8_le +local utf_16_be_getbom=patterns.utfbom_16_be^-1 +local utf_16_le_getbom=patterns.utfbom_16_le^-1 +local utf_32_be_getbom=patterns.utfbom_32_be^-1 +local utf_32_le_getbom=patterns.utfbom_32_le^-1 +local utf_16_be_linesplitter=utf_16_be_getbom*lpeg.tsplitat(patterns.utf_16_be_nl) +local utf_16_le_linesplitter=utf_16_le_getbom*lpeg.tsplitat(patterns.utf_16_le_nl) +local utf_32_be_linesplitter=utf_32_be_getbom*lpeg.tsplitat(patterns.utf_32_be_nl) +local utf_32_le_linesplitter=utf_32_le_getbom*lpeg.tsplitat(patterns.utf_32_le_nl) +local more=0 +local p_utf16_to_utf8_be=C(1)*C(1)/function(left,right) + local now=256*byte(left)+byte(right) + if more>0 then + now=(more-0xD800)*0x400+(now-0xDC00)+0x10000 + more=0 + return utfchar(now) + elseif now>=0xD800 and now<=0xDBFF then + more=now + return "" + else + return utfchar(now) + end +end +local p_utf16_to_utf8_le=C(1)*C(1)/function(right,left) + local now=256*byte(left)+byte(right) + if more>0 then + now=(more-0xD800)*0x400+(now-0xDC00)+0x10000 + more=0 + return utfchar(now) + elseif now>=0xD800 and now<=0xDBFF then + more=now + return "" + else + return utfchar(now) + end +end +local p_utf32_to_utf8_be=C(1)*C(1)*C(1)*C(1)/function(a,b,c,d) + return utfchar(256*256*256*byte(a)+256*256*byte(b)+256*byte(c)+byte(d)) +end +local p_utf32_to_utf8_le=C(1)*C(1)*C(1)*C(1)/function(a,b,c,d) + return utfchar(256*256*256*byte(d)+256*256*byte(c)+256*byte(b)+byte(a)) +end +p_utf16_to_utf8_be=P(true)/function() more=0 end*utf_16_be_getbom*Cs(p_utf16_to_utf8_be^0) +p_utf16_to_utf8_le=P(true)/function() more=0 end*utf_16_le_getbom*Cs(p_utf16_to_utf8_le^0) +p_utf32_to_utf8_be=P(true)/function() more=0 end*utf_32_be_getbom*Cs(p_utf32_to_utf8_be^0) +p_utf32_to_utf8_le=P(true)/function() more=0 end*utf_32_le_getbom*Cs(p_utf32_to_utf8_le^0) +patterns.utf16_to_utf8_be=p_utf16_to_utf8_be +patterns.utf16_to_utf8_le=p_utf16_to_utf8_le +patterns.utf32_to_utf8_be=p_utf32_to_utf8_be +patterns.utf32_to_utf8_le=p_utf32_to_utf8_le +utf16_to_utf8_be=function(s) + if s and s~="" then + return lpegmatch(p_utf16_to_utf8_be,s) + else + return s + end +end +local utf16_to_utf8_be_t=function(t) + if not t then + return nil + elseif type(t)=="string" then + t=lpegmatch(utf_16_be_linesplitter,t) + end + for i=1,#t do + local s=t[i] + if s~="" then + t[i]=lpegmatch(p_utf16_to_utf8_be,s) + end + end + return t +end +utf16_to_utf8_le=function(s) + if s and s~="" then + return lpegmatch(p_utf16_to_utf8_le,s) + else + return s + end +end +local utf16_to_utf8_le_t=function(t) + if not t then + return nil + elseif type(t)=="string" then + t=lpegmatch(utf_16_le_linesplitter,t) + end + for i=1,#t do + local s=t[i] + if s~="" then + t[i]=lpegmatch(p_utf16_to_utf8_le,s) + end + end + return t +end +utf32_to_utf8_be=function(s) + if s and s~="" then + return lpegmatch(p_utf32_to_utf8_be,s) + else + return s + end +end +local utf32_to_utf8_be_t=function(t) + if not t then + return nil + elseif type(t)=="string" then + t=lpegmatch(utf_32_be_linesplitter,t) + end + for i=1,#t do + local s=t[i] + if s~="" then + t[i]=lpegmatch(p_utf32_to_utf8_be,s) + end + end + return t +end +utf32_to_utf8_le=function(s) + if s and s~="" then + return lpegmatch(p_utf32_to_utf8_le,s) + else + return s + end +end +local utf32_to_utf8_le_t=function(t) + if not t then + return nil + elseif type(t)=="string" then + t=lpegmatch(utf_32_le_linesplitter,t) + end + for i=1,#t do + local s=t[i] + if s~="" then + t[i]=lpegmatch(p_utf32_to_utf8_le,s) + end + end + return t +end +utf.utf16_to_utf8_le_t=utf16_to_utf8_le_t +utf.utf16_to_utf8_be_t=utf16_to_utf8_be_t +utf.utf32_to_utf8_le_t=utf32_to_utf8_le_t +utf.utf32_to_utf8_be_t=utf32_to_utf8_be_t +utf.utf16_to_utf8_le=utf16_to_utf8_le +utf.utf16_to_utf8_be=utf16_to_utf8_be +utf.utf32_to_utf8_le=utf32_to_utf8_le +utf.utf32_to_utf8_be=utf32_to_utf8_be +function utf.utf8_to_utf8_t(t) + return type(t)=="string" and lpegmatch(utflinesplitter,t) or t +end +function utf.utf16_to_utf8_t(t,endian) + return endian and utf16_to_utf8_be_t(t) or utf16_to_utf8_le_t(t) or t +end +function utf.utf32_to_utf8_t(t,endian) + return endian and utf32_to_utf8_be_t(t) or utf32_to_utf8_le_t(t) or t +end +local function little(b) + 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(b) + 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 l_remap=Cs((p_utf8byte/little+P(1)/"")^0) +local b_remap=Cs((p_utf8byte/big+P(1)/"")^0) +local function utf8_to_utf16_be(str,nobom) + if nobom then + return lpegmatch(b_remap,str) + else + return char(254,255)..lpegmatch(b_remap,str) + end +end +local function utf8_to_utf16_le(str,nobom) + if nobom then + return lpegmatch(l_remap,str) + else + return char(255,254)..lpegmatch(l_remap,str) + end +end +utf.utf8_to_utf16_be=utf8_to_utf16_be +utf.utf8_to_utf16_le=utf8_to_utf16_le +function utf.utf8_to_utf16(str,littleendian,nobom) + if littleendian then + return utf8_to_utf16_le(str,nobom) + else + return utf8_to_utf16_be(str,nobom) + end +end +local pattern=Cs ( + (p_utf8byte/function(unicode ) return format("0x%04X",unicode) end)*(p_utf8byte*Carg(1)/function(unicode,separator) return format("%s0x%04X",separator,unicode) end)^0 +) +function utf.tocodes(str,separator) + return lpegmatch(pattern,str,1,separator or " ") +end +function utf.ustring(s) + return format("U+%05X",type(s)=="number" and s or utfbyte(s)) +end +function utf.xstring(s) + return format("0x%05X",type(s)=="number" and s or utfbyte(s)) +end +function utf.toeight(str) + if not str or str=="" then + return nil + end + local utftype=lpegmatch(p_utfstricttype,str) + if utftype=="utf-8" then + return sub(str,4) + elseif utftype=="utf-16-be" then + return utf16_to_utf8_be(str) + elseif utftype=="utf-16-le" then + return utf16_to_utf8_le(str) + else + return str + end +end +local p_nany=p_utf8char/"" +if utfgmatch then + function utf.count(str,what) + if type(what)=="string" then + local n=0 + for _ in utfgmatch(str,what) do + n=n+1 + end + return n + else + return #lpegmatch(Cs((P(what)/" "+p_nany)^0),str) + end + end +else + local cache={} + function utf.count(str,what) + if type(what)=="string" then + local p=cache[what] + if not p then + p=Cs((P(what)/" "+p_nany)^0) + cache[p]=p + end + return #lpegmatch(p,str) + else + return #lpegmatch(Cs((P(what)/" "+p_nany)^0),str) + end + end +end +if not utf.characters then + function utf.characters(str) + return gmatch(str,".[\128-\191]*") + end + string.utfcharacters=utf.characters +end +if not utf.values then + local find=string.find + local dummy=function() + end + function utf.values(str) + local n=#str + if n==0 then + return dummy + elseif n==1 then + return function() return utfbyte(str) end + else + local p=1 + return function() + local b,e=find(str,".[\128-\191]*",p) + if b then + p=e+1 + return utfbyte(sub(str,b,e)) + end + end + end + end + string.utfvalues=utf.values +end +function utf.chrlen(u) + return + (u<0x80 and 1) or + (u<0xE0 and 2) or + (u<0xF0 and 3) or + (u<0xF8 and 4) or + (u<0xFC and 5) or + (u<0xFE and 6) or 0 +end + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["l-math"] = package.loaded["l-math"] or true + +-- original size: 974, stripped down to: 890 + +if not modules then modules={} end modules ['l-math']={ + version=1.001, + comment="companion to luat-lib.mkiv", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +local floor,sin,cos,tan=math.floor,math.sin,math.cos,math.tan +if not math.ceiling then + math.ceiling=math.ceil +end +if not math.round then + 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 +end +if not math.mod then + function math.mod(n,m) return n%m end +end +local pipi=2*math.pi/360 +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 +if not math.odd then + function math.odd (n) return n%2~=0 end + function math.even(n) return n%2==0 end +end + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["util-str"] = package.loaded["util-str"] or true + +-- original size: 34503, stripped down to: 18933 + +if not modules then modules={} end modules ['util-str']={ + 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.strings=utilities.strings or {} +local strings=utilities.strings +local format,gsub,rep,sub=string.format,string.gsub,string.rep,string.sub +local load,dump=load,string.dump +local tonumber,type,tostring=tonumber,type,tostring +local unpack,concat=table.unpack,table.concat +local P,V,C,S,R,Ct,Cs,Cp,Carg,Cc=lpeg.P,lpeg.V,lpeg.C,lpeg.S,lpeg.R,lpeg.Ct,lpeg.Cs,lpeg.Cp,lpeg.Carg,lpeg.Cc +local patterns,lpegmatch=lpeg.patterns,lpeg.match +local utfchar,utfbyte=utf.char,utf.byte +local loadstripped=nil +if _LUAVERSION<5.2 then + loadstripped=function(str,shortcuts) + return load(str) + end +else + loadstripped=function(str,shortcuts) + if shortcuts then + return load(dump(load(str),true),nil,nil,shortcuts) + else + return load(dump(load(str),true)) + end + end +end +if not number then number={} end +local stripper=patterns.stripzeros +local newline=patterns.newline +local endofstring=patterns.endofstring +local whitespace=patterns.whitespace +local spacer=patterns.spacer +local spaceortab=patterns.spaceortab +local function points(n) + n=tonumber(n) + return (not n or n==0) and "0pt" or lpegmatch(stripper,format("%.5fpt",n/65536)) +end +local function basepoints(n) + n=tonumber(n) + return (not n or n==0) and "0bp" or lpegmatch(stripper,format("%.5fbp",n*(7200/7227)/65536)) +end +number.points=points +number.basepoints=basepoints +local rubish=spaceortab^0*newline +local anyrubish=spaceortab+newline +local anything=patterns.anything +local stripped=(spaceortab^1/"")*newline +local leading=rubish^0/"" +local trailing=(anyrubish^1*endofstring)/"" +local redundant=rubish^3/"\n" +local pattern=Cs(leading*(trailing+redundant+stripped+anything)^0) +function strings.collapsecrlf(str) + return lpegmatch(pattern,str) +end +local repeaters={} +function strings.newrepeater(str,offset) + offset=offset or 0 + local s=repeaters[str] + if not s then + s={} + repeaters[str]=s + end + local t=s[offset] + if t then + return t + end + t={} + setmetatable(t,{ __index=function(t,k) + if not k then + return "" + end + local n=k+offset + local s=n>0 and rep(str,n) or "" + t[k]=s + return s + end }) + s[offset]=t + return t +end +local extra,tab,start=0,0,4,0 +local nspaces=strings.newrepeater(" ") +string.nspaces=nspaces +local pattern=Carg(1)/function(t) + extra,tab,start=0,t or 7,1 + end*Cs(( + Cp()*patterns.tab/function(position) + local current=(position-start+1)+extra + local spaces=tab-(current-1)%tab + if spaces>0 then + extra=extra+spaces-1 + return nspaces[spaces] + else + return "" + end + end+newline*Cp()/function(position) + extra,start=0,position + end+patterns.anything + )^1) +function strings.tabtospace(str,tab) + return lpegmatch(pattern,str,1,tab or 7) +end +local space=spacer^0 +local nospace=space/"" +local endofline=nospace*newline +local stripend=(whitespace^1*endofstring)/"" +local normalline=(nospace*((1-space*(newline+endofstring))^1)*nospace) +local stripempty=endofline^1/"" +local normalempty=endofline^1 +local singleempty=endofline*(endofline^0/"") +local doubleempty=endofline*endofline^-1*(endofline^0/"") +local stripstart=stripempty^0 +local p_prune_normal=Cs (stripstart*(stripend+normalline+normalempty )^0 ) +local p_prune_collapse=Cs (stripstart*(stripend+normalline+doubleempty )^0 ) +local p_prune_noempty=Cs (stripstart*(stripend+normalline+singleempty )^0 ) +local p_retain_normal=Cs ((normalline+normalempty )^0 ) +local p_retain_collapse=Cs ((normalline+doubleempty )^0 ) +local p_retain_noempty=Cs ((normalline+singleempty )^0 ) +local striplinepatterns={ + ["prune"]=p_prune_normal, + ["prune and collapse"]=p_prune_collapse, + ["prune and no empty"]=p_prune_noempty, + ["retain"]=p_retain_normal, + ["retain and collapse"]=p_retain_collapse, + ["retain and no empty"]=p_retain_noempty, + ["collapse"]=patterns.collapser, +} +setmetatable(striplinepatterns,{ __index=function(t,k) return p_prune_collapse end }) +strings.striplinepatterns=striplinepatterns +function strings.striplines(str,how) + return str and lpegmatch(striplinepatterns[how],str) or str +end +strings.striplong=strings.striplines +function strings.nice(str) + str=gsub(str,"[:%-+_]+"," ") + return str +end +local n=0 +local sequenced=table.sequenced +function string.autodouble(s,sep) + if s==nil then + return '""' + end + local t=type(s) + if t=="number" then + return tostring(s) + end + if t=="table" then + return ('"'..sequenced(s,sep or ",")..'"') + end + return ('"'..tostring(s)..'"') +end +function string.autosingle(s,sep) + if s==nil then + return "''" + end + local t=type(s) + if t=="number" then + return tostring(s) + end + if t=="table" then + return ("'"..sequenced(s,sep or ",").."'") + end + return ("'"..tostring(s).."'") +end +local tracedchars={} +string.tracedchars=tracedchars +strings.tracers=tracedchars +function string.tracedchar(b) + if type(b)=="number" then + return tracedchars[b] or (utfchar(b).." (U+"..format("%05X",b)..")") + else + local c=utfbyte(b) + return tracedchars[c] or (b.." (U+"..(c and format("%05X",c) or "?????")..")") + end +end +function number.signed(i) + if i>0 then + return "+",i + else + return "-",-i + end +end +local zero=P("0")^1/"" +local plus=P("+")/"" +local minus=P("-") +local separator=S(".") +local digit=R("09") +local trailing=zero^1*#S("eE") +local exponent=(S("eE")*(plus+Cs((minus*zero^0*P(-1))/"")+minus)*zero^0*(P(-1)*Cc("0")+P(1)^1)) +local pattern_a=Cs(minus^0*digit^1*(separator/""*trailing+separator*(trailing+digit)^0)*exponent) +local pattern_b=Cs((exponent+P(1))^0) +function number.sparseexponent(f,n) + if not n then + n=f + f="%e" + end + local tn=type(n) + if tn=="string" then + local m=tonumber(n) + if m then + return lpegmatch((f=="%e" or f=="%E") and pattern_a or pattern_b,format(f,m)) + end + elseif tn=="number" then + return lpegmatch((f=="%e" or f=="%E") and pattern_a or pattern_b,format(f,n)) + end + return tostring(n) +end +local template=[[ +%s +%s +return function(%s) return %s end +]] +local preamble,environment="",{} +if _LUAVERSION<5.2 then + preamble=[[ +local lpeg=lpeg +local type=type +local tostring=tostring +local tonumber=tonumber +local format=string.format +local concat=table.concat +local signed=number.signed +local points=number.points +local basepoints= number.basepoints +local utfchar=utf.char +local utfbyte=utf.byte +local lpegmatch=lpeg.match +local nspaces=string.nspaces +local tracedchar=string.tracedchar +local autosingle=string.autosingle +local autodouble=string.autodouble +local sequenced=table.sequenced +local formattednumber=number.formatted +local sparseexponent=number.sparseexponent + ]] +else + environment={ + global=global or _G, + lpeg=lpeg, + type=type, + tostring=tostring, + tonumber=tonumber, + format=string.format, + concat=table.concat, + signed=number.signed, + points=number.points, + basepoints=number.basepoints, + utfchar=utf.char, + utfbyte=utf.byte, + lpegmatch=lpeg.match, + nspaces=string.nspaces, + tracedchar=string.tracedchar, + autosingle=string.autosingle, + autodouble=string.autodouble, + sequenced=table.sequenced, + formattednumber=number.formatted, + sparseexponent=number.sparseexponent, + } +end +local arguments={ "a1" } +setmetatable(arguments,{ __index=function(t,k) + local v=t[k-1]..",a"..k + t[k]=v + return v + end +}) +local prefix_any=C((S("+- .")+R("09"))^0) +local prefix_tab=P("{")*C((1-P("}"))^0)*P("}")+C((1-R("az","AZ","09","%%"))^0) +local format_s=function(f) + n=n+1 + if f and f~="" then + return format("format('%%%ss',a%s)",f,n) + else + return format("(a%s or '')",n) + end +end +local format_S=function(f) + n=n+1 + if f and f~="" then + return format("format('%%%ss',tostring(a%s))",f,n) + else + return format("tostring(a%s)",n) + end +end +local format_q=function() + n=n+1 + return format("(a%s and format('%%q',a%s) or '')",n,n) +end +local format_Q=function() + n=n+1 + return format("format('%%q',tostring(a%s))",n) +end +local format_i=function(f) + n=n+1 + if f and f~="" then + return format("format('%%%si',a%s)",f,n) + else + return format("format('%%i',a%s)",n) + end +end +local format_d=format_i +local format_I=function(f) + n=n+1 + return format("format('%%s%%%si',signed(a%s))",f,n) +end +local format_f=function(f) + n=n+1 + return format("format('%%%sf',a%s)",f,n) +end +local format_F=function(f) + n=n+1 + if not f or f=="" then + return format("(((a%s > -0.0000000005 and a%s < 0.0000000005) and '0') or format((a%s %% 1 == 0) and '%%i' or '%%.9f',a%s))",n,n,n,n) + else + return format("format((a%s %% 1 == 0) and '%%i' or '%%%sf',a%s)",n,f,n) + end +end +local format_g=function(f) + n=n+1 + return format("format('%%%sg',a%s)",f,n) +end +local format_G=function(f) + n=n+1 + return format("format('%%%sG',a%s)",f,n) +end +local format_e=function(f) + n=n+1 + return format("format('%%%se',a%s)",f,n) +end +local format_E=function(f) + n=n+1 + return format("format('%%%sE',a%s)",f,n) +end +local format_j=function(f) + n=n+1 + return format("sparseexponent('%%%se',a%s)",f,n) +end +local format_J=function(f) + n=n+1 + return format("sparseexponent('%%%sE',a%s)",f,n) +end +local format_x=function(f) + n=n+1 + return format("format('%%%sx',a%s)",f,n) +end +local format_X=function(f) + n=n+1 + return format("format('%%%sX',a%s)",f,n) +end +local format_o=function(f) + n=n+1 + return format("format('%%%so',a%s)",f,n) +end +local format_c=function() + n=n+1 + return format("utfchar(a%s)",n) +end +local format_C=function() + n=n+1 + return format("tracedchar(a%s)",n) +end +local format_r=function(f) + n=n+1 + return format("format('%%%s.0f',a%s)",f,n) +end +local format_h=function(f) + n=n+1 + if f=="-" then + f=sub(f,2) + return format("format('%%%sx',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n) + else + return format("format('0x%%%sx',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n) + end +end +local format_H=function(f) + n=n+1 + if f=="-" then + f=sub(f,2) + return format("format('%%%sX',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n) + else + return format("format('0x%%%sX',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n) + end +end +local format_u=function(f) + n=n+1 + if f=="-" then + f=sub(f,2) + return format("format('%%%sx',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n) + else + return format("format('u+%%%sx',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n) + end +end +local format_U=function(f) + n=n+1 + if f=="-" then + f=sub(f,2) + return format("format('%%%sX',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n) + else + return format("format('U+%%%sX',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n) + end +end +local format_p=function() + n=n+1 + return format("points(a%s)",n) +end +local format_b=function() + n=n+1 + return format("basepoints(a%s)",n) +end +local format_t=function(f) + n=n+1 + if f and f~="" then + return format("concat(a%s,%q)",n,f) + else + return format("concat(a%s)",n) + end +end +local format_T=function(f) + n=n+1 + if f and f~="" then + return format("sequenced(a%s,%q)",n,f) + else + return format("sequenced(a%s)",n) + end +end +local format_l=function() + n=n+1 + return format("(a%s and 'true' or 'false')",n) +end +local format_L=function() + n=n+1 + return format("(a%s and 'TRUE' or 'FALSE')",n) +end +local format_N=function() + n=n+1 + return format("tostring(tonumber(a%s) or a%s)",n,n) +end +local format_a=function(f) + n=n+1 + if f and f~="" then + return format("autosingle(a%s,%q)",n,f) + else + return format("autosingle(a%s)",n) + end +end +local format_A=function(f) + n=n+1 + if f and f~="" then + return format("autodouble(a%s,%q)",n,f) + else + return format("autodouble(a%s)",n) + end +end +local format_w=function(f) + n=n+1 + f=tonumber(f) + if f then + return format("nspaces[%s+a%s]",f,n) + else + return format("nspaces[a%s]",n) + end +end +local format_W=function(f) + return format("nspaces[%s]",tonumber(f) or 0) +end +local digit=patterns.digit +local period=patterns.period +local three=digit*digit*digit +local splitter=Cs ( + (((1-(three^1*period))^1+C(three))*(Carg(1)*three)^1+C((1-period)^1))*(P(1)/""*Carg(2))*C(2) +) +patterns.formattednumber=splitter +function number.formatted(n,sep1,sep2) + local s=type(s)=="string" and n or format("%0.2f",n) + if sep1==true then + return lpegmatch(splitter,s,1,".",",") + elseif sep1=="." then + return lpegmatch(splitter,s,1,sep1,sep2 or ",") + elseif sep1=="," then + return lpegmatch(splitter,s,1,sep1,sep2 or ".") + else + return lpegmatch(splitter,s,1,sep1 or ",",sep2 or ".") + end +end +local format_m=function(f) + n=n+1 + if not f or f=="" then + f="," + end + return format([[formattednumber(a%s,%q,".")]],n,f) +end +local format_M=function(f) + n=n+1 + if not f or f=="" then + f="." + end + return format([[formattednumber(a%s,%q,",")]],n,f) +end +local format_z=function(f) + n=n+(tonumber(f) or 1) + return "''" +end +local format_rest=function(s) + return format("%q",s) +end +local format_extension=function(extensions,f,name) + local extension=extensions[name] or "tostring(%s)" + local f=tonumber(f) or 1 + if f==0 then + return extension + elseif f==1 then + n=n+1 + local a="a"..n + return format(extension,a,a) + elseif f<0 then + local a="a"..(n+f+1) + return format(extension,a,a) + else + local t={} + for i=1,f do + n=n+1 + t[#t+1]="a"..n + end + return format(extension,unpack(t)) + end +end +local builder=Cs { "start", + start=( + ( + P("%")/""*( + V("!") ++V("s")+V("q")+V("i")+V("d")+V("f")+V("F")+V("g")+V("G")+V("e")+V("E")+V("x")+V("X")+V("o") ++V("c")+V("C")+V("S") ++V("Q") ++V("N") ++V("r")+V("h")+V("H")+V("u")+V("U")+V("p")+V("b")+V("t")+V("T")+V("l")+V("L")+V("I")+V("w") ++V("W") ++V("a") ++V("A") ++V("j")+V("J") ++V("m")+V("M") ++V("z") + )+V("*") + )*(P(-1)+Carg(1)) + )^0, + ["s"]=(prefix_any*P("s"))/format_s, + ["q"]=(prefix_any*P("q"))/format_q, + ["i"]=(prefix_any*P("i"))/format_i, + ["d"]=(prefix_any*P("d"))/format_d, + ["f"]=(prefix_any*P("f"))/format_f, + ["F"]=(prefix_any*P("F"))/format_F, + ["g"]=(prefix_any*P("g"))/format_g, + ["G"]=(prefix_any*P("G"))/format_G, + ["e"]=(prefix_any*P("e"))/format_e, + ["E"]=(prefix_any*P("E"))/format_E, + ["x"]=(prefix_any*P("x"))/format_x, + ["X"]=(prefix_any*P("X"))/format_X, + ["o"]=(prefix_any*P("o"))/format_o, + ["S"]=(prefix_any*P("S"))/format_S, + ["Q"]=(prefix_any*P("Q"))/format_S, + ["N"]=(prefix_any*P("N"))/format_N, + ["c"]=(prefix_any*P("c"))/format_c, + ["C"]=(prefix_any*P("C"))/format_C, + ["r"]=(prefix_any*P("r"))/format_r, + ["h"]=(prefix_any*P("h"))/format_h, + ["H"]=(prefix_any*P("H"))/format_H, + ["u"]=(prefix_any*P("u"))/format_u, + ["U"]=(prefix_any*P("U"))/format_U, + ["p"]=(prefix_any*P("p"))/format_p, + ["b"]=(prefix_any*P("b"))/format_b, + ["t"]=(prefix_tab*P("t"))/format_t, + ["T"]=(prefix_tab*P("T"))/format_T, + ["l"]=(prefix_any*P("l"))/format_l, + ["L"]=(prefix_any*P("L"))/format_L, + ["I"]=(prefix_any*P("I"))/format_I, + ["w"]=(prefix_any*P("w"))/format_w, + ["W"]=(prefix_any*P("W"))/format_W, + ["j"]=(prefix_any*P("j"))/format_j, + ["J"]=(prefix_any*P("J"))/format_J, + ["m"]=(prefix_tab*P("m"))/format_m, + ["M"]=(prefix_tab*P("M"))/format_M, + ["z"]=(prefix_any*P("z"))/format_z, + ["a"]=(prefix_any*P("a"))/format_a, + ["A"]=(prefix_any*P("A"))/format_A, + ["*"]=Cs(((1-P("%"))^1+P("%%")/"%%")^1)/format_rest, + ["?"]=Cs(((1-P("%"))^1 )^1)/format_rest, + ["!"]=Carg(2)*prefix_any*P("!")*C((1-P("!"))^1)*P("!")/format_extension, +} +local direct=Cs ( + P("%")*(S("+- .")+R("09"))^0*S("sqidfgGeExXo")*P(-1)/[[local format = string.format return function(str) return format("%0",str) end]] +) +local function make(t,str) + local f + local p + local p=lpegmatch(direct,str) + if p then + f=loadstripped(p)() + else + n=0 + p=lpegmatch(builder,str,1,t._connector_,t._extensions_) + if n>0 then + p=format(template,preamble,t._preamble_,arguments[n],p) + f=loadstripped(p,t._environment_)() + else + f=function() return str end + end + end + t[str]=f + return f +end +local function use(t,fmt,...) + return t[fmt](...) +end +strings.formatters={} +if _LUAVERSION<5.2 then + function strings.formatters.new(noconcat) + local t={ _type_="formatter",_connector_=noconcat and "," or "..",_extensions_={},_preamble_=preamble,_environment_={} } + setmetatable(t,{ __index=make,__call=use }) + return t + end +else + function strings.formatters.new(noconcat) + local e={} + for k,v in next,environment do + e[k]=v + end + local t={ _type_="formatter",_connector_=noconcat and "," or "..",_extensions_={},_preamble_="",_environment_=e } + setmetatable(t,{ __index=make,__call=use }) + return t + end +end +local formatters=strings.formatters.new() +string.formatters=formatters +string.formatter=function(str,...) return formatters[str](...) end +local function add(t,name,template,preamble) + if type(t)=="table" and t._type_=="formatter" then + t._extensions_[name]=template or "%s" + if type(preamble)=="string" then + t._preamble_=preamble.."\n"..t._preamble_ + elseif type(preamble)=="table" then + for k,v in next,preamble do + t._environment_[k]=v + end + end + end +end +strings.formatters.add=add +patterns.xmlescape=Cs((P("<")/"<"+P(">")/">"+P("&")/"&"+P('"')/"""+P(1))^0) +patterns.texescape=Cs((C(S("#$%\\{}"))/"\\%1"+P(1))^0) +patterns.luaescape=Cs(((1-S('"\n'))^1+P('"')/'\\"'+P('\n')/'\\n"')^0) +patterns.luaquoted=Cs(Cc('"')*((1-S('"\n'))^1+P('"')/'\\"'+P('\n')/'\\n"')^0*Cc('"')) +if _LUAVERSION<5.2 then + add(formatters,"xml",[[lpegmatch(xmlescape,%s)]],"local xmlescape = lpeg.patterns.xmlescape") + add(formatters,"tex",[[lpegmatch(texescape,%s)]],"local texescape = lpeg.patterns.texescape") + add(formatters,"lua",[[lpegmatch(luaescape,%s)]],"local luaescape = lpeg.patterns.luaescape") +else + add(formatters,"xml",[[lpegmatch(xmlescape,%s)]],{ xmlescape=lpeg.patterns.xmlescape }) + add(formatters,"tex",[[lpegmatch(texescape,%s)]],{ texescape=lpeg.patterns.texescape }) + add(formatters,"lua",[[lpegmatch(luaescape,%s)]],{ luaescape=lpeg.patterns.luaescape }) +end +local dquote=patterns.dquote +local equote=patterns.escaped+dquote/'\\"'+1 +local space=patterns.space +local cquote=Cc('"') +local pattern=Cs(dquote*(equote-P(-2))^0*dquote) ++Cs(cquote*(equote-space)^0*space*equote^0*cquote) +function string.optionalquoted(str) + return lpegmatch(pattern,str) or str +end +local pattern=Cs((newline/os.newline+1)^0) +function string.replacenewlines(str) + return lpegmatch(pattern,str) +end + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["util-tab"] = package.loaded["util-tab"] or true + +-- original size: 25338, stripped down to: 16247 + +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,gsub,sub=string.format,string.gmatch,string.gsub,string.sub +local concat,insert,remove,sort=table.concat,table.insert,table.remove,table.sort +local setmetatable,getmetatable,tonumber,tostring=setmetatable,getmetatable,tonumber,tostring +local type,next,rawset,tonumber,tostring,load,select=type,next,rawset,tonumber,tostring,load,select +local lpegmatch,P,Cs,Cc=lpeg.match,lpeg.P,lpeg.Cs,lpeg.Cc +local sortedkeys,sortedpairs=table.sortedkeys,table.sortedpairs +local formatters=string.formatters +local utftoeight=utf.toeight +local splitter=lpeg.tsplitat(".") +function utilities.tables.definetable(target,nofirst,nolast) + local composed,t=nil,{} + local snippets=lpegmatch(splitter,target) + for i=1,#snippets-(nolast and 1 or 0) do + local name=snippets[i] + if composed then + composed=composed.."."..name + t[#t+1]=formatters["if not %s then %s = { } end"](composed,composed) + else + composed=name + if not nofirst then + t[#t+1]=formatters["%s = %s or { }"](composed,composed) + end + end + end + if composed then + if nolast then + composed=composed.."."..snippets[#snippets] + end + return concat(t,"\n"),composed + else + return "",target + end +end +function tables.definedtable(...) + local t=_G + for i=1,select("#",...) do + local li=select(i,...) + local tl=t[li] + if not tl then + tl={} + t[li]=tl + end + t=tl + end + return t +end +function tables.accesstable(target,root) + local t=root or _G + for name in gmatch(target,"([^%.]+)") do + t=t[name] + if not t then + return + end + end + return t +end +function tables.migratetable(target,v,root) + local t=root or _G + local names=lpegmatch(splitter,target) + for i=1,#names-1 do + local name=names[i] + t[name]=t[name] or {} + t=t[name] + if not t then + return + end + end + t[names[#names]]=v +end +function tables.removevalue(t,value) + if value then + for i=1,#t do + if t[i]==value then + remove(t,i) + end + end + end +end +function tables.replacevalue(t,oldvalue,newvalue) + if oldvalue and newvalue then + for i=1,#t do + if t[i]==oldvalue then + t[i]=newvalue + 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 +local escape=Cs(Cc('"')*((P('"')/'""'+P(1))^0)*Cc('"')) +function table.tocsv(t,specification) + if t and #t>0 then + local result={} + local r={} + specification=specification or {} + local fields=specification.fields + if type(fields)~="string" then + fields=sortedkeys(t[1]) + end + local separator=specification.separator or "," + if specification.preamble==true then + for f=1,#fields do + r[f]=lpegmatch(escape,tostring(fields[f])) + end + result[1]=concat(r,separator) + end + for i=1,#t do + local ti=t[i] + for f=1,#fields do + local field=ti[fields[f]] + if type(field)=="string" then + r[f]=lpegmatch(escape,field) + else + r[f]=tostring(field) + end + end + result[#result+1]=concat(r,separator) + end + return concat(result,"\n") + else + return "" + end +end +local nspaces=utilities.strings.newrepeater(" ") +local function toxml(t,d,result,step) + for k,v in sortedpairs(t) do + local s=nspaces[d] + local tk=type(k) + local tv=type(v) + if tv=="table" then + if tk=="number" then + result[#result+1]=formatters["%s<entry n='%s'>"](s,k) + toxml(v,d+step,result,step) + result[#result+1]=formatters["%s</entry>"](s,k) + else + result[#result+1]=formatters["%s<%s>"](s,k) + toxml(v,d+step,result,step) + result[#result+1]=formatters["%s</%s>"](s,k) + end + elseif tv=="string" then + if tk=="number" then + result[#result+1]=formatters["%s<entry n='%s'>%!xml!</entry>"](s,k,v,k) + else + result[#result+1]=formatters["%s<%s>%!xml!</%s>"](s,k,v,k) + end + elseif tk=="number" then + result[#result+1]=formatters["%s<entry n='%s'>%S</entry>"](s,k,v,k) + else + result[#result+1]=formatters["%s<%s>%S</%s>"](s,k,v,k) + end + end +end +function table.toxml(t,specification) + specification=specification or {} + local name=specification.name + local noroot=name==false + local result=(specification.nobanner or noroot) and {} or { "<?xml version='1.0' standalone='yes' ?>" } + local indent=specification.indent or 0 + local spaces=specification.spaces or 1 + if noroot then + toxml(t,indent,result,spaces) + else + toxml({ [name or "data"]=t },indent,result,spaces) + end + return concat(result,"\n") +end +function tables.encapsulate(core,capsule,protect) + if type(capsule)~="table" then + protect=true + capsule={} + end + for key,value in next,core do + if capsule[key] then + print(formatters["\ninvalid %s %a in %a"]("inheritance",key,core)) + os.exit() + else + capsule[key]=value + end + end + if protect then + for key,value in next,core do + core[key]=nil + end + setmetatable(core,{ + __index=capsule, + __newindex=function(t,key,value) + if capsule[key] then + print(formatters["\ninvalid %s %a' in %a"]("overload",key,core)) + os.exit() + else + rawset(t,key,value) + end + end + } ) + end +end +local f_hashed_string=formatters["[%q]=%q,"] +local f_hashed_number=formatters["[%q]=%s,"] +local f_hashed_boolean=formatters["[%q]=%l,"] +local f_hashed_table=formatters["[%q]="] +local f_indexed_string=formatters["[%s]=%q,"] +local f_indexed_number=formatters["[%s]=%s,"] +local f_indexed_boolean=formatters["[%s]=%l,"] +local f_indexed_table=formatters["[%s]="] +local f_ordered_string=formatters["%q,"] +local f_ordered_number=formatters["%s,"] +local f_ordered_boolean=formatters["%l,"] +function table.fastserialize(t,prefix) + local r={ type(prefix)=="string" and prefix or "return" } + local m=1 + local function fastserialize(t,outer) + local n=#t + m=m+1 + r[m]="{" + if n>0 then + for i=0,n do + local v=t[i] + local tv=type(v) + if tv=="string" then + m=m+1 r[m]=f_ordered_string(v) + elseif tv=="number" then + m=m+1 r[m]=f_ordered_number(v) + elseif tv=="table" then + fastserialize(v) + elseif tv=="boolean" then + m=m+1 r[m]=f_ordered_boolean(v) + end + end + end + for k,v in next,t do + local tk=type(k) + if tk=="number" then + if k>n or k<0 then + local tv=type(v) + if tv=="string" then + m=m+1 r[m]=f_indexed_string(k,v) + elseif tv=="number" then + m=m+1 r[m]=f_indexed_number(k,v) + elseif tv=="table" then + m=m+1 r[m]=f_indexed_table(k) + fastserialize(v) + elseif tv=="boolean" then + m=m+1 r[m]=f_indexed_boolean(k,v) + end + end + else + local tv=type(v) + if tv=="string" then + m=m+1 r[m]=f_hashed_string(k,v) + elseif tv=="number" then + m=m+1 r[m]=f_hashed_number(k,v) + elseif tv=="table" then + m=m+1 r[m]=f_hashed_table(k) + fastserialize(v) + elseif tv=="boolean" then + m=m+1 r[m]=f_hashed_boolean(k,v) + end + end + end + m=m+1 + if outer then + r[m]="}" + else + r[m]="}," + end + return r + end + return concat(fastserialize(t,true)) +end +function table.deserialize(str) + if not str or str=="" then + return + end + local code=load(str) + if not code then + return + end + code=code() + if not code then + return + end + return code +end +function table.load(filename,loader) + if filename then + local t=(loader or io.loaddata)(filename) + if t and t~="" then + local t=utftoeight(t) + t=load(t) + if type(t)=="function" then + t=t() + if type(t)=="table" then + return t + end + end + end + end +end +function table.save(filename,t,n,...) + io.savedata(filename,table.serialize(t,n==nil and true or n,...)) +end +local f_key_value=formatters["%s=%q"] +local f_add_table=formatters[" {%t},\n"] +local f_return_table=formatters["return {\n%t}"] +local function slowdrop(t) + local r={} + local l={} + for i=1,#t do + local ti=t[i] + local j=0 + for k,v in next,ti do + j=j+1 + l[j]=f_key_value(k,v) + end + r[i]=f_add_table(l) + end + return f_return_table(r) +end +local function fastdrop(t) + local r={ "return {\n" } + local m=1 + for i=1,#t do + local ti=t[i] + m=m+1 r[m]=" {" + for k,v in next,ti do + m=m+1 r[m]=f_key_value(k,v) + end + m=m+1 r[m]="},\n" + end + m=m+1 + r[m]="}" + return concat(r) +end +function table.drop(t,slow) + if #t==0 then + return "return { }" + elseif slow==true then + return slowdrop(t) + else + return fastdrop(t) + end +end +function table.autokey(t,k) + local v={} + t[k]=v + return v +end +local selfmapper={ __index=function(t,k) t[k]=k return k end } +function table.twowaymapper(t) + if not t then + t={} + else + for i=0,#t do + local ti=t[i] + if ti then + local i=tostring(i) + t[i]=ti + t[ti]=i + end + end + t[""]=t[0] or "" + end + setmetatable(t,selfmapper) + return t +end +local f_start_key_idx=formatters["%w{"] +local f_start_key_num=formatters["%w[%s]={"] +local f_start_key_str=formatters["%w[%q]={"] +local f_start_key_boo=formatters["%w[%l]={"] +local f_start_key_nop=formatters["%w{"] +local f_stop=formatters["%w},"] +local f_key_num_value_num=formatters["%w[%s]=%s,"] +local f_key_str_value_num=formatters["%w[%q]=%s,"] +local f_key_boo_value_num=formatters["%w[%l]=%s,"] +local f_key_num_value_str=formatters["%w[%s]=%q,"] +local f_key_str_value_str=formatters["%w[%q]=%q,"] +local f_key_boo_value_str=formatters["%w[%l]=%q,"] +local f_key_num_value_boo=formatters["%w[%s]=%l,"] +local f_key_str_value_boo=formatters["%w[%q]=%l,"] +local f_key_boo_value_boo=formatters["%w[%l]=%l,"] +local f_key_num_value_not=formatters["%w[%s]={},"] +local f_key_str_value_not=formatters["%w[%q]={},"] +local f_key_boo_value_not=formatters["%w[%l]={},"] +local f_key_num_value_seq=formatters["%w[%s]={ %, t },"] +local f_key_str_value_seq=formatters["%w[%q]={ %, t },"] +local f_key_boo_value_seq=formatters["%w[%l]={ %, t },"] +local f_val_num=formatters["%w%s,"] +local f_val_str=formatters["%w%q,"] +local f_val_boo=formatters["%w%l,"] +local f_val_not=formatters["%w{},"] +local f_val_seq=formatters["%w{ %, t },"] +local f_table_return=formatters["return {"] +local f_table_name=formatters["%s={"] +local f_table_direct=formatters["{"] +local f_table_entry=formatters["[%q]={"] +local f_table_finish=formatters["}"] +local spaces=utilities.strings.newrepeater(" ") +local serialize=table.serialize +function table.serialize(root,name,specification) + if type(specification)=="table" then + return serialize(root,name,specification) + end + local t + local n=1 + local function simple_table(t) + local nt=#t + if nt>0 then + local n=0 + for _,v in next,t do + n=n+1 + if type(v)=="table" then + return nil + end + end + if n==nt then + local tt={} + for i=1,nt do + local v=t[i] + local tv=type(v) + if tv=="number" then + tt[i]=v + elseif tv=="string" then + tt[i]=format("%q",v) + elseif tv=="boolean" then + tt[i]=v and "true" or "false" + else + return nil + end + end + return tt + end + end + return nil + end + local function do_serialize(root,name,depth,level,indexed) + if level>0 then + n=n+1 + if indexed then + t[n]=f_start_key_idx(depth) + else + local tn=type(name) + if tn=="number" then + t[n]=f_start_key_num(depth,name) + elseif tn=="string" then + t[n]=f_start_key_str(depth,name) + elseif tn=="boolean" then + t[n]=f_start_key_boo(depth,name) + else + t[n]=f_start_key_nop(depth) + end + end + depth=depth+1 + end + if root and next(root)~=nil then + local first=nil + local last=0 + last=#root + for k=1,last do + if root[k]==nil then + last=k-1 + break + end + end + if last>0 then + first=1 + end + local sk=sortedkeys(root) + for i=1,#sk do + local k=sk[i] + local v=root[k] + local tv=type(v) + local tk=type(k) + if first and tk=="number" and k<=last and k>=first then + if tv=="number" then + n=n+1 t[n]=f_val_num(depth,v) + elseif tv=="string" then + n=n+1 t[n]=f_val_str(depth,v) + elseif tv=="table" then + if next(v)==nil then + n=n+1 t[n]=f_val_not(depth) + else + local st=simple_table(v) + if st then + n=n+1 t[n]=f_val_seq(depth,st) + else + do_serialize(v,k,depth,level+1,true) + end + end + elseif tv=="boolean" then + n=n+1 t[n]=f_val_boo(depth,v) + end + elseif tv=="number" then + if tk=="number" then + n=n+1 t[n]=f_key_num_value_num(depth,k,v) + elseif tk=="string" then + n=n+1 t[n]=f_key_str_value_num(depth,k,v) + elseif tk=="boolean" then + n=n+1 t[n]=f_key_boo_value_num(depth,k,v) + end + elseif tv=="string" then + if tk=="number" then + n=n+1 t[n]=f_key_num_value_str(depth,k,v) + elseif tk=="string" then + n=n+1 t[n]=f_key_str_value_str(depth,k,v) + elseif tk=="boolean" then + n=n+1 t[n]=f_key_boo_value_str(depth,k,v) + end + elseif tv=="table" then + if next(v)==nil then + if tk=="number" then + n=n+1 t[n]=f_key_num_value_not(depth,k) + elseif tk=="string" then + n=n+1 t[n]=f_key_str_value_not(depth,k) + elseif tk=="boolean" then + n=n+1 t[n]=f_key_boo_value_not(depth,k) + end + else + local st=simple_table(v) + if not st then + do_serialize(v,k,depth,level+1) + elseif tk=="number" then + n=n+1 t[n]=f_key_num_value_seq(depth,k,st) + elseif tk=="string" then + n=n+1 t[n]=f_key_str_value_seq(depth,k,st) + elseif tk=="boolean" then + n=n+1 t[n]=f_key_boo_value_seq(depth,k,st) + end + end + elseif tv=="boolean" then + if tk=="number" then + n=n+1 t[n]=f_key_num_value_boo(depth,k,v) + elseif tk=="string" then + n=n+1 t[n]=f_key_str_value_boo(depth,k,v) + elseif tk=="boolean" then + n=n+1 t[n]=f_key_boo_value_boo(depth,k,v) + end + end + end + end + if level>0 then + n=n+1 t[n]=f_stop(depth-1) + end + end + local tname=type(name) + if tname=="string" then + if name=="return" then + t={ f_table_return() } + else + t={ f_table_name(name) } + end + elseif tname=="number" then + t={ f_table_entry(name) } + elseif tname=="boolean" then + if name then + t={ f_table_return() } + else + t={ f_table_direct() } + end + else + t={ f_table_name("t") } + end + if root then + if getmetatable(root) then + local dummy=root._w_h_a_t_e_v_e_r_ + root._w_h_a_t_e_v_e_r_=nil + end + if next(root)~=nil then + do_serialize(root,name,1,0) + end + end + n=n+1 + t[n]=f_table_finish() + return concat(t,"\n") +end + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["util-sto"] = package.loaded["util-sto"] or true + +-- original size: 4172, stripped down to: 2953 + +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,type=setmetatable,getmetatable,type +utilities=utilities or {} +utilities.storage=utilities.storage or {} +local storage=utilities.storage +function storage.mark(t) + if not t then + print("\nfatal error: storage cannot be marked\n") + os.exit() + return + 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 + report("\nfatal error: storage has not been allocated\n") + os.exit() + return + end + return t +end +function storage.setinitializer(data,initialize) + local m=getmetatable(data) or {} + m.__index=function(data,k) + m.__index=nil + 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 +local function f_empty () return "" end +local function f_self (t,k) t[k]=k return k end +local function f_table (t,k) local v={} t[k]=v return v end +local function f_number(t,k) t[k]=0 return 0 end +local function f_ignore() end +local f_index={ + ["empty"]=f_empty, + ["self"]=f_self, + ["table"]=f_table, + ["number"]=f_number, +} +local t_index={ + ["empty"]={ __index=f_empty }, + ["self"]={ __index=f_self }, + ["table"]={ __index=f_table }, + ["number"]={ __index=f_number }, +} +function table.setmetatableindex(t,f) + if type(t)~="table" then + f,t=t,{} + end + local m=getmetatable(t) + if m then + m.__index=f_index[f] or f + else + setmetatable(t,t_index[f] or { __index=f }) + end + return t +end +local f_index={ + ["ignore"]=f_ignore, +} +local t_index={ + ["ignore"]={ __newindex=f_ignore }, +} +function table.setmetatablenewindex(t,f) + if type(t)~="table" then + f,t=t,{} + end + local m=getmetatable(t) + if m then + m.__newindex=f_index[f] or f + else + setmetatable(t,t_index[f] or { __newindex=f }) + end + return t +end +function table.setmetatablecall(t,f) + if type(t)~="table" then + f,t=t,{} + end + local m=getmetatable(t) + if m then + m.__call=f + else + setmetatable(t,{ __call=f }) + end + return t +end +function table.setmetatablekey(t,key,value) + local m=getmetatable(t) + if not m then + m={} + setmetatable(t,m) + end + m[key]=value + return t +end +function table.getmetatablekey(t,key,value) + local m=getmetatable(t) + return m and m[key] +end + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["util-prs"] = package.loaded["util-prs"] or true + +-- original size: 21780, stripped down to: 15121 + +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", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +local lpeg,table,string=lpeg,table,string +local P,R,V,S,C,Ct,Cs,Carg,Cc,Cg,Cf,Cp=lpeg.P,lpeg.R,lpeg.V,lpeg.S,lpeg.C,lpeg.Ct,lpeg.Cs,lpeg.Carg,lpeg.Cc,lpeg.Cg,lpeg.Cf,lpeg.Cp +local lpegmatch,lpegpatterns=lpeg.match,lpeg.patterns +local concat,gmatch,find=table.concat,string.gmatch,string.find +local tostring,type,next,rawset=tostring,type,next,rawset +local mod,div=math.mod,math.div +utilities=utilities or {} +local parsers=utilities.parsers or {} +utilities.parsers=parsers +local patterns=parsers.patterns or {} +parsers.patterns=patterns +local setmetatableindex=table.setmetatableindex +local sortedhash=table.sortedhash +local sortedkeys=table.sortedkeys +local tohash=table.tohash +local digit=R("09") +local space=P(' ') +local equal=P("=") +local comma=P(",") +local lbrace=P("{") +local rbrace=P("}") +local lparent=P("(") +local rparent=P(")") +local period=S(".") +local punctuation=S(".,:;") +local spacer=lpegpatterns.spacer +local whitespace=lpegpatterns.whitespace +local newline=lpegpatterns.newline +local anything=lpegpatterns.anything +local endofstring=lpegpatterns.endofstring +local nobrace=1-(lbrace+rbrace ) +local noparent=1-(lparent+rparent) +local escape,left,right=P("\\"),P('{'),P('}') +lpegpatterns.balanced=P { + [1]=((escape*(left+right))+(1-(left+right))+V(2))^0, + [2]=left*V(1)*right +} +local nestedbraces=P { lbrace*(nobrace+V(1))^0*rbrace } +local nestedparents=P { lparent*(noparent+V(1))^0*rparent } +local spaces=space^0 +local argument=Cs((lbrace/"")*((nobrace+nestedbraces)^0)*(rbrace/"")) +local content=(1-endofstring)^0 +lpegpatterns.nestedbraces=nestedbraces +lpegpatterns.nestedparents=nestedparents +lpegpatterns.nested=nestedbraces +lpegpatterns.argument=argument +lpegpatterns.content=content +local value=P(lbrace*C((nobrace+nestedbraces)^0)*rbrace)+C((nestedbraces+(1-comma))^0) +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=C((1-space-equal-comma)^1) +local pattern_b=spaces*comma^0*spaces*(key*((spaces*equal*spaces*value)+C(""))) +local hash={} +local function set(key,value) + hash[key]=value +end +local pattern_a_s=(pattern_a/set)^1 +local pattern_b_s=(pattern_b/set)^1 +local pattern_c_s=(pattern_c/set)^1 +patterns.settings_to_hash_a=pattern_a_s +patterns.settings_to_hash_b=pattern_b_s +patterns.settings_to_hash_c=pattern_c_s +function parsers.make_settings_to_hash_pattern(set,how) + if how=="strict" then + return (pattern_c/set)^1 + elseif how=="tolerant" then + return (pattern_b/set)^1 + else + return (pattern_a/set)^1 + end +end +function parsers.settings_to_hash(str,existing) + if not str or str=="" then + return {} + elseif type(str)=="table" then + if existing then + for k,v in next,str do + existing[k]=v + end + return exiting + else + return str + end + else + hash=existing or {} + lpegmatch(pattern_a_s,str) + return hash + end +end +function parsers.settings_to_hash_tolerant(str,existing) + if not str or str=="" then + return {} + elseif type(str)=="table" then + if existing then + for k,v in next,str do + existing[k]=v + end + return exiting + else + return str + end + else + hash=existing or {} + lpegmatch(pattern_b_s,str) + return hash + end +end +function parsers.settings_to_hash_strict(str,existing) + if not str or str=="" then + return nil + elseif type(str)=="table" then + if existing then + for k,v in next,str do + existing[k]=v + end + return exiting + else + return str + end + elseif str and str~="" then + hash=existing or {} + lpegmatch(pattern_c_s,str) + return next(hash) and hash + end +end +local separator=comma*space^0 +local value=P(lbrace*C((nobrace+nestedbraces)^0)*rbrace)+C((nestedbraces+(1-comma))^0) +local pattern=spaces*Ct(value*(separator*value)^0) +patterns.settings_to_array=pattern +function parsers.settings_to_array(str,strict) + if not str or str=="" then + return {} + elseif type(str)=="table" then + return str + elseif strict then + if find(str,"{",1,true) then + return lpegmatch(pattern,str) + else + return { str } + end + elseif find(str,",",1,true) then + return lpegmatch(pattern,str) + else + return { str } + end +end +local cache_a={} +local cache_b={} +function parsers.groupedsplitat(symbol,withaction) + if not symbol then + symbol="," + end + local pattern=(withaction and cache_b or cache_a)[symbol] + if not pattern then + local symbols=S(symbol) + local separator=space^0*symbols*space^0 + local value=P(lbrace*C((nobrace+nestedbraces)^0)*rbrace)+C((nestedbraces+(1-(space^0*(symbols+P(-1)))))^0) + if withaction then + local withvalue=Carg(1)*value/function(f,s) return f(s) end + pattern=spaces*withvalue*(separator*withvalue)^0 + cache_b[symbol]=pattern + else + pattern=spaces*Ct(value*(separator*value)^0) + cache_a[symbol]=pattern + end + end + return pattern +end +local pattern_a=parsers.groupedsplitat(",",false) +local pattern_b=parsers.groupedsplitat(",",true) +function parsers.stripped_settings_to_array(str) + if not str or str=="" then + return {} + else + return lpegmatch(pattern_a,str) + end +end +function parsers.process_stripped_settings(str,action) + if not str or str=="" then + return {} + else + return lpegmatch(pattern_b,str,1,action) + end +end +local function set(t,v) + t[#t+1]=v +end +local value=P(Carg(1)*value)/set +local pattern=value*(separator*value)^0*Carg(1) +function parsers.add_settings_to_array(t,str) + return lpegmatch(pattern,str,nil,t) +end +function parsers.hash_to_string(h,separator,yes,no,strict,omit) + if h then + local t,tn,s={},0,sortedkeys(h) + omit=omit and tohash(omit) + for i=1,#s do + local key=s[i] + if not omit or not omit[key] then + local value=h[key] + if type(value)=="boolean" then + if yes and no then + if value then + tn=tn+1 + t[tn]=key..'='..yes + elseif not strict then + tn=tn+1 + t[tn]=key..'='..no + end + elseif value or not strict then + tn=tn+1 + t[tn]=key..'='..tostring(value) + end + else + tn=tn+1 + t[tn]=key..'='..value + end + end + end + return concat(t,separator or ",") + else + return "" + end +end +function parsers.array_to_string(a,separator) + if a then + return concat(a,separator or ",") + else + return "" + end +end +local pattern=Cf(Ct("")*Cg(C((1-S(", "))^1)*S(", ")^0*Cc(true))^1,rawset) +function utilities.parsers.settings_to_set(str,t) + return str and lpegmatch(pattern,str) or {} +end +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 str=Cs(lpegpatterns.unquoted)+C((1-whitespace-equal)^1) +local setting=Cf(Carg(1)*(whitespace^0*Cg(str*whitespace^0*(equal*whitespace^0*str+Cc(""))))^1,rawset) +local splitter=setting^1 +function utilities.parsers.options_to_hash(str,target) + return str and lpegmatch(splitter,str,1,target or {}) or {} +end +local splitter=lpeg.tsplitat(" ") +function utilities.parsers.options_to_array(str) + return str and lpegmatch(splitter,str) or {} +end +local value=P(lbrace*C((nobrace+nestedbraces)^0)*rbrace)+C(digit^1*lparent*(noparent+nestedparents)^1*rparent)+C((nestedbraces+(1-comma))^1) +local pattern_a=spaces*Ct(value*(separator*value)^0) +local function repeater(n,str) + if not n then + return str + else + local s=lpegmatch(pattern_a,str) + if n==1 then + return unpack(s) + else + local t,tn={},0 + for i=1,n do + for j=1,#s do + tn=tn+1 + t[tn]=s[j] + end + end + return unpack(t) + end + end +end +local value=P(lbrace*C((nobrace+nestedbraces)^0)*rbrace)+(C(digit^1)/tonumber*lparent*Cs((noparent+nestedparents)^1)*rparent)/repeater+C((nestedbraces+(1-comma))^1) +local pattern_b=spaces*Ct(value*(separator*value)^0) +function parsers.settings_to_array_with_repeat(str,expand) + if expand then + return lpegmatch(pattern_b,str) or {} + else + return lpegmatch(pattern_a,str) or {} + end +end +local value=lbrace*C((nobrace+nestedbraces)^0)*rbrace +local pattern=Ct((space+value)^0) +function parsers.arguments_to_table(str) + return lpegmatch(pattern,str) +end +function parsers.getparameters(self,class,parentclass,settings) + local sc=self[class] + if not sc then + 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 + parsers.settings_to_hash(settings,sc) +end +function parsers.listitem(str) + return gmatch(str,"[^, ]+") +end +local pattern=Cs { "start", + start=V("one")+V("two")+V("three"), + rest=(Cc(",")*V("thousand"))^0*(P(".")+endofstring)*anything^0, + thousand=digit*digit*digit, + one=digit*V("rest"), + two=digit*digit*V("rest"), + three=V("thousand")*V("rest"), +} +lpegpatterns.splitthousands=pattern +function parsers.splitthousands(str) + return lpegmatch(pattern,str) or str +end +local optionalwhitespace=whitespace^0 +lpegpatterns.words=Ct((Cs((1-punctuation-whitespace)^1)+anything)^1) +lpegpatterns.sentences=Ct((optionalwhitespace*Cs((1-period)^0*period))^1) +lpegpatterns.paragraphs=Ct((optionalwhitespace*Cs((whitespace^1*endofstring/""+1-(spacer^0*newline*newline))^1))^1) +local dquote=P('"') +local equal=P('=') +local escape=P('\\') +local separator=S(' ,') +local key=C((1-equal)^1) +local value=dquote*C((1-dquote-escape*dquote)^0)*dquote +local pattern=Cf(Ct("")*(Cg(key*equal*value)*separator^0)^1,rawset)^0*P(-1) +function parsers.keq_to_hash(str) + if str and str~="" then + return lpegmatch(pattern,str) + else + return {} + end +end +local defaultspecification={ separator=",",quote='"' } +function parsers.csvsplitter(specification) + specification=specification and setmetatableindex(specification,defaultspecification) or defaultspecification + local separator=specification.separator + local quotechar=specification.quote + local separator=S(separator~="" and separator or ",") + local whatever=C((1-separator-newline)^0) + if quotechar and quotechar~="" then + local quotedata=nil + for chr in gmatch(quotechar,".") do + local quotechar=P(chr) + local quoteword=quotechar*C((1-quotechar)^0)*quotechar + if quotedata then + quotedata=quotedata+quoteword + else + quotedata=quoteword + end + end + whatever=quotedata+whatever + end + local parser=Ct((Ct(whatever*(separator*whatever)^0)*S("\n\r")^1)^0 ) + return function(data) + return lpegmatch(parser,data) + end +end +function parsers.rfc4180splitter(specification) + specification=specification and setmetatableindex(specification,defaultspecification) or defaultspecification + local separator=specification.separator + local quotechar=P(specification.quote) + local dquotechar=quotechar*quotechar +/specification.quote + local separator=S(separator~="" and separator or ",") + local escaped=quotechar*Cs((dquotechar+(1-quotechar))^0)*quotechar + local non_escaped=C((1-quotechar-newline-separator)^1) + local field=escaped+non_escaped+Cc("") + local record=Ct(field*(separator*field)^1) + local headerline=record*Cp() + local wholeblob=Ct((newline^(specification.strict and -1 or 1)*record)^0) + return function(data,getheader) + if getheader then + local header,position=lpegmatch(headerline,data) + local data=lpegmatch(wholeblob,data,position) + return data,header + else + return lpegmatch(wholeblob,data) + end + end +end +local function ranger(first,last,n,action) + if not first then + elseif last==true then + for i=first,n or first do + action(i) + end + elseif last then + for i=first,last do + action(i) + end + else + action(first) + end +end +local cardinal=lpegpatterns.cardinal/tonumber +local spacers=lpegpatterns.spacer^0 +local endofstring=lpegpatterns.endofstring +local stepper=spacers*(C(cardinal)*(spacers*S(":-")*spacers*(C(cardinal)+Cc(true) )+Cc(false) )*Carg(1)*Carg(2)/ranger*S(", ")^0 )^1 +local stepper=spacers*(C(cardinal)*(spacers*S(":-")*spacers*(C(cardinal)+(P("*")+endofstring)*Cc(true) )+Cc(false) )*Carg(1)*Carg(2)/ranger*S(", ")^0 )^1*endofstring +function parsers.stepper(str,n,action) + if type(n)=="function" then + lpegmatch(stepper,str,1,false,n or print) + else + lpegmatch(stepper,str,1,n,action or print) + end +end +local pattern_math=Cs((P("%")/"\\percent "+P("^")*Cc("{")*lpegpatterns.integer*Cc("}")+anything)^0) +local pattern_text=Cs((P("%")/"\\percent "+(P("^")/"\\high")*Cc("{")*lpegpatterns.integer*Cc("}")+anything)^0) +patterns.unittotex=pattern +function parsers.unittotex(str,textmode) + return lpegmatch(textmode and pattern_text or pattern_math,str) +end +local pattern=Cs((P("^")/"<sup>"*lpegpatterns.integer*Cc("</sup>")+anything)^0) +function parsers.unittoxml(str) + return lpegmatch(pattern,str) +end +local cache={} +local spaces=lpegpatterns.space^0 +local dummy=function() end +setmetatableindex(cache,function(t,k) + local separator=P(k) + local value=(1-separator)^0 + local pattern=spaces*C(value)*separator^0*Cp() + t[k]=pattern + return pattern +end) +local commalistiterator=cache[","] +function utilities.parsers.iterator(str,separator) + local n=#str + if n==0 then + return dummy + else + local pattern=separator and cache[separator] or commalistiterator + local p=1 + return function() + if p<=n then + local s,e=lpegmatch(pattern,str,p) + if e then + p=e + return s + end + end + end + end +end +local function initialize(t,name) + local source=t[name] + if source then + local result={} + for k,v in next,t[name] do + result[k]=v + end + return result + else + return {} + end +end +local function fetch(t,name) + return t[name] or {} +end +local function process(result,more) + for k,v in next,more do + result[k]=v + end + return result +end +local name=C((1-S(", "))^1) +local parser=(Carg(1)*name/initialize)*(S(", ")^1*(Carg(1)*name/fetch))^0 +local merge=Cf(parser,process) +function utilities.parsers.mergehashes(hash,list) + return lpegmatch(merge,list,1,hash) +end +function utilities.parsers.runtime(time) + if not time then + time=os.runtime() + end + local days=div(time,24*60*60) + time=mod(time,24*60*60) + local hours=div(time,60*60) + time=mod(time,60*60) + local minutes=div(time,60) + local seconds=mod(time,60) + return days,hours,minutes,seconds +end +local spacing=whitespace^0 +local apply=P("->") +local method=C((1-apply)^1) +local token=lbrace*C((1-rbrace)^1)*rbrace+C(anything^1) +local pattern=spacing*(method*spacing*apply+Carg(1))*spacing*token +function utilities.parsers.splitmethod(str,default) + if str then + return lpegmatch(pattern,str,1,default or false) + else + return default or false,"" + end +end + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["util-fmt"] = package.loaded["util-fmt"] or true + +-- original size: 2274, stripped down to: 1781 + +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 lpegmatch=lpeg.match +local stripper=lpeg.patterns.stripzeros +function formatters.stripzeros(str) + return lpegmatch(stripper,str) +end +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 + end + return result +end + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["trac-set"] = package.loaded["trac-set"] or true + +-- original size: 12482, stripped down to: 8864 + +if not modules then modules={} end modules ['trac-set']={ + version=1.001, + comment="companion to luat-lib.mkiv", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +local type,next,tostring=type,next,tostring +local concat=table.concat +local format,find,lower,gsub,topattern=string.format,string.find,string.lower,string.gsub,string.topattern +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 +local setters=utilities.setters or {} +utilities.setters=setters +local data={} +local trace_initialize=false +function setters.initialize(filename,name,values) + local setter=data[name] + if setter then + frozen=true + local data=setter.data + if data then + for key,newvalue in next,values do + local newvalue=is_boolean(newvalue,newvalue,true) + local functions=data[key] + if functions then + local oldvalue=functions.value + if functions.frozen then + if trace_initialize then + setter.report("%s: %a is %s to %a",filename,key,"frozen",oldvalue) + end + elseif #functions>0 and not oldvalue then + if trace_initialize then + setter.report("%s: %a is %s to %a",filename,key,"set",newvalue) + end + for i=1,#functions do + functions[i](newvalue) + end + functions.value=newvalue + functions.frozen=functions.frozen or frozen + else + if trace_initialize then + setter.report("%s: %a is %s as %a",filename,key,"kept",oldvalue) + end + end + else + functions={ default=newvalue,frozen=frozen } + data[key]=functions + if trace_initialize then + setter.report("%s: %a is %s to %a",filename,key,"defaulted",newvalue) + end + end + end + return true + end + end +end +local function set(t,what,newvalue) + local data=t.data + if not data.frozen then + local done=t.done + if type(what)=="string" then + what=settings_to_hash(what) + end + if type(what)~="table" then + return + end + if not done then + done={} + t.done=done + end + for w,value in next,what do + if value=="" then + value=newvalue + elseif not value then + value=false + else + value=is_boolean(value,value,true) + end + w=topattern(w,true,true) + for name,functions in next,data do + if done[name] then + elseif find(name,w) then + done[name]=true + for i=1,#functions do + functions[i](value) + end + functions.value=value + end + end + end + end +end +local function reset(t) + local data=t.data + if not data.frozen then + for name,functions in next,data do + for i=1,#functions do + functions[i](false) + end + functions.value=false + end + end +end +local function enable(t,what) + set(t,what,true) +end +local function disable(t,what) + local data=t.data + if not what or what=="" then + t.done={} + reset(t) + else + set(t,what,false) + end +end +function setters.register(t,what,...) + local data=t.data + what=lower(what) + local functions=data[what] + if not functions then + functions={} + data[what]=functions + if trace_initialize then + t.report("defining %a",what) + end + end + local default=functions.default + for i=1,select("#",...) do + local fnc=select(i,...) + local typ=type(fnc) + if typ=="string" then + if trace_initialize then + t.report("coupling %a to %a",what,fnc) + end + local s=fnc + fnc=function(value) set(t,s,value) end + elseif typ~="function" then + fnc=nil + end + if fnc then + functions[#functions+1]=fnc + local value=functions.value or default + if value~=nil then + fnc(value) + functions.value=value + end + end + end + return false +end +function setters.enable(t,what) + local e=t.enable + t.enable,t.done=enable,{} + 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,what) + t.disable,t.done=e,{} +end +function setters.reset(t) + t.done={} + reset(t) +end +function setters.list(t) + local list=table.sortedkeys(t.data) + local user,system={},{} + for l=1,#list do + local what=list[l] + if find(what,"^%*") then + system[#system+1]=what + else + user[#user+1]=what + end + end + return user,system +end +function setters.show(t) + local category=t.name + local list=setters.list(t) + t.report() + for k=1,#list do + 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("%-50s modules: %2i default: %-12s value: %-12s",name,modules,default,value) + end + end + t.report() +end +local enable,disable,register,list,show=setters.enable,setters.disable,setters.register,setters.list,setters.show +function setters.report(setter,...) + print(format("%-15s : %s\n",setter.name,format(...))) +end +local function default(setter,name) + local d=setter.data[name] + return d and d.default +end +local function value(setter,name) + local d=setter.data[name] + return d and (d.value or d.default) +end +function setters.new(name) + local setter + setter={ + data=allocate(), + name=name, + report=function(...) setters.report (setter,...) end, + enable=function(...) enable (setter,...) end, + disable=function(...) disable (setter,...) end, + reset=function(...) reset (setter,...) end, + register=function(...) register(setter,...) end, + list=function(...) list (setter,...) end, + show=function(...) show (setter,...) end, + default=function(...) return default (setter,...) end, + value=function(...) return value (setter,...) end, + } + data[name]=setter + return setter +end +trackers=setters.new("trackers") +directives=setters.new("directives") +experiments=setters.new("experiments") +local t_enable,t_disable=trackers .enable,trackers .disable +local d_enable,d_disable=directives .enable,directives .disable +local e_enable,e_disable=experiments.enable,experiments.disable +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) +function directives.enable(...) + if trace_directives then + directives.report("enabling: % t",{...}) + end + d_enable(...) +end +function directives.disable(...) + if trace_directives then + directives.report("disabling: % t",{...}) + end + d_disable(...) +end +function experiments.enable(...) + if trace_experiments then + experiments.report("enabling: % t",{...}) + end + e_enable(...) +end +function experiments.disable(...) + if trace_experiments then + experiments.report("disabling: % t",{...}) + end + e_disable(...) +end +directives.register("system.nostatistics",function(v) + if statistics then + statistics.enable=not v + else + end +end) +directives.register("system.nolibraries",function(v) + if libraries then + libraries=nil + else + end +end) +if environment then + local engineflags=environment.engineflags + if engineflags then + local list=engineflags["c:trackers"] or engineflags["trackers"] + if type(list)=="string" then + setters.initialize("commandline flags","trackers",settings_to_hash(list)) + end + local list=engineflags["c:directives"] or engineflags["directives"] + if type(list)=="string" then + setters.initialize("commandline flags","directives",settings_to_hash(list)) + end + end +end +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 + +package.loaded["trac-log"] = package.loaded["trac-log"] or true + +-- original size: 29359, stripped down to: 20483 + +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" +} +local next,type,select,print=next,type,select,print +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 topattern=string.topattern +local utfchar=utf.char +local datetime=os.date +local openfile=io.open +local setmetatableindex=table.setmetatableindex +local formatters=string.formatters +local texgetcount=tex and tex.getcount +local variant="default" +logs=logs or {} +local logs=logs +local moreinfo=[[ +More information about ConTeXt and the tools that come with it can be found at: +]].."\n"..[[ +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 +]] +formatters.add ( + formatters,"unichr", + [["U+" .. format("%%05X",%s) .. " (" .. utfchar(%s) .. ")"]] +) +formatters.add ( + formatters,"chruni", + [[utfchar(%s) .. " (U+" .. format("%%05X",%s) .. ")"]] +) +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,setlogfile,settimedlog,setprocessor,setformatters,newline +if tex and (tex.jobname or tex.formatname) then + local function useluawrites() + local texio_write_nl=texio.write_nl + local texio_write=texio.write + local io_write=io.write + write_nl=function(target,...) + if not io_write then + io_write=io.write + end + if target=="term and log" then + texio_write_nl("log",...) + texio_write_nl("term","") + io_write(...) + elseif target=="log" then + texio_write_nl("log",...) + elseif target=="term" then + texio_write_nl("term","") + io_write(...) + elseif target~="none" then + texio_write_nl("log",target,...) + texio_write_nl("term","") + io_write(target,...) + end + end + write=function(target,...) + if not io_write then + io_write=io.write + end + if target=="term and log" then + texio_write("log",...) + io_write(...) + elseif target=="log" then + texio_write("log",...) + elseif target=="term" then + io_write(...) + elseif target~="none" then + texio_write("log",target,...) + io_write(target,...) + end + end + texio.write=write + texio.write_nl=write_nl + useluawrites=ignore + end + local whereto="both" + local target=nil + local targets=nil + local formats=table.setmetatableindex("self") + local translations=table.setmetatableindex("self") + local report_yes,subreport_yes,direct_yes,subdirect_yes,status_yes + local report_nop,subreport_nop,direct_nop,subdirect_nop,status_nop + local variants={ + default={ + formats={ + report_yes=formatters["%-15s > %s\n"], + report_nop=formatters["%-15s >\n"], + direct_yes=formatters["%-15s > %s"], + direct_nop=formatters["%-15s >"], + subreport_yes=formatters["%-15s > %s > %s\n"], + subreport_nop=formatters["%-15s > %s >\n"], + subdirect_yes=formatters["%-15s > %s > %s"], + subdirect_nop=formatters["%-15s > %s >"], + status_yes=formatters["%-15s : %s\n"], + status_nop=formatters["%-15s :\n"], + }, + targets={ + logfile="log", + log="log", + file="log", + console="term", + terminal="term", + both="term and log", + }, + }, + ansi={ + formats={ + report_yes=formatters["[0;33m%-15s [0;1m>[0m %s\n"], + report_nop=formatters["[0;33m%-15s [0;1m>[0m\n"], + direct_yes=formatters["[0;33m%-15s [0;1m>[0m %s"], + direct_nop=formatters["[0;33m%-15s [0;1m>[0m"], + subreport_yes=formatters["[0;33m%-15s [0;1m>[0;35m %s [0;1m>[0m %s\n"], + subreport_nop=formatters["[0;33m%-15s [0;1m>[0;35m %s [0;1m>[0m\n"], + subdirect_yes=formatters["[0;33m%-15s [0;1m>[0;35m %s [0;1m>[0m %s"], + subdirect_nop=formatters["[0;33m%-15s [0;1m>[0;35m %s [0;1m>[0m"], + status_yes=formatters["[0;33m%-15s [0;1m:[0m %s\n"], + status_nop=formatters["[0;33m%-15s [0;1m:[0m\n"], + }, + targets={ + logfile="none", + log="none", + file="none", + console="term", + terminal="term", + both="term", + }, + } + } + logs.flush=io.flush + writer=function(...) + write_nl(target,...) + end + newline=function() + write_nl(target,"\n") + end + report=function(a,b,c,...) + if c then + write_nl(target,report_yes(translations[a],formatters[formats[b]](c,...))) + elseif b then + write_nl(target,report_yes(translations[a],formats[b])) + elseif a then + write_nl(target,report_nop(translations[a])) + else + write_nl(target,"\n") + end + end + direct=function(a,b,c,...) + if c then + return direct_yes(translations[a],formatters[formats[b]](c,...)) + elseif b then + return direct_yes(translations[a],formats[b]) + elseif a then + return direct_nop(translations[a]) + else + return "" + end + end + subreport=function(a,s,b,c,...) + if c then + write_nl(target,subreport_yes(translations[a],translations[s],formatters[formats[b]](c,...))) + elseif b then + write_nl(target,subreport_yes(translations[a],translations[s],formats[b])) + elseif a then + write_nl(target,subreport_nop(translations[a],translations[s])) + else + write_nl(target,"\n") + end + end + subdirect=function(a,s,b,c,...) + if c then + return subdirect_yes(translations[a],translations[s],formatters[formats[b]](c,...)) + elseif b then + return subdirect_yes(translations[a],translations[s],formats[b]) + elseif a then + return subdirect_nop(translations[a],translations[s]) + else + return "" + end + end + status=function(a,b,c,...) + if c then + write_nl(target,status_yes(translations[a],formatters[formats[b]](c,...))) + elseif b then + write_nl(target,status_yes(translations[a],formats[b])) + elseif a then + write_nl(target,status_nop(translations[a])) + else + write_nl(target,"\n") + end + end + settarget=function(askedwhereto) + whereto=askedwhereto or whereto or "both" + target=targets[whereto] + if not target then + whereto="both" + target=targets[whereto] + end + 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 + setprocessor=function(f) + local writeline=write_nl + write_nl=function(target,...) + writeline(target,f(...)) + end + end + setformatters=function(specification) + local t=nil + local f=nil + local d=variants.default + if not specification then + elseif type(specification)=="table" then + t=specification.targets + f=specification.formats or specification + else + local v=variants[specification] + if v then + t=v.targets + f=v.formats + variant=specification + end + end + targets=t or d.targets + target=targets[whereto] or target + if f then + d=d.formats + else + f=d.formats + d=f + end + setmetatableindex(f,d) + report_yes=f.report_yes + report_nop=f.report_nop + subreport_yes=f.subreport_yes + subreport_nop=f.subreport_nop + direct_yes=f.direct_yes + direct_nop=f.direct_nop + subdirect_yes=f.subdirect_yes + subdirect_nop=f.subdirect_nop + status_yes=f.status_yes + status_nop=f.status_nop + if variant=="ansi" then + useluawrites() + end + settarget(whereto) + end + setformatters(variant) + setlogfile=ignore + settimedlog=ignore +else + local report_yes,subreport_yes,status_yes + local report_nop,subreport_nop,status_nop + local variants={ + default={ + formats={ + report_yes=formatters["%-15s | %s"], + report_nop=formatters["%-15s |"], + subreport_yes=formatters["%-15s | %s | %s"], + subreport_nop=formatters["%-15s | %s |"], + status_yes=formatters["%-15s : %s\n"], + status_nop=formatters["%-15s :\n"], + }, + }, + ansi={ + formats={ + report_yes=formatters["[0;32m%-15s [0;1m|[0m %s"], + report_nop=formatters["[0;32m%-15s [0;1m|[0m"], + subreport_yes=formatters["[0;32m%-15s [0;1m|[0;31m %s [0;1m|[0m %s"], + subreport_nop=formatters["[0;32m%-15s [0;1m|[0;31m %s [0;1m|[0m"], + status_yes=formatters["[0;32m%-15s [0;1m:[0m %s\n"], + status_nop=formatters["[0;32m%-15s [0;1m:[0m\n"], + }, + }, + } + logs.flush=ignore + writer=function(s) + write_nl(s) + end + newline=function() + write_nl("\n") + end + report=function(a,b,c,...) + if c then + write_nl(report_yes(a,formatters[b](c,...))) + elseif b then + write_nl(report_yes(a,b)) + elseif a then + write_nl(report_nop(a)) + else + write_nl("") + end + end + subreport=function(a,sub,b,c,...) + if c then + write_nl(subreport_yes(a,sub,formatters[b](c,...))) + elseif b then + write_nl(subreport_yes(a,sub,b)) + elseif a then + write_nl(subreport_nop(a,sub)) + else + write_nl("") + end + end + status=function(a,b,c,...) + if c then + write_nl(status_yes(a,formatters[b](c,...))) + elseif b then + write_nl(status_yes(a,b)) + elseif a then + write_nl(status_nop(a)) + else + write_nl("\n") + end + end + direct=ignore + subdirect=ignore + settarget=ignore + pushtarget=ignore + poptarget=ignore + setformats=ignore + settranslations=ignore + setprocessor=function(f) + local writeline=write_nl + write_nl=function(s) + writeline(f(s)) + end + end + setformatters=function(specification) + local f=nil + local d=variants.default + if specification then + if type(specification)=="table" then + f=specification.formats or specification + else + local v=variants[specification] + if v then + f=v.formats + end + end + end + if f then + d=d.formats + else + f=d.formats + d=f + end + setmetatableindex(f,d) + report_yes=f.report_yes + report_nop=f.report_nop + subreport_yes=f.subreport_yes + subreport_nop=f.subreport_nop + status_yes=f.status_yes + status_nop=f.status_nop + end + setformatters(variant) + setlogfile=function(name,keepopen) + if name and name~="" then + local localtime=os.localtime + local writeline=write_nl + if keepopen then + local f=io.open(name,"ab") + write_nl=function(s) + writeline(s) + f:write(localtime()," | ",s,"\n") + end + else + write_nl=function(s) + writeline(s) + local f=io.open(name,"ab") + f:write(localtime()," | ",s,"\n") + f:close() + end + end + end + setlogfile=ignore + end + settimedlog=function() + local localtime=os.localtime + local writeline=write_nl + write_nl=function(s) + writeline(localtime().." | "..s) + end + settimedlog=ignore + end +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.setlogfile=setlogfile +logs.settimedlog=settimedlog +logs.setprocessor=setprocessor +logs.setformatters=setformatters +logs.direct=direct +logs.subdirect=subdirect +logs.writer=writer +logs.newline=newline +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 +local ctxreport=logs.writer +function logs.setmessenger(m) + ctxreport=m +end +function logs.messenger(category,subcategory) + if subcategory then + return function(...) + ctxreport(subdirect(category,subcategory,...)) + end + else + return function(...) + ctxreport(direct(category,...)) + end + end +end +local function setblocked(category,value) + if category==true then + category,value="*",true + elseif category==false then + category,value="*",false + elseif value==nil then + 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,type(states)=="table" and states or nil) + for c,_ in next,states do + local v=data[c] + if v then + v.state=value + else + c=topattern(c,true,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 + report("logging","category %a, subcategories %a, state %a",category,subcategories,state) + end + report("logging","categories: %s, max category: %s, max subcategory: %s, max combined: %s",n,c,s,max) +end +local delayed_reporters={} +setmetatableindex(delayed_reporters,function(t,k) + local v=logs.reporter(k.name) + t[k]=v + return v +end) +function utilities.setters.report(setter,...) + delayed_reporters[setter](...) +end +directives.register("logs.blocked",function(v) + setblocked(v,true) +end) +directives.register("logs.target",function(v) + settarget(v) +end) +local report_pages=logs.reporter("pages") +local real,user,sub +function logs.start_page_number() + real=texgetcount("realpageno") + user=texgetcount("userpageno") + sub=texgetcount("subpageno") +end +local timing=false +local starttime=nil +local lasttime=nil +trackers.register("pages.timing",function(v) + starttime=os.clock() + timing=true +end) +function logs.stop_page_number() + if timing then + local elapsed,average + local stoptime=os.clock() + if not lasttime or real<2 then + elapsed=stoptime + average=stoptime + starttime=stoptime + else + elapsed=stoptime-lasttime + average=(stoptime-starttime)/(real-1) + end + lasttime=stoptime + if real<=0 then + report_pages("flushing page, time %0.04f / %0.04f",elapsed,average) + elseif user<=0 then + report_pages("flushing realpage %s, time %0.04f / %0.04f",real,elapsed,average) + elseif sub<=0 then + report_pages("flushing realpage %s, userpage %s, time %0.04f / %0.04f",real,user,elapsed,average) + else + report_pages("flushing realpage %s, userpage %s, subpage %s, time %0.04f / %0.04f",real,user,sub,elapsed,average) + end + else + if real<=0 then + report_pages("flushing page") + elseif user<=0 then + report_pages("flushing realpage %s",real) + elseif sub<=0 then + report_pages("flushing realpage %s, userpage %s",real,user) + else + report_pages("flushing realpage %s, userpage %s, subpage %s",real,user,sub) + end + end + logs.flush() +end +local report_files=logs.reporter("files") +local nesting=0 +local verbose=false +local hasscheme=url.hasscheme +function logs.show_open(name) +end +function logs.show_close(name) +end +function logs.show_load(name) +end +local simple=logs.reporter("comment") +logs.simple=simple +logs.simpleline=simple +logs.setprogram=ignore +logs.extendbanner=ignore +logs.reportlines=ignore +logs.reportbanner=ignore +logs.reportline=ignore +logs.simplelines=ignore +logs.help=ignore +local Carg,C,lpegmatch=lpeg.Carg,lpeg.C,lpeg.match +local p_newline=lpeg.patterns.newline +local linewise=( + Carg(1)*C((1-p_newline)^1)/function(t,s) t.report(s) end+Carg(1)*p_newline^2/function(t) t.report() end+p_newline +)^1 +local function reportlines(t,str) + if str then + lpegmatch(linewise,str,1,t) + end +end +local function reportbanner(t) + local banner=t.banner + if banner then + t.report(banner) + t.report() + end +end +local function reportversion(t) + local banner=t.banner + if banner then + t.report(banner) + end +end +local function reporthelp(t,...) + local helpinfo=t.helpinfo + if type(helpinfo)=="string" then + reportlines(t,helpinfo) + elseif type(helpinfo)=="table" then + for i=1,select("#",...) do + reportlines(t,t.helpinfo[select(i,...)]) + if i<n then + t.report() + end + end + end +end +local function reportinfo(t) + t.report() + reportlines(t,t.moreinfo) +end +local function reportexport(t,method) + report(t.helpinfo) +end +local reporters={ + lines=reportlines, + banner=reportbanner, + version=reportversion, + help=reporthelp, + info=reportinfo, + export=reportexport, +} +local exporters={ +} +logs.reporters=reporters +logs.exporters=exporters +function logs.application(t) + t.name=t.name or "unknown" + t.banner=t.banner + t.moreinfo=moreinfo + t.report=logs.reporter(t.name) + t.help=function(...) + reporters.banner(t) + reporters.help(t,...) + reporters.info(t) + end + t.export=function(...) + reporters.export(t,...) + end + t.identify=function() + reporters.banner(t) + end + t.version=function() + reporters.version(t) + end + return t +end +local f_syslog=formatters["%s %s => %s => %s => %s\r"] +function logs.system(whereto,process,jobname,category,fmt,arg,...) + local message=f_syslog(datetime("%d/%m/%y %H:%m:%S"),process,jobname,category,arg==nil and fmt or format(fmt,arg,...)) + for i=1,10 do + local f=openfile(whereto,"a") + 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 %a is obsolete, use %a",old,new) + loadstring(old.."="..new.." return "..old)()(...) + end + elseif type(o)=="table" then + local t,m={},{} + m.__index=function(t,k) + report_system("table %a is obsolete, use %a",old,new) + m.__index,m.__newindex=o,o + return o[k] + end + m.__newindex=function(t,k,v) + report_system("table %a is obsolete, use %a",old,new) + m.__index,m.__newindex=o,o + o[k]=v + end + if libraries then + libraries.obsolete[old]=t + 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(...) + tex.error(format(...),{}) + end +else + function logs.texerrormessage(...) + print(format(...)) + end +end +io.stdout:setvbuf('no') +io.stderr:setvbuf('no') +if package.helpers.report then + package.helpers.report=logs.reporter("package loader") +end + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["trac-inf"] = package.loaded["trac-inf"] or true + +-- original size: 6704, stripped down to: 5343 + +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" +} +local type,tonumber,select=type,tonumber,select +local format,lower,find=string.format,string.lower,string.find +local concat=table.concat +local clock=os.gettimeofday or os.clock +local setmetatableindex=table.setmetatableindex +local serialize=table.serialize +local formatters=string.formatters +statistics=statistics or {} +local statistics=statistics +statistics.enable=true +statistics.threshold=0.01 +local statusinfo,n,registered,timers={},0,{},{} +setmetatableindex(timers,function(t,k) + local v={ timing=0,loadtime=0 } + t[k]=v + return v +end) +local function hastiming(instance) + return instance and timers[instance] +end +local function resettiming(instance) + timers[instance or "notimer"]={ timing=0,loadtime=0 } +end +local function starttiming(instance) + local timer=timers[instance or "notimer"] + local it=timer.timing or 0 + if it==0 then + timer.starttime=clock() + if not timer.loadtime then + timer.loadtime=0 + end + end + timer.timing=it+1 +end +local function stoptiming(instance) + 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 + timer.timing=0 + return loadtime + end + end + return 0 +end +local function elapsed(instance) + if type(instance)=="number" then + return instance or 0 + else + local timer=timers[instance or "notimer"] + return timer and timer.loadtime or 0 + end +end +local function elapsedtime(instance) + return format("%0.3f",elapsed(instance)) +end +local function elapsedindeed(instance) + return elapsed(instance)>statistics.threshold +end +local function elapsedseconds(instance,rest) + if elapsedindeed(instance) then + return format("%0.3f seconds %s",elapsed(instance),rest or "") + end +end +statistics.hastiming=hastiming +statistics.resettiming=resettiming +statistics.starttiming=starttiming +statistics.stoptiming=stoptiming +statistics.elapsed=elapsed +statistics.elapsedtime=elapsedtime +statistics.elapsedindeed=elapsedindeed +statistics.elapsedseconds=elapsedseconds +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 +local report=logs.reporter("mkiv lua stats") +function statistics.show() + if statistics.enable then + local register=statistics.register + register("used platform",function() + return format("%s, type: %s, binary subtree: %s", + os.platform or "unknown",os.type or "unknown",environment.texos or "unknown") + end) + register("luatex banner",function() + return lower(status.banner) + end) + register("control sequences",function() + return format("%s of %s + %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) + if jit then + local jitstatus={ jit.status() } + if jitstatus[1] then + register("luajit options",concat(jitstatus," ",2)) + end + end + register("lua properties",function() + local list=status.list() + local hashchar=tonumber(list.luatex_hashchars) + local mask=lua.mask or "ascii" + return format("engine: %s, used memory: %s, hash type: %s, hash chars: min(%s,40), symbol mask: %s (%s)", + jit and "luajit" or "lua", + statistics.memused(), + list.luatex_hashtype or "default", + hashchar and 2^hashchar or "unknown", + mask, + mask=="utf" and "τεχ" or "tex") + end) + register("runtime",statistics.runtime) + logs.newline() + for i=1,#statusinfo do + local s=statusinfo[i] + local r=s[2]() + if r then + report("%s: %s",s[1],r) + end + end + statistics.enable=false + end +end +function statistics.memused() + 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) + return format("%s seconds",runtime) +end +function statistics.runtime() + stoptiming(statistics) + return statistics.formatruntime(elapsedtime(statistics)) +end +local report=logs.reporter("system") +function statistics.timed(action) + starttiming("run") + action() + stoptiming("run") + report("total runtime: %s seconds",elapsedtime("run")) +end +function statistics.tracefunction(base,tag,...) + for i=1,select("#",...) do + local name=select(i,...) + local stat={} + local func=base[name] + setmetatableindex(stat,function(t,k) t[k]=0 return 0 end) + base[name]=function(n,k,v) stat[k]=stat[k]+1 return func(n,k,v) end + statistics.register(formatters["%s.%s"](tag,name),function() return serialize(stat,"calls") end) + end +end + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["trac-pro"] = package.loaded["trac-pro"] or true + +-- original size: 5829, stripped down to: 3501 + +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 +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 %a in protected namespace %a: %s",k,name) + debugger.showtraceback(report_system) + else + report_system("reference to %a in protected namespace %a",k,name) + end +end +local function report_newindex(k,name) + if trace_namespaces then + report_system("assignment to %a in protected namespace %a: %s",k,name) + debugger.showtraceback(report_system) + else + report_system("assignment to %a in protected namespace %a",k,name) + end +end +local function register(name) + local data=name=="global" and _G or _G[name] + if not data then + return + 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) + 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 +namespaces.private=private +namespaces.protect=protect +namespaces.unprotect=unprotect +namespaces.protectall=protectall +namespaces.unprotectall=unprotectall +namespaces.private("namespaces") registered={} register("global") +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) + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["util-lua"] = package.loaded["util-lua"] or true + +-- original size: 4982, stripped down to: 3511 + +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", + comment="the strip code is written by Peter Cawley", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +local rep,sub,byte,dump,format=string.rep,string.sub,string.byte,string.dump,string.format +local load,loadfile,type=load,loadfile,type +utilities=utilities or {} +utilities.lua=utilities.lua or {} +local luautilities=utilities.lua +local report_lua=logs.reporter("system","lua") +local tracestripping=false +local forcestupidcompile=true +luautilities.stripcode=true +luautilities.alwaysstripcode=false +luautilities.nofstrippedchunks=0 +luautilities.nofstrippedbytes=0 +local strippedchunks={} +luautilities.strippedchunks=strippedchunks +luautilities.suffixes={ + tma="tma", + tmc=jit and "tmb" or "tmc", + lua="lua", + luc=jit and "lub" or "luc", + lui="lui", + luv="luv", + luj="luj", + tua="tua", + tuc="tuc", +} +local function register(name) + if tracestripping then + report_lua("stripped bytecode from %a",name or "unknown") + end + strippedchunks[#strippedchunks+1]=name + luautilities.nofstrippedchunks=luautilities.nofstrippedchunks+1 +end +local function stupidcompile(luafile,lucfile,strip) + local code=io.loaddata(luafile) + if code and code~="" then + code=load(code) + if code then + code=dump(code,strip and luautilities.stripcode or luautilities.alwaysstripcode) + if code and code~="" then + register(name) + io.savedata(lucfile,code) + return true,0 + end + else + report_lua("fatal error %a in file %a",1,luafile) + end + else + report_lua("fatal error %a in file %a",2,luafile) + end + return false,0 +end +function luautilities.loadedluacode(fullname,forcestrip,name) + name=name or fullname + local code,message + if environment.loadpreprocessedfile then + code,message=environment.loadpreprocessedfile(fullname) + else + code,message=loadfile(fullname) + end + if code then + code() + else + report_lua("loading of file %a failed:\n\t%s",fullname,message or "no message") + end + if forcestrip and luautilities.stripcode then + if type(forcestrip)=="function" then + forcestrip=forcestrip(fullname) + end + if forcestrip or luautilities.alwaysstripcode then + register(name) + return load(dump(code,true)),0 + else + return code,0 + end + elseif luautilities.alwaysstripcode then + register(name) + return load(dump(code,true)),0 + else + return code,0 + end +end +function luautilities.strippedloadstring(code,forcestrip,name) + local code,message=load(code) + if not code then + report_lua("loading of file %a failed:\n\t%s",name,message or "no message") + end + if forcestrip and luautilities.stripcode or luautilities.alwaysstripcode then + register(name) + return load(dump(code,true)),0 + else + return code,0 + end +end +function luautilities.compile(luafile,lucfile,cleanup,strip,fallback) + report_lua("compiling %a into %a",luafile,lucfile) + os.remove(lucfile) + local done=stupidcompile(luafile,lucfile,strip~=false) + if done then + report_lua("dumping %a into %a stripped",luafile,lucfile) + if cleanup==true and lfs.isfile(lucfile) and lfs.isfile(luafile) then + report_lua("removing %a",luafile) + os.remove(luafile) + end + end + return done +end +function luautilities.loadstripped(...) + local l=load(...) + if l then + return load(dump(l,true)) + end +end + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["util-deb"] = package.loaded["util-deb"] or true + +-- original size: 3898, stripped down to: 2644 + +if not modules then modules={} end modules ['util-deb']={ + 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 debug=require "debug" +local getinfo=debug.getinfo +local type,next,tostring=type,next,tostring +local format,find=string.format,string.find +local is_boolean=string.is_boolean +utilities=utilities or {} +local debugger=utilities.debugger or {} +utilities.debugger=debugger +local counters={} +local names={} +local report=logs.reporter("debugger") +local function hook() + local f=getinfo(2) + if f then + local n="unknown" + if f.what=="C" then + n=f.name or '<anonymous>' + if not names[n] then + names[n]=format("%42s",n) + end + else + n=f.name or f.namewhat or f.what + if not n or n=="" then + n="?" + end + if not names[n] then + names[n]=format("%42s : % 5i : %s",n,f.linedefined or 0,f.short_src or "unknown source") + end + end + counters[n]=(counters[n] or 0)+1 + end +end +function debugger.showstats(printer,threshold) + printer=printer or report + threshold=threshold or 0 + local total,grandtotal,functions=0,0,0 + local dataset={} + for name,count in next,counters do + dataset[#dataset+1]={ name,count } + end + table.sort(dataset,function(a,b) return a[2]==b[2] and b[1]>a[1] or a[2]>b[2] end) + for i=1,#dataset do + local d=dataset[i] + local name=d[1] + local count=d[2] + if count>threshold and not find(name,"for generator") then + printer(format("%8i %s\n",count,names[name])) + total=total+count + end + grandtotal=grandtotal+count + functions=functions+1 + end + printer("\n") + printer(format("functions : % 10i\n",functions)) + printer(format("total : % 10i\n",total)) + printer(format("grand total: % 10i\n",grandtotal)) + printer(format("threshold : % 10i\n",threshold)) +end +function debugger.savestats(filename,threshold) + local f=io.open(filename,'w') + if f then + debugger.showstats(function(str) f:write(str) end,threshold) + f:close() + end +end +function debugger.enable() + debug.sethook(hook,"c") +end +function debugger.disable() + debug.sethook() +end +local function showtraceback(rep) + local level=2 + local reporter=rep or report + while true do + local info=getinfo(level,"Sl") + if not info then + break + elseif info.what=="C" then + reporter("%2i : %s",level-1,"C function") + else + reporter("%2i : %s : %s",level-1,info.short_src,info.currentline) + end + level=level+1 + end +end +debugger.showtraceback=showtraceback + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["util-mrg"] = package.loaded["util-mrg"] or true + +-- original size: 7757, stripped down to: 6015 + +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", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +local gsub,format=string.gsub,string.format +local concat=table.concat +local type,next=type,next +local P,R,S,V,Ct,C,Cs,Cc,Cp,Cmt,Cb,Cg=lpeg.P,lpeg.R,lpeg.S,lpeg.V,lpeg.Ct,lpeg.C,lpeg.Cs,lpeg.Cc,lpeg.Cp,lpeg.Cmt,lpeg.Cb,lpeg.Cg +local lpegmatch,patterns=lpeg.match,lpeg.patterns +utilities=utilities or {} +local merger=utilities.merger or {} +utilities.merger=merger +merger.strip_comment=true +local report=logs.reporter("system","merge") +utilities.report=report +local m_begin_merge="begin library merge" +local m_end_merge="end library merge" +local m_begin_closure="do -- create closure to overcome 200 locals limit" +local m_end_closure="end -- of closure" +local m_pattern="%c+".."%-%-%s+"..m_begin_merge.."%c+(.-)%c+".."%-%-%s+"..m_end_merge.."%c+" +local m_format="\n\n-- "..m_begin_merge.."\n%s\n".."-- "..m_end_merge.."\n\n" +local m_faked="-- ".."created merged file".."\n\n".."-- "..m_begin_merge.."\n\n".."-- "..m_end_merge.."\n\n" +local m_report=[[ +-- used libraries : %s +-- skipped libraries : %s +-- original bytes : %s +-- stripped bytes : %s +]] +local m_preloaded=[[package.loaded[%q] = package.loaded[%q] or true]] +local function self_fake() + return m_faked +end +local function self_nothing() + return "" +end +local function self_load(name) + local data=io.loaddata(name) or "" + if data=="" then + report("unknown file %a",name) + else + report("inserting file %a",name) + end + return data or "" +end +local space=patterns.space +local eol=patterns.newline +local equals=P("=")^0 +local open=P("[")*Cg(equals,"init")*P("[")*P("\n")^-1 +local close=P("]")*C(equals)*P("]") +local closeeq=Cmt(close*Cb("init"),function(s,i,a,b) return a==b end) +local longstring=open*(1-closeeq)^0*close +local quoted=patterns.quoted +local digit=patterns.digit +local emptyline=space^0*eol +local operator1=P("<=")+P(">=")+P("~=")+P("..")+S("/^<>=*+%%") +local operator2=S("*+/") +local operator3=S("-") +local operator4=P("..") +local separator=S(",;") +local ignore=(P("]")*space^1*P("=")*space^1*P("]"))/"]=["+(P("=")*space^1*P("{"))/"={"+(P("(")*space^1)/"("+(P("{")*(space+eol)^1*P("}"))/"{}" +local strings=quoted +local longcmt=(emptyline^0*P("--")*longstring*emptyline^0)/"" +local longstr=longstring +local comment=emptyline^0*P("--")*P("-")^0*(1-eol)^0*emptyline^1/"\n" +local optionalspaces=space^0/"" +local mandatespaces=space^1/"" +local optionalspacing=(eol+space)^0/"" +local mandatespacing=(eol+space)^1/"" +local pack=digit*space^1*operator4*optionalspacing+optionalspacing*operator1*optionalspacing+optionalspacing*operator2*optionalspaces+mandatespacing*operator3*mandatespaces+optionalspaces*separator*optionalspaces +local lines=emptyline^2/"\n" +local spaces=(space*space)/" " +local compact=Cs (( + ignore+strings+longcmt+longstr+comment+pack+lines+spaces+1 +)^1 ) +local strip=Cs((emptyline^2/"\n"+1)^0) +local stripreturn=Cs((1-P("return")*space^1*P(1-space-eol)^1*(space+eol)^0*P(-1))^1) +function merger.compact(data) + return lpegmatch(strip,lpegmatch(compact,data)) +end +local function self_compact(data) + local delta=0 + if merger.strip_comment then + local before=#data + data=lpegmatch(compact,data) + data=lpegmatch(strip,data) + local after=#data + delta=before-after + report("original size %s, compacted to %s, stripped %s",before,after,delta) + data=format("-- original size: %s, stripped down to: %s\n\n%s",before,after,data) + end + return lpegmatch(stripreturn,data) or data,delta +end +local function self_save(name,data) + if data~="" then + io.savedata(name,data) + report("saving %s with size %s",name,#data) + end +end +local function self_swap(data,code) + return data~="" and (gsub(data,m_pattern,function() return format(m_format,code) end,1)) or "" +end +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 + for i=1,#libs do + local lib=libs[i] + for j=1,#list do + local pth=gsub(list[j],"\\","/") + report("checking library path %a",pth) + local name=pth.."/"..lib + if lfs.isfile(name) then + foundpath=pth + end + end + if foundpath then break end + end + if foundpath then + report("using library path %a",foundpath) + local right,wrong,original,stripped={},{},0,0 + for i=1,#libs do + local lib=libs[i] + local fullname=foundpath.."/"..lib + if lfs.isfile(fullname) then + report("using library %a",fullname) + local preloaded=file.nameonly(lib) + local data=io.loaddata(fullname,true) + original=original+#data + local data,delta=self_compact(data) + right[#right+1]=lib + result[#result+1]=m_begin_closure + result[#result+1]=format(m_preloaded,preloaded,preloaded) + result[#result+1]=data + result[#result+1]=m_end_closure + stripped=stripped+delta + else + report("skipping library %a",fullname) + wrong[#wrong+1]=lib + end + end + right=#right>0 and concat(right," ") or "-" + wrong=#wrong>0 and concat(wrong," ") or "-" + report("used libraries: %a",right) + report("skipped libraries: %a",wrong) + report("original bytes: %a",original) + report("stripped bytes: %a",stripped) + result[#result+1]=format(m_report,right,wrong,original,stripped) + else + report("no valid library path found") + end + return concat(result,"\n\n") +end +function merger.selfcreate(libs,list,target) + if target then + self_save(target,self_swap(self_fake(),self_libs(libs,list))) + end +end +function merger.selfmerge(name,libs,list,target) + self_save(target or name,self_swap(self_load(name),self_libs(libs,list))) +end +function merger.selfclean(name) + self_save(name,self_swap(self_load(name),self_nothing())) +end + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["util-tpl"] = package.loaded["util-tpl"] or true + +-- original size: 7100, stripped down to: 3978 + +if not modules then modules={} end modules ['util-tpl']={ + 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.templates=utilities.templates or {} +local templates=utilities.templates +local trace_template=false trackers.register("templates.trace",function(v) trace_template=v end) +local report_template=logs.reporter("template") +local tostring=tostring +local format,sub,byte=string.format,string.sub,string.byte +local P,C,R,Cs,Cc,Carg,lpegmatch,lpegpatterns=lpeg.P,lpeg.C,lpeg.R,lpeg.Cs,lpeg.Cc,lpeg.Carg,lpeg.match,lpeg.patterns +local replacer +local function replacekey(k,t,how,recursive) + local v=t[k] + if not v then + if trace_template then + report_template("unknown key %a",k) + end + return "" + else + v=tostring(v) + if trace_template then + report_template("setting key %a to value %a",k,v) + end + if recursive then + return lpegmatch(replacer,v,1,t,how,recursive) + else + return v + end + end +end +local sqlescape=lpeg.replacer { + { "'","''" }, + { "\\","\\\\" }, + { "\r\n","\\n" }, + { "\r","\\n" }, +} +local sqlquoted=Cs(Cc("'")*sqlescape*Cc("'")) +lpegpatterns.sqlescape=sqlescape +lpegpatterns.sqlquoted=sqlquoted +local luaescape=lpegpatterns.luaescape +local escapers={ + lua=function(s) + return lpegmatch(luaescape,s) + end, + sql=function(s) + return lpegmatch(sqlescape,s) + end, +} +local quotedescapers={ + lua=function(s) + return format("%q",s) + end, + sql=function(s) + return lpegmatch(sqlquoted,s) + end, +} +local luaescaper=escapers.lua +local quotedluaescaper=quotedescapers.lua +local function replacekeyunquoted(s,t,how,recurse) + if how==false then + return replacekey(s,t,how,recurse) + else + local escaper=how and escapers[how] or luaescaper + return escaper(replacekey(s,t,how,recurse)) + end +end +local function replacekeyquoted(s,t,how,recurse) + if how==false then + return replacekey(s,t,how,recurse) + else + local escaper=how and quotedescapers[how] or quotedluaescaper + return escaper(replacekey(s,t,how,recurse)) + end +end +local function replaceoptional(l,m,r,t,how,recurse) + local v=t[l] + return v and v~="" and lpegmatch(replacer,r,1,t,how or "lua",recurse or false) or "" +end +local single=P("%") +local double=P("%%") +local lquoted=P("%[") +local rquoted=P("]%") +local lquotedq=P("%(") +local rquotedq=P(")%") +local escape=double/'%%' +local nosingle=single/'' +local nodouble=double/'' +local nolquoted=lquoted/'' +local norquoted=rquoted/'' +local nolquotedq=lquotedq/'' +local norquotedq=rquotedq/'' +local noloptional=P("%?")/'' +local noroptional=P("?%")/'' +local nomoptional=P(":")/'' +local args=Carg(1)*Carg(2)*Carg(3) +local key=nosingle*((C((1-nosingle )^1)*args)/replacekey )*nosingle +local quoted=nolquotedq*((C((1-norquotedq )^1)*args)/replacekeyquoted )*norquotedq +local unquoted=nolquoted*((C((1-norquoted )^1)*args)/replacekeyunquoted)*norquoted +local optional=noloptional*((C((1-nomoptional)^1)*nomoptional*C((1-noroptional)^1)*args)/replaceoptional)*noroptional +local any=P(1) + replacer=Cs((unquoted+quoted+escape+optional+key+any)^0) +local function replace(str,mapping,how,recurse) + if mapping and str then + return lpegmatch(replacer,str,1,mapping,how or "lua",recurse or false) or str + else + return str + end +end +templates.replace=replace +function templates.replacer(str,how,recurse) + return function(mapping) + return lpegmatch(replacer,str,1,mapping,how or "lua",recurse or false) or str + end +end +function templates.load(filename,mapping,how,recurse) + local data=io.loaddata(filename) or "" + if mapping and next(mapping) then + return replace(data,mapping,how,recurse) + else + return data + end +end +function templates.resolve(t,mapping,how,recurse) + if not mapping then + mapping=t + end + for k,v in next,t do + t[k]=replace(v,mapping,how,recurse) + end + return t +end + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["util-env"] = package.loaded["util-env"] or true + +-- original size: 8022, stripped down to: 5038 + +if not modules then modules={} end modules ['util-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" +} +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,optionalquoted=string.unquoted,string.quoted,string.optionalquoted +local concat,insert,remove=table.concat,table.insert,table.remove +environment=environment or {} +local environment=environment +os.setlocale(nil,nil) +function os.setlocale() +end +local validengines=allocate { + ["luatex"]=true, + ["luajittex"]=true, +} +local basicengines=allocate { + ["luatex"]="luatex", + ["texlua"]="luatex", + ["texluac"]="luatex", + ["luajittex"]="luajittex", + ["texluajit"]="luajittex", +} +local luaengines=allocate { + ["lua"]=true, + ["luajit"]=true, +} +environment.validengines=validengines +environment.basicengines=basicengines +if not arg then + environment.used_as_library=true +elseif luaengines[file.removesuffix(arg[-1])] then +elseif validengines[file.removesuffix(arg[0])] then + if arg[1]=="--luaonly" then + arg[-1]=arg[0] + arg[ 0]=arg[2] + for k=3,#arg do + arg[k-2]=arg[k] + end + remove(arg) + remove(arg) + else + end + local originalzero=file.basename(arg[0]) + local specialmapping={ luatools=="base" } + if originalzero~="mtxrun" and originalzero~="mtxrun.lua" then + arg[0]=specialmapping[originalzero] or originalzero + insert(arg,0,"--script") + insert(arg,0,"mtxrun") + end +end +environment.arguments=allocate() +environment.files=allocate() +environment.sortedflags=nil +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 + flag=gsub(flag,"^c:","") + arguments[flag]=unquoted(value or "") + else + flag=match(argument,"^%-+(.+)") + if flag then + flag=gsub(flag,"^c:","") + arguments[flag]=true + else + files[#files+1]=argument + end + end + end + end + environment.ownname=file.reslash(environment.ownname or arg[0] or 'unknown.lua') +end +function environment.setargument(name,value) + environment.arguments[name]=value +end +function environment.getargument(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 + 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 +environment.argument=environment.getargument +function environment.splitarguments(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) + local resolveprefix=resolvers.resolve + arg=arg or environment.originalarguments + if noquote and #arg==1 then + return unquoted(resolveprefix and resolveprefix(arg[1]) or arg[1]) + elseif #arg>0 then + local result={} + for i=1,#arg do + result[i]=optionalquoted(resolveprefix and resolveprefix(arg[i]) or resolveprefix) + end + return concat(result," ") + else + return "" + end +end +function environment.relativepath(path,root) + if not path then + path="" + end + if not file.is_rootbased_path(path) then + if not root then + root=file.pathpart(environment.ownscript or environment.ownname or ".") + end + if root=="" then + root="." + end + path=root.."/"..path + end + return file.collapsepath(path,true) +end +if arg then + 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={} +end + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["luat-env"] = package.loaded["luat-env"] or true + +-- original size: 6174, stripped down to: 4141 + + 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" +} +local rawset,rawget,loadfile,assert=rawset,rawget,loadfile,assert +local trace_locating=false trackers.register("resolvers.locating",function(v) trace_locating=v end) +local report_lua=logs.reporter("resolvers","lua") +local luautilities=utilities.lua +local luasuffixes=luautilities.suffixes +local texgettoks=tex and tex.gettoks +environment=environment or {} +local environment=environment +local mt={ + __index=function(_,k) + if k=="version" then + local version=texgettoks and texgettoks("contextversiontoks") + if version and version~="" then + rawset(environment,"version",version) + return version + else + return "unknown" + end + elseif k=="kind" then + local kind=texgettoks and texgettoks("contextkindtoks") + if kind and kind~="" then + rawset(environment,"kind",kind) + return kind + 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.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 +local stripindeed=false directives.register("system.compile.strip",function(v) stripindeed=v end) +local function strippable(filename) + if stripindeed then + local modu=modules[file.nameonly(filename)] + return modu and modu.dataonly + else + return false + end +end +function environment.luafilechunk(filename,silent) + filename=file.replacesuffix(filename,"lua") + local fullname=environment.luafile(filename) + if fullname and fullname~="" then + local data=luautilities.loadedluacode(fullname,strippable,filename) + if not silent then + report_lua("loading file %a %s",fullname,not data and "failed" or "succeeded") + end + return data + else + if not silent then + report_lua("unknown file %a",filename) + end + return nil + end +end +function environment.loadluafile(filename,version) + local lucname,luaname,chunk + local basename=file.removesuffix(filename) + if basename==filename then + luaname=file.addsuffix(basename,luasuffixes.lua) + lucname=file.addsuffix(basename,luasuffixes.luc) + else + luaname=basename + lucname=nil + end + local fullname=(lucname and environment.luafile(lucname)) or "" + if fullname~="" then + if trace_locating then + report_lua("loading %a",fullname) + end + chunk=loadfile(fullname) + end + if chunk then + assert(chunk)() + if version then + local v=version + if modules and modules[filename] then + v=modules[filename].version + elseif versions and versions[filename] then + v=versions[filename] + end + if v==version then + return true + else + if trace_locating then + report_lua("version mismatch for %a, lua version %a, luc version %a",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 %a",fullname) + end + chunk=loadfile(fullname) + if not chunk then + if trace_locating then + report_lua("unknown file %a",filename) + end + else + assert(chunk)() + return true + end + end + return false +end + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["lxml-tab"] = package.loaded["lxml-tab"] or true + +-- original size: 45683, stripped down to: 27866 + +if not modules then modules={} end modules ['lxml-tab']={ + version=1.001, + comment="this module is the basis for the lxml-* ones", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +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(string.format(...)) end +if lpeg.setmaxstack then lpeg.setmaxstack(1000) end +xml=xml or {} +local xml=xml +local concat,remove,insert=table.concat,table.remove,table.insert +local type,next,setmetatable,getmetatable,tonumber,rawset=type,next,setmetatable,getmetatable,tonumber,rawset +local lower,find,match,gsub=string.lower,string.find,string.match,string.gsub +local utfchar=utf.char +local lpegmatch,lpegpatterns=lpeg.match,lpeg.patterns +local P,S,R,C,V,C,Cs=lpeg.P,lpeg.S,lpeg.R,lpeg.C,lpeg.V,lpeg.C,lpeg.Cs +local formatters=string.formatters +xml.xmlns=xml.xmlns or {} +local check=P(false) +local parse=check +function xml.registerns(namespace,pattern) + check=check+C(P(lower(pattern)))/namespace + parse=P { P(check)+1*V(1) } +end +function xml.checkns(namespace,url) + local ns=lpegmatch(parse,lower(url)) + if ns and namespace~=ns then + xml.xmlns[namespace]=ns + end +end +function xml.resolvens(url) + return lpegmatch(parse,lower(url)) or "" +end +local nsremap,resolvens=xml.xmlns,xml.resolvens +local stack={} +local top={} +local dt={} +local at={} +local xmlns={} +local errorstr=nil +local entities={} +local strip=false +local cleanup=false +local utfize=false +local resolve_predefined=false +local unify_predefined=false +local dcache={} +local hcache={} +local acache={} +local mt={} +local function initialize_mt(root) + mt={ __index=root } +end +function xml.setproperty(root,k,v) + getmetatable(root).__index[k]=v +end +function xml.checkerror(top,toclose) + return "" +end +local function add_attribute(namespace,tag,value) + if cleanup and #value>0 then + value=cleanup(value) + end + if tag=="xmlns" then + xmlns[#xmlns+1]=resolvens(value) + at[tag]=value + elseif namespace=="" then + at[tag]=value + elseif namespace=="xmlns" then + xml.checkns(tag,value) + at["xmlns:"..tag]=value + else + at[namespace..":"..tag]=value + end +end +local function add_empty(spacing,namespace,tag) + if #spacing>0 then + dt[#dt+1]=spacing + end + local resolved=namespace=="" and xmlns[#xmlns] or nsremap[namespace] or namespace + top=stack[#stack] + dt=top.dt + local t={ ns=namespace or "",rn=resolved,tg=tag,at=at,dt={},__p__=top } + dt[#dt+1]=t + setmetatable(t,mt) + if at.xmlns then + remove(xmlns) + end + at={} +end +local function add_begin(spacing,namespace,tag) + if #spacing>0 then + dt[#dt+1]=spacing + end + local resolved=namespace=="" and xmlns[#xmlns] or nsremap[namespace] or namespace + top={ ns=namespace or "",rn=resolved,tg=tag,at=at,dt={},__p__=stack[#stack] } + setmetatable(top,mt) + dt=top.dt + stack[#stack+1]=top + at={} +end +local function add_end(spacing,namespace,tag) + if #spacing>0 then + dt[#dt+1]=spacing + end + local toclose=remove(stack) + top=stack[#stack] + if #stack<1 then + errorstr=formatters["unable to close %s %s"](tag,xml.checkerror(top,toclose) or "") + report_xml(errorstr) + elseif toclose.tg~=tag then + errorstr=formatters["unable to close %s with %s %s"](toclose.tg,tag,xml.checkerror(top,toclose) or "") + report_xml(errorstr) + end + dt=top.dt + dt[#dt+1]=toclose + if toclose.at.xmlns then + remove(xmlns) + end +end +local function add_text(text) + local n=#dt + if cleanup and #text>0 then + if n>0 then + local s=dt[n] + if type(s)=="string" then + dt[n]=s..cleanup(text) + else + dt[n+1]=cleanup(text) + end + else + dt[1]=cleanup(text) + end + else + if n>0 then + local s=dt[n] + if type(s)=="string" then + dt[n]=s..text + else + dt[n+1]=text + end + else + dt[1]=text + end + end +end +local function add_special(what,spacing,text) + if #spacing>0 then + dt[#dt+1]=spacing + end + if strip and (what=="@cm@" or what=="@dt@") then + else + dt[#dt+1]={ special=true,ns="",tg=what,dt={ text } } + end +end +local function set_message(txt) + errorstr="garbage at the end of the file: "..gsub(txt,"([ \n\r\t]*)","") +end +local reported_attribute_errors={} +local function attribute_value_error(str) + if not reported_attribute_errors[str] then + report_xml("invalid attribute value %a",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 + report_xml("invalid attribute specification %a",str) + reported_attribute_errors[str]=true + at._error_=str + end + return str +end +local badentity="&error;" +local badentity="&" +xml.placeholders={ + unknown_dec_entity=function(str) return str=="" and badentity or formatters["&%s;"](str) end, + unknown_hex_entity=function(str) return formatters["&#x%s;"](str) end, + unknown_any_entity=function(str) return formatters["&#x%s;"](str) end, +} +local placeholders=xml.placeholders +local function fromhex(s) + local n=tonumber(s,16) + if n then + return utfchar(n) + else + return formatters["h:%s"](s),true + end +end +local function fromdec(s) + local n=tonumber(s) + if n then + return utfchar(n) + else + return formatters["d:%s"](s),true + end +end +local p_rest=(1-P(";"))^0 +local p_many=P(1)^0 +local p_char=lpegpatterns.utf8character +local parsedentity=P("&")*(P("#x")*(p_rest/fromhex)+P("#")*(p_rest/fromdec))*P(";")*P(-1)+(P("#x")*(p_many/fromhex)+P("#")*(p_many/fromdec)) +local predefined_unified={ + [38]="&", + [42]=""", + [47]="'", + [74]="<", + [76]=">", +} +local predefined_simplified={ + [38]="&",amp="&", + [42]='"',quot='"', + [47]="'",apos="'", + [74]="<",lt="<", + [76]=">",gt=">", +} +local nofprivates=0xF0000 +local privates_u={ + [ [[&]] ]="&", + [ [["]] ]=""", + [ [[']] ]="'", + [ [[<]] ]="<", + [ [[>]] ]=">", +} +local privates_p={} +local privates_n={ +} +local escaped=utf.remapper(privates_u,"dynamic") +local unprivatized=utf.remapper(privates_p,"dynamic") +xml.unprivatized=unprivatized +local function unescaped(s) + local p=privates_n[s] + if not p then + nofprivates=nofprivates+1 + p=utfchar(nofprivates) + privates_n[s]=p + s="&"..s..";" + privates_u[p]=s + privates_p[p]=s + end + return p +end +xml.privatetoken=unescaped +xml.privatecodes=privates_n +local function handle_hex_entity(str) + local h=hcache[str] + if not h then + local n=tonumber(str,16) + h=unify_predefined and predefined_unified[n] + if h then + if trace_entities then + report_xml("utfize, converting hex entity &#x%s; into %a",str,h) + end + elseif utfize then + h=(n and utfchar(n)) or xml.unknown_hex_entity(str) or "" + if not n then + report_xml("utfize, ignoring hex entity &#x%s;",str) + elseif trace_entities then + report_xml("utfize, converting hex entity &#x%s; into %a",str,h) + end + else + if trace_entities then + report_xml("found entity &#x%s;",str) + end + h="&#x"..str..";" + end + hcache[str]=h + end + return h +end +local function handle_dec_entity(str) + local d=dcache[str] + if not d then + local n=tonumber(str) + d=unify_predefined and predefined_unified[n] + if d then + if trace_entities then + report_xml("utfize, converting dec entity &#%s; into %a",str,d) + end + elseif utfize then + d=(n and utfchar(n)) or placeholders.unknown_dec_entity(str) or "" + if not n then + report_xml("utfize, ignoring dec entity &#%s;",str) + elseif trace_entities then + report_xml("utfize, converting dec entity &#%s; into %a",str,d) + end + else + if trace_entities then + report_xml("found entity &#%s;",str) + end + d="&#"..str..";" + end + dcache[str]=d + end + return d +end +xml.parsedentitylpeg=parsedentity +local function handle_any_entity(str) + if resolve then + local a=acache[str] + if not a then + a=resolve_predefined and predefined_simplified[str] + if a then + if trace_entities then + report_xml("resolving entity &%s; to predefined %a",str,a) + end + else + if type(resolve)=="function" then + a=resolve(str) or entities[str] + else + a=entities[str] + end + if a then + if type(a)=="function" then + if trace_entities then + report_xml("expanding entity &%s; to function call",str) + end + a=a(str) or "" + end + a=lpegmatch(parsedentity,a) or a + if trace_entities then + report_xml("resolving entity &%s; to internal %a",str,a) + end + else + 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 + report_xml("resolving entity &%s; to external %s",str,a) + end + else + if trace_entities then + report_xml("keeping entity &%s;",str) + end + if str=="" then + a=badentity + else + a="&"..str..";" + end + end + end + end + acache[str]=a + elseif trace_entities then + if not acache[str] then + report_xml("converting entity &%s; to %a",str,a) + acache[str]=a + end + end + return a + else + local a=acache[str] + if not a then + a=resolve_predefined and predefined_simplified[str] + if a then + acache[str]=a + if trace_entities then + report_xml("entity &%s; becomes %a",str,a) + end + elseif str=="" then + if trace_entities then + report_xml("invalid entity &%s;",str) + end + a=badentity + acache[str]=a + else + if trace_entities then + report_xml("entity &%s; is made private",str) + end + a=unescaped(str) + acache[str]=a + end + end + return a + end +end +local function handle_end_entity(str) + report_xml("error in entity, %a found without ending %a",str,";") + return str +end +local function handle_crap_error(chr) + report_xml("error in parsing, unexpected %a found ",chr) + add_text(chr) + return chr +end +local space=S(' \r\n\t') +local open=P('<') +local close=P('>') +local squote=S("'") +local dquote=S('"') +local equal=P('=') +local slash=P('/') +local colon=P(':') +local semicolon=P(';') +local ampersand=P('&') +local valid=R('az','AZ','09')+S('_-.') +local name_yes=C(valid^1)*colon*C(valid^1) +local name_nop=C(P(true))*C(valid^1) +local name=name_yes+name_nop +local utfbom=lpegpatterns.utfbom +local spacing=C(space^0) +local anyentitycontent=(1-open-semicolon-space-close-ampersand)^0 +local hexentitycontent=R("AF","af","09")^0 +local decentitycontent=R("09")^0 +local parsedentity=P("#")/""*( + P("x")/""*(hexentitycontent/handle_hex_entity)+(decentitycontent/handle_dec_entity) + )+(anyentitycontent/handle_any_entity) +local entity=(ampersand/"")*parsedentity*(semicolon/"")+ampersand*(anyentitycontent/handle_end_entity) +local text_unparsed=C((1-open)^1) +local text_parsed=Cs(((1-open-ampersand)^1+entity)^1) +local somespace=space^1 +local optionalspace=space^0 +local value=(squote*Cs((entity+(1-squote))^0)*squote)+(dquote*Cs((entity+(1-dquote))^0)*dquote) +local endofattributes=slash*close+close +local whatever=space*name*optionalspace*equal +local wrongvalue=Cs(P(entity+(1-space-endofattributes))^1)/attribute_value_error +local attributevalue=value+wrongvalue +local attribute=(somespace*name*optionalspace*equal*optionalspace*attributevalue)/add_attribute +local attributes=(attribute+somespace^-1*(((1-endofattributes)^1)/attribute_specification_error))^0 +local parsedtext=text_parsed/add_text +local unparsedtext=text_unparsed/add_text +local balanced=P { "["*((1-S"[]")+V(1))^0*"]" } +local emptyelement=(spacing*open*name*attributes*optionalspace*slash*close)/add_empty +local beginelement=(spacing*open*name*attributes*optionalspace*close)/add_begin +local endelement=(spacing*open*slash*name*optionalspace*close)/add_end +local begincomment=open*P("!--") +local endcomment=P("--")*close +local begininstruction=open*P("?") +local endinstruction=P("?")*close +local begincdata=open*P("![CDATA[") +local endcdata=P("]]")*close +local someinstruction=C((1-endinstruction)^0) +local somecomment=C((1-endcomment )^0) +local somecdata=C((1-endcdata )^0) +local function normalentity(k,v ) entities[k]=v end +local function systementity(k,v,n) entities[k]=v end +local function publicentity(k,v,n) entities[k]=v end +local begindoctype=open*P("!DOCTYPE") +local enddoctype=close +local beginset=P("[") +local endset=P("]") +local doctypename=C((1-somespace-close)^0) +local elementdoctype=optionalspace*P("<!ELEMENT")*(1-close)^0*close +local basiccomment=begincomment*((1-endcomment)^0)*endcomment +local normalentitytype=(doctypename*somespace*value)/normalentity +local publicentitytype=(doctypename*somespace*P("PUBLIC")*somespace*value)/publicentity +local systementitytype=(doctypename*somespace*P("SYSTEM")*somespace*value*somespace*P("NDATA")*somespace*doctypename)/systementity +local entitydoctype=optionalspace*P("<!ENTITY")*somespace*(systementitytype+publicentitytype+normalentitytype)*optionalspace*close +local doctypeset=beginset*optionalspace*P(elementdoctype+entitydoctype+basiccomment+space)^0*optionalspace*endset +local definitiondoctype=doctypename*somespace*doctypeset +local publicdoctype=doctypename*somespace*P("PUBLIC")*somespace*value*somespace*value*somespace*doctypeset +local systemdoctype=doctypename*somespace*P("SYSTEM")*somespace*value*somespace*doctypeset +local simpledoctype=(1-close)^1 +local somedoctype=C((somespace*(publicdoctype+systemdoctype+definitiondoctype+simpledoctype)*optionalspace)^0) +local instruction=(spacing*begininstruction*someinstruction*endinstruction)/function(...) add_special("@pi@",...) end +local comment=(spacing*begincomment*somecomment*endcomment )/function(...) add_special("@cm@",...) end +local cdata=(spacing*begincdata*somecdata*endcdata )/function(...) add_special("@cd@",...) end +local doctype=(spacing*begindoctype*somedoctype*enddoctype )/function(...) add_special("@dt@",...) end +local crap_parsed=1-beginelement-endelement-emptyelement-begininstruction-begincomment-begincdata-ampersand +local crap_unparsed=1-beginelement-endelement-emptyelement-begininstruction-begincomment-begincdata +local parsedcrap=Cs((crap_parsed^1+entity)^1)/handle_crap_error +local unparsedcrap=Cs((crap_unparsed )^1)/handle_crap_error +local trailer=space^0*(text_unparsed/set_message)^0 +local grammar_parsed_text=P { "preamble", + preamble=utfbom^0*instruction^0*(doctype+comment+instruction)^0*V("parent")*trailer, + parent=beginelement*V("children")^0*endelement, + children=parsedtext+V("parent")+emptyelement+comment+cdata+instruction+parsedcrap, +} +local grammar_unparsed_text=P { "preamble", + preamble=utfbom^0*instruction^0*(doctype+comment+instruction)^0*V("parent")*trailer, + parent=beginelement*V("children")^0*endelement, + children=unparsedtext+V("parent")+emptyelement+comment+cdata+instruction+unparsedcrap, +} +local function _xmlconvert_(data,settings) + settings=settings or {} + strip=settings.strip_cm_and_dt + utfize=settings.utfize_entities + resolve=settings.resolve_entities + resolve_predefined=settings.resolve_predefined_entities + unify_predefined=settings.unify_predefined_entities + cleanup=settings.text_cleanup + entities=settings.entities or {} + if utfize==nil then + settings.utfize_entities=true + utfize=true + end + if resolve_predefined==nil then + settings.resolve_predefined_entities=true + resolve_predefined=true + end + stack,top,at,xmlns,errorstr={},{},{},{},nil + acache,hcache,dcache={},{},{} + reported_attribute_errors={} + if settings.parent_root then + mt=getmetatable(settings.parent_root) + else + initialize_mt(top) + end + stack[#stack+1]=top + top.dt={} + dt=top.dt + if not data or data=="" then + errorstr="empty xml file" + elseif utfize or resolve then + if lpegmatch(grammar_parsed_text,data) then + else + errorstr="invalid xml file - parsed text" + end + elseif type(data)=="string" then + if lpegmatch(grammar_unparsed_text,data) then + errorstr="" + else + errorstr="invalid xml file - unparsed text" + end + 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(result,mt) +setmetatable(result.dt[1],mt) + setmetatable(stack,mt) + local errorhandler=settings.error_handler + if errorhandler==false then + else + errorhandler=errorhandler or xml.errorhandler + if errorhandler then + local currentresource=settings.currentresource + if currentresource and currentresource~="" then + xml.errorhandler(formatters["load error in [%s]: %s"](currentresource,errorstr)) + else + xml.errorhandler(formatters["load error: %s"](errorstr)) + end + end + end + else + result=stack[1] + end + if not settings.no_root then + result={ special=true,ns="",tg='@rt@',dt=result.dt,at={},entities=entities,settings=settings } + setmetatable(result,mt) + local rdt=result.dt + for k=1,#rdt do + local v=rdt[k] + if type(v)=="table" and not v.special then + result.ri=k + v.__p__=result + break + end + end + end + if errorstr and errorstr~="" then + result.error=true + else + errorstr=nil + end + result.statistics={ + errormessage=errorstr, + entities={ + decimals=dcache, + hexadecimals=hcache, + names=acache, + } + } + strip,utfize,resolve,resolve_predefined=nil,nil,nil,nil + unify_predefined,cleanup,entities=nil,nil,nil + stack,top,at,xmlns,errorstr=nil,nil,nil,nil,nil + acache,hcache,dcache=nil,nil,nil + reported_attribute_errors,mt,errorhandler=nil,nil,nil + return result +end +local function xmlconvert(data,settings) + local ok,result=pcall(function() return _xmlconvert_(data,settings) end) + if ok then + return result + else + return _xmlconvert_("",settings) + end +end +xml.convert=xmlconvert +function xml.inheritedconvert(data,xmldata) + local settings=xmldata.settings + if settings then + settings.parent_root=xmldata + end + local xc=xmlconvert(data,settings) + return xc +end +function xml.is_valid(root) + return root and root.dt and root.dt[1] and type(root.dt[1])=="table" and not root.dt[1].er +end +function xml.package(tag,attributes,data) + local ns,tg=match(tag,"^(.-):?([^:]+)$") + local t={ ns=ns,tg=tg,dt=data or "",at=attributes or {} } + setmetatable(t,mt) + return t +end +function xml.is_valid(root) + return root and not root.error +end +xml.errorhandler=report_xml +function xml.load(filename,settings) + local data="" + if type(filename)=="string" then + local f=io.open(filename,'r') + if f then + data=f:read("*all") + f:close() + end + elseif filename then + data=filename:read("*all") + end + if settings then + settings.currentresource=filename + local result=xmlconvert(data,settings) + settings.currentresource=nil + return result + else + return xmlconvert(data,{ currentresource=filename }) + end +end +local no_root={ no_root=true } +function xml.toxml(data) + if type(data)=="string" then + local root={ xmlconvert(data,no_root) } + return (#root>1 and root) or root[1] + else + return data + end +end +local function copy(old,tables) + if old then + tables=tables or {} + local new={} + if not tables[old] then + tables[old]=new + end + for k,v in next,old do + new[k]=(type(v)=="table" and (tables[v] or copy(v,tables))) or v + end + local mt=getmetatable(old) + if mt then + setmetatable(new,mt) + end + return new + else + return {} + end +end +xml.copy=copy +function xml.checkbom(root) + if root.ri then + local dt=root.dt + for k=1,#dt do + local v=dt[k] + if type(v)=="table" and v.special and v.tg=="@pi@" and find(v.dt[1],"xml.*version=") then + return + end + end + insert(dt,1,{ special=true,ns="",tg="@pi@",dt={ "xml version='1.0' standalone='yes'" } } ) + insert(dt,2,"\n" ) + end +end +local f_attribute=formatters['%s=%q'] +local function verbose_element(e,handlers,escape) + local handle=handlers.handle + local serialize=handlers.serialize + local ens,etg,eat,edt,ern=e.ns,e.tg,e.at,e.dt,e.rn + local ats=eat and next(eat) and {} + if ats then + local n=0 + for k,v in next,eat do + n=n+1 + ats[n]=f_attribute(k,escaped(v)) + end + end + if ern and trace_entities and ern~=ens then + ens=ern + end + if ens~="" then + if edt and #edt>0 then + if ats then + handle("<",ens,":",etg," ",concat(ats," "),">") + else + handle("<",ens,":",etg,">") + end + for i=1,#edt do + local e=edt[i] + if type(e)=="string" then + handle(escaped(e)) + else + serialize(e,handlers) + end + end + handle("</",ens,":",etg,">") + else + if ats then + handle("<",ens,":",etg," ",concat(ats," "),"/>") + else + handle("<",ens,":",etg,"/>") + end + end + else + if edt and #edt>0 then + if ats then + handle("<",etg," ",concat(ats," "),">") + else + handle("<",etg,">") + end + for i=1,#edt do + local e=edt[i] + if type(e)=="string" then + handle(escaped(e)) + else + serialize(e,handlers) + end + end + handle("</",etg,">") + else + if ats then + handle("<",etg," ",concat(ats," "),"/>") + else + handle("<",etg,"/>") + end + end + end +end +local function verbose_pi(e,handlers) + handlers.handle("<?",e.dt[1],"?>") +end +local function verbose_comment(e,handlers) + handlers.handle("<!--",e.dt[1],"-->") +end +local function verbose_cdata(e,handlers) + handlers.handle("<![CDATA[",e.dt[1],"]]>") +end +local function verbose_doctype(e,handlers) + handlers.handle("<!DOCTYPE ",e.dt[1],">") +end +local function verbose_root(e,handlers) + handlers.serialize(e.dt,handlers) +end +local function verbose_text(e,handlers) + handlers.handle(escaped(e)) +end +local function verbose_document(e,handlers) + local serialize=handlers.serialize + local functions=handlers.functions + for i=1,#e do + local ei=e[i] + if type(ei)=="string" then + functions["@tx@"](ei,handlers) + else + serialize(ei,handlers) + end + end +end +local function serialize(e,handlers,...) + if e then + local initialize=handlers.initialize + local finalize=handlers.finalize + local functions=handlers.functions + if initialize then + local state=initialize(...) + if not state==true then + return state + end + end + local etg=e.tg + if etg then + (functions[etg] or functions["@el@"])(e,handlers) + else + functions["@dc@"](e,handlers) + end + if finalize then + return finalize() + end + end +end +local function xserialize(e,handlers) + local functions=handlers.functions + local etg=e.tg + if etg then + (functions[etg] or functions["@el@"])(e,handlers) + else + functions["@dc@"](e,handlers) + end +end +local handlers={} +local function newhandlers(settings) + local t=table.copy(handlers[settings and settings.parent or "verbose"] or {}) + if settings then + for k,v in next,settings do + if type(v)=="table" then + local tk=t[k] if not tk then tk={} t[k]=tk end + for kk,vv in next,v do + tk[kk]=vv + end + else + t[k]=v + end + end + if settings.name then + handlers[settings.name]=t + end + end + utilities.storage.mark(t) + return t +end +local nofunction=function() end +function xml.sethandlersfunction(handler,name,fnc) + handler.functions[name]=fnc or nofunction +end +function xml.gethandlersfunction(handler,name) + return handler.functions[name] +end +function xml.gethandlers(name) + return handlers[name] +end +newhandlers { + name="verbose", + initialize=false, + finalize=false, + serialize=xserialize, + handle=print, + functions={ + ["@dc@"]=verbose_document, + ["@dt@"]=verbose_doctype, + ["@rt@"]=verbose_root, + ["@el@"]=verbose_element, + ["@pi@"]=verbose_pi, + ["@cm@"]=verbose_comment, + ["@cd@"]=verbose_cdata, + ["@tx@"]=verbose_text, + } +} +local result +local xmlfilehandler=newhandlers { + name="file", + initialize=function(name) + result=io.open(name,"wb") + return result + end, + finalize=function() + result:close() + return true + end, + handle=function(...) + result:write(...) + end, +} +function xml.save(root,name) + serialize(root,xmlfilehandler,name) +end +local result +local xmlstringhandler=newhandlers { + name="string", + initialize=function() + result={} + return result + end, + finalize=function() + return concat(result) + end, + handle=function(...) + result[#result+1]=concat {... } + end, +} +local function xmltostring(root) + if not root then + return "" + elseif type(root)=="string" then + return root + else + return serialize(root,xmlstringhandler) or "" + end +end +local function __tostring(root) + return (root and xmltostring(root)) or "" +end +initialize_mt=function(root) + mt={ __tostring=__tostring,__index=root } +end +xml.defaulthandlers=handlers +xml.newhandlers=newhandlers +xml.serialize=serialize +xml.tostring=xmltostring +local function xmlstring(e,handle) + if not handle or (e.special and e.tg~="@rt@") then + elseif e.tg then + local edt=e.dt + if edt then + for i=1,#edt do + xmlstring(edt[i],handle) + end + end + else + handle(e) + end +end +xml.string=xmlstring +function xml.settings(e) + while e do + local s=e.settings + if s then + return s + else + e=e.__p__ + end + end + return nil +end +function xml.root(e) + local r=e + while e do + e=e.__p__ + if e then + r=e + end + end + return r +end +function xml.parent(root) + return root.__p__ +end +function xml.body(root) + return root.ri and root.dt[root.ri] or root +end +function xml.name(root) + if not root then + return "" + end + local ns=root.ns + local tg=root.tg + if ns=="" then + return tg + else + return ns..":"..tg + end +end +function xml.erase(dt,k) + if dt then + if k then + dt[k]="" + else for k=1,#dt do + dt[1]={ "" } + end end + end +end +function xml.assign(dt,k,root) + if dt and k then + dt[k]=type(root)=="table" and xml.body(root) or root + return dt[k] + else + return xml.body(root) + end +end +function xml.tocdata(e,wrapper) + local whatever=type(e)=="table" and xmltostring(e.dt) or e or "" + if wrapper then + whatever=formatters["<%s>%s</%s>"](wrapper,whatever,wrapper) + end + local t={ special=true,ns="",tg="@cd@",at={},rn="",dt={ whatever },__p__=e } + setmetatable(t,getmetatable(e)) + e.dt={ t } +end +function xml.makestandalone(root) + if root.ri then + local dt=root.dt + for k=1,#dt do + local v=dt[k] + if type(v)=="table" and v.special and v.tg=="@pi@" then + local txt=v.dt[1] + if find(txt,"xml.*version=") then + v.dt[1]=txt.." standalone='yes'" + break + end + end + end + end + return root +end +function xml.kind(e) + local dt=e and e.dt + if dt then + local n=#dt + if n==1 then + local d=dt[1] + if d.special then + local tg=d.tg + if tg=="@cd@" then + return "cdata" + elseif tg=="@cm" then + return "comment" + elseif tg=="@pi@" then + return "instruction" + elseif tg=="@dt@" then + return "declaration" + end + elseif type(d)=="string" then + return "text" + end + return "element" + elseif n>0 then + return "mixed" + end + end + return "empty" +end + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["lxml-lpt"] = package.loaded["lxml-lpt"] or true + +-- original size: 48229, stripped down to: 30684 + +if not modules then modules={} end modules ['lxml-lpt']={ + version=1.001, + comment="this module is the basis for the lxml-* ones", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +local concat,remove,insert=table.concat,table.remove,table.insert +local type,next,tonumber,tostring,setmetatable,load,select=type,next,tonumber,tostring,setmetatable,load,select +local format,upper,lower,gmatch,gsub,find,rep=string.format,string.upper,string.lower,string.gmatch,string.gsub,string.find,string.rep +local lpegmatch,lpegpatterns=lpeg.match,lpeg.patterns +local setmetatableindex=table.setmetatableindex +local formatters=string.formatters +local trace_lpath=false if trackers then trackers.register("xml.path",function(v) trace_lpath=v end) end +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") +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 {} +local functions=xml.functions +xml.expressions=xml.expressions or {} +local expressions=xml.expressions +xml.finalizers=xml.finalizers or {} +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 {} +local function fallback (t,name) + local fn=finalizers[name] + if fn then + t[name]=fn + else + report_lpath("unknown sub finalizer %a",name) + fn=function() end + end + return fn +end +setmetatableindex(finalizers.xml,fallback) +setmetatableindex(finalizers.tex,fallback) +xml.defaultprotocol="xml" +local apply_axis={} +apply_axis['root']=function(list) + local collected={} + for l=1,#list do + local ll=list[l] + local rt=ll + while ll do + ll=ll.__p__ + if ll then + rt=ll + end + end + collected[l]=rt + end + return collected +end +apply_axis['self']=function(list) + return list +end +apply_axis['child']=function(list) + local collected,c={},0 + for l=1,#list do + local ll=list[l] + local dt=ll.dt + if dt then + 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 + en=en+1 + dk.ei=en + end + end + ll.en=en + end + end + return collected +end +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 + c=c+1 + collected[c]=dk + dk.ni=k + en=en+1 + dk.ei=en + c=collect(dk,collected,c) + end + end + list.en=en + end + return c +end +apply_axis['descendant']=function(list) + local collected,c={},0 + for l=1,#list do + c=collect(list[l],collected,c) + end + return collected +end +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 + c=c+1 + collected[c]=dk + dk.ni=k + en=en+1 + dk.ei=en + c=collect(dk,collected,c) + end + end + list.en=en + end + return c +end +apply_axis['descendant-or-self']=function(list) + local collected,c={},0 + for l=1,#list do + local ll=list[l] + if ll.special~=true then + c=c+1 + collected[c]=ll + end + c=collect(ll,collected,c) + end + return collected +end +apply_axis['ancestor']=function(list) + local collected,c={},0 + for l=1,#list do + local ll=list[l] + while ll do + ll=ll.__p__ + if ll then + c=c+1 + collected[c]=ll + end + end + end + return collected +end +apply_axis['ancestor-or-self']=function(list) + local collected,c={},0 + for l=1,#list do + local ll=list[l] + c=c+1 + collected[c]=ll + while ll do + ll=ll.__p__ + if ll then + c=c+1 + collected[c]=ll + end + end + end + return collected +end +apply_axis['parent']=function(list) + local collected,c={},0 + for l=1,#list do + local pl=list[l].__p__ + if pl then + c=c+1 + collected[c]=pl + end + end + return collected +end +apply_axis['attribute']=function(list) + return {} +end +apply_axis['namespace']=function(list) + return {} +end +apply_axis['following']=function(list) + return {} +end +apply_axis['preceding']=function(list) + return {} +end +apply_axis['following-sibling']=function(list) + local collected,c={},0 + 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 + c=c+1 + collected[c]=di + end + end + end + return collected +end +apply_axis['preceding-sibling']=function(list) + local collected,c={},0 + for l=1,#list do + local ll=list[l] + local p=ll.__p__ + local d=p.dt + for i=1,ll.ni-1 do + local di=d[i] + if type(di)=="table" then + c=c+1 + collected[c]=di + end + end + end + return collected +end +apply_axis['reverse-sibling']=function(list) + local collected,c={},0 + 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 + c=c+1 + collected[c]=di + end + end + end + return collected +end +apply_axis['auto-descendant-or-self']=apply_axis['descendant-or-self'] +apply_axis['auto-descendant']=apply_axis['descendant'] +apply_axis['auto-child']=apply_axis['child'] +apply_axis['auto-self']=apply_axis['self'] +apply_axis['initial-child']=apply_axis['child'] +local function apply_nodes(list,directive,nodes) + local maxn=#nodes + if maxn==3 then + local nns,ntg=nodes[2],nodes[3] + if not nns and not ntg then + if directive then + return list + else + return {} + end + else + local collected,c,m,p={},0,0,nil + if not nns then + for l=1,#list do + local ll=list[l] + local ltg=ll.tg + if ltg then + if directive then + if ntg==ltg then + local llp=ll.__p__;if llp~=p then p,m=llp,1 else m=m+1 end + 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 + c=c+1 + collected[c],ll.mi=ll,m + end + end + end + elseif not ntg then + for l=1,#list do + local ll=list[l] + local lns=ll.rn or ll.ns + if lns then + if directive then + if lns==nns then + local llp=ll.__p__;if llp~=p then p,m=llp,1 else m=m+1 end + 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 + c=c+1 + collected[c],ll.mi=ll,m + end + end + end + else + for l=1,#list do + local ll=list[l] + local ltg=ll.tg + if ltg then + local lns=ll.rn or ll.ns + local ok=ltg==ntg and lns==nns + if directive then + if ok then + local llp=ll.__p__;if llp~=p then p,m=llp,1 else m=m+1 end + 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 + c=c+1 + collected[c],ll.mi=ll,m + end + end + end + end + return collected + end + else + local collected,c,m,p={},0,0,nil + for l=1,#list do + local ll=list[l] + local ltg=ll.tg + if ltg then + local lns=ll.rn or ll.ns + local ok=false + for n=1,maxn,3 do + local nns,ntg=nodes[n+1],nodes[n+2] + ok=(not ntg or ltg==ntg) and (not nns or lns==nns) + if ok then + break + end + end + if directive then + if ok then + local llp=ll.__p__;if llp~=p then p,m=llp,1 else m=m+1 end + 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 + c=c+1 + collected[c],ll.mi=ll,m + end + end + end + return collected + end +end +local quit_expression=false +local function apply_expression(list,expression,order) + local collected,c={},0 + quit_expression=false + for l=1,#list do + local ll=list[l] + if expression(list,ll,l,order) then + c=c+1 + collected[c]=ll + end + if quit_expression then + break + end + end + return collected +end +local P,V,C,Cs,Cc,Ct,R,S,Cg,Cb=lpeg.P,lpeg.V,lpeg.C,lpeg.Cs,lpeg.Cc,lpeg.Ct,lpeg.R,lpeg.S,lpeg.Cg,lpeg.Cb +local spaces=S(" \n\r\t\f")^0 +local lp_space=S(" \n\r\t\f") +local lp_any=P(1) +local lp_noequal=P("!=")/"~="+P("<=")+P(">=")+P("==") +local lp_doequal=P("=")/"==" +local lp_or=P("|")/" or " +local lp_and=P("&")/" and " +local lp_builtin=P ( + P("text")/"(ll.dt[1] or '')"+ + P("content")/"ll.dt"+ + P("name")/"((ll.ns~='' and ll.ns..':'..ll.tg) or ll.tg)"+P("tag")/"ll.tg"+P("position")/"l"+ + P("firstindex")/"1"+P("lastindex")/"(#ll.__p__.dt or 1)"+P("firstelement")/"1"+P("lastelement")/"(ll.__p__.en or 1)"+P("first")/"1"+P("last")/"#list"+P("rootposition")/"order"+P("order")/"order"+P("element")/"(ll.ei or 1)"+P("index")/"(ll.ni or 1)"+P("match")/"(ll.mi or 1)"+ + P("ns")/"ll.ns" + )*((spaces*P("(")*spaces*P(")"))/"") +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)/"l==%0" +local lp_fastpos_n=P("-")*R("09")^1*P(-1)/"(%0<0 and (#list+%0==l))" +local lp_fastpos=lp_fastpos_n+lp_fastpos_p +local lp_reserved=C("and")+C("or")+C("not")+C("div")+C("mod")+C("true")+C("false") +local lp_lua_function=Cs((R("az","AZ","__")^1*(P(".")*R("az","AZ","__")^1)^1)*("("))/"%0" +local lp_function=C(R("az","AZ","__")^1)*P("(")/function(t) + if expressions[t] then + return "expr."..t.."(" + else + return "expr.error(" + end +end +local lparent=P("(") +local rparent=P(")") +local noparent=1-(lparent+rparent) +local nested=P{lparent*(noparent+V(1))^0*rparent} +local value=P(lparent*C((noparent+nested)^0)*rparent) +local lp_child=Cc("expr.child(ll,'")*R("az","AZ","--","__")^1*Cc("')") +local lp_number=S("+-")*R("09")^1 +local lp_string=Cc("'")*R("az","AZ","--","__")^1*Cc("'") +local lp_content=(P("'")*(1-P("'"))^0*P("'")+P('"')*(1-P('"'))^0*P('"')) +local cleaner +local lp_special=(C(P("name")+P("text")+P("tag")+P("count")+P("child")))*value/function(t,s) + if expressions[t] then + s=s and s~="" and lpegmatch(cleaner,s) + if s and s~="" then + return "expr."..t.."(ll,"..s..")" + else + return "expr."..t.."(ll)" + end + else + return "expr.error("..t..")" + end +end +local content=lp_builtin+lp_attribute+lp_special+lp_noequal+lp_doequal+lp_or+lp_and+lp_reserved+lp_lua_function+lp_function+lp_content+ + lp_child+lp_any +local converter=Cs ( + lp_fastpos+(P { lparent*(V(1))^0*rparent+content } )^0 +) +cleaner=Cs (( + lp_reserved+lp_number+lp_string+1 )^1 ) +local template_e=[[ + local expr = xml.expressions + return function(list,ll,l,order) + return %s + end +]] +local template_f_y=[[ + local finalizer = xml.finalizers['%s']['%s'] + return function(collection) + return finalizer(collection,%s) + end +]] +local template_f_n=[[ + return xml.finalizers['%s']['%s'] +]] +local register_self={ kind="axis",axis="self" } +local register_parent={ kind="axis",axis="parent" } +local register_descendant={ kind="axis",axis="descendant" } +local register_child={ kind="axis",axis="child" } +local register_descendant_or_self={ kind="axis",axis="descendant-or-self" } +local register_root={ kind="axis",axis="root" } +local register_ancestor={ kind="axis",axis="ancestor" } +local register_ancestor_or_self={ kind="axis",axis="ancestor-or-self" } +local register_attribute={ kind="axis",axis="attribute" } +local register_namespace={ kind="axis",axis="namespace" } +local register_following={ kind="axis",axis="following" } +local register_following_sibling={ kind="axis",axis="following-sibling" } +local register_preceding={ kind="axis",axis="preceding" } +local register_preceding_sibling={ kind="axis",axis="preceding-sibling" } +local register_reverse_sibling={ kind="axis",axis="reverse-sibling" } +local register_auto_descendant_or_self={ kind="axis",axis="auto-descendant-or-self" } +local register_auto_descendant={ kind="axis",axis="auto-descendant" } +local register_auto_self={ kind="axis",axis="auto-self" } +local register_auto_child={ kind="axis",axis="auto-child" } +local register_initial_child={ kind="axis",axis="initial-child" } +local register_all_nodes={ kind="nodes",nodetest=true,nodes={ true,false,false } } +local skip={} +local function errorrunner_e(str,cnv) + if not skip[str] then + report_lpath("error in expression: %s => %s",str,cnv) + skip[str]=cnv or str + end + return false +end +local function errorrunner_f(str,arg) + report_lpath("error in finalizer: %s(%s)",str,arg or "") + return false +end +local function register_nodes(nodetest,nodes) + return { kind="nodes",nodetest=nodetest,nodes=nodes } +end +local function register_expression(expression) + local converted=lpegmatch(converter,expression) + local runner=load(format(template_e,converted)) + runner=(runner and runner()) or function() errorrunner_e(expression,converted) end + return { kind="expression",expression=expression,converted=converted,evaluator=runner } +end +local function register_finalizer(protocol,name,arguments) + local runner + if arguments and arguments~="" then + runner=load(format(template_f_y,protocol or xml.defaultprotocol,name,arguments)) + else + runner=load(format(template_f_n,protocol or xml.defaultprotocol,name)) + end + runner=(runner and runner()) or function() errorrunner_f(name,arguments) end + return { kind="finalizer",name=name,arguments=arguments,finalizer=runner } +end +local expression=P { "ex", + ex="["*C((V("sq")+V("dq")+(1-S("[]"))+V("ex"))^0)*"]", + sq="'"*(1-S("'"))^0*"'", + dq='"'*(1-S('"'))^0*'"', +} +local arguments=P { "ar", + ar="("*Cs((V("sq")+V("dq")+V("nq")+P(1-P(")")))^0)*")", + nq=((1-S("),'\""))^1)/function(s) return format("%q",s) end, + sq=P("'")*(1-P("'"))^0*P("'"), + dq=P('"')*(1-P('"'))^0*P('"'), +} +local function register_error(str) + return { kind="error",error=format("unparsed: %s",str) } +end +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 no_nextcolon=P(-1)+#(1-P(":")) +local no_nextlparent=P(-1)+#(1-P("(")) +local pathparser=Ct { "patterns", + patterns=spaces*V("protocol")*spaces*( + (V("special")*spaces*P(-1) )+(V("initial")*spaces*V("step")*spaces*(P("/")*spaces*V("step")*spaces)^0 ) + ), + protocol=Cg(V("letters"),"protocol")*P("://")+Cg(Cc(nil),"protocol"), + step=((V("shortcuts")+P("/")+V("axis"))*spaces*V("nodes")^0+V("error"))*spaces*V("expressions")^0*spaces*V("finalizer")^0, + axis=V("descendant")+V("child")+V("parent")+V("self")+V("root")+V("ancestor")+V("descendant_or_self")+V("following_sibling")+V("following")+V("reverse_sibling")+V("preceding_sibling")+V("preceding")+V("ancestor_or_self")+#(1-P(-1))*Cc(register_auto_child), + special=special_1+special_2+special_3, + initial=(P("/")*spaces*Cc(register_initial_child))^-1, + error=(P(1)^1)/register_error, + shortcuts_a=V("s_descendant_or_self")+V("s_descendant")+V("s_child")+V("s_parent")+V("s_self")+V("s_root")+V("s_ancestor"), + shortcuts=V("shortcuts_a")*(spaces*"/"*spaces*V("shortcuts_a"))^0, + s_descendant_or_self=(P("***/")+P("/"))*Cc(register_descendant_or_self), + s_descendant=P("**")*Cc(register_descendant), + s_child=P("*")*no_nextcolon*Cc(register_child ), + s_parent=P("..")*Cc(register_parent ), + s_self=P("." )*Cc(register_self ), + s_root=P("^^")*Cc(register_root ), + s_ancestor=P("^")*Cc(register_ancestor ), + descendant=P("descendant::")*Cc(register_descendant ), + child=P("child::")*Cc(register_child ), + parent=P("parent::")*Cc(register_parent ), + self=P("self::")*Cc(register_self ), + root=P('root::')*Cc(register_root ), + ancestor=P('ancestor::')*Cc(register_ancestor ), + descendant_or_self=P('descendant-or-self::')*Cc(register_descendant_or_self ), + ancestor_or_self=P('ancestor-or-self::')*Cc(register_ancestor_or_self ), + following=P('following::')*Cc(register_following ), + following_sibling=P('following-sibling::')*Cc(register_following_sibling ), + preceding=P('preceding::')*Cc(register_preceding ), + preceding_sibling=P('preceding-sibling::')*Cc(register_preceding_sibling ), + reverse_sibling=P('reverse-sibling::')*Cc(register_reverse_sibling ), + nodes=(V("nodefunction")*spaces*P("(")*V("nodeset")*P(")")+V("nodetest")*V("nodeset"))/register_nodes, + expressions=expression/register_expression, + letters=R("az")^1, + name=(1-S("/[]()|:*!"))^1, + negate=P("!")*Cc(false), + nodefunction=V("negate")+P("not")*Cc(false)+Cc(true), + nodetest=V("negate")+Cc(true), + nodename=(V("negate")+Cc(true))*spaces*((V("wildnodename")*P(":")*V("wildnodename"))+(Cc(false)*V("wildnodename"))), + wildnodename=(C(V("name"))+P("*")*Cc(false))*no_nextlparent, + nodeset=spaces*Ct(V("nodename")*(spaces*P("|")*spaces*V("nodename"))^0)*spaces, + finalizer=(Cb("protocol")*P("/")^-1*C(V("name"))*arguments*P(-1))/register_finalizer, +} +xmlpatterns.pathparser=pathparser +local cache={} +local function nodesettostring(set,nodetest) + local t={} + for i=1,#set,3 do + local directive,ns,tg=set[i],set[i+1],set[i+2] + 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) + end + if nodetest==false then + return format("not(%s)",concat(t,"|")) + else + return concat(t,"|") + end +end +local function tagstostring(list) + if #list==0 then + return "no elements" + else + local t={} + for i=1,#list do + local li=list[i] + local ns,tg=li.ns,li.tg + if not ns or ns=="" then ns="*" end + if not tg or tg=="" then tg="*" end + t[i]=(tg=="@rt@" and "[root]") or format("%s:%s",ns,tg) + end + return concat(t," ") + end +end +xml.nodesettostring=nodesettostring +local lpath +local lshowoptions={ functions=false } +local function lshow(parsed) + if type(parsed)=="string" then + parsed=lpath(parsed) + end + report_lpath("%s://%s => %s",parsed.protocol or xml.defaultprotocol,parsed.pattern, + table.serialize(parsed,false,lshowoptions)) +end +xml.lshow=lshow +local function add_comment(p,str) + local pc=p.comment + if not pc then + p.comment={ str } + else + pc[#pc+1]=str + end +end +lpath=function (pattern) + lpathcalls=lpathcalls+1 + if type(pattern)=="table" then + return pattern + else + local parsed=cache[pattern] + if parsed then + lpathcached=lpathcached+1 + else + 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" } + report_lpath("parsing error in pattern: %s",pattern) + lshow(parsed) + else + local pi=parsed[1] + if pi.axis=="auto-child" then + if false then + add_comment(parsed,"auto-child replaced by auto-descendant-or-self") + parsed[1]=register_auto_descendant_or_self + else + add_comment(parsed,"auto-child replaced by auto-descendant") + parsed[1]=register_auto_descendant + end + elseif pi.axis=="initial-child" and np>1 and parsed[2].axis then + add_comment(parsed,"initial-child removed") + remove(parsed,1) + end + local np=#parsed + if np>1 then + local pnp=parsed[np] + if pnp.kind=="nodes" and pnp.nodetest==true then + local nodes=pnp.nodes + if nodes[1]==true and nodes[2]==false and nodes[3]==false then + add_comment(parsed,"redundant final wildcard filter removed") + remove(parsed,np) + end + end + end + end + else + parsed={ pattern=pattern } + end + cache[pattern]=parsed + if trace_lparse and not trace_lprofile then + lshow(parsed) + end + end + return parsed + end +end +xml.lpath=lpath +local profiled={} xml.profiled=profiled +local function profiled_apply(list,parsed,nofparsed,order) + local p=profiled[parsed.pattern] + if p then + p.tested=p.tested+1 + else + p={ tested=1,matched=0,finalized=0 } + profiled[parsed.pattern]=p + end + 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) + elseif kind=="nodes" then + collected=apply_nodes(collected,pi.nodetest,pi.nodes) + elseif kind=="expression" then + collected=apply_expression(collected,pi.evaluator,order) + elseif kind=="finalizer" then + collected=pi.finalizer(collected) + p.matched=p.matched+1 + p.finalized=p.finalized+1 + 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) + p.finalized=p.finalized+1 + return collected + end + return nil + end + end + if collected then + p.matched=p.matched+1 + end + return collected +end +local function traced_apply(list,parsed,nofparsed,order) + if trace_lparse then + lshow(parsed) + end + 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) + report_lpath("% 10i : ax : %s",(collected and #collected) or 0,pi.axis) + elseif kind=="nodes" then + collected=apply_nodes(collected,pi.nodetest,pi.nodes) + 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) + report_lpath("% 10i : ex : %s -> %s",(collected and #collected) or 0,pi.expression,pi.converted) + elseif kind=="finalizer" then + collected=pi.finalizer(collected) + 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) + 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 + end + end + return collected +end +local function normal_apply(list,parsed,nofparsed,order) + local collected=list + for i=1,nofparsed do + local pi=parsed[i] + local kind=pi.kind + if kind=="axis" then + local axis=pi.axis + if axis~="self" then + collected=apply_axis[axis](collected) + end + elseif kind=="nodes" then + collected=apply_nodes(collected,pi.nodetest,pi.nodes) + elseif kind=="expression" then + collected=apply_expression(collected,pi.evaluator,order) + elseif kind=="finalizer" then + return pi.finalizer(collected) + end + if not collected or #collected==0 then + local pf=i<nofparsed and parsed[nofparsed].finalizer + if pf then + return pf(collected) + end + return nil + end + end + return collected +end +local function applylpath(list,pattern) + if not list then + return + end + local parsed=cache[pattern] + if parsed then + lpathcalls=lpathcalls+1 + lpathcached=lpathcached+1 + elseif type(pattern)=="table" then + lpathcalls=lpathcalls+1 + parsed=pattern + else + parsed=lpath(pattern) or pattern + end + if not parsed then + return + end + local nofparsed=#parsed + if nofparsed==0 then + return + end + if not trace_lpath then + return normal_apply ({ list },parsed,nofparsed,list.mi) + elseif trace_lprofile then + return profiled_apply({ list },parsed,nofparsed,list.mi) + else + return traced_apply ({ list },parsed,nofparsed,list.mi) + end +end +xml.applylpath=applylpath +function xml.filter(root,pattern) + return applylpath(root,pattern) +end +expressions.child=function(e,pattern) + return applylpath(e,pattern) +end +expressions.count=function(e,pattern) + local collected=applylpath(e,pattern) + return pattern and (collected and #collected) or 0 +end +expressions.oneof=function(s,...) + for i=1,select("#",...) do + if s==select(i,...) then + return true + end + end + return false +end +expressions.error=function(str) + xml.errorhandler(format("unknown function in lpath expression: %s",tostring(str or "?"))) + return false +end +expressions.undefined=function(s) + return s==nil +end +expressions.quit=function(s) + if s or s==nil then + quit_expression=true + end + return true +end +expressions.print=function(...) + print(...) + return true +end +expressions.find=find +expressions.upper=upper +expressions.lower=lower +expressions.number=tonumber +expressions.boolean=toboolean +function expressions.contains(str,pattern) + local t=type(str) + if t=="string" then + if find(str,pattern) then + return true + end + elseif t=="table" then + for i=1,#str do + local d=str[i] + if type(d)=="string" and find(d,pattern) then + return true + end + end + end + return false +end +function xml.expressions.idstring(str) + return type(str)=="string" and gsub(str,"^#","") or "" +end +local function traverse(root,pattern,handle) + local collected=applylpath(root,pattern) + if collected then + for c=1,#collected do + local e=collected[c] + local r=e.__p__ + handle(r,r.dt,e.ni) + end + end +end +local function selection(root,pattern,handle) + local collected=applylpath(root,pattern) + if collected then + if handle then + for c=1,#collected do + handle(collected[c]) + end + else + return collected + end + end +end +xml.traverse=traverse +xml.selection=selection +local function dofunction(collected,fnc,...) + if collected then + local f=functions[fnc] + if f then + for c=1,#collected do + f(collected[c],...) + end + else + report_lpath("unknown function %a",fnc) + end + end +end +finalizers.xml["function"]=dofunction +finalizers.tex["function"]=dofunction +expressions.text=function(e,n) + local rdt=e.__p__.dt + return rdt and rdt[n] or "" +end +expressions.name=function(e,n) + local found=false + n=tonumber(n) or 0 + if n==0 then + found=type(e)=="table" and e + elseif n<0 then + local d,k=e.__p__.dt,e.ni + for i=k-1,1,-1 do + local di=d[i] + if type(di)=="table" then + if n==-1 then + found=di + break + else + n=n+1 + end + end + end + else + local d,k=e.__p__.dt,e.ni + for i=k+1,#d,1 do + local di=d[i] + if type(di)=="table" then + if n==1 then + found=di + break + else + n=n-1 + end + end + end + end + if found then + local ns,tg=found.rn or found.ns or "",found.tg + if ns~="" then + return ns..":"..tg + else + return tg + end + else + return "" + end +end +expressions.tag=function(e,n) + if not e then + return "" + else + local found=false + n=tonumber(n) or 0 + if n==0 then + found=(type(e)=="table") and e + elseif n<0 then + local d,k=e.__p__.dt,e.ni + for i=k-1,1,-1 do + local di=d[i] + if type(di)=="table" then + if n==-1 then + found=di + break + else + n=n+1 + end + end + end + else + local d,k=e.__p__.dt,e.ni + for i=k+1,#d,1 do + local di=d[i] + if type(di)=="table" then + if n==1 then + found=di + break + else + n=n-1 + end + end + end + end + return (found and found.tg) or "" + end +end +local dummy=function() end +function xml.elements(root,pattern,reverse) + local collected=applylpath(root,pattern) + if not collected then + return dummy + end + local n=#collected + if n==0 then + return dummy + end + if reverse then + local c=n+1 + return function() + if c>1 then + c=c-1 + local e=collected[c] + local r=e.__p__ + return r,r.dt,e.ni + end + end + else + local c=0 + return function() + if c<n then + c=c+1 + local e=collected[c] + local r=e.__p__ + return r,r.dt,e.ni + end + end + end +end +function xml.collected(root,pattern,reverse) + local collected=applylpath(root,pattern) + if not collected then + return dummy + end + local n=#collected + if n==0 then + return dummy + end + if reverse then + local c=n+1 + return function() + if c>1 then + c=c-1 + return collected[c] + end + end + else + local c=0 + return function() + if c<n then + c=c+1 + return collected[c] + end + end + end +end +function xml.inspect(collection,pattern) + pattern=pattern or "." + for e in xml.collected(collection,pattern or ".") do + report_lpath("pattern: %s\n\n%s\n",pattern,xml.tostring(e)) + end +end +local function split(e) + local dt=e.dt + if dt then + for i=1,#dt do + local dti=dt[i] + if type(dti)=="string" then + dti=gsub(dti,"^[\n\r]*(.-)[\n\r]*","%1") + dti=gsub(dti,"[\n\r]+","\n\n") + dt[i]=dti + else + split(dti) + end + end + end + return e +end +function xml.finalizers.paragraphs(c) + for i=1,#c do + split(c[i]) + end + return c +end + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["lxml-mis"] = package.loaded["lxml-mis"] or true + +-- original size: 3684, stripped down to: 1957 + +if not modules then modules={} end modules ['lxml-mis']={ + version=1.001, + comment="this module is the basis for the lxml-* ones", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + 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,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 +local function xmlgsub(t,old,new) + local dt=t.dt + if dt then + for k=1,#dt do + local v=dt[k] + if type(v)=="string" then + dt[k]=gsub(v,old,new) + else + xmlgsub(v,old,new) + end + end + end +end +function xml.stripleadingspaces(dk,d,k) + if d and k then + local dkm=d[k-1] + if dkm and type(dkm)=="string" then + local s=match(dkm,"\n(%s+)") + xmlgsub(dk,"\n"..rep(" ",#s),"\n") + end + end +end +local normal=(1-S("<&>"))^0 +local special=P("<")/"<"+P(">")/">"+P("&")/"&" +local escaped=Cs(normal*(special*normal)^0) +local normal=(1-S"&")^0 +local special=P("<")/"<"+P(">")/">"+P("&")/"&" +local unescaped=Cs(normal*(special*normal)^0) +local cleansed=Cs(((P("<")*(1-P(">"))^0*P(">"))/""+1)^0) +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 +function xml.cleansed (str) return lpegmatch(cleansed,str) end +function xml.fillin(root,pattern,str,check) + local e=xml.first(root,pattern) + if e then + local n=#e.dt + if not check or n==0 or (n==1 and e.dt[1]=="") then + e.dt={ str } + end + end +end + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["lxml-aux"] = package.loaded["lxml-aux"] or true + +-- original size: 28786, stripped down to: 20578 + +if not modules then modules={} end modules ['lxml-aux']={ + version=1.001, + comment="this module is the basis for the lxml-* ones", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +local trace_manipulations=false trackers.register("lxml.manipulations",function(v) trace_manipulations=v end) +local trace_inclusions=false trackers.register("lxml.inclusions",function(v) trace_inclusions=v end) +local report_xml=logs.reporter("xml") +local xml=xml +local xmlcopy,xmlname=xml.copy,xml.name +local xmlinheritedconvert=xml.inheritedconvert +local xmlapplylpath=xml.applylpath +local xmlfilter=xml.filter +local type,next,setmetatable,getmetatable=type,next,setmetatable,getmetatable +local insert,remove,fastcopy,concat=table.insert,table.remove,table.fastcopy,table.concat +local gmatch,gsub,format,find,strip=string.gmatch,string.gsub,string.format,string.find,string.strip +local utfbyte=utf.byte +local lpegmatch=lpeg.match +local striplinepatterns=utilities.strings.striplinepatterns +local function report(what,pattern,c,e) + report_xml("%s element %a, root %a, position %a, index %a, pattern %a",what,xmlname(e),xmlname(e.__p__),c,e.ni,pattern) +end +local function withelements(e,handle,depth) + if e and handle then + local edt=e.dt + if edt then + depth=depth or 0 + for i=1,#edt do + local e=edt[i] + if type(e)=="table" then + handle(e,depth) + withelements(e,handle,depth+1) + end + end + end + end +end +xml.withelements=withelements +function xml.withelement(e,n,handle) + if e and n~=0 and handle then + local edt=e.dt + if edt then + if n>0 then + for i=1,#edt do + local ei=edt[i] + if type(ei)=="table" then + if n==1 then + handle(ei) + return + else + n=n-1 + end + end + end + elseif n<0 then + for i=#edt,1,-1 do + local ei=edt[i] + if type(ei)=="table" then + if n==-1 then + handle(ei) + return + else + n=n+1 + end + end + end + end + end + end +end +function xml.each(root,pattern,handle,reverse) + local collected=xmlapplylpath(root,pattern) + if collected then + if handle then + if reverse then + for c=#collected,1,-1 do + handle(collected[c]) + end + else + for c=1,#collected do + handle(collected[c]) + end + end + end + return collected + end +end +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) + end + end + return collected +end +function xml.collect(root,pattern) + return xmlapplylpath(root,pattern) +end +function xml.collecttexts(root,pattern,flatten) + local collected=xmlapplylpath(root,pattern) + if collected and flatten then + local xmltostring=xml.tostring + for c=1,#collected do + collected[c]=xmltostring(collected[c].dt) + end + end + return collected or {} +end +function xml.collect_tags(root,pattern,nonamespace) + local collected=xmlapplylpath(root,pattern) + if collected then + 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[n]=tg + elseif ns=="" then + t[n]=tg + else + t[n]=ns..":"..tg + end + end + return t + end +end +local no_root={ no_root=true } +local function redo_ni(d) + for k=1,#d do + local dk=d[k] + if type(dk)=="table" then + dk.ni=k + end + end +end +xml.reindex=redo_ni +local function xmltoelement(whatever,root) + if not whatever then + return nil + end + local element + if type(whatever)=="string" then + element=xmlinheritedconvert(whatever,root) + else + element=whatever + end + if element.error then + return whatever + end + if element then + end + return element +end +xml.toelement=xmltoelement +local function copiedelement(element,newparent) + if type(element)=="string" then + return element + else + element=xmlcopy(element).dt + if newparent and type(element)=="table" then + element.__p__=newparent + end + return element + end +end +function xml.delete(root,pattern) + if not pattern or pattern=="" then + local p=root.__p__ + if p then + if trace_manipulations then + report('deleting',"--",c,root) + end + local d=p.dt + remove(d,root.ni) + redo_ni(d) + end + else + local collected=xmlapplylpath(root,pattern) + if collected then + for c=1,#collected do + local e=collected[c] + local p=e.__p__ + if p then + if trace_manipulations then + report('deleting',pattern,c,e) + end + local d=p.dt + local ni=e.ni + if ni<=#d then + if false then + p.dt[ni]="" + else + remove(d,ni) + redo_ni(d) + end + else + end + end + end + end + end +end +function xml.replace(root,pattern,whatever) + local element=root and xmltoelement(whatever,root) + local collected=element and xmlapplylpath(root,pattern) + if collected then + for c=1,#collected do + local e=collected[c] + local p=e.__p__ + if p then + if trace_manipulations then + report('replacing',pattern,c,e) + end + local d=p.dt + d[e.ni]=copiedelement(element,p) + redo_ni(d) + 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 xmlapplylpath(root,pattern) + local function inject_e(e) + local r=e.__p__ + local d,k,rri=r.dt,e.ni,r.ri + local edt=(rri and d[rri].dt) or (d and d[k] and d[k].dt) + if edt then + local be,af + local cp=copiedelement(element,e) + if prepend then + be,af=cp,edt + else + be,af=edt,cp + end + local bn=#be + for i=1,#af do + bn=bn+1 + be[bn]=af[i] + end + if rri then + r.dt[rri].dt=be + else + d[k].dt=be + end + redo_ni(d) + end + end + if not collected then + elseif collected.tg then + inject_e(collected) + else + for c=1,#collected do + inject_e(collected[c]) + end + end +end +local function insert_element(root,pattern,whatever,before) + local element=root and xmltoelement(whatever,root) + local collected=element and xmlapplylpath(root,pattern) + local function insert_e(e) + local r=e.__p__ + local d,k=r.dt,e.ni + if not before then + k=k+1 + end + insert(d,k,copiedelement(element,r)) + redo_ni(d) + end + if not collected then + elseif collected.tg then + insert_e(collected) + else + for c=1,#collected do + insert_e(collected[c]) + end + end +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,level) + pattern=pattern or 'include' + loaddata=loaddata or io.loaddata + local collected=xmlapplylpath(xmldata,pattern) + if collected then + if not level then + level=1 + end + for c=1,#collected do + local ek=collected[c] + local name=nil + local ekdt=ek.dt + local ekat=ek.at + local ekrt=ek.__p__ + local epdt=ekrt.dt + if not attribute or attribute=="" then + name=(type(ekdt)=="table" and ekdt[1]) or ekdt + end + if not name then + for a in gmatch(attribute or "href","([^|]+)") do + name=ekat[a] + if name then + break + end + end + end + local data=nil + if name and name~="" then + data=loaddata(name) or "" + if trace_inclusions then + report_xml("including %s bytes from %a at level %s by pattern %a and attribute %a (%srecursing)",#data,name,level,pattern,attribute or "",recursive and "" or "not ") + end + end + if not data or data=="" then + epdt[ek.ni]="" + elseif ekat["parse"]=="text" then + epdt[ek.ni]=xml.escaped(data) + else + local xi=xmlinheritedconvert(data,xmldata) + if not xi then + epdt[ek.ni]="" + else + if recursive then + include(xi,pattern,attribute,recursive,loaddata,level+1) + end + local child=xml.body(xi) + child.__p__=ekrt + child.__f__=name + epdt[ek.ni]=child + local inclusions=xmldata.settings.inclusions + if inclusions then + inclusions[#inclusions+1]=name + else + xmldata.settings.inclusions={ name } + end + if child.er then + local badinclusions=xmldata.settings.badinclusions + if badinclusions then + badinclusions[#badinclusions+1]=name + else + xmldata.settings.badinclusions={ name } + end + end + end + end + end + end +end +xml.include=include +function xml.inclusion(e,default) + while e do + local f=e.__f__ + if f then + return f + else + e=e.__p__ + end + end + return default +end +local function getinclusions(key,e,sorted) + while e do + local settings=e.settings + if settings then + local inclusions=settings[key] + if inclusions then + inclusions=table.unique(inclusions) + if sorted then + table.sort(inclusions) + end + return inclusions + else + e=e.__p__ + end + else + e=e.__p__ + end + end +end +function xml.inclusions(e,sorted) + return getinclusions("inclusions",e,sorted) +end +function xml.badinclusions(e,sorted) + return getinclusions("badinclusions",e,sorted) +end +local b_collapser=lpeg.patterns.b_collapser +local m_collapser=lpeg.patterns.m_collapser +local e_collapser=lpeg.patterns.e_collapser +local b_stripper=lpeg.patterns.b_stripper +local m_stripper=lpeg.patterns.m_stripper +local e_stripper=lpeg.patterns.e_stripper +local lpegmatch=lpeg.match +local function stripelement(e,nolines,anywhere) + local edt=e.dt + if edt then + local n=#edt + if n==0 then + return e + elseif anywhere then + local t={} + local m=0 + for e=1,n do + local str=edt[e] + if type(str)~="string" then + m=m+1 + t[m]=str + elseif str~="" then + if nolines then + str=lpegmatch((n==1 and b_collapser) or (n==m and e_collapser) or m_collapser,str) + else + str=lpegmatch((n==1 and b_stripper) or (n==m and e_stripper) or m_stripper,str) + end + if str~="" then + m=m+1 + t[m]=str + end + end + end + e.dt=t + else + local str=edt[1] + if type(str)=="string" then + if str~="" then + str=lpegmatch(nolines and b_collapser or b_stripper,str) + end + if str=="" then + remove(edt,1) + n=n-1 + else + edt[1]=str + end + end + if n>0 then + str=edt[n] + if type(str)=="string" then + if str=="" then + remove(edt) + else + str=lpegmatch(nolines and e_collapser or e_stripper,str) + if str=="" then + remove(edt) + else + edt[n]=str + end + end + end + end + end + end + return e +end +xml.stripelement=stripelement +function xml.strip(root,pattern,nolines,anywhere) + local collected=xmlapplylpath(root,pattern) + if collected then + for i=1,#collected do + stripelement(collected[i],nolines,anywhere) + end + end +end +local function renamespace(root,oldspace,newspace) + local ndt=#root.dt + for i=1,ndt or 0 do + local e=root[i] + if type(e)=="table" then + if e.ns==oldspace then + e.ns=newspace + if e.rn then + e.rn=newspace + end + end + local edt=e.dt + if edt then + renamespace(edt,oldspace,newspace) + end + end + end +end +xml.renamespace=renamespace +function xml.remaptag(root,pattern,newtg) + local collected=xmlapplylpath(root,pattern) + if collected then + for c=1,#collected do + collected[c].tg=newtg + end + end +end +function xml.remapnamespace(root,pattern,newns) + local collected=xmlapplylpath(root,pattern) + if collected then + for c=1,#collected do + collected[c].ns=newns + end + end +end +function xml.checknamespace(root,pattern,newns) + local collected=xmlapplylpath(root,pattern) + if collected then + for c=1,#collected do + local e=collected[c] + if (not e.rn or e.rn=="") and e.ns=="" then + e.rn=newns + end + end + end +end +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] + e.tg,e.ns,e.rn=newtg,newns,newrn + end + end +end +function xml.cdatatotext(e) + local dt=e.dt + if #dt==1 then + local first=dt[1] + if first.tg=="@cd@" then + e.dt=first.dt + end + else + end +end +function xml.texttocdata(e) + local dt=e.dt + local s=xml.tostring(dt) + e.tg="@cd@" + e.special=true + e.ns="" + e.rn="" + e.dt={ s } + e.at=nil +end +function xml.elementtocdata(e) + local dt=e.dt + local s=xml.tostring(e) + e.tg="@cd@" + e.special=true + e.ns="" + e.rn="" + e.dt={ s } + e.at=nil +end +xml.builtinentities=table.tohash { "amp","quot","apos","lt","gt" } +local entities=characters and characters.entities or nil +local builtinentities=xml.builtinentities +function xml.addentitiesdoctype(root,option) + if not entities then + require("char-ent") + entities=characters.entities + end + if entities and root and root.tg=="@rt@" and root.statistics then + local list={} + local hexify=option=="hexadecimal" + for k,v in table.sortedhash(root.statistics.entities.names) do + if not builtinentities[k] then + local e=entities[k] + if not e then + e=format("[%s]",k) + elseif hexify then + e=format("&#%05X;",utfbyte(k)) + end + list[#list+1]=format(" <!ENTITY %s %q >",k,e) + end + end + local dt=root.dt + local n=dt[1].tg=="@pi@" and 2 or 1 + if #list>0 then + insert(dt,n,{ "\n" }) + insert(dt,n,{ + tg="@dt@", + dt={ format("Something [\n%s\n] ",concat(list)) }, + ns="", + special=true, + }) + insert(dt,n,{ "\n\n" }) + else + end + end +end +xml.all=xml.each +xml.insert=xml.insertafter +xml.inject=xml.injectafter +xml.after=xml.insertafter +xml.before=xml.insertbefore +xml.process=xml.each +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 +function xml.cdata(e) + if e then + local dt=e.dt + if dt and #dt==1 then + local first=dt[1] + return first.tg=="@cd@" and first.dt[1] or "" + end + end + return "" +end +function xml.finalizers.xml.cdata(collected) + if collected then + local e=collected[1] + if e then + local dt=e.dt + if dt and #dt==1 then + local first=dt[1] + return first.tg=="@cd@" and first.dt[1] or "" + end + end + end + return "" +end +function xml.insertcomment(e,str,n) + insert(e.dt,n or 1,{ + tg="@cm@", + ns="", + special=true, + at={}, + dt={ str }, + }) +end +function xml.insertcdata(e,str,n) + insert(e.dt,n or 1,{ + tg="@cd@", + ns="", + special=true, + at={}, + dt={ str }, + }) +end +function xml.setcomment(e,str,n) + e.dt={ { + tg="@cm@", + ns="", + special=true, + at={}, + dt={ str }, + } } +end +function xml.setcdata(e,str) + e.dt={ { + tg="@cd@", + ns="", + special=true, + at={}, + dt={ str }, + } } +end +function xml.separate(x,pattern) + local collected=xmlapplylpath(x,pattern) + if collected then + for c=1,#collected do + local e=collected[c] + local d=e.dt + if d==x then + report_xml("warning: xml.separate changes root") + x=d + end + local t,n={ "\n" },1 + local i,nd=1,#d + while i<=nd do + while i<=nd do + local di=d[i] + if type(di)=="string" then + if di=="\n" or find(di,"^%s+$") then + i=i+1 + else + d[i]=strip(di) + break + end + else + break + end + end + if i>nd then + break + end + t[n+1]="\n" + t[n+2]=d[i] + t[n+3]="\n" + n=n+3 + i=i+1 + end + t[n+1]="\n" + setmetatable(t,getmetatable(d)) + e.dt=t + end + end + return x +end +local helpers=xml.helpers or {} +xml.helpers=helpers +local function normal(e,action) + local edt=e.dt + if edt then + for i=1,#edt do + local str=edt[i] + if type(str)=="string" and str~="" then + edt[i]=action(str) + end + end + end +end +local function recurse(e,action) + local edt=e.dt + if edt then + for i=1,#edt do + local str=edt[i] + if type(str)~="string" then + recurse(str,action) + elseif str~="" then + edt[i]=action(str) + end + end + end +end +function helpers.recursetext(collected,action,recursive) + if recursive then + for i=1,#collected do + recurse(collected[i],action) + end + else + for i=1,#collected do + normal(collected[i],action) + end + end +end +local specials={ + ["@rt@"]="root", + ["@pi@"]="instruction", + ["@cm@"]="comment", + ["@dt@"]="declaration", + ["@cd@"]="cdata", +} +local function convert(x,strip,flat) + local ns=x.ns + local tg=x.tg + local at=x.at + local dt=x.dt + local node=flat and { + [0]=(not x.special and (ns~="" and ns..":"..tg or tg)) or nil, + } or { + _namespace=ns~="" and ns or nil, + _tag=not x.special and tg or nil, + _type=specials[tg] or "_element", + } + if at then + for k,v in next,at do + node[k]=v + end + end + local n=0 + for i=1,#dt do + local di=dt[i] + if type(di)=="table" then + if flat and di.special then + else + di=convert(di,strip,flat) + if di then + n=n+1 + node[n]=di + end + end + elseif strip then + di=lpegmatch(strip,di) + if di~="" then + n=n+1 + node[n]=di + end + else + n=n+1 + node[n]=di + end + end + if next(node) then + return node + end +end +function xml.totable(x,strip,flat) + if type(x)=="table" then + if strip then + strip=striplinepatterns[strip] + end + return convert(x,strip,flat) + end +end + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["lxml-xml"] = package.loaded["lxml-xml"] or true + +-- original size: 10274, stripped down to: 7538 + +if not modules then modules={} end modules ['lxml-xml']={ + version=1.001, + comment="this module is the basis for the lxml-* ones", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +local concat=table.concat +local find,lower,upper=string.find,string.lower,string.upper +local xml=xml +local finalizers=xml.finalizers.xml +local xmlfilter=xml.filter +local xmltostring=xml.tostring +local xmlserialize=xml.serialize +local xmlcollected=xml.collected +local xmlnewhandlers=xml.newhandlers +local function first(collected) + return collected and collected[1] +end +local function last(collected) + return collected and collected[#collected] +end +local function all(collected) + return collected +end +local reverse=table.reversed +local function attribute(collected,name) + if collected and #collected>0 then + local at=collected[1].at + return at and at[name] + end +end +local function att(id,name) + local at=id.at + return at and at[name] +end +local function count(collected) + return collected and #collected or 0 +end +local function position(collected,n) + if not collected then + return 0 + end + local nc=#collected + if nc==0 then + return 0 + end + n=tonumber(n) or 0 + if n<0 then + return collected[nc+n+1] + elseif n>0 then + return collected[n] + else + return collected[1].mi or 0 + end +end +local function match(collected) + return collected and #collected>0 and collected[1].mi or 0 +end +local function index(collected) + return collected and #collected>0 and collected[1].ni or 0 +end +local function attributes(collected,arguments) + if collected and #collected>0 then + local at=collected[1].at + if arguments then + return at[arguments] + elseif next(at) then + return at + end + end +end +local function chainattribute(collected,arguments) + if collected and #collected>0 then + local e=collected[1] + while e do + local at=e.at + if at then + local a=at[arguments] + if a then + return a + end + else + break + end + e=e.__p__ + end + end + return "" +end +local function raw(collected) + if collected and #collected>0 then + local e=collected[1] or collected + return e and xmltostring(e) or "" + else + return "" + end +end +local xmltexthandler=xmlnewhandlers { + name="string", + initialize=function() + result={} + return result + end, + finalize=function() + return concat(result) + end, + handle=function(...) + result[#result+1]=concat {... } + end, + escape=false, +} +local function xmltotext(root) + local dt=root.dt + if not dt then + return "" + end + local nt=#dt + if nt==0 then + return "" + elseif nt==1 and type(dt[1])=="string" then + return dt[1] + else + return xmlserialize(root,xmltexthandler) or "" + end +end +local function text(collected) + if collected then + local e=collected[1] or collected + return e and xmltotext(e) or "" + else + return "" + end +end +local function texts(collected) + if not collected then + return {} + end + local nc=#collected + if nc==0 then + return {} + end + local t,n={},0 + for c=1,nc do + local e=collected[c] + if e and e.dt then + n=n+1 + t[n]=e.dt + end + end + return t +end +local function tag(collected,n) + if not collected then + return + end + local nc=#collected + if nc==0 then + return + end + local c + if n==0 or not n then + c=collected[1] + elseif n>1 then + c=collected[n] + else + c=collected[nc-n+1] + end + return c and c.tg +end +local function name(collected,n) + if not collected then + return + end + local nc=#collected + if nc==0 then + return + end + local c + if n==0 or not n then + c=collected[1] + elseif n>1 then + c=collected[n] + else + c=collected[nc-n+1] + end + if not c then + elseif c.ns=="" then + return c.tg + else + return c.ns..":"..c.tg + end +end +local function tags(collected,nonamespace) + if not collected then + return + end + local nc=#collected + if nc==0 then + return + end + local t,n={},0 + for c=1,nc do + local e=collected[c] + local ns,tg=e.ns,e.tg + n=n+1 + if nonamespace or ns=="" then + t[n]=tg + else + t[n]=ns..":"..tg + end + end + return t +end +local function empty(collected,spacesonly) + if not collected then + return true + end + local nc=#collected + if nc==0 then + return true + end + for c=1,nc do + local e=collected[c] + if e then + local edt=e.dt + if edt then + local n=#edt + if n==1 then + local edk=edt[1] + local typ=type(edk) + if typ=="table" then + return false + elseif edk~="" then + return false + elseif spacesonly and not find(edk,"%S") then + return false + end + elseif n>1 then + return false + end + end + end + end + return true +end +finalizers.first=first +finalizers.last=last +finalizers.all=all +finalizers.reverse=reverse +finalizers.elements=all +finalizers.default=all +finalizers.attribute=attribute +finalizers.att=att +finalizers.count=count +finalizers.position=position +finalizers.match=match +finalizers.index=index +finalizers.attributes=attributes +finalizers.chainattribute=chainattribute +finalizers.text=text +finalizers.texts=texts +finalizers.tag=tag +finalizers.name=name +finalizers.tags=tags +finalizers.empty=empty +function xml.first(id,pattern) + return first(xmlfilter(id,pattern)) +end +function xml.last(id,pattern) + return last(xmlfilter(id,pattern)) +end +function xml.count(id,pattern) + return count(xmlfilter(id,pattern)) +end +function xml.attribute(id,pattern,a,default) + return attribute(xmlfilter(id,pattern),a,default) +end +function xml.raw(id,pattern) + if pattern then + return raw(xmlfilter(id,pattern)) + else + return raw(id) + end +end +function xml.text(id,pattern) + if pattern then + local collected=xmlfilter(id,pattern) + return collected and #collected>0 and xmltotext(collected[1]) or "" + elseif id then + return xmltotext(id) or "" + else + return "" + end +end +xml.content=text +function xml.position(id,pattern,n) + return position(xmlfilter(id,pattern),n) +end +function xml.match(id,pattern) + return match(xmlfilter(id,pattern)) +end +function xml.empty(id,pattern,spacesonly) + return empty(xmlfilter(id,pattern),spacesonly) +end +xml.all=xml.filter +xml.index=xml.position +xml.found=xml.filter +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) + return concat(textonly(e,{})) +end +function finalizers.lowerall(collected) + for c=1,#collected do + local e=collected[c] + if not e.special then + e.tg=lower(e.tg) + local eat=e.at + if eat then + local t={} + for k,v in next,eat do + t[lower(k)]=v + end + e.at=t + end + end + end +end +function finalizers.upperall(collected) + for c=1,#collected do + local e=collected[c] + if not e.special then + e.tg=upper(e.tg) + local eat=e.at + if eat then + local t={} + for k,v in next,eat do + t[upper(k)]=v + end + e.at=t + end + end + end +end + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["trac-xml"] = package.loaded["trac-xml"] or true + +-- original size: 6351, stripped down to: 4919 + +if not modules then modules={} end modules ['trac-xml']={ + 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" +} +local formatters=string.formatters +local reporters=logs.reporters +local xmlserialize=xml.serialize +local xmlcollected=xml.collected +local xmltext=xml.text +local xmlfirst=xml.first +local function showhelp(specification,...) + local root=xml.convert(specification.helpinfo or "") + if not root then + return + end + local xs=xml.gethandlers("string") + xml.sethandlersfunction(xs,"short",function(e,handler) xmlserialize(e.dt,handler) end) + xml.sethandlersfunction(xs,"ref",function(e,handler) handler.handle("--"..e.at.name) end) + local wantedcategories=select("#",...)==0 and true or table.tohash {... } + local nofcategories=xml.count(root,"/application/flags/category") + local report=specification.report + for category in xmlcollected(root,"/application/flags/category") do + local categoryname=category.at.name or "" + if wantedcategories==true or wantedcategories[categoryname] then + if nofcategories>1 then + report("%s options:",categoryname) + report() + end + for subcategory in xmlcollected(category,"/subcategory") do + for flag in xmlcollected(subcategory,"/flag") do + local name=flag.at.name + local value=flag.at.value + local short=xmltext(xmlfirst(flag,"/short")) + if value then + report("--%-20s %s",formatters["%s=%s"](name,value),short) + else + report("--%-20s %s",name,short) + end + end + report() + end + end + end + for category in xmlcollected(root,"/application/examples/category") do + local title=xmltext(xmlfirst(category,"/title")) + if title and title~="" then + report() + report(title) + report() + end + for subcategory in xmlcollected(category,"/subcategory") do + for example in xmlcollected(subcategory,"/example") do + local command=xmltext(xmlfirst(example,"/command")) + local comment=xmltext(xmlfirst(example,"/comment")) + report(command) + end + report() + end + end + for comment in xmlcollected(root,"/application/comments/comment") do + local comment=xmltext(comment) + report() + report(comment) + report() + end +end +local reporthelp=reporters.help +local exporthelp=reporters.export +local function xmlfound(t) + local helpinfo=t.helpinfo + if type(helpinfo)=="table" then + return false + end + if type(helpinfo)~="string" then + helpinfo="Warning: no helpinfo found." + t.helpinfo=helpinfo + return false + end + if string.find(helpinfo,".xml$") then + local ownscript=environment.ownscript + local helpdata=false + if ownscript then + local helpfile=file.join(file.pathpart(ownscript),helpinfo) + helpdata=io.loaddata(helpfile) + if helpdata=="" then + helpdata=false + end + end + if not helpdata then + local helpfile=resolvers.findfile(helpinfo,"tex") + helpdata=helpfile and io.loaddata(helpfile) + end + if helpdata and helpdata~="" then + helpinfo=helpdata + else + helpinfo=formatters["Warning: help file %a is not found."](helpinfo) + end + end + t.helpinfo=helpinfo + return string.find(t.helpinfo,"^<%?xml") and true or false +end +function reporters.help(t,...) + if xmlfound(t) then + showhelp(t,...) + else + reporthelp(t,...) + end +end +function reporters.export(t,methods,filename) + if not xmlfound(t) then + return exporthelp(t) + end + if not methods or methods=="" then + methods=environment.arguments["exporthelp"] + end + if not filename or filename=="" then + filename=environment.files[1] + end + dofile(resolvers.findfile("trac-exp.lua","tex")) + local exporters=logs.exporters + if not exporters or not methods then + return exporthelp(t) + end + if methods=="all" then + methods=table.keys(exporters) + elseif type(methods)=="string" then + methods=utilities.parsers.settings_to_array(methods) + else + return exporthelp(t) + end + if type(filename)~="string" or filename=="" then + filename=false + elseif file.pathpart(filename)=="" then + t.report("export file %a will not be saved on the current path (safeguard)",filename) + return + end + for i=1,#methods do + local method=methods[i] + local exporter=exporters[method] + if exporter then + local result=exporter(t,method) + if result and result~="" then + if filename then + local fullname=file.replacesuffix(filename,method) + t.report("saving export in %a",fullname) + io.savedata(fullname,result) + else + reporters.lines(t,result) + end + else + t.report("no output from exporter %a",method) + end + else + t.report("unknown exporter %a",method) + end + end +end + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["data-ini"] = package.loaded["data-ini"] or true + +-- original size: 11085, stripped down to: 7662 + +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", +} +local next,type,getmetatable,rawset=next,type,getmetatable,rawset +local gsub,find,gmatch,char=string.gsub,string.find,string.gmatch,string.char +local filedirname,filebasename,filejoin=file.dirname,file.basename,file.join +local ostype,osname,osuname,ossetenv,osgetenv=os.type,os.name,os.uname,os.setenv,os.getenv +local P,S,R,C,Cs,Cc,lpegmatch=lpeg.P,lpeg.S,lpeg.R,lpeg.C,lpeg.Cs,lpeg.Cc,lpeg.match +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 report_initialization=logs.reporter("resolvers","initialization") +resolvers=resolvers or {} +local resolvers=resolvers +texconfig.kpse_init=false +texconfig.shell_escape='t' +if not (environment and environment.default_texmfcnf) and kpse and kpse.default_texmfcnf then + local default_texmfcnf=kpse.default_texmfcnf() + default_texmfcnf=gsub(default_texmfcnf,"$SELFAUTOLOC","selfautoloc:") + default_texmfcnf=gsub(default_texmfcnf,"$SELFAUTODIR","selfautodir:") + default_texmfcnf=gsub(default_texmfcnf,"$SELFAUTOPARENT","selfautoparent:") + default_texmfcnf=gsub(default_texmfcnf,"$HOME","home:") + environment.default_texmfcnf=default_texmfcnf +end +kpse={ original=kpse } +setmetatable(kpse,{ + __index=function(kp,name) + report_initialization("fatal error: kpse library is accessed (key: %s)",name) + os.exit() + end +} ) +do + local osfontdir=osgetenv("OSFONTDIR") + if osfontdir and osfontdir~="" then + elseif osname=="windows" then + ossetenv("OSFONTDIR","c:/windows/fonts//") + elseif osname=="macosx" then + ossetenv("OSFONTDIR","$HOME/Library/Fonts//;/Library/Fonts//;/System/Library/Fonts//") + end +end +do + local homedir=osgetenv(ostype=="windows" and 'USERPROFILE' or 'HOME') or '' + if not homedir or homedir=="" then + homedir=char(127) + end + homedir=file.collapsepath(homedir) + ossetenv("HOME",homedir) + ossetenv("USERPROFILE",homedir) + environment.homedir=homedir +end +do + local args=environment.originalarguments or arg + if not environment.ownmain then + environment.ownmain=status and string.match(string.lower(status.banner),"this is ([%a]+)") or "luatex" + end + 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 + 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 + local olddir=lfs.currentdir() + if lfs.chdir(p) then + local pp=lfs.currentdir() + if trace_locating and p~=pp then + report_initialization("following symlink %a to %a",p,pp) + end + ownpath=pp + lfs.chdir(olddir) + else + if trace_locating then + report_initialization("unable to check path %a",p) + end + ownpath=p + end + break + end + end + end + end + if not ownpath or ownpath=="" then + ownpath="." + report_initialization("forcing fallback to ownpath %a",ownpath) + elseif trace_locating then + report_initialization("using ownpath %a",ownpath) + end + end + environment.ownbin=ownbin + environment.ownpath=ownpath +end +resolvers.ownpath=environment.ownpath +function resolvers.getownpath() + return environment.ownpath +end +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 + report_initialization("error: unable to locate ownpath") + os.exit() + end +end +local texos=environment.texos or osgetenv("TEXOS") +local texmfos=environment.texmfos or osgetenv('SELFAUTODIR') +if not texos or texos=="" then + texos=file.basename(texmfos) +end +ossetenv('TEXMFOS',texmfos) +ossetenv('TEXOS',texos) +ossetenv('SELFAUTOSYSTEM',os.platform) +environment.texos=texos +environment.texmfos=texmfos +local texroot=environment.texroot or osgetenv("TEXROOT") +if not texroot or texroot=="" then + texroot=osgetenv('SELFAUTOPARENT') + ossetenv('TEXROOT',texroot) +end +environment.texroot=file.collapsepath(texroot) +if type(profiler)=="table" and not jit then + directives.register("system.profile",function() + profiler.start("luatex-profile.log") + end) +end +local prefixes=utilities.storage.allocate() +resolvers.prefixes=prefixes +local resolved={} +local abstract={} +local dynamic={} +function resolvers.resetresolve(str) + resolved,abstract={},{} +end +function resolvers.allprefixes(separator) + local all=table.sortedkeys(prefixes) + if separator then + for i=1,#all do + all[i]=all[i]..":" + end + end + return all +end +local function _resolve_(method,target) + local action=prefixes[method] + if action then + return action(target) + else + return method..":"..target + end +end +function resolvers.unresolve(str) + return abstract[str] or str +end +function resolvers.setdynamic(str) + dynamic[str]=true +end +local pattern=Cs((C(R("az")^2)*P(":")*C((1-S(" \"\';,"))^1)/_resolve_+P(1))^0) +local prefix=C(R("az")^2)*P(":") +local target=C((1-S(" \"\';,"))^1) +local notarget=(#S(";,")+P(-1))*Cc("") +local p_resolve=Cs(((prefix*(target+notarget))/_resolve_+P(1))^0) +local p_simple=prefix*P(-1) +local function resolve(str) + if type(str)=="table" then + local res={} + for i=1,#str do + res[i]=resolve(str[i]) + end + return res + end + local res=resolved[str] + if res then + return res + end + local simple=lpegmatch(p_simple,str) + local action=prefixes[simple] + if action then + local res=action(res) + if not dynamic[simple] then + resolved[simple]=res + abstract[res]=simple + end + return res + end + res=lpegmatch(p_resolve,str) + resolved[str]=res + abstract[res]=str + return res +end +resolvers.resolve=resolve +if type(osuname)=="function" then + for k,v in next,osuname() do + if not prefixes[k] then + prefixes[k]=function() return v end + end + end +end +if ostype=="unix" then + local pattern + local function makepattern(t,k,v) + if t then + rawset(t,k,v) + end + local colon=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) + end + makepattern() + table.setmetatablenewindex(prefixes,makepattern) + function resolvers.repath(str) + return lpegmatch(pattern,str) + end +else + function resolvers.repath(str) + return str + end +end + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["data-exp"] = package.loaded["data-exp"] or true + +-- original size: 17216, stripped down to: 10657 + +if not modules then modules={} end modules ['data-exp']={ + 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 format,find,gmatch,lower,char,sub=string.format,string.find,string.gmatch,string.lower,string.char,string.sub +local concat,sort=table.concat,table.sort +local lpegmatch,lpegpatterns=lpeg.match,lpeg.patterns +local Ct,Cs,Cc,Carg,P,C,S=lpeg.Ct,lpeg.Cs,lpeg.Cc,lpeg.Carg,lpeg.P,lpeg.C,lpeg.S +local type,next=type,next +local isdir=lfs.isdir +local ostype=os.type +local collapsepath,joinpath,basename=file.collapsepath,file.join,file.basename +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) +local trace_globbing=true trackers.register("resolvers.globbing",function(v) trace_globbing=v end) +local report_expansions=logs.reporter("resolvers","expansions") +local report_globbing=logs.reporter("resolvers","globbing") +local resolvers=resolvers +local resolveprefix=resolvers.resolve +local function f_both(a,b) + local t,n={},0 + for sb in gmatch(b,"[^,]+") do + for sa in gmatch(a,"[^,]+") do + n=n+1;t[n]=sa..sb + end + end + return concat(t,",") +end +local comma=P(",") +local nocomma=(1-comma)^1 +local docomma=comma^1/"," +local before=Cs((nocomma*Carg(1)+docomma)^0) +local after=Cs((Carg(1)*nocomma+docomma)^0) +local both=Cs(((C(nocomma)*Carg(1))/function(a,b) return lpegmatch(before,b,1,a) end+docomma)^0) +local function f_first (a,b) return lpegmatch(after,b,1,a) end +local function f_second(a,b) return lpegmatch(before,a,1,b) end +local function f_both (a,b) return lpegmatch(both,b,1,a) end +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) + if trace_expansions then + report_expansions("expanding variable %a",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 + 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 + if trace_expansions then + for k=1,#t do + report_expansions("% 4i: %s",k,t[k]) + end + end + return t +end +local function validate(s) + s=collapsepath(s) + return s~="" and not find(s,"^!*unset/*$") and s +end +resolvers.validatedpath=validate +function resolvers.expandedpathfromlist(pathlist) + local newlist={} + for k=1,#pathlist do + splitpathexpr(pathlist[k],newlist,validate) + end + return newlist +end +local usedhomedir=nil +local donegation=(P("!")/"" )^0 +local doslashes=(P("\\")/"/"+1)^0 +local function expandedhome() + if not usedhomedir then + usedhomedir=lpegmatch(Cs(donegation*doslashes),environment.homedir or "") + if usedhomedir=="~" or usedhomedir=="" or not isdir(usedhomedir) then + if trace_expansions then + report_expansions("no home dir set, ignoring dependent path using current path") + end + usedhomedir="." + end + end + return usedhomedir +end +local dohome=((P("~")+P("$HOME")+P("%HOME%"))/expandedhome)^0 +local cleanup=Cs(donegation*dohome*doslashes) +resolvers.cleanpath=function(str) + return str and lpegmatch(cleanup,str) or "" +end +local expandhome=P("~")/"$HOME" +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) + return type(str)=="string" and lpegmatch(stripper,str) or str +end +local cache={} +local splitter=lpeg.tsplitat(";") +local backslashswapper=lpeg.replacer("\\","/") +local function splitconfigurationpath(str) + if str then + local found=cache[str] + if not found then + if str=="" then + found={} + else + local split=lpegmatch(splitter,lpegmatch(backslashswapper,str)) + 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 %a",str) + for k=1,noffound do + report_expansions("% 4i: %s",k,found[k]) + end + end + cache[str]=found + end + end + return found + end +end +resolvers.splitconfigurationpath=splitconfigurationpath +function resolvers.splitpath(str) + if type(str)=='table' then + return str + else + return splitconfigurationpath(str) + end +end +function resolvers.joinpath(str) + if type(str)=='table' then + return joinpath(str) + else + return str + end +end +local attributes,directory=lfs.attributes,lfs.dir +local weird=P(".")^1+lpeg.anywhere(S("~`!#$%^&*()={}[]:;\"\'||<>,?\n\r\t")) +local lessweird=P(".")^1+lpeg.anywhere(S("~`#$%^&*:;\"\'||<>,?\n\r\t")) +local timer={} +local scanned={} +local nofscans=0 +local scancache={} +local fullcache={} +local nofsharedscans=0 +local function scan(files,remap,spec,path,n,m,r,onlyone,tolerant) + local full=path=="" and spec or (spec..path..'/') + local dirs={} + local nofdirs=0 + local pattern=tolerant and lessweird or weird + for name in directory(full) do + if not lpegmatch(pattern,name) then + local mode=attributes(full..name,"mode") + if mode=="file" then + n=n+1 + local lower=lower(name) + local paths=files[lower] + if paths then + if onlyone then + else + if type(paths)=="string" then + files[lower]={ paths,path } + else + paths[#paths+1]=path + end + if name~=lower then + local rl=remap[lower] + if not rl then + remap[lower]=name + r=r+1 + elseif trace_globbing and rl~=name then + report_globbing("confusing filename, name: %a, lower: %a, already: %a",name,lower,rl) + end + end + end + else + files[lower]=path + if name~=lower then + local rl=remap[lower] + if not rl then + remap[lower]=name + r=r+1 + elseif trace_globbing and rl~=name then + report_globbing("confusing filename, name: %a, lower: %a, already: %a",name,lower,rl) + end + 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,remap,n,m,r=scan(files,remap,spec,dirs[i],n,m,r,onlyonce,tolerant) + end + end + scancache[sub(full,1,-2)]=files + return files,remap,n,m,r +end +function resolvers.scanfiles(path,branch,usecache,onlyonce,tolerant) + local realpath=resolveprefix(path) + if usecache then + local content=fullcache[realpath] + if content then + if trace_locating then + report_expansions("using cached scan of path %a, branch %a",path,branch or path) + end + nofsharedscans=nofsharedscans+1 + return content + end + end + statistics.starttiming(timer) + if trace_locating then + report_expansions("scanning path %a, branch %a",path,branch or path) + end + local content + if isdir(realpath) then + local files,remap,n,m,r=scan({},{},realpath..'/',"",0,0,0,onlyonce,tolerant) + content={ + metadata={ + path=path, + files=n, + directories=m, + remappings=r, + }, + files=files, + remap=remap, + } + if trace_locating then + report_expansions("%s files found on %s directories with %s uppercase remappings",n,m,r) + end + else + content={ + metadata={ + path=path, + files=0, + directories=0, + remappings=0, + }, + files={}, + remap={}, + } + if trace_locating then + report_expansions("invalid path %a",realpath) + end + end + if usecache then + scanned[#scanned+1]=realpath + fullcache[realpath]=content + end + nofscans=nofscans+1 + statistics.stoptiming(timer) + return content +end +function resolvers.simplescanfiles(path,branch,usecache) + return resolvers.scanfiles(path,branch,usecache,true,true) +end +function resolvers.scandata() + table.sort(scanned) + return { + n=nofscans, + shared=nofsharedscans, + time=statistics.elapsedtime(timer), + paths=scanned, + } +end +function resolvers.get_from_content(content,path,name) + if not content then + return + end + local files=content.files + if not files then + return + end + local remap=content.remap + if not remap then + return + end + if name then + local used=lower(name) + return path,remap[used] or used + else + local name=path + local used=lower(name) + local path=files[used] + if path then + return path,remap[used] or used + end + end +end +local nothing=function() end +function resolvers.filtered_from_content(content,pattern) + if content and type(pattern)=="string" then + local pattern=lower(pattern) + local files=content.files + local remap=content.remap + if files and remap then + local n=next(files) + local function iterator() + while n do + local k=n + n=next(files,k) + if find(k,pattern) then + return files[k],remap and remap[k] or k + end + end + end + return iterator + end + end + return nothing +end + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["data-env"] = package.loaded["data-env"] or true + +-- original size: 9216, stripped down to: 6798 + +if not modules then modules={} end modules ['data-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", +} +local lower,gsub=string.lower,string.gsub +local resolvers=resolvers +local allocate=utilities.storage.allocate +local setmetatableindex=table.setmetatableindex +local suffixonly=file.suffixonly +local formats=allocate() +local suffixes=allocate() +local dangerous=allocate() +local suffixmap=allocate() +local usertypes=allocate() +resolvers.formats=formats +resolvers.suffixes=suffixes +resolvers.dangerous=dangerous +resolvers.suffixmap=suffixmap +resolvers.usertypes=usertypes +local luasuffixes=utilities.lua.suffixes +local relations=allocate { + core={ + ofm={ + names={ "ofm","omega font metric","omega font metrics" }, + variable='OFMFONTS', + suffixes={ 'ofm','tfm' }, + }, + ovf={ + 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={ + names={ 'mem',"metapost format" }, + variable='MPMEMS', + suffixes={ 'mem' }, + }, + mp={ + names={ "mp" }, + variable='MPINPUTS', + suffixes={ 'mp','mpvi','mpiv','mpii' }, + usertype=true, + }, + tex={ + names={ "tex" }, + variable='TEXINPUTS', + suffixes={ "tex","mkvi","mkiv","mkii","cld","lfg","xml" }, + usertype=true, + }, + icc={ + names={ "icc","icc profile","icc profiles" }, + variable='ICCPROFILES', + suffixes={ 'icc' }, + }, + texmfscripts={ + names={ "texmfscript","texmfscripts","script","scripts" }, + variable='TEXMFSCRIPTS', + suffixes={ 'lua','rb','pl','py' }, + }, + lua={ + names={ "lua" }, + variable='LUAINPUTS', + suffixes={ luasuffixes.lua,luasuffixes.luc,luasuffixes.tma,luasuffixes.tmc }, + usertype=true, + }, + lib={ + names={ "lib" }, + variable='CLUAINPUTS', + suffixes=os.libsuffix and { os.libsuffix } or { 'dll','so' }, + }, + bib={ + names={ 'bib' }, + variable='BIBINPUTS', + suffixes={ 'bib' }, + usertype=true, + }, + bst={ + names={ 'bst' }, + variable='BSTINPUTS', + suffixes={ 'bst' }, + usertype=true, + }, + 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={ + 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' }, + }, + }, +} +resolvers.relations=relations +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 + if rn and rv then + local rs=relation.suffixes + local ru=relation.usertype + 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 + if ru then + usertypes[name]=true + end + end + end + end +end +resolvers.updaterelations() +local function simplified(t,k) + return k and rawget(t,lower(gsub(k," ",""))) or nil +end +setmetatableindex(formats,simplified) +setmetatableindex(suffixes,simplified) +setmetatableindex(suffixmap,simplified) +function resolvers.suffixofformat(str) + local s=suffixes[str] + return s and s[1] or "" +end +function resolvers.suffixofformat(str) + return suffixes[str] or {} +end +for name,format in next,formats do + dangerous[name]=true +end +dangerous.tex=nil +function resolvers.formatofvariable(str) + return formats[str] or '' +end +function resolvers.formatofsuffix(str) + return suffixmap[suffixonly(str)] or 'tex' +end +function resolvers.variableofformat(str) + return formats[str] or '' +end +function resolvers.variableofformatorsuffix(str) + local v=formats[str] + if v then + return v + end + v=suffixmap[suffixonly(str)] + if v then + return formats[v] + end + return '' +end + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["data-tmp"] = package.loaded["data-tmp"] or true + +-- original size: 15618, stripped down to: 11629 + +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" +} +local format,lower,gsub,concat=string.format,string.lower,string.gsub,table.concat +local concat=table.concat +local mkdirs,isdir,isfile=dir.mkdirs,lfs.isdir,lfs.isfile +local addsuffix,is_writable,is_readable=file.addsuffix,file.is_writable,file.is_readable +local formatters=string.formatters +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 +local cleanpath=resolvers.cleanpath +local directive_cleanup=false directives.register("system.compile.cleanup",function(v) directive_cleanup=v end) +local directive_strip=false 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 +caches=caches or {} +local caches=caches +local luasuffixes=utilities.lua.suffixes +caches.base=caches.base or "luatex-cache" +caches.more=caches.more or "context" +caches.direct=false +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,{},{} +local function identify() + 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 is_readable(cachepath) then + readables[#readables+1]=cachepath + if not writable and is_writable(cachepath) then + writable=cachepath + end + end + elseif not writable and caches.force then + local cacheparent=file.dirname(cachepath) + if is_writable(cacheparent) and true 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 is_writable(cachepath) then + report_caches("path %a created",cachepath) + writable=cachepath + readables[#readables+1]=cachepath + end + end + end + end + end + end + end + local texmfcaches=caches.defaults + if texmfcaches then + for k=1,#texmfcaches do + local cachepath=texmfcaches[k] + cachepath=resolvers.expansion(cachepath) + if cachepath~="" then + cachepath=resolvers.resolve(cachepath) + cachepath=resolvers.cleanpath(cachepath) + local valid=isdir(cachepath) + if valid and is_readable(cachepath) then + if not writable and is_writable(cachepath) then + readables[#readables+1]=cachepath + writable=cachepath + break + end + end + end + end + end + 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 + writable=dir.expandname(resolvers.cleanpath(writable)) + local base,more,tree=caches.base,caches.more,caches.tree or caches.treehash() + 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 + writable=mkdirs(writable,base,more) + for i=1,#readables do + readables[i]=file.join(readables[i],base,more) + end + end + if trace_cache then + for i=1,#readables do + report_caches("using readable path %a (order %s)",readables[i],i) + end + report_caches("using writable path %a",writable) + end + identify=function() + return writable,readables + end + return writable,readables +end +function caches.usedpaths(separator) + local writable,readables=identify() + if #readables>1 then + local result={} + local done={} + for i=1,#readables do + local readable=readables[i] + if readable==writable then + done[readable]=true + result[#result+1]=formatters["readable+writable: %a"](readable) + elseif usedreadables[i] then + done[readable]=true + result[#result+1]=formatters["readable: %a"](readable) + end + end + if not done[writable] then + result[#result+1]=formatters["writable: %a"](writable) + end + return concat(result,separator or " | ") + else + return writable or "?" + end +end +function caches.configfiles() + return concat(resolvers.instance.specification,";") +end +function caches.hashed(tree) + tree=gsub(tree,"[\\/]+$","") + tree=lower(tree) + local hash=md5.hex(tree) + if trace_cache or trace_locating then + report_caches("hashing tree %a, hash %a",tree,hash) + end + return hash +end +function caches.treehash() + local tree=caches.configfiles() + if not tree or tree=="" then + return false + else + return caches.hashed(tree) + end +end +local r_cache,w_cache={},{} +local function getreadablepaths(...) + local tags={... } + local hash=concat(tags,"/") + local done=r_cache[hash] + if not done then + local writable,readables=identify() + 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 +local function getwritablepath(...) + local tags={... } + local hash=concat(tags,"/") + local done=w_cache[hash] + if not done then + local writable,readables=identify() + if #tags>0 then + done=mkdirs(writable,...) + else + done=writable + end + w_cache[hash]=done + end + return done +end +caches.getreadablepaths=getreadablepaths +caches.getwritablepath=getwritablepath +function caches.getfirstreadablefile(filename,...) + local rd=getreadablepaths(...) + for i=1,#rd do + local path=rd[i] + local fullname=file.join(path,filename) + if is_readable(fullname) then + usedreadables[i]=true + return fullname,path + end + end + return caches.setfirstwritablefile(filename,...) +end +function caches.getfirstreadablefile_TEST_ME_FIRST(filename,...) + local fullname,path=caches.setfirstwritablefile(filename,...) + if is_readable(fullname) then + return fullname,path + end + local rd=getreadablepaths(...) + for i=1,#rd do + local path=rd[i] + local fullname=file.join(path,filename) + if is_readable(fullname) then + usedreadables[i]=true + return fullname,path + end + end + return fullname,path +end +function caches.setfirstwritablefile(filename,...) + local wr=getwritablepath(...) + local fullname=file.join(wr,filename) + return fullname,wr +end +function caches.define(category,subcategory) + return function() + return getwritablepath(category,subcategory) + end +end +function caches.setluanames(path,name) + return format("%s/%s.%s",path,name,luasuffixes.tma),format("%s/%s.%s",path,name,luasuffixes.tmc) +end +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=false + if isfile(tmcname) then + loader=loadfile(tmcname) + end + if not loader and isfile(tmaname) then + utilities.lua.compile(tmaname,tmcname) + if isfile(tmcname) then + loader=loadfile(tmcname) + end + if not loader then + loader=loadfile(tmaname) + end + end + if loader then + loader=loader() + collectgarbage("step") + return loader + end + end + return false +end +function caches.is_writable(filepath,filename) + local tmaname,tmcname=caches.setluanames(filepath,filename) + return is_writable(tmaname) +end +local saveoptions={ compact=true } +function caches.savedata(filepath,filename,data,raw) + local tmaname,tmcname=caches.setluanames(filepath,filename) + data.cache_uuid=os.uuid() + if caches.direct then + file.savedata(tmaname,table.serialize(data,true,saveoptions)) + else + table.tofile(tmaname,data,true,saveoptions) + end + utilities.lua.compile(tmaname,tmcname) +end +local content_state={} +function caches.contentstate() + return content_state or {} +end +function caches.loadcontent(cachename,dataname,filename) + if not filename then + local name=caches.hashed(cachename) + local full,path=caches.getfirstreadablefile(addsuffix(name,luasuffixes.lua),"trees") + filename=file.join(path,name) + end + local blob=loadfile(addsuffix(filename,luasuffixes.luc)) or loadfile(addsuffix(filename,luasuffixes.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 %a for %a from %a",dataname,cachename,filename) + end + return data.content + else + report_resolvers("skipping %a for %a from %a (version mismatch)",dataname,cachename,filename) + end + else + report_resolvers("skipping %a for %a from %a (datatype mismatch)",dataname,cachename,filename) + end + elseif trace_locating then + report_resolvers("skipping %a for %a from %a (no content)",dataname,cachename,filename) + end + elseif trace_locating then + report_resolvers("skipping %a for %a from %a (invalid file)",dataname,cachename,filename) + end +end +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 +end +function caches.savecontent(cachename,dataname,content,filename) + if not filename then + local name=caches.hashed(cachename) + local full,path=caches.setfirstwritablefile(addsuffix(name,luasuffixes.lua),"trees") + filename=file.join(path,name) + end + local luaname=addsuffix(filename,luasuffixes.lua) + local lucname=addsuffix(filename,luasuffixes.luc) + if trace_locating then + report_resolvers("preparing %a for %a",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 %a, cachename %a saved in %a",dataname,cachename,luaname) + end + if utilities.lua.compile(luaname,lucname) then + if trace_locating then + report_resolvers("%a compiled to %a",dataname,lucname) + end + return true + else + if trace_locating then + report_resolvers("compiling failed for %a, deleting file %a",dataname,lucname) + end + os.remove(lucname) + end + elseif trace_locating then + report_resolvers("unable to save %a in %a (access error)",dataname,luaname) + end +end + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["data-met"] = package.loaded["data-met"] or true + +-- original size: 5347, stripped down to: 4015 + +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 getcurrentdir=lfs.currentdir +local trace_locating=false +local trace_methods=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") +local allocate=utilities.storage.allocate +local resolvers=resolvers +local registered={} +local function splitmethod(filename) + if not filename then + return { scheme="unknown",original=filename } + end + if type(filename)=="table" then + return filename + end + filename=file.collapsepath(filename,".") + if not find(filename,"://",1,true) 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 +resolvers.splitmethod=splitmethod +local function methodhandler(what,first,...) + 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("resolving, method %a, how %a, handler %a, argument %a",what,how,scheme,first) + end + return resolver(specification,...) + else + resolver=namespace.default or namespace.file + if resolver then + if trace_methods then + report_methods("resolving, method %a, how %a, handler %a, argument %a",what,how,"default",first) + end + return resolver(specification,...) + elseif trace_methods then + report_methods("resolving, method %a, how %a, handler %a, argument %a",what,how,"unset") + end + end + elseif how=="tag" then + local resolver=namespace and namespace[first] + if resolver then + if trace_methods then + report_methods("resolving, method %a, how %a, tag %a",what,how,first) + end + return resolver(...) + else + resolver=namespace.default or namespace.file + if resolver then + if trace_methods then + report_methods("resolving, method %a, how %a, tag %a",what,how,"default") + end + return resolver(...) + elseif trace_methods then + report_methods("resolving, method %a, how %a, tag %a",what,how,"unset") + end + end + end + else + report_methods("resolving, invalid method %a") + end +end +resolvers.methodhandler=methodhandler +function resolvers.registermethod(name,namespace,how) + registered[name]={ how=how or "tag",namespace=namespace } + namespace["byscheme"]=function(scheme,filename,...) + if scheme=="file" then + return methodhandler(name,filename,...) + else + return methodhandler(name,addurlscheme(filename,scheme),...) + end + end +end +local concatinators=allocate { notfound=file.join } +local locators=allocate { notfound=function() end } +local hashers=allocate { notfound=function() end } +local generators=allocate { notfound=function() 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 + +package.loaded["data-res"] = package.loaded["data-res"] or true + +-- original size: 67003, stripped down to: 46291 + +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", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files", +} +local gsub,find,lower,upper,match,gmatch=string.gsub,string.find,string.lower,string.upper,string.match,string.gmatch +local concat,insert,remove,sortedkeys,sortedhash=table.concat,table.insert,table.remove,table.sortedkeys,table.sortedhash +local next,type,rawget=next,type,rawget +local os=os +local P,S,R,C,Cc,Cs,Ct,Carg=lpeg.P,lpeg.S,lpeg.R,lpeg.C,lpeg.Cc,lpeg.Cs,lpeg.Ct,lpeg.Carg +local lpegmatch,lpegpatterns=lpeg.match,lpeg.patterns +local formatters=string.formatters +local filedirname=file.dirname +local filebasename=file.basename +local suffixonly=file.suffixonly +local addsuffix=file.addsuffix +local removesuffix=file.removesuffix +local filejoin=file.join +local collapsepath=file.collapsepath +local joinpath=file.joinpath +local is_qualified_path=file.is_qualified_path +local allocate=utilities.storage.allocate +local settings_to_array=utilities.parsers.settings_to_array +local getcurrentdir=lfs.currentdir +local isfile=lfs.isfile +local isdir=lfs.isdir +local setmetatableindex=table.setmetatableindex +local luasuffixes=utilities.lua.suffixes +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 trace_paths=false trackers .register("resolvers.paths",function(v) trace_paths=v end) +local resolve_otherwise=true directives.register("resolvers.otherwise",function(v) resolve_otherwise=v end) +local report_resolving=logs.reporter("resolvers","resolving") +local resolvers=resolvers +local expandedpathfromlist=resolvers.expandedpathfromlist +local checkedvariable=resolvers.checkedvariable +local splitconfigurationpath=resolvers.splitconfigurationpath +local methodhandler=resolvers.methodhandler +local filtered=resolvers.filtered_from_content +local lookup=resolvers.get_from_content +local cleanpath=resolvers.cleanpath +local resolveprefix=resolvers.resolve +local initializesetter=utilities.setters.initialize +local ostype,osname,osenv,ossetenv,osgetenv=os.type,os.name,os.env,os.setenv,os.getenv +resolvers.cacheversion="1.100" +resolvers.configbanner="" +resolvers.homedir=environment.homedir +resolvers.criticalvars=allocate { "SELFAUTOLOC","SELFAUTODIR","SELFAUTOPARENT","TEXMFCNF","TEXMF","TEXOS" } +resolvers.luacnfname="texmfcnf.lua" +resolvers.luacnfstate="unknown" +if environment.default_texmfcnf then + resolvers.luacnfspec="home:texmf/web2c;"..environment.default_texmfcnf +else + resolvers.luacnfspec=concat ({ + "home:texmf/web2c", + "selfautoparent:/texmf-local/web2c", + "selfautoparent:/texmf-context/web2c", + "selfautoparent:/texmf-dist/web2c", + "selfautoparent:/texmf/web2c", + },";") +end +local unset_variable="unset" +local formats=resolvers.formats +local suffixes=resolvers.suffixes +local usertypes=resolvers.usertypes +local dangerous=resolvers.dangerous +local suffixmap=resolvers.suffixmap +resolvers.defaultsuffixes={ "tex" } +resolvers.instance=resolvers.instance or nil +local instance=resolvers.instance or nil +function resolvers.setenv(key,value,raw) + if instance then + instance.environment[key]=value + ossetenv(key,raw and value or resolveprefix(value)) + end +end +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 +resolvers.getenv=getenv +resolvers.env=getenv +local function resolvevariable(k) + return instance.expansions[k] +end +local dollarstripper=lpeg.stripper("$") +local inhibitstripper=P("!")^0*Cs(P(1)^0) +local backslashswapper=lpeg.replacer("\\","/") +local somevariable=P("$")/"" +local somekey=C(R("az","AZ","09","__","--")^1) +local somethingelse=P(";")*((1-S("!{}/\\"))^1*P(";")/"")+P(";")*(P(";")/"")+P(1) +local variableexpander=Cs((somevariable*(somekey/resolvevariable)+somethingelse)^1 ) +local cleaner=P("\\")/"/"+P(";")*S("!{}/\\")^0*P(";")^1/";" +local variablecleaner=Cs((cleaner+P(1))^0) +local somevariable=R("az","AZ","09","__","--")^1/resolvevariable +local variable=(P("$")/"")*(somevariable+(P("{")/"")*somevariable*(P("}")/"")) +local variableresolver=Cs((variable+P(1))^0) +local function expandedvariable(var) + return lpegmatch(variableexpander,var) or var +end +function resolvers.newinstance() + if trace_locating then + report_resolving("creating instance") + end + local environment,variables,expansions,order=allocate(),allocate(),allocate(),allocate() + local newinstance={ + environment=environment, + variables=variables, + expansions=expansions, + order=order, + files=allocate(), + setups=allocate(), + found=allocate(), + foundintrees=allocate(), + hashes=allocate(), + hashed=allocate(), + pathlists=false, + specification=allocate(), + lists=allocate(), + data=allocate(), + fakepaths=allocate(), + remember=true, + diskcache=true, + renewcache=false, + renewtree=false, + loaderror=false, + savelists=true, + pattern=nil, + force_suffixes=true, + pathstack={}, + } + 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 + if v==nil then + v="" + end + t[k]=v + return v + end) + 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) + t[k]=v + return v + 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) + return newinstance +end +function resolvers.setinstance(someinstance) + instance=someinstance + resolvers.instance=someinstance + return someinstance +end +function resolvers.reset() + return resolvers.setinstance(resolvers.newinstance()) +end +local function reset_hashes() + instance.lists={} + instance.pathlists=false + instance.found={} +end +local function reset_caches() + instance.lists={} + instance.pathlists=false +end +local slash=P("/") +local pathexpressionpattern=Cs ( + Cc("^")*( + Cc("%")*S(".-")+slash^2*P(-1)/"/.*" ++slash^2/"/"+(1-slash)*P(-1)*Cc("/")+P(1) + )^1*Cc("$") +) +local cache={} +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 +end +local function reportcriticalvariables(cnfspec) + if trace_locating then + for i=1,#resolvers.criticalvars do + local k=resolvers.criticalvars[i] + local v=resolvers.getenv(k) or "unknown" + report_resolving("variable %a set to %a",k,v) + end + report_resolving() + if cnfspec then + report_resolving("using configuration specification %a",type(cnfspec)=="table" and concat(cnfspec,",") or cnfspec) + end + report_resolving() + end + reportcriticalvariables=function() end +end +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(cnfspec) + local cnfpaths=expandedpathfromlist(resolvers.splitpath(cnfspec)) + local luacnfname=resolvers.luacnfname + for i=1,#cnfpaths do + local filepath=cnfpaths[i] + local filename=collapsepath(filejoin(filepath,luacnfname)) + local realname=resolveprefix(filename) + if trace_locating then + local fullpath=gsub(resolveprefix(collapsepath(filepath)),"//","/") + local weirdpath=find(fullpath,"/texmf.+/texmf") or not find(fullpath,"/web2c",1,true) + report_resolving("looking for %a on %s path %a from specification %a",luacnfname,weirdpath and "weird" or "given",fullpath,filepath) + end + if isfile(realname) then + specification[#specification+1]=filename + if trace_locating then + report_resolving("found configuration file %a",realname) + end + end + end + if trace_locating then + report_resolving() + end + 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=resolveprefix(filename) + local blob=loadfile(realname) + if blob then + local setups=instance.setups + local data=blob() + local parent=data and data.parent + if parent then + local filename=filejoin(pathname,parent) + local realname=resolveprefix(filename) + local blob=loadfile(realname) + if blob then + local parentdata=blob() + if parentdata then + report_resolving("loading configuration file %a",filename) + data=table.merged(parentdata,data) + end + end + end + data=data and data.content + if data then + if trace_locating then + report_resolving("loading configuration file %a",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 %a in configuration file %a should move to the 'variables' subtable", + k,resolveprefix(filename)) + warning=true + end + variables[k]=v + end + end + setups[pathname]=variables + if resolvers.luacnfstate=="default" then + local cnfspec=variables["TEXMFCNF"] + if cnfspec then + if trace_locating then + report_resolving("reloading configuration due to TEXMF redefinition") + end + resolvers.setenv("TEXMFCNF",cnfspec) + instance.specification={} + identify_configuration_files() + load_configuration_files() + resolvers.luacnfstate="configuration" + break + end + end + else + if trace_locating then + report_resolving("skipping configuration file %a (no content)",filename) + end + setups[pathname]={} + instance.loaderror=true + end + elseif trace_locating then + report_resolving("skipping configuration file %a (no valid format)",filename) + end + instance.order[#instance.order+1]=instance.setups[pathname] + if instance.loaderror then + break + end + end + elseif trace_locating then + report_resolving("warning: no lua configuration files found") + end +end +local function load_file_databases() + instance.loaderror,instance.files=false,allocate() + if not instance.renewcache then + local hashes=instance.hashes + for k=1,#hashes do + local hash=hashes[k] + resolvers.hashers.byscheme(hash.type,hash.name) + if instance.loaderror then break end + end + end +end +local function locate_file_databases() + local texmfpaths=resolvers.expandedpathlist("TEXMF") + if #texmfpaths>0 then + for i=1,#texmfpaths do + local path=collapsepath(texmfpaths[i]) + path=gsub(path,"/+$","") + local stripped=lpegmatch(inhibitstripper,path) + if stripped~="" then + local runtime=stripped==path + path=cleanpath(path) + local spec=resolvers.splitmethod(stripped) + if runtime and (spec.noscheme or spec.scheme=="file") then + stripped="tree:///"..stripped + elseif spec.scheme=="cache" or spec.scheme=="file" then + stripped=spec.path + end + if trace_locating then + if runtime then + report_resolving("locating list of %a (runtime) (%s)",path,stripped) + else + report_resolving("locating list of %a (cached)",path) + end + end + methodhandler('locators',stripped) + end + end + if trace_locating then + report_resolving() + end + elseif trace_locating then + report_resolving("no texmf paths are defined (using TEXMF)") + end +end +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 +local function save_file_databases() + 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 %a",cachename) + end + caches.savecontent(cachename,"files",content) + elseif trace_locating then + report_resolving("not saving runtime tree %a",cachename) + end + end +end +function resolvers.renew(hashname) + if hashname and hashname~="" then + local expanded=resolvers.expansion(hashname) or "" + if expanded~="" then + if trace_locating then + report_resolving("identifying tree %a from %a",expanded,hashname) + end + hashname=expanded + else + if trace_locating then + report_resolving("identifying tree %a",hashname) + end + end + local realpath=resolveprefix(hashname) + if isdir(realpath) then + if trace_locating then + report_resolving("using path %a",realpath) + end + methodhandler('generators',hashname) + local content=instance.files[hashname] + caches.collapsecontent(content) + if trace_locating then + report_resolving("saving tree %a",hashname) + end + caches.savecontent(hashname,"files",content) + else + report_resolving("invalid path %a",realpath) + end + 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.appendhash(type,name,cache) + if not instance.hashed[name] then + if trace_locating then + report_resolving("hash %a appended",name) + end + insert(instance.hashes,{ type=type,name=name,cache=cache } ) + instance.hashed[name]=cache + end +end +function resolvers.prependhash(type,name,cache) + if not instance.hashed[name] then + if trace_locating then + report_resolving("hash %a prepended",name) + end + insert(instance.hashes,1,{ type=type,name=name,cache=cache } ) + instance.hashed[name]=cache + end +end +function resolvers.extendtexmfvariable(specification) + 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 + end + reset_hashes() +end +function resolvers.splitexpansions() + local ie=instance.expansions + for k,v in next,ie do + local t,tn,h,p={},0,{},splitconfigurationpath(v) + for kk=1,#p do + local vv=p[kk] + if vv~="" and not h[vv] then + tn=tn+1 + t[tn]=vv + h[vv]=true + end + end + if #t>1 then + ie[k]=t + else + ie[k]=t[1] + end + end +end +function resolvers.datastate() + return caches.contentstate() +end +function resolvers.variable(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) + local name=name and lpegmatch(dollarstripper,name) + local result=name and instance.expansions[name] + return result~=nil and result or "" +end +function resolvers.unexpandedpathlist(str) + local pth=resolvers.variable(str) + local lst=resolvers.splitpath(pth) + return expandedpathfromlist(lst) +end +function resolvers.unexpandedpath(str) + return joinpath(resolvers.unexpandedpathlist(str)) +end +function resolvers.pushpath(name) + local pathstack=instance.pathstack + local lastpath=pathstack[#pathstack] + local pluspath=filedirname(name) + if lastpath then + lastpath=collapsepath(filejoin(lastpath,pluspath)) + else + lastpath=collapsepath(pluspath) + end + insert(pathstack,lastpath) + if trace_paths then + report_resolving("pushing path %a",lastpath) + end +end +function resolvers.poppath() + local pathstack=instance.pathstack + if trace_paths and #pathstack>0 then + report_resolving("popping path %a",pathstack[#pathstack]) + end + remove(pathstack) +end +function resolvers.stackpath() + local pathstack=instance.pathstack + local currentpath=pathstack[#pathstack] + return currentpath~="" and currentpath or nil +end +local done={} +function resolvers.resetextrapath() + local ep=instance.extra_paths + if not ep then + done={} + instance.extra_paths={} + elseif #ep>0 then + done={} + reset_caches() + end +end +function resolvers.registerextrapath(paths,subpaths) + if not subpaths or subpaths=="" then + if not paths or path=="" then + return + elseif done[paths] then + return + end + end + local paths=settings_to_array(paths) + local subpaths=settings_to_array(subpaths) + local ep=instance.extra_paths or {} + local oldn=#ep + local newn=oldn + local nofpaths=#paths + local nofsubpaths=#subpaths + if nofpaths>0 then + if nofsubpaths>0 then + for i=1,nofpaths do + local p=paths[i] + for j=1,nofsubpaths do + local s=subpaths[j] + local ps=p.."/"..s + if not done[ps] then + newn=newn+1 + ep[newn]=cleanpath(ps) + done[ps]=true + end + end + end + else + for i=1,nofpaths do + local p=paths[i] + if not done[p] then + newn=newn+1 + ep[newn]=cleanpath(p) + done[p]=true + end + end + end + elseif nofsubpaths>0 then + for i=1,oldn do + for j=1,nofsubpaths do + local s=subpaths[j] + local ps=ep[i].."/"..s + if not done[ps] then + newn=newn+1 + ep[newn]=cleanpath(ps) + done[ps]=true + end + end + end + end + if newn>0 then + instance.extra_paths=ep + end + if newn~=oldn then + reset_caches() + end +end +function resolvers.pushextrapath(path) + local paths=settings_to_array(path) + if instance.extra_stack then + insert(instance.extra_stack,1,paths) + else + instance.extra_stack={ paths } + end + reset_caches() +end +function resolvers.popextrapath() + if instance.extra_stack then + reset_caches() + return remove(instance.extra_stack,1) + end +end +local function made_list(instance,list,extra_too) + local done={} + local new={} + local newn=0 + local function add(p) + for k=1,#p do + local v=p[k] + if not done[v] then + done[v]=true + newn=newn+1 + new[newn]=v + end + end + end + for k=1,#list do + local v=list[k] + if done[v] then + elseif find(v,"^[%.%/]$") then + done[v]=true + newn=newn+1 + new[newn]=v + else + break + end + end + if extra_too then + local es=instance.extra_stack + if es and #es>0 then + for k=1,#es do + add(es[k]) + end + end + local ep=instance.extra_paths + if ep and #ep>0 then + add(ep) + end + end + add(list) + return new +end +function resolvers.cleanpathlist(str) + local t=resolvers.expandedpathlist(str) + if t then + for i=1,#t do + t[i]=collapsepath(cleanpath(t[i])) + end + end + return t +end +function resolvers.expandpath(str) + return joinpath(resolvers.expandedpathlist(str)) +end +function resolvers.expandedpathlist(str,extra_too) + if not str then + return {} + elseif instance.savelists then + str=lpegmatch(dollarstripper,str) + local lists=instance.lists + local lst=lists[str] + if not lst then + local l=made_list(instance,resolvers.splitpath(resolvers.expansion(str)),extra_too) + lst=expandedpathfromlist(l) + lists[str]=lst + end + return lst + else + local lst=resolvers.splitpath(resolvers.expansion(str)) + return made_list(instance,expandedpathfromlist(lst),extra_too) + end +end +function resolvers.expandedpathlistfromvariable(str) + str=lpegmatch(dollarstripper,str) + local tmp=resolvers.variableofformatorsuffix(str) + return resolvers.expandedpathlist(tmp~="" and tmp or str) +end +function resolvers.expandpathfromvariable(str) + return joinpath(resolvers.expandedpathlistfromvariable(str)) +end +function resolvers.cleanedpathlist(v) + local t=resolvers.expandedpathlist(v) + for i=1,#t do + t[i]=resolvers.resolve(resolvers.cleanpath(t[i])) + end + return t +end +function resolvers.expandbraces(str) + local ori=str + local pth=expandedpathfromlist(resolvers.splitpath(ori)) + return joinpath(pth) +end +function resolvers.registerfilehash(name,content,someerror) + if content then + instance.files[name]=content + else + instance.files[name]={} + if somerror==true then + instance.loaderror=someerror + end + end +end +local function isreadable(name) + local readable=isfile(name) + if trace_detail then + if readable then + report_resolving("file %a is readable",name) + else + report_resolving("file %a is not readable",name) + end + end + return readable +end +local function collect_files(names) + local filelist={} + local noffiles=0 + local function check(hash,root,pathname,path,name) + if not pathname or find(path,pathname) then + local variant=hash.type + local search=filejoin(root,path,name) + local result=methodhandler('concatinators',variant,root,path,name) + if trace_detail then + report_resolving("match: variant %a, search %a, result %a",variant,search,result) + end + noffiles=noffiles+1 + filelist[noffiles]={ variant,search,result } + end + end + for k=1,#names do + local filename=names[k] + if trace_detail then + report_resolving("checking name %a",filename) + end + local basename=filebasename(filename) + local pathname=filedirname(filename) + if pathname=="" or find(pathname,"^%.") then + pathname=false + else + pathname=gsub(pathname,"%*",".*") + pathname="/"..pathname.."$" + end + local hashes=instance.hashes + for h=1,#hashes do + local hash=hashes[h] + local hashname=hash.name + local content=hashname and instance.files[hashname] + if content then + if trace_detail then + report_resolving("deep checking %a, base %a, pattern %a",hashname,basename,pathname) + end + local path,name=lookup(content,basename) + if path then + local metadata=content.metadata + local realroot=metadata and metadata.path or hashname + if type(path)=="string" then + check(hash,realroot,pathname,path,name) + else + for i=1,#path do + check(hash,realroot,pathname,path[i],name) + end + end + end + elseif trace_locating then + report_resolving("no match in %a (%s)",hashname,basename) + end + end + end + return noffiles>0 and filelist or nil +end +local fit={} +function resolvers.registerintrees(filename,format,filetype,usedmethod,foundname) + local foundintrees=instance.foundintrees + if usedmethod=="direct" and filename==foundname and fit[foundname] then + else + local t={ + filename=filename, + format=format~="" and format or nil, + filetype=filetype~="" and filetype or nil, + usedmethod=usedmethod, + foundname=foundname, + } + fit[foundname]=t + foundintrees[#foundintrees+1]=t + end +end +local function can_be_dir(name) + local fakepaths=instance.fakepaths + if not fakepaths[name] then + if isdir(name) then + fakepaths[name]=1 + else + fakepaths[name]=2 + end + end + return fakepaths[name]==1 +end +local preparetreepattern=Cs((P(".")/"%%."+P("-")/"%%-"+P(1))^0*Cc("$")) +local collect_instance_files +local function find_analyze(filename,askedformat,allresults) + local filetype,wantedfiles,ext='',{},suffixonly(filename) + wantedfiles[#wantedfiles+1]=filename + if askedformat=="" then + if ext=="" or not suffixmap[ext] then + local defaultsuffixes=resolvers.defaultsuffixes + local formatofsuffix=resolvers.formatofsuffix + for i=1,#defaultsuffixes do + local forcedname=filename..'.'..defaultsuffixes[i] + wantedfiles[#wantedfiles+1]=forcedname + filetype=formatofsuffix(forcedname) + if trace_locating then + report_resolving("forcing filetype %a",filetype) + end + end + else + filetype=resolvers.formatofsuffix(filename) + if trace_locating then + report_resolving("using suffix based filetype %a",filetype) + end + end + else + 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=askedformat + if trace_locating then + report_resolving("using given filetype %a",filetype) + end + end + return filetype,wantedfiles +end +local function find_direct(filename,allresults) + if not dangerous[askedformat] and isreadable(filename) then + if trace_detail then + report_resolving("file %a found directly",filename) + end + return "direct",{ filename } + end +end +local function find_wildcard(filename,allresults) + if find(filename,'*',1,true) then + if trace_locating then + report_resolving("checking wildcard %a",filename) + end + local result=resolvers.findwildcardfiles(filename) + if result then + return "wildcard",result + end + end +end +local function find_qualified(filename,allresults,askedformat,alsostripped) + if not is_qualified_path(filename) then + return + end + if trace_locating then + report_resolving("checking qualified name %a",filename) + end + if isreadable(filename) then + if trace_detail then + report_resolving("qualified file %a found",filename) + end + return "qualified",{ filename } + end + if trace_detail then + report_resolving("locating qualified file %a",filename) + end + local forcedname,suffix="",suffixonly(filename) + if suffix=="" then + 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 isreadable(forcedname) then + if trace_locating then + report_resolving("no suffix, forcing format filetype %a",s) + end + return "qualified",{ forcedname } + end + end + end + end + if alsostripped and suffix and suffix~="" then + local basename=filebasename(filename) + local pattern=lpegmatch(preparetreepattern,filename) + local savedformat=askedformat + local format=savedformat or "" + if format=="" then + askedformat=resolvers.formatofsuffix(suffix) + end + if not format then + askedformat="othertextfiles" + end + if basename~=filename then + local resolved=collect_instance_files(basename,askedformat,allresults) + if #resolved==0 then + local lowered=lower(basename) + if filename~=lowered then + resolved=collect_instance_files(lowered,askedformat,allresults) + end + end + resolvers.format=savedformat + if #resolved>0 then + local result={} + for r=1,#resolved do + local rr=resolved[r] + if find(rr,pattern) then + result[#result+1]=rr + end + end + if #result>0 then + return "qualified",result + end + end + end + end +end +local function check_subpath(fname) + if isreadable(fname) then + if trace_detail then + report_resolving("found %a by deep scanning",fname) + end + return fname + end +end +local function makepathlist(list,filetype) + local typespec=resolvers.variableofformat(filetype) + local pathlist=resolvers.expandedpathlist(typespec,filetype and usertypes[filetype]) + local entry={} + if pathlist and #pathlist>0 then + for k=1,#pathlist do + local path=pathlist[k] + local prescanned=find(path,'^!!') + local resursive=find(path,'//$') + local pathname=lpegmatch(inhibitstripper,path) + local expression=makepathexpression(pathname) + local barename=gsub(pathname,"/+$","") + barename=resolveprefix(barename) + local scheme=url.hasscheme(barename) + local schemename=gsub(barename,"%.%*$",'') + entry[k]={ + path=path, + pathname=pathname, + prescanned=prescanned, + recursive=recursive, + expression=expression, + barename=barename, + scheme=scheme, + schemename=schemename, + } + end + entry.typespec=typespec + list[filetype]=entry + else + list[filetype]=false + end + return entry +end +local function find_intree(filename,filetype,wantedfiles,allresults) + local pathlists=instance.pathlists + if not pathlists then + pathlists=setmetatableindex(allocate(),makepathlist) + instance.pathlists=pathlists + end + local pathlist=pathlists[filetype] + if pathlist then + local method="intree" + local filelist=collect_files(wantedfiles) + local dirlist={} + local result={} + if filelist then + for i=1,#filelist do + dirlist[i]=filedirname(filelist[i][3]).."/" + end + end + if trace_detail then + report_resolving("checking filename %a in tree",filename) + end + for k=1,#pathlist do + local entry=pathlist[k] + local path=entry.path + local pathname=entry.pathname + local done=false + if filelist then + local expression=entry.expression + if trace_detail then + report_resolving("using pattern %a for path %a",expression,pathname) + end + for k=1,#filelist do + local fl=filelist[k] + local f=fl[2] + local d=dirlist[k] + if find(d,expression) or find(resolveprefix(d),expression) then + result[#result+1]=resolveprefix(fl[3]) + done=true + if allresults then + if trace_detail then + report_resolving("match to %a in hash for file %a and path %a, continue scanning",expression,f,d) + end + else + if trace_detail then + report_resolving("match to %a in hash for file %a and path %a, quit scanning",expression,f,d) + end + break + end + elseif trace_detail then + report_resolving("no match to %a in hash for file %a and path %a",expression,f,d) + end + end + end + if done then + method="database" + else + method="filesystem" + local scheme=entry.scheme + if not scheme or scheme=="file" then + local pname=entry.schemename + if not find(pname,"*",1,true) then + if can_be_dir(pname) then + if not done and not entry.prescanned then + if trace_detail then + report_resolving("quick root scan for %a",pname) + end + for k=1,#wantedfiles do + local w=wantedfiles[k] + local fname=check_subpath(filejoin(pname,w)) + if fname then + result[#result+1]=fname + done=true + if not allresults then + break + end + end + end + if not done and entry.recursive then + if trace_detail then + report_resolving("scanning filesystem for %a",pname) + end + local files=resolvers.simplescanfiles(pname,false,true) + for k=1,#wantedfiles do + local w=wantedfiles[k] + local subpath=files[w] + if not subpath or subpath=="" then + elseif type(subpath)=="string" then + local fname=check_subpath(filejoin(pname,subpath,w)) + if fname then + result[#result+1]=fname + done=true + if not allresults then + break + end + end + else + for i=1,#subpath do + local sp=subpath[i] + if sp=="" then + else + local fname=check_subpath(filejoin(pname,sp,w)) + if fname then + result[#result+1]=fname + done=true + if not allresults then + break + end + end + end + end + if done and not allresults then + break + end + end + end + end + end + end + else + end + else + for k=1,#wantedfiles do + local pname=entry.barename + local fname=methodhandler('finders',pname.."/"..wantedfiles[k]) + if fname then + result[#result+1]=fname + done=true + if not allresults then + break + end + end + end + end + end + if done and not allresults then + break + end + end + if #result>0 then + return method,result + end + end +end +local function find_onpath(filename,filetype,wantedfiles,allresults) + if trace_detail then + report_resolving("checking filename %a, filetype %a, wanted files %a",filename,filetype,concat(wantedfiles," | ")) + end + local result={} + for k=1,#wantedfiles do + local fname=wantedfiles[k] + if fname and isreadable(fname) then + filename=fname + result[#result+1]=filejoin('.',fname) + if not allresults then + break + end + end + end + if #result>0 then + return "onpath",result + end +end +local function find_otherwise(filename,filetype,wantedfiles,allresults) + local filelist=collect_files(wantedfiles) + local fl=filelist and filelist[1] + if fl then + return "otherwise",{ resolveprefix(fl[3]) } + end +end +collect_instance_files=function(filename,askedformat,allresults) + if not filename or filename=="" then + return {} + end + askedformat=askedformat or "" + filename=collapsepath(filename,".") + filename=gsub(filename,"^%./",getcurrentdir().."/") + if allresults then + local filetype,wantedfiles=find_analyze(filename,askedformat) + local results={ + { find_direct (filename,true) }, + { find_wildcard (filename,true) }, + { find_qualified(filename,true,askedformat) }, + { find_intree (filename,filetype,wantedfiles,true) }, + { find_onpath (filename,filetype,wantedfiles,true) }, + { find_otherwise(filename,filetype,wantedfiles,true) }, + } + local result,status,done={},{},{} + for k,r in next,results do + local method,list=r[1],r[2] + if method and list then + for i=1,#list do + local c=collapsepath(list[i]) + if not done[c] then + result[#result+1]=c + done[c]=true + end + status[#status+1]=formatters["%-10s: %s"](method,c) + end + end + end + if trace_detail then + report_resolving("lookup status: %s",table.serialize(status,filename)) + end + return result,status + else + local method,result,stamp,filetype,wantedfiles + if instance.remember then + if askedformat=="" then + stamp=formatters["%s::%s"](suffixonly(filename),filename) + else + stamp=formatters["%s::%s"](askedformat,filename) + end + result=stamp and instance.found[stamp] + if result then + if trace_locating then + report_resolving("remembered file %a",filename) + end + return result + end + end + method,result=find_direct(filename) + if not result then + method,result=find_wildcard(filename) + if not result then + method,result=find_qualified(filename,false,askedformat) + if not result then + filetype,wantedfiles=find_analyze(filename,askedformat) + method,result=find_intree(filename,filetype,wantedfiles) + if not result then + method,result=find_onpath(filename,filetype,wantedfiles) + if resolve_otherwise and not result then + method,result=find_otherwise(filename,filetype,wantedfiles) + end + end + end + end + end + if result and #result>0 then + local foundname=collapsepath(result[1]) + resolvers.registerintrees(filename,askedformat,filetype,method,foundname) + result={ foundname } + else + result={} + end + if stamp then + if trace_locating then + report_resolving("remembering file %a using hash %a",filename,stamp) + end + instance.found[stamp]=result + end + return result + end +end +local function findfiles(filename,filetype,allresults) + if not filename or filename=="" then + return {} + end + local result,status=collect_instance_files(filename,filetype or "",allresults) + if not result or #result==0 then + local lowered=lower(filename) + if filename~=lowered then + result,status=collect_instance_files(lowered,filetype or "",allresults) + end + end + return result or {},status +end +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.findpath(filename,filetype) + return filedirname(findfiles(filename,filetype,false)[1] or "") +end +local function findgivenfiles(filename,allresults) + local base=filebasename(filename) + local result={} + local hashes=instance.hashes + local function okay(hash,path,name) + local found=methodhandler('concatinators',hash.type,hash.name,path,name) + if found and found~="" then + result[#result+1]=resolveprefix(found) + return not allresults + end + end + for k=1,#hashes do + local hash=hashes[k] + local content=instance.files[hash.name] + if content then + local path,name=lookup(content,base) + if not path then + elseif type(path)=="string" then + if okay(hash,path,name) then + return result + end + else + for i=1,#path do + if okay(hash,path[i],name) then + return result + end + end + end + end + end + return result +end +function resolvers.findgivenfiles(filename) + return findgivenfiles(filename,true) +end +function resolvers.findgivenfile(filename) + return findgivenfiles(filename,false)[1] or "" +end +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) + local 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=instance.files + if find(name,"*",1,true) then + local hashes=instance.hashes + local function okay(found,path,base,hashname,hashtype) + if find(found,path) then + local full=methodhandler('concatinators',hashtype,hashname,found,base) + if full and full~="" then + result[#result+1]=resolveprefix(full) + return not allresults + end + end + end + for k=1,#hashes do + local hash=hashes[k] + local hashname=hash.name + local hashtype=hash.type + if hashname and hashtype then + for found,base in filtered(files[hashname],name) do + if type(found)=='string' then + if okay(found,path,base,hashname,hashtype) then + break + end + else + for i=1,#found do + if okay(found[i],path,base,hashname,hashtype) then + break + end + end + end + end + end + end + else + local function okayokay(found,path,base,hashname,hashtype) + if find(found,path) then + local full=methodhandler('concatinators',hashtype,hashname,found,base) + if full and full~="" then + result[#result+1]=resolveprefix(full) + return not allresults + end + end + end + local hashes=instance.hashes + for k=1,#hashes do + local hash=hashes[k] + local hashname=hash.name + local hashtype=hash.type + if hashname and hashtype then + local found,base=lookup(content,base) + if not found then + elseif type(found)=='string' then + if okay(found,path,base,hashname,hashtype) then + break + end + else + for i=1,#found do + if okay(found[i],path,base,hashname,hashtype) then + break + end + end + end + end + end + end + return result +end +function resolvers.findwildcardfiles(filename,result) + return findwildcardfiles(filename,true,result) +end +function resolvers.findwildcardfile(filename) + return findwildcardfiles(filename,false)[1] or "" +end +function resolvers.automount() +end +function resolvers.load(option) + statistics.starttiming(instance) + identify_configuration_files() + load_configuration_files() + if option~="nofiles" then + load_databases() + resolvers.automount() + end + statistics.stoptiming(instance) + local files=instance.files + return files and next(files) and true +end +function resolvers.loadtime() + return statistics.elapsedtime(instance) +end +local function report(str) + if trace_locating then + report_resolving(str) + else + print(str) + end +end +function resolvers.dowithfilesandreport(command,files,...) + if files and #files>0 then + if trace_locating then + report('') + end + if type(files)=="string" then + files={ files } + end + for f=1,#files do + local file=files[f] + local result=command(file,...) + if type(result)=='string' then + report(result) + else + for i=1,#result do + report(result[i]) + end + end + end + end +end +function resolvers.showpath(str) + return joinpath(resolvers.expandedpathlist(resolvers.formatofvariable(str))) +end +function resolvers.registerfile(files,name,path) + if files[name] then + if type(files[name])=='string' then + files[name]={ files[name],path } + else + files[name]=path + end + else + files[name]=path + end +end +function resolvers.dowithpath(name,func) + local pathlist=resolvers.expandedpathlist(name) + for i=1,#pathlist do + func("^"..cleanpath(pathlist[i])) + end +end +function resolvers.dowithvariable(name,func) + func(expandedvariable(name)) +end +function resolvers.locateformat(name) + local engine=environment.ownmain or "luatex" + local barename=removesuffix(name) + local fullname=addsuffix(barename,"fmt") + local fmtname=caches.getfirstreadablefile(fullname,"formats",engine) or "" + if fmtname=="" then + fmtname=resolvers.findfile(fullname) + fmtname=cleanpath(fmtname) + end + if fmtname~="" then + local barename=removesuffix(fmtname) + local luaname=addsuffix(barename,luasuffixes.lua) + local lucname=addsuffix(barename,luasuffixes.luc) + local luiname=addsuffix(barename,luasuffixes.lui) + if isfile(luiname) then + return barename,luiname + elseif isfile(lucname) then + return barename,lucname + elseif isfile(luaname) then + return barename,luaname + end + end + return nil,nil +end +function resolvers.booleanvariable(str,default) + local b=resolvers.expansion(str) + if b=="" then + return default + else + b=toboolean(b) + return (b==nil and default) or b + end +end +function resolvers.dowithfilesintree(pattern,handle,before,after) + 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 blobtype and blobpath then + local total=0 + local checked=0 + local done=0 + if before then + before(blobtype,blobpath,pattern) + end + for path,name in filtered(instance.files[blobpath],pattern) do + if type(path)=="string" then + checked=checked+1 + if handle(blobtype,blobpath,path,name) then + done=done+1 + end + else + checked=checked+#path + for i=1,#path do + if handle(blobtype,blobpath,path[i],name) then + done=done+1 + end + end + end + end + if after then + after(blobtype,blobpath,pattern,total,checked,done) + end + end + end +end +local obsolete=resolvers.obsolete or {} +resolvers.obsolete=obsolete +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 + +package.loaded["data-pre"] = package.loaded["data-pre"] or true + +-- original size: 3950, stripped down to: 2935 + +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", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +local resolvers=resolvers +local prefixes=resolvers.prefixes +local cleanpath=resolvers.cleanpath +local findgivenfile=resolvers.findgivenfile +local expansion=resolvers.expansion +local getenv=resolvers.getenv +local basename=file.basename +local dirname=file.dirname +local joinpath=file.join +local isfile=lfs.isfile +prefixes.environment=function(str) + return cleanpath(expansion(str)) +end +local function relative(str,n) + if not isfile(str) then + local pstr="./"..str + if isfile(pstr) then + str=pstr + else + local p="../" + for i=1,n or 2 do + local pstr=p..str + if isfile(pstr) then + str=pstr + break + else + p=p.."../" + end + end + end + end + return cleanpath(str) +end +local function locate(str) + local fullname=findgivenfile(str) or "" + return cleanpath(fullname~="" and fullname or str) +end +prefixes.relative=relative +prefixes.locate=locate +prefixes.auto=function(str) + local fullname=relative(str) + if not isfile(fullname) then + fullname=locate(str) + end + return fullname +end +prefixes.filename=function(str) + local fullname=findgivenfile(str) or "" + return cleanpath(basename((fullname~="" and fullname) or str)) +end +prefixes.pathname=function(str) + local fullname=findgivenfile(str) or "" + return cleanpath(dirname((fullname~="" and fullname) or str)) +end +prefixes.selfautoloc=function(str) + return cleanpath(joinpath(getenv('SELFAUTOLOC'),str)) +end +prefixes.selfautoparent=function(str) + return cleanpath(joinpath(getenv('SELFAUTOPARENT'),str)) +end +prefixes.selfautodir=function(str) + return cleanpath(joinpath(getenv('SELFAUTODIR'),str)) +end +prefixes.home=function(str) + return cleanpath(joinpath(getenv('HOME'),str)) +end +prefixes.env=prefixes.environment +prefixes.rel=prefixes.relative +prefixes.loc=prefixes.locate +prefixes.kpse=prefixes.locate +prefixes.full=prefixes.locate +prefixes.file=prefixes.filename +prefixes.path=prefixes.pathname +local function toppath() + local inputstack=resolvers.inputstack + if not inputstack then + return "." + end + local pathname=dirname(inputstack[#inputstack] or "") + if pathname=="" then + return "." + else + return pathname + end +end +local function jobpath() + local path=resolvers.stackpath() + if not path or path=="" then + return "." + else + return path + end +end +resolvers.toppath=toppath +resolvers.jobpath=jobpath +prefixes.toppath=function(str) return cleanpath(joinpath(toppath(),str)) end +prefixes.jobpath=function(str) return cleanpath(joinpath(jobpath(),str)) end +resolvers.setdynamic("toppath") +resolvers.setdynamic("jobpath") +prefixes.jobfile=prefixes.jobpath +resolvers.setdynamic("jobfile") + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["data-inp"] = package.loaded["data-inp"] or true + +-- original size: 910, stripped down to: 823 + +if not modules then modules={} end modules ['data-inp']={ + 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 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 } +registermethod("finders",finders,"uri") +registermethod("openers",openers,"uri") +registermethod("loaders",loaders,"uri") +resolvers.finders=finders +resolvers.openers=openers +resolvers.loaders=loaders + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["data-out"] = package.loaded["data-out"] or true + +-- original size: 530, stripped down to: 475 + +if not modules then modules={} end modules ['data-out']={ + 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 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 + +package.loaded["data-fil"] = package.loaded["data-fil"] or true + +-- original size: 3863, stripped down to: 3310 + +if not modules then modules={} end modules ['data-fil']={ + version=1.001, + comment="companion to luat-lib.mkiv", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +local trace_locating=false trackers.register("resolvers.locating",function(v) trace_locating=v end) +local report_files=logs.reporter("resolvers","files") +local resolvers=resolvers +local resolveprefix=resolvers.resolve +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 filename=specification.filename + local realname=resolveprefix(filename) + if realname and realname~='' and lfs.isdir(realname) then + if trace_locating then + report_files("file locator %a found as %a",filename,realname) + end + resolvers.appendhash('file',filename,true) + elseif trace_locating then + report_files("file locator %a not found",filename) + end +end +function hashers.file(specification) + local pathname=specification.filename + local content=caches.loadcontent(pathname,'files') + resolvers.registerfilehash(pathname,content,content==nil) +end +function generators.file(specification) + local pathname=specification.filename + local content=resolvers.scanfiles(pathname,false,true) + resolvers.registerfilehash(pathname,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: %a found",filename) + end + return foundname + else + if trace_locating then + report_files("file finder: %a not found",filename) + end + return finders.notfound() + end +end +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: %a opened",filename) + end + return openers.helpers.textopener("file",filename,f) + end + end + if trace_locating then + report_files("file opener: %a 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: %a 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: %a not found",filename) + end + return loaders.notfound() +end + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["data-con"] = package.loaded["data-con"] or true + +-- original size: 5010, stripped down to: 3588 + +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) +local trace_containers=false trackers.register("resolvers.containers",function(v) trace_containers=v end) +local trace_storage=false trackers.register("resolvers.storage",function(v) trace_storage=v end) +containers=containers or {} +local containers=containers +containers.usecache=true +local report_containers=logs.reporter("resolvers","containers") +local allocated={} +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) + 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, + trace=false, + } + setmetatable(s,mt) + c[subcategory]=s + end + return s + end +end +function containers.is_usable(container,name) + return container.enabled and caches and caches.is_writable(container.writable,name) +end +function containers.is_valid(container,name) + if name and name~="" then + local storage=container.storage[name] + return storage and storage.cache_version==container.version + else + return false + end +end +function containers.read(container,name) + 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 + if trace_cache or trace_containers then + report_containers("action %a, category %a, name %a","load",container.subcategory,name) + end + else + stored=nil + end + storage[name]=stored + elseif stored then + if trace_cache or trace_containers then + report_containers("action %a, category %a, name %a","reuse",container.subcategory,name) + end + end + return stored +end +function containers.write(container,name,data) + if data then + data.cache_version=container.version + if container.enabled and caches then + local unique,shared=data.unique,data.shared + data.unique,data.shared=nil,nil + caches.savedata(container.writable,name,data) + if trace_cache or trace_containers then + report_containers("action %a, category %a, name %a","save",container.subcategory,name) + end + data.unique,data.shared=unique,shared + end + if trace_cache or trace_containers then + report_containers("action %a, category %a, name %a","store",container.subcategory,name) + end + container.storage[name]=data + end + return data +end +function containers.content(container,name) + return container.storage[name] +end +function containers.cleanname(name) + return (gsub(lower(name),"[^%w\128-\255]+","-")) +end + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["data-use"] = package.loaded["data-use"] or true + +-- original size: 3899, stripped down to: 2984 + +if not modules then modules={} end modules ['data-use']={ + 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 format,lower,gsub,find=string.format,string.lower,string.gsub,string.find +local trace_locating=false trackers.register("resolvers.locating",function(v) trace_locating=v end) +local report_mounts=logs.reporter("resolvers","mounts") +local resolvers=resolvers +resolvers.automounted=resolvers.automounted or {} +function resolvers.automount(usecache) + local mountpaths=resolvers.cleanpathlist(resolvers.expansion('TEXMFMOUNT')) + if (not mountpaths or #mountpaths==0) and usecache then + mountpaths=caches.getreadablepaths("mount") + end + if mountpaths and #mountpaths>0 then + statistics.starttiming(resolvers.instance) + for k=1,#mountpaths do + local root=mountpaths[k] + local f=io.open(root.."/url.tmi") + if f then + for line in f:lines() do + if line then + if find(line,"^[%%#%-]") then + elseif find(line,"^zip://") then + if trace_locating then + report_mounts("mounting %a",line) + end + table.insert(resolvers.automounted,line) + resolvers.usezipfile(line) + end + end + end + f:close() + end + end + statistics.stoptiming(resolvers.instance) + end +end +statistics.register("used config file",function() return caches.configfiles() end) +statistics.register("used cache path",function() return caches.usedpaths() end) +function statistics.savefmtstatus(texname,formatbanner,sourcefile) + local enginebanner=status.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.findfile(sourcefile)) or "unknown"), + sourcefile=sourcefile, + } + io.savedata(luvname,table.serialize(luvdata,true)) + end +end +function statistics.checkfmtstatus(texname) + local enginebanner=status.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.findfile(luv.sourcefile)) or "unknown") + local luvbanner=luv.enginebanner or "?" + if luvbanner~=enginebanner then + return format("engine mismatch (luv: %s <> bin: %s)",luvbanner,enginebanner) + end + local luvhash=luv.sourcehash or "?" + if luvhash~=sourcehash then + return format("source mismatch (luv: %s <> bin: %s)",luvhash,sourcehash) + end + else + return "invalid status file" + end + else + return "missing status file" + end + end + return true +end + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["data-zip"] = package.loaded["data-zip"] or true + +-- original size: 8772, stripped down to: 6841 + +if not modules then modules={} end modules ['data-zip']={ + version=1.001, + comment="companion to luat-lib.mkiv", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +local format,find,match=string.format,string.find,string.match +local trace_locating=false trackers.register("resolvers.locating",function(v) trace_locating=v end) +local report_zip=logs.reporter("resolvers","zip") +local resolvers=resolvers +zip=zip or {} +local zip=zip +zip.archives=zip.archives or {} +local archives=zip.archives +zip.registeredfiles=zip.registeredfiles or {} +local registeredfiles=zip.registeredfiles +local function validzip(str) + if not find(str,"^zip://") then + return "zip:///"..str + else + return str + end +end +function zip.openarchive(name) + if not name or name=="" then + return nil + else + local arch=archives[name] + if not arch then + local full=resolvers.findfile(name) or "" + arch=full~="" and zip.open(full) or false + archives[name]=arch + end + return arch + end +end +function zip.closearchive(name) + if not name or (name=="" and archives[name]) then + zip.close(archives[name]) + archives[name]=nil + end +end +function resolvers.locators.zip(specification) + local archive=specification.filename + local zipfile=archive and archive~="" and zip.openarchive(archive) + if trace_locating then + if zipfile then + report_zip("locator: archive %a found",archive) + else + report_zip("locator: archive %a not found",archive) + end + end +end +function resolvers.hashers.zip(specification) + local archive=specification.filename + if trace_locating then + report_zip("loading file %a",archive) + end + resolvers.usezipfile(specification.original) +end +function resolvers.concatinators.zip(zipfile,path,name) + if not path or path=="" then + return format('%s?name=%s',zipfile,name) + else + return format('%s?name=%s/%s',zipfile,path,name) + end +end +function resolvers.finders.zip(specification) + local original=specification.original + local archive=specification.filename + if archive then + local query=url.query(specification.query) + local queryname=query.name + if queryname then + local zfile=zip.openarchive(archive) + if zfile then + if trace_locating then + report_zip("finder: archive %a found",archive) + end + local dfile=zfile:open(queryname) + if dfile then + dfile=zfile:close() + if trace_locating then + report_zip("finder: file %a found",queryname) + end + return specification.original + elseif trace_locating then + report_zip("finder: file %a not found",queryname) + end + elseif trace_locating then + report_zip("finder: unknown archive %a",archive) + end + end + end + if trace_locating then + report_zip("finder: %a not found",original) + end + return resolvers.finders.notfound() +end +function resolvers.openers.zip(specification) + local original=specification.original + local archive=specification.filename + if archive then + local query=url.query(specification.query) + local queryname=query.name + if queryname then + local zfile=zip.openarchive(archive) + if zfile then + if trace_locating then + report_zip("opener; archive %a opened",archive) + end + local dfile=zfile:open(queryname) + if dfile then + if trace_locating then + report_zip("opener: file %a found",queryname) + end + return resolvers.openers.helpers.textopener('zip',original,dfile) + elseif trace_locating then + report_zip("opener: file %a not found",queryname) + end + elseif trace_locating then + report_zip("opener: unknown archive %a",archive) + end + end + end + if trace_locating then + report_zip("opener: %a not found",original) + end + return resolvers.openers.notfound() +end +function resolvers.loaders.zip(specification) + local original=specification.original + local archive=specification.filename + if archive then + local query=url.query(specification.query) + local queryname=query.name + if queryname then + local zfile=zip.openarchive(archive) + if zfile then + if trace_locating then + report_zip("loader: archive %a opened",archive) + end + local dfile=zfile:open(queryname) + if dfile then + logs.show_load(original) + if trace_locating then + report_zip("loader; file %a loaded",original) + end + local s=dfile:read("*all") + dfile:close() + return true,s,#s + elseif trace_locating then + report_zip("loader: file %a not found",queryname) + end + elseif trace_locating then + report_zip("loader; unknown archive %a",archive) + end + end + end + if trace_locating then + report_zip("loader: %a not found",original) + end + return resolvers.openers.notfound() +end +function resolvers.usezipfile(archive) + local specification=resolvers.splitmethod(archive) + local archive=specification.filename + if archive and not registeredfiles[archive] then + local z=zip.openarchive(archive) + if z then + local instance=resolvers.instance + local tree=url.query(specification.query).tree or "" + if trace_locating then + report_zip("registering: archive %a",archive) + end + statistics.starttiming(instance) + resolvers.prependhash('zip',archive) + resolvers.extendtexmfvariable(archive) + registeredfiles[archive]=z + instance.files[archive]=resolvers.registerzipfile(z,tree) + statistics.stoptiming(instance) + elseif trace_locating then + report_zip("registering: unknown archive %a",archive) + end + elseif trace_locating then + report_zip("registering: archive %a not found",archive) + end +end +function resolvers.registerzipfile(z,tree) + local names={} + local files={} + local remap={} + local n=0 + local filter=tree=="" and "^(.+)/(.-)$" or format("^%s/(.+)/(.-)$",tree) + local register=resolvers.registerfile + if trace_locating then + report_zip("registering: using filter %a",filter) + end + for i in z:files() do + local filename=i.filename + local path,name=match(filename,filter) + if not path then + n=n+1 + register(names,filename,"") + local usedname=lower(filename) + files[usedname]="" + if usedname~=filename then + remap[usedname]=filename + end + elseif name and name~="" then + n=n+1 + register(names,name,path) + local usedname=lower(name) + files[usedname]=path + if usedname~=name then + remap[usedname]=name + end + else + end + end + report_zip("registering: %s files registered",n) + return { + files=files, + remap=remap, + } +end + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["data-tre"] = package.loaded["data-tre"] or true + +-- original size: 8479, stripped down to: 5580 + +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" +} +local find,gsub,lower=string.find,string.gsub,string.lower +local basename,dirname,joinname=file.basename,file.dirname,file .join +local globdir,isdir,isfile=dir.glob,lfs.isdir,lfs.isfile +local P,lpegmatch=lpeg.P,lpeg.match +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 resolveprefix=resolvers.resolve +local notfound=resolvers.finders.notfound +local lookup=resolvers.get_from_content +local collectors={} +local found={} +function resolvers.finders.tree(specification) + local spec=specification.filename + local okay=found[spec] + if okay==nil then + if spec~="" then + local path=dirname(spec) + local name=basename(spec) + if path=="" then + path="." + end + local names=collectors[path] + if not names then + local pattern=find(path,"/%*+$") and path or (path.."/*") + names=globdir(pattern) + collectors[path]=names + end + local pattern="/"..gsub(name,"([%.%-%+])","%%%1").."$" + for i=1,#names do + local fullname=names[i] + if find(fullname,pattern) then + found[spec]=fullname + return fullname + end + end + local pattern=lower(pattern) + for i=1,#names do + local fullname=lower(names[i]) + if find(fullname,pattern) then + if isfile(fullname) then + found[spec]=fullname + return fullname + else + break + end + end + end + end + okay=notfound() + found[spec]=okay + end + return okay +end +function resolvers.locators.tree(specification) + local name=specification.filename + local realname=resolveprefix(name) + if realname and realname~='' and isdir(realname) then + if trace_locating then + report_trees("locator %a found",realname) + end + resolvers.appendhash('tree',name,false) + elseif trace_locating then + report_trees("locator %a not found",name) + end +end +function resolvers.hashers.tree(specification) + local name=specification.filename + report_trees("analyzing %a",name) + resolvers.methodhandler("hashers",name) + resolvers.generators.file(specification) +end +local collectors={} +local splitter=lpeg.splitat("/**/") +local stripper=lpeg.replacer { [P("/")*P("*")^1*P(-1)]="" } +table.setmetatableindex(collectors,function(t,k) + local rootname=lpegmatch(stripper,k) + local dataname=joinname(rootname,"dirlist") + local content=caches.loadcontent(dataname,"files",dataname) + if not content then + content=resolvers.scanfiles(rootname,nil,nil,false,true) + caches.savecontent(dataname,"files",content,dataname) + end + t[k]=content + return content +end) +local function checked(root,p,n) + if p then + if type(p)=="table" then + for i=1,#p do + local fullname=joinname(root,p[i],n) + if isfile(fullname) then + return fullname + end + end + else + local fullname=joinname(root,p,n) + if isfile(fullname) then + return fullname + end + end + end + return notfound() +end +local function resolve(specification) + local filename=specification.filename + if filename~="" then + local root,rest=lpegmatch(splitter,filename) + if root and rest then + local path,name=dirname(rest),basename(rest) + if name~=rest then + local content=collectors[root] + local p,n=lookup(content,name) + if not p then + return notfound() + end + local pattern=".*/"..path.."$" + local istable=type(p)=="table" + if istable then + for i=1,#p do + local pi=p[i] + if pi==path or find(pi,pattern) then + local fullname=joinname(root,pi,n) + if isfile(fullname) then + return fullname + end + end + end + elseif p==path or find(p,pattern) then + local fullname=joinname(root,p,n) + if isfile(fullname) then + return fullname + end + end + local queries=specification.queries + if queries and queries.option=="fileonly" then + return checked(root,p,n) + else + return notfound() + end + end + end + local path,name=dirname(filename),basename(filename) + local root=lpegmatch(stripper,path) + local content=collectors[path] + local p,n=lookup(content,name) + if p then + return checked(root,p,n) + end + end + return notfound() +end +resolvers.finders .dirlist=resolve +resolvers.locators .dirlist=resolvers.locators .tree +resolvers.hashers .dirlist=resolvers.hashers .tree +resolvers.generators.dirlist=resolvers.generators.file +resolvers.openers .dirlist=resolvers.openers .file +resolvers.loaders .dirlist=resolvers.loaders .file +function resolvers.finders.dirfile(specification) + local queries=specification.queries + if queries then + queries.option="fileonly" + else + specification.queries={ option="fileonly" } + end + return resolve(specification) +end +resolvers.locators .dirfile=resolvers.locators .dirlist +resolvers.hashers .dirfile=resolvers.hashers .dirlist +resolvers.generators.dirfile=resolvers.generators.dirlist +resolvers.openers .dirfile=resolvers.openers .dirlist +resolvers.loaders .dirfile=resolvers.loaders .dirlist + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["data-sch"] = package.loaded["data-sch"] or true + +-- original size: 6569, stripped down to: 5304 + +if not modules then modules={} end modules ['data-sch']={ + 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 load=load +local gsub,concat,format=string.gsub,table.concat,string.format +local finders,openers,loaders=resolvers.finders,resolvers.openers,resolvers.loaders +local trace_schemes=false trackers.register("resolvers.schemes",function(v) trace_schemes=v end) +local report_schemes=logs.reporter("resolvers","schemes") +local http=require("socket.http") +local ltn12=require("ltn12") +local resolvers=resolvers +local schemes=resolvers.schemes or {} +resolvers.schemes=schemes +local cleaners={} +schemes.cleaners=cleaners +local threshold=24*60*60 +directives.register("schemes.threshold",function(v) threshold=tonumber(v) or threshold end) +function cleaners.none(specification) + return specification.original +end +function cleaners.strip(specification) + local path,name=file.splitbase(specification.original) + if path=="" then + return (gsub(name,"[^%a%d%.]+","-")) + else + return (gsub((gsub(path,"%.","-").."-"..name),"[^%a%d%.]+","-")) + end +end +function cleaners.md5(specification) + return file.addsuffix(md5.hex(specification.original),file.suffix(specification.path)) +end +local cleaner=cleaners.strip +directives.register("schemes.cleanmethod",function(v) cleaner=cleaners[v] or cleaners.strip end) +function resolvers.schemes.cleanname(specification) + local hash=cleaner(specification) + if trace_schemes then + report_schemes("hashing %a to %a",specification.original,hash) + end + return hash +end +local cached,loaded,reused,thresholds,handlers={},{},{},{},{} +local function runcurl(name,cachename) + local command="curl --silent --insecure --create-dirs --output "..cachename.." "..name + os.execute(command) +end +local function fetch(specification) + local original=specification.original + local scheme=specification.scheme + local cleanname=schemes.cleanname(specification) + local cachename=caches.setfirstwritablefile(cleanname,"schemes") + if not cached[original] then + statistics.starttiming(schemes) + if not io.exists(cachename) or (os.difftime(os.time(),lfs.attributes(cachename).modification)>(thresholds[protocol] or threshold)) then + cached[original]=cachename + local handler=handlers[scheme] + if handler then + if trace_schemes then + report_schemes("fetching %a, protocol %a, method %a",original,scheme,"built-in") + end + logs.flush() + handler(specification,cachename) + else + if trace_schemes then + report_schemes("fetching %a, protocol %a, method %a",original,scheme,"curl") + end + logs.flush() + runcurl(original,cachename) + end + end + if io.exists(cachename) then + cached[original]=cachename + if trace_schemes then + report_schemes("using cached %a, protocol %a, cachename %a",original,scheme,cachename) + end + else + cached[original]="" + if trace_schemes then + report_schemes("using missing %a, protocol %a",original,scheme) + end + end + loaded[scheme]=loaded[scheme]+1 + statistics.stoptiming(schemes) + else + if trace_schemes then + report_schemes("reusing %a, protocol %a",original,scheme) + end + reused[scheme]=reused[scheme]+1 + end + return cached[original] +end +local function finder(specification,filetype) + return resolvers.methodhandler("finders",fetch(specification),filetype) +end +local opener=openers.file +local loader=loaders.file +local function install(scheme,handler,newthreshold) + handlers [scheme]=handler + loaded [scheme]=0 + reused [scheme]=0 + finders [scheme]=finder + openers [scheme]=opener + loaders [scheme]=loader + thresholds[scheme]=newthreshold or threshold +end +schemes.install=install +local function http_handler(specification,cachename) + local tempname=cachename..".tmp" + local f=io.open(tempname,"wb") + local status,message=http.request { + url=specification.original, + sink=ltn12.sink.file(f) + } + if not status then + os.remove(tempname) + else + os.remove(cachename) + os.rename(tempname,cachename) + end + return cachename +end +install('http',http_handler) +install('https') +install('ftp') +statistics.register("scheme handling time",function() + local l,r,nl,nr={},{},0,0 + for k,v in table.sortedhash(loaded) do + if v>0 then + nl=nl+1 + l[nl]=k..":"..v + end + end + for k,v in table.sortedhash(reused) do + if v>0 then + nr=nr+1 + r[nr]=k..":"..v + end + end + local n=nl+nr + if n>0 then + l=nl>0 and concat(l) or "none" + r=nr>0 and concat(r) or "none" + return format("%s seconds, %s processed, threshold %s seconds, loaded: %s, reused: %s", + statistics.elapsedtime(schemes),n,threshold,l,r) + else + return nil + end +end) +local httprequest=http.request +local toquery=url.toquery +local function fetchstring(url,data) + local q=data and toquery(data) + if q then + url=url.."?"..q + end + local reply=httprequest(url) + return reply +end +schemes.fetchstring=fetchstring +function schemes.fetchtable(url,data) + local reply=fetchstring(url,data) + if reply then + local s=load("return "..reply) + if s then + return s() + end + end +end + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["data-lua"] = package.loaded["data-lua"] or true + +-- original size: 4313, stripped down to: 3227 + +if not modules then modules={} end modules ['data-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 package,lpeg=package,lpeg +local gsub=string.gsub +local concat=table.concat +local addsuffix=file.addsuffix +local P,S,Cs,lpegmatch=lpeg.P,lpeg.S,lpeg.Cs,lpeg.match +local luasuffixes={ 'tex','lua' } +local libsuffixes={ 'lib' } +local luaformats={ 'TEXINPUTS','LUAINPUTS' } +local libformats={ 'CLUAINPUTS' } +local helpers=package.helpers or {} +local methods=helpers.methods or {} +local resolvers=resolvers +local resolveprefix=resolvers.resolve +helpers.report=logs.reporter("resolvers","libraries") +trackers.register("resolvers.libraries",function(v) helpers.trace=v end) +trackers.register("resolvers.locating",function(v) helpers.trace=v end) +helpers.sequence={ + "already loaded", + "preload table", + "lua variable format", + "lib variable format", + "lua extra list", + "lib extra list", + "path specification", + "cpath specification", + "all in one fallback", + "not loaded", +} +local pattern=Cs(P("!")^0/""*(P("/")*P(-1)/"/"+P("/")^1/"/"+1)^0) +function helpers.cleanpath(path) + return resolveprefix(lpegmatch(pattern,path)) +end +local loadedaslib=helpers.loadedaslib +local getextraluapaths=package.extraluapaths +local getextralibpaths=package.extralibpaths +local registerpath=helpers.registerpath +local lualibfile=helpers.lualibfile +local luaformatpaths +local libformatpaths +local function getluaformatpaths() + if not luaformatpaths then + luaformatpaths={} + for i=1,#luaformats do + registerpath("lua format","lua",luaformatpaths,resolvers.expandedpathlistfromvariable(luaformats[i])) + end + end + return luaformatpaths +end +local function getlibformatpaths() + if not libformatpaths then + libformatpaths={} + for i=1,#libformats do + registerpath("lib format","lib",libformatpaths,resolvers.expandedpathlistfromvariable(libformats[i])) + end + end + return libformatpaths +end +local function loadedbyformat(name,rawname,suffixes,islib,what) + local trace=helpers.trace + local report=helpers.report + for i=1,#suffixes do + local format=suffixes[i] + local resolved=resolvers.findfile(name,format) or "" + if trace then + report("%s format, identifying %a using format %a",what,name,format) + end + if resolved~="" then + if trace then + report("%s format, %a found on %a",what,name,resolved) + end + if islib then + return loadedaslib(resolved,rawname) + else + return loadfile(resolved) + end + end + end +end +helpers.loadedbyformat=loadedbyformat +methods["lua variable format"]=function(name) + if helpers.trace then + helpers.report("%s format, checking %s paths","lua",#getluaformatpaths()) + end + return loadedbyformat(addsuffix(lualibfile(name),"lua"),name,luasuffixes,false,"lua") +end +methods["lib variable format"]=function(name) + if helpers.trace then + helpers.report("%s format, checking %s paths","lib",#getlibformatpaths()) + end + return loadedbyformat(addsuffix(lualibfile(name),os.libsuffix),name,libsuffixes,true,"lib") +end +resolvers.loadlualib=require + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["data-aux"] = package.loaded["data-aux"] or true + +-- original size: 2431, stripped down to: 1996 + +if not modules then modules={} end modules ['data-aux']={ + 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 find=string.find +local type,next=type,next +local trace_locating=false trackers.register("resolvers.locating",function(v) trace_locating=v end) +local resolvers=resolvers +local report_scripts=logs.reporter("resolvers","scripts") +function resolvers.updatescript(oldname,newname) + local scriptpath="context/lua" + newname=file.addsuffix(newname,"lua") + local oldscript=resolvers.cleanpath(oldname) + if trace_locating then + report_scripts("to be replaced old script %a",oldscript) + end + local newscripts=resolvers.findfiles(newname) or {} + if #newscripts==0 then + if trace_locating then + report_scripts("unable to locate new script") + end + else + for i=1,#newscripts do + local newscript=resolvers.cleanpath(newscripts[i]) + if trace_locating then + report_scripts("checking new script %a",newscript) + end + if oldscript==newscript then + if trace_locating then + report_scripts("old and new script are the same") + end + elseif not find(newscript,scriptpath) then + if trace_locating then + report_scripts("new script should come from %a",scriptpath) + end + elseif not (find(oldscript,file.removesuffix(newname).."$") or find(oldscript,newname.."$")) then + if trace_locating then + report_scripts("invalid new script name") + end + else + local newdata=io.loaddata(newscript) + if newdata then + if trace_locating then + report_scripts("old script content replaced by new content") + end + io.savedata(oldscript,newdata) + break + elseif trace_locating then + report_scripts("unable to load new script") + end + end + end + end +end + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["data-tmf"] = package.loaded["data-tmf"] or true + +-- original size: 2601, stripped down to: 1627 + +if not modules then modules={} end modules ['data-tmf']={ + 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 resolvers=resolvers +local report_tds=logs.reporter("resolvers","tds") +function resolvers.load_tree(tree,resolve) + if type(tree)=="string" and tree~="" then + local getenv,setenv=resolvers.getenv,resolvers.setenv + 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 %a under tree %a",texos,tree) + os.exit() + end + if not lfs.isdir(newpath) then + report_tds("no '%s/bin' under tree %a",texos,tree) + os.exit() + end + local texmfos=newtree + environment.texroot=newroot + environment.texos=texos + environment.texmfos=texmfos + if resolve then + resolvers.luacnfspec=resolvers.resolve(resolvers.luacnfspec) + end + setenv('SELFAUTOPARENT',newroot) + setenv('SELFAUTODIR',newtree) + setenv('SELFAUTOLOC',newpath) + setenv('TEXROOT',newroot) + setenv('TEXOS',texos) + setenv('TEXMFOS',texmfos) + setenv('TEXMFCNF',resolvers.luacnfspec,true) + setenv('PATH',newpath..io.pathseparator..getenv('PATH')) + report_tds("changing from root %a to %a",oldroot,newroot) + report_tds("prepending %a to PATH",newpath) + report_tds("setting TEXMFCNF to %a",resolvers.luacnfspec) + report_tds() + end +end + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["data-lst"] = package.loaded["data-lst"] or true + +-- original size: 2734, stripped down to: 2354 + +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" +} +local rawget,type,next=rawget,type,next +local find,concat,upper=string.find,table.concat,string.upper +local fastcopy,sortedpairs=table.fastcopy,table.sortedpairs +local resolvers=resolvers +local listers=resolvers.listers or {} +resolvers.listers=listers +local resolveprefix=resolvers.resolve +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 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 + end + local env=fastcopy(environment) + local var=fastcopy(variables) + local exp=fastcopy(expansions) + for key,value in 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",tabstr(resolveprefix(expansions[key])) or "unset") + end + end + instance.environment=fastcopy(env) + instance.variables=fastcopy(var) + instance.expansions=fastcopy(exp) +end +local report_resolved=logs.reporter("system","resolved") +function listers.configurations() + local configurations=resolvers.instance.specification + for i=1,#configurations do + report_resolved("file : %s",resolveprefix(configurations[i])) + end + report_resolved("") + local list=resolvers.expandedpathfromlist(resolvers.splitpath(resolvers.luacnfspec)) + for i=1,#list do + local li=resolveprefix(list[i]) + if lfs.isdir(li) then + report_resolved("path - %s",li) + else + report_resolved("path + %s",li) + end + end +end + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["util-lib"] = package.loaded["util-lib"] or true + +-- original size: 11549, stripped down to: 5905 + +if not modules then modules={} end modules ['util-lib']={ + 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 gsub,find=string.gsub,string.find +local pathpart,nameonly,joinfile=file.pathpart,file.nameonly,file.join +local findfile,findfiles=resolvers and resolvers.findfile,resolvers and resolvers.findfiles +local loaded=package.loaded +local report_swiglib=logs.reporter("swiglib") +local trace_swiglib=false trackers.register("resolvers.swiglib",function(v) trace_swiglib=v end) +local done=false +local function requireswiglib(required,version) + local trace_swiglib=trace_swiglib or package.helpers.trace + local library=loaded[required] + if library==nil then + if trace_swiglib then + report_swiglib("requiring library %a with version %a",required,version or "any") + end + local required_full=gsub(required,"%.","/") + local required_path=pathpart(required_full) + local required_base=nameonly(required_full) + local required_name=required_base.."."..os.libsuffix + local version=type(version)=="string" and version~="" and version or false + local engine=environment.ownmain or false + if trace_swiglib and not done then + local list=resolvers.expandedpathlistfromvariable("lib") + for i=1,#list do + report_swiglib("tds path %i: %s",i,list[i]) + end + end + local function found(locate,asked_library,how,...) + if trace_swiglib then + report_swiglib("checking %s: %a",how,asked_library) + end + return locate(asked_library,...) + end + local function check(locate,...) + local found=nil + if version then + local asked_library=joinfile(required_path,version,required_name) + if trace_swiglib then + report_swiglib("checking %s: %a","with version",asked_library) + end + found=locate(asked_library,...) + end + if not found or found=="" then + local asked_library=joinfile(required_path,required_name) + if trace_swiglib then + report_swiglib("checking %s: %a","with version",asked_library) + end + found=locate(asked_library,...) + end + return found and found~="" and found or false + end + local function attempt(checkpattern) + if trace_swiglib then + report_swiglib("checking tds lib paths strictly") + end + local found=findfile and check(findfile,"lib") + if found and (not checkpattern or find(found,checkpattern)) then + return found + end + if trace_swiglib then + report_swiglib("checking tds lib paths with wildcard") + end + local asked_library=joinfile(required_path,".*",required_name) + if trace_swiglib then + report_swiglib("checking %s: %a","latest version",asked_library) + end + local list=findfiles(asked_library,"lib",true) + if list and #list>0 then + table.sort(list) + local found=list[#list] + if found and (not checkpattern or find(found,checkpattern)) then + return found + end + end + if trace_swiglib then + report_swiglib("checking lib paths") + end + package.extralibpath(environment.ownpath) + local paths=package.libpaths() + for i=1,#paths do + local found=check(lfs.isfile) + if found and (not checkpattern or find(found,checkpattern)) then + return found + end + end + return false + end + local found_library=nil + if engine then + if trace_swiglib then + report_swiglib("attemp 1, engine %a",engine) + end + found_library=attempt("/"..engine.."/") + if not found_library then + if trace_swiglib then + report_swiglib("attemp 2, no engine",asked_library) + end + found_library=attempt() + end + else + found_library=attempt() + end + if not found_library then + if trace_swiglib then + report_swiglib("not found: %a",required) + end + library=false + else + local path=pathpart(found_library) + local base=nameonly(found_library) + dir.push(path) + if trace_swiglib then + report_swiglib("found: %a",found_library) + end + local message=nil + local opener="luaopen_"..required_base + library,message=package.loadlib(found_library,opener) + local libtype=type(library) + if libtype=="function" then + library=library() + else + report_swiglib("load error: %a returns %a, message %a, library %a",opener,libtype,(string.gsub(message or "no message","[%s]+$","")),found_library or "no library") + library=false + end + dir.pop() + end + if not library then + report_swiglib("unknown: %a",required) + elseif trace_swiglib then + report_swiglib("stored: %a",required) + end + loaded[required]=library + else + report_swiglib("reused: %a",required) + end + return library +end +local savedrequire=require +function require(name,version) + if find(name,"^swiglib%.") then + return requireswiglib(name,version) + else + return savedrequire(name) + end +end +local swiglibs={} +local initializer="core" +function swiglib(name,version) + local library=swiglibs[name] + if not library then + statistics.starttiming(swiglibs) + if trace_swiglib then + report_swiglib("loading %a",name) + end + if not find(name,"%."..initializer.."$") then + fullname="swiglib."..name.."."..initializer + else + fullname="swiglib."..name + end + library=requireswiglib(fullname,version) + swiglibs[name]=library + statistics.stoptiming(swiglibs) + end + return library +end +statistics.register("used swiglibs",function() + if next(swiglibs) then + return string.format("%s, initial load time %s seconds",table.concat(table.sortedkeys(swiglibs)," "),statistics.elapsedtime(swiglibs)) + end +end) + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["luat-sta"] = package.loaded["luat-sta"] or true + +-- original size: 5703, stripped down to: 2507 + +if not modules then modules={} end modules ['luat-sta']={ + version=1.001, + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +local gmatch,match=string.gmatch,string.match +local type=type +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 "" +function states.save(filename,tag) + tag=tag or states.tag + filename=file.addsuffix(filename or states.filename,'lus') + io.savedata(filename, + "-- generator : luat-sta.lua\n".."-- state tag : "..tag.."\n\n"..table.serialize(data[tag or states.tag] or {},true) + ) +end +function states.load(filename,tag) + states.filename=filename + states.tag=tag or "whatever" + states.filename=file.addsuffix(states.filename,'lus') + data[states.tag],hash[states.tag]=(io.exists(filename) and dofile(filename)) or {},{} +end +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 + local pre,post=match(key,"(.+)%.([^%.]+)$") + if pre and post then + for k in gmatch(pre,"[^%.]+") do + local dk=d[k] + if not dk then + dk={} + d[k]=dk + elseif type(dk)=="string" then + break + end + d=dk + end + dkey,hkey=post,key + end + if value==nil then + value=default + elseif value==false then + elseif persistent then + value=value or d[dkey] or default + else + value=value or default + end + d[dkey],h[hkey]=value,value + elseif type(d)=="string" then + data[tag],hash[tag]=value,value + end + end +end +local function get_by_tag(tag,key,default) + local h=hash[tag] + if h and h[key] then + return h[key] + else + local d=data[tag] + if d then + for k in gmatch(key,"[^%.]+") do + local dk=d[k] + if dk~=nil then + d=dk + else + return default + end + end + 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) + set_by_tag(states.tag,key,value,default,persistent) +end +function states.get(key,default) + return get_by_tag(states.tag,key,default) +end + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +package.loaded["luat-fmt"] = package.loaded["luat-fmt"] or true + +-- original size: 5955, stripped down to: 4926 + +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 concat=table.concat +local quoted=string.quoted +local luasuffixes=utilities.lua.suffixes +local report_format=logs.reporter("resolvers","formats") +local function primaryflags() + local trackers=environment.argument("trackers") + local directives=environment.argument("directives") + local flags={} + if trackers and trackers~="" then + flags={ "--trackers="..quoted(trackers) } + end + if directives and directives~="" then + flags={ "--directives="..quoted(directives) } + end + if environment.argument("jit") then + flags={ "--jiton" } + end + return concat(flags," ") +end +function environment.make_format(name) + local engine=environment.ownmain or "luatex" + local olddir=dir.current() + local path=caches.getwritablepath("formats",engine) or "" + if path~="" then + lfs.chdir(path) + end + report_format("using format path %a",dir.current()) + 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 %a (mkiv or tex)",name) + lfs.chdir(olddir) + return + else + report_format("using tex source file %a",fulltexsourcename) + end + local texsourcepath=dir.expandname(file.dirname(fulltexsourcename)) + 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 %a",specificationname) + lfs.chdir(olddir) + return + end + local specificationpath=file.dirname(fullspecificationname) + 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 %a",fullspecificationname) + local texbasename=file.basename(name) + local luastubname=file.addsuffix(texbasename,luasuffixes.lua) + local lucstubname=file.addsuffix(texbasename,luasuffixes.luc) + report_format("creating initialization file %a",luastubname) + utilities.merger.selfcreate(usedlualibs,specificationpath,luastubname) + if utilities.lua.compile(luastubname,lucstubname) and lfs.isfile(lucstubname) then + report_format("using compiled initialization file %a",lucstubname) + usedluastub=lucstubname + else + report_format("using uncompiled initialization file %a",luastubname) + usedluastub=luastubname + end + else + report_format("invalid stub specification %a",fullspecificationname) + lfs.chdir(olddir) + return + end + local command=format("%s --ini %s --lua=%s %s %sdump",engine,primaryflags(),quoted(usedluastub),quoted(fulltexsourcename),os.platform=="unix" and "\\\\" or "\\") + report_format("running command: %s\n",command) + os.execute(command) + local pattern=file.removesuffix(file.basename(usedluastub)).."-*.mem" + local mp=dir.glob(pattern) + if mp then + for i=1,#mp do + local name=mp[i] + report_format("removing related mplib format %a",file.basename(name)) + os.remove(name) + end + end + lfs.chdir(olddir) +end +function environment.run_format(name,data,more) + if name and name~="" then + local engine=environment.ownmain or "luatex" + local barename=file.removesuffix(name) + local fmtname=caches.getfirstreadablefile(file.addsuffix(barename,"fmt"),"formats",engine) + 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 %a",name) + else + local barename=file.removesuffix(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 %a",fmtname) + report_format("no luc/lua file with name %a",barename) + else + local command=format("%s %s --fmt=%s --lua=%s %s %s",engine,primaryflags(),quoted(barename),quoted(luaname),quoted(data),more~="" and quoted(more) or "") + report_format("running command: %s",command) + os.execute(command) + end + end + end +end + + +end -- of closure + +-- used libraries : l-lua.lua l-package.lua l-lpeg.lua l-function.lua l-string.lua l-table.lua l-io.lua l-number.lua l-set.lua l-os.lua l-file.lua l-gzip.lua l-md5.lua l-url.lua l-dir.lua l-boolean.lua l-unicode.lua l-math.lua util-str.lua util-tab.lua util-sto.lua util-prs.lua util-fmt.lua trac-set.lua trac-log.lua trac-inf.lua trac-pro.lua util-lua.lua util-deb.lua util-mrg.lua util-tpl.lua util-env.lua luat-env.lua lxml-tab.lua lxml-lpt.lua lxml-mis.lua lxml-aux.lua lxml-xml.lua trac-xml.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-zip.lua data-tre.lua data-sch.lua data-lua.lua data-aux.lua data-tmf.lua data-lst.lua util-lib.lua luat-sta.lua luat-fmt.lua +-- skipped libraries : - +-- original bytes : 745618 +-- stripped bytes : 269191 + +-- end library merge + +-- We need this hack till luatex is fixed. +-- +-- for k,v in pairs(arg) do print(k,v) end + +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 + +-- End of hack. + +local format, gsub, gmatch, match, find = string.format, string.gsub, string.gmatch, string.match, string.find +local concat = table.concat + +local ownname = environment and environment.ownname or arg[0] or 'mtxrun.lua' +local ownpath = gsub(match(ownname,"^(.+)[\\/].-$") or ".","\\","/") +local owntree = environment and environment.ownpath or ownpath + +local ownlibs = { -- order can be made better + + 'l-lua.lua', + 'l-package.lua', + 'l-lpeg.lua', + 'l-function.lua', + 'l-string.lua', + 'l-table.lua', + 'l-io.lua', + 'l-number.lua', + 'l-set.lua', + 'l-os.lua', + 'l-file.lua', + 'l-gzip.lua', + 'l-md5.lua', + 'l-url.lua', + 'l-dir.lua', + 'l-boolean.lua', + 'l-unicode.lua', + 'l-math.lua', + + 'util-str.lua', -- code might move to l-string + 'util-tab.lua', + 'util-sto.lua', + 'util-prs.lua', + 'util-fmt.lua', + + 'trac-set.lua', + 'trac-log.lua', + 'trac-inf.lua', -- was before trac-set + 'trac-pro.lua', -- not really needed + 'util-lua.lua', -- indeed here? + 'util-deb.lua', + + 'util-mrg.lua', + 'util-tpl.lua', + + 'util-env.lua', + 'luat-env.lua', -- can come before inf (as in mkiv) + + 'lxml-tab.lua', + 'lxml-lpt.lua', + -- 'lxml-ent.lua', + 'lxml-mis.lua', + 'lxml-aux.lua', + 'lxml-xml.lua', + + 'trac-xml.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-sch.lua', + 'data-lua.lua', + 'data-aux.lua', -- updater + 'data-tmf.lua', + 'data-lst.lua', + + 'util-lib.lua', -- swiglib + + 'luat-sta.lua', + 'luat-fmt.lua', + +} + +-- c:/data/develop/tex-context/tex/texmf-win64/bin/../../texmf-context/tex/context/base/data-tmf.lua +-- c:/data/develop/context/sources/data-tmf.lua + +local ownlist = { + -- '.', + -- ownpath , + owntree .. "/../../../../context/sources", -- HH's development path + owntree .. "/../../texmf-local/tex/context/base", + owntree .. "/../../texmf-context/tex/context/base", + owntree .. "/../../texmf/tex/context/base", + owntree .. "/../../../texmf-local/tex/context/base", + owntree .. "/../../../texmf-context/tex/context/base", + owntree .. "/../../../texmf/tex/context/base", +} + +if ownpath == "." then table.remove(ownlist,1) end + +own = { + name = ownname, + path = ownpath, + tree = owntree, + list = ownlist, + libs = ownlibs, +} + +local function locate_libs() + for l=1,#ownlibs do + local lib = ownlibs[l] + for p =1,#ownlist do + local pth = ownlist[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,#ownlibs do + local filename = found .. "/" .. ownlibs[l] + local codeblob = loadfile(filename) + if codeblob then + codeblob() + end + end + else + resolvers = nil + end +end + +if not resolvers then + load_libs() +end + +if not resolvers then + print("") + print("Mtxrun is unable to start up due to lack of libraries. You may") + print("try to run 'lua mtxrun.lua --selfmerge' in the path where this") + print("script is located (normally under ..../scripts/context/lua) which") + print("will make this script library independent.") + os.exit() +end + +-- verbosity + +----- e_verbose = environment.arguments["verbose"] + +local e_verbose = false + +-- 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 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 + +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 + +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 = [[ +<?xml version="1.0" ?> +<application> + <metadata> + <entry name="name">mtxrun</entry> + <entry name="detail">ConTeXt TDS Runner Tool</entry> + <entry name="version">1.31</entry> + </metadata> + <flags> + <category name="basic"> + <subcategory> + <flag name="script"><short>run an mtx script (lua prefered method) (<ref name="noquotes"/>), no script gives list</short></flag> + <flag name="evaluate"><short>run code passed on the commandline (between quotes)</short></flag> + <flag name="execute"><short>run a script or program (texmfstart method) (<ref name="noquotes"/>)</short></flag> + <flag name="resolve"><short>resolve prefixed arguments</short></flag> + <flag name="ctxlua"><short>run internally (using preloaded libs)</short></flag> + <flag name="internal"><short>run script using built in libraries (same as <ref name="ctxlua"/>)</short></flag> + <flag name="locate"><short>locate given filename in database (default) or system (<ref name="first"/> <ref name="all"/> <ref name="detail"/>)</short></flag> + </subcategory> + <subcategory> + <flag name="autotree"><short>use texmf tree cf. env texmfstart_tree or texmfstarttree</short></flag> + <flag name="tree" value="pathtotree"><short>use given texmf tree (default file: setuptex.tmf)</short></flag> + <flag name="environment" value="name"><short>use given (tmf) environment file</short></flag> + <flag name="path" value="runpath"><short>go to given path before execution</short></flag> + <flag name="ifchanged" value="filename"><short>only execute when given file has changed (md checksum)</short></flag> + <flag name="iftouched" value="old,new"><short>only execute when given file has changed (time stamp)</short></flag> + </subcategory> + <subcategory> + <flag name="makestubs"><short>create stubs for (context related) scripts</short></flag> + <flag name="removestubs"><short>remove stubs (context related) scripts</short></flag> + <flag name="stubpath" value="binpath"><short>paths where stubs wil be written</short></flag> + <flag name="windows"><short>create windows (mswin) stubs</short></flag> + <flag name="unix"><short>create unix (linux) stubs</short></flag> + </subcategory> + <subcategory> + <flag name="verbose"><short>give a bit more info</short></flag> + <flag name="trackers" value="list"><short>enable given trackers</short></flag> + <flag name="progname" value="str"><short>format or backend</short></flag> + <flag name="systeminfo" value="str"><short>show current operating system, processor, etc</short></flag> + </subcategory> + <subcategory> + <flag name="edit"><short>launch editor with found file</short></flag> + <flag name="launch"><short>launch files like manuals, assumes os support (<ref name="all"/>)</short></flag> + </subcategory> + <subcategory> + <flag name="timedrun"><short>run a script and time its run</short></flag> + <flag name="autogenerate"><short>regenerate databases if needed (handy when used to run context in an editor)</short></flag> + </subcategory> + <subcategory> + <flag name="usekpse"><short>use kpse as fallback (when no mkiv and cache installed, often slower)</short></flag> + <flag name="forcekpse"><short>force using kpse (handy when no mkiv and cache installed but less functionality)</short></flag> + </subcategory> + <subcategory> + <flag name="prefixes"><short>show supported prefixes</short></flag> + </subcategory> + <subcategory> + <flag name="generate"><short>generate file database</short></flag> + </subcategory> + <subcategory> + <flag name="variables"><short>show configuration variables</short></flag> + <flag name="configurations"><short>show configuration order</short></flag> + </subcategory> + <subcategory> + <flag name="directives"><short>show (known) directives</short></flag> + <flag name="trackers"><short>show (known) trackers</short></flag> + <flag name="experiments"><short>show (known) experiments</short></flag> + </subcategory> + <subcategory> + <flag name="expand-braces"><short>expand complex variable</short></flag> + <flag name="expand-path"><short>expand variable (resolve paths)</short></flag> + <flag name="expand-var"><short>expand variable (resolve references)</short></flag> + <flag name="show-path"><short>show path expansion of ...</short></flag> + <flag name="var-value"><short>report value of variable</short></flag> + <flag name="find-file"><short>report file location</short></flag> + <flag name="find-path"><short>report path of file</short></flag> + </subcategory> + <subcategory> + <flag name="pattern" value="string"><short>filter variables</short></flag> + </subcategory> + </category> + </flags> +</application> +]] + +local application = logs.application { + name = "mtxrun", + banner = "ConTeXt TDS Runner Tool 1.31", + 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", + ["pl"] = "perl", + ["py"] = "python", + ["rb"] = "ruby", +} + +runners.suffixes = { + 'rb', 'lua', 'py', 'pl' +} + +runners.registered = { + texexec = { 'texexec.rb', false }, -- context mkii runner (only tool not to be luafied) + texutil = { 'texutil.rb', true }, -- old perl based index sorter for mkii (old versions need it) + texfont = { 'texfont.pl', true }, -- perl script that makes mkii font metric files + texfind = { 'texfind.pl', false }, -- perltk based tex searching tool, mostly used at pragma + texshow = { 'texshow.pl', false }, -- perltk based context help system, will be luafied + -- texwork = { 'texwork.pl', false }, -- perltk based editing environment, only used at pragma + 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 }, + concheck = { 'concheck.rb', false }, + runtools = { 'runtools.rb', true }, + textools = { 'textools.rb', true }, + tmftools = { 'tmftools.rb', true }, + ctxtools = { 'ctxtools.rb', true }, + rlxtools = { 'rlxtools.rb', true }, + pdftools = { 'pdftools.rb', true }, + mpstools = { 'mpstools.rb', true }, + -- exatools = { 'exatools.rb', true }, + xmltools = { 'xmltools.rb', true }, + -- luatools = { 'luatools.lua', true }, + mtxtools = { 'mtxtools.rb', true }, + pdftrimwhite = { 'pdftrimwhite.pl', false }, +} + +runners.launchers = { + windows = { }, + unix = { }, +} + +-- like runners.libpath("framework"): looks on script's subpath + +function runners.libpath(...) + package.prepend_libpath(file.dirname(environment.ownscript),...) + package.prepend_libpath(file.dirname(environment.ownname) ,...) +end + +function runners.prepare() + local checkname = environment.argument("ifchanged") + if type(checkname) == "string" and checkname ~= "" then + local oldchecksum = file.loadchecksum(checkname) + local newchecksum = file.checksum(checkname) + if oldchecksum == newchecksum then + if e_verbose then + report("file '%s' is unchanged",checkname) + end + return "skip" + elseif e_verbose then + report("file '%s' is changed, processing started",checkname) + end + file.savechecksum(checkname) + end + local touchname = environment.argument("iftouched") + if type(touchname) == "string" and touchname ~= "" then + local oldname, newname = string.splitup(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 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 + +function runners.execute_script(fullname,internal,nosplit) + local noquote = environment.argument("noquotes") + if fullname and fullname ~= "" then + local state = runners.prepare() + if state == 'error' then + return false + elseif state == 'skip' then + return true + elseif state == "run" then + local path, name, suffix = file.splitname(fullname) + local result = "" + if path ~= "" then + result = fullname + elseif name then + name = gsub(name,"^int[%a]*:",function() + internal = true + return "" + end ) + name = gsub(name,"^script:","") + if suffix == "" and runners.registered[name] and runners.registered[name][1] then + name = runners.registered[name][1] + suffix = file.suffix(name) + end + if suffix == "" then + -- loop over known suffixes + for _,s in pairs(runners.suffixes) do + result = resolvers.findfile(name .. "." .. s, 'texmfscripts') + if result ~= "" then + break + end + end + elseif runners.applications[suffix] then + result = resolvers.findfile(name, 'texmfscripts') + else + -- maybe look on path + result = resolvers.findfile(name, 'other text files') + end + end + if result and result ~= "" then + if not no_split then + local before, after = environment.splitarguments(fullname) -- already done + environment.arguments_before, environment.arguments_after = before, after + end + if internal then + arg = { } for _,v in pairs(environment.arguments_after) do arg[#arg+1] = v end + environment.ownscript = result + dofile(result) + else +local texmfcnf = resolvers.getenv("TEXMFCNF") +if not texmfcnf or texmfcnf == "" then + texmfcnf = resolvers.expandedpathfromlist(resolvers.splitpath(resolvers.resolve(resolvers.luacnfspec))) + resolvers.setenv("TEXMFCNF",table.concat(texmfcnf,";")) -- for running texexec etc (after tl change to texmf-dist) +end + local binary = runners.applications[file.suffix(result)] + result = string.quoted(string.unquoted(result)) + -- if string.match(result,' ') and not string.match(result,"^\".*\"$") then + -- result = '"' .. result .. '"' + -- end + if binary and binary ~= "" then + result = binary .. " " .. result + end + 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 + local code = os.execute(command) -- maybe spawn + if code == 0 then + return true + else + if binary then + binary = file.addsuffix(binary,os.binsuffix) + for p in gmatch(os.getenv("PATH"),"[^"..io.pathseparator.."]+") do + if lfs.isfile(file.join(p,binary)) then + return false + end + end + report() + report("This script needs '%s' which seems not to be installed.",binary) + report() + end + return false + end + end + end + end + end + return false +end + +function runners.execute_program(fullname) + local noquote = environment.argument("noquotes") + if fullname and fullname ~= "" then + local state = runners.prepare() + if state == 'error' then + return false + elseif state == 'skip' then + return true + elseif state == "run" then + local before, after = environment.splitarguments(fullname) + for k=1,#after do after[k] = resolvers.resolve(after[k]) end + environment.initializearguments(after) + fullname = gsub(fullname,"^bin:","") + 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 + end + end + return false +end + +-- 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' + +function runners.handle_stubs(create) + local stubpath = environment.argument('stubpath') or '.' -- 'auto' no longer subpathssupported + local windows = environment.argument('windows') or environment.argument('mswin') or false + local unix = environment.argument('unix') or environment.argument('linux') or false + if not windows and not unix then + if os.platform == "unix" then + unix = true + else + windows = true + end + end + for _,v in pairs(runners.registered) do + local name, doit = v[1], v[2] + if doit then + local base = gsub(file.basename(name), "%.(.-)$", "") + if create then + if windows then + 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),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 + 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 + report("unix stub for '%s' removed",base) + end + end + end + end +end + +function runners.resolve_string(filename) + if filename and filename ~= "" then + runners.report_location(resolvers.resolve(filename)) + end +end + +-- differs from texmfstart where locate appends .com .exe .bat ... todo + +function runners.locate_file(filename) -- was given file but only searches in tree + if filename and filename ~= "" then + if environment.argument("first") then + runners.report_location(resolvers.findfile(filename)) + -- resolvers.dowithfilesandreport(resolvers.findfile,filename) + elseif environment.argument("all") then + local result, status = resolvers.findfiles(filename) + if status and environment.argument("detail") then + runners.report_location(status) + else + runners.report_location(result) + end + else + runners.report_location(resolvers.findgivenfile(filename)) + -- resolvers.dowithfilesandreport(resolvers.findgivenfile,filename) + end + end +end + +function runners.locate_platform() + runners.report_location(os.platform) +end + +function runners.report_location(result) + if type(result) == "table" then + for i=1,#result do + if i > 1 then + io.write("\n") + end + io.write(result[i]) + end + else + io.write(result) + end +end + +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 e_verbose then + report() + report("starting editor: %s",command) + report() + report() + end + os.launch(command) + end +end + +function runners.save_script_session(filename, list) + local t = { } + for i=1,#list do + local key = list[i] + t[key] = environment.arguments[key] + end + io.savedata(filename,table.serialize(t,true)) +end + +function runners.load_script_session(filename) + if lfs.isfile(filename) then + local t = io.loaddata(filename) + if t then + t = loadstring(t) + if t then t = t() end + for key, value in pairs(t) do + environment.arguments[key] = value + end + end + end +end + +function resolvers.launch(str) + -- maybe we also need to test on mtxrun.launcher.suffix environment + -- variable or on windows consult the assoc and ftype vars and such + local launchers = runners.launchers[os.platform] if launchers then + local suffix = file.suffix(str) if suffix then + local runner = launchers[suffix] if runner then + str = runner .. " " .. str + end + end + end + os.launch(str) +end + +function runners.launch_file(filename) + trackers.enable("resolvers.locating") + local allresults = environment.arguments["all"] + local pattern = environment.arguments["pattern"] + if not pattern or pattern == "" then + pattern = filename + end + if not pattern or pattern == "" then + report("provide name or --pattern=") + else + local t = resolvers.findfiles(pattern,nil,allresults) + if not t or #t == 0 then + t = resolvers.findfiles("*/" .. pattern,nil,allresults) + end + if not t or #t == 0 then + t = resolvers.findfiles("*/" .. pattern .. "*",nil,allresults) + end + if t and #t > 0 then + if allresults then + for _, v in pairs(t) do + report("launching %s", v) + resolvers.launch(v) + end + else + report("launching %s", t[1]) + resolvers.launch(t[1]) + end + else + report("no match for %s", pattern) + end + end +end + +local mtxprefixes = { + { "^mtx%-", "mtx-" }, + { "^mtx%-t%-", "mtx-t-" }, +} + +function runners.find_mtx_script(filename) + local function found(name) + local path = file.dirname(name) + if path and path ~= "" then + return false + else + local fullname = own and own.path and file.join(own.path,name) + return io.exists(fullname) and fullname + end + end + filename = file.addsuffix(filename,"lua") + local basename = file.removesuffix(file.basename(filename)) + local suffix = file.suffix(filename) + -- qualified path, raw name + local fullname = file.is_qualified_path(filename) and io.exists(filename) and filename + if fullname and fullname ~= "" then + return fullname + end + -- current path, raw name + fullname = "./" .. filename + fullname = io.exists(fullname) and fullname + if fullname and fullname ~= "" then + return fullname + end + -- mtx- prefix checking + for i=1,#mtxprefixes do + local mtxprefix = mtxprefixes[i] + mtxprefix = find(filename,mtxprefix[1]) and "" or mtxprefix[2] + -- context namespace, mtx-<filename> + fullname = mtxprefix .. filename + 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.findfile(fullname) + if fullname and fullname ~= "" then + return fullname + end + -- context namespace, mtx-<filename minus trailing s> + fullname = mtxprefix .. gsub(basename,"s$","") .. "." .. suffix + fullname = found(fullname) or resolvers.findfile(fullname) + if fullname and fullname ~= "" then + return fullname + end + end + -- context namespace, just <filename> + fullname = resolvers.findfile(filename) + return fullname +end + +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.suffix(fullname) == "cld" then + -- handy in editors where we force --autopdf + report("running cld script: %s",filename) + table.insert(arguments,1,fullname) + table.insert(arguments,"--autopdf") + fullname = runners.find_mtx_script("context") or "" + end + -- retry after generate but only if --autogenerate + if fullname == "" and environment.argument("autogenerate") then -- might become the default + instance.renewcache = true + trackers.enable("resolvers.locating") + resolvers.load() + -- + fullname = runners.find_mtx_script(filename) or "" + end + -- that should do it + if fullname ~= "" then + local state = runners.prepare() + if state == 'error' then + return false + elseif state == 'skip' then + return true + 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.initializearguments(arg) + local loadname = environment.arguments['load'] + if loadname then + if type(loadname) ~= "string" then loadname = file.basename(fullname) end + loadname = file.replacesuffix(loadname,"cfg") + runners.load_script_session(loadname) + end + filename = environment.files[1] + if e_verbose then + report("using script: %s\n",fullname) + end + environment.ownscript = fullname + dofile(fullname) + local savename = environment.arguments['save'] + if savename then + local save_list = runners.save_list + if save_list and next(save_list) then + if type(savename) ~= "string" then savename = file.basename(fullname) end + savename = file.replacesuffix(savename,"cfg") + runners.save_script_session(savename,save_list) + end + end + return true + end + else + if filename == "" or filename == "help" then + local context = resolvers.findfile("mtx-context.lua") + trackers.enable("resolvers.locating") + if context ~= "" then + 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 = match(scriptname,".*mtx%-([^%-]-)%.lua") + if scriptbase then + local data = io.loaddata(scriptname) +local application = match(data,"local application.-=.-(%{.-%})") +if application then + application = loadstring("return " .. application) + if application then + application = application() + local banner = application.banner + if banner then + local description, version = match(banner,"^(.-) ([%d.]+)$") + if description then + valid[#valid+1] = { scriptbase, version, description } + else + valid[#valid+1] = { scriptbase, "", banner } + end + end + end +end + end + end + if #valid > 0 then + application.identify() + report("no script name given, known scripts:") + report() + for k=1,#valid do + local v = valid[k] + report("%-12s %4s %s",v[1],v[2],v[3]) + end + end + else + report("no script name given") + end + else + filename = file.addsuffix(filename,"lua") + if file.is_qualified_path(filename) then + report("unknown script '%s'",filename) + else + report("unknown script '%s' or 'mtx-%s'",filename,filename) + end + end + return false + end +end + +function runners.prefixes() + application.identify() + report() + report(concat(resolvers.allprefixes(true)," ")) +end + +function runners.timedrun(filename) -- just for me + if filename and filename ~= "" then + runners.timed(function() os.execute(filename) end) + end +end + +function runners.timed(action) + statistics.timed(action) +end + +function runners.associate(filename) + os.launch(filename) +end + +function runners.evaluate(code,filename) -- for Luigi + if code == "loop" then + while true do + io.write("> ") + local code = io.read() + if code ~= "" then + local temp = string.match(code,"^= (.*)$") + if temp then + code = "print("..temp..")" + end + local compiled, message = loadstring(code) + if type(compiled) ~= "function" then + io.write("! " .. (message or code).."\n") + else + io.write(compiled()) + end + end + end + else + if type(code) ~= "string" or code == "" then + code = filename + end + if code ~= "" then + local compiled, message = loadstring(code) + if type(compiled) ~= "function" then + io.write("invalid lua code: " .. (message or code)) + return + end + io.write(compiled()) + end + end +end + +function runners.gethelp(filename) + local url = environment.argument("url") + if url and url ~= "" then + local command = string.gsub(environment.argument("command") or "unknown","^%s*\\*(.-)%s*$","%1") + url = utilities.templates.replace(url,{ command = command }) + os.launch(url) + else + report("no --url given") + end +end + +function runners.systeminfo() + report("architecture : %s",os.platform or "<unset>") + report("operating system : %s",os.name or "<unset>") + report("file architecture : %s",os.type or "<unset>") + report("binary path : %s",os.selfdir or "<unset>") + report("binary suffix : %s",os.binsuffix or "<unset>") + report("library suffix : %s",os.libsuffix or "<unset>") +end + +-- this is a bit dirty ... first we store the first filename and next we +-- split the arguments so that we only see the ones meant for this script +-- ... later we will use the second half + +local filename = environment.files[1] or "" +local ok = true + +local before, after = environment.splitarguments(filename) +environment.arguments_before, environment.arguments_after = before, after +environment.initializearguments(before) + +instance.lsrmode = environment.argument("lsr") or false + +e_verbose = environment.arguments["verbose"] -- delayed till here (we need the ones before script) + +if e_verbose then + trackers.enable("resolvers.locating") +end + +-- maybe the unset has to go to this level + +local is_mkii_stub = runners.registered[file.removesuffix(file.basename(filename))] + +local e_argument = environment.argument + +if e_argument("timedlog") then + logs.settimedlog() +end + +if e_argument("usekpse") or e_argument("forcekpse") or is_mkii_stub then + + resolvers.load_tree(e_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", + other = "other text files", + } + + local progname = e_argument("progname") or 'context' + + local function kpse_initialized() + texconfig.kpse_init = true + local t = os.clock() + local k = kpse.original.new("luatex",progname) + local dummy = k:find_file("mtxrun.lua") -- so that we're initialized + report("kpse fallback with progname '%s' initialized in %s seconds",progname,os.clock()-t) + kpse_initialized = function() return k end + return k + end + + local findfile = resolvers.findfile + local showpath = resolvers.showpath + + if e_argument("forcekpse") then + + 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.showpath(name) + return (kpse_initialized():show_path(name)) or "" + end + + elseif e_argument("usekpse") or is_mkii_stub then + + resolvers.load() + + function resolvers.findfile(name,kind) + local found = findfile(name,kind) or "" + if found ~= "" then + return found + else + return (kpse_initialized():find_file(resolvers.cleanpath(name),(kind ~= "" and (remapper[kind] or kind)) or "tex") or "") or "" + end + end + function resolvers.showpath(name) + local found = showpath(name) or "" + if found ~= "" then + return found + else + return (kpse_initialized():show_path(name)) or "" + end + end + + end + + function runners.loadbase() + end + +else + + 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(e_argument('tree'),e_argument("resolve")) + +end + +-- joke .. reminds me of messing with gigi terminals + +if e_argument("ansi") then + + logs.setformatters("ansi") + + local script = e_argument("script") or e_argument("scripts") + + if type(script) == "string" then + logs.writer("]0;"..script.."") -- for Alan to test + end + +end + +if e_argument("script") or e_argument("scripts") then + + -- run a script by loading it (using libs), pass args + + if e_argument("nofiledatabase") then + -- handy for mtx-update + else + runners.loadbase() + end + if is_mkii_stub then + ok = runners.execute_script(filename,false,true) + else + ok = runners.execute_ctx_script(filename) + end + +elseif e_argument("evaluate") then + + runners.evaluate(e_argument("evaluate"),filename) + +elseif e_argument("selfmerge") then + + -- embed used libraries + + runners.loadbase() + local found = locate_libs() + + if found then + local mtxrun = resolvers.findfile("mtxrun.lua") -- includes local name + if lfs.isfile(mtxrun) then + utilities.merger.selfmerge(mtxrun,own.libs,{ found }) + application.report("runner updated on resolved path: %s",mtxrun) + else + utilities.merger.selfmerge(own.name,own.libs,{ found }) + application.report("runner updated on relative path: %s",own.name) + end + end + +elseif e_argument("selfclean") then + + -- remove embedded libraries + + runners.loadbase() + + local mtxrun = resolvers.findfile("mtxrun.lua") -- includes local name + if lfs.isfile(mtxrun) then + utilities.merger.selfclean(mtxrun) + application.report("runner cleaned on resolved path: %s",mtxrun) + else + utilities.merger.selfclean(own.name) + application.report("runner cleaned on relative path: %s",own.name) + end + +elseif e_argument("selfupdate") then + + runners.loadbase() + trackers.enable("resolvers.locating") + resolvers.updatescript(own.name,"mtxrun") + +elseif e_argument("ctxlua") or e_argument("internal") then + + -- run a script by loading it (using libs) + + runners.loadbase() + ok = runners.execute_script(filename,true) + +elseif e_argument("execute") then + + -- execute script + + runners.loadbase() + ok = runners.execute_script(filename) + +elseif e_argument("direct") then + + -- equals bin: + + runners.loadbase() + ok = runners.execute_program(filename) + +elseif e_argument("edit") then + + -- edit file + + runners.loadbase() + runners.edit_script(filename) + +elseif e_argument("launch") then + + runners.loadbase() + runners.launch_file(filename) + +elseif e_argument("associate") then + + runners.associate(filename) + +elseif e_argument("gethelp") then + + runners.gethelp() + +elseif e_argument("makestubs") then + + -- make stubs (depricated) + + runners.handle_stubs(true) + +elseif e_argument("removestubs") then + + -- remove stub (depricated) + + runners.loadbase() + runners.handle_stubs(false) + +elseif e_argument("resolve") then + + -- resolve string + + runners.loadbase() + runners.resolve_string(filename) + +elseif e_argument("locate") then + + -- locate file (only database) + + runners.loadbase() + runners.locate_file(filename) + +elseif e_argument("platform") or e_argument("show-platform") then + + -- locate platform + + runners.loadbase() + runners.locate_platform() + +elseif e_argument("prefixes") then + + runners.loadbase() + runners.prefixes() + +elseif e_argument("timedrun") then + + -- locate platform + + runners.loadbase() + runners.timedrun(filename) + +elseif e_argument("variables") or e_argument("show-variables") or e_argument("expansions") or e_argument("show-expansions") then + + -- luatools: runners.execute_ctx_script("mtx-base","--expansions",filename) + + resolvers.load("nofiles") + resolvers.listers.variables(e_argument("pattern")) + +elseif e_argument("configurations") or e_argument("show-configurations") then + + -- luatools: runners.execute_ctx_script("mtx-base","--configurations",filename) + + resolvers.load("nofiles") + resolvers.listers.configurations() + +elseif e_argument("find-file") then + + -- luatools: runners.execute_ctx_script("mtx-base","--find-file",filename) + + resolvers.load() + local e_all = e_argument("all") + local e_pattern = e_argument("pattern") + local e_format = e_argument("format") + local finder = e_all and resolvers.findfiles or resolvers.findfile + if not e_pattern then + runners.register_arguments(filename) + environment.initializearguments(environment.arguments_after) + resolvers.dowithfilesandreport(finder,environment.files,e_format) + elseif type(e_pattern) == "string" then + resolvers.dowithfilesandreport(finder,{ e_pattern },e_format) + end + +elseif e_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 e_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 e_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 e_argument("expand-var") or e_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 e_argument("show-path") or e_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 e_argument("var-value") or e_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 e_argument("format-path") then + + -- luatools: runners.execute_ctx_script("mtx-base","--format-path",filename) + + resolvers.load() + report(caches.getwritablepath("format")) + +elseif e_argument("pattern") then + + -- luatools + + runners.execute_ctx_script("mtx-base","--pattern='" .. e_argument("pattern") .. "'",filename) + +elseif e_argument("generate") then + + -- luatools + + if filename and filename ~= "" then + resolvers.load("nofiles") + trackers.enable("resolvers.locating") + resolvers.renew(filename) + else + instance.renewcache = true + trackers.enable("resolvers.locating") + resolvers.load() + end + + e_verbose = true + +elseif e_argument("make") or e_argument("ini") or e_argument("compile") then + + -- luatools: runners.execute_ctx_script("mtx-base","--make",filename) + + resolvers.load() + trackers.enable("resolvers.locating") + environment.make_format(filename) + +elseif e_argument("run") then + + -- luatools + + runners.execute_ctx_script("mtx-base","--run",filename) + +elseif e_argument("fmt") then + + -- luatools + + runners.execute_ctx_script("mtx-base","--fmt",filename) + +elseif e_argument("help") and filename=='base' then + + -- luatools + + runners.execute_ctx_script("mtx-base","--help") + +elseif e_argument("version") then + + application.version() + + application.report("source path",environment.ownbin) + +elseif e_argument("directives") then + + directives.show() + +elseif e_argument("trackers") then + + trackers.show() + +elseif e_argument("experiments") then + + experiments.show() + +elseif e_argument("exporthelp") then + + runners.loadbase() + application.export(e_argument("exporthelp"),filename) + +elseif e_argument("systeminfo") then + + runners.systeminfo() + +elseif e_argument("help") or filename=='help' or filename == "" then + + application.help() + +elseif find(filename,"^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) + +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("elapsed lua time: %0.3f seconds",os.runtime()) +end + +if os.type ~= "windows" then + texio.write("\n") -- is this still valid? +end + +if ok == false then ok = 1 elseif ok == true or ok == nil then ok = 0 end + +-- os.exit(ok,true) -- true forces a cleanup in 5.2+ + +os.exit(ok) -- true forces a cleanup in 5.2+ but reports a wrong number then diff --git a/scripts/context/stubs/win64/mtxrunjit.exe b/scripts/context/stubs/win64/mtxrunjit.exe Binary files differnew file mode 100644 index 000000000..93290a6e0 --- /dev/null +++ b/scripts/context/stubs/win64/mtxrunjit.exe diff --git a/scripts/context/stubs/win64/mtxworks.exe b/scripts/context/stubs/win64/mtxworks.exe Binary files differnew file mode 100644 index 000000000..93290a6e0 --- /dev/null +++ b/scripts/context/stubs/win64/mtxworks.exe diff --git a/scripts/context/stubs/win64/pstopdf.exe b/scripts/context/stubs/win64/pstopdf.exe Binary files differnew file mode 100644 index 000000000..93290a6e0 --- /dev/null +++ b/scripts/context/stubs/win64/pstopdf.exe diff --git a/scripts/context/stubs/win64/texexec.exe b/scripts/context/stubs/win64/texexec.exe Binary files differnew file mode 100644 index 000000000..93290a6e0 --- /dev/null +++ b/scripts/context/stubs/win64/texexec.exe diff --git a/scripts/context/stubs/win64/texmfstart.exe b/scripts/context/stubs/win64/texmfstart.exe Binary files differnew file mode 100644 index 000000000..93290a6e0 --- /dev/null +++ b/scripts/context/stubs/win64/texmfstart.exe |