1 // TarOutputStream.cs
\r
2 // Copyright (C) 2001 Mike Krueger
\r
4 // This program is free software; you can redistribute it and/or
\r
5 // modify it under the terms of the GNU General Public License
\r
6 // as published by the Free Software Foundation; either version 2
\r
7 // of the License, or (at your option) any later version.
\r
9 // This program is distributed in the hope that it will be useful,
\r
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
12 // GNU General Public License for more details.
\r
14 // You should have received a copy of the GNU General Public License
\r
15 // along with this program; if not, write to the Free Software
\r
16 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
\r
18 // Linking this library statically or dynamically with other modules is
\r
19 // making a combined work based on this library. Thus, the terms and
\r
20 // conditions of the GNU General Public License cover the whole
\r
23 // As a special exception, the copyright holders of this library give you
\r
24 // permission to link this library with independent modules to produce an
\r
25 // executable, regardless of the license terms of these independent
\r
26 // modules, and to copy and distribute the resulting executable under
\r
27 // terms of your choice, provided that you also meet, for each linked
\r
28 // independent module, the terms and conditions of the license of that
\r
29 // module. An independent module is a module which is not derived from
\r
30 // or based on this library. If you modify this library, you may extend
\r
31 // this exception to your version of the library, but you are not
\r
32 // obligated to do so. If you do not wish to do so, delete this
\r
33 // exception statement from your version.
\r
39 namespace ICSharpCode.SharpZipLib.Tar
\r
43 /// The TarOutputStream writes a UNIX tar archive as an OutputStream.
\r
44 /// Methods are provided to put entries, and then write their contents
\r
45 /// by writing to this stream using write().
\r
48 public class TarOutputStream : Stream
\r
50 protected bool debug;
\r
51 protected int currSize;
\r
52 protected int currBytes;
\r
53 protected byte[] blockBuf;
\r
54 protected int assemLen;
\r
55 protected byte[] assemBuf;
\r
57 protected TarBuffer buffer;
\r
58 protected Stream outputStream;
\r
61 /// I needed to implement the abstract member.
\r
63 public override bool CanRead
\r
67 return outputStream.CanRead;
\r
72 /// I needed to implement the abstract member.
\r
74 public override bool CanSeek
\r
78 return outputStream.CanSeek; // -jr- Should be false?
\r
83 /// I needed to implement the abstract member.
\r
85 public override bool CanWrite
\r
89 return outputStream.CanWrite;
\r
94 /// I needed to implement the abstract member.
\r
96 public override long Length
\r
100 return outputStream.Length;
\r
105 /// I needed to implement the abstract member.
\r
107 public override long Position
\r
111 return outputStream.Position;
\r
115 outputStream.Position = value;
\r
120 /// I needed to implement the abstract member.
\r
122 public override long Seek(long offset, SeekOrigin origin)
\r
124 return outputStream.Seek(offset, origin);
\r
128 /// I needed to implement the abstract member.
\r
130 public override void SetLength(long val)
\r
132 outputStream.SetLength(val);
\r
136 /// I needed to implement the abstract member.
\r
138 public override int ReadByte()
\r
140 return outputStream.ReadByte();
\r
144 /// I needed to implement the abstract member.
\r
146 public override int Read(byte[] b, int off, int len)
\r
148 return outputStream.Read(b, off, len);
\r
151 public override void Flush()
\r
153 outputStream.Flush();
\r
156 public TarOutputStream(Stream outputStream) : this(outputStream, TarBuffer.DefaultBlockFactor)
\r
160 public TarOutputStream(Stream outputStream, int blockFactor)
\r
162 this.outputStream = outputStream;
\r
163 this.buffer = TarBuffer.CreateOutputTarBuffer(outputStream, blockFactor);
\r
165 this.debug = false;
\r
167 this.assemBuf = new byte[TarBuffer.BlockSize];
\r
168 this.blockBuf = new byte[TarBuffer.BlockSize];
\r
172 /// Sets the debugging flag.
\r
174 /// <param name = "debugFlag">
\r
175 /// True to turn on debugging.
\r
177 public void SetDebug(bool debugFlag)
\r
179 this.debug = debugFlag;
\r
180 SetBufferDebug(debugFlag);
\r
183 public void SetBufferDebug(bool debug)
\r
185 this.buffer.SetDebug(debug);
\r
189 /// Ends the TAR archive without closing the underlying OutputStream.
\r
190 /// The result is that the EOF record of nulls is written.
\r
192 public void Finish()
\r
194 this.WriteEOFRecord();
\r
198 /// Ends the TAR archive and closes the underlying OutputStream.
\r
199 /// This means that finish() is called followed by calling the
\r
200 /// TarBuffer's close().
\r
202 public override void Close()
\r
205 this.buffer.Close();
\r
209 /// Get the record size being used by this stream's TarBuffer.
\r
212 /// The TarBuffer record size.
\r
214 public int GetRecordSize()
\r
216 return this.buffer.GetRecordSize();
\r
220 /// Put an entry on the output stream. This writes the entry's
\r
221 /// header and positions the output stream for writing
\r
222 /// the contents of the entry. Once this method is called, the
\r
223 /// stream is ready for calls to write() to write the entry's
\r
224 /// contents. Once the contents are written, closeEntry()
\r
225 /// <B>MUST</B> be called to ensure that all buffered data
\r
226 /// is completely written to the output stream.
\r
228 /// <param name="entry">
\r
229 /// The TarEntry to be written to the archive.
\r
231 public void PutNextEntry(TarEntry entry)
\r
233 if (entry.TarHeader.name.Length > TarHeader.NAMELEN)
\r
235 TarHeader longHeader = new TarHeader();
\r
236 longHeader.typeFlag = TarHeader.LF_GNU_LONGNAME;
\r
237 longHeader.name.Append("././@LongLink");
\r
238 longHeader.userId = 0;
\r
239 longHeader.groupId = 0;
\r
240 longHeader.groupName.Length = 0;
\r
241 longHeader.userName.Length = 0;
\r
242 longHeader.linkName.Length = 0;
\r
244 longHeader.size = entry.TarHeader.name.Length;
\r
246 Console.WriteLine("TarOutputStream: PutNext entry Long name found size = " + longHeader.size); // DEBUG
\r
248 longHeader.WriteHeader(this.blockBuf);
\r
249 this.buffer.WriteBlock(this.blockBuf); // Add special long filename header block
\r
251 int nameCharIndex = 0;
\r
253 while (nameCharIndex < entry.TarHeader.name.Length)
\r
255 TarHeader.GetNameBytes(entry.TarHeader.name, nameCharIndex, this.blockBuf, 0, TarBuffer.BlockSize);
\r
256 nameCharIndex += TarBuffer.BlockSize;
\r
257 this.buffer.WriteBlock(this.blockBuf);
\r
261 entry.WriteEntryHeader(this.blockBuf);
\r
262 this.buffer.WriteBlock(this.blockBuf);
\r
264 this.currBytes = 0;
\r
266 this.currSize = entry.IsDirectory ? 0 : (int)entry.Size;
\r
270 /// Close an entry. This method MUST be called for all file
\r
271 /// entries that contain data. The reason is that we must
\r
272 /// buffer data written to the stream in order to satisfy
\r
273 /// the buffer's block based writes. Thus, there may be
\r
274 /// data fragments still being assembled that must be written
\r
275 /// to the output stream before this entry is closed and the
\r
276 /// next entry written.
\r
278 public void CloseEntry()
\r
280 if (this.assemLen > 0)
\r
282 for (int i = this.assemLen; i < this.assemBuf.Length; ++i)
\r
284 this.assemBuf[i] = 0;
\r
287 this.buffer.WriteBlock(this.assemBuf);
\r
289 this.currBytes += this.assemLen;
\r
293 if (this.currBytes < this.currSize)
\r
295 throw new IOException("entry closed at '" + this.currBytes + "' before the '" + this.currSize + "' bytes specified in the header were written");
\r
300 /// Writes a byte to the current tar archive entry.
\r
301 /// This method simply calls Write(byte[], int, int).
\r
303 /// <param name="b">
\r
304 /// The byte written.
\r
306 public override void WriteByte(byte b)
\r
308 this.Write(new byte[] { b }, 0, 1);
\r
312 /// Writes bytes to the current tar archive entry. This method
\r
313 /// is aware of the current entry and will throw an exception if
\r
314 /// you attempt to write bytes past the length specified for the
\r
315 /// current entry. The method is also (painfully) aware of the
\r
316 /// record buffering required by TarBuffer, and manages buffers
\r
317 /// that are not a multiple of recordsize in length, including
\r
318 /// assembling records from small buffers.
\r
320 /// <param name = "wBuf">
\r
321 /// The buffer to write to the archive.
\r
323 /// <param name = "wOffset">
\r
324 /// The offset in the buffer from which to get bytes.
\r
326 /// <param name = "numToWrite">
\r
327 /// The number of bytes to write.
\r
329 public override void Write(byte[] wBuf, int wOffset, int numToWrite)
\r
331 if ((this.currBytes + numToWrite) > this.currSize)
\r
333 throw new IOException("request to write '" + numToWrite + "' bytes exceeds size in header of '" + this.currSize + "' bytes");
\r
337 // We have to deal with assembly!!!
\r
338 // The programmer can be writing little 32 byte chunks for all
\r
339 // we know, and we must assemble complete records for writing.
\r
340 // REVIEW Maybe this should be in TarBuffer? Could that help to
\r
341 // eliminate some of the buffer copying.
\r
343 if (this.assemLen > 0)
\r
345 if ((this.assemLen + numToWrite ) >= this.blockBuf.Length)
\r
347 int aLen = this.blockBuf.Length - this.assemLen;
\r
349 Array.Copy(this.assemBuf, 0, this.blockBuf, 0, this.assemLen);
\r
351 Array.Copy(wBuf, wOffset, this.blockBuf, this.assemLen, aLen);
\r
353 this.buffer.WriteBlock(this.blockBuf);
\r
355 this.currBytes += this.blockBuf.Length;
\r
358 numToWrite -= aLen;
\r
363 {// ( (this.assemLen + numToWrite ) < this.blockBuf.length )
\r
364 Array.Copy(wBuf, wOffset, this.assemBuf, this.assemLen, numToWrite);
\r
365 wOffset += numToWrite;
\r
366 this.assemLen += numToWrite;
\r
367 numToWrite -= numToWrite;
\r
372 // When we get here we have EITHER:
\r
373 // o An empty "assemble" buffer.
\r
374 // o No bytes to write (numToWrite == 0)
\r
376 while (numToWrite > 0)
\r
378 if (numToWrite < this.blockBuf.Length)
\r
380 Array.Copy(wBuf, wOffset, this.assemBuf, this.assemLen, numToWrite);
\r
381 this.assemLen += numToWrite;
\r
385 this.buffer.WriteBlock(wBuf, wOffset);
\r
387 int num = this.blockBuf.Length;
\r
388 this.currBytes += num;
\r
395 /// Write an EOF (end of archive) record to the tar archive.
\r
396 /// An EOF record consists of a record of all zeros.
\r
398 void WriteEOFRecord()
\r
400 for (int i = 0; i < this.blockBuf.Length; ++i)
\r
402 this.blockBuf[i] = 0;
\r
404 this.buffer.WriteBlock(this.blockBuf);
\r
409 /* The original Java file had this header:
\r
410 ** Authored by Timothy Gerard Endres
\r
411 ** <mailto:time@gjt.org> <http://www.trustice.com>
\r
413 ** This work has been placed into the public domain.
\r
414 ** You may use this work in any way and for any purpose you wish.
\r
416 ** THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY OF ANY KIND,
\r
417 ** NOT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY. THE AUTHOR
\r
418 ** OF THIS SOFTWARE, ASSUMES _NO_ RESPONSIBILITY FOR ANY
\r
419 ** CONSEQUENCE RESULTING FROM THE USE, MODIFICATION, OR
\r
420 ** REDISTRIBUTION OF THIS SOFTWARE.
\r