]> gitweb.hamatoma.de Git - crepublib/commitdiff
Reformatted, common license text (header)
authorhama <hama@siduction.net>
Sat, 4 Apr 2015 13:19:11 +0000 (15:19 +0200)
committerhama <hama@siduction.net>
Sat, 4 Apr 2015 13:19:11 +0000 (15:19 +0200)
92 files changed:
base/ReAppenders.cpp
base/ReAppenders.hpp
base/ReBaseUtils.cpp
base/ReBaseUtils.hpp
base/ReByteBuffer.cpp
base/ReByteBuffer.hpp
base/ReCString.cpp
base/ReCString.hpp
base/ReClassId.cpp
base/ReClassId.hpp
base/ReConfigFile.cpp
base/ReConfigFile.hpp
base/ReDirectory.cpp
base/ReDirectory.hpp
base/ReException.cpp
base/ReException.hpp
base/ReHashList.cpp
base/ReHashList.hpp
base/ReI18N.cpp
base/ReI18N.hpp
base/ReLogger.cpp
base/ReLogger.hpp
base/ReMutex.cpp
base/ReMutex.hpp
base/ReProgramArgs.cpp
base/ReProgramArgs.hpp
base/ReSeqArray.cpp
base/ReSeqArray.hpp
base/ReSerializable.cpp
base/ReSerializable.hpp
base/ReStringList.cpp
base/ReStringList.hpp
base/ReStringUtils.cpp
base/ReStringUtils.hpp
base/ReTestUnit.cpp
base/ReTestUnit.hpp
base/ReThread.cpp
base/ReThread.hpp
base/ReVarArgs.cpp
base/ReVarArgs.hpp
base/baselocations.hpp
base/rebase.hpp
base/restring.hpp
cunit/basetest.cpp
cunit/cuReByteBuffer.cpp
cunit/cuReCString.cpp
cunit/cuReDirTools.cpp
cunit/cuReDirectory.cpp
cunit/cuReException.cpp
cunit/cuReHashList.cpp
cunit/cuReI18N.cpp
cunit/cuReLogger.cpp
cunit/cuReMD5.cpp
cunit/cuReMatcher.cpp
cunit/cuReProgramArgs.cpp
cunit/cuReRPD64.cpp
cunit/cuReRandomizer.cpp
cunit/cuReSeqArray.cpp
cunit/cuReSerializable.cpp
cunit/cuReStringList.cpp
cunit/cuReStringUtils.cpp
cunit/cuReTCP.cpp
cunit/cuReTest.cpp
cunit/cuReTraverser.cpp
cunit/cuReVarArgs.cpp
cunit/cuReconfig.cpp
cunit/testall.cpp
dirtool.cpp
math/ReMD5.cpp
math/ReMD5.hpp
math/ReObfuscator.cpp
math/ReObfuscator.hpp
math/ReRPD64.cpp
math/ReRPD64.hpp
math/ReRandomizer.cpp
math/ReRandomizer.hpp
math/remath.hpp
net/ReTCP.cpp
net/ReTCP.hpp
net/ReUdpConnection.cpp
net/ReUdpConnection.hpp
net/renet.hpp
os/ReDirTools.cpp
os/ReDirTools.hpp
os/ReRemoteDir.cpp
os/ReRemoteDir.hpp
os/ReTraverser.cpp
os/ReTraverser.hpp
os/reos.hpp
string/ReMatcher.cpp
string/ReMatcher.hpp
string/restring.hpp

index 3faac42fa1fe073f131fe59c926127434f3bf1b1..90fed67042d064ebed4f75820b791f608d8810cf 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * ReAppenders.cpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 #include "base/rebase.hpp"
index 5e58cfe40e6871aa57142223766a818a77f95bf4..fcab1f72270f7a53a3be60c10520dc3fffc0d7fe 100644 (file)
@@ -1,14 +1,12 @@
 /*
  * ReAppenders.hpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
- *
- * These classes are separated from ReLogger.hpp because of the serialization
- * of classes (or include files):
- * ReLogger depends on few classes, but many classes are depending on ReLogger.
  */
 
 #ifndef BASE_REAPPENDERS_HPP_
index 772fac3cf6dd3a711985b2b0737cbb22a74ec668..64e9861828b240bacacb2e0aef4b391629a255d4 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * ReBaseUtils.cpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
index f0287015f93a046195bff4c13de0997e50d88941..cde959e45da4f64e89b8443b4ed22dfbd033097b 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * ReBaseUtils.hpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
index 0e68028dcf298b379ff2f71ea8757701937db3ae..01a7f552d7d3e907b129e738a5d3a672441322d8 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * ReByteBuffer.cpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
index 0a27f8524c7cef03483f78272613a3332e3e6545..eba2d42187612b400f2f76e4c91771ea0860c3bf 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * ReByteBuffer.hpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
index 63725f81dcf4cb60709b9e216223d3505ad32ae3..8bb4b5bad5d57e64f80cbb4165943dd3eac6a4c5 100644 (file)
@@ -1,14 +1,20 @@
 /*
  * ReCString.cpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
 #include "base/rebase.hpp"
 
+enum RELOC_CSTRING {
+       LC_CSTRING_REPLACE_1 = LOC_FIRST_OF(LOC_CSTRING), // 50901
+};
+
 /** @brief Replaces a substring by another.
  *
  * Cuts a substring from a string and inserts another substring.
index 65ae90dffe9f5a3045812dd33a15162ecef31bcc..d413c8698b0bd1add764f80a9cb306bbc07166fa 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * ReCString.hpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
index 1838046a916ddd662c98e0885d362b260233ee66..32f4b5012c3a03888b38dcf218f683c41ffba2c0 100644 (file)
@@ -1,18 +1,20 @@
 /*
  * ReClassId.cpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 #include "base/rebase.hpp"
 
 static ReByteBuffer s_unknownClassname;
 
-const char* classIdToString(ReClassId classId){
+const char* classIdToString(ReClassId classId) {
        const char* rc = "?";
-       switch(classId){
+       switch (classId) {
        case CLASSID_REMOTE_DIR:
                rc = "ReRemoteDir";
                break;
index feef67afd06b8e9c1a772840c0f1f2c638768189..37775817a9634b5fda6091ac35b437736894c161 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * ReClassId.hpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
@@ -26,10 +28,9 @@ enum ReClassId {
  * @param version      serialization version. Must be incremented if the format
  *                                     of serialization has changed
  */
-inline int buildSerialId(ReClassId classid, uint8_t version){
+inline int buildSerialId(ReClassId classid, uint8_t version) {
        return (classid << 8) + version;
 }
 extern const char* classIdToString(ReClassId classId);
 
-
 #endif /* BASE_RECLASSID_HPP_ */
index 38fdb23a35268bc74e5f31722a1ed49312fc9c98..4400c22aeaf98013a0966ff494c4d4614f2c4362 100644 (file)
@@ -1,16 +1,18 @@
 /*
  * ReConfigFile.cpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
 #include "base/rebase.hpp"
 
 enum RELOC_RECONFIGFILE {
-       LC_CONFIGFILE_DOUBLE = LC_CONFIGFILE + 1, // 50001
+       LC_CONFIGFILE_DOUBLE = LOC_FIRST_OF(LOC_CONFIGFILE), // 50001
        LC_CONFIGFILE_NO_INT,  // 50002
        LC_CONFIGFILE_NO_BOOL, // 50003
 };
index 053aacfe75017d75b5b3700d4001c82391761a5d..6f075e2097dd953ee0eb6ec06dfca2abe77361be 100644 (file)
@@ -1,9 +1,11 @@
 /*
- * ReConfigFile.h
- *
- * License: Public domain
+ * ReConfigFile.hpp
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
index ab27440fd73da3a2a7d0ccf9a82b18cdf4da8070..4288b6ef9c2e022e9822020e2f3f143303528405 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * ReDirectory.cpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
index 36428a449011548bd3b407b835ef2fa0042b10de..265167ef392e4d6b20d610284f9ae7b582fb1149 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * ReDirectory.hpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
index dc2a2166b0566e9b5bfde50c080a1aa37e0ada55..3d95941c417566d5aaab08f3ad50a27c0e1e19f8 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * ReException.cpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
@@ -121,3 +123,12 @@ ReBoundsException::ReBoundsException(const char* name, int index, int bound,
        m_message = _strdup(buffer);
 }
 
+/**
+ * Constructor.
+ *
+ * @param description  describes what is not implemented
+ */
+ReNotImplementedException::ReNotImplementedException(const char* description) :
+           ReException(
+               ReByteBuffer(i18n("not implemented: ")).append(description).str()) {
+}
index 77999e5a89e20fd3db4024678d8376c7407aee90..4f9122b33128d03db5d9b11abe5b3eb278da258e 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * ReException.hpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
@@ -57,4 +59,11 @@ public:
            int line);
 };
 
+/** This exception is called when a class/method/function is not implemented.
+ */
+class ReNotImplementedException: public ReException {
+public:
+       ReNotImplementedException(const char* description);
+};
+
 #endif /* REEXCEPTION_H_ */
index 95be346a43ba08d8656d8069cb01eb3403ff09c1..5afb527d89f83bea44ea2c758e2a606d6eb0280c 100644 (file)
@@ -1,16 +1,18 @@
 /*
  * ReHashList.cpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
 #include "base/rebase.hpp"
 
 enum RELOC_HASHLIST {
-       LC_NEXT_1 = LC_HASHLIST + 1, // 50301
+       LC_NEXT_1 = LOC_FIRST_OF(LOC_HASHLIST), // 50301
 };
 
 /**
index da7f95c24afa41bd04050360c34aff4686d4234d..6daf572a4a1460a8a0a230f43438e16320a85fe9 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * ReHashList.hpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
index 861c21e35b3841e66da6540a5c23a3afd41d056c..6736ebc04436aa41b59ddc657949bc4396e61258 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * ReI18N.cpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
index 85dcbed230bc91e4aeca77518395c29c71d9d934..cc5d7889c2a87a415933e91763da4e2f9f3343a0 100644 (file)
@@ -1,11 +1,12 @@
 /*
  * ReI18N.hpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
- *      Author: wk
  */
 
 #ifndef REI18N_H_
index 2471b564916fd5ec8e07fc18f60dcba496a23303..011ff44bdeff58915203857b2f8ea06b0b6a3d31 100644 (file)
@@ -1,16 +1,19 @@
 /*
  * ReLogger.cpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
 #include "base/rebase.hpp"
 
 enum RELOC_SEQARRAY {
-       LC_LOGGER_1 = LC_LOGGER + 1, // 50701
+       LC_LOGGER_1 = LOC_FIRST_OF(LOC_LOGGER), // 50701
+       LC_LOGGER_SAYF_OPEN,    // 50702
 };
 #ifdef __linux__
 extern size_t strftime(char* s, size_t maxsize, const char* format,
index 037dba6687f31ea7e76e7880f894d867396db264..95bfb7b435b0a0ae3e78539dd8f8abc879433ed1 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * ReLogger.hpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
index 8f8730c1f893e712940076671810d186af2258f7..6c0b13542f261aa33404d5e7cc8af3883ca7f9ca 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * ReMutex.cpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
index 04310b652eb7d70cd0f07c6faca964877d5df42f..fe5f79fc0ebab3b312d9004729a4a9ad564731cf 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * ReMutex.hpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
index 2cc0779632398e259d72a2acc2a5ce122779523c..c1d78cba57a0ffc8cafcf7355f33d8d3aab58baf 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * ReProgramArgs.cpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
index c52408e31b7a3be7a755fc495b3725971ba84912..0252e45db4a3060cff62b94a29625d6b64b4807a 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * ReProgramArgs.hpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
index 1e721ef49ab03bdfbc28b588fede6e51aeb105dc..36ba798415d4c55ca5135ea4cdc117df5e90c645 100644 (file)
@@ -1,16 +1,18 @@
 /*
  * ReSeqArray.cpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
 #include "base/rebase.hpp"
 
 enum RELOC_SEQARRAY {
-       LC_SET_SIZES_1 = LC_SEQARRAY + 1, // 50201
+       LC_SET_SIZES_1 = LOC_FIRST_OF(LOC_SEQARRAY), // 50201
        LC_SET_SIZES_2,         // 50202
        LC_SET_SIZES_3,         // 50203
        LC_SET_SEQ_1,           // 50204
index a3b5cf2e464ee27c76559b17a3e80e747b4673e0..6a9b8a9b70ba48b1df618022125a184d254fe3c3 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * ReSeqArray.hpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
index 71451bb55ff1a464c901bcb9583a96139ab1c94c..bdf25015c52af0709720dc4d81afed76d22f6bb1 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * ReSerializable.cpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
@@ -19,8 +21,9 @@ class ReSerialable;
  */
 ReSerializationException::ReSerializationException(const char* message,
     ReSerializable* instance) :
-           ReException(ReByteBuffer(message, -1).appendChar(' ').append(
-               instance->parentClassName()).str()) {
+           ReException(
+               ReByteBuffer(message, -1).appendChar(' ').append(
+                   instance->parentClassName()).str()) {
 }
 /**
  * Constructor.
@@ -46,11 +49,12 @@ ReSerializationLengthException::ReSerializationLengthException(
  * @param maxLength                    the maximal length of the string
  * @param instance                     the object to serialize
  */
-ReSerializeStringLengthException::ReSerializeStringLengthException(int currentLength, int maxLength,
-               ReSerializable* instance) :
-       ReSerializationException(
+ReSerializeStringLengthException::ReSerializeStringLengthException(
+    int currentLength, int maxLength, ReSerializable* instance) :
+           ReSerializationException(
                ReByteBuffer(i18nTranslate("string length too large: ")).appendInt(
-                   currentLength).appendChar('/').appendInt(maxLength).str(), instance),
+                   currentLength).appendChar('/').appendInt(maxLength).str(),
+               instance),
            m_currentLength(currentLength),
            m_maxLength(maxLength) {
 }
@@ -69,3 +73,53 @@ ReSerializable::ReSerializable(int serialId) :
  */
 ReSerializable::~ReSerializable() {
 }
+
+/**
+ * Puts a filetime to the byte sequence.
+ *
+ * @param sequence     IN/OUT: the byte sequence
+ * @param time         the filetime to serialize
+ *
+ */
+void ReSerializable::packFileTime(ReByteBuffer& sequence,
+    const ReFileTime_t& time) {
+       uint64_t value;
+#if defined __linux__
+       value = (time.tv_sec << 32) + time.tv_nsec;
+       sequence.appendBits64(int64_t(value));
+#elif defined __WIN32__
+#error "missing impl"
+#endif
+}
+/**
+ * Gets a filetime from a byte sequence.
+ *
+ * @param sequence     IN/OUT: the byte sequence
+ * @param length       IN/OUT: the length of <code>sequence</code>
+ * @param time         OUT: the filetime to set
+ * @param senderOs     the operating system which has serialized the data
+ * @throws ReSerializationLengthException
+ */
+void ReSerializable::unpackFileTime(const uint8_t*& sequence, size_t& length,
+    ReFileTime_t& time, ReOSType os) {
+       int64_t value;
+       if (length < 8)
+               throw ReSerializationLengthException(length, 8, this);
+
+#if defined __linux__
+       switch (os) {
+       case OS_LINUX:
+               unpackInt64(sequence, length, value);
+               time.tv_sec = int(value >> 32);
+               time.tv_nsec = int(value);
+               break;
+       case OS_WIN32:
+               throw ReNotImplementedException("unpackFileTime(): linux-win32");
+               break;
+       default:
+               break;
+       }
+#elif defined __WIN32__
+#error "missing impl"
+#endif
+}
index 6ad8e33fbbd1661293e745013c7adb3348bda2fc..a3ce56d808f34c9afe9c013ba68f2551c401638b 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * ReSerializable.hpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
@@ -26,7 +28,7 @@ public:
 class ReSerializationLengthException: public ReSerializationException {
 public:
        ReSerializationLengthException(int currentLength, int expectedLength,
-               ReSerializable* instance);
+           ReSerializable* instance);
 public:
        int m_currentLength;
        int m_expectedLength;
@@ -37,7 +39,7 @@ public:
 class ReSerializeStringLengthException: public ReSerializationException {
 public:
        ReSerializeStringLengthException(int currentLength, int maxLength,
-               ReSerializable* instance);
+           ReSerializable* instance);
 public:
        int m_currentLength;
        int m_maxLength;
@@ -67,49 +69,98 @@ public:
        ReSerializable(int serialId);
        virtual ~ReSerializable();
 public:
+       /** Sets the members of the instance from the byte sequence.
+        *
+        * @param sequence      IN: a byte sequence starting with the serialized members
+        *                                      of the instance<br>
+        *                                      OUT: the byte sequence behind the serialized members
+        * @param length        IN: the length of <code>sequence</code><br>
+        *                                      OUT: the length of <code>sequence</code> without the
+        *                                      serialized members
+        */
+       virtual void deserialize(const uint8_t*& sequence, size_t& length) = 0;
+       /** Sets the members of the instance from the byte sequence in a sequence.
+        * @param sequence      the serialized sequence
+        * @throw ReSerializationLengthException
+        */
+       inline void deserializeBuffer(const ReByteBuffer& sequence) {
+               size_t length = sequence.length();
+               const uint8_t* seq = reinterpret_cast<const uint8_t*>(sequence.str());
+               deserialize(seq, length);
+               if (length != 0)
+                       throw ReSerializationLengthException(length, 0, this);
+       }
        /** Appends a boolean value.
-        * @param buffer        IN/OUT: the buffer with the serialized values
+        * @param sequence      IN/OUT: the sequence with the serialized values
         * @param value         the value to serialize
         */
-       inline void packBool(ReByteBuffer& buffer, bool value) {
-               buffer.appendChar(value ? 't' : 'f');
+       inline void packBool(ReByteBuffer& sequence, bool value) {
+               sequence.appendChar(value ? 't' : 'f');
        }
-       /** Appends a string with a maximal length of 255 to the buffer.
-        * @param buffer        IN/OUT: the buffer with the serialized values
+       void packFileTime(ReByteBuffer& sequence, const ReFileTime_t& time);
+       /** Appends a string with a maximal length of 255 to the sequence.
+        * @param sequence      IN/OUT: the sequence with the serialized values
         * @param value         the value to serialize
         */
-       inline void packString255(ReByteBuffer& buffer, ReByteBuffer& value) {
+       inline void packString255(ReByteBuffer& sequence, ReByteBuffer& value) {
                if (value.length() > 255)
                        throw ReSerializeStringLengthException(value.length(), 255, this);
-               buffer.appendBits8(value.length()).append(value);
+               sequence.appendBits8(value.length()).append(value);
        }
-       /** Appends a string with a maximal length of 255 to the buffer.
-        * @param buffer        IN/OUT: the buffer with the serialized values
+       /** Appends a string with a maximal length of 255 to the sequence.
+        * @param sequence      IN/OUT: the sequence with the serialized values
         * @param value         the value to serialize
         */
-       inline void packString255(ReByteBuffer& buffer, const char* value,
-               size_t length = -1) {
+       inline void packString255(ReByteBuffer& sequence, const char* value,
+           size_t length = -1) {
                if (length == (size_t) -1)
                        length = strlen(value);
                if (length > 255)
                        throw ReSerializeStringLengthException(length, 255, this);
-               buffer.appendBits8(length).append(value, length);
+               sequence.appendBits8(length).append(value, length);
        }
-       /** Appends a string with a maximal length of 64KiByte to the buffer.
-        * @param buffer        IN/OUT: the buffer with the serialized values
+       /** Appends a string with a maximal length of 64KiByte to the sequence.
+        * @param sequence      IN/OUT: the sequence with the serialized values
         * @param value         the value to serialize
         */
-       inline void packString64k(ReByteBuffer& buffer, ReByteBuffer& value) {
+       inline void packString64k(ReByteBuffer& sequence, ReByteBuffer& value) {
                if (value.length() > 0xffff)
-                       throw ReSerializeStringLengthException(value.length(), 0xffff, this);
-               buffer.appendBits16(value.length()).append(value);
+                       throw ReSerializeStringLengthException(value.length(), 0xffff,
+                           this);
+               sequence.appendBits16(value.length()).append(value);
+       }
+       /** Appends a string with a maximal length of 64KiByte to the sequence.
+        * @param sequence      IN/OUT: the sequence with the serialized values
+        * @param value         the value to serialize
+        * @param length        length of <code>value</code><br>
+        *                                      -1: <code>strlen(value)</code>
+        */
+       inline void packString64k(ReByteBuffer& sequence, const char* value,
+           size_t length = -1) {
+               if (length == (size_t) -1)
+                       length = strlen(value);
+               if (length > 0xffff)
+                       throw ReSerializeStringLengthException(length, 0xffff, this);
+               sequence.appendBits16(length).append(value);
        }
-       /** Appends a string with a maximal length of 4GiByte to the buffer.
-        * @param buffer        IN/OUT: the buffer with the serialized values
+       /** Appends a string with a maximal length of 4GiByte to the sequence.
+        * @param sequence      IN/OUT: the sequence with the serialized values
         * @param value         the value to serialize
         */
-       inline void packString4t(ReByteBuffer& buffer, ReByteBuffer& value) {
-               buffer.appendBits32(value.length()).append(value);
+       inline void packString4t(ReByteBuffer& sequence, ReByteBuffer& value) {
+               sequence.appendBits32(value.length()).append(value);
+       }
+       /** Appends a string with a maximal length of 4 TiByte to the sequence.
+        * @param sequence      IN/OUT: the sequence with the serialized values
+        * @param value         the value to serialize
+        * @param length        length of <code>value</code><br>
+        *                                      -1: <code>strlen(value)</code>
+        */
+       inline void packString4t(ReByteBuffer& sequence, const char* value,
+           size_t length = -1) {
+               if (length == (size_t) -1)
+                       length = strlen(value);
+               sequence.appendBits32(length).append(value);
        }
        /** Returns the name of the parent class.
         * @return the parent's classname
@@ -117,45 +168,25 @@ public:
        inline const char* parentClassName() const {
                return classIdToString(ReClassId(m_serialId >> 8));
        }
-       /** Sets the members of the instance from the byte sequence.
-        *
-        * @param sequence      IN: a byte sequence starting with the serialized members
-        *                                      of the instance<br>
-        *                                      OUT: the byte sequence behind the serialized members
-        * @param length        IN: the length of <code>sequence</code><br>
-        *                                      OUT: the length of <code>sequence</code> without the
-        *                                      serialized members
-        */
-       virtual void deserialize(uint8_t*& sequence, size_t& length) = 0;
-       /** Sets the members of the instance from the byte sequence in a buffer.
-        * @param sequence      the serialized sequence
-        * @throw ReSerializationLengthException
-        */
-       inline void deserializeBuffer(const ReByteBuffer& sequence) {
-               size_t length = sequence.length();
-               uint8_t* seq = reinterpret_cast<uint8_t*>(sequence.buffer());
-               deserialize(seq, length);
-               if (length != 0)
-                       throw ReSerializationLengthException(length, 0, this);
-       }
        /** Returns the unique serialization id of the parent class.
         * @return the serialization id
         */
        inline int serialId() const {
                return m_serialId;
        }
-       /** Appends the class members to the end of the buffer.
+       /** Appends the class members to the end of the sequence.
         *
-        * @param buffer        IN/OUT: the buffer containing the serialized bytes
-        * @return                      the <code>buffer</code> (for chaining)
+        * @param sequence      IN/OUT: the sequence containing the serialized bytes
+        * @return                      the <code>sequence</code> (for chaining)
         */
-       virtual ReByteBuffer& serialize(ReByteBuffer& buffer) = 0;
+       virtual ReByteBuffer& serialize(ReByteBuffer& sequence) = 0;
        /** Reads a 8 bit integer from the serialized byte sequence.
         * @param sequence      IN/OUT: the byte sequence with the serialized data
         * @param length        IN/OUT: the length of <code>sequence</code>
         * @param value         OUT: the value read from the sequence
         */
-       inline void unpackBool(uint8_t*& sequence, size_t& length, bool& value) {
+       inline void unpackBool(const uint8_t*& sequence, size_t& length,
+           bool& value) {
                if (length < 1)
                        throw ReSerializationLengthException(length, 1, this);
                char cc = *sequence++;
@@ -165,12 +196,15 @@ public:
                value = cc == 't';
                length--;
        }
+       void unpackFileTime(const uint8_t*& sequence, size_t& length,
+           ReFileTime_t& time, ReOSType senderOs);
        /** Reads a 8 bit integer from the serialized byte sequence.
         * @param sequence      IN/OUT: the byte sequence with the serialized data
         * @param length        IN/OUT: the length of <code>sequence</code>
         * @param value         OUT: the value read from the sequence
         */
-       inline void unpackInt8(uint8_t*& sequence, size_t& length, int& value) {
+       inline void unpackInt8(const uint8_t*& sequence, size_t& length,
+           int& value) {
                if (length < 1)
                        throw ReSerializationLengthException(length, 1, this);
                value = *sequence++;
@@ -181,7 +215,8 @@ public:
         * @param length        IN/OUT: the length of <code>sequence</code>
         * @param value         OUT: the value read from the sequence
         */
-       inline void unpackInt16(uint8_t*& sequence, size_t& length, int& value) {
+       inline void unpackInt16(const uint8_t*& sequence, size_t& length,
+           int& value) {
                if (length < 2)
                        throw ReSerializationLengthException(length, 2, this);
                value = (*sequence++ << 8) + *sequence++;
@@ -192,7 +227,8 @@ public:
         * @param length        IN/OUT: the length of <code>sequence</code>
         * @param value         OUT: the value read from the sequence
         */
-       inline void unpackInt24(uint8_t*& sequence, size_t& length, int& value) {
+       inline void unpackInt24(const uint8_t*& sequence, size_t& length,
+           int& value) {
                if (length < 3)
                        throw ReSerializationLengthException(length, 3, this);
                value = (*sequence++ << 16) + (*sequence++ << 8) + *sequence++;
@@ -203,7 +239,8 @@ public:
         * @param length        IN/OUT: the length of <code>sequence</code>
         * @param value         OUT: the value read from the sequence
         */
-       inline void unpackInt32(uint8_t*& sequence, size_t& length, int& value) {
+       inline void unpackInt32(const uint8_t*& sequence, size_t& length,
+           int& value) {
                if (length < 4)
                        throw ReSerializationLengthException(length, 4, this);
                value = (*sequence++ << 24) + (*sequence++ << 16) + (*sequence++ << 8)
@@ -215,7 +252,7 @@ public:
         * @param length        IN/OUT: the length of <code>sequence</code>
         * @param value         OUT: the value read from the sequence
         */
-       inline void unpackInt64(uint8_t*& sequence, size_t& length,
+       inline void unpackInt64(const uint8_t*& sequence, size_t& length,
            int64_t& value) {
                if (length < 8)
                        throw ReSerializationLengthException(length, 8, this);
@@ -230,12 +267,12 @@ public:
         * @param length        IN/OUT: the length of <code>sequence</code>
         * @param value         OUT: the value read from the sequence
         */
-       inline void unpackString255(uint8_t*& sequence, size_t& length,
+       inline void unpackString255(const uint8_t*& sequence, size_t& length,
            ReByteBuffer& value) {
                size_t strLen = 0;
                if (length < 1 || (strLen = *sequence) > length)
                        throw ReSerializationLengthException(length, 1 + strLen, this);
-               value.set(reinterpret_cast<char*>(sequence + 1), strLen);
+               value.set(reinterpret_cast<const char*>(sequence + 1), strLen);
                strLen += 1;
                sequence += strLen;
                length -= strLen;
@@ -245,12 +282,12 @@ public:
         * @param length        IN/OUT: the length of <code>sequence</code>
         * @param value         OUT: the value read from the sequence
         */
-       inline void unpackString64k(uint8_t*& sequence, size_t& length,
+       inline void unpackString64k(const uint8_t*& sequence, size_t& length,
            ReByteBuffer& value) {
                size_t strLen = 0;
                if (length < 2 || (strLen = (sequence[0] << 8) + sequence[1]) > length)
                        throw ReSerializationLengthException(length, 2 + strLen, this);
-               value.set(reinterpret_cast<char*>(sequence + 2), strLen);
+               value.set(reinterpret_cast<const char*>(sequence + 2), strLen);
                strLen += 2;
                length -= strLen;
                sequence += strLen;
@@ -260,14 +297,14 @@ public:
         * @param length        IN/OUT: the length of <code>sequence</code>
         * @param value         OUT: the value read from the sequence
         */
-       inline void unpackString4t(uint8_t*& sequence, size_t& length,
+       inline void unpackString4t(const uint8_t*& sequence, size_t& length,
            ReByteBuffer& value) {
                size_t strLen = 0;
                if (length < 4
                    || (strLen = (sequence[0] << 24) + (sequence[1] << 16)
                        + (sequence[2] << 8) + sequence[3]) > length)
                        throw ReSerializationLengthException(length, 4 + strLen, this);
-               value.set(reinterpret_cast<char*>(sequence + 4), strLen);
+               value.set(reinterpret_cast<const char*>(sequence + 4), strLen);
                strLen += 4;
                length -= strLen;
                sequence += strLen;
index e08a453026df7b7bca115af336010b2bffcf25f9..4f4a8ae7de4ade949811b1ac35e8fd61e34260a4 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * ReStringList.cpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
index 934d407614cae37da2cf1945be94692c94067bfc..8c261aeb45d4b330266ad3d3a9961a4c8080c879 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * ReStringList.hpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
index 6e7802c14074b94e611dca00842b0031eb73bf9e..9621f5366175a6ac12c0383f019bfb5a5cad105f 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * ReStringUtils.cpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
index f6306b5a5342abcfcfb3456df0dfc29f06dfeba2..6b6f4764e75bf1865df1c57a1adb43ef0d6450a3 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * ReStringUtils.hpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
index 27a4079f6213626ee7d97e089036e23346d7933d..261d301d6ae320cd4d17366d2e76270088b49682 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * ReTestUnit.cpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 #include "base/rebase.hpp"
index 2752ac4c366bbcf449893db8e96e774592a23327..cc23271cf7c6ce9c03206742e1d5c715f8d981c0 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * ReTestUnit.hpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
index 512a850d179039b57760ac23e876dd1ace5c40a2..5a027de1d8fc78527d7a4bc6af899f00597cc2b1 100644 (file)
-/*\r
- * ReThreadStarter.cpp\r
- *\r
- * License: Public domain\r
- * Do what you want.\r
- * No warranties and disclaimer of any damages.\r
- * The latest sources: https://github.com/republib\r
- */\r
-\r
-#include "base/rebase.hpp"\r
-\r
-enum RELOC_HASHLIST {\r
-       LC_PREPARE_TO_RUN_1 = LC_THREAD + 1, // 50601\r
-       LC_START_THREAD_1,              // 50602\r
-       LC_INSERT_THREAD_1,             // 50603\r
-       LC_KILL_ALL_THREADS_1,  // 50604\r
-       LC_KILL_ALL_THREADS_2,  // 50605\r
-       LC_MUTEX_THREADS,               // 50606\r
-};\r
-\r
-/**\r
- * Constructor.\r
- *\r
- * @param autodelete   <code>true</code>: the <code>ReThreadStarter</code>\r
- *                                             deletes the instance when it is stopped\r
- */\r
-ReThread::ReThread(bool autoDelete) :\r
-           m_threadId(-1),\r
-           m_threadLogger(false),\r
-           m_appender(NULL),\r
-           m_pool(NULL),\r
-#if defined __linux__\r
-           m_threadInfo(),\r
-#elif defined __WIN32__\r
-           m_threadInfo(NULL),\r
-           m_osThreadId(0),\r
-#endif\r
-           m_shouldStop(false),\r
-           m_isStopped(false),\r
-           m_autoDelete(autoDelete) {\r
-#if defined __linux__\r
-       memset(&m_threadInfo, 0, sizeof m_threadInfo);\r
-#endif\r
-}\r
-/**\r
- * Destructor.\r
- */\r
-ReThread::~ReThread() {\r
-       delete m_appender;\r
-       m_appender = NULL;\r
-}\r
-\r
-/**\r
- * Returns the logger.\r
- *\r
- * @return the logger\r
- */\r
-ReLogger* ReThread::logger() {\r
-       return &m_threadLogger;\r
-}\r
-\r
-/**\r
- * Kills the thread.\r
- *\r
- * Note: This is very oppressive!\r
- * The thread starter uses that only if <code>setShouldStop(true)</code>\r
- * has no success.\r
- */\r
-void ReThread::kill() {\r
-#if defined __linux__\r
-       pthread_kill(m_threadInfo, SIGKILL);\r
-#elif defined __WIN32__\r
-       TerminateThread(m_threadInfo, 254);\r
-#endif\r
-}\r
-/**\r
- * Prepares the thread for running.\r
- *\r
- * Should only called by <code>ReThredStarter()</code>\r
- *\r
- * @param id                   the thread id\r
- * @param masterLogger the logger for error handling\r
- * @param pool                         the instance which has started the thread\r
- */\r
-bool ReThread::prepareToRun(int id, ReLogger* masterLogger,\r
-    ReThreadPool* pool) {\r
-       bool rc = false;\r
-       if (m_pool != NULL) {\r
-               ReLogger* current =\r
-                   masterLogger == NULL ? globalLogger() : masterLogger;\r
-               current->sayF(LOG_ERROR | CAT_LIB, LC_PREPARE_TO_RUN_1,\r
-                   i18n("prepareToRun($1) is called multiple times")).arg(id).end();\r
-       } else {\r
-               m_threadId = id;\r
-               if (m_appender != NULL) {\r
-                       m_appender->setMasterLogger(masterLogger);\r
-               } else {\r
-                       m_appender = new ReSlaveAppender(masterLogger,\r
-                           '@' + (id % ('z' - '@' + 1)));\r
-                       m_threadLogger.addAppender(m_appender);\r
-               }\r
-               m_pool = pool;\r
-               rc = true;\r
-       }\r
-       return rc;\r
-}\r
-/**\r
- * Runs the task and exits the thread.\r
- */\r
-void ReThread::runAndFinish() {\r
-       run();\r
-       m_isStopped = true;\r
-#if defined __linux__\r
-       pthread_exit(NULL);\r
-#elif defined __WIN32__\r
-       // Nothing to do\r
-#endif\r
-}\r
-\r
-/**\r
- * Constructor.\r
- *\r
- * @param processor            the instance that performs the actual task\r
- */\r
-ReSimpleThread::ReSimpleThread(ReProcessor* processor) :\r
-           ReThread(true),\r
-           m_processor(processor) {\r
-}\r
-/**\r
- * Destructor.\r
- */\r
-ReSimpleThread::~ReSimpleThread() {\r
-}\r
-/**\r
- * This method does the actual task.\r
- */\r
-void ReSimpleThread::run() {\r
-       m_processor->process();\r
-       m_isStopped = true;\r
-}\r
-\r
-/**\r
- * Constructor.\r
- *\r
- * @param maxThreads   the maximal number of threads\r
- * @param logger               the (master) logger for error handling\r
- */\r
-ReThreadPool::ReThreadPool(int maxThreads, ReLogger* logger) :\r
-           m_nextId(0),\r
-           m_logger(logger),\r
-           m_maxThreads(maxThreads),\r
-           m_maxKillTimeSec(3),\r
-           m_mutexThreads(LC_MUTEX_THREADS) {\r
-       m_threads = new ReThread*[maxThreads];\r
-       memset(m_threads, 0, maxThreads * sizeof *m_threads);\r
-}\r
-\r
-/**\r
- * Destructor.\r
- */\r
-ReThreadPool::~ReThreadPool() {\r
-       killAllThreads();\r
-}\r
-\r
-/**\r
- * Inserts a thread into the thread list.\r
- *\r
- * @param thread       thread to insert\r
- * @return                     <code>true</code>: success<br>\r
- *                                     <code>false</code>: too much threads (no space in list)\r
- */\r
-bool ReThreadPool::insertThread(ReThread* thread) {\r
-       bool found = false;\r
-       m_mutexThreads.lock();\r
-       for (int ii = 0; ii < m_maxThreads; ii++) {\r
-               ReThread* current = m_threads[ii];\r
-               if (current == NULL) {\r
-                       m_threads[ii] = thread;\r
-                       found = true;\r
-                       break;\r
-               } else if (current->m_isStopped) {\r
-                       if (current->m_autoDelete)\r
-                               delete current;\r
-                       m_threads[ii] = thread;\r
-                       found = true;\r
-               }\r
-       }\r
-       m_mutexThreads.unlock();\r
-       if (!found)\r
-               m_logger->sayF(LOG_ERROR | CAT_PROCESS, LC_INSERT_THREAD_1,\r
-                   i18n("too much threads: $1")).arg(m_maxThreads).end();\r
-\r
-       return found;\r
-}\r
-/**\r
- * Returns the number of active threads.\r
- *\r
- * Inactive threads will be removed.\r
- *\r
- * @return     the number of running threads\r
- */\r
-int ReThreadPool::countThreads() {\r
-       int count = 0;\r
-       m_mutexThreads.lock();\r
-       for (int ii = 0; ii < m_maxThreads; ii++) {\r
-               ReThread* current = m_threads[ii];\r
-               if (current != NULL) {\r
-                       if (!current->m_isStopped)\r
-                               count++;\r
-                       else {\r
-                               if (current->m_autoDelete)\r
-                                       delete current;\r
-                               m_threads[ii] = NULL;\r
-                       }\r
-               }\r
-       }\r
-       m_mutexThreads.unlock();\r
-       return count;\r
-}\r
-\r
-/**\r
- * Stopps all running threads.\r
- */\r
-void ReThreadPool::killAllThreads() {\r
-       // Orders stop for all threads:\r
-       int countWaiting = 0;\r
-       m_mutexThreads.lock();\r
-       for (int ii = 0; ii < m_maxThreads; ii++) {\r
-               ReThread* current = m_threads[ii];\r
-               if (current != NULL && !current->m_isStopped) {\r
-                       countWaiting++;\r
-                       current->setShouldStop(true);\r
-               }\r
-       }\r
-       m_mutexThreads.unlock();\r
-       if (countWaiting > 0)\r
-               m_logger->sayF(LOG_INFO | CAT_PROCESS, LC_KILL_ALL_THREADS_1,\r
-                   i18n("$1 thread(s) still running")).arg(countWaiting).end();\r
-\r
-       // we observe the thread stopping:\r
-       for (int wait = 0; countWaiting > 0 && wait <= m_maxKillTimeSec; wait++) {\r
-               m_mutexThreads.lock();\r
-               for (int ii = 0; ii < m_maxThreads; ii++) {\r
-                       ReThread* current = m_threads[ii];\r
-                       if (current != NULL && current->m_isStopped) {\r
-                               countWaiting--;\r
-                       }\r
-               }\r
-               m_mutexThreads.unlock();\r
-               if (countWaiting > 0)\r
-                       millisecSleep(1000);\r
-       }\r
-       // now we kill:\r
-       countWaiting = 0;\r
-       for (int ii = 0; ii < m_maxThreads; ii++) {\r
-               m_mutexThreads.lock();\r
-               ReThread* current = m_threads[ii];\r
-               if (current == NULL || current->m_isStopped)\r
-                       m_mutexThreads.unlock();\r
-               else {\r
-                       current->kill();\r
-                       int id = current->m_threadId;\r
-                       m_mutexThreads.unlock();\r
-                       m_logger->sayF(LOG_WARNING | CAT_PROCESS, LC_KILL_ALL_THREADS_2,\r
-                           i18n("thread $1 must be killed")).arg(id).end();\r
-                       countWaiting++;\r
-               }\r
-       }\r
-       if (countWaiting > 0)\r
-               // wait 1 msec for end of kill:\r
-               millisecSleep(1);\r
-       // we destroy all threads marked with auto delete:\r
-       m_mutexThreads.lock();\r
-       for (int ii = 0; ii < m_maxThreads; ii++) {\r
-               ReThread* current = m_threads[ii];\r
-               if (current != NULL && current->m_autoDelete) {\r
-                       delete current;\r
-                       m_threads[ii] = NULL;\r
-               }\r
-       }\r
-       m_mutexThreads.unlock();\r
-}\r
-\r
-/**\r
- * Starts a thread with very few prerequisites.\r
- *\r
- * Note: this method is threadsafe.\r
- *\r
- * @param processor            the instance doing the actual task\r
- * @return                             <code>true</code>: successful\r
- */\r
-bool ReThreadPool::startSimpleThread(ReProcessor& processor) {\r
-       // auto delete: the thread starter frees the instance\r
-       ReSimpleThread* thread = new ReSimpleThread(&processor);\r
-       bool rc = startThread(thread);\r
-       return rc;\r
-}\r
-\r
-#if defined __linux__\r
-/**\r
- * The start routine of pthread_start\r
- *\r
- * This will handle the connection for each client (in an own thread).\r
- *\r
- * @param pConnection  a void* pointer to the <code>ReThread</code> instance\r
- * */\r
-void* globalThreadStarterFunction(void *pConnection) {\r
-       ReThread* thread = reinterpret_cast<ReThread*>(pConnection);\r
-       thread->runAndFinish();\r
-       return NULL;\r
-}\r
-#elif defined __WIN32__\r
-DWORD WINAPI globalThreadStarterFunction(_In_ LPVOID pConnection) {\r
-       ReThread* thread = reinterpret_cast<ReThread*>(pConnection);\r
-       thread->runAndFinish();\r
-       return 0;\r
-}\r
-\r
-#endif\r
-\r
-/**\r
- * Starts a new thread.\r
- *\r
- * Note: this method is threadsafe.\r
- *\r
- * @param thread       the\r
- */\r
-bool ReThreadPool::startThread(ReThread* thread) {\r
-       bool ok = false;\r
-       if (!insertThread(thread)) {\r
-               if (thread->m_autoDelete) {\r
-                       delete thread;\r
-               }\r
-       } else {\r
-               if (thread->prepareToRun(++m_nextId, m_logger, this)) {\r
-#if defined __linux__\r
-                       ok = pthread_create(&thread->m_threadInfo, NULL,\r
-                           globalThreadStarterFunction, reinterpret_cast<void*>(thread))\r
-                           >= 0;\r
-#elif defined __WIN32__\r
-                       ok = (thread->m_threadInfo = CreateThread(NULL, 0, globalThreadStarterFunction,\r
-                                       reinterpret_cast<void*>(thread), 0, &thread->m_osThreadId)) != NULL;\r
-#endif\r
-                       if (!ok)\r
-                               m_logger->sayF(LOG_ERROR | CAT_PROCESS, LC_START_THREAD_1,\r
-                                   i18n("cannot create a thread: $1")).arg(getLastOSError())\r
-                                   .end();\r
-               }\r
-       }\r
-       return ok;\r
-}\r
-\r
-/**\r
- * Waits for the end of all threads.\r
- *\r
- * Note: this method is threadsafe.\r
- *\r
- * @param timeoutSec   maximal time for waiting\r
- * @return                             <code>true</code>: all threads are finished<br>\r
- *                                             <code>false</code>: timeout reached\r
- */\r
-bool ReThreadPool::waitForAlmostAll(int mayResist, int timeoutSec) {\r
-       bool rc = false;\r
-       time_t start = time(NULL);\r
-       time_t now;\r
-       do {\r
-               int countWaiting = 0;\r
-               m_mutexThreads.lock();\r
-               for (int ii = 0; ii < m_maxThreads; ii++) {\r
-                       ReThread* current = m_threads[ii];\r
-                       if (current != NULL && !current->m_isStopped) {\r
-                               countWaiting++;\r
-                       }\r
-               }\r
-               m_mutexThreads.unlock();\r
-               if (countWaiting <= mayResist) {\r
-                       rc = true;\r
-                       break;\r
-               }\r
-               millisecSleep(200);\r
-               now = time(NULL);\r
-       } while (now < start + timeoutSec);\r
-       return rc;\r
-}\r
-/**\r
- * Waits for the end of all threads.\r
- *\r
- * Note: this method is threadsafe.\r
- *\r
- * @param timeoutSec   maximal time for waiting\r
- * @return                             <code>true</code>: all threads are finished<br>\r
- *                                             <code>false</code>: timeout reached\r
- */\r
-bool ReThreadPool::waitForDone(int timeoutSec) {\r
-       bool rc = waitForAlmostAll(0, timeoutSec);\r
-       return rc;\r
-}\r
+/*
+ * ReThread.cpp
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
+ * Do what you want.
+ * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
+ * The latest sources: https://github.com/republib
+ */
+
+#include "base/rebase.hpp"
+
+enum RELOC_HASHLIST {
+       LC_PREPARE_TO_RUN_1 = LOC_FIRST_OF(LOC_THREAD), // 50601
+       LC_START_THREAD_1,              // 50602
+       LC_INSERT_THREAD_1,             // 50603
+       LC_KILL_ALL_THREADS_1,  // 50604
+       LC_KILL_ALL_THREADS_2,  // 50605
+       LC_MUTEX_THREADS,               // 50606
+};
+
+/**
+ * Constructor.
+ *
+ * @param autodelete   <code>true</code>: the <code>ReThreadStarter</code>
+ *                                             deletes the instance when it is stopped
+ */
+ReThread::ReThread(bool autoDelete) :
+           m_threadId(-1),
+           m_threadLogger(false),
+           m_appender(NULL),
+           m_pool(NULL),
+#if defined __linux__
+           m_threadInfo(),
+#elif defined __WIN32__
+           m_threadInfo(NULL),
+           m_osThreadId(0),
+#endif
+           m_shouldStop(false),
+           m_isStopped(false),
+           m_autoDelete(autoDelete) {
+#if defined __linux__
+       memset(&m_threadInfo, 0, sizeof m_threadInfo);
+#endif
+}
+/**
+ * Destructor.
+ */
+ReThread::~ReThread() {
+       delete m_appender;
+       m_appender = NULL;
+}
+
+/**
+ * Returns the logger.
+ *
+ * @return the logger
+ */
+ReLogger* ReThread::logger() {
+       return &m_threadLogger;
+}
+
+/**
+ * Kills the thread.
+ *
+ * Note: This is very oppressive!
+ * The thread starter uses that only if <code>setShouldStop(true)</code>
+ * has no success.
+ */
+void ReThread::kill() {
+#if defined __linux__
+       pthread_kill(m_threadInfo, SIGKILL);
+#elif defined __WIN32__
+       TerminateThread(m_threadInfo, 254);
+#endif
+}
+/**
+ * Prepares the thread for running.
+ *
+ * Should only called by <code>ReThredStarter()</code>
+ *
+ * @param id                   the thread id
+ * @param masterLogger the logger for error handling
+ * @param pool                         the instance which has started the thread
+ */
+bool ReThread::prepareToRun(int id, ReLogger* masterLogger,
+    ReThreadPool* pool) {
+       bool rc = false;
+       if (m_pool != NULL) {
+               ReLogger* current =
+                   masterLogger == NULL ? globalLogger() : masterLogger;
+               current->sayF(LOG_ERROR | CAT_LIB, LC_PREPARE_TO_RUN_1,
+                   i18n("prepareToRun($1) is called multiple times")).arg(id).end();
+       } else {
+               m_threadId = id;
+               if (m_appender != NULL) {
+                       m_appender->setMasterLogger(masterLogger);
+               } else {
+                       m_appender = new ReSlaveAppender(masterLogger,
+                           '@' + (id % ('z' - '@' + 1)));
+                       m_threadLogger.addAppender(m_appender);
+               }
+               m_pool = pool;
+               rc = true;
+       }
+       return rc;
+}
+/**
+ * Runs the task and exits the thread.
+ */
+void ReThread::runAndFinish() {
+       run();
+       m_isStopped = true;
+#if defined __linux__
+       pthread_exit(NULL);
+#elif defined __WIN32__
+       // Nothing to do
+#endif
+}
+
+/**
+ * Constructor.
+ *
+ * @param processor            the instance that performs the actual task
+ */
+ReSimpleThread::ReSimpleThread(ReProcessor* processor) :
+           ReThread(true),
+           m_processor(processor) {
+}
+/**
+ * Destructor.
+ */
+ReSimpleThread::~ReSimpleThread() {
+}
+/**
+ * This method does the actual task.
+ */
+void ReSimpleThread::run() {
+       m_processor->process();
+       m_isStopped = true;
+}
+
+/**
+ * Constructor.
+ *
+ * @param maxThreads   the maximal number of threads
+ * @param logger               the (master) logger for error handling
+ */
+ReThreadPool::ReThreadPool(int maxThreads, ReLogger* logger) :
+           m_nextId(0),
+           m_logger(logger),
+           m_maxThreads(maxThreads),
+           m_maxKillTimeSec(3),
+           m_mutexThreads(LC_MUTEX_THREADS) {
+       m_threads = new ReThread*[maxThreads];
+       memset(m_threads, 0, maxThreads * sizeof *m_threads);
+}
+
+/**
+ * Destructor.
+ */
+ReThreadPool::~ReThreadPool() {
+       killAllThreads();
+}
+
+/**
+ * Inserts a thread into the thread list.
+ *
+ * @param thread       thread to insert
+ * @return                     <code>true</code>: success<br>
+ *                                     <code>false</code>: too much threads (no space in list)
+ */
+bool ReThreadPool::insertThread(ReThread* thread) {
+       bool found = false;
+       m_mutexThreads.lock();
+       for (int ii = 0; ii < m_maxThreads; ii++) {
+               ReThread* current = m_threads[ii];
+               if (current == NULL) {
+                       m_threads[ii] = thread;
+                       found = true;
+                       break;
+               } else if (current->m_isStopped) {
+                       if (current->m_autoDelete)
+                               delete current;
+                       m_threads[ii] = thread;
+                       found = true;
+               }
+       }
+       m_mutexThreads.unlock();
+       if (!found)
+               m_logger->sayF(LOG_ERROR | CAT_PROCESS, LC_INSERT_THREAD_1,
+                   i18n("too much threads: $1")).arg(m_maxThreads).end();
+
+       return found;
+}
+/**
+ * Returns the number of active threads.
+ *
+ * Inactive threads will be removed.
+ *
+ * @return     the number of running threads
+ */
+int ReThreadPool::countThreads() {
+       int count = 0;
+       m_mutexThreads.lock();
+       for (int ii = 0; ii < m_maxThreads; ii++) {
+               ReThread* current = m_threads[ii];
+               if (current != NULL) {
+                       if (!current->m_isStopped)
+                               count++;
+                       else {
+                               if (current->m_autoDelete)
+                                       delete current;
+                               m_threads[ii] = NULL;
+                       }
+               }
+       }
+       m_mutexThreads.unlock();
+       return count;
+}
+
+/**
+ * Stopps all running threads.
+ */
+void ReThreadPool::killAllThreads() {
+       // Orders stop for all threads:
+       int countWaiting = 0;
+       m_mutexThreads.lock();
+       for (int ii = 0; ii < m_maxThreads; ii++) {
+               ReThread* current = m_threads[ii];
+               if (current != NULL && !current->m_isStopped) {
+                       countWaiting++;
+                       current->setShouldStop(true);
+               }
+       }
+       m_mutexThreads.unlock();
+       if (countWaiting > 0)
+               m_logger->sayF(LOG_INFO | CAT_PROCESS, LC_KILL_ALL_THREADS_1,
+                   i18n("$1 thread(s) still running")).arg(countWaiting).end();
+
+       // we observe the thread stopping:
+       for (int wait = 0; countWaiting > 0 && wait <= m_maxKillTimeSec; wait++) {
+               m_mutexThreads.lock();
+               for (int ii = 0; ii < m_maxThreads; ii++) {
+                       ReThread* current = m_threads[ii];
+                       if (current != NULL && current->m_isStopped) {
+                               countWaiting--;
+                       }
+               }
+               m_mutexThreads.unlock();
+               if (countWaiting > 0)
+                       millisecSleep(1000);
+       }
+       // now we kill:
+       countWaiting = 0;
+       for (int ii = 0; ii < m_maxThreads; ii++) {
+               m_mutexThreads.lock();
+               ReThread* current = m_threads[ii];
+               if (current == NULL || current->m_isStopped)
+                       m_mutexThreads.unlock();
+               else {
+                       current->kill();
+                       int id = current->m_threadId;
+                       m_mutexThreads.unlock();
+                       m_logger->sayF(LOG_WARNING | CAT_PROCESS, LC_KILL_ALL_THREADS_2,
+                           i18n("thread $1 must be killed")).arg(id).end();
+                       countWaiting++;
+               }
+       }
+       if (countWaiting > 0)
+               // wait 1 msec for end of kill:
+               millisecSleep(1);
+       // we destroy all threads marked with auto delete:
+       m_mutexThreads.lock();
+       for (int ii = 0; ii < m_maxThreads; ii++) {
+               ReThread* current = m_threads[ii];
+               if (current != NULL && current->m_autoDelete) {
+                       delete current;
+                       m_threads[ii] = NULL;
+               }
+       }
+       m_mutexThreads.unlock();
+}
+
+/**
+ * Starts a thread with very few prerequisites.
+ *
+ * Note: this method is threadsafe.
+ *
+ * @param processor            the instance doing the actual task
+ * @return                             <code>true</code>: successful
+ */
+bool ReThreadPool::startSimpleThread(ReProcessor& processor) {
+       // auto delete: the thread starter frees the instance
+       ReSimpleThread* thread = new ReSimpleThread(&processor);
+       bool rc = startThread(thread);
+       return rc;
+}
+
+#if defined __linux__
+/**
+ * The start routine of pthread_start
+ *
+ * This will handle the connection for each client (in an own thread).
+ *
+ * @param pConnection  a void* pointer to the <code>ReThread</code> instance
+ * */
+void* globalThreadStarterFunction(void *pConnection) {
+       ReThread* thread = reinterpret_cast<ReThread*>(pConnection);
+       thread->runAndFinish();
+       return NULL;
+}
+#elif defined __WIN32__
+DWORD WINAPI globalThreadStarterFunction(_In_ LPVOID pConnection) {
+       ReThread* thread = reinterpret_cast<ReThread*>(pConnection);
+       thread->runAndFinish();
+       return 0;
+}
+
+#endif
+
+/**
+ * Starts a new thread.
+ *
+ * Note: this method is threadsafe.
+ *
+ * @param thread       the
+ */
+bool ReThreadPool::startThread(ReThread* thread) {
+       bool ok = false;
+       if (!insertThread(thread)) {
+               if (thread->m_autoDelete) {
+                       delete thread;
+               }
+       } else {
+               if (thread->prepareToRun(++m_nextId, m_logger, this)) {
+#if defined __linux__
+                       ok = pthread_create(&thread->m_threadInfo, NULL,
+                           globalThreadStarterFunction, reinterpret_cast<void*>(thread))
+                           >= 0;
+#elif defined __WIN32__
+                       ok = (thread->m_threadInfo = CreateThread(NULL, 0, globalThreadStarterFunction,
+                                       reinterpret_cast<void*>(thread), 0, &thread->m_osThreadId)) != NULL;
+#endif
+                       if (!ok)
+                               m_logger->sayF(LOG_ERROR | CAT_PROCESS, LC_START_THREAD_1,
+                                   i18n("cannot create a thread: $1")).arg(getLastOSError())
+                                   .end();
+               }
+       }
+       return ok;
+}
+
+/**
+ * Waits for the end of all threads.
+ *
+ * Note: this method is threadsafe.
+ *
+ * @param timeoutSec   maximal time for waiting
+ * @return                             <code>true</code>: all threads are finished<br>
+ *                                             <code>false</code>: timeout reached
+ */
+bool ReThreadPool::waitForAlmostAll(int mayResist, int timeoutSec) {
+       bool rc = false;
+       time_t start = time(NULL);
+       time_t now;
+       do {
+               int countWaiting = 0;
+               m_mutexThreads.lock();
+               for (int ii = 0; ii < m_maxThreads; ii++) {
+                       ReThread* current = m_threads[ii];
+                       if (current != NULL && !current->m_isStopped) {
+                               countWaiting++;
+                       }
+               }
+               m_mutexThreads.unlock();
+               if (countWaiting <= mayResist) {
+                       rc = true;
+                       break;
+               }
+               millisecSleep(200);
+               now = time(NULL);
+       } while (now < start + timeoutSec);
+       return rc;
+}
+/**
+ * Waits for the end of all threads.
+ *
+ * Note: this method is threadsafe.
+ *
+ * @param timeoutSec   maximal time for waiting
+ * @return                             <code>true</code>: all threads are finished<br>
+ *                                             <code>false</code>: timeout reached
+ */
+bool ReThreadPool::waitForDone(int timeoutSec) {
+       bool rc = waitForAlmostAll(0, timeoutSec);
+       return rc;
+}
index 29eaea6ec11500f59609508754250b21efb32658..6980db8bb307ef97239bf81bb2a6002bfbaacf94 100644 (file)
-/*\r
- * ReThreadStarter.hpp\r
- *\r
- * License: Public domain\r
- * Do what you want.\r
- * No warranties and disclaimer of any damages.\r
- * The latest sources: https://github.com/republib\r
- */\r
-\r
-#ifndef BASE_RETHREAD_HPP_\r
-#define BASE_RETHREAD_HPP_\r
-\r
-class ReThreadPool;\r
-class ReSlaveAppender;\r
-/**\r
- * Abstract base class for threads\r
- *\r
- * Starting is done with a <code>ReThreadStarter</code>.\r
- */\r
-class ReThread: public ReLoggerOwner {\r
-public:\r
-       ReThread(bool autoDelete);\r
-       virtual ~ReThread();\r
-public:\r
-       /** Does the actual task.\r
-        * This method must do a <code>m_isStopped = true;</code> at the end!\r
-        */\r
-       virtual void run() = 0;\r
-       /** Returns if the thread is stopped.\r
-        * @return      <code>true</code>the thread is stopped\r
-        */\r
-       inline bool isStopped() const {\r
-               return m_isStopped;\r
-       }\r
-       virtual ReLogger* logger();\r
-       /** Sets the wish for stopping the thread.\r
-        * @param value <code>true</code>: the thread should stop as soon as possible\r
-        */\r
-       inline void setShouldStop(bool value) {\r
-               m_shouldStop = value;\r
-       }\r
-private:\r
-       friend class ReThreadPool;\r
-       void kill();\r
-       bool prepareToRun(int id, ReLogger* masterLogger, ReThreadPool* starter);\r
-#if defined __linux__\r
-       friend void* globalThreadStarterFunction(void *pConnection);\r
-#elif defined __WIN32__\r
-       friend DWORD WINAPI globalThreadStarterFunction(_In_ LPVOID pParameter);\r
-#endif\r
-       void runAndFinish();\r
-protected:\r
-       int m_threadId;\r
-       ReLogger m_threadLogger;\r
-       ReSlaveAppender* m_appender;\r
-       ReThreadPool* m_pool;\r
-#if defined __linux__\r
-       pthread_t m_threadInfo;\r
-#elif defined __WIN32__\r
-       HANDLE m_threadInfo;\r
-       DWORD m_osThreadId;\r
-#endif\r
-       bool m_shouldStop;\r
-       bool m_isStopped;\r
-       bool m_autoDelete;\r
-};\r
-\r
-class ReSimpleThread: public ReThread {\r
-public:\r
-       ReSimpleThread(ReProcessor* processor);\r
-       virtual ~ReSimpleThread();\r
-public:\r
-       virtual void run();\r
-private:\r
-       ReProcessor* m_processor;\r
-};\r
-/**\r
- * Offers a portable way to start threads.\r
- */\r
-class ReThreadPool {\r
-public:\r
-       ReThreadPool(int maxThreads, ReLogger* logger);\r
-       virtual ~ReThreadPool();\r
-public:\r
-       int countThreads();\r
-       void killAllThreads();\r
-       bool startSimpleThread(ReProcessor& processor);\r
-       bool startThread(ReThread* thread);\r
-       bool waitForAlmostAll(int mayResist, int timeoutSec);\r
-       bool waitForDone(int timeoutSec);\r
-       /** Returns whether the threads should stop.\r
-        * @return      <code>true</code>: all threads should stop\r
-        */\r
-       inline bool shouldStop() const {\r
-               return m_shouldStop;\r
-       }\r
-       /** Sets that the threads should stop.\r
-        */\r
-       inline void setShouldStop() {\r
-               m_shouldStop = true;\r
-       }\r
-private:\r
-       bool insertThread(ReThread* thread);\r
-private:\r
-       int m_nextId;\r
-       ReLogger* m_logger;\r
-       int m_maxThreads;\r
-       ReThread** m_threads;\r
-       int m_maxKillTimeSec;\r
-       ReMutex m_mutexThreads;\r
-       bool m_shouldStop;\r
-};\r
-#endif /* BASE_RETHREAD_HPP_ */\r
+/*
+ * ReThread.hpp
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
+ * Do what you want.
+ * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
+ * The latest sources: https://github.com/republib
+ */
+
+#ifndef BASE_RETHREAD_HPP_
+#define BASE_RETHREAD_HPP_
+
+class ReThreadPool;
+class ReSlaveAppender;
+/**
+ * Abstract base class for threads
+ *
+ * Starting is done with a <code>ReThreadStarter</code>.
+ */
+class ReThread: public ReLoggerOwner {
+public:
+       ReThread(bool autoDelete);
+       virtual ~ReThread();
+public:
+       /** Does the actual task.
+        * This method must do a <code>m_isStopped = true;</code> at the end!
+        */
+       virtual void run() = 0;
+       /** Returns if the thread is stopped.
+        * @return      <code>true</code>the thread is stopped
+        */
+       inline bool isStopped() const {
+               return m_isStopped;
+       }
+       virtual ReLogger* logger();
+       /** Sets the wish for stopping the thread.
+        * @param value <code>true</code>: the thread should stop as soon as possible
+        */
+       inline void setShouldStop(bool value) {
+               m_shouldStop = value;
+       }
+private:
+       friend class ReThreadPool;
+       void kill();
+       bool prepareToRun(int id, ReLogger* masterLogger, ReThreadPool* starter);
+#if defined __linux__
+       friend void* globalThreadStarterFunction(void *pConnection);
+#elif defined __WIN32__
+       friend DWORD WINAPI globalThreadStarterFunction(_In_ LPVOID pParameter);
+#endif
+       void runAndFinish();
+protected:
+       int m_threadId;
+       ReLogger m_threadLogger;
+       ReSlaveAppender* m_appender;
+       ReThreadPool* m_pool;
+#if defined __linux__
+       pthread_t m_threadInfo;
+#elif defined __WIN32__
+       HANDLE m_threadInfo;
+       DWORD m_osThreadId;
+#endif
+       bool m_shouldStop;
+       bool m_isStopped;
+       bool m_autoDelete;
+};
+
+class ReSimpleThread: public ReThread {
+public:
+       ReSimpleThread(ReProcessor* processor);
+       virtual ~ReSimpleThread();
+public:
+       virtual void run();
+private:
+       ReProcessor* m_processor;
+};
+/**
+ * Offers a portable way to start threads.
+ */
+class ReThreadPool {
+public:
+       ReThreadPool(int maxThreads, ReLogger* logger);
+       virtual ~ReThreadPool();
+public:
+       int countThreads();
+       void killAllThreads();
+       bool startSimpleThread(ReProcessor& processor);
+       bool startThread(ReThread* thread);
+       bool waitForAlmostAll(int mayResist, int timeoutSec);
+       bool waitForDone(int timeoutSec);
+       /** Returns whether the threads should stop.
+        * @return      <code>true</code>: all threads should stop
+        */
+       inline bool shouldStop() const {
+               return m_shouldStop;
+       }
+       /** Sets that the threads should stop.
+        */
+       inline void setShouldStop() {
+               m_shouldStop = true;
+       }
+private:
+       bool insertThread(ReThread* thread);
+private:
+       int m_nextId;
+       ReLogger* m_logger;
+       int m_maxThreads;
+       ReThread** m_threads;
+       int m_maxKillTimeSec;
+       ReMutex m_mutexThreads;
+       bool m_shouldStop;
+};
+#endif /* BASE_RETHREAD_HPP_ */
index 2c055957f22adef596664c844f5284303761d4a6..516cdc45285d95294535525d8170bba83e7a44f6 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * ReVarArgs.cpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 /**
index 2341a3f422017bd26f9ff9e9ee6d9b64d463b787..b0b60f95b3ea1767d0b36bf47672a8a8fbe51678 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * ReVarArgs.hpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
index 7aa301a0bbb206de36bc2bc2833e5f9763744d11..afa526a72021fba36b34f654442cf527cfe4ed8b 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * baselocations.hpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
 
 /** The files of the REal PUBlic LIB use the range from 50000 until 99999.
  */
-enum RELOC_LIB {
-       LC_CONFIGFILE = 50000,
-       LC_DIRTOOLS = 50100,
-       LC_SEQARRAY = 50200,
-       LC_HASHLIST = 50300,
-       LC_TRAVERSER = 50400,
-       LC_TCP = 50500,
-       LC_THREAD = 50600,
-       LC_LOGGER = 50700,
-};
-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,
+
+enum ReModules_t {
+       LOC_CONFIGFILE = 500,
+       LOC_DIRTOOLS,
+       LOC_SEQARRAY,
+       LOC_HASHLIST,
+       LOC_TRAVERSER,
+       LOC_TCP, /*505*/
+       LOC_THREAD,
+       LOC_LOGGER,
+       LOC_UDP,
+       LOC_CSTRING,
 };
+#define LOC_FIRST_OF(moduleNo) (moduleNo*100+1)
+
 #endif /* BASELOCATIONS_HPP_ */
index 673cb1df8a323f25e1b06581eecb22dd54c3f217..3d03b5f32c22df31c648bf22cbf2028789ef391f 100644 (file)
-/*\r
- * rebase.hpp\r
- *\r
- * License: Public domain\r
- * Do what you want.\r
- * No warranties and disclaimer of any damages.\r
- * The latest sources: https://github.com/republib\r
- */\r
-#ifndef REBASE_HPP_\r
-#define min()\r
-#define max()\r
-#define REBASE_HPP_\r
-#include <stdlib.h>\r
-#include <string.h>\r
-#include <malloc.h>\r
-#include <stdio.h>\r
-#include <sys/types.h>\r
-#include <sys/stat.h>\r
-#include <time.h>\r
-#include <errno.h>\r
-#include <ctype.h>\r
-#include <assert.h>\r
-#include <stdarg.h>\r
-#include <limits.h>\r
-\r
-#define __LITTLE_ENDIAN__\r
-//#define __BIG_ENDIAN__\r
-\r
-#if defined __linux__\r
-\r
-#      include <sys/time.h>\r
-#      include <stddef.h>\r
-#      include <dirent.h>\r
-#      include <fnmatch.h>\r
-#      include <regex.h>\r
-#      include <unistd.h>\r
-#      include <inttypes.h>\r
-#      include <fcntl.h>\r
-#include <pthread.h>\r
-#include <semaphore.h>\r
-#include <signal.h>\r
-typedef u_int64_t uint64_t;\r
-typedef u_int8_t uint8_t;\r
-typedef __off_t ReFileSize_t;\r
-typedef timespec ReFileTime_t;\r
-#      define _strdup strdup\r
-#      define _unlink unlink\r
-#      define _strnicmp(s1, s2, n) strncasecmp(s1, s2, n)\r
-#      define _stricmp(s1, s2) strcasecmp(s1, s2)\r
-#      define _snprintf snprintf\r
-#      define _memcmp(t,s,n) memcmp(t,s,n)\r
-#      define _mkdir(path, mode) mkdir(path, mode)\r
-#      define _rmdir(path) rmdir(path)\r
-#      define OS_SEPARATOR_CHAR '/'\r
-#      define OS_SEPARATOR "/"\r
-#      define millisecSleep(msec) usleep(msec*1000)\r
-inline int getLastOSError() {\r
-       return errno;\r
-}\r
-#elif defined __WIN32__\r
-#      include <direct.h>\r
-#      include <WinSock2.h>\r
-#      include <windows.h>\r
-#      define _memcmp(t,s,n) memcmp(t,s,n)\r
-#      define lstat stat\r
-#      define millisecSleep(msec) Sleep(msec)\r
-#      define OS_SEPARATOR_CHAR '\\'\r
-#      define OS_SEPARATOR "\\"\r
-typedef _int64 int64_t;\r
-typedef unsigned long long uint64_t;\r
-typedef unsigned char uint8_t;\r
-typedef unsigned long uint32_t;\r
-typedef long int int32_t;\r
-typedef int64_t ReFileSize_t;\r
-typedef FILETIME ReFileTime_t;\r
-#      define S_ISDIR(mode) (((mode) & _S_IFDIR) != 0)\r
-#      define ALLPERMS 0\r
-//#    define _mkdir(name, mode) (!CreateDirectory(name, NULL))\r
-#      define _mkdir(name, mode) _mkdir(name)\r
-inline int getLastOSError() {\r
-       return GetLastError();\r
-}\r
-#endif\r
-\r
-typedef int ReErrNo_t;\r
-/**\r
- * This base class allows to define classes with very common tasks.\r
- *\r
- * Example (a thread starter):\r
- * the class realizing the task is a sub class of <code>ReProcessor</code>.\r
- * The thread starter gets an instance of this task class and calls the\r
- * method <code>process()</code> inside the new thread.\r
- */\r
-class ReProcessor {\r
-public:\r
-       /** Destructor.\r
-        */\r
-       virtual ~ReProcessor() {\r
-       }\r
-public:\r
-       virtual void process() = 0;\r
-};\r
-#undef min\r
-#undef max\r
-/** Returns the minimum of 2 values.\r
- * @param a    first value\r
- * @param b 2nd value\r
- * @return     minimum of a and b\r
- */\r
-inline int min(int a, int b) {\r
-       return a < b ? a : b;\r
-}\r
-/** Returns the maximum of 2 values.\r
- * @param a    first value\r
- * @param b 2nd value\r
- * @return     maximum of a and b\r
- */\r
-inline int max(int a, int b) {\r
-       return a < b ? a : b;\r
-}\r
-#include "base/ReClassId.hpp"\r
-#include "base/ReException.hpp"\r
-#include "base/ReMutex.hpp"\r
-#include "base/ReByteBuffer.hpp"\r
-#include "base/ReSerializable.hpp"\r
-#include "base/ReVarArgs.hpp"\r
-#include "base/ReLogger.hpp"\r
-#include "base/ReThread.hpp"\r
-#include "base/ReTestUnit.hpp"\r
-#include "base/ReCString.hpp"\r
-#include "base/ReStringUtils.hpp"\r
-#include "base/ReDirectory.hpp"\r
-#include "base/ReSeqArray.hpp"\r
-#include "base/ReAppenders.hpp"\r
-#include "base/ReStringList.hpp"\r
-#include "base/ReHashList.hpp"\r
-#include "base/ReConfigFile.hpp"\r
-#include "base/ReI18N.hpp"\r
-#include "base/ReProgramArgs.hpp"\r
-#include "base/ReBaseUtils.hpp"\r
-\r
-#include "base/baselocations.hpp"\r
-#endif /* REBASE_HPP_ */\r
+/*
+ * rebase.hpp
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
+ * Do what you want.
+ * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
+ * The latest sources: https://github.com/republib
+ */
+#ifndef REBASE_HPP_
+#define REBASE_HPP_
+#define min()
+#define max()
+#include <stdlib.h>
+#include <string.h>
+#include <malloc.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <errno.h>
+#include <ctype.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <limits.h>
+
+#define __LITTLE_ENDIAN__
+//#define __BIG_ENDIAN__
+
+#if defined __linux__
+
+#      include <sys/time.h>
+#      include <stddef.h>
+#      include <dirent.h>
+#      include <fnmatch.h>
+#      include <regex.h>
+#      include <unistd.h>
+#      include <inttypes.h>
+#      include <fcntl.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <signal.h>
+typedef u_int64_t uint64_t;
+typedef u_int8_t uint8_t;
+typedef __off_t ReFileSize_t;
+enum ReOSType {
+       OS_UNDEF,
+       OS_LINUX = 'l',
+       OS_WIN32 = 'w'
+};
+typedef struct timespec ReFileTime_t;
+#      define _strdup strdup
+#      define _unlink unlink
+#      define _strnicmp(s1, s2, n) strncasecmp(s1, s2, n)
+#      define _stricmp(s1, s2) strcasecmp(s1, s2)
+#      define _snprintf snprintf
+#      define _memcmp(t,s,n) memcmp(t,s,n)
+#      define _mkdir(path, mode) mkdir(path, mode)
+#      define _rmdir(path) rmdir(path)
+#      define OS_SEPARATOR_CHAR '/'
+#      define OS_SEPARATOR "/"
+#      define millisecSleep(msec) usleep(msec*1000)
+inline int getLastOSError() {
+       return errno;
+}
+#elif defined __WIN32__
+#      include <direct.h>
+#      include <WinSock2.h>
+#      include <windows.h>
+#      define _memcmp(t,s,n) memcmp(t,s,n)
+#      define lstat stat
+#      define millisecSleep(msec) Sleep(msec)
+#      define OS_SEPARATOR_CHAR '\\'
+#      define OS_SEPARATOR "\\"
+typedef _int64 int64_t;
+typedef unsigned long long uint64_t;
+typedef unsigned char uint8_t;
+typedef unsigned long uint32_t;
+typedef long int int32_t;
+typedef int64_t ReFileSize_t;
+typedef FILETIME ReFileTime_t;
+#      define S_ISDIR(mode) (((mode) & _S_IFDIR) != 0)
+#      define ALLPERMS 0
+//#    define _mkdir(name, mode) (!CreateDirectory(name, NULL))
+#      define _mkdir(name, mode) _mkdir(name)
+inline int getLastOSError() {
+       return GetLastError();
+}
+#endif
+
+typedef int ReErrNo_t;
+/**
+ * This base class allows to define classes with very common tasks.
+ *
+ * Example (a thread starter):
+ * the class realizing the task is a sub class of <code>ReProcessor</code>.
+ * The thread starter gets an instance of this task class and calls the
+ * method <code>process()</code> inside the new thread.
+ */
+class ReProcessor {
+public:
+       /** Destructor.
+        */
+       virtual ~ReProcessor() {
+       }
+public:
+       virtual void process() = 0;
+};
+#undef min
+#undef max
+/** Returns the minimum of 2 values.
+ * @param a    first value
+ * @param b 2nd value
+ * @return     minimum of a and b
+ */
+inline int min(int a, int b) {
+       return a < b ? a : b;
+}
+/** Returns the maximum of 2 values.
+ * @param a    first value
+ * @param b 2nd value
+ * @return     maximum of a and b
+ */
+inline int max(int a, int b) {
+       return a < b ? a : b;
+}
+#include "base/ReClassId.hpp"
+#include "base/ReException.hpp"
+#include "base/ReMutex.hpp"
+#include "base/ReByteBuffer.hpp"
+#include "base/ReSerializable.hpp"
+#include "base/ReVarArgs.hpp"
+#include "base/ReLogger.hpp"
+#include "base/ReThread.hpp"
+#include "base/ReTestUnit.hpp"
+#include "base/ReCString.hpp"
+#include "base/ReStringUtils.hpp"
+#include "base/ReDirectory.hpp"
+#include "base/ReSeqArray.hpp"
+#include "base/ReAppenders.hpp"
+#include "base/ReStringList.hpp"
+#include "base/ReHashList.hpp"
+#include "base/ReConfigFile.hpp"
+#include "base/ReI18N.hpp"
+#include "base/ReProgramArgs.hpp"
+#include "base/ReBaseUtils.hpp"
+
+#include "base/baselocations.hpp"
+#endif /* REBASE_HPP_ */
index 82d9489c4e57fd787986f5223b5f2937cca01339..83e2d084cb6601f6487348a45978903d0f4116db 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * restring.hpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
index 9c8c397a83c5b291b2dc60786b487e8f9f650843..ffc13d5076e78ce9971130e0945469aff735014f 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * basetest.cpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 #include "base/rebase.hpp"
index 4a68ea17d671f49f447eeed7182a2d2f3f0819fc..cec85c1d9fc4b9aa434f23b4f1e36bc1a952c170 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * cuReByteBuffer.cpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 #include "base/rebase.hpp"
index 737a43cf7dd788da5c14fff95aa6d04850b555ee..6785988a9d8e71e5526f521df254ca4a67026906 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * cuReCString.cpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 #include "base/rebase.hpp"
index 6bd506f82daf0bd459d5d0c36d3bd9c01fbfefdc..3408da64a8752047477f6002a02442e498127b4f 100644 (file)
-/*\r
- * cuReTraverser.cpp\r
- *\r
- * License: Public domain\r
- * Do what you want.\r
- * No warranties and disclaimer of any damages.\r
- * The latest sources: https://github.com/republib\r
- */\r
-\r
-#include "base/rebase.hpp"\r
-#include "os/reos.hpp"\r
-\r
-static const char* s_empty[] = { NULL };\r
-// count of seconds from 1.1.1970 until 1.1.1980:\r
-const int tenYear = (365 * 10 + 2) * 24 * 3600;\r
-\r
-const char* s_allFiles[] = { //\r
-    " 1.txt", //\r
-        "*dir1", //\r
-        "*dir2", //\r
-        "*dir1/cache", //\r
-        "*dir1/dir1_1", //\r
-        "*dir1/dir1_2", //\r
-        "*dir1/dir1_2/dir1_2_1", //\r
-        " dir1/dir1_2/dir1_2_1/x1.txt", //\r
-        " dir1/dir1_2/dir1_2_1/x2.txt", //\r
-        " dir2/2.x", //\r
-        " dir1/cache/cache.txt", //\r
-        NULL };\r
-\r
-class TestReDirTools: public ReTestUnit {\r
-public:\r
-       TestReDirTools() :\r
-                   ReTestUnit("ReDirTools", __FILE__),\r
-                   // m_base\r
-                   // m_buffer\r
-                   m_testAll(true),\r
-                   m_logger(ReLogger::globalLogger()) {\r
-               m_base = testDir();\r
-               ReDirectory::deleteTree(m_base.str());\r
-               m_base.append("dirtool");\r
-               _mkdir(m_base.str(), ALLPERMS);\r
-               m_base.append(OS_SEPARATOR, -1);\r
-               m_testAll = true;\r
-               run();\r
-               ReDirectory::deleteTree(m_base.str(), true);\r
-       }\r
-private:\r
-       ReByteBuffer m_base;\r
-       ReByteBuffer m_buffer;\r
-       bool m_testAll;\r
-       ReLogger* m_logger;\r
-private:\r
-       void run() {\r
-               testRandom();\r
-               testDelete();\r
-               testChecksumBuild();\r
-               if (m_testAll) {\r
-                       testDirOptions();\r
-                       checkSetFilterFromProgramArgs();\r
-                       testList2();\r
-                       testToolStatistic();\r
-                       testBasic();\r
-                       testDirStatistic();\r
-                       testCopyFile();\r
-                       testList();\r
-                       testToolSync();\r
-                       testBatch();\r
-                       testChecksumBuild();\r
-                       testDelete();\r
-                       testRandom();\r
-               }\r
-       }\r
-       void testRandom() {\r
-               const char* argv[] = { "random", "-l20", "-s", "40", "50",\r
-               NULL };\r
-               ReDirRandom(m_logger).run(-1, argv);\r
-       }\r
-       void testDelete() {\r
-               rebuildTree();\r
-               ReByteBuffer nameCurrent;\r
-               buildFilename("current", nameCurrent);\r
-               ReByteBuffer optOutput("--output-file=");\r
-               optOutput.append(nameCurrent);\r
-               const char* argv[] = { "delete", "-p;x*.txt", optOutput.str(), m_base\r
-                   .str(), NULL };\r
-               const char* existing[] = { //\r
-                   " 1.txt", //\r
-                       "*dir1", //\r
-                       "*dir2", //\r
-                       "*dir1/cache", //\r
-                       "*dir1/dir1_1", //\r
-                       "*dir1/dir1_2", //\r
-                       "*dir1/dir1_2/dir1_2_1", //\r
-                       " dir2/2.x", //\r
-                       " dir1/cache/cache.txt", //\r
-                       NULL };\r
-               const char* notExisting[] = { //\r
-                   " dir1/dir1_2/dir1_2_1/x1.txt", //\r
-                       " dir1/dir1_2/dir1_2_1/x2.txt", //\r
-                       NULL };\r
-               ReDirDelete(m_logger).run(-1, argv);\r
-               mustExist(existing);\r
-               mayNotExist(notExisting);\r
-\r
-               const char* argv2[] = { "delete", optOutput.str(), m_base.str(), NULL };\r
-               ReDirDelete(m_logger).run(-1, argv2);\r
-               mayNotExist(s_allFiles);\r
-       }\r
-       void mustExist(const char** names) {\r
-               ReByteBuffer name;\r
-               struct stat info;\r
-               for (int ix = 0; names[ix] != NULL; ix++) {\r
-                       const char* arg = names[ix];\r
-                       name = m_base;\r
-                       name.ensureLastChar(OS_SEPARATOR_CHAR);\r
-                       name.append(arg + 1);\r
-                       name.replaceAll("/", -1, OS_SEPARATOR, -1);\r
-                       if (stat(name.str(), &info) != 0)\r
-                               checkEqu("missing", name);\r
-                       bool isDir = arg[0] == '*';\r
-                       if (isDir && !S_ISDIR(info.st_mode))\r
-                               checkEqu("file not dir", name);\r
-                       if (!isDir && S_ISDIR(info.st_mode))\r
-                               checkEqu("dir not file", name);\r
-               }\r
-       }\r
-       void mayNotExist(const char** names) {\r
-               ReByteBuffer name;\r
-               struct stat info;\r
-               for (int ix = 0; names[ix] != NULL; ix++) {\r
-                       const char* arg = names[ix];\r
-                       name = m_base;\r
-                       name.ensureLastChar(OS_SEPARATOR_CHAR);\r
-                       name.append(arg + 1);\r
-                       name.replaceAll("/", -1, OS_SEPARATOR, -1);\r
-                       if (stat(name.str(), &info) == 0)\r
-                               checkEqu("exists", name);\r
-               }\r
-       }\r
-       int secOfFileTime(ReFileTime_t data) {\r
-#if defined __linux__\r
-               return data.tv_sec;\r
-#elif defined __WIN32__\r
-               return (int) ReDirStatus_t::filetimeToTime(&data);\r
-#endif\r
-       }\r
-       const char* makeDir(const char* relPath) {\r
-               m_buffer = m_base;\r
-               m_buffer.append(relPath);\r
-               m_buffer.replaceAll("/", 1, OS_SEPARATOR, -1);\r
-               _mkdir(m_buffer.str(), ALLPERMS);\r
-               struct stat info;\r
-               if (stat(m_buffer.str(), &info) != 0) {\r
-                       logF(true, "cannot create dir %s", m_buffer.str());\r
-               }\r
-               return m_buffer.str();\r
-       }\r
-       void makeFile(const char* relPath) {\r
-               ReByteBuffer path(m_base);\r
-               path.append(relPath);\r
-               path.replaceAll("/", 1, OS_SEPARATOR, -1);\r
-               createFile(path.str(), relPath);\r
-               struct stat info;\r
-               if (stat(path.str(), &info) != 0) {\r
-                       logF(true, "cannot create file %s", path.str());\r
-               }\r
-       }\r
-       void initTree() {\r
-               for (int ix = 0; s_allFiles[ix] != 0; ix++) {\r
-                       const char* name = s_allFiles[ix];\r
-                       if (name[0] == '*')\r
-                               makeDir(name + 1);\r
-                       else\r
-                               makeFile(name + 1);\r
-               }\r
-       }\r
-       void rebuildTree() {\r
-               ReDirectory::deleteTree(m_base.str(), false);\r
-               initTree();\r
-       }\r
-       void testChecksumBuild() {\r
-               rebuildTree();\r
-               ReByteBuffer nameCurrent;\r
-               buildFilename("current", nameCurrent);\r
-               ReByteBuffer optOutput("--output-file=");\r
-               optOutput.append(nameCurrent);\r
-               const char* argv[] = { "dt", "checksum", "-P;*;-cache", "-tr",\r
-                   "-p;*.txt", "-s4711", "-arpd64", "-cBuild", optOutput.str(), m_base\r
-                       .str(), NULL };\r
-               ReDirTools tools;\r
-               tools.main(-1, (char**) argv);\r
-               // checkFile(nameCurrent, "");\r
-               const char* argv2[] = { "dt", "checksum", "-P;*;-cache", "-tr",\r
-                   "-p;*.txt", "-s4711", "-arpd64", "-cCompare", optOutput.str(),\r
-                   m_base.str(), NULL };\r
-               tools.main(-1, (char**) argv2);\r
-       }\r
-       // F:\temp\retestunit\dirtool\r
-       void testList2() {\r
-               rebuildTree();\r
-               const char* argv[] = { "dt", "list", "-r", m_base.str(), NULL };\r
-               ReDirTools tools;\r
-               tools.main(4, (char**) argv);\r
-       }\r
-       void testList() {\r
-               rebuildTree();\r
-               const char* argv[] = { "list", m_base.str(), NULL };\r
-               ReDirList(m_logger).run(2, argv);\r
-       }\r
-       void testCopyFile() {\r
-               rebuildTree();\r
-#if defined __linux__\r
-               ReByteBuffer src(m_base);\r
-               src.append("dir1/dir1_2/dir1_2_1/x1.txt");\r
-               ReByteBuffer trg(testDir());\r
-               trg.append("copy_x1.txt");\r
-               ReByteBuffer buffer;\r
-               buffer.ensureSize(5);\r
-               ReDirSync::copyFile(src.str(), NULL, trg.str(), buffer,\r
-                   ReLogger::globalLogger());\r
-               checkFileEqu(src.str(), trg.str());\r
-#else\r
-               log(false, "testCopyFile not implemented");\r
-#endif\r
-       }\r
-\r
-       void checkRelDate(ReFileTime_t absTime, int relTime) {\r
-               int diffNow = int(time(NULL) - secOfFileTime(absTime));\r
-               int diff =\r
-                   diffNow * relTime < 0 ? diffNow + relTime : diffNow - relTime;\r
-               if (diff < 0)\r
-                       diff = -diff;\r
-               checkT(diff < 2);\r
-       }\r
-       void testDirOptions() {\r
-               class MyOptions: public ReDirOptions {\r
-               public:\r
-                       MyOptions() :\r
-                                   ReDirOptions(s_empty, s_empty) {\r
-                       }\r
-               public:\r
-                       int count() {\r
-                               return m_countCompoundUsage;\r
-                       }\r
-                       const char** usage() {\r
-                               return m_compoundUsage;\r
-                       }\r
-               };\r
-               static const char* usage1[] = { "line1", "line2", NULL };\r
-               static const char* usage2[] = { "x1", "x2", "x3", NULL };\r
-               MyOptions opts;\r
-               opts.initCompoundUsage(sizeof usage1 + sizeof usage2);\r
-               opts.addCompoundUsage(usage1);\r
-               opts.addCompoundUsage(usage2);\r
-               checkEqu(7, opts.count());\r
-               checkEqu("line1", opts.usage()[0]);\r
-               checkEqu("line2", opts.usage()[1]);\r
-               checkEqu("x1", opts.usage()[2]);\r
-               checkEqu("x2", opts.usage()[3]);\r
-               checkEqu("x3", opts.usage()[4]);\r
-\r
-               // local time: +3600\r
-               const int DIFF = 3600;\r
-               checkEqu(tenYear + 24 * 60 * 60 - DIFF,\r
-                   secOfFileTime(opts.checkDate("1980.01.02")));\r
-               checkEqu(tenYear + 24 * 60 * 60 + 3600 - DIFF,\r
-                   secOfFileTime(opts.checkDate("1980.01.02/1")));\r
-               checkEqu(tenYear + 24 * 60 * 60 + 2 * 3600 + 33 * 60 - DIFF,\r
-                   secOfFileTime(opts.checkDate("1980.01.02/02:33")));\r
-               checkRelDate(opts.checkDate("now-3m"), 3 * 60);\r
-               checkRelDate(opts.checkDate("now-7h"), 7 * 60 * 60);\r
-               checkRelDate(opts.checkDate("now-5d"), 5 * 24 * 60 * 60);\r
-\r
-               checkEqu(125ll, opts.checkSize("125"));\r
-               checkEqu(125ll, opts.checkSize("125b"));\r
-               checkEqu(3000ll, opts.checkSize("3k"));\r
-               checkEqu(3 * 1024ll, opts.checkSize("3K"));\r
-               checkEqu(4 * 1000 * 1000ll, opts.checkSize("4m"));\r
-               checkEqu(4 * 1024 * 1024ll, opts.checkSize("4M"));\r
-               checkEqu(5 * 1000 * 1000 * 1000ll, opts.checkSize("5g"));\r
-               checkEqu(5 * 1024 * 1024 * 1024ll, opts.checkSize("5G"));\r
-\r
-       }\r
-       void checkSetFilterFromProgramArgs() {\r
-               ReDirOptions opts(s_empty, s_empty);\r
-               opts.addStandardFilterOptions();\r
-               const char* argv[] = { "x", "-y1980.01.02", "-o1980.01.03", "-D5",\r
-                   "-d1", "-z1k", "-Z2M", "-p;*;-*~" };\r
-               ReDirEntryFilter filter;\r
-               opts.programArgsChangeable().init(sizeof argv / sizeof argv[0], argv);\r
-               opts.setFilterFromProgramArgs(filter);\r
-               // local time: +3600\r
-               const int DIFF = 3600;\r
-               checkEqu(tenYear + 1 * 24 * 3600 - DIFF,\r
-                   secOfFileTime(filter.m_maxAge));\r
-               checkEqu(tenYear + 2 * 24 * 3600 - DIFF,\r
-                   secOfFileTime(filter.m_minAge));\r
-               checkEqu(5, (int ) filter.m_maxDepth);\r
-               checkEqu(1, (int ) filter.m_minDepth);\r
-               checkEqu(1000ll, filter.m_minSize);\r
-               checkEqu(2 * 1024 * 1024ll, filter.m_maxSize);\r
-               checkNN(filter.m_nodePatterns);\r
-               checkEqu(";*;-*~", filter.m_nodePatterns->patternString());\r
-       }\r
-       void checkOneFile(const char* node, const char* parent,\r
-           const ReHashList& hash) {\r
-               ReByteBuffer path, expected;\r
-               checkT(hash.get(ReByteBuffer(node), path));\r
-               expected.set(parent, -1);\r
-               if (!expected.endsWith(OS_SEPARATOR))\r
-                       expected.append(OS_SEPARATOR);\r
-               if (!path.endsWith(expected.str(), -1))\r
-                       checkT(false);\r
-       }\r
-       void testBasic() {\r
-               rebuildTree();\r
-               ReTraverser traverser(m_base.str());\r
-               RePatternList patterns;\r
-               // exclude */cache/*\r
-               ReByteBuffer buffer(";*;-cache");\r
-               patterns.set(buffer.str());\r
-               traverser.setDirPattern(&patterns);\r
-               int level = 0;\r
-               ReDirStatus_t* entry;\r
-               ReHashList hash;\r
-               while ((entry = traverser.rawNextFile(level)) != NULL) {\r
-                       hash.put(ReByteBuffer(entry->node(), -1), entry->m_path);\r
-                       logF(false, "%d: %-12s %2d %s", level, entry->node(),\r
-                           int(entry->fileSize()), entry->m_path.str());\r
-               }\r
-               checkOneFile("x1.txt", "dir1_2_1", hash);\r
-               checkOneFile("x2.txt", "dir1_2_1", hash);\r
-               checkOneFile("dir1_2_1", "dir1_2", hash);\r
-               checkOneFile("dir1_1", "dir1", hash);\r
-               checkOneFile("dir1_2", "dir1", hash);\r
-               checkF(hash.get("cache.txt", buffer));\r
-       }\r
-       void testDirStatistic() {\r
-               rebuildTree();\r
-               ReDirStatistic stat(m_logger);\r
-               const ReStringList& list = stat.calculate(m_base.str(), 1);\r
-               ReByteBuffer buffer;\r
-               ReByteBuffer expected;\r
-               log(false, list.join("\n", buffer).str());\r
-               checkEqu(3u, list.count());\r
-               // "1   t:\temp\winfried\2\retestunit\dir1\n"\r
-               buffer.set(list.strOf(0), list.strLengthOf(0));\r
-               checkT(buffer.startsWith("1\t"));\r
-               expected.set(m_base.str(), m_base.length()).append("dir1", -1).append(\r
-               OS_SEPARATOR);\r
-               // .append(OS_SEPARATOR, -1)\r
-               checkT(buffer.endsWith(expected.str()));\r
-\r
-               buffer.setLength(0);\r
-               const ReStringList& list2 = stat.calculate(m_base.str(), 1,\r
-                   formatWithSizeFilesAndDirs);\r
-               log(false, list2.join("\n", buffer).str());\r
-\r
-               buffer.set(list.strOf(0), list.strLengthOf(0));\r
-               checkT(buffer.startsWith("      0.000074 MB       3       4\t"));\r
-               expected.set(m_base.str(), m_base.length()).append("dir1", -1).append(\r
-               OS_SEPARATOR);\r
-               checkT(buffer.endsWith(expected.str()));\r
-\r
-               buffer.set(list.strOf(1), list.strLengthOf(1));\r
-               checkT(buffer.startsWith("      0.000008 MB       1       0\t"));\r
-               expected.set(m_base.str(), m_base.length()).append("dir2", -1).append(\r
-               OS_SEPARATOR);\r
-               checkT(buffer.endsWith(expected.str()));\r
-\r
-               buffer.set(list.strOf(2), list.strLengthOf(2));\r
-               checkT(buffer.startsWith("      0.000087 MB       5       6\t"));\r
-               expected.set(m_base.str(), m_base.length());\r
-       }\r
-       /**\r
-        * Tests a file with a given context.\r
-        *\r
-        * @param filename      name of the file to test\r
-        * @param content       expected content: each line must contain a '*',\r
-        *                                      separating the start of line and the end of line\r
-        */\r
-       void checkFile(ReByteBuffer& filename, const char* content) {\r
-               ReStringList current;\r
-               current.readFromFile(filename.str());\r
-               ReStringList expected;\r
-               expected.split(content, '\n');\r
-               checkEqu(expected.count(), current.count());\r
-               ReByteBuffer line;\r
-               ReStringList cols;\r
-               for (size_t ix = 0; ix < current.count(); ix++) {\r
-                       line.setLength(0).append(current.strOf(ix), -1);\r
-#if defined __WIN32__\r
-                       line.replaceAll("/", 1, OS_SEPARATOR, 1);\r
-#endif\r
-                       cols.split(expected.strOf(ix), '*');\r
-                       const char* col1 = cols.strOf(0);\r
-                       if (!line.startsWith(col1))\r
-                               checkEqu(col1, line);\r
-                       const char* col2 = cols.strOf(1);\r
-                       if (!line.endsWith(col2)) {\r
-                               checkEqu(col2, line);\r
-                       }\r
-               }\r
-       }\r
-       void testTouch() {\r
-               rebuildTree();\r
-               ReByteBuffer nameCurrent;\r
-               buildFilename("current", nameCurrent);\r
-               ReByteBuffer optOutput("--output-file=");\r
-               optOutput.append(nameCurrent);\r
-               ReDirTools tools;\r
-               const char* argv[] = { "dt", "touch", "-P;*;-cache", "-tr", "-p;*.txt",\r
-                   "-m2015.03.28/10:21:31", optOutput.str(), m_base.str(), NULL };\r
-               tools.main(8, (char**) argv);\r
-\r
-               checkFile(nameCurrent,\r
-                   "2015.03.28 10:21:31 *dirtool/1.txt\n"\r
-                           "2015.03.28 10:21:31 *dirtool/dir1/dir1_2/dir1_2_1/x1.txt\n"\r
-                           "2015.03.28 10:21:31 *dirtool/dir1/dir1_2/dir1_2_1/x2.txt\n"\r
-                           "=== filtered:        3 file(s)     0.000059 MByte       0 dirs(s) */sec\n"\r
-                           "===    total:        4 file(s)     0.000067 MByte       6 dirs(s) * sec");\r
-\r
-               const char* argv2[] = { "dt", "list", "-P;*;-cache", "-tr", "-p;*.txt",\r
-                   "-h", optOutput.str(), m_base.str(), NULL };\r
-               tools.main(8, (char**) argv2);\r
-\r
-               checkFile(nameCurrent,\r
-                   "      0.000005 2015.03.28 10:21:31 *dirtool/1.txt\n"\r
-                           "      0.000027 2015.03.28 10:21:31 *dirtool/dir1/dir1_2/dir1_2_1/x1.txt\n"\r
-                           "      0.000027 2015.03.28 10:21:31 *dirtool/dir1/dir1_2/dir1_2_1/x2.txt\n"\r
-                           "=== filtered:        3 file(s)     0.000059 MByte       0 dirs(s) */sec\n"\r
-                           "===    total:        4 file(s)     0.000067 MByte       6 dirs(s) * sec");\r
-\r
-       }\r
-       void testBatch() {\r
-               rebuildTree();\r
-               ReByteBuffer nameCurrent;\r
-               buildFilename("current", nameCurrent);\r
-               ReByteBuffer optOutput("--output-file=");\r
-               optOutput.append(nameCurrent);\r
-               ReDirTools tools;\r
-               const char* argv[] = { "dt", "batch", "-P;*;-cache", "-td",\r
-                   "-cmyscript", optOutput.str(), m_base.str(), NULL };\r
-               tools.main(7, (char**) argv);\r
-#if defined __linux__\r
-               static const char* content =\r
-                   "#! /bin/s*h\n"\r
-                           "myscript '*dirtool/dir1'\n"\r
-                           "myscript '*dirtool/dir2'\n"\r
-                           "myscript '*dirtool/dir1/dir1_1'\n"\r
-                           "myscript '*dirtool/dir1/dir1_2'\n"\r
-                           "myscript '*dirtool/dir1/cache'\n"\r
-                           "myscript '*dirtool/dir1/dir1_2/dir1_2_1'\n"\r
-                           "# === filtered:        0 file(s)     0.000000 MByte       6 dirs(s) */sec\n"\r
-                           "# ===    total:        4 file(s)     0.000067 MByte       6 dirs(s) * sec";\r
-#elif defined __WIN32__\r
-               static const char* content = "rem this batch is created by*dirtool\n"\r
-               "call myscript *dirtool\\dir1\"\n"\r
-               "call myscript *dirtool\\dir2\"\n"\r
-               "call myscript *dirtool\\dir1\\cache\"\n"\r
-               "call myscript *dirtool\\dir1\\dir1_1\"\n"\r
-               "call myscript *dirtool\\dir1\\dir1_2\"\n"\r
-               "call myscript *dirtool\\dir1\\dir1_2\\dir1_2_1\"\n"\r
-               "rem === filtered:        0 file(s)     0.000000 MByte       6 dirs(s) *sec\n"\r
-               "rem ===    total:        4 file(s)     0.000067 MByte       6 dirs(s) * sec";\r
-#endif\r
-               checkFile(nameCurrent, content);\r
-       }\r
-       void testToolStatistic() {\r
-               rebuildTree();\r
-               ReDirTools tools;\r
-               const char* argv[] = { "dt", "stat", "-P;*;-cache", m_base.str(), "2" };\r
-               tools.main(5, (char**) argv);\r
-       }\r
-       void testToolSync() {\r
-               rebuildTree();\r
-               ReDirTools tools;\r
-               ReByteBuffer source(m_base);\r
-               source.append("dir1");\r
-               ReByteBuffer target(testDir());\r
-               target.ensureLastChar(OS_SEPARATOR_CHAR);\r
-               target.append("synctest", -1);\r
-               _mkdir(target.str(), ALLPERMS);\r
-               const char* argv[] = { "dt", "sync", "-P;*;-cache", "-p;*.txt", source\r
-                   .str(), target.str() };\r
-               tools.main(6, (char**) argv);\r
-       }\r
-};\r
-extern void testReDirTools(void);\r
-\r
-void testReDirTools(void) {\r
-       TestReDirTools unit;\r
-}\r
+/*
+ * cuReDirTools.cpp
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
+ * Do what you want.
+ * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
+ * The latest sources: https://github.com/republib
+ */
+
+#include "base/rebase.hpp"
+#include "os/reos.hpp"
+
+static const char* s_empty[] = { NULL };
+// count of seconds from 1.1.1970 until 1.1.1980:
+const int tenYear = (365 * 10 + 2) * 24 * 3600;
+
+const char* s_allFiles[] = { //
+    " 1.txt", //
+        "*dir1", //
+        "*dir2", //
+        "*dir1/cache", //
+        "*dir1/dir1_1", //
+        "*dir1/dir1_2", //
+        "*dir1/dir1_2/dir1_2_1", //
+        " dir1/dir1_2/dir1_2_1/x1.txt", //
+        " dir1/dir1_2/dir1_2_1/x2.txt", //
+        " dir2/2.x", //
+        " dir1/cache/cache.txt", //
+        NULL };
+
+class TestReDirTools: public ReTestUnit {
+public:
+       TestReDirTools() :
+                   ReTestUnit("ReDirTools", __FILE__),
+                   // m_base
+                   // m_buffer
+                   m_testAll(true),
+                   m_logger(ReLogger::globalLogger()) {
+               m_base = testDir();
+               ReDirectory::deleteTree(m_base.str());
+               m_base.append("dirtool");
+               _mkdir(m_base.str(), ALLPERMS);
+               m_base.append(OS_SEPARATOR, -1);
+               m_testAll = true;
+               run();
+               ReDirectory::deleteTree(m_base.str(), true);
+       }
+private:
+       ReByteBuffer m_base;
+       ReByteBuffer m_buffer;
+       bool m_testAll;
+       ReLogger* m_logger;
+private:
+       void run() {
+               testRandom();
+               testDelete();
+               testChecksumBuild();
+               if (m_testAll) {
+                       testDirOptions();
+                       checkSetFilterFromProgramArgs();
+                       testList2();
+                       testToolStatistic();
+                       testBasic();
+                       testDirStatistic();
+                       testCopyFile();
+                       testList();
+                       testToolSync();
+                       testBatch();
+                       testChecksumBuild();
+                       testDelete();
+                       testRandom();
+               }
+       }
+       void testRandom() {
+               const char* argv[] = { "random", "-l20", "-s", "40", "50",
+               NULL };
+               ReDirRandom(m_logger).run(-1, argv);
+       }
+       void testDelete() {
+               rebuildTree();
+               ReByteBuffer nameCurrent;
+               buildFilename("current", nameCurrent);
+               ReByteBuffer optOutput("--output-file=");
+               optOutput.append(nameCurrent);
+               const char* argv[] = { "delete", "-p;x*.txt", optOutput.str(), m_base
+                   .str(), NULL };
+               const char* existing[] = { //
+                   " 1.txt", //
+                       "*dir1", //
+                       "*dir2", //
+                       "*dir1/cache", //
+                       "*dir1/dir1_1", //
+                       "*dir1/dir1_2", //
+                       "*dir1/dir1_2/dir1_2_1", //
+                       " dir2/2.x", //
+                       " dir1/cache/cache.txt", //
+                       NULL };
+               const char* notExisting[] = { //
+                   " dir1/dir1_2/dir1_2_1/x1.txt", //
+                       " dir1/dir1_2/dir1_2_1/x2.txt", //
+                       NULL };
+               ReDirDelete(m_logger).run(-1, argv);
+               mustExist(existing);
+               mayNotExist(notExisting);
+
+               const char* argv2[] = { "delete", optOutput.str(), m_base.str(), NULL };
+               ReDirDelete(m_logger).run(-1, argv2);
+               mayNotExist(s_allFiles);
+       }
+       void mustExist(const char** names) {
+               ReByteBuffer name;
+               struct stat info;
+               for (int ix = 0; names[ix] != NULL; ix++) {
+                       const char* arg = names[ix];
+                       name = m_base;
+                       name.ensureLastChar(OS_SEPARATOR_CHAR);
+                       name.append(arg + 1);
+                       name.replaceAll("/", -1, OS_SEPARATOR, -1);
+                       if (stat(name.str(), &info) != 0)
+                               checkEqu("missing", name);
+                       bool isDir = arg[0] == '*';
+                       if (isDir && !S_ISDIR(info.st_mode))
+                               checkEqu("file not dir", name);
+                       if (!isDir && S_ISDIR(info.st_mode))
+                               checkEqu("dir not file", name);
+               }
+       }
+       void mayNotExist(const char** names) {
+               ReByteBuffer name;
+               struct stat info;
+               for (int ix = 0; names[ix] != NULL; ix++) {
+                       const char* arg = names[ix];
+                       name = m_base;
+                       name.ensureLastChar(OS_SEPARATOR_CHAR);
+                       name.append(arg + 1);
+                       name.replaceAll("/", -1, OS_SEPARATOR, -1);
+                       if (stat(name.str(), &info) == 0)
+                               checkEqu("exists", name);
+               }
+       }
+       int secOfFileTime(ReFileTime_t data) {
+#if defined __linux__
+               return data.tv_sec;
+#elif defined __WIN32__
+               return (int) ReDirStatus_t::filetimeToTime(&data);
+#endif
+       }
+       const char* makeDir(const char* relPath) {
+               m_buffer = m_base;
+               m_buffer.append(relPath);
+               m_buffer.replaceAll("/", 1, OS_SEPARATOR, -1);
+               _mkdir(m_buffer.str(), ALLPERMS);
+               struct stat info;
+               if (stat(m_buffer.str(), &info) != 0) {
+                       logF(true, "cannot create dir %s", m_buffer.str());
+               }
+               return m_buffer.str();
+       }
+       void makeFile(const char* relPath) {
+               ReByteBuffer path(m_base);
+               path.append(relPath);
+               path.replaceAll("/", 1, OS_SEPARATOR, -1);
+               createFile(path.str(), relPath);
+               struct stat info;
+               if (stat(path.str(), &info) != 0) {
+                       logF(true, "cannot create file %s", path.str());
+               }
+       }
+       void initTree() {
+               for (int ix = 0; s_allFiles[ix] != 0; ix++) {
+                       const char* name = s_allFiles[ix];
+                       if (name[0] == '*')
+                               makeDir(name + 1);
+                       else
+                               makeFile(name + 1);
+               }
+       }
+       void rebuildTree() {
+               ReDirectory::deleteTree(m_base.str(), false);
+               initTree();
+       }
+       void testChecksumBuild() {
+               rebuildTree();
+               ReByteBuffer nameCurrent;
+               buildFilename("current", nameCurrent);
+               ReByteBuffer optOutput("--output-file=");
+               optOutput.append(nameCurrent);
+               const char* argv[] = { "dt", "checksum", "-P;*;-cache", "-tr",
+                   "-p;*.txt", "-s4711", "-arpd64", "-cBuild", optOutput.str(), m_base
+                       .str(), NULL };
+               ReDirTools tools;
+               tools.main(-1, (char**) argv);
+               // checkFile(nameCurrent, "");
+               const char* argv2[] = { "dt", "checksum", "-P;*;-cache", "-tr",
+                   "-p;*.txt", "-s4711", "-arpd64", "-cCompare", optOutput.str(),
+                   m_base.str(), NULL };
+               tools.main(-1, (char**) argv2);
+       }
+       // F:\temp\retestunit\dirtool
+       void testList2() {
+               rebuildTree();
+               const char* argv[] = { "dt", "list", "-r", m_base.str(), NULL };
+               ReDirTools tools;
+               tools.main(4, (char**) argv);
+       }
+       void testList() {
+               rebuildTree();
+               const char* argv[] = { "list", m_base.str(), NULL };
+               ReDirList(m_logger).run(2, argv);
+       }
+       void testCopyFile() {
+               rebuildTree();
+#if defined __linux__
+               ReByteBuffer src(m_base);
+               src.append("dir1/dir1_2/dir1_2_1/x1.txt");
+               ReByteBuffer trg(testDir());
+               trg.append("copy_x1.txt");
+               ReByteBuffer buffer;
+               buffer.ensureSize(5);
+               ReDirSync::copyFile(src.str(), NULL, trg.str(), buffer,
+                   ReLogger::globalLogger());
+               checkFileEqu(src.str(), trg.str());
+#else
+               log(false, "testCopyFile not implemented");
+#endif
+       }
+
+       void checkRelDate(ReFileTime_t absTime, int relTime) {
+               int diffNow = int(time(NULL) - secOfFileTime(absTime));
+               int diff =
+                   diffNow * relTime < 0 ? diffNow + relTime : diffNow - relTime;
+               if (diff < 0)
+                       diff = -diff;
+               checkT(diff < 2);
+       }
+       void testDirOptions() {
+               class MyOptions: public ReDirOptions {
+               public:
+                       MyOptions() :
+                                   ReDirOptions(s_empty, s_empty) {
+                       }
+               public:
+                       int count() {
+                               return m_countCompoundUsage;
+                       }
+                       const char** usage() {
+                               return m_compoundUsage;
+                       }
+               };
+               static const char* usage1[] = { "line1", "line2", NULL };
+               static const char* usage2[] = { "x1", "x2", "x3", NULL };
+               MyOptions opts;
+               opts.initCompoundUsage(sizeof usage1 + sizeof usage2);
+               opts.addCompoundUsage(usage1);
+               opts.addCompoundUsage(usage2);
+               checkEqu(7, opts.count());
+               checkEqu("line1", opts.usage()[0]);
+               checkEqu("line2", opts.usage()[1]);
+               checkEqu("x1", opts.usage()[2]);
+               checkEqu("x2", opts.usage()[3]);
+               checkEqu("x3", opts.usage()[4]);
+
+               // local time: +3600
+               const int DIFF = 3600;
+               checkEqu(tenYear + 24 * 60 * 60 - DIFF,
+                   secOfFileTime(opts.checkDate("1980.01.02")));
+               checkEqu(tenYear + 24 * 60 * 60 + 3600 - DIFF,
+                   secOfFileTime(opts.checkDate("1980.01.02/1")));
+               checkEqu(tenYear + 24 * 60 * 60 + 2 * 3600 + 33 * 60 - DIFF,
+                   secOfFileTime(opts.checkDate("1980.01.02/02:33")));
+               checkRelDate(opts.checkDate("now-3m"), 3 * 60);
+               checkRelDate(opts.checkDate("now-7h"), 7 * 60 * 60);
+               checkRelDate(opts.checkDate("now-5d"), 5 * 24 * 60 * 60);
+
+               checkEqu(125ll, opts.checkSize("125"));
+               checkEqu(125ll, opts.checkSize("125b"));
+               checkEqu(3000ll, opts.checkSize("3k"));
+               checkEqu(3 * 1024ll, opts.checkSize("3K"));
+               checkEqu(4 * 1000 * 1000ll, opts.checkSize("4m"));
+               checkEqu(4 * 1024 * 1024ll, opts.checkSize("4M"));
+               checkEqu(5 * 1000 * 1000 * 1000ll, opts.checkSize("5g"));
+               checkEqu(5 * 1024 * 1024 * 1024ll, opts.checkSize("5G"));
+
+       }
+       void checkSetFilterFromProgramArgs() {
+               ReDirOptions opts(s_empty, s_empty);
+               opts.addStandardFilterOptions();
+               const char* argv[] = { "x", "-y1980.01.02", "-o1980.01.03", "-D5",
+                   "-d1", "-z1k", "-Z2M", "-p;*;-*~" };
+               ReDirEntryFilter filter;
+               opts.programArgsChangeable().init(sizeof argv / sizeof argv[0], argv);
+               opts.setFilterFromProgramArgs(filter);
+               // local time: +3600
+               const int DIFF = 3600;
+               checkEqu(tenYear + 1 * 24 * 3600 - DIFF,
+                   secOfFileTime(filter.m_maxAge));
+               checkEqu(tenYear + 2 * 24 * 3600 - DIFF,
+                   secOfFileTime(filter.m_minAge));
+               checkEqu(5, (int ) filter.m_maxDepth);
+               checkEqu(1, (int ) filter.m_minDepth);
+               checkEqu(1000ll, filter.m_minSize);
+               checkEqu(2 * 1024 * 1024ll, filter.m_maxSize);
+               checkNN(filter.m_nodePatterns);
+               checkEqu(";*;-*~", filter.m_nodePatterns->patternString());
+       }
+       void checkOneFile(const char* node, const char* parent,
+           const ReHashList& hash) {
+               ReByteBuffer path, expected;
+               checkT(hash.get(ReByteBuffer(node), path));
+               expected.set(parent, -1);
+               if (!expected.endsWith(OS_SEPARATOR))
+                       expected.append(OS_SEPARATOR);
+               if (!path.endsWith(expected.str(), -1))
+                       checkT(false);
+       }
+       void testBasic() {
+               rebuildTree();
+               ReTraverser traverser(m_base.str());
+               RePatternList patterns;
+               // exclude */cache/*
+               ReByteBuffer buffer(";*;-cache");
+               patterns.set(buffer.str());
+               traverser.setDirPattern(&patterns);
+               int level = 0;
+               ReDirStatus_t* entry;
+               ReHashList hash;
+               while ((entry = traverser.rawNextFile(level)) != NULL) {
+                       hash.put(ReByteBuffer(entry->node(), -1), entry->m_path);
+                       logF(false, "%d: %-12s %2d %s", level, entry->node(),
+                           int(entry->fileSize()), entry->m_path.str());
+               }
+               checkOneFile("x1.txt", "dir1_2_1", hash);
+               checkOneFile("x2.txt", "dir1_2_1", hash);
+               checkOneFile("dir1_2_1", "dir1_2", hash);
+               checkOneFile("dir1_1", "dir1", hash);
+               checkOneFile("dir1_2", "dir1", hash);
+               checkF(hash.get("cache.txt", buffer));
+       }
+       void testDirStatistic() {
+               rebuildTree();
+               ReDirStatistic stat(m_logger);
+               const ReStringList& list = stat.calculate(m_base.str(), 1);
+               ReByteBuffer buffer;
+               ReByteBuffer expected;
+               log(false, list.join("\n", buffer).str());
+               checkEqu(3u, list.count());
+               // "1   t:\temp\winfried\2\retestunit\dir1\n"
+               buffer.set(list.strOf(0), list.strLengthOf(0));
+               checkT(buffer.startsWith("1\t"));
+               expected.set(m_base.str(), m_base.length()).append("dir1", -1).append(
+               OS_SEPARATOR);
+               // .append(OS_SEPARATOR, -1)
+               checkT(buffer.endsWith(expected.str()));
+
+               buffer.setLength(0);
+               const ReStringList& list2 = stat.calculate(m_base.str(), 1,
+                   formatWithSizeFilesAndDirs);
+               log(false, list2.join("\n", buffer).str());
+
+               buffer.set(list.strOf(0), list.strLengthOf(0));
+               checkT(buffer.startsWith("      0.000074 MB       3       4\t"));
+               expected.set(m_base.str(), m_base.length()).append("dir1", -1).append(
+               OS_SEPARATOR);
+               checkT(buffer.endsWith(expected.str()));
+
+               buffer.set(list.strOf(1), list.strLengthOf(1));
+               checkT(buffer.startsWith("      0.000008 MB       1       0\t"));
+               expected.set(m_base.str(), m_base.length()).append("dir2", -1).append(
+               OS_SEPARATOR);
+               checkT(buffer.endsWith(expected.str()));
+
+               buffer.set(list.strOf(2), list.strLengthOf(2));
+               checkT(buffer.startsWith("      0.000087 MB       5       6\t"));
+               expected.set(m_base.str(), m_base.length());
+       }
+       /**
+        * Tests a file with a given context.
+        *
+        * @param filename      name of the file to test
+        * @param content       expected content: each line must contain a '*',
+        *                                      separating the start of line and the end of line
+        */
+       void checkFile(ReByteBuffer& filename, const char* content) {
+               ReStringList current;
+               current.readFromFile(filename.str());
+               ReStringList expected;
+               expected.split(content, '\n');
+               checkEqu(expected.count(), current.count());
+               ReByteBuffer line;
+               ReStringList cols;
+               for (size_t ix = 0; ix < current.count(); ix++) {
+                       line.setLength(0).append(current.strOf(ix), -1);
+#if defined __WIN32__
+                       line.replaceAll("/", 1, OS_SEPARATOR, 1);
+#endif
+                       cols.split(expected.strOf(ix), '*');
+                       const char* col1 = cols.strOf(0);
+                       if (!line.startsWith(col1))
+                               checkEqu(col1, line);
+                       const char* col2 = cols.strOf(1);
+                       if (!line.endsWith(col2)) {
+                               checkEqu(col2, line);
+                       }
+               }
+       }
+       void testTouch() {
+               rebuildTree();
+               ReByteBuffer nameCurrent;
+               buildFilename("current", nameCurrent);
+               ReByteBuffer optOutput("--output-file=");
+               optOutput.append(nameCurrent);
+               ReDirTools tools;
+               const char* argv[] = { "dt", "touch", "-P;*;-cache", "-tr", "-p;*.txt",
+                   "-m2015.03.28/10:21:31", optOutput.str(), m_base.str(), NULL };
+               tools.main(8, (char**) argv);
+
+               checkFile(nameCurrent,
+                   "2015.03.28 10:21:31 *dirtool/1.txt\n"
+                           "2015.03.28 10:21:31 *dirtool/dir1/dir1_2/dir1_2_1/x1.txt\n"
+                           "2015.03.28 10:21:31 *dirtool/dir1/dir1_2/dir1_2_1/x2.txt\n"
+                           "=== filtered:        3 file(s)     0.000059 MByte       0 dirs(s) */sec\n"
+                           "===    total:        4 file(s)     0.000067 MByte       6 dirs(s) * sec");
+
+               const char* argv2[] = { "dt", "list", "-P;*;-cache", "-tr", "-p;*.txt",
+                   "-h", optOutput.str(), m_base.str(), NULL };
+               tools.main(8, (char**) argv2);
+
+               checkFile(nameCurrent,
+                   "      0.000005 2015.03.28 10:21:31 *dirtool/1.txt\n"
+                           "      0.000027 2015.03.28 10:21:31 *dirtool/dir1/dir1_2/dir1_2_1/x1.txt\n"
+                           "      0.000027 2015.03.28 10:21:31 *dirtool/dir1/dir1_2/dir1_2_1/x2.txt\n"
+                           "=== filtered:        3 file(s)     0.000059 MByte       0 dirs(s) */sec\n"
+                           "===    total:        4 file(s)     0.000067 MByte       6 dirs(s) * sec");
+
+       }
+       void testBatch() {
+               rebuildTree();
+               ReByteBuffer nameCurrent;
+               buildFilename("current", nameCurrent);
+               ReByteBuffer optOutput("--output-file=");
+               optOutput.append(nameCurrent);
+               ReDirTools tools;
+               const char* argv[] = { "dt", "batch", "-P;*;-cache", "-td",
+                   "-cmyscript", optOutput.str(), m_base.str(), NULL };
+               tools.main(7, (char**) argv);
+#if defined __linux__
+               static const char* content =
+                   "#! /bin/s*h\n"
+                           "myscript '*dirtool/dir1'\n"
+                           "myscript '*dirtool/dir2'\n"
+                           "myscript '*dirtool/dir1/dir1_1'\n"
+                           "myscript '*dirtool/dir1/dir1_2'\n"
+                           "myscript '*dirtool/dir1/cache'\n"
+                           "myscript '*dirtool/dir1/dir1_2/dir1_2_1'\n"
+                           "# === filtered:        0 file(s)     0.000000 MByte       6 dirs(s) */sec\n"
+                           "# ===    total:        4 file(s)     0.000067 MByte       6 dirs(s) * sec";
+#elif defined __WIN32__
+               static const char* content = "rem this batch is created by*dirtool\n"
+               "call myscript *dirtool\\dir1\"\n"
+               "call myscript *dirtool\\dir2\"\n"
+               "call myscript *dirtool\\dir1\\cache\"\n"
+               "call myscript *dirtool\\dir1\\dir1_1\"\n"
+               "call myscript *dirtool\\dir1\\dir1_2\"\n"
+               "call myscript *dirtool\\dir1\\dir1_2\\dir1_2_1\"\n"
+               "rem === filtered:        0 file(s)     0.000000 MByte       6 dirs(s) *sec\n"
+               "rem ===    total:        4 file(s)     0.000067 MByte       6 dirs(s) * sec";
+#endif
+               checkFile(nameCurrent, content);
+       }
+       void testToolStatistic() {
+               rebuildTree();
+               ReDirTools tools;
+               const char* argv[] = { "dt", "stat", "-P;*;-cache", m_base.str(), "2" };
+               tools.main(5, (char**) argv);
+       }
+       void testToolSync() {
+               rebuildTree();
+               ReDirTools tools;
+               ReByteBuffer source(m_base);
+               source.append("dir1");
+               ReByteBuffer target(testDir());
+               target.ensureLastChar(OS_SEPARATOR_CHAR);
+               target.append("synctest", -1);
+               _mkdir(target.str(), ALLPERMS);
+               const char* argv[] = { "dt", "sync", "-P;*;-cache", "-p;*.txt", source
+                   .str(), target.str() };
+               tools.main(6, (char**) argv);
+       }
+};
+extern void testReDirTools(void);
+
+void testReDirTools(void) {
+       TestReDirTools unit;
+}
index cf86066e6a1be0fa4c9bfcbd5352a3eadc5c2fc7..677a3f8604d909f4040e24edb83c2ffa5720b737 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * cuReDirectory.cpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 #include "base/rebase.hpp"
index cb67b8a9ff91011043fbab8c58d464f2e8f67d64..9909d2201e25215bf2c4e6bf78261f2974adfa2f 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * cuReException.cpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 #include "base/rebase.hpp"
index 69ed6b27436376873c12e29066b5d29accb809ac..5c2d47e498fd2fa127560ad6ea51e825062e7f5f 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * cuReHashList.cpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 #include "base/rebase.hpp"
index ceaaf589e64ad19d1110f1b3dd041c06fac7c27b..84bf6bb7efc164b62b2e03238a416418c4192e24 100644 (file)
@@ -1,9 +1,11 @@
 /*
- * cuReReI18N.cpp
- *
- * License: Public domain
+ * cuReI18N.cpp
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 #include "base/rebase.hpp"
index b4e19f0ddfdfb6a95cd3845d6d2eedb0354400bd..11df9925fd80a585ebd602b2b512a6c23aa19b80 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * cuReLogger.cpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 #include "base/rebase.hpp"
index a80828f1a7885e4e28948ceb15374cb280b79bc0..db02725a8190e00ed5a7a6dbaab0ba237ece6223 100644 (file)
@@ -1,9 +1,11 @@
 /*
- * cuReMatcher.cpp
- *
- * License: Public domain
+ * cuReMD5.cpp
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
index 699643dd594ce75ef6a0a5d1722119a3ecd67e5f..e021e6db8697e0be79149934b2b7c285faae6a14 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * cuReMatcher.cpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 /*
index e6bd316277b216c67e25395bb2870e7438502aa1..d92946da684fbc5423494dc4a38cae63904fa25a 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * cuReProgramArgs.cpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 #include "base/rebase.hpp"
index 41676ac25bcbde02d01650ee5d1a221398ce65fa..57eee08dcc7f243d7ff960b4d333d73ade83c157 100644 (file)
@@ -1,9 +1,11 @@
 /*
- * cuReMatcher.cpp
- *
- * License: Public domain
+ * cuReRPD64.cpp
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
index cc91dd231518691b6cf9cfadbb717622b35385af..4d18971fe0aead49346fc8d8e534a92d77d2cfa7 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * cuReRandomizer.cpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 #include "base/rebase.hpp"
index 1c0b17408249525f12a454fb83c91faa01e48272..52e2824b75f3689059f60bd313cdeae36fc4e059 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * cuReSeqArray.cpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 #include "base/rebase.hpp"
index af70cb98d16303c58ca113b97b2b7edc1e65f01c..f4f5f90eb93fd9680c9323ddff5ce0bae0f5c949 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * cuReSerializable.cpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 #include "base/rebase.hpp"
@@ -33,7 +35,7 @@ public:
                    m_string64k(string2),
                    m_string4t(string3) {
        }
-       virtual void deserialize(uint8_t*& sequence, size_t& length) {
+       virtual void deserialize(const uint8_t*& sequence, size_t& length) {
                int id;
                unpackInt24(sequence, length, id);
                if (id != m_serialId)
@@ -97,13 +99,15 @@ private:
                ReByteBuffer buffer;
                ExampleClass example(250, 64000, 12345678, true, 0x12345678abcdll,
                    "king", "lives", "!");
-               checkEqu("id: 1192961 i8:fa i16:fa00 i32:bc614e b:t i64:12345678abcd s255:king s64k:lives s4t:!",
+               checkEqu(
+                   "id: 1192961 i8:fa i16:fa00 i32:bc614e b:t i64:12345678abcd s255:king s64k:lives s4t:!",
                    example.toString(buffer));
                ReByteBuffer serialBuffer;
                example.serialize(serialBuffer);
                ExampleClass example2;
                example2.deserializeBuffer(serialBuffer);
-               checkEqu("id: 1192961 i8:fa i16:fa00 i32:bc614e b:t i64:12345678abcd s255:king s64k:lives s4t:!",
+               checkEqu(
+                   "id: 1192961 i8:fa i16:fa00 i32:bc614e b:t i64:12345678abcd s255:king s64k:lives s4t:!",
                    example2.toString(buffer));
        }
 };
index bf86bb00609542c675efc44fc95b2995a3cc5b07..bec68c40cb9502fecc7780e6a86f4faeb8887a9f 100644 (file)
@@ -1,9 +1,11 @@
 /*
- * cuReReStringList.cpp
- *
- * License: Public domain
+ * cuReStringList.cpp
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 #include "base/rebase.hpp"
index 15cf8eb6b96d3ec8fc66d6c86ceac3318853c2ab..05227db648b54913fa2b2018e453a88009fa58dd 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * cuReStringUtils.cpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 #include "base/rebase.hpp"
index dc9aa22ed9090fb7665165adccf16137abd252ed..162cb1dcbb3d7cc57c139fb302c62a588be4d67a 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * cuReTCP.cpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
index b510a5aa4314affe908513b3a8d3adcaffeb9338..35960352f176a784da7a39da001af0f0c249f6cd 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * cuReTest.cpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 #include "base/rebase.hpp"
index 74e8c14f0566f1c18724ca808b8450cf49ca8940..90cc05c3ed31b6134d93220817e3a4e585460f85 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * cuReTraverser.cpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
@@ -64,11 +66,16 @@ private:
                makeFile("dir1/cache/cache.txt");
        }
        void run() {
+               testFilter();
                initTree();
 
                testBasic();
                testList();
        }
+       void testFilter() {
+               ReDirEntryFilter filter;
+
+       }
        void testList() {
                const char* argv[] = { "list", m_base.str(), NULL };
                ReDirList(m_logger).run(2, argv);
index 61f32cd1957822a61ce5f1e68e938baa8875508e..283e1acc29ce80cb39ade892c76c39d1f2b332d4 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * cuReVarArgs.cpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 #include "base/rebase.hpp"
index f194bf5f17fb6d8e75b19d8ad3d09390d6a3601f..d2e9768de44be9153d3822eee7c31e022497a452 100644 (file)
@@ -1,9 +1,11 @@
 /*
- * cuReConfig.cpp
- *
- * License: Public domain
+ * cuReconfig.cpp
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 #include "base/rebase.hpp"
index 4e566a2286263c77cde293ea37f353c655de7b73..342b8232dcb3ab3c51afce28ea39b143d91aa19b 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * testall.cpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 #include "base/rebase.hpp"
index 6b8c9c3ef5093c57fd2dda49fb980d305115e6f0..43fb5f5ef4b9aab693d19072d120612b4bf4e836 100644 (file)
@@ -1,3 +1,13 @@
+/*
+ * dirtool.cpp
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
+ * Do what you want.
+ * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
+ * The latest sources: https://github.com/republib
+ */
 // dirtool.cpp : Definiert den Einstiegspunkt für die Konsolenanwendung.\r
 //\r
 #include "base/rebase.hpp"\r
index 5149a77fdd1eef32977112d6cb41ff4800030175..fe33a497b2c4b246fe163ec5911979a836986e93 100644 (file)
-/*\r
- * ReMD5.cpp\r
- *\r
- *  Created on: 30.01.2015\r
- *\r
- */\r
-\r
-#include "base/rebase.hpp"\r
-#include "math/remath.hpp"\r
-\r
-const int ReMD5::m_s[RE_DIGEST_CHUNK_SIZE] = { 7, 12, 17, 22, 7, 12, 17, 22, 7,\r
-    12, 17, 22, 7, 12, 17, 22, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9,\r
-    14, 20, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 6, 10,\r
-    15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21 };\r
-//  for x in [1..64] : int(2**32 * sin(x))\r
-const uint32_t ReMD5::m_K[RE_DIGEST_CHUNK_SIZE] = { 0xd76aa478, 0xe8c7b756,\r
-    0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,\r
-    0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193,\r
-    0xa679438e, 0x49b40821, 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,\r
-    0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, 0x21e1cde6, 0xc33707d6,\r
-    0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,\r
-    0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9,\r
-    0xf6bb4b60, 0xbebfbc70, 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,\r
-    0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, 0xf4292244, 0x432aff97,\r
-    0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,\r
-    0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235,\r
-    0x2ad7d2bb, 0xeb86d391 };\r
-\r
-/**\r
- * Constructor.\r
- *\r
- * @param digest               the buffer for the binary checksum\r
- * @param digestSize   the length of <code>digest</code>\r
- * @param waiting              a buffer for a chunk. Must have space for at least 2*chunksize bytes\r
- *                                             NULL: an intrinsic buffer will be used\r
- * @param chunkSize            the length of one full input block\r
- */\r
-ReDigest::ReDigest(uint8_t* digest, size_t digestSize, uint8_t* waiting,\r
-    size_t chunkSize) :\r
-           m_digest(digest),\r
-           m_digestSize(digestSize),\r
-           // m_waitingBuffer[RE_DIGEST_CHUNK_SIZE];\r
-           m_waiting(waiting != NULL ? waiting : m_waitingBuffer),\r
-           m_lengthWaiting(0),\r
-           m_chunkSize(chunkSize),\r
-           m_length(0),\r
-           m_finalized(false),\r
-           m_salt(0) {\r
-}\r
-/**\r
- * Destructor.\r
- */\r
-ReDigest::~ReDigest() {\r
-}\r
-/**\r
- * Returns the binary digest value.\r
- *\r
- * @return     the binary digest (16 byte array)\r
- */\r
-const uint8_t* ReDigest::digest() {\r
-       if (!m_finalized) {\r
-               finalize();\r
-       }\r
-       return m_digest;\r
-}\r
-\r
-/**\r
- * Returns the binary digest value.\r
- *\r
- * @return     the binary digest (16 byte array)\r
- */\r
-const ReByteBuffer& ReDigest::hexDigest() {\r
-       if (m_hexDigest.length() == 0) {\r
-               digest();\r
-               for (size_t ix = 0; ix < m_digestSize; ix++) {\r
-                       m_hexDigest.appendInt(m_digest[ix], "%02x");\r
-               }\r
-       }\r
-       return m_hexDigest;\r
-}\r
-\r
-/**\r
- * Processes a 64 byte block.\r
- *\r
- * @param block                        a block which should be added to the digest\r
- * @param blockLength  the length of <code>block</code>\r
- */\r
-void ReDigest::update(const uint8_t* block, int blockLength) {\r
-       if (blockLength == -1)\r
-               blockLength = strlen((const char*) block);\r
-       // process the "waiting" input (incomplete chunk):\r
-       m_length += blockLength;\r
-       if (m_lengthWaiting > 0) {\r
-               int rest = m_chunkSize - m_lengthWaiting;\r
-               if (rest > blockLength)\r
-                       rest = blockLength;\r
-               memcpy(m_waiting + m_lengthWaiting, block, rest);\r
-               blockLength -= rest;\r
-               block += rest;\r
-               m_lengthWaiting += rest;\r
-               // Is the chunk complete?\r
-               if (m_lengthWaiting == m_chunkSize) {\r
-                       processChunk(m_waiting);\r
-                       m_lengthWaiting = 0;\r
-               }\r
-       }\r
-       // process full 512 bit chunks (64 byte blocks):\r
-       for (int ix = blockLength / m_chunkSize; ix > 0; ix--) {\r
-               processChunk(block);\r
-               block += m_chunkSize;\r
-       }\r
-       blockLength %= m_chunkSize;\r
-       if (blockLength != 0) {\r
-               assert(m_lengthWaiting == 0);\r
-               memcpy(m_waiting, block, blockLength);\r
-               m_lengthWaiting = blockLength;\r
-       }\r
-}\r
-\r
-/**\r
- * Sets the salt of the checksum algorithm.\r
- *\r
- * Important: set the salt before you make the first <code>update()</code>!\r
- *\r
- * The salt makes that the checksum of the same input is (extremly) different\r
- * to another salt.\r
- *\r
- * @param salt the salt to set\r
- */\r
-void ReDigest::setSalt(uint64_t salt) {\r
-       m_salt = salt;\r
-}\r
-/**\r
- * Constructor.\r
- */\r
-ReMD5::ReMD5() :\r
-           ReDigest(m_digest, sizeof m_digest),\r
-           m_a0(0x67452301),\r
-           m_b0(0xefcdab89),\r
-           m_c0(0x98badcfe),\r
-           m_d0(0x10325476) {\r
-}\r
-\r
-/**\r
- * Destructor.\r
- */\r
-ReMD5::~ReMD5() {\r
-}\r
-\r
-/**\r
- * Finalizes the digest.\r
- *\r
- * Handles the rest block (< 64 byte) and append a special tail: a "1" bit\r
- * and the length of the total input length (in bits).\r
- *\r
- * @param block                        the rest input (incomplete chunk)\r
- * @param blockLength  the length of the block: 0..63\r
- */\r
-void ReMD5::finalize() {\r
-       uint8_t* block = m_waiting;\r
-       int blockLength = m_lengthWaiting;\r
-       // append "1" bit to message\r
-       // Notice: the input bytes are considered as bits strings,\r
-       // where the first bit is the most significant bit of the byte.\r
-       block[blockLength++] = 0x80;\r
-       //Pre-processing: padding with zeros\r
-       //append "0" bit until message length in bits â‰¡ 448 (mod 512)\r
-       // fill the rest of the chunk with '\0'.\r
-       // the last 8 bytes is set to the length in bits (length in bytes * 8)\r
-       int restLength = (m_length + 1) % RE_DIGEST_CHUNK_SIZE;\r
-       // 0 -> 56, 1 -> 55, 2 -> 54, ... 55 -> 1, 56 -> 0,\r
-       // 57 -> 63, 58 -> 62, ... 63 -> 57\r
-       int zeros = restLength <= 56 ? 56 - restLength : 120 - restLength;\r
-       memset(block + blockLength, 0, zeros);\r
-       blockLength += zeros;\r
-       //append original length in bits mod (2 pow 64) to message\r
-       uint64_t lengthBits = 8LL * m_length + m_salt;\r
-#if defined __LITTLE_ENDIAN__\r
-       memcpy(block + blockLength, &lengthBits, 8);\r
-       blockLength += 8;\r
-#else\r
-       block[blockLength++] = lengthBits;\r
-       lengthBits >>= 8;\r
-       block[blockLength++] = lengthBits;\r
-       lengthBits >>= 8;\r
-       block[blockLength++] = lengthBits;\r
-       lengthBits >>= 8;\r
-       block[blockLength++] = lengthBits;\r
-       lengthBits >>= 8;\r
-       block[blockLength++] = lengthBits;\r
-       lengthBits >>= 8;\r
-       block[blockLength++] = lengthBits;\r
-       lengthBits >>= 8;\r
-       block[blockLength++] = lengthBits;\r
-       lengthBits >>= 8;\r
-       block[blockLength++] = lengthBits;\r
-#endif\r
-       processChunk(block);\r
-       if (blockLength > RE_DIGEST_CHUNK_SIZE)\r
-               processChunk(block + RE_DIGEST_CHUNK_SIZE);\r
-#if defined __LITTLE_ENDIAN__\r
-       memcpy(m_digest, &m_a0, 4);\r
-       memcpy(m_digest + 4, &m_b0, 4);\r
-       memcpy(m_digest + 8, &m_c0, 4);\r
-       memcpy(m_digest + 12, &m_d0, 4);\r
-#else\r
+/*
+ * ReMD5.cpp
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
+ * Do what you want.
+ * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
+ * The latest sources: https://github.com/republib
+ */
+
+#include "base/rebase.hpp"
+#include "math/remath.hpp"
+
+const int ReMD5::m_s[RE_DIGEST_CHUNK_SIZE] = { 7, 12, 17, 22, 7, 12, 17, 22, 7,
+    12, 17, 22, 7, 12, 17, 22, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9,
+    14, 20, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 6, 10,
+    15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21 };
+//  for x in [1..64] : int(2**32 * sin(x))
+const uint32_t ReMD5::m_K[RE_DIGEST_CHUNK_SIZE] = { 0xd76aa478, 0xe8c7b756,
+    0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
+    0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193,
+    0xa679438e, 0x49b40821, 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
+    0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, 0x21e1cde6, 0xc33707d6,
+    0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
+    0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9,
+    0xf6bb4b60, 0xbebfbc70, 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
+    0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, 0xf4292244, 0x432aff97,
+    0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
+    0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235,
+    0x2ad7d2bb, 0xeb86d391 };
+
+/**
+ * Constructor.
+ *
+ * @param digest               the buffer for the binary checksum
+ * @param digestSize   the length of <code>digest</code>
+ * @param waiting              a buffer for a chunk. Must have space for at least 2*chunksize bytes
+ *                                             NULL: an intrinsic buffer will be used
+ * @param chunkSize            the length of one full input block
+ */
+ReDigest::ReDigest(uint8_t* digest, size_t digestSize, uint8_t* waiting,
+    size_t chunkSize) :
+           m_digest(digest),
+           m_digestSize(digestSize),
+           // m_waitingBuffer[RE_DIGEST_CHUNK_SIZE];
+           m_waiting(waiting != NULL ? waiting : m_waitingBuffer),
+           m_lengthWaiting(0),
+           m_chunkSize(chunkSize),
+           m_length(0),
+           m_finalized(false),
+           m_salt(0) {
+}
+/**
+ * Destructor.
+ */
+ReDigest::~ReDigest() {
+}
+/**
+ * Returns the binary digest value.
+ *
+ * @return     the binary digest (16 byte array)
+ */
+const uint8_t* ReDigest::digest() {
+       if (!m_finalized) {
+               finalize();
+       }
+       return m_digest;
+}
+
+/**
+ * Returns the binary digest value.
+ *
+ * @return     the binary digest (16 byte array)
+ */
+const ReByteBuffer& ReDigest::hexDigest() {
+       if (m_hexDigest.length() == 0) {
+               digest();
+               for (size_t ix = 0; ix < m_digestSize; ix++) {
+                       m_hexDigest.appendInt(m_digest[ix], "%02x");
+               }
+       }
+       return m_hexDigest;
+}
+
+/**
+ * Processes a 64 byte block.
+ *
+ * @param block                        a block which should be added to the digest
+ * @param blockLength  the length of <code>block</code>
+ */
+void ReDigest::update(const uint8_t* block, int blockLength) {
+       if (blockLength == -1)
+               blockLength = strlen((const char*) block);
+       // process the "waiting" input (incomplete chunk):
+       m_length += blockLength;
+       if (m_lengthWaiting > 0) {
+               int rest = m_chunkSize - m_lengthWaiting;
+               if (rest > blockLength)
+                       rest = blockLength;
+               memcpy(m_waiting + m_lengthWaiting, block, rest);
+               blockLength -= rest;
+               block += rest;
+               m_lengthWaiting += rest;
+               // Is the chunk complete?
+               if (m_lengthWaiting == m_chunkSize) {
+                       processChunk(m_waiting);
+                       m_lengthWaiting = 0;
+               }
+       }
+       // process full 512 bit chunks (64 byte blocks):
+       for (int ix = blockLength / m_chunkSize; ix > 0; ix--) {
+               processChunk(block);
+               block += m_chunkSize;
+       }
+       blockLength %= m_chunkSize;
+       if (blockLength != 0) {
+               assert(m_lengthWaiting == 0);
+               memcpy(m_waiting, block, blockLength);
+               m_lengthWaiting = blockLength;
+       }
+}
+
+/**
+ * Sets the salt of the checksum algorithm.
+ *
+ * Important: set the salt before you make the first <code>update()</code>!
+ *
+ * The salt makes that the checksum of the same input is (extremly) different
+ * to another salt.
+ *
+ * @param salt the salt to set
+ */
+void ReDigest::setSalt(uint64_t salt) {
+       m_salt = salt;
+}
+/**
+ * Constructor.
+ */
+ReMD5::ReMD5() :
+           ReDigest(m_digest, sizeof m_digest),
+           m_a0(0x67452301),
+           m_b0(0xefcdab89),
+           m_c0(0x98badcfe),
+           m_d0(0x10325476) {
+}
+
+/**
+ * Destructor.
+ */
+ReMD5::~ReMD5() {
+}
+
+/**
+ * Finalizes the digest.
+ *
+ * Handles the rest block (< 64 byte) and append a special tail: a "1" bit
+ * and the length of the total input length (in bits).
+ *
+ * @param block                        the rest input (incomplete chunk)
+ * @param blockLength  the length of the block: 0..63
+ */
+void ReMD5::finalize() {
+       uint8_t* block = m_waiting;
+       int blockLength = m_lengthWaiting;
+       // append "1" bit to message
+       // Notice: the input bytes are considered as bits strings,
+       // where the first bit is the most significant bit of the byte.
+       block[blockLength++] = 0x80;
+       //Pre-processing: padding with zeros
+       //append "0" bit until message length in bits â‰¡ 448 (mod 512)
+       // fill the rest of the chunk with '\0'.
+       // the last 8 bytes is set to the length in bits (length in bytes * 8)
+       int restLength = (m_length + 1) % RE_DIGEST_CHUNK_SIZE;
+       // 0 -> 56, 1 -> 55, 2 -> 54, ... 55 -> 1, 56 -> 0,
+       // 57 -> 63, 58 -> 62, ... 63 -> 57
+       int zeros = restLength <= 56 ? 56 - restLength : 120 - restLength;
+       memset(block + blockLength, 0, zeros);
+       blockLength += zeros;
+       //append original length in bits mod (2 pow 64) to message
+       uint64_t lengthBits = 8LL * m_length + m_salt;
+#if defined __LITTLE_ENDIAN__
+       memcpy(block + blockLength, &lengthBits, 8);
+       blockLength += 8;
+#else
+       block[blockLength++] = lengthBits;
+       lengthBits >>= 8;
+       block[blockLength++] = lengthBits;
+       lengthBits >>= 8;
+       block[blockLength++] = lengthBits;
+       lengthBits >>= 8;
+       block[blockLength++] = lengthBits;
+       lengthBits >>= 8;
+       block[blockLength++] = lengthBits;
+       lengthBits >>= 8;
+       block[blockLength++] = lengthBits;
+       lengthBits >>= 8;
+       block[blockLength++] = lengthBits;
+       lengthBits >>= 8;
+       block[blockLength++] = lengthBits;
+#endif
+       processChunk(block);
+       if (blockLength > RE_DIGEST_CHUNK_SIZE)
+               processChunk(block + RE_DIGEST_CHUNK_SIZE);
+#if defined __LITTLE_ENDIAN__
+       memcpy(m_digest, &m_a0, 4);
+       memcpy(m_digest + 4, &m_b0, 4);
+       memcpy(m_digest + 8, &m_c0, 4);
+       memcpy(m_digest + 12, &m_d0, 4);
+#else
 #define oneWord(word, ix) m_digest[ix] = word; word >>= 8; \\r
        m_digest[ix + 1] = word; word >>= 8; m_digest[ix + 2] = word; word >>= 8; \\r
-       m_digest[ix + 3] = word\r
-       oneWord(m_a0, 0);\r
-       oneWord(m_b0, 4);\r
-       oneWord(m_c0, 8);\r
-       oneWord(m_d0, 12);\r
-#endif\r
-}\r
-\r
-/**\r
- * Processes a 512 bit block ("chunk").\r
- *\r
- * This method is a direct programming of the algorithm described in wikipedia.\r
- */\r
-void ReMD5::processChunk2(const uint8_t block[RE_DIGEST_CHUNK_SIZE]) {\r
-       uint32_t M[16];\r
-       //      break chunk into sixteen 32-bit words M[j], 0 â‰¤ j â‰¤ 15\r
-       for (int ix = 0; ix < 16; ix++) {\r
-               uint32_t x = block[3];\r
-               for (int jj = 2; jj >= 0; jj--) {\r
-                       x = (x << 8) + block[jj];\r
-               }\r
-               M[ix] = x;\r
-               block += 4;\r
-       }\r
-       //Initialize hash value for this chunk:\r
-       uint32_t A = m_a0;\r
-       uint32_t B = m_b0;\r
-       uint32_t C = m_c0;\r
-       uint32_t D = m_d0;\r
-       //Main loop:\r
-\r
-       int F, g;\r
-//#define TRACE_MD5\r
-#if defined TRACE_MD5\r
-       printf("neu: (%s)\n", block);\r
-#endif\r
-       for (int i = 0; i < RE_DIGEST_CHUNK_SIZE; i++) {\r
-#if defined TRACE_MD5\r
-               if (i < 8)\r
-               printf("%2d: A: %08x B: %08x C: %08x D%08x\n", i, A, B, C, D);\r
-#endif\r
-               if (i < 16) {\r
-#                      define F1(B, C, D) ((B & C) | (~ B & D))\r
-                       // F := (B and C) or ((not B) and D)\r
-                       F = F1(B, C, D);\r
-                       g = i;\r
-               } else if (i < 32) {\r
-                       // F := (D and B) or (C and (not D))\r
-                       // g := (5×i + 1) mod 16\r
-#                      define F2(B, C, D) ((D & B) | (C & ~ D))\r
-                       F = F2(B, C, D);\r
-                       g = (5 * i + 1) % 16;\r
-               } else if (i < 48) {\r
-                       // F := B xor C xor D\r
-                       // g := (3×i + 5) mod 16\r
-#                      define F3(B, C, D)      ((B ^ C) ^ D)\r
-                       F = F3(B, C, D);\r
-                       g = (3 * i + 5) % 16;\r
-               } else {\r
-                       // F := C xor (B or (not D))\r
-#                      define F4(B, C, D)      (C ^ (B | ~ D))\r
-                       // g := (7×i) mod 16\r
-                       F = F4(B, C, D);\r
-                       g = (7 * i) % 16;\r
-               }\r
-#if defined TRACE_MD5\r
-               if (i < 8)\r
-               printf("    K[%2d]: %08x M[%2d]: %08x shift: %02d\n",\r
-                       i, m_K[i], g, M[g], m_s[i]);\r
-#endif\r
-               uint32_t dTemp = D;\r
-               D = C;\r
-               C = B;\r
-               // B := B + leftrotate((A + F + K[i] + M[g]), s[i])\r
-               uint32_t x = (A + F + m_K[i] + M[g]);\r
-               int shift = m_s[i];\r
-               B += (x << shift) | (x >> (32 - shift));\r
-               A = dTemp;\r
-       }\r
-       //Add this chunk's hash to result so far:\r
-       m_a0 += A;\r
-       m_b0 += B;\r
-       m_c0 += C;\r
-       m_d0 += D;\r
-}\r
-/** ----------------------\r
- */\r
-#if defined OPTIMIZER_WORKS_GREAT\r
-inline void rotate_left_and_add(uint32_t& rc, uint32_t data, int shift, uint32_t term) {\r
-       rc = ((data << shift) | (data >> (32-shift))) + term;\r
-}\r
-//#define TRACE_MD5\r
-#if defined TRACE_MD5\r
-static int s_ix = 0;\r
-#endif\r
-inline void X1(uint32_t &var, uint32_t x, uint32_t y, uint32_t z,\r
-       uint32_t data, uint32_t aConst, uint32_t shift) {\r
-#if defined TRACE_MD5\r
-       printf("%2d: A: %08x B: %08x C: %08x D%08x\n", s_ix++ % 16, var, x, y, z);\r
-       printf("    K[%2d]: %08x M[?]: %08x shift: %02d\n",\r
-               s_ix - 1, aConst, data, shift);\r
-#endif\r
-       rotate_left_and_add(var, var + F1(x, y, z) + data + aConst, shift, x);\r
-}\r
-inline void X2(uint32_t& var, uint32_t x, uint32_t y, uint32_t z,\r
-       uint32_t data, uint32_t aConst, uint32_t shift) {\r
-#if defined TRACE_MD5\r
-       printf("%2d: A: %08x B: %08x C: %08x D%08x\n", s_ix++ % 16, var, x, y, z);\r
-       printf("    K[%2d]: %08x M[?]: %08x shift: %02d\n",\r
-               s_ix - 1, aConst, data, shift);\r
-#endif\r
-       rotate_left_and_add(var, var + F2(x, y, z) + data + aConst, shift, x);\r
-}\r
-\r
-inline void X3(uint32_t& var, uint32_t x, uint32_t y, uint32_t z,\r
-       uint32_t data, uint32_t aConst, uint32_t shift) {\r
-#if defined TRACE_MD5\r
-       printf("%2d: A: %08x B: %08x C: %08x D%08x\n", s_ix++ % 16, var, x, y, z);\r
-       printf("    K[%2d]: %08x M[?]: %08x shift: %02d\n",\r
-               s_ix - 1, aConst, data, shift);\r
-#endif\r
-       rotate_left_and_add(var, var + F3(x, y, z) + data + aConst, shift, x);\r
-}\r
-\r
-inline void X4(uint32_t& var, uint32_t x, uint32_t y, uint32_t z,\r
-       uint32_t data, uint32_t aConst, uint32_t shift) {\r
-#if defined TRACE_MD5\r
-       printf("%2d: A: %08x B: %08x C: %08x D%08x\n", s_ix++ % 16, var, x, y, z);\r
-       printf("    K[%2d]: %08x M[?]: %08x shift: %02d\n",\r
-               s_ix - 1, aConst, data, shift);\r
-#endif\r
-       rotate_left_and_add(var, var + F4(x, y, z) + data + aConst, shift, x);\r
-}\r
-#else\r
+       m_digest[ix + 3] = word
+       oneWord(m_a0, 0);
+       oneWord(m_b0, 4);
+       oneWord(m_c0, 8);
+       oneWord(m_d0, 12);
+#endif
+}
+
+/**
+ * Processes a 512 bit block ("chunk").
+ *
+ * This method is a direct programming of the algorithm described in wikipedia.
+ */
+void ReMD5::processChunk2(const uint8_t block[RE_DIGEST_CHUNK_SIZE]) {
+       uint32_t M[16];
+       //      break chunk into sixteen 32-bit words M[j], 0 â‰¤ j â‰¤ 15
+       for (int ix = 0; ix < 16; ix++) {
+               uint32_t x = block[3];
+               for (int jj = 2; jj >= 0; jj--) {
+                       x = (x << 8) + block[jj];
+               }
+               M[ix] = x;
+               block += 4;
+       }
+       //Initialize hash value for this chunk:
+       uint32_t A = m_a0;
+       uint32_t B = m_b0;
+       uint32_t C = m_c0;
+       uint32_t D = m_d0;
+       //Main loop:
+
+       int F, g;
+//#define TRACE_MD5
+#if defined TRACE_MD5
+       printf("neu: (%s)\n", block);
+#endif
+       for (int i = 0; i < RE_DIGEST_CHUNK_SIZE; i++) {
+#if defined TRACE_MD5
+               if (i < 8)
+               printf("%2d: A: %08x B: %08x C: %08x D%08x\n", i, A, B, C, D);
+#endif
+               if (i < 16) {
+#                      define F1(B, C, D) ((B & C) | (~ B & D))
+                       // F := (B and C) or ((not B) and D)
+                       F = F1(B, C, D);
+                       g = i;
+               } else if (i < 32) {
+                       // F := (D and B) or (C and (not D))
+                       // g := (5×i + 1) mod 16
+#                      define F2(B, C, D) ((D & B) | (C & ~ D))
+                       F = F2(B, C, D);
+                       g = (5 * i + 1) % 16;
+               } else if (i < 48) {
+                       // F := B xor C xor D
+                       // g := (3×i + 5) mod 16
+#                      define F3(B, C, D)      ((B ^ C) ^ D)
+                       F = F3(B, C, D);
+                       g = (3 * i + 5) % 16;
+               } else {
+                       // F := C xor (B or (not D))
+#                      define F4(B, C, D)      (C ^ (B | ~ D))
+                       // g := (7×i) mod 16
+                       F = F4(B, C, D);
+                       g = (7 * i) % 16;
+               }
+#if defined TRACE_MD5
+               if (i < 8)
+               printf("    K[%2d]: %08x M[%2d]: %08x shift: %02d\n",
+                       i, m_K[i], g, M[g], m_s[i]);
+#endif
+               uint32_t dTemp = D;
+               D = C;
+               C = B;
+               // B := B + leftrotate((A + F + K[i] + M[g]), s[i])
+               uint32_t x = (A + F + m_K[i] + M[g]);
+               int shift = m_s[i];
+               B += (x << shift) | (x >> (32 - shift));
+               A = dTemp;
+       }
+       //Add this chunk's hash to result so far:
+       m_a0 += A;
+       m_b0 += B;
+       m_c0 += C;
+       m_d0 += D;
+}
+/** ----------------------
+ */
+#if defined OPTIMIZER_WORKS_GREAT
+inline void rotate_left_and_add(uint32_t& rc, uint32_t data, int shift, uint32_t term) {
+       rc = ((data << shift) | (data >> (32-shift))) + term;
+}
+//#define TRACE_MD5
+#if defined TRACE_MD5
+static int s_ix = 0;
+#endif
+inline void X1(uint32_t &var, uint32_t x, uint32_t y, uint32_t z,
+       uint32_t data, uint32_t aConst, uint32_t shift) {
+#if defined TRACE_MD5
+       printf("%2d: A: %08x B: %08x C: %08x D%08x\n", s_ix++ % 16, var, x, y, z);
+       printf("    K[%2d]: %08x M[?]: %08x shift: %02d\n",
+               s_ix - 1, aConst, data, shift);
+#endif
+       rotate_left_and_add(var, var + F1(x, y, z) + data + aConst, shift, x);
+}
+inline void X2(uint32_t& var, uint32_t x, uint32_t y, uint32_t z,
+       uint32_t data, uint32_t aConst, uint32_t shift) {
+#if defined TRACE_MD5
+       printf("%2d: A: %08x B: %08x C: %08x D%08x\n", s_ix++ % 16, var, x, y, z);
+       printf("    K[%2d]: %08x M[?]: %08x shift: %02d\n",
+               s_ix - 1, aConst, data, shift);
+#endif
+       rotate_left_and_add(var, var + F2(x, y, z) + data + aConst, shift, x);
+}
+
+inline void X3(uint32_t& var, uint32_t x, uint32_t y, uint32_t z,
+       uint32_t data, uint32_t aConst, uint32_t shift) {
+#if defined TRACE_MD5
+       printf("%2d: A: %08x B: %08x C: %08x D%08x\n", s_ix++ % 16, var, x, y, z);
+       printf("    K[%2d]: %08x M[?]: %08x shift: %02d\n",
+               s_ix - 1, aConst, data, shift);
+#endif
+       rotate_left_and_add(var, var + F3(x, y, z) + data + aConst, shift, x);
+}
+
+inline void X4(uint32_t& var, uint32_t x, uint32_t y, uint32_t z,
+       uint32_t data, uint32_t aConst, uint32_t shift) {
+#if defined TRACE_MD5
+       printf("%2d: A: %08x B: %08x C: %08x D%08x\n", s_ix++ % 16, var, x, y, z);
+       printf("    K[%2d]: %08x M[?]: %08x shift: %02d\n",
+               s_ix - 1, aConst, data, shift);
+#endif
+       rotate_left_and_add(var, var + F4(x, y, z) + data + aConst, shift, x);
+}
+#else
 #define rotate_left_and_add(var, data, shift, term) { \\r
        uint32_t val = data; \\r
        var = ((val << shift) | (val >> (32-shift))) + term; \\r
-}\r
+}
 #define X1(var, x, y, z, data, aConst, shift) \\r
-       rotate_left_and_add(var, var + F1(x, y, z) + data + aConst, shift, x)\r
+       rotate_left_and_add(var, var + F1(x, y, z) + data + aConst, shift, x)
 #define X2(var, x, y, z, data, aConst, shift) \\r
-       rotate_left_and_add(var, var + F2(x, y, z) + data + aConst, shift, x)\r
+       rotate_left_and_add(var, var + F2(x, y, z) + data + aConst, shift, x)
 #define X3(var, x, y, z, data, aConst, shift) \\r
-       rotate_left_and_add(var, var + F3(x, y, z) + data + aConst, shift, x)\r
+       rotate_left_and_add(var, var + F3(x, y, z) + data + aConst, shift, x)
 #define X4(var, x, y, z, data, aConst, shift) \\r
-       rotate_left_and_add(var, var + F4(x, y, z) + data + aConst, shift, x)\r
-#endif /* OPTIMIZER_WORKS_GREAT */\r
-\r
-/**\r
- * Processes a 512 bit block ("chunk").\r
- *\r
- * This is a optimized version, derived from the method above.\r
- * We unroll the loop, this brings speed with factor 2.\r
- * <pre>\r
- * B := B + leftrotate((A + F + K[i] + M[g]), s[i])\r
- *     D := C;\r
- *     C := B;\r
- *  B := B + leftrotate((A + F + K[i] + M[g]), s[i])\r
- *  A := D(old)\r
- * (D, C, B, A) = (C, B, B + leftrotate((A + F + K[i] + M[g]), s[i]), D)\r
- * ==> (A, B, C, D) = (D, B + leftrotate((A + F + K[i] + M[g]), s[i]), B, A)\r
- * The unrolled loop:\r
- * i = g = 0;\r
- * (A, B, C, D) = (D, B + leftrotate((A +  F1(B, C, D) + K[0] + M[0]), s[0]), B, A)\r
- * only one var must be calculated, the other 3 are exchanged only.\r
- * i = g = 1;\r
- * (A, B, C, D) = (D, B + leftrotate((A +  F1(B, C, D) + K[1] + M[1]), s[1]), B, A)\r
- * i = g = 2;\r
- * (A, B, C, D) = (D, B + leftrotate((A +  F1(B, C, D) + K[2] + M[2]), s[2]), B, A)\r
- * i = g = 3;\r
- * (A, B, C, D) = (D, B + leftrotate((A +  F1(B, C, D) + K[3] + M[3]), s[3]), B, A)\r
- * in each of the 4 statements another variable (of A, B, C and D) will be calculated\r
- * so we do not exchange in each step, we calculate in the end position\r
- * we define a function to do this:\r
- * void X1(uint32_t &var, uint32_t x, uint32_t y, uint32_t z, uint32_t data, uint32_t shift, uint32_t aConst){\r
- *      var = rotate_left(var+ F1(x, y, z) + data + aConst, shift) + x;\r
- * }\r
- * Note: the input parameter of X1 must respect the exchange:\r
- * A -> D -> C -> B ...\r
- * X1(A, B, C, D,  M[0], K[0], s[0]);\r
- * X1(D, A, B, C,  M[1], K[1], s[1]);\r
- * X1(C, D, A, B,  M[2], K[2], s[2]);\r
- * X1(B, C, D, A,  M[3], K[3], s[3]);\r
- * ...\r
- * </pre>\r
- */\r
-void ReMD5::processChunk(const uint8_t block[RE_DIGEST_CHUNK_SIZE]) {\r
-       uint32_t M[16];\r
-       //      break chunk into sixteen 32-bit words M[j], 0 â‰¤ j â‰¤ 15\r
-#ifdef __LITTLE_ENDIAN__\r
-       for (int ix = 0; ix < 16; ix++) {\r
-               //memcpy(&M[ix], block + ix * 4, 4);\r
-               M[ix] = *(uint32_t*) (block + ix * 4);\r
-       }\r
-#elif defined __BIG_ENDIAN__\r
-       for (int ix = 0; ix < 16; ix++) {\r
-               uint32_t x = block[3];\r
-               for (int jj = 2; jj >= 0; jj--) {\r
-                       x = (x << 8) + block[jj];\r
-               }\r
-               M[ix] = x;\r
-               block += 4;\r
-       }\r
-#else\r
-#      error "missing __LITTLE_ENDIAN__ or __BIG_ENDIAN__"\r
-#endif\r
-       //Initialize hash value for this chunk:\r
-       uint32_t A = m_a0;\r
-       uint32_t B = m_b0;\r
-       uint32_t C = m_c0;\r
-       uint32_t D = m_d0;\r
-#if defined NeverAndNeverAndNeverAgain\r
-       // Derivation of the optimization:\r
-\r
-#endif\r
-       /* Round 1 */\r
-       X1(A, B, C, D, M[0], 0xd76aa478, 7);\r
-       X1(D, A, B, C, M[1], 0xe8c7b756, 12);\r
-       X1(C, D, A, B, M[2], 0x242070db, 17);\r
-       X1(B, C, D, A, M[3], 0xc1bdceee, 22);\r
-       X1(A, B, C, D, M[4], 0xf57c0faf, 7);\r
-       X1(D, A, B, C, M[5], 0x4787c62a, 12);\r
-       X1(C, D, A, B, M[6], 0xa8304613, 17);\r
-       X1(B, C, D, A, M[7], 0xfd469501, 22);\r
-       X1(A, B, C, D, M[8], 0x698098d8, 7);\r
-       X1(D, A, B, C, M[9], 0x8b44f7af, 12);\r
-       X1(C, D, A, B, M[10], 0xffff5bb1, 17);\r
-       X1(B, C, D, A, M[11], 0x895cd7be, 22);\r
-       X1(A, B, C, D, M[12], 0x6b901122, 7);\r
-       X1(D, A, B, C, M[13], 0xfd987193, 12);\r
-       X1(C, D, A, B, M[14], 0xa679438e, 17);\r
-       X1(B, C, D, A, M[15], 0x49b40821, 22);\r
-\r
-       /* Round 2 */\r
-       X2(A, B, C, D, M[1], 0xf61e2562, 5);\r
-       X2(D, A, B, C, M[6], 0xc040b340, 9);\r
-       X2(C, D, A, B, M[11], 0x265e5a51, 14);\r
-       X2(B, C, D, A, M[0], 0xe9b6c7aa, 20);\r
-       X2(A, B, C, D, M[5], 0xd62f105d, 5);\r
-       X2(D, A, B, C, M[10], 0x02441453, 9);\r
-       X2(C, D, A, B, M[15], 0xd8a1e681, 14);\r
-       X2(B, C, D, A, M[4], 0xe7d3fbc8, 20);\r
-       X2(A, B, C, D, M[9], 0x21e1cde6, 5);\r
-       X2(D, A, B, C, M[14], 0xc33707d6, 9);\r
-       X2(C, D, A, B, M[3], 0xf4d50d87, 14);\r
-       X2(B, C, D, A, M[8], 0x455a14ed, 20);\r
-       X2(A, B, C, D, M[13], 0xa9e3e905, 5);\r
-       X2(D, A, B, C, M[2], 0xfcefa3f8, 9);\r
-       X2(C, D, A, B, M[7], 0x676f02d9, 14);\r
-       X2(B, C, D, A, M[12], 0x8d2a4c8a, 20);\r
-\r
-       /* Round 3 */\r
-       X3(A, B, C, D, M[5], 0xfffa3942, 4);\r
-       X3(D, A, B, C, M[8], 0x8771f681, 11);\r
-       X3(C, D, A, B, M[11], 0x6d9d6122, 16);\r
-       X3(B, C, D, A, M[14], 0xfde5380c, 23);\r
-       X3(A, B, C, D, M[1], 0xa4beea44, 4);\r
-       X3(D, A, B, C, M[4], 0x4bdecfa9, 11);\r
-       X3(C, D, A, B, M[7], 0xf6bb4b60, 16);\r
-       X3(B, C, D, A, M[10], 0xbebfbc70, 23);\r
-       X3(A, B, C, D, M[13], 0x289b7ec6, 4);\r
-       X3(D, A, B, C, M[0], 0xeaa127fa, 11);\r
-       X3(C, D, A, B, M[3], 0xd4ef3085, 16);\r
-       X3(B, C, D, A, M[6], 0x04881d05, 23);\r
-       X3(A, B, C, D, M[9], 0xd9d4d039, 4);\r
-       X3(D, A, B, C, M[12], 0xe6db99e5, 11);\r
-       X3(C, D, A, B, M[15], 0x1fa27cf8, 16);\r
-       X3(B, C, D, A, M[2], 0xc4ac5665, 23);\r
-\r
-       /* Round 4 */\r
-       X4(A, B, C, D, M[0], 0xf4292244, 6);\r
-       X4(D, A, B, C, M[7], 0x432aff97, 10);\r
-       X4(C, D, A, B, M[14], 0xab9423a7, 15);\r
-       X4(B, C, D, A, M[5], 0xfc93a039, 21);\r
-       X4(A, B, C, D, M[12], 0x655b59c3, 6);\r
-       X4(D, A, B, C, M[3], 0x8f0ccc92, 10);\r
-       X4(C, D, A, B, M[10], 0xffeff47d, 15);\r
-       X4(B, C, D, A, M[1], 0x85845dd1, 21);\r
-       X4(A, B, C, D, M[8], 0x6fa87e4f, 6);\r
-       X4(D, A, B, C, M[15], 0xfe2ce6e0, 10);\r
-       X4(C, D, A, B, M[6], 0xa3014314, 15);\r
-       X4(B, C, D, A, M[13], 0x4e0811a1, 21);\r
-       X4(A, B, C, D, M[4], 0xf7537e82, 6);\r
-       X4(D, A, B, C, M[11], 0xbd3af235, 10);\r
-       X4(C, D, A, B, M[2], 0x2ad7d2bb, 15);\r
-       X4(B, C, D, A, M[9], 0xeb86d391, 21);\r
-\r
-       //Add this chunk's hash to result so far:\r
-       m_a0 += A;\r
-       m_b0 += B;\r
-       m_c0 += C;\r
-       m_d0 += D;\r
-}\r
-/**\r
- * Prepares the instance for a new checksum.\r
- */\r
-void ReMD5::reset() {\r
-       m_a0 = 0x67452301;\r
-       m_b0 = 0xefcdab89;\r
-       m_c0 = 0x98badcfe;\r
-       m_d0 = 0x10325476;\r
-       memset(m_digest, 0, sizeof m_digest);\r
-       memset(m_waiting, 0, m_chunkSize);\r
-       m_lengthWaiting = 0;\r
-       m_length = 0;\r
-       m_hexDigest.setLength(0);\r
-       m_finalized = false;\r
-}\r
+       rotate_left_and_add(var, var + F4(x, y, z) + data + aConst, shift, x)
+#endif /* OPTIMIZER_WORKS_GREAT */
+
+/**
+ * Processes a 512 bit block ("chunk").
+ *
+ * This is a optimized version, derived from the method above.
+ * We unroll the loop, this brings speed with factor 2.
+ * <pre>
+ * B := B + leftrotate((A + F + K[i] + M[g]), s[i])
+ *     D := C;
+ *     C := B;
+ *  B := B + leftrotate((A + F + K[i] + M[g]), s[i])
+ *  A := D(old)
+ * (D, C, B, A) = (C, B, B + leftrotate((A + F + K[i] + M[g]), s[i]), D)
+ * ==> (A, B, C, D) = (D, B + leftrotate((A + F + K[i] + M[g]), s[i]), B, A)
+ * The unrolled loop:
+ * i = g = 0;
+ * (A, B, C, D) = (D, B + leftrotate((A +  F1(B, C, D) + K[0] + M[0]), s[0]), B, A)
+ * only one var must be calculated, the other 3 are exchanged only.
+ * i = g = 1;
+ * (A, B, C, D) = (D, B + leftrotate((A +  F1(B, C, D) + K[1] + M[1]), s[1]), B, A)
+ * i = g = 2;
+ * (A, B, C, D) = (D, B + leftrotate((A +  F1(B, C, D) + K[2] + M[2]), s[2]), B, A)
+ * i = g = 3;
+ * (A, B, C, D) = (D, B + leftrotate((A +  F1(B, C, D) + K[3] + M[3]), s[3]), B, A)
+ * in each of the 4 statements another variable (of A, B, C and D) will be calculated
+ * so we do not exchange in each step, we calculate in the end position
+ * we define a function to do this:
+ * void X1(uint32_t &var, uint32_t x, uint32_t y, uint32_t z, uint32_t data, uint32_t shift, uint32_t aConst){
+ *      var = rotate_left(var+ F1(x, y, z) + data + aConst, shift) + x;
+ * }
+ * Note: the input parameter of X1 must respect the exchange:
+ * A -> D -> C -> B ...
+ * X1(A, B, C, D,  M[0], K[0], s[0]);
+ * X1(D, A, B, C,  M[1], K[1], s[1]);
+ * X1(C, D, A, B,  M[2], K[2], s[2]);
+ * X1(B, C, D, A,  M[3], K[3], s[3]);
+ * ...
+ * </pre>
+ */
+void ReMD5::processChunk(const uint8_t block[RE_DIGEST_CHUNK_SIZE]) {
+       uint32_t M[16];
+       //      break chunk into sixteen 32-bit words M[j], 0 â‰¤ j â‰¤ 15
+#ifdef __LITTLE_ENDIAN__
+       for (int ix = 0; ix < 16; ix++) {
+               //memcpy(&M[ix], block + ix * 4, 4);
+               M[ix] = *(uint32_t*) (block + ix * 4);
+       }
+#elif defined __BIG_ENDIAN__
+       for (int ix = 0; ix < 16; ix++) {
+               uint32_t x = block[3];
+               for (int jj = 2; jj >= 0; jj--) {
+                       x = (x << 8) + block[jj];
+               }
+               M[ix] = x;
+               block += 4;
+       }
+#else
+#      error "missing __LITTLE_ENDIAN__ or __BIG_ENDIAN__"
+#endif
+       //Initialize hash value for this chunk:
+       uint32_t A = m_a0;
+       uint32_t B = m_b0;
+       uint32_t C = m_c0;
+       uint32_t D = m_d0;
+#if defined NeverAndNeverAndNeverAgain
+       // Derivation of the optimization:
+
+#endif
+       /* Round 1 */
+       X1(A, B, C, D, M[0], 0xd76aa478, 7);
+       X1(D, A, B, C, M[1], 0xe8c7b756, 12);
+       X1(C, D, A, B, M[2], 0x242070db, 17);
+       X1(B, C, D, A, M[3], 0xc1bdceee, 22);
+       X1(A, B, C, D, M[4], 0xf57c0faf, 7);
+       X1(D, A, B, C, M[5], 0x4787c62a, 12);
+       X1(C, D, A, B, M[6], 0xa8304613, 17);
+       X1(B, C, D, A, M[7], 0xfd469501, 22);
+       X1(A, B, C, D, M[8], 0x698098d8, 7);
+       X1(D, A, B, C, M[9], 0x8b44f7af, 12);
+       X1(C, D, A, B, M[10], 0xffff5bb1, 17);
+       X1(B, C, D, A, M[11], 0x895cd7be, 22);
+       X1(A, B, C, D, M[12], 0x6b901122, 7);
+       X1(D, A, B, C, M[13], 0xfd987193, 12);
+       X1(C, D, A, B, M[14], 0xa679438e, 17);
+       X1(B, C, D, A, M[15], 0x49b40821, 22);
+
+       /* Round 2 */
+       X2(A, B, C, D, M[1], 0xf61e2562, 5);
+       X2(D, A, B, C, M[6], 0xc040b340, 9);
+       X2(C, D, A, B, M[11], 0x265e5a51, 14);
+       X2(B, C, D, A, M[0], 0xe9b6c7aa, 20);
+       X2(A, B, C, D, M[5], 0xd62f105d, 5);
+       X2(D, A, B, C, M[10], 0x02441453, 9);
+       X2(C, D, A, B, M[15], 0xd8a1e681, 14);
+       X2(B, C, D, A, M[4], 0xe7d3fbc8, 20);
+       X2(A, B, C, D, M[9], 0x21e1cde6, 5);
+       X2(D, A, B, C, M[14], 0xc33707d6, 9);
+       X2(C, D, A, B, M[3], 0xf4d50d87, 14);
+       X2(B, C, D, A, M[8], 0x455a14ed, 20);
+       X2(A, B, C, D, M[13], 0xa9e3e905, 5);
+       X2(D, A, B, C, M[2], 0xfcefa3f8, 9);
+       X2(C, D, A, B, M[7], 0x676f02d9, 14);
+       X2(B, C, D, A, M[12], 0x8d2a4c8a, 20);
+
+       /* Round 3 */
+       X3(A, B, C, D, M[5], 0xfffa3942, 4);
+       X3(D, A, B, C, M[8], 0x8771f681, 11);
+       X3(C, D, A, B, M[11], 0x6d9d6122, 16);
+       X3(B, C, D, A, M[14], 0xfde5380c, 23);
+       X3(A, B, C, D, M[1], 0xa4beea44, 4);
+       X3(D, A, B, C, M[4], 0x4bdecfa9, 11);
+       X3(C, D, A, B, M[7], 0xf6bb4b60, 16);
+       X3(B, C, D, A, M[10], 0xbebfbc70, 23);
+       X3(A, B, C, D, M[13], 0x289b7ec6, 4);
+       X3(D, A, B, C, M[0], 0xeaa127fa, 11);
+       X3(C, D, A, B, M[3], 0xd4ef3085, 16);
+       X3(B, C, D, A, M[6], 0x04881d05, 23);
+       X3(A, B, C, D, M[9], 0xd9d4d039, 4);
+       X3(D, A, B, C, M[12], 0xe6db99e5, 11);
+       X3(C, D, A, B, M[15], 0x1fa27cf8, 16);
+       X3(B, C, D, A, M[2], 0xc4ac5665, 23);
+
+       /* Round 4 */
+       X4(A, B, C, D, M[0], 0xf4292244, 6);
+       X4(D, A, B, C, M[7], 0x432aff97, 10);
+       X4(C, D, A, B, M[14], 0xab9423a7, 15);
+       X4(B, C, D, A, M[5], 0xfc93a039, 21);
+       X4(A, B, C, D, M[12], 0x655b59c3, 6);
+       X4(D, A, B, C, M[3], 0x8f0ccc92, 10);
+       X4(C, D, A, B, M[10], 0xffeff47d, 15);
+       X4(B, C, D, A, M[1], 0x85845dd1, 21);
+       X4(A, B, C, D, M[8], 0x6fa87e4f, 6);
+       X4(D, A, B, C, M[15], 0xfe2ce6e0, 10);
+       X4(C, D, A, B, M[6], 0xa3014314, 15);
+       X4(B, C, D, A, M[13], 0x4e0811a1, 21);
+       X4(A, B, C, D, M[4], 0xf7537e82, 6);
+       X4(D, A, B, C, M[11], 0xbd3af235, 10);
+       X4(C, D, A, B, M[2], 0x2ad7d2bb, 15);
+       X4(B, C, D, A, M[9], 0xeb86d391, 21);
+
+       //Add this chunk's hash to result so far:
+       m_a0 += A;
+       m_b0 += B;
+       m_c0 += C;
+       m_d0 += D;
+}
+/**
+ * Prepares the instance for a new checksum.
+ */
+void ReMD5::reset() {
+       m_a0 = 0x67452301;
+       m_b0 = 0xefcdab89;
+       m_c0 = 0x98badcfe;
+       m_d0 = 0x10325476;
+       memset(m_digest, 0, sizeof m_digest);
+       memset(m_waiting, 0, m_chunkSize);
+       m_lengthWaiting = 0;
+       m_length = 0;
+       m_hexDigest.setLength(0);
+       m_finalized = false;
+}
index de857f050863390c803794ba1d3e40e888aa1f99..3be988352aa1ad6cb52854a271133df532b546c4 100644 (file)
@@ -1,8 +1,12 @@
 /*
  * ReMD5.hpp
- *
- *  Created on: 30.01.2015
- *      Author: hm
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
+ * Do what you want.
+ * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
+ * The latest sources: https://github.com/republib
  */
 
 #ifndef MATH_REMD5_HPP_
index 846e228b7cf1dd9fe89330cb2d5c3235ec5698c3..05971159120f5efaa55a1a7a760b2d7feae37370 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * ReObfuscator.cpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
index 1740bcc77cc088436b01e7e76e031230e1abae36..267002d30cb26d92039ebdaf5bb872252b9e9773 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * ReObfuscator.hpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
index 601feab1f38322e9d7b6430c89d68c855debfc6b..929eab3e8938e3d927eccb474d23a6301f95fc8a 100644 (file)
-/*\r
- * ReRPD64.cpp\r
- *\r
- *  Created on: 30.01.2015\r
- *\r
- */\r
-\r
-#include "base/rebase.hpp"\r
-#include "math/remath.hpp"\r
-\r
-/**\r
- * Constructor.\r
- */\r
-ReRPD64::ReRPD64() :\r
-           ReDigest(m_digest, sizeof m_digest),\r
-           //m_digest\r
-           m_a0(0),\r
-           m_b0(0),\r
-           m_c0(0),\r
-           m_d0(0) {\r
-       reset();\r
-}\r
-\r
-/**\r
- * Destructor.\r
- */\r
-ReRPD64::~ReRPD64() {\r
-}\r
-\r
-/**\r
- * Finalizes the digest.\r
- *\r
- * Handles the rest block (< 64 byte) and append a special tail: a "1" bit\r
- * and the length of the total input length (in bits).\r
- *\r
- * @param block                        the rest input (incomplete chunk)\r
- * @param blockLength  the length of the block: 0..63\r
- */\r
-void ReRPD64::finalize() {\r
-       uint8_t* block = m_waiting;\r
-       int blockLength = m_lengthWaiting;\r
-       // append "1" bit to message\r
-       // Notice: the input bytes are considered as bits strings,\r
-       // where the first bit is the most significant bit of the byte.\r
-       block[blockLength++] = 0x80;\r
-       //Pre-processing: padding with zeros\r
-       //append "0" bit until message length in bits â‰¡ 448 (mod 512)\r
-       // fill the rest of the chunk with '\0'.\r
-       // the last 8 bytes is set to the length in bits (length in bytes * 8)\r
-       int restLength = (m_length + 1) % 64;\r
-       // 0 -> 56, 1 -> 55, 2 -> 54, ... 55 -> 1, 56 -> 0,\r
-       // 57 -> 63, 58 -> 62, ... 63 -> 57\r
-       int zeros = restLength <= 56 ? 56 - restLength : 120 - restLength;\r
-       memset(block + blockLength, 0, zeros);\r
-       blockLength += zeros;\r
-       //append original length in bits mod (2 pow 64) to message\r
-       uint64_t lengthBits = 8LL * m_length + m_salt;\r
-#if defined __LITTLE_ENDIAN__\r
-       memcpy(block + blockLength, &lengthBits, 8);\r
-       blockLength += 8;\r
-#else\r
-       block[blockLength++] = lengthBits;\r
-       lengthBits >>= 8;\r
-       block[blockLength++] = lengthBits;\r
-       lengthBits >>= 8;\r
-       block[blockLength++] = lengthBits;\r
-       lengthBits >>= 8;\r
-       block[blockLength++] = lengthBits;\r
-       lengthBits >>= 8;\r
-       block[blockLength++] = lengthBits;\r
-       lengthBits >>= 8;\r
-       block[blockLength++] = lengthBits;\r
-       lengthBits >>= 8;\r
-       block[blockLength++] = lengthBits;\r
-       lengthBits >>= 8;\r
-       block[blockLength++] = lengthBits;\r
-#endif\r
-       processChunk(block);\r
-       if (blockLength > 64)\r
-               processChunk(block + 64);\r
-       m_a0 ^= m_b0;\r
-       m_c0 ^= m_d0;\r
-#if defined __LITTLE_ENDIAN__\r
-       memcpy(m_digest, &m_a0, 8);\r
-       memcpy(m_digest + 8, &m_c0, 8);\r
-#else\r
+/*
+ * ReRPD64.cpp
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
+ * Do what you want.
+ * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
+ * The latest sources: https://github.com/republib
+ */
+
+#include "base/rebase.hpp"
+#include "math/remath.hpp"
+
+/**
+ * Constructor.
+ */
+ReRPD64::ReRPD64() :
+           ReDigest(m_digest, sizeof m_digest),
+           //m_digest
+           m_a0(0),
+           m_b0(0),
+           m_c0(0),
+           m_d0(0) {
+       reset();
+}
+
+/**
+ * Destructor.
+ */
+ReRPD64::~ReRPD64() {
+}
+
+/**
+ * Finalizes the digest.
+ *
+ * Handles the rest block (< 64 byte) and append a special tail: a "1" bit
+ * and the length of the total input length (in bits).
+ *
+ * @param block                        the rest input (incomplete chunk)
+ * @param blockLength  the length of the block: 0..63
+ */
+void ReRPD64::finalize() {
+       uint8_t* block = m_waiting;
+       int blockLength = m_lengthWaiting;
+       // append "1" bit to message
+       // Notice: the input bytes are considered as bits strings,
+       // where the first bit is the most significant bit of the byte.
+       block[blockLength++] = 0x80;
+       //Pre-processing: padding with zeros
+       //append "0" bit until message length in bits â‰¡ 448 (mod 512)
+       // fill the rest of the chunk with '\0'.
+       // the last 8 bytes is set to the length in bits (length in bytes * 8)
+       int restLength = (m_length + 1) % 64;
+       // 0 -> 56, 1 -> 55, 2 -> 54, ... 55 -> 1, 56 -> 0,
+       // 57 -> 63, 58 -> 62, ... 63 -> 57
+       int zeros = restLength <= 56 ? 56 - restLength : 120 - restLength;
+       memset(block + blockLength, 0, zeros);
+       blockLength += zeros;
+       //append original length in bits mod (2 pow 64) to message
+       uint64_t lengthBits = 8LL * m_length + m_salt;
+#if defined __LITTLE_ENDIAN__
+       memcpy(block + blockLength, &lengthBits, 8);
+       blockLength += 8;
+#else
+       block[blockLength++] = lengthBits;
+       lengthBits >>= 8;
+       block[blockLength++] = lengthBits;
+       lengthBits >>= 8;
+       block[blockLength++] = lengthBits;
+       lengthBits >>= 8;
+       block[blockLength++] = lengthBits;
+       lengthBits >>= 8;
+       block[blockLength++] = lengthBits;
+       lengthBits >>= 8;
+       block[blockLength++] = lengthBits;
+       lengthBits >>= 8;
+       block[blockLength++] = lengthBits;
+       lengthBits >>= 8;
+       block[blockLength++] = lengthBits;
+#endif
+       processChunk(block);
+       if (blockLength > 64)
+               processChunk(block + 64);
+       m_a0 ^= m_b0;
+       m_c0 ^= m_d0;
+#if defined __LITTLE_ENDIAN__
+       memcpy(m_digest, &m_a0, 8);
+       memcpy(m_digest + 8, &m_c0, 8);
+#else
 #define oneQWord(word, ix) m_digest[ix] = word; word >>= 8; \\r
        m_digest[ix + 1] = word; word >>= 8; m_digest[ix + 2] = word; word >>= 8; \\r
        m_digest[ix + 3] = word; word >>= 8; m_digest[ix + 4] = word; word >>= 8; \\r
        m_digest[ix + 4] = word; word >>= 8; m_digest[ix + 5] = word; word >>= 8; \\r
-       m_digest[ix + 6] = word; word >>= 8; m_digest[ix + 7] = word\r
-       oneWord(m_a0, 0);\r
-       oneWord(m_c0, 8);\r
-#endif\r
-}\r
-\r
-#define F1(B, C, D) ((B & C) | (~ B & D))\r
-#define F2(B, C, D) ((D & B) | (C & ~ D))\r
-#define F3(B, C, D)    ((B ^ C) ^ D)\r
-#define F4(B, C, D)    (C ^ (B | ~ D))\r
-\r
-#if defined OPTIMIZER_WORKS_GREAT\r
-inline void rotate_left_and_add(uint64_t& rc, uint64_t data, int shift, uint64_t term) {\r
-       rc = ((data << shift) | (data >> (64-shift))) + term;\r
-}\r
-inline void X1(uint64_t &var, uint64_t x, uint64_t y, uint64_t z,\r
-       uint64_t data, uint64_t aConst, uint64_t shift) {\r
-       rotate_left_and_add(var, var + F1(x, y, z) + data + aConst, shift, x);\r
-}\r
-inline void X2(uint64_t& var, uint64_t x, uint64_t y, uint64_t z,\r
-       uint64_t data, uint64_t aConst, uint64_t shift) {\r
-       rotate_left_and_add(var, var + F2(x, y, z) + data + aConst, shift, x);\r
-}\r
-\r
-inline void X3(uint64_t& var, uint64_t x, uint64_t y, uint64_t z,\r
-       uint64_t data, uint64_t aConst, uint64_t shift) {\r
-       rotate_left_and_add(var, var + F3(x, y, z) + data + aConst, shift, x);\r
-}\r
-\r
-inline void X4(uint64_t& var, uint64_t x, uint64_t y, uint64_t z,\r
-       uint64_t data, uint64_t aConst, uint64_t shift) {\r
-       rotate_left_and_add(var, var + F4(x, y, z) + data + aConst, shift, x);\r
-}\r
-#else\r
+       m_digest[ix + 6] = word; word >>= 8; m_digest[ix + 7] = word
+       oneWord(m_a0, 0);
+       oneWord(m_c0, 8);
+#endif
+}
+
+#define F1(B, C, D) ((B & C) | (~ B & D))
+#define F2(B, C, D) ((D & B) | (C & ~ D))
+#define F3(B, C, D)    ((B ^ C) ^ D)
+#define F4(B, C, D)    (C ^ (B | ~ D))
+
+#if defined OPTIMIZER_WORKS_GREAT
+inline void rotate_left_and_add(uint64_t& rc, uint64_t data, int shift, uint64_t term) {
+       rc = ((data << shift) | (data >> (64-shift))) + term;
+}
+inline void X1(uint64_t &var, uint64_t x, uint64_t y, uint64_t z,
+       uint64_t data, uint64_t aConst, uint64_t shift) {
+       rotate_left_and_add(var, var + F1(x, y, z) + data + aConst, shift, x);
+}
+inline void X2(uint64_t& var, uint64_t x, uint64_t y, uint64_t z,
+       uint64_t data, uint64_t aConst, uint64_t shift) {
+       rotate_left_and_add(var, var + F2(x, y, z) + data + aConst, shift, x);
+}
+
+inline void X3(uint64_t& var, uint64_t x, uint64_t y, uint64_t z,
+       uint64_t data, uint64_t aConst, uint64_t shift) {
+       rotate_left_and_add(var, var + F3(x, y, z) + data + aConst, shift, x);
+}
+
+inline void X4(uint64_t& var, uint64_t x, uint64_t y, uint64_t z,
+       uint64_t data, uint64_t aConst, uint64_t shift) {
+       rotate_left_and_add(var, var + F4(x, y, z) + data + aConst, shift, x);
+}
+#else
 #define rotate_left_and_add(var, data, shift, term) { \\r
        uint64_t val = data; \\r
        var = ((val << (shift)) | (val >> (64-(shift)))) + term; \\r
-}\r
+}
 #define X1(var, x, y, z, data, aConst, shift) \\r
-       rotate_left_and_add(var, var + F1(x, y, z) + data + aConst, shift, x)\r
+       rotate_left_and_add(var, var + F1(x, y, z) + data + aConst, shift, x)
 #define X2(var, x, y, z, data, aConst, shift) \\r
-       rotate_left_and_add(var, var + F2(x, y, z) + data + aConst, shift, x)\r
+       rotate_left_and_add(var, var + F2(x, y, z) + data + aConst, shift, x)
 #define X3(var, x, y, z, data, aConst, shift) \\r
-       rotate_left_and_add(var, var + F3(x, y, z) + data + aConst, shift, x)\r
+       rotate_left_and_add(var, var + F3(x, y, z) + data + aConst, shift, x)
 #define X4(var, x, y, z, data, aConst, shift) \\r
-       rotate_left_and_add(var, var + F4(x, y, z) + data + aConst, shift, x)\r
-#endif /* OPTIMIZER_WORKS_GREAT */\r
-\r
-/**\r
- * Processes a 512 bit block ("chunk").\r
- *\r
- */\r
-void ReRPD64::processChunk(const uint8_t block[64]) {\r
-       uint64_t M[8];\r
-       //      break chunk into sixteen 32-bit words M[j], 0 â‰¤ j â‰¤ 15\r
-#ifdef __LITTLE_ENDIAN__\r
-       for (int ix = 0; ix < 8; ix++) {\r
-               //memcpy(&M[ix], block + ix * 8, 8);\r
-               M[ix] = *(uint64_t*) (block + ix * 8);\r
-       }\r
-#elif defined __BIG_ENDIAN__\r
-       for (int ix = 0; ix < 16; ix++) {\r
-               uint64_t x = block[8-1];\r
-               for (int jj = 8 - 2; jj >= 0; jj--) {\r
-                       x = (x << 8) + block[jj];\r
-               }\r
-               M[ix] = x;\r
-               block += 8;\r
-       }\r
-#else\r
-#      error "missing __LITTLE_ENDIAN__ or __BIG_ENDIAN__"\r
-#endif\r
-       //Initialize hash value for this chunk:\r
-       uint64_t A = m_a0;\r
-       uint64_t B = m_b0;\r
-       uint64_t C = m_c0;\r
-       uint64_t D = m_d0;\r
-       /* Round 1 */\r
-       X1(A, B, C, D, M[0], 0xd76aa478698098d8ll, 7);\r
-       X1(D, A, B, C, M[1], 0xe8c7b7568b44f7afll, 12);\r
-       X1(C, D, A, B, M[2], 0x242070dbffff5bb1ll, 17);\r
-       X1(B, C, D, A, M[3], 0xc1bdceee895cd7bell, 22);\r
-       X1(A, B, C, D, M[4], 0xf57c0faf6b901122ll, 32 + 7);\r
-       X1(D, A, B, C, M[5], 0x4787c62afd987193ll, 32 + 12);\r
-       X1(C, D, A, B, M[6], 0xa8304613a679438ell, 32 + 17);\r
-       X1(B, C, D, A, M[7], 0xfd46950149b40821ll, 32 + 22);\r
-\r
-       /* Round 2 */\r
-       X2(A, B, C, D, M[1], 0xf61e256221e1cde6ll, 5);\r
-       X2(D, A, B, C, M[6], 0xc040b340c33707d6ll, 9);\r
-       X2(C, D, A, B, M[3], 0x265e5a51f4d50d87ll, 14);\r
-       X2(B, C, D, A, M[0], 0xe9b6c7aa455a14edll, 20);\r
-       X2(A, B, C, D, M[5], 0xd62f105da9e3e905ll, 5 + 32);\r
-       X2(D, A, B, C, M[2], 0x02441453fcefa3f8ll, 9 + 32);\r
-       X2(C, D, A, B, M[7], 0xd8a1e681676f02d9ll, 14 + 32);\r
-       X2(B, C, D, A, M[4], 0xe7d3fbc88d2a4c8all, 20 + 32);\r
-\r
-       /* Round 3 */\r
-       X3(A, B, C, D, M[5], 0xfffa3942289b7ec6ll, 4);\r
-       X3(D, A, B, C, M[2], 0x8771f681eaa127fall, 11);\r
-       X3(C, D, A, B, M[7], 0x6d9d6122d4ef3085ll, 16);\r
-       X3(B, C, D, A, M[4], 0xfde5380c04881d05ll, 23);\r
-       X3(A, B, C, D, M[1], 0xa4beea44d9d4d039ll, 4 + 32);\r
-       X3(D, A, B, C, M[6], 0x4bdecfa9e6db99e5ll, 11 + 32);\r
-       X3(C, D, A, B, M[3], 0xf6bb4b601fa27cf8ll, 16 + 32);\r
-       X3(B, C, D, A, M[0], 0xbebfbc70c4ac5665ll, 23 + 32);\r
-\r
-       /* Round 4 */\r
-       X4(A, B, C, D, M[0], 0xf42922446fa87e4fll, 6);\r
-       X4(D, A, B, C, M[7], 0x432aff97fe2ce6e0ll, 10);\r
-       X4(C, D, A, B, M[4], 0xab9423a7a3014314ll, 15);\r
-       X4(B, C, D, A, M[1], 0xfc93a0394e0811a1ll, 21);\r
-       X4(A, B, C, D, M[6], 0x655b59c3f7537e82ll, 6 + 32);\r
-       X4(D, A, B, C, M[3], 0x8f0ccc92bd3af235ll, 10 + 32);\r
-       X4(C, D, A, B, M[0], 0xffeff47d2ad7d2bbll, 15 + 32);\r
-       X4(B, C, D, A, M[5], 0x85845dd1eb86d391ll, 21 + 32);\r
-\r
-       //Add this chunk's hash to result so far:\r
-       m_a0 += A;\r
-       m_b0 += B;\r
-       m_c0 += C;\r
-       m_d0 += D;\r
-}\r
-/**\r
- * Prepares the instance for a new checksum.\r
- */\r
-void ReRPD64::reset() {\r
-       m_a0 = 0xd76aa478698098d8ll;\r
-       m_b0 = 0xe8c7b7568b44f7afll;\r
-       m_c0 = 0x242070dbffff5bb1ll;\r
-       m_d0 = 0xc1bdceee895cd7bell;\r
-       memset(m_digest, 0, sizeof m_digest);\r
-       memset(m_waiting, 0, m_chunkSize * 2);\r
-       m_lengthWaiting = 0;\r
-       m_length = 0;\r
-       m_hexDigest.setLength(0);\r
-       m_finalized = false;\r
-}\r
-\r
+       rotate_left_and_add(var, var + F4(x, y, z) + data + aConst, shift, x)
+#endif /* OPTIMIZER_WORKS_GREAT */
+
+/**
+ * Processes a 512 bit block ("chunk").
+ *
+ */
+void ReRPD64::processChunk(const uint8_t block[64]) {
+       uint64_t M[8];
+       //      break chunk into sixteen 32-bit words M[j], 0 â‰¤ j â‰¤ 15
+#ifdef __LITTLE_ENDIAN__
+       for (int ix = 0; ix < 8; ix++) {
+               //memcpy(&M[ix], block + ix * 8, 8);
+               M[ix] = *(uint64_t*) (block + ix * 8);
+       }
+#elif defined __BIG_ENDIAN__
+       for (int ix = 0; ix < 16; ix++) {
+               uint64_t x = block[8-1];
+               for (int jj = 8 - 2; jj >= 0; jj--) {
+                       x = (x << 8) + block[jj];
+               }
+               M[ix] = x;
+               block += 8;
+       }
+#else
+#      error "missing __LITTLE_ENDIAN__ or __BIG_ENDIAN__"
+#endif
+       //Initialize hash value for this chunk:
+       uint64_t A = m_a0;
+       uint64_t B = m_b0;
+       uint64_t C = m_c0;
+       uint64_t D = m_d0;
+       /* Round 1 */
+       X1(A, B, C, D, M[0], 0xd76aa478698098d8ll, 7);
+       X1(D, A, B, C, M[1], 0xe8c7b7568b44f7afll, 12);
+       X1(C, D, A, B, M[2], 0x242070dbffff5bb1ll, 17);
+       X1(B, C, D, A, M[3], 0xc1bdceee895cd7bell, 22);
+       X1(A, B, C, D, M[4], 0xf57c0faf6b901122ll, 32 + 7);
+       X1(D, A, B, C, M[5], 0x4787c62afd987193ll, 32 + 12);
+       X1(C, D, A, B, M[6], 0xa8304613a679438ell, 32 + 17);
+       X1(B, C, D, A, M[7], 0xfd46950149b40821ll, 32 + 22);
+
+       /* Round 2 */
+       X2(A, B, C, D, M[1], 0xf61e256221e1cde6ll, 5);
+       X2(D, A, B, C, M[6], 0xc040b340c33707d6ll, 9);
+       X2(C, D, A, B, M[3], 0x265e5a51f4d50d87ll, 14);
+       X2(B, C, D, A, M[0], 0xe9b6c7aa455a14edll, 20);
+       X2(A, B, C, D, M[5], 0xd62f105da9e3e905ll, 5 + 32);
+       X2(D, A, B, C, M[2], 0x02441453fcefa3f8ll, 9 + 32);
+       X2(C, D, A, B, M[7], 0xd8a1e681676f02d9ll, 14 + 32);
+       X2(B, C, D, A, M[4], 0xe7d3fbc88d2a4c8all, 20 + 32);
+
+       /* Round 3 */
+       X3(A, B, C, D, M[5], 0xfffa3942289b7ec6ll, 4);
+       X3(D, A, B, C, M[2], 0x8771f681eaa127fall, 11);
+       X3(C, D, A, B, M[7], 0x6d9d6122d4ef3085ll, 16);
+       X3(B, C, D, A, M[4], 0xfde5380c04881d05ll, 23);
+       X3(A, B, C, D, M[1], 0xa4beea44d9d4d039ll, 4 + 32);
+       X3(D, A, B, C, M[6], 0x4bdecfa9e6db99e5ll, 11 + 32);
+       X3(C, D, A, B, M[3], 0xf6bb4b601fa27cf8ll, 16 + 32);
+       X3(B, C, D, A, M[0], 0xbebfbc70c4ac5665ll, 23 + 32);
+
+       /* Round 4 */
+       X4(A, B, C, D, M[0], 0xf42922446fa87e4fll, 6);
+       X4(D, A, B, C, M[7], 0x432aff97fe2ce6e0ll, 10);
+       X4(C, D, A, B, M[4], 0xab9423a7a3014314ll, 15);
+       X4(B, C, D, A, M[1], 0xfc93a0394e0811a1ll, 21);
+       X4(A, B, C, D, M[6], 0x655b59c3f7537e82ll, 6 + 32);
+       X4(D, A, B, C, M[3], 0x8f0ccc92bd3af235ll, 10 + 32);
+       X4(C, D, A, B, M[0], 0xffeff47d2ad7d2bbll, 15 + 32);
+       X4(B, C, D, A, M[5], 0x85845dd1eb86d391ll, 21 + 32);
+
+       //Add this chunk's hash to result so far:
+       m_a0 += A;
+       m_b0 += B;
+       m_c0 += C;
+       m_d0 += D;
+}
+/**
+ * Prepares the instance for a new checksum.
+ */
+void ReRPD64::reset() {
+       m_a0 = 0xd76aa478698098d8ll;
+       m_b0 = 0xe8c7b7568b44f7afll;
+       m_c0 = 0x242070dbffff5bb1ll;
+       m_d0 = 0xc1bdceee895cd7bell;
+       memset(m_digest, 0, sizeof m_digest);
+       memset(m_waiting, 0, m_chunkSize * 2);
+       m_lengthWaiting = 0;
+       m_length = 0;
+       m_hexDigest.setLength(0);
+       m_finalized = false;
+}
+
index 08211b5634642e2fb8522c70130d6a059b41cfd0..2a2e7321be2f8871f533d4db937cf9724a6fda2f 100644 (file)
@@ -1,8 +1,12 @@
 /*
  * ReRPD64.hpp
- *
- *  Created on: 30.01.2015
- *      Author: hm
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
+ * Do what you want.
+ * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
+ * The latest sources: https://github.com/republib
  */
 
 #ifndef MATH_RERPD64_HPP_
index 7a83974a6613be6cffd11583220bd790310f909b..7dbab474881f3005776a1466c2a4874fc3d2efcf 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * ReRandomizer.cpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
index c336ee720bdf4f3bfa7660c2161692b2efd51473..861a894c43e69be84f1f6c8c0b220075431e6612 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * ReRandomizer.hpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
index db9eca9d3347bb10a66396586964947d70b1c76e..e6a2a909b03e2cecba0d4763dde194938b41b7ba 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * remath.hpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
index 905b1642f36f5812fb44812d77be649f55189aa4..300c6db69d172ed2c93c47faead6586ccdfcc096 100644 (file)
-/*\r
- * ReTCP.cpp\r
- *\r
- *  Created on: 04.03.2015\r
- *      Author: hm\r
- */\r
-\r
-#include "base/rebase.hpp"\r
-#include "net/renet.hpp"\r
-\r
-enum LOCATION_DIRTOOL {\r
-       LC_LISTEN_FOR_ALL_1 = LC_TCP + 1, // 50501\r
-       LC_LISTEN_FOR_ALL_2,    // 50502\r
-       LC_LISTEN_FOR_ALL_3,    // 50503\r
-       LC_RECEIVE_5,                   // 50504\r
-       LC_LISTEN_FOR_ALL_5,    // 50505\r
-       LC_LISTEN_FOR_ALL_6,    // 50506\r
-       LC_WRITE_1,                     // 50507\r
-       LC_CONNECT_1,                   // 50508\r
-       LC_SOCKET_ADDR_SET_1,   // 50509\r
-       LC_LISTEN_FOR_ALL_7,    // 50510\r
-       LC_HANDLE_CONNECTION_1, // 50511\r
-       LC_RECEIVE_1,                   // 50512\r
-       LC_RECEIVE_2,                   // 50513\r
-       LC_RECEIVE_3,                   // 50514\r
-       LC_RECEIVE_4,                   // 50515\r
-       LC_CONNECT_2,                   // 50516\r
-       LC_CONNECT_3,                   // 50517\r
-       LC_TCP_CONNECTION_1,    // 50518\r
-       LC_WRITE_2,                             // 50519\r
-       LC_SERVER_CONNECTION_RUN_1,     // 50520\r
-       LC_SERVER_CONNECTION_RUN_2,     // 50521\r
-       LC_SERVER_CONNECTION_RUN_3,     // 50522\r
-       LC_HANDLE_CONNECTION_4, // 50523\r
-};\r
-\r
-#if defined __WIN32__\r
-bool ReTCPConnection::isGlobalInitialized = false;\r
-#endif\r
-\r
-/**\r
- * Constructor.\r
- *\r
- * @param message      the description of the exception\r
- */\r
-ReTCPException::ReTCPException(const char* message) {\r
-\r
-}\r
-;\r
-\r
-/**\r
- * Constructor.\r
- *\r
- * @param socket       the socket which is disconnected\r
- */\r
-ReTCPDisconnectException::ReTCPDisconnectException(int socket) :\r
-           ReTCPException(i18n("disconnected")),\r
-           m_socket(socket) {\r
-}\r
-\r
-/**\r
- * Constructor.\r
- *\r
- * @param logger       the logger for error handling\r
- */\r
-ReSocketAddress::ReSocketAddress(ReLoggerOwner* loggerOwner) :\r
-           m_preferredFamily(AF_INET),\r
-           m_family(-1),\r
-           m_port(0),\r
-           m_loggerOwner(loggerOwner),\r
-           // m_ip\r
-           m_name() {\r
-       memset(&m_ip, 0, sizeof m_ip);\r
-}\r
-/**\r
- * Destructor.\r
- */\r
-ReSocketAddress::~ReSocketAddress() {\r
-}\r
-void addressToString(struct addrinfo& addr, char* ip, size_t ipSize) {\r
-       if (addr.ai_family == AF_INET) {\r
-               inet_ntop(addr.ai_family, (struct sockaddr_in *) addr.ai_addr, ip,\r
-                   ipSize);\r
-       } else if (addr.ai_family == AF_INET6) {\r
-               inet_ntop(addr.ai_family, (struct sockaddr_in6 *) addr.ai_addr, ip,\r
-                   ipSize);\r
-       }\r
-}\r
-/**\r
- * Sets the data from symbolic values.\r
- */\r
-void ReSocketAddress::setAddress(const char* ip, int port) {\r
-       struct addrinfo hints;\r
-       struct addrinfo* infoList;\r
-       int status;\r
-       m_port = port;\r
-       memset(&hints, 0, sizeof hints);\r
-       hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version\r
-       hints.ai_socktype = SOCK_STREAM;\r
-\r
-       if ((status = getaddrinfo(ip, NULL, &hints, &infoList)) != 0) {\r
-               m_loggerOwner->logger()->sayF(LOG_ERROR | CAT_NETWORK,\r
-                   LC_SOCKET_ADDR_SET_1, i18n("getaddrinfo($1) failed: $2")).arg(ip)\r
-                   .arg(errno).end();\r
-       }\r
-\r
-       struct addrinfo* ptr;\r
-       bool hasIP4 = false;\r
-       bool hasIP6 = false;\r
-\r
-       // Search for a available ip address\r
-       // if more than one are available, prefer m_preferredFamily:\r
-       m_ip[0] = '\0';\r
-       for (ptr = infoList; ptr != NULL; ptr = ptr->ai_next) {\r
-               // different fields in IPv4 and IPv6:\r
-               if (!hasIP4 && ptr->ai_family == AF_INET) {\r
-                       m_family = AF_INET;\r
-                       hasIP4 = true;\r
-               } else if (!hasIP6 && ptr->ai_family == AF_INET6) {\r
-                       m_family = AF_INET6;\r
-                       hasIP6 = true;\r
-               }\r
-               addressToString(*ptr, m_ip, sizeof m_ip);\r
-               if (ptr->ai_family == m_preferredFamily)\r
-                       break;\r
-       }\r
-       // free the linked list\r
-       freeaddrinfo(infoList);\r
-       if (m_ip[0] == '\0')\r
-               m_name.setLength(0);\r
-       else\r
-               m_name.set(m_ip).appendChar(':').appendInt(port);\r
-}\r
-\r
-/**\r
- * Constructor.\r
- *\r
- * @param logger       logger for the error handling\r
- */\r
-#pragma warning( push )\r
-#pragma warning( disable : 4355 )\r
-ReTCPClient::ReTCPClient(ReLogger* logger) :\r
-           ReTCPConnection(-1, this),\r
-           m_logger(logger) {\r
-#pragma warning( pop )\r
-}\r
-/**\r
- * Destructor.\r
- */\r
-ReTCPClient::~ReTCPClient() {\r
-}\r
-/**\r
- * Connects a client with the server.\r
- *\r
- * @param ip   domain address ("denic.de") or ip address ("192.168.2.1")\r
- * @param port port number: 1..65535\r
- */\r
-bool ReTCPClient::connect(const char* ip, int port) {\r
-       bool rc = false;\r
-       struct addrinfo hints;\r
-       struct addrinfo* addr = NULL;\r
-\r
-       memset(&hints, 0, sizeof hints);\r
-       hints.ai_family = AF_UNSPEC;\r
-       hints.ai_socktype = SOCK_STREAM;\r
-\r
-       getaddrinfo(ip, ReByteBuffer("").appendInt(port).str(), &hints, &addr);\r
-       if (addr == NULL) {\r
-               m_logger->sayF(LOG_ERROR | CAT_NETWORK, LC_CONNECT_1,\r
-                   i18n("ip not reachable: $1")).arg(ip).end();\r
-       } else {\r
-               addressToString(*addr, m_ip, sizeof m_ip);\r
-               m_peerName.set(m_ip).appendChar(':').appendInt(port);\r
-               m_port = port;\r
-               if ((m_handleSocket = socket(addr->ai_family, addr->ai_socktype,\r
-                   addr->ai_protocol)) < 0)\r
-                       m_logger->sayF(LOG_ERROR | CAT_NETWORK, LC_CONNECT_2,\r
-                           i18n("socket() failed ($1): $2")).arg(errno).arg(m_peerName).end();\r
-               else if (::connect(m_handleSocket, addr->ai_addr, addr->ai_addrlen)\r
-                   != 0)\r
-                       m_logger->sayF(LOG_ERROR | CAT_NETWORK, LC_CONNECT_3,\r
-                           i18n("connect() failed ($1): $2")).arg(errno).arg(m_peerName)\r
-                           .end();\r
-               else\r
-                       rc = true;\r
-       }\r
-       return rc;\r
-}\r
-\r
-/**\r
- * Returns the logger.\r
- *\r
- * @param the logger for error handling\r
- */\r
-ReLogger* ReTCPClient::logger() {\r
-       return m_logger;\r
-}\r
-\r
-/**\r
- * Constructor.\r
- *\r
- * @param id           an identifier for logging\r
- * @param logger       the logger for error handling\r
- */\r
-ReTCPConnection::ReTCPConnection(int id, ReLoggerOwner* loggerOwner) :\r
-           ReSocketAddress(loggerOwner),\r
-           m_peerName(),\r
-           m_received(),\r
-           m_handleSocket(-1),\r
-           m_id(id),\r
-           m_noSent(0),\r
-           m_noReceived(0),\r
-           m_logSendReceive(true) {\r
-#if defined __WIN32__\r
-       if (! isGlobalInitialized){\r
-               WSADATA wsaData;\r
-               if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) {\r
-                       loggerOwner->logger()->sayF(LOG_ERROR | CAT_NETWORK, LC_TCP_CONNECTION_1,\r
-                               i18n("WSAStartup() failed: $1")).arg(errno).arg(getLastOSError()).end();\r
-                       throw ReException("WSAStartup() failed");\r
-               }\r
-               isGlobalInitialized = true;\r
-       }\r
-#endif\r
-}\r
-\r
-/**\r
- * Destructor.\r
- */\r
-ReTCPConnection::~ReTCPConnection() {\r
-}\r
-/**\r
- * Finishes the connection (in both directions) and frees the resouces.\r
- */\r
-void ReTCPConnection::close() {\r
-       if (m_handleSocket >= 0) {\r
-               reCloseSocket(m_handleSocket);\r
-               m_handleSocket = -1;\r
-       }\r
-}\r
-\r
-/**\r
- * Frees the global resources.\r
- */\r
-void ReTCPConnection::globalClose() {\r
-#if defined __WIN32__\r
-       if (isGlobalInitialized)\r
-               WSACleanup();\r
-#endif\r
-}\r
-\r
-/**\r
- * Sets the address given by a family and a string like "192.168.0.1:22".\r
- *\r
- * @param family       AF_INET or AF_INET6\r
- * @param ip           the string describing the address, e.g. "192.168.0.1"\r
- * @param port         the port of the peer\r
- */\r
-void ReTCPConnection::setConnectedAddress(int family, const char* ip,\r
-    int port) {\r
-       m_family = family;\r
-       m_name = ip;\r
-       m_port = port;\r
-       int length = strlen(ip);\r
-       memcpy(m_ip, ip, length);\r
-       m_ip[length] = '\0';\r
-       m_name.appendChar(':').appendInt(port);\r
-}\r
-\r
-/**\r
- * Receives a message.\r
- *\r
- * @param command      OUT: the received command\r
- * @param data         OUT: the received data\r
- *\r
- * @throws ReTCPBrokenPipeException\r
- */\r
-void ReTCPConnection::receive(ReByteBuffer& command, ReByteBuffer& data) {\r
-       command.setLength(8);\r
-       int received = recv(m_handleSocket, command.buffer(), 8, 0);\r
-       if (received == 0) {\r
-               // connection closed from the peer\r
-               throw ReTCPDisconnectException(m_handleSocket);\r
-       } else if (received != 8) {\r
-               m_loggerOwner->logger()->sayF(LOG_ERROR | CAT_NETWORK, LC_RECEIVE_1,\r
-                   i18n("cannot receive ($1): $2 [$3]")).arg(errno).arg(received).arg(\r
-                   m_peerName).end();\r
-       } else {\r
-               int flags = 0;\r
-               int length = 0;\r
-               int found;\r
-               if ((found = sscanf(command.str(), "%8x", &length)) != 1\r
-                   || (flags = (length >> 24)) > 256 || (length &= 0xffffff) < 8) {\r
-                       m_loggerOwner->logger()->sayF(LOG_ERROR | CAT_NETWORK, LC_RECEIVE_2,\r
-                           i18n("wrong format: $1 [$2]")).arg(command).arg(m_peerName).end();\r
-               } else {\r
-                       data.setLength(length);\r
-                       int readBytes = 0;\r
-                       int rest = length;\r
-                       char* buf = data.buffer();\r
-                       int rounds = 0;\r
-                       while (readBytes < length) {\r
-                               rounds++;\r
-                               received = recv(m_handleSocket, buf, rest, 0);\r
-                               if (received > 0) {\r
-                                       buf += received;\r
-                                       rest -= received;\r
-                                       readBytes += received;\r
-                               } else if (received == 0) {\r
-                                       data.setLength(readBytes);\r
-                                       break;\r
-                               } else {\r
-                                       m_loggerOwner->logger()->sayF(LOG_ERROR | CAT_NETWORK,\r
-                                           LC_RECEIVE_3, i18n("cannot receive ($1): $2 [$3]")).arg(\r
-                                       errno).arg(received).arg(m_peerName).end();\r
-                                       break;\r
-                               }\r
-                       }\r
-                       if (rounds)\r
-                               rounds += 0;\r
-                       if (readBytes < length) {\r
-                               m_loggerOwner->logger()->sayF(LOG_ERROR | CAT_NETWORK,\r
-                                   LC_RECEIVE_4, i18n("too few bytes read: $1/$2 [$3]")).arg(\r
-                                   readBytes).arg(received).arg(m_peerName).end();\r
-                       } else if (m_logSendReceive) {\r
-                               m_loggerOwner->logger()->sayF(LOG_DEBUG | CAT_NETWORK,\r
-                                   LC_RECEIVE_5,\r
-                                   i18n("received: $1 bytes in $2 round(s) [$3]: $4")).arg(\r
-                                   length).arg(rounds).arg(m_peerName).arg(\r
-                                   ReByteBuffer().appendDump(data.str(), data.length(), 80).str())\r
-                                   .end();\r
-                       }\r
-                       command.setLength(0);\r
-                       if (readBytes >= 8) {\r
-                               command.append(data.str(), 8);\r
-                               data.remove(0, 8);\r
-                       }\r
-               }\r
-       }\r
-}\r
-/**\r
- * Sends a command with (or without) data to the peer.\r
- *\r
- * @param command      the command to send\r
- * @param data         the data to send\r
- */\r
-void ReTCPConnection::send(const char* command, const char* data, int length) {\r
-       if (data == NULL)\r
-               length = 0;\r
-       else if (length < 0)\r
-               length = strlen(data);\r
-       m_toSend.ensureSize(length + 16);\r
-       ++m_noSent;\r
-       m_toSend.setLength(0);\r
-       int flags = 0x7b;\r
-       // add command length:\r
-       int rest = length + 8;\r
-       m_toSend.appendInt(rest | (flags << 24), "%08x");\r
-       // we send the flag/length too:\r
-       rest += 8;\r
-       m_toSend.appendFix(command, -1, 8, 8, NULL);\r
-       m_toSend.append(data, length);\r
-       int sent = 0;\r
-       const char* buf = m_toSend.str();\r
-       int rounds = 0;\r
-\r
-       while (rest > 0) {\r
-               rounds++;\r
-               if ((sent = ::send(m_handleSocket, buf, rest, 0)) > 0) {\r
-                       buf += sent;\r
-                       rest -= sent;\r
-               } else {\r
-                       m_loggerOwner->logger()->sayF(LOG_ERROR | CAT_NETWORK, LC_WRITE_1,\r
-                           i18n("cannot send ($1): $2")).arg(errno).arg(m_peerName).end();\r
-                       break;\r
-               }\r
-       }\r
-       if (rest == 0 && m_logSendReceive)\r
-               m_loggerOwner->logger()->sayF(LOG_DEBUG | CAT_NETWORK, LC_WRITE_2,\r
-                   i18n("sent: $1 bytes in $2 round(s): $3 $4")).arg(length).arg(\r
-                   rounds).arg(command).arg(\r
-                   ReByteBuffer().appendDump(data, length, 80).str()).end();\r
-}\r
-\r
-/**\r
- * Constructor.\r
- *\r
- * @param id           an identifier for logging\r
- * @param logger       the logger for error handling\r
- */\r
-#pragma warning( push )\r
-#pragma warning( disable : 4355 )\r
-ReTCPServerConnection::ReTCPServerConnection(int id, ReTCPServer* server) :\r
-           ReTCPConnection(id, this),\r
-           ReThread(true),\r
-           m_server(server) {\r
-#pragma warning( pop )\r
-}\r
-\r
-/**\r
- * Destructor.\r
- */\r
-ReTCPServerConnection::~ReTCPServerConnection() {\r
-}\r
-\r
-/**\r
- * Serves the commands of a single connection (in a single thread).\r
- */\r
-void ReTCPServerConnection::run() {\r
-       ReByteBuffer command;\r
-       ReNetCommandHandler::ProcessingState rc = ReNetCommandHandler::PS_UNDEF;\r
-       m_loggerOwner->logger()->sayF(LOG_INFO | CAT_NETWORK,\r
-           LC_SERVER_CONNECTION_RUN_1, i18n("new connection to $1")).arg(m_name)\r
-           .end();\r
-       try {\r
-               do {\r
-                       receive(command, m_received);\r
-                       rc = m_server->handler().handleNetCommand(command, m_received,\r
-                           this);\r
-                       if (rc == ReNetCommandHandler::PS_UNKNOWN) {\r
-                               logger()->sayF(LOG_ERROR | CAT_NETWORK, LC_HANDLE_CONNECTION_1,\r
-                                   i18n("unknown command: $1 length: $2")).arg(command).arg(\r
-                                   m_received.length()).end();\r
-                       }\r
-               } while (rc != ReNetCommandHandler::PS_STOP && !m_shouldStop);\r
-               m_pool->setShouldStop();\r
-       } catch (ReTCPDisconnectException& e) {\r
-               rc = ReNetCommandHandler::PS_CLOSED;\r
-       }\r
-       close();\r
-       if (rc == ReNetCommandHandler::PS_CLOSED)\r
-               logger()->sayF(LOG_INFO | CAT_NETWORK, LC_HANDLE_CONNECTION_4,\r
-                   i18n("connection closed by the peer $1")).arg(m_peerName).end();\r
-       else if (rc == ReNetCommandHandler::PS_STOP)\r
-               m_loggerOwner->logger()->sayF(LOG_INFO | CAT_NETWORK,\r
-                   LC_SERVER_CONNECTION_RUN_2, i18n("stop signal received from $1"))\r
-                   .arg(m_name).end();\r
-       else\r
-               m_loggerOwner->logger()->say(LOG_INFO | CAT_NETWORK,\r
-                   LC_SERVER_CONNECTION_RUN_3, i18n("stop order received"));\r
-       m_id = -1;\r
-}\r
-\r
-/**\r
- * Constructor.\r
- *\r
- * @param port                         the port for listening\r
- * @param commandHandler       a handler which can process the incoming commands.\r
- *                                                     May be NULL\r
- * @param logger                       the logger for error handling\r
- * @param maxConnections       maximal count of threads handling a connection\r
- */\r
-#pragma warning( push )\r
-#pragma warning( disable : 4355 )\r
-ReTCPServer::ReTCPServer(int port, class ReNetCommandHandler& commandHandler,\r
-    ReLogger* logger, int maxConnections) :\r
-           ReTCPConnection(0, this),\r
-           m_maxConnections(maxConnections),\r
-           m_handler(commandHandler),\r
-           m_logger(logger),\r
-           m_threadPool(maxConnections, logger) {\r
-#pragma warning( pop )\r
-       m_port = port;\r
-}\r
-\r
-/**\r
- * Destructor.\r
- */\r
-ReTCPServer::~ReTCPServer() {\r
-       // m_poolConnections does the things!\r
-}\r
-/**\r
- * Creates a server connection.\r
- *\r
- * @param id                   the connection identifier\r
- * @param handleSocket the handle of the read/write channel\r
- * @address                            the data about the client connection (ip, port)\r
- */\r
-ReTCPServerConnection* ReTCPServer::createConnection(int id, int handleSocket) {\r
-       ReTCPServerConnection* rc = NULL;\r
-       if (m_threadPool.countThreads() < m_maxConnections) {\r
-               union {\r
-                       struct sockaddr address;\r
-                       uint8_t dummy[32];\r
-               } u;\r
-               struct sockaddr& address = u.address;\r
-               memset(&u, -1, sizeof u);\r
-               socklen_t sizeAddress = sizeof u; //.address;\r
-               int rc2 = getpeername(handleSocket, &address, &sizeAddress);\r
-               if (rc2 != 0)\r
-                       m_logger->sayF(LOG_ERROR | CAT_NETWORK, LC_LISTEN_FOR_ALL_1,\r
-                   i18n("getpeername() failed: $1")).arg(getLastOSError()).end();\r
-               rc = new ReTCPServerConnection(id, this);\r
-               rc->setId(id);\r
-               rc->setHandleSocket(handleSocket);\r
-               char ip[INET6_ADDRSTRLEN];\r
-               inet_ntop(address.sa_family,\r
-                   address.sa_family == AF_INET ?\r
-                       (void *) &(((struct sockaddr_in*) &address)->sin_addr) :\r
-                       (void *) &(((struct sockaddr_in6*) &address)->sin6_addr), ip,\r
-                   sizeof ip);\r
-               int port =\r
-                   address.sa_family == AF_INET ?\r
-                       (int) ntohs(((struct sockaddr_in*) &address)->sin_port) :\r
-                       (int) ntohl(((struct sockaddr_in6*) &address)->sin6_port);\r
-               rc->setConnectedAddress(address.sa_family, ip, port);\r
-       }\r
-       return rc;\r
-}\r
-\r
-/**\r
- * Accepts connections and create a thread which will handle this connection.\r
- */\r
-bool ReTCPServer::listenForAll() {\r
-       bool rc = false;\r
-       struct addrinfo hints;\r
-       struct addrinfo* addrInfo;\r
-\r
-// first, load up address structs with getaddrinfo():\r
-       memset(&hints, 0, sizeof hints);\r
-       hints.ai_family = AF_UNSPEC;  // use IPv4 or IPv6, whichever\r
-       hints.ai_socktype = SOCK_STREAM;\r
-       hints.ai_flags = AI_PASSIVE;     // fill in my IP for me\r
-\r
-       memcpy(m_ip, "0.0.0.0", 8);\r
-       m_name.set(m_ip).appendChar(':').appendInt(m_port);\r
-       getaddrinfo(NULL, ReByteBuffer().appendInt(m_port).str(), &hints,\r
-           &addrInfo);\r
-       m_family = addrInfo->ai_family;\r
-// make a socket:\r
-       m_handleSocket = socket(addrInfo->ai_family, addrInfo->ai_socktype,\r
-           addrInfo->ai_protocol);\r
-       if (m_handleSocket == -1) {\r
-               m_logger->sayF(LOG_ERROR | CAT_NETWORK, LC_LISTEN_FOR_ALL_1,\r
-                   i18n("cannot create a socket: $1")).arg(errno).end();\r
-       } else {\r
-               int yes = 1;\r
-               // Avoid the "Address already in use" error message of finished processes\r
-               // that are still waiting for the release by the kernel:\r
-               if (setsockopt(m_handleSocket, SOL_SOCKET, SO_REUSEADDR,\r
-                   reinterpret_cast<const char*>(&yes), sizeof(int)) == -1) {\r
-                       m_logger->sayF(LOG_WARNING | CAT_NETWORK, LC_LISTEN_FOR_ALL_7,\r
-                           i18n("setsockopt() failed: $1")).arg(errno).end();\r
-                       // this error is not fatal, continue!\r
-               }\r
-               // bind it to the port we passed in to getaddrinfo():\r
-               if (bind(m_handleSocket, addrInfo->ai_addr, addrInfo->ai_addrlen) != 0)\r
-                       m_logger->sayF(LOG_ERROR | CAT_NETWORK, LC_LISTEN_FOR_ALL_2,\r
-                           i18n("cannot bind: $1")).arg(errno).end();\r
-               else {\r
-                       //Listen\r
-                       listen(m_handleSocket,\r
-                           m_maxConnections < 16 ? m_maxConnections : 16);\r
-\r
-                       //Accept and incoming connection\r
-                       m_logger->sayF(LOG_INFO | CAT_NETWORK, LC_LISTEN_FOR_ALL_3,\r
-                           i18n("listening on $1...")).arg(m_port).end();\r
-                       int nextId = 1;\r
-                       //Accept and incoming connection\r
-                       int clientSocket;\r
-                       while ((clientSocket = accept(m_handleSocket,\r
-                                       NULL, NULL)) > 0) {\r
-                               if (!m_threadPool.shouldStop()) {\r
-                                       if (m_threadPool.countThreads() >= m_maxConnections) {\r
-                                               // close the connection at once:\r
-                                               m_logger->sayF(LOG_WARNING | CAT_NETWORK,\r
-                                                   LC_LISTEN_FOR_ALL_5,\r
-                                                   i18n(\r
-                                                       "connection refused (too many connections): $1"))\r
-                                                   .arg(m_port).end();\r
-                                               reCloseSocket(clientSocket);\r
-                                       } else {\r
-                                               ReTCPServerConnection* connection = createConnection(\r
-                                                   nextId++, clientSocket);\r
-                                               connection->setLogSendReceive(m_logSendReceive);\r
-                                               if (!m_threadPool.startThread(connection)) {\r
-                                                       m_logger->sayF(LOG_ERROR | CAT_PROCESS,\r
-                                                           LC_LISTEN_FOR_ALL_6,\r
-                                                           i18n("cannot create a thread: $1")).arg(\r
-                                                           getLastOSError()).end();\r
-                                                       reCloseSocket(clientSocket);\r
-                                                       clientSocket = -1;\r
-                                               } else {\r
-\r
-                                               }\r
-                                       }\r
-                               }\r
-                       }\r
-                       if (clientSocket < 0)\r
-                               m_logger->sayF(LOG_ERROR | CAT_PROCESS,\r
-                                                           LC_LISTEN_FOR_ALL_6,\r
-                                                           i18n("accept() failed: $1")).arg(\r
-                                                           getLastOSError()).end();\r
-                       if (m_threadPool.shouldStop()) {\r
-                               m_logger->say(LOG_INFO | CAT_PROCESS, LC_LISTEN_FOR_ALL_7,\r
-                                   i18n("stop request received"));\r
-                       }\r
-               }\r
-       }\r
-       return rc;\r
-}\r
-\r
-/**\r
- * Returns the logger.\r
- *\r
- * @param the logger for error handling\r
- */\r
-ReLogger* ReTCPServer::logger() {\r
-       return m_logger;\r
-}\r
-\r
-/**\r
- * Constructor.\r
- */\r
-ReNetCommandHandler::ReNetCommandHandler() :\r
-           m_nextHandler(NULL) {\r
-}\r
-\r
-/**\r
- * Adds a handler at the end of the handler chain.\r
- */\r
-void ReNetCommandHandler::addHandler(ReNetCommandHandler* handler) {\r
-       if (m_nextHandler == NULL)\r
-               m_nextHandler = handler;\r
-       else\r
-               m_nextHandler->addHandler(handler);\r
-}\r
-\r
-/**\r
- * Constructor.\r
- *\r
- * @param port         port for listening\r
- * @param logger       logger for error handling\r
- */\r
-#pragma warning( push )\r
-#pragma warning( disable : 4355 )\r
-ReTCPEchoServer::ReTCPEchoServer(int port, ReLogger* logger) :\r
-           ReTCPServer(port, *this, logger),\r
-           ReNetCommandHandler() {\r
-#pragma warning( pop )\r
-}\r
-/**\r
- * Destructor.\r
- */\r
-ReTCPEchoServer::~ReTCPEchoServer() {\r
-}\r
-\r
-/**\r
- * Handler for the commands "echo", "localtim" and "stop".\r
- *\r
- * @param command              a string describing what do to\r
- * @param data                 data of the command, may be empty\r
- * @param connection   the connection which can be used for answers\r
- * @result                             PS_UNKNOWN: command is not known<br>\r
- *                                             PS_PROCESSED: command successfully processed<br>\r
- *                                             PS_FAILED: command processed, error occurred<br>\r
- *                                             PS_ABORT: connection should be finished\r
- */\r
-ReNetCommandHandler::ProcessingState ReTCPEchoServer::handleNetCommand(\r
-    ReByteBuffer& command, ReByteBuffer& data, ReTCPConnection* connection) {\r
-       ProcessingState rc = PS_UNDEF;\r
-       if (command.equals("echo    ")) {\r
-               connection->send("Echo", data.str(), data.length());\r
-               rc = PS_PROCESSED;\r
-       } else if (command.equals("strlen  ")) {\r
-               m_toSend.setLength(0).appendInt(data.length());\r
-               connection->send("Strlen  ", m_toSend.str(), m_toSend.length());\r
-               rc = PS_PROCESSED;\r
-       } else if (command.equals("filldata")) {\r
-               int length = atol(data.str());\r
-               if (m_toSend.length() != length || !m_toSend.startsWith("xxxxx"))\r
-                       m_toSend.setLength(0).appendChar('x', length);\r
-               connection->send("Filldata", m_toSend.str(), m_toSend.length());\r
-               rc = PS_PROCESSED;\r
-       } else if (command.equals("localtim")) {\r
-               time_t now2 = time(NULL);\r
-               struct tm* now = localtime(&now2);\r
-               char buffer[128];\r
-               strftime(buffer, sizeof buffer, "%y.%m.%d %H:%M:%S", now);\r
-               connection->send("Localtim", buffer, strlen(buffer));\r
-               rc = PS_PROCESSED;\r
-       } else if (command.equals("stop    ")) {\r
-               rc = PS_STOP;\r
-       } else {\r
-               rc = PS_UNKNOWN;\r
-               if (m_nextHandler != NULL)\r
-                       rc = m_nextHandler->handleNetCommand(command, data, connection);\r
-       }\r
-       return rc;\r
-}\r
-\r
-/**\r
- * Constructor.\r
- */\r
-ReTCPStopClient::ReTCPStopClient(ReLogger* logger) :\r
-           ReTCPClient(logger) {\r
-}\r
-/**\r
- * Destructor.\r
- */\r
-ReTCPStopClient::~ReTCPStopClient() {\r
-}\r
-\r
-void ReTCPStopClient::stopServer(int port, const char* ip) {\r
-       connect(ip, port);\r
-       send("stop", NULL);\r
-}\r
+/*
+ * ReTCP.cpp
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
+ * Do what you want.
+ * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
+ * The latest sources: https://github.com/republib
+ */
+
+#include "base/rebase.hpp"
+#include "net/renet.hpp"
+
+enum LOCATION_DIRTOOL {
+       LC_LISTEN_FOR_ALL_1 = LOC_FIRST_OF(LOC_TCP), // 50501
+       LC_LISTEN_FOR_ALL_2,    // 50502
+       LC_LISTEN_FOR_ALL_3,    // 50503
+       LC_RECEIVE_5,                   // 50504
+       LC_LISTEN_FOR_ALL_5,    // 50505
+       LC_LISTEN_FOR_ALL_6,    // 50506
+       LC_WRITE_1,                     // 50507
+       LC_CONNECT_1,                   // 50508
+       LC_SOCKET_ADDR_SET_1,   // 50509
+       LC_LISTEN_FOR_ALL_7,    // 50510
+       LC_HANDLE_CONNECTION_1, // 50511
+       LC_RECEIVE_1,                   // 50512
+       LC_RECEIVE_2,                   // 50513
+       LC_RECEIVE_3,                   // 50514
+       LC_RECEIVE_4,                   // 50515
+       LC_CONNECT_2,                   // 50516
+       LC_CONNECT_3,                   // 50517
+       LC_TCP_CONNECTION_1,    // 50518
+       LC_WRITE_2,                             // 50519
+       LC_SERVER_CONNECTION_RUN_1,     // 50520
+       LC_SERVER_CONNECTION_RUN_2,     // 50521
+       LC_SERVER_CONNECTION_RUN_3,     // 50522
+       LC_HANDLE_CONNECTION_4, // 50523
+};
+
+#if defined __WIN32__
+bool ReTCPConnection::isGlobalInitialized = false;
+#endif
+
+/**
+ * Constructor.
+ *
+ * @param message      the description of the exception
+ */
+ReTCPException::ReTCPException(const char* message) {
+
+}
+;
+
+/**
+ * Constructor.
+ *
+ * @param socket       the socket which is disconnected
+ */
+ReTCPDisconnectException::ReTCPDisconnectException(int socket) :
+           ReTCPException(i18n("disconnected")),
+           m_socket(socket) {
+}
+
+/**
+ * Constructor.
+ *
+ * @param logger       the logger for error handling
+ */
+ReSocketAddress::ReSocketAddress(ReLoggerOwner* loggerOwner) :
+           m_preferredFamily(AF_INET),
+           m_family(-1),
+           m_port(0),
+           m_loggerOwner(loggerOwner),
+           // m_ip
+           m_name() {
+       memset(&m_ip, 0, sizeof m_ip);
+}
+/**
+ * Destructor.
+ */
+ReSocketAddress::~ReSocketAddress() {
+}
+void addressToString(struct addrinfo& addr, char* ip, size_t ipSize) {
+       if (addr.ai_family == AF_INET) {
+               inet_ntop(addr.ai_family, (struct sockaddr_in *) addr.ai_addr, ip,
+                   ipSize);
+       } else if (addr.ai_family == AF_INET6) {
+               inet_ntop(addr.ai_family, (struct sockaddr_in6 *) addr.ai_addr, ip,
+                   ipSize);
+       }
+}
+/**
+ * Sets the data from symbolic values.
+ */
+void ReSocketAddress::setAddress(const char* ip, int port) {
+       struct addrinfo hints;
+       struct addrinfo* infoList;
+       int status;
+       m_port = port;
+       memset(&hints, 0, sizeof hints);
+       hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version
+       hints.ai_socktype = SOCK_STREAM;
+
+       if ((status = getaddrinfo(ip, NULL, &hints, &infoList)) != 0) {
+               m_loggerOwner->logger()->sayF(LOG_ERROR | CAT_NETWORK,
+                   LC_SOCKET_ADDR_SET_1, i18n("getaddrinfo($1) failed: $2")).arg(ip)
+                   .arg(errno).end();
+       }
+
+       struct addrinfo* ptr;
+       bool hasIP4 = false;
+       bool hasIP6 = false;
+
+       // Search for a available ip address
+       // if more than one are available, prefer m_preferredFamily:
+       m_ip[0] = '\0';
+       for (ptr = infoList; ptr != NULL; ptr = ptr->ai_next) {
+               // different fields in IPv4 and IPv6:
+               if (!hasIP4 && ptr->ai_family == AF_INET) {
+                       m_family = AF_INET;
+                       hasIP4 = true;
+               } else if (!hasIP6 && ptr->ai_family == AF_INET6) {
+                       m_family = AF_INET6;
+                       hasIP6 = true;
+               }
+               addressToString(*ptr, m_ip, sizeof m_ip);
+               if (ptr->ai_family == m_preferredFamily)
+                       break;
+       }
+       // free the linked list
+       freeaddrinfo(infoList);
+       if (m_ip[0] == '\0')
+               m_name.setLength(0);
+       else
+               m_name.set(m_ip).appendChar(':').appendInt(port);
+}
+
+/**
+ * Constructor.
+ *
+ * @param logger       logger for the error handling
+ */
+#pragma warning( push )
+#pragma warning( disable : 4355 )
+ReTCPClient::ReTCPClient(ReLogger* logger) :
+           ReTCPConnection(-1, this),
+           m_logger(logger) {
+#pragma warning( pop )
+}
+/**
+ * Destructor.
+ */
+ReTCPClient::~ReTCPClient() {
+}
+/**
+ * Connects a client with the server.
+ *
+ * @param ip   domain address ("denic.de") or ip address ("192.168.2.1")
+ * @param port port number: 1..65535
+ */
+bool ReTCPClient::connect(const char* ip, int port) {
+       bool rc = false;
+       struct addrinfo hints;
+       struct addrinfo* addr = NULL;
+
+       memset(&hints, 0, sizeof hints);
+       hints.ai_family = AF_UNSPEC;
+       hints.ai_socktype = SOCK_STREAM;
+
+       getaddrinfo(ip, ReByteBuffer("").appendInt(port).str(), &hints, &addr);
+       if (addr == NULL) {
+               m_logger->sayF(LOG_ERROR | CAT_NETWORK, LC_CONNECT_1,
+                   i18n("ip not reachable: $1")).arg(ip).end();
+       } else {
+               addressToString(*addr, m_ip, sizeof m_ip);
+               m_peerName.set(m_ip).appendChar(':').appendInt(port);
+               m_port = port;
+               if ((m_handleSocket = socket(addr->ai_family, addr->ai_socktype,
+                   addr->ai_protocol)) < 0)
+                       m_logger->sayF(LOG_ERROR | CAT_NETWORK, LC_CONNECT_2,
+                           i18n("socket() failed ($1): $2")).arg(errno).arg(m_peerName).end();
+               else if (::connect(m_handleSocket, addr->ai_addr, addr->ai_addrlen)
+                   != 0)
+                       m_logger->sayF(LOG_ERROR | CAT_NETWORK, LC_CONNECT_3,
+                           i18n("connect() failed ($1): $2")).arg(errno).arg(m_peerName)
+                           .end();
+               else
+                       rc = true;
+       }
+       return rc;
+}
+
+/**
+ * Returns the logger.
+ *
+ * @param the logger for error handling
+ */
+ReLogger* ReTCPClient::logger() {
+       return m_logger;
+}
+
+/**
+ * Constructor.
+ *
+ * @param id           an identifier for logging
+ * @param logger       the logger for error handling
+ */
+ReTCPConnection::ReTCPConnection(int id, ReLoggerOwner* loggerOwner) :
+           ReSocketAddress(loggerOwner),
+           m_peerName(),
+           m_received(),
+           m_handleSocket(-1),
+           m_id(id),
+           m_noSent(0),
+           m_noReceived(0),
+           m_logSendReceive(true) {
+#if defined __WIN32__
+       if (! isGlobalInitialized) {
+               WSADATA wsaData;
+               if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) {
+                       loggerOwner->logger()->sayF(LOG_ERROR | CAT_NETWORK, LC_TCP_CONNECTION_1,
+                               i18n("WSAStartup() failed: $1")).arg(errno).arg(getLastOSError()).end();
+                       throw ReException("WSAStartup() failed");
+               }
+               isGlobalInitialized = true;
+       }
+#endif
+}
+
+/**
+ * Destructor.
+ */
+ReTCPConnection::~ReTCPConnection() {
+}
+/**
+ * Finishes the connection (in both directions) and frees the resouces.
+ */
+void ReTCPConnection::close() {
+       if (m_handleSocket >= 0) {
+               reCloseSocket(m_handleSocket);
+               m_handleSocket = -1;
+       }
+}
+
+/**
+ * Frees the global resources.
+ */
+void ReTCPConnection::globalClose() {
+#if defined __WIN32__
+       if (isGlobalInitialized)
+       WSACleanup();
+#endif
+}
+
+/**
+ * Sets the address given by a family and a string like "192.168.0.1:22".
+ *
+ * @param family       AF_INET or AF_INET6
+ * @param ip           the string describing the address, e.g. "192.168.0.1"
+ * @param port         the port of the peer
+ */
+void ReTCPConnection::setConnectedAddress(int family, const char* ip,
+    int port) {
+       m_family = family;
+       m_name = ip;
+       m_port = port;
+       int length = strlen(ip);
+       memcpy(m_ip, ip, length);
+       m_ip[length] = '\0';
+       m_name.appendChar(':').appendInt(port);
+}
+
+/**
+ * Receives a message.
+ *
+ * @param command      OUT: the received command
+ * @param data         OUT: the received data
+ *
+ * @throws ReTCPBrokenPipeException
+ */
+void ReTCPConnection::receive(ReByteBuffer& command, ReByteBuffer& data) {
+       command.setLength(8);
+       int received = recv(m_handleSocket, command.buffer(), 8, 0);
+       if (received == 0) {
+               // connection closed from the peer
+               throw ReTCPDisconnectException(m_handleSocket);
+       } else if (received != 8) {
+               m_loggerOwner->logger()->sayF(LOG_ERROR | CAT_NETWORK, LC_RECEIVE_1,
+                   i18n("cannot receive ($1): $2 [$3]")).arg(errno).arg(received).arg(
+                   m_peerName).end();
+       } else {
+               int flags = 0;
+               int length = 0;
+               int found;
+               if ((found = sscanf(command.str(), "%8x", &length)) != 1
+                   || (flags = (length >> 24)) > 256 || (length &= 0xffffff) < 8) {
+                       m_loggerOwner->logger()->sayF(LOG_ERROR | CAT_NETWORK, LC_RECEIVE_2,
+                           i18n("wrong format: $1 [$2]")).arg(command).arg(m_peerName).end();
+               } else {
+                       data.setLength(length);
+                       int readBytes = 0;
+                       int rest = length;
+                       char* buf = data.buffer();
+                       int rounds = 0;
+                       while (readBytes < length) {
+                               rounds++;
+                               received = recv(m_handleSocket, buf, rest, 0);
+                               if (received > 0) {
+                                       buf += received;
+                                       rest -= received;
+                                       readBytes += received;
+                               } else if (received == 0) {
+                                       data.setLength(readBytes);
+                                       break;
+                               } else {
+                                       m_loggerOwner->logger()->sayF(LOG_ERROR | CAT_NETWORK,
+                                           LC_RECEIVE_3, i18n("cannot receive ($1): $2 [$3]")).arg(
+                                       errno).arg(received).arg(m_peerName).end();
+                                       break;
+                               }
+                       }
+                       if (rounds)
+                               rounds += 0;
+                       if (readBytes < length) {
+                               m_loggerOwner->logger()->sayF(LOG_ERROR | CAT_NETWORK,
+                                   LC_RECEIVE_4, i18n("too few bytes read: $1/$2 [$3]")).arg(
+                                   readBytes).arg(received).arg(m_peerName).end();
+                       } else if (m_logSendReceive) {
+                               m_loggerOwner->logger()->sayF(LOG_DEBUG | CAT_NETWORK,
+                                   LC_RECEIVE_5,
+                                   i18n("received: $1 bytes in $2 round(s) [$3]: $4")).arg(
+                                   length).arg(rounds).arg(m_peerName).arg(
+                                   ReByteBuffer().appendDump(data.str(), data.length(), 80).str())
+                                   .end();
+                       }
+                       command.setLength(0);
+                       if (readBytes >= 8) {
+                               command.append(data.str(), 8);
+                               data.remove(0, 8);
+                       }
+               }
+       }
+}
+/**
+ * Sends a command with (or without) data to the peer.
+ *
+ * @param command      the command to send
+ * @param data         the data to send
+ */
+void ReTCPConnection::send(const char* command, const char* data, int length) {
+       if (data == NULL)
+               length = 0;
+       else if (length < 0)
+               length = strlen(data);
+       m_toSend.ensureSize(length + 16);
+       ++m_noSent;
+       m_toSend.setLength(0);
+       int flags = 0x7b;
+       // add command length:
+       int rest = length + 8;
+       m_toSend.appendInt(rest | (flags << 24), "%08x");
+       // we send the flag/length too:
+       rest += 8;
+       m_toSend.appendFix(command, -1, 8, 8, NULL);
+       m_toSend.append(data, length);
+       int sent = 0;
+       const char* buf = m_toSend.str();
+       int rounds = 0;
+
+       while (rest > 0) {
+               rounds++;
+               if ((sent = ::send(m_handleSocket, buf, rest, 0)) > 0) {
+                       buf += sent;
+                       rest -= sent;
+               } else {
+                       m_loggerOwner->logger()->sayF(LOG_ERROR | CAT_NETWORK, LC_WRITE_1,
+                           i18n("cannot send ($1): $2")).arg(errno).arg(m_peerName).end();
+                       break;
+               }
+       }
+       if (rest == 0 && m_logSendReceive)
+               m_loggerOwner->logger()->sayF(LOG_DEBUG | CAT_NETWORK, LC_WRITE_2,
+                   i18n("sent: $1 bytes in $2 round(s): $3 $4")).arg(length).arg(
+                   rounds).arg(command).arg(
+                   ReByteBuffer().appendDump(data, length, 80).str()).end();
+}
+
+/**
+ * Constructor.
+ *
+ * @param id           an identifier for logging
+ * @param logger       the logger for error handling
+ */
+#pragma warning( push )
+#pragma warning( disable : 4355 )
+ReTCPServerConnection::ReTCPServerConnection(int id, ReTCPServer* server) :
+           ReTCPConnection(id, this),
+           ReThread(true),
+           m_server(server) {
+#pragma warning( pop )
+}
+
+/**
+ * Destructor.
+ */
+ReTCPServerConnection::~ReTCPServerConnection() {
+}
+
+/**
+ * Serves the commands of a single connection (in a single thread).
+ */
+void ReTCPServerConnection::run() {
+       ReByteBuffer command;
+       ReNetCommandHandler::ProcessingState rc = ReNetCommandHandler::PS_UNDEF;
+       m_loggerOwner->logger()->sayF(LOG_INFO | CAT_NETWORK,
+           LC_SERVER_CONNECTION_RUN_1, i18n("new connection to $1")).arg(m_name)
+           .end();
+       try {
+               do {
+                       receive(command, m_received);
+                       rc = m_server->handler().handleNetCommand(command, m_received,
+                           this);
+                       if (rc == ReNetCommandHandler::PS_UNKNOWN) {
+                               logger()->sayF(LOG_ERROR | CAT_NETWORK, LC_HANDLE_CONNECTION_1,
+                                   i18n("unknown command: $1 length: $2")).arg(command).arg(
+                                   m_received.length()).end();
+                       }
+               } while (rc != ReNetCommandHandler::PS_STOP && !m_shouldStop);
+               m_pool->setShouldStop();
+       } catch (ReTCPDisconnectException& e) {
+               rc = ReNetCommandHandler::PS_CLOSED;
+       }
+       close();
+       if (rc == ReNetCommandHandler::PS_CLOSED)
+               logger()->sayF(LOG_INFO | CAT_NETWORK, LC_HANDLE_CONNECTION_4,
+                   i18n("connection closed by the peer $1")).arg(m_peerName).end();
+       else if (rc == ReNetCommandHandler::PS_STOP)
+               m_loggerOwner->logger()->sayF(LOG_INFO | CAT_NETWORK,
+                   LC_SERVER_CONNECTION_RUN_2, i18n("stop signal received from $1"))
+                   .arg(m_name).end();
+       else
+               m_loggerOwner->logger()->say(LOG_INFO | CAT_NETWORK,
+                   LC_SERVER_CONNECTION_RUN_3, i18n("stop order received"));
+       m_id = -1;
+}
+
+/**
+ * Constructor.
+ *
+ * @param port                         the port for listening
+ * @param commandHandler       a handler which can process the incoming commands.
+ *                                                     May be NULL
+ * @param logger                       the logger for error handling
+ * @param maxConnections       maximal count of threads handling a connection
+ */
+#pragma warning( push )
+#pragma warning( disable : 4355 )
+ReTCPServer::ReTCPServer(int port, class ReNetCommandHandler& commandHandler,
+    ReLogger* logger, int maxConnections) :
+           ReTCPConnection(0, this),
+           m_maxConnections(maxConnections),
+           m_handler(commandHandler),
+           m_logger(logger),
+           m_threadPool(maxConnections, logger) {
+#pragma warning( pop )
+       m_port = port;
+}
+
+/**
+ * Destructor.
+ */
+ReTCPServer::~ReTCPServer() {
+       // m_poolConnections does the things!
+}
+/**
+ * Creates a server connection.
+ *
+ * @param id                   the connection identifier
+ * @param handleSocket the handle of the read/write channel
+ * @address                            the data about the client connection (ip, port)
+ */
+ReTCPServerConnection* ReTCPServer::createConnection(int id, int handleSocket) {
+       ReTCPServerConnection* rc = NULL;
+       if (m_threadPool.countThreads() < m_maxConnections) {
+               union {
+                       struct sockaddr address;
+                       uint8_t dummy[32];
+               } u;
+               struct sockaddr& address = u.address;
+               memset(&u, -1, sizeof u);
+               socklen_t sizeAddress = sizeof u; //.address;
+               int rc2 = getpeername(handleSocket, &address, &sizeAddress);
+               if (rc2 != 0)
+                       m_logger->sayF(LOG_ERROR | CAT_NETWORK, LC_LISTEN_FOR_ALL_1,
+                           i18n("getpeername() failed: $1")).arg(getLastOSError()).end();
+               rc = new ReTCPServerConnection(id, this);
+               rc->setId(id);
+               rc->setHandleSocket(handleSocket);
+               char ip[INET6_ADDRSTRLEN];
+               inet_ntop(address.sa_family,
+                   address.sa_family == AF_INET ?
+                       (void *) &(((struct sockaddr_in*) &address)->sin_addr) :
+                       (void *) &(((struct sockaddr_in6*) &address)->sin6_addr), ip,
+                   sizeof ip);
+               int port =
+                   address.sa_family == AF_INET ?
+                       (int) ntohs(((struct sockaddr_in*) &address)->sin_port) :
+                       (int) ntohl(((struct sockaddr_in6*) &address)->sin6_port);
+               rc->setConnectedAddress(address.sa_family, ip, port);
+       }
+       return rc;
+}
+
+/**
+ * Accepts connections and create a thread which will handle this connection.
+ */
+bool ReTCPServer::listenForAll() {
+       bool rc = false;
+       struct addrinfo hints;
+       struct addrinfo* addrInfo;
+
+// first, load up address structs with getaddrinfo():
+       memset(&hints, 0, sizeof hints);
+       hints.ai_family = AF_UNSPEC;  // use IPv4 or IPv6, whichever
+       hints.ai_socktype = SOCK_STREAM;
+       hints.ai_flags = AI_PASSIVE;     // fill in my IP for me
+
+       memcpy(m_ip, "0.0.0.0", 8);
+       m_name.set(m_ip).appendChar(':').appendInt(m_port);
+       getaddrinfo(NULL, ReByteBuffer().appendInt(m_port).str(), &hints,
+           &addrInfo);
+       m_family = addrInfo->ai_family;
+// make a socket:
+       m_handleSocket = socket(addrInfo->ai_family, addrInfo->ai_socktype,
+           addrInfo->ai_protocol);
+       if (m_handleSocket == -1) {
+               m_logger->sayF(LOG_ERROR | CAT_NETWORK, LC_LISTEN_FOR_ALL_1,
+                   i18n("cannot create a socket: $1")).arg(errno).end();
+       } else {
+               int yes = 1;
+               // Avoid the "Address already in use" error message of finished processes
+               // that are still waiting for the release by the kernel:
+               if (setsockopt(m_handleSocket, SOL_SOCKET, SO_REUSEADDR,
+                   reinterpret_cast<const char*>(&yes), sizeof(int)) == -1) {
+                       m_logger->sayF(LOG_WARNING | CAT_NETWORK, LC_LISTEN_FOR_ALL_7,
+                           i18n("setsockopt() failed: $1")).arg(errno).end();
+                       // this error is not fatal, continue!
+               }
+               // bind it to the port we passed in to getaddrinfo():
+               if (bind(m_handleSocket, addrInfo->ai_addr, addrInfo->ai_addrlen) != 0)
+                       m_logger->sayF(LOG_ERROR | CAT_NETWORK, LC_LISTEN_FOR_ALL_2,
+                           i18n("cannot bind: $1")).arg(errno).end();
+               else {
+                       //Listen
+                       listen(m_handleSocket,
+                           m_maxConnections < 16 ? m_maxConnections : 16);
+
+                       //Accept and incoming connection
+                       m_logger->sayF(LOG_INFO | CAT_NETWORK, LC_LISTEN_FOR_ALL_3,
+                           i18n("listening on $1...")).arg(m_port).end();
+                       int nextId = 1;
+                       //Accept and incoming connection
+                       int clientSocket;
+                       while ((clientSocket = accept(m_handleSocket,
+                       NULL, NULL)) > 0) {
+                               if (!m_threadPool.shouldStop()) {
+                                       if (m_threadPool.countThreads() >= m_maxConnections) {
+                                               // close the connection at once:
+                                               m_logger->sayF(LOG_WARNING | CAT_NETWORK,
+                                                   LC_LISTEN_FOR_ALL_5,
+                                                   i18n(
+                                                       "connection refused (too many connections): $1"))
+                                                   .arg(m_port).end();
+                                               reCloseSocket(clientSocket);
+                                       } else {
+                                               ReTCPServerConnection* connection = createConnection(
+                                                   nextId++, clientSocket);
+                                               connection->setLogSendReceive(m_logSendReceive);
+                                               if (!m_threadPool.startThread(connection)) {
+                                                       m_logger->sayF(LOG_ERROR | CAT_PROCESS,
+                                                           LC_LISTEN_FOR_ALL_6,
+                                                           i18n("cannot create a thread: $1")).arg(
+                                                           getLastOSError()).end();
+                                                       reCloseSocket(clientSocket);
+                                                       clientSocket = -1;
+                                               } else {
+
+                                               }
+                                       }
+                               }
+                       }
+                       if (clientSocket < 0)
+                               m_logger->sayF(LOG_ERROR | CAT_PROCESS, LC_LISTEN_FOR_ALL_6,
+                                   i18n("accept() failed: $1")).arg(getLastOSError()).end();
+                       if (m_threadPool.shouldStop()) {
+                               m_logger->say(LOG_INFO | CAT_PROCESS, LC_LISTEN_FOR_ALL_7,
+                                   i18n("stop request received"));
+                       }
+               }
+       }
+       return rc;
+}
+
+/**
+ * Returns the logger.
+ *
+ * @param the logger for error handling
+ */
+ReLogger* ReTCPServer::logger() {
+       return m_logger;
+}
+
+/**
+ * Constructor.
+ */
+ReNetCommandHandler::ReNetCommandHandler() :
+           m_nextHandler(NULL) {
+}
+
+/**
+ * Adds a handler at the end of the handler chain.
+ */
+void ReNetCommandHandler::addHandler(ReNetCommandHandler* handler) {
+       if (m_nextHandler == NULL)
+               m_nextHandler = handler;
+       else
+               m_nextHandler->addHandler(handler);
+}
+
+/**
+ * Constructor.
+ *
+ * @param port         port for listening
+ * @param logger       logger for error handling
+ */
+#pragma warning( push )
+#pragma warning( disable : 4355 )
+ReTCPEchoServer::ReTCPEchoServer(int port, ReLogger* logger) :
+           ReTCPServer(port, *this, logger),
+           ReNetCommandHandler() {
+#pragma warning( pop )
+}
+/**
+ * Destructor.
+ */
+ReTCPEchoServer::~ReTCPEchoServer() {
+}
+
+/**
+ * Handler for the commands "echo", "localtim" and "stop".
+ *
+ * @param command              a string describing what do to
+ * @param data                 data of the command, may be empty
+ * @param connection   the connection which can be used for answers
+ * @result                             PS_UNKNOWN: command is not known<br>
+ *                                             PS_PROCESSED: command successfully processed<br>
+ *                                             PS_FAILED: command processed, error occurred<br>
+ *                                             PS_ABORT: connection should be finished
+ */
+ReNetCommandHandler::ProcessingState ReTCPEchoServer::handleNetCommand(
+    ReByteBuffer& command, ReByteBuffer& data, ReTCPConnection* connection) {
+       ProcessingState rc = PS_UNDEF;
+       if (command.equals("echo    ")) {
+               connection->send("Echo", data.str(), data.length());
+               rc = PS_PROCESSED;
+       } else if (command.equals("strlen  ")) {
+               m_toSend.setLength(0).appendInt(data.length());
+               connection->send("Strlen  ", m_toSend.str(), m_toSend.length());
+               rc = PS_PROCESSED;
+       } else if (command.equals("filldata")) {
+               int length = atol(data.str());
+               if (m_toSend.length() != length || !m_toSend.startsWith("xxxxx"))
+                       m_toSend.setLength(0).appendChar('x', length);
+               connection->send("Filldata", m_toSend.str(), m_toSend.length());
+               rc = PS_PROCESSED;
+       } else if (command.equals("localtim")) {
+               time_t now2 = time(NULL);
+               struct tm* now = localtime(&now2);
+               char buffer[128];
+               strftime(buffer, sizeof buffer, "%y.%m.%d %H:%M:%S", now);
+               connection->send("Localtim", buffer, strlen(buffer));
+               rc = PS_PROCESSED;
+       } else if (command.equals("stop    ")) {
+               rc = PS_STOP;
+       } else {
+               rc = PS_UNKNOWN;
+               if (m_nextHandler != NULL)
+                       rc = m_nextHandler->handleNetCommand(command, data, connection);
+       }
+       return rc;
+}
+
+/**
+ * Constructor.
+ */
+ReTCPStopClient::ReTCPStopClient(ReLogger* logger) :
+           ReTCPClient(logger) {
+}
+/**
+ * Destructor.
+ */
+ReTCPStopClient::~ReTCPStopClient() {
+}
+
+void ReTCPStopClient::stopServer(int port, const char* ip) {
+       connect(ip, port);
+       send("stop", NULL);
+}
index 67f20f9eaced6b7ef346d46b6c3e7766f7a2cb3b..85d034754a7b005c7e28e0e9b891346f8a2919eb 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * ReTCP.hpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
index 77a298949d511db83e33aed57e7c6307498a4840..a113c916b37d537de1093638e2277f4c1baa4bf7 100644 (file)
@@ -1,12 +1,29 @@
 /*
- * ReUdpServer.cpp
- *
- *  Created on: 12.11.2010
- *      Author: wk
+ * ReUdpConnection.cpp
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
+ * Do what you want.
+ * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
+ * The latest sources: https://github.com/republib
  */
 #include "base/rebase.hpp"
 #include "net/renet.hpp"
 
+enum RELOC_UDPCONNECTION {
+       LC_UDPCONNECTION_CONSTRUCT = LOC_FIRST_OF(LOC_UDP),
+       LC_UDPCONNECTION_RECEIVE_1, // 10802
+       LC_UDPCONNECTION_RECEIVE_2, // 10803
+       LC_UDPCONNECTION_SEND_1, // 10804
+       LC_UDPCONNECTION_SEND_2, // 10805
+       LC_UDPCONNECTION_CLOSE_1, // 10806
+       LC_UDPCONNECTION_CLOSE_2, // 10807
+       LC_UDPCONNECTION_CONNECT_1, // 10808
+       LC_UDPCONNECTION_CONNECT_2, // 10809
+       LC_UDPCONNECTION_RUN_1, // 10810
+};
+
 /** @brief Constructor.
  *
  * param verbose       TRUE:
index bc2787cfc45079cb13039d31f44cacf9b2f0ae62..fd27d195e2a80741cf3bdd70d1e0615f6d82131e 100644 (file)
@@ -1,8 +1,12 @@
 /*
- * ReUdpServer.h
- *
- *  Created on: 12.11.2010
- *      Author: wk
+ * ReUdpConnection.hpp
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
+ * Do what you want.
+ * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
+ * The latest sources: https://github.com/republib
  */
 
 #ifndef UDPSERVER_H_
index 0ccc8c04ef4bb1319d5fddee40aebf9b0f6e7b2a..11bf45deabbbe3debf2c0c5e3a543083dd4ad802 100644 (file)
@@ -1,24 +1,25 @@
-/*\r
- * renet.hpp\r
- *\r
- * License: Public domain\r
- * Do what you want.\r
- * No warranties and disclaimer of any damages.\r
- * The latest sources: https://github.com/republib\r
- *\r
- */\r
-\r
-#ifndef NET_RENET_HPP_\r
-#define NET_RENET_HPP_\r
-\r
-#if defined __linux__\r
-#include <arpa/inet.h>\r
-#include <netinet/in.h>\r
-#include <sys/socket.h>\r
-#include <netdb.h>\r
-#elif defined __WIN32__\r
-#include <ws2tcpip.h>\r
-#endif\r
-#include "net/ReUdpConnection.hpp"\r
-#include "net/ReTCP.hpp"\r
-#endif /* NET_RENET_HPP_ */\r
+/*
+ * renet.hpp
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
+ * Do what you want.
+ * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
+ * The latest sources: https://github.com/republib
+ */
+
+#ifndef NET_RENET_HPP_
+#define NET_RENET_HPP_
+
+#if defined __linux__
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#elif defined __WIN32__
+#include <ws2tcpip.h>
+#endif
+#include "net/ReUdpConnection.hpp"
+#include "net/ReTCP.hpp"
+#endif /* NET_RENET_HPP_ */
index 77828ac17f4157401c151f074a3aef2d1a1c2b7c..f802f7296bb303a0b1dbe8d22cd928d27e09e1d2 100644 (file)
-/*\r
- * ReDirTools.cpp\r
- *\r
- * License: Public domain\r
- * Do what you want.\r
- * No warranties and disclaimer of any damages.\r
- * The latest sources: https://github.com/republib\r
- */\r
-\r
-#include "base/rebase.hpp"\r
-#include "math/remath.hpp"\r
-#include "os/reos.hpp"\r
-#include "net/renet.hpp"\r
-\r
-enum LOCATION_DIRTOOL {\r
-       LC_COPY_FILE_1 = LC_DIRTOOLS + 1, // 50101\r
-       LC_COPY_FILE_2, // 50102\r
-       LC_COPY_FILE_3, // 50103\r
-       LC_COPY_FILE_4, // 50104\r
-       LC_COPY_FILE_5, // 50105\r
-       LC_COPY_FILE_6, // 50106\r
-       LC_MAKE_DIR_1,  // 50107\r
-       LC_MAKE_DIR_2,  // 50108\r
-       LC_SET_PROPERTIES_1,    // 50109\r
-       LC_SET_PROPERTIES_2,    // 50110\r
-       LC_SET_PROPERTIES_3,    // 50111\r
-       LC_TOUCH_1,                             // 50112\r
-       LC_CALCULATE_CHECKSUM_1,        // 50113\r
-       LC_BUILD_DIRECTORY_1,           // 50114\r
-       LC_COMPARE_STORAGE_1,           // 50115\r
-       LC_COMPARE_STORAGE_2,           // 50116\r
-       LC_COMPARE_STORAGE_3,           // 50117\r
-       LC_COMPARE_STORAGE_4,           // 50118\r
-       LC_COMPARE_STORAGE_5,           // 50119\r
-       LC_COMPARE_STORAGE_6,           // 50120\r
-       LC_COMPARE_STORAGE_7,           // 50121\r
-       LC_COMPARE_DIR_1,                       // 50122\r
-       LC_DELETE_1,                            // 50123\r
-};\r
-const char* ReDirTools::m_version = "2015.03.22";\r
-ReLogger* ReDirTools::m_logger = NULL;\r
-\r
-static const char* s_helpSummary[] = { "dirtool or dt <command> <opts>",\r
-    "   Useful commands around directory trees.",\r
-    "   Type 'dirtool help <command>' for more help.", "<command>:",\r
-    "batch         produce output to handle the found files with a script",\r
-    "checksum      shows the checksum (MD5 or other) of the selected files",\r
-    "delete        delete the selected files",\r
-    "help          shows info about the arguments/options",\r
-    "list          shows the meta data of the selected files",\r
-    "random        displays random numbers",\r
-    "statistic     shows statistics about a direcctory tree",\r
-    "synchronize   copies only modified or new files from",\r
-    "              from a source directory tre to a target",\r
-    "touch         sets the filetimes",\r
-    "which         finds a file in a path list (like PATH)",\r
-    NULL };\r
-\r
-const char* s_batchUsage[] = {\r
-    "<command>: batch [<opts>] <dir_or_file1> [<dir_or_file2> ...]",\r
-    "   produces output usable for a batch file (script)",\r
-    "   all found files can be processed with a given script",\r
-    "   each line starts (usually) with a script name (see -c)",\r
-    "   then it follows the full filename of the found file",\r
-    "   use --arguments or --script to configure the output line",\r
-    NULL };\r
-const char* s_deleteExamples[] = {\r
-    "dirtool delete --basename-pattern=;*~;*.bak e:\\data",\r
-    "dirtool delete /tmp/log",\r
-    NULL };\r
-\r
-const char* s_deleteUsage[] = {\r
-    "<command>: delete [<opts>] <dir_or_file1> [<dir_or_file2> ...]",\r
-    "   delete the specified files",\r
-    NULL };\r
-const char* s_batchExamples[] = {\r
-    "dirtool batch -cbackup.bat --basename-pattern=;*.txt;*.doc e:\\data",\r
-    "dirtool batch --type=r '--arguments=backup.sh !basename! !path!' usr etc",\r
-    NULL };\r
-\r
-const char* s_listUsage[] = {\r
-    "<command>: l(ist) [<opts>] <dir_or_file1> [<dir_or_file2> ...]",\r
-    "   lists the metadata (size, modification date ...) of the selected files",\r
-    NULL };\r
-const char* s_listExamples[] =\r
-    { "dirtool list --min-size=10M e:\\data",\r
-        "dirtool li --type=f -y7d --size=10M -p;*.cpp;*.hpp;Makefile*;-*~ /home/data",\r
-        NULL };\r
-\r
-const char* s_checksumUsage[] =\r
-    { "<command>: c(hecksum) [<opts>] <dir_or_file1> [<dir_or_file2> ...]",\r
-        "   shows a check sum of the given files or manages a checksum storage file in each directory for observing",\r
-        NULL };\r
-const char* s_checksumExamples[] = { "dirtool ch --buffer-size=512 e:\\data",\r
-    "dirtool check -cList -p;*.iso /home/iso /down/debian.iso",\r
-    "dirtool checksum -cBuild -aMD5 /home",\r
-    "dirtool checksum --command=Compare /home",\r
-    "dirtool checksum -cUpdate -O/var/log/checksum.log /home",\r
-    NULL };\r
-const char* s_randomUsage[] = { "<command>: r(andom) <opts> count until [from]",\r
-    "  displays random numbers.", "  <count> count of generated numbers",\r
-    "  <until> the maximum (including) value of the numbers",\r
-    "  <from>  the minimum (including) value of the numbers. Default: 1",\r
-    NULL };\r
-const char* s_randomExamples[] = { "dirtool ra --multiple -w3 10 33",\r
-    "dirtool rand -s 6 49", "dirtool rand --sort --width=1 5 50",\r
-    "dirtool rand --sort --width=1 2 10",\r
-    NULL };\r
-\r
-static const char* s_statisticUsage[] =\r
-    { "<command>: st(atistic)  [<opts>] <path1> [<path2> ...] [<depth>]",\r
-        "          shows a statistic about a directory tree",\r
-        "<path>    a directory path: relative or absolute",\r
-        "<depth>   0: only the summary of <path> will be shown",\r
-        "          1: shows the summery of each subdir of <path> and the total",\r
-        "          n: shows the summery of each subdir until level <n> and the total",\r
-        "          default: 1", "<opts_stat>:",\r
-        NULL };\r
-const char* s_statisticExamples[] =\r
-    { "dirtool st -q -t0 e:\\windows",\r
-        "dirtool statistic --quiet --kbyte --trace-interval=60 ../mail ../remember 2",\r
-        "dirtool stat -q --kbyte d:data 2",\r
-        NULL };\r
-\r
-const char* s_syncUsage[] =\r
-    { "<command>: sy(nchronize) <opts> <source1> [<source2> ...] <target>",\r
-        "   Synchronizes the content of a directory tree with another.",\r
-        "   Newer or missing files will be copied",\r
-        "   If a source name ends with the separator (/ in linux, \\ in win) the target",\r
-        "   will be completed with the basename of the source.", "   Example:",\r
-        "   'sync home backup' copies the file home/x.txt to backup/home/x.txt",\r
-        "   'sync data/ backup/data2' copies the file home/x.txt to backup/data2/x.txt",\r
-        NULL };\r
-const char* s_syncExamples[] = {\r
-    "dirtool sync --basename-pattern=;*.txt;*.doc e:\\data\\ d:\\backup\\data2",\r
-    "dirtool sync --type=r --max-size=1G usr etc /media/backup",\r
-    NULL };\r
-\r
-const char* s_tcpUsage[] = {\r
-    "<command>: tcp [<opts>] <subcommand> [<param> ...]",\r
-    "   test tool for network test", "<subcommand>:", "   server",\r
-    "   client <ip> [<direction> [<rounds> [<print_interval>]]]",\r
-    "   <ip>: URL of the server", "   <rounds>: number of messages to send",\r
-    "   <direction>: 'upload', 'download' or 'mixed'",\r
-    NULL };\r
-const char* s_tcpExamples[] = { "dirtool tcp -p 5555 server",\r
-    "dirtool tcp -p 5555 client localhost download 10000 10",\r
-    "dirtool tcp -p 5555 --buffer-size=1024 client 192.168.7.3 upload 10 25",\r
-    NULL };\r
-\r
-const char* s_touchUsage[] =\r
-    { "<command>: touch [<opts>] <dir_or_file1> [<dir_or_file2> ...]",\r
-        "   sets the filetimes (modification and/or access time) of the selected files",\r
-        NULL };\r
-const char* s_touchExamples[] = {\r
-    "dirtool touch -p;*.csv -m2014.12.1/8:12 e:\\data e:\\history\\readme.txt",\r
-    "dirtool touch -p;*.cpp --modified=now-4d12H /home/data",\r
-    "dirtool touch -p;*.csv -m+1m . e:\\data e:\\history\\readme.txt",\r
-    NULL };\r
-\r
-const char* s_whichUsage[] = {\r
-    "<command>: w(hicht) [<opts>] <file_or_pattern1> [<file_or_pattern2> ...]",\r
-    "   finds a file in a path list (like PATH)",\r
-    NULL };\r
-const char* s_whichExamples[] = {\r
-    "dirtool which --list=JAVA_CLASSPATH org.jdom.jar *.jdom*.jar",\r
-    "dirtool which find.exe",\r
-    NULL };\r
-/**\r
- * Constructor.\r
- *\r
- * @param usage     a string vector with a message how to use the command\r
- * @param example   a string vector with some examples how to use the command\r
- */\r
-ReDirOptions::ReDirOptions(const char* usage[], const char* examples[]) :\r
-           ReTraceUnit(INT_MAX, INT_MAX),\r
-           m_programArgs(usage, examples),\r
-           m_nodePatterns(),\r
-           m_pathPatterns(),\r
-           m_compoundUsage(NULL),\r
-           m_countCompoundUsage(0),\r
-           m_output(stdout),\r
-           m_verboseLevel(V_NORMAL) {\r
-       m_nodePatterns.setIgnoreCase(true);\r
-       m_pathPatterns.setIgnoreCase(true);\r
-}\r
-/**\r
- * Destructor.\r
- */\r
-ReDirOptions::~ReDirOptions() {\r
-       close();\r
-       delete[] m_compoundUsage;\r
-}\r
-/**\r
- * Adds a usage component to the compound usage message list.\r
- * @param usage     a string vector containing a part of the usage message\r
- */\r
-void ReDirOptions::addCompoundUsage(const char** usage) {\r
-       int start = 0;\r
-       while (m_compoundUsage[start] != NULL) {\r
-               if (++start >= m_countCompoundUsage)\r
-                       assert(false);\r
-       }\r
-       for (int ix = 0; usage[ix] != NULL; ix++) {\r
-               if (start + ix > m_countCompoundUsage) {\r
-                       assert(false);\r
-                       break;\r
-               }\r
-               m_compoundUsage[start + ix] = usage[ix];\r
-       }\r
-}\r
-\r
-/**\r
- * Adds the standard filter options.\r
- */\r
-void ReDirOptions::addStandardFilterOptions() {\r
-       // standard short options: D d O o P p T t v y Z z\r
-       m_programArgs.addInt("maxdepth",\r
-           i18n("the depth of the subdirectory is lower or equal <number>\n"\r
-                   "0: search is done only in the base directory"), 'D', "max-depth",\r
-           512);\r
-       m_programArgs.addInt("mindepth",\r
-           i18n("the depth of the subdirectory is greater or equal <number>\n"\r
-                   "0: search is done in all subdirectories"), 'd', "min-depth", 0);\r
-       m_programArgs.addString("output", i18n("the name of the output file.\n"\r
-               "The output will written to this file instead of stdout"), 'O',\r
-           "output-file", false, NULL);\r
-       m_programArgs.addString("older",\r
-           i18n(\r
-               "the modification date is older than <string>\n"\r
-                       "<string> is a date (e.g. 2015.02.17) or number followed by an unit\n"\r
-                       "units: m(inutes) h(hours), d(days). Default: m(inutes)\n"\r
-                       "examples: -o25 --older-than=30d -o24h -o2009.3.2/12:00 -o1999.01.01"),\r
-           'o', "older-than", false, NULL);\r
-       m_programArgs.addString("pathpattern",\r
-           i18n(\r
-               "a list of patterns for the path (without basename)\n"\r
-                       "the separator is the first character of the list\n"\r
-                       "Each pattern can contain '*' as wildcard\n"\r
-                       "If the first character is '-' the pattern is a 'not pattern':\n"\r
-                       "A directory will be entered if at least one of the positive patterns\n"\r
-                       "and none of the 'not patterns' matches\n"\r
-                       "examples:\n"\r
-                       "';music;pic*' enters music and xy/Music and PIC and pictures but not xy/pic and img\n"\r
-                       "';*;-.git;.hg' ignores .git and xy/z/.git and .ht"), 'P',\r
-           "path-pattern", false, NULL);\r
-       m_programArgs.addString("nodepattern",\r
-           i18n(\r
-               "a list of patterns for the basename (name without path) separated by ';'\n"\r
-                       "Each pattern can contain '*' as wildcard\n"\r
-                       "If the first character is '-' the pattern is a 'not pattern':\n"\r
-                       "A file will be found if at least one of the positive patterns and none\n"\r
-                       "of the 'not patterns' matches\n"\r
-                       "examples: '*.cpp;*.hpp;Make*' '*;-*.bak;-*~"), 'p',\r
-           "basename-pattern", false, NULL);\r
-       m_programArgs.addString("verbose",\r
-           i18n(\r
-               "verbose level: 0: no info, 1: summary only, 2: normal, 3: chatter mode, 4: debug"),\r
-           'v', "verbose", false, "1");\r
-       m_programArgs.addInt("trace",\r
-           i18n("all <number> seconds the current path will be traced\n"\r
-                   "0: no trace"), 'T', "trace-interval", 0);\r
-       m_programArgs.addString("type", i18n("the file type\n"\r
-               "<string> is a list of <v> values:\n"\r
-               "<v>: b(lock) c(har) d(irectory) (l)i(nkdir) l(ink) o(ther)\n"\r
-               "     p(ipe) s(ocket) r(egular)\n"\r
-               "<v>-sets: S(pecial)=bcspo N(ondir)=Slr\n"\r
-               "examples: -td --type=dr -tNi"), 't', "type", false, NULL);\r
-       m_programArgs.addString("younger",\r
-           i18n(\r
-               "the modification date is younger than <string>\n"\r
-                       "<string> is a date (e.g. 2015.02.17) or number followed by an unit\n"\r
-                       "units: m(inutes) h(hours), d(days). Default: m(inutes)"), 'y',\r
-           "younger-than", false, NULL);\r
-       m_programArgs.addString("maxsize",\r
-           i18n(\r
-               "the filesize is greater or equal <string>\n"\r
-                       "<string> is a number followed by an unit\n"\r
-                       "units: b(yte) k(Byte) K(iByte) m(Byte), M(iByte), g(Byte) G(iByte)\n"\r
-                       "examples: -Z50m --max-size=1G"), 'Z', "max-size", false, NULL);\r
-       m_programArgs.addString("minsize",\r
-           i18n(\r
-               "the filesize is greater or equal <string>\n"\r
-                       "<string> is a number followed by an unit\n"\r
-                       "units: b(yte) k(Byte) K(iByte) m(Byte), M(iByte), g(Byte) G(iByte)\n"\r
-                       "examples: -z50m --min-size=1G"), 'z', "min-size", false, NULL);\r
-}\r
-\r
-/**\r
- * Checks whether the given value is a time expression.\r
- *\r
- * Possible: <date> <date_time> <n><unit>\r
- * Units: m(inutes) h(our) d(ays) s(econds)\r
- *\r
- * @param value     value to check\r
- * @return          the value converted into the absolute (or relative) time\r
- * @throws          ReOptionExecption\r
- */\r
-ReFileTime_t ReDirOptions::checkDate(const char* value) {\r
-       ReByteBuffer theValue(value, -1);\r
-       time_t rcTime;\r
-       if (theValue.count(".") == 2) {\r
-               // a date:\r
-               int year, month, day;\r
-               int hour = 0;\r
-               int minute = 0;\r
-               int sec = 0;\r
-               switch (sscanf(value, "%d.%d.%d/%d:%d:%d", &year, &month, &day, &hour,\r
-                   &minute, &sec)) {\r
-               case 3:\r
-               case 4:\r
-               case 5:\r
-               case 6: {\r
-                       if (year < 1980)\r
-                               throw ReOptionException(&m_programArgs,\r
-                                   i18n("date < 1980.01.01: $1"), value);\r
-                       struct tm time;\r
-                       memset(&time, 0, sizeof time);\r
-                       time.tm_year = year - 1900;\r
-                       time.tm_mon = month - 1;\r
-                       time.tm_mday = day;\r
-                       time.tm_hour = hour;\r
-                       time.tm_min = minute;\r
-                       time.tm_sec = sec;\r
-                       rcTime = mktime(&time);\r
-                       break;\r
-               }\r
-               default:\r
-                       throw ReOptionException(&m_programArgs,\r
-                           i18n(\r
-                               "invalid date/date-time value: $1 yyyy.mm.dd/hh:MM expected"),\r
-                           value);\r
-               }\r
-       } else if (theValue.count(":") >= 1) {\r
-               // a time:\r
-               int hour = 0;\r
-               int minute = 0;\r
-               int sec = 0;\r
-               switch (sscanf(value, "%d:%d:%d", &hour, &minute, &sec)) {\r
-               case 2:\r
-               case 3: {\r
-                       // the time (today)\r
-                       rcTime = time(NULL) / 86400 * 86400 + hour * 3600 + minute * 60\r
-                           + sec;\r
-                       break;\r
-               }\r
-               default:\r
-                       throw ReOptionException(&m_programArgs,\r
-                           i18n("invalid time value: $1. HH:MM:SS expected"), value);\r
-               }\r
-       } else {\r
-               // a time distance value:\r
-               char unit = 'm';\r
-               int count = 0;\r
-               int negativeFactor = 1;\r
-               bool fromNow = theValue.startsWith("now");\r
-               if (fromNow) {\r
-                       theValue.remove(0, 3);\r
-               }\r
-               char cc = theValue.at(0);\r
-               if (cc == '-' || cc == '+') {\r
-                       if (cc == '-')\r
-                               negativeFactor = -1;\r
-                       theValue.remove(0, 1);\r
-               }\r
-               switch (sscanf(theValue.str(), "%d%c", &count, &unit)) {\r
-               case 1:\r
-               case 2:\r
-                       switch (unit) {\r
-                       case 's':\r
-                               break;\r
-                       case 'm':\r
-                               count *= 60;\r
-                               break;\r
-                       case 'h':\r
-                               count *= 60 * 60;\r
-                               break;\r
-                       case 'd':\r
-                               count *= 24 * 60 * 60;\r
-                               break;\r
-                       default:\r
-                               throw ReOptionException(&m_programArgs,\r
-                                   i18n(\r
-                                       "invalid unit $1. expected: s(econds) m(inutes) h(ours) d(ays)"),\r
-                                   value);\r
-                       }\r
-                       rcTime = count * negativeFactor;\r
-                       break;\r
-               default:\r
-                       throw ReOptionException(&m_programArgs,\r
-                           i18n(\r
-                               "invalid relative time value $1 (<number><unit> expected). <unit>: s m h d"),\r
-                           value);\r
-               }\r
-               if (fromNow)\r
-                       rcTime += time(NULL);\r
-       }\r
-       ReFileTime_t rc;\r
-       ReDirStatus_t::timeToFiletime(rcTime, rc);\r
-       return rc;\r
-}\r
-/**\r
- * Checks whether the given value is a time expression.\r
- *\r
- * Possible: <date> <date_time> <n><unit>\r
- * Units: m(inutes) h(our) d(ays)\r
- *\r
- * @param value     value to check\r
- * @return          the value (multiplied with the unit factor)\r
- * @throws          ReOptionExecption\r
- */\r
-int64_t ReDirOptions::checkSize(const char* value) {\r
-       int64_t rc = 0;\r
-       char unit = 'b';\r
-       switch (sscanf(value, "%lld%c", (long long int*) &rc, &unit)) {\r
-       case 1:\r
-       case 2:\r
-               switch (unit) {\r
-               case 'b':\r
-                       break;\r
-               case 'k':\r
-                       rc *= 1000;\r
-                       break;\r
-               case 'K':\r
-                       rc *= 1024;\r
-                       break;\r
-               case 'm':\r
-                       rc *= 1000 * 1000;\r
-                       break;\r
-               case 'M':\r
-                       rc *= 1024 * 1024;\r
-                       break;\r
-               case 'g':\r
-                       rc *= 1000LL * 1000 * 1000;\r
-                       break;\r
-               case 'G':\r
-                       rc *= 1024LL * 1024 * 1024;\r
-                       break;\r
-               default:\r
-                       throw ReOptionException(&m_programArgs,\r
-                           i18n("invalid <unit>: $1. Expected: b k K m M g G"), value);\r
-               }\r
-               break;\r
-       default:\r
-               throw ReOptionException(&m_programArgs,\r
-                   i18n(\r
-                       "invalid size value: $1 expected: <number><unit> <unit>: b k K m M g G"),\r
-                   value);\r
-       }\r
-       return rc;\r
-}\r
-/**\r
- * Checks whether the given value is a valid pattern list.\r
- *\r
- * The first character must be a separator (not '*', '.'  and not a letter)\r
- *\r
- * @param value     value to check\r
- * @return          the <cpde>value</code> (for chaining)\r
- * @throws          ReOptionExecption\r
- */\r
-const char* ReDirOptions::checkPatternList(const char* value) {\r
-       if (isalnum(*value) || *value == '_' || *value == '*' || *value == '.'\r
-           || *value == '-')\r
-               throw ReOptionException(&m_programArgs,\r
-                   i18n("invalid separator (first character): $1 use ';' instead"),\r
-                   value);\r
-       if (strchr(value, OS_SEPARATOR_CHAR) != NULL)\r
-               throw ReOptionException(&m_programArgs,\r
-                   i18n("slash not allowed in pattern list: $1"), value);\r
-       return value;\r
-}\r
-/**\r
- * Checks whether the given value is a valid filetype list.\r
- *\r
- * @param value     value to check\r
- * @return          the bitmask\r
- * @throws          ReOptionExecption\r
- */\r
-ReDirStatus_t::Type_t ReDirOptions::checkType(const char* value) {\r
-       int rc = ReDirStatus_t::TF_UNDEF;\r
-       while (*value != '\0') {\r
-               switch (*value) {\r
-               case 'b':\r
-                       rc |= ReDirStatus_t::TF_BLOCK;\r
-                       break;\r
-               case 'c':\r
-                       rc |= ReDirStatus_t::TF_CHAR;\r
-                       break;\r
-               case 'd':\r
-                       rc |= ReDirStatus_t::TF_SUBDIR;\r
-                       break;\r
-               case 'i':\r
-                       rc |= ReDirStatus_t::TF_LINK_DIR;\r
-                       break;\r
-               case 'l':\r
-                       rc |= ReDirStatus_t::TF_LINK;\r
-                       break;\r
-               case 'o':\r
-                       rc |= ReDirStatus_t::TF_OTHER;\r
-                       break;\r
-               case 'p':\r
-                       rc |= ReDirStatus_t::TF_PIPE;\r
-                       break;\r
-               case 's':\r
-                       rc |= ReDirStatus_t::TF_SOCKET;\r
-                       break;\r
-               case 'r':\r
-                       rc |= ReDirStatus_t::TF_REGULAR;\r
-                       break;\r
-               case 'S':\r
-                       rc |= ReDirStatus_t::TC_SPECIAL;\r
-                       break;\r
-               case 'N':\r
-                       rc |= ReDirStatus_t::TC_NON_DIR;\r
-                       break;\r
-               case ' ':\r
-               case ',':\r
-                       break;\r
-               default:\r
-                       throw ReOptionException(&m_programArgs,\r
-                           i18n("invalid type: $1 Expected: b(lock) c(har) d(irectory)"\r
-                                   " (l)i(nkdir) l(ink) o(ther) p(ipe) s(ocket) r(egular)"\r
-                                   " S(pecial=bcspo) N(ondir=Slr)"), value);\r
-               }\r
-               value++;\r
-       }\r
-       return (ReDirStatus_t::Type_t) rc;\r
-}\r
-\r
-/**\r
- * Prints a help message, the error message and exits.\r
- *\r
- * @param errorMessage  the error message.\r
- * @param message2      an additional message\r
- */\r
-\r
-void ReDirOptions::help(const char* errorMessage, const char* message2) const {\r
-       ReByteBuffer msg;\r
-       if (errorMessage != 0)\r
-               msg.append(errorMessage, -1);\r
-       if (message2 != NULL)\r
-               msg.append(message2, -1);\r
-       m_programArgs.help(msg.str(), false, stdout);\r
-       exit(1);\r
-}\r
-\r
-/**\r
- * Checks the correctness of the standard filter options.\r
- * \r
- * @throws \r
- */\r
-void ReDirOptions::checkStandardFilterOptions() {\r
-       ReByteBuffer buffer;\r
-       if (m_programArgs.getString("older", buffer)[0] != '\0')\r
-               checkDate(buffer.str());\r
-       if (m_programArgs.getString("younger", buffer)[0] != '\0')\r
-               checkDate(buffer.str());\r
-       if (m_programArgs.getString("type", buffer)[0] != '\0')\r
-               checkType(buffer.str());\r
-       if (m_programArgs.getString("maxsize", buffer)[0] != '\0')\r
-               checkSize(buffer.str());\r
-       if (m_programArgs.getString("minsize", buffer)[0] != '\0')\r
-               checkSize(buffer.str());\r
-       if (m_programArgs.getString("nodepattern", buffer)[0] != '\0')\r
-               checkPatternList(buffer.str());\r
-       if (m_programArgs.getString("pathpattern", buffer)[0] != '\0')\r
-               checkPatternList(buffer.str());\r
-       if (m_programArgs.getString("verbose", buffer)[0] != '\0') {\r
-               unsigned level = V_NORMAL;\r
-               if (ReStringUtils::lengthOfUnsigned(buffer.str(), -1, &level)\r
-                   != (int) buffer.length())\r
-                       help(i18n("verbose level is not a number (or '')"), buffer.str());\r
-               else\r
-                       m_verboseLevel = VerboseLevel(level);\r
-       }\r
-}\r
-\r
-/**\r
- * Frees the resources.\r
- */\r
-void ReDirOptions::close() {\r
-       if (m_output != stdout && m_output != stderr) {\r
-               fclose(m_output);\r
-       }\r
-       m_output = stdout;\r
-}\r
-\r
-/**\r
- * Initializes the compound usage message array.\r
- *\r
- * @param size      the size of the array: size = (field1 + field2 + ...) * sizeof(const char*)\r
- */\r
-void ReDirOptions::initCompoundUsage(size_t size) {\r
-       delete[] m_compoundUsage;\r
-       int count = size / sizeof m_compoundUsage[0];\r
-       m_compoundUsage = new const char*[count];\r
-       memset(m_compoundUsage, 0, size);\r
-       m_countCompoundUsage = count;\r
-}\r
-\r
-/**\r
- * Optimizes the path patterns.\r
- *\r
- * For all patterns of the list:\r
- * <ul><li>remove a trailing "\" and "\*"</li>\r
- * <li>change "*\xy" to "*\xy" and "xy" (finds xy in the root directory)</li>\r
- * <li>replaces "/" with the os specific path separator</li>\r
- * </ul>\r
- *\r
- * @param buffer       the pattern list as string, e.g. ";*;-cache"\r
- */\r
-void ReDirOptions::optimizePathPattern(ReByteBuffer& buffer) {\r
-       ReStringList list;\r
-       ReStringList rootList;\r
-       list.split(buffer.str() + 1, buffer.str()[0]);\r
-       buffer.replaceAll(OS_SEPARATOR, 1, "/", 1);\r
-       ReByteBuffer item;\r
-       for (int ix = 0; ix < (int) list.count(); ix++) {\r
-               item.set(list.strOf(ix), -1);\r
-               if (item.endsWith("/*"))\r
-                       item.setLength(item.length() - 2);\r
-               item.removeLastChar('/');\r
-               bool notAnchored = item.startsWith("*/") || item.startsWith("-*/");\r
-               item.replaceAll("/", 1, OS_SEPARATOR, 1);\r
-               list.replace(ix, item.str());\r
-               if (notAnchored) {\r
-                       item.remove(item.str()[0] == '-' ? 1 : 0, 2);\r
-                       rootList.append(item.str(), 0);\r
-               }\r
-       }\r
-       if (rootList.count() > 0) {\r
-               list.append(rootList);\r
-       }\r
-       item.set(buffer.str(), 1);\r
-       buffer.set(item);\r
-       list.join(item.str(), buffer, true);\r
-}\r
-/**\r
- * Sets the standard filter options given by the program arguments.\r
- *\r
- * @param filter    OUT: the filter to set\r
- */\r
-void ReDirOptions::setFilterFromProgramArgs(ReDirEntryFilter& filter) {\r
-       ReByteBuffer buffer;\r
-       if (m_programArgs.getString("younger", buffer)[0] != '\0')\r
-               filter.m_maxAge = checkDate(buffer.str());\r
-       if (m_programArgs.getString("older", buffer)[0] != '\0')\r
-               filter.m_minAge = checkDate(buffer.str());\r
-       if (m_programArgs.getString("maxsize", buffer)[0] != '\0')\r
-               filter.m_maxSize = checkSize(buffer.str());\r
-       if (m_programArgs.getString("minsize", buffer)[0] != '\0')\r
-               filter.m_minSize = checkSize(buffer.str());\r
-       if (m_programArgs.getString("type", buffer)[0] != '\0')\r
-               filter.m_types = checkType(buffer.str());\r
-       filter.m_minDepth = m_programArgs.getInt("mindepth");\r
-       filter.m_maxDepth = m_programArgs.getInt("maxdepth");\r
-       if (m_programArgs.getString("nodepattern", buffer)[0] != '\0') {\r
-               checkPatternList(buffer.str());\r
-               m_nodePatterns.set(buffer.str());\r
-               filter.m_nodePatterns = &m_nodePatterns;\r
-       }\r
-       if (m_programArgs.getString("pathpattern", buffer)[0] != '\0') {\r
-               checkPatternList(buffer.str());\r
-               optimizePathPattern(buffer);\r
-               m_pathPatterns.set(buffer.str());\r
-               filter.m_pathPatterns = &m_pathPatterns;\r
-       }\r
-       if ((m_interval = m_programArgs.getInt("trace")) != 0)\r
-               m_triggerCount = 10;\r
-       if (m_programArgs.getString("output", buffer)[0] != '\0') {\r
-               if ((m_output = fopen(buffer.str(), "w")) == NULL) {\r
-                       help("cannot open output file", buffer.str());\r
-                       m_output = stdout;\r
-               }\r
-       }\r
-}\r
-\r
-/**\r
- * Constructor.\r
- *\r
- * @param usage        a string vector with a message how to use the command\r
- * @param example      a string vector with some examples how to use the command\r
- * @param minArguments minimal count of non option arguments\r
- * @param reservedFirst        count of arguments at the start which are not files\r
- * @param reservedLast count of arguments at the end which are not files\r
- * @param addCurrentDirIfNoArguments\r
- *                                             <code>true</code>: if no arguments are given the current\r
- *                                             directory will be added as argument\r
- * @param logger               logger for error messages\r
- */\r
-#pragma warning( push )\r
-#pragma warning( disable : 4355 )\r
-ReTool::ReTool(const char* usage[], const char* example[], int minArguments,\r
-    int reservedFirst, int reservedLast, bool addCurrentDirIfNoArguments,\r
-    ReLogger* logger) :\r
-           ReDirOptions(usage, example),\r
-           ReDirTreeStatistic(),\r
-           m_minArguments(minArguments),\r
-           m_reservedFirst(reservedFirst),\r
-           m_reservedLast(reservedLast),\r
-           m_addCurrentDirIfNoArguments(addCurrentDirIfNoArguments),\r
-           m_hasStandardArgs(true),\r
-           m_traverser(NULL, this, logger),\r
-           m_filter(),\r
-           m_start(time(NULL)),\r
-           m_logger(logger) {\r
-#pragma warning( pop )\r
-}\r
-\r
-/**\r
- * Destructor.\r
- */\r
-ReTool::~ReTool() {\r
-}\r
-\r
-/**\r
- * Prints a message.\r
- *\r
- * Often overwritten by a subclass.\r
- *\r
- * @param currentFile  message for the trace\r
- * @return                             <code>true</code> (for chaining)\r
- */\r
-bool ReTool::trace(const char* currentFile) {\r
-       ReByteBuffer buffer(" ");\r
-       int duration = int(time(NULL) - m_startTime);\r
-       buffer.appendInt(duration / 60).appendInt(duration % 60, ":%02d: ");\r
-       buffer.appendInt(m_files).appendChar('/').appendInt(\r
-           m_traverser.directories()).append(" dir(s)");\r
-       buffer.appendInt(m_files).appendChar('/').appendInt(m_traverser.files())\r
-           .append(" file(s)");\r
-       buffer.append(currentFile);\r
-       fputs(buffer.str(), stdout);\r
-       return true;\r
-}\r
-\r
-/**\r
- * Evaluates the arguments and calls the main function.\r
- *\r
- * @param argc                 number of arguments. If -1: <code>argv</code> ends with <code>NULL</code>\r
- * @param argv                 program arguments\r
- * @param minArguments the command needs at least so many non option arguments\r
- */\r
-void ReTool::run(int argc, const char** argv) {\r
-       try {\r
-               if (argc < 0) {\r
-                       for (argc = 0; argv[argc] != NULL; argc++) {\r
-                               // nothing to do\r
-                       }\r
-               }\r
-               m_programArgs.init(argc, argv);\r
-               if (m_programArgs.argCount() < m_minArguments)\r
-                       m_programArgs.help(i18n("too few arguments"), false, stdout);\r
-               if (m_hasStandardArgs)\r
-                       setFilterFromProgramArgs(m_filter);\r
-               doIt();\r
-               if (m_output != stdout) {\r
-                       fclose(m_output);\r
-                       m_output = stdout;\r
-               }\r
-\r
-       } catch (ReOptionException& exc) {\r
-               m_programArgs.help(exc.getMessage(), false, stdout);\r
-       }\r
-}\r
-/**\r
- * Evaluates the non option arguments which are names of files or directories.\r
- *\r
- * For each non reserved argument it will be called either <code>processSingleFile()</code>\r
- * or <code>processTree()</code>.\r
- *\r
- */\r
-void ReTool::processFileArguments() {\r
-       int max = m_programArgs.argCount() - m_reservedLast;\r
-       // Test whether the arguments are files or directories:\r
-       ReByteBuffer arg;\r
-       for (int ii = m_reservedFirst; ii < max; ii++) {\r
-               arg = m_programArgs.arg(ii);\r
-               if (!exists(arg) != 0)\r
-                       m_programArgs.help(\r
-                           ReByteBuffer(i18n("not a file or a directory: ")).append(arg)\r
-                               .str(), false, stderr);\r
-       }\r
-       // process the files:\r
-       for (int ii = m_reservedFirst; ii < max; ii++) {\r
-               const char* arg = m_programArgs.arg(ii);\r
-               if (S_ISDIR(m_statInfo.st_mode))\r
-                       processTree(arg);\r
-               else\r
-                       processSingleFile(arg);\r
-       }\r
-}\r
-/**\r
- * Processes a single file.\r
- *\r
- * Gets the file info and calls <code>processFile()</code>.\r
- *\r
- * @param filename             the name of the file\r
- */\r
-void ReTool::processSingleFile(const char* filename) {\r
-       ReByteBuffer protocol;\r
-       ReByteBuffer path;\r
-       ReByteBuffer name;\r
-       ReByteBuffer ext;\r
-       ReStringUtils::splitPath(filename, &protocol, &path, &name, &ext);\r
-       protocol.append(path);\r
-       if (protocol.length() == 0)\r
-               protocol.append(".");\r
-       else\r
-               protocol.reduceLength();\r
-       m_traverser.changeBase(protocol.str());\r
-\r
-       name.append(ext);\r
-       setFilterFromProgramArgs(m_filter);\r
-       m_traverser.setPropertiesFromFilter(&m_filter);\r
-       ReDirStatus_t entry(m_logger);\r
-       entry.m_path = protocol;\r
-       if (entry.findFirst()) {\r
-               do {\r
-#if defined    __linux__\r
-                       bool found = strcmp(entry.node(), name.str()) == 0;\r
-#elif defined __WIN32__\r
-                       bool found = _stricmp(entry.node(), name.str()) == 0;\r
-#endif\r
-                       if (found && m_filter.match(entry)) {\r
-                               processFile(&entry);\r
-                               break;\r
-                       }\r
-               } while (entry.findNext());\r
-       }\r
-       entry.freeEntry();\r
-}\r
-/**\r
- * Processes a directory tree.\r
- *\r
- * Finds all filtered files in this directory and its subdirs and call <code>processFile()</code>\r
- * or <code>processDir()</code>.\r
- *\r
- * @param directory            the name of the directory\r
- */\r
-void ReTool::processTree(const char* directory) {\r
-       m_traverser.changeBase(directory);\r
-       m_traverser.setPropertiesFromFilter(&m_filter);\r
-       ReDirStatus_t* entry;\r
-       int level;\r
-       while ((entry = m_traverser.nextFile(level, &m_filter)) != NULL) {\r
-               if (entry->isDirectory()) {\r
-                       processDir(entry);\r
-                       m_directories++;\r
-               } else {\r
-                       processFile(entry);\r
-                       m_files++;\r
-                       m_sizes += entry->fileSize();\r
-               }\r
-       }\r
-}\r
-/**\r
- * Processes one file.\r
- *\r
- * Normally this method will be overwritten.\r
- *\r
- * @param entry        the properties of the file to process\r
- */\r
-void ReTool::processFile(ReDirStatus_t* entry) {\r
-       fprintf(m_output, "+++ ignored (not a directory): %s\n", entry->fullName());\r
-}\r
-/**\r
- * Issues a summary message if verbose level allows this.\r
- * \r
- * @param prefix               NULL or a line prefix\r
- */\r
-void ReTool::printSummary(const char* prefix) {\r
-       if (m_verboseLevel >= V_SUMMARY) {\r
-               int duration = int(time(NULL) - m_start);\r
-               ReByteBuffer line;\r
-               ReByteBuffer line2;\r
-               statisticAsString(line);\r
-               double rate =\r
-                   duration == 0 ? 0.0 : (m_files + m_directories) / duration;\r
-               line.append(rate, " %.1f").append(i18n("/sec"), -1);\r
-               m_traverser.statisticAsString(line2);\r
-               line2.appendChar(' ').appendTime(duration).append(" ", 1).append(\r
-                   i18n("sec"));\r
-               fprintf(m_output, "%s=== filtered: %s\n", prefix == NULL ? "" : prefix,\r
-                   line.str());\r
-               fprintf(m_output, "%s===    total: %s\n", prefix == NULL ? "" : prefix,\r
-                   line2.str());\r
-       }\r
-\r
-}\r
-/**\r
- * Processes one directory.\r
- *\r
- * Normally this method will be overwritten.\r
- *\r
- * @param entry        the properties of the directory to process\r
- */\r
-void ReTool::processDir(ReDirStatus_t* entry) {\r
-       fprintf(m_output, "+++ ignored (not a file): %s\n", entry->fullName());\r
-}\r
-\r
-/**\r
- * Constructor.\r
- */\r
-ReDirStatisticData::ReDirStatisticData() :\r
-           ReDirTreeStatistic(),\r
-           m_path() {\r
-}\r
-/**\r
- * Copy constructor.\r
- *\r
- * @param source       the source to copy\r
- */\r
-ReDirStatisticData::ReDirStatisticData(const ReDirStatisticData& source) :\r
-           m_path(source.m_path) {\r
-}\r
-/**\r
- * Assignment operator.\r
- *\r
- * @param source       the source to copy\r
- * @return                     the instance itself\r
- */\r
-ReDirStatisticData& ReDirStatisticData::operator =(\r
-    const ReDirStatisticData& source) {\r
-       m_sizes = source.m_sizes;\r
-       m_files = source.m_files;\r
-       m_directories = source.m_directories;\r
-       m_path = source.m_path;\r
-       return *this;\r
-}\r
-\r
-/**\r
- * Constructor.\r
- *\r
- * @param logger               logger for error handling\r
- * @param deltaList            increment if list must be increased\r
- * @param deltaBuffer  increment if content buffer must be increased\r
- */\r
-ReDirStatistic::ReDirStatistic(ReLogger* logger, int deltaList, int deltaBuffer) :\r
-           ReTool(s_statisticUsage, s_statisticExamples, 2, 0, 1, false, logger),\r
-           m_list(deltaList, deltaBuffer),\r
-           m_traceInterval(60),\r
-           m_lastTrace(0) {\r
-       // standard short options: D d O o P p T t v y Z z\r
-       m_programArgs.addBool("kbyte",\r
-           i18n("output format is '<kbyte> <path>' (like unix 'du' command)"), 'k',\r
-           "kbyte", false);\r
-       addStandardFilterOptions();\r
-}\r
-/**\r
- * Destructor.\r
- */\r
-ReDirStatistic::~ReDirStatistic() {\r
-}\r
-\r
-/**\r
- * Adds the data from another instance.\r
- *\r
- * @param source               the other instance\r
- * @return                     the instance itself\r
- */\r
-ReDirStatisticData& ReDirStatisticData::add(const ReDirStatisticData& source) {\r
-       m_sizes += source.m_sizes;\r
-       m_files += source.m_files;\r
-       m_directories += source.m_directories;\r
-       return *this;\r
-}\r
-/**\r
- * Initializes the data of the instance.\r
- */\r
-void ReDirStatisticData::clear() {\r
-       ReDirTreeStatistic::clear();\r
-       m_path.setLength(0);\r
-}\r
-/**\r
- * Constructor.\r
- *\r
- * @param logger       logger for error handling\r
- */\r
-ReDirBatch::ReDirBatch(ReLogger* logger) :\r
-           ReTool(s_batchUsage, s_batchExamples, 0, 0, 0, true, logger),\r
-           m_arguments(),\r
-           m_script(),\r
-           m_isExe(false) {\r
-       // standard short options: D d O o P p T t v y Z z\r
-       m_programArgs.addString("first",\r
-           i18n("defines the first line of the output"), '1', "first-line", true,\r
-#if defined __linux__\r
-           "#! /bin/sh"\r
-#elif defined __WIN32__\r
-           "rem this batch is created by dirtool"\r
-#endif\r
-)      ;\r
-       m_programArgs.addString("arguments", i18n("template for the output line.\n"\r
-                       "Possible placeholders: (e.g. e:\\data\\sample.txt)\n"\r
-                       "   !full!: e:\\data\\sample.txt\n"\r
-                       "   !path!: e:\\data\\\n"\r
-                       "   !basename!: sample.txt\n"\r
-                       "   !name!: sample\n"\r
-                       "   !ext!: .txt\n"\r
-                       "example: --arguments='echo !basename! in !path! found'"), 'a',\r
-               "arguments", false, NULL);\r
-       m_programArgs.addString("script",\r
-               i18n("name of the script (starts each output line)"), 'c', "script",\r
-               false, NULL);\r
-#if defined __WIN32__\r
-       m_programArgs.addBool("isexe",\r
-               i18n("supresses the starting 'call' of each output line"\r
-                       "neccessary if a *.exe will be called (instead of a *.bat)"),\r
-               'x', "is-exe", false);\r
-#endif\r
-       addStandardFilterOptions();\r
-}\r
-\r
-static void replaceMakros(const char* arguments, ReDirStatus_t* entry,\r
-    const char* delim, ReByteBuffer& line) {\r
-       line.set(arguments, -1);\r
-       // we prepare the removal of unwanted delimiters in constructed placeholders:\r
-       // example: !path!!name!: without correction: "e:\\data\\""xxx"\r
-       // We want: "e:\\data\\xxx"\r
-       line.replaceAll("!!", 2, "!\x01!", 3);\r
-       ReByteBuffer replacement;\r
-       if (strstr(arguments, "!full!") != NULL) {\r
-               replacement.set(delim, -1).append(entry->m_path);\r
-               replacement.append(entry->node(), -1).append(delim, -1);\r
-               line.replaceAll("!full!", 6, replacement.str(), replacement.length());\r
-       }\r
-       if (strstr(arguments, "!path!") != NULL) {\r
-               replacement.set(delim, -1).append(entry->m_path).append(delim, -1);\r
-               line.replaceAll("!path!", 6, replacement.str(), replacement.length());\r
-       }\r
-       if (strstr(arguments, "!basename!") != NULL) {\r
-               replacement.set(delim, -1).append(entry->node(), -1).append(delim, -1);\r
-               line.replaceAll("!basename!", 10, replacement.str(),\r
-                   replacement.length());\r
-       }\r
-       if (strstr(arguments, "!name!") != NULL) {\r
-               replacement.set(delim, -1).append(entry->node(), -1);\r
-               int ix = replacement.rindexOf(".", 1);\r
-               if (ix > 1)\r
-                       replacement.setLength(ix);\r
-               replacement.append(delim, -1);\r
-               line.replaceAll("!name!", 6, replacement.str(), replacement.length());\r
-       }\r
-       if (strstr(arguments, "!ext!") != NULL) {\r
-               replacement.set(delim, -1).append(entry->node(), -1);\r
-               int ix = replacement.rindexOf(".", 1);\r
-               if (ix > 1)\r
-                       replacement.remove(1, ix - 1);\r
-               else\r
-                       replacement.setLength(1);\r
-               replacement.append(delim, -1);\r
-               line.replaceAll("!ext!", 5, replacement.str(), replacement.length());\r
-       }\r
-       // We remove the unwanted delimiters (see above):\r
-       ReByteBuffer buffer;\r
-       buffer.set(delim, -1).appendChar('\01').append(delim, -1);\r
-       line.replaceAll(buffer.str(), buffer.length(), "", 0);\r
-}\r
-/**\r
- * Creates the batch file.\r
- */\r
-void ReDirBatch::doIt() {\r
-       ReByteBuffer buffer;\r
-       m_programArgs.getString("arguments", m_arguments);\r
-       m_programArgs.getString("script", m_script);\r
-       if (m_arguments.length() + m_script.length() == 0)\r
-               help(\r
-                   i18n(\r
-                       "one of the option must be set: -a (--arguments) or -c (--script)"));\r
-#if defined __WIN32__\r
-       m_isExe = m_programArgs.getBool("isexe");\r
-#endif\r
-       m_programArgs.getString("first", buffer);\r
-       if (buffer.length() > 0)\r
-               fprintf(m_output, "%s\n", buffer.str());\r
-       processFileArguments();\r
-#if defined __linux__\r
-       static const char* prefix = "# ";\r
-#elif defined __WIN32__\r
-       static const char* prefix = "rem ";\r
-#endif\r
-       printSummary(prefix);\r
-}\r
-\r
-/**\r
- * Processes one directory.\r
- *\r
- * @param entry        the properties of the directory to process\r
- */\r
-void ReDirBatch::processDir(ReDirStatus_t* entry) {\r
-       processFile(entry);\r
-}\r
-\r
-/**\r
- * Processes one file.\r
- *\r
- * @param entry        the properties of the file to process\r
- */\r
-void ReDirBatch::processFile(ReDirStatus_t* entry) {\r
-       ReByteBuffer line;\r
-#if defined __linux__\r
-       static const char* delim = "'";\r
-#elif defined __WIN32__\r
-       static const char* delim = "\"";\r
-#endif\r
-       if (m_script.length() > 0) {\r
-#if defined __WIN32__\r
-               if (! m_isExe)\r
-               line.append("call ");\r
-#endif\r
-               line.append(m_script).appendChar(' ').appendChar(delim[0]);\r
-               line.append(entry->m_path).append(entry->node(), -1);\r
-               line.appendChar(delim[0]);\r
-       } else {\r
-               replaceMakros(m_arguments.str(), entry, delim, line);\r
-       }\r
-       fprintf(m_output, "%s\n", line.str());\r
-}\r
-\r
-/**\r
- * Constructor.\r
- *\r
- * @param logger       logger for error handling\r
- */\r
-ReDirChecksum::ReDirChecksum(ReLogger* logger) :\r
-           ReTool(s_checksumUsage, s_checksumExamples, 0, 0, 0, true, logger),\r
-           m_command(CMD_LIST),\r
-           m_digest(NULL),\r
-           m_buffer() {\r
-       // standard short options: D d O o P p T t v y Z z\r
-       m_programArgs.addString("algorithm", i18n("algorithm: 'MD5' or 'RPD64'"),\r
-           'a', "--algorithm", false, "MD5");\r
-       m_programArgs.addInt("buffersize",\r
-           i18n("buffer size for file reading (in KiByte)"), 'b', "--buffer-size",\r
-           4 * 1024);\r
-       m_programArgs.addString("command",\r
-           i18n(\r
-               "'build': builds in each directory a file '.dt.chksum' with sum and name\n"\r
-                       "'compare': shows differences between the content of '.dt.chksum' and the current files\n"\r
-                       "'list': shows checksum and filename\n"\r
-                       "'update': like compare, but update the content of '.dt.chksum'"),\r
-           'c', "--command", false, "list");\r
-       m_programArgs.addInt("salt", i18n("a number which change the checksum"),\r
-           's', "--salt", 0);\r
-       addStandardFilterOptions();\r
-}\r
-\r
-/**\r
- * Destructor.\r
- */\r
-ReDirChecksum::~ReDirChecksum() {\r
-       delete m_digest;\r
-       m_digest = NULL;\r
-}\r
-/**\r
- * Builds a checksum storage for a given directory.\r
- *\r
- * @param path the directory to process\r
- */\r
-void ReDirChecksum::buildStorage(const char* path, const char* storageFile) {\r
-       ReTraverser traverser(path);\r
-       traverser.setMaxLevel(0);\r
-       ReDirStatus_t* entry;\r
-       ReRPD64 digest2;\r
-       digest2.setSalt(0x2004199111121989ll);\r
-       ReByteBuffer line;\r
-       FILE* fp = fopen(storageFile, "w");\r
-       if (fp == NULL) {\r
-               m_logger->sayF(LOG_ERROR | CAT_FILE, LC_BUILD_DIRECTORY_1,\r
-                   i18n("cannot open file: $1 (errno: $2)")).arg(storageFile).arg(\r
-               errno).end();\r
-\r
-       } else {\r
-               int level;\r
-               while ((entry = traverser.nextFile(level, &m_filter)) != NULL) {\r
-                       line.setLength(0);\r
-                       if (!entry->isDirectory()) {\r
-                               calculateChecksum(entry->fullName(), *m_digest, m_buffer,\r
-                                   m_logger);\r
-                               line.append(m_digest->hexDigest());\r
-                       }\r
-                       line.appendChar('\t').append(entry->node());\r
-                       digest2.update(line);\r
-                       fprintf(fp, "%s\n", line.str());\r
-                       if (m_verboseLevel >= V_NORMAL)\r
-                               fprintf(m_output, "%16s\t%s",\r
-                                   entry->isDirectory() ? "" : m_digest->hexDigest().str(),\r
-                                   entry->fullName());\r
-               }\r
-               fprintf(fp, "%s\n", digest2.hexDigest().str());\r
-               fclose(fp);\r
-       }\r
-}\r
-\r
-/**\r
- * Compares the files of a directory with the files in the storage file\r
- *\r
- * @param path                 the directory\r
- * @param storageFile  the file with the checksums\r
- */\r
-void ReDirChecksum::compareDir(const char* path, ReStringList& names,\r
-    const char* storageFile) {\r
-       // forget the last line (checksum):\r
-       int count = names.count() - 1;\r
-       names.remove(count);\r
-       for (int ii = 0; ii < count; ii++) {\r
-               const char* name = names.strOf(ii);\r
-               const char* ptr = strchr(name, '\t');\r
-               if (ptr != NULL)\r
-                       names.replaceString(ii, ptr + 1);\r
-       }\r
-       names.sort();\r
-       names.setSorted(true);\r
-       ReTraverser traverser(path);\r
-       traverser.setMaxLevel(0);\r
-       ReDirStatus_t* entry;\r
-       int level;\r
-       while ((entry = traverser.nextFile(level, &m_filter)) != NULL) {\r
-               ReSeqArray::Index index;\r
-               const char* node = entry->node();\r
-               // the stringlist stores the string with trailing '\0'\r
-               if (!names.binarySearch(node, strlen(node) + 1, index)) {\r
-                       m_logger->sayF(LOG_ERROR | CAT_SECURITY, LC_COMPARE_DIR_1,\r
-                           i18n("missing file $1 in storage $2")).arg(entry->fullName())\r
-                           .arg(storageFile).end();\r
-               }\r
-       }\r
-}\r
-\r
-/**\r
- * Compares a storage file with the files of a directory.\r
- *\r
- * @param path                 the directory\r
- * @param storageFile  the file with the checksums\r
- */\r
-void ReDirChecksum::compareStorage(const char* path, const char* storageFile) {\r
-       ReStringList storage;\r
-       storage.readFromFile(storageFile);\r
-       if (storage.count() <= 0) {\r
-               m_logger->sayF(LOG_ERROR | CAT_SECURITY, LC_COMPARE_STORAGE_1,\r
-                   i18n("empty storage file: $1")).arg(storageFile).end();\r
-       } else if (!isValid(storage)) {\r
-               m_logger->sayF(LOG_ERROR | CAT_SECURITY, LC_COMPARE_STORAGE_2,\r
-                   i18n("corrupted storage file: $1")).arg(storageFile).end();\r
-       } else {\r
-               ReStringList cols;\r
-               ReByteBuffer fullname(path);\r
-               fullname.ensureLastChar(OS_SEPARATOR_CHAR);\r
-               int pathLength = fullname.length();\r
-               struct stat info;\r
-               // test all files of the storage:\r
-               for (size_t ii = 0; ii < storage.count() - 1; ii++) {\r
-                       cols.split(storage.strOf(ii), '\t');\r
-                       int colCount = cols.count();\r
-                       if (colCount != 2) {\r
-                               m_logger->sayF(LOG_ERROR | CAT_SECURITY, LC_COMPARE_STORAGE_3,\r
-                                   i18n("wrong format ($1) in storage file $2-$3")).arg(\r
-                                   colCount).arg(storageFile).arg(ii + 1).end();\r
-                               break;\r
-                       } else {\r
-                               fullname.setLength(pathLength).append(cols.strOf(1));\r
-                               if (cols.strLengthOf(0) == 0) {\r
-                                       // a directory:\r
-                                       if (stat(fullname.str(), &info) != 0) {\r
-                                               m_logger->sayF(LOG_ERROR | CAT_SECURITY,\r
-                                                   LC_COMPARE_STORAGE_4,\r
-                                                   i18n("missing directory $1 ($2-$3)")).arg(fullname)\r
-                                                   .arg(storageFile).arg(ii + 1).end();\r
-                                       } else if (!S_ISDIR(info.st_mode)) {\r
-                                               m_logger->sayF(LOG_ERROR | CAT_SECURITY,\r
-                                                   LC_COMPARE_STORAGE_5,\r
-                                                   i18n("directory is now a file: $1 ($2-$3)")).arg(\r
-                                                   fullname).arg(storageFile).arg(ii + 1).end();\r
-                                       }\r
-                               } else {\r
-                                       // a directory:\r
-                                       if (stat(fullname.str(), &info) != 0) {\r
-                                               m_logger->sayF(LOG_ERROR | CAT_SECURITY,\r
-                                                   LC_COMPARE_STORAGE_6,\r
-                                                   i18n("missing file $1 ($2-$3)")).arg(fullname).arg(\r
-                                                   storageFile).arg(ii + 1).end();\r
-                                       } else if (S_ISDIR(info.st_mode)) {\r
-                                               m_logger->sayF(LOG_ERROR | CAT_SECURITY,\r
-                                                   LC_COMPARE_STORAGE_7,\r
-                                                   i18n("file is now a directory: $1 ($2-$3)")).arg(\r
-                                                   fullname).arg(storageFile).arg(ii + 1).end();\r
-                                       } else {\r
-                                               calculateChecksum(fullname.str(), *m_digest, m_buffer,\r
-                                                   m_logger);\r
-                                               if (!m_digest->hexDigest().equals(cols.strOf(0))) {\r
-                                                       m_logger->sayF(LOG_ERROR | CAT_SECURITY,\r
-                                                           LC_COMPARE_STORAGE_7,\r
-                                                           i18n("checksum different: $1 ($2-$3)")).arg(\r
-                                                           fullname).arg(storageFile).arg(ii + 1).end();\r
-                                               }\r
-                                       }\r
-                               }\r
-                       }\r
-               }\r
-               compareDir(path, storage, storageFile);\r
-       }\r
-}\r
-/**\r
- * Checks the validity of a storage file.\r
- *\r
- * The storage file is protected with a checksum.\r
- * The checksum has another salt as the checksums of the files. This makes it\r
- * much harder to manipulate the storage file.\r
- *\r
- * @param storage      the storage file as string list.\r
- */\r
-bool ReDirChecksum::isValid(const ReStringList& storage) {\r
-       int count = storage.count();\r
-       ReRPD64 digest2;\r
-       digest2.setSalt(0x2004199111121989ll);\r
-\r
-       for (int ii = 0; ii < count - 1; ii++) {\r
-               const char* line = storage.strOf(ii);\r
-               digest2.update(line);\r
-       }\r
-       bool rc = true;\r
-       const char* hex = storage.strOf(count - 1);\r
-       if (!digest2.hexDigest().equals(hex))\r
-               rc = false;\r
-       return rc;\r
-}\r
-/**\r
- * Lists the metadata of the specified files.\r
- */\r
-void ReDirChecksum::doIt() {\r
-       int size = m_programArgs.getInt("buffersize") * 1024;\r
-       m_buffer.setLength(size);\r
-       ReByteBuffer value;\r
-       m_programArgs.getString("command", value);\r
-       if (value.equals("list", -1, true))\r
-               m_command = CMD_LIST;\r
-       else if (value.equals("build", -1, true))\r
-               m_command = CMD_BUILD;\r
-       else if (value.equals("compare", -1, true))\r
-               m_command = CMD_COMPARE;\r
-       else if (value.equals("update", -1, true))\r
-               m_command = CMD_UPDATE;\r
-       else\r
-               help(i18n("unknown command (expected: build compare list update): "),\r
-                   value.str());\r
-\r
-       m_programArgs.getString("algorithm", value);\r
-       if (value.equals("md5", -1, true))\r
-               m_digest = new ReMD5();\r
-       else if (value.equals("rpd64", -1, true))\r
-               m_digest = new ReRPD64();\r
-       else\r
-               help(i18n("unknown algorithm (expected: MD5 RPD64): "), value.str());\r
-       int salt = m_programArgs.getInt("salt");\r
-       if (salt != 0)\r
-               m_digest->setSalt(salt);\r
-       m_filter.m_allDirectories = true;\r
-       processFileArguments();\r
-       printSummary();\r
-}\r
-\r
-/**\r
- * Processes one directory.\r
- *\r
- * @param entry        the properties of the directory to process\r
- */\r
-void ReDirChecksum::processDir(ReDirStatus_t* entry) {\r
-       ReByteBuffer storageFile;\r
-       if (m_command != CMD_LIST) {\r
-               storageFile.append(entry->fullName());\r
-               storageFile.ensureLastChar(OS_SEPARATOR_CHAR);\r
-               storageFile.append(".dt.chksum");\r
-       }\r
-       switch (m_command) {\r
-       case CMD_BUILD:\r
-               buildStorage(entry->fullName(), storageFile.str());\r
-               break;\r
-       case CMD_COMPARE:\r
-               compareStorage(entry->fullName(), storageFile.str());\r
-               break;\r
-       case CMD_UPDATE:\r
-               printf("update not implemented");\r
-               break;\r
-       default:\r
-               break;\r
-       }\r
-}\r
-/**\r
- * Processes one file.\r
- *\r
- * @param entry        the properties of the file to process\r
- */\r
-void ReDirChecksum::processFile(ReDirStatus_t* entry) {\r
-       const char* name = entry->fullName();\r
-       switch (m_command) {\r
-       case CMD_LIST:\r
-               calculateChecksum(name, *m_digest, m_buffer, m_logger);\r
-               fprintf(m_output, "%s %s\n", m_digest->hexDigest().str(), name);\r
-               break;\r
-       default:\r
-               break;\r
-       }\r
-}\r
-/**\r
- * Compares a storage file with the files of a directory.\r
- *\r
- * @param path                 the directory\r
- * @param storageFile  the file with the checksums\r
- */\r
-void ReDirChecksum::updateStorage(const char* path, const char* storageFile) {\r
-\r
-}\r
-\r
-/**\r
- * Calculates the checksum of a given file.\r
- *\r
- * @param name         the full filename\r
- * @param digest       IN: defines the kind of checksum<br>\r
- *                                     OUT: contains the calculated checksum\r
- * @param buffer       IN/OUT: a buffer for file reading\r
- * @param logger       logger for error processing\r
- * @return                     <code>digest</code> (for chaining)\r
- */\r
-ReDigest& ReDirChecksum::calculateChecksum(const char* name, ReDigest& digest,\r
-    ReByteBuffer& buffer, ReLogger* logger) {\r
-       FILE* fp = fopen(name, "rb");\r
-       if (fp == NULL) {\r
-               if (logger != NULL)\r
-                       logger->sayF(LOG_ERROR | CAT_FILE, LC_CALCULATE_CHECKSUM_1,\r
-                           i18n("cannot open file: $1 (errno: $2)")).arg(name).arg(\r
-                       errno).end();\r
-       } else {\r
-               ReMD5 digest;\r
-               size_t readBytes;\r
-               uint8_t* buf = reinterpret_cast<uint8_t*>(buffer.buffer());\r
-               size_t blockSize = buffer.length();\r
-\r
-               while ((readBytes = fread(buf, 1, blockSize, fp)) > 0) {\r
-                       digest.update(buf, readBytes);\r
-               }\r
-               fclose(fp);\r
-       }\r
-       return digest;\r
-}\r
-\r
-/**\r
- * Constructor.\r
- *\r
- * @param logger       logger for error handling\r
- */\r
-ReDirDelete::ReDirDelete(ReLogger* logger) :\r
-           ReTool(s_deleteUsage, s_deleteExamples, 0, 0, 0, true, logger) {\r
-       addStandardFilterOptions();\r
-}\r
-\r
-/**\r
- * Deletes the specified files/directories.\r
- */\r
-void ReDirDelete::doIt() {\r
-       m_traverser.setDepthFirst(true);\r
-       processFileArguments();\r
-       printSummary();\r
-}\r
-\r
-/**\r
- * Processes one directory.\r
- *\r
- * @param entry        the properties of the directory to process\r
- */\r
-void ReDirDelete::processDir(ReDirStatus_t* entry) {\r
-       _rmdir(entry->fullName());\r
-}\r
-/**\r
- * Processes one file.\r
- *\r
- * @param entry        the properties of the file to process\r
- */\r
-void ReDirDelete::processFile(ReDirStatus_t* entry) {\r
-       const char* name = entry->fullName();\r
-       if (m_verboseLevel >= V_NORMAL) {\r
-               fprintf(m_output, "%s\n", name);\r
-       }\r
-       if (_unlink(name) != 0)\r
-               m_logger->sayF(LOG_ERROR | CAT_FILE, LC_DELETE_1,\r
-                   i18n("cannot delete ($1): $2")).arg(errno).arg(name).end();\r
-}\r
-\r
-/**\r
- * Constructor.\r
- *\r
- * @param logger       logger for error handling\r
- */\r
-ReDirList::ReDirList(ReLogger* logger) :\r
-           ReTool(s_listUsage, s_listExamples, 0, 0, 0, true, logger),\r
-           m_widthOwner(13),\r
-           m_shortFormat(false),\r
-           m_withRights(false),\r
-           m_numerical(false) {\r
-       // standard short options: D d O o P p T t v y Z z\r
-       m_programArgs.addBool("short", i18n("output is only path and basename"),\r
-           '1', "--short", false);\r
-       m_programArgs.addBool("rights", i18n("show the permission/right info"), 'r',\r
-           "--rights", false);\r
-       m_programArgs.addBool("numerical",\r
-           i18n("the permission/right info is shown as numbers"), 'n',\r
-           "--numerical", false);\r
-       m_programArgs.addInt("owner", i18n("space reserved for owner/group"), 'w',\r
-           "--width-owner", 13);\r
-       addStandardFilterOptions();\r
-}\r
-\r
-/**\r
- * Lists the metadata of the specified files.\r
- */\r
-void ReDirList::doIt() {\r
-       m_shortFormat = m_programArgs.getBool("short");\r
-       m_withRights = m_programArgs.getBool("rights");\r
-       m_numerical = m_programArgs.getBool("numerical");\r
-       m_widthOwner = m_programArgs.getInt("owner");\r
-       processFileArguments();\r
-       printSummary();\r
-}\r
-\r
-/**\r
- * Processes one directory.\r
- *\r
- * @param entry        the properties of the directory to process\r
- */\r
-void ReDirList::processDir(ReDirStatus_t* entry) {\r
-       processFile(entry);\r
-}\r
-/**\r
- * Processes one file.\r
- *\r
- * @param entry        the properties of the file to process\r
- */\r
-void ReDirList::processFile(ReDirStatus_t* entry) {\r
-       ReByteBuffer bufferRights;\r
-       ReByteBuffer bufferTime;\r
-       if (m_shortFormat)\r
-               fprintf(m_output, "%s%s\n", entry->m_path.str(), entry->node());\r
-       else {\r
-               if (m_withRights)\r
-                       entry->rightsAsString(bufferRights, m_numerical, m_widthOwner);\r
-               fprintf(m_output, "%c%s %12.6f %s %s%s\n", entry->typeAsChar(),\r
-                   bufferRights.str(), entry->fileSize() / 1E6,\r
-                   entry->filetimeAsString(bufferTime), entry->m_path.str(),\r
-                   entry->node());\r
-       }\r
-}\r
-\r
-/**\r
- * Constructor.\r
- *\r
- * @param logger       logger for error handling\r
- */\r
-ReDirRandom::ReDirRandom(ReLogger* logger) :\r
-           ReTool(s_randomUsage, s_randomExamples, 2, 0, 0, true, logger) {\r
-       m_hasStandardArgs = false;\r
-       m_programArgs.addBool("multiple",\r
-           i18n("result can contain a value multiple times"), 'm', "--multiple",\r
-           false);\r
-       m_programArgs.addInt("perline", i18n("number of values per line"), 'l',\r
-           "--values-per-line", 10);\r
-       m_programArgs.addBool("sort", i18n("the result is sorted"), 's', "--sort",\r
-           false);\r
-       m_programArgs.addInt("width",\r
-           i18n("output format: minimum width of the numbers"\r
-                   "   0: width of the maximum"), 'w', "--min-width", 0);\r
-}\r
-\r
-/**\r
- * Lists the metadata of the specified files.\r
- */\r
-void ReDirRandom::doIt() {\r
-       bool multiple = m_programArgs.getBool("multiple");\r
-       bool sort = m_programArgs.getBool("sort");\r
-       int count = atol(m_programArgs.arg(0));\r
-       int to = atol(m_programArgs.arg(1));\r
-       int from = 1;\r
-       if (m_programArgs.argCount() > 2)\r
-               from = atol(m_programArgs.arg(2));\r
-       ReShiftRandom rand;\r
-       rand.setSeed(rand.nearTrueRandom());\r
-       for (int ii = rand.nextInt(100); ii > 0; ii--)\r
-               rand.nextInt();\r
-       int factor = to <= 1000 ? 1000 * 1000 : 1;\r
-       int numbersPerLine = m_programArgs.getInt("perline");\r
-       int width = m_programArgs.getInt("width");\r
-       if (width <= 0)\r
-               width = ReByteBuffer("").appendInt(to).length();\r
-       ReByteBuffer format("%");\r
-       format.appendInt(width).append("d%s");\r
-       int found = 0;\r
-       ReSeqArray values(count);\r
-       ReByteBuffer value;\r
-       if (!multiple && count >= to - from + 1)\r
-               help(\r
-                   i18n(\r
-                       "not realy random: all possible values requested. Do you mean '--multiple'?"));\r
-       while (found < count) {\r
-               value.setLength(0).appendInt(\r
-                   from + rand.nextInt((to - from + 1) * factor) / factor, "%010d");\r
-               int ix = (int) values.find(value.str());\r
-               if (!multiple && ix != -1)\r
-                       continue;\r
-               found++;\r
-               values.add(-1, value.str());\r
-       }\r
-       if (sort)\r
-               values.sort();\r
-       for (int ii = 0; ii < count; ii++) {\r
-               values.get(ii, value);\r
-               int val = atol(value.str());\r
-               printf(format.str(), val,\r
-                   ii % numbersPerLine == numbersPerLine - 1 ? "\n" : " ");\r
-       }\r
-       if (count % numbersPerLine != 0)\r
-               printf("\n");\r
-}\r
-\r
-/**\r
- * Calculates the statistic of a directory tree.\r
- *\r
- *\r
- */\r
-const ReStringList& ReDirStatistic::calculate(const char* base, int level,\r
-    void (*formatter)(const ReDirStatisticData& data, ReDirStatistic& parent,\r
-        ReByteBuffer& line)) {\r
-       ReDirEntryFilter filter;\r
-       ReTraverser traverser(base, this);\r
-       setFilterFromProgramArgs(filter);\r
-       traverser.setPropertiesFromFilter(&filter);\r
-       if (level > 1024)\r
-               level = 1024;\r
-       else if (level < 0)\r
-               level = 0;\r
-       m_list.clear();\r
-       ReDirStatisticData** dataStack = new ReDirStatisticData*[level + 1];\r
-       memset(dataStack, 0, sizeof dataStack[0] * (level + 1));\r
-       dataStack[0] = new ReDirStatisticData();\r
-       int topOfStack = 0;\r
-       ReDirStatus_t* entry;\r
-       int currentDepth = -1;\r
-       ReDirStatisticData* current = dataStack[0];\r
-       current->m_path.set(base, -1);\r
-       ReByteBuffer line;\r
-       bool useFilter = filter.m_minSize > 0 || filter.m_maxSize != -1\r
-           || !filetimeIsUndefined(filter.m_minAge)\r
-           || !filetimeIsUndefined(filter.m_maxAge) || m_nodePatterns.count() > 0;\r
-       while ((entry = traverser.rawNextFile(currentDepth))) {\r
-               if (currentDepth <= level && !entry->m_path.equals(current->m_path)) {\r
-                       if (currentDepth <= topOfStack) {\r
-                               // close the entries of the stack above the new top level:\r
-                               while (topOfStack >= currentDepth) {\r
-                                       // Add the data to the parent:\r
-                                       if (topOfStack > 0)\r
-                                               dataStack[topOfStack - 1]->add(*dataStack[topOfStack]);\r
-                                       // Append it to the result:\r
-                                       (*formatter)(*dataStack[topOfStack], *this, line);\r
-                                       m_list.append(line);\r
-                                       topOfStack--;\r
-                               }\r
-                               // We reuse the top of stack:\r
-                               topOfStack++;\r
-                               current = dataStack[topOfStack];\r
-                               // exchange the top of stack with the new found directory:\r
-                               current->clear();\r
-                               current->m_path.set(entry->m_path.str(),\r
-                                   entry->m_path.length());\r
-                               ;\r
-                       } else {\r
-                               // set up a new stack entry:\r
-                               if (currentDepth != topOfStack + 1)\r
-                                       assert(currentDepth == topOfStack + 1);\r
-\r
-                               topOfStack++;\r
-                               if (dataStack[topOfStack] == NULL)\r
-                                       dataStack[topOfStack] = new ReDirStatisticData();\r
-                               else\r
-                                       dataStack[topOfStack]->clear();\r
-                               current = dataStack[topOfStack];\r
-                               current->m_path.set(entry->m_path.str(),\r
-                                   entry->m_path.length());\r
-                       }\r
-               }\r
-               if (entry->isDirectory()) {\r
-                       current->m_directories++;\r
-               } else if (!useFilter || filter.match(*entry)) {\r
-                       current->m_sizes += entry->fileSize();\r
-                       current->m_files++;\r
-               }\r
-       }\r
-       // close all dirs with parents:\r
-       while (topOfStack > 0) {\r
-               // Add the data to the parent:\r
-               dataStack[topOfStack - 1]->add(*dataStack[topOfStack]);\r
-               // Append it to the result:\r
-               (*formatter)(*dataStack[topOfStack], *this, line);\r
-               m_list.append(line);\r
-               topOfStack--;\r
-       }\r
-       // ... and the overall summery:\r
-       (*formatter)(*dataStack[0], *this, line);\r
-       m_list.append(line);\r
-       // free the resources:\r
-       for (int ix = 0; ix <= level; ix++)\r
-               delete dataStack[ix];\r
-       delete[] dataStack;\r
-       return m_list;\r
-}\r
-\r
-/**\r
- * Build the statistic and print the results.\r
- *\r
- * @param arc   count of arguments in <code>argv</code>\r
- * @param argv  the program arguments.\r
- */\r
-void ReDirStatistic::doIt() {\r
-       int depth = 1;\r
-       if (m_programArgs.argCount() > 1) {\r
-               const char* arg1 = m_programArgs.arg(1);\r
-               if (ReStringUtils::lengthOfUnsigned(arg1, -1, (unsigned *) &depth) == 0)\r
-                       m_programArgs.help("depth is not an integer", false, stdout);\r
-       }\r
-       void (*proc)(const ReDirStatisticData& data, ReDirStatistic& parent,\r
-           ReByteBuffer& line) = &formatWithSizeFilesAndDirs;\r
-       if (m_programArgs.getBool("kbyte"))\r
-               proc = &formatLikeDu;\r
-       const ReStringList& list = calculate(m_programArgs.arg(0), depth, proc);\r
-       ReByteBuffer buffer;\r
-       for (size_t ix = 0; ix < list.count(); ix++) {\r
-               buffer.set(list.strOf(ix), list.strLengthOf(ix));\r
-               fprintf(m_output, "%s\n", buffer.str());\r
-       }\r
-       if (m_verboseLevel >= V_SUMMARY) {\r
-               int duration = int(time(NULL) - m_start);\r
-               fprintf(m_output, "=== duration: ");\r
-               if (duration >= 3600)\r
-                       fprintf(m_output, "%d:", duration / 3600);\r
-               fprintf(m_output, "%02d:%02d\n", duration % 3600 / 60, duration % 60);\r
-       }\r
-}\r
-\r
-/**\r
- * Formats a line like the du (disk usage) command.\r
- *\r
- * This is a possible parameter of <code>ReDirStatistic::calculate()</code>.\r
- *\r
- * @param data         statistic data, including path name\r
- * @param parent       the caller (<code>ReDirStatistic</code>). This allows to deliver\r
- *                                     a context to this formatting routine (through derivation of\r
- *                                     <code>ReDirStatistic</code>)\r
- * @param line         OUT: the formatted line, the conclusion of the statistic data\r
- */\r
-void formatLikeDu(const ReDirStatisticData& data, ReDirStatistic& parent,\r
-    ReByteBuffer& line) {\r
-       line.setLength(0);\r
-       // Round up to the next KiByte:\r
-       line.appendInt(int((data.m_sizes + 1023) / 1024)).append("\t").append(\r
-           data.m_path);\r
-}\r
-\r
-/**\r
- * Formats a line in a standard way: MBytes, file count and directory count.\r
- *\r
- * This is a possible parameter of <code>ReDirStatistic::calculate()</code>.\r
- *\r
- * @param data         statistic data, including path name\r
- * @param parent       the caller (<code>ReDirStatistic</code>). This allows to deliver\r
- *                                     a context to this formatting routine (through derivation of\r
- *                                     <code>ReDirStatistic</code>)\r
- * @param line         OUT: the formatted line, the conclusion of the statistic data\r
- */\r
-void formatWithSizeFilesAndDirs(const ReDirStatisticData& data,\r
-    ReDirStatistic& parent, ReByteBuffer& line) {\r
-       line.setLength(0);\r
-       // Round up to the next KiByte:\r
-       char buffer[256];\r
-       _snprintf(buffer, sizeof buffer, "%14.6f MB %7d %7d\t", data.m_sizes / 1E6,\r
-           data.m_files, data.m_directories);\r
-       line.append(buffer, -1).append(data.m_path);\r
-}\r
-/**\r
- * Constructor.\r
- *\r
- * @param logger       logger for error handling\r
- */\r
-ReDirTouch::ReDirTouch(ReLogger* logger) :\r
-           ReTool(s_touchUsage, s_touchExamples, 1, 0, 0, false, logger),\r
-           m_buffer()\r
-//m_modified()\r
-//m_accessed()\r
-{\r
-       setFiletimeUndef(m_modified);\r
-       setFiletimeUndef(m_accessed);\r
-       // standard short options: D d O o P p T t v y Z z\r
-       m_programArgs.addString("accessed", i18n("the new access time.\n"\r
-               "Formats: absolute, relative to now, relative to the current filetime\n"\r
-               "[yyyy.mm.dd/]HH:MM:SS \n"\r
-               "now-<count>{s|m|h|d}\n"\r
-               "{+|-}<count>{s|m|h|d}"), 'a', "--accessed", false, NULL);\r
-       m_programArgs.addString("modified", i18n("the new modification time.\n"\r
-               "Formats: absolute, relative to now, relative to the current filetime\n"\r
-               "[yyyy.mm.dd/]HH:MM:SS\n"\r
-               "now-<count>{s|m|h|d}\n"\r
-               "{+|-}<count>{s|m|h|d}"), 'm', "--modified", false, NULL);\r
-       addStandardFilterOptions();\r
-}\r
-/**\r
- * Sets the filetime for the specified files.\r
- */\r
-void ReDirTouch::doIt() {\r
-       ReByteBuffer buffer;\r
-       if (m_programArgs.getString("modified", buffer)[0] != '\0')\r
-               m_modified = checkDate(buffer.str());\r
-       if (m_programArgs.getString("accessed", buffer)[0] != '\0')\r
-               m_accessed = checkDate(buffer.str());\r
-       if (filetimeIsUndefined(m_modified) && filetimeIsUndefined(m_accessed))\r
-               help("missing --modified and/or --accessed");\r
-       processFileArguments();\r
-       printSummary();\r
-}\r
-\r
-/**\r
- * Processes one directory.\r
- *\r
- * @param entry        the properties of the directory to process\r
- */\r
-void ReDirTouch::processDir(ReDirStatus_t* entry) {\r
-       processFile(entry);\r
-}\r
-\r
-static bool isAbsoluteTime(ReFileTime_t& time) {\r
-#if defined __linux__\r
-       static struct tm year1980 = { 0, 0, 0, 1, 1 - 1, 1980 - 1900 };\r
-       static time_t time1980 = mktime(&year1980);\r
-       return time.tv_sec >= time1980;\r
-#elif defined __WIN32__\r
-       static ReFileTime_t filetime1980 = {2148603904, 27846551};\r
-       bool rc = time > filetime1980;\r
-       return rc;\r
-#endif\r
-}\r
-static void addRelativeTime(ReFileTime_t& absTime,\r
-    const ReFileTime_t& relTime) {\r
-#if defined __linux__\r
-       if ((absTime.tv_nsec += relTime.tv_nsec) >= 1000 * 1000 * 1000) {\r
-               absTime.tv_nsec -= 1000 * 1000 * 1000;\r
-               absTime.tv_sec += 1;\r
-       }\r
-       absTime.tv_sec += relTime.tv_sec;\r
-#elif defined __WIN32__\r
-       uint64_t absValue = ((uint64_t) absTime.dwHighDateTime << 32) | absTime.dwLowDateTime;\r
-       uint64_t relValue = ((uint64_t) relTime.dwHighDateTime << 32) | relTime.dwLowDateTime;\r
-       absValue += relValue;\r
-       absTime.dwHighDateTime = (uint32_t) (absValue >> 32);\r
-       absTime.dwLowDateTime = (uint32_t) absValue;\r
-#endif\r
-}\r
-/**\r
- * Processes one file.\r
- *\r
- * @param entry        the properties of the file to process\r
- */\r
-void ReDirTouch::processFile(ReDirStatus_t* entry) {\r
-       ReFileTime_t modified = *entry->modified();\r
-       int countTimes = 0;\r
-       if (!filetimeIsUndefined(m_modified)) {\r
-               countTimes++;\r
-               if (isAbsoluteTime(m_modified))\r
-                       modified = m_modified;\r
-               else\r
-                       addRelativeTime(modified, m_modified);\r
-       }\r
-       ReFileTime_t accessed = *entry->accessed();\r
-       if (!filetimeIsUndefined(m_accessed)) {\r
-               countTimes++;\r
-               if (isAbsoluteTime(m_accessed))\r
-                       accessed = m_accessed;\r
-               else\r
-                       addRelativeTime(accessed, m_accessed);\r
-       }\r
-       const char* name = entry->fullName();\r
-       if (touch(name, modified, accessed, ReLogger::globalLogger()) == 0\r
-           && m_verboseLevel >= V_NORMAL) {\r
-               ReByteBuffer bufferTime;\r
-               ReByteBuffer bufferTime2;\r
-               if (m_verboseLevel == V_NORMAL) {\r
-                       if (countTimes == 2)\r
-                               fprintf(m_output, "%s | %s | %s\n",\r
-                                   ReDirStatus_t::filetimeToString(&modified, bufferTime),\r
-                                   ReDirStatus_t::filetimeToString(&accessed, bufferTime2),\r
-                                   name);\r
-                       else {\r
-                               ReDirStatus_t::filetimeToString(\r
-                                   filetimeIsUndefined(m_modified) ? &accessed : &modified,\r
-                                   bufferTime);\r
-                               fprintf(m_output, "%s %s\n", bufferTime.str(), name);\r
-                       }\r
-               } else {\r
-                       ReByteBuffer bufferTime3;\r
-                       ReByteBuffer bufferTime4;\r
-                       if (countTimes == 2)\r
-                               fprintf(m_output, "%s -> %s | %s -> %s | %s\n",\r
-                                   ReDirStatus_t::filetimeToString(entry->modified(),\r
-                                       bufferTime),\r
-                                   ReDirStatus_t::filetimeToString(&modified, bufferTime2),\r
-                                   ReDirStatus_t::filetimeToString(entry->accessed(),\r
-                                       bufferTime3),\r
-                                   ReDirStatus_t::filetimeToString(&accessed, bufferTime4),\r
-                                   name);\r
-                       else {\r
-                               ReDirStatus_t::filetimeToString(\r
-                                   filetimeIsUndefined(m_modified) ? &m_accessed : &m_modified,\r
-                                   bufferTime);\r
-                               fprintf(m_output, "%s -> %s %s\n",\r
-                                   ReDirStatus_t::filetimeToString(entry->modified(),\r
-                                       bufferTime2), bufferTime.str(), (char*) name);\r
-                       }\r
-               }\r
-       }\r
-}\r
-\r
-/**\r
- * Sets file times.\r
- *\r
- * @param filename             the name of the file\r
- * @param properties   contains modification and access time\r
- * @param logger               NULL or the logger\r
- */\r
-ReErrNo_t ReDirTouch::touch(const char* filename, const ReFileTime_t& modified,\r
-    const ReFileTime_t& accessed, ReLogger* logger) {\r
-#if defined __linux__\r
-       timeval times[2];\r
-       times[0].tv_sec = accessed.tv_sec;\r
-       times[0].tv_usec = accessed.tv_nsec / 1000;\r
-       times[1].tv_sec = modified.tv_sec;\r
-       times[1].tv_usec = modified.tv_nsec / 1000;\r
-       ReErrNo_t rc = utimes(filename, times) == 0 ? 0 : errno;\r
-\r
-#elif defined __WIN32__\r
-       ReErrNo_t rc = 0;\r
-       HANDLE handle = CreateFile(filename, FILE_WRITE_ATTRIBUTES,\r
-               FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING,\r
-               FILE_ATTRIBUTE_NORMAL, NULL);\r
-       if (handle == INVALID_HANDLE_VALUE)\r
-       rc = GetLastError();\r
-       else {\r
-               if (! SetFileTime(handle, (LPFILETIME) NULL, &accessed, &modified))\r
-               rc = GetLastError();\r
-               CloseHandle(handle);\r
-       }\r
-#endif\r
-       if (rc != 0 && logger != NULL)\r
-               logger->sayF(LOG_ERROR | CAT_FILE, LC_TOUCH_1,\r
-                   i18n("cannot change filetime: $1 (errno: $2)")).arg(filename).arg(\r
-               errno).end();\r
-       return rc;\r
-}\r
-\r
-/**\r
- * Prints a vector of lines.\r
- * \r
- * @param lines     a vector of lines without newline ('\n')\r
- */\r
-static void printField(const char** lines) {\r
-       for (int ix = 0; lines[ix] != NULL; ix++) {\r
-               printf("%s\n", lines[ix]);\r
-       }\r
-}\r
-\r
-/**\r
- * Prints a message how to use the module and exits.\r
- */\r
-void ReDirTools::usage(ReTool& tool) {\r
-       tool.programArgs().help(NULL, false, stdout);\r
-}\r
-\r
-/**\r
- * Prints an message how to use the program and exits.\r
- *\r
- * @param msg       an error message\r
- * @param msg2      an addition to the error message or NULL\r
- */\r
-void ReDirTools::usage(const char* msg, const char* msg2) {\r
-       printf("Version: %s\n", m_version);\r
-       printf("usage: dirtool <command> <opt>\n"\r
-               "call 'dirtool help' for more info\n");\r
-       if (msg != NULL)\r
-               printf("+++ %s%s\n", msg, msg2 == NULL ? "" : msg2);\r
-       exit(1);\r
-}\r
-\r
-/**\r
- * creates a subdirectory (and the parent directories if neccessary.\r
- *\r
- * @param path      the name of the subdir to create\r
- */\r
-void ReDirSync::makeDirWithParents(ReByteBuffer& path, int minWidth,\r
-    ReTraverser& traverser) {\r
-       if (!exists(path)) {\r
-               ReFileProperties_t* props = NULL;\r
-#if defined __linux__\r
-               ReDirStatus_t* entry = traverser.topOfStack();\r
-               props = entry == NULL ? NULL : &entry->m_status;\r
-#endif\r
-               makeDirectory(path.str(), minWidth, props, ReLogger::globalLogger());\r
-       }\r
-}\r
-/**\r
- * Constructor.\r
- *\r
- * @param logger       logger for error handling\r
- */\r
-ReDirSync::ReDirSync(ReLogger* logger) :\r
-           ReTool(s_syncUsage, s_syncExamples, 2, 0, 1, false, logger),\r
-           m_buffer() {\r
-       // standard short options: D d O o P p T t v y Z z\r
-       m_buffer.ensureSize(4u * 1024u * 1024u);\r
-       m_programArgs.addBool("add",\r
-           i18n("copies only files which does not exist on the target"), 'a',\r
-           "add", false);\r
-       m_programArgs.addBool("dry",\r
-           i18n("does nothing, but says what should be done"), 'Y', "dry", false);\r
-       m_programArgs.addInt("timediff",\r
-           i18n("filetime difference is considered to be equal\n"\r
-                   "if the difference is less than this value (in seconds)"), 'I',\r
-           "time-delta", 2);\r
-       m_programArgs.addBool("ignoredate",\r
-           i18n(\r
-               "the modification is recognized only by the different size (not time)"),\r
-           'i', "ignore-time", false);\r
-       m_programArgs.addBool("mustexist",\r
-           i18n("files which don't exist on the target will not be copied"), 'm',\r
-           "must-exist", false);\r
-       addStandardFilterOptions();\r
-}\r
-\r
-/**\r
- * Copies a file.\r
- *\r
- * @param entry                the source file info\r
- * @param target       the name of the target file\r
- */\r
-void ReDirSync::copyFile(ReDirStatus_t* entry, const char* target) {\r
-       ReFileProperties_t* props;\r
-#ifdef __linux__\r
-       props = &entry->m_status;\r
-#else\r
-       ReFileProperties_t properties;\r
-       properties.m_modified = *entry->modified();\r
-       properties.m_accessed = *entry->accessed();\r
-       properties.m_size = entry->fileSize();\r
-       props = &properties;\r
-#endif\r
-       copyFile(entry->fullName(), props, target, m_buffer,\r
-           ReLogger::globalLogger());\r
-}\r
-\r
-/**\r
- * Copies a file.\r
- *\r
- * @param source               the source file name\r
- * @param properties   NULL or the properties of the source file\r
- * @param target               the name of the target file\r
- * @param buffer               OUT: the reading uses this buffer<br>\r
- *                                             Set the capacity to make it more efficient\r
- * @param logger               NULL or the logger for error messages\r
- * @return                             <code>true</code>success<br>\r
- *                                             <code>false</code>error occurred\r
- */\r
-bool ReDirSync::copyFile(const char* source, ReFileProperties_t* properties,\r
-    const char* target, ReByteBuffer& buffer, ReLogger* logger) {\r
-       bool rc = false;\r
-#ifdef __linux__\r
-       struct stat info;\r
-       if (properties == NULL) {\r
-               if (stat(source, &info) == 0)\r
-                       properties = &info;\r
-               else {\r
-                       if (logger != NULL)\r
-                               logger->sayF(LOG_ERROR | CAT_FILE, LC_COPY_FILE_1,\r
-                                   i18n("could not find: $1 (errno: $2)")).arg(source).arg(\r
-                               errno).end();\r
-               }\r
-       }\r
-       FILE* fpSource = fopen(source, "rb");\r
-       if (fpSource == NULL) {\r
-               if (logger != NULL)\r
-                       logger->sayF(LOG_ERROR | CAT_FILE, LC_COPY_FILE_2,\r
-                           i18n("cannot open $1 (errno: $2)")).arg(source).arg(errno).end();\r
-       } else {\r
-               ReFileSize_t size =\r
-                   properties == NULL ? 0x7fffffff : properties->st_size;\r
-               FILE* fpTarget = fopen(target, "w");\r
-               if (fpTarget == NULL) {\r
-                       if (logger != NULL)\r
-                               logger->sayF(LOG_ERROR | CAT_FILE, LC_COPY_FILE_3,\r
-                                   i18n("cannot open $1 (errno: $2)")).arg(target).arg(errno)\r
-                                   .end();\r
-               } else {\r
-                       while (size > 0) {\r
-                               size_t blockSize = buffer.capacity();\r
-                               if ((int) blockSize > size)\r
-                                       blockSize = size;\r
-                               if (fread(buffer.buffer(), blockSize, 1, fpSource) != 1) {\r
-                                       if (logger != NULL)\r
-                                               logger->sayF(LOG_ERROR | CAT_FILE, LC_COPY_FILE_5,\r
-                                                   i18n("cannot read $1 (errno: $2)")).arg(source).arg(\r
-                                               errno).end();\r
-                                       break;\r
-                               }\r
-                               size_t written;\r
-                               if ((written = fwrite(buffer.buffer(), 1, blockSize, fpTarget))\r
-                                   != blockSize) {\r
-                                       if (logger != NULL)\r
-                                               logger->sayF(LOG_ERROR | CAT_FILE, LC_COPY_FILE_6,\r
-                                                   i18n("cannot write $1 [$2] (errno: $3)")).arg(\r
-                                                   target).arg(written).arg(errno).end();\r
-                                       break;\r
-                               }\r
-                               size -= blockSize;\r
-                       }\r
-                       rc = size == 0ll;\r
-                       fclose(fpTarget);\r
-                       if (properties != NULL)\r
-                               setProperties(target, properties, logger);\r
-               }\r
-               fclose(fpSource);\r
-       }\r
-#elif defined __WIN32__\r
-       BOOL cancel = false;\r
-       rc = CopyFileExA(source, target, NULL, NULL, &cancel, COPY_FILE_NO_BUFFERING) != 0;\r
-       int errNo = 0;\r
-       if (! rc)\r
-       errNo = GetLastError();\r
-#endif\r
-       return rc;\r
-}\r
-/**\r
- * Sets the file properties.\r
- *\r
- * @param fullName             the name of the file\r
- * @param properties   the properties like times and rights\r
- * @param logger               NULL or the logger for error messages\r
- * @return                             <code>true</code>: success<br>\r
- *                                             <code>false</code>: error occurred\r
- */\r
-bool ReDirSync::setProperties(const char* fullName,\r
-    ReFileProperties_t* properties, ReLogger* logger) {\r
-       bool rc = true;\r
-#if defined __linux__\r
-       timeval times[2];\r
-       times[0].tv_sec = properties->st_atim.tv_sec;\r
-       times[0].tv_usec = properties->st_atim.tv_nsec / 1000;\r
-       times[1].tv_sec = properties->st_mtim.tv_sec;\r
-       times[1].tv_usec = properties->st_mtim.tv_nsec / 1000;\r
-       if (utimes(fullName, times) != 0) {\r
-               if (logger != NULL)\r
-                       logger->sayF(LOG_ERROR | CAT_FILE, LC_SET_PROPERTIES_1,\r
-                           i18n("cannot change file times: $1 (errno: $2)")).arg(fullName)\r
-                           .arg(errno).end();\r
-               rc = false;\r
-       }\r
-       int rights = properties->st_mode & (S_IRWXO | S_IRWXG | S_IRWXU);\r
-       if (chmod(fullName, rights) != 0) {\r
-               if (logger != NULL)\r
-                       logger->sayF(LOG_ERROR | CAT_FILE, LC_SET_PROPERTIES_2,\r
-                           i18n("cannot change file modes: $1 (errno: $2)")).arg(fullName)\r
-                           .arg(errno).end();\r
-               rc = false;\r
-       }\r
-       if (chown(fullName, properties->st_uid, properties->st_gid) != 0) {\r
-               if (logger != NULL)\r
-                       logger->sayF(LOG_ERROR | CAT_FILE, LC_SET_PROPERTIES_3,\r
-                           i18n("cannot change file owner: $1 (errno: $2)")).arg(fullName)\r
-                           .arg(errno).end();\r
-               rc = false;\r
-       }\r
-#endif\r
-       return rc;\r
-}\r
-\r
-/**\r
- * Creates a directory and its parents (if neccessary).\r
- *\r
- * @param directory            the full name of the directory\r
- * @param properties   NULL or the properties of the new directory\r
- * @param logger               NULL or the logger for error messages\r
- * @return                             <code>true</code>success<br>\r
- *                                             <code>false</code>error occurred\r
- */\r
-bool ReDirSync::makeDirectory(const char* directory, int minLength,\r
-    ReFileProperties_t* properties, ReLogger* logger) {\r
-       bool rc = true;\r
-       ReByteBuffer path(directory);\r
-       int start = 0;\r
-#if defined __WIN32__\r
-       start = path.indexOf(':');\r
-#endif\r
-       path.ensureLastChar(OS_SEPARATOR_CHAR);\r
-       int ixSlash = start < 0 ? 0 : start;\r
-       struct stat info;\r
-       // for all parents and the full path itself:\r
-       while (ixSlash >= 0) {\r
-               ixSlash = path.indexOf(OS_SEPARATOR_CHAR, ixSlash + 1);\r
-               // is the slash in front of the first node, e.g. 'e:\'?\r
-               if (ixSlash == start + 1)\r
-                       // not a real node: take the next node\r
-                       ixSlash = path.indexOf(OS_SEPARATOR_CHAR, ixSlash + 1);\r
-               if (ixSlash >= 0) {\r
-                       // we handle only the next node:\r
-                       path.buffer()[ixSlash] = '\0';\r
-               }\r
-               // does the node exist?\r
-               if (lstat(path.str(), &info) != 0) {\r
-                       // no, then we make it:\r
-                       if (_mkdir(path.str(), ALLPERMS) != 0) {\r
-                               if (logger != NULL)\r
-                                       logger->sayF(LOG_ERROR | CAT_FILE, LC_MAKE_DIR_1,\r
-                                           i18n("could not make directory $1 (errno: $2)")).arg(\r
-                                           path.str()).arg(errno).end();\r
-                               rc = false;\r
-                               break;\r
-                       } else {\r
-#if defined __linux__\r
-                               setProperties(path.str(), properties);\r
-#endif\r
-                       }\r
-               }\r
-               if (ixSlash >= 0) {\r
-                       // restore the full path:\r
-                       path.buffer()[ixSlash] = OS_SEPARATOR_CHAR;\r
-               }\r
-       }\r
-       return rc;\r
-}\r
-\r
-/**\r
- * Synchronizes two directory trees.\r
- */\r
-void ReDirSync::doIt() {\r
-       ReDirEntryFilter filter;\r
-       const char* sep = OS_SEPARATOR;\r
-       ReByteBuffer buffer;\r
-       ReByteBuffer target(m_programArgs.arg(m_programArgs.argCount() - 1));\r
-       target.removeLastChar(OS_SEPARATOR_CHAR);\r
-       if (!exists(target))\r
-               help(i18n("target does not exist: $1"), target.str());\r
-       else if (!S_ISDIR(m_statInfo.st_mode))\r
-               help(i18n("target is not a directory: $1"), target.str());\r
-       size_t lengthTargetBase = target.length();\r
-       bool addOnly = m_programArgs.getBool("add");\r
-       int maxFileTimeDiff = m_programArgs.getInt("timediff");\r
-       bool dry = m_programArgs.getBool("dry");\r
-       bool ignoreDate = m_programArgs.getBool("ignoredate");\r
-       bool mustExist = m_programArgs.getBool("mustexist");\r
-       setFilterFromProgramArgs(filter);\r
-       int64_t sumSizes = 0;\r
-       int files = 0;\r
-       int treeFiles = 0;\r
-       int treeDirs = 0;\r
-       int64_t treeSumSizes = 0ll;\r
-       ReByteBuffer source, targetFile;\r
-       for (int ix = 0; ix < m_programArgs.argCount() - 1; ix++) {\r
-               source.set(m_programArgs.arg(ix), -1);\r
-               target.setLength(lengthTargetBase);\r
-               bool endsWithSlash = source.endsWith(sep, 1);\r
-               if (endsWithSlash)\r
-                       source.reduceLength();\r
-               if (!exists(source))\r
-                       help(i18n("source does not exist: $1"), source.str());\r
-               else if (!S_ISDIR(m_statInfo.st_mode))\r
-                       help(i18n("source is not a directory: $1"), source.str());\r
-               if (!endsWithSlash) {\r
-                       // the basename of the source will be appended to the target:\r
-                       int startNode = source.rindexOf(sep, 1, 0, source.length() - 1);\r
-                       target.appendChar(OS_SEPARATOR_CHAR);\r
-                       target.append(source.str() + startNode + 1, -1);\r
-               }\r
-               size_t ixSourceRelative = source.length();\r
-               size_t ixTargetRelative = target.length();\r
-\r
-               m_traverser.changeBase(source.str());\r
-               m_traverser.setPropertiesFromFilter(&filter);\r
-               int level;\r
-               ReDirStatus_t* entry;\r
-               ReByteBuffer line;\r
-               while ((entry = m_traverser.nextFile(level, &filter)) != NULL) {\r
-                       if (entry->isDirectory())\r
-                               continue;\r
-                       // append the new relative path from source to target:\r
-                       target.setLength(ixTargetRelative);\r
-                       target.append(entry->m_path.str() + ixSourceRelative, -1);\r
-                       if (!exists(target))\r
-                               makeDirWithParents(target, ixTargetRelative, m_traverser);\r
-                       targetFile.set(target).append(entry->node(), -1);\r
-                       const char* targetRelativePath = targetFile.str() + ixTargetRelative\r
-                           + 1;\r
-                       bool targetExists = exists(targetFile);\r
-                       if (!targetExists && mustExist) {\r
-                               if (m_verboseLevel == V_CHATTER)\r
-                                       fprintf(m_output, "-ignored: %s does not exist\n",\r
-                                           targetRelativePath);\r
-                               continue;\r
-                       }\r
-                       if (targetExists) {\r
-                               if (addOnly) {\r
-                                       if (m_verboseLevel >= V_CHATTER)\r
-                                               fprintf(m_output, "~ignored: %s exists\n",\r
-                                                   targetRelativePath);\r
-                                       continue;\r
-                               }\r
-                               if (ignoreDate && entry->fileSize() == m_statInfo.st_size) {\r
-                                       if (m_verboseLevel >= V_CHATTER)\r
-                                               fprintf(m_output, "_ignored: %s same size\n",\r
-                                                   targetRelativePath);\r
-                                       continue;\r
-                               }\r
-                               // target younger than source?\r
-                               int diff = int(\r
-                                   m_statInfo.st_mtime\r
-                                       - entry->filetimeToTime(entry->modified()));\r
-                               if (!ignoreDate && diff <= maxFileTimeDiff) {\r
-                                       if (m_verboseLevel >= V_CHATTER)\r
-                                               fprintf(m_output, "=ignored: %s same time\n",\r
-                                                   targetRelativePath);\r
-                                       continue;\r
-                               }\r
-                       }\r
-                       files++;\r
-                       sumSizes += entry->fileSize();\r
-                       if (m_verboseLevel >= V_NORMAL)\r
-                               fprintf(m_output, "%c%s%s\n", targetExists ? '!' : '+',\r
-                                   targetRelativePath, dry ? " would be copied" : "");\r
-                       if (!dry)\r
-                               copyFile(entry, targetFile.str());\r
-               }\r
-               treeFiles += m_traverser.files();\r
-               treeDirs += m_traverser.directories();\r
-               treeSumSizes += m_traverser.sizes();\r
-       }\r
-       if (m_verboseLevel >= V_SUMMARY) {\r
-               int duration = int(time(NULL) - m_start);\r
-               fprintf(m_output,\r
-                   i18n(\r
-                       "=== copied:    %02d:%02d sec  %7d file(s) %12.6f MByte (%.3f MB/sec).\n"\r
-                               "===   tree:  %5d dir(s) %7d file(s) %12.6f MByte\n"),\r
-                   duration / 60, duration % 60, files, sumSizes / 1E6,\r
-                   sumSizes / 1E6 / (duration == 0 ? 1 : duration), treeDirs,\r
-                   treeFiles, treeSumSizes / 1E6);\r
-       }\r
-}\r
-\r
-/**\r
- * Constructor.\r
- *\r
- * @param logger       logger for error handling\r
- */\r
-ReDirTCP::ReDirTCP(ReLogger* logger) :\r
-           ReTool(s_tcpUsage, s_tcpExamples, 1, 0, 0, true, logger) {\r
-       m_hasStandardArgs = false;\r
-       m_programArgs.addString("size", i18n("size of the message to send/receive\n"\r
-               "<string> is a number followed by an unit\n"\r
-               "units: b(yte) k(Byte) K(iByte) m(Byte), M(iByte)\n"\r
-               "Note: maximum: 16M-32=16777184\n"\r
-               "examples: -b1m --buffer-size=512K"), 'b', "size", false, "64K");\r
-       m_programArgs.addInt("port", i18n("port of the server/client"), 'p',\r
-           "--port", 58111);\r
-}\r
-\r
-/**\r
- * Lists the metadata of the specified files.\r
- */\r
-void ReDirTCP::doIt() {\r
-       int port = m_programArgs.getInt("port");\r
-       ReByteBuffer buffer;\r
-       int64_t bufferSize = checkSize(m_programArgs.getString("size", buffer));\r
-       // the protocol does not allow more than 16 MiByte because of the flags:\r
-       if (bufferSize > 16LL * 1024 * 1024 - 64LL)\r
-               help(i18n("buffersize exceeds 16777184 = 16MiByte - 32: "),\r
-                   buffer.str());\r
-       ReByteBuffer command = m_programArgs.arg(0);\r
-       if (command.isPrefixOf("server", -1, true)) {\r
-               ReTCPEchoServer server(port, m_logger);\r
-               server.setLogSendReceive(false);\r
-               server.listenForAll();\r
-       } else if (command.isPrefixOf("client", -1, true)) {\r
-               const char* ip = m_programArgs.arg(1);\r
-               ReByteBuffer direction("download");\r
-               int rounds = 10;\r
-               int interval = 5;\r
-               if (m_programArgs.argCount() > 2) {\r
-                       direction = m_programArgs.arg(2);\r
-                       if (!direction.isPrefixOf("download", -1, true)\r
-                           && !direction.isPrefixOf("upload", -1, true)\r
-                           && !direction.isPrefixOf("mixed", -1, true))\r
-                               help(\r
-                                   "unknown direction: $1 (use 'download', 'upload' or 'mixed')",\r
-                                   direction.str());\r
-               }\r
-               if (m_programArgs.argCount() > 3)\r
-                       rounds = atoi(m_programArgs.arg(3));\r
-               if (m_programArgs.argCount() > 4)\r
-                       interval = atoi(m_programArgs.arg(4));\r
-               if (tolower(direction.at(0)) == 'm')\r
-                       runMixedClient(ip, port, rounds, interval, (int) bufferSize);\r
-               else\r
-                       runOneThreadClient(ip, port, rounds, interval, (int) bufferSize,\r
-                           tolower(direction.at(0)) == 'u');\r
-       } else\r
-               help("unknown subcommand: $1", command.str());\r
-}\r
-void ReDirTCP::runMixedClient(const char* ip, int port, int rounds,\r
-    int interval, int bufferSize) {\r
-       help("not implemented: mixed");\r
-}\r
-void ReDirTCP::runOneThreadClient(const char* ip, int port, int rounds,\r
-    int interval, int bufferSize, bool upload) {\r
-       ReTCPClient client(m_logger);\r
-       if (client.connect(ip, port)) {\r
-               time_t start = time(NULL);\r
-               const char* command = upload ? "strlen" : "filldata";\r
-               ReByteBuffer message;\r
-               if (upload)\r
-                       message.appendChar('x', bufferSize);\r
-               else\r
-                       message.appendInt(bufferSize);\r
-               time_t lastPrint = start;\r
-               int64_t size = 0;\r
-               int duration = 0;\r
-               ReByteBuffer answer, data;\r
-               client.setLogSendReceive(false);\r
-               int64_t sizeCurrent = 0;\r
-               for (int ii = 0; ii < rounds; ii++) {\r
-                       client.send(command, message.str(), message.length());\r
-                       client.receive(answer, data);\r
-                       size += message.length() + data.length();\r
-                       sizeCurrent += message.length() + data.length();\r
-                       time_t now = time(NULL);\r
-                       if (now >= lastPrint + interval) {\r
-                               duration = int(now - start);\r
-                               int durationCurrent = int(now - lastPrint);\r
-                               printf("%2d: %9.3f MiByte %8.3f / %8.3f kiByte/sec %s\n", ii,\r
-                                   size / 1024.0 / 1024,\r
-                                   (double) sizeCurrent / durationCurrent / 1024,\r
-                                   (double) size / duration / 1024,\r
-                                   upload ? "up" : "down");\r
-                               lastPrint = now;\r
-                               sizeCurrent = 0;\r
-                       }\r
-               }\r
-               duration = int(time(NULL) - start);\r
-               if (duration == 0)\r
-                       duration = 1;\r
-               printf("%2d: %9.3f MiByte %8.3f kiByte %s/sec %s\n", rounds,\r
-                   size / 1024.0 / 1024, (double) size / duration / 1024,\r
-                   upload ? "up" : "down");\r
-\r
-       }\r
-}\r
-/**\r
- * Constructor.\r
- *\r
- * @param logger       logger for error handling\r
- */\r
-ReDirWhich::ReDirWhich(ReLogger* logger) :\r
-           ReTool(s_batchUsage, s_batchExamples, 0, 0, 0, true, logger) {\r
-// no standard options:\r
-       m_programArgs.addBool("all",\r
-           i18n("all files will be found, not only the first"), 'a', "all", false);\r
-       m_programArgs.addString("list",\r
-           i18n("a path list (separator see option 'separator'"), 'l', "list",\r
-           false,\r
-           NULL);\r
-       m_programArgs.addString("separator",\r
-           i18n("separator between the path elements"), 's', "separator", false,\r
-#if defined __linux__\r
-           ":"\r
-#elif defined __WIN32__\r
-           ";"\r
-#endif\r
-)      ;\r
-       m_programArgs.addString("variable", i18n("variable with the path list"),\r
-           'v', "variable", false, "PATH");\r
-       m_hasStandardArgs = false;\r
-}\r
-\r
-/**\r
- * Creates the batch file.\r
- */\r
-void ReDirWhich::doIt() {\r
-       ReByteBuffer value, path;\r
-       bool all = false;\r
-       ReStringList items;\r
-       char sep = 0;\r
-       m_programArgs.getString("list", path);\r
-       if (path.length() == 0) {\r
-               m_programArgs.getString("variable", value);\r
-               if (getenv(value.str()) == NULL)\r
-                       help("Umgebungsvariable nicht definiert: ", value.str());\r
-               else\r
-                       path = getenv(value.str());\r
-               m_programArgs.getString("separator", value);\r
-               sep = value.at(0);\r
-       }\r
-       items.split(path.str(), sep);\r
-       struct stat info;\r
-       ReByteBuffer full;\r
-       for (int ix = 0; ix < m_programArgs.argCount(); ix++) {\r
-               bool found = false;\r
-\r
-               ReByteBuffer arg(m_programArgs.arg(ix));\r
-               for (size_t ixItem = 0; ixItem < items.count(); ixItem++) {\r
-                       full.set(items.strOf(ixItem));\r
-                       if (arg.indexOf('*') < 0) {\r
-                               full.ensureLastChar(OS_SEPARATOR_CHAR);\r
-                               full.append(arg);\r
-                               found = stat(full.str(), &info) == 0;\r
-                               if (found)\r
-                                       printf("%s\n", full.str());\r
-                       } else {\r
-                               ReDirectory dir(full.str());\r
-                               if (dir.findFirst(arg.str(), false)) {\r
-                                       do {\r
-                                               printf("%s%c%s\n", full.str(), OS_SEPARATOR_CHAR,\r
-                                                   dir.currentFile());\r
-                                       } while (dir.findNext());\r
-                               }\r
-                       }\r
-                       if (found && !all)\r
-                               break;\r
-               }\r
-       }\r
-}\r
-\r
-/**\r
- * Tests whether a abrevation of an argument is given.\r
- * @param full      the full name\r
- * @param part      the part to test\r
- * @return          <code>true</code>: part is a prefix of full\r
- */\r
-static bool isArg(const char* full, const char* part) {\r
-       ReByteBuffer fullArg(full);\r
-       bool rc = fullArg.startsWith(part, -1);\r
-       return rc;\r
-}\r
-\r
-/**\r
- * Gets the arguments for the "help" command and execute this.\r
- *\r
- * @param argc      the number of arguments\r
- * @param argav     the argument vector\r
- */\r
-void ReDirTools::help(int argc, const char* argv[]) {\r
-       if (argc <= 1)\r
-               printField(s_helpSummary);\r
-       else {\r
-               argc--;\r
-               argv++;\r
-               const char* arg0 = argv[0];\r
-               if (isArg("batch", arg0)) {\r
-                       ReDirBatch(m_logger).help(NULL);\r
-               } else if (isArg("checksum", arg0)) {\r
-                       ReDirChecksum(m_logger).help(NULL);\r
-               } else if (_stricmp("delete", arg0) == 0) {\r
-                       ReDirDelete(m_logger).help(NULL);\r
-               } else if (isArg("list", arg0)) {\r
-                       ReDirList(m_logger).help(NULL);\r
-               } else if (isArg("help", arg0)) {\r
-                       printField(s_helpSummary);\r
-               } else if (isArg("random", arg0)) {\r
-                       ReDirRandom(m_logger).help(NULL);\r
-               } else if (isArg("statistic", arg0)) {\r
-                       ReDirStatistic(m_logger).help(NULL);\r
-               } else if (isArg("test", arg0)) {\r
-                       void testAll();\r
-                       testAll();\r
-               } else if (isArg("tcp", arg0)) {\r
-                       ReDirTCP(m_logger).help(NULL);\r
-               } else if (isArg("touch", arg0)) {\r
-                       ReDirTouch(m_logger).help(NULL);\r
-               } else if (isArg("which", arg0)) {\r
-                       ReDirWhich(m_logger).help(NULL);\r
-               } else\r
-                       printf("+++ unknown sub command: %s\n", arg0);\r
-       }\r
-}\r
-\r
-/**\r
- * Executes a command.\r
- *\r
- * @param argc      the number of arguments\r
- * @param argv         the argument vector\r
- * @param tool         the tool which realizes the command\r
- */\r
-void ReDirTools::run(int argc, const char* argv[], ReTool& tool) {\r
-       tool.run(argc, argv);\r
-}\r
-/**\r
- * Gets the arguments for any command and execute this.\r
- *\r
- * @param argc      the number of arguments\r
- * @param orgArgv   the argument vector\r
- */\r
-int ReDirTools::main(int argc, char* orgArgv[]) {\r
-       ReDirTools tools;\r
-       if (argc < 0) {\r
-               argc = 0;\r
-               for (int ix = 0; orgArgv[ix] != NULL; ix++)\r
-                       argc++;\r
-       }\r
-       const char** argv = (const char**) orgArgv;\r
-       if (argc < 2) {\r
-               tools.help(0, argv);\r
-               exit(1);\r
-       }\r
-       argc--;\r
-       argv++;\r
-       m_logger = ReLogger::globalLogger();\r
-       const char* arg0 = argv[0];\r
-       if (isArg("batch", arg0))\r
-               ReDirBatch(m_logger).run(argc, argv);\r
-       else if (isArg("checksum", arg0))\r
-               ReDirChecksum(m_logger).run(argc, argv);\r
-       else if (_stricmp("delete", arg0) == 0)\r
-               ReDirDelete(m_logger).run(argc, argv);\r
-       else if (isArg("help", arg0))\r
-               tools.help(argc, argv);\r
-       else if (isArg("list", arg0))\r
-               ReDirList(m_logger).run(argc, argv);\r
-       else if (isArg("random", arg0))\r
-               ReDirRandom(m_logger).run(argc, argv);\r
-       else if (isArg("statistic", arg0))\r
-               ReDirStatistic(m_logger).run(argc, argv);\r
-       else if (isArg("synchronize", arg0))\r
-               ReDirSync(m_logger).run(argc, argv);\r
-       else if (isArg("tcp", arg0))\r
-               ReDirTCP(m_logger).run(argc, argv);\r
-       else if (isArg("touch", arg0))\r
-               ReDirTouch(m_logger).run(argc, argv);\r
-       else if (isArg("which", arg0))\r
-               ReDirWhich(m_logger).run(argc, argv);\r
-       else if (isArg("test", arg0)) {\r
-               void testAll();\r
-               testAll();\r
-       } else\r
-               tools.usage("unknown command: ", argv[1]);\r
-       ReTCPServer::globalClose();\r
-       return 0;\r
-}\r
-\r
+/*
+ * ReDirTools.cpp
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
+ * Do what you want.
+ * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
+ * The latest sources: https://github.com/republib
+ */
+
+#include "base/rebase.hpp"
+#include "math/remath.hpp"
+#include "os/reos.hpp"
+#include "net/renet.hpp"
+
+enum LOCATION_DIRTOOL {
+       LC_COPY_FILE_1 = LOC_FIRST_OF(LOC_DIRTOOLS), // 50101
+       LC_COPY_FILE_2, // 50102
+       LC_COPY_FILE_3, // 50103
+       LC_COPY_FILE_4, // 50104
+       LC_COPY_FILE_5, // 50105
+       LC_COPY_FILE_6, // 50106
+       LC_MAKE_DIR_1,  // 50107
+       LC_MAKE_DIR_2,  // 50108
+       LC_SET_PROPERTIES_1,    // 50109
+       LC_SET_PROPERTIES_2,    // 50110
+       LC_SET_PROPERTIES_3,    // 50111
+       LC_TOUCH_1,                             // 50112
+       LC_CALCULATE_CHECKSUM_1,        // 50113
+       LC_BUILD_DIRECTORY_1,           // 50114
+       LC_COMPARE_STORAGE_1,           // 50115
+       LC_COMPARE_STORAGE_2,           // 50116
+       LC_COMPARE_STORAGE_3,           // 50117
+       LC_COMPARE_STORAGE_4,           // 50118
+       LC_COMPARE_STORAGE_5,           // 50119
+       LC_COMPARE_STORAGE_6,           // 50120
+       LC_COMPARE_STORAGE_7,           // 50121
+       LC_COMPARE_DIR_1,                       // 50122
+       LC_DELETE_1,                            // 50123
+};
+const char* ReDirTools::m_version = "2015.03.22";
+ReLogger* ReDirTools::m_logger = NULL;
+
+static const char* s_helpSummary[] = { "dirtool or dt <command> <opts>",
+    "   Useful commands around directory trees.",
+    "   Type 'dirtool help <command>' for more help.", "<command>:",
+    "batch         produce output to handle the found files with a script",
+    "checksum      shows the checksum (MD5 or other) of the selected files",
+    "delete        delete the selected files",
+    "help          shows info about the arguments/options",
+    "list          shows the meta data of the selected files",
+    "random        displays random numbers",
+    "statistic     shows statistics about a direcctory tree",
+    "synchronize   copies only modified or new files from",
+    "              from a source directory tre to a target",
+    "touch         sets the filetimes",
+    "which         finds a file in a path list (like PATH)",
+    NULL };
+
+const char* s_batchUsage[] = {
+    "<command>: batch [<opts>] <dir_or_file1> [<dir_or_file2> ...]",
+    "   produces output usable for a batch file (script)",
+    "   all found files can be processed with a given script",
+    "   each line starts (usually) with a script name (see -c)",
+    "   then it follows the full filename of the found file",
+    "   use --arguments or --script to configure the output line",
+    NULL };
+const char* s_deleteExamples[] = {
+    "dirtool delete --basename-pattern=;*~;*.bak e:\\data",
+    "dirtool delete /tmp/log",
+    NULL };
+
+const char* s_deleteUsage[] = {
+    "<command>: delete [<opts>] <dir_or_file1> [<dir_or_file2> ...]",
+    "   delete the specified files",
+    NULL };
+const char* s_batchExamples[] = {
+    "dirtool batch -cbackup.bat --basename-pattern=;*.txt;*.doc e:\\data",
+    "dirtool batch --type=r '--arguments=backup.sh !basename! !path!' usr etc",
+    NULL };
+
+const char* s_listUsage[] = {
+    "<command>: l(ist) [<opts>] <dir_or_file1> [<dir_or_file2> ...]",
+    "   lists the metadata (size, modification date ...) of the selected files",
+    NULL };
+const char* s_listExamples[] =
+    { "dirtool list --min-size=10M e:\\data",
+        "dirtool li --type=f -y7d --size=10M -p;*.cpp;*.hpp;Makefile*;-*~ /home/data",
+        NULL };
+
+const char* s_checksumUsage[] =
+    { "<command>: c(hecksum) [<opts>] <dir_or_file1> [<dir_or_file2> ...]",
+        "   shows a check sum of the given files or manages a checksum storage file in each directory for observing",
+        NULL };
+const char* s_checksumExamples[] = { "dirtool ch --buffer-size=512 e:\\data",
+    "dirtool check -cList -p;*.iso /home/iso /down/debian.iso",
+    "dirtool checksum -cBuild -aMD5 /home",
+    "dirtool checksum --command=Compare /home",
+    "dirtool checksum -cUpdate -O/var/log/checksum.log /home",
+    NULL };
+const char* s_randomUsage[] = { "<command>: r(andom) <opts> count until [from]",
+    "  displays random numbers.", "  <count> count of generated numbers",
+    "  <until> the maximum (including) value of the numbers",
+    "  <from>  the minimum (including) value of the numbers. Default: 1",
+    NULL };
+const char* s_randomExamples[] = { "dirtool ra --multiple -w3 10 33",
+    "dirtool rand -s 6 49", "dirtool rand --sort --width=1 5 50",
+    "dirtool rand --sort --width=1 2 10",
+    NULL };
+
+static const char* s_statisticUsage[] =
+    { "<command>: st(atistic)  [<opts>] <path1> [<path2> ...] [<depth>]",
+        "          shows a statistic about a directory tree",
+        "<path>    a directory path: relative or absolute",
+        "<depth>   0: only the summary of <path> will be shown",
+        "          1: shows the summery of each subdir of <path> and the total",
+        "          n: shows the summery of each subdir until level <n> and the total",
+        "          default: 1", "<opts_stat>:",
+        NULL };
+const char* s_statisticExamples[] =
+    { "dirtool st -q -t0 e:\\windows",
+        "dirtool statistic --quiet --kbyte --trace-interval=60 ../mail ../remember 2",
+        "dirtool stat -q --kbyte d:data 2",
+        NULL };
+
+const char* s_syncUsage[] =
+    { "<command>: sy(nchronize) <opts> <source1> [<source2> ...] <target>",
+        "   Synchronizes the content of a directory tree with another.",
+        "   Newer or missing files will be copied",
+        "   If a source name ends with the separator (/ in linux, \\ in win) the target",
+        "   will be completed with the basename of the source.", "   Example:",
+        "   'sync home backup' copies the file home/x.txt to backup/home/x.txt",
+        "   'sync data/ backup/data2' copies the file home/x.txt to backup/data2/x.txt",
+        NULL };
+const char* s_syncExamples[] = {
+    "dirtool sync --basename-pattern=;*.txt;*.doc e:\\data\\ d:\\backup\\data2",
+    "dirtool sync --type=r --max-size=1G usr etc /media/backup",
+    NULL };
+
+const char* s_tcpUsage[] = {
+    "<command>: tcp [<opts>] <subcommand> [<param> ...]",
+    "   test tool for network test", "<subcommand>:", "   server",
+    "   client <ip> [<direction> [<rounds> [<print_interval>]]]",
+    "   <ip>: URL of the server", "   <rounds>: number of messages to send",
+    "   <direction>: 'upload', 'download' or 'mixed'",
+    NULL };
+const char* s_tcpExamples[] = { "dirtool tcp -p 5555 server",
+    "dirtool tcp -p 5555 client localhost download 10000 10",
+    "dirtool tcp -p 5555 --buffer-size=1024 client 192.168.7.3 upload 10 25",
+    NULL };
+
+const char* s_touchUsage[] =
+    { "<command>: touch [<opts>] <dir_or_file1> [<dir_or_file2> ...]",
+        "   sets the filetimes (modification and/or access time) of the selected files",
+        NULL };
+const char* s_touchExamples[] = {
+    "dirtool touch -p;*.csv -m2014.12.1/8:12 e:\\data e:\\history\\readme.txt",
+    "dirtool touch -p;*.cpp --modified=now-4d12H /home/data",
+    "dirtool touch -p;*.csv -m+1m . e:\\data e:\\history\\readme.txt",
+    NULL };
+
+const char* s_whichUsage[] = {
+    "<command>: w(hicht) [<opts>] <file_or_pattern1> [<file_or_pattern2> ...]",
+    "   finds a file in a path list (like PATH)",
+    NULL };
+const char* s_whichExamples[] = {
+    "dirtool which --list=JAVA_CLASSPATH org.jdom.jar *.jdom*.jar",
+    "dirtool which find.exe",
+    NULL };
+/**
+ * Constructor.
+ *
+ * @param usage     a string vector with a message how to use the command
+ * @param example   a string vector with some examples how to use the command
+ */
+ReDirOptions::ReDirOptions(const char* usage[], const char* examples[]) :
+           ReTraceUnit(INT_MAX, INT_MAX),
+           m_programArgs(usage, examples),
+           m_nodePatterns(),
+           m_pathPatterns(),
+           m_compoundUsage(NULL),
+           m_countCompoundUsage(0),
+           m_output(stdout),
+           m_verboseLevel(V_NORMAL) {
+       m_nodePatterns.setIgnoreCase(true);
+       m_pathPatterns.setIgnoreCase(true);
+}
+/**
+ * Destructor.
+ */
+ReDirOptions::~ReDirOptions() {
+       close();
+       delete[] m_compoundUsage;
+}
+/**
+ * Adds a usage component to the compound usage message list.
+ * @param usage     a string vector containing a part of the usage message
+ */
+void ReDirOptions::addCompoundUsage(const char** usage) {
+       int start = 0;
+       while (m_compoundUsage[start] != NULL) {
+               if (++start >= m_countCompoundUsage)
+                       assert(false);
+       }
+       for (int ix = 0; usage[ix] != NULL; ix++) {
+               if (start + ix > m_countCompoundUsage) {
+                       assert(false);
+                       break;
+               }
+               m_compoundUsage[start + ix] = usage[ix];
+       }
+}
+
+/**
+ * Adds the standard filter options.
+ */
+void ReDirOptions::addStandardFilterOptions() {
+       // standard short options: D d O o P p T t v y Z z
+       m_programArgs.addInt("maxdepth",
+           i18n("the depth of the subdirectory is lower or equal <number>\n"
+                   "0: search is done only in the base directory"), 'D', "max-depth",
+           512);
+       m_programArgs.addInt("mindepth",
+           i18n("the depth of the subdirectory is greater or equal <number>\n"
+                   "0: search is done in all subdirectories"), 'd', "min-depth", 0);
+       m_programArgs.addString("output", i18n("the name of the output file.\n"
+               "The output will written to this file instead of stdout"), 'O',
+           "output-file", false, NULL);
+       m_programArgs.addString("older",
+           i18n(
+               "the modification date is older than <string>\n"
+                       "<string> is a date (e.g. 2015.02.17) or number followed by an unit\n"
+                       "units: m(inutes) h(hours), d(days). Default: m(inutes)\n"
+                       "examples: -o25 --older-than=30d -o24h -o2009.3.2/12:00 -o1999.01.01"),
+           'o', "older-than", false, NULL);
+       m_programArgs.addString("pathpattern",
+           i18n(
+               "a list of patterns for the path (without basename)\n"
+                       "the separator is the first character of the list\n"
+                       "Each pattern can contain '*' as wildcard\n"
+                       "If the first character is '-' the pattern is a 'not pattern':\n"
+                       "A directory will be entered if at least one of the positive patterns\n"
+                       "and none of the 'not patterns' matches\n"
+                       "examples:\n"
+                       "';music;pic*' enters music and xy/Music and PIC and pictures but not xy/pic and img\n"
+                       "';*;-.git;.hg' ignores .git and xy/z/.git and .ht"), 'P',
+           "path-pattern", false, NULL);
+       m_programArgs.addString("nodepattern",
+           i18n(
+               "a list of patterns for the basename (name without path) separated by ';'\n"
+                       "Each pattern can contain '*' as wildcard\n"
+                       "If the first character is '-' the pattern is a 'not pattern':\n"
+                       "A file will be found if at least one of the positive patterns and none\n"
+                       "of the 'not patterns' matches\n"
+                       "examples: '*.cpp;*.hpp;Make*' '*;-*.bak;-*~"), 'p',
+           "basename-pattern", false, NULL);
+       m_programArgs.addString("verbose",
+           i18n(
+               "verbose level: 0: no info, 1: summary only, 2: normal, 3: chatter mode, 4: debug"),
+           'v', "verbose", false, "1");
+       m_programArgs.addInt("trace",
+           i18n("all <number> seconds the current path will be traced\n"
+                   "0: no trace"), 'T', "trace-interval", 0);
+       m_programArgs.addString("type", i18n("the file type\n"
+               "<string> is a list of <v> values:\n"
+               "<v>: b(lock) c(har) d(irectory) (l)i(nkdir) l(ink) o(ther)\n"
+               "     p(ipe) s(ocket) r(egular)\n"
+               "<v>-sets: S(pecial)=bcspo N(ondir)=Slr\n"
+               "examples: -td --type=dr -tNi"), 't', "type", false, NULL);
+       m_programArgs.addString("younger",
+           i18n(
+               "the modification date is younger than <string>\n"
+                       "<string> is a date (e.g. 2015.02.17) or number followed by an unit\n"
+                       "units: m(inutes) h(hours), d(days). Default: m(inutes)"), 'y',
+           "younger-than", false, NULL);
+       m_programArgs.addString("maxsize",
+           i18n(
+               "the filesize is greater or equal <string>\n"
+                       "<string> is a number followed by an unit\n"
+                       "units: b(yte) k(Byte) K(iByte) m(Byte), M(iByte), g(Byte) G(iByte)\n"
+                       "examples: -Z50m --max-size=1G"), 'Z', "max-size", false, NULL);
+       m_programArgs.addString("minsize",
+           i18n(
+               "the filesize is greater or equal <string>\n"
+                       "<string> is a number followed by an unit\n"
+                       "units: b(yte) k(Byte) K(iByte) m(Byte), M(iByte), g(Byte) G(iByte)\n"
+                       "examples: -z50m --min-size=1G"), 'z', "min-size", false, NULL);
+}
+
+/**
+ * Checks whether the given value is a time expression.
+ *
+ * Possible: <date> <date_time> <n><unit>
+ * Units: m(inutes) h(our) d(ays) s(econds)
+ *
+ * @param value     value to check
+ * @return          the value converted into the absolute (or relative) time
+ * @throws          ReOptionExecption
+ */
+ReFileTime_t ReDirOptions::checkDate(const char* value) {
+       ReByteBuffer theValue(value, -1);
+       time_t rcTime;
+       if (theValue.count(".") == 2) {
+               // a date:
+               int year, month, day;
+               int hour = 0;
+               int minute = 0;
+               int sec = 0;
+               switch (sscanf(value, "%d.%d.%d/%d:%d:%d", &year, &month, &day, &hour,
+                   &minute, &sec)) {
+               case 3:
+               case 4:
+               case 5:
+               case 6: {
+                       if (year < 1980)
+                               throw ReOptionException(&m_programArgs,
+                                   i18n("date < 1980.01.01: $1"), value);
+                       struct tm time;
+                       memset(&time, 0, sizeof time);
+                       time.tm_year = year - 1900;
+                       time.tm_mon = month - 1;
+                       time.tm_mday = day;
+                       time.tm_hour = hour;
+                       time.tm_min = minute;
+                       time.tm_sec = sec;
+                       rcTime = mktime(&time);
+                       break;
+               }
+               default:
+                       throw ReOptionException(&m_programArgs,
+                           i18n(
+                               "invalid date/date-time value: $1 yyyy.mm.dd/hh:MM expected"),
+                           value);
+               }
+       } else if (theValue.count(":") >= 1) {
+               // a time:
+               int hour = 0;
+               int minute = 0;
+               int sec = 0;
+               switch (sscanf(value, "%d:%d:%d", &hour, &minute, &sec)) {
+               case 2:
+               case 3: {
+                       // the time (today)
+                       rcTime = time(NULL) / 86400 * 86400 + hour * 3600 + minute * 60
+                           + sec;
+                       break;
+               }
+               default:
+                       throw ReOptionException(&m_programArgs,
+                           i18n("invalid time value: $1. HH:MM:SS expected"), value);
+               }
+       } else {
+               // a time distance value:
+               char unit = 'm';
+               int count = 0;
+               int negativeFactor = 1;
+               bool fromNow = theValue.startsWith("now");
+               if (fromNow) {
+                       theValue.remove(0, 3);
+               }
+               char cc = theValue.at(0);
+               if (cc == '-' || cc == '+') {
+                       if (cc == '-')
+                               negativeFactor = -1;
+                       theValue.remove(0, 1);
+               }
+               switch (sscanf(theValue.str(), "%d%c", &count, &unit)) {
+               case 1:
+               case 2:
+                       switch (unit) {
+                       case 's':
+                               break;
+                       case 'm':
+                               count *= 60;
+                               break;
+                       case 'h':
+                               count *= 60 * 60;
+                               break;
+                       case 'd':
+                               count *= 24 * 60 * 60;
+                               break;
+                       default:
+                               throw ReOptionException(&m_programArgs,
+                                   i18n(
+                                       "invalid unit $1. expected: s(econds) m(inutes) h(ours) d(ays)"),
+                                   value);
+                       }
+                       rcTime = count * negativeFactor;
+                       break;
+               default:
+                       throw ReOptionException(&m_programArgs,
+                           i18n(
+                               "invalid relative time value $1 (<number><unit> expected). <unit>: s m h d"),
+                           value);
+               }
+               if (fromNow)
+                       rcTime += time(NULL);
+       }
+       ReFileTime_t rc;
+       ReDirStatus_t::timeToFiletime(rcTime, rc);
+       return rc;
+}
+/**
+ * Checks whether the given value is a time expression.
+ *
+ * Possible: <date> <date_time> <n><unit>
+ * Units: m(inutes) h(our) d(ays)
+ *
+ * @param value     value to check
+ * @return          the value (multiplied with the unit factor)
+ * @throws          ReOptionExecption
+ */
+int64_t ReDirOptions::checkSize(const char* value) {
+       int64_t rc = 0;
+       char unit = 'b';
+       switch (sscanf(value, "%lld%c", (long long int*) &rc, &unit)) {
+       case 1:
+       case 2:
+               switch (unit) {
+               case 'b':
+                       break;
+               case 'k':
+                       rc *= 1000;
+                       break;
+               case 'K':
+                       rc *= 1024;
+                       break;
+               case 'm':
+                       rc *= 1000 * 1000;
+                       break;
+               case 'M':
+                       rc *= 1024 * 1024;
+                       break;
+               case 'g':
+                       rc *= 1000LL * 1000 * 1000;
+                       break;
+               case 'G':
+                       rc *= 1024LL * 1024 * 1024;
+                       break;
+               default:
+                       throw ReOptionException(&m_programArgs,
+                           i18n("invalid <unit>: $1. Expected: b k K m M g G"), value);
+               }
+               break;
+       default:
+               throw ReOptionException(&m_programArgs,
+                   i18n(
+                       "invalid size value: $1 expected: <number><unit> <unit>: b k K m M g G"),
+                   value);
+       }
+       return rc;
+}
+/**
+ * Checks whether the given value is a valid pattern list.
+ *
+ * The first character must be a separator (not '*', '.'  and not a letter)
+ *
+ * @param value     value to check
+ * @return          the <cpde>value</code> (for chaining)
+ * @throws          ReOptionExecption
+ */
+const char* ReDirOptions::checkPatternList(const char* value) {
+       if (isalnum(*value) || *value == '_' || *value == '*' || *value == '.'
+           || *value == '-')
+               throw ReOptionException(&m_programArgs,
+                   i18n("invalid separator (first character): $1 use ';' instead"),
+                   value);
+       if (strchr(value, OS_SEPARATOR_CHAR) != NULL)
+               throw ReOptionException(&m_programArgs,
+                   i18n("slash not allowed in pattern list: $1"), value);
+       return value;
+}
+/**
+ * Checks whether the given value is a valid filetype list.
+ *
+ * @param value     value to check
+ * @return          the bitmask
+ * @throws          ReOptionExecption
+ */
+ReDirStatus_t::Type_t ReDirOptions::checkType(const char* value) {
+       int rc = ReDirStatus_t::TF_UNDEF;
+       while (*value != '\0') {
+               switch (*value) {
+               case 'b':
+                       rc |= ReDirStatus_t::TF_BLOCK;
+                       break;
+               case 'c':
+                       rc |= ReDirStatus_t::TF_CHAR;
+                       break;
+               case 'd':
+                       rc |= ReDirStatus_t::TF_SUBDIR;
+                       break;
+               case 'i':
+                       rc |= ReDirStatus_t::TF_LINK_DIR;
+                       break;
+               case 'l':
+                       rc |= ReDirStatus_t::TF_LINK;
+                       break;
+               case 'o':
+                       rc |= ReDirStatus_t::TF_OTHER;
+                       break;
+               case 'p':
+                       rc |= ReDirStatus_t::TF_PIPE;
+                       break;
+               case 's':
+                       rc |= ReDirStatus_t::TF_SOCKET;
+                       break;
+               case 'r':
+                       rc |= ReDirStatus_t::TF_REGULAR;
+                       break;
+               case 'S':
+                       rc |= ReDirStatus_t::TC_SPECIAL;
+                       break;
+               case 'N':
+                       rc |= ReDirStatus_t::TC_NON_DIR;
+                       break;
+               case ' ':
+               case ',':
+                       break;
+               default:
+                       throw ReOptionException(&m_programArgs,
+                           i18n("invalid type: $1 Expected: b(lock) c(har) d(irectory)"
+                                   " (l)i(nkdir) l(ink) o(ther) p(ipe) s(ocket) r(egular)"
+                                   " S(pecial=bcspo) N(ondir=Slr)"), value);
+               }
+               value++;
+       }
+       return (ReDirStatus_t::Type_t) rc;
+}
+
+/**
+ * Prints a help message, the error message and exits.
+ *
+ * @param errorMessage  the error message.
+ * @param message2      an additional message
+ */
+
+void ReDirOptions::help(const char* errorMessage, const char* message2) const {
+       ReByteBuffer msg;
+       if (errorMessage != 0)
+               msg.append(errorMessage, -1);
+       if (message2 != NULL)
+               msg.append(message2, -1);
+       m_programArgs.help(msg.str(), false, stdout);
+       exit(1);
+}
+
+/**
+ * Checks the correctness of the standard filter options.
+ * 
+ * @throws 
+ */
+void ReDirOptions::checkStandardFilterOptions() {
+       ReByteBuffer buffer;
+       if (m_programArgs.getString("older", buffer)[0] != '\0')
+               checkDate(buffer.str());
+       if (m_programArgs.getString("younger", buffer)[0] != '\0')
+               checkDate(buffer.str());
+       if (m_programArgs.getString("type", buffer)[0] != '\0')
+               checkType(buffer.str());
+       if (m_programArgs.getString("maxsize", buffer)[0] != '\0')
+               checkSize(buffer.str());
+       if (m_programArgs.getString("minsize", buffer)[0] != '\0')
+               checkSize(buffer.str());
+       if (m_programArgs.getString("nodepattern", buffer)[0] != '\0')
+               checkPatternList(buffer.str());
+       if (m_programArgs.getString("pathpattern", buffer)[0] != '\0')
+               checkPatternList(buffer.str());
+       if (m_programArgs.getString("verbose", buffer)[0] != '\0') {
+               unsigned level = V_NORMAL;
+               if (ReStringUtils::lengthOfUnsigned(buffer.str(), -1, &level)
+                   != (int) buffer.length())
+                       help(i18n("verbose level is not a number (or '')"), buffer.str());
+               else
+                       m_verboseLevel = VerboseLevel(level);
+       }
+}
+
+/**
+ * Frees the resources.
+ */
+void ReDirOptions::close() {
+       if (m_output != stdout && m_output != stderr) {
+               fclose(m_output);
+       }
+       m_output = stdout;
+}
+
+/**
+ * Initializes the compound usage message array.
+ *
+ * @param size      the size of the array: size = (field1 + field2 + ...) * sizeof(const char*)
+ */
+void ReDirOptions::initCompoundUsage(size_t size) {
+       delete[] m_compoundUsage;
+       int count = size / sizeof m_compoundUsage[0];
+       m_compoundUsage = new const char*[count];
+       memset(m_compoundUsage, 0, size);
+       m_countCompoundUsage = count;
+}
+
+/**
+ * Optimizes the path patterns.
+ *
+ * For all patterns of the list:
+ * <ul><li>remove a trailing "\" and "\*"</li>
+ * <li>change "*\xy" to "*\xy" and "xy" (finds xy in the root directory)</li>
+ * <li>replaces "/" with the os specific path separator</li>
+ * </ul>
+ *
+ * @param buffer       the pattern list as string, e.g. ";*;-cache"
+ */
+void ReDirOptions::optimizePathPattern(ReByteBuffer& buffer) {
+       ReStringList list;
+       ReStringList rootList;
+       list.split(buffer.str() + 1, buffer.str()[0]);
+       buffer.replaceAll(OS_SEPARATOR, 1, "/", 1);
+       ReByteBuffer item;
+       for (int ix = 0; ix < (int) list.count(); ix++) {
+               item.set(list.strOf(ix), -1);
+               if (item.endsWith("/*"))
+                       item.setLength(item.length() - 2);
+               item.removeLastChar('/');
+               bool notAnchored = item.startsWith("*/") || item.startsWith("-*/");
+               item.replaceAll("/", 1, OS_SEPARATOR, 1);
+               list.replace(ix, item.str());
+               if (notAnchored) {
+                       item.remove(item.str()[0] == '-' ? 1 : 0, 2);
+                       rootList.append(item.str(), 0);
+               }
+       }
+       if (rootList.count() > 0) {
+               list.append(rootList);
+       }
+       item.set(buffer.str(), 1);
+       buffer.set(item);
+       list.join(item.str(), buffer, true);
+}
+/**
+ * Sets the standard filter options given by the program arguments.
+ *
+ * @param filter    OUT: the filter to set
+ */
+void ReDirOptions::setFilterFromProgramArgs(ReDirEntryFilter& filter) {
+       ReByteBuffer buffer;
+       if (m_programArgs.getString("younger", buffer)[0] != '\0')
+               filter.m_maxAge = checkDate(buffer.str());
+       if (m_programArgs.getString("older", buffer)[0] != '\0')
+               filter.m_minAge = checkDate(buffer.str());
+       if (m_programArgs.getString("maxsize", buffer)[0] != '\0')
+               filter.m_maxSize = checkSize(buffer.str());
+       if (m_programArgs.getString("minsize", buffer)[0] != '\0')
+               filter.m_minSize = checkSize(buffer.str());
+       if (m_programArgs.getString("type", buffer)[0] != '\0')
+               filter.m_types = checkType(buffer.str());
+       filter.m_minDepth = m_programArgs.getInt("mindepth");
+       filter.m_maxDepth = m_programArgs.getInt("maxdepth");
+       if (m_programArgs.getString("nodepattern", buffer)[0] != '\0') {
+               checkPatternList(buffer.str());
+               m_nodePatterns.set(buffer.str());
+               filter.m_nodePatterns = &m_nodePatterns;
+       }
+       if (m_programArgs.getString("pathpattern", buffer)[0] != '\0') {
+               checkPatternList(buffer.str());
+               optimizePathPattern(buffer);
+               m_pathPatterns.set(buffer.str());
+               filter.m_pathPatterns = &m_pathPatterns;
+       }
+       if ((m_interval = m_programArgs.getInt("trace")) != 0)
+               m_triggerCount = 10;
+       if (m_programArgs.getString("output", buffer)[0] != '\0') {
+               if ((m_output = fopen(buffer.str(), "w")) == NULL) {
+                       help("cannot open output file", buffer.str());
+                       m_output = stdout;
+               }
+       }
+}
+
+/**
+ * Constructor.
+ *
+ * @param usage        a string vector with a message how to use the command
+ * @param example      a string vector with some examples how to use the command
+ * @param minArguments minimal count of non option arguments
+ * @param reservedFirst        count of arguments at the start which are not files
+ * @param reservedLast count of arguments at the end which are not files
+ * @param addCurrentDirIfNoArguments
+ *                                             <code>true</code>: if no arguments are given the current
+ *                                             directory will be added as argument
+ * @param logger               logger for error messages
+ */
+#pragma warning( push )
+#pragma warning( disable : 4355 )
+ReTool::ReTool(const char* usage[], const char* example[], int minArguments,
+    int reservedFirst, int reservedLast, bool addCurrentDirIfNoArguments,
+    ReLogger* logger) :
+           ReDirOptions(usage, example),
+           ReDirTreeStatistic(),
+           m_minArguments(minArguments),
+           m_reservedFirst(reservedFirst),
+           m_reservedLast(reservedLast),
+           m_addCurrentDirIfNoArguments(addCurrentDirIfNoArguments),
+           m_hasStandardArgs(true),
+           m_traverser(NULL, this, logger),
+           m_filter(),
+           m_start(time(NULL)),
+           m_logger(logger) {
+#pragma warning( pop )
+}
+
+/**
+ * Destructor.
+ */
+ReTool::~ReTool() {
+}
+
+/**
+ * Prints a message.
+ *
+ * Often overwritten by a subclass.
+ *
+ * @param currentFile  message for the trace
+ * @return                             <code>true</code> (for chaining)
+ */
+bool ReTool::trace(const char* currentFile) {
+       ReByteBuffer buffer(" ");
+       int duration = int(time(NULL) - m_startTime);
+       buffer.appendInt(duration / 60).appendInt(duration % 60, ":%02d: ");
+       buffer.appendInt(m_files).appendChar('/').appendInt(
+           m_traverser.directories()).append(" dir(s)");
+       buffer.appendInt(m_files).appendChar('/').appendInt(m_traverser.files())
+           .append(" file(s)");
+       buffer.append(currentFile);
+       fputs(buffer.str(), stdout);
+       return true;
+}
+
+/**
+ * Evaluates the arguments and calls the main function.
+ *
+ * @param argc                 number of arguments. If -1: <code>argv</code> ends with <code>NULL</code>
+ * @param argv                 program arguments
+ * @param minArguments the command needs at least so many non option arguments
+ */
+void ReTool::run(int argc, const char** argv) {
+       try {
+               if (argc < 0) {
+                       for (argc = 0; argv[argc] != NULL; argc++) {
+                               // nothing to do
+                       }
+               }
+               m_programArgs.init(argc, argv);
+               if (m_programArgs.argCount() < m_minArguments)
+                       m_programArgs.help(i18n("too few arguments"), false, stdout);
+               if (m_hasStandardArgs)
+                       setFilterFromProgramArgs(m_filter);
+               doIt();
+               if (m_output != stdout) {
+                       fclose(m_output);
+                       m_output = stdout;
+               }
+
+       } catch (ReOptionException& exc) {
+               m_programArgs.help(exc.getMessage(), false, stdout);
+       }
+}
+/**
+ * Evaluates the non option arguments which are names of files or directories.
+ *
+ * For each non reserved argument it will be called either <code>processSingleFile()</code>
+ * or <code>processTree()</code>.
+ *
+ */
+void ReTool::processFileArguments() {
+       int max = m_programArgs.argCount() - m_reservedLast;
+       // Test whether the arguments are files or directories:
+       ReByteBuffer arg;
+       for (int ii = m_reservedFirst; ii < max; ii++) {
+               arg = m_programArgs.arg(ii);
+               if (!exists(arg) != 0)
+                       m_programArgs.help(
+                           ReByteBuffer(i18n("not a file or a directory: ")).append(arg)
+                               .str(), false, stderr);
+       }
+       // process the files:
+       for (int ii = m_reservedFirst; ii < max; ii++) {
+               const char* arg = m_programArgs.arg(ii);
+               if (S_ISDIR(m_statInfo.st_mode))
+                       processTree(arg);
+               else
+                       processSingleFile(arg);
+       }
+}
+/**
+ * Processes a single file.
+ *
+ * Gets the file info and calls <code>processFile()</code>.
+ *
+ * @param filename             the name of the file
+ */
+void ReTool::processSingleFile(const char* filename) {
+       ReByteBuffer protocol;
+       ReByteBuffer path;
+       ReByteBuffer name;
+       ReByteBuffer ext;
+       ReStringUtils::splitPath(filename, &protocol, &path, &name, &ext);
+       protocol.append(path);
+       if (protocol.length() == 0)
+               protocol.append(".");
+       else
+               protocol.reduceLength();
+       m_traverser.changeBase(protocol.str());
+
+       name.append(ext);
+       setFilterFromProgramArgs(m_filter);
+       m_traverser.setPropertiesFromFilter(&m_filter);
+       ReDirStatus_t entry(m_logger);
+       entry.m_path = protocol;
+       if (entry.findFirst()) {
+               do {
+#if defined    __linux__
+                       bool found = strcmp(entry.node(), name.str()) == 0;
+#elif defined __WIN32__
+                       bool found = _stricmp(entry.node(), name.str()) == 0;
+#endif
+                       if (found && m_filter.match(entry)) {
+                               processFile(&entry);
+                               break;
+                       }
+               } while (entry.findNext());
+       }
+       entry.freeEntry();
+}
+/**
+ * Processes a directory tree.
+ *
+ * Finds all filtered files in this directory and its subdirs and call <code>processFile()</code>
+ * or <code>processDir()</code>.
+ *
+ * @param directory            the name of the directory
+ */
+void ReTool::processTree(const char* directory) {
+       m_traverser.changeBase(directory);
+       m_traverser.setPropertiesFromFilter(&m_filter);
+       ReDirStatus_t* entry;
+       int level;
+       while ((entry = m_traverser.nextFile(level, &m_filter)) != NULL) {
+               if (entry->isDirectory()) {
+                       processDir(entry);
+                       m_directories++;
+               } else {
+                       processFile(entry);
+                       m_files++;
+                       m_sizes += entry->fileSize();
+               }
+       }
+}
+/**
+ * Processes one file.
+ *
+ * Normally this method will be overwritten.
+ *
+ * @param entry        the properties of the file to process
+ */
+void ReTool::processFile(ReDirStatus_t* entry) {
+       fprintf(m_output, "+++ ignored (not a directory): %s\n", entry->fullName());
+}
+/**
+ * Issues a summary message if verbose level allows this.
+ * 
+ * @param prefix               NULL or a line prefix
+ */
+void ReTool::printSummary(const char* prefix) {
+       if (m_verboseLevel >= V_SUMMARY) {
+               int duration = int(time(NULL) - m_start);
+               ReByteBuffer line;
+               ReByteBuffer line2;
+               statisticAsString(line);
+               double rate =
+                   duration == 0 ? 0.0 : (m_files + m_directories) / duration;
+               line.append(rate, " %.1f").append(i18n("/sec"), -1);
+               m_traverser.statisticAsString(line2);
+               line2.appendChar(' ').appendTime(duration).append(" ", 1).append(
+                   i18n("sec"));
+               fprintf(m_output, "%s=== filtered: %s\n", prefix == NULL ? "" : prefix,
+                   line.str());
+               fprintf(m_output, "%s===    total: %s\n", prefix == NULL ? "" : prefix,
+                   line2.str());
+       }
+
+}
+/**
+ * Processes one directory.
+ *
+ * Normally this method will be overwritten.
+ *
+ * @param entry        the properties of the directory to process
+ */
+void ReTool::processDir(ReDirStatus_t* entry) {
+       fprintf(m_output, "+++ ignored (not a file): %s\n", entry->fullName());
+}
+
+/**
+ * Constructor.
+ */
+ReDirStatisticData::ReDirStatisticData() :
+           ReDirTreeStatistic(),
+           m_path() {
+}
+/**
+ * Copy constructor.
+ *
+ * @param source       the source to copy
+ */
+ReDirStatisticData::ReDirStatisticData(const ReDirStatisticData& source) :
+           m_path(source.m_path) {
+}
+/**
+ * Assignment operator.
+ *
+ * @param source       the source to copy
+ * @return                     the instance itself
+ */
+ReDirStatisticData& ReDirStatisticData::operator =(
+    const ReDirStatisticData& source) {
+       m_sizes = source.m_sizes;
+       m_files = source.m_files;
+       m_directories = source.m_directories;
+       m_path = source.m_path;
+       return *this;
+}
+
+/**
+ * Constructor.
+ *
+ * @param logger               logger for error handling
+ * @param deltaList            increment if list must be increased
+ * @param deltaBuffer  increment if content buffer must be increased
+ */
+ReDirStatistic::ReDirStatistic(ReLogger* logger, int deltaList, int deltaBuffer) :
+           ReTool(s_statisticUsage, s_statisticExamples, 2, 0, 1, false, logger),
+           m_list(deltaList, deltaBuffer),
+           m_traceInterval(60),
+           m_lastTrace(0) {
+       // standard short options: D d O o P p T t v y Z z
+       m_programArgs.addBool("kbyte",
+           i18n("output format is '<kbyte> <path>' (like unix 'du' command)"), 'k',
+           "kbyte", false);
+       addStandardFilterOptions();
+}
+/**
+ * Destructor.
+ */
+ReDirStatistic::~ReDirStatistic() {
+}
+
+/**
+ * Adds the data from another instance.
+ *
+ * @param source               the other instance
+ * @return                     the instance itself
+ */
+ReDirStatisticData& ReDirStatisticData::add(const ReDirStatisticData& source) {
+       m_sizes += source.m_sizes;
+       m_files += source.m_files;
+       m_directories += source.m_directories;
+       return *this;
+}
+/**
+ * Initializes the data of the instance.
+ */
+void ReDirStatisticData::clear() {
+       ReDirTreeStatistic::clear();
+       m_path.setLength(0);
+}
+/**
+ * Constructor.
+ *
+ * @param logger       logger for error handling
+ */
+ReDirBatch::ReDirBatch(ReLogger* logger) :
+           ReTool(s_batchUsage, s_batchExamples, 0, 0, 0, true, logger),
+           m_arguments(),
+           m_script(),
+           m_isExe(false) {
+       // standard short options: D d O o P p T t v y Z z
+       m_programArgs.addString("first",
+           i18n("defines the first line of the output"), '1', "first-line", true,
+#if defined __linux__
+           "#! /bin/sh"
+#elif defined __WIN32__
+           "rem this batch is created by dirtool"
+#endif
+)      ;
+       m_programArgs.addString("arguments", i18n("template for the output line.\n"
+                       "Possible placeholders: (e.g. e:\\data\\sample.txt)\n"
+                       "   !full!: e:\\data\\sample.txt\n"
+                       "   !path!: e:\\data\\\n"
+                       "   !basename!: sample.txt\n"
+                       "   !name!: sample\n"
+                       "   !ext!: .txt\n"
+                       "example: --arguments='echo !basename! in !path! found'"), 'a',
+               "arguments", false, NULL);
+       m_programArgs.addString("script",
+               i18n("name of the script (starts each output line)"), 'c', "script",
+               false, NULL);
+#if defined __WIN32__
+       m_programArgs.addBool("isexe",
+               i18n("supresses the starting 'call' of each output line"
+                       "neccessary if a *.exe will be called (instead of a *.bat)"),
+               'x', "is-exe", false);
+#endif
+       addStandardFilterOptions();
+}
+
+static void replaceMakros(const char* arguments, ReDirStatus_t* entry,
+    const char* delim, ReByteBuffer& line) {
+       line.set(arguments, -1);
+       // we prepare the removal of unwanted delimiters in constructed placeholders:
+       // example: !path!!name!: without correction: "e:\\data\\""xxx"
+       // We want: "e:\\data\\xxx"
+       line.replaceAll("!!", 2, "!\x01!", 3);
+       ReByteBuffer replacement;
+       if (strstr(arguments, "!full!") != NULL) {
+               replacement.set(delim, -1).append(entry->m_path);
+               replacement.append(entry->node(), -1).append(delim, -1);
+               line.replaceAll("!full!", 6, replacement.str(), replacement.length());
+       }
+       if (strstr(arguments, "!path!") != NULL) {
+               replacement.set(delim, -1).append(entry->m_path).append(delim, -1);
+               line.replaceAll("!path!", 6, replacement.str(), replacement.length());
+       }
+       if (strstr(arguments, "!basename!") != NULL) {
+               replacement.set(delim, -1).append(entry->node(), -1).append(delim, -1);
+               line.replaceAll("!basename!", 10, replacement.str(),
+                   replacement.length());
+       }
+       if (strstr(arguments, "!name!") != NULL) {
+               replacement.set(delim, -1).append(entry->node(), -1);
+               int ix = replacement.rindexOf(".", 1);
+               if (ix > 1)
+                       replacement.setLength(ix);
+               replacement.append(delim, -1);
+               line.replaceAll("!name!", 6, replacement.str(), replacement.length());
+       }
+       if (strstr(arguments, "!ext!") != NULL) {
+               replacement.set(delim, -1).append(entry->node(), -1);
+               int ix = replacement.rindexOf(".", 1);
+               if (ix > 1)
+                       replacement.remove(1, ix - 1);
+               else
+                       replacement.setLength(1);
+               replacement.append(delim, -1);
+               line.replaceAll("!ext!", 5, replacement.str(), replacement.length());
+       }
+       // We remove the unwanted delimiters (see above):
+       ReByteBuffer buffer;
+       buffer.set(delim, -1).appendChar('\01').append(delim, -1);
+       line.replaceAll(buffer.str(), buffer.length(), "", 0);
+}
+/**
+ * Creates the batch file.
+ */
+void ReDirBatch::doIt() {
+       ReByteBuffer buffer;
+       m_programArgs.getString("arguments", m_arguments);
+       m_programArgs.getString("script", m_script);
+       if (m_arguments.length() + m_script.length() == 0)
+               help(
+                   i18n(
+                       "one of the option must be set: -a (--arguments) or -c (--script)"));
+#if defined __WIN32__
+       m_isExe = m_programArgs.getBool("isexe");
+#endif
+       m_programArgs.getString("first", buffer);
+       if (buffer.length() > 0)
+               fprintf(m_output, "%s\n", buffer.str());
+       processFileArguments();
+#if defined __linux__
+       static const char* prefix = "# ";
+#elif defined __WIN32__
+       static const char* prefix = "rem ";
+#endif
+       printSummary(prefix);
+}
+
+/**
+ * Processes one directory.
+ *
+ * @param entry        the properties of the directory to process
+ */
+void ReDirBatch::processDir(ReDirStatus_t* entry) {
+       processFile(entry);
+}
+
+/**
+ * Processes one file.
+ *
+ * @param entry        the properties of the file to process
+ */
+void ReDirBatch::processFile(ReDirStatus_t* entry) {
+       ReByteBuffer line;
+#if defined __linux__
+       static const char* delim = "'";
+#elif defined __WIN32__
+       static const char* delim = "\"";
+#endif
+       if (m_script.length() > 0) {
+#if defined __WIN32__
+               if (! m_isExe)
+               line.append("call ");
+#endif
+               line.append(m_script).appendChar(' ').appendChar(delim[0]);
+               line.append(entry->m_path).append(entry->node(), -1);
+               line.appendChar(delim[0]);
+       } else {
+               replaceMakros(m_arguments.str(), entry, delim, line);
+       }
+       fprintf(m_output, "%s\n", line.str());
+}
+
+/**
+ * Constructor.
+ *
+ * @param logger       logger for error handling
+ */
+ReDirChecksum::ReDirChecksum(ReLogger* logger) :
+           ReTool(s_checksumUsage, s_checksumExamples, 0, 0, 0, true, logger),
+           m_command(CMD_LIST),
+           m_digest(NULL),
+           m_buffer() {
+       // standard short options: D d O o P p T t v y Z z
+       m_programArgs.addString("algorithm", i18n("algorithm: 'MD5' or 'RPD64'"),
+           'a', "--algorithm", false, "MD5");
+       m_programArgs.addInt("buffersize",
+           i18n("buffer size for file reading (in KiByte)"), 'b', "--buffer-size",
+           4 * 1024);
+       m_programArgs.addString("command",
+           i18n(
+               "'build': builds in each directory a file '.dt.chksum' with sum and name\n"
+                       "'compare': shows differences between the content of '.dt.chksum' and the current files\n"
+                       "'list': shows checksum and filename\n"
+                       "'update': like compare, but update the content of '.dt.chksum'"),
+           'c', "--command", false, "list");
+       m_programArgs.addInt("salt", i18n("a number which change the checksum"),
+           's', "--salt", 0);
+       addStandardFilterOptions();
+}
+
+/**
+ * Destructor.
+ */
+ReDirChecksum::~ReDirChecksum() {
+       delete m_digest;
+       m_digest = NULL;
+}
+/**
+ * Builds a checksum storage for a given directory.
+ *
+ * @param path the directory to process
+ */
+void ReDirChecksum::buildStorage(const char* path, const char* storageFile) {
+       ReTraverser traverser(path);
+       traverser.setMaxLevel(0);
+       ReDirStatus_t* entry;
+       ReRPD64 digest2;
+       digest2.setSalt(0x2004199111121989ll);
+       ReByteBuffer line;
+       FILE* fp = fopen(storageFile, "w");
+       if (fp == NULL) {
+               m_logger->sayF(LOG_ERROR | CAT_FILE, LC_BUILD_DIRECTORY_1,
+                   i18n("cannot open file: $1 (errno: $2)")).arg(storageFile).arg(
+               errno).end();
+
+       } else {
+               int level;
+               while ((entry = traverser.nextFile(level, &m_filter)) != NULL) {
+                       line.setLength(0);
+                       if (!entry->isDirectory()) {
+                               calculateChecksum(entry->fullName(), *m_digest, m_buffer,
+                                   m_logger);
+                               line.append(m_digest->hexDigest());
+                       }
+                       line.appendChar('\t').append(entry->node());
+                       digest2.update(line);
+                       fprintf(fp, "%s\n", line.str());
+                       if (m_verboseLevel >= V_NORMAL)
+                               fprintf(m_output, "%16s\t%s",
+                                   entry->isDirectory() ? "" : m_digest->hexDigest().str(),
+                                   entry->fullName());
+               }
+               fprintf(fp, "%s\n", digest2.hexDigest().str());
+               fclose(fp);
+       }
+}
+
+/**
+ * Compares the files of a directory with the files in the storage file
+ *
+ * @param path                 the directory
+ * @param storageFile  the file with the checksums
+ */
+void ReDirChecksum::compareDir(const char* path, ReStringList& names,
+    const char* storageFile) {
+       // forget the last line (checksum):
+       int count = names.count() - 1;
+       names.remove(count);
+       for (int ii = 0; ii < count; ii++) {
+               const char* name = names.strOf(ii);
+               const char* ptr = strchr(name, '\t');
+               if (ptr != NULL)
+                       names.replaceString(ii, ptr + 1);
+       }
+       names.sort();
+       names.setSorted(true);
+       ReTraverser traverser(path);
+       traverser.setMaxLevel(0);
+       ReDirStatus_t* entry;
+       int level;
+       while ((entry = traverser.nextFile(level, &m_filter)) != NULL) {
+               ReSeqArray::Index index;
+               const char* node = entry->node();
+               // the stringlist stores the string with trailing '\0'
+               if (!names.binarySearch(node, strlen(node) + 1, index)) {
+                       m_logger->sayF(LOG_ERROR | CAT_SECURITY, LC_COMPARE_DIR_1,
+                           i18n("missing file $1 in storage $2")).arg(entry->fullName())
+                           .arg(storageFile).end();
+               }
+       }
+}
+
+/**
+ * Compares a storage file with the files of a directory.
+ *
+ * @param path                 the directory
+ * @param storageFile  the file with the checksums
+ */
+void ReDirChecksum::compareStorage(const char* path, const char* storageFile) {
+       ReStringList storage;
+       storage.readFromFile(storageFile);
+       if (storage.count() <= 0) {
+               m_logger->sayF(LOG_ERROR | CAT_SECURITY, LC_COMPARE_STORAGE_1,
+                   i18n("empty storage file: $1")).arg(storageFile).end();
+       } else if (!isValid(storage)) {
+               m_logger->sayF(LOG_ERROR | CAT_SECURITY, LC_COMPARE_STORAGE_2,
+                   i18n("corrupted storage file: $1")).arg(storageFile).end();
+       } else {
+               ReStringList cols;
+               ReByteBuffer fullname(path);
+               fullname.ensureLastChar(OS_SEPARATOR_CHAR);
+               int pathLength = fullname.length();
+               struct stat info;
+               // test all files of the storage:
+               for (size_t ii = 0; ii < storage.count() - 1; ii++) {
+                       cols.split(storage.strOf(ii), '\t');
+                       int colCount = cols.count();
+                       if (colCount != 2) {
+                               m_logger->sayF(LOG_ERROR | CAT_SECURITY, LC_COMPARE_STORAGE_3,
+                                   i18n("wrong format ($1) in storage file $2-$3")).arg(
+                                   colCount).arg(storageFile).arg(ii + 1).end();
+                               break;
+                       } else {
+                               fullname.setLength(pathLength).append(cols.strOf(1));
+                               if (cols.strLengthOf(0) == 0) {
+                                       // a directory:
+                                       if (stat(fullname.str(), &info) != 0) {
+                                               m_logger->sayF(LOG_ERROR | CAT_SECURITY,
+                                                   LC_COMPARE_STORAGE_4,
+                                                   i18n("missing directory $1 ($2-$3)")).arg(fullname)
+                                                   .arg(storageFile).arg(ii + 1).end();
+                                       } else if (!S_ISDIR(info.st_mode)) {
+                                               m_logger->sayF(LOG_ERROR | CAT_SECURITY,
+                                                   LC_COMPARE_STORAGE_5,
+                                                   i18n("directory is now a file: $1 ($2-$3)")).arg(
+                                                   fullname).arg(storageFile).arg(ii + 1).end();
+                                       }
+                               } else {
+                                       // a directory:
+                                       if (stat(fullname.str(), &info) != 0) {
+                                               m_logger->sayF(LOG_ERROR | CAT_SECURITY,
+                                                   LC_COMPARE_STORAGE_6,
+                                                   i18n("missing file $1 ($2-$3)")).arg(fullname).arg(
+                                                   storageFile).arg(ii + 1).end();
+                                       } else if (S_ISDIR(info.st_mode)) {
+                                               m_logger->sayF(LOG_ERROR | CAT_SECURITY,
+                                                   LC_COMPARE_STORAGE_7,
+                                                   i18n("file is now a directory: $1 ($2-$3)")).arg(
+                                                   fullname).arg(storageFile).arg(ii + 1).end();
+                                       } else {
+                                               calculateChecksum(fullname.str(), *m_digest, m_buffer,
+                                                   m_logger);
+                                               if (!m_digest->hexDigest().equals(cols.strOf(0))) {
+                                                       m_logger->sayF(LOG_ERROR | CAT_SECURITY,
+                                                           LC_COMPARE_STORAGE_7,
+                                                           i18n("checksum different: $1 ($2-$3)")).arg(
+                                                           fullname).arg(storageFile).arg(ii + 1).end();
+                                               }
+                                       }
+                               }
+                       }
+               }
+               compareDir(path, storage, storageFile);
+       }
+}
+/**
+ * Checks the validity of a storage file.
+ *
+ * The storage file is protected with a checksum.
+ * The checksum has another salt as the checksums of the files. This makes it
+ * much harder to manipulate the storage file.
+ *
+ * @param storage      the storage file as string list.
+ */
+bool ReDirChecksum::isValid(const ReStringList& storage) {
+       int count = storage.count();
+       ReRPD64 digest2;
+       digest2.setSalt(0x2004199111121989ll);
+
+       for (int ii = 0; ii < count - 1; ii++) {
+               const char* line = storage.strOf(ii);
+               digest2.update(line);
+       }
+       bool rc = true;
+       const char* hex = storage.strOf(count - 1);
+       if (!digest2.hexDigest().equals(hex))
+               rc = false;
+       return rc;
+}
+/**
+ * Lists the metadata of the specified files.
+ */
+void ReDirChecksum::doIt() {
+       int size = m_programArgs.getInt("buffersize") * 1024;
+       m_buffer.setLength(size);
+       ReByteBuffer value;
+       m_programArgs.getString("command", value);
+       if (value.equals("list", -1, true))
+               m_command = CMD_LIST;
+       else if (value.equals("build", -1, true))
+               m_command = CMD_BUILD;
+       else if (value.equals("compare", -1, true))
+               m_command = CMD_COMPARE;
+       else if (value.equals("update", -1, true))
+               m_command = CMD_UPDATE;
+       else
+               help(i18n("unknown command (expected: build compare list update): "),
+                   value.str());
+
+       m_programArgs.getString("algorithm", value);
+       if (value.equals("md5", -1, true))
+               m_digest = new ReMD5();
+       else if (value.equals("rpd64", -1, true))
+               m_digest = new ReRPD64();
+       else
+               help(i18n("unknown algorithm (expected: MD5 RPD64): "), value.str());
+       int salt = m_programArgs.getInt("salt");
+       if (salt != 0)
+               m_digest->setSalt(salt);
+       m_filter.m_allDirectories = true;
+       processFileArguments();
+       printSummary();
+}
+
+/**
+ * Processes one directory.
+ *
+ * @param entry        the properties of the directory to process
+ */
+void ReDirChecksum::processDir(ReDirStatus_t* entry) {
+       ReByteBuffer storageFile;
+       if (m_command != CMD_LIST) {
+               storageFile.append(entry->fullName());
+               storageFile.ensureLastChar(OS_SEPARATOR_CHAR);
+               storageFile.append(".dt.chksum");
+       }
+       switch (m_command) {
+       case CMD_BUILD:
+               buildStorage(entry->fullName(), storageFile.str());
+               break;
+       case CMD_COMPARE:
+               compareStorage(entry->fullName(), storageFile.str());
+               break;
+       case CMD_UPDATE:
+               printf("update not implemented");
+               break;
+       default:
+               break;
+       }
+}
+/**
+ * Processes one file.
+ *
+ * @param entry        the properties of the file to process
+ */
+void ReDirChecksum::processFile(ReDirStatus_t* entry) {
+       const char* name = entry->fullName();
+       switch (m_command) {
+       case CMD_LIST:
+               calculateChecksum(name, *m_digest, m_buffer, m_logger);
+               fprintf(m_output, "%s %s\n", m_digest->hexDigest().str(), name);
+               break;
+       default:
+               break;
+       }
+}
+/**
+ * Compares a storage file with the files of a directory.
+ *
+ * @param path                 the directory
+ * @param storageFile  the file with the checksums
+ */
+void ReDirChecksum::updateStorage(const char* path, const char* storageFile) {
+
+}
+
+/**
+ * Calculates the checksum of a given file.
+ *
+ * @param name         the full filename
+ * @param digest       IN: defines the kind of checksum<br>
+ *                                     OUT: contains the calculated checksum
+ * @param buffer       IN/OUT: a buffer for file reading
+ * @param logger       logger for error processing
+ * @return                     <code>digest</code> (for chaining)
+ */
+ReDigest& ReDirChecksum::calculateChecksum(const char* name, ReDigest& digest,
+    ReByteBuffer& buffer, ReLogger* logger) {
+       FILE* fp = fopen(name, "rb");
+       if (fp == NULL) {
+               if (logger != NULL)
+                       logger->sayF(LOG_ERROR | CAT_FILE, LC_CALCULATE_CHECKSUM_1,
+                           i18n("cannot open file: $1 (errno: $2)")).arg(name).arg(
+                       errno).end();
+       } else {
+               ReMD5 digest;
+               size_t readBytes;
+               uint8_t* buf = reinterpret_cast<uint8_t*>(buffer.buffer());
+               size_t blockSize = buffer.length();
+
+               while ((readBytes = fread(buf, 1, blockSize, fp)) > 0) {
+                       digest.update(buf, readBytes);
+               }
+               fclose(fp);
+       }
+       return digest;
+}
+
+/**
+ * Constructor.
+ *
+ * @param logger       logger for error handling
+ */
+ReDirDelete::ReDirDelete(ReLogger* logger) :
+           ReTool(s_deleteUsage, s_deleteExamples, 0, 0, 0, true, logger) {
+       addStandardFilterOptions();
+}
+
+/**
+ * Deletes the specified files/directories.
+ */
+void ReDirDelete::doIt() {
+       m_traverser.setDepthFirst(true);
+       processFileArguments();
+       printSummary();
+}
+
+/**
+ * Processes one directory.
+ *
+ * @param entry        the properties of the directory to process
+ */
+void ReDirDelete::processDir(ReDirStatus_t* entry) {
+       _rmdir(entry->fullName());
+}
+/**
+ * Processes one file.
+ *
+ * @param entry        the properties of the file to process
+ */
+void ReDirDelete::processFile(ReDirStatus_t* entry) {
+       const char* name = entry->fullName();
+       if (m_verboseLevel >= V_NORMAL) {
+               fprintf(m_output, "%s\n", name);
+       }
+       if (_unlink(name) != 0)
+               m_logger->sayF(LOG_ERROR | CAT_FILE, LC_DELETE_1,
+                   i18n("cannot delete ($1): $2")).arg(errno).arg(name).end();
+}
+
+/**
+ * Constructor.
+ *
+ * @param logger       logger for error handling
+ */
+ReDirList::ReDirList(ReLogger* logger) :
+           ReTool(s_listUsage, s_listExamples, 0, 0, 0, true, logger),
+           m_widthOwner(13),
+           m_shortFormat(false),
+           m_withRights(false),
+           m_numerical(false) {
+       // standard short options: D d O o P p T t v y Z z
+       m_programArgs.addBool("short", i18n("output is only path and basename"),
+           '1', "--short", false);
+       m_programArgs.addBool("rights", i18n("show the permission/right info"), 'r',
+           "--rights", false);
+       m_programArgs.addBool("numerical",
+           i18n("the permission/right info is shown as numbers"), 'n',
+           "--numerical", false);
+       m_programArgs.addInt("owner", i18n("space reserved for owner/group"), 'w',
+           "--width-owner", 13);
+       addStandardFilterOptions();
+}
+
+/**
+ * Lists the metadata of the specified files.
+ */
+void ReDirList::doIt() {
+       m_shortFormat = m_programArgs.getBool("short");
+       m_withRights = m_programArgs.getBool("rights");
+       m_numerical = m_programArgs.getBool("numerical");
+       m_widthOwner = m_programArgs.getInt("owner");
+       processFileArguments();
+       printSummary();
+}
+
+/**
+ * Processes one directory.
+ *
+ * @param entry        the properties of the directory to process
+ */
+void ReDirList::processDir(ReDirStatus_t* entry) {
+       processFile(entry);
+}
+/**
+ * Processes one file.
+ *
+ * @param entry        the properties of the file to process
+ */
+void ReDirList::processFile(ReDirStatus_t* entry) {
+       ReByteBuffer bufferRights;
+       ReByteBuffer bufferTime;
+       if (m_shortFormat)
+               fprintf(m_output, "%s%s\n", entry->m_path.str(), entry->node());
+       else {
+               if (m_withRights)
+                       entry->rightsAsString(bufferRights, m_numerical, m_widthOwner);
+               fprintf(m_output, "%c%s %12.6f %s %s%s\n", entry->typeAsChar(),
+                   bufferRights.str(), entry->fileSize() / 1E6,
+                   entry->filetimeAsString(bufferTime), entry->m_path.str(),
+                   entry->node());
+       }
+}
+
+/**
+ * Constructor.
+ *
+ * @param logger       logger for error handling
+ */
+ReDirRandom::ReDirRandom(ReLogger* logger) :
+           ReTool(s_randomUsage, s_randomExamples, 2, 0, 0, true, logger) {
+       m_hasStandardArgs = false;
+       m_programArgs.addBool("multiple",
+           i18n("result can contain a value multiple times"), 'm', "--multiple",
+           false);
+       m_programArgs.addInt("perline", i18n("number of values per line"), 'l',
+           "--values-per-line", 10);
+       m_programArgs.addBool("sort", i18n("the result is sorted"), 's', "--sort",
+           false);
+       m_programArgs.addInt("width",
+           i18n("output format: minimum width of the numbers"
+                   "   0: width of the maximum"), 'w', "--min-width", 0);
+}
+
+/**
+ * Lists the metadata of the specified files.
+ */
+void ReDirRandom::doIt() {
+       bool multiple = m_programArgs.getBool("multiple");
+       bool sort = m_programArgs.getBool("sort");
+       int count = atol(m_programArgs.arg(0));
+       int to = atol(m_programArgs.arg(1));
+       int from = 1;
+       if (m_programArgs.argCount() > 2)
+               from = atol(m_programArgs.arg(2));
+       ReShiftRandom rand;
+       rand.setSeed(rand.nearTrueRandom());
+       for (int ii = rand.nextInt(100); ii > 0; ii--)
+               rand.nextInt();
+       int factor = to <= 1000 ? 1000 * 1000 : 1;
+       int numbersPerLine = m_programArgs.getInt("perline");
+       int width = m_programArgs.getInt("width");
+       if (width <= 0)
+               width = ReByteBuffer("").appendInt(to).length();
+       ReByteBuffer format("%");
+       format.appendInt(width).append("d%s");
+       int found = 0;
+       ReSeqArray values(count);
+       ReByteBuffer value;
+       if (!multiple && count >= to - from + 1)
+               help(
+                   i18n(
+                       "not realy random: all possible values requested. Do you mean '--multiple'?"));
+       while (found < count) {
+               value.setLength(0).appendInt(
+                   from + rand.nextInt((to - from + 1) * factor) / factor, "%010d");
+               int ix = (int) values.find(value.str());
+               if (!multiple && ix != -1)
+                       continue;
+               found++;
+               values.add(-1, value.str());
+       }
+       if (sort)
+               values.sort();
+       for (int ii = 0; ii < count; ii++) {
+               values.get(ii, value);
+               int val = atol(value.str());
+               printf(format.str(), val,
+                   ii % numbersPerLine == numbersPerLine - 1 ? "\n" : " ");
+       }
+       if (count % numbersPerLine != 0)
+               printf("\n");
+}
+
+/**
+ * Calculates the statistic of a directory tree.
+ *
+ *
+ */
+const ReStringList& ReDirStatistic::calculate(const char* base, int level,
+    void (*formatter)(const ReDirStatisticData& data, ReDirStatistic& parent,
+        ReByteBuffer& line)) {
+       ReDirEntryFilter filter;
+       ReTraverser traverser(base, this);
+       setFilterFromProgramArgs(filter);
+       traverser.setPropertiesFromFilter(&filter);
+       if (level > 1024)
+               level = 1024;
+       else if (level < 0)
+               level = 0;
+       m_list.clear();
+       ReDirStatisticData** dataStack = new ReDirStatisticData*[level + 1];
+       memset(dataStack, 0, sizeof dataStack[0] * (level + 1));
+       dataStack[0] = new ReDirStatisticData();
+       int topOfStack = 0;
+       ReDirStatus_t* entry;
+       int currentDepth = -1;
+       ReDirStatisticData* current = dataStack[0];
+       current->m_path.set(base, -1);
+       ReByteBuffer line;
+       bool useFilter = filter.m_minSize > 0 || filter.m_maxSize != -1
+           || !filetimeIsUndefined(filter.m_minAge)
+           || !filetimeIsUndefined(filter.m_maxAge) || m_nodePatterns.count() > 0;
+       while ((entry = traverser.rawNextFile(currentDepth))) {
+               if (currentDepth <= level && !entry->m_path.equals(current->m_path)) {
+                       if (currentDepth <= topOfStack) {
+                               // close the entries of the stack above the new top level:
+                               while (topOfStack >= currentDepth) {
+                                       // Add the data to the parent:
+                                       if (topOfStack > 0)
+                                               dataStack[topOfStack - 1]->add(*dataStack[topOfStack]);
+                                       // Append it to the result:
+                                       (*formatter)(*dataStack[topOfStack], *this, line);
+                                       m_list.append(line);
+                                       topOfStack--;
+                               }
+                               // We reuse the top of stack:
+                               topOfStack++;
+                               current = dataStack[topOfStack];
+                               // exchange the top of stack with the new found directory:
+                               current->clear();
+                               current->m_path.set(entry->m_path.str(),
+                                   entry->m_path.length());
+                               ;
+                       } else {
+                               // set up a new stack entry:
+                               if (currentDepth != topOfStack + 1)
+                                       assert(currentDepth == topOfStack + 1);
+
+                               topOfStack++;
+                               if (dataStack[topOfStack] == NULL)
+                                       dataStack[topOfStack] = new ReDirStatisticData();
+                               else
+                                       dataStack[topOfStack]->clear();
+                               current = dataStack[topOfStack];
+                               current->m_path.set(entry->m_path.str(),
+                                   entry->m_path.length());
+                       }
+               }
+               if (entry->isDirectory()) {
+                       current->m_directories++;
+               } else if (!useFilter || filter.match(*entry)) {
+                       current->m_sizes += entry->fileSize();
+                       current->m_files++;
+               }
+       }
+       // close all dirs with parents:
+       while (topOfStack > 0) {
+               // Add the data to the parent:
+               dataStack[topOfStack - 1]->add(*dataStack[topOfStack]);
+               // Append it to the result:
+               (*formatter)(*dataStack[topOfStack], *this, line);
+               m_list.append(line);
+               topOfStack--;
+       }
+       // ... and the overall summery:
+       (*formatter)(*dataStack[0], *this, line);
+       m_list.append(line);
+       // free the resources:
+       for (int ix = 0; ix <= level; ix++)
+               delete dataStack[ix];
+       delete[] dataStack;
+       return m_list;
+}
+
+/**
+ * Build the statistic and print the results.
+ *
+ * @param arc   count of arguments in <code>argv</code>
+ * @param argv  the program arguments.
+ */
+void ReDirStatistic::doIt() {
+       int depth = 1;
+       if (m_programArgs.argCount() > 1) {
+               const char* arg1 = m_programArgs.arg(1);
+               if (ReStringUtils::lengthOfUnsigned(arg1, -1, (unsigned *) &depth) == 0)
+                       m_programArgs.help("depth is not an integer", false, stdout);
+       }
+       void (*proc)(const ReDirStatisticData& data, ReDirStatistic& parent,
+           ReByteBuffer& line) = &formatWithSizeFilesAndDirs;
+       if (m_programArgs.getBool("kbyte"))
+               proc = &formatLikeDu;
+       const ReStringList& list = calculate(m_programArgs.arg(0), depth, proc);
+       ReByteBuffer buffer;
+       for (size_t ix = 0; ix < list.count(); ix++) {
+               buffer.set(list.strOf(ix), list.strLengthOf(ix));
+               fprintf(m_output, "%s\n", buffer.str());
+       }
+       if (m_verboseLevel >= V_SUMMARY) {
+               int duration = int(time(NULL) - m_start);
+               fprintf(m_output, "=== duration: ");
+               if (duration >= 3600)
+                       fprintf(m_output, "%d:", duration / 3600);
+               fprintf(m_output, "%02d:%02d\n", duration % 3600 / 60, duration % 60);
+       }
+}
+
+/**
+ * Formats a line like the du (disk usage) command.
+ *
+ * This is a possible parameter of <code>ReDirStatistic::calculate()</code>.
+ *
+ * @param data         statistic data, including path name
+ * @param parent       the caller (<code>ReDirStatistic</code>). This allows to deliver
+ *                                     a context to this formatting routine (through derivation of
+ *                                     <code>ReDirStatistic</code>)
+ * @param line         OUT: the formatted line, the conclusion of the statistic data
+ */
+void formatLikeDu(const ReDirStatisticData& data, ReDirStatistic& parent,
+    ReByteBuffer& line) {
+       line.setLength(0);
+       // Round up to the next KiByte:
+       line.appendInt(int((data.m_sizes + 1023) / 1024)).append("\t").append(
+           data.m_path);
+}
+
+/**
+ * Formats a line in a standard way: MBytes, file count and directory count.
+ *
+ * This is a possible parameter of <code>ReDirStatistic::calculate()</code>.
+ *
+ * @param data         statistic data, including path name
+ * @param parent       the caller (<code>ReDirStatistic</code>). This allows to deliver
+ *                                     a context to this formatting routine (through derivation of
+ *                                     <code>ReDirStatistic</code>)
+ * @param line         OUT: the formatted line, the conclusion of the statistic data
+ */
+void formatWithSizeFilesAndDirs(const ReDirStatisticData& data,
+    ReDirStatistic& parent, ReByteBuffer& line) {
+       line.setLength(0);
+       // Round up to the next KiByte:
+       char buffer[256];
+       _snprintf(buffer, sizeof buffer, "%14.6f MB %7d %7d\t", data.m_sizes / 1E6,
+           data.m_files, data.m_directories);
+       line.append(buffer, -1).append(data.m_path);
+}
+/**
+ * Constructor.
+ *
+ * @param logger       logger for error handling
+ */
+ReDirTouch::ReDirTouch(ReLogger* logger) :
+           ReTool(s_touchUsage, s_touchExamples, 1, 0, 0, false, logger),
+           m_buffer()
+//m_modified()
+//m_accessed()
+{
+       setFiletimeUndef(m_modified);
+       setFiletimeUndef(m_accessed);
+       // standard short options: D d O o P p T t v y Z z
+       m_programArgs.addString("accessed", i18n("the new access time.\n"
+               "Formats: absolute, relative to now, relative to the current filetime\n"
+               "[yyyy.mm.dd/]HH:MM:SS \n"
+               "now-<count>{s|m|h|d}\n"
+               "{+|-}<count>{s|m|h|d}"), 'a', "--accessed", false, NULL);
+       m_programArgs.addString("modified", i18n("the new modification time.\n"
+               "Formats: absolute, relative to now, relative to the current filetime\n"
+               "[yyyy.mm.dd/]HH:MM:SS\n"
+               "now-<count>{s|m|h|d}\n"
+               "{+|-}<count>{s|m|h|d}"), 'm', "--modified", false, NULL);
+       addStandardFilterOptions();
+}
+/**
+ * Sets the filetime for the specified files.
+ */
+void ReDirTouch::doIt() {
+       ReByteBuffer buffer;
+       if (m_programArgs.getString("modified", buffer)[0] != '\0')
+               m_modified = checkDate(buffer.str());
+       if (m_programArgs.getString("accessed", buffer)[0] != '\0')
+               m_accessed = checkDate(buffer.str());
+       if (filetimeIsUndefined(m_modified) && filetimeIsUndefined(m_accessed))
+               help("missing --modified and/or --accessed");
+       processFileArguments();
+       printSummary();
+}
+
+/**
+ * Processes one directory.
+ *
+ * @param entry        the properties of the directory to process
+ */
+void ReDirTouch::processDir(ReDirStatus_t* entry) {
+       processFile(entry);
+}
+
+static bool isAbsoluteTime(ReFileTime_t& time) {
+#if defined __linux__
+       static struct tm year1980 = { 0, 0, 0, 1, 1 - 1, 1980 - 1900 };
+       static time_t time1980 = mktime(&year1980);
+       return time.tv_sec >= time1980;
+#elif defined __WIN32__
+       static ReFileTime_t filetime1980 = {2148603904, 27846551};
+       bool rc = time > filetime1980;
+       return rc;
+#endif
+}
+static void addRelativeTime(ReFileTime_t& absTime,
+    const ReFileTime_t& relTime) {
+#if defined __linux__
+       if ((absTime.tv_nsec += relTime.tv_nsec) >= 1000 * 1000 * 1000) {
+               absTime.tv_nsec -= 1000 * 1000 * 1000;
+               absTime.tv_sec += 1;
+       }
+       absTime.tv_sec += relTime.tv_sec;
+#elif defined __WIN32__
+       uint64_t absValue = ((uint64_t) absTime.dwHighDateTime << 32) | absTime.dwLowDateTime;
+       uint64_t relValue = ((uint64_t) relTime.dwHighDateTime << 32) | relTime.dwLowDateTime;
+       absValue += relValue;
+       absTime.dwHighDateTime = (uint32_t) (absValue >> 32);
+       absTime.dwLowDateTime = (uint32_t) absValue;
+#endif
+}
+/**
+ * Processes one file.
+ *
+ * @param entry        the properties of the file to process
+ */
+void ReDirTouch::processFile(ReDirStatus_t* entry) {
+       ReFileTime_t modified = *entry->modified();
+       int countTimes = 0;
+       if (!filetimeIsUndefined(m_modified)) {
+               countTimes++;
+               if (isAbsoluteTime(m_modified))
+                       modified = m_modified;
+               else
+                       addRelativeTime(modified, m_modified);
+       }
+       ReFileTime_t accessed = *entry->accessed();
+       if (!filetimeIsUndefined(m_accessed)) {
+               countTimes++;
+               if (isAbsoluteTime(m_accessed))
+                       accessed = m_accessed;
+               else
+                       addRelativeTime(accessed, m_accessed);
+       }
+       const char* name = entry->fullName();
+       if (touch(name, modified, accessed, ReLogger::globalLogger()) == 0
+           && m_verboseLevel >= V_NORMAL) {
+               ReByteBuffer bufferTime;
+               ReByteBuffer bufferTime2;
+               if (m_verboseLevel == V_NORMAL) {
+                       if (countTimes == 2)
+                               fprintf(m_output, "%s | %s | %s\n",
+                                   ReDirStatus_t::filetimeToString(&modified, bufferTime),
+                                   ReDirStatus_t::filetimeToString(&accessed, bufferTime2),
+                                   name);
+                       else {
+                               ReDirStatus_t::filetimeToString(
+                                   filetimeIsUndefined(m_modified) ? &accessed : &modified,
+                                   bufferTime);
+                               fprintf(m_output, "%s %s\n", bufferTime.str(), name);
+                       }
+               } else {
+                       ReByteBuffer bufferTime3;
+                       ReByteBuffer bufferTime4;
+                       if (countTimes == 2)
+                               fprintf(m_output, "%s -> %s | %s -> %s | %s\n",
+                                   ReDirStatus_t::filetimeToString(entry->modified(),
+                                       bufferTime),
+                                   ReDirStatus_t::filetimeToString(&modified, bufferTime2),
+                                   ReDirStatus_t::filetimeToString(entry->accessed(),
+                                       bufferTime3),
+                                   ReDirStatus_t::filetimeToString(&accessed, bufferTime4),
+                                   name);
+                       else {
+                               ReDirStatus_t::filetimeToString(
+                                   filetimeIsUndefined(m_modified) ? &m_accessed : &m_modified,
+                                   bufferTime);
+                               fprintf(m_output, "%s -> %s %s\n",
+                                   ReDirStatus_t::filetimeToString(entry->modified(),
+                                       bufferTime2), bufferTime.str(), (char*) name);
+                       }
+               }
+       }
+}
+
+/**
+ * Sets file times.
+ *
+ * @param filename             the name of the file
+ * @param properties   contains modification and access time
+ * @param logger               NULL or the logger
+ */
+ReErrNo_t ReDirTouch::touch(const char* filename, const ReFileTime_t& modified,
+    const ReFileTime_t& accessed, ReLogger* logger) {
+#if defined __linux__
+       timeval times[2];
+       times[0].tv_sec = accessed.tv_sec;
+       times[0].tv_usec = accessed.tv_nsec / 1000;
+       times[1].tv_sec = modified.tv_sec;
+       times[1].tv_usec = modified.tv_nsec / 1000;
+       ReErrNo_t rc = utimes(filename, times) == 0 ? 0 : errno;
+
+#elif defined __WIN32__
+       ReErrNo_t rc = 0;
+       HANDLE handle = CreateFile(filename, FILE_WRITE_ATTRIBUTES,
+               FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
+               FILE_ATTRIBUTE_NORMAL, NULL);
+       if (handle == INVALID_HANDLE_VALUE)
+       rc = GetLastError();
+       else {
+               if (! SetFileTime(handle, (LPFILETIME) NULL, &accessed, &modified))
+               rc = GetLastError();
+               CloseHandle(handle);
+       }
+#endif
+       if (rc != 0 && logger != NULL)
+               logger->sayF(LOG_ERROR | CAT_FILE, LC_TOUCH_1,
+                   i18n("cannot change filetime: $1 (errno: $2)")).arg(filename).arg(
+               errno).end();
+       return rc;
+}
+
+/**
+ * Prints a vector of lines.
+ * 
+ * @param lines     a vector of lines without newline ('\n')
+ */
+static void printField(const char** lines) {
+       for (int ix = 0; lines[ix] != NULL; ix++) {
+               printf("%s\n", lines[ix]);
+       }
+}
+
+/**
+ * Prints a message how to use the module and exits.
+ */
+void ReDirTools::usage(ReTool& tool) {
+       tool.programArgs().help(NULL, false, stdout);
+}
+
+/**
+ * Prints an message how to use the program and exits.
+ *
+ * @param msg       an error message
+ * @param msg2      an addition to the error message or NULL
+ */
+void ReDirTools::usage(const char* msg, const char* msg2) {
+       printf("Version: %s\n", m_version);
+       printf("usage: dirtool <command> <opt>\n"
+               "call 'dirtool help' for more info\n");
+       if (msg != NULL)
+               printf("+++ %s%s\n", msg, msg2 == NULL ? "" : msg2);
+       exit(1);
+}
+
+/**
+ * creates a subdirectory (and the parent directories if neccessary.
+ *
+ * @param path      the name of the subdir to create
+ */
+void ReDirSync::makeDirWithParents(ReByteBuffer& path, int minWidth,
+    ReTraverser& traverser) {
+       if (!exists(path)) {
+               ReFileProperties_t* props = NULL;
+#if defined __linux__
+               ReDirStatus_t* entry = traverser.topOfStack();
+               props = entry == NULL ? NULL : &entry->m_status;
+#endif
+               makeDirectory(path.str(), minWidth, props, ReLogger::globalLogger());
+       }
+}
+/**
+ * Constructor.
+ *
+ * @param logger       logger for error handling
+ */
+ReDirSync::ReDirSync(ReLogger* logger) :
+           ReTool(s_syncUsage, s_syncExamples, 2, 0, 1, false, logger),
+           m_buffer() {
+       // standard short options: D d O o P p T t v y Z z
+       m_buffer.ensureSize(4u * 1024u * 1024u);
+       m_programArgs.addBool("add",
+           i18n("copies only files which does not exist on the target"), 'a',
+           "add", false);
+       m_programArgs.addBool("dry",
+           i18n("does nothing, but says what should be done"), 'Y', "dry", false);
+       m_programArgs.addInt("timediff",
+           i18n("filetime difference is considered to be equal\n"
+                   "if the difference is less than this value (in seconds)"), 'I',
+           "time-delta", 2);
+       m_programArgs.addBool("ignoredate",
+           i18n(
+               "the modification is recognized only by the different size (not time)"),
+           'i', "ignore-time", false);
+       m_programArgs.addBool("mustexist",
+           i18n("files which don't exist on the target will not be copied"), 'm',
+           "must-exist", false);
+       addStandardFilterOptions();
+}
+
+/**
+ * Copies a file.
+ *
+ * @param entry                the source file info
+ * @param target       the name of the target file
+ */
+void ReDirSync::copyFile(ReDirStatus_t* entry, const char* target) {
+       ReFileProperties_t* props;
+#ifdef __linux__
+       props = &entry->m_status;
+#else
+       ReFileProperties_t properties;
+       properties.m_modified = *entry->modified();
+       properties.m_accessed = *entry->accessed();
+       properties.m_size = entry->fileSize();
+       props = &properties;
+#endif
+       copyFile(entry->fullName(), props, target, m_buffer,
+           ReLogger::globalLogger());
+}
+
+/**
+ * Copies a file.
+ *
+ * @param source               the source file name
+ * @param properties   NULL or the properties of the source file
+ * @param target               the name of the target file
+ * @param buffer               OUT: the reading uses this buffer<br>
+ *                                             Set the capacity to make it more efficient
+ * @param logger               NULL or the logger for error messages
+ * @return                             <code>true</code>success<br>
+ *                                             <code>false</code>error occurred
+ */
+bool ReDirSync::copyFile(const char* source, ReFileProperties_t* properties,
+    const char* target, ReByteBuffer& buffer, ReLogger* logger) {
+       bool rc = false;
+#ifdef __linux__
+       struct stat info;
+       if (properties == NULL) {
+               if (stat(source, &info) == 0)
+                       properties = &info;
+               else {
+                       if (logger != NULL)
+                               logger->sayF(LOG_ERROR | CAT_FILE, LC_COPY_FILE_1,
+                                   i18n("could not find: $1 (errno: $2)")).arg(source).arg(
+                               errno).end();
+               }
+       }
+       FILE* fpSource = fopen(source, "rb");
+       if (fpSource == NULL) {
+               if (logger != NULL)
+                       logger->sayF(LOG_ERROR | CAT_FILE, LC_COPY_FILE_2,
+                           i18n("cannot open $1 (errno: $2)")).arg(source).arg(errno).end();
+       } else {
+               ReFileSize_t size =
+                   properties == NULL ? 0x7fffffff : properties->st_size;
+               FILE* fpTarget = fopen(target, "w");
+               if (fpTarget == NULL) {
+                       if (logger != NULL)
+                               logger->sayF(LOG_ERROR | CAT_FILE, LC_COPY_FILE_3,
+                                   i18n("cannot open $1 (errno: $2)")).arg(target).arg(errno)
+                                   .end();
+               } else {
+                       while (size > 0) {
+                               size_t blockSize = buffer.capacity();
+                               if ((int) blockSize > size)
+                                       blockSize = size;
+                               if (fread(buffer.buffer(), blockSize, 1, fpSource) != 1) {
+                                       if (logger != NULL)
+                                               logger->sayF(LOG_ERROR | CAT_FILE, LC_COPY_FILE_5,
+                                                   i18n("cannot read $1 (errno: $2)")).arg(source).arg(
+                                               errno).end();
+                                       break;
+                               }
+                               size_t written;
+                               if ((written = fwrite(buffer.buffer(), 1, blockSize, fpTarget))
+                                   != blockSize) {
+                                       if (logger != NULL)
+                                               logger->sayF(LOG_ERROR | CAT_FILE, LC_COPY_FILE_6,
+                                                   i18n("cannot write $1 [$2] (errno: $3)")).arg(
+                                                   target).arg(written).arg(errno).end();
+                                       break;
+                               }
+                               size -= blockSize;
+                       }
+                       rc = size == 0ll;
+                       fclose(fpTarget);
+                       if (properties != NULL)
+                               setProperties(target, properties, logger);
+               }
+               fclose(fpSource);
+       }
+#elif defined __WIN32__
+       BOOL cancel = false;
+       rc = CopyFileExA(source, target, NULL, NULL, &cancel, COPY_FILE_NO_BUFFERING) != 0;
+       int errNo = 0;
+       if (! rc)
+       errNo = GetLastError();
+#endif
+       return rc;
+}
+/**
+ * Sets the file properties.
+ *
+ * @param fullName             the name of the file
+ * @param properties   the properties like times and rights
+ * @param logger               NULL or the logger for error messages
+ * @return                             <code>true</code>: success<br>
+ *                                             <code>false</code>: error occurred
+ */
+bool ReDirSync::setProperties(const char* fullName,
+    ReFileProperties_t* properties, ReLogger* logger) {
+       bool rc = true;
+#if defined __linux__
+       timeval times[2];
+       times[0].tv_sec = properties->st_atim.tv_sec;
+       times[0].tv_usec = properties->st_atim.tv_nsec / 1000;
+       times[1].tv_sec = properties->st_mtim.tv_sec;
+       times[1].tv_usec = properties->st_mtim.tv_nsec / 1000;
+       if (utimes(fullName, times) != 0) {
+               if (logger != NULL)
+                       logger->sayF(LOG_ERROR | CAT_FILE, LC_SET_PROPERTIES_1,
+                           i18n("cannot change file times: $1 (errno: $2)")).arg(fullName)
+                           .arg(errno).end();
+               rc = false;
+       }
+       int rights = properties->st_mode & (S_IRWXO | S_IRWXG | S_IRWXU);
+       if (chmod(fullName, rights) != 0) {
+               if (logger != NULL)
+                       logger->sayF(LOG_ERROR | CAT_FILE, LC_SET_PROPERTIES_2,
+                           i18n("cannot change file modes: $1 (errno: $2)")).arg(fullName)
+                           .arg(errno).end();
+               rc = false;
+       }
+       if (chown(fullName, properties->st_uid, properties->st_gid) != 0) {
+               if (logger != NULL)
+                       logger->sayF(LOG_ERROR | CAT_FILE, LC_SET_PROPERTIES_3,
+                           i18n("cannot change file owner: $1 (errno: $2)")).arg(fullName)
+                           .arg(errno).end();
+               rc = false;
+       }
+#endif
+       return rc;
+}
+
+/**
+ * Creates a directory and its parents (if neccessary).
+ *
+ * @param directory            the full name of the directory
+ * @param properties   NULL or the properties of the new directory
+ * @param logger               NULL or the logger for error messages
+ * @return                             <code>true</code>success<br>
+ *                                             <code>false</code>error occurred
+ */
+bool ReDirSync::makeDirectory(const char* directory, int minLength,
+    ReFileProperties_t* properties, ReLogger* logger) {
+       bool rc = true;
+       ReByteBuffer path(directory);
+       int start = 0;
+#if defined __WIN32__
+       start = path.indexOf(':');
+#endif
+       path.ensureLastChar(OS_SEPARATOR_CHAR);
+       int ixSlash = start < 0 ? 0 : start;
+       struct stat info;
+       // for all parents and the full path itself:
+       while (ixSlash >= 0) {
+               ixSlash = path.indexOf(OS_SEPARATOR_CHAR, ixSlash + 1);
+               // is the slash in front of the first node, e.g. 'e:\'?
+               if (ixSlash == start + 1)
+                       // not a real node: take the next node
+                       ixSlash = path.indexOf(OS_SEPARATOR_CHAR, ixSlash + 1);
+               if (ixSlash >= 0) {
+                       // we handle only the next node:
+                       path.buffer()[ixSlash] = '\0';
+               }
+               // does the node exist?
+               if (lstat(path.str(), &info) != 0) {
+                       // no, then we make it:
+                       if (_mkdir(path.str(), ALLPERMS) != 0) {
+                               if (logger != NULL)
+                                       logger->sayF(LOG_ERROR | CAT_FILE, LC_MAKE_DIR_1,
+                                           i18n("could not make directory $1 (errno: $2)")).arg(
+                                           path.str()).arg(errno).end();
+                               rc = false;
+                               break;
+                       } else {
+#if defined __linux__
+                               setProperties(path.str(), properties);
+#endif
+                       }
+               }
+               if (ixSlash >= 0) {
+                       // restore the full path:
+                       path.buffer()[ixSlash] = OS_SEPARATOR_CHAR;
+               }
+       }
+       return rc;
+}
+
+/**
+ * Synchronizes two directory trees.
+ */
+void ReDirSync::doIt() {
+       ReDirEntryFilter filter;
+       const char* sep = OS_SEPARATOR;
+       ReByteBuffer buffer;
+       ReByteBuffer target(m_programArgs.arg(m_programArgs.argCount() - 1));
+       target.removeLastChar(OS_SEPARATOR_CHAR);
+       if (!exists(target))
+               help(i18n("target does not exist: $1"), target.str());
+       else if (!S_ISDIR(m_statInfo.st_mode))
+               help(i18n("target is not a directory: $1"), target.str());
+       size_t lengthTargetBase = target.length();
+       bool addOnly = m_programArgs.getBool("add");
+       int maxFileTimeDiff = m_programArgs.getInt("timediff");
+       bool dry = m_programArgs.getBool("dry");
+       bool ignoreDate = m_programArgs.getBool("ignoredate");
+       bool mustExist = m_programArgs.getBool("mustexist");
+       setFilterFromProgramArgs(filter);
+       int64_t sumSizes = 0;
+       int files = 0;
+       int treeFiles = 0;
+       int treeDirs = 0;
+       int64_t treeSumSizes = 0ll;
+       ReByteBuffer source, targetFile;
+       for (int ix = 0; ix < m_programArgs.argCount() - 1; ix++) {
+               source.set(m_programArgs.arg(ix), -1);
+               target.setLength(lengthTargetBase);
+               bool endsWithSlash = source.endsWith(sep, 1);
+               if (endsWithSlash)
+                       source.reduceLength();
+               if (!exists(source))
+                       help(i18n("source does not exist: $1"), source.str());
+               else if (!S_ISDIR(m_statInfo.st_mode))
+                       help(i18n("source is not a directory: $1"), source.str());
+               if (!endsWithSlash) {
+                       // the basename of the source will be appended to the target:
+                       int startNode = source.rindexOf(sep, 1, 0, source.length() - 1);
+                       target.appendChar(OS_SEPARATOR_CHAR);
+                       target.append(source.str() + startNode + 1, -1);
+               }
+               size_t ixSourceRelative = source.length();
+               size_t ixTargetRelative = target.length();
+
+               m_traverser.changeBase(source.str());
+               m_traverser.setPropertiesFromFilter(&filter);
+               int level;
+               ReDirStatus_t* entry;
+               ReByteBuffer line;
+               while ((entry = m_traverser.nextFile(level, &filter)) != NULL) {
+                       if (entry->isDirectory())
+                               continue;
+                       // append the new relative path from source to target:
+                       target.setLength(ixTargetRelative);
+                       target.append(entry->m_path.str() + ixSourceRelative, -1);
+                       if (!exists(target))
+                               makeDirWithParents(target, ixTargetRelative, m_traverser);
+                       targetFile.set(target).append(entry->node(), -1);
+                       const char* targetRelativePath = targetFile.str() + ixTargetRelative
+                           + 1;
+                       bool targetExists = exists(targetFile);
+                       if (!targetExists && mustExist) {
+                               if (m_verboseLevel == V_CHATTER)
+                                       fprintf(m_output, "-ignored: %s does not exist\n",
+                                           targetRelativePath);
+                               continue;
+                       }
+                       if (targetExists) {
+                               if (addOnly) {
+                                       if (m_verboseLevel >= V_CHATTER)
+                                               fprintf(m_output, "~ignored: %s exists\n",
+                                                   targetRelativePath);
+                                       continue;
+                               }
+                               if (ignoreDate && entry->fileSize() == m_statInfo.st_size) {
+                                       if (m_verboseLevel >= V_CHATTER)
+                                               fprintf(m_output, "_ignored: %s same size\n",
+                                                   targetRelativePath);
+                                       continue;
+                               }
+                               // target younger than source?
+                               int diff = int(
+                                   m_statInfo.st_mtime
+                                       - entry->filetimeToTime(entry->modified()));
+                               if (!ignoreDate && diff <= maxFileTimeDiff) {
+                                       if (m_verboseLevel >= V_CHATTER)
+                                               fprintf(m_output, "=ignored: %s same time\n",
+                                                   targetRelativePath);
+                                       continue;
+                               }
+                       }
+                       files++;
+                       sumSizes += entry->fileSize();
+                       if (m_verboseLevel >= V_NORMAL)
+                               fprintf(m_output, "%c%s%s\n", targetExists ? '!' : '+',
+                                   targetRelativePath, dry ? " would be copied" : "");
+                       if (!dry)
+                               copyFile(entry, targetFile.str());
+               }
+               treeFiles += m_traverser.files();
+               treeDirs += m_traverser.directories();
+               treeSumSizes += m_traverser.sizes();
+       }
+       if (m_verboseLevel >= V_SUMMARY) {
+               int duration = int(time(NULL) - m_start);
+               fprintf(m_output,
+                   i18n(
+                       "=== copied:    %02d:%02d sec  %7d file(s) %12.6f MByte (%.3f MB/sec).\n"
+                               "===   tree:  %5d dir(s) %7d file(s) %12.6f MByte\n"),
+                   duration / 60, duration % 60, files, sumSizes / 1E6,
+                   sumSizes / 1E6 / (duration == 0 ? 1 : duration), treeDirs,
+                   treeFiles, treeSumSizes / 1E6);
+       }
+}
+
+/**
+ * Constructor.
+ *
+ * @param logger       logger for error handling
+ */
+ReDirTCP::ReDirTCP(ReLogger* logger) :
+           ReTool(s_tcpUsage, s_tcpExamples, 1, 0, 0, true, logger) {
+       m_hasStandardArgs = false;
+       m_programArgs.addString("size", i18n("size of the message to send/receive\n"
+               "<string> is a number followed by an unit\n"
+               "units: b(yte) k(Byte) K(iByte) m(Byte), M(iByte)\n"
+               "Note: maximum: 16M-32=16777184\n"
+               "examples: -b1m --buffer-size=512K"), 'b', "size", false, "64K");
+       m_programArgs.addInt("port", i18n("port of the server/client"), 'p',
+           "--port", 58111);
+}
+
+/**
+ * Lists the metadata of the specified files.
+ */
+void ReDirTCP::doIt() {
+       int port = m_programArgs.getInt("port");
+       ReByteBuffer buffer;
+       int64_t bufferSize = checkSize(m_programArgs.getString("size", buffer));
+       // the protocol does not allow more than 16 MiByte because of the flags:
+       if (bufferSize > 16LL * 1024 * 1024 - 64LL)
+               help(i18n("buffersize exceeds 16777184 = 16MiByte - 32: "),
+                   buffer.str());
+       ReByteBuffer command = m_programArgs.arg(0);
+       if (command.isPrefixOf("server", -1, true)) {
+               ReTCPEchoServer server(port, m_logger);
+               server.setLogSendReceive(false);
+               server.listenForAll();
+       } else if (command.isPrefixOf("client", -1, true)) {
+               const char* ip = m_programArgs.arg(1);
+               ReByteBuffer direction("download");
+               int rounds = 10;
+               int interval = 5;
+               if (m_programArgs.argCount() > 2) {
+                       direction = m_programArgs.arg(2);
+                       if (!direction.isPrefixOf("download", -1, true)
+                           && !direction.isPrefixOf("upload", -1, true)
+                           && !direction.isPrefixOf("mixed", -1, true))
+                               help(
+                                   "unknown direction: $1 (use 'download', 'upload' or 'mixed')",
+                                   direction.str());
+               }
+               if (m_programArgs.argCount() > 3)
+                       rounds = atoi(m_programArgs.arg(3));
+               if (m_programArgs.argCount() > 4)
+                       interval = atoi(m_programArgs.arg(4));
+               if (tolower(direction.at(0)) == 'm')
+                       runMixedClient(ip, port, rounds, interval, (int) bufferSize);
+               else
+                       runOneThreadClient(ip, port, rounds, interval, (int) bufferSize,
+                           tolower(direction.at(0)) == 'u');
+       } else
+               help("unknown subcommand: $1", command.str());
+}
+void ReDirTCP::runMixedClient(const char* ip, int port, int rounds,
+    int interval, int bufferSize) {
+       help("not implemented: mixed");
+}
+void ReDirTCP::runOneThreadClient(const char* ip, int port, int rounds,
+    int interval, int bufferSize, bool upload) {
+       ReTCPClient client(m_logger);
+       if (client.connect(ip, port)) {
+               time_t start = time(NULL);
+               const char* command = upload ? "strlen" : "filldata";
+               ReByteBuffer message;
+               if (upload)
+                       message.appendChar('x', bufferSize);
+               else
+                       message.appendInt(bufferSize);
+               time_t lastPrint = start;
+               int64_t size = 0;
+               int duration = 0;
+               ReByteBuffer answer, data;
+               client.setLogSendReceive(false);
+               int64_t sizeCurrent = 0;
+               for (int ii = 0; ii < rounds; ii++) {
+                       client.send(command, message.str(), message.length());
+                       client.receive(answer, data);
+                       size += message.length() + data.length();
+                       sizeCurrent += message.length() + data.length();
+                       time_t now = time(NULL);
+                       if (now >= lastPrint + interval) {
+                               duration = int(now - start);
+                               int durationCurrent = int(now - lastPrint);
+                               printf("%2d: %9.3f MiByte %8.3f / %8.3f kiByte/sec %s\n", ii,
+                                   size / 1024.0 / 1024,
+                                   (double) sizeCurrent / durationCurrent / 1024,
+                                   (double) size / duration / 1024, upload ? "up" : "down");
+                               lastPrint = now;
+                               sizeCurrent = 0;
+                       }
+               }
+               duration = int(time(NULL) - start);
+               if (duration == 0)
+                       duration = 1;
+               printf("%2d: %9.3f MiByte %8.3f kiByte %s/sec %s\n", rounds,
+                   size / 1024.0 / 1024, (double) size / duration / 1024,
+                   upload ? "up" : "down");
+
+       }
+}
+/**
+ * Constructor.
+ *
+ * @param logger       logger for error handling
+ */
+ReDirWhich::ReDirWhich(ReLogger* logger) :
+           ReTool(s_batchUsage, s_batchExamples, 0, 0, 0, true, logger) {
+// no standard options:
+       m_programArgs.addBool("all",
+           i18n("all files will be found, not only the first"), 'a', "all", false);
+       m_programArgs.addString("list",
+           i18n("a path list (separator see option 'separator'"), 'l', "list",
+           false,
+           NULL);
+       m_programArgs.addString("separator",
+           i18n("separator between the path elements"), 's', "separator", false,
+#if defined __linux__
+           ":"
+#elif defined __WIN32__
+           ";"
+#endif
+)      ;
+       m_programArgs.addString("variable", i18n("variable with the path list"),
+           'v', "variable", false, "PATH");
+       m_hasStandardArgs = false;
+}
+
+/**
+ * Creates the batch file.
+ */
+void ReDirWhich::doIt() {
+       ReByteBuffer value, path;
+       bool all = false;
+       ReStringList items;
+       char sep = 0;
+       m_programArgs.getString("list", path);
+       if (path.length() == 0) {
+               m_programArgs.getString("variable", value);
+               if (getenv(value.str()) == NULL)
+                       help("Umgebungsvariable nicht definiert: ", value.str());
+               else
+                       path = getenv(value.str());
+               m_programArgs.getString("separator", value);
+               sep = value.at(0);
+       }
+       items.split(path.str(), sep);
+       struct stat info;
+       ReByteBuffer full;
+       for (int ix = 0; ix < m_programArgs.argCount(); ix++) {
+               bool found = false;
+
+               ReByteBuffer arg(m_programArgs.arg(ix));
+               for (size_t ixItem = 0; ixItem < items.count(); ixItem++) {
+                       full.set(items.strOf(ixItem));
+                       if (arg.indexOf('*') < 0) {
+                               full.ensureLastChar(OS_SEPARATOR_CHAR);
+                               full.append(arg);
+                               found = stat(full.str(), &info) == 0;
+                               if (found)
+                                       printf("%s\n", full.str());
+                       } else {
+                               ReDirectory dir(full.str());
+                               if (dir.findFirst(arg.str(), false)) {
+                                       do {
+                                               printf("%s%c%s\n", full.str(), OS_SEPARATOR_CHAR,
+                                                   dir.currentFile());
+                                       } while (dir.findNext());
+                               }
+                       }
+                       if (found && !all)
+                               break;
+               }
+       }
+}
+
+/**
+ * Tests whether a abrevation of an argument is given.
+ * @param full      the full name
+ * @param part      the part to test
+ * @return          <code>true</code>: part is a prefix of full
+ */
+static bool isArg(const char* full, const char* part) {
+       ReByteBuffer fullArg(full);
+       bool rc = fullArg.startsWith(part, -1);
+       return rc;
+}
+
+/**
+ * Gets the arguments for the "help" command and execute this.
+ *
+ * @param argc      the number of arguments
+ * @param argav     the argument vector
+ */
+void ReDirTools::help(int argc, const char* argv[]) {
+       if (argc <= 1)
+               printField(s_helpSummary);
+       else {
+               argc--;
+               argv++;
+               const char* arg0 = argv[0];
+               if (isArg("batch", arg0)) {
+                       ReDirBatch(m_logger).help(NULL);
+               } else if (isArg("checksum", arg0)) {
+                       ReDirChecksum(m_logger).help(NULL);
+               } else if (_stricmp("delete", arg0) == 0) {
+                       ReDirDelete(m_logger).help(NULL);
+               } else if (isArg("list", arg0)) {
+                       ReDirList(m_logger).help(NULL);
+               } else if (isArg("help", arg0)) {
+                       printField(s_helpSummary);
+               } else if (isArg("random", arg0)) {
+                       ReDirRandom(m_logger).help(NULL);
+               } else if (isArg("statistic", arg0)) {
+                       ReDirStatistic(m_logger).help(NULL);
+               } else if (isArg("test", arg0)) {
+                       void testAll();
+                       testAll();
+               } else if (isArg("tcp", arg0)) {
+                       ReDirTCP(m_logger).help(NULL);
+               } else if (isArg("touch", arg0)) {
+                       ReDirTouch(m_logger).help(NULL);
+               } else if (isArg("which", arg0)) {
+                       ReDirWhich(m_logger).help(NULL);
+               } else
+                       printf("+++ unknown sub command: %s\n", arg0);
+       }
+}
+
+/**
+ * Executes a command.
+ *
+ * @param argc      the number of arguments
+ * @param argv         the argument vector
+ * @param tool         the tool which realizes the command
+ */
+void ReDirTools::run(int argc, const char* argv[], ReTool& tool) {
+       tool.run(argc, argv);
+}
+/**
+ * Gets the arguments for any command and execute this.
+ *
+ * @param argc      the number of arguments
+ * @param orgArgv   the argument vector
+ */
+int ReDirTools::main(int argc, char* orgArgv[]) {
+       ReDirTools tools;
+       if (argc < 0) {
+               argc = 0;
+               for (int ix = 0; orgArgv[ix] != NULL; ix++)
+                       argc++;
+       }
+       const char** argv = (const char**) orgArgv;
+       if (argc < 2) {
+               tools.help(0, argv);
+               exit(1);
+       }
+       argc--;
+       argv++;
+       m_logger = ReLogger::globalLogger();
+       const char* arg0 = argv[0];
+       if (isArg("batch", arg0))
+               ReDirBatch(m_logger).run(argc, argv);
+       else if (isArg("checksum", arg0))
+               ReDirChecksum(m_logger).run(argc, argv);
+       else if (_stricmp("delete", arg0) == 0)
+               ReDirDelete(m_logger).run(argc, argv);
+       else if (isArg("help", arg0))
+               tools.help(argc, argv);
+       else if (isArg("list", arg0))
+               ReDirList(m_logger).run(argc, argv);
+       else if (isArg("random", arg0))
+               ReDirRandom(m_logger).run(argc, argv);
+       else if (isArg("statistic", arg0))
+               ReDirStatistic(m_logger).run(argc, argv);
+       else if (isArg("synchronize", arg0))
+               ReDirSync(m_logger).run(argc, argv);
+       else if (isArg("tcp", arg0))
+               ReDirTCP(m_logger).run(argc, argv);
+       else if (isArg("touch", arg0))
+               ReDirTouch(m_logger).run(argc, argv);
+       else if (isArg("which", arg0))
+               ReDirWhich(m_logger).run(argc, argv);
+       else if (isArg("test", arg0)) {
+               void testAll();
+               testAll();
+       } else
+               tools.usage("unknown command: ", argv[1]);
+       ReTCPServer::globalClose();
+       return 0;
+}
+
index d2f7b600e97b90f90ff77648d38de6a2a9dabea8..8af9434d2e274a447f3434abd98e1b677d535453 100644 (file)
@@ -1,9 +1,11 @@
 /*
- * DirTools.hpp
- *
- * License: Public domain
+ * ReDirTools.hpp
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
@@ -291,9 +293,10 @@ public:
 protected:
        virtual void doIt();
 private:
-       void runMixedClient(const char* ip, int port, int rounds, int interval, int bufferSize);
-       void runOneThreadClient(const char* ip, int port, int rounds,
-           int interval, int bufferSize, bool upload);
+       void runMixedClient(const char* ip, int port, int rounds, int interval,
+           int bufferSize);
+       void runOneThreadClient(const char* ip, int port, int rounds, int interval,
+           int bufferSize, bool upload);
 };
 
 /**
index 0207988e01db7fa0fa6c07aea89d7eb053fdef8b..35943c0f6a30bfca6a369d087a335aaf281c066a 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * ReRemoteDir.cpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
@@ -17,13 +19,12 @@ int ReRemoteDir::m_serialNo = (CLASSID_REMOTE_DIR << 8) + 1;
  * Constructor.
  */
 ReRemoteDir::ReRemoteDir() :
-       ReSerializable(m_serialNo),
-       m_parent(NULL),
-       m_indexFileInParent(-1),
-       m_namePool(),
-       m_fileCount(0),
-       m_files(NULL)
-{
+           ReSerializable(m_serialNo),
+           m_parent(NULL),
+           m_indexFileInParent(-1),
+           m_namePool(),
+           m_fileCount(0),
+           m_files(NULL) {
 }
 
 /**
@@ -39,15 +40,14 @@ ReRemoteDir::~ReRemoteDir() {
  * @param sequence     IN/OUT: the serialized byte sequence
  * @param length       INT/OUT the length of <code>sequence</code>
  */
-void ReRemoteDir::deserialize(uint8_t*& sequence, size_t& length){
-
+void ReRemoteDir::deserialize(uint8_t*& sequence, size_t& length) {
 }
 /**
  * Reads the info from a directory into the instance.
  *
  * @param path         the full path name of the directory to read
  */
-void ReRemoteDir::populate(const char* path){
+void ReRemoteDir::populate(const char* path) {
        ReTraverser dir(path);
 
 }
@@ -57,22 +57,22 @@ void ReRemoteDir::populate(const char* path){
  *
  * @param sequence     IN/OUT: the place for the byte sequence
  */
-ReByteBuffer& ReRemoteDir::serialize(ReByteBuffer& sequence){
+ReByteBuffer& ReRemoteDir::serialize(ReByteBuffer& sequence) {
 }
 
 /**
  * Constructor.
  */
 ReRemoteDirService::ReRemoteDirService() :
-       ReNetCommandHandler(){
+           ReNetCommandHandler() {
 
 }
 
-ReRemoteDirService::~ReRemoteDirService(){
+ReRemoteDirService::~ReRemoteDirService() {
 }
 
-ReNetCommandHandler::ProcessingState ReRemoteDirService::handleNetCommand(ReByteBuffer& command,
-           ReByteBuffer& data, ReTCPConnection* connection){
+ReNetCommandHandler::ProcessingState ReRemoteDirService::handleNetCommand(
+    ReByteBuffer& command, ReByteBuffer& data, ReTCPConnection* connection) {
 
 }
 
index c8837de6297e56c86c99266eb3ef9cabc74d1b79..9e257c1929198fd22e34b1301951d82e5ed30a64 100644 (file)
@@ -1,8 +1,12 @@
 /*
  * ReRemoteDir.hpp
- *
- *  Created on: 25.03.2015
- *      Author: hm
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
+ * Do what you want.
+ * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
+ * The latest sources: https://github.com/republib
  */
 
 #ifndef REREMOTEDIR_HPP_
@@ -22,7 +26,7 @@ public:
 /*
  *
  */
-class ReRemoteDir : public ReSerializable {
+class ReRemoteDir: public ReSerializable {
 public:
        ReRemoteDir();
        virtual ~ReRemoteDir();
@@ -43,7 +47,7 @@ private:
 /**
  * A TCP server for file and directory processing.
  */
-class ReRemoteDirService : public ReNetCommandHandler {
+class ReRemoteDirService: public ReNetCommandHandler {
 public:
        ReRemoteDirService();
        ~ReRemoteDirService();
index 184b05c729d7f1aeb63bf51dfa1cb93eb96948b7..19231f576cbfe9e719166b4c2cb3b6ec36c9be89 100644 (file)
-/*\r
- * ReTraverser.cpp\r
- *\r
- * License: Public domain\r
- * Do what you want.\r
- * No warranties and disclaimer of any damages.\r
- * The latest sources: https://github.com/republib\r
- */\r
-\r
-#include "base/rebase.hpp"\r
-#include "os/reos.hpp"\r
-#if defined __WIN32__\r
-#include "accctrl.h"\r
-#include "aclapi.h"\r
-#pragma comment(lib, "advapi32.lib")\r
-#endif\r
-\r
-enum RELOC_TRAVERSER {\r
-       LC_RIGHTS_AS_STRING_1 = LC_TRAVERSER + 1, // 50401\r
-       LC_RIGHTS_AS_STRING_2,  // 50402\r
-       LC_RIGHTS_AS_STRING_3,  // 50403\r
-       LC_GET_PRIVILEGE_1,             // 50404\r
-       LC_GET_PRIVILEGE_2,             // 50405\r
-       LC_GET_PRIVILEGE_3,             // 50406\r
-       LC_GET_FILE_OWNER_1,    // 50407\r
-       LC_GET_FILE_OWNER_2,    // 50408\r
-};\r
-\r
-int ReDirEntryFilter::m_serialId = buildSerialId(CLASSID_DIR_ENTRY_FILTER, 1);\r
-\r
-/**\r
- * Constructor.\r
- */\r
-ReDirStatus_t::ReDirStatus_t(ReLogger* logger) :\r
-           m_path(),\r
-           m_fullName(),\r
-           m_passNo(0),\r
-           m_logger(logger),\r
-#ifdef __linux__\r
-           m_handle(NULL),\r
-           m_data(NULL)\r
-//m_status;\r
-#elif defined WIN32\r
-m_handle(INVALID_HANDLE_VALUE),\r
-//m_data;\r
-m_getPrivilege(true)\r
-#endif\r
-{\r
-#ifdef __linux__\r
-       memset(&m_status, 0, sizeof m_status);\r
-#elif defined WIN32\r
-       memset(&m_data, 0, sizeof m_data);\r
-#endif\r
-}\r
-\r
-/**\r
- * Returns the last access time.\r
- *\r
- * @return     the last access time\r
- */\r
-const ReFileTime_t* ReDirStatus_t::accessed() {\r
-#ifdef __linux__\r
-       return &(getStatus()->st_atim);\r
-#elif defined __WIN32__\r
-       return &m_data.ftLastAccessTime;\r
-#endif\r
-}\r
-\r
-/**\r
- * Returns the filesize.\r
- *\r
- * @return     the filesize\r
- */\r
-ReFileSize_t ReDirStatus_t::fileSize() {\r
-#ifdef __linux__\r
-       return getStatus()->st_size;\r
-#elif defined __WIN32__\r
-       return ((int64_t) m_data.nFileSizeHigh << 32) + m_data.nFileSizeLow;\r
-#endif\r
-}\r
-\r
-/**\r
- * Returns the file time as a string.\r
- *\r
- * @param buffer    OUT: the file time\r
- * @return          <code>buffer.str()</code> (for chaining)\r
- */\r
-const char* ReDirStatus_t::filetimeAsString(ReByteBuffer& buffer) {\r
-       return filetimeToString(modified(), buffer);\r
-}\r
-\r
-/**\r
- * Converts a filetime to a string.\r
- *\r
- * @param time         the filetime to convert\r
- * @param buffer       OUT: the buffer for the string\r
- * @return                     <code>buffer.str()</code>, e.g. "2014.01.07 02:59:43"\r
- */\r
-const char* ReDirStatus_t::filetimeToString(const ReFileTime_t* time,\r
-    ReByteBuffer& buffer) {\r
-       time_t time1 = filetimeToTime(time);\r
-       struct tm* time2 = localtime(&time1);\r
-       buffer.setLength(4 + 2 * 2 + 2 * 2 + 1 + 3 * 2 + 2 * 1);\r
-       strftime(buffer.buffer(), buffer.length(), "%Y.%m.%d %H:%M:%S", time2);\r
-       return buffer.str();\r
-}\r
-\r
-/**\r
- * Converts a filetime to a unix time (seconds since the Epoche).\r
- *\r
- * @param filetime             the filetime to convert\r
- * @return                             the count of seconds since 1.1.1970\r
- */\r
-time_t ReDirStatus_t::filetimeToTime(const ReFileTime_t* filetime) {\r
-#ifdef __linux__\r
-       return filetime->tv_sec;\r
-#elif defined __WIN32__\r
-       // 64-bit arithmetic:\r
-       LARGE_INTEGER date, adjust;\r
-       date.HighPart = filetime->dwHighDateTime;\r
-       date.LowPart = filetime->dwLowDateTime;\r
-       // 100-nanoseconds = milliseconds * 10000\r
-       adjust.QuadPart = 11644473600000 * 10000;\r
-       // removes the diff between 1970 and 1601\r
-       date.QuadPart -= adjust.QuadPart;\r
-       // converts back from 100-nanoseconds to seconds\r
-       time_t rc = (time_t) (date.QuadPart / 10000000);\r
-       return rc;\r
-#endif\r
-}\r
-\r
-/**\r
- * Loads the info about the first file into the instance.\r
- *\r
- * @return     <code>true</code>: success\r
- */\r
-bool ReDirStatus_t::findFirst() {\r
-       bool rc = false;\r
-#if defined __linux__\r
-       if (m_handle != NULL)\r
-               closedir(m_handle);\r
-       m_handle = opendir(m_path.str());\r
-       rc = m_handle != NULL && (m_data = readdir(m_handle)) != NULL;\r
-       m_status.st_ino = 0;\r
-#elif defined __WIN32__\r
-       if (m_handle != INVALID_HANDLE_VALUE)\r
-       FindClose(m_handle);\r
-       ReByteBuffer thePath(m_path);\r
-       thePath.append(m_path.lastChar() == '\\' ? "*" : "\\*");\r
-       m_handle = FindFirstFileA(thePath.str(), &m_data);\r
-       rc = m_handle != INVALID_HANDLE_VALUE;\r
-#endif\r
-       m_fullName.setLength(0);\r
-       return rc;\r
-}\r
-\r
-/**\r
- * Loads the info about the next file into the instance.\r
- *\r
- * @return     <code>true</code>: success\r
- */\r
-bool ReDirStatus_t::findNext() {\r
-#if defined __linux__\r
-       bool rc = m_handle != NULL && (m_data = readdir(m_handle)) != NULL;\r
-       m_status.st_ino = 0;\r
-#elif defined __WIN32__\r
-       bool rc = m_handle != INVALID_HANDLE_VALUE && FindNextFileA(m_handle, &m_data);\r
-#endif\r
-       m_fullName.setLength(0);\r
-       return rc;\r
-}\r
-\r
-/**\r
- * Frees the resources of an instance.\r
- */\r
-void ReDirStatus_t::freeEntry() {\r
-#if defined __linux__\r
-       if (m_handle != NULL) {\r
-               closedir(m_handle);\r
-               m_handle = NULL;\r
-       }\r
-#elif defined __WIN32__\r
-       if (m_handle != INVALID_HANDLE_VALUE) {\r
-               FindClose(m_handle);\r
-               m_handle = INVALID_HANDLE_VALUE;\r
-       }\r
-#endif\r
-       m_path.setLength(0);\r
-       m_fullName.setLength(0);\r
-}\r
-\r
-/**\r
- * Returns the full filename (with path).\r
- *\r
- * @return     the filename with path\r
- */\r
-const char* ReDirStatus_t::fullName() {\r
-       if (m_fullName.length() == 0)\r
-               m_fullName.set(m_path).append(node(), -1);\r
-       return m_fullName.str();\r
-}\r
-\r
-#if defined __WIN32__\r
-/** Gets the name of the file owner.\r
- *\r
- * @param handle               file handle (see <code>CreateFile()</code>)\r
- * @param name         OUT: the owner: [domain\\]name\r
- * @return                     <code>true</code>: success\r
- */\r
-bool ReDirStatus_t::getFileOwner(HANDLE handle, const char* file,\r
-       ReByteBuffer& name, ReLogger* logger) {\r
-       bool rc = false;\r
-       PSID pSidOwner = NULL;\r
-       PSECURITY_DESCRIPTOR pSD = NULL;\r
-       if (GetSecurityInfo(handle, SE_FILE_OBJECT,\r
-                       OWNER_SECURITY_INFORMATION, &pSidOwner, NULL, NULL, NULL, &pSD) != ERROR_SUCCESS) {\r
-               if (logger != NULL)\r
-               logger->sayF(LOG_ERROR | CAT_FILE, LC_GET_FILE_OWNER_1,\r
-                       "GetSecurityInfo($1): $2").arg(file).arg((int) GetLastError()).end();\r
-       } else {\r
-               char accountName[128];\r
-               char domainName[128];\r
-               DWORD dwAcctName = sizeof accountName;\r
-               DWORD dwDomainName = sizeof domainName;\r
-               SID_NAME_USE eUse = SidTypeUnknown;\r
-               if (! LookupAccountSid(NULL, pSidOwner, accountName, &dwAcctName, domainName,\r
-                               &dwDomainName, &eUse)) {\r
-                       if (logger != NULL)\r
-                       logger->sayF(LOG_ERROR | CAT_SECURITY, LC_GET_FILE_OWNER_2,\r
-                               "LookupAccountSid(): $1").arg((int) GetLastError()).end();\r
-               } else {\r
-                       if (dwDomainName > 0)\r
-                       name.append(domainName).appendChar('\\');\r
-                       name.append(accountName);\r
-                       rc = true;\r
-               }\r
-       }\r
-       return rc;\r
-}\r
-#endif /* __WIN32__ */\r
-\r
-#if defined __WIN32__\r
-/** Tries to get a privilege.\r
- *\r
- * @param privilege    the name of the privilege, e.g. "SeBackup"\r
- * @param logger               logger for error logging\r
- */\r
-bool ReDirStatus_t::getPrivilege(const char* privilege, ReLogger* logger) {\r
-       bool rc = false;\r
-       LUID luidPrivilege;\r
-       HANDLE hAccessToken;\r
-       if (! OpenProcessToken (GetCurrentProcess(),\r
-                       TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hAccessToken)) {\r
-               if (logger != NULL)\r
-               logger->sayF(LOG_ERROR | CAT_FILE, LC_GET_PRIVILEGE_1,\r
-                       "OpenProcessToken(): $1").arg((int) GetLastError()).end();\r
-       } else if (! LookupPrivilegeValue (NULL, SE_BACKUP_NAME, &luidPrivilege)) {\r
-               if (logger != NULL)\r
-               logger->sayF(LOG_ERROR | CAT_FILE, LC_GET_PRIVILEGE_2,\r
-                       "LookupPrivilegeValue(): $1").arg((int) GetLastError()).end();\r
-       } else {\r
-               TOKEN_PRIVILEGES tpPrivileges;\r
-               tpPrivileges.PrivilegeCount = 1;\r
-               tpPrivileges.Privileges[0].Luid = luidPrivilege;\r
-               tpPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;\r
-               if (AdjustTokenPrivileges (hAccessToken, FALSE, &tpPrivileges,\r
-                               0, NULL, NULL) == 0)\r
-               rc = true;\r
-               else {\r
-                       int error = GetLastError();\r
-                       if (error != 1300 && logger != NULL)\r
-                       logger->sayF(LOG_ERROR | CAT_FILE, LC_GET_PRIVILEGE_3,\r
-                               "AdjustTokenPrivileges(): $1").arg((int) GetLastError()).end();\r
-               }\r
-       }\r
-       return rc;\r
-}\r
-#endif /* __WIN32__ */\r
-\r
-/**\r
- * Tests whether the instance is a directory.\r
- *\r
- * @return     <code>true</code>: instance contains the data of a directory\r
- */\r
-bool ReDirStatus_t::isDirectory() {\r
-#ifdef __linux__\r
-       return m_data->d_type == DT_DIR\r
-           || (m_data->d_type == DT_UNKNOWN && S_ISDIR(getStatus()->st_mode));\r
-#elif defined __WIN32__\r
-       return 0 != (m_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);\r
-#endif\r
-}\r
-\r
-/**\r
- * Tests whether the instance contains data about "." or "..".\r
- *\r
- * @return     <code>true</code>: an ignorable entry has been found\r
- */\r
-bool ReDirStatus_t::isDotDir() const {\r
-#ifdef __linux__\r
-       bool rc = m_data == NULL\r
-           || (m_data->d_name[0] == '.'\r
-               && (m_data->d_name[1] == '\0'\r
-                   || (m_data->d_name[1] == '.' && m_data->d_name[2] == '\0')));\r
-#elif defined __WIN32__\r
-       bool rc = m_data.cFileName[0] == '.' && (m_data.cFileName[1] == '\0'\r
-               || (m_data.cFileName[1] == '.' && m_data.cFileName[2] == '\0'));\r
-#endif\r
-       return rc;\r
-}\r
-\r
-/**\r
- * Tests whether the instance is a symbolic link.\r
- *\r
- * Unter windows it tests whether the the instance is a reparse point.\r
- *\r
- * @return     <code>true</code>: instance contains the data of a link\r
- */\r
-bool ReDirStatus_t::isLink() {\r
-       bool rc;\r
-#ifdef __linux__\r
-       rc = m_data->d_type == DT_LNK\r
-           || (m_data->d_type == DT_UNKNOWN && S_ISLNK(getStatus()->st_mode));\r
-#elif defined __WIN32__\r
-       rc = 0 != (m_data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT);\r
-#endif\r
-       return rc;\r
-}\r
-/**\r
- * Tests whether the instance is a "normal" file.\r
- *\r
- * @return     <code>true</code>: instance contains the data of a not special file\r
- */\r
-bool ReDirStatus_t::isRegular() {\r
-#ifdef __linux__\r
-       return m_data->d_type == DT_REG\r
-           || (m_data->d_type == DT_UNKNOWN && S_ISREG(getStatus()->st_mode));\r
-#elif defined __WIN32__\r
-       return 0 == (m_data.dwFileAttributes & (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE));\r
-#endif\r
-}\r
-/**\r
- * Returns the modification time.\r
- *\r
- * @return     the modification time\r
- */\r
-const ReFileTime_t* ReDirStatus_t::modified() {\r
-#ifdef __linux__\r
-       return &(getStatus()->st_mtim);\r
-#elif defined __WIN32__\r
-       return &m_data.ftLastWriteTime;\r
-#endif\r
-}\r
-\r
-/**\r
- * Returns the name of the current file (without path).\r
- *\r
- * @return     the name of the current file.\r
- */\r
-const char* ReDirStatus_t::node() const {\r
-#ifdef __linux__\r
-       return m_data->d_name;\r
-#elif defined __WIN32__\r
-       return m_data.cFileName;\r
-#endif\r
-}\r
-\r
-inline void addRight(int mode, ReByteBuffer& buffer) {\r
-       char right;\r
-       switch (mode & 7) {\r
-       case 1:\r
-               right = 'x';\r
-               break;\r
-       case 2:\r
-               right = 'w';\r
-               break;\r
-       case 3:\r
-               right = 'X';\r
-               break;\r
-       case 4:\r
-               right = 'r';\r
-               break;\r
-       case 5:\r
-               right = 'R';\r
-               break;\r
-       case 6:\r
-               right = 'W';\r
-               break;\r
-       case 7:\r
-               right = 'A';\r
-               break;\r
-       default:\r
-               right = '-';\r
-               break;\r
-       }\r
-       buffer.appendChar(right);\r
-}\r
-inline void addId(const char* id, int maxLength, ReByteBuffer& buffer) {\r
-       int length = strlen(id);\r
-       if (length == maxLength)\r
-               buffer.append(id, length);\r
-       else if (length < maxLength)\r
-               buffer.append(id, length).appendChar(' ', maxLength - length);\r
-       else {\r
-               buffer.append(id, 2);\r
-               buffer.append(id + length - maxLength - 2, maxLength - 2);\r
-       }\r
-}\r
-/**\r
- * Returns the file rights as a string.\r
- *\r
- * @param buffer               OUT: the file rights\r
- * @param numerical            <code>true</code>: the owner/group should be numerical (UID/GID)\r
- * @param ownerWidth   the width for group/owner\r
- * @return                             <code>buffer.str()</code> (for chaining)\r
- */\r
-const char* ReDirStatus_t::rightsAsString(ReByteBuffer& buffer, bool numerical,\r
-    int ownerWidth) {\r
-       buffer.setLength(0);\r
-#if defined __linux__\r
-       if (numerical) {\r
-               buffer.appendInt(getStatus()->st_mode & ALLPERMS, "%04o");\r
-               buffer.appendInt(getStatus()->st_uid, " %4d");\r
-               buffer.appendInt(getStatus()->st_gid, " %4d");\r
-       } else {\r
-               int mode = getStatus()->st_mode & ALLPERMS;\r
-               addRight(mode >> 6, buffer);\r
-               addRight(mode >> 3, buffer);\r
-               addRight(mode, buffer);\r
-               buffer.appendChar(' ');\r
-               struct passwd* passwd = getpwuid(getStatus()->st_uid);\r
-               if (passwd == NULL)\r
-                       buffer.appendInt(getStatus()->st_uid, "%4d");\r
-               else\r
-                       addId(passwd->pw_name, 5, buffer);\r
-               buffer.appendChar(' ');\r
-               struct group* group = getgrgid(getStatus()->st_gid);\r
-               if (group == NULL)\r
-                       buffer.appendInt(getStatus()->st_gid, "%4d");\r
-               else\r
-                       addId(group->gr_name, 5, buffer);\r
-               buffer.appendChar(' ');\r
-       }\r
-#elif defined __WIN32__\r
-       const char* name = fullName();\r
-       HANDLE handle = INVALID_HANDLE_VALUE;\r
-       if (! isDirectory()) {\r
-               if ( (handle = CreateFile(name, GENERIC_READ, FILE_SHARE_READ, NULL,\r
-                                       OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)\r
-               m_logger->sayF(LOG_ERROR | CAT_FILE, LC_RIGHTS_AS_STRING_1,\r
-                       "CreateFile($1): $2").arg(name).arg((int) GetLastError()).end();\r
-       } else if (m_getPrivilege) {\r
-               // we try only one time:\r
-               m_getPrivilege = false;\r
-               if (getPrivilege(SE_BACKUP_NAME, m_logger)) {\r
-                       if ( (handle = CreateFile(name, 0, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,\r
-                                               NULL)) != INVALID_HANDLE_VALUE)\r
-                       m_logger->sayF(LOG_ERROR | CAT_FILE, LC_RIGHTS_AS_STRING_2,\r
-                               "CreateFile($1): $2").arg(name).arg((int) GetLastError()).end();\r
-               }\r
-\r
-       }\r
-       ReByteBuffer owner;\r
-       if (handle != INVALID_HANDLE_VALUE)\r
-       getFileOwner(handle, name, owner, m_logger);\r
-       CloseHandle(handle);\r
-       buffer.appendFix(owner.str(), owner.length(), ownerWidth, ownerWidth);\r
-#endif\r
-       return buffer.str();\r
-}\r
-\r
-/**\r
- * Converts the unix time (time_t) to the file time.\r
- *\r
- * @param time                 the unix time (secondes since 1.1.1970)\r
- * @param filetime     OUT: the OS specific filetime\r
- */\r
-void ReDirStatus_t::timeToFiletime(time_t time, ReFileTime_t& filetime) {\r
-#ifdef __linux__\r
-       filetime.tv_sec = time;\r
-       filetime.tv_nsec = 0;\r
-#elif defined __WIN32__\r
-       LONGLONG ll = Int32x32To64(time, 10000000) + 116444736000000000;\r
-       filetime.dwLowDateTime = (DWORD)ll;\r
-       filetime.dwHighDateTime = ll >> 32;\r
-#endif\r
-}\r
-/**\r
- * Returns the type of the entry.\r
- * return       the file type, e.g. TF_REGULAR\r
- */\r
-ReDirStatus_t::Type_t ReDirStatus_t::type() {\r
-       Type_t rc = TF_UNDEF;\r
-#if defined __linux__\r
-       int flags = getStatus()->st_mode;\r
-       if (S_ISDIR(flags))\r
-               rc = TF_SUBDIR;\r
-       else if (flags == 0 || S_ISREG(flags))\r
-               rc = TF_REGULAR;\r
-       else if (S_ISLNK(flags))\r
-               rc = TF_LINK;\r
-       else if (S_ISCHR(flags))\r
-               rc = TF_CHAR;\r
-       else if (S_ISBLK(flags))\r
-               rc = TF_BLOCK;\r
-       else if (S_ISFIFO(flags))\r
-               rc = TF_PIPE;\r
-       else if (S_ISSOCK(flags))\r
-               rc = TF_SOCKET;\r
-       else\r
-               rc = TF_OTHER;\r
-#elif defined __WIN32__\r
-       int flags = (m_data.dwFileAttributes & ~(FILE_ATTRIBUTE_READONLY\r
-                       | FILE_ATTRIBUTE_HIDDEN\r
-                       | FILE_ATTRIBUTE_SYSTEM\r
-                       | FILE_ATTRIBUTE_ARCHIVE\r
-                       | FILE_ATTRIBUTE_NORMAL\r
-                       | FILE_ATTRIBUTE_TEMPORARY\r
-                       | FILE_ATTRIBUTE_SPARSE_FILE\r
-                       | FILE_ATTRIBUTE_COMPRESSED\r
-                       | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED\r
-                       | FILE_ATTRIBUTE_ENCRYPTED\r
-                       | FILE_ATTRIBUTE_HIDDEN));\r
-\r
-       if (0 == flags)\r
-       rc = TF_REGULAR;\r
-       else if (0 != (m_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {\r
-               rc = (0 != (m_data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))\r
-               ? TF_LINK_DIR : TF_SUBDIR;\r
-       } else if (0 != (m_data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))\r
-       rc = TF_LINK;\r
-       else\r
-       rc = TF_OTHER;\r
-#endif\r
-       return rc;\r
-}\r
-\r
-/**\r
- * Returns the filetype as a single character.\r
- *\r
- * @return     the filetype, e.g. 'd' for a directory\r
- */\r
-char ReDirStatus_t::typeAsChar() {\r
-       char rc = ' ';\r
-       switch (type()) {\r
-       case TF_REGULAR:\r
-               rc = ' ';\r
-               break;\r
-       case TF_LINK:\r
-               rc = 'l';\r
-               break;\r
-       case TF_SUBDIR:\r
-               rc = 'd';\r
-               break;\r
-       case TF_CHAR:\r
-               rc = 'c';\r
-               break;\r
-       case TF_BLOCK:\r
-               rc = 'b';\r
-               break;\r
-       case TF_PIPE:\r
-               rc = 'p';\r
-               break;\r
-       case TF_SOCKET:\r
-               rc = 's';\r
-               break;\r
-       default:\r
-               rc = 'o';\r
-               break;\r
-       }\r
-       return rc;\r
-}\r
-/**\r
- * Constructor.\r
- */\r
-ReDirEntryFilter::ReDirEntryFilter() :\r
-               ReSerializable(m_serialId),\r
-           m_types(ReDirStatus_t::TC_ALL),\r
-           m_nodePatterns(NULL),\r
-           m_pathPatterns(NULL),\r
-           m_minSize(0),\r
-           m_maxSize(-1),\r
-           //m_minAge(0),\r
-           //m_maxAge(0),\r
-           m_minDepth(0),\r
-           m_maxDepth(512),\r
-           m_allDirectories(false) {\r
-       setFiletimeUndef(m_minAge);\r
-       setFiletimeUndef(m_maxAge);\r
-}\r
-\r
-/**\r
- * Destructor.\r
- */\r
-ReDirEntryFilter::~ReDirEntryFilter() {\r
-}\r
-\r
-/**\r
- * Sets the members of the instance from a byte sequence.\r
- *\r
- * @param sequence     IN/OUT: the serialized byte sequence\r
- * @param length       INT/OUT the length of <code>sequence</code>\r
- */\r
-void ReDirEntryFilter::deserialize(uint8_t*& sequence, size_t& length){\r
-       /*ReDirStatus_t::Type_t m_types;\r
-       RePatternList* m_nodePatterns;\r
-       RePatternList* m_pathPatterns;\r
-       ReFileSize_t m_minSize;\r
-       ReFileSize_t m_maxSize;\r
-       ReFileTime_t m_minAge;\r
-       ReFileTime_t m_maxAge;\r
-       int m_minDepth;\r
-       int m_maxDepth;\r
-       bool m_allDirectories; */\r
-\r
-\r
-}\r
-/**\r
- * Packs the members into a byte sequence.\r
- *\r
- * @param sequence     IN/OUT: the place for the byte sequence\r
- */\r
-ReByteBuffer& ReDirEntryFilter::serialize(ReByteBuffer& sequence){\r
-       /*ReDirStatus_t::Type_t m_types;\r
-       RePatternList* m_nodePatterns;\r
-       RePatternList* m_pathPatterns;\r
-       ReFileSize_t m_minSize;\r
-       ReFileSize_t m_maxSize;\r
-       ReFileTime_t m_minAge;\r
-       ReFileTime_t m_maxAge;\r
-       int m_minDepth;\r
-       int m_maxDepth;\r
-       bool m_allDirectories; */\r
-       sequence.appendBits24(m_serialId);\r
-       packString255(sequence, m_nodePatterns->patternString());\r
-\r
-}\r
-\r
-\r
-/**\r
- * Tests whether an entry matches the conditions of the filter.\r
- *\r
- * @param entry                entry to test\r
- * @return                     <true>: the entry matches the conditions of the filter<br>\r
- *                                     <false>: otherwise\r
- */\r
-bool ReDirEntryFilter::match(ReDirStatus_t& entry) {\r
-       bool rc = false;\r
-       do {\r
-               if (m_allDirectories && entry.isDirectory()) {\r
-                       rc = true;\r
-                       break;\r
-               }\r
-               if (0 == (entry.type() & m_types))\r
-                       break;\r
-               int64_t size = entry.fileSize();\r
-               if (m_minSize > 0 && size < m_minSize)\r
-                       break;\r
-               if (m_maxSize >= 0 && size > m_maxSize)\r
-                       break;\r
-               if (!filetimeIsUndefined(m_minAge) && *entry.modified() > m_minAge)\r
-                       break;\r
-               if (!filetimeIsUndefined(m_maxAge) && m_maxAge > *entry.modified())\r
-                       break;\r
-               const char* node = entry.node();\r
-               if (m_nodePatterns != NULL && !m_nodePatterns->match(node))\r
-                       break;\r
-               rc = true;\r
-       } while (false);\r
-       return rc;\r
-}\r
-;\r
-\r
-#ifdef __linux__\r
-/**\r
- * Returns the status of the current file (lazy loading).\r
- *\r
- * @return     the status of the current file\r
- */\r
-struct stat* ReDirStatus_t::getStatus() {\r
-       if (m_status.st_ino == 0) {\r
-               if (stat(fullName(), &m_status) != 0)\r
-                       m_status.st_ino = 0;\r
-       }\r
-       return &m_status;\r
-}\r
-#endif\r
-\r
-/**\r
- * Constructor.\r
- */\r
-ReDirTreeStatistic::ReDirTreeStatistic() :\r
-           m_directories(0),\r
-           m_files(0),\r
-           m_sizes(0ll) {\r
-}\r
-/**\r
- * Builds a string describing the data.\r
- *\r
- * @param buffer               IN/OUT: a buffer for the result\r
- * @param append               <code>true</code>: the string will be appended to the buffer<br>\r
- *                                             <code>false</code>: the buffer will be cleared at the beginning\r
- * @param formatFiles  the <code>sprintf</code> format for the file count, e.g. "%8d"\r
- * @param formatSized  the <code>sprintf</code> format for the MByte format, e.g. "%12.6f"\r
- * @param formatFiles  the <code>sprintf</code> format for the directory count, e.g. "%6d"\r
- * @return                             a human readable string\r
- */\r
-const char* ReDirTreeStatistic::statisticAsString(ReByteBuffer& buffer,\r
-    bool append, const char* formatFiles, const char* formatSizes,\r
-    const char* formatDirs) {\r
-       if (!append)\r
-               buffer.setLength(0);\r
-       buffer.appendInt(m_files, formatFiles);\r
-       buffer.append(i18n("file(s)")).appendChar(' ');\r
-       buffer.append(m_sizes / 1000.0 / 1000, formatSizes);\r
-       buffer.append(" ", 1).append(i18n("MByte")).appendChar(' ');\r
-       buffer.appendInt(m_directories, formatDirs);\r
-       buffer.append(i18n("dirs(s)"));\r
-       return buffer.str();\r
-}\r
-\r
-/**\r
- * Constructor.\r
- *\r
- * @param triggerCount efficiency: only every N calls a time check takes place\r
- * @param interval             the minimum number of seconds between two traces\r
- */\r
-ReTraceUnit::ReTraceUnit(int triggerCount, int interval) :\r
-           m_count(0),\r
-           m_triggerCount(triggerCount),\r
-           m_lastTrace(0),\r
-           m_interval(interval),\r
-           m_startTime(time(NULL)) {\r
-       m_lastTrace = m_startTime;\r
-}\r
-/**\r
- * Destructor.\r
- */\r
-ReTraceUnit::~ReTraceUnit() {\r
-}\r
-\r
-/**\r
- * Prints a message.\r
- *\r
- * Often overwritten by a subclass.\r
- *\r
- * @param message      message for the trace\r
- * @return                     <code>true</code> (for chaining)\r
- */\r
-bool ReTraceUnit::trace(const char* message) {\r
-       printf("%s\n", message);\r
-       return true;\r
-}\r
-\r
-/**\r
- * Constructor.\r
- *\r
- * @param base         the base directory. The traversal starts at this point\r
- */\r
-ReTraverser::ReTraverser(const char* base, ReTraceUnit* tracer,\r
-    ReLogger* logger) :\r
-           ReDirTreeStatistic(),\r
-           m_minLevel(0),\r
-           m_maxLevel(512),\r
-           m_level(-1),\r
-           m_base(base),\r
-           // m_dirs\r
-           m_passNoForDirSearch(2),\r
-           m_dirPatterns(NULL),\r
-           m_tracer(tracer),\r
-           m_logger(logger) {\r
-       memset(m_dirs, 0, sizeof m_dirs);\r
-       m_dirs[0] = new ReDirStatus_t(m_logger);\r
-       // remove a preceeding "./". This simplifies the pattern expressions:\r
-       if (m_base.startsWith(\r
-           ReByteBuffer(".").appendChar(OS_SEPARATOR_CHAR).str())) {\r
-               m_base.remove(0, 2);\r
-       }\r
-}\r
-\r
-/**\r
- * Destructor.\r
- */\r
-ReTraverser::~ReTraverser() {\r
-       destroy();\r
-}\r
-\r
-/**\r
- * Initializes the instance to process a new base.\r
- *\r
- * @param base the base directory to search\r
- */\r
-void ReTraverser::changeBase(const char* base) {\r
-       destroy();\r
-       m_base.setLength(0).append(base);\r
-       memset(m_dirs, 0, sizeof m_dirs);\r
-       m_dirs[0] = new ReDirStatus_t(m_logger);\r
-       // remove a preceeding "./". This simplifies the pattern expressions:\r
-       if (m_base.startsWith(\r
-           ReByteBuffer(".").appendChar(OS_SEPARATOR_CHAR).str())) {\r
-               m_base.remove(0, 2);\r
-       }\r
-}\r
-\r
-/**\r
- * Releases the resources.\r
- */\r
-void ReTraverser::destroy() {\r
-       for (size_t ix = 0; ix < sizeof m_dirs / sizeof m_dirs[0]; ix++) {\r
-               if (m_dirs[ix] != NULL) {\r
-                       m_dirs[ix]->freeEntry();\r
-                       delete m_dirs[ix];\r
-                       m_dirs[ix] = NULL;\r
-               }\r
-       }\r
-}\r
-/**\r
- * Returns the info about the next file in the directory tree traversal.\r
- *\r
- * @param level        OUT: the level relative to the base.<br>\r
- *                                     0 means the file is inside the base.<br>\r
- *                                     Not defined if the result is NULL\r
- * @return NULL        no more files<br>\r
- *                                     otherwise: the stack entry with the next file in the\r
- *                                     directory tree. May be a directory too\r
- */\r
-ReDirStatus_t* ReTraverser::rawNextFile(int& level) {\r
-       ReDirStatus_t* rc = NULL;\r
-       bool alreadyRead = false;\r
-       bool again;\r
-       do {\r
-               again = false;\r
-               if (m_level < 0) {\r
-                       // Not yet initialized?\r
-                       if (m_dirs[0]->m_passNo == 2)\r
-                               rc = NULL;\r
-                       else {\r
-                               // first call:\r
-                               if (initEntry(m_base.str(), NULL, 0)) {\r
-                                       m_directories++;\r
-                                       if (1 != m_passNoForDirSearch)\r
-                                               rc = m_dirs[0];\r
-                                       else\r
-                                               again = alreadyRead = true;\r
-                               }\r
-                       }\r
-               } else {\r
-                       ReDirStatus_t* current = m_dirs[m_level];\r
-                       if (alreadyRead || current->findNext()) {\r
-                               alreadyRead = false;\r
-                               // a file or directory found:\r
-                               if (m_tracer != NULL && m_tracer->isCountTriggered()\r
-                                   && m_tracer->isTimeTriggered())\r
-                                       m_tracer->trace(current->fullName());\r
-                               if (current->m_passNo != m_passNoForDirSearch) {\r
-                                       // we search for any file:\r
-                                       rc = m_dirs[m_level];\r
-                               } else {\r
-                                       // we are interested only in true subdirectories:\r
-                                       again = true;\r
-                                       if (m_level < m_maxLevel && current->isDirectory()\r
-                                           && !current->isDotDir() && !current->isLink()\r
-                                           && (m_dirPatterns == NULL\r
-                                               || isAllowedDir(current->node()))) {\r
-                                               // open a new level\r
-                                               alreadyRead = initEntry(current->m_path,\r
-                                                   current->node(), m_level + 1);\r
-                                               m_directories++;\r
-                                       }\r
-                               }\r
-                       } else {\r
-                               // the current subdir does not have more files:\r
-                               if (current->m_passNo == 1) {\r
-                                       // we start the second pass:\r
-                                       alreadyRead = initEntry(current->m_path, NULL, -1);\r
-                                       current->m_passNo = 2;\r
-                                       again = true;\r
-                               } else {\r
-                                       // this subdirectory is complete. We continue in the parent directory:\r
-                                       current->freeEntry();\r
-                                       if (--m_level >= 0) {\r
-                                               again = true;\r
-                                       }\r
-                               }\r
-                       }\r
-               }\r
-               if (rc != NULL && rc->isDotDir())\r
-                       again = true;\r
-       } while (again);\r
-       if (rc != NULL && !rc->isDirectory()) {\r
-               m_files++;\r
-               if (m_sizes >= 0)\r
-                       m_sizes += rc->fileSize();\r
-       }\r
-       level = m_level;\r
-       return rc;\r
-}\r
-/**\r
- * Returns the info about the next file matching the filter options.\r
- *\r
- * @param level        OUT: the level relative to the base.<br>\r
- *                                     0 means the file is inside the base.<br>\r
- *                                     Not defined if the result is NULL\r
- * @param filter       NULL: every file matches<br>\r
- *                                     otherwise: each found file must match this filter conditions\r
- * @return NULL        no more files<br>\r
- *                                     otherwise: the info about the next file in the\r
- *                                     directory tree\r
- */\r
-ReDirStatus_t* ReTraverser::nextFile(int& level, ReDirEntryFilter* filter) {\r
-       ReDirStatus_t* rc = rawNextFile(level);\r
-       while (rc != NULL) {\r
-               if (filter == NULL || filter->match(*rc)) {\r
-                       break;\r
-               }\r
-               rc = rawNextFile(level);\r
-       }\r
-       return rc;\r
-}\r
-\r
-/**\r
- * Initializes an entry in the directory entry stack.\r
- *\r
- * @param parent       the parent directory of the entry\r
- * @param node      the name of the directory belonging to the entry (without path)\r
- * @param level            the index of the entry in the stack.<br>\r
- *                  If < 0: m_levels and m_path will not be changed\r
- * @return          <code>true</code>: a new file is available<br>\r
- *                  <cude>false/code>: findFirstEntry() signals: no entry.\r
- */\r
-bool ReTraverser::initEntry(const ReByteBuffer& parent, const char* node,\r
-    int level) {\r
-       bool rc = false;\r
-       if (level < MAX_ENTRY_STACK_DEPTH) {\r
-               if (level >= 0)\r
-                       m_level = level;\r
-               if (m_dirs[m_level] == NULL)\r
-                       m_dirs[m_level] = new ReDirStatus_t(m_logger);\r
-               ReDirStatus_t* current = m_dirs[m_level];\r
-               current->m_passNo = 1;\r
-               if (level >= 0) {\r
-                       current->m_path.set(parent.str(), parent.length());\r
-                       if (!parent.endsWith(OS_SEPARATOR))\r
-                               current->m_path.append(OS_SEPARATOR);\r
-                       if (node != NULL)\r
-                               current->m_path.append(node).append(OS_SEPARATOR);\r
-               }\r
-               rc = current->findFirst();\r
-       }\r
-       return rc;\r
-}\r
-\r
-/**\r
- * Sets some properties from a filter.\r
- *\r
- * @param filter       the filter with the properties to set\r
- */\r
-void ReTraverser::setPropertiesFromFilter(ReDirEntryFilter* filter) {\r
-       m_minLevel = filter->m_minDepth;\r
-       m_maxLevel = filter->m_maxDepth;\r
-       setDirPattern(filter->m_pathPatterns);\r
-}\r
-\r
-/**\r
- * Returns the info of an entry the directory stack.\r
- *\r
- * @param offsetFromTop                0: return the top of stack<br>\r
- *                                                     1: returns the entry one below the top<br>\r
- *                                                     2: ...\r
- * @return                                     NULL: not available<br>\r
- *                                                     otherwise: the wanted entry\r
- */\r
-ReDirStatus_t* ReTraverser::topOfStack(int offsetFromTop) {\r
-       ReDirStatus_t* rc = NULL;\r
-       if (offsetFromTop >= 0 && m_level - 1 - offsetFromTop >= 0)\r
-               rc = m_dirs[m_level - 1 - offsetFromTop];\r
-       return rc;\r
-}\r
+/*
+ * ReTraverser.cpp
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
+ * Do what you want.
+ * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
+ * The latest sources: https://github.com/republib
+ */
+
+#include "base/rebase.hpp"
+#include "os/reos.hpp"
+#if defined __WIN32__
+#include "accctrl.h"
+#include "aclapi.h"
+#pragma comment(lib, "advapi32.lib")
+#endif
+
+enum RELOC_TRAVERSER {
+       LC_RIGHTS_AS_STRING_1 = LOC_FIRST_OF(LOC_TRAVERSER), // 50401
+       LC_RIGHTS_AS_STRING_2,  // 50402
+       LC_RIGHTS_AS_STRING_3,  // 50403
+       LC_GET_PRIVILEGE_1,             // 50404
+       LC_GET_PRIVILEGE_2,             // 50405
+       LC_GET_PRIVILEGE_3,             // 50406
+       LC_GET_FILE_OWNER_1,    // 50407
+       LC_GET_FILE_OWNER_2,    // 50408
+};
+
+int ReDirEntryFilter::m_serialId = buildSerialId(CLASSID_DIR_ENTRY_FILTER, 1);
+
+/**
+ * Constructor.
+ */
+ReDirStatus_t::ReDirStatus_t(ReLogger* logger) :
+           m_path(),
+           m_fullName(),
+           m_passNo(0),
+           m_logger(logger),
+#ifdef __linux__
+           m_handle(NULL),
+           m_data(NULL)
+//m_status;
+#elif defined WIN32
+m_handle(INVALID_HANDLE_VALUE),
+//m_data;
+m_getPrivilege(true)
+#endif
+{
+#ifdef __linux__
+       memset(&m_status, 0, sizeof m_status);
+#elif defined WIN32
+       memset(&m_data, 0, sizeof m_data);
+#endif
+}
+
+/**
+ * Returns the last access time.
+ *
+ * @return     the last access time
+ */
+const ReFileTime_t* ReDirStatus_t::accessed() {
+#ifdef __linux__
+       return &(getStatus()->st_atim);
+#elif defined __WIN32__
+       return &m_data.ftLastAccessTime;
+#endif
+}
+
+/**
+ * Returns the filesize.
+ *
+ * @return     the filesize
+ */
+ReFileSize_t ReDirStatus_t::fileSize() {
+#ifdef __linux__
+       return getStatus()->st_size;
+#elif defined __WIN32__
+       return ((int64_t) m_data.nFileSizeHigh << 32) + m_data.nFileSizeLow;
+#endif
+}
+
+/**
+ * Returns the file time as a string.
+ *
+ * @param buffer    OUT: the file time
+ * @return          <code>buffer.str()</code> (for chaining)
+ */
+const char* ReDirStatus_t::filetimeAsString(ReByteBuffer& buffer) {
+       return filetimeToString(modified(), buffer);
+}
+
+/**
+ * Converts a filetime to a string.
+ *
+ * @param time         the filetime to convert
+ * @param buffer       OUT: the buffer for the string
+ * @return                     <code>buffer.str()</code>, e.g. "2014.01.07 02:59:43"
+ */
+const char* ReDirStatus_t::filetimeToString(const ReFileTime_t* time,
+    ReByteBuffer& buffer) {
+       time_t time1 = filetimeToTime(time);
+       struct tm* time2 = localtime(&time1);
+       buffer.setLength(4 + 2 * 2 + 2 * 2 + 1 + 3 * 2 + 2 * 1);
+       strftime(buffer.buffer(), buffer.length(), "%Y.%m.%d %H:%M:%S", time2);
+       return buffer.str();
+}
+
+/**
+ * Converts a filetime to a unix time (seconds since the Epoche).
+ *
+ * @param filetime             the filetime to convert
+ * @return                             the count of seconds since 1.1.1970
+ */
+time_t ReDirStatus_t::filetimeToTime(const ReFileTime_t* filetime) {
+#ifdef __linux__
+       return filetime->tv_sec;
+#elif defined __WIN32__
+       // 64-bit arithmetic:
+       LARGE_INTEGER date, adjust;
+       date.HighPart = filetime->dwHighDateTime;
+       date.LowPart = filetime->dwLowDateTime;
+       // 100-nanoseconds = milliseconds * 10000
+       adjust.QuadPart = 11644473600000 * 10000;
+       // removes the diff between 1970 and 1601
+       date.QuadPart -= adjust.QuadPart;
+       // converts back from 100-nanoseconds to seconds
+       time_t rc = (time_t) (date.QuadPart / 10000000);
+       return rc;
+#endif
+}
+
+/**
+ * Loads the info about the first file into the instance.
+ *
+ * @return     <code>true</code>: success
+ */
+bool ReDirStatus_t::findFirst() {
+       bool rc = false;
+#if defined __linux__
+       if (m_handle != NULL)
+               closedir(m_handle);
+       m_handle = opendir(m_path.str());
+       rc = m_handle != NULL && (m_data = readdir(m_handle)) != NULL;
+       m_status.st_ino = 0;
+#elif defined __WIN32__
+       if (m_handle != INVALID_HANDLE_VALUE)
+       FindClose(m_handle);
+       ReByteBuffer thePath(m_path);
+       thePath.append(m_path.lastChar() == '\\' ? "*" : "\\*");
+       m_handle = FindFirstFileA(thePath.str(), &m_data);
+       rc = m_handle != INVALID_HANDLE_VALUE;
+#endif
+       m_fullName.setLength(0);
+       return rc;
+}
+
+/**
+ * Loads the info about the next file into the instance.
+ *
+ * @return     <code>true</code>: success
+ */
+bool ReDirStatus_t::findNext() {
+#if defined __linux__
+       bool rc = m_handle != NULL && (m_data = readdir(m_handle)) != NULL;
+       m_status.st_ino = 0;
+#elif defined __WIN32__
+       bool rc = m_handle != INVALID_HANDLE_VALUE && FindNextFileA(m_handle, &m_data);
+#endif
+       m_fullName.setLength(0);
+       return rc;
+}
+
+/**
+ * Frees the resources of an instance.
+ */
+void ReDirStatus_t::freeEntry() {
+#if defined __linux__
+       if (m_handle != NULL) {
+               closedir(m_handle);
+               m_handle = NULL;
+       }
+#elif defined __WIN32__
+       if (m_handle != INVALID_HANDLE_VALUE) {
+               FindClose(m_handle);
+               m_handle = INVALID_HANDLE_VALUE;
+       }
+#endif
+       m_path.setLength(0);
+       m_fullName.setLength(0);
+}
+
+/**
+ * Returns the full filename (with path).
+ *
+ * @return     the filename with path
+ */
+const char* ReDirStatus_t::fullName() {
+       if (m_fullName.length() == 0)
+               m_fullName.set(m_path).append(node(), -1);
+       return m_fullName.str();
+}
+
+#if defined __WIN32__
+/** Gets the name of the file owner.
+ *
+ * @param handle               file handle (see <code>CreateFile()</code>)
+ * @param name         OUT: the owner: [domain\\]name
+ * @return                     <code>true</code>: success
+ */
+bool ReDirStatus_t::getFileOwner(HANDLE handle, const char* file,
+       ReByteBuffer& name, ReLogger* logger) {
+       bool rc = false;
+       PSID pSidOwner = NULL;
+       PSECURITY_DESCRIPTOR pSD = NULL;
+       if (GetSecurityInfo(handle, SE_FILE_OBJECT,
+                       OWNER_SECURITY_INFORMATION, &pSidOwner, NULL, NULL, NULL, &pSD) != ERROR_SUCCESS) {
+               if (logger != NULL)
+               logger->sayF(LOG_ERROR | CAT_FILE, LC_GET_FILE_OWNER_1,
+                       "GetSecurityInfo($1): $2").arg(file).arg((int) GetLastError()).end();
+       } else {
+               char accountName[128];
+               char domainName[128];
+               DWORD dwAcctName = sizeof accountName;
+               DWORD dwDomainName = sizeof domainName;
+               SID_NAME_USE eUse = SidTypeUnknown;
+               if (! LookupAccountSid(NULL, pSidOwner, accountName, &dwAcctName, domainName,
+                               &dwDomainName, &eUse)) {
+                       if (logger != NULL)
+                       logger->sayF(LOG_ERROR | CAT_SECURITY, LC_GET_FILE_OWNER_2,
+                               "LookupAccountSid(): $1").arg((int) GetLastError()).end();
+               } else {
+                       if (dwDomainName > 0)
+                       name.append(domainName).appendChar('\\');
+                       name.append(accountName);
+                       rc = true;
+               }
+       }
+       return rc;
+}
+#endif /* __WIN32__ */
+
+#if defined __WIN32__
+/** Tries to get a privilege.
+ *
+ * @param privilege    the name of the privilege, e.g. "SeBackup"
+ * @param logger               logger for error logging
+ */
+bool ReDirStatus_t::getPrivilege(const char* privilege, ReLogger* logger) {
+       bool rc = false;
+       LUID luidPrivilege;
+       HANDLE hAccessToken;
+       if (! OpenProcessToken (GetCurrentProcess(),
+                       TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hAccessToken)) {
+               if (logger != NULL)
+               logger->sayF(LOG_ERROR | CAT_FILE, LC_GET_PRIVILEGE_1,
+                       "OpenProcessToken(): $1").arg((int) GetLastError()).end();
+       } else if (! LookupPrivilegeValue (NULL, SE_BACKUP_NAME, &luidPrivilege)) {
+               if (logger != NULL)
+               logger->sayF(LOG_ERROR | CAT_FILE, LC_GET_PRIVILEGE_2,
+                       "LookupPrivilegeValue(): $1").arg((int) GetLastError()).end();
+       } else {
+               TOKEN_PRIVILEGES tpPrivileges;
+               tpPrivileges.PrivilegeCount = 1;
+               tpPrivileges.Privileges[0].Luid = luidPrivilege;
+               tpPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+               if (AdjustTokenPrivileges (hAccessToken, FALSE, &tpPrivileges,
+                               0, NULL, NULL) == 0)
+               rc = true;
+               else {
+                       int error = GetLastError();
+                       if (error != 1300 && logger != NULL)
+                       logger->sayF(LOG_ERROR | CAT_FILE, LC_GET_PRIVILEGE_3,
+                               "AdjustTokenPrivileges(): $1").arg((int) GetLastError()).end();
+               }
+       }
+       return rc;
+}
+#endif /* __WIN32__ */
+
+/**
+ * Tests whether the instance is a directory.
+ *
+ * @return     <code>true</code>: instance contains the data of a directory
+ */
+bool ReDirStatus_t::isDirectory() {
+#ifdef __linux__
+       return m_data->d_type == DT_DIR
+           || (m_data->d_type == DT_UNKNOWN && S_ISDIR(getStatus()->st_mode));
+#elif defined __WIN32__
+       return 0 != (m_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
+#endif
+}
+
+/**
+ * Tests whether the instance contains data about "." or "..".
+ *
+ * @return     <code>true</code>: an ignorable entry has been found
+ */
+bool ReDirStatus_t::isDotDir() const {
+#ifdef __linux__
+       bool rc = m_data == NULL
+           || (m_data->d_name[0] == '.'
+               && (m_data->d_name[1] == '\0'
+                   || (m_data->d_name[1] == '.' && m_data->d_name[2] == '\0')));
+#elif defined __WIN32__
+       bool rc = m_data.cFileName[0] == '.' && (m_data.cFileName[1] == '\0'
+               || (m_data.cFileName[1] == '.' && m_data.cFileName[2] == '\0'));
+#endif
+       return rc;
+}
+
+/**
+ * Tests whether the instance is a symbolic link.
+ *
+ * Unter windows it tests whether the the instance is a reparse point.
+ *
+ * @return     <code>true</code>: instance contains the data of a link
+ */
+bool ReDirStatus_t::isLink() {
+       bool rc;
+#ifdef __linux__
+       rc = m_data->d_type == DT_LNK
+           || (m_data->d_type == DT_UNKNOWN && S_ISLNK(getStatus()->st_mode));
+#elif defined __WIN32__
+       rc = 0 != (m_data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT);
+#endif
+       return rc;
+}
+/**
+ * Tests whether the instance is a "normal" file.
+ *
+ * @return     <code>true</code>: instance contains the data of a not special file
+ */
+bool ReDirStatus_t::isRegular() {
+#ifdef __linux__
+       return m_data->d_type == DT_REG
+           || (m_data->d_type == DT_UNKNOWN && S_ISREG(getStatus()->st_mode));
+#elif defined __WIN32__
+       return 0 == (m_data.dwFileAttributes & (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE));
+#endif
+}
+/**
+ * Returns the modification time.
+ *
+ * @return     the modification time
+ */
+const ReFileTime_t* ReDirStatus_t::modified() {
+#ifdef __linux__
+       return &(getStatus()->st_mtim);
+#elif defined __WIN32__
+       return &m_data.ftLastWriteTime;
+#endif
+}
+
+/**
+ * Returns the name of the current file (without path).
+ *
+ * @return     the name of the current file.
+ */
+const char* ReDirStatus_t::node() const {
+#ifdef __linux__
+       return m_data->d_name;
+#elif defined __WIN32__
+       return m_data.cFileName;
+#endif
+}
+
+inline void addRight(int mode, ReByteBuffer& buffer) {
+       char right;
+       switch (mode & 7) {
+       case 1:
+               right = 'x';
+               break;
+       case 2:
+               right = 'w';
+               break;
+       case 3:
+               right = 'X';
+               break;
+       case 4:
+               right = 'r';
+               break;
+       case 5:
+               right = 'R';
+               break;
+       case 6:
+               right = 'W';
+               break;
+       case 7:
+               right = 'A';
+               break;
+       default:
+               right = '-';
+               break;
+       }
+       buffer.appendChar(right);
+}
+inline void addId(const char* id, int maxLength, ReByteBuffer& buffer) {
+       int length = strlen(id);
+       if (length == maxLength)
+               buffer.append(id, length);
+       else if (length < maxLength)
+               buffer.append(id, length).appendChar(' ', maxLength - length);
+       else {
+               buffer.append(id, 2);
+               buffer.append(id + length - maxLength - 2, maxLength - 2);
+       }
+}
+/**
+ * Returns the file rights as a string.
+ *
+ * @param buffer               OUT: the file rights
+ * @param numerical            <code>true</code>: the owner/group should be numerical (UID/GID)
+ * @param ownerWidth   the width for group/owner
+ * @return                             <code>buffer.str()</code> (for chaining)
+ */
+const char* ReDirStatus_t::rightsAsString(ReByteBuffer& buffer, bool numerical,
+    int ownerWidth) {
+       buffer.setLength(0);
+#if defined __linux__
+       if (numerical) {
+               buffer.appendInt(getStatus()->st_mode & ALLPERMS, "%04o");
+               buffer.appendInt(getStatus()->st_uid, " %4d");
+               buffer.appendInt(getStatus()->st_gid, " %4d");
+       } else {
+               int mode = getStatus()->st_mode & ALLPERMS;
+               addRight(mode >> 6, buffer);
+               addRight(mode >> 3, buffer);
+               addRight(mode, buffer);
+               buffer.appendChar(' ');
+               struct passwd* passwd = getpwuid(getStatus()->st_uid);
+               if (passwd == NULL)
+                       buffer.appendInt(getStatus()->st_uid, "%4d");
+               else
+                       addId(passwd->pw_name, 5, buffer);
+               buffer.appendChar(' ');
+               struct group* group = getgrgid(getStatus()->st_gid);
+               if (group == NULL)
+                       buffer.appendInt(getStatus()->st_gid, "%4d");
+               else
+                       addId(group->gr_name, 5, buffer);
+               buffer.appendChar(' ');
+       }
+#elif defined __WIN32__
+       const char* name = fullName();
+       HANDLE handle = INVALID_HANDLE_VALUE;
+       if (! isDirectory()) {
+               if ( (handle = CreateFile(name, GENERIC_READ, FILE_SHARE_READ, NULL,
+                                       OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
+               m_logger->sayF(LOG_ERROR | CAT_FILE, LC_RIGHTS_AS_STRING_1,
+                       "CreateFile($1): $2").arg(name).arg((int) GetLastError()).end();
+       } else if (m_getPrivilege) {
+               // we try only one time:
+               m_getPrivilege = false;
+               if (getPrivilege(SE_BACKUP_NAME, m_logger)) {
+                       if ( (handle = CreateFile(name, 0, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
+                                               NULL)) != INVALID_HANDLE_VALUE)
+                       m_logger->sayF(LOG_ERROR | CAT_FILE, LC_RIGHTS_AS_STRING_2,
+                               "CreateFile($1): $2").arg(name).arg((int) GetLastError()).end();
+               }
+
+       }
+       ReByteBuffer owner;
+       if (handle != INVALID_HANDLE_VALUE)
+       getFileOwner(handle, name, owner, m_logger);
+       CloseHandle(handle);
+       buffer.appendFix(owner.str(), owner.length(), ownerWidth, ownerWidth);
+#endif
+       return buffer.str();
+}
+
+/**
+ * Converts the unix time (time_t) to the file time.
+ *
+ * @param time                 the unix time (secondes since 1.1.1970)
+ * @param filetime     OUT: the OS specific filetime
+ */
+void ReDirStatus_t::timeToFiletime(time_t time, ReFileTime_t& filetime) {
+#ifdef __linux__
+       filetime.tv_sec = time;
+       filetime.tv_nsec = 0;
+#elif defined __WIN32__
+       LONGLONG ll = Int32x32To64(time, 10000000) + 116444736000000000;
+       filetime.dwLowDateTime = (DWORD)ll;
+       filetime.dwHighDateTime = ll >> 32;
+#endif
+}
+/**
+ * Returns the type of the entry.
+ * return       the file type, e.g. TF_REGULAR
+ */
+ReDirStatus_t::Type_t ReDirStatus_t::type() {
+       Type_t rc = TF_UNDEF;
+#if defined __linux__
+       int flags = getStatus()->st_mode;
+       if (S_ISDIR(flags))
+               rc = TF_SUBDIR;
+       else if (flags == 0 || S_ISREG(flags))
+               rc = TF_REGULAR;
+       else if (S_ISLNK(flags))
+               rc = TF_LINK;
+       else if (S_ISCHR(flags))
+               rc = TF_CHAR;
+       else if (S_ISBLK(flags))
+               rc = TF_BLOCK;
+       else if (S_ISFIFO(flags))
+               rc = TF_PIPE;
+       else if (S_ISSOCK(flags))
+               rc = TF_SOCKET;
+       else
+               rc = TF_OTHER;
+#elif defined __WIN32__
+       int flags = (m_data.dwFileAttributes & ~(FILE_ATTRIBUTE_READONLY
+                       | FILE_ATTRIBUTE_HIDDEN
+                       | FILE_ATTRIBUTE_SYSTEM
+                       | FILE_ATTRIBUTE_ARCHIVE
+                       | FILE_ATTRIBUTE_NORMAL
+                       | FILE_ATTRIBUTE_TEMPORARY
+                       | FILE_ATTRIBUTE_SPARSE_FILE
+                       | FILE_ATTRIBUTE_COMPRESSED
+                       | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED
+                       | FILE_ATTRIBUTE_ENCRYPTED
+                       | FILE_ATTRIBUTE_HIDDEN));
+
+       if (0 == flags)
+       rc = TF_REGULAR;
+       else if (0 != (m_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
+               rc = (0 != (m_data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
+               ? TF_LINK_DIR : TF_SUBDIR;
+       } else if (0 != (m_data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
+       rc = TF_LINK;
+       else
+       rc = TF_OTHER;
+#endif
+       return rc;
+}
+
+/**
+ * Returns the filetype as a single character.
+ *
+ * @return     the filetype, e.g. 'd' for a directory
+ */
+char ReDirStatus_t::typeAsChar() {
+       char rc = ' ';
+       switch (type()) {
+       case TF_REGULAR:
+               rc = ' ';
+               break;
+       case TF_LINK:
+               rc = 'l';
+               break;
+       case TF_SUBDIR:
+               rc = 'd';
+               break;
+       case TF_CHAR:
+               rc = 'c';
+               break;
+       case TF_BLOCK:
+               rc = 'b';
+               break;
+       case TF_PIPE:
+               rc = 'p';
+               break;
+       case TF_SOCKET:
+               rc = 's';
+               break;
+       default:
+               rc = 'o';
+               break;
+       }
+       return rc;
+}
+/**
+ * Constructor.
+ */
+ReDirEntryFilter::ReDirEntryFilter() :
+           ReSerializable(m_serialId),
+           m_types(ReDirStatus_t::TC_ALL),
+           m_nodePatterns(NULL),
+           m_pathPatterns(NULL),
+           m_minSize(0),
+           m_maxSize(-1),
+           //m_minAge(0),
+           //m_maxAge(0),
+           m_minDepth(0),
+           m_maxDepth(512),
+           m_allDirectories(false) {
+       setFiletimeUndef(m_minAge);
+       setFiletimeUndef(m_maxAge);
+}
+
+/**
+ * Destructor.
+ */
+ReDirEntryFilter::~ReDirEntryFilter() {
+}
+
+/**
+ * Sets the members of the instance from a byte sequence.
+ *
+ * @param sequence     IN/OUT: the serialized byte sequence
+ * @param length       INT/OUT the length of <code>sequence</code>
+ */
+void ReDirEntryFilter::deserialize(const uint8_t*& sequence, size_t& length) {
+       int id;
+       unpackInt24(sequence, length, id);
+       if (id != m_serialId)
+               throw ReSerializeFormatException("wrong serialId", this);
+       ReByteBuffer buffer;
+       unpackString64k(sequence, length, buffer);
+       bool ignoreCase;
+       unpackBool(sequence, length, ignoreCase);
+       m_nodePatterns->set(buffer.str(), ignoreCase);
+       unpackString64k(sequence, length, buffer);
+       unpackBool(sequence, length, ignoreCase);
+       unpackString64k(sequence, length, buffer);
+       m_pathPatterns->set(buffer.str(), ignoreCase);
+       unpackInt64(sequence, length, m_minSize);
+       unpackInt64(sequence, length, m_maxSize);
+       int64_t value;
+       unpackInt64(sequence, length, value);
+       unpackBool(sequence, length, m_allDirectories);
+}
+/**
+ * Packs the members into a byte sequence.
+ *
+ * @param sequence     IN/OUT: the place for the byte sequence
+ */
+ReByteBuffer& ReDirEntryFilter::serialize(ReByteBuffer& sequence) {
+       sequence.appendBits24(m_serialId);
+       packString64k(sequence, m_nodePatterns->patternString());
+       packBool(sequence, m_nodePatterns->ignoreCase());
+       packString64k(sequence, m_pathPatterns->patternString());
+       packBool(sequence, m_pathPatterns->ignoreCase());
+       sequence.appendBits64(m_minSize);
+       sequence.appendBits64(m_maxSize);
+       uint64_t value;
+#if defined __linux__
+       value = (m_minAge.tv_sec << 32) + m_minAge.tv_nsec;
+       sequence.appendBits64(int64_t(value));
+       value = (m_minAge.tv_sec << 32) + m_minAge.tv_nsec;
+#elif defined __WIN32__
+#error "missing impl"
+#endif
+       sequence.appendBits64(int64_t(value));
+       packBool(sequence, m_allDirectories);
+}
+
+/**
+ * Tests whether an entry matches the conditions of the filter.
+ *
+ * @param entry                entry to test
+ * @return                     <true>: the entry matches the conditions of the filter<br>
+ *                                     <false>: otherwise
+ */
+bool ReDirEntryFilter::match(ReDirStatus_t& entry) {
+       bool rc = false;
+       do {
+               if (m_allDirectories && entry.isDirectory()) {
+                       rc = true;
+                       break;
+               }
+               if (0 == (entry.type() & m_types))
+                       break;
+               int64_t size = entry.fileSize();
+               if (m_minSize > 0 && size < m_minSize)
+                       break;
+               if (m_maxSize >= 0 && size > m_maxSize)
+                       break;
+               if (!filetimeIsUndefined(m_minAge) && *entry.modified() > m_minAge)
+                       break;
+               if (!filetimeIsUndefined(m_maxAge) && m_maxAge > *entry.modified())
+                       break;
+               const char* node = entry.node();
+               if (m_nodePatterns != NULL && !m_nodePatterns->match(node))
+                       break;
+               rc = true;
+       } while (false);
+       return rc;
+}
+;
+
+#ifdef __linux__
+/**
+ * Returns the status of the current file (lazy loading).
+ *
+ * @return     the status of the current file
+ */
+struct stat* ReDirStatus_t::getStatus() {
+       if (m_status.st_ino == 0) {
+               if (stat(fullName(), &m_status) != 0)
+                       m_status.st_ino = 0;
+       }
+       return &m_status;
+}
+#endif
+
+/**
+ * Constructor.
+ */
+ReDirTreeStatistic::ReDirTreeStatistic() :
+           m_directories(0),
+           m_files(0),
+           m_sizes(0ll) {
+}
+/**
+ * Builds a string describing the data.
+ *
+ * @param buffer               IN/OUT: a buffer for the result
+ * @param append               <code>true</code>: the string will be appended to the buffer<br>
+ *                                             <code>false</code>: the buffer will be cleared at the beginning
+ * @param formatFiles  the <code>sprintf</code> format for the file count, e.g. "%8d"
+ * @param formatSized  the <code>sprintf</code> format for the MByte format, e.g. "%12.6f"
+ * @param formatFiles  the <code>sprintf</code> format for the directory count, e.g. "%6d"
+ * @return                             a human readable string
+ */
+const char* ReDirTreeStatistic::statisticAsString(ReByteBuffer& buffer,
+    bool append, const char* formatFiles, const char* formatSizes,
+    const char* formatDirs) {
+       if (!append)
+               buffer.setLength(0);
+       buffer.appendInt(m_files, formatFiles);
+       buffer.append(i18n("file(s)")).appendChar(' ');
+       buffer.append(m_sizes / 1000.0 / 1000, formatSizes);
+       buffer.append(" ", 1).append(i18n("MByte")).appendChar(' ');
+       buffer.appendInt(m_directories, formatDirs);
+       buffer.append(i18n("dirs(s)"));
+       return buffer.str();
+}
+
+/**
+ * Constructor.
+ *
+ * @param triggerCount efficiency: only every N calls a time check takes place
+ * @param interval             the minimum number of seconds between two traces
+ */
+ReTraceUnit::ReTraceUnit(int triggerCount, int interval) :
+           m_count(0),
+           m_triggerCount(triggerCount),
+           m_lastTrace(0),
+           m_interval(interval),
+           m_startTime(time(NULL)) {
+       m_lastTrace = m_startTime;
+}
+/**
+ * Destructor.
+ */
+ReTraceUnit::~ReTraceUnit() {
+}
+
+/**
+ * Prints a message.
+ *
+ * Often overwritten by a subclass.
+ *
+ * @param message      message for the trace
+ * @return                     <code>true</code> (for chaining)
+ */
+bool ReTraceUnit::trace(const char* message) {
+       printf("%s\n", message);
+       return true;
+}
+
+/**
+ * Constructor.
+ *
+ * @param base         the base directory. The traversal starts at this point
+ */
+ReTraverser::ReTraverser(const char* base, ReTraceUnit* tracer,
+    ReLogger* logger) :
+           ReDirTreeStatistic(),
+           m_minLevel(0),
+           m_maxLevel(512),
+           m_level(-1),
+           m_base(base),
+           // m_dirs
+           m_passNoForDirSearch(2),
+           m_dirPatterns(NULL),
+           m_tracer(tracer),
+           m_logger(logger) {
+       memset(m_dirs, 0, sizeof m_dirs);
+       m_dirs[0] = new ReDirStatus_t(m_logger);
+       // remove a preceeding "./". This simplifies the pattern expressions:
+       if (m_base.startsWith(
+           ReByteBuffer(".").appendChar(OS_SEPARATOR_CHAR).str())) {
+               m_base.remove(0, 2);
+       }
+}
+
+/**
+ * Destructor.
+ */
+ReTraverser::~ReTraverser() {
+       destroy();
+}
+
+/**
+ * Initializes the instance to process a new base.
+ *
+ * @param base the base directory to search
+ */
+void ReTraverser::changeBase(const char* base) {
+       destroy();
+       m_base.setLength(0).append(base);
+       memset(m_dirs, 0, sizeof m_dirs);
+       m_dirs[0] = new ReDirStatus_t(m_logger);
+       // remove a preceeding "./". This simplifies the pattern expressions:
+       if (m_base.startsWith(
+           ReByteBuffer(".").appendChar(OS_SEPARATOR_CHAR).str())) {
+               m_base.remove(0, 2);
+       }
+}
+
+/**
+ * Releases the resources.
+ */
+void ReTraverser::destroy() {
+       for (size_t ix = 0; ix < sizeof m_dirs / sizeof m_dirs[0]; ix++) {
+               if (m_dirs[ix] != NULL) {
+                       m_dirs[ix]->freeEntry();
+                       delete m_dirs[ix];
+                       m_dirs[ix] = NULL;
+               }
+       }
+}
+/**
+ * Returns the info about the next file in the directory tree traversal.
+ *
+ * @param level        OUT: the level relative to the base.<br>
+ *                                     0 means the file is inside the base.<br>
+ *                                     Not defined if the result is NULL
+ * @return NULL        no more files<br>
+ *                                     otherwise: the stack entry with the next file in the
+ *                                     directory tree. May be a directory too
+ */
+ReDirStatus_t* ReTraverser::rawNextFile(int& level) {
+       ReDirStatus_t* rc = NULL;
+       bool alreadyRead = false;
+       bool again;
+       do {
+               again = false;
+               if (m_level < 0) {
+                       // Not yet initialized?
+                       if (m_dirs[0]->m_passNo == 2)
+                               rc = NULL;
+                       else {
+                               // first call:
+                               if (initEntry(m_base.str(), NULL, 0)) {
+                                       m_directories++;
+                                       if (1 != m_passNoForDirSearch)
+                                               rc = m_dirs[0];
+                                       else
+                                               again = alreadyRead = true;
+                               }
+                       }
+               } else {
+                       ReDirStatus_t* current = m_dirs[m_level];
+                       if (alreadyRead || current->findNext()) {
+                               alreadyRead = false;
+                               // a file or directory found:
+                               if (m_tracer != NULL && m_tracer->isCountTriggered()
+                                   && m_tracer->isTimeTriggered())
+                                       m_tracer->trace(current->fullName());
+                               if (current->m_passNo != m_passNoForDirSearch) {
+                                       // we search for any file:
+                                       rc = m_dirs[m_level];
+                               } else {
+                                       // we are interested only in true subdirectories:
+                                       again = true;
+                                       if (m_level < m_maxLevel && current->isDirectory()
+                                           && !current->isDotDir() && !current->isLink()
+                                           && (m_dirPatterns == NULL
+                                               || isAllowedDir(current->node()))) {
+                                               // open a new level
+                                               alreadyRead = initEntry(current->m_path,
+                                                   current->node(), m_level + 1);
+                                               m_directories++;
+                                       }
+                               }
+                       } else {
+                               // the current subdir does not have more files:
+                               if (current->m_passNo == 1) {
+                                       // we start the second pass:
+                                       alreadyRead = initEntry(current->m_path, NULL, -1);
+                                       current->m_passNo = 2;
+                                       again = true;
+                               } else {
+                                       // this subdirectory is complete. We continue in the parent directory:
+                                       current->freeEntry();
+                                       if (--m_level >= 0) {
+                                               again = true;
+                                       }
+                               }
+                       }
+               }
+               if (rc != NULL && rc->isDotDir())
+                       again = true;
+       } while (again);
+       if (rc != NULL && !rc->isDirectory()) {
+               m_files++;
+               if (m_sizes >= 0)
+                       m_sizes += rc->fileSize();
+       }
+       level = m_level;
+       return rc;
+}
+/**
+ * Returns the info about the next file matching the filter options.
+ *
+ * @param level        OUT: the level relative to the base.<br>
+ *                                     0 means the file is inside the base.<br>
+ *                                     Not defined if the result is NULL
+ * @param filter       NULL: every file matches<br>
+ *                                     otherwise: each found file must match this filter conditions
+ * @return NULL        no more files<br>
+ *                                     otherwise: the info about the next file in the
+ *                                     directory tree
+ */
+ReDirStatus_t* ReTraverser::nextFile(int& level, ReDirEntryFilter* filter) {
+       ReDirStatus_t* rc = rawNextFile(level);
+       while (rc != NULL) {
+               if (filter == NULL || filter->match(*rc)) {
+                       break;
+               }
+               rc = rawNextFile(level);
+       }
+       return rc;
+}
+
+/**
+ * Initializes an entry in the directory entry stack.
+ *
+ * @param parent       the parent directory of the entry
+ * @param node      the name of the directory belonging to the entry (without path)
+ * @param level            the index of the entry in the stack.<br>
+ *                  If < 0: m_levels and m_path will not be changed
+ * @return          <code>true</code>: a new file is available<br>
+ *                  <cude>false/code>: findFirstEntry() signals: no entry.
+ */
+bool ReTraverser::initEntry(const ReByteBuffer& parent, const char* node,
+    int level) {
+       bool rc = false;
+       if (level < MAX_ENTRY_STACK_DEPTH) {
+               if (level >= 0)
+                       m_level = level;
+               if (m_dirs[m_level] == NULL)
+                       m_dirs[m_level] = new ReDirStatus_t(m_logger);
+               ReDirStatus_t* current = m_dirs[m_level];
+               current->m_passNo = 1;
+               if (level >= 0) {
+                       current->m_path.set(parent.str(), parent.length());
+                       if (!parent.endsWith(OS_SEPARATOR))
+                               current->m_path.append(OS_SEPARATOR);
+                       if (node != NULL)
+                               current->m_path.append(node).append(OS_SEPARATOR);
+               }
+               rc = current->findFirst();
+       }
+       return rc;
+}
+
+/**
+ * Sets some properties from a filter.
+ *
+ * @param filter       the filter with the properties to set
+ */
+void ReTraverser::setPropertiesFromFilter(ReDirEntryFilter* filter) {
+       m_minLevel = filter->m_minDepth;
+       m_maxLevel = filter->m_maxDepth;
+       setDirPattern(filter->m_pathPatterns);
+}
+
+/**
+ * Returns the info of an entry the directory stack.
+ *
+ * @param offsetFromTop                0: return the top of stack<br>
+ *                                                     1: returns the entry one below the top<br>
+ *                                                     2: ...
+ * @return                                     NULL: not available<br>
+ *                                                     otherwise: the wanted entry
+ */
+ReDirStatus_t* ReTraverser::topOfStack(int offsetFromTop) {
+       ReDirStatus_t* rc = NULL;
+       if (offsetFromTop >= 0 && m_level - 1 - offsetFromTop >= 0)
+               rc = m_dirs[m_level - 1 - offsetFromTop];
+       return rc;
+}
index 7b42461c18265c95e025b32da0b0b06329cb03ea..241b913488aed2b5b517c6c83e41c77fb4ca743e 100644 (file)
@@ -1,9 +1,11 @@
 /*
- * RpTraverser.hpp
- *
- * License: Public domain
+ * ReTraverser.hpp
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
@@ -106,12 +108,12 @@ public:
        bool m_getPrivilege;
 #endif
 };
-class ReDirEntryFilter : public ReSerializable {
+class ReDirEntryFilter: public ReSerializable {
 public:
        ReDirEntryFilter();
        ~ReDirEntryFilter();
 public:
-       virtual void deserialize(uint8_t*& sequence, size_t& length);
+       virtual void deserialize(const uint8_t*& sequence, size_t& length);
        bool match(ReDirStatus_t& entry);
        virtual ReByteBuffer& serialize(ReByteBuffer& sequence);
 public:
index f6b57d36581f4ba7a068472100b25c3942a8868b..342a9f143e737d9360f9cfbc6562733838217626 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * reos.hpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
index 2a68b872153ef17be881a3b84dc9eaad465f9978..55f784e98baf5f51262aa910ee3fbf7ecba956b9 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * ReMatcher.cpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
index 138139610060ec5789e91a2f3631b00590c02823..7bf0467e2930264a520a2edc761c670f9c48cace 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * ReMatcher.hpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */
 
@@ -74,6 +76,12 @@ public:
 public:
        virtual bool compile(const char* pattern);
        void dump(FILE* fp, const char* prefix) const;
+       /** Returns whether the search is case insensitive.
+        * @return      <code>true</code>: the search is case insensitive
+        */
+       inline bool ignoreCase() const {
+               return m_ignoreCase;
+       }
        virtual bool match(const ReByteBuffer& toTest, ReHit* hit = NULL) const;
        virtual bool search(const ReByteBuffer& toTest, ReHit* hit = NULL,
            bool greedy = false) const;
@@ -106,6 +114,13 @@ public:
        }
        void destroy();
        void dump(FILE* fp, const char* prefix) const;
+       /** Tests whether the search is case insensitive
+        * @return              <code>true</code>: the search is case insensitive
+        */
+       bool ignoreCase() const {
+               bool rc = m_count == 0 ? false : m_patterns[0]->ignoreCase();
+               return rc;
+       }
        bool match(const ReByteBuffer& name);
        /** @brief Tests whether a string matches the patterns.
         * @param name  the string to Test
index 59f1988b41953686d4df780d65f4db826762a30f..46a50697a7b90c9e827c82e470ec1f59bcd90d5a 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * restring.hpp
- *
- * License: Public domain
+ * 
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
  * Do what you want.
  * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
  * The latest sources: https://github.com/republib
  */