source: other-projects/trunk/gs3-release-maker/apache-ant-1.6.5/src/main/org/apache/tools/tar/TarInputStream.java@ 14627

Last change on this file since 14627 was 14627, checked in by oranfry, 17 years ago

initial import of the gs3-release-maker

File size: 11.9 KB
Line 
1/*
2 * Copyright 2000-2005 The Apache Software Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18/*
19 * This package is based on the work done by Timothy Gerard Endres
20 * ([email protected]) to whom the Ant project is very grateful for his great code.
21 */
22
23package org.apache.tools.tar;
24
25import java.io.FilterInputStream;
26import java.io.IOException;
27import java.io.InputStream;
28import java.io.OutputStream;
29
30/**
31 * The TarInputStream reads a UNIX tar archive as an InputStream.
32 * methods are provided to position at each successive entry in
33 * the archive, and the read each entry as a normal input stream
34 * using read().
35 *
36 */
37public class TarInputStream extends FilterInputStream {
38
39 protected boolean debug;
40 protected boolean hasHitEOF;
41 protected int entrySize;
42 protected int entryOffset;
43 protected byte[] readBuf;
44 protected TarBuffer buffer;
45 protected TarEntry currEntry;
46 private boolean v7Format;
47
48 /**
49 * This contents of this array is not used at all in this class,
50 * it is only here to avoid repreated object creation during calls
51 * to the no-arg read method.
52 */
53 protected byte[] oneBuf;
54
55 /**
56 * Constructor for TarInputStream.
57 * @param is the input stream to use
58 */
59 public TarInputStream(InputStream is) {
60 this(is, TarBuffer.DEFAULT_BLKSIZE, TarBuffer.DEFAULT_RCDSIZE);
61 }
62
63 /**
64 * Constructor for TarInputStream.
65 * @param is the input stream to use
66 * @param blockSize the block size to use
67 */
68 public TarInputStream(InputStream is, int blockSize) {
69 this(is, blockSize, TarBuffer.DEFAULT_RCDSIZE);
70 }
71
72 /**
73 * Constructor for TarInputStream.
74 * @param is the input stream to use
75 * @param blockSize the block size to use
76 * @param recordSize the record size to use
77 */
78 public TarInputStream(InputStream is, int blockSize, int recordSize) {
79 super(is);
80
81 this.buffer = new TarBuffer(is, blockSize, recordSize);
82 this.readBuf = null;
83 this.oneBuf = new byte[1];
84 this.debug = false;
85 this.hasHitEOF = false;
86 this.v7Format = false;
87 }
88
89 /**
90 * Sets the debugging flag.
91 *
92 * @param debug True to turn on debugging.
93 */
94 public void setDebug(boolean debug) {
95 this.debug = debug;
96 this.buffer.setDebug(debug);
97 }
98
99 /**
100 * Closes this stream. Calls the TarBuffer's close() method.
101 * @throws IOException on error
102 */
103 public void close() throws IOException {
104 this.buffer.close();
105 }
106
107 /**
108 * Get the record size being used by this stream's TarBuffer.
109 *
110 * @return The TarBuffer record size.
111 */
112 public int getRecordSize() {
113 return this.buffer.getRecordSize();
114 }
115
116 /**
117 * Get the available data that can be read from the current
118 * entry in the archive. This does not indicate how much data
119 * is left in the entire archive, only in the current entry.
120 * This value is determined from the entry's size header field
121 * and the amount of data already read from the current entry.
122 *
123 *
124 * @return The number of available bytes for the current entry.
125 * @throws IOException for signature
126 */
127 public int available() throws IOException {
128 return this.entrySize - this.entryOffset;
129 }
130
131 /**
132 * Skip bytes in the input buffer. This skips bytes in the
133 * current entry's data, not the entire archive, and will
134 * stop at the end of the current entry's data if the number
135 * to skip extends beyond that point.
136 *
137 * @param numToSkip The number of bytes to skip.
138 * @return the number actually skipped
139 * @throws IOException on error
140 */
141 public long skip(long numToSkip) throws IOException {
142 // REVIEW
143 // This is horribly inefficient, but it ensures that we
144 // properly skip over bytes via the TarBuffer...
145 //
146 byte[] skipBuf = new byte[8 * 1024];
147 long skip = numToSkip;
148 while (skip > 0) {
149 int realSkip = (int) (skip > skipBuf.length ? skipBuf.length : skip);
150 int numRead = this.read(skipBuf, 0, realSkip);
151 if (numRead == -1) {
152 break;
153 }
154 skip -= numRead;
155 }
156 return (numToSkip - skip);
157 }
158
159 /**
160 * Since we do not support marking just yet, we return false.
161 *
162 * @return False.
163 */
164 public boolean markSupported() {
165 return false;
166 }
167
168 /**
169 * Since we do not support marking just yet, we do nothing.
170 *
171 * @param markLimit The limit to mark.
172 */
173 public void mark(int markLimit) {
174 }
175
176 /**
177 * Since we do not support marking just yet, we do nothing.
178 */
179 public void reset() {
180 }
181
182 /**
183 * Get the next entry in this tar archive. This will skip
184 * over any remaining data in the current entry, if there
185 * is one, and place the input stream at the header of the
186 * next entry, and read the header and instantiate a new
187 * TarEntry from the header bytes and return that entry.
188 * If there are no more entries in the archive, null will
189 * be returned to indicate that the end of the archive has
190 * been reached.
191 *
192 * @return The next TarEntry in the archive, or null.
193 * @throws IOException on error
194 */
195 public TarEntry getNextEntry() throws IOException {
196 if (this.hasHitEOF) {
197 return null;
198 }
199
200 if (this.currEntry != null) {
201 int numToSkip = this.entrySize - this.entryOffset;
202
203 if (this.debug) {
204 System.err.println("TarInputStream: SKIP currENTRY '"
205 + this.currEntry.getName() + "' SZ "
206 + this.entrySize + " OFF "
207 + this.entryOffset + " skipping "
208 + numToSkip + " bytes");
209 }
210
211 if (numToSkip > 0) {
212 this.skip(numToSkip);
213 }
214
215 this.readBuf = null;
216 }
217
218 byte[] headerBuf = this.buffer.readRecord();
219
220 if (headerBuf == null) {
221 if (this.debug) {
222 System.err.println("READ NULL RECORD");
223 }
224 this.hasHitEOF = true;
225 } else if (this.buffer.isEOFRecord(headerBuf)) {
226 if (this.debug) {
227 System.err.println("READ EOF RECORD");
228 }
229 this.hasHitEOF = true;
230 }
231
232 if (this.hasHitEOF) {
233 this.currEntry = null;
234 } else {
235 this.currEntry = new TarEntry(headerBuf);
236
237 if (!(headerBuf[257] == 'u' && headerBuf[258] == 's'
238 && headerBuf[259] == 't' && headerBuf[260] == 'a'
239 && headerBuf[261] == 'r')) {
240 this.v7Format = true;
241 }
242
243 if (this.debug) {
244 System.err.println("TarInputStream: SET CURRENTRY '"
245 + this.currEntry.getName()
246 + "' size = "
247 + this.currEntry.getSize());
248 }
249
250 this.entryOffset = 0;
251
252 // REVIEW How do we resolve this discrepancy?!
253 this.entrySize = (int) this.currEntry.getSize();
254 }
255
256 if (this.currEntry != null && this.currEntry.isGNULongNameEntry()) {
257 // read in the name
258 StringBuffer longName = new StringBuffer();
259 byte[] buf = new byte[256];
260 int length = 0;
261 while ((length = read(buf)) >= 0) {
262 longName.append(new String(buf, 0, length));
263 }
264 getNextEntry();
265
266 // remove trailing null terminator
267 if (longName.length() > 0
268 && longName.charAt(longName.length() - 1) == 0) {
269 longName.deleteCharAt(longName.length() - 1);
270 }
271 this.currEntry.setName(longName.toString());
272 }
273
274 return this.currEntry;
275 }
276
277 /**
278 * Reads a byte from the current tar archive entry.
279 *
280 * This method simply calls read( byte[], int, int ).
281 *
282 * @return The byte read, or -1 at EOF.
283 * @throws IOException on error
284 */
285 public int read() throws IOException {
286 int num = this.read(this.oneBuf, 0, 1);
287 return num == -1 ? -1 : ((int) this.oneBuf[0]) & 0xFF;
288 }
289
290 /**
291 * Reads bytes from the current tar archive entry.
292 *
293 * This method is aware of the boundaries of the current
294 * entry in the archive and will deal with them as if they
295 * were this stream's start and EOF.
296 *
297 * @param buf The buffer into which to place bytes read.
298 * @param offset The offset at which to place bytes read.
299 * @param numToRead The number of bytes to read.
300 * @return The number of bytes read, or -1 at EOF.
301 * @throws IOException on error
302 */
303 public int read(byte[] buf, int offset, int numToRead) throws IOException {
304 int totalRead = 0;
305
306 if (this.entryOffset >= this.entrySize) {
307 return -1;
308 }
309
310 if ((numToRead + this.entryOffset) > this.entrySize) {
311 numToRead = (this.entrySize - this.entryOffset);
312 }
313
314 if (this.readBuf != null) {
315 int sz = (numToRead > this.readBuf.length) ? this.readBuf.length
316 : numToRead;
317
318 System.arraycopy(this.readBuf, 0, buf, offset, sz);
319
320 if (sz >= this.readBuf.length) {
321 this.readBuf = null;
322 } else {
323 int newLen = this.readBuf.length - sz;
324 byte[] newBuf = new byte[newLen];
325
326 System.arraycopy(this.readBuf, sz, newBuf, 0, newLen);
327
328 this.readBuf = newBuf;
329 }
330
331 totalRead += sz;
332 numToRead -= sz;
333 offset += sz;
334 }
335
336 while (numToRead > 0) {
337 byte[] rec = this.buffer.readRecord();
338
339 if (rec == null) {
340 // Unexpected EOF!
341 throw new IOException("unexpected EOF with " + numToRead
342 + " bytes unread");
343 }
344
345 int sz = numToRead;
346 int recLen = rec.length;
347
348 if (recLen > sz) {
349 System.arraycopy(rec, 0, buf, offset, sz);
350
351 this.readBuf = new byte[recLen - sz];
352
353 System.arraycopy(rec, sz, this.readBuf, 0, recLen - sz);
354 } else {
355 sz = recLen;
356
357 System.arraycopy(rec, 0, buf, offset, recLen);
358 }
359
360 totalRead += sz;
361 numToRead -= sz;
362 offset += sz;
363 }
364
365 this.entryOffset += totalRead;
366
367 return totalRead;
368 }
369
370 /**
371 * Copies the contents of the current tar archive entry directly into
372 * an output stream.
373 *
374 * @param out The OutputStream into which to write the entry's data.
375 * @throws IOException on error
376 */
377 public void copyEntryContents(OutputStream out) throws IOException {
378 byte[] buf = new byte[32 * 1024];
379
380 while (true) {
381 int numRead = this.read(buf, 0, buf.length);
382
383 if (numRead == -1) {
384 break;
385 }
386
387 out.write(buf, 0, numRead);
388 }
389 }
390}
Note: See TracBrowser for help on using the repository browser.