data-ini.lua /size: 10 Kb    last modification: 2023-12-21 09:44
1if not modules then modules = { } end modules ['data-ini'] = {
2    version   = 1.001,
3    comment   = "companion to luat-lib.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
9local next, type, getmetatable, rawset = next, type, getmetatable, rawset
10local gsub, find, gmatch, char = string.gsub, string.find, string.gmatch, string.char
11local filedirname, filebasename, filejoin, replacesuffix = file.dirname, file.basename, file.join, file.replacesuffix
12local ostype, osname, osuname, ossetenv, osgetenv = os.type, os.name, os.uname, os.setenv, os.getenv
13local sortedpairs = table.sortedpairs
14local isfile, currentdir = lfs.isfile, lfs.currentdir
15local expandlink = dir.expandlink
16
17local P, S, R, C, Cs, Cc, lpegmatch = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.Cs, lpeg.Cc, lpeg.match
18
19local trace_locating   = false  trackers.register("resolvers.locating",   function(v) trace_locating   = v end)
20local trace_expansions = false  trackers.register("resolvers.expansions", function(v) trace_expansions = v end)
21
22local report_initialization = logs.reporter("resolvers","initialization")
23
24-- The code here used to be part of a data-res but for convenience we now split it over multiple
25-- files. As this file is now the starting point we introduce resolvers here. We also put some
26-- helpers here that later can be reimplemented of extended.
27
28resolvers       = resolvers or { }
29local resolvers = resolvers
30
31-- We don't want the kpse library to kick in. Also, we want to be able to
32-- execute programs. Control over execution is implemented later.
33
34texconfig.kpse_init    = false
35texconfig.shell_escape = 't'
36
37if not (environment and environment.default_texmfcnf) and kpse and kpse.default_texmfcnf then
38    local default_texmfcnf = kpse.default_texmfcnf()
39    -- looks more like context
40    default_texmfcnf = gsub(default_texmfcnf,"$SELFAUTOLOC","selfautoloc:")
41    default_texmfcnf = gsub(default_texmfcnf,"$SELFAUTODIR","selfautodir:")
42    default_texmfcnf = gsub(default_texmfcnf,"$SELFAUTOPARENT","selfautoparent:")
43    default_texmfcnf = gsub(default_texmfcnf,"$HOME","home:")
44    --
45    environment.default_texmfcnf = default_texmfcnf
46end
47
48kpse = { original = kpse }
49
50setmetatable(kpse, {
51    __index = function(kp,name)
52        report_initialization("fatal error: kpse library is accessed (key: %s)",name)
53        os.exit()
54    end
55} )
56
57-- First we check a couple of environment variables. Some might be
58-- set already but we need then later on. We start with the system
59-- font path.
60
61do
62
63    local osfontdir = osgetenv("OSFONTDIR")
64
65    if osfontdir and osfontdir ~= "" then
66        -- ok
67    elseif osname == "windows" then
68        ossetenv("OSFONTDIR","c:/windows/fonts//")
69    elseif osname == "macosx" then
70        ossetenv("OSFONTDIR","$HOME/Library/Fonts//;/Library/Fonts//;/System/Library/Fonts//")
71    end
72
73end
74
75-- Next comes the user's home path. We need this as later on we have
76-- to replace ~ with its value.
77
78if not environment.homedir then
79
80    local oldhome = osgetenv('HOME')
81    local homedir = osgetenv(ostype == "windows" and 'USERPROFILE' or 'HOME') or ''
82
83    if not homedir or homedir == "" then
84        homedir = char(127) -- we need a value, later we wil trigger on it
85    end
86
87    homedir = file.collapsepath(homedir)
88
89    ossetenv("HOME",       homedir) -- can be used in unix cnf files
90    ossetenv("USERPROFILE",homedir) -- can be used in windows cnf files
91
92    environment.oldhome = oldhome
93    environment.homedir = homedir
94
95end
96
97-- The following code sets the name of the own binary and its
98-- path. This is fallback code as we have os.selfdir now.
99
100do
101
102    local args = environment.originalarguments or arg -- this needs a cleanup
103
104    if not environment.ownmain then
105        environment.ownmain = status and string.match(string.lower(status.banner),"this is ([%a]+)") or "luatex"
106    end
107
108    local ownbin  = environment.ownbin  or os.selfbin or args[-2] or arg[-2] or args[-1] or arg[-1] or arg[0] or "luametatex"
109    local ownpath = environment.ownpath or os.selfdir
110
111    ownbin  = file.collapsepath(ownbin)   -- This one an actually also contain the path!
112    ownpath = file.collapsepath(ownpath)
113
114    -- We need to follow the symlink on osx - texlive. This only works luametatex and
115    -- lmtx so we might as wel forget about luatex/mkiv there. The regular context
116    -- installation doesn't have this link issue. I tried several variants but in the
117    -- end the chdir variants was most reliable. Everything else is ugly. (Experimtal
118    -- code is in data-osx.lua).
119
120    ownpath = expandlink(ownpath,trace_locating and report_initialization)
121
122    if not ownpath or ownpath == "" or ownpath == "unset" then
123        ownpath = args[-1] or arg[-1]
124        ownpath = ownpath and filedirname(gsub(ownpath,"\\","/"))
125        if not ownpath or ownpath == "" then
126            ownpath = args[-0] or arg[-0]
127            ownpath = ownpath and filedirname(gsub(ownpath,"\\","/"))
128        end
129        local binary = ownbin
130        if not ownpath or ownpath == "" then
131            ownpath = ownpath and filedirname(binary)
132        end
133        if not ownpath or ownpath == "" then
134            if os.binsuffix ~= "" then
135                binary = replacesuffix(binary,os.binsuffix)
136            end
137            local path = osgetenv("PATH")
138            if path then
139                for p in gmatch(path,"[^"..io.pathseparator.."]+") do
140                    local b = filejoin(p,binary)
141                    if isfile(b) then
142                        ownpath = expandlink(p,trace_locating and report_initialization)
143                        break
144                    end
145                end
146            end
147        end
148        if not ownpath or ownpath == "" then
149            ownpath = "."
150            report_initialization("forcing fallback to ownpath %a",ownpath)
151        elseif trace_locating then
152            report_initialization("using ownpath %a",ownpath)
153        end
154    end
155
156    environment.ownbin  = ownbin
157    environment.ownpath = ownpath
158
159end
160
161resolvers.ownpath = environment.ownpath
162
163function resolvers.getownpath()
164    return environment.ownpath
165end
166
167-- The self variables permit us to use only a few (or even no)
168-- environment variables.
169
170do
171
172    local ownpath = environment.ownpath or dir.current()
173
174    if ownpath then
175        ossetenv('SELFAUTOLOC',    file.collapsepath(ownpath))
176        ossetenv('SELFAUTODIR',    file.collapsepath(ownpath .. "/.."))
177        ossetenv('SELFAUTOPARENT', file.collapsepath(ownpath .. "/../.."))
178    else
179        report_initialization("error: unable to locate ownpath")
180        os.exit()
181    end
182
183end
184
185-- The running os:
186
187-- todo: check is context sits here os.platform is more trustworthy
188-- that the bin check as mtx-update runs from another path
189
190local texos   = environment.texos   or osgetenv("TEXOS")
191local texmfos = environment.texmfos or osgetenv('SELFAUTODIR')
192
193if not texos or texos == "" then
194    texos = file.basename(texmfos)
195end
196
197ossetenv('TEXMFOS',       texmfos)      -- full bin path
198ossetenv('TEXOS',         texos)        -- partial bin parent
199ossetenv('SELFAUTOSYSTEM',os.platform)  -- bonus
200
201environment.texos   = texos
202environment.texmfos = texmfos
203
204-- The current root:
205
206local texroot = environment.texroot or osgetenv("TEXROOT")
207
208if not texroot or texroot == "" then
209    texroot = osgetenv('SELFAUTOPARENT')
210    ossetenv('TEXROOT',texroot)
211end
212
213environment.texroot = file.collapsepath(texroot)
214
215-- a forward definition
216
217-- Because we use resolvers.resolve a lot later on, we will implement the basics here and
218-- add more later.
219
220local prefixes     = utilities.storage.allocate()
221resolvers.prefixes = prefixes
222
223local resolved     = { }
224local abstract     = { }
225local dynamic      = { }
226
227function resolvers.resetresolve(str)
228    resolved, abstract = { }, { }
229end
230
231function resolvers.allprefixes(separator)
232    local all = table.sortedkeys(prefixes)
233    if separator then
234        for i=1,#all do
235            all[i] = all[i] .. ":"
236        end
237    end
238    return all
239end
240
241local function _resolve_(method,target)
242    local action = prefixes[method]
243    if action then
244        return action(target)
245    else
246        return method .. ":" .. target
247    end
248end
249
250function resolvers.unresolve(str)
251    return abstract[str] or str
252end
253
254function resolvers.setdynamic(str)
255    dynamic[str] = true
256end
257
258-- home:xx;selfautoparent:xx;
259
260local pattern   = Cs((C(R("az")^2) * P(":") * C((1-S(" \"\';,"))^1) / _resolve_ + P(1))^0)
261
262local prefix    = C(R("az")^2) * P(":")
263local target    = C((1-S(" \"\';,"))^1)
264local notarget  = (#S(";,") + P(-1)) * Cc("")
265
266local p_resolve = Cs(((prefix * (target + notarget)) / _resolve_ + P(1))^0)
267local p_simple  = prefix * P(-1)
268
269local function resolve(str) -- use schemes, this one is then for the commandline only
270    if type(str) == "table" then
271        local res = { }
272        for i=1,#str do
273            res[i] = resolve(str[i])
274        end
275        return res
276    end
277    -- already resolved
278    local res = resolved[str]
279    if res then
280        return res
281    end
282    -- simple resolving of (dynamic) methods
283    local simple = lpegmatch(p_simple,str)
284    local action = prefixes[simple]
285    if action then
286        local res = action(res)
287        if not dynamic[simple] then
288            resolved[simple] = res
289            abstract[res] = simple
290        end
291        return res
292    end
293    -- more extensive resolving (multiple too)
294    res = lpegmatch(p_resolve,str)
295    resolved[str] = res
296    abstract[res] = str
297    return res
298end
299
300resolvers.resolve = resolve
301
302if type(osuname) == "function" then
303
304    for k, v in next, osuname() do
305        if not prefixes[k] then
306            prefixes[k] = function() return v end
307        end
308    end
309
310end
311
312if ostype == "unix" then
313
314    -- We need to distringuish between a prefix and something else : so we
315    -- have a special repath variant for linux. Also, when a new prefix is
316    -- defined, we need to remake the matcher.
317
318    local pattern
319
320    local function makepattern(t,k,v)
321        if t then
322            rawset(t,k,v)
323        end
324        local colon = P(":")
325        for k, v in sortedpairs(prefixes) do
326            if p then
327                p = P(k) + p
328            else
329                p = P(k)
330            end
331        end
332        pattern = Cs((p * colon + colon/";" + P(1))^0)
333    end
334
335    makepattern()
336
337    table.setmetatablenewindex(prefixes,makepattern)
338
339    function resolvers.repath(str)
340        return lpegmatch(pattern,str)
341    end
342
343else -- already the default:
344
345    function resolvers.repath(str)
346        return str
347    end
348
349end
350