root/trunk/Updater/ICSharpCode.SharpZipLib/Tar/TarOutputStream.cs @ 597

Wersja 597, 15.2 KB (wprowadzona przez marek, 17 years temu)

re #165

Line 
1// TarOutputStream.cs
2//
3// Copyright (C) 2001 Mike Krueger
4// Copyright 2005 John Reilly
5//
6// This program is free software; you can redistribute it and/or
7// modify it under the terms of the GNU General Public License
8// as published by the Free Software Foundation; either version 2
9// of the License, or (at your option) any later version.
10//
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14// GNU General Public License for more details.
15//
16// You should have received a copy of the GNU General Public License
17// along with this program; if not, write to the Free Software
18// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19//
20// Linking this library statically or dynamically with other modules is
21// making a combined work based on this library.  Thus, the terms and
22// conditions of the GNU General Public License cover the whole
23// combination.
24//
25// As a special exception, the copyright holders of this library give you
26// permission to link this library with independent modules to produce an
27// executable, regardless of the license terms of these independent
28// modules, and to copy and distribute the resulting executable under
29// terms of your choice, provided that you also meet, for each linked
30// independent module, the terms and conditions of the license of that
31// module.  An independent module is a module which is not derived from
32// or based on this library.  If you modify this library, you may extend
33// this exception to your version of the library, but you are not
34// obligated to do so.  If you do not wish to do so, delete this
35// exception statement from your version.
36
37using System;
38using System.IO;
39using System.Text;
40
41namespace ICSharpCode.SharpZipLib.Tar
42{
43       
44        /// <summary>
45        /// The TarOutputStream writes a UNIX tar archive as an OutputStream.
46        /// Methods are provided to put entries, and then write their contents
47        /// by writing to this stream using write().
48        /// </summary>
49        /// public
50        public class TarOutputStream : Stream
51        {
52                #region Constructors
53                /// <summary>
54                /// Construct TarOutputStream using default block factor
55                /// </summary>
56                /// <param name="outputStream">stream to write to</param>
57                public TarOutputStream(Stream outputStream)
58                        : this(outputStream, TarBuffer.DefaultBlockFactor)
59                {
60                }
61               
62                /// <summary>
63                /// Construct TarOutputStream with user specified block factor
64                /// </summary>
65                /// <param name="outputStream">stream to write to</param>
66                /// <param name="blockFactor">blocking factor</param>
67                public TarOutputStream(Stream outputStream, int blockFactor)
68                {
69                        if ( outputStream == null )
70                        {
71                                throw new ArgumentNullException("outputStream");
72                        }
73
74                        this.outputStream = outputStream;
75                        buffer = TarBuffer.CreateOutputTarBuffer(outputStream, blockFactor);
76                       
77                        assemblyBuffer = new byte[TarBuffer.BlockSize];
78                        blockBuffer  = new byte[TarBuffer.BlockSize];
79                }
80                #endregion             
81
82                /// <summary>
83                /// true if the stream supports reading; otherwise, false.
84                /// </summary>
85                public override bool CanRead
86                {
87                        get
88                        {
89                                return outputStream.CanRead;
90                        }
91                }
92               
93                /// <summary>
94                /// true if the stream supports seeking; otherwise, false.
95                /// </summary>
96                public override bool CanSeek
97                {
98                        get
99                        {
100                                return outputStream.CanSeek;
101                        }
102                }
103               
104                /// <summary>
105                /// true if stream supports writing; otherwise, false.
106                /// </summary>
107                public override bool CanWrite
108                {
109                        get
110                        {
111                                return outputStream.CanWrite;
112                        }
113                }
114               
115                /// <summary>
116                /// length of stream in bytes
117                /// </summary>
118                public override long Length
119                {
120                        get
121                        {
122                                return outputStream.Length;
123                        }
124                }
125               
126                /// <summary>
127                /// gets or sets the position within the current stream.
128                /// </summary>
129                public override long Position
130                {
131                        get
132                        {
133                                return outputStream.Position;
134                        }
135                        set
136                        {
137                                outputStream.Position = value;
138                        }
139                }
140               
141                /// <summary>
142                /// set the position within the current stream
143                /// </summary>
144                /// <param name="offset">The offset relative to the <paramref name="origin"/> to seek to</param>
145                /// <param name="origin">The <see cref="SeekOrigin"/> to seek from.</param>
146                /// <returns>The new position in the stream.</returns>
147                public override long Seek(long offset, SeekOrigin origin)
148                {
149                        return outputStream.Seek(offset, origin);
150                }
151               
152                /// <summary>
153                /// Set the length of the current stream
154                /// </summary>
155                /// <param name="value">The new stream length.</param>
156                public override void SetLength(long value)
157                {
158                        outputStream.SetLength(value);
159                }
160               
161                /// <summary>
162                /// Read a byte from the stream and advance the position within the stream
163                /// by one byte or returns -1 if at the end of the stream.
164                /// </summary>
165                /// <returns>The byte value or -1 if at end of stream</returns>
166                public override int ReadByte()
167                {
168                        return outputStream.ReadByte();
169                }
170               
171                /// <summary>
172                /// read bytes from the current stream and advance the position within the
173                /// stream by the number of bytes read.
174                /// </summary>
175                /// <param name="buffer">The buffer to store read bytes in.</param>
176                /// <param name="offset">The index into the buffer to being storing bytes at.</param>
177                /// <param name="count">The desired number of bytes to read.</param>
178                /// <returns>The total number of bytes read, or zero if at the end of the stream.
179                /// The number of bytes may be less than the <paramref name="count">count</paramref>
180                /// requested if data is not avialable.</returns>
181                public override int Read(byte[] buffer, int offset, int count)
182                {
183                        return outputStream.Read(buffer, offset, count);
184                }
185
186                /// <summary>
187                /// All buffered data is written to destination
188                /// </summary>         
189                public override void Flush()
190                {
191                        outputStream.Flush();
192                }
193                               
194                /// <summary>
195                /// Ends the TAR archive without closing the underlying OutputStream.
196                /// The result is that the EOF block of nulls is written.
197                /// </summary>
198                public void Finish()
199                {
200                        if ( IsEntryOpen )
201                        {
202                                CloseEntry();
203                        }
204                        WriteEofBlock();
205                }
206               
207                /// <summary>
208                /// Ends the TAR archive and closes the underlying OutputStream.
209                /// </summary>
210                /// <remarks>This means that Finish() is called followed by calling the
211                /// TarBuffer's Close().</remarks>
212                public override void Close()
213                {
214                        if ( !isClosed )
215                        {
216                                isClosed = true;
217                                Finish();
218                                buffer.Close();
219                        }
220                }
221               
222                /// <summary>
223                /// Get the record size being used by this stream's TarBuffer.
224                /// </summary>
225                public int RecordSize
226                {
227                        get { return buffer.RecordSize; }
228                }
229
230                /// <summary>
231                /// Get the record size being used by this stream's TarBuffer.
232                /// </summary>
233                /// <returns>
234                /// The TarBuffer record size.
235                /// </returns>
236                [Obsolete("Use RecordSize property instead")]
237                public int GetRecordSize()
238                {
239                        return buffer.RecordSize;
240                }
241               
242                /// <summary>
243                /// Get a value indicating wether an entry is open, requiring more data to be written.
244                /// </summary>
245                bool IsEntryOpen
246                {
247                        get { return (currBytes < currSize); }
248
249                }
250
251                /// <summary>
252                /// Put an entry on the output stream. This writes the entry's
253                /// header and positions the output stream for writing
254                /// the contents of the entry. Once this method is called, the
255                /// stream is ready for calls to write() to write the entry's
256                /// contents. Once the contents are written, closeEntry()
257                /// <B>MUST</B> be called to ensure that all buffered data
258                /// is completely written to the output stream.
259                /// </summary>
260                /// <param name="entry">
261                /// The TarEntry to be written to the archive.
262                /// </param>
263                public void PutNextEntry(TarEntry entry)
264                {
265                        if ( entry == null ) {
266                                throw new ArgumentNullException("entry");
267                        }
268
269                        if (entry.TarHeader.Name.Length >= TarHeader.NAMELEN) {
270                                TarHeader longHeader = new TarHeader();
271                                longHeader.TypeFlag = TarHeader.LF_GNU_LONGNAME;
272                                longHeader.Name = longHeader.Name + "././@LongLink";
273                                longHeader.UserId = 0;
274                                longHeader.GroupId = 0;
275                                longHeader.GroupName = "";
276                                longHeader.UserName = "";
277                                longHeader.LinkName = "";
278                longHeader.Size = entry.TarHeader.Name.Length;
279
280                                longHeader.WriteHeader(this.blockBuffer);
281                                this.buffer.WriteBlock(this.blockBuffer);  // Add special long filename header block
282
283                                int nameCharIndex = 0;
284
285                                while (nameCharIndex < entry.TarHeader.Name.Length) {
286                                        Array.Clear(blockBuffer, 0, blockBuffer.Length);
287                                        TarHeader.GetAsciiBytes(entry.TarHeader.Name, nameCharIndex, this.blockBuffer, 0, TarBuffer.BlockSize);
288                                        nameCharIndex += TarBuffer.BlockSize;
289                                        buffer.WriteBlock(blockBuffer);
290                                }
291                        }
292                       
293                        entry.WriteEntryHeader(blockBuffer);
294                        buffer.WriteBlock(blockBuffer);
295                       
296                        currBytes = 0;
297                       
298                        currSize = entry.IsDirectory ? 0 : entry.Size;
299                }
300               
301                /// <summary>
302                /// Close an entry. This method MUST be called for all file
303                /// entries that contain data. The reason is that we must
304                /// buffer data written to the stream in order to satisfy
305                /// the buffer's block based writes. Thus, there may be
306                /// data fragments still being assembled that must be written
307                /// to the output stream before this entry is closed and the
308                /// next entry written.
309                /// </summary>
310                public void CloseEntry()
311                {
312                        if (assemblyBufferLength > 0) {
313                                Array.Clear(assemblyBuffer, assemblyBufferLength, assemblyBuffer.Length - assemblyBufferLength);
314                               
315                                buffer.WriteBlock(assemblyBuffer);
316                               
317                                currBytes += assemblyBufferLength;
318                                assemblyBufferLength = 0;
319                        }
320                       
321                        if (currBytes < currSize) {
322                                string errorText = string.Format(
323                                        "Entry closed at '{0}' before the '{1}' bytes specified in the header were written",
324                                        currBytes, currSize);
325                                throw new TarException(errorText);
326                        }
327                }
328               
329                /// <summary>
330                /// Writes a byte to the current tar archive entry.
331                /// This method simply calls Write(byte[], int, int).
332                /// </summary>
333                /// <param name="value">
334                /// The byte to be written.
335                /// </param>
336                public override void WriteByte(byte value)
337                {
338                        Write(new byte[] { value }, 0, 1);
339                }
340               
341                /// <summary>
342                /// Writes bytes to the current tar archive entry. This method
343                /// is aware of the current entry and will throw an exception if
344                /// you attempt to write bytes past the length specified for the
345                /// current entry. The method is also (painfully) aware of the
346                /// record buffering required by TarBuffer, and manages buffers
347                /// that are not a multiple of recordsize in length, including
348                /// assembling records from small buffers.
349                /// </summary>
350                /// <param name = "buffer">
351                /// The buffer to write to the archive.
352                /// </param>
353                /// <param name = "offset">
354                /// The offset in the buffer from which to get bytes.
355                /// </param>
356                /// <param name = "count">
357                /// The number of bytes to write.
358                /// </param>
359                public override void Write(byte[] buffer, int offset, int count)
360                {
361                        if ( buffer == null ) {
362                                throw new ArgumentNullException("buffer");
363                        }
364                       
365                        if ( offset < 0 )
366                        {
367#if NETCF_1_0
368                                throw new ArgumentOutOfRangeException("offset");
369#else
370                                throw new ArgumentOutOfRangeException("offset", "Cannot be negative");
371#endif                         
372                        }
373
374                        if ( buffer.Length - offset < count )
375                        {
376                                throw new ArgumentException("offset and count combination is invalid");
377                        }
378
379                        if ( count < 0 )
380                        {
381#if NETCF_1_0
382                                throw new ArgumentOutOfRangeException("count");
383#else
384                                throw new ArgumentOutOfRangeException("count", "Cannot be negative");
385#endif
386                        }
387
388                        if ( (currBytes + count) > currSize ) {
389                                string errorText = string.Format("request to write '{0}' bytes exceeds size in header of '{1}' bytes",
390                                        count, this.currSize);
391#if NETCF_1_0
392                                throw new ArgumentOutOfRangeException("count");
393#else
394                                throw new ArgumentOutOfRangeException("count", errorText);
395#endif                         
396                        }
397                       
398                        //
399                        // We have to deal with assembly!!!
400                        // The programmer can be writing little 32 byte chunks for all
401                        // we know, and we must assemble complete blocks for writing.
402                        // TODO  REVIEW Maybe this should be in TarBuffer? Could that help to
403                        //        eliminate some of the buffer copying.
404                        //
405                        if (assemblyBufferLength > 0) {
406                                if ((assemblyBufferLength + count ) >= blockBuffer.Length) {
407                                        int aLen = blockBuffer.Length - assemblyBufferLength;
408                                       
409                                        Array.Copy(assemblyBuffer, 0, blockBuffer, 0, assemblyBufferLength);
410                                        Array.Copy(buffer, offset, blockBuffer, assemblyBufferLength, aLen);
411                                       
412                                        this.buffer.WriteBlock(blockBuffer);
413                                       
414                                        currBytes += blockBuffer.Length;
415                                       
416                                        offset    += aLen;
417                                        count -= aLen;
418                                       
419                                        assemblyBufferLength = 0;
420                                } else {
421                                        Array.Copy(buffer, offset, assemblyBuffer, assemblyBufferLength, count);
422                                        offset += count;
423                                        assemblyBufferLength += count;
424                                        count -= count;
425                                }
426                        }
427                       
428                        //
429                        // When we get here we have EITHER:
430                        //   o An empty "assembly" buffer.
431                        //   o No bytes to write (count == 0)
432                        //
433                        while (count > 0) {
434                                if (count < blockBuffer.Length) {
435                                        Array.Copy(buffer, offset, assemblyBuffer, assemblyBufferLength, count);
436                                        assemblyBufferLength += count;
437                                        break;
438                                }
439                               
440                                this.buffer.WriteBlock(buffer, offset);
441                               
442                                int bufferLength = blockBuffer.Length;
443                                currBytes += bufferLength;
444                                count -= bufferLength;
445                                offset += bufferLength;
446                        }
447                }
448               
449                /// <summary>
450                /// Write an EOF (end of archive) block to the tar archive.
451                /// An EOF block consists of all zeros.
452                /// </summary>
453                void WriteEofBlock()
454                {
455                        Array.Clear(blockBuffer, 0, blockBuffer.Length);
456                        buffer.WriteBlock(blockBuffer);
457                }
458
459                #region Instance Fields
460                /// <summary>
461                /// bytes written for this entry so far
462                /// </summary>
463                long currBytes;
464               
465                /// <summary>
466                /// current 'Assembly' buffer length
467                /// </summary>         
468                int assemblyBufferLength;
469
470                /// <summary>
471                /// Flag indicating wether this instance has been closed or not.
472                /// </summary>
473                bool isClosed;
474
475                /// <summary>
476                /// Size for the current entry
477                /// </summary>
478                protected long currSize;
479
480                /// <summary>
481                /// single block working buffer
482                /// </summary>
483                protected byte[] blockBuffer;
484
485                /// <summary>
486                /// 'Assembly' buffer used to assemble data before writing
487                /// </summary>
488                protected byte[] assemblyBuffer;
489               
490                /// <summary>
491                /// TarBuffer used to provide correct blocking factor
492                /// </summary>
493                protected TarBuffer buffer;
494               
495                /// <summary>
496                /// the destination stream for the archive contents
497                /// </summary>
498                protected Stream outputStream;
499                #endregion
500        }
501}
502
503/* The original Java file had this header:
504        ** Authored by Timothy Gerard Endres
505        ** <mailto:time@gjt.org>  <http://www.trustice.com>
506        **
507        ** This work has been placed into the public domain.
508        ** You may use this work in any way and for any purpose you wish.
509        **
510        ** THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY OF ANY KIND,
511        ** NOT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY. THE AUTHOR
512        ** OF THIS SOFTWARE, ASSUMES _NO_ RESPONSIBILITY FOR ANY
513        ** CONSEQUENCE RESULTING FROM THE USE, MODIFICATION, OR
514        ** REDISTRIBUTION OF THIS SOFTWARE.
515        **
516        */
Notatka: Zobacz TracBrowser aby uzyskać więcej informacji.