00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "TTerminalUserInterface.hh"
00022 #include "TRunManager.hh"
00023 #include "TCommand.hh"
00024
00025 static const Tchar _asciiCtrlA = '\001';
00026 static const Tchar _asciiCtrlB = '\002';
00027 static const Tchar _asciiCtrlD = '\004';
00028 static const Tchar _asciiCtrlE = '\005';
00029 static const Tchar _asciiCtrlF = '\006';
00030 static const Tchar _asciiCtrlK = '\013';
00031 static const Tchar _asciiCtrlL = '\014';
00032 static const Tchar _asciiCtrlN = '\016';
00033 static const Tchar _asciiCtrlP = '\020';
00034 static const Tchar _asciiCtrlY = '\031';
00035 static const Tchar _asciiCtrlZ = '\032';
00036 static const Tchar _asciiTAB = '\011';
00037 static const Tchar _asciiBS = '\010';
00038 static const Tchar _asciiDEL = '\177';
00039 static const Tchar _asciiESC = '\033';
00040
00041 TTerminalUserInterface::TTerminalUserInterface( const Tstring& prompt, const Tstring& history )
00042 : TUserInterface( history ), theCommandHistoryIndex( 0 ),
00043 theCursorPosition( 0 ), thePrompt( prompt ), theAcceptString(),
00044 theStringBuffer(), theCommandBuffer()
00045 {
00046 tcgetattr( 0, &theTerminal );
00047 setupterm( 0, 1, 0 );
00048 }
00049
00050 TTerminalUserInterface::~TTerminalUserInterface()
00051 {;}
00052
00053 Tvoid TTerminalUserInterface::initializeCommandLine()
00054 {
00055 theCommandHistoryIndex = (Tint)theCommandHistory.size();
00056 theCursorPosition = 0;
00057 theAcceptString.erase();
00058 theCommandBuffer.erase();
00059 std::cout << thePrompt << std::flush;
00060 return;
00061 }
00062
00063 const Tstring& TTerminalUserInterface::readLine()
00064 {
00065 setTerminalInputMode();
00066 initializeCommandLine();
00067
00068 Tchar cc;
00069
00070 do {
00071 Tcin.get( cc );
00072
00073 switch ( cc ) {
00074
00075 case _asciiCtrlA:
00076 moveCursorTop();
00077 break;
00078
00079 case _asciiCtrlB:
00080 backwardCursor();
00081 break;
00082
00083 case _asciiCtrlD:
00084 deleteCharacter();
00085 break;
00086
00087 case _asciiCtrlE:
00088 moveCursorEnd();
00089 break;
00090
00091 case _asciiCtrlF:
00092 forwardCursor();
00093 break;
00094
00095 case _asciiCtrlK:
00096 cutCharacter();
00097 break;
00098
00099 case _asciiCtrlL:
00100 clearScreen();
00101 break;
00102
00103 case _asciiCtrlN:
00104 nextCommand();
00105 break;
00106
00107 case _asciiCtrlP:
00108 previousCommand();
00109 break;
00110
00111 case _asciiCtrlY:
00112 pasteCharacter();
00113 break;
00114
00115 case _asciiCtrlZ:
00116 suspendTerminal();
00117 break;
00118
00119 case _asciiTAB:
00120 completeCommand();
00121 break;
00122
00123 case _asciiBS:
00124 backspaceCharacter();
00125 break;
00126
00127 case _asciiDEL:
00128 deleteCharacter();
00129 break;
00130
00131 default:
00132 break;
00133 }
00134
00135 if ( cc == _asciiESC ) {
00136 Tcin.get( cc );
00137 if ( cc == '[' ) {
00138 Tcin.get( cc );
00139 switch ( cc ) {
00140 case 'A':
00141 cc = 'P' - '@';
00142 previousCommand();
00143 break;
00144 case 'B':
00145 cc = 'N' - '@';
00146 nextCommand();
00147 break;
00148 case 'C':
00149 cc = 'F' - '@';
00150 forwardCursor();
00151 break;
00152 case 'D':
00153 cc = 'B' - '@';
00154 backwardCursor();
00155 break;
00156 default:
00157 cc = 0;
00158 break;
00159 }
00160 }
00161 }
00162
00163 insertCharacter( cc );
00164
00165 } while ( cc != Teol );
00166
00167
00168 if ( theAcceptString.size() > 0 ) {
00169
00170
00171 while ( !( theAcceptString.empty() ) && ( theAcceptString.rfind( Tspace ) == theAcceptString.size() - 1 ) ) {
00172 theAcceptString.erase( theAcceptString.end() - 1 );
00173 }
00174
00175
00176 while ( !( theAcceptString.empty() ) && ( theAcceptString.find( Tspace ) == 0 ) ) {
00177 theAcceptString.erase( theAcceptString.begin() );
00178 }
00179 }
00180 std::cout << std::endl;
00181
00182 resetTerminal();
00183
00184 return theAcceptString;
00185 }
00186
00187 Tvoid TTerminalUserInterface::insertCharacter( Tchar cc )
00188 {
00189 if ( ! isprint( cc ) ) {
00190 return;
00191 }
00192
00193 std::cout << cc;
00194 for ( Tint i = theCursorPosition; i < (Tint)theAcceptString.size(); i ++ ) {
00195 std::cout << theAcceptString[ i ];
00196 }
00197 for ( Tint i = theCursorPosition; i < (Tint)theAcceptString.size(); i ++ ) {
00198 std::cout << _asciiBS;
00199 }
00200 std::cout << std::flush;
00201
00202 if ( isCursorEnd() ) {
00203 theAcceptString += cc;
00204 } else {
00205 theAcceptString.insert( theAcceptString.begin() + theCursorPosition, cc );
00206 }
00207
00208 theCursorPosition ++;
00209 return;
00210 }
00211
00212 Tvoid TTerminalUserInterface::backspaceCharacter()
00213 {
00214 if ( isCursorTop() ) {
00215 return;
00216 }
00217
00218 if ( isCursorEnd() ) {
00219 std::cout << _asciiBS << Tspace << _asciiBS;
00220 } else {
00221 std::cout << _asciiBS;
00222 for ( Tint i = theCursorPosition; i < (Tint)theAcceptString.size(); i ++ ) {
00223 std::cout << theAcceptString[ i ];
00224 }
00225 std::cout << Tspace;
00226 for ( Tint i = theCursorPosition; i < (Tint)( theAcceptString.size() + 1 ); i ++ ) {
00227 std::cout << _asciiBS;
00228 }
00229 }
00230 std::cout << std::flush;
00231 theAcceptString.erase( theCursorPosition - 1, 1 );
00232 theCursorPosition --;
00233 return;
00234 }
00235
00236 Tvoid TTerminalUserInterface::deleteCharacter()
00237 {
00238 forwardCursor();
00239 backspaceCharacter();
00240 return;
00241 }
00242
00243 Tvoid TTerminalUserInterface::clearLine()
00244 {
00245 moveCursorTop();
00246 clearAfterCursor();
00247 return;
00248 }
00249
00250 Tvoid TTerminalUserInterface::clearScreen()
00251 {
00252 ClearScreen();
00253 if ( ! theAcceptString.empty() ) {
00254 Tint posbuf = theCursorPosition;
00255 theCursorPosition = theAcceptString.size();
00256 std::cout << thePrompt << theAcceptString;
00257 Tint nback = theCursorPosition - posbuf;
00258 if ( nback > 0 ) {
00259 for ( Tint i = 0; i < nback; i ++ ) {
00260 backwardCursor();
00261 }
00262 }
00263 return;
00264 }
00265 theCursorPosition = 0;
00266 std::cout << thePrompt << std::flush;
00267 return;
00268 }
00269
00270 Tvoid TTerminalUserInterface::clearAfterCursor()
00271 {
00272 if ( isCursorEnd() ) {
00273 return;
00274 }
00275 for ( Tint i = theCursorPosition; i < (Tint)theAcceptString.size(); i ++ ) {
00276 std::cout << Tspace;
00277 }
00278 for ( Tint i = (Tint)theAcceptString.size(); i > theCursorPosition; i -- ) {
00279 std::cout << _asciiBS;
00280 }
00281 std::cout << std::flush;
00282 theAcceptString.erase( theCursorPosition, theAcceptString.size() - theCursorPosition );
00283 return;
00284 }
00285
00286 Tvoid TTerminalUserInterface::forwardCursor()
00287 {
00288 if ( isCursorEnd() ) {
00289 return;
00290 }
00291 std::cout << theAcceptString[ (Tsize_t)( theCursorPosition ) ] << std::flush;
00292 theCursorPosition ++;
00293 return;
00294 }
00295
00296 Tvoid TTerminalUserInterface::backwardCursor()
00297 {
00298 if ( isCursorTop() ) {
00299 return;
00300 }
00301 std::cout << _asciiBS << std::flush;
00302 theCursorPosition --;
00303 return;
00304 }
00305
00306 Tvoid TTerminalUserInterface::moveCursorTop()
00307 {
00308 while ( isCursorTop() == Tfalse ) {
00309 backwardCursor();
00310 }
00311 return;
00312 }
00313
00314 Tvoid TTerminalUserInterface::moveCursorEnd()
00315 {
00316 while ( isCursorEnd() == Tfalse ) {
00317 forwardCursor();
00318 }
00319 return;
00320 }
00321
00322 Tvoid TTerminalUserInterface::cutCharacter()
00323 {
00324 if ( !( theStringBuffer.empty() ) ) {
00325 theStringBuffer.erase();
00326 }
00327 for ( Tint i = theCursorPosition; i < (Tint)theAcceptString.size(); i ++ ) {
00328 theStringBuffer += theAcceptString[ i ];
00329 }
00330 Tint nback = (Tint)( theAcceptString.size() ) - theCursorPosition;
00331 moveCursorEnd();
00332 for ( Tint i = 0; i < nback; i ++ ) {
00333 backspaceCharacter();
00334 }
00335 return;
00336 }
00337
00338 Tvoid TTerminalUserInterface::pasteCharacter()
00339 {
00340 if ( theStringBuffer.empty() ) {
00341 return;
00342 }
00343 for ( int i = 0; i < (Tint)theStringBuffer.size(); i ++ ) {
00344 insertCharacter( theStringBuffer[ i ] );
00345 }
00346 return;
00347 }
00348
00349 Tvoid TTerminalUserInterface::nextCommand()
00350 {
00351 if ( theCommandHistoryIndex > (Tint)theCommandHistory.size() - 1 ) {
00352 return;
00353 }
00354 clearLine();
00355 Tstring com;
00356 if ( theCommandHistoryIndex == (Tint)theCommandHistory.size() - 1 ) {
00357 com = theCommandBuffer;
00358 } else {
00359 com = theCommandHistory[ theCommandHistoryIndex + 1 ];
00360 }
00361
00362 for ( int i = 0; i < (Tint)com.size(); i ++ ) {
00363 insertCharacter( com[ i ] );
00364 }
00365 theCommandHistoryIndex ++;
00366 return;
00367 }
00368
00369 Tvoid TTerminalUserInterface::previousCommand()
00370 {
00371 if ( theCommandHistory.empty() || theCommandHistoryIndex == 0 ) {
00372 return;
00373 }
00374
00375 if ( theCommandHistoryIndex == (Tint)theCommandHistory.size() ) {
00376 theCommandBuffer = theAcceptString;
00377 }
00378
00379 clearLine();
00380
00381 Tstring com = theCommandHistory[ theCommandHistoryIndex - 1 ];
00382 for ( int i = 0; i < (Tint)com.size(); i ++ ) {
00383 insertCharacter( com[ i ] );
00384 }
00385 theCommandHistoryIndex --;
00386 return;
00387 }
00388
00389 Tvoid TTerminalUserInterface::completeCommand()
00390 {
00391 Tstring strbuf = theAcceptString;
00392 TstringList input = divide( strbuf );
00393
00394 if ( input.empty() ) {
00395 complete();
00396 } else if ( input.size() == 1 && strbuf[ strbuf.size() - 1 ] != ' ' ) {
00397 complete( input[ 0 ] );
00398 } else if ( input.size() == 1 && input[ 0 ] == "cd" && strbuf[ strbuf.size() - 1 ] == ' ' ) {
00399 completeDirectory();
00400 } else if ( input.size() == 2 && input[ 0 ] == "cd" && strbuf[ strbuf.size() - 1 ] != ' ' ) {
00401 completeDirectory( input[ 1 ] );
00402 } else if ( input.size() > 1 && strbuf[ strbuf.size() - 1 ] != ' ' ) {
00403 complete( input );
00404 } else if ( input.size() > 0 && strbuf[ strbuf.size() - 1 ] == ' ' ) {
00405 complete();
00406 }
00407
00408 return;
00409 }
00410
00411 Tvoid TTerminalUserInterface::complete()
00412 {
00413 std::cout << std::endl;
00414 ExecuteCommand( "ls" );
00415
00416 if ( ! theAcceptString.empty() ) {
00417 Tint posbuf = theCursorPosition;
00418 theCursorPosition = theAcceptString.size();
00419 std::cout << thePrompt << theAcceptString;
00420 Tint nback = theCursorPosition - posbuf;
00421 if ( nback > 0 ) {
00422 for ( Tint i = 0; i < nback; i ++ ) {
00423 backwardCursor();
00424 }
00425 }
00426 return;
00427 }
00428
00429 theCursorPosition = 0;
00430 std::cout << thePrompt << std::flush;
00431
00432 return;
00433 }
00434
00435 Tvoid TTerminalUserInterface::complete( const Tstring& input )
00436 {
00437
00438 Tstring abspath = ModifyPath( input );
00439 TstringList candidate;
00440 for ( Tint i = 0; i < theCommandTable.GetSize(); i ++ ) {
00441 Tstring fullname = theCommandTable[ i ].GetFullName();
00442 if ( fullname.substr( 0, abspath.size() ) == abspath ) {
00443 candidate.push_back( fullname );
00444 } else if ( theCommandTable[ i ].IsBuiltinCommand() && fullname.substr( 0, input.size() ) == input ) {
00445 candidate.push_back( fullname );
00446 }
00447 }
00448
00449
00450 if ( candidate.empty() ) {
00451 return;
00452 }
00453
00454
00455
00456
00457 TstringList local = candidate;
00458 for ( Tsize_t i = 0; i < local.size(); i ++ ) {
00459 if ( abspath != "/" && input[ input.size() - 1 ] == '/' ) {
00460 local[ i ] = input.substr( 0, input.size() - 1 ) + local[ i ].substr( abspath.size(), local[ i ].size() - abspath.size() );
00461 } else {
00462 if ( candidate[ i ][ 0 ] == '/' ) {
00463 local[ i ] = input + local[ i ].substr( abspath.size(), local[ i ].size() - abspath.size() );
00464 }
00465 }
00466 }
00467
00468
00469 if ( local.size() == 1 ) {
00470
00471 clearLine();
00472 theAcceptString = local[ 0 ];
00473 theAcceptString += ' ';
00474 theCursorPosition = theAcceptString.size();
00475 std::cout << theAcceptString;
00476 } else {
00477
00478 for ( Tsize_t i = 0; i < candidate.size(); i ++ ) {
00479 if ( theCommandTable.GetCommandSpecified( candidate[ i ] ).IsAliasedCommand() ) {
00480 local[ i ] += "@";
00481 } else {
00482 local[ i ] += "*";
00483 }
00484 }
00485
00486 theCommandTable.Sort( local );
00487 std::cout << std::endl;
00488 theCommandTable.List( GetNumberOfColumns(), local );
00489
00490
00491 Tsize_t pos = input.size() - 1;
00492 Tbool coincidence = Ttrue;
00493 while ( coincidence ) {
00494 pos ++;
00495 for ( Tsize_t i = 0; i < local.size() - 1; i ++ ) {
00496 coincidence &= ( ( local[ i ] )[ pos ] == ( local[ i + 1 ] )[ pos ] );
00497 }
00498 }
00499
00500 theAcceptString = local[ 0 ];
00501 theAcceptString.erase( pos, ( theAcceptString.size() - pos ) );
00502 theCursorPosition = theAcceptString.size();
00503 std::cout << thePrompt << theAcceptString;
00504
00505 }
00506
00507 std::cout << std::flush;
00508
00509 return;
00510 }
00511
00512 Tvoid TTerminalUserInterface::complete( const TstringList& inputs )
00513 {
00514
00515
00516
00517 return;
00518 }
00519
00520 Tvoid TTerminalUserInterface::completeDirectory()
00521 {
00522 TstringList dirs = theCommandTable.GetDirectoryList( theCurrentWorkingDirectory );
00523 if ( dirs.empty() ) {
00524 return;
00525 }
00526
00527 std::cout << std::endl;
00528 theCommandTable.List( GetNumberOfColumns(), theCommandTable.Sort( dirs ) );
00529
00530 std::cout << thePrompt << theAcceptString;
00531
00532 return;
00533 }
00534
00535 Tvoid TTerminalUserInterface::completeDirectory( const Tstring& input )
00536 {
00537 if ( input.size() >= 2 && input.substr( input.size() - 2, 2 ) == ".." ) {
00538 theAcceptString += "/";
00539 theCursorPosition += 1;
00540 std::cout << "/" << std::flush;
00541 return;
00542 }
00543
00544
00545 enum { DIRLIST, COMPLETE };
00546 Tint mode;
00547 if ( input[ input.size() - 1 ] == '/' ) {
00548 mode = DIRLIST;
00549 } else {
00550 mode = COMPLETE;
00551 }
00552
00553
00554 Tstring temppath = input;
00555 if ( temppath.size() > 2 && temppath.substr( 0, 2 ) == "./" ) {
00556 temppath.erase( 0, 2 );
00557 }
00558 Tstring abspath = ModifyPath( temppath );
00559 Tstring targetdir;
00560 Tsize_t pos = abspath.rfind( "/" );
00561 if ( input[ input.size() - 1 ] == '/' && theCommandTable.AlreadyExistDirectory( abspath ) ) {
00562 targetdir = abspath;
00563 } else if ( pos == 0 ) {
00564 targetdir = "/";
00565 } else {
00566 targetdir = abspath.substr( 0, pos );
00567 }
00568
00569 TstringList dirlist = theCommandTable.GetDirectoryList( targetdir );
00570 TstringList candidate;
00571 if ( dirlist.empty() ) {
00572 return;
00573 } else if ( mode == DIRLIST ) {
00574 std::cout << std::endl;
00575 theCommandTable.Sort( dirlist );
00576 theCommandTable.List( GetNumberOfColumns(), dirlist );
00577 theCursorPosition = theAcceptString.size();
00578 std::cout << thePrompt << theAcceptString << std::flush;
00579 return;
00580 } else if ( mode == COMPLETE ) {
00581 Tsize_t pos = abspath.rfind( '/' );
00582 Tstring dir = abspath.substr( pos + 1, abspath.size() - pos - 1 );
00583 for ( Tsize_t i = 0; i < dirlist.size(); i ++ ) {
00584 if ( dirlist[ i ].substr( 0, dir.size() ) == dir ) {
00585 candidate.push_back( dirlist[ i ] );
00586 }
00587 }
00588
00589 if ( candidate.empty() ) {
00590 return;
00591 }
00592 } else {
00593 return;
00594 }
00595
00596
00597
00598 if ( candidate.size() == 1 ) {
00599
00600 clearLine();
00601 theAcceptString = "cd " + input.substr( 0, input.rfind( '/' ) + 1 ) + candidate[ 0 ];
00602 theCursorPosition = theAcceptString.size();
00603 std::cout << theAcceptString;
00604 } else {
00605 std::cout << std::endl;
00606 theCommandTable.Sort( candidate );
00607 theCommandTable.List( GetNumberOfColumns(), candidate );
00608
00609
00610 Tsize_t pos = 0;
00611 Tbool coincidence = Ttrue;
00612 while ( coincidence ) {
00613 pos ++;
00614 for ( Tsize_t i = 0; i < candidate.size() - 1; i ++ ) {
00615 coincidence &= ( ( candidate[ i ] )[ pos ] == ( candidate[ i + 1 ] )[ pos ] );
00616 }
00617 }
00618
00619 theAcceptString = candidate[ 0 ];
00620 theAcceptString.erase( pos, ( theAcceptString.size() - pos ) );
00621 theAcceptString = "cd " + input.substr( 0, input.rfind( '/' ) + 1 ) + theAcceptString;
00622 theCursorPosition = theAcceptString.size();
00623 std::cout << thePrompt << theAcceptString << std::flush;
00624 }
00625
00626 return;
00627 }
00628
00629 Tvoid TTerminalUserInterface::suspendTerminal() const
00630 {
00631 pid_t pid = getpid();
00632 if ( fork() == 0 ) {
00633 exit( kill( pid, SIGSTOP ) );
00634 } else {
00635 wait( 0 );
00636 }
00637 return;
00638 }
00639
00640 Tvoid TTerminalUserInterface::setTerminalInputMode()
00641 {
00642 termios tiosbuf = theTerminal;
00643 tiosbuf.c_iflag |= IGNBRK;
00644 tiosbuf.c_iflag |= IGNPAR;
00645 tiosbuf.c_lflag &= ~ICANON;
00646 tiosbuf.c_lflag &= ~ECHO;
00647 tiosbuf.c_lflag &= ~ISIG;
00648 tiosbuf.c_cc[ VMIN ] = 1;
00649 tiosbuf.c_cc[ VTIME ] = 0;
00650 tcsetattr( 0, TCSANOW, &tiosbuf );
00651
00652 return;
00653 }
00654
00655 Tvoid TTerminalUserInterface::resetTerminal()
00656 {
00657 tcsetattr( 0, TCSANOW, &theTerminal );
00658 return;
00659 }
00660
00661 Tbool TTerminalUserInterface::AcceptCommand()
00662 {
00663 return ( theAcceptString.c_str() != 0 ) ? Ttrue : Tfalse;
00664 }
00665
00666 const Tstring& TTerminalUserInterface::GetInputCommand()
00667 {
00668 return readLine();
00669 }
00670
00671 Tvoid TTerminalUserInterface::NotFoundCommand( const Tstring& commandname ) const
00672 {
00673 std::cerr << commandname << ": Command not found." << std::endl;
00674 return;
00675 }
00676
00677 TstringList TTerminalUserInterface::divide( const Tstring& input ) const
00678 {
00679 TstringList divided;
00680 Tsize_t begin = 0;
00681 Tsize_t end = 0;
00682 Tsize_t inputlen = input.size();
00683
00684 do {
00685 begin = input.find_first_not_of( ' ', end );
00686 end = input.find( ' ', begin );
00687 if ( begin < inputlen && end >= inputlen ) {
00688 divided.push_back( input.substr( begin, inputlen - begin ) );
00689 } else if ( begin >= inputlen && end >= inputlen ) {
00690 break;
00691 } else {
00692 divided.push_back( input.substr( begin, end - begin ) );
00693 }
00694 } while ( begin < inputlen && end < inputlen );
00695
00696 return divided;
00697 }
00698
00699 #ifdef __CLDAQ_ROOT_DLL
00700 ClassImp(TTerminalUserInterface)
00701 #endif