if not modules then modules = { } end modules ['file-job'] = { version = 1.001, comment = "companion to file-job.mkiv", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright = "PRAGMA ADE / ConTeXt Development Team", license = "see context related readme files" } -- in retrospect dealing it's not that bad to deal with the nesting -- and push/poppign at the tex end local next, rawget, tostring, tonumber = next, rawget, tostring, tonumber local gsub, match, gmatch, ind = string.gsub, string.match, string.gmatch, string.find local insert, remove, concat, unique, imerged = table.insert, table.remove, table.concat, table.unique, table.imerged local validstring, formatters = string.valid, string.formatters local sortedhash, copytable = table.sortedhash, table.copy local setmetatableindex, setmetatablenewindex = table.setmetatableindex, table.setmetatablenewindex local commands = commands local resolvers = resolvers local context = context local ctx_doifelse = commands.doifelse local implement = interfaces.implement local trace_jobfiles = false trackers.register("system.jobfiles", function(v) trace_jobfiles = v end) local trace_structure = false trackers.register("system.structure", function(v) trace_structure = v end) local report = logs.reporter("system") local report_jobfiles = logs.reporter("system", "jobfiles") local report_structure = logs.reporter("system", "structure") local report_functions = logs.reporter("system", "functions") local texsetcount = tex.setcount local elements = interfaces.elements local constants = interfaces.constants local variables = interfaces.variables local settings_to_array = utilities.parsers.settings_to_array local allocate = utilities.storage.allocate local nameonly = file.nameonly local suffixonly = file.suffix local basename = file.basename local addsuffix = file.addsuffix local removesuffix = file.removesuffix local dirname = file.dirname local is_qualified_path = file.is_qualified_path local cleanpath = resolvers.cleanpath local toppath = resolvers.toppath local resolveprefix = resolvers.resolve local currentfile = luatex.currentfile local hasscheme = url.hasscheme local jobresolvers = resolvers.jobs local registerextrapath = resolvers.registerextrapath local resetextrapaths = resolvers.resetextrapaths local getextrapaths = resolvers.getextrapaths local pushextrapath = resolvers.pushextrapath local popextrapath = resolvers.popextrapath ----- v_outer = variables.outer local v_text = variables.text local v_project = variables.project local v_environment = variables.environment local v_product = variables.product local v_component = variables.component local v_yes = variables.yes local v_local = variables["local"] local v_global = variables["global"] local e_start = elements.start local e_stop = elements.stop -- main code .. there is some overlap .. here we have loc:// local function findctxfile(name) -- loc ? any ? if is_qualified_path(name) then -- maybe when no suffix do some test for tex return name elseif not hasscheme(name) then return resolvers.finders.byscheme("loc",name) or "" else return resolvers.findtexfile(name) or "" end end resolvers.findctxfile = findctxfile implement { name = "processfile", arguments = "string", public = true, protected = true, actions = function(name) name = findctxfile(name) if name ~= "" then context.input(name) end end } implement { name = "doifelseinputfile", arguments = "string", public = true, protected = true, actions = function(name) ctx_doifelse(findctxfile(name) ~= "") end } implement { name = "locatefilepath", arguments = "string", actions = function(name) context(dirname(findctxfile(name))) end } implement { name = "usepath", arguments = "optional", public = true, protected = true, actions = function(paths) report_jobfiles("using path: %s",paths) registerextrapath(paths) end } implement { name = "pushpath", arguments = "optional", public = true, protected = true, actions = function(paths) report_jobfiles("pushing path: %s",paths) pushextrapath(paths) end } implement { name = "poppath", public = true, protected = true, actions = function(paths) popextrapath() report_jobfiles("popping path") end } implement { name = "usesubpath", arguments = "optional", public = true, protected = true, actions = function(subpaths) report_jobfiles("using subpath: %s",subpaths) registerextrapath(nil,subpaths) end } implement { name = "resetpath", public = true, protected = true, actions = function() report_jobfiles("resetting path") resetextrapaths() end } implement { name = "allinputpaths", public = true, actions = function() context(concat(getextrapaths(),",")) end } implement { name = "usezipfile", public = true, protected = true, arguments = { "optional", "optional" }, actions = function(name,tree) if tree and tree ~= "" then resolvers.usezipfile(formatters["zip:///%s?tree=%s"](name,tree)) else resolvers.usezipfile(formatters["zip:///%s"](name)) end end } -- moved from tex to lua: local texpatterns = { "%s.mklx", "%s.mkxl", "%s.mkvi", "%s.mkiv", "%s.tex" } local luapatterns = { "%s" .. utilities.lua.suffixes.luc, "%s.lmt", "%s.lua" } local cldpatterns = { "%s.cld" } local xmlpatterns = { "%s.xml" } local uselibrary = resolvers.uselibrary local input = context.input -- status -- -- these need to be synced with input stream: local processstack = { } local processedfile = "" local processedfiles = { } implement { name = "processedfile", actions = function() context(processedfile) end } implement { name = "processedfiles", actions = function() context(concat(processedfiles,",")) end } implement { name = "dostarttextfile", public = true, protected = true, arguments = "string", actions = function(name) insert(processstack,name) processedfile = name insert(processedfiles,name) end } implement { name = "dostoptextfile", public = true, protected = true, actions = function() processedfile = remove(processstack) or "" end } local function startprocessing(name,notext) if not notext then -- report("begin file %a at line %a",name,status.linenumber or 0) context.dostarttextfile(name) end end local function stopprocessing(notext) if not notext then context.dostoptextfile() -- report("end file %a at line %a",name,status.linenumber or 0) end end -- local typestack = { } local currenttype = v_text local nofmissing = 0 local missing = { tex = setmetatableindex("number"), lua = setmetatableindex("number"), cld = setmetatableindex("number"), xml = setmetatableindex("number"), } local function reportfailure(kind,name) nofmissing = nofmissing + 1 missing[kind][name] = true report_jobfiles("unknown %s file %a",kind,name) end -- local function action(name,foundname) input(foundname) end local function failure(name,foundname) reportfailure("tex",name) end local function usetexfile(name,onlyonce,notext) startprocessing(name,notext) uselibrary { name = name, patterns = texpatterns, action = action, failure = failure, onlyonce = onlyonce, } stopprocessing(notext) end local function action(name,foundname) dofile(foundname) end local function failure(name,foundname) reportfailure("lua",name) end local function useluafile(name,onlyonce,notext) uselibrary { name = name, patterns = luapatterns, action = action, failure = failure, onlyonce = onlyonce, } end local function action(name,foundname) dofile(foundname) end local function failure(name,foundname) reportfailure("cld",name) end local function usecldfile(name,onlyonce,notext) startprocessing(name,notext) uselibrary { name = name, patterns = cldpatterns, action = action, failure = failure, onlyonce = onlyonce, } stopprocessing(notext) end local function action(name,foundname) context.xmlprocess(foundname,"main","") end local function failure(name,foundname) reportfailure("xml",name) end local function usexmlfile(name,onlyonce,notext) startprocessing(name,notext) uselibrary { name = name, patterns = xmlpatterns, action = action, failure = failure, onlyonce = onlyonce, } stopprocessing(notext) end local suffixes = { mkvi = usetexfile, mkiv = usetexfile, mklx = usetexfile, mkxl = usetexfile, tex = usetexfile, luc = useluafile, lua = useluafile, cld = usecldfile, xml = usexmlfile, [""] = usetexfile, } local function useanyfile(name,onlyonce) local s = suffixes[suffixonly(name)] context(function() resolvers.pushpath(name) end) if s then -- s(removesuffix(name),onlyonce) s(name,onlyonce) -- so, first with suffix, then without else usetexfile(name,onlyonce) -- e.g. ctx file -- resolvers.readfilename(name) end context(resolvers.poppath) end implement { name = "loadtexfile", public = true, protected = true, actions = usetexfile, arguments = "optional" } implement { name = "loadluafile", public = true, protected = true, actions = useluafile, arguments = "optional" } implement { name = "loadcldfile", public = true, protected = true, actions = usecldfile, arguments = "optional" } implement { name = "loadxmlfile", public = true, protected = true, actions = usexmlfile, arguments = "optional" } implement { name = "loadtexfileonce", public = true, protected = true, actions = usetexfile, arguments = { "optional", true } } implement { name = "loadluafileonce", public = true, protected = true, actions = useluafile, arguments = { "optional", true } } implement { name = "loadcldfileonce", public = true, protected = true, actions = usecldfile, arguments = { "optional", true } } implement { name = "loadxmlfileonce", public = true, protected = true, actions = usexmlfile, arguments = { "optional", true } } implement { name = "useanyfile", actions = useanyfile, arguments = "string" } implement { name = "useanyfileonce", actions = useanyfile, arguments = { "string", true } } function jobresolvers.usefile(name,onlyonce,notext) local s = suffixes[suffixonly(name)] if s then -- s(removesuffix(name),onlyonce,notext) s(name,onlyonce,notext) -- so, first with suffix, then without end end -- document structure local textlevel = 0 -- inaccessible for user, we need to define counter textlevel at the tex end local c_textlevel = tex.iscount("textlevel") local function dummyfunction() end local function startstoperror() report("invalid \\%s%s ... \\%s%s structure",e_start,v_text,e_stop,v_text) startstoperror = dummyfunction end local stopped local function starttext() if textlevel == 0 then if trace_jobfiles then report_jobfiles("starting text") end context.dostarttext() end textlevel = textlevel + 1 texsetcount("global",c_textlevel,textlevel) end local function stoptext() if not stopped then if textlevel == 0 then startstoperror() elseif textlevel > 0 then textlevel = textlevel - 1 end texsetcount("global",c_textlevel,textlevel) if textlevel <= 0 then if trace_jobfiles then report_jobfiles("stopping text") end context.dostoptext() stopped = true end end end implement { name = "starttext", public = true, protected = true, actions = starttext } implement { name = "stoptext", public = true, protected = true, actions = stoptext } implement { name = "forcequitjob", arguments = "string", public = true, protected = true, actions = function(reason) if reason then report("forcing quit: %s",reason) else report("forcing quit") end context.batchmode() while textlevel >= 0 do context.stoptext() end end } implement { name = "forceendjob", public = true, protected = true, actions = function() report([[don't use \end to finish a document]]) context.stoptext() end } implement { name = "autostarttext", public = true, protected = true, actions = function() if textlevel == 0 then report([[auto \starttext ... \stoptext]]) end context.starttext() end } implement { name = "autostoptext", public = true, protected = true, actions = stoptext } -- project structure implement { name = "processfilemany", public = true, protected = true, arguments = { "string", false }, actions = useanyfile } implement { name = "processfileonce", public = true, protected = true, arguments = { "string", true }, actions = useanyfile } implement { name = "processfilenone", arguments = "string", public = true, protected = true, actions = dummyfunction, } local tree = { type = "text", name = "", branches = { } } local treestack = { } local top = tree.branches local root = tree local namestack = { } local namelist = { } local namehash = { } local nametop = false local project_stack = { } local product_stack = { } local component_stack = { } local environment_stack = { } local stacks = { [v_project ] = project_stack, [v_product ] = product_stack, [v_component ] = component_stack, [v_environment] = environment_stack, } -- local function pushtree(what,name) local t = { } top[#top+1] = { type = what, name = name, branches = t } insert(treestack,top) top = t end local function pushcurrent(what,name) if what == v_product or what == v_component or what == v_text then insert(namestack,name) nametop = copytable(namestack) namelist[#namelist+1] = nametop insert(namehash,#namelist) end end local function poptree() top = remove(treestack) if #top[#top].branches == 0 then top[#top].branches = nil -- saves space in tuc end end local function popcurrent(what) if what == v_product or what == v_component then remove(namestack) remove(namehash) end end do local function log_tree(report,top,depth) report("%s%s: %s",depth,top.type,top.name) local branches = top.branches if branches then local n = #branches if n > 0 then depth = depth .. " " for i=1,n do log_tree(report,branches[i],depth) end else top.brances = nil -- saves space in tuc end end end logs.registerfinalactions(function() root.name = environment.jobname -- logs.startfilelogging(report,"used files") log_tree(report,root,"") logs.stopfilelogging() -- if nofmissing > 0 and logs.loggingerrors() then logs.starterrorlogging(report,"missing files") for kind, list in sortedhash(missing) do for name in sortedhash(list) do report("%w%s %s",6,kind,name) end end logs.stoperrorlogging() end end) end local jobstructure = job.structure or { } job.structure = jobstructure jobstructure.collected = jobstructure.collected or { } jobstructure.tobesaved = root jobstructure.components = { } jobstructure.namestack = { } function collectstructure(collected) local namestack = { } local n = 0 local function collect(root,result,stack) local branches = root.branches if branches then local s = copytable(stack) local m = #s + 1 for i=1,#branches do local branch = branches[i] local what = branch.type if what == v_component then result[#result+1] = branch.name end s[m] = branch.name if what == v_product or what == v_component then n = n + 1 namestack[n] = copytable(s) end collect(branch,result,s) end end return result end local components = collect(collected,{},{}) -- inspect(namestack) return components, namestack end local function initialize() jobstructure.components, jobstructure.namestack = collectstructure(jobstructure.collected) end function jobresolvers.collectstructure(collected) return collectstructure(collected or jobstructure.collected) end do local reported = { } function jobresolvers.currentstructure() -- inspect(namehash) local n = namehash[#namehash] if trace_structure and not reported[n] then local namestack = jobstructure.namestack local new = namelist [n] local old = namestack[n] new = new and concat(new," ") or "" old = old and concat(old," ") or "" if old == new then report_structure("%02i : %a",n,old) else report_structure("%02i : %a <> %a",n,old,new) end reported[n] = true end return n, namelist[n] end end function jobresolvers.namelist(n,utilitydata) if utilitydata then return utilitydata.namestack[n] else return namelist[n] or jobstructure.namestack[n] end end job.register('job.structure.collected',root,initialize) -- component: small unit, either or not components itself -- product : combination of components local ctx_processfilemany = context.processfilemany local ctx_processfileonce = context.processfileonce local ctx_processfilenone = context.processfilenone -- we need a plug in the nested loaded, push pop pseudo current dir local function processfilecommon(name,action) -- experiment, might go away -- if not hasscheme(name) then -- local path = dirname(name) -- if path ~= "" then -- registerextrapath(path) -- report_jobfiles("adding search path %a",path) -- end -- end -- till here action(name) end local function processfilemany(name) processfilecommon(name,ctx_processfilemany) end local function processfileonce(name) processfilecommon(name,ctx_processfileonce) end local function processfilenone(name) processfilecommon(name,ctx_processfilenone) end local processors = utilities.storage.allocate { -- [v_outer] = { -- [v_text] = { "many", processfilemany }, -- [v_project] = { "once", processfileonce }, -- [v_environment] = { "once", processfileonce }, -- [v_product] = { "once", processfileonce }, -- [v_component] = { "many", processfilemany }, -- }, [v_text] = { [v_text] = { "many", processfilemany }, [v_project] = { "once", processfileonce }, -- dubious [v_environment] = { "once", processfileonce }, [v_product] = { "many", processfilemany }, -- dubious [v_component] = { "many", processfilemany }, }, [v_project] = { [v_text] = { "many", processfilemany }, [v_project] = { "none", processfilenone }, [v_environment] = { "once", processfileonce }, [v_product] = { "none", processfilenone }, [v_component] = { "none", processfilenone }, }, [v_environment] = { [v_text] = { "many", processfilemany }, [v_project] = { "none", processfilenone }, [v_environment] = { "once", processfileonce }, [v_product] = { "none", processfilenone }, [v_component] = { "none", processfilenone }, }, [v_product] = { [v_text] = { "many", processfilemany }, [v_project] = { "once", processfileonce }, [v_environment] = { "once", processfileonce }, [v_product] = { "many", processfilemany }, [v_component] = { "many", processfilemany }, }, [v_component] = { [v_text] = { "many", processfilemany }, [v_project] = { "once", processfileonce }, [v_environment] = { "once", processfileonce }, [v_product] = { "none", processfilenone }, [v_component] = { "many", processfilemany }, } } local start = { [v_text] = nil, [v_project] = nil, [v_environment] = context.startreadingfile, [v_product] = context.starttext, [v_component] = context.starttext, } local stop = { [v_text] = nil, [v_project] = nil, [v_environment] = context.stopreadingfile, [v_product] = context.stoptext, [v_component] = context.stoptext, } jobresolvers.processors = processors local function topofstack(what) local stack = stacks[what] return stack and stack[#stack] or environment.jobname end local function productcomponent() -- only when in product local product = product_stack[#product_stack] if product and product ~= "" then local component = component_stack[1] if component and component ~= "" then return component end end end local function justacomponent() local product = product_stack[#product_stack] if not product or product == "" then local component = component_stack[1] if component and component ~= "" then return component end end end jobresolvers.productcomponent = productcomponent jobresolvers.justacomponent = justacomponent function jobresolvers.currentproject () return topofstack(v_project ) end function jobresolvers.currentproduct () return topofstack(v_product ) end function jobresolvers.currentcomponent () return topofstack(v_component ) end function jobresolvers.currentenvironment() return topofstack(v_environment) end local done = { } local tolerant = false -- too messy, mkii user with the wrong structure should adapt local function process(what,name) local depth = #typestack local process -- name = resolveprefix(name) -- -- if not tolerant then -- okay, would be best but not compatible with mkii process = processors[currenttype][what] -- elseif depth == 0 then -- -- could be a component, product or (brr) project -- if trace_jobfiles then -- report_jobfiles("%s : %s > %s (case 1)",depth,currenttype,v_outer) -- end -- process = processors[v_outer][what] -- elseif depth == 1 and typestack[1] == v_text then -- -- we're still not doing a component or product -- if trace_jobfiles then -- report_jobfiles("%s : %s > %s (case 2)",depth,currenttype,v_outer) -- end -- process = processors[v_outer][what] -- else -- process = processors[currenttype][what] -- end if process then local method = process[1] if method == "none" then if trace_jobfiles then report_jobfiles("%s : %s : %s %s %a in %s %a",depth,method,"ignoring",what,name,currenttype,topofstack(currenttype)) end elseif method == "once" and done[name] then if trace_jobfiles then report_jobfiles("%s : %s : %s %s %a in %s %a",depth,method,"skipping",what,name,currenttype,topofstack(currenttype)) end else -- keep in mind that we also handle "once" at the file level -- so there is a double catch done[name] = true local before = start[what] local after = stop [what] if trace_jobfiles then report_jobfiles("%s : %s : %s %s %a in %s %a",depth,method,"processing",what,name,currenttype,topofstack(currenttype)) end if before then before() end process[2](name) if after then after() end end else if trace_jobfiles then report_jobfiles("%s : %s : %s %s %a in %s %a",depth,"none","ignoring",what,name,currenttype,topofstack(currenttype)) end end end local scan_delimited = tokens.scanners.delimited local function getname() return scan_delimited(91,93) or scan_delimited(0,32) -- [name] or name - use optional end implement { name = "project", public = true, protected = true, actions = function() process(v_project, getname()) end } implement { name = "environment", public = true, protected = true, actions = function() process(v_environment,getname()) end } implement { name = "product", public = true, protected = true, actions = function() process(v_product, getname()) end } -- will be overloaded implement { name = "component", public = true, protected = true, actions = function() process(v_component, getname()) end } implement { name = "useproject", public = true, protected = true, actions = function(name) process(v_project, name) end, arguments = "optional" } implement { name = "useenvironment", public = true, protected = true, actions = function(name) process(v_environment,name) end, arguments = "optional" } implement { name = "useproduct", public = true, protected = true, actions = function(name) process(v_product, name) end, arguments = "optional" } -- will be overloaded implement { name = "usecomponent", public = true, protected = true, actions = function(name) process(v_component, name) end, arguments = "optional" } implement { name = "ignorefile", public = true, protected = true, arguments = "array", actions = function(s) for i=1,#s do resolvers.ignorelibrary(s[i]) end end } -- todo: setsystemmode to currenttype -- todo: make start/stop commands at the tex end -- local start = { -- [v_project] = context.startprojectindeed, -- [v_product] = context.startproductindeed, -- [v_component] = context.startcomponentindeed, -- [v_environment] = context.startenvironmentindeed, -- } -- local stop = { -- [v_project] = context.stopprojectindeed, -- [v_product] = context.stopproductindeed, -- [v_component] = context.stopcomponentindeed, -- [v_environment] = context.stopenvironmentindeed, -- } -- local start = { -- [v_project] = "startprojectindeed", -- [v_product] = "startproductindeed", -- [v_component] = "startcomponentindeed", -- [v_environment] = "startenvironmentindeed", -- } -- local stop = { -- [v_project] = "stopprojectindeed", -- [v_product] = "stopproductindeed", -- [v_component] = "stopcomponentindeed", -- [v_environment] = "stopenvironmentindeed", -- } local function gotonextlevel(what,how,name) -- todo: something with suffix name insert(stacks[what],name) insert(typestack,currenttype) currenttype = what pushtree(what,name) pushcurrent(what,name) token.expandmacro(e_start..":"..what..":"..how) end local function gotopreviouslevel(what,how) token.expandmacro(e_stop..":"..what..":"..how) popcurrent(what) poptree() currenttype = remove(typestack) or v_text remove(stacks[what]) -- not currenttype ... weak recovery -- context.endinput() -- now at the tex end ! end local function autoname() local name = scan_delimited(91,93) or scan_delimited(0,32) -- [name] or name if name == "*" then -- name = nameonly(toppath() or name) name = nameonly(currentfile() or name) end return name end implement { name = "startproject", public = true, protected = true, actions = function() gotonextlevel(v_project, v_global, autoname()) end } implement { name = "startproduct", public = true, protected = true, actions = function() gotonextlevel(v_product, v_global, autoname()) end } implement { name = "startcomponent", public = true, protected = true, actions = function() gotonextlevel(v_component, v_global, autoname()) end } implement { name = "startlocalcomponent", public = true, protected = true, actions = function() gotonextlevel(v_component, v_local, autoname()) end } implement { name = "startenvironment", public = true, protected = true, actions = function() gotonextlevel(v_environment, v_global, autoname()) end } implement { name = "stopproject", public = true, protected = true, actions = function() gotopreviouslevel(v_project, v_global) end } implement { name = "stopproduct", public = true, protected = true, actions = function() gotopreviouslevel(v_product, v_global) end } implement { name = "stopcomponent", public = true, protected = true, actions = function() gotopreviouslevel(v_component, v_global) end } implement { name = "stoplocalcomponent", public = true, protected = true, actions = function() gotopreviouslevel(v_component, v_local ) end } implement { name = "stopenvironment", public = true, protected = true, actions = function() gotopreviouslevel(v_environment, v_global) end } implement { name = "currentproject", public = true, actions = function() context(topofstack(v_project )) end } implement { name = "currentproduct", public = true, actions = function() context(topofstack(v_product )) end } implement { name = "currentcomponent", public = true, actions = function() context(topofstack(v_component )) end } implement { name = "currentenvironment", public = true, actions = function() context(topofstack(v_environment)) end } -- -- -- this will move -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- nee -- standaard -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- local report_examodes = logs.reporter("system","examodes") local function convertexamodes(str) local x = xml.convert(str) for e in xml.collected(x,"exa:variable") do local label = e.at and e.at.label if label and label ~= "" then local data = xml.text(e) local mode = match(label,"^mode:(.+)$") if mode then context.enablemode { formatters["%s:%s"](mode,data) } end context.setvariable("exa:variables",label,(gsub(data,"([{}])","\\%1"))) end end end function environment.loadexamodes(filename) if not filename or filename == "" then -- todo: environment.fulljobnmame filename = removesuffix(tex.jobname) end filename = resolvers.findfile(addsuffix(filename,'ctm')) or "" if filename ~= "" then report_examodes("loading %a",filename) -- todo: message system convertexamodes(io.loaddata(filename)) else report_examodes("no mode file %a",filename) -- todo: message system end end implement { name = "loadexamodes", actions = environment.loadexamodes, public = true, protected = true, arguments = "optional" } -- changed in mtx-context -- code moved from luat-ini -- todo: locals when mtx-context is changed document = document or { arguments = allocate(), files = allocate(), variables = allocate(), -- for templates options = { commandline = { environments = allocate(), modules = allocate(), modes = allocate(), }, ctxfile = { environments = allocate(), modules = allocate(), modes = allocate(), }, }, functions = table.setmetatablenewindex(function(t,k,v) if rawget(t,k) then report_functions("overloading document function %a",k) end rawset(t,k,v) return v end), } function document.setargument(key,value) document.arguments[key] = value end function document.setdefaultargument(key,default) local v = document.arguments[key] if v == nil or v == "" then document.arguments[key] = default end end function document.setfilename(i,name) if name then document.files[tonumber(i)] = name else document.files[#document.files+1] = tostring(i) end end function document.getargument(key,default) local v = document.arguments[key] if type(v) == "boolean" then v = (v and "yes") or "no" document.arguments[key] = v end return v or default or "" end function document.getfilename(i) return document.files[tonumber(i)] or "" end implement { name = "setdocumentargument", actions = document.setargument, arguments = "2 strings" } implement { name = "setdocumentdefaultargument", actions = document.setdefaultargument, arguments = "2 strings" } implement { name = "setdocumentfilename", actions = document.setfilename, arguments = { "integer", "string" } } implement { name = "getdocumentargument", actions = { document.getargument, context }, arguments = "2 strings" } implement { name = "getdocumentfilename", actions = { document.getfilename, context }, arguments = "integer" } function document.setcommandline() -- has to happen at the tex end in order to expand -- the document[arguments|files] tables are copies local arguments = document.arguments local files = document.files local options = document.options for k, v in next, environment.arguments do k = gsub(k,"^c:","") -- already done, but better be safe than sorry if arguments[k] == nil then arguments[k] = v end end -- in the new mtx=context approach we always pass a stub file so we need to -- to trick the files table which actually only has one entry in a tex job if arguments.timing then context.usemodule { "timing" } end if arguments.usage then trackers.enable("system.usage") end if arguments.batchmode then context.batchmode(false) end if arguments.nonstopmode then context.nonstopmode(false) end if arguments.nostatistics then directives.enable("system.nostatistics") end if arguments.notuc then directives.disable("job.save") end if arguments.paranoid then context.setvalue("maxreadlevel",1) end if validstring(arguments.path) then context.usepath { arguments.path } end if arguments.export then context.setupbackend { export = v_yes } end local inputfile = validstring(arguments.input) if inputfile and dirname(inputfile) == "." and lfs.isfile(inputfile) then -- nicer in checks inputfile = basename(inputfile) end local forcedruns = arguments.forcedruns local kindofrun = arguments.kindofrun local currentrun = arguments.currentrun local maxnofruns = arguments.maxnofruns or arguments.runs -- context.setupsystem { -- [constants.directory] = validstring(arguments.setuppath), -- [constants.inputfile] = inputfile, -- [constants.file] = validstring(arguments.result), -- [constants.random] = validstring(arguments.randomseed), -- -- old: -- [constants.n] = validstring(kindofrun), -- [constants.m] = validstring(currentrun), -- } context.setupsystem { directory = validstring(arguments.setuppath), inputfile = inputfile, file = validstring(arguments.result), random = validstring(arguments.randomseed), -- old: n = validstring(kindofrun), m = validstring(currentrun), } forcedruns = tonumber(forcedruns) or 0 kindofrun = tonumber(kindofrun) or 0 maxnofruns = tonumber(maxnofruns) or 0 currentrun = tonumber(currentrun) or 0 local prerollrun = forcedruns > 0 and currentrun > 0 and currentrun < forcedruns environment.forcedruns = forcedruns environment.kindofrun = kindofrun environment.maxnofruns = maxnofruns environment.currentrun = currentrun environment.prerollrun = prerollrun context.setconditional("prerollrun",prerollrun) if validstring(arguments.arguments) then context.setupenv { arguments.arguments } end if arguments.once then directives.enable("system.runonce") end if arguments.noarrange then context.setuparranging { variables.disable } end -- local commandline = options.commandline commandline.environments = table.append(commandline.environments,settings_to_array(validstring(arguments.environment))) commandline.modules = table.append(commandline.modules, settings_to_array(validstring(arguments.usemodule))) commandline.modes = table.append(commandline.modes, settings_to_array(validstring(arguments.mode))) -- if #files == 0 then local list = settings_to_array(validstring(arguments.files)) if list and #list > 0 then files = list end end if #files == 0 then files = { validstring(arguments.input) } end -- document.arguments = arguments document.files = files end -- commandline wins over ctxfile local function apply(list,action) if list then for i=1,#list do action { list[i] } end end end function document.setmodes() -- was setup: *runtime:modes apply(document.options.ctxfile .modes,context.enablemode) apply(document.options.commandline.modes,context.enablemode) end function document.setmodules() -- was setup: *runtime:modules apply(document.options.ctxfile .modules,context.usemodule) apply(document.options.commandline.modules,context.usemodule) end function document.setenvironments() -- was setup: *runtime:environments apply(document.options.ctxfile .environments,context.environment) apply(document.options.commandline.environments,context.environment) end function document.setfilenames() local initialize = environment.initializefilenames if initialize then initialize() else -- fatal error end end implement { name = "setdocumentcommandline", actions = document.setcommandline, onlyonce = true } implement { name = "setdocumentmodes", actions = document.setmodes, onlyonce = true } implement { name = "setdocumentmodules", actions = document.setmodules, onlyonce = true } implement { name = "setdocumentenvironments", actions = document.setenvironments, onlyonce = true } implement { name = "setdocumentfilenames", actions = document.setfilenames, onlyonce = true } do logs.registerfinalactions(function() local foundintrees = resolvers.foundintrees() if #foundintrees > 0 then logs.startfilelogging(report,"used files") for i=1,#foundintrees do report("%4i: % T",i,foundintrees[i]) end logs.stopfilelogging() end end) logs.registerfinalactions(function() local files = document.files -- or environment.files local arguments = document.arguments -- or environment.arguments -- logs.startfilelogging(report,"commandline options") if arguments and next(arguments) then for argument, value in sortedhash(arguments) do report(" %s=%A",argument,value) end else report("no arguments") end logs.stopfilelogging() -- logs.startfilelogging(report,"commandline files") if files and #files > 0 then for i=1,#files do report("% 4i: %s",i,files[i]) end else report("no files") end logs.stopfilelogging() end) end if environment.initex then logs.registerfinalactions(function() local startfilelogging = logs.startfilelogging local stopfilelogging = logs.stopfilelogging startfilelogging(report,"stored tables") for k,v in sortedhash(storage.data) do report("%03i %s",k,v[1]) end stopfilelogging() startfilelogging(report,"stored modules") for k,v in sortedhash(lua.bytedata) do report("%03i %s %s",k,v.name) end stopfilelogging() startfilelogging(report,"stored attributes") for k,v in sortedhash(attributes.names) do report("%03i %s",k,v) end stopfilelogging() startfilelogging(report,"stored catcodetables") for k,v in sortedhash(catcodes.names) do report("%03i % t",k,v) end stopfilelogging() startfilelogging(report,"stored corenamespaces") for k,v in sortedhash(interfaces.corenamespaces) do report("%03i %s",k,v) end stopfilelogging() end) end implement { name = "continueifinputfile", public = true, protected = true, arguments = "string", actions = function(inpname,basetoo) local inpnamefull = addsuffix(inpname,"tex") local inpfilefull = addsuffix(environment.inputfilename,"tex") local continue = inpnamefull == inpfilefull -- if basetoo and not continue then if not continue then continue = inpnamefull == basename(inpfilefull) end if continue then report("continuing input file %a",inpname) else context.endinput() end -- ctx_doifelse(continue) end } -- data-hsh.lmt: local helpers = resolvers.finders.helpers local validhashed = helpers.validhashed local registerhashed = helpers.registerhashed local registerfilescheme = helpers.registerfilescheme implement { name = "registerhashedfiles", public = true, protected = true, arguments = "optional", actions = function(list) for name in gmatch(list,"[^, ]+") do registerhashed(name) end end, } implement { name = "registerfilescheme", public = true, protected = true, arguments = "optional", actions = function(list) for name in gmatch(list,"[^, ]+") do registerfilescheme(name) end end, } implement { name = "doifelsevalidhashedfiles", public = true, protected = true, arguments = "string", actions = function(name) ctx_doifelse(validhashed(name)) end, } implement { name = "adddefaultsuffix", public = true, protected = true, actions = function(list) resolvers.defaultsuffixes = unique( imerged( resolvers.defaultsuffixes, list ) ) end, arguments = "array", } -- don't we have this already? implement { name = "filebasename", public = true, arguments = "argument", actions = { basename, context }, } implement { name = "filenameonly", public = true, arguments = "argument", actions = { nameonly, context }, } implement { name = "filedirname", public = true, arguments = "argument", actions = { dirname, context }, } implement { name = "filesuffix", public = true, arguments = "argument", actions = { dirname, context }, }