file-mod.lmt /size: 9 Kb    last modification: 2025-02-21 11:03
1if not modules then modules = { } end modules ['file-mod'] = {
2    version   = 1.001,
3    comment   = "companion to file-mod.mkvi",
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-- This module will be redone! For instance, the prefixes will move to data-* as
10-- they are sort of generic along with home:// etc/.
11--
12-- It is more convenient to manipulate filenames (paths) in Lua than in TeX. The
13-- methods below have counterparts at the TeX end.
14
15local format, find, concat, tonumber = string.format, string.find, table.concat, tonumber
16local sortedhash = table.sortedhash
17local basename = file.basename
18
19local trace_modules     = false  trackers  .register("modules.loading",          function(v) trace_modules     = v end)
20local permit_unprefixed = false  directives.register("modules.permitunprefixed", function(v) permit_unprefixed = v end)
21
22local report            = logs.reporter("modules")
23
24local commands          = commands
25local context           = context
26local implement         = interfaces.implement
27
28local findbyscheme      = resolvers.finders.byscheme -- use different one
29local iterator          = utilities.parsers.iterator
30
31-- modules can have a specific suffix or can specify one
32
33local prefixes = {
34    "m", -- module, extends functionality
35    "p", -- private code
36    "s", -- styles
37    "x", -- xml specific modules
38 -- "v", -- an old internal one for examples
39    "t", -- third party extensions
40}
41
42-- the order might change and how about cld
43
44local suffixes = CONTEXTLMTXMODE > 0 and
45{
46    "mklx", -- preprocessed mkiv lmtx files
47    "mkxl", -- mkiv lmtx files
48    "mkvi", -- preprocessed mkiv files
49    "mkiv", -- mkiv files
50    "tex",  -- normally source code files
51    "cld",  -- context lua documents (often stand alone)
52    "lua",  -- lua files
53}
54    or
55{
56    "mkvi",
57    "mkiv",
58    "tex",
59    "cld",
60    "lua",
61}
62
63-- This controls embedded overloads. Maybe at some point we will limit this to files in the
64-- tree but on the other hand we don't need to be that strict. It is just an extra level
65-- of protection.
66
67-- todo: tex.getrunstatevalues
68--
69-- enforced : when state != production
70--
71-- 0 : initializing
72-- 1 : updating
73-- 2 : production
74
75local pushrunstate, poprunstate  do
76
77    local runstatecodes = tex.runstatecodes
78    local runstate      = tex.getrunstate()
79    local setrunstate   = tex.setrunstate
80
81    local initializing <const> = runstatecodes.initializing
82    local updating     <const> = runstatecodes.updating
83    ----- production   <const> = runstatecodes.production
84
85    if runstate == initializing then
86
87        pushrunstate = function() end
88        poprunstate  = function() end
89
90    else
91
92        local level = 0
93
94        pushrunstate = function()
95            level = level + 1
96            if level == 1 then
97                setrunstate(updating)
98            end
99        end
100
101        poprunstate = function()
102            if level == 1 then
103                setrunstate(runstate)
104            end
105            if level > 0 then
106                level = level - 1
107            end
108        end
109
110    end
111
112    luatex.pushrunstate = pushrunstate
113    luatex.poprunstate  = poprunstate
114
115    tex.setrunstate     = function() end
116
117    implement { name = "pushrunstate", public = true, protected = true, actions = pushrunstate }
118    implement { name = "poprunstate",  public = true, protected = true, actions = poprunstate  }
119
120end
121
122-- Till here.
123
124local modstatus = { }
125local missing   = false
126
127local function usemodule(name,hasscheme)
128    local foundname
129    if hasscheme then
130        -- no auto suffix as http will return a home page or error page
131        -- so we only add one if missing
132        local fullname = file.addsuffix(name,"tex")
133        if trace_modules then
134            report("checking url %a",fullname)
135        end
136        foundname = resolvers.findtexfile(fullname) or ""
137    elseif file.suffix(name) ~= "" then
138        if trace_modules then
139            report("checking file %a",name)
140        end
141        foundname = findbyscheme("any",name) or ""
142    else
143        for i=1,#suffixes do
144            local fullname = file.addsuffix(name,suffixes[i])
145            if trace_modules then
146                report("checking file %a",fullname)
147            end
148            foundname = findbyscheme("any",fullname) or ""
149            if foundname ~= "" then
150                break
151            end
152        end
153    end
154    if foundname ~= "" then
155        if trace_modules then
156            report("loading file %a",foundname)
157        end
158        context.pushrunstate()
159        context.startreadingfile()
160        resolvers.jobs.usefile(foundname,true) -- once, notext
161     -- context.input(foundname)
162        context.stopreadingfile()
163        context.poprunstate()
164        return true
165    else
166        return false
167    end
168end
169
170function environment.usemodules(prefix,askedname,truename)
171    local truename = truename or environment.truefilename(askedname) or askedname
172    if truename and truename ~= "" then
173        local hasprefix = prefix and prefix ~= ""
174        local hashname = ((hasprefix and prefix) or "*") .. "-" .. truename
175        local status = modstatus[hashname] or false -- yet unset
176        if status == 0 then
177            -- not found
178        elseif status == 1 then
179            status = status + 1
180        else
181            if trace_modules then
182                report("locating, prefix %a, askedname %a, truename %a",prefix,askedname,truename)
183            end
184            local hasscheme = url.hasscheme(truename)
185            if hasscheme then
186                -- no prefix and suffix done
187                if usemodule(truename,true) then
188                    status = 1
189                else
190                    status = 0
191                end
192            elseif hasprefix then
193                if usemodule(prefix .. "-" .. truename) then
194                    status = 1
195                else
196                    status = 0
197                end
198            else
199                for i=1,#prefixes do
200                    -- todo: reconstruct name i.e. basename
201                    local thename = prefixes[i] .. "-" .. truename
202                    if thename == tex.jobname then
203                        -- in case we process a module
204                        status = 1
205                        break
206                    elseif usemodule(thename) then
207                        status = 1
208                        break
209                    end
210                end
211                if status then
212                    -- ok, don't change
213                elseif find(truename,"-",1,true) and usemodule(truename) then
214                    -- assume a user namespace
215                    report("using user prefixed file %a",truename)
216                    status = 1
217                elseif permit_unprefixed and usemodule(truename) then
218                    report("using unprefixed file %a",truename)
219                    status = 1
220                else
221                    status = 0
222                end
223            end
224        end
225        if status == 0 then
226            missing = true
227            report("%a is not found",askedname)
228        elseif status == 1 then
229            report("%a is loaded",trace_modules and truename or askedname)
230        else
231            report("%a is already loaded",trace_modules and truename or askedname)
232        end
233        modstatus[hashname] = status
234    end
235end
236
237statistics.register("loaded tex modules", function()
238    if next(modstatus) then
239        local t, f, nt, nf = { }, { }, 0, 0
240        for k, v in sortedhash(modstatus) do
241            local b = basename(k)
242            if v == 0 then
243                nf = nf + 1
244                f[nf] = b
245            else
246                nt = nt + 1
247                t[nt] = b
248            end
249        end
250        if nf == 0 then
251            return format("%s requested, all found (%s)",nt,concat(t," "))
252        elseif nt == 0 then
253            return format("%s requested, all missing (%s)",nf,concat(f," "))
254        else
255            return format("%s requested, %s found (%s), %s missing (%s)",nt+nf,nt,concat(t," "),nf,concat(f," "))
256        end
257    else
258        return nil
259    end
260end)
261
262logs.registerfinalactions(function()
263    logs.startfilelogging(report,"used modules")
264    for k, v in sortedhash(modstatus) do
265        report(v == 0 and "  missing: %s" or "  loaded : %s",basename(k))
266    end
267    logs.stopfilelogging()
268    if missing and logs.loggingerrors() then
269        logs.starterrorlogging(report,"missing modules")
270        for k, v in sortedhash(modstatus) do
271            if v == 0 then
272                report("%w%s",6,basename(k))
273            end
274        end
275        logs.stoperrorlogging()
276    end
277end)
278
279-- moved from syst-lua.lua:
280
281local lpegmatch = lpeg.match
282local splitter  = lpeg.tsplitter(lpeg.S(". "),tonumber)
283
284function environment.comparedversion(one,two) -- one >= two
285    if not two or two == "" then
286        one, two = environment.version, one
287    elseif one == "" then
288        one = environment.version
289    end
290    one = lpegmatch(splitter,one)
291    two = lpegmatch(splitter,two)
292    one = (one[1] or 0) * 10000 + (one[2] or 0) * 100 + (one[3] or 0)
293    two = (two[1] or 0) * 10000 + (two[2] or 0) * 100 + (two[3] or 0)
294    if one < two then
295        return -1
296    elseif one > two then
297        return 1
298    else
299        return 0
300    end
301end
302
303environment.comparedversion = comparedversion
304
305
306function environment.useluamodule(list)
307    for filename in iterator(list) do
308        environment.loadluafile(filename)
309    end
310end
311
312implement {
313    name      = "usemodules",
314    actions   = environment.usemodules,
315    arguments = "2 strings",
316}
317
318implement {
319    name      = "doifelseolderversion",
320    actions   = function(one,two) commands.doifelse(comparedversion(one,two) >= 0) end,
321    arguments = "2 strings"
322}
323
324implement {
325    name      = "useluamodule",
326    actions   = environment.useluamodule,
327    arguments = "string"
328}
329
330implement {
331    name      = "loadluamodule",
332    actions   = function(name) dofile(resolvers.findctxfile(name)) end, -- hack
333    arguments = "string"
334}
335
336