PageRenderTime 92ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/mcs/class/ICSharpCode.SharpZipLib/ICSharpCode.SharpZipLib/Tar/TarOutputStream.cs

https://github.com/acken/mono
C# | 428 lines | 178 code | 52 blank | 198 comment | 13 complexity | 3eeefa49cb712bbf91039f4e7b6b6e62 MD5 | raw file
  1. // TarOutputStream.cs
  2. //
  3. // Copyright (C) 2001 Mike Krueger
  4. //
  5. // This program is free software; you can redistribute it and/or
  6. // modify it under the terms of the GNU General Public License
  7. // as published by the Free Software Foundation; either version 2
  8. // of the License, or (at your option) any later version.
  9. //
  10. // This program is distributed in the hope that it will be useful,
  11. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. // GNU General Public License for more details.
  14. //
  15. // You should have received a copy of the GNU General Public License
  16. // along with this program; if not, write to the Free Software
  17. // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  18. //
  19. // Linking this library statically or dynamically with other modules is
  20. // making a combined work based on this library. Thus, the terms and
  21. // conditions of the GNU General Public License cover the whole
  22. // combination.
  23. //
  24. // As a special exception, the copyright holders of this library give you
  25. // permission to link this library with independent modules to produce an
  26. // executable, regardless of the license terms of these independent
  27. // modules, and to copy and distribute the resulting executable under
  28. // terms of your choice, provided that you also meet, for each linked
  29. // independent module, the terms and conditions of the license of that
  30. // module. An independent module is a module which is not derived from
  31. // or based on this library. If you modify this library, you may extend
  32. // this exception to your version of the library, but you are not
  33. // obligated to do so. If you do not wish to do so, delete this
  34. // exception statement from your version.
  35. using System;
  36. using System.IO;
  37. using System.Text;
  38. namespace ICSharpCode.SharpZipLib.Tar
  39. {
  40. /// <summary>
  41. /// The TarOutputStream writes a UNIX tar archive as an OutputStream.
  42. /// Methods are provided to put entries, and then write their contents
  43. /// by writing to this stream using write().
  44. /// </summary>
  45. /// public
  46. public class TarOutputStream : Stream
  47. {
  48. /// <summary>
  49. /// flag indicating debugging code should be activated or not
  50. /// </summary>
  51. protected bool debug;
  52. /// <summary>
  53. /// Size for the current entry
  54. /// </summary>
  55. protected long currSize;
  56. /// <summary>
  57. /// bytes written for this entry so far
  58. /// </summary>
  59. protected long currBytes;
  60. /// <summary>
  61. /// single block working buffer
  62. /// </summary>
  63. protected byte[] blockBuf;
  64. /// <summary>
  65. /// current 'Assembly' buffer length
  66. /// </summary>
  67. protected int assemLen;
  68. /// <summary>
  69. /// 'Assembly' buffer used to assmble data before writing
  70. /// </summary>
  71. protected byte[] assemBuf;
  72. /// <summary>
  73. /// TarBuffer used to provide correct blocking factor
  74. /// </summary>
  75. protected TarBuffer buffer;
  76. /// <summary>
  77. /// the destination stream for the archive contents
  78. /// </summary>
  79. protected Stream outputStream;
  80. /// <summary>
  81. /// true if the stream supports reading; otherwise, false.
  82. /// </summary>
  83. public override bool CanRead {
  84. get {
  85. return outputStream.CanRead;
  86. }
  87. }
  88. /// <summary>
  89. /// true if the stream supports seeking; otherwise, false.
  90. /// </summary>
  91. public override bool CanSeek {
  92. get {
  93. return outputStream.CanSeek;
  94. }
  95. }
  96. /// <summary>
  97. /// true if stream supports writing; otherwise, false.
  98. /// </summary>
  99. public override bool CanWrite {
  100. get {
  101. return outputStream.CanWrite;
  102. }
  103. }
  104. /// <summary>
  105. /// length of stream in bytes
  106. /// </summary>
  107. public override long Length {
  108. get {
  109. return outputStream.Length;
  110. }
  111. }
  112. /// <summary>
  113. /// gets or sets the position within the current stream.
  114. /// </summary>
  115. public override long Position {
  116. get {
  117. return outputStream.Position;
  118. }
  119. set {
  120. outputStream.Position = value;
  121. }
  122. }
  123. /// <summary>
  124. /// set the position within the current stream
  125. /// </summary>
  126. public override long Seek(long offset, SeekOrigin origin)
  127. {
  128. return outputStream.Seek(offset, origin);
  129. }
  130. /// <summary>
  131. /// set the length of the current stream
  132. /// </summary>
  133. public override void SetLength(long val)
  134. {
  135. outputStream.SetLength(val);
  136. }
  137. /// <summary>
  138. /// Read a byte from the stream and advance the position within the stream
  139. /// by one byte or returns -1 if at the end of the stream.
  140. /// </summary>
  141. /// <returns>The byte value or -1 if at end of stream</returns>
  142. public override int ReadByte()
  143. {
  144. return outputStream.ReadByte();
  145. }
  146. /// <summary>
  147. /// read bytes from the current stream and advance the position within the
  148. /// stream by the number of bytes read.
  149. /// </summary>
  150. /// <returns>The total number of bytes read, or zero if at the end of the stream</returns>
  151. public override int Read(byte[] b, int off, int len)
  152. {
  153. return outputStream.Read(b, off, len);
  154. }
  155. /// <summary>
  156. /// All buffered data is written to destination
  157. /// </summary>
  158. public override void Flush()
  159. {
  160. outputStream.Flush();
  161. }
  162. /// <summary>
  163. /// Construct TarOutputStream using default block factor
  164. /// </summary>
  165. /// <param name="outputStream">stream to write to</param>
  166. public TarOutputStream(Stream outputStream) : this(outputStream, TarBuffer.DefaultBlockFactor)
  167. {
  168. }
  169. /// <summary>
  170. /// Construct TarOutputStream with user specified block factor
  171. /// </summary>
  172. /// <param name="outputStream">stream to write to</param>
  173. /// <param name="blockFactor">blocking factor</param>
  174. public TarOutputStream(Stream outputStream, int blockFactor)
  175. {
  176. this.outputStream = outputStream;
  177. this.buffer = TarBuffer.CreateOutputTarBuffer(outputStream, blockFactor);
  178. this.debug = false;
  179. this.assemLen = 0;
  180. this.assemBuf = new byte[TarBuffer.BlockSize];
  181. this.blockBuf = new byte[TarBuffer.BlockSize];
  182. }
  183. /// <summary>
  184. /// Ends the TAR archive without closing the underlying OutputStream.
  185. /// The result is that the EOF record of nulls is written.
  186. /// </summary>
  187. public void Finish()
  188. {
  189. this.WriteEOFRecord();
  190. }
  191. /// <summary>
  192. /// Ends the TAR archive and closes the underlying OutputStream.
  193. /// This means that finish() is called followed by calling the
  194. /// TarBuffer's close().
  195. /// </summary>
  196. public override void Close()
  197. {
  198. this.Finish();
  199. this.buffer.Close();
  200. }
  201. /// <summary>
  202. /// Get the record size being used by this stream's TarBuffer.
  203. /// </summary>
  204. /// <returns>
  205. /// The TarBuffer record size.
  206. /// </returns>
  207. public int GetRecordSize()
  208. {
  209. return this.buffer.GetRecordSize();
  210. }
  211. /// <summary>
  212. /// Put an entry on the output stream. This writes the entry's
  213. /// header and positions the output stream for writing
  214. /// the contents of the entry. Once this method is called, the
  215. /// stream is ready for calls to write() to write the entry's
  216. /// contents. Once the contents are written, closeEntry()
  217. /// <B>MUST</B> be called to ensure that all buffered data
  218. /// is completely written to the output stream.
  219. /// </summary>
  220. /// <param name="entry">
  221. /// The TarEntry to be written to the archive.
  222. /// </param>
  223. public void PutNextEntry(TarEntry entry)
  224. {
  225. if (entry.TarHeader.Name.Length >= TarHeader.NAMELEN) {
  226. TarHeader longHeader = new TarHeader();
  227. longHeader.TypeFlag = TarHeader.LF_GNU_LONGNAME;
  228. longHeader.Name = longHeader.Name + "././@LongLink";
  229. longHeader.UserId = 0;
  230. longHeader.GroupId = 0;
  231. longHeader.GroupName = "";
  232. longHeader.UserName = "";
  233. longHeader.LinkName = "";
  234. longHeader.Size = entry.TarHeader.Name.Length;
  235. longHeader.WriteHeader(this.blockBuf);
  236. this.buffer.WriteBlock(this.blockBuf); // Add special long filename header block
  237. int nameCharIndex = 0;
  238. while (nameCharIndex < entry.TarHeader.Name.Length) {
  239. Array.Clear(blockBuf, 0, blockBuf.Length);
  240. TarHeader.GetAsciiBytes(entry.TarHeader.Name, nameCharIndex, this.blockBuf, 0, TarBuffer.BlockSize);
  241. nameCharIndex += TarBuffer.BlockSize;
  242. this.buffer.WriteBlock(this.blockBuf);
  243. }
  244. }
  245. entry.WriteEntryHeader(this.blockBuf);
  246. this.buffer.WriteBlock(this.blockBuf);
  247. this.currBytes = 0;
  248. this.currSize = entry.IsDirectory ? 0 : entry.Size;
  249. }
  250. /// <summary>
  251. /// Close an entry. This method MUST be called for all file
  252. /// entries that contain data. The reason is that we must
  253. /// buffer data written to the stream in order to satisfy
  254. /// the buffer's block based writes. Thus, there may be
  255. /// data fragments still being assembled that must be written
  256. /// to the output stream before this entry is closed and the
  257. /// next entry written.
  258. /// </summary>
  259. public void CloseEntry()
  260. {
  261. if (this.assemLen > 0) {
  262. for (int i = this.assemLen; i < this.assemBuf.Length; ++i) {
  263. this.assemBuf[i] = 0;
  264. }
  265. this.buffer.WriteBlock(this.assemBuf);
  266. this.currBytes += this.assemLen;
  267. this.assemLen = 0;
  268. }
  269. if (this.currBytes < this.currSize) {
  270. throw new TarException("entry closed at '" + this.currBytes + "' before the '" + this.currSize + "' bytes specified in the header were written");
  271. }
  272. }
  273. /// <summary>
  274. /// Writes a byte to the current tar archive entry.
  275. /// This method simply calls Write(byte[], int, int).
  276. /// </summary>
  277. /// <param name="b">
  278. /// The byte to be written.
  279. /// </param>
  280. public override void WriteByte(byte b)
  281. {
  282. this.Write(new byte[] { b }, 0, 1);
  283. }
  284. /// <summary>
  285. /// Writes bytes to the current tar archive entry. This method
  286. /// is aware of the current entry and will throw an exception if
  287. /// you attempt to write bytes past the length specified for the
  288. /// current entry. The method is also (painfully) aware of the
  289. /// record buffering required by TarBuffer, and manages buffers
  290. /// that are not a multiple of recordsize in length, including
  291. /// assembling records from small buffers.
  292. /// </summary>
  293. /// <param name = "wBuf">
  294. /// The buffer to write to the archive.
  295. /// </param>
  296. /// <param name = "wOffset">
  297. /// The offset in the buffer from which to get bytes.
  298. /// </param>
  299. /// <param name = "numToWrite">
  300. /// The number of bytes to write.
  301. /// </param>
  302. public override void Write(byte[] wBuf, int wOffset, int numToWrite)
  303. {
  304. if (wBuf == null) {
  305. throw new ArgumentNullException("TarOutputStream.Write buffer null");
  306. }
  307. if ((this.currBytes + numToWrite) > this.currSize) {
  308. throw new ArgumentOutOfRangeException("request to write '" + numToWrite + "' bytes exceeds size in header of '" + this.currSize + "' bytes");
  309. }
  310. //
  311. // We have to deal with assembly!!!
  312. // The programmer can be writing little 32 byte chunks for all
  313. // we know, and we must assemble complete blocks for writing.
  314. // TODO REVIEW Maybe this should be in TarBuffer? Could that help to
  315. // eliminate some of the buffer copying.
  316. //
  317. if (this.assemLen > 0) {
  318. if ((this.assemLen + numToWrite ) >= this.blockBuf.Length) {
  319. int aLen = this.blockBuf.Length - this.assemLen;
  320. Array.Copy(this.assemBuf, 0, this.blockBuf, 0, this.assemLen);
  321. Array.Copy(wBuf, wOffset, this.blockBuf, this.assemLen, aLen);
  322. this.buffer.WriteBlock(this.blockBuf);
  323. this.currBytes += this.blockBuf.Length;
  324. wOffset += aLen;
  325. numToWrite -= aLen;
  326. this.assemLen = 0;
  327. } else { // ( (this.assemLen + numToWrite ) < this.blockBuf.length )
  328. Array.Copy(wBuf, wOffset, this.assemBuf, this.assemLen, numToWrite);
  329. wOffset += numToWrite;
  330. this.assemLen += numToWrite;
  331. numToWrite -= numToWrite;
  332. }
  333. }
  334. //
  335. // When we get here we have EITHER:
  336. // o An empty "assemble" buffer.
  337. // o No bytes to write (numToWrite == 0)
  338. //
  339. while (numToWrite > 0) {
  340. if (numToWrite < this.blockBuf.Length) {
  341. Array.Copy(wBuf, wOffset, this.assemBuf, this.assemLen, numToWrite);
  342. this.assemLen += numToWrite;
  343. break;
  344. }
  345. this.buffer.WriteBlock(wBuf, wOffset);
  346. int num = this.blockBuf.Length;
  347. this.currBytes += num;
  348. numToWrite -= num;
  349. wOffset += num;
  350. }
  351. }
  352. /// <summary>
  353. /// Write an EOF (end of archive) record to the tar archive.
  354. /// An EOF record consists of a record of all zeros.
  355. /// </summary>
  356. void WriteEOFRecord()
  357. {
  358. Array.Clear(blockBuf, 0, blockBuf.Length);
  359. this.buffer.WriteBlock(this.blockBuf);
  360. }
  361. }
  362. }
  363. /* The original Java file had this header:
  364. ** Authored by Timothy Gerard Endres
  365. ** <mailto:time@gjt.org> <http://www.trustice.com>
  366. **
  367. ** This work has been placed into the public domain.
  368. ** You may use this work in any way and for any purpose you wish.
  369. **
  370. ** THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY OF ANY KIND,
  371. ** NOT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY. THE AUTHOR
  372. ** OF THIS SOFTWARE, ASSUMES _NO_ RESPONSIBILITY FOR ANY
  373. ** CONSEQUENCE RESULTING FROM THE USE, MODIFICATION, OR
  374. ** REDISTRIBUTION OF THIS SOFTWARE.
  375. **
  376. */