G4UItcsh.cc

Go to the documentation of this file.
00001 //
00002 // ********************************************************************
00003 // * License and Disclaimer                                           *
00004 // *                                                                  *
00005 // * The  Geant4 software  is  copyright of the Copyright Holders  of *
00006 // * the Geant4 Collaboration.  It is provided  under  the terms  and *
00007 // * conditions of the Geant4 Software License,  included in the file *
00008 // * LICENSE and available at  http://cern.ch/geant4/license .  These *
00009 // * include a list of copyright holders.                             *
00010 // *                                                                  *
00011 // * Neither the authors of this software system, nor their employing *
00012 // * institutes,nor the agencies providing financial support for this *
00013 // * work  make  any representation or  warranty, express or implied, *
00014 // * regarding  this  software system or assume any liability for its *
00015 // * use.  Please see the license in the file  LICENSE  and URL above *
00016 // * for the full disclaimer and the limitation of liability.         *
00017 // *                                                                  *
00018 // * This  code  implementation is the result of  the  scientific and *
00019 // * technical work of the GEANT4 collaboration.                      *
00020 // * By using,  copying,  modifying or  distributing the software (or *
00021 // * any work based  on the software)  you  agree  to acknowledge its *
00022 // * use  in  resulting  scientific  publications,  and indicate your *
00023 // * acceptance of all terms of the Geant4 Software license.          *
00024 // ********************************************************************
00025 //
00026 //
00027 // $Id$
00028 //
00029 
00030 #ifndef WIN32
00031 
00032 #include "G4Types.hh"
00033 #include "G4StateManager.hh"
00034 #include "G4UIcommandStatus.hh"
00035 #include "G4UItcsh.hh"
00036 #include <ctype.h>
00037 #include <sstream>
00038 #include <fstream>
00039 #include <stdlib.h>
00040 
00041 // ASCII character code
00042 static const char AsciiCtrA = '\001';
00043 static const char AsciiCtrB = '\002';
00044 static const char AsciiCtrC = '\003';
00045 static const char AsciiCtrD = '\004';
00046 static const char AsciiCtrE = '\005';
00047 static const char AsciiCtrF = '\006';
00048 static const char AsciiCtrK = '\013';
00049 static const char AsciiCtrL = '\014';
00050 static const char AsciiCtrN = '\016';
00051 static const char AsciiCtrP = '\020';
00052 static const char AsciiCtrQ = '\021';
00053 static const char AsciiCtrS = '\023';
00054 static const char AsciiCtrZ = '\032';
00055 static const char AsciiTAB   = '\011';
00056 static const char AsciiBS    = '\010';
00057 static const char AsciiDEL   = '\177';
00058 static const char AsciiESC   = '\033';
00059 
00060 static const int AsciiPrintableMin = 32;
00061 
00062 // history file
00063 static const G4String historyFileName= "/.g4_hist";
00064 
00066 G4UItcsh::G4UItcsh(const G4String& prompt, G4int maxhist)
00067   : G4VUIshell(prompt),
00068     commandLine(""), cursorPosition(1),
00069     commandHistory(maxhist), maxHistory(maxhist),
00070     currentHistoryNo(1), relativeHistoryIndex(0)
00072 {  
00073   // get current terminal mode
00074   tcgetattr(0, &tios);
00075 
00076   // read a shell history file
00077   const char* path = getenv("HOME");
00078   if( path == NULL ) return;
00079 
00080   G4String homedir= path;
00081   G4String fname= homedir + historyFileName;
00082 
00083   std::ifstream histfile;
00084   enum { BUFSIZE= 1024 }; char linebuf[BUFSIZE];
00085 
00086   histfile.open(fname, std::ios::in);
00087   while (histfile.good()) {
00088     if(histfile.eof()) break;
00089 
00090     histfile.getline(linebuf, BUFSIZE);
00091     G4String aline= linebuf;
00092     aline.strip(G4String::both);
00093     if(aline.size() !=  0) StoreHistory(linebuf);
00094   }
00095   histfile.close();
00096 }
00097 
00099 G4UItcsh::~G4UItcsh()
00101 {
00102   // store a shell history
00103   const char* path = getenv("HOME");
00104   if( path == NULL ) return;
00105 
00106   G4String homedir= path;
00107   G4String fname= homedir + historyFileName;
00108 
00109   std::ofstream histfile;
00110   histfile.open(fname, std::ios::out);
00111 
00112   G4int n0hist= 1;
00113   if( currentHistoryNo > maxHistory ) n0hist= currentHistoryNo-maxHistory+1;
00114 
00115   for (G4int i=n0hist; i<= currentHistoryNo; i++) {
00116     histfile << RestoreHistory(i) << G4endl;
00117   }
00118   
00119   histfile.close();
00120 }
00121 
00123 void G4UItcsh::MakePrompt(const char* msg)
00125 {
00126   if(promptSetting.length()<=1) {
00127     promptString= promptSetting;
00128     return;
00129   }
00130 
00131   promptString="";
00132   size_t i;
00133   for(i=0; i<promptSetting.length()-1; i++){
00134     if(promptSetting[i]=='%'){
00135       switch (promptSetting[i+1]) {
00136       case 's':  // current application status
00137         {
00138           G4String stateStr;
00139           if(msg)
00140           { stateStr = msg; }
00141           else
00142           {
00143             G4StateManager* statM= G4StateManager::GetStateManager();
00144             stateStr= statM-> GetStateString(statM->GetCurrentState());
00145           }
00146           promptString.append(stateStr);
00147           i++;
00148         }
00149         break;
00150       case '/':  // current working directory
00151         promptString.append(currentCommandDir);
00152         i++;
00153         break;
00154       case 'h':  // history#
00155         {
00156         std::ostringstream os;
00157         os << currentHistoryNo;
00158         promptString.append(os.str());
00159         i++;
00160         }
00161         break;
00162       default:
00163         break;
00164       } 
00165     } else {
00166       promptString.append(G4String(promptSetting[i]));
00167     }
00168   }
00169 
00170   // append last chaacter
00171   if(i == promptSetting.length()-1) 
00172     promptString.append(G4String(promptSetting[i]));
00173 }
00174 
00175 
00177 void G4UItcsh::ResetTerminal()
00179 {
00180   RestoreTerm();
00181 }
00182 
00183 
00184 // --------------------------------------------------------------------
00185 //      commad line operations
00186 // --------------------------------------------------------------------
00188 void G4UItcsh::InitializeCommandLine()
00190 {
00191   commandLine= "";
00192   cursorPosition= 1;
00193 }
00194 
00196 void G4UItcsh::InsertCharacter(char cc)
00198 {
00199   if( ! (cc >= AsciiPrintableMin  && isprint(cc)) ) return;
00200 
00201   // display...
00202   G4cout << cc;
00203   size_t i;
00204   for(i=cursorPosition-1; i<commandLine.length() ;i++) 
00205     G4cout << commandLine[i];
00206   for(i=cursorPosition-1; i<commandLine.length() ;i++)
00207     G4cout << AsciiBS;
00208   G4cout << std::flush;
00209     
00210   // command line string...
00211   if(IsCursorLast()) {  // add
00212     commandLine+= cc;
00213   } else { // insert
00214     commandLine.insert(cursorPosition-1, G4String(cc));
00215   }
00216   cursorPosition++;
00217 }
00218   
00220 void G4UItcsh::BackspaceCharacter()
00222 {
00223   if(cursorPosition==1) return;
00224 
00225   // display...
00226   if(IsCursorLast()) {  
00227     G4cout << AsciiBS << ' ' << AsciiBS << std::flush;
00228   } else { 
00229     G4cout << AsciiBS;
00230     size_t i;
00231     for(i=cursorPosition-2; i< commandLine.length()-1 ;i++){
00232       G4cout << commandLine[i+1];
00233     }
00234     G4cout << ' ';
00235     for(i=cursorPosition-2; i< commandLine.length() ;i++){
00236       G4cout << AsciiBS;
00237     }
00238     G4cout << std::flush;
00239   }
00240 
00241   // command line string...
00242   commandLine.erase(cursorPosition-2, 1);
00243 
00244   cursorPosition--;
00245 }
00246 
00248 void G4UItcsh::DeleteCharacter()
00250 {
00251   if(IsCursorLast()) return;
00252 
00253   // display...
00254   size_t i;
00255   for(i=cursorPosition-1; i< commandLine.length()-1 ;i++){
00256     G4cout << commandLine[i+1];
00257   }
00258   G4cout << ' ';
00259   for(i=cursorPosition-1; i< commandLine.length() ;i++){
00260     G4cout << AsciiBS;
00261   }
00262   G4cout << std::flush;
00263 
00264   // command lin string...
00265   commandLine.erase(cursorPosition-1, 1);
00266 }
00267 
00269 void G4UItcsh::ClearLine()
00271 {
00272   // display...
00273   G4int i;
00274   for(i= cursorPosition; i>=2; i--) G4cout << AsciiBS;
00275   for(i=1; i<=G4int(commandLine.length()); i++) G4cout << ' ';
00276   for(i=1; i<=G4int(commandLine.length()); i++) G4cout << AsciiBS;
00277   G4cout << std::flush;
00278   
00279   // command line string...
00280   commandLine.erase();
00281   cursorPosition= 1;
00282 }
00283 
00285 void G4UItcsh::ClearAfterCursor()
00287 {
00288   if(IsCursorLast()) return;
00289 
00290   // display...
00291   G4int i;
00292   for(i=cursorPosition; i<=G4int(commandLine.length()); i++) G4cout << ' ';
00293   for(i=commandLine.length(); i>=cursorPosition; i--) G4cout << AsciiBS;
00294   G4cout << std::flush;
00295 
00296   // command line string...
00297   commandLine.erase(cursorPosition-1, 
00298                     commandLine.length()-cursorPosition+1);
00299 }
00300 
00302 void G4UItcsh::ClearScreen()
00304 {
00305   if(! clearString.empty() ) {
00306     G4cout << clearString;
00307 
00308     G4cout << promptString << commandLine << std::flush;
00309     // reset cursur position
00310     for(G4int i=commandLine.length()+1; i>=cursorPosition+1; i--) 
00311       G4cout << AsciiBS << std::flush;
00312   }
00313 }
00314 
00316 void G4UItcsh::ForwardCursor()
00318 {
00319   if(IsCursorLast()) return;
00320 
00321   G4cout << commandLine[(size_t)(cursorPosition-1)] << std::flush;
00322   cursorPosition++;
00323 }
00324 
00326 void G4UItcsh::BackwardCursor()
00328 {
00329   if(cursorPosition==1) return;
00330 
00331   cursorPosition--;
00332   G4cout << AsciiBS << std::flush;
00333 }
00334 
00336 void G4UItcsh::MoveCursorTop()
00338 {
00339   for(G4int i=cursorPosition; i>1; i--){
00340     G4cout << AsciiBS;
00341   }
00342   G4cout << std::flush;
00343   cursorPosition=1;
00344 }
00345 
00347 void G4UItcsh::MoveCursorEnd()
00349 {
00350   for(size_t i=cursorPosition-1; i<commandLine.length(); i++){
00351     G4cout << commandLine[i];
00352   }
00353   G4cout << std::flush;
00354   cursorPosition=commandLine.length()+1;
00355 }
00356 
00358 void G4UItcsh::PreviousCommand()
00360 {
00361   G4int nhmax= currentHistoryNo-1 >= maxHistory ? 
00362                  maxHistory : currentHistoryNo-1;
00363 
00364   // retain current input
00365   if(relativeHistoryIndex==0) commandLineBuf= commandLine;
00366 
00367   if(relativeHistoryIndex>=-nhmax+1 && relativeHistoryIndex<=0) {
00368     ClearLine();
00369     relativeHistoryIndex--;
00370     commandLine= RestoreHistory(currentHistoryNo+relativeHistoryIndex);
00371 
00372     G4cout << commandLine << std::flush;
00373     cursorPosition= commandLine.length()+1;
00374   }
00375 }
00376 
00378 void G4UItcsh::NextCommand()
00380 {  
00381   G4int nhmax= currentHistoryNo-1 >= maxHistory ? 
00382                  maxHistory : currentHistoryNo-1;
00383 
00384   if(relativeHistoryIndex>=-nhmax && relativeHistoryIndex<=-1) {
00385     ClearLine();
00386     relativeHistoryIndex++;
00387 
00388     if(relativeHistoryIndex==0) commandLine= commandLineBuf;
00389     else commandLine= RestoreHistory(currentHistoryNo+relativeHistoryIndex);
00390 
00391     G4cout << commandLine << std::flush;      
00392     cursorPosition= commandLine.length()+1;
00393   }
00394 }
00395 
00396 
00398 void G4UItcsh::ListMatchedCommand()
00400 {
00401   G4cout << G4endl;
00402   
00403   // input string
00404   G4String input= G4String(commandLine).strip(G4String::leading);
00405   // target token is last token
00406   G4int jhead= input.last(' ');
00407   if(jhead != G4int(G4String::npos)) {
00408     input.remove(0, jhead);
00409     input= input.strip(G4String::leading);
00410   }
00411   //G4cout << "@@@@ input=" << input << G4endl;
00412 
00413   // command tree of "user specified directory"
00414   G4String vpath = currentCommandDir;
00415   G4String vcmd = "";
00416 
00417   if( !input.empty() ) {
00418     G4int len= input.length();
00419     G4int indx=-1;
00420     for(G4int i=len-1; i>=0; i--) {
00421       if(input[(size_t)i]=='/') {
00422         indx= i;
00423         break;
00424       }
00425     }
00426     // get abs. path
00427     if(indx != -1) vpath= GetAbsCommandDirPath(input(0,indx+1));  
00428     if(!(indx==0  && len==1)) vcmd= input(indx+1,len-indx-1);  // care for "/"
00429   }
00430 
00431   // list matched dirs/commands
00432   //G4cout << "@@@ vpath=" << vpath <<":vcmd=" << vcmd << G4endl;
00433   ListCommand(vpath, vpath+vcmd);
00434 
00435   G4cout << promptString << commandLine << std::flush;
00436 }
00437 
00439 void G4UItcsh::CompleteCommand()
00441 {
00442   // inputting string
00443   G4String input= G4String(commandLine).strip(G4String::leading);
00444   // target token is last token
00445   G4int jhead= input.last(' ');
00446   if(jhead != G4int(G4String::npos)) {
00447     input.remove(0, jhead);
00448     input= input.strip(G4String::leading);
00449   }
00450 
00451   // tail string
00452   size_t thead = input.find_last_of('/');
00453   G4String strtail = input;
00454   if (thead != G4String::npos) strtail = input(thead+1, input.size()-thead-1);
00455 
00456   // command tree of "user specified directory"  
00457   G4String vpath= currentCommandDir;
00458   G4String vcmd;
00459 
00460   G4int len= input.length();
00461   if(!input.empty()) {
00462     G4int indx= -1;
00463     for(G4int i=len-1; i>=0; i--) {
00464       if(input(i)=='/') {
00465         indx= i;
00466         break;
00467       }   
00468     }
00469     // get abs. path
00470     if(indx != -1) vpath= GetAbsCommandDirPath(input(0,indx+1));
00471     if(!(indx==0  && len==1)) vcmd= input(indx+1,len-indx-1);  // care for "/"
00472   }
00473 
00474   G4UIcommandTree* atree= GetCommandTree(vpath);  // get command tree
00475   if(atree == NULL) return;
00476 
00477   // list matched directories/commands
00478   G4String stream, strtmp;
00479   G4String inputpath= vpath+vcmd;
00480   G4int nMatch= 0;
00481 
00482   int Ndir= atree-> GetTreeEntry();
00483   int Ncmd= atree-> GetCommandEntry();
00484   
00485   // directory ...
00486   for(G4int idir=1; idir<=Ndir; idir++) {
00487     G4String fpdir= atree-> GetTree(idir)-> GetPathName();
00488     // matching test
00489     if( fpdir.index(inputpath, 0) == 0) {
00490       if(nMatch==0) {
00491         stream= GetCommandPathTail(fpdir);
00492       } else {
00493         strtmp= GetCommandPathTail(fpdir);
00494         stream= GetFirstMatchedString(stream, strtmp);
00495       }
00496       nMatch++;
00497     }
00498   }
00499   
00500   // command ...
00501   for(G4int icmd=1; icmd<=Ncmd; icmd++){
00502     G4String fpcmd= atree-> GetPathName() +
00503                     atree-> GetCommand(icmd) -> GetCommandName();
00504     // matching test
00505     if( fpcmd.index(inputpath, 0) ==0) {
00506       if(nMatch==0) {
00507         stream= GetCommandPathTail(fpcmd) + " ";
00508       } else {
00509         strtmp= GetCommandPathTail(fpcmd) + " ";
00510         stream= GetFirstMatchedString(stream, strtmp);
00511       }
00512       nMatch++;
00513     }
00514   }
00515 
00516   // display...
00517   input= commandLine;
00518   // target token is last token
00519   jhead= input.last(' ');
00520   if(jhead == G4int(G4String::npos)) jhead=0;
00521   else jhead++;
00522 
00523   G4int jt = jhead;
00524 
00525   G4String dspstr; 
00526   G4int i;
00527   for(i=jt; i<=G4int(input.length())-1; i++) dspstr+= G4String(AsciiBS); 
00528   for(i=jt; i<=G4int(input.length())-1; i++) dspstr+= G4String(' '); 
00529   for(i=jt; i<=G4int(input.length())-1; i++) dspstr+= G4String(AsciiBS); 
00530 
00531   dspstr+= (vpath + stream);
00532   if (nMatch == 0) dspstr+= strtail;
00533   G4cout << dspstr << std::flush;
00534 
00535   // command line string
00536   input.remove(jt);
00537   input+= (vpath + stream);
00538   if (nMatch==0) input+= strtail;
00539 
00540   commandLine= input;
00541   cursorPosition= commandLine.length()+1;
00542 }
00543 
00544 // --------------------------------------------------------------------
00545 //      commad line
00546 // --------------------------------------------------------------------
00548 G4String G4UItcsh::ReadLine()
00550 {
00551   InitializeCommandLine();
00552 
00553   char cc;
00554   do{  // input loop
00555     G4cin.get(cc);
00556 
00557     // treatment for special character
00558     switch(cc){
00559     case AsciiCtrA:       // ... move cursor to the top
00560       MoveCursorTop();
00561       break;
00562     case AsciiCtrB:       // ... backward cursor
00563       BackwardCursor();
00564       break;
00565     case AsciiCtrD:       // ... delete/exit/show matched list
00566       if(commandLine.length()!=0 && IsCursorLast()) ListMatchedCommand();
00567       else if (commandLine.empty()) {
00568         return G4String("exit");        
00569       } else DeleteCharacter();
00570       break;
00571     case AsciiCtrE:       // ... move cursor to the end
00572       MoveCursorEnd();
00573       break;
00574     case AsciiCtrF:       // ... forward cursor
00575       ForwardCursor();
00576       break;
00577     case AsciiCtrK:       // ... clear after the cursor
00578       ClearAfterCursor();
00579       break;
00580     case AsciiCtrL:       // ... clear screen
00581       // ClearScreen();
00582       break;
00583     case AsciiCtrN:     // ... next command
00584       NextCommand();
00585       break;
00586     case AsciiCtrP:     // ... previous command
00587       PreviousCommand();
00588       break;
00589     case AsciiTAB:         // ... command completion
00590       if( (!commandLine.empty()) && IsCursorLast()) CompleteCommand();
00591       break;
00592     case AsciiDEL:         // ... backspace
00593       BackspaceCharacter();
00594       break;
00595     case AsciiBS:          // ... backspace
00596       BackspaceCharacter();
00597       break;
00598     case AsciiCtrC:       // ... kill prompt
00599       break;
00600     case AsciiCtrQ:       // ... restarts suspeded output
00601       break;
00602     case AsciiCtrS:       // ... suspend output
00603       break;
00604     case AsciiCtrZ:       // ... suspend
00605       break;
00606     default:
00607       break;
00608     }
00609 
00610     // treatment for ESC. character
00611     if( cc == AsciiESC) { // ESC
00612       G4cin.get(cc);
00613       if (cc == '[' || cc == 'O') { // care for another termcap, such as konsole
00614         G4cin.get(cc);
00615         switch(cc) {
00616         case 'A': // [UP]
00617           cc = 'P' - '@';
00618           PreviousCommand();  // ... show previous commad
00619           break;
00620         case 'B': // [DOWN]
00621           cc = 'N' - '@';
00622           NextCommand();  // ... show next commad
00623           break;
00624         case 'C': // [RIGHT]
00625           cc = 'F' - '@';
00626           ForwardCursor();   // ... forward cursor
00627           break;
00628         case 'D': // [LEFT]
00629           cc = 'B' - '@';
00630           BackwardCursor();      // ... backward cursor
00631           break;
00632         default:  // who knows !?
00633           cc = 0;
00634           break;
00635         }
00636       }
00637     }
00638 
00639     // insert character to command line and display
00640     InsertCharacter(cc);
00641   
00642   } while( cc != '\n');
00643 
00644   return commandLine;
00645 }
00646 
00648 G4String G4UItcsh::GetCommandLineString(const char* msg)
00650 {
00651   SetTermToInputMode();
00652 
00653   MakePrompt(msg); // update
00654   relativeHistoryIndex= 0;
00655 
00656   G4cout << promptString << std::flush;
00657 
00658   G4String newCommand= ReadLine();  // read line...
00659   // multi-line
00660   while( (newCommand.length() > 0) &&
00661          ( newCommand[newCommand.length()-1] == '_') ) {
00662     newCommand.remove(newCommand.length()-1);
00663     G4cout << G4endl;
00664     promptString= "? ";
00665     G4cout << promptString << std::flush;
00666     G4String newLine= ReadLine();
00667     newCommand.append(newLine);
00668   }
00669 
00670   // update history...
00671   G4bool isMeaningfull= FALSE; // check NULL command
00672   for (size_t i=0; i<newCommand.length(); i++) {
00673     if(newCommand[i] != ' ') {
00674       isMeaningfull= TRUE;
00675       break;
00676     }
00677   }
00678   if( !newCommand.empty() && isMeaningfull) StoreHistory(newCommand);
00679 
00680   // reset terminal
00681   RestoreTerm();
00682 
00683   G4cout << G4endl;
00684   return newCommand;
00685 }
00686 
00688 G4String G4UItcsh::GetFirstMatchedString(const G4String& str1, 
00689                                          const G4String& str2) const
00691 {
00692   int nlen1= str1.length();
00693   int nlen2= str2.length();
00694 
00695   int nmin = nlen1<nlen2 ? nlen1 : nlen2;
00696 
00697   G4String strMatched;
00698   for(size_t i=0; G4int(i)<nmin; i++){
00699     if(str1[i]==str2[i]) {
00700       strMatched+= str1[i];
00701     } else {
00702       break;
00703     }
00704   }
00705 
00706   return strMatched;
00707 }
00708 
00709 // --------------------------------------------------------------------
00710 //      history
00711 // --------------------------------------------------------------------
00713 void G4UItcsh::StoreHistory(G4String aCommand)
00715 {
00716   G4int i= currentHistoryNo%maxHistory; 
00717   if(i==0) i=maxHistory;
00718 
00719   commandHistory[i-1]= aCommand;  // 0-offset
00720   currentHistoryNo++;
00721 }
00722 
00724 G4String G4UItcsh::RestoreHistory(G4int histNo)
00726 {
00727   if(histNo>= currentHistoryNo) return "";
00728 
00729   G4int index= histNo%maxHistory;
00730   if(index==0) index= maxHistory;
00731 
00732   return commandHistory[index-1]; // 0-offset
00733 }
00734 
00735 // --------------------------------------------------------------------
00736 //      terminal mode
00737 // --------------------------------------------------------------------
00739 void G4UItcsh::SetTermToInputMode()
00741 {  
00742   termios tiosbuf= tios;
00743 
00744   tiosbuf.c_iflag &= ~(BRKINT | ISTRIP);
00745   tiosbuf.c_iflag |= (IGNBRK | IGNPAR);
00746   tiosbuf.c_lflag &= ~(ICANON | IEXTEN | ECHO);
00747   tiosbuf.c_cc[VMIN] = 1;
00748   tiosbuf.c_cc[VTIME] = 0;
00749   
00750   tcsetattr(0, TCSAFLUSH, &tiosbuf);
00751 }
00752 
00753 
00755 void G4UItcsh::RestoreTerm()
00757 {
00758   tcsetattr(0, TCSAFLUSH, &tios);
00759 }  
00760 
00761 #endif
00762 

Generated on Mon May 27 17:50:06 2013 for Geant4 by  doxygen 1.4.7