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