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.IOException;
22
import java.io.InputStream;
23
import java.io.OutputStream;
26
* The TarBuffer class implements the tar archive concept of a buffered input
27
* stream. This concept goes back to the days of blocked tape drives and special
28
* io devices. In the Java universe, the only real function that this class
29
* performs is to ensure that files have the correct "block" size, or other tars
32
* You should never have a need to access this class directly. TarBuffers are
33
* created by Tar IO Streams.
35
* @author <a href="mailto:time@ice.com">Timothy Gerard Endres</a>
36
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
37
* @version $Revision: 155439 $ $Date: 2006-08-28 08:10:02 +0200 (Mo, 28 Aug 2006) $
39
public class TarBuffer
41
public static final int DEFAULT_RECORDSIZE = ( 512 );
42
public static final int DEFAULT_BLOCKSIZE = ( DEFAULT_RECORDSIZE * 20 );
44
private byte[] m_blockBuffer;
45
private int m_blockSize;
46
private int m_currBlkIdx;
47
private int m_currRecIdx;
48
private boolean m_debug;
50
private InputStream m_input;
51
private OutputStream m_output;
52
private int m_recordSize;
53
private int m_recsPerBlock;
55
public TarBuffer( final InputStream input )
57
this( input, TarBuffer.DEFAULT_BLOCKSIZE );
60
public TarBuffer( final InputStream input, final int blockSize )
62
this( input, blockSize, TarBuffer.DEFAULT_RECORDSIZE );
65
public TarBuffer( final InputStream input,
67
final int recordSize )
70
initialize( blockSize, recordSize );
73
public TarBuffer( final OutputStream output )
75
this( output, TarBuffer.DEFAULT_BLOCKSIZE );
78
public TarBuffer( final OutputStream output, final int blockSize )
80
this( output, blockSize, TarBuffer.DEFAULT_RECORDSIZE );
83
public TarBuffer( final OutputStream output,
85
final int recordSize )
88
initialize( blockSize, recordSize );
92
* Set the debugging flag for the buffer.
94
* @param debug If true, print debugging output.
96
public void setDebug( final boolean debug )
102
* Get the TAR Buffer's block size. Blocks consist of multiple records.
104
* @return The BlockSize value
106
public int getBlockSize()
112
* Get the current block number, zero based.
114
* @return The current zero based block number.
116
public int getCurrentBlockNum()
122
* Get the current record number, within the current block, zero based.
123
* Thus, current offset = (currentBlockNum * recsPerBlk) + currentRecNum.
125
* @return The current zero based record number.
127
public int getCurrentRecordNum()
129
return m_currRecIdx - 1;
133
* Get the TAR Buffer's record size.
135
* @return The RecordSize value
137
public int getRecordSize()
143
* Determine if an archive record indicate End of Archive. End of archive is
144
* indicated by a record that consists entirely of null bytes.
146
* @param record The record data to check.
147
* @return The EOFRecord value
149
public boolean isEOFRecord( final byte[] record )
151
final int size = getRecordSize();
152
for( int i = 0; i < size; ++i )
154
if( record[ i ] != 0 )
164
* Close the TarBuffer. If this is an output buffer, also flush the current
165
* block before closing.
172
debug( "TarBuffer.closeBuffer()." );
175
if( null != m_output )
179
if( m_output != System.out && m_output != System.err )
185
else if( m_input != null )
187
if( m_input != System.in )
196
* Read a record from the input stream and return the data.
198
* @return The record data.
199
* @exception IOException Description of Exception
201
public byte[] readRecord()
206
final String message = "ReadRecord: recIdx = " + m_currRecIdx +
207
" blkIdx = " + m_currBlkIdx;
211
if( null == m_input )
213
final String message = "reading from an output buffer";
214
throw new IOException( message );
217
if( m_currRecIdx >= m_recsPerBlock )
225
final byte[] result = new byte[ m_recordSize ];
226
System.arraycopy( m_blockBuffer,
227
( m_currRecIdx * m_recordSize ),
238
* Skip over a record on the input stream.
240
public void skipRecord()
245
final String message = "SkipRecord: recIdx = " + m_currRecIdx +
246
" blkIdx = " + m_currBlkIdx;
250
if( null == m_input )
252
final String message = "reading (via skip) from an output buffer";
253
throw new IOException( message );
256
if( m_currRecIdx >= m_recsPerBlock )
268
* Write an archive record to the archive.
270
* @param record The record data to write to the archive.
272
public void writeRecord( final byte[] record )
277
final String message = "WriteRecord: recIdx = " + m_currRecIdx +
278
" blkIdx = " + m_currBlkIdx;
282
if( null == m_output )
284
final String message = "writing to an input buffer";
285
throw new IOException( message );
288
if( record.length != m_recordSize )
290
final String message = "record to write has length '" +
291
record.length + "' which is not the record size of '" +
293
throw new IOException( message );
296
if( m_currRecIdx >= m_recsPerBlock )
301
System.arraycopy( record,
304
( m_currRecIdx * m_recordSize ),
311
* Write an archive record to the archive, where the record may be inside of
312
* a larger array buffer. The buffer must be "offset plus record size" long.
314
* @param buffer The buffer containing the record data to write.
315
* @param offset The offset of the record data within buf.
317
public void writeRecord( final byte[] buffer, final int offset )
322
final String message = "WriteRecord: recIdx = " + m_currRecIdx +
323
" blkIdx = " + m_currBlkIdx;
327
if( null == m_output )
329
final String message = "writing to an input buffer";
330
throw new IOException( message );
333
if( ( offset + m_recordSize ) > buffer.length )
335
final String message = "record has length '" + buffer.length +
336
"' with offset '" + offset + "' which is less than the record size of '" +
338
throw new IOException( message );
341
if( m_currRecIdx >= m_recsPerBlock )
346
System.arraycopy( buffer,
349
( m_currRecIdx * m_recordSize ),
356
* Flush the current data block if it has any data in it.
358
private void flushBlock()
363
final String message = "TarBuffer.flushBlock() called.";
367
if( m_output == null )
369
final String message = "writing to an input buffer";
370
throw new IOException( message );
373
if( m_currRecIdx > 0 )
380
* Initialization common to all constructors.
382
private void initialize( final int blockSize, final int recordSize )
385
m_blockSize = blockSize;
386
m_recordSize = recordSize;
387
m_recsPerBlock = ( m_blockSize / m_recordSize );
388
m_blockBuffer = new byte[ m_blockSize ];
390
if( null != m_input )
393
m_currRecIdx = m_recsPerBlock;
403
* @return false if End-Of-File, else true
405
private boolean readBlock()
410
final String message = "ReadBlock: blkIdx = " + m_currBlkIdx;
414
if( null == m_input )
416
final String message = "reading from an output buffer";
417
throw new IOException( message );
423
int bytesNeeded = m_blockSize;
425
while( bytesNeeded > 0 )
427
final long numBytes = m_input.read( m_blockBuffer, offset, bytesNeeded );
431
// We have fit EOF, and the block is not full!
433
// This is a broken archive. It does not follow the standard
434
// blocking algorithm. However, because we are generous, and
435
// it requires little effort, we will simply ignore the error
436
// and continue as if the entire block were read. This does
437
// not appear to break anything upstream. We used to return
438
// false in this case.
440
// Thanks to 'Yohann.Roussel@alcatel.fr' for this fix.
448
bytesNeeded -= numBytes;
450
if( numBytes != m_blockSize )
454
System.err.println( "ReadBlock: INCOMPLETE READ "
455
+ numBytes + " of " + m_blockSize
467
* Write a TarBuffer block to the archive.
469
* @exception IOException Description of Exception
471
private void writeBlock()
476
final String message = "WriteBlock: blkIdx = " + m_currBlkIdx;
480
if( null == m_output )
482
final String message = "writing to an input buffer";
483
throw new IOException( message );
486
m_output.write( m_blockBuffer, 0, m_blockSize );
493
protected void debug( final String message )
497
System.err.println( message );