From: hama If not it will be allocated and the old value will be copied.
+ * A new allocation will be use at least The allocated space is incremented by one because of the ending '\\0'.
+ *
+ * Note: This class can be used as a poor man's string class.
+ * Very effizient:
+ *
+ *
+ * @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;
+ * assert(4 == buf.append("abc", 3).append("x").getLength());
+ *
+ *
+ * @param number The number to append.
+ * @param format The format of the number used by Example:
+ * ReByteBuffer buf;
+ * buf.appendInt(33, 4);
+ * assert(strcmp("33 ", buf.getBuffer()) == 0);
+ *
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.
+ *
+ *
+ *
+ * @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 name; name.append("mydoc");
+ * ReByteBuffer ext; ext.append(".txt");
+ * name.append(ext);
+ * assert("mydoc.txt", name.getBuffer());
+ *
+ *
+ * @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.
+ *
+ * Example:
+ * ReByteBuffer buf;
+ * buf.set("abc123", 6);
+ * assert(12 == buf.atoi(3, 3 + 2));
+ *
m_delta
bytes.
+ *
+ *
+ * @param toFind The sequence to find.
+ * @param toFindLength Length of Example:
+ * ReByteBuffer buf;
+ * buf.set("12abc123", 8);
+ * assert(5 == buf.indexOf("12", -1, 3));
+ *
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()
.
+ *
+ *
+ *
+ * @param toFind The substring which will be replaced.
+ * @param toFindLength The length of Example:
+ * ReByteBuffer buf;
+ * buf.set("12abc123", 8);
+ * buf.replaceAll("12", -1, "XYZ", -1);
+ * assert(strcmp("XYZabcXYZ3", buf.str()) == 0);
+ *
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.
+ *
+ *
+ *
+ * @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.setLength(5);
+ * assert(strcmp("123", buf.str()) == 0);
+ * assert(5 == buf.getLength());
+ *
+ *
+ * @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("123", 3);
+ * buf.setLengthAndFill(5, 'X');
+ * assert(strcmp("123XX", 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: Example:
+ * ReByteBuffer buf;
+ * buf.set("12345", 5);
+ * buf.splice(2, 2, "XYZ", 3);
+ * assert(strcmp("12XYZ5", buf.str()) == 0);
+ *
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.
+ *
+ * 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');
+ *
+ *
+ *
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 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 ofm_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:
+ * 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: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:
+ *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("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 beNULL
.
+ *
+ * @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: + *
Sequence
s.
+ 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:
+ * 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 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 ? "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.
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 true
: successfalse
: 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 matchesfalse
: 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 matchesfalse
: 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 separatorpattern
+ */
+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.