file-mod.lmt /size: 9416 b    last modification: 2023-12-21 09:44
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
67local setrunstate = tex.setrunstate
68local level       = 0
69
70local function pushrunstate()
71    level = level + 1
72    if level == 1 then
73        setrunstate(1)
74    end
75end
76
77local function poprunstate()
78    if level == 1 then
79        setrunstate(3)
80    end
81    if level > 0 then
82        level = level - 1
83    end
84end
85
86luatex.pushrunstate = pushrunstate
87luatex.poprunstate  = poprunstate
88
89tex.setrunstate     = function() end
90
91implement { name = "pushrunstate", public = true, protected = true, actions = pushrunstate }
92implement { name = "poprunstate",  public = true, protected = true, actions = poprunstate  }
93
94-- Till here.
95
96local modstatus = { }
97local missing   = false
98
99local function usemodule(name,hasscheme)
100    local foundname
101    if hasscheme then
102        -- no auto suffix as http will return a home page or error page
103        -- so we only add one if missing
104        local fullname = file.addsuffix(name,"tex")
105        if trace_modules then
106            report("checking url %a",fullname)
107        end
108        foundname = resolvers.findtexfile(fullname) or ""
109    elseif file.suffix(name) ~= "" then
110        if trace_modules then
111            report("checking file %a",name)
112        end
113        foundname = findbyscheme("any",name) or ""
114    else
115        for i=1,#suffixes do
116            local fullname = file.addsuffix(name,suffixes[i])
117            if trace_modules then
118                report("checking file %a",fullname)
119            end
120            foundname = findbyscheme("any",fullname) or ""
121            if foundname ~= "" then
122                break
123            end
124        end
125    end
126    if foundname ~= "" then
127        if trace_modules then
128            report("loading file %a",foundname)
129        end
130        context.pushrunstate()
131        context.startreadingfile()
132        resolvers.jobs.usefile(foundname,true) -- once, notext
133     -- context.input(foundname)
134        context.stopreadingfile()
135        context.poprunstate()
136        return true
137    else
138        return false
139    end
140end
141
142function environment.usemodules(prefix,askedname,truename)
143    local truename = truename or environment.truefilename(askedname) or askedname
144    if truename and truename ~= "" then
145        local hasprefix = prefix and prefix ~= ""
146        local hashname = ((hasprefix and prefix) or "*") .. "-" .. truename
147        local status = modstatus[hashname] or false -- yet unset
148        if status == 0 then
149            -- not found
150        elseif status == 1 then
151            status = status + 1
152        else
153            if trace_modules then
154                report("locating, prefix %a, askedname %a, truename %a",prefix,askedname,truename)
155            end
156            local hasscheme = url.hasscheme(truename)
157            if hasscheme then
158                -- no prefix and suffix done
159                if usemodule(truename,true) then
160                    status = 1
161                else
162                    status = 0
163                end
164            elseif hasprefix then
165                if usemodule(prefix .. "-" .. truename) then
166                    status = 1
167                else
168                    status = 0
169                end
170            else
171                for i=1,#prefixes do
172                    -- todo: reconstruct name i.e. basename
173                    local thename = prefixes[i] .. "-" .. truename
174                    if thename == tex.jobname then
175                        -- in case we process a module
176                        status = 1
177                        break
178                    elseif usemodule(thename) then
179                        status = 1
180                        break
181                    end
182                end
183                if status then
184                    -- ok, don't change
185                elseif find(truename,"-",1,true) and usemodule(truename) then
186                    -- assume a user namespace
187                    report("using user prefixed file %a",truename)
188                    status = 1
189                elseif permit_unprefixed and usemodule(truename) then
190                    report("using unprefixed file %a",truename)
191                    status = 1
192                else
193                    status = 0
194                end
195            end
196        end
197        if status == 0 then
198            missing = true
199            report("%a is not found",askedname)
200        elseif status == 1 then
201            report("%a is loaded",trace_modules and truename or askedname)
202        else
203            report("%a is already loaded",trace_modules and truename or askedname)
204        end
205        modstatus[hashname] = status
206    end
207end
208
209statistics.register("loaded tex modules", function()
210    if next(modstatus) then
211        local t, f, nt, nf = { }, { }, 0, 0
212        for k, v in sortedhash(modstatus) do
213            local b = basename(k)
214            if v == 0 then
215                nf = nf + 1
216                f[nf] = b
217            else
218                nt = nt + 1
219                t[nt] = b
220            end
221        end
222        if nf == 0 then
223            return format("%s requested, all found (%s)",nt,concat(t," "))
224        elseif nt == 0 then
225            return format("%s requested, all missing (%s)",nf,concat(f," "))
226        else
227            return format("%s requested, %s found (%s), %s missing (%s)",nt+nf,nt,concat(t," "),nf,concat(f," "))
228        end
229    else
230        return nil
231    end
232end)
233
234logs.registerfinalactions(function()
235    logs.startfilelogging(report,"used modules")
236    for k, v in sortedhash(modstatus) do
237        report(v == 0 and "missing: %s" or "loaded : %s",basename(k))
238    end
239    logs.stopfilelogging()
240    if missing and logs.loggingerrors() then
241        logs.starterrorlogging(report,"missing modules")
242        for k, v in sortedhash(modstatus) do
243            if v == 0 then
244                report("%w%s",6,basename(k))
245            end
246        end
247        logs.stoperrorlogging()
248    end
249end)
250
251-- moved from syst-lua.lua:
252
253local lpegmatch = lpeg.match
254local splitter  = lpeg.tsplitter(lpeg.S(". "),tonumber)
255
256function environment.comparedversion(one,two) -- one >= two
257    if not two or two == "" then
258        one, two = environment.version, one
259    elseif one == "" then
260        one = environment.version
261    end
262    one = lpegmatch(splitter,one)
263    two = lpegmatch(splitter,two)
264    one = (one[1] or 0) * 10000 + (one[2] or 0) * 100 + (one[3] or 0)
265    two = (two[1] or 0) * 10000 + (two[2] or 0) * 100 + (two[3] or 0)
266    if one < two then
267        return -1
268    elseif one > two then
269        return 1
270    else
271        return 0
272    end
273end
274
275environment.comparedversion = comparedversion
276
277
278function environment.useluamodule(list)
279    for filename in iterator(list) do
280        environment.loadluafile(filename)
281    end
282end
283
284implement {
285    name      = "usemodules",
286    actions   = environment.usemodules,
287    arguments = "2 strings",
288}
289
290implement {
291    name      = "doifelseolderversion",
292    actions   = function(one,two) commands.doifelse(comparedversion(one,two) >= 0) end,
293    arguments = "2 strings"
294}
295
296implement {
297    name      = "useluamodule",
298    actions   = environment.useluamodule,
299    arguments = "string"
300}
301
302implement {
303    name      = "loadluamodule",
304    actions   = function(name) dofile(resolvers.findctxfile(name)) end, -- hack
305    arguments = "string"
306}
307
308