#include <stdio.h>
#include <assert.h>
#include "RConfigure.h"
#include "TTabCom.h"
#include "TClass.h"
#include "TSystem.h"
#include "TROOT.h"
#include "TMethod.h"
#include "TEnv.h"
#include "TBenchmark.h"
#include "TError.h"
#include "TGlobal.h"
#include "TList.h"
#include "Getline.h"
#include "TFunction.h"
#include "TMethodArg.h"
#include "TInterpreter.h"
#include "Riostream.h"
#include "Rstrstream.h"
#define BUF_SIZE 1024 // must match value in C_Getline.c (for bounds checking)
#define IfDebug(x) if(gDebug==TTabCom::kDebug) x
#ifdef R__WIN32
#undef tmpnam
#define tmpnam(a) _tempnam(a, 0)
const char kDelim = ';';
#else
const char kDelim = ':';
#endif
ClassImp(TTabCom)
TTabCom *gTabCom = 0;
TTabCom::TTabCom()
{
fpDirectives = 0;
fpPragmas = 0;
fpGlobals = 0;
fpGlobalFuncs = 0;
fpClasses = 0;
fpNamespaces = 0;
fpUsers = 0;
fBuf = 0;
fpLoc = 0;
fpEnvVars = 0;
fpFiles = 0;
fpSysIncFiles = 0;
fVarIsPointer = kFALSE;
fLastIter = 0;
InitPatterns();
}
TTabCom::~TTabCom()
{
ClearAll();
ClearSysIncFiles();
ClearUsers();
}
void TTabCom::ClearClasses()
{
if (fpClasses) {
fpClasses->Delete(0);
delete fpClasses;
fpClasses = 0;
}
if (fpNamespaces) {
fpNamespaces->Delete(0);
delete fpNamespaces;
fpNamespaces = 0;
}
}
void TTabCom::ClearCppDirectives()
{
if (!fpDirectives)
return;
fpDirectives->Delete(0);
delete fpDirectives;
fpDirectives = 0;
}
void TTabCom::ClearEnvVars()
{
if (!fpEnvVars)
return;
fpEnvVars->Delete(0);
delete fpEnvVars;
fpEnvVars = 0;
}
void TTabCom::ClearFiles()
{
if (!fpFiles)
return;
fpFiles->Delete(0);
delete fpFiles;
fpFiles = 0;
}
void TTabCom::ClearGlobalFunctions()
{
if (!fpGlobalFuncs)
return;
fpGlobalFuncs->Delete(0);
delete fpGlobalFuncs;
fpGlobalFuncs = 0;
}
void TTabCom::ClearGlobals()
{
if (!fpGlobals)
return;
fpGlobals->Delete(0);
delete fpGlobals;
fpGlobals = 0;
}
void TTabCom::ClearPragmas()
{
if (!fpPragmas)
return;
fpPragmas->Delete(0);
delete fpPragmas;
fpPragmas = 0;
}
void TTabCom::ClearSysIncFiles()
{
if (!fpSysIncFiles)
return;
fpSysIncFiles->Delete(0);
delete fpSysIncFiles;
fpSysIncFiles = 0;
}
void TTabCom::ClearUsers()
{
if (!fpUsers)
return;
fpUsers->Delete(0);
delete fpUsers;
fpUsers = 0;
}
void TTabCom::ClearAll()
{
ClearClasses();
ClearCppDirectives();
ClearEnvVars();
ClearFiles();
ClearGlobalFunctions();
ClearGlobals();
ClearPragmas();
}
void TTabCom::RehashClasses()
{
ClearClasses();
GetListOfClasses();
}
void TTabCom::RehashCppDirectives()
{
ClearCppDirectives();
GetListOfCppDirectives();
}
void TTabCom::RehashEnvVars()
{
ClearEnvVars();
GetListOfEnvVars();
}
void TTabCom::RehashFiles()
{
ClearFiles();
}
void TTabCom::RehashGlobalFunctions()
{
ClearGlobalFunctions();
GetListOfGlobalFunctions();
}
void TTabCom::RehashGlobals()
{
ClearGlobals();
GetListOfGlobals();
}
void TTabCom::RehashPragmas()
{
ClearPragmas();
GetListOfPragmas();
}
void TTabCom::RehashSysIncFiles()
{
ClearSysIncFiles();
GetListOfSysIncFiles();
}
void TTabCom::RehashUsers()
{
ClearUsers();
GetListOfUsers();
}
void TTabCom::RehashAll()
{
RehashClasses();
RehashCppDirectives();
RehashEnvVars();
RehashFiles();
RehashGlobalFunctions();
RehashGlobals();
RehashPragmas();
}
const TSeqCollection *TTabCom::GetListOfClasses()
{
if (!fpClasses) {
const char *tmpfilename = tmpnam(0);
FILE *fout = fopen(tmpfilename, "w");
if (!fout) return 0;
gCint->DisplayClass(fout, (char*)"", 0, 0);
fclose(fout);
ifstream file1(tmpfilename);
if (!file1) {
Error("TTabCom::GetListOfClasses", "could not open file \"%s\"",
tmpfilename);
gSystem->Unlink(tmpfilename);
return 0;
}
file1.ignore(32000, '\n');
file1.ignore(32000, '\n');
fpClasses = new TContainer;
fpNamespaces = new TContainer;
TString line;
while (file1) {
line = "";
line.ReadLine(file1, kFALSE);
line = line(23, 32000);
int index;
Bool_t isanamespace = kFALSE;
if (0);
else if ((index = line.Index(" class ")) >= 0)
line = line(1 + index + 6, 32000);
else if ((index = line.Index(" namespace ")) >= 0) {
line = line(1 + index + 10, 32000);
isanamespace = kTRUE;
} else if ((index = line.Index(" struct ")) >= 0)
line = line(1 + index + 7, 32000);
else if ((index = line.Index(" enum ")) >= 0)
line = line(1 + index + 5, 32000);
else if ((index = line.Index(" (unknown) ")) >= 0)
line = line(1 + index + 10, 32000);
line = line("[^ ]*");
if (isanamespace)
fpNamespaces->Add(new TObjString(line));
else
fpClasses->Add(new TObjString(line));
}
file1.close();
gSystem->Unlink(tmpfilename);
}
return fpClasses;
}
const TSeqCollection *TTabCom::GetListOfCppDirectives()
{
if (!fpDirectives) {
fpDirectives = new TContainer;
fpDirectives->Add(new TObjString("if"));
fpDirectives->Add(new TObjString("ifdef"));
fpDirectives->Add(new TObjString("ifndef"));
fpDirectives->Add(new TObjString("elif"));
fpDirectives->Add(new TObjString("else"));
fpDirectives->Add(new TObjString("endif"));
fpDirectives->Add(new TObjString("include"));
fpDirectives->Add(new TObjString("define"));
fpDirectives->Add(new TObjString("undef"));
fpDirectives->Add(new TObjString("line"));
fpDirectives->Add(new TObjString("error"));
fpDirectives->Add(new TObjString("pragma"));
}
return fpDirectives;
}
const TSeqCollection *TTabCom::GetListOfFilesInPath(const char path[])
{
static TString previousPath;
if (path && fpFiles && strcmp(path, previousPath) == 0) {
return fpFiles;
} else {
ClearFiles();
fpFiles = NewListOfFilesInPath(path);
previousPath = path;
}
return fpFiles;
}
const TSeqCollection *TTabCom::GetListOfEnvVars()
{
if (!fpEnvVars) {
const char *tmpfilename = tmpnam(0);
TString cmd;
#ifndef WIN32
char *env = gSystem->Which(gSystem->Getenv("PATH"), "env", kExecutePermission);
if (!env)
return 0;
cmd = env;
cmd += " > ";
delete [] env;
#else
cmd = "set > ";
#endif
cmd += tmpfilename;
cmd += "\n";
gSystem->Exec(cmd.Data());
ifstream file1(tmpfilename);
if (!file1) {
Error("TTabCom::GetListOfEnvVars", "could not open file \"%s\"",
tmpfilename);
gSystem->Unlink(tmpfilename);
return 0;
}
fpEnvVars = new TContainer;
TString line;
while (file1)
{
line.ReadToDelim(file1, '=');
file1.ignore(32000, '\n');
fpEnvVars->Add(new TObjString(line.Data()));
}
file1.close();
gSystem->Unlink(tmpfilename);
}
return fpEnvVars;
}
const TSeqCollection *TTabCom::GetListOfGlobals()
{
if (!fpGlobals) {
fpGlobals = new TContainer;
DataMemberInfo_t *a;
int last = 0;
int nglob = 0;
DataMemberInfo_t *t = gCint->DataMemberInfo_Factory();
while (gCint->DataMemberInfo_Next(t))
nglob++;
for (int i = 0; i < nglob; i++) {
a = gCint->DataMemberInfo_Factory();
gCint->DataMemberInfo_Next(a);
for (int j = 0; j < last; j++)
gCint->DataMemberInfo_Next(a);
if (gCint->DataMemberInfo_IsValid(a) && gCint->DataMemberInfo_Name(a)) {
fpGlobals->Add(new TGlobal(a));
} else
gCint->DataMemberInfo_Delete(a);
last++;
}
gCint->DataMemberInfo_Delete(t);
}
return fpGlobals;
}
const TSeqCollection *TTabCom::GetListOfGlobalFunctions()
{
if (!fpGlobalFuncs) {
fpGlobalFuncs = new TContainer;
MethodInfo_t *a;
int last = 0;
int nglob = 0;
MethodInfo_t *t = gCint->MethodInfo_Factory();
while (gCint->MethodInfo_Next(t))
nglob++;
for (int i = 0; i < nglob; i++) {
a = gCint->MethodInfo_Factory();
gCint->MethodInfo_Next(a);
for (int j = 0; j < last; j++)
gCint->MethodInfo_Next(a);
if (gCint->MethodInfo_IsValid(a) && gCint->MethodInfo_Name(a)) {
fpGlobalFuncs->Add(new TFunction(a));
} else
gCint->MethodInfo_Delete(a);
last++;
}
gCint->MethodInfo_Delete(t);
}
return fpGlobalFuncs;
}
const TSeqCollection *TTabCom::GetListOfPragmas()
{
if (!fpPragmas) {
fpPragmas = new TContainer;
fpPragmas->Add(new TObjString("ANSI "));
fpPragmas->Add(new TObjString("autocompile "));
fpPragmas->Add(new TObjString("bytecode "));
fpPragmas->Add(new TObjString("compile "));
fpPragmas->Add(new TObjString("endbytecode "));
fpPragmas->Add(new TObjString("endcompile "));
fpPragmas->Add(new TObjString("include "));
fpPragmas->Add(new TObjString("includepath "));
fpPragmas->Add(new TObjString("K&R "));
fpPragmas->Add(new TObjString("link "));
fpPragmas->Add(new TObjString("preprocess "));
fpPragmas->Add(new TObjString("preprocessor "));
fpPragmas->Add(new TObjString("security level"));
}
return fpPragmas;
}
const TSeqCollection *TTabCom::GetListOfSysIncFiles()
{
if (!fpSysIncFiles) {
fpSysIncFiles = NewListOfFilesInPath(GetSysIncludePath());
}
return fpSysIncFiles;
}
const TSeqCollection *TTabCom::GetListOfUsers()
{
if (!fpUsers) {
fpUsers = new TContainer;
ifstream passwd;
TString user;
passwd.open("/etc/passwd");
while (passwd) {
user.ReadToDelim(passwd, ':');
fpUsers->Add(new TObjString(user));
passwd.ignore(32000, '\n');
}
passwd.close();
}
return fpUsers;
}
Char_t TTabCom::AllAgreeOnChar(int i, const TSeqCollection * pList,
Int_t & nGoodStrings)
{
assert(pList != 0);
TIter next(pList);
TObject *pObj;
const char *s = "";
char ch0;
Bool_t isGood;
Bool_t atLeast1GoodString;
nGoodStrings = 0;
atLeast1GoodString = kFALSE;
do {
if ((pObj = next())) {
s = pObj->GetName();
isGood = !ExcludedByFignore(s);
if (isGood) {
atLeast1GoodString = kTRUE;
nGoodStrings += 1;
}
} else {
next.Reset();
pObj = next();
if (pObj) s = pObj->GetName();
break;
}
}
while (!isGood);
ch0 = s[i];
do {
if ((pObj = next())) {
s = pObj->GetName();
isGood = !ExcludedByFignore(s);
if (isGood)
nGoodStrings += 1;
} else
return ch0;
}
while (((int) strlen(s) >= i && s[i] == ch0) ||
(atLeast1GoodString && !isGood));
return 0;
}
void TTabCom::AppendListOfFilesInDirectory(const char dirName[],
TSeqCollection * pList)
{
assert(dirName != 0);
assert(pList != 0);
void *dir = gSystem->OpenDirectory(dirName);
if (!dir)
return;
const char *tmp_ptr;
TString fileName;
while ((tmp_ptr = gSystem->GetDirEntry(dir))) {
fileName = tmp_ptr;
if (fileName == "." || fileName == "..")
continue;
pList->Add(new TObjString(dirName + fileName.Prepend("/")));
}
gSystem->FreeDirectory(dir);
}
TString TTabCom::DetermineClass(const char varName[])
{
assert(varName != 0);
IfDebug(cerr << "DetermineClass(\"" << varName << "\");" << endl);
const char *tmpfile = tmpnam(0);
TString cmd("gROOT->ProcessLine(\"");
cmd += varName;
cmd += "\"); > ";
cmd += tmpfile;
cmd += "\n";
gROOT->ProcessLineSync(cmd.Data());
TString type = "";
int c;
ifstream file1(tmpfile);
if (!file1) {
Error("TTabCom::DetermineClass", "could not open file \"%s\"",
tmpfile);
goto cleanup;
}
c = file1.get();
if (!file1 || c <= 0 || c == '*' || c != '(') {
Error("TTabCom::DetermineClass", "variable \"%s\" not defined?",
varName);
goto cleanup;
}
IfDebug(cerr << (char) c << flush);
file1 >> type;
if (type == "const")
file1 >> type;
if (type != "class" && type != "struct") {
type = "";
goto cleanup;
}
c = file1.get();
IfDebug(cerr << (char) c << flush);
type.ReadToDelim(file1, ')');
IfDebug(cerr << type << endl);
if (type.EndsWith("const"))
type.Remove(type.Length() - 5);
cleanup:
file1.close();
gSystem->Unlink(tmpfile);
return type;
}
Bool_t TTabCom::ExcludedByFignore(TString s)
{
const char *fignore = gEnv->GetValue("TabCom.FileIgnore", (char *) 0);
if (!fignore) {
return kFALSE;
} else {
#ifdef R__SSTREAM
istringstream endings((char *) fignore);
#else
istrstream endings((char *) fignore);
#endif
TString ending;
ending.ReadToDelim(endings, kDelim);
while (!ending.IsNull()) {
if (s.EndsWith(ending))
return kTRUE;
else
ending.ReadToDelim(endings, kDelim);
}
return kFALSE;
}
}
TString TTabCom::GetSysIncludePath()
{
const char *tmpfilename = tmpnam(0);
FILE *fout = fopen(tmpfilename, "w");
if (!fout) return "";
gCint->DisplayIncludePath(fout);
fclose(fout);
ifstream file1(tmpfilename);
if (!file1) {
Error("TTabCom::GetSysIncludePath", "could not open file \"%s\"",
tmpfilename);
gSystem->Unlink(tmpfilename);
return "";
}
TString token;
TString path;
file1 >> token;
file1 >> token;
while (file1) {
file1 >> token;
if (!token.IsNull()) {
if (path.Length() > 0)
path.Append(":");
path.Append(token.Data() + 2);
}
}
file1.close();
gSystem->Unlink(tmpfilename);
#ifndef CINTINCDIR
TString sCINTSYSDIR("$ROOTSYS/cint");
#else
TString sCINTSYSDIR(CINTINCDIR);
#endif
path.Append(":" + sCINTSYSDIR + "/include");
path.Append(":/usr/include");
return path;
}
Bool_t TTabCom::IsDirectory(const char fileName[])
{
FileStat_t stat;
gSystem->GetPathInfo(fileName, stat);
return R_ISDIR(stat.fMode);
}
TSeqCollection *TTabCom::NewListOfFilesInPath(const char path1[])
{
assert(path1 != 0);
if (!path1[0]) path1 = ".";
TContainer *pList = new TContainer;
#ifdef R__SSTREAM
istringstream path((char *) path1);
#else
istrstream path((char *) path1);
#endif
while (path.good())
{
TString dirName;
dirName.ReadToDelim(path, kDelim);
if (dirName.IsNull())
continue;
IfDebug(cerr << "NewListOfFilesInPath(): dirName = " << dirName <<
endl);
AppendListOfFilesInDirectory(dirName, pList);
}
return pList;
}
Bool_t TTabCom::PathIsSpecifiedInFileName(const TString & fileName)
{
char c1 = (fileName.Length() > 0) ? fileName[0] : 0;
return c1 == '/' || c1 == '~' || c1 == '$' || fileName.BeginsWith("./")
|| fileName.BeginsWith("../");
}
void TTabCom::NoMsg(Int_t errorLevel)
{
const Int_t kNotDefined = -2;
static Int_t old_level = kNotDefined;
if (errorLevel < 0)
{
if (old_level == kNotDefined) {
cerr << "NoMsg(): ERROR 1. old_level==" << old_level << endl;
return;
}
gErrorIgnoreLevel = old_level;
old_level = kNotDefined;
} else
{
if (old_level != kNotDefined) {
cerr << "NoMsg(): ERROR 2. old_level==" << old_level << endl;
return;
}
old_level = gErrorIgnoreLevel;
if (gErrorIgnoreLevel <= errorLevel)
gErrorIgnoreLevel = errorLevel + 1;
}
}
Int_t TTabCom::Complete(const TRegexp & re,
const TSeqCollection * pListOfCandidates,
const char appendage[],
ostream& out,
TString::ECaseCompare cmp)
{
IfDebug(cerr << "TTabCom::Complete() ..." << endl);
assert(fpLoc != 0);
assert(pListOfCandidates != 0);
Int_t pos = 0;
const int loc = *fpLoc;
TString s1(fBuf);
TString s2 = s1(0, loc);
TString s3 = s2(re);
int start = s2.Index(re);
IfDebug(cerr << " s1: " << s1 << endl);
IfDebug(cerr << " s2: " << s2 << endl);
IfDebug(cerr << " s3: " << s3 << endl);
IfDebug(cerr << "start: " << start << endl);
IfDebug(cerr << endl);
TList listOfMatches;
TList listOfFullPaths;
listOfMatches.SetOwner();
listOfFullPaths.SetOwner();
int nMatches = 0;
TObject *pObj;
TIter next_candidate(pListOfCandidates);
TIter next_match(&listOfMatches);
TIter next_fullpath(&listOfFullPaths);
while ((pObj = next_candidate())) {
const char *s4 = pObj->GetName();
assert(s4 != 0);
const char *s5 = strrchr(s4, '/');
if (!s5)
s5 = s4;
else
s5 += 1;
if ((cmp == TString::kExact) && (strstr(s5, s3) == s5)) {
nMatches += 1;
listOfMatches.Add(new TObjString(s5));
listOfFullPaths.Add(new TObjString(s4));
IfDebug(cerr << "adding " << s5 << '\t' << s4 << endl);
} else if (cmp == TString::kIgnoreCase) {
TString ts5(s5);
if (ts5.BeginsWith(s3, cmp))
{
nMatches += 1;
listOfMatches.Add(new TObjString(s5));
listOfFullPaths.Add(new TObjString(s4));
IfDebug(cerr << "adding " << s5 << '\t' << s4 << endl);
}
} else {
}
}
TString partialMatch = "";
if (nMatches == 0) {
gSystem->Beep();
pos = -1;
goto done;
}
char match[1024];
if (nMatches == 1) {
const char *short_name = next_match()->GetName();
const char *full_name = next_fullpath()->GetName();
pObj = pListOfCandidates->FindObject(short_name);
if (pObj) {
IfDebug(cerr << endl << "class: " << pObj->ClassName() << endl);
TString className = pObj->ClassName();
if (0);
else if (className == "TMethod" || className == "TFunction") {
TFunction *pFunc = (TFunction *) pObj;
if (0 == pFunc->GetNargs())
appendage = "()";
else
appendage = "(";
} else if (className == "TDataMember") {
appendage = " ";
}
}
CopyMatch(match, short_name, appendage, full_name);
} else {
Char_t ch;
Int_t nGoodStrings;
for (int i = 0;
(ch = AllAgreeOnChar(i, &listOfMatches, nGoodStrings));
i += 1) {
IfDebug(cerr << " i=" << i << " ch=" << ch << endl);
partialMatch.Append(ch);
}
const char *s;
const char *s0;
if (nGoodStrings == 1) {
do {
s = next_match()->GetName();
s0 = next_fullpath()->GetName();
}
while (ExcludedByFignore(s));
CopyMatch(match, s, appendage, s0);
} else {
IfDebug(cerr << "more than 1 GoodString" << endl);
if (partialMatch.Length() > s3.Length())
{
CopyMatch(match, partialMatch.Data());
} else
{
IfDebug(cerr << "printing ambiguous matches" << endl);
out << endl;
while ((pObj = next_match())) {
s = pObj->GetName();
s0 = next_fullpath()->GetName();
if (!ExcludedByFignore(s) || nGoodStrings == 0) {
if (IsDirectory(s0))
out << s << "/" << endl;
else
out << s << endl;
}
}
pos = -2;
if (cmp == TString::kExact || partialMatch.Length() < s3.Length()) {
goto done;
}
CopyMatch(match, partialMatch.Data());
}
}
}
{
int i = strlen(fBuf);
int l = strlen(match) - (loc - start);
if (strlen(fBuf) + strlen(match) + 1 > BUF_SIZE) {
Error("TTabCom::Complete", "buffer overflow");
pos = -2;
goto done;
}
IfDebug(cerr << " i=" << i << endl);
IfDebug(cerr << " L=" << l << endl);
IfDebug(cerr << "loc=" << loc << endl);
for (; i >= loc; i -= 1) {
fBuf[i + l] = fBuf[i];
}
strncpy(fBuf + start, match, strlen(match));
if (pos != -2) {
pos = loc;
if (cmp == TString::kIgnoreCase && pos < 0) {
pos = start;
}
}
*fpLoc = loc + l;
}
done:
fpLoc = 0;
fBuf = 0;
return pos;
}
void TTabCom::CopyMatch(char dest[], const char localName[],
const char appendage[],
const char fullName[]) const
{
assert(dest != 0);
assert(localName != 0);
strcpy(dest, localName);
const char *key = "filename";
const int key_len = strlen(key);
IfDebug(cerr << "CopyMatch()." << endl);
IfDebug(cerr << "localName: " << (localName ? localName : "0") <<
endl);
IfDebug(cerr << "appendage: " << (appendage ? appendage : "0") <<
endl);
IfDebug(cerr << " fullName: " << (fullName ? fullName : "0") <<
endl);
if (appendage && strncmp(appendage, key, key_len) == 0) {
appendage += key_len;
IfDebug(cerr << "new appendage: " << appendage << endl);
if (IsDirectory(fullName)) {
if (fullName)
strcpy(dest + strlen(localName), "/");
} else {
if (appendage)
strcpy(dest + strlen(localName), appendage);
}
} else {
if (appendage)
strcpy(dest + strlen(localName), appendage);
}
}
TTabCom::EContext_t TTabCom::DetermineContext() const
{
assert(fBuf != 0);
const char *pStart;
const char *pEnd;
for (int context = 0; context < kNUM_PAT; ++context) {
pEnd = Matchs(fBuf, *fpLoc, fPat[context], &pStart);
if (pEnd) {
IfDebug(cerr << endl
<< "context=" << context << " "
<< "RegExp=" << fRegExp[context]
<< endl);
return EContext_t(context);
}
}
return kUNKNOWN_CONTEXT;
}
TString TTabCom::DeterminePath(const TString & fileName,
const char defaultPath[]) const
{
if (PathIsSpecifiedInFileName(fileName)) {
TString path = fileName;
gSystem->ExpandPathName(path);
Int_t end = path.Length()-1;
if (end>0 && path[end]!='/' && path[end]!='\\') {
path = gSystem->DirName(path);
}
return path;
} else {
TString newBase;
TString extendedPath;
if (fileName.Contains("/")) {
Int_t end = fileName.Length()-1;
if (fileName[end] != '/' && fileName[end] != '\\') {
newBase = gSystem->DirName(fileName);
} else {
newBase = fileName;
}
extendedPath = ExtendPath(defaultPath, newBase);
} else {
newBase = "";
extendedPath = defaultPath;
}
IfDebug(cerr << endl);
IfDebug(cerr << " fileName: " << fileName << endl);
IfDebug(cerr << " pathBase: " << newBase << endl);
if (defaultPath) {
IfDebug(cerr << " defaultPath: " << defaultPath << endl);
} else {
IfDebug(cerr << " defaultPath: " << endl);
}
IfDebug(cerr << "extendedPath: " << extendedPath << endl);
IfDebug(cerr << endl);
return extendedPath;
}
}
TString TTabCom::ExtendPath(const char originalPath[], TString newBase) const
{
if (newBase.BeginsWith("/"))
newBase.Remove(TString::kLeading, '/');
#ifdef R__SSTREAM
stringstream str;
#else
strstream str;
#endif
TString dir;
TString newPath;
if (originalPath) str << originalPath;
while (str.good())
{
dir = "";
dir.ReadToDelim(str, kDelim);
if (dir.IsNull())
continue;
newPath.Append(dir);
if (!newPath.EndsWith("/"))
newPath.Append("/");
newPath.Append(newBase);
newPath.Append(kDelim);
}
return newPath.Strip(TString::kTrailing, kDelim);
}
Int_t TTabCom::Hook(char *buf, int *pLoc, ostream& out)
{
fBuf = buf;
fpLoc = pLoc;
fLastIter = 0;
Int_t pos = -2;
EContext_t context = DetermineContext();
const char dummy[] = ".";
TRegexp re1(context == kUNKNOWN_CONTEXT ? dummy : fRegExp[context]);
TString s1(fBuf);
TString s2 = s1(0, *fpLoc);
TString s3 = s2(re1);
switch (context) {
case kUNKNOWN_CONTEXT:
cerr << endl << "tab completion not implemented for this context" <<
endl;
pos = -2;
break;
case kSYS_UserName:
{
const TSeqCollection *pListOfUsers = GetListOfUsers();
pos = Complete("[^~]*$", pListOfUsers, "/", out);
}
break;
case kSYS_EnvVar:
{
const TSeqCollection *pEnv = GetListOfEnvVars();
pos = Complete("[^$]*$", pEnv, "", out);
}
break;
case kCINT_stdout:
case kCINT_stderr:
case kCINT_stdin:
{
const TString fileName = s3("[^ ><]*$");
const TString filePath = DeterminePath(fileName,0);
const TSeqCollection *pListOfFiles =
GetListOfFilesInPath(filePath.Data());
pos = Complete("[^ /]*$", pListOfFiles, "filename ", out);
}
break;
case kCINT_Edit:
case kCINT_Load:
case kCINT_Exec:
case kCINT_EXec:
{
const TString fileName = s3("[^ ]*$");
const TString macroPath =
DeterminePath(fileName, TROOT::GetMacroPath());
const TSeqCollection *pListOfFiles =
GetListOfFilesInPath(macroPath.Data());
pos = Complete("[^ /]*$", pListOfFiles, "filename ", out);
}
break;
case kCINT_pragma:
{
pos = Complete("[^ ]*$", GetListOfPragmas(), "", out);
}
break;
case kCINT_includeSYS:
{
TString fileName = s3("[^<]*$");
if (PathIsSpecifiedInFileName(fileName) || fileName.Contains("/")) {
TString includePath =
DeterminePath(fileName, GetSysIncludePath());
pos =
Complete("[^</]*$", GetListOfFilesInPath(includePath),
"filename> ", out);
} else {
pos =
Complete("[^</]*$", GetListOfSysIncFiles(), "filename> ", out);
}
}
break;
case kCINT_includePWD:
{
const TString fileName = s3("[^\"]*$");
const TString includePath = DeterminePath(fileName, ".");
const TSeqCollection *pListOfFiles =
GetListOfFilesInPath(includePath.Data());
pos = Complete("[^\"/]*$", pListOfFiles, "filename\" ", out);
}
break;
case kCINT_cpp:
{
pos = Complete("[^# ]*$", GetListOfCppDirectives(), " ", out);
}
break;
case kROOT_Load:
{
const TString fileName = s3("[^\"]*$");
const TString dynamicPath = DeterminePath(fileName,gEnv->GetValue("Root.DynamicPath",(char *) 0));
const TSeqCollection *pListOfFiles = GetListOfFilesInPath(dynamicPath);
pos = Complete("[^\"/]*$", pListOfFiles, "filename\");", out);
}
break;
case kSYS_FileName:
{
const TString fileName = s3("[^ \"]*$");
const TString filePath = DeterminePath(fileName,".");
const TSeqCollection *pListOfFiles = GetListOfFilesInPath(filePath.Data());
pos = Complete("[^\" /]*$", pListOfFiles, "filename\"", out);
}
break;
case kCXX_ScopeMember:
{
const EContext_t original_context = context;
TClass *pClass;
TString name = s3("^[_a-zA-Z][_a-zA-Z0-9]*");
IfDebug(cerr << endl);
IfDebug(cerr << "name: " << '"' << name << '"' << endl);
TString partname = s3("[_a-zA-Z][_a-zA-Z0-9]*$");
TString prefix = "";
TString str = s2;
str.Remove(str.Length() - partname.Length(), partname.Length());
while (1) {
TString sym = str("[_a-zA-Z][_a-zA-Z0-9]*::$");
if (sym.Length() == 0)
break;
str.Remove(str.Length() - sym.Length(), sym.Length());
prefix = sym + prefix;
}
TString preprefix = prefix;
TString sym = prefix("[_a-zA-Z][_a-zA-Z0-9]*::$");
preprefix.Remove(preprefix.Length() - sym.Length(), sym.Length());
IfDebug(cerr << "prefix: " << '"' << prefix << '"' << endl);
IfDebug(cerr << "preprefix: " << '"' << preprefix << '"' << endl);
TString namesp = prefix;
if (namesp.Length() >= 2)
namesp.Remove(namesp.Length() - 2, 2);
IfDebug(cerr << "namesp: " << '"' << namesp << '"' << endl);
delete TryMakeClassFromClassName(namesp);
if (!fpNamespaces)
RehashClasses();
TObjString objstr(namesp);
TObjString *foundstr = 0;
if (fpNamespaces)
foundstr = (TObjString *)fpNamespaces->FindObject(&objstr);
if (foundstr) {
TContainer *pList = new TContainer;
const TSeqCollection *tmp = GetListOfClasses();
if (!tmp) break;
Int_t i;
for (i = 0; i < tmp->GetSize(); i++) {
TString astr = ((TObjString *) tmp->At(i))->String();
TString rxp = "^";
rxp += prefix;
if (astr.Contains(TRegexp(rxp))) {
astr.Remove(0, prefix.Length());
TString s = astr("^[^: ]*");
TObjString *ostr = new TObjString(s);
if (!pList->Contains(ostr))
pList->Add(ostr);
else
delete ostr;
}
}
for (i = 0; i < fpNamespaces->GetSize(); i++) {
TString astr =
((TObjString *) fpNamespaces->At(i))->String();
TString rxp = "^";
rxp += prefix;
if (astr.Contains(TRegexp(rxp))) {
astr.Remove(0, prefix.Length());
TString s = astr("^[^: ]*");
TObjString *ostr = new TObjString(s);
if (!pList->Contains(ostr))
pList->Add(ostr);
else
delete ostr;
}
}
pClass = TryMakeClassFromClassName(preprefix + name);
if (pClass) {
pList->AddAll(pClass->GetListOfAllPublicMethods());
pList->AddAll(pClass->GetListOfAllPublicDataMembers());
}
pos = Complete("[^: ]*$", pList, "", out);
delete pList;
if (pClass)
delete pClass;
} else {
pClass = MakeClassFromClassName(preprefix + name);
if (!pClass) {
pos = -2;
break;
}
TContainer *pList = new TContainer;
pList->AddAll(pClass->GetListOfAllPublicMethods());
pList->AddAll(pClass->GetListOfAllPublicDataMembers());
pos = Complete("[^: ]*$", pList, "(", out);
delete pList;
delete pClass;
}
if (context != original_context)
pos = -2;
}
break;
case kCXX_DirectMember:
case kCXX_IndirectMember:
{
const EContext_t original_context = context;
TClass *pClass;
TString name = s1("[_a-zA-Z][-_a-zA-Z0-9<>():.]*$");
IfDebug(cerr << endl);
IfDebug(cerr << "name: " << '"' << name << '"' << endl);
switch (context) {
case kCXX_DirectMember:
pClass = MakeClassFromVarName(name, context);
break;
case kCXX_IndirectMember:
pClass = MakeClassFromVarName(name, context);
break;
default:
assert(0);
break;
}
if (!pClass) {
pos = -2;
break;
}
TContainer *pList = new TContainer;
pList->AddAll(pClass->GetListOfAllPublicMethods());
pList->AddAll(pClass->GetListOfAllPublicDataMembers());
switch (context) {
case kCXX_DirectMember:
{
int* store_fpLoc = fpLoc;
char* store_fBuf = fBuf;
pos = Complete("[^. ]*$", pList, "(", out);
if (pos == -1) {
fpLoc = store_fpLoc;
fBuf = store_fBuf;
pos = Complete("[^. ]*$", pList, "(", out, TString::kIgnoreCase);
}
break;
}
case kCXX_IndirectMember:
pos = Complete("[^> ]*$", pList, "(", out);
break;
default:
assert(0);
break;
}
delete pList;
delete pClass;
if (context != original_context)
pos = -2;
}
break;
case kCXX_ScopeProto:
{
const EContext_t original_context = context;
TClass *pClass;
TString name = s3("^[_a-zA-Z][_a-zA-Z0-9]*");
IfDebug(cerr << endl);
IfDebug(cerr << "name: " << '"' << name << '"' << endl);
TString partname = s3("[_a-zA-Z][_a-zA-Z0-9]* *($");
TString prefix = "";
TString str = s2;
str.Remove(str.Length() - partname.Length(), partname.Length());
while (1) {
TString sym = str("[_a-zA-Z][_a-zA-Z0-9]*::$");
if (sym.Length() == 0)
break;
str.Remove(str.Length() - sym.Length(), sym.Length());
prefix = sym + prefix;
}
TString preprefix = prefix;
TString sym = prefix("[_a-zA-Z][_a-zA-Z0-9]*::$");
preprefix.Remove(preprefix.Length() - sym.Length(), sym.Length());
IfDebug(cerr << "prefix: " << '"' << prefix << '"' << endl);
IfDebug(cerr << "preprefix: " << '"' << preprefix << '"' << endl);
pClass = MakeClassFromClassName(preprefix + name);
if (!pClass) {
pos = -2;
break;
}
TString methodName;
methodName = s3("[^:>\\.(]*($");
methodName.Chop();
methodName.Remove(TString::kTrailing, ' ');
IfDebug(cerr << methodName << endl);
TContainer *pList = new TContainer;
pList->AddAll(pClass->GetListOfAllPublicMethods());
Bool_t foundOne = kFALSE;
TIter nextMethod(pList);
TMethod *pMethod;
while ((pMethod = (TMethod *) nextMethod())) {
if (methodName == pMethod->GetName()) {
foundOne = kTRUE;
out << endl << pMethod->GetReturnTypeName()
<< " " << pMethod->GetName()
<< pMethod->GetSignature();
const char *comment = pMethod->GetCommentString();
if (comment && comment[0] != '\0') {
out << " \t// " << comment;
}
}
}
if (foundOne) {
out << endl;
pos = -2;
} else {
gSystem->Beep();
pos = -1;
}
delete pList;
delete pClass;
if (context != original_context)
pos = -2;
}
break;
case kCXX_DirectProto:
case kCXX_IndirectProto:
case kCXX_NewProto:
case kCXX_ConstructorProto:
{
const EContext_t original_context = context;
TClass *pClass;
TString name;
if (context == kCXX_NewProto) {
name = s3("[_a-zA-Z][_a-zA-Z0-9:]* *($", 3);
name.Chop();
name.Remove(TString::kTrailing, ' ');
} else {
name = s3("^[_a-zA-Z][_a-zA-Z0-9:]*");
}
IfDebug(cerr << endl);
IfDebug(cerr << "name: " << '"' << name << '"' << endl);
TString namerec = s1;
switch (context) {
case kCXX_ScopeProto:
pClass = MakeClassFromClassName(name);
break;
case kCXX_DirectProto:
pClass = MakeClassFromVarName(namerec, context);
break;
case kCXX_IndirectProto:
pClass = MakeClassFromVarName(namerec, context);
break;
case kCXX_NewProto:
pClass = MakeClassFromClassName(name);
break;
case kCXX_ConstructorProto:
pClass = MakeClassFromClassName(name);
break;
default:
assert(0);
break;
}
if (!pClass) {
pos = -2;
break;
}
TString methodName;
if (context == kCXX_ConstructorProto || context == kCXX_NewProto) {
methodName = name("[_a-zA-Z][_a-zA-Z0-9]*$");
} else {
methodName = s3("[^:>\\.(]*($");
methodName.Chop();
methodName.Remove(TString::kTrailing, ' ');
}
IfDebug(cerr << methodName << endl);
TContainer *pList = new TContainer;
pList->AddAll(pClass->GetListOfAllPublicMethods());
Bool_t foundOne = kFALSE;
TIter nextMethod(pList);
TMethod *pMethod;
while ((pMethod = (TMethod *) nextMethod())) {
if (methodName == pMethod->GetName()) {
foundOne = kTRUE;
out << endl << pMethod->GetReturnTypeName()
<< " " << pMethod->GetName()
<< pMethod->GetSignature();
const char *comment = pMethod->GetCommentString();
if (comment && comment[0] != '\0') {
out << " \t// " << comment;
}
}
}
if (foundOne) {
out << endl;
pos = -2;
} else {
gSystem->Beep();
pos = -1;
}
delete pList;
delete pClass;
if (context != original_context)
pos = -2;
}
break;
case kCXX_Global:
{
int l2 = s2.Length(), l3 = s3.Length();
if (l2 > l3 && s2[l2 - l3 - 1] == '.') {
cerr << endl <<
"tab completion not implemented for this context" << endl;
break;
}
if (l2 > l3 + 1 && s2(l2 - l3 - 2, 2) == "->") {
cerr << endl <<
"tab completion not implemented for this context" << endl;
break;
}
TContainer *pList = new TContainer;
const TSeqCollection *pL2 = GetListOfClasses();
if (pL2) pList->AddAll(pL2);
if (fpNamespaces) pList->AddAll(fpNamespaces);
const TSeqCollection *pC1 = GetListOfGlobals();
if (pC1) pList->AddAll(pC1);
const TSeqCollection *pC3 = GetListOfGlobalFunctions();
if (pC3) pList->AddAll(pC3);
pos = Complete("[_a-zA-Z][_a-zA-Z0-9]*$", pList, "", out);
delete pList;
}
break;
case kCXX_GlobalProto:
{
TString functionName = s3("[_a-zA-Z][_a-zA-Z0-9]*");
IfDebug(cerr << functionName << endl);
TContainer listOfMatchingGlobalFuncs;
TIter nextGlobalFunc(GetListOfGlobalFunctions());
TObject *pObj;
while ((pObj = nextGlobalFunc())) {
if (strcmp(pObj->GetName(), functionName) == 0) {
listOfMatchingGlobalFuncs.Add(pObj);
}
}
if (listOfMatchingGlobalFuncs.IsEmpty()) {
cerr << endl << "no such function: " << dblquote(functionName)
<< endl;
} else {
out << endl;
TIter next(&listOfMatchingGlobalFuncs);
TFunction *pFunction;
while ((pFunction = (TFunction *) next())) {
out << pFunction->GetReturnTypeName()
<< " " << pFunction->GetName()
<< pFunction->GetSignature()
<< endl;
}
}
pos = -2;
}
break;
default:
assert(0);
break;
}
return pos;
}
void TTabCom::InitPatterns()
{
SetPattern(kSYS_UserName, "~[_a-zA-Z0-9]*$");
SetPattern(kSYS_EnvVar, "$[_a-zA-Z0-9]*$");
SetPattern(kCINT_stdout, "; *>>?.*$");
SetPattern(kCINT_stderr, "; *2>>?.*$");
SetPattern(kCINT_stdin, "; *<.*$");
SetPattern(kCINT_Edit, "^ *\\.E .*$");
SetPattern(kCINT_Load, "^ *\\.L .*$");
SetPattern(kCINT_Exec, "^ *\\.x +[-0-9_a-zA-Z~$./]*$");
SetPattern(kCINT_EXec, "^ *\\.X +[-0-9_a-zA-Z~$./]*$");
SetPattern(kCINT_pragma, "^# *pragma +[_a-zA-Z0-9]*$");
SetPattern(kCINT_includeSYS, "^# *include *<[^>]*$");
SetPattern(kCINT_includePWD, "^# *include *\"[^\"]*$");
SetPattern(kCINT_cpp, "^# *[_a-zA-Z0-9]*$");
SetPattern(kROOT_Load, "gSystem *-> *Load *( *\"[^\"]*$");
SetPattern(kCXX_NewProto, "new +[_a-zA-Z][_a-zA-Z0-9:]* *($");
SetPattern(kCXX_ConstructorProto,
"[_a-zA-Z][_a-zA-Z0-9:]* +[_a-zA-Z][_a-zA-Z0-9]* *($");
SetPattern(kCXX_ScopeProto,
"[_a-zA-Z][_a-zA-Z0-9]* *:: *[_a-zA-Z0-9]* *($");
SetPattern(kCXX_DirectProto,
"[_a-zA-Z][_a-zA-Z0-9()]* *\\. *[_a-zA-Z0-9]* *($");
SetPattern(kCXX_IndirectProto,
"[_a-zA-Z][_a-zA-Z0-9()]* *-> *[_a-zA-Z0-9]* *($");
SetPattern(kCXX_ScopeMember,
"[_a-zA-Z][_a-zA-Z0-9]* *:: *[_a-zA-Z0-9]*$");
SetPattern(kCXX_DirectMember,
"[_a-zA-Z][_a-zA-Z0-9()]* *\\. *[_a-zA-Z0-9()]*$");
SetPattern(kCXX_IndirectMember,
"[_a-zA-Z][_a-zA-Z0-9()]* *-> *[_a-zA-Z0-9()]*$");
SetPattern(kSYS_FileName, "\"[-0-9_a-zA-Z~$./]*$");
SetPattern(kCXX_Global, "[_a-zA-Z][_a-zA-Z0-9]*$");
SetPattern(kCXX_GlobalProto, "[_a-zA-Z][_a-zA-Z0-9]* *($");
}
TClass *TTabCom::MakeClassFromClassName(const char className[]) const
{
NoMsg(kWarning);
TClass *pClass = new TClass(className);
NoMsg(-1);
if (pClass->GetListOfAllPublicMethods()->GetSize() == 0 &&
pClass->GetListOfAllPublicDataMembers()->GetSize() == 0) {
cerr << endl << "class " << dblquote(className) << " not defined." <<
endl;
return 0;
}
return pClass;
}
TClass *TTabCom::TryMakeClassFromClassName(const char className[]) const
{
NoMsg(kWarning);
TClass *pClass = new TClass(className);
NoMsg(-1);
if (pClass->GetListOfAllPublicMethods()->GetSize() == 0 &&
pClass->GetListOfAllPublicDataMembers()->GetSize() == 0) {
return 0;
}
return pClass;
}
TClass *TTabCom::MakeClassFromVarName(const char varName[],
EContext_t & context, int iter)
{
Bool_t varName_exists = GetListOfGlobals()->Contains(varName) ||
(gROOT->FindObject(varName) != 0);
if (0) printf("varName is [%s] with iteration [%i]\n", varName, iter);
Int_t cut = ParseReverse(varName, strlen(varName));
if (!varName_exists && cut != 0)
{
TString parentName = varName;
TString memberName = varName;
if (iter > fLastIter) fLastIter = iter;
parentName[cut] = 0;
if (0) printf("Parent string is [%s]\n", parentName.Data());
if (cut>2) {
UInt_t level = 0;
for(Int_t i = cut-1; i>=0; --i) {
switch (parentName[i]) {
case '(':
if (level) --level;
else {
parentName = parentName(i+1,cut-i-1);
i = 0;
}
break;
case ')':
++level; break;
}
}
}
TClass *pclass;
if (varName[cut] == '.') {
memberName = varName+cut+1;
if (0) printf("Member/method is [%s]\n", memberName.Data());
EContext_t subcontext = kCXX_DirectMember;
pclass = MakeClassFromVarName(parentName.Data(), subcontext, iter+1);
} else {
memberName = varName+cut+2;
if (0) printf("Member/method is [%s]\n", memberName.Data());
EContext_t subcontext = kCXX_IndirectMember;
pclass = MakeClassFromVarName(parentName.Data(), subcontext, iter+1);
}
if (0) printf("I got [%s] from MakeClassFromVarName()\n", pclass->GetName());
if (pclass)
{
if (0) printf("Variable [%s] exists!\n", parentName.Data());
if (iter == 0) return pclass;
if (0) printf("Trying data member [%s] of class [%s] ...\n",
memberName.Data(), pclass->GetName());
TDataMember *dmptr = 0;
TList *dlist = pclass->GetListOfDataMembers();
TIter next(pclass->GetListOfAllPublicDataMembers());
while ((dmptr = (TDataMember *) next())) {
if (memberName == dmptr->GetName()) break;
}
delete dlist;
if (dmptr)
{
if (0) printf("It's a member!\n");
TString returnName = dmptr->GetTypeName();
TClass *mclass = new TClass(returnName.Data());
return mclass;
}
char *parentesis_ptr = (char*)strrchr(memberName.Data(), '(');
if (parentesis_ptr) *parentesis_ptr = 0;
if (0) printf("Trying method [%s] of class [%s] ...\n",
memberName.Data(), pclass->GetName());
TMethod *mptr = 0;
TList *mlist = pclass->GetListOfAllPublicMethods();
next = mlist;
while ((mptr = (TMethod *) next())) {
if (strcmp(memberName.Data(),mptr->GetName())==0) break;
}
delete mlist;
if (mptr)
{
TString returnName = mptr->GetReturnTypeName();
if (0) printf("It's a method called [%s] with return type [%s]\n",
memberName.Data(), returnName.Data());
if (returnName[returnName.Length()-1] == '*')
{
returnName[returnName.Length()-1] = 0;
fVarIsPointer = kTRUE;
}
else
{
fVarIsPointer = kFALSE;
}
TClass *mclass = new TClass(returnName.Data());
return mclass;
}
}
}
if (!varName_exists) {
cerr << endl << "variable " << dblquote(varName) << " not defined."
<< endl;
return 0;
}
TString className = DetermineClass(varName);
if (className.IsNull() || className == "*") {
cerr << endl << "problem determining class of " << dblquote(varName)
<< endl;
return 0;
}
fVarIsPointer = className[className.Length() - 1] == '*';
if (fVarIsPointer)
className[className.Length()-1] = 0;
if (fVarIsPointer &&
(context == kCXX_DirectMember || context == kCXX_DirectProto)) {
switch (context) {
case kCXX_DirectMember:
context = kCXX_IndirectMember;
break;
case kCXX_DirectProto:
context = kCXX_IndirectProto;
break;
default:
assert(0);
break;
}
int i;
for (i = *fpLoc; fBuf[i] != '.'; i -= 1) {
}
int loc = i;
for (i = strlen(fBuf); i >= loc; i -= 1) {
fBuf[i + 1] = fBuf[i];
}
fBuf[loc] = '-';
fBuf[loc + 1] = '>';
*fpLoc += 1;
cerr << endl << dblquote(varName) <<
" is of pointer type. Use this operator: ->" << endl;
}
if (context == kCXX_IndirectMember || context == kCXX_IndirectProto) {
if (fVarIsPointer) {
className.Chop();
if (className[className.Length() - 1] == '*') {
cerr << endl << "can't handle pointers to pointers." << endl;
return 0;
}
} else {
switch (context) {
case kCXX_IndirectMember:
context = kCXX_DirectMember;
break;
case kCXX_IndirectProto:
context = kCXX_DirectProto;
break;
default:
assert(0);
break;
}
int i;
for (i = *fpLoc; fBuf[i - 1] != '-' && fBuf[i] != '>'; i -= 1) {
}
fBuf[i - 1] = '.';
int len = strlen(fBuf);
for (; i < len; i += 1) {
fBuf[i] = fBuf[i + 1];
}
*fpLoc -= 1;
cerr << endl << dblquote(varName) <<
" is not of pointer type. Use this operator: ." << endl;
}
}
return new TClass(className);
}
void TTabCom::SetPattern(EContext_t handle, const char regexp[])
{
if (handle >= kNUM_PAT) {
cerr << endl
<< "ERROR: handle="
<< (int) handle << " >= kNUM_PAT=" << (int) kNUM_PAT << endl;
return;
}
fRegExp[handle] = regexp;
Makepat(regexp, fPat[handle], MAX_LEN_PAT);
}
int TTabCom::ParseReverse(const char *var_str, int start)
{
int end = 0;
if (start > (int)strlen(var_str)) start = strlen(var_str);
for (int i = start; i > 0; i--)
{
if (var_str[i] == '.') return i;
if (var_str[i] == '>' && i > 0 && var_str[i-1] == '-') return i-1;
}
return end;
}