Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
TPluginManager.cxx
Go to the documentation of this file.
1 // @(#)root/base:$Id$
2 // Author: Fons Rademakers 26/1/2002
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2002, 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 TPluginManager
13 \ingroup Base
14 
15 This class implements a plugin library manager.
16 
17 It keeps track of a list of plugin handlers. A plugin handler knows which plugin
18 library to load to get a specific class that is used to extend the
19 functionality of a specific base class and how to create an object
20 of this class. For example, to extend the base class TFile to be
21 able to read SQLite files one needs to load the plugin library
22 libRSQLite.so which defines the TRSQLiteServer class. This loading
23 should be triggered when a given URI contains a regular expression
24 defined by the handler.
25 
26 Plugin handlers can be defined via macros in a list of plugin
27 directories. With $ROOTSYS/etc/plugins the default top plugin
28 directory specified in $ROOTSYS/etc/system.rootrc. Additional
29 directories can be specified by adding them to the end of the list.
30 Macros for identical plugin handlers in later directories will
31 override previous ones (the inverse of normal search path behavior).
32 The macros must have names like `<BaseClass>/PX0_<PluginClass>.C`,
33 e.g. TSQLServer/P20_TMySQLServer.C, to allow easy sorting and grouping.
34 If the BaseClass is in a namespace the directory must have the name
35 NameSpace@@BaseClass as `:` is a reserved pathname character on some
36 operating systems. Macros not beginning with 'P' and ending with ".C"
37 are ignored. These macros typically look like:
38 ~~~ {.cpp}
39  void P10_TDCacheFile()
40  {
41  gPluginMgr->AddHandler("TFile", "^dcache", "TDCacheFile",
42  "DCache", "TDCacheFile(const char*,Option_t*)");
43  }
44 ~~~
45 Plugin handlers can also be defined via resources in the .rootrc
46 file. Although now deprecated this method still works for backward
47 compatibility, e.g.:
48 ~~~ {.cpp}
49  Plugin.TSQLServer: ^mysql: TMySQLServer MySQL "<constructor>"
50  +Plugin.TSQLServer: ^pgsql: TPgSQLServer PgSQL "<constructor>"
51  Plugin.TVirtualFitter: * TFitter Minuit "TFitter(Int_t)"
52 ~~~
53 Where the + in front of Plugin.TSQLServer says that it extends the
54 existing definition of TSQLServer, useful when there is more than
55 one plugin that can extend the same base class. The "<constructor>"
56 should be the constructor or a static method that generates an
57 instance of the specified class. Global methods should start with
58 "::" in their name, like "::CreateFitter()".
59 Instead of being a shared library a plugin can also be a CINT
60 script, so instead of libDialog.so one can have Dialog.C.
61 The * is a placeholder in case there is no need for a URI to
62 differentiate between different plugins for the same base class.
63 For the default plugins see $ROOTSYS/etc/system.rootrc.
64 
65 Plugin handlers can also be registered at run time, e.g.:
66 ~~~ {.cpp}
67  gPluginMgr->AddHandler("TSQLServer", "^sqlite:",
68  "TSQLiteServer", "RSQLite",
69  "TSQLiteServer(const char*,const char*,const char*)");
70 ~~~
71 A list of currently defined handlers can be printed using:
72 ~~~ {.cpp}
73  gPluginMgr->Print(); // use option="a" to see ctors
74 ~~~
75 The use of the plugin library manager removes all textual references
76 to hard-coded class and library names and the resulting dependencies
77 in the base classes. The plugin manager is used to extend a.o.
78 TFile, TSQLServer, TGrid, etc. functionality.
79 */
80 
81 #include "TPluginManager.h"
82 #include "Varargs.h"
83 #include "TEnv.h"
84 #include "TRegexp.h"
85 #include "TROOT.h"
86 #include "TSortedList.h"
87 #include "THashList.h"
88 #include "THashTable.h"
89 #include "Varargs.h"
90 #include "TClass.h"
91 #include "TInterpreter.h"
92 #include "TMethod.h"
93 #include "TMethodArg.h"
94 #include "TDataType.h"
95 #include "TMethodCall.h"
96 #include "TVirtualMutex.h"
97 #include "TSystem.h"
98 #include "TObjString.h"
99 #include "ThreadLocalStorage.h"
100 
101 #include <memory>
102 
103 TPluginManager *gPluginMgr; // main plugin manager created in TROOT
104 
105 static TVirtualMutex *gPluginManagerMutex;
106 
107 static bool &TPH__IsReadingDirs() {
108  TTHREAD_TLS(bool) readingDirs (false);
109  return readingDirs;
110 }
111 
112 ClassImp(TPluginHandler);
113 
114 ////////////////////////////////////////////////////////////////////////////////
115 /// Create a plugin handler. Called by TPluginManager.
116 
117 TPluginHandler::TPluginHandler(const char *base, const char *regexp,
118  const char *className, const char *pluginName,
119  const char *ctor, const char *origin):
120  fBase(base),
121  fRegexp(regexp),
122  fClass(className),
123  fPlugin(pluginName),
124  fCtor(ctor),
125  fOrigin(origin),
126  fCallEnv(0),
127  fMethod(0),
128  fCanCall(0),
129  fIsMacro(kFALSE),
130  fIsGlobal(kFALSE)
131 {
132  TString aclicMode, arguments, io;
133  TString fname = gSystem->SplitAclicMode(fPlugin, aclicMode, arguments, io);
134  Bool_t validMacro = kFALSE;
135  if (fname.EndsWith(".C") || fname.EndsWith(".cxx") || fname.EndsWith(".cpp") ||
136  fname.EndsWith(".cc"))
137  validMacro = kTRUE;
138 
139  if (validMacro && gROOT->LoadMacro(fPlugin, 0, kTRUE) == 0)
140  fIsMacro = kTRUE;
141 
142  if (fCtor.BeginsWith("::")) {
143  fIsGlobal = kTRUE;
144  fCtor = fCtor.Strip(TString::kLeading, ':');
145  }
146 }
147 
148 ////////////////////////////////////////////////////////////////////////////////
149 /// Cleanup plugin handler object.
150 
151 TPluginHandler::~TPluginHandler()
152 {
153  delete fCallEnv;
154 }
155 
156 ////////////////////////////////////////////////////////////////////////////////
157 /// Check if regular expression appears in the URI, if so return kTRUE.
158 /// If URI = 0 always return kTRUE.
159 
160 Bool_t TPluginHandler::CanHandle(const char *base, const char *uri)
161 {
162  if (fBase != base)
163  return kFALSE;
164 
165  if (!uri || fRegexp == "*")
166  return kTRUE;
167 
168  Bool_t wildcard = kFALSE;
169  if (!fRegexp.MaybeRegexp())
170  wildcard = kTRUE;
171 
172  TRegexp re(fRegexp, wildcard);
173  TString ruri = uri;
174 
175  if (ruri.Index(re) != kNPOS)
176  return kTRUE;
177  return kFALSE;
178 }
179 
180 ////////////////////////////////////////////////////////////////////////////////
181 /// Setup ctor or static method call environment.
182 
183 void TPluginHandler::SetupCallEnv()
184 {
185  int setCanCall = -1;
186 
187  // Use a exit_scope guard, to insure that fCanCall is set (to the value of
188  // result) as the last action of this function before returning.
189 
190  // When the standard supports it, we should use std::exit_code
191  // See N4189 for example.
192  // auto guard = make_exit_scope( [...]() { ... } );
193  using exit_scope = std::shared_ptr<void*>;
194  exit_scope guard(nullptr,
195  [this,&setCanCall](void *) { this->fCanCall = setCanCall; } );
196 
197  // check if class exists
198  TClass *cl = TClass::GetClass(fClass);
199  if (!cl && !fIsGlobal) {
200  Error("SetupCallEnv", "class %s not found in plugin %s", fClass.Data(),
201  fPlugin.Data());
202  return;
203  }
204 
205  // split method and prototype strings
206  TString method = fCtor(0, fCtor.Index("("));
207  TString proto = fCtor(fCtor.Index("(")+1, fCtor.Index(")")-fCtor.Index("(")-1);
208 
209  if (fIsGlobal) {
210  cl = 0;
211  fMethod = gROOT->GetGlobalFunctionWithPrototype(method, proto, kFALSE);
212  } else {
213  fMethod = cl->GetMethodWithPrototype(method, proto);
214  }
215 
216  if (!fMethod) {
217  if (fIsGlobal)
218  Error("SetupCallEnv", "global function %s not found", method.Data());
219  else
220  Error("SetupCallEnv", "method %s not found in class %s", method.Data(),
221  fClass.Data());
222  return;
223  }
224 
225  if (!fIsGlobal && !(fMethod->Property() & kIsPublic)) {
226  Error("SetupCallEnv", "method %s is not public", method.Data());
227  return;
228  }
229 
230  fCallEnv = new TMethodCall;
231  fCallEnv->Init(fMethod);
232 
233  setCanCall = 1;
234 
235  return;
236 }
237 
238 ////////////////////////////////////////////////////////////////////////////////
239 /// Check if the plugin library for this handler exits. Returns 0
240 /// when it exists and -1 in case the plugin does not exist.
241 
242 Int_t TPluginHandler::CheckPlugin() const
243 {
244  if (fIsMacro) {
245  if (TClass::GetClass(fClass)) return 0;
246  return gROOT->LoadMacro(fPlugin, 0, kTRUE);
247  } else
248  return gROOT->LoadClass(fClass, fPlugin, kTRUE);
249 }
250 
251 ////////////////////////////////////////////////////////////////////////////////
252 /// Load the plugin library for this handler. Returns 0 on successful loading
253 /// and -1 in case the library does not exist or in case of error.
254 
255 Int_t TPluginHandler::LoadPlugin()
256 {
257  if (fIsMacro) {
258  if (TClass::GetClass(fClass)) return 0;
259  return gROOT->LoadMacro(fPlugin);
260  } else {
261  // first call also loads dependent libraries declared via the rootmap file
262  if (TClass::LoadClass(fClass, /* silent = */ kFALSE)) return 0;
263  return gROOT->LoadClass(fClass, fPlugin);
264  }
265 }
266 
267 ////////////////////////////////////////////////////////////////////////////////
268 /// Check that we can properly run ExecPlugin.
269 
270 Bool_t TPluginHandler::CheckForExecPlugin(Int_t nargs)
271 {
272  if (fCtor.IsNull()) {
273  Error("ExecPlugin", "no ctor specified for this handler %s", fClass.Data());
274  return kFALSE;
275  }
276 
277  if (fCanCall == 0) {
278  // Not initialized yet.
279  // SetupCallEnv is likely to require/take the interpreter lock.
280  // Grab it now to avoid dead-lock. In particular TPluginHandler::ExecPluginImpl
281  // takes the gInterpreterMutex and *then* call (indirectly) code that
282  // take the gPluginManagerMutex.
283  R__LOCKGUARD(gInterpreterMutex);
284  R__LOCKGUARD2(gPluginManagerMutex);
285 
286  // Now check if another thread did not already do the work.
287  if (fCanCall == 0)
288  SetupCallEnv();
289  }
290 
291  if (fCanCall == -1)
292  return kFALSE;
293 
294  if (nargs < fMethod->GetNargs() - fMethod->GetNargsOpt() ||
295  nargs > fMethod->GetNargs()) {
296  Error("ExecPlugin", "nargs (%d) not consistent with expected number of arguments ([%d-%d])",
297  nargs, fMethod->GetNargs() - fMethod->GetNargsOpt(),
298  fMethod->GetNargs());
299  return kFALSE;
300  }
301 
302  return kTRUE;
303 }
304 
305 ////////////////////////////////////////////////////////////////////////////////
306 /// Print info about the plugin handler. If option is "a" print
307 /// also the ctor's that will be used.
308 
309 void TPluginHandler::Print(Option_t *opt) const
310 {
311  const char *exist = "";
312  if (CheckPlugin() == -1)
313  exist = " [*]";
314 
315  Printf("%-20s %-13s %-18s %s%s", fBase.Data(), fRegexp.Data(),
316  fClass.Data(), fPlugin.Data(), exist);
317  if (strchr(opt, 'a')) {
318  if (!exist[0]) {
319  TString lib = fPlugin;
320  if (!lib.BeginsWith("lib"))
321  lib = "lib" + lib;
322  char *path = gSystem->DynamicPathName(lib, kTRUE);
323  if (path) Printf(" [Lib: %s]", path);
324  delete [] path;
325  }
326  Printf(" [Ctor: %s]", fCtor.Data());
327  Printf(" [origin: %s]", fOrigin.Data());
328  }
329 }
330 
331 
332 ClassImp(TPluginManager);
333 
334 ////////////////////////////////////////////////////////////////////////////////
335 /// Clean up the plugin manager.
336 
337 TPluginManager::~TPluginManager()
338 {
339  delete fHandlers;
340  delete fBasesLoaded;
341 }
342 
343 ////////////////////////////////////////////////////////////////////////////////
344 /// Load plugin handlers specified in config file, like:
345 /// ~~~ {.cpp}
346 /// Plugin.TSQLServer: ^mysql: TMySQLServer MySQL "TMySQLServer(...)"
347 /// +Plugin.TSQLServer: ^pgsql: TPgSQLServer PgSQL "TPgSQLServer(...)"
348 /// ~~~
349 /// The + allows the extension of an already defined resource (see TEnv).
350 
351 void TPluginManager::LoadHandlersFromEnv(TEnv *env)
352 {
353  if (!env) return;
354 
355  TIter next(env->GetTable());
356  TEnvRec *er;
357 
358  while ((er = (TEnvRec*) next())) {
359  const char *s;
360  if ((s = strstr(er->GetName(), "Plugin."))) {
361  // use s, i.e. skip possible OS and application prefix to Plugin.
362  // so that GetValue() takes properly care of returning the value
363  // for the specified OS and/or application
364  const char *val = env->GetValue(s, (const char*)0);
365  if (val) {
366  Int_t cnt = 0;
367  char *v = StrDup(val);
368  s += 7;
369  while (1) {
370  TString regexp = strtok(!cnt ? v : 0, "; "); // this method does not need to be reentrant
371  if (regexp.IsNull()) break;
372  TString clss = strtok(0, "; ");
373  if (clss.IsNull()) break;
374  TString plugin = strtok(0, "; ");
375  if (plugin.IsNull()) break;
376  TString ctor = strtok(0, ";\"");
377  if (!ctor.Contains("("))
378  ctor = strtok(0, ";\"");
379  AddHandler(s, regexp, clss, plugin, ctor, "TEnv");
380  cnt++;
381  }
382  delete [] v;
383  }
384  }
385  }
386 }
387 
388 ////////////////////////////////////////////////////////////////////////////////
389 /// Load all plugin macros from the specified path/base directory.
390 
391 void TPluginManager::LoadHandlerMacros(const char *path)
392 {
393  void *dirp = gSystem->OpenDirectory(path);
394  if (dirp) {
395  if (gDebug > 0)
396  Info("LoadHandlerMacros", "%s", path);
397  TSortedList macros;
398  macros.SetOwner();
399  const char *f1;
400  while ((f1 = gSystem->GetDirEntry(dirp))) {
401  TString f = f1;
402  if (f[0] == 'P' && f.EndsWith(".C")) {
403  const char *p = gSystem->ConcatFileName(path, f);
404  if (!gSystem->AccessPathName(p, kReadPermission)) {
405  macros.Add(new TObjString(p));
406  }
407  delete [] p;
408  }
409  }
410  // load macros in alphabetical order
411  TIter next(&macros);
412  TObjString *s;
413  while ((s = (TObjString*)next())) {
414  if (gDebug > 1)
415  Info("LoadHandlerMacros", " plugin macro: %s", s->String().Data());
416  Long_t res;
417  if ((res = gROOT->Macro(s->String(), 0, kFALSE)) < 0) {
418  Error("LoadHandlerMacros", "pluging macro %s returned %ld",
419  s->String().Data(), res);
420  }
421  }
422  }
423  gSystem->FreeDirectory(dirp);
424 }
425 
426 ////////////////////////////////////////////////////////////////////////////////
427 /// Load plugin handlers specified via macros in a list of plugin
428 /// directories. The `$ROOTSYS/etc/plugins` is the default top plugin directory
429 /// specified in `$ROOTSYS/etc/system.rootrc`. The macros must have names
430 /// like `<BaseClass>/PX0_<PluginClass>.C`, e.g. //`TSQLServer/P20_TMySQLServer.C`,
431 /// to allow easy sorting and grouping. If the BaseClass is in a namespace
432 /// the directory must have the name NameSpace@@BaseClass as : is a reserved
433 /// pathname character on some operating systems. Macros not beginning with
434 /// 'P' and ending with ".C" are ignored. If base is specified only plugin
435 /// macros for that base class are loaded. The macros typically
436 /// should look like:
437 /// ~~~ {.cpp}
438 /// void P10_TDCacheFile()
439 /// {
440 /// gPluginMgr->AddHandler("TFile", "^dcache", "TDCacheFile",
441 /// "DCache", "TDCacheFile(const char*,Option_t*,const char*,Int_t)");
442 /// }
443 /// ~~~
444 /// In general these macros should not cause side effects, by changing global
445 /// ROOT state via, e.g. gSystem calls, etc. However, in specific cases
446 /// this might be useful, e.g. adding a library search path, adding a specific
447 /// dependency, check on some OS or ROOT capability or downloading
448 /// of the plugin.
449 
450 void TPluginManager::LoadHandlersFromPluginDirs(const char *base)
451 {
452  TString sbase = base;
453  if (sbase.Length())
454  sbase.ReplaceAll("::", "@@");
455 
456  R__READ_LOCKGUARD(ROOT::gCoreMutex);
457 
458  if (fBasesLoaded && fBasesLoaded->FindObject(sbase))
459  return;
460 
461  R__WRITE_LOCKGUARD(ROOT::gCoreMutex);
462 
463  // While waiting for the lock, another thread may
464  // have process the requested plugin.
465  if (fBasesLoaded && fBasesLoaded->FindObject(sbase))
466  return;
467 
468  if (!fBasesLoaded) {
469  fBasesLoaded = new THashTable();
470  fBasesLoaded->SetOwner();
471  }
472  fBasesLoaded->Add(new TObjString(sbase));
473 
474  TPH__IsReadingDirs() = kTRUE;
475 
476  TString plugindirs = gEnv->GetValue("Root.PluginPath", (char*)0);
477  if (plugindirs.Length() == 0) {
478  plugindirs = "plugins";
479  gSystem->PrependPathName(TROOT::GetEtcDir(), plugindirs);
480  }
481 #ifdef WIN32
482  TObjArray *dirs = plugindirs.Tokenize(";");
483 #else
484  TObjArray *dirs = plugindirs.Tokenize(":");
485 #endif
486  TString d;
487  for (Int_t i = 0; i < dirs->GetEntriesFast(); i++) {
488  d = ((TObjString*)dirs->At(i))->GetString();
489  // check if directory already scanned
490  Int_t skip = 0;
491  for (Int_t j = 0; j < i; j++) {
492  TString pd = ((TObjString*)dirs->At(j))->GetString();
493  if (pd == d) {
494  skip++;
495  break;
496  }
497  }
498  if (!skip) {
499  if (sbase != "") {
500  const char *p = gSystem->ConcatFileName(d, sbase);
501  LoadHandlerMacros(p);
502  delete [] p;
503  } else {
504  void *dirp = gSystem->OpenDirectory(d);
505  if (dirp) {
506  if (gDebug > 0)
507  Info("LoadHandlersFromPluginDirs", "%s", d.Data());
508  const char *f1;
509  while ((f1 = gSystem->GetDirEntry(dirp))) {
510  TString f = f1;
511  const char *p = gSystem->ConcatFileName(d, f);
512  LoadHandlerMacros(p);
513  fBasesLoaded->Add(new TObjString(f));
514  delete [] p;
515  }
516  }
517  gSystem->FreeDirectory(dirp);
518  }
519  }
520  }
521  TPH__IsReadingDirs() = kFALSE;
522  delete dirs;
523 }
524 
525 ////////////////////////////////////////////////////////////////////////////////
526 /// Add plugin handler to the list of handlers. If there is already a
527 /// handler defined for the same base and regexp it will be replaced.
528 
529 void TPluginManager::AddHandler(const char *base, const char *regexp,
530  const char *className, const char *pluginName,
531  const char *ctor, const char *origin)
532 {
533  {
534  R__LOCKGUARD2(gPluginManagerMutex);
535  if (!fHandlers) {
536  fHandlers = new TList;
537  fHandlers->SetOwner();
538  }
539  }
540  // make sure there is no previous handler for the same case
541  RemoveHandler(base, regexp);
542 
543  if (TPH__IsReadingDirs())
544  origin = gInterpreter->GetCurrentMacroName();
545 
546  TPluginHandler *h = new TPluginHandler(base, regexp, className,
547  pluginName, ctor, origin);
548  {
549  R__LOCKGUARD2(gPluginManagerMutex);
550  fHandlers->Add(h);
551  }
552 }
553 
554 ////////////////////////////////////////////////////////////////////////////////
555 /// Remove handler for the specified base class and the specified
556 /// regexp. If regexp=0 remove all handlers for the specified base.
557 
558 void TPluginManager::RemoveHandler(const char *base, const char *regexp)
559 {
560  R__LOCKGUARD2(gPluginManagerMutex);
561  if (!fHandlers) return;
562 
563  TIter next(fHandlers);
564  TPluginHandler *h;
565 
566  while ((h = (TPluginHandler*) next())) {
567  if (h->fBase == base) {
568  if (!regexp || h->fRegexp == regexp) {
569  fHandlers->Remove(h);
570  delete h;
571  }
572  }
573  }
574 }
575 
576 ////////////////////////////////////////////////////////////////////////////////
577 /// Returns the handler if there exists a handler for the specified URI.
578 /// The uri can be 0 in which case the first matching plugin handler
579 /// will be returned. Returns 0 in case handler is not found.
580 
581 TPluginHandler *TPluginManager::FindHandler(const char *base, const char *uri)
582 {
583  LoadHandlersFromPluginDirs(base);
584 
585  R__LOCKGUARD2(gPluginManagerMutex);
586  TIter next(fHandlers);
587  TPluginHandler *h;
588 
589  while ((h = (TPluginHandler*) next())) {
590  if (h->CanHandle(base, uri)) {
591  if (gDebug > 0)
592  Info("FindHandler", "found plugin for %s", h->GetClass());
593  return h;
594  }
595  }
596 
597  if (gDebug > 2) {
598  if (uri)
599  Info("FindHandler", "did not find plugin for class %s and uri %s", base, uri);
600  else
601  Info("FindHandler", "did not find plugin for class %s", base);
602  }
603 
604  return 0;
605 }
606 
607 ////////////////////////////////////////////////////////////////////////////////
608 /// Print list of registered plugin handlers. If option is "a" print
609 /// also the ctor's that will be used.
610 
611 void TPluginManager::Print(Option_t *opt) const
612 {
613  if (!fHandlers) return;
614 
615  TIter next(fHandlers);
616  TPluginHandler *h;
617  Int_t cnt = 0, cntmiss = 0;
618 
619  Printf("=====================================================================");
620  Printf("Base Regexp Class Plugin");
621  Printf("=====================================================================");
622 
623  while ((h = (TPluginHandler*) next())) {
624  cnt++;
625  h->Print(opt);
626  if (h->CheckPlugin() == -1)
627  cntmiss++;
628  }
629  Printf("=====================================================================");
630  Printf("%d plugin handlers registered", cnt);
631  Printf("[*] %d %s not available", cntmiss, cntmiss==1 ? "plugin" : "plugins");
632  Printf("=====================================================================\n");
633 }
634 
635 ////////////////////////////////////////////////////////////////////////////////
636 /// Write in the specified directory the plugin macros. If plugin is specified
637 /// and if it is a base class all macros for that base will be written. If it
638 /// is a plugin class name, only that one macro will be written. If plugin
639 /// is 0 all macros are written. Returns -1 if dir does not exist, 0 otherwise.
640 
641 Int_t TPluginManager::WritePluginMacros(const char *dir, const char *plugin) const
642 {
643  const_cast<TPluginManager*>(this)->LoadHandlersFromPluginDirs();
644 
645  if (!fHandlers) return 0;
646 
647  TString d;
648  if (!dir || !dir[0])
649  d = ".";
650  else
651  d = dir;
652 
653  if (gSystem->AccessPathName(d, kWritePermission)) {
654  Error("WritePluginMacros", "cannot write in directory %s", d.Data());
655  return -1;
656  }
657 
658  TString base;
659  Int_t idx = 0;
660 
661  TObjLink *lnk = fHandlers->FirstLink();
662  while (lnk) {
663  TPluginHandler *h = (TPluginHandler *) lnk->GetObject();
664  if (plugin && strcmp(plugin, h->fBase) && strcmp(plugin, h->fClass)) {
665  lnk = lnk->Next();
666  continue;
667  }
668  if (base != h->fBase) {
669  idx = 10;
670  base = h->fBase;
671  } else
672  idx += 10;
673  const char *dd = gSystem->ConcatFileName(d, h->fBase);
674  TString sdd = dd;
675  sdd.ReplaceAll("::", "@@");
676  delete [] dd;
677  if (gSystem->AccessPathName(sdd, kWritePermission)) {
678  if (gSystem->MakeDirectory(sdd) < 0) {
679  Error("WritePluginMacros", "cannot create directory %s", sdd.Data());
680  return -1;
681  }
682  }
683  TString fn;
684  fn.Form("P%03d_%s.C", idx, h->fClass.Data());
685  const char *fd = gSystem->ConcatFileName(sdd, fn);
686  FILE *f = fopen(fd, "w");
687  if (f) {
688  fprintf(f, "void P%03d_%s()\n{\n", idx, h->fClass.Data());
689  fprintf(f, " gPluginMgr->AddHandler(\"%s\", \"%s\", \"%s\",\n",
690  h->fBase.Data(), h->fRegexp.Data(), h->fClass.Data());
691  fprintf(f, " \"%s\", \"%s\");\n", h->fPlugin.Data(), h->fCtor.Data());
692 
693  // check for different regexps cases for the same base + class and
694  // put them all in the same macro
695  TObjLink *lnk2 = lnk->Next();
696  while (lnk2) {
697  TPluginHandler *h2 = (TPluginHandler *) lnk2->GetObject();
698  if (h->fBase != h2->fBase || h->fClass != h2->fClass)
699  break;
700 
701  fprintf(f, " gPluginMgr->AddHandler(\"%s\", \"%s\", \"%s\",\n",
702  h2->fBase.Data(), h2->fRegexp.Data(), h2->fClass.Data());
703  fprintf(f, " \"%s\", \"%s\");\n", h2->fPlugin.Data(), h2->fCtor.Data());
704 
705  lnk = lnk2;
706  lnk2 = lnk2->Next();
707  }
708  fprintf(f, "}\n");
709  fclose(f);
710  }
711  delete [] fd;
712  lnk = lnk->Next();
713  }
714  return 0;
715 }
716 
717 ////////////////////////////////////////////////////////////////////////////////
718 /// Write in the specified environment config file the plugin records. If
719 /// plugin is specified and if it is a base class all records for that
720 /// base will be written. If it is a plugin class name, only that one
721 /// record will be written. If plugin is 0 all macros are written.
722 /// If envFile is 0 or "" the records are written to stdout.
723 /// Returns -1 if envFile cannot be created or opened, 0 otherwise.
724 
725 Int_t TPluginManager::WritePluginRecords(const char *envFile, const char *plugin) const
726 {
727  const_cast<TPluginManager*>(this)->LoadHandlersFromPluginDirs();
728 
729  if (!fHandlers) return 0;
730 
731  FILE *fd;
732  if (!envFile || !envFile[0])
733  fd = stdout;
734  else
735  fd = fopen(envFile, "w+");
736 
737  if (!fd) {
738  Error("WritePluginRecords", "error opening file %s", envFile);
739  return -1;
740  }
741 
742  TString base, base2;
743  Int_t idx = 0;
744 
745  TObjLink *lnk = fHandlers->FirstLink();
746  while (lnk) {
747  TPluginHandler *h = (TPluginHandler *) lnk->GetObject();
748  if (plugin && strcmp(plugin, h->fBase) && strcmp(plugin, h->fClass)) {
749  lnk = lnk->Next();
750  continue;
751  }
752  if (base != h->fBase) {
753  idx = 1;
754  base = h->fBase;
755  base2 = base;
756  base2.ReplaceAll("::", "@@");
757  } else
758  idx += 1;
759 
760  if (idx == 1)
761  fprintf(fd, "Plugin.%s: %s %s %s \"%s\"\n", base2.Data(), h->fRegexp.Data(),
762  h->fClass.Data(), h->fPlugin.Data(), h->fCtor.Data());
763  else
764  fprintf(fd, "+Plugin.%s: %s %s %s \"%s\"\n", base2.Data(), h->fRegexp.Data(),
765  h->fClass.Data(), h->fPlugin.Data(), h->fCtor.Data());
766 
767  // check for different regexps cases for the same base + class and
768  // put them all in the same macro
769  TObjLink *lnk2 = lnk->Next();
770  while (lnk2) {
771  TPluginHandler *h2 = (TPluginHandler *) lnk2->GetObject();
772  if (h->fBase != h2->fBase || h->fClass != h2->fClass)
773  break;
774 
775  fprintf(fd, "+Plugin.%s: %s %s %s \"%s\"\n", base2.Data(), h2->fRegexp.Data(),
776  h2->fClass.Data(), h2->fPlugin.Data(), h2->fCtor.Data());
777 
778  lnk = lnk2;
779  lnk2 = lnk2->Next();
780  }
781  lnk = lnk->Next();
782  }
783 
784  if (envFile && envFile[0])
785  fclose(fd);
786 
787  return 0;
788 }