// ========================================================================= // @author Leonardo Florez-Valencia (florez-l@javeriana.edu.co) // ========================================================================= #include #include // ------------------------------------------------------------------------- #if defined(WIN32) || defined(_WIN32) || defined(__WIN32) # include # include const char TinyCon::Console::KEY_CTRL1 = -32; const char TinyCon::Console::BACKSPACE = 8; const char TinyCon::Console::UP_ARROW = 72; const char TinyCon::Console::DOWN_ARROW = 80; const char TinyCon::Console::RIGHT_ARROW = 77; const char TinyCon::Console::LEFT_ARROW = 75; const char TinyCon::Console::NEWLINE = '\r'; #else # include # include const char TinyCon::Console::KEY_CTRL1 = 17; const char TinyCon::Console::BACKSPACE = 127; const char TinyCon::Console::UP_ARROW = 65; const char TinyCon::Console::DOWN_ARROW = 66; const char TinyCon::Console::RIGHT_ARROW = 67; const char TinyCon::Console::LEFT_ARROW = 68; const char TinyCon::Console::NEWLINE = '\n'; int getch( ) { struct termios oldt, newt; int ch; tcgetattr( STDIN_FILENO, &oldt ); newt = oldt; newt.c_lflag &= ~( ICANON | ECHO ); tcsetattr( STDIN_FILENO, TCSANOW, &newt ); ch = getchar( ); if( ch == TinyCon::Console::ESC ) { ch = getchar( ); if( ch == 91 ) ch = TinyCon::Console::KEY_CTRL1; } tcsetattr( STDIN_FILENO, TCSANOW, &oldt ); return( ch ); } #endif // defined(WIN32) || defined(_WIN32) || defined(__WIN32) // ------------------------------------------------------------------------- const unsigned long TinyCon::Console::MAX_HISTORY = 500; const char TinyCon::Console::TAB = 9; const char TinyCon::Console::ESC = 27; const char TinyCon::Console::DEL = 51; const short TinyCon::Console::M_LINE = 0; const short TinyCon::Console::M_PASSWORD = 1; // ------------------------------------------------------------------------- TinyCon::Console:: Console( ) { this->m_MaxHistory = Self::MAX_HISTORY; this->m_Quit = false; this->m_Pos = -1; this->m_LinePos = 0; this->m_SkipOut = false; } // ------------------------------------------------------------------------- TinyCon::Console:: Console( const std::string& prompt ) { this->m_MaxHistory = Self::MAX_HISTORY; this->m_Quit = false; this->m_Prompt = prompt; this->m_Pos = -1; this->m_LinePos = 0; this->m_SkipOut = false; } // ------------------------------------------------------------------------- TinyCon::Console:: ~Console( ) { } // ------------------------------------------------------------------------- void TinyCon::Console:: run( ) { std::cout << this->m_Prompt; bool ok = true; while( ok ) { this->m_Char = getch( ); if( this->hotkeys( this->m_Char ) == 0 ) { switch( this->m_Char ) { case Self::ESC: { } // end case break; case Self::KEY_CTRL1: { char c = getch( ); switch( c ) { case Self::UP_ARROW: { if( this->m_History.size( ) > 0 ) { if( this->m_Pos == -1 ) { // store current command this->m_Unused = ""; this->m_Unused.assign( this->m_Buffer.begin( ), this->m_Buffer.end( ) ); } // end if // clear line for( int i = 0; i < this->m_LinePos; ++i ) std::cout << "\b \b"; // clean buffer this->m_Buffer.clear( ); this->m_Pos++; if( this->m_Pos > ( this->m_History.size( ) - 1 ) ) this->m_Pos = this->m_History.size( ) - 1; // store in buffer for( int i = 0; i < this->m_History[ this->m_Pos ].size( ); ++i ) this->m_Buffer.push_back( this->m_History[ this->m_Pos ][ i ] ); this->m_LinePos = this->m_Buffer.size( ); std::cout << this->m_History[ this->m_Pos ]; } // end if } // end case break; case Self::DOWN_ARROW: { if( this->m_History.size( ) > 0 ) { // clear line for( int i = 0; i < this->m_LinePos; ++i ) std::cout << "\b \b"; // clean buffer this->m_Buffer.clear( ); this->m_Pos--; if( this->m_Pos < -1 ) this->m_Pos = -1; if( this->m_Pos >= 0 ) { std::cout << this->m_History[ this->m_Pos ]; // store in buffer for( int i = 0; i < this->m_History[ this->m_Pos ].size( ); ++i ) this->m_Buffer.push_back( this->m_History[ this->m_Pos ][ i ] ); } else { if( this->m_Buffer.size( ) > 0 ) { std::cout << this->m_Unused; // store in buffer for( int i = 0; i < this->m_Unused.size( ); ++i ) this->m_Buffer.push_back( this->m_Unused[ i ] ); } // end if } // end if this->m_LinePos = this->m_Buffer.size( ); } // end if } // end case break; case Self::LEFT_ARROW: { // if there are characters to move left over, do so if( this->m_LinePos > 0 ) { std::cout << "\b"; this->m_LinePos--; } // end if } // end case break; case Self::RIGHT_ARROW: { // if there are characters to move right over, do so if( this->m_LinePos < this->m_Buffer.size( ) ) { std::cout << this->m_Buffer[ this->m_LinePos ]; this->m_LinePos++; } // end if } // end case break; case Self::DEL: { if( this->m_LinePos < this->m_Buffer.size( ) ) { this->m_SkipOut = true; this->m_Buffer.erase( this->m_Buffer.begin( ) + this->m_LinePos ); // update screen after current position for( int i = this->m_LinePos; i < this->m_Buffer.size( ); ++i ) std::cout << this->m_Buffer[ i ]; // erase last char std::cout << " "; for( int i = this->m_LinePos; i < this->m_Buffer.size( ); ++i ) std::cout << "\b"; // make-up for erase position std::cout << "\b"; } // end if } // end case break; default: { this->m_SkipOut = true; } // end case break; } // end switch } // end case break; case Self::BACKSPACE: { if( this->m_LinePos > 0 ) { // move cursor back, blank char, and move cursor back again std::cout << "\b \b"; // don't forget to clean the buffer and update line position if( this->m_LinePos == this->m_Buffer.size( ) ) { this->m_Buffer.pop_back( ); this->m_LinePos--; } else { this->m_LinePos--; this->m_Buffer.erase( this->m_Buffer.begin( ) + this->m_LinePos ); // update screen after current position for( int i = this->m_LinePos; i < this->m_Buffer.size( ); ++i ) std::cout << this->m_Buffer[ i ]; // erase last char std::cout << " "; for( int i = this->m_LinePos + 1; i < this->m_Buffer.size( ); ++i ) std::cout << "\b"; // make-up for erase position and go to new position std::cout << "\b\b"; } // end if } // end if } // end case break; case Self::TAB: { } // end case break; case Self::NEWLINE: { // store in string this->m_String.assign( this->m_Buffer.begin( ), this->m_Buffer.end( ) ); // save command to history // trimming of command should be done in callback function if( this->m_String.length( ) > 0 ) this->m_History.push_front( this->m_String ); // run command std::cout << std::endl; this->trigger( this->m_String ); // check for exit command ok = !( this->m_Quit ); if( ok ) { if( this->m_History.size( ) > this->m_MaxHistory ) this->m_History.pop_back( ); // clean buffer this->m_Buffer.clear( ); // print prompt. new line should be added from callback function std::cout << this->m_Prompt; // reset position this->m_Pos = -1; this->m_LinePos = 0; } // end if } // end case break; default: { if( !( this->m_SkipOut ) ) { std::cout << this->m_Char; if( this->m_LinePos != this->m_Buffer.size( ) ) { // line position is not at end. Insert new char this->m_Buffer.insert( this->m_Buffer.begin( ) + this->m_LinePos, this->m_Char ); // update screen after current position for( int i = this->m_LinePos + 1; i < this->m_Buffer.size( ); ++i ) std::cout << this->m_Buffer[ i ]; for( int i = this->m_LinePos + 1; i < this->m_Buffer.size( ); ++i ) std::cout << "\b"; } else this->m_Buffer.push_back( this->m_Char ); this->m_LinePos++; } else this->m_SkipOut = false; } // end case break; } // end switch } // end if } // end while } // ------------------------------------------------------------------------- void TinyCon::Console:: setPrompt( const std::string& prompt ) { this->m_Prompt = prompt; } // ------------------------------------------------------------------------- int TinyCon::Console:: trigger( const std::string& cmd ) { return( ( this->m_Quit )? 1: 0 ); } // ------------------------------------------------------------------------- int TinyCon::Console:: hotkeys( char c ) { return( 0 ); } // ------------------------------------------------------------------------- void TinyCon::Console:: pause( ) { getch( ); } // ------------------------------------------------------------------------- void TinyCon::Console:: quit( ) { this->m_Quit = true; } // ------------------------------------------------------------------------- std::string TinyCon::Console:: getLine( ) { return( this->getLine( Self::M_LINE, "" ) ); } // ------------------------------------------------------------------------- std::string TinyCon::Console:: getLine( int mode, const std::string& delimiter ) { std::string line; char c; bool ok = true; while( ok ) { c = getch( ); if( c == Self::NEWLINE ) { std::cout << std::endl; ok = false; } else if( c == Self::BACKSPACE ) { if( line.length( ) > 0 ) { line = line.substr( 0,line.size( ) - 1 ); if( mode != Self::M_PASSWORD ) std::cout << "\b \b"; } // end if } else { line += c; if( mode != Self::M_PASSWORD ) std::cout << c; } // end if } // end while return( line ); } // ------------------------------------------------------------------------- void TinyCon::Console:: setMaxHistory( int count ) { this->m_MaxHistory = count; } // ------------------------------------------------------------------------- void TinyCon::Console:: setBuffer( const std::string& buffer ) { this->m_Buffer.assign( buffer.begin( ), buffer.end( ) ); this->m_LinePos = this->m_Buffer.size( ); } // eof - $RCSfile$