~ubuntu-branches/ubuntu/jaunty/ant/jaunty-proposed

« back to all changes in this revision

Viewing changes to src/main/org/apache/tools/tar/TarInputStream.java

  • Committer: Bazaar Package Importer
  • Author(s): Stefan Gybas
  • Date: 2002-02-14 14:28:48 UTC
  • Revision ID: james.westby@ubuntu.com-20020214142848-2ww7ynmqkj31vlmn
Tags: upstream-1.4.1
ImportĀ upstreamĀ versionĀ 1.4.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * The Apache Software License, Version 1.1
 
3
 *
 
4
 * Copyright (c) 1999 The Apache Software Foundation.  All rights
 
5
 * reserved.
 
6
 *
 
7
 * Redistribution and use in source and binary forms, with or without
 
8
 * modification, are permitted provided that the following conditions
 
9
 * are met:
 
10
 *
 
11
 * 1. Redistributions of source code must retain the above copyright
 
12
 *    notice, this list of conditions and the following disclaimer.
 
13
 *
 
14
 * 2. Redistributions in binary form must reproduce the above copyright
 
15
 *    notice, this list of conditions and the following disclaimer in
 
16
 *    the documentation and/or other materials provided with the
 
17
 *    distribution.
 
18
 *
 
19
 * 3. The end-user documentation included with the redistribution, if
 
20
 *    any, must include the following acknowlegement:
 
21
 *       "This product includes software developed by the
 
22
 *        Apache Software Foundation (http://www.apache.org/)."
 
23
 *    Alternately, this acknowlegement may appear in the software itself,
 
24
 *    if and wherever such third-party acknowlegements normally appear.
 
25
 *
 
26
 * 4. The names "The Jakarta Project", "Ant", and "Apache Software
 
27
 *    Foundation" must not be used to endorse or promote products derived
 
28
 *    from this software without prior written permission. For written
 
29
 *    permission, please contact apache@apache.org.
 
30
 *
 
31
 * 5. Products derived from this software may not be called "Apache"
 
32
 *    nor may "Apache" appear in their names without prior written
 
33
 *    permission of the Apache Group.
 
34
 *
 
35
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 
36
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 
37
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 
38
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 
39
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 
40
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 
41
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 
42
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 
43
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 
44
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 
45
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 
46
 * SUCH DAMAGE.
 
47
 * ====================================================================
 
48
 *
 
49
 * This software consists of voluntary contributions made by many
 
50
 * individuals on behalf of the Apache Software Foundation.  For more
 
51
 * information on the Apache Software Foundation, please see
 
52
 * <http://www.apache.org/>.
 
53
 */
 
54
 
 
55
/*
 
56
 * This package is based on the work done by Timothy Gerard Endres 
 
57
 * (time@ice.com) to whom the Ant project is very grateful for his great code.
 
58
 */
 
59
 
 
60
package org.apache.tools.tar;
 
61
 
 
62
import java.io.*;
 
63
 
 
64
/**
 
65
 * The TarInputStream reads a UNIX tar archive as an InputStream.
 
66
 * methods are provided to position at each successive entry in
 
67
 * the archive, and the read each entry as a normal input stream
 
68
 * using read().
 
69
 * 
 
70
 * @author Timothy Gerard Endres <a href="mailto:time@ice.com">time@ice.com</a>
 
71
 * @author Stefano Mazzocchi <a href="mailto:stefano@apache.org">stefano@apache.org</a>
 
72
 */
 
73
public class TarInputStream extends FilterInputStream {
 
74
    
 
75
    protected boolean      debug;
 
76
    protected boolean      hasHitEOF;
 
77
    protected int          entrySize;
 
78
    protected int          entryOffset;
 
79
    protected byte[]       oneBuf;
 
80
    protected byte[]       readBuf;
 
81
    protected TarBuffer    buffer;
 
82
    protected TarEntry     currEntry;
 
83
 
 
84
    public TarInputStream(InputStream is) {
 
85
        this(is, TarBuffer.DEFAULT_BLKSIZE, TarBuffer.DEFAULT_RCDSIZE);
 
86
    }
 
87
 
 
88
    public TarInputStream(InputStream is, int blockSize) {
 
89
        this(is, blockSize, TarBuffer.DEFAULT_RCDSIZE);
 
90
    }
 
91
 
 
92
    public TarInputStream(InputStream is, int blockSize, int recordSize) {
 
93
        super(is);
 
94
 
 
95
        this.buffer = new TarBuffer(is, blockSize, recordSize);
 
96
        this.readBuf = null;
 
97
        this.oneBuf = new byte[1];
 
98
        this.debug = false;
 
99
        this.hasHitEOF = false;
 
100
    }
 
101
 
 
102
    /**
 
103
     * Sets the debugging flag.
 
104
     * 
 
105
     * @param debugF True to turn on debugging.
 
106
     */
 
107
    public void setDebug(boolean debug) {
 
108
        this.debug = debug;
 
109
        this.buffer.setDebug(debug);
 
110
    } 
 
111
 
 
112
    /**
 
113
     * Closes this stream. Calls the TarBuffer's close() method.
 
114
     */
 
115
    public void close() throws IOException {
 
116
        this.buffer.close();
 
117
    } 
 
118
 
 
119
    /**
 
120
     * Get the record size being used by this stream's TarBuffer.
 
121
     * 
 
122
     * @return The TarBuffer record size.
 
123
     */
 
124
    public int getRecordSize() {
 
125
        return this.buffer.getRecordSize();
 
126
    } 
 
127
 
 
128
    /**
 
129
     * Get the available data that can be read from the current
 
130
     * entry in the archive. This does not indicate how much data
 
131
     * is left in the entire archive, only in the current entry.
 
132
     * This value is determined from the entry's size header field
 
133
     * and the amount of data already read from the current entry.
 
134
     * 
 
135
     * 
 
136
     * @return The number of available bytes for the current entry.
 
137
     */
 
138
    public int available() throws IOException {
 
139
        return this.entrySize - this.entryOffset;
 
140
    } 
 
141
 
 
142
    /**
 
143
     * Skip bytes in the input buffer. This skips bytes in the
 
144
     * current entry's data, not the entire archive, and will
 
145
     * stop at the end of the current entry's data if the number
 
146
     * to skip extends beyond that point.
 
147
     * 
 
148
     * @param numToSkip The number of bytes to skip.
 
149
     */
 
150
    public void skip(int numToSkip) throws IOException {
 
151
 
 
152
        // REVIEW
 
153
        // This is horribly inefficient, but it ensures that we
 
154
        // properly skip over bytes via the TarBuffer...
 
155
        // 
 
156
        byte[] skipBuf = new byte[8 * 1024];
 
157
 
 
158
        for (int num = numToSkip; num > 0; ) {
 
159
            int numRead = this.read(skipBuf, 0, 
 
160
                                    (num > skipBuf.length ? skipBuf.length 
 
161
                                     : num));
 
162
 
 
163
            if (numRead == -1) {
 
164
                break;
 
165
            } 
 
166
 
 
167
            num -= numRead;
 
168
        } 
 
169
    } 
 
170
 
 
171
    /**
 
172
     * Since we do not support marking just yet, we return false.
 
173
     * 
 
174
     * @return False.
 
175
     */
 
176
    public boolean markSupported() {
 
177
        return false;
 
178
    } 
 
179
 
 
180
    /**
 
181
     * Since we do not support marking just yet, we do nothing.
 
182
     * 
 
183
     * @param markLimit The limit to mark.
 
184
     */
 
185
    public void mark(int markLimit) {}
 
186
 
 
187
    /**
 
188
     * Since we do not support marking just yet, we do nothing.
 
189
     */
 
190
    public void reset() {}
 
191
 
 
192
    /**
 
193
     * Get the next entry in this tar archive. This will skip
 
194
     * over any remaining data in the current entry, if there
 
195
     * is one, and place the input stream at the header of the
 
196
     * next entry, and read the header and instantiate a new
 
197
     * TarEntry from the header bytes and return that entry.
 
198
     * If there are no more entries in the archive, null will
 
199
     * be returned to indicate that the end of the archive has
 
200
     * been reached.
 
201
     * 
 
202
     * @return The next TarEntry in the archive, or null.
 
203
     */
 
204
    public TarEntry getNextEntry() throws IOException {
 
205
        if (this.hasHitEOF) {
 
206
            return null;
 
207
        } 
 
208
 
 
209
        if (this.currEntry != null) {
 
210
            int numToSkip = this.entrySize - this.entryOffset;
 
211
 
 
212
            if (this.debug) {
 
213
                System.err.println("TarInputStream: SKIP currENTRY '" 
 
214
                                   + this.currEntry.getName() + "' SZ " 
 
215
                                   + this.entrySize + " OFF " 
 
216
                                   + this.entryOffset + "  skipping " 
 
217
                                   + numToSkip + " bytes");
 
218
            } 
 
219
 
 
220
            if (numToSkip > 0) {
 
221
                this.skip(numToSkip);
 
222
            } 
 
223
 
 
224
            this.readBuf = null;
 
225
        } 
 
226
 
 
227
        byte[] headerBuf = this.buffer.readRecord();
 
228
 
 
229
        if (headerBuf == null) {
 
230
            if (this.debug) {
 
231
                System.err.println("READ NULL RECORD");
 
232
            } 
 
233
            this.hasHitEOF = true;
 
234
        } else if (this.buffer.isEOFRecord(headerBuf)) {
 
235
            if (this.debug) {
 
236
                System.err.println("READ EOF RECORD");
 
237
            } 
 
238
            this.hasHitEOF = true;
 
239
        } 
 
240
 
 
241
        if (this.hasHitEOF) {
 
242
            this.currEntry = null;
 
243
        } else {
 
244
            this.currEntry = new TarEntry(headerBuf);
 
245
 
 
246
            if (!(headerBuf[257] == 'u' && headerBuf[258] == 's' 
 
247
                    && headerBuf[259] == 't' && headerBuf[260] == 'a' 
 
248
                    && headerBuf[261] == 'r')) {
 
249
                this.entrySize = 0;
 
250
                this.entryOffset = 0;
 
251
                this.currEntry = null;
 
252
 
 
253
                throw new IOException("bad header in block " 
 
254
                                                 + this.buffer.getCurrentBlockNum() 
 
255
                                                 + " record " 
 
256
                                                 + this.buffer.getCurrentRecordNum() 
 
257
                                                 + ", " + 
 
258
                                       "header magic is not 'ustar', but '" 
 
259
                                                 + headerBuf[257] 
 
260
                                                 + headerBuf[258] 
 
261
                                                 + headerBuf[259] 
 
262
                                                 + headerBuf[260] 
 
263
                                                 + headerBuf[261] 
 
264
                                                 + "', or (dec) " 
 
265
                                                 + ((int) headerBuf[257]) 
 
266
                                                 + ", " 
 
267
                                                 + ((int) headerBuf[258]) 
 
268
                                                 + ", " 
 
269
                                                 + ((int) headerBuf[259]) 
 
270
                                                 + ", " 
 
271
                                                 + ((int) headerBuf[260]) 
 
272
                                                 + ", " 
 
273
                                                 + ((int) headerBuf[261]));
 
274
            } 
 
275
 
 
276
            if (this.debug) {
 
277
                System.err.println("TarInputStream: SET CURRENTRY '" 
 
278
                                   + this.currEntry.getName() 
 
279
                                   + "' size = " 
 
280
                                   + this.currEntry.getSize());
 
281
            } 
 
282
 
 
283
            this.entryOffset = 0;
 
284
 
 
285
            // REVIEW How do we resolve this discrepancy?!
 
286
            this.entrySize = (int) this.currEntry.getSize();
 
287
        } 
 
288
 
 
289
        if (this.currEntry != null && this.currEntry.isGNULongNameEntry()) {
 
290
            // read in the name
 
291
            StringBuffer longName = new StringBuffer();
 
292
            byte[] buffer = new byte[256];
 
293
            int length = 0;
 
294
            while ((length = read(buffer)) >= 0) {
 
295
                longName.append(new String(buffer, 0, length));
 
296
            }
 
297
            getNextEntry();
 
298
            this.currEntry.setName(longName.toString());
 
299
        }
 
300
 
 
301
        return this.currEntry;
 
302
    } 
 
303
 
 
304
    /**
 
305
     * Reads a byte from the current tar archive entry.
 
306
     * 
 
307
     * This method simply calls read( byte[], int, int ).
 
308
     * 
 
309
     * @return The byte read, or -1 at EOF.
 
310
     */
 
311
    public int read() throws IOException {
 
312
        int num = this.read(this.oneBuf, 0, 1);
 
313
 
 
314
        if (num == -1) {
 
315
            return num;
 
316
        } else {
 
317
            return (int) this.oneBuf[0];
 
318
        }
 
319
    } 
 
320
 
 
321
    /**
 
322
     * Reads bytes from the current tar archive entry.
 
323
     * 
 
324
     * This method simply calls read( byte[], int, int ).
 
325
     * 
 
326
     * @param buf The buffer into which to place bytes read.
 
327
     * @return The number of bytes read, or -1 at EOF.
 
328
     */
 
329
    public int read(byte[] buf) throws IOException {
 
330
        return this.read(buf, 0, buf.length);
 
331
    } 
 
332
 
 
333
    /**
 
334
     * Reads bytes from the current tar archive entry.
 
335
     * 
 
336
     * This method is aware of the boundaries of the current
 
337
     * entry in the archive and will deal with them as if they
 
338
     * were this stream's start and EOF.
 
339
     * 
 
340
     * @param buf The buffer into which to place bytes read.
 
341
     * @param offset The offset at which to place bytes read.
 
342
     * @param numToRead The number of bytes to read.
 
343
     * @return The number of bytes read, or -1 at EOF.
 
344
     */
 
345
    public int read(byte[] buf, int offset, int numToRead) throws IOException {
 
346
        int totalRead = 0;
 
347
 
 
348
        if (this.entryOffset >= this.entrySize) {
 
349
            return -1;
 
350
        } 
 
351
 
 
352
        if ((numToRead + this.entryOffset) > this.entrySize) {
 
353
            numToRead = (this.entrySize - this.entryOffset);
 
354
        } 
 
355
 
 
356
        if (this.readBuf != null) {
 
357
            int sz = (numToRead > this.readBuf.length) ? this.readBuf.length 
 
358
                     : numToRead;
 
359
 
 
360
            System.arraycopy(this.readBuf, 0, buf, offset, sz);
 
361
 
 
362
            if (sz >= this.readBuf.length) {
 
363
                this.readBuf = null;
 
364
            } else {
 
365
                int    newLen = this.readBuf.length - sz;
 
366
                byte[] newBuf = new byte[newLen];
 
367
 
 
368
                System.arraycopy(this.readBuf, sz, newBuf, 0, newLen);
 
369
 
 
370
                this.readBuf = newBuf;
 
371
            } 
 
372
 
 
373
            totalRead += sz;
 
374
            numToRead -= sz;
 
375
            offset += sz;
 
376
        } 
 
377
 
 
378
        while (numToRead > 0) {
 
379
            byte[] rec = this.buffer.readRecord();
 
380
 
 
381
            if (rec == null) {
 
382
                // Unexpected EOF!
 
383
                throw new IOException("unexpected EOF with " + numToRead 
 
384
                                      + " bytes unread");
 
385
            } 
 
386
 
 
387
            int sz = numToRead;
 
388
            int recLen = rec.length;
 
389
 
 
390
            if (recLen > sz) {
 
391
                System.arraycopy(rec, 0, buf, offset, sz);
 
392
 
 
393
                this.readBuf = new byte[recLen - sz];
 
394
 
 
395
                System.arraycopy(rec, sz, this.readBuf, 0, recLen - sz);
 
396
            } else {
 
397
                sz = recLen;
 
398
 
 
399
                System.arraycopy(rec, 0, buf, offset, recLen);
 
400
            } 
 
401
 
 
402
            totalRead += sz;
 
403
            numToRead -= sz;
 
404
            offset += sz;
 
405
        } 
 
406
 
 
407
        this.entryOffset += totalRead;
 
408
 
 
409
        return totalRead;
 
410
    } 
 
411
 
 
412
    /**
 
413
     * Copies the contents of the current tar archive entry directly into
 
414
     * an output stream.
 
415
     * 
 
416
     * @param out The OutputStream into which to write the entry's data.
 
417
     */
 
418
    public void copyEntryContents(OutputStream out) throws IOException {
 
419
        byte[] buf = new byte[32 * 1024];
 
420
 
 
421
        while (true) {
 
422
            int numRead = this.read(buf, 0, buf.length);
 
423
 
 
424
            if (numRead == -1) {
 
425
                break;
 
426
            } 
 
427
 
 
428
            out.write(buf, 0, numRead);
 
429
        } 
 
430
    } 
 
431
}