123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402 |
- #include <algorithm>
- #include <memory>
- #include <fstream>
- #include <cstring>
-
- #ifndef _WIN32
-
- #include <unistd.h>
- #include <fcntl.h>
- #include <sys/stat.h>
-
- #endif /* _WIN32 */
-
- #include "replxx.hxx"
- #include "history.hxx"
-
- using namespace std;
-
- namespace replxx {
-
- namespace {
- void delete_ReplxxHistoryScanImpl( Replxx::HistoryScanImpl* impl_ ) {
- delete impl_;
- }
- }
-
- static int const REPLXX_DEFAULT_HISTORY_MAX_LEN( 1000 );
-
- Replxx::HistoryScan::HistoryScan( impl_t impl_ )
- : _impl( std::move( impl_ ) ) {
- }
-
- bool Replxx::HistoryScan::next( void ) {
- return ( _impl->next() );
- }
-
- Replxx::HistoryScanImpl::HistoryScanImpl( History::entries_t const& entries_ )
- : _entries( entries_ )
- , _it( _entries.end() )
- , _utf8Cache()
- , _entryCache( std::string(), std::string() )
- , _cacheValid( false ) {
- }
-
- Replxx::HistoryEntry const& Replxx::HistoryScan::get( void ) const {
- return ( _impl->get() );
- }
-
- bool Replxx::HistoryScanImpl::next( void ) {
- if ( _it == _entries.end() ) {
- _it = _entries.begin();
- } else {
- ++ _it;
- }
- _cacheValid = false;
- return ( _it != _entries.end() );
- }
-
- Replxx::HistoryEntry const& Replxx::HistoryScanImpl::get( void ) const {
- if ( _cacheValid ) {
- return ( _entryCache );
- }
- _utf8Cache.assign( _it->text() );
- _entryCache = Replxx::HistoryEntry( _it->timestamp(), _utf8Cache.get() );
- _cacheValid = true;
- return ( _entryCache );
- }
-
- Replxx::HistoryScan::impl_t History::scan( void ) const {
- return ( Replxx::HistoryScan::impl_t( new Replxx::HistoryScanImpl( _entries ), delete_ReplxxHistoryScanImpl ) );
- }
-
- History::History( void )
- : _entries()
- , _maxSize( REPLXX_DEFAULT_HISTORY_MAX_LEN )
- , _current( _entries.begin() )
- , _yankPos( _entries.end() )
- , _previous( _entries.begin() )
- , _recallMostRecent( false )
- , _unique( true ) {
- }
-
- void History::add( UnicodeString const& line, std::string const& when ) {
- if ( _maxSize <= 0 ) {
- return;
- }
- if ( ! _entries.empty() && ( line == _entries.back().text() ) ) {
- _entries.back() = Entry( now_ms_str(), line );
- return;
- }
- remove_duplicate( line );
- trim_to_max_size();
- _entries.emplace_back( when, line );
- _locations.insert( make_pair( line, last() ) );
- if ( _current == _entries.end() ) {
- _current = last();
- }
- _yankPos = _entries.end();
- }
-
- #ifndef _WIN32
- class FileLock {
- std::string _path;
- int _lockFd;
- public:
- FileLock( std::string const& name_ )
- : _path( name_ + ".lock" )
- , _lockFd( ::open( _path.c_str(), O_CREAT | O_RDWR, 0600 ) ) {
- static_cast<void>( ::lockf( _lockFd, F_LOCK, 0 ) == 0 );
- }
- ~FileLock( void ) {
- static_cast<void>( ::lockf( _lockFd, F_ULOCK, 0 ) == 0 );
- ::close( _lockFd );
- ::unlink( _path.c_str() );
- return;
- }
- };
- #endif
-
- bool History::save( std::string const& filename, bool sync_ ) {
- #ifndef _WIN32
- mode_t old_umask = umask( S_IXUSR | S_IRWXG | S_IRWXO );
- FileLock fileLock( filename );
- #endif
- entries_t entries;
- locations_t locations;
- if ( ! sync_ ) {
- entries.swap( _entries );
- locations.swap( _locations );
- _entries = entries;
- reset_iters();
- }
- do_load( filename );
- sort();
- remove_duplicates();
- trim_to_max_size();
- ofstream histFile( filename );
- if ( ! histFile ) {
- return ( false );
- }
- #ifndef _WIN32
- umask( old_umask );
- chmod( filename.c_str(), S_IRUSR | S_IWUSR );
- #endif
- Utf8String utf8;
- for ( Entry const& h : _entries ) {
- if ( ! h.text().is_empty() ) {
- utf8.assign( h.text() );
- histFile << "### " << h.timestamp() << "\n" << utf8.get() << endl;
- }
- }
- if ( ! sync_ ) {
- _entries = std::move( entries );
- _locations = std::move( locations );
- }
- reset_iters();
- return ( true );
- }
-
- namespace {
-
- bool is_timestamp( std::string const& s ) {
- static char const TIMESTAMP_PATTERN[] = "### dddd-dd-dd dd:dd:dd.ddd";
- static int const TIMESTAMP_LENGTH( sizeof ( TIMESTAMP_PATTERN ) - 1 );
- if ( s.length() != TIMESTAMP_LENGTH ) {
- return ( false );
- }
- for ( int i( 0 ); i < TIMESTAMP_LENGTH; ++ i ) {
- if ( TIMESTAMP_PATTERN[i] == 'd' ) {
- if ( ! isdigit( s[i] ) ) {
- return ( false );
- }
- } else if ( s[i] != TIMESTAMP_PATTERN[i] ) {
- return ( false );
- }
- }
- return ( true );
- }
-
- }
-
- bool History::do_load( std::string const& filename ) {
- ifstream histFile( filename );
- if ( ! histFile ) {
- return ( false );
- }
- string line;
- string when( "0000-00-00 00:00:00.000" );
- while ( getline( histFile, line ).good() ) {
- string::size_type eol( line.find_first_of( "\r\n" ) );
- if ( eol != string::npos ) {
- line.erase( eol );
- }
- if ( is_timestamp( line ) ) {
- when.assign( line, 4, std::string::npos );
- continue;
- }
- if ( ! line.empty() ) {
- _entries.emplace_back( when, UnicodeString( line ) );
- }
- }
- return ( true );
- }
-
- bool History::load( std::string const& filename ) {
- clear();
- bool success( do_load( filename ) );
- sort();
- remove_duplicates();
- trim_to_max_size();
- _previous = _current = last();
- _yankPos = _entries.end();
- return ( success );
- }
-
- void History::sort( void ) {
- typedef std::vector<Entry> sortable_entries_t;
- _locations.clear();
- sortable_entries_t sortableEntries( _entries.begin(), _entries.end() );
- std::stable_sort( sortableEntries.begin(), sortableEntries.end() );
- _entries.clear();
- _entries.insert( _entries.begin(), sortableEntries.begin(), sortableEntries.end() );
- }
-
- void History::clear( void ) {
- _locations.clear();
- _entries.clear();
- _current = _entries.begin();
- _recallMostRecent = false;
- }
-
- void History::set_max_size( int size_ ) {
- if ( size_ >= 0 ) {
- _maxSize = size_;
- trim_to_max_size();
- }
- }
-
- void History::reset_yank_iterator( void ) {
- _yankPos = _entries.end();
- }
-
- bool History::next_yank_position( void ) {
- bool resetYankSize( false );
- if ( _yankPos == _entries.end() ) {
- resetYankSize = true;
- }
- if ( ( _yankPos != _entries.begin() ) && ( _yankPos != _entries.end() ) ) {
- -- _yankPos;
- } else {
- _yankPos = moved( _entries.end(), -2 );
- }
- return ( resetYankSize );
- }
-
- bool History::move( bool up_ ) {
- bool doRecall( _recallMostRecent && ! up_ );
- if ( doRecall ) {
- _current = _previous; // emulate Windows down-arrow
- }
- _recallMostRecent = false;
- return ( doRecall || move( _current, up_ ? -1 : 1 ) );
- }
-
- void History::jump( bool start_, bool reset_ ) {
- if ( start_ ) {
- _current = _entries.begin();
- } else {
- _current = last();
- }
- if ( reset_ ) {
- _recallMostRecent = false;
- }
- }
-
- void History::save_pos( void ) {
- _previous = _current;
- }
-
- void History::restore_pos( void ) {
- _current = _previous;
- }
-
- bool History::common_prefix_search( UnicodeString const& prefix_, int prefixSize_, bool back_ ) {
- int step( back_ ? -1 : 1 );
- entries_t::const_iterator it( moved( _current, step, true ) );
- while ( it != _current ) {
- if ( it->text().starts_with( prefix_.begin(), prefix_.begin() + prefixSize_ ) ) {
- _current = it;
- commit_index();
- return ( true );
- }
- move( it, step, true );
- }
- return ( false );
- }
-
- bool History::move( entries_t::const_iterator& it_, int by_, bool wrapped_ ) const {
- if ( by_ > 0 ) {
- for ( int i( 0 ); i < by_; ++ i ) {
- ++ it_;
- if ( it_ != _entries.end() ) {
- } else if ( wrapped_ ) {
- it_ = _entries.begin();
- } else {
- -- it_;
- return ( false );
- }
- }
- } else {
- for ( int i( 0 ); i > by_; -- i ) {
- if ( it_ != _entries.begin() ) {
- -- it_;
- } else if ( wrapped_ ) {
- it_ = last();
- } else {
- return ( false );
- }
- }
- }
- return ( true );
- }
-
- History::entries_t::const_iterator History::moved( entries_t::const_iterator it_, int by_, bool wrapped_ ) const {
- move( it_, by_, wrapped_ );
- return ( it_ );
- }
-
- void History::erase( entries_t::const_iterator it_ ) {
- bool invalidated( it_ == _current );
- _locations.erase( it_->text() );
- it_ = _entries.erase( it_ );
- if ( invalidated ) {
- _current = it_;
- }
- if ( ( _current == _entries.end() ) && ! _entries.empty() ) {
- -- _current;
- }
- _yankPos = _entries.end();
- _previous = _current;
- }
-
- void History::trim_to_max_size( void ) {
- while ( size() > _maxSize ) {
- erase( _entries.begin() );
- }
- }
-
- void History::remove_duplicate( UnicodeString const& line_ ) {
- if ( ! _unique ) {
- return;
- }
- locations_t::iterator it( _locations.find( line_ ) );
- if ( it == _locations.end() ) {
- return;
- }
- erase( it->second );
- }
-
- void History::remove_duplicates( void ) {
- if ( ! _unique ) {
- return;
- }
- _locations.clear();
- typedef std::pair<locations_t::iterator, bool> locations_insertion_result_t;
- for ( entries_t::iterator it( _entries.begin() ), end( _entries.end() ); it != end; ++ it ) {
- locations_insertion_result_t locationsInsertionResult( _locations.insert( make_pair( it->text(), it ) ) );
- if ( ! locationsInsertionResult.second ) {
- _entries.erase( locationsInsertionResult.first->second );
- locationsInsertionResult.first->second = it;
- }
- }
- }
-
- void History::update_last( UnicodeString const& line_ ) {
- if ( _unique ) {
- _locations.erase( _entries.back().text() );
- remove_duplicate( line_ );
- _locations.insert( make_pair( line_, last() ) );
- }
- _entries.back() = Entry( now_ms_str(), line_ );
- }
-
- void History::drop_last( void ) {
- erase( last() );
- }
-
- bool History::is_last( void ) const {
- return ( _current == last() );
- }
-
- History::entries_t::const_iterator History::last( void ) const {
- return ( moved( _entries.end(), -1 ) );
- }
-
- void History::reset_iters( void ) {
- _previous = _current = last();
- _yankPos = _entries.end();
- }
-
- }
|