// ZipFile.java - Read contents of a ZIP file.

/* Copyright (C) 1999  Cygnus Solutions

   This file is part of libgcj.

This software is copyrighted work licensed under the terms of the
Libgcj License.  Please consult the file "LIBGCJ_LICENSE" for
details.  */

package java.util.zip;
import java.io.*;

/* Written using on-line Java Platform 1.2 API Specification
 * and JCL book.
 * Believed complete and correct.
 */

public class ZipFile implements ZipConstants
{
  public ZipFile (String fname) throws IOException
  {
    file = new RandomAccessFile(fname, "r");
    name = fname;
    readDirectory ();
  }

  public ZipFile (File f) throws IOException
  {
    file = new RandomAccessFile(f, "r");
    name = f.getName();
    readDirectory ();
  }

  void readDirectory () throws IOException
  {
    long size = file.length ();
    if (size < ZipConstants.END_CENTRAL_DIR_SIZE)
      throw new ZipException ("zipfile too short");
    // We do not handle a "zipfile comment", which the appnote says can
    // be at the end of a .zip file.  We could handle this by seeking
    // to the beginning and reading forwards.
    file.seek(size - ZipConstants.END_CENTRAL_DIR_SIZE);
    if (file.read() != 'P'
	|| file.read() != 'K'
	|| file.read() != '\005'
	|| file.read() != '\006')
      throw new ZipException("not a valid zipfile");
    file.skipBytes(6);
    numEntries = readu2();
    int dir_size = read4 ();  // Read "size of the central directory".
    file.seek(size - (dir_size + ZipConstants.END_CENTRAL_DIR_SIZE));

    ZipEntry last = null;
    for (int i = 0;  i < numEntries;  i++)
      {
	file.skipBytes(10);
	int method = readu2();
	int modtime = readu2();
	int moddate = readu2();
	int crc = read4();
	int compressedSize = read4();
	int uncompressedSize = read4();
	int filenameLength = readu2();
	int extraLength = readu2();
	int commentLength = readu2();
	int diskNumberStart = readu2();
	int intAttributes = readu2();
	int extAttributes = read4();
	int relativeOffset = read4();
	byte[] bname = new byte[filenameLength];
	file.readFully(bname);
	ZipEntry entry = new ZipEntry(new String(bname, "8859_1"));
	if (extraLength > 0)
	  {
	    byte[] bextra = new byte[extraLength];
	    file.readFully(bextra);
	    entry.extra = bextra;
	  }
	if (commentLength > 0)
	  {
	    byte[] bcomment = new byte[commentLength];
	    file.readFully(bcomment);
	    entry.comment = new String(bcomment, "8859_1");
	  }
	entry.compressedSize = compressedSize;
	entry.size = uncompressedSize;
	entry.crc = (long) crc & 0xffffffffL;
	entry.method = method;
	entry.relativeOffset = relativeOffset;
	entry.time = ZipEntry.timeFromDOS(moddate, modtime);
	if (last == null)
	  entries = entry;
	else
	  last.next = entry;
	last = entry;
      }
  }

  public java.util.Enumeration entries()
  {
    return new ZipEnumeration(this);
  }

  public void close() throws IOException
  {
    file.close();
    entries = null;
    numEntries = 0;
  }

  public ZipEntry getEntry(String name)
  {
    for (ZipEntry entry = entries;  entry != null;  entry = entry.next)
      {
	if (name.equals(entry.getName()))
	  return entry;
      }
    return null;
  }

  public InputStream getInputStream(ZipEntry ze)  throws IOException
  {
    byte[] buffer = new byte[(int) ze.getSize()];
    int data_offset = ZipConstants.LOCAL_FILE_HEADER_SIZE + name.length();
    if (ze.extra != null)
      data_offset += ze.extra.length;
    file.seek(ze.relativeOffset + data_offset);
    file.readFully(buffer);

    InputStream is = new ByteArrayInputStream (buffer);
    if (ze.getMethod() == ZipEntry.DEFLATED)
      is = new InflaterInputStream (is);
    return is;
  }

  public String getName () { return name; }

  private int readu2 () throws IOException
  {
    int byte0 = file.read();
    int byte1 = file.read();
    if (byte0 < 0 || byte1 < 0)
      throw new ZipException (".zip archive ended prematurely");
    return ((byte1 & 0xFF) << 8) | (byte0 & 0xFF);
  }

  private int read4 () throws IOException
  {
    int byte0 = file.read();
    int byte1 = file.read();
    int byte2 = file.read();
    int byte3 = file.read();
    if (byte3 < 0)
      throw new ZipException (".zip archive ended prematurely");
    return ((byte3 & 0xFF) << 24) + ((byte2 & 0xFF) << 16)
      + ((byte1 & 0xFF) << 8) + (byte0 & 0xFF);
  }

  ZipEntry entries;
  int numEntries;
  RandomAccessFile file;
  String name;
}

class ZipEnumeration implements java.util.Enumeration
{
  ZipEntry entry;

  ZipEnumeration (ZipFile zfile)
  {
    entry = zfile.entries;
  }

  public boolean hasMoreElements ()
  {
    return entry != null;
  }

  public Object nextElement ()
  {
    ZipEntry cur = entry;
    if (cur == null)
      throw new java.util.NoSuchElementException();
    entry = cur.next;
    return cur;
  }
}
