PageRenderTime 43ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

/jEdit/tags/jedit-4-2-pre14/installer/TarBuffer.java

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