From fef17ce37f6ec541c932d594bc3fa553800b1cfc Mon Sep 17 00:00:00 2001 From: hama Date: Wed, 24 Dec 2014 17:43:56 +0100 Subject: [PATCH] initial state --- .cproject | 676 ++++++++++++++++ .gitignore | 3 + .project | 83 ++ Doxy.sh | 3 + base/ReByteBuffer.cpp | 363 +++++++++ base/ReByteBuffer.hpp | 158 ++++ base/ReCString.cpp | 44 + base/ReCString.hpp | 13 + base/ReConfigFile.cpp | 166 ++++ base/ReConfigFile.hpp | 39 + base/ReDirectory.cpp | 258 ++++++ base/ReDirectory.hpp | 41 + base/ReException.cpp | 195 +++++ base/ReException.hpp | 56 ++ base/ReHashList.cpp | 244 ++++++ base/ReHashList.hpp | 42 + base/ReI18N.cpp | 210 +++++ base/ReI18N.hpp | 53 ++ base/ReLogger.cpp | 425 ++++++++++ base/ReLogger.hpp | 174 ++++ base/ReProgramArgs.cpp | 697 ++++++++++++++++ base/ReProgramArgs.hpp | 94 +++ base/ReSeqList.cpp | 248 ++++++ base/ReSeqList.hpp | 78 ++ base/ReStringList.cpp | 597 ++++++++++++++ base/ReStringList.hpp | 61 ++ base/ReStringUtils.cpp | 413 ++++++++++ base/ReStringUtils.hpp | 39 + base/ReTestUnit.cpp | 267 +++++++ base/ReTestUnit.hpp | 51 ++ base/ReVarArgs.cpp | 339 ++++++++ base/ReVarArgs.hpp | 68 ++ base/baselocations.hpp | 36 + base/rebase.hpp | 55 ++ base/remath.hpp | 17 + base/renet.hpp | 15 + base/restring.hpp | 16 + crepublib.doxyfile | 1630 ++++++++++++++++++++++++++++++++++++++ cunit/basetest.cpp | 42 + cunit/cuReByteBuffer.cpp | 298 +++++++ cunit/cuReCString.cpp | 39 + cunit/cuReTraverser.cpp | 31 + cunit/testall.cpp | 68 ++ math/ReObfuscator.cpp | 208 +++++ math/ReObfuscator.hpp | 39 + math/ReRandomizer.cpp | 234 ++++++ math/ReRandomizer.hpp | 57 ++ math/remath.hpp | 14 + net/ReUdpConnection.cpp | 275 +++++++ net/ReUdpConnection.hpp | 85 ++ net/renet.hpp | 13 + os/ReTraverser.cpp | 381 +++++++++ os/ReTraverser.hpp | 131 +++ os/reos.hpp | 24 + 54 files changed, 9906 insertions(+) create mode 100644 .cproject create mode 100644 .gitignore create mode 100644 .project create mode 100755 Doxy.sh create mode 100644 base/ReByteBuffer.cpp create mode 100644 base/ReByteBuffer.hpp create mode 100644 base/ReCString.cpp create mode 100644 base/ReCString.hpp create mode 100644 base/ReConfigFile.cpp create mode 100644 base/ReConfigFile.hpp create mode 100644 base/ReDirectory.cpp create mode 100644 base/ReDirectory.hpp create mode 100644 base/ReException.cpp create mode 100644 base/ReException.hpp create mode 100644 base/ReHashList.cpp create mode 100644 base/ReHashList.hpp create mode 100644 base/ReI18N.cpp create mode 100644 base/ReI18N.hpp create mode 100644 base/ReLogger.cpp create mode 100644 base/ReLogger.hpp create mode 100644 base/ReProgramArgs.cpp create mode 100644 base/ReProgramArgs.hpp create mode 100644 base/ReSeqList.cpp create mode 100644 base/ReSeqList.hpp create mode 100644 base/ReStringList.cpp create mode 100644 base/ReStringList.hpp create mode 100644 base/ReStringUtils.cpp create mode 100644 base/ReStringUtils.hpp create mode 100644 base/ReTestUnit.cpp create mode 100644 base/ReTestUnit.hpp create mode 100644 base/ReVarArgs.cpp create mode 100644 base/ReVarArgs.hpp create mode 100644 base/baselocations.hpp create mode 100644 base/rebase.hpp create mode 100644 base/remath.hpp create mode 100644 base/renet.hpp create mode 100644 base/restring.hpp create mode 100644 crepublib.doxyfile create mode 100644 cunit/basetest.cpp create mode 100644 cunit/cuReByteBuffer.cpp create mode 100644 cunit/cuReCString.cpp create mode 100644 cunit/cuReTraverser.cpp create mode 100644 cunit/testall.cpp create mode 100644 math/ReObfuscator.cpp create mode 100644 math/ReObfuscator.hpp create mode 100644 math/ReRandomizer.cpp create mode 100644 math/ReRandomizer.hpp create mode 100644 math/remath.hpp create mode 100644 net/ReUdpConnection.cpp create mode 100644 net/ReUdpConnection.hpp create mode 100644 net/renet.hpp create mode 100644 os/ReTraverser.cpp create mode 100644 os/ReTraverser.hpp create mode 100644 os/reos.hpp diff --git a/.cproject b/.cproject new file mode 100644 index 0000000..5783069 --- /dev/null +++ b/.cproject @@ -0,0 +1,676 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + make + + doxygen + true + true + true + + + make + all + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..23f8dcb --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.o +dirtool +alltest diff --git a/.project b/.project new file mode 100644 index 0000000..24b1d09 --- /dev/null +++ b/.project @@ -0,0 +1,83 @@ + + + crepublib + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + ?name? + + + + org.eclipse.cdt.make.core.append_environment + true + + + org.eclipse.cdt.make.core.autoBuildTarget + all + + + org.eclipse.cdt.make.core.buildArguments + + + + org.eclipse.cdt.make.core.buildCommand + make + + + org.eclipse.cdt.make.core.buildLocation + ${workspace_loc:/crepublib/Debug} + + + org.eclipse.cdt.make.core.cleanBuildTarget + clean + + + org.eclipse.cdt.make.core.contents + org.eclipse.cdt.make.core.activeConfigSettings + + + org.eclipse.cdt.make.core.enableAutoBuild + false + + + org.eclipse.cdt.make.core.enableCleanBuild + true + + + org.eclipse.cdt.make.core.enableFullBuild + true + + + org.eclipse.cdt.make.core.fullBuildTarget + all + + + org.eclipse.cdt.make.core.stopOnError + true + + + org.eclipse.cdt.make.core.useDefaultBuildCmd + true + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.core.ccnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/Doxy.sh b/Doxy.sh new file mode 100755 index 0000000..339cc85 --- /dev/null +++ b/Doxy.sh @@ -0,0 +1,3 @@ +#! /bin/bash +rm -Rf doxygen/* +doxygen crepublib.doxyfile diff --git a/base/ReByteBuffer.cpp b/base/ReByteBuffer.cpp new file mode 100644 index 0000000..ee44320 --- /dev/null +++ b/base/ReByteBuffer.cpp @@ -0,0 +1,363 @@ +/* + * ReByteBuffer.cpp + * + * Created on: 06.05.2010 + * Author: wk + */ + +#include "rebase.hpp" + +/** @brief Constructor. + * + * @param delta If a new storage must be allocated the size + * is incremented by at least this count of bytes. + */ +ReByteBuffer::ReByteBuffer(size_t delta) + : + m_delta(delta), + // m_primaryBuffer + m_buffer(m_primaryBuffer), + m_length(0), + m_size(sizeof m_primaryBuffer - 1) +{ + m_buffer[0] = '\0'; +} +/** @brief Copy constructor. + * + * @param source The instance to copy (C string). + */ +ReByteBuffer::ReByteBuffer(const char* source) + : + m_delta(256), + // m_primaryBuffer + m_buffer(m_primaryBuffer), + m_length(0), + m_size(sizeof m_primaryBuffer) +{ + m_buffer[0] = '\0'; + append(source); +} +/** @brief Destructor. + * + */ +ReByteBuffer::~ReByteBuffer(){ + if (m_buffer != m_primaryBuffer) + delete m_buffer; + m_buffer = NULL; + m_size = 0; + m_length = 0; +} +/** @brief Copy constructor. + * + * @param source The instance to copy. + */ +ReByteBuffer::ReByteBuffer(const ReByteBuffer& source) + : + m_delta(source.getDelta()), + // m_primaryBuffer + m_buffer(m_primaryBuffer), + m_length(0), + m_size(sizeof m_primaryBuffer) +{ + append(source.getBuffer(), source.getLength()); +} + +/** @brief The assignment operator. + * + * @param source The instance to copy. + * + * @return The instance itself. + */ +ReByteBuffer& ReByteBuffer::operator =(const ReByteBuffer& source){ + m_delta = source.getDelta(); + set(source.getBuffer(), source.getLength()); + return *this; +} + +/** @brief Appends a byte sequence at the end of the buffer. + * + * If the space is not enough it will be allocated. + * + *
Example:
+ * ReByteBuffer buf;
+ * assert(4 == buf.append("abc", 3).append("x").getLength());
+ * 
+ * + * @param source The sequence to append. + * @param length The length of the sequence. + * + * @return *this (for chaining). + */ +ReByteBuffer& ReByteBuffer::append(const Byte* source, size_t length){ + if (length == (size_t) -1) + length = strlen(source); + ensureSize(m_length + length); + memcpy(m_buffer + m_length, source, length); + m_length += length; + m_buffer[m_length] = '\0'; + return *this; +} +/** @brief Appends an integer as string at the end of the buffer. + * + * If the space is not enough it will be allocated. + * + *
Example:
+ * ReByteBuffer buf;
+ * buf.appendInt(33, 4);
+ * assert(strcmp("33  ", buf.getBuffer()) == 0);
+ * 
+ * + * @param number The number to append. + * @param format The format of the number used by sprintf(). + * + * @return *this (for chaining). + */ +ReByteBuffer& ReByteBuffer::appendInt(int number, const char* format){ + char buffer [256]; + + snprintf(buffer, sizeof buffer, format, number); + size_t length = strlen(buffer); + + ensureSize(m_length + length); + memcpy(m_buffer + m_length, buffer, length); + m_length += length; + m_buffer[m_length] = '\0'; + return *this; +} + +/** @brief Appends the content of another ReByteBuffer instance at the end of the buffer. + * + * If the space is not enough it will be allocated. + * + *
Example:
+ * ReByteBuffer name; name.append("mydoc");
+ * ReByteBuffer ext; ext.append(".txt");
+ * name.append(ext);
+ * assert("mydoc.txt", name.getBuffer());
+ * 
+ * + * @return *this (for chaining). + */ +ReByteBuffer& ReByteBuffer::append(ReByteBuffer& source){ + return append(source.getBuffer(), source.getLength()); +} + +/** Converts a subsequence into an integer. + * + * The number may be a decimal or a hexadecimal number. Hex numbers start with 0x. + *
Example:
+ * ReByteBuffer buf;
+ * buf.set("abc123", 6);
+ * assert(12 == buf.atoi(3, 3 + 2));
+ * 
+ * + * @param start The first index to convert. + * @param end -1: all found digits will be converted. + * Otherwise: The maximal number of digits to convert. + */ +int ReByteBuffer::atoi(size_t start, int end) const{ + int rc = 0; + if (start < m_length){ + if (end < 0) + end = m_length; + char cc; + if (m_buffer[start] == '0' && tolower(m_buffer[start + 1]) == 'x'){ + // Hexadecimal number: + start += 2; + while (start < (size_t) end && (cc = m_buffer[start++]) >= '0' && isxdigit(cc)) + rc = rc * 16 + (cc <= '9' ? cc - '0' : toupper(cc) - 'A' + 10); + } else { + // Decimal number: + while (start < (size_t) end && (cc = m_buffer[start++]) >= '0' && cc <= '9') + rc = rc * 10 + cc - '0'; + } + } + return rc; +} +/** @brief Ensures that there is enough space. + * + *

If not it will be allocated and the old value will be copied. + *

+ *

A new allocation will be use at least m_delta bytes. + *

The allocated space is incremented by one because of the ending '\\0'. + *

+ * + * @param size At the end the size will be at least this value. + */ +void ReByteBuffer::ensureSize(size_t size){ + if (m_size < size){ + if (size - m_size < m_delta) + size = m_size + m_delta; + // Think for the ending '\0': + Byte* buffer = new Byte[size + 1]; + assert(buffer != NULL); + memcpy(buffer, m_buffer, m_length); + if (m_buffer != m_primaryBuffer) + delete m_buffer; + m_buffer = buffer; + m_size = size; + } +} +/** @brief Search for a byte sequence in the internal buffer. + * + *
Example:
+ * ReByteBuffer buf;
+ * buf.set("12abc123", 8);
+ * assert(5 == buf.indexOf("12", -1, 3));
+ * 
+ * + * @param toFind The sequence to find. + * @param toFindLength Length of toFind. + * If -1 the strlen(toFind) will be taken. + * @param start The index of the internal buffer where the search starts. + * + * @return -1: The sequence could not be found. + * Otherwise: The index of toFind in the internal buffer. + */ +int ReByteBuffer::indexOf(const Byte* toFind, size_t toFindLength, int start) const{ + if (toFindLength == (size_t) -1) + toFindLength = strlen(toFind); + int rc = -1; + while(rc < 0 && start <= int(m_length - toFindLength)){ + if (memcmp(toFind, m_buffer + start, toFindLength) == 0) + rc = start; + else + start++; + } + return rc; +} +/** @brief Replaces all occurrences of toFind with replacement. + * + * A single replacement can be done with splice(). + * + *
Example:
+ * ReByteBuffer buf;
+ * buf.set("12abc123", 8);
+ * buf.replaceAll("12", -1, "XYZ", -1);
+ * assert(strcmp("XYZabcXYZ3", buf.str()) == 0);
+ * 
+ * + * @param toFind The substring which will be replaced. + * @param toFindLength The length of toFind. -1: strlen() will be used. + * @param replacement The replacement. + * @param replacementLength The length of replacement. -1: strlen() will be used. + * @param start The first index to inspect. + */ +ReByteBuffer& ReByteBuffer::replaceAll(const Byte* toFind, size_t toFindLength, + const Byte* replacement, size_t replacementLength, int start){ + if (toFindLength == size_t(-1)) + toFindLength = strlen(toFind); + if (replacementLength == size_t(-1)) + replacementLength = strlen(replacement); + + while( (start = indexOf(toFind, toFindLength, start)) != -1){ + splice(start, toFindLength, replacement, replacementLength); + start += replacementLength; + } + return *this; +} + +/** @brief Sets the length to a given value. + * + * The new length is greater than the current size the buffer will reallocated. + * + *
Example:
+ * ReByteBuffer buf;
+ * buf.set("123", 3);
+ * buf.setLength(5);
+ * assert(strcmp("123", buf.str()) == 0);
+ * assert(5 == buf.getLength());
+ * 
+ * + * @param length The new length. + * + * @return *this (for chaining). + */ +ReByteBuffer& ReByteBuffer::setLength(size_t length){ + ensureSize(length); + m_length = length; + m_buffer[length] = '\0'; + return *this; +} +/** @brief Sets the length to a given value and fills the new allocated part of the buffer with a given value. + * + * The new length is greater than the current size the buffer will reallocated. + * + *
Example:
+ * ReByteBuffer buf;
+ * buf.set("123", 3);
+ * buf.setLengthAndFill(5, 'X');
+ * assert(strcmp("123XX", buf.str()) == 0);
+ * 
+ * + * @param length The new length. + * @param filler If the new length is greater than the current length the space + * will be filled with this value. + * @return *this (for chaining). + */ +ReByteBuffer& ReByteBuffer::setLengthAndFill(size_t length, Byte filler){ + ensureSize(length); + if (length > m_length) + memset(m_buffer + m_length, filler, length - m_length); + m_length = length; + m_buffer[length] = '\0'; + return *this; +} +/** @brief Cuts a sequence from the internal buffer and inserts another. + * + * This function implies a pure insertion and pure deletion of a subsequence. + * + *
Example:
+ * ReByteBuffer buf;
+ * buf.set("12345", 5);
+ * buf.splice(2, 2, "XYZ", 3);
+ * assert(strcmp("12XYZ5", buf.str()) == 0);
+ * 
+ * + * @param ix The index where the cut/insertion takes place. + * @param replacedLength The number of deleted bytes. If 0 a pure insertion will be done. + * @param source The sequence to insert. May be NULL (for pure deletion). + * @param length The length of the inserted sequence. + * If 0: a pure deletion will be done. + * If -1: strlen(source) will be taken. + * + * @return true: Success. false: ix out of range. + */ +bool ReByteBuffer::splice(size_t ix, size_t replacedLength, + const ReByteBuffer::Byte* source, size_t length){ + bool rc; + if (ix < 0 || ix > m_length) + rc = false; + else{ + rc = true; + + if (source == NULL) + length = 0; + if (length == (size_t) -1) + length = strlen(source); + if (ix + replacedLength > m_length) + replacedLength = m_length - ix; + + ensureSize(m_length + length - replacedLength); + + Byte* start = m_buffer + ix; + Byte* tail = start + replacedLength; + size_t lengthTail = m_length - ix; + + if (length <= replacedLength){ + // The sequence will not be longer: + if (length > 0) + memcpy(start, source, length); + if (length < replacedLength){ + memmove(start + length, tail, lengthTail); + } + } else { + // The sequence will be longer: + memmove(start + length, tail, lengthTail); + memcpy(start, source, length); + } + m_length += length - replacedLength; + m_buffer[m_length] = '\0'; + } + return rc; +} diff --git a/base/ReByteBuffer.hpp b/base/ReByteBuffer.hpp new file mode 100644 index 0000000..5fe2949 --- /dev/null +++ b/base/ReByteBuffer.hpp @@ -0,0 +1,158 @@ +/* + * @file ReByteBuffer.hpp + * + * Created on: 06.05.2010 + * Author: wk + */ + +#ifndef REBYTEBUFFER_H_ +#define REBYTEBUFFER_H_ + +/** @brief An efficient dynamic memory buffer. + * + * Implements a very efficient dynamic byte sequence. + *

+ * Note:
+ * It is guaranteed that the sequence is terminated by a '\\0' like C strings. + * This end marker is outside the visible buffer.
+ * Technically:
+ * ReByteBuffer buf; assert(buf.getBuffer()[buf.getLength()] == '\\0'); + *

+ *

This class can be used as a poor man's string class. + *

+ *

Very effizient: + *

+ *

+ */ +class ReByteBuffer { +public: + typedef char Byte; +public: + ReByteBuffer(size_t delta = 512); + ReByteBuffer(const char* source); + virtual ~ReByteBuffer(); + ReByteBuffer(const ReByteBuffer& source); + ReByteBuffer& operator =(const ReByteBuffer& source); +public: + ReByteBuffer& append(const Byte* source, size_t length = -1); + ReByteBuffer& append(ReByteBuffer& source); + ReByteBuffer& appendInt(int number, const char* format = "%d"); + /** @brief Returns the n-th byte of the internal buffer. + * @param index The index of the wanted byte. + * @return 0: Wrong index. Otherwise: The byte from the wanted position. + */ + inline Byte at(size_t index) const { + return index >= m_length ? 0 : m_buffer[index]; + } + int atoi(size_t start = 0, int end = -1) const; + void ensureSize(size_t size); + /** @brief Returns the buffer. + * @return The internal used buffer. + */ + inline Byte* getBuffer() const{ + return m_buffer; + } + /**@brief Returns the minimum allocation unit. + * @return The minimum of bytes to allocate. + */ + inline size_t getDelta() const{ + return m_delta; + } + /**@brief Returns the length of the buffer (the count of used bytes). + * @return The count of the allocated bytes in the internal buffer. + */ + inline size_t getLength() const{ + return m_length; + } + /**@brief Returns the current size of the internal buffer. + * @return The current size of the internal buffer. + */ + inline size_t getSize() const{ + return m_size; + } + /** @brief Finds the index of the first occurrence of a given byte. + * @param toFind This byte will be searched. + * @param start The first index for searching. + * @return -1: not found. Otherwise: The index of the first occurrence. + */ + inline int indexOf(Byte toFind, int start = 0) const { + while ((size_t) start < m_length) + if (m_buffer[start++] == toFind) + return start - 1; + return -1; + } + int indexOf(const Byte* toFind, size_t toFindLength, int start = 0) const; + /** @brief Inserts a byte sequence. + * @param ix The position in the internal buffer for the insertion. + * @param source The sequence to insert. + * @param length The length of the inserted sequence. + * + * @return true: Success. false: ix out of range. + */ + inline bool insert(size_t ix, const Byte* source, size_t length){ + return splice(ix, 0, source, length); + } + /** @brief Cuts a sequence from the internal buffer. + * + * @param ix The index where the cut/insertion takes place. + * @param deletedLength The number of deleted bytes. + * This value may be larger than the existing bytes. + * + * @return true: Success. false: ix out of range. + */ + inline bool remove(size_t ix, size_t deletedLength){ + return splice(ix, deletedLength, NULL, 0); + } + ReByteBuffer& replaceAll(const Byte* toFind, size_t toFindLength, + const Byte* replacement, size_t replacementLength, int start = 0); + /** @brief Finds the index of the last occurrence of a given byte (reverse index of). + * @param toFind This byte will be searched. + * @param start The first index for searching. If < 0: relative to the end. + * @return -1: not found. Otherwise: The index of the last occurrence. + */ + inline int rindexOf(Byte toFind, int start = -1) const { + if (start < 0) + start = m_length + start; + while (start >= 0) + if (m_buffer[start--] == toFind) + return start + 1; + return -1; + } + /** @brief Sets the content of the buffer by copying a byte sequence. + * @param source The byte sequence to copy. + * @param length The length of source. + * @return *this (for chaining). + */ + inline ReByteBuffer& set(const Byte* source, size_t length){ + return setLength(0).append(source, length); + } + ReByteBuffer& setLength(size_t length); + ReByteBuffer& setLengthAndFill(size_t length, Byte filler = 0); + + bool splice(size_t ix, size_t replacedLength, const Byte* source, size_t length); + /** @brief Returns the buffer content as C string. + * This is exactly the same result as from getBuffer(). + * @return The internal used buffer. + */ + inline const char* str() const{ + return (const char*) m_buffer; + } +protected: + // The minimum difference between old and new size after a new allocation. + size_t m_delta; + //@ If the needed space is small enough this buffer will be used. + char m_primaryBuffer[512]; + //@ The internal buffer. Points to m_primaryBuffer if the space is small enough. + char* m_buffer; + //@ The used bytes in m_buffer. + size_t m_length; + //@ The size of m_buffer. + size_t m_size; +}; + +#endif /* REBYTEBUFFER_H_ */ diff --git a/base/ReCString.cpp b/base/ReCString.cpp new file mode 100644 index 0000000..95b0a2b --- /dev/null +++ b/base/ReCString.cpp @@ -0,0 +1,44 @@ +/* + * ReString.cpp + * + * Created on: 05.05.2010 + * Author: wk + */ + +#include "../base/restring.hpp" + +/** @brief Replaces a substring by another. + * + * Cuts a substring from a string and inserts another substring. + * + * @param start In/Out: The string to change. + * @param bufferSize The size of start. + * @param lengthReplaced The length of the substring to cut. + * @param newString The string which will be inserted. + */ +void replaceSubstring(char* start, size_t bufferSize, size_t lengthReplaced, + const char* newString) +{ + size_t lengthNew = strlen(newString); + char* tail = start + lengthReplaced; + size_t lengthTail = strlen(tail) + 1; + + if (lengthNew <= lengthReplaced){ + // The string will not be longer: + memcpy(start, newString, lengthNew); + if (lengthNew < lengthReplaced){ + memmove(start + lengthNew, tail, lengthTail); + } + } else { + // The string will be longer: + if (lengthTail + lengthNew - lengthReplaced > bufferSize) + globalLogger()->sayF(LOG_ERROR|GRAN_USER|CAT_LIB, LC_CSTRING_REPLACE_1, + i18n("+++ The name size exceeds: $1 / $2\n")) + .arg(int(lengthTail + lengthNew - lengthReplaced)).arg((int) bufferSize) + .end(); + else { + memmove(start + lengthNew, tail, lengthTail); + memcpy(start, newString, lengthNew); + } + } +} diff --git a/base/ReCString.hpp b/base/ReCString.hpp new file mode 100644 index 0000000..7784b6d --- /dev/null +++ b/base/ReCString.hpp @@ -0,0 +1,13 @@ +/* + * ReString.h + * + * Created on: 05.05.2010 + * Author: wk + */ + +#ifndef RESTRING_H_ +#define RESTRING_H_ + +void replaceSubstring(char* start, size_t bufferSize, size_t lengthReplaced, + const char* newString); +#endif /* RESTRING_H_ */ diff --git a/base/ReConfigFile.cpp b/base/ReConfigFile.cpp new file mode 100644 index 0000000..25b1210 --- /dev/null +++ b/base/ReConfigFile.cpp @@ -0,0 +1,166 @@ +/* + * @file ReConfigFile.cpp + * + * @brief Configuration file handling for *.ini files. + */ + +#include "rebase.hpp" + +const char* ReConfigFile::m_trueValues = i18nm(";true;t;1;j;ja;yes;"); +const char* ReConfigFile::m_falseValues = i18nm(";false;f;0;n;nein;no;"); + +/** @brief Constructor. + * + * @param filename The name of the configuration file. + */ +ReConfigFile::ReConfigFile(const char* filename) + : + ReHashList(), + m_filename(), + m_valid(false) +{ + readFile(filename); +} + +/** @brief Constructor. + * + * @param filename The name of the configuration file. + */ +ReConfigFile::~ReConfigFile() { + m_valid = false; +} + +/** @brief Reads the file content into the internal structure. + * + * If the file cannot be read the m_valid flag is set to false. + * + * @param filename The name of the configuration file. + */ +void ReConfigFile::readFile(const char* filename){ + m_valid = false; + m_filename.set(filename, -1); + clear(); + FILE* fp = fopen(filename, "r"); + char line[4096]; + char* equal; + ReByteBuffer buffer; + + if (fp != NULL){ + int lineNo = 0; + while (fgets(line, sizeof line, fp) != NULL){ + lineNo++; + if (isalnum(line[0]) && (equal = strchr(line, '=')) != NULL){ + size_t ix = strlen(line) - 1; + // Remove all newlines: + while (ix > 0 && isspace(line[ix])){ + line[ix] = '\0'; + ix--; + } + *equal = '\0'; + if (get(line, -1, buffer)) + globalLogger()->sayF(LOG_ERROR | CAT_LIB, LC_CONFIGFILE_DOUBLE, + "$1-$2: Key is defined again: $3").arg(filename).arg(lineNo).arg(line).end(); + else + put(line, -1, equal + 1, -1); + } + } + fclose(fp); + } +} +/** @brief Reads an integer from the configuration. + * + * @param key The key of the integer value. + * @param defaultVal If the key could not found this value will be returned. + * + * @return The wanted integer or the default value. + */ +int ReConfigFile::getInteger(const char* key, int defaultVal){ + ReByteBuffer value; + getString(key, value, NULL); + int rc = defaultVal; + if (! isdigit(value.at(0))) + globalLogger()->sayF(LOG_WARNING | CAT_LIB, LC_CONFIGFILE_NO_INT, + "$1: Not an integer: $2 [$3]").arg(m_filename.str()).arg(value.str()).arg(key).end(); + else + rc = value.atoi(); + return rc; +} +/** @brief Reads a string from the configuration. + * + * @param key The key of the string value. + * @param buffer Out: The buffer for the result string. + * @param defaultVal If the key could not found this value will be returned. May be NULL. + */ +void ReConfigFile::getString(const char* key, ReByteBuffer& buffer, const char* defaultVal){ + if (! get(key, -1, buffer)){ + if (defaultVal == NULL) + buffer.setLength(0); + else + buffer.set(defaultVal, -1); + } +} +/** @brief Reads a boolean from the configuration. + * + * @param key The key of the boolean value. + * @param defaultVal If the key could not found this value will be returned. + * + * @return The wanted boolean or the default value. + */ +bool ReConfigFile::getBool(const char* key, bool defaultVal){ + ReByteBuffer value; + getString(key, value, NULL); + int rc = defaultVal; + if (ReStringUtils::isInList(value.str(), i18n(m_falseValues))) + rc = false; + else if (ReStringUtils::isInList(value.str(), i18n(m_trueValues))) + rc = true; + else + globalLogger()->sayF(LOG_WARNING | CAT_LIB, LC_CONFIGFILE_NO_BOOL, + "$1: Not an boolean: $2 [$3] Allowed: $4$5").arg(m_filename.str()) + .arg(value.str()).arg(key).arg(i18n(m_falseValues)) + .arg(i18n(m_trueValues)).end(); + return rc; +} + +#if defined RE_TESTUNIT +class TestReConfigFile : public ReTestUnit { + typedef ReHashList::Byte Byte; +public: + TestReConfigFile() : ReTestUnit("ReConfigFile", __FILE__){ + run(); + } +private: + void run(){ + testBasic(); + } + void testBasic(){ + createTestDir(); + ReByteBuffer fn; + fn.append(getTestDir(), -1).append("reconfigfile.cfg", -1); + createFile(fn.str(), "#x.int=a\nx.int=1\nx.bool=true\nx.str=abc\nx.bool2=0\nstring=abc\n"); + ReConfigFile config(fn.str()); + checkT(config.isValid()); + checkEqu(1, config.getInteger("x.int", 2)); + checkEqu(2, config.getInteger("x.int2", 2)); + + checkT(config.getBool("x.bool", true)); + checkT(config.getBool("x.bool", false)); + checkT(config.getBool("y.bool", true)); + checkF(config.getBool("y.bool", false)); + + checkF(config.getBool("x.bool2", true)); + checkF(config.getBool("x.bool2", false)); + + ReByteBuffer buffer; + config.getString("string", buffer, "x"); + checkEqu("abc", buffer.str()); + config.getString("string1", buffer, "x"); + checkEqu("x", buffer.str()); + } +}; +extern void testReConfigFile(void); + +void testReConfigFile(void){ + TestReConfigFile unit; +} +#endif /*RE_TESTUNIT*/ diff --git a/base/ReConfigFile.hpp b/base/ReConfigFile.hpp new file mode 100644 index 0000000..c8ac063 --- /dev/null +++ b/base/ReConfigFile.hpp @@ -0,0 +1,39 @@ +/* + * ReConfigFile.h + * + * Created on: 18.05.2010 + * Author: wk + */ + +#ifndef RECONFIGFILE_H_ +#define RECONFIGFILE_H_ + +class ReConfigFile : protected ReHashList { +public: + static const char* m_trueValues; + static const char* m_falseValues; +public: + ReConfigFile(const char* filename); + virtual ~ReConfigFile(); +private: + //@ Not implemented and forbidden! + ReConfigFile(const ReConfigFile& source); + //@ Not implemented and forbidden! + ReConfigFile& operator =(const ReConfigFile& source); +public: + void readFile(const char* filename); + int getInteger(const char* key, int defaultVal = 0); + void getString(const char* key, ReByteBuffer& buffer, const char* defaultVal = NULL); + bool getBool(const char* key, bool defaultVal = false); + /** @brief Returns whether the instance is in a valid state. + * @return true: The configuration is valid. false: Otherwise. + */ + inline bool isValid() const { + return m_valid; + } +protected: + ReByteBuffer m_filename; + bool m_valid; +}; + +#endif /* RECONFIGFILE_H_ */ diff --git a/base/ReDirectory.cpp b/base/ReDirectory.cpp new file mode 100644 index 0000000..73c1222 --- /dev/null +++ b/base/ReDirectory.cpp @@ -0,0 +1,258 @@ +/* + * ReFileUtils.cpp + * + * Created on: 16.05.2010 + * Author: wk + */ + +#include "rebase.hpp" + + +/** @brief Constructor. + */ +ReDirectory::ReDirectory() + : + m_valid(false), + m_dir(NULL), + m_path(), + m_entry(NULL), + m_pattern(), + m_isRegExpr(false), + m_regExprFlags(0) +{ +} +/** @brief Constructor. + * + * @param path The path of the directory. + */ +ReDirectory::ReDirectory(const char* path) + : + m_valid(false), + m_dir(NULL), + m_path(), + m_entry(NULL), + m_pattern(), + m_isRegExpr(false), + m_regExprFlags(0) +{ + setDir(path); +} +/** @brief Destructor. + */ +ReDirectory::~ReDirectory(){ + if (m_dir != NULL){ + closedir(m_dir); + m_dir = NULL; + m_valid = false; + } +} +/** @brief Sets the directory. + * + * @param path The name of the directory. + */ +void ReDirectory::setDir(const char* path){ + if (m_dir != NULL){ + closedir(m_dir); + m_dir = NULL; + } + m_entry = NULL; + m_path.set(path, -1); + m_dir = opendir(path); + m_valid = m_dir != NULL; + if (m_valid){ + if (m_path.at(m_path.getLength() - 1) != ReStringUtils::pathSeparator()) + m_path.append(ReStringUtils::pathSeparatorStr(), 1); + } +} +/** @brief Returns the name of the directory. + * + * @return The name of the directory. + */ +const char* ReDirectory::getDir(void) const { + return m_path.getBuffer(); +} + +/** @brief Tests whether the directory state is valid. + * + * @return true: The instance is OK. false: Some error has occurred. + */ +bool ReDirectory::isValid(){ + return m_valid; +} +/** @brief Sets the flags of the regular expression handling. + * + * @param flags Usefull flags are: REG_ICASE: ignore case. + */ +void ReDirectory::setRegExprFlags(int flags){ + m_regExprFlags = flags; +} + +/** @brief Find the first file with a given filename pattern. + * + * @param pattern A filename pattern. + * @param isRegExpr true: The pattern contains regular expressions. false: The pattern contains wildcards. + * + * @return true: A file has been found. false: The directory contains no file with the given pattern. + */ +bool ReDirectory::findFirst(const char* pattern, bool isRegExpr){ + bool rc = false; + if (m_valid){ + m_pattern.set(pattern, -1); + m_isRegExpr = isRegExpr; + if (isRegExpr){ + int rc2 = regcomp(&m_regExpr, pattern, m_regExprFlags); + + if (rc2 != 0){ + rc = false; + } else { + rewinddir(m_dir); + m_entry = NULL; + rc = findNext(); + } + } else { + rewinddir(m_dir); + m_entry = NULL; + rc = findNext(); + } + } + return rc; +} +/** @brief Find the next file with the pattern defined with the last findFirst() call. + * + * @return true: A file has been found. false: The directory contains no more file with the given pattern. + */ +bool ReDirectory::findNext(){ + bool rc = false; + if (m_valid){ + while (! rc && (m_entry = readdir(m_dir)) != NULL){ + if (m_isRegExpr){ + regmatch_t match[10]; + int rc2 = regexec (&m_regExpr, m_entry->d_name, sizeof match / sizeof match[0], match, 0); + if (rc2 == 0) + rc = true; + } else { + if (fnmatch(m_pattern.str(), m_entry->d_name, 0) == 0) + rc = true; + } + } + } + return rc; +} +/** @brief Finds the file with the youngest modification date. + * + * The other search criteria will be set by the preceding call of findFirst(). + * + * @param filename Out: "": No file found. Otherwise: The name of the youngest file. + * + * @param false: No file found. true: A file was found. + */ +bool ReDirectory::findYoungest(ReByteBuffer& filename){ + bool rc = false; + filename.setLength(0); + if (m_entry != NULL) + { + ReByteBuffer fullname; + fullname.append(m_path); + size_t length = fullname.getLength(); + struct stat info; + timespec youngest; + youngest.tv_sec = youngest.tv_nsec = 0; + do{ + fullname.setLength(length); + fullname.append(m_entry->d_name, -1); + if (stat(fullname.str(), &info) == 0 + && (info.st_mtim.tv_sec > youngest.tv_sec + || (info.st_mtim.tv_sec == youngest.tv_sec + && info.st_mtim.tv_nsec > youngest.tv_nsec))){ + youngest = info.st_mtim; + filename.set(m_entry->d_name, -1); + rc = true; + } + + } while(findNext()); + } + return rc; +} +/** @brief Returns the name of the current file found by the last findFirst() + * or findNext(). + * + * @return NULL: No current file exists. Otherwise: The name of the current file. + * + */ +const char* ReDirectory::currentFile(){ + const char* rc = m_entry == NULL ? NULL : m_entry->d_name; + return rc; +} +/** @brief Returns the full path of the current file or a given node. + * + * The current file is the file found by the last call of findFirst() or findNext(). + * + * @param path Out: The buffer for the full filename. + * If there is no current file the filename is empty. + * @param name NULL: The current file will be extended by the directory's path. + * Otherwise: This name will be extended by the directory's path. + * + * @return path (for chaining). + */ +ReByteBuffer& ReDirectory::fullpath(ReByteBuffer& path, const char* name){ + path.setLength(0); + if (name != NULL){ + path.append(m_path); + path.append(name, -1); + } else if (m_valid && m_entry != NULL){ + path.append(m_path); + path.append(m_entry->d_name, -1); + } + return path; +} + +/** @brief Find the first file matching a given filename pattern. + * + * @param dir The directory handle. + * @param pattern The pattern for the searched files. May contain wildcards. + * @param useRegExpr True: pattern is a regular expression. + */ + +#if defined RE_TESTUNIT +class TestReDirectory : public ReTestUnit { +public: + TestReDirectory() : ReTestUnit("ReFileFinder", __FILE__){ + run(); + } +private: + void run(){ + createTestDir(); + ReByteBuffer dir; + dir.set(getTestDir(), -1); + ReByteBuffer file1 = dir; + file1.append("abc.1.txt", -1); + createFile(file1.str(), "abc1"); + + ReByteBuffer file2 = dir; + file2.append("abc.2.txt", -1); + createFile(file2.str(), "abc2"); + + ReDirectory finder(dir.str()); + checkT(finder.isValid()); + + checkT(finder.findFirst("abc.*.txt", false)); + checkEqu("abc.1.txt", finder.currentFile()); + checkT(finder.findNext()); + checkEqu("abc.2.txt", finder.currentFile()); + checkF(finder.findNext()); + checkF(finder.findFirst("abx.*.txt", false)); + + checkT(finder.findFirst("abc[.][0-9][.]txt", true)); + checkEqu("abc.1.txt", finder.currentFile()); + checkT(finder.findNext()); + checkEqu("abc.2.txt", finder.currentFile()); + checkF(finder.findNext()); + checkF(finder.findFirst("abx[.][0-9][.]txt", true)); + } +}; +extern void testReDirectory(void); + +void testReDirectory(void){ + TestReDirectory unit; +} +#endif /*RE_TESTUNIT*/ diff --git a/base/ReDirectory.hpp b/base/ReDirectory.hpp new file mode 100644 index 0000000..3e93a43 --- /dev/null +++ b/base/ReDirectory.hpp @@ -0,0 +1,41 @@ +/* + * ReFileUtils.h + * + * Created on: 16.05.2010 + * Author: wk + */ + +#ifndef REFILEFINDER_H_ +#define REFILEFINDER_H_ + +/** ReDirectory searches files using pattern matching and/or file date. + */ +class ReDirectory{ +public: + ReDirectory(); + ReDirectory(const char* path); + ~ReDirectory(); +public: + void setDir(const char* path); + const char* getDir(void) const; + bool isValid(); + void setRegExprFlags(int flags); + bool findFirst(const char* pattern, bool isRegExpr); + bool findNext(); + bool findYoungest(ReByteBuffer& filename); + const char* currentFile(); + ReByteBuffer& fullpath(ReByteBuffer& path, const char* name = NULL); +private: + //@ true: The directory is ok. false: The directory is undefined. + bool m_valid; + DIR* m_dir; + //@ The name of the current directory. Always ends with slash! + ReByteBuffer m_path; + struct dirent* m_entry; + ReByteBuffer m_pattern; + bool m_isRegExpr; + regex_t m_regExpr; + // Flags for the regular expressions: REG_ICASE: ignore Case. + int m_regExprFlags; +}; +#endif /* REFILEFINDER_H_ */ diff --git a/base/ReException.cpp b/base/ReException.cpp new file mode 100644 index 0000000..7876bf8 --- /dev/null +++ b/base/ReException.cpp @@ -0,0 +1,195 @@ +/* + * ReException.cpp + * + * Created on: 07.05.2010 + * Author: wk + */ + +#include "rebase.hpp" + +/** @brief Constructor. + * + * This constructor is for derived classes only. + * The constructor of the derived class must fill the m_message. + */ +ReException::ReException() + : + m_message(NULL) +{ +} + +/** @brief Constructor. + * + * @param message The error message. + */ +ReException::ReException(const char* message) + : + m_message(strdup(message)) +{ +} +/** @brief Constructor. + * + * @param message The error message. + * @param file The name of the file where the exception is thrown. + * @param lineNo The line no name of the file where the exception is thrown. + */ +ReException::ReException(const char* message, const char* file, int lineNo) + : + m_message(NULL) +{ + char buffer[512]; + snprintf(buffer, sizeof buffer, "%s [%.100s-%d]", message, file, lineNo); + m_message = strdup(buffer); +} +/** @brief Destructor. + */ +ReException::~ReException(){ + if (m_message != NULL) + free((void*)m_message); +} + +ReException::ReException(const ReException& source) + : + m_message(strdup(source.getMessage())) +{ +} +ReException& ReException::operator =(const ReException& source){ + if (m_message != NULL) + free((void*) m_message); + m_message = strdup(source.getMessage()); + return *this; +} + +/** @brief Constructor. + * + * @param message The error message. + * @param format The format string with the error. + */ + +ReFormatException::ReFormatException(const char* message, const char* format) + : + ReException() +{ + char buffer[512]; + snprintf(buffer, sizeof buffer, "%s%s", message, format == NULL ? "" : format); + m_message = strdup(buffer); + +} +/** @brief Constructor. + * + * @param message The error message. + * @param format The format string with the error. + * @param file The name of the file where the exception is thrown. + * @param lineNo The line no name of the file where the exception is thrown. + */ +ReFormatException::ReFormatException(const char* message, const char* format, + const char* file, int lineNo) + : + ReException() +{ + char buffer[512]; + snprintf(buffer, sizeof buffer, "%s%s [%.100s-%d]", + message, format == NULL ? "" : format, file, lineNo); + m_message = strdup(buffer); +} + +/** @brief Constructor. + * + * @param name Name of the array. + * @param index The index which violates the array bounds. + * @param bound The bound of the array: upper or lower. + */ +ReBoundsException::ReBoundsException(const char* name, int index, int bound) +{ + char buffer[256]; + const char* format = index < bound + ? i18n("%s: index underflow: %d / %d") : i18n("%s: index overflow: %d / %d"); + snprintf(buffer, sizeof buffer, format, name, index, bound); + m_message = strdup(buffer); +} +/** @brief Constructor. + * + * @param name Name of the array. + * @param index The index which violates the array bounds. + * @param bound The bound of the array: upper or lower. + * @param file The name of the file where the exception is thrown. + * @param lineNo The line no name of the file where the exception is thrown. + */ +ReBoundsException::ReBoundsException(const char* name, int index, int bound, const char* file, int line) + : + ReException() +{ + char buffer[256]; + const char* format = index < bound + ? i18n("%s: index underflow: %d / %d [%s-%d]") : i18n("%s: index overflow: %d / %d [%s-%d]"); + snprintf(buffer, sizeof buffer, format, name, index, bound, file, line); + m_message = strdup(buffer); +} + +#if defined RE_TESTUNIT +class TestReException : public ReTestUnit, public ReVarArgTrigger { +public: + TestReException() + : + ReTestUnit("ReException", __FILE__), + m_argNo(0), + m_maxNo(0) + { + run(); + } + virtual void newArg(int no, int maxNo){ + m_argNo = no; + m_maxNo = maxNo; + } +private: + void run(){ + try{ + throw ReException("ReException"); + checkT(false); + } catch (ReException& e){ + log(false, e.getMessage()); + } + try{ + throw ReException("ReException", __FILE__, __LINE__); + checkT(false); + } catch (ReException& e){ + log(false, e.getMessage()); + } + try{ + throw ReFormatException("ReFormatException", "format"); + checkT(false); + } catch (ReException& e){ + log(false, e.getMessage()); + } + try{ + throw ReFormatException("ReFormatException", "format", __FILE__, __LINE__); + checkT(false); + } catch (ReException& e){ + log(false, e.getMessage()); + } + try{ + throw ReBoundsException("myArray", 101, 100); + checkT(false); + } catch (ReException& e){ + log(false, e.getMessage()); + } + + try{ + throw ReBoundsException("myArray", -1, 0, __FILE__, __LINE__); + checkT(false); + } catch (ReException& e){ + log(false, e.getMessage()); + } + + } +private: + int m_argNo; + int m_maxNo; +}; +extern void testReException(void); + +void testReException(void){ + TestReException unit; +} +#endif /*RE_TESTUNIT*/ + diff --git a/base/ReException.hpp b/base/ReException.hpp new file mode 100644 index 0000000..2aa09ec --- /dev/null +++ b/base/ReException.hpp @@ -0,0 +1,56 @@ +/* + * ReException.h + * + * Created on: 07.05.2010 + * Author: wk + */ + +#ifndef REEXCEPTION_H_ +#define REEXCEPTION_H_ + +/** The base exception. + */ +class ReException { +public: + ReException(const char* message); + ReException(const char* message, const char* file, int line); + virtual ~ReException(); +public: + ReException(const ReException& source); + ReException& operator =(const ReException& source); +protected: + ReException(); +public: + /** @brief Sets the message. + * @param message The new message. + */ + inline void setMessage(const char* message){ + if (m_message != NULL) + free((void *) m_message); + m_message = strdup(message); + } + /** @brief Returns the message. + * @returns The description of the exception. + */ + inline const char* getMessage() const{ + return m_message; + } +protected: + const char* m_message; +}; +/** This exception is used on format errors. + */ +class ReFormatException : public ReException{ +public: + ReFormatException(const char* message, const char* format); + ReFormatException(const char* message, const char* format, const char* file, int line); +}; +/** This exception is called when an index underflow or overflow is done. + */ +class ReBoundsException : public ReException{ +public: + ReBoundsException(const char* name, int index, int bound); + ReBoundsException(const char* name, int index, int bound, const char* file, int line); +}; + +#endif /* REEXCEPTION_H_ */ diff --git a/base/ReHashList.cpp b/base/ReHashList.cpp new file mode 100644 index 0000000..88b9501 --- /dev/null +++ b/base/ReHashList.cpp @@ -0,0 +1,244 @@ +/* + * ReStringList.cpp + * + * Created on: 18.05.2010 + * Author: wk + */ + +#include "rebase.hpp" + +/** Constructor. + */ +ReHashList::ReHashList() + : + m_keys(), + m_values() +{ +} +/** Destructor + */ +ReHashList::~ReHashList() { +} + +/** @brief Puts a key value pair into the hashlist. + * + * @param key The key byte sequence. + * @param keyLength The length of key. + * @param value The value byte sequence. + * @param valueLength The length of value. + */ +void ReHashList::put(const Byte* key, size_t keyLength, + const Byte* value, size_t valueLength){ + if (keyLength == (size_t) -1) + keyLength = strlen(key) + 1; + if (valueLength == (size_t) -1) + valueLength = strlen(value) + 1; + int ix = find(key, keyLength); + bool storeValue = false; + if (ix < 0){ + ReSeqList::Index pos = m_values.getLength(); + storeValue = true; + m_keys.add(-1, key, keyLength, pos); + } else { + Sequence* seq = m_keys.getInfo(ix); + // m_tag contains the index into m_values. + Byte* ptr = m_values.getBuffer() + seq->m_tag; + size_t valLength = * (size_t *) ptr; + // Can we take the old storage? + if (valLength >= valueLength){ + // Yes, it is enough space: + * (size_t *) ptr = valueLength; + ptr += sizeof (size_t); + memcpy(ptr, value, valueLength); + } else { + storeValue = true; + seq->m_tag = m_values.getLength(); + } + } + if (storeValue){ + m_values.append((Byte*) &valueLength, sizeof valueLength); + m_values.append(value, valueLength); + } +} +/** @brief Puts a key value pair into the hashlist. + * + * @param key The key. This is a C string. + * @param value The value. This is a C string. + */ +void ReHashList::put(const char* key, const char* value){ + put(key, -1, value, -1); +} +/** @brief Puts a key value pair into the hashlist. + * + * @param key The key. + * @param value The value. + */ +void ReHashList::put(const ReByteBuffer& key, const ReByteBuffer& value){ + put(key.getBuffer(), key.getLength(), value.getBuffer(), value.getLength()); +} +/** @brief Returns the value of a key value pair. + * + * @param key The key byte sequence. + * @param keyLength The length of key. + * @param value Out: The buffer for the value. + * + * @return false: The key was not found. true: The key was found. + */ +bool ReHashList::get(const Byte* key, size_t keyLength, + ReByteBuffer& value) const{ + if (keyLength == (size_t) -1) + keyLength = strlen(key) + 1; + + int ix = find(key, keyLength); + bool rc = ix >= 0; + if (rc){ + ReSeqList::Sequence* seq = m_keys.getInfo(ix); + // m_tag contains the index into m_values: + Byte* ptr = m_values.getBuffer() + seq->m_tag; + // m_values contains : + size_t valLength = * (size_t*) ptr; + ptr += sizeof (size_t); + value.set(ptr, valLength); + } + return rc; +} +/** @brief Returns the value of a key value pair. + * + * @param key The key. + * @param value Out: The buffer for the value. + * + * @return false: The key was not found. true: The key was found. + */ +bool ReHashList::get(const ReByteBuffer& key, + ReByteBuffer value) const{ + bool rc = get(key.getBuffer(), key.getLength(), value); + return rc; +} +/** @brief Deletes all entries in the list. + */ +void ReHashList::clear(){ + m_keys.clear(); + m_values.setLength(0); +} + +/** @brief Implements an iterator. + * + * Returns the next item with its key and/or its value. + * + * @param position In/Out: The position of the next item. Set it to 0 for the first item. + * @param key Out: The key of the found item. If NULL the key is not interesting. + * @param value Out: The value of the found item. If NULL the value is not interesting. + * + * @param true: An item was found. false: No more items. + */ +bool ReHashList::next(size_t& position, ReByteBuffer* key, ReByteBuffer* value){ + bool rc = position < m_keys.getCount(); + if (rc){ + ReSeqList::Sequence* seq = m_keys.getInfo(position++); + if (key != NULL){ + const Byte* ptr = m_keys.getContent() + seq->m_index; + key->setLength(seq->m_length); + memcpy(key->getBuffer(), ptr, seq->m_length); + } + if (value != NULL){ + const Byte* ptr = m_values.getBuffer() + seq->m_tag; + size_t length = * (size_t*) ptr; + ptr += sizeof (size_t); + value->setLength(length); + memcpy(value->getBuffer(), ptr, length); + } + } + return rc; +} + +/** @brief Returns the index of key. + * + * @param key The byte sequence of the key. + * @param length The length of key. + * + * @return: -1: The key was not found. Otherwise: The index of the key in the key sequence array. + */ +int ReHashList::find(const Byte* key, size_t length) const{ + if (length == (size_t) -1) + length = strlen(key) + 1; + int rc = -1; + int count = m_keys.getCount(); + for (int ii = 0; rc < 0 && ii < count; ii++){ + ReSeqList::Sequence* seq = m_keys.getInfo(ii); + if (seq->m_length == length){ + const Byte* ptr = m_keys.getContent() + seq->m_index; + if (memcmp(ptr, key, length) == 0) + rc = ii; + } + } + return rc; +} + +#if defined RE_TESTUNIT +class TestReHashList : public ReTestUnit { +public: + TestReHashList() : ReTestUnit("ReHashList", __FILE__){ + run(); + } +private: + void run(){ + testBasic(); + testNext(); + } + void testBasic(){ + ReHashList hash; + ReByteBuffer key, value; + + hash.put("abc", "123"); + checkT(hash.get("abc", -1, value)); + checkEqu("123", value.str()); + + hash.put("ab", "999"); + checkT(hash.get("ab", -1, value)); + checkEqu("999", value.str()); + + checkT(hash.get("abc", -1, value)); + checkEqu("123", value.str()); + + hash.put("abc", "!!!"); + checkT(hash.get("abc", -1, value)); + checkEqu("!!!", value.str()); + + checkT(hash.get("ab", -1, value)); + checkEqu("999", value.str()); + + hash.put("abc", "longer"); + checkT(hash.get("abc", -1, value)); + checkEqu("longer", value.str()); + + checkT(hash.get("ab", -1, value)); + checkEqu("999", value.str()); + } + void testNext(){ + ReHashList hash; + hash.put("1", "8"); + hash.put("2", "4"); + hash.put("4", "2"); + hash.put("8", "1"); + int flagsKey = 0; + int flagsVal = 0; + size_t pos = 0; + ReByteBuffer key, value; + while(hash.next(pos, &key, &value)){ + int x = atol(key.getBuffer()); + int y = atol(value.getBuffer()); + checkEqu(8, x * y); + flagsKey += x; + flagsVal += y; + } + checkEqu(15, flagsKey); + checkEqu(15, flagsVal); + } +}; +extern void testReHashList(void); + +void testReHashList(void){ + TestReHashList unit; +} +#endif /*RE_TESTUNIT*/ + diff --git a/base/ReHashList.hpp b/base/ReHashList.hpp new file mode 100644 index 0000000..09f5e86 --- /dev/null +++ b/base/ReHashList.hpp @@ -0,0 +1,42 @@ +/* + * ReStringList.h + * + * Created on: 18.05.2010 + * Author: wk + */ + +#ifndef REHASHLIST_H_ +#define REHASHLIST_H_ + +/** @brief A (very) simple associative array. + *

An instance stores key value pairs.

+ *

Keys and values are byte sequences. This includes c strings. + *

+ */ +class ReHashList { +public: + typedef char Byte; + typedef ReSeqList::Sequence Sequence; +public: + ReHashList(); + virtual ~ReHashList(); +public: + void clear(); + bool get(const Byte* key, size_t keyLength, ReByteBuffer& value) const; + bool get(const ReByteBuffer& key, ReByteBuffer value) const; + bool next(size_t& position, ReByteBuffer* key, ReByteBuffer* val); + void put(const Byte* key, size_t keyLength, const Byte* value, size_t valueLength); + void put(const char* key, const char* value); + void put(const ReByteBuffer& key, const ReByteBuffer& value); +protected: + int find(const Byte* key, size_t length) const; + +protected: + //@ Containing an array of keys. + ReSeqList m_keys; + //@ Containing the values. The tag of m_key is the index + //@of the start position in m_values. + ReByteBuffer m_values; +}; + +#endif /* REHASHLIST_H_ */ diff --git a/base/ReI18N.cpp b/base/ReI18N.cpp new file mode 100644 index 0000000..c40a121 --- /dev/null +++ b/base/ReI18N.cpp @@ -0,0 +1,210 @@ +/* + * ReI18N.cpp + * + * Created on: 22.05.2010 + * Author: wk + */ + +#include "../base/restring.hpp" + +//@ The current language. If NULL no language has been chosen. +ReI18N::LanguagePack* ReI18N::m_current = 0; +//@ An array of all available translation packages. +ReI18N::LanguagePack ReI18N::m_list[8]; +//@ The number of available translations. +int ReI18N::m_count = 0; +//@ This instance will be used for convenient usage of i18nf(const char* key) +//@ Because of this i18nf(const char* key) is not reentrant. +ReVarArgs ReI18N::m_varArgs; + + +/** @brief Initializes the module. + * + * The language will be chosen in the following order: + *
    + *
  • Environmentvariable LC_MESSAGES
  • + *
  • Environmentvariable LC_ALL
  • + *
  • Environmentvariable LANG
  • + *
  • Default language: here "de"
  • + *
+ */ +void ReI18N::init() { + static bool first = true; + if (first){ + if (m_current == NULL) + first = false; + if (getenv("LC_MESSAGES") != NULL){ + changeLanguage(getenv("LC_MESSAGES")); + } else if (getenv("LC_ALL") != NULL){ + changeLanguage(getenv("LC_ALL")); + } else if (getenv("LANG") != NULL){ + changeLanguage(getenv("LANG")); + } else + changeLanguage("de"); + } +} +/** @brief Puts a language package to the language list. + * + *
Typical usage:
+ * 
+ * static Phrase s_de [] = {
+ *    { "Example", "Beispiel" },
+ *    { "Very short", "Sehr kurz" }
+ * };
+ * static bool s_init = ReI18N::registerLanguage("de", s_de);
+ * 
+ * + * @param language The id of the language: xx or xx_yy or xx_yy.zz + * @param translation A list of pairs: English message (key) and translated message. + * The entries are sorted by the English text. + * @param count The number of entries in translation. + * @return true So it can used in an expression. + */ +bool ReI18N::registerLanguage(const char* language, + const Phrase translation[], size_t count){ + if ((size_t) m_count < sizeof m_list / sizeof m_list[0]){ + LanguagePack* lang = m_list + m_count++; + lang->m_language = language; + lang->m_translation = translation; + lang->m_count = count; + } + return true; +} +/** @brief: Change the current language package. + * + * The best matching language package will be chosen: + * Example: + * language is "de_de". + * If a package exists with the id "de_ch" it will be chosen. + * If not and "de.*" exists it will be choosenI + * If not and "de_*" exists it will be choosenI + * + * + * @param language The wanted language: may be xx or xx_yy* + */ +void ReI18N::changeLanguage(const char* language){ + int found = -1; + // Search for xx_yy* + if (strlen(language) >= 5){ + for (int ii = 0; found < 0 && ii < m_count; ii++){ + if (strncmp(m_list[ii].m_language, language, 5) == 0) + found = ii; + } + } + if (found < 0){ + // Search for xx or xx.*: + for (int ii = 0; found < 0 && ii < m_count; ii++){ + const char* current = m_list[ii].m_language; + if (ReStringUtils::strnicmp(current, language, 2) == 0 + && (strlen(current) == 2 + || (strlen(current) > 2 && current[2] == '.'))) + found = ii; + } + } + // Search for xx* + if (found < 0 && strlen(language) >= 5){ + for (int ii = 0; found < 0 && ii < m_count; ii++){ + if (strncmp(m_list[ii].m_language, language, 2) == 0) + found = ii; + } + } + + if (found >= 0) + m_current = m_list + found; +} + +static int compare(const void* i1, const void* i2){ + const char* s1 = ((ReI18N::Phrase*) i1)->m_key; + const char* s2 = ((ReI18N::Phrase*) i2)->m_key; + return strcmp(s1, s2); +} +/** @brief Translates a string without placeholders. + * + * Note: Despite of being static this method + * is reentrant and threadsafe. + * + * @param key The english text. + * + * @return key: No translation could be found. + * Otherwise: The translated key. + */ +const char* ReI18N::tr(const char* key){ + init(); + const char* rc = key; + if (m_current != NULL){ + Phrase current; + current.m_key = key; + Phrase* found = (Phrase*) bsearch(¤t, m_current->m_translation, + m_current->m_count, sizeof (Phrase), compare); + if (found != NULL) + rc = found->m_value; + } + return rc; +} +/** @brief Translates a string with placeholders. + * + * Note: This method is not reentrant + * and not threadsafe. It uses a static buffer. + * + *
Typical usage:
+ * 
+ * printf ("%s\n",
+ *    ReI18N.trF("Number of files: $1 Number of dirs: $2")
+ *       .arg(files).arg(dirs).asCString());
+ * 
+ * @param key The english text. + * + * @return An variant argument instance for chaining. + */ +ReVarArgs& ReI18N::trF(const char* key){ + return trF(key, m_varArgs); +} +/** @brief Translates a string with placeholders. + * + * Note: This method is reentrant + * and threadsafe. + * + *
Typical usage:
+ * 
+ * ReVarArgs args;
+ * printf ("%s\n",
+ *    ReI18N.trF("Number of files: $1 Number of dirs: $2", args)
+ *       .arg(files).arg(dirs).asCString());
+ * 
+ * @param key The english text. + * @param args A buffer for the variant argument handling. + * + * @return The variant argument instance args for chaining. + */ +ReVarArgs& ReI18N::trF(const char* key, ReVarArgs& args){ + init(); + // We hide the method name tr. xgettext must not see tr() +#define translate tr + args.reset(translate(key)); + return args; +} + +#if defined RE_TESTUNIT +class TestReI18N : public ReTestUnit { +public: + TestReI18N() + : + ReTestUnit("ReI18N", __FILE__) + { + run(); + } +private: + void run(){ + checkEqu("dies ist ein Test", i18n("this is a test")); + checkEqu("eins: 1 zwei: 2", i18nf("one: $1 two: $2").arg(1).arg(2).asCString()); + + } +}; +extern void testReI18N(void); + +void testReI18N(void){ + TestReI18N unit; +} +#endif /*RE_TESTUNIT*/ + + diff --git a/base/ReI18N.hpp b/base/ReI18N.hpp new file mode 100644 index 0000000..070d00e --- /dev/null +++ b/base/ReI18N.hpp @@ -0,0 +1,53 @@ +/* + * ReI18N.h + * + * Created on: 22.05.2010 + * Author: wk + */ + +#ifndef REI18N_H_ +#define REI18N_H_ + +class ReI18N { +public: + //@ Storage for the ISO language names (with trailing '\\0'). + typedef struct { char m_id[3]; } Language; + typedef struct { + const char* m_key; + const char* m_value; + } Phrase; + typedef struct { + const char* m_language; + const Phrase* m_translation; + size_t m_count; + } LanguagePack; +public: + static void init(); + static bool registerLanguage(const char* language, + const Phrase translation[], size_t count); + static const char* tr(const char* key); + static ReVarArgs& trF(const char* key); + static ReVarArgs& trF(const char* key, ReVarArgs& args); + static void changeLanguage(const char* language); +protected: + static LanguagePack* m_current; + static LanguagePack m_list[8]; + static int m_count; + static ReVarArgs m_varArgs; +}; +inline const char* i18n(const char* key){ + return ReI18N::tr(key); +} +//@ Translates a string into another language. +//@ Not threadsafe, not reentrant. +inline ReVarArgs& i18nf(const char* key){ + return ReI18N::trF(key); +} +//@ Translates a string into another language. +//@ Threadsafe, reentrant. +inline ReVarArgs& i18nf(const char* key, ReVarArgs& args){ + return ReI18N::trF(key, args); +} +//@ Use this macros for transformable strings in initializations of variables. +#define i18nm(key) key +#endif /* REI18N_H_ */ diff --git a/base/ReLogger.cpp b/base/ReLogger.cpp new file mode 100644 index 0000000..9a2699f --- /dev/null +++ b/base/ReLogger.cpp @@ -0,0 +1,425 @@ +/* + * ReLogger.cpp + * + * Created on: 05.05.2010 + * Author: wk + */ + +#include "rebase.hpp" + + +ReLogger* ReLogger::m_globalLogger = NULL; + +/** @brief Returns the global logger. + * + * If no global logger exists a standard logger will be initialized + * and set to the global logger. + * + * @return The global logger. + */ +ReLogger* ReLogger::globalLogger(){ + if (m_globalLogger == NULL){ + ReStreamAppender* stream = new ReStreamAppender(stderr); + ReFileAppender* file = new ReFileAppender(); + file->setConfig("globallogger", 5, 1000100); + m_globalLogger = new ReLogger(); + m_globalLogger->addAppender(stream); + m_globalLogger->addAppender(file); + } + return m_globalLogger; +} +/** @brief Constructor. + */ +ReAppender::ReAppender() + : + m_errorCategories(CAT_ALL), + m_warningCategories(CAT_ALL), + m_infoCategories(CAT_ALL), + m_granularities(GRAN_ALL) +{ +} +/** @brief Destructor. + */ +ReAppender::~ReAppender(){ + +} +/** Tests whether the log message should be logged. + * + * @param mode The specification of the current log message. + * + * @return true: The + */ +bool ReAppender::accept(ReClassCategoryGranularity mode){ + ReLogCategory category = (ReLogCategory) (mode & CAT_ALL); + ReLogClass aClass = (ReLogClass) (mode & LOG_CLASS_ALL); + bool rc; + + switch (aClass) + { + case LOG_ERROR: + case LOG_ABORT: + rc = (m_errorCategories & category) != 0; + break; + case LOG_WARNING: + rc = (m_warningCategories & category) != 0; + break; + default: + { + ReLogGranularity granularity = (ReLogGranularity) (mode & GRAN_ALL); + if (granularity == 0) + granularity = GRAN_USER; + + rc = (m_infoCategories & category) != 0 + && (m_granularities & granularity) != 0; + break; + } + } + return rc; +} +/** @brief Sets the mode parameters. + * + * @param errors The categories in which error logging will be done. + * @param warnings The categories in which warning logging will be done. + * @param info The categories in which info logging will be done. + * @param granularities The granularities in which info logging will be done. + */ +void ReAppender::setMode(ReLogCategories errors, ReLogCategories warnings, + ReLogCategories infos, ReLogGranularities granularities){ + m_errorCategories = (errors & CAT_ALL); + m_warningCategories = (warnings & CAT_ALL); + m_infoCategories = (infos & CAT_ALL); + m_granularities = (granularities & GRAN_ALL); +} + +/** @brief Constructor. + * + * @param stream An open stream used for the output. + */ +ReStreamAppender::ReStreamAppender(FILE* stream) + : + ReAppender(), + m_stream(stream) +{ +} +/** @brief Destructor. + */ +ReStreamAppender::~ReStreamAppender(){ +} + +/** @brief Puts a message into the internal stream. + * + * @param logger The logger calling the method. + * @param message The message to write. + */ +void ReStreamAppender::say(ReLogger* logger, const char* message){ + const char* prefix = logger->getStandardPrefix(); + fputs(prefix, m_stream); + if (message != NULL) + fputs(message, m_stream); + else + fputs(logger->asCString(), m_stream); + fputc('\n', m_stream); + fflush(m_stream); +} +/** @brief Constructor. + */ +ReFileAppender::ReFileAppender() + : + ReStreamAppender(NULL), + //m_filePattern + m_maxFiles(10), + m_maxSize(1000000), + m_currentSize(0), + m_currentFileNo(0) +{ + strcpy(m_filePattern, "logger.*"); +} +/** @brief Destructor. + */ +ReFileAppender::~ReFileAppender(){ +} + +/** @brief Sets the configuration data. + * + * @param pattern A filename pattern. Should contain a '*' which will replaced + * with a version number. + * @param maxFiles Maximum number of logfiles. If more files exist they will be deleted. + * @param maxSize Maximum size of one logfile. + * + */ +void ReFileAppender::setConfig(const char* pattern, int maxFiles, int maxSize){ + const char* placeholder = "%04d"; + m_maxFiles = maxFiles < 2 ? 2 : maxFiles; + m_maxSize = maxSize < 10 ? 10 : maxSize; + + const char* first = strchr(pattern, '*'); + size_t len; + if (first == NULL){ + snprintf(m_filePattern, sizeof m_filePattern, "%s.%s.log", pattern, placeholder); + } else { + len = first - pattern; + memcpy(m_filePattern, pattern, len); + strcpy(m_filePattern + len, placeholder); + strcat(m_filePattern + len, first + 1); + } + + ReByteBuffer fn, protocol, path, name, ext; + ReStringUtils::splitPath(m_filePattern, &protocol, &path, &name, &ext); + ReStringUtils::joinPath(fn, &protocol, &path, NULL, NULL); + if (fn.getLength() == 0) + fn.set(".", 1); + ReDirectory dir(fn.str()); + if (! dir.isValid()){ + fprintf(stderr, "%s\n", i18nf("$1: Not a valid directory: $2").arg(pattern).arg(dir.getDir()).asCString()); + assert(! dir.isValid()); + } else { + ReStringUtils::joinPath(fn, NULL, NULL, &name, &ext); + int ix = fn.indexOf(placeholder, -1, 0); + fn.splice(ix, 4, "[0-9]{4}", -1); + + // Looking for the current logfile: + if (! dir.findFirst(fn.str(), true)){ + fn.splice(ix, 8, "0001", 4); + m_currentFileNo = 1; + } else { + dir.findYoungest(fn); + m_currentFileNo = fn.atoi(ix, 4); + } + struct stat info; + ReByteBuffer fullname; + dir.fullpath(fullname, fn.str()); + int rc = lstat(fullname.str(), &info); + + if (rc == 0 && (m_currentSize = info.st_size) > m_maxSize) + changeFile(); + else{ + m_stream = fopen(fullname.str(), "a"); + assert(m_stream != NULL); + } + } +} +/** @brief Opens a new file for logging. + */ +void ReFileAppender::changeFile(){ + if (m_stream != NULL){ + fclose(m_stream); + m_stream = NULL; + } + + char filename[512]; + const size_t FILENO_UBOUND = 10000; + + // Delete some of the oldest files to make room for the new: + for (int ii = 0; ii < int(FILENO_UBOUND - m_maxFiles); ii++){ + int fileNo = (FILENO_UBOUND + m_currentFileNo - m_maxFiles - ii) % m_maxFiles; + snprintf(filename, sizeof filename, m_filePattern, fileNo); + struct stat info; + if (lstat(filename, &info) == 0) + unlink(filename); + else + break; + } + if (++m_currentFileNo >= FILENO_UBOUND) + m_currentFileNo = 0; + snprintf(filename, sizeof filename, m_filePattern, m_currentFileNo); + m_stream = fopen(filename, "w"); + m_currentSize = 0; +} +/** @brief Constructor. + */ +ReLogger::ReLogger(bool isGlobal) + : + ReVarArgs(), + m_appenderList(new ReAppender* [8]), + m_appenderListSize(8), + m_appenderListLength(0), + // m_standardPrefix[64] + m_mode(0), + m_location(0), + m_stdConsoleAppender(NULL), + m_stdFileAppender(NULL), + m_locationOfOpenSayF(0) +{ + if (isGlobal) + m_globalLogger = this; + m_standardPrefix[0] = '\0'; +} +/** @brief Destructor. + */ +ReLogger::~ReLogger(){ + delete m_stdConsoleAppender; + m_stdConsoleAppender = NULL; + delete m_stdFileAppender; + m_stdFileAppender = NULL; + + delete[] m_appenderList; + m_appenderList = NULL; + m_appenderListSize = 0; +} +/** @brief Issues a log message. + * + * The message will be put to all appenders which accept the log message. + * + * @param mode The mode controlling the issuing of the logging. + * This mode will be compared to the settings of the appenders. + * @param position The identification of the call. + * @param message The message to log. + * + * @return true + */ +bool ReLogger::say(ReClassCategoryGranularity mode, ReLogLocation location, const char* message){ + m_mode = mode; + m_location = location; + m_standardPrefix[0] = '\0'; + + for (size_t ii = 0; ii < m_appenderListLength; ii++){ + ReAppender* app = m_appenderList[ii]; + if (app->accept(mode)) + app->say(this, message); + } + return true; +} +/** @brief Issues a formatted log message. + * + * The message will be put to all appenders which accept the log message. + * Note:
+ * Don't forget to finish the argument list with ReVarArgs::end()! + * + * @param mode The mode controlling the issuing of the logging. + * This mode will be compared to the settings of the appenders. + * @param location The identification of the call. + * @param message The message to log. + * + * @return true + */ +ReVarArgs& ReLogger::sayF(ReClassCategoryGranularity mode, ReLogLocation location, + const char* format){ + if (m_locationOfOpenSayF != 0){ + char message[128]; + snprintf(message, sizeof message, "missing ReLogger::end(): Location: %d", m_locationOfOpenSayF); + say(LOG_ERROR|GRAN_USER|CAT_LIB, LC_LOGGER_SAYF_OPEN, message); + } + m_locationOfOpenSayF = location; + m_mode = mode; + m_location = location; + m_standardPrefix[0] = '\0'; + reset(format); + return *this; +} +/** Adds an appender to the appender list. +* +* @param appender The appender. +*/ +void ReLogger::addAppender(ReAppender* appender){ + if (m_appenderListLength >= m_appenderListSize) + throw ReBoundsException("appender list", + m_appenderListLength + 1, m_appenderListSize, + __FILE__, __LINE__); + m_appenderList[m_appenderListLength++] = appender; +} + +/** Adds standard appenders to the appender list. +* +* @param console true: A console appender will be added. +* @param file NULL: No file appender. +* Otherwise: A file appender will be appended. +* @param maxFiles The maximal count of log files produced by the file appender. +* @param masSize The maximal size of one log file produced by the file appender. +*/ +void ReLogger::addStandardAppenders(bool console, + const char* file, int maxFiles, int maxSize){ + if (console){ + m_stdConsoleAppender = new ReStreamAppender(stdout); + addAppender(m_stdConsoleAppender); + } + if (file != NULL){ + m_stdFileAppender = new ReFileAppender(); + m_stdFileAppender->setConfig(file, maxFiles, maxSize); + addAppender(m_stdFileAppender); + } + + +} + +/** @brief Returns the standard prefix of a log line. + * + * The standard prefix is log class, date, time, and location). + * Most of the appenders will use this prefix. + * It will be build on demand. + * + * @return The standard prefix. + */ +const char* ReLogger::getStandardPrefix(void){ + if (m_standardPrefix[0] == 0) + { + char cc; + switch(m_mode & LOG_CLASS_ALL){ + case LOG_ABORT: + cc = '#'; + break; + case LOG_ERROR: + cc = '!'; + break; + case LOG_WARNING: + cc = '+'; + break; + default: + cc = ' '; + break; + } + m_standardPrefix[0] = cc; + + time_t now = time(NULL); + struct tm* now1 = localtime(&now); + strftime(m_standardPrefix + 1, sizeof m_standardPrefix - 1, + "%Y.%m.%d %H:%M:%S", now1); + size_t len = strlen(m_standardPrefix); + char* ptr = m_standardPrefix + len; + snprintf (ptr, sizeof m_standardPrefix - len, " %ld: ", + (long) m_location); + } + return m_standardPrefix; +} +/** @brief Terminates the chain of arg() calls. + * + * This method puts the data to the (accepting) appenders. + * + */ +void ReLogger::end(void){ + for (size_t ii = 0; ii < m_appenderListLength; ii++){ + ReAppender* app = m_appenderList[ii]; + if (app->accept(m_mode)) + app->say(this, NULL); + } + m_locationOfOpenSayF = 0; +} + +#if defined RE_TESTUNIT +class TestReLogger : public ReTestUnit { +public: + TestReLogger() : ReTestUnit("ReLogger", __FILE__){ + run(); + } +private: + void run(){ + ReStreamAppender app1(stdout); + app1.setMode(CAT_ALL, CAT_ALL, CAT_ALL, GRAN_ALL); + + ReLogger logger; + logger.addAppender(&app1); + log(false, "2 Errors and a warning:"); + logger.say(LOG_ABORT | CAT_TEST | GRAN_TRACE, __LINE__, "abort");; + logger.say(LOG_ERROR | CAT_TEST, __LINE__, "error"); + logger.say(LOG_WARNING | CAT_TEST, __LINE__, "warning"); + + logger.sayF(CAT_TEST, __LINE__, "Two: $1 eleven: $021").arg(2).arg(1).end(); + globalLogger()->say(CAT_LIB, __LINE__, "globalLogger()"); + } +}; +extern void testReLogger(void); + +void testReLogger(void){ + TestReLogger unit; +} +#endif /*RE_TESTUNIT*/ + diff --git a/base/ReLogger.hpp b/base/ReLogger.hpp new file mode 100644 index 0000000..422adfc --- /dev/null +++ b/base/ReLogger.hpp @@ -0,0 +1,174 @@ +/* + * ReLogger.h + * + * Created on: 05.05.2010 + * Author: wk + */ + +#ifndef RELOGGER_H_ +#define RELOGGER_H_ + +/** + * Every call of the logger should have an unique id. This is the type for this. + */ +typedef unsigned long int ReLogLocation; +/** + * The log messages can be divided in 4 classes: + * Aborts, errors, warnings and informations. + */ +enum ReLogClass{ + LOG_ABORT = 1, + LOG_ERROR = 2, + LOG_WARNING = 4, + LOG_INFO = 8, + + LOG_CLASS_ALL = 0xf +}; +/* + * + */ +typedef unsigned int ReSetOfClasses; +/** + * The granularity allows to filter log messages under the aspect of the amount. + */ +enum ReLogGranularity{ + GRAN_USER = 0x00000010, + GRAN_GURU = 0x00000020, + GRAN_DUMP = 0x00000040, + GRAN_TRACE = 0x00000080, + GRAN_SPECIAL_1 = 0x00000100, + GRAN_SPECIAL_2 = 0x00000200, + GRAN_SPECIAL_3 = 0x00000400, + GRAN_SPECIAL_4 = 0x00000800, + + GRAN_ALL = 0x00000ff0 +}; +/** A (bitwise) combination of granularities. + */ +typedef unsigned long int ReLogGranularities; +enum ReLogCategory{ + CAT_START_STOP = 0x00001000, + CAT_CONFIG = 0x00002000, + CAT_NETWORK = 0x00004000, + CAT_GUI = 0x00008000, + CAT_LIB = 0x00010000, + CAT_OS = 0x00020000, + CAT_FILE = 0x00040000, + CAT_PROG = 0x00080000, + CAT_RESOURCE = 0x00100000, + CAT_TEST = 0x00200000, + + CAT_ALL = 0xfffff000, +}; +/** A (bitwise) combination of categories. + */ +typedef unsigned long int ReLogCategories; +/** A (bitwise) combination of a class, a category and a granularity. + */ +typedef unsigned long int ReClassCategoryGranularity; +class ReLogger; + +/** + * Implements an abstract base class for handling of the output of an logger. + */ +class ReAppender{ +public: + ReAppender(); + virtual ~ReAppender(); +public: + virtual void say(ReLogger* logger, const char* message) = 0; + bool accept(ReClassCategoryGranularity mode); + void setMode(ReLogCategories errors, ReLogCategories warnings, + ReLogCategories infos, ReLogGranularities granularities); +protected: + ReLogCategories m_errorCategories; + ReLogCategories m_warningCategories; + ReLogCategories m_infoCategories; + ReLogGranularities m_granularities; +}; + +/** + * Implements a class which is writing log messages to a stream. + */ +class ReStreamAppender : public ReAppender{ +public: + ReStreamAppender(FILE* stream); + virtual ~ReStreamAppender(); +private: + // Not accessible, not implemented! + ReStreamAppender(const ReStreamAppender& source); + // Not accessible, not implemented! + ReStreamAppender& operator =(const ReStreamAppender& source); +public: + virtual void say(ReLogger* logger, const char* message); +protected: + FILE* m_stream; +}; + +/** + * Implements a class which is writing log messages to a file. + */ +class ReFileAppender : public ReStreamAppender { +public: + ReFileAppender(); + virtual ~ReFileAppender(); +public: + void setConfig(const char* filename, int maxFiles, int maxSize); +private: + void changeFile(); + +protected: + char m_filePattern[512]; + size_t m_maxFiles; + size_t m_maxSize; + size_t m_currentSize; + size_t m_currentFileNo; +}; + +/** This class allows the logging of messages. + * The output itself is done by so called appenders. + * This allows a flexible handling of different media: files, dialog boxes, console... + * The message can be contain placeholders which will be replaced + * by computed data similar sprintf(), but in a typesafe way (@see sayF()). + */ +class ReLogger : public ReVarArgs { +public: + static ReLogger* globalLogger(); + static ReLogger* m_globalLogger; +public: + ReLogger(bool isGlobal = true); + virtual ~ReLogger(); +private: + // Not accessible, not implemented! + ReLogger(const ReLogger& source); + // Not accessible, not implemented! + ReLogger& operator =(const ReLogger& source); +public: + bool say(ReClassCategoryGranularity mode, ReLogLocation location, const char* message); + ReVarArgs& sayF(ReClassCategoryGranularity mode, ReLogLocation location, const char* format); + virtual void end(void); + + void addAppender(ReAppender* appender); + + const char* getStandardPrefix(void); + ReClassCategoryGranularity getCurrentMode(void) const; + int getCurrentPosition(void) const; + void addStandardAppenders(bool console, const char* file, + int fileCount = 5, int fileSize = 1000100); +protected: + ReAppender** m_appenderList; + size_t m_appenderListSize; + size_t m_appenderListLength; + char m_standardPrefix[64]; + ReClassCategoryGranularity m_mode; + ReLogLocation m_location; + ReAppender* m_stdConsoleAppender; + ReFileAppender* m_stdFileAppender; + int m_locationOfOpenSayF; +}; + +inline ReLogger* globalLogger() { + return ReLogger::globalLogger(); +} + +#endif /* RELOGGER_H_ */ diff --git a/base/ReProgramArgs.cpp b/base/ReProgramArgs.cpp new file mode 100644 index 0000000..7deb6aa --- /dev/null +++ b/base/ReProgramArgs.cpp @@ -0,0 +1,697 @@ +/* + * ReProgramArgs.cpp + * + * Created on: 25.05.2010 + * Author: wk + */ + +#include "rebase.hpp" + + +/** @brief Constructor. + * + * @param caller The object which throw the exception. + * @param message The error message with one or two placeholders. + * @param arg1 The first argument (for the first placeholder). + * @param arg2 The 2nd argument (for the 2nd placeholder). If NULL only one placeholder exists. +*/ +ReOptionException::ReOptionException(ReProgramArgs* caller, const char* message, + const char* arg1, const char* arg2) + : + ReException() +{ + ReVarArgs args(message); + args.arg(arg1); + if (arg2 != NULL) + args.arg(arg2); + setMessage(args.asCString()); + if (caller != NULL) + caller->setLastError(args.asCString()); +} + +/** @brief Constructor. + * + * @param usage A string array with the description of the usage. + * Every string will be issued in a separate line. + * @param examples A string with one ore more calling examples. +* Every string will be issued in a separate line. +*/ +ReProgramArgs::ReProgramArgs(const char* usageList[], const char* examples[]) + : + m_usage(), + m_examples(), + m_properties(), + m_values(), + m_args(NULL), + m_argCount(0), + m_lastError() +{ + for (const char** argv = usageList; *argv != NULL; argv++){ + m_usage.add(-1, *argv, -1); + } + if (examples != NULL){ + for (const char** argv = examples; *argv != NULL; argv++){ + if (strncmp(*argv, "$0", 2) != 0) + m_examples.append(*argv); + else{ + ReByteBuffer line; + line.append(m_program, -1); + m_examples.append(line.str() + 2); + } + } + } +} +/** @brief Constructor. + * + * @param usage A string with the description of the usage. + * It may contain '\\n' for separate lines. + * @param examples A string with one ore more calling examples. + * It may contain '\\n' for separate lines. +*/ +ReProgramArgs::ReProgramArgs(const char* usageString, const char* examples) + : + m_usage(), + m_examples(), + m_properties(), + m_values(), + m_args(NULL), + m_argCount(0), + m_lastError() +{ + m_usage.split(usageString, '\n'); + if (examples != NULL){ + if (strstr(examples, "$0") != NULL) + m_examples.split(examples, '\n'); + else{ + ReByteBuffer line; + line.append(examples, -1); + line.replaceAll("$0", 2, m_program, -1); + m_examples.split(line.str(), '\n'); + } + + } +} +/** @brief Destructor. + */ +ReProgramArgs::~ReProgramArgs() { +} + +/** @brief Puts the property infos into the property string. + * + * The property string is a string stored in the hashlist. + * It contains all infos about the option but the current value. + * + * @param name The name of the option. Used in the methods getInt(), ... + * @param description A short description of the option. Used in the user messages. + * @param shortOpt The one character option identifier. Used in the arguments. Must be preceded by '-'. + * @param longOpt The multi character option identifier. Used in the arguments. Must be preceded by '--'. + * @param dataType The data type of the option: DT_INT, DT_BOOL ... + * @param defaultValue The default value of the option. + * @param lengthValue The length of defaultValue. + */ +void ReProgramArgs::addProperties(const char*name, const char* description, char shortOpt, + const char* longOpt, DataType dataType, const char* defaultValue, size_t lengthValue){ + ReByteBuffer properties; + properties.append(description, strlen(description)).append("\1", 1); + properties.append(&shortOpt, 1).append("\1", 1); + properties.append(longOpt, strlen(longOpt)).append("\1", 1); + properties.append((char*) &dataType, 1).append("\1", 1); + properties.append(defaultValue, -1).append("\1", 1); + m_properties.put(name, properties.str()); + + // Mark current value as default: + properties.set("!", 1); + // Copy default value as current value: + properties.append(defaultValue, -1); + m_values.put(name, properties.str()); +} +static const int IxDescr = 0; +static const int IxShort = 1; +static const int IxLong = 2; +static const int IxType = 3; +static const int IxDefault = 4; + +/** @brief Adds an option with an integer value. + * + * @param name The name of the option. Used in the methods getInt(). + * @param description A short description of the option. Used in the user messages. + * @param shortOpt The one character option identifier. Used in the arguments. Must be preceded by '-'. + * @param longOpt The multi character option identifier. Used in the arguments. Must be preceded by '--'. + * @param defaultValue The default value of the option. + * + * * @see getInt() + */ +void ReProgramArgs::addInt(const char* name, const char* description, + char shortOpt, const char* longOpt, int defaultVal){ + ReByteBuffer number; + number.appendInt(defaultVal); + addProperties(name, description, shortOpt, longOpt, DT_INT, + number.str(), number.getLength()); +} + +/** @brief Adds an option with a boolean value. + * + * @param name The name of the option. Used in the methods getBool(). + * @param description A short description of the option. Used in the user messages. + * @param shortOpt The one character option identifier. Used in the arguments. Must be preceded by '-'. + * @param longOpt The multi character option identifier. Used in the arguments. Must be preceded by '--'. + * @param defaultValue The default value of the option. + * + * @see getBool() + */ +void ReProgramArgs::addBool(const char* name, const char* description, + char shortOpt, const char* longOpt, bool defaultVal){ + addProperties(name, description, shortOpt, longOpt, DT_BOOL, + defaultVal ? "t" : "f", 1); +} + +/** @brief Adds an option with a string value. + * + * @param name The name of the option. Used in the methods getString(). + * @param description A short description of the option. Used in the user messages. + * @param shortOpt The one character option identifier. Used in the arguments. Must be preceded by '-'. + * @param longOpt The multi character option identifier. Used in the arguments. Must be preceded by '--'. + * @param defaultValue The default value of the option. + * + * @see getString() + */ +void ReProgramArgs::addString(const char* name, const char* description, + char shortOpt, const char* longOpt, bool mayBeEmpty, const char* defaultVal){ + addProperties(name, description, shortOpt, longOpt, + mayBeEmpty ? DT_STRING_EMPTY : DT_STRING, + defaultVal, strlen(defaultVal)); +} + +/** @brief Returns the value of a boolean option. + * + * @param name Name of the option. + * + * @return The value of the option set in the programs arguments or the default value. + * + * @throws ReOptionException Unknown name or wrong type. + */ +bool ReProgramArgs::getBool(const char* name) { + ReStringList properties; + ReByteBuffer buffer; + ReVarArgs args; + if (! m_properties.get(name, -1, buffer)) + ReOptionException(this, i18n("$1 is not an option name"), name); + + properties.split(buffer.str(), '\1'); + if (properties.getCStr(IxType)[0] != 'b') + ReOptionException(this, i18n("$1 is not an boolean option. Type is $2"), name, + properties.getCStr(IxType)); + + m_values.get(name, -1, buffer); + bool rc = buffer.at(1) == 't'; + return rc; +} + +/** @brief Returns the value of an integer option. + * + * @param name Name of the option. + * + * @return The value of the option set in the programs arguments or the default value. + * + * @throws ReOptionException Unknown name or wrong type. + */ +int ReProgramArgs::getInt(const char* name) { + ReStringList properties; + ReByteBuffer buffer; + ReVarArgs args; + if (! m_properties.get(name, -1, buffer)) + ReOptionException(this, i18n("$1 is not an option name"), name); + + properties.split(buffer.str(), '\1'); + if (properties.getCStr(IxType)[0] != DT_INT) + ReOptionException(this, i18n("$1 is not an integer option. Type is $2"), name, + properties.getCStr(IxType)); + + m_values.get(name, -1, buffer); + int rc = buffer.atoi(1); + return rc; +} + +/** @brief Returns the value of a string option. + * + * @param name Name of the option. + * + * @return The value of the option set in the programs arguments or the default value. + * + * @throws ReOptionException Unknown name or wrong type. + */ +const char* ReProgramArgs::getString(const char* name, ReByteBuffer& buffer) { + ReStringList properties; + ReVarArgs args; + if (! m_properties.get(name, -1, buffer)) + ReOptionException(this, i18n("$1 is not an option name"), name); + + properties.split(buffer.str(), '\1'); + DataType dataType = (DataType) properties.getCStr(IxType)[0]; + if (dataType != DT_STRING && dataType != DT_STRING_EMPTY) + ReOptionException(this, i18n("$1 is not a string option. Type is $2"), name, + properties.getCStr(IxType)); + + m_values.get(name, -1, buffer); + const char* rc = buffer.getBuffer() + 1; + return rc; +} + +/** @brief Returns the count of arguments (without options). + * + * @return The count of arguments. + */ +int ReProgramArgs::getArgCount() const{ + return m_argCount; +} + +/** @brief Returns a not option argument given by an index. + * + * @param index The index of the wanted program argument which is not an option. + * + * @return NULL: Wrong index. Otherwise: The wanted argument. + */ +const char* ReProgramArgs::getArg(size_t index) const{ + const char* rc = NULL; + + if (index < (size_t) m_argCount) + rc = m_args[index]; + return rc; +} +/** @brief Returns the program name. + * + * @return The name of the application. + */ +const char* ReProgramArgs::getProgramName() const{ + return m_program; +} + +/** @brief Search the property string of an option. + * + * @param shortName The option`s short name. Not relevant if longName != NULL. + * @param LongName The option`s long name. Not relevant if longName == NULL. + * @param name Out: The name of the option. + * @param list Out: The properties are returned in this list. + * + * @throws ReOptionException Unknown option. + */ +void ReProgramArgs::search(char shortName, const char* longName, + ReByteBuffer& name, ReStringList& list){ + size_t position = 0; + ReByteBuffer properties; + bool found = false; + size_t lengthLongName = 0; + if (longName != NULL){ + const char* ptr; + if ( (ptr = strchr(longName, '=')) != NULL) + lengthLongName = ptr - longName; + else + lengthLongName = strlen(longName); + } + while (! found && m_properties.next(position, &name, &properties)){ + list.split(properties.str(), '\1'); + if (longName == NULL && shortName == list.getCStr(IxShort)[0]) + found = true; + else if (lengthLongName > 0 && list.getLength(IxLong) == lengthLongName + 1 + && strncmp(longName, list.getCStr(IxLong), lengthLongName) == 0) + found = true; + } + if (! found){ + if (longName == NULL) + name.set(&shortName, 1); + else + name.set(longName, lengthLongName); + ReOptionException(this, i18n("Unknown option: $1"), name.str()); + } +} +/** @brief Sets the option value. + * + * @param name The option's name. + * @param value The option's value. + * @param dataType Theo option's data type. + */ +void ReProgramArgs::setValue(const char* name, const char* value, const char* dataType){ + switch(dataType[0]){ + case DT_INT: + if (strspn(value, "01234567890") != strlen(value)) + ReOptionException(this, i18n("Option $1 expect an integer as parameter, not $2"), + name, value); + break; + case DT_STRING: + if (value[0] == '\0') + ReOptionException(this, i18n("Option $1: Empty parameter is not allowed"), name); + break; + case DT_STRING_EMPTY: + case DT_BOOL: + default: + break; + } + ReByteBuffer buffer; + // First character says: defined. + buffer.append(" ", 1).append(value, -1); + m_values.put(name, buffer.str()); +} +/** @brief Analyses one or more short name options. + * + * Multiple short name options can be written in one word: + *

Example: -x -y -z can be noted as -xyz

+ *

On the other side an option with parameter can be written in two forms:

+ *
    + *
  • -xABC
  • + *
  • -x ABC
  • + *
+ * + * @param opt An option string. + * @param nextArg The next argument behind the current option string. + * May be NULL (no more arguments). + * + * @return true a second word has been used: It was a parameter of an string or integer option. + * false: The next argument has not been used. + */ +bool ReProgramArgs::analyseShort(const char* opt, const char* nextArg){ + bool rc = false; + ReStringList properties; + bool again; + ReByteBuffer name; + do { + again = false; + + search(opt[0], NULL, name, properties); + const char* dataType = properties.getCStr(IxType); + const char* nameStr = name.str(); + // Forget the option short name: + opt++; + switch (dataType[0]){ + case DT_INT: + case DT_STRING: + case DT_STRING_EMPTY: + if (opt[0] != '\0'){ + setValue(nameStr, opt, dataType); + } else { + if (nextArg == NULL || nextArg[0] == '-'){ + if (dataType[0] == DT_STRING_EMPTY) + setValue(nameStr, "", dataType); + else + ReOptionException(this, i18n("Option $1 has type $2! There is no parameter."), + nameStr, dataType); + } else { + setValue(nameStr, nextArg, dataType); + rc = true; + } + } + break; + case DT_BOOL: + { + // Get the current value: + const char* value = "t"; + if (opt[0] == '-'){ + opt++; + value = "f"; + } else if (opt[0] == '+') + opt++; + // Invert the default value: + if (properties.getCStr(IxDefault)[0] == 't') + value = value[0] =='t' ? "f" : "t"; + setValue(nameStr, value, dataType); + again = opt[0] != '\0'; + break; + } + default: + break; + } + } while (again); + return rc; +} +/** @brief Analyses a long name option. + * + * The syntax of an long name option is --name or --name=value + * + * @param opt The option string without --. + * + */ +void ReProgramArgs::analyseLong(const char* opt){ + ReStringList properties; + ReByteBuffer name; + search('\0', opt, name, properties); + + const char* nameStr = name.str(); + const char* dataType = properties.getCStr(IxType); + const char* value = strchr(opt, '='); + if (value != NULL) + value++; + + switch(dataType[0]){ + case DT_INT: + if (value == NULL) + ReOptionException(this, i18n("Option $1: parameter expected. Use --$2=number"), + nameStr, nameStr); + else + setValue(nameStr, value, dataType); + break; + case DT_STRING: + if (value == NULL) + ReOptionException(this, i18n("Option $1: parameter expected. Use --$2=string"), + nameStr, nameStr); + setValue(nameStr, value, dataType); + break; + case DT_STRING_EMPTY: + if (value == NULL) + value = ""; + setValue(nameStr, value, dataType); + break; + case DT_BOOL: + { + const char* boolValue = "f"; + if (value == NULL || ReStringUtils::isInList(value, + ReConfigFile::m_trueValues, true, ReStringUtils::AUTO_SEPARATOR)) + boolValue = "t"; + else if (! ReStringUtils::isInList(value, ReConfigFile::m_falseValues, + true, ReStringUtils::AUTO_SEPARATOR)) + ReOptionException(this, i18n("Option $1: Not a boolean value: $2. Use true or false"), + nameStr, value); + // Invert the default value: + if (properties.getCStr(IxDefault)[0] == 't') + boolValue = boolValue[0] =='t' ? "f" : "t"; + setValue(nameStr, boolValue, dataType); + break; + } + default: + break; + } +} +/** @brief Initializes the options from the program arguments. + * + * While arguments are preceded by an '-' they will be treated as options. + * The rest of arguments are stored for retrieving with getArg(). + * + * @param argc The count of program arguments (inclusive options). + * @param argv The argument vector. + * + * @throws ReException + */ +void ReProgramArgs::init(int argc, char* argv[]){ + m_program = argv[0]; + argv++; + argc--; + + while (argc > 0 && argv[0][0] == '-'){ + if (argv[0][1] == '-') + analyseLong(argv[0] + 2); + else{ + if (analyseShort(argv[0] + 1, argc <= 1 ? NULL : argv[1])) + argc--, argv++; + } + argc--; + argv++; + } + m_argCount = argc; + m_args = (const char**) argv; +} +/** @brief Sets the last error message. + * + * @param message The error message. + */ +void ReProgramArgs::setLastError(const char* message){ + m_lastError.set(message, -1); +} + +void ReProgramArgs::help(const char* message, bool issueLastError, ReStringList& lines){ + lines.append(m_usage); + lines.append(""); + + size_t position = 0; + if (m_properties.next(position, NULL, NULL)){ + lines.append(i18n(":")); + } + ReByteBuffer name; + ReByteBuffer prop; + ReByteBuffer line; + ReByteBuffer param; + + while(m_properties.next(position, &name, &prop)){ + ReStringList properties; + properties.split(prop.str(), '\1'); + line.setLength(0); + DataType dataType = DataType(properties.getCStr(IxType)[0]); + const char* shortName = properties.getCStr(IxShort); + param.setLength(0); + switch(dataType){ + case DT_INT: + param.append(i18n(""), -1); + break; + case DT_STRING: + param.append(i18n(""), -1); + break; + case DT_STRING_EMPTY: + param.append(i18n("[]"), -1); + break; + default: + break; + } + if (shortName[0] != HIDDEN_SHORT_NAME){ + line.append("-", 1).append(shortName, 1); + line.append(param.str(), -1).append(" ", 1).append(i18n(" or "), -1); + } + line.append(i18n("--"), -1).append(properties.getCStr(IxLong), -1); + if (param.getLength() > 0) + line.append("=", -1).append(param.str(), -1) + .append(i18n(" Default value: "), -1).append(properties.getCStr(IxDefault), -1); + lines.append(line.str()); + line.set("\t", 1).append(properties.getCStr(IxDescr), -1); + lines.append(line.str()); + } + if (m_examples.getCount() > 0){ + lines.append(i18n("Example:")); + lines.append(m_examples); + } + if (issueLastError && m_lastError.getLength() > 0){ + line.set("+++ ", 4).append(m_lastError.str(), -1); + lines.append(line.str()); + } + + if (message != NULL){ + line.set("+++ ", 4).append(message, -1); + lines.append(line.str()); + } +} + +void ReProgramArgs::help(const char* message, bool issueLastError, FILE* stream){ + ReStringList lines; + help(message, issueLastError, lines); + for(size_t ii = 0; ii < lines.getCount(); ii++){ + fputs(lines.getCStr(ii), stream); + fputc('\n', stream); + } +} + +#if defined RE_TESTUNIT +class TestReProgramArgs : public ReTestUnit { +public: + TestReProgramArgs() + : + ReTestUnit("ReProgramArgs", __FILE__) + { + run(); + } +private: + void run(){ + testLong(); + testShort(); + } + void testShort(){ + ReProgramArgs args("test \nThis tests the usage of ReProgramArgs", + "$0 -b+ -B- file dir\n\ttest of an example"); + + args.addBool("boolarg", "This is a boolean arg", 'b', "boolval", false); + args.addBool("boolarg2", "This is the 2nd boolean arg", 'B', "boolval2", true); + args.addBool("boolarg3", "This is the 3rd boolean arg", 'x', "boolval3", false); + args.addBool("boolarg4", "This is the 4th boolean arg", 'Y', "boolval4", true); + args.addInt("intarg", "This is an integer arg", 'i', "intval", 9); + args.addInt("intarg2", "This is the 2nd integer arg", 'I', "intval", 1000); + args.addString("stringarg", "This string must be non empty", 's', "string", false, "abc"); + args.addString("stringarg2", "This 2nd string must be non empty", 'u', "string2", false, "undef"); + args.addString("estringarg", "This string may be empty", 'S', "estring", true, "empty"); + args.addString("estringarg2", "This 2nd string may be empty", 'U', "estring2", true, "undef2"); + + checkF(args.getBool("boolarg")); + checkEqu(9, args.getInt("intarg")); + ReByteBuffer buffer; + checkEqu("empty", args.getString("estringarg", buffer)); + checkEqu("abc", args.getString("stringarg", buffer)); + + const char* vector[] = { + "testprog", "-bB+i123", "-S", "-x-", "-Y+", "-s", "2nd string", "arg1", "arg2" + }; + args.init(sizeof vector / sizeof vector[0], (char**) vector); + + checkEqu("testprog", args.getProgramName()); + checkT(args.getBool("boolarg")); + checkF(args.getBool("boolarg2")); + checkF(args.getBool("boolarg3")); + checkF(args.getBool("boolarg4")); + checkEqu(123, args.getInt("intarg")); + checkEqu(1000, args.getInt("intarg2")); + checkEqu("", args.getString("estringarg", buffer)); + checkEqu("2nd string", args.getString("stringarg", buffer)); + checkEqu("undef", args.getString("stringarg2", buffer)); + checkEqu("undef2", args.getString("estringarg2", buffer)); + checkEqu("testprog", args.getProgramName()); + checkEqu("arg1", args.getArg(0)); + checkEqu("arg2", args.getArg(1)); + checkEqu(2, args.getArgCount()); + + args.help("Not really an error!", false, stdout); + } + void testLong(){ + const char* call[] = { + "test ", + "This tests the usage of ReProgramArgs", + NULL + }; + const char* examples[] = { "test -intval=10 --boolval=t", NULL}; + ReProgramArgs args(call, examples); + + args.addBool("boolarg", "This is a boolean arg", 'b', "boolval", false); + char none = ReProgramArgs::HIDDEN_SHORT_NAME; + args.addBool("boolarg2", "This is the 2nd boolean arg", none, "boolval2", true); + args.addBool("boolarg3", "This is the 3rd boolean arg", 'x', "boolval3", false); + args.addBool("boolarg4", "This is the 3rd boolean arg", none, "boolval4", true); + args.addInt("intarg", "This is an integer arg", 'i', "intval", 9); + args.addString("stringarg", "This string must be non empty", 's', "string", false, "abc"); + args.addString("estringarg", "This string may be empty", none, "estring", true, "empty"); + args.addString("estringarg2", "This 2nd string may be empty", 'U', "estring2", true, "undef2"); + args.addString("estringarg3", "This 3thrd string may be empty", 'V', "estring3", true, "undef3"); + + ReByteBuffer buffer; + const char* vector[] = { + "testprog", + "--boolval", "--boolval2=true", "--boolval3=f", "--boolval4=0", + "--intval=3", + "--string=x y", "--estring=", "--estring2=not empty", + "arg1", "arg2" + }; + args.init(sizeof vector / sizeof vector[0], (char**) vector); + + checkEqu("testprog", args.getProgramName()); + checkT(args.getBool("boolarg")); + checkF(args.getBool("boolarg2")); + checkF(args.getBool("boolarg3")); + checkT(args.getBool("boolarg4")); + checkEqu(3, args.getInt("intarg")); + checkEqu("x y", args.getString("stringarg", buffer)); + checkEqu("", args.getString("estringarg", buffer)); + checkEqu("not empty", args.getString("estringarg2", buffer)); + checkEqu("arg1", args.getArg(0)); + checkEqu("arg2", args.getArg(1)); + checkEqu(2, args.getArgCount()); + args.help(NULL, false, stdout); + } +}; +extern void testReProgramArgs(void); + +void testReProgramArgs(void){ + TestReProgramArgs unit; +} +#endif /*RE_TESTUNIT*/ + + diff --git a/base/ReProgramArgs.hpp b/base/ReProgramArgs.hpp new file mode 100644 index 0000000..a07b08e --- /dev/null +++ b/base/ReProgramArgs.hpp @@ -0,0 +1,94 @@ +/* + * ReProgramArgs.h + * + * Created on: 25.05.2010 + * Author: wk + */ + +#ifndef REPROGRAMARGS_H_ +#define REPROGRAMARGS_H_ + +class ReProgramArgs; +/** All errors will reported by this exception. + */ +class ReOptionException : public ReException{ +public: + ReOptionException(ReProgramArgs* caller, const char* message, + const char* arg1, const char* arg2 = NULL); +}; + +/** + * This class analyses the program arguments and give an interface for retrieving them. + * + * Program arguments contains the program name, possibly options and "true" arguments. + *

Options are short name options or long name options.

+ *

A short name option is preceded by a single dash ('-'), a long name option starts with two dashes: (--).

+ *

There are three types of options: boolean, integer and string.

+ *

Every option must have an default value.

+ *

A boolean option has normally a default value false. If it appears in the arguments + * it will be have the value true.

+ *

An integer or string option can be followed by an integer or string value:
+ * Short name option: -x value or -xvalue
+ * Long name option: -xxx=value

+ *

The program must contain a definition of the options: addInt(), addBool() and/or addString(), + * the analyse (init()) and the retrieval (getInt(), getBool() and/or getString().

+ *

The connection between definition and retrieval are names.

+ */ +class ReProgramArgs { +public: + enum DataType { + DT_UNDEF = 0, + DT_INT = 'i', + DT_BOOL = 'b', + DT_STRING = 's', + DT_STRING_EMPTY = 'S' + }; + enum { + HIDDEN_SHORT_NAME = 2 + }; +public: + ReProgramArgs(const char* usageList[], const char* examples[] = NULL); + ReProgramArgs(const char* usageString, const char* examples = NULL); + virtual ~ReProgramArgs(); +public: + void addInt(const char* name, const char* description, + char shortOpt, const char* longOpt, int defaultVal); + void addBool(const char* name, const char* description, + char shortOpt, const char* longOpt, bool defaultVal); + void addString(const char* name, const char* description, + char shortOpt, const char* longOpt, bool mayBeEmpty, + const char* defaultVal); + + bool getBool(const char* name); + int getInt(const char* name); + const char* getString(const char* name, ReByteBuffer& buffer); + + int getArgCount() const; + const char* getArg(size_t index) const; + const char* getProgramName() const; + + void init(int argc, char* argv[]); + + void setLastError(const char* message); + void help(const char* message, bool issueLastError, ReStringList& lines); + void help(const char* message, bool issueLastError, FILE* stream); + +private: + void addProperties(const char*name, const char* description, char shortOpt, + const char* longOpt, DataType dataType, const char* defaultValue, size_t lengthValue); + void search(char shortName, const char* longName, ReByteBuffer& name, ReStringList& list); + void setValue(const char* name, const char* value, const char* dataType); + bool analyseShort(const char* opt, const char* nextArg); + void analyseLong(const char* opt); +private: + ReStringList m_usage; + ReStringList m_examples; + ReHashList m_properties; + ReHashList m_values; + const char** m_args; + int m_argCount; + const char* m_program; + ReByteBuffer m_lastError; +}; + +#endif /* REPROGRAMARGS_H_ */ diff --git a/base/ReSeqList.cpp b/base/ReSeqList.cpp new file mode 100644 index 0000000..4d5625a --- /dev/null +++ b/base/ReSeqList.cpp @@ -0,0 +1,248 @@ +/* + * ReSeqList.cpp + * + * Created on: 19.05.2010 + * Author: wk + */ + +#include "rebase.hpp" + +/** @brief Constructor. + * + * @param deltaList If there is not enough space in the list (array) + * this amount is the minimum to reserve. + * @param deltaList If there is not enough space in the content buffer + * this amount of bytes is the minimum to reserve. + */ +ReSeqList::ReSeqList(size_t deltaList, int deltaBuffer) + : + m_content(deltaBuffer), + m_list(deltaList), + m_lost(0) +{ +} +/** @brief Destructor. + */ +ReSeqList::~ReSeqList() { +} +/** @brief Copy constructor. + * + * @param source This instance will be copied. + */ +ReSeqList::ReSeqList(const ReSeqList& source) + : + m_content(source.m_content), + m_list(source.m_list), + m_lost(source.m_lost) +{ + +} +/** @brief Assignment operator. + * + * @param source This instance will be copied. + * + * @result The instance itself. + */ +ReSeqList& ReSeqList::operator = (const ReSeqList& source){ + m_content = source.m_content; + m_list = source.m_list; + m_lost = source.m_lost; + return *this; +} +/** @brief Adds a byte sequence to the list. + * + * @param index The index of the new entry. If greater than the list length it will be appended. + * @param source The pointer of the byte sequence to insert. + * @param sourceLength The length of the byte sequence to insert. + * @param tag An item stored with the byte sequence. We know nothing about this. + */ +void ReSeqList::add(Index index, const Byte* source, size_t sourceLength, Tag tag){ + Sequence seq; + if (sourceLength == (size_t) -1) + sourceLength = strlen(source) + 1; + seq.m_index = m_content.getLength(); + seq.m_length = sourceLength; + seq.m_tag = tag; + m_content.append(source, sourceLength); + if (index >= getCount()){ + m_list.append((Byte*) &seq, sizeof seq); + }else{ + m_list.insert(index * sizeof(Sequence), (Byte*) &seq, sizeof seq); + } +} +/** @brief Returns a byte sequence from the list. + * + * @param index The index of the sequence in the list. + * @param value Out: The stored sequence will be copied here. + * @param tag Out: The info which is stored with the entry. May be NULL. + * + * @param true: The index is ok, the sequence is in the output buffer. + * false: No copy has been done. + */ +bool ReSeqList::get(Index index, ReByteBuffer& value, Tag* tag) const{ + bool rc = false; + if (index < getCount()){ + Sequence* seq = ((Sequence*)m_list.getBuffer()) + index; + value.set(m_content.getBuffer() + seq->m_index, seq->m_length); + if (tag != NULL) + *tag = seq->m_tag; + rc = true; + } + return rc; +} +/** @brief Replaces the byte sequence in the list. + * + * @param index The index of the sequence to replace. + * @param source The new value. + * @param sourceLength The length of the new value. + * @param tag An additional info associated to the source. + */ +void ReSeqList::set(Index index, const Byte* source, size_t sourceLength, Tag tag){ + if (index >= getCount()) + add(index, source, sourceLength, tag); + else { + if (sourceLength == (size_t) -1) + sourceLength = strlen(source) + 1; + Sequence* seq = getInfo(index); + seq->m_tag = tag; + if (seq->m_length >= sourceLength){ + // Use the existing space: + memcpy(m_content.getBuffer() + seq->m_index, source, sourceLength); + m_lost += seq->m_length - sourceLength; + } else { + // New space must be allocated: + m_lost += seq->m_length; + seq->m_index = m_content.getLength(); + m_content.append(source, sourceLength); + } + } +} +/** @brief Removes an element given by its index. + * + * @param index The index of the entry to remove. + */ +void ReSeqList::remove(Index index){ + if (index <= getCount()){ + Sequence* seq = getInfo(index); + // Is this the last entry in m_content? + if (seq->m_index + seq->m_length >= m_content.getLength()){ + // We can free the content: + m_content.setLength(seq->m_index); + } else { + m_lost += seq->m_length; + } + // Remove the entry from the list: + m_list.remove(index * sizeof (Sequence), sizeof (Sequence)); + } +} + +/** @brief Deletes all entries in the list. + */ +void ReSeqList::clear(){ + m_content.setLength(0); + m_list.setLength(0); +} + +#if defined RE_TESTUNIT +class TestReSeqList : public ReTestUnit { +public: + TestReSeqList() : ReTestUnit("ReSeqList", __FILE__){ + run(); + } +private: + void run(){ + testBase(); + testRemove(); + } + void testBase(){ + ReSeqList list; + ReByteBuffer value; + ReSeqList::Tag tag = 0; + + list.add(-1, "123", -1, 100); + checkEqu(1, list.getCount()); + checkT(list.get(0, value, &tag)); + checkEqu("123", value.str()); + checkEqu(100, tag); + + list.add(-1, "ab", -1, 200); + checkEqu(2, list.getCount()); + checkT(list.get(0, value)); + checkEqu("123", value.str()); + checkT(list.get(1, value, &tag)); + checkEqu("ab", value.str()); + checkEqu(200, tag); + + list.add(0, "xyz", -1, 300); + checkEqu(3, list.getCount()); + checkT(list.get(0, value, &tag)); + checkEqu("xyz", value.str()); + checkT(list.get(1, value)); + checkEqu("123", value.str()); + checkT(list.get(2, value)); + checkEqu("ab", value.str()); + checkEqu(300, tag); + + list.add(1, "vw", -1, 400); + checkEqu(4, list.getCount()); + checkT(list.get(0, value)); + checkEqu("xyz", value.str()); + checkT(list.get(1, value, &tag)); + checkEqu("vw", value.str()); + checkT(list.get(2, value)); + checkEqu("123", value.str()); + checkT(list.get(3, value)); + checkEqu("ab", value.str()); + checkEqu(400, tag); + + list.clear(); + checkEqu(0, list.getCount()); + checkF(list.get(0, value)); + } + void testRemove(){ + ReSeqList list; + ReByteBuffer value; + ReSeqList::Tag tag = 0; + + list.add(-1, "abc", -1, 100); + list.add(-1, "def12", -1, 200); + list.add(-1, "ghi", -1, 300); + list.add(-1, "jkl134", -1, 400); + + list.remove(3); + checkEqu(3, list.getCount()); + list.get(0, value, &tag); + checkEqu("abc", value.str()); + checkEqu(100, tag); + list.get(1, value, &tag); + checkEqu("def12", value.str()); + checkEqu(200, tag); + list.get(2, value, &tag); + checkEqu("ghi", value.str()); + checkEqu(300, tag); + + + list.remove(1); + checkEqu(2, list.getCount()); + list.get(0, value, &tag); + checkEqu("abc", value.str()); + checkEqu(100, tag); + list.get(1, value, &tag); + checkEqu("ghi", value.str()); + checkEqu(300, tag); + + list.remove(0); + checkEqu(1, list.getCount()); + list.get(0, value, &tag); + checkEqu("ghi", value.str()); + checkEqu(300, tag); + + } +}; +extern void testReSeqList(void); + +void testReSeqList(void){ + TestReSeqList unit; +} +#endif /*RE_TESTUNIT*/ + diff --git a/base/ReSeqList.hpp b/base/ReSeqList.hpp new file mode 100644 index 0000000..34c938f --- /dev/null +++ b/base/ReSeqList.hpp @@ -0,0 +1,78 @@ +/* + * ReSeqList.h + * + * Created on: 19.05.2010 + * Author: wk + */ + +#ifndef RESEQLIST_H_ +#define RESEQLIST_H_ + +/** @brief This class implements a dynamic (selfgrowing) array of byte sequences. + *

A byte sequence is an array of byte. + * The byte sequences may have different lengths. + * This implies the handling of C string arrays too. + *

There is room for storing a so called tag with any sequence. + * This can be used for any purpose. + *

+ *

This is a very simple implementation: use it when: + *

  • The needed size is known nearly.
  • + *
  • The array elements increment rarely their length.
  • + *
+ *

+ */ +class ReSeqList { +public: + typedef char Byte; + typedef unsigned int Index; + typedef long int Tag; + typedef struct { + Index m_index; + size_t m_length; + Tag m_tag; + } Sequence; +public: + ReSeqList(size_t deltaList = 128, int deltaBuffer = 1024); + virtual ~ReSeqList(); + ReSeqList(const ReSeqList& source); + ReSeqList& operator = (const ReSeqList& source); +public: + void clear(); + void add(Index index, const Byte* source, size_t sourceLength, Tag tag = 0); + bool get(Index index, ReByteBuffer& value, Tag* tag = NULL) const; + void remove(Index index); + void set(Index index, const Byte* source, size_t sourceLength, Tag tag); + /** @brief Returns the count of defined entries in the list. + * @return The number of defined entries in the list (array). + */ + inline Index getCount() const { + return m_list.getLength() / sizeof (Sequence); + } +protected: + /** @brief Returns a pointer of the content buffer. + * @return A pointer of the first byte of the content buffer. + */ + inline const Byte* getContent() const { + return m_content.getBuffer(); + } + friend class ReHashList; + /** @brief Returns the info of an entry of the list. + * @param index The index of the wanted entry. + * @return The pointer of the entry. + */ + inline Sequence* getInfo(Index index) const { + return &((Sequence*) m_list.getBuffer())[index]; + } + +protected: + // friend class ReSeqList; + //@ Contains the sequences itself. + ReByteBuffer m_content; + //@ Contains an array of Sequences. + ReByteBuffer m_list; + //@ If strings have been replaced the space in m_content is still allocated. + //@ This is the sum of lost space. + size_t m_lost; +}; + +#endif /* RESEQLIST_H_ */ diff --git a/base/ReStringList.cpp b/base/ReStringList.cpp new file mode 100644 index 0000000..6d4930f --- /dev/null +++ b/base/ReStringList.cpp @@ -0,0 +1,597 @@ +/* + * ReStringList.cpp + * + * Created on: 20.05.2010 + * Author: wk + */ + +#include "../base/restring.hpp" + +/** @brief Constructor. + */ +ReStringList::ReStringList() + : + ReSeqList() +{ + +} + +/** @brief Destructor. + */ +ReStringList::~ReStringList() { +} +/** @brief Appends a string at the end. + * + * @param source The new string. + * @param tag An item which will stored with the string. It can be retrieved by the same index. + * T his class knows nothing about this. + */ +void ReStringList::append(const char* source, Tag tag){ + add(-1, source, -1, tag); +} +/** @brief Appends a stringlist at the end. + * + * @param source The new stringlist. + */ +void ReStringList::append(ReStringList& source){ + for (size_t ii = 0; ii < source.getCount(); ii++) + add(-1, source.getCStr(ii), -1, source.getTag(ii)); +} +/** @brief Inserts a string at a given index. + * + * If the index exceeds the length of the array it will be appended. + * + * @param source The new string. + * @param tag An item which will stored with the string. It can be retrieved by the same index. + * This class knows nothing about this. + */ +void ReStringList::insert(Index index, const char* source, Tag tag){ + add(index, source, -1, tag); +} +/** @brief Replaces an element in the internal array: a string and a tag. + * + * @param index The element with this index will be replaced. + * @param source The new string of the replaced element. + * @param tag The tag of the replace element. + */ +void ReStringList::replace(Index index, const char* source, Tag tag){ + set(index, source, -1, tag); +} +/** @brief Replaces a string in the internal array. + * + * The tag of the element remains unchanged. + * + * @param index The element with this index will be replaced. + * @param source The new string of the replaced element. + */ +void ReStringList::replaceString(Index index, const char* source){ + if (index < getCount()){ + Sequence* seq = getInfo(index); + set(index, source, -1, seq->m_tag); + } +} +/** @brief Replaces a tag in the internal array. + * + * The string of the element remains unchanged. + * + * @param index The element with this index will be replaced. + * @param source The new string of the replaced element. + */ +void ReStringList::replaceTag(Index index, Tag tag){ + if (index < getCount()){ + Sequence* seq = getInfo(index); + seq->m_tag = tag; + } +} + +/** @brief Returns the C string given by an index. + * + * @param index The index of the wanted string. + * + * @return NULL: The index is too large. + * Otherwise: The wanted string. + */ +const char* ReStringList::getCStr(Index index) const{ + const char* rc = NULL; + if (index < getCount()){ + Sequence* seq = getInfo(index); + rc = m_content.getBuffer() + seq->m_index; + } + return rc; +} +/** @brief Returns the tag given by an index. + * + * A tag is an additional info stored with the string. + * + * @param index The index of the wanted tag. + * + * @return -1: The index is too large. + * Otherwise: The wanted tag. + */ +ReSeqList::Tag ReStringList::getTag(Index index) const{ + Tag rc = -1; + if (index < getCount()){ + Sequence* seq = getInfo(index); + rc = seq->m_tag; + } + return rc; +} +/** @brief Returns the length of the string given by an index. + * + * @param index The index of the wanted string length. + * + * @return 0: The index is too large. + * Otherwise: The length of the index-th string. + */ +size_t ReStringList::ReStringList::getLength(Index index) const{ + size_t rc = 0; + if (index < getCount()){ + Sequence* seq = getInfo(index); + rc = seq->m_length; + } + return rc; +} +/** @brief Returns the sum of all string lengths stored in the array. + * + * @return The sum of all string lengths stored in the array. + */ +size_t ReStringList::sumOfLength() const{ + size_t rc = 0; + + for (int ii = getCount() - 1; ii >= 0; ii--){ + Sequence* seq = getInfo(ii); + rc += seq->m_length; + } + return rc; +} +/** @brief Returns the index of a given string in the array. + * + * @param toFind The string which will be searched. + * @param ignoreCase true: The search is case insensitive. + * false: The search is case sensitive. + * @param start The search starts at this index. + * + * @return -1: The string was not found. Otherwise: The index of the string. + */ +ReSeqList::Index ReStringList::indexOf(const char* toFind, + bool ignoreCase, Index start) const{ + Index rc = (Index) -1; + Index count = getCount(); + + for (; rc == (size_t) -1 && start < count; start++){ + const char* item = getCStr(start); + int rc2; + if (ignoreCase) + rc2 = strcasecmp(item, toFind); + else + rc2 = strcmp(item, toFind); + if (rc2 == 0) + rc = start; + } + return rc; +} +/** Returns the index of the next string starting with a given substring. + * + * @param start The search starts at this index. + * @param prefix The substring which will be searched. + * @param ignoreCase true: The search is case insensitive. + * false: The search is case sensitive. + * + * @return -1: The string was not found. Otherwise: The index of the string. + */ +ReSeqList::Index ReStringList::nextStartingWith(Index start, + const char* prefix, bool ignoreCase){ + Index rc = (Index) -1; + Index count = getCount(); + size_t length = strlen(prefix); + + for (; rc == (size_t) -1 && start < count; start++){ + const char* item = getCStr(start); + int rc2; + if (ignoreCase) + rc2 = strncasecmp(item, prefix, length); + else + rc2 = strncmp(item, prefix, length); + if (rc2 == 0) + rc = start; + } + return rc; +} +/** @brief Splits a string in an array. + * + * @param list The string which is splitted. + * @param separator The separator of the substrings. + * If '\\n' a preceeding or trailing '\\r' will be ignored too. + * @param append false: The list will be cleared at the beginning. + * true: The new content is stored at the end. + */ +void ReStringList::split(const char* list, char separator, bool append){ + if (! append) + clear(); + const char* end = strchr(list, separator); + const char* end2; + ReByteBuffer item; + while(end != NULL){ + if (separator == '\n' && end != list && end[-1] == '\r') + end2 = end - 1; + else + end2 = end; + item.setLength(0); + size_t length = end2 - list; + item.append(list, length); + // Append '\0': + item.append("", 1); + add(-1, item.getBuffer(), length + 1); + list = end + 1; + if (separator == '\n' && list[0] == '\r') + list++; + end = strchr(list, separator); + } + if (list[0] != '\0') + add(-1, list, -1); +} +/** @brief Joins all string of the array into a string. + * + * @param separator This string was put between the substrings. May be NULL or "". + * @param result Out: The result buffer. + */ +void ReStringList::join(const char* separator, ReByteBuffer& result){ + size_t count = getCount(); + + result.setLength(0); + size_t lengthSep = strlen(separator); + + for (size_t ix = 0; ix < count; ix++){ + result.append(getCStr(ix), getLength(ix) - 1); + if (ix != count - 1 && separator != NULL) + result.append(separator, lengthSep); + } +} +/** @brief Writes the stringlist to a file. + * + * @param filename The name of the file. + * @param separator This string was put between the substrings. May be NULL. + * @param mode The file open mode: "w" for truncate and write, "a" for append. + * + * @return true: The file could be opened. false: otherwise. + */ +bool ReStringList::writeToFile(const char* filename, + const char* separator, const char* mode){ + bool rc = false; + FILE* fp = fopen(filename, mode); + if (fp){ + size_t count = getCount(); + for (size_t ix = 0; ix < count; ix++){ + fputs(getCStr(ix), fp); + if (ix != count - 1 && separator != NULL) + fputs(separator, fp); + } + fclose(fp); + rc = true; + } + return rc; +} +/** @brief Reads a file into the array. + * + * Every line is stored as entry of the array. + * + * @param filename The name of the file. + * @param cutNewline true: The newline characters will be cut. + * false: The newline characters will be stored. + * + * @return true: The file could be opened. false: otherwise. + */ +bool ReStringList::readFromFile(const char* filename, bool cutNewline){ + FILE* fp = fopen(filename, "r"); + bool rc = false; + if (fp != NULL){ + char line[8096]; + + while(fgets(line, sizeof line, fp) != NULL){ + size_t length = strlen(line); + if (cutNewline){ + while(--length > 0 && (line[length] == '\n' || line[length] == '\r')) + line[length] = '\n'; + } + add(-1, line, length + 1); + } + } + return rc; +} +/** @brief Returns the index of the first different string. + * + * Compares the internal array of strings with another instance. + * + * @param toCompare The other instance which will be compared. + * + * @return -1: The instances are equal. Otherwise: The index of the first different string. + * + */ +int ReStringList::firstDiff(const ReStringList& toCompare) const{ + int rc = -1; + for (size_t ix = 0; rc == -1 && ix < getCount(); ix++){ + if (ix >= toCompare.getCount()) + rc = (int) ix; + else if (getLength(ix) != toCompare.getLength(ix) + || strcmp(getCStr(ix), toCompare.getCStr(ix)) != 0) + rc = (int) ix; + } + if (rc == -1 && getCount() < toCompare.getCount()) + rc = getCount(); + return rc; +} +/** @brief Tests the equality with another instance. + * + * Compares the internal array of strings with another instance. + * Two instances are equal when the number of strings are equal + * and the n.th string is equal to the n.th string in the other instance. + * + * @param toCompare The other instance which will be compared. + * + * @return true: The other instance is equal. false: Otherwise. + */ +bool ReStringList::equal(const ReStringList& toCompare) const{ + bool rc = getCount() == toCompare.getCount() && firstDiff(toCompare) == -1; + return rc; +} + +#if defined RE_TESTUNIT +class TestReStringList : public ReTestUnit { +public: + TestReStringList() : ReTestUnit("ReStringList", __FILE__){ + run(); + } +private: + void run(){ + testBase(); + testReplace(); + testJoin(); + testEqu(); + testFile(); + } + void testReplace(){ + ReStringList list; + + list.append("123", 100); + checkEqu(100, list.getTag(0)); + list.append("2", 200); + checkEqu(100, list.getTag(0)); + checkEqu(200, list.getTag(1)); + list.append("34", 300); + checkEqu(100, list.getTag(0)); + checkEqu(200, list.getTag(1)); + checkEqu(300, list.getTag(2)); + + list.replace(0, "1", 111); + checkEqu("1", list.getCStr(0)); + checkEqu(111, list.getTag(0)); + checkEqu(200, list.getTag(1)); + checkEqu(300, list.getTag(2)); + + list.replace(1, "124", 222); + checkEqu("124", list.getCStr(1)); + checkEqu(111, list.getTag(0)); + checkEqu(222, list.getTag(1)); + checkEqu(300, list.getTag(2)); + + checkEqu(300, list.getTag(2)); + list.replaceString(2, "4"); + checkEqu("4", list.getCStr(2)); + checkEqu(111, list.getTag(0)); + checkEqu(222, list.getTag(1)); + checkEqu(300, list.getTag(2)); + + list.replaceTag(2, 123); + checkEqu("4", list.getCStr(2)); + checkEqu(111, list.getTag(0)); + checkEqu(222, list.getTag(1)); + checkEqu(123, list.getTag(2)); + } + void testEqu(){ + ReStringList list1; + ReStringList list2; + + list1.split("1;2;1;3", ';'); + list2.split("1\n2\n1\n3", '\n'); + checkEqu(-1, list1.firstDiff(list2)); + checkEqu(-1, list2.firstDiff(list1)); + checkT(list1.equal(list2)); + checkT(list2.equal(list1)); + + list1.insert(2, "x"); + list1.remove(3); + checkEqu(2, list1.firstDiff(list2)); + checkEqu(2, list2.firstDiff(list1)); + checkF(list1.equal(list2)); + checkF(list2.equal(list1)); + + list2.replace(2, "x"); + checkEqu(-1, list1.firstDiff(list2)); + checkEqu(-1, list2.firstDiff(list1)); + checkT(list1.equal(list2)); + checkT(list2.equal(list1)); + + list2.remove(3); + checkEqu(3, list1.firstDiff(list2)); + checkEqu(3, list2.firstDiff(list1)); + checkF(list1.equal(list2)); + checkF(list2.equal(list1)); + + list1.replace(0, ""); + checkEqu(0, list1.firstDiff(list2)); + checkEqu(0, list2.firstDiff(list1)); + checkF(list1.equal(list2)); + checkF(list2.equal(list1)); + + list1.clear(); + list2.clear(); + checkEqu(-1, list1.firstDiff(list2)); + checkEqu(-1, list2.firstDiff(list1)); + checkT(list1.equal(list2)); + checkT(list2.equal(list1)); + + list1.append("fjkdajfdkla"); + checkEqu(0, list1.firstDiff(list2)); + checkEqu(0, list2.firstDiff(list1)); + checkF(list1.equal(list2)); + checkF(list2.equal(list1)); + } + void testJoin(){ + ReStringList list; + const char* str = "1;abc;xyz;4;;99"; + list.split(str, ';'); + checkEqu(6, list.getCount()); + checkEqu("1", list.getCStr(0)); + checkEqu("abc", list.getCStr(1)); + checkEqu("xyz", list.getCStr(2)); + checkEqu("4", list.getCStr(3)); + checkEqu("", list.getCStr(4)); + checkEqu("99", list.getCStr(5)); + ReByteBuffer value; + list.join(";", value); + checkEqu(str, value.str()); + + list.split("1\r\n2\n\r3", '\n'); + checkEqu(3, list.getCount()); + checkEqu("1", list.getCStr(0)); + checkEqu("2", list.getCStr(1)); + checkEqu("3", list.getCStr(2)); + + list.split("xyz\tXYZ", '\t', true); + checkEqu(5, list.getCount()); + checkEqu("1", list.getCStr(0)); + checkEqu("2", list.getCStr(1)); + checkEqu("3", list.getCStr(2)); + checkEqu("xyz", list.getCStr(3)); + checkEqu("XYZ", list.getCStr(4)); + + + } + void testFile(){ + createTestDir(); + ReByteBuffer file; + file.set(getTestDir(), -1).append("abc.csv", -1); + + ReStringList list; + const char* str = "1;abc;xyz;4;;99"; + list.split(str, ';'); + list.writeToFile(file.str(), "\n"); + + ReStringList list2; + list2.readFromFile(file.str(), true); + + checkEqu(-1, list2.firstDiff(list2)); + } + void testBase(){ + ReStringList list; + ReByteBuffer value; + + list.append("123", 100); + list.append("a", 200); + list.append("vwxyz", 300); + + checkEqu(3, list.getCount()); + int index = 0; + checkEqu("123", list.getCStr(index)); + checkEqu(4, list.getLength(index)); + checkEqu(100, list.getTag(index)); + + index++; + checkEqu("a", list.getCStr(index)); + checkEqu(2, list.getLength(index)); + checkEqu(200, list.getTag(index)); + + index++; + checkEqu("vwxyz", list.getCStr(index)); + checkEqu(6, list.getLength(index)); + checkEqu(300, list.getTag(index)); + + checkEqu(12, list.sumOfLength()); + + list.insert(0, "0", 50); + checkEqu(4, list.getCount()); + checkEqu(14, list.sumOfLength()); + + index = 0; + checkEqu("0", list.getCStr(index)); + checkEqu(2, list.getLength(index)); + checkEqu(50, list.getTag(index)); + + index++; + checkEqu("123", list.getCStr(index)); + checkEqu(4, list.getLength(index)); + checkEqu(100, list.getTag(index)); + + index++; + checkEqu("a", list.getCStr(index)); + checkEqu(2, list.getLength(index)); + checkEqu(200, list.getTag(index)); + + index++; + checkEqu("vwxyz", list.getCStr(index)); + checkEqu(6, list.getLength(index)); + checkEqu(300, list.getTag(index)); + + checkEqu(0, list.indexOf("0")); + checkEqu(1, list.indexOf("123")); + checkEqu(2, list.indexOf("a")); + checkEqu(2, list.indexOf("A", true)); + checkEqu(3, list.indexOf("vwxyz")); + checkEqu(3, list.indexOf("VwXyz", true)); + + checkEqu(0, list.indexOf("0", false, 0)); + checkEqu(1, list.indexOf("123", false, 1)); + checkEqu(2, list.indexOf("a", false, 1)); + checkEqu(2, list.indexOf("a", false, 2)); + checkEqu(2, list.indexOf("A", true, 2)); + checkEqu(3, list.indexOf("vwxyz", false, 2)); + checkEqu(3, list.indexOf("vwxyz", false, 3)); + checkEqu(3, list.indexOf("VwXyz", true, 3)); + + checkEqu(-1, list.indexOf("A")); + checkEqu(-1, list.indexOf("0123")); + checkEqu(-1, list.indexOf("a", false, 3)); + checkEqu(-1, list.indexOf("A", true, 3)); + + checkEqu(0, list.nextStartingWith(0, "0")); + checkEqu(1, list.nextStartingWith(0, "12")); + checkEqu(2, list.nextStartingWith(0, "a")); + checkEqu(2, list.nextStartingWith(1, "a")); + checkEqu(2, list.nextStartingWith(2, "a")); + checkEqu(2, list.nextStartingWith(0, "A", true)); + checkEqu(-1, list.nextStartingWith(2, "Ab", true)); + checkEqu(-1, list.nextStartingWith(0, "b", true)); + + checkEqu(3, list.nextStartingWith(0, "vwxy", false)); + checkEqu(3, list.nextStartingWith(0, "vwxy", true)); + checkEqu(-1, list.nextStartingWith(0, "vWxY", false)); + + ReStringList list2; + list2.append("a", 100); + list2.append("b", 200); + list2.append("c", 300); + ReStringList list3; + list3.append("x", 1000); + list3.append("y", 2000); + + list2.append(list3); + checkEqu(5, list2.getCount()); + checkEqu("a", list2.getCStr(0)); + checkEqu(100, list2.getTag(0)); + checkEqu("b", list2.getCStr(1)); + checkEqu(200, list2.getTag(1)); + checkEqu("c", list2.getCStr(2)); + checkEqu(300, list2.getTag(2)); + checkEqu("x", list2.getCStr(3)); + checkEqu(1000, list2.getTag(3)); + checkEqu("y", list2.getCStr(4)); + checkEqu(2000, list2.getTag(4)); + } +}; +extern void testReStringList(void); + +void testReStringList(void){ + TestReStringList unit; +} +#endif /*RE_TESTUNIT*/ + diff --git a/base/ReStringList.hpp b/base/ReStringList.hpp new file mode 100644 index 0000000..ceb35da --- /dev/null +++ b/base/ReStringList.hpp @@ -0,0 +1,61 @@ +/* + * ReStringList.h + * + * Created on: 20.05.2010 + * Author: wk + */ + +#ifndef RESTRINGLIST_H_ +#define RESTRINGLIST_H_ + +/** + * This class implements a dynamic array of C strings. + *

With this class it is very simple to break a string into a vector of substrings.

+ *
+ * Example:
+ * This example adds a column with a current number to a CSV file (comma separated file):
+ * 
+ * ReStringList listIn, listOut;
+ * listIn.readFromFile("abc.csv");
+ * for (int ii = 0; ii < listIn.getCount(); ii++){
+ *    ReStringList cols;
+ *    cols.split(list.getCStr(ii), ',');
+ *    char number[20]; sprintf(number, "%d", ii + 1);
+ *    cols.append(0, nummber);
+ *    ReByteBuffer line; line.append(cols.join(",");
+ *    listOut.append(line.str());
+ * }
+ * listOut.writeToFile("abc.csv");
+ * 
+ */ +class ReStringList : public ReSeqList { +public: + ReStringList(); + virtual ~ReStringList(); +public: + void append(const char* source, Tag tag = 0); + void append(ReStringList& source); + void insert(Index index, const char* source, Tag tag = 0); + void replace(Index index, const char* source, Tag tag = 0); + void replaceString(Index index, const char* source); + void replaceTag(Index index, Tag tag); + + const char* getCStr(Index index) const; + Tag getTag(Index index) const; + size_t getLength(Index index) const; + size_t sumOfLength() const; + + Index indexOf(const char* toFind, bool ignoreCase = false, Index start = 0) const; + Index nextStartingWith(Index index, const char* prefix, bool ignoreCase = false); + + void split(const char* list, char separator, bool append = false); + void join(const char* separator, ReByteBuffer& result); + + int firstDiff(const ReStringList& toCompare) const; + bool equal(const ReStringList& toCompare) const; + + bool writeToFile(const char* filename, const char* separator = "\n", const char* mode = "w"); + bool readFromFile(const char* filename, bool cutNewline = true); +}; + +#endif /* RESTRINGLIST_H_ */ diff --git a/base/ReStringUtils.cpp b/base/ReStringUtils.cpp new file mode 100644 index 0000000..794a2be --- /dev/null +++ b/base/ReStringUtils.cpp @@ -0,0 +1,413 @@ +/* + * ReStringUtils.cpp + * + * Created on: 16.05.2010 + * Author: wk + */ + +#include "../base/restring.hpp" + +char ReStringUtils::slash = ReStringUtils::initPathSeparator(); +const char* ReStringUtils::slashStr = NULL; +const char ReStringUtils::AUTO_SEPARATOR = '\0'; + + +/** @brief Initializes the os specific path separator (char and string). + * + * @return The path separator: '/' under unix and '\\' under windows. + */ +char ReStringUtils::initPathSeparator(){ + slashStr = NULL; + if (getenv("PATH") != NULL){ + if (strchr(getenv("PATH"), '\\') != NULL) + slashStr = "\\"; + else if (strchr(getenv("PATH"), '/') == NULL) + slashStr = "/"; + } + if (slashStr != NULL && getenv("TMP") != NULL){ + if (strchr(getenv("TMP"), '\\') != NULL) + slashStr = "\\"; + else if (strchr(getenv("TMP"), '/') == NULL) + slashStr = "/"; + } + if (slashStr == NULL && getenv("TEMP") != NULL){ + if (strchr(getenv("TEMP"), '\\') != NULL) + slashStr = "\\"; + else if (strchr(getenv("TMP"), '/') == NULL) + slashStr = "/"; + } + if (slashStr == NULL) + slashStr = "/"; + return slashStr[0]; +} + + +/** @brief Splits a filename into its parts. + * + * Example: file:/etc/samba/smb.conf + * The 4 parts are: + *
  • A protocol: file:
  • + *
  • The path. /etc/samba/
  • + *
  • The name: smb
  • + *
  • The extension: .conf
  • + *
+ * All parts exclusive the name can be empty. + * + *
Example:
+ * ReByteBuffer protocol, path, name, ext;
+ * ReByteBuffer fnBackup;
+ * ReStringUtils::splitPath("/etc/sambe/smb.conf", &protocol, &path, &name, NULL);
+ * ext.append(".bak");
+ * ReStringUtils::joinPath(fnBackup, &protocol, &path, &name, &ext);
+ * assert("/etc/sambe/smb.bak", fnBackup.getBuffer());
+ * 
+ * + * @param fullname The full name to split. + * @param protocol Out: The protocol part of the filename. May be NULL. + * @param path Out: The path of the filename. May be NULL. + * @param name Out: The name part of the filename. May be NULL. + * @param ext Out: The extension. May be NULL. + */ +void ReStringUtils::splitPath(const char* fullname, + ReByteBuffer* protocol, ReByteBuffer* path, ReByteBuffer* name, ReByteBuffer* ext){ + const char* start = strchr(fullname, ':'); + if (protocol != NULL){ + protocol->setLength(0); + if (start != NULL) + protocol->append(fullname, start - fullname + 1); + } + if (start == NULL) + start = fullname; + else + start++; + + const char* end = strrchr(start, slash); + + if (path != 0){ + path->setLength(0); + if (end != NULL) + path->append(start, end - start + 1); + } + if (end != NULL) + start = end + 1; + end = strrchr(start, '.'); + if (name != NULL){ + name->setLength(0); + if (end == NULL) + name->append(start, strlen(start)); + else + name->append(start, end - start); + } + if (ext != NULL){ + ext->setLength(0); + if (end != NULL) + ext->append(end, strlen(end)); + } +} +/** Joins a filename from parts. + * + *
Example:
+ * ReByteBuffer protocol, path, name, ext;
+ * ReByteBuffer fnBackup;
+ * ReStringUtils::splitPath("/etc/sambe/smb.conf", &protocol, &path, &name, NULL);
+ * ext.append(".bak");
+ * ReStringUtils::joinPath(fnBackup, &protocol, &path, &name, &ext);
+ * assert("/etc/sambe/smb.bak", fnBackup.getBuffer());
+ * 
+ * + * @param fullpath Out: The full path of the filename. + * @param protocol The protocol part. May be NULL. + * @param path The path part. May be NULL. + * @param name The name part. May be NULL. + * @param ext The extension part. May be NULL. + * + * @result fullpath. (for chaining). + */ +ReByteBuffer& ReStringUtils::joinPath(ReByteBuffer& fullpath, + ReByteBuffer* protocol, ReByteBuffer* path, ReByteBuffer* name, ReByteBuffer* ext){ + fullpath.setLength(0); + if (protocol != NULL) + fullpath.append(*protocol); + if (path != NULL) + fullpath.append(*path); + if (name != NULL) + fullpath.append(*name); + if (ext != NULL) + fullpath.append(*ext); + return fullpath; +} +/** Joins a filename from parts. + * + *
Example:
+ * ReByteBuffer protocol, path, name;
+ * ReByteBuffer fnBackup;
+ * ReStringUtils::splitPath("/etc/sambe/smb.conf", &protocol, &path, &name, NULL);
+ * ReStringUtils::joinPath(fnBackup, protocol.getBuffer(), path.getBuffer(), name.getBuffer(), ".bak");
+ * assert("/etc/sambe/smb.bak", fnBackup.getBuffer());
+ * 
+ * + * @param fullpath Out: The full path of the filename. + * @param protocol The protocol part. May be NULL. + * @param path The path part. May be NULL. + * @param name The name part. May be NULL. + * @param ext The extension part. May be NULL. + * + * @result fullpath. (for chaining). + */ +ReByteBuffer& ReStringUtils::joinPath(ReByteBuffer& fullpath, + const char* protocol, const char* path, const char* name, const char* ext){ + fullpath.setLength(0); + if (protocol != NULL) + fullpath.append(protocol, strlen(protocol)); + if (path != NULL) + fullpath.append(path, strlen(path)); + if (name != NULL) + fullpath.append(name, strlen(name)); + if (ext != NULL) + fullpath.append(ext, strlen(ext)); + return fullpath; +} + +/** @brief Compares two strings case insensitive. + * + * @param string1 The first C string to compare. + * @param string2 The second C string to compare. + * @param length The maximum length to compare. + * + * @return < 0: string1 < string2 0: string1 == string2 > 0: string1 > string2 + */ +int ReStringUtils::strnicmp(const char* string1, const char* string2, size_t length){ + int rc = 0; + while(rc == 0 && length-- > 0){ + char cc1 = *string1++; + char cc2 = *string2++; + if (toupper(cc1) != toupper(cc2)) + rc = toupper(cc1) - toupper(cc2); + else if (cc1 == '\0') + break; + } + return rc; +} + +/** @brief Tests whether a phrase is in a phrase list. + * + * @param phrase The word to search. + * @param list The list to search. All phrases of the list are separated by separator. + * @param ignoreCase true: The search is case insensitive. false: The search is case sensitive. + * @param separator The separator in list. If AUTO_SEPARATOR the separator will + * be taken from the list itself (the first character). + */ +bool ReStringUtils::isInList(const char* phrase, const char* list, + bool ignoreCase, char separator){ + if (separator == AUTO_SEPARATOR) + separator = *list++; + const char* end = strchr(list, separator); + int phraseLength = strlen(phrase); + bool rc = false; + while(! rc && end != NULL){ + if (end - list == phraseLength){ + if (ignoreCase) + rc = strnicmp(list, phrase, phraseLength) == 0; + else + rc = strncmp(list, phrase, end - list) == 0; + if (rc) + break; + } + list = end + 1; + end = strchr(list, separator); + } + if (! rc){ + if (ignoreCase) + rc = strnicmp(list, phrase, end - list) == 0; + else + rc = strncmp(list, phrase, end - list) == 0; + } + return rc; +} + +#if defined RE_TESTUNIT +class TestReStringUtils : public ReTestUnit { +public: + TestReStringUtils() : ReTestUnit("ReStringUtils", __FILE__){ + run(); + } +private: + void run(){ + testStrnicmp(); + testIsInList(); + testSplitPath(); + } + void testStrnicmp(){ + checkT(ReStringUtils::strnicmp("abc", "abc", 3) == 0); + checkT(ReStringUtils::strnicmp("abc", "ab", 3) > 0); + checkT(ReStringUtils::strnicmp("ab", "abc", 3) < 0); + + checkT(ReStringUtils::strnicmp("aBc", "Abc", 3) == 0); + checkT(ReStringUtils::strnicmp("Abc", "aB", 3) > 0); + checkT(ReStringUtils::strnicmp("AB", "abc", 3) < 0); + + checkT(ReStringUtils::strnicmp("ABC", "ABD", 2) == 0); + checkT(ReStringUtils::strnicmp("abC", "ABD", 2) == 0); + + checkT(ReStringUtils::strnicmp("AAC", "ABD", 2) < 0); + checkT(ReStringUtils::strnicmp("aaC", "ABD", 2) < 0); + + checkT(ReStringUtils::strnicmp("", "x", 99) < 0); + checkT(ReStringUtils::strnicmp("x", "", 99) > 0); + + checkT(ReStringUtils::strnicmp("abc", "abc", 99) == 0); + checkT(ReStringUtils::strnicmp("abc", "ab", 99) > 0); + checkT(ReStringUtils::strnicmp("ab", "abc", 99) < 0); + } + void testIsInList(){ + checkT(ReStringUtils::isInList("abc", ";abc;def", true)); + checkT(ReStringUtils::isInList("aBc", ";abc;def", true)); + checkF(ReStringUtils::isInList("aBc", ";abc;def", false)); + + checkF(ReStringUtils::isInList("aBc", ";abc;def", false)); + + checkT(ReStringUtils::isInList("abc", ";a;abc;def", true)); + checkT(ReStringUtils::isInList("aBc", ";b;abc;def", true)); + checkF(ReStringUtils::isInList("aBc", ";c;abc;def", false)); + + checkF(ReStringUtils::isInList("aBc", ";a;abcabc;def", false)); + + checkT(ReStringUtils::isInList("abc", ";abc", true)); + checkT(ReStringUtils::isInList("aBc", ";abc", true)); + checkF(ReStringUtils::isInList("aBc", ";abc", false)); + + checkF(ReStringUtils::isInList("aBc", ";abc", false)); + + } + void testSplitPath(){ + ReByteBuffer fullname, protocol, path, name, ext; + const char* fn = "file:/etc/samba/smb.cnf"; + + ReStringUtils::splitPath(fn, &protocol, &path, &name, &ext); + checkEqu("file:", protocol.str()); + checkEqu("/etc/samba/", path.str()); + checkEqu("smb", name.str()); + checkEqu(".cnf", ext.str()); + + ReStringUtils::joinPath(fullname, &protocol, &path, &name, &ext); + checkEqu(fn, fullname.str()); + + fn = "/etc/samba/smb.cnf"; + + ReStringUtils::splitPath(fn, &protocol, &path, &name, &ext); + checkEqu("", protocol.str()); + checkEqu("/etc/samba/", path.str()); + checkEqu("smb", name.str()); + checkEqu(".cnf", ext.str()); + + ReStringUtils::joinPath(fullname, &protocol, &path, &name, &ext); + checkEqu(fn, fullname.str()); + + fn = "smb.cnf"; + + ReStringUtils::splitPath(fn, &protocol, &path, &name, &ext); + checkEqu("", protocol.str()); + checkEqu("", path.str()); + checkEqu("smb", name.str()); + checkEqu(".cnf", ext.str()); + + ReStringUtils::joinPath(fullname, &protocol, &path, &name, &ext); + checkEqu(fn, fullname.str()); + + fn = "smb"; + + ReStringUtils::splitPath(fn, &protocol, &path, &name, &ext); + checkEqu("", protocol.str()); + checkEqu("", path.str()); + checkEqu("smb", name.str()); + checkEqu("", ext.str()); + + ReStringUtils::joinPath(fullname, &protocol, &path, &name, &ext); + checkEqu(fn, fullname.str()); + + fn = "file:smb.003.cnf"; + + ReStringUtils::splitPath(fn, &protocol, &path, &name, &ext); + checkEqu("file:", protocol.str()); + checkEqu("", path.str()); + checkEqu("smb.003", name.str()); + checkEqu(".cnf", ext.str()); + + ReStringUtils::joinPath(fullname, &protocol, &path, &name, &ext); + checkEqu(fn, fullname.str()); + + fn = "file:/etc.bak/smb"; + + ReStringUtils::splitPath(fn, &protocol, &path, &name, &ext); + checkEqu("file:", protocol.str()); + checkEqu("/etc.bak/", path.str()); + checkEqu("smb", name.str()); + checkEqu("", ext.str()); + + ReStringUtils::joinPath(fullname, &protocol, &path, &name, &ext); + checkEqu(fn, fullname.str()); + + fn = "file:/etc/samba/smb.cnf"; + + ReStringUtils::splitPath(fn, NULL, &path, &name, &ext); + checkEqu("file:", protocol.str()); + checkEqu("smb", name.str()); + checkEqu(".cnf", ext.str()); + + ReStringUtils::joinPath(fullname, &protocol, NULL, &name, &ext); + checkEqu("file:smb.cnf", fullname.str()); + + fn = "file:/etc/samba/smb.cnf"; + + ReStringUtils::splitPath(fn, NULL, NULL, &name, &ext); + checkEqu("smb", name.str()); + checkEqu(".cnf", ext.str()); + + ReStringUtils::joinPath(fullname, NULL, NULL, &name, &ext); + checkEqu("smb.cnf", fullname.str()); + + fn = "file:/etc/samba/smb.cnf"; + + ReStringUtils::splitPath(fn, NULL, NULL, &name, NULL); + //checkEqu("", protocol.str()); + //checkEqu("/etc/samba/", path.str()); + checkEqu("smb", name.str()); + //checkEqu(".cnf", ext.str()); + + ReStringUtils::joinPath(fullname, NULL, NULL, &name, NULL); + checkEqu("smb", fullname.str()); + + fn = "file:/etc/samba/smb.cnf"; + + ReStringUtils::splitPath(fn, NULL, &path, NULL, &ext); + //checkEqu("", protocol.str()); + checkEqu("/etc/samba/", path.str()); + //checkEqu("smb", name.str()); + checkEqu(".cnf", ext.str()); + + ReStringUtils::joinPath(fullname, NULL, &path, NULL, &ext); + checkEqu("/etc/samba/.cnf", fullname.str()); + + ReStringUtils::joinPath(fullname, "http:", "//any.de/", "name", ".ext"); + checkEqu("http://any.de/name.ext", fullname.str()); + + ReStringUtils::joinPath(fullname, NULL, "/any.de/", "name", ".ext"); + checkEqu("/any.de/name.ext", fullname.str()); + + ReStringUtils::joinPath(fullname, NULL, NULL, "name", ".ext"); + checkEqu("name.ext", fullname.str()); + + ReStringUtils::joinPath(fullname, NULL, NULL, "name", NULL); + checkEqu("name", fullname.str()); + + ReStringUtils::joinPath(fullname, "file:", "/", NULL, NULL); + checkEqu("file:/", fullname.str()); + } +}; +extern void testReStringUtils(void); + +void testReStringUtils(void){ + TestReStringUtils unit; +} +#endif /*RE_TESTUNIT*/ + diff --git a/base/ReStringUtils.hpp b/base/ReStringUtils.hpp new file mode 100644 index 0000000..20b667a --- /dev/null +++ b/base/ReStringUtils.hpp @@ -0,0 +1,39 @@ +/* + * ReStringUtils.h + * + * Created on: 16.05.2010 + * Author: wk + */ + +#ifndef RESTRINGUTILS_H_ +#define RESTRINGUTILS_H_ + +/** + * This class contains static methods only. In other words it builds a protected namespace. + * The methods defined here handle services on strings implemented by ReByteBuffer. + */ +class ReStringUtils { +public: + //@ If used in isInList() the first character of the list will be the separator. + static const char AUTO_SEPARATOR; +public: + static char initPathSeparator(); + static char pathSeparator() + { return slash; } + static const char* pathSeparatorStr() + { return slashStr; } + static void splitPath(const char* fullname, + ReByteBuffer* protocol, ReByteBuffer* path, ReByteBuffer* name, ReByteBuffer* ext); + static ReByteBuffer& joinPath(ReByteBuffer& result, + ReByteBuffer* protocol, ReByteBuffer* path, ReByteBuffer* name, ReByteBuffer* ext); + static ReByteBuffer& joinPath(ReByteBuffer& result, + const char* protocol, const char* path, const char* name, const char* ext); + static int strnicmp(const char* string1, const char* string2, size_t length); + static bool isInList(const char* phrase, const char* list, + bool ignoreCase = true, char separator = AUTO_SEPARATOR); +private: + static char slash; + static const char* slashStr; +}; + +#endif /* RESTRINGUTILS_H_ */ diff --git a/base/ReTestUnit.cpp b/base/ReTestUnit.cpp new file mode 100644 index 0000000..4d72caa --- /dev/null +++ b/base/ReTestUnit.cpp @@ -0,0 +1,267 @@ +/* + * ReTestUnit.cpp + * + * Created on: 04.05.2010 + * Author: wk + */ +#include + +#include "rebase.hpp" +/** @brief Constructor. + * + * @param name The name of the test class. + * @param sourceFile The file where contain the tests. + * This will be used for error messages. + */ +ReTestUnit::ReTestUnit(const char* name, const char* sourceFile) + : + m_name(strdup(name)), + m_errorCount(0), + m_sourceFile(strdup(sourceFile)) +{ + logF(false, i18n("Start %s"), name); +} + +/** @brief Destructor. + */ +ReTestUnit::~ReTestUnit() { + if (m_errorCount > 0) + logF(true, i18n("%s: %d error(s)"), m_name, m_errorCount); + free((void*) m_name); + free((void*) m_sourceFile); +} +/** @brief Checks a boolean expression. A false value will be logged. + * + * @param condition This expression will be tested. + * If false an error messsage will be issued. + * @param lineNo The line number of the test (for the error messge). + */ +void ReTestUnit::assertTrue(bool condition, int lineNo) +{ + if (! condition){ + logF(true, i18n("%s-%d: not true!"), m_sourceFile, lineNo); + m_errorCount++; + } +} +/** @brief Checks a boolean expression. A true value will be logged. + * + * @param condition This expression will be tested. + * If tre an error messsage will be issued. + * @param lineNo The line number of the test (for the error messge). + */ +void ReTestUnit::assertFalse(bool condition, int lineNo) +{ + if (condition){ + logF(true, i18n("%s-%d: not false!"), m_sourceFile, lineNo); + m_errorCount++; + } +} +/** @brief Checks a pointer expression. A not null value will be logged. + * + * @param pointer This expression will be tested. + * If not null an error messsage will be issued. + * @param lineNo The line number of the test (for the error messge). + */ +void ReTestUnit::assertNull(void* pointer, int lineNo){ + if (pointer != NULL){ + logF(true, "%s-%d: is not null %lx", m_sourceFile, lineNo, pointer); + m_errorCount++; + } +} +/** @brief Checks a pointer expression. A null value will be logged. + * + * @param pointer This expression will be tested. + * If null an error messsage will be issued. + * @param lineNo The line number of the test (for the error messge). + */ +void ReTestUnit::assertNotNull(void* pointer, int lineNo){ + if (pointer == NULL){ + logF(true, i18n("%s-%d: is null"), m_sourceFile, lineNo); + m_errorCount++; + } +} +/** @brief Compares two integer values. If not equal this will be logged. + * + * @param expected The expected value + * @param current The current value. + * @param lineNo The line number of the test (for the error messge). + */ +void ReTestUnit::assertEqual(long expected, long current, int lineNo){ + if (expected != current){ + logF(true, i18n("%s-%d: expected: %ld (%lx) current: %ld (%lx)"), + m_sourceFile, lineNo, expected, expected, current, current); + m_errorCount++; + } +} +/** @brief Compares two string values. If not equal this will be logged. + * + * @param expected The expected value + * @param current The current value. + * @param lineNo The line number of the test (for the error messge). + */ +void ReTestUnit::assertEqual(const char* expected, const char* current, int lineNo){ + if (current == NULL || strcmp(expected, current) != 0){ + logF(true, i18n("%s-%d: expected / current: length: %d / %d\n%.512s\n%.512s"), + m_sourceFile, lineNo, strlen(expected), current == NULL ? 0 : strlen(current), + expected, current == NULL ? "" : current); + m_errorCount++; + } +} +/** @brief Checks whether a file exists. If not this will be logged. + * + * @param name The filename. + * @param lineNo The line number of the test (for the error messge). + */ +void ReTestUnit::assertFileExists(const char* name, int lineNo){ + struct stat info; + if (stat(name, &info) != 0){ + logF(true, i18n("%s-%d: File does not exist: %s"), + m_sourceFile, lineNo, name); + m_errorCount++; + } else if ((info.st_mode & __S_IFDIR) != 0){ + logF(true, i18n("%s-%d: File does exist but this is a directory: %s"), + m_sourceFile, lineNo, name); + m_errorCount++; + } +} +/** @brief Creates an empty temporary directory. + * The name can be retrieved by getTestDir(). + */ +void ReTestUnit::createTestDir(){ + char name[512]; + if (getenv("TMP") != NULL){ + strcpy(name, getenv("TMP")); + } else if (getenv("TEMP")){ + strcpy(name, getenv("TEMP")); + } else { + strcpy(name, "/tmp/"); + } + char* ptr = name + strlen(name) - 1; + if (*ptr != ReStringUtils::pathSeparator()) + strcpy(ptr + 1, ReStringUtils::pathSeparatorStr()); + strcat(ptr, "retestunit"); + strcat(ptr, ReStringUtils::pathSeparatorStr()); + struct stat info; + if (lstat(name, &info) != 0) + mkdir(name, ALLPERMS); + else{ + char cmd[512 + 128]; + snprintf(cmd, sizeof cmd, "rm -Rf %s*", name); + system(cmd); + } + strcpy(m_tempDir, name); +} +/** @brief Returns The temporary directory. + * + * @return The name of a temporary directory. + */ +const char* ReTestUnit::getTestDir(){ + return (const char*) m_tempDir; +} +/** @brief Creates a file and fills it with an given content. + * + * @param filename The name of the file. + * @param content The content of the file. If NULL the file will be empty. + */ +void ReTestUnit::createFile(const char* filename, const char* content){ + FILE* fp = fopen(filename, "w"); + if (fp != NULL && content != NULL){ + fwrite(content, strlen(content), 1, fp); + fclose(fp); + } +} +/** @brief Creates a file and fills it with an given content. + * + * @param filename The name of the file. + * @param content The content of the file. If NULL the file will be empty. + */ +void ReTestUnit::createDir(const char* filename){ + mkdir(filename, ALLPERMS); +} + + +/** @brief Checks whether a directory exists. If not this will be logged. + * + * @param dir The name of the directory. + * @param lineNo The line number of the test (for the error messge). + */ +void ReTestUnit::assertDirExists(const char* dir, int lineNo){ + struct stat info; + if (stat(dir, &info) != 0){ + logF(true, i18n("%s-%d: Directory does not exist: %s"), + m_sourceFile, lineNo, dir); + m_errorCount++; + } else if ((info.st_mode & __S_IFDIR) == 0){ + logF(true, i18n("%s-%d: File exists but this is not a directory: %s"), + m_sourceFile, lineNo, dir); + m_errorCount++; + } +} + +/** @brief Logs a message. + * + * It can be used to inform the user about coming (error-) messages. + * + * @param isError true: The message is an error message. false: Otherwise. + * @param message The message to issue. + * + * @return ! isError + */ +bool ReTestUnit::log(bool isError, const char* message){ + printf("%s%s\n", isError ? "+++ " : "", message); + return ! isError; +} +/** @brief Logs a formated message with placeholders. + * + * It can be used to inform the user about coming (error-) messages. + * + * @param isError true: The message is an error message. false: Otherwise. + * @param format The message with placeholders like printf. + * @param ... A variable number of arguments which replace the placeholders. + * + * @return ! isError + */ +bool ReTestUnit::logF(bool isError, const char* format, ...){ + char buffer[2048]; + va_list args; + + va_start(args, format); + vsnprintf(buffer, sizeof buffer, format, args); + va_end(args); + return log(isError, buffer); +} + +#if defined RE_TESTUNIT +class TestTestUnit : public ReTestUnit { +public: + TestTestUnit() : ReTestUnit("TestTest", __FILE__){ + run(); + } +private: + void run(){ + checkT(true); + checkF(false); + checkN(NULL); + checkNN(""); + checkEqu(1, 1); + checkEqu("abc", "abc"); + checkDirExists("/etc/"); + checkFileExists("/etc/passwd"); + log(false, "8 errors follow:"); + checkT(false); + checkF(true); + checkN(""); + checkNN(NULL); + checkEqu(1, 2); + checkEqu("abc", "abcd"); + checkDirExists("/etc!/"); + checkFileExists("/etc!/passwd"); + log(false, "8 errors expected!"); + } +}; +extern void testReTestUnit(void); + +void testReTestUnit(void){ + TestTestUnit unit; +} +#endif /*RE_TESTUNIT*/ diff --git a/base/ReTestUnit.hpp b/base/ReTestUnit.hpp new file mode 100644 index 0000000..3a8bc6c --- /dev/null +++ b/base/ReTestUnit.hpp @@ -0,0 +1,51 @@ +/* + * ReTestUnit.h + * + * Created on: 04.05.2010 + * Author: wk + */ + +#ifndef RETESTUNIT_H_ +#define RETESTUNIT_H_ + +class ReTestUnit { +public: + ReTestUnit(const char* name, const char* sourcefile); + virtual ~ReTestUnit(); +private: + // Not accessible, not implemented! + ReTestUnit(const ReTestUnit& source); + // Not accessible, not implemented! + ReTestUnit& operator =(const ReTestUnit& source); +public: + void assertTrue(bool conditon, int lineNo); + void assertFalse(bool conditon, int lineNo); + void assertNull(void* pointer, int lineNo); + void assertNotNull(void* pointer, int lineNo); + void assertEqual(long expected, long current, int lineNo); + void assertEqual(const char* expected, const char* current, int lineNo); + + void createTestDir(); + const char* getTestDir(); + void createFile(const char* filename, const char* content); + void createDir(const char* filename); + + void assertFileExists(const char* name, int lineNo); + void assertDirExists(const char* dir, int lineNo); + + virtual bool log(bool isError, const char* message); + virtual bool logF(bool isError, const char* format, ...); +protected: + const char* m_name; + int m_errorCount; + const char* m_sourceFile; + char m_tempDir[512]; +}; +#define checkT(cond) assertTrue(cond, __LINE__) +#define checkF(cond) assertFalse(cond, __LINE__) +#define checkN(ptr) assertNull((void*) ptr, __LINE__) +#define checkNN(ptr) assertNotNull((void*) ptr, __LINE__) +#define checkEqu(exp, cur) assertEqual(exp, cur, __LINE__) +#define checkFileExists(fn) assertFileExists(fn, __LINE__) +#define checkDirExists(fn) assertDirExists(fn, __LINE__) +#endif /* RETESTUNIT_H_ */ diff --git a/base/ReVarArgs.cpp b/base/ReVarArgs.cpp new file mode 100644 index 0000000..96e8eea --- /dev/null +++ b/base/ReVarArgs.cpp @@ -0,0 +1,339 @@ +/* + * ReVarArgs.cpp + * + * Created on: 05.05.2010 + * Author: wk + */ +/** + *
Example:
+ * 
+ * struct stat info;
+ * stat("/etc/passwd");
+ * ReVarArgs list;
+ * const char* info2 = list.reset("$1: Size: $2 $3").arg(info.st_fname)
+ *     .arg(info.st_size, "%8d").arg(info.st_size, "%8x").asCString();
+ * 
+ */ +#include "rebase.hpp" + +int const ReVarArgs::STD_SPACE = 20; +char const ReVarArgs::PLACE_HOLDER_MARK = '$'; + +typedef ReByteBuffer::Byte Byte; +/** @brief Constructor. + */ +ReVarArgs::ReVarArgs(void) + : + m_argNo(0), + m_format(), + m_argBuffer(), + // m_args + m_stringIsReady(false), + m_trigger(NULL) +{ +} + +/** @brief Constructor. + * + * @param format The format with the placeholders. + * @throws ReFormatException() There are gaps in the numbers of the placeholders. + */ +ReVarArgs::ReVarArgs(const char* format) + : + m_argNo(0), + m_format(), + m_argBuffer(), + // m_args + m_stringIsReady(false), + m_trigger(NULL) +{ + reset(format); +} +/** @brief Resets the instance. + * + * Sets a new format and waits for the arguments. + * + * @param format A format string containing placeholders for arguments. + * + * @result A reference for the instance itself. This allows chaining. + * @throws ReFormatException() There are gaps in the numbers of the placeholders. + */ +ReVarArgs& ReVarArgs::reset(const char* format){ + m_argNo = 0; + memset(m_args, 0, sizeof m_args); + m_format.setLength(0); + m_format.append(format, strlen(format)); + m_stringIsReady = false; + + // Test the placehoders in the format and find the argument count: + // No gaps are allowed. + m_maxArgNo = 0; + int position = m_format.indexOf(PLACE_HOLDER_MARK); + int argNo; + while (position >= 0){ + Byte cc = m_format.at(position + 1); + if (cc == PLACE_HOLDER_MARK){ + // $$ found: replace with $: + position++; + } else if (! isdigit(cc)){ + // $ found. No action. + position++; + } else { + int argNoLen = 2; + argNo = m_format.at(position + 1) - '0'; + cc = m_format.at(position + 2); + if (isdigit(cc)){ + argNoLen++; + argNo = 10 * argNo + cc - '0'; + } + if (argNo > m_maxArgNo) + m_maxArgNo = argNo; + m_args[argNo]++; + position += argNoLen; + } + position = m_format.indexOf(PLACE_HOLDER_MARK, position); + } + // Searching the first gap: + for (int ii = 1; ii < m_maxArgNo; ii++) + if (m_args[ii] == 0){ + char msg[128]; + snprintf(msg, sizeof msg, i18n("missing $%d in format: "), ii); + throw ReFormatException(msg, format); + } + + memset(m_args, 0, sizeof m_args); + return *this; +} +/** @brief Replaces the placeholders with the strings from m_args. + * + * @throws ReFormatException There are wrong placeholders. + */ +void ReVarArgs::replacePlaceholder(){ + int position = m_format.indexOf(PLACE_HOLDER_MARK); + int found = 0; + int argNo; + while (position >= 0){ + Byte cc = m_format.at(position + 1); + if (cc == PLACE_HOLDER_MARK){ + // $$ found: replace with $: + m_format.remove(position, 1); + position++; + } else if (! isdigit(cc)){ + // $ found. No action. + position++; + } else { + found++; + int argNoLen = 2; + argNo = m_format.at(position + 1) - '0'; + cc = m_format.at(position + 2); + if (isdigit(cc)){ + argNoLen++; + argNo = 10 * argNo + cc - '0'; + } + if (argNo > m_argNo){ + throw ReFormatException(i18n("Too large argument number: "), + m_format.getBuffer() + position, + __FILE__, __LINE__); + } else { + char* arg = m_argBuffer.getBuffer() + m_args[argNo]; + size_t argLen = strlen(arg); + m_format.splice(position, argNoLen, arg, argLen); + position += argLen; + } + } + position = m_format.indexOf(PLACE_HOLDER_MARK, position); + } + if (found < m_argNo) + throw ReFormatException(i18n("Format contains to few placeholders"), + m_format.getBuffer(), __FILE__, __LINE__); +} +/** @brief Stores an argument. + * + * After the argument is stored and a trigger exists this trigger will be called. + * + * @param value The argument string. + * @param length The length of value. + */ +void ReVarArgs::store(const char* value, int length){ + if (length < 0) + length = strlen(value); + // Store the first index of the argument in m_argBuffer. + m_args[++m_argNo] = m_argBuffer.getLength(); + // Store the string with trailing '\0': + m_argBuffer.append(value, length); + m_argBuffer.append("", 1); + if (m_trigger != NULL) + m_trigger->newArg(m_argNo, m_maxArgNo); +} +/* @brief Defines an integer argument. + * + * @param value The integer value of the argument. + * @param format The format (used with sprintf()) of the argument, e.g. "%d". + * + * @return The instance itself. This is useful for chaining. + */ +ReVarArgs& ReVarArgs::ReVarArgs::arg(int value, const char* format){ + char number[256]; + snprintf(number, sizeof number, format, value); + store(number); + return *this; +} + +/** @brief Stores an long argument. + * + * @param value The argument value. + * @param format The format (used with sprintf()) of the argument, e.g. "%d". + * + * @return The instance itself. This is useful for chaining. + */ +ReVarArgs& ReVarArgs::arg(long value, const char* format){ + char number[256]; + snprintf(number, sizeof number, format, value); + store(number); + return *this; +} +/** @brief Stores a string argument. + * + * If the string is shorter than the given minimum the string was padded with a given filler. + * The position of the supplemented part (in top of or behind) was controlled by the alignment. + * If the string was longer than the given maximum the string was cut. + * + * @param value The argument string. + * @param minWidth The minimum length of the string. + * + * @return The instance itself. This is useful for chaining. + */ +ReVarArgs& ReVarArgs::arg(const char* value, int minWidth, int maxWidth, bool alignRight){ + int length = strlen(value); + if (maxWidth <= 0 || length < maxWidth) + maxWidth = length; + if (minWidth > maxWidth) + maxWidth = minWidth; + if (length >= minWidth){ + store(value, maxWidth); + } else { + ReByteBuffer buffer; + buffer.setLengthAndFill(minWidth, ' '); + if (alignRight) + memcpy(buffer.getBuffer() + minWidth - length, value, length); + else + memcpy(buffer.getBuffer(), value, length); + store (buffer.getBuffer(), buffer.getLength()); + } + return *this; +} +/** @brief Stores an double argument. + * + * @param value The argument value. + * @param format The format (used with sprintf()) of the argument, e.g. "%d". + * + * @return The instance itself. This is useful for chaining. + */ +ReVarArgs& ReVarArgs::arg(double value, const char* format){ + char number[256]; + snprintf(number, sizeof number, format, value); + store(number); + return *this; +} +/** @brief Returns a C string with the expanded format. + * + * If not done the placeholders will be replaced by the arguments. + * + * @return A C string with the string which is the format with + * the replaced placeholders. + * @throws ReFormatException() Too few arguments. + */ +const char* ReVarArgs::asCString(){ + if (m_argNo != m_maxArgNo) + throw ReFormatException(i18n("To few arguments"), + m_format.getBuffer()); + + if (! m_stringIsReady){ + replacePlaceholder(); + m_stringIsReady = true; + } + return m_format.getBuffer(); +} + +/** @brief Stores a trigger. + * + * @param trigger An instance of ReVarArgTrigger or NULL. + * After storing an argument (calling arg() + * trigger->newArg() will be called. + */ +void ReVarArgs::registerTrigger(ReVarArgTrigger* trigger){ + m_trigger = trigger; +} +/** @brief Marks the end of a arg() chain. + * + * This method is only meaningful in derived classes. + * If some actions can be done only when all arguments are known + * they will be defined in an overwritten method of end(). + *
+ * Example:
+ * 
+ * // ReLogger is derived from ReVarArgs:
+ * logger->sayF("Files: $1 Directories: $2").arg(files).arg(dirs).end();
+ * // The logging is done in the method end().
+ * 
+ */ +void ReVarArgs::end(void){ + // Nothing to do in this base class +} + +#if defined RE_TESTUNIT +class TestReVarArgs : public ReTestUnit, public ReVarArgTrigger { +public: + TestReVarArgs() + : + ReTestUnit("ReVarArgs", __FILE__), + m_argNo(0), + m_maxNo(0) + { + run(); + } + virtual void newArg(int no, int maxNo){ + m_argNo = no; + m_maxNo = maxNo; + } +private: + void run(){ + ReVarArgs list("$1 $$ $2"); + list.registerTrigger(this); + + list.arg(0).arg(9, "%03u"); + checkEqu(m_argNo, 2); + checkEqu(m_maxNo, 2); + checkEqu("0 $ 009", list.asCString()); + + + list.reset("x$1y$2"); + list.arg(1.5); + checkEqu(m_argNo, 1); + checkEqu(m_maxNo, 2); + list.arg(2.45,"%7.3f"); + checkEqu(m_argNo, 2); + checkEqu(m_maxNo, 2); + checkEqu("x1.500000y 2.450", list.asCString()); + + list.reset("$2,$1!$3;$4"); + list.arg("1").arg("ab", 4); + list.arg("xy", 0, 1); + checkEqu(m_argNo, 3); + checkEqu(m_maxNo, 4); + list.arg("ww", 5, 0, true); + checkEqu(m_argNo, 4); + checkEqu(m_maxNo, 4); + checkEqu("ab ,1!x; ww", list.asCString()); + } +private: + int m_argNo; + int m_maxNo; +}; +extern void testReVarArgs(void); + +void testReVarArgs(void){ + TestReVarArgs unit; +} +#endif /*RE_TESTUNIT*/ + diff --git a/base/ReVarArgs.hpp b/base/ReVarArgs.hpp new file mode 100644 index 0000000..e4258a1 --- /dev/null +++ b/base/ReVarArgs.hpp @@ -0,0 +1,68 @@ +/* + * ReVarArgs.h + * + * Created on: 05.05.2010 + * Author: wk + */ + +#ifndef REVARARGS_H_ +#define REVARARGS_H_ + +/** This class will be used as callback method for ReVarArgs. + */ +class ReVarArgTrigger{ +public: + /** @brief This method will be called when a new ReVarArg::log() call was done. + * The call was done after the insertion of the argument into the internal structures. + * @param no The number of the argument (1..max) + * @param maxNo The number of placeholders in the format. + */ + virtual void newArg(int no, int maxNo) = 0; +}; +/** + * Implements a typesafe method for populate a format string containing placeholders + * with some values. + *

Another known system with this functionality is printf. + * But the ReVarArgs is typesafe.

+ */ +class ReVarArgs{ +private: + static int const STD_SPACE; + static char const PLACE_HOLDER_MARK; + +public: + ReVarArgs(); + ReVarArgs(const char* format); +private: + void initialize(size_t size); + void replacePlaceholder(); + void store(const char* value, int length = -1); + +public: + ReVarArgs& reset(const char* format); + ReVarArgs& arg(int value, const char* format = "%d"); + ReVarArgs& arg(long arg, const char* format = "%ld"); + ReVarArgs& arg(const char* arg, int minWidth = 0, int maxWidth = 1024, bool alignRight = false); + ReVarArgs& arg(double arg, const char* format = "%f"); + const char* asCString(); + virtual void end(void); + void registerTrigger(ReVarArgTrigger* trigger); +protected: + //@ The current argument number: 1..99 + int m_argNo; + //@ The maximum of all argument numbers in the format. + int m_maxArgNo; + //@ Stores the format. When the first call of asCharPtr() + //@ the expanded string will be stored here. + ReByteBuffer m_format; + //@ Stores the argument strings including the trailing '\0'. + ReByteBuffer m_argBuffer; + //@ Stores the positions (indexes) of the arguments in m_argBuffer. + int m_args[100]; + //@ true: The compound string is constructed (with expanded placeholders). false: Otherwise. + bool m_stringIsReady; + //@ Callback mechanism: m_trigger.newArg() will be called in store(). + ReVarArgTrigger* m_trigger; +}; + +#endif /* REVARARGS_H_ */ diff --git a/base/baselocations.hpp b/base/baselocations.hpp new file mode 100644 index 0000000..c67c0ee --- /dev/null +++ b/base/baselocations.hpp @@ -0,0 +1,36 @@ +/* + * baselocations.hpp + * + * Created on: 19.05.2010 + * Author: wk + */ + +#ifndef BASELOCATIONS_HPP_ +#define BASELOCATIONS_HPP_ + +/** The files of the REal PUBlic LIB use the range from 50000 until 99999. + */ +enum RELOC_RECONFIGFILE { + LC_CONFIGFILE_DOUBLE = 50001, + LC_CONFIGFILE_NO_INT = 50002, + LC_CONFIGFILE_NO_BOOL = 50003, +}; +enum RELOC_UDPCONNECTION { + LC_UDPCONNECTION_CONSTRUCT = 50101, + LC_UDPCONNECTION_RECEIVE_1 = 50102, + LC_UDPCONNECTION_RECEIVE_2 = 50103, + LC_UDPCONNECTION_SEND_1 = 50104, + LC_UDPCONNECTION_SEND_2 = 50105, + LC_UDPCONNECTION_CLOSE_1 = 50106, + LC_UDPCONNECTION_CLOSE_2 = 50107, + LC_UDPCONNECTION_CONNECT_1 = 50108, + LC_UDPCONNECTION_CONNECT_2 = 50109, + LC_UDPCONNECTION_RUN_1 = 50110, +}; +enum RELOC_LOGGER { + LC_LOGGER_SAYF_OPEN = 50201, +}; +enum RELOC_CSTRING { + LC_CSTRING_REPLACE_1 = 50301, +}; +#endif /* BASELOCATIONS_HPP_ */ diff --git a/base/rebase.hpp b/base/rebase.hpp new file mode 100644 index 0000000..bbcd5b1 --- /dev/null +++ b/base/rebase.hpp @@ -0,0 +1,55 @@ +/* + * stdincludes.hpp + * + * Created on: 05.05.2010 + * Author: wk + */ +#ifndef REBASE_HPP_ +#define REBASE_HPP_ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define __LINUX__ + +#if defined __LINUX__ + +#include +#include +#include +#include +#include + +#elif defined WIN32 + +typedef _int64 int64_t; + +#endif + +#define RE_TESTUNIT +#include "base/ReByteBuffer.hpp" +#include "base/ReVarArgs.hpp" +#include "base/ReLogger.hpp" +#include "base/ReTestUnit.hpp" +#include "base/ReCString.hpp" +#include "base/ReException.hpp" +#include "base/ReStringUtils.hpp" +#include "base/ReDirectory.hpp" +#include "base/ReSeqList.hpp" +#include "base/ReStringList.hpp" +#include "base/ReHashList.hpp" +#include "base/ReConfigFile.hpp" +#include "base/ReI18N.hpp" +#include "base/ReProgramArgs.hpp" + +typedef unsigned char byte_t; + +#include "../base/baselocations.hpp" +#endif /* REBASE_HPP_ */ diff --git a/base/remath.hpp b/base/remath.hpp new file mode 100644 index 0000000..a7528d5 --- /dev/null +++ b/base/remath.hpp @@ -0,0 +1,17 @@ +/* + * remath.hpp + * + * Created on: 26.11.2010 + * Author: wk + */ + +#ifndef REMATH_HPP_ +#define REMATH_HPP_ + +#ifndef REBASE_HPP_ +#include "base/rebase.hpp" +#endif +#include "math/ReObfuscator.hpp" +#include "math/ReRandomizer.hpp" + +#endif /* REMATH_HPP_ */ diff --git a/base/renet.hpp b/base/renet.hpp new file mode 100644 index 0000000..4be9e2e --- /dev/null +++ b/base/renet.hpp @@ -0,0 +1,15 @@ +/* + * renet.hpp + * + * Created on: 26.11.2010 + * Author: wk + */ + +#ifndef RENET_HPP_ +#define RENET_HPP_ + +#ifndef REBASE_HPP_ +#include "../base/rebase.hpp" +#endif +#include "net/ReUdpConnection.hpp" +#endif /* RENET_HPP_ */ diff --git a/base/restring.hpp b/base/restring.hpp new file mode 100644 index 0000000..3cbd7cd --- /dev/null +++ b/base/restring.hpp @@ -0,0 +1,16 @@ +/* + * restring.hpp + * + * Created on: 15.11.2010 + * Author: wk + */ + +#ifndef RESTRING_HPP_ +#define RESTRING_HPP_ + +#ifndef REBASE_HPP_ +#include "../base/rebase.hpp" +#endif +// ReCString.hpp, ReI18N.hpp, ReStringList.hpp and ReStringUtils.hpp are included in rebase.hpp! + +#endif /* RESTRING_HPP_ */ diff --git a/crepublib.doxyfile b/crepublib.doxyfile new file mode 100644 index 0000000..aec1b5c --- /dev/null +++ b/crepublib.doxyfile @@ -0,0 +1,1630 @@ +# Doxyfile 1.7.1 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = crepublib + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = v0.3 + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = doxygen + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, +# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, +# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given extension. +# Doxygen has a built-in mapping, but you can override or extend it using this +# tag. The format is ext=language, where ext is a file extension, and language +# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, +# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make +# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C +# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions +# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate getter +# and setter methods for a property. Setting this option to YES (the default) +# will make doxygen to replace the get and set methods by a property in the +# documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = NO + +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penality. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will rougly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols + +SYMBOL_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = YES + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespace are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen +# will list include files with double quotes in the documentation +# rather than with sharp brackets. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen +# will sort the (brief and detailed) documentation of class members so that +# constructors and destructors are listed first. If set to NO (the default) +# the constructors will appear in the respective orders defined by +# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. +# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO +# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST = YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. +# This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. The create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. +# You can optionally specify a file name after the option, if omitted +# DoxygenLayout.xml will be used as the name of the layout file. + +LAYOUT_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = reinc base net string math + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. +# If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. +# Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. +# The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. +# Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. +# Doxygen will adjust the colors in the stylesheet and background images +# according to this color. Hue is specified as an angle on a colorwheel, +# see http://en.wikipedia.org/wiki/Hue for more information. +# For instance the value 0 represents red, 60 is yellow, 120 is green, +# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. +# The allowed range is 0 to 359. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of +# the colors in the HTML output. For a value of 0 the output will use +# grayscales only. A value of 255 will produce the most vivid colors. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to +# the luminance component of the colors in the HTML output. Values below +# 100 gradually make the output lighter, whereas values above 100 make +# the output darker. The value divided by 100 is the actual gamma applied, +# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, +# and 100 does not change the gamma. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting +# this to NO can help when comparing the output of multiple runs. + +HTML_TIMESTAMP = YES + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +HTML_DYNAMIC_SECTIONS = NO + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated +# that can be used as input for Qt's qhelpgenerator to generate a +# Qt Compressed Help (.qch) of the generated HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to +# add. For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see +# +# Qt Help Project / Custom Filters. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's +# filter section matches. +# +# Qt Help Project / Filter Attributes. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files +# will be generated, which together with the HTML files, form an Eclipse help +# plugin. To install this plugin and make it available under the help contents +# menu in Eclipse, the contents of the directory containing the HTML and XML +# files needs to be copied into the plugins directory of eclipse. The name of +# the directory within the plugins directory should be the same as +# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before +# the help appears. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have +# this name. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to YES, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, +# and Class Hierarchy pages using a tree view instead of an ordered list. + +USE_INLINE_TREES = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open +# links to external symbols imported via tag files in a separate window. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are +# not supported properly for IE 6.0, but are supported on all modern browsers. +# Note that when changing this option you need to delete any form_*.png files +# in the HTML output before the changes have effect. + +FORMULA_TRANSPARENT = YES + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box +# for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using +# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets +# (GENERATE_DOCSET) there is already a search function so this one should +# typically be disabled. For large projects the javascript based search engine +# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. + +SEARCHENGINE = YES + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a PHP enabled web server instead of at the web client +# using Javascript. Doxygen will generate the search PHP script and index +# file to put on the web server. The advantage of the server +# based approach is that it scales better to large projects and allows +# full text search. The disadvances is that it is more difficult to setup +# and does not have live searching capabilities. + +SERVER_BASED_SEARCH = NO + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. +# Note that when enabling USE_PDFLATEX this option is only used for +# generating bitmaps for formulas in the HTML output, but not in the +# Makefile that is written to the output directory. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include +# source code with syntax highlighting in the LaTeX output. +# Note that which sources are shown also depends on other settings +# such as SOURCE_BROWSER. + +LATEX_SOURCE_CODE = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. +# This is useful +# if you want to understand what is going on. +# On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is +# allowed to run in parallel. When set to 0 (the default) doxygen will +# base this on the number of processors available in the system. You can set it +# explicitly to a value larger than 0 to get control over the balance +# between CPU load and processing speed. + +DOT_NUM_THREADS = 0 + +# By default doxygen will write a font called FreeSans.ttf to the output +# directory and reference it in all dot files that doxygen generates. This +# font does not include all possible unicode characters however, so when you need +# these (or just want a differently looking font) you can specify the font name +# using DOT_FONTNAME. You need need to make sure dot is able to find the font, +# which can be done by putting it in a standard location or by setting the +# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory +# containing the font. + +DOT_FONTNAME = FreeSans.ttf + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the output directory to look for the +# FreeSans.ttf font (which doxygen will put there itself). If you specify a +# different font using DOT_FONTNAME you can set the path where dot +# can find it using this tag. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = YES + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES diff --git a/cunit/basetest.cpp b/cunit/basetest.cpp new file mode 100644 index 0000000..ff4a23a --- /dev/null +++ b/cunit/basetest.cpp @@ -0,0 +1,42 @@ +/* + * stdtest.cpp + * + * Created on: 05.05.2010 + * Author: wk + */ +#include "../base/rebase.hpp" +#if defined RE_TESTUNIT +extern void testReBase(void); +void testReBase(void){ + try{ + extern void testReProgramArgs(void); + testReProgramArgs(); + extern void testReI18N(void); + testReI18N(); + extern void testReStringList(void); + testReStringList(); + extern void testReSeqList(void); + testReSeqList(); + extern void testReHashList(void); + testReHashList(); + void testReException(void); + testReException(); + void testReStringUtils(void); + testReStringUtils(); + extern void testReVarArgs(void); + testReVarArgs(); + extern void testReByteBuffer(); + testReByteBuffer(); + extern void testReString(void); + testReString(); + extern void testReVarArgs(void); + testReVarArgs(); + extern void testReLogger(void); + testReLogger(); + extern void testReDirectory(void); + testReDirectory(); + } catch (ReException e){ + fprintf(stderr, "unexpected exception: %s\n", e.getMessage()); + } +} +#endif /*RE_TESTUNIT*/ diff --git a/cunit/cuReByteBuffer.cpp b/cunit/cuReByteBuffer.cpp new file mode 100644 index 0000000..927fc17 --- /dev/null +++ b/cunit/cuReByteBuffer.cpp @@ -0,0 +1,298 @@ +/* + * cuReByteBuffer.cpp + * + * Created on: 27.11.2010 + * Author: wk + */ +#include "../base/rebase.hpp" +class TestReByteBuffer : public ReTestUnit { + typedef ReByteBuffer::Byte Byte; +public: + TestReByteBuffer() : ReTestUnit("ReByteBuffer", __FILE__){ + run(); + } +private: + void run(){ + testOpAssignCopyConstructor(); + testAt(); + testEnsureSizeGetLength(); + testBasic(); + testAtoi(); + testIndexOfRIndexOf(); + testInsert(); + testRemove(); + testSplice(); + testReplace(); + } + void testBasic(){ + ReByteBuffer buffer(10); + + buffer.append((Byte*)"123", 3); + checkEqu("123", buffer.getBuffer()); + checkEqu("123", buffer.str()); + checkEqu(3, buffer.getLength()); + buffer.append((Byte*)"ab", 2); + checkEqu("123ab", buffer.getBuffer()); + checkEqu(5, buffer.getLength()); + + buffer.setLengthAndFill(8, 'x'); + checkEqu("123abxxx", buffer.getBuffer()); + checkEqu(8, buffer.getLength()); + buffer.setLength(3); + checkEqu("123", buffer.getBuffer()); + checkEqu(3, buffer.getLength()); + + buffer.setLengthAndFill(511, 'y'); + buffer.setLengthAndFill(512, 'z'); + checkEqu("yyz", buffer.getBuffer() + 509); + checkEqu(521, buffer.getSize()); + + ReByteBuffer buffer2; + buffer2.set("xyz", -1); + buffer.set("abc", -1); + checkEqu("abc", buffer.getBuffer()); + checkEqu(3, buffer.getLength()); + buffer.append(buffer2); + checkEqu("abcxyz", buffer.getBuffer()); + checkEqu("abcxyz", buffer.str()); + checkEqu(6, buffer.getLength()); + + buffer.setLength(0); + buffer.appendInt(-1); + checkEqu("-1", buffer.str()); + checkEqu(2, buffer.getLength()); + + buffer.appendInt(9, "%03d"); + checkEqu("-1009", buffer.str()); + checkEqu(5, buffer.getLength()); + } + void testOpAssignCopyConstructor() { + ReByteBuffer buf1; + buf1.set("abc", 3); + ReByteBuffer buf3; + { + ReByteBuffer buf2(buf1); + checkEqu("abc", buf2.str()); + buf3 = buf2; + buf2.append("123", 3); + } + checkEqu("abc", buf3.str()); + checkEqu("abc", buf1.str()); + } + void testAt(){ + ReByteBuffer buf1; + buf1.set("abc", 3); + for (size_t ii = 0; ii < buf1.getLength(); ii++){ + checkEqu('a' + ii, buf1.at(ii)); + } + checkEqu(0, buf1.at(-1)); + checkEqu(0, buf1.at(3)); + checkEqu(0, buf1.at(4)); + } + void testAtoi(){ + ReByteBuffer buffer; + buffer.set("y1234", -1); + + checkEqu(1, buffer.atoi(1, 2)); + checkEqu(12, buffer.atoi(1, 3)); + checkEqu(123, buffer.atoi(1, 4)); + checkEqu(1234, buffer.atoi(1, 5)); + checkEqu(1234, buffer.atoi(1, 6)); + checkEqu(0, buffer.atoi(0, 6)); + + buffer.set("456y", -1); + + checkEqu(4, buffer.atoi(0, 1)); + checkEqu(45, buffer.atoi(0, 2)); + checkEqu(456, buffer.atoi(0, 3)); + checkEqu(456, buffer.atoi(0, 4)); + checkEqu(0, buffer.atoi(4, 6)); + + buffer.set("987654321", -1); + checkEqu(987654321, buffer.atoi()); + + buffer.set("187654302ab", -1); + checkEqu(187654302, buffer.atoi()); + + + buffer.set("y0x1234", -1); + + checkEqu(0x1, buffer.atoi(1, 4)); + checkEqu(0x12, buffer.atoi(1, 5)); + checkEqu(0x123, buffer.atoi(1, 6)); + checkEqu(0x1234, buffer.atoi(1, 7)); + checkEqu(0x1234, buffer.atoi(1, 8)); + checkEqu(0, buffer.atoi(0, 8)); + checkEqu(0, buffer.atoi(2, 8)); + + buffer.set("0x456y", -1); + + checkEqu(0x4, buffer.atoi(0, 3)); + checkEqu(0x45, buffer.atoi(0, 4)); + checkEqu(0x456, buffer.atoi(0, 6)); + checkEqu(0x456, buffer.atoi(0, 7)); + checkEqu(0, buffer.atoi(5, 6)); + + buffer.set("0x98765432", -1); + checkEqu(0x98765432, buffer.atoi()); + + buffer.set("0xabcdef01", -1); + checkEqu(0xabcdef01, buffer.atoi()); + buffer.set("0xABCDEF01", -1); + checkEqu(0xabcdef01, buffer.atoi()); + buffer.set("0xaFFe01", -1); + checkEqu(0xaFFe01, buffer.atoi()); + } + void testEnsureSizeGetLength(){ + ReByteBuffer buf1; + buf1.ensureSize(2000); + checkEqu(2000, buf1.getSize()); + buf1.set("0123456789", 10); + checkEqu(10, buf1.getLength()); + + buf1.setLength(5); + checkEqu("01234", buf1.str()); + checkEqu(5, buf1.getLength()); + + buf1.setLengthAndFill(8, 'X'); + checkEqu("01234XXX", buf1.str()); + checkEqu(8, buf1.getLength()); + checkEqu(2000, buf1.getSize()); + } + + void testIndexOfRIndexOf(){ + ReByteBuffer buffer; + buffer.set("123123", -1); + checkEqu(0, buffer.indexOf('1')); + checkEqu(1, buffer.indexOf('2')); + checkEqu(3, buffer.indexOf('1', 1)); + checkEqu(2, buffer.indexOf('3', 2)); + checkEqu(5, buffer.indexOf('3', 3)); + checkEqu(-1, buffer.indexOf('4')); + checkEqu(-1, buffer.indexOf('1', 4)); + + checkEqu(3, buffer.rindexOf('1')); + checkEqu(4, buffer.rindexOf('2')); + checkEqu(3, buffer.rindexOf('1', -2)); + checkEqu(3, buffer.rindexOf('1', -3)); + checkEqu(0, buffer.rindexOf('1', -4)); + + checkEqu(4, buffer.rindexOf('2', 4)); + checkEqu(1, buffer.rindexOf('2', 3)); + + checkEqu(4, buffer.rindexOf('2', -2)); + checkEqu(1, buffer.rindexOf('2', -3)); + + checkEqu(0, buffer.rindexOf('1', 0)); + + checkEqu(-1, buffer.rindexOf('4')); + checkEqu(-1, buffer.rindexOf('2', 0)); + + checkEqu(1, buffer.indexOf("23", 2, 0)); + checkEqu(1, buffer.indexOf("23", -1, 0)); + checkEqu(1, buffer.indexOf("23x", 2, 0)); + checkEqu(4, buffer.indexOf("23", 2, 2)); + checkEqu(4, buffer.indexOf("23", -1, 2)); + + checkEqu(-1, buffer.indexOf("234", -1, 0)); + checkEqu(-1, buffer.indexOf("23", -1, 5)); + } + void testInsert(){ + ReByteBuffer buf1; + checkT(buf1.insert(0, "123", 2)); + checkEqu("12", buf1.str()); + checkEqu(2, buf1.getLength()); + + checkT(buf1.insert(0, "abc", 1)); + checkEqu("a12", buf1.str()); + checkEqu(3, buf1.getLength()); + + checkT(buf1.insert(1, "x", 1)); + checkEqu("ax12", buf1.str()); + checkEqu(4, buf1.getLength()); + + checkT(buf1.insert(4, "yz", 2)); + checkEqu("ax12yz", buf1.str()); + checkEqu(6, buf1.getLength()); + + checkF(buf1.insert(-1, "-", 1)); + checkF(buf1.insert(8, "/", 1)); + + } + void testRemove(){ + ReByteBuffer buf1; + buf1.set("1234567890", 10); + checkT(buf1.remove(0, 2)); + checkEqu("34567890", buf1.str()); + checkEqu(8, buf1.getLength()); + + checkF(buf1.remove(-1, 2)); + checkEqu("34567890", buf1.str()); + checkEqu(8, buf1.getLength()); + + checkF(buf1.remove(9, 2)); + checkEqu("34567890", buf1.str()); + checkEqu(8, buf1.getLength()); + + checkT(buf1.remove(7, 2)); + checkEqu("3456789", buf1.str()); + checkEqu(7, buf1.getLength()); + + checkT(buf1.remove(5, 2)); + checkEqu("34567", buf1.str()); + checkEqu(5, buf1.getLength()); + + checkT(buf1.remove(0, 99)); + checkEqu("", buf1.str()); + checkEqu(0, buf1.getLength()); + } + void testReplace(){ + ReByteBuffer buffer; + + buffer.set("1234", 4); + buffer.replaceAll("12", 2, "abc", 3); + checkEqu("abc34", buffer.str()); + + buffer.replaceAll("c34", 2, "uv", 3); + checkEqu("abuv", buffer.str()); + + buffer.set("$$x$$", -1); + buffer.replaceAll("$", 1, "XX", 2); + checkEqu("XXXXxXXXX", buffer.str()); + } + void testSplice(){ + ReByteBuffer buffer; + + buffer.append((Byte*) "12.ab", 5); + checkT(buffer.splice(0, 0, "xy", 2)); + checkEqu("xy12.ab", buffer.getBuffer()); + buffer.splice(2, 2, NULL, 0); + checkEqu("xy.ab", buffer.getBuffer()); + buffer.splice(0, 2, "w", 1); + checkEqu("w.ab", buffer.getBuffer()); + + buffer.setLength(0); + buffer.append((Byte*) "123", 3); + buffer.insert(1, "ab", 2); + checkEqu("1ab23", buffer.getBuffer()); + checkEqu(5, buffer.getLength()); + buffer.remove(0, 1); + checkEqu("ab23", buffer.getBuffer()); + checkEqu(4, buffer.getLength()); + buffer.remove(3, 55); + checkEqu("ab2", buffer.getBuffer()); + checkEqu(3, buffer.getLength()); + buffer.remove(2, 2); + checkEqu("ab", buffer.getBuffer()); + checkEqu(2, buffer.getLength()); + buffer.remove(1, 1); + checkEqu("a", buffer.getBuffer()); + checkEqu(1, buffer.getLength()); + } +}; +extern void testReByteBuffer(void); + +void testReByteBuffer(void){ + TestReByteBuffer unit; +} + diff --git a/cunit/cuReCString.cpp b/cunit/cuReCString.cpp new file mode 100644 index 0000000..ae5aaa8 --- /dev/null +++ b/cunit/cuReCString.cpp @@ -0,0 +1,39 @@ +/* + * cuReCString.cpp + * + * Created on: 23.11.2010 + * Author: wk + */ +#include "../base/restring.hpp" + +class TestReString : public ReTestUnit { +public: + TestReString() : ReTestUnit("ReString", __FILE__){ + run(); + } +private: + void run(){ + testReplaceSubstring(); + } + void testReplaceSubstring(){ + char buffer[256]; + + strcpy(buffer, "12.ab"); + replaceSubstring(buffer, sizeof buffer, 0, "xy"); + checkEqu("xy12.ab", buffer); + replaceSubstring(buffer + 2, sizeof buffer - 2, 2, ""); + checkEqu("xy.ab", buffer); + replaceSubstring(buffer, sizeof buffer, 2, "w"); + checkEqu("w.ab", buffer); + log(false, "1 error expected!"); + replaceSubstring(buffer, 5, 0, "x"); + replaceSubstring(buffer, 6, 0, "x"); + checkEqu("xw.ab", buffer); + } +}; +extern void testReString(void); + +void testReString(void){ + TestReString unit; +} + diff --git a/cunit/cuReTraverser.cpp b/cunit/cuReTraverser.cpp new file mode 100644 index 0000000..7ad4b2e --- /dev/null +++ b/cunit/cuReTraverser.cpp @@ -0,0 +1,31 @@ +/* + * cuReTraverser.cpp + * + * Created on: 23.12.2014 + * Author: hm + */ + +#include "base/rebase.hpp" +#include "os/reos.hpp" + +class TestReTraverser : public ReTestUnit { +public: + TestReTraverser() : ReTestUnit("ReTraverser", __FILE__){ + run(); + } +private: + void run(){ + testReplaceSubstring(); + } + void testReplaceSubstring(){ + ReTraverser traverser("/tmp/test"); + int level = 0; + const DirStatus_t* entry = traverser.nextFile(level); + checkEqu("xy12.ab", nameOfEntry(entry)); + } +}; +extern void testReTraverser(void); + +void testReTraverser(void){ + TestReTraverser unit; +} diff --git a/cunit/testall.cpp b/cunit/testall.cpp new file mode 100644 index 0000000..6635971 --- /dev/null +++ b/cunit/testall.cpp @@ -0,0 +1,68 @@ +/* + * testall.cpp + * + * Created on: 24.11.2010 + * Author: wk + */ +#include "base/rebase.hpp" +#include "os/reos.hpp" +#include "net/renet.hpp" + +void testBase(){ + extern void testReByteBuffer(); + testReByteBuffer(); + extern void testReSeqList(void); + testReSeqList(); + extern void testReHashList(void); + testReHashList(); + void testReException(void); + testReException(); + extern void testReVarArgs(void); + testReVarArgs(); + extern void testReString(void); + testReString(); + extern void testReVarArgs(void); + testReVarArgs(); + extern void testReLogger(void); + testReLogger(); + extern void testReDirectory(void); + testReDirectory(); + extern void testReProgramArgs(void); + testReProgramArgs(); +} +void testString(){ + void testReString(); + testReString(); + extern void testReI18N(void); + testReI18N(); + extern void testReStringList(void); + testReStringList(); + void testReStringUtils(void); + testReStringUtils(); + extern void testReString(void); + testReString(); +} +void testOs(){ + void testReTraverser(); + testReTraverser(); +} +void testAll(){ + try + { + testOs(); + testBase(); + testString(); + } catch (ReException e){ + fprintf(stderr, "testBase.cpp: unexpected exception: %s\n", e.getMessage()); + } + +} +void testCurrent(){ + try + { + void testReByteBuffer(); + testReByteBuffer(); + } catch (ReException e){ + fprintf(stderr, "testBase.cpp: unexpected exception: %s\n", e.getMessage()); + } +} diff --git a/math/ReObfuscator.cpp b/math/ReObfuscator.cpp new file mode 100644 index 0000000..8bd4e70 --- /dev/null +++ b/math/ReObfuscator.cpp @@ -0,0 +1,208 @@ +/* + * ReObfuscator.cpp + * + * Created on: 13.11.2010 + * Author: wk + */ + +#include "base/rebase.hpp" +#include "base/remath.hpp" + +/** @brief Constructor. + * + * @param rand Random generator. + * @param charSet Specifies the character set which will be en/decoded. + */ +ReObfuscator::ReObfuscator(ReRandomizer& rand, CharSet charSet) : + m_rand(rand), + m_charSet(charSet), + m_min(0), + m_max(0), + m_range(0) +{ + switch(charSet){ + case CHAR96: + m_min = ' '; + m_max = 127; + m_range = 96; + break; + case CHAR224: + m_min = ' '; + m_max = 255; + m_range = 224; + break; + case CHAR256: + m_min = 0; + m_max = 255; + m_range = 256; + break; + } +} + +/** @brief Destructor. + */ +ReObfuscator::~ReObfuscator() { +} + +/** @brief Decodes one character. + * + * @param cc The character to decode. + * + * @return The decoded character. + */ +char ReObfuscator::decode(char cc) const{ + char rc; + int src = (int) (unsigned char) cc; + switch(m_charSet){ + case CHAR96: + case CHAR224: + if (src < m_min || src > m_max) + rc = cc; + else { + src = (src - m_min) - m_rand.nextInt(0, m_range - 1); + if (src < 0) + src += m_range; + rc = (char) m_min + src; + } + break; + case CHAR256: + rc = (char) (src + (src + m_rand.nextInt(0, m_range - 1) % m_range)); + break; + default: + rc = (char) src; + break; + } + return rc; + +} + +/** @brief Decodes a string. + * + * Decodes every character of a given string in place. + * + * @param buffer In/Out: The string to encode. + * @param bufferLength -1: buffer contains a 0-terminated string. + * Otherwise: The number of character to decode. + * + * @return The buffer. + */ +char* ReObfuscator::decode(char* buffer, int bufferLength) const { + if(bufferLength == -1) + bufferLength = strlen(buffer); + for (int ii = 0; ii < bufferLength; ii++) + buffer[ii] = decode(buffer[ii]); + return buffer; +} + +/** @brief Encodes one character. + * + * @param cc The character to encode. + * + * @return The encoded character. + */ +char ReObfuscator::encode(char cc) const{ + char rc; + int src = (int) (unsigned char) cc; + switch(m_charSet) + { + case CHAR96: + case CHAR224: + if (src < m_min || src > m_max) + rc = cc; + else + rc = char (((src - m_min) + m_rand.nextInt(0, m_range - 1)) % m_range + m_min); + break; + case CHAR256: + rc = (char) (src + (src + m_rand.nextInt(0, m_range - 1) % m_range)); + break; + default: + rc = (char) src; + break; + } + return rc; +} + +/** @brief Encodes a string. + * + * Encodes every character of a given string in place. + * + * @param buffer In/Out: The string to encode. + * @param bufferLength -1: buffer contains a 0-terminated string. + * Otherwise: The number of character to encode. + * + * @return The buffer. + */ +char* ReObfuscator::encode(char* buffer, int bufferLength) const{ + if(bufferLength == -1) + bufferLength = strlen(buffer); + for (int ii = 0; ii < bufferLength; ii++) + buffer[ii] = encode(buffer[ii]); + return buffer; +} + +/** @brief Returns the current character set. + * + * @return The current character set. + */ +ReObfuscator::CharSet ReObfuscator::getCharSet() const{ + return m_charSet; +} + +/** @brief Returns the random generator + * + * @return The random generator. + */ +ReRandomizer& ReObfuscator::getRandomizer() const{ + return m_rand; +} + +/** @brief Sets the character set. + * + * @param newSet The character set to set. + */ +void ReObfuscator::setCharSet(CharSet newSet){ + m_charSet = newSet; +} + +static void testOne(const char* src, int bufferLength, + ReObfuscator& obf, ReCongruentialGenerator& rand){ + char buffer[256+1]; + assert((size_t) bufferLength < sizeof buffer); + memcpy(buffer, src, bufferLength); + buffer[bufferLength] = '\0'; + rand.setSeed(0x4711); + obf.encode(buffer, bufferLength); + char buffer2[256+1]; + memcpy(buffer2, buffer, bufferLength); + buffer2[bufferLength] = '\0'; + rand.setSeed(0x4711); + obf.decode(buffer2, bufferLength); + if (memcmp(buffer2, src, bufferLength) != 0) + printf("%3d:%s\n -> %s\n -> %s\n", (int) obf.getCharSet(), src, buffer, buffer2); +} +static void testIt(const char* src, int bufferLength, + ReObfuscator& obf, ReCongruentialGenerator& rand){ + if (bufferLength == -1) + bufferLength = strlen(src); + obf.setCharSet(ReObfuscator::CHAR96); + testOne(src, bufferLength, obf, rand); + obf.setCharSet(ReObfuscator::CHAR224); + testOne(src, bufferLength, obf, rand); + obf.setCharSet(ReObfuscator::CHAR256); + testOne(src, bufferLength, obf, rand); +} + +void testObfuscator(){ + ReCongruentialGenerator rand; + ReObfuscator obf(rand); + + //testOne(" ", obf, rand); + testIt("ABC", -1, obf, rand); + testIt("Hello Dolly!", -1, obf, rand); + testIt(" !§$%&/(()=`öäü1234\r\n\t\nabcdefghi", -1, obf, rand); + char line[256]; + for (int ii = 0; ii < 255; ii++) + line[ii] = ii + 1; + line[255] = '\0'; + testIt(line, 256, obf, rand); +} diff --git a/math/ReObfuscator.hpp b/math/ReObfuscator.hpp new file mode 100644 index 0000000..38f5fa9 --- /dev/null +++ b/math/ReObfuscator.hpp @@ -0,0 +1,39 @@ +/* + * ReObfuscator.h + * + * Created on: 13.11.2010 + * Author: wk + */ + +#ifndef REOBFUSCATOR_H_ +#define REOBFUSCATOR_H_ + +#include "math/ReRandomizer.hpp" + +class ReObfuscator { +public: + enum CharSet { + CHAR96 = 96, + CHAR224 = 224, + CHAR256 = 256, + }; +public: + ReObfuscator(ReRandomizer& rand, CharSet charSet = CHAR96); + virtual ~ReObfuscator(); +public: + char encode(char cc) const; + char decode(char cc) const; + char* encode(char* buffer, int bufferLength = -1) const; + char* decode(char* buffer, int bufferLength = -1) const; + void setCharSet(CharSet newSet); + CharSet getCharSet() const; + ReRandomizer& getRandomizer() const; +private: + ReRandomizer& m_rand; + CharSet m_charSet; + int m_min; + int m_max; + int m_range; +}; + +#endif /* REOBFUSCATOR_H_ */ diff --git a/math/ReRandomizer.cpp b/math/ReRandomizer.cpp new file mode 100644 index 0000000..552b750 --- /dev/null +++ b/math/ReRandomizer.cpp @@ -0,0 +1,234 @@ +/* + * ReRandomizer.cpp + * + * Created on: 01.11.2010 + * Author: wk + */ + +#include "assert.h" +#include "memory.h" +#include "limits.h" +#include "math.h" +#include "remath.hpp" + +static bool s_trace = false; +/** + * @brief Constructor. + */ +ReRandomizer::ReRandomizer(){ +} +/** + * @brief Destructor. + */ +ReRandomizer::~ReRandomizer() { +} + +inline int abs(int x) { return x < 0 ? -x : x; } + +/** + * @brief Returns the next random character. + * + * The character is in the range ' ' .. chr(127) (inclusive). + * + * @return The next random character. + */ +char ReRandomizer::nextChar() +{ + char rc = nextInt(' ', ' ' + CHARRANGE - 1); + return rc; +} + +/** + * @brief Returns the next random integer. + * + * + * @param minValue The minimum of the result. + * @param maxValue The maximum of the result (including). + * + * @return The next seed. + */ +int ReRandomizer::nextInt(int minValue, int maxValue){ + int rc; + if (minValue > maxValue){ + rc = minValue; + minValue = maxValue; + maxValue = rc; + } + seed_t seed = nextSeed(); + if (minValue == maxValue) + rc = minValue; + else if (minValue == 0 && maxValue == INT_MAX) + rc = abs(seed); + else if (unsigned(maxValue - minValue) < INT_MAX) + rc = minValue + seed % (maxValue - minValue + 1); + else { + double rc2 = minValue + fmod((double) seed, maxValue - minValue); + rc = (int) rc2; + } + if (s_trace){ + static int count = 0; + printf ("%c %8x ", count++ % 4 == 0 ? '\n' : ' ', rc); + } + return rc; +} + +/** + * @brief Calculates the next seed for the generator. + * + * @return The next seed. + */ +ReRandomizer::seed_t ReCongruentialGenerator::nextSeed(){ + m_seed = m_seed * m_factor + m_increment; + return m_seed; +} + +/** + * @brief Returns a string with random characters. + * + * All character will be inside the range ' ' .. chr(127). + * + * @param buffer Out: The place for the string. + * @param maxLength The maximum length of the result string. + * @param minLength The minimum length of the result string. + * + * @result The buffer. + */ +char* ReRandomizer::nextString(char* buffer, int maxLength, int minLength){ + int len = nextInt(minLength, maxLength); + for (int ii = 0; ii < len; ii++){ + buffer[ii] = nextChar(); + } + buffer[len] = '\0'; + return buffer; +} +/** + * @brief Builds a random permutation of an array. + * + * The operation will be done in place: + * + * @param array In/Out: The array to shuffle. + * @param length The length of the array. + * @param elemSize The size of one element of the array. + */ +void ReRandomizer::shuffle(void* array, size_t length, size_t elemSize){ + int ii; + char* cptr; + int* iptr; + int count = length * 3 / 2; + + switch(elemSize) + { + case 1: + cptr = (char*) array; + for (ii = 0; ii < count; ii++){ + int ix1 = length - nextInt(1, length); + int ix2 = length - nextInt(1, length); + char x = cptr[ix1]; + cptr[ix1] = cptr[ix2]; + cptr[ix2] = x; + } + break; + case sizeof(int): + iptr = (int*) array; + for (ii = 0; ii < count; ii++){ + int ix1 = length - nextInt(1, length); + int ix2 = length - nextInt(1, length); + int x = iptr[ix1]; + iptr[ix1] = iptr[ix2]; + iptr[ix2] = x; + } + break; + default: + { + char buffer[0x10000]; + assert(elemSize < sizeof buffer); + iptr = (int*) array; + for (ii = 0; ii < count; ii++){ + int ix1 = length - nextInt(1, length); + int ix2 = length - nextInt(1, length); + char* p1 = ((char*) array) + ix1 * elemSize; + char* p2 = ((char*) array) + ix2 * elemSize; + memcpy(buffer, p1, elemSize); + memcpy(p1, p2, elemSize); + memcpy(p2, buffer, elemSize); + } + break; + } + } +} +/** + * @brief Constructor. + */ +ReCongruentialGenerator::ReCongruentialGenerator() : + m_seed(0x4711), + m_factor(2631), + m_increment(0x9) +{ +} +/** + * @brief Destructor. + */ +ReCongruentialGenerator::~ReCongruentialGenerator(){ +} + +/** + * @brief Returns the current factor. + * + * @return The current factor. + */ +ReRandomizer::seed_t ReCongruentialGenerator::getFactor() const +{ + return m_factor; +} + +/** + * @brief Returns the current increment. + * + * @return The current increment. + */ +ReRandomizer::seed_t ReCongruentialGenerator::getIncrement() const +{ + return m_increment; +} + +/** + * Returns the current seed. + * + * @return The current seed. + */ +ReRandomizer::seed_t ReCongruentialGenerator::getSeed() const +{ + return m_seed; +} + +/** + * Sets the factor. + * + * @param factor The new factor. + */ +void ReCongruentialGenerator::setFactor(seed_t factor) +{ + this->m_factor = factor; +} + +/** + * Sets the increment. + * + * @param increment The new increment. + */ +void ReCongruentialGenerator::setIncrement(seed_t increment) +{ + this->m_increment = increment; +} + +/** @brief Sets the current seed. + * + * @param seed The seed to set. + */ +void ReCongruentialGenerator::setSeed(seed_t seed) +{ + this->m_seed = seed; + if (s_trace) + printf(" Seed: %x ", seed); +} + diff --git a/math/ReRandomizer.hpp b/math/ReRandomizer.hpp new file mode 100644 index 0000000..958649b --- /dev/null +++ b/math/ReRandomizer.hpp @@ -0,0 +1,57 @@ +/* + * ReRandomizer.h + * + * Created on: 01.11.2010 + * Author: wk + */ + +#ifndef RANDOMIZER_H_ +#define RANDOMIZER_H_ +#include "stdio.h" +#include "limits.h" + +/** + * This implements an abstract base class for random generators. + */ +class ReRandomizer { +public: + enum { CHARRANGE = 128 - ' ' }; + typedef unsigned int seed_t; +public: + ReRandomizer(); + virtual ~ReRandomizer(); +public: + virtual int nextInt(int minValue = 0, int maxValue = INT_MAX); + void shuffle(void* array, size_t length, size_t elemSize); + char nextChar(); + char* nextString(char* buffer, int maxLength = 80, int minLength = 0); +protected: + virtual seed_t nextSeed() = 0; +}; + +/** + * Implements a simple random generator. + * A linear congruential generator produces the next value using this formula: + * seed = (seed * factor + increment) % modulus + * In this implementation modulus is 2**32. + */ +class ReCongruentialGenerator : public ReRandomizer { +public: + ReCongruentialGenerator(); + virtual ~ReCongruentialGenerator(); +public: + seed_t getFactor() const; + seed_t getIncrement() const; + void setFactor(seed_t factor); + void setIncrement(seed_t increment); + seed_t getSeed() const; + void setSeed(seed_t m_seed); +private: + virtual seed_t nextSeed(); +private: + seed_t m_seed; + seed_t m_factor; + seed_t m_increment; +}; + +#endif /* RANDOMIZER_H_ */ diff --git a/math/remath.hpp b/math/remath.hpp new file mode 100644 index 0000000..d3b64bc --- /dev/null +++ b/math/remath.hpp @@ -0,0 +1,14 @@ +/* + * remath.hpp + * + * Created on: 23.12.2014 + * Author: hm + */ + +#ifndef REMATH_HPP_ +#define REMATH_HPP_ + +#include "ReObfuscator.hpp" +#include "ReRandomizer.hpp" + +#endif /* REMATH_HPP_ */ diff --git a/net/ReUdpConnection.cpp b/net/ReUdpConnection.cpp new file mode 100644 index 0000000..6c2ad5b --- /dev/null +++ b/net/ReUdpConnection.cpp @@ -0,0 +1,275 @@ +/* + * ReUdpServer.cpp + * + * Created on: 12.11.2010 + * Author: wk + */ +#include "../base/renet.hpp" + +/** @brief Constructor. + * + * param verbose TRUE: + */ +ReUdpConnection::ReUdpConnection(bool isServer, ReLogger* logger) : + m_socket(0), + //m_address + m_buffer(8096), + m_port(-1), + m_logger(logger), + m_isServer(isServer) +{ + memset(&m_address, 0, sizeof m_address); + if ((m_socket = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { + m_logger->sayF(LOG_ERROR|GRAN_USER|CAT_NETWORK, LC_UDPCONNECTION_CONSTRUCT, + i18n("Cannot creat a socket: $1")).arg(strerror(errno)).end(); + exit(1); + } +} +/** @brief Destructor. + * + * Closes the connection. + */ +ReUdpConnection::~ReUdpConnection() { + close(); +} +/** @brief Returns the ip address as a string. + * + * @return The IP address as string. + */ +const char* ReUdpConnection::getAddress() const { + const char* rc = inet_ntoa(m_address.sin_addr); + return rc; +} +/** @brief Receives an UDP message. + * + * There are two modes: + *
  • Blocking: The method waits until the next message.
  • + *
  • Non blocking: The method returns when a message has been received + * or the timeout has reached.
  • + *
+ * + * @param timeout 0: Blocking mode. + * Otherwise: The timeout in milliseconds. + * @param buffer NULL: The internal buffer will be used. + * Otherwise: The buffer for the received message. + * + * @return 0: No message has been received. + * Otherwise: The length of the received message. + * + */ +int ReUdpConnection::receive(int timeout, ReByteBuffer* buffer, bool doLog){ + socklen_t addrLength = sizeof m_address; + bool doRead = true; + if (buffer == NULL){ + buffer = &m_buffer; + } + if (timeout > 0){ + int maxfd = m_socket; + fd_set rdset; + bzero(&rdset, sizeof rdset); + FD_SET(m_socket, &rdset); + timeval timeval; + timeval.tv_sec = timeout / 1000; + timeval.tv_usec = timeout % 1000 * 1000; + select(maxfd+1, &rdset, NULL, NULL, &timeval); + doRead = FD_ISSET(m_socket, &rdset); + } + buffer->ensureSize(8096 + 1); + size_t size = buffer->getSize(); + buffer->setLength(size); + if (!doRead) + m_logger->sayF(LOG_WARNING|GRAN_USER|CAT_NETWORK, LC_UDPCONNECTION_RECEIVE_2, + i18n("Nothing received since $1 sec")).arg(timeout/1000.0, "%.3f").end(); + else { + int length = recvfrom( + m_socket, + buffer->getBuffer(), size - 1, 0, + (struct sockaddr *) &m_address, + &addrLength); + buffer->setLength(length >= 0 ? length : 0); + if (doLog) + m_logger->sayF(LOG_INFO|GRAN_USER|CAT_NETWORK, LC_UDPCONNECTION_RECEIVE_1, + "$1:$2 $3").arg(getAddress()).arg(m_port) + .arg(buffer->getBuffer()).end(); + } + return buffer->getLength(); +} +/** @brief Sends a message. + * + * @param buffer The buffer to send. + * @param bufferLength The length of buffer[]. + * + * @return > 0: Failure. + * Otherwise: The number of sent bytes. + * + */ +int ReUdpConnection::send(const char* buffer, int bufferLength){ + socklen_t size = sizeof (m_address); + if (bufferLength == -1) + bufferLength = strlen(buffer); + int rc = sendto(m_socket, buffer, bufferLength, 0, + (sockaddr*)&m_address, size); + if (rc <= 0) + m_logger->sayF(LOG_ERROR|GRAN_USER|CAT_NETWORK, LC_UDPCONNECTION_SEND_1, + i18n("Sending failed: $1:$2 $3")) + .arg(getAddress()).arg(m_port).arg(strerror(errno)).end(); + else + m_logger->sayF(LOG_INFO|GRAN_USER|CAT_NETWORK, LC_UDPCONNECTION_SEND_2, + i18n("$1 bytes sent")).arg(rc).end(); + return rc; +} +/** @brief Returns the internal receive buffer. + * + * @return The internal buffer. + */ +ReByteBuffer& ReUdpConnection::getBuffer(){ + return m_buffer; +} + +/** @brief Closes the connection. + */ +void ReUdpConnection::close(){ + if (m_socket != 0) + { + m_logger->sayF(LOG_INFO|GRAN_USER|CAT_NETWORK, LC_UDPCONNECTION_CLOSE_1, + i18n("Connection has been closed: $1:$2")).arg(getAddress()).arg(m_port).end(); + if (::close(m_socket) != 0) + m_logger->sayF(LOG_ERROR|GRAN_USER|CAT_NETWORK, LC_UDPCONNECTION_CLOSE_2, + i18n("socket close failed: $1")).arg(strerror(errno)).end(); + m_socket = 0; + } +} + +/** @brief Constructor. + * + * @param logger Logger. + */ +ReUdpServer::ReUdpServer(ReLogger* logger) : + ReUdpConnection(true, logger) +{ +} + +/** @brief Destructor. + */ +ReUdpServer::~ReUdpServer() { +} + +/** @brief Initializes a connection. + * + * @param port The port to connect. + * + * @return true: Success. false: Connection failed. + */ + +bool ReUdpServer::connect(int port){ + bool rc = true; + m_address.sin_family = AF_INET; + m_address.sin_port = htons(port); + m_address.sin_addr.s_addr = INADDR_ANY; + bzero(&(m_address.sin_zero), 8); + + if (bind(m_socket,(struct sockaddr *)&m_address, + sizeof(struct sockaddr)) == -1) + { + m_logger->sayF(LOG_ERROR|GRAN_USER|CAT_NETWORK, LC_UDPCONNECTION_CONNECT_1, + i18n("bind() failed on port $1: $2")).arg(port).arg(strerror(errno)).end(); + rc = false; + } + else + m_logger->sayF(LOG_INFO|GRAN_USER|CAT_NETWORK, LC_UDPCONNECTION_CONNECT_2, + i18n("Waiting for client on port $1")).arg(port).end(); + + m_port = port; + return rc; +} +/** @brief Constructor. + * + * @param logger Logger. + */ +ReUdpClient::ReUdpClient(ReLogger* logger) : + ReUdpConnection(false, logger){ +} + +/** @brief Destructor. + */ +ReUdpClient::~ReUdpClient() { +} + +/** @brief Initializes a UDP connection. + * + * @param ip The IP address as string. + * @param port The port of the connection. + * + * @param return true: Success. + */ +bool ReUdpClient::connect(const char* ip, int port){ + struct hostent *host; + bool rc = false; + host= (struct hostent *) gethostbyname((char *) ip); + + if (host == NULL){ + m_logger->sayF(LOG_ERROR|GRAN_USER|CAT_NETWORK, LC_UDPCONNECTION_CONNECT_1, + i18n("Invalid IP: $1")).arg(ip).end(); + } else { + m_address.sin_family = AF_INET; + m_address.sin_port = htons(port); + m_address.sin_addr = *((struct in_addr *)host->h_addr); + bzero(&(m_address.sin_zero),8); + + m_logger->sayF(LOG_INFO|GRAN_USER|CAT_NETWORK, LC_UDPCONNECTION_CONNECT_2, + i18n("Connected to $1:$2")).arg(ip).arg(port).end(); + m_port = port; + rc = true; + } + return rc; +} + +/** @brief Constructor. + * + * @param port The port of the connection. + * @param logger Logger. + */ +ReUdpMaster::ReUdpMaster(int port, ReLogger* logger) : + ReUdpServer(logger) +{ + connect(port); +} + +/** @brief Destructor. + */ +ReUdpMaster::~ReUdpMaster(){ +} +/** @brief Tests whether the message can be logged. + * + * This is a simple default method returning always true. + * A deriving class can overwrite to log some messages and + * hide others. + * + * @param message The message to test. + * + * @return true: The message should be logged. + * false: Otherwise. + */ +bool ReUdpMaster::canLog(ReByteBuffer& message){ + return true; +} + +/** @brief Receives UDP messages and send answers. + */ +void ReUdpMaster::run(){ + ReByteBuffer answer; + bool again = true; + while(again){ + receive(0, NULL, false); + answer.setLength(0); + if (canLog(m_buffer)) + m_logger->sayF(LOG_INFO|GRAN_USER|CAT_NETWORK, LC_UDPCONNECTION_RUN_1, + "$1:$2 $3").arg(getAddress()).arg(m_port) + .arg(m_buffer.getBuffer()).end(); + + again = handlePage(m_buffer, answer, *this); + if (answer.getLength() > 0){ + send(answer.getBuffer(), answer.getLength()); + } + } +} diff --git a/net/ReUdpConnection.hpp b/net/ReUdpConnection.hpp new file mode 100644 index 0000000..2b729d1 --- /dev/null +++ b/net/ReUdpConnection.hpp @@ -0,0 +1,85 @@ +/* + * ReUdpServer.h + * + * Created on: 12.11.2010 + * Author: wk + */ + +#ifndef UDPSERVER_H_ +#define UDPSERVER_H_ + +#include +#include +#include +#include +#include + +/** + * Implements a base class for UDP server and client. + */ +class ReUdpConnection { +public: + ReUdpConnection(bool isServer, ReLogger* logger); + virtual ~ReUdpConnection(); +public: + const char* getAddress() const; + int getPort() const + { return m_port; } + int receive(int timeout = 0, ReByteBuffer* buffer = NULL, bool doLog = true); + int send(const char* buffer, int bufferLength = -1); + void close(); + ReByteBuffer& getBuffer(); +protected: + int m_socket; + struct sockaddr_in m_address; + ReByteBuffer m_buffer; + int m_port; + time_t m_lastReceipt; + ReLogger* m_logger; + bool m_isServer; +}; + +/** + * Implements the base functionality of a server + * handling the UDP protocol. + */ +class ReUdpServer : public ReUdpConnection { +public: + ReUdpServer(ReLogger* logger); + virtual ~ReUdpServer(); +public: + bool connect(int port); +}; + +/** + * Implements an abstract base class for a server + * handling the UDP protocol. + * A real server must implement only the method + * handlePage(). + */ +class ReUdpMaster : public ReUdpServer +{ +public: + ReUdpMaster(int port, ReLogger* logger); + virtual ~ReUdpMaster(); + virtual bool canLog(ReByteBuffer& message); +public: + void run(); +protected: + virtual bool handlePage(ReByteBuffer& buffer, + ReByteBuffer& answer, ReUdpMaster& server) = 0; +}; + +/** + * Implements a client handling the UDP protocol. + */ +class ReUdpClient : public ReUdpConnection { +public: + ReUdpClient(ReLogger* logger); + virtual ~ReUdpClient(); +public: + bool connect(const char* ip, int port); +}; + + +#endif /* UDPSERVER_H_ */ diff --git a/net/renet.hpp b/net/renet.hpp new file mode 100644 index 0000000..ba71b75 --- /dev/null +++ b/net/renet.hpp @@ -0,0 +1,13 @@ +/* + * renet.hpp + * + * Created on: 23.12.2014 + * Author: hm + */ + +#ifndef NET_RENET_HPP_ +#define NET_RENET_HPP_ + +#include "net/ReUdpConnection.hpp" + +#endif /* NET_RENET_HPP_ */ diff --git a/os/ReTraverser.cpp b/os/ReTraverser.cpp new file mode 100644 index 0000000..61a73aa --- /dev/null +++ b/os/ReTraverser.cpp @@ -0,0 +1,381 @@ +/* + * ReTraverser.cpp + * + * Created on: 23.12.2014 + * Author: hm + */ + +#include "../base/rebase.hpp" +#include "os/reos.hpp" + +#ifdef __LINUX__ +#define isUndefHandle(handle) ((handle) == NULL) +#define findFirstEntry(path, data) opendir(path) +#define findNextEntry(handle,data) (((data) = readdir(handle)) != NULL) +#define closeDir(handle) closedir(handle) +#define initEntryBuffer(entry) +#define setHandleUndef(h) ((h) = NULL) +#else +#define isUndefHandle(handle) ((handle) == INVALID_HANDLE_VALUE) +#define setHandleUndef(h) ((h) = INVALID_HANDLE_VALUE) +#define findFirstEntry(path, pattern, data) FindFirstFileA(path, pattern, data) +#define findNextEntry(handle, data) (FindNextFileA(handle, data) != 0) +#define closeDir(handle) FindClose(handle) +#define initEntryBuffer(entry) ((entry)->m_data = &(entry)->m_buffer) +#endif + +/** + * Constructor. + */ +ReMatcher::ReMatcher() : + m_prefix(1), + m_suffix(1), + m_tokens(0), + m_findAll(false), + m_ignoreCase(true), + m_notPattern(false) +{ + memset((void*) m_tokenStart, 0, sizeof m_tokenStart); + memset((void*) m_tokenEnd, 0, sizeof m_tokenEnd); + memset((void*) m_tokenLength, 0, sizeof m_tokenLength); + memset((void*) m_type, 0, sizeof m_type); +} +/** + * Constructor. + * + * @param pattern the search pattern + */ +ReMatcher::ReMatcher(const char* pattern) : + m_prefix(1), + m_suffix(1), + m_tokens(0), + m_findAll(false), + m_ignoreCase(true), + m_notPattern(false) +{ + memset((void*) m_tokenStart, 0, sizeof m_tokenStart); + memset((void*) m_tokenEnd, 0, sizeof m_tokenEnd); + memset((void*) m_tokenLength, 0, sizeof m_tokenLength); + memset((void*) m_type, 0, sizeof m_type); + compile(pattern); +} +/** + * Destructor. + */ +ReMatcher::~ReMatcher(){ +} + +/** + * Compiles the pattern into a internal structure. + * + * @param pattern pattern to compile + * @return true: success
+ * false: error occurred + */ +bool ReMatcher::compile(const char* pattern){ + bool rc = true; + const char* start = strchr(pattern, '*'); + size_t length = strlen(pattern); + if (start == NULL){ + m_prefix = pattern; + m_suffix = pattern; + } else { + if (length == 1) + m_findAll = true; + else{ + size_t ix = size_t(start - pattern); + if (start != pattern){ + m_prefix.append(pattern, ix); + } + if (ix < length - 1){ + m_suffix.append(start + 1, length - ix); + } + } + } + return rc; +} +/** + * Tests whether a name matches the pattern stored in the instance. + * + * @param name the name to test + * @return true: ! m_notPattern: the name matches
+ * m_notPattern: the name matches not
+ * false: otherwise + */ +bool ReMatcher::match(const char* name){ + bool rc = m_findAll; + if (! rc){ + size_t width = m_prefix.getLength(); + if (width == 0) + rc = true; + else { + rc = m_ignoreCase ? strncasecmp(name, m_prefix.str(), width) == 0 + : strncmp(name, m_prefix.str(), width) == 0; + } + if (rc && (width = m_suffix.getLength()) != 0){ + size_t length = strlen(name); + rc = length >= m_suffix.getLength(); + if (rc){ + const char* tail = name + length - width; + rc = m_ignoreCase ? strncasecmp(tail, m_suffix.str(), width) == 0 + : strncmp(tail, m_suffix.str(), width) == 0; + } + } + } + if (m_notPattern) + rc = ! rc; + return rc; +} + +/** + * Constructor. + */ +RePatternList::RePatternList() : + m_patterns(NULL), + m_count(0) +{ +} +/** + * Destructor. + */ +RePatternList::~RePatternList(){ + destroy(); +} +void RePatternList::destroy(){ + if (m_patterns != NULL){ + for (int ix = 0; ix < m_count; ix++){ + delete m_patterns[ix]; + m_patterns[ix] = NULL; + } + } + delete[] m_patterns; + m_patterns = NULL; +} +/** + * Tests whether a name matches at least one of the patterns. + * @param name name to test + * @return true: at least one pattern matches
+ * false: no pattern matches + */ +bool RePatternList::match(const char* name){ + bool rc = false; + for (int ix = 0; ix < m_count; ix++) + if (m_patterns[ix]->match(name)){ + rc = true; + break; + } + return rc; +} +/** + * Sets the pattern list from a string. + * + * @param patterns a string with one or more patterns + * @param separator NULL: the first char of patterns is the the separator
+ * otherwise: the separator between the patterns + */ +void RePatternList::set(const char* patterns, const char* separator){ + char buffer[2]; + destroy(); + if (separator == NULL){ + buffer[0] = patterns[0]; + buffer[1] = '\0'; + separator = buffer; + patterns++; + } + const char* start = patterns; + m_count = 1; + size_t length = strlen(separator); + while( (start = strstr(start, separator)) != NULL){ + m_count++; + start += length; + } + m_patterns = new ReMatcher*[m_count]; + int ix = 0; + start = patterns; + const char* end; + while( (end = strstr(start, separator)) != NULL){ + setOne(ix, start, end - start); + start = end + length; + ix++; + } + setOne(ix, start, strlen(start)); +} + +/** + * Sets one pattern in the pattern list. + * + * @param ix index of the pattern in the list + * @param pattern the pattern string + * @param patternLength the length of pattern + */ +void RePatternList::setOne(int ix, const char* pattern, size_t patternLength){ + ReByteBuffer buffer; + buffer.append(pattern, patternLength); + m_patterns[ix] = new ReMatcher(buffer.str()); +} +/** + * Constructor. + */ +DirEntryFilter_t::DirEntryFilter_t() : + m_regulars(true), + m_specials(true), + m_directories(true), + m_nodePatterns(), + m_pathPatterns(), + m_minSize(0), + m_maxSize(-1), + m_minAge(0), + m_maxAge(0) +{ +} +/** + * + */ +bool DirEntryFilter_t::match(DirStatus_t& entry){ + bool rc = false; + do { + if (! m_directories && isDirEntry(&entry)) + break; + if (m_specials && (isDirEntry(&entry) || isRegularEntry(&entry))) + break; + if (m_regulars && ! isRegularEntry(&entry)) + break; + if (m_minSize > 0 && sizeOfEntry(&entry) > m_minSize) + break; + if (m_maxSize >= 0 && sizeOfEntry(&entry) < m_maxSize) + break; + if (m_minAge != 0 && modifiedOfEntry(&entry) < m_minAge) + break; + if (m_maxAge != 0 && modifiedOfEntry(&entry) > m_maxAge) + break; + if (m_nodePatterns != NULL && ! m_nodePatterns->match(nameOfEntry(&entry))) + break; + if (m_pathPatterns != NULL && ! m_pathPatterns->match(entry.m_path.str())) + break; + rc = true; + } while(false); + return rc; +}; + +/** + * Returns the status of the current file (lazy loading). + * + * @return the status of the current file + */ +struct stat* DirStatus_t::getStatus() { + if (m_status.st_ino == 0) + if (stat(m_data->d_name, &m_status) != 0) + memset((void*) &m_status, 0, sizeof m_status); + return &m_status; +} + +/** + * Constructor. + * + * @param base the base directory. The traversal starts at this point + */ +ReTraverser::ReTraverser(const char* base) : + m_level(-1), + m_base(base) +{ + initEntry(base, 0); +} + +/** + * Destructor. + */ +ReTraverser::~ReTraverser() { +} + +/** + * Returns the info about the next file in the directory tree traversal. + * + * @param level OUT: the level relative to the base.
+ * 0 means the file is inside the base.
+ * Not defined if the result is NULL + * @return NULL no more files
+ * otherwise: the stack entry with the next file in the + * directory tree. May be a directory too + */ +DirStatus_t* ReTraverser::rawNextFile(int& level) +{ + DirStatus_t* rc = NULL; + bool again = false; + do{ + if (m_level < 0){ + initEntry(m_base.str(), 0); + if (! isUndefHandle(m_dirs[0].m_handle)) + rc = &m_dirs[0]; + } else { + DirStatus_t* current = &m_dirs[level]; + if (findNextEntry(current->m_handle, current->m_data)){ + if (current->m_passNo != m_passNoForDirSearch){ + rc = &m_dirs[m_level]; + } else { + + } + } else { + if (current->m_passNo == 1){ + initEntry(m_base.str(), m_level); + current->m_passNo = 2; + if (! isUndefHandle(m_dirs[0].m_handle)) + rc = &m_dirs[0]; + } + } + } + } while(again); + return rc; +} +/** + * Returns the info about the next file matching the filter options. + * + * @param level OUT: the level relative to the base.
+ * 0 means the file is inside the base.
+ * Not defined if the result is NULL + * @param filter NULL: every file matches
+ * otherwise: each found file must match this filter conditions + * @return NULL no more files
+ * otherwise: the info about the next file in the + * directory tree + */ +DirStatus_t* ReTraverser::nextFile(int& level, DirEntryFilter_t* filter){ + DirStatus_t* rc = rawNextFile(level); + while (rc != NULL){ + if (filter == NULL || filter->match(*rc)){ + break; + } + } + return rc; +} + +/** + * Initializes an entry in the directory entry stack. + * + * @param path the name of the directory belonging to the entry + * @param level the index of the entry in the stack + */ +void ReTraverser::initEntry(const char* path, int level){ + if (level < MAX_ENTRY_STACK_DEPTH){ + DirStatus_t* current = &m_dirs[level]; + initEntryBuffer(current); + current->m_handle = findFirstEntry(path, &m_current->m_data); + m_level = level; + } +} + +/** + * Frees the resources of an entry of the directory entry stack. + * + * @param level the index of the entry in the stack + */ +void ReTraverser::freeEntry(int level){ + if (level < MAX_ENTRY_STACK_DEPTH){ + DirStatus_t* current = &m_dirs[level]; + if (! isUndefHandle(current->m_handle)){ + closeDir(current->m_handle); + setHandleUndef(current->m_handle); + } + current->m_path.setLength(0); + } +} + diff --git a/os/ReTraverser.hpp b/os/ReTraverser.hpp new file mode 100644 index 0000000..75e7db0 --- /dev/null +++ b/os/ReTraverser.hpp @@ -0,0 +1,131 @@ +/* + * RpTraverser.hpp + * + * Created on: 23.12.2014 + * Author: hm + */ + +#ifndef OS_RETRAVERSER_HPP_ +#define OS_RETRAVERSER_HPP_ + +#ifdef __LINUX__ +#include + #include + +typedef DIR* FindFileHandle_t; +typedef __off_t FileSize_t; +typedef struct dirent DirInfoStruct_t; +#define nameOfEntry(entry) ((entry)->m_data->d_name) +#define isDirEntry(entry) (((entry)->m_data->d_type != DT_UNKNOWN && (entry)->m_data->d_type == DT_DIR) \ + || S_ISDIR((entry)->getStatus()->st_mode)) +#define isLinkEntry(entry) (((entry)->m_data->d_type != DT_UNKNOWN && (entry)->m_data->d_type == DT_LINK) \ + || S_ISLNK(entry.getStatus()->st_mode))) +#define isRegularEntry(entry) (((entry)->m_data->d_type != DT_UNKNOWN && (entry)->m_data->d_type == DT_REG) \ + || S_ISREG((entry)->getStatus()->st_mode)) +#define sizeOfEntry(entry) ((entry)->getStatus()->st_size) +#define modifiedOfEntry(entry) ((entry)->getStatus()->st_mtime) +#else +typedef int64_t FileSize_t; +typedef HANDLE FindFileHandle_t; +typedef WIN32_FIND_DATAA DirInfoStruct_t; +#define nameOfEntry(entry) ((entry)->m_data->d_name) +#define isDirEntry(data) (data.getStatus()) & ) +#endif +class DirStatus_t{ +public: + ReByteBuffer m_path; + DirInfoStruct_t* m_data; + FindFileHandle_t m_handle; + int m_passNo; +#if defined __LINUX__ + struct stat m_status; +public: + struct stat* getStatus(); +#elif defined WIN32 + DirInfoStruct_t m_buffer; +#endif + +}; +#define MAX_MATCHER_TOKEN 32 +class ReMatcher { +public: + enum TokenType_t { + TT_UNDEF, + TT_STRING, + TT_STAR, + TT_ONE_CHAR, + TT_CHAR_CLASS + }; +public: + ReMatcher(); + ReMatcher(const char* pattern); + ~ReMatcher(); +public: + bool compile(const char* pattern); + bool match(const char* name); +private: + ReByteBuffer m_prefix; + ReByteBuffer m_suffix; + ReByteBuffer m_pattern; + int m_tokenStart[MAX_MATCHER_TOKEN]; + int m_tokenEnd[MAX_MATCHER_TOKEN]; + int m_tokenLength[MAX_MATCHER_TOKEN]; + TokenType_t m_type[MAX_MATCHER_TOKEN]; + int m_tokens; + bool m_findAll; + bool m_ignoreCase; + bool m_notPattern; +}; +class RePatternList{ +public: + RePatternList(); + ~RePatternList(); +public: + void destroy(); + bool match(const char* pattern); + void set(const char* patterns, const char* separator = NULL); +private: + void setOne(int ix, const char* pattern, size_t patternLength); +private: + ReMatcher** m_patterns; + int m_count; +}; +class DirEntryFilter_t { +public: + DirEntryFilter_t(); + ~DirEntryFilter_t(); +public: + bool match(DirStatus_t& entry); +public: + bool m_regulars; + bool m_specials; + bool m_directories; + RePatternList* m_nodePatterns; + RePatternList* m_pathPatterns; + FileSize_t m_minSize; + FileSize_t m_maxSize; + time_t m_minAge; + time_t m_maxAge; +}; +#define MAX_ENTRY_STACK_DEPTH 256 +class ReTraverser { +public: + ReTraverser(const char* base); + virtual ~ReTraverser(); +public: + DirStatus_t* rawNextFile(int& level); + DirStatus_t* nextFile(int& level, DirEntryFilter_t* filter = NULL); +protected: + void initEntry(const char* path, int level); + void freeEntry(int level); + +protected: + int m_level; + ReByteBuffer m_base; + DirStatus_t m_dirs[MAX_ENTRY_STACK_DEPTH]; + /// each directory will be passed twice: for all files + for directories only + /// 1: depth first 2: breadth first + int m_passNoForDirSearch; +}; + +#endif /* OS_RETRAVERSER_HPP_ */ diff --git a/os/reos.hpp b/os/reos.hpp new file mode 100644 index 0000000..e5efc38 --- /dev/null +++ b/os/reos.hpp @@ -0,0 +1,24 @@ +/* + * reos.hpp + * + * Created on: 23.12.2014 + * Author: hm + */ + +#ifndef OS_REOS_HPP_ +#define OS_REOS_HPP_ +#define __LINUX__ +#if defined __LINUX__ +#include "unistd.h" +#include +#elif defined WIN32 +#include +#include "windows.h" +#else +#error "unknown os" +#endif + +#include "os/ReTraverser.hpp" + + +#endif /* OS_REOS_HPP_ */ -- 2.39.5