blob: 873103730bc05d2a1c3493e616c715fc1867b046 [file] [log] [blame]
package aQute.lib.putjar;
import java.io.*;
import java.util.zip.*;
import aQute.libg.fileiterator.*;
public class DirectoryInputStream extends InputStream {
final File root;
final FileIterator fi;
File element;
int entries = 0;
int state = START;
long where = 0;
final static int START = 0;
final static int HEADER = 1;
final static int DATA = 2;
final static int DIRECTORY = 4;
final static int EOF = 5;
final static InputStream eof = new ByteArrayInputStream(new byte[0]);
ByteArrayOutputStream directory = new ByteArrayOutputStream();
InputStream current = eof;
public DirectoryInputStream(File dir) {
root = dir;
fi = new FileIterator(dir);
}
@Override
public int read() throws IOException {
if (fi == null)
return -1;
int c = current.read();
if (c < 0) {
next();
c = current.read();
}
if (c >= 0)
where++;
return c;
}
void next() throws IOException {
switch (state) {
case START :
case DATA :
nextHeader();
break;
case HEADER :
if (element.isFile() && element.length() > 0) {
current = new FileInputStream(element);
state = DATA;
} else
nextHeader();
break;
case DIRECTORY :
state = EOF;
current = eof;
break;
case EOF :
break;
}
}
private void nextHeader() throws IOException {
if (fi.hasNext()) {
element = fi.next();
state = HEADER;
current = getHeader(root, element);
entries++;
} else {
current = getDirectory();
state = DIRECTORY;
}
}
/**
* <pre>
* end of central dir signature 4 bytes (0x06054b50)
* number of this disk 2 bytes
* number of the disk with the
* start of the central directory 2 bytes
* total number of entries in the
* central directory on this disk 2 bytes
* total number of entries in
* the central directory 2 bytes
* size of the central directory 4 bytes
* offset of start of central
* directory with respect to
* the starting disk number 4 bytes
* .ZIP file comment length 2 bytes
* .ZIP file comment (variable size)
* </pre>
*
* @return
*/
InputStream getDirectory() throws IOException {
long where = this.where;
int sizeDirectory = directory.size();
writeInt(directory, 0x504b0506); // Signature
writeShort(directory, 0); // # of disk
writeShort(directory, 0); // # of the disk with start of the central
// dir
writeShort(directory, entries); // # of entries
writeInt(directory, sizeDirectory); // Size of central dir
writeInt(directory, (int) where);
writeShort(directory, 0);
directory.close();
byte[] data = directory.toByteArray();
return new ByteArrayInputStream(data);
}
private void writeShort(OutputStream out, int v) throws IOException {
for (int i = 0; i < 2; i++) {
out.write((byte) (v & 0xFF));
v = v >> 8;
}
}
private void writeInt(OutputStream out, int v) throws IOException {
for (int i = 0; i < 4; i++) {
out.write((byte) (v & 0xFF));
v = v >> 8;
}
}
/**
* Local file header:
*
* <pre>
*
* local file header signature 4 bytes (0x04034b50)
* version needed to extract 2 bytes
* general purpose bit flag 2 bytes
* compression method 2 bytes
* last mod file time 2 bytes
* last mod file date 2 bytes
* crc-32 4 bytes
* compressed size 4 bytes
* uncompressed size 4 bytes
* file name length 2 bytes
* extra field length 2 bytes
*
* file name (variable size)
* extra field (variable size)
*
* central file header signature 4 bytes (0x02014b50)
* version made by 2 bytes
* version needed to extract 2 bytes
* general purpose bit flag 2 bytes
* compression method 2 bytes
* last mod file time 2 bytes
* last mod file date 2 bytes
* crc-32 4 bytes
* compressed size 4 bytes
* uncompressed size 4 bytes
* file name length 2 bytes
* extra field length 2 bytes
* file comment length 2 bytes
* disk number start 2 bytes
* internal file attributes 2 bytes
* external file attributes 4 bytes
* relative offset of local header 4 bytes
*
* file name (variable size)
* extra field (variable size)
* file comment (variable size)
* </pre>
*
* </pre>
*
* @param file
* @return
*/
private InputStream getHeader(File root, File file) throws IOException {
long where = this.where;
ByteArrayOutputStream bout = new ByteArrayOutputStream();
// Signature
writeInt(bout, 0x04034b50);
writeInt(directory, 0x504b0102);
// Version needed to extract
writeShort(directory, 0);
// Version needed to extract
writeShort(bout, 10);
writeShort(directory, 10);
// General purpose bit flag (use descriptor)
writeShort(bout, 0); // descriptor follows data
writeShort(directory, 0); // descriptor follows data
// Compresson method (stored)
writeShort(bout, 0);
writeShort(directory, 0);
// Mod time
writeInt(bout, 0);
writeInt(directory, 0);
if (file.isDirectory()) {
writeInt(bout, 0); // CRC
writeInt(bout, 0); // Compressed size
writeInt(bout, 0); // Uncompressed Size
writeInt(directory, 0);
writeInt(directory, 0);
writeInt(directory, 0);
} else {
CRC32 crc = getCRC(file);
writeInt(bout, (int) crc.getValue());
writeInt(bout, (int) file.length());
writeInt(bout, (int) file.length());
writeInt(directory, (int) crc.getValue());
writeInt(directory, (int) file.length());
writeInt(directory, (int) file.length());
}
String p = getPath(root, file);
if (file.isDirectory())
p = p + "/";
byte[] path = p.getBytes("UTF-8");
writeShort(bout, path.length);
writeShort(directory, path.length);
writeShort(bout, 0); // extra length
writeShort(directory, 0);
bout.write(path);
writeShort(directory, 0); // File comment length
writeShort(directory, 0); // disk number start 2 bytes
writeShort(directory, 0); // internal file attributes 2 bytes
writeInt(directory, 0); // external file attributes 4 bytes
writeInt(directory, (int) where); // relative offset of local header 4
// bytes
directory.write(path);
byte[] bytes = bout.toByteArray();
return new ByteArrayInputStream(bytes);
}
private String getPath(File root, File file) {
if (file.equals(root))
return "";
String p = getPath(root, file.getParentFile());
if (p.length() == 0)
p = file.getName();
else {
p = p + "/" + file.getName();
}
return p;
}
private CRC32 getCRC(File file) throws IOException {
CRC32 crc = new CRC32();
FileInputStream in = new FileInputStream(file);
try {
byte data[] = new byte[10000];
int size = in.read(data);
while (size > 0) {
crc.update(data, 0, size);
size = in.read(data);
}
}
finally {
in.close();
}
return crc;
}
}