/*
** Authored by Timothy Gerard Endres
** <mailto:[email protected]> <http://www.trustice.com>
**
** This work has been placed into the public domain.
** You may use this work in any way and for any purpose you wish.
**
** THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY OF ANY KIND,
** NOT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY. THE AUTHOR
** OF THIS SOFTWARE, ASSUMES _NO_ RESPONSIBILITY FOR ANY
** CONSEQUENCE RESULTING FROM THE USE, MODIFICATION, OR
** REDISTRIBUTION OF THIS SOFTWARE.
**
*/
package installer;
import java.io.*;
/**
* 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().
*
* @version $Revision: 12504 $
* @author Timothy Gerard Endres,
* <a href="mailto:[email protected]">[email protected]</a>.
* @see TarBuffer
* @see TarHeader
* @see TarEntry
*/
public
class TarInputStream
extends FilterInputStream
{
protected boolean debug;
protected boolean hasHitEOF;
protected int entrySize;
protected int entryOffset;
protected byte[] oneBuf;
protected byte[] readBuf;
protected TarBuffer buffer;
protected TarEntry currEntry;
protected EntryFactory eFactory;
public
TarInputStream( InputStream is )
{
this( is, TarBuffer.DEFAULT_BLKSIZE, TarBuffer.DEFAULT_RCDSIZE );
}
public
TarInputStream( InputStream is, int blockSize )
{
this( is, blockSize, TarBuffer.DEFAULT_RCDSIZE );
}
public
TarInputStream( InputStream is, int blockSize, int recordSize )
{
super( is );
this.buffer = new TarBuffer( is, blockSize, recordSize );
this.readBuf = null;
this.oneBuf = new byte[1];
this.debug = false;
this.hasHitEOF = false;
this.eFactory = null;
}
/**
* Sets the debugging flag.
*
* @param debugF True to turn on debugging.
*/
public void
setDebug( boolean debugF )
{
this.debug = debugF;
}
/**
* Sets the debugging flag.
*
* @param debugF True to turn on debugging.
*/
public void
setEntryFactory( EntryFactory factory )
{
this.eFactory = factory;
}
/**
* Sets the debugging flag in this stream's TarBuffer.
*
* @param debugF True to turn on debugging.
*/
public void
setBufferDebug( boolean debug )
{
this.buffer.setDebug( debug );
}
/**
* Closes this stream. Calls the TarBuffer's close() method.
*/
public void
close()
throws IOException
{
this.buffer.close();
}
/**
* Get the record size being used by this stream's TarBuffer.
*
* @return The TarBuffer record size.
*/
public int
getRecordSize()
{
return this.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.
*
*
* @return The number of available bytes for the current entry.
*/
public int
available()
throws IOException
{
return this.entrySize - this.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.
*/
public void
skip( int numToSkip )
throws IOException
{
// REVIEW
// This is horribly inefficient, but it ensures that we
// properly skip over bytes via the TarBuffer...
//
byte[] skipBuf = new byte[ 8 * 1024 ];
for ( int num = numToSkip ; num > 0 ; )
{
int numRead =
this.read( skipBuf, 0,
( num > skipBuf.length ? skipBuf.length : num ) );
if ( numRead == -1 )
break;
num -= numRead;
}
}
/**
* Since we do not support marking just yet, we return false.
*
* @return False.
*/
public boolean
markSupported()
{
return false;
}
/**
* Since we do not support marking just yet, we do nothing.
*
* @param markLimit The limit to mark.
*/
public void
mark( int markLimit )
{
}
/**
* 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.
*/
public TarEntry
getNextEntry()
throws IOException
{
if ( this.hasHitEOF )
return null;
if ( this.currEntry != null )
{
int numToSkip = this.entrySize - this.entryOffset;
if ( this.debug )
System.err.println
( "TarInputStream: SKIP currENTRY '"
+ this.currEntry.getName() + "' SZ "
+ this.entrySize + " OFF " + this.entryOffset
+ " skipping " + numToSkip + " bytes" );
if ( numToSkip > 0 )
{
this.skip( numToSkip );
}
this.readBuf = null;
}
byte[] headerBuf = this.buffer.readRecord();
if ( headerBuf == null )
{
if ( this.debug )
{
System.err.println( "READ NULL RECORD" );
}
this.hasHitEOF = true;
}
else if ( this.buffer.isEOFRecord( headerBuf ) )
{
if ( this.debug )
{
System.err.println( "READ EOF RECORD" );
}
this.hasHitEOF = true;
}
if ( this.hasHitEOF )
{
this.currEntry = null;
}
else
{
try {
if ( this.eFactory == null )
{
this.currEntry = new TarEntry( headerBuf );
}
else
{
this.currEntry =
this.eFactory.createEntry( headerBuf );
}
if ( ! ( headerBuf[257] == 'u' && headerBuf[258] == 's'
&& headerBuf[259] == 't' && headerBuf[260] == 'a'
&& headerBuf[261] == 'r' ) )
{
throw new InvalidHeaderException
( "header magic is not 'ustar', but '"
+ headerBuf[257] + headerBuf[258] + headerBuf[259]
+ headerBuf[260] + headerBuf[261] + "', or (dec) "
+ ((int)headerBuf[257]) + ", "
+ ((int)headerBuf[258]) + ", "
+ ((int)headerBuf[259]) + ", "
+ ((int)headerBuf[260]) + ", "
+ ((int)headerBuf[261]) );
}
if ( this.debug )
System.err.println
( "TarInputStream: SET CURRENTRY '"
+ this.currEntry.getName()
+ "' size = " + this.currEntry.getSize() );
this.entryOffset = 0;
// REVIEW How do we resolve this discrepancy?!
this.entrySize = (int) this.currEntry.getSize();
}
catch ( InvalidHeaderException ex )
{
this.entrySize = 0;
this.entryOffset = 0;
this.currEntry = null;
throw new InvalidHeaderException
( "bad header in block "
+ this.buffer.getCurrentBlockNum()
+ " record "
+ this.buffer.getCurrentRecordNum()
+ ", " + ex.getMessage() );
}
}
return this.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.
*/
public int
read()
throws IOException
{
int num = this.read( this.oneBuf, 0, 1 );
if ( num == -1 )
return num;
else
return this.oneBuf[0];
}
/**
* Reads bytes from the current tar archive entry.
*
* This method simply calls read( byte[], int, int ).
*
* @param buf The buffer into which to place bytes read.
* @return The number of bytes read, or -1 at EOF.
*/
public int
read( byte[] buf )
throws IOException
{
return this.read( buf, 0, buf.length );
}
/**
* 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.
*/
public int
read( byte[] buf, int offset, int numToRead )
throws IOException
{
int totalRead = 0;
if ( this.entryOffset >= this.entrySize )
return -1;
if ( (numToRead + this.entryOffset) > this.entrySize )
{
numToRead = (this.entrySize - this.entryOffset);
}
if ( this.readBuf != null )
{
int sz = ( numToRead > this.readBuf.length )
? this.readBuf.length : numToRead;
System.arraycopy( this.readBuf, 0, buf, offset, sz );
if ( sz >= this.readBuf.length )
{
this.readBuf = null;
}
else
{
int newLen = this.readBuf.length - sz;
byte[] newBuf = new byte[ newLen ];
System.arraycopy( this.readBuf, sz, newBuf, 0, newLen );
this.readBuf = newBuf;
}
totalRead += sz;
numToRead -= sz;
offset += sz;
}
for ( ; numToRead > 0 ; )
{
byte[] rec = this.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 );
this.readBuf = new byte[ recLen - sz ];
System.arraycopy( rec, sz, this.readBuf, 0, recLen - sz );
}
else
{
sz = recLen;
System.arraycopy( rec, 0, buf, offset, recLen );
}
totalRead += sz;
numToRead -= sz;
offset += sz;
}
this.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.
*/
public void
copyEntryContents( OutputStream out )
throws IOException
{
byte[] buf = new byte[ 32 * 1024 ];
for ( ; ; )
{
int numRead = this.read( buf, 0, buf.length );
if ( numRead == -1 )
break;
out.write( buf, 0, numRead );
}
}
/**
* This interface is provided, with the method setEntryFactory(), to allow
* the programmer to have their own TarEntry subclass instantiated for the
* entries return from getNextEntry().
*/
public
interface EntryFactory
{
public TarEntry
createEntry( String name );
public TarEntry
createEntry( File path )
throws InvalidHeaderException;
public TarEntry
createEntry( byte[] headerBuf )
throws InvalidHeaderException;
}
public
class EntryAdapter
implements EntryFactory
{
public TarEntry
createEntry( String name )
{
return new TarEntry( name );
}
public TarEntry
createEntry( File path )
throws InvalidHeaderException
{
return new TarEntry( path );
}
public TarEntry
createEntry( byte[] headerBuf )
throws InvalidHeaderException
{
return new TarEntry( headerBuf );
}
}
}
¤ Dauer der Verarbeitung: 0.36 Sekunden
(vorverarbeitet)
¤
|
Haftungshinweis
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.
|