#include "TPluginManager.h"
#include "Varargs.h"
#include "TEnv.h"
#include "TRegexp.h"
#include "TROOT.h"
#include "TSortedList.h"
#include "THashList.h"
#include "THashTable.h"
#include "Varargs.h"
#include "TClass.h"
#include "TInterpreter.h"
#include "TMethod.h"
#include "TMethodArg.h"
#include "TDataType.h"
#include "TMethodCall.h"
#include "TVirtualMutex.h"
#include "TSystem.h"
#include "TObjString.h"
#include "ThreadLocalStorage.h"
TPluginManager *gPluginMgr;
static TVirtualMutex *gPluginManagerMutex;
static bool &TPH__IsReadingDirs() {
TTHREAD_TLS(bool) readingDirs (false);
return readingDirs;
}
ClassImp(TPluginHandler)
TPluginHandler::TPluginHandler(const char *base, const char *regexp,
const char *className, const char *pluginName,
const char *ctor, const char *origin):
fBase(base),
fRegexp(regexp),
fClass(className),
fPlugin(pluginName),
fCtor(ctor),
fOrigin(origin),
fCallEnv(0),
fMethod(0),
fCanCall(0),
fIsMacro(kFALSE),
fIsGlobal(kFALSE)
{
TString aclicMode, arguments, io;
TString fname = gSystem->SplitAclicMode(fPlugin, aclicMode, arguments, io);
Bool_t validMacro = kFALSE;
if (fname.EndsWith(".C") || fname.EndsWith(".cxx") || fname.EndsWith(".cpp") ||
fname.EndsWith(".cc"))
validMacro = kTRUE;
if (validMacro && gROOT->LoadMacro(fPlugin, 0, kTRUE) == 0)
fIsMacro = kTRUE;
if (fCtor.Contains("::")) {
fIsGlobal = kTRUE;
fCtor = fCtor.Strip(TString::kLeading, ':');
}
}
TPluginHandler::~TPluginHandler()
{
delete fCallEnv;
}
Bool_t TPluginHandler::CanHandle(const char *base, const char *uri)
{
if (fBase != base)
return kFALSE;
if (!uri || fRegexp == "*")
return kTRUE;
Bool_t wildcard = kFALSE;
if (!fRegexp.MaybeRegexp())
wildcard = kTRUE;
TRegexp re(fRegexp, wildcard);
TString ruri = uri;
if (ruri.Index(re) != kNPOS)
return kTRUE;
return kFALSE;
}
void TPluginHandler::SetupCallEnv()
{
fCanCall = -1;
TClass *cl = TClass::GetClass(fClass);
if (!cl && !fIsGlobal) {
Error("SetupCallEnv", "class %s not found in plugin %s", fClass.Data(),
fPlugin.Data());
return;
}
TString method = fCtor(0, fCtor.Index("("));
TString proto = fCtor(fCtor.Index("(")+1, fCtor.Index(")")-fCtor.Index("(")-1);
if (fIsGlobal) {
cl = 0;
fMethod = gROOT->GetGlobalFunctionWithPrototype(method, proto, kTRUE);
} else {
fMethod = cl->GetMethodWithPrototype(method, proto);
}
if (!fMethod) {
if (fIsGlobal)
Error("SetupCallEnv", "global function %s not found", method.Data());
else
Error("SetupCallEnv", "method %s not found in class %s", method.Data(),
fClass.Data());
return;
}
if (!fIsGlobal && !(fMethod->Property() & kIsPublic)) {
Error("SetupCallEnv", "method %s is not public", method.Data());
return;
}
fCallEnv = new TMethodCall;
fCallEnv->InitWithPrototype(cl, method, proto);
fCanCall = 1;
return;
}
Int_t TPluginHandler::CheckPlugin() const
{
if (fIsMacro) {
if (TClass::GetClass(fClass)) return 0;
return gROOT->LoadMacro(fPlugin, 0, kTRUE);
} else
return gROOT->LoadClass(fClass, fPlugin, kTRUE);
}
Int_t TPluginHandler::LoadPlugin()
{
if (fIsMacro) {
if (TClass::GetClass(fClass)) return 0;
return gROOT->LoadMacro(fPlugin);
} else {
if (gROOT->LoadClass(fClass)) return 0;
return gROOT->LoadClass(fClass, fPlugin);
}
}
Long_t TPluginHandler::ExecPlugin(Int_t va_(nargs), ...)
{
if (fCtor.IsNull()) {
Error("ExecPlugin", "no ctor specified for this handler %s", fClass.Data());
return 0;
}
if (!fCallEnv && !fCanCall)
SetupCallEnv();
if (fCanCall == -1)
return 0;
if (nargs < fMethod->GetNargs() - fMethod->GetNargsOpt() ||
nargs > fMethod->GetNargs()) {
Error("ExecPlugin", "nargs (%d) not consistent with expected number of arguments ([%d-%d])",
nargs, fMethod->GetNargs() - fMethod->GetNargsOpt(),
fMethod->GetNargs());
return 0;
}
R__LOCKGUARD2(gCINTMutex);
fCallEnv->ResetParam();
if (nargs > 0) {
TIter next(fMethod->GetListOfMethodArgs());
TMethodArg *arg;
va_list ap;
va_start(ap, va_(nargs));
for (int i = 0; i < nargs; i++) {
arg = (TMethodArg*) next();
TString type = arg->GetFullTypeName();
TDataType *dt = gROOT->GetType(type);
if (dt)
type = dt->GetFullTypeName();
if (arg->Property() & (kIsPointer | kIsArray | kIsReference))
fCallEnv->SetParam((Long_t) va_arg(ap, void*));
else if (type == "bool")
fCallEnv->SetParam((Long_t) va_arg(ap, int));
else if (type == "char" || type == "unsigned char")
fCallEnv->SetParam((Long_t) va_arg(ap, int));
else if (type == "short" || type == "unsigned short")
fCallEnv->SetParam((Long_t) va_arg(ap, int));
else if (type == "int" || type == "unsigned int")
fCallEnv->SetParam((Long_t) va_arg(ap, int));
else if (type == "long" || type == "unsigned long")
fCallEnv->SetParam((Long_t) va_arg(ap, long));
else if (type == "long long")
fCallEnv->SetParam((Long64_t) va_arg(ap, Long64_t));
else if (type == "unsigned long long")
fCallEnv->SetParam((ULong64_t) va_arg(ap, ULong64_t));
else if (type == "float")
fCallEnv->SetParam((Double_t) va_arg(ap, double));
else if (type == "double")
fCallEnv->SetParam((Double_t) va_arg(ap, double));
}
va_end(ap);
}
Long_t ret;
fCallEnv->Execute(ret);
return ret;
}
void TPluginHandler::Print(Option_t *opt) const
{
const char *exist = "";
if (CheckPlugin() == -1)
exist = " [*]";
Printf("%-20s %-13s %-18s %s%s", fBase.Data(), fRegexp.Data(),
fClass.Data(), fPlugin.Data(), exist);
if (strchr(opt, 'a')) {
if (strlen(exist) == 0) {
TString lib = fPlugin;
if (!lib.BeginsWith("lib"))
lib = "lib" + lib;
char *path = gSystem->DynamicPathName(lib, kTRUE);
if (path) Printf(" [Lib: %s]", path);
delete [] path;
}
Printf(" [Ctor: %s]", fCtor.Data());
Printf(" [origin: %s]", fOrigin.Data());
}
}
ClassImp(TPluginManager)
TPluginManager::~TPluginManager()
{
delete fHandlers;
delete fBasesLoaded;
}
void TPluginManager::LoadHandlersFromEnv(TEnv *env)
{
if (!env) return;
TIter next(env->GetTable());
TEnvRec *er;
while ((er = (TEnvRec*) next())) {
const char *s;
if ((s = strstr(er->GetName(), "Plugin."))) {
const char *val = env->GetValue(s, (const char*)0);
if (val) {
Int_t cnt = 0;
char *v = StrDup(val);
s += 7;
while (1) {
TString regexp = strtok(!cnt ? v : 0, "; ");
if (regexp.IsNull()) break;
TString clss = strtok(0, "; ");
if (clss.IsNull()) break;
TString plugin = strtok(0, "; ");
if (plugin.IsNull()) break;
TString ctor = strtok(0, ";\"");
if (!ctor.Contains("("))
ctor = strtok(0, ";\"");
AddHandler(s, regexp, clss, plugin, ctor, "TEnv");
cnt++;
}
delete [] v;
}
}
}
}
void TPluginManager::LoadHandlerMacros(const char *path)
{
void *dirp = gSystem->OpenDirectory(path);
if (dirp) {
if (gDebug > 0)
Info("LoadHandlerMacros", "%s", path);
TSortedList macros;
macros.SetOwner();
const char *f1;
while ((f1 = gSystem->GetDirEntry(dirp))) {
TString f = f1;
if (f[0] == 'P' && f.EndsWith(".C")) {
const char *p = gSystem->ConcatFileName(path, f);
if (!gSystem->AccessPathName(p, kReadPermission)) {
macros.Add(new TObjString(p));
}
delete [] p;
}
}
TIter next(¯os);
TObjString *s;
while ((s = (TObjString*)next())) {
if (gDebug > 1)
Info("LoadHandlerMacros", " plugin macro: %s", s->String().Data());
Long_t res;
if ((res = gROOT->Macro(s->String(), 0, kFALSE)) < 0) {
Error("LoadHandlerMacros", "pluging macro %s returned %ld",
s->String().Data(), res);
}
}
}
gSystem->FreeDirectory(dirp);
}
void TPluginManager::LoadHandlersFromPluginDirs(const char *base)
{
TObjArray *dirs = nullptr;
{
R__LOCKGUARD2(gCINTMutex);
if (!fBasesLoaded) {
fBasesLoaded = new THashTable();
fBasesLoaded->SetOwner();
gInterpreter->InitializeDictionaries();
}
TString sbase = base;
if (sbase != "") {
sbase.ReplaceAll("::", "@@");
if (fBasesLoaded->FindObject(sbase))
return;
fBasesLoaded->Add(new TObjString(sbase));
}
TPH__IsReadingDirs() = kTRUE;
TString plugindirs = gEnv->GetValue("Root.PluginPath", (char*)0);
#ifdef WIN32
dirs = plugindirs.Tokenize(";");
#else
dirs = plugindirs.Tokenize(":");
#endif
TString d;
for (Int_t i = 0; i < dirs->GetEntriesFast(); i++) {
d = ((TObjString*)dirs->At(i))->GetString();
Int_t skip = 0;
for (Int_t j = 0; j < i; j++) {
TString pd = ((TObjString*)dirs->At(j))->GetString();
if (pd == d) {
skip++;
break;
}
}
if (!skip) {
if (sbase != "") {
const char *p = gSystem->ConcatFileName(d, sbase);
LoadHandlerMacros(p);
delete [] p;
} else {
void *dirp = gSystem->OpenDirectory(d);
if (dirp) {
if (gDebug > 0)
Info("LoadHandlersFromPluginDirs", "%s", d.Data());
const char *f1;
while ((f1 = gSystem->GetDirEntry(dirp))) {
TString f = f1;
const char *p = gSystem->ConcatFileName(d, f);
LoadHandlerMacros(p);
fBasesLoaded->Add(new TObjString(f));
delete [] p;
}
}
gSystem->FreeDirectory(dirp);
}
}
}
TPH__IsReadingDirs() = kFALSE;
}
delete dirs;
}
void TPluginManager::AddHandler(const char *base, const char *regexp,
const char *className, const char *pluginName,
const char *ctor, const char *origin)
{
{
R__LOCKGUARD2(gPluginManagerMutex);
if (!fHandlers) {
fHandlers = new TList;
fHandlers->SetOwner();
}
}
RemoveHandler(base, regexp);
if (TPH__IsReadingDirs())
origin = gInterpreter->GetCurrentMacroName();
TPluginHandler *h = new TPluginHandler(base, regexp, className,
pluginName, ctor, origin);
{
R__LOCKGUARD2(gPluginManagerMutex);
fHandlers->Add(h);
}
}
void TPluginManager::RemoveHandler(const char *base, const char *regexp)
{
R__LOCKGUARD2(gPluginManagerMutex);
if (!fHandlers) return;
TIter next(fHandlers);
TPluginHandler *h;
while ((h = (TPluginHandler*) next())) {
if (h->fBase == base) {
if (!regexp || h->fRegexp == regexp) {
fHandlers->Remove(h);
delete h;
}
}
}
}
TPluginHandler *TPluginManager::FindHandler(const char *base, const char *uri)
{
LoadHandlersFromPluginDirs(base);
R__LOCKGUARD2(gPluginManagerMutex);
TIter next(fHandlers);
TPluginHandler *h;
while ((h = (TPluginHandler*) next())) {
if (h->CanHandle(base, uri)) {
if (gDebug > 0)
Info("FindHandler", "found plugin for %s", h->GetClass());
return h;
}
}
if (gDebug > 2) {
if (uri)
Info("FindHandler", "did not find plugin for class %s and uri %s", base, uri);
else
Info("FindHandler", "did not find plugin for class %s", base);
}
return 0;
}
void TPluginManager::Print(Option_t *opt) const
{
if (!fHandlers) return;
TIter next(fHandlers);
TPluginHandler *h;
Int_t cnt = 0, cntmiss = 0;
Printf("=====================================================================");
Printf("Base Regexp Class Plugin");
Printf("=====================================================================");
while ((h = (TPluginHandler*) next())) {
cnt++;
h->Print(opt);
if (h->CheckPlugin() == -1)
cntmiss++;
}
Printf("=====================================================================");
Printf("%d plugin handlers registered", cnt);
Printf("[*] %d %s not available", cntmiss, cntmiss==1 ? "plugin" : "plugins");
Printf("=====================================================================\n");
}
Int_t TPluginManager::WritePluginMacros(const char *dir, const char *plugin) const
{
const_cast<TPluginManager*>(this)->LoadHandlersFromPluginDirs();
if (!fHandlers) return 0;
TString d;
if (!dir || !dir[0])
d = ".";
else
d = dir;
if (gSystem->AccessPathName(d, kWritePermission)) {
Error("WritePluginMacros", "cannot write in directory %s", d.Data());
return -1;
}
TString base;
Int_t idx = 0;
TObjLink *lnk = fHandlers->FirstLink();
while (lnk) {
TPluginHandler *h = (TPluginHandler *) lnk->GetObject();
if (plugin && strcmp(plugin, h->fBase) && strcmp(plugin, h->fClass)) {
lnk = lnk->Next();
continue;
}
if (base != h->fBase) {
idx = 10;
base = h->fBase;
} else
idx += 10;
const char *dd = gSystem->ConcatFileName(d, h->fBase);
TString sdd = dd;
sdd.ReplaceAll("::", "@@");
delete [] dd;
if (gSystem->AccessPathName(sdd, kWritePermission)) {
if (gSystem->MakeDirectory(sdd) < 0) {
Error("WritePluginMacros", "cannot create directory %s", sdd.Data());
return -1;
}
}
TString fn;
fn.Form("P%03d_%s.C", idx, h->fClass.Data());
const char *fd = gSystem->ConcatFileName(sdd, fn);
FILE *f = fopen(fd, "w");
if (f) {
fprintf(f, "void P%03d_%s()\n{\n", idx, h->fClass.Data());
fprintf(f, " gPluginMgr->AddHandler(\"%s\", \"%s\", \"%s\",\n",
h->fBase.Data(), h->fRegexp.Data(), h->fClass.Data());
fprintf(f, " \"%s\", \"%s\");\n", h->fPlugin.Data(), h->fCtor.Data());
TObjLink *lnk2 = lnk->Next();
while (lnk2) {
TPluginHandler *h2 = (TPluginHandler *) lnk2->GetObject();
if (h->fBase != h2->fBase || h->fClass != h2->fClass)
break;
fprintf(f, " gPluginMgr->AddHandler(\"%s\", \"%s\", \"%s\",\n",
h2->fBase.Data(), h2->fRegexp.Data(), h2->fClass.Data());
fprintf(f, " \"%s\", \"%s\");\n", h2->fPlugin.Data(), h2->fCtor.Data());
lnk = lnk2;
lnk2 = lnk2->Next();
}
fprintf(f, "}\n");
fclose(f);
}
delete [] fd;
lnk = lnk->Next();
}
return 0;
}
Int_t TPluginManager::WritePluginRecords(const char *envFile, const char *plugin) const
{
const_cast<TPluginManager*>(this)->LoadHandlersFromPluginDirs();
if (!fHandlers) return 0;
FILE *fd;
if (!envFile || !envFile[0])
fd = stdout;
else
fd = fopen(envFile, "w+");
if (!fd) {
Error("WritePluginRecords", "error opening file %s", envFile);
return -1;
}
TString base, base2;
Int_t idx = 0;
TObjLink *lnk = fHandlers->FirstLink();
while (lnk) {
TPluginHandler *h = (TPluginHandler *) lnk->GetObject();
if (plugin && strcmp(plugin, h->fBase) && strcmp(plugin, h->fClass)) {
lnk = lnk->Next();
continue;
}
if (base != h->fBase) {
idx = 1;
base = h->fBase;
base2 = base;
base2.ReplaceAll("::", "@@");
} else
idx += 1;
if (idx == 1)
fprintf(fd, "Plugin.%s: %s %s %s \"%s\"\n", base2.Data(), h->fRegexp.Data(),
h->fClass.Data(), h->fPlugin.Data(), h->fCtor.Data());
else
fprintf(fd, "+Plugin.%s: %s %s %s \"%s\"\n", base2.Data(), h->fRegexp.Data(),
h->fClass.Data(), h->fPlugin.Data(), h->fCtor.Data());
TObjLink *lnk2 = lnk->Next();
while (lnk2) {
TPluginHandler *h2 = (TPluginHandler *) lnk2->GetObject();
if (h->fBase != h2->fBase || h->fClass != h2->fClass)
break;
fprintf(fd, "+Plugin.%s: %s %s %s \"%s\"\n", base2.Data(), h2->fRegexp.Data(),
h2->fClass.Data(), h2->fPlugin.Data(), h2->fCtor.Data());
lnk = lnk2;
lnk2 = lnk2->Next();
}
lnk = lnk->Next();
}
if (envFile && envFile[0])
fclose(fd);
return 0;
}