From 5536e688dc40648660b55c6b1918512eb8b00c41 Mon Sep 17 00:00:00 2001 From: hama Date: Tue, 12 Apr 2016 00:52:57 +0200 Subject: [PATCH] v2016.04.07, rebackgui, warnings found by clang rebackgui: * test of symbolic link before test of directory * allows being root by setuid() * translation improvements * documentation in rebackgui.html lib: * file copy under linux: ** copying symbolic links ** setting owner and group * linux: using QT setpermissions() instead of chmod() * new: modifyToNonExisting() * ReLogger(): correction of logging file "ring" (incrementing numbers, limited count of files) * ReProcess::executeAndRead(): timeout * added: I18N::b2s() * more tests * uint64_t changed to quint64 * ReGuiApplication(): default base dir is now /.reappl --- PrepTmp.sh | 8 ++ RenTmp.sh | 4 + appl/rebackgui/BackupEngine.cpp | 52 +++++-- appl/rebackgui/main.cpp | 13 +- appl/rebackgui/mainwindow.cpp | 4 +- appl/rebackgui/mainwindow.ui | 34 ++--- appl/rebackgui/rebackgui.de.qm | Bin 0 -> 21696 bytes appl/rebackgui/rebackgui.de.ts | 119 +++++++++------- appl/rebackgui/rebackgui.html | 73 ++++++++++ appl/rebackgui/rebackgui.pro | 3 +- appl/recommand/recommand.pro | 3 +- base/ReConfig.hpp | 2 +- base/ReContainer.cpp | 60 ++++---- base/ReFileUtils.cpp | 243 ++++++++++++++++++++++---------- base/ReFileUtils.hpp | 6 +- base/ReLogger.cpp | 93 ++++++++---- base/ReLogger.hpp | 5 +- base/ReProcess.cpp | 2 +- base/ReQStringUtils.hpp | 13 ++ base/ReStringUtils.cpp | 2 +- base/rebase.hpp | 3 + cunit/allTests.cpp | 29 ++-- cunit/cuReByteStorage.cpp | 2 +- cunit/cuReFileUtils.cpp | 45 +++++- cunit/cuReProgArgs.cpp | 1 - cunit/cuReQStringUtils.cpp | 2 +- cunit/cuReRandomizer.cpp | 2 +- cunit/cunit.pro | 34 ++--- cunit/main.cpp | 6 +- expr/ReLexer.cpp | 2 +- gui/ReGuiApplication.cpp | 4 + gui/ReSettings.hpp | 4 +- 32 files changed, 602 insertions(+), 271 deletions(-) create mode 100755 PrepTmp.sh create mode 100755 RenTmp.sh create mode 100644 appl/rebackgui/rebackgui.de.qm create mode 100644 appl/rebackgui/rebackgui.html diff --git a/PrepTmp.sh b/PrepTmp.sh new file mode 100755 index 0000000..dd555f5 --- /dev/null +++ b/PrepTmp.sh @@ -0,0 +1,8 @@ +#! /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 + diff --git a/RenTmp.sh b/RenTmp.sh new file mode 100755 index 0000000..8259916 --- /dev/null +++ b/RenTmp.sh @@ -0,0 +1,4 @@ +#! /bin/bash +cp -a /tmp/src /tmp/trg +rename "s/py/px/g" /tmp/trg/src/* +touch /tmp/trg/src/* diff --git a/appl/rebackgui/BackupEngine.cpp b/appl/rebackgui/BackupEngine.cpp index 9e9a138..d2345a8 100644 --- a/appl/rebackgui/BackupEngine.cpp +++ b/appl/rebackgui/BackupEngine.cpp @@ -157,15 +157,15 @@ void BackupTask::copyFile(int index, const QString& 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) @@ -645,7 +645,8 @@ void SearchTask::searchOneDirectory(const QString& source, } it.next(); node = it.fileName(); - if (it.fileInfo().isDir()){ + QFileInfo fileInfo = it.fileInfo(); + if (! fileInfo.isSymLink() && fileInfo.isDir()){ // nothing to do } else if (! m_fileMatcher.matches(node)){ m_mutex.lock(); @@ -666,7 +667,7 @@ void SearchTask::searchOneDirectory(const QString& source, 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; } @@ -694,8 +695,9 @@ void SearchTask::searchOneDirectory(const QString& source, 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(); @@ -753,7 +755,8 @@ bool SearchTargetTask::removeOlder(const QString& directory, const QDateTime& ti } 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; @@ -820,7 +823,8 @@ void SearchTargetTask::searchOneDirectory(const QString& target, } 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; @@ -857,12 +861,13 @@ void SearchTargetTask::searchOneDirectory(const QString& target, 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{ @@ -883,6 +888,25 @@ void SearchTargetTask::searchOneDirectory(const QString& target, 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") diff --git a/appl/rebackgui/main.cpp b/appl/rebackgui/main.cpp index df7047f..2c2fcde 100644 --- a/appl/rebackgui/main.cpp +++ b/appl/rebackgui/main.cpp @@ -15,10 +15,11 @@ #include 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(); } diff --git a/appl/rebackgui/mainwindow.cpp b/appl/rebackgui/mainwindow.cpp index c430ab9..e4cbb7c 100644 --- a/appl/rebackgui/mainwindow.cpp +++ b/appl/rebackgui/mainwindow.cpp @@ -13,7 +13,7 @@ #include "aboutdialog.hpp" #include -const QString VERSION("2016.02.24"); +const QString VERSION("2016.04.07"); /** * Constructor. @@ -23,7 +23,7 @@ const QString VERSION("2016.02.24"); */ 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), diff --git a/appl/rebackgui/mainwindow.ui b/appl/rebackgui/mainwindow.ui index f556cc3..8fb4ae9 100644 --- a/appl/rebackgui/mainwindow.ui +++ b/appl/rebackgui/mainwindow.ui @@ -197,7 +197,7 @@ - + @@ -785,12 +785,12 @@ 0 0 839 - 26 + 29 - File + Fi&le @@ -801,12 +801,12 @@ - Edit + Edi&t - Help + Hel&p @@ -815,7 +815,7 @@ - Action + A&ction @@ -838,37 +838,37 @@ - Load Config + &Load Config - Save Config + &Save Config - Exit + &Exit - Start backup + &Start backup - Stop + St&op - About + &About - Checksums + &Checksums Calculates checksums for source and target files and reports differences @@ -876,7 +876,7 @@ - Clean + C&lean @@ -884,7 +884,7 @@ true - English + &English @@ -892,7 +892,7 @@ true - German + &German @@ -900,7 +900,7 @@ true - Autosave + &Autosave diff --git a/appl/rebackgui/rebackgui.de.qm b/appl/rebackgui/rebackgui.de.qm new file mode 100644 index 0000000000000000000000000000000000000000..ca4ccf55cbd745f203875ecdb6ddf58326a21cdd GIT binary patch literal 21696 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 ) zWYSvA9$o%5v$fU4iBys>6=H6&h?uBwMmA7{2(pZ;t}D7zFK3{g^>+Qe!2w(e}=%9 zmn*z&N;y29xm3*InVtx&rXn}+I;1_rL%I{AUS(jWAzxY~7JzY>uCMhrRd%JfXQ&r_ zzd3N6g}*xfw(!|!AK>OQ`t-lbMqY^qD0Nl~qO)&MTh^Waq>j7H915%EPA-1g2@_9H V9x9daWRrS|r|Dg|x~Zw@{{T)VO$-14 literal 0 HcmV?d00001 diff --git a/appl/rebackgui/rebackgui.de.ts b/appl/rebackgui/rebackgui.de.ts index e1adf8b..b8a17ec 100644 --- a/appl/rebackgui/rebackgui.de.ts +++ b/appl/rebackgui/rebackgui.de.ts @@ -106,7 +106,6 @@ p, li { white-space: pre-wrap; } - Action Aktion @@ -205,7 +204,6 @@ p, li { white-space: pre-wrap; } - Stop Beenden @@ -217,7 +215,6 @@ p, li { white-space: pre-wrap; } - Checksums Prüfsummen @@ -228,7 +225,6 @@ p, li { white-space: pre-wrap; } - Clean Protokoll: Bereinigen @@ -272,7 +268,6 @@ p, li { white-space: pre-wrap; } - Autosave Autom. Speichern @@ -356,81 +351,91 @@ p, li { white-space: pre-wrap; } - File + Fi&le + File Bearbeiten - Datei + Datei - Edit - Bearbeiten + Edi&t + Edit + Bearbeiten - Help - Hilfe + Hel&p + Help + Hilfe - Load Config - Konfiguration laden + &Load Config + Load Config + Konfiguration laden - Save Config - Konfiguration sichern + &Save Config + Save Config + Konfiguration sichern - Exit - Beenden + &Exit + Exit + Beenden - Start backup - Sicherung starten + &Start backup + Start backup + Sicherung starten - About - Über + &About + About + Über - English - + &English + English + - German - + &German + German + - - - + + + no backup item selected Kein Sicherungselement ausgewählt - - - + + + Target not available Ziel nicht vorhanden - + Select Source Directory Quellverzeichnis auswählen - + Select Target Directory Zielverzeichnis auswählen - + target initialized with %1 Ziel mit %1 vorbelegt @@ -519,17 +524,22 @@ p, li { white-space: pre-wrap; } Suche beendet: zu behandeln: %1 mit %2 passend: %3 gesamt: %4 Unterverz: %5 Laufzeit: %6 - + + kann Schattenverzeichnis nicht umbenennen: %1 -> %2 + + + + cannot create shadow directory (%1): %2 Kann Schattenverzeichnis nicht erstellen (%1): %2 - + cannot move to shadow directory (%1): %2 -> %3 Kann Datei nicht in Schattenverzeichnis verschieben (%1): %2 -> %3 - + Search in target finished: to process: %1 with %2 dirs to delete: %3 total: %4 subdirs: %5 runtime: %6 Suche im Ziel bendet: zu behandeln: %1 mit %2 zu löschende Verzeichnisse: %3 gesamt: %4 Unterverz.: %5 Laufzeit: %6 @@ -540,9 +550,9 @@ p, li { white-space: pre-wrap; } - - - + + + cannot open (%1): %2 Kann nicht öffnen (%1): %2 @@ -552,33 +562,33 @@ p, li { white-space: pre-wrap; } Unbekanntes Format in %1-%2: %3 - + cannot write (%1): %2 Kann nicht schreiben (%1): %2 - + configuration saved: %1 configuration changed: %1 Konfiguration gespeichert: %1 - + Search started... Suche gestartet... - + yes Ja - + no nein - + not found: %1 Nicht gefunden: %1 @@ -587,32 +597,37 @@ p, li { white-space: pre-wrap; } Kann nicht schreiben(%1): %2 [%3/%4] - + + copy file failed (%1): %2 + + + + cannot write (%1): %2 [%3/%4] Kann nicht schreiben(%1): %2 [%3/%4] - + file can be read only partitionally: %1 [%2/%3] Datei kann nur teilweise gelesen werden: %1 [%2/%3] - + cannot set date/time (%1): %2 Kann Datum/Zeit nicht setzen (%1): %2 - + cannot set permissions (%1): %2 Kann Dateirechte nicht setzen (%1): %2 - + can't create directory (%1): %2 Kann Verzeichnis nicht erzeugen (%1): %2 - + can't create directory (is a file): Kann Verzeichnis nicht erzeugen (ist eine Datei): diff --git a/appl/rebackgui/rebackgui.html b/appl/rebackgui/rebackgui.html new file mode 100644 index 0000000..8ca4bc0 --- /dev/null +++ b/appl/rebackgui/rebackgui.html @@ -0,0 +1,73 @@ + + + + +
Documentation of rebackgui
+

Release Notes

+

+

    +
  • v2016.04.06
    +
    • Basic version
    • +
    +
  • +
+

+ +

User Manual

+

Purpose:

+

This program makes backups.

+ +

Usage

+
hmbackgui [<home_dir>]
+<home_dir>:
+ the base directory for the configuration directory ".reappl"
+ Default: the home directory of the user
+
+

Example:

+
hmbackgui /home/hugo
+
+ +

Installation

+

Linux

+

+

  • Copy rebackgui to /usr/local/bin
  • +
  • Copy rebackgui.*.qm to the configuration directory, e.g. /home/hm/.reappl
  • +
+
cd /usr/local/bin
+chown root.root rebackgui
+chmod u+s rebackgui
+
+

+ +

Configuration

+

The configuration is stored in <home_dir>/.rebackgui/rebackgui.conf +

+ +

Program Documentation

+ +

Programming Features

+ +

This is a example for a complete QT application with the following features: +

    +
  • multithreaded
  • +
  • pattern matching
  • +
  • multithreaded GUI building
  • +
+

+ +

(Un)License: Public Domain

+ +

You can use and modify this file without any restriction.
+Do what you want.
+No warranties and disclaimer of any damages.
+More info: http://unlicense.org
+The latest sources: https://github.com/republib. +

+ + + diff --git a/appl/rebackgui/rebackgui.pro b/appl/rebackgui/rebackgui.pro index 4b10c3d..ba86e1c 100644 --- a/appl/rebackgui/rebackgui.pro +++ b/appl/rebackgui/rebackgui.pro @@ -53,7 +53,8 @@ FORMS += mainwindow.ui \ DISTFILES += \ ReBackGui.html \ - osconnect.pl + osconnect.pl \ + rebackgui.html TRANSLATIONS = rebackgui.de.ts CODECFORTR = UTF-8 diff --git a/appl/recommand/recommand.pro b/appl/recommand/recommand.pro index 8efbe4f..318b324 100644 --- a/appl/recommand/recommand.pro +++ b/appl/recommand/recommand.pro @@ -29,7 +29,8 @@ SOURCES += main.cpp\ ../../gui/ReGuiQueue.cpp \ ../../gui/ReGuiUtils.cpp \ mainwindow.cpp \ - CommandProcessor.cpp + CommandProcessor.cpp \ + ../../base/ReProcess.cpp HEADERS += mainwindow.hpp \ CommandProcessor.hpp \ diff --git a/base/ReConfig.hpp b/base/ReConfig.hpp index 617e566..b2bdb44 100644 --- a/base/ReConfig.hpp +++ b/base/ReConfig.hpp @@ -11,7 +11,7 @@ #ifndef RECONFIG_HPP #define RECONFIG_HPP -class ReConfig: public ReConfigurator, protected QHash { +class ReConfig: public ReConfigurator, public QHash { public: ReConfig(const char* file = NULL, bool readOnly = true, ReLogger* logger = NULL); diff --git a/base/ReContainer.cpp b/base/ReContainer.cpp index 62d7683..9f50f2d 100644 --- a/base/ReContainer.cpp +++ b/base/ReContainer.cpp @@ -67,12 +67,12 @@ const char* ReContainer::MAGIC_1 = "Rpl&1"; * @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); } @@ -188,10 +188,10 @@ const QByteArray& ReContainer::getData() { 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; @@ -207,25 +207,25 @@ void ReContainer::fill(const QByteArray& 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); @@ -247,13 +247,13 @@ int ReContainer::getCountBags() const { 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. @@ -267,20 +267,20 @@ void ReContainer::nextItem(type_tag_t expected) { } 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()); } /** @@ -307,7 +307,7 @@ int ReContainer::nextInt() { 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", m_readPosition, 16); + "not a hex_number", m_readPosition, 16); m_readPosition = (uint8_t*) strchr((const char*) m_readPosition, ' ') + 1; if (isNegativ) value = -value; @@ -323,10 +323,10 @@ int64_t ReContainer::nextInt64() { 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", m_readPosition, 16); + "not a hex_number", m_readPosition, 16); m_readPosition = (uint8_t*) strchr((const char*) m_readPosition, ' ') + 1; if (isNegativ) value = -value; @@ -392,7 +392,7 @@ size_t ReContainer::nextData(QByteArray& data, bool append) { * @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'); @@ -409,7 +409,7 @@ QByteArray ReContainer::dump(const char* title, int maxBags, 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; @@ -422,9 +422,9 @@ QByteArray ReContainer::dump(const char* title, int maxBags, 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(); @@ -439,10 +439,10 @@ QByteArray ReContainer::dump(const char* title, int maxBags, 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; diff --git a/base/ReFileUtils.cpp b/base/ReFileUtils.cpp index 0897aa8..6f9b56f 100644 --- a/base/ReFileUtils.cpp +++ b/base/ReFileUtils.cpp @@ -21,6 +21,7 @@ enum { LOC_MAKE_DIR_2, // 12506 LOC_SET_TIMES_2, // 12507 }; +int ReFileUtils::m_maxCharSet = 128; QDateTime ReFileUtils::m_undefinedTime; @@ -161,11 +162,13 @@ QString ReFileUtils::cleanPath(const QString& path) { * @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(); @@ -182,9 +185,23 @@ QString ReFileUtils::copy(const QString& source, const QString& target, 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); @@ -228,6 +245,15 @@ QString ReFileUtils::copy(const QString& source, const QString& target, } } 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 @@ -372,6 +398,73 @@ QStringList ReFileUtils::findRootDirs() return rc; } +/** + * Modify a filename until the file does not exist. + * + * @param filename filename to modify + * @return "": no unused filename found
+ * 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. * @@ -413,14 +506,15 @@ bool ReFileUtils::isAbsolutPath(const char* path) { * * @param path full name of the directory to inspect * @param isFile OUT: true: this is a file (and not a directory) - * @return true: path is a directory + * @return true: 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; @@ -776,72 +870,6 @@ QByteArray ReFileUtils::replaceExtension(const char* path, const char* ext) { return rc; } -/** - * Splits an URL into its parts. - * - * Examples: - *
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"
- * 
- * - * @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. * @@ -853,10 +881,10 @@ 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 (! 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; @@ -986,6 +1014,72 @@ int ReFileUtils::seek(FILE* file, int64_t offset, int whence) { return rc; } +/** + * Splits an URL into its parts. + * + * Examples: + *
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"
+ * 
+ * + * @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. * @@ -1003,6 +1097,7 @@ int64_t ReFileUtils::tell(FILE* file) { return rc; } + /** * @brief Returns the name of a directory in the temp dir. * diff --git a/base/ReFileUtils.hpp b/base/ReFileUtils.hpp index aab4356..3c8a7b1 100644 --- a/base/ReFileUtils.hpp +++ b/base/ReFileUtils.hpp @@ -47,7 +47,8 @@ public: 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); @@ -57,6 +58,7 @@ public: 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 '/'. @@ -121,6 +123,8 @@ public: size_t contentLength = (size_t) - 1, const char* mode = "w"); public: static QDateTime m_undefinedTime; + /// see modifyToNonExisting() + static int m_maxCharSet; }; #endif // REFILEUTILS_HPP diff --git a/base/ReLogger.cpp b/base/ReLogger.cpp index 8c2037c..2147d6a 100644 --- a/base/ReLogger.cpp +++ b/base/ReLogger.cpp @@ -324,24 +324,6 @@ bool ReLogger::logv(ReLoggerLevel level, int location, const char* format, 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. @@ -524,7 +506,8 @@ ReFileAppender::ReFileAppender(const QByteArray& prefix, int maxSize, m_currentSize(0), m_currentNo(0), m_fp(NULL) { - open(); + findLastNo(); + reopen(true); } /** @@ -537,21 +520,68 @@ ReFileAppender::~ReFileAppender() { } } +/** + * 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 true: 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()); } } @@ -571,6 +601,15 @@ void ReFileAppender::log(ReLoggerLevel level, int location, const char* message, 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); } } diff --git a/base/ReLogger.hpp b/base/ReLogger.hpp index a8063d4..f3cc752 100644 --- a/base/ReLogger.hpp +++ b/base/ReLogger.hpp @@ -92,7 +92,6 @@ public: 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); @@ -149,10 +148,12 @@ public: 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 "..log" QByteArray m_prefix; diff --git a/base/ReProcess.cpp b/base/ReProcess.cpp index 7469921..2fdd856 100644 --- a/base/ReProcess.cpp +++ b/base/ReProcess.cpp @@ -37,7 +37,7 @@ QByteArray ReProcess::executeAndRead(const QString& program, 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; diff --git a/base/ReQStringUtils.hpp b/base/ReQStringUtils.hpp index 6bb421f..c05b8c5 100644 --- a/base/ReQStringUtils.hpp +++ b/base/ReQStringUtils.hpp @@ -31,6 +31,19 @@ public: else return source.toLocal8Bit(); } + /** Converts a QString into a QByteArray. + * The character set is a global setting: m_standardCharSet. + * @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; }; diff --git a/base/ReStringUtils.cpp b/base/ReStringUtils.cpp index 7039125..d8b450e 100644 --- a/base/ReStringUtils.cpp +++ b/base/ReStringUtils.cpp @@ -41,7 +41,7 @@ static const char* m_base64Chars = */ 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; } diff --git a/base/rebase.hpp b/base/rebase.hpp index fd6898b..60dfb22 100644 --- a/base/rebase.hpp +++ b/base/rebase.hpp @@ -150,6 +150,9 @@ inline double max(double a, double b) { 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 diff --git a/cunit/allTests.cpp b/cunit/allTests.cpp index d539467..cc5dc81 100644 --- a/cunit/allTests.cpp +++ b/cunit/allTests.cpp @@ -17,18 +17,6 @@ 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(); @@ -64,6 +52,20 @@ static void testBase() { 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() { } @@ -100,8 +102,10 @@ static void testOs() { testReFileSystem(); testReCryptFileSystem(); } +#endif void allTests() { testBase(); +#if 0 testOs(); testExpr(); testGui(); @@ -111,5 +115,6 @@ void allTests() { testNet(); testOs(); } +#endif } diff --git a/cunit/cuReByteStorage.cpp b/cunit/cuReByteStorage.cpp index 13c80af..ef4dc7b 100644 --- a/cunit/cuReByteStorage.cpp +++ b/cunit/cuReByteStorage.cpp @@ -17,7 +17,7 @@ class TestReByteStorage: public ReTest { public: TestReByteStorage() : - ReTest("ReByteStorage") { + ReTest("ReByteStorage") { doIt(); } private: diff --git a/cunit/cuReFileUtils.cpp b/cunit/cuReFileUtils.cpp index af4f614..533d777 100644 --- a/cunit/cuReFileUtils.cpp +++ b/cunit/cuReFileUtils.cpp @@ -45,7 +45,7 @@ public: 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); @@ -65,13 +65,13 @@ public: 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); } @@ -87,7 +87,7 @@ public: // 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)); @@ -354,8 +354,45 @@ public: 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(); diff --git a/cunit/cuReProgArgs.cpp b/cunit/cuReProgArgs.cpp index 2155eb0..07c118a 100644 --- a/cunit/cuReProgArgs.cpp +++ b/cunit/cuReProgArgs.cpp @@ -146,7 +146,6 @@ public: 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" diff --git a/cunit/cuReQStringUtils.cpp b/cunit/cuReQStringUtils.cpp index ed9c3bb..fe743cb 100644 --- a/cunit/cuReQStringUtils.cpp +++ b/cunit/cuReQStringUtils.cpp @@ -33,7 +33,7 @@ public: } void testLengthOfUInt64() { - uint64_t value = -3; + quint64 value = -3; checkEqu(1, ReQStringUtils::lengthOfUInt64(ReString("0"), 0, 10, &value)); checkEqu(int64_t(0), value); diff --git a/cunit/cuReRandomizer.cpp b/cunit/cuReRandomizer.cpp index 4a06f5d..b6adb3c 100644 --- a/cunit/cuReRandomizer.cpp +++ b/cunit/cuReRandomizer.cpp @@ -39,7 +39,7 @@ public: rand.saveSeed(seed2); checkEqu(seed1, seed2); char cc2 = rand.nextChar(); - if (cc1 != cc1) { + if (cc1 != cc2) { checkEqu(cc1, cc2); } diff --git a/cunit/cunit.pro b/cunit/cunit.pro index 7cb6f61..e510dc2 100644 --- a/cunit/cunit.pro +++ b/cunit/cunit.pro @@ -18,17 +18,6 @@ TEMPLATE = app 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 \ @@ -51,18 +40,31 @@ SOURCES += main.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 \ diff --git a/cunit/main.cpp b/cunit/main.cpp index 19adf49..447ea6b 100644 --- a/cunit/main.cpp +++ b/cunit/main.cpp @@ -12,8 +12,10 @@ #include 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; } diff --git a/expr/ReLexer.cpp b/expr/ReLexer.cpp index 66cc75f..a57fb4c 100644 --- a/expr/ReLexer.cpp +++ b/expr/ReLexer.cpp @@ -428,7 +428,7 @@ static void charClassToCharInfo(const char* charClass, int flag, 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); diff --git a/gui/ReGuiApplication.cpp b/gui/ReGuiApplication.cpp index cea4ab5..598387c 100644 --- a/gui/ReGuiApplication.cpp +++ b/gui/ReGuiApplication.cpp @@ -96,6 +96,10 @@ QString ReGuiApplication::buildHomeDir(QString homeDirBase, const QString& node) 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); diff --git a/gui/ReSettings.hpp b/gui/ReSettings.hpp index eed6b29..135d639 100644 --- a/gui/ReSettings.hpp +++ b/gui/ReSettings.hpp @@ -71,8 +71,8 @@ typedef QMap*> ReChapterMap; 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(); -- 2.39.5