Logo ROOT  
Reference Guide
Loading...
Searching...
No Matches
RSysFile.cxx
Go to the documentation of this file.
1/*************************************************************************
2 * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers. *
3 * All rights reserved. *
4 * *
5 * For the licensing terms see $ROOTSYS/LICENSE. *
6 * For the list of contributors see $ROOTSYS/README/CREDITS. *
7 *************************************************************************/
8
9/// \file
10/// \author Sergey Linev <S.Linev@gsi.de>
11/// \date 2019-10-15
12/// \warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback
13/// is welcome!
14
15
17
22
23#include "ROOT/RLogger.hxx"
24
25#include "TROOT.h"
26#include "TList.h"
27#include "TBase64.h"
28#include "snprintf.h"
29
30#include <sstream>
31#include <fstream>
32#include <algorithm>
33
34#ifdef _MSC_VER
35#include <windows.h>
36#include <tchar.h>
37#endif
38
39using namespace std::string_literals;
40
41using namespace ROOT::Browsable;
42
43namespace ROOT {
44namespace Browsable {
45
46
47#ifdef _MSC_VER
48bool IsWindowsLink(const std::string &path)
49{
50 return (path.length() > 4) && (path.rfind(".lnk") == path.length() - 4);
51}
52#endif
53
54
55/** \class RSysDirLevelIter
56\ingroup rbrowser
57
58Iterator over files in in sub-directory
59*/
60
61
63 std::string fPath; ///<! fully qualified path without final slash
64 void *fDir{nullptr}; ///<! current directory handle
65 std::string fCurrentName; ///<! current file name
66 std::string fItemName; ///<! current item name
67 FileStat_t fCurrentStat; ///<! stat for current file name
68
69 /** Open directory for listing */
70 bool OpenDir()
71 {
72 if (fDir)
73 CloseDir();
74
75#ifdef _MSC_VER
76 // on Windows path can be redirected via .lnk therefore get real path name before OpenDirectory,
77 // otherwise such realname will not be known for us
78 if (IsWindowsLink(fPath)) {
79 char *realWinPath = gSystem->ExpandPathName(fPath.c_str());
80 if (realWinPath) fPath = realWinPath;
81 delete [] realWinPath;
82 }
83#endif
84
85 fDir = gSystem->OpenDirectory(fPath.c_str());
86
87#ifdef _MSC_VER
88 // Directory can be an soft link (not as .lnk) and should be tried as well
89 if (!fDir) {
90
91 auto hFile = CreateFile(fPath.c_str(), // file to open
92 0, // open for reading
93 0, // share for reading
94 0, // default security
95 OPEN_EXISTING, // existing file only
96 FILE_FLAG_BACKUP_SEMANTICS, // flag to work with dirs
97 NULL); // no attr. template
98
99 if( hFile != INVALID_HANDLE_VALUE) {
100 const int BUFSIZE = 2048;
101 TCHAR path[BUFSIZE];
102 auto dwRet = GetFinalPathNameByHandle( hFile, path, BUFSIZE, VOLUME_NAME_DOS );
103 // produced file name may include \\? symbols, which are indicating long file name
104 if ((dwRet > 0) && (dwRet < BUFSIZE))
105 if ((path[0] == '\\') && (path[1] == '\\') && (path[2] == '?') && (path[3] == '\\')) {
106 R__LOG_DEBUG(0, BrowsableLog()) << "Try to open directory " << (path+4) << " instead of " << fPath;
107 fDir = gSystem->OpenDirectory(path + 4);
108 if (fDir) fPath = path + 4;
109 }
110 }
111
112 CloseHandle(hFile);
113 }
114
115#endif
116
117 if (!fDir) {
118 R__LOG_ERROR(BrowsableLog()) << "Fail to open directory " << fPath;
119 return false;
120 }
121
122 return true;
123 }
124
125 /** Close directory for listing */
126 void CloseDir()
127 {
128 if (fDir)
129 gSystem->FreeDirectory(fDir);
130 fDir = nullptr;
131 fCurrentName.clear();
132 fItemName.clear();
133 }
134
135 /** Return full dir name with appropriate slash at the end */
136 std::string FullDirName() const
137 {
138 std::string path = fPath;
139#ifdef _MSC_VER
140 const char *slash = "\\";
141#else
142 const char *slash = "/";
143#endif
144 if (path.rfind(slash) != path.length() - 1)
145 path.append(slash);
146 return path;
147 }
148
149 /** Check if entry of that name exists */
150 bool TestDirEntry(const std::string &name)
151 {
152 auto testname = name;
153
154 auto path = FullDirName() + testname;
155
156 auto pathinfores = gSystem->GetPathInfo(path.c_str(), fCurrentStat);
157
158#ifdef _MSC_VER
159 if (pathinfores && !IsWindowsLink(path)) {
160 std::string lpath = path + ".lnk";
161 pathinfores = gSystem->GetPathInfo(lpath.c_str(), fCurrentStat);
162 if (!pathinfores) testname.append(".lnk");
163 }
164#endif
165
166 if (pathinfores) {
167
168 if (fCurrentStat.fIsLink) {
169 R__LOG_DEBUG(0, BrowsableLog()) << "Broken symlink of " << path;
170 } else {
171 R__LOG_DEBUG(0, BrowsableLog()) << "Can't read file attributes of \"" << path << "\" err:" << gSystem->GetError();
172 }
173 return false;
174 }
175
176 fItemName = fCurrentName = testname;
177#ifdef _MSC_VER
178 if (IsWindowsLink(fItemName))
179 fItemName.resize(fItemName.length() - 4);
180#endif
181 return true;
182 }
183
184 /** Trying to produce next entry */
186 {
187 fCurrentName.clear();
188 fItemName.clear();
189
190 if (!fDir)
191 return false;
192
193 while (fCurrentName.empty()) {
194
195 // one have to use const char* to correctly check for nullptr
196 const char *name = gSystem->GetDirEntry(fDir);
197
198 if (!name) {
199 CloseDir();
200 return false;
201 }
202
203 std::string sname = name;
204
205 if ((sname == ".") || (sname == ".."))
206 continue;
207
208 TestDirEntry(sname);
209 }
210
211
212 return true;
213 }
214
215 std::string GetFileExtension(const std::string &fname) const
216 {
217 auto pos = fname.rfind(".");
218 if ((pos != std::string::npos) && (pos < fname.length() - 1) && (pos > 0))
219 return fname.substr(pos+1);
220
221 return ""s;
222 }
223
224public:
225 explicit RSysDirLevelIter(const std::string &path = "") : fPath(path) { OpenDir(); }
226
227 ~RSysDirLevelIter() override { CloseDir(); }
228
229 bool Next() override { return NextDirEntry(); }
230
231 bool Find(const std::string &name, int = -1) override
232 {
233 // ignore index, it is not possible to have duplicated file names
234
235 if (!fDir && !OpenDir())
236 return false;
237
238 return TestDirEntry(name);
239 }
240
241 std::string GetItemName() const override { return fItemName; }
242
243 /** Returns true if directory or is file format supported */
244 bool CanItemHaveChilds() const override
245 {
246 if (R_ISDIR(fCurrentStat.fMode))
247 return true;
248
250 return true;
251
252 return false;
253 }
254
255 std::unique_ptr<RItem> CreateItem() override
256 {
257 auto item = std::make_unique<RSysFileItem>(GetItemName(), CanItemHaveChilds() ? -1 : 0);
258
259 // this is construction of current item
260 char tmp[256];
261
262 item->type = fCurrentStat.fMode;
263 item->size = fCurrentStat.fSize;
264 item->uid = fCurrentStat.fUid;
265 item->gid = fCurrentStat.fGid;
266 item->modtime = fCurrentStat.fMtime;
267 item->islink = fCurrentStat.fIsLink;
268 item->isdir = R_ISDIR(fCurrentStat.fMode);
269
270 if (item->isdir)
271 item->SetIcon("sap-icon://folder-blank"s);
272 else
273 item->SetIcon(RSysFile::GetFileIcon(GetItemName()));
274
275 // set file size as string
276 item->SetSize(item->size);
277
278 // modification time
279 time_t loctime = (time_t) item->modtime;
280 struct tm *newtime = localtime(&loctime);
281 if (newtime) {
282 snprintf(tmp, sizeof(tmp), "%d-%02d-%02d %02d:%02d", newtime->tm_year + 1900,
283 newtime->tm_mon+1, newtime->tm_mday, newtime->tm_hour,
284 newtime->tm_min);
285 item->SetMTime(tmp);
286 } else {
287 item->SetMTime("1901-01-01 00:00");
288 }
289
290 // file type
291 snprintf(tmp, sizeof(tmp), "%c%c%c%c%c%c%c%c%c%c",
292 (item->islink ?
293 'l' :
294 R_ISREG(item->type) ?
295 '-' :
296 (R_ISDIR(item->type) ?
297 'd' :
298 (R_ISCHR(item->type) ?
299 'c' :
300 (R_ISBLK(item->type) ?
301 'b' :
302 (R_ISFIFO(item->type) ?
303 'p' :
304 (R_ISSOCK(item->type) ?
305 's' : '?' )))))),
306 ((item->type & kS_IRUSR) ? 'r' : '-'),
307 ((item->type & kS_IWUSR) ? 'w' : '-'),
308 ((item->type & kS_ISUID) ? 's' : ((item->type & kS_IXUSR) ? 'x' : '-')),
309 ((item->type & kS_IRGRP) ? 'r' : '-'),
310 ((item->type & kS_IWGRP) ? 'w' : '-'),
311 ((item->type & kS_ISGID) ? 's' : ((item->type & kS_IXGRP) ? 'x' : '-')),
312 ((item->type & kS_IROTH) ? 'r' : '-'),
313 ((item->type & kS_IWOTH) ? 'w' : '-'),
314 ((item->type & kS_ISVTX) ? 't' : ((item->type & kS_IXOTH) ? 'x' : '-')));
315 item->SetType(tmp);
316
317 struct UserGroup_t *user_group = gSystem->GetUserInfo(item->uid);
318 if (user_group) {
319 item->SetUid(user_group->fUser.Data());
320 item->SetGid(user_group->fGroup.Data());
321 delete user_group;
322 } else {
323 item->SetUid(std::to_string(item->uid));
324 item->SetGid(std::to_string(item->gid));
325 }
326
327 return item;
328 }
329
330 /** Returns full information for current element */
331 std::shared_ptr<RElement> GetElement() override
332 {
333 if (!R_ISDIR(fCurrentStat.fMode)) {
335
338 if (elem) return elem;
339 }
340 }
341
342 return std::make_shared<RSysFile>(fCurrentStat, FullDirName(), fCurrentName);
343 }
344
345};
346
347
348} // namespace Browsable
349} // namespace ROOT
350
351
352/////////////////////////////////////////////////////////////////////////////////
353/// Get icon for the type of given file name
354
355std::string RSysFile::GetFileIcon(const std::string &fname)
356{
357 std::string name = fname;
358 std::transform(name.begin(), name.end(), name.begin(), ::tolower);
359
360 auto EndsWith = [name](const std::string &suffix) {
361 return (name.length() > suffix.length()) ? (0 == name.compare (name.length() - suffix.length(), suffix.length(), suffix)) : false;
362 };
363
364 if (EndsWith(".c") || EndsWith(".cpp") || EndsWith(".cxx") || EndsWith(".c++") || EndsWith(".cxx") ||
365 EndsWith(".cc") || EndsWith(".h") || EndsWith(".hh") || EndsWith(".hpp") || EndsWith(".hxx") ||
366 EndsWith(".h++") || EndsWith(".py") || EndsWith(".txt") || EndsWith(".cmake") || EndsWith(".dat") ||
367 EndsWith(".log") || EndsWith(".xml") || EndsWith(".htm") || EndsWith(".html") || EndsWith(".json") ||
368 EndsWith(".sh") || EndsWith(".md") || EndsWith(".css") || EndsWith(".mjs") || EndsWith(".js"))
369 return "sap-icon://document-text"s;
370 if (EndsWith(".bmp") || EndsWith(".gif") || EndsWith(".jpeg") || EndsWith(".jpg") || EndsWith(".png") ||
371 EndsWith(".webp") || EndsWith(".svg"))
372 return "sap-icon://picture"s;
373 if (EndsWith(".root"))
374 return "sap-icon://org-chart"s;
375
376 return "sap-icon://document"s;
377}
378
379
380/////////////////////////////////////////////////////////////////////////////////
381/// Create file element
382
383RSysFile::RSysFile(const std::string &filename) : fFileName(filename)
384{
385 if (gSystem->GetPathInfo(fFileName.c_str(), fStat)) {
386 if (fStat.fIsLink) {
387 R__LOG_DEBUG(0, BrowsableLog()) << "Broken symlink of " << fFileName;
388 } else {
389 R__LOG_DEBUG(0, BrowsableLog()) << "Can't read file attributes of \"" << fFileName
390 << "\" err:" << gSystem->GetError();
391 }
392 }
393
394 auto pos = fFileName.find_last_of("\\/");
395 if ((pos != std::string::npos) && (pos < fFileName.length() - 1)) {
396 fDirName = fFileName.substr(0, pos+1);
397 fFileName.erase(0, pos+1);
398 }
399}
400
401/////////////////////////////////////////////////////////////////////////////////
402/// Create file element with already provided stats information
403
404RSysFile::RSysFile(const FileStat_t &stat, const std::string &dirname, const std::string &filename)
405 : fStat(stat), fDirName(dirname), fFileName(filename)
406{
407}
408
409/////////////////////////////////////////////////////////////////////////////////
410/// return file name
411
412std::string RSysFile::GetName() const
413{
414 return fFileName;
415}
416
417/////////////////////////////////////////////////////////////////////////////////
418/// Check if file name the same, ignore case on Windows
419
420bool RSysFile::MatchName(const std::string &name) const
421{
422 auto ownname = GetName();
423
424#ifdef _MSC_VER
425
426 return std::equal(name.begin(), name.end(),
427 ownname.begin(), ownname.end(),
428 [](char a, char b) {
429 return tolower(a) == tolower(b);
430 });
431#else
432
433 return ownname == name;
434
435#endif
436}
437
438/////////////////////////////////////////////////////////////////////////////////
439/// Get default action for the file
440/// Either start text editor or image viewer or just do file browsing
441
443{
444 if (R_ISDIR(fStat.fMode)) return kActBrowse;
445
446 auto icon = GetFileIcon(GetName());
447 if (icon == "sap-icon://document-text"s) return kActEdit;
448 if (icon == "sap-icon://picture"s) return kActImage;
449 if (icon == "sap-icon://org-chart"s) return kActBrowse;
450 return kActNone;
451}
452
453/////////////////////////////////////////////////////////////////////////////////
454/// Returns full file name - including fully qualified path
455
456std::string RSysFile::GetFullName() const
457{
458 return fDirName + fFileName;
459}
460
461/////////////////////////////////////////////////////////////////////////////////
462/// Returns iterator for files in directory
463
464std::unique_ptr<RLevelIter> RSysFile::GetChildsIter()
465{
466 if (!R_ISDIR(fStat.fMode))
467 return nullptr;
468
469 return std::make_unique<RSysDirLevelIter>(GetFullName());
470}
471
472/////////////////////////////////////////////////////////////////////////////////
473/// Returns file content of requested kind
474
475std::string RSysFile::GetContent(const std::string &kind)
476{
477 if ((GetContentKind(kind) == kText) && (GetFileIcon(GetName()) == "sap-icon://document-text"s)) {
478 std::ifstream t(GetFullName());
479 return std::string(std::istreambuf_iterator<char>(t), std::istreambuf_iterator<char>());
480 }
481
482 if ((GetContentKind(kind) == kImage) && (GetFileIcon(GetName()) == "sap-icon://picture"s)) {
483 std::ifstream t(GetFullName(), std::ios::binary);
484 std::string content = std::string(std::istreambuf_iterator<char>(t), std::istreambuf_iterator<char>());
485
486 auto encode = TBase64::Encode(content.data(), content.length());
487
488 auto pos = GetName().rfind(".");
489
490 std::string image_kind = GetName().substr(pos+1);
491 std::transform(image_kind.begin(), image_kind.end(), image_kind.begin(), ::tolower);
492 if (image_kind == "svg") image_kind = "svg+xml";
493
494 return "data:image/"s + image_kind + ";base64,"s + encode.Data();
495 }
496
497 if (GetContentKind(kind) == kFileName) {
498 return GetFullName();
499 }
500
501 return ""s;
502}
503
504
505/////////////////////////////////////////////////////////////////////////////////
506/// Provide top entries for file system
507/// On windows it is list of existing drivers, on Linux it is "File system" and "Home"
508
509RElementPath_t RSysFile::ProvideTopEntries(std::shared_ptr<RGroup> &comp, const std::string &workdir)
510{
511 std::string seldir = workdir;
512
513 if (seldir.empty())
514 seldir = gSystem->WorkingDirectory();
515
516 seldir = gSystem->UnixPathName(seldir.c_str());
517
518 auto volumes = gSystem->GetVolumes("all");
519 if (volumes) {
520 // this is Windows
521 TIter iter(volumes);
522 TObject *obj;
523 while ((obj = iter()) != nullptr) {
524 std::string name = obj->GetName();
525 std::string dir = name + "\\"s;
526 comp->Add(std::make_shared<Browsable::RWrapper>(name, std::make_unique<RSysFile>(dir)));
527 }
528 delete volumes;
529
530 } else {
531 comp->Add(std::make_shared<Browsable::RWrapper>("File system", std::make_unique<RSysFile>("/")));
532
533 seldir = "/File system"s + seldir;
534
535 std::string homedir = gSystem->UnixPathName(gSystem->HomeDirectory());
536
537 if (!homedir.empty())
538 comp->Add(std::make_shared<Browsable::RWrapper>("Home", std::make_unique<RSysFile>(homedir)));
539 }
540
541 return RElement::ParsePath(seldir);
542}
543
544/////////////////////////////////////////////////////////////////////////////////
545/// Return working path in browser hierarchy
546
547RElementPath_t RSysFile::GetWorkingPath(const std::string &workdir)
548{
549 std::string seldir = workdir;
550
551 if (seldir.empty())
552 seldir = gSystem->WorkingDirectory();
553
554 seldir = gSystem->UnixPathName(seldir.c_str());
555
556 auto volumes = gSystem->GetVolumes("all");
557 if (volumes) {
558 delete volumes;
559 } else {
560 seldir = "/File system"s + seldir;
561 }
562
563 return RElement::ParsePath(seldir);
564}
565
#define R__LOG_ERROR(...)
Definition RLogger.hxx:356
#define R__LOG_DEBUG(DEBUGLEVEL,...)
Definition RLogger.hxx:359
#define b(i)
Definition RSha256.hxx:100
#define a(i)
Definition RSha256.hxx:99
char name[80]
Definition TGX11.cxx:148
#define INVALID_HANDLE_VALUE
Definition TMapFile.cxx:84
Bool_t R_ISFIFO(Int_t mode)
Definition TSystem.h:128
Bool_t R_ISSOCK(Int_t mode)
Definition TSystem.h:129
Bool_t R_ISBLK(Int_t mode)
Definition TSystem.h:125
Bool_t R_ISREG(Int_t mode)
Definition TSystem.h:126
Bool_t R_ISDIR(Int_t mode)
Definition TSystem.h:123
externTSystem * gSystem
Definition TSystem.h:582
Bool_t R_ISCHR(Int_t mode)
Definition TSystem.h:124
@ kS_IRGRP
Definition TSystem.h:114
@ kS_IWUSR
Definition TSystem.h:111
@ kS_ISUID
Definition TSystem.h:106
@ kS_IRUSR
Definition TSystem.h:110
@ kS_IXOTH
Definition TSystem.h:120
@ kS_ISGID
Definition TSystem.h:107
@ kS_IROTH
Definition TSystem.h:118
@ kS_IWGRP
Definition TSystem.h:115
@ kS_IXUSR
Definition TSystem.h:112
@ kS_ISVTX
Definition TSystem.h:108
@ kS_IWOTH
Definition TSystem.h:119
@ kS_IXGRP
Definition TSystem.h:116
#define BUFSIZE
const char * extension
Definition civetweb.c:8515
#define snprintf
Definition civetweb.c:1579
static EContentKind GetContentKind(const std::string &kind)
Find item with specified name Default implementation, should work for all.
Definition RElement.cxx:54
@ kFileName
"filename" - file name if applicable
Definition RElement.hxx:44
@ kText
"text" - plain text for code editor
Definition RElement.hxx:39
@ kImage
"image64" - base64 for supported image formats (png/gif/gpeg)
Definition RElement.hxx:40
EActionKind
Possible actions on double-click.
Definition RElement.hxx:50
@ kActImage
can be shown in image viewer, can provide image
Definition RElement.hxx:54
@ kActBrowse
just browse (expand) item
Definition RElement.hxx:52
@ kActEdit
can provide data for text editor
Definition RElement.hxx:53
static RElementPath_t ParsePath(const std::string &str)
Parse string path to produce RElementPath_t One should avoid to use string pathes as much as possible...
Definition RElement.cxx:118
Iterator over single level hierarchy like any array, keys list, ...
static bool IsFileFormatSupported(const std::string &extension)
static std::shared_ptr< RElement > OpenFile(const std::string &extension, const std::string &fullname)
void * fDir
! current directory handle
Definition RSysFile.cxx:64
FileStat_t fCurrentStat
! stat for current file name
Definition RSysFile.cxx:67
bool Find(const std::string &name, int=-1) override
Find item with specified name Default implementation, should work for all If index specified,...
Definition RSysFile.cxx:231
std::string fItemName
! current item name
Definition RSysFile.cxx:66
void CloseDir()
Close directory for listing.
Definition RSysFile.cxx:126
std::string GetFileExtension(const std::string &fname) const
Definition RSysFile.cxx:215
std::string FullDirName() const
Return full dir name with appropriate slash at the end.
Definition RSysFile.cxx:136
std::shared_ptr< RElement > GetElement() override
Returns full information for current element.
Definition RSysFile.cxx:331
bool TestDirEntry(const std::string &name)
Check if entry of that name exists.
Definition RSysFile.cxx:150
std::string fCurrentName
! current file name
Definition RSysFile.cxx:65
RSysDirLevelIter(const std::string &path="")
Definition RSysFile.cxx:225
std::string fPath
! fully qualified path without final slash
Definition RSysFile.cxx:63
std::unique_ptr< RItem > CreateItem() override
Create generic description item for RBrowser.
Definition RSysFile.cxx:255
bool OpenDir()
Open directory for listing.
Definition RSysFile.cxx:70
bool NextDirEntry()
Trying to produce next entry.
Definition RSysFile.cxx:185
bool Next() override
Shift to next entry.
Definition RSysFile.cxx:229
bool CanItemHaveChilds() const override
Returns true if directory or is file format supported.
Definition RSysFile.cxx:244
std::string GetItemName() const override
Returns current entry name.
Definition RSysFile.cxx:241
std::string GetFullName() const
Returns full file name - including fully qualified path.
Definition RSysFile.cxx:456
std::unique_ptr< RLevelIter > GetChildsIter() override
Returns iterator for files in directory.
Definition RSysFile.cxx:464
std::string fDirName
! fully-qualified directory name
Definition RSysFile.hxx:32
FileStat_t fStat
! file stat object
Definition RSysFile.hxx:31
RSysFile(const std::string &filename)
Create file element.
Definition RSysFile.cxx:383
std::string GetName() const override
Name of RElement - file name in this case.
Definition RSysFile.cxx:412
static std::string GetFileIcon(const std::string &fname)
Get icon for the type of given file name.
Definition RSysFile.cxx:355
EActionKind GetDefaultAction() const override
Get default action for the file Either start text editor or image viewer or just do file browsing.
Definition RSysFile.cxx:442
std::string GetContent(const std::string &kind) override
Returns file content of requested kind.
Definition RSysFile.cxx:475
static RElementPath_t ProvideTopEntries(std::shared_ptr< RGroup > &comp, const std::string &workdir="")
Provide top entries for file system On windows it is list of existing drivers, on Linux it is "File s...
Definition RSysFile.cxx:509
bool MatchName(const std::string &name) const override
Checks if element name match to provided value.
Definition RSysFile.cxx:420
std::string fFileName
! file name in current dir
Definition RSysFile.hxx:33
static RElementPath_t GetWorkingPath(const std::string &workdir="")
Return working path in browser hierarchy.
Definition RSysFile.cxx:547
static TString Encode(const char *data)
Transform data into a null terminated base64 string.
Definition TBase64.cxx:106
Mother of all ROOT objects.
Definition TObject.h:42
virtual const char * GetName() const
Returns name of object.
Definition TObject.cxx:462
const char * Data() const
Definition TString.h:384
std::vector< std::string > RElementPath_t
Definition RElement.hxx:20
bool EndsWith(std::string_view string, std::string_view suffix)
ROOT::RLogChannel & BrowsableLog()
Log channel for Browsable diagnostics.
Definition RElement.cxx:22
TCanvas * slash()
Definition slash.C:1
TString fUser
Definition TSystem.h:149
TString fGroup
Definition TSystem.h:150