source: other-projects/trunk/gs3-release-maker/apache-ant-1.6.5/src/main/org/apache/tools/tar/TarBuffer.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: 12.6 KB
Line 
1/*
2 * Copyright 2000,2002,2004-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.InputStream;
26import java.io.OutputStream;
27import java.io.IOException;
28import java.util.Arrays;
29
30/**
31 * The TarBuffer class implements the tar archive concept
32 * of a buffered input stream. This concept goes back to the
33 * days of blocked tape drives and special io devices. In the
34 * Java universe, the only real function that this class
35 * performs is to ensure that files have the correct "block"
36 * size, or other tars will complain.
37 * <p>
38 * You should never have a need to access this class directly.
39 * TarBuffers are created by Tar IO Streams.
40 *
41 */
42
43public class TarBuffer {
44
45 public static final int DEFAULT_RCDSIZE = (512);
46 public static final int DEFAULT_BLKSIZE = (DEFAULT_RCDSIZE * 20);
47
48 private InputStream inStream;
49 private OutputStream outStream;
50 private byte[] blockBuffer;
51 private int currBlkIdx;
52 private int currRecIdx;
53 private int blockSize;
54 private int recordSize;
55 private int recsPerBlock;
56 private boolean debug;
57
58 public TarBuffer(InputStream inStream) {
59 this(inStream, TarBuffer.DEFAULT_BLKSIZE);
60 }
61
62 public TarBuffer(InputStream inStream, int blockSize) {
63 this(inStream, blockSize, TarBuffer.DEFAULT_RCDSIZE);
64 }
65
66 public TarBuffer(InputStream inStream, int blockSize, int recordSize) {
67 this.inStream = inStream;
68 this.outStream = null;
69
70 this.initialize(blockSize, recordSize);
71 }
72
73 public TarBuffer(OutputStream outStream) {
74 this(outStream, TarBuffer.DEFAULT_BLKSIZE);
75 }
76
77 public TarBuffer(OutputStream outStream, int blockSize) {
78 this(outStream, blockSize, TarBuffer.DEFAULT_RCDSIZE);
79 }
80
81 public TarBuffer(OutputStream outStream, int blockSize, int recordSize) {
82 this.inStream = null;
83 this.outStream = outStream;
84
85 this.initialize(blockSize, recordSize);
86 }
87
88 /**
89 * Initialization common to all constructors.
90 */
91 private void initialize(int blockSize, int recordSize) {
92 this.debug = false;
93 this.blockSize = blockSize;
94 this.recordSize = recordSize;
95 this.recsPerBlock = (this.blockSize / this.recordSize);
96 this.blockBuffer = new byte[this.blockSize];
97
98 if (this.inStream != null) {
99 this.currBlkIdx = -1;
100 this.currRecIdx = this.recsPerBlock;
101 } else {
102 this.currBlkIdx = 0;
103 this.currRecIdx = 0;
104 }
105 }
106
107 /**
108 * Get the TAR Buffer's block size. Blocks consist of multiple records.
109 */
110 public int getBlockSize() {
111 return this.blockSize;
112 }
113
114 /**
115 * Get the TAR Buffer's record size.
116 */
117 public int getRecordSize() {
118 return this.recordSize;
119 }
120
121 /**
122 * Set the debugging flag for the buffer.
123 *
124 * @param debug If true, print debugging output.
125 */
126 public void setDebug(boolean debug) {
127 this.debug = debug;
128 }
129
130 /**
131 * Determine if an archive record indicate End of Archive. End of
132 * archive is indicated by a record that consists entirely of null bytes.
133 *
134 * @param record The record data to check.
135 */
136 public boolean isEOFRecord(byte[] record) {
137 for (int i = 0, sz = this.getRecordSize(); i < sz; ++i) {
138 if (record[i] != 0) {
139 return false;
140 }
141 }
142
143 return true;
144 }
145
146 /**
147 * Skip over a record on the input stream.
148 */
149 public void skipRecord() throws IOException {
150 if (this.debug) {
151 System.err.println("SkipRecord: recIdx = " + this.currRecIdx
152 + " blkIdx = " + this.currBlkIdx);
153 }
154
155 if (this.inStream == null) {
156 throw new IOException("reading (via skip) from an output buffer");
157 }
158
159 if (this.currRecIdx >= this.recsPerBlock) {
160 if (!this.readBlock()) {
161 return; // UNDONE
162 }
163 }
164
165 this.currRecIdx++;
166 }
167
168 /**
169 * Read a record from the input stream and return the data.
170 *
171 * @return The record data.
172 */
173 public byte[] readRecord() throws IOException {
174 if (this.debug) {
175 System.err.println("ReadRecord: recIdx = " + this.currRecIdx
176 + " blkIdx = " + this.currBlkIdx);
177 }
178
179 if (this.inStream == null) {
180 throw new IOException("reading from an output buffer");
181 }
182
183 if (this.currRecIdx >= this.recsPerBlock) {
184 if (!this.readBlock()) {
185 return null;
186 }
187 }
188
189 byte[] result = new byte[this.recordSize];
190
191 System.arraycopy(this.blockBuffer,
192 (this.currRecIdx * this.recordSize), result, 0,
193 this.recordSize);
194
195 this.currRecIdx++;
196
197 return result;
198 }
199
200 /**
201 * @return false if End-Of-File, else true
202 */
203 private boolean readBlock() throws IOException {
204 if (this.debug) {
205 System.err.println("ReadBlock: blkIdx = " + this.currBlkIdx);
206 }
207
208 if (this.inStream == null) {
209 throw new IOException("reading from an output buffer");
210 }
211
212 this.currRecIdx = 0;
213
214 int offset = 0;
215 int bytesNeeded = this.blockSize;
216
217 while (bytesNeeded > 0) {
218 long numBytes = this.inStream.read(this.blockBuffer, offset,
219 bytesNeeded);
220
221 //
222 // NOTE
223 // We have fit EOF, and the block is not full!
224 //
225 // This is a broken archive. It does not follow the standard
226 // blocking algorithm. However, because we are generous, and
227 // it requires little effort, we will simply ignore the error
228 // and continue as if the entire block were read. This does
229 // not appear to break anything upstream. We used to return
230 // false in this case.
231 //
232 // Thanks to '[email protected]' for this fix.
233 //
234 if (numBytes == -1) {
235 // However, just leaving the unread portion of the buffer dirty does
236 // cause problems in some cases. This problem is described in
237 // http://issues.apache.org/bugzilla/show_bug.cgi?id=29877
238 //
239 // The solution is to fill the unused portion of the buffer with zeros.
240
241 Arrays.fill(blockBuffer, offset, offset + bytesNeeded, (byte) 0);
242
243 break;
244 }
245
246 offset += numBytes;
247 bytesNeeded -= numBytes;
248
249 if (numBytes != this.blockSize) {
250 if (this.debug) {
251 System.err.println("ReadBlock: INCOMPLETE READ "
252 + numBytes + " of " + this.blockSize
253 + " bytes read.");
254 }
255 }
256 }
257
258 this.currBlkIdx++;
259
260 return true;
261 }
262
263 /**
264 * Get the current block number, zero based.
265 *
266 * @return The current zero based block number.
267 */
268 public int getCurrentBlockNum() {
269 return this.currBlkIdx;
270 }
271
272 /**
273 * Get the current record number, within the current block, zero based.
274 * Thus, current offset = (currentBlockNum * recsPerBlk) + currentRecNum.
275 *
276 * @return The current zero based record number.
277 */
278 public int getCurrentRecordNum() {
279 return this.currRecIdx - 1;
280 }
281
282 /**
283 * Write an archive record to the archive.
284 *
285 * @param record The record data to write to the archive.
286 */
287 public void writeRecord(byte[] record) throws IOException {
288 if (this.debug) {
289 System.err.println("WriteRecord: recIdx = " + this.currRecIdx
290 + " blkIdx = " + this.currBlkIdx);
291 }
292
293 if (this.outStream == null) {
294 throw new IOException("writing to an input buffer");
295 }
296
297 if (record.length != this.recordSize) {
298 throw new IOException("record to write has length '"
299 + record.length
300 + "' which is not the record size of '"
301 + this.recordSize + "'");
302 }
303
304 if (this.currRecIdx >= this.recsPerBlock) {
305 this.writeBlock();
306 }
307
308 System.arraycopy(record, 0, this.blockBuffer,
309 (this.currRecIdx * this.recordSize),
310 this.recordSize);
311
312 this.currRecIdx++;
313 }
314
315 /**
316 * Write an archive record to the archive, where the record may be
317 * inside of a larger array buffer. The buffer must be "offset plus
318 * record size" long.
319 *
320 * @param buf The buffer containing the record data to write.
321 * @param offset The offset of the record data within buf.
322 */
323 public void writeRecord(byte[] buf, int offset) throws IOException {
324 if (this.debug) {
325 System.err.println("WriteRecord: recIdx = " + this.currRecIdx
326 + " blkIdx = " + this.currBlkIdx);
327 }
328
329 if (this.outStream == null) {
330 throw new IOException("writing to an input buffer");
331 }
332
333 if ((offset + this.recordSize) > buf.length) {
334 throw new IOException("record has length '" + buf.length
335 + "' with offset '" + offset
336 + "' which is less than the record size of '"
337 + this.recordSize + "'");
338 }
339
340 if (this.currRecIdx >= this.recsPerBlock) {
341 this.writeBlock();
342 }
343
344 System.arraycopy(buf, offset, this.blockBuffer,
345 (this.currRecIdx * this.recordSize),
346 this.recordSize);
347
348 this.currRecIdx++;
349 }
350
351 /**
352 * Write a TarBuffer block to the archive.
353 */
354 private void writeBlock() throws IOException {
355 if (this.debug) {
356 System.err.println("WriteBlock: blkIdx = " + this.currBlkIdx);
357 }
358
359 if (this.outStream == null) {
360 throw new IOException("writing to an input buffer");
361 }
362
363 this.outStream.write(this.blockBuffer, 0, this.blockSize);
364 this.outStream.flush();
365
366 this.currRecIdx = 0;
367 this.currBlkIdx++;
368 }
369
370 /**
371 * Flush the current data block if it has any data in it.
372 */
373 private void flushBlock() throws IOException {
374 if (this.debug) {
375 System.err.println("TarBuffer.flushBlock() called.");
376 }
377
378 if (this.outStream == null) {
379 throw new IOException("writing to an input buffer");
380 }
381
382 if (this.currRecIdx > 0) {
383 this.writeBlock();
384 }
385 }
386
387 /**
388 * Close the TarBuffer. If this is an output buffer, also flush the
389 * current block before closing.
390 */
391 public void close() throws IOException {
392 if (this.debug) {
393 System.err.println("TarBuffer.closeBuffer().");
394 }
395
396 if (this.outStream != null) {
397 this.flushBlock();
398
399 if (this.outStream != System.out
400 && this.outStream != System.err) {
401 this.outStream.close();
402
403 this.outStream = null;
404 }
405 } else if (this.inStream != null) {
406 if (this.inStream != System.in) {
407 this.inStream.close();
408
409 this.inStream = null;
410 }
411 }
412 }
413}
Note: See TracBrowser for help on using the repository browser.