// @(#)root/io:$Id$
// Author: Andreas Peters + Fons Rademakers + Rene Brun  26/5/2005

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

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TFileMerger                                                          //
//                                                                      //
// This class provides file copy and merging services.                  //
//                                                                      //
// It can be used to copy files (not only ROOT files), using TFile or   //
// any of its remote file access plugins. It is therefore usefull in    //
// a Grid environment where the files might be accessable via Castor,   //
// rfio, dcap, etc.                                                     //
// The merging interface allows files containing histograms and trees   //
// to be merged, like the standalone hadd program.                      //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include "TFileMerger.h"
#include "TDirectory.h"
#include "TUrl.h"
#include "TFile.h"
#include "TUUID.h"
#include "TSystem.h"
#include "TKey.h"
#include "THashList.h"
#include "TObjString.h"
#include "TClass.h"
#include "TMethodCall.h"
#include "Riostream.h"
#include "TFileMergeInfo.h"
#include "TClassRef.h"
#include "TROOT.h"
#include "TMemFile.h"

#ifdef WIN32
// For _getmaxstdio
#include <stdio.h>
#else
// For getrlimit
#include <sys/time.h>
#include <sys/resource.h>
#endif

ClassImp(TFileMerger)

TClassRef R__TH1_Class("TH1");
TClassRef R__TTree_Class("TTree");

static const Int_t kCpProgress = BIT(14);
static const Int_t kCintFileNumber = 100;
//______________________________________________________________________________
static Int_t R__GetSystemMaxOpenedFiles()
{
   // Return the maximum number of allowed opened files minus some wiggle room
   // for CINT or at least of the standard library (stdio).
   int maxfiles;
#ifdef WIN32
   maxfiles = _getmaxstdio();
#else
   rlimit filelimit;
   if (getrlimit(RLIMIT_NOFILE,&filelimit)==0) {
      maxfiles = filelimit.rlim_cur;
   } else {
      // We could not get the value from getrlimit, let's return a reasonable default.
      maxfiles = 512;
   }
#endif
   if (maxfiles > kCintFileNumber) {
      return maxfiles - kCintFileNumber;
   } else if (maxfiles > 5) {
      return maxfiles - 5;
   } else {
      return maxfiles;
   }
}

//______________________________________________________________________________
TFileMerger::TFileMerger(Bool_t isLocal, Bool_t histoOneGo)
            : fOutputFile(0), fFastMethod(kTRUE), fNoTrees(kFALSE), fExplicitCompLevel(kFALSE), fCompressionChange(kFALSE),
              fPrintLevel(0), fMsgPrefix("TFileMerger"), fMaxOpenedFiles( R__GetSystemMaxOpenedFiles() ),
              fLocal(isLocal), fHistoOneGo(histoOneGo), fObjectNames()
{
   // Create file merger object.

   fFileList = new TList;

   fMergeList = new TList;
   fMergeList->SetOwner(kTRUE);

   fExcessFiles = new TList;
   fExcessFiles->SetOwner(kTRUE);

   gROOT->GetListOfCleanups()->Add(this);
}

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

   gROOT->GetListOfCleanups()->Remove(this);
   SafeDelete(fFileList);
   SafeDelete(fMergeList);
   SafeDelete(fOutputFile);
   SafeDelete(fExcessFiles);
}

//______________________________________________________________________________
void TFileMerger::Reset()
{
   // Reset merger file list.

   fFileList->Clear();
   fMergeList->Clear();
   fExcessFiles->Clear();
   fObjectNames.Clear();
}

//______________________________________________________________________________
Bool_t TFileMerger::AddFile(const char *url, Bool_t cpProgress)
{
   // Add file to file merger.

   if (fPrintLevel > 0) {
      Printf("%s Source file %d: %s",fMsgPrefix.Data(),fFileList->GetEntries()+fExcessFiles->GetEntries()+1,url);
   }

   TFile *newfile = 0;
   TString localcopy;

   if (fFileList->GetEntries() >= (fMaxOpenedFiles-1)) {

      TObjString *urlObj = new TObjString(url);
      fMergeList->Add(urlObj);

      urlObj = new TObjString(url);
      urlObj->SetBit(kCpProgress);
      fExcessFiles->Add(urlObj);
      return kTRUE;
   }

   // We want gDirectory untouched by anything going on here
   TDirectory::TContext ctxt;

   if (fLocal) {
      TUUID uuid;
      localcopy.Form("file:%s/ROOTMERGE-%s.root", gSystem->TempDirectory(), uuid.AsString());
      if (!TFile::Cp(url, localcopy, cpProgress)) {
         Error("AddFile", "cannot get a local copy of file %s", url);
         return kFALSE;
      }
      newfile = TFile::Open(localcopy, "READ");
   } else {
      newfile = TFile::Open(url, "READ");
   }

   if (!newfile) {
      if (fLocal)
         Error("AddFile", "cannot open local copy %s of URL %s",
               localcopy.Data(), url);
      else
         Error("AddFile", "cannot open file %s", url);
      return kFALSE;
   } else {
      if (fOutputFile && fOutputFile->GetCompressionLevel() != newfile->GetCompressionLevel()) fCompressionChange = kTRUE;

      newfile->SetBit(kCanDelete);
      fFileList->Add(newfile);

      TObjString *urlObj = new TObjString(url);
      fMergeList->Add(urlObj);

      return  kTRUE;
   }
}

//______________________________________________________________________________
Bool_t TFileMerger::AddFile(TFile *source, Bool_t cpProgress)
{
   // Add the TFile to this file merger and *do not* give ownership of the TFile to this
   // object.
   //
   // Return kTRUE if the addition was successful.

   return AddFile(source,kFALSE,cpProgress);
}

//______________________________________________________________________________
Bool_t TFileMerger::AddAdoptFile(TFile *source, Bool_t cpProgress)
{
   // Add the TFile to this file merger and give ownership of the TFile to this
   // object (unless kFALSE is returned).
   //
   // Return kTRUE if the addition was successful.

   return AddFile(source,kTRUE,cpProgress);
}

//______________________________________________________________________________
Bool_t TFileMerger::AddFile(TFile *source, Bool_t own, Bool_t cpProgress)
{
   // Add the TFile to this file merger and give ownership of the TFile to this
   // object (unless kFALSE is returned).
   //
   // Return kTRUE if the addition was successful.

   if (source == 0) {
      return kFALSE;
   }

   if (fPrintLevel > 0) {
      Printf("%s Source file %d: %s",fMsgPrefix.Data(),fFileList->GetEntries()+1,source->GetName());
   }

   TFile *newfile = 0;
   TString localcopy;

   // We want gDirectory untouched by anything going on here
   TDirectory::TContext ctxt;
   if (fLocal && !source->InheritsFrom(TMemFile::Class())) {
      TUUID uuid;
      localcopy.Form("file:%s/ROOTMERGE-%s.root", gSystem->TempDirectory(), uuid.AsString());
      if (!source->Cp(localcopy, cpProgress)) {
         Error("AddFile", "cannot get a local copy of file %s", source->GetName());
         return kFALSE;
      }
      newfile = TFile::Open(localcopy, "READ");
   } else {
      newfile = source;
   }

   if (!newfile) {
      if (fLocal)
         Error("AddFile", "cannot open local copy %s of URL %s",
               localcopy.Data(), source->GetName());
      else
         Error("AddFile", "cannot open file %s", source->GetName());
      return kFALSE;
   } else {
      if (fOutputFile && fOutputFile->GetCompressionLevel() != newfile->GetCompressionLevel()) fCompressionChange = kTRUE;

      if (own || newfile != source) {
         newfile->SetBit(kCanDelete);
      } else {
         newfile->ResetBit(kCanDelete);
      }
      fFileList->Add(newfile);

      if (!fMergeList) {
         fMergeList = new TList;
      }
      TObjString *urlObj = new TObjString(source->GetName());
      fMergeList->Add(urlObj);

      if (newfile != source && own) {
         delete source;
      }
      return  kTRUE;
   }
}

//______________________________________________________________________________
Bool_t TFileMerger::OutputFile(const char *outputfile, Bool_t force, Int_t compressionLevel)
{
   // Open merger output file.

   return OutputFile(outputfile,(force?"RECREATE":"CREATE"),compressionLevel);
}

//______________________________________________________________________________
Bool_t TFileMerger::OutputFile(const char *outputfile, Bool_t force)
{
   // Open merger output file.

   Bool_t res = OutputFile(outputfile,(force?"RECREATE":"CREATE"),1); // 1 is the same as the default from the TFile constructor.
   fExplicitCompLevel = kFALSE;
   return res;
}

//______________________________________________________________________________
Bool_t TFileMerger::OutputFile(const char *outputfile, const char *mode, Int_t compressionLevel)
{
   // Open merger output file.  'mode' is passed to the TFile constructor as the option, it should
   // be one of 'NEW','CREATE','RECREATE','UPDATE'
   // 'UPDATE' is usually used in conjunction with IncrementalMerge.

   fExplicitCompLevel = kTRUE;

   TFile *oldfile = fOutputFile;
   fOutputFile = 0; // This avoids the complaint from RecursiveRemove about the file being deleted which is here spurrious. (see RecursiveRemove).
   SafeDelete(oldfile);

   fOutputFilename = outputfile;

   // We want gDirectory untouched by anything going on here
   TDirectory::TContext ctxt;
   if (!(fOutputFile = TFile::Open(outputfile, mode, "", compressionLevel)) || fOutputFile->IsZombie()) {
      Error("OutputFile", "cannot open the MERGER output file %s", fOutputFilename.Data());
      return kFALSE;
   }
   return kTRUE;
}

//______________________________________________________________________________
Bool_t TFileMerger::OutputFile(const char *outputfile, const char *mode /* = "RECREATE" */)
{
   // Open merger output file.  'mode' is passed to the TFile constructor as the option, it should
   // be one of 'NEW','CREATE','RECREATE','UPDATE'
   // 'UPDATE' is usually used in conjunction with IncrementalMerge.

   Bool_t res = OutputFile(outputfile,mode,1); // 1 is the same as the default from the TFile constructor.
   fExplicitCompLevel = kFALSE;
   return res;
}

//______________________________________________________________________________
void TFileMerger::PrintFiles(Option_t *options)
{
   // Print list of files being merged.

   fFileList->Print(options);
   fExcessFiles->Print(options);
}

//______________________________________________________________________________
Bool_t TFileMerger::Merge(Bool_t)
{
   // Merge the files. If no output file was specified it will write into
   // the file "FileMerger.root" in the working directory. Returns true
   // on success, false in case of error.

   return PartialMerge(kAll | kRegular);
}

//______________________________________________________________________________
Bool_t TFileMerger::MergeRecursive(TDirectory *target, TList *sourcelist, Int_t type /* = kRegular | kAll */)
{
   // Merge all objects in a directory
   // The type is defined by the bit values in EPartialMergeType:
   //   kRegular      : normal merge, overwritting the output file (default)
   //   kIncremental  : merge the input file with the (existing) content of the output file (if already exising)
   //   kAll          : merge all type of objects (default)
   //   kResetable    : merge only the objects with a MergeAfterReset member function.
   //   kNonResetable : merge only the objects without a MergeAfterReset member function.
   //   kOnlyListed   : merge only objects listed in fObjectNames
   //   kSkipListed   : skip merging of objects listed in fObjectNames
   //   kKeepCompression : Keep compression level of the buffer as it is in each the original input files.
   Bool_t status = kTRUE;
   Bool_t onlyListed = kFALSE;
   if (fPrintLevel > 0) {
      Printf("%s Target path: %s",fMsgPrefix.Data(),target->GetPath());
   }

   // Get the dir name
   TString path(target->GetPath());
   // coverity[unchecked_value] 'target' is from a file so GetPath always returns path starting with filename:
   path.Remove(0, path.Last(':') + 2);

   Int_t nguess = sourcelist->GetSize()+1000;
   THashList allNames(nguess);
   allNames.SetOwner(kTRUE);
   // If the mode is set to skipping list objects, add names to the allNames list
   if (type & kSkipListed) {
      TObjArray *arr = fObjectNames.Tokenize(" ");
      arr->SetOwner(kFALSE);
      for (Int_t iname=0; iname<arr->GetEntriesFast(); iname++)
         allNames.Add(arr->At(iname));
      delete arr;
   }
   ((THashList*)target->GetList())->Rehash(nguess);
   ((THashList*)target->GetListOfKeys())->Rehash(nguess);

   TFileMergeInfo info(target);

   if (fFastMethod && ((type&kKeepCompression) || !fCompressionChange) ) {
      info.fOptions.Append(" fast");
   }

   TFile      *current_file;
   TDirectory *current_sourcedir;
   if (type & kIncremental) {
      current_file      = 0;
      current_sourcedir = target;
   } else {
      current_file      = (TFile*)sourcelist->First();
      current_sourcedir = current_file->GetDirectory(path);
   }
   while (current_file || current_sourcedir) {
      // When current_sourcedir != 0 and current_file == 0 we are going over the target
      // for an incremental merge.
      if (current_sourcedir && (current_file == 0 || current_sourcedir != target)) {

         // loop over all keys in this directory
         TIter nextkey( current_sourcedir->GetListOfKeys() );
         TKey *key;
         TString oldkeyname;

         while ( (key = (TKey*)nextkey())) {

            // Keep only the highest cycle number for each key for mergeable objects. They are stored
            // in the (hash) list onsecutively and in decreasing order of cycles, so we can continue
            // until the name changes. We flag the case here and we act consequently later.
            Bool_t alreadyseen = (oldkeyname == key->GetName()) ? kTRUE : kFALSE;

            // Read in but do not copy directly the processIds.
            if (strcmp(key->GetClassName(),"TProcessID") == 0) { key->ReadObj(); continue;}

            // If we have already seen this object [name], we already processed
            // the whole list of files for this objects and we can just skip it
            // and any related cycles.
            if (allNames.FindObject(key->GetName())) {
               oldkeyname = key->GetName();
               continue;
            }

            TClass *cl = TClass::GetClass(key->GetClassName());
            if (!cl) {
               Info("MergeRecursive", "cannot indentify object type (%s), name: %s title: %s",
                    key->GetClassName(), key->GetName(), key->GetTitle());
               continue;
            }
            // For mergeable objects we add the names in a local hashlist handling them
            // again (see above)
            if (cl->GetMerge() || cl->InheritsFrom(TDirectory::Class()) ||
               (cl->IsTObject() &&
               (cl->GetMethodWithPrototype("Merge", "TCollection*,TFileMergeInfo*") ||
                cl->GetMethodWithPrototype("Merge", "TCollection*"))))
               allNames.Add(new TObjString(key->GetName()));

            if (fNoTrees && cl->InheritsFrom(R__TTree_Class)) {
               // Skip the TTree objects and any related cycles.
               oldkeyname = key->GetName();
               continue;
            }
            // Check if only the listed objects are to be merged
            if (type & kOnlyListed) {
               onlyListed = kFALSE;
               oldkeyname = key->GetName();
               oldkeyname += " ";
               onlyListed = fObjectNames.Contains(oldkeyname);
               oldkeyname = key->GetName();
               if ((!onlyListed) && (!cl->InheritsFrom(TDirectory::Class()))) continue;
            }

            if (!(type&kResetable && type&kNonResetable)) {
               // If neither or both are requested at the same time, we merger both types.
               if (!(type&kResetable)) {
                  if (cl->GetResetAfterMerge()) {
                     // Skip the object with a reset after merge routine (TTree and other incrementally mergeable objects)
                     oldkeyname = key->GetName();
                     continue;
                  }
               }
               if (!(type&kNonResetable)) {
                  if (!cl->GetResetAfterMerge()) {
                     // Skip the object without a reset after merge routine (Histograms and other non incrementally mergeable objects)
                     oldkeyname = key->GetName();
                     continue;
                  }
               }
            }
            // read object from first source file
            TObject *obj;
            if (type & kIncremental) {
               obj = current_sourcedir->GetList()->FindObject(key->GetName());
               if (!obj) {
                  obj = key->ReadObj();
               }
            } else {
               obj = key->ReadObj();
            }
            if (!obj) {
               Info("MergeRecursive", "could not read object for key {%s, %s}",
                    key->GetName(), key->GetTitle());
               continue;
            }
            if (cl->IsTObject() && cl != obj->IsA()) {
               Error("MergeRecursive", "TKey and object retrieve disagree on type (%s vs %s).  Continuing with %s.",
                    key->GetClassName(), obj->IsA()->GetName(), obj->IsA()->GetName());
               cl = obj->IsA();
            }
            Bool_t canBeMerged = kTRUE;

            if ( cl->InheritsFrom( TDirectory::Class() ) ) {
               // it's a subdirectory

               target->cd();
               TDirectory *newdir;

               // For incremental or already seen we may have already a directory created
               if (type & kIncremental || alreadyseen) {
                  newdir = target->GetDirectory(obj->GetName());
                  if (!newdir) {
                     newdir = target->mkdir( obj->GetName(), obj->GetTitle() );
                  }
               } else {
                  newdir = target->mkdir( obj->GetName(), obj->GetTitle() );
               }

               // newdir is now the starting point of another round of merging
               // newdir still knows its depth within the target file via
               // GetPath(), so we can still figure out where we are in the recursion

               // If this folder is a onlyListed object, merge everything inside.
               if (onlyListed) type &= ~kOnlyListed;
               status = MergeRecursive(newdir, sourcelist, type);
               if (onlyListed) type |= kOnlyListed;
               if (!status) return status;
            } else if (cl->GetMerge()) {

               // Check if already treated
               if (alreadyseen) continue;

               TList inputs;
               Bool_t oneGo = fHistoOneGo && cl->InheritsFrom(R__TH1_Class);

               // Loop over all source files and merge same-name object
               TFile *nextsource = current_file ? (TFile*)sourcelist->After( current_file ) : (TFile*)sourcelist->First();
               if (nextsource == 0) {
                  // There is only one file in the list
                  ROOT::MergeFunc_t func = cl->GetMerge();
                  func(obj, &inputs, &info);
                  info.fIsFirst = kFALSE;
               } else {
                  do {
                     // make sure we are at the correct directory level by cd'ing to path
                     TDirectory *ndir = nextsource->GetDirectory(path);
                     if (ndir) {
                        ndir->cd();
                        TKey *key2 = (TKey*)ndir->GetListOfKeys()->FindObject(key->GetName());
                        if (key2) {
                           TObject *hobj = key2->ReadObj();
                           if (!hobj) {
                              Info("MergeRecursive", "could not read object for key {%s, %s}; skipping file %s",
                                   key->GetName(), key->GetTitle(), nextsource->GetName());
                              nextsource = (TFile*)sourcelist->After(nextsource);
                              continue;
                           }
                           // Set ownership for collections
                           if (hobj->InheritsFrom(TCollection::Class())) {
                              ((TCollection*)hobj)->SetOwner();
                           }
                           hobj->ResetBit(kMustCleanup);
                           inputs.Add(hobj);
                           if (!oneGo) {
                              ROOT::MergeFunc_t func = cl->GetMerge();
                              Long64_t result = func(obj, &inputs, &info);
                              info.fIsFirst = kFALSE;
                              if (result < 0) {
                                 Error("MergeRecursive", "calling Merge() on '%s' with the corresponding object in '%s'",
                                       obj->GetName(), nextsource->GetName());
                              }
                              inputs.Delete();
                           }
                        }
                     }
                     nextsource = (TFile*)sourcelist->After( nextsource );
                  } while (nextsource);
                  // Merge the list, if still to be done
                  if (oneGo || info.fIsFirst) {
                     ROOT::MergeFunc_t func = cl->GetMerge();
                     func(obj, &inputs, &info);
                     info.fIsFirst = kFALSE;
                     inputs.Delete();
                  }
               }
            } else if (cl->IsTObject() &&
                       cl->GetMethodWithPrototype("Merge", "TCollection*,TFileMergeInfo*") ) {
               // Object implements Merge(TCollection*,TFileMergeInfo*) and has a reflex dictionary ...

               // Check if already treated
               if (alreadyseen) continue;

               TList listH;
               TString listHargs;
               listHargs.Form("(TCollection*)0x%lx,(TFileMergeInfo*)0x%lx", (ULong_t)&listH,(ULong_t)&info);

               // Loop over all source files and merge same-name object
               TFile *nextsource = current_file ? (TFile*)sourcelist->After( current_file ) : (TFile*)sourcelist->First();
               if (nextsource == 0) {
                  // There is only one file in the list
                  Int_t error = 0;
                  obj->Execute("Merge", listHargs.Data(), &error);
                  info.fIsFirst = kFALSE;
                  if (error) {
                     Error("MergeRecursive", "calling Merge() on '%s' with the corresponding object in '%s'",
                           obj->GetName(), key->GetName());
                  }
               } else {
                  while (nextsource) {
                     // make sure we are at the correct directory level by cd'ing to path
                     TDirectory *ndir = nextsource->GetDirectory(path);
                     if (ndir) {
                        ndir->cd();
                        TKey *key2 = (TKey*)ndir->GetListOfKeys()->FindObject(key->GetName());
                        if (key2) {
                           TObject *hobj = key2->ReadObj();
                           if (!hobj) {
                              Info("MergeRecursive", "could not read object for key {%s, %s}; skipping file %s",
                                   key->GetName(), key->GetTitle(), nextsource->GetName());
                              nextsource = (TFile*)sourcelist->After(nextsource);
                              continue;
                           }
                           // Set ownership for collections
                           if (hobj->InheritsFrom(TCollection::Class())) {
                              ((TCollection*)hobj)->SetOwner();
                           }
                           hobj->ResetBit(kMustCleanup);
                           listH.Add(hobj);
                           Int_t error = 0;
                           obj->Execute("Merge", listHargs.Data(), &error);
                           info.fIsFirst = kFALSE;
                           if (error) {
                              Error("MergeRecursive", "calling Merge() on '%s' with the corresponding object in '%s'",
                                    obj->GetName(), nextsource->GetName());
                           }
                           listH.Delete();
                        }
                     }
                     nextsource = (TFile*)sourcelist->After( nextsource );
                  }
                  // Merge the list, if still to be done
                  if (info.fIsFirst) {
                     Int_t error = 0;
                     obj->Execute("Merge", listHargs.Data(), &error);
                     info.fIsFirst = kFALSE;
                     listH.Delete();
                  }
               }
            } else if (cl->IsTObject() &&
                       cl->GetMethodWithPrototype("Merge", "TCollection*") ) {
               // Object implements Merge(TCollection*) and has a reflex dictionary ...

               // Check if already treated
               if (alreadyseen) continue;

               TList listH;
               TString listHargs;
               listHargs.Form("((TCollection*)0x%lx)", (ULong_t)&listH);

               // Loop over all source files and merge same-name object
               TFile *nextsource = current_file ? (TFile*)sourcelist->After( current_file ) : (TFile*)sourcelist->First();
               if (nextsource == 0) {
                  // There is only one file in the list
                  Int_t error = 0;
                  obj->Execute("Merge", listHargs.Data(), &error);
                  if (error) {
                     Error("MergeRecursive", "calling Merge() on '%s' with the corresponding object in '%s'",
                           obj->GetName(), key->GetName());
                  }
               } else {
                  while (nextsource) {
                     // make sure we are at the correct directory level by cd'ing to path
                     TDirectory *ndir = nextsource->GetDirectory(path);
                     if (ndir) {
                        ndir->cd();
                        TKey *key2 = (TKey*)ndir->GetListOfKeys()->FindObject(key->GetName());
                        if (key2) {
                           TObject *hobj = key2->ReadObj();
                           if (!hobj) {
                              Info("MergeRecursive", "could not read object for key {%s, %s}; skipping file %s",
                                   key->GetName(), key->GetTitle(), nextsource->GetName());
                              nextsource = (TFile*)sourcelist->After(nextsource);
                              continue;
                           }
                           // Set ownership for collections
                           if (hobj->InheritsFrom(TCollection::Class())) {
                              ((TCollection*)hobj)->SetOwner();
                           }
                           hobj->ResetBit(kMustCleanup);
                           listH.Add(hobj);
                           Int_t error = 0;
                           obj->Execute("Merge", listHargs.Data(), &error);
                           info.fIsFirst = kFALSE;
                           if (error) {
                              Error("MergeRecursive", "calling Merge() on '%s' with the corresponding object in '%s'",
                                    obj->GetName(), nextsource->GetName());
                           }
                           listH.Delete();
                        }
                     }
                     nextsource = (TFile*)sourcelist->After( nextsource );
                  }
                  // Merge the list, if still to be done
                  if (info.fIsFirst) {
                     Int_t error = 0;
                     obj->Execute("Merge", listHargs.Data(), &error);
                     info.fIsFirst = kFALSE;
                     listH.Delete();
                  }
               }
            } else {
               // Object is of no type that we can merge
               canBeMerged = kFALSE;
            }

            // now write the merged histogram (which is "in" obj) to the target file
            // note that this will just store obj in the current directory level,
            // which is not persistent until the complete directory itself is stored
            // by "target->SaveSelf()" below
            target->cd();

            oldkeyname = key->GetName();
            //!!if the object is a tree, it is stored in globChain...
            if(cl->InheritsFrom( TDirectory::Class() )) {
               //printf("cas d'une directory\n");
               // Do not delete the directory if it is part of the output
               // and we are in incremental mode (because it will be reuse
               // and has not been written to disk (for performance reason).
               // coverity[var_deref_model] the IsA()->InheritsFrom guarantees that the dynamic_cast will succeed.
               if (!(type&kIncremental) || dynamic_cast<TDirectory*>(obj)->GetFile() != target) {
                  delete obj;
               }
            } else if (cl->InheritsFrom( TCollection::Class() )) {
               // Don't overwrite, if the object were not merged.
               if ( obj->Write( oldkeyname, canBeMerged ? TObject::kSingleKey | TObject::kOverwrite : TObject::kSingleKey) <= 0 ) {
                  status = kFALSE;
               }
               ((TCollection*)obj)->SetOwner();
               delete obj;
            } else {
               // Don't overwrite, if the object were not merged.
               // NOTE: this is probably wrong for emulated objects.
               if (cl->IsTObject()) {
                  if ( obj->Write( oldkeyname, canBeMerged ? TObject::kOverwrite : 0) <= 0) {
                     status = kFALSE;
                  }
               } else {
                  if ( target->WriteObjectAny( (void*)obj, cl, oldkeyname, canBeMerged ? "OverWrite" : "" ) <= 0) {
                     status = kFALSE;
                  }
               }
               cl->Destructor(obj); // just in case the class is not loaded.
            }
            info.Reset();
         } // while ( ( TKey *key = (TKey*)nextkey() ) )
      }
      current_file = current_file ? (TFile*)sourcelist->After(current_file) : (TFile*)sourcelist->First();
      if (current_file) {
         current_sourcedir = current_file->GetDirectory(path);
      } else {
         current_sourcedir = 0;
      }
   }
   // save modifications to the target directory.
   if (!(type&kIncremental)) {
      // In case of incremental build, we will call Write on the top directory/file, so we do not need
      // to call SaveSelf explicilty.
      target->SaveSelf(kTRUE);
   }
   return status;
}

//______________________________________________________________________________
Bool_t TFileMerger::PartialMerge(Int_t in_type)
{
   // Merge the files. If no output file was specified it will write into
   // the file "FileMerger.root" in the working directory. Returns true
   // on success, false in case of error.
   // The type is defined by the bit values in EPartialMergeType:
   //   kRegular      : normal merge, overwritting the output file
   //   kIncremental  : merge the input file with the content of the output file (if already exising) (default)
   //   kAll          : merge all type of objects (default)
   //   kResetable    : merge only the objects with a MergeAfterReset member function.
   //   kNonResetable : merge only the objects without a MergeAfterReset member function.
   //
   // If the type is set to kIncremental the output file is done deleted at the end of
   // this operation.  If the type is not set to kIncremental, the output file is closed.

   if (!fOutputFile) {
      TString outf(fOutputFilename);
      if (outf.IsNull()) {
         outf.Form("file:%s/FileMerger.root", gSystem->TempDirectory());
         Info("PartialMerge", "will merge the results to the file %s\n"
              "since you didn't specify a merge filename",
              TUrl(outf).GetFile());
      }
      if (!OutputFile(outf.Data())) {
         return kFALSE;
      }
   }

   // Special treament for the single file case ...
   if ((fFileList->GetEntries() == 1) && !fExcessFiles->GetEntries() &&
      !(in_type & kIncremental) && !fCompressionChange && !fExplicitCompLevel) {
      fOutputFile->Close();
      SafeDelete(fOutputFile);

      TFile *file = (TFile *) fFileList->First();
      if (!file || (file && file->IsZombie())) {
         Error("PartialMerge", "one-file case: problem attaching to file");
         return kFALSE;
      }
      Bool_t result = kTRUE;
      if (!(result = file->Cp(fOutputFilename))) {
         Error("PartialMerge", "one-file case: could not copy '%s' to '%s'",
                               file->GetPath(), fOutputFilename.Data());
         return kFALSE;
      }
      if (file->TestBit(kCanDelete)) file->Close();

      // Remove the temporary file
      if (fLocal) {
         TUrl u(file->GetPath(), kTRUE);
         if (gSystem->Unlink(u.GetFile()) != 0)
            Warning("PartialMerge", "problems removing temporary local file '%s'", u.GetFile());
      }
      fFileList->Clear();
      return result;
   }

   fOutputFile->SetBit(kMustCleanup);

   TDirectory::TContext ctxt;

   Bool_t result = kTRUE;
   Int_t type = in_type;
   while (result && fFileList->GetEntries()>0) {
      result = MergeRecursive(fOutputFile, fFileList, type);

      // Remove local copies if there are any
      TIter next(fFileList);
      TFile *file;
      while ((file = (TFile*) next())) {
         // close the files
         if (file->TestBit(kCanDelete)) file->Close();
         // remove the temporary files
         if(fLocal) {
            TString p(file->GetPath());
            // coverity[unchecked_value] Index is return a value with range or NPos to select the whole name.
            p = p(0, p.Index(':',0));
            gSystem->Unlink(p);
         }
      }
      fFileList->Clear();
      if (fExcessFiles->GetEntries() > 0) {
         // We merge the first set of files in the output,
         // we now need to open the next set and make
         // sure we accumulate into the output, so we
         // switch to incremental merging (if not already set)
         type = type | kIncremental;
         OpenExcessFiles();
      }
   }
   if (!result) {
      Error("Merge", "error during merge of your ROOT files");
   } else {
      // Close or write is required so the file is complete.
      if (in_type & kIncremental) {
         fOutputFile->Write("",TObject::kOverwrite);
      } else {
         fOutputFile->Close();
      }
   }

   // Cleanup
   if (in_type & kIncremental) {
      Clear();
   } else {
      fOutputFile->ResetBit(kMustCleanup);
      SafeDelete(fOutputFile);
   }
   return result;
}

//______________________________________________________________________________
Bool_t TFileMerger::OpenExcessFiles()
{
   // Open up to fMaxOpenedFiles of the excess files.

   if (fPrintLevel > 0) {
      Printf("%s Opening the next %d files",fMsgPrefix.Data(),TMath::Min(fExcessFiles->GetEntries(),(fMaxOpenedFiles-1)));
   }
   Int_t nfiles = 0;
   TIter next(fExcessFiles);
   TObjString *url = 0;
   TString localcopy;
   // We want gDirectory untouched by anything going on here
   TDirectory::TContext ctxt;
   while( nfiles < (fMaxOpenedFiles-1) && ( url = (TObjString*)next() ) ) {
      TFile *newfile = 0;
      if (fLocal) {
         TUUID uuid;
         localcopy.Form("file:%s/ROOTMERGE-%s.root", gSystem->TempDirectory(), uuid.AsString());
         if (!TFile::Cp(url->GetName(), localcopy, url->TestBit(kCpProgress))) {
            Error("OpenExcessFiles", "cannot get a local copy of file %s", url->GetName());
            return kFALSE;
         }
         newfile = TFile::Open(localcopy, "READ");
      } else {
         newfile = TFile::Open(url->GetName(), "READ");
      }

      if (!newfile) {
         if (fLocal)
            Error("OpenExcessFiles", "cannot open local copy %s of URL %s",
                  localcopy.Data(), url->GetName());
         else
            Error("OpenExcessFiles", "cannot open file %s", url->GetName());
         return kFALSE;
      } else {
         if (fOutputFile && fOutputFile->GetCompressionLevel() != newfile->GetCompressionLevel()) fCompressionChange = kTRUE;

         newfile->SetBit(kCanDelete);
         fFileList->Add(newfile);
         ++nfiles;
         fExcessFiles->Remove(url);
      }
   }
   return kTRUE;
}

//______________________________________________________________________________
void TFileMerger::RecursiveRemove(TObject *obj)
{
   // Intercept the case where the output TFile is deleted!

   if (obj == fOutputFile) {
      Fatal("RecursiveRemove","Output file of the TFile Merger (targeting %s) has been deleted (likely due to a TTree larger than 100Gb)", fOutputFilename.Data());
   }

}

//______________________________________________________________________________
void TFileMerger::SetMaxOpenedFiles(Int_t newmax)
{
   // Set a limit to the number file that TFileMerger will opened at one time.
   // If the request is higher than the system limit, we reset it to the system limit.
   // If the request is less than two, we reset it to 2 (one for the output file and one for the input file).

   Int_t sysmax = R__GetSystemMaxOpenedFiles();
   if (newmax < sysmax) {
      fMaxOpenedFiles = newmax;
   } else {
      fMaxOpenedFiles = sysmax;
   }
   if (fMaxOpenedFiles < 2) {
      fMaxOpenedFiles = 2;
   }
}

//______________________________________________________________________________
void TFileMerger::SetMsgPrefix(const char *prefix)
{
   // Set the prefix to be used when printing informational message.

   fMsgPrefix = prefix;
}

 TFileMerger.cxx:1
 TFileMerger.cxx:2
 TFileMerger.cxx:3
 TFileMerger.cxx:4
 TFileMerger.cxx:5
 TFileMerger.cxx:6
 TFileMerger.cxx:7
 TFileMerger.cxx:8
 TFileMerger.cxx:9
 TFileMerger.cxx:10
 TFileMerger.cxx:11
 TFileMerger.cxx:12
 TFileMerger.cxx:13
 TFileMerger.cxx:14
 TFileMerger.cxx:15
 TFileMerger.cxx:16
 TFileMerger.cxx:17
 TFileMerger.cxx:18
 TFileMerger.cxx:19
 TFileMerger.cxx:20
 TFileMerger.cxx:21
 TFileMerger.cxx:22
 TFileMerger.cxx:23
 TFileMerger.cxx:24
 TFileMerger.cxx:25
 TFileMerger.cxx:26
 TFileMerger.cxx:27
 TFileMerger.cxx:28
 TFileMerger.cxx:29
 TFileMerger.cxx:30
 TFileMerger.cxx:31
 TFileMerger.cxx:32
 TFileMerger.cxx:33
 TFileMerger.cxx:34
 TFileMerger.cxx:35
 TFileMerger.cxx:36
 TFileMerger.cxx:37
 TFileMerger.cxx:38
 TFileMerger.cxx:39
 TFileMerger.cxx:40
 TFileMerger.cxx:41
 TFileMerger.cxx:42
 TFileMerger.cxx:43
 TFileMerger.cxx:44
 TFileMerger.cxx:45
 TFileMerger.cxx:46
 TFileMerger.cxx:47
 TFileMerger.cxx:48
 TFileMerger.cxx:49
 TFileMerger.cxx:50
 TFileMerger.cxx:51
 TFileMerger.cxx:52
 TFileMerger.cxx:53
 TFileMerger.cxx:54
 TFileMerger.cxx:55
 TFileMerger.cxx:56
 TFileMerger.cxx:57
 TFileMerger.cxx:58
 TFileMerger.cxx:59
 TFileMerger.cxx:60
 TFileMerger.cxx:61
 TFileMerger.cxx:62
 TFileMerger.cxx:63
 TFileMerger.cxx:64
 TFileMerger.cxx:65
 TFileMerger.cxx:66
 TFileMerger.cxx:67
 TFileMerger.cxx:68
 TFileMerger.cxx:69
 TFileMerger.cxx:70
 TFileMerger.cxx:71
 TFileMerger.cxx:72
 TFileMerger.cxx:73
 TFileMerger.cxx:74
 TFileMerger.cxx:75
 TFileMerger.cxx:76
 TFileMerger.cxx:77
 TFileMerger.cxx:78
 TFileMerger.cxx:79
 TFileMerger.cxx:80
 TFileMerger.cxx:81
 TFileMerger.cxx:82
 TFileMerger.cxx:83
 TFileMerger.cxx:84
 TFileMerger.cxx:85
 TFileMerger.cxx:86
 TFileMerger.cxx:87
 TFileMerger.cxx:88
 TFileMerger.cxx:89
 TFileMerger.cxx:90
 TFileMerger.cxx:91
 TFileMerger.cxx:92
 TFileMerger.cxx:93
 TFileMerger.cxx:94
 TFileMerger.cxx:95
 TFileMerger.cxx:96
 TFileMerger.cxx:97
 TFileMerger.cxx:98
 TFileMerger.cxx:99
 TFileMerger.cxx:100
 TFileMerger.cxx:101
 TFileMerger.cxx:102
 TFileMerger.cxx:103
 TFileMerger.cxx:104
 TFileMerger.cxx:105
 TFileMerger.cxx:106
 TFileMerger.cxx:107
 TFileMerger.cxx:108
 TFileMerger.cxx:109
 TFileMerger.cxx:110
 TFileMerger.cxx:111
 TFileMerger.cxx:112
 TFileMerger.cxx:113
 TFileMerger.cxx:114
 TFileMerger.cxx:115
 TFileMerger.cxx:116
 TFileMerger.cxx:117
 TFileMerger.cxx:118
 TFileMerger.cxx:119
 TFileMerger.cxx:120
 TFileMerger.cxx:121
 TFileMerger.cxx:122
 TFileMerger.cxx:123
 TFileMerger.cxx:124
 TFileMerger.cxx:125
 TFileMerger.cxx:126
 TFileMerger.cxx:127
 TFileMerger.cxx:128
 TFileMerger.cxx:129
 TFileMerger.cxx:130
 TFileMerger.cxx:131
 TFileMerger.cxx:132
 TFileMerger.cxx:133
 TFileMerger.cxx:134
 TFileMerger.cxx:135
 TFileMerger.cxx:136
 TFileMerger.cxx:137
 TFileMerger.cxx:138
 TFileMerger.cxx:139
 TFileMerger.cxx:140
 TFileMerger.cxx:141
 TFileMerger.cxx:142
 TFileMerger.cxx:143
 TFileMerger.cxx:144
 TFileMerger.cxx:145
 TFileMerger.cxx:146
 TFileMerger.cxx:147
 TFileMerger.cxx:148
 TFileMerger.cxx:149
 TFileMerger.cxx:150
 TFileMerger.cxx:151
 TFileMerger.cxx:152
 TFileMerger.cxx:153
 TFileMerger.cxx:154
 TFileMerger.cxx:155
 TFileMerger.cxx:156
 TFileMerger.cxx:157
 TFileMerger.cxx:158
 TFileMerger.cxx:159
 TFileMerger.cxx:160
 TFileMerger.cxx:161
 TFileMerger.cxx:162
 TFileMerger.cxx:163
 TFileMerger.cxx:164
 TFileMerger.cxx:165
 TFileMerger.cxx:166
 TFileMerger.cxx:167
 TFileMerger.cxx:168
 TFileMerger.cxx:169
 TFileMerger.cxx:170
 TFileMerger.cxx:171
 TFileMerger.cxx:172
 TFileMerger.cxx:173
 TFileMerger.cxx:174
 TFileMerger.cxx:175
 TFileMerger.cxx:176
 TFileMerger.cxx:177
 TFileMerger.cxx:178
 TFileMerger.cxx:179
 TFileMerger.cxx:180
 TFileMerger.cxx:181
 TFileMerger.cxx:182
 TFileMerger.cxx:183
 TFileMerger.cxx:184
 TFileMerger.cxx:185
 TFileMerger.cxx:186
 TFileMerger.cxx:187
 TFileMerger.cxx:188
 TFileMerger.cxx:189
 TFileMerger.cxx:190
 TFileMerger.cxx:191
 TFileMerger.cxx:192
 TFileMerger.cxx:193
 TFileMerger.cxx:194
 TFileMerger.cxx:195
 TFileMerger.cxx:196
 TFileMerger.cxx:197
 TFileMerger.cxx:198
 TFileMerger.cxx:199
 TFileMerger.cxx:200
 TFileMerger.cxx:201
 TFileMerger.cxx:202
 TFileMerger.cxx:203
 TFileMerger.cxx:204
 TFileMerger.cxx:205
 TFileMerger.cxx:206
 TFileMerger.cxx:207
 TFileMerger.cxx:208
 TFileMerger.cxx:209
 TFileMerger.cxx:210
 TFileMerger.cxx:211
 TFileMerger.cxx:212
 TFileMerger.cxx:213
 TFileMerger.cxx:214
 TFileMerger.cxx:215
 TFileMerger.cxx:216
 TFileMerger.cxx:217
 TFileMerger.cxx:218
 TFileMerger.cxx:219
 TFileMerger.cxx:220
 TFileMerger.cxx:221
 TFileMerger.cxx:222
 TFileMerger.cxx:223
 TFileMerger.cxx:224
 TFileMerger.cxx:225
 TFileMerger.cxx:226
 TFileMerger.cxx:227
 TFileMerger.cxx:228
 TFileMerger.cxx:229
 TFileMerger.cxx:230
 TFileMerger.cxx:231
 TFileMerger.cxx:232
 TFileMerger.cxx:233
 TFileMerger.cxx:234
 TFileMerger.cxx:235
 TFileMerger.cxx:236
 TFileMerger.cxx:237
 TFileMerger.cxx:238
 TFileMerger.cxx:239
 TFileMerger.cxx:240
 TFileMerger.cxx:241
 TFileMerger.cxx:242
 TFileMerger.cxx:243
 TFileMerger.cxx:244
 TFileMerger.cxx:245
 TFileMerger.cxx:246
 TFileMerger.cxx:247
 TFileMerger.cxx:248
 TFileMerger.cxx:249
 TFileMerger.cxx:250
 TFileMerger.cxx:251
 TFileMerger.cxx:252
 TFileMerger.cxx:253
 TFileMerger.cxx:254
 TFileMerger.cxx:255
 TFileMerger.cxx:256
 TFileMerger.cxx:257
 TFileMerger.cxx:258
 TFileMerger.cxx:259
 TFileMerger.cxx:260
 TFileMerger.cxx:261
 TFileMerger.cxx:262
 TFileMerger.cxx:263
 TFileMerger.cxx:264
 TFileMerger.cxx:265
 TFileMerger.cxx:266
 TFileMerger.cxx:267
 TFileMerger.cxx:268
 TFileMerger.cxx:269
 TFileMerger.cxx:270
 TFileMerger.cxx:271
 TFileMerger.cxx:272
 TFileMerger.cxx:273
 TFileMerger.cxx:274
 TFileMerger.cxx:275
 TFileMerger.cxx:276
 TFileMerger.cxx:277
 TFileMerger.cxx:278
 TFileMerger.cxx:279
 TFileMerger.cxx:280
 TFileMerger.cxx:281
 TFileMerger.cxx:282
 TFileMerger.cxx:283
 TFileMerger.cxx:284
 TFileMerger.cxx:285
 TFileMerger.cxx:286
 TFileMerger.cxx:287
 TFileMerger.cxx:288
 TFileMerger.cxx:289
 TFileMerger.cxx:290
 TFileMerger.cxx:291
 TFileMerger.cxx:292
 TFileMerger.cxx:293
 TFileMerger.cxx:294
 TFileMerger.cxx:295
 TFileMerger.cxx:296
 TFileMerger.cxx:297
 TFileMerger.cxx:298
 TFileMerger.cxx:299
 TFileMerger.cxx:300
 TFileMerger.cxx:301
 TFileMerger.cxx:302
 TFileMerger.cxx:303
 TFileMerger.cxx:304
 TFileMerger.cxx:305
 TFileMerger.cxx:306
 TFileMerger.cxx:307
 TFileMerger.cxx:308
 TFileMerger.cxx:309
 TFileMerger.cxx:310
 TFileMerger.cxx:311
 TFileMerger.cxx:312
 TFileMerger.cxx:313
 TFileMerger.cxx:314
 TFileMerger.cxx:315
 TFileMerger.cxx:316
 TFileMerger.cxx:317
 TFileMerger.cxx:318
 TFileMerger.cxx:319
 TFileMerger.cxx:320
 TFileMerger.cxx:321
 TFileMerger.cxx:322
 TFileMerger.cxx:323
 TFileMerger.cxx:324
 TFileMerger.cxx:325
 TFileMerger.cxx:326
 TFileMerger.cxx:327
 TFileMerger.cxx:328
 TFileMerger.cxx:329
 TFileMerger.cxx:330
 TFileMerger.cxx:331
 TFileMerger.cxx:332
 TFileMerger.cxx:333
 TFileMerger.cxx:334
 TFileMerger.cxx:335
 TFileMerger.cxx:336
 TFileMerger.cxx:337
 TFileMerger.cxx:338
 TFileMerger.cxx:339
 TFileMerger.cxx:340
 TFileMerger.cxx:341
 TFileMerger.cxx:342
 TFileMerger.cxx:343
 TFileMerger.cxx:344
 TFileMerger.cxx:345
 TFileMerger.cxx:346
 TFileMerger.cxx:347
 TFileMerger.cxx:348
 TFileMerger.cxx:349
 TFileMerger.cxx:350
 TFileMerger.cxx:351
 TFileMerger.cxx:352
 TFileMerger.cxx:353
 TFileMerger.cxx:354
 TFileMerger.cxx:355
 TFileMerger.cxx:356
 TFileMerger.cxx:357
 TFileMerger.cxx:358
 TFileMerger.cxx:359
 TFileMerger.cxx:360
 TFileMerger.cxx:361
 TFileMerger.cxx:362
 TFileMerger.cxx:363
 TFileMerger.cxx:364
 TFileMerger.cxx:365
 TFileMerger.cxx:366
 TFileMerger.cxx:367
 TFileMerger.cxx:368
 TFileMerger.cxx:369
 TFileMerger.cxx:370
 TFileMerger.cxx:371
 TFileMerger.cxx:372
 TFileMerger.cxx:373
 TFileMerger.cxx:374
 TFileMerger.cxx:375
 TFileMerger.cxx:376
 TFileMerger.cxx:377
 TFileMerger.cxx:378
 TFileMerger.cxx:379
 TFileMerger.cxx:380
 TFileMerger.cxx:381
 TFileMerger.cxx:382
 TFileMerger.cxx:383
 TFileMerger.cxx:384
 TFileMerger.cxx:385
 TFileMerger.cxx:386
 TFileMerger.cxx:387
 TFileMerger.cxx:388
 TFileMerger.cxx:389
 TFileMerger.cxx:390
 TFileMerger.cxx:391
 TFileMerger.cxx:392
 TFileMerger.cxx:393
 TFileMerger.cxx:394
 TFileMerger.cxx:395
 TFileMerger.cxx:396
 TFileMerger.cxx:397
 TFileMerger.cxx:398
 TFileMerger.cxx:399
 TFileMerger.cxx:400
 TFileMerger.cxx:401
 TFileMerger.cxx:402
 TFileMerger.cxx:403
 TFileMerger.cxx:404
 TFileMerger.cxx:405
 TFileMerger.cxx:406
 TFileMerger.cxx:407
 TFileMerger.cxx:408
 TFileMerger.cxx:409
 TFileMerger.cxx:410
 TFileMerger.cxx:411
 TFileMerger.cxx:412
 TFileMerger.cxx:413
 TFileMerger.cxx:414
 TFileMerger.cxx:415
 TFileMerger.cxx:416
 TFileMerger.cxx:417
 TFileMerger.cxx:418
 TFileMerger.cxx:419
 TFileMerger.cxx:420
 TFileMerger.cxx:421
 TFileMerger.cxx:422
 TFileMerger.cxx:423
 TFileMerger.cxx:424
 TFileMerger.cxx:425
 TFileMerger.cxx:426
 TFileMerger.cxx:427
 TFileMerger.cxx:428
 TFileMerger.cxx:429
 TFileMerger.cxx:430
 TFileMerger.cxx:431
 TFileMerger.cxx:432
 TFileMerger.cxx:433
 TFileMerger.cxx:434
 TFileMerger.cxx:435
 TFileMerger.cxx:436
 TFileMerger.cxx:437
 TFileMerger.cxx:438
 TFileMerger.cxx:439
 TFileMerger.cxx:440
 TFileMerger.cxx:441
 TFileMerger.cxx:442
 TFileMerger.cxx:443
 TFileMerger.cxx:444
 TFileMerger.cxx:445
 TFileMerger.cxx:446
 TFileMerger.cxx:447
 TFileMerger.cxx:448
 TFileMerger.cxx:449
 TFileMerger.cxx:450
 TFileMerger.cxx:451
 TFileMerger.cxx:452
 TFileMerger.cxx:453
 TFileMerger.cxx:454
 TFileMerger.cxx:455
 TFileMerger.cxx:456
 TFileMerger.cxx:457
 TFileMerger.cxx:458
 TFileMerger.cxx:459
 TFileMerger.cxx:460
 TFileMerger.cxx:461
 TFileMerger.cxx:462
 TFileMerger.cxx:463
 TFileMerger.cxx:464
 TFileMerger.cxx:465
 TFileMerger.cxx:466
 TFileMerger.cxx:467
 TFileMerger.cxx:468
 TFileMerger.cxx:469
 TFileMerger.cxx:470
 TFileMerger.cxx:471
 TFileMerger.cxx:472
 TFileMerger.cxx:473
 TFileMerger.cxx:474
 TFileMerger.cxx:475
 TFileMerger.cxx:476
 TFileMerger.cxx:477
 TFileMerger.cxx:478
 TFileMerger.cxx:479
 TFileMerger.cxx:480
 TFileMerger.cxx:481
 TFileMerger.cxx:482
 TFileMerger.cxx:483
 TFileMerger.cxx:484
 TFileMerger.cxx:485
 TFileMerger.cxx:486
 TFileMerger.cxx:487
 TFileMerger.cxx:488
 TFileMerger.cxx:489
 TFileMerger.cxx:490
 TFileMerger.cxx:491
 TFileMerger.cxx:492
 TFileMerger.cxx:493
 TFileMerger.cxx:494
 TFileMerger.cxx:495
 TFileMerger.cxx:496
 TFileMerger.cxx:497
 TFileMerger.cxx:498
 TFileMerger.cxx:499
 TFileMerger.cxx:500
 TFileMerger.cxx:501
 TFileMerger.cxx:502
 TFileMerger.cxx:503
 TFileMerger.cxx:504
 TFileMerger.cxx:505
 TFileMerger.cxx:506
 TFileMerger.cxx:507
 TFileMerger.cxx:508
 TFileMerger.cxx:509
 TFileMerger.cxx:510
 TFileMerger.cxx:511
 TFileMerger.cxx:512
 TFileMerger.cxx:513
 TFileMerger.cxx:514
 TFileMerger.cxx:515
 TFileMerger.cxx:516
 TFileMerger.cxx:517
 TFileMerger.cxx:518
 TFileMerger.cxx:519
 TFileMerger.cxx:520
 TFileMerger.cxx:521
 TFileMerger.cxx:522
 TFileMerger.cxx:523
 TFileMerger.cxx:524
 TFileMerger.cxx:525
 TFileMerger.cxx:526
 TFileMerger.cxx:527
 TFileMerger.cxx:528
 TFileMerger.cxx:529
 TFileMerger.cxx:530
 TFileMerger.cxx:531
 TFileMerger.cxx:532
 TFileMerger.cxx:533
 TFileMerger.cxx:534
 TFileMerger.cxx:535
 TFileMerger.cxx:536
 TFileMerger.cxx:537
 TFileMerger.cxx:538
 TFileMerger.cxx:539
 TFileMerger.cxx:540
 TFileMerger.cxx:541
 TFileMerger.cxx:542
 TFileMerger.cxx:543
 TFileMerger.cxx:544
 TFileMerger.cxx:545
 TFileMerger.cxx:546
 TFileMerger.cxx:547
 TFileMerger.cxx:548
 TFileMerger.cxx:549
 TFileMerger.cxx:550
 TFileMerger.cxx:551
 TFileMerger.cxx:552
 TFileMerger.cxx:553
 TFileMerger.cxx:554
 TFileMerger.cxx:555
 TFileMerger.cxx:556
 TFileMerger.cxx:557
 TFileMerger.cxx:558
 TFileMerger.cxx:559
 TFileMerger.cxx:560
 TFileMerger.cxx:561
 TFileMerger.cxx:562
 TFileMerger.cxx:563
 TFileMerger.cxx:564
 TFileMerger.cxx:565
 TFileMerger.cxx:566
 TFileMerger.cxx:567
 TFileMerger.cxx:568
 TFileMerger.cxx:569
 TFileMerger.cxx:570
 TFileMerger.cxx:571
 TFileMerger.cxx:572
 TFileMerger.cxx:573
 TFileMerger.cxx:574
 TFileMerger.cxx:575
 TFileMerger.cxx:576
 TFileMerger.cxx:577
 TFileMerger.cxx:578
 TFileMerger.cxx:579
 TFileMerger.cxx:580
 TFileMerger.cxx:581
 TFileMerger.cxx:582
 TFileMerger.cxx:583
 TFileMerger.cxx:584
 TFileMerger.cxx:585
 TFileMerger.cxx:586
 TFileMerger.cxx:587
 TFileMerger.cxx:588
 TFileMerger.cxx:589
 TFileMerger.cxx:590
 TFileMerger.cxx:591
 TFileMerger.cxx:592
 TFileMerger.cxx:593
 TFileMerger.cxx:594
 TFileMerger.cxx:595
 TFileMerger.cxx:596
 TFileMerger.cxx:597
 TFileMerger.cxx:598
 TFileMerger.cxx:599
 TFileMerger.cxx:600
 TFileMerger.cxx:601
 TFileMerger.cxx:602
 TFileMerger.cxx:603
 TFileMerger.cxx:604
 TFileMerger.cxx:605
 TFileMerger.cxx:606
 TFileMerger.cxx:607
 TFileMerger.cxx:608
 TFileMerger.cxx:609
 TFileMerger.cxx:610
 TFileMerger.cxx:611
 TFileMerger.cxx:612
 TFileMerger.cxx:613
 TFileMerger.cxx:614
 TFileMerger.cxx:615
 TFileMerger.cxx:616
 TFileMerger.cxx:617
 TFileMerger.cxx:618
 TFileMerger.cxx:619
 TFileMerger.cxx:620
 TFileMerger.cxx:621
 TFileMerger.cxx:622
 TFileMerger.cxx:623
 TFileMerger.cxx:624
 TFileMerger.cxx:625
 TFileMerger.cxx:626
 TFileMerger.cxx:627
 TFileMerger.cxx:628
 TFileMerger.cxx:629
 TFileMerger.cxx:630
 TFileMerger.cxx:631
 TFileMerger.cxx:632
 TFileMerger.cxx:633
 TFileMerger.cxx:634
 TFileMerger.cxx:635
 TFileMerger.cxx:636
 TFileMerger.cxx:637
 TFileMerger.cxx:638
 TFileMerger.cxx:639
 TFileMerger.cxx:640
 TFileMerger.cxx:641
 TFileMerger.cxx:642
 TFileMerger.cxx:643
 TFileMerger.cxx:644
 TFileMerger.cxx:645
 TFileMerger.cxx:646
 TFileMerger.cxx:647
 TFileMerger.cxx:648
 TFileMerger.cxx:649
 TFileMerger.cxx:650
 TFileMerger.cxx:651
 TFileMerger.cxx:652
 TFileMerger.cxx:653
 TFileMerger.cxx:654
 TFileMerger.cxx:655
 TFileMerger.cxx:656
 TFileMerger.cxx:657
 TFileMerger.cxx:658
 TFileMerger.cxx:659
 TFileMerger.cxx:660
 TFileMerger.cxx:661
 TFileMerger.cxx:662
 TFileMerger.cxx:663
 TFileMerger.cxx:664
 TFileMerger.cxx:665
 TFileMerger.cxx:666
 TFileMerger.cxx:667
 TFileMerger.cxx:668
 TFileMerger.cxx:669
 TFileMerger.cxx:670
 TFileMerger.cxx:671
 TFileMerger.cxx:672
 TFileMerger.cxx:673
 TFileMerger.cxx:674
 TFileMerger.cxx:675
 TFileMerger.cxx:676
 TFileMerger.cxx:677
 TFileMerger.cxx:678
 TFileMerger.cxx:679
 TFileMerger.cxx:680
 TFileMerger.cxx:681
 TFileMerger.cxx:682
 TFileMerger.cxx:683
 TFileMerger.cxx:684
 TFileMerger.cxx:685
 TFileMerger.cxx:686
 TFileMerger.cxx:687
 TFileMerger.cxx:688
 TFileMerger.cxx:689
 TFileMerger.cxx:690
 TFileMerger.cxx:691
 TFileMerger.cxx:692
 TFileMerger.cxx:693
 TFileMerger.cxx:694
 TFileMerger.cxx:695
 TFileMerger.cxx:696
 TFileMerger.cxx:697
 TFileMerger.cxx:698
 TFileMerger.cxx:699
 TFileMerger.cxx:700
 TFileMerger.cxx:701
 TFileMerger.cxx:702
 TFileMerger.cxx:703
 TFileMerger.cxx:704
 TFileMerger.cxx:705
 TFileMerger.cxx:706
 TFileMerger.cxx:707
 TFileMerger.cxx:708
 TFileMerger.cxx:709
 TFileMerger.cxx:710
 TFileMerger.cxx:711
 TFileMerger.cxx:712
 TFileMerger.cxx:713
 TFileMerger.cxx:714
 TFileMerger.cxx:715
 TFileMerger.cxx:716
 TFileMerger.cxx:717
 TFileMerger.cxx:718
 TFileMerger.cxx:719
 TFileMerger.cxx:720
 TFileMerger.cxx:721
 TFileMerger.cxx:722
 TFileMerger.cxx:723
 TFileMerger.cxx:724
 TFileMerger.cxx:725
 TFileMerger.cxx:726
 TFileMerger.cxx:727
 TFileMerger.cxx:728
 TFileMerger.cxx:729
 TFileMerger.cxx:730
 TFileMerger.cxx:731
 TFileMerger.cxx:732
 TFileMerger.cxx:733
 TFileMerger.cxx:734
 TFileMerger.cxx:735
 TFileMerger.cxx:736
 TFileMerger.cxx:737
 TFileMerger.cxx:738
 TFileMerger.cxx:739
 TFileMerger.cxx:740
 TFileMerger.cxx:741
 TFileMerger.cxx:742
 TFileMerger.cxx:743
 TFileMerger.cxx:744
 TFileMerger.cxx:745
 TFileMerger.cxx:746
 TFileMerger.cxx:747
 TFileMerger.cxx:748
 TFileMerger.cxx:749
 TFileMerger.cxx:750
 TFileMerger.cxx:751
 TFileMerger.cxx:752
 TFileMerger.cxx:753
 TFileMerger.cxx:754
 TFileMerger.cxx:755
 TFileMerger.cxx:756
 TFileMerger.cxx:757
 TFileMerger.cxx:758
 TFileMerger.cxx:759
 TFileMerger.cxx:760
 TFileMerger.cxx:761
 TFileMerger.cxx:762
 TFileMerger.cxx:763
 TFileMerger.cxx:764
 TFileMerger.cxx:765
 TFileMerger.cxx:766
 TFileMerger.cxx:767
 TFileMerger.cxx:768
 TFileMerger.cxx:769
 TFileMerger.cxx:770
 TFileMerger.cxx:771
 TFileMerger.cxx:772
 TFileMerger.cxx:773
 TFileMerger.cxx:774
 TFileMerger.cxx:775
 TFileMerger.cxx:776
 TFileMerger.cxx:777
 TFileMerger.cxx:778
 TFileMerger.cxx:779
 TFileMerger.cxx:780
 TFileMerger.cxx:781
 TFileMerger.cxx:782
 TFileMerger.cxx:783
 TFileMerger.cxx:784
 TFileMerger.cxx:785
 TFileMerger.cxx:786
 TFileMerger.cxx:787
 TFileMerger.cxx:788
 TFileMerger.cxx:789
 TFileMerger.cxx:790
 TFileMerger.cxx:791
 TFileMerger.cxx:792
 TFileMerger.cxx:793
 TFileMerger.cxx:794
 TFileMerger.cxx:795
 TFileMerger.cxx:796
 TFileMerger.cxx:797
 TFileMerger.cxx:798
 TFileMerger.cxx:799
 TFileMerger.cxx:800
 TFileMerger.cxx:801
 TFileMerger.cxx:802
 TFileMerger.cxx:803
 TFileMerger.cxx:804
 TFileMerger.cxx:805
 TFileMerger.cxx:806
 TFileMerger.cxx:807
 TFileMerger.cxx:808
 TFileMerger.cxx:809
 TFileMerger.cxx:810
 TFileMerger.cxx:811
 TFileMerger.cxx:812
 TFileMerger.cxx:813
 TFileMerger.cxx:814
 TFileMerger.cxx:815
 TFileMerger.cxx:816
 TFileMerger.cxx:817
 TFileMerger.cxx:818
 TFileMerger.cxx:819
 TFileMerger.cxx:820
 TFileMerger.cxx:821
 TFileMerger.cxx:822
 TFileMerger.cxx:823
 TFileMerger.cxx:824
 TFileMerger.cxx:825
 TFileMerger.cxx:826
 TFileMerger.cxx:827
 TFileMerger.cxx:828
 TFileMerger.cxx:829
 TFileMerger.cxx:830
 TFileMerger.cxx:831
 TFileMerger.cxx:832
 TFileMerger.cxx:833
 TFileMerger.cxx:834
 TFileMerger.cxx:835
 TFileMerger.cxx:836
 TFileMerger.cxx:837
 TFileMerger.cxx:838
 TFileMerger.cxx:839
 TFileMerger.cxx:840
 TFileMerger.cxx:841
 TFileMerger.cxx:842
 TFileMerger.cxx:843
 TFileMerger.cxx:844
 TFileMerger.cxx:845
 TFileMerger.cxx:846
 TFileMerger.cxx:847
 TFileMerger.cxx:848
 TFileMerger.cxx:849
 TFileMerger.cxx:850
 TFileMerger.cxx:851
 TFileMerger.cxx:852
 TFileMerger.cxx:853
 TFileMerger.cxx:854
 TFileMerger.cxx:855
 TFileMerger.cxx:856
 TFileMerger.cxx:857
 TFileMerger.cxx:858
 TFileMerger.cxx:859
 TFileMerger.cxx:860
 TFileMerger.cxx:861
 TFileMerger.cxx:862
 TFileMerger.cxx:863
 TFileMerger.cxx:864
 TFileMerger.cxx:865
 TFileMerger.cxx:866
 TFileMerger.cxx:867
 TFileMerger.cxx:868
 TFileMerger.cxx:869
 TFileMerger.cxx:870
 TFileMerger.cxx:871
 TFileMerger.cxx:872
 TFileMerger.cxx:873
 TFileMerger.cxx:874
 TFileMerger.cxx:875
 TFileMerger.cxx:876
 TFileMerger.cxx:877
 TFileMerger.cxx:878
 TFileMerger.cxx:879
 TFileMerger.cxx:880
 TFileMerger.cxx:881
 TFileMerger.cxx:882
 TFileMerger.cxx:883
 TFileMerger.cxx:884
 TFileMerger.cxx:885
 TFileMerger.cxx:886
 TFileMerger.cxx:887
 TFileMerger.cxx:888
 TFileMerger.cxx:889
 TFileMerger.cxx:890
 TFileMerger.cxx:891
 TFileMerger.cxx:892
 TFileMerger.cxx:893
 TFileMerger.cxx:894
 TFileMerger.cxx:895
 TFileMerger.cxx:896
 TFileMerger.cxx:897
 TFileMerger.cxx:898
 TFileMerger.cxx:899
 TFileMerger.cxx:900
 TFileMerger.cxx:901
 TFileMerger.cxx:902
 TFileMerger.cxx:903
 TFileMerger.cxx:904
 TFileMerger.cxx:905
 TFileMerger.cxx:906
 TFileMerger.cxx:907
 TFileMerger.cxx:908
 TFileMerger.cxx:909
 TFileMerger.cxx:910
 TFileMerger.cxx:911
 TFileMerger.cxx:912
 TFileMerger.cxx:913
 TFileMerger.cxx:914
 TFileMerger.cxx:915
 TFileMerger.cxx:916
 TFileMerger.cxx:917
 TFileMerger.cxx:918
 TFileMerger.cxx:919
 TFileMerger.cxx:920
 TFileMerger.cxx:921
 TFileMerger.cxx:922
 TFileMerger.cxx:923
 TFileMerger.cxx:924
 TFileMerger.cxx:925
 TFileMerger.cxx:926
 TFileMerger.cxx:927
 TFileMerger.cxx:928
 TFileMerger.cxx:929
 TFileMerger.cxx:930
 TFileMerger.cxx:931
 TFileMerger.cxx:932
 TFileMerger.cxx:933
 TFileMerger.cxx:934
 TFileMerger.cxx:935
 TFileMerger.cxx:936
 TFileMerger.cxx:937
 TFileMerger.cxx:938
 TFileMerger.cxx:939
 TFileMerger.cxx:940
 TFileMerger.cxx:941
 TFileMerger.cxx:942
 TFileMerger.cxx:943
 TFileMerger.cxx:944
 TFileMerger.cxx:945
 TFileMerger.cxx:946
 TFileMerger.cxx:947
 TFileMerger.cxx:948
 TFileMerger.cxx:949
 TFileMerger.cxx:950
 TFileMerger.cxx:951
 TFileMerger.cxx:952
 TFileMerger.cxx:953
 TFileMerger.cxx:954
 TFileMerger.cxx:955
 TFileMerger.cxx:956
 TFileMerger.cxx:957
 TFileMerger.cxx:958