    // @(#)root/meta:$Name:  $:$Id: TCint.cxx,v 1.89 2004/05/28 18:13:12 rdm Exp $
    // Author: Fons Rademakers   01/03/96

    /*************************************************************************
     * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
     * All rights reserved.                                                  *
     *                                                                       *
     * For the licensing terms see $ROOTSYS/LICENSE.                         *
     * For the list of contributors see $ROOTSYS/README/CREDITS.             *
     *************************************************************************/

    //////////////////////////////////////////////////////////////////////////
    //                                                                      //
    // This class defines an interface to the CINT C/C++ interpreter made   //
    // by Masaharu Goto from HP Japan.                                      //
    //                                                                      //
    // CINT is an almost full ANSI compliant C/C++ interpreter.             //
    //                                                                      //
    //////////////////////////////////////////////////////////////////////////

    #include "TCint.h"
    #include "TROOT.h"
    #include "TApplication.h"
    #include "TGlobal.h"
    #include "TDataType.h"
    #include "TClass.h"
    #include "TClassEdit.h"
    #include "TBaseClass.h"
    #include "TDataMember.h"
    #include "TMethod.h"
    #include "TMethodArg.h"
    #include "TObjArray.h"
    #include "TObjString.h"
    #include "TString.h"
    #include "TList.h"
    #include "TOrdCollection.h"
    #include "TVirtualPad.h"
    #include "TSystem.h"
    #include "TVirtualMutex.h"
    #include "TError.h"
    #include "TEnv.h"

    #ifdef WIN32
    #  ifndef ROOT_TGWin32Command
    #    include "TGWin32Command.h"
    #    undef GetClassInfo
    #  endif
    #endif

    #include <vector>
    #include <string>
    using namespace std;

    R__EXTERN int optind;

    // reference cint includes
    // make sure fproto.h is loaded (it was excluded in TCint.h)
    #undef G__FPROTO_H
    #include "fproto.h"
    #include "Api.h"

    // Those are missing from the cint API files:
    extern "C" int G__const_whatnoerror();
    extern "C" int G__const_setnoerror();
    extern "C" int G__const_resetnoerror();

    extern "C" void G__clearfilebusy(int);
    extern "C" void G__clearstack();

    extern "C" int ScriptCompiler(const char *filename, const char *opt) {
       return gSystem->CompileMacro(filename, opt);
    }

    extern "C" int IgnoreInclude(const char *fname, const char *expandedfname) {
       return gROOT->IgnoreInclude(fname,expandedfname);
    }

    extern "C" void TCint_UpdateClassInfo(char *c, Long_t l) {
      TCint::UpdateClassInfo(c, l);
    }

    extern "C" int TCint_AutoLoadCallback(char *c, char *l) {
      return TCint::AutoLoadCallback(c, l);
    }

    extern "C" void *TCint_FindSpecialObject(char *c, G__ClassInfo *ci, void **p1, void **p2) {
      return TCint::FindSpecialObject(c, ci, p1, p2);
    }

    // It is a "fantom" method to synchronize user keyboard input
    // and ROOT prompt line (for WIN32)
    const char *fantomline = "TRint::EndOfLineAction();";

    ClassImp(TCint)

    //______________________________________________________________________________
     TCint::TCint(const char *name, const char *title) : TInterpreter(name, title)
    {
       // Initialize the CINT interpreter interface.

       fMore      = 0;
       fPrompt[0] = 0;
       fMapfile   = 0;

       G__RegisterScriptCompiler(&ScriptCompiler);
       G__set_ignoreinclude(&IgnoreInclude);
       G__InitUpdateClassInfo(&TCint_UpdateClassInfo);
       G__InitGetSpecialObject(&TCint_FindSpecialObject);

       fDictPos.ptype = 0;
       fDictPosGlobals.ptype = 0;

       ResetAll();

    #ifndef WIN32
       optind = 1;  // make sure getopt() works in the main program
    #endif

       // Make sure that ALL macros are seen as C++.
       G__LockCpp();
    }

    //______________________________________________________________________________
     TCint::~TCint()
    {
       // Destroy the CINT interpreter interface.

       if (fMore != -1) {
         // only close the opened files do not free memory:
         // G__scratch_all();
         G__close_inputfiles();
       }

       delete fMapfile;

       //G__scratch_all();
    }

    //______________________________________________________________________________
     void TCint::ClearFileBusy()
    {
       // Reset CINT internal state in case a previous action was not correctly
       // terminated by G__init_cint() and G__dlmod().

       G__clearfilebusy(0);
    }

    //______________________________________________________________________________
     void TCint::ClearStack()
    {
       // Delete existing temporary values

       G__clearstack();
    }

    //______________________________________________________________________________
     Int_t TCint::InitializeDictionaries()
    {
       // Initialize all registered dictionaries. Normally this is already done
       // by G__init_cint() and G__dlmod().

       return G__call_setup_funcs();
    }

    //______________________________________________________________________________
     void TCint::EnableAutoLoading()
    {
       // Enable the automatic loading of shared libraries when a class
       // is used that is stored in a not yet loaded library. Uses the
       // information stored in the class/library map (typically
       // $ROOTSYS/etc/system.rootmap).

       G__set_class_autoloading_callback(&TCint_AutoLoadCallback);
       LoadLibraryMap();
    }

    //______________________________________________________________________________
     void TCint::EndOfLineAction()
    {
       // It calls a "fantom" method to synchronize user keyboard input
       // and ROOT prompt line.

        ProcessLineSynch(fantomline);
    }

    //______________________________________________________________________________
     void TCint::ExecThreadCB(TWin32SendClass *command)
    {
       // This function must be called from the "Command thread only".

    #ifdef WIN32
    #ifndef GDK_WIN32
       char *line = (char *)(command->GetData(0));
       EErrorCode *error = (EErrorCode*)(command->GetData(1));
       Int_t iret = ProcessLine((const char *)line,error);
       delete [] line;
       if (LOWORD(command->GetCOP()) == kSendWaitClass)
          ((TWin32SendWaitClass *)command)->Release();
       else
          delete command;
    #else
       if (command) { }
    #endif
    #else
       if (command) { }
    #endif
    }

    //______________________________________________________________________________
     Bool_t TCint::IsLoaded(const char* filename) const
    {
       // Return true if the file has already been loaded by cint.

       // We will try in this order:
       //   actual filename
       //   filename as a path relative to
       //            the include path
       //            the shared library path

       G__SourceFileInfo file(filename);
       if (file.IsValid()) { return kTRUE; };

       char *next = gSystem->Which(TROOT::GetMacroPath(), filename, kReadPermission);
       if (next) {
         file.Init(next);
         delete [] next;
         if (file.IsValid()) { return kTRUE; };
       }

       TString incPath = gSystem->GetIncludePath(); // of the form -Idir1  -Idir2 -Idir3
       incPath.Append(":").Prepend(" ");
       incPath.ReplaceAll(" -I",":");       // of form :dir1 :dir2:dir3
       while ( incPath.Index(" :") != -1 ) {
         incPath.ReplaceAll(" :",":");
       }
       incPath.Prepend(".:");
       incPath.Append(":$ROOTSYS/cint/include:$ROOTSYS/cint/stl");
       next = gSystem->Which(incPath, filename, kReadPermission);
       if (next) {
         file.Init(next);
         delete [] next;
         if (file.IsValid()) { return kTRUE; };
       }

       next = gSystem->DynamicPathName(filename,kTRUE);
       if (next) {
         file.Init(next);
         delete [] next;
         if (file.IsValid()) { return kTRUE; };
       }

       return kFALSE;
    }

    //______________________________________________________________________________
     Int_t TCint::Load(const char *filename, Bool_t system)
    {
       // Load a library file in CINT's memory.
       // if 'system' is true, the library is never unloaded.

       R__LOCKGUARD(gCINTMutex);
       int i;
       if (!system)
          i = G__loadfile(filename);
       else
          i = G__loadsystemfile(filename);

       UpdateListOfTypes();

       return i;
    }

    //______________________________________________________________________________
     void TCint::LoadMacro(const char *filename, EErrorCode *error)
    {
       // Load a macro file in CINT's memory.

       ProcessLine(Form(".L %s", filename), error);
    }

    //______________________________________________________________________________
     Long_t TCint::ProcessLine(const char *line, EErrorCode *error)
    {
       // Let CINT process a command line.
       // If the command is executed and the result of G__process_cmd is 0,
       // the return value is the int value corresponding to the result of the command
       // (float and double return values will be truncated).

       Int_t ret = 0;
       if (gApplication) {
          if (gApplication->IsCmdThread()) {
             gROOT->SetLineIsProcessing();

             G__value local_res;
             G__setnull(&local_res);

             // It checks whether the input line contains the "fantom" method
             // to synchronize user keyboard input and ROOT prompt line
             if (strstr(line,fantomline)) {
                G__free_tempobject();
                TCint::UpdateAllCanvases();
             } else {
                int local_error = 0;

                ret = G__process_cmd((char *)line, fPrompt, &fMore, &local_error, &local_res);
                if (local_error == 0 && G__get_return(&fExitCode) == G__RETURN_EXIT2) {
                   ResetGlobals();
                   gApplication->Terminate(fExitCode);
                }
                if (error)
                   *error = (EErrorCode)local_error;
             }

             if (ret==0) ret = G__int(local_res);

             gROOT->SetLineHasBeenProcessed();
          } else
             ret = ProcessLineAsynch(line, error);
       }
       return ret;
    }

    //______________________________________________________________________________
     Long_t TCint::ProcessLineAsynch(const char *line, EErrorCode *error)
    {
       // Let CINT process a command line asynch.

    #ifndef WIN32
       return ProcessLine(line, error);
    #else
    #ifndef GDK_WIN32
       if (error) *error = kProcessing;
       char *cmd = new char[strlen(line)+1];
       strcpy(cmd,line);
       TWin32SendClass *code = new TWin32SendClass(this,(UInt_t)cmd,(UInt_t)error,0,0);
       ExecCommandThread(code,kFALSE);
       return 0;
    #else
       return ProcessLine(line, error);
    #endif
    #endif
    }

    //______________________________________________________________________________
     Long_t TCint::ProcessLineSynch(const char *line, EErrorCode *error)
    {
       // Let CINT process a command line synchronously, i.e we are waiting
       // it will be finished.

      if (gApplication && gApplication->IsCmdThread())
         return ProcessLine(line, error);
    #ifdef WIN32
    #ifndef GDK_WIN32
       if (error) *error = kProcessing;
       char *cmd = new char[strlen(line)+1];
       strcpy(cmd,line);
       TWin32SendWaitClass code(this,(UInt_t)cmd,(UInt_t)error,0,0);
       ExecCommandThread(&code,kFALSE);
       code.Wait();
    #endif
    #endif
       return 0;
    }

    //______________________________________________________________________________
     Long_t TCint::Calc(const char *line, EErrorCode *error)
    {
       // Directly execute an executable statement (e.g. "func()", "3+5", etc.
       // however not declarations, like "Int_t x;").

       Long_t result;

    #ifdef WIN32
       // Test on ApplicationImp not being 0 is needed because only at end of
       // TApplication ctor the IsLineProcessing flag is set to 0, so before
       // we can not use it.
       if (gApplication && gApplication->GetApplicationImp()) {
          while (gROOT->IsLineProcessing() && !gApplication) {
             Warning("Calc", "waiting for CINT thread to free");
             gSystem->Sleep(500);
          }
          gROOT->SetLineIsProcessing();
       }
    #endif

       result = (Long_t) G__int(G__calc((char *)line));
       if (error) *error = (EErrorCode)G__lasterror();

    #ifdef WIN32
       if (gApplication && gApplication->GetApplicationImp())
          gROOT->SetLineHasBeenProcessed();
    #endif

       return result;
    }

    //______________________________________________________________________________
     void TCint::PrintIntro()
    {
       // Print CINT introduction and help message.

       Printf("nCINT/ROOT C/C++ Interpreter version %s", G__cint_version());
       Printf("Type ? for help. Commands must be C++ statements.");
       Printf("Enclose multiple statements between { }.");
    }

    //______________________________________________________________________________
     void TCint::Reset()
    {
       // Reset the CINT state to the state saved by the last call to
       // TCint::SaveContext().

       G__scratch_upto(&fDictPos);
    }

    //______________________________________________________________________________
     void TCint::ResetAll()
    {
       // Reset the CINT state to its initial state.

       G__init_cint("cint +V");
       G__init_process_cmd();
    }

    //______________________________________________________________________________
     void TCint::ResetGlobals()
    {
       // Reset the CINT global object state to the state saved by the last
       // call to TCint::SaveGlobalsContext().

       G__scratch_globals_upto(&fDictPosGlobals);
    }

    //______________________________________________________________________________
     void TCint::RewindDictionary()
    {
       // Rewind CINT dictionary to the point where it was before executing
       // the current macro. This function is typically called after SEGV or
       // ctlr-C after doing a longjmp back to the prompt.

       G__rewinddictionary();
    }

    //______________________________________________________________________________
     Int_t TCint::DeleteGlobal(void *obj)
    {
       // Delete obj from CINT symbol table so it cannot be accessed anymore.
       // Returns 1 in case of success and 0 in case object was not in table.

       return G__deleteglobal(obj);
    }

    //______________________________________________________________________________
     void TCint::SaveContext()
    {
       // Save the current CINT state.

       G__store_dictposition(&fDictPos);
    }

    //______________________________________________________________________________
     void TCint::SaveGlobalsContext()
    {
       // Save the current CINT state of global objects.

       G__store_dictposition(&fDictPosGlobals);
    }

    //______________________________________________________________________________
     void TCint::UpdateListOfGlobals()
    {
       // Update the list of pointers to global variables. This function
       // is called by TROOT::GetListOfGlobals().

       R__LOCKGUARD(gCINTMutex);
       G__DataMemberInfo t, *a;
       while (t.Next()) {
          // if name cannot be obtained no use to put in list
          if (t.IsValid() && t.Name()) {
             // first remove if already in list
             TGlobal *g = (TGlobal *)gROOT->fGlobals->FindObject(t.Name());
             if (g) {
                gROOT->fGlobals->Remove(g);
                delete g;
             }
             a = new G__DataMemberInfo(t);
             gROOT->fGlobals->Add(new TGlobal(a));
          }
       }
    }

    //______________________________________________________________________________
     void TCint::UpdateListOfGlobalFunctions()
    {
       // Update the list of pointers to global functions. This function
       // is called by TROOT::GetListOfGlobalFunctions().

       R__LOCKGUARD(gCINTMutex);
       G__MethodInfo t, *a;
       while (t.Next()) {
          // if name cannot be obtained no use to put in list
          if (t.IsValid() && t.Name()) {
             // first remove if already in list
             TFunction *f = (TFunction *)gROOT->fGlobalFunctions->FindObject(t.Name());
             if (f && f->InterfaceMethod()==((void*)t.InterfaceMethod()) ) {
                TString mangled = f->GetMangledName();
                if (mangled==t.GetMangledName()) {
                   gROOT->fGlobalFunctions->Remove(f);
                   delete f;
                }
             }
             a = new G__MethodInfo(t);
             gROOT->fGlobalFunctions->Add(new TFunction(a));
          }
       }
    }

    //______________________________________________________________________________
     void TCint::UpdateListOfTypes()
    {
       // Update the list of pointers to Datatype (typedef) definitions. This
       // function is called by TROOT::GetListOfTypes().

       R__LOCKGUARD(gCINTMutex);
       G__TypedefInfo t;
       while (t.Next()) {
          if (gROOT && gROOT->fTypes && t.IsValid() && t.Name()) {
             TDataType *d = (TDataType *)gROOT->fTypes->FindObject(t.Name());
             // only add new types, don't delete old ones with the same name
             // (as is done in UpdateListOfGlobals()),
             // this 'feature' is being used in TROOT::GetType().
             if (!d) {
                gROOT->fTypes->Add(new TDataType(new G__TypedefInfo(t)));
             }
          }
       }
    }

    //______________________________________________________________________________
     void TCint::SetClassInfo(TClass *cl, Bool_t reload)
    {
       // Set pointer to CINT's G__ClassInfo in TClass.

       R__LOCKGUARD(gCINTMutex);
       if (!cl->fClassInfo || reload) {

          delete cl->fClassInfo; cl->fClassInfo = 0;
          if (CheckClassInfo(cl->GetName())) {

             cl->fClassInfo = new G__ClassInfo(cl->GetName());

             // In case a class contains an external enum, the enum will be seen as a
             // class. We must detect this special case and make the class a Zombie.
             // Here we assume that a class has at least one method.
             // We can NOT call TClass::Property from here, because this method
             // assumes that the TClass is well formed to do a lot of information
             // caching. The method SetClassInfo (i.e. here) is usually called during
             // the building phase of the TClass, hence it is NOT well formed yet.
             if (cl->fClassInfo->IsValid() &&
                 !(cl->fClassInfo->Property() & (kIsClass|kIsStruct))) {
                cl->MakeZombie();
             }

             if (!cl->fClassInfo->IsLoaded()) {
                // this happens when no CINT dictionary is available
                delete cl->fClassInfo;
                cl->fClassInfo = 0;
             }

          }
       }
    }

    //______________________________________________________________________________
     Bool_t TCint::CheckClassInfo(const char *name)
    {
       // Checks if a class with the specified name is defined in CINT.
       // Returns kFALSE is class is not defined.

       // In the case where the class is not loaded and belongs to a namespace
       // or is nested, looking for the full class name is outputing a lots of
       // (expected) error messages.  Currently the only way to avoid this is to
       // specifically check that each level of nesting is already loaded.
       // In case of templates the idea is that everything between the outer
       // '<' and '>' has to be skipped, e.g.: aap<pipo<noot>::klaas>::a_class

       char *classname = StrDup(name);
       char *current = classname;
       while (*current) {

          while (*current && *current != ':' && *current != '<')
             current++;

          if (!*current) break;

          if (*current == '<') {
             int level = 1;
             current++;
             while (*current && level > 0) {
                if (*current == '<') level++;
                if (*current == '>') level--;
                current++;
             }
             continue;
          }

          // *current == ':', must be a "::"
          if (*(current+1) != ':') {
             Error("CheckClassInfo", "unexpected token : in %s", classname);
             delete [] classname;
             return kFALSE;
          }

          *current = '0';
          G__ClassInfo info(classname);
          if (!info.IsLoaded()) {
             delete [] classname;
             return kFALSE;
          }
          *current = ':';
          current += 2;
       }
       delete [] classname;

       Int_t tagnum = G__defined_tagname(name, 2);
       if (tagnum >= 0) return kTRUE;
       G__TypedefInfo t(name);
       if (t.IsValid() && !(t.Property()&G__BIT_ISFUNDAMENTAL)) return kTRUE;
       return kFALSE;
    }

    //______________________________________________________________________________
     void TCint::CreateListOfBaseClasses(TClass *cl)
    {
       // Create list of pointers to base class(es) for TClass cl.

       R__LOCKGUARD(gCINTMutex);
       if (!cl->fBase) {

          cl->fBase = new TList;

          G__BaseClassInfo t(*cl->GetClassInfo()), *a;
          while (t.Next()) {
             // if name cannot be obtained no use to put in list
             if (t.IsValid() && t.Name()) {
                a = new G__BaseClassInfo(t);
                cl->fBase->Add(new TBaseClass(a, cl));
             }
          }
       }
    }

    //______________________________________________________________________________
     void TCint::CreateListOfDataMembers(TClass *cl)
    {
       // Create list of pointers to data members for TClass cl.

       R__LOCKGUARD(gCINTMutex);
       if (!cl->fData) {

          cl->fData = new TList;

          G__DataMemberInfo t(*cl->GetClassInfo()), *a;
          while (t.Next()) {
             // if name cannot be obtained no use to put in list
             if (t.IsValid() && t.Name() && strcmp(t.Name(), "G__virtualinfo")) {
                a = new G__DataMemberInfo(t);
                cl->fData->Add(new TDataMember(a, cl));
             }
          }
       }
    }

    //______________________________________________________________________________
     void TCint::CreateListOfMethods(TClass *cl)
    {
       // Create list of pointers to methods for TClass cl.

       R__LOCKGUARD(gCINTMutex);
       if (!cl->fMethod) {

          cl->fMethod = new TList;

          G__MethodInfo t(*cl->GetClassInfo()), *a;
          while (t.Next()) {
             // if name cannot be obtained no use to put in list
             if (t.IsValid() && t.Name()) {
                a = new G__MethodInfo(t);
                cl->fMethod->Add(new TMethod(a, cl));
             }
          }
       }
    }

    //______________________________________________________________________________
     void TCint::CreateListOfMethodArgs(TFunction *m)
    {
       // Create list of pointers to method arguments for TMethod m.

       R__LOCKGUARD(gCINTMutex);
       if (!m->fMethodArgs) {

          m->fMethodArgs = new TList;

          G__MethodArgInfo t(*m->fInfo), *a;
          while (t.Next()) {
             // if type cannot be obtained no use to put in list
             if (t.IsValid() && t.Type()) {
                a = new G__MethodArgInfo(t);
                m->fMethodArgs->Add(new TMethodArg(a, m));
             }
          }
       }
    }

    //______________________________________________________________________________
     TString TCint::GetMangledName(TClass *cl, const char *method,
                                 const char *params)
    {
       // Return the CINT mangled name for a method of a class with parameters
       // params (params is a string of actual arguments, not formal ones). If the
       // class is 0 the global function list will be searched.

       R__LOCKGUARD(gCINTMutex);
       G__CallFunc  func;
       Long_t       offset;

       if (cl)
          func.SetFunc(cl->GetClassInfo(), method, params, &offset);
       else {
          G__ClassInfo gcl;   // default G__ClassInfo is global environment
          func.SetFunc(&gcl, method, params, &offset);
       }
       return func.GetMethodInfo().GetMangledName();
    }

    //______________________________________________________________________________
     TString TCint::GetMangledNameWithPrototype(TClass *cl, const char *method,
                                               const char *proto)
    {
       // Return the CINT mangled name for a method of a class with a certain
       // prototype, i.e. "char*,int,float". If the class is 0 the global function
       // list will be searched.

       R__LOCKGUARD(gCINTMutex);
       Long_t             offset;

       if (cl)
          return cl->GetClassInfo()->GetMethod(method, proto, &offset).GetMangledName();
       else {
          G__ClassInfo gcl;   // default G__ClassInfo is global environment
          return gcl.GetMethod(method, proto, &offset).GetMangledName();
       }
    }

    //______________________________________________________________________________
     void *TCint::GetInterfaceMethod(TClass *cl, const char *method,
                                    const char *params)
    {
       // Return pointer to CINT interface function for a method of a class with
       // parameters params (params is a string of actual arguments, not formal
       // ones). If the class is 0 the global function list will be searched.

       R__LOCKGUARD(gCINTMutex);
       G__CallFunc  func;
       Long_t       offset;

       if (cl)
          func.SetFunc(cl->GetClassInfo(), method, params, &offset);
       else {
          G__ClassInfo gcl;   // default G__ClassInfo is global environment
          func.SetFunc(&gcl, method, params, &offset);
       }
       return (void *)func.InterfaceMethod();
    }

    //______________________________________________________________________________
     void *TCint::GetInterfaceMethodWithPrototype(TClass *cl, const char *method,
                                                 const char *proto)
    {
       // Return pointer to CINT interface function for a method of a class with
       // a certain prototype, i.e. "char*,int,float". If the class is 0 the global
       // function list will be searched.

       R__LOCKGUARD(gCINTMutex);
       G__InterfaceMethod f;
       Long_t             offset;

       if (cl)
          f = cl->GetClassInfo()->GetMethod(method, proto, &offset).InterfaceMethod();
       else {
          G__ClassInfo gcl;   // default G__ClassInfo is global environment
          f = gcl.GetMethod(method, proto, &offset).InterfaceMethod();
       }
       return (void *)f;
    }

    //______________________________________________________________________________
     const char *TCint::GetInterpreterTypeName(const char *name)
    {
       // The 'name' is known to the interpreter, this function returns
       // the internal version of this name (usually just resolving typedefs)
       // This is used in particular to synchronize between the name used
       // by rootcint and by the run-time enviroment (TClass)
       // Return 0 if the name is not known.

       if (!gInterpreter->CheckClassInfo(name)) return 0;
       G__ClassInfo cl(name);
       if (cl.IsValid()) return cl.Name();
       else return 0;
    }

    //______________________________________________________________________________
     void TCint::Execute(const char *function, const char *params, int *error)
    {
       // Execute a global function with arguments params.

       R__LOCKGUARD(gCINTMutex);
       G__CallFunc  func;
       G__ClassInfo cl;
       Long_t       offset;

       // set pointer to interface method and arguments
       func.SetFunc(&cl, function, params, &offset);

       // call function
       func.Exec(0);
       if (error) *error = G__lasterror();
    }

    //______________________________________________________________________________
     void TCint::Execute(TObject *obj, TClass *cl, const char *method,
                        const char *params, int *error)
    {
       // Execute a method from class cl with arguments params.

       R__LOCKGUARD(gCINTMutex);
       void       *address;
       long        offset;
       G__CallFunc func;

       // If the actuall class of this object inherit 2nd (or more) from TObject,
       // 'obj' is unlikely to be the start of the object (as described by IsA()),
       // hence gInterpreter->Execute will improperly correct the offset.

       void *addr = cl->DynamicCast( TObject::Class(), obj, kFALSE);

       // set pointer to interface method and arguments
       func.SetFunc(cl->GetClassInfo(), method, params, &offset);

       // call function
       address = (void*)((Long_t)addr + offset);
       func.Exec(address);
       if (error) *error = G__lasterror();
    }

    //______________________________________________________________________________
     void TCint::Execute(TObject *obj, TClass *cl, TMethod *method, TObjArray *params,
                        int *error)
    {
       // Execute a method from class cl with the arguments in array params
       // (params[0] ... params[n] = array of TObjString parameters).

       // Convert the TObjArray array of TObjString parameters to a character
       // string of comma separated parameters.
       // The parameters of type 'char' are enclosed in double quotes and all
       // internal quotes are escaped.

       if (!method) {
          Error("Execute","No method was defined");
          return;
       }

       TList *argList = method->GetListOfMethodArgs();

       // Check number of actual parameters against of expected formal ones

       Int_t nparms = argList->LastIndex()+1;
       Int_t argc   = params ? params->LastIndex()+1:0;

       if (nparms != argc) {
          Error("Execute","Wrong number of the parameters");
          return;
       }

       const char *listpar = "";
       TString complete(10);

       if (params)
       {
           // Create a character string of parameters from TObjArray
           TIter next(params);
           for (Int_t i = 0; i < argc; i ++)
           {
               TMethodArg *arg = (TMethodArg *) argList->At( i );
               G__TypeInfo type( arg->GetFullTypeName() );
               TObjString *nxtpar = (TObjString *)next();
               if (i) complete += ',';
               if (strstr( type.TrueName(), "char" ))
               {
                   TString chpar('"');
                   chpar += (nxtpar->String()).ReplaceAll(""","\"");
                   // At this point we have to check if string contains \"
                   // and apply some more sophisticated parser. Not implemented yet!
                   complete += chpar;
                   complete += '"';
               }
               else
                   complete += nxtpar->String();
           }
           listpar = complete.Data();
       }

       Execute(obj, cl, (char *)method->GetName(), (char *)listpar, error);
    }

    //______________________________________________________________________________
     Long_t TCint::ExecuteMacro(const char *filename, EErrorCode *error)
    {
       // Execute a CINT macro.

       if (gApplication)
          return gApplication->ProcessFile(filename, (int*)error);
       else
          /*G__value result =*/ G__exec_tempfile((char*)filename);
       return 0;  // could get return value from result, but what about return type?
    }

    //______________________________________________________________________________
     const char *TCint::TypeName(const char *typeDesc)
    {
       // Return the absolute type of typeDesc.
       // E.g.: typeDesc = "class TNamed**", returns "TNamed".
       // You need to use the result immediately before it is being overwritten.

       static char t[1024];
       char *s, *template_start;
       if (!strstr(typeDesc, "(*)(")) {
          s = (char*)strchr(typeDesc, ' ');
          template_start = (char*)strchr(typeDesc, '<');
          if (!strcmp(typeDesc, "long long"))
             strcpy(t, typeDesc);
          // s is the position of the second 'word' (if any)
          // except in the case of templates where there will be a space
          // just before any closing '>': eg.
          //    TObj<std::vector<UShort_t,__malloc_alloc_template<0> > >*
          else if (s && (template_start==0 || (s < template_start)) )
             strcpy(t, s+1);
          else
             strcpy(t, typeDesc);
       }

       int l = strlen(t);
       while (l > 0 && (t[l-1] == '*' || t[l-1] == '&') ) t[--l] = 0;

       return t;
    }

    //______________________________________________________________________________
     Int_t TCint::LoadLibraryMap()
    {
       // Load map between class and library. Cint uses this information to
       // automatically load the shared library for a class (autoload mechanism).
       // See also the AutoLoadCallback() method below.

       // open the [system].rootmap files
       if (!fMapfile)
          fMapfile = new TEnv(".rootmap");

       TEnvRec *rec;
       TIter next(fMapfile->GetTable());

       while ((rec = (TEnvRec*) next())) {
          const char *cls = rec->GetName();
          if (!strncmp(cls, "Library.", 8) && strlen(cls) > 8) {
             // get the first lib from the list of lib and dependent libs
             TString libs = rec->GetValue();
             TString delim(" ");
             TObjArray *tokens = libs.Tokenize(delim);
             // convert back from "@@" to "::", we used "@@" because TEnv
             // considers "::" a terminator
             ((TObjString*)tokens->At(0))->String().ReplaceAll("@@", "::");
             char *lib = (char *)((TObjString*)tokens->At(0))->GetName();
             G__set_class_autoloading_table((char*)(cls+8), lib);
             if (gDebug > 0)
                printf("<TCint::LoadLibraryMap>: adding class %s in lib %sn",
                       cls+8, lib);
             delete tokens;
          }
       }
       return 0;
    }

    //______________________________________________________________________________
     int TCint::AutoLoadCallback(const char *cls, const char *lib)
    {
       // Load library containing specified class. Returns 0 in case of error
       // and 1 in case if success.

       if (!gROOT || !gInterpreter) return 0;

       // calls to load libCore might come in the very beginning when libCore
       // dictionary is not fully loaded yet, ignore it since libCore is always
       // loaded
       if (strstr(lib, "libCore")) return 1;

       // lookup class to find list of dependent libraries
       TString deplibs = gInterpreter->GetClassSharedLibs(cls);
       if (!deplibs.IsNull()) {
          TString delim(" ");
          TObjArray *tokens = deplibs.Tokenize(delim);
          for (Int_t i = tokens->GetEntries()-1; i > 0; i--) {
             const char *deplib = ((TObjString*)tokens->At(i))->GetName();
             gROOT->LoadClass(cls, deplib);
             if (gDebug > 0)
                ::Info("TCint::AutoLoadCallback", "loaded dependent library %s for class %s",
                       deplib, cls);
          }
          delete tokens;
       }

       if (gROOT->LoadClass(cls, lib) == 0) {
          if (gDebug > 0)
             ::Info("TCint::AutoLoadCallback", "loaded library %s for class %s",
                    lib, cls);
          return 1;
       } else
          ::Error("TCint::AutoLoadCallback", "failure loading library %s for class %s",
                  lib, cls);
       return 0;
    }

    //______________________________________________________________________________
     void *TCint::FindSpecialObject(const char *item, G__ClassInfo *type,
                                   void **prevObj, void **assocPtr)
    {
       // Static function called by CINT when it finds an un-indentified object.
       // This function tries to find the UO in the ROOT files, directories, etc.
       // This functions has been registered by the TCint ctor.

       if (!*prevObj || *assocPtr != gDirectory) {
          *prevObj = gROOT->FindSpecialObject(item, *assocPtr);
       }

       if (*prevObj) type->Init(((TObject *)*prevObj)->ClassName());
       return *prevObj;
    }

    //______________________________________________________________________________
     void TCint::UpdateClassInfo(char *item, Long_t tagnum)
    {
       // Static function called by CINT when it changes the tagnum for
       // a class (e.g. after re-executing the setup function). In such
       // cases we have to update the tagnum in the G__ClassInfo used by
       // the TClass for class "item".

       if (gROOT && gROOT->GetListOfClasses()) {

          Bool_t load = kFALSE;

          if (strchr(item,'<')) {
             // We have a template which may have duplicates.

             TIter next( gROOT->GetListOfClasses() );
             TClass *cl;

             TString resolvedItem(
                TClassEdit::ResolveTypedef(TClassEdit::ShortType(item,TClassEdit::kDropStlDefault).c_str()
                                           ,kTRUE) );
             TString resolved;
             while ( (cl = (TClass*)next()) ) {
                resolved = TClassEdit::ResolveTypedef(TClassEdit::ShortType(cl->GetName(),
                                                                            TClassEdit::kDropStlDefault).c_str()
                                                      ,kTRUE);
                if (resolved==resolvedItem) {
                   // we found at least one equivalent.
                   // let's force a reload
                   load = kTRUE;
                }
             }
          }

          TClass *cl = gROOT->GetClass(item, load);
          if (cl) {
             G__ClassInfo *info = cl->GetClassInfo();
             if (info && info->Tagnum() != tagnum) {
                info->Init((int)tagnum);
                if (cl->fBase)
                   cl->fBase->Delete();
                delete cl->fBase;
                cl->fBase = 0;
             }
          }
       }
    }

    //______________________________________________________________________________
     void TCint::UpdateAllCanvases()
    {
       // Update all canvases at end the terminal input command.

       TIter next(gROOT->GetListOfCanvases());
       TVirtualPad *canvas;
       while ((canvas = (TVirtualPad *)next()))
          canvas->Update();
    }

    //______________________________________________________________________________
     const char* TCint::GetSharedLibs()
    {
       // Refresh the list of shared libraries and return it.

       fSharedLibs = "";

       G__SourceFileInfo cursor(0);
       while (cursor.IsValid()) {
          const char *filename = cursor.Name();
          if (filename &&
              (strstr(filename,".sl")  != 0 ||
               strstr(filename,".dl")  != 0 ||
               strstr(filename,".so")  != 0 ||
               strstr(filename,".dll") != 0 ||
               strstr(filename,".DLL") != 0 ||
               strstr(filename,".a")   != 0)) {
             if (!fSharedLibs.IsNull())
                fSharedLibs.Append(" ");
             fSharedLibs.Append(filename);
          }

          cursor.Next();
       }

       return fSharedLibs;
    }

    //______________________________________________________________________________
     const char *TCint::GetClassSharedLibs(const char *cls)
    {
       // Get the list of shared libraries containing the code for class cls.
       // The first library in the list is the one containing the class, the
       // others are the libraries the first one depends on. Returns 0
       // in case the library is not found.

       if (!cls || !*cls)
          return 0;

       // lookup class to find list of libraries
       if (fMapfile) {
          TString c = TString("Library.") + cls;
          c.ReplaceAll("::", "@@");
          const char *libs = fMapfile->GetValue(c, "");
          return (*libs) ? libs : 0;
       }
       return 0;
    }

    //______________________________________________________________________________
     Bool_t TCint::IsErrorMessagesEnabled()
    {
       // If error messages are disabled, the interpreter should suppress its
       // failures and warning messages from stdout.

       return !G__const_whatnoerror();
    }

    //______________________________________________________________________________
     Bool_t TCint::SetErrorMessages(Bool_t enable)
    {
       // If error messages are disabled, the interpreter should  suppress its
       // failures and warning messages from stdout.

      if (enable) G__const_resetnoerror();
      else        G__const_setnoerror();
      return     !G__const_whatnoerror();
    }

    //______________________________________________________________________________
     void TCint::AddIncludePath(const char *path)
    {
      // Add the given path to the list of directory in which the interpreter
      // look for include files.

      G__add_ipath((char*)path);

    }

    //______________________________________________________________________________
     const char* TCint::GetIncludePath()
    {
      // Refresh the list of include path known to the interpreter and return it,
      // with -I prepended.

      fIncludePath = "";

      G__IncludePathInfo path;

      while ( path.Next() ) {
        const char *pathname = path.Name();
        fIncludePath.Append(" -I").Append(pathname);
      } ;

      return fIncludePath;

    }