2
* Licensed to the Apache Software Foundation (ASF) under one
3
* or more contributor license agreements. See the NOTICE file
4
* distributed with this work for additional information
5
* regarding copyright ownership. The ASF licenses this file
6
* to you under the Apache License, Version 2.0 (the
7
* "License"); you may not use this file except in compliance
8
* with the License. You may obtain a copy of the License at
10
* http://www.apache.org/licenses/LICENSE-2.0
12
* Unless required by applicable law or agreed to in writing,
13
* software distributed under the License is distributed on an
14
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
* KIND, either express or implied. See the License for the
16
* specific language governing permissions and limitations
19
package org.apache.commons.compress.archivers.tar;
21
import java.io.FilterInputStream;
22
import java.io.IOException;
23
import java.io.InputStream;
24
import java.io.OutputStream;
26
* The TarInputStream reads a UNIX tar archive as an InputStream. methods are
27
* provided to position at each successive entry in the archive, and the read
28
* each entry as a normal input stream using read().
30
* @author <a href="mailto:time@ice.com">Timothy Gerard Endres</a>
31
* @author <a href="mailto:stefano@apache.org">Stefano Mazzocchi</a>
32
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
33
* @version $Revision: 155439 $ $Date: 2006-08-28 08:10:02 +0200 (Mo, 28 Aug 2006) $
37
public final class TarInputStream extends FilterInputStream
39
private TarBuffer m_buffer;
40
private TarEntry m_currEntry;
41
private boolean m_debug;
42
private int m_entryOffset;
43
private int m_entrySize;
44
private boolean m_hasHitEOF;
45
private byte[] m_oneBuf;
46
private byte[] m_readBuf;
49
* Construct a TarInputStream using specified input
50
* stream and default block and record sizes.
52
* @param input stream to create TarInputStream from
53
* @see TarBuffer#DEFAULT_BLOCKSIZE
54
* @see TarBuffer#DEFAULT_RECORDSIZE
56
public TarInputStream( final InputStream input )
58
this( input, TarBuffer.DEFAULT_BLOCKSIZE, TarBuffer.DEFAULT_RECORDSIZE );
62
* Construct a TarInputStream using specified input
63
* stream, block size and default record sizes.
65
* @param input stream to create TarInputStream from
66
* @param blockSize the block size to use
67
* @see TarBuffer#DEFAULT_RECORDSIZE
69
public TarInputStream( final InputStream input,
72
this( input, blockSize, TarBuffer.DEFAULT_RECORDSIZE );
76
* Construct a TarInputStream using specified input
77
* stream, block size and record sizes.
79
* @param input stream to create TarInputStream from
80
* @param blockSize the block size to use
81
* @param recordSize the record size to use
83
public TarInputStream( final InputStream input,
85
final int recordSize )
89
m_buffer = new TarBuffer( input, blockSize, recordSize );
90
m_oneBuf = new byte[ 1 ];
94
* Sets the debugging flag.
96
* @param debug The new Debug value
98
public void setDebug( final boolean debug )
101
m_buffer.setDebug( debug );
105
* Get the next entry in this tar archive. This will skip over any remaining
106
* data in the current entry, if there is one, and place the input stream at
107
* the header of the next entry, and read the header and instantiate a new
108
* TarEntry from the header bytes and return that entry. If there are no
109
* more entries in the archive, null will be returned to indicate that the
110
* end of the archive has been reached.
112
* @return The next TarEntry in the archive, or null.
113
* @exception IOException Description of Exception
115
public TarEntry getNextEntry()
123
if( m_currEntry != null )
125
final int numToSkip = m_entrySize - m_entryOffset;
129
final String message = "TarInputStream: SKIP currENTRY '" +
130
m_currEntry.getName() + "' SZ " + m_entrySize +
131
" OFF " + m_entryOffset + " skipping " + numToSkip + " bytes";
143
final byte[] headerBuf = m_buffer.readRecord();
144
if( headerBuf == null )
148
debug( "READ NULL RECORD" );
152
else if( m_buffer.isEOFRecord( headerBuf ) )
156
debug( "READ EOF RECORD" );
167
m_currEntry = new TarEntry( headerBuf );
169
if( !( headerBuf[ 257 ] == 'u' && headerBuf[ 258 ] == 's' &&
170
headerBuf[ 259 ] == 't' && headerBuf[ 260 ] == 'a' &&
171
headerBuf[ 261 ] == 'r' ) )
178
final String message = "TarInputStream: SET CURRENTRY '" +
179
m_currEntry.getName() + "' size = " + m_currEntry.getSize();
185
// REVIEW How do we resolve this discrepancy?!
186
m_entrySize = (int)m_currEntry.getSize();
189
if( null != m_currEntry && m_currEntry.isGNULongNameEntry() )
192
final StringBuffer longName = new StringBuffer();
193
final byte[] buffer = new byte[ 256 ];
195
while( ( length = read( buffer ) ) >= 0 )
197
final String str = new String( buffer, 0, length );
198
longName.append( str );
202
// remove trailing null terminator
203
if (longName.length() > 0
204
&& longName.charAt(longName.length() - 1) == 0) {
205
longName.deleteCharAt(longName.length() - 1);
208
m_currEntry.setName( longName.toString() );
215
* Get the record size being used by this stream's TarBuffer.
217
* @return The TarBuffer record size.
219
public int getRecordSize()
221
return m_buffer.getRecordSize();
225
* Get the available data that can be read from the current entry in the
226
* archive. This does not indicate how much data is left in the entire
227
* archive, only in the current entry. This value is determined from the
228
* entry's size header field and the amount of data already read from the
231
* @return The number of available bytes for the current entry.
232
* @exception IOException when an IO error causes operation to fail
234
public int available()
237
return m_entrySize - m_entryOffset;
241
* Closes this stream. Calls the TarBuffer's close() method.
243
* @exception IOException when an IO error causes operation to fail
252
* Copies the contents of the current tar archive entry directly into an
255
* @param output The OutputStream into which to write the entry's data.
256
* @exception IOException when an IO error causes operation to fail
258
public void copyEntryContents( final OutputStream output )
261
final byte[] buffer = new byte[ 32 * 1024 ];
264
final int numRead = read( buffer, 0, buffer.length );
270
output.write( buffer, 0, numRead );
275
* Since we do not support marking just yet, we do nothing.
277
* @param markLimit The limit to mark.
279
public void mark( int markLimit )
284
* Since we do not support marking just yet, we return false.
288
public boolean markSupported()
294
* Reads a byte from the current tar archive entry. This method simply calls
295
* read( byte[], int, int ).
297
* @return The byte read, or -1 at EOF.
298
* @exception IOException when an IO error causes operation to fail
303
final int num = read( m_oneBuf, 0, 1 );
310
return (int)m_oneBuf[ 0 ];
315
* Reads bytes from the current tar archive entry. This method simply calls
316
* read( byte[], int, int ).
318
* @param buffer The buffer into which to place bytes read.
319
* @return The number of bytes read, or -1 at EOF.
320
* @exception IOException when an IO error causes operation to fail
322
public int read( final byte[] buffer )
325
return read( buffer, 0, buffer.length );
329
* Reads bytes from the current tar archive entry. This method is aware of
330
* the boundaries of the current entry in the archive and will deal with
331
* them as if they were this stream's start and EOF.
333
* @param buffer The buffer into which to place bytes read.
334
* @param offset The offset at which to place bytes read.
335
* @param count The number of bytes to read.
336
* @return The number of bytes read, or -1 at EOF.
337
* @exception IOException when an IO error causes operation to fail
339
public int read( final byte[] buffer,
344
int position = offset;
345
int numToRead = count;
348
if( m_entryOffset >= m_entrySize )
353
if( ( numToRead + m_entryOffset ) > m_entrySize )
355
numToRead = ( m_entrySize - m_entryOffset );
358
if( null != m_readBuf )
361
( numToRead > m_readBuf.length ) ? m_readBuf.length : numToRead;
363
System.arraycopy( m_readBuf, 0, buffer, position, size );
365
if( size >= m_readBuf.length )
371
final int newLength = m_readBuf.length - size;
372
final byte[] newBuffer = new byte[ newLength ];
374
System.arraycopy( m_readBuf, size, newBuffer, 0, newLength );
376
m_readBuf = newBuffer;
384
while( numToRead > 0 )
386
final byte[] rec = m_buffer.readRecord();
390
final String message =
391
"unexpected EOF with " + numToRead + " bytes unread";
392
throw new IOException( message );
395
int size = numToRead;
396
final int recordLength = rec.length;
398
if( recordLength > size )
400
System.arraycopy( rec, 0, buffer, position, size );
402
m_readBuf = new byte[ recordLength - size ];
404
System.arraycopy( rec, size, m_readBuf, 0, recordLength - size );
410
System.arraycopy( rec, 0, buffer, position, recordLength );
418
m_entryOffset += totalRead;
424
* Since we do not support marking just yet, we do nothing.
431
* Skip bytes in the input buffer. This skips bytes in the current entry's
432
* data, not the entire archive, and will stop at the end of the current
433
* entry's data if the number to skip extends beyond that point.
435
* @param numToSkip The number of bytes to skip.
436
* @exception IOException when an IO error causes operation to fail
438
public void skip( final int numToSkip )
442
// This is horribly inefficient, but it ensures that we
443
// properly skip over bytes via the TarBuffer...
445
final byte[] skipBuf = new byte[ 8 * 1024 ];
449
final int count = ( num > skipBuf.length ) ? skipBuf.length : num;
450
final int numRead = read( skipBuf, 0, count );
461
* Utility method to do debugging.
462
* Capable of being overidden in sub-classes.
464
* @param message the message to use in debugging
466
protected void debug( final String message )
470
System.err.println( message );