blob: 2865c0a2c16aa7ad4dc4cecfeea9275dbfc052e1 [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
Guido van Rossum9c673f32001-04-10 15:37:12 +000011except ImportError:
Guido van Rossum32abe6f2000-03-31 17:30:02 +000012 zlib = None
13
Skip Montanaro40fc1602001-03-01 04:27:19 +000014__all__ = ["BadZipfile", "error", "ZIP_STORED", "ZIP_DEFLATED", "is_zipfile",
Thomas Wouters0e3f5912006-08-11 14:57:12 +000015 "ZipInfo", "ZipFile", "PyZipFile", "LargeZipFile" ]
Skip Montanaro40fc1602001-03-01 04:27:19 +000016
Fred Drake5db246d2000-09-29 20:44:48 +000017class BadZipfile(Exception):
Guido van Rossum32abe6f2000-03-31 17:30:02 +000018 pass
Thomas Wouters0e3f5912006-08-11 14:57:12 +000019
20
21class LargeZipFile(Exception):
22 """
23 Raised when writing a zipfile, the zipfile requires ZIP64 extensions
24 and those extensions are disabled.
25 """
26
Tim Peterse1190062001-01-15 03:34:38 +000027error = BadZipfile # The exception raised by this module
Guido van Rossum32abe6f2000-03-31 17:30:02 +000028
Thomas Wouters0e3f5912006-08-11 14:57:12 +000029ZIP64_LIMIT= (1 << 31) - 1
30
Guido van Rossum32abe6f2000-03-31 17:30:02 +000031# constants for Zip file compression methods
32ZIP_STORED = 0
33ZIP_DEFLATED = 8
34# Other ZIP compression methods not supported
35
36# Here are some struct module formats for reading headers
Christian Heimesfdab48e2008-01-20 09:06:41 +000037structEndArchive = "<4s4H2LH" # 9 items, end of archive, 22 bytes
Guido van Rossumd6ca5462007-05-22 01:29:33 +000038stringEndArchive = b"PK\005\006" # magic number for end of archive record
Christian Heimesfdab48e2008-01-20 09:06:41 +000039structCentralDir = "<4s4B4HlLL5HLL"# 19 items, central directory, 46 bytes
Guido van Rossumd6ca5462007-05-22 01:29:33 +000040stringCentralDir = b"PK\001\002" # magic number for central directory
Brett Cannonff450f72004-07-10 19:09:20 +000041structFileHeader = "<4s2B4HlLL2H" # 12 items, file header record, 30 bytes
Guido van Rossumd6ca5462007-05-22 01:29:33 +000042stringFileHeader = b"PK\003\004" # magic number for file header
Thomas Wouters0e3f5912006-08-11 14:57:12 +000043structEndArchive64Locator = "<4slql" # 4 items, locate Zip64 header, 20 bytes
Guido van Rossumd6ca5462007-05-22 01:29:33 +000044stringEndArchive64Locator = b"PK\x06\x07" # magic token for locator header
Thomas Wouters0e3f5912006-08-11 14:57:12 +000045structEndArchive64 = "<4sqhhllqqqq" # 10 items, end of archive (Zip64), 56 bytes
Guido van Rossumd6ca5462007-05-22 01:29:33 +000046stringEndArchive64 = b"PK\x06\x06" # magic token for Zip64 header
Thomas Wouters0e3f5912006-08-11 14:57:12 +000047
Guido van Rossum32abe6f2000-03-31 17:30:02 +000048
Fred Drake3e038e52001-02-28 17:56:26 +000049# indexes of entries in the central directory structure
50_CD_SIGNATURE = 0
51_CD_CREATE_VERSION = 1
52_CD_CREATE_SYSTEM = 2
53_CD_EXTRACT_VERSION = 3
54_CD_EXTRACT_SYSTEM = 4 # is this meaningful?
55_CD_FLAG_BITS = 5
56_CD_COMPRESS_TYPE = 6
57_CD_TIME = 7
58_CD_DATE = 8
59_CD_CRC = 9
60_CD_COMPRESSED_SIZE = 10
61_CD_UNCOMPRESSED_SIZE = 11
62_CD_FILENAME_LENGTH = 12
63_CD_EXTRA_FIELD_LENGTH = 13
64_CD_COMMENT_LENGTH = 14
65_CD_DISK_NUMBER_START = 15
66_CD_INTERNAL_FILE_ATTRIBUTES = 16
67_CD_EXTERNAL_FILE_ATTRIBUTES = 17
68_CD_LOCAL_HEADER_OFFSET = 18
69
70# indexes of entries in the local file header structure
71_FH_SIGNATURE = 0
72_FH_EXTRACT_VERSION = 1
73_FH_EXTRACT_SYSTEM = 2 # is this meaningful?
74_FH_GENERAL_PURPOSE_FLAG_BITS = 3
75_FH_COMPRESSION_METHOD = 4
76_FH_LAST_MOD_TIME = 5
77_FH_LAST_MOD_DATE = 6
78_FH_CRC = 7
79_FH_COMPRESSED_SIZE = 8
80_FH_UNCOMPRESSED_SIZE = 9
81_FH_FILENAME_LENGTH = 10
82_FH_EXTRA_FIELD_LENGTH = 11
83
Guido van Rossum32abe6f2000-03-31 17:30:02 +000084def is_zipfile(filename):
Martin v. Löwis6f6873b2002-10-13 13:54:50 +000085 """Quickly see if file is a ZIP file by checking the magic number."""
Guido van Rossum32abe6f2000-03-31 17:30:02 +000086 try:
Guido van Rossumd6ca5462007-05-22 01:29:33 +000087 fpin = io.open(filename, "rb")
Martin v. Löwis6f6873b2002-10-13 13:54:50 +000088 endrec = _EndRecData(fpin)
Guido van Rossum32abe6f2000-03-31 17:30:02 +000089 fpin.close()
Martin v. Löwis6f6873b2002-10-13 13:54:50 +000090 if endrec:
Guido van Rossum8ca162f2002-04-07 06:36:23 +000091 return True # file has correct magic number
Fred Drake7e473802001-05-11 19:52:57 +000092 except IOError:
Guido van Rossum32abe6f2000-03-31 17:30:02 +000093 pass
Guido van Rossum8ca162f2002-04-07 06:36:23 +000094 return False
Guido van Rossum32abe6f2000-03-31 17:30:02 +000095
Thomas Wouters0e3f5912006-08-11 14:57:12 +000096def _EndRecData64(fpin, offset, endrec):
97 """
98 Read the ZIP64 end-of-archive records and use that to update endrec
99 """
100 locatorSize = struct.calcsize(structEndArchive64Locator)
101 fpin.seek(offset - locatorSize, 2)
102 data = fpin.read(locatorSize)
103 sig, diskno, reloff, disks = struct.unpack(structEndArchive64Locator, data)
104 if sig != stringEndArchive64Locator:
105 return endrec
106
107 if diskno != 0 or disks != 1:
108 raise BadZipfile("zipfiles that span multiple disks are not supported")
109
110 # Assume no 'zip64 extensible data'
111 endArchiveSize = struct.calcsize(structEndArchive64)
112 fpin.seek(offset - locatorSize - endArchiveSize, 2)
113 data = fpin.read(endArchiveSize)
114 sig, sz, create_version, read_version, disk_num, disk_dir, \
115 dircount, dircount2, dirsize, diroffset = \
116 struct.unpack(structEndArchive64, data)
117 if sig != stringEndArchive64:
118 return endrec
119
120 # Update the original endrec using data from the ZIP64 record
121 endrec[1] = disk_num
122 endrec[2] = disk_dir
123 endrec[3] = dircount
124 endrec[4] = dircount2
125 endrec[5] = dirsize
126 endrec[6] = diroffset
127 return endrec
128
129
Martin v. Löwis6f6873b2002-10-13 13:54:50 +0000130def _EndRecData(fpin):
131 """Return data from the "End of Central Directory" record, or None.
132
133 The data is a list of the nine items in the ZIP "End of central dir"
134 record followed by a tenth item, the file seek offset of this record."""
135 fpin.seek(-22, 2) # Assume no archive comment.
136 filesize = fpin.tell() + 22 # Get file size
137 data = fpin.read()
Jeremy Hylton9ff05b22007-08-29 19:09:54 +0000138 if data[0:4] == stringEndArchive and data[-2:] == b"\000\000":
Martin v. Löwis6f6873b2002-10-13 13:54:50 +0000139 endrec = struct.unpack(structEndArchive, data)
140 endrec = list(endrec)
141 endrec.append("") # Append the archive comment
142 endrec.append(filesize - 22) # Append the record start offset
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000143 if endrec[-4] == -1 or endrec[-4] == 0xffffffff:
144 return _EndRecData64(fpin, -22, endrec)
Martin v. Löwis6f6873b2002-10-13 13:54:50 +0000145 return endrec
146 # Search the last END_BLOCK bytes of the file for the record signature.
147 # The comment is appended to the ZIP file and has a 16 bit length.
148 # So the comment may be up to 64K long. We limit the search for the
149 # signature to a few Kbytes at the end of the file for efficiency.
150 # also, the signature must not appear in the comment.
151 END_BLOCK = min(filesize, 1024 * 4)
152 fpin.seek(filesize - END_BLOCK, 0)
153 data = fpin.read()
154 start = data.rfind(stringEndArchive)
155 if start >= 0: # Correct signature string was found
156 endrec = struct.unpack(structEndArchive, data[start:start+22])
157 endrec = list(endrec)
158 comment = data[start+22:]
159 if endrec[7] == len(comment): # Comment length checks out
160 # Append the archive comment and start offset
161 endrec.append(comment)
162 endrec.append(filesize - END_BLOCK + start)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000163 if endrec[-4] == -1 or endrec[-4] == 0xffffffff:
164 return _EndRecData64(fpin, - END_BLOCK + start, endrec)
Martin v. Löwis6f6873b2002-10-13 13:54:50 +0000165 return endrec
166 return # Error, return None
167
Fred Drake484d7352000-10-02 21:14:52 +0000168
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000169class ZipInfo (object):
Fred Drake484d7352000-10-02 21:14:52 +0000170 """Class with attributes describing each file in the ZIP archive."""
171
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000172 __slots__ = (
173 'orig_filename',
174 'filename',
175 'date_time',
176 'compress_type',
177 'comment',
178 'extra',
179 'create_system',
180 'create_version',
181 'extract_version',
182 'reserved',
183 'flag_bits',
184 'volume',
185 'internal_attr',
186 'external_attr',
187 'header_offset',
188 'CRC',
189 'compress_size',
190 'file_size',
Christian Heimesfdab48e2008-01-20 09:06:41 +0000191 '_raw_time',
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000192 )
193
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000194 def __init__(self, filename="NoName", date_time=(1980,1,1,0,0,0)):
Greg Ward8e36d282003-06-18 00:53:06 +0000195 self.orig_filename = filename # Original file name in archive
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000196
197 # Terminate the file name at the first null byte. Null bytes in file
198 # names are used as tricks by viruses in archives.
Greg Ward8e36d282003-06-18 00:53:06 +0000199 null_byte = filename.find(chr(0))
200 if null_byte >= 0:
201 filename = filename[0:null_byte]
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000202 # This is used to ensure paths in generated ZIP files always use
203 # forward slashes as the directory separator, as required by the
204 # ZIP format specification.
205 if os.sep != "/" and os.sep in filename:
Greg Ward8e36d282003-06-18 00:53:06 +0000206 filename = filename.replace(os.sep, "/")
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000207
Greg Ward8e36d282003-06-18 00:53:06 +0000208 self.filename = filename # Normalized file name
Tim Peterse1190062001-01-15 03:34:38 +0000209 self.date_time = date_time # year, month, day, hour, min, sec
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000210 # Standard values:
Tim Peterse1190062001-01-15 03:34:38 +0000211 self.compress_type = ZIP_STORED # Type of compression for the file
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000212 self.comment = b"" # Comment for each file
213 self.extra = b"" # ZIP extra data
Martin v. Löwis00756902006-02-05 17:09:41 +0000214 if sys.platform == 'win32':
215 self.create_system = 0 # System which created ZIP archive
216 else:
217 # Assume everything else is unix-y
218 self.create_system = 3 # System which created ZIP archive
Tim Peterse1190062001-01-15 03:34:38 +0000219 self.create_version = 20 # Version which created ZIP archive
220 self.extract_version = 20 # Version needed to extract archive
221 self.reserved = 0 # Must be zero
222 self.flag_bits = 0 # ZIP flag bits
223 self.volume = 0 # Volume number of file header
224 self.internal_attr = 0 # Internal attributes
225 self.external_attr = 0 # External file attributes
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000226 # Other attributes are set by class ZipFile:
Tim Peterse1190062001-01-15 03:34:38 +0000227 # header_offset Byte offset to the file header
Tim Peterse1190062001-01-15 03:34:38 +0000228 # CRC CRC-32 of the uncompressed file
229 # compress_size Size of the compressed file
230 # file_size Size of the uncompressed file
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000231
232 def FileHeader(self):
Fred Drake484d7352000-10-02 21:14:52 +0000233 """Return the per-file header as a string."""
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000234 dt = self.date_time
235 dosdate = (dt[0] - 1980) << 9 | dt[1] << 5 | dt[2]
Tim Peters3caca232001-12-06 06:23:26 +0000236 dostime = dt[3] << 11 | dt[4] << 5 | (dt[5] // 2)
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000237 if self.flag_bits & 0x08:
Tim Peterse1190062001-01-15 03:34:38 +0000238 # Set these to zero because we write them after the file data
239 CRC = compress_size = file_size = 0
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000240 else:
Tim Peterse1190062001-01-15 03:34:38 +0000241 CRC = self.CRC
242 compress_size = self.compress_size
243 file_size = self.file_size
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000244
245 extra = self.extra
246
247 if file_size > ZIP64_LIMIT or compress_size > ZIP64_LIMIT:
248 # File is larger than what fits into a 4 byte integer,
249 # fall back to the ZIP64 extension
250 fmt = '<hhqq'
251 extra = extra + struct.pack(fmt,
252 1, struct.calcsize(fmt)-4, file_size, compress_size)
253 file_size = 0xffffffff # -1
254 compress_size = 0xffffffff # -1
255 self.extract_version = max(45, self.extract_version)
256 self.create_version = max(45, self.extract_version)
257
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000258 header = struct.pack(structFileHeader, stringFileHeader,
259 self.extract_version, self.reserved, self.flag_bits,
260 self.compress_type, dostime, dosdate, CRC,
261 compress_size, file_size,
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000262 len(self.filename), len(extra))
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000263 return header + self.filename.encode("utf-8") + extra
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000264
265 def _decodeExtra(self):
266 # Try to decode the extra field.
267 extra = self.extra
268 unpack = struct.unpack
269 while extra:
270 tp, ln = unpack('<hh', extra[:4])
271 if tp == 1:
272 if ln >= 24:
273 counts = unpack('<qqq', extra[4:28])
274 elif ln == 16:
275 counts = unpack('<qq', extra[4:20])
276 elif ln == 8:
277 counts = unpack('<q', extra[4:12])
278 elif ln == 0:
279 counts = ()
280 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000281 raise RuntimeError("Corrupt extra field %s"%(ln,))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000282
283 idx = 0
284
285 # ZIP64 extension (large files and/or large archives)
Guido van Rossume2a383d2007-01-15 16:59:06 +0000286 if self.file_size == -1 or self.file_size == 0xFFFFFFFF:
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000287 self.file_size = counts[idx]
288 idx += 1
289
Guido van Rossume2a383d2007-01-15 16:59:06 +0000290 if self.compress_size == -1 or self.compress_size == 0xFFFFFFFF:
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000291 self.compress_size = counts[idx]
292 idx += 1
293
Guido van Rossume2a383d2007-01-15 16:59:06 +0000294 if self.header_offset == -1 or self.header_offset == 0xffffffff:
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000295 old = self.header_offset
296 self.header_offset = counts[idx]
297 idx+=1
298
299 extra = extra[ln+4:]
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000300
301
Thomas Wouterscf297e42007-02-23 15:07:44 +0000302class _ZipDecrypter:
303 """Class to handle decryption of files stored within a ZIP archive.
304
305 ZIP supports a password-based form of encryption. Even though known
306 plaintext attacks have been found against it, it is still useful
Christian Heimesfdab48e2008-01-20 09:06:41 +0000307 to be able to get data out of such a file.
Thomas Wouterscf297e42007-02-23 15:07:44 +0000308
309 Usage:
310 zd = _ZipDecrypter(mypwd)
311 plain_char = zd(cypher_char)
312 plain_text = map(zd, cypher_text)
313 """
314
315 def _GenerateCRCTable():
316 """Generate a CRC-32 table.
317
318 ZIP encryption uses the CRC32 one-byte primitive for scrambling some
319 internal keys. We noticed that a direct implementation is faster than
320 relying on binascii.crc32().
321 """
322 poly = 0xedb88320
323 table = [0] * 256
324 for i in range(256):
325 crc = i
326 for j in range(8):
327 if crc & 1:
328 crc = ((crc >> 1) & 0x7FFFFFFF) ^ poly
329 else:
330 crc = ((crc >> 1) & 0x7FFFFFFF)
331 table[i] = crc
332 return table
333 crctable = _GenerateCRCTable()
334
335 def _crc32(self, ch, crc):
336 """Compute the CRC32 primitive on one byte."""
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000337 return ((crc >> 8) & 0xffffff) ^ self.crctable[(crc ^ ch) & 0xff]
Thomas Wouterscf297e42007-02-23 15:07:44 +0000338
339 def __init__(self, pwd):
340 self.key0 = 305419896
341 self.key1 = 591751049
342 self.key2 = 878082192
343 for p in pwd:
344 self._UpdateKeys(p)
345
346 def _UpdateKeys(self, c):
347 self.key0 = self._crc32(c, self.key0)
348 self.key1 = (self.key1 + (self.key0 & 255)) & 4294967295
349 self.key1 = (self.key1 * 134775813 + 1) & 4294967295
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000350 self.key2 = self._crc32((self.key1 >> 24) & 255, self.key2)
Thomas Wouterscf297e42007-02-23 15:07:44 +0000351
352 def __call__(self, c):
353 """Decrypt a single character."""
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000354 assert isinstance(c, int)
Thomas Wouterscf297e42007-02-23 15:07:44 +0000355 k = self.key2 | 2
356 c = c ^ (((k * (k^1)) >> 8) & 255)
Thomas Wouterscf297e42007-02-23 15:07:44 +0000357 self._UpdateKeys(c)
358 return c
359
Guido van Rossumd8faa362007-04-27 19:54:29 +0000360class ZipExtFile:
361 """File-like object for reading an archive member.
362 Is returned by ZipFile.open().
363 """
364
365 def __init__(self, fileobj, zipinfo, decrypt=None):
366 self.fileobj = fileobj
367 self.decrypter = decrypt
368 self.bytes_read = 0
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000369 self.rawbuffer = b''
370 self.readbuffer = b''
371 self.linebuffer = b''
Guido van Rossumd8faa362007-04-27 19:54:29 +0000372 self.eof = False
373 self.univ_newlines = False
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000374 self.nlSeps = (b"\n", )
375 self.lastdiscard = b''
Guido van Rossumd8faa362007-04-27 19:54:29 +0000376
377 self.compress_type = zipinfo.compress_type
378 self.compress_size = zipinfo.compress_size
379
380 self.closed = False
381 self.mode = "r"
382 self.name = zipinfo.filename
383
384 # read from compressed files in 64k blocks
385 self.compreadsize = 64*1024
386 if self.compress_type == ZIP_DEFLATED:
387 self.dc = zlib.decompressobj(-15)
388
389 def set_univ_newlines(self, univ_newlines):
390 self.univ_newlines = univ_newlines
391
392 # pick line separator char(s) based on universal newlines flag
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000393 self.nlSeps = (b"\n", )
Guido van Rossumd8faa362007-04-27 19:54:29 +0000394 if self.univ_newlines:
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000395 self.nlSeps = (b"\r\n", b"\r", b"\n")
Guido van Rossumd8faa362007-04-27 19:54:29 +0000396
397 def __iter__(self):
398 return self
399
400 def __next__(self):
401 nextline = self.readline()
402 if not nextline:
403 raise StopIteration()
404
405 return nextline
406
407 def close(self):
408 self.closed = True
409
410 def _checkfornewline(self):
411 nl, nllen = -1, -1
412 if self.linebuffer:
413 # ugly check for cases where half of an \r\n pair was
414 # read on the last pass, and the \r was discarded. In this
415 # case we just throw away the \n at the start of the buffer.
Guido van Rossum814661e2007-07-18 22:07:29 +0000416 if (self.lastdiscard, self.linebuffer[:1]) == (b'\r', b'\n'):
Guido van Rossumd8faa362007-04-27 19:54:29 +0000417 self.linebuffer = self.linebuffer[1:]
418
419 for sep in self.nlSeps:
420 nl = self.linebuffer.find(sep)
421 if nl >= 0:
422 nllen = len(sep)
423 return nl, nllen
424
425 return nl, nllen
426
427 def readline(self, size = -1):
428 """Read a line with approx. size. If size is negative,
429 read a whole line.
430 """
431 if size < 0:
Christian Heimesa37d4c62007-12-04 23:02:19 +0000432 size = sys.maxsize
Guido van Rossumd8faa362007-04-27 19:54:29 +0000433 elif size == 0:
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000434 return b''
Guido van Rossumd8faa362007-04-27 19:54:29 +0000435
436 # check for a newline already in buffer
437 nl, nllen = self._checkfornewline()
438
439 if nl >= 0:
440 # the next line was already in the buffer
441 nl = min(nl, size)
442 else:
443 # no line break in buffer - try to read more
444 size -= len(self.linebuffer)
445 while nl < 0 and size > 0:
446 buf = self.read(min(size, 100))
447 if not buf:
448 break
449 self.linebuffer += buf
450 size -= len(buf)
451
452 # check for a newline in buffer
453 nl, nllen = self._checkfornewline()
454
455 # we either ran out of bytes in the file, or
456 # met the specified size limit without finding a newline,
457 # so return current buffer
458 if nl < 0:
459 s = self.linebuffer
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000460 self.linebuffer = b''
Guido van Rossumd8faa362007-04-27 19:54:29 +0000461 return s
462
463 buf = self.linebuffer[:nl]
464 self.lastdiscard = self.linebuffer[nl:nl + nllen]
465 self.linebuffer = self.linebuffer[nl + nllen:]
466
467 # line is always returned with \n as newline char (except possibly
468 # for a final incomplete line in the file, which is handled above).
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000469 return buf + b"\n"
Guido van Rossumd8faa362007-04-27 19:54:29 +0000470
471 def readlines(self, sizehint = -1):
472 """Return a list with all (following) lines. The sizehint parameter
473 is ignored in this implementation.
474 """
475 result = []
476 while True:
477 line = self.readline()
478 if not line: break
479 result.append(line)
480 return result
481
482 def read(self, size = None):
Guido van Rossum814661e2007-07-18 22:07:29 +0000483 # act like file obj and return empty string if size is 0
Guido van Rossumd8faa362007-04-27 19:54:29 +0000484 if size == 0:
Guido van Rossum814661e2007-07-18 22:07:29 +0000485 return b''
Guido van Rossumd8faa362007-04-27 19:54:29 +0000486
487 # determine read size
488 bytesToRead = self.compress_size - self.bytes_read
489
490 # adjust read size for encrypted files since the first 12 bytes
491 # are for the encryption/password information
492 if self.decrypter is not None:
493 bytesToRead -= 12
494
495 if size is not None and size >= 0:
496 if self.compress_type == ZIP_STORED:
497 lr = len(self.readbuffer)
498 bytesToRead = min(bytesToRead, size - lr)
499 elif self.compress_type == ZIP_DEFLATED:
500 if len(self.readbuffer) > size:
501 # the user has requested fewer bytes than we've already
502 # pulled through the decompressor; don't read any more
503 bytesToRead = 0
504 else:
505 # user will use up the buffer, so read some more
506 lr = len(self.rawbuffer)
507 bytesToRead = min(bytesToRead, self.compreadsize - lr)
508
509 # avoid reading past end of file contents
510 if bytesToRead + self.bytes_read > self.compress_size:
511 bytesToRead = self.compress_size - self.bytes_read
512
513 # try to read from file (if necessary)
514 if bytesToRead > 0:
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000515 data = self.fileobj.read(bytesToRead)
516 self.bytes_read += len(data)
517 try:
518 self.rawbuffer += data
519 except:
520 print(repr(self.fileobj), repr(self.rawbuffer),
521 repr(data))
522 raise
Guido van Rossumd8faa362007-04-27 19:54:29 +0000523
524 # handle contents of raw buffer
525 if self.rawbuffer:
526 newdata = self.rawbuffer
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000527 self.rawbuffer = b''
Guido van Rossumd8faa362007-04-27 19:54:29 +0000528
529 # decrypt new data if we were given an object to handle that
530 if newdata and self.decrypter is not None:
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000531 newdata = bytes(map(self.decrypter, newdata))
Guido van Rossumd8faa362007-04-27 19:54:29 +0000532
533 # decompress newly read data if necessary
534 if newdata and self.compress_type == ZIP_DEFLATED:
535 newdata = self.dc.decompress(newdata)
536 self.rawbuffer = self.dc.unconsumed_tail
537 if self.eof and len(self.rawbuffer) == 0:
538 # we're out of raw bytes (both from the file and
539 # the local buffer); flush just to make sure the
540 # decompressor is done
541 newdata += self.dc.flush()
542 # prevent decompressor from being used again
543 self.dc = None
544
545 self.readbuffer += newdata
546
547
548 # return what the user asked for
549 if size is None or len(self.readbuffer) <= size:
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000550 data = self.readbuffer
551 self.readbuffer = b''
Guido van Rossumd8faa362007-04-27 19:54:29 +0000552 else:
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000553 data = self.readbuffer[:size]
Guido van Rossumd8faa362007-04-27 19:54:29 +0000554 self.readbuffer = self.readbuffer[size:]
555
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000556 return data
Guido van Rossumd8faa362007-04-27 19:54:29 +0000557
558
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000559class ZipFile:
Tim Petersa19a1682001-03-29 04:36:09 +0000560 """ Class with methods to open, read, write, close, list zip files.
561
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000562 z = ZipFile(file, mode="r", compression=ZIP_STORED, allowZip64=True)
Tim Petersa19a1682001-03-29 04:36:09 +0000563
Fred Drake3d9091e2001-03-26 15:49:24 +0000564 file: Either the path to the file, or a file-like object.
565 If it is a path, the file will be opened and closed by ZipFile.
566 mode: The mode can be either read "r", write "w" or append "a".
567 compression: ZIP_STORED (no compression) or ZIP_DEFLATED (requires zlib).
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000568 allowZip64: if True ZipFile will create files with ZIP64 extensions when
569 needed, otherwise it will raise an exception when this would
570 be necessary.
571
Fred Drake3d9091e2001-03-26 15:49:24 +0000572 """
Fred Drake484d7352000-10-02 21:14:52 +0000573
Fred Drake90eac282001-02-28 05:29:34 +0000574 fp = None # Set here since __del__ checks it
575
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000576 def __init__(self, file, mode="r", compression=ZIP_STORED, allowZip64=False):
Fred Drake484d7352000-10-02 21:14:52 +0000577 """Open the ZIP file with mode read "r", write "w" or append "a"."""
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000578 if mode not in ("r", "w", "a"):
579 raise RuntimeError('ZipFile() requires mode "r", "w", or "a"')
580
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000581 if compression == ZIP_STORED:
582 pass
583 elif compression == ZIP_DEFLATED:
584 if not zlib:
Collin Winterce36ad82007-08-30 01:19:48 +0000585 raise RuntimeError(
586 "Compression requires the (missing) zlib module")
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000587 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000588 raise RuntimeError("That compression method is not supported")
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000589
590 self._allowZip64 = allowZip64
591 self._didModify = False
Tim Peterse1190062001-01-15 03:34:38 +0000592 self.debug = 0 # Level of printing: 0 through 3
593 self.NameToInfo = {} # Find file info given name
594 self.filelist = [] # List of ZipInfo instances for archive
595 self.compression = compression # Method of compression
Raymond Hettinger2ca7c192005-02-16 09:27:49 +0000596 self.mode = key = mode.replace('b', '')[0]
Thomas Wouterscf297e42007-02-23 15:07:44 +0000597 self.pwd = None
Tim Petersa19a1682001-03-29 04:36:09 +0000598
Fred Drake3d9091e2001-03-26 15:49:24 +0000599 # Check if we were passed a file-like object
Guido van Rossum3172c5d2007-10-16 18:12:55 +0000600 if isinstance(file, str):
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000601 # No, it's a filename
Fred Drake3d9091e2001-03-26 15:49:24 +0000602 self._filePassed = 0
603 self.filename = file
604 modeDict = {'r' : 'rb', 'w': 'wb', 'a' : 'r+b'}
Thomas Wouterscf297e42007-02-23 15:07:44 +0000605 try:
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000606 self.fp = io.open(file, modeDict[mode])
Thomas Wouterscf297e42007-02-23 15:07:44 +0000607 except IOError:
608 if mode == 'a':
609 mode = key = 'w'
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000610 self.fp = io.open(file, modeDict[mode])
Thomas Wouterscf297e42007-02-23 15:07:44 +0000611 else:
612 raise
Fred Drake3d9091e2001-03-26 15:49:24 +0000613 else:
614 self._filePassed = 1
615 self.fp = file
616 self.filename = getattr(file, 'name', None)
Tim Petersa19a1682001-03-29 04:36:09 +0000617
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000618 if key == 'r':
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000619 self._GetContents()
620 elif key == 'w':
Fred Drake3d9091e2001-03-26 15:49:24 +0000621 pass
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000622 elif key == 'a':
Martin v. Löwis6f6873b2002-10-13 13:54:50 +0000623 try: # See if file is a zip file
624 self._RealGetContents()
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000625 # seek to start of directory and overwrite
Martin v. Löwis6f6873b2002-10-13 13:54:50 +0000626 self.fp.seek(self.start_dir, 0)
627 except BadZipfile: # file is not a zip file, just append
628 self.fp.seek(0, 2)
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000629 else:
Tim Peters7d3bad62001-04-04 18:56:49 +0000630 if not self._filePassed:
631 self.fp.close()
632 self.fp = None
Collin Winterce36ad82007-08-30 01:19:48 +0000633 raise RuntimeError('Mode must be "r", "w" or "a"')
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000634
635 def _GetContents(self):
Tim Peters7d3bad62001-04-04 18:56:49 +0000636 """Read the directory, making sure we close the file if the format
637 is bad."""
638 try:
639 self._RealGetContents()
640 except BadZipfile:
641 if not self._filePassed:
642 self.fp.close()
643 self.fp = None
644 raise
645
646 def _RealGetContents(self):
Fred Drake484d7352000-10-02 21:14:52 +0000647 """Read in the table of contents for the ZIP file."""
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000648 fp = self.fp
Martin v. Löwis6f6873b2002-10-13 13:54:50 +0000649 endrec = _EndRecData(fp)
650 if not endrec:
Collin Winterce36ad82007-08-30 01:19:48 +0000651 raise BadZipfile("File is not a zip file")
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000652 if self.debug > 1:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000653 print(endrec)
Tim Peterse1190062001-01-15 03:34:38 +0000654 size_cd = endrec[5] # bytes in central directory
655 offset_cd = endrec[6] # offset of central directory
Martin v. Löwis6f6873b2002-10-13 13:54:50 +0000656 self.comment = endrec[8] # archive comment
657 # endrec[9] is the offset of the "End of Central Dir" record
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000658 if endrec[9] > ZIP64_LIMIT:
659 x = endrec[9] - size_cd - 56 - 20
660 else:
661 x = endrec[9] - size_cd
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000662 # "concat" is zero, unless zip was concatenated to another file
663 concat = x - offset_cd
664 if self.debug > 2:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000665 print("given, inferred, offset", offset_cd, x, concat)
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000666 # self.start_dir: Position of start of central directory
667 self.start_dir = offset_cd + concat
668 fp.seek(self.start_dir, 0)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000669 data = fp.read(size_cd)
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000670 fp = io.BytesIO(data)
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000671 total = 0
672 while total < size_cd:
673 centdir = fp.read(46)
674 total = total + 46
675 if centdir[0:4] != stringCentralDir:
Collin Winterce36ad82007-08-30 01:19:48 +0000676 raise BadZipfile("Bad magic number for central directory")
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000677 centdir = struct.unpack(structCentralDir, centdir)
678 if self.debug > 2:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000679 print(centdir)
Fred Drake3e038e52001-02-28 17:56:26 +0000680 filename = fp.read(centdir[_CD_FILENAME_LENGTH])
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000681 # Create ZipInfo instance to store file information
Guido van Rossum98297ee2007-11-06 21:34:58 +0000682 x = ZipInfo(filename.decode("utf-8"))
Fred Drake3e038e52001-02-28 17:56:26 +0000683 x.extra = fp.read(centdir[_CD_EXTRA_FIELD_LENGTH])
684 x.comment = fp.read(centdir[_CD_COMMENT_LENGTH])
685 total = (total + centdir[_CD_FILENAME_LENGTH]
686 + centdir[_CD_EXTRA_FIELD_LENGTH]
687 + centdir[_CD_COMMENT_LENGTH])
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000688 x.header_offset = centdir[_CD_LOCAL_HEADER_OFFSET]
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000689 (x.create_version, x.create_system, x.extract_version, x.reserved,
690 x.flag_bits, x.compress_type, t, d,
691 x.CRC, x.compress_size, x.file_size) = centdir[1:12]
692 x.volume, x.internal_attr, x.external_attr = centdir[15:18]
693 # Convert date/time code to (year, month, day, hour, min, sec)
Christian Heimesfdab48e2008-01-20 09:06:41 +0000694 x._raw_time = t
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000695 x.date_time = ( (d>>9)+1980, (d>>5)&0xF, d&0x1F,
Fred Drake414ca662000-06-13 18:49:53 +0000696 t>>11, (t>>5)&0x3F, (t&0x1F) * 2 )
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000697
698 x._decodeExtra()
699 x.header_offset = x.header_offset + concat
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000700 self.filelist.append(x)
701 self.NameToInfo[x.filename] = x
702 if self.debug > 2:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000703 print("total", total)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000704
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000705
706 def namelist(self):
Fred Drake484d7352000-10-02 21:14:52 +0000707 """Return a list of file names in the archive."""
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000708 l = []
709 for data in self.filelist:
710 l.append(data.filename)
711 return l
712
713 def infolist(self):
Fred Drake484d7352000-10-02 21:14:52 +0000714 """Return a list of class ZipInfo instances for files in the
715 archive."""
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000716 return self.filelist
717
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000718 def printdir(self, file=None):
Fred Drake484d7352000-10-02 21:14:52 +0000719 """Print a table of contents for the zip file."""
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000720 print("%-46s %19s %12s" % ("File Name", "Modified ", "Size"),
721 file=file)
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000722 for zinfo in self.filelist:
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000723 date = "%d-%02d-%02d %02d:%02d:%02d" % zinfo.date_time[:6]
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000724 print("%-46s %s %12d" % (zinfo.filename, date, zinfo.file_size),
725 file=file)
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000726
727 def testzip(self):
Fred Drake484d7352000-10-02 21:14:52 +0000728 """Read all the files and check the CRC."""
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000729 for zinfo in self.filelist:
730 try:
Tim Peterse1190062001-01-15 03:34:38 +0000731 self.read(zinfo.filename) # Check CRC-32
Raymond Hettingerc0fac962003-06-27 22:25:03 +0000732 except BadZipfile:
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000733 return zinfo.filename
734
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000735
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000736 def getinfo(self, name):
Fred Drake484d7352000-10-02 21:14:52 +0000737 """Return the instance of ZipInfo given 'name'."""
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000738 info = self.NameToInfo.get(name)
739 if info is None:
740 raise KeyError(
741 'There is no item named %r in the archive' % name)
742
743 return info
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000744
Thomas Wouterscf297e42007-02-23 15:07:44 +0000745 def setpassword(self, pwd):
746 """Set default password for encrypted files."""
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000747 assert isinstance(pwd, bytes)
Thomas Wouterscf297e42007-02-23 15:07:44 +0000748 self.pwd = pwd
749
750 def read(self, name, pwd=None):
Fred Drake484d7352000-10-02 21:14:52 +0000751 """Return file bytes (as a string) for name."""
Guido van Rossumd8faa362007-04-27 19:54:29 +0000752 return self.open(name, "r", pwd).read()
753
754 def open(self, name, mode="r", pwd=None):
755 """Return file-like object for 'name'."""
756 if mode not in ("r", "U", "rU"):
Collin Winterce36ad82007-08-30 01:19:48 +0000757 raise RuntimeError('open() requires mode "r", "U", or "rU"')
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000758 if not self.fp:
Collin Winterce36ad82007-08-30 01:19:48 +0000759 raise RuntimeError(
760 "Attempt to read ZIP archive that was already closed")
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000761
Guido van Rossumd8faa362007-04-27 19:54:29 +0000762 # Only open a new file for instances where we were not
763 # given a file object in the constructor
764 if self._filePassed:
765 zef_file = self.fp
766 else:
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000767 zef_file = io.open(self.filename, 'rb')
Guido van Rossumd8faa362007-04-27 19:54:29 +0000768
769 # Get info object for name
770 zinfo = self.getinfo(name)
771
772 filepos = zef_file.tell()
773
774 zef_file.seek(zinfo.header_offset, 0)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000775
776 # Skip the file header:
Guido van Rossumd8faa362007-04-27 19:54:29 +0000777 fheader = zef_file.read(30)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000778 if fheader[0:4] != stringFileHeader:
Collin Winterce36ad82007-08-30 01:19:48 +0000779 raise BadZipfile("Bad magic number for file header")
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000780
781 fheader = struct.unpack(structFileHeader, fheader)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000782 fname = zef_file.read(fheader[_FH_FILENAME_LENGTH])
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000783 if fheader[_FH_EXTRA_FIELD_LENGTH]:
Guido van Rossumd8faa362007-04-27 19:54:29 +0000784 zef_file.read(fheader[_FH_EXTRA_FIELD_LENGTH])
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000785
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000786 if fname != zinfo.orig_filename.encode("utf-8"):
Collin Winterce36ad82007-08-30 01:19:48 +0000787 raise BadZipfile(
788 'File name in directory %r and header %r differ.'
789 % (zinfo.orig_filename, fname))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000790
Guido van Rossumd8faa362007-04-27 19:54:29 +0000791 # check for encrypted flag & handle password
792 is_encrypted = zinfo.flag_bits & 0x1
793 zd = None
Thomas Wouterscf297e42007-02-23 15:07:44 +0000794 if is_encrypted:
Guido van Rossumd8faa362007-04-27 19:54:29 +0000795 if not pwd:
796 pwd = self.pwd
797 if not pwd:
Collin Winterce36ad82007-08-30 01:19:48 +0000798 raise RuntimeError("File %s is encrypted, "
799 "password required for extraction" % name)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000800
Thomas Wouterscf297e42007-02-23 15:07:44 +0000801 zd = _ZipDecrypter(pwd)
802 # The first 12 bytes in the cypher stream is an encryption header
803 # used to strengthen the algorithm. The first 11 bytes are
804 # completely random, while the 12th contains the MSB of the CRC,
Christian Heimesfdab48e2008-01-20 09:06:41 +0000805 # or the MSB of the file time depending on the header type
Thomas Wouterscf297e42007-02-23 15:07:44 +0000806 # and is used to check the correctness of the password.
Guido van Rossumd8faa362007-04-27 19:54:29 +0000807 bytes = zef_file.read(12)
Guido van Rossumc1f779c2007-07-03 08:25:58 +0000808 h = list(map(zd, bytes[0:12]))
Christian Heimesfdab48e2008-01-20 09:06:41 +0000809 if zinfo.flag_bits & 0x8:
810 # compare against the file type from extended local headers
811 check_byte = (zinfo._raw_time >> 8) & 0xff
812 else:
813 # compare against the CRC otherwise
814 check_byte = (zinfo.CRC >> 24) & 0xff
815 if h[11] != check_byte:
816 raise RuntimeError("Bad password for file", name)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000817
818 # build and return a ZipExtFile
819 if zd is None:
820 zef = ZipExtFile(zef_file, zinfo)
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000821 else:
Guido van Rossumd8faa362007-04-27 19:54:29 +0000822 zef = ZipExtFile(zef_file, zinfo, zd)
823
824 # set universal newlines on ZipExtFile if necessary
825 if "U" in mode:
826 zef.set_univ_newlines(True)
827 return zef
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000828
Christian Heimes790c8232008-01-07 21:14:23 +0000829 def extract(self, member, path=None, pwd=None):
830 """Extract a member from the archive to the current working directory,
831 using its full name. Its file information is extracted as accurately
832 as possible. `member' may be a filename or a ZipInfo object. You can
833 specify a different directory using `path'.
834 """
835 if not isinstance(member, ZipInfo):
836 member = self.getinfo(member)
837
838 if path is None:
839 path = os.getcwd()
840
841 return self._extract_member(member, path, pwd)
842
843 def extractall(self, path=None, members=None, pwd=None):
844 """Extract all members from the archive to the current working
845 directory. `path' specifies a different directory to extract to.
846 `members' is optional and must be a subset of the list returned
847 by namelist().
848 """
849 if members is None:
850 members = self.namelist()
851
852 for zipinfo in members:
853 self.extract(zipinfo, path, pwd)
854
855 def _extract_member(self, member, targetpath, pwd):
856 """Extract the ZipInfo object 'member' to a physical
857 file on the path targetpath.
858 """
859 # build the destination pathname, replacing
860 # forward slashes to platform specific separators.
861 if targetpath[-1:] == "/":
862 targetpath = targetpath[:-1]
863
864 # don't include leading "/" from file name if present
865 if os.path.isabs(member.filename):
866 targetpath = os.path.join(targetpath, member.filename[1:])
867 else:
868 targetpath = os.path.join(targetpath, member.filename)
869
870 targetpath = os.path.normpath(targetpath)
871
872 # Create all upper directories if necessary.
873 upperdirs = os.path.dirname(targetpath)
874 if upperdirs and not os.path.exists(upperdirs):
875 os.makedirs(upperdirs)
876
877 source = self.open(member.filename, pwd=pwd)
878 target = open(targetpath, "wb")
879 shutil.copyfileobj(source, target)
880 source.close()
881 target.close()
882
883 return targetpath
884
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000885 def _writecheck(self, zinfo):
Fred Drake484d7352000-10-02 21:14:52 +0000886 """Check for errors before writing a file to the archive."""
Raymond Hettinger54f02222002-06-01 14:18:47 +0000887 if zinfo.filename in self.NameToInfo:
Tim Peterse1190062001-01-15 03:34:38 +0000888 if self.debug: # Warning for duplicate names
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000889 print("Duplicate name:", zinfo.filename)
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000890 if self.mode not in ("w", "a"):
Collin Winterce36ad82007-08-30 01:19:48 +0000891 raise RuntimeError('write() requires mode "w" or "a"')
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000892 if not self.fp:
Collin Winterce36ad82007-08-30 01:19:48 +0000893 raise RuntimeError(
894 "Attempt to write ZIP archive that was already closed")
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000895 if zinfo.compress_type == ZIP_DEFLATED and not zlib:
Collin Winterce36ad82007-08-30 01:19:48 +0000896 raise RuntimeError(
897 "Compression requires the (missing) zlib module")
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000898 if zinfo.compress_type not in (ZIP_STORED, ZIP_DEFLATED):
Collin Winterce36ad82007-08-30 01:19:48 +0000899 raise RuntimeError("That compression method is not supported")
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000900 if zinfo.file_size > ZIP64_LIMIT:
901 if not self._allowZip64:
902 raise LargeZipFile("Filesize would require ZIP64 extensions")
903 if zinfo.header_offset > ZIP64_LIMIT:
904 if not self._allowZip64:
Collin Winterce36ad82007-08-30 01:19:48 +0000905 raise LargeZipFile(
906 "Zipfile size would require ZIP64 extensions")
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000907
908 def write(self, filename, arcname=None, compress_type=None):
Fred Drake484d7352000-10-02 21:14:52 +0000909 """Put the bytes from filename into the archive under the name
910 arcname."""
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000911 if not self.fp:
912 raise RuntimeError(
913 "Attempt to write to ZIP archive that was already closed")
914
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000915 st = os.stat(filename)
Raymond Hettinger32200ae2002-06-01 19:51:15 +0000916 mtime = time.localtime(st.st_mtime)
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000917 date_time = mtime[0:6]
918 # Create ZipInfo instance to store file information
919 if arcname is None:
Georg Brandl8f7c54e2006-02-20 08:40:38 +0000920 arcname = filename
921 arcname = os.path.normpath(os.path.splitdrive(arcname)[1])
922 while arcname[0] in (os.sep, os.altsep):
923 arcname = arcname[1:]
924 zinfo = ZipInfo(arcname, date_time)
Guido van Rossume2a383d2007-01-15 16:59:06 +0000925 zinfo.external_attr = (st[0] & 0xFFFF) << 16 # Unix attributes
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000926 if compress_type is None:
Tim Peterse1190062001-01-15 03:34:38 +0000927 zinfo.compress_type = self.compression
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000928 else:
Tim Peterse1190062001-01-15 03:34:38 +0000929 zinfo.compress_type = compress_type
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000930
931 zinfo.file_size = st.st_size
Finn Bock03a3bb82001-09-05 18:40:33 +0000932 zinfo.flag_bits = 0x00
Tim Peterse1190062001-01-15 03:34:38 +0000933 zinfo.header_offset = self.fp.tell() # Start of header bytes
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000934
935 self._writecheck(zinfo)
936 self._didModify = True
Guido van Rossumd6ca5462007-05-22 01:29:33 +0000937 fp = io.open(filename, "rb")
Finn Bock03a3bb82001-09-05 18:40:33 +0000938 # Must overwrite CRC and sizes with correct data later
939 zinfo.CRC = CRC = 0
940 zinfo.compress_size = compress_size = 0
941 zinfo.file_size = file_size = 0
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000942 self.fp.write(zinfo.FileHeader())
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000943 if zinfo.compress_type == ZIP_DEFLATED:
944 cmpr = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION,
945 zlib.DEFLATED, -15)
946 else:
947 cmpr = None
948 while 1:
949 buf = fp.read(1024 * 8)
950 if not buf:
951 break
952 file_size = file_size + len(buf)
953 CRC = binascii.crc32(buf, CRC)
954 if cmpr:
955 buf = cmpr.compress(buf)
956 compress_size = compress_size + len(buf)
957 self.fp.write(buf)
958 fp.close()
959 if cmpr:
960 buf = cmpr.flush()
961 compress_size = compress_size + len(buf)
962 self.fp.write(buf)
963 zinfo.compress_size = compress_size
964 else:
965 zinfo.compress_size = file_size
966 zinfo.CRC = CRC
967 zinfo.file_size = file_size
Finn Bock03a3bb82001-09-05 18:40:33 +0000968 # Seek backwards and write CRC and file sizes
Tim Petersb64bec32001-09-18 02:26:39 +0000969 position = self.fp.tell() # Preserve current position in file
Finn Bock03a3bb82001-09-05 18:40:33 +0000970 self.fp.seek(zinfo.header_offset + 14, 0)
Brett Cannonff450f72004-07-10 19:09:20 +0000971 self.fp.write(struct.pack("<lLL", zinfo.CRC, zinfo.compress_size,
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000972 zinfo.file_size))
Finn Bock03a3bb82001-09-05 18:40:33 +0000973 self.fp.seek(position, 0)
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000974 self.filelist.append(zinfo)
975 self.NameToInfo[zinfo.filename] = zinfo
976
Guido van Rossum85825dc2007-08-27 17:03:28 +0000977 def writestr(self, zinfo_or_arcname, data):
978 """Write a file into the archive. The contents is 'data', which
979 may be either a 'str' or a 'bytes' instance; if it is a 'str',
980 it is encoded as UTF-8 first.
981 'zinfo_or_arcname' is either a ZipInfo instance or
Just van Rossumb083cb32002-12-12 12:23:32 +0000982 the name of the file in the archive."""
Guido van Rossum85825dc2007-08-27 17:03:28 +0000983 if isinstance(data, str):
984 data = data.encode("utf-8")
Just van Rossumb083cb32002-12-12 12:23:32 +0000985 if not isinstance(zinfo_or_arcname, ZipInfo):
986 zinfo = ZipInfo(filename=zinfo_or_arcname,
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000987 date_time=time.localtime(time.time())[:6])
Just van Rossumb083cb32002-12-12 12:23:32 +0000988 zinfo.compress_type = self.compression
989 else:
990 zinfo = zinfo_or_arcname
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000991
992 if not self.fp:
993 raise RuntimeError(
994 "Attempt to write to ZIP archive that was already closed")
995
Guido van Rossum85825dc2007-08-27 17:03:28 +0000996 zinfo.file_size = len(data) # Uncompressed size
997 zinfo.header_offset = self.fp.tell() # Start of header data
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000998 self._writecheck(zinfo)
999 self._didModify = True
Guido van Rossum85825dc2007-08-27 17:03:28 +00001000 zinfo.CRC = binascii.crc32(data) # CRC-32 checksum
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001001 if zinfo.compress_type == ZIP_DEFLATED:
1002 co = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION,
1003 zlib.DEFLATED, -15)
Guido van Rossum85825dc2007-08-27 17:03:28 +00001004 data = co.compress(data) + co.flush()
1005 zinfo.compress_size = len(data) # Compressed size
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001006 else:
1007 zinfo.compress_size = zinfo.file_size
Guido van Rossum85825dc2007-08-27 17:03:28 +00001008 zinfo.header_offset = self.fp.tell() # Start of header data
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001009 self.fp.write(zinfo.FileHeader())
Guido van Rossum85825dc2007-08-27 17:03:28 +00001010 self.fp.write(data)
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001011 self.fp.flush()
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001012 if zinfo.flag_bits & 0x08:
Tim Peterse1190062001-01-15 03:34:38 +00001013 # Write CRC and file sizes after the file data
Brett Cannonff450f72004-07-10 19:09:20 +00001014 self.fp.write(struct.pack("<lLL", zinfo.CRC, zinfo.compress_size,
Tim Peterse1190062001-01-15 03:34:38 +00001015 zinfo.file_size))
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001016 self.filelist.append(zinfo)
1017 self.NameToInfo[zinfo.filename] = zinfo
1018
1019 def __del__(self):
Fred Drake484d7352000-10-02 21:14:52 +00001020 """Call the "close()" method in case the user forgot."""
Tim Petersd15f8bb2001-11-28 23:16:40 +00001021 self.close()
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001022
1023 def close(self):
Fred Drake484d7352000-10-02 21:14:52 +00001024 """Close the file, and for mode "w" and "a" write the ending
1025 records."""
Tim Petersd15f8bb2001-11-28 23:16:40 +00001026 if self.fp is None:
1027 return
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001028
1029 if self.mode in ("w", "a") and self._didModify: # write ending records
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001030 count = 0
1031 pos1 = self.fp.tell()
Tim Peterse1190062001-01-15 03:34:38 +00001032 for zinfo in self.filelist: # write central directory
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001033 count = count + 1
1034 dt = zinfo.date_time
1035 dosdate = (dt[0] - 1980) << 9 | dt[1] << 5 | dt[2]
Tim Peters3caca232001-12-06 06:23:26 +00001036 dostime = dt[3] << 11 | dt[4] << 5 | (dt[5] // 2)
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001037 extra = []
1038 if zinfo.file_size > ZIP64_LIMIT \
1039 or zinfo.compress_size > ZIP64_LIMIT:
1040 extra.append(zinfo.file_size)
1041 extra.append(zinfo.compress_size)
1042 file_size = 0xffffffff #-1
1043 compress_size = 0xffffffff #-1
1044 else:
1045 file_size = zinfo.file_size
1046 compress_size = zinfo.compress_size
1047
1048 if zinfo.header_offset > ZIP64_LIMIT:
1049 extra.append(zinfo.header_offset)
1050 header_offset = -1 # struct "l" format: 32 one bits
1051 else:
1052 header_offset = zinfo.header_offset
1053
1054 extra_data = zinfo.extra
1055 if extra:
1056 # Append a ZIP64 field to the extra's
1057 extra_data = struct.pack(
1058 '<hh' + 'q'*len(extra),
1059 1, 8*len(extra), *extra) + extra_data
1060
1061 extract_version = max(45, zinfo.extract_version)
1062 create_version = max(45, zinfo.create_version)
1063 else:
1064 extract_version = zinfo.extract_version
1065 create_version = zinfo.create_version
1066
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001067 centdir = struct.pack(structCentralDir,
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001068 stringCentralDir, create_version,
1069 zinfo.create_system, extract_version, zinfo.reserved,
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001070 zinfo.flag_bits, zinfo.compress_type, dostime, dosdate,
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001071 zinfo.CRC, compress_size, file_size,
1072 len(zinfo.filename), len(extra_data), len(zinfo.comment),
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001073 0, zinfo.internal_attr, zinfo.external_attr,
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001074 header_offset)
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001075 self.fp.write(centdir)
Guido van Rossumd6ca5462007-05-22 01:29:33 +00001076 self.fp.write(zinfo.filename.encode("utf-8"))
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001077 self.fp.write(extra_data)
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001078 self.fp.write(zinfo.comment)
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001079
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001080 pos2 = self.fp.tell()
1081 # Write end-of-zip-archive record
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001082 if pos1 > ZIP64_LIMIT:
1083 # Need to write the ZIP64 end-of-archive records
1084 zip64endrec = struct.pack(
1085 structEndArchive64, stringEndArchive64,
1086 44, 45, 45, 0, 0, count, count, pos2 - pos1, pos1)
1087 self.fp.write(zip64endrec)
1088
1089 zip64locrec = struct.pack(
1090 structEndArchive64Locator,
1091 stringEndArchive64Locator, 0, pos2, 1)
1092 self.fp.write(zip64locrec)
1093
1094 # XXX Why is `pos3` computed next? It's never referenced.
1095 pos3 = self.fp.tell()
1096 endrec = struct.pack(structEndArchive, stringEndArchive,
1097 0, 0, count, count, pos2 - pos1, -1, 0)
1098 self.fp.write(endrec)
1099
1100 else:
1101 endrec = struct.pack(structEndArchive, stringEndArchive,
1102 0, 0, count, count, pos2 - pos1, pos1, 0)
1103 self.fp.write(endrec)
Guido van Rossumf85af612001-04-14 16:45:14 +00001104 self.fp.flush()
Fred Drake3d9091e2001-03-26 15:49:24 +00001105 if not self._filePassed:
1106 self.fp.close()
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001107 self.fp = None
1108
1109
1110class PyZipFile(ZipFile):
Fred Drake484d7352000-10-02 21:14:52 +00001111 """Class to create ZIP archives with Python library files and packages."""
1112
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001113 def writepy(self, pathname, basename = ""):
1114 """Add all files from "pathname" to the ZIP archive.
1115
Fred Drake484d7352000-10-02 21:14:52 +00001116 If pathname is a package directory, search the directory and
1117 all package subdirectories recursively for all *.py and enter
1118 the modules into the archive. If pathname is a plain
1119 directory, listdir *.py and enter all modules. Else, pathname
1120 must be a Python *.py file and the module will be put into the
1121 archive. Added modules are always module.pyo or module.pyc.
1122 This method will compile the module.py into module.pyc if
1123 necessary.
1124 """
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001125 dir, name = os.path.split(pathname)
1126 if os.path.isdir(pathname):
1127 initname = os.path.join(pathname, "__init__.py")
1128 if os.path.isfile(initname):
1129 # This is a package directory, add it
1130 if basename:
1131 basename = "%s/%s" % (basename, name)
1132 else:
1133 basename = name
1134 if self.debug:
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001135 print("Adding package in", pathname, "as", basename)
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001136 fname, arcname = self._get_codename(initname[0:-3], basename)
1137 if self.debug:
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001138 print("Adding", arcname)
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001139 self.write(fname, arcname)
1140 dirlist = os.listdir(pathname)
1141 dirlist.remove("__init__.py")
1142 # Add all *.py files and package subdirectories
1143 for filename in dirlist:
1144 path = os.path.join(pathname, filename)
1145 root, ext = os.path.splitext(filename)
1146 if os.path.isdir(path):
1147 if os.path.isfile(os.path.join(path, "__init__.py")):
1148 # This is a package directory, add it
1149 self.writepy(path, basename) # Recursive call
1150 elif ext == ".py":
1151 fname, arcname = self._get_codename(path[0:-3],
1152 basename)
1153 if self.debug:
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001154 print("Adding", arcname)
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001155 self.write(fname, arcname)
1156 else:
1157 # This is NOT a package directory, add its files at top level
1158 if self.debug:
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001159 print("Adding files from directory", pathname)
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001160 for filename in os.listdir(pathname):
1161 path = os.path.join(pathname, filename)
1162 root, ext = os.path.splitext(filename)
1163 if ext == ".py":
1164 fname, arcname = self._get_codename(path[0:-3],
1165 basename)
1166 if self.debug:
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001167 print("Adding", arcname)
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001168 self.write(fname, arcname)
1169 else:
1170 if pathname[-3:] != ".py":
Collin Winterce36ad82007-08-30 01:19:48 +00001171 raise RuntimeError(
1172 'Files added with writepy() must end with ".py"')
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001173 fname, arcname = self._get_codename(pathname[0:-3], basename)
1174 if self.debug:
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001175 print("Adding file", arcname)
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001176 self.write(fname, arcname)
1177
1178 def _get_codename(self, pathname, basename):
1179 """Return (filename, archivename) for the path.
1180
Fred Drake484d7352000-10-02 21:14:52 +00001181 Given a module name path, return the correct file path and
1182 archive name, compiling if necessary. For example, given
1183 /python/lib/string, return (/python/lib/string.pyc, string).
1184 """
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001185 file_py = pathname + ".py"
1186 file_pyc = pathname + ".pyc"
1187 file_pyo = pathname + ".pyo"
1188 if os.path.isfile(file_pyo) and \
Raymond Hettinger32200ae2002-06-01 19:51:15 +00001189 os.stat(file_pyo).st_mtime >= os.stat(file_py).st_mtime:
Tim Peterse1190062001-01-15 03:34:38 +00001190 fname = file_pyo # Use .pyo file
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001191 elif not os.path.isfile(file_pyc) or \
Raymond Hettinger32200ae2002-06-01 19:51:15 +00001192 os.stat(file_pyc).st_mtime < os.stat(file_py).st_mtime:
Fred Drake484d7352000-10-02 21:14:52 +00001193 import py_compile
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001194 if self.debug:
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001195 print("Compiling", file_py)
Martin v. Löwis0c6774d2003-01-15 11:51:06 +00001196 try:
1197 py_compile.compile(file_py, file_pyc, None, True)
Guido van Rossumb940e112007-01-10 16:19:56 +00001198 except py_compile.PyCompileError as err:
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001199 print(err.msg)
Guido van Rossum32abe6f2000-03-31 17:30:02 +00001200 fname = file_pyc
1201 else:
1202 fname = file_pyc
1203 archivename = os.path.split(fname)[1]
1204 if basename:
1205 archivename = "%s/%s" % (basename, archivename)
1206 return (fname, archivename)
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001207
1208
1209def main(args = None):
1210 import textwrap
1211 USAGE=textwrap.dedent("""\
1212 Usage:
1213 zipfile.py -l zipfile.zip # Show listing of a zipfile
1214 zipfile.py -t zipfile.zip # Test if a zipfile is valid
1215 zipfile.py -e zipfile.zip target # Extract zipfile into target dir
1216 zipfile.py -c zipfile.zip src ... # Create zipfile from sources
1217 """)
1218 if args is None:
1219 args = sys.argv[1:]
1220
1221 if not args or args[0] not in ('-l', '-c', '-e', '-t'):
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001222 print(USAGE)
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001223 sys.exit(1)
1224
1225 if args[0] == '-l':
1226 if len(args) != 2:
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001227 print(USAGE)
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001228 sys.exit(1)
1229 zf = ZipFile(args[1], 'r')
1230 zf.printdir()
1231 zf.close()
1232
1233 elif args[0] == '-t':
1234 if len(args) != 2:
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001235 print(USAGE)
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001236 sys.exit(1)
1237 zf = ZipFile(args[1], 'r')
1238 zf.testzip()
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001239 print("Done testing")
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001240
1241 elif args[0] == '-e':
1242 if len(args) != 3:
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001243 print(USAGE)
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001244 sys.exit(1)
1245
1246 zf = ZipFile(args[1], 'r')
1247 out = args[2]
1248 for path in zf.namelist():
1249 if path.startswith('./'):
1250 tgt = os.path.join(out, path[2:])
1251 else:
1252 tgt = os.path.join(out, path)
1253
1254 tgtdir = os.path.dirname(tgt)
1255 if not os.path.exists(tgtdir):
1256 os.makedirs(tgtdir)
Guido van Rossumd6ca5462007-05-22 01:29:33 +00001257 fp = io.open(tgt, 'wb')
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001258 fp.write(zf.read(path))
1259 fp.close()
1260 zf.close()
1261
1262 elif args[0] == '-c':
1263 if len(args) < 3:
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001264 print(USAGE)
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001265 sys.exit(1)
1266
1267 def addToZip(zf, path, zippath):
1268 if os.path.isfile(path):
1269 zf.write(path, zippath, ZIP_DEFLATED)
1270 elif os.path.isdir(path):
1271 for nm in os.listdir(path):
1272 addToZip(zf,
1273 os.path.join(path, nm), os.path.join(zippath, nm))
1274 # else: ignore
1275
1276 zf = ZipFile(args[1], 'w', allowZip64=True)
1277 for src in args[2:]:
1278 addToZip(zf, src, os.path.basename(src))
1279
1280 zf.close()
1281
1282if __name__ == "__main__":
1283 main()