Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

history.cxx 9.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. #include <algorithm>
  2. #include <memory>
  3. #include <fstream>
  4. #include <cstring>
  5. #ifndef _WIN32
  6. #include <unistd.h>
  7. #include <fcntl.h>
  8. #include <sys/stat.h>
  9. #endif /* _WIN32 */
  10. #include "replxx.hxx"
  11. #include "history.hxx"
  12. using namespace std;
  13. namespace replxx {
  14. namespace {
  15. void delete_ReplxxHistoryScanImpl( Replxx::HistoryScanImpl* impl_ ) {
  16. delete impl_;
  17. }
  18. }
  19. static int const REPLXX_DEFAULT_HISTORY_MAX_LEN( 1000 );
  20. Replxx::HistoryScan::HistoryScan( impl_t impl_ )
  21. : _impl( std::move( impl_ ) ) {
  22. }
  23. bool Replxx::HistoryScan::next( void ) {
  24. return ( _impl->next() );
  25. }
  26. Replxx::HistoryScanImpl::HistoryScanImpl( History::entries_t const& entries_ )
  27. : _entries( entries_ )
  28. , _it( _entries.end() )
  29. , _utf8Cache()
  30. , _entryCache( std::string(), std::string() )
  31. , _cacheValid( false ) {
  32. }
  33. Replxx::HistoryEntry const& Replxx::HistoryScan::get( void ) const {
  34. return ( _impl->get() );
  35. }
  36. bool Replxx::HistoryScanImpl::next( void ) {
  37. if ( _it == _entries.end() ) {
  38. _it = _entries.begin();
  39. } else {
  40. ++ _it;
  41. }
  42. _cacheValid = false;
  43. return ( _it != _entries.end() );
  44. }
  45. Replxx::HistoryEntry const& Replxx::HistoryScanImpl::get( void ) const {
  46. if ( _cacheValid ) {
  47. return ( _entryCache );
  48. }
  49. _utf8Cache.assign( _it->text() );
  50. _entryCache = Replxx::HistoryEntry( _it->timestamp(), _utf8Cache.get() );
  51. _cacheValid = true;
  52. return ( _entryCache );
  53. }
  54. Replxx::HistoryScan::impl_t History::scan( void ) const {
  55. return ( Replxx::HistoryScan::impl_t( new Replxx::HistoryScanImpl( _entries ), delete_ReplxxHistoryScanImpl ) );
  56. }
  57. History::History( void )
  58. : _entries()
  59. , _maxSize( REPLXX_DEFAULT_HISTORY_MAX_LEN )
  60. , _current( _entries.begin() )
  61. , _yankPos( _entries.end() )
  62. , _previous( _entries.begin() )
  63. , _recallMostRecent( false )
  64. , _unique( true ) {
  65. }
  66. void History::add( UnicodeString const& line, std::string const& when ) {
  67. if ( _maxSize <= 0 ) {
  68. return;
  69. }
  70. if ( ! _entries.empty() && ( line == _entries.back().text() ) ) {
  71. _entries.back() = Entry( now_ms_str(), line );
  72. return;
  73. }
  74. remove_duplicate( line );
  75. trim_to_max_size();
  76. _entries.emplace_back( when, line );
  77. _locations.insert( make_pair( line, last() ) );
  78. if ( _current == _entries.end() ) {
  79. _current = last();
  80. }
  81. _yankPos = _entries.end();
  82. }
  83. #ifndef _WIN32
  84. class FileLock {
  85. std::string _path;
  86. int _lockFd;
  87. public:
  88. FileLock( std::string const& name_ )
  89. : _path( name_ + ".lock" )
  90. , _lockFd( ::open( _path.c_str(), O_CREAT | O_RDWR, 0600 ) ) {
  91. static_cast<void>( ::lockf( _lockFd, F_LOCK, 0 ) == 0 );
  92. }
  93. ~FileLock( void ) {
  94. static_cast<void>( ::lockf( _lockFd, F_ULOCK, 0 ) == 0 );
  95. ::close( _lockFd );
  96. ::unlink( _path.c_str() );
  97. return;
  98. }
  99. };
  100. #endif
  101. bool History::save( std::string const& filename, bool sync_ ) {
  102. #ifndef _WIN32
  103. mode_t old_umask = umask( S_IXUSR | S_IRWXG | S_IRWXO );
  104. FileLock fileLock( filename );
  105. #endif
  106. entries_t entries;
  107. locations_t locations;
  108. if ( ! sync_ ) {
  109. entries.swap( _entries );
  110. locations.swap( _locations );
  111. _entries = entries;
  112. reset_iters();
  113. }
  114. do_load( filename );
  115. sort();
  116. remove_duplicates();
  117. trim_to_max_size();
  118. ofstream histFile( filename );
  119. if ( ! histFile ) {
  120. return ( false );
  121. }
  122. #ifndef _WIN32
  123. umask( old_umask );
  124. chmod( filename.c_str(), S_IRUSR | S_IWUSR );
  125. #endif
  126. Utf8String utf8;
  127. for ( Entry const& h : _entries ) {
  128. if ( ! h.text().is_empty() ) {
  129. utf8.assign( h.text() );
  130. histFile << "### " << h.timestamp() << "\n" << utf8.get() << endl;
  131. }
  132. }
  133. if ( ! sync_ ) {
  134. _entries = std::move( entries );
  135. _locations = std::move( locations );
  136. }
  137. reset_iters();
  138. return ( true );
  139. }
  140. namespace {
  141. bool is_timestamp( std::string const& s ) {
  142. static char const TIMESTAMP_PATTERN[] = "### dddd-dd-dd dd:dd:dd.ddd";
  143. static int const TIMESTAMP_LENGTH( sizeof ( TIMESTAMP_PATTERN ) - 1 );
  144. if ( s.length() != TIMESTAMP_LENGTH ) {
  145. return ( false );
  146. }
  147. for ( int i( 0 ); i < TIMESTAMP_LENGTH; ++ i ) {
  148. if ( TIMESTAMP_PATTERN[i] == 'd' ) {
  149. if ( ! isdigit( s[i] ) ) {
  150. return ( false );
  151. }
  152. } else if ( s[i] != TIMESTAMP_PATTERN[i] ) {
  153. return ( false );
  154. }
  155. }
  156. return ( true );
  157. }
  158. }
  159. bool History::do_load( std::string const& filename ) {
  160. ifstream histFile( filename );
  161. if ( ! histFile ) {
  162. return ( false );
  163. }
  164. string line;
  165. string when( "0000-00-00 00:00:00.000" );
  166. while ( getline( histFile, line ).good() ) {
  167. string::size_type eol( line.find_first_of( "\r\n" ) );
  168. if ( eol != string::npos ) {
  169. line.erase( eol );
  170. }
  171. if ( is_timestamp( line ) ) {
  172. when.assign( line, 4, std::string::npos );
  173. continue;
  174. }
  175. if ( ! line.empty() ) {
  176. _entries.emplace_back( when, UnicodeString( line ) );
  177. }
  178. }
  179. return ( true );
  180. }
  181. bool History::load( std::string const& filename ) {
  182. clear();
  183. bool success( do_load( filename ) );
  184. sort();
  185. remove_duplicates();
  186. trim_to_max_size();
  187. _previous = _current = last();
  188. _yankPos = _entries.end();
  189. return ( success );
  190. }
  191. void History::sort( void ) {
  192. typedef std::vector<Entry> sortable_entries_t;
  193. _locations.clear();
  194. sortable_entries_t sortableEntries( _entries.begin(), _entries.end() );
  195. std::stable_sort( sortableEntries.begin(), sortableEntries.end() );
  196. _entries.clear();
  197. _entries.insert( _entries.begin(), sortableEntries.begin(), sortableEntries.end() );
  198. }
  199. void History::clear( void ) {
  200. _locations.clear();
  201. _entries.clear();
  202. _current = _entries.begin();
  203. _recallMostRecent = false;
  204. }
  205. void History::set_max_size( int size_ ) {
  206. if ( size_ >= 0 ) {
  207. _maxSize = size_;
  208. trim_to_max_size();
  209. }
  210. }
  211. void History::reset_yank_iterator( void ) {
  212. _yankPos = _entries.end();
  213. }
  214. bool History::next_yank_position( void ) {
  215. bool resetYankSize( false );
  216. if ( _yankPos == _entries.end() ) {
  217. resetYankSize = true;
  218. }
  219. if ( ( _yankPos != _entries.begin() ) && ( _yankPos != _entries.end() ) ) {
  220. -- _yankPos;
  221. } else {
  222. _yankPos = moved( _entries.end(), -2 );
  223. }
  224. return ( resetYankSize );
  225. }
  226. bool History::move( bool up_ ) {
  227. bool doRecall( _recallMostRecent && ! up_ );
  228. if ( doRecall ) {
  229. _current = _previous; // emulate Windows down-arrow
  230. }
  231. _recallMostRecent = false;
  232. return ( doRecall || move( _current, up_ ? -1 : 1 ) );
  233. }
  234. void History::jump( bool start_, bool reset_ ) {
  235. if ( start_ ) {
  236. _current = _entries.begin();
  237. } else {
  238. _current = last();
  239. }
  240. if ( reset_ ) {
  241. _recallMostRecent = false;
  242. }
  243. }
  244. void History::save_pos( void ) {
  245. _previous = _current;
  246. }
  247. void History::restore_pos( void ) {
  248. _current = _previous;
  249. }
  250. bool History::common_prefix_search( UnicodeString const& prefix_, int prefixSize_, bool back_ ) {
  251. int step( back_ ? -1 : 1 );
  252. entries_t::const_iterator it( moved( _current, step, true ) );
  253. while ( it != _current ) {
  254. if ( it->text().starts_with( prefix_.begin(), prefix_.begin() + prefixSize_ ) ) {
  255. _current = it;
  256. commit_index();
  257. return ( true );
  258. }
  259. move( it, step, true );
  260. }
  261. return ( false );
  262. }
  263. bool History::move( entries_t::const_iterator& it_, int by_, bool wrapped_ ) const {
  264. if ( by_ > 0 ) {
  265. for ( int i( 0 ); i < by_; ++ i ) {
  266. ++ it_;
  267. if ( it_ != _entries.end() ) {
  268. } else if ( wrapped_ ) {
  269. it_ = _entries.begin();
  270. } else {
  271. -- it_;
  272. return ( false );
  273. }
  274. }
  275. } else {
  276. for ( int i( 0 ); i > by_; -- i ) {
  277. if ( it_ != _entries.begin() ) {
  278. -- it_;
  279. } else if ( wrapped_ ) {
  280. it_ = last();
  281. } else {
  282. return ( false );
  283. }
  284. }
  285. }
  286. return ( true );
  287. }
  288. History::entries_t::const_iterator History::moved( entries_t::const_iterator it_, int by_, bool wrapped_ ) const {
  289. move( it_, by_, wrapped_ );
  290. return ( it_ );
  291. }
  292. void History::erase( entries_t::const_iterator it_ ) {
  293. bool invalidated( it_ == _current );
  294. _locations.erase( it_->text() );
  295. it_ = _entries.erase( it_ );
  296. if ( invalidated ) {
  297. _current = it_;
  298. }
  299. if ( ( _current == _entries.end() ) && ! _entries.empty() ) {
  300. -- _current;
  301. }
  302. _yankPos = _entries.end();
  303. _previous = _current;
  304. }
  305. void History::trim_to_max_size( void ) {
  306. while ( size() > _maxSize ) {
  307. erase( _entries.begin() );
  308. }
  309. }
  310. void History::remove_duplicate( UnicodeString const& line_ ) {
  311. if ( ! _unique ) {
  312. return;
  313. }
  314. locations_t::iterator it( _locations.find( line_ ) );
  315. if ( it == _locations.end() ) {
  316. return;
  317. }
  318. erase( it->second );
  319. }
  320. void History::remove_duplicates( void ) {
  321. if ( ! _unique ) {
  322. return;
  323. }
  324. _locations.clear();
  325. typedef std::pair<locations_t::iterator, bool> locations_insertion_result_t;
  326. for ( entries_t::iterator it( _entries.begin() ), end( _entries.end() ); it != end; ++ it ) {
  327. locations_insertion_result_t locationsInsertionResult( _locations.insert( make_pair( it->text(), it ) ) );
  328. if ( ! locationsInsertionResult.second ) {
  329. _entries.erase( locationsInsertionResult.first->second );
  330. locationsInsertionResult.first->second = it;
  331. }
  332. }
  333. }
  334. void History::update_last( UnicodeString const& line_ ) {
  335. if ( _unique ) {
  336. _locations.erase( _entries.back().text() );
  337. remove_duplicate( line_ );
  338. _locations.insert( make_pair( line_, last() ) );
  339. }
  340. _entries.back() = Entry( now_ms_str(), line_ );
  341. }
  342. void History::drop_last( void ) {
  343. erase( last() );
  344. }
  345. bool History::is_last( void ) const {
  346. return ( _current == last() );
  347. }
  348. History::entries_t::const_iterator History::last( void ) const {
  349. return ( moved( _entries.end(), -1 ) );
  350. }
  351. void History::reset_iters( void ) {
  352. _previous = _current = last();
  353. _yankPos = _entries.end();
  354. }
  355. }