blob: 2672d0a90ede30f0169fb9344e9980200ebb6964 [file] [log] [blame]
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001"""
2Read and write ZIP files.
Guido van Rossumd6ca5462007-05-22 01:29:33 +00003
4XXX references to utf-8 need further investigation.
Thomas Wouters0e3f5912006-08-11 14:57:12 +00005"""
Christian Heimes790c8232008-01-07 21:14:23 +00006import struct, os, time, sys, shutil
Guido van Rossum68937b42007-05-18 00:51:22 +00007import binascii, io
Guido van Rossum32abe6f2000-03-31 17:30:02 +00008
9try:
Tim Peterse1190062001-01-15 03:34:38 +000010 import zlib # We may need its compression method
Christian Heimesd5e2b6f2008-03-19 21:50:51 +000011 crc32 = zlib.crc32
Guido van Rossum9c673f32001-04-10 15:37:12 +000012except ImportError:
Guido van Rossum32abe6f2000-03-31 17:30:02 +000013 zlib = None
Christian Heimesd5e2b6f2008-03-19 21:50:51 +000014 crc32 = binascii.crc32
Guido van Rossum32abe6f2000-03-31 17:30:02 +000015
Skip Montanaro40fc1602001-03-01 04:27:19 +000016__all__ = ["BadZipfile", "error", "ZIP_STORED", "ZIP_DEFLATED", "is_zipfile",
Thomas Wouters0e3f5912006-08-11 14:57:12 +000017 "ZipInfo", "ZipFile", "PyZipFile", "LargeZipFile" ]
Skip Montanaro40fc1602001-03-01 04:27:19 +000018
Fred Drake5db246d2000-09-29 20:44:48 +000019class BadZipfile(Exception):
Guido van Rossum32abe6f2000-03-31 17:30:02 +000020 pass
Thomas Wouters0e3f5912006-08-11 14:57:12 +000021
22
23class LargeZipFile(Exception):
24 """
25 Raised when writing a zipfile, the zipfile requires ZIP64 extensions
26 and those extensions are disabled.
27 """
28
Tim Peterse1190062001-01-15 03:34:38 +000029error = BadZipfile # The exception raised by this module
Guido van Rossum32abe6f2000-03-31 17:30:02 +000030
Thomas Wouters0e3f5912006-08-11 14:57:12 +000031ZIP64_LIMIT= (1 << 31) - 1
32
Guido van Rossum32abe6f2000-03-31 17:30:02 +000033# constants for Zip file compression methods
34ZIP_STORED = 0
35ZIP_DEFLATED = 8
36# Other ZIP compression methods not supported
37
38# Here are some struct module formats for reading headers
Christian Heimesfdab48e2008-01-20 09:06:41 +000039structEndArchive = "<4s4H2LH" # 9 items, end of archive, 22 bytes
Guido van Rossumd6ca5462007-05-22 01:29:33 +000040stringEndArchive = b"PK\005\006" # magic number for end of archive record
Christian Heimesd5e2b6f2008-03-19 21:50:51 +000041structCentralDir = "<4s4B4HLLL5HLL"# 19 items, central directory, 46 bytes
Guido van Rossumd6ca5462007-05-22 01:29:33 +000042stringCentralDir = b"PK\001\002" # magic number for central directory
Christian Heimesd5e2b6f2008-03-19 21:50:51 +000043structFileHeader = "<4s2B4HLLL2H" # 12 items, file header record, 30 bytes
Guido van Rossumd6ca5462007-05-22 01:29:33 +000044stringFileHeader = b"PK\003\004" # magic number for file header
Christian Heimesd5e2b6f2008-03-19 21:50:51 +000045structEndArchive64Locator = "<4sLQL" # 4 items, locate Zip64 header, 20 bytes
Guido van Rossumd6ca5462007-05-22 01:29:33 +000046stringEndArchive64Locator = b"PK\x06\x07" # magic token for locator header
Christian Heimesd5e2b6f2008-03-19 21:50:51 +000047structEndArchive64 = "<4sQHHLLQQQQ" # 10 items, end of archive (Zip64), 56 bytes
Guido van Rossumd6ca5462007-05-22 01:29:33 +000048stringEndArchive64 = b"PK\x06\x06" # magic token for Zip64 header
Thomas Wouters0e3f5912006-08-11 14:57:12 +000049
Guido van Rossum32abe6f2000-03-31 17:30:02 +000050
Fred Drake3e038e52001-02-28 17:56:26 +000051# indexes of entries in the central directory structure
52_CD_SIGNATURE = 0
53_CD_CREATE_VERSION = 1
54_CD_CREATE_SYSTEM = 2
55_CD_EXTRACT_VERSION = 3
56_CD_EXTRACT_SYSTEM = 4 # is this meaningful?
57_CD_FLAG_BITS = 5
58_CD_COMPRESS_TYPE = 6
59_CD_TIME = 7
60_CD_DATE = 8
61_CD_CRC = 9
62_CD_COMPRESSED_SIZE = 10
63_CD_UNCOMPRESSED_SIZE = 11
64_CD_FILENAME_LENGTH = 12
65_CD_EXTRA_FIELD_LENGTH = 13
66_CD_COMMENT_LENGTH = 14
67_CD_DISK_NUMBER_START = 15
68_CD_INTERNAL_FILE_ATTRIBUTES = 16
69_CD_EXTERNAL_FILE_ATTRIBUTES = 17
70_CD_LOCAL_HEADER_OFFSET = 18
71
72# indexes of entries in the local file header structure
73_FH_SIGNATURE = 0
74_FH_EXTRACT_VERSION = 1
75_FH_EXTRACT_SYSTEM = 2 # is this meaningful?
76_FH_GENERAL_PURPOSE_FLAG_BITS = 3
77_FH_COMPRESSION_METHOD = 4
78_FH_LAST_MOD_TIME = 5
79_FH_LAST_MOD_DATE = 6
80_FH_CRC = 7
81_FH_COMPRESSED_SIZE = 8
82_FH_UNCOMPRESSED_SIZE = 9
83_FH_FILENAME_LENGTH = 10
84_FH_EXTRA_FIELD_LENGTH = 11
85
Guido van Rossum32abe6f2000-03-31 17:30:02 +000086def is_zipfile(filename):
Martin v. Löwis6f6873b2002-10-13 13:54:50 +000087 """Quickly see if file is a ZIP file by checking the magic number."""
Guido van Rossum32abe6f2000-03-31 17:30:02 +000088 try:
Guido van Rossumd6ca5462007-05-22 01:29:33 +000089 fpin = io.open(filename, "rb")
Martin v. Löwis6f6873b2002-10-13 13:54:50 +000090 endrec = _EndRecData(fpin)
Guido van Rossum32abe6f2000-03-31 17:30:02 +000091 fpin.close()
Martin v. Löwis6f6873b2002-10-13 13:54:50 +000092 if endrec:
Guido van Rossum8ca162f2002-04-07 06:36:23 +000093 return True # file has correct magic number
Fred Drake7e473802001-05-11 19:52:57 +000094 except IOError:
Guido van Rossum32abe6f2000-03-31 17:30:02 +000095 pass
Guido van Rossum8ca162f2002-04-07 06:36:23 +000096 return False
Guido van Rossum32abe6f2000-03-31 17:30:02 +000097
Thomas Wouters0e3f5912006-08-11 14:57:12 +000098def _EndRecData64(fpin, offset, endrec):
99 """
100 Read the ZIP64 end-of-archive records and use that to update endrec
101 """
102 locatorSize = struct.calcsize(structEndArchive64Locator)
103 fpin.seek(offset - locatorSize, 2)
104 data = fpin.read(locatorSize)
105 sig, diskno, reloff, disks = struct.unpack(structEndArchive64Locator, data)
106 if sig != stringEndArchive64Locator:
107 return endrec
108
109 if diskno != 0 or disks != 1:
110 raise BadZipfile("zipfiles that span multiple disks are not supported")
111
112 # Assume no 'zip64 extensible data'
113 endArchiveSize = struct.calcsize(structEndArchive64)
114 fpin.seek(offset - locatorSize - endArchiveSize, 2)
115 data = fpin.read(endArchiveSize)
116 sig, sz, create_version, read_version, disk_num, disk_dir, \
117 dircount, dircount2, dirsize, diroffset = \
118 struct.unpack(structEndArchive64, data)
119 if sig != stringEndArchive64:
120 return endrec
121
122 # Update the original endrec using data from the ZIP64 record
123 endrec[1] = disk_num
124 endrec[2] = disk_dir
125 endrec[3] = dircount
126 endrec[4] = dircount2
127 endrec[5] = dirsize
128 endrec[6] = diroffset
129 return endrec
130
131
Martin v. Löwis6f6873b2002-10-13 13:54:50 +0000132def _EndRecData(fpin):
133 """Return data from the "End of Central Directory" record, or None.
134
135 The data is a list of the nine items in the ZIP "End of central dir"
136 record followed by a tenth item, the file seek offset of this record."""
137 fpin.seek(-22, 2) # Assume no archive comment.
138 filesize = fpin.tell() + 22 # Get file size
139 data = fpin.read()
Jeremy Hylton9ff05b22007-08-29 19:09:54 +0000140 if data[0:4] == stringEndArchive and data[-2:] == b"\000\000":
Martin v. Löwis6f6873b2002-10-13 13:54:50 +0000141 endrec = struct.unpack(structEndArchive, data)
142 endrec = list(endrec)
143 endrec.append("") # Append the archive comment
144 endrec.append(filesize - 22) # Append the record start offset
Christian Heimesd5e2b6f2008-03-19 21:50:51 +0000145 if endrec[-4] == 0xffffffff:
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000146 return _EndRecData64(fpin, -22, endrec)
Martin v. Löwis6f6873b2002-10-13 13:54:50 +0000147 return endrec
148 # Search the last END_BLOCK bytes of the file for the record signature.
149 # The comment is appended to the ZIP file and has a 16 bit length.
150 # So the comment may be up to 64K long. We limit the search for the
151 # signature to a few Kbytes at the end of the file for efficiency.
152 # also, the signature must not appear in the comment.
153 END_BLOCK = min(filesize, 1024 * 4)
154 fpin.seek(filesize - END_BLOCK, 0)
155 data = fpin.read()
156 start = data.rfind(stringEndArchive)
157 if start >= 0: # Correct signature string was found
158 endrec = struct.unpack(structEndArchive, data[start:start+22])
159 endrec = list(endrec)
160 comment = data[start+22:]
161 if endrec[7] == len(comment): # Comment length checks out
162 # Append the archive comment and start offset
163 endrec.append(comment)
164 endrec.append(filesize - END_BLOCK + start)
Christian Heimesd5e2b6f2008-03-19 21:50:51 +0000165 if endrec[-4] == 0xffffffff:
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000166 return _EndRecData64(fpin, - END_BLOCK + start, endrec)
Martin v. Löwis6f6873b2002-10-13 13:54:50 +0000167 return endrec
168 return # Error, return None
169
Fred Drake484d7352000-10-02 21:14:52 +0000170
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000171class ZipInfo (object):
Fred Drake484d7352000-10-02 21:14:52 +0000172 """Class with attributes describing each file in the ZIP archive."""
173
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000174 __slots__ = (
175 'orig_filename',
176 'filename',
177 'date_time',
178 'compress_type',
179 'comment',
180 'extra',
181 'create_system',
182 'create_version',
183 'extract_version',
184 'reserved',
185 'flag_bits',
186 'volume',
187 'internal_attr',
188 'external_attr',
189 'header_offset',
190 'CRC',
191 'compress_size',
192 'file_size',
Christian Heimesfdab48e2008-01-20 09:06:41 +0000193 '_raw_time',
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000194 )
195
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000196 def __init__(self, filename="NoName", date_time=(1980,1,1,0,0,0)):
Greg Ward8e36d282003-06-18 00:53:06 +0000197 self.orig_filename = filename # Original file name in archive
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000198
199 # Terminate the file name at the first null byte. Null bytes in file
200 # names are used as tricks by viruses in archives.
Greg Ward8e36d282003-06-18 00:53:06 +0000201 null_byte = filename.find(chr(0))
202 if null_byte >= 0:
203 filename = filename[0:null_byte]
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000204 # This is used to ensure paths in generated ZIP files always use
205 # forward slashes as the directory separator, as required by the
206 # ZIP format specification.
207 if os.sep != "/" and os.sep in filename:
Greg Ward8e36d282003-06-18 00:53:06 +0000208 filename = filename.replace(os.sep, "/")
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000209
Greg Ward8e36d282003-06-18 00:53:06 +0000210 self.filename = filename # Normalized file name
Tim Peterse1190062001-01-15 03:34:38 +0000211 self.date_time = date_time # year, month, day, hour, min, sec
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000212 # Standard values:
Tim Peterse1190062001-01-15 03:34:38 +0000213 self.compress_type = ZIP_STORED # Type of compression for the file
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000214 self.comment = b"" # Comment for each file
215 self.extra = b"" # ZIP extra data
Martin v. Löwis00756902006-02-05 17:09:41 +0000216 if sys.platform == 'win32':
217 self.create_system = 0 # System which created ZIP archive
218 else:
219 # Assume everything else is unix-y
220 self.create_system = 3 # System which created ZIP archive
Tim Peterse1190062001-01-15 03:34:38 +0000221 self.create_version = 20 # Version which created ZIP archive
222 self.extract_version = 20 # Version needed to extract archive
223 self.reserved = 0 # Must be zero
224 self.flag_bits = 0 # ZIP flag bits
225 self.volume = 0 # Volume number of file header
226 self.internal_attr = 0 # Internal attributes
227 self.external_attr = 0 # External file attributes
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000228 # Other attributes are set by class ZipFile:
Tim Peterse1190062001-01-15 03:34:38 +0000229 # header_offset Byte offset to the file header
Tim Peterse1190062001-01-15 03:34:38 +0000230 # CRC CRC-32 of the uncompressed file
231 # compress_size Size of the compressed file
232 # file_size Size of the uncompressed file
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000233
234 def FileHeader(self):
Fred Drake484d7352000-10-02 21:14:52 +0000235 """Return the per-file header as a string."""
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000236 dt = self.date_time
237 dosdate = (dt[0] - 1980) << 9 | dt[1] << 5 | dt[2]
Tim Peters3caca232001-12-06 06:23:26 +0000238 dostime = dt[3] << 11 | dt[4] << 5 | (dt[5] // 2)
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000239 if self.flag_bits & 0x08:
Tim Peterse1190062001-01-15 03:34:38 +0000240 # Set these to zero because we write them after the file data
241 CRC = compress_size = file_size = 0
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000242 else:
Tim Peterse1190062001-01-15 03:34:38 +0000243 CRC = self.CRC
244 compress_size = self.compress_size
245 file_size = self.file_size
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000246
247 extra = self.extra
248
249 if file_size > ZIP64_LIMIT or compress_size > ZIP64_LIMIT:
250 # File is larger than what fits into a 4 byte integer,
251 # fall back to the ZIP64 extension
Christian Heimesd5e2b6f2008-03-19 21:50:51 +0000252 fmt = '<HHQQ'
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000253 extra = extra + struct.pack(fmt,
254 1, struct.calcsize(fmt)-4, file_size, compress_size)
255 file_size = 0xffffffff # -1
256 compress_size = 0xffffffff # -1
257 self.extract_version = max(45, self.extract_version)
258 self.create_version = max(45, self.extract_version)
259
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000260 header = struct.pack(structFileHeader, stringFileHeader,
261 self.extract_version, self.reserved, self.flag_bits,
262 self.compress_type, dostime, dosdate, CRC,
263 compress_size, file_size,
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000264 len(self.filename), len(extra))
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000265 return header + self.filename.encode("utf-8") + extra
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000266
267 def _decodeExtra(self):
268 # Try to decode the extra field.
269 extra = self.extra
270 unpack = struct.unpack
271 while extra:
Christian Heimesd5e2b6f2008-03-19 21:50:51 +0000272 tp, ln = unpack('<HH', extra[:4])
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000273 if tp == 1:
274 if ln >= 24:
Christian Heimesd5e2b6f2008-03-19 21:50:51 +0000275 counts = unpack('<QQQ', extra[4:28])
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000276 elif ln == 16:
Christian Heimesd5e2b6f2008-03-19 21:50:51 +0000277 counts = unpack('<QQ', extra[4:20])
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000278 elif ln == 8:
Christian Heimesd5e2b6f2008-03-19 21:50:51 +0000279 counts = unpack('<Q', extra[4:12])
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000280 elif ln == 0:
281 counts = ()
282 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000283 raise RuntimeError("Corrupt extra field %s"%(ln,))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000284
285 idx = 0
286
287 # ZIP64 extension (large files and/or large archives)
Christian Heimesd5e2b6f2008-03-19 21:50:51 +0000288 # XXX Is this correct? won't this exclude 2**32-1 byte files?
289 if self.file_size in (0xffffffffffffffff, 0xffffffff):
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000290 self.file_size = counts[idx]
291 idx += 1
292
Guido van Rossume2a383d2007-01-15 16:59:06 +0000293 if self.compress_size == -1 or self.compress_size == 0xFFFFFFFF:
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000294 self.compress_size = counts[idx]
295 idx += 1
296
Guido van Rossume2a383d2007-01-15 16:59:06 +0000297 if self.header_offset == -1 or self.header_offset == 0xffffffff:
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000298 old = self.header_offset
299 self.header_offset = counts[idx]
300 idx+=1
301
302 extra = extra[ln+4:]
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000303
304
Thomas Wouterscf297e42007-02-23 15:07:44 +0000305class _ZipDecrypter:
306 """Class to handle decryption of files stored within a ZIP archive.
307
308 ZIP supports a password-based form of encryption. Even though known
309 plaintext attacks have been found against it, it is still useful
Christian Heimesfdab48e2008-01-20 09:06:41 +0000310 to be able to get data out of such a file.
Thomas Wouterscf297e42007-02-23 15:07:44 +0000311
312 Usage:
313 zd = _ZipDecrypter(mypwd)
314 plain_char = zd(cypher_char)
315 plain_text = map(zd, cypher_text)
316 """
317
318 def _GenerateCRCTable():
319 """Generate a CRC-32 table.
320
321 ZIP encryption uses the CRC32 one-byte primitive for scrambling some
322 internal keys. We noticed that a direct implementation is faster than
323 relying on binascii.crc32().
324 """
325 poly = 0xedb88320
326 table = [0] * 256
327 for i in range(256):
328 crc = i
329 for j in range(8):
330 if crc & 1:
331 crc = ((crc >> 1) & 0x7FFFFFFF) ^ poly
332 else:
333 crc = ((crc >> 1) & 0x7FFFFFFF)
334 table[i] = crc
335 return table
336 crctable = _GenerateCRCTable()
337
338 def _crc32(self, ch, crc):
339 """Compute the CRC32 primitive on one byte."""
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000340 return ((crc >> 8) & 0xffffff) ^ self.crctable[(crc ^ ch) & 0xff]
Thomas Wouterscf297e42007-02-23 15:07:44 +0000341
342 def __init__(self, pwd):
343 self.key0 = 305419896
344 self.key1 = 591751049
345 self.key2 = 878082192
346 for p in pwd:
347 self._UpdateKeys(p)
348
349 def _UpdateKeys(self, c):
350 self.key0 = self._crc32(c, self.key0)
351 self.key1 = (self.key1 + (self.key0 & 255)) & 4294967295
352 self.key1 = (self.key1 * 134775813 + 1) & 4294967295
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000353 self.key2 = self._crc32((self.key1 >> 24) & 255, self.key2)
Thomas Wouterscf297e42007-02-23 15:07:44 +0000354
355 def __call__(self, c):
356 """Decrypt a single character."""
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000357 assert isinstance(c, int)
Thomas Wouterscf297e42007-02-23 15:07:44 +0000358 k = self.key2 | 2
359 c = c ^ (((k * (k^1)) >> 8) & 255)
Thomas Wouterscf297e42007-02-23 15:07:44 +0000360 self._UpdateKeys(c)
361 return c
362
Guido van Rossumd8faa362007-04-27 19:54:29 +0000363class ZipExtFile:
364 """File-like object for reading an archive member.
365 Is returned by ZipFile.open().
366 """
367
368 def __init__(self, fileobj, zipinfo, decrypt=None):
369 self.fileobj = fileobj
370 self.decrypter = decrypt
371 self.bytes_read = 0
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000372 self.rawbuffer = b''
373 self.readbuffer = b''
374 self.linebuffer = b''
Guido van Rossumd8faa362007-04-27 19:54:29 +0000375 self.eof = False
376 self.univ_newlines = False
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000377 self.nlSeps = (b"\n", )
378 self.lastdiscard = b''
Guido van Rossumd8faa362007-04-27 19:54:29 +0000379
380 self.compress_type = zipinfo.compress_type
381 self.compress_size = zipinfo.compress_size
382
383 self.closed = False
384 self.mode = "r"
385 self.name = zipinfo.filename
386
387 # read from compressed files in 64k blocks
388 self.compreadsize = 64*1024
389 if self.compress_type == ZIP_DEFLATED:
390 self.dc = zlib.decompressobj(-15)
391
392 def set_univ_newlines(self, univ_newlines):
393 self.univ_newlines = univ_newlines
394
395 # pick line separator char(s) based on universal newlines flag
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000396 self.nlSeps = (b"\n", )
Guido van Rossumd8faa362007-04-27 19:54:29 +0000397 if self.univ_newlines:
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000398 self.nlSeps = (b"\r\n", b"\r", b"\n")
Guido van Rossumd8faa362007-04-27 19:54:29 +0000399
400 def __iter__(self):
401 return self
402
403 def __next__(self):
404 nextline = self.readline()
405 if not nextline:
406 raise StopIteration()
407
408 return nextline
409
410 def close(self):
411 self.closed = True
412
413 def _checkfornewline(self):
414 nl, nllen = -1, -1
415 if self.linebuffer:
416 # ugly check for cases where half of an \r\n pair was
417 # read on the last pass, and the \r was discarded. In this
418 # case we just throw away the \n at the start of the buffer.
Guido van Rossum814661e2007-07-18 22:07:29 +0000419 if (self.lastdiscard, self.linebuffer[:1]) == (b'\r', b'\n'):
Guido van Rossumd8faa362007-04-27 19:54:29 +0000420 self.linebuffer = self.linebuffer[1:]
421
422 for sep in self.nlSeps:
423 nl = self.linebuffer.find(sep)
424 if nl >= 0:
425 nllen = len(sep)
426 return nl, nllen
427
428 return nl, nllen
429
430 def readline(self, size = -1):
431 """Read a line with approx. size. If size is negative,
432 read a whole line.
433 """
434 if size < 0:
Christian Heimesa37d4c62007-12-04 23:02:19 +0000435 size = sys.maxsize
Guido van Rossumd8faa362007-04-27 19:54:29 +0000436 elif size == 0:
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000437 return b''
Guido van Rossumd8faa362007-04-27 19:54:29 +0000438
439 # check for a newline already in buffer
440 nl, nllen = self._checkfornewline()
441
442 if nl >= 0:
443 # the next line was already in the buffer
444 nl = min(nl, size)
445 else:
446 # no line break in buffer - try to read more
447 size -= len(self.linebuffer)
448 while nl < 0 and size > 0:
449 buf = self.read(min(size, 100))
450 if not buf:
451 break
452 self.linebuffer += buf
453 size -= len(buf)
454
455 # check for a newline in buffer
456 nl, nllen = self._checkfornewline()
457
458 # we either ran out of bytes in the file, or
459 # met the specified size limit without finding a newline,
460 # so return current buffer
461 if nl < 0:
462 s = self.linebuffer
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000463 self.linebuffer = b''
Guido van Rossumd8faa362007-04-27 19:54:29 +0000464 return s
465
466 buf = self.linebuffer[:nl]
467 self.lastdiscard = self.linebuffer[nl:nl + nllen]
468 self.linebuffer = self.linebuffer[nl + nllen:]
469
470 # line is always returned with \n as newline char (except possibly
471 # for a final incomplete line in the file, which is handled above).
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000472 return buf + b"\n"
Guido van Rossumd8faa362007-04-27 19:54:29 +0000473
474 def readlines(self, sizehint = -1):
475 """Return a list with all (following) lines. The sizehint parameter
476 is ignored in this implementation.
477 """
478 result = []
479 while True:
480 line = self.readline()
481 if not line: break
482 result.append(line)
483 return result
484
485 def read(self, size = None):
Guido van Rossum814661e2007-07-18 22:07:29 +0000486 # act like file obj and return empty string if size is 0
Guido van Rossumd8faa362007-04-27 19:54:29 +0000487 if size == 0:
Guido van Rossum814661e2007-07-18 22:07:29 +0000488 return b''
Guido van Rossumd8faa362007-04-27 19:54:29 +0000489
490 # determine read size
491 bytesToRead = self.compress_size - self.bytes_read
492
493 # adjust read size for encrypted files since the first 12 bytes
494 # are for the encryption/password information
495 if self.decrypter is not None:
496 bytesToRead -= 12
497
498 if size is not None and size >= 0:
499 if self.compress_type == ZIP_STORED:
500 lr = len(self.readbuffer)
501 bytesToRead = min(bytesToRead, size - lr)
502 elif self.compress_type == ZIP_DEFLATED:
503 if len(self.readbuffer) > size:
504 # the user has requested fewer bytes than we've already
505 # pulled through the decompressor; don't read any more
506 bytesToRead = 0
507 else:
508 # user will use up the buffer, so read some more
509 lr = len(self.rawbuffer)
510 bytesToRead = min(bytesToRead, self.compreadsize - lr)
511
512 # avoid reading past end of file contents
513 if bytesToRead + self.bytes_read > self.compress_size:
514 bytesToRead = self.compress_size - self.bytes_read
515
516 # try to read from file (if necessary)
517 if bytesToRead > 0:
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000518 data = self.fileobj.read(bytesToRead)
519 self.bytes_read += len(data)
520 try:
521 self.rawbuffer += data
522 except:
523 print(repr(self.fileobj), repr(self.rawbuffer),
524 repr(data))
525 raise
Guido van Rossumd8faa362007-04-27 19:54:29 +0000526
527 # handle contents of raw buffer
528 if self.rawbuffer:
529 newdata = self.rawbuffer
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000530 self.rawbuffer = b''
Guido van Rossumd8faa362007-04-27 19:54:29 +0000531
532 # decrypt new data if we were given an object to handle that
533 if newdata and self.decrypter is not None:
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000534 newdata = bytes(map(self.decrypter, newdata))
Guido van Rossumd8faa362007-04-27 19:54:29 +0000535
536 # decompress newly read data if necessary
537 if newdata and self.compress_type == ZIP_DEFLATED:
538 newdata = self.dc.decompress(newdata)
539 self.rawbuffer = self.dc.unconsumed_tail
540 if self.eof and len(self.rawbuffer) == 0:
541 # we're out of raw bytes (both from the file and
542 # the local buffer); flush just to make sure the
543 # decompressor is done
544 newdata += self.dc.flush()
545 # prevent decompressor from being used again
546 self.dc = None
547
548 self.readbuffer += newdata
549
550
551 # return what the user asked for
552 if size is None or len(self.readbuffer) <= size:
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000553 data = self.readbuffer
554 self.readbuffer = b''
Guido van Rossumd8faa362007-04-27 19:54:29 +0000555 else:
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000556 data = self.readbuffer[:size]
Guido van Rossumd8faa362007-04-27 19:54:29 +0000557 self.readbuffer = self.readbuffer[size:]
558
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000559 return data
Guido van Rossumd8faa362007-04-27 19:54:29 +0000560
561
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000562class ZipFile:
Tim Petersa19a1682001-03-29 04:36:09 +0000563 """ Class with methods to open, read, write, close, list zip files.
564
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000565 z = ZipFile(file, mode="r", compression=ZIP_STORED, allowZip64=True)
Tim Petersa19a1682001-03-29 04:36:09 +0000566
Fred Drake3d9091e2001-03-26 15:49:24 +0000567 file: Either the path to the file, or a file-like object.
568 If it is a path, the file will be opened and closed by ZipFile.
569 mode: The mode can be either read "r", write "w" or append "a".
570 compression: ZIP_STORED (no compression) or ZIP_DEFLATED (requires zlib).
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000571 allowZip64: if True ZipFile will create files with ZIP64 extensions when
572 needed, otherwise it will raise an exception when this would
573 be necessary.
574
Fred Drake3d9091e2001-03-26 15:49:24 +0000575 """
Fred Drake484d7352000-10-02 21:14:52 +0000576
Fred Drake90eac282001-02-28 05:29:34 +0000577 fp = None # Set here since __del__ checks it
578
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000579 def __init__(self, file, mode="r", compression=ZIP_STORED, allowZip64=False):
Fred Drake484d7352000-10-02 21:14:52 +0000580 """Open the ZIP file with mode read "r", write "w" or append "a"."""
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000581 if mode not in ("r", "w", "a"):
582 raise RuntimeError('ZipFile() requires mode "r", "w", or "a"')
583
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000584 if compression == ZIP_STORED:
585 pass
586 elif compression == ZIP_DEFLATED:
587 if not zlib:
Collin Winterce36ad82007-08-30 01:19:48 +0000588 raise RuntimeError(
589 "Compression requires the (missing) zlib module")
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000590 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000591 raise RuntimeError("That compression method is not supported")
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000592
593 self._allowZip64 = allowZip64
594 self._didModify = False
Tim Peterse1190062001-01-15 03:34:38 +0000595 self.debug = 0 # Level of printing: 0 through 3
596 self.NameToInfo = {} # Find file info given name
597 self.filelist = [] # List of ZipInfo instances for archive
598 self.compression = compression # Method of compression
Raymond Hettinger2ca7c192005-02-16 09:27:49 +0000599 self.mode = key = mode.replace('b', '')[0]
Thomas Wouterscf297e42007-02-23 15:07:44 +0000600 self.pwd = None
Tim Petersa19a1682001-03-29 04:36:09 +0000601
Fred Drake3d9091e2001-03-26 15:49:24 +0000602 # Check if we were passed a file-like object
Guido van Rossum3172c5d2007-10-16 18:12:55 +0000603 if isinstance(file, str):
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000604 # No, it's a filename
Fred Drake3d9091e2001-03-26 15:49:24 +0000605 self._filePassed = 0
606 self.filename = file
607 modeDict = {'r' : 'rb', 'w': 'wb', 'a' : 'r+b'}
Thomas Wouterscf297e42007-02-23 15:07:44 +0000608 try:
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000609 self.fp = io.open(file, modeDict[mode])
Thomas Wouterscf297e42007-02-23 15:07:44 +0000610 except IOError:
611 if mode == 'a':
612 mode = key = 'w'
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000613 self.fp = io.open(file, modeDict[mode])
Thomas Wouterscf297e42007-02-23 15:07:44 +0000614 else:
615 raise
Fred Drake3d9091e2001-03-26 15:49:24 +0000616 else:
617 self._filePassed = 1
618 self.fp = file
619 self.filename = getattr(file, 'name', None)
Tim Petersa19a1682001-03-29 04:36:09 +0000620
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000621 if key == 'r':
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000622 self._GetContents()
623 elif key == 'w':
Fred Drake3d9091e2001-03-26 15:49:24 +0000624 pass
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000625 elif key == 'a':
Martin v. Löwis6f6873b2002-10-13 13:54:50 +0000626 try: # See if file is a zip file
627 self._RealGetContents()
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000628 # seek to start of directory and overwrite
Martin v. Löwis6f6873b2002-10-13 13:54:50 +0000629 self.fp.seek(self.start_dir, 0)
630 except BadZipfile: # file is not a zip file, just append
631 self.fp.seek(0, 2)
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000632 else:
Tim Peters7d3bad62001-04-04 18:56:49 +0000633 if not self._filePassed:
634 self.fp.close()
635 self.fp = None
Collin Winterce36ad82007-08-30 01:19:48 +0000636 raise RuntimeError('Mode must be "r", "w" or "a"')
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000637
638 def _GetContents(self):
Tim Peters7d3bad62001-04-04 18:56:49 +0000639 """Read the directory, making sure we close the file if the format
640 is bad."""
641 try:
642 self._RealGetContents()
643 except BadZipfile:
644 if not self._filePassed:
645 self.fp.close()
646 self.fp = None
647 raise
648
649 def _RealGetContents(self):
Fred Drake484d7352000-10-02 21:14:52 +0000650 """Read in the table of contents for the ZIP file."""
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000651 fp = self.fp
Martin v. Löwis6f6873b2002-10-13 13:54:50 +0000652 endrec = _EndRecData(fp)
653 if not endrec:
Collin Winterce36ad82007-08-30 01:19:48 +0000654 raise BadZipfile("File is not a zip file")
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000655 if self.debug > 1:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000656 print(endrec)
Tim Peterse1190062001-01-15 03:34:38 +0000657 size_cd = endrec[5] # bytes in central directory
658 offset_cd = endrec[6] # offset of central directory
Martin v. Löwis6f6873b2002-10-13 13:54:50 +0000659 self.comment = endrec[8] # archive comment
660 # endrec[9] is the offset of the "End of Central Dir" record
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000661 if endrec[9] > ZIP64_LIMIT:
662 x = endrec[9] - size_cd - 56 - 20
663 else:
664 x = endrec[9] - size_cd
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000665 # "concat" is zero, unless zip was concatenated to another file
666 concat = x - offset_cd
667 if self.debug > 2:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000668 print("given, inferred, offset", offset_cd, x, concat)
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000669 # self.start_dir: Position of start of central directory
670 self.start_dir = offset_cd + concat
671 fp.seek(self.start_dir, 0)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000672 data = fp.read(size_cd)
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000673 fp = io.BytesIO(data)
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000674 total = 0
675 while total < size_cd:
676 centdir = fp.read(46)
677 total = total + 46
678 if centdir[0:4] != stringCentralDir:
Collin Winterce36ad82007-08-30 01:19:48 +0000679 raise BadZipfile("Bad magic number for central directory")
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000680 centdir = struct.unpack(structCentralDir, centdir)
681 if self.debug > 2:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000682 print(centdir)
Fred Drake3e038e52001-02-28 17:56:26 +0000683 filename = fp.read(centdir[_CD_FILENAME_LENGTH])
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000684 # Create ZipInfo instance to store file information
Guido van Rossum98297ee2007-11-06 21:34:58 +0000685 x = ZipInfo(filename.decode("utf-8"))
Fred Drake3e038e52001-02-28 17:56:26 +0000686 x.extra = fp.read(centdir[_CD_EXTRA_FIELD_LENGTH])
687 x.comment = fp.read(centdir[_CD_COMMENT_LENGTH])
688 total = (total + centdir[_CD_FILENAME_LENGTH]
689 + centdir[_CD_EXTRA_FIELD_LENGTH]
690 + centdir[_CD_COMMENT_LENGTH])
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000691 x.header_offset = centdir[_CD_LOCAL_HEADER_OFFSET]
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000692 (x.create_version, x.create_system, x.extract_version, x.reserved,
693 x.flag_bits, x.compress_type, t, d,
694 x.CRC, x.compress_size, x.file_size) = centdir[1:12]
695 x.volume, x.internal_attr, x.external_attr = centdir[15:18]
696 # Convert date/time code to (year, month, day, hour, min, sec)
Christian Heimesfdab48e2008-01-20 09:06:41 +0000697 x._raw_time = t
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000698 x.date_time = ( (d>>9)+1980, (d>>5)&0xF, d&0x1F,
Fred Drake414ca662000-06-13 18:49:53 +0000699 t>>11, (t>>5)&0x3F, (t&0x1F) * 2 )
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000700
701 x._decodeExtra()
702 x.header_offset = x.header_offset + concat
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000703 self.filelist.append(x)
704 self.NameToInfo[x.filename] = x
705 if self.debug > 2:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000706 print("total", total)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000707
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000708
709 def namelist(self):
Fred Drake484d7352000-10-02 21:14:52 +0000710 """Return a list of file names in the archive."""
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000711 l = []
712 for data in self.filelist:
713 l.append(data.filename)
714 return l
715
716 def infolist(self):
Fred Drake484d7352000-10-02 21:14:52 +0000717 """Return a list of class ZipInfo instances for files in the
718 archive."""
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000719 return self.filelist
720
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000721 def printdir(self, file=None):
Fred Drake484d7352000-10-02 21:14:52 +0000722 """Print a table of contents for the zip file."""
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000723 print("%-46s %19s %12s" % ("File Name", "Modified ", "Size"),
724 file=file)
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000725 for zinfo in self.filelist:
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000726 date = "%d-%02d-%02d %02d:%02d:%02d" % zinfo.date_time[:6]
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000727 print("%-46s %s %12d" % (zinfo.filename, date, zinfo.file_size),
728 file=file)
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000729
730 def testzip(self):
Fred Drake484d7352000-10-02 21:14:52 +0000731 """Read all the files and check the CRC."""
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000732 for zinfo in self.filelist:
733 try:
Tim Peterse1190062001-01-15 03:34:38 +0000734 self.read(zinfo.filename) # Check CRC-32
Raymond Hettingerc0fac962003-06-27 22:25:03 +0000735 except BadZipfile:
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000736 return zinfo.filename
737
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000738
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000739 def getinfo(self, name):
Fred Drake484d7352000-10-02 21:14:52 +0000740 """Return the instance of ZipInfo given 'name'."""
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000741 info = self.NameToInfo.get(name)
742 if info is None:
743 raise KeyError(
744 'There is no item named %r in the archive' % name)
745
746 return info
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000747
Thomas Wouterscf297e42007-02-23 15:07:44 +0000748 def setpassword(self, pwd):
749 """Set default password for encrypted files."""
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000750 assert isinstance(pwd, bytes)
Thomas Wouterscf297e42007-02-23 15:07:44 +0000751 self.pwd = pwd
752
753 def read(self, name, pwd=None):
Fred Drake484d7352000-10-02 21:14:52 +0000754 """Return file bytes (as a string) for name."""
Guido van Rossumd8faa362007-04-27 19:54:29 +0000755 return self.open(name, "r", pwd).read()
756
757 def open(self, name, mode="r", pwd=None):
758 """Return file-like object for 'name'."""
759 if mode not in ("r", "U", "rU"):
Collin Winterce36ad82007-08-30 01:19:48 +0000760 raise RuntimeError('open() requires mode "r", "U", or "rU"')
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000761 if not self.fp:
Collin Winterce36ad82007-08-30 01:19:48 +0000762 raise RuntimeError(
763 "Attempt to read ZIP archive that was already closed")
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000764
Guido van Rossumd8faa362007-04-27 19:54:29 +0000765 # Only open a new file for instances where we were not
766 # given a file object in the constructor
767 if self._filePassed:
768 zef_file = self.fp
769 else:
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000770 zef_file = io.open(self.filename, 'rb')
Guido van Rossumd8faa362007-04-27 19:54:29 +0000771
772 # Get info object for name
773 zinfo = self.getinfo(name)
774
775 filepos = zef_file.tell()
776
777 zef_file.seek(zinfo.header_offset, 0)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000778
779 # Skip the file header:
Guido van Rossumd8faa362007-04-27 19:54:29 +0000780 fheader = zef_file.read(30)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000781 if fheader[0:4] != stringFileHeader:
Collin Winterce36ad82007-08-30 01:19:48 +0000782 raise BadZipfile("Bad magic number for file header")
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000783
784 fheader = struct.unpack(structFileHeader, fheader)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000785 fname = zef_file.read(fheader[_FH_FILENAME_LENGTH])
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000786 if fheader[_FH_EXTRA_FIELD_LENGTH]:
Guido van Rossumd8faa362007-04-27 19:54:29 +0000787 zef_file.read(fheader[_FH_EXTRA_FIELD_LENGTH])
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000788
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000789 if fname != zinfo.orig_filename.encode("utf-8"):
Collin Winterce36ad82007-08-30 01:19:48 +0000790 raise BadZipfile(
791 'File name in directory %r and header %r differ.'
792 % (zinfo.orig_filename, fname))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000793
Guido van Rossumd8faa362007-04-27 19:54:29 +0000794 # check for encrypted flag & handle password
795 is_encrypted = zinfo.flag_bits & 0x1
796 zd = None
Thomas Wouterscf297e42007-02-23 15:07:44 +0000797 if is_encrypted:
Guido van Rossumd8faa362007-04-27 19:54:29 +0000798 if not pwd:
799 pwd = self.pwd
800 if not pwd:
Collin Winterce36ad82007-08-30 01:19:48 +0000801 raise RuntimeError("File %s is encrypted, "
802 "password required for extraction" % name)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000803
Thomas Wouterscf297e42007-02-23 15:07:44 +0000804 zd = _ZipDecrypter(pwd)
805 # The first 12 bytes in the cypher stream is an encryption header
806 # used to strengthen the algorithm. The first 11 bytes are
807 # completely random, while the 12th contains the MSB of the CRC,
Christian Heimesfdab48e2008-01-20 09:06:41 +0000808 # or the MSB of the file time depending on the header type
Thomas Wouterscf297e42007-02-23 15:07:44 +0000809 # and is used to check the correctness of the password.
Guido van Rossumd8faa362007-04-27 19:54:29 +0000810 bytes = zef_file.read(12)
Guido van Rossumc1f779c2007-07-03 08:25:58 +0000811 h = list(map(zd, bytes[0:12]))
Christian Heimesfdab48e2008-01-20 09:06:41 +0000812 if zinfo.flag_bits & 0x8:
813 # compare against the file type from extended local headers
814 check_byte = (zinfo._raw_time >> 8) & 0xff
815 else:
816 # compare against the CRC otherwise
817 check_byte = (zinfo.CRC >> 24) & 0xff
818 if h[11] != check_byte:
819 raise RuntimeError("Bad password for file", name)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000820
821 # build and return a ZipExtFile
822 if zd is None:
823 zef = ZipExtFile(zef_file, zinfo)
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000824 else:
Guido van Rossumd8faa362007-04-27 19:54:29 +0000825 zef = ZipExtFile(zef_file, zinfo, zd)
826
827 # set universal newlines on ZipExtFile if necessary
828 if "U" in mode:
829 zef.set_univ_newlines(True)
830 return zef
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000831
Christian Heimes790c8232008-01-07 21:14:23 +0000832 def extract(self, member, path=None, pwd=None):
833 """Extract a member from the archive to the current working directory,
834 using its full name. Its file information is extracted as accurately
835 as possible. `member' may be a filename or a ZipInfo object. You can
836 specify a different directory using `path'.
837 """
838 if not isinstance(member, ZipInfo):
839 member = self.getinfo(member)
840
841 if path is None:
842 path = os.getcwd()
843
844 return self._extract_member(member, path, pwd)
845
846 def extractall(self, path=None, members=None, pwd=None):
847 """Extract all members from the archive to the current working
848 directory. `path' specifies a different directory to extract to.
849 `members' is optional and must be a subset of the list returned
850 by namelist().
851 """
852 if members is None:
853 members = self.namelist()
854
855 for zipinfo in members:
856 self.extract(zipinfo, path, pwd)
857
858 def _extract_member(self, member, targetpath, pwd):
859 """Extract the ZipInfo object 'member' to a physical
860 file on the path targetpath.
861 """
862 # build the destination pathname, replacing
863 # forward slashes to platform specific separators.
864 if targetpath[-1:] == "/":
865 targetpath = targetpath[:-1]
866
867 # don't include leading "/" from file name if present
868 if os.path.isabs(member.filename):
869 targetpath = os.path.join(targetpath, member.filename[1:])
870 else:
871 targetpath = os.path.join(targetpath, member.filename)
872
873 targetpath = os.path.normpath(targetpath)
874
875 # Create all upper directories if necessary.
876 upperdirs = os.path.dirname(targetpath)
877 if upperdirs and not os.path.exists(upperdirs):
878 os.makedirs(upperdirs)
879
880 source = self.open(member.filename, pwd=pwd)
881 target = open(targetpath, "wb")
882 shutil.copyfileobj(source, target)
883 source.close()
884 target.close()
885
886 return targetpath
887
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000888 def _writecheck(self, zinfo):
Fred Drake484d7352000-10-02 21:14:52 +0000889 """Check for errors before writing a file to the archive."""
Raymond Hettinger54f02222002-06-01 14:18:47 +0000890 if zinfo.filename in self.NameToInfo:
Tim Peterse1190062001-01-15 03:34:38 +0000891 if self.debug: # Warning for duplicate names
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000892 print("Duplicate name:", zinfo.filename)
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000893 if self.mode not in ("w", "a"):
Collin Winterce36ad82007-08-30 01:19:48 +0000894 raise RuntimeError('write() requires mode "w" or "a"')
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000895 if not self.fp:
Collin Winterce36ad82007-08-30 01:19:48 +0000896 raise RuntimeError(
897 "Attempt to write ZIP archive that was already closed")
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000898 if zinfo.compress_type == ZIP_DEFLATED and not zlib:
Collin Winterce36ad82007-08-30 01:19:48 +0000899 raise RuntimeError(
900 "Compression requires the (missing) zlib module")
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000901 if zinfo.compress_type not in (ZIP_STORED, ZIP_DEFLATED):
Collin Winterce36ad82007-08-30 01:19:48 +0000902 raise RuntimeError("That compression method is not supported")
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000903 if zinfo.file_size > ZIP64_LIMIT:
904 if not self._allowZip64:
905 raise LargeZipFile("Filesize would require ZIP64 extensions")
906 if zinfo.header_offset > ZIP64_LIMIT:
907 if not self._allowZip64:
Collin Winterce36ad82007-08-30 01:19:48 +0000908 raise LargeZipFile(
909 "Zipfile size would require ZIP64 extensions")
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000910
911 def write(self, filename, arcname=None, compress_type=None):
Fred Drake484d7352000-10-02 21:14:52 +0000912 """Put the bytes from filename into the archive under the name
913 arcname."""
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000914 if not self.fp:
915 raise RuntimeError(
916 "Attempt to write to ZIP archive that was already closed")
917
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000918 st = os.stat(filename)
Raymond Hettinger32200ae2002-06-01 19:51:15 +0000919 mtime = time.localtime(st.st_mtime)
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000920 date_time = mtime[0:6]
921 # Create ZipInfo instance to store file information
922 if arcname is None:
Georg Brandl8f7c54e2006-02-20 08:40:38 +0000923 arcname = filename
924 arcname = os.path.normpath(os.path.splitdrive(arcname)[1])
925 while arcname[0] in (os.sep, os.altsep):
926 arcname = arcname[1:]
927 zinfo = ZipInfo(arcname, date_time)
Guido van Rossume2a383d2007-01-15 16:59:06 +0000928 zinfo.external_attr = (st[0] & 0xFFFF) << 16 # Unix attributes
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000929 if compress_type is None:
Tim Peterse1190062001-01-15 03:34:38 +0000930 zinfo.compress_type = self.compression
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000931 else:
Tim Peterse1190062001-01-15 03:34:38 +0000932 zinfo.compress_type = compress_type
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000933
934 zinfo.file_size = st.st_size
Finn Bock03a3bb82001-09-05 18:40:33 +0000935 zinfo.flag_bits = 0x00
Tim Peterse1190062001-01-15 03:34:38 +0000936 zinfo.header_offset = self.fp.tell() # Start of header bytes
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000937
938 self._writecheck(zinfo)
939 self._didModify = True
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000940 fp = io.open(filename, "rb")
Finn Bock03a3bb82001-09-05 18:40:33 +0000941 # Must overwrite CRC and sizes with correct data later
942 zinfo.CRC = CRC = 0
943 zinfo.compress_size = compress_size = 0
944 zinfo.file_size = file_size = 0
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000945 self.fp.write(zinfo.FileHeader())
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000946 if zinfo.compress_type == ZIP_DEFLATED:
947 cmpr = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION,
948 zlib.DEFLATED, -15)
949 else:
950 cmpr = None
951 while 1:
952 buf = fp.read(1024 * 8)
953 if not buf:
954 break
955 file_size = file_size + len(buf)
Christian Heimesd5e2b6f2008-03-19 21:50:51 +0000956 CRC = crc32(buf, CRC) & 0xffffffff
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000957 if cmpr:
958 buf = cmpr.compress(buf)
959 compress_size = compress_size + len(buf)
960 self.fp.write(buf)
961 fp.close()
962 if cmpr:
963 buf = cmpr.flush()
964 compress_size = compress_size + len(buf)
965 self.fp.write(buf)
966 zinfo.compress_size = compress_size
967 else:
968 zinfo.compress_size = file_size
969 zinfo.CRC = CRC
970 zinfo.file_size = file_size
Finn Bock03a3bb82001-09-05 18:40:33 +0000971 # Seek backwards and write CRC and file sizes
Tim Petersb64bec32001-09-18 02:26:39 +0000972 position = self.fp.tell() # Preserve current position in file
Finn Bock03a3bb82001-09-05 18:40:33 +0000973 self.fp.seek(zinfo.header_offset + 14, 0)
Christian Heimesd5e2b6f2008-03-19 21:50:51 +0000974 self.fp.write(struct.pack("<LLL", zinfo.CRC, zinfo.compress_size,
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000975 zinfo.file_size))
Finn Bock03a3bb82001-09-05 18:40:33 +0000976 self.fp.seek(position, 0)
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000977 self.filelist.append(zinfo)
978 self.NameToInfo[zinfo.filename] = zinfo
979
Guido van Rossum85825dc2007-08-27 17:03:28 +0000980 def writestr(self, zinfo_or_arcname, data):
981 """Write a file into the archive. The contents is 'data', which
982 may be either a 'str' or a 'bytes' instance; if it is a 'str',
983 it is encoded as UTF-8 first.
984 'zinfo_or_arcname' is either a ZipInfo instance or
Just van Rossumb083cb32002-12-12 12:23:32 +0000985 the name of the file in the archive."""
Guido van Rossum85825dc2007-08-27 17:03:28 +0000986 if isinstance(data, str):
987 data = data.encode("utf-8")
Just van Rossumb083cb32002-12-12 12:23:32 +0000988 if not isinstance(zinfo_or_arcname, ZipInfo):
989 zinfo = ZipInfo(filename=zinfo_or_arcname,
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000990 date_time=time.localtime(time.time())[:6])
Just van Rossumb083cb32002-12-12 12:23:32 +0000991 zinfo.compress_type = self.compression
992 else:
993 zinfo = zinfo_or_arcname
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000994
995 if not self.fp:
996 raise RuntimeError(
997 "Attempt to write to ZIP archive that was already closed")
998
Guido van Rossum85825dc2007-08-27 17:03:28 +0000999 zinfo.file_size = len(data) # Uncompressed size
1000 zinfo.header_offset = self.fp.tell() # Start of header data
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001001 self._writecheck(zinfo)
1002 self._didModify = True
Christian Heimesd5e2b6f2008-03-19 21:50:51 +00001003 zinfo.CRC = crc32(data) & 0xffffffff # CRC-32 checksum
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001004 if zinfo.compress_type == ZIP_DEFLATED:
1005 co = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION,
1006 zlib.DEFLATED, -15)
Guido van Rossum85825dc2007-08-27 17:03:28 +00001007 data = co.compress(data) + co.flush()
1008 zinfo.compress_size = len(data) # Compressed size
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001009 else:
1010 zinfo.compress_size = zinfo.file_size
Guido van Rossum85825dc2007-08-27 17:03:28 +00001011 zinfo.header_offset = self.fp.tell() # Start of header data
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001012 self.fp.write(zinfo.FileHeader())
Guido van Rossum85825dc2007-08-27 17:03:28 +00001013 self.fp.write(data)
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001014 self.fp.flush()
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001015 if zinfo.flag_bits & 0x08:
Tim Peterse1190062001-01-15 03:34:38 +00001016 # Write CRC and file sizes after the file data
Brett Cannonff450f72004-07-10 19:09:20 +00001017 self.fp.write(struct.pack("<lLL", zinfo.CRC, zinfo.compress_size,
Tim Peterse1190062001-01-15 03:34:38 +00001018 zinfo.file_size))
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001019 self.filelist.append(zinfo)
1020 self.NameToInfo[zinfo.filename] = zinfo
1021
1022 def __del__(self):
Fred Drake484d7352000-10-02 21:14:52 +00001023 """Call the "close()" method in case the user forgot."""
Tim Petersd15f8bb2001-11-28 23:16:40 +00001024 self.close()
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001025
1026 def close(self):
Fred Drake484d7352000-10-02 21:14:52 +00001027 """Close the file, and for mode "w" and "a" write the ending
1028 records."""
Tim Petersd15f8bb2001-11-28 23:16:40 +00001029 if self.fp is None:
1030 return
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001031
1032 if self.mode in ("w", "a") and self._didModify: # write ending records
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001033 count = 0
1034 pos1 = self.fp.tell()
Tim Peterse1190062001-01-15 03:34:38 +00001035 for zinfo in self.filelist: # write central directory
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001036 count = count + 1
1037 dt = zinfo.date_time
1038 dosdate = (dt[0] - 1980) << 9 | dt[1] << 5 | dt[2]
Tim Peters3caca232001-12-06 06:23:26 +00001039 dostime = dt[3] << 11 | dt[4] << 5 | (dt[5] // 2)
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001040 extra = []
1041 if zinfo.file_size > ZIP64_LIMIT \
1042 or zinfo.compress_size > ZIP64_LIMIT:
1043 extra.append(zinfo.file_size)
1044 extra.append(zinfo.compress_size)
1045 file_size = 0xffffffff #-1
1046 compress_size = 0xffffffff #-1
1047 else:
1048 file_size = zinfo.file_size
1049 compress_size = zinfo.compress_size
1050
1051 if zinfo.header_offset > ZIP64_LIMIT:
1052 extra.append(zinfo.header_offset)
Christian Heimesd5e2b6f2008-03-19 21:50:51 +00001053 header_offset = 0xffffffff # -1 32 bit
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001054 else:
1055 header_offset = zinfo.header_offset
1056
1057 extra_data = zinfo.extra
1058 if extra:
1059 # Append a ZIP64 field to the extra's
1060 extra_data = struct.pack(
Christian Heimesd5e2b6f2008-03-19 21:50:51 +00001061 '<HH' + 'Q'*len(extra),
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001062 1, 8*len(extra), *extra) + extra_data
1063
1064 extract_version = max(45, zinfo.extract_version)
1065 create_version = max(45, zinfo.create_version)
1066 else:
1067 extract_version = zinfo.extract_version
1068 create_version = zinfo.create_version
1069
Amaury Forgeot d'Arc2f9d4d12008-03-20 00:35:03 +00001070 centdir = struct.pack(structCentralDir,
1071 stringCentralDir, create_version,
1072 zinfo.create_system, extract_version, zinfo.reserved,
1073 zinfo.flag_bits, zinfo.compress_type, dostime, dosdate,
1074 zinfo.CRC, compress_size, file_size,
1075 len(zinfo.filename), len(extra_data), len(zinfo.comment),
1076 0, zinfo.internal_attr, zinfo.external_attr,
1077 header_offset)
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001078 self.fp.write(centdir)
Guido van Rossumd6ca5462007-05-22 01:29:33 +00001079 self.fp.write(zinfo.filename.encode("utf-8"))
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001080 self.fp.write(extra_data)
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001081 self.fp.write(zinfo.comment)
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001082
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001083 pos2 = self.fp.tell()
1084 # Write end-of-zip-archive record
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001085 if pos1 > ZIP64_LIMIT:
1086 # Need to write the ZIP64 end-of-archive records
1087 zip64endrec = struct.pack(
1088 structEndArchive64, stringEndArchive64,
1089 44, 45, 45, 0, 0, count, count, pos2 - pos1, pos1)
1090 self.fp.write(zip64endrec)
1091
1092 zip64locrec = struct.pack(
1093 structEndArchive64Locator,
1094 stringEndArchive64Locator, 0, pos2, 1)
1095 self.fp.write(zip64locrec)
1096
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001097 endrec = struct.pack(structEndArchive, stringEndArchive,
Christian Heimesd5e2b6f2008-03-19 21:50:51 +00001098 0, 0, count, count, pos2 - pos1, 0xffffffff, 0)
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001099 self.fp.write(endrec)
1100
1101 else:
1102 endrec = struct.pack(structEndArchive, stringEndArchive,
1103 0, 0, count, count, pos2 - pos1, pos1, 0)
1104 self.fp.write(endrec)
Guido van Rossumf85af612001-04-14 16:45:14 +00001105 self.fp.flush()
Fred Drake3d9091e2001-03-26 15:49:24 +00001106 if not self._filePassed:
1107 self.fp.close()
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001108 self.fp = None
1109
1110
1111class PyZipFile(ZipFile):
Fred Drake484d7352000-10-02 21:14:52 +00001112 """Class to create ZIP archives with Python library files and packages."""
1113
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001114 def writepy(self, pathname, basename = ""):
1115 """Add all files from "pathname" to the ZIP archive.
1116
Fred Drake484d7352000-10-02 21:14:52 +00001117 If pathname is a package directory, search the directory and
1118 all package subdirectories recursively for all *.py and enter
1119 the modules into the archive. If pathname is a plain
1120 directory, listdir *.py and enter all modules. Else, pathname
1121 must be a Python *.py file and the module will be put into the
1122 archive. Added modules are always module.pyo or module.pyc.
1123 This method will compile the module.py into module.pyc if
1124 necessary.
1125 """
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001126 dir, name = os.path.split(pathname)
1127 if os.path.isdir(pathname):
1128 initname = os.path.join(pathname, "__init__.py")
1129 if os.path.isfile(initname):
1130 # This is a package directory, add it
1131 if basename:
1132 basename = "%s/%s" % (basename, name)
1133 else:
1134 basename = name
1135 if self.debug:
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001136 print("Adding package in", pathname, "as", basename)
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001137 fname, arcname = self._get_codename(initname[0:-3], basename)
1138 if self.debug:
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001139 print("Adding", arcname)
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001140 self.write(fname, arcname)
1141 dirlist = os.listdir(pathname)
1142 dirlist.remove("__init__.py")
1143 # Add all *.py files and package subdirectories
1144 for filename in dirlist:
1145 path = os.path.join(pathname, filename)
1146 root, ext = os.path.splitext(filename)
1147 if os.path.isdir(path):
1148 if os.path.isfile(os.path.join(path, "__init__.py")):
1149 # This is a package directory, add it
1150 self.writepy(path, basename) # Recursive call
1151 elif ext == ".py":
1152 fname, arcname = self._get_codename(path[0:-3],
1153 basename)
1154 if self.debug:
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001155 print("Adding", arcname)
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001156 self.write(fname, arcname)
1157 else:
1158 # This is NOT a package directory, add its files at top level
1159 if self.debug:
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001160 print("Adding files from directory", pathname)
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001161 for filename in os.listdir(pathname):
1162 path = os.path.join(pathname, filename)
1163 root, ext = os.path.splitext(filename)
1164 if ext == ".py":
1165 fname, arcname = self._get_codename(path[0:-3],
1166 basename)
1167 if self.debug:
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001168 print("Adding", arcname)
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001169 self.write(fname, arcname)
1170 else:
1171 if pathname[-3:] != ".py":
Collin Winterce36ad82007-08-30 01:19:48 +00001172 raise RuntimeError(
1173 'Files added with writepy() must end with ".py"')
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001174 fname, arcname = self._get_codename(pathname[0:-3], basename)
1175 if self.debug:
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001176 print("Adding file", arcname)
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001177 self.write(fname, arcname)
1178
1179 def _get_codename(self, pathname, basename):
1180 """Return (filename, archivename) for the path.
1181
Fred Drake484d7352000-10-02 21:14:52 +00001182 Given a module name path, return the correct file path and
1183 archive name, compiling if necessary. For example, given
1184 /python/lib/string, return (/python/lib/string.pyc, string).
1185 """
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001186 file_py = pathname + ".py"
1187 file_pyc = pathname + ".pyc"
1188 file_pyo = pathname + ".pyo"
1189 if os.path.isfile(file_pyo) and \
Raymond Hettinger32200ae2002-06-01 19:51:15 +00001190 os.stat(file_pyo).st_mtime >= os.stat(file_py).st_mtime:
Tim Peterse1190062001-01-15 03:34:38 +00001191 fname = file_pyo # Use .pyo file
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001192 elif not os.path.isfile(file_pyc) or \
Raymond Hettinger32200ae2002-06-01 19:51:15 +00001193 os.stat(file_pyc).st_mtime < os.stat(file_py).st_mtime:
Fred Drake484d7352000-10-02 21:14:52 +00001194 import py_compile
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001195 if self.debug:
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001196 print("Compiling", file_py)
Martin v. Löwis0c6774d2003-01-15 11:51:06 +00001197 try:
1198 py_compile.compile(file_py, file_pyc, None, True)
Guido van Rossumb940e112007-01-10 16:19:56 +00001199 except py_compile.PyCompileError as err:
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001200 print(err.msg)
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001201 fname = file_pyc
1202 else:
1203 fname = file_pyc
1204 archivename = os.path.split(fname)[1]
1205 if basename:
1206 archivename = "%s/%s" % (basename, archivename)
1207 return (fname, archivename)
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001208
1209
1210def main(args = None):
1211 import textwrap
1212 USAGE=textwrap.dedent("""\
1213 Usage:
1214 zipfile.py -l zipfile.zip # Show listing of a zipfile
1215 zipfile.py -t zipfile.zip # Test if a zipfile is valid
1216 zipfile.py -e zipfile.zip target # Extract zipfile into target dir
1217 zipfile.py -c zipfile.zip src ... # Create zipfile from sources
1218 """)
1219 if args is None:
1220 args = sys.argv[1:]
1221
1222 if not args or args[0] not in ('-l', '-c', '-e', '-t'):
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001223 print(USAGE)
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001224 sys.exit(1)
1225
1226 if args[0] == '-l':
1227 if len(args) != 2:
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001228 print(USAGE)
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001229 sys.exit(1)
1230 zf = ZipFile(args[1], 'r')
1231 zf.printdir()
1232 zf.close()
1233
1234 elif args[0] == '-t':
1235 if len(args) != 2:
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001236 print(USAGE)
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001237 sys.exit(1)
1238 zf = ZipFile(args[1], 'r')
1239 zf.testzip()
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001240 print("Done testing")
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001241
1242 elif args[0] == '-e':
1243 if len(args) != 3:
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001244 print(USAGE)
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001245 sys.exit(1)
1246
1247 zf = ZipFile(args[1], 'r')
1248 out = args[2]
1249 for path in zf.namelist():
1250 if path.startswith('./'):
1251 tgt = os.path.join(out, path[2:])
1252 else:
1253 tgt = os.path.join(out, path)
1254
1255 tgtdir = os.path.dirname(tgt)
1256 if not os.path.exists(tgtdir):
1257 os.makedirs(tgtdir)
Guido van Rossumd6ca5462007-05-22 01:29:33 +00001258 fp = io.open(tgt, 'wb')
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001259 fp.write(zf.read(path))
1260 fp.close()
1261 zf.close()
1262
1263 elif args[0] == '-c':
1264 if len(args) < 3:
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001265 print(USAGE)
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001266 sys.exit(1)
1267
1268 def addToZip(zf, path, zippath):
1269 if os.path.isfile(path):
1270 zf.write(path, zippath, ZIP_DEFLATED)
1271 elif os.path.isdir(path):
1272 for nm in os.listdir(path):
1273 addToZip(zf,
1274 os.path.join(path, nm), os.path.join(zippath, nm))
1275 # else: ignore
1276
1277 zf = ZipFile(args[1], 'w', allowZip64=True)
1278 for src in args[2:]:
1279 addToZip(zf, src, os.path.basename(src))
1280
1281 zf.close()
1282
1283if __name__ == "__main__":
1284 main()