Milán Major

netscape-revival

1 branch
Code

sun-java/classsrc/netscape/tools/car/Car.java

/*
 * @(#)
 *
 * CAR file reader/writer code
 *
 * @author Kipp E.B. Hickman
 */
package netscape.tools.car;

import java.util.*;
import java.io.*;

public class Car {
    Vector contents;
    Hashtable excludes;
    long tocsize;

    /**
     * Make a brand new empty car
     */
    public Car(Hashtable excludes) {
	contents = new Vector();
	this.excludes = excludes;
    }

    /*
     * Add a file/directory to the car. When given a directory add it's
     * entire contents recursively.
     */
    void add(String name) throws FileNotFoundException, IOException {
	try {
	    File file = new File(name);
	    if (file.isDirectory()) {
		String files[] = file.list();
		if (files != null) {
		    for (int i = 0; i < files.length; i++) {
			add(name + File.separator + files[i]);
		    }
		} else {
		    System.err.println(file.getPath() + " is empty");
		}
	    } else if (file.isFile()) {
		if (!file.canRead()) {
		    System.err.println(file.getPath() + " is unreadable");
		}
		add(file);
	    } else {
		System.err.println(file.getPath() + " is not a file or a directory");
	    }
	} catch (IOException e) {
	    e.printStackTrace();
	    throw new FileNotFoundException(name);
	}
    }

    /*
     * Add a file to the car. Setup an Entry for it.
     */
    void add(File file) throws IOException {
	String name = file.getPath();
	if (excludes.containsKey(name)) {
	    return;
	}
	if (name.endsWith(".class")) {
	    CarTOCEntry e = new CarTOCEntry();
	    e.source = file;
	    e.name = name.startsWith("./") ? name.substring(2) : name;
	    if (excludes.containsKey(e.name)) {
		return;
	    }
	    e.lastModified = file.lastModified();
	    e.length = file.length();
	    e.offset = 0;
	    contents.addElement(e);
	    tocsize = 0;
	}
    }

    void buildTOC() {
	long off = headersize() + tocsize();
	Enumeration e = contents.elements();
	while (e.hasMoreElements()) {
	    CarTOCEntry entry = (CarTOCEntry) e.nextElement();
	    entry.offset = off;
	    off += entry.length;
	}
    }

    int count() { return contents.size(); }

    long tocsize() {
	if (tocsize == 0) {
	    Enumeration e = contents.elements();
	    while (e.hasMoreElements()) {
		CarTOCEntry entry = (CarTOCEntry) e.nextElement();
		tocsize = tocsize + entry.tocsize();
	    }
	}
	return tocsize;
    }

    long headersize() {
	return 4 + 4 + 4 + 8;
    }

    public final int MAGIC = 0xBEBACAFF;
    public final int VERSION = 0x00000002;

    /**
     * Write the car file out
     */
    void write(DataOutputStream out) throws FileNotFoundException, IOException {
	buildTOC();

	// Write out the header
	out.writeInt(MAGIC);
	out.writeInt(VERSION);
	out.writeInt(count());
	out.writeLong(tocsize());

	// Write out the TOC
	writeTOC(out);

	// Write out the contents
	Enumeration e = contents.elements();
	while (e.hasMoreElements()) {
	    CarTOCEntry entry = (CarTOCEntry) e.nextElement();
	    BufferedInputStream in = new BufferedInputStream(
		new FileInputStream(entry.source), 4096);
	    byte buf[] = new byte[4096];
	    int nb;
	    while ((nb = in.read(buf, 0, 4096)) >= 0) {
		out.write(buf, 0, nb);
	    }
	    in.close();
	}
	out.close();
    }

    void writeTOC(DataOutputStream out) throws IOException {
	Enumeration e = contents.elements();
	while (e.hasMoreElements()) {
	    CarTOCEntry entry = (CarTOCEntry) e.nextElement();
	    entry.writeTOC(out);
	}
    }

    void printTOC(PrintStream out, boolean verbose) throws IOException {
	Enumeration e = contents.elements();
	while (e.hasMoreElements()) {
	    CarTOCEntry entry = (CarTOCEntry) e.nextElement();
	    entry.printTOC(out);
	}
    }

    public String toString() {
	return "<car>";
    }

    /**
     * Read in an existing car
     */
    public Car(DataInputStream in) throws IOException {
	int magic = in.readInt();
	if (magic != MAGIC) {
	    throw new IOException("bad magic number " + magic + " (expected "
		+ MAGIC + ")");
	}
	int version = in.readInt();
	if (version != VERSION) {
	    throw new IOException("bad version number " + version + " (expected "
		+ VERSION + ")");
	}
	int entries = in.readInt();
	if (entries < 0) {
	    throw new IOException("bad entries " + entries);
	}
	long length = in.readLong();
	readTOC(in, entries);	// XXX: should be able to check length
    }

    void readTOC(DataInputStream in, int n) throws IOException, UTFDataFormatException {
	contents = new Vector();
	for (int i = 0; i < n; i++) {
	    contents.addElement(new CarTOCEntry(in));
	}
    }

    /**
     * Check a car out (kick the tires)
     */
    boolean check(RandomAccessFile in, String root, boolean verbose) {/* XXX root not used */
	Enumeration e = contents.elements();
	while (e.hasMoreElements()) {
	    CarTOCEntry entry = (CarTOCEntry) e.nextElement();
	    entry.source = new File(entry.name);
	    try {
		if (verbose) {
		    System.out.println("checking " + entry.name);
		}
		entry.check(in);
	    } catch (IOException x) {
		x.printStackTrace();
		System.err.println("IO exception processing " + entry.source.getPath());
		return false;
	    } catch (CarCheckException x) {
		x.printStackTrace();
		System.err.println(entry.source.getPath() + " " + x.getMessage());
		return false;
	    }
	}
	return true;
    }
}