Logo ROOT  
Reference Guide
TFile.cxx
Go to the documentation of this file.
1 // @(#)root/io:$Id: 3a19890259ad6443ee313e090166614971ad4296 $
2 // Author: Rene Brun 28/11/94
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers. *
6  * All rights reserved. *
7  * *
8  * For the licensing terms see $ROOTSYS/LICENSE. *
9  * For the list of contributors see $ROOTSYS/README/CREDITS. *
10  *************************************************************************/
11 
12 /**
13  \defgroup IO Input/Output Library
14 
15  The library collecting the ROOT classes dedicated to data input and output.
16 
17  The detailed internal description of the \ref rootio is available.
18 */
19 
20 /**
21 \file TFile.cxx
22 \class TFile
23 \ingroup IO
24 
25 A ROOT file is a suite of consecutive data records (TKey instances) with
26 a well defined format.
27 
28 If the key is located past the 32 bit file limit (> 2 GB) then some fields will
29 be 8 instead of 4 bytes:
30 
31 Byte Range | Member Name | Description
32 ----------------|-----------|--------------
33 1->4 | Nbytes | Length of compressed object (in bytes)
34 5->6 | Version | TKey version identifier
35 7->10 | ObjLen | Length of uncompressed object
36 11->14 | Datime | Date and time when object was written to file
37 15->16 | KeyLen | Length of the key structure (in bytes)
38 17->18 | Cycle | Cycle of key
39 19->22 [19->26] | SeekKey | Pointer to record itself (consistency check)
40 23->26 [27->34] | SeekPdir | Pointer to directory header
41 27->27 [35->35] | lname | Number of bytes in the class name
42 28->.. [36->..] | ClassName | Object Class Name
43 ..->.. | lname | Number of bytes in the object name
44 ..->.. | Name | lName bytes with the name of the object
45 ..->.. | lTitle | Number of bytes in the object title
46 ..->.. | Title | Title of the object
47 -----> | DATA | Data bytes associated to the object
48 
49 The first data record starts at byte fBEGIN (currently set to kBEGIN).
50 Bytes 1->kBEGIN contain the file description, when fVersion >= 1000000
51 it is a large file (> 2 GB) and the offsets will be 8 bytes long and
52 fUnits will be set to 8:
53 Byte Range | Record Name | Description
54 ----------------|-------------|------------
55 1->4 | "root" | Root file identifier
56 5->8 | fVersion | File format version
57 9->12 | fBEGIN | Pointer to first data record
58 13->16 [13->20] | fEND | Pointer to first free word at the EOF
59 17->20 [21->28] | fSeekFree | Pointer to FREE data record
60 21->24 [29->32] | fNbytesFree | Number of bytes in FREE data record
61 25->28 [33->36] | nfree | Number of free data records
62 29->32 [37->40] | fNbytesName | Number of bytes in TNamed at creation time
63 33->33 [41->41] | fUnits | Number of bytes for file pointers
64 34->37 [42->45] | fCompress | Compression level and algorithm
65 38->41 [46->53] | fSeekInfo | Pointer to TStreamerInfo record
66 42->45 [54->57] | fNbytesInfo | Number of bytes in TStreamerInfo record
67 46->63 [58->75] | fUUID | Universal Unique ID
68 
69 Begin_Macro
70 ../../../tutorials/io/file.C
71 End_Macro
72 The structure of a directory is shown in TDirectoryFile::TDirectoryFile
73 */
74 
75 #include <ROOT/RConfig.hxx>
76 
77 #ifdef R__LINUX
78 // for posix_fadvise
79 #ifndef _XOPEN_SOURCE
80 #define _XOPEN_SOURCE 600
81 #endif
82 #endif
83 #include <fcntl.h>
84 #include <errno.h>
85 #include <sys/stat.h>
86 #ifndef WIN32
87 # include <unistd.h>
88 #else
89 # define ssize_t int
90 # include <io.h>
91 # include <sys/types.h>
92 #endif
93 
94 #include "Bytes.h"
95 #include "Compression.h"
96 #include "RConfigure.h"
97 #include "Strlen.h"
98 #include "strlcpy.h"
99 #include "snprintf.h"
100 #include "TArrayC.h"
101 #include "TBuffer.h"
102 #include "TClass.h"
103 #include "TClassEdit.h"
104 #include "TClassTable.h"
105 #include "TDatime.h"
106 #include "TError.h"
107 #include "TFile.h"
108 #include "TFileCacheRead.h"
109 #include "TFileCacheWrite.h"
110 #include "TFree.h"
111 #include "TInterpreter.h"
112 #include "TKey.h"
113 #include "TMakeProject.h"
114 #include "TPluginManager.h"
115 #include "TProcessUUID.h"
116 #include "TRegexp.h"
117 #include "TPRegexp.h"
118 #include "TROOT.h"
119 #include "TStreamerInfo.h"
120 #include "TStreamerElement.h"
121 #include "TSystem.h"
122 #include "TTimeStamp.h"
123 #include "TVirtualPerfStats.h"
124 #include "TArchiveFile.h"
125 #include "TEnv.h"
126 #include "TVirtualMonitoring.h"
127 #include "TVirtualMutex.h"
128 #include "TMap.h"
129 #include "TMathBase.h"
130 #include "TObjString.h"
131 #include "TStopwatch.h"
132 #include "compiledata.h"
133 #include <cmath>
134 #include <iostream>
135 #include <set>
136 #include "TSchemaRule.h"
137 #include "TSchemaRuleSet.h"
138 #include "TThreadSlots.h"
139 #include "TGlobal.h"
140 #include "ROOT/RMakeUnique.hxx"
142 
143 using std::sqrt;
144 
145 std::atomic<Long64_t> TFile::fgBytesRead{0};
146 std::atomic<Long64_t> TFile::fgBytesWrite{0};
147 std::atomic<Long64_t> TFile::fgFileCounter{0};
148 std::atomic<Int_t> TFile::fgReadCalls{0};
157 #ifdef R__USE_IMT
159 #endif
160 
161 const Int_t kBEGIN = 100;
162 
163 ClassImp(TFile);
164 
165 //*-*x17 macros/layout_file
166 // Needed to add the "fake" global gFile to the list of globals.
167 namespace {
168 static struct AddPseudoGlobals {
169 AddPseudoGlobals() {
170  // User "gCling" as synonym for "libCore static initialization has happened".
171  // This code here must not trigger it.
173 }
174 } gAddPseudoGlobals;
175 }
176 ////////////////////////////////////////////////////////////////////////////////
177 /// File default Constructor.
178 
179 TFile::TFile() : TDirectoryFile(), fCompress(ROOT::RCompressionSetting::EAlgorithm::kUseGlobal)
180 {
181  fCacheReadMap = new TMap();
183 
184  if (gDebug)
185  Info("TFile", "default ctor");
186 }
187 
188 ////////////////////////////////////////////////////////////////////////////////
189 /// Opens or creates a local ROOT file.
190 ///
191 /// \param[in] fname1 The name of the file
192 /// \param[in] option Specifies the mode in which the file is opened
193 /// \param[in] ftitle The title of the file
194 /// \param[in] compress Specifies the compression algorithm and level
195 ///
196 /// It is recommended to specify fname1 as "<file>.root". The suffix ".root"
197 /// will be used by object browsers to automatically identify the file as
198 /// a ROOT file. If the constructor fails in any way IsZombie() will
199 /// return true. Use IsOpen() to check if the file is (still) open.
200 /// To open non-local files use the static TFile::Open() method, that
201 /// will take care of opening the files using the correct remote file
202 /// access plugin.
203 ///
204 /// Option | Description
205 /// -------|------------
206 /// NEW or CREATE | Create a new file and open it for writing, if the file already exists the file is not opened.
207 /// RECREATE | Create a new file, if the file already exists it will be overwritten.
208 /// UPDATE | Open an existing file for writing. If no file exists, it is created.
209 /// READ | Open an existing file for reading (default).
210 /// NET | Used by derived remote file access classes, not a user callable option.
211 /// WEB | Used by derived remote http access class, not a user callable option.
212 ///
213 /// If option = "" (default), READ is assumed.
214 /// The file can be specified as a URL of the form:
215 ///
216 /// file:///user/rdm/bla.root or file:/user/rdm/bla.root
217 ///
218 /// The file can also be a member of an archive, in which case it is
219 /// specified as:
220 ///
221 /// multi.zip#file.root or multi.zip#0
222 ///
223 /// which will open file.root which is a member of the file multi.zip
224 /// archive or member 1 from the archive. For more on archive file
225 /// support see the TArchiveFile class.
226 /// TFile and its remote access plugins can also be used to open any
227 /// file, i.e. also non ROOT files, using:
228 ///
229 /// file.tar?filetype=raw
230 ///
231 /// This is convenient because the many remote file access plugins allow
232 /// easy access to/from the many different mass storage systems.
233 /// The title of the file (ftitle) will be shown by the ROOT browsers.
234 /// A ROOT file (like a Unix file system) may contain objects and
235 /// directories. There are no restrictions for the number of levels
236 /// of directories.
237 /// A ROOT file is designed such that one can write in the file in pure
238 /// sequential mode (case of BATCH jobs). In this case, the file may be
239 /// read sequentially again without using the file index written
240 /// at the end of the file. In case of a job crash, all the information
241 /// on the file is therefore protected.
242 /// A ROOT file can be used interactively. In this case, one has the
243 /// possibility to delete existing objects and add new ones.
244 /// When an object is deleted from the file, the freed space is added
245 /// into the FREE linked list (fFree). The FREE list consists of a chain
246 /// of consecutive free segments on the file. At the same time, the first
247 /// 4 bytes of the freed record on the file are overwritten by GAPSIZE
248 /// where GAPSIZE = -(Number of bytes occupied by the record).
249 /// Option compress is used to specify the compression level and algorithm:
250 ///
251 /// compress = 100 * algorithm + level
252 ///
253 /// Level | Explanation
254 /// ------|-------------
255 /// 0 | objects written to this file will not be compressed.
256 /// 1 | minimal compression level but fast.
257 /// ... | ....
258 /// 9 | maximal compression level but slower and might use more memory.
259 /// (For the currently supported algorithms, the maximum level is 9)
260 /// If compress is negative it indicates the compression level is not set yet.
261 /// The enumeration ROOT::RCompressionSetting::EAlgorithm associates each
262 /// algorithm with a number. There is a utility function to help
263 /// to set the value of compress. For example,
264 /// ROOT::CompressionSettings(ROOT::kLZMA, 1)
265 /// will build an integer which will set the compression to use
266 /// the LZMA algorithm and compression level 1. These are defined
267 /// in the header file <em>Compression.h</em>.
268 /// Note that the compression settings may be changed at any time.
269 /// The new compression settings will only apply to branches created
270 /// or attached after the setting is changed and other objects written
271 /// after the setting is changed.
272 /// In case the file does not exist or is not a valid ROOT file,
273 /// it is made a Zombie. One can detect this situation with a code like:
274 /// ~~~{.cpp}
275 /// TFile f("file.root");
276 /// if (f.IsZombie()) {
277 /// std::cout << "Error opening file" << std::endl;
278 /// exit(-1);
279 /// }
280 /// ~~~
281 /// When opening the file, the system checks the validity of this directory.
282 /// If something wrong is detected, an automatic Recovery is performed. In
283 /// this case, the file is scanned sequentially reading all logical blocks
284 /// and attempting to rebuild a correct directory (see TFile::Recover).
285 /// One can disable the automatic recovery procedure when reading one
286 /// or more files by setting the environment variable "TFile.Recover: 0"
287 /// in the system.rootrc file.
288 ///
289 /// A bit `TFile::kReproducible` can be enabled specifying
290 /// the `"reproducible"` url option when creating the file:
291 /// ~~~{.cpp}
292 /// TFile *f = TFile::Open("name.root?reproducible","RECREATE","File title");
293 /// ~~~
294 /// Unlike regular `TFile`s, the content of such file has reproducible binary
295 /// content when writing exactly same data. This achieved by writing pre-defined
296 /// values for creation and modification date of TKey/TDirectory objects and
297 /// null value for TUUID objects inside TFile. As drawback, TRef objects stored
298 /// in such file cannot be read correctly.
299 ///
300 /// In case the name of the file is not reproducible either (in case of
301 /// creating temporary filenames) a value can be passed to the reproducible
302 /// option to replace the name stored in the file.
303 /// ~~~{.cpp}
304 /// TFile *f = TFile::Open("tmpname.root?reproducible=fixedname","RECREATE","File title");
305 /// ~~~
306 
307 TFile::TFile(const char *fname1, Option_t *option, const char *ftitle, Int_t compress)
308  : TDirectoryFile(), fCompress(compress), fUrl(fname1,kTRUE)
309 {
310  if (!gROOT)
311  ::Fatal("TFile::TFile", "ROOT system not initialized");
312 
313  // store name without the options as name and title
314  TString sfname1 = fname1;
315  if (sfname1.Index("?") != kNPOS) {
316  TString s = sfname1(0, sfname1.Index("?"));
317  SetName(s);
319  } else
320  SetName(fname1);
321 
322  SetTitle(ftitle);
323 
324  // accept also URL like "file:..." syntax
325  fname1 = fUrl.GetFile();
326 
327  // if option contains filetype=raw then go into raw file mode
328  if (strstr(fUrl.GetOptions(), "filetype=raw"))
330 
331  // if option contains filetype=pcm then go into ROOT PCM file mode
332  if (strstr(fUrl.GetOptions(), "filetype=pcm"))
333  fIsPcmFile = kTRUE;
334 
335  if (fUrl.HasOption("reproducible"))
337 
338  // We are opening synchronously
340 
341  BuildDirectoryFile(this, nullptr);
342 
343  fVersion = gROOT->GetVersionInt(); //ROOT version in integer format
344  fUnits = 4;
345  fOption = option;
346  fCacheReadMap = new TMap();
348 
349  fOption.ToUpper();
350 
351  if (fIsRootFile && !fIsPcmFile && fOption != "NEW" && fOption != "CREATE"
352  && fOption != "RECREATE") {
353  // If !gPluginMgr then we are at startup and cannot handle plugins
354  // as TArchiveFile yet.
355  fArchive = gPluginMgr ? TArchiveFile::Open(fUrl.GetUrl(), this) : nullptr;
356  if (fArchive) {
357  fname1 = fArchive->GetArchiveName();
358  // if no archive member is specified then this TFile is just used
359  // to read the archive contents
360  if (!strlen(fArchive->GetMemberName()))
361  fIsArchive = kTRUE;
362  }
363  }
364 
365  if (fOption == "NET")
366  return;
367 
368  if (fOption == "WEB") {
369  fOption = "READ";
370  fWritable = kFALSE;
371  return;
372  }
373 
374  if (fOption == "NEW")
375  fOption = "CREATE";
376 
377  Bool_t create = (fOption == "CREATE") ? kTRUE : kFALSE;
378  Bool_t recreate = (fOption == "RECREATE") ? kTRUE : kFALSE;
379  Bool_t update = (fOption == "UPDATE") ? kTRUE : kFALSE;
380  Bool_t read = (fOption == "READ") ? kTRUE : kFALSE;
381  if (!create && !recreate && !update && !read) {
382  read = kTRUE;
383  fOption = "READ";
384  }
385 
386  Bool_t devnull = kFALSE;
387 
388  if (!fname1 || !fname1[0]) {
389  Error("TFile", "file name is not specified");
390  goto zombie;
391  }
392 
393  // support dumping to /dev/null on UNIX
394  if (!strcmp(fname1, "/dev/null") &&
396  devnull = kTRUE;
397  create = kTRUE;
398  recreate = kFALSE;
399  update = kFALSE;
400  read = kFALSE;
401  fOption = "CREATE";
402  SetBit(kDevNull);
403  }
404 
405  const char *fname;
406  if ((fname = gSystem->ExpandPathName(fname1))) {
407  SetName(fname);
408  delete [] fname;
409  fRealName = GetName();
412  }
413  fname = fRealName.Data();
414  } else {
415  Error("TFile", "error expanding path %s", fname1);
416  goto zombie;
417  }
418 
419  // If the user supplied a value to the option take it as the name to set for
420  // the file instead of the actual filename
421  if (TestBit(kReproducible)) {
422  if(auto name=fUrl.GetValueFromOptions("reproducible")) {
423  SetName(name);
424  }
425  }
426 
427  if (recreate) {
428  if (!gSystem->AccessPathName(fname, kFileExists)) {
429  if (gSystem->Unlink(fname) != 0) {
430  SysError("TFile", "could not delete %s (errno: %d)",
431  fname, gSystem->GetErrno());
432  goto zombie;
433  }
434  }
435  recreate = kFALSE;
436  create = kTRUE;
437  fOption = "CREATE";
438  }
439  if (create && !devnull && !gSystem->AccessPathName(fname, kFileExists)) {
440  Error("TFile", "file %s already exists", fname);
441  goto zombie;
442  }
443  if (update) {
444  if (gSystem->AccessPathName(fname, kFileExists)) {
445  update = kFALSE;
446  create = kTRUE;
447  }
448  if (update && gSystem->AccessPathName(fname, kWritePermission)) {
449  Error("TFile", "no write permission, could not open file %s", fname);
450  goto zombie;
451  }
452  }
453  if (read) {
454  if (gSystem->AccessPathName(fname, kFileExists)) {
455  Error("TFile", "file %s does not exist", fname);
456  goto zombie;
457  }
458  if (gSystem->AccessPathName(fname, kReadPermission)) {
459  Error("TFile", "no read permission, could not open file %s", fname);
460  goto zombie;
461  }
462  }
463 
464  // Connect to file system stream
465  if (create || update) {
466 #ifndef WIN32
467  fD = TFile::SysOpen(fname, O_RDWR | O_CREAT, 0644);
468 #else
469  fD = TFile::SysOpen(fname, O_RDWR | O_CREAT | O_BINARY, S_IREAD | S_IWRITE);
470 #endif
471  if (fD == -1) {
472  SysError("TFile", "file %s can not be opened", fname);
473  goto zombie;
474  }
475  fWritable = kTRUE;
476  } else {
477 #ifndef WIN32
478  fD = TFile::SysOpen(fname, O_RDONLY, 0644);
479 #else
480  fD = TFile::SysOpen(fname, O_RDONLY | O_BINARY, S_IREAD | S_IWRITE);
481 #endif
482  if (fD == -1) {
483  SysError("TFile", "file %s can not be opened for reading", fname);
484  goto zombie;
485  }
486  fWritable = kFALSE;
487  }
488 
489  // calling virtual methods from constructor not a good idea, but it is how code was developed
490  TFile::Init(create); // NOLINT: silence clang-tidy warnings
491 
492  return;
493 
494 zombie:
495  // error in file opening occurred, make this object a zombie
496  {
498  gROOT->GetListOfClosedObjects()->Add(this);
499  }
500  MakeZombie();
501  gDirectory = gROOT;
502 }
503 
504 ////////////////////////////////////////////////////////////////////////////////
505 /// File destructor.
506 
508 {
509  Close(); // NOLINT: silence clang-tidy warnings
510 
511  // In case where the TFile is still open at 'tear-down' time the order of operation will be
512  // call Close("nodelete")
513  // then later call delete TFile
514  // which means that at this point we might still have object held and those
515  // might requires a 'valid' TFile object in their desctructor (for example,
516  // TTree call's GetReadCache which expects a non-null fCacheReadMap).
517  // So delete the objects (if any) now.
518 
519  if (fList)
520  fList->Delete("slow");
521 
527  SafeDelete(fFree);
531 
532  {
534  gROOT->GetListOfClosedObjects()->Remove(this);
535  gROOT->GetUUIDs()->RemoveUUID(GetUniqueID());
536  }
537 
538  if (IsOnHeap()) {
539  // Delete object from CINT symbol table so it can not be used anymore.
540  // CINT object are always on the heap.
541  gInterpreter->ResetGlobalVar(this);
542  }
543 
544  if (gDebug)
545  Info("~TFile", "dtor called for %s [%zx]", GetName(),(size_t)this);
546 }
547 
548 ////////////////////////////////////////////////////////////////////////////////
549 /// Initialize a TFile object.
550 ///
551 /// \param[in] create Create a new file.
552 ///
553 /// TFile implementations providing asynchronous open functionality need to
554 /// override this method to run the appropriate checks before calling this
555 /// standard initialization part. See TXNetFile::Init for an example.
556 
557 void TFile::Init(Bool_t create)
558 {
559  if (fInitDone)
560  // Already called once
561  return;
562  fInitDone = kTRUE;
563 
564  if (!fIsRootFile) {
565  gDirectory = gROOT;
566  return;
567  }
568 
569  if (fArchive) {
570  if (fOption != "READ") {
571  Error("Init", "archive %s can only be opened in read mode", GetName());
572  delete fArchive;
573  fArchive = nullptr;
574  fIsArchive = kFALSE;
575  goto zombie;
576  }
577 
579 
580  if (fIsArchive) return;
581 
582  // Make sure the anchor is in the name
583  if (!fNoAnchorInName)
584  if (!strchr(GetName(),'#'))
586 
587  if (fArchive->SetCurrentMember() != -1)
589  else {
590  Error("Init", "member %s not found in archive %s",
592  delete fArchive;
593  fArchive = nullptr;
594  fIsArchive = kFALSE;
595  goto zombie;
596  }
597  }
598 
599  Int_t nfree;
600  fBEGIN = (Long64_t)kBEGIN; //First used word in file following the file header
601 
602  // make newly opened file the current file and directory
603  cd();
604 
605  if (create) {
606  //*-*---------------NEW file
607  fFree = new TList;
608  fEND = fBEGIN; //Pointer to end of file
609  new TFree(fFree, fBEGIN, Long64_t(kStartBigFile)); //Create new free list
610 
611  //*-* Write Directory info
612  Int_t namelen= TNamed::Sizeof();
613  Int_t nbytes = namelen + TDirectoryFile::Sizeof();
614  TKey *key = new TKey(fName, fTitle, IsA(), nbytes, this);
615  fNbytesName = key->GetKeylen() + namelen;
616  fSeekDir = key->GetSeekKey();
617  fSeekFree = 0;
618  fNbytesFree = 0;
619  WriteHeader();
620  char *buffer = key->GetBuffer();
621  TNamed::FillBuffer(buffer);
623  key->WriteFile();
624  delete key;
625  } else {
626  //*-*----------------UPDATE
627  //char *header = new char[kBEGIN];
628  char *header = new char[kBEGIN+200];
629  Seek(0); // NOLINT: silence clang-tidy warnings
630  //ReadBuffer(header, kBEGIN);
631  if (ReadBuffer(header, kBEGIN+200)) { // NOLINT: silence clang-tidy warnings
632  // ReadBuffer returns kTRUE in case of failure.
633  Error("Init","%s failed to read the file type data.",
634  GetName());
635  delete [] header;
636  goto zombie;
637  }
638 
639  // make sure this is a ROOT file
640  if (strncmp(header, "root", 4)) {
641  Error("Init", "%s not a ROOT file", GetName());
642  delete [] header;
643  goto zombie;
644  }
645 
646  char *buffer = header + 4; // skip the "root" file identifier
647  frombuf(buffer, &fVersion);
648  Int_t headerLength;
649  frombuf(buffer, &headerLength);
650  fBEGIN = (Long64_t)headerLength;
651  if (fVersion < 1000000) { //small file
652  Int_t send,sfree,sinfo;
653  frombuf(buffer, &send); fEND = (Long64_t)send;
654  frombuf(buffer, &sfree); fSeekFree= (Long64_t)sfree;
655  frombuf(buffer, &fNbytesFree);
656  frombuf(buffer, &nfree);
657  frombuf(buffer, &fNbytesName);
658  frombuf(buffer, &fUnits );
659  frombuf(buffer, &fCompress);
660  frombuf(buffer, &sinfo); fSeekInfo = (Long64_t)sinfo;
661  frombuf(buffer, &fNbytesInfo);
662  } else { // new format to support large files
663  frombuf(buffer, &fEND);
664  frombuf(buffer, &fSeekFree);
665  frombuf(buffer, &fNbytesFree);
666  frombuf(buffer, &nfree);
667  frombuf(buffer, &fNbytesName);
668  frombuf(buffer, &fUnits );
669  frombuf(buffer, &fCompress);
670  frombuf(buffer, &fSeekInfo);
671  frombuf(buffer, &fNbytesInfo);
672  }
673  if (fBEGIN < 0 || fBEGIN > fEND) {
674  // humm fBEGIN is wrong ....
675  Error("Init","file %s has an incorrect header length (%lld) or incorrect end of file length (%lld)",
676  GetName(),fBEGIN,fEND);
677  delete [] header;
678  goto zombie;
679  }
680  fSeekDir = fBEGIN;
681  //*-*-------------Read Free segments structure if file is writable
682  if (fWritable) {
683  fFree = new TList;
684  if (fSeekFree > fBEGIN) {
685  ReadFree(); // NOLINT: silence clang-tidy warnings
686  } else {
687  Warning("Init","file %s probably not closed, cannot read free segments",GetName());
688  }
689  }
690  //*-*-------------Read directory info
691  // buffer_keyloc is the start of the key record.
692  char *buffer_keyloc = nullptr;
693 
695  if ( (nbytes + fBEGIN) > fEND) {
696  // humm fBEGIN is wrong ....
697  Error("Init","file %s has an incorrect header length (%lld) or incorrect end of file length (%lld)",
698  GetName(),fBEGIN+nbytes,fEND);
699  delete [] header;
700  goto zombie;
701  }
702  if (nbytes+fBEGIN > kBEGIN+200) {
703  delete [] header;
704  header = new char[nbytes];
705  buffer = header;
706  Seek(fBEGIN); // NOLINT: silence clang-tidy warnings
707  if (ReadBuffer(buffer,nbytes)) { // NOLINT: silence clang-tidy warnings
708  // ReadBuffer returns kTRUE in case of failure.
709  Error("Init","%s failed to read the file header information at %lld (size=%d)",
710  GetName(),fBEGIN,nbytes);
711  delete [] header;
712  goto zombie;
713  }
714  buffer = header+fNbytesName;
715  buffer_keyloc = header;
716  } else {
717  buffer = header+fBEGIN+fNbytesName;
718  buffer_keyloc = header+fBEGIN;
719  }
720  Version_t version,versiondir;
721  frombuf(buffer,&version); versiondir = version%1000;
722  fDatimeC.ReadBuffer(buffer);
723  fDatimeM.ReadBuffer(buffer);
724  frombuf(buffer, &fNbytesKeys);
725  frombuf(buffer, &fNbytesName);
726  if (version > 1000) {
727  frombuf(buffer, &fSeekDir);
728  frombuf(buffer, &fSeekParent);
729  frombuf(buffer, &fSeekKeys);
730  } else {
731  Int_t sdir,sparent,skeys;
732  frombuf(buffer, &sdir); fSeekDir = (Long64_t)sdir;
733  frombuf(buffer, &sparent); fSeekParent = (Long64_t)sparent;
734  frombuf(buffer, &skeys); fSeekKeys = (Long64_t)skeys;
735  }
736  if (versiondir > 1) fUUID.ReadBuffer(buffer);
737 
738  //*-*---------read TKey::FillBuffer info
739  buffer_keyloc += sizeof(Int_t); // Skip NBytes;
740  Version_t keyversion;
741  frombuf(buffer_keyloc, &keyversion);
742  // Skip ObjLen, DateTime, KeyLen, Cycle, SeekKey, SeekPdir
743  if (keyversion > 1000) {
744  // Large files
745  buffer_keyloc += 2*sizeof(Int_t)+2*sizeof(Short_t)+2*sizeof(Long64_t);
746  } else {
747  buffer_keyloc += 2*sizeof(Int_t)+2*sizeof(Short_t)+2*sizeof(Int_t);
748  }
749  TString cname;
750  cname.ReadBuffer(buffer_keyloc);
751  cname.ReadBuffer(buffer_keyloc); // fName.ReadBuffer(buffer); file may have been renamed
752  fTitle.ReadBuffer(buffer_keyloc);
753  delete [] header;
754  if (fNbytesName < 10 || fNbytesName > 10000) {
755  Error("Init","cannot read directory info of file %s", GetName());
756  goto zombie;
757  }
758 
759  //*-* -------------Check if file is truncated
760  Long64_t size;
761  if ((size = GetSize()) == -1) { // NOLINT: silence clang-tidy warnings
762  Error("Init", "cannot stat the file %s", GetName());
763  goto zombie;
764  }
765 
766  //*-* -------------Check if, in case of inconsistencies, we are requested to
767  //*-* -------------attempt recovering the file
768  Bool_t tryrecover = (gEnv->GetValue("TFile.Recover", 1) == 1) ? kTRUE : kFALSE;
769 
770  //*-* -------------Read keys of the top directory
771  if (fSeekKeys > fBEGIN && fEND <= size) {
772  //normal case. Recover only if file has no keys
774  gDirectory = this;
775  if (!GetNkeys()) {
776  if (tryrecover) {
777  Recover(); // NOLINT: silence clang-tidy warnings
778  } else {
779  Error("Init", "file %s has no keys", GetName());
780  goto zombie;
781  }
782  }
783  } else if ((fBEGIN+nbytes == fEND) && (fEND == size)) {
784  //the file might be open by another process and nothing written to the file yet
785  Warning("Init","file %s has no keys", GetName());
786  gDirectory = this;
787  } else {
788  //something had been written to the file. Trailer is missing, must recover
789  if (fEND > size) {
790  if (tryrecover) {
791  Error("Init","file %s is truncated at %lld bytes: should be %lld, "
792  "trying to recover", GetName(), size, fEND);
793  } else {
794  Error("Init","file %s is truncated at %lld bytes: should be %lld",
795  GetName(), size, fEND);
796  goto zombie;
797  }
798  } else {
799  if (tryrecover) {
800  Warning("Init","file %s probably not closed, "
801  "trying to recover", GetName());
802  } else {
803  Warning("Init","file %s probably not closed", GetName());
804  goto zombie;
805  }
806  }
807  Int_t nrecov = Recover(); // NOLINT: silence clang-tidy warnings
808  if (nrecov) {
809  Warning("Init", "successfully recovered %d keys", nrecov);
810  } else {
811  Warning("Init", "no keys recovered, file has been made a Zombie");
812  goto zombie;
813  }
814  }
815  }
816 
817  {
819  gROOT->GetListOfFiles()->Add(this);
820  gROOT->GetUUIDs()->AddUUID(fUUID,this);
821  }
822 
823  // Create StreamerInfo index
824  {
825  Int_t lenIndex = gROOT->GetListOfStreamerInfo()->GetSize()+1;
826  if (lenIndex < 5000) lenIndex = 5000;
827  fClassIndex = new TArrayC(lenIndex);
828  if (fgReadInfo) {
829  if (fSeekInfo > fBEGIN) {
830  ReadStreamerInfo(); // NOLINT: silence clang-tidy warnings
831  if (IsZombie()) {
833  gROOT->GetListOfFiles()->Remove(this);
834  goto zombie;
835  }
836  } else if (fVersion != gROOT->GetVersionInt() && fVersion > 30000) {
837  // Don't complain about missing streamer info for empty files.
838  if (fKeys->GetSize()) {
839  Warning("Init","no StreamerInfo found in %s therefore preventing schema evolution when reading this file."
840  " The file was produced with version %d.%02d/%02d of ROOT.",
841  GetName(), fVersion / 10000, (fVersion / 100) % (100), fVersion % 100);
842  }
843  }
844  }
845  }
846 
847  // Count number of TProcessIDs in this file
848  {
849  TIter next(fKeys);
850  TKey *key;
851  while ((key = (TKey*)next())) {
852  if (!strcmp(key->GetClassName(),"TProcessID")) fNProcessIDs++;
853  }
855  }
856  return;
857 
858 zombie:
859  {
861  gROOT->GetListOfClosedObjects()->Add(this);
862  }
863  // error in file opening occurred, make this object a zombie
864  fWritable = kFALSE;
865  MakeZombie();
866  gDirectory = gROOT;
867 }
868 
869 ////////////////////////////////////////////////////////////////////////////////
870 /// Close a file.
871 ///
872 /// \param[in] option If option == "R", all TProcessIDs referenced by this file are deleted.
873 ///
874 /// Calling TFile::Close("R") might be necessary in case one reads a long list
875 /// of files having TRef, writing some of the referenced objects or TRef
876 /// to a new file. If the TRef or referenced objects of the file being closed
877 /// will not be referenced again, it is possible to minimize the size
878 /// of the TProcessID data structures in memory by forcing a delete of
879 /// the unused TProcessID.
880 
881 void TFile::Close(Option_t *option)
882 {
883  TString opt = option;
884 
885  opt.ToLower();
886 
887  if (!IsOpen()) return;
888 
889  if (fIsArchive || !fIsRootFile) {
890  FlushWriteCache();
891  SysClose(fD);
892  fD = -1;
893 
894  if (gMonitoringWriter)
896 
897  return;
898  }
899 
900  if (IsWritable()) {
902  }
903 
904  // Finish any concurrent I/O operations before we close the file handles.
905  if (fCacheRead) fCacheRead->Close();
906  {
907  TIter iter(fCacheReadMap);
908  TObject *key = nullptr;
909  while ((key = iter()) != nullptr) {
910  TFileCacheRead *cache = dynamic_cast<TFileCacheRead *>(fCacheReadMap->GetValue(key));
911  cache->Close();
912  }
913  }
914 
915  // Delete all supported directories structures from memory
916  // If gDirectory points to this object or any of the nested
917  // TDirectoryFile, TDirectoryFile::Close will induce the proper cd.
918  fMustFlush = kFALSE; // Make sure there is only one Flush.
919  TDirectoryFile::Close(option);
920 
921  if (IsWritable()) {
922  TFree *f1 = (TFree*)fFree->First();
923  if (f1) {
924  WriteFree(); //*-*- Write free segments linked list
925  WriteHeader(); //*-*- Now write file header ; this forces a Flush/fsync
926  } else {
927  Flush();
928  }
929  }
930  fMustFlush = kTRUE;
931 
932  FlushWriteCache();
933 
934  if (gMonitoringWriter)
936 
937  delete fClassIndex;
938  fClassIndex = nullptr;
939 
940  // Delete free segments from free list (but don't delete list header)
941  if (fFree) {
942  fFree->Delete();
943  }
944 
945  if (IsOpen()) {
946  SysClose(fD);
947  fD = -1;
948  }
949 
950  fWritable = kFALSE;
951 
952  // delete the TProcessIDs
953  TList pidDeleted;
954  TIter next(fProcessIDs);
955  TProcessID *pid;
956  while ((pid = (TProcessID*)next())) {
957  if (!pid->DecrementCount()) {
958  if (pid != TProcessID::GetSessionProcessID()) pidDeleted.Add(pid);
959  } else if(opt.Contains("r")) {
960  pid->Clear();
961  }
962  }
963  pidDeleted.Delete();
964 
965  if (!IsZombie()) {
967  gROOT->GetListOfFiles()->Remove(this);
968  gROOT->GetListOfBrowsers()->RecursiveRemove(this);
969  gROOT->GetListOfClosedObjects()->Add(this);
970  } else {
971  // If we are a zombie, we are already in the list of closed objects.
972  }
973 }
974 
975 ////////////////////////////////////////////////////////////////////////////////
976 /// Creates key for object and converts data to buffer.
977 
978 TKey* TFile::CreateKey(TDirectory* mother, const TObject* obj, const char* name, Int_t bufsize)
979 {
980  return new TKey(obj, name, bufsize, mother);
981 }
982 
983 ////////////////////////////////////////////////////////////////////////////////
984 /// Creates key for object and converts data to buffer.
985 
986 TKey* TFile::CreateKey(TDirectory* mother, const void* obj, const TClass* cl, const char* name, Int_t bufsize)
987 {
988  return new TKey(obj, cl, name, bufsize, mother);
989 }
990 
991 ////////////////////////////////////////////////////////////////////////////////
992 /// Return the current ROOT file if any.
993 ///
994 /// Note that if 'cd' has been called on a TDirectory that does not belong to a file,
995 /// gFile will be unchanged and still points to the file of the previous current
996 /// directory that was a file.
997 
999 {
1000  static TFile *currentFile = nullptr;
1001  if (!gThreadTsd)
1002  return currentFile;
1003  else
1004  return *(TFile**)(*gThreadTsd)(&currentFile,ROOT::kFileThreadSlot);
1005 }
1006 
1007 ////////////////////////////////////////////////////////////////////////////////
1008 /// Delete object namecycle.
1009 ///
1010 /// \param[in] namecycle Encodes the name and cycle of the objects to delete
1011 ///
1012 /// Namecycle identifies an object in the top directory of the file namecycle
1013 /// has the format <em>name;cycle</em>.
1014 /// - <em>name = *</em> means all objects
1015 /// - <em>cycle = *</em> means all cycles (memory and keys)
1016 /// - <em>cycle = ""</em> or cycle = 9999 ==> apply to a memory object
1017 /// When name=* use T* to delete subdirectories also
1018 ///
1019 /// Examples:
1020 /// name/cycle | Action
1021 /// -----------|-------
1022 /// foo | delete object named foo in memory
1023 /// foo;1 | delete cycle 1 of foo on file
1024 /// foo;* | delete all cycles of foo on disk and also from memory
1025 /// *;2 | delete all objects on file having the cycle 2
1026 /// *;* | delete all objects from memory and file
1027 /// T*;* | delete all objects from memory and file and all subdirectories
1029 void TFile::Delete(const char *namecycle)
1030 {
1031  if (gDebug)
1032  Info("Delete", "deleting name = %s", namecycle);
1033 
1034  TDirectoryFile::Delete(namecycle);
1035 }
1036 
1037 ////////////////////////////////////////////////////////////////////////////////
1038 /// Fill Graphics Structure and Paint.
1039 ///
1040 /// Loop on all objects (memory or file) and all subdirectories.
1042 void TFile::Draw(Option_t *option)
1043 {
1044  GetList()->R__FOR_EACH(TObject,Draw)(option);
1045 }
1046 
1047 ////////////////////////////////////////////////////////////////////////////////
1048 /// Draw map of objects in this file.
1050 void TFile::DrawMap(const char *keys, Option_t *option)
1051 {
1052  TPluginHandler *h;
1053  if ((h = gROOT->GetPluginManager()->FindHandler("TFileDrawMap"))) {
1054  if (h->LoadPlugin() == -1)
1055  return;
1056  h->ExecPlugin(3, this, keys, option);
1057  }
1058 }
1059 
1060 ////////////////////////////////////////////////////////////////////////////////
1061 /// Synchronize a file's in-memory and on-disk states.
1063 void TFile::Flush()
1064 {
1065  if (IsOpen() && fWritable) {
1066  FlushWriteCache();
1067  if (SysSync(fD) < 0) {
1068  // Write the system error only once for this file
1070  SysError("Flush", "error flushing file %s", GetName());
1071  }
1072  }
1073 }
1074 
1075 ////////////////////////////////////////////////////////////////////////////////
1076 /// Flush the write cache if active.
1077 ///
1078 /// Return kTRUE in case of error
1081 {
1082  if (fCacheWrite && IsOpen() && fWritable)
1083  return fCacheWrite->Flush();
1084  return kFALSE;
1085 }
1086 
1087 ////////////////////////////////////////////////////////////////////////////////
1088 /// Encode file output buffer.
1089 ///
1090 /// The file output buffer contains only the FREE data record.
1092 void TFile::FillBuffer(char *&buffer)
1093 {
1094  Version_t version = TFile::Class_Version();
1095  tobuf(buffer, version);
1096 }
1097 
1098 ////////////////////////////////////////////////////////////////////////////////
1099 /// Return the best buffer size of objects on this file.
1100 ///
1101 /// The best buffer size is estimated based on the current mean value
1102 /// and standard deviation of all objects written so far to this file.
1103 /// Returns mean value + one standard deviation.
1106 {
1107  if (!fWritten) return TBuffer::kInitialSize;
1108  Double_t mean = fSumBuffer/fWritten;
1109  Double_t rms2 = TMath::Abs(fSum2Buffer/fSumBuffer -mean*mean);
1110  Double_t result = mean + sqrt(rms2);
1111  if (result >= (double)std::numeric_limits<Int_t>::max()) {
1112  return std::numeric_limits<Int_t>::max() -1;
1113  } else {
1114  return (Int_t)result;
1115  }
1116 }
1117 
1118 ////////////////////////////////////////////////////////////////////////////////
1119 /// Return the file compression factor.
1120 ///
1121 /// Add total number of compressed/uncompressed bytes for each key.
1122 /// Returns the ratio of the two.
1125 {
1126  Short_t keylen;
1127  UInt_t datime;
1128  Int_t nbytes, objlen, nwh = 64;
1129  char *header = new char[fBEGIN];
1130  char *buffer;
1131  Long64_t idcur = fBEGIN;
1132  Float_t comp,uncomp;
1133  comp = uncomp = fBEGIN;
1134 
1135  while (idcur < fEND-100) {
1136  Seek(idcur);
1137  if (ReadBuffer(header, nwh)) {
1138  // ReadBuffer returns kTRUE in case of failure.
1139 // Error("GetCompressionFactor","%s failed to read the key header information at %lld (size=%d).",
1140 // GetName(),idcur,nwh);
1141  break;
1142  }
1143  buffer=header;
1144  frombuf(buffer, &nbytes);
1145  if (nbytes < 0) {
1146  idcur -= nbytes;
1147  Seek(idcur);
1148  continue;
1149  }
1150  if (nbytes == 0) break; //this may happen when the file is corrupted
1151  Version_t versionkey;
1152  frombuf(buffer, &versionkey);
1153  frombuf(buffer, &objlen);
1154  frombuf(buffer, &datime);
1155  frombuf(buffer, &keylen);
1156  if (!objlen) objlen = nbytes-keylen;
1157  comp += nbytes;
1158  uncomp += keylen + objlen;
1159  idcur += nbytes;
1160  }
1161  delete [] header;
1162  return uncomp/comp;
1163 }
1164 
1165 ////////////////////////////////////////////////////////////////////////////////
1166 /// Method returning errno.
1168 Int_t TFile::GetErrno() const
1169 {
1170  return TSystem::GetErrno();
1171 }
1172 
1173 ////////////////////////////////////////////////////////////////////////////////
1174 /// Method resetting the errno.
1176 void TFile::ResetErrno() const
1177 {
1179 }
1180 
1181 ////////////////////////////////////////////////////////////////////////////////
1182 /// Return a pointer to the current read cache.
1185 {
1186  if (!tree) {
1187  if (!fCacheRead && fCacheReadMap->GetSize() == 1) {
1188  TIter next(fCacheReadMap);
1189  return (TFileCacheRead *)fCacheReadMap->GetValue(next());
1190  }
1191  return fCacheRead;
1192  }
1194  if (!cache) return fCacheRead;
1195  return cache;
1196 }
1197 
1198 ////////////////////////////////////////////////////////////////////////////////
1199 /// Return a pointer to the current write cache.
1202 {
1203  return fCacheWrite;
1204 }
1205 
1206 ////////////////////////////////////////////////////////////////////////////////
1207 /// Read the logical record header starting at a certain postion.
1208 ///
1209 /// \param[in] maxbytes Bytes which are read into buf.
1210 /// \param[out] nbytes Number of bytes in record if negative, this is a deleted
1211 /// record if 0, cannot read record, wrong value of argument first
1212 /// \param[out] objlen Uncompressed object size
1213 /// \param[out] keylen Length of logical record header
1214 ///
1215 /// The function reads nread bytes
1216 /// where nread is the minimum of maxbytes and the number of bytes
1217 /// before the end of file. The function returns nread.
1218 /// Note that the arguments objlen and keylen are returned only
1219 /// if maxbytes >=16
1221 Int_t TFile::GetRecordHeader(char *buf, Long64_t first, Int_t maxbytes, Int_t &nbytes, Int_t &objlen, Int_t &keylen)
1222 {
1223  nbytes = 0;
1224  objlen = 0;
1225  keylen = 0;
1226  if (first < fBEGIN) return 0;
1227  if (first > fEND) return 0;
1228  Seek(first);
1229  Int_t nread = maxbytes;
1230  if (first+maxbytes > fEND) nread = fEND-maxbytes;
1231  if (nread < 4) {
1232  Warning("GetRecordHeader","%s: parameter maxbytes = %d must be >= 4",
1233  GetName(), nread);
1234  return nread;
1235  }
1236  if (ReadBuffer(buf,nread)) {
1237  // ReadBuffer return kTRUE in case of failure.
1238  Warning("GetRecordHeader","%s: failed to read header data (maxbytes = %d)",
1239  GetName(), nread);
1240  return nread;
1241  }
1242  Version_t versionkey;
1243  Short_t klen;
1244  UInt_t datime;
1245  Int_t nb,olen;
1246  char *buffer = buf;
1247  frombuf(buffer,&nb);
1248  nbytes = nb;
1249  if (nb < 0) return nread;
1250  // const Int_t headerSize = Int_t(sizeof(nb) +sizeof(versionkey) +sizeof(olen) +sizeof(datime) +sizeof(klen));
1251  const Int_t headerSize = 16;
1252  if (nread < headerSize) return nread;
1253  frombuf(buffer, &versionkey);
1254  frombuf(buffer, &olen);
1255  frombuf(buffer, &datime);
1256  frombuf(buffer, &klen);
1257  if (!olen) olen = nbytes-klen;
1258  objlen = olen;
1259  keylen = klen;
1260  return nread;
1261 }
1262 
1263 ////////////////////////////////////////////////////////////////////////////////
1264 /// Returns the current file size. Returns -1 in case the file could not
1265 /// be stat'ed.
1267 Long64_t TFile::GetSize() const
1268 {
1269  Long64_t size;
1270 
1271  if (fArchive && fArchive->GetMember()) {
1273  } else {
1274  Long_t id, flags, modtime;
1275  if (const_cast<TFile*>(this)->SysStat(fD, &id, &size, &flags, &modtime)) { // NOLINT: silence clang-tidy warnings
1276  Error("GetSize", "cannot stat the file %s", GetName());
1277  return -1;
1278  }
1279  }
1280  return size;
1281 }
1282 
1283 ////////////////////////////////////////////////////////////////////////////////
1284 /// Returns the cached list of StreamerInfos used in this file.
1287 {
1289 }
1290 
1291 ////////////////////////////////////////////////////////////////////////////////
1292 /// See documentation of GetStreamerInfoList for more details.
1293 /// This is an internal method which returns the list of streamer infos and also
1294 /// information about the success of the operation.
1297 {
1299 
1300  if (fIsPcmFile) return {nullptr, 1, hash}; // No schema evolution for ROOT PCM files.
1301 
1302  TList *list = nullptr;
1303  if (fSeekInfo) {
1304  TDirectory::TContext ctxt(this); // gFile and gDirectory used in ReadObj
1305  auto key = std::make_unique<TKey>(this);
1306  std::vector<char> buffer(fNbytesInfo+1);
1307  auto buf = buffer.data();
1308  Seek(fSeekInfo); // NOLINT: silence clang-tidy warnings
1309  if (ReadBuffer(buf,fNbytesInfo)) { // NOLINT: silence clang-tidy warnings
1310  // ReadBuffer returns kTRUE in case of failure.
1311  Warning("GetRecordHeader","%s: failed to read the StreamerInfo data from disk.",
1312  GetName());
1313  return {nullptr, 1, hash};
1314  }
1315 
1316 #ifdef R__USE_IMT
1317  if (lookupSICache) {
1318  hash = fgTsSIHashes.Hash(buf, fNbytesInfo);
1319  if (fgTsSIHashes.Find(hash)) {
1320  if (gDebug > 0) Info("GetStreamerInfo", "The streamer info record for file %s has already been treated, skipping it.", GetName());
1321  return {nullptr, 0, hash};
1322  }
1323  }
1324 #else
1325  (void) lookupSICache;
1326 #endif
1327  key->ReadKeyBuffer(buf);
1328  list = dynamic_cast<TList*>(key->ReadObjWithBuffer(buffer.data()));
1329  if (list) list->SetOwner();
1330  } else {
1331  list = (TList*)Get("StreamerInfo"); //for versions 2.26 (never released)
1332  }
1333 
1334  if (!list) {
1335  Info("GetStreamerInfoList", "cannot find the StreamerInfo record in file %s",
1336  GetName());
1337  return {nullptr, 1, hash};
1338  }
1339 
1340  return {list, 0, hash};
1341 }
1342 
1343 ////////////////////////////////////////////////////////////////////////////////
1344 /// Read the list of TStreamerInfo objects written to this file.
1345 ///
1346 /// The function returns a TList. It is the user's responsibility
1347 /// to delete the list created by this function.
1348 ///
1349 /// Note the list, in addition to TStreamerInfo object, contains sometimes
1350 /// a TList named 'listOfRules' and containing the schema evolution rules
1351 /// related to the file's content.
1352 ///
1353 /// Using the list, one can access additional information, e.g.:
1354 /// ~~~{.cpp}
1355 /// TFile f("myfile.root");
1356 /// auto list = f.GetStreamerInfoList();
1357 /// auto info = dynamic_cast<TStreamerInfo*>(list->FindObject("MyClass"));
1358 /// if (info) auto classversionid = info->GetClassVersion();
1359 /// delete list;
1360 /// ~~~
1361 ///
1364 {
1365  return GetStreamerInfoListImpl(/*lookupSICache*/ false).fList;
1366 }
1367 
1368 ////////////////////////////////////////////////////////////////////////////////
1369 /// List file contents.
1370 ///
1371 /// Indentation is used to identify the file tree.
1372 /// Subdirectories are listed first, then objects in memory,
1373 /// then objects on the file.
1375 void TFile::ls(Option_t *option) const
1376 {
1378  std::cout <<ClassName()<<"**\t\t"<<GetName()<<"\t"<<GetTitle()<<std::endl;
1380  TDirectoryFile::ls(option);
1382 }
1383 
1384 ////////////////////////////////////////////////////////////////////////////////
1385 /// Returns kTRUE in case file is open and kFALSE if file is not open.
1387 Bool_t TFile::IsOpen() const
1388 {
1389  return fD == -1 ? kFALSE : kTRUE;
1390 }
1391 
1392 ////////////////////////////////////////////////////////////////////////////////
1393 /// Mark unused bytes on the file.
1394 ///
1395 /// The list of free segments is in the fFree linked list.
1396 /// When an object is deleted from the file, the freed space is added
1397 /// into the FREE linked list (fFree). The FREE list consists of a chain
1398 /// of consecutive free segments on the file. At the same time, the first
1399 /// 4 bytes of the freed record on the file are overwritten by GAPSIZE
1400 /// where GAPSIZE = -(Number of bytes occupied by the record).
1403 {
1404  TFree *f1 = (TFree*)fFree->First();
1405  if (!f1) return;
1406  TFree *newfree = f1->AddFree(fFree,first,last);
1407  if(!newfree) return;
1408  Long64_t nfirst = newfree->GetFirst();
1409  Long64_t nlast = newfree->GetLast();
1410  Long64_t nbytesl= nlast-nfirst+1;
1411  if (nbytesl > 2000000000) nbytesl = 2000000000;
1412  Int_t nbytes = -Int_t (nbytesl);
1413  Int_t nb = sizeof(Int_t);
1414  char * buffer = new char[nb];
1415  char * psave = buffer;
1416  tobuf(buffer, nbytes);
1417  if (last == fEND-1) fEND = nfirst;
1418  Seek(nfirst);
1419  // We could not update the meta data for this block on the file.
1420  // This is not fatal as this only means that we won't get it 'right'
1421  // if we ever need to Recover the file before the block is actually
1422  // (attempted to be reused.
1423  // coverity[unchecked_value]
1424  WriteBuffer(psave, nb);
1425  if (fMustFlush) Flush();
1426  delete [] psave;
1427 }
1428 
1429 ////////////////////////////////////////////////////////////////////////////////
1430 /// List the contents of a file sequentially.
1431 /// For each logical record found, it prints:
1432 ///
1433 /// Date/Time Record_Adress Logical_Record_Length ClassName CompressionFactor
1434 ///
1435 /// Example of output
1436 ///
1437 /// 20010404/150437 At:64 N=150 TFile
1438 /// 20010404/150440 At:214 N=28326 TBasket CX = 1.13
1439 /// 20010404/150440 At:28540 N=29616 TBasket CX = 1.08
1440 /// 20010404/150440 At:58156 N=29640 TBasket CX = 1.08
1441 /// 20010404/150440 At:87796 N=29076 TBasket CX = 1.10
1442 /// 20010404/150440 At:116872 N=10151 TBasket CX = 3.15
1443 /// 20010404/150441 At:127023 N=28341 TBasket CX = 1.13
1444 /// 20010404/150441 At:155364 N=29594 TBasket CX = 1.08
1445 /// 20010404/150441 At:184958 N=29616 TBasket CX = 1.08
1446 /// 20010404/150441 At:214574 N=29075 TBasket CX = 1.10
1447 /// 20010404/150441 At:243649 N=9583 TBasket CX = 3.34
1448 /// 20010404/150442 At:253232 N=28324 TBasket CX = 1.13
1449 /// 20010404/150442 At:281556 N=29641 TBasket CX = 1.08
1450 /// 20010404/150442 At:311197 N=29633 TBasket CX = 1.08
1451 /// 20010404/150442 At:340830 N=29091 TBasket CX = 1.10
1452 /// 20010404/150442 At:369921 N=10341 TBasket CX = 3.09
1453 /// 20010404/150442 At:380262 N=509 TH1F CX = 1.93
1454 /// 20010404/150442 At:380771 N=1769 TH2F CX = 4.32
1455 /// 20010404/150442 At:382540 N=1849 TProfile CX = 1.65
1456 /// 20010404/150442 At:384389 N=18434 TNtuple CX = 4.51
1457 /// 20010404/150442 At:402823 N=307 KeysList
1458 /// 20010404/150443 At:403130 N=4548 StreamerInfo CX = 3.65
1459 /// 20010404/150443 At:407678 N=86 FreeSegments
1460 /// 20010404/150443 At:407764 N=1 END
1461 ///
1462 /// If the parameter opt contains "forComp", the Date/Time is ommitted
1463 /// and the decompressed size is also printed.
1464 ///
1465 /// Record_Adress Logical_Record_Length Key_Length Object_Record_Length ClassName CompressionFactor
1466 ///
1467 /// If the parameter opt contains "extended", the name and title of the keys are added:
1468 /// 20200820/155031 At:100 N=180 TFile name: hsimple.root title: Demo ROOT file with histograms
1469 /// 220200820/155032 At:280 N=28880 TBasket CX = 1.11 name: random title: ntuple
1470 /// 220200820/155032 At:29160 N=29761 TBasket CX = 1.08 name: px title: ntuple
1471 /// 220200820/155032 At:58921 N=29725 TBasket CX = 1.08 name: py title: ntuple
1472 /// 220200820/155032 At:88646 N=29209 TBasket CX = 1.10 name: pz title: ntuple
1473 /// 220200820/155032 At:117855 N=10197 TBasket CX = 3.14 name: i title: ntuple
1474 /// ...
1475 /// 20200820/155032 At:405110 N=808 TNtuple CX = 3.53 name: ntuple title: Demo ntuple
1476 /// 20200820/155706 At:405918 N=307 KeysList name: hsimple.root title: Demo ROOT file with histograms
1477 /// 20200820/155032 At:406225 N=8556 StreamerInfo CX = 3.42 name: StreamerInfo title: Doubly linked list
1478 /// 20200820/155708 At:414781 N=86 FreeSegments name: hsimple.root title: Demo ROOT file with histograms
1479 /// 20200820/155708 At:414867 N=1 END
1480 ///
1481 /// Note: The combined size of the classname, name and title is truncated to 476 characters (a little more for regular keys of small files)
1482 ///
1483 
1485 void TFile::Map(Option_t *opt)
1486 {
1487  TString options(opt);
1488  options.ToLower();
1489  bool forComp = options.Contains("forcomp");
1490  bool extended = options.Contains("extended");
1491 
1492  Short_t keylen,cycle;
1493  UInt_t datime;
1494  Int_t nbytes,date,time,objlen;
1495  date = 0;
1496  time = 0;
1497  Long64_t seekkey,seekpdir;
1498  char *buffer;
1499  char nwhc;
1500  Long64_t idcur = fBEGIN;
1501 
1502  constexpr Int_t nwheader = 512;
1503 
1504  char header[nwheader];
1505  char classname[512];
1506  char keyname[512];
1507  char keytitle[512];
1508  TString extrainfo;
1509 
1510  unsigned char nDigits = std::log10(fEND) + 1;
1511 
1512  while (idcur < fEND) {
1513  Seek(idcur);
1514  Int_t nread = nwheader;
1515  if (idcur+nread >= fEND) nread = fEND-idcur-1;
1516  if (ReadBuffer(header, nread)) {
1517  // ReadBuffer returns kTRUE in case of failure.
1518  Warning("Map","%s: failed to read the key data from disk at %lld.",
1519  GetName(),idcur);
1520  break;
1521  }
1522 
1523  buffer=header;
1524  frombuf(buffer, &nbytes);
1525  if (!nbytes) {
1526  Printf("Address = %lld\tNbytes = %d\t=====E R R O R=======", idcur, nbytes);
1527  date = 0; time = 0;
1528  break;
1529  }
1530  if (nbytes < 0) {
1531  Printf("Address = %lld\tNbytes = %d\t=====G A P===========", idcur, nbytes);
1532  idcur -= nbytes;
1533  Seek(idcur);
1534  continue;
1535  }
1536  Version_t versionkey;
1537  frombuf(buffer, &versionkey);
1538  frombuf(buffer, &objlen);
1539  frombuf(buffer, &datime);
1540  frombuf(buffer, &keylen);
1541  frombuf(buffer, &cycle);
1542  if (versionkey > 1000) {
1543  frombuf(buffer, &seekkey);
1544  frombuf(buffer, &seekpdir);
1545  } else {
1546  Int_t skey,sdir;
1547  frombuf(buffer, &skey); seekkey = (Long64_t)skey;
1548  frombuf(buffer, &sdir); seekpdir = (Long64_t)sdir;
1549  }
1550  frombuf(buffer, &nwhc);
1551  if ( ((buffer-header) + nwhc) > nwheader ) // Don't read past the end of the part of the key we have read.
1552  nwhc = nwheader - (buffer-header);
1553  for (int i = 0;i < nwhc; i++) frombuf(buffer, &classname[i]);
1554  classname[(int)nwhc] = '\0'; //cast to avoid warning with gcc3.4
1555  if (idcur == fSeekFree) strlcpy(classname,"FreeSegments",512);
1556  if (idcur == fSeekInfo) strlcpy(classname,"StreamerInfo",512);
1557  if (idcur == fSeekKeys) strlcpy(classname,"KeysList",512);
1558 
1559  if (extended) {
1560  if ( (buffer-header) >= nwheader )
1561  nwhc = 0;
1562  else {
1563  frombuf(buffer, &nwhc);
1564  if (nwhc < 0)
1565  nwhc = 0;
1566  else if ( ((buffer-header) + nwhc) > nwheader ) // Don't read past the end of the part of the key we have read.
1567  nwhc = nwheader - (buffer-header);
1568  }
1569  for (int i = 0;i < nwhc; i++) frombuf(buffer, &keyname[i]);
1570  keyname[(int)nwhc] = '\0'; //cast to avoid warning with gcc3.4
1571 
1572  if ( (buffer-header) >= nwheader )
1573  nwhc = 0;
1574  else {
1575  frombuf(buffer, &nwhc);
1576  if (nwhc < 0)
1577  nwhc = 0;
1578  else if ( ((buffer-header) + nwhc) > nwheader ) // Don't read past the end of the part of the key we have read.
1579  nwhc = nwheader - (buffer-header);
1580  }
1581  for (int i = 0;i < nwhc; i++) frombuf(buffer, &keytitle[i]);
1582  keytitle[(int)nwhc] = '\0'; //cast to avoid warning with gcc3.4
1583 
1584  extrainfo.Form(" name: %-16s title: %s", keyname, keytitle);
1585  }
1586 
1587  TDatime::GetDateTime(datime, date, time);
1588  if (!forComp) {
1589  if (objlen != nbytes - keylen) {
1590  Float_t cx = Float_t(objlen + keylen) / Float_t(nbytes);
1591  Printf("%d/%06d At:%-*lld N=%-8d %-14s CX = %5.2f %s", date, time, nDigits + 1, idcur, nbytes, classname,
1592  cx, extrainfo.Data());
1593  } else {
1594  Printf("%d/%06d At:%-*lld N=%-8d %-14s %s", date, time, nDigits + 1, idcur, nbytes, classname, extrainfo.Data());
1595  }
1596  } else {
1597  // Printing to help compare two files.
1598  if (objlen != nbytes - keylen) {
1599  Float_t cx = Float_t(objlen + keylen) / Float_t(nbytes);
1600  Printf("At:%-*lld N=%-8d K=%-3d O=%-8d %-14s CX = %5.2f %s", nDigits+1, idcur, nbytes, keylen, objlen, classname, cx, extrainfo.Data());
1601  } else {
1602  Printf("At:%-*lld N=%-8d K=%-3d O=%-8d %-14s CX = 1 %s", nDigits+1, idcur, nbytes, keylen, objlen, classname, extrainfo.Data());
1603  }
1604  }
1605  idcur += nbytes;
1606  }
1607  if (!forComp)
1608  Printf("%d/%06d At:%-*lld N=%-8d %-14s",date,time, nDigits+1, idcur,1,"END");
1609  else
1610  Printf("At:%-*lld N=%-8d K= O= %-14s", nDigits+1, idcur,1,"END");
1611 }
1612 
1613 ////////////////////////////////////////////////////////////////////////////////
1614 /// Paint all objects in the file.
1616 void TFile::Paint(Option_t *option)
1617 {
1618  GetList()->R__FOR_EACH(TObject,Paint)(option);
1619 }
1620 
1621 ////////////////////////////////////////////////////////////////////////////////
1622 /// Print all objects in the file.
1624 void TFile::Print(Option_t *option) const
1625 {
1626  Printf("TFile: name=%s, title=%s, option=%s", GetName(), GetTitle(), GetOption());
1627  GetList()->R__FOR_EACH(TObject,Print)(option);
1628 }
1629 
1630 ////////////////////////////////////////////////////////////////////////////////
1631 /// Read a buffer from the file at the offset 'pos' in the file.
1632 ///
1633 /// Returns kTRUE in case of failure.
1634 /// Compared to ReadBuffer(char*, Int_t), this routine does _not_
1635 /// change the cursor on the physical file representation (fD)
1636 /// if the data is in this TFile's cache.
1638 Bool_t TFile::ReadBuffer(char *buf, Long64_t pos, Int_t len)
1639 {
1640  if (IsOpen()) {
1641 
1642  SetOffset(pos);
1643 
1644  Int_t st;
1645  Double_t start = 0;
1646  if (gPerfStats) start = TTimeStamp();
1647 
1648  if ((st = ReadBufferViaCache(buf, len))) {
1649  if (st == 2)
1650  return kTRUE;
1651  return kFALSE;
1652  }
1653 
1654  Seek(pos);
1655  ssize_t siz;
1656 
1657  while ((siz = SysRead(fD, buf, len)) < 0 && GetErrno() == EINTR)
1658  ResetErrno();
1659 
1660  if (siz < 0) {
1661  SysError("ReadBuffer", "error reading from file %s", GetName());
1662  return kTRUE;
1663  }
1664  if (siz != len) {
1665  Error("ReadBuffer", "error reading all requested bytes from file %s, got %ld of %d",
1666  GetName(), (Long_t)siz, len);
1667  return kTRUE;
1668  }
1669  fBytesRead += siz;
1670  fgBytesRead += siz;
1671  fReadCalls++;
1672  fgReadCalls++;
1673 
1674  if (gMonitoringWriter)
1676  if (gPerfStats) {
1677  gPerfStats->FileReadEvent(this, len, start);
1678  }
1679  return kFALSE;
1680  }
1681  return kTRUE;
1682 }
1683 
1684 ////////////////////////////////////////////////////////////////////////////////
1685 /// Read a buffer from the file. This is the basic low level read operation.
1686 /// Returns kTRUE in case of failure.
1688 Bool_t TFile::ReadBuffer(char *buf, Int_t len)
1689 {
1690  if (IsOpen()) {
1691 
1692  Int_t st;
1693  if ((st = ReadBufferViaCache(buf, len))) {
1694  if (st == 2)
1695  return kTRUE;
1696  return kFALSE;
1697  }
1698 
1699  ssize_t siz;
1700  Double_t start = 0;
1701 
1702  if (gPerfStats) start = TTimeStamp();
1703 
1704  while ((siz = SysRead(fD, buf, len)) < 0 && GetErrno() == EINTR)
1705  ResetErrno();
1706 
1707  if (siz < 0) {
1708  SysError("ReadBuffer", "error reading from file %s", GetName());
1709  return kTRUE;
1710  }
1711  if (siz != len) {
1712  Error("ReadBuffer", "error reading all requested bytes from file %s, got %ld of %d",
1713  GetName(), (Long_t)siz, len);
1714  return kTRUE;
1715  }
1716  fBytesRead += siz;
1717  fgBytesRead += siz;
1718  fReadCalls++;
1719  fgReadCalls++;
1720 
1721  if (gMonitoringWriter)
1723  if (gPerfStats) {
1724  gPerfStats->FileReadEvent(this, len, start);
1725  }
1726  return kFALSE;
1727  }
1728  return kTRUE;
1729 }
1730 
1731 ////////////////////////////////////////////////////////////////////////////////
1732 /// Read the nbuf blocks described in arrays pos and len.
1733 ///
1734 /// The value pos[i] is the seek position of block i of length len[i].
1735 /// Note that for nbuf=1, this call is equivalent to TFile::ReafBuffer.
1736 /// This function is overloaded by TNetFile, TWebFile, etc.
1737 /// Returns kTRUE in case of failure.
1739 Bool_t TFile::ReadBuffers(char *buf, Long64_t *pos, Int_t *len, Int_t nbuf)
1740 {
1741  // called with buf=0, from TFileCacheRead to pass list of readahead buffers
1742  if (!buf) {
1743  for (Int_t j = 0; j < nbuf; j++) {
1744  if (ReadBufferAsync(pos[j], len[j])) {
1745  return kTRUE;
1746  }
1747  }
1748  return kFALSE;
1749  }
1750 
1751  Int_t k = 0;
1752  Bool_t result = kTRUE;
1753  TFileCacheRead *old = fCacheRead;
1754  fCacheRead = nullptr;
1755  Long64_t curbegin = pos[0];
1756  Long64_t cur;
1757  char *buf2 = nullptr;
1758  Int_t i = 0, n = 0;
1759  while (i < nbuf) {
1760  cur = pos[i]+len[i];
1761  Bool_t bigRead = kTRUE;
1762  if (cur -curbegin < fgReadaheadSize) {n++; i++; bigRead = kFALSE;}
1763  if (bigRead || (i>=nbuf)) {
1764  if (n == 0) {
1765  //if the block to read is about the same size as the read-ahead buffer
1766  //we read the block directly
1767  Seek(pos[i]);
1768  result = ReadBuffer(&buf[k], len[i]);
1769  if (result) break;
1770  k += len[i];
1771  i++;
1772  } else {
1773  //otherwise we read all blocks that fit in the read-ahead buffer
1774  Seek(curbegin);
1775  if (!buf2) buf2 = new char[fgReadaheadSize];
1776  //we read ahead
1777  Long64_t nahead = pos[i-1]+len[i-1]-curbegin;
1778  result = ReadBuffer(buf2, nahead);
1779  if (result) break;
1780  //now copy from the read-ahead buffer to the cache
1781  Int_t kold = k;
1782  for (Int_t j=0;j<n;j++) {
1783  memcpy(&buf[k],&buf2[pos[i-n+j]-curbegin],len[i-n+j]);
1784  k += len[i-n+j];
1785  }
1786  Int_t nok = k-kold;
1787  Long64_t extra = nahead-nok;
1788  fBytesReadExtra += extra;
1789  fBytesRead -= extra;
1790  fgBytesRead -= extra;
1791  n = 0;
1792  }
1793  curbegin = i < nbuf ? pos[i] : 0;
1794  }
1795  }
1796  if (buf2) delete [] buf2;
1797  fCacheRead = old;
1798  return result;
1799 }
1800 
1801 ////////////////////////////////////////////////////////////////////////////////
1802 /// Read buffer via cache.
1803 ///
1804 /// Returns 0 if the requested block is not in the cache, 1 in case read via
1805 /// cache was successful, 2 in case read via cache failed.
1807 Int_t TFile::ReadBufferViaCache(char *buf, Int_t len)
1808 {
1809  Long64_t off = GetRelOffset();
1810  if (fCacheRead) {
1811  Int_t st = fCacheRead->ReadBuffer(buf, off, len);
1812  if (st < 0)
1813  return 2; // failure reading
1814  else if (st == 1) {
1815  // fOffset might have been changed via TFileCacheRead::ReadBuffer(), reset it
1816  SetOffset(off + len);
1817  return 1;
1818  }
1819  // fOffset might have been changed via TFileCacheRead::ReadBuffer(), reset it
1820  Seek(off);
1821  } else {
1822  // if write cache is active check if data still in write cache
1823  if (fWritable && fCacheWrite) {
1824  if (fCacheWrite->ReadBuffer(buf, off, len) == 0) {
1825  SetOffset(off + len);
1826  return 1;
1827  }
1828  // fOffset might have been changed via TFileCacheWrite::ReadBuffer(), reset it
1829  SetOffset(off);
1830  }
1831  }
1832 
1833  return 0;
1834 }
1835 
1836 ////////////////////////////////////////////////////////////////////////////////
1837 /// Read the FREE linked list.
1838 ///
1839 /// Every file has a linked list (fFree) of free segments.
1840 /// This linked list has been written on the file via WriteFree
1841 /// as a single data record.
1843 void TFile::ReadFree()
1844 {
1845  // Avoid problem with file corruption.
1846  if (fNbytesFree < 0 || fNbytesFree > fEND) {
1847  fNbytesFree = 0;
1848  return;
1849  }
1850  TKey *headerfree = new TKey(fSeekFree, fNbytesFree, this);
1851  headerfree->ReadFile();
1852  char *buffer = headerfree->GetBuffer();
1853  headerfree->ReadKeyBuffer(buffer);
1854  buffer = headerfree->GetBuffer();
1855  while (1) {
1856  TFree *afree = new TFree();
1857  afree->ReadBuffer(buffer);
1858  fFree->Add(afree);
1859  if (afree->GetLast() > fEND) break;
1860  }
1861  delete headerfree;
1862 }
1863 
1864 ////////////////////////////////////////////////////////////////////////////////
1865 /// The TProcessID with number pidf is read from this file.
1866 ///
1867 /// If the object is not already entered in the gROOT list, it is added.
1870 {
1871  TProcessID *pid = nullptr;
1872  TObjArray *pids = GetListOfProcessIDs();
1873  if (pidf < pids->GetSize()) pid = (TProcessID *)pids->UncheckedAt(pidf);
1874  if (pid) {
1875  pid->CheckInit();
1876  return pid;
1877  }
1878 
1879  //check if fProcessIDs[uid] is set in file
1880  //if not set, read the process uid from file
1881  char pidname[32];
1882  snprintf(pidname,32,"ProcessID%d",pidf);
1883  pid = (TProcessID *)Get(pidname);
1884  if (gDebug > 0) {
1885  printf("ReadProcessID, name=%s, file=%s, pid=%zx\n",pidname,GetName(),(size_t)pid);
1886  }
1887  if (!pid) {
1888  //file->Error("ReadProcessID","Cannot find %s in file %s",pidname,file->GetName());
1889  return pid;
1890  }
1891 
1892  //check that a similar pid is not already registered in fgPIDs
1893  TObjArray *pidslist = TProcessID::GetPIDs();
1894  TIter next(pidslist);
1895  TProcessID *p;
1896  bool found = false;
1897 
1898  {
1900  while ((p = (TProcessID*)next())) {
1901  if (!strcmp(p->GetTitle(),pid->GetTitle())) {
1902  found = true;
1903  break;
1904  }
1905  }
1906  }
1907 
1908  if (found) {
1909  delete pid;
1910  pids->AddAtAndExpand(p,pidf);
1911  p->IncrementCount();
1912  return p;
1913  }
1914 
1915  pids->AddAtAndExpand(pid,pidf);
1916  pid->IncrementCount();
1917 
1918  {
1920  pidslist->Add(pid);
1921  Int_t ind = pidslist->IndexOf(pid);
1922  pid->SetUniqueID((UInt_t)ind);
1923  }
1924 
1925  return pid;
1926 }
1927 
1928 
1929 ////////////////////////////////////////////////////////////////////////////////
1930 /// Attempt to recover file if not correctly closed
1931 ///
1932 /// The function returns the number of keys that have been recovered.
1933 /// If no keys can be recovered, the file will be declared Zombie by
1934 /// the calling function. This function is automatically called when
1935 /// opening a file.
1936 /// If the file is open in read only mode, the file is not modified.
1937 /// If open in update mode and the function finds something to recover,
1938 /// a new directory header is written to the file. When opening the file gain
1939 /// no message from Recover will be reported.
1940 /// If keys have been recovered, the file is usable and you can safely
1941 /// read the corresponding objects.
1942 /// If the file is not usable (a zombie), you can test for this case
1943 /// with code like:
1944 ///
1945 /// ~~~{.cpp}
1946 /// TFile f("myfile.root");
1947 /// if (f.IsZombie()) {<actions to take if file is unusable>}
1948 /// ~~~
1949 ///
1950 /// If the file has been recovered, the bit kRecovered is set in the TFile object in memory.
1951 /// You can test if the file has been recovered with
1952 ///
1953 /// if (f.TestBit(TFile::kRecovered)) {... the file has been recovered}
1954 ///
1955 /// When writing TTrees to a file, it is important to save the Tree header
1956 /// at regular intervals (see TTree::AutoSave). If a file containing a Tree
1957 /// is recovered, the last Tree header written to the file will be used.
1958 /// In this case all the entries in all the branches written before writing
1959 /// the header are valid entries.
1960 /// One can disable the automatic recovery procedure by setting
1961 ///
1962 /// TFile.Recover 0
1963 ///
1964 /// in the <em>system.rootrc</em> file.
1967 {
1968  Short_t keylen,cycle;
1969  UInt_t datime;
1970  Int_t nbytes,date,time,objlen,nwheader;
1971  Long64_t seekkey,seekpdir;
1972  char header[1024];
1973  char *buffer, *bufread;
1974  char nwhc;
1975  Long64_t idcur = fBEGIN;
1976 
1977  Long64_t size;
1978  if ((size = GetSize()) == -1) { // NOLINT: silence clang-tidy warnings
1979  Error("Recover", "cannot stat the file %s", GetName());
1980  return 0;
1981  }
1982 
1983  fEND = Long64_t(size);
1984 
1985  if (fWritable && !fFree) fFree = new TList;
1986 
1987  TKey *key;
1988  Int_t nrecov = 0;
1989  nwheader = 1024;
1990  Int_t nread = nwheader;
1991 
1992  while (idcur < fEND) {
1993  Seek(idcur); // NOLINT: silence clang-tidy warnings
1994  if (idcur+nread >= fEND) nread = fEND-idcur-1;
1995  if (ReadBuffer(header, nread)) { // NOLINT: silence clang-tidy warnings
1996  // ReadBuffer returns kTRUE in case of failure.
1997  Error("Recover","%s: failed to read the key data from disk at %lld.",
1998  GetName(),idcur);
1999  break;
2000  }
2001  buffer = header;
2002  bufread = header;
2003  frombuf(buffer, &nbytes);
2004  if (!nbytes) {
2005  Error("Recover","Address = %lld\tNbytes = %d\t=====E R R O R=======", idcur, nbytes);
2006  break;
2007  }
2008  if (nbytes < 0) {
2009  idcur -= nbytes;
2010  if (fWritable) new TFree(fFree,idcur,idcur-nbytes-1);
2011  Seek(idcur);
2012  continue;
2013  }
2014  Version_t versionkey;
2015  frombuf(buffer, &versionkey);
2016  frombuf(buffer, &objlen);
2017  frombuf(buffer, &datime);
2018  frombuf(buffer, &keylen);
2019  frombuf(buffer, &cycle);
2020  if (versionkey > 1000) {
2021  frombuf(buffer, &seekkey);
2022  frombuf(buffer, &seekpdir);
2023  } else {
2024  Int_t skey,sdir;
2025  frombuf(buffer, &skey); seekkey = (Long64_t)skey;
2026  frombuf(buffer, &sdir); seekpdir = (Long64_t)sdir;
2027  }
2028  frombuf(buffer, &nwhc);
2029  char *classname = nullptr;
2030  if (nwhc <= 0 || nwhc > 100) break;
2031  classname = new char[nwhc+1];
2032  int i, nwhci = nwhc;
2033  for (i = 0;i < nwhc; i++) frombuf(buffer, &classname[i]);
2034  classname[nwhci] = '\0';
2035  TDatime::GetDateTime(datime, date, time);
2036  TClass *tclass = TClass::GetClass(classname);
2037  if (seekpdir == fSeekDir && tclass && !tclass->InheritsFrom(TFile::Class())
2038  && strcmp(classname,"TBasket")) {
2039  key = new TKey(this);
2040  key->ReadKeyBuffer(bufread);
2041  if (!strcmp(key->GetName(),"StreamerInfo")) {
2042  fSeekInfo = seekkey;
2044  fNbytesInfo = nbytes;
2045  } else {
2046  AppendKey(key);
2047  nrecov++;
2048  SetBit(kRecovered);
2049  Info("Recover", "%s, recovered key %s:%s at address %lld",GetName(),key->GetClassName(),key->GetName(),idcur);
2050  }
2051  }
2052  delete [] classname;
2053  idcur += nbytes;
2054  }
2055  if (fWritable) {
2056  Long64_t max_file_size = Long64_t(kStartBigFile);
2057  if (max_file_size < fEND) max_file_size = fEND+1000000000;
2058  TFree *last = (TFree*)fFree->Last();
2059  if (last) {
2060  last->AddFree(fFree,fEND,max_file_size);
2061  } else {
2062  new TFree(fFree,fEND,max_file_size);
2063  }
2064  if (nrecov) Write();
2065  }
2066  return nrecov;
2067 }
2068 
2069 ////////////////////////////////////////////////////////////////////////////////
2070 /// Reopen a file with a different access mode.
2071 ///
2072 /// For example, it is possible to change from READ to
2073 /// UPDATE or from NEW, CREATE, RECREATE, UPDATE to READ. Thus the
2074 /// mode argument can be either "READ" or "UPDATE". The method returns
2075 /// 0 in case the mode was successfully modified, 1 in case the mode
2076 /// did not change (was already as requested or wrong input arguments)
2077 /// and -1 in case of failure, in which case the file cannot be used
2078 /// anymore. The current directory (gFile) is changed to this file.
2081 {
2082  cd();
2083 
2084  TString opt = mode;
2085  opt.ToUpper();
2086 
2087  if (opt != "READ" && opt != "UPDATE") {
2088  Error("ReOpen", "mode must be either READ or UPDATE, not %s", opt.Data());
2089  return 1;
2090  }
2091 
2092  if (opt == fOption || (opt == "UPDATE" && fOption == "CREATE"))
2093  return 1;
2094 
2095  if (opt == "READ") {
2096  // switch to READ mode
2097 
2098  // flush data still in the pipeline and close the file
2099  if (IsOpen() && IsWritable()) {
2101 
2102  // save directory key list and header
2103  Save();
2104 
2105  TFree *f1 = (TFree*)fFree->First();
2106  if (f1) {
2107  WriteFree(); // write free segments linked list
2108  WriteHeader(); // now write file header
2109  }
2110 
2111  FlushWriteCache();
2112 
2113  // delete free segments from free list
2114  fFree->Delete();
2115  SafeDelete(fFree);
2116 
2117  SysClose(fD);
2118  fD = -1;
2119 
2121  }
2122 
2123  // open in READ mode
2124  fOption = opt; // set fOption before SysOpen() for TNetFile
2125 #ifndef WIN32
2126  fD = SysOpen(fRealName, O_RDONLY, 0644);
2127 #else
2128  fD = SysOpen(fRealName, O_RDONLY | O_BINARY, S_IREAD | S_IWRITE);
2129 #endif
2130  if (fD == -1) {
2131  SysError("ReOpen", "file %s can not be opened in read mode", GetName());
2132  return -1;
2133  }
2135 
2136  } else {
2137  // switch to UPDATE mode
2138 
2139  // close readonly file
2140  if (IsOpen()) {
2141  SysClose(fD);
2142  fD = -1;
2143  }
2144 
2145  // open in UPDATE mode
2146  fOption = opt; // set fOption before SysOpen() for TNetFile
2147 #ifndef WIN32
2148  fD = SysOpen(fRealName, O_RDWR | O_CREAT, 0644);
2149 #else
2150  fD = SysOpen(fRealName, O_RDWR | O_CREAT | O_BINARY, S_IREAD | S_IWRITE);
2151 #endif
2152  if (fD == -1) {
2153  SysError("ReOpen", "file %s can not be opened in update mode", GetName());
2154  return -1;
2155  }
2156  SetWritable(kTRUE);
2157 
2158  fFree = new TList;
2159  if (fSeekFree > fBEGIN)
2160  ReadFree();
2161  else
2162  Warning("ReOpen","file %s probably not closed, cannot read free segments", GetName());
2163  }
2164 
2165  return 0;
2166 }
2167 
2168 ////////////////////////////////////////////////////////////////////////////////
2169 /// Set position from where to start reading.
2171 void TFile::SetOffset(Long64_t offset, ERelativeTo pos)
2172 {
2173  switch (pos) {
2174  case kBeg:
2175  fOffset = offset + fArchiveOffset;
2176  break;
2177  case kCur:
2178  fOffset += offset;
2179  break;
2180  case kEnd:
2181  // this option is not used currently in the ROOT code
2182  if (fArchiveOffset)
2183  Error("SetOffset", "seeking from end in archive is not (yet) supported");
2184  fOffset = fEND + offset; // is fEND really EOF or logical EOF?
2185  break;
2186  }
2187 }
2188 
2189 ////////////////////////////////////////////////////////////////////////////////
2190 /// Seek to a specific position in the file. Pos it either kBeg, kCur or kEnd.
2192 void TFile::Seek(Long64_t offset, ERelativeTo pos)
2193 {
2194  int whence = 0;
2195  switch (pos) {
2196  case kBeg:
2197  whence = SEEK_SET;
2198  offset += fArchiveOffset;
2199  break;
2200  case kCur:
2201  whence = SEEK_CUR;
2202  break;
2203  case kEnd:
2204  whence = SEEK_END;
2205  // this option is not used currently in the ROOT code
2206  if (fArchiveOffset)
2207  Error("Seek", "seeking from end in archive is not (yet) supported");
2208  break;
2209  }
2210  Long64_t retpos;
2211  if ((retpos = SysSeek(fD, offset, whence)) < 0) // NOLINT: silence clang-tidy warnings
2212  SysError("Seek", "cannot seek to position %lld in file %s, retpos=%lld",
2213  offset, GetName(), retpos);
2214 
2215  // used by TFileCacheRead::ReadBuffer()
2216  fOffset = retpos;
2217 }
2218 
2219 ////////////////////////////////////////////////////////////////////////////////
2220 /// See comments for function SetCompressionSettings
2221 ///
2223 void TFile::SetCompressionAlgorithm(Int_t algorithm)
2224 {
2225  if (algorithm < 0 || algorithm >= ROOT::RCompressionSetting::EAlgorithm::kUndefined) algorithm = 0;
2226  if (fCompress < 0) {
2228  } else {
2229  int level = fCompress % 100;
2230  fCompress = 100 * algorithm + level;
2231  }
2232 }
2233 
2234 ////////////////////////////////////////////////////////////////////////////////
2235 /// See comments for function SetCompressionSettings
2238 {
2239  if (level < 0) level = 0;
2240  if (level > 99) level = 99;
2241  if (fCompress < 0) {
2242  // if the algorithm is not defined yet use 0 as a default
2243  fCompress = level;
2244  } else {
2245  int algorithm = fCompress / 100;
2246  if (algorithm >= ROOT::RCompressionSetting::EAlgorithm::kUndefined) algorithm = 0;
2247  fCompress = 100 * algorithm + level;
2248  }
2249 }
2250 
2251 ////////////////////////////////////////////////////////////////////////////////
2252 /// Used to specify the compression level and algorithm.
2253 ///
2254 /// See the TFile constructor for the details.
2256 void TFile::SetCompressionSettings(Int_t settings)
2257 {
2258  fCompress = settings;
2259 }
2260 
2261 ////////////////////////////////////////////////////////////////////////////////
2262 /// Set a pointer to the read cache.
2263 ///
2264 /// <b>This relinquishes ownership</b> of the previous cache, so if you do not
2265 /// already have a pointer to the previous cache (and there was a previous
2266 /// cache), you ought to retrieve (and delete it if needed) using:
2267 ///
2268 /// TFileCacheRead *older = myfile->GetCacheRead();
2269 ///
2270 /// The action specifies how to behave when detaching a cache from the
2271 /// the TFile. If set to (default) kDisconnect, the contents of the cache
2272 /// will be flushed when it is removed from the file, and it will disconnect
2273 /// the cache object from the file. In almost all cases, this is what you want.
2274 /// If you want to disconnect the cache temporarily from this tree and re-attach
2275 /// later to the same fil, you can set action to kDoNotDisconnect. This will allow
2276 /// things like prefetching to continue in the background while it is no longer the
2277 /// default cache for the TTree. Except for a few expert use cases, kDisconnect is
2278 /// likely the correct setting.
2279 ///
2280 /// WARNING: if action=kDoNotDisconnect, you MUST delete the cache before TFile.
2281 ///
2284 {
2285  if (tree) {
2286  if (cache) fCacheReadMap->Add(tree, cache);
2287  else {
2288  // The only addition to fCacheReadMap is via an interface that takes
2289  // a TFileCacheRead* so the C-cast is safe.
2292  if (tpf && (tpf->GetFile() == this) && (action != kDoNotDisconnect)) tpf->SetFile(0, action);
2293  }
2294  }
2295  if (cache) cache->SetFile(this, action);
2296  else if (!tree && fCacheRead && (action != kDoNotDisconnect)) fCacheRead->SetFile(0, action);
2297  // For backward compatibility the last Cache set is the default cache.
2298  fCacheRead = cache;
2299 }
2300 
2301 ////////////////////////////////////////////////////////////////////////////////
2302 /// Set a pointer to the write cache.
2303 ///
2304 /// If file is null the existing write cache is deleted.
2307 {
2308  if (!cache && fCacheWrite) delete fCacheWrite;
2309  fCacheWrite = cache;
2310 }
2311 
2312 ////////////////////////////////////////////////////////////////////////////////
2313 /// Return the size in bytes of the file header.
2315 Int_t TFile::Sizeof() const
2316 {
2317  return 0;
2318 }
2319 
2320 ////////////////////////////////////////////////////////////////////////////////
2321 /// Stream a TFile object.
2322 
2323 void TFile::Streamer(TBuffer &b)
2324 {
2325  if (b.IsReading()) {
2326  b.ReadVersion(); //Version_t v = b.ReadVersion();
2327  } else {
2328  b.WriteVersion(TFile::IsA());
2329  }
2330 }
2331 
2332 ////////////////////////////////////////////////////////////////////////////////
2333 /// Increment statistics for buffer sizes of objects in this file.
2335 void TFile::SumBuffer(Int_t bufsize)
2336 {
2337  fWritten++;
2338  fSumBuffer += double(bufsize);
2339  fSum2Buffer += double(bufsize) * double(bufsize); // avoid reaching MAXINT for temporary
2340 }
2341 
2342 ////////////////////////////////////////////////////////////////////////////////
2343 /// Write memory objects to this file.
2344 ///
2345 /// Loop on all objects in memory (including subdirectories).
2346 /// A new key is created in the KEYS linked list for each object.
2347 /// The list of keys is then saved on the file (via WriteKeys)
2348 /// as a single data record.
2349 /// For values of opt see TObject::Write().
2350 /// The directory header info is rewritten on the directory header record.
2351 /// The linked list of FREE segments is written.
2352 /// The file header is written (bytes 1->fBEGIN).
2354 Int_t TFile::Write(const char *, Int_t opt, Int_t bufsiz)
2355 {
2356  if (!IsWritable()) {
2357  if (!TestBit(kWriteError)) {
2358  // Do not print the warning if we already had a SysError.
2359  Warning("Write", "file %s not opened in write mode", GetName());
2360  }
2361  return 0;
2362  }
2363 
2364  if (gDebug) {
2365  if (!GetTitle() || strlen(GetTitle()) == 0)
2366  Info("Write", "writing name = %s", GetName());
2367  else
2368  Info("Write", "writing name = %s title = %s", GetName(), GetTitle());
2369  }
2370 
2371  fMustFlush = kFALSE;
2372  Int_t nbytes = TDirectoryFile::Write(0, opt, bufsiz); // Write directory tree
2374  WriteFree(); // Write free segments linked list
2375  WriteHeader(); // Now write file header
2376  fMustFlush = kTRUE;
2377 
2378  return nbytes;
2379 }
2380 
2381 ////////////////////////////////////////////////////////////////////////////////
2382 /// One can not save a const TDirectory object.
2384 Int_t TFile::Write(const char *n, Int_t opt, Int_t bufsize) const
2385 {
2386  Error("Write const","A const TFile object should not be saved. We try to proceed anyway.");
2387  return const_cast<TFile*>(this)->Write(n, opt, bufsize);
2388 }
2389 
2390 ////////////////////////////////////////////////////////////////////////////////
2391 /// Write a buffer to the file. This is the basic low level write operation.
2392 /// Returns kTRUE in case of failure.
2394 Bool_t TFile::WriteBuffer(const char *buf, Int_t len)
2395 {
2396  if (IsOpen() && fWritable) {
2397 
2398  Int_t st;
2399  if ((st = WriteBufferViaCache(buf, len))) {
2400  if (st == 2)
2401  return kTRUE;
2402  return kFALSE;
2403  }
2404 
2405  ssize_t siz;
2407  while ((siz = SysWrite(fD, buf, len)) < 0 && GetErrno() == EINTR) // NOLINT: silence clang-tidy warnings
2408  ResetErrno(); // NOLINT: silence clang-tidy warnings
2410  if (siz < 0) {
2411  // Write the system error only once for this file
2413  SysError("WriteBuffer", "error writing to file %s (%ld)", GetName(), (Long_t)siz);
2414  return kTRUE;
2415  }
2416  if (siz != len) {
2418  Error("WriteBuffer", "error writing all requested bytes to file %s, wrote %ld of %d",
2419  GetName(), (Long_t)siz, len);
2420  return kTRUE;
2421  }
2422  fBytesWrite += siz;
2423  fgBytesWrite += siz;
2424 
2425  if (gMonitoringWriter)
2427 
2428  return kFALSE;
2429  }
2430  return kTRUE;
2431 }
2432 
2433 ////////////////////////////////////////////////////////////////////////////////
2434 /// Write buffer via cache. Returns 0 if cache is not active, 1 in case
2435 /// write via cache was successful, 2 in case write via cache failed.
2437 Int_t TFile::WriteBufferViaCache(const char *buf, Int_t len)
2438 {
2439  if (!fCacheWrite) return 0;
2440 
2441  Int_t st;
2442  Long64_t off = GetRelOffset();
2443  if ((st = fCacheWrite->WriteBuffer(buf, off, len)) < 0) {
2445  Error("WriteBuffer", "error writing to cache");
2446  return 2;
2447  }
2448  if (st > 0) {
2449  // fOffset might have been changed via TFileCacheWrite::WriteBuffer(), reset it
2450  Seek(off + len);
2451  return 1;
2452  }
2453  return 0;
2454 }
2455 
2456 ////////////////////////////////////////////////////////////////////////////////
2457 /// Write FREE linked list on the file.
2458 /// The linked list of FREE segments (fFree) is written as a single data
2459 /// record.
2461 void TFile::WriteFree()
2462 {
2463  //*-* Delete old record if it exists
2464  if (fSeekFree != 0) {
2466  }
2467 
2468  Bool_t largeFile = (fEND > TFile::kStartBigFile);
2469 
2470  auto createKey = [this]() {
2471  Int_t nbytes = 0;
2472  TFree *afree;
2473  TIter next (fFree);
2474  while ((afree = (TFree*) next())) {
2475  nbytes += afree->Sizeof();
2476  }
2477  if (!nbytes) return (TKey*)nullptr;
2478 
2479  TKey *key = new TKey(fName,fTitle,IsA(),nbytes,this);
2480 
2481  if (key->GetSeekKey() == 0) {
2482  delete key;
2483  return (TKey*)nullptr;
2484  }
2485  return key;
2486  };
2487 
2488  TKey *key = createKey();
2489  if (!key) return;
2490 
2491  if (!largeFile && (fEND > TFile::kStartBigFile)) {
2492  // The free block list is large enough to bring the file to larger
2493  // than 2Gb, the references/offsets are now 64bits in the output
2494  // so we need to redo the calculation since the list of free block
2495  // information will not fit in the original size.
2496  key->Delete();
2497  delete key;
2498 
2499  key = createKey();
2500  if (!key) return;
2501  }
2502 
2503  Int_t nbytes = key->GetObjlen();
2504  char *buffer = key->GetBuffer();
2505  char *start = buffer;
2506 
2507  TIter next (fFree);
2508  TFree *afree;
2509  while ((afree = (TFree*) next())) {
2510  // We could 'waste' time here and double check that
2511  // (buffer+afree->Sizeof() < (start+nbytes)
2512  afree->FillBuffer(buffer);
2513  }
2514  auto actualBytes = buffer-start;
2515  if ( actualBytes != nbytes ) {
2516  if (actualBytes < nbytes) {
2517  // Most likely one of the 'free' segment was used to store this
2518  // TKey, so we had one less TFree to store than we planned.
2519  memset(buffer,0,nbytes-actualBytes);
2520  } else {
2521  Error("WriteFree","The free block list TKey wrote more data than expected (%d vs %ld). Most likely there has been an out-of-bound write.",nbytes,(long int)actualBytes);
2522  }
2523  }
2524  fNbytesFree = key->GetNbytes();
2525  fSeekFree = key->GetSeekKey();
2526  key->WriteFile();
2527  delete key;
2528 }
2529 
2530 ////////////////////////////////////////////////////////////////////////////////
2531 /// Write File Header.
2533 void TFile::WriteHeader()
2534 {
2536  TFree *lastfree = (TFree*)fFree->Last();
2537  if (lastfree) fEND = lastfree->GetFirst();
2538  const char *root = "root";
2539  char *psave = new char[fBEGIN];
2540  char *buffer = psave;
2541  Int_t nfree = fFree->GetSize();
2542  memcpy(buffer, root, 4); buffer += 4;
2543  Int_t version = fVersion;
2544  if (version <1000000 && fEND > kStartBigFile) {version += 1000000; fUnits = 8;}
2545  tobuf(buffer, version);
2546  tobuf(buffer, (Int_t)fBEGIN);
2547  if (version < 1000000) {
2548  tobuf(buffer, (Int_t)fEND);
2549  tobuf(buffer, (Int_t)fSeekFree);
2550  tobuf(buffer, fNbytesFree);
2551  tobuf(buffer, nfree);
2552  tobuf(buffer, fNbytesName);
2553  tobuf(buffer, fUnits);
2554  tobuf(buffer, fCompress);
2555  tobuf(buffer, (Int_t)fSeekInfo);
2556  tobuf(buffer, fNbytesInfo);
2557  } else {
2558  tobuf(buffer, fEND);
2559  tobuf(buffer, fSeekFree);
2560  tobuf(buffer, fNbytesFree);
2561  tobuf(buffer, nfree);
2562  tobuf(buffer, fNbytesName);
2563  tobuf(buffer, fUnits);
2564  tobuf(buffer, fCompress);
2565  tobuf(buffer, fSeekInfo);
2566  tobuf(buffer, fNbytesInfo);
2567  }
2568  if (TestBit(kReproducible))
2569  TUUID("00000000-0000-0000-0000-000000000000").FillBuffer(buffer);
2570  else
2571  fUUID.FillBuffer(buffer);
2572  Int_t nbytes = buffer - psave;
2573  Seek(0); // NOLINT: silence clang-tidy warnings
2574  WriteBuffer(psave, nbytes); // NOLINT: silence clang-tidy warnings
2575  Flush(); // NOLINT: silence clang-tidy warnings, Intentionally not conditional on fMustFlush, this is the 'obligatory' flush.
2576  delete [] psave;
2577 }
2578 
2579 ////////////////////////////////////////////////////////////////////////////////
2580 /// Generate source code necessary to access the objects stored in the file.
2581 ///
2582 /// Generate code in directory dirname for all classes specified in
2583 /// argument classes If classes = "*" (default and currently the
2584 /// only supported value), the function generates an include file
2585 /// for each class in the StreamerInfo list for which a TClass
2586 /// object does not exist.
2587 ///
2588 /// The code generated includes:
2589 /// - <em>dirnameProjectHeaders.h</em>, which contains one #include statement per generated header file
2590 /// - <em>dirnameProjectSource.cxx</em>,which contains all the constructors and destructors implementation.
2591 /// and one header per class that is not nested inside another class.
2592 /// The header file name is the fully qualified name of the class after all the special characters
2593 /// "<>,:" are replaced by underscored. For example for std::pair<edm::Vertex,int> the file name is
2594 /// pair_edm__Vertex_int_.h
2595 ///
2596 /// In the generated classes, map, multimap when the first template parameter is a class
2597 /// are replaced by a vector of pair. set and multiset when the tempalte parameter
2598 /// is a class are replaced by a vector. This is required since we do not have the
2599 /// code needed to order and/or compare the object of the classes.
2600 /// This is a quick explanation of the options available:
2601 /// Option | Details
2602 /// -------|--------
2603 /// new (default) | A new directory dirname is created. If dirname already exist, an error message is printed and the function returns.
2604 /// recreate | 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.
2605 /// update | 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".
2606 /// genreflex | Use genreflex rather than rootcint to generate the dictionary.
2607 /// par | Create a PAR file with the minimal set of code needed to read the content of the ROOT file. The name of the PAR file is basename(dirname), with extension '.par' enforced; the PAR file will be created at dirname(dirname).
2608 ///
2609 /// If, in addition to one of the 3 above options, the option "+" is specified,
2610 /// the function will generate:
2611 /// - a script called MAKEP to build the shared lib
2612 /// - a dirnameLinkDef.h file
2613 /// - rootcint will be run to generate a dirnameProjectDict.cxx file
2614 /// - dirnameProjectDict.cxx will be compiled with the current options in compiledata.h
2615 /// - a shared lib dirname.so will be created.
2616 /// If the option "++" is specified, the generated shared lib is dynamically
2617 /// linked with the current executable module.
2618 /// If the option "+" and "nocompile" are specified, the utility files are generated
2619 /// as in the option "+" but they are not executed.
2620 /// Example:
2621 /// file.MakeProject("demo","*","recreate++");
2622 /// - creates a new directory demo unless it already exist
2623 /// - clear the previous directory content
2624 /// - generate the xxx.h files for all classes xxx found in this file
2625 /// and not yet known to the CINT dictionary.
2626 /// - creates the build script MAKEP
2627 /// - creates a LinkDef.h file
2628 /// - runs rootcint generating demoProjectDict.cxx
2629 /// - compiles demoProjectDict.cxx into demoProjectDict.o
2630 /// - generates a shared lib demo.so
2631 /// - dynamically links the shared lib demo.so to the executable
2632 /// If only the option "+" had been specified, one can still link the
2633 /// shared lib to the current executable module with:
2634 ///
2635 /// gSystem->load("demo/demo.so");
2636 ///
2637 /// The following feature is not yet enabled:
2638 /// One can restrict the list of classes to be generated by using expressions like:
2639 ///
2640 /// classes = "Ali*" generate code only for classes starting with Ali
2641 /// classes = "myClass" generate code for class MyClass only.
2642 ///
2644 void TFile::MakeProject(const char *dirname, const char * /*classes*/,
2645  Option_t *option)
2646 {
2647  TString opt = option;
2648  opt.ToLower();
2649  Bool_t makepar = kFALSE;
2650  TString parname, pardir;
2651  if (opt.Contains("par")) {
2652  // Create a PAR file
2653  parname = gSystem->BaseName(dirname);
2654  if (parname.EndsWith(".par")) parname.ReplaceAll(".par","");
2655  pardir = gSystem->GetDirName(dirname);
2656  // Cleanup or prepare the dirs
2657  TString path, filepath;
2658  void *dir = gSystem->OpenDirectory(pardir);
2659  if (dir) {
2660  path.Form("%s/%s", pardir.Data(), parname.Data());
2661  void *dirp = gSystem->OpenDirectory(path);
2662  if (dirp) {
2663  path += "/PROOF-INF";
2664  void *dirinf = gSystem->OpenDirectory(path);
2665  const char *afile = 0;
2666  if (dirinf) {
2667  while ((afile = gSystem->GetDirEntry(dirinf))) {
2668  if (strcmp(afile,".") == 0) continue;
2669  if (strcmp(afile,"..") == 0) continue;
2670  filepath.Form("%s/%s", path.Data(), afile);
2671  if (gSystem->Unlink(filepath))
2672  Warning("MakeProject", "1: problems unlinking '%s' ('%s', '%s')", filepath.Data(), path.Data(), afile);
2673  }
2674  gSystem->FreeDirectory(dirinf);
2675  }
2676  gSystem->Unlink(path);
2677  path.Form("%s/%s", pardir.Data(), parname.Data());
2678  while ((afile = gSystem->GetDirEntry(dirp))) {
2679  if (strcmp(afile,".") == 0) continue;
2680  if (strcmp(afile,"..") == 0) continue;
2681  filepath.Form("%s/%s", path.Data(), afile);
2682  if (gSystem->Unlink(filepath))
2683  Warning("MakeProject", "2: problems unlinking '%s' ('%s', '%s')", filepath.Data(), path.Data(), afile);
2684  }
2685  gSystem->FreeDirectory(dirp);
2686  if (gSystem->Unlink(path))
2687  Warning("MakeProject", "problems unlinking '%s'", path.Data());
2688  }
2689  }
2690  // Make sure that the relevant dirs exists: this is mandatory, so we fail if unsuccessful
2691  path.Form("%s/%s/PROOF-INF", pardir.Data(), parname.Data());
2692  if (gSystem->mkdir(path, kTRUE)) {
2693  Error("MakeProject", "problems creating '%s'", path.Data());
2694  return;
2695  }
2696  makepar = kTRUE;
2697 
2698  } else {
2699  void *dir = gSystem->OpenDirectory(dirname);
2700  TString dirpath;
2701 
2702  if (opt.Contains("update")) {
2703  // check that directory exist, if not create it
2704  if (!dir) {
2705  gSystem->mkdir(dirname);
2706  }
2707 
2708  } else if (opt.Contains("recreate")) {
2709  // check that directory exist, if not create it
2710  if (!dir) {
2711  if (gSystem->mkdir(dirname) < 0) {
2712  Error("MakeProject","cannot create directory '%s'",dirname);
2713  return;
2714  }
2715  }
2716  // clear directory
2717  while (dir) {
2718  const char *afile = gSystem->GetDirEntry(dir);
2719  if (!afile) break;
2720  if (strcmp(afile,".") == 0) continue;
2721  if (strcmp(afile,"..") == 0) continue;
2722  dirpath.Form("%s/%s",dirname,afile);
2723  gSystem->Unlink(dirpath);
2724  }
2725 
2726  } else {
2727  // new is assumed
2728  // if directory already exist, print error message and return
2729  if (dir) {
2730  Error("MakeProject","cannot create directory %s, already existing",dirname);
2731  gSystem->FreeDirectory(dir);
2732  return;
2733  }
2734  if (gSystem->mkdir(dirname) < 0) {
2735  Error("MakeProject","cannot create directory '%s'",dirname);
2736  return;
2737  }
2738  }
2739  if (dir) {
2740  gSystem->FreeDirectory(dir);
2741  }
2742  }
2743  Bool_t genreflex = opt.Contains("genreflex");
2744 
2745  // we are now ready to generate the classes
2746  // loop on all TStreamerInfo
2747  TList *filelist = (TList*)GetStreamerInfoCache();
2748  if (filelist) filelist = (TList*)filelist->Clone();
2749  if (!filelist) {
2750  Error("MakeProject","file %s has no StreamerInfo", GetName());
2751  return;
2752  }
2753 
2754  TString clean_dirname(dirname);
2755  if (makepar) clean_dirname.Form("%s/%s", pardir.Data(), parname.Data());
2756  if (clean_dirname[clean_dirname.Length()-1]=='/') {
2757  clean_dirname.Remove(clean_dirname.Length()-1);
2758  } else if (clean_dirname[clean_dirname.Length()-1]=='\\') {
2759  clean_dirname.Remove(clean_dirname.Length()-1);
2760  if (clean_dirname[clean_dirname.Length()-1]=='\\') {
2761  clean_dirname.Remove(clean_dirname.Length()-1);
2762  }
2763  }
2764  TString subdirname( gSystem->BaseName(clean_dirname) );
2765  if (makepar) subdirname = parname;
2766  if (subdirname == "") {
2767  Error("MakeProject","Directory name must not be empty.");
2768  return;
2769  }
2770 
2771  // Start the source file
2772  TString spath; spath.Form("%s/%sProjectSource.cxx",clean_dirname.Data(),subdirname.Data());
2773  FILE *sfp = fopen(spath.Data(),"w");
2774  if (!sfp) {
2775  Error("MakeProject","Unable to create the source file %s.",spath.Data());
2776  return;
2777  }
2778  fprintf(sfp, "namespace std {}\nusing namespace std;\n");
2779  fprintf(sfp, "#include \"%sProjectHeaders.h\"\n\n",subdirname.Data() );
2780  if (!genreflex) fprintf(sfp, "#include \"%sLinkDef.h\"\n\n",subdirname.Data() );
2781  fprintf(sfp, "#include \"%sProjectDict.cxx\"\n\n",subdirname.Data() );
2782  fprintf(sfp, "struct DeleteObjectFunctor {\n");
2783  fprintf(sfp, " template <typename T>\n");
2784  fprintf(sfp, " void operator()(const T *ptr) const {\n");
2785  fprintf(sfp, " delete ptr;\n");
2786  fprintf(sfp, " }\n");
2787  fprintf(sfp, " template <typename T, typename Q>\n");
2788  fprintf(sfp, " void operator()(const std::pair<T,Q> &) const {\n");
2789  fprintf(sfp, " // Do nothing\n");
2790  fprintf(sfp, " }\n");
2791  fprintf(sfp, " template <typename T, typename Q>\n");
2792  fprintf(sfp, " void operator()(const std::pair<T,Q*> &ptr) const {\n");
2793  fprintf(sfp, " delete ptr.second;\n");
2794  fprintf(sfp, " }\n");
2795  fprintf(sfp, " template <typename T, typename Q>\n");
2796  fprintf(sfp, " void operator()(const std::pair<T*,Q> &ptr) const {\n");
2797  fprintf(sfp, " delete ptr.first;\n");
2798  fprintf(sfp, " }\n");
2799  fprintf(sfp, " template <typename T, typename Q>\n");
2800  fprintf(sfp, " void operator()(const std::pair<T*,Q*> &ptr) const {\n");
2801  fprintf(sfp, " delete ptr.first;\n");
2802  fprintf(sfp, " delete ptr.second;\n");
2803  fprintf(sfp, " }\n");
2804  fprintf(sfp, "};\n\n");
2805  fclose( sfp );
2806 
2807  // loop on all TStreamerInfo classes to check for empty classes
2808  // and enums listed either as data member or template parameters,
2809  // and filter out 'duplicates' classes/streamerInfos.
2810  TStreamerInfo *info;
2811  TIter flnext(filelist);
2812  TList extrainfos;
2813  TList *list = new TList();
2814  while ((info = (TStreamerInfo*)flnext())) {
2815  if (info->IsA() != TStreamerInfo::Class()) {
2816  continue;
2817  }
2818  if (strstr(info->GetName(),"@@")) {
2819  // Skip schema evolution support streamerInfo
2820  continue;
2821  }
2822  TClass *cl = TClass::GetClass(info->GetName());
2823  if (cl) {
2824  if (cl->HasInterpreterInfo()) continue; // skip known classes
2825  }
2826  // Find and use the proper rules for the TStreamerInfos.
2827  TMakeProject::GenerateMissingStreamerInfos( &extrainfos, info->GetName() );
2828  TIter enext( info->GetElements() );
2829  TStreamerElement *el;
2831  if (cl && cl->GetSchemaRules()) {
2832  rules = cl->GetSchemaRules()->FindRules(cl->GetName(), info->GetClassVersion());
2833  }
2834  while( (el=(TStreamerElement*)enext()) ) {
2835  for(auto rule : rules) {
2836  if( rule->IsRenameRule() || rule->IsAliasRule() )
2837  continue;
2838  // Check whether this is an 'attribute' rule.
2839  if ( rule->HasTarget( el->GetName()) && rule->GetAttributes()[0] != 0 ) {
2840  TString attr( rule->GetAttributes() );
2841  attr.ToLower();
2842  if (attr.Contains("owner")) {
2843  if (attr.Contains("notowner")) {
2845  } else {
2847  }
2848  }
2849  }
2850  }
2852  }
2853  TVirtualStreamerInfo *alternate = (TVirtualStreamerInfo*)list->FindObject(info->GetName());
2854  if (alternate) {
2855  if ((info->GetClass() && info->GetClassVersion() == info->GetClass()->GetClassVersion())
2856  || (info->GetClassVersion() > alternate->GetClassVersion()) ) {
2857  list->AddAfter(alternate, info);
2858  list->Remove(alternate);
2859  } // otherwise ignore this info as not being the official one.
2860  } else {
2861  list->Add(info);
2862  }
2863  }
2864  // Now transfer the new StreamerInfo onto the main list and
2865  // to the owning list.
2866  TIter nextextra(&extrainfos);
2867  while ((info = (TStreamerInfo*)nextextra())) {
2868  list->Add(info);
2869  filelist->Add(info);
2870  }
2871 
2872  // loop on all TStreamerInfo classes
2873  TIter next(list);
2874  Int_t ngener = 0;
2875  while ((info = (TStreamerInfo*)next())) {
2876  if (info->IsA() != TStreamerInfo::Class()) {
2877  continue;
2878  }
2879  if (info->GetClassVersion()==-4) continue; // Skip outer level namespace
2880  TIter subnext(list);
2881  TStreamerInfo *subinfo;
2882  TList subClasses;
2883  Int_t len = strlen(info->GetName());
2884  while ((subinfo = (TStreamerInfo*)subnext())) {
2885  if (subinfo->IsA() != TStreamerInfo::Class()) {
2886  continue;
2887  }
2888  if (strncmp(info->GetName(),subinfo->GetName(),len)==0) {
2889  // The 'sub' StreamerInfo start with the main StreamerInfo name,
2890  // it subinfo is likely to be a nested class.
2891  const Int_t sublen = strlen(subinfo->GetName());
2892  if ( (sublen > len) && subinfo->GetName()[len+1]==':'
2893  && !subClasses.FindObject(subinfo->GetName()) /* We need to insure uniqueness */)
2894  {
2895  subClasses.Add(subinfo);
2896  }
2897  }
2898  }
2899  ngener += info->GenerateHeaderFile(clean_dirname.Data(),&subClasses,&extrainfos);
2900  subClasses.Clear("nodelete");
2901  }
2902  extrainfos.Clear("nodelete"); // We are done with this list.
2903 
2904  TString path;
2905  path.Form("%s/%sProjectHeaders.h",clean_dirname.Data(),subdirname.Data());
2906  FILE *allfp = fopen(path,"a");
2907  if (!allfp) {
2908  Error("MakeProject","Cannot open output file:%s\n",path.Data());
2909  } else {
2910  fprintf(allfp,"#include \"%sProjectInstances.h\"\n", subdirname.Data());
2911  fclose(allfp);
2912  }
2913 
2914  printf("MakeProject has generated %d classes in %s\n",ngener,clean_dirname.Data());
2915 
2916  // generate the shared lib
2917  if (!opt.Contains("+") && !makepar) {
2918  delete list;
2919  filelist->Delete();
2920  delete filelist;
2921  return;
2922  }
2923 
2924  // Makefiles files
2925  FILE *fpMAKE = nullptr;
2926  if (!makepar) {
2927  // Create the MAKEP file by looping on all *.h files
2928  // delete MAKEP if it already exists
2929 #ifdef WIN32
2930  path.Form("%s/makep.cmd",clean_dirname.Data());
2931 #else
2932  path.Form("%s/MAKEP",clean_dirname.Data());
2933 #endif
2934 #ifdef R__WINGCC
2935  fpMAKE = fopen(path,"wb");
2936 #else
2937  fpMAKE = fopen(path,"w");
2938 #endif
2939  if (!fpMAKE) {
2940  Error("MakeProject", "cannot open file %s", path.Data());
2941  delete list;
2942  filelist->Delete();
2943  delete filelist;
2944  return;
2945  }
2946  }
2947 
2948  // Add rootcint/genreflex statement generating ProjectDict.cxx
2949  FILE *ifp = nullptr;
2950  path.Form("%s/%sProjectInstances.h",clean_dirname.Data(),subdirname.Data());
2951 #ifdef R__WINGCC
2952  ifp = fopen(path,"wb");
2953 #else
2954  ifp = fopen(path,"w");
2955 #endif
2956  if (!ifp) {
2957  Error("MakeProject", "cannot open path file %s", path.Data());
2958  delete list;
2959  filelist->Delete();
2960  delete filelist;
2961  fclose(fpMAKE);
2962  return;
2963  }
2964 
2965  if (!makepar) {
2966  if (genreflex) {
2967  fprintf(fpMAKE,"genreflex %sProjectHeaders.h -o %sProjectDict.cxx --comments --iocomments %s ",subdirname.Data(),subdirname.Data(),gSystem->GetIncludePath());
2968  path.Form("%s/%sSelection.xml",clean_dirname.Data(),subdirname.Data());
2969  } else {
2970  fprintf(fpMAKE,"rootcint -v1 -f %sProjectDict.cxx %s ", subdirname.Data(), gSystem->GetIncludePath());
2971  path.Form("%s/%sLinkDef.h",clean_dirname.Data(),subdirname.Data());
2972  }
2973  } else {
2974  path.Form("%s/%sLinkDef.h",clean_dirname.Data(),subdirname.Data());
2975  }
2976 
2977  // Create the LinkDef.h or xml selection file by looping on all *.h files
2978  // replace any existing file.
2979 #ifdef R__WINGCC
2980  FILE *fp = fopen(path,"wb");
2981 #else
2982  FILE *fp = fopen(path,"w");
2983 #endif
2984  if (!fp) {
2985  Error("MakeProject", "cannot open path file %s", path.Data());
2986  delete list;
2987  filelist->Delete();
2988  delete filelist;
2989  fclose(fpMAKE);
2990  fclose(ifp);
2991  return;
2992  }
2993  if (genreflex) {
2994  fprintf(fp,"<lcgdict>\n");
2995  fprintf(fp,"\n");
2996  } else {
2997  fprintf(fp,"#ifdef __CINT__\n");
2998  fprintf(fp,"\n");
2999  }
3000 
3001  TString tmp;
3002  TString instances;
3003  TString selections;
3004  next.Reset();
3005  while ((info = (TStreamerInfo*)next())) {
3006  if (info->IsA() != TStreamerInfo::Class()) {
3007  continue;
3008  }
3009  if (strncmp(info->GetName(), "auto_ptr<", strlen("auto_ptr<")) == 0) {
3010  continue;
3011  }
3012  TClass *cl = TClass::GetClass(info->GetName());
3013  if (cl) {
3014  if (cl->HasInterpreterInfo()) continue; // skip known classes
3015  if (cl->GetSchemaRules()) {
3016  auto rules = cl->GetSchemaRules()->FindRules(cl->GetName(), info->GetClassVersion());
3017  TString strrule;
3018  for(auto rule : rules) {
3019  strrule.Clear();
3020  if (genreflex) {
3021  rule->AsString(strrule,"x");
3022  strrule.Append("\n");
3023  if ( selections.Index(strrule) == kNPOS ) {
3024  selections.Append(strrule);
3025  }
3026  } else {
3027  rule->AsString(strrule);
3028  if (strncmp(strrule.Data(),"type=",5)==0) {
3029  strrule.Remove(0,5);
3030  }
3031  fprintf(fp,"#pragma %s;\n",strrule.Data());
3032  }
3033  }
3034  }
3035 
3036  }
3037  if ((info->GetClass() && info->GetClass()->GetCollectionType()) || TClassEdit::IsSTLCont(info->GetName())) {
3038  std::vector<std::string> inside;
3039  int nestedLoc;
3040  TClassEdit::GetSplit( info->GetName(), inside, nestedLoc, TClassEdit::kLong64 );
3041  Int_t stlkind = TClassEdit::STLKind(inside[0]);
3042  TClass *key = TClass::GetClass(inside[1].c_str());
3043  if (key) {
3044  TString what;
3045  switch ( stlkind ) {
3046  case ROOT::kSTLmap:
3047  case ROOT::kSTLmultimap:
3048  if (TClass::GetClass(inside[1].c_str())) {
3049  what = "std::pair<";
3050  what += TMakeProject::UpdateAssociativeToVector( inside[1].c_str() );
3051  what += ",";
3052  what += TMakeProject::UpdateAssociativeToVector( inside[2].c_str() );
3053  if (what[what.Length()-1]=='>') {
3054  what += " >";
3055  } else {
3056  what += ">";
3057  }
3058  if (genreflex) {
3059  tmp.Form("<class name=\"%s\" />\n",what.Data());
3060  if ( selections.Index(tmp) == kNPOS ) {
3061  selections.Append(tmp);
3062  }
3063  tmp.Form("template class %s;\n",what.Data());
3064  if ( instances.Index(tmp) == kNPOS ) {
3065  instances.Append(tmp);
3066  }
3067  } else {
3068  what.ReplaceAll("std::","");
3069  TClass *paircl = TClass::GetClass(what.Data());
3070  if (!paircl || !paircl->HasInterpreterInfo()) {
3071  fprintf(fp,"#pragma link C++ class %s+;\n",what.Data());
3072  }
3073  }
3074  break;
3075  }
3076  default:
3077  if (TClassEdit::IsStdPair(key->GetName())) {
3078  if (genreflex) {
3079  tmp.Form("<class name=\"%s\" />\n",key->GetName());
3080  if ( selections.Index(tmp) == kNPOS ) {
3081  selections.Append(tmp);
3082  }
3083  tmp.Form("template class %s;\n",key->GetName());
3084  if ( instances.Index(tmp) == kNPOS ) {
3085  instances.Append(tmp);
3086  }
3087  } else {
3088  what.ReplaceAll("std::","");
3089  fprintf(fp,"#pragma link C++ class %s+;\n",key->GetName());
3090  }
3091  }
3092  break;
3093  }
3094  }
3095  continue;
3096  }
3097  {
3099  if (genreflex) {
3100  tmp.Form("<class name=\"%s\" />\n",what.Data());
3101  if ( selections.Index(tmp) == kNPOS ) {
3102  selections.Append(tmp);
3103  }
3104  if (what[what.Length()-1] == '>') {
3105  tmp.Form("template class %s;\n",what.Data());
3106  if ( instances.Index(tmp) == kNPOS ) {
3107  instances.Append(tmp);
3108  }
3109  }
3110  } else {
3111  what.ReplaceAll("std::","");
3112  fprintf(fp,"#pragma link C++ class %s+;\n",what.Data());
3113  }
3114  }
3115  if (genreflex) {
3116  // Also request the dictionary for the STL container used as members ...
3117  TIter eliter( info->GetElements() );
3118  TStreamerElement *element;
3119  while( (element = (TStreamerElement*)eliter() ) ) {
3120  if (element->GetClass() && !element->GetClass()->IsLoaded() && element->GetClass()->GetCollectionProxy()) {
3122  tmp.Form("<class name=\"%s\" />\n",what.Data());
3123  if ( selections.Index(tmp) == kNPOS ) {
3124  selections.Append(tmp);
3125  }
3126  tmp.Form("template class %s;\n",what.Data());
3127  if ( instances.Index(tmp) == kNPOS ) {
3128  instances.Append(tmp);
3129  }
3130  }
3131  }
3132  }
3133  }
3134  if (genreflex) {
3135  fprintf(ifp,"#ifndef PROJECT_INSTANCES_H\n");
3136  fprintf(ifp,"#define PROJECT_INSTANCES_H\n");
3137  fprintf(ifp,"%s",instances.Data());
3138  fprintf(ifp,"#endif\n");
3139  fprintf(fp,"%s",selections.Data());
3140  fprintf(fp,"</lcgdict>\n");
3141  } else {
3142  fprintf(fp,"#endif\n");
3143  }
3144  fclose(fp);
3145  fclose(ifp);
3146 
3147  if (!makepar) {
3148  // add compilation line
3149  TString sdirname(subdirname);
3150 
3151  TString cmd = gSystem->GetMakeSharedLib();
3152  TString sources = TString::Format("%sProjectSource.cxx ", sdirname.Data());
3153  cmd.ReplaceAll("$SourceFiles",sources.Data());
3154  TString object = TString::Format("%sProjectSource.", sdirname.Data());
3155  object.Append( gSystem->GetObjExt() );
3156  cmd.ReplaceAll("$ObjectFiles", object.Data());
3157  cmd.ReplaceAll("$IncludePath",TString(gSystem->GetIncludePath()) + " -I" + clean_dirname.Data());
3158  cmd.ReplaceAll("$SharedLib",sdirname+"."+gSystem->GetSoExt());
3159  cmd.ReplaceAll("$LinkedLibs",gSystem->GetLibraries("","SDL"));
3160  cmd.ReplaceAll("$LibName",sdirname);
3161  cmd.ReplaceAll("$BuildDir",".");
3162  TString sOpt;
3163  TString rootbuild = ROOTBUILD;
3164  if (rootbuild.Index("debug",0,TString::kIgnoreCase)==kNPOS) {
3165  sOpt = gSystem->GetFlagsOpt();
3166  } else {
3167  sOpt = gSystem->GetFlagsDebug();
3168  }
3169  cmd.ReplaceAll("$Opt", sOpt);
3170 
3171  if (genreflex) {
3172  fprintf(fpMAKE,"-s %sSelection.xml \n",subdirname.Data());
3173  } else {
3174  fprintf(fpMAKE,"%sProjectHeaders.h ",subdirname.Data());
3175  fprintf(fpMAKE,"%sLinkDef.h \n",subdirname.Data());
3176  }
3177 
3178  fprintf(fpMAKE,"%s\n",cmd.Data());
3179 
3180  printf("%s/MAKEP file has been generated\n", clean_dirname.Data());
3181 
3182  fclose(fpMAKE);
3183 
3184  } else {
3185 
3186  // Create the Makefile
3187  TString filemake = TString::Format("%s/Makefile", clean_dirname.Data());
3188  if (MakeProjectParMake(parname, filemake.Data()) != 0) {
3189  Error("MakeProject", "problems creating PAR make file '%s'", filemake.Data());
3190  delete list;
3191  filelist->Delete();
3192  delete filelist;
3193  return;
3194  }
3195  // Get Makefile.arch
3196  TString mkarchsrc = TString::Format("%s/Makefile.arch", TROOT::GetEtcDir().Data());
3197  if (gSystem->ExpandPathName(mkarchsrc))
3198  Warning("MakeProject", "problems expanding '%s'", mkarchsrc.Data());
3199  TString mkarchdst = TString::Format("%s/Makefile.arch", clean_dirname.Data());
3200  if (gSystem->CopyFile(mkarchsrc.Data(), mkarchdst.Data(), kTRUE) != 0) {
3201  Error("MakeProject", "problems retrieving '%s' to '%s'", mkarchsrc.Data(), mkarchdst.Data());
3202  delete list;
3203  filelist->Delete();
3204  delete filelist;
3205  return;
3206  }
3207  // Create the Makefile
3208  TString proofinf = TString::Format("%s/PROOF-INF", clean_dirname.Data());
3209  if (MakeProjectParProofInf(parname, proofinf.Data()) != 0) {
3210  Error("MakeProject", "problems creating BUILD.sh and/or SETUP.C under '%s'", proofinf.Data());
3211  delete list;
3212  filelist->Delete();
3213  delete filelist;
3214  return;
3215  }
3216 
3217  // Make sure BUILD.sh is executable and create SETUP.C
3218  TString cmod = TString::Format("chmod +x %s/PROOF-INF/BUILD.sh", clean_dirname.Data());
3219 #ifndef WIN32
3220  gSystem->Exec(cmod.Data());
3221 #else
3222  // not really needed for Windows but it would work both both Unix and NT
3223  chmod(cmod.Data(), 00700);
3224 #endif
3225  Printf("Files Makefile, Makefile.arch, PROOF-INF/BUILD.sh and"
3226  " PROOF-INF/SETUP.C have been generated under '%s'", clean_dirname.Data());
3227 
3228  // Generate the PAR file, if not Windows
3229 #ifndef WIN32
3230  TString curdir = gSystem->WorkingDirectory();
3231  if (gSystem->ChangeDirectory(pardir)) {
3232  TString cmd = TString::Format("tar czvf %s.par %s", parname.Data(), parname.Data());
3233  gSystem->Exec(cmd.Data());
3234  if (gSystem->ChangeDirectory(curdir)) {
3235  Info("MakeProject", "PAR file %s.par generated", clean_dirname.Data());
3236  } else {
3237  Warning("MakeProject", "problems changing directory back to '%s'", curdir.Data());
3238  }
3239  } else {
3240  Error("MakeProject", "problems changing directory to '%s' - skipping PAR file generation", pardir.Data());
3241  }
3242 #else
3243  Warning("MakeProject", "on Windows systems the PAR file cannot be generated out of the package directory!");
3244 #endif
3245  }
3246 
3247 
3248  if (!makepar && !opt.Contains("nocompilation")) {
3249  // now execute the generated script compiling and generating the shared lib
3250  path = gSystem->WorkingDirectory();
3251  gSystem->ChangeDirectory(clean_dirname.Data());
3252 #ifndef WIN32
3253  gSystem->Exec("chmod +x MAKEP");
3254  int res = !gSystem->Exec("./MAKEP");
3255 #else
3256  // not really needed for Windows but it would work both both Unix and NT
3257  chmod("makep.cmd",00700);
3258  int res = !gSystem->Exec("MAKEP");
3259 #endif
3260  gSystem->ChangeDirectory(path);
3261  path.Form("%s/%s.%s",clean_dirname.Data(),subdirname.Data(),gSystem->GetSoExt());
3262  if (res) printf("Shared lib %s has been generated\n",path.Data());
3263 
3264  //dynamically link the generated shared lib
3265  if (opt.Contains("++")) {
3266  res = !gSystem->Load(path);
3267  if (res) printf("Shared lib %s has been dynamically linked\n",path.Data());
3268  }
3269  }
3270 
3271  delete list;
3272  filelist->Delete();
3273  delete filelist;
3274 }
3275 
3276 ////////////////////////////////////////////////////////////////////////////////
3277 /// Create makefile at 'filemake' for PAR package 'pack'.
3278 ///
3279 /// Called by MakeProject when option 'par' is given.
3280 /// Return 0 on success, -1 on error.
3282 Int_t TFile::MakeProjectParMake(const char *pack, const char *filemake)
3283 {
3284  // Output file path must be defined
3285  if (!filemake || (filemake && strlen(filemake) <= 0)) {
3286  Error("MakeProjectParMake", "path for output file undefined!");
3287  return -1;
3288  }
3289 
3290  // Package name must be defined
3291  if (!pack || (pack && strlen(pack) <= 0)) {
3292  Error("MakeProjectParMake", "package name undefined!");
3293  return -1;
3294  }
3295 
3296 #ifdef R__WINGCC
3297  FILE *fmk = fopen(filemake, "wb");
3298 #else
3299  FILE *fmk = fopen(filemake, "w");
3300 #endif
3301  if (!fmk) {
3302  Error("MakeProjectParMake", "cannot create file '%s' (errno: %d)", filemake, TSystem::GetErrno());
3303  return -1;
3304  }
3305 
3306  // Fill the file now
3307  fprintf(fmk, "# Makefile for the ROOT test programs.\n");
3308  fprintf(fmk, "# This Makefile shows how to compile and link applications\n");
3309  fprintf(fmk, "# using the ROOT libraries on all supported platforms.\n");
3310  fprintf(fmk, "#\n");
3311  fprintf(fmk, "# Copyright (c) 2000 Rene Brun and Fons Rademakers\n");
3312  fprintf(fmk, "#\n");
3313  fprintf(fmk, "# Author: this makefile has been automatically generated via TFile::MakeProject\n");
3314  fprintf(fmk, "\n");
3315  fprintf(fmk, "include Makefile.arch\n");
3316  fprintf(fmk, "\n");
3317  fprintf(fmk, "#------------------------------------------------------------------------------\n");
3318  fprintf(fmk, "\n");
3319  fprintf(fmk, "PACKO = %sProjectSource.$(ObjSuf)\n", pack);
3320  fprintf(fmk, "PACKS = %sProjectSource.$(SrcSuf) %sProjectDict.$(SrcSuf)\n", pack, pack);
3321  fprintf(fmk, "PACKSO = lib%s.$(DllSuf)\n", pack);
3322  fprintf(fmk, "\n");
3323  fprintf(fmk, "ifeq ($(PLATFORM),win32)\n");
3324  fprintf(fmk, "PACKLIB = lib%s.lib\n", pack);
3325  fprintf(fmk, "else\n");
3326  fprintf(fmk, "PACKLIB = $(PACKSO)\n");
3327  fprintf(fmk, "endif\n");
3328  fprintf(fmk, "\n");
3329  fprintf(fmk, "OBJS = $(PACKO)\n");
3330  fprintf(fmk, "\n");
3331  fprintf(fmk, "PROGRAMS =\n");
3332  fprintf(fmk, "\n");
3333  fprintf(fmk, "#------------------------------------------------------------------------------\n");
3334  fprintf(fmk, "\n");
3335  fprintf(fmk, ".SUFFIXES: .$(SrcSuf) .$(ObjSuf) .$(DllSuf)\n");
3336  fprintf(fmk, "\n");
3337  fprintf(fmk, "all: $(PACKLIB)\n");
3338  fprintf(fmk, "\n");
3339  fprintf(fmk, "$(PACKSO): $(PACKO)\n");
3340  fprintf(fmk, "ifeq ($(ARCH),aix)\n");
3341  fprintf(fmk, "\t\t/usr/ibmcxx/bin/makeC++SharedLib $(OutPutOpt) $@ $(LIBS) -p 0 $^\n");
3342  fprintf(fmk, "else\n");
3343  fprintf(fmk, "ifeq ($(ARCH),aix5)\n");
3344  fprintf(fmk, "\t\t/usr/vacpp/bin/makeC++SharedLib $(OutPutOpt) $@ $(LIBS) -p 0 $^\n");
3345  fprintf(fmk, "else\n");
3346  fprintf(fmk, "ifeq ($(PLATFORM),macosx)\n");
3347  fprintf(fmk, "# We need to make both the .dylib and the .so\n");
3348  fprintf(fmk, "\t\t$(LD) $(SOFLAGS)$@ $(LDFLAGS) $^ $(OutPutOpt) $@ $(LIBS)\n");
3349  fprintf(fmk, "ifneq ($(subst $(MACOSX_MINOR),,1234),1234)\n");
3350  fprintf(fmk, "ifeq ($(MACOSX_MINOR),4)\n");
3351  fprintf(fmk, "\t\tln -sf $@ $(subst .$(DllSuf),.so,$@)\n");
3352  fprintf(fmk, "else\n");
3353  fprintf(fmk, "\t\t$(LD) -bundle -undefined $(UNDEFOPT) $(LDFLAGS) $^ \\\n");
3354  fprintf(fmk, "\t\t $(OutPutOpt) $(subst .$(DllSuf),.so,$@)\n");
3355  fprintf(fmk, "endif\n");
3356  fprintf(fmk, "endif\n");
3357  fprintf(fmk, "else\n");
3358  fprintf(fmk, "ifeq ($(PLATFORM),win32)\n");
3359  fprintf(fmk, "\t\tbindexplib $* $^ > $*.def\n");
3360  fprintf(fmk, "\t\tlib -nologo -MACHINE:IX86 $^ -def:$*.def \\\n");
3361  fprintf(fmk, "\t\t $(OutPutOpt)$(PACKLIB)\n");
3362  fprintf(fmk, "\t\t$(LD) $(SOFLAGS) $(LDFLAGS) $^ $*.exp $(LIBS) \\\n");
3363  fprintf(fmk, "\t\t $(OutPutOpt)$@\n");
3364  fprintf(fmk, "else\n");
3365  fprintf(fmk, "\t\t$(LD) $(SOFLAGS) $(LDFLAGS) $^ $(OutPutOpt) $@ $(LIBS) $(EXPLLINKLIBS)\n");
3366  fprintf(fmk, "endif\n");
3367  fprintf(fmk, "endif\n");
3368  fprintf(fmk, "endif\n");
3369  fprintf(fmk, "endif\n");
3370  fprintf(fmk, "\t\t@echo \"$@ done\"\n");
3371  fprintf(fmk, "\n");
3372  fprintf(fmk, "clean:\n");
3373  fprintf(fmk, "\t\t@rm -f $(OBJS) core\n");
3374  fprintf(fmk, "\n");
3375  fprintf(fmk, "distclean: clean\n");
3376  fprintf(fmk, "\t\t@rm -f $(PROGRAMS) $(PACKSO) $(PACKLIB) *Dict.* *.def *.exp \\\n");
3377  fprintf(fmk, "\t\t *.so *.lib *.dll *.d *.log .def so_locations\n");
3378  fprintf(fmk, "\t\t@rm -rf cxx_repository\n");
3379  fprintf(fmk, "\n");
3380  fprintf(fmk, "# Dependencies\n");
3381  fprintf(fmk, "\n");
3382  fprintf(fmk, "%sProjectSource.$(ObjSuf): %sProjectHeaders.h %sLinkDef.h %sProjectDict.$(SrcSuf)\n", pack, pack, pack, pack);
3383  fprintf(fmk, "\n");
3384  fprintf(fmk, "%sProjectDict.$(SrcSuf): %sProjectHeaders.h %sLinkDef.h\n", pack, pack, pack);
3385  fprintf(fmk, "\t\t@echo \"Generating dictionary $@...\"\n");
3386  fprintf(fmk, "\t\t@rootcint -f $@ $^\n");
3387  fprintf(fmk, "\n");
3388  fprintf(fmk, ".$(SrcSuf).$(ObjSuf):\n");
3389  fprintf(fmk, "\t\t$(CXX) $(CXXFLAGS) -c $<\n");
3390  fprintf(fmk, "\n");
3391 
3392  // Close the file
3393  fclose(fmk);
3394 
3395  // Done
3396  return 0;
3397 }
3398 
3399 ////////////////////////////////////////////////////////////////////////////////
3400 /// Create BUILD.sh and SETUP.C under 'proofinf' for PAR package 'pack'.
3401 /// Called by MakeProject when option 'par' is given.
3402 /// Return 0 on success, -1 on error.
3404 Int_t TFile::MakeProjectParProofInf(const char *pack, const char *proofinf)
3405 {
3406  // Output directory path must be defined ...
3407  if (!proofinf || (proofinf && strlen(proofinf) <= 0)) {
3408  Error("MakeProjectParProofInf", "directory path undefined!");
3409  return -1;
3410  }
3411 
3412  // ... and exist and be a directory
3413  Int_t rcst = 0;
3414  FileStat_t st;
3415  if ((rcst = gSystem->GetPathInfo(proofinf, st)) != 0 || !R_ISDIR(st.fMode)) {
3416  Error("MakeProjectParProofInf", "path '%s' %s", proofinf,
3417  ((rcst == 0) ? "is not a directory" : "does not exist"));
3418  return -1;
3419  }
3420 
3421  // Package name must be defined
3422  if (!pack || (pack && strlen(pack) <= 0)) {
3423  Error("MakeProjectParProofInf", "package name undefined!");
3424  return -1;
3425  }
3426 
3427  TString path;
3428 
3429  // The BUILD.sh first
3430  path.Form("%s/BUILD.sh", proofinf);
3431 #ifdef R__WINGCC
3432  FILE *f = fopen(path.Data(), "wb");
3433 #else
3434  FILE *f = fopen(path.Data(), "w");
3435 #endif
3436  if (!f) {
3437  Error("MakeProjectParProofInf", "cannot create file '%s' (errno: %d)",
3438  path.Data(), TSystem::GetErrno());
3439  return -1;
3440  }
3441 
3442  fprintf(f, "#! /bin/sh\n");
3443  fprintf(f, "# Build libEvent library.\n");
3444  fprintf(f, "\n");
3445  fprintf(f, "#\n");
3446  fprintf(f, "# The environment variables ROOTPROOFLITE and ROOTPROOFCLIENT can be used to\n");
3447  fprintf(f, "# adapt the script to the calling environment\n");
3448  fprintf(f, "#\n");
3449  fprintf(f, "# if test ! \"x$ROOTPROOFLITE\" = \"x\"; then\n");
3450  fprintf(f, "# echo \"event-BUILD: PROOF-Lite node (session has $ROOTPROOFLITE workers)\"\n");
3451  fprintf(f, "# elif test ! \"x$ROOTPROOFCLIENT\" = \"x\"; then\n");
3452  fprintf(f, "# echo \"event-BUILD: PROOF client\"\n");
3453  fprintf(f, "# else\n");
3454  fprintf(f, "# echo \"event-BUILD: standard PROOF node\"\n");
3455  fprintf(f, "# fi\n");
3456  fprintf(f, "\n");
3457  fprintf(f, "if [ \"\" = \"clean\" ]; then\n");
3458  fprintf(f, " make distclean\n");
3459  fprintf(f, " exit 0\n");
3460  fprintf(f, "fi\n");
3461  fprintf(f, "\n");
3462  fprintf(f, "make\n");
3463  fprintf(f, "rc=$?\n");
3464  fprintf(f, "echo \"rc=$?\"\n");
3465  fprintf(f, "if [ $? != \"0\" ] ; then\n");
3466  fprintf(f, " exit 1\n");
3467  fprintf(f, "fi\n");
3468  fprintf(f, "exit 0\n");
3469 
3470  // Close the file
3471  fclose(f);
3472 
3473  // Then SETUP.C
3474  path.Form("%s/SETUP.C", proofinf);
3475 #ifdef R__WINGCC
3476  f = fopen(path.Data(), "wb");
3477 #else
3478  f = fopen(path.Data(), "w");
3479 #endif
3480  if (!f) {
3481  Error("MakeProjectParProofInf", "cannot create file '%s' (errno: %d)",
3482  path.Data(), TSystem::GetErrno());
3483  return -1;
3484  }
3485 
3486  fprintf(f, "Int_t SETUP()\n");
3487  fprintf(f, "{\n");
3488  fprintf(f, "\n");
3489  fprintf(f, "//\n");
3490  fprintf(f, "// The environment variables ROOTPROOFLITE and ROOTPROOFCLIENT can be used to\n");
3491  fprintf(f, "// adapt the macro to the calling environment\n");
3492  fprintf(f, "//\n");
3493  fprintf(f, "// if (gSystem->Getenv(\"ROOTPROOFLITE\")) {\n");
3494  fprintf(f, "// Printf(\"event-SETUP: PROOF-Lite node (session has %%s workers)\",\n");
3495  fprintf(f, "// gSystem->Getenv(\"ROOTPROOFLITE\"));\n");
3496  fprintf(f, "// } else if (gSystem->Getenv(\"ROOTPROOFCLIENT\")) {\n");
3497  fprintf(f, "// Printf(\"event-SETUP: PROOF client\");\n");
3498  fprintf(f, "// } else {\n");
3499  fprintf(f, "// Printf(\"event-SETUP: standard PROOF node\");\n");
3500  fprintf(f, "// }\n");
3501  fprintf(f, "\n");
3502  fprintf(f, " if (gSystem->Load(\"lib%s\") == -1)\n", pack);
3503  fprintf(f, " return -1;\n");
3504  fprintf(f, " return 0;\n");
3505  fprintf(f, "}\n");
3506  fprintf(f, "\n");
3507 
3508  // Close the file
3509  fclose(f);
3510 
3511  // Done
3512  return 0;
3513 }
3514 
3515 ////////////////////////////////////////////////////////////////////////////////
3516 /// Read the list of StreamerInfo from this file.
3517 ///
3518 /// The key with name holding the list of TStreamerInfo objects is read.
3519 /// The corresponding TClass objects are updated.
3520 /// Note that this function is not called if the static member fgReadInfo is false.
3521 /// (see TFile::SetReadStreamerInfo)
3524 {
3525  auto listRetcode = GetStreamerInfoListImpl(/*lookupSICache*/ true); // NOLINT: silence clang-tidy warnings
3526  TList *list = listRetcode.fList;
3527  auto retcode = listRetcode.fReturnCode;
3528  if (!list) {
3529  if (retcode) MakeZombie();
3530  return;
3531  }
3532 
3533  list->SetOwner(kFALSE);
3534 
3535  if (gDebug > 0) Info("ReadStreamerInfo", "called for file %s",GetName());
3536 
3537  TStreamerInfo *info;
3538 
3539  Int_t version = fVersion;
3540  if (version > 1000000) version -= 1000000;
3541  if (version < 53419 || (59900 < version && version < 59907)) {
3542  // We need to update the fCheckSum field of the TStreamerBase.
3543 
3544  // loop on all TStreamerInfo classes
3545  TObjLink *lnk = list->FirstLink();
3546  while (lnk) {
3547  info = (TStreamerInfo*)lnk->GetObject();
3548  if (!info || info->IsA() != TStreamerInfo::Class()) {
3549  lnk = lnk->Next();
3550  continue;
3551  }
3552  TIter next(info->GetElements());
3553  TStreamerElement *element;
3554  while ((element = (TStreamerElement*) next())) {
3555  TStreamerBase *base = dynamic_cast<TStreamerBase*>(element);
3556  if (!base) continue;
3557  if (base->GetBaseCheckSum() != 0) continue;
3558  TStreamerInfo *baseinfo = (TStreamerInfo*)list->FindObject(base->GetName());
3559  if (baseinfo) {
3560  base->SetBaseCheckSum(baseinfo->GetCheckSum());
3561  }
3562  }
3563  lnk = lnk->Next();
3564  }
3565  }
3566 
3567  // loop on all TStreamerInfo classes
3568  for (int mode=0;mode<2; ++mode) {
3569  // In order for the collection proxy to be initialized properly, we need
3570  // to setup the TStreamerInfo for non-stl class before the stl classes.
3571  TObjLink *lnk = list->FirstLink();
3572  while (lnk) {
3573  info = (TStreamerInfo*)lnk->GetObject();
3574  if (!info) {
3575  lnk = lnk->Next();
3576  continue;
3577  }
3578  if (info->IsA() != TStreamerInfo::Class()) {
3579  if (mode==1) {
3580  TObject *obj = (TObject*)info;
3581  if (strcmp(obj->GetName(),"listOfRules")==0) {
3582 #if 0
3583  // Completely ignore the rules for now.
3584  TList *listOfRules = (TList*)obj;
3585  TObjLink *rulelnk = listOfRules->FirstLink();
3586  while (rulelnk) {
3587  TObjString *rule = (TObjString*)rulelnk->GetObject();
3588  TClass::AddRule( rule->String().Data() );
3589  rulelnk = rulelnk->Next();
3590  }
3591 #endif
3592  } else {
3593  Warning("ReadStreamerInfo","%s has a %s in the list of TStreamerInfo.", GetName(), info->IsA()->GetName());
3594  }
3595  info->SetBit(kCanDelete);
3596  }
3597  lnk = lnk->Next();
3598  continue;
3599  }
3600  // This is a quick way (instead of parsing the name) to see if this is
3601  // the description of an STL container.
3602  if (info->GetElements()==0) {
3603  Warning("ReadStreamerInfo","The StreamerInfo for %s does not have a list of elements.",info->GetName());
3604  lnk = lnk->Next();
3605  continue;
3606  }
3607  TObject *element = info->GetElements()->UncheckedAt(0);
3608  Bool_t isstl = element && strcmp("This",element->GetName())==0;
3609 
3610  if ( (!isstl && mode ==0) || (isstl && mode ==1) ) {
3611  // Skip the STL container the first time around
3612  // Skip the regular classes the second time around;
3613  info->BuildCheck(this);
3614  Int_t uid = info->GetNumber();
3615  Int_t asize = fClassIndex->GetSize();
3616  if (uid >= asize && uid <100000) fClassIndex->Set(2*asize);
3617  if (uid >= 0 && uid < fClassIndex->GetSize()) fClassIndex->fArray[uid] = 1;
3618  else if (!isstl && !info->GetClass()->IsSyntheticPair()) {
3619  printf("ReadStreamerInfo, class:%s, illegal uid=%d\n",info->GetName(),uid);
3620  }
3621  if (gDebug > 0) printf(" -class: %s version: %d info read at slot %d\n",info->GetName(), info->GetClassVersion(),uid);
3622  }
3623  lnk = lnk->Next();
3624  }
3625  }
3626  fClassIndex->fArray[0] = 0;
3627  list->Clear(); //this will delete all TStreamerInfo objects with kCanDelete bit set
3628  delete list;
3629 
3630 #ifdef R__USE_IMT
3631  // We are done processing the record, let future calls and other threads that it
3632  // has been done.
3633  fgTsSIHashes.Insert(listRetcode.fHash);
3634 #endif
3635 }
3636 
3637 ////////////////////////////////////////////////////////////////////////////////
3638 /// Specify if the streamerinfos must be read at file opening.
3639 ///
3640 /// If fgReadInfo is true (default) TFile::ReadStreamerInfo is called
3641 /// when opening the file.
3642 /// It may be interesting to set fgReadInfo to false to speedup the file
3643 /// opening time or in case libraries containing classes referenced
3644 /// by the file have not yet been loaded.
3645 /// if fgReadInfo is false, one can still read the StreamerInfo with
3646 /// myfile.ReadStreamerInfo();
3648 void TFile::SetReadStreamerInfo(Bool_t readinfo)
3649 {
3650  fgReadInfo = readinfo;
3651 }
3652 
3653 ////////////////////////////////////////////////////////////////////////////////
3654 /// If the streamerinfos are to be read at file opening.
3655 ///
3656 /// See TFile::SetReadStreamerInfo for more documentation.
3659 {
3660  return fgReadInfo;
3661 }
3662 
3663 ////////////////////////////////////////////////////////////////////////////////
3664 /// Show the StreamerInfo of all classes written to this file.
3667 {
3668  TList *list = GetStreamerInfoList();
3669  if (!list) return;
3670 
3671  list->ls();
3672  delete list;
3673 }
3674 
3675 ////////////////////////////////////////////////////////////////////////////////
3676 /// Check if the ProcessID pidd is already in the file,
3677 /// if not, add it and return the index number in the local file list.
3680 {
3681  TProcessID *pid = pidd;
3682  if (!pid) pid = TProcessID::GetPID();
3683  TObjArray *pids = GetListOfProcessIDs();
3684  Int_t npids = GetNProcessIDs();
3685  for (Int_t i=0;i<npids;i++) {
3686  if (pids->At(i) == pid) return (UShort_t)i;
3687  }
3688 
3690  pids->AddAtAndExpand(pid,npids);
3691  pid->IncrementCount();
3692  char name[32];
3693  snprintf(name,32,"ProcessID%d",npids);
3694  this->WriteTObject(pid,name);
3695  this->IncrementProcessIDs();
3696  if (gDebug > 0) {
3697  Info("WriteProcessID", "name=%s, file=%s", name, GetName());
3698  }
3699  return (UShort_t)npids;
3700 }
3701 
3702 
3703 ////////////////////////////////////////////////////////////////////////////////
3704 /// Write the list of TStreamerInfo as a single object in this file
3705 /// The class Streamer description for all classes written to this file
3706 /// is saved. See class TStreamerInfo.
3709 {
3710  //if (!gFile) return;
3711  if (!fWritable) return;
3712  if (!fClassIndex) return;
3713  if (fIsPcmFile) return; // No schema evolution for ROOT PCM files.
3714  if (fClassIndex->fArray[0] == 0
3715  && fSeekInfo != 0) {
3716  // No need to update the index if no new classes added to the file
3717  // but write once an empty StreamerInfo list to mark that there is no need
3718  // for StreamerInfos in this file.
3719  return;
3720  }
3721  if (gDebug > 0) Info("WriteStreamerInfo", "called for file %s",GetName());
3722 
3724 
3725  // build a temporary list with the marked files
3726  TIter next(gROOT->GetListOfStreamerInfo());
3727  TStreamerInfo *info;
3728  TList list;
3729  TList listOfRules;
3730  listOfRules.SetOwner(kTRUE);
3731  listOfRules.SetName("listOfRules");
3732  std::set<TClass*> classSet;
3733 
3734  while ((info = (TStreamerInfo*)next())) {
3735  Int_t uid = info->GetNumber();
3736  if (fClassIndex->fArray[uid]) {
3737  list.Add(info);
3738  if (gDebug > 0) printf(" -class: %s info number %d saved\n",info->GetName(),uid);
3739 
3740  // Add the IO customization rules to the list to be saved for the underlying
3741  // class but make sure to add them only once.
3742  TClass *clinfo = info->GetClass();
3743  if (clinfo && clinfo->GetSchemaRules()) {
3744  if ( classSet.find( clinfo ) == classSet.end() ) {
3745  if (gDebug > 0) printf(" -class: %s stored the I/O customization rules\n",info->GetName());
3746 
3747  TObjArrayIter it( clinfo->GetSchemaRules()->GetRules() );
3748  ROOT::TSchemaRule *rule;
3749  while( (rule = (ROOT::TSchemaRule*)it.Next()) ) {
3750  TObjString *obj = new TObjString();
3751  rule->AsString(obj->String());
3752  listOfRules.Add(obj);
3753  }
3754  classSet.insert(clinfo);
3755  }
3756  }
3757  }
3758  }
3759 
3760  // Write the StreamerInfo list even if it is empty.
3761  fClassIndex->fArray[0] = 2; //to prevent adding classes in TStreamerInfo::TagFile
3762 
3763  if (listOfRules.GetEntries()) {
3764  // Only add the list of rules if we have something to say.
3765  list.Add(&listOfRules);
3766  }
3767 
3768  //free previous StreamerInfo record
3770  //Create new key
3771  TKey key(&list,"StreamerInfo",GetBestBuffer(), this);
3772  fKeys->Remove(&key);
3773  fSeekInfo = key.GetSeekKey();
3774  fNbytesInfo = key.GetNbytes();
3775  SumBuffer(key.GetObjlen());
3776  key.WriteFile(0);
3777 
3778  fClassIndex->fArray[0] = 0;
3779 
3780  list.RemoveLast(); // remove the listOfRules.
3781 }
3782 
3783 ////////////////////////////////////////////////////////////////////////////////
3784 /// Open a file for reading through the file cache.
3785 ///
3786 /// The file will be downloaded to the cache and opened from there.
3787 /// If the download fails, it will be opened remotely.
3788 /// The file will be downloaded to the directory specified by SetCacheFileDir().
3790 TFile *TFile::OpenFromCache(const char *name, Option_t *, const char *ftitle,
3791  Int_t compress, Int_t netopt)
3792 {
3793  TFile *f = nullptr;
3794 
3795  if (fgCacheFileDir == "") {
3796  ::Warning("TFile::OpenFromCache",
3797  "you want to read through a cache, but you have no valid cache "
3798  "directory set - reading remotely");
3799  ::Info("TFile::OpenFromCache", "set cache directory using TFile::SetCacheFileDir()");
3800  } else {
3801  TUrl fileurl(name);
3802  TUrl tagurl;
3803 
3804  if ((!strcmp(fileurl.GetProtocol(), "file"))) {
3805  // it makes no sense to read local files through a file cache
3806  if (!fgCacheFileForce)
3807  ::Warning("TFile::OpenFromCache",
3808  "you want to read through a cache, but you are reading "
3809  "local files - CACHEREAD disabled");
3810  } else {
3811  // this is a remote file and worthwhile to be put into the local cache
3812  // now create cachepath to put it
3813  TString cachefilepath;
3814  TString cachefilepathbasedir;
3815  cachefilepath = fgCacheFileDir;
3816  cachefilepath += fileurl.GetFile();
3817  cachefilepathbasedir = gSystem->GetDirName(cachefilepath);
3818  if ((gSystem->mkdir(cachefilepathbasedir, kTRUE) < 0) &&
3819  (gSystem->AccessPathName(cachefilepathbasedir, kFileExists))) {
3820  ::Warning("TFile::OpenFromCache","you want to read through a cache, but I "
3821  "cannot create the directory %s - CACHEREAD disabled",
3822  cachefilepathbasedir.Data());
3823  } else {
3824  // check if this should be a zip file
3825  if (strlen(fileurl.GetAnchor())) {
3826  // remove the anchor and change the target name
3827  cachefilepath += "__";
3828  cachefilepath += fileurl.GetAnchor();
3829  fileurl.SetAnchor("");
3830  }
3831  if (strstr(name,"zip=")) {
3832  // filter out this option and change the target cache name
3833  TString urloptions = fileurl.GetOptions();
3834  TString newoptions;
3835  TObjArray *objOptions = urloptions.Tokenize("&");
3836  Int_t optioncount = 0;
3837  TString zipname;
3838  for (Int_t n = 0; n < objOptions->GetEntries(); n++) {
3839  TString loption = ((TObjString*)objOptions->At(n))->GetName();
3840  TObjArray *objTags = loption.Tokenize("=");
3841  if (objTags->GetEntries() == 2) {
3842  TString key = ((TObjString*)objTags->At(0))->GetName();
3843  TString value = ((TObjString*)objTags->At(1))->GetName();
3844  if (key.CompareTo("zip", TString::kIgnoreCase)) {
3845  if (optioncount!=0) {
3846  newoptions += "&";
3847  }
3848  newoptions += key;
3849  newoptions += "=";
3850  newoptions += value;
3851  ++optioncount;
3852  } else {
3853  zipname = value;
3854  }
3855  }
3856  delete objTags;
3857  }
3858  delete objOptions;
3859  fileurl.SetOptions(newoptions.Data());
3860  cachefilepath += "__";
3861  cachefilepath += zipname;
3862  fileurl.SetAnchor("");
3863  }
3864 
3865  Bool_t need2copy = kFALSE;
3866 
3867  // check if file is in the cache
3868  Long_t id;
3869  Long64_t size;
3870  Long_t flags;
3871  Long_t modtime;
3872  if (!gSystem->GetPathInfo(cachefilepath, &id, &size, &flags, &modtime)) {
3873  // file is in the cache
3874  if (!fgCacheFileDisconnected) {
3875  char cacheblock[256];
3876  char remotblock[256];
3877  // check the remote file for it's size and compare some magic bytes
3878  TString cfurl;
3879  cfurl = cachefilepath;
3880  cfurl += "?filetype=raw";
3881  TUrl rurl(name);
3882  TString ropt = rurl.GetOptions();
3883  ropt += "&filetype=raw";
3884  rurl.SetOptions(ropt);
3885 
3886  Bool_t forcedcache = fgCacheFileForce;
3888 
3889  TFile *cachefile = TFile::Open(cfurl, "READ");
3890  TFile *remotfile = TFile::Open(rurl.GetUrl(), "READ");
3891 
3892  fgCacheFileForce = forcedcache;
3893 
3894  if (!cachefile) {
3895  need2copy = kTRUE;
3896  ::Error("TFile::OpenFromCache",
3897  "cannot open the cache file to check cache consistency");
3898  return nullptr;
3899  }
3900 
3901  if (!remotfile) {
3902  ::Error("TFile::OpenFromCache",
3903  "cannot open the remote file to check cache consistency");
3904  return nullptr;
3905  }
3906 
3907  cachefile->Seek(0);
3908  remotfile->Seek(0);
3909 
3910  if ((!cachefile->ReadBuffer(cacheblock,256)) &&
3911  (!remotfile->ReadBuffer(remotblock,256))) {
3912  if (memcmp(cacheblock, remotblock, 256)) {
3913  ::Warning("TFile::OpenFromCache", "the header of the cache file "
3914  "differs from the remote file - forcing an update");
3915  need2copy = kTRUE;
3916  }
3917  } else {
3918  ::Warning("TFile::OpenFromCache", "the header of the cache and/or "
3919  "remote file are not readable - forcing an update");
3920  need2copy = kTRUE;
3921  }
3922 
3923  delete remotfile;
3924  delete cachefile;
3925  }
3926  } else {
3927  need2copy = kTRUE;
3928  }
3929 
3930  // try to fetch the file (disable now the forced caching)
3931  Bool_t forcedcache = fgCacheFileForce;
3933  if (need2copy && !TFile::Cp(name, cachefilepath)) {
3934  ::Warning("TFile::OpenFromCache", "you want to read through a cache, but I "
3935  "cannot make a cache copy of %s - CACHEREAD disabled",
3936  cachefilepathbasedir.Data());
3937  fgCacheFileForce = forcedcache;
3938  if (fgOpenTimeout != 0)
3939  return nullptr;
3940  } else {
3941  fgCacheFileForce = forcedcache;
3942  ::Info("TFile::OpenFromCache", "using local cache copy of %s [%s]",
3943  name, cachefilepath.Data());
3944  // finally we have the file and can open it locally
3945  fileurl.SetProtocol("file");
3946  fileurl.SetFile(cachefilepath);
3947 
3948  tagurl = fileurl;
3949  TString tagfile;
3950  tagfile = cachefilepath;
3951  tagfile += ".ROOT.cachefile";
3952  tagurl.SetFile(tagfile);
3953  // we symlink this file as a ROOT cached file
3954  gSystem->Symlink(gSystem->BaseName(cachefilepath), tagfile);
3955  return TFile::Open(fileurl.GetUrl(), "READ", ftitle, compress, netopt);
3956  }
3957  }
3958  }
3959  }
3960 
3961  // Failed
3962  return f;
3963 }
3964 
3965 ////////////////////////////////////////////////////////////////////////////////
3966 /// Create / open a file
3967 ///
3968 /// The type of the file can be either a
3969 /// TFile, TNetFile, TWebFile or any TFile derived class for which an
3970 /// plugin library handler has been registered with the plugin manager
3971 /// (for the plugin manager see the TPluginManager class). The returned
3972 /// type of TFile depends on the file name specified by 'url'.
3973 /// If 'url' is a '|'-separated list of file URLs, the 'URLs' are tried
3974 /// sequentially in the specified order until a successful open.
3975 /// If the file starts with "root:", "roots:" or "rootk:" a TNetFile object
3976 /// will be returned, with "http:" a TWebFile, with "file:" a local TFile,
3977 /// etc. (see the list of TFile plugin handlers in $ROOTSYS/etc/system.rootrc
3978 /// for regular expressions that will be checked) and as last a local file will
3979 /// be tried.
3980 /// Before opening a file via TNetFile a check is made to see if the URL
3981 /// specifies a local file. If that is the case the file will be opened
3982 /// via a normal TFile. To force the opening of a local file via a
3983 /// TNetFile use either TNetFile directly or specify as host "localhost".
3984 /// The netopt argument is only used by TNetFile. For the meaning of the
3985 /// options and other arguments see the constructors of the individual
3986 /// file classes. In case of error returns 0.
3987 ///
3988 /// For TFile implementations supporting asynchronous file open, see
3989 /// TFile::AsyncOpen(...), it is possible to request a timeout with the
3990 /// option <b>TIMEOUT=<secs></b>: the timeout must be specified in seconds and
3991 /// it will be internally checked with granularity of one millisec.
3992 /// For remote files there is the option: <b>CACHEREAD</b> opens an existing
3993 /// file for reading through the file cache. The file will be downloaded to
3994 /// the cache and opened from there. If the download fails, it will be opened remotely.
3995 /// The file will be downloaded to the directory specified by SetCacheFileDir().
3996 ///
3997 /// *The caller is responsible for deleting the pointer.*
3999 TFile *TFile::Open(const char *url, Option_t *options, const char *ftitle,
4000  Int_t compress, Int_t netopt)
4001 {
4002  TPluginHandler *h;
4003  TFile *f = nullptr;
4004  EFileType type = kFile;
4005 
4006  // Check input
4007  if (!url || strlen(url) <= 0) {
4008  ::Error("TFile::Open", "no url specified");
4009  return f;
4010  }
4011 
4012  TString expandedUrl(url);
4013  gSystem->ExpandPathName(expandedUrl);
4014 
4015  // If a timeout has been specified extract the value and try to apply it (it requires
4016  // support for asynchronous open, though; the following is completely transparent if
4017  // such support if not available for the required protocol)
4018  TString opts(options);
4019  Int_t ito = opts.Index("TIMEOUT=");
4020  if (ito != kNPOS) {
4021  TString sto = opts(ito + strlen("TIMEOUT="), opts.Length());
4022  while (!(sto.IsDigit()) && !(sto.IsNull())) { sto.Remove(sto.Length()-1,1); }
4023  if (!(sto.IsNull())) {
4024  // Timeout in millisecs
4025  Int_t toms = sto.Atoi() * 1000;
4026  if (gDebug > 0) ::Info("TFile::Open", "timeout of %d millisec requested", toms);
4027  // Remove from the options field
4028  sto.Insert(0, "TIMEOUT=");
4029  opts.ReplaceAll(sto, "");
4030  // Asynchrounous open
4031  TFileOpenHandle *fh = TFile::AsyncOpen(expandedUrl, opts, ftitle, compress, netopt);
4032  // Check the result in steps of 1 millisec
4034  aos = TFile::GetAsyncOpenStatus(fh);
4035  Int_t xtms = toms;
4036  while (aos == TFile::kAOSInProgress && xtms > 0) {
4037  gSystem->Sleep(1);
4038  xtms -= 1;
4039  aos = TFile::GetAsyncOpenStatus(fh);
4040  }
4041  if (aos == TFile::kAOSNotAsync || aos == TFile::kAOSSuccess) {
4042  // Do open the file now
4043  f = TFile::Open(fh);
4044  if (gDebug > 0) {
4045  if (aos == TFile::kAOSSuccess)
4046  ::Info("TFile::Open", "waited %d millisec for asynchronous open", toms - xtms);
4047  else
4048  ::Info("TFile::Open", "timeout option not supported (requires asynchronous"
4049  " open support)");
4050  }
4051  } else {
4052  if (xtms <= 0)
4053  ::Error("TFile::Open", "timeout expired while opening '%s'", expandedUrl.Data());
4054  // Cleanup the request
4055  SafeDelete(fh);
4056  }
4057  // Done
4058  return f;
4059  } else {
4060  ::Warning("TFile::Open", "incomplete 'TIMEOUT=' option specification - ignored");
4061  opts.ReplaceAll("TIMEOUT=", "");
4062  }
4063  }
4064 
4065  // We will use this from now on
4066  const char *option = opts;
4067 
4068  // Many URLs? Redirect output and print errors in case of global failure
4069  TString namelist(expandedUrl);
4070  Ssiz_t ip = namelist.Index("|");
4071  Bool_t rediroutput = (ip != kNPOS &&
4072  ip != namelist.Length()-1 && gDebug <= 0) ? kTRUE : kFALSE;
4073  RedirectHandle_t rh;
4074  if (rediroutput) {
4075  TString outf = ".TFileOpen_";
4076  FILE *fout = gSystem->TempFileName(outf);
4077  if (fout) {
4078  fclose(fout);
4079  gSystem->RedirectOutput(outf, "w", &rh);
4080  }
4081  }
4082 
4083  // Try sequentially all names in 'names'
4084  TString name, n;
4085  Ssiz_t from = 0;
4086  while (namelist.Tokenize(n, from, "|") && !f) {
4087 
4088  // check if we read through a file cache
4089  if (!strcasecmp(option, "CACHEREAD") ||
4090  ((!strcasecmp(option,"READ") || !option[0]) && fgCacheFileForce)) {
4091  // Try opening the file from the cache
4092  if ((f = TFile::OpenFromCache(n, option, ftitle, compress, netopt)))
4093  return f;
4094  }
4095 
4097 
4098  // change names to be recognized by the plugin manager
4099  // e.g. /protocol/path/to/file.root -> protocol:/path/to/file.root
4100  TUrl urlname(n, kTRUE);
4101  name = urlname.GetUrl();
4102  // Check first if a pending async open request matches this one
4105  TFileOpenHandle *fh = nullptr;
4106  while ((fh = (TFileOpenHandle *)nxr()))
4107  if (fh->Matches(name))
4108  return TFile::Open(fh);
4109  }
4110 
4111  TString urlOptions(urlname.GetOptions());
4112  if (urlOptions.BeginsWith("pmerge") || urlOptions.Contains("&pmerge") || urlOptions.Contains(" pmerge")) {
4113  type = kMerge;
4114 
4115  // Pass the full name including the url options:
4116  f = (TFile*) gROOT->ProcessLineFast(TString::Format("new TParallelMergingFile(\"%s\",\"%s\",\"%s\",%d)",n.Data(),option,ftitle,compress));
4117 
4118  } else {
4119  // Resolve the file type; this also adjusts names
4120  TString lfname = gEnv->GetValue("Path.Localroot", "");
4121  type = GetType(name, option, &lfname);
4122 
4123  if (type == kLocal) {
4124 
4125  // Local files
4126  if (lfname.IsNull()) {
4127  urlname.SetHost("");
4128  urlname.SetProtocol("file");
4129  lfname = urlname.GetUrl();
4130  }
4131  f = new TFile(lfname.Data(), option, ftitle, compress);
4132 
4133  } else if (type == kNet) {
4134 
4135  // Network files
4136  if ((h = gROOT->GetPluginManager()->FindHandler("TFile", name))) {
4137  if (h->LoadPlugin() == -1)
4138  return nullptr;
4139  f = (TFile*) h->ExecPlugin(5, name.Data(), option, ftitle, compress, netopt);
4140  }
4141 
4142  } else if (type == kWeb) {
4143 
4144  // Web files
4145  if ((h = gROOT->GetPluginManager()->FindHandler("TFile", name))) {
4146  if (h->LoadPlugin() == -1)
4147  return nullptr;
4148  f = (TFile*) h->ExecPlugin(2, name.Data(), option);
4149  }
4150 
4151  } else if (type == kFile) {
4152 
4153  // 'file:' protocol
4154  if ((h = gROOT->GetPluginManager()->FindHandler("TFile", name)) &&
4155  h->LoadPlugin() == 0) {
4156  name.ReplaceAll("file:", "");
4157  f = (TFile*) h->ExecPlugin(4, name.Data(), option, ftitle, compress);
4158  } else
4159  f = new TFile(name.Data(), option, ftitle, compress);
4160 
4161  } else {
4162 
4163  // no recognized specification: try the plugin manager
4164  if ((h = gROOT->GetPluginManager()->FindHandler("TFile", name.Data()))) {
4165  if (h->LoadPlugin() == -1)
4166  return nullptr;
4167  TClass *cl = TClass::GetClass(h->GetClass());
4168  if (cl && cl->InheritsFrom("TNetFile"))
4169  f = (TFile*) h->ExecPlugin(5, name.Data(), option, ftitle, compress, netopt);
4170  else
4171  f = (TFile*) h->ExecPlugin(4, name.Data(), option, ftitle, compress);
4172  } else {
4173  // Just try to open it locally but via TFile::Open, so that we pick-up the correct
4174  // plug-in in the case file name contains information about a special backend (e.g.
4175  f = TFile::Open(urlname.GetFileAndOptions(), option, ftitle, compress);
4176  }
4177  }
4178  }
4179 
4180  if (f && f->IsZombie()) {
4181  TString newUrl = f->GetNewUrl();
4182  delete f;
4183  if( newUrl.Length() && gEnv->GetValue("TFile.CrossProtocolRedirects", 1) )
4184  f = TFile::Open( newUrl, option, ftitle, compress );
4185  else
4186  f = nullptr;
4187  }
4188  }
4189 
4190  if (rediroutput) {
4191  // Restore output to stdout
4192  gSystem->RedirectOutput(0, "", &rh);
4193  // If we failed print error messages
4194  if (!f)
4195  gSystem->ShowOutput(&rh);
4196  // Remove the file
4197  gSystem->Unlink(rh.fFile);
4198  }
4199 
4200  // if the file is writable, non local, and not opened in raw mode
4201  // we create a default write cache of 512 KBytes
4202  if (type != kLocal && type != kFile &&
4203  f && f->IsWritable() && !f->IsRaw()) {
4204  new TFileCacheWrite(f, 1);
4205  }
4206 
4207  return f;
4208 }
4209 
4210 ////////////////////////////////////////////////////////////////////////////////
4211 /// Submit an asynchronous open request.
4212 
4213 /// See TFile::Open(const char *, ...) for an
4214 /// explanation of the arguments. A handler is returned which is to be passed
4215 /// to TFile::Open(TFileOpenHandle *) to get the real TFile instance once
4216 /// the file is open.
4217 /// This call never blocks and it is provided to allow parallel submission
4218 /// of file opening operations expected to take a long time.
4219 /// TFile::Open(TFileOpenHandle *) may block if the file is not yet ready.
4220 /// The sequence
4221 ///
4222 /// TFile::Open(TFile::AsyncOpen(const char *, ...))
4223 ///
4224 /// is equivalent to
4225 ///
4226 /// TFile::Open(const char *, ...)
4227 ///
4228 /// To be effective, the underlying TFile implementation must be able to
4229 /// support asynchronous open functionality. Currently, only TXNetFile
4230 /// supports it. If the functionality is not implemented, this call acts
4231 /// transparently by returning an handle with the arguments for the
4232 /// standard synchronous open run by TFile::Open(TFileOpenHandle *).
4233 /// The retuned handle will be adopted by TFile after opening completion
4234 /// in TFile::Open(TFileOpenHandle *); if opening is not finalized the
4235 /// handle must be deleted by the caller.
4237 TFileOpenHandle *TFile::AsyncOpen(const char *url, Option_t *option,
4238  const char *ftitle, Int_t compress,
4239  Int_t netopt)
4240 {
4241  TFileOpenHandle *fh = nullptr;
4242  TFile *f = nullptr;
4243  Bool_t notfound = kTRUE;
4244 
4245  // Check input
4246  if (!url || strlen(url) <= 0) {
4247  ::Error("TFile::AsyncOpen", "no url specified");
4248  return fh;
4249  }
4250 
4251  // Many URLs? Redirect output and print errors in case of global failure
4252  TString namelist(url);
4253  gSystem->ExpandPathName(namelist);
4254  Ssiz_t ip = namelist.Index("|");
4255  Bool_t rediroutput = (ip != kNPOS &&
4256  ip != namelist.Length()-1 && gDebug <= 0) ? kTRUE : kFALSE;
4257  RedirectHandle_t rh;
4258  if (rediroutput) {
4259  TString outf = ".TFileAsyncOpen_";
4260  FILE *fout = gSystem->TempFileName(outf);
4261  if (fout) {
4262  fclose(fout);
4263  gSystem->RedirectOutput(outf, "w", &rh);
4264  }
4265  }
4266 
4267  // Try sequentially all names in 'names'
4268  TString name, n;
4269  Ssiz_t from = 0;
4270  while (namelist.Tokenize(n, from, "|") && !f) {
4271 
4272  // change names to be recognized by the plugin manager
4273  // e.g. /protocol/path/to/file.root -> protocol:/path/to/file.root
4274  TUrl urlname(n, kTRUE);
4275  name = urlname.GetUrl();
4276 
4277  // Resolve the file type; this also adjusts names
4278  EFileType type = GetType(name, option);
4279 
4280  TPluginHandler *h = nullptr;
4281 
4282  // Here we send the asynchronous request if the functionality is implemented
4283  if (type == kNet) {
4284  // Network files
4285  if ((h = gROOT->GetPluginManager()->FindHandler("TFile", name)) &&
4286  (!strcmp(h->GetClass(),"TXNetFile") || !strcmp(h->GetClass(),"TNetXNGFile"))
4287  && h->LoadPlugin() == 0) {
4288  f = (TFile*) h->ExecPlugin(6, name.Data(), option, ftitle, compress, netopt, kTRUE);
4289  notfound = kFALSE;
4290  }
4291  }
4292  if ((h = gROOT->GetPluginManager()->FindHandler("TFile", name)) &&
4293  !strcmp(h->GetClass(),"TAlienFile") && h->LoadPlugin() == 0) {
4294  f = (TFile*) h->ExecPlugin(5, name.Data(), option, ftitle, compress, kTRUE);
4295  notfound = kFALSE;
4296  }
4297 
4298  }
4299 
4300  if (rediroutput) {
4301  // Restore output to stdout
4302  gSystem->RedirectOutput(0, "", &rh);
4303  // If we failed print error messages
4304  if (!notfound && !f)
4305  gSystem->ShowOutput(&rh);
4306  // Remove the file
4307  gSystem->Unlink(rh.fFile);
4308  }
4309 
4310  // Make sure that no error occurred
4311  if (notfound) {
4312  SafeDelete(f);
4313  // Save the arguments in the handler, so that a standard open can be
4314  // attempted later on
4315  fh = new TFileOpenHandle(name, option, ftitle, compress, netopt);
4316  } else if (f) {
4317  // Fill the opaque handler to be use to attach the file later on
4318  fh = new TFileOpenHandle(f);
4319  }
4320 
4321  // Record this request
4322  if (fh) {
4323  // Create the lst, if not done already
4324  if (!fgAsyncOpenRequests)
4325  fgAsyncOpenRequests = new TList;
4326  fgAsyncOpenRequests->Add(fh);
4327  }
4328 
4329  // We are done
4330  return fh;
4331 }
4332 
4333 ////////////////////////////////////////////////////////////////////////////////
4334 /// Waits for the completion of an asynchronous open request.
4335 ///
4336 /// Returns the pointer to the associated TFile, transferring ownership of the
4337 /// handle to the TFile instance.
4340 {
4341  TFile *f = nullptr;
4342 
4343  // Note that the request may have failed
4344  if (fh && fgAsyncOpenRequests) {
4345  // Remove it from the pending list: we need to do it at this level to avoid
4346  // recursive calls in the standard TFile::Open
4348  // Was asynchronous open functionality implemented?
4349  if ((f = fh->GetFile()) && !(f->IsZombie())) {
4350  // Yes: wait for the completion of the open phase, if needed
4351  Bool_t cr = (!strcmp(f->GetOption(),"CREATE") ||
4352  !strcmp(f->GetOption(),"RECREATE") ||
4353  !strcmp(f->GetOption(),"NEW")) ? kTRUE : kFALSE;
4354  f->Init(cr);
4355  } else {
4356  // No: process a standard open
4357  f = TFile::Open(fh->GetName(), fh->GetOpt(), fh->GetTitle(),
4358  fh->GetCompress(), fh->GetNetOpt());
4359  }
4360 
4361  // Adopt the handle instance in the TFile instance so that it gets
4362  // automatically cleaned up
4363  if (f) f->fAsyncHandle = fh;
4364  }
4365 
4366  // We are done
4367  return f;
4368 }
4369 
4370 ////////////////////////////////////////////////////////////////////////////////
4371 /// Interface to system open. All arguments like in POSIX open().
4373 Int_t TFile::SysOpen(const char *pathname, Int_t flags, UInt_t mode)
4374 {
4375 #if defined(R__WINGCC)
4376  // ALWAYS use binary mode - even cygwin text should be in unix format
4377  // although this is posix default it has to be set explicitly
4378  return ::open(pathname, flags | O_BINARY, mode);
4379 #elif defined(R__SEEK64)
4380  return ::open64(pathname, flags, mode);
4381 #else
4382  return ::open(pathname, flags, mode);
4383 #endif
4384 }
4385 
4386 ////////////////////////////////////////////////////////////////////////////////
4387 /// Interface to system close. All arguments like in POSIX close().
4390 {
4391  if (fd < 0) return 0;
4392  return ::close(fd);
4393 }
4394 
4395 ////////////////////////////////////////////////////////////////////////////////
4396 /// Interface to system read. All arguments like in POSIX read().
4398 Int_t TFile::SysRead(Int_t fd, void *buf, Int_t len)
4399 {
4400  return ::read(fd, buf, len);
4401 }
4402 
4403 ////////////////////////////////////////////////////////////////////////////////
4404 /// Interface to system write. All arguments like in POSIX write().
4406 Int_t TFile::SysWrite(Int_t fd, const void *buf, Int_t len)
4407 {
4408  return ::write(fd, buf, len);
4409 }
4410 ////////////////////////////////////////////////////////////////////////////////
4411 /// Interface to system lseek.
4412 ///
4413 /// All arguments like in POSIX lseek()
4414 /// except that the offset and return value are of a type which are
4415 /// able to handle 64 bit file systems.
4417 Long64_t TFile::SysSeek(Int_t fd, Long64_t offset, Int_t whence)
4418 {
4419 #if defined (R__SEEK64)
4420  return ::lseek64(fd, offset, whence);
4421 #elif defined(WIN32)
4422  return ::_lseeki64(fd, offset, whence);
4423 #else
4424  return ::lseek(fd, offset, whence);
4425 #endif
4426 }
4427 
4428 ////////////////////////////////////////////////////////////////////////////////
4429 /// Return file stat information.
4430 ///
4431 /// The interface and return value is
4432 /// identical to TSystem::GetPathInfo(). The function returns 0 in
4433 /// case of success and 1 if the file could not be stat'ed.
4436  Long_t *modtime)
4437 {
4438  return gSystem->GetPathInfo(fRealName, id, size, flags, modtime);
4439 }
4440 
4441 ////////////////////////////////////////////////////////////////////////////////
4442 /// Interface to system fsync. All arguments like in POSIX fsync().
4445 {
4446  if (TestBit(kDevNull)) return 0;
4447 
4448 #ifndef WIN32
4449  return ::fsync(fd);
4450 #else
4451  return ::_commit(fd);
4452 #endif
4453 }
4454 
4455 ////////////////////////////////////////////////////////////////////////////////
4456 /// Return the total number of bytes written so far to the file.
4459 {
4461 }
4462 
4463 ////////////////////////////////////////////////////////////////////////////////
4464 /// Static function returning the total number of bytes read from all files.
4467 {
4468  return fgBytesRead;
4469 }
4470 
4471 ////////////////////////////////////////////////////////////////////////////////
4472 /// Static function returning the total number of bytes written to all files.
4473 /// Does not take into account what might still be in the write caches.
4476 {
4477  return fgBytesWrite;
4478 }
4479 
4480 ////////////////////////////////////////////////////////////////////////////////
4481 /// Static function returning the total number of read calls from all files.
4484 {
4485  return fgReadCalls;
4486 }
4487 
4488 ////////////////////////////////////////////////////////////////////////////////
4489 /// Static function returning the readahead buffer size.
4492 {
4493  return fgReadaheadSize;
4494 }
4495 
4496 //______________________________________________________________________________
4497 void TFile::SetReadaheadSize(Int_t bytes) { fgReadaheadSize = bytes; }
4498 
4499 //______________________________________________________________________________
4500 void TFile::SetFileBytesRead(Long64_t bytes) { fgBytesRead = bytes; }
4501 
4502 //______________________________________________________________________________
4503 void TFile::SetFileBytesWritten(Long64_t bytes) { fgBytesWrite = bytes; }
4504 
4505 //______________________________________________________________________________
4506 void TFile::SetFileReadCalls(Int_t readcalls) { fgReadCalls = readcalls; }
4507 
4508 //______________________________________________________________________________
4510 
4511 //______________________________________________________________________________
4513 
4514 ////////////////////////////////////////////////////////////////////////////////
4515 /// Sets the directory where to locally stage/cache remote files.
4516 /// If the directory is not writable by us return kFALSE.
4518 Bool_t TFile::SetCacheFileDir(std::string_view cachedir, Bool_t operatedisconnected,
4519  Bool_t forcecacheread )
4520 {
4521  TString cached{cachedir};
4522  if (!cached.EndsWith("/"))
4523  cached += "/";
4524 
4525  if (gSystem->AccessPathName(cached, kFileExists)) {
4526  // try to create it
4527  gSystem->mkdir(cached, kTRUE);
4528  if (gSystem->AccessPathName(cached, kFileExists)) {
4529  ::Error("TFile::SetCacheFileDir", "no sufficient permissions on cache directory %s or cannot create it", TString(cachedir).Data());
4530  fgCacheFileDir = "";
4531  return kFALSE;
4532  }
4533  gSystem->Chmod(cached, 0700);
4534  }
4535  if (gSystem->AccessPathName(cached, kWritePermission))
4536  gSystem->Chmod(cached, 0700);
4537  fgCacheFileDir = cached;
4538  fgCacheFileDisconnected = operatedisconnected;
4539  fgCacheFileForce = forcecacheread;
4540  return kTRUE;
4541 }
4542 
4543 ////////////////////////////////////////////////////////////////////////////////
4544 /// Get the directory where to locally stage/cache remote files.
4546 const char *TFile::GetCacheFileDir()
4547 {
4548  return fgCacheFileDir;
4549 }
4550 
4551 ////////////////////////////////////////////////////////////////////////////////
4552 /// Try to shrink the cache to the desired size.
4553 ///
4554 /// With the clenupinterval you can specify the minimum amount of time after
4555 /// the previous cleanup before the cleanup operation is repeated in
4556 /// the cache directory
4558 Bool_t TFile::ShrinkCacheFileDir(Long64_t shrinksize, Long_t cleanupinterval)
4559 {
4560  if (fgCacheFileDir == "") {
4561  return kFALSE;
4562  }
4563 
4564  // check the last clean-up in the cache
4565  Long_t id;
4566  Long64_t size;
4567  Long_t flags;
4568  Long_t modtime;
4569 
4570  TString cachetagfile = fgCacheFileDir;
4571  cachetagfile += ".tag.ROOT.cache";
4572  if (!gSystem->GetPathInfo(cachetagfile, &id, &size, &flags, &modtime)) {
4573  // check the time passed since last cache cleanup
4574  Long_t lastcleanuptime = ((Long_t)time(0) - modtime);
4575  if (lastcleanuptime < cleanupinterval) {
4576  ::Info("TFile::ShrinkCacheFileDir", "clean-up is skipped - last cleanup %lu seconds ago - you requested %lu", lastcleanuptime, cleanupinterval);
4577  return kTRUE;
4578  }
4579  }
4580 
4581  // (re-)create the cache tag file
4582  cachetagfile += "?filetype=raw";
4583  TFile *tagfile = nullptr;
4584 
4585  if (!(tagfile = TFile::Open(cachetagfile, "RECREATE"))) {
4586  ::Error("TFile::ShrinkCacheFileDir", "cannot create the cache tag file %s", cachetagfile.Data());
4587  return kFALSE;
4588  }
4589 
4590  // the shortest garbage collector in the world - one long line of PERL - unlinks files only,
4591  // if there is a symbolic link with '.ROOT.cachefile' for safety ;-)
4592 
4593  TString cmd;
4594 #if defined(R__WIN32)
4595  cmd = "echo <TFile::ShrinkCacheFileDir>: cleanup to be implemented";
4596 #elif defined(R__MACOSX)
4597  cmd.Form("perl -e 'my $cachepath = \"%s\"; my $cachesize = %lld;my $findcommand=\"find $cachepath -type f -exec stat -f \\\"\\%%a::\\%%N::\\%%z\\\" \\{\\} \\\\\\;\";my $totalsize=0;open FIND, \"$findcommand | sort -k 1 |\";while (<FIND>) { my ($accesstime, $filename, $filesize) = split \"::\",$_; $totalsize += $filesize;if ($totalsize > $cachesize) {if ( ( -e \"${filename}.ROOT.cachefile\" ) || ( -e \"${filename}\" ) ) {unlink \"$filename.ROOT.cachefile\";unlink \"$filename\";}}}close FIND;' ", fgCacheFileDir.Data(),shrinksize);
4598 #else
4599  cmd.Form("perl -e 'my $cachepath = \"%s\"; my $cachesize = %lld;my $findcommand=\"find $cachepath -type f -exec stat -c \\\"\\%%x::\\%%n::\\%%s\\\" \\{\\} \\\\\\;\";my $totalsize=0;open FIND, \"$findcommand | sort -k 1 |\";while (<FIND>) { my ($accesstime, $filename, $filesize) = split \"::\",$_; $totalsize += $filesize;if ($totalsize > $cachesize) {if ( ( -e \"${filename}.ROOT.cachefile\" ) || ( -e \"${filename}\" ) ) {unlink \"$filename.ROOT.cachefile\";unlink \"$filename\";}}}close FIND;' ", fgCacheFileDir.Data(),shrinksize);
4600 #endif
4601 
4602  tagfile->WriteBuffer(cmd, 4096);
4603  delete tagfile;
4604 
4605  if ((gSystem->Exec(cmd)) != 0) {
4606  ::Error("TFile::ShrinkCacheFileDir", "error executing clean-up script");
4607  return kFALSE;
4608  }
4609 
4610  return kTRUE;
4611 }
4612 
4613 ////////////////////////////////////////////////////////////////////////////////
4614 /// Sets open timeout time (in ms). Returns previous timeout value.
4617 {
4618  UInt_t to = fgOpenTimeout;
4619  fgOpenTimeout = timeout;
4620  return to;
4621 }
4622 
4623 ////////////////////////////////////////////////////////////////////////////////
4624 /// Returns open timeout (in ms).
4627 {
4628  return fgOpenTimeout;
4629 }
4630 
4631 ////////////////////////////////////////////////////////////////////////////////
4632 /// Sets only staged flag. Returns previous value of flag.
4633 /// When true we check before opening the file if it is staged, if not,
4634 /// the open fails.
4637 {
4638  Bool_t f = fgOnlyStaged;
4639  fgOnlyStaged = onlystaged;
4640  return f;
4641 }
4642 
4643 ////////////////////////////////////////////////////////////////////////////////
4644 /// Returns staged only flag.
4647 {
4648  return fgOnlyStaged;
4649 }
4650 
4651 ////////////////////////////////////////////////////////////////////////////////
4652 /// Return kTRUE if 'url' matches the coordinates of this file.
4653 ///
4654 /// The check is implementation dependent and may need to be overload
4655 /// by each TFile implementation relying on this check.
4656 /// The default implementation checks the file name only.
4658 Bool_t TFile::Matches(const char *url)
4659 {
4660  // Check the full URL, including port and FQDN.
4661  TUrl u(url);
4662 
4663  // Check
4664  if (!strcmp(u.GetFile(), fUrl.GetFile())) {
4665  // Check ports
4666  if (u.GetPort() == fUrl.GetPort()) {
4667  if (!strcmp(u.GetHostFQDN(), fUrl.GetHostFQDN())) {
4668  // Ok, coordinates match
4669  return kTRUE;
4670  }
4671  }
4672  }
4673 
4674  // Default is not matching
4675  return kFALSE;
4676 }
4677 
4678 ////////////////////////////////////////////////////////////////////////////////
4679 /// Return kTRUE if this async request matches the open request
4680 /// specified by 'url'
4682 Bool_t TFileOpenHandle::Matches(const char *url)
4683 {
4684  if (fFile) {
4685  return fFile->Matches(url);
4686  } else if (fName.Length() > 0){
4687  // Deep check of URLs
4688  TUrl u(url);
4689  TUrl uref(fName);
4690  if (!strcmp(u.GetFile(), uref.GetFile())) {
4691  // Check ports
4692  if (u.GetPort() == uref.GetPort()) {
4693  // Check also the host name
4694  if (!strcmp(u.GetHostFQDN(), uref.GetHostFQDN())) {
4695  // Ok, coordinates match
4696  return kTRUE;
4697  }
4698  }
4699  }
4700  }
4701 
4702  // Default is not matching
4703  return kFALSE;
4704 }
4705 
4706 ////////////////////////////////////////////////////////////////////////////////
4707 /// Resolve the file type as a function of the protocol field in 'name'
4708 ///
4709 /// If defined, the string 'prefix' is added when testing the locality of
4710 /// a 'name' with network-like structure (i.e. root://host//path); if the file
4711 /// is local, on return 'prefix' will contain the actual local path of the file.
4713 TFile::EFileType TFile::GetType(const char *name, Option_t *option, TString *prefix)
4714 {
4716 
4717  TPMERegexp re("^(root|xroot).*", "i");
4718  if (re.Match(name)) {
4719  //
4720  // Should be a network file ...
4721  type = kNet;
4722  // ... but make sure that is not local or that a remote-like connection
4723  // is forced. Treat it as local if:
4724  // i) the url points to the localhost, the file will be opened in
4725  // readonly mode and the current user has read access;
4726  // ii) the specified user is equal to the current user then open local
4727  // TFile.
4728  Bool_t localFile = kFALSE;
4729  TUrl url(name);
4730  //
4731  // Check whether we should try to optimize for local files
4732  Bool_t forceRemote = gEnv->GetValue("Path.ForceRemote", 0);
4733  forceRemote = (forceRemote) ? kTRUE : gEnv->GetValue("TFile.ForceRemote", 0);
4734  TString opts = url.GetOptions();
4735  if (opts.Contains("remote=1"))
4736  forceRemote = kTRUE;
4737  else if (opts.Contains("remote=0"))
4738  forceRemote = kFALSE;
4739  if (!forceRemote) {
4740  // Generic locality test
4741  localFile = gSystem->IsPathLocal(name);
4742  if (localFile) {
4743  // Local path including the prefix
4744  const char *fname = url.GetFileAndOptions();
4745  TString lfname;
4746  if (fname[0] == '/') {
4747  if (prefix)
4748  lfname.Form("%s%s", prefix->Data(), fname);
4749  else
4750  lfname = fname;
4751  } else if (fname[0] == '~' || fname[0] == '$') {
4752  lfname = fname;
4753  } else {
4754  lfname.Form("%s/%s", gSystem->HomeDirectory(), fname);
4755  }
4756  // If option "READ" test existence and access
4757  TString opt = option;
4758  Bool_t read = (opt.IsNull() ||
4759  !opt.CompareTo("READ", TString::kIgnoreCase)) ? kTRUE : kFALSE;
4760  if (read) {
4761  char *fn;
4762  if ((fn = gSystem->ExpandPathName(TUrl(lfname).GetFile()))) {
4764  localFile = kFALSE;
4765  delete [] fn;
4766  }
4767  }
4768  // Return full local path if requested (and if the case)
4769  if (localFile && prefix)
4770  *prefix = lfname;
4771  }
4772  }
4773  //
4774  // Adjust the type according to findings
4775  type = (localFile) ? kLocal : type;
4776  } else if (TPMERegexp("^(http[s]?|s3http[s]?|[a]?s3|gs|gshttp[s]?){1}:", "i").Match(name)) {
4777  //
4778  // Web file
4779  type = kWeb;
4780  } else if (!strncmp(name, "file:", 5)) {
4781  //
4782  // 'file' protocol
4783  type = kFile;
4784  }
4785  // We are done
4786  return type;
4787 }
4788 
4789 ////////////////////////////////////////////////////////////////////////////////
4790 /// Get status of the async open request related to 'name'.
4793 {
4794  // Check the list of pending async open requests
4797  TFileOpenHandle *fh = nullptr;
4798  while ((fh = (TFileOpenHandle *)nxr()))
4799  if (fh->Matches(name))
4800  return TFile::GetAsyncOpenStatus(fh);
4801  }
4802 
4803  // Check also the list of files open
4805  TSeqCollection *of = gROOT->GetListOfFiles();
4806  if (of && (of->GetSize() > 0)) {
4807  TIter nxf(of);
4808  TFile *f = nullptr;
4809  while ((f = (TFile *)nxf()))
4810  if (f->Matches(name))
4811  return f->GetAsyncOpenStatus();
4812  }
4813 
4814  // Default is synchronous mode
4815  return kAOSNotAsync;
4816 }
4817 
4818 ////////////////////////////////////////////////////////////////////////////////
4819 /// Get status of the async open request related to 'handle'.
4822 {
4823  if (handle && handle->fFile) {
4824  if (!handle->fFile->IsZombie())
4825  return handle->fFile->GetAsyncOpenStatus();
4826  else
4827  return TFile::kAOSFailure;
4828  }
4829 
4830  // Default is synchronous mode
4831  return TFile::kAOSNotAsync;
4832 }
4833 
4834 ////////////////////////////////////////////////////////////////////////////////
4835 /// Get final URL for file being opened asynchronously.
4836 /// Returns 0 is the information is not yet available.
4838 const TUrl *TFile::GetEndpointUrl(const char* name)
4839 {
4840  // Check the list of pending async open requests
4843  TFileOpenHandle *fh = nullptr;
4844  while ((fh = (TFileOpenHandle *)nxr()))
4845  if (fh->Matches(name))
4846  if (fh->fFile)
4847  return fh->fFile->GetEndpointUrl();
4848  }
4849 
4850  // Check also the list of files open
4852  TSeqCollection *of = gROOT->GetListOfFiles();
4853  if (of && (of->GetSize() > 0)) {
4854  TIter nxf(of);
4855  TFile *f = nullptr;
4856  while ((f = (TFile *)nxf()))
4857  if (f->Matches(name))
4858  return f->GetEndpointUrl();
4859  }
4860 
4861  // Information not yet available
4862  return (const TUrl *)nullptr;
4863 }
4864 
4865 ////////////////////////////////////////////////////////////////////////////////
4866 /// Print file copy progress.
4868 void TFile::CpProgress(Long64_t bytesread, Long64_t size, TStopwatch &watch)
4869 {
4870  fprintf(stderr, "[TFile::Cp] Total %.02f MB\t|", (Double_t)size/1048576);
4871 
4872  for (int l = 0; l < 20; l++) {
4873  if (size > 0) {
4874  if (l < 20*bytesread/size)
4875  fprintf(stderr, "=");
4876  else if (l == 20*bytesread/size)
4877  fprintf(stderr, ">");
4878  else if (l > 20*bytesread/size)
4879  fprintf(stderr, ".");
4880  } else
4881  fprintf(stderr, "=");
4882  }
4883  // Allow to update the GUI while uploading files
4885  watch.Stop();
4886  Double_t lCopy_time = watch.RealTime();
4887  fprintf(stderr, "| %.02f %% [%.01f MB/s]\r",
4888  100.0*(size?(bytesread/((float)size)):1), (lCopy_time>0.)?bytesread/lCopy_time/1048576.:0.);
4889  watch.Continue();
4890 }
4891 
4892 ////////////////////////////////////////////////////////////////////////////////
4893 /// Allows to copy this file to the dst URL. Returns kTRUE in case of success,
4894 /// kFALSE otherwise.
4896 Bool_t TFile::Cp(const char *dst, Bool_t progressbar, UInt_t buffersize)
4897 {
4898  Bool_t rmdestiferror = kFALSE;
4899  TStopwatch watch;
4900  Bool_t success = kFALSE;
4901 
4902  TUrl dURL(dst, kTRUE);
4903 
4904  TString oopt = "RECREATE";
4905  TString ourl = dURL.GetUrl();
4906 
4907  // Files will be open in RAW mode
4908  TString raw = "filetype=raw";
4909 
4910  // Set optimization options for the destination file
4911  TString opt = dURL.GetOptions();
4912  if (opt != "") opt += "&";
4913  opt += raw;
4914 
4915  // AliEn files need to know where the source file is
4916  if (!strcmp(dURL.GetProtocol(), "alien"))
4917  opt += TString::Format("&source=%s", GetName());
4918 
4919  dURL.SetOptions(opt);
4920 
4921  char *copybuffer = nullptr;
4922 
4923  TFile *sfile = this;
4924  TFile *dfile = nullptr;
4925 
4926  // "RECREATE" does not work always well with XROOTD
4927  // namely when some pieces of the path are missing;
4928  // we force "NEW" in such a case
4929  if (TFile::GetType(ourl, "") == TFile::kNet) {
4930  if (gSystem->AccessPathName(ourl)) {
4931  oopt = "NEW";
4932  // Force creation of the missing parts of the path
4933  opt += "&mkpath=1";
4934  dURL.SetOptions(opt);
4935  }
4936  }
4937 
4938  // Open destination file
4939  if (!(dfile = TFile::Open(dURL.GetUrl(), oopt))) {
4940  ::Error("TFile::Cp", "cannot open destination file %s", dst);
4941  goto copyout;
4942  }
4943 
4944  // Probably we created a new file
4945  // We have to remove it in case of errors
4946  rmdestiferror = kTRUE;
4947 
4948  sfile->Seek(0);
4949  dfile->Seek(0);
4950 
4951  copybuffer = new char[buffersize];
4952  if (!copybuffer) {
4953  ::Error("TFile::Cp", "cannot allocate the copy buffer");
4954  goto copyout;
4955  }
4956 
4957  Bool_t readop, writeop;
4958  Long64_t read, written, totalread, filesize, b00;
4959 
4960  totalread = 0;
4961  filesize = sfile->GetSize();
4962 
4963  watch.Start();
4964 
4965  b00 = sfile->GetBytesRead();
4966 
4967  do {
4968  if (progressbar) CpProgress(totalread, filesize,watch);
4969 
4970  Long64_t b1 = sfile->GetBytesRead() - b00;
4971 
4972  Long64_t readsize;
4973  if (filesize - b1 > (Long64_t)buffersize) {
4974  readsize = buffersize;
4975  } else {
4976  readsize = filesize - b1;
4977  }
4978 
4979  if (readsize == 0) break;
4980 
4981  Long64_t b0 = sfile->GetBytesRead();
4982  sfile->Seek(totalread,TFile::kBeg);
4983  readop = sfile->ReadBuffer(copybuffer, (Int_t)readsize);
4984  read = sfile->GetBytesRead() - b0;
4985  if ((read <= 0) || readop) {
4986  ::Error("TFile::Cp", "cannot read from source file %s. readsize=%lld read=%lld readop=%d",
4987  sfile->GetName(), readsize, read, readop);
4988  goto copyout;
4989  }
4990 
4991  Long64_t w0 = dfile->GetBytesWritten();
4992  writeop = dfile->WriteBuffer(copybuffer, (Int_t)read);
4993  written = dfile->GetBytesWritten() - w0;
4994  if ((written != read) || writeop) {
4995  ::Error("TFile::Cp", "cannot write %lld bytes to destination file %s", read, dst);
4996  goto copyout;
4997  }
4998  totalread += read;
4999  } while (read == (Long64_t)buffersize);
5000 
5001  if (progressbar) {
5002  CpProgress(totalread, filesize,watch);
5003  fprintf(stderr, "\n");
5004  }
5005 
5006  success = kTRUE;
5007 
5008 copyout:
5009  if (dfile) dfile->Close();
5010 
5011  if (dfile) delete dfile;
5012  if (copybuffer) delete[] copybuffer;
5013 
5014  if (rmdestiferror && (success != kTRUE))
5015  gSystem->Unlink(dst);
5016 
5017  watch.Stop();
5018  watch.Reset();
5019 
5020  return success;
5021 }
5022 
5023 ////////////////////////////////////////////////////////////////////////////////
5024 /// Allows to copy file from src to dst URL. Returns kTRUE in case of success,
5025 /// kFALSE otherwise.
5027 Bool_t TFile::Cp(const char *src, const char *dst, Bool_t progressbar,
5028  UInt_t buffersize)
5029 {
5030  TUrl sURL(src, kTRUE);
5031 
5032  // Files will be open in RAW mode
5033  TString raw = "filetype=raw";
5034 
5035  // Set optimization options for the source file
5036  TString opt = sURL.GetOptions();
5037  if (opt != "") opt += "&";
5038  opt += raw;
5039  // Netx-related options:
5040  // cachesz = 4*buffersize -> 4 buffers as peak mem usage
5041  // readaheadsz = 2*buffersize -> Keep at max 4*buffersize bytes outstanding when reading
5042  // rmpolicy = 1 -> Remove from the cache the blk with the least offset
5043  opt += TString::Format("&cachesz=%d&readaheadsz=%d&rmpolicy=1", 4*buffersize, 2*buffersize);
5044  sURL.SetOptions(opt);
5045 
5046  TFile *sfile = nullptr;
5047 
5048  Bool_t success = kFALSE;
5049 
5050  // Open source file
5051  if (!(sfile = TFile::Open(sURL.GetUrl(), "READ"))) {
5052  ::Error("TFile::Cp", "cannot open source file %s", src);
5053  } else {
5054  success = sfile->Cp(dst, progressbar, buffersize);
5055  }
5056 
5057  if (sfile) {
5058  sfile->Close();
5059  delete sfile;
5060  }
5061 
5062  return success;
5063 }
5064 
5065 //______________________________________________________________________________
5066 //The next statement is not active anymore on Linux.
5067 //Using posix_fadvise introduces a performance penalty (10 %) on optimized files
5068 //and in addition it destroys the information of TTreePerfStats
5069 #if defined(R__neverLINUX) && !defined(R__WINGCC)
5071 {
5072  // Read specified byte range asynchronously. Actually we tell the kernel
5073  // which blocks we are going to read so it can start loading these blocks
5074  // in the buffer cache.
5075 
5076  // Shortcut to avoid having to implement dummy ReadBufferAsync() in all
5077  // I/O plugins. Override ReadBufferAsync() in plugins if async is supported.
5078  if (IsA() != TFile::Class())
5079  return kTRUE;
5080 
5081  int advice = POSIX_FADV_WILLNEED;
5082  if (len == 0) {
5083  // according POSIX spec if len is zero, all data following offset
5084  // is specified. Nevertheless ROOT uses zero to probe readahead
5085  // capabilities.
5086  advice = POSIX_FADV_NORMAL;
5087  }
5088  Double_t start = 0;
5089  if (gPerfStats) start = TTimeStamp();
5090 #if defined(R__SEEK64)
5091  Int_t result = posix_fadvise64(fD, offset, len, advice);
5092 #else
5093  Int_t result = posix_fadvise(fD, offset, len, advice);
5094 #endif
5095  if (gPerfStats) {
5096  gPerfStats->FileReadEvent(this, len, start);
5097  }
5098  return (result != 0);
5099 }
5100 #else
5102 {
5103  // Not supported yet on non Linux systems.
5104 
5105  return kTRUE;
5106 }
5107 #endif
5108 
5109 ////////////////////////////////////////////////////////////////////////////////
5110 /// Max number of bytes to prefetch.
5111 ///
5112 /// By default this is 75% of the
5113 /// read cache size. But specific TFile implementations may need to change it
5116 {
5117  TFileCacheRead *cr = nullptr;
5118  if ((cr = GetCacheRead())) {
5119  Int_t bytes = cr->GetBufferSize() / 4 * 3;
5120  return ((bytes < 0) ? 0 : bytes);
5121  }
5122  return 0;
5123 }
TFile::fBEGIN
Long64_t fBEGIN
First used byte in file.
Definition: TFile.h:79
TDirectoryFile::ReadKeys
Int_t ReadKeys(Bool_t forceRead=kTRUE) override
Read the linked list of keys.
Definition: TDirectoryFile.cxx:1340
l
auto * l
Definition: textangle.C:4
TFile::fNProcessIDs
Int_t fNProcessIDs
Number of TProcessID written to this file.
Definition: TFile.h:89
TArchiveFile.h
TFile::kRecovered
@ kRecovered
Definition: TFile.h:183
Compression.h
TSystem::Unlink
virtual int Unlink(const char *name)
Unlink, i.e.
Definition: TSystem.cxx:1380
n
const Int_t n
Definition: legend1.C:16
TSystem::GetPathInfo
int GetPathInfo(const char *path, Long_t *id, Long_t *size, Long_t *flags, Long_t *modtime)
Get info about a file: id, size, flags, modification time.
Definition: TSystem.cxx:1397
TFile::fBytesWrite
Long64_t fBytesWrite
Number of bytes written to this file.
Definition: TFile.h:76
TVirtualPerfStats.h
TUUID::FillBuffer
void FillBuffer(char *&buffer)
Stream UUID into output buffer.
Definition: TUUID.cxx:274
TFileOpenHandle::GetFile
TFile * GetFile() const
Definition: TFile.h:374
TKey::GetKeylen
Int_t GetKeylen() const
Definition: TKey.h:85
TFile::ReadBufferAsync
virtual Bool_t ReadBufferAsync(Long64_t offs, Int_t len)
Definition: TFile.cxx:5100
TFile::fVersion
Int_t fVersion
File format version.
Definition: TFile.h:84
TFile::kCur
@ kCur
Definition: TFile.h:191
TString::ReadBuffer
virtual void ReadBuffer(char *&buffer)
Read string from I/O buffer.
Definition: TString.cxx:1289
TPMERegexp
Wrapper for PCRE library (Perl Compatible Regular Expressions).
Definition: TPRegexp.h:97
first
Definition: first.py:1
TFile::fgBytesRead
static std::atomic< Long64_t > fgBytesRead
Number of bytes read by all TFile objects.
Definition: TFile.h:130
TFile::ReOpen
virtual Int_t ReOpen(Option_t *mode)
Reopen a file with a different access mode.
Definition: TFile.cxx:2079
TClass::GetCollectionProxy
TVirtualCollectionProxy * GetCollectionProxy() const
Return the proxy describing the collection (if any).
Definition: TClass.cxx:2884
TFile::fCompress
Int_t fCompress
Compression level and algorithm.
Definition: TFile.h:85
TList::RemoveLast
virtual void RemoveLast()
Remove the last object of the list.
Definition: TList.cxx:909
kTRUE
const Bool_t kTRUE
Definition: RtypesCore.h:100
TFile::ReadFree
virtual void ReadFree()
Read the FREE linked list.
Definition: TFile.cxx:1842
TObject::SysError
virtual void SysError(const char *method, const char *msgfmt,...) const
Issue system error message.
Definition: TObject.cxx:907
TDirectory::GetList
virtual TList * GetList() const
Definition: TDirectory.h:213
TObject::TestBit
R__ALWAYS_INLINE Bool_t TestBit(UInt_t f) const
Definition: TObject.h:187
TFree.h
TFile::SetCompressionAlgorithm
virtual void SetCompressionAlgorithm(Int_t algorithm=ROOT::RCompressionSetting::EAlgorithm::kUseGlobal)
See comments for function SetCompressionSettings.
Definition: TFile.cxx:2222
TStopwatch::Continue
void Continue()
Resume a stopped stopwatch.
Definition: TStopwatch.cxx:93
Version_t
short Version_t
Definition: RtypesCore.h:65
snprintf
#define snprintf
Definition: civetweb.c:1540
TObjArray
An array of TObjects.
Definition: TObjArray.h:37
TUrl::GetValueFromOptions
const char * GetValueFromOptions(const char *key) const
Return a value for a given key from the URL options.
Definition: TUrl.cxx:659
f
#define f(i)
Definition: RSha256.hxx:104
TFile::kDoNotDisconnect
@ kDoNotDisconnect
Definition: TFile.h:71
TArchiveFile::OpenArchive
virtual Int_t OpenArchive()=0
R__READ_LOCKGUARD
#define R__READ_LOCKGUARD(mutex)
Definition: TVirtualRWMutex.h:160
TFile::fUnits
Char_t fUnits
Number of bytes for file pointers.
Definition: TFile.h:93
TROOT::DecreaseDirLevel
static Int_t DecreaseDirLevel()
Decrease the indentation level for ls().
Definition: TROOT.cxx:2692
ROOT::RCompressionSetting::ELevel::kUseMin
@ kUseMin
Compression level reserved when we are not sure what to use (1 is for the fastest compression)
Definition: Compression.h:68
Option_t
const char Option_t
Definition: RtypesCore.h:66
TFile::OpenFromCache
static TFile * OpenFromCache(const char *name, Option_t *="", const char *ftitle="", Int_t compress=ROOT::RCompressionSetting::EDefaults::kUseCompiledDefault, Int_t netopt=0)
Open a file for reading through the file cache.
Definition: TFile.cxx:3789
TFile::fSeekFree
Long64_t fSeekFree
Location on disk of free segments structure.
Definition: TFile.h:81
TFile::FillBuffer
void FillBuffer(char *&buffer) override
Encode file output buffer.
Definition: TFile.cxx:1091
TSystem::ShowOutput
virtual void ShowOutput(RedirectHandle_t *h)
Display the content associated with the redirection described by the opaque handle 'h'.
Definition: TSystem.cxx:1722
TFile::fReadCalls
Int_t fReadCalls
Number of read calls ( not counting the cache calls )
Definition: TFile.h:90
TSystem::ChangeDirectory
virtual Bool_t ChangeDirectory(const char *path)
Change directory.
Definition: TSystem.cxx:862
TStreamerInfo.h
TFile::fProcessIDs
TObjArray * fProcessIDs
!Array of pointers to TProcessIDs
Definition: TFile.h:96
TString::Atoi
Int_t Atoi() const
Return integer value of string.
Definition: TString.cxx:1946
TFile::Seek
virtual void Seek(Long64_t offset, ERelativeTo pos=kBeg)
Seek to a specific position in the file. Pos it either kBeg, kCur or kEnd.
Definition: TFile.cxx:2191
kNPOS
const Ssiz_t kNPOS
Definition: RtypesCore.h:124
TFile::fBytesRead
Long64_t fBytesRead
Number of bytes read from this file.
Definition: TFile.h:77
TCollection::GetEntries
virtual Int_t GetEntries() const
Definition: TCollection.h:177
TFile::SetCacheFileDir
static Bool_t SetCacheFileDir(ROOT::Internal::TStringView cacheDir, Bool_t operateDisconnected=kTRUE, Bool_t forceCacheread=kFALSE)
Definition: TFile.h:324
TFile::kBeg
@ kBeg
Definition: TFile.h:191
TFile::ReadProcessID
virtual TProcessID * ReadProcessID(UShort_t pidf)
The TProcessID with number pidf is read from this file.
Definition: TFile.cxx:1868
UShort_t
unsigned short UShort_t
Definition: RtypesCore.h:40
gEnv
R__EXTERN TEnv * gEnv
Definition: TEnv.h:170
TUUID::ReadBuffer
void ReadBuffer(char *&buffer)
Stream UUID from input buffer.
Definition: TUUID.cxx:290
TFile::fgReadInfo
static Bool_t fgReadInfo
if true (default) ReadStreamerInfo is called when opening a file
Definition: TFile.h:134
TSystem::BaseName
virtual const char * BaseName(const char *pathname)
Base name of a file name. Base name of /user/root is root.
Definition: TSystem.cxx:934
TFile::SysSeek
virtual Long64_t SysSeek(Int_t fd, Long64_t offset, Int_t whence)
Interface to system lseek.
Definition: TFile.cxx:4416
TFile::GetBestBuffer
Int_t GetBestBuffer() const
Return the best buffer size of objects on this file.
Definition: TFile.cxx:1104
TList::FindObject
virtual TObject * FindObject(const char *name) const
Find an object in this list using its name.
Definition: TList.cxx:578
TTimeStamp.h
TList::Delete
virtual void Delete(Option_t *option="")
Remove all objects from the list AND delete all heap based objects.
Definition: TList.cxx:470
TDirectoryFile::Save
void Save() override
Save recursively all directory keys and headers.
Definition: TDirectoryFile.cxx:1506
TString::Data
const char * Data() const
Definition: TString.h:369
TSystem::FreeDirectory
virtual void FreeDirectory(void *dirp)
Free a directory.
Definition: TSystem.cxx:845
TFileCacheRead::GetBufferSize
virtual Int_t GetBufferSize() const
Definition: TFileCacheRead.h:88
TFile::SetCompressionLevel
virtual void SetCompressionLevel(Int_t level=ROOT::RCompressionSetting::ELevel::kUseMin)
See comments for function SetCompressionSettings.
Definition: TFile.cxx:2236
TSystem::IsAbsoluteFileName
virtual Bool_t IsAbsoluteFileName(const char *dir)
Return true if dir is an absolute pathname.
Definition: TSystem.cxx:951
TFile::ECacheAction
ECacheAction
TTreeCache flushing semantics.
Definition: TFile.h:71
tree
Definition: tree.py:1
TDatime::ReadBuffer
void ReadBuffer(char *&buffer)
Decode Date/Time from output buffer, used by I/O system.
Definition: TDatime.cxx:278
ClassImp
#define ClassImp(name)
Definition: Rtypes.h:364
TKey::GetBuffer
virtual char * GetBuffer() const
Definition: TKey.h:79
TNamed::GetTitle
virtual const char * GetTitle() const
Returns title of object.
Definition: TNamed.h:48
TFile::SetFileBytesWritten
static void SetFileBytesWritten(Long64_t bytes=0)
Definition: TFile.cxx:4502
TStreamerElement::kDoNotDelete
@ kDoNotDelete
Definition: TStreamerElement.h:82
TObjString.h
TDirectoryFile::fDatimeM
TDatime fDatimeM
Date and time of last modification.
Definition: TDirectoryFile.h:38
TSystem::GetSoExt
virtual const char * GetSoExt() const
Get the shared library extension.
Definition: TSystem.cxx:3999
TFile::IncrementFileCounter
static void IncrementFileCounter()
Definition: TFile.cxx:4511
FileStat_t
Definition: TSystem.h:124
TFile::GetRelOffset
Long64_t GetRelOffset() const
Definition: TFile.h:243
TFile::fClassIndex
TArrayC * fClassIndex
!Index of TStreamerInfo classes written to this file
Definition: TFile.h:95
TFileOpenHandle::GetCompress
Int_t GetCompress() const
Definition: TFile.h:382
TDatime.h
TSystem::GetFlagsDebug
virtual const char * GetFlagsDebug() const
Return the debug flags.
Definition: TSystem.cxx:3913
TFile::fIsArchive
Bool_t fIsArchive
!True if this is a pure archive file
Definition: TFile.h:103
TFile::fIsPcmFile
Bool_t fIsPcmFile
!True if the file is a ROOT pcm file.
Definition: TFile.h:108
TSystem::GetFlagsOpt
virtual const char * GetFlagsOpt() const
Return the optimization flags.
Definition: TSystem.cxx:3921
tobuf
void tobuf(char *&buf, Bool_t x)
Definition: Bytes.h:55
TStreamerElement.h
TFile::kDefault
@ kDefault
Definition: TFile.h:194
TSystem::Chmod
virtual int Chmod(const char *file, UInt_t mode)
Set the file permission bits. Returns -1 in case or error, 0 otherwise.
Definition: TSystem.cxx:1505
TFile::fCacheReadMap
TMap * fCacheReadMap
!Pointer to the read cache (if any)
Definition: TFile.h:100
TStopwatch.h
TFile::ResetErrno
virtual void ResetErrno() const
Method resetting the errno.
Definition: TFile.cxx:1175
TClassEdit::kLong64
@ kLong64
Definition: TClassEdit.h:84
TObject::Info
virtual void Info(const char *method, const char *msgfmt,...) const
Issue info message.
Definition: TObject.cxx:867
Long64_t
long long Long64_t
Definition: RtypesCore.h:80
TObject::Error
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition: TObject.cxx:893
ROOT::Internal::RConcurrentHashColl::Hash
static HashValue Hash(char *buf, int len)
Return the hash object corresponding to the buffer.
Definition: RConcurrentHashColl.cxx:43
TString::kIgnoreCase
@ kIgnoreCase
Definition: TString.h:268
TArrayC::Set
void Set(Int_t n)
Set size of this array to n chars.
Definition: TArrayC.cxx:105
TFile::CreateKey
virtual TKey * CreateKey(TDirectory *mother, const TObject *obj, const char *name, Int_t bufsize)
Creates key for object and converts data to buffer.
Definition: TFile.cxx:977
string_view
basic_string_view< char > string_view
Definition: