PageRenderTime 44ms CodeModel.GetById 19ms RepoModel.GetById 0ms 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. package installer;
  16. import java.io.*;
  17. /**
  18. * The TarOutputStream writes a UNIX tar archive as an OutputStream.
  19. * Methods are provided to put entries, and then write their contents
  20. * by writing to this stream using write().
  21. *
  22. *
  23. * @version $Revision: 4480 $
  24. * @author Timothy Gerard Endres,
  25. * <a href="mailto:time@gjt.org">time@trustice.com</a>.
  26. * @see TarBuffer
  27. * @see TarHeader
  28. * @see TarEntry
  29. */
  30. public
  31. class TarOutputStream
  32. extends FilterOutputStream
  33. {
  34. protected boolean debug;
  35. protected int currSize;
  36. protected int currBytes;
  37. protected byte[] oneBuf;
  38. protected byte[] recordBuf;
  39. protected int assemLen;
  40. protected byte[] assemBuf;
  41. protected TarBuffer buffer;
  42. public
  43. TarOutputStream( OutputStream os )
  44. {
  45. this( os, TarBuffer.DEFAULT_BLKSIZE, TarBuffer.DEFAULT_RCDSIZE );
  46. }
  47. public
  48. TarOutputStream( OutputStream os, int blockSize )
  49. {
  50. this( os, blockSize, TarBuffer.DEFAULT_RCDSIZE );
  51. }
  52. public
  53. TarOutputStream( OutputStream os, int blockSize, int recordSize )
  54. {
  55. super( os );
  56. this.buffer = new TarBuffer( os, blockSize, recordSize );
  57. this.debug = false;
  58. this.assemLen = 0;
  59. this.assemBuf = new byte[ recordSize ];
  60. this.recordBuf = new byte[ recordSize ];
  61. this.oneBuf = new byte[1];
  62. }
  63. /**
  64. * Sets the debugging flag.
  65. *
  66. * @param debugF True to turn on debugging.
  67. */
  68. public void
  69. setDebug( boolean debugF )
  70. {
  71. this.debug = debugF;
  72. }
  73. /**
  74. * Sets the debugging flag in this stream's TarBuffer.
  75. *
  76. * @param debugF True to turn on debugging.
  77. */
  78. public void
  79. setBufferDebug( boolean debug )
  80. {
  81. this.buffer.setDebug( debug );
  82. }
  83. /**
  84. * Ends the TAR archive without closing the underlying OutputStream.
  85. * The result is that the EOF record of nulls is written.
  86. */
  87. public void
  88. finish()
  89. throws IOException
  90. {
  91. this.writeEOFRecord();
  92. }
  93. /**
  94. * Ends the TAR archive and closes the underlying OutputStream.
  95. * This means that finish() is called followed by calling the
  96. * TarBuffer's close().
  97. */
  98. public void
  99. close()
  100. throws IOException
  101. {
  102. this.finish();
  103. this.buffer.close();
  104. }
  105. /**
  106. * Get the record size being used by this stream's TarBuffer.
  107. *
  108. * @return The TarBuffer record size.
  109. */
  110. public int
  111. getRecordSize()
  112. {
  113. return this.buffer.getRecordSize();
  114. }
  115. /**
  116. * Put an entry on the output stream. This writes the entry's
  117. * header record and positions the output stream for writing
  118. * the contents of the entry. Once this method is called, the
  119. * stream is ready for calls to write() to write the entry's
  120. * contents. Once the contents are written, closeEntry()
  121. * <B>MUST</B> be called to ensure that all buffered data
  122. * is completely written to the output stream.
  123. *
  124. * @param entry The TarEntry to be written to the archive.
  125. */
  126. public void
  127. putNextEntry( TarEntry entry )
  128. throws IOException
  129. {
  130. if ( entry.getHeader().name.length() > TarHeader.NAMELEN )
  131. throw new InvalidHeaderException
  132. ( "file name '" + entry.getHeader().name
  133. + "' is too long ( > "
  134. + TarHeader.NAMELEN + " bytes )" );
  135. entry.writeEntryHeader( this.recordBuf );
  136. this.buffer.writeRecord( this.recordBuf );
  137. this.currBytes = 0;
  138. if ( entry.isDirectory() )
  139. this.currSize = 0;
  140. else
  141. this.currSize = (int)entry.getSize();
  142. }
  143. /**
  144. * Close an entry. This method MUST be called for all file
  145. * entries that contain data. The reason is that we must
  146. * buffer data written to the stream in order to satisfy
  147. * the buffer's record based writes. Thus, there may be
  148. * data fragments still being assembled that must be written
  149. * to the output stream before this entry is closed and the
  150. * next entry written.
  151. */
  152. public void
  153. closeEntry()
  154. throws IOException
  155. {
  156. if ( this.assemLen > 0 )
  157. {
  158. for ( int i = this.assemLen ; i < this.assemBuf.length ; ++i )
  159. this.assemBuf[i] = 0;
  160. this.buffer.writeRecord( this.assemBuf );
  161. this.currBytes += this.assemLen;
  162. this.assemLen = 0;
  163. }
  164. if ( this.currBytes < this.currSize )
  165. throw new IOException
  166. ( "entry closed at '" + this.currBytes
  167. + "' before the '" + this.currSize
  168. + "' bytes specified in the header were written" );
  169. }
  170. /**
  171. * Writes a byte to the current tar archive entry.
  172. *
  173. * This method simply calls read( byte[], int, int ).
  174. *
  175. * @param b The byte written.
  176. */
  177. public void
  178. write( int b )
  179. throws IOException
  180. {
  181. this.oneBuf[0] = (byte) b;
  182. this.write( this.oneBuf, 0, 1 );
  183. }
  184. /**
  185. * Writes bytes to the current tar archive entry.
  186. *
  187. * This method simply calls read( byte[], int, int ).
  188. *
  189. * @param wBuf The buffer to write to the archive.
  190. * @return The number of bytes read, or -1 at EOF.
  191. */
  192. public void
  193. write( byte[] wBuf )
  194. throws IOException
  195. {
  196. this.write( wBuf, 0, wBuf.length );
  197. }
  198. /**
  199. * Writes bytes to the current tar archive entry. This method
  200. * is aware of the current entry and will throw an exception if
  201. * you attempt to write bytes past the length specified for the
  202. * current entry. The method is also (painfully) aware of the
  203. * record buffering required by TarBuffer, and manages buffers
  204. * that are not a multiple of recordsize in length, including
  205. * assembling records from small buffers.
  206. *
  207. * This method simply calls read( byte[], int, int ).
  208. *
  209. * @param wBuf The buffer to write to the archive.
  210. * @param wOffset The offset in the buffer from which to get bytes.
  211. * @param numToWrite The number of bytes to write.
  212. */
  213. public void
  214. write( byte[] wBuf, int wOffset, int numToWrite )
  215. throws IOException
  216. {
  217. if ( (this.currBytes + numToWrite) > this.currSize )
  218. throw new IOException
  219. ( "request to write '" + numToWrite
  220. + "' bytes exceeds size in header of '"
  221. + this.currSize + "' bytes" );
  222. //
  223. // We have to deal with assembly!!!
  224. // The programmer can be writing little 32 byte chunks for all
  225. // we know, and we must assemble complete records for writing.
  226. // REVIEW Maybe this should be in TarBuffer? Could that help to
  227. // eliminate some of the buffer copying.
  228. //
  229. if ( this.assemLen > 0 )
  230. {
  231. if ( (this.assemLen + numToWrite ) >= this.recordBuf.length )
  232. {
  233. int aLen = this.recordBuf.length - this.assemLen;
  234. System.arraycopy
  235. ( this.assemBuf, 0, this.recordBuf, 0, this.assemLen );
  236. System.arraycopy
  237. ( wBuf, wOffset, this.recordBuf, this.assemLen, aLen );
  238. this.buffer.writeRecord( this.recordBuf );
  239. this.currBytes += this.recordBuf.length;
  240. wOffset += aLen;
  241. numToWrite -= aLen;
  242. this.assemLen = 0;
  243. }
  244. else // ( (this.assemLen + numToWrite ) < this.recordBuf.length )
  245. {
  246. System.arraycopy
  247. ( wBuf, wOffset, this.assemBuf,
  248. this.assemLen, numToWrite );
  249. wOffset += numToWrite;
  250. this.assemLen += numToWrite;
  251. numToWrite -= numToWrite;
  252. }
  253. }
  254. //
  255. // When we get here we have EITHER:
  256. // o An empty "assemble" buffer.
  257. // o No bytes to write (numToWrite == 0)
  258. //
  259. for ( ; numToWrite > 0 ; )
  260. {
  261. if ( numToWrite < this.recordBuf.length )
  262. {
  263. System.arraycopy
  264. ( wBuf, wOffset, this.assemBuf, this.assemLen, numToWrite );
  265. this.assemLen += numToWrite;
  266. break;
  267. }
  268. this.buffer.writeRecord( wBuf, wOffset );
  269. int num = this.recordBuf.length;
  270. this.currBytes += num;
  271. numToWrite -= num;
  272. wOffset += num;
  273. }
  274. }
  275. /**
  276. * Write an EOF (end of archive) record to the tar archive.
  277. * An EOF record consists of a record of all zeros.
  278. */
  279. private void
  280. writeEOFRecord()
  281. throws IOException
  282. {
  283. for ( int i = 0 ; i < this.recordBuf.length ; ++i )
  284. this.recordBuf[i] = 0;
  285. this.buffer.writeRecord( this.recordBuf );
  286. }
  287. }