Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
TApplication.cxx
Go to the documentation of this file.
1 // @(#)root/base:$Id$
2 // Author: Fons Rademakers 22/12/95
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 /** \class TApplication
13 \ingroup Base
14 
15 This class creates the ROOT Application Environment that interfaces
16 to the windowing system eventloop and eventhandlers.
17 This class must be instantiated exactly once in any given
18 application. Normally the specific application class inherits from
19 TApplication (see TRint).
20 */
21 
22 #include "RConfigure.h"
23 #include "Riostream.h"
24 #include "TApplication.h"
25 #include "TException.h"
26 #include "TGuiFactory.h"
27 #include "TVirtualX.h"
28 #include "TROOT.h"
29 #include "TSystem.h"
30 #include "TString.h"
31 #include "TError.h"
32 #include "TObjArray.h"
33 #include "TObjString.h"
34 #include "TTimer.h"
35 #include "TInterpreter.h"
36 #include "TStyle.h"
37 #include "TVirtualPad.h"
38 #include "TEnv.h"
39 #include "TColor.h"
40 #include "TClassTable.h"
41 #include "TPluginManager.h"
42 #include "TClassTable.h"
43 #include "TBrowser.h"
44 #include "TUrl.h"
45 #include "TVirtualMutex.h"
46 #include "TClassEdit.h"
47 #include "TMethod.h"
48 #include "TDataMember.h"
49 #include "TApplicationCommandLineOptionsHelp.h"
50 #include "TPRegexp.h"
51 #include <stdlib.h>
52 
53 TApplication *gApplication = 0;
54 Bool_t TApplication::fgGraphNeeded = kFALSE;
55 Bool_t TApplication::fgGraphInit = kFALSE;
56 TList *TApplication::fgApplications = 0; // List of available applications
57 
58 ////////////////////////////////////////////////////////////////////////////////
59 
60 class TIdleTimer : public TTimer {
61 public:
62  TIdleTimer(Long_t ms) : TTimer(ms, kTRUE) { }
63  Bool_t Notify();
64 };
65 
66 ////////////////////////////////////////////////////////////////////////////////
67 /// Notify handler.
68 
69 Bool_t TIdleTimer::Notify()
70 {
71  gApplication->HandleIdleTimer();
72  Reset();
73  return kFALSE;
74 }
75 
76 
77 ClassImp(TApplication);
78 
79 static void CallEndOfProcessCleanups()
80 {
81  // Insure that the files, canvases and sockets are closed.
82 
83  // If we get here, the tear down has started. We have no way to know what
84  // has or has not yet been done. In particular on Ubuntu, this was called
85  // after the function static in TSystem.cxx has been destructed. So we
86  // set gROOT in its end-of-life mode which prevents executing code, like
87  // autoloading libraries (!) that is pointless ...
88  if (gROOT) {
89  gROOT->SetBit(kInvalidObject);
90  gROOT->EndOfProcessCleanups();
91  }
92 }
93 
94 ////////////////////////////////////////////////////////////////////////////////
95 /// Default ctor. Can be used by classes deriving from TApplication.
96 
97 TApplication::TApplication() :
98  fArgc(0), fArgv(0), fAppImp(0), fIsRunning(kFALSE), fReturnFromRun(kFALSE),
99  fNoLog(kFALSE), fNoLogo(kFALSE), fQuit(kFALSE), fUseMemstat(kFALSE),
100  fFiles(0), fIdleTimer(0), fSigHandler(0), fExitOnException(kDontExit),
101  fAppRemote(0)
102 {
103  ResetBit(kProcessRemotely);
104 }
105 
106 ////////////////////////////////////////////////////////////////////////////////
107 /// Create an application environment. The application environment
108 /// provides an interface to the graphics system and eventloop
109 /// (be it X, Windows, MacOS or BeOS). After creating the application
110 /// object start the eventloop by calling its Run() method. The command
111 /// line options recognized by TApplication are described in the GetOptions()
112 /// method. The recognized options are removed from the argument array.
113 /// The original list of argument options can be retrieved via the Argc()
114 /// and Argv() methods. The appClassName "proofserv" is reserved for the
115 /// PROOF system. The "options" and "numOptions" arguments are not used,
116 /// except if you want to by-pass the argv processing by GetOptions()
117 /// in which case you should specify numOptions<0. All options will
118 /// still be available via the Argv() method for later use.
119 
120 TApplication::TApplication(const char *appClassName, Int_t *argc, char **argv,
121  void * /*options*/, Int_t numOptions) :
122  fArgc(0), fArgv(0), fAppImp(0), fIsRunning(kFALSE), fReturnFromRun(kFALSE),
123  fNoLog(kFALSE), fNoLogo(kFALSE), fQuit(kFALSE), fUseMemstat(kFALSE),
124  fFiles(0), fIdleTimer(0), fSigHandler(0), fExitOnException(kDontExit),
125  fAppRemote(0)
126 {
127  R__LOCKGUARD(gInterpreterMutex);
128 
129  // Create the list of applications the first time
130  if (!fgApplications)
131  fgApplications = new TList;
132 
133  // Add the new TApplication early, so that the destructor of the
134  // default TApplication (if it is called in the block of code below)
135  // will not destroy the files, socket or TColor that have already been
136  // created.
137  fgApplications->Add(this);
138 
139  if (gApplication && gApplication->TestBit(kDefaultApplication)) {
140  // allow default TApplication to be replaced by a "real" TApplication
141  delete gApplication;
142  gApplication = 0;
143  gROOT->SetBatch(kFALSE);
144  fgGraphInit = kFALSE;
145  }
146 
147  if (gApplication) {
148  Error("TApplication", "only one instance of TApplication allowed");
149  fgApplications->Remove(this);
150  return;
151  }
152 
153  if (!gROOT)
154  ::Fatal("TApplication::TApplication", "ROOT system not initialized");
155 
156  if (!gSystem)
157  ::Fatal("TApplication::TApplication", "gSystem not initialized");
158 
159  static Bool_t hasRegisterAtExit(kFALSE);
160  if (!hasRegisterAtExit) {
161  // If we are the first TApplication register the atexit)
162  atexit(CallEndOfProcessCleanups);
163  hasRegisterAtExit = kTRUE;
164  }
165  gROOT->SetName(appClassName);
166 
167  // copy command line arguments, can be later accessed via Argc() and Argv()
168  if (argc && *argc > 0) {
169  fArgc = *argc;
170  fArgv = (char **)new char*[fArgc];
171  }
172 
173  for (int i = 0; i < fArgc; i++)
174  fArgv[i] = StrDup(argv[i]);
175 
176  if (numOptions >= 0)
177  GetOptions(argc, argv);
178 
179  if (fArgv)
180  gSystem->SetProgname(fArgv[0]);
181 
182  // Tell TSystem the TApplication has been created
183  gSystem->NotifyApplicationCreated();
184 
185  fAppImp = gGuiFactory->CreateApplicationImp(appClassName, argc, argv);
186  ResetBit(kProcessRemotely);
187 
188  // Initialize the graphics environment
189  if (gClassTable->GetDict("TPad")) {
190  fgGraphNeeded = kTRUE;
191  InitializeGraphics();
192  }
193 
194  // Save current interpreter context
195  gInterpreter->SaveContext();
196  gInterpreter->SaveGlobalsContext();
197 
198  // to allow user to interact with TCanvas's under WIN32
199  gROOT->SetLineHasBeenProcessed();
200 
201  // activate TMemStat
202  if (fUseMemstat || gEnv->GetValue("Root.TMemStat", 0)) {
203  fUseMemstat = kTRUE;
204  Int_t buffersize = gEnv->GetValue("Root.TMemStat.buffersize", 100000);
205  Int_t maxcalls = gEnv->GetValue("Root.TMemStat.maxcalls", 5000000);
206  const char *ssystem = gEnv->GetValue("Root.TMemStat.system","gnubuiltin");
207  if (maxcalls > 0) {
208  gROOT->ProcessLine(Form("new TMemStat(\"%s\",%d,%d);",ssystem,buffersize,maxcalls));
209  }
210  }
211 
212  //Needs to be done last
213  gApplication = this;
214  gROOT->SetApplication(this);
215 
216 }
217 
218 ////////////////////////////////////////////////////////////////////////////////
219 /// TApplication dtor.
220 
221 TApplication::~TApplication()
222 {
223  for (int i = 0; i < fArgc; i++)
224  if (fArgv[i]) delete [] fArgv[i];
225  delete [] fArgv;
226 
227  if (fgApplications)
228  fgApplications->Remove(this);
229 
230  //close TMemStat
231  if (fUseMemstat) {
232  ProcessLine("TMemStat::Close()");
233  fUseMemstat = kFALSE;
234  }
235 
236  // Reduce the risk of the files or sockets being closed after the
237  // end of 'main' (or more exactly before the library start being
238  // unloaded).
239  if (fgApplications == 0 || fgApplications->FirstLink() == 0 ) {
240  TROOT::ShutDown();
241  }
242 
243  // Now that all the canvases and files have been closed we can
244  // delete the implementation.
245  SafeDelete(fAppImp);
246 }
247 
248 ////////////////////////////////////////////////////////////////////////////////
249 /// Static method. This method should be called from static library
250 /// initializers if the library needs the low level graphics system.
251 
252 void TApplication::NeedGraphicsLibs()
253 {
254  fgGraphNeeded = kTRUE;
255 }
256 
257 ////////////////////////////////////////////////////////////////////////////////
258 /// Initialize the graphics environment.
259 
260 void TApplication::InitializeGraphics()
261 {
262  if (fgGraphInit || !fgGraphNeeded) return;
263 
264  // Load the graphics related libraries
265  LoadGraphicsLibs();
266 
267  // Try to load TrueType font renderer. Only try to load if not in batch
268  // mode and Root.UseTTFonts is true and Root.TTFontPath exists. Abort silently
269  // if libttf or libGX11TTF are not found in $ROOTSYS/lib or $ROOTSYS/ttf/lib.
270  const char *ttpath = gEnv->GetValue("Root.TTFontPath",
271  TROOT::GetTTFFontDir());
272  char *ttfont = gSystem->Which(ttpath, "arialbd.ttf", kReadPermission);
273  // Check for use of DFSG - fonts
274  if (!ttfont)
275  ttfont = gSystem->Which(ttpath, "FreeSansBold.ttf", kReadPermission);
276 
277 #if !defined(R__WIN32)
278  if (!gROOT->IsBatch() && !strcmp(gVirtualX->GetName(), "X11") &&
279  ttfont && gEnv->GetValue("Root.UseTTFonts", 1)) {
280  if (gClassTable->GetDict("TGX11TTF")) {
281  // in principle we should not have linked anything against libGX11TTF
282  // but with ACLiC this can happen, initialize TGX11TTF by hand
283  // (normally this is done by the static library initializer)
284  ProcessLine("TGX11TTF::Activate();");
285  } else {
286  TPluginHandler *h;
287  if ((h = gROOT->GetPluginManager()->FindHandler("TVirtualX", "x11ttf")))
288  if (h->LoadPlugin() == -1)
289  Info("InitializeGraphics", "no TTF support");
290  }
291  }
292 #endif
293  delete [] ttfont;
294 
295  // Create WM dependent application environment
296  if (fAppImp)
297  delete fAppImp;
298  fAppImp = gGuiFactory->CreateApplicationImp(gROOT->GetName(), &fArgc, fArgv);
299  if (!fAppImp) {
300  MakeBatch();
301  fAppImp = gGuiFactory->CreateApplicationImp(gROOT->GetName(), &fArgc, fArgv);
302  }
303 
304  // Create the canvas colors early so they are allocated before
305  // any color table expensive bitmaps get allocated in GUI routines (like
306  // creation of XPM bitmaps).
307  TColor::InitializeColors();
308 
309  // Hook for further initializing the WM dependent application environment
310  Init();
311 
312  // Set default screen factor (if not disabled in rc file)
313  if (gEnv->GetValue("Canvas.UseScreenFactor", 1)) {
314  Int_t x, y;
315  UInt_t w, h;
316  if (gVirtualX) {
317  gVirtualX->GetGeometry(-1, x, y, w, h);
318  if (h > 0 && h < 1000) gStyle->SetScreenFactor(0.0011*h);
319  }
320  }
321 }
322 
323 ////////////////////////////////////////////////////////////////////////////////
324 /// Clear list containing macro files passed as program arguments.
325 /// This method is called from TRint::Run() to ensure that the macro
326 /// files are only executed the first time Run() is called.
327 
328 void TApplication::ClearInputFiles()
329 {
330  if (fFiles) {
331  fFiles->Delete();
332  SafeDelete(fFiles);
333  }
334 }
335 
336 ////////////////////////////////////////////////////////////////////////////////
337 /// Return specified argument.
338 
339 char *TApplication::Argv(Int_t index) const
340 {
341  if (fArgv) {
342  if (index >= fArgc) {
343  Error("Argv", "index (%d) >= number of arguments (%d)", index, fArgc);
344  return 0;
345  }
346  return fArgv[index];
347  }
348  return 0;
349 }
350 
351 ////////////////////////////////////////////////////////////////////////////////
352 /// Get and handle command line options. Arguments handled are removed
353 /// from the argument array. See CommandLineOptionsHelp.h for options.
354 
355 void TApplication::GetOptions(Int_t *argc, char **argv)
356 {
357  static char null[1] = { "" };
358 
359  fNoLog = kFALSE;
360  fQuit = kFALSE;
361  fFiles = 0;
362 
363  if (!argc)
364  return;
365 
366  int i, j;
367  TString pwd;
368 
369  for (i = 1; i < *argc; i++) {
370  if (!strcmp(argv[i], "-?") || !strncmp(argv[i], "-h", 2) ||
371  !strncmp(argv[i], "--help", 6)) {
372  fprintf(stderr, kCommandLineOptionsHelp);
373  Terminate(0);
374  } else if (!strncmp(argv[i], "--version", 9)) {
375  fprintf(stderr, "ROOT Version: %s\n", gROOT->GetVersion());
376  fprintf(stderr, "Built for %s on %s\n",
377  gSystem->GetBuildArch(),
378  gROOT->GetGitDate());
379 
380  fprintf(stderr, "From %s@%s\n",
381  gROOT->GetGitBranch(),
382  gROOT->GetGitCommit());
383 
384  Terminate(0);
385  } else if (!strcmp(argv[i], "-config")) {
386  fprintf(stderr, "ROOT ./configure options:\n%s\n", gROOT->GetConfigOptions());
387  Terminate(0);
388  } else if (!strcmp(argv[i], "-memstat")) {
389  fUseMemstat = kTRUE;
390  argv[i] = null;
391  } else if (!strcmp(argv[i], "-b")) {
392  MakeBatch();
393  argv[i] = null;
394  } else if (!strcmp(argv[i], "-n")) {
395  fNoLog = kTRUE;
396  argv[i] = null;
397  } else if (!strcmp(argv[i], "-t")) {
398  ROOT::EnableImplicitMT();
399  // EnableImplicitMT() only enables thread safety if IMT was configured;
400  // enable thread safety even with IMT off:
401  ROOT::EnableThreadSafety();
402  argv[i] = null;
403  } else if (!strcmp(argv[i], "-q")) {
404  fQuit = kTRUE;
405  argv[i] = null;
406  } else if (!strcmp(argv[i], "-l")) {
407  // used by front-end program to not display splash screen
408  fNoLogo = kTRUE;
409  argv[i] = null;
410  } else if (!strcmp(argv[i], "-x")) {
411  fExitOnException = kExit;
412  argv[i] = null;
413  } else if (!strcmp(argv[i], "-splash")) {
414  // used when started by front-end program to signal that
415  // splash screen can be popped down (TRint::PrintLogo())
416  argv[i] = null;
417  } else if (strncmp(argv[i], "--web", 5) == 0) {
418  // the web mode is requested
419  const char *opt = argv[i] + 5;
420  argv[i] = null;
421  TString argw;
422  if (gROOT->IsBatch()) argw = "batch";
423  if (*opt == '=') argw.Append(opt+1);
424  if (gSystem->Load("libROOTWebDisplay") >= 0) {
425  gROOT->SetWebDisplay(argw.Data());
426  gEnv->SetValue("Gui.Factory", "web");
427  } else {
428  Error("GetOptions", "--web option not supported, ROOT should be built with at least c++14 enabled");
429  }
430  } else if (!strcmp(argv[i], "-e")) {
431  argv[i] = null;
432  ++i;
433 
434  if ( i < *argc ) {
435  if (!fFiles) fFiles = new TObjArray;
436  TObjString *expr = new TObjString(argv[i]);
437  expr->SetBit(kExpression);
438  fFiles->Add(expr);
439  argv[i] = null;
440  } else {
441  Warning("GetOptions", "-e must be followed by an expression.");
442  }
443  } else if (!strcmp(argv[i], "--")) {
444  TObjString* macro = nullptr;
445  bool warnShown = false;
446 
447  if (fFiles) {
448  for (auto f: *fFiles) {
449  TObjString* file = dynamic_cast<TObjString*>(f);
450  if (!file) {
451  if (!dynamic_cast<TNamed*>(f)) {
452  Error("GetOptions()", "Inconsistent file entry (not a TObjString)!");
453  f->Dump();
454  } // else we did not find the file.
455  continue;
456  }
457 
458  if (file->TestBit(kExpression))
459  continue;
460  if (file->String().EndsWith(".root"))
461  continue;
462  if (file->String().Contains('('))
463  continue;
464 
465  if (macro && !warnShown && (warnShown = true))
466  Warning("GetOptions", "-- is used with several macros. "
467  "The arguments will be passed to the last one.");
468 
469  macro = file;
470  }
471  }
472 
473  if (macro) {
474  argv[i] = null;
475  ++i;
476  TString& str = macro->String();
477 
478  str += '(';
479  for (; i < *argc; i++) {
480  str += argv[i];
481  str += ',';
482  argv[i] = null;
483  }
484  str.EndsWith(",") ? str[str.Length() - 1] = ')' : str += ')';
485  } else {
486  Warning("GetOptions", "no macro to pass arguments to was provided. "
487  "Everything after the -- will be ignored.");
488  for (; i < *argc; i++)
489  argv[i] = null;
490  }
491  } else if (argv[i][0] != '-' && argv[i][0] != '+') {
492  Long64_t size;
493  Long_t id, flags, modtime;
494  char *arg = strchr(argv[i], '(');
495  if (arg) *arg = '\0';
496  char *dir = gSystem->ExpandPathName(argv[i]);
497  // ROOT-9959: we do not continue if we could not expand the path
498  if (!dir) continue;
499  TUrl udir(dir, kTRUE);
500  // remove options and anchor to check the path
501  TString sfx = udir.GetFileAndOptions();
502  TString fln = udir.GetFile();
503  sfx.Replace(sfx.Index(fln), fln.Length(), "");
504  TString path = udir.GetFile();
505  if (strcmp(udir.GetProtocol(), "file")) {
506  path = udir.GetUrl();
507  path.Replace(path.Index(sfx), sfx.Length(), "");
508  }
509  // 'path' is the full URL without suffices (options and/or anchor)
510  if (arg) *arg = '(';
511  if (!arg && !gSystem->GetPathInfo(path.Data(), &id, &size, &flags, &modtime)) {
512  if ((flags & 2)) {
513  // if directory set it in fWorkDir
514  if (pwd == "") {
515  pwd = gSystem->WorkingDirectory();
516  fWorkDir = dir;
517  gSystem->ChangeDirectory(dir);
518  argv[i] = null;
519  } else if (!strcmp(gROOT->GetName(), "Rint")) {
520  Warning("GetOptions", "only one directory argument can be specified (%s)", dir);
521  }
522  } else if (size > 0) {
523  // if file add to list of files to be processed
524  if (!fFiles) fFiles = new TObjArray;
525  fFiles->Add(new TObjString(path.Data()));
526  argv[i] = null;
527  } else {
528  Warning("GetOptions", "file %s has size 0, skipping", dir);
529  }
530  } else {
531  if (TString(udir.GetFile()).EndsWith(".root")) {
532  if (!strcmp(udir.GetProtocol(), "file")) {
533  // file ending on .root but does not exist, likely a typo
534  // warn user if plain root...
535  if (!strcmp(gROOT->GetName(), "Rint"))
536  Warning("GetOptions", "file %s not found", dir);
537  } else {
538  // remote file, give it the benefit of the doubt and add it to list of files
539  if (!fFiles) fFiles = new TObjArray;
540  fFiles->Add(new TObjString(argv[i]));
541  argv[i] = null;
542  }
543  } else {
544  TString mode,fargs,io;
545  TString fname = gSystem->SplitAclicMode(dir,mode,fargs,io);
546  char *mac;
547  if (!fFiles) fFiles = new TObjArray;
548  if ((mac = gSystem->Which(TROOT::GetMacroPath(), fname,
549  kReadPermission))) {
550  // if file add to list of files to be processed
551  fFiles->Add(new TObjString(argv[i]));
552  argv[i] = null;
553  delete [] mac;
554  } else {
555  // if file add an invalid entry to list of files to be processed
556  fFiles->Add(new TNamed("NOT FOUND!", argv[i]));
557  // only warn if we're plain root,
558  // other progs might have their own params
559  if (!strcmp(gROOT->GetName(), "Rint"))
560  Warning("GetOptions", "macro %s not found", fname.Data());
561  }
562  }
563  }
564  delete [] dir;
565  }
566  // ignore unknown options
567  }
568 
569  // go back to startup directory
570  if (pwd != "")
571  gSystem->ChangeDirectory(pwd);
572 
573  // remove handled arguments from argument array
574  j = 0;
575  for (i = 0; i < *argc; i++) {
576  if (strcmp(argv[i], "")) {
577  argv[j] = argv[i];
578  j++;
579  }
580  }
581 
582  *argc = j;
583 }
584 
585 ////////////////////////////////////////////////////////////////////////////////
586 /// Handle idle timeout. When this timer expires the registered idle command
587 /// will be executed by this routine and a signal will be emitted.
588 
589 void TApplication::HandleIdleTimer()
590 {
591  if (!fIdleCommand.IsNull())
592  ProcessLine(GetIdleCommand());
593 
594  Emit("HandleIdleTimer()");
595 }
596 
597 ////////////////////////////////////////////////////////////////////////////////
598 /// Handle exceptions (kSigBus, kSigSegmentationViolation,
599 /// kSigIllegalInstruction and kSigFloatingException) trapped in TSystem.
600 /// Specific TApplication implementations may want something different here.
601 
602 void TApplication::HandleException(Int_t sig)
603 {
604  if (TROOT::Initialized()) {
605  if (gException) {
606  gInterpreter->RewindDictionary();
607  gInterpreter->ClearFileBusy();
608  }
609  if (fExitOnException == kExit)
610  gSystem->Exit(128 + sig);
611  else if (fExitOnException == kAbort)
612  gSystem->Abort();
613  else
614  Throw(sig);
615  }
616  gSystem->Exit(128 + sig);
617 }
618 
619 ////////////////////////////////////////////////////////////////////////////////
620 /// Set the exit on exception option. Setting this option determines what
621 /// happens in HandleException() in case an exception (kSigBus,
622 /// kSigSegmentationViolation, kSigIllegalInstruction or kSigFloatingException)
623 /// is trapped. Choices are: kDontExit (default), kExit or kAbort.
624 /// Returns the previous value.
625 
626 TApplication::EExitOnException TApplication::ExitOnException(TApplication::EExitOnException opt)
627 {
628  EExitOnException old = fExitOnException;
629  fExitOnException = opt;
630  return old;
631 }
632 
633 /////////////////////////////////////////////////////////////////////////////////
634 /// The function generates and executes a command that loads the Doxygen URL in
635 /// a browser. It works for Mac, Windows and Linux. In the case of Linux, the
636 /// function also checks if the DISPLAY is set. If it isn't, a warning message
637 /// and the URL will be displayed on the terminal.
638 ///
639 /// \param[in] url web page to be displayed in a browser
640 
641 void TApplication::OpenInBrowser(const TString &url)
642 {
643  // We check what operating system the user has.
644 #ifdef R__MACOSX
645  // Command for opening a browser on Mac.
646  TString cMac("open ");
647  // We generate the full command and execute it.
648  cMac.Append(url);
649  gSystem->Exec(cMac);
650 #elif defined(R__WIN32)
651  // Command for opening a browser on Windows.
652  TString cWindows("start ");
653  cWindows.Append(url);
654  gSystem->Exec(cWindows);
655 #else
656  // Command for opening a browser in Linux.
657  TString cLinux("xdg-open ");
658  // For Linux we check if the DISPLAY is set.
659  if (gSystem->Getenv("DISPLAY")) {
660  // If the DISPLAY is set it will open the browser.
661  cLinux.Append(url);
662  gSystem->Exec(cLinux);
663  } else {
664  // Else the user will have a warning and the URL in the terminal.
665  Warning("OpenInBrowser", "The $DISPLAY is not set! Please open (e.g. Ctrl-click) %s\n", url.Data());
666  }
667 #endif
668 }
669 
670 namespace {
671 enum EUrl { kURLforClass, kURLforNameSpace, kURLforStruct };
672 ////////////////////////////////////////////////////////////////////////////////
673 /// The function generates a URL address for class or namespace (scopeName).
674 /// This is the URL to the online reference guide, generated by Doxygen.
675 /// With the enumeration "EUrl" we pick which case we need - the one for
676 /// class (kURLforClass) or the one for namespace (kURLforNameSpace).
677 ///
678 /// \param[in] scopeName the name of the class or the namespace
679 /// \param[in] scopeType the enumerator for class or namespace
680 
681 static TString UrlGenerator(TString scopeName, EUrl scopeType)
682 {
683  // We start the URL with a static part, the same for all scopes and members.
684  TString url = "https://root.cern/doc/";
685  // Then we check the ROOT version used.
686  TPRegexp re4(R"(.*/v(\d)-(\d\d)-00-patches)");
687  const char *branchName = gROOT->GetGitBranch();
688  TObjArray *objarr = re4.MatchS(branchName);
689  TString version;
690  // We extract the correct version name for the URL.
691  if (objarr && objarr->GetEntries() == 3) {
692  // We have a valid version of ROOT and we will extract the correct name for the URL.
693  version = ((TObjString *)objarr->At(1))->GetString() + ((TObjString *)objarr->At(2))->GetString();
694  } else {
695  // If it's not a supported version, we will go to "master" branch.
696  version = "master";
697  }
698  delete objarr;
699  url.Append(version);
700  url.Append("/");
701  // We will replace all "::" with "_1_1" and all "_" with "__" in the
702  // classes definitions, due to Doxygen syntax requirements.
703  scopeName.ReplaceAll("_", "__");
704  scopeName.ReplaceAll("::", "_1_1");
705  // We build the URL for the correct scope type and name.
706  if (scopeType == kURLforClass) {
707  url.Append("class");
708  } else if (scopeType == kURLforStruct) {
709  url.Append("struct");
710  } else {
711  url.Append("namespace");
712  }
713  url.Append(scopeName);
714  url.Append(".html");
715  return url;
716 }
717 } // namespace
718 
719 namespace {
720 ////////////////////////////////////////////////////////////////////////////////
721 /// The function returns a TString with the arguments of a method from the
722 /// scope (scopeName), but modified with respect to Doxygen syntax - spacing
723 /// around special symbols and adding the missing scopes ("std::").
724 /// "FormatMethodArgsForDoxygen" works for functions defined inside namespaces
725 /// as well. We avoid looking up twice for the TFunction by passing "func".
726 ///
727 /// \param[in] scopeName the name of the class/namespace/struct
728 /// \param[in] func pointer to the method
729 
730 static TString FormatMethodArgsForDoxygen(const TString &scopeName, TFunction *func)
731 {
732  // With "GetSignature" we get the arguments of the method and put them in a TString.
733  TString methodArguments = func->GetSignature();
734  // "methodArguments" is modified with respect of Doxygen requirements.
735  methodArguments.ReplaceAll(" = ", "=");
736  methodArguments.ReplaceAll("* ", " *");
737  methodArguments.ReplaceAll("*=", " *=");
738  methodArguments.ReplaceAll("*)", " *)");
739  methodArguments.ReplaceAll("*,", " *,");
740  methodArguments.ReplaceAll("*& ", " *&");
741  methodArguments.ReplaceAll("& ", " &");
742  // TODO: prepend "std::" to all stdlib classes!
743  methodArguments.ReplaceAll("ostream", "std::ostream");
744  methodArguments.ReplaceAll("istream", "std::istream");
745  methodArguments.ReplaceAll("map", "std::map");
746  methodArguments.ReplaceAll("vector", "std::vector");
747  // We need to replace the "currentClass::foo" with "foo" in the arguments.
748  // TODO: protect the global functions.
749  TString scopeNameRE("\\b");
750  scopeNameRE.Append(scopeName);
751  scopeNameRE.Append("::\\b");
752  TPRegexp argFix(scopeNameRE);
753  argFix.Substitute(methodArguments, "");
754  return methodArguments;
755 }
756 } // namespace
757 
758 namespace {
759 ////////////////////////////////////////////////////////////////////////////////
760 /// The function checks if a member function of a scope is defined as inline.
761 /// If so, it also checks if it is virtual. Then the return type of "func" is
762 /// modified for the need of Doxygen and with respect to the function
763 /// definition. We pass pointer to the method (func) to not re-do the
764 /// TFunction lookup.
765 ///
766 /// \param[in] scopeName the name of the class/namespace/struct
767 /// \param[in] func pointer to the method
768 
769 static TString FormatReturnTypeForDoxygen(const TString &scopeName, TFunction *func)
770 {
771  // We put the return type of "func" in a TString "returnType".
772  TString returnType = func->GetReturnTypeName();
773  // If the return type is a type nested in the current class, it will appear scoped (Class::Enumeration).
774  // Below we make sure to remove the current class, because the syntax of Doxygen requires it.
775  TString scopeNameRE("\\b");
776  scopeNameRE.Append(scopeName);
777  scopeNameRE.Append("::\\b");
778  TPRegexp returnFix(scopeNameRE);
779  returnFix.Substitute(returnType, "");
780  // We check is if the method is defined as inline.
781  if (func->ExtraProperty() & kIsInlined) {
782  // We check if the function is defined as virtual.
783  if (func->Property() & kIsVirtual) {
784  // If the function is virtual, we append "virtual" before the return type.
785  returnType.Prepend("virtual ");
786  }
787  returnType.ReplaceAll(" *", "*");
788  } else {
789  // If the function is not inline we only change the spacing in "returnType"
790  returnType.ReplaceAll("*", " *");
791  }
792  // In any case (with no respect to virtual/inline check) we need to change
793  // the return type as following.
794  // TODO: prepend "std::" to all stdlib classes!
795  returnType.ReplaceAll("istream", "std::istream");
796  returnType.ReplaceAll("ostream", "std::ostream");
797  returnType.ReplaceAll("map", "std::map");
798  returnType.ReplaceAll("vector", "std::vector");
799  returnType.ReplaceAll("&", " &");
800  return returnType;
801 }
802 } // namespace
803 
804 namespace {
805 ////////////////////////////////////////////////////////////////////////////////
806 /// The function generates a URL for "dataMemberName" defined in "scopeName".
807 /// It returns a TString with the URL used in the online reference guide,
808 /// generated with Doxygen. For data members the URL consist of 2 parts -
809 /// URL for "scopeName" and a part for "dataMemberName".
810 /// For enumerator, the URL could be separated into 3 parts - URL for
811 /// "scopeName", part for the enumeration and a part for the enumerator.
812 ///
813 /// \param[in] scopeName the name of the class/namespace/struct
814 /// \param[in] dataMemberName the name of the data member/enumerator
815 /// \param[in] dataMember pointer to the data member/enumerator
816 /// \param[in] scopeType enumerator to the scope type
817 
818 static TString
819 GetUrlForDataMember(const TString &scopeName, const TString &dataMemberName, TDataMember *dataMember, EUrl scopeType)
820 {
821  // We first check if the data member is not enumerator.
822  if (!dataMember->IsEnum()) {
823  // If we work with data members, we have to append a hashed with MD5 text, consisting of:
824  // "Type ClassName::DataMemberNameDataMemberName(arguments)".
825  // We first get the type of the data member.
826  TString md5DataMember(dataMember->GetFullTypeName());
827  md5DataMember.Append(" ");
828  // We append the scopeName and "::".
829  md5DataMember.Append(scopeName);
830  md5DataMember.Append("::");
831  // We append the dataMemberName twice.
832  md5DataMember.Append(dataMemberName);
833  md5DataMember.Append(dataMemberName);
834  // We call UrlGenerator for the scopeName.
835  TString urlForDataMember = UrlGenerator(scopeName, scopeType);
836  // Then we append "#a" and the hashed text.
837  urlForDataMember.Append("#a");
838  urlForDataMember.Append(md5DataMember.MD5());
839  return urlForDataMember;
840  }
841  // If the data member is enumerator, then we first have to check if the enumeration is anonymous.
842  // Doxygen requires different syntax for anonymous enumeration ("scopeName::@1@1").
843  // We create a TString with the name of the scope and the enumeration from which the enumerator is.
844  TString scopeEnumeration = dataMember->GetTrueTypeName();
845  TString md5EnumClass;
846  if (scopeEnumeration.Contains("(anonymous)")) {
847  // FIXME: need to investigate the numbering scheme.
848  md5EnumClass.Append(scopeName);
849  md5EnumClass.Append("::@1@1");
850  } else {
851  // If the enumeration is not anonymous we put "scopeName::Enumeration" in a TString,
852  // which will be hashed with MD5 later.
853  md5EnumClass.Append(scopeEnumeration);
854  // We extract the part after "::" (this is the enumerator name).
855  TString enumOnlyName = TClassEdit::GetUnqualifiedName(scopeEnumeration);
856  // The syntax is "Class::EnumeratorEnumerator
857  md5EnumClass.Append(enumOnlyName);
858  }
859  // The next part of the URL is hashed "@ scopeName::EnumeratorEnumerator".
860  TString md5Enumerator("@ ");
861  md5Enumerator.Append(scopeName);
862  md5Enumerator.Append("::");
863  md5Enumerator.Append(dataMemberName);
864  md5Enumerator.Append(dataMemberName);
865  // We make the URL for the "scopeName".
866  TString url = UrlGenerator(scopeName, scopeType);
867  // Then we have to append the hashed text for the enumerator.
868  url.Append("#a");
869  url.Append(md5EnumClass.MD5());
870  // We append "a" and then the next hashed text.
871  url.Append("a");
872  url.Append(md5Enumerator.MD5());
873  return url;
874 }
875 } // namespace
876 
877 namespace {
878 ////////////////////////////////////////////////////////////////////////////////
879 /// The function generates URL for enumeration. The hashed text consist of:
880 /// "Class::EnumerationEnumeration".
881 ///
882 /// \param[in] scopeName the name of the class/namespace/struct
883 /// \param[in] enumeration the name of the enumeration
884 /// \param[in] scopeType enumerator for class/namespace/struct
885 
886 static TString GetUrlForEnumeration(TString scopeName, const TString &enumeration, EUrl scopeType)
887 {
888  // The URL consists of URL for the "scopeName", "#a" and hashed as MD5 text.
889  // The text is "Class::EnumerationEnumeration.
890  TString md5Enumeration(scopeName);
891  md5Enumeration.Append("::");
892  md5Enumeration.Append(enumeration);
893  md5Enumeration.Append(enumeration);
894  // We make the URL for the scope "scopeName".
895  TString url(UrlGenerator(scopeName, scopeType));
896  // Then we have to append "#a" and the hashed text.
897  url.Append("#a");
898  url.Append(md5Enumeration.MD5());
899  return url;
900 }
901 } // namespace
902 
903 namespace {
904 enum EMethodKind { kURLforMethod, kURLforStructor };
905 ////////////////////////////////////////////////////////////////////////////////
906 /// The function generates URL for any member function (including Constructor/
907 /// Destructor) of "scopeName". Doxygen first generates the URL for the scope.
908 /// We do that with the help of "UrlGenerator". Then we append "#a" and a
909 /// hashed with MD5 text. It consists of:
910 /// "ReturnType ScopeName::MethodNameMethodName(Method arguments)".
911 /// For constructor/destructor of a class, the return type is not appended.
912 ///
913 /// \param[in] scopeName the name of the class/namespace/struct
914 /// \param[in] methodName the name of the method from the scope
915 /// \param[in] func pointer to the method
916 /// \param[in] methodType enumerator for method or constructor
917 /// \param[in] scopeType enumerator for class/namespace/struct
918 
919 static TString GetUrlForMethod(const TString &scopeName, const TString &methodName, TFunction *func,
920  EMethodKind methodType, EUrl scopeType)
921 {
922  TString md5Text;
923  if (methodType == kURLforMethod) {
924  // In the case of method, we append the return type too.
925  // "FormatReturnTypeForDoxygen" modifies the return type with respect to Doxygen's requirement.
926  md5Text.Append((FormatReturnTypeForDoxygen(scopeName, func)));
927  if (scopeType == kURLforNameSpace) {
928  // We need to append "constexpr" if we work with constexpr functions in namespaces.
929  if (func->Property() & kIsConstexpr) {
930  md5Text.Prepend("constexpr ");
931  }
932  }
933  md5Text.Append(" ");
934  }
935  // We append ScopeName::MethodNameMethodName.
936  md5Text.Append(scopeName);
937  md5Text.Append("::");
938  md5Text.Append(methodName);
939  md5Text.Append(methodName);
940  // We use "FormatMethodArgsForDoxygen" to modify the arguments of Method with respect of Doxygen.
941  md5Text.Append(FormatMethodArgsForDoxygen(scopeName, func));
942  // We generate the URL for the class/namespace/struct.
943  TString url = UrlGenerator(scopeName, scopeType);
944  url.Append("#a");
945  // We append the hashed text.
946  url.Append(md5Text.MD5());
947  return url;
948 }
949 } // namespace
950 
951 
952 ////////////////////////////////////////////////////////////////////////////////
953 /// It opens the online reference guide, generated with Doxygen, for the
954 /// chosen scope (class/namespace/struct) or member (method/function/
955 /// data member/enumeration/enumerator. If the user types incorrect value,
956 /// it will return an error or warning.
957 ///
958 /// \param[in] strippedClass the scope or scope::member
959 
960 void TApplication::OpenReferenceGuideFor(const TString &strippedClass)
961 {
962  // We check if the user is searching for a scope and if the scope exists.
963  if (TClass *clas = TClass::GetClass(strippedClass)) {
964  // We check what scope he is searching for (class/namespace/struct).
965  // Enumerators will switch between the possible cases.
966  EUrl scopeType;
967  if (clas->Property() & kIsNamespace) {
968  scopeType = kURLforNameSpace;
969  } else if (clas->Property() & kIsStruct) {
970  scopeType = kURLforStruct;
971  } else {
972  scopeType = kURLforClass;
973  }
974  // If the user search directly for a scope we open the URL for him with OpenInBrowser.
975  OpenInBrowser(UrlGenerator(strippedClass, scopeType));
976  return;
977  }
978  // Else we subtract the name of the method and remove it from the command.
979  TString memberName = TClassEdit::GetUnqualifiedName(strippedClass);
980  // Error out if "strippedClass" is un-scoped (and it's not a class, see `TClass::GetClass(strippedClass)` above).
981  // TODO: Global functions.
982  if (strippedClass == memberName) {
983  Error("OpenReferenceGuideFor", "Unknown entity \"%s\" - global variables / functions not supported yet!",
984  strippedClass.Data());
985  return;
986  }
987  // Else we remove the member name to be left with the scope.
988  TString scopeName = strippedClass(0, strippedClass.Length() - memberName.Length() - 2);
989  // We check if the scope exists in ROOT.
990  TClass *cl = TClass::GetClass(scopeName);
991  if (!cl) {
992  // That's a member of something ROOT doesn't know.
993  Warning("OpenReferenceGuideFor", "\"%s\" does not exist in ROOT!", scopeName.Data());
994  return;
995  }
996  // We have enumerators for the three available cases - class, namespace and struct.
997  EUrl scopeType;
998  if (cl->Property() & kIsNamespace) {
999  scopeType = kURLforNameSpace;
1000  } else if (cl->Property() & kIsStruct) {
1001  scopeType = kURLforStruct;
1002  } else {
1003  scopeType = kURLforClass;
1004  }
1005  // If the user wants to search for a method, we take its name (memberName) and
1006  // modify it - we delete everything starting at the first "(" so the user won't have to
1007  // do it by hand when they use Tab.
1008  int bracket = memberName.First("(");
1009  if (bracket > 0) {
1010  memberName.Remove(bracket);
1011  }
1012  // We check if "memberName" is a member function of "cl" or any of its base classes.
1013  if (TFunction *func = cl->GetMethodAllAny(memberName)) {
1014  // If so we find the name of the class that it belongs to.
1015  TString baseClName = ((TMethod *)func)->GetClass()->GetName();
1016  // We define an enumerator to distinguish between structor and method.
1017  EMethodKind methodType;
1018  // We check if "memberName" is a constructor.
1019  if (baseClName == memberName) {
1020  methodType = kURLforStructor;
1021  // We check if "memberName" is a destructor.
1022  } else if (memberName[0] == '~') {
1023  methodType = kURLforStructor;
1024  // We check if "memberName" is a method.
1025  } else {
1026  methodType = kURLforMethod;
1027  }
1028  // We call "GetUrlForMethod" for the correct class and scope.
1029  OpenInBrowser(GetUrlForMethod(baseClName, memberName, func, methodType, scopeType));
1030  return;
1031  }
1032  // We check if "memberName" is an enumeration.
1033  if (cl->GetListOfEnums()->FindObject(memberName)) {
1034  // If so with OpenInBrowser we open the URL generated with GetUrlForEnumeration
1035  // with respect to the "scopeType".
1036  OpenInBrowser(GetUrlForEnumeration(scopeName, memberName, scopeType));
1037  return;
1038  }
1039 
1040  // We check if "memberName" is enumerator defined in one the base classes of "scopeName".
1041  if (auto enumerator = (TDataMember *)cl->GetListOfAllPublicDataMembers()->FindObject(memberName)) {
1042  // We find the actual scope (might be in a base) and open the URL in a browser.
1043  TString baseClName = ((TMethod *)enumerator->GetClass())->GetName();
1044  OpenInBrowser(GetUrlForDataMember(baseClName, memberName, enumerator, scopeType));
1045  return;
1046  }
1047 
1048  // Warning message will appear if the user types the function name incorrectly
1049  // or the function is not a member function of "cl" or any of its base classes.
1050  Warning("Help", "cannot find \"%s\" as member of %s or its base classes! Check %s\n", memberName.Data(),
1051  scopeName.Data(), UrlGenerator(scopeName, scopeType).Data());
1052 }
1053 
1054 ////////////////////////////////////////////////////////////////////////////////
1055 /// The function lists useful commands (".help") or opens the online reference
1056 /// guide, generated with Doxygen (".help scope" or ".help scope::member").
1057 ///
1058 /// \param[in] line command from the command line
1059 
1060 void TApplication::Help(const char *line)
1061 {
1062  // We first check if the user wants to print the help on the interpreter.
1063  TString strippedCommand = TString(line).Strip(TString::kBoth);
1064  // If the user chooses ".help" or ".?".
1065  if ((strippedCommand == ".help") || (strippedCommand == ".?")) {
1066  gInterpreter->ProcessLine(line);
1067  Printf("\nROOT special commands.");
1068  Printf("==========================================================================");
1069  Printf(" pwd : show current directory, pad and style");
1070  Printf(" ls : list contents of current directory");
1071  Printf(" which [file] : shows path of macro file");
1072  Printf(" .help Class : opens the reference guide for that class");
1073  Printf(" .help Class::Member : opens the reference guide for function/member");
1074  return;
1075  } else {
1076  // If the user wants to use the extended ".help scopeName" command to access
1077  // the online reference guide, we first check if the command starts correctly.
1078  if ((!strippedCommand.BeginsWith(".help ")) && (!strippedCommand.BeginsWith(".? "))) {
1079  Error("Help", "Unknown command!");
1080  return;
1081  }
1082  // We remove the command ".help" or ".?" from the TString.
1083  if (strippedCommand.BeginsWith(".? ")) {
1084  strippedCommand.Remove(0, 3);
1085  } else {
1086  strippedCommand.Remove(0, 5);
1087  }
1088  // We strip the command line after removing ".help" or ".?".
1089  strippedCommand = strippedCommand.Strip(TString::kBoth);
1090  // We call the function what handles the extended ".help scopeName" command.
1091  OpenReferenceGuideFor(strippedCommand);
1092  }
1093 }
1094 
1095 /// Load shared libs necessary for graphics. These libraries are only
1096 /// loaded when gROOT->IsBatch() is kFALSE.
1097 
1098 void TApplication::LoadGraphicsLibs()
1099 {
1100  if (gROOT->IsBatch()) return;
1101 
1102  TPluginHandler *h;
1103  if ((h = gROOT->GetPluginManager()->FindHandler("TVirtualPad")))
1104  if (h->LoadPlugin() == -1)
1105  return;
1106 
1107  TString name;
1108  TString title1 = "ROOT interface to ";
1109  TString nativex, title;
1110  TString nativeg = "root";
1111 
1112 #ifdef R__WIN32
1113  nativex = "win32gdk";
1114  name = "Win32gdk";
1115  title = title1 + "Win32gdk";
1116 #elif defined(R__HAS_COCOA)
1117  nativex = "quartz";
1118  name = "quartz";
1119  title = title1 + "Quartz";
1120 #else
1121  nativex = "x11";
1122  name = "X11";
1123  title = title1 + "X11";
1124 #endif
1125 
1126  TString guiBackend(gEnv->GetValue("Gui.Backend", "native"));
1127  guiBackend.ToLower();
1128  if (guiBackend == "native") {
1129  guiBackend = nativex;
1130  } else {
1131  name = guiBackend;
1132  title = title1 + guiBackend;
1133  }
1134  TString guiFactory(gEnv->GetValue("Gui.Factory", "native"));
1135  guiFactory.ToLower();
1136  if (guiFactory == "native")
1137  guiFactory = nativeg;
1138 
1139  if ((h = gROOT->GetPluginManager()->FindHandler("TVirtualX", guiBackend))) {
1140  if (h->LoadPlugin() == -1) {
1141  gROOT->SetBatch(kTRUE);
1142  return;
1143  }
1144  gVirtualX = (TVirtualX *) h->ExecPlugin(2, name.Data(), title.Data());
1145  fgGraphInit = kTRUE;
1146  }
1147  if ((h = gROOT->GetPluginManager()->FindHandler("TGuiFactory", guiFactory))) {
1148  if (h->LoadPlugin() == -1) {
1149  gROOT->SetBatch(kTRUE);
1150  return;
1151  }
1152  gGuiFactory = (TGuiFactory *) h->ExecPlugin(0);
1153  }
1154 }
1155 
1156 ////////////////////////////////////////////////////////////////////////////////
1157 /// Switch to batch mode.
1158 
1159 void TApplication::MakeBatch()
1160 {
1161  gROOT->SetBatch();
1162  if (gGuiFactory != gBatchGuiFactory) delete gGuiFactory;
1163  gGuiFactory = gBatchGuiFactory;
1164 #ifndef R__WIN32
1165  if (gVirtualX != gGXBatch) delete gVirtualX;
1166 #endif
1167  gVirtualX = gGXBatch;
1168 }
1169 
1170 ////////////////////////////////////////////////////////////////////////////////
1171 /// Parse the content of a line starting with ".R" (already stripped-off)
1172 /// The format is
1173 /// ~~~ {.cpp}
1174 /// [user@]host[:dir] [-l user] [-d dbg] [script]
1175 /// ~~~
1176 /// The variable 'dir' is the remote directory to be used as working dir.
1177 /// The username can be specified in two ways, "-l" having the priority
1178 /// (as in ssh).
1179 /// A 'dbg' value > 0 gives increasing verbosity.
1180 /// The last argument 'script' allows to specify an alternative script to
1181 /// be executed remotely to startup the session.
1182 
1183 Int_t TApplication::ParseRemoteLine(const char *ln,
1184  TString &hostdir, TString &user,
1185  Int_t &dbg, TString &script)
1186 {
1187  if (!ln || strlen(ln) <= 0)
1188  return 0;
1189 
1190  Int_t rc = 0;
1191  Bool_t isHostDir = kTRUE;
1192  Bool_t isScript = kFALSE;
1193  Bool_t isUser = kFALSE;
1194  Bool_t isDbg = kFALSE;
1195 
1196  TString line(ln);
1197  TString tkn;
1198  Int_t from = 0;
1199  while (line.Tokenize(tkn, from, " ")) {
1200  if (tkn == "-l") {
1201  // Next is a user name
1202  isUser = kTRUE;
1203  } else if (tkn == "-d") {
1204  isDbg = kTRUE;
1205  } else if (tkn == "-close") {
1206  rc = 1;
1207  } else if (tkn.BeginsWith("-")) {
1208  ::Warning("TApplication::ParseRemoteLine","unknown option: %s", tkn.Data());
1209  } else {
1210  if (isUser) {
1211  user = tkn;
1212  isUser = kFALSE;
1213  } else if (isDbg) {
1214  dbg = tkn.Atoi();
1215  isDbg = kFALSE;
1216  } else if (isHostDir) {
1217  hostdir = tkn;
1218  hostdir.ReplaceAll(":","/");
1219  isHostDir = kFALSE;
1220  isScript = kTRUE;
1221  } else if (isScript) {
1222  // Add everything left
1223  script = tkn;
1224  script.Insert(0, "\"");
1225  script += "\"";
1226  isScript = kFALSE;
1227  break;
1228  }
1229  }
1230  }
1231 
1232  // Done
1233  return rc;
1234 }
1235 
1236 ////////////////////////////////////////////////////////////////////////////////
1237 /// Process the content of a line starting with ".R" (already stripped-off)
1238 /// The format is
1239 /// ~~~ {.cpp}
1240 /// [user@]host[:dir] [-l user] [-d dbg] [script] | [host] -close
1241 /// ~~~
1242 /// The variable 'dir' is the remote directory to be used as working dir.
1243 /// The username can be specified in two ways, "-l" having the priority
1244 /// (as in ssh).
1245 /// A 'dbg' value > 0 gives increasing verbosity.
1246 /// The last argument 'script' allows to specify an alternative script to
1247 /// be executed remotely to startup the session.
1248 
1249 Long_t TApplication::ProcessRemote(const char *line, Int_t *)
1250 {
1251  if (!line) return 0;
1252 
1253  if (!strncmp(line, "-?", 2) || !strncmp(line, "-h", 2) ||
1254  !strncmp(line, "--help", 6)) {
1255  Info("ProcessRemote", "remote session help:");
1256  Printf(".R [user@]host[:dir] [-l user] [-d dbg] [[<]script] | [host] -close");
1257  Printf("Create a ROOT session on the specified remote host.");
1258  Printf("The variable \"dir\" is the remote directory to be used as working dir.");
1259  Printf("The username can be specified in two ways, \"-l\" having the priority");
1260  Printf("(as in ssh). A \"dbg\" value > 0 gives increasing verbosity.");
1261  Printf("The last argument \"script\" allows to specify an alternative script to");
1262  Printf("be executed remotely to startup the session, \"roots\" being");
1263  Printf("the default. If the script is preceded by a \"<\" the script will be");
1264  Printf("sourced, after which \"roots\" is executed. The sourced script can be ");
1265  Printf("used to change the PATH and other variables, allowing an alternative");
1266  Printf("\"roots\" script to be found.");
1267  Printf("To close down a session do \".R host -close\".");
1268  Printf("To switch between sessions do \".R host\", to switch to the local");
1269  Printf("session do \".R\".");
1270  Printf("To list all open sessions do \"gApplication->GetApplications()->Print()\".");
1271  return 0;
1272  }
1273 
1274  TString hostdir, user, script;
1275  Int_t dbg = 0;
1276  Int_t rc = ParseRemoteLine(line, hostdir, user, dbg, script);
1277  if (hostdir.Length() <= 0) {
1278  // Close the remote application if required
1279  if (rc == 1) {
1280  TApplication::Close(fAppRemote);
1281  delete fAppRemote;
1282  }
1283  // Return to local run
1284  fAppRemote = 0;
1285  // Done
1286  return 1;
1287  } else if (rc == 1) {
1288  // close an existing remote application
1289  TApplication *ap = TApplication::Open(hostdir, 0, 0);
1290  if (ap) {
1291  TApplication::Close(ap);
1292  delete ap;
1293  }
1294  }
1295  // Attach or start a remote application
1296  if (user.Length() > 0)
1297  hostdir.Insert(0,Form("%s@", user.Data()));
1298  const char *sc = (script.Length() > 0) ? script.Data() : 0;
1299  TApplication *ap = TApplication::Open(hostdir, dbg, sc);
1300  if (ap) {
1301  fAppRemote = ap;
1302  }
1303 
1304  // Done
1305  return 1;
1306 }
1307 
1308 namespace {
1309  static int PrintFile(const char* filename) {
1310  TString sFileName(filename);
1311  gSystem->ExpandPathName(sFileName);
1312  if (gSystem->AccessPathName(sFileName)) {
1313  Error("ProcessLine()", "Cannot find file %s", filename);
1314  return 1;
1315  }
1316  std::ifstream instr(sFileName);
1317  TString content;
1318  content.ReadFile(instr);
1319  Printf("%s", content.Data());
1320  return 0;
1321  }
1322  } // namespace
1323 
1324 ////////////////////////////////////////////////////////////////////////////////
1325 /// Process a single command line, either a C++ statement or an interpreter
1326 /// command starting with a ".".
1327 /// Return the return value of the command cast to a long.
1328 
1329 Long_t TApplication::ProcessLine(const char *line, Bool_t sync, Int_t *err)
1330 {
1331  if (!line || !*line) return 0;
1332 
1333  // If we are asked to go remote do it
1334  if (!strncmp(line, ".R", 2)) {
1335  Int_t n = 2;
1336  while (*(line+n) == ' ')
1337  n++;
1338  return ProcessRemote(line+n, err);
1339  }
1340 
1341  // Redirect, if requested
1342  if (fAppRemote && TestBit(kProcessRemotely)) {
1343  ResetBit(kProcessRemotely);
1344  return fAppRemote->ProcessLine(line, err);
1345  }
1346 
1347  if (!strncasecmp(line, ".qqqqqqq", 7)) {
1348  gSystem->Abort();
1349  } else if (!strncasecmp(line, ".qqqqq", 5)) {
1350  Info("ProcessLine", "Bye... (try '.qqqqqqq' if still running)");
1351  gSystem->Exit(1);
1352  } else if (!strncasecmp(line, ".exit", 4) || !strncasecmp(line, ".quit", 2)) {
1353  Terminate(0);
1354  return 0;
1355  }
1356 
1357  if (!strncmp(line, ".?", 2) || !strncmp(line, ".help", 5)) {
1358  Help(line);
1359  return 1;
1360  }
1361 
1362  if (!strncmp(line, ".demo", 5)) {
1363  if (gROOT->IsBatch()) {
1364  Error("ProcessLine", "Cannot show demos in batch mode!");
1365  return 1;
1366  }
1367  ProcessLine(".x " + TROOT::GetTutorialDir() + "/demos.C");
1368  return 0;
1369  }
1370 
1371  if (!strncmp(line, ".license", 8)) {
1372  return PrintFile(TROOT::GetDocDir() + "/LICENSE");
1373  }
1374 
1375  if (!strncmp(line, ".credits", 8)) {
1376  TString credits = TROOT::GetDocDir() + "/CREDITS";
1377  if (gSystem->AccessPathName(credits, kReadPermission))
1378  credits = TROOT::GetDocDir() + "/README/CREDITS";
1379  return PrintFile(credits);
1380  }
1381 
1382  if (!strncmp(line, ".pwd", 4)) {
1383  if (gDirectory)
1384  Printf("Current directory: %s", gDirectory->GetPath());
1385  if (gPad)
1386  Printf("Current pad: %s", gPad->GetName());
1387  if (gStyle)
1388  Printf("Current style: %s", gStyle->GetName());
1389  return 1;
1390  }
1391 
1392  if (!strncmp(line, ".ls", 3)) {
1393  const char *opt = 0;
1394  if (line[3]) opt = &line[3];
1395  if (gDirectory) gDirectory->ls(opt);
1396  return 1;
1397  }
1398 
1399  if (!strncmp(line, ".which", 6)) {
1400  char *fn = Strip(line+7);
1401  char *s = strtok(fn, "+("); // this method does not need to be reentrant
1402  char *mac = gSystem->Which(TROOT::GetMacroPath(), s, kReadPermission);
1403  if (!mac)
1404  Printf("No macro %s in path %s", s, TROOT::GetMacroPath());
1405  else
1406  Printf("%s", mac);
1407  delete [] fn;
1408  delete [] mac;
1409  return mac ? 1 : 0;
1410  }
1411 
1412  if (!strncmp(line, ".L", 2) || !strncmp(line, ".U", 2)) {
1413  TString aclicMode;
1414  TString arguments;
1415  TString io;
1416  TString fname = gSystem->SplitAclicMode(line+3, aclicMode, arguments, io);
1417 
1418  char *mac = gSystem->Which(TROOT::GetMacroPath(), fname, kReadPermission);
1419  if (arguments.Length()) {
1420  Warning("ProcessLine", "argument(s) \"%s\" ignored with .%c", arguments.Data(),
1421  line[1]);
1422  }
1423  Long_t retval = 0;
1424  if (!mac)
1425  Error("ProcessLine", "macro %s not found in path %s", fname.Data(),
1426  TROOT::GetMacroPath());
1427  else {
1428  TString cmd(line+1);
1429  Ssiz_t posSpace = cmd.Index(' ');
1430  if (posSpace == -1) cmd.Remove(1);
1431  else cmd.Remove(posSpace);
1432  TString tempbuf;
1433  if (sync) {
1434  tempbuf.Form(".%s %s%s%s", cmd.Data(), mac, aclicMode.Data(),io.Data());
1435  retval = gInterpreter->ProcessLineSynch(tempbuf,
1436  (TInterpreter::EErrorCode*)err);
1437  } else {
1438  tempbuf.Form(".%s %s%s%s", cmd.Data(), mac, aclicMode.Data(),io.Data());
1439  retval = gInterpreter->ProcessLine(tempbuf,
1440  (TInterpreter::EErrorCode*)err);
1441  }
1442  }
1443 
1444  delete [] mac;
1445 
1446  InitializeGraphics();
1447 
1448  return retval;
1449  }
1450 
1451  if (!strncmp(line, ".X", 2) || !strncmp(line, ".x", 2)) {
1452  return ProcessFile(line+3, err, line[2] == 'k');
1453  }
1454 
1455  if (!strcmp(line, ".reset")) {
1456  // Do nothing, .reset disabled in CINT because too many side effects
1457  Printf("*** .reset not allowed, please use gROOT->Reset() ***");
1458  return 0;
1459 
1460 #if 0
1461  // delete the ROOT dictionary since CINT will destroy all objects
1462  // referenced by the dictionary classes (TClass et. al.)
1463  gROOT->GetListOfClasses()->Delete();
1464  // fall through
1465 #endif
1466  }
1467 
1468  if (sync)
1469  return gInterpreter->ProcessLineSynch(line, (TInterpreter::EErrorCode*)err);
1470  else
1471  return gInterpreter->ProcessLine(line, (TInterpreter::EErrorCode*)err);
1472 }
1473 
1474 ////////////////////////////////////////////////////////////////////////////////
1475 /// Process a file containing a C++ macro.
1476 
1477 Long_t TApplication::ProcessFile(const char *file, Int_t *error, Bool_t keep)
1478 {
1479  return ExecuteFile(file, error, keep);
1480 }
1481 
1482 ////////////////////////////////////////////////////////////////////////////////
1483 /// Execute a file containing a C++ macro (static method). Can be used
1484 /// while TApplication is not yet created.
1485 
1486 Long_t TApplication::ExecuteFile(const char *file, Int_t *error, Bool_t keep)
1487 {
1488  static const Int_t kBufSize = 1024;
1489 
1490  if (!file || !*file) return 0;
1491 
1492  TString aclicMode;
1493  TString arguments;
1494  TString io;
1495  TString fname = gSystem->SplitAclicMode(file, aclicMode, arguments, io);
1496 
1497  char *exnam = gSystem->Which(TROOT::GetMacroPath(), fname, kReadPermission);
1498  if (!exnam) {
1499  ::Error("TApplication::ExecuteFile", "macro %s not found in path %s", fname.Data(),
1500  TROOT::GetMacroPath());
1501  delete [] exnam;
1502  if (error)
1503  *error = (Int_t)TInterpreter::kRecoverable;
1504  return 0;
1505  }
1506 
1507  ::std::ifstream macro(exnam, std::ios::in);
1508  if (!macro.good()) {
1509  ::Error("TApplication::ExecuteFile", "%s no such file", exnam);
1510  if (error)
1511  *error = (Int_t)TInterpreter::kRecoverable;
1512  delete [] exnam;
1513  return 0;
1514  }
1515 
1516  char currentline[kBufSize];
1517  char dummyline[kBufSize];
1518  int tempfile = 0;
1519  int comment = 0;
1520  int ifndefc = 0;
1521  int ifdef = 0;
1522  char *s = 0;
1523  Bool_t execute = kFALSE;
1524  Long_t retval = 0;
1525 
1526  while (1) {
1527  bool res = (bool)macro.getline(currentline, kBufSize);
1528  if (macro.eof()) break;
1529  if (!res) {
1530  // Probably only read kBufSize, let's ignore the remainder of
1531  // the line.
1532  macro.clear();
1533  while (!macro.getline(dummyline, kBufSize) && !macro.eof()) {
1534  macro.clear();
1535  }
1536  }
1537  s = currentline;
1538  while (s && (*s == ' ' || *s == '\t')) s++; // strip-off leading blanks
1539 
1540  // very simple minded pre-processor parsing, only works in case macro file
1541  // starts with "#ifndef __CINT__". In that case everything till next
1542  // "#else" or "#endif" will be skipped.
1543  if (*s == '#') {
1544  char *cs = Compress(currentline);
1545  if (strstr(cs, "#ifndef__CINT__") ||
1546  strstr(cs, "#if!defined(__CINT__)"))
1547  ifndefc = 1;
1548  else if (ifndefc && (strstr(cs, "#ifdef") || strstr(cs, "#ifndef") ||
1549  strstr(cs, "#ifdefined") || strstr(cs, "#if!defined")))
1550  ifdef++;
1551  else if (ifndefc && strstr(cs, "#endif")) {
1552  if (ifdef)
1553  ifdef--;
1554  else
1555  ifndefc = 0;
1556  } else if (ifndefc && !ifdef && strstr(cs, "#else"))
1557  ifndefc = 0;
1558  delete [] cs;
1559  }
1560  if (!*s || *s == '#' || ifndefc || !strncmp(s, "//", 2)) continue;
1561 
1562  if (!comment && (!strncmp(s, ".X", 2) || !strncmp(s, ".x", 2))) {
1563  retval = ExecuteFile(s+3);
1564  execute = kTRUE;
1565  continue;
1566  }
1567 
1568  if (!strncmp(s, "/*", 2)) comment = 1;
1569  if (comment) {
1570  // handle slightly more complex cases like: /* */ /*
1571 again:
1572  s = strstr(s, "*/");
1573  if (s) {
1574  comment = 0;
1575  s += 2;
1576 
1577  while (s && (*s == ' ' || *s == '\t')) s++; // strip-off leading blanks
1578  if (!*s) continue;
1579  if (!strncmp(s, "//", 2)) continue;
1580  if (!strncmp(s, "/*", 2)) {
1581  comment = 1;
1582  goto again;
1583  }
1584  }
1585  }
1586  if (!comment && *s == '{') tempfile = 1;
1587  if (!comment) break;
1588  }
1589  macro.close();
1590 
1591  if (!execute) {
1592  TString exname = exnam;
1593  if (!tempfile) {
1594  // We have a script that does NOT contain an unnamed macro,
1595  // so we can call the script compiler on it.
1596  exname += aclicMode;
1597  }
1598  exname += arguments;
1599  exname += io;
1600 
1601  TString tempbuf;
1602  if (tempfile) {
1603  tempbuf.Form(".x %s", exname.Data());
1604  } else {
1605  tempbuf.Form(".X%s %s", keep ? "k" : " ", exname.Data());
1606  }
1607  retval = gInterpreter->ProcessLineSynch(tempbuf,(TInterpreter::EErrorCode*)error);
1608  }
1609 
1610  delete [] exnam;
1611  return retval;
1612 }
1613 
1614 ////////////////////////////////////////////////////////////////////////////////
1615 /// Main application eventloop. Calls system dependent eventloop via gSystem.
1616 
1617 void TApplication::Run(Bool_t retrn)
1618 {
1619  SetReturnFromRun(retrn);
1620 
1621  fIsRunning = kTRUE;
1622 
1623  gSystem->Run();
1624  fIsRunning = kFALSE;
1625 }
1626 
1627 ////////////////////////////////////////////////////////////////////////////////
1628 /// Set the command to be executed after the system has been idle for
1629 /// idleTimeInSec seconds. Normally called via TROOT::Idle(...).
1630 
1631 void TApplication::SetIdleTimer(UInt_t idleTimeInSec, const char *command)
1632 {
1633  if (fIdleTimer) RemoveIdleTimer();
1634  fIdleCommand = command;
1635  fIdleTimer = new TIdleTimer(idleTimeInSec*1000);
1636  gSystem->AddTimer(fIdleTimer);
1637 }
1638 
1639 ////////////////////////////////////////////////////////////////////////////////
1640 /// Remove idle timer. Normally called via TROOT::Idle(0).
1641 
1642 void TApplication::RemoveIdleTimer()
1643 {
1644  if (fIdleTimer) {
1645  // timers are removed from the gSystem timer list by their dtor
1646  SafeDelete(fIdleTimer);
1647  }
1648 }
1649 
1650 ////////////////////////////////////////////////////////////////////////////////
1651 /// Called when system starts idleing.
1652 
1653 void TApplication::StartIdleing()
1654 {
1655  if (fIdleTimer) {
1656  fIdleTimer->Reset();
1657  gSystem->AddTimer(fIdleTimer);
1658  }
1659 }
1660 
1661 ////////////////////////////////////////////////////////////////////////////////
1662 /// Called when system stops idleing.
1663 
1664 void TApplication::StopIdleing()
1665 {
1666  if (fIdleTimer)
1667  gSystem->RemoveTimer(fIdleTimer);
1668 }
1669 
1670 ////////////////////////////////////////////////////////////////////////////////
1671 /// What to do when tab is pressed. Re-implemented by TRint.
1672 /// See TTabCom::Hook() for meaning of return values.
1673 
1674 Int_t TApplication::TabCompletionHook(char* /*buf*/, int* /*pLoc*/, std::ostream& /*out*/)
1675 {
1676  return -1;
1677 }
1678 
1679 
1680 ////////////////////////////////////////////////////////////////////////////////
1681 /// Terminate the application by call TSystem::Exit() unless application has
1682 /// been told to return from Run(), by a call to SetReturnFromRun().
1683 
1684 void TApplication::Terminate(Int_t status)
1685 {
1686  Emit("Terminate(Int_t)", status);
1687 
1688  if (fReturnFromRun)
1689  gSystem->ExitLoop();
1690  else {
1691  //close TMemStat
1692  if (fUseMemstat) {
1693  ProcessLine("TMemStat::Close()");
1694  fUseMemstat = kFALSE;
1695  }
1696 
1697  gSystem->Exit(status);
1698  }
1699 }
1700 
1701 ////////////////////////////////////////////////////////////////////////////////
1702 /// Emit signal when a line has been processed.
1703 
1704 void TApplication::LineProcessed(const char *line)
1705 {
1706  Emit("LineProcessed(const char*)", line);
1707 }
1708 
1709 ////////////////////////////////////////////////////////////////////////////////
1710 /// Emit signal when console keyboard key was pressed.
1711 
1712 void TApplication::KeyPressed(Int_t key)
1713 {
1714  Emit("KeyPressed(Int_t)", key);
1715 }
1716 
1717 ////////////////////////////////////////////////////////////////////////////////
1718 /// Emit signal when return key was pressed.
1719 
1720 void TApplication::ReturnPressed(char *text )
1721 {
1722  Emit("ReturnPressed(char*)", text);
1723 }
1724 
1725 ////////////////////////////////////////////////////////////////////////////////
1726 /// Set console echo mode:
1727 ///
1728 /// - mode = kTRUE - echo input symbols
1729 /// - mode = kFALSE - noecho input symbols
1730 
1731 void TApplication::SetEchoMode(Bool_t)
1732 {
1733 }
1734 
1735 ////////////////////////////////////////////////////////////////////////////////
1736 /// Static function used to create a default application environment.
1737 
1738 void TApplication::CreateApplication()
1739 {
1740  R__LOCKGUARD(gROOTMutex);
1741  // gApplication is set at the end of 'new TApplication.
1742  if (!gApplication) {
1743  char *a = StrDup("RootApp");
1744  char *b = StrDup("-b");
1745  char *argv[2];
1746  Int_t argc = 2;
1747  argv[0] = a;
1748  argv[1] = b;
1749  new TApplication("RootApp", &argc, argv, 0, 0);
1750  if (gDebug > 0)
1751  Printf("<TApplication::CreateApplication>: "
1752  "created default TApplication");
1753  delete [] a; delete [] b;
1754  gApplication->SetBit(kDefaultApplication);
1755  }
1756 }
1757 
1758 ////////////////////////////////////////////////////////////////////////////////
1759 /// Static function used to attach to an existing remote application
1760 /// or to start one.
1761 
1762 TApplication *TApplication::Open(const char *url,
1763  Int_t debug, const char *script)
1764 {
1765  TApplication *ap = 0;
1766  TUrl nu(url);
1767  Int_t nnew = 0;
1768 
1769  // Look among the existing ones
1770  if (fgApplications) {
1771  TIter nxa(fgApplications);
1772  while ((ap = (TApplication *) nxa())) {
1773  TString apn(ap->ApplicationName());
1774  if (apn == url) {
1775  // Found matching application
1776  return ap;
1777  } else {
1778  // Check if same machine and user
1779  TUrl au(apn);
1780  if (strlen(au.GetUser()) > 0 && strlen(nu.GetUser()) > 0 &&
1781  !strcmp(au.GetUser(), nu.GetUser())) {
1782  if (!strncmp(au.GetHost(), nu.GetHost(), strlen(nu.GetHost())))
1783  // New session on a known machine
1784  nnew++;
1785  }
1786  }
1787  }
1788  } else {
1789  ::Error("TApplication::Open", "list of applications undefined - protocol error");
1790  return ap;
1791  }
1792 
1793  // If new session on a known machine pass the number as option
1794  if (nnew > 0) {
1795  nnew++;
1796  nu.SetOptions(Form("%d", nnew));
1797  }
1798 
1799  // Instantiate the TApplication object to be run
1800  TPluginHandler *h = 0;
1801  if ((h = gROOT->GetPluginManager()->FindHandler("TApplication","remote"))) {
1802  if (h->LoadPlugin() == 0) {
1803  ap = (TApplication *) h->ExecPlugin(3, nu.GetUrl(), debug, script);
1804  } else {
1805  ::Error("TApplication::Open", "failed to load plugin for TApplicationRemote");
1806  }
1807  } else {
1808  ::Error("TApplication::Open", "failed to find plugin for TApplicationRemote");
1809  }
1810 
1811  // Add to the list
1812  if (ap && !(ap->TestBit(kInvalidObject))) {
1813  fgApplications->Add(ap);
1814  gROOT->GetListOfBrowsables()->Add(ap, ap->ApplicationName());
1815  TIter next(gROOT->GetListOfBrowsers());
1816  TBrowser *b;
1817  while ((b = (TBrowser*) next()))
1818  b->Add(ap, ap->ApplicationName());
1819  gROOT->RefreshBrowsers();
1820  } else {
1821  SafeDelete(ap);
1822  ::Error("TApplication::Open",
1823  "TApplicationRemote for %s could not be instantiated", url);
1824  }
1825 
1826  // Done
1827  return ap;
1828 }
1829 
1830 ////////////////////////////////////////////////////////////////////////////////
1831 /// Static function used to close a remote application
1832 
1833 void TApplication::Close(TApplication *app)
1834 {
1835  if (app) {
1836  app->Terminate(0);
1837  fgApplications->Remove(app);
1838  gROOT->GetListOfBrowsables()->RecursiveRemove(app);
1839  TIter next(gROOT->GetListOfBrowsers());
1840  TBrowser *b;
1841  while ((b = (TBrowser*) next()))
1842  b->RecursiveRemove(app);
1843  gROOT->RefreshBrowsers();
1844  }
1845 }
1846 
1847 ////////////////////////////////////////////////////////////////////////////////
1848 /// Show available sessions
1849 
1850 void TApplication::ls(Option_t *opt) const
1851 {
1852  if (fgApplications) {
1853  TIter nxa(fgApplications);
1854  TApplication *a = 0;
1855  while ((a = (TApplication *) nxa())) {
1856  a->Print(opt);
1857  }
1858  } else {
1859  Print(opt);
1860  }
1861 }
1862 
1863 ////////////////////////////////////////////////////////////////////////////////
1864 /// Static method returning the list of available applications
1865 
1866 TList *TApplication::GetApplications()
1867 {
1868  return fgApplications;
1869 }