Return-Path: Delivered-To: apmail-commons-commits-archive@minotaur.apache.org Received: (qmail 29777 invoked from network); 10 Feb 2009 16:12:15 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.2) by minotaur.apache.org with SMTP; 10 Feb 2009 16:12:15 -0000 Received: (qmail 91531 invoked by uid 500); 10 Feb 2009 16:12:09 -0000 Delivered-To: apmail-commons-commits-archive@commons.apache.org Received: (qmail 91493 invoked by uid 500); 10 Feb 2009 16:12:09 -0000 Mailing-List: contact commits-help@commons.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@commons.apache.org Delivered-To: mailing list commits@commons.apache.org Received: (qmail 91484 invoked by uid 99); 10 Feb 2009 16:12:09 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 10 Feb 2009 08:12:09 -0800 X-ASF-Spam-Status: No, hits=-2000.0 required=10.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 10 Feb 2009 16:11:54 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id 8B7082388AD9; Tue, 10 Feb 2009 16:11:32 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r743006 - in /commons/sandbox/compress/trunk/src: main/java/org/apache/commons/compress/archivers/ main/java/org/apache/commons/compress/archivers/tar/ test/java/org/apache/commons/compress/ Date: Tue, 10 Feb 2009 16:11:21 -0000 To: commits@commons.apache.org From: bodewig@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20090210161132.8B7082388AD9@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: bodewig Date: Tue Feb 10 16:10:45 2009 New Revision: 743006 URL: http://svn.apache.org/viewvc?rev=743006&view=rev Log: rename Tar*Stream Added: commons/sandbox/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveInputStream.java commons/sandbox/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveOutputStream.java Removed: commons/sandbox/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarInputStream.java commons/sandbox/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarOutputStream.java Modified: commons/sandbox/compress/trunk/src/main/java/org/apache/commons/compress/archivers/ArchiveStreamFactory.java commons/sandbox/compress/trunk/src/test/java/org/apache/commons/compress/DetectArchiverTestCase.java Modified: commons/sandbox/compress/trunk/src/main/java/org/apache/commons/compress/archivers/ArchiveStreamFactory.java URL: http://svn.apache.org/viewvc/commons/sandbox/compress/trunk/src/main/java/org/apache/commons/compress/archivers/ArchiveStreamFactory.java?rev=743006&r1=743005&r2=743006&view=diff ============================================================================== --- commons/sandbox/compress/trunk/src/main/java/org/apache/commons/compress/archivers/ArchiveStreamFactory.java (original) +++ commons/sandbox/compress/trunk/src/main/java/org/apache/commons/compress/archivers/ArchiveStreamFactory.java Tue Feb 10 16:10:45 2009 @@ -28,8 +28,8 @@ import org.apache.commons.compress.archivers.cpio.CpioArchiveOutputStream; import org.apache.commons.compress.archivers.jar.JarArchiveInputStream; import org.apache.commons.compress.archivers.jar.JarArchiveOutputStream; -import org.apache.commons.compress.archivers.tar.TarInputStream; -import org.apache.commons.compress.archivers.tar.TarOutputStream; +import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; +import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream; import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream; @@ -52,7 +52,7 @@ } else if("zip".equalsIgnoreCase(archiverName)) { return new ZipArchiveInputStream(in); } else if("tar".equalsIgnoreCase(archiverName)) { - return new TarInputStream(in); + return new TarArchiveInputStream(in); } else if("jar".equalsIgnoreCase(archiverName)) { return new JarArchiveInputStream(in); } else if("cpio".equalsIgnoreCase(archiverName)) { @@ -67,7 +67,7 @@ } else if("zip".equalsIgnoreCase(archiverName)) { return new ZipArchiveOutputStream(out); } else if("tar".equalsIgnoreCase(archiverName)) { - return new TarOutputStream(out); + return new TarArchiveOutputStream(out); } else if("jar".equalsIgnoreCase(archiverName)) { return new JarArchiveOutputStream(out); } else if("cpio".equalsIgnoreCase(archiverName)) { @@ -88,8 +88,8 @@ return new ZipArchiveInputStream(input); } else if(JarArchiveInputStream.matches(signature, signatureLength)) { return new JarArchiveInputStream(input); - } else if(TarInputStream.matches(signature, signatureLength)) { - return new TarInputStream(input); + } else if(TarArchiveInputStream.matches(signature, signatureLength)) { + return new TarArchiveInputStream(input); } else if(ArArchiveInputStream.matches(signature, signatureLength)) { return new ArArchiveInputStream(input); } else if(CpioArchiveInputStream.matches(signature, signatureLength)) { Added: commons/sandbox/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveInputStream.java URL: http://svn.apache.org/viewvc/commons/sandbox/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveInputStream.java?rev=743006&view=auto ============================================================================== --- commons/sandbox/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveInputStream.java (added) +++ commons/sandbox/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveInputStream.java Tue Feb 10 16:10:45 2009 @@ -0,0 +1,431 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* + * This package is based on the work done by Timothy Gerard Endres + * (time@ice.com) to whom the Ant project is very grateful for his great code. + */ + +package org.apache.commons.compress.archivers.tar; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import org.apache.commons.compress.archivers.ArchiveEntry; +import org.apache.commons.compress.archivers.ArchiveInputStream; + +/** + * The TarInputStream reads a UNIX tar archive as an InputStream. + * methods are provided to position at each successive entry in + * the archive, and the read each entry as a normal input stream + * using read(). + * + */ +public class TarArchiveInputStream extends ArchiveInputStream { + private static final int SMALL_BUFFER_SIZE = 256; + private static final int BUFFER_SIZE = 8 * 1024; + private static final int LARGE_BUFFER_SIZE = 32 * 1024; + private static final int BYTE_MASK = 0xFF; + + // CheckStyle:VisibilityModifier OFF - bc + protected boolean debug; + protected boolean hasHitEOF; + protected long entrySize; + protected long entryOffset; + protected byte[] readBuf; + protected TarBuffer buffer; + protected TarArchiveEntry currEntry; + + /** + * This contents of this array is not used at all in this class, + * it is only here to avoid repreated object creation during calls + * to the no-arg read method. + */ + protected byte[] oneBuf; + + // CheckStyle:VisibilityModifier ON + + private final InputStream in; + + /** + * Constructor for TarInputStream. + * @param is the input stream to use + */ + public TarArchiveInputStream(InputStream is) { + this(is, TarBuffer.DEFAULT_BLKSIZE, TarBuffer.DEFAULT_RCDSIZE); + } + + /** + * Constructor for TarInputStream. + * @param is the input stream to use + * @param blockSize the block size to use + */ + public TarArchiveInputStream(InputStream is, int blockSize) { + this(is, blockSize, TarBuffer.DEFAULT_RCDSIZE); + } + + /** + * Constructor for TarInputStream. + * @param is the input stream to use + * @param blockSize the block size to use + * @param recordSize the record size to use + */ + public TarArchiveInputStream(InputStream is, int blockSize, int recordSize) { + this.in = is; + + this.buffer = new TarBuffer(is, blockSize, recordSize); + this.readBuf = null; + this.oneBuf = new byte[1]; + this.debug = false; + this.hasHitEOF = false; + } + + /** + * Sets the debugging flag. + * + * @param debug True to turn on debugging. + */ + public void setDebug(boolean debug) { + this.debug = debug; + buffer.setDebug(debug); + } + + /** + * Closes this stream. Calls the TarBuffer's close() method. + * @throws IOException on error + */ + public void close() throws IOException { + buffer.close(); + } + + /** + * Get the record size being used by this stream's TarBuffer. + * + * @return The TarBuffer record size. + */ + public int getRecordSize() { + return buffer.getRecordSize(); + } + + /** + * Get the available data that can be read from the current + * entry in the archive. This does not indicate how much data + * is left in the entire archive, only in the current entry. + * This value is determined from the entry's size header field + * and the amount of data already read from the current entry. + * Integer.MAX_VALUE is returen in case more than Integer.MAX_VALUE + * bytes are left in the current entry in the archive. + * + * @return The number of available bytes for the current entry. + * @throws IOException for signature + */ + public int available() throws IOException { + if (entrySize - entryOffset > Integer.MAX_VALUE) { + return Integer.MAX_VALUE; + } + return (int) (entrySize - entryOffset); + } + + /** + * Skip bytes in the input buffer. This skips bytes in the + * current entry's data, not the entire archive, and will + * stop at the end of the current entry's data if the number + * to skip extends beyond that point. + * + * @param numToSkip The number of bytes to skip. + * @return the number actually skipped + * @throws IOException on error + */ + public long skip(long numToSkip) throws IOException { + // REVIEW + // This is horribly inefficient, but it ensures that we + // properly skip over bytes via the TarBuffer... + // + byte[] skipBuf = new byte[BUFFER_SIZE]; + long skip = numToSkip; + while (skip > 0) { + int realSkip = (int) (skip > skipBuf.length ? skipBuf.length : skip); + int numRead = read(skipBuf, 0, realSkip); + if (numRead == -1) { + break; + } + skip -= numRead; + } + return (numToSkip - skip); + } + + /** + * Since we do not support marking just yet, we do nothing. + */ + public void reset() { + } + + /** + * Get the next entry in this tar archive. This will skip + * over any remaining data in the current entry, if there + * is one, and place the input stream at the header of the + * next entry, and read the header and instantiate a new + * TarEntry from the header bytes and return that entry. + * If there are no more entries in the archive, null will + * be returned to indicate that the end of the archive has + * been reached. + * + * @return The next TarEntry in the archive, or null. + * @throws IOException on error + */ + public ArchiveEntry getNextEntry() throws IOException { + if (hasHitEOF) { + return null; + } + + if (currEntry != null) { + long numToSkip = entrySize - entryOffset; + + if (debug) { + System.err.println("TarInputStream: SKIP currENTRY '" + + currEntry.getName() + "' SZ " + + entrySize + " OFF " + + entryOffset + " skipping " + + numToSkip + " bytes"); + } + + while (numToSkip > 0) { + long skipped = skip(numToSkip); + if (skipped <= 0) { + throw new RuntimeException("failed to skip current tar" + + " entry"); + } + numToSkip -= skipped; + } + + readBuf = null; + } + + byte[] headerBuf = buffer.readRecord(); + + if (headerBuf == null) { + if (debug) { + System.err.println("READ NULL RECORD"); + } + hasHitEOF = true; + } else if (buffer.isEOFRecord(headerBuf)) { + if (debug) { + System.err.println("READ EOF RECORD"); + } + hasHitEOF = true; + } + + if (hasHitEOF) { + currEntry = null; + } else { + currEntry = new TarArchiveEntry(headerBuf); + + if (debug) { + System.err.println("TarInputStream: SET CURRENTRY '" + + currEntry.getName() + + "' size = " + + currEntry.getSize()); + } + + entryOffset = 0; + + entrySize = currEntry.getSize(); + } + + if (currEntry != null && currEntry.isGNULongNameEntry()) { + // read in the name + StringBuffer longName = new StringBuffer(); + byte[] buf = new byte[SMALL_BUFFER_SIZE]; + int length = 0; + while ((length = read(buf)) >= 0) { + longName.append(new String(buf, 0, length)); + } + getNextEntry(); + if (currEntry == null) { + // Bugzilla: 40334 + // Malformed tar file - long entry name not followed by entry + return null; + } + // remove trailing null terminator + if (longName.length() > 0 + && longName.charAt(longName.length() - 1) == 0) { + longName.deleteCharAt(longName.length() - 1); + } + currEntry.setName(longName.toString()); + } + + return currEntry; + } + + /** + * Reads a byte from the current tar archive entry. + * + * This method simply calls read( byte[], int, int ). + * + * @return The byte read, or -1 at EOF. + * @throws IOException on error + */ + public int read() throws IOException { + int num = read(oneBuf, 0, 1); + return num == -1 ? -1 : ((int) oneBuf[0]) & BYTE_MASK; + } + + /** + * Reads bytes from the current tar archive entry. + * + * This method is aware of the boundaries of the current + * entry in the archive and will deal with them as if they + * were this stream's start and EOF. + * + * @param buf The buffer into which to place bytes read. + * @param offset The offset at which to place bytes read. + * @param numToRead The number of bytes to read. + * @return The number of bytes read, or -1 at EOF. + * @throws IOException on error + */ + public int read(byte[] buf, int offset, int numToRead) throws IOException { + int totalRead = 0; + + if (entryOffset >= entrySize) { + return -1; + } + + if ((numToRead + entryOffset) > entrySize) { + numToRead = (int) (entrySize - entryOffset); + } + + if (readBuf != null) { + int sz = (numToRead > readBuf.length) ? readBuf.length + : numToRead; + + System.arraycopy(readBuf, 0, buf, offset, sz); + + if (sz >= readBuf.length) { + readBuf = null; + } else { + int newLen = readBuf.length - sz; + byte[] newBuf = new byte[newLen]; + + System.arraycopy(readBuf, sz, newBuf, 0, newLen); + + readBuf = newBuf; + } + + totalRead += sz; + numToRead -= sz; + offset += sz; + } + + while (numToRead > 0) { + byte[] rec = buffer.readRecord(); + + if (rec == null) { + // Unexpected EOF! + throw new IOException("unexpected EOF with " + numToRead + + " bytes unread"); + } + + int sz = numToRead; + int recLen = rec.length; + + if (recLen > sz) { + System.arraycopy(rec, 0, buf, offset, sz); + + readBuf = new byte[recLen - sz]; + + System.arraycopy(rec, sz, readBuf, 0, recLen - sz); + } else { + sz = recLen; + + System.arraycopy(rec, 0, buf, offset, recLen); + } + + totalRead += sz; + numToRead -= sz; + offset += sz; + } + + entryOffset += totalRead; + + return totalRead; + } + + /** + * Copies the contents of the current tar archive entry directly into + * an output stream. + * + * @param out The OutputStream into which to write the entry's data. + * @throws IOException on error + */ + public void copyEntryContents(OutputStream out) throws IOException { + byte[] buf = new byte[LARGE_BUFFER_SIZE]; + + while (true) { + int numRead = read(buf, 0, buf.length); + + if (numRead == -1) { + break; + } + + out.write(buf, 0, numRead); + } + } + + // used to be implemented via FilterInputStream + public int read(byte[] b) throws IOException { + return read(b, 0, b.length); + } + + // ArchiveInputStream + + public static boolean matches(byte[] signature, int length) { + // 6574 7473 2e31 6d78 + + if (length < 8) { + return false; + } + + if (signature[0] != 0x74) { + return false; + } + if (signature[1] != 0x65) { + return false; + } + if (signature[2] != 0x73) { + return false; + } + if (signature[3] != 0x74) { + return false; + } + if (signature[4] != 0x31) { + return false; + } + if (signature[5] != 0x2e) { + return false; + } + if (signature[6] != 0x78) { + return false; + } + if (signature[7] != 0x6d) { + return false; + } + + return true; + } + +} Added: commons/sandbox/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveOutputStream.java URL: http://svn.apache.org/viewvc/commons/sandbox/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveOutputStream.java?rev=743006&view=auto ============================================================================== --- commons/sandbox/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveOutputStream.java (added) +++ commons/sandbox/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveOutputStream.java Tue Feb 10 16:10:45 2009 @@ -0,0 +1,380 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.commons.compress.archivers.tar; + +import java.io.IOException; +import java.io.OutputStream; +import org.apache.commons.compress.archivers.ArchiveEntry; +import org.apache.commons.compress.archivers.ArchiveOutputStream; + +/** + * The TarOutputStream writes a UNIX tar archive as an OutputStream. + * Methods are provided to put entries, and then write their contents + * by writing to this stream using write(). + * + */ +public class TarArchiveOutputStream extends ArchiveOutputStream { + /** Fail if a long file name is required in the archive. */ + public static final int LONGFILE_ERROR = 0; + + /** Long paths will be truncated in the archive. */ + public static final int LONGFILE_TRUNCATE = 1; + + /** GNU tar extensions are used to store long file names in the archive. */ + public static final int LONGFILE_GNU = 2; + + // CheckStyle:VisibilityModifier OFF - bc + protected boolean debug; + protected long currSize; + protected String currName; + protected long currBytes; + protected byte[] oneBuf; + protected byte[] recordBuf; + protected int assemLen; + protected byte[] assemBuf; + protected TarBuffer buffer; + protected int longFileMode = LONGFILE_ERROR; + // CheckStyle:VisibilityModifier ON + + private boolean closed = false; + + private final OutputStream out; + + /** + * Constructor for TarInputStream. + * @param os the output stream to use + */ + public TarArchiveOutputStream(OutputStream os) { + this(os, TarBuffer.DEFAULT_BLKSIZE, TarBuffer.DEFAULT_RCDSIZE); + } + + /** + * Constructor for TarInputStream. + * @param os the output stream to use + * @param blockSize the block size to use + */ + public TarArchiveOutputStream(OutputStream os, int blockSize) { + this(os, blockSize, TarBuffer.DEFAULT_RCDSIZE); + } + + /** + * Constructor for TarInputStream. + * @param os the output stream to use + * @param blockSize the block size to use + * @param recordSize the record size to use + */ + public TarArchiveOutputStream(OutputStream os, int blockSize, int recordSize) { + out = os; + + this.buffer = new TarBuffer(os, blockSize, recordSize); + this.debug = false; + this.assemLen = 0; + this.assemBuf = new byte[recordSize]; + this.recordBuf = new byte[recordSize]; + this.oneBuf = new byte[1]; + } + + /** + * Set the long file mode. + * This can be LONGFILE_ERROR(0), LONGFILE_TRUNCATE(1) or LONGFILE_GNU(2). + * This specifies the treatment of long file names (names >= TarConstants.NAMELEN). + * Default is LONGFILE_ERROR. + * @param longFileMode the mode to use + */ + public void setLongFileMode(int longFileMode) { + this.longFileMode = longFileMode; + } + + + /** + * Sets the debugging flag. + * + * @param debugF True to turn on debugging. + */ + public void setDebug(boolean debugF) { + this.debug = debugF; + } + + /** + * Sets the debugging flag in this stream's TarBuffer. + * + * @param debug True to turn on debugging. + */ + public void setBufferDebug(boolean debug) { + buffer.setDebug(debug); + } + + /** + * Ends the TAR archive without closing the underlying OutputStream. + * The result is that the two EOF records of nulls are written. + * @throws IOException on error + */ + public void finish() throws IOException { + // See Bugzilla 28776 for a discussion on this + // http://issues.apache.org/bugzilla/show_bug.cgi?id=28776 + writeEOFRecord(); + writeEOFRecord(); + } + + /** + * Ends the TAR archive and closes the underlying OutputStream. + * This means that finish() is called followed by calling the + * TarBuffer's close(). + * @throws IOException on error + */ + public void close() throws IOException { + if (!closed) { + finish(); + buffer.close(); + out.close(); + closed = true; + } + } + + /** + * Get the record size being used by this stream's TarBuffer. + * + * @return The TarBuffer record size. + */ + public int getRecordSize() { + return buffer.getRecordSize(); + } + + /** + * Put an entry on the output stream. This writes the entry's + * header record and positions the output stream for writing + * the contents of the entry. Once this method is called, the + * stream is ready for calls to write() to write the entry's + * contents. Once the contents are written, closeEntry() + * MUST be called to ensure that all buffered data + * is completely written to the output stream. + * + * @param entry The TarEntry to be written to the archive. + * @throws IOException on error + */ + public void putNextEntry(TarArchiveEntry entry) throws IOException { + if (entry.getName().length() >= TarConstants.NAMELEN) { + + if (longFileMode == LONGFILE_GNU) { + // create a TarEntry for the LongLink, the contents + // of which are the entry's name + TarArchiveEntry longLinkEntry = new TarArchiveEntry(TarConstants.GNU_LONGLINK, + TarConstants.LF_GNUTYPE_LONGNAME); + + longLinkEntry.setSize(entry.getName().length() + 1); + putNextEntry(longLinkEntry); + write(entry.getName().getBytes()); + write(0); + closeEntry(); + } else if (longFileMode != LONGFILE_TRUNCATE) { + throw new RuntimeException("file name '" + entry.getName() + + "' is too long ( > " + + TarConstants.NAMELEN + " bytes)"); + } + } + + entry.writeEntryHeader(recordBuf); + buffer.writeRecord(recordBuf); + + currBytes = 0; + + if (entry.isDirectory()) { + currSize = 0; + } else { + currSize = entry.getSize(); + } + currName = entry.getName(); + } + + /** + * Close an entry. This method MUST be called for all file + * entries that contain data. The reason is that we must + * buffer data written to the stream in order to satisfy + * the buffer's record based writes. Thus, there may be + * data fragments still being assembled that must be written + * to the output stream before this entry is closed and the + * next entry written. + * @throws IOException on error + */ + public void closeEntry() throws IOException { + if (assemLen > 0) { + for (int i = assemLen; i < assemBuf.length; ++i) { + assemBuf[i] = 0; + } + + buffer.writeRecord(assemBuf); + + currBytes += assemLen; + assemLen = 0; + } + + if (currBytes < currSize) { + throw new IOException("entry '" + currName + "' closed at '" + + currBytes + + "' before the '" + currSize + + "' bytes specified in the header were written"); + } + } + + /** + * Writes a byte to the current tar archive entry. + * + * This method simply calls read( byte[], int, int ). + * + * @param b The byte written. + * @throws IOException on error + */ + public void write(int b) throws IOException { + oneBuf[0] = (byte) b; + + write(oneBuf, 0, 1); + } + + /** + * Writes bytes to the current tar archive entry. + * + * This method simply calls write( byte[], int, int ). + * + * @param wBuf The buffer to write to the archive. + * @throws IOException on error + */ + public void write(byte[] wBuf) throws IOException { + write(wBuf, 0, wBuf.length); + } + + /** + * Writes bytes to the current tar archive entry. This method + * is aware of the current entry and will throw an exception if + * you attempt to write bytes past the length specified for the + * current entry. The method is also (painfully) aware of the + * record buffering required by TarBuffer, and manages buffers + * that are not a multiple of recordsize in length, including + * assembling records from small buffers. + * + * @param wBuf The buffer to write to the archive. + * @param wOffset The offset in the buffer from which to get bytes. + * @param numToWrite The number of bytes to write. + * @throws IOException on error + */ + public void write(byte[] wBuf, int wOffset, int numToWrite) throws IOException { + if ((currBytes + numToWrite) > currSize) { + throw new IOException("request to write '" + numToWrite + + "' bytes exceeds size in header of '" + + currSize + "' bytes for entry '" + + currName + "'"); + + // + // We have to deal with assembly!!! + // The programmer can be writing little 32 byte chunks for all + // we know, and we must assemble complete records for writing. + // REVIEW Maybe this should be in TarBuffer? Could that help to + // eliminate some of the buffer copying. + // + } + + if (assemLen > 0) { + if ((assemLen + numToWrite) >= recordBuf.length) { + int aLen = recordBuf.length - assemLen; + + System.arraycopy(assemBuf, 0, recordBuf, 0, + assemLen); + System.arraycopy(wBuf, wOffset, recordBuf, + assemLen, aLen); + buffer.writeRecord(recordBuf); + + currBytes += recordBuf.length; + wOffset += aLen; + numToWrite -= aLen; + assemLen = 0; + } else { + System.arraycopy(wBuf, wOffset, assemBuf, assemLen, + numToWrite); + + wOffset += numToWrite; + assemLen += numToWrite; + numToWrite = 0; + } + } + + // + // When we get here we have EITHER: + // o An empty "assemble" buffer. + // o No bytes to write (numToWrite == 0) + // + while (numToWrite > 0) { + if (numToWrite < recordBuf.length) { + System.arraycopy(wBuf, wOffset, assemBuf, assemLen, + numToWrite); + + assemLen += numToWrite; + + break; + } + + buffer.writeRecord(wBuf, wOffset); + + int num = recordBuf.length; + + currBytes += num; + numToWrite -= num; + wOffset += num; + } + } + + /** + * Write an EOF (end of archive) record to the tar archive. + * An EOF record consists of a record of all zeros. + */ + private void writeEOFRecord() throws IOException { + for (int i = 0; i < recordBuf.length; ++i) { + recordBuf[i] = 0; + } + + buffer.writeRecord(recordBuf); + } + + // used to be implemented via FilterOutputStream + public void flush() throws IOException { + out.flush(); + } + + // ArchiveOutputStream + + public void closeArchiveEntry() throws IOException { + closeEntry(); + } + + public void putArchiveEntry(ArchiveEntry entry) throws IOException { + putNextEntry((TarArchiveEntry) entry); + } + + public String getDefaultFileExtension() { + return "tar"; + } + + public byte[] getHeader() { + // TODO Auto-generated method stub + return null; + } + + public String getName() { + return "tar"; + } + +} Modified: commons/sandbox/compress/trunk/src/test/java/org/apache/commons/compress/DetectArchiverTestCase.java URL: http://svn.apache.org/viewvc/commons/sandbox/compress/trunk/src/test/java/org/apache/commons/compress/DetectArchiverTestCase.java?rev=743006&r1=743005&r2=743006&view=diff ============================================================================== --- commons/sandbox/compress/trunk/src/test/java/org/apache/commons/compress/DetectArchiverTestCase.java (original) +++ commons/sandbox/compress/trunk/src/test/java/org/apache/commons/compress/DetectArchiverTestCase.java Tue Feb 10 16:10:45 2009 @@ -29,7 +29,7 @@ import org.apache.commons.compress.archivers.ar.ArArchiveInputStream; import org.apache.commons.compress.archivers.cpio.CpioArchiveInputStream; import org.apache.commons.compress.archivers.jar.JarArchiveInputStream; -import org.apache.commons.compress.archivers.tar.TarInputStream; +import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream; public final class DetectArchiverTestCase extends TestCase { @@ -46,7 +46,7 @@ new BufferedInputStream(new FileInputStream( new File(getClass().getClassLoader().getResource("bla.tar").getFile())))); assertNotNull(tar); - assertTrue(tar instanceof TarInputStream); + assertTrue(tar instanceof TarArchiveInputStream); final ArchiveInputStream zip = factory.createArchiveInputStream( new BufferedInputStream(new FileInputStream(