PageRenderTime 28ms CodeModel.GetById 5ms app.highlight 19ms RepoModel.GetById 1ms app.codeStats 0ms

/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