--- /dev/null 2014-05-19 10:02:58.886376731 -0700
+++ new/make/src/classes/build/tools/tzdb/TarInputStream.java 2014-05-19 10:47:36.000000000 -0700
@@ -0,0 +1,367 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package build.tools.tzdb;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.FilterInputStream;
+import java.io.InputStream;
+
+/**
+ * This class implements an input stream filter for reading files in the
+ * UNIX tar file format.
+ *
+ * @implNote
+ * This class does not handle symbol link, gnu long name and check checksum.
+ *
+ * @author Xueming Shen
+ *
+ * @since 1.9
+ */
+class TarInputStream extends FilterInputStream {
+
+ /** Default block size */
+ private static final int DEFAULT_BLKSIZE = 512;
+
+ private int blockSize;
+ private TarEntry entry; // the current entry
+ private long remaining; // the remaining bytes in current entry
+
+ private boolean closed;
+
+ /**
+ * Creates a new Tar input stream.
+ *
+ * @param is the source input stream to use
+ */
+ public TarInputStream(InputStream is) {
+ this(is, DEFAULT_BLKSIZE);
+ }
+
+ /**
+ * Creates a new Tar input stream.
+ *
+ * @param is the input stream to use
+ * @param blockSize the block size to use
+ */
+ public TarInputStream(InputStream is, int blockSize) {
+ super(is);
+ this.blockSize = blockSize;
+ this.remaining = 0;
+ this.closed = false;
+ }
+
+ /**
+ * Check to make sure that this stream has not been closed
+ */
+ private void ensureOpen() throws IOException {
+ if (closed) {
+ throw new IOException("Input stream closed");
+ }
+ }
+
+ /**
+ * Closes this input stream and releases any system resource associated.
+ *
+ * @throws IOException if an on error has occured
+ */
+ @Override
+ public void close() throws IOException {
+ if (!closed) {
+ super.close();
+ closed = true;
+ }
+ }
+
+ private boolean isEOF(byte[] block) {
+ for (int i = 0; i < block.length; i++) {
+ if (block[i] != 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Skip the specified number of bytes in the current Tar entry.
+ *
+ * @param n the number of bytes to skip.
+ * @return the number actually skipped
+ * @throws IOException if an IO error has occurred
+ * @throws IllegalArgumentException if {@code n < 0}
+ */
+ @Override
+ public long skip(long n) throws IOException {
+ if (n < 0) {
+ throw new IllegalArgumentException("negative skip length");
+ }
+ ensureOpen();
+ int max = (int)Math.min(n, Integer.MAX_VALUE);
+ byte[] tmpbuf = new byte[Math.min(max, 8192)];
+ int total = 0;
+ while (total < max) {
+ int len = max - total;
+ if (len > tmpbuf.length) {
+ len = tmpbuf.length;
+ }
+ len = read(tmpbuf, 0, len);
+ if (len == -1) {
+ break;
+ }
+ total += len;
+ }
+ return total;
+ }
+
+ /**
+ * Reads the next Tar file entry and positions the stream at
+ * the beginning of the entry data.
+ *
+ * @return the next Tar file entry, or null, if there are no
+ * more entries
+ * @throws IOException if an I/O error has occured
+ */
+ public TarEntry getNextEntry() throws IOException {
+ ensureOpen();
+ if (entry != null) {
+ closeEntry();
+ }
+ byte[] header = new byte[blockSize];
+ int off = 0;
+ // read until we get blockSize of bytes or EOF
+ while (off < blockSize) {
+ int n = super.read(header, off, blockSize - off);
+ if (n == -1) { // unexpected EOF,
+ return null;
+ }
+ off += n;
+ }
+ if (isEOF(header)) {
+ return null;
+ }
+ entry = new TarEntry(header);
+ remaining = entry.getSize();
+ return entry;
+ }
+
+ /**
+ * Closes the current Tar entry and positions the stream for reading
+ * the next entry.
+ *
+ * @exception IOException if an I/O error has occurred
+ */
+ public void closeEntry() throws IOException {
+ ensureOpen();
+ byte[] tmpbuf = new byte[blockSize];
+ while (read(tmpbuf, 0, tmpbuf.length) != -1) ;
+ }
+
+ /**
+ * Returns the available data that can be read from the current entry
+ * data.
+ *
+ * @return the number of available bytes for the current entry, 0
+ * if EOF of current entry has reached, or Integer.MAX_VALUE,
+ * if more than Integer.MAX_VALUE is available
+ * @exception IOException if an I/O error occurs.
+ *
+ */
+ public int available() throws IOException {
+ ensureOpen();
+ if (remaining > Integer.MAX_VALUE) {
+ return Integer.MAX_VALUE;
+ }
+ return (int)remaining;
+ }
+
+ /**
+ * Reads bytes from the current Tar entry.
+ *
+ * If {@code len} is not zero, this method blocks until some input is
+ * available; otherwise, no bytes are read and [@code 0} is returned.
+ *
+ * @param b the buffer into which the data is read
+ * @param off the start offset in the destination array b
+ * @param len the maximum number of bytes read
+ * @return the actual number of bytes read, or -1 if the end of the
+ * entry is reached
+ * @throws IOException on error
+ */
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ ensureOpen();
+ if (off < 0 || len < 0 || off > b.length - len) {
+ throw new IndexOutOfBoundsException();
+ } else if (len == 0) {
+ return 0;
+ }
+ if (entry == null && remaining <= 0) {
+ return -1;
+ }
+ if (len > remaining) {
+ len = (int)remaining;
+ }
+ int n = super.read(b, off, len);
+ if (n == -1) {
+ entry = null;
+ return -1; // throw new IOException("unexpected EOF");
+ }
+ remaining -= n;
+ if (remaining == 0) {
+ int leftover = blockSize - (int)(entry.getSize() % blockSize);
+ if (leftover > 0) {
+ byte[] buf = new byte[leftover];
+ while (leftover > 0) {
+ int m = super.read(buf, 0, leftover);
+ if (m == -1) {
+ break;
+ }
+ leftover -= m;
+ }
+ }
+ entry = null;
+ }
+ return n;
+ }
+
+ public static class TarEntry {
+ /*
+ * The C structure for a Tar Entry's header is:
+ *
+ * struct header { + * char name[100]; + * char mode[8]; + * char uid[8]; + * char gid[8]; + * char size[12]; + * char mtime[12]; + * char chksum[8]; + * char linkflag; + * char linkname[100]; + * char magic[8]; + * char uname[32]; + * char gname[32]; + * char devmajor[8]; + * char devminor[8]; + * } header; + *+ * + */ + private static final int NAME_OFF = 0; + private static final int NAME_LEN = 100; + /* + private static final int MODE_OFF = 100; + private static final int MODE_LEN = 8; + private static final int UID_OFF = 108; + private static final int UID_LEN = 8; + private static final int GID_OFF = 116; + private static final int GID_LEN = 8; + */ + private static final int SIZE_OFF = 124; + private static final int SIZE_LEN = 12; + /* + private static final int MTIME_OFF = 136; + private static final int MTIME_LEN = 12; + private static final int CHECKSUM_OFF = 148; + private static final int CHECKSUM_LEN = 8; + private static final int TYPEFLAG_OFF = 156; + private static final int TYPEFLAG_LEN = 1; + private static final int LINKNAME_OFF = 157; + private static final int LINKNAME_LEN = 100; + */ + private static final int MAGIC_OFF = 257; + private static final int MAGIC_LEN = 6; + /* + private static final int VERSION_OFF = 263; + private static final int VERSION_LEN = 2; + private static final int UNAME_OFF = 265; + private static final int UNAME_LEN = 32; + private static final int GNAME_OFF = 297; + private static final int GNAME_LEN = 32; + private static final int DEVMAJOR_OFF = 329; + private static final int DEVMAJOR_LEN = 8; + private static final int DEVMINOR_OFF = 337; + private static final int DEVMINOR_LEN = 8; + private static final int PREFIX_OFF = 345; + private static final int PREFIX_LEN = 155; + */ + // The maximum size of a file in a tar archive. + private static final long MAXSIZE = 077777777777L; + + //private byte[] header; + private String name; + private long size; + + /** + * Construct an entry from an archive's header bytes. + * + * @param headerBuf The header bytes from a tar archive entry. + */ + public TarEntry(byte[] header) { + //this.header = header; + this.name = toString(header, NAME_OFF, NAME_LEN); + this.size = toNumber(header, SIZE_OFF, SIZE_LEN); + // verify magic and size + String magic = toString(header, MAGIC_OFF, MAGIC_LEN); + if (!magic.startsWith("ustar") || + size < 0 || size > MAXSIZE) { + throw new IllegalArgumentException("Illegal tar entry: magic=" + + magic + ", size=" + size); + } + } + + public long getSize() { + return size; + } + + public String getName() { + return name; + } + + private static long toNumber(byte[] header, int off, int len) { + long ret = 0; + int limit = off + len; + int b; + while (off < limit && ((b = header[off]) == ' ' || b == '0')) { + off++; + } + while (off < limit && ((b = header[off++]) >= '0' && b < '8')) { + ret = (ret << 3) + (b - '0'); + } + return ret; + } + + private static String toString(byte[] header, int off, int len) { + int off0 = off; + int limit = off + len; + while (off < limit && header[off] != 0) { + off++; + } + return new String(header, off0, off - off0); + } + + } +}