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
50 /// This class represents a Zip archive. You can ask for the contained
\r
51 /// entries, or get an input stream for a file entry. The entry is
\r
52 /// automatically decompressed.
\r
54 /// This class is thread safe: You can open input streams for arbitrary
\r
55 /// entries in different threads.
\r
57 /// author of the original java version : Jochen Hoenicke
\r
61 /// using System.Text;
\r
62 /// using System.Collections;
\r
63 /// using System.IO;
\r
65 /// using NZlib.Zip;
\r
69 /// static public void Main(string[] args)
\r
71 /// ZipFile zFile = new ZipFile(args[0]);
\r
72 /// //Console.WriteLine("Listing of : " + zFile.Name);
\r
73 /// //Console.WriteLine("");
\r
74 /// //Console.WriteLine("Raw Size Size Date Time Name");
\r
75 /// //Console.WriteLine("-------- -------- -------- ------ ---------");
\r
76 /// foreach (ZipEntry e in zFile) {
\r
77 /// DateTime d = e.DateTime;
\r
78 /// //Console.WriteLine("{0, -10}{1, -10}{2} {3} {4}", e.Size, e.CompressedSize,
\r
79 /// d.ToString("dd-MM-yy"), d.ToString("t"),
\r
85 public class ZipFile : IEnumerable
\r
93 /// Opens a Zip file with the given name for reading.
\r
95 /// <exception name="System.IO.IOException">
\r
96 /// IOException if a i/o error occured.
\r
98 /// <exception name="ICSharpCode.SharpZipLib.ZipException">
\r
99 /// if the file doesn't contain a valid zip archive.
\r
101 public ZipFile(string name) : this(File.OpenRead(name))
\r
106 /// Opens a Zip file reading the given FileStream
\r
108 /// <exception name="System.IO.IOException">
\r
109 /// IOException if a i/o error occured.
\r
111 /// <exception name="ICSharpCode.SharpZipLib.ZipException">
\r
112 /// if the file doesn't contain a valid zip archive.
\r
114 public ZipFile(FileStream file)
\r
116 this.baseStream = file;
\r
117 this.name = file.Name;
\r
122 /// Opens a Zip file reading the given Stream
\r
124 /// <exception name="System.IO.IOException">
\r
125 /// IOException if a i/o error occured.
\r
127 /// <exception name="ICSharpCode.SharpZipLib.ZipException">
\r
128 /// if the file doesn't contain a valid zip archive.
\r
130 public ZipFile(Stream baseStream)
\r
132 this.baseStream = baseStream;
\r
139 /// Read an unsigned short in little endian byte order.
\r
141 /// <exception name="System.IO.IOException">
\r
142 /// if a i/o error occured.
\r
144 /// <exception name="System.IO.EndOfStreamException">
\r
145 /// if the file ends prematurely
\r
149 return baseStream.ReadByte() | baseStream.ReadByte() << 8;
\r
153 /// Read an int in little endian byte order.
\r
155 /// <exception name="System.IO.IOException">
\r
156 /// if a i/o error occured.
\r
158 /// <exception name="System.IO.EndOfStreamException">
\r
159 /// if the file ends prematurely
\r
163 return ReadLeShort() | ReadLeShort() << 16;
\r
167 /// Read the central directory of a zip file and fill the entries
\r
168 /// array. This is called exactly once by the constructors.
\r
170 /// <exception name="System.IO.IOException">
\r
171 /// if a i/o error occured.
\r
173 /// <exception name="ICSharpCode.SharpZipLib.ZipException">
\r
174 /// if the central directory is malformed
\r
178 /* Search for the End Of Central Directory. When a zip comment is
\r
179 * present the directory may start earlier.
\r
180 * FIXME: This searches the whole file in a very slow manner if the
\r
181 * file isn't a zip file.
\r
183 long pos = baseStream.Length - ZipConstants.ENDHDR;
\r
186 throw new ZipException("central directory not found, probably not a zip file");
\r
188 baseStream.Seek(pos--, SeekOrigin.Begin);
\r
189 } while (ReadLeInt() != ZipConstants.ENDSIG);
\r
191 long oldPos = baseStream.Position;
\r
192 baseStream.Position += ZipConstants.ENDTOT - ZipConstants.ENDNRD;
\r
194 if (baseStream.Position - oldPos != ZipConstants.ENDTOT - ZipConstants.ENDNRD) {
\r
195 throw new EndOfStreamException();
\r
197 int count = ReadLeShort();
\r
199 oldPos = baseStream.Position;
\r
200 baseStream.Position += ZipConstants.ENDOFF - ZipConstants.ENDSIZ;
\r
202 if (baseStream.Position - oldPos != ZipConstants.ENDOFF - ZipConstants.ENDSIZ) {
\r
203 throw new EndOfStreamException();
\r
206 int centralOffset = ReadLeInt();
\r
208 // GET COMMENT SIZE (COMES AFTER CENTRALOFFSET)
\r
209 int commentSize = ReadLeShort();
\r
210 byte[] zipComment = new byte[commentSize];
\r
211 baseStream.Read(zipComment, 0, zipComment.Length);
\r
212 comment = ZipConstants.ConvertToString(zipComment);
\r
214 entries = new ZipEntry[count];
\r
215 baseStream.Seek(centralOffset, SeekOrigin.Begin);
\r
216 for (int i = 0; i < count; i++) {
\r
217 if (ReadLeInt() != ZipConstants.CENSIG) {
\r
218 throw new ZipException("Wrong Central Directory signature");
\r
221 oldPos = baseStream.Position;
\r
222 baseStream.Position += ZipConstants.CENHOW - ZipConstants.CENVEM;
\r
224 if (baseStream.Position - oldPos != ZipConstants.CENHOW - ZipConstants.CENVEM) {
\r
225 throw new EndOfStreamException();
\r
227 int method = ReadLeShort();
\r
228 int dostime = ReadLeInt();
\r
229 int crc = ReadLeInt();
\r
230 int csize = ReadLeInt();
\r
231 int size = ReadLeInt();
\r
232 int nameLen = ReadLeShort();
\r
233 int extraLen = ReadLeShort();
\r
234 int commentLen = ReadLeShort();
\r
236 oldPos = baseStream.Position;
\r
237 baseStream.Position += ZipConstants.CENOFF - ZipConstants.CENDSK;
\r
238 if (baseStream.Position - oldPos != ZipConstants.CENOFF - ZipConstants.CENDSK) {
\r
239 throw new EndOfStreamException();
\r
241 int offset = ReadLeInt();
\r
243 byte[] buffer = new byte[Math.Max(nameLen, commentLen)];
\r
245 baseStream.Read(buffer, 0, nameLen);
\r
246 string name = ZipConstants.ConvertToString(buffer);
\r
248 ZipEntry entry = new ZipEntry(name);
\r
249 entry.CompressionMethod = (CompressionMethod)method;
\r
250 entry.Crc = crc & 0xffffffffL;
\r
251 entry.Size = size & 0xffffffffL;
\r
252 entry.CompressedSize = csize & 0xffffffffL;
\r
253 entry.DosTime = (uint)dostime;
\r
254 if (extraLen > 0) {
\r
255 byte[] extra = new byte[extraLen];
\r
256 baseStream.Read(extra, 0, extraLen);
\r
257 entry.ExtraData = extra;
\r
259 if (commentLen > 0) {
\r
260 baseStream.Read(buffer, 0, commentLen);
\r
261 entry.Comment = ZipConstants.ConvertToString(buffer);
\r
263 entry.ZipFileIndex = i;
\r
264 entry.Offset = offset;
\r
265 entries[i] = entry;
\r
270 /// Closes the ZipFile. This also closes all input streams given by
\r
271 /// this class. After this is called, no further method should be
\r
274 /// <exception name="System.IO.IOException">
\r
275 /// if a i/o error occured.
\r
277 public void Close()
\r
281 baseStream.Close();
\r
286 /// Returns an IEnumerator of all Zip entries in this Zip file.
\r
288 public IEnumerator GetEnumerator()
\r
290 if (entries == null) {
\r
291 throw new InvalidOperationException("ZipFile has closed");
\r
294 return new ZipEntryEnumeration(entries);
\r
297 int GetEntryIndex(string name)
\r
299 for (int i = 0; i < entries.Length; i++) {
\r
300 if (name.Equals(entries[i].Name)) {
\r
308 /// Searches for a zip entry in this archive with the given name.
\r
310 /// <param name="name">
\r
311 /// the name. May contain directory components separated by slashes ('/').
\r
314 /// the zip entry, or null if no entry with that name exists.
\r
316 public ZipEntry GetEntry(string name)
\r
318 if (entries == null) {
\r
319 throw new InvalidOperationException("ZipFile has closed");
\r
321 int index = GetEntryIndex(name);
\r
322 return index >= 0 ? (ZipEntry) entries[index].Clone() : null;
\r
326 /// Checks, if the local header of the entry at index i matches the
\r
327 /// central directory, and returns the offset to the data.
\r
330 /// the start offset of the (compressed) data.
\r
332 /// <exception name="System.IO.IOException">
\r
333 /// if a i/o error occured.
\r
335 /// <exception name="ICSharpCode.SharpZipLib.ZipException">
\r
336 /// if the local header doesn't match the central directory header
\r
338 long CheckLocalHeader(ZipEntry entry)
\r
341 baseStream.Seek(entry.Offset, SeekOrigin.Begin);
\r
342 if (ReadLeInt() != ZipConstants.LOCSIG) {
\r
343 throw new ZipException("Wrong Local header signature");
\r
346 /* skip version and flags */
\r
347 long oldPos = baseStream.Position;
\r
348 baseStream.Position += ZipConstants.LOCHOW - ZipConstants.LOCVER;
\r
349 if (baseStream.Position - oldPos != ZipConstants.LOCHOW - ZipConstants.LOCVER) {
\r
350 throw new EndOfStreamException();
\r
353 if (entry.CompressionMethod != (CompressionMethod)ReadLeShort()) {
\r
354 throw new ZipException("Compression method mismatch");
\r
357 /* Skip time, crc, size and csize */
\r
358 oldPos = baseStream.Position;
\r
359 baseStream.Position += ZipConstants.LOCNAM - ZipConstants.LOCTIM;
\r
361 if (baseStream.Position - oldPos != ZipConstants.LOCNAM - ZipConstants.LOCTIM) {
\r
362 throw new EndOfStreamException();
\r
365 if (entry.Name.Length != ReadLeShort()) {
\r
366 throw new ZipException("file name length mismatch");
\r
369 int extraLen = entry.Name.Length + ReadLeShort();
\r
370 return entry.Offset + ZipConstants.LOCHDR + extraLen;
\r
375 /// Creates an input stream reading the given zip entry as
\r
376 /// uncompressed data. Normally zip entry should be an entry
\r
377 /// returned by GetEntry().
\r
380 /// the input stream.
\r
382 /// <exception name="System.IO.IOException">
\r
383 /// if a i/o error occured.
\r
385 /// <exception name="ICSharpCode.SharpZipLib.ZipException">
\r
386 /// if the Zip archive is malformed.
\r
388 public Stream GetInputStream(ZipEntry entry)
\r
390 if (entries == null) {
\r
391 throw new InvalidOperationException("ZipFile has closed");
\r
394 int index = entry.ZipFileIndex;
\r
395 if (index < 0 || index >= entries.Length || entries[index].Name != entry.Name) {
\r
396 index = GetEntryIndex(entry.Name);
\r
398 throw new IndexOutOfRangeException();
\r
402 long start = CheckLocalHeader(entries[index]);
\r
403 CompressionMethod method = entries[index].CompressionMethod;
\r
404 Stream istr = new PartialInputStream(baseStream, start, entries[index].CompressedSize);
\r
406 case CompressionMethod.Stored:
\r
408 case CompressionMethod.Deflated:
\r
409 return new InflaterInputStream(istr, new Inflater(true));
\r
411 throw new ZipException("Unknown compression method " + method);
\r
416 /// The comment for the whole zip file.
\r
418 public string ZipFileComment {
\r
425 /// Returns the name of this zip file.
\r
427 public string Name {
\r
434 /// Returns the number of entries in this zip file.
\r
439 return entries.Length;
\r
440 } catch (Exception) {
\r
441 throw new InvalidOperationException("ZipFile has closed");
\r
446 class ZipEntryEnumeration : IEnumerator
\r
451 public ZipEntryEnumeration(ZipEntry[] arr)
\r
456 public object Current {
\r
462 public void Reset()
\r
467 public bool MoveNext()
\r
469 return (++ptr < array.Length);
\r
473 class PartialInputStream : InflaterInputStream
\r
478 public PartialInputStream(Stream baseStream, long start, long len) : base(baseStream)
\r
480 this.baseStream = baseStream;
\r
485 public override int Available
\r
488 long amount = end - filepos;
\r
489 if (amount > Int32.MaxValue) {
\r
490 return Int32.MaxValue;
\r
493 return (int) amount;
\r
497 public override int ReadByte()
\r
499 if (filepos == end) {
\r
504 baseStream.Seek(filepos++, SeekOrigin.Begin);
\r
505 return baseStream.ReadByte();
\r
509 public override int Read(byte[] b, int off, int len)
\r
511 if (len > end - filepos) {
\r
512 len = (int) (end - filepos);
\r
518 baseStream.Seek(filepos, SeekOrigin.Begin);
\r
519 int count = baseStream.Read(b, off, len);
\r
527 public long SkipBytes(long amount)
\r
530 throw new ArgumentOutOfRangeException();
\r
532 if (amount > end - filepos) {
\r
533 amount = end - filepos;
\r