context: // ZipDirEntry.cs
  // ------------------------------------------------------------------
  //
  // Copyright (c) 2006-2011 Dino Chiesa .
  // All rights reserved.
  //
  // This code module is part of DotNetZip, a zipfile class library.
  //
  // ------------------------------------------------------------------
  //
  // This code is licensed under the Microsoft Public License.
  // See the file License.txt for the license details.
  // More info on: http://dotnetzip.codeplex.com
  //
  // ------------------------------------------------------------------
  //
  // last saved (in emacs):
  // Time-stamp: <2011-July-11 12:03:03>
  //
  // ------------------------------------------------------------------
  //
  // This module defines members of the ZipEntry class for reading the
  // Zip file central directory.
  //
  // Created: Tue, 27 Mar 2007  15:30
  //
  // ------------------------------------------------------------------
  
  
  using System;
  using System.Collections.Generic;
  
  namespace AL.Utils.Ionic.Zip
  {
  
      partial class ZipEntry
      {
          /// <summary>
          /// True if the referenced entry is a directory.
          /// </summary>
          internal bool AttributesIndicateDirectory
          {
              get { return ((_InternalFileAttrs == 0) && ((_ExternalFileAttrs & 0x0010) == 0x0010)); }
          }
  
  
          internal void ResetDirEntry()
          {
              // __FileDataPosition is the position of the file data for an entry.
              // It is _RelativeOffsetOfLocalHeader + size of local header.
  
              // We cannot know the __FileDataPosition until we read the local
              // header.
  
              // The local header is not necessarily the same length as the record
              // in the central directory.
  
              // Set to -1, to indicate we need to read this later.
              this.__FileDataPosition = -1;
  
              // set _LengthOfHeader to 0, to indicate we need to read later.
              this._LengthOfHeader = 0;
          }
  
          /// <summary>
          /// Provides a human-readable string with information about the ZipEntry.
          /// </summary>
          public string Info
          {
              get
              {
                  var builder = new System.Text.StringBuilder();
                  builder
                      .Append(string.Format("          ZipEntry: {0}\n", this.FileName))
                      .Append(string.Format("   Version Made By: {0}\n", this._VersionMadeBy))
                      .Append(string.Format(" Needed to extract: {0}\n", this.VersionNeeded));
  
                  if (this._IsDirectory)
                      builder.Append("        Entry type: directory\n");
                  else
                  {
                      builder.Append(string.Format("         File type: {0}\n", this._IsText? "text":"binary"))
                          .Append(string.Format("       Compression: {0}\n", this.CompressionMethod))
                          .Append(string.Format("        Compressed: 0x{0:X}\n", this.CompressedSize))
                          .Append(string.Format("      Uncompressed: 0x{0:X}\n", this.UncompressedSize))
                          .Append(string.Format("             CRC32: 0x{0:X8}\n", this._Crc32));
                  }
                  builder.Append(string.Format("       Disk Number: {0}\n", this._diskNumber));
                  if (this._RelativeOffsetOfLocalHeader > 0xFFFFFFFF)
                      builder
                          .Append(string.Format("   Relative Offset: 0x{0:X16}\n", this._RelativeOffsetOfLocalHeader));
                          else
                      builder
                          .Append(string.Format("   Relative Offset: 0x{0:X8}\n", this._RelativeOffsetOfLocalHeader));
  
                      builder
                      .Append(string.Format("         Bit Field: 0x{0:X4}\n", this._BitField))
                      .Append(string.Format("        Encrypted?: {0}\n", this._sourceIsEncrypted))
                      .Append(string.Format("          Timeblob: 0x{0:X8}\n", this._TimeBlob))
                          .Append(string.Format("              Time: {0}\n", AL.Utils.Ionic.Zip.SharedUtilities.PackedToDateTime(this._TimeBlob)));
  
                  builder.Append(string.Format("         Is Zip64?: {0}\n", this._InputUsesZip64));
                  if (!string.IsNullOrEmpty(this._Comment))
                  {
                      builder.Append(string.Format("           Comment: {0}\n", this._Comment));
                  }
                  builder.Append("\n");
                  return builder.ToString();
              }
          }
  
  
          // workitem 10330
          private class CopyHelper
          {
              private static System.Text.RegularExpressions.Regex re =
                  new System.Text.RegularExpressions.Regex(" \\(copy (\\d+)\\)$");
  
              private static int callCount = 0;
  
              internal static string AppendCopyToFileName(string f)
              {
                  callCount++;
                  if (callCount > 25)
                      throw new OverflowException("overflow while creating filename");
  
                  int n = 1;
                  int r = f.LastIndexOf(".");
  
                  if (r == -1)
                  {
                      // there is no extension
                      System.Text.RegularExpressions.Match m = re.Match(f);
                      if (m.Success)
                      {
                          n = Int32.Parse(m.Groups[1].Value) + 1;
                          string copy = String.Format(" (copy {0})", n);
                          f = f.Substring(0, m.Index) + copy;
                      }
                      else
                      {
                          string copy = String.Format(" (copy {0})", n);
                          f = f + copy;
                      }
                  }
                  else
                  {
                      //System.Console.WriteLine("HasExtension");
                      System.Text.RegularExpressions.Match m = re.Match(f.Substring(0, r));
                      if (m.Success)
                      {
                          n = Int32.Parse(m.Groups[1].Value) + 1;
                          string copy = String.Format(" (copy {0})", n);
                          f = f.Substring(0, m.Index) + copy + f.Substring(r);
                      }
                      else
                      {
                          string copy = String.Format(" (copy {0})", n);
                          f = f.Substring(0, r) + copy + f.Substring(r);
                      }
  
                      //System.Console.WriteLine("returning f({0})", f);
                  }
                  return f;
              }
          }
  
  
  
          /// <summary>
          ///   Reads one entry from the zip directory structure in the zip file.
          /// </summary>
          ///
          /// <param name="zf">
          ///   The zipfile for which a directory entry will be read.  From this param, the
          ///   method gets the ReadStream and the expected text encoding
          ///   (ProvisionalAlternateEncoding) which is used if the entry is not marked
          ///   UTF-8.
          /// </param>
          ///
          /// <param name="previouslySeen">
          ///   a list of previously seen entry names; used to prevent duplicates.
          /// </param>
          ///
          /// <returns>the entry read from the archive.</returns>
          internal static ZipEntry ReadDirEntry(ZipFile zf,
                                                Dictionary<String,Object> previouslySeen)
          {
              System.IO.Stream s = zf.ReadStream;
              System.Text.Encoding expectedEncoding = (zf.AlternateEncodingUsage == ZipOption.Always)
                  ? zf.AlternateEncoding
                  : ZipFile.DefaultEncoding;
  
              int signature = AL.Utils.Ionic.Zip.SharedUtilities.ReadSignature(s);
              // return null if this is not a local file header signature
              if (IsNotValidZipDirEntrySig(signature))
              {
                  s.Seek(-4, System.IO.SeekOrigin.Current);
                  // workitem 10178
                  AL.Utils.Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(s);
  
                  // Getting "not a ZipDirEntry signature" here is not always wrong or an
                  // error.  This can happen when walking through a zipfile.  After the
                  // last ZipDirEntry, we expect to read an
                  // EndOfCentralDirectorySignature.  When we get this is how we know
                  // we've reached the end of the central directory.
                  if (signature != ZipConstants.EndOfCentralDirectorySignature &&
                      signature != ZipConstants.Zip64EndOfCentralDirectoryRecordSignature &&
                      signature != ZipConstants.ZipEntrySignature  // workitem 8299
                      )
                  {
                      throw new BadReadException(String.Format("  Bad signature (0x{0:X8}) at position 0x{1:X8}", signature, s.Position));
                  }
                  return null;
              }
  
              int bytesRead = 42 + 4;
              byte[] block = new byte[42];
              int n = s.Read(block, 0, block.Length);
              if (n != block.Length) return null;
  
              int i = 0;
              ZipEntry zde = new ZipEntry();
              zde.AlternateEncoding = expectedEncoding;
              zde._Source = ZipEntrySource.ZipFile;
              zde._container = new ZipContainer(zf);
  
              unchecked
              {
                  zde._VersionMadeBy = (short)(block[i++] + block[i++] * 256);
                  zde._VersionNeeded = (short)(block[i++] + block[i++] * 256);
                  zde._BitField = (short)(block[i++] + block[i++] * 256);
                  zde._CompressionMethod = (Int16)(block[i++] + block[i++] * 256);
                  zde._TimeBlob = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256;
                  zde._LastModified = AL.Utils.Ionic.Zip.SharedUtilities.PackedToDateTime(zde._TimeBlob);
                  zde._timestamp |= ZipEntryTimestamp.DOS;
  
                  zde._Crc32 = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256;
                  zde._CompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256);
                  zde._UncompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256);
              }
  
              // preserve
              zde._CompressionMethod_FromZipFile = zde._CompressionMethod;
  
              zde._filenameLength = (short)(block[i++] + block[i++] * 256);
              zde._extraFieldLength = (short)(block[i++] + block[i++] * 256);
              zde._commentLength = (short)(block[i++] + block[i++] * 256);
              zde._diskNumber = (UInt32)(block[i++] + block[i++] * 256);
  
              zde._InternalFileAttrs = (short)(block[i++] + block[i++] * 256);
              zde._ExternalFileAttrs = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256;
  
              zde._RelativeOffsetOfLocalHeader = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256);
  
              // workitem 7801
              zde.IsText = ((zde._InternalFileAttrs & 0x01) == 0x01);
  
              block = new byte[zde._filenameLength];
              n = s.Read(block, 0, block.Length);
              bytesRead += n;
              if ((zde._BitField & 0x0800) == 0x0800)
              {
                  // UTF-8 is in use
                  zde._FileNameInArchive = AL.Utils.Ionic.Zip.SharedUtilities.Utf8StringFromBuffer(block);
              }
              else
              {
                  zde._FileNameInArchive = AL.Utils.Ionic.Zip.SharedUtilities.StringFromBuffer(block, expectedEncoding);
              }
  
              // workitem 10330
              // insure unique entry names
              while (previouslySeen.ContainsKey(zde._FileNameInArchive))
              {
                  zde._FileNameInArchive = CopyHelper.AppendCopyToFileName(zde._FileNameInArchive);
                  zde._metadataChanged = true;
              }
  
              if (zde.AttributesIndicateDirectory)
                  zde.MarkAsDirectory();  // may append a slash to filename if nec.
              // workitem 6898
              else if (zde._FileNameInArchive.EndsWith("/")) zde.MarkAsDirectory();
  
              zde._CompressedFileDataSize = zde._CompressedSize;
              if ((zde._BitField & 0x01) == 0x01)
              {
                  // this may change after processing the Extra field
                  zde._Encryption_FromZipFile = zde._Encryption =
                      EncryptionAlgorithm.PkzipWeak;
                  zde._sourceIsEncrypted = true;
              }
  
              if (zde._extraFieldLength > 0)
              {
                  zde._InputUsesZip64 = (zde._CompressedSize == 0xFFFFFFFF ||
                        zde._UncompressedSize == 0xFFFFFFFF ||
                        zde._RelativeOffsetOfLocalHeader == 0xFFFFFFFF);
  
                  // Console.WriteLine("  Input uses Z64?:      {0}", zde._InputUsesZip64);
  
                  bytesRead += zde.ProcessExtraField(s, zde._extraFieldLength);
                  zde._CompressedFileDataSize = zde._CompressedSize;
              }
  
              // we've processed the extra field, so we know the encryption method is set now.
              if (zde._Encryption == EncryptionAlgorithm.PkzipWeak)
              {
                  // the "encryption header" of 12 bytes precedes the file data
                  zde._CompressedFileDataSize -= 12;
              }
  #if AESCRYPTO
              else if (zde.Encryption == EncryptionAlgorithm.WinZipAes128 ||
                          zde.Encryption == EncryptionAlgorithm.WinZipAes256)
              {
                  zde._CompressedFileDataSize = zde.CompressedSize -
                      (ZipEntry.GetLengthOfCryptoHeaderBytes(zde.Encryption) + 10);
                  zde._LengthOfTrailer = 10;
              }
  #endif
  
              // tally the trailing descriptor
              if ((zde._BitField & 0x0008) == 0x0008)
              {
                  // sig, CRC, Comp and Uncomp sizes
                  if (zde._InputUsesZip64)
                      zde._LengthOfTrailer += 24;
                  else
                      zde._LengthOfTrailer += 16;
              }
  
              // workitem 12744
              zde.AlternateEncoding = ((zde._BitField & 0x0800) == 0x0800)
                  ? System.Text.Encoding.UTF8
                  :expectedEncoding;
  
              zde.AlternateEncodingUsage = ZipOption.Always;
  
              if (zde._commentLength > 0)
              {
                  block = new byte[zde._commentLength];
  
gt:                 n = s.Read(block, 0, block.Length);