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