2 // Copyright (C) 2001 Mike Krueger
\r
4 // This file was translated from java, it was part of the GNU Classpath
\r
5 // Copyright (C) 2001 Free Software Foundation, Inc.
\r
7 // This program is free software; you can redistribute it and/or
\r
8 // modify it under the terms of the GNU General Public License
\r
9 // as published by the Free Software Foundation; either version 2
\r
10 // of the License, or (at your option) any later version.
\r
12 // This program is distributed in the hope that it will be useful,
\r
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
15 // GNU General Public License for more details.
\r
17 // You should have received a copy of the GNU General Public License
\r
18 // along with this program; if not, write to the Free Software
\r
19 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
\r
21 // Linking this library statically or dynamically with other modules is
\r
22 // making a combined work based on this library. Thus, the terms and
\r
23 // conditions of the GNU General Public License cover the whole
\r
26 // As a special exception, the copyright holders of this library give you
\r
27 // permission to link this library with independent modules to produce an
\r
28 // executable, regardless of the license terms of these independent
\r
29 // modules, and to copy and distribute the resulting executable under
\r
30 // terms of your choice, provided that you also meet, for each linked
\r
31 // independent module, the terms and conditions of the license of that
\r
32 // module. An independent module is a module which is not derived from
\r
33 // or based on this library. If you modify this library, you may extend
\r
34 // this exception to your version of the library, but you are not
\r
35 // obligated to do so. If you do not wish to do so, delete this
\r
36 // exception statement from your version.
\r
39 using System.Collections;
\r
43 using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
\r
44 using ICSharpCode.SharpZipLib.Zip.Compression;
\r
46 namespace ICSharpCode.SharpZipLib.Zip {
\r
49 /// This class represents a Zip archive. You can ask for the contained
\r
50 /// entries, or get an input stream for a file entry. The entry is
\r
51 /// automatically decompressed.
\r
53 /// This class is thread safe: You can open input streams for arbitrary
\r
54 /// entries in different threads.
\r
56 /// author of the original java version : Jochen Hoenicke
\r
60 /// using System.Text;
\r
61 /// using System.Collections;
\r
62 /// using System.IO;
\r
64 /// using NZlib.Zip;
\r
68 /// static public void Main(string[] args)
\r
70 /// ZipFile zFile = new ZipFile(args[0]);
\r
71 /// Console.WriteLine("Listing of : " + zFile.Name);
\r
72 /// Console.WriteLine("");
\r
73 /// Console.WriteLine("Raw Size Size Date Time Name");
\r
74 /// Console.WriteLine("-------- -------- -------- ------ ---------");
\r
75 /// foreach (ZipEntry e in zFile) {
\r
76 /// DateTime d = e.DateTime;
\r
77 /// Console.WriteLine("{0, -10}{1, -10}{2} {3} {4}", e.Size, e.CompressedSize,
\r
78 /// d.ToString("dd-MM-yy"), d.ToString("t"),
\r
84 public class ZipFile : IEnumerable
\r
91 /// Opens a Zip file with the given name for reading.
\r
93 /// <exception name="System.IO.IOException">
\r
94 /// IOException if a i/o error occured.
\r
96 /// <exception name="ICSharpCode.SharpZipLib.ZipException">
\r
97 /// if the file doesn't contain a valid zip archive.
\r
99 public ZipFile(string name) : this(File.OpenRead(name))
\r
104 /// Opens a Zip file reading the given FileStream
\r
106 /// <exception name="System.IO.IOException">
\r
107 /// IOException if a i/o error occured.
\r
109 /// <exception name="ICSharpCode.SharpZipLib.ZipException">
\r
110 /// if the file doesn't contain a valid zip archive.
\r
112 public ZipFile(FileStream file)
\r
114 this.baseStream = file;
\r
115 this.name = file.Name;
\r
120 /// Opens a Zip file reading the given Stream
\r
122 /// <exception name="System.IO.IOException">
\r
123 /// IOException if a i/o error occured.
\r
125 /// <exception name="ICSharpCode.SharpZipLib.ZipException">
\r
126 /// if the file doesn't contain a valid zip archive.
\r
128 public ZipFile(Stream baseStream)
\r
130 this.baseStream = baseStream;
\r
137 /// Read an unsigned short in little endian byte order.
\r
139 /// <exception name="System.IO.IOException">
\r
140 /// if a i/o error occured.
\r
142 /// <exception name="System.IO.EndOfStreamException">
\r
143 /// if the file ends prematurely
\r
147 return baseStream.ReadByte() | baseStream.ReadByte() << 8;
\r
151 /// Read an int in little endian byte order.
\r
153 /// <exception name="System.IO.IOException">
\r
154 /// if a i/o error occured.
\r
156 /// <exception name="System.IO.EndOfStreamException">
\r
157 /// if the file ends prematurely
\r
161 return ReadLeShort() | ReadLeShort() << 16;
\r
165 /// Read the central directory of a zip file and fill the entries
\r
166 /// array. This is called exactly once by the constructors.
\r
168 /// <exception name="System.IO.IOException">
\r
169 /// if a i/o error occured.
\r
171 /// <exception name="ICSharpCode.SharpZipLib.ZipException">
\r
172 /// if the central directory is malformed
\r
176 /* Search for the End Of Central Directory. When a zip comment is
\r
177 * present the directory may start earlier.
\r
178 * FIXME: This searches the whole file in a very slow manner if the
\r
179 * file isn't a zip file.
\r
181 long pos = baseStream.Length - ZipConstants.ENDHDR;
\r
184 throw new ZipException("central directory not found, probably not a zip file");
\r
186 baseStream.Seek(pos--, SeekOrigin.Begin);
\r
187 } while (ReadLeInt() != ZipConstants.ENDSIG);
\r
189 long oldPos = baseStream.Position;
\r
190 baseStream.Position += ZipConstants.ENDTOT - ZipConstants.ENDNRD;
\r
192 if (baseStream.Position - oldPos != ZipConstants.ENDTOT - ZipConstants.ENDNRD) {
\r
193 throw new EndOfStreamException();
\r
195 int count = ReadLeShort();
\r
197 oldPos = baseStream.Position;
\r
198 baseStream.Position += ZipConstants.ENDOFF - ZipConstants.ENDSIZ;
\r
200 if (baseStream.Position - oldPos != ZipConstants.ENDOFF - ZipConstants.ENDSIZ) {
\r
201 throw new EndOfStreamException();
\r
204 int centralOffset = ReadLeInt();
\r
206 entries = new ZipEntry[count];
\r
207 baseStream.Seek(centralOffset, SeekOrigin.Begin);
\r
208 for (int i = 0; i < count; i++) {
\r
209 if (ReadLeInt() != ZipConstants.CENSIG) {
\r
210 throw new ZipException("Wrong Central Directory signature");
\r
213 oldPos = baseStream.Position;
\r
214 baseStream.Position += ZipConstants.CENHOW - ZipConstants.CENVEM;
\r
216 if (baseStream.Position - oldPos != ZipConstants.CENHOW - ZipConstants.CENVEM) {
\r
217 throw new EndOfStreamException();
\r
219 int method = ReadLeShort();
\r
220 int dostime = ReadLeInt();
\r
221 int crc = ReadLeInt();
\r
222 int csize = ReadLeInt();
\r
223 int size = ReadLeInt();
\r
224 int nameLen = ReadLeShort();
\r
225 int extraLen = ReadLeShort();
\r
226 int commentLen = ReadLeShort();
\r
228 oldPos = baseStream.Position;
\r
229 baseStream.Position += ZipConstants.CENOFF - ZipConstants.CENDSK;
\r
230 if (baseStream.Position - oldPos != ZipConstants.CENOFF - ZipConstants.CENDSK) {
\r
231 throw new EndOfStreamException();
\r
233 int offset = ReadLeInt();
\r
235 byte[] buffer = new byte[Math.Max(nameLen, commentLen)];
\r
237 baseStream.Read(buffer, 0, nameLen);
\r
238 string name = ZipConstants.ConvertToString(buffer);
\r
240 ZipEntry entry = new ZipEntry(name);
\r
241 entry.CompressionMethod = (CompressionMethod)method;
\r
242 entry.Crc = crc & 0xffffffffL;
\r
243 entry.Size = size & 0xffffffffL;
\r
244 entry.CompressedSize = csize & 0xffffffffL;
\r
245 entry.DosTime = dostime;
\r
246 if (extraLen > 0) {
\r
247 byte[] extra = new byte[extraLen];
\r
248 baseStream.Read(extra, 0, extraLen);
\r
249 entry.ExtraData = extra;
\r
251 if (commentLen > 0) {
\r
252 baseStream.Read(buffer, 0, commentLen);
\r
253 entry.Comment = ZipConstants.ConvertToString(buffer);
\r
255 entry.zipFileIndex = i;
\r
256 entry.offset = offset;
\r
257 entries[i] = entry;
\r
262 /// Closes the ZipFile. This also closes all input streams given by
\r
263 /// this class. After this is called, no further method should be
\r
266 /// <exception name="System.IO.IOException">
\r
267 /// if a i/o error occured.
\r
269 public void Close()
\r
273 baseStream.Close();
\r
278 /// Returns an IEnumerator of all Zip entries in this Zip file.
\r
280 public IEnumerator GetEnumerator()
\r
282 if (entries == null) {
\r
283 throw new InvalidOperationException("ZipFile has closed");
\r
286 return new ZipEntryEnumeration(entries);
\r
289 int GetEntryIndex(string name)
\r
291 for (int i = 0; i < entries.Length; i++) {
\r
292 if (name.Equals(entries[i].Name)) {
\r
300 /// Searches for a zip entry in this archive with the given name.
\r
302 /// <param name="name">
\r
303 /// the name. May contain directory components separated by slashes ('/').
\r
306 /// the zip entry, or null if no entry with that name exists.
\r
308 public ZipEntry GetEntry(string name)
\r
310 if (entries == null) {
\r
311 throw new InvalidOperationException("ZipFile has closed");
\r
313 int index = GetEntryIndex(name);
\r
314 return index >= 0 ? (ZipEntry) entries[index].Clone() : null;
\r
318 /// Checks, if the local header of the entry at index i matches the
\r
319 /// central directory, and returns the offset to the data.
\r
322 /// the start offset of the (compressed) data.
\r
324 /// <exception name="System.IO.IOException">
\r
325 /// if a i/o error occured.
\r
327 /// <exception name="ICSharpCode.SharpZipLib.ZipException">
\r
328 /// if the local header doesn't match the central directory header
\r
330 long CheckLocalHeader(ZipEntry entry)
\r
333 baseStream.Seek(entry.offset, SeekOrigin.Begin);
\r
334 if (ReadLeInt() != ZipConstants.LOCSIG) {
\r
335 throw new ZipException("Wrong Local header signature");
\r
338 /* skip version and flags */
\r
339 long oldPos = baseStream.Position;
\r
340 baseStream.Position += ZipConstants.LOCHOW - ZipConstants.LOCVER;
\r
341 if (baseStream.Position - oldPos != ZipConstants.LOCHOW - ZipConstants.LOCVER) {
\r
342 throw new EndOfStreamException();
\r
345 if (entry.CompressionMethod != (CompressionMethod)ReadLeShort()) {
\r
346 throw new ZipException("Compression method mismatch");
\r
349 /* Skip time, crc, size and csize */
\r
350 oldPos = baseStream.Position;
\r
351 baseStream.Position += ZipConstants.LOCNAM - ZipConstants.LOCTIM;
\r
353 if (baseStream.Position - oldPos != ZipConstants.LOCNAM - ZipConstants.LOCTIM) {
\r
354 throw new EndOfStreamException();
\r
357 if (entry.Name.Length != ReadLeShort()) {
\r
358 throw new ZipException("file name length mismatch");
\r
361 int extraLen = entry.Name.Length + ReadLeShort();
\r
362 return entry.offset + ZipConstants.LOCHDR + extraLen;
\r
367 /// Creates an input stream reading the given zip entry as
\r
368 /// uncompressed data. Normally zip entry should be an entry
\r
369 /// returned by GetEntry().
\r
372 /// the input stream.
\r
374 /// <exception name="System.IO.IOException">
\r
375 /// if a i/o error occured.
\r
377 /// <exception name="ICSharpCode.SharpZipLib.ZipException">
\r
378 /// if the Zip archive is malformed.
\r
380 public Stream GetInputStream(ZipEntry entry)
\r
382 if (entries == null) {
\r
383 throw new InvalidOperationException("ZipFile has closed");
\r
386 int index = entry.zipFileIndex;
\r
387 if (index < 0 || index >= entries.Length || entries[index].Name != entry.Name) {
\r
388 index = GetEntryIndex(entry.Name);
\r
390 throw new IndexOutOfRangeException();
\r
394 long start = CheckLocalHeader(entries[index]);
\r
395 CompressionMethod method = entries[index].CompressionMethod;
\r
396 Stream istr = new PartialInputStream(baseStream, start, entries[index].CompressedSize);
\r
398 case CompressionMethod.Stored:
\r
400 case CompressionMethod.Deflated:
\r
401 return new InflaterInputStream(istr, new Inflater(true));
\r
403 throw new ZipException("Unknown compression method " + method);
\r
408 /// Returns the name of this zip file.
\r
410 public string Name {
\r
417 /// Returns the number of entries in this zip file.
\r
422 return entries.Length;
\r
423 } catch (Exception) {
\r
424 throw new InvalidOperationException("ZipFile has closed");
\r
429 class ZipEntryEnumeration : IEnumerator
\r
434 public ZipEntryEnumeration(ZipEntry[] arr)
\r
439 public object Current {
\r
445 public void Reset()
\r
450 public bool MoveNext()
\r
452 return (++ptr < array.Length);
\r
456 class PartialInputStream : InflaterInputStream
\r
461 public PartialInputStream(Stream baseStream, long start, long len) : base(baseStream)
\r
463 this.baseStream = baseStream;
\r
468 public override int Available {
\r
470 long amount = end - filepos;
\r
471 if (amount > Int32.MaxValue) {
\r
472 return Int32.MaxValue;
\r
475 return (int) amount;
\r
479 public override int ReadByte()
\r
481 if (filepos == end) {
\r
485 baseStream.Seek(filepos++, SeekOrigin.Begin);
\r
486 return baseStream.ReadByte();
\r
490 public override int Read(byte[] b, int off, int len)
\r
492 if (len > end - filepos) {
\r
493 len = (int) (end - filepos);
\r
499 baseStream.Seek(filepos, SeekOrigin.Begin);
\r
500 int count = baseStream.Read(b, off, len);
\r
508 public long SkipBytes(long amount)
\r
511 throw new ArgumentOutOfRangeException();
\r
513 if (amount > end - filepos) {
\r
514 amount = end - filepos;
\r