From: hama Date: Sat, 4 Apr 2015 13:19:11 +0000 (+0200) Subject: Reformatted, common license text (header) X-Git-Url: https://gitweb.hamatoma.de/?a=commitdiff_plain;h=b6195b27174f8e9b14d670afef2a2ed43243caf8;p=crepublib Reformatted, common license text (header) --- diff --git a/base/ReAppenders.cpp b/base/ReAppenders.cpp index 3faac42..90fed67 100644 --- a/base/ReAppenders.cpp +++ b/base/ReAppenders.cpp @@ -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" diff --git a/base/ReAppenders.hpp b/base/ReAppenders.hpp index 5e58cfe..fcab1f7 100644 --- a/base/ReAppenders.hpp +++ b/base/ReAppenders.hpp @@ -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_ diff --git a/base/ReBaseUtils.cpp b/base/ReBaseUtils.cpp index 772fac3..64e9861 100644 --- a/base/ReBaseUtils.cpp +++ b/base/ReBaseUtils.cpp @@ -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 */ diff --git a/base/ReBaseUtils.hpp b/base/ReBaseUtils.hpp index f028701..cde959e 100644 --- a/base/ReBaseUtils.hpp +++ b/base/ReBaseUtils.hpp @@ -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 */ diff --git a/base/ReByteBuffer.cpp b/base/ReByteBuffer.cpp index 0e68028..01a7f55 100644 --- a/base/ReByteBuffer.cpp +++ b/base/ReByteBuffer.cpp @@ -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 */ diff --git a/base/ReByteBuffer.hpp b/base/ReByteBuffer.hpp index 0a27f85..eba2d42 100644 --- a/base/ReByteBuffer.hpp +++ b/base/ReByteBuffer.hpp @@ -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 */ diff --git a/base/ReCString.cpp b/base/ReCString.cpp index 63725f8..8bb4b5b 100644 --- a/base/ReCString.cpp +++ b/base/ReCString.cpp @@ -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. diff --git a/base/ReCString.hpp b/base/ReCString.hpp index 65ae90d..d413c86 100644 --- a/base/ReCString.hpp +++ b/base/ReCString.hpp @@ -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 */ diff --git a/base/ReClassId.cpp b/base/ReClassId.cpp index 1838046..32f4b50 100644 --- a/base/ReClassId.cpp +++ b/base/ReClassId.cpp @@ -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; diff --git a/base/ReClassId.hpp b/base/ReClassId.hpp index feef67a..3777581 100644 --- a/base/ReClassId.hpp +++ b/base/ReClassId.hpp @@ -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_ */ diff --git a/base/ReConfigFile.cpp b/base/ReConfigFile.cpp index 38fdb23..4400c22 100644 --- a/base/ReConfigFile.cpp +++ b/base/ReConfigFile.cpp @@ -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 }; diff --git a/base/ReConfigFile.hpp b/base/ReConfigFile.hpp index 053aacf..6f075e2 100644 --- a/base/ReConfigFile.hpp +++ b/base/ReConfigFile.hpp @@ -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 */ diff --git a/base/ReDirectory.cpp b/base/ReDirectory.cpp index ab27440..4288b6e 100644 --- a/base/ReDirectory.cpp +++ b/base/ReDirectory.cpp @@ -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 */ diff --git a/base/ReDirectory.hpp b/base/ReDirectory.hpp index 36428a4..265167e 100644 --- a/base/ReDirectory.hpp +++ b/base/ReDirectory.hpp @@ -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 */ diff --git a/base/ReException.cpp b/base/ReException.cpp index dc2a216..3d95941 100644 --- a/base/ReException.cpp +++ b/base/ReException.cpp @@ -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()) { +} diff --git a/base/ReException.hpp b/base/ReException.hpp index 77999e5..4f9122b 100644 --- a/base/ReException.hpp +++ b/base/ReException.hpp @@ -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_ */ diff --git a/base/ReHashList.cpp b/base/ReHashList.cpp index 95be346..5afb527 100644 --- a/base/ReHashList.cpp +++ b/base/ReHashList.cpp @@ -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 }; /** diff --git a/base/ReHashList.hpp b/base/ReHashList.hpp index da7f95c..6daf572 100644 --- a/base/ReHashList.hpp +++ b/base/ReHashList.hpp @@ -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 */ diff --git a/base/ReI18N.cpp b/base/ReI18N.cpp index 861c21e..6736ebc 100644 --- a/base/ReI18N.cpp +++ b/base/ReI18N.cpp @@ -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 */ diff --git a/base/ReI18N.hpp b/base/ReI18N.hpp index 85dcbed..cc5d788 100644 --- a/base/ReI18N.hpp +++ b/base/ReI18N.hpp @@ -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_ diff --git a/base/ReLogger.cpp b/base/ReLogger.cpp index 2471b56..011ff44 100644 --- a/base/ReLogger.cpp +++ b/base/ReLogger.cpp @@ -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, diff --git a/base/ReLogger.hpp b/base/ReLogger.hpp index 037dba6..95bfb7b 100644 --- a/base/ReLogger.hpp +++ b/base/ReLogger.hpp @@ -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 */ diff --git a/base/ReMutex.cpp b/base/ReMutex.cpp index 8f8730c..6c0b135 100644 --- a/base/ReMutex.cpp +++ b/base/ReMutex.cpp @@ -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 */ diff --git a/base/ReMutex.hpp b/base/ReMutex.hpp index 04310b6..fe5f79f 100644 --- a/base/ReMutex.hpp +++ b/base/ReMutex.hpp @@ -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 */ diff --git a/base/ReProgramArgs.cpp b/base/ReProgramArgs.cpp index 2cc0779..c1d78cb 100644 --- a/base/ReProgramArgs.cpp +++ b/base/ReProgramArgs.cpp @@ -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 */ diff --git a/base/ReProgramArgs.hpp b/base/ReProgramArgs.hpp index c52408e..0252e45 100644 --- a/base/ReProgramArgs.hpp +++ b/base/ReProgramArgs.hpp @@ -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 */ diff --git a/base/ReSeqArray.cpp b/base/ReSeqArray.cpp index 1e721ef..36ba798 100644 --- a/base/ReSeqArray.cpp +++ b/base/ReSeqArray.cpp @@ -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 diff --git a/base/ReSeqArray.hpp b/base/ReSeqArray.hpp index a3b5cf2..6a9b8a9 100644 --- a/base/ReSeqArray.hpp +++ b/base/ReSeqArray.hpp @@ -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 */ diff --git a/base/ReSerializable.cpp b/base/ReSerializable.cpp index 71451bb..bdf2501 100644 --- a/base/ReSerializable.cpp +++ b/base/ReSerializable.cpp @@ -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 sequence + * @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 +} diff --git a/base/ReSerializable.hpp b/base/ReSerializable.hpp index 6ad8e33..a3ce56d 100644 --- a/base/ReSerializable.hpp +++ b/base/ReSerializable.hpp @@ -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
+ * OUT: the byte sequence behind the serialized members + * @param length IN: the length of sequence
+ * OUT: the length of sequence 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(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 value
+ * -1: strlen(value) + */ + 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 value
+ * -1: strlen(value) + */ + 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
- * OUT: the byte sequence behind the serialized members - * @param length IN: the length of sequence
- * OUT: the length of sequence 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(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 buffer (for chaining) + * @param sequence IN/OUT: the sequence containing the serialized bytes + * @return the sequence (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 sequence * @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 sequence * @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 sequence * @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 sequence * @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 sequence * @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 sequence * @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 sequence * @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(sequence + 1), strLen); + value.set(reinterpret_cast(sequence + 1), strLen); strLen += 1; sequence += strLen; length -= strLen; @@ -245,12 +282,12 @@ public: * @param length IN/OUT: the length of sequence * @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(sequence + 2), strLen); + value.set(reinterpret_cast(sequence + 2), strLen); strLen += 2; length -= strLen; sequence += strLen; @@ -260,14 +297,14 @@ public: * @param length IN/OUT: the length of sequence * @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(sequence + 4), strLen); + value.set(reinterpret_cast(sequence + 4), strLen); strLen += 4; length -= strLen; sequence += strLen; diff --git a/base/ReStringList.cpp b/base/ReStringList.cpp index e08a453..4f4a8ae 100644 --- a/base/ReStringList.cpp +++ b/base/ReStringList.cpp @@ -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 */ diff --git a/base/ReStringList.hpp b/base/ReStringList.hpp index 934d407..8c261ae 100644 --- a/base/ReStringList.hpp +++ b/base/ReStringList.hpp @@ -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 */ diff --git a/base/ReStringUtils.cpp b/base/ReStringUtils.cpp index 6e7802c..9621f53 100644 --- a/base/ReStringUtils.cpp +++ b/base/ReStringUtils.cpp @@ -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 */ diff --git a/base/ReStringUtils.hpp b/base/ReStringUtils.hpp index f6306b5..6b6f476 100644 --- a/base/ReStringUtils.hpp +++ b/base/ReStringUtils.hpp @@ -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 */ diff --git a/base/ReTestUnit.cpp b/base/ReTestUnit.cpp index 27a4079..261d301 100644 --- a/base/ReTestUnit.cpp +++ b/base/ReTestUnit.cpp @@ -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" diff --git a/base/ReTestUnit.hpp b/base/ReTestUnit.hpp index 2752ac4..cc23271 100644 --- a/base/ReTestUnit.hpp +++ b/base/ReTestUnit.hpp @@ -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 */ diff --git a/base/ReThread.cpp b/base/ReThread.cpp index 512a850..5a027de 100644 --- a/base/ReThread.cpp +++ b/base/ReThread.cpp @@ -1,397 +1,399 @@ -/* - * ReThreadStarter.cpp - * - * License: Public domain - * Do what you want. - * No warranties and disclaimer of any damages. - * The latest sources: https://github.com/republib - */ - -#include "base/rebase.hpp" - -enum RELOC_HASHLIST { - LC_PREPARE_TO_RUN_1 = LC_THREAD + 1, // 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 true: the ReThreadStarter - * 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 setShouldStop(true) - * 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 ReThredStarter() - * - * @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 true: success
- * false: 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 true: 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 ReThread instance - * */ -void* globalThreadStarterFunction(void *pConnection) { - ReThread* thread = reinterpret_cast(pConnection); - thread->runAndFinish(); - return NULL; -} -#elif defined __WIN32__ -DWORD WINAPI globalThreadStarterFunction(_In_ LPVOID pConnection) { - ReThread* thread = reinterpret_cast(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(thread)) - >= 0; -#elif defined __WIN32__ - ok = (thread->m_threadInfo = CreateThread(NULL, 0, globalThreadStarterFunction, - reinterpret_cast(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 true: all threads are finished
- * false: 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 true: all threads are finished
- * false: timeout reached - */ -bool ReThreadPool::waitForDone(int timeoutSec) { - bool rc = waitForAlmostAll(0, timeoutSec); - return rc; -} +/* + * 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 true: the ReThreadStarter + * 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 setShouldStop(true) + * 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 ReThredStarter() + * + * @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 true: success
+ * false: 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 true: 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 ReThread instance + * */ +void* globalThreadStarterFunction(void *pConnection) { + ReThread* thread = reinterpret_cast(pConnection); + thread->runAndFinish(); + return NULL; +} +#elif defined __WIN32__ +DWORD WINAPI globalThreadStarterFunction(_In_ LPVOID pConnection) { + ReThread* thread = reinterpret_cast(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(thread)) + >= 0; +#elif defined __WIN32__ + ok = (thread->m_threadInfo = CreateThread(NULL, 0, globalThreadStarterFunction, + reinterpret_cast(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 true: all threads are finished
+ * false: 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 true: all threads are finished
+ * false: timeout reached + */ +bool ReThreadPool::waitForDone(int timeoutSec) { + bool rc = waitForAlmostAll(0, timeoutSec); + return rc; +} diff --git a/base/ReThread.hpp b/base/ReThread.hpp index 29eaea6..6980db8 100644 --- a/base/ReThread.hpp +++ b/base/ReThread.hpp @@ -1,113 +1,115 @@ -/* - * ReThreadStarter.hpp - * - * License: Public domain - * Do what you want. - * No warranties and disclaimer of any damages. - * 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 ReThreadStarter. - */ -class ReThread: public ReLoggerOwner { -public: - ReThread(bool autoDelete); - virtual ~ReThread(); -public: - /** Does the actual task. - * This method must do a m_isStopped = true; at the end! - */ - virtual void run() = 0; - /** Returns if the thread is stopped. - * @return truethe thread is stopped - */ - inline bool isStopped() const { - return m_isStopped; - } - virtual ReLogger* logger(); - /** Sets the wish for stopping the thread. - * @param value true: 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 true: 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_ */ +/* + * 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 ReThreadStarter. + */ +class ReThread: public ReLoggerOwner { +public: + ReThread(bool autoDelete); + virtual ~ReThread(); +public: + /** Does the actual task. + * This method must do a m_isStopped = true; at the end! + */ + virtual void run() = 0; + /** Returns if the thread is stopped. + * @return truethe thread is stopped + */ + inline bool isStopped() const { + return m_isStopped; + } + virtual ReLogger* logger(); + /** Sets the wish for stopping the thread. + * @param value true: 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 true: 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_ */ diff --git a/base/ReVarArgs.cpp b/base/ReVarArgs.cpp index 2c05595..516cdc4 100644 --- a/base/ReVarArgs.cpp +++ b/base/ReVarArgs.cpp @@ -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 */ /** diff --git a/base/ReVarArgs.hpp b/base/ReVarArgs.hpp index 2341a3f..b0b60f9 100644 --- a/base/ReVarArgs.hpp +++ b/base/ReVarArgs.hpp @@ -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 */ diff --git a/base/baselocations.hpp b/base/baselocations.hpp index 7aa301a..afa526a 100644 --- a/base/baselocations.hpp +++ b/base/baselocations.hpp @@ -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 */ @@ -12,32 +14,19 @@ /** 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_ */ diff --git a/base/rebase.hpp b/base/rebase.hpp index 673cb1d..3d03b5f 100644 --- a/base/rebase.hpp +++ b/base/rebase.hpp @@ -1,143 +1,150 @@ -/* - * rebase.hpp - * - * License: Public domain - * Do what you want. - * No warranties and disclaimer of any damages. - * The latest sources: https://github.com/republib - */ -#ifndef REBASE_HPP_ -#define min() -#define max() -#define REBASE_HPP_ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define __LITTLE_ENDIAN__ -//#define __BIG_ENDIAN__ - -#if defined __linux__ - -# include -# include -# include -# include -# include -# include -# include -# include -#include -#include -#include -typedef u_int64_t uint64_t; -typedef u_int8_t uint8_t; -typedef __off_t ReFileSize_t; -typedef 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 -# include -# include -# 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 ReProcessor. - * The thread starter gets an instance of this task class and calls the - * method process() 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_ */ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define __LITTLE_ENDIAN__ +//#define __BIG_ENDIAN__ + +#if defined __linux__ + +# include +# include +# include +# include +# include +# include +# include +# include +#include +#include +#include +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 +# include +# include +# 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 ReProcessor. + * The thread starter gets an instance of this task class and calls the + * method process() 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_ */ diff --git a/base/restring.hpp b/base/restring.hpp index 82d9489..83e2d08 100644 --- a/base/restring.hpp +++ b/base/restring.hpp @@ -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 */ diff --git a/cunit/basetest.cpp b/cunit/basetest.cpp index 9c8c397..ffc13d5 100644 --- a/cunit/basetest.cpp +++ b/cunit/basetest.cpp @@ -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" diff --git a/cunit/cuReByteBuffer.cpp b/cunit/cuReByteBuffer.cpp index 4a68ea1..cec85c1 100644 --- a/cunit/cuReByteBuffer.cpp +++ b/cunit/cuReByteBuffer.cpp @@ -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" diff --git a/cunit/cuReCString.cpp b/cunit/cuReCString.cpp index 737a43c..6785988 100644 --- a/cunit/cuReCString.cpp +++ b/cunit/cuReCString.cpp @@ -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" diff --git a/cunit/cuReDirTools.cpp b/cunit/cuReDirTools.cpp index 6bd506f..3408da6 100644 --- a/cunit/cuReDirTools.cpp +++ b/cunit/cuReDirTools.cpp @@ -1,494 +1,496 @@ -/* - * cuReTraverser.cpp - * - * License: Public domain - * Do what you want. - * No warranties and disclaimer of any damages. - * 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; -} +/* + * 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; +} diff --git a/cunit/cuReDirectory.cpp b/cunit/cuReDirectory.cpp index cf86066..677a3f8 100644 --- a/cunit/cuReDirectory.cpp +++ b/cunit/cuReDirectory.cpp @@ -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" diff --git a/cunit/cuReException.cpp b/cunit/cuReException.cpp index cb67b8a..9909d22 100644 --- a/cunit/cuReException.cpp +++ b/cunit/cuReException.cpp @@ -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" diff --git a/cunit/cuReHashList.cpp b/cunit/cuReHashList.cpp index 69ed6b2..5c2d47e 100644 --- a/cunit/cuReHashList.cpp +++ b/cunit/cuReHashList.cpp @@ -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" diff --git a/cunit/cuReI18N.cpp b/cunit/cuReI18N.cpp index ceaaf58..84bf6bb 100644 --- a/cunit/cuReI18N.cpp +++ b/cunit/cuReI18N.cpp @@ -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" diff --git a/cunit/cuReLogger.cpp b/cunit/cuReLogger.cpp index b4e19f0..11df992 100644 --- a/cunit/cuReLogger.cpp +++ b/cunit/cuReLogger.cpp @@ -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" diff --git a/cunit/cuReMD5.cpp b/cunit/cuReMD5.cpp index a80828f..db02725 100644 --- a/cunit/cuReMD5.cpp +++ b/cunit/cuReMD5.cpp @@ -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 */ diff --git a/cunit/cuReMatcher.cpp b/cunit/cuReMatcher.cpp index 699643d..e021e6d 100644 --- a/cunit/cuReMatcher.cpp +++ b/cunit/cuReMatcher.cpp @@ -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 */ /* diff --git a/cunit/cuReProgramArgs.cpp b/cunit/cuReProgramArgs.cpp index e6bd316..d92946d 100644 --- a/cunit/cuReProgramArgs.cpp +++ b/cunit/cuReProgramArgs.cpp @@ -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" diff --git a/cunit/cuReRPD64.cpp b/cunit/cuReRPD64.cpp index 41676ac..57eee08 100644 --- a/cunit/cuReRPD64.cpp +++ b/cunit/cuReRPD64.cpp @@ -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 */ diff --git a/cunit/cuReRandomizer.cpp b/cunit/cuReRandomizer.cpp index cc91dd2..4d18971 100644 --- a/cunit/cuReRandomizer.cpp +++ b/cunit/cuReRandomizer.cpp @@ -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" diff --git a/cunit/cuReSeqArray.cpp b/cunit/cuReSeqArray.cpp index 1c0b174..52e2824 100644 --- a/cunit/cuReSeqArray.cpp +++ b/cunit/cuReSeqArray.cpp @@ -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" diff --git a/cunit/cuReSerializable.cpp b/cunit/cuReSerializable.cpp index af70cb9..f4f5f90 100644 --- a/cunit/cuReSerializable.cpp +++ b/cunit/cuReSerializable.cpp @@ -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)); } }; diff --git a/cunit/cuReStringList.cpp b/cunit/cuReStringList.cpp index bf86bb0..bec68c4 100644 --- a/cunit/cuReStringList.cpp +++ b/cunit/cuReStringList.cpp @@ -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" diff --git a/cunit/cuReStringUtils.cpp b/cunit/cuReStringUtils.cpp index 15cf8eb..05227db 100644 --- a/cunit/cuReStringUtils.cpp +++ b/cunit/cuReStringUtils.cpp @@ -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" diff --git a/cunit/cuReTCP.cpp b/cunit/cuReTCP.cpp index dc9aa22..162cb1d 100644 --- a/cunit/cuReTCP.cpp +++ b/cunit/cuReTCP.cpp @@ -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 */ diff --git a/cunit/cuReTest.cpp b/cunit/cuReTest.cpp index b510a5a..3596035 100644 --- a/cunit/cuReTest.cpp +++ b/cunit/cuReTest.cpp @@ -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" diff --git a/cunit/cuReTraverser.cpp b/cunit/cuReTraverser.cpp index 74e8c14..90cc05c 100644 --- a/cunit/cuReTraverser.cpp +++ b/cunit/cuReTraverser.cpp @@ -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); diff --git a/cunit/cuReVarArgs.cpp b/cunit/cuReVarArgs.cpp index 61f32cd..283e1ac 100644 --- a/cunit/cuReVarArgs.cpp +++ b/cunit/cuReVarArgs.cpp @@ -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" diff --git a/cunit/cuReconfig.cpp b/cunit/cuReconfig.cpp index f194bf5..d2e9768 100644 --- a/cunit/cuReconfig.cpp +++ b/cunit/cuReconfig.cpp @@ -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" diff --git a/cunit/testall.cpp b/cunit/testall.cpp index 4e566a2..342b823 100644 --- a/cunit/testall.cpp +++ b/cunit/testall.cpp @@ -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" diff --git a/dirtool.cpp b/dirtool.cpp index 6b8c9c3..43fb5f5 100644 --- a/dirtool.cpp +++ b/dirtool.cpp @@ -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. // #include "base/rebase.hpp" diff --git a/math/ReMD5.cpp b/math/ReMD5.cpp index 5149a77..fe33a49 100644 --- a/math/ReMD5.cpp +++ b/math/ReMD5.cpp @@ -1,516 +1,520 @@ -/* - * ReMD5.cpp - * - * Created on: 30.01.2015 - * - */ - -#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 digest - * @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 block - */ -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 update()! - * - * 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 +/* + * 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 digest + * @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 block + */ +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 update()! + * + * 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; \ m_digest[ix + 1] = word; word >>= 8; m_digest[ix + 2] = word; word >>= 8; \ - 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 + 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) { \ uint32_t val = data; \ var = ((val << shift) | (val >> (32-shift))) + term; \ -} +} #define X1(var, x, y, z, data, aConst, shift) \ - rotate_left_and_add(var, var + F1(x, y, z) + data + aConst, shift, x) + rotate_left_and_add(var, var + F1(x, y, z) + data + aConst, shift, x) #define X2(var, x, y, z, data, aConst, shift) \ - rotate_left_and_add(var, var + F2(x, y, z) + data + aConst, shift, x) + rotate_left_and_add(var, var + F2(x, y, z) + data + aConst, shift, x) #define X3(var, x, y, z, data, aConst, shift) \ - rotate_left_and_add(var, var + F3(x, y, z) + data + aConst, shift, x) + rotate_left_and_add(var, var + F3(x, y, z) + data + aConst, shift, x) #define X4(var, x, y, z, data, aConst, shift) \ - 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. - *
- * 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]);
- * ...
- * 
- */ -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; -} + 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. + *
+ * 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]);
+ * ...
+ * 
+ */ +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; +} diff --git a/math/ReMD5.hpp b/math/ReMD5.hpp index de857f0..3be9883 100644 --- a/math/ReMD5.hpp +++ b/math/ReMD5.hpp @@ -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_ diff --git a/math/ReObfuscator.cpp b/math/ReObfuscator.cpp index 846e228..0597115 100644 --- a/math/ReObfuscator.cpp +++ b/math/ReObfuscator.cpp @@ -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 */ diff --git a/math/ReObfuscator.hpp b/math/ReObfuscator.hpp index 1740bcc..267002d 100644 --- a/math/ReObfuscator.hpp +++ b/math/ReObfuscator.hpp @@ -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 */ diff --git a/math/ReRPD64.cpp b/math/ReRPD64.cpp index 601feab..929eab3 100644 --- a/math/ReRPD64.cpp +++ b/math/ReRPD64.cpp @@ -1,229 +1,233 @@ -/* - * ReRPD64.cpp - * - * Created on: 30.01.2015 - * - */ - -#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 +/* + * 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; \ m_digest[ix + 1] = word; word >>= 8; m_digest[ix + 2] = word; word >>= 8; \ m_digest[ix + 3] = word; word >>= 8; m_digest[ix + 4] = word; word >>= 8; \ m_digest[ix + 4] = word; word >>= 8; m_digest[ix + 5] = word; word >>= 8; \ - 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 + 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) { \ uint64_t val = data; \ var = ((val << (shift)) | (val >> (64-(shift)))) + term; \ -} +} #define X1(var, x, y, z, data, aConst, shift) \ - rotate_left_and_add(var, var + F1(x, y, z) + data + aConst, shift, x) + rotate_left_and_add(var, var + F1(x, y, z) + data + aConst, shift, x) #define X2(var, x, y, z, data, aConst, shift) \ - rotate_left_and_add(var, var + F2(x, y, z) + data + aConst, shift, x) + rotate_left_and_add(var, var + F2(x, y, z) + data + aConst, shift, x) #define X3(var, x, y, z, data, aConst, shift) \ - rotate_left_and_add(var, var + F3(x, y, z) + data + aConst, shift, x) + rotate_left_and_add(var, var + F3(x, y, z) + data + aConst, shift, x) #define X4(var, x, y, z, data, aConst, shift) \ - 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; -} - + 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; +} + diff --git a/math/ReRPD64.hpp b/math/ReRPD64.hpp index 08211b5..2a2e732 100644 --- a/math/ReRPD64.hpp +++ b/math/ReRPD64.hpp @@ -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_ diff --git a/math/ReRandomizer.cpp b/math/ReRandomizer.cpp index 7a83974..7dbab47 100644 --- a/math/ReRandomizer.cpp +++ b/math/ReRandomizer.cpp @@ -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 */ diff --git a/math/ReRandomizer.hpp b/math/ReRandomizer.hpp index c336ee7..861a894 100644 --- a/math/ReRandomizer.hpp +++ b/math/ReRandomizer.hpp @@ -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 */ diff --git a/math/remath.hpp b/math/remath.hpp index db9eca9..e6a2a90 100644 --- a/math/remath.hpp +++ b/math/remath.hpp @@ -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 */ diff --git a/net/ReTCP.cpp b/net/ReTCP.cpp index 905b164..300c6db 100644 --- a/net/ReTCP.cpp +++ b/net/ReTCP.cpp @@ -1,707 +1,709 @@ -/* - * ReTCP.cpp - * - * Created on: 04.03.2015 - * Author: hm - */ - -#include "base/rebase.hpp" -#include "net/renet.hpp" - -enum LOCATION_DIRTOOL { - LC_LISTEN_FOR_ALL_1 = LC_TCP + 1, // 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(&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
- * PS_PROCESSED: command successfully processed
- * PS_FAILED: command processed, error occurred
- * 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); -} +/* + * 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(&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
+ * PS_PROCESSED: command successfully processed
+ * PS_FAILED: command processed, error occurred
+ * 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); +} diff --git a/net/ReTCP.hpp b/net/ReTCP.hpp index 67f20f9..85d0347 100644 --- a/net/ReTCP.hpp +++ b/net/ReTCP.hpp @@ -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 */ diff --git a/net/ReUdpConnection.cpp b/net/ReUdpConnection.cpp index 77a2989..a113c91 100644 --- a/net/ReUdpConnection.cpp +++ b/net/ReUdpConnection.cpp @@ -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: diff --git a/net/ReUdpConnection.hpp b/net/ReUdpConnection.hpp index bc2787c..fd27d19 100644 --- a/net/ReUdpConnection.hpp +++ b/net/ReUdpConnection.hpp @@ -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_ diff --git a/net/renet.hpp b/net/renet.hpp index 0ccc8c0..11bf45d 100644 --- a/net/renet.hpp +++ b/net/renet.hpp @@ -1,24 +1,25 @@ -/* - * renet.hpp - * - * License: Public domain - * Do what you want. - * No warranties and disclaimer of any damages. - * The latest sources: https://github.com/republib - * - */ - -#ifndef NET_RENET_HPP_ -#define NET_RENET_HPP_ - -#if defined __linux__ -#include -#include -#include -#include -#elif defined __WIN32__ -#include -#endif -#include "net/ReUdpConnection.hpp" -#include "net/ReTCP.hpp" -#endif /* NET_RENET_HPP_ */ +/* + * 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 +#include +#include +#include +#elif defined __WIN32__ +#include +#endif +#include "net/ReUdpConnection.hpp" +#include "net/ReTCP.hpp" +#endif /* NET_RENET_HPP_ */ diff --git a/os/ReDirTools.cpp b/os/ReDirTools.cpp index 77828ac..f802f72 100644 --- a/os/ReDirTools.cpp +++ b/os/ReDirTools.cpp @@ -1,2654 +1,2655 @@ -/* - * ReDirTools.cpp - * - * License: Public domain - * Do what you want. - * No warranties and disclaimer of any damages. - * 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 = LC_DIRTOOLS + 1, // 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 ", - " Useful commands around directory trees.", - " Type 'dirtool help ' for more help.", ":", - "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[] = { - ": batch [] [ ...]", - " 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[] = { - ": delete [] [ ...]", - " 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[] = { - ": l(ist) [] [ ...]", - " 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[] = - { ": c(hecksum) [] [ ...]", - " 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[] = { ": r(andom) count until [from]", - " displays random numbers.", " count of generated numbers", - " the maximum (including) value of the numbers", - " 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[] = - { ": st(atistic) [] [ ...] []", - " shows a statistic about a directory tree", - " a directory path: relative or absolute", - " 0: only the summary of will be shown", - " 1: shows the summery of each subdir of and the total", - " n: shows the summery of each subdir until level and the total", - " default: 1", ":", - 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[] = - { ": sy(nchronize) [ ...] ", - " 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[] = { - ": tcp [] [ ...]", - " test tool for network test", ":", " server", - " client [ [ []]]", - " : URL of the server", " : number of messages to send", - " : '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[] = - { ": touch [] [ ...]", - " 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[] = { - ": w(hicht) [] [ ...]", - " 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 \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 \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 \n" - " 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 seconds the current path will be traced\n" - "0: no trace"), 'T', "trace-interval", 0); - m_programArgs.addString("type", i18n("the file type\n" - " is a list of values:\n" - ": b(lock) c(har) d(irectory) (l)i(nkdir) l(ink) o(ther)\n" - " p(ipe) s(ocket) r(egular)\n" - "-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 \n" - " 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 \n" - " 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 \n" - " 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: - * 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 ( expected). : 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: - * 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 : $1. Expected: b k K m M g G"), value); - } - break; - default: - throw ReOptionException(&m_programArgs, - i18n( - "invalid size value: $1 expected: : 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 value (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: - *
  • remove a trailing "\" and "\*"
  • - *
  • change "*\xy" to "*\xy" and "xy" (finds xy in the root directory)
  • - *
  • replaces "/" with the os specific path separator
  • - *
- * - * @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 - * true: 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 true (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: argv ends with NULL - * @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 processSingleFile() - * or processTree(). - * - */ -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 processFile(). - * - * @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 processFile() - * or processDir(). - * - * @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 ' ' (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
- * OUT: contains the calculated checksum - * @param buffer IN/OUT: a buffer for file reading - * @param logger logger for error processing - * @return digest (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(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 argv - * @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 ReDirStatistic::calculate(). - * - * @param data statistic data, including path name - * @param parent the caller (ReDirStatistic). This allows to deliver - * a context to this formatting routine (through derivation of - * ReDirStatistic) - * @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 ReDirStatistic::calculate(). - * - * @param data statistic data, including path name - * @param parent the caller (ReDirStatistic). This allows to deliver - * a context to this formatting routine (through derivation of - * ReDirStatistic) - * @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-{s|m|h|d}\n" - "{+|-}{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-{s|m|h|d}\n" - "{+|-}{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 \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
- * Set the capacity to make it more efficient - * @param logger NULL or the logger for error messages - * @return truesuccess
- * falseerror 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 true: success
- * false: 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 truesuccess
- * falseerror 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" - " 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 true: 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; -} - +/* + * 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 ", + " Useful commands around directory trees.", + " Type 'dirtool help ' for more help.", ":", + "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[] = { + ": batch [] [ ...]", + " 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[] = { + ": delete [] [ ...]", + " 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[] = { + ": l(ist) [] [ ...]", + " 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[] = + { ": c(hecksum) [] [ ...]", + " 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[] = { ": r(andom) count until [from]", + " displays random numbers.", " count of generated numbers", + " the maximum (including) value of the numbers", + " 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[] = + { ": st(atistic) [] [ ...] []", + " shows a statistic about a directory tree", + " a directory path: relative or absolute", + " 0: only the summary of will be shown", + " 1: shows the summery of each subdir of and the total", + " n: shows the summery of each subdir until level and the total", + " default: 1", ":", + 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[] = + { ": sy(nchronize) [ ...] ", + " 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[] = { + ": tcp [] [ ...]", + " test tool for network test", ":", " server", + " client [ [ []]]", + " : URL of the server", " : number of messages to send", + " : '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[] = + { ": touch [] [ ...]", + " 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[] = { + ": w(hicht) [] [ ...]", + " 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 \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 \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 \n" + " 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 seconds the current path will be traced\n" + "0: no trace"), 'T', "trace-interval", 0); + m_programArgs.addString("type", i18n("the file type\n" + " is a list of values:\n" + ": b(lock) c(har) d(irectory) (l)i(nkdir) l(ink) o(ther)\n" + " p(ipe) s(ocket) r(egular)\n" + "-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 \n" + " 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 \n" + " 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 \n" + " 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: + * 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 ( expected). : 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: + * 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 : $1. Expected: b k K m M g G"), value); + } + break; + default: + throw ReOptionException(&m_programArgs, + i18n( + "invalid size value: $1 expected: : 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 value (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: + *
  • remove a trailing "\" and "\*"
  • + *
  • change "*\xy" to "*\xy" and "xy" (finds xy in the root directory)
  • + *
  • replaces "/" with the os specific path separator
  • + *
+ * + * @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 + * true: 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 true (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: argv ends with NULL + * @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 processSingleFile() + * or processTree(). + * + */ +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 processFile(). + * + * @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 processFile() + * or processDir(). + * + * @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 ' ' (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
+ * OUT: contains the calculated checksum + * @param buffer IN/OUT: a buffer for file reading + * @param logger logger for error processing + * @return digest (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(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 argv + * @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 ReDirStatistic::calculate(). + * + * @param data statistic data, including path name + * @param parent the caller (ReDirStatistic). This allows to deliver + * a context to this formatting routine (through derivation of + * ReDirStatistic) + * @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 ReDirStatistic::calculate(). + * + * @param data statistic data, including path name + * @param parent the caller (ReDirStatistic). This allows to deliver + * a context to this formatting routine (through derivation of + * ReDirStatistic) + * @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-{s|m|h|d}\n" + "{+|-}{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-{s|m|h|d}\n" + "{+|-}{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 \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
+ * Set the capacity to make it more efficient + * @param logger NULL or the logger for error messages + * @return truesuccess
+ * falseerror 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 true: success
+ * false: 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 truesuccess
+ * falseerror 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" + " 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 true: 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; +} + diff --git a/os/ReDirTools.hpp b/os/ReDirTools.hpp index d2f7b60..8af9434 100644 --- a/os/ReDirTools.hpp +++ b/os/ReDirTools.hpp @@ -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); }; /** diff --git a/os/ReRemoteDir.cpp b/os/ReRemoteDir.cpp index 0207988..35943c0 100644 --- a/os/ReRemoteDir.cpp +++ b/os/ReRemoteDir.cpp @@ -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 sequence */ -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) { } diff --git a/os/ReRemoteDir.hpp b/os/ReRemoteDir.hpp index c8837de..9e257c1 100644 --- a/os/ReRemoteDir.hpp +++ b/os/ReRemoteDir.hpp @@ -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(); diff --git a/os/ReTraverser.cpp b/os/ReTraverser.cpp index 184b05c..19231f5 100644 --- a/os/ReTraverser.cpp +++ b/os/ReTraverser.cpp @@ -1,978 +1,989 @@ -/* - * ReTraverser.cpp - * - * License: Public domain - * Do what you want. - * No warranties and disclaimer of any damages. - * 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 = LC_TRAVERSER + 1, // 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 buffer.str() (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 buffer.str(), 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 true: 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 true: 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 CreateFile()) - * @param name OUT: the owner: [domain\\]name - * @return true: 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 true: 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 true: 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 true: 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 true: 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 true: the owner/group should be numerical (UID/GID) - * @param ownerWidth the width for group/owner - * @return buffer.str() (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 sequence - */ -void ReDirEntryFilter::deserialize(uint8_t*& sequence, size_t& length){ - /*ReDirStatus_t::Type_t m_types; - RePatternList* m_nodePatterns; - RePatternList* m_pathPatterns; - ReFileSize_t m_minSize; - ReFileSize_t m_maxSize; - ReFileTime_t m_minAge; - ReFileTime_t m_maxAge; - int m_minDepth; - int m_maxDepth; - bool m_allDirectories; */ - - -} -/** - * Packs the members into a byte sequence. - * - * @param sequence IN/OUT: the place for the byte sequence - */ -ReByteBuffer& ReDirEntryFilter::serialize(ReByteBuffer& sequence){ - /*ReDirStatus_t::Type_t m_types; - RePatternList* m_nodePatterns; - RePatternList* m_pathPatterns; - ReFileSize_t m_minSize; - ReFileSize_t m_maxSize; - ReFileTime_t m_minAge; - ReFileTime_t m_maxAge; - int m_minDepth; - int m_maxDepth; - bool m_allDirectories; */ - sequence.appendBits24(m_serialId); - packString255(sequence, m_nodePatterns->patternString()); - -} - - -/** - * Tests whether an entry matches the conditions of the filter. - * - * @param entry entry to test - * @return : the entry matches the conditions of the filter
- * : 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 true: the string will be appended to the buffer
- * false: the buffer will be cleared at the beginning - * @param formatFiles the sprintf format for the file count, e.g. "%8d" - * @param formatSized the sprintf format for the MByte format, e.g. "%12.6f" - * @param formatFiles the sprintf 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 true (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.
- * 0 means the file is inside the base.
- * Not defined if the result is NULL - * @return NULL no more files
- * otherwise: the stack entry with the next file in the - * directory tree. May be a directory too - */ -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.
- * 0 means the file is inside the base.
- * Not defined if the result is NULL - * @param filter NULL: every file matches
- * otherwise: each found file must match this filter conditions - * @return NULL no more files
- * otherwise: the info about the next file in the - * directory tree - */ -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.
- * If < 0: m_levels and m_path will not be changed - * @return true: a new file is available
- * 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
- * 1: returns the entry one below the top
- * 2: ... - * @return NULL: not available
- * 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; -} +/* + * 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 buffer.str() (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 buffer.str(), 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 true: 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 true: 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 CreateFile()) + * @param name OUT: the owner: [domain\\]name + * @return true: 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 true: 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 true: 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 true: 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 true: 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 true: the owner/group should be numerical (UID/GID) + * @param ownerWidth the width for group/owner + * @return buffer.str() (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 sequence + */ +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 : the entry matches the conditions of the filter
+ * : 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 true: the string will be appended to the buffer
+ * false: the buffer will be cleared at the beginning + * @param formatFiles the sprintf format for the file count, e.g. "%8d" + * @param formatSized the sprintf format for the MByte format, e.g. "%12.6f" + * @param formatFiles the sprintf 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 true (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.
+ * 0 means the file is inside the base.
+ * Not defined if the result is NULL + * @return NULL no more files
+ * otherwise: the stack entry with the next file in the + * directory tree. May be a directory too + */ +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.
+ * 0 means the file is inside the base.
+ * Not defined if the result is NULL + * @param filter NULL: every file matches
+ * otherwise: each found file must match this filter conditions + * @return NULL no more files
+ * otherwise: the info about the next file in the + * directory tree + */ +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.
+ * If < 0: m_levels and m_path will not be changed + * @return true: a new file is available
+ * 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
+ * 1: returns the entry one below the top
+ * 2: ... + * @return NULL: not available
+ * 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; +} diff --git a/os/ReTraverser.hpp b/os/ReTraverser.hpp index 7b42461..241b913 100644 --- a/os/ReTraverser.hpp +++ b/os/ReTraverser.hpp @@ -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: diff --git a/os/reos.hpp b/os/reos.hpp index f6b57d3..342a9f1 100644 --- a/os/reos.hpp +++ b/os/reos.hpp @@ -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 */ diff --git a/string/ReMatcher.cpp b/string/ReMatcher.cpp index 2a68b87..55f784e 100644 --- a/string/ReMatcher.cpp +++ b/string/ReMatcher.cpp @@ -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 */ diff --git a/string/ReMatcher.hpp b/string/ReMatcher.hpp index 1381396..7bf0467 100644 --- a/string/ReMatcher.hpp +++ b/string/ReMatcher.hpp @@ -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 true: 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 true: 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 diff --git a/string/restring.hpp b/string/restring.hpp index 59f1988..46a5069 100644 --- a/string/restring.hpp +++ b/string/restring.hpp @@ -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 */