--- /dev/null
+#! /bin/bash
+
+mkdir /tmp/trg
+mkdir /tmp/src
+cp -a /home/bin/std/*.py /tmp/src
+mkdir /tmp/src/x
+cp -a /home/bin/std/a*.pl /tmp/src/x
+
--- /dev/null
+#! /bin/bash
+cp -a /tmp/src /tmp/trg
+rename "s/py/px/g" /tmp/trg/src/*
+touch /tmp/trg/src/*
*/
#include "backupgui.hpp"
+//#define WITH_TRACE
+#include "base/retrace.hpp"
+DECLARE_TRACER(s_traceSearch, "/tmp/bup_search.log");
+DECLARE_TRACER(s_traceBackup, "/tmp/bup_backup.log");
+DECLARE_TRACER(s_traceChecksum, "/tmp/bup_sum.log");
+DECLARE_TRACER(s_traceClean, "/tmp/bup_clean.log");
+
ReVerbose_t BackupEngine::m_verboseLevel = VerboseStandard;
bool BackupEngine::m_shouldStop = false;
QStringList BackupEngine::m_files;
m_lastRelPath(),
m_buffer()
{
-
+ FILETRACE_IT(s_traceBackup, (s_traceBackup.m_fp,
+ "=== new BackupTask (%s): %s -> %s\n",
+ name.toLocal8Bit().constData(),
+ sourceDirs.first().toLocal8Bit().constData(),
+ targetDir.toLocal8Bit().constData()));
}
/**
*/
void BackupTask::copyFile(int index, const QString& relPath,
const QString& node){
+ FILETRACE_IT(s_traceBackup, (s_traceBackup.m_fp,
+ "+%s%s\n",
+ relPath.toLocal8Bit().constData(),
+ node.toLocal8Bit().constData()));
qint64 start = QDateTime::currentMSecsSinceEpoch();
QString source = m_sourceDirs.at(index) + relPath + node;
QString targetDir = m_targetDirs.at(index) + relPath;
if (! ReFileUtils::makeDirWithParents(targetDir))
error(QObject::tr("cannot make directory (%1): %2").arg(errno).arg(targetDir));
}
- QFileInfo info(source);
+ QFileInfo sourceInfo(source);
if (m_verboseLevel >= VerboseStandard)
- m_mainWindow->addToFileList(source + " " + ReQStringUtils::readableSize(info.size()));
+ m_mainWindow->addToFileList(source + " " + ReQStringUtils::readableSize(sourceInfo.size()));
m_mutex.lock();
m_processedFiles++;
- m_processedBytes += info.size();
+ m_processedBytes += sourceInfo.size();
m_mutex.unlock();
- QString errorMsg = ReFileUtils::copy(source, target, &info, m_buffer);
+ QString errorMsg = ReFileUtils::copy(source, target, &sourceInfo, m_buffer);
if (! errorMsg.isEmpty()){
error(errorMsg);
if (m_verboseLevel >= VerboseStandard)
m_mainWindow->expandFileList(ReQStringUtils::readableDuration(
QDateTime::currentMSecsSinceEpoch() - start));
}
+ FILETRACE_IT(s_traceBackup, (s_traceBackup.m_fp,
+ "~\n",
+ relPath.toLocal8Bit().constData(),
+ node.toLocal8Bit().constData()));
}
/**
}
}
}
+ FILETRACE_IT(s_traceBackup, (s_traceBackup.m_fp,
+ "=== backup ready\n"));
m_mainWindow->externalTaskFinished(QObject::tr("backup complete after %1. Errors: %2")
.arg(ReQStringUtils::readableDuration(
QDateTime::currentMSecsSinceEpoch() - start.toMSecsSinceEpoch()))
m_dirMatcher(dirPatterns),
m_compareWithTarget(compareWithTarget)
{
+ FILETRACE_IT(s_traceSearch, (s_traceSearch.m_fp,
+ "=== new search (%s): %s -> %s\n",
+ name.toLocal8Bit().constData(),
+ sourceDirs.first().toLocal8Bit().constData(),
+ targetDir.toLocal8Bit().constData()));
}
/**
} else {
prefix = QChar(1 + index) + m_separatorString;
}
+ FILETRACE_IT(s_traceSearch, (s_traceSearch.m_fp, "=%s\n",
+ source.toLocal8Bit().constData()));
while (it.hasNext()){
if (m_shouldStop){
break;
}
it.next();
node = it.fileName();
- if (it.fileInfo().isDir()){
+ FILETRACE_IT(s_traceSearch, (s_traceSearch.m_fp, "?%s\n",
+ node.toLocal8Bit().constData()));
+ QFileInfo fileInfo = it.fileInfo();
+ if (! fileInfo.isSymLink() && fileInfo.isDir()){
// nothing to do
} else if (! m_fileMatcher.matches(node)){
m_mutex.lock();
const QFileInfo src = it.fileInfo();
if (trg.size() != src.size())
doTransfer = true;
- else if ((diff = abs(src.lastModified().toMSecsSinceEpoch()
+ else if ((diff = _abs(src.lastModified().toMSecsSinceEpoch()
- trg.lastModified().toMSecsSinceEpoch())) >= 2000)
doTransfer = true;
}
if (doTransfer){
info = prefix + it.fileName();
}
+ FILETRACE_IT(s_traceSearch, (s_traceSearch.m_fp, ">%s\n",
+ node.toLocal8Bit().constData()));
m_mutex.lock();
if (doTransfer){
m_files.append(info);
break;
}
it2.next();
-
- if (it2.fileInfo().isDir() && (node = it2.fileName()) != "." && node != ".."
+ QFileInfo fileInfo = it2.fileInfo();
+ if (! fileInfo.isSymLink() && fileInfo.isDir()
+ && (node = it2.fileName()) != "." && node != ".."
&& m_dirMatcher.matches(node)){
if (target.isEmpty())
subTarget.clear();
bool SearchTargetTask::removeOlder(const QString& directory, const QDateTime& time){
QDirIterator it(directory);
QString info, node;
+ IF_TRACE(QByteArray directory2(directory.toLocal8Bit()));
m_mutex.lock();
m_totalDirs++;
m_mutex.unlock();
+ FILETRACE_IT(s_traceSearch, (s_traceSearch.m_fp, "=removeOlder: %s\n",
+ directory2.constData()));
bool isEmpty = true;
while (it.hasNext()){
if (m_shouldStop){
}
it.next();
node = it.fileName();
- if (it.fileInfo().isDir()){
+ QFileInfo fileInfo = it.fileInfo();
+ if (! fileInfo.isSymLink() && fileInfo.isDir()){
if (node != "." && node != ".."){
if (! removeOlder(ReFileUtils::nativePath(it.filePath()), time))
isEmpty = false;
m_hotBytes += it.fileInfo().size();
m_totalFiles++;
m_mutex.unlock();
+ FILETRACE_IT(s_traceSearch, (s_traceSearch.m_fp, "-%s/%s\n",
+ directory2.constData(),
+ fileInfo.baseName().toLocal8Bit().constData()));
}else {
isEmpty = false;
}
m_matchedFiles++;
m_hotFiles++;
m_mutex.unlock();
+ FILETRACE_IT(s_traceSearch, (s_traceSearch.m_fp, "-%s\n",
+ directory2.constData()));
}
return isEmpty;
}
}
it.next();
node = it.fileName();
- if (it.fileInfo().isDir()){
+ QFileInfo fileInfo = it.fileInfo();
+ if (! fileInfo.isSymLink() && fileInfo.isDir()){
// nothing to do
} else{
Command command = CmdUndef;
if (m_shouldStop){
break;
}
- it2.next();
-
- if (it2.fileInfo().isDir() && (node = it2.fileName()) != "." && node != ".."){
+ it2.next();
+ QFileInfo fileInfo = it2.fileInfo();
+ if (! fileInfo.isSymLink() && fileInfo.isDir()
+ && (node = it2.fileName()) != "." && node != ".."){
QString newSource = source + it2.fileName();
QFileInfo src(newSource);
- if (src.exists() && src.isDir())
+ if (src.exists() && ! src.isSymLink() && src.isDir())
searchOneDirectory(ReFileUtils::nativePath(it2.filePath()),
newSource + OS_SEPARATOR_STR, index);
else{
void SearchTargetTask::moveToShadow(const QString& target, const QString& relPath, int index){
QString shadowDir = m_shadowDirs.at(index) + relPath;
ReQStringUtils::chomp(shadowDir, OS_SEPARATOR);
+ if (ReFileUtils::isDirectory(shadowDir)){
+ QString targetName = shadowDir;
+ int pos = targetName.length() / 2;
+ while(pos < shadowDir.length()){
+ char cc = '0' - 1;
+ do{
+ targetName[pos] = ++cc;
+ } while (cc <= 'Z' && QFileInfo(targetName).exists());
+ if (cc <= 'Z'){
+ if (rename(I18N::s2b(shadowDir).constData(),
+ I18N::s2b(targetName).constData()) != 0)
+ error(QObject::tr("kann Schattenverzeichnis nicht umbenennen: %1 -> %2")
+ .arg(shadowDir, targetName));
+ else break;
+ }
+ pos++;
+ }
+
+ }
if (! ReFileUtils::makeDirWithParents(shadowDir)){
error(QObject::tr("cannot create shadow directory (%1): %2")
#include <QApplication>
char** g_argv;
int main(int argc, char* argv[]) {
- g_argv = argv;
- QString homeDir = argc > 1 ? argv[1] : "";
- QApplication a(argc, argv);
- MainWindow w(a, homeDir);
- w.show();
- return a.exec();
+ g_argv = argv;
+ QString homeDir = argc > 1 ? argv[1] : "";
+ QApplication::setSetuidAllowed(true);
+ QApplication a(argc, argv);
+ MainWindow w(a, homeDir);
+ w.show();
+ return a.exec();
}
#include "aboutdialog.hpp"
#include <QFileDialog>
-const QString VERSION("2016.02.24");
+const QString VERSION("2016.04.07");
/**
* Constructor.
*/
MainWindow::MainWindow(QApplication& application, const QString& homeDir,
QWidget *parent) :
- ReGuiApplication(application, "rebackgui", homeDir, 2, 100100100, "de", parent),
+ ReGuiApplication(application, "rebackgui", homeDir, 2, 10100100, "de", parent),
ReGuiValidator(),
ui(new Ui::MainWindow),
m_configuration(this),
int countStatusMessage = 0;
clock_t start = clock();
clock_t diff;
- while(count-- > 0 || (diff = clock() - start) > CLOCKS_PER_SEC){
+ while(count-- > 0 && (diff = clock() - start) < CLOCKS_PER_SEC * 7 / 8){
m_mutexGuiQueue.lock();
ReGuiQueueItem item = m_guiQueue.popFront();
m_mutexGuiQueue.unlock();
</layout>
</item>
<item>
- <layout class="QHBoxLayout" name="horizontalLayout">
+ <layout class="QHBoxLayout" name="horizontalLayoutX1">
<item>
<widget class="QPushButton" name="pushButtonBackup">
<property name="minimumSize">
<x>0</x>
<y>0</y>
<width>839</width>
- <height>26</height>
+ <height>29</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
<property name="title">
- <string>File</string>
+ <string>Fi&le</string>
</property>
<addaction name="actionLoadConfig"/>
<addaction name="actionSaveConfig"/>
</widget>
<widget class="QMenu" name="menuEdit">
<property name="title">
- <string>Edit</string>
+ <string>Edi&t</string>
</property>
</widget>
<widget class="QMenu" name="menuHelp">
<property name="title">
- <string>Help</string>
+ <string>Hel&p</string>
</property>
<addaction name="actionAbout"/>
<addaction name="separator"/>
</widget>
<widget class="QMenu" name="menuAction">
<property name="title">
- <string>Action</string>
+ <string>A&ction</string>
</property>
<addaction name="actionStart"/>
<addaction name="actionStop"/>
<widget class="QStatusBar" name="statusBar"/>
<action name="actionLoadConfig">
<property name="text">
- <string>Load Config</string>
+ <string>&Load Config</string>
</property>
</action>
<action name="actionSaveConfig">
<property name="text">
- <string>Save Config</string>
+ <string>&Save Config</string>
</property>
</action>
<action name="actionExit">
<property name="text">
- <string>Exit</string>
+ <string>&Exit</string>
</property>
</action>
<action name="actionStart">
<property name="text">
- <string>Start backup</string>
+ <string>&Start backup</string>
</property>
</action>
<action name="actionStop">
<property name="text">
- <string>Stop</string>
+ <string>St&op</string>
</property>
</action>
<action name="actionAbout">
<property name="text">
- <string>About</string>
+ <string>&About</string>
</property>
</action>
<action name="actionChecksums">
<property name="text">
- <string>Checksums</string>
+ <string>&Checksums</string>
</property>
<property name="toolTip">
<string>Calculates checksums for source and target files and reports differences</string>
</action>
<action name="actionClean">
<property name="text">
- <string>Clean</string>
+ <string>C&lean</string>
</property>
</action>
<action name="actionEnglish">
<bool>true</bool>
</property>
<property name="text">
- <string>English</string>
+ <string>&English</string>
</property>
</action>
<action name="actionGerman">
<bool>true</bool>
</property>
<property name="text">
- <string>German</string>
+ <string>&German</string>
</property>
</action>
<action name="actionAutosave">
<bool>true</bool>
</property>
<property name="text">
- <string>Autosave</string>
+ <string>&Autosave</string>
</property>
</action>
</widget>
</message>
<message>
<location filename="mainwindow.ui" line="25"/>
- <location filename="mainwindow.ui" line="818"/>
<source>Action</source>
<translation>Aktion</translation>
</message>
</message>
<message>
<location filename="mainwindow.ui" line="226"/>
- <location filename="mainwindow.ui" line="861"/>
<source>Stop</source>
<translation>Beenden</translation>
</message>
</message>
<message>
<location filename="mainwindow.ui" line="255"/>
- <location filename="mainwindow.ui" line="871"/>
<source>Checksums</source>
<translation>Prüfsummen</translation>
</message>
</message>
<message>
<location filename="mainwindow.ui" line="271"/>
- <location filename="mainwindow.ui" line="879"/>
<source>Clean</source>
<translatorcomment>Protokoll:</translatorcomment>
<translation>Bereinigen</translation>
</message>
<message>
<location filename="mainwindow.ui" line="462"/>
- <location filename="mainwindow.ui" line="903"/>
<source>Autosave</source>
<translation>Autom. Speichern</translation>
</message>
</message>
<message>
<location filename="mainwindow.ui" line="793"/>
- <source>File</source>
+ <source>Fi&le</source>
+ <oldsource>File</oldsource>
<translatorcomment>Bearbeiten</translatorcomment>
- <translation>Datei</translation>
+ <translation type="unfinished">Datei</translation>
</message>
<message>
<location filename="mainwindow.ui" line="804"/>
- <source>Edit</source>
- <translation>Bearbeiten</translation>
+ <source>Edi&t</source>
+ <oldsource>Edit</oldsource>
+ <translation type="unfinished">Bearbeiten</translation>
</message>
<message>
<location filename="mainwindow.ui" line="809"/>
- <source>Help</source>
- <translation>Hilfe</translation>
+ <source>Hel&p</source>
+ <oldsource>Help</oldsource>
+ <translation type="unfinished">Hilfe</translation>
</message>
<message>
<location filename="mainwindow.ui" line="841"/>
- <source>Load Config</source>
- <translation>Konfiguration laden</translation>
+ <source>&Load Config</source>
+ <oldsource>Load Config</oldsource>
+ <translation type="unfinished">Konfiguration laden</translation>
</message>
<message>
<location filename="mainwindow.ui" line="846"/>
- <source>Save Config</source>
- <translation>Konfiguration sichern</translation>
+ <source>&Save Config</source>
+ <oldsource>Save Config</oldsource>
+ <translation type="unfinished">Konfiguration sichern</translation>
</message>
<message>
<location filename="mainwindow.ui" line="851"/>
- <source>Exit</source>
- <translation>Beenden</translation>
+ <source>&Exit</source>
+ <oldsource>Exit</oldsource>
+ <translation type="unfinished">Beenden</translation>
</message>
<message>
<location filename="mainwindow.ui" line="856"/>
- <source>Start backup</source>
- <translation>Sicherung starten</translation>
+ <source>&Start backup</source>
+ <oldsource>Start backup</oldsource>
+ <translation type="unfinished">Sicherung starten</translation>
</message>
<message>
<location filename="mainwindow.ui" line="866"/>
- <source>About</source>
- <translation>Ãœber</translation>
+ <source>&About</source>
+ <oldsource>About</oldsource>
+ <translation type="unfinished">Ãœber</translation>
</message>
<message>
<location filename="mainwindow.ui" line="887"/>
- <source>English</source>
- <translation></translation>
+ <source>&English</source>
+ <oldsource>English</oldsource>
+ <translation type="unfinished"></translation>
</message>
<message>
<location filename="mainwindow.ui" line="895"/>
- <source>German</source>
- <translation></translation>
+ <source>&German</source>
+ <oldsource>German</oldsource>
+ <translation type="unfinished"></translation>
</message>
<message>
- <location filename="mainwindow.cpp" line="359"/>
- <location filename="mainwindow.cpp" line="393"/>
- <location filename="mainwindow.cpp" line="583"/>
+ <location filename="mainwindow.cpp" line="361"/>
+ <location filename="mainwindow.cpp" line="395"/>
+ <location filename="mainwindow.cpp" line="588"/>
<source>no backup item selected</source>
<translation>Kein Sicherungselement ausgewählt</translation>
</message>
<message>
- <location filename="mainwindow.cpp" line="364"/>
- <location filename="mainwindow.cpp" line="398"/>
- <location filename="mainwindow.cpp" line="588"/>
+ <location filename="mainwindow.cpp" line="366"/>
+ <location filename="mainwindow.cpp" line="400"/>
+ <location filename="mainwindow.cpp" line="593"/>
<source>Target not available</source>
<translation>Ziel nicht vorhanden</translation>
</message>
<message>
- <location filename="mainwindow.cpp" line="303"/>
+ <location filename="mainwindow.cpp" line="305"/>
<source>Select Source Directory</source>
<translation>Quellverzeichnis auswählen</translation>
</message>
<message>
- <location filename="mainwindow.cpp" line="566"/>
+ <location filename="mainwindow.cpp" line="568"/>
<source>Select Target Directory</source>
<translation>Zielverzeichnis auswählen</translation>
</message>
<message>
- <location filename="mainwindow.cpp" line="572"/>
+ <location filename="mainwindow.cpp" line="574"/>
<source>target initialized with %1</source>
<translation>Ziel mit %1 vorbelegt</translation>
</message>
<translation>Suche beendet: zu behandeln: %1 mit %2 passend: %3 gesamt: %4 Unterverz: %5 Laufzeit: %6</translation>
</message>
<message>
- <location filename="BackupEngine.cpp" line="888"/>
+ <location filename="BackupEngine.cpp" line="897"/>
+ <source>kann Schattenverzeichnis nicht umbenennen: %1 -> %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="BackupEngine.cpp" line="907"/>
<source>cannot create shadow directory (%1): %2</source>
<translation>Kann Schattenverzeichnis nicht erstellen (%1): %2</translation>
</message>
<message>
- <location filename="BackupEngine.cpp" line="893"/>
+ <location filename="BackupEngine.cpp" line="912"/>
<source>cannot move to shadow directory (%1): %2 -> %3</source>
<translation>Kann Datei nicht in Schattenverzeichnis verschieben (%1): %2 -> %3</translation>
</message>
<message>
- <location filename="BackupEngine.cpp" line="918"/>
+ <location filename="BackupEngine.cpp" line="937"/>
<source>Search in target finished: to process: %1 with %2 dirs to delete: %3 total: %4 subdirs: %5 runtime: %6</source>
<translation>Suche im Ziel bendet: zu behandeln: %1 mit %2 zu löschende Verzeichnisse: %3 gesamt: %4 Unterverz.: %5 Laufzeit: %6</translation>
</message>
</message>
<message>
<location filename="Configuration.cpp" line="119"/>
- <location filename="Configuration.cpp" line="206"/>
- <location filename="../../base/ReFileUtils.cpp" line="181"/>
- <location filename="../../base/ReFileUtils.cpp" line="185"/>
+ <location filename="Configuration.cpp" line="205"/>
+ <location filename="../../base/ReFileUtils.cpp" line="191"/>
+ <location filename="../../base/ReFileUtils.cpp" line="195"/>
<source>cannot open (%1): %2</source>
<translation>Kann nicht öffnen (%1): %2</translation>
</message>
<translation>Unbekanntes Format in %1-%2: %3</translation>
</message>
<message>
- <location filename="Configuration.cpp" line="227"/>
+ <location filename="Configuration.cpp" line="226"/>
<source>cannot write (%1): %2</source>
<translation>Kann nicht schreiben (%1): %2</translation>
</message>
<message>
- <location filename="Configuration.cpp" line="231"/>
+ <location filename="Configuration.cpp" line="230"/>
<source>configuration saved: %1</source>
<oldsource>configuration changed: %1</oldsource>
<translation>Konfiguration gespeichert: %1</translation>
</message>
<message>
- <location filename="mainwindow.cpp" line="242"/>
+ <location filename="mainwindow.cpp" line="244"/>
<source>Search started...</source>
<translation>Suche gestartet...</translation>
</message>
<message>
- <location filename="mainwindow.cpp" line="763"/>
+ <location filename="mainwindow.cpp" line="782"/>
<source>yes</source>
<translation>Ja</translation>
</message>
<message>
- <location filename="mainwindow.cpp" line="763"/>
+ <location filename="mainwindow.cpp" line="782"/>
<source>no</source>
<translation>nein</translation>
</message>
<message>
- <location filename="../../base/ReFileUtils.cpp" line="177"/>
+ <location filename="../../base/ReFileUtils.cpp" line="187"/>
<source>not found: %1</source>
<translation>Nicht gefunden: %1</translation>
</message>
<translation type="obsolete">Kann nicht schreiben(%1): %2 [%3/%4]</translation>
</message>
<message>
- <location filename="../../base/ReFileUtils.cpp" line="202"/>
+ <location filename="../../base/ReFileUtils.cpp" line="176"/>
+ <source>copy file failed (%1): %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../base/ReFileUtils.cpp" line="212"/>
<source>cannot write (%1): %2 [%3/%4]</source>
<translation>Kann nicht schreiben(%1): %2 [%3/%4]</translation>
</message>
<message>
- <location filename="../../base/ReFileUtils.cpp" line="209"/>
+ <location filename="../../base/ReFileUtils.cpp" line="219"/>
<source>file can be read only partitionally: %1 [%2/%3]</source>
<translation>Datei kann nur teilweise gelesen werden: %1 [%2/%3]</translation>
</message>
<message>
- <location filename="../../base/ReFileUtils.cpp" line="214"/>
+ <location filename="../../base/ReFileUtils.cpp" line="224"/>
<source>cannot set date/time (%1): %2</source>
<translation>Kann Datum/Zeit nicht setzen (%1): %2</translation>
</message>
<message>
- <location filename="../../base/ReFileUtils.cpp" line="217"/>
+ <location filename="../../base/ReFileUtils.cpp" line="227"/>
<source>cannot set permissions (%1): %2</source>
<translation>Kann Dateirechte nicht setzen (%1): %2</translation>
</message>
<message>
- <location filename="../../base/ReFileUtils.cpp" line="491"/>
+ <location filename="../../base/ReFileUtils.cpp" line="569"/>
<source>can't create directory (%1): %2</source>
<translation>Kann Verzeichnis nicht erzeugen (%1): %2</translation>
</message>
<message>
- <location filename="../../base/ReFileUtils.cpp" line="497"/>
+ <location filename="../../base/ReFileUtils.cpp" line="575"/>
<source>can't create directory (is a file): </source>
<translation>Kann Verzeichnis nicht erzeugen (ist eine Datei): </translation>
</message>
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<style>#doc_header { font-size: 3em; color: blue; }
+h1 { border-bottom: 1px solid red; }
+h2 { border-bottom: 1px solid blue; }
+h3 { border-bottom: 1px solid black; }
+</style>
+<body>
+<div id="doc_header">Documentation of rebackgui</div>
+<h1>Release Notes</h1>
+<p>
+<ul>
+<li>v2016.04.06<br/>
+ <ul><li>Basic version</li>
+ </ul>
+</li>
+</ul>
+</p>
+
+<h1>User Manual</h1>
+<h2>Purpose:</h2>
+<p>This program makes backups.</p>
+
+<h2>Usage</h2>
+<pre>hmbackgui [<home_dir>]
+<home_dir>:
+ the base directory for the configuration directory ".reappl"
+ Default: the home directory of the user
+</pre>
+<p>Example:</p>
+<pre>hmbackgui /home/hugo
+</pre>
+
+<h2>Installation</h2>
+<h3>Linux</h3>
+<p>
+<ul><li>Copy rebackgui to /usr/local/bin</li>
+<li>Copy rebackgui.*.qm to the configuration directory, e.g. /home/hm/.reappl</li>
+</ul>
+<pre><code>cd /usr/local/bin
+chown root.root rebackgui
+chmod u+s rebackgui
+</code></pre>
+</p>
+
+<h2>Configuration</h2>
+<p>The configuration is stored in <home_dir>/.rebackgui/rebackgui.conf
+</p>
+
+<h1>Program Documentation</h1>
+
+<h2>Programming Features</h2>
+
+<p>This is a example for a complete QT application with the following features:
+<ul>
+<li>multithreaded</li>
+<li>pattern matching</li>
+<li>multithreaded GUI building</li>
+</ul>
+</p>
+
+<h1>(Un)License: Public Domain</h1>
+
+<p>You can use and modify this file without any restriction.<br/>
+Do what you want.<br/>
+No warranties and disclaimer of any damages.<br/>
+More info: <a href="http://unlicense.org">http://unlicense.org</a><br/>
+The latest sources: <a href="https://github.com/republib">https://github.com/republib</a>.
+</p>
+
+</body>
+</html>
DISTFILES += \
ReBackGui.html \
- osconnect.pl
+ osconnect.pl \
+ rebackgui.html
TRANSLATIONS = rebackgui.de.ts
CODECFORTR = UTF-8
../../gui/ReGuiQueue.cpp \
../../gui/ReGuiUtils.cpp \
mainwindow.cpp \
- CommandProcessor.cpp
+ CommandProcessor.cpp \
+ ../../base/ReProcess.cpp
HEADERS += mainwindow.hpp \
CommandProcessor.hpp \
#ifndef RECONFIG_HPP
#define RECONFIG_HPP
-class ReConfig: public ReConfigurator, protected QHash<QByteArray, QByteArray> {
+class ReConfig: public ReConfigurator, public QHash<QByteArray, QByteArray> {
public:
ReConfig(const char* file = NULL, bool readOnly = true, ReLogger* logger =
NULL);
* @param sizeHint Probable length of the container
*/
ReContainer::ReContainer(size_t sizeHint) :
- m_data(""),
- m_countBags(0),
- m_typeList(""),
- m_ixItem(0),
- m_ixBag(0),
- m_readPosition(NULL) {
+ m_data(""),
+ m_countBags(0),
+ m_typeList(""),
+ m_ixItem(0),
+ m_ixBag(0),
+ m_readPosition(NULL) {
if (sizeHint > 0)
m_data.reserve(sizeHint);
}
char buffer[128];
// RPL&1 0a b5[2]cis: !12
qsnprintf(buffer, sizeof buffer, "%x[%d]%s:",
- (unsigned int) m_data.length(), m_countBags, m_typeList.data());
+ (unsigned int) m_data.length(), m_countBags, m_typeList.data());
char header[128 + 8];
qsnprintf(header, sizeof header, "%s%02x%s", MAGIC_1,
- (unsigned int) strlen(buffer), buffer);
+ (unsigned int) strlen(buffer), buffer);
m_data.insert(0, header);
}
return m_data;
const char* ptr = m_data.data();
if (strncmp(ptr, MAGIC_1, strlen(MAGIC_1)) != 0)
throw RplInvalidDataException(LOG_ERROR, LOC_FILL_1,
- "container has no magic", data.data(), data.length());
+ "container has no magic", data.data(), data.length());
ptr += strlen(MAGIC_1);
unsigned int headerSize = 0;
if (sscanf(ptr, "%02x", &headerSize) != 1)
throw RplInvalidDataException(LOG_ERROR, LOC_FILL_2,
- "container has no header size", ptr, 2);
+ "container has no header size", ptr, 2);
ptr += 2;
unsigned int dataSize = 0;
unsigned int countBags = 0;
if (sscanf(ptr, "%x[%x]", &dataSize, &countBags) != 2)
throw RplInvalidDataException(LOG_ERROR, LOC_FILL_2,
- "container has no data_size[bag_count]", ptr, 16);
+ "container has no data_size[bag_count]", ptr, 16);
m_countBags = countBags;
ptr = strchr(ptr, ']') + 1;
const char* end = ptr + strspn(ptr, "cisdDX!");
if (end == ptr || *end != ':') {
throw RplInvalidDataException(LOG_ERROR, LOC_FILL_2,
- "container has no valid typelist", ptr, 16);
+ "container has no valid typelist", ptr, 16);
}
m_typeList.clear();
m_typeList.append(ptr, end - ptr);
void ReContainer::nextBag() {
if (m_ixItem < m_typeList.length() && m_ixItem != -1)
throw ReException(LOG_ERROR, LOC_NEXT_BAG_1, NULL,
- "end of bag not reached: remaining items: %s",
- m_typeList.data() + m_ixItem);
+ "end of bag not reached: remaining items: %s",
+ m_typeList.data() + m_ixItem);
m_ixItem = 0;
m_ixBag++;
if (m_ixBag >= m_countBags)
throw ReException(LOG_ERROR, LOC_NEXT_BAG_2, NULL, "no more bags: %d",
- m_ixBag);
+ m_ixBag);
}
/**
* @brief Sets the next item.
}
if (m_ixItem >= m_typeList.length())
throw ReException(LOG_ERROR, LOC_NEXT_ITEM_1, ReLogger::globalLogger(),
- "no more items in the bag");
+ "no more items in the bag");
type_tag_t current = (type_tag_t) m_typeList.at(m_ixItem);
// Unify all data types:
if (current == TAG_DATA4G || current == TAG_DATA64K)
current = TAG_DATA255;
if (current != expected)
throw ReException(LOG_ERROR, LOC_NEXT_ITEM_2, NULL,
- "current item is a %c, not a %c", (char) m_typeList.at(m_ixItem),
- (char) expected);
+ "current item is a %c, not a %c", (char) m_typeList.at(m_ixItem),
+ (char) expected);
m_ixItem++;
if (m_readPosition > (uint8_t*) (m_data.data() + m_data.length()))
throw ReException(LOG_ERROR, LOC_NEXT_ITEM_3, NULL,
- "container size too small. Bag: %d of %d Item: %d of %d",
- 1 + m_ixBag, m_countBags, 1 + m_ixItem, m_typeList.length());
+ "container size too small. Bag: %d of %d Item: %d of %d",
+ 1 + m_ixBag, m_countBags, 1 + m_ixItem, m_typeList.length());
}
/**
unsigned int value = 0;
if (sscanf((const char*) m_readPosition, "%x ", &value) != 1)
throw RplInvalidDataException(LOG_ERROR, LOC_NEXT_INT_1,
- "not a hex_number<blank>", m_readPosition, 16);
+ "not a hex_number<blank>", m_readPosition, 16);
m_readPosition = (uint8_t*) strchr((const char*) m_readPosition, ' ') + 1;
if (isNegativ)
value = -value;
bool isNegativ = *m_readPosition == '-';
if (isNegativ)
m_readPosition++;
- uint64_t value = 0;
+ quint64 value = 0;
if (sscanf((const char*) m_readPosition, "%llx ", &value) != 1)
throw RplInvalidDataException(LOG_ERROR, LOC_NEXT_INT_1,
- "not a hex_number<blank>", m_readPosition, 16);
+ "not a hex_number<blank>", m_readPosition, 16);
m_readPosition = (uint8_t*) strchr((const char*) m_readPosition, ' ') + 1;
if (isNegativ)
value = -value;
* @return a human readable string describing the container
*/
QByteArray ReContainer::dump(const char* title, int maxBags,
- int maxStringLength, int maxBlobLength, char separatorItems) {
+ int maxStringLength, int maxBlobLength, char separatorItems) {
QByteArray rc;
rc.reserve(64000);
rc.append("=== ").append(title).append('\n');
maxBags = m_countBags;
for (int ixBag = 0; ixBag < maxBags; ixBag++) {
rc.append("--- bag ").append(ReStringUtils::toNumber(ixBag)).append(
- ":\n");
+ ":\n");
nextBag();
QByteArray item;
int maxLength;
case TAG_INT:
iValue = nextInt();
rc.append(" i: ").append(ReStringUtils::toNumber(iValue)).append(
- " / ");
+ " / ");
rc.append(ReStringUtils::toNumber(iValue, "%x")).append(
- separatorItems);
+ separatorItems);
break;
case TAG_STRING:
sValue = nextString();
rc.append(' ').append((char) currentType).append(": [");
rc.append(ReStringUtils::toNumber(item.length())).append("] ");
maxLength =
- item.length() < maxBlobLength ?
- item.length() : maxBlobLength;
+ item.length() < maxBlobLength ?
+ item.length() : maxBlobLength;
rc.append(ReStringUtils::hexDump(item.data(), maxLength, 16))
- .append(separatorItems);
+ .append(separatorItems);
break;
default:
break;
LOC_MAKE_DIR_2, // 12506
LOC_SET_TIMES_2, // 12507
};
+int ReFileUtils::m_maxCharSet = 128;
QDateTime ReFileUtils::m_undefinedTime;
* @param source full path of the source file
* @param target full path of the target name
* @param sourceInfo NULL or the info about the surce
- * @param buffer IN/OUT: used as
+ * @param buffer IN/OUT: used for buffering the file content
+ *
* @return
*/
QString ReFileUtils::copy(const QString& source, const QString& target,
- const QFileInfo* sourceInfo, QByteArray& buffer){
+ const QFileInfo* sourceInfo, QByteArray& buffer,
+ bool setUser){
QString rc;
#if defined _WIN32
const ushort* src = source.utf16();
sourceInfo2.setFile(source);
sourceInfo = &sourceInfo2;
}
- if (! sourceInfo->exists())
+ if (! sourceInfo->exists()){
rc = QObject::tr("not found: %1").arg(source);
- else {
+ } else if (sourceInfo->isSymLink()){
+ // sourceInfo->symLink() returns the absolute path for relative links too.
+ // Though we use readlink():
+ size_t size = buffer.capacity();
+ if (size < 4096){
+ buffer.resize(4096);
+ size = buffer.capacity();
+ }
+ int length = readlink(source2.constData(), buffer.data(), size);
+ buffer.resize(length);
+ QString link = I18N::s2b(buffer);
+ QFile::link(link, target);
+ } else if (! sourceInfo->isFile()){
+ rc = QObject::tr("not a regular file: %1").arg(source);
+ } else {
FILE* fpSource = fopen(source2.constData(), "rb");
if (fpSource == NULL){
rc = QObject::tr("cannot open (%1): %2").arg(errno).arg(source);
}
}
fclose(fpSource);
+#ifdef __linux__
+ if (setUser){
+ if (chown(target2.constData(), sourceInfo->ownerId(),
+ sourceInfo->groupId()) != 0 && rc.isEmpty()){
+ rc = QObject::tr("cannot set user/gid (%1): %2")
+ .arg(errno).arg(target);
+ }
+ }
+#endif
}
}
#endif
return rc;
}
+/**
+ * Modify a filename until the file does not exist.
+ *
+ * @param filename filename to modify
+ * @return "": no unused filename found<br>
+ * otherwise: a modification of filename which does not exist
+ */
+QString ReFileUtils::modifyToNonExisting(const QString& path){
+ static const char* charSet = "$_-+=!^#%~01234567890abcdefghijklmnopqrstuvwxyz";
+ static int charSetLength = 0;
+ static char indexOf[128] = { 0 };
+ if (charSetLength == 0){
+ charSetLength = min(m_maxCharSet, strlen(charSet));
+ memset(indexOf, charSetLength, sizeof indexOf);
+ for (int ix = 0; ix < charSetLength; ix++){
+ indexOf[(int) charSet[ix]] = ix;
+ }
+ }
+ QString rc = path;
+ int ix;
+ // begin is the first index of the filename (without path)
+ int begin = 0;
+ // last is the last index of the filename (without extension)
+ int last = -1;
+ for (ix = rc.length() - 1; ix >= 0; ix--){
+ if (rc.at(ix) == OS_SEPARATOR || rc.at(ix) == OS_2nd_SEPARATOR){
+ begin = ix - 1;
+ if (last < 0)
+ last = rc.length() - 1;
+ break;
+ } else if (last < 0 && rc.at(ix) == '.')
+ last = ix + 1;
+ }
+ if (last <= 0)
+ last = rc.length() - 1;
+ // first is the index of the first modified character in the filename
+ int first = last + 1;
+ while (first <= begin && QFile(rc).exists()){
+ if (first < last){
+ first++;
+ rc[last] = charSet[0];
+ } else if ( (ix = indexOf[rc.at(last).unicode()]) < charSetLength - 1){
+ rc[last] = charSet[ix + 1];
+ } else {
+ int pos = last;
+ rc[last] = charSet[0];
+ while(true){
+ if (--pos < begin){
+ first = begin + 1;
+ rc = "";
+ break;
+ }
+ if (pos > first){
+ first++;
+ rc[first] = charSet[0];
+ break;
+ } else if ( (ix = indexOf[rc.at(pos).unicode()]) < charSetLength - 1){
+ rc[pos] = charSet[ix + 1];
+ break;
+ } else {
+ rc[pos] = charSet[0];
+ }
+ }
+ }
+ }
+ return rc;
+}
/**
* Returns whether a path is an absolute path.
*
*
* @param path full name of the directory to inspect
* @param isFile OUT: <code>true</code>: this is a file (and not a directory)
- * @return <code>true</code>: path is a directory
+ * @return <code>true</code>: path is a directory (and not a symbolic
+ * link)
*/
bool ReFileUtils::isDirectory(const QString& path, bool* isFile)
{
QFileInfo info(path);
bool rc = info.exists();
if (rc){
- if (! info.isDir())
+ if (info.isSymLink() || ! info.isDir())
rc = false;
if (isFile != NULL)
*isFile = ! rc;
return rc;
}
-/**
- * Splits an URL into its parts.
- *
- * Examples:
- * <pre>url: "file:///path/name.ext"
- * protocol: "file:" host: "//" path: "/path/" node: "name.ext" param: ""
- *
- * url: "http://b2.de/xy/index.htm?id=1"
- * protocol: "http:" host: "//b2.de" path: "/xy/" node: "index.htm" param: "?id=1"
- * </pre>
- *
- * @param url the URL to split
- * @param protocol OUT: NULL or the protocol
- * @param host OUT: NULL or the host part
- * @param path OUT: NULL or the path without the last node
- * @param node OUT: NULL or the last node of the path
- * @param params OUT: NULL or the parameters (e.g. at http)
- */
-void ReFileUtils::splitUrl(const QString& url, QString* protocol, QString* host,
- QString* path, QString* node, QString* params){
- if (protocol != NULL)
- *protocol = ReQStringUtils::m_empty;
- if (host != NULL)
- *host = ReQStringUtils::m_empty;
- if (path != NULL)
- *path = ReQStringUtils::m_empty;
- if (params != NULL)
- *params = ReQStringUtils::m_empty;
- int ix = url.indexOf(':');
- if (ix < 0){
- ix = 0;
- } else {
- ix++;
- if (protocol != NULL)
- *protocol = url.mid(0, ix);
- }
- int start = ix;
- if (url.length() >= start + 2 && url.at(start) == '/' && url.at(start + 1) == '/'){
- ix = url.indexOf("/", start + 2);
- if (ix < 0)
- ix = start + 2;
- if (host != NULL)
- *host = url.mid(start, ix - start);
- start = ix;
- }
- ix = url.lastIndexOf(OS_SEPARATOR);
- if (ix < 0)
- ix = url.lastIndexOf(OS_2nd_SEPARATOR);
- if (ix >= 0){
- ix++;
- if (path != NULL)
- *path = url.mid(start, ix - start);
- start = ix;
- }
- ix = url.indexOf('?', start);
- if (ix < 0){
- if (node != NULL)
- *node = url.mid(start);
- } else {
- if (node != NULL)
- *node = url.mid(start, ix - start);
- if (params != NULL)
- *params = url.mid(ix);
- }
-}
-
/**
* Sets the permissions.
*
bool ReFileUtils::setPermissions(const char* filename,
QFile::Permissions permissions, ReLogger* logger) {
bool rc = true;
- if (chmod(filename, nativePermissions(permissions)) != 0){
+ if (! QFile::setPermissions(QString(filename), permissions)){
if (logger != NULL)
logger->logv(LOG_ERROR, LOC_SET_TIMES_1,
- "cannot change permissions (%d): $s", errno, filename);
+ "cannot change permissions (%d): %s", errno, filename);
rc = false;
}
return rc;
return rc;
}
+/**
+ * Splits an URL into its parts.
+ *
+ * Examples:
+ * <pre>url: "file:///path/name.ext"
+ * protocol: "file:" host: "//" path: "/path/" node: "name.ext" param: ""
+ *
+ * url: "http://b2.de/xy/index.htm?id=1"
+ * protocol: "http:" host: "//b2.de" path: "/xy/" node: "index.htm" param: "?id=1"
+ * </pre>
+ *
+ * @param url the URL to split
+ * @param protocol OUT: NULL or the protocol
+ * @param host OUT: NULL or the host part
+ * @param path OUT: NULL or the path without the last node
+ * @param node OUT: NULL or the last node of the path
+ * @param params OUT: NULL or the parameters (e.g. at http)
+ */
+void ReFileUtils::splitUrl(const QString& url, QString* protocol, QString* host,
+ QString* path, QString* node, QString* params){
+ if (protocol != NULL)
+ *protocol = ReQStringUtils::m_empty;
+ if (host != NULL)
+ *host = ReQStringUtils::m_empty;
+ if (path != NULL)
+ *path = ReQStringUtils::m_empty;
+ if (params != NULL)
+ *params = ReQStringUtils::m_empty;
+ int ix = url.indexOf(':');
+ if (ix < 0){
+ ix = 0;
+ } else {
+ ix++;
+ if (protocol != NULL)
+ *protocol = url.mid(0, ix);
+ }
+ int start = ix;
+ if (url.length() >= start + 2 && url.at(start) == '/' && url.at(start + 1) == '/'){
+ ix = url.indexOf("/", start + 2);
+ if (ix < 0)
+ ix = start + 2;
+ if (host != NULL)
+ *host = url.mid(start, ix - start);
+ start = ix;
+ }
+ ix = url.lastIndexOf(OS_SEPARATOR);
+ if (ix < 0)
+ ix = url.lastIndexOf(OS_2nd_SEPARATOR);
+ if (ix >= 0){
+ ix++;
+ if (path != NULL)
+ *path = url.mid(start, ix - start);
+ start = ix;
+ }
+ ix = url.indexOf('?', start);
+ if (ix < 0){
+ if (node != NULL)
+ *node = url.mid(start);
+ } else {
+ if (node != NULL)
+ *node = url.mid(start, ix - start);
+ if (params != NULL)
+ *params = url.mid(ix);
+ }
+}
+
/**
* Returns the current file position.
*
return rc;
}
+
/**
* @brief Returns the name of a directory in the temp dir.
*
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);
+ const QFileInfo* sourceInfo, QByteArray& buffer,
+ bool setUser = true);
static bool deleteTree(const QString& path, bool withBase,
ReLogger* logger = NULL);
static QString extensionOf(const QString& filename);
static bool isAbsolutPath(const char* path);
static bool isDirectory(const QString& path, bool* isFile = NULL);
static bool isRootDir(const char* path);
+ static QString modifyToNonExisting(const QString& filename);
static mode_t nativePermissions(QFile::Permissions permissions);
/** Returns a path with native separators.
* QT under windows can operator with 2 separators: '\\' and '/'.
size_t contentLength = (size_t) - 1, const char* mode = "w");
public:
static QDateTime m_undefinedTime;
+ /// see modifyToNonExisting()
+ static int m_maxCharSet;
};
#endif // REFILEUTILS_HPP
return log(level, location, buffer);
}
-/**
- * @brief Logs (or not) the calling location.
- *
- * @param level the level of the location
- * @param location an unique identifier of the location
- * @param format the logging message with placeholders (like printf).
- * @param ... the values of the placeholders (varargs)
- * @return true: for chaining
- */
-bool ReLogger::logv(ReLoggerLevel level, int location, const QByteArray& format,
- ...) {
- char buffer[64000];
- va_list ap;
- va_start(ap, format);
- qvsnprintf(buffer, sizeof buffer, format, ap);
- va_end(ap);
- return log(level, location, buffer);
-}
/**
* @brief Logs (or not) the calling location.
m_currentSize(0),
m_currentNo(0),
m_fp(NULL) {
- open();
+ findLastNo();
+ reopen(true);
}
/**
}
}
+/**
+ * Finds the largest number of all files with the given pattern.
+ */
+void ReFileAppender::findLastNo()
+{
+ QFileInfo info(m_prefix);
+ QDir parent = info.dir();
+ QStringList patterns;
+ QString basename = info.baseName();
+ patterns << basename + ".*.log";
+ QStringList files = parent.entryList(patterns);
+ m_currentNo = 0;
+ int prefixLength = basename.length() + 1;
+ QString lastFile;
+ int maxNo = 0;
+ for (int ix = 0; ix < files.count(); ix++){
+ int no = atoi(files.at(ix).mid(prefixLength).toLatin1().constData());
+ if (no > maxNo){
+ m_currentNo = no;
+ lastFile = files.at(ix);
+ }
+ }
+ qint64 size = QFile(parent.absoluteFilePath(lastFile)).size();
+ if (size < m_maxSize)
+ m_currentNo--;
+ QByteArray fullName;
+ for (int ix = m_currentNo; ix > 0 && ix > m_currentNo - 10; ix--){
+ fullName = nameOf(ix);
+ unlink(fullName);
+ }
+}
+
+/**
+ * Returns the full logfile name of a given number.
+ *
+ * @param no the number of the logfile
+ * @return the full name of the logfile
+ */
+QByteArray ReFileAppender::nameOf(int no){
+ char rc[512];
+ qsnprintf(rc, sizeof rc, "%s.%03d.log", m_prefix.data(), no);
+ return rc;
+}
+
/**
* @brief Opens the next log file.
+ *
+ * @param first <code>true</code>: the first open, file should be appended
*/
-void ReFileAppender::open() {
+void ReFileAppender::reopen(bool isFirst) {
if (m_fp != NULL)
fclose(m_fp);
- char fullName[512];
- qsnprintf(fullName, sizeof fullName, "%s.%03d.log", m_prefix.data(),
- ++m_currentNo);
- m_fp = fopen(fullName, "a");
+ QByteArray fullName = nameOf(++m_currentNo);
+ m_fp = fopen(fullName, isFirst ? "a" : "w");
if (m_fp == NULL)
- fprintf(stderr, "cannot open: %s\n", fullName);
+ fprintf(stderr, "cannot open: %s\n", fullName.constData());
else {
- //@ToDo
- m_currentSize = 0;
+ m_currentSize = isFirst ? QFile(fullName).size() : 0;
+ }
+ if (m_currentNo - m_maxCount > 0){
+ fullName = nameOf(m_currentNo - m_maxCount);
+ unlink(fullName.constData());
}
}
fputs(message, m_fp);
fputc('\n', m_fp);
fflush(m_fp);
+ const int NEWLINE_LENGTH
+#ifdef __linux__
+ = 1;
+#else
+ = 2;
+#endif
+ m_currentSize += prefix.length() + strlen(message) + NEWLINE_LENGTH;
+ if (m_currentSize >= m_maxSize)
+ reopen(false);
}
}
bool log(ReLoggerLevel level, int location, const QByteArray& message);
bool log(ReLoggerLevel level, int location, const ReString& message);
bool logv(ReLoggerLevel level, int location, const char* format, ...);
- bool logv(ReLoggerLevel level, int location, const QByteArray& format, ...);
bool log(ReLoggerLevel level, int location, const char* format,
va_list& varlist);
void addAppender(ReAppender* appender);
const char* appenderName = "FileAppender");
virtual ~ReFileAppender();
public:
- void open();
+ void findLastNo();
+ void reopen(bool first);
virtual void log(ReLoggerLevel level, int location, const char* message,
ReLogger* logger);
+ QByteArray nameOf(int no);
private:
// prefix of the log file name. Will be appended by ".<no>.log"
QByteArray m_prefix;
if (mergeStdError)
process.setProcessChannelMode(QProcess::MergedChannels);
process.start(program, args, QIODevice::ReadOnly);
- process.waitForFinished();
+ process.waitForFinished(timeout * 1000);
QByteArray rc = process.readAllStandardOutput();
QString error = process.errorString();
return rc;
else
return source.toLocal8Bit();
}
+ /** Converts a <code>QString</code> into a <code>QByteArray</code>.
+ * The character set is a global setting: <code>m_standardCharSet</code>.
+ * @param source the string to convert
+ * @return the converted string
+ */
+ inline static QString b2s(const QByteArray& source){
+ if (m_standardCharSet == UTF8)
+ return QString::fromUtf8(source);
+ else if (m_standardCharSet == LATIN)
+ return QString::fromLatin1(source);
+ else
+ return QString::fromLocal8Bit(source);
+ }
public:
static CharSet m_standardCharSet;
};
*/
QByteArray& ReStringUtils::base64Decode(const char* input, int inputLength, QByteArray& output) {
static uint8_t decoder[256] = {0};
- if (decoder['A'] == 0){
+ if (decoder[(int)'A'] == 0){
for (int ix = strlen(m_base64Chars) - 1; ix >= 0; ix--)
decoder[(int) m_base64Chars[ix]] = ix;
}
inline double min(double a, double b) {
return a < b ? a : b;
}
+inline qint64 _abs(qint64 a){
+ return a >= 0 ? a : -1;
+}
/** Returns the integer value of a hex digit.
* @param hex the hex digit
#define TRACE2(format, a1, a2) printf(format, a1, a2)
#define TRACE_IT(args) printf args
#define IF_TRACE(statem) statem
+#define DECLARE_TRACER(varName, fileName) static ReTracer varName(fileName)
+// Example: FILETRACE_IT(s_tracerLoop, (s_tracerLoop.m_fp, "value: %d", 33));
+#define FILETRACE_IT(varName, args) do { fprintf args; fflush(varName.m_fp); } while(false)
#else
#define TRACE(m)
#define TRACE1(format, a1)
#define TRACE2(format, a1, a2)
#define TRACE_IT(args)
#define IF_TRACE(statem)
+#define DECLARE_TRACER(varName, fileName)
+#define FILETRACE_IT(varName, args)
#endif
#ifdef WITH_TRACE
-static QByteArray hexBytes(const void* arg, int length = 8){
- char buffer[16+1];
- const unsigned char* src = reinterpret_cast<const unsigned char*>(arg);
- QByteArray rc;
- rc.reserve(length * 2);
- for (int ii = 0; ii < length; ii++){
- snprintf(buffer, sizeof buffer, "%02x", src[ii]);
- rc += buffer;
- }
- return rc;
+#define DECLARE_HEX_BYTES static QByteArray hexBytes(const void* arg, int length = 8){ \
+ char buffer[16+1]; \
+ const unsigned char* src = reinterpret_cast<const unsigned char*>(arg); \
+ QByteArray rc; \
+ rc.reserve(length * 2); \
+ for (int ii = 0; ii < length; ii++){ \
+ snprintf(buffer, sizeof buffer, "%02x", src[ii]); \
+ rc += buffer; \
+ } \
+ return rc; \
}
+
+class ReTracer {
+public:
+ ReTracer(const char* filename) :
+ m_file(filename),
+ m_fp(fopen(filename, "a")){
+ if (m_fp == NULL)
+ fprintf(stderr, "+++ cannot open: %s", filename);
+ }
+ ~ReTracer(){
+ if (m_fp != NULL)
+ fclose(m_fp);
+ m_fp = NULL;
+ }
+protected:
+ QByteArray m_file;
+public:
+ FILE* m_fp;
+};
+#else
+#define DECLARE_HEX_BYTES
#endif // WITH_TRACE
#endif // RETRACE_HPP
static bool s_allTest = false;
-static void testGui() {
- char* argv[2] = { (char*) "dummy", NULL };
- int argc = 1;
- QApplication a(argc, argv);
- void testReStateStorage();
- void testReEdit();
- void testReSettings();
- testReSettings();
- testReStateStorage();
- testReEdit();
-}
-
static void testBase() {
void testReProgArgs();
void testReProcess();
testReWriter();
}
}
+
+#if 0
+static void testGui() {
+ char* argv[2] = { (char*) "dummy", NULL };
+ int argc = 1;
+ QApplication a(argc, argv);
+ void testReStateStorage();
+ void testReEdit();
+ void testReSettings();
+ testReSettings();
+ testReStateStorage();
+ testReEdit();
+}
+
static void testMath() {
}
testReFileSystem();
testReCryptFileSystem();
}
+#endif
void allTests() {
testBase();
+#if 0
testOs();
testExpr();
testGui();
testNet();
testOs();
}
+#endif
}
class TestReByteStorage: public ReTest {
public:
TestReByteStorage() :
- ReTest("ReByteStorage") {
+ ReTest("ReByteStorage") {
doIt();
}
private:
ReFileUtils::tempDirEmpty("subdir2", "cuReFileUtils", true));
QByteArray subdir(dir);
subdir.append("subdirX");
- _mkdir(subdir.constData());
+ _mkdir(subdir.constData());
struct stat info;
checkEqu(0, stat(subdir.constData(), &info));
ReFileUtils::tempDirEmpty("subdir2", "cuReFileUtils", true);
QByteArray base = ReFileUtils::tempDir("ReFileUtils");
for (char cc = 'a'; cc < 'f'; cc++) {
QByteArray subdir(base + cc);
- _mkdir(subdir.constData());
+ _mkdir(subdir.constData());
for (char cc2 = '1'; cc2 < '5'; cc2++) {
QByteArray name(subdir);
name.append(OS_SEPARATOR_STR).append(&cc2, 1);
ReFileUtils::writeToFile(name, name);
name += "dir";
- _mkdir(name.constData());
+ _mkdir(name.constData());
name.append(OS_SEPARATOR_STR).append("x.txt");
ReFileUtils::writeToFile(name, name);
}
// the dir must exist:
checkEqu(0, stat(base, &info));
// rmdir() works only if the dir is empty:
- checkEqu(0, _rmdir(base));
+ checkEqu(0, _rmdir(base));
buildTree();
checkT(ReFileUtils::deleteTree(QString(base), false, &m_logger));
checkEqu(0, stat(base, &info));
checkLogContainsLocation(51003);
checkLogContains(dir.constData(), false);
}
+ void testIsDirectoy(){
+ QByteArray dir(ReFileUtils::tempDir("subdir", "cuReFileUtils", false));
+ checkT(ReFileUtils::isDirectory(dir));
+ QByteArray fn(ReFileUtils::tempFile("node.txt", "subdir", false));
+ ReStringUtils::write(fn);
+ checkF(ReFileUtils::isDirectory(fn));
+ checkF(ReFileUtils::isDirectory("/isImpossible/dir/nowhere"));
+ }
+ void testIsRoot(){
+#if defined __linux__
+ checkT(ReFileUtils::isRootDir("/"));
+ checkF(ReFileUtils::isRootDir("/doesNotExist"));
+ checkF(ReFileUtils::isRootDir("/etc"));
+#elif defined _WIN32
+ checkT(ReFileUtils::isRootDir("c:\\"));
+ checkF(ReFileUtils::isRootDir("z:\\doesNotExist"));
+ checkF(ReFileUtils::isRootDir("c:\\windows"));
+#endif
+ }
+ void testSetPermissions(){
+ QByteArray fn(ReFileUtils::tempFile("node.txt", "subdir", false));
+ ReStringUtils::write(fn);
+ QFile::Permissions perms = QFile::ReadUser | QFile::WriteUser | QFile::ExeUser;
+ checkT(ReFileUtils::setPermissions(fn, perms));
+ checkT(perms == QFile::permissions(fn));
+ }
+ void testMakeDir(){
+ QByteArray base(ReFileUtils::tempDir("cuReFileUtils", NULL, true));
+ QByteArray dir = base + "newdir";
+ ReFileUtils::deleteTree(dir, true, &m_logger);
+ checkT(ReFileUtils::makeDir(dir.constData(), &m_logger));
+ ReFileUtils::deleteTree(dir, true, &m_logger);
+ checkT(ReFileUtils::makeDir(QString(dir), &m_logger));
+ }
virtual void runTests() {
+ testSetPermissions();
+ testIsRoot();
+ testIsDirectoy();
testMakeDirectoryWithParents();
testSplitUrl();
args.addBool("boolArg", "a bool arg", 'b', "bool-arg", false);
args.addInt("intArg", "integer arg", 'i', "int-arg", 99);
args.addString("stringArg", "string argument", 's', "string-arg", true, "");
- const char* arguments[] = { "example" };
QByteArrayList list;
args.help("dummy error", false, list);
QByteArray expected = "usage: example prog\n"
}
void testLengthOfUInt64() {
- uint64_t value = -3;
+ quint64 value = -3;
checkEqu(1,
ReQStringUtils::lengthOfUInt64(ReString("0"), 0, 10, &value));
checkEqu(int64_t(0), value);
rand.saveSeed(seed2);
checkEqu(seed1, seed2);
char cc2 = rand.nextChar();
- if (cc1 != cc1) {
+ if (cc1 != cc2) {
checkEqu(cc1, cc2);
}
INCLUDEPATH = ..
SOURCES += main.cpp \
- cuReLexer.cpp \
- cuReProgArgs.cpp \
- cuReFileSystem.cpp \
- cuReCryptFileSystem.cpp \
- cuReRandomizer.cpp \
- cuReQStringUtils.cpp \
- cuReStringUtils.cpp \
- cuReFile.cpp \
- cuReFileUtils.cpp \
- cuReByteStorage.cpp \
- cuReException.cpp \
../expr/ReSource.cpp \
../expr/ReLexer.cpp \
../base/ReByteStorage.cpp \
../gui/ReEdit.cpp \
../os/ReFileSystem.cpp \
../os/ReCryptFileSystem.cpp \
+ allTests.cpp \
+ cuReException.cpp \
+ cuReProcess.cpp \
+ cuReMatcher.cpp \
cuReConfig.cpp \
cuReContainer.cpp \
cuReWriter.cpp \
cuReCharPtrMap.cpp \
+ cuReRandomizer.cpp \
+ cuReQStringUtils.cpp \
+ cuReStringUtils.cpp \
+ cuReFile.cpp \
+ cuReFileUtils.cpp \
+ cuReByteStorage.cpp \
+ cuReProgArgs.cpp \
+ ../base/ReProcess.cpp \
+ ../base/ReProgramArgs.cpp
+
+RESERVE = \
cuReEdit.cpp \
cuReStateStorage.cpp \
cuReSettings.cpp \
- cuReMatcher.cpp \
- allTests.cpp \
- ../base/ReProcess.cpp \
- cuReProcess.cpp \
- ../base/ReProgramArgs.cpp
+ cuReLexer.cpp \
+ cuReFileSystem.cpp \
+ cuReCryptFileSystem.cpp \
HEADERS += \
../base/ReFile.hpp \
#include <QCoreApplication>
int main(int argc, char *argv[]) {
- QCoreApplication a(argc, argv);
+ printf("main...\n");
+ //QCoreApplication a(argc, argv);
void allTests();
allTests();
- return a.exec();
+ //return a.exec();
+ return 0;
}
if (charClass[ix + 1] == '-') {
unsigned char ubound = charClass[ix + 2];
if (ubound == '\0')
- charInfo['-'] |= flag;
+ charInfo[(int) '-'] |= flag;
else if (cc >= ubound)
throw new ReException("wrong character class range: %c-%c (%s)",
cc, ubound, charClass);
homeDir = ReFileUtils::nativePath(QDir::home().absoluteFilePath(node));
} else if (ReFileUtils::isRootDir(homeDirBase.toLatin1().constData())){
homeDir = homeDirBase + node;
+ } else {
+ homeDir = homeDirBase;
+ ReQStringUtils::ensureLastChar(homeDir, OS_SEPARATOR);
+ homeDir += node;
}
ReQStringUtils::chomp(homeDir, OS_SEPARATOR);
QDir home(homeDir);
QCoreApplication* app = QCoreApplication::instance();
QObject::connect(app, SIGNAL(aboutToQuit()), this, SLOT(onAboutToQuit()));
connect(m_guiTimer, SIGNAL(timeout()), this, SLOT(onGuiTimerUpdate()));
- m_guiTimer->start(100);
+ m_guiTimer->start(500);
statusBar()->addWidget(m_statusMessage);
m_guiIsInitialized = true;
}
class ReSettings {
public:
- static QString m_TRUE;
- static QString m_FALSE;
+ static QString TRUE;
+ static QString FALSE;
public:
ReSettings(const QString& path, const QString& prefix, ReLogger* logger);
~ReSettings();