1if not modules then modules = { } end modules ['mtx-install-modules'] = {
2 version = 1.234,
3 comment = "companion to mtxrun.lua",
4 author = "Hans Hagen",
5 copyright = "ConTeXt Development Team",
6 license = "see context related readme files"
7}
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32local find = string.find
33
34local helpinfo = [[
35<?xml version="1.0"?>
36<application>
37 <metadata>
38 <entry name="name">mtx-install</entry>
39 <entry name="detail">ConTeXt Installer</entry>
40 <entry name="version">2.01</entry>
41 </metadata>
42 <flags>
43 <category name="basic">
44 <subcategory>
45 <flag name="list"><short>list modules</short></flag>
46 <flag name="installed"><short>list installed modules</short></flag>
47 <flag name="install"><short>install modules</short></flag>
48 <flag name="uninstall"><short>uninstall modules</short></flag>
49 <flag name="module"><short>install (zip) file(s)</short></flag>
50 </subcategory>
51 </category>
52 </flags>
53 <examples>
54 <category>
55 <title>Examples</title>
56 <subcategory>
57 <example><command>mtxrun --script install-modules --list</command></example>
58 </subcategory>
59 <subcategory>
60 <example><command>mtxrun --script install-modules --install filter letter</command></example>
61 <example><command>mtxrun --script install-modules --install tikz</command></example>
62 <example><command>mtxrun --script install-modules --install --all</command></example>
63 </subcategory>
64 <subcategory>
65 <example><command>mtxrun --script install-modules --install --module t-letter.zip</command></example>
66 <example><command>mtxrun --script install-modules --uninstall --module t-letter.zip</command></example>
67 </subcategory>
68 <subcategory>
69 <example><command>mtxrun --script install-modules --installed</command></example>
70 </subcategory>
71 </category>
72 </examples>
73</application>
74]]
75
76local application = logs.application {
77 name = "mtx-install-modules",
78 banner = "ConTeXt Module Installer 1.00",
79 helpinfo = helpinfo,
80}
81
82local report = application.report
83
84scripts = scripts or { }
85scripts.modules = scripts.modules or { }
86
87local okay, curl = pcall(require,"libs-imp-curl")
88
89local fetched = curl and curl.fetch and function(str)
90 local data, message = curl.fetch {
91 url = str,
92 followlocation = true,
93 sslverifyhost = false,
94 sslverifypeer = false,
95 }
96 if not data then
97 report("some error: %s",message)
98 end
99 return data
100end or function(str)
101
102
103 local data = os.resultof("curl -sSL " .. str)
104 return data
105end
106
107
108
109local urls = {
110 ctan = "https://mirrors.ctan.org/install",
111 modules = "https://modules.contextgarden.net/dl"
112}
113
114
115
116
117
118
119
120
121
122
123local tmpzipfile = "temp.zip"
124local checkdir = "texmf-context"
125local targetdir = "texmf-modules"
126local basefile = "mtx-install-imp-modules.lua"
127local lists = false
128
129
130
131
132
133
134
135
136
137
138
139
140
141local function loadlists()
142 if not lists then
143 lists = { }
144 local mainfile = resolvers.findfile(basefile)
145 if mainfile and mainfile ~= "" then
146 local path = file.pathpart(mainfile)
147 local files = dir.glob((path == "" and "." or path) .. "/mtx-install-imp*.lua")
148 for i=1,#files do
149 local name = files[i]
150 local data = table.load(name)
151 if data then
152 local entries = data.lists
153 if entries then
154 report("loading entries from file %a",name)
155 for entry, data in table.sortedhash(entries) do
156 if lists[entry] then
157 report("entry %a already set from %a",entry,name)
158 else
159 lists[entry] = data
160 end
161 end
162 else
163 report("no entries in file %a",name)
164 end
165 end
166 end
167 else
168 report("base file %a is not found",basefile)
169 end
170 report()
171 end
172end
173
174local function validate(n)
175 return not (
176 find(n,"latex")
177 or find(n,"lualatex")
178 or find(n,"plain")
179 or find(n,"optex")
180
181
182 )
183end
184
185local function install(list,wipe)
186 if type(list) ~= "table" then
187 report("unknown specification")
188 end
189 local zips = list.zips
190 local wipes = list.wipes
191 if type(zips) ~= "table" then
192 report("incomplete specification")
193 else
194
195 for i=1,#zips do
196 local remote = list.url
197 local where = zips[i]
198 local hash = file.addsuffix(sha2.HASH256(where),"tma")
199 local data = table.load(hash)
200 if data then
201 local name = data.name
202 local list = data.list
203 if name and list then
204 report()
205 report("removing %i old files for %a",#list,name)
206 report()
207 for i=1,#list do
208 os.remove(list[i])
209 end
210 end
211 end
212 if not wipe then
213 if remote then
214 where = (urls[remote] or remote) .. "/" .. where
215 end
216 local data = fetched(where)
217 if string.find(data,"^PK") then
218 io.savedata(tmpzipfile,data)
219 report("from %a",where)
220 report("into %a",targetdir)
221 local done = utilities.zipfiles.unzipdir {
222 zipname = tmpzipfile,
223 path = ".",
224 verbose = "steps",
225 collect = true,
226 validate = validate,
227 }
228 table.save(hash,{ name = where, list = done })
229 os.remove(tmpzipfile)
230 else
231 report("unknown %a",where)
232 end
233 end
234 end
235
236 local function wiper(wipes)
237 for i=1,#wipes do
238 local s = wipes[i]
239 if type(s) == "table" then
240 wiper(s)
241 elseif type(s) == "string" then
242 local t = dir.glob(s)
243 report("wiping %i files in %a",#t,s)
244 for i=1,#t do
245
246 os.remove(t[i])
247 end
248 end
249 end
250 end
251
252 if type(wipes) == "table" then
253 wiper(wipes)
254 end
255 end
256end
257
258function scripts.modules.list()
259 loadlists()
260 for k, v in table.sortedhash(lists) do
261 report("%-20s: %-36s : % t",k,urls[v.url],v.zips)
262 end
263end
264
265function scripts.modules.installed()
266 local files = dir.glob(targetdir .. "/*.tma")
267 if files then
268 for i=1,#files do
269 local data = table.load(files[i])
270 if data then
271 local name = data.name
272 local list = data.list
273 if name and list then
274 report("%4i : %s",#list,name)
275 end
276 end
277 end
278 end
279end
280
281function scripts.modules.install(wipe)
282 local curdir = dir.current()
283 local done = false
284 if not lfs.isdir(checkdir) then
285 report("unknown subdirectory %a",checkdir)
286 elseif not dir.mkdirs(targetdir) then
287 report("unable to create %a",targetdir)
288 elseif not lfs.chdir(targetdir) then
289 report("unable to go into %a",targetdir)
290 elseif environment.argument("module") or environment.argument("modules") then
291 local files = environment.files
292 if #files == 0 then
293 report("no archive names provided")
294 else
295 for i=1,#files do
296 local name = files[i]
297 if url.hasscheme(name) then
298 install({ url = false, zips = { file.addsuffix(name,"zip") } }, wipe)
299 else
300 loadlists()
301 install({ url = "modules", zips = { file.addsuffix(name,"zip") } }, wipe)
302 end
303 end
304 done = files
305 end
306 else
307 loadlists()
308 local files = environment.argument("all") and table.sortedkeys(lists) or environment.files
309 if #files == 0 then
310 report("no module names provided")
311 else
312 for i=1,#files do
313 local name = files[i]
314 local list = lists[name]
315 if list then
316 install(list,wipe)
317 end
318 end
319 done = files
320 end
321 end
322 if done then
323
324
325
326
327 local okay = false
328 local files = dir.glob("*")
329 for i=1,#files do
330 local name = files[i]
331 if file.suffix(name) == "tma" then
332
333 else
334 if not okay then
335 report()
336 okay = true
337 end
338 report("removed %a",name)
339 os.remove(name)
340 end
341 end
342
343 report()
344 report("renewing file database")
345 report()
346 resolvers.renewcache()
347 resolvers.load()
348 report()
349 report("installed: % t",done)
350 report()
351 end
352 lfs.chdir(curdir)
353end
354
355function scripts.modules.uninstall()
356 scripts.modules.install(true)
357end
358
359if environment.argument("list") then
360 scripts.modules.list()
361elseif environment.argument("installed") then
362 scripts.modules.installed()
363elseif environment.argument("install") then
364 scripts.modules.install()
365elseif environment.argument("uninstall") then
366 scripts.modules.uninstall()
367elseif environment.argument("exporthelp") then
368 application.export(environment.argument("exporthelp"),environment.files[1])
369else
370 application.help()
371 report("")
372end
373
374 |