Add this for backwards compatibility
[mono.git] / mcs / class / Compat.ICSharpCode.SharpZipLib / ICSharpCode.SharpZipLib / Tar / TarOutputStream.cs
1 // TarOutputStream.cs\r
2 // Copyright (C) 2001 Mike Krueger\r
3 //\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
8 //\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
13 //\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
17 //\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
21 // combination.\r
22 // \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
34 \r
35 using System;\r
36 using System.IO;\r
37 using System.Text;\r
38 \r
39 namespace ICSharpCode.SharpZipLib.Tar \r
40 {\r
41         \r
42         /// <summary>\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
46         /// </summary>\r
47         /// public\r
48         public class TarOutputStream : Stream\r
49         {\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
56                 \r
57                 protected TarBuffer buffer;\r
58                 protected Stream    outputStream;\r
59                 \r
60                 /// <summary>\r
61                 /// I needed to implement the abstract member.\r
62                 /// </summary>\r
63                 public override bool CanRead \r
64                 {\r
65                         get \r
66                         {\r
67                                 return outputStream.CanRead;\r
68                         }\r
69                 }\r
70                 \r
71                 /// <summary>\r
72                 /// I needed to implement the abstract member.\r
73                 /// </summary>\r
74                 public override bool CanSeek \r
75                 {\r
76                         get \r
77                         {\r
78                                 return outputStream.CanSeek;     // -jr- Should be false?\r
79                         }\r
80                 }\r
81                 \r
82                 /// <summary>\r
83                 /// I needed to implement the abstract member.\r
84                 /// </summary>\r
85                 public override bool CanWrite \r
86                 {\r
87                         get \r
88                         {\r
89                                 return outputStream.CanWrite;\r
90                         }\r
91                 }\r
92                 \r
93                 /// <summary>\r
94                 /// I needed to implement the abstract member.\r
95                 /// </summary>\r
96                 public override long Length \r
97                 {\r
98                         get \r
99                         {\r
100                                 return outputStream.Length;\r
101                         }\r
102                 }\r
103                 \r
104                 /// <summary>\r
105                 /// I needed to implement the abstract member.\r
106                 /// </summary>\r
107                 public override long Position \r
108                 {\r
109                         get \r
110                         {\r
111                                 return outputStream.Position;\r
112                         }\r
113                         set \r
114                         {\r
115                                 outputStream.Position = value;\r
116                         }\r
117                 }\r
118                 \r
119                 /// <summary>\r
120                 /// I needed to implement the abstract member.\r
121                 /// </summary>\r
122                 public override long Seek(long offset, SeekOrigin origin)\r
123                 {\r
124                         return outputStream.Seek(offset, origin);\r
125                 }\r
126                 \r
127                 /// <summary>\r
128                 /// I needed to implement the abstract member.\r
129                 /// </summary>\r
130                 public override void SetLength(long val)\r
131                 {\r
132                         outputStream.SetLength(val);\r
133                 }\r
134                 \r
135                 /// <summary>\r
136                 /// I needed to implement the abstract member.\r
137                 /// </summary>\r
138                 public override int ReadByte()\r
139                 {\r
140                         return outputStream.ReadByte();\r
141                 }\r
142                 \r
143                 /// <summary>\r
144                 /// I needed to implement the abstract member.\r
145                 /// </summary>\r
146                 public override int Read(byte[] b, int off, int len)\r
147                 {\r
148                         return outputStream.Read(b, off, len);\r
149                 }\r
150                 \r
151                 public override void Flush()\r
152                 {\r
153                         outputStream.Flush();\r
154                 }\r
155                                 \r
156                 public TarOutputStream(Stream outputStream) : this(outputStream, TarBuffer.DefaultBlockFactor)\r
157                 {\r
158                 }\r
159                 \r
160                 public TarOutputStream(Stream outputStream, int blockFactor)\r
161                 {\r
162                         this.outputStream = outputStream;\r
163                         this.buffer       = TarBuffer.CreateOutputTarBuffer(outputStream, blockFactor);\r
164                         \r
165                         this.debug     = false;\r
166                         this.assemLen  = 0;\r
167                         this.assemBuf  = new byte[TarBuffer.BlockSize];\r
168                         this.blockBuf = new byte[TarBuffer.BlockSize];\r
169                 }\r
170                 \r
171                 /// <summary>\r
172                 /// Sets the debugging flag.\r
173                 /// </summary>\r
174                 /// <param name = "debugFlag">\r
175                 /// True to turn on debugging.\r
176                 /// </param>\r
177                 public void SetDebug(bool debugFlag)\r
178                 {\r
179                         this.debug = debugFlag;\r
180                         SetBufferDebug(debugFlag);\r
181                 }\r
182                 \r
183                 public void SetBufferDebug(bool debug)\r
184                 {\r
185                         this.buffer.SetDebug(debug);\r
186                 }\r
187                 \r
188                 /// <summary>\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
191                 /// </summary>\r
192                 public void Finish()\r
193                 {\r
194                         this.WriteEOFRecord();\r
195                 }\r
196                 \r
197                 /// <summary>\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
201                 /// </summary>\r
202                 public override void Close()\r
203                 {\r
204                         this.Finish();\r
205                         this.buffer.Close();\r
206                 }\r
207                 \r
208                 /// <summary>\r
209                 /// Get the record size being used by this stream's TarBuffer.\r
210                 /// </summary>\r
211                 /// <returns>\r
212                 /// The TarBuffer record size.\r
213                 /// </returns>\r
214                 public int GetRecordSize()\r
215                 {\r
216                         return this.buffer.GetRecordSize();\r
217                 }\r
218                 \r
219                 /// <summary>\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
227                 /// </summary>\r
228                 /// <param name="entry">\r
229                 /// The TarEntry to be written to the archive.\r
230                 /// </param>\r
231                 public void PutNextEntry(TarEntry entry)\r
232                 {\r
233                         if (entry.TarHeader.name.Length > TarHeader.NAMELEN) \r
234                         {\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
243 \r
244             longHeader.size = entry.TarHeader.name.Length;\r
245 \r
246             Console.WriteLine("TarOutputStream: PutNext entry Long name found size = " + longHeader.size); // DEBUG\r
247 \r
248             longHeader.WriteHeader(this.blockBuf);\r
249             this.buffer.WriteBlock(this.blockBuf);  // Add special long filename header block\r
250 \r
251             int nameCharIndex = 0;\r
252 \r
253             while (nameCharIndex < entry.TarHeader.name.Length)\r
254             {\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
258             }\r
259                         }\r
260                         \r
261                         entry.WriteEntryHeader(this.blockBuf);\r
262                         this.buffer.WriteBlock(this.blockBuf);\r
263                         \r
264                         this.currBytes = 0;\r
265                         \r
266                         this.currSize = entry.IsDirectory ? 0 : (int)entry.Size;\r
267                 }\r
268                 \r
269                 /// <summary>\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
277                 /// </summary>\r
278                 public void CloseEntry()\r
279                 {\r
280                         if (this.assemLen > 0) \r
281                         {\r
282                                 for (int i = this.assemLen; i < this.assemBuf.Length; ++i) \r
283                                 {\r
284                                         this.assemBuf[i] = 0;\r
285                                 }\r
286                                 \r
287                                 this.buffer.WriteBlock(this.assemBuf);\r
288                                 \r
289                                 this.currBytes += this.assemLen;\r
290                                 this.assemLen = 0;\r
291                         }\r
292                         \r
293                         if (this.currBytes < this.currSize) \r
294                         {\r
295                                 throw new IOException("entry closed at '" + this.currBytes + "' before the '" + this.currSize + "' bytes specified in the header were written");\r
296                         }\r
297                 }\r
298                 \r
299                 /// <summary>\r
300                 /// Writes a byte to the current tar archive entry.\r
301                 /// This method simply calls Write(byte[], int, int).\r
302                 /// </summary>\r
303                 /// <param name="b">\r
304                 /// The byte written.\r
305                 /// </param>\r
306                 public override void WriteByte(byte b)\r
307                 {\r
308                         this.Write(new byte[] { b }, 0, 1);\r
309                 }\r
310                 \r
311                 /// <summary>\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
319                 /// </summary>\r
320                 /// <param name = "wBuf">\r
321                 /// The buffer to write to the archive.\r
322                 /// </param>\r
323                 /// <param name = "wOffset">\r
324                 /// The offset in the buffer from which to get bytes.\r
325                 /// </param>\r
326                 /// <param name = "numToWrite">\r
327                 /// The number of bytes to write.\r
328                 /// </param>\r
329                 public override void Write(byte[] wBuf, int wOffset, int numToWrite)\r
330                 {\r
331                         if ((this.currBytes + numToWrite) > this.currSize) \r
332                         {\r
333                                 throw new IOException("request to write '" + numToWrite + "' bytes exceeds size in header of '" + this.currSize + "' bytes");\r
334                         }\r
335                         \r
336                         //\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
342                         //\r
343                         if (this.assemLen > 0) \r
344                         {\r
345                                 if ((this.assemLen + numToWrite ) >= this.blockBuf.Length) \r
346                                 {\r
347                                         int aLen = this.blockBuf.Length - this.assemLen;\r
348                                         \r
349                                         Array.Copy(this.assemBuf, 0, this.blockBuf, 0, this.assemLen);\r
350                                         \r
351                                         Array.Copy(wBuf, wOffset, this.blockBuf, this.assemLen, aLen);\r
352                                         \r
353                                         this.buffer.WriteBlock(this.blockBuf);\r
354                                         \r
355                                         this.currBytes += this.blockBuf.Length;\r
356                                         \r
357                                         wOffset    += aLen;\r
358                                         numToWrite -= aLen;\r
359                                         \r
360                                         this.assemLen = 0;\r
361                                 } \r
362                                 else \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
368                                 }\r
369                         }\r
370                         \r
371                         //\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
375                         //\r
376                         while (numToWrite > 0) \r
377                         {\r
378                                 if (numToWrite < this.blockBuf.Length) \r
379                                 {\r
380                                         Array.Copy(wBuf, wOffset, this.assemBuf, this.assemLen, numToWrite);\r
381                                         this.assemLen += numToWrite;\r
382                                         break;\r
383                                 }\r
384                                 \r
385                                 this.buffer.WriteBlock(wBuf, wOffset);\r
386                                 \r
387                                 int num = this.blockBuf.Length;\r
388                                 this.currBytes += num;\r
389                                 numToWrite     -= num;\r
390                                 wOffset        += num;\r
391                         }\r
392                 }\r
393                 \r
394                 /// <summary>\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
397                 /// </summary>\r
398                 void WriteEOFRecord()\r
399                 {\r
400                         for (int i = 0; i < this.blockBuf.Length; ++i) \r
401                         {\r
402                                 this.blockBuf[i] = 0;\r
403                         }\r
404                         this.buffer.WriteBlock(this.blockBuf);\r
405                 }\r
406         }\r
407 }\r
408 \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
412         **\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
415         **\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
421         **\r
422         */\r