#include "RConfigure.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include "TEnv.h"
#include "TROOT.h"
#include "TSystem.h"
#include "THashList.h"
#include "TError.h"
TEnv *gEnv;
static struct BoolNameTable_t {
const char *fName;
Int_t fValue;
} gBoolNames[]= {
{ "TRUE", 1 },
{ "FALSE", 0 },
{ "ON", 1 },
{ "OFF", 0 },
{ "YES", 1 },
{ "NO", 0 },
{ "OK", 1 },
{ "NOT", 0 },
{ 0, 0 }
};
class TEnvParser {
private:
FILE *fIfp;
protected:
TEnv *fEnv;
public:
TEnvParser(TEnv *e, FILE *f) : fIfp(f), fEnv(e) { }
virtual ~TEnvParser() { }
virtual void KeyValue(const TString&, const TString&, const TString&) { }
virtual void Char(Int_t) { }
void Parse();
};
void TEnvParser::Parse()
{
TString name(1024);
TString type(1024);
TString value(1024);
int c, state = 0;
while ((c = fgetc(fIfp)) != EOF) {
if (c == 13)
continue;
if (c == '\n') {
state = 0;
if (name.Length() > 0) {
KeyValue(name, value, type);
name.Clear();
value.Clear();
type.Clear();
}
Char(c);
continue;
}
switch (state) {
case 0:
switch (c) {
case ' ':
case '\t':
break;
case '#':
state = 1;
break;
default:
state = 2;
break;
}
break;
case 1:
break;
case 2:
switch (c) {
case ' ':
case '\t':
case ':':
state = 3;
break;
case '(':
state = 7;
break;
default:
break;
}
break;
case 3:
if (c != ' ' && c != '\t')
state = 4;
break;
case 4:
break;
case 5:
if (c == ')')
state = 6;
break;
case 6:
state = (c == ':') ? 3 : 4;
break;
case 7:
state = (c == ')') ? 6 : 5;
break;
}
switch (state) {
case 2:
name.Append(c);
break;
case 4:
value.Append(c);
break;
case 5:
type.Append(c);
break;
}
if (state != 4)
Char(c);
}
}
class TReadEnvParser : public TEnvParser {
private:
EEnvLevel fLevel;
public:
TReadEnvParser(TEnv *e, FILE *f, EEnvLevel l) : TEnvParser(e, f), fLevel(l) { }
void KeyValue(const TString &name, const TString &value, const TString &type)
{ fEnv->SetValue(name, value, fLevel, type); }
};
class TWriteEnvParser : public TEnvParser {
private:
FILE *fOfp;
public:
TWriteEnvParser(TEnv *e, FILE *f, FILE *of) : TEnvParser(e, f), fOfp(of) { }
void KeyValue(const TString &name, const TString &value, const TString &type);
void Char(Int_t c) { fputc(c, fOfp); }
};
void TWriteEnvParser::KeyValue(const TString &name, const TString &value,
const TString &)
{
TEnvRec *er = fEnv->Lookup(name);
if (er && er->fModified) {
er->fModified = kFALSE;
fprintf(fOfp, "%s", er->fValue.Data());
} else
fprintf(fOfp, "%s", value.Data());
}
TEnvRec::TEnvRec(const char *n, const char *v, const char *t, EEnvLevel l)
: fName(n), fType(t), fLevel(l)
{
fValue = ExpandValue(v);
fModified = (l == kEnvChange);
}
void TEnvRec::ChangeValue(const char *v, const char *, EEnvLevel l,
Bool_t append, Bool_t ignoredup)
{
if (l != kEnvChange && fLevel == l && !append) {
if (fValue != v && !ignoredup)
::Warning("TEnvRec::ChangeValue",
"duplicate entry <%s=%s> for level %d; ignored", fName.Data(), v, l);
return;
}
if (!append) {
if (fValue != v) {
if (l == kEnvChange)
fModified = kTRUE;
else
fModified = kFALSE;
fLevel = l;
fValue = ExpandValue(v);
}
} else {
if (l == kEnvChange)
fModified = kTRUE;
fLevel = l;
fValue += " ";
fValue += ExpandValue(v);
}
}
Int_t TEnvRec::Compare(const TObject *op) const
{
return fName.CompareTo(((TEnvRec*)op)->fName);
}
TString TEnvRec::ExpandValue(const char *value)
{
const char *vv;
char *v, *vorg = StrDup(value);
v = vorg;
char *s1, *s2;
int len = 0;
while ((s1 = (char*)strstr(v, "$("))) {
s1 += 2;
s2 = (char*)strchr(s1, ')');
if (!s2) {
len = 0;
break;
}
*s2 = 0;
vv = gSystem->Getenv(s1);
if (vv) len += strlen(vv);
*s2 = ')';
v = s2 + 1;
}
if (!len) {
delete [] vorg;
return TString(value);
}
v = vorg;
int nch = strlen(v) + len;
char *nv = new char[nch];
*nv = 0;
while ((s1 = (char*)strstr(v, "$("))) {
*s1 = 0;
strlcat(nv, v,nch);
*s1 = '$';
s1 += 2;
s2 = (char*)strchr(s1, ')');
*s2 = 0;
vv = gSystem->Getenv(s1);
if (vv) strlcat(nv, vv,nch);
*s2 = ')';
v = s2 + 1;
}
if (*v) strlcat(nv, v,nch);
TString val = nv;
delete [] nv;
delete [] vorg;
return val;
}
ClassImp(TEnv)
TEnv::TEnv(const char *name)
{
fIgnoreDup = kFALSE;
if (!name || !name[0] || !gSystem)
fTable = 0;
else {
fTable = new THashList(1000);
fRcName = name;
TString sname = "system";
sname += name;
#ifdef ROOTETCDIR
char *s = gSystem->ConcatFileName(ROOTETCDIR, sname);
#else
TString etc = gRootDir;
#ifdef WIN32
etc += "\\etc";
#else
etc += "/etc";
#endif
#if defined(R__MACOSX) && (TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR)
etc = gRootDir;
#endif
char *s = gSystem->ConcatFileName(etc, sname);
#endif
ReadFile(s, kEnvGlobal);
delete [] s;
if (!gSystem->Getenv("ROOTENV_NO_HOME")) {
s = gSystem->ConcatFileName(gSystem->HomeDirectory(), name);
ReadFile(s, kEnvUser);
delete [] s;
if (strcmp(gSystem->HomeDirectory(), gSystem->WorkingDirectory()))
ReadFile(name, kEnvLocal);
} else
ReadFile(name, kEnvLocal);
}
}
TEnv::~TEnv()
{
if (fTable) {
fTable->Delete();
SafeDelete(fTable);
}
}
const char *TEnv::Getvalue(const char *name)
{
Bool_t haveProgName = kFALSE;
if (gProgName && strlen(gProgName) > 0)
haveProgName = kTRUE;
TString aname;
TEnvRec *er = 0;
if (haveProgName && gSystem && gProgName) {
aname = gSystem->GetName(); aname += "."; aname += gProgName;
aname += "."; aname += name;
er = Lookup(aname);
}
if (er == 0 && gSystem && gROOT) {
aname = gSystem->GetName(); aname += "."; aname += gROOT->GetName();
aname += "."; aname += name;
er = Lookup(aname);
}
if (er == 0 && gSystem) {
aname = gSystem->GetName(); aname += ".*."; aname += name;
er = Lookup(aname);
}
if (er == 0 && haveProgName && gProgName) {
aname = gProgName; aname += "."; aname += name;
er = Lookup(aname);
}
if (er == 0 && gROOT) {
aname = gROOT->GetName(); aname += "."; aname += name;
er = Lookup(aname);
}
if (er == 0) {
aname = "*.*."; aname += name;
er = Lookup(aname);
}
if (er == 0) {
aname = "*."; aname += name;
er = Lookup(aname);
}
if (er == 0) {
er = Lookup(name);
}
if (er == 0)
return 0;
return er->fValue;
}
Int_t TEnv::GetValue(const char *name, Int_t dflt)
{
const char *cp = TEnv::Getvalue(name);
if (cp) {
char buf2[512], *cp2 = buf2;
while (isspace((int)*cp))
cp++;
if (*cp) {
BoolNameTable_t *bt;
if (isdigit((int)*cp) || *cp == '-' || *cp == '+')
return atoi(cp);
while (isalpha((int)*cp))
*cp2++ = toupper((int)*cp++);
*cp2 = 0;
for (bt = gBoolNames; bt->fName; bt++)
if (strcmp(buf2, bt->fName) == 0)
return bt->fValue;
}
}
return dflt;
}
Double_t TEnv::GetValue(const char *name, Double_t dflt)
{
const char *cp = TEnv::Getvalue(name);
if (cp) {
char *endptr;
Double_t val = strtod(cp, &endptr);
if (val == 0.0 && cp == endptr)
return dflt;
return val;
}
return dflt;
}
const char *TEnv::GetValue(const char *name, const char *dflt)
{
const char *cp = TEnv::Getvalue(name);
if (cp)
return cp;
return dflt;
}
TEnvRec *TEnv::Lookup(const char *name)
{
if (!fTable) return 0;
return (TEnvRec*) fTable->FindObject(name);
}
void TEnv::Print(Option_t *opt) const
{
if (!opt || !opt[0]) {
PrintEnv();
return;
}
if (!strcmp(opt, "global"))
PrintEnv(kEnvGlobal);
if (!strcmp(opt, "user"))
PrintEnv(kEnvUser);
if (!strcmp(opt, "local"))
PrintEnv(kEnvLocal);
}
void TEnv::PrintEnv(EEnvLevel level) const
{
if (!fTable) return;
TIter next(fTable);
TEnvRec *er;
static const char *lc[] = { "Global", "User", "Local", "Changed", "All" };
while ((er = (TEnvRec*) next()))
if (er->fLevel == level || level == kEnvAll)
Printf("%-25s %-30s [%s]", Form("%s:", er->fName.Data()),
er->fValue.Data(), lc[er->fLevel]);
}
Int_t TEnv::ReadFile(const char *fname, EEnvLevel level)
{
if (!fname || !fname[0]) {
Error("ReadFile", "no file name specified");
return -1;
}
FILE *ifp;
if ((ifp = fopen(fname, "r"))) {
TReadEnvParser rp(this, ifp, level);
rp.Parse();
fclose(ifp);
return 0;
}
return -1;
}
Int_t TEnv::WriteFile(const char *fname, EEnvLevel level)
{
if (!fname || !fname[0]) {
Error("WriteFile", "no file name specified");
return -1;
}
if (!fTable) {
Error("WriteFile", "TEnv table is empty");
return -1;
}
FILE *ofp;
if ((ofp = fopen(fname, "w"))) {
TIter next(fTable);
TEnvRec *er;
while ((er = (TEnvRec*) next()))
if (er->fLevel == level || level == kEnvAll)
fprintf(ofp, "%-40s %s\n", Form("%s:", er->fName.Data()),
er->fValue.Data());
fclose(ofp);
return 0;
}
Error("WriteFile", "cannot open %s for writing", fname);
return -1;
}
void TEnv::Save()
{
if (fRcName == "") {
Error("Save", "no resource file name specified");
return;
}
SaveLevel(kEnvLocal);
SaveLevel(kEnvUser);
SaveLevel(kEnvGlobal);
}
void TEnv::SaveLevel(EEnvLevel level)
{
if (fRcName == "") {
Error("SaveLevel", "no resource file name specified");
return;
}
if (!fTable) {
Error("SaveLevel", "TEnv table is empty");
return;
}
TString rootrcdir;
FILE *ifp, *ofp;
if (level == kEnvGlobal) {
TString sname = "system";
sname += fRcName;
#ifdef ROOTETCDIR
char *s = gSystem->ConcatFileName(ROOTETCDIR, sname);
#else
TString etc = gRootDir;
#ifdef WIN32
etc += "\\etc";
#else
etc += "/etc";
#endif
char *s = gSystem->ConcatFileName(etc, sname);
#endif
rootrcdir = s;
delete [] s;
} else if (level == kEnvUser) {
char *s = gSystem->ConcatFileName(gSystem->HomeDirectory(), fRcName);
rootrcdir = s;
delete [] s;
} else if (level == kEnvLocal)
rootrcdir = fRcName;
else
return;
if ((ofp = fopen(Form("%s.new", rootrcdir.Data()), "w"))) {
ifp = fopen(rootrcdir.Data(), "r");
if (ifp == 0) {
ifp = fopen(rootrcdir.Data(), "w");
if (ifp) {
fclose(ifp);
ifp = 0;
}
}
if (ifp || (ifp = fopen(rootrcdir.Data(), "r"))) {
TWriteEnvParser wp(this, ifp, ofp);
wp.Parse();
TIter next(fTable);
TEnvRec *er;
while ((er = (TEnvRec*) next())) {
if (er->fModified) {
if (er->fLevel == kEnvChange) er->fLevel = level;
if (er->fLevel == level) {
er->fModified = kFALSE;
fprintf(ofp, "%-40s %s\n", Form("%s:", er->fName.Data()),
er->fValue.Data());
}
}
}
fclose(ifp);
fclose(ofp);
gSystem->Rename(rootrcdir.Data(), Form("%s.bak", rootrcdir.Data()));
gSystem->Rename(Form("%s.new", rootrcdir.Data()), rootrcdir.Data());
return;
}
fclose(ofp);
} else
Error("SaveLevel", "cannot write to file %s", rootrcdir.Data());
}
void TEnv::SetValue(const char *name, const char *value, EEnvLevel level,
const char *type)
{
if (!fTable)
fTable = new THashList(1000);
const char *nam = name;
Bool_t append = kFALSE;
if (name[0] == '+') {
nam = &name[1];
append = kTRUE;
}
TEnvRec *er = Lookup(nam);
if (er)
er->ChangeValue(value, type, level, append, fIgnoreDup);
else
fTable->Add(new TEnvRec(nam, value, type, level));
}
void TEnv::SetValue(const char *name, EEnvLevel level)
{
TString buf = name;
int l = buf.Index("=");
if (l > 0) {
TString nm = buf(0, l);
TString val = buf(l+1, buf.Length());
SetValue(nm, val, level);
} else
SetValue(name, "1", level);
}
void TEnv::SetValue(const char *name, Int_t value)
{
SetValue(name, Form("%d", value));
}
void TEnv::SetValue(const char *name, double value)
{
SetValue(name, Form("%g", value));
}
Bool_t TEnv::IgnoreDuplicates(Bool_t ignore)
{
Bool_t ret = fIgnoreDup;
fIgnoreDup = ignore;
return ret;
}