File D:\maven-qcr-plugin-testsunitaires\qcr4java\jenkins-master\target\site\qc\core\src\main\java\hudson\org\apache\tools\tar\TarInputStream.java.html

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