]> gitweb.hamatoma.de Git - reqt/commitdiff
ReFileUtils, ReSettings
authorhama <hama@siduction.net>
Sun, 23 Aug 2015 20:57:37 +0000 (22:57 +0200)
committerhama <hama@siduction.net>
Sun, 23 Aug 2015 20:57:37 +0000 (22:57 +0200)
* ReFileUtils extracted from ReFile
* Storage moved to gui/ReSettings

37 files changed:
appl/reditor/mainwindow.cpp
appl/reditor/project.cpp
appl/reditor/reditor.hpp
appl/reditor/reditor.pro
appl/reditor/storage.cpp [deleted file]
appl/reditor/storage.hpp [deleted file]
appl/reditor/workspace.cpp
base/ReContainer.cpp
base/ReFile.cpp
base/ReFile.hpp
base/ReFileUtils.cpp [new file with mode: 0644]
base/ReFileUtils.hpp [new file with mode: 0644]
base/ReQStringUtil.cpp [deleted file]
base/ReQStringUtil.hpp [deleted file]
base/ReQStringUtils.cpp [new file with mode: 0644]
base/ReQStringUtils.hpp [new file with mode: 0644]
base/ReStringUtil.cpp [deleted file]
base/ReStringUtil.hpp [deleted file]
base/ReStringUtils.cpp [new file with mode: 0644]
base/ReStringUtils.hpp [new file with mode: 0644]
base/ReTest.cpp
base/rebase.hpp
cunit/allTests.cpp
cunit/cuReConfig.cpp
cunit/cuReFile.cpp
cunit/cuReFileUtils.cpp [new file with mode: 0644]
cunit/cuReQStringUtil.cpp [deleted file]
cunit/cuReQStringUtils.cpp [new file with mode: 0644]
cunit/cuReSettings.cpp [new file with mode: 0644]
cunit/cuReStringUtil.cpp [deleted file]
cunit/cuReStringUtils.cpp [new file with mode: 0644]
cunit/cuReWriter.cpp
cunit/cunit.pro
gui/ReSettings.cpp [new file with mode: 0644]
gui/ReSettings.hpp [new file with mode: 0644]
gui/regui.hpp
remodules.hpp

index 9a847642e1c746748bdaaa87489368aacc6a9454..341dd7f5568e1b12f113abdec0484e9d3d4d81e8 100644 (file)
@@ -114,11 +114,11 @@ ReLogger* MainWindow::logger() const {
  *
  * @return the current workspace
  */
-Storage* MainWindow::project() const {
+ReSettings* MainWindow::project() const {
        return m_project;
 }
 
-Storage* MainWindow::workspace() const {
+ReSettings* MainWindow::workspace() const {
        return m_workspace;
 }
 
index 314cb1f6d81af9f3a0039dc7a4182254d57319b6..52629d87a9e5656b81e2dd2ec5fd78f1b579efe0 100644 (file)
@@ -18,6 +18,6 @@
  * @param logger       the logger
  */
 Project::Project(const QString& path, ReLogger* logger) :
-           Storage(path, "proj", logger) {
+           ReSettings(path, ".reditor.proj", logger) {
 }
 
index 4d083d1e10045b7f25a4fe9ef2b2a7b190ef7f6f..2088b69e6a638f834c35b3240c00b68a4302ff1c 100644 (file)
@@ -13,7 +13,6 @@
 #define REDITOR_HPP
 #include "base/rebase.hpp"
 #include "gui/regui.hpp"
-#include "storage.hpp"
 #include "workspace.hpp"
 #include "project.hpp"
 #include "mainwindow.hpp"
index 22e1ff58115559b9734fb2189c0f6258cb5f29a6..ede1294f0ff3783bd5dd0db3230fa3c6ac085bec 100644 (file)
@@ -13,32 +13,34 @@ TEMPLATE = app
 
 INCLUDEPATH += ../..
 
-SOURCES += main.cpp\
+SOURCES += \
         ../../gui/ReEdit.cpp \
         ../../gui/ReStateStorage.cpp \
+        ../../gui/ReStorage.cpp \
         ../../base/ReFile.cpp \
        mainwindow.cpp \
        ../../base/ReLogger.cpp \
-       ../../base/ReQStringUtil.cpp \
+       ../../base/ReQStringUtils.cpp \
+       ../../base/ReFileUtils.cpp \
        ../../base/ReException.cpp \
        projectselection.cpp \
-    workspace.cpp \
-    project.cpp \
-    storage.cpp
+       workspace.cpp \
+       project.cpp \
+       main.cpp
 
 
 HEADERS  += mainwindow.hpp \
         ../../base/rebase.hpp \
         ../../gui/regui.hpp \
         ../../gui/ReEdit.hpp \
-       ../../base/ReStringUtil.hpp \
-       ../../base/ReQStringUtil.hpp \
+       ../../base/ReStringUtils.hpp \
+       ../../base/ReQStringUtils.hpp \
        ../../base/ReException.hpp \
        projectselection.hpp \
-    workspace.hpp \
-    project.hpp \
-    reditor.hpp \
-    storage.hpp
+       workspace.hpp \
+       project.hpp \
+       reditor.hpp \
+       storage.hpp
 
 FORMS    += mainwindow.ui \
        projectselection.ui
diff --git a/appl/reditor/storage.cpp b/appl/reditor/storage.cpp
deleted file mode 100644 (file)
index f13c302..0000000
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * storage.cpp
- *
- * License: Public Domain
- * You can use and modify this file without any restriction.
- * Do what you want.
- * No warranties and disclaimer of any damages.
- * You also can use this license: http://www.wtfpl.net
- * The latest sources: https://github.com/republib
- */
-
-#include "reditor.hpp"
-
-enum {
-       LOC_BOOL_VALUE_1 = LOC_FIRST_OF(LOC_STORAGE), // 11701
-       LOC_BOOL_VALUE_2,       // 11702
-       LOC_INT_VALUE_1,        // 11703
-       LOC_INT_VALUE_2,        // 11704
-       LOC_STRING_VALUE_1,     // 11705
-       LOC_STRING_VALUE_2,     // 11706
-};
-
-/**
- * Constructor.
- *
- * @param path         the parent directory for the storage files
- * @param prefix       type of the storage: "proj" or "ws" (workspace)
- */
-Storage::Storage(const QString& path, const QString prefix, ReLogger* logger) :
-           m_path(path),
-           m_fileHistory(path + OS_SEPARATOR + ".reditor." + prefix + ".history"),
-           m_fileSettings(
-               path + OS_SEPARATOR + ".reditor." + prefix + ".settings"),
-           m_settings(),
-           m_chapters(),
-           m_logger(logger) {
-
-}
-
-/*
- * Adds an entry to a history item at the first position.
- *
- * The entry will removed from the other positions.
- *
- * @param key          the key in the map
- * @param value                the value to add
- * @param separator    separates the entries in the history item
- * @param maxEntries   the maximal count of entries in the history item.<br>
- * If the number exceeds the last entries will be removed
- * @param form the prefix of the key. If NULL the current form will be taken
- */
-void Storage::addHistoryEntry(const char* key, const QString& value,
-    char separator, int maxEntries) {
-       ReStateStorage store(m_fileHistory);
-       store.addHistoryEntry(key, value, separator, maxEntries);
-       store.close();
-       store.flushMap();
-}
-
-/**
- * Returns the value of a boolean property.
- *
- * @param name the name of the property
- * @return the value of the property
- */
-bool Storage::boolValue(const char* name) {
-       bool rc = false;
-       Property* property = m_settings.value(name, NULL);
-       if (property == NULL)
-               m_logger->logv(LOG_ERROR, LOC_BOOL_VALUE_1, "missing bool property %s",
-                   name);
-       else if (property->m_type != PT_BOOL)
-               m_logger->logv(LOG_ERROR, LOC_BOOL_VALUE_2, "not a  bool property %s",
-                   name);
-       else
-               rc = !property->m_value.isEmpty();
-       return rc;
-}
-/**
- * Returns a history item as a list.
- *
- * @param key  key of the history item
- * @param list OUT: the list is filled with the history entries
- * @param form a common prefix of the key. If NULL the current form is used
- * @return <code>list</code> (for chaining)
- */
-QStringList&Storage::historyAsList(const char* key, QStringList& list,
-    const char* form) {
-       ReStateStorage store(m_fileHistory);
-       QStringList& rc = store.historyAsList(key, list, form);
-       store.close();
-       return rc;
-}
-
-/**
- * Returns the value of an integer property.
- *
- * @param name the name of the property
- * @return the value of the property
- */
-int Storage::intValue(const char* name) {
-       int rc = 0;
-       Property* property = m_settings.value(name, NULL);
-       if (property == NULL)
-               m_logger->logv(LOG_ERROR, LOC_INT_VALUE_1, "missing int property %s",
-                   name);
-       else if (property->m_type != PT_INT)
-               m_logger->logv(LOG_ERROR, LOC_INT_VALUE_2, "not a  int property %s",
-                   name);
-       else
-               rc = property->m_value.toInt();
-       return rc;
-}
-
-/**
- * Returns the directory containing the configuration data.
- *
- * @return the name of the storage's directory
- */
-QString Storage::path() const {
-       return m_path;
-}
-
-/**
- * Returns the value of a string property.
- *
- * @param name the name of the property
- * @return the value of the property
- */
-QString Storage::stringValue(const char* name) {
-       QString rc = "";
-       Property* property = m_settings.value(name, NULL);
-       if (property == NULL)
-               m_logger->logv(LOG_ERROR, LOC_STRING_VALUE_1,
-                   "missing string property %s", name);
-       else if (property->m_type != PT_STRING)
-               m_logger->logv(LOG_ERROR, LOC_STRING_VALUE_2,
-                   "not a string property %s", name);
-       else
-               rc = property->m_value;
-       return rc;
-}
-
-/**
- * Inserts a property.
- *
- * @param property     the property to insert
- */
-void Storage::insertProperty(Property* property) {
-       m_settings.insert(property->m_name, property);
-       QByteArray chapter(property->m_name);
-       int ix = chapter.lastIndexOf('.');
-       chapter = chapter.left(ix);
-       QList<Property*>* list = m_chapters.value(chapter, NULL);
-       if (list == NULL) {
-               m_chapters.insert(chapter, list = new QList<Property*>());
-       }
-       list->append(property);
-}
-
diff --git a/appl/reditor/storage.hpp b/appl/reditor/storage.hpp
deleted file mode 100644 (file)
index 84f7c7b..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * storage.hpp
- *
- * License: Public Domain
- * You can use and modify this file without any restriction.
- * Do what you want.
- * No warranties and disclaimer of any damages.
- * You also can use this license: http://www.wtfpl.net
- * The latest sources: https://github.com/republib
- */
-
-#ifndef STORAGE_HPP
-#define STORAGE_HPP
-
-enum PropertyType {
-       PT_UNDEF,
-       PT_INT,
-       PT_STRING,
-       PT_BOOL
-};
-
-class Property {
-public:
-       Property(const char* name, const QString& title, const QString& description,
-           const QString& defaultValue, PropertyType type, const char* limits =
-               NULL) :
-                   m_name(name),
-                   m_title(title),
-                   m_description(description),
-                   m_value(defaultValue),
-                   m_defaultValue(defaultValue),
-                   m_type(type),
-                   m_limits(limits) {
-       }
-
-public:
-       const char* m_name;
-       QString m_title;
-       QString m_description;
-       QString m_value;
-       QString m_defaultValue;
-       PropertyType m_type;
-       const char* m_limits;
-};
-
-class Storage {
-public:
-       Storage(const QString& path, const QString prefix, ReLogger* logger);
-public:
-       void addHistoryEntry(const char* key, const QString& value, char separator,
-           int maxEntries);
-       bool boolValue(const char* name);
-       QStringList& historyAsList(const char* key, QStringList& list,
-           const char* form = NULL);
-       int intValue(const char* name);
-       QString path() const;
-       QString stringValue(const char* name);
-protected:
-       void insertProperty(Property* property);
-protected:
-       QString m_path;
-       QString m_fileHistory;
-       QString m_fileSettings;
-       QMap<const char*, Property*> m_settings;
-       QMap<QByteArray, QList<Property*>*> m_chapters;
-       ReLogger* m_logger;
-};
-
-#endif // STORAGE_HPP
index 64d54a39c6e9a535b8860d5f007e97c59e8c5ec9..5b5c64e4cf5700806740bf101c6c907f57cb6e78 100644 (file)
  * @param logger       the logger
  */
 Workspace::Workspace(const QString& path, ReLogger* logger) :
-           Storage(path, "ws", logger) {
+           ReSettings(path, ".reditor.ws", logger) {
        insertProperty(
-           new Property("editor.tabwidth", QObject::tr("Tabulator width"),
+           new ReProperty("editor.tabwidth", QObject::tr("Tabulator width"),
                QObject::tr("Maximal length of the gap displaying a tabulator"),
                "4", PT_INT, "[1,16]"));
        insertProperty(
-           new Property("history.max_projects",
+           new ReProperty("history.max_projects",
                QObject::tr("Maximal project entries"),
                QObject::tr(
                    "Maximal number of projects in the 'last opened projects'"),
                "20", PT_INT, "[1,100]"));
        insertProperty(
-           new Property("history.max_files", QObject::tr("Maximal file entries"),
+           new ReProperty("history.max_files", QObject::tr("Maximal file entries"),
                QObject::tr("Maximal number of files in the 'last opened files'"),
                "20", PT_INT, "[1,100]"));
 }
index 84ec460f46a0d5c4e177b942c5ae3a75b227771e..81a44c67e36c54ac344fbbf293b355f10b43d4f9 100644 (file)
@@ -396,7 +396,7 @@ QByteArray ReContainer::dump(const char* title, int maxBags,
        QByteArray rc;
        rc.reserve(64000);
        rc.append("=== ").append(title).append('\n');
-       rc.append("Bags: ").append(ReStringUtil::toNumber(m_countBags));
+       rc.append("Bags: ").append(ReStringUtils::toNumber(m_countBags));
        rc.append(" Types: ").append(m_typeList).append('\n');
        // save the current state:
        int safeIxBag = m_ixBag;
@@ -408,7 +408,7 @@ QByteArray ReContainer::dump(const char* title, int maxBags,
        if (maxBags > m_countBags)
                maxBags = m_countBags;
        for (int ixBag = 0; ixBag < maxBags; ixBag++) {
-               rc.append("--- bag ").append(ReStringUtil::toNumber(ixBag)).append(
+               rc.append("--- bag ").append(ReStringUtils::toNumber(ixBag)).append(
                    ":\n");
                nextBag();
                QByteArray item;
@@ -421,9 +421,9 @@ QByteArray ReContainer::dump(const char* title, int maxBags,
                                break;
                        case TAG_INT:
                                iValue = nextInt();
-                               rc.append(" i: ").append(ReStringUtil::toNumber(iValue)).append(
+                               rc.append(" i: ").append(ReStringUtils::toNumber(iValue)).append(
                                    " / ");
-                               rc.append(ReStringUtil::toNumber(iValue, "%x")).append(
+                               rc.append(ReStringUtils::toNumber(iValue, "%x")).append(
                                    separatorItems);
                                break;
                        case TAG_STRING:
@@ -437,11 +437,11 @@ QByteArray ReContainer::dump(const char* title, int maxBags,
                        case TAG_DATA4G:
                                nextData(item, false);
                                rc.append(' ').append((char) currentType).append(": [");
-                               rc.append(ReStringUtil::toNumber(item.length())).append("] ");
+                               rc.append(ReStringUtils::toNumber(item.length())).append("] ");
                                maxLength =
                                    item.length() < maxBlobLength ?
                                        item.length() : maxBlobLength;
-                               rc.append(ReStringUtil::hexDump(item.data(), maxLength, 16))
+                               rc.append(ReStringUtils::hexDump(item.data(), maxLength, 16))
                                    .append(separatorItems);
                                break;
                        default:
index 31abc84ede59921826bb6f315fe74f0958a8c625..dc04fedfe6ecdf52f9e796877619b04083bd9aa2 100644 (file)
 
 #include "base/rebase.hpp"
 
+enum {
+       LOC_DELETE_TREE_1 = LOC_FIRST_OF(LOC_FILE), // 11801
+       LOC_DELETE_TREE_2,              // 11802
+       LOC_DELETE_TREE_3,              // 11803
+};
+
 #if defined __linux__ || defined WIN32
 void* memichr(void* heap, int cc, size_t length) {
        const char* heap2 = reinterpret_cast<const char*>(heap);
@@ -83,6 +89,60 @@ void ReLines::clear() {
        QStringList::clear();
 }
 
+/**
+ * Delete a directory tree.
+ *
+ * @param path         the directory to delete
+ * @param withBase     <code>true</code>: the directory itself will be deleted<br>
+ *                                     <code>false</code>: only all files/dirs inside will be
+ *                                     deleted
+ * @param logger       NULL or error logger
+ * @return                     <code>true</code>:  all files deleted<br>
+ *                                     <code>false</code>: at least one deletion failed
+ */
+bool ReFile::deleteTree(const QString& path, bool withBase, ReLogger* logger) {
+       bool rc = true;
+       QDir dir(path);
+
+       if (dir.exists(path)) {
+               QFileInfo info;
+               QStringList names = dir.entryList(
+                   QDir::NoDotAndDotDot | QDir::System | QDir::Hidden | QDir::AllDirs
+                       | QDir::Files);
+               QStringList::const_iterator it;
+               for (it = names.constBegin(); it != names.constEnd(); ++it) {
+                       QString full(path);
+                       full.append(OS_SEPARATOR_STR).append(*it);
+                       QFileInfo info(full);
+                       if (info.isDir()) {
+                               if (!deleteTree(full, true, logger))
+                                       rc = false;
+                               else if (rmdir(full.toUtf8()) != 0) {
+                                       rc = false;
+                                       if (logger != NULL)
+                                               logger->logv(LOG_ERROR, LOC_DELETE_TREE_1,
+                                                   "cannot delete directory (%d): %s", errno,
+                                                   full.toUtf8().constData());
+                               }
+                       } else {
+                               if (!QFile::remove(full)) {
+                                       rc = false;
+                                       if (logger != NULL)
+                                               logger->logv(LOG_ERROR, LOC_DELETE_TREE_2,
+                                                   "cannot delete file (%d): %s", errno,
+                                                   full.toUtf8().constData());
+                               }
+                       }
+               }
+       }
+       if (withBase && (rmdir(path.toUtf8())) != 0) {
+               rc = false;
+               logger->logv(LOG_ERROR, LOC_DELETE_TREE_3,
+                   "cannot delete directory (%d): %s", errno, path.toUtf8());
+       }
+       return rc;
+}
+
 /**
  * Inserts one or more lines into the lines.
  *
@@ -97,7 +157,7 @@ void ReLines::insertLines(int lineNo, const QString& text, bool withUndo) {
                if (text.isEmpty())
                        count = 1;
                else {
-                       count = ReQStringUtil::countOf(text, '\n');
+                       count = ReQStringUtils::countOf(text, '\n');
                        if (text.at(text.length() - 1) != '\n')
                                count++;
                }
index 5123c68142fb5d0819c606c61d13ef14da5191dd..953b74f65b4bc8a6c5fafd1123b09828023cea3d 100644 (file)
@@ -155,7 +155,8 @@ public:
        static QByteArray& readFromFile(const char* filename, QByteArray& buffer);
        static void writeToFile(const char* filename, const char* content,
            size_t contentLength = (size_t) - 1, const char* mode = "w");
-
+       static bool deleteTree(const QString& path, bool withBase,
+           ReLogger* logger);
 private:
        QByteArray m_endOfLine;
        QString m_filename;
diff --git a/base/ReFileUtils.cpp b/base/ReFileUtils.cpp
new file mode 100644 (file)
index 0000000..7228f05
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ * ReFileUtils.cpp
+ *
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
+ * Do what you want.
+ * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
+ * The latest sources: https://github.com/republib
+ */
+
+#include "base/rebase.hpp"
+
+enum {
+       LOC_DELETE_TREE_1 = LOC_FIRST_OF(LOC_FILE), // 11801
+       LOC_DELETE_TREE_2,              // 11802
+       LOC_DELETE_TREE_3,              // 11803
+};
+
+
+/**
+ * Constructor.
+ */
+ReTreeStatistic::ReTreeStatistic() :
+       m_files(0),
+       m_directories(0),
+       m_fileSizes(0L){
+}
+
+/**
+ * Delete a directory tree.
+ *
+ * @param path         the directory to delete
+ * @param withBase     <code>true</code>: the directory itself will be deleted<br>
+ *                                     <code>false</code>: only all files/dirs inside will be
+ *                                     deleted
+ * @param logger       NULL or error logger
+ * @return                     <code>true</code>:  all files deleted<br>
+ *                                     <code>false</code>: at least one deletion failed
+ */
+bool ReFileUtils::deleteTree(const QString& path, bool withBase, ReLogger* logger){
+       bool rc = true;
+       QDir dir(path);
+
+       if (dir.exists(path)) {
+               QFileInfo info;
+               QStringList names = dir.entryList(QDir::NoDotAndDotDot
+                       | QDir::System | QDir::Hidden | QDir::AllDirs | QDir::Files);
+               QStringList::const_iterator it;
+               for (it = names.constBegin(); it != names.constEnd(); ++it){
+                       QString full(path);
+                       full.append(OS_SEPARATOR_STR).append(*it);
+                       QFileInfo info(full);
+                       if (info.isDir()) {
+                               if (! deleteTree(full, false, logger))
+                                       rc = false;
+                               else if (rmdir(full.toUtf8()) != 0){
+                                       rc = false;
+                                       if (logger != NULL)
+                                               logger->logv(LOG_ERROR, LOC_DELETE_TREE_1,
+                                                                        "cannot delete directory (%d): %s",
+                                                       errno, full.toUtf8().constData());
+                               }
+                       } else {
+                               if (! QFile::remove(full)){
+                                       rc = false;
+                                       if (logger != NULL)
+                                               logger->logv(LOG_ERROR, LOC_DELETE_TREE_2,
+                                                                        "cannot delete file (%d): %s",
+                                                                        errno, full.toUtf8().constData());
+                               }
+                       }
+               }
+       }
+       if (withBase && (rmdir(path.toUtf8())) != 0){
+               rc = false;
+               logger->logv(LOG_ERROR, LOC_DELETE_TREE_3,
+                                        "cannot delete directory (%d): %s",
+                       errno, path.toUtf8());
+       }
+       return rc;
+}
+
+
+
+
+
+/**
+ * Reads a string from a given file.
+ *
+ * @param filename      name of the file to read
+ * @param buffer        OUT: the buffer to write
+ * @return              <code>buffer</code> (for chaining)
+ */
+QByteArray& ReFileUtils::readFromFile(const char* filename, QByteArray& buffer) {
+       FILE* fp = fopen(filename, "r");
+       if (fp != NULL) {
+               struct stat info;
+               stat(filename, &info);
+               buffer.resize(info.st_size);
+               size_t newLength = fread(buffer.data(), 1, info.st_size, fp);
+               if (newLength != (size_t) info.st_size)
+                       buffer.truncate(newLength == (size_t) - 1 ? 0 : newLength);
+               fclose(fp);
+       }
+       return buffer;
+}
+
+/**
+ * @brief Returns the name of a directory in the temp dir.
+ *
+ * If the named directory does not exist it will be created.
+ *
+ * @param node          NULL or the node (name without path)
+ * @param parent        NULL or a node of the parent
+ * @param withSeparator true: the result ends with slash/backslash
+ * @return              the name of an existing directory
+ */
+QByteArray ReFileUtils::tempDir(const char* node, const char* parent,
+       bool withSeparator) {
+#if defined __linux__
+       QByteArray temp("/tmp");
+       static const char* firstVar = "TMP";
+       static const char* secondVar = "TEMP";
+#elif defined WIN32
+       QByteArray temp("c:\\temp");
+       static const char* firstVar = "TEMP";
+       static const char* secondVar = "TMP";
+#endif
+       struct stat info;
+       const char* ptr;
+       if ((ptr = getenv(firstVar)) != NULL)
+               temp = ptr;
+       else if ((ptr = getenv(secondVar)) != NULL)
+               temp = ptr;
+#if defined WIN32
+       temp.replace('\\', '/');
+#endif
+       if (temp.at(temp.length() - 1) != '/')
+               temp += '/';
+       if (parent != NULL) {
+               temp += parent;
+               if (stat(temp.constData(), &info) != 0)
+                       _mkdir(temp.constData());
+               temp += '/';
+       }
+       if (node != NULL) {
+               temp += node;
+               if (stat(temp.data(), &info) != 0)
+                       _mkdir(temp.data());
+               temp += '/';
+       }
+       if (!withSeparator)
+               temp.resize(temp.length() - 1);
+       return temp;
+}
+
+/**
+ * @brief Returns a name of a file in a temporary directory.
+ *
+ * @param node              the file's name without path
+ * @param parent            NULL or the name of a subdirectory the file will be inside
+ * @param deleteIfExists    true: if the file exists it will be removed
+ * @return                  the full name of a temporary file
+ */
+QByteArray ReFileUtils::tempFile(const char* node, const char* parent,
+       bool deleteIfExists) {
+       QByteArray rc(tempDir(parent));
+       if (!rc.endsWith('/'))
+               rc += '/';
+       rc += node;
+       struct stat info;
+       if (deleteIfExists && stat(rc.constData(), &info) == 0)
+               unlink(rc.constData());
+       return rc;
+}
+
+/**
+ * Writes a string into a given file.
+ *
+ * @param filename      name of the file to write
+ * @param content       the content to write
+ * @param contentLength -1: <code>strlen(content)</code><br>
+ *                      otherwise: the length of <code>content</code>
+ * @param mode          file write mode: "w" (write) or "a" (append)
+ */
+void ReFileUtils::writeToFile(const char* filename, const char* content,
+       size_t contentLength, const char* mode) {
+       FILE* fp = fopen(filename, mode);
+       if (fp != NULL) {
+               if (contentLength == (size_t) - 1)
+                       contentLength = strlen(content);
+               fwrite(content, 1, contentLength, fp);
+               fclose(fp);
+       }
+}
diff --git a/base/ReFileUtils.hpp b/base/ReFileUtils.hpp
new file mode 100644 (file)
index 0000000..1fe2154
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * ReFileUtils.hpp
+ *
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
+ * Do what you want.
+ * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
+ * The latest sources: https://github.com/republib
+ */
+
+#ifndef REFILEUTILS_HPP
+#define REFILEUTILS_HPP
+
+/**
+ * Statistic of a directory tree.
+ */
+class ReTreeStatistic {
+       ReTreeStatistic();
+public:
+       int m_files;
+       int m_directories;
+       int64_t m_fileSizes;
+};
+
+/**
+ * Usefull static methods around files/directories missing in QT.
+ */
+class ReFileUtils {
+public:
+       static QByteArray tempDir(const char* node, const char* parent = NULL,
+               bool withSeparator = true);
+       static QByteArray tempFile(const char* node, const char* parent = NULL,
+               bool deleteIfExists = true);
+       static QByteArray& readFromFile(const char* filename, QByteArray& buffer);
+       static void writeToFile(const char* filename, const char* content,
+               size_t contentLength = (size_t) - 1, const char* mode = "w");
+       static bool deleteTree(const QString& path, bool withBase, ReLogger* logger);
+};
+
+#endif // REFILEUTILS_HPP
diff --git a/base/ReQStringUtil.cpp b/base/ReQStringUtil.cpp
deleted file mode 100644 (file)
index 0b5b7c9..0000000
+++ /dev/null
@@ -1,811 +0,0 @@
-/*
- * ReQStringUtil.cpp
- *
- * License: Public Domain
- * You can use and modify this file without any restriction.
- * Do what you want.
- * No warranties and disclaimer of any damages.
- * You also can use this license: http://www.wtfpl.net
- * The latest sources: https://github.com/republib
- */
-
-/** @file
- * @brief Missed operation for <code>ReString</code>s.
- */
-/** @file rplcore/rplqstring.hpp
- *
- * @brief Definitions for missed operation for <code>ReString</code>s.
- */
-#include "base/rebase.hpp"
-#include <QtCore/qmath.h>
-#include <QDateTime>
-
-/**
- * Removes end of line characters if any.
- *
- * @param text  text to inspect
- * @return      <code>text</code> without trailing '\n' and/or '\r'
- */
-ReString ReQStringUtil::chomp(const ReString& text) {
-       int last = text.length() - 1;
-       while (last >= 0 && (text[last] == '\n' || text[last] == '\r')) {
-               last--;
-       }
-       return last == text.length() - 1 ? text : text.left(last + 1);
-}
-
-/**
- * Counts the occurencies of a character in a string.
- *
- * @param value                string to inspect
- * @param toFind       character to find
- * @param start                first index to search
- *
- * @return the count of occurrencies of the character
- */
-int ReQStringUtil::countOf(const QString& value, QChar toFind, int start) {
-       int rc = 0;
-       if (start >= 0) {
-               for (int ix = start; ix < value.length(); ix++)
-                       if (value.at(ix) == toFind)
-                               rc++;
-       }
-       return rc;
-}
-
-/**
- * Tests whether a given character is the last of the string and append it if not.
- *
- * @param value     string to test
- * @param lastChar  the last character
- */
-QString& ReQStringUtil::ensureLastChar(QString& value, QChar lastChar) {
-       if (value.isEmpty() || value.at(value.length() - 1) != lastChar)
-               value += lastChar;
-       return value;
-}
-
-/**
- * Extracts the extension of a filename.
- *
- * @param filename  the filename (with or without path)
- * @return          "": no extension found<br>
- *                  otherwise: the extension of <code>filename</code>
- */
-ReString ReQStringUtil::extensionOf(const ReString& filename) {
-       QString rc;
-       int index = filename.lastIndexOf('.');
-       int index2 = 0;
-       if (index > 0) {
-               index2 = filename.lastIndexOf('/');
-               if (index2 >= 0) {
-                       if (index > index2)
-                               rc = filename.mid(index);
-               } else {
-#if defined __linux__
-                       rc = filename.mid(index);
-#elif defined WIN32
-                       index2 = filename.lastIndexOf('\\');
-                       if (index2 < 0 || index > index2)
-                       rc = filename.mid(index);
-#endif
-               }
-       }
-       return rc;
-}
-
-/**
- * @brief Determines the length and vlaue of an integer.
- *
- * @param text      the number as text
- * @param start     the first index to inspect
- * @param radix     the base of the number sytem: 8 (octal), 10 or 16
- * @param pValue     OUT: the value of the integer. May be NULL
- *
- * @return          <=0: no integer found
- *                  otherwise: the length of the integer
- */
-int ReQStringUtil::lengthOfUInt64(const ReString& text, int start, int radix,
-    quint64* pValue) {
-       int inputLength = text.size();
-       int64_t value = 0;
-       int ix = start;
-       int cc;
-       if (radix == 10) {
-               while (ix < inputLength) {
-                       if ((cc = text[ix].unicode()) >= '0' && cc <= '9')
-                               value = value * 10 + cc - '0';
-                       else
-                               break;
-                       ix++;
-               }
-       } else if (radix == 16) {
-               while (ix < inputLength) {
-                       if ((cc = text[ix].unicode()) >= '0' && cc <= '9')
-                               value = value * 16 + cc - '0';
-                       else if (cc >= 'A' && cc <= 'F')
-                               value = value * 16 + cc - 'A' + 10;
-                       else if (cc >= 'a' && cc <= 'f')
-                               value = value * 16 + cc - 'a' + 10;
-                       else
-                               break;
-                       ix++;
-               }
-       } else if (radix == 8) {
-               while (ix < inputLength) {
-                       if ((cc = text[ix].unicode()) >= '0' && cc <= '7')
-                               value = value * 8 + cc - '0';
-                       else
-                               break;
-                       ix++;
-               }
-       } else {
-               throw ReException("ReQStringUtil::lengthOfInt(): wrong radix: %d",
-                   radix);
-       }
-       if (pValue != NULL)
-               *pValue = value;
-       return ix - start;
-}
-/**
- * @brief Determines the length and value of an unsigned integer.
- *
- * @param text      the number as text
- * @param start     the first index to inspect
- * @param radix     the base of the number sytem: 8 (octal), 10 or 16
- * @param pValue     OUT: the value of the integer. May be NULL
- *
- * @return          0: no integer found
- *                  otherwise: the length of the integer
- */
-int ReQStringUtil::lengthOfUInt(const ReString& text, int start, int radix,
-    uint* pValue) {
-       quint64 value;
-       int rc = lengthOfUInt64(text, start, radix, &value);
-       if (pValue != NULL)
-               *pValue = (uint) value;
-       return rc;
-}
-
-/**
- * Returns the length of a date in a string.
- *
- * The syntax of a time is 'dd.mm.yyyy' or 'yyyy.mm.dd'.
- *
- * @param text  text to inspect
- * @param start the first index in <code>text</code> to inspect
- * @param value OUT: the value of the found date. Not changed if result is 0.<br>
- *              May be NULL
- * @return      0: no date found<br>
- *              otherwise: the length of the date in the string
- */
-int ReQStringUtil::lengthOfDate(const ReString& text, int start, QDate* value) {
-       uint day = 0;
-       uint month = 0;
-       uint year = 0;
-       int length = lengthOfUInt(text, start, 10, &year);
-       switch (length) {
-       case 1:
-       case 2:
-               day = year;
-               year = 0;
-               break;
-       case 4:
-               break;
-       default:
-               length = 0;
-               break;
-       }
-       int length2;
-       start += length;
-       skipExpected(text, '.', start, length);
-       if (length > 0) {
-               length2 = lengthOfUInt(text, start, 10, &month);
-               if (length2 < 1 || length2 > 2)
-                       length = 0;
-               else {
-                       start += length2;
-                       length += length2;
-               }
-       }
-       skipExpected(text, '.', start, length);
-       if (year > 0) {
-               length2 = lengthOfUInt(text, start, 10, &day);
-               if (length2 < 1 || length2 > 2)
-                       length = 0;
-               else {
-                       start += length2;
-                       length += length2;
-               }
-       } else {
-               length2 = lengthOfUInt(text, start, 10, &year);
-               if (length2 != 4)
-                       length = 0;
-               else {
-                       start += length2;
-                       length += length2;
-               }
-       }
-       if (day < 1 || day > 31 || month < 1 || month > 12 || year < 1970
-           || year > 2100)
-               length = 0;
-       if (length != 0 && value != NULL)
-               *value = QDate(year, month, day);
-       return length;
-}
-
-/**
- * Returns the length of a date and/or time in a string.
- *
- * @param text          text to inspect
- * @param start         the first index in <code>text</code> to inspect
- * @param allowDateOnly <code>false</code>: if the date is not followed by
- *                      a time the result will be 0
- * @param allowTimeOnly <code>false</code>: if no date is found at the given
- *                      text position the result will be 0
- * @param value         the value of the found date. Not changed if result is 0.<br>
- *                      May be NULL
- * @return              0: no date found<br>
- *                      otherwise: the length of the date in the string
- */
-int ReQStringUtil::lengthOfDateTime(const ReString& text, int start,
-    bool allowDateOnly, bool allowTimeOnly, QDateTime* value) {
-       QDate date;
-       QTime time;
-       int length = lengthOfDate(text, start, &date);
-       if (length == 0) {
-               if (allowTimeOnly) {
-                       date = QDate::currentDate();
-                       length = lengthOfTime(text, start, &time);
-               }
-       } else {
-               if (start + length + 1 + 3 <= text.length()) {
-                       start += length;
-                       int length2 = 0;
-                       if (!text[start].isDigit()) {
-                               QTime time2;
-                               length2 = lengthOfTime(text, start + 1, &time2);
-                               if (length2 == 0 && !allowDateOnly)
-                                       length = 0;
-                               else if (length2 > 0) {
-                                       length += 1 + length2;
-                                       time = time2;
-                               }
-                       }
-               }
-       }
-       if (length > 0 && value != NULL)
-               *value = QDateTime(date, time);
-       return length;
-}
-/**
- * Returns the length of a time in a string.
- *
- * The syntax of a time is hh:mm[:ss]
- *
- * @param text  text to inspect
- * @param start the first index in <code>text</code> to inspect
- * @param value OUT: the value of the found time. Not changed if result is 0.<br>
- *              May be NULL
- * @return  0: no date found<br>
- *          otherwise: the length of the date in the string
- */
-int ReQStringUtil::lengthOfTime(const ReString& text, int start, QTime* value) {
-       uint hour = 0;
-       uint minute = 0;
-       uint sec = 0;
-       int length = lengthOfUInt(text, start, 10, &hour);
-       if (length > 0 && hour > 23)
-               length = 0;
-       if (length > 0) {
-               start += length;
-       }
-       int length2;
-       skipExpected(text, ':', start, length);
-       if (length > 0) {
-               length2 = lengthOfUInt(text, start, 10, &minute);
-               if (length2 < 1 || length2 > 2 || minute >= 60)
-                       length = 0;
-               else
-                       start += length2, length += length2;
-       }
-       if (length > 0 && start < text.length() && text[start] == ':') {
-               length++;
-               start++;
-               length2 = lengthOfUInt(text, start, 10, &sec);
-               if (length2 < 1 || length2 > 2 || sec >= 60)
-                       length = 0;
-               else
-                       start += length2, length += length2;
-       }
-       if (length != 0 && value != NULL)
-               *value = QTime(hour, minute, sec);
-       return length;
-}
-
-/**
- * @brief Determines the length and value of a floting point number.
- *
- * @param text      the number as text
- * @param start     the first index to inspect
- * @param pValue     OUT: the value of the integer. May be NULL
- *
- * @return          <=0: no real number found
- *                  otherwise: the length of the floating point number
- */
-int ReQStringUtil::lengthOfReal(const ReString& text, int start,
-    qreal* pValue) {
-       int inputLength = text.size();
-       qreal value = 0.0;
-       int cc;
-       int ix = start;
-       while (ix < inputLength) {
-               if ((cc = text[ix].unicode()) >= '0' && cc <= '9')
-                       value = value * 10 + (cc - '0');
-               else
-                       break;
-               ix++;
-       }
-       // found: a digit has been found (in front of or behind the '.'
-       bool found = ix > start;
-       if (ix < inputLength && text[ix].unicode() == '.') {
-               ix++;
-       }
-       if (ix < inputLength && text[ix].isDigit()) {
-               found = true;
-               qreal divisor = 1;
-               qreal precision = 0;
-               while (ix < inputLength && (cc = text[ix].unicode()) >= '0' && cc <= '9') {
-                       divisor *= 10;
-                       precision = precision * 10 + cc - '0';
-                       ix++;
-               }
-               value += precision / divisor;
-       } else if (!found) {
-               ix = start;
-       }
-       if (found && ix + 1 < inputLength && toupper(text[ix].unicode()) == 'E') {
-               int savePoint = ix;
-               ix++;
-               bool negative = false;
-               if ((cc = text[ix].unicode()) == '+')
-                       ix++;
-               else if (cc == '-') {
-                       ix++;
-                       negative = true;
-               }
-               if (ix >= inputLength || !text[ix].isDigit())
-                       ix = savePoint;
-               else {
-                       int exponent = 0;
-                       while (ix < inputLength && text[ix].isDigit()) {
-                               exponent = exponent * 10 + text[ix].unicode() - '0';
-                               ix++;
-                       }
-                       if (negative)
-                               value /= qPow(10, exponent);
-                       else
-                               value *= qPow(10, exponent);
-               }
-       }
-       if (pValue)
-               *pValue = value;
-       return found ? ix - start : 0;
-}
-
-/**
- * Extracts the node of a filename.
- *
- * The node is the filename without path.
- *
- * @param filename  the filename (with or without path)
- * @return          the node of <code>filename</code>
- */
-ReString ReQStringUtil::nodeOf(const ReString& filename) {
-       QString rc;
-
-       int index = filename.lastIndexOf('/');
-       if (index >= 0)
-               rc = filename.mid(index + 1);
-       else {
-#if defined WIN32
-               index = filename.lastIndexOf('\\');
-               if (index < 0)
-               rc = filename;
-               else
-               rc = filename.mid(index + 1);
-#endif
-       }
-       return rc;
-}
-
-/**
- * Appends a relative path to base directory name (absolute or relative).
- *
- * @param base  the base directory, relative or absolute
- * @param toAdd a relative path (relative to <code>base</code>)
- * @return      the combined path
- */
-QString ReQStringUtil::pathAppend(const QString& base, const QString& path) {
-       QString rc;
-       if (!base.isEmpty())
-               rc = QDir::cleanPath(base + QDir::separator() + path);
-       else {
-               rc = path;
-               rc.replace("\\", "/");
-               if (path.startsWith("/"))
-                       rc.remove(0, 1);
-       }
-       return rc;
-}
-
-/**
- * Replaces the file extension of a filename.
- *
- * @param path  the filename to change
- * @param ext   the new file extension, e.g. ".txt"
- * @return      path with a new file extension
- */
-
-QString ReQStringUtil::replaceExtension(const QString& path,
-    const QString& ext) {
-       QString oldExt = extensionOf(path);
-       QString rc;
-       if (oldExt.isEmpty())
-               rc = path + ext;
-       else
-               rc = path.mid(0, path.size() - oldExt.size()) + ext;
-       return rc;
-}
-/**
- * Replaces placeholders by their values.
- *
- * Example for a placeholder: ${path}
- *
- * @param text          IN/OUT: the text to inspect/change
- * @param placeholders  a hashmap with (name, value) pairs, e.g. ("path", "/")
- * @param error         OUT: NULL or the error message (unknown name)
- * @return              <code>true</code>: success<br>
- *                      <code>false</code>: unknown name found (not replaced)
- */
-bool ReQStringUtil::replacePlaceholders(QString& text,
-    const QMap<QString, QString>& placeholders, QString* error) {
-       int start = 0;
-       bool rc = true;
-       QString name;
-       QMap<QString, QString>::const_iterator it;
-       while (start >= 0) {
-               start = text.indexOf("${", start);
-               if (start < 0)
-                       break;
-               int end = text.indexOf('}', start + 1);
-               if (end < 0)
-                       break;
-               name = text.mid(start + 2, end - start - 2);
-               it = placeholders.find(name);
-               if (it == placeholders.end()) {
-                       rc = false;
-                       if (error != NULL) {
-                               *error = QObject::tr("unknown placeholder: ") + name;
-                       }
-               } else {
-                       text = text.replace("${" + name + "}", *it);
-               }
-               start += (*it).length();
-       }
-       return rc;
-}
-
-/**
- * Skips a character in a text at a given position if it has an expected value.
- *
- * @param text      text to inspect
- * @param expected  the character which is expected
- * @param index     IN/OUT: the position of the expected character.
- *                  Will be incremented if the expected character is found
- * @param length    IN/OUT: IN: 0: do nothing<br>
- *                  OUT: 0: the expected character was not found.
- *                  otherwise: the length is incremented
- */
-void ReQStringUtil::skipExpected(const ReString& text, QChar expected,
-    int& index, int& length) {
-       if (length == 0) {
-               // error state, do nothing
-       } else if (index >= text.length() || text[index] != expected) {
-               length = 0;
-       } else {
-               index++;
-               length++;
-       }
-}
-
-/**
- * @brief Converts a ReString into an utf-8 string
- *
- * The expression <code>qstring.toUtf8().constData()</code> is not allowed
- * in a variable argument list like sprintf. This is a thread save workaround.
- *
- * @param source        string to convert
- * @param buffer        OUT: target buffer
- * @param bufferSize    size of the target buffer
- * @return              <code>buffer</code>
- */
-char*ReQStringUtil::utf8(const ReString& source, char buffer[],
-    size_t bufferSize) {
-       QByteArray val = source.toUtf8();
-       if (val.length() < (int) bufferSize)
-               bufferSize = val.length() + 1;
-       memcpy(buffer, val.constData(), bufferSize - 1);
-       buffer[bufferSize - 1] = '\0';
-       return buffer;
-}
-
-class ReParserException: public ReException {
-public:
-       ReParserException(const QString& message) :
-                   ReException(),
-                   m_message(message) {
-       }
-public:
-       QString m_message;
-};
-/**
- * Constructor.
- *
- * @param expr       an expression, e.g. "10*1024kByte+5MiByte"
- * @param unitList   description of the allowed units with its factor<br>
- *                   example: "kibyte:1024;kbyte:1000;mibyte:1048576;mbyte:1000000"
- */
-ReUnitParser::ReUnitParser(const QString& expr, const char* unitList,
-    bool parseAtOnce) :
-           m_result(0),
-           m_expr(expr),
-           m_message(),
-           m_unitList(unitList) {
-       normalize();
-       if (parseAtOnce)
-               parse();
-}
-
-/**
- * Returns the result of the expression as a 64 bit integer.
- *
- * @param defaultValue  the result if the expression was not valid
- * @return              <code>defaultValue</code>: the result was not valid<br>
- *                      the result as a 64 bit integer
- */
-int64_t ReUnitParser::asInt64(int64_t defaultValue) {
-       return m_message.isEmpty() ? m_result : defaultValue;
-}
-/**
- * Returns the result of the expression as an integer.
- *
- * @param defaultValue  the result if the expression was not valid
- * @return              <code>defaultValue</code>: the result was not valid<br>
- *                      the result as an integer
- */
-int ReUnitParser::asInt(int defaultValue) {
-       return m_message.isEmpty() ? (int) m_result : defaultValue;
-}
-/**
- * Returns the result of the expression as floating point number.
- *
- * @param defaultValue  the result if the expression was not valid
- * @return              <code>defaultValue</code>: the result was not valid<br>
- *                      the result as a floating point
- */
-real_t ReUnitParser::asReal(real_t defaultValue) {
-       return m_message.isEmpty() ? (real_t) m_result : defaultValue;
-}
-
-/**
- * Returns an empty string or the error message.
- *
- * @return  "": no error occurred<br>
- *          otherwise: the error message
- */
-const QString& ReUnitParser::errorMessage() const {
-       return m_message;
-}
-
-/**
- * Returns whether the given expression is valid.
- *
- * @return  <code>true</code>: the expression is valid, a result was calculated
- */
-bool ReUnitParser::isValid() const {
-       return m_message.isEmpty();
-}
-
-/**
- * @brief Normalizes the internal stored unit expression.
- */
-void ReUnitParser::normalize() {
-       // Remove the blanks:
-       for (int ii = m_expr.length() - 1; ii >= 0; ii--) {
-               if (m_expr[ii].isSpace())
-                       m_expr.remove(ii, 1);
-       }
-       // Replace the '-' operator by '+' as operator and '-' as sign:
-       // This makes the syntax easier to parse: only one sum operator ('+').
-       for (int ii = m_expr.length() - 1; ii > 0; ii--) {
-               if (m_expr[ii] == '-' && m_expr[ii - 1] != '+'
-                   && m_expr[ii - 1] != '*') {
-                       m_expr.insert(ii, '+');
-               }
-       }
-}
-
-/**
- * Evaluate the expression.
- */
-void ReUnitParser::parse() {
-       QStringList addends = m_expr.split("+");
-       QStringList::const_iterator it;
-       try {
-               m_result = 0;
-               for (it = addends.begin(); it != addends.end(); ++it) {
-                       QStringList factors = it->split("*");
-                       QStringList::const_iterator it2;
-                       int64_t product = 1;
-                       for (it2 = factors.begin(); it2 != factors.end(); ++it2) {
-                               QStringList powerOperands = it2->split("^");
-                               if (powerOperands.count() > 2)
-                                       throw ReParserException(
-                                           QObject::tr(
-                                               "more than 2 power operators, e.g. '2^3^4'"));
-                               QStringList::const_iterator it3 = powerOperands.begin();
-                               QString op = *it3;
-                               bool isNeg = op.startsWith("-");
-                               if (isNeg)
-                                       op = op.mid(1);
-                               uint64_t value = valueOf(op);
-                               if (powerOperands.count() > 1) {
-                                       uint64_t fac = value;
-                                       uint64_t exponent = valueOf(*++it3);
-                                       if (qLn(value) * qLn(exponent) >= qLn(qPow(2.0, 64)))
-                                               throw ReParserException(
-                                                   QObject::tr(
-                                                       "number overflow while power operation"));
-                                       for (int ii = 1; ii < (int) exponent; ii++)
-                                               value = value * fac;
-                               }
-                               product *= value;
-                               if (isNeg)
-                                       product = -product;
-                       }
-                       m_result += product;
-               }
-
-       } catch (ReParserException& e) {
-               m_message = e.m_message;
-       }
-}
-
-/**
- * Calculates the value of a number or a (number, unit) pair.
- *
- * @param value   a non negative number or a number followed by a unit<br>
- *                only units defined in m_unitList are allowed<br>
- *                examples: "4kByte" returns 4000, "4kibyte" returns 4096
- * @return        the value of the number multiplied by the factor given by the unit
- * @throws  ReParserException
- */
-uint64_t ReUnitParser::valueOf(const QString& value) const {
-       uint64_t rc = 0;
-       int ix = ReQStringUtil::lengthOfUInt64(value, 0, 10, &rc);
-       if (ix == 0)
-               throw ReParserException(QObject::tr("number expected: ") + value);
-       QString unit = value.mid(ix);
-       if (!unit.isEmpty()) {
-               QStringList units = QString(m_unitList).split(";");
-               QStringList::const_iterator it;
-               bool found = false;
-               for (it = units.begin(); it != units.end(); ++it) {
-                       QStringList pair = it->split(":");
-                       if (pair.count() == 0)
-                               throw ReParserException(
-                                   QObject::tr(
-                                       "missing ':' in unit definition, e.g. 'k:1000': ")
-                                       + *it);
-                       if (pair.count() > 2)
-                               throw ReParserException(
-                                   QObject::tr("too many  ':' in unit definition: ") + *it);
-                       bool ok = false;
-                       QString unit2 = *pair.begin();
-                       QString factor = *++pair.begin();
-                       uint64_t nFactor = factor.toLongLong(&ok);
-                       if (!ok)
-                               throw ReParserException(QObject::tr("not a number: ") + factor);
-                       if (unit2.startsWith(unit, Qt::CaseInsensitive)) {
-                               rc *= nFactor;
-                               found = true;
-                               break;
-                       }
-               }
-               if (!found)
-                       throw ReParserException(
-                           QObject::tr("unknown unit '$1'. Allowed: ").arg(unit)
-                               + QString(m_unitList));
-       }
-       return rc;
-}
-
-/**
- * Constructor.
- *
- * @param expr       an expression, e.g. "10*1024kByte+5MiByte"
- */
-ReSizeParser::ReSizeParser(const QString& expr) :
-           ReUnitParser(expr, "byte:1;kbyte:1000;kibyte:1024;"
-                   "mbyte:1000000;mibyte:1048576;"
-                   "gbyte:1000000000;gibyte:1073741824;"
-                   "tbyte:1000000000000;tibyte:1099511627776") {
-}
-/**
- * Constructor.
- *
- * @param expr     an expression, e.g. "3*3days-5min+3weeks"
- */
-ReDateTimeParser::ReDateTimeParser(const QString& expr) :
-           ReUnitParser("", "minutes:60;hours:3600;days:86400;weeks:604800", false) {
-       parseDateTime(expr);
-}
-
-/**
- * Returns the parser result as a <code>QDateTime</code> instance.
- *
- * @return the parse result. If invalid input the result is the begin of the epoche
- */
-QDateTime ReDateTimeParser::asDateTime() const {
-       return m_dateTime;
-}
-
-/**
- * Parses a date/time expression.
- *
- * Syntax: { "now" | <date> [<time>] | <time> } [ { '+' | '-' } <second expr>]
- *          | <second expr>
- * @param expr  expression to parse, e.g. "now+3weeks-5*30days"
- * @return      start of the epoche: no valid input<br>
- *              othewise: the calculated date/time
- */
-QDateTime ReDateTimeParser::parseDateTime(const QString& expr) {
-       m_expr = expr;
-       normalize();
-       QDateTime rc = QDateTime::currentDateTime();
-       int64_t relativeSeconds = 0;
-       if (m_expr.isEmpty())
-               m_message = QObject::tr("empty string is not a date/time");
-       else {
-               QDateTime dateTime;
-               int length2 = 0;
-               bool checkSum = true;
-               if (m_expr.startsWith("now", Qt::CaseInsensitive)) {
-                       m_expr.remove(0, 3);
-               } else if ((length2 = ReQStringUtil::lengthOfDateTime(m_expr, 0, true,
-                   true, &dateTime)) > 0) {
-                       rc = dateTime;
-                       m_expr.remove(0, length2);
-               } else {
-                       checkSum = false;
-                       parse();
-                       // meaning is "older than x seconds"
-                       relativeSeconds = m_result = -m_result;
-               }
-               if (checkSum) {
-                       if (m_expr.startsWith("+")) {
-                               m_expr.remove(0, 1);
-                       }
-                       if (!m_expr.isEmpty()) {
-                               parse();
-                               relativeSeconds = m_result;
-                       }
-               }
-       }
-       rc.setMSecsSinceEpoch(
-           isValid() ? rc.toMSecsSinceEpoch() + 1000 * relativeSeconds : 0);
-       m_dateTime = rc;
-       return rc;
-}
-
diff --git a/base/ReQStringUtil.hpp b/base/ReQStringUtil.hpp
deleted file mode 100644 (file)
index 2a860c6..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * ReQStringUtil.hpp
- *
- * License: Public Domain
- * You can use and modify this file without any restriction.
- * Do what you want.
- * No warranties and disclaimer of any damages.
- * You also can use this license: http://www.wtfpl.net
- * The latest sources: https://github.com/republib
- */
-
-#ifndef RPLQSTRING_HPP
-#define RPLQSTRING_HPP
-
-class ReQStringUtil {
-public:
-       static ReString chomp(const ReString& text);
-       static int countOf(const QString& value, QChar toFind, int start = 0);
-       static QString& ensureLastChar(QString& value, QChar lastChar);
-       static ReString extensionOf(const ReString& filename);
-       static int lengthOfDate(const ReString& text, int start = 0, QDate* value =
-           NULL);
-       static int lengthOfDateTime(const ReString& text, int start = 0,
-           bool allowDateOnly = true, bool allowTimeOnly = true, QDateTime* value =
-               NULL);
-       static int lengthOfReal(const ReString& text, int start = 0, qreal* value =
-           NULL);
-       static int lengthOfTime(const ReString& text, int start = 0, QTime* value =
-           NULL);
-       static int lengthOfUInt64(const ReString& text, int start = 0, int radix =
-           10, uint64_t* value = NULL);
-       static int lengthOfUInt(const ReString& text, int start, int radix,
-           uint* pValue);
-       /**
-        * Returns the path with native path separators.
-        *
-        * @param path   the path to change
-        * @return       the path with native path separators
-        */
-       inline static QString nativePath(const QString& path) {
-#if defined WIN32
-               QString rc(path); return rc.replace("/", "\\");
-#else
-               return path;
-#endif
-       }
-       static ReString nodeOf(const ReString& filename);
-       static QString pathAppend(const QString& base, const QString& path);
-       static QString replaceExtension(const QString& path, const QString& ext);
-       static bool replacePlaceholders(QString& text,
-           const QMap<QString, QString>& placeholders, QString* error);
-       static void skipExpected(const ReString& text, QChar expected, int& index,
-           int& length);
-       /**
-        * @brief Returns the value of a hexadecimal digit.
-        *
-        * @param digit     a (unicode) character
-        * @return          -1: not a hexadecimal digit<br>
-        *                  otherwise: the value, e.g. 10 for 'a'
-        */
-       inline static int valueOfHexDigit(int digit) {
-               return digit >= '0' && digit <= '9' ? digit - '0' :
-                      digit >= 'A' && digit <= 'F' ? digit - 'A' + 10 :
-                      digit >= 'a' && digit <= 'f' ? digit - 'a' + 10 : -1;
-       }
-       static char* utf8(const ReString& source, char buffer[], size_t bufferSize);
-};
-
-class ReUnitParser {
-public:
-       ReUnitParser(const QString& expr, const char* unitList, bool parseAtOnce =
-           true);
-public:
-       bool isValid() const;
-       const QString& errorMessage() const;
-       int64_t asInt64(int64_t defaultValue = -1ll);
-       int asInt(int defaultValue = -1);
-       real_t asReal(real_t defaultValue = -1.0);
-protected:
-       void parse();
-       void normalize();
-       uint64_t valueOf(const QString& value) const;
-protected:
-       int64_t m_result;
-       QString m_expr;
-       QString m_message;
-       const char* m_unitList;
-};
-
-class ReSizeParser: public ReUnitParser {
-public:
-       ReSizeParser(const QString& expr);
-};
-
-class ReDateTimeParser: public ReUnitParser {
-public:
-       ReDateTimeParser(const QString& expr);
-public:
-       QDateTime parseDateTime(const QString& expr);
-       QDateTime asDateTime() const;
-private:
-       QDateTime m_dateTime;
-};
-
-#endif // RPLQSTRING_HPP
diff --git a/base/ReQStringUtils.cpp b/base/ReQStringUtils.cpp
new file mode 100644 (file)
index 0000000..e1f5d25
--- /dev/null
@@ -0,0 +1,811 @@
+/*
+ * ReQStringUtil.cpp
+ *
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
+ * Do what you want.
+ * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
+ * The latest sources: https://github.com/republib
+ */
+
+/** @file
+ * @brief Missed operation for <code>ReString</code>s.
+ */
+/** @file rplcore/rplqstring.hpp
+ *
+ * @brief Definitions for missed operation for <code>ReString</code>s.
+ */
+#include "base/rebase.hpp"
+#include <QtCore/qmath.h>
+#include <QDateTime>
+
+/**
+ * Removes end of line characters if any.
+ *
+ * @param text  text to inspect
+ * @return      <code>text</code> without trailing '\n' and/or '\r'
+ */
+ReString ReQStringUtils::chomp(const ReString& text) {
+       int last = text.length() - 1;
+       while (last >= 0 && (text[last] == '\n' || text[last] == '\r')) {
+               last--;
+       }
+       return last == text.length() - 1 ? text : text.left(last + 1);
+}
+
+/**
+ * Counts the occurencies of a character in a string.
+ *
+ * @param value                string to inspect
+ * @param toFind       character to find
+ * @param start                first index to search
+ *
+ * @return the count of occurrencies of the character
+ */
+int ReQStringUtils::countOf(const QString& value, QChar toFind, int start) {
+       int rc = 0;
+       if (start >= 0) {
+               for (int ix = start; ix < value.length(); ix++)
+                       if (value.at(ix) == toFind)
+                               rc++;
+       }
+       return rc;
+}
+
+/**
+ * Tests whether a given character is the last of the string and append it if not.
+ *
+ * @param value     string to test
+ * @param lastChar  the last character
+ */
+QString& ReQStringUtils::ensureLastChar(QString& value, QChar lastChar) {
+       if (value.isEmpty() || value.at(value.length() - 1) != lastChar)
+               value += lastChar;
+       return value;
+}
+
+/**
+ * Extracts the extension of a filename.
+ *
+ * @param filename  the filename (with or without path)
+ * @return          "": no extension found<br>
+ *                  otherwise: the extension of <code>filename</code>
+ */
+ReString ReQStringUtils::extensionOf(const ReString& filename) {
+       QString rc;
+       int index = filename.lastIndexOf('.');
+       int index2 = 0;
+       if (index > 0) {
+               index2 = filename.lastIndexOf('/');
+               if (index2 >= 0) {
+                       if (index > index2)
+                               rc = filename.mid(index);
+               } else {
+#if defined __linux__
+                       rc = filename.mid(index);
+#elif defined WIN32
+                       index2 = filename.lastIndexOf('\\');
+                       if (index2 < 0 || index > index2)
+                       rc = filename.mid(index);
+#endif
+               }
+       }
+       return rc;
+}
+
+/**
+ * @brief Determines the length and vlaue of an integer.
+ *
+ * @param text      the number as text
+ * @param start     the first index to inspect
+ * @param radix     the base of the number sytem: 8 (octal), 10 or 16
+ * @param pValue     OUT: the value of the integer. May be NULL
+ *
+ * @return          <=0: no integer found
+ *                  otherwise: the length of the integer
+ */
+int ReQStringUtils::lengthOfUInt64(const ReString& text, int start, int radix,
+    quint64* pValue) {
+       int inputLength = text.size();
+       int64_t value = 0;
+       int ix = start;
+       int cc;
+       if (radix == 10) {
+               while (ix < inputLength) {
+                       if ((cc = text[ix].unicode()) >= '0' && cc <= '9')
+                               value = value * 10 + cc - '0';
+                       else
+                               break;
+                       ix++;
+               }
+       } else if (radix == 16) {
+               while (ix < inputLength) {
+                       if ((cc = text[ix].unicode()) >= '0' && cc <= '9')
+                               value = value * 16 + cc - '0';
+                       else if (cc >= 'A' && cc <= 'F')
+                               value = value * 16 + cc - 'A' + 10;
+                       else if (cc >= 'a' && cc <= 'f')
+                               value = value * 16 + cc - 'a' + 10;
+                       else
+                               break;
+                       ix++;
+               }
+       } else if (radix == 8) {
+               while (ix < inputLength) {
+                       if ((cc = text[ix].unicode()) >= '0' && cc <= '7')
+                               value = value * 8 + cc - '0';
+                       else
+                               break;
+                       ix++;
+               }
+       } else {
+               throw ReException("ReQStringUtil::lengthOfInt(): wrong radix: %d",
+                   radix);
+       }
+       if (pValue != NULL)
+               *pValue = value;
+       return ix - start;
+}
+/**
+ * @brief Determines the length and value of an unsigned integer.
+ *
+ * @param text      the number as text
+ * @param start     the first index to inspect
+ * @param radix     the base of the number sytem: 8 (octal), 10 or 16
+ * @param pValue     OUT: the value of the integer. May be NULL
+ *
+ * @return          0: no integer found
+ *                  otherwise: the length of the integer
+ */
+int ReQStringUtils::lengthOfUInt(const ReString& text, int start, int radix,
+    uint* pValue) {
+       quint64 value;
+       int rc = lengthOfUInt64(text, start, radix, &value);
+       if (pValue != NULL)
+               *pValue = (uint) value;
+       return rc;
+}
+
+/**
+ * Returns the length of a date in a string.
+ *
+ * The syntax of a time is 'dd.mm.yyyy' or 'yyyy.mm.dd'.
+ *
+ * @param text  text to inspect
+ * @param start the first index in <code>text</code> to inspect
+ * @param value OUT: the value of the found date. Not changed if result is 0.<br>
+ *              May be NULL
+ * @return      0: no date found<br>
+ *              otherwise: the length of the date in the string
+ */
+int ReQStringUtils::lengthOfDate(const ReString& text, int start, QDate* value) {
+       uint day = 0;
+       uint month = 0;
+       uint year = 0;
+       int length = lengthOfUInt(text, start, 10, &year);
+       switch (length) {
+       case 1:
+       case 2:
+               day = year;
+               year = 0;
+               break;
+       case 4:
+               break;
+       default:
+               length = 0;
+               break;
+       }
+       int length2;
+       start += length;
+       skipExpected(text, '.', start, length);
+       if (length > 0) {
+               length2 = lengthOfUInt(text, start, 10, &month);
+               if (length2 < 1 || length2 > 2)
+                       length = 0;
+               else {
+                       start += length2;
+                       length += length2;
+               }
+       }
+       skipExpected(text, '.', start, length);
+       if (year > 0) {
+               length2 = lengthOfUInt(text, start, 10, &day);
+               if (length2 < 1 || length2 > 2)
+                       length = 0;
+               else {
+                       start += length2;
+                       length += length2;
+               }
+       } else {
+               length2 = lengthOfUInt(text, start, 10, &year);
+               if (length2 != 4)
+                       length = 0;
+               else {
+                       start += length2;
+                       length += length2;
+               }
+       }
+       if (day < 1 || day > 31 || month < 1 || month > 12 || year < 1970
+           || year > 2100)
+               length = 0;
+       if (length != 0 && value != NULL)
+               *value = QDate(year, month, day);
+       return length;
+}
+
+/**
+ * Returns the length of a date and/or time in a string.
+ *
+ * @param text          text to inspect
+ * @param start         the first index in <code>text</code> to inspect
+ * @param allowDateOnly <code>false</code>: if the date is not followed by
+ *                      a time the result will be 0
+ * @param allowTimeOnly <code>false</code>: if no date is found at the given
+ *                      text position the result will be 0
+ * @param value         the value of the found date. Not changed if result is 0.<br>
+ *                      May be NULL
+ * @return              0: no date found<br>
+ *                      otherwise: the length of the date in the string
+ */
+int ReQStringUtils::lengthOfDateTime(const ReString& text, int start,
+    bool allowDateOnly, bool allowTimeOnly, QDateTime* value) {
+       QDate date;
+       QTime time;
+       int length = lengthOfDate(text, start, &date);
+       if (length == 0) {
+               if (allowTimeOnly) {
+                       date = QDate::currentDate();
+                       length = lengthOfTime(text, start, &time);
+               }
+       } else {
+               if (start + length + 1 + 3 <= text.length()) {
+                       start += length;
+                       int length2 = 0;
+                       if (!text[start].isDigit()) {
+                               QTime time2;
+                               length2 = lengthOfTime(text, start + 1, &time2);
+                               if (length2 == 0 && !allowDateOnly)
+                                       length = 0;
+                               else if (length2 > 0) {
+                                       length += 1 + length2;
+                                       time = time2;
+                               }
+                       }
+               }
+       }
+       if (length > 0 && value != NULL)
+               *value = QDateTime(date, time);
+       return length;
+}
+/**
+ * Returns the length of a time in a string.
+ *
+ * The syntax of a time is hh:mm[:ss]
+ *
+ * @param text  text to inspect
+ * @param start the first index in <code>text</code> to inspect
+ * @param value OUT: the value of the found time. Not changed if result is 0.<br>
+ *              May be NULL
+ * @return  0: no date found<br>
+ *          otherwise: the length of the date in the string
+ */
+int ReQStringUtils::lengthOfTime(const ReString& text, int start, QTime* value) {
+       uint hour = 0;
+       uint minute = 0;
+       uint sec = 0;
+       int length = lengthOfUInt(text, start, 10, &hour);
+       if (length > 0 && hour > 23)
+               length = 0;
+       if (length > 0) {
+               start += length;
+       }
+       int length2;
+       skipExpected(text, ':', start, length);
+       if (length > 0) {
+               length2 = lengthOfUInt(text, start, 10, &minute);
+               if (length2 < 1 || length2 > 2 || minute >= 60)
+                       length = 0;
+               else
+                       start += length2, length += length2;
+       }
+       if (length > 0 && start < text.length() && text[start] == ':') {
+               length++;
+               start++;
+               length2 = lengthOfUInt(text, start, 10, &sec);
+               if (length2 < 1 || length2 > 2 || sec >= 60)
+                       length = 0;
+               else
+                       start += length2, length += length2;
+       }
+       if (length != 0 && value != NULL)
+               *value = QTime(hour, minute, sec);
+       return length;
+}
+
+/**
+ * @brief Determines the length and value of a floting point number.
+ *
+ * @param text      the number as text
+ * @param start     the first index to inspect
+ * @param pValue     OUT: the value of the integer. May be NULL
+ *
+ * @return          <=0: no real number found
+ *                  otherwise: the length of the floating point number
+ */
+int ReQStringUtils::lengthOfReal(const ReString& text, int start,
+    qreal* pValue) {
+       int inputLength = text.size();
+       qreal value = 0.0;
+       int cc;
+       int ix = start;
+       while (ix < inputLength) {
+               if ((cc = text[ix].unicode()) >= '0' && cc <= '9')
+                       value = value * 10 + (cc - '0');
+               else
+                       break;
+               ix++;
+       }
+       // found: a digit has been found (in front of or behind the '.'
+       bool found = ix > start;
+       if (ix < inputLength && text[ix].unicode() == '.') {
+               ix++;
+       }
+       if (ix < inputLength && text[ix].isDigit()) {
+               found = true;
+               qreal divisor = 1;
+               qreal precision = 0;
+               while (ix < inputLength && (cc = text[ix].unicode()) >= '0' && cc <= '9') {
+                       divisor *= 10;
+                       precision = precision * 10 + cc - '0';
+                       ix++;
+               }
+               value += precision / divisor;
+       } else if (!found) {
+               ix = start;
+       }
+       if (found && ix + 1 < inputLength && toupper(text[ix].unicode()) == 'E') {
+               int savePoint = ix;
+               ix++;
+               bool negative = false;
+               if ((cc = text[ix].unicode()) == '+')
+                       ix++;
+               else if (cc == '-') {
+                       ix++;
+                       negative = true;
+               }
+               if (ix >= inputLength || !text[ix].isDigit())
+                       ix = savePoint;
+               else {
+                       int exponent = 0;
+                       while (ix < inputLength && text[ix].isDigit()) {
+                               exponent = exponent * 10 + text[ix].unicode() - '0';
+                               ix++;
+                       }
+                       if (negative)
+                               value /= qPow(10, exponent);
+                       else
+                               value *= qPow(10, exponent);
+               }
+       }
+       if (pValue)
+               *pValue = value;
+       return found ? ix - start : 0;
+}
+
+/**
+ * Extracts the node of a filename.
+ *
+ * The node is the filename without path.
+ *
+ * @param filename  the filename (with or without path)
+ * @return          the node of <code>filename</code>
+ */
+ReString ReQStringUtils::nodeOf(const ReString& filename) {
+       QString rc;
+
+       int index = filename.lastIndexOf('/');
+       if (index >= 0)
+               rc = filename.mid(index + 1);
+       else {
+#if defined WIN32
+               index = filename.lastIndexOf('\\');
+               if (index < 0)
+               rc = filename;
+               else
+               rc = filename.mid(index + 1);
+#endif
+       }
+       return rc;
+}
+
+/**
+ * Appends a relative path to base directory name (absolute or relative).
+ *
+ * @param base  the base directory, relative or absolute
+ * @param toAdd a relative path (relative to <code>base</code>)
+ * @return      the combined path
+ */
+QString ReQStringUtils::pathAppend(const QString& base, const QString& path) {
+       QString rc;
+       if (!base.isEmpty())
+               rc = QDir::cleanPath(base + QDir::separator() + path);
+       else {
+               rc = path;
+               rc.replace("\\", "/");
+               if (path.startsWith("/"))
+                       rc.remove(0, 1);
+       }
+       return rc;
+}
+
+/**
+ * Replaces the file extension of a filename.
+ *
+ * @param path  the filename to change
+ * @param ext   the new file extension, e.g. ".txt"
+ * @return      path with a new file extension
+ */
+
+QString ReQStringUtils::replaceExtension(const QString& path,
+    const QString& ext) {
+       QString oldExt = extensionOf(path);
+       QString rc;
+       if (oldExt.isEmpty())
+               rc = path + ext;
+       else
+               rc = path.mid(0, path.size() - oldExt.size()) + ext;
+       return rc;
+}
+/**
+ * Replaces placeholders by their values.
+ *
+ * Example for a placeholder: ${path}
+ *
+ * @param text          IN/OUT: the text to inspect/change
+ * @param placeholders  a hashmap with (name, value) pairs, e.g. ("path", "/")
+ * @param error         OUT: NULL or the error message (unknown name)
+ * @return              <code>true</code>: success<br>
+ *                      <code>false</code>: unknown name found (not replaced)
+ */
+bool ReQStringUtils::replacePlaceholders(QString& text,
+    const QMap<QString, QString>& placeholders, QString* error) {
+       int start = 0;
+       bool rc = true;
+       QString name;
+       QMap<QString, QString>::const_iterator it;
+       while (start >= 0) {
+               start = text.indexOf("${", start);
+               if (start < 0)
+                       break;
+               int end = text.indexOf('}', start + 1);
+               if (end < 0)
+                       break;
+               name = text.mid(start + 2, end - start - 2);
+               it = placeholders.find(name);
+               if (it == placeholders.end()) {
+                       rc = false;
+                       if (error != NULL) {
+                               *error = QObject::tr("unknown placeholder: ") + name;
+                       }
+               } else {
+                       text = text.replace("${" + name + "}", *it);
+               }
+               start += (*it).length();
+       }
+       return rc;
+}
+
+/**
+ * Skips a character in a text at a given position if it has an expected value.
+ *
+ * @param text      text to inspect
+ * @param expected  the character which is expected
+ * @param index     IN/OUT: the position of the expected character.
+ *                  Will be incremented if the expected character is found
+ * @param length    IN/OUT: IN: 0: do nothing<br>
+ *                  OUT: 0: the expected character was not found.
+ *                  otherwise: the length is incremented
+ */
+void ReQStringUtils::skipExpected(const ReString& text, QChar expected,
+    int& index, int& length) {
+       if (length == 0) {
+               // error state, do nothing
+       } else if (index >= text.length() || text[index] != expected) {
+               length = 0;
+       } else {
+               index++;
+               length++;
+       }
+}
+
+/**
+ * @brief Converts a ReString into an utf-8 string
+ *
+ * The expression <code>qstring.toUtf8().constData()</code> is not allowed
+ * in a variable argument list like sprintf. This is a thread save workaround.
+ *
+ * @param source        string to convert
+ * @param buffer        OUT: target buffer
+ * @param bufferSize    size of the target buffer
+ * @return              <code>buffer</code>
+ */
+char*ReQStringUtils::utf8(const ReString& source, char buffer[],
+    size_t bufferSize) {
+       QByteArray val = source.toUtf8();
+       if (val.length() < (int) bufferSize)
+               bufferSize = val.length() + 1;
+       memcpy(buffer, val.constData(), bufferSize - 1);
+       buffer[bufferSize - 1] = '\0';
+       return buffer;
+}
+
+class ReParserException: public ReException {
+public:
+       ReParserException(const QString& message) :
+                   ReException(),
+                   m_message(message) {
+       }
+public:
+       QString m_message;
+};
+/**
+ * Constructor.
+ *
+ * @param expr       an expression, e.g. "10*1024kByte+5MiByte"
+ * @param unitList   description of the allowed units with its factor<br>
+ *                   example: "kibyte:1024;kbyte:1000;mibyte:1048576;mbyte:1000000"
+ */
+ReUnitParser::ReUnitParser(const QString& expr, const char* unitList,
+    bool parseAtOnce) :
+           m_result(0),
+           m_expr(expr),
+           m_message(),
+           m_unitList(unitList) {
+       normalize();
+       if (parseAtOnce)
+               parse();
+}
+
+/**
+ * Returns the result of the expression as a 64 bit integer.
+ *
+ * @param defaultValue  the result if the expression was not valid
+ * @return              <code>defaultValue</code>: the result was not valid<br>
+ *                      the result as a 64 bit integer
+ */
+int64_t ReUnitParser::asInt64(int64_t defaultValue) {
+       return m_message.isEmpty() ? m_result : defaultValue;
+}
+/**
+ * Returns the result of the expression as an integer.
+ *
+ * @param defaultValue  the result if the expression was not valid
+ * @return              <code>defaultValue</code>: the result was not valid<br>
+ *                      the result as an integer
+ */
+int ReUnitParser::asInt(int defaultValue) {
+       return m_message.isEmpty() ? (int) m_result : defaultValue;
+}
+/**
+ * Returns the result of the expression as floating point number.
+ *
+ * @param defaultValue  the result if the expression was not valid
+ * @return              <code>defaultValue</code>: the result was not valid<br>
+ *                      the result as a floating point
+ */
+real_t ReUnitParser::asReal(real_t defaultValue) {
+       return m_message.isEmpty() ? (real_t) m_result : defaultValue;
+}
+
+/**
+ * Returns an empty string or the error message.
+ *
+ * @return  "": no error occurred<br>
+ *          otherwise: the error message
+ */
+const QString& ReUnitParser::errorMessage() const {
+       return m_message;
+}
+
+/**
+ * Returns whether the given expression is valid.
+ *
+ * @return  <code>true</code>: the expression is valid, a result was calculated
+ */
+bool ReUnitParser::isValid() const {
+       return m_message.isEmpty();
+}
+
+/**
+ * @brief Normalizes the internal stored unit expression.
+ */
+void ReUnitParser::normalize() {
+       // Remove the blanks:
+       for (int ii = m_expr.length() - 1; ii >= 0; ii--) {
+               if (m_expr[ii].isSpace())
+                       m_expr.remove(ii, 1);
+       }
+       // Replace the '-' operator by '+' as operator and '-' as sign:
+       // This makes the syntax easier to parse: only one sum operator ('+').
+       for (int ii = m_expr.length() - 1; ii > 0; ii--) {
+               if (m_expr[ii] == '-' && m_expr[ii - 1] != '+'
+                   && m_expr[ii - 1] != '*') {
+                       m_expr.insert(ii, '+');
+               }
+       }
+}
+
+/**
+ * Evaluate the expression.
+ */
+void ReUnitParser::parse() {
+       QStringList addends = m_expr.split("+");
+       QStringList::const_iterator it;
+       try {
+               m_result = 0;
+               for (it = addends.begin(); it != addends.end(); ++it) {
+                       QStringList factors = it->split("*");
+                       QStringList::const_iterator it2;
+                       int64_t product = 1;
+                       for (it2 = factors.begin(); it2 != factors.end(); ++it2) {
+                               QStringList powerOperands = it2->split("^");
+                               if (powerOperands.count() > 2)
+                                       throw ReParserException(
+                                           QObject::tr(
+                                               "more than 2 power operators, e.g. '2^3^4'"));
+                               QStringList::const_iterator it3 = powerOperands.begin();
+                               QString op = *it3;
+                               bool isNeg = op.startsWith("-");
+                               if (isNeg)
+                                       op = op.mid(1);
+                               uint64_t value = valueOf(op);
+                               if (powerOperands.count() > 1) {
+                                       uint64_t fac = value;
+                                       uint64_t exponent = valueOf(*++it3);
+                                       if (qLn(value) * qLn(exponent) >= qLn(qPow(2.0, 64)))
+                                               throw ReParserException(
+                                                   QObject::tr(
+                                                       "number overflow while power operation"));
+                                       for (int ii = 1; ii < (int) exponent; ii++)
+                                               value = value * fac;
+                               }
+                               product *= value;
+                               if (isNeg)
+                                       product = -product;
+                       }
+                       m_result += product;
+               }
+
+       } catch (ReParserException& e) {
+               m_message = e.m_message;
+       }
+}
+
+/**
+ * Calculates the value of a number or a (number, unit) pair.
+ *
+ * @param value   a non negative number or a number followed by a unit<br>
+ *                only units defined in m_unitList are allowed<br>
+ *                examples: "4kByte" returns 4000, "4kibyte" returns 4096
+ * @return        the value of the number multiplied by the factor given by the unit
+ * @throws  ReParserException
+ */
+uint64_t ReUnitParser::valueOf(const QString& value) const {
+       uint64_t rc = 0;
+       int ix = ReQStringUtils::lengthOfUInt64(value, 0, 10, &rc);
+       if (ix == 0)
+               throw ReParserException(QObject::tr("number expected: ") + value);
+       QString unit = value.mid(ix);
+       if (!unit.isEmpty()) {
+               QStringList units = QString(m_unitList).split(";");
+               QStringList::const_iterator it;
+               bool found = false;
+               for (it = units.begin(); it != units.end(); ++it) {
+                       QStringList pair = it->split(":");
+                       if (pair.count() == 0)
+                               throw ReParserException(
+                                   QObject::tr(
+                                       "missing ':' in unit definition, e.g. 'k:1000': ")
+                                       + *it);
+                       if (pair.count() > 2)
+                               throw ReParserException(
+                                   QObject::tr("too many  ':' in unit definition: ") + *it);
+                       bool ok = false;
+                       QString unit2 = *pair.begin();
+                       QString factor = *++pair.begin();
+                       uint64_t nFactor = factor.toLongLong(&ok);
+                       if (!ok)
+                               throw ReParserException(QObject::tr("not a number: ") + factor);
+                       if (unit2.startsWith(unit, Qt::CaseInsensitive)) {
+                               rc *= nFactor;
+                               found = true;
+                               break;
+                       }
+               }
+               if (!found)
+                       throw ReParserException(
+                           QObject::tr("unknown unit '$1'. Allowed: ").arg(unit)
+                               + QString(m_unitList));
+       }
+       return rc;
+}
+
+/**
+ * Constructor.
+ *
+ * @param expr       an expression, e.g. "10*1024kByte+5MiByte"
+ */
+ReSizeParser::ReSizeParser(const QString& expr) :
+           ReUnitParser(expr, "byte:1;kbyte:1000;kibyte:1024;"
+                   "mbyte:1000000;mibyte:1048576;"
+                   "gbyte:1000000000;gibyte:1073741824;"
+                   "tbyte:1000000000000;tibyte:1099511627776") {
+}
+/**
+ * Constructor.
+ *
+ * @param expr     an expression, e.g. "3*3days-5min+3weeks"
+ */
+ReDateTimeParser::ReDateTimeParser(const QString& expr) :
+           ReUnitParser("", "minutes:60;hours:3600;days:86400;weeks:604800", false) {
+       parseDateTime(expr);
+}
+
+/**
+ * Returns the parser result as a <code>QDateTime</code> instance.
+ *
+ * @return the parse result. If invalid input the result is the begin of the epoche
+ */
+QDateTime ReDateTimeParser::asDateTime() const {
+       return m_dateTime;
+}
+
+/**
+ * Parses a date/time expression.
+ *
+ * Syntax: { "now" | <date> [<time>] | <time> } [ { '+' | '-' } <second expr>]
+ *          | <second expr>
+ * @param expr  expression to parse, e.g. "now+3weeks-5*30days"
+ * @return      start of the epoche: no valid input<br>
+ *              othewise: the calculated date/time
+ */
+QDateTime ReDateTimeParser::parseDateTime(const QString& expr) {
+       m_expr = expr;
+       normalize();
+       QDateTime rc = QDateTime::currentDateTime();
+       int64_t relativeSeconds = 0;
+       if (m_expr.isEmpty())
+               m_message = QObject::tr("empty string is not a date/time");
+       else {
+               QDateTime dateTime;
+               int length2 = 0;
+               bool checkSum = true;
+               if (m_expr.startsWith("now", Qt::CaseInsensitive)) {
+                       m_expr.remove(0, 3);
+               } else if ((length2 = ReQStringUtils::lengthOfDateTime(m_expr, 0, true,
+                   true, &dateTime)) > 0) {
+                       rc = dateTime;
+                       m_expr.remove(0, length2);
+               } else {
+                       checkSum = false;
+                       parse();
+                       // meaning is "older than x seconds"
+                       relativeSeconds = m_result = -m_result;
+               }
+               if (checkSum) {
+                       if (m_expr.startsWith("+")) {
+                               m_expr.remove(0, 1);
+                       }
+                       if (!m_expr.isEmpty()) {
+                               parse();
+                               relativeSeconds = m_result;
+                       }
+               }
+       }
+       rc.setMSecsSinceEpoch(
+           isValid() ? rc.toMSecsSinceEpoch() + 1000 * relativeSeconds : 0);
+       m_dateTime = rc;
+       return rc;
+}
+
diff --git a/base/ReQStringUtils.hpp b/base/ReQStringUtils.hpp
new file mode 100644 (file)
index 0000000..0552f9d
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * ReQStringUtil.hpp
+ *
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
+ * Do what you want.
+ * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
+ * The latest sources: https://github.com/republib
+ */
+
+#ifndef RPLQSTRING_HPP
+#define RPLQSTRING_HPP
+
+class ReQStringUtils {
+public:
+       static ReString chomp(const ReString& text);
+       static int countOf(const QString& value, QChar toFind, int start = 0);
+       static QString& ensureLastChar(QString& value, QChar lastChar);
+       static ReString extensionOf(const ReString& filename);
+       static int lengthOfDate(const ReString& text, int start = 0, QDate* value =
+           NULL);
+       static int lengthOfDateTime(const ReString& text, int start = 0,
+           bool allowDateOnly = true, bool allowTimeOnly = true, QDateTime* value =
+               NULL);
+       static int lengthOfReal(const ReString& text, int start = 0, qreal* value =
+           NULL);
+       static int lengthOfTime(const ReString& text, int start = 0, QTime* value =
+           NULL);
+       static int lengthOfUInt64(const ReString& text, int start = 0, int radix =
+           10, uint64_t* value = NULL);
+       static int lengthOfUInt(const ReString& text, int start, int radix,
+           uint* pValue);
+       /**
+        * Returns the path with native path separators.
+        *
+        * @param path   the path to change
+        * @return       the path with native path separators
+        */
+       inline static QString nativePath(const QString& path) {
+#if defined WIN32
+               QString rc(path); return rc.replace("/", "\\");
+#else
+               return path;
+#endif
+       }
+       static ReString nodeOf(const ReString& filename);
+       static QString pathAppend(const QString& base, const QString& path);
+       static QString replaceExtension(const QString& path, const QString& ext);
+       static bool replacePlaceholders(QString& text,
+           const QMap<QString, QString>& placeholders, QString* error);
+       static void skipExpected(const ReString& text, QChar expected, int& index,
+           int& length);
+       /**
+        * @brief Returns the value of a hexadecimal digit.
+        *
+        * @param digit     a (unicode) character
+        * @return          -1: not a hexadecimal digit<br>
+        *                  otherwise: the value, e.g. 10 for 'a'
+        */
+       inline static int valueOfHexDigit(int digit) {
+               return digit >= '0' && digit <= '9' ? digit - '0' :
+                      digit >= 'A' && digit <= 'F' ? digit - 'A' + 10 :
+                      digit >= 'a' && digit <= 'f' ? digit - 'a' + 10 : -1;
+       }
+       static char* utf8(const ReString& source, char buffer[], size_t bufferSize);
+};
+
+class ReUnitParser {
+public:
+       ReUnitParser(const QString& expr, const char* unitList, bool parseAtOnce =
+           true);
+public:
+       bool isValid() const;
+       const QString& errorMessage() const;
+       int64_t asInt64(int64_t defaultValue = -1ll);
+       int asInt(int defaultValue = -1);
+       real_t asReal(real_t defaultValue = -1.0);
+protected:
+       void parse();
+       void normalize();
+       uint64_t valueOf(const QString& value) const;
+protected:
+       int64_t m_result;
+       QString m_expr;
+       QString m_message;
+       const char* m_unitList;
+};
+
+class ReSizeParser: public ReUnitParser {
+public:
+       ReSizeParser(const QString& expr);
+};
+
+class ReDateTimeParser: public ReUnitParser {
+public:
+       ReDateTimeParser(const QString& expr);
+public:
+       QDateTime parseDateTime(const QString& expr);
+       QDateTime asDateTime() const;
+private:
+       QDateTime m_dateTime;
+};
+
+#endif // RPLQSTRING_HPP
diff --git a/base/ReStringUtil.cpp b/base/ReStringUtil.cpp
deleted file mode 100644 (file)
index 9333a70..0000000
+++ /dev/null
@@ -1,540 +0,0 @@
-/*
- * ReStringUtil.cpp
- *
- * License: Public Domain
- * You can use and modify this file without any restriction.
- * Do what you want.
- * No warranties and disclaimer of any damages.
- * You also can use this license: http://www.wtfpl.net
- * The latest sources: https://github.com/republib
- */
-/** @file
- * @brief Missed operations for <code>QByteArray</code>s.
- */
-/** @file base/ReStringUtil.cpp
- *
- * @brief Definitions for missed operations for <code>QByteArray</code>s.
- */
-#include "base/rebase.hpp"
-
-/** @class ReStringUtil ReStringUtil.hpp "base/ReStringUtil.hpp"
- *
- * @brief Implements some services around strings.
- *
- * This is a class with static members only.
- */
-
-/**
- * @brief Counts the occurrences of a given char in a string.
- *
- * @param line the text to inspect
- * @param cc   the char to count
- * @return             the number of <code>cc</code> in the text
- */
-int ReStringUtil::countChar(const char* line, char cc) {
-       const char* ptr = line;
-       int rc = 0;
-       while ((ptr = strchr(ptr, cc)) != NULL) {
-               rc++;
-               ptr++;
-       }
-       return rc;
-}
-/**
- * Counts the occurrences of a string in a string.
- *
- * @param source    in this string will be searched
- * @param item      this item will be searched
- * @return          the count of occurrences
- */
-int ReStringUtil::count(const char* source, const char* item) {
-       const char* end = source;
-       int rc = 0;
-       int lengthItem = strlen(item);
-       while (true) {
-               const char* start = end;
-               end = strstr(start, item);
-               if (end == NULL)
-                       break;
-               else {
-                       rc++;
-                       end += lengthItem;
-               }
-       }
-       return rc;
-}
-
-/**
- * Returns a string with a given maximum length.
- *
- * @param source        the source
- * @param maxLength     the maximum length of the result
- * @param buffer        Out: used if length of the result is shorter
- * @param appendix      if the result is cut this string will be appended.<br>
- *                      May be NULL.
- * @return              source: the source is enough short<br>
- *                      the prefix of source with the given length
- */
-const QByteArray& ReStringUtil::cutString(const QByteArray& source,
-    int maxLength, QByteArray& buffer, const char* appendix) {
-       QByteArray& rc =
-           source.length() <= maxLength ? (QByteArray&) source : buffer;
-       if (source.length() > maxLength) {
-               buffer = source.left(maxLength);
-               if (appendix != NULL && appendix[0] != '\0')
-                       buffer.append(appendix);
-       }
-       return rc;
-}
-static char s_fileSeparator = 0;
-
-/**
- * @brief Returns the os specific file path separator.
- * @return the file path separator, e.g. "/" for linux
- */
-const char* ReStringUtil::fileSeparator() {
-       return fileSeparatorChar() == '/' ? "/" : "\\";
-}
-
-/**
- * @brief Returns the os specific file path separator.
- * @return the file path separator, e.g. '/' for linux
- */
-char ReStringUtil::fileSeparatorChar() {
-       if (s_fileSeparator == 0) {
-               const char* path = getenv("PATH");
-               if (path != NULL) {
-                       s_fileSeparator =
-                           strchr(path, ';') != NULL || strchr(path, '\\') != NULL ?
-                               '\\' : '/';
-               } else {
-                       if (getenv("windows") != NULL)
-                               s_fileSeparator = '\\';
-                       else
-                               s_fileSeparator = '/';
-               }
-       }
-       return s_fileSeparator;
-}
-
-/**
- * Builds a hexadecimal dump.
- *
- * Format: a sequence of hex digits followed by the ascii interpretation.
- *
- * Example: "42 30 61  B0a"
- *
- * @param data              data to convert
- * @param length            length of data
- * @param bytesPerLine      one line containes so many bytes of data
- * @return                  the hex dump
- */
-QByteArray ReStringUtil::hexDump(uint8_t* data, int length, int bytesPerLine) {
-       QByteArray rc;
-       int fullLines = length / bytesPerLine;
-       int expectedLength = (bytesPerLine * 4 + 2) * (fullLines + 1);
-       rc.reserve(expectedLength + 100);
-       int ixData = 0;
-       int col;
-       char buffer[16];
-       for (int lineNo = 0; lineNo < fullLines; lineNo++) {
-               for (col = 0; col < bytesPerLine; col++) {
-                       qsnprintf(buffer, sizeof buffer, "%02x ", data[ixData + col]);
-                       rc.append(buffer);
-               }
-               rc.append(' ');
-               for (col = 0; col < bytesPerLine; col++) {
-                       uint8_t cc = data[ixData + col];
-                       rc.append(cc > ' ' && cc < 128 ? (char) cc : '.');
-               }
-               ixData += bytesPerLine;
-               rc.append('\n');
-       }
-       // incomplete last line:
-       int restBytes = length - ixData;
-       if (restBytes > 0) {
-               for (col = 0; col < restBytes; col++) {
-                       qsnprintf(buffer, sizeof buffer, "%02x ", data[ixData + col]);
-                       rc.append(buffer);
-               }
-               for (col = restBytes; col < bytesPerLine; col++) {
-                       rc.append("   ");
-               }
-               rc.append(' ');
-               for (col = 0; col < restBytes; col++) {
-                       uint8_t cc = data[ixData + col];
-                       rc.append(cc > ' ' && cc < 128 ? (char) cc : '.');
-               }
-               rc.append('\n');
-       }
-       return rc;
-}
-
-/**
- * Reads a file into a string.
- *
- * @param file              file to read
- * @param removeLastNewline true: if the last character is a newline
- *                          the result will not contain this
- * @return                  the file's content
- */
-QByteArray ReStringUtil::read(const char* file, bool removeLastNewline) {
-       QByteArray rc;
-       struct stat info;
-       size_t size;
-       if (stat(file, &info) == 0 && (size = info.st_size) > 0) {
-               FILE* fp = fopen(file, "r");
-               if (fp != NULL) {
-                       rc.resize(info.st_size);
-                       fread(rc.data(), 1, size, fp);
-                       fclose(fp);
-                       if (removeLastNewline && rc.at(size - 1) == '\n') {
-                               rc.resize(size - 1);
-                       }
-               }
-       }
-       return rc;
-}
-
-QByteArray ReStringUtil::replaceNode(const char* source, const char* newNode) {
-       char sep = fileSeparatorChar();
-       const char* ptr = strrchr(source, sep);
-       QByteArray rc;
-       rc.reserve(strlen(source) + strlen(newNode) + 1);
-       if (ptr == NULL) {
-               rc.append(source).append(sep).append(newNode);
-       } else if (ptr[0] == '\0') {
-               rc.append(source).append(newNode);
-       } else {
-               rc.append(source, ptr - source + 1).append(newNode);
-       }
-       return rc;
-}
-
-/**
- * Converts a string into an array of strings.
- *
- * @param source        string to convert
- * @param separator     the separator between the items to split
- * @return              an array with the splitted source
- */
-QList<QByteArray> ReStringUtil::toArray(const char* source,
-    const char* separator) {
-       const char* end = source;
-       QList < QByteArray > rc;
-       rc.reserve(count(source, separator) + 1);
-       int lengthItem = strlen(separator);
-       while (*end != '\0') {
-               const char* start = end;
-               end = strstr(start, separator);
-               if (end == NULL) {
-                       end = start + strlen(start);
-               }
-               rc.append(QByteArray(start, end - start));
-               if (end[0] != '\0')
-                       end += lengthItem;
-       }
-       return rc;
-}
-
-QByteArray ReStringUtil::toCString(const char* source, int maxLength) {
-       if (maxLength <= 0)
-               maxLength = strlen(source);
-       int binaries = 0;
-       int ix;
-       for (ix = 0; ix < maxLength; ix++)
-               if (source[ix] < ' ') {
-                       binaries++;
-               }
-       QByteArray rc;
-       rc.reserve(maxLength + 3 * binaries + 1);
-       char cc;
-       for (ix = 0; ix < maxLength; ix++)
-               if ((cc = source[ix]) >= ' ') {
-                       rc += source[ix];
-               } else {
-                       switch (cc) {
-                       case '\0':
-                               // stop looping:
-                               ix = maxLength;
-                               break;
-                       case '\n':
-                               rc += "\\n";
-                               break;
-                       case '\r':
-                               rc += "\\r";
-                               break;
-                       case '\t':
-                               rc += "\\t";
-                               break;
-                       default: {
-                               char buffer[5];
-                               qsnprintf(buffer, sizeof buffer, "\\x%02x",
-                                   ((unsigned int) cc) % 0xff);
-                               rc += buffer;
-                               break;
-                       }
-                       }
-               }
-       return rc;
-}
-
-/**
- * Return an integer as an QByteArray.
- *
- * @param value     value to convert
- * @param format    format like in sprintf()
- * @return          the ascii form of the value
- */
-QByteArray ReStringUtil::toNumber(int value, const char* format) {
-       char buffer[128];
-       qsnprintf(buffer, sizeof buffer, format, value);
-       return QByteArray(buffer);
-}
-
-/**
- * Writes a string to a file.
- *
- * @param file      the file's name
- * @param content   NULL or the file's content
- * @param mode      the file open mode: "w" for write, "a" for append
- * @return          true: successful<br>
- *                  false: error occurred
- */
-bool ReStringUtil::write(const char* file, const char* content,
-    const char* mode) {
-       FILE* fp = fopen(file, mode);
-       if (fp != NULL) {
-               fputs(content, fp);
-               fclose(fp);
-       }
-       return fp != NULL;
-}
-/**
- * @brief Returns the length of the number string.
- *
- * @param text                  a text to inspect
- * @param skipTrailingSpaces    true: if spaces are behind the number
- *                              the result contains the length of these
- * @return             0: not a number<br>
- *                             otherwise: the length of the number string
- */
-int ReStringUtil::lengthOfNumber(const char* text, bool skipTrailingSpaces) {
-       int rc = 0;
-       bool found = false;
-       const char* ptr = text;
-       while (isspace(*ptr))
-               ptr++;
-       if ((*ptr == '+' || *ptr == '-'))
-               ptr++;
-       found = isdigit(*ptr);
-       while (isdigit(*ptr)) {
-               ptr++;
-       }
-       if (*ptr == '.') {
-               ptr++;
-               if (isdigit(*ptr)) {
-                       found = true;
-                       while (isdigit(*ptr))
-                               ptr++;
-               }
-       }
-       if (found && toupper(*ptr) == 'E') {
-               const char* ptrToE = ptr;
-               ptr++;
-               if (*ptr == '+' || *ptr == '-')
-                       ptr++;
-               if (!isdigit(*ptr))
-                       ptr = ptrToE;
-               else {
-                       while (isdigit(*ptr))
-                               ptr++;
-               }
-       }
-       if (found && skipTrailingSpaces) {
-               while (isspace(*ptr)) {
-                       ptr++;
-               }
-       }
-       rc = !found ? 0 : ptr - text;
-       return rc;
-}
-/**
- * @brief Adds the count of the possible separators.
- *
- * @param countCommas          IN/OUT: number of ','
- * @param countSemicolons      IN/OUT: number of ';'
- * @param countPipes           IN/OUT: number of '|'
- * @param countBlanks          IN/OUT: number of ' '
- */
-static void addSeparators(const char* line, int& commas, int& semicolons,
-    int& pipes, int& blanks) {
-       commas += ReStringUtil::countChar(line, ',');
-       semicolons += ReStringUtil::countChar(line, ';');
-       pipes += ReStringUtil::countChar(line, '|');
-       blanks += ReStringUtil::countChar(line, ' ');
-}
-
-/**
- * @brief Finds the separator of the CSV file.
- *
- * If the file contain TABs the result is TAB.
- * If not:
- * Inspects the first 5 lines and counts the possible separators.
- * The most found separator will be returned.
- *
- * @param fp                   CSV file
- * @param buffer               a line buffer
- * @param bufferSize   the size of <code>buffer[]</code>
- */
-char ReStringUtil::findCsvSeparator(FILE* fp, char* buffer, size_t bufferSize) {
-       char rc = '\0';
-       int lineNo = 0;
-       int maxLines = 5;
-       const char* line;
-       int commas = 0;
-       int semicolons = 0;
-       int pipes = 0;
-       int blanks = 0;
-       while (++lineNo < maxLines && (line = fgets(buffer, bufferSize, fp)) != NULL) {
-               if (strchr(line, '\t') != NULL) {
-                       rc = '\t';
-                       break;
-               }
-               addSeparators(line, commas, semicolons, pipes, blanks);
-       }
-       fseek(fp, 0, SEEK_SET);
-       if (rc != '\t') {
-               if (semicolons > 0 && commas > 0) {
-                       // if ',' is decimal separator and ';' is the column separator:
-                       // Add one semicolon per line because of number of values is
-                       // 1 greater than the number of separators
-                       semicolons += lineNo;
-               }
-               if (commas + semicolons + pipes == 0) {
-                       rc = blanks > 0 ? ' ' : '\0';
-               } else if (semicolons >= commas && semicolons >= pipes)
-                       rc = ';';
-               else if (commas > semicolons && commas > pipes)
-                       rc = ',';
-               else if (pipes > commas && pipes > semicolons)
-                       rc = '|';
-       }
-       return rc;
-}
-
-/**
- * @brief Determines the length and vlaue of an integer.
- *
- * @param text      the number as text
- * @param radix     the base of the number system: 8 (octal), 10 or 16
- * @param pValue    OUT: the value of the integer. May be NULL
- *
- * @return          <=0: no integer found
- *                  otherwise: the length of the integer
- */
-int ReStringUtil::lengthOfUInt64(const char* text, int radix, quint64* pValue) {
-       int64_t value = 0;
-       int length = 0;
-       int cc;
-       if (radix == 10) {
-               while ((cc = text[length]) >= '0' && cc <= '9') {
-                       value = value * 10 + cc - '0';
-                       length++;
-               }
-       } else if (radix == 16) {
-               while (true) {
-                       if ((cc = text[length]) >= '0' && cc <= '9')
-                               value = value * 16 + cc - '0';
-                       else if (cc >= 'A' && cc <= 'F')
-                               value = value * 16 + cc - 'A' + 10;
-                       else if (cc >= 'a' && cc <= 'f')
-                               value = value * 16 + cc - 'a' + 10;
-                       else
-                               break;
-                       length++;
-               }
-       } else if (radix == 8) {
-               while (true) {
-                       if ((cc = text[length]) >= '0' && cc <= '7')
-                               value = value * 8 + cc - '0';
-                       else
-                               break;
-                       length++;
-               }
-       } else {
-               throw ReException("ReStringUtil::lengthOfInt(): wrong radix: %d",
-                   radix);
-       }
-       if (pValue != NULL)
-               *pValue = value;
-       return length;
-}
-
-/**
- * @brief Determines the length and value of a floting point number.
- *
- * @param text      the number as text
- * @param pValue     OUT: the value of the integer. May be NULL
- *
- * @return          <=0: no real number found
- *                  otherwise: the length of the floating point number
- */
-int ReStringUtil::lengthOfReal(const char* text, qreal* pValue) {
-       qreal value = 0.0;
-       int cc;
-       int length = 0;
-       while (true) {
-               if ((cc = text[length]) >= '0' && cc <= '9')
-                       value = value * 10 + (cc - '0');
-               else
-                       break;
-               length++;
-       }
-       // found: a digit has been found (in front of or behind the '.'
-       bool found = length > 0;
-       if (text[length] == '.') {
-               length++;
-       }
-       if (isdigit(text[length])) {
-               found = true;
-               qreal divisor = 1;
-               qreal precision = 0;
-               while ((cc = text[length]) >= '0' && cc <= '9') {
-                       divisor *= 10;
-                       precision = precision * 10 + cc - '0';
-                       length++;
-               }
-               value += precision / divisor;
-       } else if (!found) {
-               length = 0;
-       }
-       if (found && toupper(text[length]) == 'E') {
-               int savePoint = length;
-               length++;
-               bool negative = false;
-               if ((cc = text[length]) == '+')
-                       length++;
-               else if (cc == '-') {
-                       length++;
-                       negative = true;
-               }
-               if (!isdigit(text[length]))
-                       length = savePoint;
-               else {
-                       int exponent = 0;
-                       while (isdigit(text[length])) {
-                               exponent = exponent * 10 + text[length] - '0';
-                               length++;
-                       }
-                       if (negative)
-                               value /= qPow(10, exponent);
-                       else
-                               value *= qPow(10, exponent);
-               }
-       }
-       if (pValue)
-               *pValue = value;
-       return found ? length : 0;
-}
-
diff --git a/base/ReStringUtil.hpp b/base/ReStringUtil.hpp
deleted file mode 100644 (file)
index 555db1a..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * ReStringUtil.hpp
- *
- * License: Public Domain
- * You can use and modify this file without any restriction.
- * Do what you want.
- * No warranties and disclaimer of any damages.
- * You also can use this license: http://www.wtfpl.net
- * The latest sources: https://github.com/republib
- */
-#ifndef RPLSTRING_HPP
-#define RPLSTRING_HPP
-
-class ReStringUtil {
-public:
-       static int countChar(const char* line, char cc);
-       static int count(const char* source, const char* item);
-       static const QByteArray& cutString(const QByteArray& source, int maxLength,
-           QByteArray& buffer, const char* appendix = "...");
-       static const char* fileSeparator();
-       static char fileSeparatorChar();
-       static QByteArray hexDump(uint8_t* data, int length, int bytesPerLine = 16);
-       static QByteArray hexDump(const void* data, int length, int bytesPerLine =
-           16) {
-               return hexDump((uint8_t*) data, length, bytesPerLine);
-       }
-       static QByteArray read(const char* file, bool removeLastNewline = true);
-       static QByteArray replaceNode(const char* source, const char* newNode);
-       static bool write(const char* file, const char* content = NULL,
-           const char* mode = "w");
-       static QList<QByteArray> toArray(const char* source, const char* separator);
-       static QByteArray toCString(const char* source, int maxLength = -1);
-       static QByteArray toNumber(int value, const char* format = "%d");
-       static int lengthOfNumber(const char* text,
-           bool skipTrailingSpaces = false);
-       static char findCsvSeparator(FILE* fp, char* buffer, size_t bufferSize);
-       static int lengthOfUInt64(const char* text, int radix, quint64* pValue);
-       static int lengthOfReal(const char* text, qreal* pValue);
-};
-
-#endif // RPLSTRING_HPP
diff --git a/base/ReStringUtils.cpp b/base/ReStringUtils.cpp
new file mode 100644 (file)
index 0000000..0faf845
--- /dev/null
@@ -0,0 +1,540 @@
+/*
+ * ReStringUtil.cpp
+ *
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
+ * Do what you want.
+ * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
+ * The latest sources: https://github.com/republib
+ */
+/** @file
+ * @brief Missed operations for <code>QByteArray</code>s.
+ */
+/** @file base/ReStringUtil.cpp
+ *
+ * @brief Definitions for missed operations for <code>QByteArray</code>s.
+ */
+#include "base/rebase.hpp"
+
+/** @class ReStringUtil ReStringUtil.hpp "base/ReStringUtil.hpp"
+ *
+ * @brief Implements some services around strings.
+ *
+ * This is a class with static members only.
+ */
+
+/**
+ * @brief Counts the occurrences of a given char in a string.
+ *
+ * @param line the text to inspect
+ * @param cc   the char to count
+ * @return             the number of <code>cc</code> in the text
+ */
+int ReStringUtils::countChar(const char* line, char cc) {
+       const char* ptr = line;
+       int rc = 0;
+       while ((ptr = strchr(ptr, cc)) != NULL) {
+               rc++;
+               ptr++;
+       }
+       return rc;
+}
+/**
+ * Counts the occurrences of a string in a string.
+ *
+ * @param source    in this string will be searched
+ * @param item      this item will be searched
+ * @return          the count of occurrences
+ */
+int ReStringUtils::count(const char* source, const char* item) {
+       const char* end = source;
+       int rc = 0;
+       int lengthItem = strlen(item);
+       while (true) {
+               const char* start = end;
+               end = strstr(start, item);
+               if (end == NULL)
+                       break;
+               else {
+                       rc++;
+                       end += lengthItem;
+               }
+       }
+       return rc;
+}
+
+/**
+ * Returns a string with a given maximum length.
+ *
+ * @param source        the source
+ * @param maxLength     the maximum length of the result
+ * @param buffer        Out: used if length of the result is shorter
+ * @param appendix      if the result is cut this string will be appended.<br>
+ *                      May be NULL.
+ * @return              source: the source is enough short<br>
+ *                      the prefix of source with the given length
+ */
+const QByteArray& ReStringUtils::cutString(const QByteArray& source,
+    int maxLength, QByteArray& buffer, const char* appendix) {
+       QByteArray& rc =
+           source.length() <= maxLength ? (QByteArray&) source : buffer;
+       if (source.length() > maxLength) {
+               buffer = source.left(maxLength);
+               if (appendix != NULL && appendix[0] != '\0')
+                       buffer.append(appendix);
+       }
+       return rc;
+}
+static char s_fileSeparator = 0;
+
+/**
+ * @brief Returns the os specific file path separator.
+ * @return the file path separator, e.g. "/" for linux
+ */
+const char* ReStringUtils::fileSeparator() {
+       return fileSeparatorChar() == '/' ? "/" : "\\";
+}
+
+/**
+ * @brief Returns the os specific file path separator.
+ * @return the file path separator, e.g. '/' for linux
+ */
+char ReStringUtils::fileSeparatorChar() {
+       if (s_fileSeparator == 0) {
+               const char* path = getenv("PATH");
+               if (path != NULL) {
+                       s_fileSeparator =
+                           strchr(path, ';') != NULL || strchr(path, '\\') != NULL ?
+                               '\\' : '/';
+               } else {
+                       if (getenv("windows") != NULL)
+                               s_fileSeparator = '\\';
+                       else
+                               s_fileSeparator = '/';
+               }
+       }
+       return s_fileSeparator;
+}
+
+/**
+ * Builds a hexadecimal dump.
+ *
+ * Format: a sequence of hex digits followed by the ascii interpretation.
+ *
+ * Example: "42 30 61  B0a"
+ *
+ * @param data              data to convert
+ * @param length            length of data
+ * @param bytesPerLine      one line containes so many bytes of data
+ * @return                  the hex dump
+ */
+QByteArray ReStringUtils::hexDump(uint8_t* data, int length, int bytesPerLine) {
+       QByteArray rc;
+       int fullLines = length / bytesPerLine;
+       int expectedLength = (bytesPerLine * 4 + 2) * (fullLines + 1);
+       rc.reserve(expectedLength + 100);
+       int ixData = 0;
+       int col;
+       char buffer[16];
+       for (int lineNo = 0; lineNo < fullLines; lineNo++) {
+               for (col = 0; col < bytesPerLine; col++) {
+                       qsnprintf(buffer, sizeof buffer, "%02x ", data[ixData + col]);
+                       rc.append(buffer);
+               }
+               rc.append(' ');
+               for (col = 0; col < bytesPerLine; col++) {
+                       uint8_t cc = data[ixData + col];
+                       rc.append(cc > ' ' && cc < 128 ? (char) cc : '.');
+               }
+               ixData += bytesPerLine;
+               rc.append('\n');
+       }
+       // incomplete last line:
+       int restBytes = length - ixData;
+       if (restBytes > 0) {
+               for (col = 0; col < restBytes; col++) {
+                       qsnprintf(buffer, sizeof buffer, "%02x ", data[ixData + col]);
+                       rc.append(buffer);
+               }
+               for (col = restBytes; col < bytesPerLine; col++) {
+                       rc.append("   ");
+               }
+               rc.append(' ');
+               for (col = 0; col < restBytes; col++) {
+                       uint8_t cc = data[ixData + col];
+                       rc.append(cc > ' ' && cc < 128 ? (char) cc : '.');
+               }
+               rc.append('\n');
+       }
+       return rc;
+}
+
+/**
+ * Reads a file into a string.
+ *
+ * @param file              file to read
+ * @param removeLastNewline true: if the last character is a newline
+ *                          the result will not contain this
+ * @return                  the file's content
+ */
+QByteArray ReStringUtils::read(const char* file, bool removeLastNewline) {
+       QByteArray rc;
+       struct stat info;
+       size_t size;
+       if (stat(file, &info) == 0 && (size = info.st_size) > 0) {
+               FILE* fp = fopen(file, "r");
+               if (fp != NULL) {
+                       rc.resize(info.st_size);
+                       fread(rc.data(), 1, size, fp);
+                       fclose(fp);
+                       if (removeLastNewline && rc.at(size - 1) == '\n') {
+                               rc.resize(size - 1);
+                       }
+               }
+       }
+       return rc;
+}
+
+QByteArray ReStringUtils::replaceNode(const char* source, const char* newNode) {
+       char sep = fileSeparatorChar();
+       const char* ptr = strrchr(source, sep);
+       QByteArray rc;
+       rc.reserve(strlen(source) + strlen(newNode) + 1);
+       if (ptr == NULL) {
+               rc.append(source).append(sep).append(newNode);
+       } else if (ptr[0] == '\0') {
+               rc.append(source).append(newNode);
+       } else {
+               rc.append(source, ptr - source + 1).append(newNode);
+       }
+       return rc;
+}
+
+/**
+ * Converts a string into an array of strings.
+ *
+ * @param source        string to convert
+ * @param separator     the separator between the items to split
+ * @return              an array with the splitted source
+ */
+QList<QByteArray> ReStringUtils::toArray(const char* source,
+    const char* separator) {
+       const char* end = source;
+       QList < QByteArray > rc;
+       rc.reserve(count(source, separator) + 1);
+       int lengthItem = strlen(separator);
+       while (*end != '\0') {
+               const char* start = end;
+               end = strstr(start, separator);
+               if (end == NULL) {
+                       end = start + strlen(start);
+               }
+               rc.append(QByteArray(start, end - start));
+               if (end[0] != '\0')
+                       end += lengthItem;
+       }
+       return rc;
+}
+
+QByteArray ReStringUtils::toCString(const char* source, int maxLength) {
+       if (maxLength <= 0)
+               maxLength = strlen(source);
+       int binaries = 0;
+       int ix;
+       for (ix = 0; ix < maxLength; ix++)
+               if (source[ix] < ' ') {
+                       binaries++;
+               }
+       QByteArray rc;
+       rc.reserve(maxLength + 3 * binaries + 1);
+       char cc;
+       for (ix = 0; ix < maxLength; ix++)
+               if ((cc = source[ix]) >= ' ') {
+                       rc += source[ix];
+               } else {
+                       switch (cc) {
+                       case '\0':
+                               // stop looping:
+                               ix = maxLength;
+                               break;
+                       case '\n':
+                               rc += "\\n";
+                               break;
+                       case '\r':
+                               rc += "\\r";
+                               break;
+                       case '\t':
+                               rc += "\\t";
+                               break;
+                       default: {
+                               char buffer[5];
+                               qsnprintf(buffer, sizeof buffer, "\\x%02x",
+                                   ((unsigned int) cc) % 0xff);
+                               rc += buffer;
+                               break;
+                       }
+                       }
+               }
+       return rc;
+}
+
+/**
+ * Return an integer as an QByteArray.
+ *
+ * @param value     value to convert
+ * @param format    format like in sprintf()
+ * @return          the ascii form of the value
+ */
+QByteArray ReStringUtils::toNumber(int value, const char* format) {
+       char buffer[128];
+       qsnprintf(buffer, sizeof buffer, format, value);
+       return QByteArray(buffer);
+}
+
+/**
+ * Writes a string to a file.
+ *
+ * @param file      the file's name
+ * @param content   NULL or the file's content
+ * @param mode      the file open mode: "w" for write, "a" for append
+ * @return          true: successful<br>
+ *                  false: error occurred
+ */
+bool ReStringUtils::write(const char* file, const char* content,
+    const char* mode) {
+       FILE* fp = fopen(file, mode);
+       if (fp != NULL) {
+               fputs(content, fp);
+               fclose(fp);
+       }
+       return fp != NULL;
+}
+/**
+ * @brief Returns the length of the number string.
+ *
+ * @param text                  a text to inspect
+ * @param skipTrailingSpaces    true: if spaces are behind the number
+ *                              the result contains the length of these
+ * @return             0: not a number<br>
+ *                             otherwise: the length of the number string
+ */
+int ReStringUtils::lengthOfNumber(const char* text, bool skipTrailingSpaces) {
+       int rc = 0;
+       bool found = false;
+       const char* ptr = text;
+       while (isspace(*ptr))
+               ptr++;
+       if ((*ptr == '+' || *ptr == '-'))
+               ptr++;
+       found = isdigit(*ptr);
+       while (isdigit(*ptr)) {
+               ptr++;
+       }
+       if (*ptr == '.') {
+               ptr++;
+               if (isdigit(*ptr)) {
+                       found = true;
+                       while (isdigit(*ptr))
+                               ptr++;
+               }
+       }
+       if (found && toupper(*ptr) == 'E') {
+               const char* ptrToE = ptr;
+               ptr++;
+               if (*ptr == '+' || *ptr == '-')
+                       ptr++;
+               if (!isdigit(*ptr))
+                       ptr = ptrToE;
+               else {
+                       while (isdigit(*ptr))
+                               ptr++;
+               }
+       }
+       if (found && skipTrailingSpaces) {
+               while (isspace(*ptr)) {
+                       ptr++;
+               }
+       }
+       rc = !found ? 0 : ptr - text;
+       return rc;
+}
+/**
+ * @brief Adds the count of the possible separators.
+ *
+ * @param countCommas          IN/OUT: number of ','
+ * @param countSemicolons      IN/OUT: number of ';'
+ * @param countPipes           IN/OUT: number of '|'
+ * @param countBlanks          IN/OUT: number of ' '
+ */
+static void addSeparators(const char* line, int& commas, int& semicolons,
+    int& pipes, int& blanks) {
+       commas += ReStringUtils::countChar(line, ',');
+       semicolons += ReStringUtils::countChar(line, ';');
+       pipes += ReStringUtils::countChar(line, '|');
+       blanks += ReStringUtils::countChar(line, ' ');
+}
+
+/**
+ * @brief Finds the separator of the CSV file.
+ *
+ * If the file contain TABs the result is TAB.
+ * If not:
+ * Inspects the first 5 lines and counts the possible separators.
+ * The most found separator will be returned.
+ *
+ * @param fp                   CSV file
+ * @param buffer               a line buffer
+ * @param bufferSize   the size of <code>buffer[]</code>
+ */
+char ReStringUtils::findCsvSeparator(FILE* fp, char* buffer, size_t bufferSize) {
+       char rc = '\0';
+       int lineNo = 0;
+       int maxLines = 5;
+       const char* line;
+       int commas = 0;
+       int semicolons = 0;
+       int pipes = 0;
+       int blanks = 0;
+       while (++lineNo < maxLines && (line = fgets(buffer, bufferSize, fp)) != NULL) {
+               if (strchr(line, '\t') != NULL) {
+                       rc = '\t';
+                       break;
+               }
+               addSeparators(line, commas, semicolons, pipes, blanks);
+       }
+       fseek(fp, 0, SEEK_SET);
+       if (rc != '\t') {
+               if (semicolons > 0 && commas > 0) {
+                       // if ',' is decimal separator and ';' is the column separator:
+                       // Add one semicolon per line because of number of values is
+                       // 1 greater than the number of separators
+                       semicolons += lineNo;
+               }
+               if (commas + semicolons + pipes == 0) {
+                       rc = blanks > 0 ? ' ' : '\0';
+               } else if (semicolons >= commas && semicolons >= pipes)
+                       rc = ';';
+               else if (commas > semicolons && commas > pipes)
+                       rc = ',';
+               else if (pipes > commas && pipes > semicolons)
+                       rc = '|';
+       }
+       return rc;
+}
+
+/**
+ * @brief Determines the length and vlaue of an integer.
+ *
+ * @param text      the number as text
+ * @param radix     the base of the number system: 8 (octal), 10 or 16
+ * @param pValue    OUT: the value of the integer. May be NULL
+ *
+ * @return          <=0: no integer found
+ *                  otherwise: the length of the integer
+ */
+int ReStringUtils::lengthOfUInt64(const char* text, int radix, quint64* pValue) {
+       int64_t value = 0;
+       int length = 0;
+       int cc;
+       if (radix == 10) {
+               while ((cc = text[length]) >= '0' && cc <= '9') {
+                       value = value * 10 + cc - '0';
+                       length++;
+               }
+       } else if (radix == 16) {
+               while (true) {
+                       if ((cc = text[length]) >= '0' && cc <= '9')
+                               value = value * 16 + cc - '0';
+                       else if (cc >= 'A' && cc <= 'F')
+                               value = value * 16 + cc - 'A' + 10;
+                       else if (cc >= 'a' && cc <= 'f')
+                               value = value * 16 + cc - 'a' + 10;
+                       else
+                               break;
+                       length++;
+               }
+       } else if (radix == 8) {
+               while (true) {
+                       if ((cc = text[length]) >= '0' && cc <= '7')
+                               value = value * 8 + cc - '0';
+                       else
+                               break;
+                       length++;
+               }
+       } else {
+               throw ReException("ReStringUtil::lengthOfInt(): wrong radix: %d",
+                   radix);
+       }
+       if (pValue != NULL)
+               *pValue = value;
+       return length;
+}
+
+/**
+ * @brief Determines the length and value of a floting point number.
+ *
+ * @param text      the number as text
+ * @param pValue     OUT: the value of the integer. May be NULL
+ *
+ * @return          <=0: no real number found
+ *                  otherwise: the length of the floating point number
+ */
+int ReStringUtils::lengthOfReal(const char* text, qreal* pValue) {
+       qreal value = 0.0;
+       int cc;
+       int length = 0;
+       while (true) {
+               if ((cc = text[length]) >= '0' && cc <= '9')
+                       value = value * 10 + (cc - '0');
+               else
+                       break;
+               length++;
+       }
+       // found: a digit has been found (in front of or behind the '.'
+       bool found = length > 0;
+       if (text[length] == '.') {
+               length++;
+       }
+       if (isdigit(text[length])) {
+               found = true;
+               qreal divisor = 1;
+               qreal precision = 0;
+               while ((cc = text[length]) >= '0' && cc <= '9') {
+                       divisor *= 10;
+                       precision = precision * 10 + cc - '0';
+                       length++;
+               }
+               value += precision / divisor;
+       } else if (!found) {
+               length = 0;
+       }
+       if (found && toupper(text[length]) == 'E') {
+               int savePoint = length;
+               length++;
+               bool negative = false;
+               if ((cc = text[length]) == '+')
+                       length++;
+               else if (cc == '-') {
+                       length++;
+                       negative = true;
+               }
+               if (!isdigit(text[length]))
+                       length = savePoint;
+               else {
+                       int exponent = 0;
+                       while (isdigit(text[length])) {
+                               exponent = exponent * 10 + text[length] - '0';
+                               length++;
+                       }
+                       if (negative)
+                               value /= qPow(10, exponent);
+                       else
+                               value *= qPow(10, exponent);
+               }
+       }
+       if (pValue)
+               *pValue = value;
+       return found ? length : 0;
+}
+
diff --git a/base/ReStringUtils.hpp b/base/ReStringUtils.hpp
new file mode 100644 (file)
index 0000000..a896bce
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * ReStringUtil.hpp
+ *
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
+ * Do what you want.
+ * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
+ * The latest sources: https://github.com/republib
+ */
+#ifndef RPLSTRING_HPP
+#define RPLSTRING_HPP
+
+class ReStringUtils {
+public:
+       static int countChar(const char* line, char cc);
+       static int count(const char* source, const char* item);
+       static const QByteArray& cutString(const QByteArray& source, int maxLength,
+           QByteArray& buffer, const char* appendix = "...");
+       static const char* fileSeparator();
+       static char fileSeparatorChar();
+       static QByteArray hexDump(uint8_t* data, int length, int bytesPerLine = 16);
+       static QByteArray hexDump(const void* data, int length, int bytesPerLine =
+           16) {
+               return hexDump((uint8_t*) data, length, bytesPerLine);
+       }
+       static QByteArray read(const char* file, bool removeLastNewline = true);
+       static QByteArray replaceNode(const char* source, const char* newNode);
+       static bool write(const char* file, const char* content = NULL,
+           const char* mode = "w");
+       static QList<QByteArray> toArray(const char* source, const char* separator);
+       static QByteArray toCString(const char* source, int maxLength = -1);
+       static QByteArray toNumber(int value, const char* format = "%d");
+       static int lengthOfNumber(const char* text,
+           bool skipTrailingSpaces = false);
+       static char findCsvSeparator(FILE* fp, char* buffer, size_t bufferSize);
+       static int lengthOfUInt64(const char* text, int radix, quint64* pValue);
+       static int lengthOfReal(const char* text, qreal* pValue);
+};
+
+#endif // RPLSTRING_HPP
index c16e430f0475682b51440f38880cd3d5cf59eba4..d57229d6c4886981effe767b66d74a8c8a087949 100644 (file)
@@ -174,8 +174,8 @@ bool ReTest::assertEquals(const char* expected, const char* current,
        bool equal = strcmp(expected, current) == 0;
        if (!equal) {
                if (strchr(expected, '\n') != NULL || strchr(current, '\n')) {
-                       QList < QByteArray > exp = ReStringUtil::toArray(expected, "\n");
-                       QList < QByteArray > cur = ReStringUtil::toArray(current, "\n");
+                       QList < QByteArray > exp = ReStringUtils::toArray(expected, "\n");
+                       QList < QByteArray > cur = ReStringUtils::toArray(current, "\n");
                        equal = assertEquals(exp, cur, file, lineNo);
                } else {
                        int ix = 0;
@@ -397,8 +397,8 @@ bool ReTest::assertNotNull(const void* ptr, const char* file, int lineNo) {
 bool ReTest::assertEqualFiles(const char* expected, const char* current,
     const char* file, int lineNo) {
        bool rc = false;
-       QByteArray expectedContent = ReStringUtil::read(expected, true);
-       QByteArray currentContent = ReStringUtil::read(current, true);
+       QByteArray expectedContent = ReStringUtils::read(expected, true);
+       QByteArray currentContent = ReStringUtils::read(current, true);
        if (expectedContent.isEmpty()) {
                char buffer[512];
                qsnprintf(buffer, sizeof buffer, "%s has no content. Does it exist?",
index ba1697ea10fa3726d8464cd6dbee2ad4ce014567..599a5b1c24c74992763b098b9161fa35b516c927 100644 (file)
@@ -94,12 +94,13 @@ inline int roundInt(double value) {
 #include "base/ReLogger.hpp"
 #include "base/ReException.hpp"
 #include "base/ReContainer.hpp"
-#include "base/ReStringUtil.hpp"
-#include "ReQStringUtil.hpp"
+#include "base/ReStringUtils.hpp"
+#include "ReQStringUtils.hpp"
 #include "base/ReConfigurator.hpp"
 #include "base/ReConfig.hpp"
 #include "base/ReTerminator.hpp"
 #include "base/ReLineSource.hpp"
+#include "base/ReFileUtils.hpp"
 #include "base/ReFile.hpp"
 #include "base/ReTest.hpp"
 
index da2369bf7ccc34c26612e0d6c76bf3667acc1783..2010274b2c4acdaeae4e950d4a13b1c712abea8e 100644 (file)
@@ -21,8 +21,10 @@ static void testGui() {
        int argc = 1;
        QApplication a(argc, argv);
        void testReStateStorage();
-       testReStateStorage();
        void testReEdit();
+       void testReSettings();
+       testReSettings();
+       testReStateStorage();
        testReEdit();
 }
 
@@ -36,7 +38,9 @@ static void testBase() {
        void testReStringUtil();
        void testReWriter();
        void testReFile();
+       void testReFileUtils();
        testReFile();
+       testReFileUtils();
        if (s_allTest) {
                testReQStringUtil();
                testReByteStorage();
index af08cd02a9b83fe26ba188f998429dd92557ba99..02c5c65ffcbeddcc7230fa7da613d379e2309f3d 100644 (file)
@@ -23,7 +23,7 @@ public:
 public:
        void testBasic() {
                QByteArray fn = getTempFile("test.data", "config");
-               ReStringUtil::write(fn, "#comment\na=1\nb.1==x\n#=\nB=zzz");
+               ReStringUtils::write(fn, "#comment\na=1\nb.1==x\n#=\nB=zzz");
                ReConfig config(fn.constData());
                checkEqu(3, config.size());
                checkEqu("1", config["a"]);
@@ -32,7 +32,7 @@ public:
        }
        void testAsX() {
                QByteArray fn = getTempFile("test.data", "config");
-               ReStringUtil::write(fn, "i=123\nb=1\nb2=true\nb3=yes\ns=abc");
+               ReStringUtils::write(fn, "i=123\nb=1\nb2=true\nb3=yes\ns=abc");
                ReConfig config(fn.constData());
                checkEqu(5, config.size());
                checkEqu(123, config.asInt("i", -1));
index bf1bf8084c7b6aabc5e303795c719768554a5533..28865f82c7b3cb3a549c14159ed4d4cd9f373eba 100644 (file)
@@ -17,7 +17,7 @@
 class TestReFile: public ReTest {
 public:
        TestReFile() :
-                       ReTest("ReFile") {
+                   ReTest("ReFile") {
                doIt();
        }
 
@@ -25,7 +25,7 @@ public:
        void testBasic() {
                QByteArray fn(ReFile::tempFile("big.txt", NULL, true));
                const char* content =
-                       "123456789 123456789 123456789 123456789 123456789\n";
+                   "123456789 123456789 123456789 123456789 123456789\n";
                int contentLength = strlen(content);
                ReFile::writeToFile(fn.constData(), content);
                ReFile file(fn.constData());
@@ -36,19 +36,19 @@ public:
                checkNN(ptr);
                checkEqu(4, length);
                checkEqu(0,
-                       strncmp(content, reinterpret_cast<const char*>(ptr), length));
+                   strncmp(content, reinterpret_cast<const char*>(ptr), length));
                int part = size / 2;
                ptr = file.remap(contentLength - part, size, length);
                checkEqu(size / 2, length);
                checkEqu(content + contentLength - part,
-                       reinterpret_cast<const char*>(ptr));
+                   reinterpret_cast<const char*>(ptr));
                for (int ix = 0; ix < contentLength - size; ix++) {
                        ptr = file.remap(ix, size, length);
                        checkNN(ptr);
                        checkEqu(length, size);
                        checkEqu(0,
-                               strncmp(content + ix, reinterpret_cast<const char*>(ptr),
-                                       length));
+                           strncmp(content + ix, reinterpret_cast<const char*>(ptr),
+                               length));
                }
 
        }
@@ -93,7 +93,7 @@ public:
                }
                double duration = double(clock() - start) / CLOCKS_PER_SEC;
                printf("linecount (ReFile, %d kB): %d lines %.3f sec\n",
-                       int(blocksize / 1024), lines, duration);
+                   int(blocksize / 1024), lines, duration);
 
        }
        void countLinesFopen(const char* filename) {
@@ -367,7 +367,6 @@ public:
                checkEqu("2", lines.lineAt(1));
                checkEqu("", lines.lineAt(2));
        }
-
        virtual void run() {
                testReLinesInsert();
                testReLinesSplitLine();
@@ -378,9 +377,6 @@ public:
                testWritableFile();
                testPerformance();
                testBasic();
-               testTempDir();
-               testTempFile();
-               testWriteRead();
        }
 };
 void testReFile() {
diff --git a/cunit/cuReFileUtils.cpp b/cunit/cuReFileUtils.cpp
new file mode 100644 (file)
index 0000000..5889992
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * cuReFileUtils.cpp
+ *
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
+ * Do what you want.
+ * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
+ * The latest sources: https://github.com/republib
+ */
+#include "base/rebase.hpp"
+
+/** @file
+ * @brief Unit test of the basic exceptions.
+ */
+
+class TestReFileUtils: public ReTest {
+public:
+       TestReFileUtils() :
+                       ReTest("ReFileUtils") {
+               doIt();
+       }
+
+public:
+       void testTempFile() {
+               QByteArray fn(ReFileUtils::tempFile("node.txt", "subdir", true));
+               QByteArray content;
+               ReFileUtils::writeToFile(fn, "123");
+               struct stat info;
+               checkEqu(0, stat(fn.constData(), &info));
+               checkEqu(3, (int ) info.st_size);
+               ReFileUtils::tempFile("node.txt", "subdir", true);
+               checkEqu(-1, stat(fn.constData(), &info));
+       }
+       void testTempDir() {
+               QByteArray dir(ReFileUtils::tempDir("subdir", "cuReFileUtils", false));
+               checkT(dir.endsWith("subdir"));
+               checkT(dir.endsWith("cuReFileUtils/subdir"));
+               struct stat info;
+               checkEqu(0, stat(dir, &info));
+               checkT(S_ISDIR(info.st_mode));
+       }
+       void testWriteRead() {
+               QByteArray fn(ReFileUtils::tempFile("node.txt", "subdir", true));
+               ReFileUtils::writeToFile(fn, "123");
+               QByteArray content;
+               ReFileUtils::readFromFile(fn, content);
+               checkEqu("123", content);
+               ReFileUtils::writeToFile(fn, "abcdef", 2);
+               ReFileUtils::readFromFile(fn, content);
+               checkEqu("ab", content);
+       }
+       QByteArray buildTree(){
+               QByteArray base = ReFileUtils::tempDir("ReFileUtils");
+               for (char cc = 'a'; cc < 'f'; cc++){
+                       QByteArray subdir(base + cc);
+                       mkdir(subdir.constData(), ALLPERMS);
+                       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(), ALLPERMS);
+                               name.append(OS_SEPARATOR_STR).append("x.txt");
+                               ReFileUtils::writeToFile(name, name);
+                       }
+               }
+               // remove the separator:
+               base.remove(base.length() - 1, 1);
+               return base;
+       }
+       void testDeleteTree(){
+               QByteArray base = buildTree();
+               checkT(ReFileUtils::deleteTree(QString(base), false, &m_logger));
+               struct stat info;
+               // the dir must exist:
+               checkEqu(0, stat(base, &info));
+               // rmdir() works only if the dir is empty:
+               checkEqu(0, rmdir(base));
+               buildTree();
+               checkT(ReFileUtils::deleteTree(QString(base), false, &m_logger));
+               checkEqu(0, stat(base, &info));
+       }
+       virtual void run() {
+               testDeleteTree();
+               testTempDir();
+               testTempFile();
+               testWriteRead();
+       }
+};
+void testReFileUtils() {
+       TestReFileUtils test;
+}
+
diff --git a/cunit/cuReQStringUtil.cpp b/cunit/cuReQStringUtil.cpp
deleted file mode 100644 (file)
index 1aae6fa..0000000
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * cuReQStringUtil.cpp
- *
- * License: Public Domain
- * You can use and modify this file without any restriction.
- * Do what you want.
- * No warranties and disclaimer of any damages.
- * You also can use this license: http://www.wtfpl.net
- * The latest sources: https://github.com/republib
- */
-/** @file
- * @brief Unit test of the ReString tools.
- */
-
-#include "../base/rebase.hpp"
-
-class TestReQStringUtil: public ReTest {
-public:
-       TestReQStringUtil() :
-                   ReTest("ReQStringUtil") {
-               doIt();
-       }
-
-public:
-       void testCountOf() {
-               checkEqu(2, ReQStringUtil::countOf(QString("axbx"), 'x'));
-               checkEqu(2, ReQStringUtil::countOf(QString("axbx"), 'x', 1));
-               checkEqu(1, ReQStringUtil::countOf(QString("axbx"), 'x', 2));
-               checkEqu(1, ReQStringUtil::countOf(QString("axbx"), 'x', 3));
-               checkEqu(0, ReQStringUtil::countOf(QString("axbx"), 'x', 4));
-               checkEqu(0, ReQStringUtil::countOf(QString("axbx"), 'x', 5));
-               checkEqu(0, ReQStringUtil::countOf(QString("axbx"), 'x', -1));
-       }
-
-       void testLengthOfUInt64() {
-               quint64 value = -3;
-               checkEqu(1,
-                   ReQStringUtil::lengthOfUInt64(ReString("0"), 0, 10, &value));
-               checkEqu(int64_t(0), value);
-               checkEqu(3, ReQStringUtil::lengthOfUInt64("x432", 1, 10, &value));
-               checkEqu(int64_t(432LL), value);
-               checkEqu(3, ReQStringUtil::lengthOfUInt64("x432 x", 1, 10, &value));
-               checkEqu(int64_t(432LL), value);
-               checkEqu(3, ReQStringUtil::lengthOfUInt64("x432fabc x", 1, 10, &value));
-               checkEqu(int64_t(432LL), value);
-               checkEqu(16,
-                   ReQStringUtil::lengthOfUInt64("a1234567890123567", 1, 10, &value));
-               checkEqu(int64_t(1234567890123567LL), value);
-               checkEqu(10,
-                   ReQStringUtil::lengthOfUInt64("x1234abcdef", 1, 16, &value));
-               checkEqu(int64_t(0x1234abcdefLL), value);
-               checkEqu(3, ReQStringUtil::lengthOfUInt64("432", 0, 8, &value));
-               checkEqu(int64_t(0432LL), value);
-               checkEqu(6, ReQStringUtil::lengthOfUInt64(" 765432 ", 1, 8, &value));
-               checkEqu(int64_t(0765432LL), value);
-
-               checkEqu(0, ReQStringUtil::lengthOfUInt64("1 ", 1, 8, &value));
-               checkEqu(0, ReQStringUtil::lengthOfUInt64("", 1, 8, &value));
-       }
-       void testLengthOfUInt() {
-               uint value = 3;
-               checkEqu(1, ReQStringUtil::lengthOfUInt(ReString("0"), 0, 10, &value));
-               checkEqu(0, value);
-               checkEqu(3, ReQStringUtil::lengthOfUInt("x432", 1, 10, &value));
-               checkEqu(432, value);
-               checkEqu(3, ReQStringUtil::lengthOfUInt("x432 x", 1, 10, &value));
-               checkEqu(432, value);
-               checkEqu(3, ReQStringUtil::lengthOfUInt("x432fabc x", 1, 10, &value));
-               checkEqu(432, value);
-               checkEqu(3, ReQStringUtil::lengthOfUInt("432", 0, 8, &value));
-               checkEqu(0432, value);
-               checkEqu(6, ReQStringUtil::lengthOfUInt(" 765432 ", 1, 8, &value));
-               checkEqu(0765432, value);
-
-               checkEqu(0, ReQStringUtil::lengthOfUInt("1 ", 1, 8, &value));
-               checkEqu(0, ReQStringUtil::lengthOfUInt("", 1, 8, &value));
-       }
-       void testLengthOfReal() {
-               qreal value;
-               checkEqu(4, ReQStringUtil::lengthOfReal(ReString("0.25"), 0, &value));
-               checkEqu(0.25, value);
-               checkEqu(3, ReQStringUtil::lengthOfReal(ReString("X.25"), 1, &value));
-               checkEqu(0.25, value);
-               checkEqu(1, ReQStringUtil::lengthOfReal(ReString(" 0"), 1, &value));
-               checkEqu(0.0, value);
-               checkEqu(17,
-                   ReQStringUtil::lengthOfReal(ReString("X12345678901234567"), 1,
-                       &value));
-               checkEqu(12345678901234567.0, value);
-               checkEqu(2, ReQStringUtil::lengthOfReal(ReString(".5"), 0, &value));
-               checkEqu(0.5, value);
-               checkEqu(5, ReQStringUtil::lengthOfReal(ReString("2.5e2x"), 0, &value));
-               checkEqu(250.0, value);
-               checkEqu(6, ReQStringUtil::lengthOfReal(ReString("2.5e+2"), 0, &value));
-               checkEqu(250.0, value);
-               checkEqu(7,
-                   ReQStringUtil::lengthOfReal(ReString("2.5E-33"), 0, &value));
-               checkEqu(2.5e-33, value);
-
-               checkEqu(3, ReQStringUtil::lengthOfReal(ReString("2.5E"), 0, &value));
-               checkEqu(2.5, value);
-               checkEqu(3, ReQStringUtil::lengthOfReal(ReString("2.5E+"), 0, &value));
-               checkEqu(2.5, value);
-               checkEqu(3, ReQStringUtil::lengthOfReal(ReString("2.5E-a"), 0, &value));
-               checkEqu(2.5, value);
-       }
-
-       void testValueOfHexDigit() {
-               checkEqu(0, ReQStringUtil::valueOfHexDigit('0'));
-               checkEqu(9, ReQStringUtil::valueOfHexDigit('9'));
-               checkEqu(10, ReQStringUtil::valueOfHexDigit('a'));
-               checkEqu(15, ReQStringUtil::valueOfHexDigit('f'));
-               checkEqu(10, ReQStringUtil::valueOfHexDigit('A'));
-               checkEqu(15, ReQStringUtil::valueOfHexDigit('F'));
-
-               checkEqu(-1, ReQStringUtil::valueOfHexDigit('0' - 1));
-               checkEqu(-1, ReQStringUtil::valueOfHexDigit('9' + 1));
-               checkEqu(-1, ReQStringUtil::valueOfHexDigit('A' - 1));
-               checkEqu(-1, ReQStringUtil::valueOfHexDigit('F' + 1));
-               checkEqu(-1, ReQStringUtil::valueOfHexDigit('a' - 1));
-               checkEqu(-1, ReQStringUtil::valueOfHexDigit('f' + 1));
-       }
-       void testUtf8() {
-               ReString name = "Heinz Müller";
-               char buffer[32];
-               checkEqu("Heinz Müller",
-                   ReQStringUtil::utf8(name, buffer, sizeof buffer));
-               memset(buffer, 'x', sizeof buffer);
-               checkEqu("Heinz", ReQStringUtil::utf8(name, buffer, (size_t)(5 + 1)));
-               checkEqu(buffer[6], 'x');
-       }
-
-       void testUnitParser() {
-               ReUnitParser parser("-1-2*3*4+2^3*4", NULL);
-               checkT(parser.isValid());
-               checkEqu(7, parser.asInt());
-               checkEqu(7LL, parser.asInt64());
-               checkEqu(7.0, parser.asReal());
-       }
-       void testSizeParser() {
-               ReSizeParser parser("2^3byte+2*1k+1m+1g+1t");
-               checkT(parser.isValid());
-               checkEqu(1001001002008LL, parser.asInt64());
-
-               ReSizeParser parser2("1ki+1mi+1gi+1ti");
-               checkT(parser2.isValid());
-               checkEqu(1100586419200ll, parser2.asInt64());
-       }
-       void testDateTimeParser() {
-               ReDateTimeParser parser("3.4.2014");
-               checkEqu(QDateTime(QDate(2014, 4, 3)), parser.asDateTime());
-               ReDateTimeParser parser2("21.4.2014-2w");
-               checkEqu(QDateTime(QDate(2014, 4, 7)), parser2.asDateTime());
-               ReDateTimeParser parserB2("1+1min+1h+1day+1week");
-               checkT(parserB2.isValid());
-               checkEqu(-694861, parserB2.asInt());
-       }
-       void testLengtOfDate() {
-               QDate date;
-               checkEqu(8, ReQStringUtil::lengthOfDate("1.2.2001", 0, &date));
-               checkEqu(QDate(2001, 2, 1), date);
-               checkEqu(9, ReQStringUtil::lengthOfDate("5.12.2001xxx", 0, &date));
-               checkEqu(QDate(2001, 12, 5), date);
-               checkEqu(10, ReQStringUtil::lengthOfDate("011.10.2001xxx", 1, &date));
-               checkEqu(QDate(2001, 10, 11), date);
-
-               checkEqu(8, ReQStringUtil::lengthOfDate("2001.2.1", 0, &date));
-               checkEqu(QDate(2001, 2, 1), date);
-               checkEqu(9, ReQStringUtil::lengthOfDate("2001.12.5xxx", 0, &date));
-               checkEqu(QDate(2001, 12, 5), date);
-               checkEqu(10, ReQStringUtil::lengthOfDate("02001.03.01xxx", 1, &date));
-               checkEqu(QDate(2001, 3, 1), date);
-       }
-       void testLengtOfTime() {
-               QTime time;
-               checkEqu(3, ReQStringUtil::lengthOfTime("1:2", 0, &time));
-               checkEqu(QTime(1, 2, 0), time);
-               checkEqu(5, ReQStringUtil::lengthOfTime("301:02", 1, &time));
-               checkEqu(QTime(1, 2, 0), time);
-               checkEqu(7, ReQStringUtil::lengthOfTime("301:02:9", 1, &time));
-               checkEqu(QTime(1, 2, 9), time);
-               checkEqu(8, ReQStringUtil::lengthOfTime("301:02:09x", 1, &time));
-               checkEqu(QTime(1, 2, 9), time);
-       }
-
-       virtual void run(void) {
-               testLengtOfTime();
-               testLengtOfDate();
-               testDateTimeParser();
-               testUnitParser();
-               testSizeParser();
-               testUtf8();
-               testLengthOfUInt64();
-               testLengthOfUInt();
-               testLengthOfReal();
-               testValueOfHexDigit();
-       }
-};
-void testReQStringUtil() {
-       TestReQStringUtil test;
-}
-
diff --git a/cunit/cuReQStringUtils.cpp b/cunit/cuReQStringUtils.cpp
new file mode 100644 (file)
index 0000000..3c06e38
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * cuReQStringUtil.cpp
+ *
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
+ * Do what you want.
+ * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
+ * The latest sources: https://github.com/republib
+ */
+/** @file
+ * @brief Unit test of the ReString tools.
+ */
+
+#include "../base/rebase.hpp"
+
+class TestReQStringUtil: public ReTest {
+public:
+       TestReQStringUtil() :
+                       ReTest("ReQStringUtil") {
+               doIt();
+       }
+
+public:
+       void testCountOf() {
+               checkEqu(2, ReQStringUtils::countOf(QString("axbx"), 'x'));
+               checkEqu(2, ReQStringUtils::countOf(QString("axbx"), 'x', 1));
+               checkEqu(1, ReQStringUtils::countOf(QString("axbx"), 'x', 2));
+               checkEqu(1, ReQStringUtils::countOf(QString("axbx"), 'x', 3));
+               checkEqu(0, ReQStringUtils::countOf(QString("axbx"), 'x', 4));
+               checkEqu(0, ReQStringUtils::countOf(QString("axbx"), 'x', 5));
+               checkEqu(0, ReQStringUtils::countOf(QString("axbx"), 'x', -1));
+       }
+
+       void testLengthOfUInt64() {
+               quint64 value = -3;
+               checkEqu(1,
+                       ReQStringUtils::lengthOfUInt64(ReString("0"), 0, 10, &value));
+               checkEqu(int64_t(0), value);
+               checkEqu(3, ReQStringUtils::lengthOfUInt64("x432", 1, 10, &value));
+               checkEqu(int64_t(432LL), value);
+               checkEqu(3, ReQStringUtils::lengthOfUInt64("x432 x", 1, 10, &value));
+               checkEqu(int64_t(432LL), value);
+               checkEqu(3, ReQStringUtils::lengthOfUInt64("x432fabc x", 1, 10, &value));
+               checkEqu(int64_t(432LL), value);
+               checkEqu(16,
+                       ReQStringUtils::lengthOfUInt64("a1234567890123567", 1, 10, &value));
+               checkEqu(int64_t(1234567890123567LL), value);
+               checkEqu(10,
+                       ReQStringUtils::lengthOfUInt64("x1234abcdef", 1, 16, &value));
+               checkEqu(int64_t(0x1234abcdefLL), value);
+               checkEqu(3, ReQStringUtils::lengthOfUInt64("432", 0, 8, &value));
+               checkEqu(int64_t(0432LL), value);
+               checkEqu(6, ReQStringUtils::lengthOfUInt64(" 765432 ", 1, 8, &value));
+               checkEqu(int64_t(0765432LL), value);
+
+               checkEqu(0, ReQStringUtils::lengthOfUInt64("1 ", 1, 8, &value));
+               checkEqu(0, ReQStringUtils::lengthOfUInt64("", 1, 8, &value));
+       }
+       void testLengthOfUInt() {
+               uint value = 3;
+               checkEqu(1, ReQStringUtils::lengthOfUInt(ReString("0"), 0, 10, &value));
+               checkEqu(0, value);
+               checkEqu(3, ReQStringUtils::lengthOfUInt("x432", 1, 10, &value));
+               checkEqu(432, value);
+               checkEqu(3, ReQStringUtils::lengthOfUInt("x432 x", 1, 10, &value));
+               checkEqu(432, value);
+               checkEqu(3, ReQStringUtils::lengthOfUInt("x432fabc x", 1, 10, &value));
+               checkEqu(432, value);
+               checkEqu(3, ReQStringUtils::lengthOfUInt("432", 0, 8, &value));
+               checkEqu(0432, value);
+               checkEqu(6, ReQStringUtils::lengthOfUInt(" 765432 ", 1, 8, &value));
+               checkEqu(0765432, value);
+
+               checkEqu(0, ReQStringUtils::lengthOfUInt("1 ", 1, 8, &value));
+               checkEqu(0, ReQStringUtils::lengthOfUInt("", 1, 8, &value));
+       }
+       void testLengthOfReal() {
+               qreal value;
+               checkEqu(4, ReQStringUtils::lengthOfReal(ReString("0.25"), 0, &value));
+               checkEqu(0.25, value);
+               checkEqu(3, ReQStringUtils::lengthOfReal(ReString("X.25"), 1, &value));
+               checkEqu(0.25, value);
+               checkEqu(1, ReQStringUtils::lengthOfReal(ReString(" 0"), 1, &value));
+               checkEqu(0.0, value);
+               checkEqu(17,
+                       ReQStringUtils::lengthOfReal(ReString("X12345678901234567"), 1,
+                               &value));
+               checkEqu(12345678901234567.0, value);
+               checkEqu(2, ReQStringUtils::lengthOfReal(ReString(".5"), 0, &value));
+               checkEqu(0.5, value);
+               checkEqu(5, ReQStringUtils::lengthOfReal(ReString("2.5e2x"), 0, &value));
+               checkEqu(250.0, value);
+               checkEqu(6, ReQStringUtils::lengthOfReal(ReString("2.5e+2"), 0, &value));
+               checkEqu(250.0, value);
+               checkEqu(7,
+                       ReQStringUtils::lengthOfReal(ReString("2.5E-33"), 0, &value));
+               checkEqu(2.5e-33, value);
+
+               checkEqu(3, ReQStringUtils::lengthOfReal(ReString("2.5E"), 0, &value));
+               checkEqu(2.5, value);
+               checkEqu(3, ReQStringUtils::lengthOfReal(ReString("2.5E+"), 0, &value));
+               checkEqu(2.5, value);
+               checkEqu(3, ReQStringUtils::lengthOfReal(ReString("2.5E-a"), 0, &value));
+               checkEqu(2.5, value);
+       }
+
+       void testValueOfHexDigit() {
+               checkEqu(0, ReQStringUtils::valueOfHexDigit('0'));
+               checkEqu(9, ReQStringUtils::valueOfHexDigit('9'));
+               checkEqu(10, ReQStringUtils::valueOfHexDigit('a'));
+               checkEqu(15, ReQStringUtils::valueOfHexDigit('f'));
+               checkEqu(10, ReQStringUtils::valueOfHexDigit('A'));
+               checkEqu(15, ReQStringUtils::valueOfHexDigit('F'));
+
+               checkEqu(-1, ReQStringUtils::valueOfHexDigit('0' - 1));
+               checkEqu(-1, ReQStringUtils::valueOfHexDigit('9' + 1));
+               checkEqu(-1, ReQStringUtils::valueOfHexDigit('A' - 1));
+               checkEqu(-1, ReQStringUtils::valueOfHexDigit('F' + 1));
+               checkEqu(-1, ReQStringUtils::valueOfHexDigit('a' - 1));
+               checkEqu(-1, ReQStringUtils::valueOfHexDigit('f' + 1));
+       }
+       void testUtf8() {
+               ReString name = "Heinz Müller";
+               char buffer[32];
+               checkEqu("Heinz Müller",
+                       ReQStringUtils::utf8(name, buffer, sizeof buffer));
+               memset(buffer, 'x', sizeof buffer);
+               checkEqu("Heinz", ReQStringUtils::utf8(name, buffer, (size_t)(5 + 1)));
+               checkEqu(buffer[6], 'x');
+       }
+
+       void testUnitParser() {
+               ReUnitParser parser("-1-2*3*4+2^3*4", NULL);
+               checkT(parser.isValid());
+               checkEqu(7, parser.asInt());
+               checkEqu(7LL, parser.asInt64());
+               checkEqu(7.0, parser.asReal());
+       }
+       void testSizeParser() {
+               ReSizeParser parser("2^3byte+2*1k+1m+1g+1t");
+               checkT(parser.isValid());
+               checkEqu(1001001002008LL, parser.asInt64());
+
+               ReSizeParser parser2("1ki+1mi+1gi+1ti");
+               checkT(parser2.isValid());
+               checkEqu(1100586419200ll, parser2.asInt64());
+       }
+       void testDateTimeParser() {
+               ReDateTimeParser parser("3.4.2014");
+               checkEqu(QDateTime(QDate(2014, 4, 3)), parser.asDateTime());
+               ReDateTimeParser parser2("21.4.2014-2w");
+               checkEqu(QDateTime(QDate(2014, 4, 7)), parser2.asDateTime());
+               ReDateTimeParser parserB2("1+1min+1h+1day+1week");
+               checkT(parserB2.isValid());
+               checkEqu(-694861, parserB2.asInt());
+       }
+       void testLengtOfDate() {
+               QDate date;
+               checkEqu(8, ReQStringUtils::lengthOfDate("1.2.2001", 0, &date));
+               checkEqu(QDate(2001, 2, 1), date);
+               checkEqu(9, ReQStringUtils::lengthOfDate("5.12.2001xxx", 0, &date));
+               checkEqu(QDate(2001, 12, 5), date);
+               checkEqu(10, ReQStringUtils::lengthOfDate("011.10.2001xxx", 1, &date));
+               checkEqu(QDate(2001, 10, 11), date);
+
+               checkEqu(8, ReQStringUtils::lengthOfDate("2001.2.1", 0, &date));
+               checkEqu(QDate(2001, 2, 1), date);
+               checkEqu(9, ReQStringUtils::lengthOfDate("2001.12.5xxx", 0, &date));
+               checkEqu(QDate(2001, 12, 5), date);
+               checkEqu(10, ReQStringUtils::lengthOfDate("02001.03.01xxx", 1, &date));
+               checkEqu(QDate(2001, 3, 1), date);
+       }
+       void testLengtOfTime() {
+               QTime time;
+               checkEqu(3, ReQStringUtils::lengthOfTime("1:2", 0, &time));
+               checkEqu(QTime(1, 2, 0), time);
+               checkEqu(5, ReQStringUtils::lengthOfTime("301:02", 1, &time));
+               checkEqu(QTime(1, 2, 0), time);
+               checkEqu(7, ReQStringUtils::lengthOfTime("301:02:9", 1, &time));
+               checkEqu(QTime(1, 2, 9), time);
+               checkEqu(8, ReQStringUtils::lengthOfTime("301:02:09x", 1, &time));
+               checkEqu(QTime(1, 2, 9), time);
+       }
+
+       virtual void run(void) {
+               testLengtOfTime();
+               testLengtOfDate();
+               testDateTimeParser();
+               testUnitParser();
+               testSizeParser();
+               testUtf8();
+               testLengthOfUInt64();
+               testLengthOfUInt();
+               testLengthOfReal();
+               testValueOfHexDigit();
+       }
+};
+void testReQStringUtil() {
+       TestReQStringUtil test;
+}
+
diff --git a/cunit/cuReSettings.cpp b/cunit/cuReSettings.cpp
new file mode 100644 (file)
index 0000000..f79c3f4
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * cuReSettings.cpp
+ *
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
+ * Do what you want.
+ * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
+ * The latest sources: https://github.com/republib
+ */
+
+/** @file
+ * @brief Unit test of the ReSettings tools.
+ */
+#include "base/rebase.hpp"
+#include "gui/regui.hpp"
+/**
+ * @brief Unit test for <code>ReSettings</code>.
+ */
+class TestReSettings: public ReTest {
+public:
+       TestReSettings() :
+                       ReTest("ReSettings") {
+               doIt();
+       }
+
+public:
+       void testAddHistoryEntry(){
+               QByteArray dir(ReFile::tempDir("resettings", NULL, false));
+               ReFile::deleteTree((QString) dir, false, &m_logger);
+               {
+                       ReSettings settings(dir, "test", &m_logger);
+                       settings.addHistoryEntry("digits", "4", ' ', 3);
+                       settings.addHistoryEntry("digits", "3", ' ', 3);
+                       settings.addHistoryEntry("digits", "2", ' ', 3);
+                       settings.addHistoryEntry("digits", "1", ' ', 3);
+               }
+               ReSettings settings(dir, "test", &m_logger);
+               QStringList list;
+               settings.historyAsList("digits", list);
+               checkEqu("1 2 3", list.join(' '));
+       }
+       void setSettings(ReSettings& settings){
+               settings.insertProperty(new ReProperty("level1.boolVal", "Boolean",
+                                                                                          "Boolean value for test", ReSettings::TRUE, PT_BOOL));
+               settings.insertProperty(new ReProperty("level1.intVal", "Integer",
+                                                                                          "Integer value for test", "4711", PT_INT, "[0,9999]"));
+               settings.insertProperty(new ReProperty("level2.strVal", "String",
+                                                                                          "String value for test", "crazy horse", PT_STRING));
+
+       }
+
+       void testBasic(){
+               QByteArray dir(ReFile::tempDir("resettings", NULL, false));
+               ReFile::deleteTree((QString) dir, false, &m_logger);
+               ReSettings settings(dir, "test", &m_memoryLogger);
+               setSettings(settings);
+               settings.writeSettings();
+               checkT(settings.boolValue("level1.boolVal"));
+               checkEqu(4711, settings.intValue("level1.intVal"));
+               checkEqu("crazy horse", settings.stringValue("level2.strVal"));
+               // unknown names:
+               checkF(settings.boolValue("level2.boolVal"));
+               logContains("level2.boolValue");
+               m_memoryAppender.clear();
+               checkEqu(0, settings.intValue("level2.intVal"));
+               logContains("level2.intVal");
+               m_memoryAppender.clear();
+               checkEqu("", settings.stringValue("strVal"));
+               logContains("stringVal");
+               m_memoryAppender.clear();
+
+               ReSettings settings2(dir, "test", &m_memoryLogger);
+               setSettings(settings2);
+               settings2.changeValue("level1.boolVal", ReSettings::FALSE);
+               settings2.changeValue("level1.intVal", "1234");
+               settings2.changeValue("level2.strVal", "pretty women");
+               settings2.writeSettings();
+
+               settings.readSettings();
+               checkF(settings.boolValue("level1.boolVal"));
+               checkEqu(1234, settings.intValue("level1.intVal"));
+               checkEqu("pretty woman", settings.stringValue("level2.strVal"));
+       }
+
+       virtual void run() {
+               testBasic();
+               testAddHistoryEntry();
+       }
+};
+
+void testReSettings() {
+       TestReSettings test;
+}
+
diff --git a/cunit/cuReStringUtil.cpp b/cunit/cuReStringUtil.cpp
deleted file mode 100644 (file)
index d71ab6d..0000000
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * cuReStringUtil.cpp
- *
- * License: Public Domain
- * You can use and modify this file without any restriction.
- * Do what you want.
- * No warranties and disclaimer of any damages.
- * You also can use this license: http://www.wtfpl.net
- * The latest sources: https://github.com/republib
- */
-
-/** @file
- * @brief Unit test of the QByteArray tools.
- */
-#include "base/rebase.hpp"
-/**
- * @brief Unit test for <code>ReStringUtil</code>.
- */
-class TestReStringUtil: public ReTest {
-public:
-       TestReStringUtil() :
-                   ReTest("ReStringUtil") {
-               doIt();
-       }
-
-public:
-       void testCountChar() {
-               checkEqu(1, ReStringUtil::countChar("x", 'x'));
-               checkEqu(0, ReStringUtil::countChar("X", 'x'));
-               checkEqu(2, ReStringUtil::countChar("xbxxbxx", 'b'));
-       }
-
-       void testCount() {
-               checkEqu(0, ReStringUtil::count("abc", " "));
-               checkEqu(1, ReStringUtil::count("abc", "b"));
-               checkEqu(2, ReStringUtil::count("axx", "x"));
-
-               checkEqu(0, ReStringUtil::count("abbc", "bbb"));
-               checkEqu(1, ReStringUtil::count("\n\n", "\n\n"));
-               checkEqu(2, ReStringUtil::count("  a  ", "  "));
-       }
-
-       void testCutString() {
-               QByteArray source("123");
-               QByteArray buffer;
-               checkEqu(QByteArray("123"), ReStringUtil::cutString(source, 4, buffer));
-               checkEqu(QByteArray("123"), ReStringUtil::cutString(source, 3, buffer));
-               checkEqu(QByteArray("12..."),
-                   ReStringUtil::cutString(source, 2, buffer));
-               checkEqu(QByteArray("12"),
-                   ReStringUtil::cutString(source, 2, buffer, ""));
-       }
-
-       void testHexDump() {
-               QByteArray data("abc123\nxyz");
-               checkEqu(QByteArray("61 62 63 31  abc1\n"
-                       "32 33 0a 78  23.x\n"
-                       "79 7a        yz\n"),
-                   ReStringUtil::hexDump((uint8_t* ) data.constData(), data.length(),
-                       4));
-               checkEqu(QByteArray("61 62 63 31 32 33 0a 78 79 7a  abc123.xyz"),
-                   ReStringUtil::hexDump((uint8_t* ) data.constData(), data.length(),
-                       10));
-               checkEqu(QByteArray("61 62 63 31 32 33 0a 78 79 7a        abc123.xyz"),
-                   ReStringUtil::hexDump((uint8_t* ) data.constData(), data.length(),
-                       12));
-       }
-
-       void testReadWrite() {
-               QByteArray fn = getTempFile("test.dat");
-               const char* content = "Hello world\nLine2\n";
-               checkT(ReStringUtil::write(fn, content));
-               checkEqu(content, ReStringUtil::read(fn, false));
-               checkEqu(content, ReStringUtil::read(fn, true) + "\n");
-       }
-
-       void testToArray() {
-               QList < QByteArray > array = ReStringUtil::toArray("1 abc 3", " ");
-               checkEqu(3, array.size());
-               checkEqu("1", array.at(0));
-               checkEqu("abc", array.at(1));
-               checkEqu("3", array.at(2));
-       }
-
-       void testToNumber() {
-               checkEqu("3", ReStringUtil::toNumber(3));
-               checkEqu("-33", ReStringUtil::toNumber(-33));
-               checkEqu("003", ReStringUtil::toNumber(3, "%03d"));
-       }
-
-       void testLengthOfNumber() {
-               checkEqu(3, ReStringUtil::lengthOfNumber("0.3xxx"));
-               checkEqu(5, ReStringUtil::lengthOfNumber(" \t0.3xxx"));
-               checkEqu(3, ReStringUtil::lengthOfNumber("-.3xxx"));
-               checkEqu(2, ReStringUtil::lengthOfNumber(".3exxx"));
-               checkEqu(2, ReStringUtil::lengthOfNumber(".3e+xxx"));
-               checkEqu(16, ReStringUtil::lengthOfNumber("1234567.9012E+77"));
-               checkEqu(17, ReStringUtil::lengthOfNumber("-1234567.9012E+77 "));
-               checkEqu(18, ReStringUtil::lengthOfNumber("-1234567.9012E+77 ", true));
-               checkEqu(18, ReStringUtil::lengthOfNumber("-1234567.9012E+77 x", true));
-               checkEqu(20,
-                   ReStringUtil::lengthOfNumber("  -1234567.9012E+77 x", true));
-       }
-
-       void checkCsv(const char* content, char expected) {
-               QByteArray fn = getTempFile("testrplstring.csv");
-               ReStringUtil::write(fn, content);
-               FILE* fp = fopen(fn, "r");
-               checkNN(fp);
-               char buffer[256];
-               checkEqu(expected,
-                   ReStringUtil::findCsvSeparator(fp, buffer, sizeof buffer));
-               fclose(fp);
-       }
-
-       void testFindCsvSeparator() {
-               const char* content = ",,,\t;;;||||";
-               checkCsv(content, '\t');
-
-               content = "col1,col2\n1.5,3,5\n";
-               checkCsv(content, ',');
-
-               content = "col1;col2\n1,50;3.5\n"
-                       "7;8\n10;12\n13;14";
-               checkCsv(content, ';');
-
-               content = "0.3 7.8 8.9\n7.8 9.4 8.3";
-               checkCsv(content, ' ');
-
-               content = "0.3|7.8|8.9\n7.8|         9.4|8.3";
-               checkCsv(content, '|');
-
-               content = "0,3;7.8;8.9";
-               checkCsv(content, ';');
-       }
-       void testLengthOfUInt64() {
-               quint64 value = -3;
-               checkEqu(1, ReStringUtil::lengthOfUInt64("0", 10, &value));
-               checkEqu((int64_t ) 0LL, value);
-               checkEqu(3, ReStringUtil::lengthOfUInt64("432", 10, &value));
-               checkEqu((int64_t ) 432LL, value);
-               checkEqu(3, ReStringUtil::lengthOfUInt64("432 x", 10, &value));
-               checkEqu((int64_t ) 432LL, value);
-               checkEqu(3, ReStringUtil::lengthOfUInt64("432fabc x", 10, &value));
-               checkEqu((int64_t ) 432LL, value);
-               checkEqu(16,
-                   ReStringUtil::lengthOfUInt64("1234567890123567", 10, &value));
-               checkEqu((int64_t ) 1234567890123567LL, value);
-               checkEqu(10, ReStringUtil::lengthOfUInt64("1234abcdef", 16, &value));
-               checkEqu((int64_t ) 0x1234abcdefLL, value);
-               checkEqu(3, ReStringUtil::lengthOfUInt64("432", 8, &value));
-               checkEqu((int64_t ) 0432LL, value);
-               checkEqu(6, ReStringUtil::lengthOfUInt64("765432 ", 8, &value));
-               checkEqu((int64_t ) 0765432LL, value);
-
-               checkEqu(0, ReStringUtil::lengthOfUInt64(" ", 8, &value));
-               checkEqu(0, ReStringUtil::lengthOfUInt64("", 8, &value));
-       }
-       void testLengthOfReal() {
-               qreal value;
-               checkEqu(1, ReStringUtil::lengthOfReal("0", &value));
-               checkEqu(0.0, value);
-               checkEqu(1, ReStringUtil::lengthOfReal("0%", &value));
-               checkEqu(0.0, value);
-               checkEqu(4, ReStringUtil::lengthOfReal("0.25", &value));
-               checkEqu(0.25, value);
-               checkEqu(3, ReStringUtil::lengthOfReal(".25", &value));
-               checkEqu(0.25, value);
-               checkEqu(17, ReStringUtil::lengthOfReal("12345678901234567", &value));
-               checkEqu(12345678901234567.0, value);
-               checkEqu(2, ReStringUtil::lengthOfReal(".5", &value));
-               checkEqu(0.5, value);
-               checkEqu(5, ReStringUtil::lengthOfReal("2.5e2x", &value));
-               checkEqu(250.0, value);
-               checkEqu(6, ReStringUtil::lengthOfReal("2.5e+2", &value));
-               checkEqu(250.0, value);
-               checkEqu(7, ReStringUtil::lengthOfReal("2.5E-33", &value));
-               checkEqu(2.5e-33, value);
-
-               checkEqu(3, ReStringUtil::lengthOfReal("2.5E", &value));
-               checkEqu(2.5, value);
-               checkEqu(3, ReStringUtil::lengthOfReal("2.5E+", &value));
-               checkEqu(2.5, value);
-               checkEqu(3, ReStringUtil::lengthOfReal("2.5E-a", &value));
-               checkEqu(2.5, value);
-       }
-
-       virtual void run() {
-               testLengthOfReal();
-               testLengthOfUInt64();
-               testCountChar();
-               testCount();
-               testCutString();
-               testToNumber();
-               testToArray();
-               testHexDump();
-               testReadWrite();
-               testLengthOfNumber();
-               testFindCsvSeparator();
-       }
-};
-
-void testReStringUtil() {
-       TestReStringUtil test;
-}
-
diff --git a/cunit/cuReStringUtils.cpp b/cunit/cuReStringUtils.cpp
new file mode 100644 (file)
index 0000000..bb987ab
--- /dev/null
@@ -0,0 +1,206 @@
+/*
+ * cuReStringUtil.cpp
+ *
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
+ * Do what you want.
+ * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
+ * The latest sources: https://github.com/republib
+ */
+
+/** @file
+ * @brief Unit test of the QByteArray tools.
+ */
+#include "base/rebase.hpp"
+/**
+ * @brief Unit test for <code>ReStringUtil</code>.
+ */
+class TestReStringUtil: public ReTest {
+public:
+       TestReStringUtil() :
+                       ReTest("ReStringUtil") {
+               doIt();
+       }
+
+public:
+       void testCountChar() {
+               checkEqu(1, ReStringUtils::countChar("x", 'x'));
+               checkEqu(0, ReStringUtils::countChar("X", 'x'));
+               checkEqu(2, ReStringUtils::countChar("xbxxbxx", 'b'));
+       }
+
+       void testCount() {
+               checkEqu(0, ReStringUtils::count("abc", " "));
+               checkEqu(1, ReStringUtils::count("abc", "b"));
+               checkEqu(2, ReStringUtils::count("axx", "x"));
+
+               checkEqu(0, ReStringUtils::count("abbc", "bbb"));
+               checkEqu(1, ReStringUtils::count("\n\n", "\n\n"));
+               checkEqu(2, ReStringUtils::count("  a  ", "  "));
+       }
+
+       void testCutString() {
+               QByteArray source("123");
+               QByteArray buffer;
+               checkEqu(QByteArray("123"), ReStringUtils::cutString(source, 4, buffer));
+               checkEqu(QByteArray("123"), ReStringUtils::cutString(source, 3, buffer));
+               checkEqu(QByteArray("12..."),
+                       ReStringUtils::cutString(source, 2, buffer));
+               checkEqu(QByteArray("12"),
+                       ReStringUtils::cutString(source, 2, buffer, ""));
+       }
+
+       void testHexDump() {
+               QByteArray data("abc123\nxyz");
+               checkEqu(QByteArray("61 62 63 31  abc1\n"
+                       "32 33 0a 78  23.x\n"
+                       "79 7a        yz\n"),
+                       ReStringUtils::hexDump((uint8_t* ) data.constData(), data.length(),
+                               4));
+               checkEqu(QByteArray("61 62 63 31 32 33 0a 78 79 7a  abc123.xyz"),
+                       ReStringUtils::hexDump((uint8_t* ) data.constData(), data.length(),
+                               10));
+               checkEqu(QByteArray("61 62 63 31 32 33 0a 78 79 7a        abc123.xyz"),
+                       ReStringUtils::hexDump((uint8_t* ) data.constData(), data.length(),
+                               12));
+       }
+
+       void testReadWrite() {
+               QByteArray fn = getTempFile("test.dat");
+               const char* content = "Hello world\nLine2\n";
+               checkT(ReStringUtils::write(fn, content));
+               checkEqu(content, ReStringUtils::read(fn, false));
+               checkEqu(content, ReStringUtils::read(fn, true) + "\n");
+       }
+
+       void testToArray() {
+               QList < QByteArray > array = ReStringUtils::toArray("1 abc 3", " ");
+               checkEqu(3, array.size());
+               checkEqu("1", array.at(0));
+               checkEqu("abc", array.at(1));
+               checkEqu("3", array.at(2));
+       }
+
+       void testToNumber() {
+               checkEqu("3", ReStringUtils::toNumber(3));
+               checkEqu("-33", ReStringUtils::toNumber(-33));
+               checkEqu("003", ReStringUtils::toNumber(3, "%03d"));
+       }
+
+       void testLengthOfNumber() {
+               checkEqu(3, ReStringUtils::lengthOfNumber("0.3xxx"));
+               checkEqu(5, ReStringUtils::lengthOfNumber(" \t0.3xxx"));
+               checkEqu(3, ReStringUtils::lengthOfNumber("-.3xxx"));
+               checkEqu(2, ReStringUtils::lengthOfNumber(".3exxx"));
+               checkEqu(2, ReStringUtils::lengthOfNumber(".3e+xxx"));
+               checkEqu(16, ReStringUtils::lengthOfNumber("1234567.9012E+77"));
+               checkEqu(17, ReStringUtils::lengthOfNumber("-1234567.9012E+77 "));
+               checkEqu(18, ReStringUtils::lengthOfNumber("-1234567.9012E+77 ", true));
+               checkEqu(18, ReStringUtils::lengthOfNumber("-1234567.9012E+77 x", true));
+               checkEqu(20,
+                       ReStringUtils::lengthOfNumber("  -1234567.9012E+77 x", true));
+       }
+
+       void checkCsv(const char* content, char expected) {
+               QByteArray fn = getTempFile("testrplstring.csv");
+               ReStringUtils::write(fn, content);
+               FILE* fp = fopen(fn, "r");
+               checkNN(fp);
+               char buffer[256];
+               checkEqu(expected,
+                       ReStringUtils::findCsvSeparator(fp, buffer, sizeof buffer));
+               fclose(fp);
+       }
+
+       void testFindCsvSeparator() {
+               const char* content = ",,,\t;;;||||";
+               checkCsv(content, '\t');
+
+               content = "col1,col2\n1.5,3,5\n";
+               checkCsv(content, ',');
+
+               content = "col1;col2\n1,50;3.5\n"
+                       "7;8\n10;12\n13;14";
+               checkCsv(content, ';');
+
+               content = "0.3 7.8 8.9\n7.8 9.4 8.3";
+               checkCsv(content, ' ');
+
+               content = "0.3|7.8|8.9\n7.8|         9.4|8.3";
+               checkCsv(content, '|');
+
+               content = "0,3;7.8;8.9";
+               checkCsv(content, ';');
+       }
+       void testLengthOfUInt64() {
+               quint64 value = -3;
+               checkEqu(1, ReStringUtils::lengthOfUInt64("0", 10, &value));
+               checkEqu((int64_t ) 0LL, value);
+               checkEqu(3, ReStringUtils::lengthOfUInt64("432", 10, &value));
+               checkEqu((int64_t ) 432LL, value);
+               checkEqu(3, ReStringUtils::lengthOfUInt64("432 x", 10, &value));
+               checkEqu((int64_t ) 432LL, value);
+               checkEqu(3, ReStringUtils::lengthOfUInt64("432fabc x", 10, &value));
+               checkEqu((int64_t ) 432LL, value);
+               checkEqu(16,
+                       ReStringUtils::lengthOfUInt64("1234567890123567", 10, &value));
+               checkEqu((int64_t ) 1234567890123567LL, value);
+               checkEqu(10, ReStringUtils::lengthOfUInt64("1234abcdef", 16, &value));
+               checkEqu((int64_t ) 0x1234abcdefLL, value);
+               checkEqu(3, ReStringUtils::lengthOfUInt64("432", 8, &value));
+               checkEqu((int64_t ) 0432LL, value);
+               checkEqu(6, ReStringUtils::lengthOfUInt64("765432 ", 8, &value));
+               checkEqu((int64_t ) 0765432LL, value);
+
+               checkEqu(0, ReStringUtils::lengthOfUInt64(" ", 8, &value));
+               checkEqu(0, ReStringUtils::lengthOfUInt64("", 8, &value));
+       }
+       void testLengthOfReal() {
+               qreal value;
+               checkEqu(1, ReStringUtils::lengthOfReal("0", &value));
+               checkEqu(0.0, value);
+               checkEqu(1, ReStringUtils::lengthOfReal("0%", &value));
+               checkEqu(0.0, value);
+               checkEqu(4, ReStringUtils::lengthOfReal("0.25", &value));
+               checkEqu(0.25, value);
+               checkEqu(3, ReStringUtils::lengthOfReal(".25", &value));
+               checkEqu(0.25, value);
+               checkEqu(17, ReStringUtils::lengthOfReal("12345678901234567", &value));
+               checkEqu(12345678901234567.0, value);
+               checkEqu(2, ReStringUtils::lengthOfReal(".5", &value));
+               checkEqu(0.5, value);
+               checkEqu(5, ReStringUtils::lengthOfReal("2.5e2x", &value));
+               checkEqu(250.0, value);
+               checkEqu(6, ReStringUtils::lengthOfReal("2.5e+2", &value));
+               checkEqu(250.0, value);
+               checkEqu(7, ReStringUtils::lengthOfReal("2.5E-33", &value));
+               checkEqu(2.5e-33, value);
+
+               checkEqu(3, ReStringUtils::lengthOfReal("2.5E", &value));
+               checkEqu(2.5, value);
+               checkEqu(3, ReStringUtils::lengthOfReal("2.5E+", &value));
+               checkEqu(2.5, value);
+               checkEqu(3, ReStringUtils::lengthOfReal("2.5E-a", &value));
+               checkEqu(2.5, value);
+       }
+
+       virtual void run() {
+               testLengthOfReal();
+               testLengthOfUInt64();
+               testCountChar();
+               testCount();
+               testCutString();
+               testToNumber();
+               testToArray();
+               testHexDump();
+               testReadWrite();
+               testLengthOfNumber();
+               testFindCsvSeparator();
+       }
+};
+
+void testReStringUtil() {
+       TestReStringUtil test;
+}
+
index 586567aa3d2269a946fc9d21e33a15674af13f83..f9d45f78f77d01a98244a20d7b133b71531cbbbf 100644 (file)
@@ -36,7 +36,7 @@ private:
                writer.format("%3c%.2f", ':', 3.1415);
                writer.writeLine();
                writer.close();
-               QByteArray current = ReStringUtil::read(fn, false);
+               QByteArray current = ReStringUtils::read(fn, false);
                checkEqu("abc\n0042\n\t\t\t123\n\t\tpi  :3.14\n", current);
        }
 
index f04aecf6bb70659ea5e0bbfc2357b23404bed76d..6bb2b9eb4e99fd66fdd3944c72ee204ba2b6ed8d 100644 (file)
@@ -16,8 +16,10 @@ TEMPLATE = app
 INCLUDEPATH = ..
 
 SOURCES += main.cpp \
-        cuReQStringUtil.cpp \
-        cuReStringUtil.cpp \
+        cuReQStringUtils.cpp \
+        cuReStringUtils.cpp \
+        cuReFile.cpp \
+        cuReFileUtils.cpp \
         cuReByteStorage.cpp \
         cuReException.cpp \
         ../base/ReByteStorage.cpp \
@@ -26,21 +28,23 @@ SOURCES += main.cpp \
         ../base/ReContainer.cpp \
         ../base/ReException.cpp \
         ../base/ReFile.cpp \
-        ../base/ReQStringUtil.cpp \
+        ../base/ReFileUtils.cpp \
+        ../base/ReQStringUtils.cpp \
         ../base/ReLogger.cpp \
-        ../base/ReStringUtil.cpp \
+        ../base/ReStringUtils.cpp \
         ../base/ReTerminator.cpp \
         ../base/ReTest.cpp \
         ../base/ReWriter.cpp \
         ../gui/ReStateStorage.cpp \
+        ../gui/ReSettings.cpp \
        ../gui/ReEdit.cpp \
         cuReConfig.cpp \
         cuReContainer.cpp \
         cuReWriter.cpp \
         cuReCharPtrMap.cpp \
-        cuReFile.cpp \
        cuReEdit.cpp \
        cuReStateStorage.cpp \
+       cuReSettings.cpp \
         allTests.cpp
 
 HEADERS += \
diff --git a/gui/ReSettings.cpp b/gui/ReSettings.cpp
new file mode 100644 (file)
index 0000000..2133768
--- /dev/null
@@ -0,0 +1,349 @@
+/*
+ * storage.cpp
+ *
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
+ * Do what you want.
+ * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
+ * The latest sources: https://github.com/republib
+ */
+
+#include "base/rebase.hpp"
+#include "gui/regui.hpp"
+
+enum {
+       LOC_BOOL_VALUE_1 = LOC_FIRST_OF(LOC_SETTINGS), // 11701
+       LOC_BOOL_VALUE_2,       // 11702
+       LOC_INT_VALUE_1,        // 11703
+       LOC_INT_VALUE_2,        // 11704
+       LOC_STRING_VALUE_1,     // 11705
+       LOC_STRING_VALUE_2,     // 11706
+       LOC_WRITE_SETTINGS_1,   // 11707
+       LOC_READ_SETTINGS_1,    // 11708
+       LOC_READ_SETTINGS_2,    // 11709
+       LOC_READ_SETTINGS_3,    // 11710
+       LOC_CHANGE_VALUE_1,             // 11711
+       LOC_CHANGE_VALUE_2,             // 11712
+};
+
+QString ReSettings::TRUE = "t";
+QString ReSettings::FALSE = "";
+
+/**
+ * Constructor.
+ *
+ * @param name name of the property
+ * @param title        title in the configuration form (translated)
+ * @param description description in the form (translated)
+ * @param defaultValue the value if not user defined
+ * @param type the type of the property
+ * @param limits       NULL or some rules for the property value
+ */
+ReProperty::ReProperty(const char* name, const QString& title, const QString& description, const QString& defaultValue, RePropertyType type, const char* limits) :
+       m_name(name),
+       m_title(title),
+       m_description(description),
+       m_value(defaultValue),
+       m_defaultValue(defaultValue),
+       m_type(type),
+       m_limits(limits) {
+}
+
+/**
+ * Tests whether a given value is allowed respecting the <code>m_limits</code>
+ * @param value        the value to test
+ * @param error        OUT: NULL or the error message
+ * @return <code>true</code>: the value is allowed<br>
+ */
+bool ReProperty::isValid(const QString& value, QString* error)
+{
+       bool rc = true;
+       if (error != NULL)
+               *error = "";
+       if (m_limits != NULL){
+               switch(m_type){
+               case PT_BOOL:
+                       break;
+               case PT_INT:
+               {
+                       int nValue = value.toInt(&rc);
+                       if (! rc){
+                               if (error != NULL)
+                                       *error = QObject::tr("not an integer: ") + value;
+                       } else {
+                               int minimum, maximum;
+                               if (sscanf(m_limits, "[%d,%d]", &minimum, &maximum) == 2)
+                               {
+                                       if (nValue < minimum){
+                                               rc = false;
+                                               if (error != NULL)
+                                                       *error = value + " < " + minimum;
+                                       } else if (nValue > maximum){
+                                               rc = false;
+                                               if (error != NULL)
+                                                       *error = value + " < " + maximum;
+                                       }
+                               }
+                       }
+                       break;
+               }
+               case PT_STRING:
+                       break;
+               default:
+                       break;
+               }
+       }
+       return rc;
+}
+
+/**
+ * Constructor.
+ *
+ * @param path         the parent directory for the storage files
+ * @param prefix       type of the storage: "proj" or "ws" (workspace)
+ */
+ReSettings::ReSettings(const QString& path, const QString prefix, ReLogger* logger) :
+               m_path(path),
+               m_fileHistory(path + OS_SEPARATOR + prefix + ".history"),
+               m_fileSettings(
+                       path + OS_SEPARATOR + prefix + ".settings"),
+               m_settings(),
+               m_chapters(),
+               m_logger(logger) {
+
+}
+
+/*
+ * Adds an entry to a history item at the first position.
+ *
+ * The entry will removed from the other positions.
+ *
+ * @param key          the key in the map
+ * @param value                the value to add
+ * @param separator    separates the entries in the history item
+ * @param maxEntries   the maximal count of entries in the history item.<br>
+ * If the number exceeds the last entries will be removed
+ * @param form the prefix of the key. If NULL the current form will be taken
+ */
+void ReSettings::addHistoryEntry(const char* key, const QString& value,
+       char separator, int maxEntries) {
+       ReStateStorage store(m_fileHistory);
+       store.addHistoryEntry(key, value, separator, maxEntries);
+       store.close();
+       store.flushMap();
+}
+
+/**
+ * Returns the value of a boolean property.
+ *
+ * @param name the name of the property
+ * @return the value of the property
+ */
+bool ReSettings::boolValue(const char* name) {
+       bool rc = false;
+       ReProperty* property = m_settings.value(name, NULL);
+       if (property == NULL)
+               m_logger->logv(LOG_ERROR, LOC_BOOL_VALUE_1, "missing bool property %s",
+                       name);
+       else if (property->m_type != PT_BOOL)
+               m_logger->logv(LOG_ERROR, LOC_BOOL_VALUE_2, "not a  bool property %s",
+                       name);
+       else
+               rc = !property->m_value.isEmpty();
+       return rc;
+}
+
+/**
+ * Changes the value of an existing property.
+ *
+ * @param name the name of the property
+ * @param value        the new value
+ */
+void ReSettings::changeValue(const char* name, const QString& value)
+{
+       QString error;
+       ReProperty* property = m_settings.value(name, NULL);
+       if (property == NULL)
+               m_logger->logv(LOG_ERROR, LOC_CHANGE_VALUE_1, "unknown property: %s",
+                                          name);
+       else if (!property->isValid(value, &error))
+               m_logger->logv(LOG_ERROR, LOC_CHANGE_VALUE_2, "invalid value for %s: %s\n+++ %s",
+                                       name, value.toUtf8().constData(),
+                                          error.toUtf8().constData());
+       else
+               property->m_value = value;
+}
+
+/**
+ * Returns a history item as a list.
+ *
+ * @param key  key of the history item
+ * @param list OUT: the list is filled with the history entries
+ * @param form a common prefix of the key. If NULL the current form is used
+ * @return <code>list</code> (for chaining)
+ */
+QStringList&ReSettings::historyAsList(const char* key, QStringList& list,
+       const char* form) {
+       ReStateStorage store(m_fileHistory);
+       QStringList& rc = store.historyAsList(key, list, form);
+       store.close();
+       return rc;
+}
+
+/**
+ * Inserts a property.
+ *
+ * @param property     the property to insert
+ */
+void ReSettings::insertProperty(ReProperty* property) {
+       m_settings.insert(property->m_name, property);
+       QByteArray chapter(property->m_name);
+       int ix = chapter.lastIndexOf('.');
+       chapter = chapter.left(ix);
+       QList<ReProperty*>* list = m_chapters.value(chapter, NULL);
+       if (list == NULL) {
+               m_chapters.insert(chapter, list = new QList<ReProperty*>());
+       }
+       list->append(property);
+}
+
+/**
+ * Returns the value of an integer property.
+ *
+ * @param name the name of the property
+ * @return the value of the property
+ */
+int ReSettings::intValue(const char* name) {
+       int rc = 0;
+       ReProperty* property = m_settings.value(name, NULL);
+       if (property == NULL)
+               m_logger->logv(LOG_ERROR, LOC_INT_VALUE_1, "missing int property %s",
+                       name);
+       else if (property->m_type != PT_INT)
+               m_logger->logv(LOG_ERROR, LOC_INT_VALUE_2, "not a  int property %s",
+                       name);
+       else
+               rc = property->m_value.toInt();
+       return rc;
+}
+
+/**
+ * Returns the directory containing the configuration data.
+ *
+ * @return the name of the storage's directory
+ */
+QString ReSettings::path() const {
+       return m_path;
+}
+
+/**
+ * Reads the configuration file.
+ */
+void ReSettings::readSettings()
+{
+       QFile file(m_fileSettings);
+       if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
+                       m_logger->logv(LOG_ERROR, LOC_READ_SETTINGS_1, "cannot open (%d): ",
+                                                  errno, m_fileSettings.toUtf8().constData());
+       else{
+               QTextStream input(&file);
+               int lineNo = 0;
+               while (!input.atEnd()) {
+                       lineNo++;
+                       QString line = input.readLine();
+                       int ix = line.indexOf('=');
+                       if (ix < 0)
+                               m_logger->logv(LOG_ERROR, LOC_READ_SETTINGS_2, "missing '=': %s-%d: %s",
+                                       m_fileSettings.toUtf8().constData(), lineNo,
+                                                          line.mid(0, 20).toUtf8().constData());
+                       else if (ix == 0 || ix == 1 && line.at(0) == '!')
+                               m_logger->logv(LOG_ERROR, LOC_READ_SETTINGS_3, "line starts with '=': %s-%d: %s",
+                                       m_fileSettings.toUtf8().constData(), lineNo,
+                                                          line.mid(0, 20).toUtf8().constData());
+                       else {
+                               QByteArray name;
+                               QString value;
+                               if (line.at(ix-1) == '!'){
+                                       name = line.left(ix - 1).toUtf8();
+                                       value = line.mid(ix + 1);
+                                       value.replace("\\\\", "\01").replace("\\n", "\n")
+                                                       .replace("\\r", "\r").replace('\01', '\\');
+                               } else {
+                                       name = line.left(ix).toUtf8();
+                                       value = line.mid(ix + 1);
+                               }
+                               ReProperty* property = m_settings.value(name, NULL);
+                               if (property != NULL){
+                                       switch(property->m_type){
+                                       case PT_BOOL:
+                                               property->m_value = value.isEmpty() ? ReSettings::FALSE
+                                                                                                                 : ReSettings::TRUE;
+                                               break;
+                                       case PT_INT:
+                                               if (property->isValid(value))
+                                                       property->m_value = value;
+                                               break;
+                                       case PT_STRING:
+                                               if (property->isValid(value))
+                                                       property->m_value = value;
+                                               break;
+                                       default:
+                                               break;
+                                       }
+                               }
+                       }
+               }
+               file.close();
+       }
+}
+
+/**
+ * Returns the value of a string property.
+ *
+ * @param name the name of the property
+ * @return the value of the property
+ */
+QString ReSettings::stringValue(const char* name) {
+       QString rc = "";
+       ReProperty* property = m_settings.value(name, NULL);
+       if (property == NULL)
+               m_logger->logv(LOG_ERROR, LOC_STRING_VALUE_1,
+                       "missing string property %s", name);
+       else if (property->m_type != PT_STRING)
+               m_logger->logv(LOG_ERROR, LOC_STRING_VALUE_2,
+                       "not a string property %s", name);
+       else
+               rc = property->m_value;
+       return rc;
+}
+
+/**
+ * Writes the values into the configuration file.
+ */
+void ReSettings::writeSettings()
+{
+       QFile file(m_fileSettings);
+       if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
+                       m_logger->logv(LOG_ERROR, LOC_WRITE_SETTINGS_1, "cannot open (%d): ",
+                                                  errno, m_fileSettings.toUtf8().constData());
+       else{
+               QTextStream out(&file);
+               QMap<const char*, ReProperty*>::const_iterator it;
+               for (it = m_settings.cbegin(); it != m_settings.cend(); ++it){
+                       ReProperty* property = it.value();
+                       if (property->m_value == property->m_defaultValue){
+                               // we do not write the default value
+                       } else if (it.value()->m_value.indexOf('\n') <= 0)
+                               out << it.key() << "=" << it.value()->m_value << "\n";
+                       else{
+                               QString value = it.value()->m_value;
+                               out << it.key() << "!=" << value.replace("\\", "\\\\")
+                                          .replace("\n", "\\n").replace("\r", "\\r") << "\n";
+                       }
+               }
+               file.close();
+       }
+}
+
+
diff --git a/gui/ReSettings.hpp b/gui/ReSettings.hpp
new file mode 100644 (file)
index 0000000..210c7bd
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * storage.hpp
+ *
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
+ * Do what you want.
+ * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
+ * The latest sources: https://github.com/republib
+ */
+
+#ifndef STORAGE_HPP
+#define STORAGE_HPP
+
+enum RePropertyType {
+       PT_UNDEF,
+       PT_INT,
+       PT_STRING,
+       PT_BOOL
+};
+
+class ReProperty {
+public:
+       ReProperty(const char* name, const QString& title, const QString& description,
+               const QString& defaultValue, RePropertyType type, const char* limits =
+                       NULL);
+public:
+       bool isValid(const QString& value, QString* error = NULL);
+public:
+       const char* m_name;
+       QString m_title;
+       QString m_description;
+       QString m_value;
+       QString m_defaultValue;
+       RePropertyType m_type;
+       const char* m_limits;
+};
+
+class ReSettings {
+public:
+       static QString TRUE;
+       static QString FALSE;
+public:
+       ReSettings(const QString& path, const QString prefix, ReLogger* logger);
+public:
+       void addHistoryEntry(const char* key, const QString& value, char separator,
+               int maxEntries);
+       bool boolValue(const char* name);
+       void changeValue(const char* name, const QString& value);
+       QStringList& historyAsList(const char* key, QStringList& list,
+               const char* form = NULL);
+       void insertProperty(ReProperty* property);
+       int intValue(const char* name);
+       QString path() const;
+       void readSettings();
+       QString stringValue(const char* name);
+       void writeSettings();
+protected:
+       QString m_path;
+       QString m_fileHistory;
+       QString m_fileSettings;
+       QMap<const char*, ReProperty*> m_settings;
+       QMap<QByteArray, QList<ReProperty*>*> m_chapters;
+       ReLogger* m_logger;
+};
+
+#endif // STORAGE_HPP
index ba1fc14aaac45171c89fd631507705e35d734dc7..f5dc636ab1fa6d4776093a8216b62ac9e9e42012 100644 (file)
@@ -15,6 +15,7 @@
 #include "gui/ReStateStorage.hpp"
 #include "gui/ReGuiValidator.hpp"
 #include "gui/ReEdit.hpp"
+#include "gui/ReSettings.hpp"
 /**
  * Tests whether a point is inside the rectangle (including border).
  * @param rect  rectangle to test
index ee1cb35fcf5b616f0a98e3b3b12f2142ea15a0b3..af5562c0f32c0b725e8a093e4b1893b134c584d9 100644 (file)
 #define RPLMODULES_HPP
 
 enum {
-       LOC_LOGGER = 101, LOC_CONFIG, LOC_CONTAINER, LOC_EXCEPTION, LOC_TEST, // 105
+       LOC_LOGGER = 101,
+       LOC_CONFIG,
+       LOC_CONTAINER,
+       LOC_EXCEPTION,
+       LOC_TEST, // 105
        LOC_TCPSERVER,
        LOC_TCPCLIENT,
        LOC_TCPPEER,
@@ -24,13 +28,14 @@ enum {
        LOC_VM,
        LOC_MFPARSER, // 115
        LOC_TRAVERSER,
-       LOC_STORAGE
+       LOC_SETTINGS,
+       LOC_FILE,
 };
 #define LOC_FIRST_OF(moduleNo) (moduleNo*100+1)
 class RplModules {
 public:
-   static int fileToNumber(const char* file);
-   static const char* numberToFile(int location);
+       static int fileToNumber(const char* file);
+       static const char* numberToFile(int location);
 };
 
 #endif // RPLMODULES_HPP