From: hama Date: Sun, 23 Aug 2015 20:57:37 +0000 (+0200) Subject: ReFileUtils, ReSettings X-Git-Url: https://gitweb.hamatoma.de/?a=commitdiff_plain;h=046c80278c4468fd28c57794e81fb56390d3e16b;p=reqt ReFileUtils, ReSettings * ReFileUtils extracted from ReFile * Storage moved to gui/ReSettings --- diff --git a/appl/reditor/mainwindow.cpp b/appl/reditor/mainwindow.cpp index 9a84764..341dd7f 100644 --- a/appl/reditor/mainwindow.cpp +++ b/appl/reditor/mainwindow.cpp @@ -114,11 +114,11 @@ ReLogger* MainWindow::logger() const { * * @return the current workspace */ -Storage* MainWindow::project() const { +ReSettings* MainWindow::project() const { return m_project; } -Storage* MainWindow::workspace() const { +ReSettings* MainWindow::workspace() const { return m_workspace; } diff --git a/appl/reditor/project.cpp b/appl/reditor/project.cpp index 314cb1f..52629d8 100644 --- a/appl/reditor/project.cpp +++ b/appl/reditor/project.cpp @@ -18,6 +18,6 @@ * @param logger the logger */ Project::Project(const QString& path, ReLogger* logger) : - Storage(path, "proj", logger) { + ReSettings(path, ".reditor.proj", logger) { } diff --git a/appl/reditor/reditor.hpp b/appl/reditor/reditor.hpp index 4d083d1..2088b69 100644 --- a/appl/reditor/reditor.hpp +++ b/appl/reditor/reditor.hpp @@ -13,7 +13,6 @@ #define REDITOR_HPP #include "base/rebase.hpp" #include "gui/regui.hpp" -#include "storage.hpp" #include "workspace.hpp" #include "project.hpp" #include "mainwindow.hpp" diff --git a/appl/reditor/reditor.pro b/appl/reditor/reditor.pro index 22e1ff5..ede1294 100644 --- a/appl/reditor/reditor.pro +++ b/appl/reditor/reditor.pro @@ -13,32 +13,34 @@ TEMPLATE = app INCLUDEPATH += ../.. -SOURCES += main.cpp\ +SOURCES += \ ../../gui/ReEdit.cpp \ ../../gui/ReStateStorage.cpp \ + ../../gui/ReStorage.cpp \ ../../base/ReFile.cpp \ mainwindow.cpp \ ../../base/ReLogger.cpp \ - ../../base/ReQStringUtil.cpp \ + ../../base/ReQStringUtils.cpp \ + ../../base/ReFileUtils.cpp \ ../../base/ReException.cpp \ projectselection.cpp \ - workspace.cpp \ - project.cpp \ - storage.cpp + workspace.cpp \ + project.cpp \ + main.cpp HEADERS += mainwindow.hpp \ ../../base/rebase.hpp \ ../../gui/regui.hpp \ ../../gui/ReEdit.hpp \ - ../../base/ReStringUtil.hpp \ - ../../base/ReQStringUtil.hpp \ + ../../base/ReStringUtils.hpp \ + ../../base/ReQStringUtils.hpp \ ../../base/ReException.hpp \ projectselection.hpp \ - workspace.hpp \ - project.hpp \ - reditor.hpp \ - storage.hpp + workspace.hpp \ + project.hpp \ + reditor.hpp \ + storage.hpp FORMS += mainwindow.ui \ projectselection.ui diff --git a/appl/reditor/storage.cpp b/appl/reditor/storage.cpp deleted file mode 100644 index f13c302..0000000 --- a/appl/reditor/storage.cpp +++ /dev/null @@ -1,160 +0,0 @@ -/* - * storage.cpp - * - * License: Public Domain - * You can use and modify this file without any restriction. - * Do what you want. - * No warranties and disclaimer of any damages. - * You also can use this license: http://www.wtfpl.net - * The latest sources: https://github.com/republib - */ - -#include "reditor.hpp" - -enum { - LOC_BOOL_VALUE_1 = LOC_FIRST_OF(LOC_STORAGE), // 11701 - LOC_BOOL_VALUE_2, // 11702 - LOC_INT_VALUE_1, // 11703 - LOC_INT_VALUE_2, // 11704 - LOC_STRING_VALUE_1, // 11705 - LOC_STRING_VALUE_2, // 11706 -}; - -/** - * Constructor. - * - * @param path the parent directory for the storage files - * @param prefix type of the storage: "proj" or "ws" (workspace) - */ -Storage::Storage(const QString& path, const QString prefix, ReLogger* logger) : - m_path(path), - m_fileHistory(path + OS_SEPARATOR + ".reditor." + prefix + ".history"), - m_fileSettings( - path + OS_SEPARATOR + ".reditor." + prefix + ".settings"), - m_settings(), - m_chapters(), - m_logger(logger) { - -} - -/* - * Adds an entry to a history item at the first position. - * - * The entry will removed from the other positions. - * - * @param key the key in the map - * @param value the value to add - * @param separator separates the entries in the history item - * @param maxEntries the maximal count of entries in the history item.
- * 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 list (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* list = m_chapters.value(chapter, NULL); - if (list == NULL) { - m_chapters.insert(chapter, list = new QList()); - } - list->append(property); -} - diff --git a/appl/reditor/storage.hpp b/appl/reditor/storage.hpp deleted file mode 100644 index 84f7c7b..0000000 --- a/appl/reditor/storage.hpp +++ /dev/null @@ -1,69 +0,0 @@ -/* - * storage.hpp - * - * License: Public Domain - * You can use and modify this file without any restriction. - * Do what you want. - * No warranties and disclaimer of any damages. - * You also can use this license: http://www.wtfpl.net - * The latest sources: https://github.com/republib - */ - -#ifndef STORAGE_HPP -#define STORAGE_HPP - -enum PropertyType { - PT_UNDEF, - PT_INT, - PT_STRING, - PT_BOOL -}; - -class Property { -public: - Property(const char* name, const QString& title, const QString& description, - const QString& defaultValue, PropertyType type, const char* limits = - NULL) : - m_name(name), - m_title(title), - m_description(description), - m_value(defaultValue), - m_defaultValue(defaultValue), - m_type(type), - m_limits(limits) { - } - -public: - const char* m_name; - QString m_title; - QString m_description; - QString m_value; - QString m_defaultValue; - PropertyType m_type; - const char* m_limits; -}; - -class Storage { -public: - Storage(const QString& path, const QString prefix, ReLogger* logger); -public: - void addHistoryEntry(const char* key, const QString& value, char separator, - int maxEntries); - bool boolValue(const char* name); - QStringList& historyAsList(const char* key, QStringList& list, - const char* form = NULL); - int intValue(const char* name); - QString path() const; - QString stringValue(const char* name); -protected: - void insertProperty(Property* property); -protected: - QString m_path; - QString m_fileHistory; - QString m_fileSettings; - QMap m_settings; - QMap*> m_chapters; - ReLogger* m_logger; -}; - -#endif // STORAGE_HPP diff --git a/appl/reditor/workspace.cpp b/appl/reditor/workspace.cpp index 64d54a3..5b5c64e 100644 --- a/appl/reditor/workspace.cpp +++ b/appl/reditor/workspace.cpp @@ -18,19 +18,19 @@ * @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]")); } diff --git a/base/ReContainer.cpp b/base/ReContainer.cpp index 84ec460..81a44c6 100644 --- a/base/ReContainer.cpp +++ b/base/ReContainer.cpp @@ -396,7 +396,7 @@ QByteArray ReContainer::dump(const char* title, int maxBags, QByteArray rc; rc.reserve(64000); rc.append("=== ").append(title).append('\n'); - rc.append("Bags: ").append(ReStringUtil::toNumber(m_countBags)); + rc.append("Bags: ").append(ReStringUtils::toNumber(m_countBags)); rc.append(" Types: ").append(m_typeList).append('\n'); // save the current state: int safeIxBag = m_ixBag; @@ -408,7 +408,7 @@ QByteArray ReContainer::dump(const char* title, int maxBags, if (maxBags > m_countBags) maxBags = m_countBags; for (int ixBag = 0; ixBag < maxBags; ixBag++) { - rc.append("--- bag ").append(ReStringUtil::toNumber(ixBag)).append( + rc.append("--- bag ").append(ReStringUtils::toNumber(ixBag)).append( ":\n"); nextBag(); QByteArray item; @@ -421,9 +421,9 @@ QByteArray ReContainer::dump(const char* title, int maxBags, break; case TAG_INT: iValue = nextInt(); - rc.append(" i: ").append(ReStringUtil::toNumber(iValue)).append( + rc.append(" i: ").append(ReStringUtils::toNumber(iValue)).append( " / "); - rc.append(ReStringUtil::toNumber(iValue, "%x")).append( + rc.append(ReStringUtils::toNumber(iValue, "%x")).append( separatorItems); break; case TAG_STRING: @@ -437,11 +437,11 @@ QByteArray ReContainer::dump(const char* title, int maxBags, case TAG_DATA4G: nextData(item, false); rc.append(' ').append((char) currentType).append(": ["); - rc.append(ReStringUtil::toNumber(item.length())).append("] "); + rc.append(ReStringUtils::toNumber(item.length())).append("] "); maxLength = item.length() < maxBlobLength ? item.length() : maxBlobLength; - rc.append(ReStringUtil::hexDump(item.data(), maxLength, 16)) + rc.append(ReStringUtils::hexDump(item.data(), maxLength, 16)) .append(separatorItems); break; default: diff --git a/base/ReFile.cpp b/base/ReFile.cpp index 31abc84..dc04fed 100644 --- a/base/ReFile.cpp +++ b/base/ReFile.cpp @@ -11,6 +11,12 @@ #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(heap); @@ -83,6 +89,60 @@ void ReLines::clear() { QStringList::clear(); } +/** + * Delete a directory tree. + * + * @param path the directory to delete + * @param withBase true: the directory itself will be deleted
+ * false: only all files/dirs inside will be + * deleted + * @param logger NULL or error logger + * @return true: all files deleted
+ * false: at least one deletion failed + */ +bool ReFile::deleteTree(const QString& path, bool withBase, ReLogger* logger) { + bool rc = true; + QDir dir(path); + + if (dir.exists(path)) { + QFileInfo info; + QStringList names = dir.entryList( + QDir::NoDotAndDotDot | QDir::System | QDir::Hidden | QDir::AllDirs + | QDir::Files); + QStringList::const_iterator it; + for (it = names.constBegin(); it != names.constEnd(); ++it) { + QString full(path); + full.append(OS_SEPARATOR_STR).append(*it); + QFileInfo info(full); + if (info.isDir()) { + if (!deleteTree(full, true, logger)) + rc = false; + else if (rmdir(full.toUtf8()) != 0) { + rc = false; + if (logger != NULL) + logger->logv(LOG_ERROR, LOC_DELETE_TREE_1, + "cannot delete directory (%d): %s", errno, + full.toUtf8().constData()); + } + } else { + if (!QFile::remove(full)) { + rc = false; + if (logger != NULL) + logger->logv(LOG_ERROR, LOC_DELETE_TREE_2, + "cannot delete file (%d): %s", errno, + full.toUtf8().constData()); + } + } + } + } + if (withBase && (rmdir(path.toUtf8())) != 0) { + rc = false; + logger->logv(LOG_ERROR, LOC_DELETE_TREE_3, + "cannot delete directory (%d): %s", errno, path.toUtf8()); + } + return rc; +} + /** * Inserts one or more lines into the lines. * @@ -97,7 +157,7 @@ void ReLines::insertLines(int lineNo, const QString& text, bool withUndo) { if (text.isEmpty()) count = 1; else { - count = ReQStringUtil::countOf(text, '\n'); + count = ReQStringUtils::countOf(text, '\n'); if (text.at(text.length() - 1) != '\n') count++; } diff --git a/base/ReFile.hpp b/base/ReFile.hpp index 5123c68..953b74f 100644 --- a/base/ReFile.hpp +++ b/base/ReFile.hpp @@ -155,7 +155,8 @@ public: static QByteArray& readFromFile(const char* filename, QByteArray& buffer); static void writeToFile(const char* filename, const char* content, size_t contentLength = (size_t) - 1, const char* mode = "w"); - + static bool deleteTree(const QString& path, bool withBase, + ReLogger* logger); private: QByteArray m_endOfLine; QString m_filename; diff --git a/base/ReFileUtils.cpp b/base/ReFileUtils.cpp new file mode 100644 index 0000000..7228f05 --- /dev/null +++ b/base/ReFileUtils.cpp @@ -0,0 +1,196 @@ +/* + * ReFileUtils.cpp + * + * License: Public Domain + * You can use and modify this file without any restriction. + * Do what you want. + * No warranties and disclaimer of any damages. + * You also can use this license: http://www.wtfpl.net + * The latest sources: https://github.com/republib + */ + +#include "base/rebase.hpp" + +enum { + LOC_DELETE_TREE_1 = LOC_FIRST_OF(LOC_FILE), // 11801 + LOC_DELETE_TREE_2, // 11802 + LOC_DELETE_TREE_3, // 11803 +}; + + +/** + * Constructor. + */ +ReTreeStatistic::ReTreeStatistic() : + m_files(0), + m_directories(0), + m_fileSizes(0L){ +} + +/** + * Delete a directory tree. + * + * @param path the directory to delete + * @param withBase true: the directory itself will be deleted
+ * false: only all files/dirs inside will be + * deleted + * @param logger NULL or error logger + * @return true: all files deleted
+ * false: 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 buffer (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: strlen(content)
+ * otherwise: the length of content + * @param mode file write mode: "w" (write) or "a" (append) + */ +void ReFileUtils::writeToFile(const char* filename, const char* content, + size_t contentLength, const char* mode) { + FILE* fp = fopen(filename, mode); + if (fp != NULL) { + if (contentLength == (size_t) - 1) + contentLength = strlen(content); + fwrite(content, 1, contentLength, fp); + fclose(fp); + } +} diff --git a/base/ReFileUtils.hpp b/base/ReFileUtils.hpp new file mode 100644 index 0000000..1fe2154 --- /dev/null +++ b/base/ReFileUtils.hpp @@ -0,0 +1,41 @@ +/* + * ReFileUtils.hpp + * + * License: Public Domain + * You can use and modify this file without any restriction. + * Do what you want. + * No warranties and disclaimer of any damages. + * You also can use this license: http://www.wtfpl.net + * The latest sources: https://github.com/republib + */ + +#ifndef REFILEUTILS_HPP +#define REFILEUTILS_HPP + +/** + * Statistic of a directory tree. + */ +class ReTreeStatistic { + ReTreeStatistic(); +public: + int m_files; + int m_directories; + int64_t m_fileSizes; +}; + +/** + * Usefull static methods around files/directories missing in QT. + */ +class ReFileUtils { +public: + static QByteArray tempDir(const char* node, const char* parent = NULL, + bool withSeparator = true); + static QByteArray tempFile(const char* node, const char* parent = NULL, + bool deleteIfExists = true); + static QByteArray& readFromFile(const char* filename, QByteArray& buffer); + static void writeToFile(const char* filename, const char* content, + size_t contentLength = (size_t) - 1, const char* mode = "w"); + static bool deleteTree(const QString& path, bool withBase, ReLogger* logger); +}; + +#endif // REFILEUTILS_HPP diff --git a/base/ReQStringUtil.cpp b/base/ReQStringUtil.cpp deleted file mode 100644 index 0b5b7c9..0000000 --- a/base/ReQStringUtil.cpp +++ /dev/null @@ -1,811 +0,0 @@ -/* - * ReQStringUtil.cpp - * - * License: Public Domain - * You can use and modify this file without any restriction. - * Do what you want. - * No warranties and disclaimer of any damages. - * You also can use this license: http://www.wtfpl.net - * The latest sources: https://github.com/republib - */ - -/** @file - * @brief Missed operation for ReStrings. - */ -/** @file rplcore/rplqstring.hpp - * - * @brief Definitions for missed operation for ReStrings. - */ -#include "base/rebase.hpp" -#include -#include - -/** - * Removes end of line characters if any. - * - * @param text text to inspect - * @return text 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
- * otherwise: the extension of filename - */ -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 text to inspect - * @param value OUT: the value of the found date. Not changed if result is 0.
- * May be NULL - * @return 0: no date found
- * 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 text to inspect - * @param allowDateOnly false: if the date is not followed by - * a time the result will be 0 - * @param allowTimeOnly false: 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.
- * May be NULL - * @return 0: no date found
- * 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 text to inspect - * @param value OUT: the value of the found time. Not changed if result is 0.
- * May be NULL - * @return 0: no date found
- * 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 filename - */ -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 base) - * @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 true: success
- * false: unknown name found (not replaced) - */ -bool ReQStringUtil::replacePlaceholders(QString& text, - const QMap& placeholders, QString* error) { - int start = 0; - bool rc = true; - QString name; - QMap::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
- * 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 qstring.toUtf8().constData() 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 buffer - */ -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
- * 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 defaultValue: the result was not valid
- * 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 defaultValue: the result was not valid
- * 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 defaultValue: the result was not valid
- * 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
- * otherwise: the error message - */ -const QString& ReUnitParser::errorMessage() const { - return m_message; -} - -/** - * Returns whether the given expression is valid. - * - * @return true: 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
- * only units defined in m_unitList are allowed
- * 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 QDateTime 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" | [