// @(#)root/base:$Name:  $:$Id: TFile.cxx,v 1.126 2004/07/07 23:25:33 rdm Exp $
// Author: Rene Brun   28/11/94

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

#include "RConfig.h"

#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>
#ifndef WIN32
#   include <unistd.h>
#else
#   define ssize_t int
#   include <io.h>
#   include <sys/types.h>
#endif

#include "Bytes.h"
#include "Riostream.h"
#include "Strlen.h"
#include "TArrayC.h"
#include "TClassTable.h"
#include "TDatime.h"
#include "TError.h"
#include "TFile.h"
#include "TFree.h"
#include "TInterpreter.h"
#include "TKey.h"
#include "TNetFile.h"
#include "TPluginManager.h"
#include "TProcessUUID.h"
#include "TRegexp.h"
#include "TROOT.h"
#include "TStreamerInfo.h"
#include "TSystem.h"
#include "TTimeStamp.h"
#include "TVirtualPerfStats.h"
#include "TWebFile.h"
#include "TArchiveFile.h"


TFile *gFile;                 //Pointer to current file


Double_t TFile::fgBytesRead  = 0;
Double_t TFile::fgBytesWrite = 0;

const Int_t kBEGIN = 100;

ClassImp(TFile)

//*-*x17 macros/layout_file

//______________________________________________________________________________
TFile::TFile() : TDirectory()
{
   // File default Constructor.

   fD             = -1;
   fFree          = 0;
   fWritten       = 0;
   fSumBuffer     = 0;
   fSum2Buffer    = 0;
   fClassIndex    = 0;
   fCache         = 0;
   fProcessIDs    = 0;
   fNProcessIDs   = 0;
   fArchiveOffset = 0;
   fArchive       = 0;
   fIsArchive     = kFALSE;

   if (gDebug)
      Info("TFile", "default ctor");
}

//1_____________________________________________________________________________
TFile::TFile(const char *fname1, Option_t *option, const char *ftitle, Int_t compress)
           : TDirectory()
{
   // Opens or creates a local ROOT file whose name is fname1. It is
   // recommended to specify fname1 as "<file>.root". The suffix ".root"
   // will be used by object browsers to automatically identify the file as
   // a ROOT file. If the constructor fails in any way IsZombie() will
   // return true. Use IsOpen() to check if the file is (still) open.
   //
   // To open non-local files use the static TFile::Open() method, that
   // will take care of opening the files using the correct remote file
   // access plugin.
   //
   // If option = NEW or CREATE   create a new file and open it for writing,
   //                             if the file already exists the file is
   //                             not opened.
   //           = RECREATE        create a new file, if the file already
   //                             exists it will be overwritten.
   //           = UPDATE          open an existing file for writing.
   //                             if no file exists, it is created.
   //           = READ            open an existing file for reading.
   //
   // The title of the file (ftitle) will be shown by the ROOT browsers.
   //
   // A ROOT file (like a Unix file system) may contain objects and
   // directories. There are no restrictions for the number of levels
   // of directories.
   //
   // A ROOT file is designed such that one can write in the file in pure
   // sequential mode (case of BATCH jobs). In this case, the file may be
   // read sequentially again without using the file index written
   // at the end of the file. In case of a job crash, all the information
   // on the file is therefore protected.
   //
   // A ROOT file can be used interactively. In this case, one has the
   // possibility to delete existing objects and add new ones.
   // When an object is deleted from the file, the freed space is added
   // into the FREE linked list (fFree). The FREE list consists of a chain
   // of consecutive free segments on the file. At the same time, the first
   // 4 bytes of the freed record on the file are overwritten by GAPSIZE
   // where GAPSIZE = -(Number of bytes occupied by the record).
   //
   // Option compress is used to specify the compression level:
   //  compress = 0 objects written to this file will not be compressed.
   //  compress = 1 minimal compression level but fast.
   //  ....
   //  compress = 9 maximal compression level but slow.
   //
   // Note that the compression level may be changed at any time.
   // The new compression level will only apply to newly written objects.
   // The function TFile::Map() shows the compression factor
   // for each object written to this file.
   // The function TFile::GetCompressionFactor returns the global
   // compression factor for this file.
   //
   // In case the file does not exist or is not a valid ROOT file,
   // it is made a Zombie. One can detect this situation with a code like:
   //    TFile f("file.root");
   //    if (f.IsZombie()) {
   //       cout << "Error opening file" << endl;
   //       exit(-1);
   //    }
   //
   // A ROOT file is a suite of consecutive data records (TKey's) with
   // the following format (see also the TKey class). If the key is
   // located past the 32 bit file limit (> 2 GB) then some fields will
   // be 8 instead of 4 bytes:
   //    1->4            Nbytes    = Length of compressed object (in bytes)
   //    5->6            Version   = TKey version identifier
   //    7->10           ObjLen    = Length of uncompressed object
   //    11->14          Datime    = Date and time when object was written to file
   //    15->16          KeyLen    = Length of the key structure (in bytes)
   //    17->18          Cycle     = Cycle of key
   //    19->22 [19->26] SeekKey   = Pointer to record itself (consistency check)
   //    23->26 [27->34] SeekPdir  = Pointer to directory header
   //    27->27 [35->35] lname     = Number of bytes in the class name
   //    28->.. [36->..] ClassName = Object Class Name
   //    ..->..          lname     = Number of bytes in the object name
   //    ..->..          Name      = lName bytes with the name of the object
   //    ..->..          lTitle    = Number of bytes in the object title
   //    ..->..          Title     = Title of the object
   //    ----->          DATA      = Data bytes associated to the object
   //
   // The first data record starts at byte fBEGIN (currently set to kBEGIN).
   // Bytes 1->kBEGIN contain the file description, when fVersion >= 1000000
   // it is a large file (> 2 GB) and the offsets will be 8 bytes long and
   // fUnits will be set to 8:
   //    1->4            "root"      = Root file identifier
   //    5->8            fVersion    = File format version
   //    9->12           fBEGIN      = Pointer to first data record
   //    13->16 [13->20] fEND        = Pointer to first free word at the EOF
   //    17->20 [21->28] fSeekFree   = Pointer to FREE data record
   //    21->24 [29->32] fNbytesFree = Number of bytes in FREE data record
   //    25->28 [33->36] nfree       = Number of free data records
   //    29->32 [37->40] fNbytesName = Number of bytes in TNamed at creation time
   //    33->33 [41->41] fUnits      = Number of bytes for file pointers
   //    34->37 [42->45] fCompress   = Zip compression level
   //    38->41 [46->53] fSeekInfo   = Pointer to TStreamerInfo record
   //    42->45 [54->57] fNbytesInfo = Number of bytes in TStreamerInfo record
   //    46->63 [58->75] fUUID       = Universal Unique ID
//Begin_Html
/*
<img src="gif/file_layout.gif">
*/
//End_Html
   //
   // The structure of a directory is shown in TDirectory::TDirectory

   if (!gROOT)
      ::Fatal("TFile::TFile", "ROOT system not initialized");

   if (!strncmp(fname1, "file:", 5))
      fname1 += 5;

   gDirectory = 0;
   SetName(fname1);
   SetTitle(ftitle);
   TDirectory::Build();

   fD          = -1;
   fFile       = this;
   fFree       = 0;
   fVersion    = gROOT->GetVersionInt();  //ROOT version in integer format
   fUnits      = 4;
   fOption     = option;
   fCompress   = compress;
   fWritten    = 0;
   fSumBuffer  = 0;
   fSum2Buffer = 0;
   fBytesRead  = 0;
   fBytesWrite = 0;
   fClassIndex = 0;
   fSeekInfo   = 0;
   fNbytesInfo = 0;
   fCache      = 0;
   fProcessIDs = 0;
   fNProcessIDs= 0;

   fOption.ToUpper();

   fArchiveOffset = 0;
   fIsArchive     = kFALSE;
   fArchive = TArchiveFile::Open(fname1, this);
   if (fArchive) {
      fname1 = fArchive->GetArchiveName();
      // if no archive member is specified then this TFile is just used
      // to read the archive contents
      if (!strlen(fArchive->GetMemberName()))
         fIsArchive = kTRUE;
   }

   if (fOption == "NET")
      return;

   if (fOption == "WEB") {
      fOption   = "READ";
      fWritable = kFALSE;
      return;
   }

   if (fOption == "NEW")
      fOption = "CREATE";

   Bool_t create   = (fOption == "CREATE") ? kTRUE : kFALSE;
   Bool_t recreate = (fOption == "RECREATE") ? kTRUE : kFALSE;
   Bool_t update   = (fOption == "UPDATE") ? kTRUE : kFALSE;
   Bool_t read     = (fOption == "READ") ? kTRUE : kFALSE;
   if (!create && !recreate && !update && !read) {
      read    = kTRUE;
      fOption = "READ";
   }

   Bool_t devnull = kFALSE;

   if (!fname1 || !strlen(fname1)) {
      Error("TFile", "file name is not specified");
      goto zombie;
   }

   // support dumping to /dev/null on UNIX
   if (!strcmp(fname1, "/dev/null") &&
       !gSystem->AccessPathName(fname1, kWritePermission)) {
      devnull  = kTRUE;
      create   = kTRUE;
      recreate = kFALSE;
      update   = kFALSE;
      read     = kFALSE;
      fOption  = "CREATE";
      SetBit(kDevNull);
   }

   const char *fname;
   if ((fname = gSystem->ExpandPathName(fname1))) {
      SetName(fname);
      delete [] (char*)fname;
      fRealName = GetName();
      fname = fRealName.Data();
      if (fArchive) {
         TString full = fname;
         if (!fIsArchive)
            full += "#"; full += fArchive->GetMemberName();
         SetName(full);
      }
   } else {
      Error("TFile", "error expanding path %s", fname1);
      goto zombie;
   }

   if (recreate) {
      if (!gSystem->AccessPathName(fname, kFileExists))
         gSystem->Unlink(fname);
      recreate = kFALSE;
      create   = kTRUE;
      fOption  = "CREATE";
   }
   if (create && !devnull && !gSystem->AccessPathName(fname, kFileExists)) {
      Error("TFile", "file %s already exists", fname);
      goto zombie;
   }
   if (update) {
      if (gSystem->AccessPathName(fname, kFileExists)) {
         update = kFALSE;
         create = kTRUE;
      }
      if (update && gSystem->AccessPathName(fname, kWritePermission)) {
         Error("TFile", "no write permission, could not open file %s", fname);
         goto zombie;
      }
   }
   if (read) {
      if (gSystem->AccessPathName(fname, kFileExists)) {
         Error("TFile", "file %s does not exist", fname);
         goto zombie;
      }
      if (gSystem->AccessPathName(fname, kReadPermission)) {
         Error("TFile", "no read permission, could not open file %s", fname);
         goto zombie;
      }
   }

   // Connect to file system stream
   if (create || update) {
#ifndef WIN32
      fD = SysOpen(fname, O_RDWR | O_CREAT, 0644);
#else
      fD = SysOpen(fname, O_RDWR | O_CREAT | O_BINARY, S_IREAD | S_IWRITE);
#endif
      if (fD == -1) {
         SysError("TFile", "file %s can not be opened", fname);
         goto zombie;
      }
      fWritable = kTRUE;
   } else {
#ifndef WIN32
      fD = SysOpen(fname, O_RDONLY, 0644);
#else
      fD = SysOpen(fname, O_RDONLY | O_BINARY, S_IREAD | S_IWRITE);
#endif
      if (fD == -1) {
         SysError("TFile", "file %s can not be opened for reading", fname);
         goto zombie;
      }
      fWritable = kFALSE;
   }

   Init(create);

   return;

zombie:
   // error in file opening occured, make this object a zombie
   MakeZombie();
   gDirectory = gROOT;
}

//______________________________________________________________________________
TFile::TFile(const TFile &file) : TDirectory()
{
   ((TFile&)file).Copy(*this);
}

//______________________________________________________________________________
TFile::~TFile()
{
   // File destructor.

   if (!fIsArchive)
      Close();

   SafeDelete(fProcessIDs);
   SafeDelete(fFree);
   SafeDelete(fCache);
   SafeDelete(fArchive);

   gROOT->GetListOfFiles()->Remove(this);
   gROOT->GetUUIDs()->RemoveUUID(GetUniqueID());

   if (gDebug)
      Info("~TFile", "dtor called for %s", GetName());
}

//______________________________________________________________________________
void TFile::Init(Bool_t create)
{
   // Initialize a TFile object.

   if (fArchive) {
      if (fOption != "READ") {
         Error("Init", "archive %s can only be opened in read mode", GetName());
         delete fArchive;
         fArchive = 0;
         fIsArchive = kFALSE;
         goto zombie;
      }
      fArchive->OpenArchive();
      if (fIsArchive) return;
      if (fArchive->SetCurrentMember() != -1)
         fArchiveOffset = fArchive->GetMemberFilePosition();
      else {
         Error("Init", "member %s not found in archive %s",
               fArchive->GetMemberName(), fArchive->GetArchiveName());
         delete fArchive;
         fArchive = 0;
         fIsArchive = kFALSE;
         goto zombie;
      }
   }

   Int_t nfree;
   fBEGIN = (Long64_t)kBEGIN;    //First used word in file following the file header

   // make newly opened file the current file and directory
   cd();

//*-*---------------NEW file
   if (create) {
      fFree        = new TList;
      fEND         = fBEGIN;    //Pointer to end of file
      new TFree(fFree, fBEGIN, Long64_t(kStartBigFile));  //Create new free list

//*-* Write Directory info
      Int_t namelen= TNamed::Sizeof();
      Int_t nbytes = namelen + TDirectory::Sizeof();
      TKey *key    = new TKey(fName,fTitle,IsA(),nbytes);
      fNbytesName  = key->GetKeylen() + namelen;
      fSeekDir     = key->GetSeekKey();
      fSeekFree    = 0;
      fNbytesFree  = 0;
      WriteHeader();
      char *buffer = key->GetBuffer();
      TNamed::FillBuffer(buffer);
      TDirectory::FillBuffer(buffer);
      key->WriteFile();
      delete key;
   }
//*-*----------------UPDATE
   else {
      char *header = new char[kBEGIN];
      Seek(0);
      ReadBuffer(header, kBEGIN);

      // make sure this is a ROOT file
      if (strncmp(header, "root", 4)) {
         Error("Init", "%s not a ROOT file", GetName());
         delete [] header;
         goto zombie;
      }

      char *buffer = header + 4;    // skip the "root" file identifier
      frombuf(buffer, &fVersion);
      Int_t headerLength;
      frombuf(buffer, &headerLength);
      fBEGIN = (Long64_t)headerLength;
      if (fVersion < 1000000) { //small file
         Int_t send,sfree,sinfo;
         frombuf(buffer, &send);         fEND     = (Long64_t)send;
         frombuf(buffer, &sfree);        fSeekFree= (Long64_t)sfree;
         frombuf(buffer, &fNbytesFree);
         frombuf(buffer, &nfree);
         frombuf(buffer, &fNbytesName);
         frombuf(buffer, &fUnits );
         frombuf(buffer, &fCompress);
         frombuf(buffer, &sinfo);        fSeekInfo = (Long64_t)sinfo;
         frombuf(buffer, &fNbytesInfo);
      } else { // new format to support large files
         frombuf(buffer, &fEND);
         frombuf(buffer, &fSeekFree);
         frombuf(buffer, &fNbytesFree);
         frombuf(buffer, &nfree);
         frombuf(buffer, &fNbytesName);
         frombuf(buffer, &fUnits );
         frombuf(buffer, &fCompress);
         frombuf(buffer, &fSeekInfo);
         frombuf(buffer, &fNbytesInfo);
      }
      fSeekDir = fBEGIN;
      delete [] header;
//*-*-------------Read Free segments structure if file is writable
      if (fWritable) {
         fFree = new TList;
         if (fSeekFree > fBEGIN) {
            ReadFree();
         } else {
            Warning("Init","file %s probably not closed, cannot read free segments",GetName());
         }
      }
//*-*-------------Read directory info
      Int_t nbytes = fNbytesName + TDirectory::Sizeof();
      header       = new char[nbytes];
      buffer       = header;
      Seek(fBEGIN);
      ReadBuffer(buffer,nbytes);
      buffer = header+fNbytesName;
      Version_t version,versiondir;
      frombuf(buffer,&version); versiondir = version%1000;
      fDatimeC.ReadBuffer(buffer);
      fDatimeM.ReadBuffer(buffer);
      frombuf(buffer, &fNbytesKeys);
      frombuf(buffer, &fNbytesName);
      Int_t nk = sizeof(Int_t) +sizeof(Version_t) +2*sizeof(Int_t)+2*sizeof(Short_t)
                +2*sizeof(Int_t);
      if (version > 1000) {
         nk += 12;
         frombuf(buffer, &fSeekDir);
         frombuf(buffer, &fSeekParent);
         frombuf(buffer, &fSeekKeys);
      } else {
         Int_t sdir,sparent,skeys;
         frombuf(buffer, &sdir);    fSeekDir    = (Long64_t)sdir;
         frombuf(buffer, &sparent); fSeekParent = (Long64_t)sparent;
         frombuf(buffer, &skeys);   fSeekKeys   = (Long64_t)skeys;
      }
      if (versiondir > 1) fUUID.ReadBuffer(buffer);

//*-*---------read TKey::FillBuffer info
      buffer = header+nk;
      TString cname;
      cname.ReadBuffer(buffer);
      cname.ReadBuffer(buffer); // fName.ReadBuffer(buffer); file may have been renamed
      fTitle.ReadBuffer(buffer);
      delete [] header;
      if (fNbytesName < 10 || fNbytesName > 1000) {
         Error("Init","cannot read directory info of file %s", GetName());
         goto zombie;
      }
//*-* -------------Check if file is truncated
      Long64_t size;
      if ((size = GetSize()) == -1) {
         Error("Init", "cannot stat the file %s", GetName());
         goto zombie;
      }
//*-* -------------Read keys of the top directory

      if (fSeekKeys > fBEGIN && fEND <= size) {
         //normal case. Recover only if file has no keys
         TDirectory::ReadKeys();
         gDirectory = this;
         if (!GetNkeys()) Recover();
      } else if ((fBEGIN+nbytes == fEND) && (fEND == size)) {
         //the file might be open by another process and nothing written to the file yet
         Warning("Init","file %s has no keys", GetName());
         gDirectory = this;
      } else {
         //something had been written to the file. Trailer is missing, must recover
         if (fEND > size) {
            Error("Init","file %s is truncated at %lld bytes: should be %lld, trying to recover",
                  GetName(), size, fEND);
         } else {
            Warning("Init","file %s probably not closed, trying to recover",
                    GetName());
         }
         Int_t nrecov = Recover();
         if (nrecov) {
            Warning("Init", "successfully recovered %d keys", nrecov);
         } else {
            Warning("Init", "no keys recovered, file has been made a Zombie");
            goto zombie;
         }
      }
   }
   gROOT->GetListOfFiles()->Add(this);
   gROOT->GetUUIDs()->AddUUID(fUUID,this);

   // Create StreamerInfo index
   {
      Int_t lenIndex = gROOT->GetListOfStreamerInfo()->GetSize()+1;
      if (lenIndex < 5000) lenIndex = 5000;
      fClassIndex = new TArrayC(lenIndex);
      if (fSeekInfo > fBEGIN) ReadStreamerInfo();
   }

   // Count number of TProcessIDs in this file
   {
      TIter next(fKeys);
      TKey *key;
      while ((key = (TKey*)next())) {
          if (!strcmp(key->GetClassName(),"TProcessID")) fNProcessIDs++;
      }
      fProcessIDs = new TObjArray(fNProcessIDs+1);
      return;
   }

zombie:
   // error in file opening occured, make this object a zombie
   MakeZombie();
   gDirectory = gROOT;
}

//______________________________________________________________________________
void TFile::Close(Option_t *option)
{
//*-*-*-*-*-*-*-*-*-*-*-*Close a file*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
//*-*                    ============
// if option == "R", all TProcessIDs referenced by this file are deleted.
// Calling TFile::Close("R") might be necessary in case one reads a long list
// of files having TRef, writing some of the referenced objects or TRef
// to a new file. If the TRef or referenced objects of the file being closed
// will not be referenced again, it is possible to minimize the size
// of the TProcessID data structures in memory by forcing a delete of
// the unused TProcessID.

   TString opt = option;
   opt.ToLower();

   if (!IsOpen()) return;

   if (IsWritable()) {
      WriteStreamerInfo();
   }

   delete fClassIndex;
   fClassIndex = 0;

   TCollection::StartGarbageCollection();

   TDirectory *cursav = gDirectory;
   cd();

   if (cursav == this || cursav->GetFile() == this) {
      cursav = 0;
   }

   // Delete all supported directories structures from memory
   TDirectory::Close();
   cd();      // Close() sets gFile = 0

   if (IsWritable()) {
      TFree *f1 = (TFree*)fFree->First();
      if (f1) {
         WriteFree();       //*-*- Write free segments linked list
         WriteHeader();     //*-*- Now write file header
      }
      if (fCache) fCache->Flush();
   }

   // Delete free segments from free list (but don't delete list header)
   if (fFree) {
      fFree->Delete();
   }

   if (IsOpen()) {
      SysClose(fD);
      fD = -1;
   }

   fWritable = kFALSE;

   if (cursav)
      cursav->cd();
   else {
      gFile      = 0;
      gDirectory = gROOT;
   }

   //delete the TProcessIDs
   TList pidDeleted;
   TIter next(fProcessIDs);
   TProcessID *pid;
   while ((pid = (TProcessID*)next())) {
      if (!pid->DecrementCount()) {
         if (pid != TProcessID::GetSessionProcessID()) pidDeleted.Add(pid);
      } else if(opt.Contains("r")) {
         pid->Clear();
      }
   }
   pidDeleted.Delete();

   gROOT->GetListOfFiles()->Remove(this);

   TCollection::EmptyGarbageCollection();
}

//______________________________________________________________________________
void TFile::Delete(const char *namecycle)
{
//*-*-*-*-*-*-*-*-*-*-*-*-*Delete object namecycle*-*-*-*-*-*-*-*-*-*-*-*-*-*
//*-*                      =======================
//  namecycle identifies an object in the top directory of the file
//   namecycle has the format name;cycle
//   name  = * means all
//   cycle = * means all cycles (memory and keys)
//   cycle = "" or cycle = 9999 ==> apply to a memory object
//   When name=* use T* to delete subdirectories also
//
//   examples:
//     foo   : delete object named foo in memory
//     foo;1 : delete cycle 1 of foo on file
//     foo;* : delete all cycles of foo on disk and also from memory
//     *;2   : delete all objects on file having the cycle 2
//     *;*   : delete all objects from memory and file
//    T*;*   : delete all objects from memory and file and all subdirectories
//

   if (gDebug)
      Info("Delete", "deleting name = %s", namecycle);

   TDirectory::Delete(namecycle);
}

//______________________________________________________________________________
void TFile::Draw(Option_t *option)
{
//*-*-*-*-*-*-*-*-*-*-*-*Fill Graphics Structure and Paint-*-*-*-*-*-*-*-*-*-*
//*-*                    =================================
// Loop on all objects (memory or file) and all subdirectories
//

   GetList()->ForEach(TObject,Draw)(option);
}

//______________________________________________________________________________
void TFile::DrawMap(const char *keys, Option_t *option)
{
// Draw map of objects in this file

   TPluginHandler *h;
   if ((h = gROOT->GetPluginManager()->FindHandler("TFileDrawMap"))) {
      if (h->LoadPlugin() == -1)
         return;
      h->ExecPlugin(3, this, keys, option);
   }
}

//______________________________________________________________________________
void TFile::Flush()
{
   // Synchornize a file's in-core and on-disk states.

   if (IsOpen() && fWritable) {
      if (SysSync(fD) < 0) {
         // Write the system error only once for this file
         SetBit(kWriteError); SetWritable(kFALSE);
         SysError("Flush", "error flushing file %s", GetName());
      }
   }
}

//______________________________________________________________________________
void TFile::FillBuffer(char *&buffer)
{
//*-*-*-*-*-*-*-*-*-*-*-*Encode file output buffer*-*-*-*-*-*-*
//*-*                    =========================
// The file output buffer contains only the FREE data record
//

   Version_t version = TFile::Class_Version();
   tobuf(buffer, version);
}

//______________________________________________________________________________
Int_t TFile::GetBestBuffer() const
{
//*-*-*-*-*-*-*-*Return the best buffer size of objects on this file*-*-*-*-*-*
//*-*            ===================================================
//
//  The best buffer size is estimated based on the current mean value
//  and standard deviation of all objects written so far to this file.
//  Returns mean value + one standard deviation.
//

   if (!fWritten) return TBuffer::kInitialSize;
   Double_t mean = fSumBuffer/fWritten;
   Double_t rms2 = TMath::Abs(fSum2Buffer/fSumBuffer -mean*mean);
   return (Int_t)(mean + TMath::Sqrt(rms2));
}

//______________________________________________________________________________
Float_t TFile::GetCompressionFactor()
{
//*-*-*-*-*-*-*-*-*-*Return the file compression factor*-*-*-*-*-*-*-*-*-*
//*-*                =================================
//
//  Add total number of compressed/uncompressed bytes for each key.
//  return ratio of the two.
//
   Short_t  keylen;
   UInt_t   datime;
   Int_t    nbytes, objlen, nwh = 64;
   char    *header = new char[fBEGIN];
   char    *buffer;
   Long64_t   idcur = fBEGIN;
   Float_t comp,uncomp;
   comp = uncomp = fBEGIN;

   while (idcur < fEND-100) {
      Seek(idcur);
      ReadBuffer(header, nwh);
      buffer=header;
      frombuf(buffer, &nbytes);
      if (nbytes < 0) {
         idcur -= nbytes;
         Seek(idcur);
         continue;
      }
      if (nbytes == 0) break; //this may happen when the file is corrupted
      Version_t versionkey;
      frombuf(buffer, &versionkey);
      frombuf(buffer, &objlen);
      frombuf(buffer, &datime);
      frombuf(buffer, &keylen);
      if (!objlen) objlen = nbytes-keylen;
      comp   += nbytes;
      uncomp += keylen + objlen;
      idcur  += nbytes;
   }
   delete [] header;
   return uncomp/comp;
}

//______________________________________________________________________________
Int_t TFile::GetErrno() const
{
   // Method returning errno. Is overriden in TRFIOFile.

   return TSystem::GetErrno();
}

//______________________________________________________________________________
void TFile::ResetErrno() const
{
   // Method resetting the errno. Is overridden in TRFIOFile.

   TSystem::ResetErrno();
}

//______________________________________________________________________________
Int_t TFile::GetRecordHeader(char *buf, Long64_t first, Int_t maxbytes, Int_t &nbytes, Int_t &objlen, Int_t &keylen)
{
//*-*-*-*-*-*-*-*-*Read the logical record header starting at position first
//*-*              =========================================================
// maxbytes bytes are read into buf
// the function reads nread bytes where nread is the minimum of maxbytes
// and the number of bytes before the end of file.
// the function returns nread.
// In output arguments:
//    nbytes : number of bytes in record
//             if negative, this is a deleted record
//             if 0, cannot read record, wrong value of argument first
//    objlen : uncompressed object size
//    keylen : length of logical record header
// Note that the arguments objlen and keylen are returned only if maxbytes >=16

   if (first < fBEGIN) return 0;
   if (first > fEND)   return 0;
   Seek(first);
   Int_t nread = maxbytes;
   if (first+maxbytes > fEND) nread = fEND-maxbytes;
   if (nread < 4) {
      Warning("GetRecordHeader","%s: parameter maxbytes = %d must be >= 4",
              GetName(), nread);
      return nread;
   }
   ReadBuffer(buf,nread);
   Version_t versionkey;
   Short_t  klen;
   UInt_t   datime;
   Int_t    nb,olen;
   char *buffer = buf;
   frombuf(buffer,&nb);
   nbytes = nb;
   if (nb < 0) return nread;
//   const Int_t headerSize = Int_t(sizeof(nb) +sizeof(versionkey) +sizeof(olen) +sizeof(datime) +sizeof(klen));
   const Int_t headerSize = 16;
   if (nread < headerSize) return nread;
   frombuf(buffer, &versionkey);
   frombuf(buffer, &olen);
   frombuf(buffer, &datime);
   frombuf(buffer, &klen);
   if (!olen) olen = nbytes-klen;
   objlen = olen;
   keylen = klen;
   return nread;
}

//______________________________________________________________________________
Long64_t TFile::GetSize() const
{
   // Returns the current file size. Returns -1 in case the file could not
   // be stat'ed.

   Long64_t size;
   Long_t id, flags, modtime;

   if (const_cast<TFile*>(this)->SysStat(fD, &id, &size, &flags, &modtime)) {
      Error("GetSize", "cannot stat the file %s", GetName());
      return -1;
   }

   return size;
}

//______________________________________________________________________________
TList *TFile::GetStreamerInfoList()
{
// Read the list of TStreamerInfo objects written to this file.
// The function returns a TList. It is the user'responsability
// to delete the list created by this function.
//
// Using the list, one can access additional information,eg:
//   TFile f("myfile.root");
//   TList *list = f.GetStreamerInfoList();
//   TStreamerInfo *info = (TStreamerInfo*)list->FindObject("MyClass");
//   Int_t classversionid = info->GetClassVersion();
//   delete list;

   TList *list = 0;
   if (fSeekInfo) {
      TKey *key = new TKey();
      char *buffer = new char[fNbytesInfo+1];
      char *buf    = buffer;
      Seek(fSeekInfo);
      ReadBuffer(buf,fNbytesInfo);
      key->ReadBuffer(buf);
      TFile *filesave = gFile;
      gFile = this; // used in ReadObj
      list = (TList*)key->ReadObj();
      gFile = filesave;
      list->SetOwner();
      delete [] buffer;
      delete key;
   } else {
      list = (TList*)Get("StreamerInfo"); //for versions 2.26 (never released)
   }

   if (list == 0) {
      Info("GetStreamerInfoList", "cannot find the StreamerInfo record in file %s",
           GetName());
      return 0;
   }

   return list;
}

//______________________________________________________________________________
void TFile::ls(Option_t *option) const
{
//*-*-*-*-*-*-*-*-*-*-*-*List File contents*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
//*-*                    ==================
//  Indentation is used to identify the file tree
//  Subdirectories are listed first
//  then objects in memory
//  then objects on the file
//

   TROOT::IndentLevel();
   cout <<ClassName()<<"**\t\t"<<GetName()<<"\t"<<GetTitle()<<endl;
   TROOT::IncreaseDirLevel();
   TDirectory::ls(option);
   TROOT::DecreaseDirLevel();
}

//______________________________________________________________________________
Bool_t TFile::IsOpen() const
{
   // Returns kTRUE in case file is open and kFALSE if file is not open.

   return fD == -1 ? kFALSE : kTRUE;
}

//______________________________________________________________________________
void TFile::MakeFree(Long64_t first, Long64_t last)
{
//*-*-*-*-*-*-*-*-*-*-*-*Mark unused bytes on the file*-*-*-*-*-*-*-*-*-*-*
//*-*                    =============================
//  The list of free segments is in the fFree linked list
//  When an object is deleted from the file, the freed space is added
//  into the FREE linked list (fFree). The FREE list consists of a chain
//  of consecutive free segments on the file. At the same time, the first
//  4 bytes of the freed record on the file are overwritten by GAPSIZE
//  where GAPSIZE = -(Number of bytes occupied by the record).
//

   TFree *f1      = (TFree*)fFree->First();
   if (!f1) return;
   TFree *newfree = f1->AddFree(fFree,first,last);
   if(!newfree) return;
   Long64_t nfirst = newfree->GetFirst();
   Long64_t nlast  = newfree->GetLast();
   Long64_t nbytesl= nlast-nfirst+1;
   if (nbytesl > 2000000000) nbytesl = 2000000000;
   Int_t nbytes    = -Int_t (nbytesl);
   Int_t nb        = sizeof(Int_t);
   char * buffer   = new char[nb];
   char * psave    = buffer;
   tobuf(buffer, nbytes);
   if (nlast == fEND-1) fEND = nfirst;
   Seek(nfirst);
   WriteBuffer(psave, nb);
   Flush();
   delete [] psave;
}


//______________________________________________________________________________
void TFile::Map()
{
//*-*-*-*-*-*-*-*-*-*List the contents of a file sequentially*-*-*-*-*-*
//*-*                ========================================
//
//  For each logical record found, it prints
//     Date/Time  Record_Adress Logical_Record_Length  ClassName  CompressionFactor
//
//  Example of output
//  20010404/150437  At:64        N=150       TFile
//  20010404/150440  At:214       N=28326     TBasket        CX =  1.13
//  20010404/150440  At:28540     N=29616     TBasket        CX =  1.08
//  20010404/150440  At:58156     N=29640     TBasket        CX =  1.08
//  20010404/150440  At:87796     N=29076     TBasket        CX =  1.10
//  20010404/150440  At:116872    N=10151     TBasket        CX =  3.15
//  20010404/150441  At:127023    N=28341     TBasket        CX =  1.13
//  20010404/150441  At:155364    N=29594     TBasket        CX =  1.08
//  20010404/150441  At:184958    N=29616     TBasket        CX =  1.08
//  20010404/150441  At:214574    N=29075     TBasket        CX =  1.10
//  20010404/150441  At:243649    N=9583      TBasket        CX =  3.34
//  20010404/150442  At:253232    N=28324     TBasket        CX =  1.13
//  20010404/150442  At:281556    N=29641     TBasket        CX =  1.08
//  20010404/150442  At:311197    N=29633     TBasket        CX =  1.08
//  20010404/150442  At:340830    N=29091     TBasket        CX =  1.10
//  20010404/150442  At:369921    N=10341     TBasket        CX =  3.09
//  20010404/150442  At:380262    N=509       TH1F           CX =  1.93
//  20010404/150442  At:380771    N=1769      TH2F           CX =  4.32
//  20010404/150442  At:382540    N=1849      TProfile       CX =  1.65
//  20010404/150442  At:384389    N=18434     TNtuple        CX =  4.51
//  20010404/150442  At:402823    N=307       KeysList
//  20010404/150443  At:403130    N=4548      StreamerInfo   CX =  3.65
//  20010404/150443  At:407678    N=86        FreeSegments
//  20010404/150443  At:407764    N=1         END
//
   Short_t  keylen,cycle;
   UInt_t   datime;
   Int_t    nbytes,date,time,objlen,nwheader;
   Long64_t seekkey,seekpdir;
   char    *buffer;
   char     nwhc;
   Long64_t idcur = fBEGIN;

   nwheader = 64;
   Int_t nread = nwheader;

   char header[kBEGIN];
   char classname[512];

   while (idcur < fEND) {
      Seek(idcur);
      if (idcur+nread >= fEND) nread = fEND-idcur-1;
      ReadBuffer(header, nread);
      buffer=header;
      frombuf(buffer, &nbytes);
      if (!nbytes) {
         Printf("Address = %lld\tNbytes = %d\t=====E R R O R=======", idcur, nbytes);
         break;
      }
      if (nbytes < 0) {
         Printf("Address = %lld\tNbytes = %d\t=====G A P===========", idcur, nbytes);
         idcur -= nbytes;
         Seek(idcur);
         continue;
      }
      Version_t versionkey;
      frombuf(buffer, &versionkey);
      frombuf(buffer, &objlen);
      frombuf(buffer, &datime);
      frombuf(buffer, &keylen);
      frombuf(buffer, &cycle);
      if (versionkey > 1000) {
         frombuf(buffer, &seekkey);
         frombuf(buffer, &seekpdir);
      } else {
         Int_t skey,sdir;
         frombuf(buffer, &skey);  seekkey  = (Long64_t)skey;
         frombuf(buffer, &sdir);  seekpdir = (Long64_t)sdir;
      }
      frombuf(buffer, &nwhc);
      int i;
      for (i = 0;i < nwhc; i++) frombuf(buffer, &classname[i]);
      classname[(int)nwhc] = '\0'; //cast to avoid warning with gcc3.4
      if (idcur == fSeekFree) strcpy(classname,"FreeSegments");
      if (idcur == fSeekInfo) strcpy(classname,"StreamerInfo");
      if (idcur == fSeekKeys) strcpy(classname,"KeysList");
      TDatime::GetDateTime(datime, date, time);
      if (objlen != nbytes-keylen) {
         Float_t cx = Float_t(objlen+keylen)/Float_t(nbytes);
         //Printf("%d/%06d  At:%-8d  N=%-8d  %-14s CX = %5.2f",date,time,idcur,nbytes,classname,cx);
         Printf("%d/%06d  At:%lld  N=%-8d  %-14s CX = %5.2f",date,time,idcur,nbytes,classname,cx);
      } else {
         //Printf("%d/%06d  At:%-8d  N=%-8d  %-14s",date,time,idcur,nbytes,classname);
         Printf("%d/%06d  At:%lld  N=%-8d  %-14s",date,time,idcur,nbytes,classname);
      }
      idcur += nbytes;
   }
   //Printf("%d/%06d  At:%-8d  N=%-8d  %-14s",date,time,idcur,1,"END");
   Printf("%d/%06d  At:%lld  N=%-8d  %-14s",date,time,idcur,1,"END");
}

//______________________________________________________________________________
void TFile::Paint(Option_t *option)
{
//*-*-*-*-*-*-*-*-*-*-*-*Paint all objects in the file*-*-*-*-*-*-*-*-*-*-*
//*-*                    =============================
//

   GetList()->ForEach(TObject,Paint)(option);
}

//______________________________________________________________________________
void TFile::Print(Option_t *option) const
{
//*-*-*-*-*-*-*-*-*-*-*-*Print all objects in the file*-*-*-*-*-*-*-*-*-*-*
//*-*                    =============================
//

   Printf("TFile: name=%s, title=%s, option=%s", GetName(), GetTitle(), GetOption());
   GetList()->ForEach(TObject,Print)(option);
}

//______________________________________________________________________________
Bool_t TFile::ReadBuffer(char *buf, Int_t len)
{
   // Read a buffer from the file. This is the basic low level read operation.
   // Returns kTRUE in case of failure.

   if (IsOpen()) {
      ssize_t siz;

      Double_t start = 0;
      if (gPerfStats != 0) start = TTimeStamp();

      while ((siz = SysRead(fD, buf, len)) < 0 && GetErrno() == EINTR)
         ResetErrno();
      if (siz < 0) {
         SysError("ReadBuffer", "error reading from file %s", GetName());
         return kTRUE;
      }
      if (siz != len) {
         Error("ReadBuffer", "error reading all requested bytes from file %s, got %d of %d",
               GetName(), siz, len);
         return kTRUE;
      }
      fBytesRead  += siz;
      fgBytesRead += siz;

      if (gPerfStats != 0) {
         gPerfStats->FileReadEvent(this, len, double(TTimeStamp())-start);
      }
      return kFALSE;
   }
   return kTRUE;
}

//______________________________________________________________________________
void TFile::ReadFree()
{
//*-*-*-*-*-*-*-*-*-*-*-*-*Read the FREE linked list*-*-*-*-*-*-*-*-*-*-*-*-*
//*-*                      =========================
//  Every file has a linked list (fFree) of free segments
//  This linked list has been written on the file via WriteFree
//  as a single data record
//

   TKey *headerfree = new TKey(fSeekFree,fNbytesFree);
   headerfree->ReadFile();
   char *buffer = headerfree->GetBuffer();
   headerfree->ReadBuffer(buffer);
   buffer = headerfree->GetBuffer();
   while (1) {
      TFree *afree = new TFree();
      afree->ReadBuffer(buffer);
      fFree->Add(afree);
      if (afree->GetLast() > fEND) break;
   }
   delete headerfree;
}

//______________________________________________________________________________
Int_t TFile::Recover()
{
//*-*-*-*-*-*-*-*-*Attempt to recover file if not correctly closed*-*-*-*-*
//*-*              ===============================================
//
//  The function returns the number of keys that have been recovered.
//  If no keys can be recovered, the file will be declared Zombie by
//  the calling function.

   Short_t  keylen,cycle;
   UInt_t   datime;
   Int_t    nbytes,date,time,objlen,nwheader;
   Long64_t seekkey,seekpdir;
   char     header[1024];
   char    *buffer, *bufread;
   char     nwhc;
   Long64_t idcur = fBEGIN;

   Long64_t size;
   if ((size = GetSize()) == -1) {
      Error("Recover", "cannot stat the file %s", GetName());
      return 0;
   }

   fEND = Long64_t(size);

   if (fWritable && !fFree) fFree  = new TList;

   TKey *key;
   Int_t nrecov = 0;
   nwheader = 1024;
   Int_t nread = nwheader;

   while (idcur < fEND) {
      Seek(idcur);
      if (idcur+nread >= fEND) nread = fEND-idcur-1;
      ReadBuffer(header, nread);
      buffer  = header;
      bufread = header;
      frombuf(buffer, &nbytes);
      if (!nbytes) {
         Printf("Address = %lld\tNbytes = %d\t=====E R R O R=======", idcur, nbytes);
         break;
      }
      if (nbytes < 0) {
         idcur -= nbytes;
         if (fWritable) new TFree(fFree,idcur,idcur-nbytes-1);
         Seek(idcur);
         continue;
      }
      Version_t versionkey;
      frombuf(buffer, &versionkey);
      frombuf(buffer, &objlen);
      frombuf(buffer, &datime);
      frombuf(buffer, &keylen);
      frombuf(buffer, &cycle);
      if (versionkey > 1000) {
         frombuf(buffer, &seekkey);
         frombuf(buffer, &seekpdir);
      } else {
         Int_t skey,sdir;
         frombuf(buffer, &skey);  seekkey  = (Long64_t)skey;
         frombuf(buffer, &sdir);  seekpdir = (Long64_t)sdir;
      }
      frombuf(buffer, &nwhc);
      char *classname = 0;
      if (nwhc <= 0 || nwhc > 100) break;
      classname = new char[nwhc+1];
      int i;
      for (i = 0;i < nwhc; i++) frombuf(buffer, &classname[i]);
      classname[nwhc] = '\0';
      TDatime::GetDateTime(datime, date, time);
      if (seekpdir == fSeekDir && strcmp(classname,"TFile") && strcmp(classname,"TBasket")) {
         key = new TKey();
         key->ReadBuffer(bufread);
         if (!strcmp(key->GetName(),"StreamerInfo")) {
            fSeekInfo = seekkey;
            fNbytesInfo = nbytes;
         } else {
            AppendKey(key);
            nrecov++;
            SetBit(kRecovered);
            Info("Recover", "%s, recovered key %s:%s at address %lld",GetName(),key->GetClassName(),key->GetName(),idcur);
         }
      }
      delete [] classname;
      idcur += nbytes;
   }
   if (fWritable) {
      Long64_t max_file_size = Long64_t(kStartBigFile);
      if (max_file_size < fEND) max_file_size = fEND+1000000000;
      new TFree(fFree,fEND,max_file_size);
      if (nrecov) Write();
   }
   return nrecov;
}

//______________________________________________________________________________
Int_t TFile::ReOpen(Option_t *mode)
{
   // Reopen a file with a different access mode, like from READ to
   // UPDATE or from NEW, CREATE, RECREATE, UPDATE to READ. Thus the
   // mode argument can be either "READ" or "UPDATE". The method returns
   // 0 in case the mode was successfully modified, 1 in case the mode
   // did not change (was already as requested or wrong input arguments)
   // and -1 in case of failure, in which case the file cannot be used
   // anymore. The current directory (gFile) is changed to this file.

   cd();

   TString opt = mode;
   opt.ToUpper();

   if (opt != "READ" && opt != "UPDATE") {
      Error("ReOpen", "mode must be either READ or UPDATE, not %s", opt.Data());
      return 1;
   }

   if (opt == fOption || (opt == "UPDATE" && fOption == "CREATE"))
      return 1;

   if (opt == "READ") {
      // switch to READ mode

      // flush data still in the pipeline and close the file
      if (IsOpen() && IsWritable()) {
         WriteStreamerInfo();

         // save directory key list and header
         Save();

         TFree *f1 = (TFree*)fFree->First();
         if (f1) {
            WriteFree();       // write free segments linked list
            WriteHeader();     // now write file header
         }
         if (fCache) fCache->Flush();

         // delete free segments from free list
         if (fFree) {
            fFree->Delete();
            SafeDelete(fFree);
         }

         SysClose(fD);
         fD = -1;

         SetWritable(kFALSE);
      }

      // open in READ mode
      fOption = opt;    // set fOption before SysOpen() for TNetFile
#ifndef WIN32
      fD = SysOpen(fRealName, O_RDONLY, 0644);
#else
      fD = SysOpen(fRealName, O_RDONLY | O_BINARY, S_IREAD | S_IWRITE);
#endif
      if (fD == -1) {
         SysError("ReOpen", "file %s can not be opened in read mode", GetName());
         return -1;
      }
      SetWritable(kFALSE);

   } else {
      // switch to UPDATE mode

      // close readonly file
      if (IsOpen()) {
         SysClose(fD);
         fD = -1;
      }

      // open in UPDATE mode
      fOption = opt;    // set fOption before SysOpen() for TNetFile
#ifndef WIN32
      fD = SysOpen(fRealName, O_RDWR | O_CREAT, 0644);
#else
      fD = SysOpen(fRealName, O_RDWR | O_CREAT | O_BINARY, S_IREAD | S_IWRITE);
#endif
      if (fD == -1) {
         SysError("ReOpen", "file %s can not be opened in update mode", GetName());
         return -1;
      }
      SetWritable(kTRUE);

      fFree = new TList;
      if (fSeekFree > fBEGIN)
         ReadFree();
      else
         Warning("ReOpen","file %s probably not closed, cannot read free segments", GetName());
   }

   return 0;
}

//______________________________________________________________________________
void TFile::Seek(Long64_t offset, ERelativeTo pos)
{
   // Seek to a specific position in the file. Pos it either kBeg, kCur or kEnd.

   int whence = 0;
   switch (pos) {
      case kBeg:
         whence = SEEK_SET;
         offset += fArchiveOffset;
         break;
      case kCur:
         whence = SEEK_CUR;
         break;
      case kEnd:
         whence = SEEK_END;
         // this option is not used currently in the ROOT code
         if (fArchiveOffset)
            Error("Seek", "seeking from end in archive is not (yet) supported");
         break;
   }
   if (Long64_t retpos = SysSeek(fD, offset, whence) < 0)
      SysError("Seek", "cannot seek to position %lld in file %s, retpos=%lld",
               offset, GetName(), retpos);
}

//______________________________________________________________________________
void TFile::SetCompressionLevel(Int_t level)
{
//*-*-*-*-*-*-*-*-*-*Set level of compression for this file*-*-*-*-*-*-*-*
//*-*                ======================================
//
//  level = 0 objects written to this file will not be compressed.
//  level = 1 minimal compression level but fast.
//  ....
//  level = 9 maximal compression level but slow.
//
//  Note that the compression level may be changed at any time.
//  The new compression level will only apply to newly written objects.
//  The function TFile::Map shows the compression factor
//  for each object written to this file.
//  The function TFile::GetCompressionFactor returns the global
//  compression factor for this file.
//

   if (level < 0) level = 0;
   if (level > 9) level = 9;
   fCompress = level;
}

//______________________________________________________________________________
Int_t TFile::Sizeof() const
{
   // Return the size in bytes of the file header.

   return 0;
}

//_______________________________________________________________________
void TFile::Streamer(TBuffer &b)
{
   // Stream a TFile object.

   if (b.IsReading()) {
      b.ReadVersion();  //Version_t v = b.ReadVersion();
   } else {
      b.WriteVersion(TFile::IsA());
   }
}

//_______________________________________________________________________
void TFile::SumBuffer(Int_t bufsize)
{
//*-*-*-*-*Increment statistics for buffer sizes of objects in this file*-*-*
//*-*      =============================================================

   fWritten++;
   fSumBuffer  += bufsize;
   fSum2Buffer += bufsize*bufsize;
}

//_______________________________________________________________________
void TFile::UseCache(Int_t maxCacheSize, Int_t pageSize)
{
   // Activate caching. Use maxCacheSize to specify the maximum cache size
   // in MB's (default is 10 MB) and pageSize to specify the page size
   // (default is 512 KB). To turn off the cache use maxCacheSize=0.
   // Not needed for normal disk files since the operating system will
   // do proper caching (via the "buffer cache"). Use it for TNetFile,
   // TWebFile, TRFIOFile, TDCacheFile, etc.

   if (IsA() == TFile::Class())
      return;

   if (maxCacheSize == 0) {
      if (fCache) {
         if (IsWritable())
            fCache->Flush();
         delete fCache;
         fCache = 0;
      }
      return;
   }

   if (fCache) {
      // if pageSize is changed, we need to delete the cache and recreate it
      if (pageSize != fCache->GetPageSize()) {
         if (IsWritable())
            fCache->Flush();
         delete fCache;
      } else if (maxCacheSize != fCache->GetMaxCacheSize()) {
         fCache->Resize(maxCacheSize);
         return;
      }
   }
   fCache = new TCache(maxCacheSize, this, pageSize);
}

//______________________________________________________________________________
Int_t TFile::Write(const char *, Int_t opt, Int_t bufsiz)
{
//*-*-*-*-*-*-*-*-*-*Write memory objects to this file*-*-*-*-*-*-*-*-*-*
//*-*                =================================
//  Loop on all objects in memory (including subdirectories).
//  A new key is created in the KEYS linked list for each object.
//  The list of keys is then saved on the file (via WriteKeys)
//  as a single data record.
//  For values of opt see TObject::Write().
//  The directory header info is rewritten on the directory header record.
//  The linked list of FREE segments is written.
//  The file header is written (bytes 1->fBEGIN).
//

   if (!IsWritable()) {
      if (!TestBit(kWriteError)) {
         // Do not print the warning if we already had a SysError.
         Warning("Write", "file %s not opened in write mode", GetName());
      }
      return 0;
   }

   TDirectory *cursav = gDirectory;
   cd();

   if (gDebug) {
      if (!GetTitle() || strlen(GetTitle()) == 0)
         Info("Write", "writing name = %s", GetName());
      else
         Info("Write", "writing name = s title = %s", GetName(), GetTitle());
   }

   Int_t nbytes = TDirectory::Write(0, opt, bufsiz); // Write directory tree
   WriteStreamerInfo();
   WriteFree();                       // Write free segments linked list
   WriteHeader();                     // Now write file header

   cursav->cd();
   return nbytes;
}

//______________________________________________________________________________
Bool_t TFile::WriteBuffer(const char *buf, Int_t len)
{
   // Write a buffer to the file. This is the basic low level write operation.
   // Returns kTRUE in case of failure.

   if (IsOpen() && fWritable) {
      ssize_t siz;
      gSystem->IgnoreInterrupt();
      while ((siz = SysWrite(fD, buf, len)) < 0 && GetErrno() == EINTR)
         ResetErrno();
      gSystem->IgnoreInterrupt(kFALSE);
      if (siz < 0) {
         // Write the system error only once for this file
         SetBit(kWriteError); SetWritable(kFALSE);
         SysError("WriteBuffer", "error writing to file %s (%d)", GetName(), siz);
         return kTRUE;
      }
      if (siz != len) {
         SetBit(kWriteError);
         Error("WriteBuffer", "error writing all requested bytes to file %s, wrote %d of %d",
               GetName(), siz, len);
         return kTRUE;
      }
      fBytesWrite  += siz;
      fgBytesWrite += siz;

      return kFALSE;
   }
   return kTRUE;
}

//______________________________________________________________________________
void TFile::WriteFree()
{
//*-*-*-*-*-*-*-*-*-*-*-*Write FREE linked list on the file *-*-*-*-*-*-*-*
//*-*                    ==================================
//  The linked list of FREE segments (fFree) is written as a single data
//  record
//

//*-* Delete old record if it exists
   if (fSeekFree != 0){
      MakeFree(fSeekFree, fSeekFree + fNbytesFree -1);
   }

   Int_t nbytes = 0;
   TFree *afree;
   TIter next (fFree);
   while ((afree = (TFree*) next())) {
      nbytes += afree->Sizeof();
   }
   if (!nbytes) return;
   TKey *key    = new TKey(fName,fTitle,IsA(),nbytes);
   if (key->GetSeekKey() == 0) {
      delete key;
      return;
   }
   char *buffer = key->GetBuffer();

   next.Reset();
   while ((afree = (TFree*) next())) {
      afree->FillBuffer(buffer);
   }
   fNbytesFree = key->GetNbytes();
   fSeekFree   = key->GetSeekKey();
   key->WriteFile();
   delete key;
}

//______________________________________________________________________________
void TFile::WriteHeader()
{
//*-*-*-*-*-*-*-*-*-*-*-*Write File Header*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
//*-*                    =================
//
   TFree *lastfree = (TFree*)fFree->Last();
   if (lastfree) fEND  = lastfree->GetFirst();
   const char *root = "root";
   char *psave  = new char[fBEGIN];
   char *buffer = psave;
   Int_t nfree  = fFree->GetSize();
   memcpy(buffer, root, 4); buffer += 4;
   Int_t version = fVersion;
   if (fEND > kStartBigFile) {version += 1000000; fUnits = 8;}
   tobuf(buffer, version);
   tobuf(buffer, (Int_t)fBEGIN);
   if (version < 1000000) {
      tobuf(buffer, (Int_t)fEND);
      tobuf(buffer, (Int_t)fSeekFree);
      tobuf(buffer, fNbytesFree);
      tobuf(buffer, nfree);
      tobuf(buffer, fNbytesName);
      tobuf(buffer, fUnits);
      tobuf(buffer, fCompress);
      tobuf(buffer, (Int_t)fSeekInfo);
      tobuf(buffer, fNbytesInfo);
   } else {
      tobuf(buffer, fEND);
      tobuf(buffer, fSeekFree);
      tobuf(buffer, fNbytesFree);
      tobuf(buffer, nfree);
      tobuf(buffer, fNbytesName);
      tobuf(buffer, fUnits);
      tobuf(buffer, fCompress);
      tobuf(buffer, fSeekInfo);
      tobuf(buffer, fNbytesInfo);
   }
   fUUID.FillBuffer(buffer);
   Int_t nbytes  = buffer - psave;
   Seek(0);
   WriteBuffer(psave, nbytes);
   Flush();
   delete [] psave;
}

//______________________________________________________________________________
void TFile::MakeProject(const char *dirname, const char * /*classes*/,
                        Option_t *option)
{
// Generate code in directory dirname for all classes specified in argument classes
// If classes = "*" (default), the function generates an include file for each
// class in the StreamerInfo list for which a TClass object does not exist.
// One can restrict the list of classes to be generated by using expressions like:
//   classes = "Ali*" generate code only for classes starting with Ali
//   classes = "myClass" generate code for class MyClass only.
//
// if option = "new" (default) a new directory dirname is created.
//                   If dirname already exist, an error message is printed
//                   and the function returns.
// if option = "recreate", then;
//                   if dirname does not exist, it is created (like in "new")
//                   if dirname already exist, all existing files in dirname
//                   are deleted before creating the new files.
// if option = "update", then new classes are added to the existing directory.
//                   Existing classes with the same name are replaced by the
//                   new definition. If the directory dirname doest not exist,
//                   same effect as "new".
// if, in addition to one of the 3 above options, the option "+" is specified,
// the function will generate:
//   - a script called MAKE to build the shared lib
//   - a LinkDef.h file
//   - rootcint will be run to generate a dirnameProjectDict.cxx file
//   - dirnameProjectDict.cxx will be compiled with the current options in compiledata.h
//   - a shared lib dirname.so will be created.
// if the option "++" is specified, the generated shared lib is dynamically
// linked with the current executable module.
// example:
//  file.MakeProject("demo","*","recreate++");
//  - creates a new directory demo unless it already exist
//  - clear the previous directory content
//  - generate the xxx.h files for all classes xxx found in this file
//    and not yet known to the CINT dictionary.
//  - creates the build script MAKE
//  - creates a LinkDef.h file
//  - runs rootcint generating demoProjectDict.cxx
//  - compiles demoProjectDict.cxx into demoProjectDict.o
//  - generates a shared lib demo.so
//  - dynamically links the shared lib demo.so to the executable
//  If only the option "+" had been specified, one can still link the
//  shared lib to the current executable module with:
//     gSystem->load("demo/demo.so");
//

   TString opt = option;
   opt.ToLower();
   void *dir = gSystem->OpenDirectory(dirname);
   char *path = new char[4000];

   if (opt.Contains("update")) {
      // check that directory exist, if not create it
      if (dir == 0) {
         gSystem->mkdir(dirname);
      }

   } else if (opt.Contains("recreate")) {
      // check that directory exist, if not create it
      if (dir == 0) {
         gSystem->mkdir(dirname);
      }
      // clear directory
      while (dir) {
         const char *afile = gSystem->GetDirEntry(dir);
         if (afile == 0) break;
         if (strcmp(afile,".") == 0) continue;
         if (strcmp(afile,"..") == 0) continue;
         sprintf(path,"%s/%s",dirname,afile);
         gSystem->Unlink(path);
      }

   } else {
      // new is assumed
      // if directory already exist, print error message and return
      if (dir) {
         Error("MakeProject","cannot create directory %s, already existing",dirname);
         delete [] path;
         return;
      }
      gSystem->mkdir(dirname);
   }

   // we are now ready to generate the classes
   // loop on all TStreamerInfo
   TList *list = 0;
   if (fSeekInfo) {
      TKey *key = new TKey();
      char *buffer = new char[fNbytesInfo+1];
      char *buf    = buffer;
      Seek(fSeekInfo);
      ReadBuffer(buf,fNbytesInfo);
      key->ReadBuffer(buf);
      list = (TList*)key->ReadObj();
      delete [] buffer;
      delete key;
   } else {
      list = (TList*)Get("StreamerInfo"); //for versions 2.26 (never released)
   }
   if (list == 0) {
      Error("MakeProject","file %s has no StreamerInfo", GetName());
      delete [] path;
      return;
   }

   // loop on all TStreamerInfo classes
   TStreamerInfo *info;
   TIter next(list);
   Int_t ngener = 0;
   while ((info = (TStreamerInfo*)next())) {
      ngener += info->GenerateHeaderFile(dirname);
   }
   list->Delete();
   delete list;
   printf("MakeProject has generated %d classes in %s\n",ngener,dirname);

   // generate the shared lib
   if (!opt.Contains("+")) { delete [] path; return;}

   // create the MAKE file by looping on all *.h files
   // delete MAKE if it already exists
#ifdef WIN32
   sprintf(path,"%s/make.cmd",dirname);
#else
   sprintf(path,"%s/MAKE",dirname);
#endif
#ifdef R__WINGCC
   FILE *fpMAKE = fopen(path,"wb");
#else
   FILE *fpMAKE = fopen(path,"w");
#endif
   if (!fpMAKE) {
      Error("MakeProject", "cannot open file %s", path);
      delete [] path;
      return;
   }

   // add rootcint statement generating ProjectDict.cxx
   fprintf(fpMAKE,"rootcint -f %sProjectDict.cxx -c %s ",dirname,gSystem->GetIncludePath());

   // create the LinkDef.h file by looping on all *.h files
   // delete LinkDef.h if it already exists
   sprintf(path,"%s/LinkDef.h",dirname);
#ifdef R__WINGCC
   FILE *fp = fopen(path,"wb");
#else
   FILE *fp = fopen(path,"w");
#endif
   if (!fp) {
      Error("MakeProject", "cannot open path file %s", path);
      delete [] path;
      return;
   }
   fprintf(fp,"#ifdef __CINT__\n");
   fprintf(fp,"#pragma link off all globals;\n");
   fprintf(fp,"#pragma link off all classes;\n");
   fprintf(fp,"#pragma link off all functions;\n");
   fprintf(fp,"\n");
   dir = gSystem->OpenDirectory(dirname);
   while (dir) {
      const char *afile = gSystem->GetDirEntry(dir);
      if (afile == 0) break;
      if(strcmp(afile,"LinkDef.h") == 0) continue;
      if(strstr(afile,"ProjectDict.h") != 0) continue;
      strcpy(path,afile);
      char *h = strstr(path,".h");
      if (!h) continue;
      *h = 0;
      fprintf(fp,"#pragma link C++ class %s+;\n",path);
      fprintf(fpMAKE,"%s ",afile);
  }
   fprintf(fp,"#endif\n");
   fclose(fp);
   fprintf(fpMAKE,"LinkDef.h \n");

   // add compilation line
   TString sdirname(dirname);

   TString cmd = gSystem->GetMakeSharedLib();
   cmd.ReplaceAll("$SourceFiles",sdirname+"ProjectDict.cxx");
   cmd.ReplaceAll("$ObjectFiles",sdirname+"ProjectDict."+gSystem->GetObjExt());
   cmd.ReplaceAll("$IncludePath",TString(gSystem->GetIncludePath()) + " -I" + dirname);
   cmd.ReplaceAll("$SharedLib",sdirname+"."+gSystem->GetSoExt());
   cmd.ReplaceAll("$LinkedLibs",gSystem->GetLibraries("","SDL"));
   cmd.ReplaceAll("$LibName",sdirname);
   cmd.ReplaceAll("$BuildDir",".");

   fprintf(fpMAKE,"%s\n",cmd.Data());

   fclose(fpMAKE);
   printf("%s/MAKE file has been generated\n",dirname);

   // now execute the generated script compiling and generating the shared lib
   strcpy(path,gSystem->WorkingDirectory());
   gSystem->ChangeDirectory(dirname);
#ifndef WIN32
   gSystem->Exec("chmod +x MAKE");
#else
   // not really needed for Windows but it would work both both Unix and NT
   chmod("make.cmd",00700);
#endif
   int res = !gSystem->Exec("MAKE");
   gSystem->ChangeDirectory(path);
   sprintf(path,"%s/%s.%s",dirname,dirname,gSystem->GetSoExt());
   if (res) printf("Shared lib %s has been generated\n",path);

   //dynamically link the generated shared lib
   if (opt.Contains("++")) {
      res = !gSystem->Load(path);
      if (res) printf("Shared lib %s has been dynamically linked\n",path);
   }
   delete [] path;
}

//______________________________________________________________________________
void TFile::ReadStreamerInfo()
{
// Read the list of StreamerInfo from this file
// The key with name holding the list of TStreamerInfo objects is read.
// The corresponding TClass objects are updated.

   TList *list = 0;
   if (fSeekInfo > 0 && fSeekInfo < fEND) {
      TKey *key = new TKey();
      char *buffer = new char[fNbytesInfo+1];
      char *buf    = buffer;
      Seek(fSeekInfo);
      ReadBuffer(buf,fNbytesInfo);
      key->ReadBuffer(buf);
      TFile *filesave = gFile;
      gFile = this; // used in ReadObj
      list = (TList*)key->ReadObj();
      if (!list) {
         gDirectory->GetListOfKeys()->Remove(key);
         MakeZombie();
      }
      gFile = filesave;
      delete [] buffer;
      delete key;
   } else {
      list = (TList*)Get("StreamerInfo"); //for versions 2.26 (never released)
   }

   if (list == 0) return;
   if (gDebug > 0) Info("ReadStreamerInfo", "called for file %s",GetName());

   // loop on all TStreamerInfo classes
   TStreamerInfo *info;
   TIter next(list);
   while ((info = (TStreamerInfo*)next())) {
      if (info->IsA() != TStreamerInfo::Class()) {
         Warning("ReadStreamerInfo","%s: not a TStreamerInfo object", GetName());
         continue;
      }
      info->BuildCheck();
      Int_t uid = info->GetNumber();
      Int_t asize = fClassIndex->GetSize();
      if (uid >= asize && uid <100000) fClassIndex->Set(2*asize);
      if (uid >= 0 && uid < fClassIndex->GetSize()) fClassIndex->fArray[uid] = 1;
      else {
         printf("ReadStreamerInfo, class:%s, illegal uid=%d\n",info->GetName(),uid);
      }
      if (gDebug > 0) printf(" -class: %s version: %d info read at slot %d\n",info->GetName(), info->GetClassVersion(),uid);
   }
   fClassIndex->fArray[0] = 0;
   list->Clear();  //this will delete all TStreamerInfo objects with kCanDelete bit set
   delete list;
}

//______________________________________________________________________________
void TFile::ShowStreamerInfo()
{
// Show the StreamerInfo of all classes written to this file.

   TList *list = GetStreamerInfoList();

   if (!list) return;

   list->ls();
   delete list;
}

//______________________________________________________________________________
void TFile::WriteStreamerInfo()
{
//  Write the list of TStreamerInfo as a single object in this file
//  The class Streamer description for all classes written to this file
//  is saved.
//  see class TStreamerInfo

   //if (!gFile) return;
   if (!fWritable) return;
   if (!fClassIndex) return;
   //no need to update the index if no new classes added to the file
   if (fClassIndex->fArray[0] == 0) return;
   if (gDebug > 0) Info("WriteStreamerInfo", "called for file %s",GetName());

   // build a temporary list with the marked files
   TIter next(gROOT->GetListOfStreamerInfo());
   TStreamerInfo *info;
   TList list;

   while ((info = (TStreamerInfo*)next())) {
      Int_t uid = info->GetNumber();
      if (fClassIndex->fArray[uid]) list.Add(info);
      if (gDebug > 0) printf(" -class: %s info number %d saved\n",info->GetName(),uid);
   }
   if (list.GetSize() == 0) return;
   fClassIndex->fArray[0] = 2; //to prevent adding classes in TStreamerInfo::TagFile

   // always write with compression on
   Int_t compress = fCompress;
   fCompress = 1;
   TFile * fileSave = gFile;
   TDirectory *dirSave = gDirectory;
   gFile = this;
   gDirectory = this;

   //free previous StreamerInfo record
   if (fSeekInfo) MakeFree(fSeekInfo,fSeekInfo+fNbytesInfo-1);
   //Create new key
   TKey key(&list,"StreamerInfo",GetBestBuffer());
   fKeys->Remove(&key);
   fSeekInfo   = key.GetSeekKey();
   fNbytesInfo = key.GetNbytes();
   SumBuffer(key.GetObjlen());
   key.WriteFile(0);

   fClassIndex->fArray[0] = 0;
   gFile = fileSave;
   gDirectory = dirSave;
   fCompress = compress;
}

//______________________________________________________________________________
TFile *TFile::Open(const char *name, Option_t *option, const char *ftitle,
                   Int_t compress, Int_t netopt)
{
   // Static member function allowing the creation/opening of either a
   // TFile, TNetFile, TWebFile or any TFile derived class for which an
   // plugin library handler has been registered with the plugin manager
   // (for the plugin manager see the TPluginManager class). The returned
   // type of TFile depends on the file name. If the file starts with
   // "root:", "roots:" or "rootk:" a TNetFile object will be returned,
   // with "http:" a TWebFile, with "file:" a local TFile, etc. (see the
   // list of TFile plugin handlers in $ROOTSYS/etc/system.rootrc for regular
   // expressions that will be checked) and as last a local file will be tried.
   // Before opening a file via TNetFile a check is made to see if the URL
   // specifies a local file. If that is the case the file will be opened
   // via a normal TFile. To force the opening of a local file via a
   // TNetFile use either TNetFile directly or specify as host "localhost".
   // The netopt argument is only used by TNetFile. For the meaning of the
   // options and other arguments see the constructors of the individual
   // file classes. In case of error returns 0.

   TPluginHandler *h;
   TFile *f = 0;

   TRegexp re("^root.*:");
   TString sname = name;
   if (sname.Index(re) != kNPOS) {
      // If the url points to the localhost and the file will be opened in
      // readonly mode and the current user has read access or the specified
      // user is equal to the current user then open local TFile.
      const char *lfname = 0;
      Bool_t localFile = kFALSE;
      TUrl url(name);
      TInetAddress a(gSystem->GetHostByName(url.GetHost()));
      TInetAddress b(gSystem->GetHostByName(gSystem->HostName()));
      if (!strcmp(a.GetHostName(), b.GetHostName())) {
         Bool_t read = kFALSE;
         TString opt = option;
         opt.ToUpper();
         if (opt == "" || opt == "READ") read = kTRUE;
         const char *fname = url.GetFile();
         if (fname[1] == '/' || fname[1] == '~' || fname[1] == '$')
            lfname = &fname[1];
         else
            lfname = Form("%s%s", gSystem->HomeDirectory(), fname);
         if (read) {
            char *fn;
            if ((fn = gSystem->ExpandPathName(lfname))) {
               if (gSystem->AccessPathName(fn, kReadPermission))
                  read = kFALSE;
               delete [] fn;
            }
         }
         Bool_t sameUser = kFALSE;
         UserGroup_t *u = gSystem->GetUserInfo();
         if (u && !strcmp(u->fUser, url.GetUser()))
            sameUser = kTRUE;
         delete u;
         if (read || sameUser)
            localFile = kTRUE;
      }
      if (!localFile) {
         if ((h = gROOT->GetPluginManager()->FindHandler("TFile", name)) &&
             h->LoadPlugin() == 0)
            f = (TFile*) h->ExecPlugin(5, name, option, ftitle, compress, netopt);
         else
            f = new TNetFile(name, option, ftitle, compress, netopt);
      } else {
         f = new TFile(lfname, option, ftitle, compress);
      }
   } else if (!strncmp(name, "http:", 5)) {
      if ((h = gROOT->GetPluginManager()->FindHandler("TFile", name)) &&
          h->LoadPlugin() == 0)
         f = (TFile*) h->ExecPlugin(1, name);
      else
         f = new TWebFile(name);
   } else if (!strncmp(name, "file:", 5)) {
      if ((h = gROOT->GetPluginManager()->FindHandler("TFile", name)) &&
          h->LoadPlugin() == 0)
         f = (TFile*) h->ExecPlugin(4, name+5, option, ftitle, compress);
      else
         f = new TFile(name, option, ftitle, compress);
   } else if ((h = gROOT->GetPluginManager()->FindHandler("TFile", name))) {
      if (h->LoadPlugin() == -1)
         return 0;
      TClass *cl = gROOT->GetClass(h->GetClass());
      if (cl && cl->InheritsFrom("TNetFile"))
         f = (TFile*) h->ExecPlugin(5, name, option, ftitle, compress, netopt);
      else
         f = (TFile*) h->ExecPlugin(4, name, option, ftitle, compress);
   } else
      f = new TFile(name, option, ftitle, compress);

   if (f && f->IsZombie()) {
      delete f;
      f = 0;
   }

   return f;
}

//______________________________________________________________________________
Int_t TFile::SysOpen(const char *pathname, Int_t flags, UInt_t mode)
{
   // Interface to system open. All arguments like in POSIX open().

#if defined(R__WINGCC)
   // ALWAYS use binary mode - even cygwin text should be in unix format
   // although this is posix default it has to be set explicitly
   return ::open(pathname, flags | O_BINARY, mode);
#elif defined(R__SEEK64)
   return ::open64(pathname, flags, mode);
#else
   return ::open(pathname, flags, mode);
#endif
}

//______________________________________________________________________________
Int_t TFile::SysClose(Int_t fd)
{
   // Interface to system close. All arguments like in POSIX close().

   return ::close(fd);
}

//______________________________________________________________________________
Int_t TFile::SysRead(Int_t fd, void *buf, Int_t len)
{
   // Interface to system read. All arguments like in POSIX read().

   return ::read(fd, buf, len);
}

//______________________________________________________________________________
Int_t TFile::SysWrite(Int_t fd, const void *buf, Int_t len)
{
   // Interface to system write. All arguments like in POSIX write().

   return ::write(fd, buf, len);
}

//______________________________________________________________________________
Long64_t TFile::SysSeek(Int_t fd, Long64_t offset, Int_t whence)
{
   // Interface to system lseek. All arguments like in POSIX lseek()
   // except that the offset and return value are of a type which are
   // able to handle 64 bit file systems.

#if defined (R__SEEK64)
   return ::lseek64(fd, offset, whence);
#elif defined(WIN32)
   return ::_lseeki64(fd, offset, whence);
#else
   return ::lseek(fd, offset, whence);
#endif
}

//______________________________________________________________________________
Int_t TFile::SysStat(Int_t, Long_t *id, Long64_t *size, Long_t *flags,
                     Long_t *modtime)
{
   // Return file stat information. The interface and return value is
   // identical to TSystem::GetPathInfo(). The function returns 0 in
   // case of success and 1 if the file could not be stat'ed.

   return gSystem->GetPathInfo(fRealName, id, size, flags, modtime);
}

//______________________________________________________________________________
Int_t TFile::SysSync(Int_t fd)
{
   // Interface to system fsync. All arguments like in POSIX fsync().

   if (TestBit(kDevNull)) return 0;

#ifndef WIN32
   return ::fsync(fd);
#else
   return ::_commit(fd);
#endif
}

//______________________________________________________________________________
Double_t TFile::GetFileBytesRead() { return fgBytesRead; }

//______________________________________________________________________________
Double_t TFile::GetFileBytesWritten() { return fgBytesWrite; }

//______________________________________________________________________________
void TFile::SetFileBytesRead(Double_t bytes){ fgBytesRead = bytes; }

//______________________________________________________________________________
void TFile::SetFileBytesWritten(Double_t bytes){ fgBytesWrite = bytes; }
