TTabCom.cxx

Go to the documentation of this file.
00001 // @(#)root/rint:$Id: TTabCom.cxx 35775 2010-09-27 08:23:54Z rdm $
00002 // Author: Christian Lacunza <lacunza@cdfsg6.lbl.gov>   27/04/99
00003 
00004 // Modified by Artur Szostak <artur@alice.phy.uct.ac.za> : 1 June 2003
00005 //   Added support for namespaces.
00006 
00007 /*************************************************************************
00008  * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
00009  * All rights reserved.                                                  *
00010  *                                                                       *
00011  * For the licensing terms see $ROOTSYS/LICENSE.                         *
00012  * For the list of contributors see $ROOTSYS/README/CREDITS.             *
00013  *************************************************************************/
00014 
00015 ////////////////////////////////////////////////////////////////////////////
00016 //                                                                        //
00017 // TTabCom                                                                //
00018 //                                                                        //
00019 // This class performs basic tab completion.                              //
00020 // You should be able to hit [TAB] to complete a partially typed:         //
00021 //                                                                        //
00022 //   username                                                             //
00023 //   environment variable                                                 //
00024 //   preprocessor directive                                               //
00025 //   pragma                                                               //
00026 //   filename (with a context-sensitive path)                             //
00027 //   public member function or data member (including base classes)       //
00028 //   global variable, function, or class name                             //
00029 //                                                                        //
00030 // Also, something like                                                   //
00031 //                                                                        //
00032 //   someObject->Func([TAB]                                               //
00033 //   someObject.Func([TAB]                                                //
00034 //   someClass::Func([TAB]                                                //
00035 //   someClass var([TAB]                                                  //
00036 //   new someClass([TAB]                                                  //
00037 //                                                                        //
00038 // will print a list of prototypes for the indicated                      //
00039 // method or constructor.                                                 //
00040 //                                                                        //
00041 // Current limitations and bugs:                                          //
00042 //                                                                        //
00043 //  1. you can only use one member access operator at a time.             //
00044 //     eg, this will work: gROOT->GetListOfG[TAB]                         //
00045 //     but this will not:  gROOT->GetListOfGlobals()->Conta[TAB]          //
00046 //                                                                        //
00047 //  2. nothing is guaranteed to work on windows or VMS                    //
00048 //     (for one thing, /bin/env and /etc/passwd are hardcoded)            //
00049 //                                                                        //
00050 //  3. CINT shortcut #2 is deliberately not supported.                    //
00051 //     (using "operator.()" instead of "operator->()")                    //
00052 //                                                                        //
00053 //  4. most identifiers (including C++ identifiers, usernames,            //
00054 //     environment variables, etc)                                        //
00055 //     are restriceted to this character set: [_a-zA-Z0-9]                //
00056 //     therefore, you won't be able to complete things like               //
00057 //                                                                        //
00058 //          operator new                                                  //
00059 //          operator+                                                     //
00060 //          etc                                                           //
00061 //                                                                        //
00062 //  5. ~whatever[TAB] always tries to complete a username.                //
00063 //     use whitespace (~ whatever[TAB]) if you want to complete a global  //
00064 //     identifier.                                                        //
00065 //                                                                        //
00066 //  6. CINT shortcut #3 is not supported when trying to complete          //
00067 //     the name of a global object.  (it is supported when trying to      //
00068 //     complete a member of a global object)                              //
00069 //                                                                        //
00070 //  7. the list of #pragma's is hardcoded                                 //
00071 //     (ie not obtained from the interpreter at runtime)                  //
00072 //     ==> user-defined #pragma's will not be recognized                  //
00073 //                                                                        //
00074 //  8. the system include directories are also hardcoded                  //
00075 //     because i don't know how to get them from the interpreter.         //
00076 //     fons, maybe they should be #ifdef'd for the different sytems?      //
00077 //                                                                        //
00078 //  9. the TabCom.FileIgnore resource is always applied, even if you      //
00079 //     are not trying to complete a filename.                             //
00080 //                                                                        //
00081 // 10. anything in quotes is assumed to be a filename                     //
00082 //     so (among other things) you can't complete a quoted class name:    //
00083 //     eg, TClass class1( "TDict[TAB]                                     //
00084 //     this won't work... looks for a file in pwd starting with TDict     //
00085 //                                                                        //
00086 // 11. the prototypes tend to omit the word "const" a lot.                //
00087 //     this is a problem with ROOT or CINT.                               //
00088 //                                                                        //
00089 // 12. when listing ambiguous matches, only one column is used,           //
00090 //     even if there are many completions.                                //
00091 //                                                                        //
00092 // 13. anonymous objects are not currently identified                     //
00093 //     so, for example,                                                   //
00094 //                                                                        //
00095 //          root> printf( TString([TAB                                    //
00096 //                                                                        //
00097 //     gives an error message instead of listing TString's constructors.  //
00098 //     (this could be fixed)                                              //
00099 //                                                                        //
00100 // 14. the routine that adds the "appendage" isn't smart enough to know   //
00101 //     if it's already there:                                             //
00102 //                                                                        //
00103 //          root> TCanvas::Update()                                       //
00104 //              press [TAB] here ^                                        //
00105 //          root> TCanvas::Update()()                                     //
00106 //     (this could be fixed)                                              //
00107 //                                                                        //
00108 // 15. the appendage is only applied if there is exactly 1 match.         //
00109 //     eg, this                                                           //
00110 //                                                                        //
00111 //          root> G__at[TAB]                                              //
00112 //          root> G__ateval                                               //
00113 //                                                                        //
00114 //     happens instead of this                                            //
00115 //                                                                        //
00116 //          root> G__at[TAB]                                              //
00117 //          root> G__ateval(                                              //
00118 //                                                                        //
00119 //     because there are several overloaded versions of G__ateval().      //
00120 //     (this could be fixed)                                              //
00121 //                                                                        //
00122 ////////////////////////////////////////////////////////////////////////////
00123 
00124 #include <stdio.h>
00125 #include <assert.h>
00126 
00127 #include "RConfigure.h"
00128 #include "TTabCom.h"
00129 #include "TClass.h"
00130 #include "TSystem.h"
00131 #include "TROOT.h"
00132 #include "TMethod.h"
00133 #include "TEnv.h"
00134 #include "TBenchmark.h"
00135 #include "TError.h"
00136 #include "TGlobal.h"
00137 #include "TList.h"
00138 #include "Getline.h"
00139 #include "TFunction.h"
00140 #include "TMethodArg.h"
00141 #include "TInterpreter.h"
00142 #include "Riostream.h"
00143 #include "Rstrstream.h"
00144 
00145 #define BUF_SIZE    1024        // must match value in C_Getline.c (for bounds checking)
00146 #define IfDebug(x)  if(gDebug==TTabCom::kDebug) x
00147 
00148 #ifdef R__WIN32
00149 #undef tmpnam
00150 #define tmpnam(a) _tempnam(a, 0)
00151 const char kDelim = ';';
00152 #else
00153 const char kDelim = ':';
00154 #endif
00155 
00156 
00157 ClassImp(TTabCom)
00158 // ----------------------------------------------------------------------------
00159 //
00160 //             global/file scope variables
00161 //
00162 TTabCom *gTabCom = 0;
00163 
00164 
00165 extern "C" int gl_root_tab_hook(char *buf, int /*prompt_width */ ,
00166                                 int *pLoc)
00167 {
00168    return gTabCom ? gTabCom->Hook(buf, pLoc) : -1;
00169 }
00170 
00171 
00172 // ----------------------------------------------------------------------------
00173 //
00174 //              constructors
00175 //
00176 
00177 //______________________________________________________________________________
00178 TTabCom::TTabCom()
00179 {
00180    // Default constructor.
00181    fpDirectives = 0;
00182    fpPragmas = 0;
00183    fpGlobals = 0;
00184    fpGlobalFuncs = 0;
00185    fpClasses = 0;
00186    fpNamespaces = 0;
00187    fpUsers = 0;
00188    fBuf = 0;
00189    fpLoc = 0;
00190    fpEnvVars = 0;
00191    fpFiles = 0;
00192    fpSysIncFiles = 0;
00193    fVarIsPointer = kFALSE;
00194    fLastIter = 0;
00195 
00196    InitPatterns();
00197 
00198    Gl_tab_hook = gl_root_tab_hook;
00199 }
00200 
00201 //
00202 //              constructors
00203 //
00204 // ----------------------------------------------------------------------------
00205 
00206 TTabCom::~TTabCom()
00207 {
00208    // Destructor.
00209 
00210    ClearAll();
00211    ClearSysIncFiles(); // this one stays cached
00212    ClearUsers();       // this one stays cached
00213 }
00214 
00215 // ----------------------------------------------------------------------------
00216 //
00217 //              public member functions
00218 //
00219 
00220 
00221 //______________________________________________________________________________
00222 void TTabCom::ClearClasses()
00223 {
00224    // Clear classes and namespace collections.
00225 
00226    if (fpClasses) {
00227       fpClasses->Delete(0);
00228       delete fpClasses;
00229       fpClasses = 0;
00230    }
00231 
00232    // Since the namespace array is filled at the same time as fpClasses we
00233    // delete it at the same time.
00234    if (fpNamespaces) {
00235       fpNamespaces->Delete(0);
00236       delete fpNamespaces;
00237       fpNamespaces = 0;
00238    }
00239 }
00240 
00241 //______________________________________________________________________________
00242 void TTabCom::ClearCppDirectives()
00243 {
00244    // Forget all Cpp directives seen so far.
00245 
00246    if (!fpDirectives)
00247       return;
00248    fpDirectives->Delete(0);
00249    delete fpDirectives;
00250    fpDirectives = 0;
00251 }
00252 
00253 //______________________________________________________________________________
00254 void TTabCom::ClearEnvVars()
00255 {
00256    // Forget all environment variables seen so far.
00257    if (!fpEnvVars)
00258       return;
00259    fpEnvVars->Delete(0);
00260    delete fpEnvVars;
00261    fpEnvVars = 0;
00262 }
00263 
00264 //______________________________________________________________________________
00265 void TTabCom::ClearFiles()
00266 {
00267    // Close all files.
00268    if (!fpFiles)
00269       return;
00270    fpFiles->Delete(0);
00271    delete fpFiles;
00272    fpFiles = 0;
00273 }
00274 
00275 //______________________________________________________________________________
00276 void TTabCom::ClearGlobalFunctions()
00277 {
00278    // Forget all global functions seen so far.
00279    if (!fpGlobalFuncs)
00280       return;
00281    fpGlobalFuncs->Delete(0);
00282    delete fpGlobalFuncs;
00283    fpGlobalFuncs = 0;
00284 }
00285 
00286 //______________________________________________________________________________
00287 void TTabCom::ClearGlobals()
00288 {
00289    // Forget all global variables seen so far.
00290    if (!fpGlobals)
00291       return;
00292    fpGlobals->Delete(0);
00293    delete fpGlobals;
00294    fpGlobals = 0;
00295 }
00296 
00297 //______________________________________________________________________________
00298 void TTabCom::ClearPragmas()
00299 {
00300    // Forget all pragmas seen so far.
00301    if (!fpPragmas)
00302       return;
00303    fpPragmas->Delete(0);
00304    delete fpPragmas;
00305    fpPragmas = 0;
00306 }
00307 
00308 //______________________________________________________________________________
00309 void TTabCom::ClearSysIncFiles()
00310 {
00311    // Close system files.
00312    if (!fpSysIncFiles)
00313       return;
00314    fpSysIncFiles->Delete(0);
00315    delete fpSysIncFiles;
00316    fpSysIncFiles = 0;
00317 }
00318 
00319 //______________________________________________________________________________
00320 void TTabCom::ClearUsers()
00321 {
00322    // Forget all user seen so far.
00323    if (!fpUsers)
00324       return;
00325    fpUsers->Delete(0);
00326    delete fpUsers;
00327    fpUsers = 0;
00328 }
00329 
00330 //______________________________________________________________________________
00331 void TTabCom::ClearAll()
00332 {
00333    // clears all lists
00334    // except for user names and system include files.
00335 
00336    ClearClasses();
00337    ClearCppDirectives();
00338    ClearEnvVars();
00339    ClearFiles();
00340    ClearGlobalFunctions();
00341    ClearGlobals();
00342    ClearPragmas();
00343 //   ClearSysIncFiles(); <-- this one stays cached
00344 //   ClearUsers();       <-- this one stays cached
00345 }
00346 
00347 //______________________________________________________________________________
00348 void TTabCom::RehashClasses()
00349 {
00350    // Do the class rehash.
00351    ClearClasses();
00352    GetListOfClasses();
00353 }
00354 
00355 //______________________________________________________________________________
00356 void TTabCom::RehashCppDirectives()
00357 {
00358    // Cpp rehashing.
00359    ClearCppDirectives();
00360    GetListOfCppDirectives();
00361 }
00362 
00363 //______________________________________________________________________________
00364 void TTabCom::RehashEnvVars()
00365 {
00366    // Environemnt variables rehashing.
00367    ClearEnvVars();
00368    GetListOfEnvVars();
00369 }
00370 
00371 //______________________________________________________________________________
00372 void TTabCom::RehashFiles()
00373 {
00374    // Close files.
00375    ClearFiles();                /* path unknown */
00376 }                               // think about this
00377 
00378 //______________________________________________________________________________
00379 void TTabCom::RehashGlobalFunctions()
00380 {
00381    // Reload global functions.
00382    ClearGlobalFunctions();
00383    GetListOfGlobalFunctions();
00384 }
00385 
00386 //______________________________________________________________________________
00387 void TTabCom::RehashGlobals()
00388 {
00389    // Reload globals.
00390    ClearGlobals();
00391    GetListOfGlobals();
00392 }
00393 
00394 //______________________________________________________________________________
00395 void TTabCom::RehashPragmas()
00396 {
00397    // Reload pragmas.
00398    ClearPragmas();
00399    GetListOfPragmas();
00400 }
00401 
00402 //______________________________________________________________________________
00403 void TTabCom::RehashSysIncFiles()
00404 {
00405    // Reload system include files.
00406    ClearSysIncFiles();
00407    GetListOfSysIncFiles();
00408 }
00409 
00410 //______________________________________________________________________________
00411 void TTabCom::RehashUsers()
00412 {
00413    // Reload users.
00414    ClearUsers();
00415    GetListOfUsers();
00416 }
00417 
00418 //______________________________________________________________________________
00419 void TTabCom::RehashAll()
00420 {
00421    // clears and then rebuilds all lists
00422    // except for user names and system include files.
00423 
00424    RehashClasses();
00425    RehashCppDirectives();
00426    RehashEnvVars();
00427    RehashFiles();
00428    RehashGlobalFunctions();
00429    RehashGlobals();
00430    RehashPragmas();
00431 //   RehashSysIncFiles(); <-- this one stays cached
00432 //   RehashUsers();       <-- this one stays cached
00433 }
00434 
00435 //______________________________________________________________________________
00436 const TSeqCollection *TTabCom::GetListOfClasses()
00437 {
00438    // Return the list of classes.
00439    if (!fpClasses) {
00440       // generate a text list of classes on disk
00441       const char *tmpfilename = tmpnam(0);
00442       FILE *fout = fopen(tmpfilename, "w");
00443       if (!fout) return 0;
00444       gCint->DisplayClass(fout, (char*)"", 0, 0);
00445       fclose(fout);
00446 
00447       // open the file
00448       ifstream file1(tmpfilename);
00449       if (!file1) {
00450          Error("TTabCom::GetListOfClasses", "could not open file \"%s\"",
00451                tmpfilename);
00452          gSystem->Unlink(tmpfilename);
00453          return 0;
00454       }
00455       // skip the first 2 lines (which are just header info)
00456       file1.ignore(32000, '\n');
00457       file1.ignore(32000, '\n');
00458 
00459       // parse file, add to list
00460       fpClasses = new TContainer;
00461       fpNamespaces = new TContainer;
00462       TString line;
00463       while (file1) {
00464          line = "";
00465          line.ReadLine(file1, kFALSE);  // kFALSE ==> don't skip whitespace
00466          line = line(23, 32000);
00467 // old way...
00468 //             if (line.Index("class") >= 0)
00469 //                  line = line(6, 32000);
00470 //             else if (line.Index("enum") >= 0)
00471 //                  line = line(5, 32000);
00472 //             else if (line.Index("(unknown)") >= 0)
00473 //                  line = line(10, 32000);
00474 //             line = line("[^ ]*");
00475 // new way...
00476          int index;
00477          Bool_t isanamespace = kFALSE;  // Flag used to check if we found a namespace name.
00478          if (0);
00479          else if ((index = line.Index(" class ")) >= 0)
00480             line = line(1 + index + 6, 32000);
00481          else if ((index = line.Index(" namespace ")) >= 0) {
00482             line = line(1 + index + 10, 32000);
00483             isanamespace = kTRUE;
00484          } else if ((index = line.Index(" struct ")) >= 0)
00485             line = line(1 + index + 7, 32000);
00486          else if ((index = line.Index(" enum ")) >= 0)
00487             line = line(1 + index + 5, 32000);
00488          else if ((index = line.Index(" (unknown) ")) >= 0)
00489             line = line(1 + index + 10, 32000);
00490          // 2 changes: 1. use spaces ^         ^          2. use offset ^^^^^ in case of long
00491          //               to reduce probablility that        filename which overflows
00492          //               these keywords will occur in       its field.
00493          //               filename or classname.
00494          line = line("[^ ]*");
00495 
00496          // If we find namespace names then add them to the fpNamespaces array and
00497          // not the classes array.
00498          if (isanamespace)
00499             fpNamespaces->Add(new TObjString(line));
00500          else
00501             fpClasses->Add(new TObjString(line));
00502       }
00503 
00504       // done with this file
00505       file1.close();
00506       gSystem->Unlink(tmpfilename);
00507    }
00508 
00509    return fpClasses;
00510 }
00511 
00512 //______________________________________________________________________________
00513 const TSeqCollection *TTabCom::GetListOfCppDirectives()
00514 {
00515    // Return the list of CPP directives.
00516    if (!fpDirectives) {
00517       fpDirectives = new TContainer;
00518 
00519       fpDirectives->Add(new TObjString("if"));
00520       fpDirectives->Add(new TObjString("ifdef"));
00521       fpDirectives->Add(new TObjString("ifndef"));
00522       fpDirectives->Add(new TObjString("elif"));
00523       fpDirectives->Add(new TObjString("else"));
00524       fpDirectives->Add(new TObjString("endif"));
00525       fpDirectives->Add(new TObjString("include"));
00526       fpDirectives->Add(new TObjString("define"));
00527       fpDirectives->Add(new TObjString("undef"));
00528       fpDirectives->Add(new TObjString("line"));
00529       fpDirectives->Add(new TObjString("error"));
00530       fpDirectives->Add(new TObjString("pragma"));
00531    }
00532 
00533    return fpDirectives;
00534 }
00535 
00536 //______________________________________________________________________________
00537 const TSeqCollection *TTabCom::GetListOfFilesInPath(const char path[])
00538 {
00539    // "path" should be initialized with a colon separated list of
00540    // system directories
00541 
00542    static TString previousPath;
00543 
00544    if (path && fpFiles && strcmp(path, previousPath) == 0) {
00545       return fpFiles;
00546    } else {
00547       ClearFiles();
00548 
00549       fpFiles = NewListOfFilesInPath(path);
00550       previousPath = path;
00551    }
00552 
00553    return fpFiles;
00554 }
00555 
00556 //______________________________________________________________________________
00557 const TSeqCollection *TTabCom::GetListOfEnvVars()
00558 {
00559    // Uses "env" (Unix) or "set" (Windows) to get list of environment variables.
00560 
00561    if (!fpEnvVars) {
00562       const char *tmpfilename = tmpnam(0);
00563       TString cmd;
00564 
00565 #ifndef WIN32
00566       char *env = gSystem->Which(gSystem->Getenv("PATH"), "env", kExecutePermission);
00567       if (!env)
00568          return 0;
00569       cmd = env;
00570       cmd += " > ";
00571       delete [] env;
00572 #else
00573       cmd = "set > ";
00574 #endif
00575       cmd += tmpfilename;
00576       cmd += "\n";
00577       gSystem->Exec(cmd.Data());
00578 
00579       // open the file
00580       ifstream file1(tmpfilename);
00581       if (!file1) {
00582          Error("TTabCom::GetListOfEnvVars", "could not open file \"%s\"",
00583                tmpfilename);
00584          gSystem->Unlink(tmpfilename);
00585          return 0;
00586       }
00587       // parse, add
00588       fpEnvVars = new TContainer;
00589       TString line;
00590       while (file1)             // i think this loop goes one time extra which
00591          // results in an empty string in the list, but i don't think it causes any
00592          // problems.
00593       {
00594          line.ReadToDelim(file1, '=');
00595          file1.ignore(32000, '\n');
00596          fpEnvVars->Add(new TObjString(line.Data()));
00597       }
00598 
00599       file1.close();
00600       gSystem->Unlink(tmpfilename);
00601    }
00602 
00603    return fpEnvVars;
00604 }
00605 
00606 //______________________________________________________________________________
00607 const TSeqCollection *TTabCom::GetListOfGlobals()
00608 {
00609    // Return the list of globals.
00610    if (!fpGlobals) {
00611 
00612       fpGlobals = new TContainer;
00613 
00614       DataMemberInfo_t *a;
00615       int last = 0;
00616       int nglob = 0;
00617 
00618       // find the number of global objects
00619       DataMemberInfo_t *t = gCint->DataMemberInfo_Factory();
00620       while (gCint->DataMemberInfo_Next(t))
00621          nglob++;
00622 
00623       for (int i = 0; i < nglob; i++) {
00624          a = gCint->DataMemberInfo_Factory();
00625          gCint->DataMemberInfo_Next(a);             // initial positioning
00626 
00627          for (int j = 0; j < last; j++)
00628             gCint->DataMemberInfo_Next(a);
00629 
00630          // if name cannot be obtained no use to put in list
00631          if (gCint->DataMemberInfo_IsValid(a) && gCint->DataMemberInfo_Name(a)) {
00632             fpGlobals->Add(new TGlobal(a));
00633          } else
00634             gCint->DataMemberInfo_Delete(a);
00635 
00636          last++;
00637       }
00638       gCint->DataMemberInfo_Delete(t);
00639    }
00640 
00641    return fpGlobals;
00642 }
00643 
00644 //______________________________________________________________________________
00645 const TSeqCollection *TTabCom::GetListOfGlobalFunctions()
00646 {
00647    // Return the list of global functions.
00648    if (!fpGlobalFuncs) {
00649 
00650       fpGlobalFuncs = new TContainer;
00651 
00652       MethodInfo_t *a;
00653       int last = 0;
00654       int nglob = 0;
00655 
00656       // find the number of global functions
00657       MethodInfo_t *t = gCint->MethodInfo_Factory();
00658       while (gCint->MethodInfo_Next(t))
00659          nglob++;
00660 
00661       for (int i = 0; i < nglob; i++) {
00662          a = gCint->MethodInfo_Factory();
00663          gCint->MethodInfo_Next(a);             // initial positioning
00664 
00665          for (int j = 0; j < last; j++)
00666             gCint->MethodInfo_Next(a);
00667 
00668          // if name cannot be obtained no use to put in list
00669          if (gCint->MethodInfo_IsValid(a) && gCint->MethodInfo_Name(a)) {
00670             fpGlobalFuncs->Add(new TFunction(a));
00671          } else
00672             gCint->MethodInfo_Delete(a);
00673 
00674          last++;
00675       }
00676       gCint->MethodInfo_Delete(t);
00677    }
00678 
00679    return fpGlobalFuncs;
00680 }
00681 
00682 //______________________________________________________________________________
00683 const TSeqCollection *TTabCom::GetListOfPragmas()
00684 {
00685    // Return the list of pragmas
00686    if (!fpPragmas) {
00687       fpPragmas = new TContainer;
00688 
00689       fpPragmas->Add(new TObjString("ANSI "));
00690       fpPragmas->Add(new TObjString("autocompile "));
00691       fpPragmas->Add(new TObjString("bytecode "));
00692       fpPragmas->Add(new TObjString("compile "));
00693       fpPragmas->Add(new TObjString("endbytecode "));
00694       fpPragmas->Add(new TObjString("endcompile "));
00695       fpPragmas->Add(new TObjString("include "));
00696       fpPragmas->Add(new TObjString("includepath "));
00697       fpPragmas->Add(new TObjString("K&R "));
00698       fpPragmas->Add(new TObjString("link "));
00699       fpPragmas->Add(new TObjString("preprocess "));
00700       fpPragmas->Add(new TObjString("preprocessor "));
00701       fpPragmas->Add(new TObjString("security level"));
00702       // "setertti "  omitted. Ordinary user should not use this statement
00703       // "setstdio "  omitted. Ordinary user should not use this statement
00704       // "setstream " omitted. Ordinary user should not use this statement
00705       // "stub"       omitted. Ordinary user should not use this statement
00706 
00707    }
00708 
00709    return fpPragmas;
00710 }
00711 
00712 //______________________________________________________________________________
00713 const TSeqCollection *TTabCom::GetListOfSysIncFiles()
00714 {
00715    // Return the list of system include files.
00716    if (!fpSysIncFiles) {
00717       fpSysIncFiles = NewListOfFilesInPath(GetSysIncludePath());
00718    }
00719 
00720    return fpSysIncFiles;
00721 }
00722 
00723 //______________________________________________________________________________
00724 const TSeqCollection *TTabCom::GetListOfUsers()
00725 {
00726    // reads from "/etc/passwd"
00727 
00728    if (!fpUsers) {
00729       fpUsers = new TContainer;
00730 
00731       ifstream passwd;
00732       TString user;
00733 
00734       passwd.open("/etc/passwd");
00735       while (passwd) {
00736          user.ReadToDelim(passwd, ':');
00737          fpUsers->Add(new TObjString(user));
00738          passwd.ignore(32000, '\n');
00739       }
00740       passwd.close();
00741    }
00742 
00743    return fpUsers;
00744 }
00745 
00746 //
00747 //              public member functions
00748 //
00749 // ----------------------------------------------------------------------------
00750 
00751 // ----------------------------------------------------------------------------
00752 //
00753 //                           static utility functions
00754 //
00755 
00756 //______________________________________________________________________________
00757 Char_t TTabCom::AllAgreeOnChar(int i, const TSeqCollection * pList,
00758                                Int_t & nGoodStrings)
00759 {
00760    //[static utility function]///////////////////////////////////////////
00761    //
00762    //  if all the strings in "*pList" have the same ith character,
00763    //  that character is returned.
00764    //  otherwise 0 is returned.
00765    //
00766    //  any string "s" for which "ExcludedByFignore(s)" is true
00767    //  will be ignored unless All the strings in "*pList"
00768    //  are "ExcludedByFignore()"
00769    //
00770    //  in addition, the number of strings which were not
00771    //  "ExcludedByFignore()" is returned in "nGoodStrings".
00772    //
00773    /////////////////////////////////////////////////////////////////////////
00774 
00775    assert(pList != 0);
00776 
00777    TIter next(pList);
00778    TObject *pObj;
00779    const char *s;
00780    char ch0;
00781    Bool_t isGood;
00782    Bool_t atLeast1GoodString;
00783 
00784    // init
00785    nGoodStrings = 0;
00786    atLeast1GoodString = kFALSE;
00787 
00788    // first look for a good string
00789    do {
00790       if ((pObj = next())) {
00791          s = pObj->GetName();
00792          isGood = !ExcludedByFignore(s);
00793          if (isGood) {
00794             atLeast1GoodString = kTRUE;
00795             nGoodStrings += 1;
00796          }
00797       } else {
00798          // reached end of list without finding a single good string.
00799          // just use the first one.
00800          next.Reset();
00801          pObj = next();
00802          s = pObj->GetName();
00803          break;
00804       }
00805    }
00806    while (!isGood);
00807 
00808    // found a good string...
00809    ch0 = s[i];
00810 
00811    // all subsequent good strings must have the same ith char
00812    do {
00813       if ((pObj = next())) {
00814          s = pObj->GetName();
00815          isGood = !ExcludedByFignore(s);
00816          if (isGood)
00817             nGoodStrings += 1;
00818       } else
00819          return ch0;
00820    }
00821    while (((int) strlen(s) >= i && s[i] == ch0) ||
00822           (atLeast1GoodString && !isGood));
00823 
00824    return 0;
00825 }
00826 
00827 //______________________________________________________________________________
00828 void TTabCom::AppendListOfFilesInDirectory(const char dirName[],
00829                                            TSeqCollection * pList)
00830 {
00831    //[static utility function]/////////////////////////////
00832    //
00833    //  adds a TObjString to "*pList"
00834    //  for each entry found in the system directory "dirName"
00835    //
00836    //  directories that do not exist are silently ignored.
00837    //
00838    //////////////////////////////////////////////////////////
00839 
00840    assert(dirName != 0);
00841    assert(pList != 0);
00842 
00843    // open the directory
00844    void *dir = gSystem->OpenDirectory(dirName);
00845 
00846    // it is normal for this function to receive names of directories that do not exist.
00847    // they should be ignored and should not generate any error messages.
00848    if (!dir)
00849       return;
00850 
00851    // put each filename in the list
00852    const char *tmp_ptr;         // gSystem->GetDirEntry() returns 0 when no more files.
00853    TString fileName;
00854 
00855    while ((tmp_ptr = gSystem->GetDirEntry(dir))) {
00856       fileName = tmp_ptr;
00857 
00858       // skip "." and ".."
00859       if (fileName == "." || fileName == "..")
00860          continue;
00861 
00862       // add to list
00863       pList->Add(new TObjString(dirName + fileName.Prepend("/")));
00864    }
00865    // NOTE:
00866    // with a path like "/usr/include:/usr/include/CC:$ROOTDIR/include:$ROOTDIR/cint/include:..."
00867    // the above loop could get traversed 700 times or more.
00868    // ==> keep it minimal or it could cost whole seconds on slower machines.
00869    // also: TClonesArray doesn't help.
00870 
00871    // close the directory
00872    gSystem->FreeDirectory(dir);
00873 }
00874 
00875 // -----\/-------- homemade RTTI ---------------\/------------------------
00876 //______________________________________________________________________________
00877 TString TTabCom::DetermineClass(const char varName[])
00878 {
00879    //[static utility function]/////////////////////////////
00880    //
00881    //  returns empty string on failure.
00882    //  otherwise returns something like this: "TROOT*".
00883    //  fails for non-class types (ie, int, char, etc).
00884    //  fails for pointers to functions.
00885    //
00886    ///////////////////////////////////
00887 
00888 
00889    ///////////////////////////////////
00890    //
00891    //  note that because of the strange way this function works,
00892    //  CINT will print
00893    //
00894    //     Error: No symbol asdf in current scope  FILE:/var/tmp/gaaa001HR LINE:1
00895    //
00896    //  if "varName" is not defined. (in this case, varName=="asdf")
00897    //  i don't know how to suppress this.
00898    //
00899    ///////////////////////////////////
00900 
00901    assert(varName != 0);
00902    IfDebug(cerr << "DetermineClass(\"" << varName << "\");" << endl);
00903 
00904    const char *tmpfile = tmpnam(0);
00905    TString cmd("gROOT->ProcessLine(\"");
00906    cmd += varName;
00907    cmd += "\"); > ";
00908    cmd += tmpfile;
00909    cmd += "\n";
00910 
00911    gROOT->ProcessLineSync(cmd.Data());
00912    // the type of the variable whose name is "varName"
00913    // should now be stored on disk in the file "tmpfile"
00914 
00915    TString type = "";
00916    int c;
00917 
00918    // open the file
00919    ifstream file1(tmpfile);
00920    if (!file1) {
00921       Error("TTabCom::DetermineClass", "could not open file \"%s\"",
00922             tmpfile);
00923       goto cleanup;
00924    }
00925    // first char should be '(', which we can ignore.
00926    c = file1.get();
00927    if (!file1 || c <= 0 || c == '*' || c != '(') {
00928       Error("TTabCom::DetermineClass", "variable \"%s\" not defined?",
00929             varName);
00930       goto cleanup;
00931    }
00932    IfDebug(cerr << (char) c << flush);
00933 
00934    // in case of success, "class TClassName*)0x12345" remains,
00935    // since the opening '(' was removed.
00936    file1 >> type;               // ignore "class"
00937 
00938    // non-class type ==> failure
00939    if (type == "const")
00940       file1 >> type;
00941 
00942    if (type != "class" && type != "struct") {
00943       type = "";                // empty return string indicates failure.
00944       goto cleanup;             //* RETURN *//
00945    }
00946    // ignore ' '
00947    c = file1.get();
00948    IfDebug(cerr << (char) c << flush);
00949 
00950    // this is what we want
00951    type.ReadToDelim(file1, ')');
00952    IfDebug(cerr << type << endl);
00953 
00954    // new version of CINT returns: "class TClassName*const)0x12345"
00955    // so we have to strip off "const"
00956    if (type.EndsWith("const"))
00957       type.Remove(type.Length() - 5);
00958 
00959 cleanup:
00960    // done reading from file
00961    file1.close();
00962    gSystem->Unlink(tmpfile);
00963 
00964    return type;
00965 }
00966 
00967 //______________________________________________________________________________
00968 Bool_t TTabCom::ExcludedByFignore(TString s)
00969 {
00970    //[static utility function]/////////////////////////////
00971    //
00972    //  returns true iff "s" ends with one of
00973    //  the strings listed in the "TabCom.FileIgnore" resource.
00974    //
00975    /////////////////////////////////////////////////////////////
00976 
00977    const char *fignore = gEnv->GetValue("TabCom.FileIgnore", (char *) 0);
00978 
00979    if (!fignore) {
00980       return kFALSE;
00981    } else {
00982 #ifdef R__SSTREAM
00983       istringstream endings((char *) fignore);
00984 #else
00985       istrstream endings((char *) fignore);  // do i need to make a copy first?
00986 #endif
00987       TString ending;
00988 
00989       ending.ReadToDelim(endings, kDelim);
00990 
00991       while (!ending.IsNull()) {
00992          if (s.EndsWith(ending))
00993             return kTRUE;
00994          else
00995             ending.ReadToDelim(endings, kDelim);  // next
00996       }
00997       return kFALSE;
00998    }
00999 }
01000 
01001 //______________________________________________________________________________
01002 TString TTabCom::GetSysIncludePath()
01003 {
01004    //[static utility function]/////////////////////////////
01005    //
01006    //  returns a colon-separated string of directories
01007    //  that CINT will search when you call #include<...>
01008    //
01009    //  returns empty string on failure.
01010    //
01011    ///////////////////////////////////////////////////////////
01012 
01013    // >i noticed that .include doesn't list the standard directories like
01014    // >/usr/include or /usr/include/CC.
01015    // >
01016    // >how can i get a list of all the directories the interpreter will
01017    // >search through when the user does a #include<...> ?
01018    //
01019    // Right now, there is no easy command to tell you about it.  Instead, I can
01020    // describe it here.
01021    //
01022    // 1) CINT first searches current working directory for #include "xxx"
01023    //   (#include <xxx> does not)
01024    //
01025    // 2) CINT searches include path directories given by -I option
01026    //
01027    // 3) CINT searches following standard include directories.
01028    //    $CINTSYSDIR/include
01029    //    $CINTSYSDIR/stl
01030    //    $CINTSYSDIR/msdev/include   if VC++4.0
01031    //    $CINTSYSDIR/sc/include      if Symantec C++
01032    //    /usr/include
01033    //    /usr/include/g++            if gcc,g++
01034    //    /usr/include/CC             if HP-UX
01035    //    /usr/include/codelibs       if HP-UX
01036    //
01037    // .include command only displays 2).
01038    //
01039    // Thank you
01040    // Masaharu Goto
01041 
01042    // 1) current dir
01043    // ----------------------------------------------
01044    // N/A
01045 
01046 
01047    // 2) -I option (and #pragma includepath)
01048    // ----------------------------------------------
01049 
01050    // get this part of the include path from the interpreter
01051    // and stick it in a tmp file.
01052    const char *tmpfilename = tmpnam(0);
01053 
01054    FILE *fout = fopen(tmpfilename, "w");
01055    if (!fout) return "";
01056    gCint->DisplayIncludePath(fout);
01057    fclose(fout);
01058 
01059    // open the tmp file
01060    ifstream file1(tmpfilename);
01061    if (!file1) {                // error
01062       Error("TTabCom::GetSysIncludePath", "could not open file \"%s\"",
01063             tmpfilename);
01064       gSystem->Unlink(tmpfilename);
01065       return "";
01066    }
01067    // parse it.
01068    TString token;               // input buffer
01069    TString path;                // all directories so far (colon-separated)
01070    file1 >> token;              // skip "include"
01071    file1 >> token;              // skip "path:"
01072    while (file1) {
01073       file1 >> token;
01074       if (!token.IsNull()) {
01075          if (path.Length() > 0)
01076             path.Append(":");
01077          path.Append(token.Data() + 2);  // +2 skips "-I"
01078       }
01079    }
01080 
01081    // done with the tmp file
01082    file1.close();
01083    gSystem->Unlink(tmpfilename);
01084 
01085    // 3) standard directories
01086    // ----------------------------------------------
01087 
01088 #ifndef CINTINCDIR
01089    TString sCINTSYSDIR("$ROOTSYS/cint");
01090 #else
01091    TString sCINTSYSDIR(CINTINCDIR);
01092 #endif
01093    path.Append(":" + sCINTSYSDIR + "/include");
01094 //   path.Append(":"+CINTSYSDIR+"/stl");
01095 //   path.Append(":"+CINTSYSDIR+"/msdev/include");
01096 //   path.Append(":"+CINTSYSDIR+"/sc/include");
01097    path.Append(":/usr/include");
01098 //   path.Append(":/usr/include/g++");
01099 //   path.Append(":/usr/include/CC");
01100 //   path.Append(":/usr/include/codelibs");
01101 
01102    return path;
01103 }
01104 
01105 //______________________________________________________________________________
01106 Bool_t TTabCom::IsDirectory(const char fileName[])
01107 {
01108    //[static utility function]/////////////////////////////
01109    //
01110    //  calls TSystem::GetPathInfo() to see if "fileName"
01111    //  is a system directory.
01112    //
01113    ///////////////////////////////////////////////////////
01114 
01115    FileStat_t stat;
01116    gSystem->GetPathInfo(fileName, stat);
01117    return R_ISDIR(stat.fMode);
01118 }
01119 
01120 //______________________________________________________________________________
01121 TSeqCollection *TTabCom::NewListOfFilesInPath(const char path1[])
01122 {
01123    //[static utility function]/////////////////////////////
01124    //
01125    //  creates a list containing the full path name for each file
01126    //  in the (colon separated) string "path1"
01127    //
01128    //  memory is allocated with "new", so
01129    //  whoever calls this function takes responsibility for deleting it.
01130    //
01131    //////////////////////////////////////////////////////////////////////
01132 
01133    assert(path1 != 0);
01134    if (!path1[0]) path1 = ".";
01135 
01136    TContainer *pList = new TContainer;  // maybe use RTTI here? (since its a static function)
01137 #ifdef R__SSTREAM
01138    istringstream path((char *) path1);
01139 #else
01140    istrstream path((char *) path1);
01141 #endif
01142 
01143    while (path.good())
01144    {
01145       TString dirName;
01146       dirName.ReadToDelim(path, kDelim);
01147       if (dirName.IsNull())
01148          continue;
01149 
01150       IfDebug(cerr << "NewListOfFilesInPath(): dirName = " << dirName <<
01151               endl);
01152 
01153       AppendListOfFilesInDirectory(dirName, pList);
01154    }
01155 
01156    return pList;
01157 }
01158 
01159 //______________________________________________________________________________
01160 Bool_t TTabCom::PathIsSpecifiedInFileName(const TString & fileName)
01161 {
01162    //[static utility function]/////////////////////////////
01163    //
01164    //  true if "fileName"
01165    //  1. is an absolute path ("/tmp/a")
01166    //  2. is a relative path  ("../whatever", "./test")
01167    //  3. starts with user name ("~/mail")
01168    //  4. starts with an environment variable ("$ROOTSYS/bin")
01169    //
01170    //////////////////////////////////////////////////////////////////////////
01171 
01172    char c1 = (fileName.Length() > 0) ? fileName[0] : 0;
01173    return c1 == '/' || c1 == '~' || c1 == '$' || fileName.BeginsWith("./")
01174        || fileName.BeginsWith("../");
01175 }
01176 
01177 //______________________________________________________________________________
01178 void TTabCom::NoMsg(Int_t errorLevel)
01179 {
01180    //[static utility function]/////////////////////////////
01181    //
01182    //  calling "NoMsg( errorLevel )",
01183    //  sets "gErrorIgnoreLevel" to "errorLevel+1" so that
01184    //  all errors with "level < errorLevel" will be ignored.
01185    //
01186    //  calling the function with a negative argument
01187    //  (e.g., "NoMsg( -1 )")
01188    //  resets gErrorIgnoreLevel to its previous value.
01189    //
01190    //////////////////////////////////////////////////////////////////
01191 
01192    ////////////////////////////////////////////////////////////////
01193    //
01194    // if you call the function twice with a non-negative argument
01195    // (without an intervening call with a negative argument)
01196    // it will complain because it is almost certainly an error
01197    // that will cause the function to loose track of the previous
01198    // value of gErrorIgnoreLevel.
01199    //
01200    // most common causes: 1. suspiciously placed "return;" statement
01201    //                     2. calling a function that calls "NoMsg()"
01202    //
01203    //////////////////////////////////////////////////////////////////
01204 
01205    const Int_t kNotDefined = -2;
01206    static Int_t old_level = kNotDefined;
01207 
01208    if (errorLevel < 0)          // reset
01209    {
01210       if (old_level == kNotDefined) {
01211          cerr << "NoMsg(): ERROR 1. old_level==" << old_level << endl;
01212          return;
01213       }
01214 
01215       gErrorIgnoreLevel = old_level;  // restore
01216       old_level = kNotDefined;
01217    } else                       // set
01218    {
01219       if (old_level != kNotDefined) {
01220          cerr << "NoMsg(): ERROR 2. old_level==" << old_level << endl;
01221          return;
01222       }
01223 
01224       old_level = gErrorIgnoreLevel;
01225       if (gErrorIgnoreLevel <= errorLevel)
01226          gErrorIgnoreLevel = errorLevel + 1;
01227    }
01228 }
01229 
01230 //
01231 //                           static utility functions
01232 //
01233 // ----------------------------------------------------------------------------
01234 
01235 
01236 // ----------------------------------------------------------------------------
01237 //
01238 //                       private member functions
01239 //
01240 //
01241 
01242 //______________________________________________________________________________
01243 Int_t TTabCom::Complete(const TRegexp & re,
01244                         const TSeqCollection * pListOfCandidates,
01245                         const char appendage[],
01246                         TString::ECaseCompare cmp)
01247 {
01248    // [private]
01249 
01250    // returns position of first change in buffer
01251    // ------------------------------------------
01252    // -2 ==> new line altogether (whole thing needs to be redrawn, including prompt)
01253    // -1 ==> no changes
01254    //  0 ==> beginning of line
01255    //  1 ==> after 1st char
01256    //  n ==> after nth char
01257 
01258    IfDebug(cerr << "TTabCom::Complete() ..." << endl);
01259    assert(fpLoc != 0);
01260    assert(pListOfCandidates != 0);
01261 
01262    Int_t pos = 0;               // position of first change
01263    const int loc = *fpLoc;      // location where TAB was pressed
01264 
01265    // -----------------------------------------
01266    //
01267    // 1. get the substring we need to complete
01268    //
01269    // NOTES:
01270    // s1 = original buffer
01271    // s2 = sub-buffer from 0 to wherever the user hit TAB
01272    // s3 = the actual text that needs completing
01273    //
01274    // -----------------------------------------
01275    TString s1(fBuf);
01276    TString s2 = s1(0, loc);
01277    TString s3 = s2(re);
01278 
01279    int start = s2.Index(re);
01280 
01281    IfDebug(cerr << "   s1: " << s1 << endl);
01282    IfDebug(cerr << "   s2: " << s2 << endl);
01283    IfDebug(cerr << "   s3: " << s3 << endl);
01284    IfDebug(cerr << "start: " << start << endl);
01285    IfDebug(cerr << endl);
01286 
01287    // -----------------------------------------
01288    // 2. go through each possible completion,
01289    //    keeping track of the number of matches
01290    // -----------------------------------------
01291    TList listOfMatches;         // list of matches (local filenames only) (insertion order must agree across these 3 lists)
01292    TList listOfFullPaths;       // list of matches (full filenames)       (insertion order must agree across these 3 lists)
01293    listOfMatches.SetOwner();
01294    listOfFullPaths.SetOwner();
01295 
01296    int nMatches = 0;            // number of matches
01297    TObject *pObj;               // pointer returned by iterator
01298    TIter next_candidate(pListOfCandidates);
01299    TIter next_match(&listOfMatches);
01300    TIter next_fullpath(&listOfFullPaths);
01301 
01302    // stick all matches into "listOfMatches"
01303    while ((pObj = next_candidate())) {
01304       // get the full filename
01305       const char *s4 = pObj->GetName();
01306 
01307       assert(s4 != 0);
01308 
01309       // pick off tail
01310       const char *s5 = strrchr(s4, '/');
01311       if (!s5)
01312          s5 = s4;               // no '/' found
01313       else
01314          s5 += 1;               // advance past '/'
01315 
01316       // if case sensitive (normal behaviour), check for match
01317       // if case insensitive, convert to TString and compare case insensitively
01318       if ((cmp == TString::kExact) && (strstr(s5, s3) == s5)) {
01319          nMatches += 1;
01320          listOfMatches.Add(new TObjString(s5));
01321          listOfFullPaths.Add(new TObjString(s4));
01322          IfDebug(cerr << "adding " << s5 << '\t' << s4 << endl);
01323       } else if (cmp == TString::kIgnoreCase) {
01324          TString ts5(s5);
01325          if (ts5.BeginsWith(s3, cmp))
01326          {
01327             nMatches += 1;
01328             listOfMatches.Add(new TObjString(s5));
01329             listOfFullPaths.Add(new TObjString(s4));
01330             IfDebug(cerr << "adding " << s5 << '\t' << s4 << endl);
01331          }
01332       } else {
01333 //rdm         IfDebug(cerr << "considered " << s5 << '\t' << s4 << endl);
01334       }
01335 
01336    }
01337 
01338    // -----------------------------------------
01339    // 3. beep, list, or complete
01340    //    depending on how many matches were found
01341    // -----------------------------------------
01342 
01343    // 3a. no matches ==> bell
01344    TString partialMatch = "";
01345 
01346    if (nMatches == 0) {
01347       // Ring a bell!
01348       gSystem->Beep();
01349       pos = -1;
01350       goto done;                //* RETURN *//
01351    }
01352    // 3b. one or more matches.
01353    char match[1024];
01354 
01355    if (nMatches == 1) {
01356       // get the (lone) match
01357       const char *short_name = next_match()->GetName();
01358       const char *full_name = next_fullpath()->GetName();
01359 
01360       pObj = pListOfCandidates->FindObject(short_name);
01361       if (pObj) {
01362          IfDebug(cerr << endl << "class: " << pObj->ClassName() << endl);
01363          TString className = pObj->ClassName();
01364          if (0);
01365          else if (className == "TMethod" || className == "TFunction") {
01366             TFunction *pFunc = (TFunction *) pObj;
01367             if (0 == pFunc->GetNargs())
01368                appendage = "()";  // no args
01369             else
01370                appendage = "("; // user needs to supply some args
01371          } else if (className == "TDataMember") {
01372             appendage = " ";
01373          }
01374       }
01375 
01376       CopyMatch(match, short_name, appendage, full_name);
01377    } else {
01378       // multiple matches ==> complete as far as possible
01379       Char_t ch;
01380       Int_t nGoodStrings;
01381 
01382       for (int i = 0;
01383            (ch = AllAgreeOnChar(i, &listOfMatches, nGoodStrings));
01384            i += 1) {
01385          IfDebug(cerr << " i=" << i << " ch=" << ch << endl);
01386          partialMatch.Append(ch);
01387       }
01388 
01389       const char *s;
01390       const char *s0;
01391 
01392       // multiple matches, but maybe only 1 of them is any good.
01393       if (nGoodStrings == 1) {
01394 
01395          // find the 1 good match
01396          do {
01397             s = next_match()->GetName();
01398             s0 = next_fullpath()->GetName();
01399          }
01400          while (ExcludedByFignore(s));
01401 
01402          // and use it.
01403          CopyMatch(match, s, appendage, s0);
01404       } else {
01405          IfDebug(cerr << "more than 1 GoodString" << endl);
01406 
01407          if (partialMatch.Length() > s3.Length())
01408             // this partial match is our (partial) completion.
01409          {
01410             CopyMatch(match, partialMatch.Data());
01411          } else
01412             // couldn't do any completing at all,
01413             // print a list of all the ambiguous matches
01414             // (except for those excluded by "FileIgnore")
01415          {
01416             IfDebug(cerr << "printing ambiguous matches" << endl);
01417             cout << endl;
01418             while ((pObj = next_match())) {
01419                s = pObj->GetName();
01420                s0 = next_fullpath()->GetName();
01421                if (!ExcludedByFignore(s) || nGoodStrings == 0) {
01422                   if (IsDirectory(s0))
01423                      cout << s << "/" << endl;
01424                   else
01425                      cout << s << endl;
01426                }
01427             }
01428             pos = -2;
01429             if (cmp == TString::kExact || partialMatch.Length() < s3.Length()) {
01430                goto done;          //* RETURN *//
01431             } // else:
01432             // update the matching part, will have changed
01433             // capitalization because only cmp == TString::kIgnoreCase
01434             // matches.
01435             CopyMatch(match, partialMatch.Data());
01436          }
01437       }
01438    }
01439 
01440 
01441    // ---------------------------------------
01442    // 4. finally write text into the buffer.
01443    // ---------------------------------------
01444    {
01445       int i = strlen(fBuf);     // old EOL position is i
01446       int l = strlen(match) - (loc - start);  // new EOL position will be i+L
01447 
01448       // first check for overflow
01449       if (strlen(fBuf) + strlen(match) + 1 > BUF_SIZE) {
01450          Error("TTabCom::Complete", "buffer overflow");
01451          pos = -2;
01452          goto done;             /* RETURN */
01453       }
01454       // debugging output
01455       IfDebug(cerr << "  i=" << i << endl);
01456       IfDebug(cerr << "  L=" << l << endl);
01457       IfDebug(cerr << "loc=" << loc << endl);
01458 
01459       // slide everything (including the null terminator) over to make space
01460       for (; i >= loc; i -= 1) {
01461          fBuf[i + l] = fBuf[i];
01462       }
01463 
01464       // insert match
01465       strncpy(fBuf + start, match, strlen(match));
01466 
01467       // the "get"->"Get" case of TString::kIgnore sets pos to -2
01468       // and falls through to update the buffer; we need to return
01469       // -2 in that case, so check here:
01470       if (pos != -2) {
01471          pos = loc;                // position of first change in "fBuf"
01472          if (cmp == TString::kIgnoreCase && pos < 0) {
01473             // We might have changed somthing before loc, due to differences in
01474             // capitalization. So return start:
01475             pos = start;
01476          }
01477       }
01478       *fpLoc = loc + l;         // new cursor position
01479    }
01480 
01481 done:                         // <----- goto label
01482    // un-init
01483    fpLoc = 0;
01484    fBuf = 0;
01485 
01486    return pos;
01487 }
01488 
01489 //______________________________________________________________________________
01490 void TTabCom::CopyMatch(char dest[], const char localName[],
01491                         const char appendage[],
01492                         const char fullName[]) const
01493 {
01494    // [private]
01495 
01496    // if "appendage" is 0, no appendage is applied.
01497    //
01498    // if "appendage" is of the form "filenameXXX" then,
01499    // "filename" is ignored and "XXX" is taken to be the appendage,
01500    // but it will only be applied if the file is not a directory...
01501    // if the file is a directory, a "/" will be used for the appendage instead.
01502    //
01503    // if "appendage" is of the form "XXX" then "XXX" will be appended to the match.
01504 
01505    assert(dest != 0);
01506    assert(localName != 0);
01507 
01508    // potential buffer overflow.
01509    strcpy(dest, localName);
01510 
01511    const char *key = "filename";
01512    const int key_len = strlen(key);
01513 
01514    IfDebug(cerr << "CopyMatch()." << endl);
01515    IfDebug(cerr << "localName: " << (localName ? localName : "0") <<
01516            endl);
01517    IfDebug(cerr << "appendage: " << (appendage ? appendage : "0") <<
01518            endl);
01519    IfDebug(cerr << " fullName: " << (fullName ? fullName : "0") <<
01520            endl);
01521 
01522 
01523    // check to see if "appendage" starts with "key"
01524    if (appendage && strncmp(appendage, key, key_len) == 0) {
01525       // filenames get special treatment
01526       appendage += key_len;
01527       IfDebug(cerr << "new appendage: " << appendage << endl);
01528       if (IsDirectory(fullName)) {
01529          if (fullName)
01530             strcpy(dest + strlen(localName), "/");
01531       } else {
01532          if (appendage)
01533             strcpy(dest + strlen(localName), appendage);
01534       }
01535    } else {
01536       if (appendage)
01537          strcpy(dest + strlen(localName), appendage);
01538    }
01539 }
01540 
01541 //______________________________________________________________________________
01542 TTabCom::EContext_t TTabCom::DetermineContext() const
01543 {
01544    // [private]
01545 
01546    assert(fBuf != 0);
01547 
01548    const char *pStart;          // start of match
01549    const char *pEnd;            // end of match
01550 
01551    for (int context = 0; context < kNUM_PAT; ++context) {
01552       pEnd = Matchs(fBuf, *fpLoc, fPat[context], &pStart);
01553       if (pEnd) {
01554          IfDebug(cerr << endl
01555                  << "context=" << context << " "
01556                  << "RegExp=" << fRegExp[context]
01557                  << endl);
01558          return EContext_t(context);  //* RETURN *//
01559       }
01560    }
01561 
01562    return kUNKNOWN_CONTEXT;     //* RETURN *//
01563 }
01564 
01565 //______________________________________________________________________________
01566 TString TTabCom::DeterminePath(const TString & fileName,
01567                                const char defaultPath[]) const
01568 {
01569    // [private]
01570 
01571    if (PathIsSpecifiedInFileName(fileName)) {
01572       TString path = fileName;
01573       gSystem->ExpandPathName(path);
01574       Int_t end = path.Length()-1;
01575       if (end>0 && path[end]!='/' && path[end]!='\\') {
01576          path = gSystem->DirName(path);
01577       }
01578       return path;
01579    } else {
01580       TString newBase;
01581       TString extendedPath;
01582       if (fileName.Contains("/")) {
01583          Int_t end = fileName.Length()-1;
01584          if (fileName[end] != '/' && fileName[end] != '\\') {
01585             newBase = gSystem->DirName(fileName);
01586          } else {
01587             newBase = fileName;
01588          }
01589          extendedPath = ExtendPath(defaultPath, newBase);
01590       } else {
01591          newBase = "";
01592          extendedPath = defaultPath;
01593       }
01594       IfDebug(cerr << endl);
01595       IfDebug(cerr << "    fileName: " << fileName << endl);
01596       IfDebug(cerr << "    pathBase: " << newBase << endl);
01597       IfDebug(cerr << " defaultPath: " << defaultPath << endl);
01598       IfDebug(cerr << "extendedPath: " << extendedPath << endl);
01599       IfDebug(cerr << endl);
01600 
01601       return extendedPath;
01602    }
01603 }
01604 
01605 //______________________________________________________________________________
01606 TString TTabCom::ExtendPath(const char originalPath[], TString newBase) const
01607 {
01608    // [private]
01609 
01610    if (newBase.BeginsWith("/"))
01611       newBase.Remove(TString::kLeading, '/');
01612 #ifdef R__SSTREAM
01613    stringstream str;
01614 #else
01615    strstream str;
01616 #endif
01617    TString dir;
01618    TString newPath;
01619    str << originalPath;
01620 
01621    while (str.good())
01622    {
01623       dir = "";
01624       dir.ReadToDelim(str, kDelim);
01625       if (dir.IsNull())
01626          continue;              // ignore blank entries
01627       newPath.Append(dir);
01628       if (!newPath.EndsWith("/"))
01629          newPath.Append("/");
01630       newPath.Append(newBase);
01631       newPath.Append(kDelim);
01632    }
01633 
01634    return newPath.Strip(TString::kTrailing, kDelim);
01635 }
01636 
01637 //______________________________________________________________________________
01638 Int_t TTabCom::Hook(char *buf, int *pLoc)
01639 {
01640    // [private]
01641 
01642    // initialize
01643    fBuf = buf;
01644    fpLoc = pLoc;
01645 
01646    // frodo: iteration counter for recursive MakeClassFromVarName
01647    fLastIter = 0;
01648 
01649    // default
01650    Int_t pos = -2;  // position of the first character that was changed in the buffer (needed for redrawing)
01651 
01652    // get the context this tab was triggered in.
01653    EContext_t context = DetermineContext();
01654 
01655    // get the substring that triggered this tab (as defined by "SetPattern()")
01656    const char dummy[] = ".";
01657    TRegexp re1(context == kUNKNOWN_CONTEXT ? dummy : fRegExp[context]);
01658    TString s1(fBuf);
01659    TString s2 = s1(0, *fpLoc);
01660    TString s3 = s2(re1);
01661 
01662    switch (context) {
01663    case kUNKNOWN_CONTEXT:
01664       cerr << endl << "tab completion not implemented for this context" <<
01665           endl;
01666       pos = -2;
01667       break;
01668 
01669    case kSYS_UserName:
01670       {
01671          const TSeqCollection *pListOfUsers = GetListOfUsers();
01672 
01673          pos = Complete("[^~]*$", pListOfUsers, "/");
01674       }
01675       break;
01676    case kSYS_EnvVar:
01677       {
01678          const TSeqCollection *pEnv = GetListOfEnvVars();
01679 
01680          pos = Complete("[^$]*$", pEnv, "");
01681       }
01682       break;
01683 
01684    case kCINT_stdout:
01685    case kCINT_stderr:
01686    case kCINT_stdin:
01687       {
01688          const TString fileName = s3("[^ ><]*$");
01689          const TString filePath = DeterminePath(fileName,0);
01690          const TSeqCollection *pListOfFiles =
01691              GetListOfFilesInPath(filePath.Data());
01692 
01693 //             pos = Complete( "[^ /]*$", pListOfFiles, " " );
01694          pos = Complete("[^ /]*$", pListOfFiles, "filename ");
01695       }
01696       break;
01697 
01698    case kCINT_Edit:
01699    case kCINT_Load:
01700    case kCINT_Exec:
01701    case kCINT_EXec:
01702       {
01703          const TString fileName = s3("[^ ]*$");
01704          const TString macroPath =
01705              DeterminePath(fileName, TROOT::GetMacroPath());
01706          const TSeqCollection *pListOfFiles =
01707              GetListOfFilesInPath(macroPath.Data());
01708 
01709 //             pos = Complete( "[^ /]*$", pListOfFiles, " " );
01710          pos = Complete("[^ /]*$", pListOfFiles, "filename ");
01711       }
01712       break;
01713 
01714    case kCINT_pragma:
01715       {
01716          pos = Complete("[^ ]*$", GetListOfPragmas(), "");
01717       }
01718       break;
01719    case kCINT_includeSYS:
01720       {
01721          TString fileName = s3("[^<]*$");
01722          if (PathIsSpecifiedInFileName(fileName) || fileName.Contains("/")) {
01723             TString includePath =
01724                 DeterminePath(fileName, GetSysIncludePath());
01725 
01726 //                  pos = Complete( "[^</]*$", GetListOfFilesInPath( includePath ), "> " );
01727             pos =
01728                 Complete("[^</]*$", GetListOfFilesInPath(includePath),
01729                          "filename> ");
01730          } else {
01731 //                  pos = Complete( "[^</]*$", GetListOfSysIncFiles(), "> " );
01732             pos =
01733                 Complete("[^</]*$", GetListOfSysIncFiles(), "filename> ");
01734          }
01735       }
01736       break;
01737    case kCINT_includePWD:
01738       {
01739          const TString fileName = s3("[^\"]*$");
01740          const TString includePath = DeterminePath(fileName, ".");
01741          const TSeqCollection *pListOfFiles =
01742              GetListOfFilesInPath(includePath.Data());
01743 
01744 //             pos = Complete( "[^\"/]*$", pListOfFiles, "\" " );
01745          pos = Complete("[^\"/]*$", pListOfFiles, "filename\" ");
01746       }
01747       break;
01748 
01749    case kCINT_cpp:
01750       {
01751          pos = Complete("[^# ]*$", GetListOfCppDirectives(), " ");
01752       }
01753       break;
01754 
01755    case kROOT_Load:
01756       {
01757          const TString fileName = s3("[^\"]*$");
01758 //             const TString  dynamicPath  = DeterminePath( fileName, TROOT::GetDynamicPath() ); /* should use this one */
01759          const TString dynamicPath = DeterminePath(fileName,gEnv->GetValue("Root.DynamicPath",(char *) 0));
01760          const TSeqCollection *pListOfFiles = GetListOfFilesInPath(dynamicPath);
01761 
01762 //             pos = Complete( "[^\"/]*$", pListOfFiles, "\");" );
01763          pos = Complete("[^\"/]*$", pListOfFiles, "filename\");");
01764       }
01765       break;
01766 
01767    case kSYS_FileName:
01768       {
01769          const TString fileName = s3("[^ \"]*$");
01770          const TString filePath = DeterminePath(fileName,".");
01771          const TSeqCollection *pListOfFiles = GetListOfFilesInPath(filePath.Data());
01772 
01773          pos = Complete("[^\" /]*$", pListOfFiles, "filename\"");
01774       }
01775       break;
01776 
01777    case kCXX_ScopeMember:
01778       {
01779          const EContext_t original_context = context;  // save this for later
01780 
01781          TClass *pClass;
01782          // may be a namespace, class, object, or pointer
01783          TString name = s3("^[_a-zA-Z][_a-zA-Z0-9]*");
01784 
01785          IfDebug(cerr << endl);
01786          IfDebug(cerr << "name: " << '"' << name << '"' << endl);
01787 
01788          // We need to decompose s3 a little more:
01789          // The part name is the partial symbol at the end of ::
01790          // eg. given s3 = "foo::bar::part" ,  partname = "part"
01791          TString partname = s3("[_a-zA-Z][_a-zA-Z0-9]*$");
01792 
01793          // The prefix, considering the s3 = "foo::bar::part" example would be
01794          // prefix = "foo::bar::". prefix equals the empty string if there is only one
01795          // or no set of colons in s3.
01796          // Note: we reconstruct the fully qualified name with a while loop because
01797          // it does not seem that TRegexp can handle something like "([_a-zA-Z][_a-zA-Z0-9]*::)+$"
01798          TString prefix = "";
01799          TString str = s2;
01800          str.Remove(str.Length() - partname.Length(), partname.Length());
01801          while (1) {
01802             TString sym = str("[_a-zA-Z][_a-zA-Z0-9]*::$");
01803             if (sym.Length() == 0)
01804                break;
01805             str.Remove(str.Length() - sym.Length(), sym.Length());
01806             prefix = sym + prefix;
01807          }
01808 
01809          // Not the preprefix would be = "foo::" from our previous example or the empty
01810          // string, "" if there is only one or no set of colons in prefix, eg. prefix = "bar::"
01811          TString preprefix = prefix;
01812          TString sym = prefix("[_a-zA-Z][_a-zA-Z0-9]*::$");
01813          preprefix.Remove(preprefix.Length() - sym.Length(), sym.Length());
01814 
01815          IfDebug(cerr << "prefix: " << '"' << prefix << '"' << endl);
01816          IfDebug(cerr << "preprefix: " << '"' << preprefix << '"' << endl);
01817 
01818          TString namesp = prefix;
01819          if (namesp.Length() >= 2)
01820             namesp.Remove(namesp.Length() - 2, 2);  // Remove the '::' at the end of the string.
01821          IfDebug(cerr << "namesp: " << '"' << namesp << '"' << endl);
01822 
01823          // Make sure autoloading happens (if it can).
01824          delete TryMakeClassFromClassName(namesp);
01825 
01826          // Sometimes, eg on startup of ROOT fpNamespaces might be 0,
01827          // so create and fill the array.
01828          if (!fpNamespaces)
01829             RehashClasses();
01830 
01831          // Try find the namesp string in the list of namespaces. If its found then
01832          // we need to treat the different prefices a little differently:
01833          TObjString objstr(namesp);
01834          TObjString *foundstr = 0;
01835          if (fpNamespaces)
01836             foundstr = (TObjString *)fpNamespaces->FindObject(&objstr);
01837          if (foundstr) {
01838             TContainer *pList = new TContainer;
01839 
01840             // Add all classes to pList that contain the prefix, i.e. are in the
01841             // specified namespace.
01842             const TSeqCollection *tmp = GetListOfClasses();
01843             if (!tmp) break;
01844 
01845             Int_t i;
01846             for (i = 0; i < tmp->GetSize(); i++) {
01847                TString astr = ((TObjString *) tmp->At(i))->String();
01848                TString rxp = "^";
01849                rxp += prefix;
01850                if (astr.Contains(TRegexp(rxp))) {
01851                   astr.Remove(0, prefix.Length());
01852                   TString s = astr("^[^: ]*");
01853                   TObjString *ostr = new TObjString(s);
01854                   if (!pList->Contains(ostr))
01855                      pList->Add(ostr);
01856                   else
01857                      delete ostr;
01858                }
01859             }
01860 
01861             // Add all the sub-namespaces in the specified namespace.
01862             for (i = 0; i < fpNamespaces->GetSize(); i++) {
01863                TString astr =
01864                    ((TObjString *) fpNamespaces->At(i))->String();
01865                TString rxp = "^";
01866                rxp += prefix;
01867                if (astr.Contains(TRegexp(rxp))) {
01868                   astr.Remove(0, prefix.Length());
01869                   TString s = astr("^[^: ]*");
01870                   TObjString *ostr = new TObjString(s);
01871                   if (!pList->Contains(ostr))
01872                      pList->Add(ostr);
01873                   else
01874                      delete ostr;
01875                }
01876             }
01877 
01878             // If a class with the same name as the Namespace name exists then
01879             // add it to the pList. (I don't think the C++ spec allows for this
01880             // but do this anyway, cant harm).
01881             pClass = TryMakeClassFromClassName(preprefix + name);
01882             if (pClass) {
01883                pList->AddAll(pClass->GetListOfAllPublicMethods());
01884                pList->AddAll(pClass->GetListOfAllPublicDataMembers());
01885             }
01886 
01887             pos = Complete("[^: ]*$", pList, "");
01888 
01889             delete pList;
01890             if (pClass)
01891                delete pClass;
01892          } else {
01893             pClass = MakeClassFromClassName(preprefix + name);
01894             if (!pClass) {
01895                pos = -2;
01896                break;
01897             }
01898 
01899             TContainer *pList = new TContainer;
01900 
01901             pList->AddAll(pClass->GetListOfAllPublicMethods());
01902             pList->AddAll(pClass->GetListOfAllPublicDataMembers());
01903 
01904             pos = Complete("[^: ]*$", pList, "(");
01905 
01906             delete pList;
01907             delete pClass;
01908          }
01909 
01910          if (context != original_context)
01911             pos = -2;
01912       }
01913       break;
01914 
01915    case kCXX_DirectMember:
01916    case kCXX_IndirectMember:
01917       {
01918          const EContext_t original_context = context;  // save this for later
01919 
01920          TClass *pClass;
01921 
01922          // frodo: Instead of just passing the last portion of the string to
01923          //        MakeClassFromVarName(), we now pass the all string and let
01924          //        it decide how to handle it... I know it's not the best way
01925          //        because of the context handling, but I wanted to "minimize"
01926          //        the changes to the current code and this seemed the best way
01927          //        to do it
01928          TString name = s1("[_a-zA-Z][-_a-zA-Z0-9<>():.]*$");
01929 
01930          IfDebug(cerr << endl);
01931          IfDebug(cerr << "name: " << '"' << name << '"' << endl);
01932 
01933          switch (context) {
01934          case kCXX_DirectMember:
01935             pClass = MakeClassFromVarName(name, context);
01936             break;
01937          case kCXX_IndirectMember:
01938             pClass = MakeClassFromVarName(name, context);
01939             break;
01940          default:
01941             assert(0);
01942             break;
01943          }
01944          if (!pClass) {
01945             pos = -2;
01946             break;
01947          }
01948 
01949          TContainer *pList = new TContainer;
01950 
01951          pList->AddAll(pClass->GetListOfAllPublicMethods());
01952          pList->AddAll(pClass->GetListOfAllPublicDataMembers());
01953 
01954          switch (context) {
01955          case kCXX_DirectMember:
01956             {
01957                int* store_fpLoc = fpLoc;
01958                char* store_fBuf = fBuf;
01959                pos = Complete("[^. ]*$", pList, "(");
01960                if (pos == -1) {
01961                   fpLoc = store_fpLoc;
01962                   fBuf = store_fBuf;
01963                   pos = Complete("[^. ]*$", pList, "(", TString::kIgnoreCase);
01964                }
01965                break;
01966             }
01967          case kCXX_IndirectMember:
01968             pos = Complete("[^> ]*$", pList, "(");
01969             break;
01970          default:
01971             assert(0);
01972             break;
01973          }
01974 
01975          delete pList;
01976          delete pClass;
01977 
01978          if (context != original_context)
01979             pos = -2;
01980       }
01981       break;
01982 
01983    case kCXX_ScopeProto:
01984       {
01985          const EContext_t original_context = context;  // save this for later
01986 
01987          // get class
01988          TClass *pClass;
01989          TString name = s3("^[_a-zA-Z][_a-zA-Z0-9]*");
01990          // "name" may now be the name of a class, object, or pointer
01991 
01992          IfDebug(cerr << endl);
01993          IfDebug(cerr << "name: " << '"' << name << '"' << endl);
01994 
01995          // We need to decompose s3 a little more:
01996          // The partname is the method symbol and a bracket at the end of ::
01997          // eg. given s3 = "foo::bar::part(" ,  partname = "part("
01998          TString partname = s3("[_a-zA-Z][_a-zA-Z0-9]* *($");
01999 
02000          // The prefix, considering the s3 = "foo::bar::part" example would be
02001          // prefix = "foo::bar::". prefix equals the empty string if there is only one
02002          // or no set of colons in s3.
02003          // Note: we reconstruct the fully qualified name with a while loop because
02004          // it does not seem that TRegexp can handle something like "([_a-zA-Z][_a-zA-Z0-9]*::)+$"
02005          TString prefix = "";
02006          TString str = s2;
02007          str.Remove(str.Length() - partname.Length(), partname.Length());
02008          while (1) {
02009             TString sym = str("[_a-zA-Z][_a-zA-Z0-9]*::$");
02010             if (sym.Length() == 0)
02011                break;
02012             str.Remove(str.Length() - sym.Length(), sym.Length());
02013             prefix = sym + prefix;
02014          }
02015 
02016          // Not the preprefix would be = "foo::" from our previous example or the empty
02017          // string, "" if there is only one or no set of colons in prefix, eg. prefix = "bar::"
02018          TString preprefix = prefix;
02019          TString sym = prefix("[_a-zA-Z][_a-zA-Z0-9]*::$");
02020          preprefix.Remove(preprefix.Length() - sym.Length(), sym.Length());
02021 
02022          IfDebug(cerr << "prefix: " << '"' << prefix << '"' << endl);
02023          IfDebug(cerr << "preprefix: " << '"' << preprefix << '"' << endl);
02024 
02025          pClass = MakeClassFromClassName(preprefix + name);
02026          if (!pClass) {
02027             pos = -2;
02028             break;
02029          }
02030          // get method name
02031          TString methodName;
02032 
02033          // (normal member function)
02034          methodName = s3("[^:>\\.(]*($");
02035          methodName.Chop();
02036          methodName.Remove(TString::kTrailing, ' ');
02037 
02038          IfDebug(cerr << methodName << endl);
02039 
02040          // get methods
02041          TContainer *pList = new TContainer;
02042          pList->AddAll(pClass->GetListOfAllPublicMethods());
02043 
02044          // print prototypes
02045          Bool_t foundOne = kFALSE;
02046          TIter nextMethod(pList);
02047          TMethod *pMethod;
02048          while ((pMethod = (TMethod *) nextMethod())) {
02049             if (methodName == pMethod->GetName()) {
02050                foundOne = kTRUE;
02051                cout << endl << pMethod->GetReturnTypeName()
02052                    << " " << pMethod->GetName()
02053                    << pMethod->GetSignature();
02054                const char *comment = pMethod->GetCommentString();
02055                if (comment && comment[0] != '\0') {
02056                   cout << " \t// " << comment;
02057                }
02058             }
02059          }
02060 
02061          // done
02062          if (foundOne) {
02063             cout << endl;
02064             pos = -2;
02065          } else {
02066             gSystem->Beep();
02067             pos = -1;
02068          }
02069 
02070          // cleanup
02071          delete pList;
02072          delete pClass;
02073 
02074          if (context != original_context)
02075             pos = -2;
02076       }
02077       break;
02078 
02079    case kCXX_DirectProto:
02080    case kCXX_IndirectProto:
02081    case kCXX_NewProto:
02082    case kCXX_ConstructorProto:
02083       {
02084          const EContext_t original_context = context;  // save this for later
02085 
02086          // get class
02087          TClass *pClass;
02088          TString name;
02089          if (context == kCXX_NewProto) {
02090             name = s3("[_a-zA-Z][_a-zA-Z0-9:]* *($", 3);
02091             name.Chop();
02092             name.Remove(TString::kTrailing, ' ');
02093             // "name" should now be the name of a class
02094          } else {
02095             name = s3("^[_a-zA-Z][_a-zA-Z0-9:]*");
02096             // "name" may now be the name of a class, object, or pointer
02097          }
02098          IfDebug(cerr << endl);
02099          IfDebug(cerr << "name: " << '"' << name << '"' << endl);
02100 
02101          // frodo: Again, passing the all string
02102          TString namerec = s1;
02103 
02104          switch (context) {
02105          case kCXX_ScopeProto:
02106             pClass = MakeClassFromClassName(name);
02107             break;
02108          case kCXX_DirectProto:
02109             pClass = MakeClassFromVarName(namerec, context); // frodo
02110             break;
02111          case kCXX_IndirectProto:
02112             pClass = MakeClassFromVarName(namerec, context); // frodo
02113             break;
02114          case kCXX_NewProto:
02115             pClass = MakeClassFromClassName(name);
02116             break;
02117          case kCXX_ConstructorProto:
02118             pClass = MakeClassFromClassName(name);
02119             break;
02120          default:
02121             assert(0);
02122             break;
02123          }
02124          if (!pClass) {
02125             pos = -2;
02126             break;
02127          }
02128          // get method name
02129          TString methodName;
02130          if (context == kCXX_ConstructorProto || context == kCXX_NewProto) {
02131             // (constructor)
02132             methodName = name("[_a-zA-Z][_a-zA-Z0-9]*$");
02133          } else {
02134             // (normal member function)
02135             methodName = s3("[^:>\\.(]*($");
02136             methodName.Chop();
02137             methodName.Remove(TString::kTrailing, ' ');
02138          }
02139          IfDebug(cerr << methodName << endl);
02140 
02141          // get methods
02142          TContainer *pList = new TContainer;
02143          pList->AddAll(pClass->GetListOfAllPublicMethods());
02144 
02145          // print prototypes
02146          Bool_t foundOne = kFALSE;
02147          TIter nextMethod(pList);
02148          TMethod *pMethod;
02149          while ((pMethod = (TMethod *) nextMethod())) {
02150             if (methodName == pMethod->GetName()) {
02151                foundOne = kTRUE;
02152                cout << endl << pMethod->GetReturnTypeName()
02153                    << " " << pMethod->GetName()
02154                    << pMethod->GetSignature();
02155                const char *comment = pMethod->GetCommentString();
02156                if (comment && comment[0] != '\0') {
02157                   cout << " \t// " << comment;
02158                }
02159             }
02160          }
02161 
02162          // done
02163          if (foundOne) {
02164             cout << endl;
02165             pos = -2;
02166          } else {
02167             gSystem->Beep();
02168             pos = -1;
02169          }
02170 
02171          // cleanup
02172          delete pList;
02173          delete pClass;
02174 
02175          if (context != original_context)
02176             pos = -2;
02177       }
02178       break;
02179 
02180    case kCXX_Global:
02181       {
02182          // first need to veto a few possibilities.
02183          int l2 = s2.Length(), l3 = s3.Length();
02184 
02185          // "abc().whatever[TAB]"
02186          if (l2 > l3 && s2[l2 - l3 - 1] == '.') {
02187             cerr << endl <<
02188                 "tab completion not implemented for this context" << endl;
02189             break;              // veto
02190          }
02191          // "abc()->whatever[TAB]"
02192          if (l2 > l3 + 1 && s2(l2 - l3 - 2, 2) == "->") {
02193             cerr << endl <<
02194                 "tab completion not implemented for this context" << endl;
02195             break;              // veto
02196          }
02197 
02198          TContainer *pList = new TContainer;
02199 
02200          const TSeqCollection *pL2 = GetListOfClasses();
02201          if (pL2) pList->AddAll(pL2);
02202 
02203          if (fpNamespaces) pList->AddAll(fpNamespaces); //rdm
02204          //
02205          const TSeqCollection *pC1 = GetListOfGlobals();
02206          if (pC1) pList->AddAll(pC1);
02207          //
02208          const TSeqCollection *pC3 = GetListOfGlobalFunctions();
02209          if (pC3) pList->AddAll(pC3);
02210 
02211          pos = Complete("[_a-zA-Z][_a-zA-Z0-9]*$", pList, "");
02212 
02213          delete pList;
02214       }
02215       break;
02216 
02217    case kCXX_GlobalProto:
02218       {
02219          // get function name
02220          TString functionName = s3("[_a-zA-Z][_a-zA-Z0-9]*");
02221          IfDebug(cerr << functionName << endl);
02222 
02223          TContainer listOfMatchingGlobalFuncs;
02224          TIter nextGlobalFunc(GetListOfGlobalFunctions());
02225          TObject *pObj;
02226          while ((pObj = nextGlobalFunc())) {
02227             if (strcmp(pObj->GetName(), functionName) == 0) {
02228                listOfMatchingGlobalFuncs.Add(pObj);
02229             }
02230          }
02231 
02232          if (listOfMatchingGlobalFuncs.IsEmpty()) {
02233             cerr << endl << "no such function: " << dblquote(functionName)
02234                 << endl;
02235          } else {
02236             cout << endl;
02237             TIter next(&listOfMatchingGlobalFuncs);
02238             TFunction *pFunction;
02239             while ((pFunction = (TFunction *) next())) {
02240                cout << pFunction->GetReturnTypeName()
02241                    << " " << pFunction->GetName()
02242                    << pFunction->GetSignature()
02243                    << endl;
02244             }
02245          }
02246 
02247          pos = -2;
02248       }
02249       break;
02250 
02251       /******************************************************************/
02252       /*                                                                */
02253       /* default: should never happen                                   */
02254       /*                                                                */
02255       /******************************************************************/
02256    default:
02257       assert(0);
02258       break;
02259    }
02260 
02261    return pos;
02262 }
02263 
02264 //______________________________________________________________________________
02265 void TTabCom::InitPatterns()
02266 {
02267    // [private]
02268 
02269    // add more patterns somewhere below.
02270    // add corresponding enum to "EContext_t"
02271    //
02272    // note:
02273    // 1. in some cases order is important ...
02274    //
02275    //    the order of the "case" statements in "switch( context )" in "TTabCom::Hook()" is Not important.
02276    //
02277    //    the order of the "SetPattern()" function calls below is Not important.
02278    //
02279    //    the order of the initializers in the "EContext_t" enumeration Is important
02280    //    because DetermineContext() goes through the array in order, and returns at the first match.
02281    //
02282    // 2. below, "$" will match cursor position
02283 
02284    SetPattern(kSYS_UserName, "~[_a-zA-Z0-9]*$");
02285    SetPattern(kSYS_EnvVar, "$[_a-zA-Z0-9]*$");
02286 
02287    SetPattern(kCINT_stdout, "; *>>?.*$");  // stdout
02288    SetPattern(kCINT_stderr, "; *2>>?.*$"); // stderr
02289    SetPattern(kCINT_stdin, "; *<.*$");     // stdin
02290 
02291    SetPattern(kCINT_Edit, "^ *\\.E .*$");
02292    SetPattern(kCINT_Load, "^ *\\.L .*$");
02293    SetPattern(kCINT_Exec, "^ *\\.x +[-0-9_a-zA-Z~$./]*$");
02294    SetPattern(kCINT_EXec, "^ *\\.X +[-0-9_a-zA-Z~$./]*$");
02295 
02296    SetPattern(kCINT_pragma, "^# *pragma +[_a-zA-Z0-9]*$");
02297    SetPattern(kCINT_includeSYS, "^# *include *<[^>]*$");   // system files
02298    SetPattern(kCINT_includePWD, "^# *include *\"[^\"]*$"); // local files
02299 
02300    SetPattern(kCINT_cpp, "^# *[_a-zA-Z0-9]*$");
02301 
02302    SetPattern(kROOT_Load, "gSystem *-> *Load *( *\"[^\"]*$");
02303 
02304    SetPattern(kCXX_NewProto, "new +[_a-zA-Z][_a-zA-Z0-9:]* *($");
02305    SetPattern(kCXX_ConstructorProto,
02306               "[_a-zA-Z][_a-zA-Z0-9:]* +[_a-zA-Z][_a-zA-Z0-9]* *($");
02307    SetPattern(kCXX_ScopeProto,
02308               "[_a-zA-Z][_a-zA-Z0-9]* *:: *[_a-zA-Z0-9]* *($");
02309    SetPattern(kCXX_DirectProto,
02310               "[_a-zA-Z][_a-zA-Z0-9()]* *\\. *[_a-zA-Z0-9]* *($");
02311    SetPattern(kCXX_IndirectProto,
02312               "[_a-zA-Z][_a-zA-Z0-9()]* *-> *[_a-zA-Z0-9]* *($");
02313 
02314    SetPattern(kCXX_ScopeMember,
02315               "[_a-zA-Z][_a-zA-Z0-9]* *:: *[_a-zA-Z0-9]*$");
02316    SetPattern(kCXX_DirectMember,
02317               "[_a-zA-Z][_a-zA-Z0-9()]* *\\. *[_a-zA-Z0-9()]*$");  // frodo
02318    SetPattern(kCXX_IndirectMember,
02319               "[_a-zA-Z][_a-zA-Z0-9()]* *-> *[_a-zA-Z0-9()]*$");    // frodo
02320 
02321    SetPattern(kSYS_FileName, "\"[-0-9_a-zA-Z~$./]*$");
02322    SetPattern(kCXX_Global, "[_a-zA-Z][_a-zA-Z0-9]*$");
02323    SetPattern(kCXX_GlobalProto, "[_a-zA-Z][_a-zA-Z0-9]* *($");
02324 }
02325 
02326 //______________________________________________________________________________
02327 TClass *TTabCom::MakeClassFromClassName(const char className[]) const
02328 {
02329    // [private]
02330    //   (does some specific error handling that makes the function unsuitable for general use.)
02331    //   returns a new'd TClass given the name of a class.
02332    //   user must delete.
02333    //   returns 0 in case of error.
02334 
02335    // the TClass constructor will print a Warning message for classes that don't exist
02336    // so, ignore warnings temporarily.
02337    NoMsg(kWarning);
02338    TClass *pClass = new TClass(className);
02339    NoMsg(-1);
02340 
02341    // make sure "className" exists
02342    // if (pClass->Size() == 0) {   //namespace has 0 size
02343    if (pClass->GetListOfAllPublicMethods()->GetSize() == 0 &&
02344        pClass->GetListOfAllPublicDataMembers()->GetSize() == 0) {
02345       // i'm assuming this happens iff there was some error.
02346       // (misspelled the class name, for example)
02347       cerr << endl << "class " << dblquote(className) << " not defined." <<
02348           endl;
02349       return 0;
02350    }
02351 
02352    return pClass;
02353 }
02354 
02355 //______________________________________________________________________________
02356 TClass *TTabCom::TryMakeClassFromClassName(const char className[]) const
02357 {
02358    // Same as above but does not print the error message.
02359 
02360    // the TClass constructor will print a Warning message for classes that don't exist
02361    // so, ignore warnings temporarily.
02362    NoMsg(kWarning);
02363    TClass *pClass = new TClass(className);
02364    NoMsg(-1);
02365 
02366    // make sure "className" exists
02367    // if (pClass->Size() == 0) {   //namespace has 0 size
02368    if (pClass->GetListOfAllPublicMethods()->GetSize() == 0 &&
02369        pClass->GetListOfAllPublicDataMembers()->GetSize() == 0) {
02370       return 0;
02371    }
02372 
02373    return pClass;
02374 }
02375 
02376 //______________________________________________________________________________
02377 TClass *TTabCom::MakeClassFromVarName(const char varName[],
02378                                       EContext_t & context, int iter)
02379 {
02380    // [private]
02381    //   (does some specific error handling that makes the function unsuitable for general use.)
02382    //   returns a new'd TClass given the name of a variable.
02383    //   user must delete.
02384    //   returns 0 in case of error.
02385    //   if user has operator.() or operator->() backwards, will modify: context, *fpLoc and fBuf.
02386    //   context sensitive behavior.
02387 
02388    // frodo:
02389    // Because of the Member and Proto recursion, this has become a bit
02390    // complicated, so here is how it works:
02391    //
02392    // root [1] var.a.b.c[TAB]
02393    //
02394    // will generate the sucessive calls:
02395    // MakeClassFromVarName("var.a.b.c", context, 0) returns the class of "c"
02396    // MakeClassFromVarName("var.a.b", context, 1)   returns the class of "b"
02397    // MakeClassFromVarName("var.a", context, 2)     returns the class of "a"
02398    // MakeClassFromVarName("var", context, 3)
02399 
02400    // need to make sure "varName" exists
02401    // because "DetermineClass()" prints clumsy error message otherwise.
02402    Bool_t varName_exists = GetListOfGlobals()->Contains(varName) || // check in list of globals first.
02403        (gROOT->FindObject(varName) != 0);  // then check CINT "shortcut #3"
02404 
02405 
02406    //
02407    // frodo: Member and Proto recursion code
02408    //
02409    if (0) printf("varName is [%s] with iteration [%i]\n", varName, iter);
02410 
02411    // ParseReverse will return 0 if there are no "." or "->" in the varName
02412    Int_t cut = ParseReverse(varName, strlen(varName));
02413 
02414    // If it's not a "simple" variable and if there is at least one "." or "->"
02415    if (!varName_exists && cut != 0)
02416    {
02417       TString parentName = varName;
02418       TString memberName = varName;
02419 
02420       // Check to see if this is the last call (last member/method)
02421       if (iter > fLastIter) fLastIter = iter;
02422 
02423       parentName[cut] = 0;
02424       if (0) printf("Parent string is [%s]\n", parentName.Data());
02425 
02426       // We are treating here cases like h->SetXTitle(gROOT->Get<TAB>
02427       // i.e. when the parentName has an unbalanced number of paranthesis.
02428       if (cut>2) {
02429          UInt_t level = 0;
02430          for(Int_t i = cut-1; i>=0; --i) {
02431             switch (parentName[i]) {
02432                case '(':
02433                   if (level) --level;
02434                   else {
02435                      parentName = parentName(i+1,cut-i-1);
02436                      i = 0;
02437                   }
02438                   break;
02439                case ')':
02440                   ++level; break;
02441             }
02442          }
02443       }
02444 
02445       TClass *pclass;
02446       // Can be "." or "->"
02447       if (varName[cut] == '.') {
02448          memberName = varName+cut+1;
02449          if (0) printf("Member/method is [%s]\n", memberName.Data());
02450          EContext_t subcontext = kCXX_DirectMember;
02451          pclass = MakeClassFromVarName(parentName.Data(), subcontext, iter+1);
02452       } else {
02453          memberName = varName+cut+2;
02454          if (0) printf("Member/method is [%s]\n", memberName.Data());
02455          EContext_t subcontext = kCXX_IndirectMember;
02456          pclass = MakeClassFromVarName(parentName.Data(), subcontext, iter+1);
02457       }
02458 
02459       if (0) printf("I got [%s] from MakeClassFromVarName()\n", pclass->GetName());
02460 
02461       if (pclass)
02462       {
02463          if (0) printf("Variable [%s] exists!\n", parentName.Data());
02464 
02465          // If it's back in the first call of the function, return immediatly
02466          if (iter == 0) return pclass;
02467 
02468          if (0) printf("Trying data member [%s] of class [%s] ...\n",
02469             memberName.Data(), pclass->GetName());
02470 
02471          // Check if it's a member
02472          TDataMember *dmptr = 0; //pclass->GetDataMember(memberName.Data());
02473          TList  *dlist = pclass->GetListOfDataMembers();
02474          TIter   next(pclass->GetListOfAllPublicDataMembers());
02475          while ((dmptr = (TDataMember *) next())) {
02476             if (memberName == dmptr->GetName()) break;
02477          }
02478          delete dlist;
02479          if (dmptr)
02480          {
02481             if (0) printf("It's a member!\n");
02482 
02483             TString returnName = dmptr->GetTypeName();
02484             //              if (returnName[returnName.Length()-1] == '*')
02485             //                  printf("It's a pointer!\n");
02486 
02487             TClass *mclass = new TClass(returnName.Data());
02488             return mclass;
02489          }
02490 
02491 
02492          // Check if it's a proto: must have ()
02493          // This might not be too safe to use   :(
02494          char *parentesis_ptr = (char*)strrchr(memberName.Data(), '(');
02495          if (parentesis_ptr) *parentesis_ptr = 0;
02496 
02497 
02498          if (0) printf("Trying method [%s] of class [%s] ...\n",
02499             memberName.Data(), pclass->GetName());
02500 
02501          // Check if it's a method
02502          TMethod *mptr = 0; // pclass->GetMethodAny(memberName.Data());
02503          TList  *mlist = pclass->GetListOfAllPublicMethods();
02504          next = mlist;
02505          while ((mptr = (TMethod *) next())) {
02506             if (strcmp(memberName.Data(),mptr->GetName())==0) break;
02507          }
02508          delete mlist;
02509 
02510          if (mptr)
02511          {
02512             TString returnName = mptr->GetReturnTypeName();
02513 
02514             if (0) printf("It's a method called [%s] with return type [%s]\n",
02515                memberName.Data(), returnName.Data());
02516 
02517             // This will handle the methods that returns a pointer to a class
02518             if (returnName[returnName.Length()-1] == '*')
02519             {
02520                returnName[returnName.Length()-1] = 0;
02521                fVarIsPointer = kTRUE;
02522             }
02523             else
02524             {
02525                fVarIsPointer = kFALSE;
02526             }
02527 
02528             TClass *mclass = new TClass(returnName.Data());
02529             return mclass;
02530          }
02531       }
02532    }
02533 
02534    //
02535    // frodo: End of Member and Proto recursion code
02536    //
02537 
02538 
02539    // not found...
02540    if (!varName_exists) {
02541       cerr << endl << "variable " << dblquote(varName) << " not defined."
02542          << endl;
02543       return 0;                 //* RETURN *//
02544    }
02545 
02546    /*****************************************************************************************/
02547    /*                                                                                       */
02548    /*  this section is really ugly.                                                         */
02549    /*  and slow.                                                                            */
02550    /*  it could be made a lot better if there was some way to tell whether or not a given   */
02551    /*  variable is a pointer or a pointer to a pointer.                                     */
02552    /*                                                                                       */
02553    /*****************************************************************************************/
02554 
02555    TString className = DetermineClass(varName);
02556 
02557    if (className.IsNull() || className == "*") {
02558       // this will happen if "varName" is a fundamental type (as opposed to class type).
02559       // or a pointer to a pointer.
02560       // or a function pointer.
02561       cerr << endl << "problem determining class of " << dblquote(varName)
02562          << endl;
02563       return 0;                 //* RETURN *//
02564    }
02565 
02566    fVarIsPointer = className[className.Length() - 1] == '*';
02567 
02568    // frodo: I shouldn't have to do this, but for some reason now I have to
02569    //        otherwise the varptr->[TAB] won't work    :(
02570    if (fVarIsPointer)
02571       className[className.Length()-1] = 0;
02572 
02573    //
02574    // frodo: I wasn't able to put the automatic "." to "->" replacing working
02575    //        so I just commented out.
02576    //
02577 
02578 
02579    //   Bool_t varIsPointer = className[className.Length() - 1] == '*';
02580 
02581    //printf("Context is %i, fContext is %i, pointer is %i\n", context, fContext, fVarIsPointer);
02582 
02583    if (fVarIsPointer &&
02584       (context == kCXX_DirectMember || context == kCXX_DirectProto)) {
02585          // user is using operator.() instead of operator->()
02586          // ==>
02587          //      1. we are in wrong context.
02588          //      2. user is lazy
02589          //      3. or maybe confused
02590 
02591          // 1. fix the context
02592          switch (context) {
02593       case kCXX_DirectMember:
02594          context = kCXX_IndirectMember;
02595          break;
02596       case kCXX_DirectProto:
02597          context = kCXX_IndirectProto;
02598          break;
02599       default:
02600          assert(0);
02601          break;
02602          }
02603 
02604          // 2. fix the operator.
02605          int i;
02606          for (i = *fpLoc; fBuf[i] != '.'; i -= 1) {
02607          }
02608          int loc = i;
02609          for (i = strlen(fBuf); i >= loc; i -= 1) {
02610             fBuf[i + 1] = fBuf[i];
02611          }
02612          fBuf[loc] = '-';
02613          fBuf[loc + 1] = '>';
02614          *fpLoc += 1;
02615 
02616          // 3. inform the user.
02617          cerr << endl << dblquote(varName) <<
02618             " is of pointer type. Use this operator: ->" << endl;
02619    }
02620 
02621    if (context == kCXX_IndirectMember || context == kCXX_IndirectProto) {
02622       if (fVarIsPointer) {
02623          // frodo: This part no longer makes sense...
02624          className.Chop();      // remove the '*'
02625 
02626          if (className[className.Length() - 1] == '*') {
02627             cerr << endl << "can't handle pointers to pointers." << endl;
02628             return 0;           // RETURN
02629          }
02630       } else {
02631          // user is using operator->() instead of operator.()
02632          // ==>
02633          //      1. we are in wrong context.
02634          //      2. user is lazy
02635          //      3. or maybe confused
02636 
02637          // 1. fix the context
02638          switch (context) {
02639          case kCXX_IndirectMember:
02640             context = kCXX_DirectMember;
02641             break;
02642          case kCXX_IndirectProto:
02643             context = kCXX_DirectProto;
02644             break;
02645          default:
02646             assert(0);
02647             break;
02648          }
02649 
02650          // 2. fix the operator.
02651          int i;
02652          for (i = *fpLoc; fBuf[i - 1] != '-' && fBuf[i] != '>'; i -= 1) {
02653          }
02654          fBuf[i - 1] = '.';
02655          int len = strlen(fBuf);
02656          for (; i < len; i += 1) {
02657             fBuf[i] = fBuf[i + 1];
02658          }
02659          *fpLoc -= 1;
02660 
02661          // 3. inform the user.
02662          cerr << endl << dblquote(varName) <<
02663              " is not of pointer type. Use this operator: ." << endl;
02664       }
02665    }
02666 
02667    return new TClass(className);
02668 }
02669 
02670 //______________________________________________________________________________
02671 void TTabCom::SetPattern(EContext_t handle, const char regexp[])
02672 {
02673    // [private]
02674 
02675    // prevent overflow
02676    if (handle >= kNUM_PAT) {
02677       cerr << endl
02678           << "ERROR: handle="
02679           << (int) handle << " >= kNUM_PAT=" << (int) kNUM_PAT << endl;
02680       return;
02681    }
02682 
02683    fRegExp[handle] = regexp;
02684    Makepat(regexp, fPat[handle], MAX_LEN_PAT);
02685 }
02686 
02687 
02688 
02689 //______________________________________________________________________________
02690 int TTabCom::ParseReverse(const char *var_str, int start)
02691 {
02692    //
02693    // Returns the place in the string where to put the \0, starting the search
02694    // from "start"
02695    //
02696    int end = 0;
02697    if (start > (int)strlen(var_str)) start = strlen(var_str);
02698 
02699    for (int i = start; i > 0; i--)
02700    {
02701       if (var_str[i] == '.') return i;
02702       if (var_str[i] == '>' && i > 0 && var_str[i-1] == '-') return i-1;
02703    }
02704 
02705    return end;
02706 }

Generated on Tue Jul 5 14:11:59 2011 for ROOT_528-00b_version by  doxygen 1.5.1