1if not modules then modules = { } end modules ['util-tar'] = {
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 type, tonumber = type, tonumber
10local gsub, escapedpattern = string.gsub, string.escapedpattern
11local nameonly, dirname, makedirs = file.nameonly, file.dirname, dir.makedirs
12local savedata = io.savedata
13local newreader = io.newreader
14
15local report = logs.reporter("tar")
16
17local types = {
18 ["0"] = "file",
19 ["\0"] = "regular",
20 ["1"] = "link",
21 ["2"] = "symbolic",
22 ["3"] = "character",
23 ["4"] = "block",
24 ["5"] = "directory",
25 ["6"] = "fifo",
26 ["7"] = "continuation",
27 ["x"] = "extended",
28}
29
30local function asstring(str)
31 return str and gsub(str,"[\0 ]+$","") or nil
32end
33
34local function asnumber(str)
35 str = gsub(str,"\0$","")
36 return tonumber(str,8)
37end
38
39local function opentar(whatever,filename)
40 local f = newreader(filename,whatever)
41 if f then
42 f.metadata = {
43 nofpaths = 0,
44 noffiles = 0,
45 noflinks = 0,
46 nofbytes = 0,
47 }
48 return f
49 end
50end
51
52local function readheader(t)
53
54 local p = t:getposition()
55 local h = t:readbytetable(512)
56 t:setposition(p)
57 for i=149,156 do
58 h[i] = 0
59 end
60 local c = 256
61 for i=1,512 do
62 c = c + h[i]
63 end
64
65 local header = {
66 name = asstring(t:readstring(100)),
67 mode = asnumber(t:readstring( 8)),
68 uid = asnumber(t:readstring( 8)),
69 gid = asnumber(t:readstring( 8)),
70 size = asnumber(t:readstring( 12)),
71 mtime = asnumber(t:readstring( 12)),
72 checksum = asnumber(t:readstring( 6)),
73 dummy = t:skip (2) ,
74 typeflag = t:readstring( 1) ,
75 linkname = asstring(t:readstring(100)),
76
77
78
79
80
81
82
83 padding = t:skip (255) ,
84 }
85 local typeflag = header.typeflag
86 if typeflag then
87 header.filetype = types[typeflag]
88 if c == header.checksum then
89 return header
90 end
91 end
92end
93
94local readers = {
95
96 directory = function(t,h)
97 local metadata = t.metadata
98 local filename = h.name
99 if metadata.verbose then
100 report("%8s %s","",filename)
101 end
102 metadata.nofpaths = metadata.nofpaths + 1
103 return true
104 end,
105
106 file = function(t,h)
107 local metadata = t.metadata
108 local filename = h.name
109 local filesize = h.size
110 local pathname = dirname(filename)
111 if metadata.verbose then
112 report("% 8i : %s",filesize,filename)
113 end
114 if makedirs(pathname) then
115 savedata(filename,t:readstring(filesize))
116 else
117 t.skip(filesize)
118 end
119 local position = t:getposition()
120 local target = position + (512 - position % 512) % 512
121 t:setposition(target)
122 metadata.noffiles = metadata.noffiles + 1
123 metadata.nofbytes = metadata.nofbytes + filesize
124 return true
125 end,
126
127 symbolic = function(t,h)
128 local metadata = t.metadata
129 local filename = h.name
130 local linkname = h.linkname
131 if metadata.verbose then
132 report("%8s %s => %s","",linkname,filename)
133 end
134 metadata.noflinks = metadata.noflinks + 1
135 return true
136 end,
137
138}
139
140local skippers = {
141
142 directory = function(t,h)
143 return true
144 end,
145
146 file = function(t,h)
147 local filesize = h.size
148 local fileoffset = t:getposition()
149 local position = filesize + fileoffset
150 local target = position + (512 - position % 512) % 512
151 t:setposition(target)
152 return fileoffset
153 end,
154
155 symbolic = function(t,h)
156 return true
157 end,
158
159}
160
161local writers = {
162
163}
164
165local function saveheader(t,h)
166 local filetype = h.filetype
167 local reader = readers[filetype]
168 if reader then
169 return filetype, reader(t,h)
170 else
171 report("no reader for %s",filetype)
172 end
173end
174
175local function skipheader(t,h)
176 local filetype = h.filetype
177 local skipper = skippers[filetype]
178 if skipper then
179 return filetype, skipper(t,h)
180 else
181 report("no skipper for %s",filetype)
182 end
183end
184
185local function unpacktar(whatever,filename,verbose)
186 local t = opentar(whatever,filename)
187 if t then
188 local metadata = t.metadata
189 statistics.starttiming(metadata)
190 if verbose then
191 if whatever == "string" then
192 report("unpacking: %i bytes",#filename)
193 else
194 report("unpacking: %s",filename)
195 end
196 report("")
197 metadata.verbose = verbose
198 end
199 while true do
200 local h = readheader(t)
201 if not h then
202 break
203 else
204 local filetype, saved = saveheader(t,h)
205 if not saved then
206 break
207 end
208 end
209 end
210 statistics.stoptiming(metadata)
211 metadata.runtime = statistics.elapsed(metadata)
212 if verbose then
213 report("")
214 report("number of paths : %i",metadata.nofpaths)
215 report("number of files : %i",metadata.noffiles)
216 report("number of links : %i",metadata.noflinks)
217 report("number of bytes : %i",metadata.nofbytes)
218 report("")
219 report("runtime needed : %s",statistics.elapsedseconds(metadata))
220 report("")
221 end
222 t.close()
223 return metadata
224 end
225end
226
227local function listtar(whatever,filename,onlyfiles)
228 local t = opentar(whatever,filename)
229 if t then
230 local list, n = { }, 0
231 while true do
232 local h = readheader(t)
233 if not h then
234 break
235 else
236 local filetype, offset = skipheader(t,h)
237 if not offset then
238 break
239 elseif filetype == "file" then
240 n = n + 1 ; list[n] = { filetype, h.name, h.size }
241 elseif filetype == "link" then
242 n = n + 1 ; list[n] = { filetype, h.name, h.linkfile }
243 elseif not onlyfiles then
244 n = n + 1 ; list[n] = { filetype, h.name }
245 end
246 end
247 end
248 t.close()
249
250 table.sort(list,function(a,b) return a[2] < b[2] end)
251 return list
252 end
253end
254
255local function hashtar(whatever,filename,strip)
256 local t = opentar(whatever,filename)
257 if t then
258 local list = { }
259 if strip then
260 strip = "^" .. escapedpattern(nameonly(nameonly(strip))) .. "/"
261 end
262 while true do
263 local h = readheader(t)
264 if not h then
265 break
266 else
267 local filetype, offset = skipheader(t,h)
268 if not offset then
269 break
270 else
271 local name = h.name
272 if strip then
273 name = gsub(name,strip,"")
274 end
275 if filetype == "file" then
276 list[name] = { offset, h.size }
277 elseif filetype == "link" then
278 list[name] = h.linkname
279 end
280 end
281 end
282 end
283 t.close()
284 return list
285 end
286end
287
288
289
290local function fetchtar(whatever,archive,filename,list)
291 if not list then
292 list = hashtar(whatever,archive)
293 end
294 if list then
295 local what = list[filename]
296 if type(what) == "string" then
297 what = list[what]
298 end
299 if what then
300 local t = opentar(whatever,archive)
301 if t then
302 t:setposition(what[1])
303 return t:readstring(what[2])
304 end
305 end
306 end
307end
308
309local function packtar(whatever,filename,verbose)
310 report("packing will be implemented when we need it")
311end
312
313local tar = {
314 files = {
315 unpack = function(...) return unpacktar("file", ...) end,
316 pack = function(...) return packtar ("file", ...) end,
317 list = function(...) return listtar ("file", ...) end,
318 hash = function(...) return hashtar ("file", ...) end,
319 fetch = function(...) return fetchtar ("file", ...) end,
320 },
321 strings = {
322 unpack = function(...) return unpacktar("string",...) end,
323 pack = function(...) return packtar ("string",...) end,
324 list = function(...) return listtar ("string",...) end,
325 hash = function(...) return hashtar ("string",...) end,
326 fetch = function(...) return fetchtar ("string",...) end,
327 },
328 streams = {
329 unpack = function(...) return unpacktar("stream",...) end,
330 pack = function(...) return packtar ("stream",...) end,
331 list = function(...) return listtar ("stream",...) end,
332 hash = function(...) return hashtar ("stream",...) end,
333 fetch = function(...) return fetchtar ("stream",...) end,
334 },
335}
336
337utilities.tar = tar
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359return tar
360 |