xref: /glogg/src/data/logdata.cpp (revision e191a6372ce0efc66787e543b1949479c1e79a59)
1bb02e0acSNicolas Bonnefon /*
2653377b6SNicolas Bonnefon  * Copyright (C) 2009, 2010, 2013, 2014, 2015 Nicolas Bonnefon and other contributors
3bb02e0acSNicolas Bonnefon  *
4bb02e0acSNicolas Bonnefon  * This file is part of glogg.
5bb02e0acSNicolas Bonnefon  *
6bb02e0acSNicolas Bonnefon  * glogg is free software: you can redistribute it and/or modify
7bb02e0acSNicolas Bonnefon  * it under the terms of the GNU General Public License as published by
8bb02e0acSNicolas Bonnefon  * the Free Software Foundation, either version 3 of the License, or
9bb02e0acSNicolas Bonnefon  * (at your option) any later version.
10bb02e0acSNicolas Bonnefon  *
11bb02e0acSNicolas Bonnefon  * glogg is distributed in the hope that it will be useful,
12bb02e0acSNicolas Bonnefon  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13bb02e0acSNicolas Bonnefon  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14bb02e0acSNicolas Bonnefon  * GNU General Public License for more details.
15bb02e0acSNicolas Bonnefon  *
16bb02e0acSNicolas Bonnefon  * You should have received a copy of the GNU General Public License
17bb02e0acSNicolas Bonnefon  * along with glogg.  If not, see <http://www.gnu.org/licenses/>.
18bb02e0acSNicolas Bonnefon  */
19bb02e0acSNicolas Bonnefon 
20bb02e0acSNicolas Bonnefon // This file implements LogData, the content of a log file.
21bb02e0acSNicolas Bonnefon 
22bb02e0acSNicolas Bonnefon #include <iostream>
23bb02e0acSNicolas Bonnefon 
24306d9ac9SNicolas Bonnefon #include <cassert>
25306d9ac9SNicolas Bonnefon 
26bb02e0acSNicolas Bonnefon #include <QFileInfo>
27bb02e0acSNicolas Bonnefon 
28bb02e0acSNicolas Bonnefon #include "log.h"
29bb02e0acSNicolas Bonnefon 
30bb02e0acSNicolas Bonnefon #include "logdata.h"
31bb02e0acSNicolas Bonnefon #include "logfiltereddata.h"
32f869e41dSNicolas Bonnefon #if defined(GLOGG_SUPPORTS_INOTIFY) || defined(GLOGG_SUPPORTS_KQUEUE) || defined(WIN32)
3384b2179eSNicolas Bonnefon #include "platformfilewatcher.h"
346a12446eSNicolas Bonnefon #else
356a12446eSNicolas Bonnefon #include "qtfilewatcher.h"
366a12446eSNicolas Bonnefon #endif
37bb02e0acSNicolas Bonnefon 
38bb02e0acSNicolas Bonnefon // Implementation of the 'start' functions for each operation
39bb02e0acSNicolas Bonnefon 
doStart(LogDataWorkerThread & workerThread) const40bb02e0acSNicolas Bonnefon void LogData::AttachOperation::doStart(
41bb02e0acSNicolas Bonnefon         LogDataWorkerThread& workerThread ) const
42bb02e0acSNicolas Bonnefon {
43bb02e0acSNicolas Bonnefon     LOG(logDEBUG) << "Attaching " << filename_.toStdString();
44bb02e0acSNicolas Bonnefon     workerThread.attachFile( filename_ );
45bb02e0acSNicolas Bonnefon     workerThread.indexAll();
46bb02e0acSNicolas Bonnefon }
47bb02e0acSNicolas Bonnefon 
doStart(LogDataWorkerThread & workerThread) const48bb02e0acSNicolas Bonnefon void LogData::FullIndexOperation::doStart(
49bb02e0acSNicolas Bonnefon         LogDataWorkerThread& workerThread ) const
50bb02e0acSNicolas Bonnefon {
51bb02e0acSNicolas Bonnefon     LOG(logDEBUG) << "Reindexing (full)";
52bb02e0acSNicolas Bonnefon     workerThread.indexAll();
53bb02e0acSNicolas Bonnefon }
54bb02e0acSNicolas Bonnefon 
doStart(LogDataWorkerThread & workerThread) const55bb02e0acSNicolas Bonnefon void LogData::PartialIndexOperation::doStart(
56bb02e0acSNicolas Bonnefon         LogDataWorkerThread& workerThread ) const
57bb02e0acSNicolas Bonnefon {
58bb02e0acSNicolas Bonnefon     LOG(logDEBUG) << "Reindexing (partial)";
59de1abac6SNicolas Bonnefon     workerThread.indexAdditionalLines();
60bb02e0acSNicolas Bonnefon }
61bb02e0acSNicolas Bonnefon 
62bb02e0acSNicolas Bonnefon 
63bb02e0acSNicolas Bonnefon // Constructs an empty log file.
64bb02e0acSNicolas Bonnefon // It must be displayed without error.
LogData()65653377b6SNicolas Bonnefon LogData::LogData() : AbstractLogData(), indexing_data_(),
66653377b6SNicolas Bonnefon     fileMutex_(), workerThread_( &indexing_data_ )
67bb02e0acSNicolas Bonnefon {
68bb02e0acSNicolas Bonnefon     // Start with an "empty" log
691ee847caSNicolas Bonnefon     attached_file_ = nullptr;
70306d9ac9SNicolas Bonnefon     currentOperation_ = nullptr;
71306d9ac9SNicolas Bonnefon     nextOperation_    = nullptr;
72bb02e0acSNicolas Bonnefon 
735fa25391SNicolas Bonnefon     codec_ = QTextCodec::codecForName( "ISO-8859-1" );
745fa25391SNicolas Bonnefon 
75f869e41dSNicolas Bonnefon #if defined(GLOGG_SUPPORTS_INOTIFY) || defined(GLOGG_SUPPORTS_KQUEUE) || defined(WIN32)
7684b2179eSNicolas Bonnefon     fileWatcher_ = std::make_shared<PlatformFileWatcher>();
776a12446eSNicolas Bonnefon #else
786a12446eSNicolas Bonnefon     fileWatcher_ = std::make_shared<QtFileWatcher>();
796a12446eSNicolas Bonnefon #endif
806a12446eSNicolas Bonnefon 
81bb02e0acSNicolas Bonnefon     // Initialise the file watcher
826a12446eSNicolas Bonnefon     connect( fileWatcher_.get(), SIGNAL( fileChanged( const QString& ) ),
83bb02e0acSNicolas Bonnefon             this, SLOT( fileChangedOnDisk() ) );
84bb02e0acSNicolas Bonnefon     // Forward the update signal
85bb02e0acSNicolas Bonnefon     connect( &workerThread_, SIGNAL( indexingProgressed( int ) ),
86bb02e0acSNicolas Bonnefon             this, SIGNAL( loadingProgressed( int ) ) );
87812146a8SNicolas Bonnefon     connect( &workerThread_, SIGNAL( indexingFinished( LoadingStatus ) ),
88812146a8SNicolas Bonnefon             this, SLOT( indexingFinished( LoadingStatus ) ) );
89bb02e0acSNicolas Bonnefon 
90bb02e0acSNicolas Bonnefon     // Starts the worker thread
91bb02e0acSNicolas Bonnefon     workerThread_.start();
92bb02e0acSNicolas Bonnefon }
93bb02e0acSNicolas Bonnefon 
~LogData()94bb02e0acSNicolas Bonnefon LogData::~LogData()
95bb02e0acSNicolas Bonnefon {
961ee847caSNicolas Bonnefon     // Remove the current file from the watch list
971ee847caSNicolas Bonnefon     if ( attached_file_ )
981ee847caSNicolas Bonnefon         fileWatcher_->removeFile( attached_file_->fileName() );
991ee847caSNicolas Bonnefon 
100ef13a493SNicolas Bonnefon     // FIXME
101ef13a493SNicolas Bonnefon     // workerThread_.stop();
102bb02e0acSNicolas Bonnefon }
103bb02e0acSNicolas Bonnefon 
104bb02e0acSNicolas Bonnefon //
105bb02e0acSNicolas Bonnefon // Public functions
106bb02e0acSNicolas Bonnefon //
107bb02e0acSNicolas Bonnefon 
attachFile(const QString & fileName)108bb02e0acSNicolas Bonnefon void LogData::attachFile( const QString& fileName )
109bb02e0acSNicolas Bonnefon {
110bb02e0acSNicolas Bonnefon     LOG(logDEBUG) << "LogData::attachFile " << fileName.toStdString();
111bb02e0acSNicolas Bonnefon 
1121ee847caSNicolas Bonnefon     if ( attached_file_ ) {
1131ee847caSNicolas Bonnefon         // We cannot reattach
1141ee847caSNicolas Bonnefon         throw CantReattachErr();
115bb02e0acSNicolas Bonnefon     }
116bb02e0acSNicolas Bonnefon 
11790684c57SNicolas Bonnefon     attached_file_.reset( new QFile( fileName ) );
11890684c57SNicolas Bonnefon     attached_file_->open( QIODevice::ReadOnly );
119bb02e0acSNicolas Bonnefon 
120cdc631c3SNicolas Bonnefon     std::shared_ptr<const LogDataOperation> operation( new AttachOperation( fileName ) );
121cdc631c3SNicolas Bonnefon     enqueueOperation( std::move( operation ) );
122bb02e0acSNicolas Bonnefon }
123bb02e0acSNicolas Bonnefon 
interruptLoading()124bb02e0acSNicolas Bonnefon void LogData::interruptLoading()
125bb02e0acSNicolas Bonnefon {
126bb02e0acSNicolas Bonnefon     workerThread_.interrupt();
127bb02e0acSNicolas Bonnefon }
128bb02e0acSNicolas Bonnefon 
getFileSize() const129bb02e0acSNicolas Bonnefon qint64 LogData::getFileSize() const
130bb02e0acSNicolas Bonnefon {
131653377b6SNicolas Bonnefon     return indexing_data_.getSize();
132bb02e0acSNicolas Bonnefon }
133bb02e0acSNicolas Bonnefon 
getLastModifiedDate() const134bb02e0acSNicolas Bonnefon QDateTime LogData::getLastModifiedDate() const
135bb02e0acSNicolas Bonnefon {
136bb02e0acSNicolas Bonnefon     return lastModifiedDate_;
137bb02e0acSNicolas Bonnefon }
138bb02e0acSNicolas Bonnefon 
139bb02e0acSNicolas Bonnefon // Return an initialised LogFilteredData. The search is not started.
getNewFilteredData() const140bb02e0acSNicolas Bonnefon LogFilteredData* LogData::getNewFilteredData() const
141bb02e0acSNicolas Bonnefon {
142bb02e0acSNicolas Bonnefon     LogFilteredData* newFilteredData = new LogFilteredData( this );
143bb02e0acSNicolas Bonnefon 
144bb02e0acSNicolas Bonnefon     return newFilteredData;
145bb02e0acSNicolas Bonnefon }
146bb02e0acSNicolas Bonnefon 
reload()14732e44cfdSNicolas Bonnefon void LogData::reload()
14832e44cfdSNicolas Bonnefon {
14932e44cfdSNicolas Bonnefon     workerThread_.interrupt();
15032e44cfdSNicolas Bonnefon 
151*84a8a910SNicolas Bonnefon     // Re-open the file, useful in case the file has been moved
152*84a8a910SNicolas Bonnefon     reOpenFile();
153*84a8a910SNicolas Bonnefon 
15432e44cfdSNicolas Bonnefon     enqueueOperation( std::make_shared<FullIndexOperation>() );
15532e44cfdSNicolas Bonnefon }
15632e44cfdSNicolas Bonnefon 
setPollingInterval(uint32_t interval_ms)15780bca0a3SNicolas Bonnefon void LogData::setPollingInterval( uint32_t interval_ms )
15880bca0a3SNicolas Bonnefon {
15980bca0a3SNicolas Bonnefon     fileWatcher_->setPollingInterval( interval_ms );
16080bca0a3SNicolas Bonnefon }
16180bca0a3SNicolas Bonnefon 
16232e44cfdSNicolas Bonnefon //
16332e44cfdSNicolas Bonnefon // Private functions
16432e44cfdSNicolas Bonnefon //
16532e44cfdSNicolas Bonnefon 
166bb02e0acSNicolas Bonnefon // Add an operation to the queue and perform it immediately if
167bb02e0acSNicolas Bonnefon // there is none ongoing.
enqueueOperation(std::shared_ptr<const LogDataOperation> new_operation)168306d9ac9SNicolas Bonnefon void LogData::enqueueOperation( std::shared_ptr<const LogDataOperation> new_operation )
169bb02e0acSNicolas Bonnefon {
170306d9ac9SNicolas Bonnefon     if ( currentOperation_ == nullptr )
171bb02e0acSNicolas Bonnefon     {
172bb02e0acSNicolas Bonnefon         // We do it immediately
173306d9ac9SNicolas Bonnefon         currentOperation_ =  new_operation;
174bb02e0acSNicolas Bonnefon         startOperation();
175bb02e0acSNicolas Bonnefon     }
176bb02e0acSNicolas Bonnefon     else
177bb02e0acSNicolas Bonnefon     {
178bb02e0acSNicolas Bonnefon         // An operation is in progress...
179bb02e0acSNicolas Bonnefon         // ... we schedule the attach op for later
180306d9ac9SNicolas Bonnefon         nextOperation_ = new_operation;
181bb02e0acSNicolas Bonnefon     }
182bb02e0acSNicolas Bonnefon }
183bb02e0acSNicolas Bonnefon 
184bb02e0acSNicolas Bonnefon // Performs the current operation asynchronously, a indexingFinished
185bb02e0acSNicolas Bonnefon // signal will be received when it's finished.
startOperation()186bb02e0acSNicolas Bonnefon void LogData::startOperation()
187bb02e0acSNicolas Bonnefon {
188bb02e0acSNicolas Bonnefon     if ( currentOperation_ )
189bb02e0acSNicolas Bonnefon     {
190bb02e0acSNicolas Bonnefon         LOG(logDEBUG) << "startOperation found something to do.";
191bb02e0acSNicolas Bonnefon 
192653377b6SNicolas Bonnefon         // Let the operation do its stuff
193bb02e0acSNicolas Bonnefon         currentOperation_->start( workerThread_ );
194bb02e0acSNicolas Bonnefon     }
195bb02e0acSNicolas Bonnefon }
196bb02e0acSNicolas Bonnefon 
197bb02e0acSNicolas Bonnefon //
198bb02e0acSNicolas Bonnefon // Slots
199bb02e0acSNicolas Bonnefon //
200bb02e0acSNicolas Bonnefon 
fileChangedOnDisk()201bb02e0acSNicolas Bonnefon void LogData::fileChangedOnDisk()
202bb02e0acSNicolas Bonnefon {
2031ee847caSNicolas Bonnefon     const QString name = attached_file_->fileName();
204bb02e0acSNicolas Bonnefon 
2059f850936SNicolas Bonnefon     LOG(logDEBUG) << "signalFileChanged: " << name.toStdString();
2069f850936SNicolas Bonnefon 
2079f850936SNicolas Bonnefon     QFileInfo info( name );
2089f850936SNicolas Bonnefon     qint64 file_size = indexing_data_.getSize();
2099f850936SNicolas Bonnefon     LOG(logDEBUG) << "current indexed fileSize=" << file_size;
2109f850936SNicolas Bonnefon     LOG(logDEBUG) << "info file_->size()=" << info.size();
2119f850936SNicolas Bonnefon     LOG(logDEBUG) << "attached_file_->size()=" << attached_file_->size();
2129f850936SNicolas Bonnefon     // In absence of any clearer information, we use the following size comparison
2139f850936SNicolas Bonnefon     // to determine whether we are following the same file or not (i.e. the file
2149f850936SNicolas Bonnefon     // has been moved and the inode we are following is now under a new name, if for
2159f850936SNicolas Bonnefon     // instance log has been rotated). We want to follow the name so we have to reopen
2169f850936SNicolas Bonnefon     // the file to ensure we are reading the right one.
2179f850936SNicolas Bonnefon     // This is a crude heuristic but necessary for notification services that do not
2189f850936SNicolas Bonnefon     // give details (e.g. kqueues)
2199f850936SNicolas Bonnefon     if ( ( info.size() != attached_file_->size() )
2209f850936SNicolas Bonnefon             || ( attached_file_->openMode() == QIODevice::NotOpen ) ) {
2219f850936SNicolas Bonnefon         LOG(logINFO) << "Inconsistent size, the file might have changed, re-opening";
222*84a8a910SNicolas Bonnefon         reOpenFile();
2239f850936SNicolas Bonnefon 
2249f850936SNicolas Bonnefon         // We don't force a (slow) full reindex as this routinely happens if
2259f850936SNicolas Bonnefon         // the file is appended quickly.
2269f850936SNicolas Bonnefon         // This means we can occasionally have false negatives (should be dealt with at
2279f850936SNicolas Bonnefon         // a lower level): e.g. if a new file is created with the same name as the old one
2289f850936SNicolas Bonnefon         // and with a size greater than the old one (should be rare in practice).
2299f850936SNicolas Bonnefon     }
2302686d8e5SNicolas Bonnefon 
231306d9ac9SNicolas Bonnefon     std::shared_ptr<LogDataOperation> newOperation;
232bb02e0acSNicolas Bonnefon 
2339f850936SNicolas Bonnefon     qint64 real_file_size = attached_file_->size();
2349f850936SNicolas Bonnefon     if ( real_file_size < file_size ) {
235bb02e0acSNicolas Bonnefon         fileChangedOnDisk_ = Truncated;
236bb02e0acSNicolas Bonnefon         LOG(logINFO) << "File truncated";
237306d9ac9SNicolas Bonnefon         newOperation = std::make_shared<FullIndexOperation>();
238bb02e0acSNicolas Bonnefon     }
2399f850936SNicolas Bonnefon     else if ( real_file_size == file_size ) {
240c1f737e4SNicolas Bonnefon         LOG(logINFO) << "No change in file";
241c1f737e4SNicolas Bonnefon     }
242bb02e0acSNicolas Bonnefon     else if ( fileChangedOnDisk_ != DataAdded ) {
243bb02e0acSNicolas Bonnefon         fileChangedOnDisk_ = DataAdded;
244bb02e0acSNicolas Bonnefon         LOG(logINFO) << "New data on disk";
245de1abac6SNicolas Bonnefon         newOperation = std::make_shared<PartialIndexOperation>();
246bb02e0acSNicolas Bonnefon     }
247bb02e0acSNicolas Bonnefon 
248c1f737e4SNicolas Bonnefon     if ( newOperation ) {
249bb02e0acSNicolas Bonnefon         enqueueOperation( newOperation );
250bb02e0acSNicolas Bonnefon         lastModifiedDate_ = info.lastModified();
251bb02e0acSNicolas Bonnefon 
252bb02e0acSNicolas Bonnefon         emit fileChanged( fileChangedOnDisk_ );
253c1f737e4SNicolas Bonnefon     }
254bb02e0acSNicolas Bonnefon }
255bb02e0acSNicolas Bonnefon 
indexingFinished(LoadingStatus status)256812146a8SNicolas Bonnefon void LogData::indexingFinished( LoadingStatus status )
257bb02e0acSNicolas Bonnefon {
258812146a8SNicolas Bonnefon     LOG(logDEBUG) << "indexingFinished: " <<
259812146a8SNicolas Bonnefon         ( status == LoadingStatus::Successful ) <<
260653377b6SNicolas Bonnefon         ", found " << indexing_data_.getNbLines() << " lines.";
261bb02e0acSNicolas Bonnefon 
262812146a8SNicolas Bonnefon     if ( status == LoadingStatus::Successful ) {
26390684c57SNicolas Bonnefon         // Start watching we watch the file for updates
2641ee847caSNicolas Bonnefon         fileChangedOnDisk_ = Unchanged;
2651ee847caSNicolas Bonnefon         fileWatcher_->addFile( attached_file_->fileName() );
266bb02e0acSNicolas Bonnefon 
267bb02e0acSNicolas Bonnefon         // Update the modified date/time if the file exists
268bb02e0acSNicolas Bonnefon         lastModifiedDate_ = QDateTime();
2691ee847caSNicolas Bonnefon         QFileInfo fileInfo( *attached_file_ );
270bb02e0acSNicolas Bonnefon         if ( fileInfo.exists() )
271bb02e0acSNicolas Bonnefon             lastModifiedDate_ = fileInfo.lastModified();
272bb02e0acSNicolas Bonnefon     }
273bb02e0acSNicolas Bonnefon 
2741ee847caSNicolas Bonnefon     // FIXME be cleverer here as a notification might have arrived whilst we
2751ee847caSNicolas Bonnefon     // were indexing.
276bb02e0acSNicolas Bonnefon     fileChangedOnDisk_ = Unchanged;
277bb02e0acSNicolas Bonnefon 
2781ee847caSNicolas Bonnefon     LOG(logDEBUG) << "Sending indexingFinished.";
279812146a8SNicolas Bonnefon     emit loadingFinished( status );
280bb02e0acSNicolas Bonnefon 
281bb02e0acSNicolas Bonnefon     // So now the operation is done, let's see if there is something
282bb02e0acSNicolas Bonnefon     // else to do, in which case, do it!
283306d9ac9SNicolas Bonnefon     assert( currentOperation_ );
284306d9ac9SNicolas Bonnefon 
285f8bd90d8SNicolas Bonnefon     currentOperation_ = std::move( nextOperation_ );
286f8bd90d8SNicolas Bonnefon     nextOperation_.reset();
287bb02e0acSNicolas Bonnefon 
288bb02e0acSNicolas Bonnefon     if ( currentOperation_ ) {
289bb02e0acSNicolas Bonnefon         LOG(logDEBUG) << "indexingFinished is performing the next operation";
290bb02e0acSNicolas Bonnefon         startOperation();
291bb02e0acSNicolas Bonnefon     }
292bb02e0acSNicolas Bonnefon }
293bb02e0acSNicolas Bonnefon 
294bb02e0acSNicolas Bonnefon //
295bb02e0acSNicolas Bonnefon // Implementation of virtual functions
296bb02e0acSNicolas Bonnefon //
doGetNbLine() const297bb02e0acSNicolas Bonnefon qint64 LogData::doGetNbLine() const
298bb02e0acSNicolas Bonnefon {
299653377b6SNicolas Bonnefon     return indexing_data_.getNbLines();
300bb02e0acSNicolas Bonnefon }
301bb02e0acSNicolas Bonnefon 
doGetMaxLength() const302bb02e0acSNicolas Bonnefon int LogData::doGetMaxLength() const
303bb02e0acSNicolas Bonnefon {
304653377b6SNicolas Bonnefon     return indexing_data_.getMaxLength();
305bb02e0acSNicolas Bonnefon }
306bb02e0acSNicolas Bonnefon 
doGetLineLength(qint64 line) const307bb02e0acSNicolas Bonnefon int LogData::doGetLineLength( qint64 line ) const
308bb02e0acSNicolas Bonnefon {
309653377b6SNicolas Bonnefon     if ( line >= indexing_data_.getNbLines() ) { return 0; /* exception? */ }
310bb02e0acSNicolas Bonnefon 
311bb02e0acSNicolas Bonnefon     int length = doGetExpandedLineString( line ).length();
312bb02e0acSNicolas Bonnefon 
313bb02e0acSNicolas Bonnefon     return length;
314bb02e0acSNicolas Bonnefon }
315bb02e0acSNicolas Bonnefon 
doSetDisplayEncoding(Encoding encoding)316209000a6SNicolas Bonnefon void LogData::doSetDisplayEncoding( Encoding encoding )
3175fa25391SNicolas Bonnefon {
318209000a6SNicolas Bonnefon     LOG(logDEBUG) << "AbstractLogData::setDisplayEncoding: " << static_cast<int>( encoding );
319209000a6SNicolas Bonnefon 
320209000a6SNicolas Bonnefon     static const char* latin1_encoding = "iso-8859-1";
321209000a6SNicolas Bonnefon     static const char* utf8_encoding   = "utf-8";
322209000a6SNicolas Bonnefon     static const char* utf16le_encoding   = "utf-16le";
323209000a6SNicolas Bonnefon     static const char* utf16be_encoding   = "utf-16be";
324209000a6SNicolas Bonnefon     static const char* cp1251_encoding   = "CP1251";
325209000a6SNicolas Bonnefon     static const char* cp1252_encoding   = "CP1252";
326048334c9SSeerauber     static const char* big5_encoding  = "Big5";
327048334c9SSeerauber     static const char* gb18030_encoding  = "GB18030";
328048334c9SSeerauber     static const char* shiftJIS_encoding  = "Shift-JIS";
329048334c9SSeerauber     static const char* koi8r_encoding  = "KOI8-R";
330209000a6SNicolas Bonnefon 
331209000a6SNicolas Bonnefon     const char* qt_encoding = latin1_encoding;
332209000a6SNicolas Bonnefon 
333209000a6SNicolas Bonnefon     // Default to 0, for 8bit encodings
334209000a6SNicolas Bonnefon     int before_cr = 0;
335209000a6SNicolas Bonnefon     int after_cr  = 0;
336209000a6SNicolas Bonnefon 
337209000a6SNicolas Bonnefon     switch ( encoding ) {
338209000a6SNicolas Bonnefon         case Encoding::ENCODING_UTF8:
339209000a6SNicolas Bonnefon             qt_encoding = utf8_encoding;
340209000a6SNicolas Bonnefon             break;
341209000a6SNicolas Bonnefon         case Encoding::ENCODING_UTF16LE:
342209000a6SNicolas Bonnefon             qt_encoding = utf16le_encoding;
343209000a6SNicolas Bonnefon             before_cr = 0;
344209000a6SNicolas Bonnefon             after_cr  = 1;
345209000a6SNicolas Bonnefon             break;
346209000a6SNicolas Bonnefon         case Encoding::ENCODING_UTF16BE:
347209000a6SNicolas Bonnefon             qt_encoding = utf16be_encoding;
348209000a6SNicolas Bonnefon             before_cr = 1;
349209000a6SNicolas Bonnefon             after_cr  = 0;
350209000a6SNicolas Bonnefon             break;
351209000a6SNicolas Bonnefon         case Encoding::ENCODING_CP1251:
352209000a6SNicolas Bonnefon             qt_encoding = cp1251_encoding;
353209000a6SNicolas Bonnefon             break;
354209000a6SNicolas Bonnefon         case Encoding::ENCODING_CP1252:
355209000a6SNicolas Bonnefon             qt_encoding = cp1252_encoding;
356209000a6SNicolas Bonnefon             break;
357048334c9SSeerauber         case Encoding::ENCODING_BIG5:
358048334c9SSeerauber             qt_encoding = big5_encoding;
359048334c9SSeerauber             break;
360048334c9SSeerauber         case Encoding::ENCODING_GB18030:
361048334c9SSeerauber             qt_encoding = gb18030_encoding;
362048334c9SSeerauber             break;
363048334c9SSeerauber         case Encoding::ENCODING_SHIFT_JIS:
364048334c9SSeerauber             qt_encoding = shiftJIS_encoding;
365048334c9SSeerauber             break;
366048334c9SSeerauber         case Encoding::ENCODING_KOI8R:
367048334c9SSeerauber             qt_encoding = koi8r_encoding;
368048334c9SSeerauber             break;
369209000a6SNicolas Bonnefon         case Encoding::ENCODING_ISO_8859_1:
370209000a6SNicolas Bonnefon             qt_encoding = latin1_encoding;
371209000a6SNicolas Bonnefon             break;
372209000a6SNicolas Bonnefon         default:
373209000a6SNicolas Bonnefon             LOG( logERROR ) << "Unknown encoding set!";
374209000a6SNicolas Bonnefon             assert( false );
375209000a6SNicolas Bonnefon             break;
376209000a6SNicolas Bonnefon     }
377209000a6SNicolas Bonnefon 
378209000a6SNicolas Bonnefon     doSetMultibyteEncodingOffsets( before_cr, after_cr );
379209000a6SNicolas Bonnefon     codec_ = QTextCodec::codecForName( qt_encoding );
3805fa25391SNicolas Bonnefon }
3815fa25391SNicolas Bonnefon 
doSetMultibyteEncodingOffsets(int before_cr,int after_cr)3824a4a124eSNicolas Bonnefon void LogData::doSetMultibyteEncodingOffsets( int before_cr, int after_cr )
3834a4a124eSNicolas Bonnefon {
3844a4a124eSNicolas Bonnefon     before_cr_offset_ = before_cr;
3854a4a124eSNicolas Bonnefon     after_cr_offset_ = after_cr;
3864a4a124eSNicolas Bonnefon }
3874a4a124eSNicolas Bonnefon 
doGetLineString(qint64 line) const388bb02e0acSNicolas Bonnefon QString LogData::doGetLineString( qint64 line ) const
389bb02e0acSNicolas Bonnefon {
390653377b6SNicolas Bonnefon     if ( line >= indexing_data_.getNbLines() ) { return 0; /* exception? */ }
391bb02e0acSNicolas Bonnefon 
392bb02e0acSNicolas Bonnefon     fileMutex_.lock();
393bb02e0acSNicolas Bonnefon 
3942f6fa462SNicolas Bonnefon     // end_byte is non-inclusive.(is not read)
3952f6fa462SNicolas Bonnefon     const qint64 first_byte = (line == 0) ?
3962f6fa462SNicolas Bonnefon         0 : ( indexing_data_.getPosForLine( line-1 ) + after_cr_offset_ );
397209000a6SNicolas Bonnefon     const qint64 end_byte  = endOfLinePosition( line );
398bb02e0acSNicolas Bonnefon 
3992f6fa462SNicolas Bonnefon     attached_file_->seek( first_byte );
4002f6fa462SNicolas Bonnefon 
4012f6fa462SNicolas Bonnefon     QString string = codec_->toUnicode( attached_file_->read( end_byte - first_byte ) );
402bb02e0acSNicolas Bonnefon 
403bb02e0acSNicolas Bonnefon     fileMutex_.unlock();
404bb02e0acSNicolas Bonnefon 
405bb02e0acSNicolas Bonnefon     return string;
406bb02e0acSNicolas Bonnefon }
407bb02e0acSNicolas Bonnefon 
doGetExpandedLineString(qint64 line) const408bb02e0acSNicolas Bonnefon QString LogData::doGetExpandedLineString( qint64 line ) const
409bb02e0acSNicolas Bonnefon {
410653377b6SNicolas Bonnefon     if ( line >= indexing_data_.getNbLines() ) { return 0; /* exception? */ }
411bb02e0acSNicolas Bonnefon 
412bb02e0acSNicolas Bonnefon     fileMutex_.lock();
413bb02e0acSNicolas Bonnefon 
4142f6fa462SNicolas Bonnefon     // end_byte is non-inclusive.(is not read) We also exclude the final \r.
4152f6fa462SNicolas Bonnefon     const qint64 first_byte = (line == 0) ?
4162f6fa462SNicolas Bonnefon         0 : ( indexing_data_.getPosForLine( line-1 ) + after_cr_offset_ );
417209000a6SNicolas Bonnefon     const qint64 end_byte  = endOfLinePosition( line );
418bb02e0acSNicolas Bonnefon 
4192f6fa462SNicolas Bonnefon     attached_file_->seek( first_byte );
4202f6fa462SNicolas Bonnefon 
4212f6fa462SNicolas Bonnefon     // LOG(logDEBUG) << "LogData::doGetExpandedLineString first_byte:" << first_byte << " end_byte:" << end_byte;
4222f6fa462SNicolas Bonnefon     QByteArray rawString = attached_file_->read( end_byte - first_byte );
423bb02e0acSNicolas Bonnefon 
424bb02e0acSNicolas Bonnefon     fileMutex_.unlock();
425bb02e0acSNicolas Bonnefon 
4265fa25391SNicolas Bonnefon     QString string = untabify( codec_->toUnicode( rawString ) );
4272f6fa462SNicolas Bonnefon 
4282f6fa462SNicolas Bonnefon     // LOG(logDEBUG) << "doGetExpandedLineString Line is: " << string.toStdString();
429bb02e0acSNicolas Bonnefon 
430bb02e0acSNicolas Bonnefon     return string;
431bb02e0acSNicolas Bonnefon }
432bb02e0acSNicolas Bonnefon 
433bb02e0acSNicolas Bonnefon // Note this function is also called from the LogFilteredDataWorker thread, so
434bb02e0acSNicolas Bonnefon // data must be protected because they are changed in the main thread (by
435bb02e0acSNicolas Bonnefon // indexingFinished).
doGetLines(qint64 first_line,int number) const436bb02e0acSNicolas Bonnefon QStringList LogData::doGetLines( qint64 first_line, int number ) const
437bb02e0acSNicolas Bonnefon {
438bb02e0acSNicolas Bonnefon     QStringList list;
439bb02e0acSNicolas Bonnefon     const qint64 last_line = first_line + number - 1;
440bb02e0acSNicolas Bonnefon 
441bb02e0acSNicolas Bonnefon     // LOG(logDEBUG) << "LogData::doGetLines first_line:" << first_line << " nb:" << number;
442bb02e0acSNicolas Bonnefon 
443bb02e0acSNicolas Bonnefon     if ( number == 0 ) {
444bb02e0acSNicolas Bonnefon         return QStringList();
445bb02e0acSNicolas Bonnefon     }
446bb02e0acSNicolas Bonnefon 
447653377b6SNicolas Bonnefon     if ( last_line >= indexing_data_.getNbLines() ) {
448bb02e0acSNicolas Bonnefon         LOG(logWARNING) << "LogData::doGetLines Lines out of bound asked for";
449bb02e0acSNicolas Bonnefon         return QStringList(); /* exception? */
450bb02e0acSNicolas Bonnefon     }
451bb02e0acSNicolas Bonnefon 
452bb02e0acSNicolas Bonnefon     fileMutex_.lock();
453bb02e0acSNicolas Bonnefon 
454653377b6SNicolas Bonnefon     const qint64 first_byte = (first_line == 0) ?
4554a4a124eSNicolas Bonnefon         0 : ( indexing_data_.getPosForLine( first_line-1 ) + after_cr_offset_ );
456209000a6SNicolas Bonnefon     const qint64 end_byte  = endOfLinePosition( last_line );
4572f6fa462SNicolas Bonnefon     // LOG(logDEBUG) << "LogData::doGetLines first_byte:" << first_byte << " end_byte:" << end_byte;
4581ee847caSNicolas Bonnefon     attached_file_->seek( first_byte );
4592f6fa462SNicolas Bonnefon     QByteArray blob = attached_file_->read( end_byte - first_byte );
460bb02e0acSNicolas Bonnefon 
461bb02e0acSNicolas Bonnefon     fileMutex_.unlock();
462bb02e0acSNicolas Bonnefon 
463bb02e0acSNicolas Bonnefon     qint64 beginning = 0;
464bb02e0acSNicolas Bonnefon     qint64 end = 0;
465bb02e0acSNicolas Bonnefon     for ( qint64 line = first_line; (line <= last_line); line++ ) {
466209000a6SNicolas Bonnefon         end = endOfLinePosition( line ) - first_byte;
467bb02e0acSNicolas Bonnefon         // LOG(logDEBUG) << "Getting line " << line << " beginning " << beginning << " end " << end;
4682f6fa462SNicolas Bonnefon         QByteArray this_line = blob.mid( beginning, end - beginning );
469bb02e0acSNicolas Bonnefon         // LOG(logDEBUG) << "Line is: " << QString( this_line ).toStdString();
4705fa25391SNicolas Bonnefon         list.append( codec_->toUnicode( this_line ) );
471209000a6SNicolas Bonnefon         beginning = beginningOfNextLine( end );
472bb02e0acSNicolas Bonnefon     }
473bb02e0acSNicolas Bonnefon 
474bb02e0acSNicolas Bonnefon     return list;
475bb02e0acSNicolas Bonnefon }
476bb02e0acSNicolas Bonnefon 
doGetExpandedLines(qint64 first_line,int number) const477bb02e0acSNicolas Bonnefon QStringList LogData::doGetExpandedLines( qint64 first_line, int number ) const
478bb02e0acSNicolas Bonnefon {
479bb02e0acSNicolas Bonnefon     QStringList list;
480bb02e0acSNicolas Bonnefon     const qint64 last_line = first_line + number - 1;
481bb02e0acSNicolas Bonnefon 
482bb02e0acSNicolas Bonnefon     if ( number == 0 ) {
483bb02e0acSNicolas Bonnefon         return QStringList();
484bb02e0acSNicolas Bonnefon     }
485bb02e0acSNicolas Bonnefon 
486653377b6SNicolas Bonnefon     if ( last_line >= indexing_data_.getNbLines() ) {
487bb02e0acSNicolas Bonnefon         LOG(logWARNING) << "LogData::doGetExpandedLines Lines out of bound asked for";
488bb02e0acSNicolas Bonnefon         return QStringList(); /* exception? */
489bb02e0acSNicolas Bonnefon     }
490bb02e0acSNicolas Bonnefon 
491bb02e0acSNicolas Bonnefon     fileMutex_.lock();
492bb02e0acSNicolas Bonnefon 
4932f6fa462SNicolas Bonnefon     // end_byte is non-inclusive.(is not read)
494653377b6SNicolas Bonnefon     const qint64 first_byte = (first_line == 0) ?
4954a4a124eSNicolas Bonnefon         0 : ( indexing_data_.getPosForLine( first_line-1 ) + after_cr_offset_ );
496209000a6SNicolas Bonnefon     const qint64 end_byte  = endOfLinePosition( last_line );
4972f6fa462SNicolas Bonnefon     LOG(logDEBUG) << "LogData::doGetExpandedLines first_byte:" << first_byte << " end_byte:" << end_byte;
4982686d8e5SNicolas Bonnefon 
4991ee847caSNicolas Bonnefon     attached_file_->seek( first_byte );
5002f6fa462SNicolas Bonnefon     QByteArray blob = attached_file_->read( end_byte - first_byte );
501bb02e0acSNicolas Bonnefon 
502bb02e0acSNicolas Bonnefon     fileMutex_.unlock();
503bb02e0acSNicolas Bonnefon 
504bb02e0acSNicolas Bonnefon     qint64 beginning = 0;
505bb02e0acSNicolas Bonnefon     qint64 end = 0;
506bb02e0acSNicolas Bonnefon     for ( qint64 line = first_line; (line <= last_line); line++ ) {
5072f6fa462SNicolas Bonnefon         // end is non-inclusive
5082f6fa462SNicolas Bonnefon         // LOG(logDEBUG) << "EoL " << line << ": " << indexing_data_.getPosForLine( line );
509209000a6SNicolas Bonnefon         end = endOfLinePosition( line ) - first_byte;
5102f6fa462SNicolas Bonnefon         // LOG(logDEBUG) << "Getting line " << line << " beginning " << beginning << " end " << end;
5112f6fa462SNicolas Bonnefon         QByteArray this_line = blob.mid( beginning, end - beginning );
5122f6fa462SNicolas Bonnefon         QString conv_line = codec_->toUnicode( this_line );
5132f6fa462SNicolas Bonnefon         // LOG(logDEBUG) << "Line is: " << conv_line.toStdString();
5142f6fa462SNicolas Bonnefon         list.append( untabify( conv_line ) );
515209000a6SNicolas Bonnefon         beginning = beginningOfNextLine( end );
516bb02e0acSNicolas Bonnefon     }
517bb02e0acSNicolas Bonnefon 
518bb02e0acSNicolas Bonnefon     return list;
519bb02e0acSNicolas Bonnefon }
5205fa25391SNicolas Bonnefon 
getDetectedEncoding() const5215fa25391SNicolas Bonnefon EncodingSpeculator::Encoding LogData::getDetectedEncoding() const
5225fa25391SNicolas Bonnefon {
5235fa25391SNicolas Bonnefon     return indexing_data_.getEncodingGuess();
5245fa25391SNicolas Bonnefon }
525209000a6SNicolas Bonnefon 
526209000a6SNicolas Bonnefon // Given a line number, returns the position (offset in file) of
527209000a6SNicolas Bonnefon // the byte immediately past its end.
528209000a6SNicolas Bonnefon // e.g. in utf-16: T e s t \n2 n d l i n e \n
529209000a6SNicolas Bonnefon //                 --------------------------
530209000a6SNicolas Bonnefon //                           ^
531209000a6SNicolas Bonnefon //                   endOfLinePosition( 0 )
endOfLinePosition(qint64 line) const532209000a6SNicolas Bonnefon qint64 LogData::endOfLinePosition( qint64 line ) const
533209000a6SNicolas Bonnefon {
534209000a6SNicolas Bonnefon     return indexing_data_.getPosForLine( line ) - 1 - before_cr_offset_;
535209000a6SNicolas Bonnefon }
536209000a6SNicolas Bonnefon 
537209000a6SNicolas Bonnefon // Given the position (offset in file) of the end of a line, returns
538209000a6SNicolas Bonnefon // the position of the beginning of the following, taking into account
539209000a6SNicolas Bonnefon // encoding and newline signalling.
beginningOfNextLine(qint64 end_pos) const540209000a6SNicolas Bonnefon qint64 LogData::beginningOfNextLine( qint64 end_pos ) const
541209000a6SNicolas Bonnefon {
542209000a6SNicolas Bonnefon     return end_pos + 1 + before_cr_offset_ + after_cr_offset_;
543209000a6SNicolas Bonnefon }
544*84a8a910SNicolas Bonnefon 
545*84a8a910SNicolas Bonnefon // Close and reopen the file.
546*84a8a910SNicolas Bonnefon // Used if we suspect the file has been moved (we follow the old
547*84a8a910SNicolas Bonnefon // inode but really want the one now associated with the name)
reOpenFile()548*84a8a910SNicolas Bonnefon void LogData::reOpenFile()
549*84a8a910SNicolas Bonnefon {
550*84a8a910SNicolas Bonnefon     auto reopened = std::make_unique<QFile>( attached_file_->fileName() );
551*84a8a910SNicolas Bonnefon     reopened->open( QIODevice::ReadOnly );
552*84a8a910SNicolas Bonnefon     QMutexLocker locker( &fileMutex_ );
553*84a8a910SNicolas Bonnefon     attached_file_ = std::move( reopened );      // This will close the old one and open the new
554*84a8a910SNicolas Bonnefon }
555