]> gitweb.hamatoma.de Git - reqt/commitdiff
rebackgui: checksum works
authorhama <hama@siduction.net>
Sun, 31 Jan 2016 17:40:59 +0000 (18:40 +0100)
committerhama <hama@siduction.net>
Sun, 31 Jan 2016 17:40:59 +0000 (18:40 +0100)
appl/rebackgui/BackupEngine.cpp
appl/rebackgui/BackupEngine.hpp
appl/rebackgui/mainwindow.cpp
appl/rebackgui/mainwindow.hpp
appl/rebackgui/mainwindow.ui
base/ReFileUtils.cpp
base/ReFileUtils.hpp
gui/ReGuiApplication.cpp
gui/ReGuiApplication.hpp

index 2b280868fb32a05b508fe50388404e9fa6e49797..5e7eadcd45025c6e46d5e60f44bc77af8179503e 100644 (file)
@@ -21,6 +21,8 @@ bool BackupEngine::m_searchReady = false;
 QMutex BackupEngine::m_mutex;
 QChar BackupEngine::m_separator = '\t';
 QString BackupEngine::m_separatorString = "\t";
+QStringList ChecksumTask::m_checksumInfo;
+bool ChecksumTask::m_sourceProcessingReady = false;
 
 /**
  * Constructor.
@@ -78,6 +80,9 @@ bool BackupEngine::log(const QString& message){
 /**
  * Constructor.
  *
+ * @param compareWithTarget    <code>false</code>: all files will be found<br>
+ *                                                     otherwise: only not existing or newer files will
+ *                                                     be found
  * @param name                 name of the task
  * @param filePatterns only files which match the patterns will be found
  * @param dirPatterns  only subdirectories which matches the patterns will be entered
@@ -85,15 +90,15 @@ bool BackupEngine::log(const QString& message){
  * @param targetDir            the base target directory
  * @param mainWindow   the GUI module, the parent
  */
-SearchTask::SearchTask(const QString& name,
+SearchTask::SearchTask(bool compareWithTarget, const QString& name,
                const QString& filePatterns, const QString& dirPatterns,
                const QStringList& sourceDirs, const QString& targetDir,
                MainWindow* mainWindow) :
        BackupEngine(name, sourceDirs, targetDir, mainWindow),
        m_fileMatcher(filePatterns),
-       m_dirMatcher(dirPatterns)
+       m_dirMatcher(dirPatterns),
+       m_compareWithTarget(compareWithTarget)
 {
-
 }
 
 /**
@@ -106,12 +111,13 @@ void SearchTask::run()
        QString targetDir, sourceDir;
        for (int ix = 0; ix < m_sourceDirs.size(); ix++){
                sourceDir = m_sourceDirs.at(ix);
-               targetDir = m_targetDirs.at(ix) + ReFileUtils::nodeOf(sourceDir);
-               processOneDirectory(sourceDir, targetDir, ix);
+               if (m_compareWithTarget)
+                       targetDir = m_targetDirs.at(ix) + ReFileUtils::nodeOf(sourceDir);
+               searchOneDirectory(sourceDir, targetDir, ix);
        }
        m_searchReady = true;
        m_mainWindow->externalLog(tr(
-               "Search finished: to copy: %1 with %2 matching: %3 total: %4 "
+               "Search finished: to process: %1 with %2 matching: %3 total: %4 "
                "subdirs: %5 runtime: %6")
                .arg(m_hotFiles)
                .arg(ReQStringUtils::readableSize(m_hotBytes))
@@ -132,12 +138,16 @@ void SearchTask::run()
  *                                     it is newer or has another size
  * @param index                the index of the base directory in m_sourceDirs
  */
-void SearchTask::processOneDirectory(const QString& source,
+void SearchTask::searchOneDirectory(const QString& source,
                                                                         const QString& target, int index){
        QDirIterator it(source);
-       QString prefix;
        QString info, node;
        int lengthBase = m_sourceDirs.at(index).length();
+       assert(index < 0x7fff);
+       QString prefix;
+       m_mutex.lock();
+       m_totalDirs++;
+       m_mutex.unlock();
        if (source.length() > lengthBase){
                prefix = QChar(1 + index) + source.mid(lengthBase) + OS_SEPARATOR_STR
                                + m_separator;
@@ -159,7 +169,9 @@ void SearchTask::processOneDirectory(const QString& source,
           } else {
                   qint64 diff = 0;
                   bool doCopy = false;
-                  if (target.isEmpty())
+                  if (! m_compareWithTarget)
+                          doCopy = true;
+                  else if (target.isEmpty())
                           doCopy = true;
                   else {
                           QFileInfo trg(target + it.fileName());
@@ -175,7 +187,6 @@ void SearchTask::processOneDirectory(const QString& source,
                           }
                   }
                   if (doCopy){
-                          assert(index < 0x7fff);
                           info = prefix + it.fileName();
                   }
                   m_mutex.lock();
@@ -209,11 +220,8 @@ void SearchTask::processOneDirectory(const QString& source,
                                           subTarget.clear();
                                   else
                                           subTarget += OS_SEPARATOR_STR;
-                                  processOneDirectory(it2.filePath(), subTarget, index);
-                                  m_mutex.lock();
-                                  m_totalDirs++;
-                                  m_mutex.unlock();
                           }
+                          searchOneDirectory(it2.filePath(), subTarget, index);
                   }
                }
        }
@@ -231,7 +239,8 @@ BackupTask::BackupTask(const QString& name,
                                           const QStringList& sourceDirs, const QString& targetDir,
                                           MainWindow* mainWindow) :
        BackupEngine(name, sourceDirs, targetDir, mainWindow),
-       m_lastRelPath()
+       m_lastRelPath(),
+       m_buffer()
 {
 
 }
@@ -253,7 +262,7 @@ void BackupTask::copyFile(int index, const QString& relPath,
        bool isFile;
        if (! ReFileUtils::isDirectory(targetDir, &isFile)){
                if (isFile){
-            if (_unlink(I18N::s2b(targetDir).constData()) != 0)
+                       if (_unlink(I18N::s2b(targetDir).constData()) != 0)
                                error(tr("cannot remove file (for making a directory (%1): %2")
                                          .arg(errno).arg(targetDir));
                }
@@ -318,8 +327,203 @@ void BackupTask::run()
                                                                                 .arg(ReQStringUtils::readableDuration(estimated)));
                }
        }
-    m_mainWindow->externalTaskFinished(tr("backup complete after %1. Errors: %2")
-                .arg(ReQStringUtils::readableDuration(
-                    QDateTime::currentMSecsSinceEpoch() - start))
-                                       .arg(m_mainWindow->errors()));
+       m_mainWindow->externalTaskFinished(tr("backup complete after %1. Errors: %2")
+                               .arg(ReQStringUtils::readableDuration(
+                                       QDateTime::currentMSecsSinceEpoch() - start))
+                                                                          .arg(m_mainWindow->errors()));
+}
+
+/**
+ * Constructor.
+ *
+ * @param name                 name of the task
+ * @param sourceDirs   the list of source directories to inspect
+ * @param targetDir            the base of the target directories
+ * @param mainWindow   the GUI module
+ */
+ChecksumTask::ChecksumTask(const QString& name,
+                                          const QStringList& sourceDirs, const QString& targetDir,
+                                          MainWindow* mainWindow) :
+       BackupEngine(name, sourceDirs, targetDir, mainWindow),
+       m_lastRelPath(),
+       m_buffer()
+{
+       m_buffer.resize(1024*1024);
+}
+
+/**
+ * Calculates the checksum of a given file.
+ *
+ * @param isSource     <code>true</code>: the file is taken from the source<br>
+ *                                     <code>true</code>: the file is taken from the target
+ * @param index                index in source/target dirs (dependin on <code>isSource</code>)
+ * @param relpath      the relative path from the base (source or target)
+ * @param node         the node of the file (without path)
+ * @return                     "": checksum could not be calculated<br>
+ *                                     otherwise: the checksum of the file
+ */
+QByteArray ChecksumTask::buildChecksum(bool isSource, int index,
+       const QString& relpath, const QString& node)
+{
+       QString filename;
+       if (isSource)
+               filename = m_sourceDirs.at(index) + relpath + node;
+       else
+               filename = m_targetDirs.at(index) + relpath + node;
+       qint64 size = 0;
+       QByteArray rc = ReFileUtils::buildChecksum(filename, m_buffer, &size).toHex();
+       m_mutex.lock();
+       m_processedFiles++;
+       m_processedBytes += size;
+       m_mutex.unlock();
+       if (rc.isEmpty()){
+               error(QObject::tr("cannot build checksum: %1").arg(filename) );
+       } else {
+               m_mainWindow->addToFileList(QString(rc) + " " + filename);
+       }
+       return rc;
 }
+
+/**
+ * Constructor.
+ *
+ * @param name                 name of the task
+ * @param sourceDirs   the list of source directories to inspect
+ * @param targetDir            the base of the target directories
+ * @param mainWindow   the GUI module
+ */
+ChecksumOfSourceTask::ChecksumOfSourceTask(const QString& name,
+                                          const QStringList& sourceDirs, const QString& targetDir,
+                                          MainWindow* mainWindow) :
+       ChecksumTask(name, sourceDirs, targetDir, mainWindow)
+{
+}
+
+/**
+ * Calculats the checksum of source files.
+ */
+void ChecksumOfSourceTask::run()
+{
+       qint64 start = QDateTime::currentMSecsSinceEpoch();
+       QString relPath, node;
+       QString info;
+       int count = 0;
+       while (true){
+               m_mutex.lock();
+               if (m_files.size() == 0)
+                       info.clear();
+               else{
+                       info = m_files.first();
+                       m_files.removeFirst();
+               }
+               m_mutex.unlock();
+               if (info.isEmpty()){
+                       if (m_searchReady)
+                               break;
+                       else
+                               QThread::msleep(50);
+               } else {
+                       int index = int(info.at(0).unicode()) - 1;
+                       int pos = info.indexOf(m_separator, 1);
+                       if (pos == 1)
+                               relPath.clear();
+                       else
+                               relPath = info.mid(1, pos - 1);
+                       node = info.mid(pos + 1);
+                       QByteArray checksum = buildChecksum(true, index, relPath, node);
+                       count++;
+                       if (! checksum.isEmpty()){
+                               QString info = QChar(1 + index) + relPath + m_separator
+                                               + node + m_separatorString + checksum;
+                               m_mutex.lock();
+                               m_checksumInfo.append(info);
+                               m_mutex.unlock();
+                       }
+               }
+       }
+       m_sourceProcessingReady = true;
+       qint64 now = QDateTime::currentMSecsSinceEpoch();
+       m_mainWindow->externalTaskFinished(tr("Building source checksum complete after %1. Processed: %2")
+                               .arg(ReQStringUtils::readableDuration(now - start))
+                                                                          .arg(count));
+}
+
+/**
+ * Constructor.
+ *
+ * @param name                 name of the task
+ * @param sourceDirs   the list of source directories to inspect
+ * @param targetDir            the base of the target directories
+ * @param mainWindow   the GUI module
+ */
+ChecksumOfTargetTask::ChecksumOfTargetTask(const QString& name,
+                                          const QStringList& sourceDirs, const QString& targetDir,
+                                          MainWindow* mainWindow) :
+       ChecksumTask(name, sourceDirs, targetDir, mainWindow)
+{
+
+}
+
+/**
+ * Calculats the checksum of target files.
+ */
+void ChecksumOfTargetTask::run()
+{
+       qint64 start = QDateTime::currentMSecsSinceEpoch();
+       QString relPath, node;
+       qint64 now = 0;
+       QString info;
+       int count = 0;
+       while (true){
+               m_mutex.lock();
+               if (m_checksumInfo.size() == 0)
+                       info.clear();
+               else{
+                       info = m_checksumInfo.first();
+                       m_checksumInfo.removeFirst();
+               }
+               m_mutex.unlock();
+               if (info.isEmpty()){
+                       if (m_sourceProcessingReady)
+                               break;
+                       else
+                               QThread::msleep(50);
+               } else {
+                       int index = int(info.at(0).unicode()) - 1;
+                       int pos = info.indexOf(m_separator, 1);
+                       if (pos == 1)
+                               relPath.clear();
+                       else
+                               relPath = info.mid(1, pos - 1);
+                       int pos2 = info.indexOf(m_separator, pos + 1);
+                       node = info.mid(pos + 1, pos2 - pos - 1);
+                       QString sourceChecksum = info.mid(pos2 + 1);
+
+                       QByteArray checksum = buildChecksum(false, index, relPath, node);
+                       count++;
+                       if (! checksum.isEmpty()){
+                               if (checksum != sourceChecksum)
+                                       error(QObject::tr("checksum differs: ") + relPath + node
+                                                 + " [" + sourceChecksum + "/" + checksum + "]");
+                       }
+                       now = QDateTime::currentMSecsSinceEpoch();
+                       qint64 estimated = (now - start) * m_hotBytes * 2 /  max(1LL, m_processedBytes);
+                       m_mainWindow->externalAppend(ReGuiQueueItem::StatusLine, NULL,
+                                       tr("%1 of %2 (%3 of %4) %5 MB/sec Remaining: %6 of %7")
+                                       .arg(m_processedFiles).arg(m_hotFiles * 2)
+                                                                                .arg(ReQStringUtils::readableSize(m_processedBytes))
+                                                                                .arg(ReQStringUtils::readableSize(m_hotBytes * 2))
+                                                                                .arg(m_processedBytes / 1024.0 / 1024 / (now - start) * 1000, 0, 'f', 3)
+                                                                                .arg(ReQStringUtils::readableDuration(estimated - (now - start)))
+                                                                                .arg(ReQStringUtils::readableDuration(estimated)));
+
+               }
+       }
+       now = QDateTime::currentMSecsSinceEpoch();
+       m_mainWindow->externalTaskFinished(tr("Building target checksums complete after %1. Processed: %2 Errors: %3")
+                               .arg(ReQStringUtils::readableDuration(now - start))
+                                                                          .arg(count)
+                                                                          .arg(m_mainWindow->errors()));
+}
+
+
index 07f0a1f5d2bb702e20975d3fdafe3e7cdc3d6b47..5171ee110b29db3000b70da0150761c2988071c9 100644 (file)
@@ -10,6 +10,9 @@
 #define BACKUPPROCESSOR_HPP
 
 
+/**
+ * Abstract base class of executing backup and related tasks.
+ */
 class BackupEngine : public QThread
 {
 public:
@@ -32,6 +35,7 @@ protected:
    QString m_name;
 public:
    static bool m_shouldStop;
+   /// Entry: <index_of_source_dir><relative_path>TAB<node>
    static QStringList m_files;
    static qint64 m_hotBytes;
    static int m_processedFiles;
@@ -46,37 +50,99 @@ public:
    static QString m_separatorString;
 };
 
-class SearchTask : public BackupEngine
+/**
+ * Reads filenames from the file list and copies the files from source to target.
+ */
+class BackupTask : public BackupEngine
 {
 public:
-       SearchTask(const QString& name,
-                          const QString& filePatterns, const QString& dirPatterns,
-                          const QStringList& sourceDirs, const QString& targetDir,
+       BackupTask(const QString& name, const QStringList& sourceDirs, const QString& targetDir,
                           MainWindow* mainWindow);
 public:
        virtual void run();
+protected:
+       void copyFile(int index, const QString& relpath,
+                                const QString& node);
 private:
-       QString m_filePatterns;
-       QString m_dirPatterns;
-       ReIncludeExcludeMatcher m_fileMatcher;
-       ReIncludeExcludeMatcher m_dirMatcher;
-       void processOneDirectory(const QString& source, const QString& target,
-                                                        int index);
+       QString m_lastRelPath;
+       QByteArray m_buffer;
 };
 
-class BackupTask : public BackupEngine
+/**
+ * Abstract base class of the checksum test classes.
+ */
+class ChecksumTask : public BackupEngine
 {
 public:
-       BackupTask(const QString& name, const QStringList& sourceDirs, const QString& targetDir,
+       ChecksumTask(const QString& name, const QStringList& sourceDirs, const QString& targetDir,
                           MainWindow* mainWindow);
 public:
-       virtual void run();
+       virtual void run() = 0;
 protected:
-       void copyFile(int index, const QString& relpath,
+       QByteArray buildChecksum( bool isSource, int index, const QString& relpath,
                                 const QString& node);
-private:
+protected:
        QString m_lastRelPath;
        QByteArray m_buffer;
+protected:
+       /// Entry: <index_of_target_dir><relative_path>TAB<node>TAB<checksum>
+       static QStringList m_checksumInfo;
+public:
+       static bool m_sourceProcessingReady;
+};
+
+/**
+  Handles the checksum activities for the source files.
+
+ * Reads from the file list, calculates the checksum of the source file and
+ * writes an entry into the checksum file list
+ */
+class ChecksumOfSourceTask : public ChecksumTask
+{
+public:
+       ChecksumOfSourceTask(const QString& name, const QStringList& sourceDirs, const QString& targetDir,
+                          MainWindow* mainWindow);
+public:
+       virtual void run();
 };
 
+/**
+  Handles the checksum activities for the target files.
+
+ * Reads from the checksum file list, calculates the checksum of the target file
+ * and compares it with the checksum of the source file.
+ */
+class ChecksumOfTargetTask : public ChecksumTask
+{
+public:
+       ChecksumOfTargetTask(const QString& name, const QStringList& sourceDirs, const QString& targetDir,
+                          MainWindow* mainWindow);
+public:
+       virtual void run();
+};
+
+/**
+ * Searches the files matching the file patterns and writes them into the file list.
+ */
+class SearchTask : public BackupEngine
+{
+public:
+       SearchTask(bool compareWithTarget, const QString& name,
+                          const QString& filePatterns, const QString& dirPatterns,
+                          const QStringList& sourceDirs, const QString& targetDir,
+                          MainWindow* mainWindow);
+public:
+       virtual void run();
+private:
+       QString m_filePatterns;
+       QString m_dirPatterns;
+       ReIncludeExcludeMatcher m_fileMatcher;
+       ReIncludeExcludeMatcher m_dirMatcher;
+       void searchOneDirectory(const QString& source, const QString& target,
+                                                        int index);
+private:
+       bool m_compareWithTarget;
+};
+
+
 #endif // BACKUPPROCESSOR_HPP
index 59887d7997e38662e5e45b6b0b5aeb25a975fa95..293657c2fcb7379a3812e544f7020781585d0920 100644 (file)
@@ -10,7 +10,7 @@
 #include "aboutdialog.hpp"
 #include <QFileDialog>
 
-const QString VERSION("2016.01.30");
+const QString VERSION("2016.01.31");
 
 /**
  * Constructor.
@@ -28,6 +28,8 @@ MainWindow::MainWindow(const QString& homeDir, QWidget *parent) :
        m_lastSource(),
        m_searchTask(NULL),
        m_backupTask(NULL),
+       m_checksumOfSourceTask(NULL),
+       m_checksumOfTargetTask(NULL),
        m_errors(0)
 {
        ui->setupUi(this);
@@ -35,9 +37,12 @@ MainWindow::MainWindow(const QString& homeDir, QWidget *parent) :
    startStop(false);
    connect(ui->actionStart, SIGNAL(triggered()), this,
          SLOT(onStart()));
+   connect(ui->actionChecksums, SIGNAL(triggered()), this,
+         SLOT(onChecksums()));
    connect(ui->actionStop, SIGNAL(triggered()), this,
          SLOT(onStop()));
    connect(ui->pushButtonBackup, SIGNAL(clicked()), this, SLOT(onStart()));
+   connect(ui->pushButtonChecksum, SIGNAL(clicked()), this, SLOT(onChecksums()));
    connect(ui->pushButtonStop, SIGNAL(clicked()), this, SLOT(onStop()));
    connect(ui->pushButtonAddItem, SIGNAL(clicked()), this, SLOT(onAddItem()));
    connect(ui->pushButtonDeleteItem, SIGNAL(clicked()), this, SLOT(onDeleteItem()));
@@ -78,7 +83,7 @@ MainWindow::~MainWindow()
 /**
  * Calles at the program's end.
  */
-void MainWindow::aboutToQuit()
+void MainWindow::onAboutToQuit()
 {
 
 }
@@ -153,7 +158,7 @@ QString MainWindow::extractTarget(const QString& dir){
 /**
  * Set GUI elements from the queue when the GUI timer is triggered.
  */
-void MainWindow::guiTimerUpdate()
+void MainWindow::onGuiTimerUpdate()
 {
        int count = m_guiQueue.count();
        while(count-- > 0){
@@ -189,6 +194,23 @@ void MainWindow::guiTimerUpdate()
        }
 }
 
+/**
+ * Common initializations for all task starts.
+ */
+void MainWindow::initializeStart(){
+       BackupEngine::m_searchReady = false;
+       BackupEngine::m_hotBytes = 0;
+       BackupEngine::m_hotFiles = 0;
+       BackupEngine::m_matchedFiles = 0;
+       BackupEngine::m_totalDirs = 0;
+       BackupEngine::m_totalFiles = 0;
+       BackupEngine::m_processedFiles = 0;
+       BackupEngine::m_processedBytes = 0;
+       ChecksumTask::m_sourceProcessingReady = false;
+       m_errors = 0;
+       startStop(true);
+}
+
 /**
  * @brief Logs a message
  *
@@ -261,6 +283,40 @@ void MainWindow::onSelectionChanged(const QItemSelection& selected,
        }
 
 }
+
+/**
+ * Starts the backup.
+ */
+void MainWindow::onChecksums(){
+       int row = ui->tableWidget->currentRow();
+       if (row < 0){
+               say(LOG_ERROR, tr("no backup item selected"));
+       } else {
+               BackupItem& item = m_configuration.items()[row];
+               QString target = BackupUtils::findTarget(item, &m_logger);
+               if (target.isEmpty()){
+                       say(LOG_ERROR, tr("Target not available"));
+               } else {
+                       ReQStringUtils::ensureLastChar(target, OS_SEPARATOR);
+                       initializeStart();
+                       writeTargetConfiguration(item, target);
+                       delete m_searchTask;
+                       m_searchTask = new SearchTask(false, item.m_name,
+                                                                                 item.m_filePatterns, item.m_dirPatterns,
+                                                                                 item.m_sources, target, this);
+                       m_searchTask->start();
+                       delete m_checksumOfSourceTask;
+                       m_checksumOfSourceTask = new ChecksumOfSourceTask(item.m_name,
+                                       item.m_sources, target, this);
+                       m_checksumOfSourceTask->start();
+                       delete m_checksumOfTargetTask;
+                       m_checksumOfTargetTask = new ChecksumOfTargetTask(item.m_name,
+                                       item.m_sources, target, this);
+                       m_checksumOfTargetTask->start();
+               }
+       }
+}
+
 /**
  * Handles the push of the button "select directory".
  */
@@ -367,24 +423,15 @@ void MainWindow::onStart(){
                say(LOG_ERROR, tr("no backup item selected"));
        } else {
                BackupItem& item = m_configuration.items()[row];
-               BackupEngine::m_searchReady = false;
-               BackupEngine::m_hotBytes = 0;
-               BackupEngine::m_hotFiles = 0;
-               BackupEngine::m_matchedFiles = 0;
-               BackupEngine::m_totalDirs = 0;
-               BackupEngine::m_totalFiles = 0;
-               BackupEngine::m_processedFiles = 0;
-               BackupEngine::m_processedBytes = 0;
-               m_errors = 0;
                QString target = BackupUtils::findTarget(item, &m_logger);
                if (target.isEmpty()){
                        say(LOG_ERROR, tr("Target not available"));
                } else {
                        ReQStringUtils::ensureLastChar(target, OS_SEPARATOR);
                        writeTargetConfiguration(item, target);
-                       startStop(true);
+                       initializeStart();
                        delete m_searchTask;
-                       m_searchTask = new SearchTask(item.m_name,
+                       m_searchTask = new SearchTask(true, item.m_name,
                                                                                  item.m_filePatterns, item.m_dirPatterns,
                                                                                  item.m_sources, target, this);
                        m_searchTask->start();
@@ -480,6 +527,8 @@ void MainWindow::startStop(bool isStart){
        ui->actionStop->setEnabled(isStart);
        ui->pushButtonBackup->setEnabled(! isStart);
        ui->pushButtonStop->setEnabled(isStart);
+       ui->pushButtonChecksum->setEnabled(! isStart);
+       ui->actionChecksums->setEnabled(! isStart);
 }
 
 
index e2c6edf3d5542d6b0084844e797fa174b55efd0e..9e2163b8b189ac74c9298be554e72335da129386 100644 (file)
@@ -35,21 +35,24 @@ public:
    void restoreState();
    void saveState();
 
+public slots:
 private:
    QString extractTarget(const QString& dir);
+   void initializeStart();
    void writeTargetConfiguration(BackupItem& item, const QString& target);
 private slots:
-   virtual void aboutToQuit();
-   virtual void guiTimerUpdate();
    void onAbout();
+   virtual void onAboutToQuit();
    void onAddItem();
    void onAddSource();
+   void onChecksums();
    void onClearFileList();
    void onClearErrorList();
    void onClearLog();
    void onCurrentChanged(const QModelIndex& current, const QModelIndex& previous);
    void onDeleteItem();
    void onDeleteSource();
+   virtual void onGuiTimerUpdate();
    void onSaveConfig();
    void onStart();
    void onSelectionChanged(const QItemSelection& prior, const QItemSelection& later);
@@ -68,6 +71,8 @@ private:
        QString m_lastSource;
        SearchTask* m_searchTask;
        BackupTask* m_backupTask;
+       ChecksumOfSourceTask* m_checksumOfSourceTask;
+       ChecksumOfTargetTask* m_checksumOfTargetTask;
        int m_errors;
 };
 
index 6cf4e042b056f5a79652585402a944cce0f4cc3c..c62cbfbc607833cba79a3013434810412e3a8f49 100644 (file)
                 </property>
                </spacer>
               </item>
+              <item>
+               <widget class="QPushButton" name="pushButtonChecksum">
+                <property name="minimumSize">
+                 <size>
+                  <width>125</width>
+                  <height>0</height>
+                 </size>
+                </property>
+                <property name="toolTip">
+                 <string>Calculates checksums for source and target files and reports differences</string>
+                </property>
+                <property name="text">
+                 <string>Checksums</string>
+                </property>
+               </widget>
+              </item>
              </layout>
             </item>
            </layout>
     </property>
     <addaction name="actionStart"/>
     <addaction name="actionStop"/>
+    <addaction name="actionChecksums"/>
    </widget>
    <addaction name="menuFile"/>
    <addaction name="menuEdit"/>
   </action>
   <action name="actionStop">
    <property name="text">
-    <string>Stop backup</string>
+    <string>Stop</string>
    </property>
   </action>
   <action name="actionAbout">
     <string>About</string>
    </property>
   </action>
+  <action name="actionChecksums">
+   <property name="text">
+    <string>Checksums</string>
+   </property>
+   <property name="toolTip">
+    <string>Calculates checksums for source and target files and reports differences</string>
+   </property>
+  </action>
  </widget>
  <layoutdefault spacing="6" margin="11"/>
  <resources/>
index 4e36b449cd6f7caf7cc6bd12e4c3d2b5bb819d56..e1bcf518eb729eabae8800b9a6c35c6b90d5fd3b 100644 (file)
@@ -10,6 +10,8 @@
  */
 
 #include "base/rebase.hpp"
+#include <QCryptographicHash>
+
 enum {
        LOC_DELETE_TREE_1 = LOC_FIRST_OF(LOC_FILE_UTILS), // 12501
        LOC_DELETE_TREE_2,              // 12502
@@ -30,6 +32,41 @@ ReTreeStatistic::ReTreeStatistic() :
                m_fileSizes(0L) {
 }
 
+/**
+ * Builds a checksum of the given file.
+ *
+ * @param filename     the name of the file to process
+ * @param buffer       OUT: buffer for reading the files
+ * @param sizes                OUT: size of the file content. May be NULL
+ * @return                     "": the file could not be read completely<br>
+ *                                     otherwise: the checksum
+ */
+QByteArray ReFileUtils::buildChecksum(const char* filename, QByteArray& buffer,
+                                                                         qint64* size)
+{
+       if (buffer.capacity() < 0x10000)
+               buffer.reserve(0x10000);
+       // round it down to a multiple of the checksum size:
+       int blocksize = buffer.capacity() / 128 * 128;
+       buffer.resize(blocksize);
+       qint64 sumBytes = 0;
+       QByteArray rc;
+       FILE* fp = fopen(filename, "rb");
+       if (fp != NULL){
+               int bytesRead = 0;
+               char* bufferPointer = buffer.data();
+               QCryptographicHash hash(QCryptographicHash::Md5);
+               while( (bytesRead = fread(bufferPointer, 1, blocksize, fp)) > 0){
+                       hash.addData(bufferPointer, bytesRead);
+                       sumBytes += bytesRead;
+               }
+               rc = hash.result();
+               fclose(fp);
+               if (size != NULL)
+                       *size = sumBytes;
+       }
+       return rc;
+}
 
 /** Normalizes a file path.
  *
@@ -427,10 +464,10 @@ mode_t ReFileUtils::nativePermissions(QFile::Permissions permissions){
        if (permissions.testFlag(QFile::ExeOther))
                rc |= S_IXOTH;
 #elif defined _WIN32
-    if (permissions.testFlag(QFile::ReadOwner) || permissions.testFlag(QFile::ReadOther))
-        rc |= _S_IREAD;
-    if (permissions.testFlag(QFile::ReadOwner) ||permissions.testFlag(QFile::WriteOther))
-        rc |= _S_IWRITE;
+       if (permissions.testFlag(QFile::ReadOwner) || permissions.testFlag(QFile::ReadOther))
+               rc |= _S_IREAD;
+       if (permissions.testFlag(QFile::ReadOwner) ||permissions.testFlag(QFile::WriteOther))
+               rc |= _S_IWRITE;
 #endif
        return rc;
 }
@@ -491,8 +528,8 @@ bool ReFileUtils::makeDirWithParents(const char* path, ReLogger* logger) {
                else {
                        const char* end;
                        const char* start = path;
-            rc = true;
-            dir.clear();
+                       rc = true;
+                       dir.clear();
                        while (rc && (end = strchr(start, OS_SEPARATOR)) != NULL) {
                                if (end == path) {
                                        start++;
@@ -501,10 +538,10 @@ bool ReFileUtils::makeDirWithParents(const char* path, ReLogger* logger) {
                                        dir.append(start, end - start);
                                        start = end + 1;
 #if defined _WIN32
-                    if (dir.length() == 2 && dir.at(1) == ':'){
-                        dir.append(OS_SEPARATOR);
+                                       if (dir.length() == 2 && dir.at(1) == ':'){
+                                               dir.append(OS_SEPARATOR);
                                                continue;
-                    }
+                                       }
 #endif
                                        rc = makeDir(dir.constData(), logger);
                                        dir += OS_SEPARATOR;
@@ -804,7 +841,7 @@ void ReFileUtils::splitUrl(const QString& url, QString* protocol, QString* host,
 bool ReFileUtils::setPermissions(const char* filename,
                QFile::Permissions permissions, ReLogger* logger) {
        bool rc = true;
-    if (chmod(filename, nativePermissions(permissions)) != 0){
+       if (chmod(filename, nativePermissions(permissions)) != 0){
                if (logger != NULL)
                        logger->logv(LOG_ERROR, LOC_SET_TIMES_1,
                                                 "cannot change permissions (%d): $s", errno, filename);
index 1f52787d32c5a7e6f784b5f218f1d7009d076888..2571afb78ba094d597dfe49fabf8d1c51e1f0567 100644 (file)
@@ -28,12 +28,28 @@ public:
  */
 class ReFileUtils {
 public:
-       static bool deleteTree(const QString& path, bool withBase,
-               ReLogger* logger = NULL);
+       static QByteArray buildChecksum(const char* filename, QByteArray& buffer,
+                                                                       qint64* size = NULL);
+       /**
+        * Builds a checksum of the given file.
+        *
+        * @param filename      the name of the file to process
+        * @param buffer        OUT: buffer for reading the files
+        * @param sizes         OUT: size of the file content. May be NULL
+        * @return                      "": the file could not be read completely<br>
+        *                                      otherwise: the checksum
+        */
+       inline static QByteArray buildChecksum(const QString& filename,
+                               QByteArray& buffer, qint64* size = NULL){
+               return buildChecksum(I18N::s2b(filename).constData(), buffer, size);
+       }
+
        static QByteArray cleanPath(const char* path);
        static QString cleanPath(const QString& path);
        static QString copy(const QString& source, const QString& target,
                        const QFileInfo* sourceInfo, QByteArray& buffer);
+       static bool deleteTree(const QString& path, bool withBase,
+               ReLogger* logger = NULL);
        static QString extensionOf(const QString& filename);
        static QByteArray extensionOf(const char* filename);
        static QStringList findRootDirs();
index bbe07345007573d6a6325c0bab2c6efa4b03a0a8..1fc1523e07dc33617d3d09e0a83b98b9d2baeb9f 100644 (file)
@@ -51,7 +51,7 @@ ReGuiApplication::ReGuiApplication(const char* applicationName,
 /**
  * Callback method at the application's end.
  */
-void ReGuiApplication::aboutToQuit()
+void ReGuiApplication::onAboutToQuit()
 {
        m_logger.log(LOG_INFO, LOC_QUIT_1, "end");
 }
@@ -79,7 +79,7 @@ QString ReGuiApplication::fileOfHome(const QString& node)
 QString ReGuiApplication::buildHomeDir(QString homeDirBase, const QString& node){
        QString homeDir;
        if (homeDirBase.isEmpty()){
-        homeDir = ReFileUtils::nativePath(QDir::home().absoluteFilePath(node));
+               homeDir = ReFileUtils::nativePath(QDir::home().absoluteFilePath(node));
        } else if (ReFileUtils::isRootDir(homeDirBase.toLatin1().constData())){
                homeDir = homeDirBase + node;
        }
@@ -115,7 +115,7 @@ void ReGuiApplication::externalTaskFinished(const QString& message){
 void ReGuiApplication::initializeGuiElements(){
        QCoreApplication* app = QCoreApplication::instance();
        QObject::connect(app, SIGNAL(aboutToQuit()), this, SLOT(aboutToQuit()));
-       connect(m_guiTimer, SIGNAL(timeout()), this, SLOT(guiTimerUpdate()));
+       connect(m_guiTimer, SIGNAL(timeout()), this, SLOT(onGuiTimerUpdate()));
        m_guiTimer->start(100);
        statusBar()->addWidget(m_statusMessage);
 }
index e99da720c1e64f89c6b29f578865c599946e35e2..2b90c39c95802a8175f96cacfc07e2fc06666758 100644 (file)
@@ -26,7 +26,7 @@ public slots:
        /**
         * Callback method called when the application is closed.
         */
-       void aboutToQuit();
+       virtual void onAboutToQuit();
 public:
        const QByteArray& applicationName() const;
        QString fileOfHome(const QString& node);
@@ -43,7 +43,7 @@ protected slots:
        /**
         * Reads the <code>m_guiQueue</code>.
         */
-       virtual void guiTimerUpdate() = 0;
+       virtual void onGuiTimerUpdate() = 0;
 protected:
        QByteArray m_applicationName;
        /// the base directory for resources of the program like configuration file.