Implement Array.splice method
[vadmium-streams.git] / zipstream.py
blob7f8352c8e11926a86e0762222d3897ea5af2ff0a
1 #! /usr/bin/env python3
3 import sys
4 from struct import Struct
5 from streams import streamcopy, DelegateWriter
6 from binascii import crc32
7 from io import BufferedIOBase
9 FILE_SIG = b"PK\x03\x04"
10 FILE_STRUCT = Struct("< BB H H L LLL HH")
11 FLAGS_ENCRYPTED = 0
12 FLAGS_STREAMED = 3
13 FLAGS_STRONG_ENC = 6
14 FLAGS_UTF8 = 11
15 METHOD_STORE = 0
17 def iter_files(zip):
18 while True:
19 sig = zip.read(4)
20 if not sig:
21 break
22 if len(sig) != 4 or not sig.startswith(b"PK"):
23 raise ValueError("Invalid Zip record signature")
24 if sig != FILE_SIG:
25 break
26 yield read_file_header(zip)
28 def read_file_header(zip):
29 (_, _, flags, method, _, crc, comp, uncomp, namelen, extra) = (
30 FILE_STRUCT.unpack(zip.read(FILE_STRUCT.size)))
32 name = zip.read(namelen)
33 if len(name) != namelen:
34 raise EOFError("EOF in file name")
35 encoding = "utf-8" if flags >> FLAGS_UTF8 & 1 else "cp437"
36 name = name.decode(encoding)
38 if flags >> FLAGS_STREAMED & 1:
39 raise NotImplementedError("Data descriptor")
40 return (flags, method, crc, comp, uncomp, name, extra)
42 def extract():
43 zip = sys.stdin.buffer
44 out = sys.stdout.buffer
46 for [flags, method, crc, comp, uncomp, name, extra] in iter_files(zip):
47 if name:
48 print(name, file=sys.stderr)
50 streamcopy(zip, DelegateWriter(), extra)
52 skip = False
53 if flags & (1 << FLAGS_ENCRYPTED | 1 << FLAGS_STRONG_ENC):
54 print("Encrypted file", file=sys.stderr)
55 skip = True
56 if method != METHOD_STORE:
57 print("Compression method {!r}".format(method), file=sys.stderr)
58 skip = True
59 if name.endswith((".txt", ".URL")):
60 skip = True
62 if skip:
63 streamcopy(zip, DelegateWriter(), comp)
64 else:
65 crcwriter = Crc32Writer()
66 streamcopy(zip, DelegateWriter(out, crcwriter), uncomp)
67 if crcwriter.crc != crc:
68 fmt = "Extracted CRC {:08X} != expected CRC {:08X}"
69 print(fmt.format(crcwriter.crc, crc), file=sys.stderr)
71 streamcopy(zip, DelegateWriter(), comp - uncomp)
73 class Crc32Writer(BufferedIOBase):
74 def __init__(self):
75 self.crc = 0
76 def write(self, b):
77 self.crc = crc32(b, self.crc)
79 if __name__ == "__main__":
80 extract()