~ubuntu-branches/ubuntu/precise/classpath/precise

« back to all changes in this revision

Viewing changes to java/util/zip/ZipFile.java

  • Committer: Bazaar Package Importer
  • Author(s): Michael Koch
  • Date: 2006-05-27 16:11:15 UTC
  • mfrom: (1.1.3 upstream)
  • Revision ID: james.westby@ubuntu.com-20060527161115-h6e39eposdt5snb6
Tags: 2:0.91-3
* Install header files to /usr/include/classpath.
* debian/control: classpath: Conflict with jamvm < 1.4.3 and
  cacao < 0.96 (Closes: #368172).

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/* ZipFile.java --
2
 
   Copyright (C) 2001, 2002, 2003, 2004, 2005
 
2
   Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006
3
3
   Free Software Foundation, Inc.
4
4
 
5
5
This file is part of GNU Classpath.
41
41
 
42
42
import gnu.java.util.EmptyEnumeration;
43
43
 
44
 
import java.io.BufferedInputStream;
45
 
import java.io.DataInput;
46
44
import java.io.EOFException;
47
45
import java.io.File;
 
46
import java.io.FileNotFoundException;
48
47
import java.io.IOException;
49
48
import java.io.InputStream;
50
49
import java.io.RandomAccessFile;
77
76
   */
78
77
  public static final int OPEN_DELETE = 0x4;
79
78
 
 
79
  /**
 
80
   * This field isn't defined in the JDK's ZipConstants, but should be.
 
81
   */
 
82
  static final int ENDNRD =  4;
 
83
 
80
84
  // Name of this zip file.
81
85
  private final String name;
82
86
 
88
92
 
89
93
  private boolean closed = false;
90
94
 
 
95
 
 
96
  /**
 
97
   * Helper function to open RandomAccessFile and throw the proper
 
98
   * ZipException in case opening the file fails.
 
99
   *
 
100
   * @param name the file name, or null if file is provided
 
101
   *
 
102
   * @param file the file, or null if name is provided
 
103
   *
 
104
   * @return the newly open RandomAccessFile, never null
 
105
   */
 
106
  private RandomAccessFile openFile(String name, 
 
107
                                    File file) 
 
108
    throws ZipException, IOException
 
109
  {                                       
 
110
    try 
 
111
      {
 
112
        return 
 
113
          (name != null)
 
114
          ? new RandomAccessFile(name, "r")
 
115
          : new RandomAccessFile(file, "r");
 
116
      }
 
117
    catch (FileNotFoundException f)
 
118
      { 
 
119
        ZipException ze = new ZipException(f.getMessage());
 
120
        ze.initCause(f);
 
121
        throw ze;
 
122
      }
 
123
  }
 
124
 
 
125
 
91
126
  /**
92
127
   * Opens a Zip file with the given name for reading.
93
128
   * @exception IOException if a i/o error occured.
96
131
   */
97
132
  public ZipFile(String name) throws ZipException, IOException
98
133
  {
99
 
    this.raf = new RandomAccessFile(name, "r");
 
134
    this.raf = openFile(name,null);
100
135
    this.name = name;
101
136
    checkZipFile();
102
137
  }
109
144
   */
110
145
  public ZipFile(File file) throws ZipException, IOException
111
146
  {
112
 
    this.raf = new RandomAccessFile(file, "r");
 
147
    this.raf = openFile(null,file);
113
148
    this.name = file.getPath();
114
149
    checkZipFile();
115
150
  }
136
171
      throw new IllegalArgumentException("invalid mode");
137
172
    if ((mode & OPEN_DELETE) != 0)
138
173
      file.deleteOnExit();
139
 
    this.raf = new RandomAccessFile(file, "r");
 
174
    this.raf = openFile(null,file);
140
175
    this.name = file.getPath();
141
176
    checkZipFile();
142
177
  }
143
178
 
144
 
  private void checkZipFile() throws IOException, ZipException
 
179
  private void checkZipFile() throws ZipException
145
180
  {
146
 
    byte[] magicBuf = new byte[4];
147
 
    boolean validRead = true;
 
181
    boolean valid = false;
148
182
 
149
183
    try 
150
184
      {
151
 
        raf.readFully(magicBuf);
152
 
      } 
153
 
    catch (EOFException eof) 
 
185
        byte[] buf = new byte[4];
 
186
        raf.readFully(buf);
 
187
        int sig = buf[0] & 0xFF
 
188
                | ((buf[1] & 0xFF) << 8)
 
189
                | ((buf[2] & 0xFF) << 16)
 
190
                | ((buf[3] & 0xFF) << 24);
 
191
        valid = sig == LOCSIG;
 
192
      }
 
193
    catch (IOException _)
154
194
      {
155
 
        validRead = false;
156
195
      } 
157
196
 
158
 
    if (validRead == false || readLeInt(magicBuf, 0) != LOCSIG)
 
197
    if (!valid)
159
198
      {
160
 
        raf.close();
 
199
        try
 
200
          {
 
201
            raf.close();
 
202
          }
 
203
        catch (IOException _)
 
204
          {
 
205
          }
161
206
        throw new ZipException("Not a valid zip file");
162
207
      }
163
208
  }
172
217
  }
173
218
 
174
219
  /**
175
 
   * Read an unsigned short in little endian byte order from the given
176
 
   * DataInput stream using the given byte buffer.
177
 
   *
178
 
   * @param di DataInput stream to read from.
179
 
   * @param b the byte buffer to read in (must be at least 2 bytes long).
180
 
   * @return The value read.
181
 
   *
182
 
   * @exception IOException if a i/o error occured.
183
 
   * @exception EOFException if the file ends prematurely
184
 
   */
185
 
  private int readLeShort(DataInput di, byte[] b) throws IOException
186
 
  {
187
 
    di.readFully(b, 0, 2);
188
 
    return (b[0] & 0xff) | (b[1] & 0xff) << 8;
189
 
  }
190
 
 
191
 
  /**
192
 
   * Read an int in little endian byte order from the given
193
 
   * DataInput stream using the given byte buffer.
194
 
   *
195
 
   * @param di DataInput stream to read from.
196
 
   * @param b the byte buffer to read in (must be at least 4 bytes long).
197
 
   * @return The value read.
198
 
   *
199
 
   * @exception IOException if a i/o error occured.
200
 
   * @exception EOFException if the file ends prematurely
201
 
   */
202
 
  private int readLeInt(DataInput di, byte[] b) throws IOException
203
 
  {
204
 
    di.readFully(b, 0, 4);
205
 
    return ((b[0] & 0xff) | (b[1] & 0xff) << 8)
206
 
            | ((b[2] & 0xff) | (b[3] & 0xff) << 8) << 16;
207
 
  }
208
 
 
209
 
  /**
210
 
   * Read an unsigned short in little endian byte order from the given
211
 
   * byte buffer at the given offset.
212
 
   *
213
 
   * @param b the byte array to read from.
214
 
   * @param off the offset to read from.
215
 
   * @return The value read.
216
 
   */
217
 
  private int readLeShort(byte[] b, int off)
218
 
  {
219
 
    return (b[off] & 0xff) | (b[off+1] & 0xff) << 8;
220
 
  }
221
 
 
222
 
  /**
223
 
   * Read an int in little endian byte order from the given
224
 
   * byte buffer at the given offset.
225
 
   *
226
 
   * @param b the byte array to read from.
227
 
   * @param off the offset to read from.
228
 
   * @return The value read.
229
 
   */
230
 
  private int readLeInt(byte[] b, int off)
231
 
  {
232
 
    return ((b[off] & 0xff) | (b[off+1] & 0xff) << 8)
233
 
            | ((b[off+2] & 0xff) | (b[off+3] & 0xff) << 8) << 16;
234
 
  }
235
 
  
236
 
 
237
 
  /**
238
220
   * Read the central directory of a zip file and fill the entries
239
221
   * array.  This is called exactly once when first needed. It is called
240
222
   * while holding the lock on <code>raf</code>.
246
228
  {
247
229
    /* Search for the End Of Central Directory.  When a zip comment is 
248
230
     * present the directory may start earlier.
249
 
     * FIXME: This searches the whole file in a very slow manner if the
250
 
     * file isn't a zip file.
 
231
     * Note that a comment has a maximum length of 64K, so that is the
 
232
     * maximum we search backwards.
251
233
     */
 
234
    PartialInputStream inp = new PartialInputStream(raf, 4096);
252
235
    long pos = raf.length() - ENDHDR;
253
 
    byte[] ebs  = new byte[CENHDR];
254
 
    
 
236
    long top = Math.max(0, pos - 65536);
255
237
    do
256
238
      {
257
 
        if (pos < 0)
 
239
        if (pos < top)
258
240
          throw new ZipException
259
241
            ("central directory not found, probably not a zip file: " + name);
260
 
        raf.seek(pos--);
 
242
        inp.seek(pos--);
261
243
      }
262
 
    while (readLeInt(raf, ebs) != ENDSIG);
 
244
    while (inp.readLeInt() != ENDSIG);
263
245
    
264
 
    if (raf.skipBytes(ENDTOT - ENDNRD) != ENDTOT - ENDNRD)
265
 
      throw new EOFException(name);
266
 
    int count = readLeShort(raf, ebs);
267
 
    if (raf.skipBytes(ENDOFF - ENDSIZ) != ENDOFF - ENDSIZ)
268
 
      throw new EOFException(name);
269
 
    int centralOffset = readLeInt(raf, ebs);
 
246
    if (inp.skip(ENDTOT - ENDNRD) != ENDTOT - ENDNRD)
 
247
      throw new EOFException(name);
 
248
    int count = inp.readLeShort();
 
249
    if (inp.skip(ENDOFF - ENDSIZ) != ENDOFF - ENDSIZ)
 
250
      throw new EOFException(name);
 
251
    int centralOffset = inp.readLeInt();
270
252
 
271
253
    entries = new HashMap(count+count/2);
272
 
    raf.seek(centralOffset);
 
254
    inp.seek(centralOffset);
273
255
    
274
 
    byte[] buffer = new byte[16];
275
256
    for (int i = 0; i < count; i++)
276
257
      {
277
 
        raf.readFully(ebs);
278
 
        if (readLeInt(ebs, 0) != CENSIG)
 
258
        if (inp.readLeInt() != CENSIG)
279
259
          throw new ZipException("Wrong Central Directory signature: " + name);
280
260
 
281
 
        int method = readLeShort(ebs, CENHOW);
282
 
        int dostime = readLeInt(ebs, CENTIM);
283
 
        int crc = readLeInt(ebs, CENCRC);
284
 
        int csize = readLeInt(ebs, CENSIZ);
285
 
        int size = readLeInt(ebs, CENLEN);
286
 
        int nameLen = readLeShort(ebs, CENNAM);
287
 
        int extraLen = readLeShort(ebs, CENEXT);
288
 
        int commentLen = readLeShort(ebs, CENCOM);
289
 
        
290
 
        int offset = readLeInt(ebs, CENOFF);
291
 
 
292
 
        int needBuffer = Math.max(nameLen, commentLen);
293
 
        if (buffer.length < needBuffer)
294
 
          buffer = new byte[needBuffer];
295
 
 
296
 
        raf.readFully(buffer, 0, nameLen);
297
 
        String name;
298
 
        try
299
 
          {
300
 
            name = new String(buffer, 0, nameLen, "UTF-8");
301
 
          }
302
 
        catch (UnsupportedEncodingException uee)
303
 
          {
304
 
            throw new AssertionError(uee);
305
 
          }
 
261
        inp.skip(6);
 
262
        int method = inp.readLeShort();
 
263
        int dostime = inp.readLeInt();
 
264
        int crc = inp.readLeInt();
 
265
        int csize = inp.readLeInt();
 
266
        int size = inp.readLeInt();
 
267
        int nameLen = inp.readLeShort();
 
268
        int extraLen = inp.readLeShort();
 
269
        int commentLen = inp.readLeShort();
 
270
        inp.skip(8);
 
271
        int offset = inp.readLeInt();
 
272
        String name = inp.readString(nameLen);
306
273
 
307
274
        ZipEntry entry = new ZipEntry(name);
308
275
        entry.setMethod(method);
313
280
        if (extraLen > 0)
314
281
          {
315
282
            byte[] extra = new byte[extraLen];
316
 
            raf.readFully(extra);
 
283
            inp.readFully(extra);
317
284
            entry.setExtra(extra);
318
285
          }
319
286
        if (commentLen > 0)
320
287
          {
321
 
            raf.readFully(buffer, 0, commentLen);
322
 
            try
323
 
              {
324
 
                entry.setComment(new String(buffer, 0, commentLen, "UTF-8"));
325
 
              }
326
 
            catch (UnsupportedEncodingException uee)
327
 
              {
328
 
                throw new AssertionError(uee);
329
 
              }
 
288
            entry.setComment(inp.readString(commentLen));
330
289
          }
331
290
        entry.offset = offset;
332
291
        entries.put(name, entry);
429
388
      }
430
389
  }
431
390
 
432
 
 
433
 
  //access should be protected by synchronized(raf)
434
 
  private byte[] locBuf = new byte[LOCHDR];
435
 
 
436
 
  /**
437
 
   * Checks, if the local header of the entry at index i matches the
438
 
   * central directory, and returns the offset to the data.
439
 
   * 
440
 
   * @param entry to check.
441
 
   * @return the start offset of the (compressed) data.
442
 
   *
443
 
   * @exception IOException if a i/o error occured.
444
 
   * @exception ZipException if the local header doesn't match the 
445
 
   * central directory header
446
 
   */
447
 
  private long checkLocalHeader(ZipEntry entry) throws IOException
448
 
  {
449
 
    synchronized (raf)
450
 
      {
451
 
        raf.seek(entry.offset);
452
 
        raf.readFully(locBuf);
453
 
        
454
 
        if (readLeInt(locBuf, 0) != LOCSIG)
455
 
          throw new ZipException("Wrong Local header signature: " + name);
456
 
 
457
 
        if (entry.getMethod() != readLeShort(locBuf, LOCHOW))
458
 
          throw new ZipException("Compression method mismatch: " + name);
459
 
 
460
 
        if (entry.getName().length() != readLeShort(locBuf, LOCNAM))
461
 
          throw new ZipException("file name length mismatch: " + name);
462
 
 
463
 
        int extraLen = entry.getName().length() + readLeShort(locBuf, LOCEXT);
464
 
        return entry.offset + LOCHDR + extraLen;
465
 
      }
466
 
  }
467
 
 
468
391
  /**
469
392
   * Creates an input stream reading the given zip entry as
470
393
   * uncompressed data.  Normally zip entry should be an entry
497
420
    if (zipEntry == null)
498
421
      return null;
499
422
 
500
 
    long start = checkLocalHeader(zipEntry);
 
423
    PartialInputStream inp = new PartialInputStream(raf, 1024);
 
424
    inp.seek(zipEntry.offset);
 
425
 
 
426
    if (inp.readLeInt() != LOCSIG)
 
427
      throw new ZipException("Wrong Local header signature: " + name);
 
428
 
 
429
    inp.skip(4);
 
430
 
 
431
    if (zipEntry.getMethod() != inp.readLeShort())
 
432
      throw new ZipException("Compression method mismatch: " + name);
 
433
 
 
434
    inp.skip(16);
 
435
 
 
436
    int nameLen = inp.readLeShort();
 
437
    int extraLen = inp.readLeShort();
 
438
    inp.skip(nameLen + extraLen);
 
439
 
 
440
    inp.setLength(zipEntry.getCompressedSize());
 
441
 
501
442
    int method = zipEntry.getMethod();
502
 
    InputStream is = new BufferedInputStream(new PartialInputStream
503
 
      (raf, start, zipEntry.getCompressedSize()));
504
443
    switch (method)
505
444
      {
506
445
      case ZipOutputStream.STORED:
507
 
        return is;
 
446
        return inp;
508
447
      case ZipOutputStream.DEFLATED:
509
 
        return new InflaterInputStream(is, new Inflater(true));
 
448
        final Inflater inf = new Inflater(true);
 
449
        final int sz = (int) entry.getSize();
 
450
        return new InflaterInputStream(inp, inf)
 
451
        {
 
452
          public int available() throws IOException
 
453
          {
 
454
            if (sz == -1)
 
455
              return super.available();
 
456
            if (super.available() != 0)
 
457
              return sz - inf.getTotalOut();
 
458
            return 0;
 
459
          }
 
460
        };
510
461
      default:
511
462
        throw new ZipException("Unknown compression method " + method);
512
463
      }
562
513
    }
563
514
  }
564
515
 
565
 
  private static class PartialInputStream extends InputStream
 
516
  private static final class PartialInputStream extends InputStream
566
517
  {
567
518
    private final RandomAccessFile raf;
568
 
    long filepos, end;
 
519
    private final byte[] buffer;
 
520
    private long bufferOffset;
 
521
    private int pos;
 
522
    private long end;
569
523
 
570
 
    public PartialInputStream(RandomAccessFile raf, long start, long len)
 
524
    public PartialInputStream(RandomAccessFile raf, int bufferSize)
 
525
      throws IOException
571
526
    {
572
527
      this.raf = raf;
573
 
      filepos = start;
574
 
      end = start + len;
 
528
      buffer = new byte[bufferSize];
 
529
      bufferOffset = -buffer.length;
 
530
      pos = buffer.length;
 
531
      end = raf.length();
 
532
    }
 
533
 
 
534
    void setLength(long length)
 
535
    {
 
536
      end = bufferOffset + pos + length;
 
537
    }
 
538
 
 
539
    private void fillBuffer() throws IOException
 
540
    {
 
541
      synchronized (raf)
 
542
        {
 
543
          raf.seek(bufferOffset);
 
544
          raf.readFully(buffer, 0, (int) Math.min(buffer.length, end - bufferOffset));
 
545
        }
575
546
    }
576
547
    
577
548
    public int available()
578
549
    {
579
 
      long amount = end - filepos;
 
550
      long amount = end - (bufferOffset + pos);
580
551
      if (amount > Integer.MAX_VALUE)
581
552
        return Integer.MAX_VALUE;
582
553
      return (int) amount;
584
555
    
585
556
    public int read() throws IOException
586
557
    {
587
 
      if (filepos == end)
 
558
      if (bufferOffset + pos >= end)
588
559
        return -1;
589
 
      synchronized (raf)
590
 
        {
591
 
          raf.seek(filepos++);
592
 
          return raf.read();
593
 
        }
 
560
      if (pos == buffer.length)
 
561
        {
 
562
          bufferOffset += buffer.length;
 
563
          pos = 0;
 
564
          fillBuffer();
 
565
        }
 
566
      
 
567
      return buffer[pos++] & 0xFF;
594
568
    }
595
569
 
596
570
    public int read(byte[] b, int off, int len) throws IOException
597
571
    {
598
 
      if (len > end - filepos)
 
572
      if (len > end - (bufferOffset + pos))
599
573
        {
600
 
          len = (int) (end - filepos);
 
574
          len = (int) (end - (bufferOffset + pos));
601
575
          if (len == 0)
602
576
            return -1;
603
577
        }
604
 
      synchronized (raf)
605
 
        {
606
 
          raf.seek(filepos);
607
 
          int count = raf.read(b, off, len);
608
 
          if (count > 0)
609
 
            filepos += len;
610
 
          return count;
611
 
        }
 
578
 
 
579
      int totalBytesRead = Math.min(buffer.length - pos, len);
 
580
      System.arraycopy(buffer, pos, b, off, totalBytesRead);
 
581
      pos += totalBytesRead;
 
582
      off += totalBytesRead;
 
583
      len -= totalBytesRead;
 
584
 
 
585
      while (len > 0)
 
586
        {
 
587
          bufferOffset += buffer.length;
 
588
          pos = 0;
 
589
          fillBuffer();
 
590
          int remain = Math.min(buffer.length, len);
 
591
          System.arraycopy(buffer, pos, b, off, remain);
 
592
          pos += remain;
 
593
          off += remain;
 
594
          len -= remain;
 
595
          totalBytesRead += remain;
 
596
        }
 
597
      
 
598
      return totalBytesRead;
612
599
    }
613
600
 
614
 
    public long skip(long amount)
 
601
    public long skip(long amount) throws IOException
615
602
    {
616
603
      if (amount < 0)
617
 
        throw new IllegalArgumentException();
618
 
      if (amount > end - filepos)
619
 
        amount = end - filepos;
620
 
      filepos += amount;
 
604
        return 0;
 
605
      if (amount > end - (bufferOffset + pos))
 
606
        amount = end - (bufferOffset + pos);
 
607
      seek(bufferOffset + pos + amount);
621
608
      return amount;
622
609
    }
 
610
 
 
611
    void seek(long newpos) throws IOException
 
612
    {
 
613
      long offset = newpos - bufferOffset;
 
614
      if (offset >= 0 && offset <= buffer.length)
 
615
        {
 
616
          pos = (int) offset;
 
617
        }
 
618
      else
 
619
        {
 
620
          bufferOffset = newpos;
 
621
          pos = 0;
 
622
          fillBuffer();
 
623
        }
 
624
    }
 
625
 
 
626
    void readFully(byte[] buf) throws IOException
 
627
    {
 
628
      if (read(buf, 0, buf.length) != buf.length)
 
629
        throw new EOFException();
 
630
    }
 
631
 
 
632
    void readFully(byte[] buf, int off, int len) throws IOException
 
633
    {
 
634
      if (read(buf, off, len) != len)
 
635
        throw new EOFException();
 
636
    }
 
637
 
 
638
    int readLeShort() throws IOException
 
639
    {
 
640
      int b0 = read();
 
641
      int b1 = read();
 
642
      if (b1 == -1)
 
643
        throw new EOFException();
 
644
      return (b0 & 0xff) | (b1 & 0xff) << 8;
 
645
    }
 
646
 
 
647
    int readLeInt() throws IOException
 
648
    {
 
649
      int b0 = read();
 
650
      int b1 = read();
 
651
      int b2 = read();
 
652
      int b3 = read();
 
653
      if (b3 == -1)
 
654
        throw new EOFException();
 
655
      return ((b0 & 0xff) | (b1 & 0xff) << 8)
 
656
            | ((b2 & 0xff) | (b3 & 0xff) << 8) << 16;
 
657
    }
 
658
 
 
659
    String readString(int length) throws IOException
 
660
    {
 
661
      if (length > end - (bufferOffset + pos))
 
662
        throw new EOFException();
 
663
 
 
664
      try
 
665
        {
 
666
          if (buffer.length - pos >= length)
 
667
            {
 
668
              String s = new String(buffer, pos, length, "UTF-8");
 
669
              pos += length;
 
670
              return s;
 
671
            }
 
672
          else
 
673
            {
 
674
              byte[] b = new byte[length];
 
675
              readFully(b);
 
676
              return new String(b, 0, length, "UTF-8");
 
677
            }
 
678
        }
 
679
      catch (UnsupportedEncodingException uee)
 
680
        {
 
681
          throw new AssertionError(uee);
 
682
        }
 
683
    }
623
684
  }
624
685
}