// @(#)root/io:$Id$
// Author: Fons Rademakers and Lassi Tuura  30/6/04

/*************************************************************************
 * Copyright (C) 1995-2004, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TZIPFile                                                             //
//                                                                      //
// This class describes a ZIP archive file containing multiple          //
// sub-files. Typically the sub-files are ROOT files. Notice that       //
// the ROOT files should not be compressed when being added to the      //
// ZIP file, since ROOT files are normally already compressed.          //
// Such a ZIP file should be created like:                              //
//                                                                      //
//    zip -n root multi file1.root file2.root                           //
//                                                                      //
// which creates a ZIP file multi.zip.                                  //
//                                                                      //
// A ZIP archive consists of files compressed with the popular ZLIB     //
// compression algorithm. The archive format is used among others by    //
// PKZip and Info-ZIP. The compression algorithm is also used by        //
// GZIP and the PNG graphics standard. The format of the archives is    //
// explained briefly below. This class provides an interface to read    //
// such archives.                                                       //
//                                                                      //
// A ZIP archive contains a prefix, series of archive members           //
// (sub-files), and a central directory. In theory the archive could    //
// span multiple disks (or files) with the central directory of the     //
// whole archive on the last disk, but this class does not support      //
// such multi-part archives. The prefix is only used in self-extracting //
// executable archive files.                                            //
//                                                                      //
// The members are stored in the archive sequentially, each with a      //
// local header followed by the (optionally) compressed data; the local //
// header describes the member, including its file name and compressed  //
// and real sizes. The central directory includes the member details    //
// again, plus allows an extra member comment to be added. The last     //
// member in the central directory is an end marker that can contain    //
// a comment for the whole archive. Both the local header and the       //
// central directory can also carry extra member-specific data; the     //
// data in the local and global parts can be different.                 //
// The fact that the archive has a global directory makes it efficient  //
// and allows for only the reading of the desired data, one does not    //
// have to scan through the whole file to find the desired sub-file.    //
// The Zip64 extensions are supported so files larger than 2GB can be   //
// stored in archives larger than 4 GB.                                 //
//                                                                      //
// Once the archive has been opened, the client can query the members   //
// and read their contents by asking the archive for an offset where    //
// the sub-file starts. The members can be accessed in any order.       //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include "TZIPFile.h"
#include "TFile.h"
#include "TObjArray.h"


ClassImp(TZIPFile)

//______________________________________________________________________________
TZIPFile::TZIPFile() : TArchiveFile()
{
   // Default ctor.

   fDirPos     = 0;
   fDirSize    = 0;
   fDirOffset  = 0;
}

//______________________________________________________________________________
TZIPFile::TZIPFile(const char *archive, const char *member, TFile *file)
   : TArchiveFile(archive, member, file)
{
   // Specify the archive name and member name. The member can be a decimal
   // number which allows to access the n-th member.

   fDirPos     = 0;
   fDirSize    = 0;
   fDirOffset  = 0;
}

//______________________________________________________________________________
Int_t TZIPFile::OpenArchive()
{
   // Open archive and read end-header and directory. Returns -1 in case
   // of error, 0 otherwise.

   if (ReadEndHeader(FindEndHeader()) == -1)
      return -1;
   return ReadDirectory();
}

//______________________________________________________________________________
Long64_t TZIPFile::FindEndHeader()
{
   // Find the end header of the ZIP archive. Returns 0 in case of error.

   const Int_t kBUFSIZE = 1024;
   Long64_t    size = fFile->GetSize();
   Long64_t    limit = TMath::Min(size, Long64_t(kMAX_VAR_LEN));
   char        buf[kBUFSIZE+4];

   // Note, this works correctly even if the signature straddles read
   // boundaries since we always read an overlapped area of four
   // bytes on the next read
   for (Long64_t offset = 4; offset < limit; ) {
      offset = TMath::Min(offset + kBUFSIZE, limit);

      Long64_t pos = size - offset;
      Int_t    n = TMath::Min(kBUFSIZE+4, Int_t(offset));

      fFile->Seek(pos);
      if (fFile->ReadBuffer(buf, n)) {
         Error("FindEndHeader", "error reading %d bytes at %lld", n, pos);
         return 0;
      }

      for (Int_t i = n - 4; i > 0; i--)
         if (buf[i]   == 0x50 && buf[i+1] == 0x4b &&
             buf[i+2] == 0x05 && buf[i+3] == 0x06) {
            return pos + i;
         }
   }

   Error("FindEndHeader", "did not find end header in %s", fArchiveName.Data());

   return 0;
}

//______________________________________________________________________________
Int_t TZIPFile::ReadEndHeader(Long64_t pos)
{
   // Read the end header of the ZIP archive including the archive comment
   // at the current file position. Check that it really was a single-disk
   // archive with all the entries as expected. Most importantly, figure
   // out where the central directory begins. Returns -1 in case of error,
   // 0 otherwise.

   char buf[kEND_HEADER_SIZE];

   // read and validate first the end header magic
   fFile->Seek(pos);
   if (fFile->ReadBuffer(buf, kZIP_MAGIC_LEN) ||
       Get(buf, kZIP_MAGIC_LEN) != kEND_HEADER_MAGIC) {
      Error("ReadEndHeader", "wrong end header magic in %s", fArchiveName.Data());
      return -1;
   }

   // read rest of the header
   if (fFile->ReadBuffer(buf + kZIP_MAGIC_LEN,  kEND_HEADER_SIZE - kZIP_MAGIC_LEN)) {
      Error("ReadEndHeader", "error reading %d end header bytes from %s",
            kEND_HEADER_SIZE - kZIP_MAGIC_LEN, fArchiveName.Data());
      return -1;
   }

   UInt_t   disk    = Get(buf + kEND_DISK_OFF,       kEND_DISK_LEN);
   UInt_t   dirdisk = Get(buf + kEND_DIR_DISK_OFF,   kEND_DIR_DISK_LEN);
   UInt_t   dhdrs   = Get(buf + kEND_DISK_HDRS_OFF,  kEND_DISK_HDRS_LEN);
   UInt_t   thdrs   = Get(buf + kEND_TOTAL_HDRS_OFF, kEND_TOTAL_HDRS_LEN);
   Long64_t dirsz   = Get(buf + kEND_DIR_SIZE_OFF,   kEND_DIR_SIZE_LEN);
   Long64_t diroff  = Get(buf + kEND_DIR_OFFSET_OFF, kEND_DIR_OFFSET_LEN);
   Int_t    commlen = Get(buf + kEND_COMMENTLEN_OFF, kEND_COMMENTLEN_LEN);

   if (disk != 0 || dirdisk != 0) {
      Error("ReadHeader", "only single disk archives are supported in %s",
            fArchiveName.Data());
      return -1;
   }
   if (dhdrs != thdrs) {
      Error("ReadEndHeader", "inconsistency in end header data in %s",
            fArchiveName.Data());
      return -1;
   }

   char *comment = new char[commlen+1];
   if (fFile->ReadBuffer(comment, commlen)) {
      Error("ReadEndHeader", "error reading %d end header comment bytes from %s",
            commlen, fArchiveName.Data());
      delete [] comment;
      return -1;
   }
   comment[commlen] = '\0';

   fComment   = comment;
   fDirOffset = fDirPos = diroff;
   fDirSize   = dirsz;

   delete [] comment;
   
   // Try to read Zip64 end of central directory locator
   Long64_t recoff = ReadZip64EndLocator(pos - kZIP64_EDL_HEADER_SIZE);
   if (recoff < 0) {
      if (recoff == -1)
         return -1;
      return 0;
   }
   
   if (ReadZip64EndRecord(recoff) < 0)
      return -1;

   return 0;
}

//______________________________________________________________________________
Long64_t TZIPFile::ReadZip64EndLocator(Long64_t pos)
{
   // Read Zip64 end of central directory locator. Returns -1 in case of error,
   // -2 in case end locator magic is not found (i.e. not a zip64 file) and
   // offset of Zip64 end of central directory record in case of success.
   
   char buf[kZIP64_EDL_HEADER_SIZE];
   
   // read and validate first the end header magic
   fFile->Seek(pos);
   if (fFile->ReadBuffer(buf, kZIP_MAGIC_LEN) ||
       Get(buf, kZIP_MAGIC_LEN) != kZIP64_EDL_HEADER_MAGIC) {
      return -2;
   }
   
   // read rest of the header
   if (fFile->ReadBuffer(buf + kZIP_MAGIC_LEN,  kZIP64_EDL_HEADER_SIZE - kZIP_MAGIC_LEN)) {
      Error("ReadZip64EndLocator", "error reading %d Zip64 end locator header bytes from %s",
            kZIP64_EDL_HEADER_SIZE - kZIP_MAGIC_LEN, fArchiveName.Data());
      return -1;
   }

   UInt_t   dirdisk = Get(  buf + kZIP64_EDL_DISK_OFF,       kZIP64_EDL_DISK_LEN);
   Long64_t recoff  = Get64(buf + kZIP64_EDL_REC_OFFSET_OFF, kZIP64_EDL_REC_OFFSET_LEN);
   UInt_t   totdisk = Get(  buf + kZIP64_EDL_TOTAL_DISK_OFF, kZIP64_EDL_TOTAL_DISK_LEN);

   if (dirdisk != 0 || totdisk != 1) {
      Error("ReadZip64EndLocator", "only single disk archives are supported in %s",
            fArchiveName.Data());
      return -1;
   }

   return recoff;
}

//______________________________________________________________________________
Int_t TZIPFile::ReadZip64EndRecord(Long64_t pos)
{
   // Read Zip64 end of central directory record. Returns -1 in case of error
   // and 0 in case of success.
   
   char buf[kZIP64_EDR_HEADER_SIZE];
   
   // read and validate first the end header magic
   fFile->Seek(pos);
   if (fFile->ReadBuffer(buf, kZIP_MAGIC_LEN) ||
       Get(buf, kZIP_MAGIC_LEN) != kZIP64_EDR_HEADER_MAGIC) {
      Error("ReadZip64EndRecord", "no Zip64 end of directory record\n");
      return -1;
   }
   
   // read rest of the header
   if (fFile->ReadBuffer(buf + kZIP_MAGIC_LEN,  kZIP64_EDR_HEADER_SIZE - kZIP_MAGIC_LEN)) {
      Error("ReadZip64EndRecord", "error reading %d Zip64 end record header bytes from %s",
            kZIP64_EDR_HEADER_SIZE - kZIP_MAGIC_LEN, fArchiveName.Data());
      return -1;
   }
   
   Long64_t dirsz  = Get64(buf + kZIP64_EDR_DIR_SIZE_OFF,   kZIP64_EDR_DIR_SIZE_LEN);
   Long64_t diroff = Get64(buf + kZIP64_EDR_DIR_OFFSET_OFF, kZIP64_EDR_DIR_OFFSET_LEN);
   
   fDirOffset = fDirPos = diroff;
   fDirSize   = dirsz;
   
   return 0;
}

//______________________________________________________________________________
Int_t TZIPFile::ReadDirectory()
{
   // Read the directory of the ZIP archive. Returns -1 in case of error,
   // 0 otherwise.

   char   buf[kDIR_HEADER_SIZE];
   UInt_t n, i;

   // read and validate first the header magic
   fFile->Seek(fDirPos);
   if (fFile->ReadBuffer(buf, kZIP_MAGIC_LEN) ||
       (n = Get(buf, kZIP_MAGIC_LEN)) != kDIR_HEADER_MAGIC) {
      Error("ReadDirectory", "wrong directory header magic in %s",
            fArchiveName.Data());
      return -1;
   }

   // now read the full directory
   for (i = 0; n == kDIR_HEADER_MAGIC; i++) {
      // read the rest of the header
      if (fFile->ReadBuffer(buf + kZIP_MAGIC_LEN, kDIR_HEADER_SIZE - kZIP_MAGIC_LEN)) {
         Error("ReadDirectory", "error reading %d directory bytes from %s",
               kDIR_HEADER_SIZE - kZIP_MAGIC_LEN, fArchiveName.Data());
         return -1;
      }

      UInt_t   version = Get(buf + kDIR_VREQD_OFF,      kDIR_VREQD_LEN);
      UInt_t   flags   = Get(buf + kDIR_FLAG_OFF,       kDIR_FLAG_LEN);
      UInt_t   method  = Get(buf + kDIR_METHOD_OFF,     kDIR_METHOD_LEN);
      UInt_t   time    = Get(buf + kDIR_DATE_OFF,       kDIR_DATE_LEN);
      UInt_t   crc32   = Get(buf + kDIR_CRC32_OFF,      kDIR_CRC32_LEN);
      Long64_t csize   = Get(buf + kDIR_CSIZE_OFF,      kDIR_CSIZE_LEN);
      Long64_t usize   = Get(buf + kDIR_USIZE_OFF,      kDIR_USIZE_LEN);
      Int_t    namelen = Get(buf + kDIR_NAMELEN_OFF,    kDIR_NAMELEN_LEN);
      Int_t    extlen  = Get(buf + kDIR_EXTRALEN_OFF,   kDIR_EXTRALEN_LEN);
      Int_t    commlen = Get(buf + kDIR_COMMENTLEN_OFF, kDIR_COMMENTLEN_LEN);
      UInt_t   disk    = Get(buf + kDIR_DISK_START_OFF, kDIR_DISK_START_LEN);
      UInt_t   iattr   = Get(buf + kDIR_INT_ATTR_OFF,   kDIR_INT_ATTR_LEN);
      UInt_t   xattr   = Get(buf + kDIR_EXT_ATTR_OFF,   kDIR_EXT_ATTR_LEN);
      Long64_t offset  = Get(buf + kDIR_ENTRY_POS_OFF,  kDIR_ENTRY_POS_LEN);

      // check value sanity and the variable-length fields
      if (Get(buf + kDIR_MAGIC_OFF, kZIP_MAGIC_LEN) != kDIR_HEADER_MAGIC ||
          version > kARCHIVE_VERSION ||
          flags & 8 ||
          (method != kSTORED && method != kDEFLATED) ||
          disk != 0 ||
          csize < 0 ||
          usize < 0 ||
          csize > kMaxUInt ||
          usize > kMaxUInt) {
         Error("ReadDirectory", "inconsistency in directory data in %s",
               fArchiveName.Data());
         return -1;
      }

      char *name    = new char[namelen+1];
      char *extra   = new char[extlen];
      char *comment = new char[commlen+1];
      if (fFile->ReadBuffer(name, namelen) ||
          fFile->ReadBuffer(extra, extlen) ||
          fFile->ReadBuffer(comment, commlen)) {
         Error("ReadDirectory", "error reading additional directory data from %s",
               fArchiveName.Data());
         delete [] name;
         delete [] extra;
         delete [] comment;
         return -1;
      }
      name[namelen]    = '\0';
      comment[commlen] = '\0';

      // create a new archive member and store the fields
      TZIPMember *m = new TZIPMember(name);
      fMembers->Add(m);

      m->fMethod = method;
      m->fLevel  = method == kSTORED ? 0
                                     : (flags & 6)/2 == 0 ? 3  // default (:N)
                                     : (flags & 6)/2 == 1 ? 9  // best (:X)
                                     : (flags & 6)/2 == 2 ? 2  // fast (:F)
                                     : (flags & 6)/2 == 3 ? 1  // fastest (:F)
                                     : 3;                      // unreached
      m->fCsize     = csize;
      m->fDsize     = usize;
      m->fCRC32     = crc32;
      m->fModTime.Set(time, kTRUE);   // DOS date/time format
      m->fGlobalLen = extlen;
      m->fGlobal    = extra;
      m->fComment   = comment;
      m->fAttrInt   = iattr;
      m->fAttrExt   = xattr;
      m->fPosition  = offset;
      
      delete [] name;
      delete [] comment;
      // extra is adopted be the TZIPMember

      if (DecodeZip64ExtendedExtraField(m) == -1)
         return -1;

      if (gDebug)
         Info("ReadDirectory", "%lld  %lld  %s  %s",
              m->GetDecompressedSize(), m->GetCompressedSize(),
              m->GetModTime().AsSQLString(), m->GetName());

      // done, read the next magic
      if (fFile->ReadBuffer(buf, kZIP_MAGIC_LEN)) {
         Error("ReadDirectory", "error reading %d directory bytes from %s",
               kZIP_MAGIC_LEN, fArchiveName.Data());
         return -1;
      }
      n = Get(buf, kZIP_MAGIC_LEN);
   }

   // should now see end of archive
   if (n != kEND_HEADER_MAGIC && n != kZIP64_EDR_HEADER_MAGIC) {
      Error("ReadDirectory", "wrong end header magic in %s", fArchiveName.Data());
      return -1;
   }

   return 0;
}

//______________________________________________________________________________
Int_t TZIPFile::ReadMemberHeader(TZIPMember *member)
{
   // Read the member header of the ZIP archive. Sets the position where
   // the data starts in the member object. Returns -1 in case of error,
   // 0 otherwise.

   // read file header to find start of data, since extra len might be
   // different we cannot take it from the directory data
   char buf[kENTRY_HEADER_SIZE];

   // read and validate first the entry header magic
   fFile->Seek(member->fPosition);
   if (fFile->ReadBuffer(buf, kZIP_MAGIC_LEN) ||
       Get(buf, kZIP_MAGIC_LEN) != kENTRY_HEADER_MAGIC) {
      Error("ReadMemberHeader", "wrong entry header magic in %s",
            fArchiveName.Data());
      return -1;
   }

   // read rest of the header
   if (fFile->ReadBuffer(buf + kZIP_MAGIC_LEN,  kENTRY_HEADER_SIZE - kZIP_MAGIC_LEN)) {
      Error("ReadMemberHeader", "error reading %d member header bytes from %s",
            kENTRY_HEADER_SIZE - kZIP_MAGIC_LEN, fArchiveName.Data());
      return -1;
   }
   Int_t namelen = Get(buf + kENTRY_NAMELEN_OFF,  kENTRY_NAMELEN_LEN);
   Int_t extlen  = Get(buf + kENTRY_EXTRALEN_OFF, kENTRY_EXTRALEN_LEN);

   member->fFilePosition = member->fPosition + kENTRY_HEADER_SIZE +
                           namelen + extlen;

   return 0;
}

//______________________________________________________________________________
Int_t TZIPFile::DecodeZip64ExtendedExtraField(TZIPMember *m, Bool_t global)
{
   // Decode the Zip64 extended extra field. If global is true, decode the
   // extra field coming from the central directory, if false decode the
   // extra field coming from the local file header. Returns -1 in case of
   // error, -2 in case Zip64 extra block was not found and 0 in case of
   // success.
   
   char  *buf;
   Int_t  len;
   Int_t  ret = -2;
   
   if (global) {
      buf = (char *) m->fGlobal;
      len = m->fGlobalLen;
   } else {
      buf = (char *) m->fLocal;
      len = m->fLocalLen;
   }
   
   if (!buf || !len) {
      return ret;
   }
   
   Int_t off = 0;
   while (len > 0) {
      UInt_t   tag  = Get(buf + off + kZIP64_EXTENDED_MAGIC_OFF, kZIP64_EXTENDED_MAGIC_LEN);
      UInt_t   size = Get(buf + off + kZIP64_EXTENDED_SIZE_OFF,  kZIP64_EXTENDED_SIZE_LEN);
      if (tag == kZIP64_EXTENDED_MAGIC) {
         Long64_t usize = Get64(buf + off + kZIP64_EXTENDED_USIZE_OFF,      kZIP64_EXTENDED_USIZE_LEN);
         Long64_t csize = Get64(buf + off + kZIP64_EXTENTED_CSIZE_OFF,      kZIP64_EXTENDED_CSIZE_LEN);
         m->fDsize = usize;
         m->fCsize = csize;
         if (size >= 24) {
            Long64_t offset = Get64(buf + off + kZIP64_EXTENDED_HDR_OFFSET_OFF, kZIP64_EXTENDED_HDR_OFFSET_LEN);
            m->fPosition = offset;
         }

         ret = 0;
      }
      len -= (Int_t)size + kZIP64_EXTENDED_MAGIC_LEN + kZIP64_EXTENDED_MAGIC_LEN;
      off += (Int_t)size + kZIP64_EXTENDED_MAGIC_LEN + kZIP64_EXTENDED_MAGIC_LEN;
   }
   
   return ret;
}

//______________________________________________________________________________
Int_t TZIPFile::SetCurrentMember()
{
   // Find the desired member in the member array and make it the
   // current member. Returns -1 in case member is not found, 0 otherwise.

   fCurMember = 0;

   if (fMemberIndex > -1) {
      fCurMember = (TZIPMember *) fMembers->At(fMemberIndex);
      if (!fCurMember)
         return -1;
      fMemberName = fCurMember->GetName();
   } else {
      for (int i = 0; i < fMembers->GetEntriesFast(); i++) {
         TZIPMember *m = (TZIPMember *) fMembers->At(i);
         if (fMemberName == m->fName) {
            fCurMember   = m;
            fMemberIndex = i;
            break;
         }
      }
      if (!fCurMember)
         return -1;
   }

   return ReadMemberHeader((TZIPMember *)fCurMember);
}

//______________________________________________________________________________
UInt_t TZIPFile::Get(const void *buffer, Int_t bytes)
{
   // Read a "bytes" long little-endian integer value from "buffer".

   UInt_t value = 0;

   if (bytes > 4) {
      Error("Get", "can not read > 4 byte integers, use Get64");
      return value;
   }
#ifdef R__BYTESWAP
   memcpy(&value, buffer, bytes);
#else
   const UChar_t *buf = static_cast<const unsigned char *>(buffer);
   for (UInt_t shift = 0; bytes; shift += 8, --bytes, ++buf)
      value += *buf << shift;
#endif
   return value;
}

//______________________________________________________________________________
ULong64_t TZIPFile::Get64(const void *buffer, Int_t bytes)
{
   // Read a 8 byte long little-endian integer value from "buffer".
   
   ULong64_t value = 0;
   
   if (bytes != 8) {
      Error("Get64", "bytes must be 8 (asked for %d)", bytes);
      return value;
   }
   
#ifdef R__BYTESWAP
   memcpy(&value, buffer, bytes);
#else
   const UChar_t *buf = static_cast<const unsigned char *>(buffer);
   for (UInt_t shift = 0; bytes; shift += 8, --bytes, ++buf)
      value += *buf << shift;
#endif
   return value;
}

//______________________________________________________________________________
void TZIPFile::Print(Option_t *) const
{
   // Pretty print ZIP archive members.

   if (fMembers)
      fMembers->Print();
}


ClassImp(TZIPMember)

//______________________________________________________________________________
TZIPMember::TZIPMember()
{
   // Default ctor.

   fLocal     = 0;
   fLocalLen  = 0;
   fGlobal    = 0;
   fGlobalLen = 0;
   fCRC32     = 0;
   fAttrInt   = 0;
   fAttrExt   = 0;
   fMethod    = 0;
   fLevel     = 0;
}

//______________________________________________________________________________
TZIPMember::TZIPMember(const char *name)
   : TArchiveMember(name)
{
   // Create ZIP member file.

   fLocal     = 0;
   fLocalLen  = 0;
   fGlobal    = 0;
   fGlobalLen = 0;
   fCRC32     = 0;
   fAttrInt   = 0;
   fAttrExt   = 0;
   fMethod    = 0;
   fLevel     = 0;
}

//______________________________________________________________________________
TZIPMember::TZIPMember(const TZIPMember &member)
   : TArchiveMember(member)
{
   // Copy ctor.

   fLocal     = 0;
   fLocalLen  = member.fLocalLen;
   fGlobal    = 0;
   fGlobalLen = member.fGlobalLen;
   fCRC32     = member.fCRC32;
   fAttrInt   = member.fAttrInt;
   fAttrExt   = member.fAttrExt;
   fMethod    = member.fMethod;
   fLevel     = member.fLevel;

   if (member.fLocal) {
      fLocal = new char [fLocalLen];
      memcpy(fLocal, member.fLocal, fLocalLen);
   }
   if (member.fGlobal) {
      fGlobal = new char [fGlobalLen];
      memcpy(fGlobal, member.fGlobal, fGlobalLen);
   }
}

//______________________________________________________________________________
TZIPMember &TZIPMember::operator=(const TZIPMember &rhs)
{
   // Assignment operator.

   if (this != &rhs) {
      TArchiveMember::operator=(rhs);

      delete [] (char*) fLocal;
      delete [] (char*) fGlobal;

      fLocal     = 0;
      fLocalLen  = rhs.fLocalLen;
      fGlobal    = 0;
      fGlobalLen = rhs.fGlobalLen;
      fCRC32     = rhs.fCRC32;
      fAttrInt   = rhs.fAttrInt;
      fAttrExt   = rhs.fAttrExt;
      fMethod    = rhs.fMethod;
      fLevel     = rhs.fLevel;

      if (rhs.fLocal) {
         fLocal = new char [fLocalLen];
         memcpy(fLocal, rhs.fLocal, fLocalLen);
      }
      if (rhs.fGlobal) {
         fGlobal = new char [fGlobalLen];
         memcpy(fGlobal, rhs.fGlobal, fGlobalLen);
      }
   }
   return *this;
}

//______________________________________________________________________________
TZIPMember::~TZIPMember()
{
   // Cleanup.

   delete [] (char*) fLocal;
   delete [] (char*) fGlobal;
}

//______________________________________________________________________________
void TZIPMember::Print(Option_t *) const
{
   // Pretty print basic ZIP member info.

   printf("%-20lld", fDsize);
   printf(" %s   %s\n", fModTime.AsSQLString(), fName.Data());
}
 TZIPFile.cxx:1
 TZIPFile.cxx:2
 TZIPFile.cxx:3
 TZIPFile.cxx:4
 TZIPFile.cxx:5
 TZIPFile.cxx:6
 TZIPFile.cxx:7
 TZIPFile.cxx:8
 TZIPFile.cxx:9
 TZIPFile.cxx:10
 TZIPFile.cxx:11
 TZIPFile.cxx:12
 TZIPFile.cxx:13
 TZIPFile.cxx:14
 TZIPFile.cxx:15
 TZIPFile.cxx:16
 TZIPFile.cxx:17
 TZIPFile.cxx:18
 TZIPFile.cxx:19
 TZIPFile.cxx:20
 TZIPFile.cxx:21
 TZIPFile.cxx:22
 TZIPFile.cxx:23
 TZIPFile.cxx:24
 TZIPFile.cxx:25
 TZIPFile.cxx:26
 TZIPFile.cxx:27
 TZIPFile.cxx:28
 TZIPFile.cxx:29
 TZIPFile.cxx:30
 TZIPFile.cxx:31
 TZIPFile.cxx:32
 TZIPFile.cxx:33
 TZIPFile.cxx:34
 TZIPFile.cxx:35
 TZIPFile.cxx:36
 TZIPFile.cxx:37
 TZIPFile.cxx:38
 TZIPFile.cxx:39
 TZIPFile.cxx:40
 TZIPFile.cxx:41
 TZIPFile.cxx:42
 TZIPFile.cxx:43
 TZIPFile.cxx:44
 TZIPFile.cxx:45
 TZIPFile.cxx:46
 TZIPFile.cxx:47
 TZIPFile.cxx:48
 TZIPFile.cxx:49
 TZIPFile.cxx:50
 TZIPFile.cxx:51
 TZIPFile.cxx:52
 TZIPFile.cxx:53
 TZIPFile.cxx:54
 TZIPFile.cxx:55
 TZIPFile.cxx:56
 TZIPFile.cxx:57
 TZIPFile.cxx:58
 TZIPFile.cxx:59
 TZIPFile.cxx:60
 TZIPFile.cxx:61
 TZIPFile.cxx:62
 TZIPFile.cxx:63
 TZIPFile.cxx:64
 TZIPFile.cxx:65
 TZIPFile.cxx:66
 TZIPFile.cxx:67
 TZIPFile.cxx:68
 TZIPFile.cxx:69
 TZIPFile.cxx:70
 TZIPFile.cxx:71
 TZIPFile.cxx:72
 TZIPFile.cxx:73
 TZIPFile.cxx:74
 TZIPFile.cxx:75
 TZIPFile.cxx:76
 TZIPFile.cxx:77
 TZIPFile.cxx:78
 TZIPFile.cxx:79
 TZIPFile.cxx:80
 TZIPFile.cxx:81
 TZIPFile.cxx:82
 TZIPFile.cxx:83
 TZIPFile.cxx:84
 TZIPFile.cxx:85
 TZIPFile.cxx:86
 TZIPFile.cxx:87
 TZIPFile.cxx:88
 TZIPFile.cxx:89
 TZIPFile.cxx:90
 TZIPFile.cxx:91
 TZIPFile.cxx:92
 TZIPFile.cxx:93
 TZIPFile.cxx:94
 TZIPFile.cxx:95
 TZIPFile.cxx:96
 TZIPFile.cxx:97
 TZIPFile.cxx:98
 TZIPFile.cxx:99
 TZIPFile.cxx:100
 TZIPFile.cxx:101
 TZIPFile.cxx:102
 TZIPFile.cxx:103
 TZIPFile.cxx:104
 TZIPFile.cxx:105
 TZIPFile.cxx:106
 TZIPFile.cxx:107
 TZIPFile.cxx:108
 TZIPFile.cxx:109
 TZIPFile.cxx:110
 TZIPFile.cxx:111
 TZIPFile.cxx:112
 TZIPFile.cxx:113
 TZIPFile.cxx:114
 TZIPFile.cxx:115
 TZIPFile.cxx:116
 TZIPFile.cxx:117
 TZIPFile.cxx:118
 TZIPFile.cxx:119
 TZIPFile.cxx:120
 TZIPFile.cxx:121
 TZIPFile.cxx:122
 TZIPFile.cxx:123
 TZIPFile.cxx:124
 TZIPFile.cxx:125
 TZIPFile.cxx:126
 TZIPFile.cxx:127
 TZIPFile.cxx:128
 TZIPFile.cxx:129
 TZIPFile.cxx:130
 TZIPFile.cxx:131
 TZIPFile.cxx:132
 TZIPFile.cxx:133
 TZIPFile.cxx:134
 TZIPFile.cxx:135
 TZIPFile.cxx:136
 TZIPFile.cxx:137
 TZIPFile.cxx:138
 TZIPFile.cxx:139
 TZIPFile.cxx:140
 TZIPFile.cxx:141
 TZIPFile.cxx:142
 TZIPFile.cxx:143
 TZIPFile.cxx:144
 TZIPFile.cxx:145
 TZIPFile.cxx:146
 TZIPFile.cxx:147
 TZIPFile.cxx:148
 TZIPFile.cxx:149
 TZIPFile.cxx:150
 TZIPFile.cxx:151
 TZIPFile.cxx:152
 TZIPFile.cxx:153
 TZIPFile.cxx:154
 TZIPFile.cxx:155
 TZIPFile.cxx:156
 TZIPFile.cxx:157
 TZIPFile.cxx:158
 TZIPFile.cxx:159
 TZIPFile.cxx:160
 TZIPFile.cxx:161
 TZIPFile.cxx:162
 TZIPFile.cxx:163
 TZIPFile.cxx:164
 TZIPFile.cxx:165
 TZIPFile.cxx:166
 TZIPFile.cxx:167
 TZIPFile.cxx:168
 TZIPFile.cxx:169
 TZIPFile.cxx:170
 TZIPFile.cxx:171
 TZIPFile.cxx:172
 TZIPFile.cxx:173
 TZIPFile.cxx:174
 TZIPFile.cxx:175
 TZIPFile.cxx:176
 TZIPFile.cxx:177
 TZIPFile.cxx:178
 TZIPFile.cxx:179
 TZIPFile.cxx:180
 TZIPFile.cxx:181
 TZIPFile.cxx:182
 TZIPFile.cxx:183
 TZIPFile.cxx:184
 TZIPFile.cxx:185
 TZIPFile.cxx:186
 TZIPFile.cxx:187
 TZIPFile.cxx:188
 TZIPFile.cxx:189
 TZIPFile.cxx:190
 TZIPFile.cxx:191
 TZIPFile.cxx:192
 TZIPFile.cxx:193
 TZIPFile.cxx:194
 TZIPFile.cxx:195
 TZIPFile.cxx:196
 TZIPFile.cxx:197
 TZIPFile.cxx:198
 TZIPFile.cxx:199
 TZIPFile.cxx:200
 TZIPFile.cxx:201
 TZIPFile.cxx:202
 TZIPFile.cxx:203
 TZIPFile.cxx:204
 TZIPFile.cxx:205
 TZIPFile.cxx:206
 TZIPFile.cxx:207
 TZIPFile.cxx:208
 TZIPFile.cxx:209
 TZIPFile.cxx:210
 TZIPFile.cxx:211
 TZIPFile.cxx:212
 TZIPFile.cxx:213
 TZIPFile.cxx:214
 TZIPFile.cxx:215
 TZIPFile.cxx:216
 TZIPFile.cxx:217
 TZIPFile.cxx:218
 TZIPFile.cxx:219
 TZIPFile.cxx:220
 TZIPFile.cxx:221
 TZIPFile.cxx:222
 TZIPFile.cxx:223
 TZIPFile.cxx:224
 TZIPFile.cxx:225
 TZIPFile.cxx:226
 TZIPFile.cxx:227
 TZIPFile.cxx:228
 TZIPFile.cxx:229
 TZIPFile.cxx:230
 TZIPFile.cxx:231
 TZIPFile.cxx:232
 TZIPFile.cxx:233
 TZIPFile.cxx:234
 TZIPFile.cxx:235
 TZIPFile.cxx:236
 TZIPFile.cxx:237
 TZIPFile.cxx:238
 TZIPFile.cxx:239
 TZIPFile.cxx:240
 TZIPFile.cxx:241
 TZIPFile.cxx:242
 TZIPFile.cxx:243
 TZIPFile.cxx:244
 TZIPFile.cxx:245
 TZIPFile.cxx:246
 TZIPFile.cxx:247
 TZIPFile.cxx:248
 TZIPFile.cxx:249
 TZIPFile.cxx:250
 TZIPFile.cxx:251
 TZIPFile.cxx:252
 TZIPFile.cxx:253
 TZIPFile.cxx:254
 TZIPFile.cxx:255
 TZIPFile.cxx:256
 TZIPFile.cxx:257
 TZIPFile.cxx:258
 TZIPFile.cxx:259
 TZIPFile.cxx:260
 TZIPFile.cxx:261
 TZIPFile.cxx:262
 TZIPFile.cxx:263
 TZIPFile.cxx:264
 TZIPFile.cxx:265
 TZIPFile.cxx:266
 TZIPFile.cxx:267
 TZIPFile.cxx:268
 TZIPFile.cxx:269
 TZIPFile.cxx:270
 TZIPFile.cxx:271
 TZIPFile.cxx:272
 TZIPFile.cxx:273
 TZIPFile.cxx:274
 TZIPFile.cxx:275
 TZIPFile.cxx:276
 TZIPFile.cxx:277
 TZIPFile.cxx:278
 TZIPFile.cxx:279
 TZIPFile.cxx:280
 TZIPFile.cxx:281
 TZIPFile.cxx:282
 TZIPFile.cxx:283
 TZIPFile.cxx:284
 TZIPFile.cxx:285
 TZIPFile.cxx:286
 TZIPFile.cxx:287
 TZIPFile.cxx:288
 TZIPFile.cxx:289
 TZIPFile.cxx:290
 TZIPFile.cxx:291
 TZIPFile.cxx:292
 TZIPFile.cxx:293
 TZIPFile.cxx:294
 TZIPFile.cxx:295
 TZIPFile.cxx:296
 TZIPFile.cxx:297
 TZIPFile.cxx:298
 TZIPFile.cxx:299
 TZIPFile.cxx:300
 TZIPFile.cxx:301
 TZIPFile.cxx:302
 TZIPFile.cxx:303
 TZIPFile.cxx:304
 TZIPFile.cxx:305
 TZIPFile.cxx:306
 TZIPFile.cxx:307
 TZIPFile.cxx:308
 TZIPFile.cxx:309
 TZIPFile.cxx:310
 TZIPFile.cxx:311
 TZIPFile.cxx:312
 TZIPFile.cxx:313
 TZIPFile.cxx:314
 TZIPFile.cxx:315
 TZIPFile.cxx:316
 TZIPFile.cxx:317
 TZIPFile.cxx:318
 TZIPFile.cxx:319
 TZIPFile.cxx:320
 TZIPFile.cxx:321
 TZIPFile.cxx:322
 TZIPFile.cxx:323
 TZIPFile.cxx:324
 TZIPFile.cxx:325
 TZIPFile.cxx:326
 TZIPFile.cxx:327
 TZIPFile.cxx:328
 TZIPFile.cxx:329
 TZIPFile.cxx:330
 TZIPFile.cxx:331
 TZIPFile.cxx:332
 TZIPFile.cxx:333
 TZIPFile.cxx:334
 TZIPFile.cxx:335
 TZIPFile.cxx:336
 TZIPFile.cxx:337
 TZIPFile.cxx:338
 TZIPFile.cxx:339
 TZIPFile.cxx:340
 TZIPFile.cxx:341
 TZIPFile.cxx:342
 TZIPFile.cxx:343
 TZIPFile.cxx:344
 TZIPFile.cxx:345
 TZIPFile.cxx:346
 TZIPFile.cxx:347
 TZIPFile.cxx:348
 TZIPFile.cxx:349
 TZIPFile.cxx:350
 TZIPFile.cxx:351
 TZIPFile.cxx:352
 TZIPFile.cxx:353
 TZIPFile.cxx:354
 TZIPFile.cxx:355
 TZIPFile.cxx:356
 TZIPFile.cxx:357
 TZIPFile.cxx:358
 TZIPFile.cxx:359
 TZIPFile.cxx:360
 TZIPFile.cxx:361
 TZIPFile.cxx:362
 TZIPFile.cxx:363
 TZIPFile.cxx:364
 TZIPFile.cxx:365
 TZIPFile.cxx:366
 TZIPFile.cxx:367
 TZIPFile.cxx:368
 TZIPFile.cxx:369
 TZIPFile.cxx:370
 TZIPFile.cxx:371
 TZIPFile.cxx:372
 TZIPFile.cxx:373
 TZIPFile.cxx:374
 TZIPFile.cxx:375
 TZIPFile.cxx:376
 TZIPFile.cxx:377
 TZIPFile.cxx:378
 TZIPFile.cxx:379
 TZIPFile.cxx:380
 TZIPFile.cxx:381
 TZIPFile.cxx:382
 TZIPFile.cxx:383
 TZIPFile.cxx:384
 TZIPFile.cxx:385
 TZIPFile.cxx:386
 TZIPFile.cxx:387
 TZIPFile.cxx:388
 TZIPFile.cxx:389
 TZIPFile.cxx:390
 TZIPFile.cxx:391
 TZIPFile.cxx:392
 TZIPFile.cxx:393
 TZIPFile.cxx:394
 TZIPFile.cxx:395
 TZIPFile.cxx:396
 TZIPFile.cxx:397
 TZIPFile.cxx:398
 TZIPFile.cxx:399
 TZIPFile.cxx:400
 TZIPFile.cxx:401
 TZIPFile.cxx:402
 TZIPFile.cxx:403
 TZIPFile.cxx:404
 TZIPFile.cxx:405
 TZIPFile.cxx:406
 TZIPFile.cxx:407
 TZIPFile.cxx:408
 TZIPFile.cxx:409
 TZIPFile.cxx:410
 TZIPFile.cxx:411
 TZIPFile.cxx:412
 TZIPFile.cxx:413
 TZIPFile.cxx:414
 TZIPFile.cxx:415
 TZIPFile.cxx:416
 TZIPFile.cxx:417
 TZIPFile.cxx:418
 TZIPFile.cxx:419
 TZIPFile.cxx:420
 TZIPFile.cxx:421
 TZIPFile.cxx:422
 TZIPFile.cxx:423
 TZIPFile.cxx:424
 TZIPFile.cxx:425
 TZIPFile.cxx:426
 TZIPFile.cxx:427
 TZIPFile.cxx:428
 TZIPFile.cxx:429
 TZIPFile.cxx:430
 TZIPFile.cxx:431
 TZIPFile.cxx:432
 TZIPFile.cxx:433
 TZIPFile.cxx:434
 TZIPFile.cxx:435
 TZIPFile.cxx:436
 TZIPFile.cxx:437
 TZIPFile.cxx:438
 TZIPFile.cxx:439
 TZIPFile.cxx:440
 TZIPFile.cxx:441
 TZIPFile.cxx:442
 TZIPFile.cxx:443
 TZIPFile.cxx:444
 TZIPFile.cxx:445
 TZIPFile.cxx:446
 TZIPFile.cxx:447
 TZIPFile.cxx:448
 TZIPFile.cxx:449
 TZIPFile.cxx:450
 TZIPFile.cxx:451
 TZIPFile.cxx:452
 TZIPFile.cxx:453
 TZIPFile.cxx:454
 TZIPFile.cxx:455
 TZIPFile.cxx:456
 TZIPFile.cxx:457
 TZIPFile.cxx:458
 TZIPFile.cxx:459
 TZIPFile.cxx:460
 TZIPFile.cxx:461
 TZIPFile.cxx:462
 TZIPFile.cxx:463
 TZIPFile.cxx:464
 TZIPFile.cxx:465
 TZIPFile.cxx:466
 TZIPFile.cxx:467
 TZIPFile.cxx:468
 TZIPFile.cxx:469
 TZIPFile.cxx:470
 TZIPFile.cxx:471
 TZIPFile.cxx:472
 TZIPFile.cxx:473
 TZIPFile.cxx:474
 TZIPFile.cxx:475
 TZIPFile.cxx:476
 TZIPFile.cxx:477
 TZIPFile.cxx:478
 TZIPFile.cxx:479
 TZIPFile.cxx:480
 TZIPFile.cxx:481
 TZIPFile.cxx:482
 TZIPFile.cxx:483
 TZIPFile.cxx:484
 TZIPFile.cxx:485
 TZIPFile.cxx:486
 TZIPFile.cxx:487
 TZIPFile.cxx:488
 TZIPFile.cxx:489
 TZIPFile.cxx:490
 TZIPFile.cxx:491
 TZIPFile.cxx:492
 TZIPFile.cxx:493
 TZIPFile.cxx:494
 TZIPFile.cxx:495
 TZIPFile.cxx:496
 TZIPFile.cxx:497
 TZIPFile.cxx:498
 TZIPFile.cxx:499
 TZIPFile.cxx:500
 TZIPFile.cxx:501
 TZIPFile.cxx:502
 TZIPFile.cxx:503
 TZIPFile.cxx:504
 TZIPFile.cxx:505
 TZIPFile.cxx:506
 TZIPFile.cxx:507
 TZIPFile.cxx:508
 TZIPFile.cxx:509
 TZIPFile.cxx:510
 TZIPFile.cxx:511
 TZIPFile.cxx:512
 TZIPFile.cxx:513
 TZIPFile.cxx:514
 TZIPFile.cxx:515
 TZIPFile.cxx:516
 TZIPFile.cxx:517
 TZIPFile.cxx:518
 TZIPFile.cxx:519
 TZIPFile.cxx:520
 TZIPFile.cxx:521
 TZIPFile.cxx:522
 TZIPFile.cxx:523
 TZIPFile.cxx:524
 TZIPFile.cxx:525
 TZIPFile.cxx:526
 TZIPFile.cxx:527
 TZIPFile.cxx:528
 TZIPFile.cxx:529
 TZIPFile.cxx:530
 TZIPFile.cxx:531
 TZIPFile.cxx:532
 TZIPFile.cxx:533
 TZIPFile.cxx:534
 TZIPFile.cxx:535
 TZIPFile.cxx:536
 TZIPFile.cxx:537
 TZIPFile.cxx:538
 TZIPFile.cxx:539
 TZIPFile.cxx:540
 TZIPFile.cxx:541
 TZIPFile.cxx:542
 TZIPFile.cxx:543
 TZIPFile.cxx:544
 TZIPFile.cxx:545
 TZIPFile.cxx:546
 TZIPFile.cxx:547
 TZIPFile.cxx:548
 TZIPFile.cxx:549
 TZIPFile.cxx:550
 TZIPFile.cxx:551
 TZIPFile.cxx:552
 TZIPFile.cxx:553
 TZIPFile.cxx:554
 TZIPFile.cxx:555
 TZIPFile.cxx:556
 TZIPFile.cxx:557
 TZIPFile.cxx:558
 TZIPFile.cxx:559
 TZIPFile.cxx:560
 TZIPFile.cxx:561
 TZIPFile.cxx:562
 TZIPFile.cxx:563
 TZIPFile.cxx:564
 TZIPFile.cxx:565
 TZIPFile.cxx:566
 TZIPFile.cxx:567
 TZIPFile.cxx:568
 TZIPFile.cxx:569
 TZIPFile.cxx:570
 TZIPFile.cxx:571
 TZIPFile.cxx:572
 TZIPFile.cxx:573
 TZIPFile.cxx:574
 TZIPFile.cxx:575
 TZIPFile.cxx:576
 TZIPFile.cxx:577
 TZIPFile.cxx:578
 TZIPFile.cxx:579
 TZIPFile.cxx:580
 TZIPFile.cxx:581
 TZIPFile.cxx:582
 TZIPFile.cxx:583
 TZIPFile.cxx:584
 TZIPFile.cxx:585
 TZIPFile.cxx:586
 TZIPFile.cxx:587
 TZIPFile.cxx:588
 TZIPFile.cxx:589
 TZIPFile.cxx:590
 TZIPFile.cxx:591
 TZIPFile.cxx:592
 TZIPFile.cxx:593
 TZIPFile.cxx:594
 TZIPFile.cxx:595
 TZIPFile.cxx:596
 TZIPFile.cxx:597
 TZIPFile.cxx:598
 TZIPFile.cxx:599
 TZIPFile.cxx:600
 TZIPFile.cxx:601
 TZIPFile.cxx:602
 TZIPFile.cxx:603
 TZIPFile.cxx:604
 TZIPFile.cxx:605
 TZIPFile.cxx:606
 TZIPFile.cxx:607
 TZIPFile.cxx:608
 TZIPFile.cxx:609
 TZIPFile.cxx:610
 TZIPFile.cxx:611
 TZIPFile.cxx:612
 TZIPFile.cxx:613
 TZIPFile.cxx:614
 TZIPFile.cxx:615
 TZIPFile.cxx:616
 TZIPFile.cxx:617
 TZIPFile.cxx:618
 TZIPFile.cxx:619
 TZIPFile.cxx:620
 TZIPFile.cxx:621
 TZIPFile.cxx:622
 TZIPFile.cxx:623
 TZIPFile.cxx:624
 TZIPFile.cxx:625
 TZIPFile.cxx:626
 TZIPFile.cxx:627
 TZIPFile.cxx:628
 TZIPFile.cxx:629
 TZIPFile.cxx:630
 TZIPFile.cxx:631
 TZIPFile.cxx:632
 TZIPFile.cxx:633
 TZIPFile.cxx:634
 TZIPFile.cxx:635
 TZIPFile.cxx:636
 TZIPFile.cxx:637
 TZIPFile.cxx:638
 TZIPFile.cxx:639
 TZIPFile.cxx:640
 TZIPFile.cxx:641
 TZIPFile.cxx:642
 TZIPFile.cxx:643
 TZIPFile.cxx:644
 TZIPFile.cxx:645
 TZIPFile.cxx:646
 TZIPFile.cxx:647
 TZIPFile.cxx:648
 TZIPFile.cxx:649
 TZIPFile.cxx:650
 TZIPFile.cxx:651
 TZIPFile.cxx:652
 TZIPFile.cxx:653
 TZIPFile.cxx:654
 TZIPFile.cxx:655
 TZIPFile.cxx:656
 TZIPFile.cxx:657
 TZIPFile.cxx:658
 TZIPFile.cxx:659
 TZIPFile.cxx:660
 TZIPFile.cxx:661
 TZIPFile.cxx:662
 TZIPFile.cxx:663
 TZIPFile.cxx:664
 TZIPFile.cxx:665
 TZIPFile.cxx:666
 TZIPFile.cxx:667
 TZIPFile.cxx:668
 TZIPFile.cxx:669
 TZIPFile.cxx:670
 TZIPFile.cxx:671
 TZIPFile.cxx:672
 TZIPFile.cxx:673
 TZIPFile.cxx:674
 TZIPFile.cxx:675
 TZIPFile.cxx:676
 TZIPFile.cxx:677
 TZIPFile.cxx:678
 TZIPFile.cxx:679
 TZIPFile.cxx:680