diff options
Diffstat (limited to 'Master/texmf-dist/scripts/context/lua/mtx-context.lua')
-rw-r--r-- | Master/texmf-dist/scripts/context/lua/mtx-context.lua | 1118 |
1 files changed, 920 insertions, 198 deletions
diff --git a/Master/texmf-dist/scripts/context/lua/mtx-context.lua b/Master/texmf-dist/scripts/context/lua/mtx-context.lua index 6c444d53162..71d2eee5611 100644 --- a/Master/texmf-dist/scripts/context/lua/mtx-context.lua +++ b/Master/texmf-dist/scripts/context/lua/mtx-context.lua @@ -6,11 +6,16 @@ if not modules then modules = { } end modules ['mtx-context'] = { license = "see context related readme files" } -texmf.instance = instance -- we need to get rid of this / maybe current instance in global table - scripts = scripts or { } scripts.context = scripts.context or { } +-- a demo cld file: +-- +-- context.starttext() +-- context.chapter("Hello There") +-- context.readfile("tufte","","not found") +-- context.stoptext() + -- l-file / todo function file.needsupdate(oldfile,newfile) @@ -25,30 +30,6 @@ function io.copydata(fromfile,tofile) io.savedata(tofile,io.loaddata(fromfile) or "") end --- luat-inp - -function input.locate_format(name) -- move this to core / luat-xxx - local barename, fmtname = name:gsub("%.%a+$",""), "" - if input.usecache then - local path = file.join(caches.setpath(instance,"formats")) -- maybe platform - fmtname = file.join(path,barename..".fmt") or "" - end - if fmtname == "" then - fmtname = input.find_files(instance,barename..".fmt")[1] or "" - end - fmtname = input.clean_path(fmtname) - if fmtname ~= "" then - barename = fmtname:gsub("%.%a+$","") - local luaname, lucname = barename .. ".lua", barename .. ".luc" - if lfs.isfile(lucname) then - return barename, luaname - elseif lfs.isfile(luaname) then - return barename, luaname - end - end - return nil, nil -end - -- ctx ctxrunner = { } @@ -137,15 +118,15 @@ do elseif ctxdata.ctxname then ctlname = file.replacesuffix(ctxdata.ctxname,'ctl') else - input.report(string.format("invalid ctl name %s",ctlname or "?")) + logs.simple("invalid ctl name: %s",ctlname or "?") return end end if table.is_empty(ctxdata.prepfiles) then - input.report("nothing prepared, no ctl file saved") + logs.simple("nothing prepared, no ctl file saved") os.remove(ctlname) else - input.report(string.format("saving logdata in %s",ctlname)) + logs.simple("saving logdata in: %s",ctlname) f = io.open(ctlname,'w') if f then f:write("<?xml version='1.0' standalone='yes'?>\n\n") @@ -190,8 +171,8 @@ do ctxdata.jobname = file.addsuffix(ctxdata.jobname,'tex') ctxdata.ctxname = file.addsuffix(ctxdata.ctxname,'ctx') - input.report("jobname:",ctxdata.jobname) - input.report("ctxname:",ctxdata.ctxname) + logs.simple("jobname: %s",ctxdata.jobname) + logs.simple("ctxname: %s",ctxdata.ctxname) -- mtxrun should resolve kpse: and file: @@ -208,7 +189,7 @@ do end end - if not found and defaultname and defaultname ~= "" and file.exists(defaultname) then + if not found and defaultname and defaultname ~= "" and lfs.isfile(defaultname) then usedname, found = defaultname, true end @@ -238,17 +219,9 @@ do ctxdata.flags = ctxrunner.reflag(ctxdata.flags) for _, message in ipairs(ctxdata.messages) do - -- message ctxdata.justtext(xml.tostring(message)) + logs.simple("ctx comment: %s", xml.tostring(message)) end - --~ REXML::XPath.each(root,"//ctx:block") do |blk| - --~ if @jobname && blk.attributes['pattern'] then - --~ root.delete(blk) unless @jobname =~ /#{blk.attributes['pattern']}/ - --~ else - --~ root.delete(blk) - --~ end - --~ end - xml.each(ctxdata.xmldata,"ctx:value[@name='job']", function(ek,e,k) e[k] = ctxdata.variables['job'] or "" end) @@ -259,8 +232,8 @@ do commands[ek.at and ek.at['name'] or "unknown"] = ek end) - local suffix = xml.first(ctxdata.xmldata,"/ctx:job/ctx:preprocess/@suffix") or ctxdata.suffix - local runlocal = xml.first(ctxdata.xmldata,"/ctx:job/ctx:preprocess/ctx:processors/@local") + local suffix = xml.filter(ctxdata.xmldata,"/ctx:job/ctx:preprocess/attribute(suffix)") or ctxdata.suffix + local runlocal = xml.filter(ctxdata.xmldata,"/ctx:job/ctx:preprocess/ctx:processors/attribute(local)") runlocal = toboolean(runlocal) @@ -283,6 +256,7 @@ do pattern = ctxrunner.justtext(xml.tostring(pattern)) local oldfiles = dir.glob(pattern) + local pluspath = false if #oldfiles == 0 then -- message: no files match pattern @@ -333,11 +307,12 @@ do end end) -- potential optimization: when mtxrun run internal + command = xml.text(command) command = ctxrunner.justtext(command) -- command is still xml element here - input.report("command",command) - local result = os.spawn(command) + logs.simple("command: %s",command) + local result = os.spawn(command) or 0 if result > 0 then - input.report("error, return code",result) + logs.simple("error, return code: %s",result) end if ctxdata.runlocal then oldfile = file.basename(oldfile) @@ -348,11 +323,11 @@ do file.syncmtimes(oldfile,newfile) ctxdata.prepfiles[oldfile] = true else - input.report("error, check target location of new file", newfile) + logs.simple("error, check target location of new file: %s", newfile) ctxdata.prepfiles[oldfile] = false end else - input.report("old file needs no preprocessing") + logs.simple("old file needs no preprocessing") ctxdata.prepfiles[oldfile] = lfs.isfile(newfile) end end @@ -370,7 +345,8 @@ end -- rest scripts.context.multipass = { - suffixes = { ".tuo", ".tuc" }, +-- suffixes = { ".tuo", ".tuc" }, + suffixes = { ".tuc" }, nofruns = 8, } @@ -400,15 +376,14 @@ scripts.context.backends = { dvips = 'dvips' } -function scripts.context.multipass.makeoptionfile(jobname,ctxdata) +function scripts.context.multipass.makeoptionfile(jobname,ctxdata,kindofrun,currentrun,finalrun) -- take jobname from ctx + jobname = file.removesuffix(jobname) local f = io.open(jobname..".top","w") if f then - local finalrun, kindofrun, currentrun = false, 0, 0 local function someflag(flag) return (ctxdata and ctxdata.flags[flag]) or environment.argument(flag) end ---~ local someflag = environment.argument local function setvalue(flag,format,hash,default) local a = someflag(flag) or default if a and a ~= "" then @@ -421,13 +396,13 @@ function scripts.context.multipass.makeoptionfile(jobname,ctxdata) end end end - local function setvalues(flag,format) + local function setvalues(flag,format,plural) if type(flag) == "table" then for k, v in pairs(flag) do f:write(format:format(v),"\n") end else - local a = someflag(flag) + local a = someflag(flag) or (plural and someflag(flag.."s")) if a and a ~= "" then for v in a:gmatch("%s*([^,]+)") do f:write(format:format(v),"\n") @@ -443,98 +418,235 @@ function scripts.context.multipass.makeoptionfile(jobname,ctxdata) local function setalways(format,...) f:write(format:format(...),"\n") end + -- + setalways("%% runtime options files (command line driven)") + -- setalways("\\unprotect") - setvalue('output' , "\\setupoutput[%s]", scripts.context.backends, 'pdftex') - setalways( "\\setupsystem[\\c!n=%s,\\c!m=%s]", kindofrun, currentrun) - setalways( "\\setupsystem[\\c!type=%s]",os.platform) + -- + setalways("%% special commands, mostly for the ctx development team") + -- + if environment.argument("dumpdelta") then + setalways("\\tracersdumpdelta") + elseif environment.argument("dumphash") then + setalways("\\tracersdumphash") + end + setalways("%% feedback and basic job control") + if type(environment.argument("track")) == "string" then + setvalue ("track" , "\\enabletrackers[%s]") + end + setfixed ("timing" , "\\usemodule[timing]") setfixed ("batchmode" , "\\batchmode") setfixed ("nonstopmode" , "\\nonstopmode") setfixed ("tracefiles" , "\\tracefilestrue") + setfixed ("nostats" , "\\nomkivstatistics") setfixed ("paranoid" , "\\def\\maxreadlevel{1}") - setvalues("modefile" , "\\readlocfile{%s}{}{}") + -- + setalways("%% handy for special styles") + -- + setalways("\\startluacode") + setalways("document = document or { }") + setalways(table.serialize(environment.arguments, "document.arguments")) + setalways(table.serialize(environment.files, "document.files")) + setalways("\\stopluacode") + -- + setalways("%% process info") + -- + setalways( "\\setupsystem[\\c!n=%s,\\c!m=%s]", kindofrun or 0, currentrun or 0) + setalways( "\\setupsystem[\\c!type=%s]",os.platform) + setvalue ("inputfile" , "\\setupsystem[inputfile=%s]") setvalue ("result" , "\\setupsystem[file=%s]") setvalues("path" , "\\usepath[%s]") + setvalue ("setuppath" , "\\setupsystem[\\c!directory={%s}]") + setvalue ("randomseed" , "\\setupsystem[\\c!random=%s]") + setvalue ("arguments" , "\\setupenv[%s]") + setalways("%% modes") + setvalues("modefile" , "\\readlocfile{%s}{}{}") + setvalues("mode" , "\\enablemode[%s]", true) + if ctxdata then + setvalues(ctxdata.modes, "\\enablemode[%s]") + end + -- + setalways("%% options (not that important)") + -- + setalways("\\startsetups *runtime:options") + setvalue ('output' , "\\setupoutput[%s]", scripts.context.backends, 'pdftex') setfixed ("color" , "\\setupcolors[\\c!state=\\v!start]") - setfixed ("nompmode" , "\\runMPgraphicsfalse") -- obsolete, we assume runtime mp graphics - setfixed ("nomprun" , "\\runMPgraphicsfalse") -- obsolete, we assume runtime mp graphics - setfixed ("automprun" , "\\runMPgraphicsfalse") -- obsolete, we assume runtime mp graphics - setfixed ("fast" , "\\fastmode\n") - setfixed ("silentmode" , "\\silentmode\n") setvalue ("separation" , "\\setupcolors[\\c!split=%s]") - setvalue ("setuppath" , "\\setupsystem[\\c!directory={%s}]") setfixed ("noarrange" , "\\setuparranging[\\v!disable]") if environment.argument('arrange') and not finalrun then setalways( "\\setuparranging[\\v!disable]") end - setvalue ("arguments" , "\\setupenv[%s]") - setvalue ("randomseed" , "\\setupsystem[\\c!random=%s]") - setvalues("modes" , "\\enablemode[%s]") - setvalues("mode" , "\\enablemode[%s]") - setvalues("filters" , "\\useXMLfilter[%s]") - setvalues("usemodules" , "\\usemodule[%s]") - setvalues("environments" , "\\environment %s ") - -- ctx stuff + setalways("\\stopsetups") + -- + setalways("%% styles and modules") + -- + setalways("\\startsetups *runtime:modules") + setvalues("filter" , "\\useXMLfilter[%s]", true) + setvalues("usemodule" , "\\usemodule[%s]", true) + setvalues("environment" , "\\environment %s ", true) if ctxdata then - setvalues(ctxdata.modes, "\\enablemode[%s]") setvalues(ctxdata.modules, "\\usemodule[%s]") setvalues(ctxdata.environments, "\\environment %s ") end - -- done - setalways( "\\protect") - setalways( "\\endinput") + setalways("\\stopsetups") + -- + setalways("%% done") + -- + setalways("\\protect \\endinput") f:close() end end function scripts.context.multipass.copyluafile(jobname) - io.savedata(jobname..".tuc",io.loaddata(jobname..".tua") or "") -end - -function scripts.context.multipass.copytuifile(jobname) - local f, g = io.open(jobname..".tui"), io.open(jobname..".tuo",'w') - if f and g then - g:write("% traditional utility file, only commands written by mtxrun/context\n%\n") - for line in f:lines() do - if line:find("^c ") then - g:write((line:gsub("^c ","")),"%\n") - end - end - g:write("\\endinput\n") - f:close() - g:close() +-- io.savedata(jobname..".tuc",io.loaddata(jobname..".tua") or "") + local tuaname, tucname = jobname..".tua", jobname..".tuc" + if lfs.isfile(tuaname) then + os.remove(tucname) + os.rename(tuaname,tucname) end end +-- obsolete: +-- +-- function scripts.context.multipass.copytuifile(jobname) +-- local tuiname, tuoname = jobname .. ".tui", jobname .. ".tuo" +-- if lfs.isfile(tuiname) then +-- local f, g = io.open(tuiname), io.open(tuoname,'w') +-- if f and g then +-- g:write("% traditional utility file, only commands written by mtxrun/context\n%\n") +-- for line in f:lines() do +-- if line:find("^c ") then +-- g:write((line:gsub("^c ","")),"%\n") +-- end +-- end +-- g:write("\\endinput\n") +-- f:close() +-- g:close() +-- end +-- else +-- -- os.remove(tuoname) +-- end +-- end + +scripts.context.cldsuffixes = table.tohash { + "cld", +} + scripts.context.xmlsuffixes = table.tohash { "xml", } -function scripts.context.run(ctxdata) - local function makestub(format,filename) - local stubname = file.replacesuffix(file.basename(filename),'run') - local f = io.open(stubname,'w') - if f then - f:write("\\starttext\n") - f:write(string.format(format,filename),"\n") - f:write("\\stoptext\n") - f:close() - filename = stubname +scripts.context.luasuffixes = table.tohash { + "lua", +} + +scripts.context.beforesuffixes = { + "tuo", "tuc" +} +scripts.context.aftersuffixes = { + "pdf", "tuo", "tuc", "log" +} + +scripts.context.interfaces = { + en = "cont-en", + uk = "cont-uk", + de = "cont-de", + fr = "cont-fr", + nl = "cont-nl", + cz = "cont-cz", + it = "cont-it", + ro = "cont-ro", + pe = "cont-pe", + -- for taco and me + -- xp = "cont-xp", +} + +scripts.context.defaultformats = { + "cont-en", + "cont-nl", +-- "cont-xp", + "mptopdf", +-- "metatex", + "metafun", + "plain" +} + +local function analyze(filename) + local f = io.open(file.addsuffix(filename,"tex")) + if f then + local t = { } + local line = f:read("*line") or "" + local preamble = line:match("[\254\255]*%%%s+(.+)$") -- there can be an utf bomb in front + if preamble then + for key, value in preamble:gmatch("(%S+)=(%S+)") do + t[key] = value + end + t.type = "tex" + elseif line:find("^<?xml ") then + t.type = "xml" + end + if not t.engine then + t.engine = 'luatex' end - return filename + f:close() + return t end + return nil +end + +local function makestub(format,filename) + local stubname = file.replacesuffix(file.basename(filename),'run') + local f = io.open(stubname,'w') + if f then + f:write("\\starttext\n") + f:write(string.format(format,filename),"\n") + f:write("\\stoptext\n") + f:close() + filename = stubname + end + return filename +end + +function scripts.context.openpdf(name) + os.spawn(string.format('pdfopen --file "%s" 2>&1', file.replacesuffix(name,"pdf"))) +end +function scripts.context.closepdf(name) + os.spawn(string.format('pdfclose --file "%s" 2>&1', file.replacesuffix(name,"pdf"))) +end + +--~ function scripts.context.openpdf(name) +--~ -- somehow two instances start up, one with a funny filename +--~ os.spawn(string.format("\"c:/program files/kde/bin/okular.exe\" --unique %s",file.replacesuffix(name,"pdf"))) +--~ end +--~ function scripts.context.closepdf(name) +--~ -- +--~ end + +function scripts.context.run(ctxdata,filename) + -- filename overloads environment.files + local files = (filename and { filename }) or environment.files if ctxdata then -- todo: interface for k,v in pairs(ctxdata.flags) do environment.setargument(k,v) end end - local files = environment.files if #files > 0 then - input.identify_cnf(instance) - input.load_cnf(instance) - input.expand_variables(instance) - local formatname = "cont-en" - local formatfile, scriptfile = input.locate_format(formatname) + -- + local interface = environment.argument("interface") + -- todo: environment.argument("interface","en") + interface = (type(interface) == "string" and interface) or "en" + -- + local formatname = scripts.context.interfaces[interface] or "cont-en" + local formatfile, scriptfile = resolvers.locate_format(formatname) + -- this catches the command line + if not formatfile or not scriptfile then + logs.simple("warning: no format found, forcing remake (commandline driven)") + scripts.context.generate() + scripts.context.make(formatname) + formatfile, scriptfile = resolvers.locate_format(formatname) + end + -- if formatfile and scriptfile then for _, filename in ipairs(files) do local basename, pathname = file.basename(filename), file.dirname(filename) @@ -542,66 +654,278 @@ function scripts.context.run(ctxdata) if pathname == "" then filename = "./" .. filename end - -- also other stubs - if environment.argument("forcexml") or scripts.context.xmlsuffixes[file.extname(filename) or "?"] then -- mkii - filename = makestub("\\processXMLfilegrouped{%s}",filename) - elseif environment.argument("processxml") then -- mkiv - filename = makestub("\\xmlprocess{%s}",filename) - end - -- - if environment.argument("autopdf") then - os.spawn(string.format('pdfclose --file "%s" 2>&1', file.replacesuffix(filename,"pdf"))) - end - -- - local command = "luatex --fmt=" .. string.quote(formatfile) .. " --lua=" .. string.quote(scriptfile) .. " " .. string.quote(filename) - local oldhash, newhash = scripts.context.multipass.hashfiles(jobname), { } - scripts.context.multipass.makeoptionfile(jobname,ctxdata) - for i=1, scripts.context.multipass.nofruns do - input.report(string.format("run %s: %s",i,command)) - local returncode = os.spawn(command) - input.report("return code: " .. returncode) - if returncode > 0 then - input.report("fatal error, run aborted") - break + -- look at the first line + local a = analyze(filename) + if a and (a.engine == 'pdftex' or a.engine == 'xetex' or environment.argument("pdftex") or environment.argument("xetex")) then + local texexec = resolvers.find_file("texexec.rb") or "" + if texexec ~= "" then + os.setenv("RUBYOPT","") + local command = string.format("ruby %s %s",texexec,environment.reconstruct_commandline(environment.arguments_after)) + os.exec(command) + end + else + if a and a.interface and a.interface ~= interface then + formatname = scripts.context.interfaces[a.interface] or formatname + formatfile, scriptfile = resolvers.locate_format(formatname) + end + -- this catches the command line + if not formatfile or not scriptfile then + logs.simple("warning: no format found, forcing remake (source driven)") + scripts.context.generate() + scripts.context.make(formatname) + formatfile, scriptfile = resolvers.locate_format(formatname) + end + if formatfile and scriptfile then + -- we default to mkiv xml ! + local suffix = file.extname(filename) or "?" + if scripts.context.xmlsuffixes[suffix] or environment.argument("forcexml") then + if environment.argument("mkii") then + filename = makestub("\\processXMLfilegrouped{%s}",filename) + else + filename = makestub("\\xmlprocess{\\xmldocument}{%s}{}",filename) + end + elseif scripts.context.cldsuffixes[suffix] or environment.argument("forcecld") then + filename = makestub("\\ctxlua{context.runfile('%s')}",filename) + elseif scripts.context.luasuffixes[suffix] or environment.argument("forcelua") then + filename = makestub("\\ctxlua{dofile('%s')}",filename) + end + -- + -- todo: also other stubs + -- + local suffix, resultname = environment.argument("suffix"), environment.argument("result") + if type(suffix) == "string" then + resultname = file.removesuffix(jobname) .. suffix + end + local oldbase, newbase = "", "" + if type(resultname) == "string" then + oldbase = file.removesuffix(jobname) + newbase = file.removesuffix(resultname) + if oldbase ~= newbase then + for _, suffix in pairs(scripts.context.beforesuffixes) do + local oldname = file.addsuffix(oldbase,suffix) + local newname = file.addsuffix(newbase,suffix) + local tmpname = "keep-"..oldname + os.remove(tmpname) + os.rename(oldname,tmpname) + os.remove(oldname) + os.rename(newname,oldname) + end + else + resultname = nil + end + else + resultname = nil + end + -- + if environment.argument("autopdf") then + scripts.context.closepdf(filename) + if resultname then + scripts.context.closepdf(resultname) + end + end + -- + local okay = statistics.check_fmt_status(formatfile) + if okay ~= true then + logs.simple("warning: %s, forcing remake",tostring(okay)) + scripts.context.generate() + scripts.context.make(formatname) + end + -- + local flags = { } + if environment.argument("batchmode") then + flags[#flags+1] = "--interaction=batchmode" + end + flags[#flags+1] = "--fmt=" .. string.quote(formatfile) + flags[#flags+1] = "--lua=" .. string.quote(scriptfile) + flags[#flags+1] = "--backend=pdf" + local command = string.format("luatex %s %s", table.concat(flags," "), string.quote(filename)) + local oldhash, newhash = scripts.context.multipass.hashfiles(jobname), { } + local once = environment.argument("once") + local maxnofruns = (once and 1) or scripts.context.multipass.nofruns + local arrange = environment.argument("arrange") + for i=1,maxnofruns do + -- 1:first run, 2:successive run, 3:once, 4:last of maxruns + local kindofrun = (once and 3) or (i==1 and 1) or (i==maxnofruns and 4) or 2 + scripts.context.multipass.makeoptionfile(jobname,ctxdata,kindofrun,i,false) -- kindofrun, currentrun, final + logs.simple("run %s: %s",i,command) + local returncode, errorstring = os.spawn(command) + --~ if returncode == 3 then + --~ scripts.context.generate() + --~ scripts.context.make(formatname) + --~ returncode, errorstring = os.spawn(command) + --~ if returncode == 3 then + --~ logs.simple("fatal error, return code 3, message: %s",errorstring or "?") + --~ os.exit(1) + --~ end + --~ end + if not returncode then + logs.simple("fatal error, no return code, message: %s",errorstring or "?") + os.exit(1) + break + elseif returncode > 0 then + logs.simple("fatal error, return code: %s",returncode or "?") + os.exit(returncode) + break + else + scripts.context.multipass.copyluafile(jobname) + -- scripts.context.multipass.copytuifile(jobname) + newhash = scripts.context.multipass.hashfiles(jobname) + if scripts.context.multipass.changed(oldhash,newhash) then + oldhash = newhash + else + break + end + end + end + -- + if arrange then + local kindofrun = 3 + scripts.context.multipass.makeoptionfile(jobname,ctxdata,kindofrun,i,true) -- kindofrun, currentrun, final + logs.simple("arrange run: %s",command) + local returncode, errorstring = os.spawn(command) + if not returncode then + logs.simple("fatal error, no return code, message: %s",errorstring or "?") + os.exit(1) + elseif returncode > 0 then + logs.simple("fatal error, return code: %s",returncode or "?") + os.exit(returncode) + end + end + -- + if environment.argument("purge") then + scripts.context.purge_job(filename) + elseif environment.argument("purgeall") then + scripts.context.purge_job(filename,true) + end + -- + os.remove(jobname..".top") + -- + if resultname then + for _, suffix in pairs(scripts.context.aftersuffixes) do + local oldname = file.addsuffix(oldbase,suffix) + local newname = file.addsuffix(newbase,suffix) + local tmpname = "keep-"..oldname + os.remove(newname) + os.rename(oldname,newname) + os.rename(tmpname,oldname) + end + logs.simple("result renamed to: %s",newbase) + end + -- + if environment.argument("purge") then + scripts.context.purge_job(resultname) + elseif environment.argument("purgeall") then + scripts.context.purge_job(resultname,true) + end + -- + if environment.argument("autopdf") then + scripts.context.openpdf(resultname or filename) + end + -- + if environment.argument("timing") then + logs.line() + logs.simple("you can process (timing) statistics with:",jobname) + logs.line() + logs.simple("context --extra=timing '%s'",jobname) + logs.simple("mtxrun --script timing --xhtml [--launch --remove] '%s'",jobname) + logs.line() + end else - scripts.context.multipass.copyluafile(jobname) - scripts.context.multipass.copytuifile(jobname) - newhash = scripts.context.multipass.hashfiles(jobname) - if scripts.context.multipass.changed(oldhash,newhash) then - oldhash = newhash + if formatname then + logs.simple("error, no format found with name: %s, skipping",formatname) else - break + logs.simple("error, no format found (provide formatname or interface)") end + break end end - -- - -- todo: result - -- - if environment.argument("autopdf") then - os.spawn(string.format('pdfopen --file "%s" 2>&1', file.replacesuffix(filename,"pdf"))) - end - -- end else - input.verbose = true - input.report("error", "no format found with name " .. formatname) + if formatname then + logs.simple("error, no format found with name: %s, aborting",formatname) + else + logs.simple("error, no format found (provide formatname or interface)") + end end end end -function scripts.context.make() - local list = (environment.files[1] and environment.files) or { "cont-en", "cont-nl", "mptopdf" } - for _, name in ipairs(list) do - local command = "luatools --make --compile " .. name - input.report("running command: " .. command) +function scripts.context.pipe() + -- context --pipe + -- context --pipe --purge --dummyfile=whatever.tmp + local interface = environment.argument("interface") + interface = (type(interface) == "string" and interface) or "en" + local formatname = scripts.context.interfaces[interface] or "cont-en" + local formatfile, scriptfile = resolvers.locate_format(formatname) + if not formatfile or not scriptfile then + logs.simple("warning: no format found, forcing remake (commandline driven)") + scripts.context.generate() + scripts.context.make(formatname) + formatfile, scriptfile = resolvers.locate_format(formatname) + end + if formatfile and scriptfile then + local okay = statistics.check_fmt_status(formatfile) + if okay ~= true then + logs.simple("warning: %s, forcing remake",tostring(okay)) + scripts.context.generate() + scripts.context.make(formatname) + end + local flags = { + "--interaction=scrollmode", + "--fmt=" .. string.quote(formatfile), + "--lua=" .. string.quote(scriptfile), + "--backend=pdf", + } + local filename = environment.argument("dummyfile") or "" + if filename == "" then + filename = "\\relax" + logs.simple("entering scrollmode, end job with \\end") + else + filename = file.addsuffix(filename,"tmp") + io.savedata(filename,"\\relax") + scripts.context.multipass.makeoptionfile(filename,{ flags = flags },3,1,false) -- kindofrun, currentrun, final + logs.simple("entering scrollmode using '%s' with optionfile, end job with \\end",filename) + end + local command = string.format("luatex %s %s", table.concat(flags," "), string.quote(filename)) os.spawn(command) + if environment.argument("purge") then + scripts.context.purge_job(filename) + elseif environment.argument("purgeall") then + scripts.context.purge_job(filename,true) + os.remove(filename) + end + else + if formatname then + logs.simple("error, no format found with name: %s, aborting",formatname) + else + logs.simple("error, no format found (provide formatname or interface)") + end + end +end + +function scripts.context.make(name) + local runners = { + "luatools --make --compile ", + (environment.argument("pdftex") and "mtxrun texexec.rb --make --pdftex ") or false, + (environment.argument("xetex") and "mtxrun texexec.rb --make --xetex " ) or false, + } + local list = (name and { name }) or (environment.files[1] and environment.files) or scripts.context.defaultformats + for _, name in ipairs(list) do + name = scripts.context.interfaces[name] or name + for _, runner in ipairs(runners) do + if runner then + local command = runner .. name + logs.simple("running command: %s",command) + os.spawn(command) + end + end end end function scripts.context.generate() -- hack, should also be a shared function local command = "luatools --generate " - input.report("running command: " .. command) + logs.simple("running command: %s",command) os.spawn(command) end @@ -612,110 +936,508 @@ function scripts.context.ctx() scripts.context.run(ctxdata) end +function scripts.context.autoctx() + local ctxdata = nil + local files = (filename and { filename }) or environment.files + local firstfile = #files > 0 and files[1] + if firstfile and file.extname(firstfile) == "xml" then + local f = io.open(firstfile) + if f then + local chunk = f:read(512) or "" + f:close() + local ctxname = string.match(chunk,"<%?context%-directive%s+job%s+ctxfile%s+([^ ]-)%s*?>") + if ctxname then + ctxdata = ctxrunner.new() + ctxdata.jobname = firstfile + ctxrunner.manipulate(ctxdata,ctxname) + end + end + end + scripts.context.run(ctxdata) +end + +-- todo: quite after first image + +local template = [[ + \starttext + \startMPpage %% %s + input "%s" ; + \stopMPpage + \stoptext +]] + +local loaded = false + +function scripts.context.metapost() + local filename = environment.files[1] or "" +--~ local tempname = "mtx-context-metapost.tex" +--~ local tempdata = string.format(template,"metafun",filename) +--~ io.savedata(tempname,tempdata) +--~ environment.files[1] = tempname +--~ environment.setargument("result",file.removesuffix(filename)) +--~ environment.setargument("once",true) +--~ scripts.context.run() + if not loaded then + dofile(resolvers.find_file("mlib-run.lua")) + loaded = true + commands = commands or { } + commands.writestatus = logs.report + end + local formatname = environment.arguments("format") or "metafun" + if formatname == "" or type(format) == "boolean" then + formatname = "metafun" + end + if environment.arguments("svg") then + metapost.directrun(formatname,filename,"svg") + else + metapost.directrun(formatname,filename,"mps") + end +end + function scripts.context.version() - local name = input.find_file(instance,"context.tex") + local name = resolvers.find_file("context.tex") if name ~= "" then - input.report(string.format("main context file: %s",name)) + logs.simple("main context file: %s",name) local data = io.loaddata(name) if data then local version = data:match("\\edef\\contextversion{(.-)}") if version then - input.report(string.format("current version : %s",version)) + logs.simple("current version: %s",version) else - input.report("context version: unknown, no timestamp found") + logs.simple("context version: unknown, no timestamp found") end else - input.report("context version: unknown, load error") + logs.simple("context version: unknown, load error") end else - input.report("main context file: unknown, 'context.tex' not found") + logs.simple("main context file: unknown, 'context.tex' not found") + end +end + +local generic_files = { + "texexec.tex", "texexec.tui", "texexec.tuo", + "texexec.tuc", "texexec.tua", + "texexec.ps", "texexec.pdf", "texexec.dvi", + "cont-opt.tex", "cont-opt.bak" +} + +local obsolete_results = { + "dvi", +} + +local temporary_runfiles = { + "tui", "tua", "tup", "ted", "tes", "top", + "log", "tmp", "run", "bck", "rlg", + "mpt", "mpx", "mpd", "mpo", "mpb", "ctl", + "synctex.gz", "pgf" +} + +local persistent_runfiles = { + "tuo", "tub", "top", "tuc" +} + +local function purge_file(dfile,cfile) + if cfile and lfs.isfile(cfile) then + if os.remove(dfile) then + return file.basename(dfile) + end + else + if os.remove(dfile) then + return file.basename(dfile) + end + end +end + +function scripts.context.purge_job(jobname,all) + jobname = file.basename(jobname) + local filebase = file.removesuffix(jobname) + local deleted = { } + for _, suffix in ipairs(obsolete_results) do + deleted[#deleted+1] = purge_file(filebase.."."..suffix,filebase..".pdf") + end + for _, suffix in ipairs(temporary_runfiles) do + deleted[#deleted+1] = purge_file(filebase.."."..suffix) + end + if all then + for _, suffix in ipairs(persistent_runfiles) do + deleted[#deleted+1] = purge_file(filebase.."."..suffix) + end + end + if #deleted > 0 then + logs.simple("purged files: %s", table.join(deleted,", ")) + end +end + +function scripts.context.purge(all) + local all = all or environment.argument("all") + local pattern = environment.argument("pattern") or "*.*" + local files = dir.glob(pattern) + local obsolete = table.tohash(obsolete_results) + local temporary = table.tohash(temporary_runfiles) + local persistent = table.tohash(persistent_runfiles) + local generic = table.tohash(generic_files) + local deleted = { } + for _, name in ipairs(files) do + local suffix = file.extname(name) + local basename = file.basename(name) + if obsolete[suffix] or temporary[suffix] or persistent[suffix] or generic[basename] then + deleted[#deleted+1] = purge_file(name) + end + end + if #deleted > 0 then + logs.simple("purged files: %s", table.join(deleted,", ")) + end +end + +--~ purge_for_files("test",true) +--~ purge_all_files() + +local function touch(name,pattern) + local name = resolvers.find_file(name) + local olddata = io.loaddata(name) + if olddata then + local oldversion, newversion = "", os.date("%Y.%m.%d %H:%M") + local newdata, ok = olddata:gsub(pattern,function(pre,mid,post) + oldversion = mid + return pre .. newversion .. post + end) + if ok > 0 then + local backup = file.replacesuffix(name,"tmp") + os.remove(backup) + os.rename(name,backup) + io.savedata(name,newdata) + return true, oldversion, newversion, name + else + return false + end end end function scripts.context.touch() if environment.argument("expert") then - local function touch(name,pattern) - local name = input.find_file(instance,name) - local olddata = io.loaddata(name) - if olddata then - local oldversion, newversion = "", os.date("%Y.%M.%d %H:%m") - local newdata, ok = olddata:gsub(pattern,function(pre,mid,post) - oldversion = mid - return pre .. newversion .. post - end) - if ok > 0 then - local backup = file.replacesuffix(name,"tmp") - os.remove(backup) - os.rename(name,backup) - io.savedata(name,newdata) - return true, oldversion, newversion, name - else - return false - end - end - end local done, oldversion, newversion, foundname = touch("context.tex", "(\\edef\\contextversion{)(.-)(})") if done then - input.report(string.format("old version : %s", oldversion)) - input.report(string.format("new version : %s", newversion)) - input.report(string.format("touched file: %s", foundname)) + logs.simple("old version : %s", oldversion) + logs.simple("new version : %s", newversion) + logs.simple("touched file: %s", foundname) local ok, _, _, foundname = touch("cont-new.tex", "(\\newcontextversion{)(.-)(})") if ok then - input.report(string.format("touched file: %s", foundname)) + logs.simple("touched file: %s", foundname) + end + local ok, _, _, foundname = touch("cont-xp.tex", "(\\edef\\contextversion{)(.-)(})") + if ok then + logs.simple("touched file: %s", foundname) + end + end + end +end + +-- extras + +function scripts.context.extras(pattern) + local found = resolvers.find_file("context.tex") + if found == "" then + logs.simple("unknown extra: %s", extra) + else + pattern = file.join(dir.expand_name(file.dirname(found)),string.format("mtx-context-%s.tex",pattern or "*")) + local list = dir.glob(pattern) + if not extra or extra == "" then + logs.extendbanner("extras") + else + logs.extendbanner(extra) + end + for k,v in ipairs(list) do + local data = io.loaddata(v) or "" + data = string.match(data,"begin help(.-)end help") + if data then + local h = { string.format("extra: %s (%s)",string.gsub(v,"^.*mtx%-context%-(.-)%.tex$","%1"),v) } + for s in string.gmatch(data,"%% *(.-)[\n\r]") do + h[#h+1] = s + end + logs.help(table.concat(h,"\n"),"nomoreinfo") + end + end + end +end + +function scripts.context.extra() + local extra = environment.argument("extra") + if type(extra) == "string" then + if environment.argument("help") then + scripts.context.extras(extra) + else + local fullextra = extra + if not string.find(fullextra,"mtx%-context%-") then + fullextra = "mtx-context-" .. extra + end + local foundextra = resolvers.find_file(fullextra) + if foundextra == "" then + scripts.context.extras() + return + else + logs.simple("processing extra: %s", foundextra) + end + environment.setargument("purgeall",true) + local result = environment.setargument("result") or "" + if result == "" then + environment.setargument("result","context-extra") end + scripts.context.run(nil,foundextra) end + else + scripts.context.extras() end end +-- todo: we need to do a dummy run + +function scripts.context.track() + environment.files = { "m-track" } + scripts.context.multipass.nofruns = 1 + scripts.context.run() + -- maybe filter from log +end + function scripts.context.timed(action) - input.starttiming(scripts.context) - action() - input.stoptiming(scripts.context) - input.report("total runtime: " .. input.elapsedtime(scripts.context)) + statistics.timed(action) +end + +local zipname = "cont-tmf.zip" +local mainzip = "http://www.pragma-ade.com/context/latest/" .. zipname +local validtrees = { "texmf-local", "texmf-context" } + +function zip.loaddata(zipfile,filename) -- should be in zip lib + local f = zipfile:open(filename) + if f then + local data = f:read("*a") + f:close() + return data + end + return nil +end + +function scripts.context.update() + local force = environment.argument("force") + local socket = require("socket") + local http = require("socket.http") + local basepath = resolvers.find_file("context.tex") or "" + if basepath == "" then + logs.simple("quiting, no 'context.tex' found") + return + end + local basetree = basepath.match(basepath,"^(.-)tex/context/base/context.tex$") or "" + if basetree == "" then + logs.simple("quiting, no proper tds structure (%s)",basepath) + return + end + local function is_okay(basetree) + for _, tree in next, validtrees do + local pattern = string.gsub(tree,"%-","%%-") + if basetree:find(pattern) then + return tree + end + end + return false + end + local okay = is_okay(basetree) + if not okay then + logs.simple("quiting, tree '%s' is protected",okay) + return + else + logs.simple("updating tree '%s'",okay) + end + if not lfs.chdir(basetree) then + logs.simple("quiting, unable to change to '%s'",okay) + return + end + logs.simple("fetching '%s'",mainzip) + local latest = http.request(mainzip) + if not latest then + logs.simple("context tree '%s' can be updated, use --force",okay) + return + end + io.savedata("cont-tmf.zip",latest) + if false then + -- variant 1 + os.execute("mtxrun --script unzip cont-tmf.zip") + else + -- variant 2 + local zipfile = zip.open(zipname) + if not zipfile then + logs.simple("quiting, unable to open '%s'",zipname) + return + end + local newfile = zip.loaddata(zipfile,"tex/context/base/context.tex") + if not newfile then + logs.simple("quiting, unable to open '%s'","context.tex") + return + end + local oldfile = io.loaddata(resolvers.find_file("context.tex")) or "" + local function versiontonumber(what,str) + local version = str:match("\\edef\\contextversion{(.-)}") or "" + local year, month, day, hour, minute = str:match("\\edef\\contextversion{(%d+)%.(%d+)%.(%d+) *(%d+)%:(%d+)}") + if year and minute then + local time = os.time { year=year,month=month,day=day,hour=hour,minute=minute} + logs.simple("%s version: %s (%s)",what,version,time) + return time + else + logs.simple("%s version: %s (unknown)",what,version) + return nil + end + end + local oldversion = versiontonumber("old",oldfile) + local newversion = versiontonumber("new",newfile) + if not oldversion or not newversion then + logs.simple("quiting, version cannot be determined") + return + elseif oldversion == newversion then + logs.simple("quiting, your current version is up-to-date") + return + elseif oldversion > newversion then + logs.simple("quiting, your current version is newer") + return + end + for k in zipfile:files() do + local filename = k.filename + if filename:find("/$") then + lfs.mkdir(filename) + else + local data = zip.loaddata(zipfile,filename) + if data then + if force then + io.savedata(filename,data) + end + logs.simple(filename) + end + end + end + for _, scriptname in next, { "luatools.lua", "mtxrun.lua" } do + local oldscript = resolvers.find_file(scriptname) or "" + if oldscript ~= "" and is_okay(oldscript) then + local newscript = "./scripts/context/lua/" .. scriptname + local data = io.loaddata(newscript) or "" + if data ~= "" then + logs.simple("replacing script '%s' by '%s'",oldscript,newscript) + if force then + io.savedata(oldscript,data) + end + end + else + logs.simple("keeping script '%s'",oldscript) + end + end + if force then + os.execute("context --generate") + os.execute("context --make") + end + end + if force then + logs.simple("context tree '%s' has been updated",okay) + else + logs.simple("context tree '%s' can been updated (use --force)",okay) + end end -banner = banner .. " | context tools " +logs.extendbanner("ConTeXt Tools 0.51",true) messages.help = [[ --run process (one or more) files (default action) ---make create context formats formats +--make create context formats --generate generate file database etc. --ctx=name use ctx file --version report installed context version ---autopdf open pdf file afterwards +--forcexml force xml stub (optional flag: --mkii) +--forcecld force cld (context lua document) stub +--autopdf close pdf file in viewer and start pdf viewer afterwards +--once only one run +--purge(all) purge files (--pattern=...) +--result=name rename result to given name +--arrange run extra arrange pass --expert expert options +--interface use specified user interface ]] messages.expert = [[ -expert options: also provide --expert +expert options: + +--touch update context version number (remake needed afterwards, also provide --expert) +--update update context from website (not to be confused with contextgarden) +--profile profile job (use: mtxrun --script profile --analyse) +--track show/set tracker variables +--timing generate timing and statistics overview +--extra=name process extra (mtx-context-<name> in distribution) +]] ---touch update context version (remake needed afterwards) +messages.private = [[ +private options: + +--dumphash dump hash table afterwards +--dumpdelta dump hash table afterwards (only new entries) ]] -input.verbose = true +messages.special = [[ +special options: + +--pdftex process file with texexec using pdftex +--xetex process file with texexec using xetex + +--pipe don't check for file and enter scroll mode (--dummyfile=whatever.tmp) +]] if environment.argument("once") then scripts.context.multipass.nofruns = 1 end +if environment.argument("profile") then + os.setenv("MTX_PROFILE_RUN","YES") +end + if environment.argument("run") then - scripts.context.timed(scripts.context.run) -elseif environment.argument("make") then - scripts.context.timed(scripts.context.make) +-- scripts.context.timed(scripts.context.run) + scripts.context.timed(scripts.context.autoctx) +elseif environment.argument("make") or environment.argument("generate") then + scripts.context.timed(function() + if environment.argument("generate") then + scripts.context.generate() + end + if environment.argument("make") then + scripts.context.make() + end + end) elseif environment.argument("ctx") then scripts.context.timed(scripts.context.ctx) +elseif environment.argument("mp") or environment.argument("metapost") then + scripts.context.timed(scripts.context.metapost) elseif environment.argument("version") then scripts.context.version() elseif environment.argument("touch") then scripts.context.touch() -elseif environment.argument("help") then - input.help(banner,messages.help) +elseif environment.argument("update") then + scripts.context.update() elseif environment.argument("expert") then - input.help(banner,messages.expert) + logs.help(table.join({ messages.expert, messages.private, messages.special },"\n")) +elseif environment.argument("extra") then + scripts.context.extra() +elseif environment.argument("help") then + logs.help(messages.help) +elseif environment.argument("track") and type(environment.argument("track")) == "boolean" then + scripts.context.track() elseif environment.files[1] then - scripts.context.timed(scripts.context.run) +-- scripts.context.timed(scripts.context.run) + scripts.context.timed(scripts.context.autoctx) +elseif environment.argument("pipe") then + scripts.context.timed(scripts.context.pipe) +elseif environment.argument("purge") then + -- only when no filename given, supports --pattern + scripts.context.purge() +elseif environment.argument("purgeall") then + -- only when no filename given, supports --pattern + scripts.context.purge(true) else - input.help(banner,messages.help) + logs.help(messages.help) end +if environment.argument("profile") then + os.setenv("MTX_PROFILE_RUN","NO") +end |