*
* @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;
}
* @param logger the logger
*/
Project::Project(const QString& path, ReLogger* logger) :
- Storage(path, "proj", logger) {
+ ReSettings(path, ".reditor.proj", logger) {
}
#define REDITOR_HPP
#include "base/rebase.hpp"
#include "gui/regui.hpp"
-#include "storage.hpp"
#include "workspace.hpp"
#include "project.hpp"
#include "mainwindow.hpp"
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
+++ /dev/null
-/*
- * 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);
-}
-
+++ /dev/null
-/*
- * 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
* @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]"));
}
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;
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;
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:
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:
#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);
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.
*
if (text.isEmpty())
count = 1;
else {
- count = ReQStringUtil::countOf(text, '\n');
+ count = ReQStringUtils::countOf(text, '\n');
if (text.at(text.length() - 1) != '\n')
count++;
}
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;
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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
+++ /dev/null
-/*
- * 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;
-}
-
+++ /dev/null
-/*
- * 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
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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
+++ /dev/null
-/*
- * 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;
-}
-
+++ /dev/null
-/*
- * 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
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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
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;
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?",
#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"
int argc = 1;
QApplication a(argc, argv);
void testReStateStorage();
- testReStateStorage();
void testReEdit();
+ void testReSettings();
+ testReSettings();
+ testReStateStorage();
testReEdit();
}
void testReStringUtil();
void testReWriter();
void testReFile();
+ void testReFileUtils();
testReFile();
+ testReFileUtils();
if (s_allTest) {
testReQStringUtil();
testReByteStorage();
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"]);
}
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));
class TestReFile: public ReTest {
public:
TestReFile() :
- ReTest("ReFile") {
+ ReTest("ReFile") {
doIt();
}
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());
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));
}
}
}
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) {
checkEqu("2", lines.lineAt(1));
checkEqu("", lines.lineAt(2));
}
-
virtual void run() {
testReLinesInsert();
testReLinesSplitLine();
testWritableFile();
testPerformance();
testBasic();
- testTempDir();
- testTempFile();
- testWriteRead();
}
};
void testReFile() {
--- /dev/null
+/*
+ * 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;
+}
+
+++ /dev/null
-/*
- * 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;
-}
-
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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;
+}
+
+++ /dev/null
-/*
- * 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;
-}
-
--- /dev/null
+/*
+ * 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;
+}
+
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);
}
INCLUDEPATH = ..
SOURCES += main.cpp \
- cuReQStringUtil.cpp \
- cuReStringUtil.cpp \
+ cuReQStringUtils.cpp \
+ cuReStringUtils.cpp \
+ cuReFile.cpp \
+ cuReFileUtils.cpp \
cuReByteStorage.cpp \
cuReException.cpp \
../base/ReByteStorage.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 += \
--- /dev/null
+/*
+ * 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();
+ }
+}
+
+
--- /dev/null
+/*
+ * 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
#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
#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,
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