~ubuntu-branches/ubuntu/maverick/libcommons-compress-java/maverick

« back to all changes in this revision

Viewing changes to src/java/org/apache/commons/compress/archivers/tar/TarBuffer.java

  • Committer: Bazaar Package Importer
  • Author(s): Torsten Werner
  • Date: 2007-12-17 15:17:09 UTC
  • Revision ID: james.westby@ubuntu.com-20071217151709-dqfo6iq1zxyssgf6
Tags: upstream-0~svn604876
ImportĀ upstreamĀ versionĀ 0~svn604876

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
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
 
9
 *
 
10
 * http://www.apache.org/licenses/LICENSE-2.0
 
11
 *
 
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
 
17
 * under the License.
 
18
 */
 
19
package org.apache.commons.compress.archivers.tar;
 
20
 
 
21
import java.io.IOException;
 
22
import java.io.InputStream;
 
23
import java.io.OutputStream;
 
24
 
 
25
/**
 
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
 
30
 * will complain. <p>
 
31
 *
 
32
 * You should never have a need to access this class directly. TarBuffers are
 
33
 * created by Tar IO Streams.
 
34
 *
 
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) $
 
38
 */
 
39
public class TarBuffer
 
40
{
 
41
    public static final int DEFAULT_RECORDSIZE = ( 512 );
 
42
    public static final int DEFAULT_BLOCKSIZE = ( DEFAULT_RECORDSIZE * 20 );
 
43
 
 
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;
 
49
 
 
50
    private InputStream m_input;
 
51
    private OutputStream m_output;
 
52
    private int m_recordSize;
 
53
    private int m_recsPerBlock;
 
54
 
 
55
    public TarBuffer( final InputStream input )
 
56
    {
 
57
        this( input, TarBuffer.DEFAULT_BLOCKSIZE );
 
58
    }
 
59
 
 
60
    public TarBuffer( final InputStream input, final int blockSize )
 
61
    {
 
62
        this( input, blockSize, TarBuffer.DEFAULT_RECORDSIZE );
 
63
    }
 
64
 
 
65
    public TarBuffer( final InputStream input,
 
66
                      final int blockSize,
 
67
                      final int recordSize )
 
68
    {
 
69
        m_input = input;
 
70
        initialize( blockSize, recordSize );
 
71
    }
 
72
 
 
73
    public TarBuffer( final OutputStream output )
 
74
    {
 
75
        this( output, TarBuffer.DEFAULT_BLOCKSIZE );
 
76
    }
 
77
 
 
78
    public TarBuffer( final OutputStream output, final int blockSize )
 
79
    {
 
80
        this( output, blockSize, TarBuffer.DEFAULT_RECORDSIZE );
 
81
    }
 
82
 
 
83
    public TarBuffer( final OutputStream output,
 
84
                      final int blockSize,
 
85
                      final int recordSize )
 
86
    {
 
87
        m_output = output;
 
88
        initialize( blockSize, recordSize );
 
89
    }
 
90
 
 
91
    /**
 
92
     * Set the debugging flag for the buffer.
 
93
     *
 
94
     * @param debug If true, print debugging output.
 
95
     */
 
96
    public void setDebug( final boolean debug )
 
97
    {
 
98
        m_debug = debug;
 
99
    }
 
100
 
 
101
    /**
 
102
     * Get the TAR Buffer's block size. Blocks consist of multiple records.
 
103
     *
 
104
     * @return The BlockSize value
 
105
     */
 
106
    public int getBlockSize()
 
107
    {
 
108
        return m_blockSize;
 
109
    }
 
110
 
 
111
    /**
 
112
     * Get the current block number, zero based.
 
113
     *
 
114
     * @return The current zero based block number.
 
115
     */
 
116
    public int getCurrentBlockNum()
 
117
    {
 
118
        return m_currBlkIdx;
 
119
    }
 
120
 
 
121
    /**
 
122
     * Get the current record number, within the current block, zero based.
 
123
     * Thus, current offset = (currentBlockNum * recsPerBlk) + currentRecNum.
 
124
     *
 
125
     * @return The current zero based record number.
 
126
     */
 
127
    public int getCurrentRecordNum()
 
128
    {
 
129
        return m_currRecIdx - 1;
 
130
    }
 
131
 
 
132
    /**
 
133
     * Get the TAR Buffer's record size.
 
134
     *
 
135
     * @return The RecordSize value
 
136
     */
 
137
    public int getRecordSize()
 
138
    {
 
139
        return m_recordSize;
 
140
    }
 
141
 
 
142
    /**
 
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.
 
145
     *
 
146
     * @param record The record data to check.
 
147
     * @return The EOFRecord value
 
148
     */
 
149
    public boolean isEOFRecord( final byte[] record )
 
150
    {
 
151
        final int size = getRecordSize();
 
152
        for( int i = 0; i < size; ++i )
 
153
        {
 
154
            if( record[ i ] != 0 )
 
155
            {
 
156
                return false;
 
157
            }
 
158
        }
 
159
 
 
160
        return true;
 
161
    }
 
162
 
 
163
    /**
 
164
     * Close the TarBuffer. If this is an output buffer, also flush the current
 
165
     * block before closing.
 
166
     */
 
167
    public void close()
 
168
        throws IOException
 
169
    {
 
170
        if( m_debug )
 
171
        {
 
172
            debug( "TarBuffer.closeBuffer()." );
 
173
        }
 
174
 
 
175
        if( null != m_output )
 
176
        {
 
177
            flushBlock();
 
178
 
 
179
            if( m_output != System.out && m_output != System.err )
 
180
            {
 
181
                m_output.close();
 
182
                m_output = null;
 
183
            }
 
184
        }
 
185
        else if( m_input != null )
 
186
        {
 
187
            if( m_input != System.in )
 
188
            {
 
189
                m_input.close();
 
190
                m_input = null;
 
191
            }
 
192
        }
 
193
    }
 
194
 
 
195
    /**
 
196
     * Read a record from the input stream and return the data.
 
197
     *
 
198
     * @return The record data.
 
199
     * @exception IOException Description of Exception
 
200
     */
 
201
    public byte[] readRecord()
 
202
        throws IOException
 
203
    {
 
204
        if( m_debug )
 
205
        {
 
206
            final String message = "ReadRecord: recIdx = " + m_currRecIdx +
 
207
                " blkIdx = " + m_currBlkIdx;
 
208
            debug( message );
 
209
        }
 
210
 
 
211
        if( null == m_input )
 
212
        {
 
213
            final String message = "reading from an output buffer";
 
214
            throw new IOException( message );
 
215
        }
 
216
 
 
217
        if( m_currRecIdx >= m_recsPerBlock )
 
218
        {
 
219
            if( !readBlock() )
 
220
            {
 
221
                return null;
 
222
            }
 
223
        }
 
224
 
 
225
        final byte[] result = new byte[ m_recordSize ];
 
226
        System.arraycopy( m_blockBuffer,
 
227
                          ( m_currRecIdx * m_recordSize ),
 
228
                          result,
 
229
                          0,
 
230
                          m_recordSize );
 
231
 
 
232
        m_currRecIdx++;
 
233
 
 
234
        return result;
 
235
    }
 
236
 
 
237
    /**
 
238
     * Skip over a record on the input stream.
 
239
     */
 
240
    public void skipRecord()
 
241
        throws IOException
 
242
    {
 
243
        if( m_debug )
 
244
        {
 
245
            final String message = "SkipRecord: recIdx = " + m_currRecIdx +
 
246
                " blkIdx = " + m_currBlkIdx;
 
247
            debug( message );
 
248
        }
 
249
 
 
250
        if( null == m_input )
 
251
        {
 
252
            final String message = "reading (via skip) from an output buffer";
 
253
            throw new IOException( message );
 
254
        }
 
255
 
 
256
        if( m_currRecIdx >= m_recsPerBlock )
 
257
        {
 
258
            if( !readBlock() )
 
259
            {
 
260
                return;// UNDONE
 
261
            }
 
262
        }
 
263
 
 
264
        m_currRecIdx++;
 
265
    }
 
266
 
 
267
    /**
 
268
     * Write an archive record to the archive.
 
269
     *
 
270
     * @param record The record data to write to the archive.
 
271
     */
 
272
    public void writeRecord( final byte[] record )
 
273
        throws IOException
 
274
    {
 
275
        if( m_debug )
 
276
        {
 
277
            final String message = "WriteRecord: recIdx = " + m_currRecIdx +
 
278
                " blkIdx = " + m_currBlkIdx;
 
279
            debug( message );
 
280
        }
 
281
 
 
282
        if( null == m_output )
 
283
        {
 
284
            final String message = "writing to an input buffer";
 
285
            throw new IOException( message );
 
286
        }
 
287
 
 
288
        if( record.length != m_recordSize )
 
289
        {
 
290
            final String message = "record to write has length '" +
 
291
                record.length + "' which is not the record size of '" +
 
292
                m_recordSize + "'";
 
293
            throw new IOException( message );
 
294
        }
 
295
 
 
296
        if( m_currRecIdx >= m_recsPerBlock )
 
297
        {
 
298
            writeBlock();
 
299
        }
 
300
 
 
301
        System.arraycopy( record,
 
302
                          0,
 
303
                          m_blockBuffer,
 
304
                          ( m_currRecIdx * m_recordSize ),
 
305
                          m_recordSize );
 
306
 
 
307
        m_currRecIdx++;
 
308
    }
 
309
 
 
310
    /**
 
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.
 
313
     *
 
314
     * @param buffer The buffer containing the record data to write.
 
315
     * @param offset The offset of the record data within buf.
 
316
     */
 
317
    public void writeRecord( final byte[] buffer, final int offset )
 
318
        throws IOException
 
319
    {
 
320
        if( m_debug )
 
321
        {
 
322
            final String message = "WriteRecord: recIdx = " + m_currRecIdx +
 
323
                " blkIdx = " + m_currBlkIdx;
 
324
            debug( message );
 
325
        }
 
326
 
 
327
        if( null == m_output )
 
328
        {
 
329
            final String message = "writing to an input buffer";
 
330
            throw new IOException( message );
 
331
        }
 
332
 
 
333
        if( ( offset + m_recordSize ) > buffer.length )
 
334
        {
 
335
            final String message = "record has length '" + buffer.length +
 
336
                "' with offset '" + offset + "' which is less than the record size of '" +
 
337
                m_recordSize + "'";
 
338
            throw new IOException( message );
 
339
        }
 
340
 
 
341
        if( m_currRecIdx >= m_recsPerBlock )
 
342
        {
 
343
            writeBlock();
 
344
        }
 
345
 
 
346
        System.arraycopy( buffer,
 
347
                          offset,
 
348
                          m_blockBuffer,
 
349
                          ( m_currRecIdx * m_recordSize ),
 
350
                          m_recordSize );
 
351
 
 
352
        m_currRecIdx++;
 
353
    }
 
354
 
 
355
    /**
 
356
     * Flush the current data block if it has any data in it.
 
357
     */
 
358
    private void flushBlock()
 
359
        throws IOException
 
360
    {
 
361
        if( m_debug )
 
362
        {
 
363
            final String message = "TarBuffer.flushBlock() called.";
 
364
            debug( message );
 
365
        }
 
366
 
 
367
        if( m_output == null )
 
368
        {
 
369
            final String message = "writing to an input buffer";
 
370
            throw new IOException( message );
 
371
        }
 
372
 
 
373
        if( m_currRecIdx > 0 )
 
374
        {
 
375
            writeBlock();
 
376
        }
 
377
    }
 
378
 
 
379
    /**
 
380
     * Initialization common to all constructors.
 
381
     */
 
382
    private void initialize( final int blockSize, final int recordSize )
 
383
    {
 
384
        m_debug = false;
 
385
        m_blockSize = blockSize;
 
386
        m_recordSize = recordSize;
 
387
        m_recsPerBlock = ( m_blockSize / m_recordSize );
 
388
        m_blockBuffer = new byte[ m_blockSize ];
 
389
 
 
390
        if( null != m_input )
 
391
        {
 
392
            m_currBlkIdx = -1;
 
393
            m_currRecIdx = m_recsPerBlock;
 
394
        }
 
395
        else
 
396
        {
 
397
            m_currBlkIdx = 0;
 
398
            m_currRecIdx = 0;
 
399
        }
 
400
    }
 
401
 
 
402
    /**
 
403
     * @return false if End-Of-File, else true
 
404
     */
 
405
    private boolean readBlock()
 
406
        throws IOException
 
407
    {
 
408
        if( m_debug )
 
409
        {
 
410
            final String message = "ReadBlock: blkIdx = " + m_currBlkIdx;
 
411
            debug( message );
 
412
        }
 
413
 
 
414
        if( null == m_input )
 
415
        {
 
416
            final String message = "reading from an output buffer";
 
417
            throw new IOException( message );
 
418
        }
 
419
 
 
420
        m_currRecIdx = 0;
 
421
 
 
422
        int offset = 0;
 
423
        int bytesNeeded = m_blockSize;
 
424
 
 
425
        while( bytesNeeded > 0 )
 
426
        {
 
427
            final long numBytes = m_input.read( m_blockBuffer, offset, bytesNeeded );
 
428
 
 
429
            //
 
430
            // NOTE
 
431
            // We have fit EOF, and the block is not full!
 
432
            //
 
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.
 
439
            //
 
440
            // Thanks to 'Yohann.Roussel@alcatel.fr' for this fix.
 
441
            //
 
442
            if( numBytes == -1 )
 
443
            {
 
444
                break;
 
445
            }
 
446
 
 
447
            offset += numBytes;
 
448
            bytesNeeded -= numBytes;
 
449
 
 
450
            if( numBytes != m_blockSize )
 
451
            {
 
452
                if( m_debug )
 
453
                {
 
454
                    System.err.println( "ReadBlock: INCOMPLETE READ "
 
455
                                        + numBytes + " of " + m_blockSize
 
456
                                        + " bytes read." );
 
457
                }
 
458
            }
 
459
        }
 
460
 
 
461
        m_currBlkIdx++;
 
462
 
 
463
        return true;
 
464
    }
 
465
 
 
466
    /**
 
467
     * Write a TarBuffer block to the archive.
 
468
     *
 
469
     * @exception IOException Description of Exception
 
470
     */
 
471
    private void writeBlock()
 
472
        throws IOException
 
473
    {
 
474
        if( m_debug )
 
475
        {
 
476
            final String message = "WriteBlock: blkIdx = " + m_currBlkIdx;
 
477
            debug( message );
 
478
        }
 
479
 
 
480
        if( null == m_output )
 
481
        {
 
482
            final String message = "writing to an input buffer";
 
483
            throw new IOException( message );
 
484
        }
 
485
 
 
486
        m_output.write( m_blockBuffer, 0, m_blockSize );
 
487
        m_output.flush();
 
488
 
 
489
        m_currRecIdx = 0;
 
490
        m_currBlkIdx++;
 
491
    }
 
492
 
 
493
    protected void debug( final String message )
 
494
    {
 
495
        if( m_debug )
 
496
        {
 
497
            System.err.println( message );
 
498
        }
 
499
    }
 
500
}