1if not modules then modules = { } end modules ['file-job'] = {
2 version = 1.001,
3 comment = "companion to file-job.mkiv",
4 author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5 copyright = "PRAGMA ADE / ConTeXt Development Team",
6 license = "see context related readme files"
7}
8
9
10
11
12local next, rawget, tostring, tonumber = next, rawget, tostring, tonumber
13local gsub, match, gmatch, ind = string.gsub, string.match, string.gmatch, string.find
14local insert, remove, concat, unique, imerged = table.insert, table.remove, table.concat, table.unique, table.imerged
15local validstring, formatters = string.valid, string.formatters
16local sortedhash, copytable = table.sortedhash, table.copy
17local setmetatableindex, setmetatablenewindex = table.setmetatableindex, table.setmetatablenewindex
18
19local commands = commands
20local resolvers = resolvers
21local context = context
22
23local ctx_doifelse = commands.doifelse
24
25local implement = interfaces.implement
26
27local trace_jobfiles = false trackers.register("system.jobfiles", function(v) trace_jobfiles = v end)
28local trace_structure = false trackers.register("system.structure", function(v) trace_structure = v end)
29
30local report = logs.reporter("system")
31local report_jobfiles = logs.reporter("system", "jobfiles")
32local report_structure = logs.reporter("system", "structure")
33local report_functions = logs.reporter("system", "functions")
34
35local texsetcount = tex.setcount
36local elements = interfaces.elements
37local constants = interfaces.constants
38local variables = interfaces.variables
39local settings_to_array = utilities.parsers.settings_to_array
40local allocate = utilities.storage.allocate
41
42local nameonly = file.nameonly
43local suffixonly = file.suffix
44local basename = file.basename
45local addsuffix = file.addsuffix
46local removesuffix = file.removesuffix
47local dirname = file.dirname
48local is_qualified_path = file.is_qualified_path
49
50local cleanpath = resolvers.cleanpath
51local toppath = resolvers.toppath
52local resolveprefix = resolvers.resolve
53
54local currentfile = luatex.currentfile
55
56local hasscheme = url.hasscheme
57
58local jobresolvers = resolvers.jobs
59
60local registerextrapath = resolvers.registerextrapath
61local resetextrapaths = resolvers.resetextrapaths
62local getextrapaths = resolvers.getextrapaths
63local pushextrapath = resolvers.pushextrapath
64local popextrapath = resolvers.popextrapath
65
66
67local v_text = variables.text
68local v_project = variables.project
69local v_environment = variables.environment
70local v_product = variables.product
71local v_component = variables.component
72local v_yes = variables.yes
73
74local v_local = variables["local"]
75local v_global = variables["global"]
76
77local e_start = elements.start
78local e_stop = elements.stop
79
80
81
82
83local function findctxfile(name)
84 if is_qualified_path(name) then
85 return name
86 elseif not hasscheme(name) then
87 return resolvers.finders.byscheme("loc",name) or ""
88 else
89 return resolvers.findtexfile(name) or ""
90 end
91end
92
93resolvers.findctxfile = findctxfile
94
95implement {
96 name = "processfile",
97 arguments = "string",
98 public = true,
99 protected = true,
100 actions = function(name)
101 name = findctxfile(name)
102 if name ~= "" then
103 context.input(name)
104 end
105 end
106}
107
108implement {
109 name = "doifelseinputfile",
110 arguments = "string",
111 public = true,
112 protected = true,
113 actions = function(name)
114 ctx_doifelse(findctxfile(name) ~= "")
115 end
116}
117
118implement {
119 name = "locatefilepath",
120 arguments = "string",
121 actions = function(name)
122 context(dirname(findctxfile(name)))
123 end
124}
125
126implement {
127 name = "usepath",
128 arguments = "optional",
129 public = true,
130 protected = true,
131 actions = function(paths)
132 report_jobfiles("using path: %s",paths)
133 registerextrapath(paths)
134 end
135}
136
137implement {
138 name = "pushpath",
139 arguments = "optional",
140 public = true,
141 protected = true,
142 actions = function(paths)
143 report_jobfiles("pushing path: %s",paths)
144 pushextrapath(paths)
145 end
146}
147
148implement {
149 name = "poppath",
150 public = true,
151 protected = true,
152 actions = function(paths)
153 popextrapath()
154 report_jobfiles("popping path")
155 end
156}
157
158implement {
159 name = "usesubpath",
160 arguments = "optional",
161 public = true,
162 protected = true,
163 actions = function(subpaths)
164 report_jobfiles("using subpath: %s",subpaths)
165 registerextrapath(nil,subpaths)
166 end
167}
168
169implement {
170 name = "resetpath",
171 public = true,
172 protected = true,
173 actions = function()
174 report_jobfiles("resetting path")
175 resetextrapaths()
176 end
177}
178
179implement {
180 name = "allinputpaths",
181 public = true,
182 actions = function()
183 context(concat(getextrapaths(),","))
184 end
185}
186
187implement {
188 name = "usezipfile",
189 public = true,
190 protected = true,
191 arguments = { "optional", "optional" },
192 actions = function(name,tree)
193 if tree and tree ~= "" then
194 resolvers.usezipfile(formatters["zip:///%s?tree=%s"](name,tree))
195 else
196 resolvers.usezipfile(formatters["zip:///%s"](name))
197 end
198 end
199}
200
201
202
203local texpatterns = { "%s.mklx", "%s.mkxl", "%s.mkvi", "%s.mkiv", "%s.tex" }
204local luapatterns = { "%s" .. utilities.lua.suffixes.luc, "%s.lmt", "%s.lua" }
205local cldpatterns = { "%s.cld" }
206local xmlpatterns = { "%s.xml" }
207
208local uselibrary = resolvers.uselibrary
209local input = context.input
210
211
212
213
214
215local processstack = { }
216local processedfile = ""
217local processedfiles = { }
218
219implement {
220 name = "processedfile",
221 actions = function()
222 context(processedfile)
223 end
224}
225
226implement {
227 name = "processedfiles",
228 actions = function()
229 context(concat(processedfiles,","))
230 end
231}
232
233implement {
234 name = "dostarttextfile",
235 public = true,
236 protected = true,
237 arguments = "string",
238 actions = function(name)
239 insert(processstack,name)
240 processedfile = name
241 insert(processedfiles,name)
242 end
243}
244
245implement {
246 name = "dostoptextfile",
247 public = true,
248 protected = true,
249 actions = function()
250 processedfile = remove(processstack) or ""
251 end
252}
253
254local function startprocessing(name,notext)
255 if not notext then
256
257 context.dostarttextfile(name)
258 end
259end
260
261local function stopprocessing(notext)
262 if not notext then
263 context.dostoptextfile()
264
265 end
266end
267
268
269
270local typestack = { }
271local currenttype = v_text
272local nofmissing = 0
273local missing = {
274 tex = setmetatableindex("number"),
275 lua = setmetatableindex("number"),
276 cld = setmetatableindex("number"),
277 xml = setmetatableindex("number"),
278}
279
280local function reportfailure(kind,name)
281 nofmissing = nofmissing + 1
282 missing[kind][name] = true
283 report_jobfiles("unknown %s file %a",kind,name)
284end
285
286
287
288local function action(name,foundname)
289 input(foundname)
290end
291local function failure(name,foundname)
292 reportfailure("tex",name)
293end
294local function usetexfile(name,onlyonce,notext)
295 startprocessing(name,notext)
296 uselibrary {
297 name = name,
298 patterns = texpatterns,
299 action = action,
300 failure = failure,
301 onlyonce = onlyonce,
302 }
303 stopprocessing(notext)
304end
305
306local function action(name,foundname)
307 dofile(foundname)
308end
309local function failure(name,foundname)
310 reportfailure("lua",name)
311end
312local function useluafile(name,onlyonce,notext)
313 uselibrary {
314 name = name,
315 patterns = luapatterns,
316 action = action,
317 failure = failure,
318 onlyonce = onlyonce,
319 }
320end
321
322local function action(name,foundname)
323 dofile(foundname)
324end
325local function failure(name,foundname)
326 reportfailure("cld",name)
327end
328local function usecldfile(name,onlyonce,notext)
329 startprocessing(name,notext)
330 uselibrary {
331 name = name,
332 patterns = cldpatterns,
333 action = action,
334 failure = failure,
335 onlyonce = onlyonce,
336 }
337 stopprocessing(notext)
338end
339
340local function action(name,foundname)
341 context.xmlprocess(foundname,"main","")
342end
343local function failure(name,foundname)
344 reportfailure("xml",name)
345end
346local function usexmlfile(name,onlyonce,notext)
347 startprocessing(name,notext)
348 uselibrary {
349 name = name,
350 patterns = xmlpatterns,
351 action = action,
352 failure = failure,
353 onlyonce = onlyonce,
354 }
355 stopprocessing(notext)
356end
357
358local suffixes = {
359 mkvi = usetexfile,
360 mkiv = usetexfile,
361 mklx = usetexfile,
362 mkxl = usetexfile,
363 tex = usetexfile,
364 luc = useluafile,
365 lua = useluafile,
366 cld = usecldfile,
367 xml = usexmlfile,
368 [""] = usetexfile,
369}
370
371local function useanyfile(name,onlyonce)
372 local s = suffixes[suffixonly(name)]
373 context(function() resolvers.pushpath(name) end)
374 if s then
375
376 s(name,onlyonce)
377 else
378 usetexfile(name,onlyonce)
379
380 end
381 context(resolvers.poppath)
382end
383
384implement { name = "loadtexfile", public = true, protected = true, actions = usetexfile, arguments = "optional" }
385implement { name = "loadluafile", public = true, protected = true, actions = useluafile, arguments = "optional" }
386implement { name = "loadcldfile", public = true, protected = true, actions = usecldfile, arguments = "optional" }
387implement { name = "loadxmlfile", public = true, protected = true, actions = usexmlfile, arguments = "optional" }
388
389implement { name = "loadtexfileonce", public = true, protected = true, actions = usetexfile, arguments = { "optional", true } }
390implement { name = "loadluafileonce", public = true, protected = true, actions = useluafile, arguments = { "optional", true } }
391implement { name = "loadcldfileonce", public = true, protected = true, actions = usecldfile, arguments = { "optional", true } }
392implement { name = "loadxmlfileonce", public = true, protected = true, actions = usexmlfile, arguments = { "optional", true } }
393
394implement { name = "useanyfile", actions = useanyfile, arguments = "string" }
395implement { name = "useanyfileonce", actions = useanyfile, arguments = { "string", true } }
396
397function jobresolvers.usefile(name,onlyonce,notext)
398 local s = suffixes[suffixonly(name)]
399 if s then
400
401 s(name,onlyonce,notext)
402 end
403end
404
405
406
407local textlevel = 0
408
409local c_textlevel = tex.iscount("textlevel")
410
411local function dummyfunction() end
412
413local function startstoperror()
414 report("invalid \\%s%s ... \\%s%s structure",e_start,v_text,e_stop,v_text)
415 startstoperror = dummyfunction
416end
417
418local stopped
419
420local function starttext()
421 if textlevel == 0 then
422 if trace_jobfiles then
423 report_jobfiles("starting text")
424 end
425 context.dostarttext()
426 end
427 textlevel = textlevel + 1
428 texsetcount("global",c_textlevel,textlevel)
429end
430
431local function stoptext()
432 if not stopped then
433 if textlevel == 0 then
434 startstoperror()
435 elseif textlevel > 0 then
436 textlevel = textlevel - 1
437 end
438 texsetcount("global",c_textlevel,textlevel)
439 if textlevel <= 0 then
440 if trace_jobfiles then
441 report_jobfiles("stopping text")
442 end
443 context.dostoptext()
444 stopped = true
445 end
446 end
447end
448
449implement {
450 name = "starttext",
451 public = true,
452 protected = true,
453 actions = starttext
454}
455
456implement {
457 name = "stoptext",
458 public = true,
459 protected = true,
460 actions = stoptext
461}
462
463implement {
464 name = "forcequitjob",
465 arguments = "string",
466 public = true,
467 protected = true,
468 actions = function(reason)
469 if reason then
470 report("forcing quit: %s",reason)
471 else
472 report("forcing quit")
473 end
474 context.batchmode()
475 while textlevel >= 0 do
476 context.stoptext()
477 end
478 end
479}
480
481implement {
482 name = "forceendjob",
483 public = true,
484 protected = true,
485 actions = function()
486 report([[don't use \end to finish a document]])
487 context.stoptext()
488 end
489}
490
491implement {
492 name = "autostarttext",
493 public = true,
494 protected = true,
495 actions = function()
496 if textlevel == 0 then
497 report([[auto \starttext ... \stoptext]])
498 end
499 context.starttext()
500 end
501}
502
503implement {
504 name = "autostoptext",
505 public = true,
506 protected = true,
507 actions = stoptext
508}
509
510
511
512implement {
513 name = "processfilemany",
514 public = true,
515 protected = true,
516 arguments = { "string", false },
517 actions = useanyfile
518}
519
520implement {
521 name = "processfileonce",
522 public = true,
523 protected = true,
524 arguments = { "string", true },
525 actions = useanyfile
526}
527
528implement {
529 name = "processfilenone",
530 arguments = "string",
531 public = true,
532 protected = true,
533 actions = dummyfunction,
534}
535
536local tree = { type = "text", name = "", branches = { } }
537local treestack = { }
538local top = tree.branches
539local root = tree
540local namestack = { }
541local namelist = { }
542local namehash = { }
543local nametop = false
544
545local project_stack = { }
546local product_stack = { }
547local component_stack = { }
548local environment_stack = { }
549
550local stacks = {
551 [v_project ] = project_stack,
552 [v_product ] = product_stack,
553 [v_component ] = component_stack,
554 [v_environment] = environment_stack,
555}
556
557
558
559local function pushtree(what,name)
560 local t = { }
561 top[#top+1] = { type = what, name = name, branches = t }
562 insert(treestack,top)
563 top = t
564end
565
566local function pushcurrent(what,name)
567 if what == v_product or what == v_component or what == v_text then
568 insert(namestack,name)
569 nametop = copytable(namestack)
570 namelist[#namelist+1] = nametop
571 insert(namehash,#namelist)
572 end
573end
574
575local function poptree()
576 top = remove(treestack)
577 if #top[#top].branches == 0 then
578 top[#top].branches = nil
579 end
580end
581
582local function popcurrent(what)
583 if what == v_product or what == v_component then
584 remove(namestack)
585 remove(namehash)
586 end
587end
588
589do
590
591 local function log_tree(report,top,depth)
592 report("%s%s: %s",depth,top.type,top.name)
593 local branches = top.branches
594 if branches then
595 local n = #branches
596 if n > 0 then
597 depth = depth .. " "
598 for i=1,n do
599 log_tree(report,branches[i],depth)
600 end
601 else
602 top.brances = nil
603 end
604 end
605 end
606
607 logs.registerfinalactions(function()
608 root.name = environment.jobname
609
610 logs.startfilelogging(report,"used files")
611 log_tree(report,root,"")
612 logs.stopfilelogging()
613
614 if nofmissing > 0 and logs.loggingerrors() then
615 logs.starterrorlogging(report,"missing files")
616 for kind, list in sortedhash(missing) do
617 for name in sortedhash(list) do
618 report("%w%s %s",6,kind,name)
619 end
620 end
621 logs.stoperrorlogging()
622 end
623 end)
624
625end
626
627local jobstructure = job.structure or { }
628job.structure = jobstructure
629jobstructure.collected = jobstructure.collected or { }
630jobstructure.tobesaved = root
631jobstructure.components = { }
632jobstructure.namestack = { }
633
634function collectstructure(collected)
635 local namestack = { }
636 local n = 0
637 local function collect(root,result,stack)
638 local branches = root.branches
639 if branches then
640 local s = copytable(stack)
641 local m = #s + 1
642 for i=1,#branches do
643 local branch = branches[i]
644 local what = branch.type
645 if what == v_component then
646 result[#result+1] = branch.name
647 end
648 s[m] = branch.name
649 if what == v_product or what == v_component then
650 n = n + 1
651 namestack[n] = copytable(s)
652 end
653 collect(branch,result,s)
654 end
655 end
656 return result
657 end
658 local components = collect(collected,{},{})
659
660 return components, namestack
661end
662
663local function initialize()
664 jobstructure.components, jobstructure.namestack = collectstructure(jobstructure.collected)
665end
666
667function jobresolvers.collectstructure(collected)
668 return collectstructure(collected or jobstructure.collected)
669end
670
671do
672 local reported = { }
673
674 function jobresolvers.currentstructure()
675
676 local n = namehash[#namehash]
677 if trace_structure and not reported[n] then
678 local namestack = jobstructure.namestack
679 local new = namelist [n]
680 local old = namestack[n]
681 new = new and concat(new," ") or ""
682 old = old and concat(old," ") or ""
683 if old == new then
684 report_structure("%02i : %a",n,old)
685 else
686 report_structure("%02i : %a <> %a",n,old,new)
687 end
688 reported[n] = true
689 end
690 return n, namelist[n]
691 end
692
693end
694
695function jobresolvers.namelist(n,utilitydata)
696 if utilitydata then
697 return utilitydata.namestack[n]
698 else
699 return namelist[n] or jobstructure.namestack[n]
700 end
701end
702
703job.register('job.structure.collected',root,initialize)
704
705
706
707
708local ctx_processfilemany = context.processfilemany
709local ctx_processfileonce = context.processfileonce
710local ctx_processfilenone = context.processfilenone
711
712
713
714local function processfilecommon(name,action)
715
716
717
718
719
720
721
722
723
724 action(name)
725end
726
727local function processfilemany(name) processfilecommon(name,ctx_processfilemany) end
728local function processfileonce(name) processfilecommon(name,ctx_processfileonce) end
729local function processfilenone(name) processfilecommon(name,ctx_processfilenone) end
730
731local processors = utilities.storage.allocate {
732
733
734
735
736
737
738
739 [v_text] = {
740 [v_text] = { "many", processfilemany },
741 [v_project] = { "once", processfileonce },
742 [v_environment] = { "once", processfileonce },
743 [v_product] = { "many", processfilemany },
744 [v_component] = { "many", processfilemany },
745 },
746 [v_project] = {
747 [v_text] = { "many", processfilemany },
748 [v_project] = { "none", processfilenone },
749 [v_environment] = { "once", processfileonce },
750 [v_product] = { "none", processfilenone },
751 [v_component] = { "none", processfilenone },
752 },
753 [v_environment] = {
754 [v_text] = { "many", processfilemany },
755 [v_project] = { "none", processfilenone },
756 [v_environment] = { "once", processfileonce },
757 [v_product] = { "none", processfilenone },
758 [v_component] = { "none", processfilenone },
759 },
760 [v_product] = {
761 [v_text] = { "many", processfilemany },
762 [v_project] = { "once", processfileonce },
763 [v_environment] = { "once", processfileonce },
764 [v_product] = { "many", processfilemany },
765 [v_component] = { "many", processfilemany },
766 },
767 [v_component] = {
768 [v_text] = { "many", processfilemany },
769 [v_project] = { "once", processfileonce },
770 [v_environment] = { "once", processfileonce },
771 [v_product] = { "none", processfilenone },
772 [v_component] = { "many", processfilemany },
773 }
774}
775
776local start = {
777 [v_text] = nil,
778 [v_project] = nil,
779 [v_environment] = context.startreadingfile,
780 [v_product] = context.starttext,
781 [v_component] = context.starttext,
782}
783
784local stop = {
785 [v_text] = nil,
786 [v_project] = nil,
787 [v_environment] = context.stopreadingfile,
788 [v_product] = context.stoptext,
789 [v_component] = context.stoptext,
790}
791
792jobresolvers.processors = processors
793
794local function topofstack(what)
795 local stack = stacks[what]
796 return stack and stack[#stack] or environment.jobname
797end
798
799local function productcomponent()
800 local product = product_stack[#product_stack]
801 if product and product ~= "" then
802 local component = component_stack[1]
803 if component and component ~= "" then
804 return component
805 end
806 end
807end
808
809local function justacomponent()
810 local product = product_stack[#product_stack]
811 if not product or product == "" then
812 local component = component_stack[1]
813 if component and component ~= "" then
814 return component
815 end
816 end
817end
818
819jobresolvers.productcomponent = productcomponent
820jobresolvers.justacomponent = justacomponent
821
822function jobresolvers.currentproject () return topofstack(v_project ) end
823function jobresolvers.currentproduct () return topofstack(v_product ) end
824function jobresolvers.currentcomponent () return topofstack(v_component ) end
825function jobresolvers.currentenvironment() return topofstack(v_environment) end
826
827local done = { }
828local tolerant = false
829
830local function process(what,name)
831 local depth = #typestack
832 local process
833
834 name = resolveprefix(name)
835
836
837
838 process = processors[currenttype][what]
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854 if process then
855 local method = process[1]
856 if method == "none" then
857 if trace_jobfiles then
858 report_jobfiles("%s : %s : %s %s %a in %s %a",depth,method,"ignoring",what,name,currenttype,topofstack(currenttype))
859 end
860 elseif method == "once" and done[name] then
861 if trace_jobfiles then
862 report_jobfiles("%s : %s : %s %s %a in %s %a",depth,method,"skipping",what,name,currenttype,topofstack(currenttype))
863 end
864 else
865
866
867 done[name] = true
868 local before = start[what]
869 local after = stop [what]
870 if trace_jobfiles then
871 report_jobfiles("%s : %s : %s %s %a in %s %a",depth,method,"processing",what,name,currenttype,topofstack(currenttype))
872 end
873 if before then
874 before()
875 end
876 process[2](name)
877 if after then
878 after()
879 end
880 end
881 else
882 if trace_jobfiles then
883 report_jobfiles("%s : %s : %s %s %a in %s %a",depth,"none","ignoring",what,name,currenttype,topofstack(currenttype))
884 end
885 end
886end
887
888local scan_delimited = tokens.scanners.delimited
889
890local function getname()
891 return scan_delimited(91,93) or scan_delimited(0,32)
892end
893
894implement { name = "project", public = true, protected = true, actions = function() process(v_project, getname()) end }
895implement { name = "environment", public = true, protected = true, actions = function() process(v_environment,getname()) end }
896implement { name = "product", public = true, protected = true, actions = function() process(v_product, getname()) end }
897implement { name = "component", public = true, protected = true, actions = function() process(v_component, getname()) end }
898
899implement { name = "useproject", public = true, protected = true, actions = function(name) process(v_project, name) end, arguments = "optional" }
900implement { name = "useenvironment", public = true, protected = true, actions = function(name) process(v_environment,name) end, arguments = "optional" }
901implement { name = "useproduct", public = true, protected = true, actions = function(name) process(v_product, name) end, arguments = "optional" }
902implement { name = "usecomponent", public = true, protected = true, actions = function(name) process(v_component, name) end, arguments = "optional" }
903
904implement {
905 name = "ignorefile",
906 public = true,
907 protected = true,
908 arguments = "array",
909 actions = function(s)
910 for i=1,#s do
911 resolvers.ignorelibrary(s[i])
912 end
913 end
914}
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947local function gotonextlevel(what,how,name)
948 insert(stacks[what],name)
949 insert(typestack,currenttype)
950 currenttype = what
951 pushtree(what,name)
952 pushcurrent(what,name)
953 token.expandmacro(e_start..":"..what..":"..how)
954end
955
956local function gotopreviouslevel(what,how)
957 token.expandmacro(e_stop..":"..what..":"..how)
958 popcurrent(what)
959 poptree()
960 currenttype = remove(typestack) or v_text
961 remove(stacks[what])
962
963end
964
965local function autoname()
966 local name = scan_delimited(91,93) or scan_delimited(0,32)
967 if name == "*" then
968
969 name = nameonly(currentfile() or name)
970 end
971 return name
972end
973
974implement { name = "startproject", public = true, protected = true, actions = function() gotonextlevel(v_project, v_global, autoname()) end }
975implement { name = "startproduct", public = true, protected = true, actions = function() gotonextlevel(v_product, v_global, autoname()) end }
976implement { name = "startcomponent", public = true, protected = true, actions = function() gotonextlevel(v_component, v_global, autoname()) end }
977implement { name = "startlocalcomponent", public = true, protected = true, actions = function() gotonextlevel(v_component, v_local, autoname()) end }
978implement { name = "startenvironment", public = true, protected = true, actions = function() gotonextlevel(v_environment, v_global, autoname()) end }
979
980implement { name = "stopproject", public = true, protected = true, actions = function() gotopreviouslevel(v_project, v_global) end }
981implement { name = "stopproduct", public = true, protected = true, actions = function() gotopreviouslevel(v_product, v_global) end }
982implement { name = "stopcomponent", public = true, protected = true, actions = function() gotopreviouslevel(v_component, v_global) end }
983implement { name = "stoplocalcomponent", public = true, protected = true, actions = function() gotopreviouslevel(v_component, v_local ) end }
984implement { name = "stopenvironment", public = true, protected = true, actions = function() gotopreviouslevel(v_environment, v_global) end }
985
986implement { name = "currentproject", public = true, actions = function() context(topofstack(v_project )) end }
987implement { name = "currentproduct", public = true, actions = function() context(topofstack(v_product )) end }
988implement { name = "currentcomponent", public = true, actions = function() context(topofstack(v_component )) end }
989implement { name = "currentenvironment", public = true, actions = function() context(topofstack(v_environment)) end }
990
991
992
993
994
995
996
997
998
999
1000
1001local report_examodes = logs.reporter("system","examodes")
1002
1003local function convertexamodes(str)
1004 local x = xml.convert(str)
1005 for e in xml.collected(x,"exa:variable") do
1006 local label = e.at and e.at.label
1007 if label and label ~= "" then
1008 local data = xml.text(e)
1009 local mode = match(label,"^mode:(.+)$")
1010 if mode then
1011 context.enablemode { formatters["%s:%s"](mode,data) }
1012 end
1013 context.setvariable("exa:variables",label,(gsub(data,"([{}])","\\%1")))
1014 end
1015 end
1016end
1017
1018function environment.loadexamodes(filename)
1019 if not filename or filename == "" then
1020
1021 filename = removesuffix(tex.jobname)
1022 end
1023 filename = resolvers.findfile(addsuffix(filename,'ctm')) or ""
1024 if filename ~= "" then
1025 report_examodes("loading %a",filename)
1026 convertexamodes(io.loaddata(filename))
1027 else
1028 report_examodes("no mode file %a",filename)
1029 end
1030end
1031
1032implement {
1033 name = "loadexamodes",
1034 actions = environment.loadexamodes,
1035 public = true,
1036 protected = true,
1037 arguments = "optional"
1038}
1039
1040
1041
1042
1043
1044
1045document = document or {
1046 arguments = allocate(),
1047 files = allocate(),
1048 variables = allocate(),
1049 options = {
1050 commandline = {
1051 environments = allocate(),
1052 modules = allocate(),
1053 modes = allocate(),
1054 },
1055 ctxfile = {
1056 environments = allocate(),
1057 modules = allocate(),
1058 modes = allocate(),
1059 },
1060 },
1061 functions = table.setmetatablenewindex(function(t,k,v)
1062 if rawget(t,k) then
1063 report_functions("overloading document function %a",k)
1064 end
1065 rawset(t,k,v)
1066 return v
1067 end),
1068}
1069
1070function document.setargument(key,value)
1071 document.arguments[key] = value
1072end
1073
1074function document.setdefaultargument(key,default)
1075 local v = document.arguments[key]
1076 if v == nil or v == "" then
1077 document.arguments[key] = default
1078 end
1079end
1080
1081function document.setfilename(i,name)
1082 if name then
1083 document.files[tonumber(i)] = name
1084 else
1085 document.files[#document.files+1] = tostring(i)
1086 end
1087end
1088
1089function document.getargument(key,default)
1090 local v = document.arguments[key]
1091 if type(v) == "boolean" then
1092 v = (v and "yes") or "no"
1093 document.arguments[key] = v
1094 end
1095 return v or default or ""
1096end
1097
1098function document.getfilename(i)
1099 return document.files[tonumber(i)] or ""
1100end
1101
1102implement {
1103 name = "setdocumentargument",
1104 actions = document.setargument,
1105 arguments = "2 strings"
1106}
1107
1108implement {
1109 name = "setdocumentdefaultargument",
1110 actions = document.setdefaultargument,
1111 arguments = "2 strings"
1112}
1113
1114implement {
1115 name = "setdocumentfilename",
1116 actions = document.setfilename,
1117 arguments = { "integer", "string" }
1118}
1119
1120implement {
1121 name = "getdocumentargument",
1122 actions = { document.getargument, context },
1123 arguments = "2 strings"
1124}
1125
1126implement {
1127 name = "getdocumentfilename",
1128 actions = { document.getfilename, context },
1129 arguments = "integer"
1130}
1131
1132function document.setcommandline()
1133
1134
1135
1136 local arguments = document.arguments
1137 local files = document.files
1138 local options = document.options
1139
1140 for k, v in next, environment.arguments do
1141 k = gsub(k,"^c:","")
1142 if arguments[k] == nil then
1143 arguments[k] = v
1144 end
1145 end
1146
1147
1148
1149
1150
1151 if arguments.timing then
1152 context.usemodule { "timing" }
1153 end
1154
1155 if arguments.usage then
1156 trackers.enable("system.usage")
1157 end
1158
1159 if arguments.batchmode then
1160 context.batchmode(false)
1161 end
1162
1163 if arguments.nonstopmode then
1164 context.nonstopmode(false)
1165 end
1166
1167 if arguments.nostatistics then
1168 directives.enable("system.nostatistics")
1169 end
1170
1171 if arguments.notuc then
1172 directives.disable("job.save")
1173 end
1174
1175 if arguments.paranoid then
1176 context.setvalue("maxreadlevel",1)
1177 end
1178
1179 if validstring(arguments.path) then
1180 context.usepath { arguments.path }
1181 end
1182
1183 if arguments.export then
1184 context.setupbackend { export = v_yes }
1185 end
1186
1187 local inputfile = validstring(arguments.input)
1188
1189 if inputfile and dirname(inputfile) == "." and lfs.isfile(inputfile) then
1190
1191 inputfile = basename(inputfile)
1192 end
1193
1194 local forcedruns = arguments.forcedruns
1195 local kindofrun = arguments.kindofrun
1196 local currentrun = arguments.currentrun
1197 local maxnofruns = arguments.maxnofruns or arguments.runs
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209 context.setupsystem {
1210 directory = validstring(arguments.setuppath),
1211 inputfile = inputfile,
1212 file = validstring(arguments.result),
1213 random = validstring(arguments.randomseed),
1214
1215 n = validstring(kindofrun),
1216 m = validstring(currentrun),
1217 }
1218
1219 forcedruns = tonumber(forcedruns) or 0
1220 kindofrun = tonumber(kindofrun) or 0
1221 maxnofruns = tonumber(maxnofruns) or 0
1222 currentrun = tonumber(currentrun) or 0
1223
1224 local prerollrun = forcedruns > 0 and currentrun > 0 and currentrun < forcedruns
1225
1226 environment.forcedruns = forcedruns
1227 environment.kindofrun = kindofrun
1228 environment.maxnofruns = maxnofruns
1229 environment.currentrun = currentrun
1230 environment.prerollrun = prerollrun
1231
1232 context.setconditional("prerollrun",prerollrun)
1233
1234 if validstring(arguments.arguments) then
1235 context.setupenv { arguments.arguments }
1236 end
1237
1238 if arguments.once then
1239 directives.enable("system.runonce")
1240 end
1241
1242 if arguments.noarrange then
1243 context.setuparranging { variables.disable }
1244 end
1245
1246
1247
1248 local commandline = options.commandline
1249
1250 commandline.environments = table.append(commandline.environments,settings_to_array(validstring(arguments.environment)))
1251 commandline.modules = table.append(commandline.modules, settings_to_array(validstring(arguments.usemodule)))
1252 commandline.modes = table.append(commandline.modes, settings_to_array(validstring(arguments.mode)))
1253
1254
1255
1256 if #files == 0 then
1257 local list = settings_to_array(validstring(arguments.files))
1258 if list and #list > 0 then
1259 files = list
1260 end
1261 end
1262
1263 if #files == 0 then
1264 files = { validstring(arguments.input) }
1265 end
1266
1267
1268
1269 document.arguments = arguments
1270 document.files = files
1271
1272end
1273
1274
1275
1276local function apply(list,action)
1277 if list then
1278 for i=1,#list do
1279 action { list[i] }
1280 end
1281 end
1282end
1283
1284function document.setmodes()
1285 apply(document.options.ctxfile .modes,context.enablemode)
1286 apply(document.options.commandline.modes,context.enablemode)
1287end
1288
1289function document.setmodules()
1290 apply(document.options.ctxfile .modules,context.usemodule)
1291 apply(document.options.commandline.modules,context.usemodule)
1292end
1293
1294function document.setenvironments()
1295 apply(document.options.ctxfile .environments,context.environment)
1296 apply(document.options.commandline.environments,context.environment)
1297end
1298
1299function document.setfilenames()
1300 local initialize = environment.initializefilenames
1301 if initialize then
1302 initialize()
1303 else
1304
1305 end
1306end
1307
1308implement { name = "setdocumentcommandline", actions = document.setcommandline, onlyonce = true }
1309implement { name = "setdocumentmodes", actions = document.setmodes, onlyonce = true }
1310implement { name = "setdocumentmodules", actions = document.setmodules, onlyonce = true }
1311implement { name = "setdocumentenvironments", actions = document.setenvironments, onlyonce = true }
1312implement { name = "setdocumentfilenames", actions = document.setfilenames, onlyonce = true }
1313
1314do
1315
1316 logs.registerfinalactions(function()
1317 local foundintrees = resolvers.foundintrees()
1318 if #foundintrees > 0 then
1319 logs.startfilelogging(report,"used files")
1320 for i=1,#foundintrees do
1321 report("%4i: % T",i,foundintrees[i])
1322 end
1323 logs.stopfilelogging()
1324 end
1325 end)
1326
1327 logs.registerfinalactions(function()
1328 local files = document.files
1329 local arguments = document.arguments
1330
1331 logs.startfilelogging(report,"commandline options")
1332 if arguments and next(arguments) then
1333 for argument, value in sortedhash(arguments) do
1334 report(" %s=%A",argument,value)
1335 end
1336 else
1337 report("no arguments")
1338 end
1339 logs.stopfilelogging()
1340
1341 logs.startfilelogging(report,"commandline files")
1342 if files and #files > 0 then
1343 for i=1,#files do
1344 report("% 4i: %s",i,files[i])
1345 end
1346 else
1347 report("no files")
1348 end
1349 logs.stopfilelogging()
1350 end)
1351
1352end
1353
1354if environment.initex then
1355
1356 logs.registerfinalactions(function()
1357 local startfilelogging = logs.startfilelogging
1358 local stopfilelogging = logs.stopfilelogging
1359 startfilelogging(report,"stored tables")
1360 for k,v in sortedhash(storage.data) do
1361 report("%03i %s",k,v[1])
1362 end
1363 stopfilelogging()
1364 startfilelogging(report,"stored modules")
1365 for k,v in sortedhash(lua.bytedata) do
1366 report("%03i %s %s",k,v.name)
1367 end
1368 stopfilelogging()
1369 startfilelogging(report,"stored attributes")
1370 for k,v in sortedhash(attributes.names) do
1371 report("%03i %s",k,v)
1372 end
1373 stopfilelogging()
1374 startfilelogging(report,"stored catcodetables")
1375 for k,v in sortedhash(catcodes.names) do
1376 report("%03i % t",k,v)
1377 end
1378 stopfilelogging()
1379 startfilelogging(report,"stored corenamespaces")
1380 for k,v in sortedhash(interfaces.corenamespaces) do
1381 report("%03i %s",k,v)
1382 end
1383 stopfilelogging()
1384 end)
1385
1386end
1387
1388implement {
1389 name = "continueifinputfile",
1390 public = true,
1391 protected = true,
1392 arguments = "string",
1393 actions = function(inpname,basetoo)
1394 local inpnamefull = addsuffix(inpname,"tex")
1395 local inpfilefull = addsuffix(environment.inputfilename,"tex")
1396 local continue = inpnamefull == inpfilefull
1397
1398 if not continue then
1399 continue = inpnamefull == basename(inpfilefull)
1400 end
1401 if continue then
1402 report("continuing input file %a",inpname)
1403 else
1404 context.endinput()
1405 end
1406
1407 end
1408}
1409
1410
1411
1412local helpers = resolvers.finders.helpers
1413local validhashed = helpers.validhashed
1414local registerhashed = helpers.registerhashed
1415local registerfilescheme = helpers.registerfilescheme
1416
1417implement {
1418 name = "registerhashedfiles",
1419 public = true,
1420 protected = true,
1421 arguments = "optional",
1422 actions = function(list)
1423 for name in gmatch(list,"[^, ]+") do
1424 registerhashed(name)
1425 end
1426 end,
1427}
1428
1429implement {
1430 name = "registerfilescheme",
1431 public = true,
1432 protected = true,
1433 arguments = "optional",
1434 actions = function(list)
1435 for name in gmatch(list,"[^, ]+") do
1436 registerfilescheme(name)
1437 end
1438 end,
1439}
1440
1441implement {
1442 name = "doifelsevalidhashedfiles",
1443 public = true,
1444 protected = true,
1445 arguments = "string",
1446 actions = function(name)
1447 ctx_doifelse(validhashed(name))
1448 end,
1449}
1450
1451implement {
1452 name = "adddefaultsuffix",
1453 public = true,
1454 protected = true,
1455 actions = function(list)
1456 resolvers.defaultsuffixes = unique(
1457 imerged(
1458 resolvers.defaultsuffixes,
1459 list
1460 )
1461 )
1462 end,
1463 arguments = "array",
1464}
1465
1466
1467
1468implement {
1469 name = "filebasename",
1470 public = true,
1471 arguments = "argument",
1472 actions = { basename, context },
1473}
1474
1475implement {
1476 name = "filenameonly",
1477 public = true,
1478 arguments = "argument",
1479 actions = { nameonly, context },
1480}
1481
1482implement {
1483 name = "filedirname",
1484 public = true,
1485 arguments = "argument",
1486 actions = { dirname, context },
1487}
1488
1489implement {
1490 name = "filesuffix",
1491 public = true,
1492 arguments = "argument",
1493 actions = { dirname, context },
1494}
1495 |