util-zip.lua /size: 19 Kb    last modification: 2020-07-01 14:35
1
if
not
modules
then
modules
=
{
}
end
modules
[
'
util-zip
'
]
=
{
2
version
=
1
.
001
,
3
author
=
"
Hans Hagen, PRAGMA-ADE, Hasselt NL
"
,
4
copyright
=
"
PRAGMA ADE / ConTeXt Development Team
"
,
5
license
=
"
see context related readme files
"
6
}
7 8
-- This module is mostly meant for relative simple zip and unzip tasks. We can read
9
-- and write zip files but with limitations. Performance is quite good and it makes
10
-- us independent of zip tools, which (for some reason) are not always installed.
11
--
12
-- This is an lmtx module and at some point will be lmtx only but for a while we
13
-- keep some hybrid functionality.
14 15
local
type
,
tostring
,
tonumber
=
type
,
tostring
,
tonumber
16
local
sort
=
table
.
sort
17 18
local
find
,
format
,
sub
,
gsub
=
string
.
find
,
string
.
format
,
string
.
sub
,
string
.
gsub
19
local
osdate
,
ostime
,
osclock
=
os
.
date
,
os
.
time
,
os
.
clock
20
local
ioopen
=
io
.
open
21
local
loaddata
,
savedata
=
io
.
loaddata
,
io
.
savedata
22
local
filejoin
,
isdir
,
dirname
,
mkdirs
=
file
.
join
,
lfs
.
isdir
,
file
.
dirname
,
dir
.
mkdirs
23 24
local
files
=
utilities
.
files
25
local
openfile
=
files
.
open
26
local
closefile
=
files
.
close
27
local
readstring
=
files
.
readstring
28
local
readcardinal2
=
files
.
readcardinal2le
29
local
readcardinal4
=
files
.
readcardinal4le
30
local
setposition
=
files
.
setposition
31
local
getposition
=
files
.
getposition
32 33
local
band
=
bit32
.
band
34
local
rshift
=
bit32
.
rshift
35
local
lshift
=
bit32
.
lshift
36 37
local
decompress
,
expandsize
,
calculatecrc
38 39
-- if flate then
40
--
41
-- decompress = flate.flate_decompress
42
-- calculatecrc = flate.update_crc32
43
--
44
-- else
45 46
local
zlibdecompress
=
zlib
.
decompress
47
local
zlibexpandsize
=
zlib
.
expandsize
48
local
zlibchecksum
=
zlib
.
crc32
49 50
decompress
=
function
(
source
)
51
return
zlibdecompress
(
source
,
-15
)
-- auto
52
end
53 54
expandsize
=
zlibexpandsize
and
function
(
source
,
targetsize
)
55
return
zlibexpandsize
(
source
,
targetsize
,
-15
)
-- auto
56
end
or
decompress
57 58
calculatecrc
=
function
(
buffer
,
initial
)
59
return
zlibchecksum
(
initial
or
0
,
buffer
)
60
end
61 62
-- end
63 64
local
zipfiles
=
{
}
65
utilities
.
zipfiles
=
zipfiles
66 67
local
openzipfile
,
closezipfile
,
unzipfile
,
foundzipfile
,
getziphash
,
getziplist
do
68 69
function
openzipfile
(
name
)
70
return
{
71
name
=
name
,
72
handle
=
openfile
(
name
,
0
)
,
73
}
74
end
75 76
local
function
collect
(
z
)
77
if
not
z
.
list
then
78
local
list
=
{
}
79
local
hash
=
{
}
80
local
position
=
0
81
local
index
=
0
82
local
handle
=
z
.
handle
83
while
true
do
84
setposition
(
handle
,
position
)
85
local
signature
=
readstring
(
handle
,
4
)
86
if
signature
=
=
"
PK\3\4
"
then
87
-- [local file header 1]
88
-- [encryption header 1]
89
-- [file data 1]
90
-- [data descriptor 1]
91
local
version
=
readcardinal2
(
handle
)
92
local
flag
=
readcardinal2
(
handle
)
93
local
method
=
readcardinal2
(
handle
)
94
local
filetime
=
readcardinal2
(
handle
)
95
local
filedate
=
readcardinal2
(
handle
)
96
local
crc32
=
readcardinal4
(
handle
)
97
local
compressed
=
readcardinal4
(
handle
)
98
local
uncompressed
=
readcardinal4
(
handle
)
99
local
namelength
=
readcardinal2
(
handle
)
100
local
extralength
=
readcardinal2
(
handle
)
101
local
filename
=
readstring
(
handle
,
namelength
)
102
local
descriptor
=
band
(
flag
,
8
)
~
=
0
103
local
encrypted
=
band
(
flag
,
1
)
~
=
0
104
local
acceptable
=
method
=
=
0
or
method
=
=
8
105
-- 30 bytes of header including the signature
106
local
skipped
=
0
107
local
size
=
0
108
if
encrypted
then
109
size
=
readcardinal2
(
handle
)
110
skipbytes
(
size
)
111
skipped
=
skipped
+
size
+
2
112
skipbytes
(
8
)
113
skipped
=
skipped
+
8
114
size
=
readcardinal2
(
handle
)
115
skipbytes
(
size
)
116
skipped
=
skipped
+
size
+
2
117
size
=
readcardinal4
(
handle
)
118
skipbytes
(
size
)
119
skipped
=
skipped
+
size
+
4
120
size
=
readcardinal2
(
handle
)
121
skipbytes
(
size
)
122
skipped
=
skipped
+
size
+
2
123
end
124
position
=
position
+
30
+
namelength
+
extralength
+
skipped
125
if
descriptor
then
126
setposition
(
handle
,
position
+
compressed
)
127
crc32
=
readcardinal4
(
handle
)
128
compressed
=
readcardinal4
(
handle
)
129
uncompressed
=
readcardinal4
(
handle
)
130
end
131
if
acceptable
then
132
index
=
index
+
1
133
local
data
=
{
134
filename
=
filename
,
135
index
=
index
,
136
position
=
position
,
137
method
=
method
,
138
compressed
=
compressed
,
139
uncompressed
=
uncompressed
,
140
crc32
=
crc32
,
141
encrypted
=
encrypted
,
142
}
143
hash
[
filename
]
=
data
144
list
[
index
]
=
data
145
else
146
-- maybe a warning when encrypted
147
end
148
position
=
position
+
compressed
149
else
150
break
151
end
152
z
.
list
=
list
153
z
.
hash
=
hash
154
end
155
end
156
end
157 158
function
getziplist
(
z
)
159
local
list
=
z
.
list
160
if
not
list
then
161
collect
(
z
)
162
end
163
return
z
.
list
164
end
165 166
function
getziphash
(
z
)
167
local
hash
=
z
.
hash
168
if
not
hash
then
169
collect
(
z
)
170
end
171
return
z
.
hash
172
end
173 174
function
foundzipfile
(
z
,
name
)
175
return
getziphash
(
z
)
[
name
]
176
end
177 178
function
closezipfile
(
z
)
179
local
f
=
z
.
handle
180
if
f
then
181
closefile
(
f
)
182
z
.
handle
=
nil
183
end
184
end
185 186
function
unzipfile
(
z
,
filename
,
check
)
187
local
hash
=
z
.
hash
188
if
not
hash
then
189
hash
=
zipfiles
.
hash
(
z
)
190
end
191
local
data
=
hash
[
filename
]
-- normalize
192
if
not
data
then
193
-- lower and cleanup
194
-- only name
195
end
196
if
data
then
197
local
handle
=
z
.
handle
198
local
position
=
data
.
position
199
local
compressed
=
data
.
compressed
200
if
compressed
>
0
then
201
setposition
(
handle
,
position
)
202
local
result
=
readstring
(
handle
,
compressed
)
203
if
data
.
method
=
=
8
then
204
if
expandsize
then
205
result
=
expandsize
(
result
,
data
.
uncompressed
)
206
else
207
result
=
decompress
(
result
)
208
end
209
end
210
if
check
and
data
.
crc32
~
=
calculatecrc
(
result
)
then
211
print
(
"
checksum mismatch
"
)
212
return
"
"
213
end
214
return
result
215
else
216
return
"
"
217
end
218
end
219
end
220 221
zipfiles
.
open
=
openzipfile
222
zipfiles
.
close
=
closezipfile
223
zipfiles
.
unzip
=
unzipfile
224
zipfiles
.
hash
=
getziphash
225
zipfiles
.
list
=
getziplist
226
zipfiles
.
found
=
foundzipfile
227 228
end
229 230
if
xzip
then
-- flate then do
231 232
local
writecardinal1
=
files
.
writebyte
233
local
writecardinal2
=
files
.
writecardinal2le
234
local
writecardinal4
=
files
.
writecardinal4le
235 236
local
logwriter
=
logs
.
writer
237 238
local
globpattern
=
dir
.
globpattern
239
-- local compress = flate.flate_compress
240
-- local checksum = flate.update_crc32
241
local
compress
=
xzip
.
compress
242
local
checksum
=
xzip
.
crc32
243 244
-- local function fromdostime(dostime,dosdate)
245
-- return ostime {
246
-- year = (dosdate >> 9) + 1980, -- 25 .. 31
247
-- month = (dosdate >> 5) & 0x0F, -- 21 .. 24
248
-- day = (dosdate ) & 0x1F, -- 16 .. 20
249
-- hour = (dostime >> 11) , -- 11 .. 15
250
-- min = (dostime >> 5) & 0x3F, -- 5 .. 10
251
-- sec = (dostime ) & 0x1F, -- 0 .. 4
252
-- }
253
-- end
254
--
255
-- local function todostime(time)
256
-- local t = osdate("*t",time)
257
-- return
258
-- ((t.year - 1980) << 9) + (t.month << 5) + t.day,
259
-- (t.hour << 11) + (t.min << 5) + (t.sec >> 1)
260
-- end
261 262
local
function
fromdostime
(
dostime
,
dosdate
)
263
return
ostime
{
264
year
=
rshift
(
dosdate
,
9
)
+
1980
,
-- 25 .. 31
265
month
=
band
(
rshift
(
dosdate
,
5
)
,
0x0F
)
,
-- 21 .. 24
266
day
=
band
(
(
dosdate
)
,
0x1F
)
,
-- 16 .. 20
267
hour
=
band
(
rshift
(
dostime
,
11
)
)
,
-- 11 .. 15
268
min
=
band
(
rshift
(
dostime
,
5
)
,
0x3F
)
,
-- 5 .. 10
269
sec
=
band
(
(
dostime
)
,
0x1F
)
,
-- 0 .. 4
270
}
271
end
272 273
local
function
todostime
(
time
)
274
local
t
=
osdate
(
"
*t
"
,
time
)
275
return
276
lshift
(
t
.
year
-
1980
,
9
)
+
lshift
(
t
.
month
,
5
)
+
t
.
day
,
277
lshift
(
t
.
hour
,
11
)
+
lshift
(
t
.
min
,
5
)
+
rshift
(
t
.
sec
,
1
)
278
end
279 280
local
function
openzip
(
filename
,
level
,
comment
,
verbose
)
281
local
f
=
ioopen
(
filename
,
"
wb
"
)
282
if
f
then
283
return
{
284
filename
=
filename
,
285
handle
=
f
,
286
list
=
{
}
,
287
level
=
tonumber
(
level
)
or
3
,
288
comment
=
tostring
(
comment
)
,
289
verbose
=
verbose
,
290
uncompressed
=
0
,
291
compressed
=
0
,
292
}
293
end
294
end
295 296
local
function
writezip
(
z
,
name
,
data
,
level
,
time
)
297
local
f
=
z
.
handle
298
local
list
=
z
.
list
299
local
level
=
tonumber
(
level
)
or
z
.
level
or
3
300
local
method
=
8
301
local
zipped
=
compress
(
data
,
level
)
302
local
checksum
=
checksum
(
data
)
303
local
verbose
=
z
.
verbose
304
--
305
if
not
zipped
then
306
method
=
0
307
zipped
=
data
308
end
309
--
310
local
start
=
f
:
seek
(
)
311
local
compressed
=
#
zipped
312
local
uncompressed
=
#
data
313
--
314
z
.
compressed
=
z
.
compressed
+
compressed
315
z
.
uncompressed
=
z
.
uncompressed
+
uncompressed
316
--
317
if
verbose
then
318
local
pct
=
100
*
compressed
/
uncompressed
319
if
pct
>
=
100
then
320
logwriter
(
format
(
"
%10i %s
"
,
uncompressed
,
name
)
)
321
else
322
logwriter
(
format
(
"
%10i %02.1f %s
"
,
uncompressed
,
pct
,
name
)
)
323
end
324
end
325
--
326
f
:
write
(
"
\x50\x4b\x03\x04
"
)
-- PK.. 0x04034b50
327
--
328
writecardinal2
(
f
,
0
)
-- minimum version
329
writecardinal2
(
f
,
0
)
-- flag
330
writecardinal2
(
f
,
method
)
-- method
331
writecardinal2
(
f
,
0
)
-- time
332
writecardinal2
(
f
,
0
)
-- date
333
writecardinal4
(
f
,
checksum
)
-- crc32
334
writecardinal4
(
f
,
compressed
)
-- compressed
335
writecardinal4
(
f
,
uncompressed
)
-- uncompressed
336
writecardinal2
(
f
,
#
name
)
-- namelength
337
writecardinal2
(
f
,
0
)
-- extralength
338
--
339
f
:
write
(
name
)
-- name
340
f
:
write
(
zipped
)
341
--
342
list
[
#
list
+
1
]
=
{
#
zipped
,
#
data
,
name
,
checksum
,
start
,
time
or
0
}
343
end
344 345
local
function
closezip
(
z
)
346
local
f
=
z
.
handle
347
local
list
=
z
.
list
348
local
comment
=
z
.
comment
349
local
verbose
=
z
.
verbose
350
local
count
=
#
list
351
local
start
=
f
:
seek
(
)
352
--
353
for
i
=
1
,
count
do
354
local
l
=
list
[
i
]
355
local
compressed
=
l
[
1
]
356
local
uncompressed
=
l
[
2
]
357
local
name
=
l
[
3
]
358
local
checksum
=
l
[
4
]
359
local
start
=
l
[
5
]
360
local
time
=
l
[
6
]
361
local
date
,
time
=
todostime
(
time
)
362
f
:
write
(
'
\x50\x4b\x01\x02
'
)
363
writecardinal2
(
f
,
0
)
-- version made by
364
writecardinal2
(
f
,
0
)
-- version needed to extract
365
writecardinal2
(
f
,
0
)
-- flags
366
writecardinal2
(
f
,
8
)
-- method
367
writecardinal2
(
f
,
time
)
-- time
368
writecardinal2
(
f
,
date
)
-- date
369
writecardinal4
(
f
,
checksum
)
-- crc32
370
writecardinal4
(
f
,
compressed
)
-- compressed
371
writecardinal4
(
f
,
uncompressed
)
-- uncompressed
372
writecardinal2
(
f
,
#
name
)
-- namelength
373
writecardinal2
(
f
,
0
)
-- extralength
374
writecardinal2
(
f
,
0
)
-- commentlength
375
writecardinal2
(
f
,
0
)
-- nofdisks -- ?
376
writecardinal2
(
f
,
0
)
-- internal attr (type)
377
writecardinal4
(
f
,
0
)
-- external attr (mode)
378
writecardinal4
(
f
,
start
)
-- local offset
379
f
:
write
(
name
)
-- name
380
end
381
--
382
local
stop
=
f
:
seek
(
)
383
local
size
=
stop
-
start
384
--
385
f
:
write
(
'
\x50\x4b\x05\x06
'
)
386
writecardinal2
(
f
,
0
)
-- disk
387
writecardinal2
(
f
,
0
)
-- disks
388
writecardinal2
(
f
,
count
)
-- entries
389
writecardinal2
(
f
,
count
)
-- entries
390
writecardinal4
(
f
,
size
)
-- dir size
391
writecardinal4
(
f
,
start
)
-- dir offset
392
if
type
(
comment
)
=
=
"
string
"
and
comment
~
=
"
"
then
393
writecardinal2
(
f
,
#
comment
)
-- comment length
394
f
:
write
(
comment
)
-- comemnt
395
else
396
writecardinal2
(
f
,
0
)
397
end
398
--
399
if
verbose
then
400
local
compressed
=
z
.
compressed
401
local
uncompressed
=
z
.
uncompressed
402
local
filename
=
z
.
filename
403
--
404
local
pct
=
100
*
compressed
/
uncompressed
405
logwriter
(
"
"
)
406
if
pct
>
=
100
then
407
logwriter
(
format
(
"
%10i %s
"
,
uncompressed
,
filename
)
)
408
else
409
logwriter
(
format
(
"
%10i %02.1f %s
"
,
uncompressed
,
pct
,
filename
)
)
410
end
411
end
412
--
413
f
:
close
(
)
414
end
415 416
local
function
zipdir
(
zipname
,
path
,
level
,
verbose
)
417
if
type
(
zipname
)
=
=
"
table
"
then
418
verbose
=
zipname
.
verbose
419
level
=
zipname
.
level
420
path
=
zipname
.
path
421
zipname
=
zipname
.
zipname
422
end
423
if
not
zipname
or
zipname
=
=
"
"
then
424
return
425
end
426
if
not
path
or
path
=
=
"
"
then
427
path
=
"
.
"
428
end
429
if
not
isdir
(
path
)
then
430
return
431
end
432
path
=
gsub
(
path
,
"
\\+
"
,
"
/
"
)
433
path
=
gsub
(
path
,
"
/+
"
,
"
/
"
)
434
local
list
=
{
}
435
local
count
=
0
436
globpattern
(
path
,
"
"
,
true
,
function
(
name
,
size
,
time
)
437
count
=
count
+
1
438
list
[
count
]
=
{
name
,
time
}
439
end
)
440
sort
(
list
,
function
(
a
,
b
)
441
return
a
[
1
]
<
b
[
1
]
442
end
)
443
local
zipf
=
openzip
(
zipname
,
level
,
comment
,
verbose
)
444
if
zipf
then
445
local
p
=
#
path
+
2
446
for
i
=
1
,
count
do
447
local
li
=
list
[
i
]
448
local
name
=
li
[
1
]
449
local
time
=
li
[
2
]
450
local
data
=
loaddata
(
name
)
451
local
name
=
sub
(
name
,
p
,
#
name
)
452
writezip
(
zipf
,
name
,
data
,
level
,
time
,
verbose
)
453
end
454
closezip
(
zipf
)
455
end
456
end
457 458
local
function
unzipdir
(
zipname
,
path
,
verbose
)
459
if
type
(
zipname
)
=
=
"
table
"
then
460
verbose
=
zipname
.
verbose
461
path
=
zipname
.
path
462
zipname
=
zipname
.
zipname
463
end
464
if
not
zipname
or
zipname
=
=
"
"
then
465
return
466
end
467
if
not
path
or
path
=
=
"
"
then
468
path
=
"
.
"
469
end
470
local
z
=
openzipfile
(
zipname
)
471
if
z
then
472
local
list
=
getziplist
(
z
)
473
if
list
then
474
local
total
=
0
475
local
count
=
#
list
476
local
step
=
number
.
idiv
(
count
,
10
)
477
local
done
=
0
478
local
steps
=
verbose
=
=
"
steps
"
479
local
time
=
steps
and
osclock
(
)
480
for
i
=
1
,
count
do
481
local
l
=
list
[
i
]
482
local
n
=
l
.
filename
483
local
d
=
unzipfile
(
z
,
n
)
-- true for check
484
if
d
then
485
local
p
=
filejoin
(
path
,
n
)
486
if
mkdirs
(
dirname
(
p
)
)
then
487
if
steps
then
488
total
=
total
+
#
d
489
done
=
done
+
1
490
if
done
>
=
step
then
491
done
=
0
492
logwriter
(
format
(
"
%4i files of %4i done, %10i bytes, %0.3f seconds
"
,
i
,
count
,
total
,
osclock
(
)
-
time
)
)
493
end
494
elseif
verbose
then
495
logwriter
(
n
)
496
end
497
savedata
(
p
,
d
)
498
end
499
else
500
logwriter
(
format
(
"
problem with file %s
"
,
n
)
)
501
end
502
end
503
if
steps
then
504
logwriter
(
format
(
"
%4i files of %4i done, %10i bytes, %0.3f seconds
"
,
count
,
count
,
total
,
osclock
(
)
-
time
)
)
505
end
506
closezipfile
(
z
)
507
return
true
508
else
509
closezipfile
(
z
)
510
end
511
end
512
end
513 514
zipfiles
.
zipdir
=
zipdir
515
zipfiles
.
unzipdir
=
unzipdir
516 517
end
518 519
zipfiles
.
gunzipfile
=
gzip
.
load
520 521
-- if flate then
522
--
523
-- local streams = utilities.streams
524
-- local openfile = streams.open
525
-- local closestream = streams.close
526
-- local setposition = streams.setposition
527
-- local getsize = streams.size
528
-- local readcardinal4 = streams.readcardinal4le
529
-- local getstring = streams.getstring
530
-- local decompress = flate.gz_decompress
531
--
532
-- -- id1=1 id2=1 method=1 flags=1 mtime=4(le) extra=1 os=1
533
-- -- flags:8 comment=...<nul> flags:4 name=...<nul> flags:2 extra=...<nul> flags:1 crc=2
534
-- -- data:?
535
-- -- crc=4 size=4
536
--
537
-- function zipfiles.gunzipfile(filename)
538
-- local strm = openfile(filename)
539
-- if strm then
540
-- setposition(strm,getsize(strm) - 4 + 1)
541
-- local size = readcardinal4(strm)
542
-- local data = decompress(getstring(strm),size)
543
-- closestream(strm)
544
-- return data
545
-- end
546
-- end
547
--
548
-- elseif gzip then
549
--
550
-- local openfile = gzip.open
551
--
552
-- function zipfiles.gunzipfile(filename)
553
-- local g = openfile(filename,"rb")
554
-- if g then
555
-- local d = g:read("*a")
556
-- d:close()
557
-- return d
558
-- end
559
-- end
560
--
561
-- end
562 563
return
zipfiles
564