/jEdit/tags/jedit-4-2-pre4/installer/TarOutputStream.java
Java | 330 lines | 170 code | 43 blank | 117 comment | 12 complexity | a915243bb734f0028bc85b9d5dab3ae8 MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, Apache-2.0, LGPL-2.0, LGPL-3.0, GPL-2.0, CC-BY-SA-3.0, LGPL-2.1, GPL-3.0, MPL-2.0-no-copyleft-exception, IPL-1.0
1/*
2** Authored by Timothy Gerard Endres
3** <mailto:time@gjt.org> <http://www.trustice.com>
4**
5** This work has been placed into the public domain.
6** You may use this work in any way and for any purpose you wish.
7**
8** THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY OF ANY KIND,
9** NOT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY. THE AUTHOR
10** OF THIS SOFTWARE, ASSUMES _NO_ RESPONSIBILITY FOR ANY
11** CONSEQUENCE RESULTING FROM THE USE, MODIFICATION, OR
12** REDISTRIBUTION OF THIS SOFTWARE.
13**
14*/
15
16package installer;
17
18import java.io.*;
19
20
21/**
22 * The TarOutputStream writes a UNIX tar archive as an OutputStream.
23 * Methods are provided to put entries, and then write their contents
24 * by writing to this stream using write().
25 *
26 *
27 * @version $Revision: 4480 $
28 * @author Timothy Gerard Endres,
29 * <a href="mailto:time@gjt.org">time@trustice.com</a>.
30 * @see TarBuffer
31 * @see TarHeader
32 * @see TarEntry
33 */
34
35
36public
37class TarOutputStream
38extends FilterOutputStream
39 {
40 protected boolean debug;
41 protected int currSize;
42 protected int currBytes;
43 protected byte[] oneBuf;
44 protected byte[] recordBuf;
45 protected int assemLen;
46 protected byte[] assemBuf;
47 protected TarBuffer buffer;
48
49
50 public
51 TarOutputStream( OutputStream os )
52 {
53 this( os, TarBuffer.DEFAULT_BLKSIZE, TarBuffer.DEFAULT_RCDSIZE );
54 }
55
56 public
57 TarOutputStream( OutputStream os, int blockSize )
58 {
59 this( os, blockSize, TarBuffer.DEFAULT_RCDSIZE );
60 }
61
62 public
63 TarOutputStream( OutputStream os, int blockSize, int recordSize )
64 {
65 super( os );
66
67 this.buffer = new TarBuffer( os, blockSize, recordSize );
68
69 this.debug = false;
70 this.assemLen = 0;
71 this.assemBuf = new byte[ recordSize ];
72 this.recordBuf = new byte[ recordSize ];
73 this.oneBuf = new byte[1];
74 }
75
76 /**
77 * Sets the debugging flag.
78 *
79 * @param debugF True to turn on debugging.
80 */
81 public void
82 setDebug( boolean debugF )
83 {
84 this.debug = debugF;
85 }
86
87 /**
88 * Sets the debugging flag in this stream's TarBuffer.
89 *
90 * @param debugF True to turn on debugging.
91 */
92 public void
93 setBufferDebug( boolean debug )
94 {
95 this.buffer.setDebug( debug );
96 }
97
98 /**
99 * Ends the TAR archive without closing the underlying OutputStream.
100 * The result is that the EOF record of nulls is written.
101 */
102
103 public void
104 finish()
105 throws IOException
106 {
107 this.writeEOFRecord();
108 }
109
110 /**
111 * Ends the TAR archive and closes the underlying OutputStream.
112 * This means that finish() is called followed by calling the
113 * TarBuffer's close().
114 */
115
116 public void
117 close()
118 throws IOException
119 {
120 this.finish();
121 this.buffer.close();
122 }
123
124 /**
125 * Get the record size being used by this stream's TarBuffer.
126 *
127 * @return The TarBuffer record size.
128 */
129 public int
130 getRecordSize()
131 {
132 return this.buffer.getRecordSize();
133 }
134
135 /**
136 * Put an entry on the output stream. This writes the entry's
137 * header record and positions the output stream for writing
138 * the contents of the entry. Once this method is called, the
139 * stream is ready for calls to write() to write the entry's
140 * contents. Once the contents are written, closeEntry()
141 * <B>MUST</B> be called to ensure that all buffered data
142 * is completely written to the output stream.
143 *
144 * @param entry The TarEntry to be written to the archive.
145 */
146 public void
147 putNextEntry( TarEntry entry )
148 throws IOException
149 {
150 if ( entry.getHeader().name.length() > TarHeader.NAMELEN )
151 throw new InvalidHeaderException
152 ( "file name '" + entry.getHeader().name
153 + "' is too long ( > "
154 + TarHeader.NAMELEN + " bytes )" );
155
156 entry.writeEntryHeader( this.recordBuf );
157 this.buffer.writeRecord( this.recordBuf );
158
159 this.currBytes = 0;
160
161 if ( entry.isDirectory() )
162 this.currSize = 0;
163 else
164 this.currSize = (int)entry.getSize();
165 }
166
167 /**
168 * Close an entry. This method MUST be called for all file
169 * entries that contain data. The reason is that we must
170 * buffer data written to the stream in order to satisfy
171 * the buffer's record based writes. Thus, there may be
172 * data fragments still being assembled that must be written
173 * to the output stream before this entry is closed and the
174 * next entry written.
175 */
176 public void
177 closeEntry()
178 throws IOException
179 {
180 if ( this.assemLen > 0 )
181 {
182 for ( int i = this.assemLen ; i < this.assemBuf.length ; ++i )
183 this.assemBuf[i] = 0;
184
185 this.buffer.writeRecord( this.assemBuf );
186
187 this.currBytes += this.assemLen;
188 this.assemLen = 0;
189 }
190
191 if ( this.currBytes < this.currSize )
192 throw new IOException
193 ( "entry closed at '" + this.currBytes
194 + "' before the '" + this.currSize
195 + "' bytes specified in the header were written" );
196 }
197
198 /**
199 * Writes a byte to the current tar archive entry.
200 *
201 * This method simply calls read( byte[], int, int ).
202 *
203 * @param b The byte written.
204 */
205 public void
206 write( int b )
207 throws IOException
208 {
209 this.oneBuf[0] = (byte) b;
210 this.write( this.oneBuf, 0, 1 );
211 }
212
213 /**
214 * Writes bytes to the current tar archive entry.
215 *
216 * This method simply calls read( byte[], int, int ).
217 *
218 * @param wBuf The buffer to write to the archive.
219 * @return The number of bytes read, or -1 at EOF.
220 */
221 public void
222 write( byte[] wBuf )
223 throws IOException
224 {
225 this.write( wBuf, 0, wBuf.length );
226 }
227
228 /**
229 * Writes bytes to the current tar archive entry. This method
230 * is aware of the current entry and will throw an exception if
231 * you attempt to write bytes past the length specified for the
232 * current entry. The method is also (painfully) aware of the
233 * record buffering required by TarBuffer, and manages buffers
234 * that are not a multiple of recordsize in length, including
235 * assembling records from small buffers.
236 *
237 * This method simply calls read( byte[], int, int ).
238 *
239 * @param wBuf The buffer to write to the archive.
240 * @param wOffset The offset in the buffer from which to get bytes.
241 * @param numToWrite The number of bytes to write.
242 */
243 public void
244 write( byte[] wBuf, int wOffset, int numToWrite )
245 throws IOException
246 {
247 if ( (this.currBytes + numToWrite) > this.currSize )
248 throw new IOException
249 ( "request to write '" + numToWrite
250 + "' bytes exceeds size in header of '"
251 + this.currSize + "' bytes" );
252
253 //
254 // We have to deal with assembly!!!
255 // The programmer can be writing little 32 byte chunks for all
256 // we know, and we must assemble complete records for writing.
257 // REVIEW Maybe this should be in TarBuffer? Could that help to
258 // eliminate some of the buffer copying.
259 //
260 if ( this.assemLen > 0 )
261 {
262 if ( (this.assemLen + numToWrite ) >= this.recordBuf.length )
263 {
264 int aLen = this.recordBuf.length - this.assemLen;
265
266 System.arraycopy
267 ( this.assemBuf, 0, this.recordBuf, 0, this.assemLen );
268
269 System.arraycopy
270 ( wBuf, wOffset, this.recordBuf, this.assemLen, aLen );
271
272 this.buffer.writeRecord( this.recordBuf );
273
274 this.currBytes += this.recordBuf.length;
275
276 wOffset += aLen;
277 numToWrite -= aLen;
278 this.assemLen = 0;
279 }
280 else // ( (this.assemLen + numToWrite ) < this.recordBuf.length )
281 {
282 System.arraycopy
283 ( wBuf, wOffset, this.assemBuf,
284 this.assemLen, numToWrite );
285 wOffset += numToWrite;
286 this.assemLen += numToWrite;
287 numToWrite -= numToWrite;
288 }
289 }
290
291 //
292 // When we get here we have EITHER:
293 // o An empty "assemble" buffer.
294 // o No bytes to write (numToWrite == 0)
295 //
296
297 for ( ; numToWrite > 0 ; )
298 {
299 if ( numToWrite < this.recordBuf.length )
300 {
301 System.arraycopy
302 ( wBuf, wOffset, this.assemBuf, this.assemLen, numToWrite );
303 this.assemLen += numToWrite;
304 break;
305 }
306
307 this.buffer.writeRecord( wBuf, wOffset );
308
309 int num = this.recordBuf.length;
310 this.currBytes += num;
311 numToWrite -= num;
312 wOffset += num;
313 }
314 }
315
316 /**
317 * Write an EOF (end of archive) record to the tar archive.
318 * An EOF record consists of a record of all zeros.
319 */
320 private void
321 writeEOFRecord()
322 throws IOException
323 {
324 for ( int i = 0 ; i < this.recordBuf.length ; ++i )
325 this.recordBuf[i] = 0;
326 this.buffer.writeRecord( this.recordBuf );
327 }
328
329 }
330