/*
* 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"
/*
* 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_
/*
* 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
*/
/*
* 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
*/
/*
* 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
*/
/*
* 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
*/
/*
* 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.
/*
* 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
*/
/*
* 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;
/*
* 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
*/
* @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_ */
/*
* 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
};
/*
- * 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
*/
/*
* 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
*/
/*
* 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
*/
/*
* 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
*/
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()) {
+}
/*
* 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
*/
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_ */
/*
* 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
};
/**
/*
* 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
*/
/*
* 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
*/
/*
* 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_
/*
* 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,
/*
* 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
*/
/*
* 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
*/
/*
* 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
*/
/*
* 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
*/
/*
* 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
*/
/*
* 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
/*
* 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
*/
/*
* 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
*/
*/
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.
* @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) {
}
*/
ReSerializable::~ReSerializable() {
}
+
+/**
+ * Puts a filetime to the byte sequence.
+ *
+ * @param sequence IN/OUT: the byte sequence
+ * @param time the filetime to serialize
+ *
+ */
+void ReSerializable::packFileTime(ReByteBuffer& sequence,
+ const ReFileTime_t& time) {
+ uint64_t value;
+#if defined __linux__
+ value = (time.tv_sec << 32) + time.tv_nsec;
+ sequence.appendBits64(int64_t(value));
+#elif defined __WIN32__
+#error "missing impl"
+#endif
+}
+/**
+ * Gets a filetime from a byte sequence.
+ *
+ * @param sequence IN/OUT: the byte sequence
+ * @param length IN/OUT: the length of <code>sequence</code>
+ * @param time OUT: the filetime to set
+ * @param senderOs the operating system which has serialized the data
+ * @throws ReSerializationLengthException
+ */
+void ReSerializable::unpackFileTime(const uint8_t*& sequence, size_t& length,
+ ReFileTime_t& time, ReOSType os) {
+ int64_t value;
+ if (length < 8)
+ throw ReSerializationLengthException(length, 8, this);
+
+#if defined __linux__
+ switch (os) {
+ case OS_LINUX:
+ unpackInt64(sequence, length, value);
+ time.tv_sec = int(value >> 32);
+ time.tv_nsec = int(value);
+ break;
+ case OS_WIN32:
+ throw ReNotImplementedException("unpackFileTime(): linux-win32");
+ break;
+ default:
+ break;
+ }
+#elif defined __WIN32__
+#error "missing impl"
+#endif
+}
/*
* 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
*/
class ReSerializationLengthException: public ReSerializationException {
public:
ReSerializationLengthException(int currentLength, int expectedLength,
- ReSerializable* instance);
+ ReSerializable* instance);
public:
int m_currentLength;
int m_expectedLength;
class ReSerializeStringLengthException: public ReSerializationException {
public:
ReSerializeStringLengthException(int currentLength, int maxLength,
- ReSerializable* instance);
+ ReSerializable* instance);
public:
int m_currentLength;
int m_maxLength;
ReSerializable(int serialId);
virtual ~ReSerializable();
public:
+ /** Sets the members of the instance from the byte sequence.
+ *
+ * @param sequence IN: a byte sequence starting with the serialized members
+ * of the instance<br>
+ * OUT: the byte sequence behind the serialized members
+ * @param length IN: the length of <code>sequence</code><br>
+ * OUT: the length of <code>sequence</code> without the
+ * serialized members
+ */
+ virtual void deserialize(const uint8_t*& sequence, size_t& length) = 0;
+ /** Sets the members of the instance from the byte sequence in a sequence.
+ * @param sequence the serialized sequence
+ * @throw ReSerializationLengthException
+ */
+ inline void deserializeBuffer(const ReByteBuffer& sequence) {
+ size_t length = sequence.length();
+ const uint8_t* seq = reinterpret_cast<const uint8_t*>(sequence.str());
+ deserialize(seq, length);
+ if (length != 0)
+ throw ReSerializationLengthException(length, 0, this);
+ }
/** Appends a boolean value.
- * @param buffer IN/OUT: the buffer with the serialized values
+ * @param sequence IN/OUT: the sequence with the serialized values
* @param value the value to serialize
*/
- inline void packBool(ReByteBuffer& buffer, bool value) {
- buffer.appendChar(value ? 't' : 'f');
+ inline void packBool(ReByteBuffer& sequence, bool value) {
+ sequence.appendChar(value ? 't' : 'f');
}
- /** Appends a string with a maximal length of 255 to the buffer.
- * @param buffer IN/OUT: the buffer with the serialized values
+ void packFileTime(ReByteBuffer& sequence, const ReFileTime_t& time);
+ /** Appends a string with a maximal length of 255 to the sequence.
+ * @param sequence IN/OUT: the sequence with the serialized values
* @param value the value to serialize
*/
- inline void packString255(ReByteBuffer& buffer, ReByteBuffer& value) {
+ inline void packString255(ReByteBuffer& sequence, ReByteBuffer& value) {
if (value.length() > 255)
throw ReSerializeStringLengthException(value.length(), 255, this);
- buffer.appendBits8(value.length()).append(value);
+ sequence.appendBits8(value.length()).append(value);
}
- /** Appends a string with a maximal length of 255 to the buffer.
- * @param buffer IN/OUT: the buffer with the serialized values
+ /** Appends a string with a maximal length of 255 to the sequence.
+ * @param sequence IN/OUT: the sequence with the serialized values
* @param value the value to serialize
*/
- inline void packString255(ReByteBuffer& buffer, const char* value,
- size_t length = -1) {
+ inline void packString255(ReByteBuffer& sequence, const char* value,
+ size_t length = -1) {
if (length == (size_t) -1)
length = strlen(value);
if (length > 255)
throw ReSerializeStringLengthException(length, 255, this);
- buffer.appendBits8(length).append(value, length);
+ sequence.appendBits8(length).append(value, length);
}
- /** Appends a string with a maximal length of 64KiByte to the buffer.
- * @param buffer IN/OUT: the buffer with the serialized values
+ /** Appends a string with a maximal length of 64KiByte to the sequence.
+ * @param sequence IN/OUT: the sequence with the serialized values
* @param value the value to serialize
*/
- inline void packString64k(ReByteBuffer& buffer, ReByteBuffer& value) {
+ inline void packString64k(ReByteBuffer& sequence, ReByteBuffer& value) {
if (value.length() > 0xffff)
- throw ReSerializeStringLengthException(value.length(), 0xffff, this);
- buffer.appendBits16(value.length()).append(value);
+ throw ReSerializeStringLengthException(value.length(), 0xffff,
+ this);
+ sequence.appendBits16(value.length()).append(value);
+ }
+ /** Appends a string with a maximal length of 64KiByte to the sequence.
+ * @param sequence IN/OUT: the sequence with the serialized values
+ * @param value the value to serialize
+ * @param length length of <code>value</code><br>
+ * -1: <code>strlen(value)</code>
+ */
+ inline void packString64k(ReByteBuffer& sequence, const char* value,
+ size_t length = -1) {
+ if (length == (size_t) -1)
+ length = strlen(value);
+ if (length > 0xffff)
+ throw ReSerializeStringLengthException(length, 0xffff, this);
+ sequence.appendBits16(length).append(value);
}
- /** Appends a string with a maximal length of 4GiByte to the buffer.
- * @param buffer IN/OUT: the buffer with the serialized values
+ /** Appends a string with a maximal length of 4GiByte to the sequence.
+ * @param sequence IN/OUT: the sequence with the serialized values
* @param value the value to serialize
*/
- inline void packString4t(ReByteBuffer& buffer, ReByteBuffer& value) {
- buffer.appendBits32(value.length()).append(value);
+ inline void packString4t(ReByteBuffer& sequence, ReByteBuffer& value) {
+ sequence.appendBits32(value.length()).append(value);
+ }
+ /** Appends a string with a maximal length of 4 TiByte to the sequence.
+ * @param sequence IN/OUT: the sequence with the serialized values
+ * @param value the value to serialize
+ * @param length length of <code>value</code><br>
+ * -1: <code>strlen(value)</code>
+ */
+ inline void packString4t(ReByteBuffer& sequence, const char* value,
+ size_t length = -1) {
+ if (length == (size_t) -1)
+ length = strlen(value);
+ sequence.appendBits32(length).append(value);
}
/** Returns the name of the parent class.
* @return the parent's classname
inline const char* parentClassName() const {
return classIdToString(ReClassId(m_serialId >> 8));
}
- /** Sets the members of the instance from the byte sequence.
- *
- * @param sequence IN: a byte sequence starting with the serialized members
- * of the instance<br>
- * OUT: the byte sequence behind the serialized members
- * @param length IN: the length of <code>sequence</code><br>
- * OUT: the length of <code>sequence</code> without the
- * serialized members
- */
- virtual void deserialize(uint8_t*& sequence, size_t& length) = 0;
- /** Sets the members of the instance from the byte sequence in a buffer.
- * @param sequence the serialized sequence
- * @throw ReSerializationLengthException
- */
- inline void deserializeBuffer(const ReByteBuffer& sequence) {
- size_t length = sequence.length();
- uint8_t* seq = reinterpret_cast<uint8_t*>(sequence.buffer());
- deserialize(seq, length);
- if (length != 0)
- throw ReSerializationLengthException(length, 0, this);
- }
/** Returns the unique serialization id of the parent class.
* @return the serialization id
*/
inline int serialId() const {
return m_serialId;
}
- /** Appends the class members to the end of the buffer.
+ /** Appends the class members to the end of the sequence.
*
- * @param buffer IN/OUT: the buffer containing the serialized bytes
- * @return the <code>buffer</code> (for chaining)
+ * @param sequence IN/OUT: the sequence containing the serialized bytes
+ * @return the <code>sequence</code> (for chaining)
*/
- virtual ReByteBuffer& serialize(ReByteBuffer& buffer) = 0;
+ virtual ReByteBuffer& serialize(ReByteBuffer& sequence) = 0;
/** Reads a 8 bit integer from the serialized byte sequence.
* @param sequence IN/OUT: the byte sequence with the serialized data
* @param length IN/OUT: the length of <code>sequence</code>
* @param value OUT: the value read from the sequence
*/
- inline void unpackBool(uint8_t*& sequence, size_t& length, bool& value) {
+ inline void unpackBool(const uint8_t*& sequence, size_t& length,
+ bool& value) {
if (length < 1)
throw ReSerializationLengthException(length, 1, this);
char cc = *sequence++;
value = cc == 't';
length--;
}
+ void unpackFileTime(const uint8_t*& sequence, size_t& length,
+ ReFileTime_t& time, ReOSType senderOs);
/** Reads a 8 bit integer from the serialized byte sequence.
* @param sequence IN/OUT: the byte sequence with the serialized data
* @param length IN/OUT: the length of <code>sequence</code>
* @param value OUT: the value read from the sequence
*/
- inline void unpackInt8(uint8_t*& sequence, size_t& length, int& value) {
+ inline void unpackInt8(const uint8_t*& sequence, size_t& length,
+ int& value) {
if (length < 1)
throw ReSerializationLengthException(length, 1, this);
value = *sequence++;
* @param length IN/OUT: the length of <code>sequence</code>
* @param value OUT: the value read from the sequence
*/
- inline void unpackInt16(uint8_t*& sequence, size_t& length, int& value) {
+ inline void unpackInt16(const uint8_t*& sequence, size_t& length,
+ int& value) {
if (length < 2)
throw ReSerializationLengthException(length, 2, this);
value = (*sequence++ << 8) + *sequence++;
* @param length IN/OUT: the length of <code>sequence</code>
* @param value OUT: the value read from the sequence
*/
- inline void unpackInt24(uint8_t*& sequence, size_t& length, int& value) {
+ inline void unpackInt24(const uint8_t*& sequence, size_t& length,
+ int& value) {
if (length < 3)
throw ReSerializationLengthException(length, 3, this);
value = (*sequence++ << 16) + (*sequence++ << 8) + *sequence++;
* @param length IN/OUT: the length of <code>sequence</code>
* @param value OUT: the value read from the sequence
*/
- inline void unpackInt32(uint8_t*& sequence, size_t& length, int& value) {
+ inline void unpackInt32(const uint8_t*& sequence, size_t& length,
+ int& value) {
if (length < 4)
throw ReSerializationLengthException(length, 4, this);
value = (*sequence++ << 24) + (*sequence++ << 16) + (*sequence++ << 8)
* @param length IN/OUT: the length of <code>sequence</code>
* @param value OUT: the value read from the sequence
*/
- inline void unpackInt64(uint8_t*& sequence, size_t& length,
+ inline void unpackInt64(const uint8_t*& sequence, size_t& length,
int64_t& value) {
if (length < 8)
throw ReSerializationLengthException(length, 8, this);
* @param length IN/OUT: the length of <code>sequence</code>
* @param value OUT: the value read from the sequence
*/
- inline void unpackString255(uint8_t*& sequence, size_t& length,
+ inline void unpackString255(const uint8_t*& sequence, size_t& length,
ReByteBuffer& value) {
size_t strLen = 0;
if (length < 1 || (strLen = *sequence) > length)
throw ReSerializationLengthException(length, 1 + strLen, this);
- value.set(reinterpret_cast<char*>(sequence + 1), strLen);
+ value.set(reinterpret_cast<const char*>(sequence + 1), strLen);
strLen += 1;
sequence += strLen;
length -= strLen;
* @param length IN/OUT: the length of <code>sequence</code>
* @param value OUT: the value read from the sequence
*/
- inline void unpackString64k(uint8_t*& sequence, size_t& length,
+ inline void unpackString64k(const uint8_t*& sequence, size_t& length,
ReByteBuffer& value) {
size_t strLen = 0;
if (length < 2 || (strLen = (sequence[0] << 8) + sequence[1]) > length)
throw ReSerializationLengthException(length, 2 + strLen, this);
- value.set(reinterpret_cast<char*>(sequence + 2), strLen);
+ value.set(reinterpret_cast<const char*>(sequence + 2), strLen);
strLen += 2;
length -= strLen;
sequence += strLen;
* @param length IN/OUT: the length of <code>sequence</code>
* @param value OUT: the value read from the sequence
*/
- inline void unpackString4t(uint8_t*& sequence, size_t& length,
+ inline void unpackString4t(const uint8_t*& sequence, size_t& length,
ReByteBuffer& value) {
size_t strLen = 0;
if (length < 4
|| (strLen = (sequence[0] << 24) + (sequence[1] << 16)
+ (sequence[2] << 8) + sequence[3]) > length)
throw ReSerializationLengthException(length, 4 + strLen, this);
- value.set(reinterpret_cast<char*>(sequence + 4), strLen);
+ value.set(reinterpret_cast<const char*>(sequence + 4), strLen);
strLen += 4;
length -= strLen;
sequence += strLen;
/*
* 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
*/
/*
* 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
*/
/*
* 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
*/
/*
* 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
*/
/*
* 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"
/*
* 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
*/
-/*\r
- * ReThreadStarter.cpp\r
- *\r
- * License: Public domain\r
- * Do what you want.\r
- * No warranties and disclaimer of any damages.\r
- * The latest sources: https://github.com/republib\r
- */\r
-\r
-#include "base/rebase.hpp"\r
-\r
-enum RELOC_HASHLIST {\r
- LC_PREPARE_TO_RUN_1 = LC_THREAD + 1, // 50601\r
- LC_START_THREAD_1, // 50602\r
- LC_INSERT_THREAD_1, // 50603\r
- LC_KILL_ALL_THREADS_1, // 50604\r
- LC_KILL_ALL_THREADS_2, // 50605\r
- LC_MUTEX_THREADS, // 50606\r
-};\r
-\r
-/**\r
- * Constructor.\r
- *\r
- * @param autodelete <code>true</code>: the <code>ReThreadStarter</code>\r
- * deletes the instance when it is stopped\r
- */\r
-ReThread::ReThread(bool autoDelete) :\r
- m_threadId(-1),\r
- m_threadLogger(false),\r
- m_appender(NULL),\r
- m_pool(NULL),\r
-#if defined __linux__\r
- m_threadInfo(),\r
-#elif defined __WIN32__\r
- m_threadInfo(NULL),\r
- m_osThreadId(0),\r
-#endif\r
- m_shouldStop(false),\r
- m_isStopped(false),\r
- m_autoDelete(autoDelete) {\r
-#if defined __linux__\r
- memset(&m_threadInfo, 0, sizeof m_threadInfo);\r
-#endif\r
-}\r
-/**\r
- * Destructor.\r
- */\r
-ReThread::~ReThread() {\r
- delete m_appender;\r
- m_appender = NULL;\r
-}\r
-\r
-/**\r
- * Returns the logger.\r
- *\r
- * @return the logger\r
- */\r
-ReLogger* ReThread::logger() {\r
- return &m_threadLogger;\r
-}\r
-\r
-/**\r
- * Kills the thread.\r
- *\r
- * Note: This is very oppressive!\r
- * The thread starter uses that only if <code>setShouldStop(true)</code>\r
- * has no success.\r
- */\r
-void ReThread::kill() {\r
-#if defined __linux__\r
- pthread_kill(m_threadInfo, SIGKILL);\r
-#elif defined __WIN32__\r
- TerminateThread(m_threadInfo, 254);\r
-#endif\r
-}\r
-/**\r
- * Prepares the thread for running.\r
- *\r
- * Should only called by <code>ReThredStarter()</code>\r
- *\r
- * @param id the thread id\r
- * @param masterLogger the logger for error handling\r
- * @param pool the instance which has started the thread\r
- */\r
-bool ReThread::prepareToRun(int id, ReLogger* masterLogger,\r
- ReThreadPool* pool) {\r
- bool rc = false;\r
- if (m_pool != NULL) {\r
- ReLogger* current =\r
- masterLogger == NULL ? globalLogger() : masterLogger;\r
- current->sayF(LOG_ERROR | CAT_LIB, LC_PREPARE_TO_RUN_1,\r
- i18n("prepareToRun($1) is called multiple times")).arg(id).end();\r
- } else {\r
- m_threadId = id;\r
- if (m_appender != NULL) {\r
- m_appender->setMasterLogger(masterLogger);\r
- } else {\r
- m_appender = new ReSlaveAppender(masterLogger,\r
- '@' + (id % ('z' - '@' + 1)));\r
- m_threadLogger.addAppender(m_appender);\r
- }\r
- m_pool = pool;\r
- rc = true;\r
- }\r
- return rc;\r
-}\r
-/**\r
- * Runs the task and exits the thread.\r
- */\r
-void ReThread::runAndFinish() {\r
- run();\r
- m_isStopped = true;\r
-#if defined __linux__\r
- pthread_exit(NULL);\r
-#elif defined __WIN32__\r
- // Nothing to do\r
-#endif\r
-}\r
-\r
-/**\r
- * Constructor.\r
- *\r
- * @param processor the instance that performs the actual task\r
- */\r
-ReSimpleThread::ReSimpleThread(ReProcessor* processor) :\r
- ReThread(true),\r
- m_processor(processor) {\r
-}\r
-/**\r
- * Destructor.\r
- */\r
-ReSimpleThread::~ReSimpleThread() {\r
-}\r
-/**\r
- * This method does the actual task.\r
- */\r
-void ReSimpleThread::run() {\r
- m_processor->process();\r
- m_isStopped = true;\r
-}\r
-\r
-/**\r
- * Constructor.\r
- *\r
- * @param maxThreads the maximal number of threads\r
- * @param logger the (master) logger for error handling\r
- */\r
-ReThreadPool::ReThreadPool(int maxThreads, ReLogger* logger) :\r
- m_nextId(0),\r
- m_logger(logger),\r
- m_maxThreads(maxThreads),\r
- m_maxKillTimeSec(3),\r
- m_mutexThreads(LC_MUTEX_THREADS) {\r
- m_threads = new ReThread*[maxThreads];\r
- memset(m_threads, 0, maxThreads * sizeof *m_threads);\r
-}\r
-\r
-/**\r
- * Destructor.\r
- */\r
-ReThreadPool::~ReThreadPool() {\r
- killAllThreads();\r
-}\r
-\r
-/**\r
- * Inserts a thread into the thread list.\r
- *\r
- * @param thread thread to insert\r
- * @return <code>true</code>: success<br>\r
- * <code>false</code>: too much threads (no space in list)\r
- */\r
-bool ReThreadPool::insertThread(ReThread* thread) {\r
- bool found = false;\r
- m_mutexThreads.lock();\r
- for (int ii = 0; ii < m_maxThreads; ii++) {\r
- ReThread* current = m_threads[ii];\r
- if (current == NULL) {\r
- m_threads[ii] = thread;\r
- found = true;\r
- break;\r
- } else if (current->m_isStopped) {\r
- if (current->m_autoDelete)\r
- delete current;\r
- m_threads[ii] = thread;\r
- found = true;\r
- }\r
- }\r
- m_mutexThreads.unlock();\r
- if (!found)\r
- m_logger->sayF(LOG_ERROR | CAT_PROCESS, LC_INSERT_THREAD_1,\r
- i18n("too much threads: $1")).arg(m_maxThreads).end();\r
-\r
- return found;\r
-}\r
-/**\r
- * Returns the number of active threads.\r
- *\r
- * Inactive threads will be removed.\r
- *\r
- * @return the number of running threads\r
- */\r
-int ReThreadPool::countThreads() {\r
- int count = 0;\r
- m_mutexThreads.lock();\r
- for (int ii = 0; ii < m_maxThreads; ii++) {\r
- ReThread* current = m_threads[ii];\r
- if (current != NULL) {\r
- if (!current->m_isStopped)\r
- count++;\r
- else {\r
- if (current->m_autoDelete)\r
- delete current;\r
- m_threads[ii] = NULL;\r
- }\r
- }\r
- }\r
- m_mutexThreads.unlock();\r
- return count;\r
-}\r
-\r
-/**\r
- * Stopps all running threads.\r
- */\r
-void ReThreadPool::killAllThreads() {\r
- // Orders stop for all threads:\r
- int countWaiting = 0;\r
- m_mutexThreads.lock();\r
- for (int ii = 0; ii < m_maxThreads; ii++) {\r
- ReThread* current = m_threads[ii];\r
- if (current != NULL && !current->m_isStopped) {\r
- countWaiting++;\r
- current->setShouldStop(true);\r
- }\r
- }\r
- m_mutexThreads.unlock();\r
- if (countWaiting > 0)\r
- m_logger->sayF(LOG_INFO | CAT_PROCESS, LC_KILL_ALL_THREADS_1,\r
- i18n("$1 thread(s) still running")).arg(countWaiting).end();\r
-\r
- // we observe the thread stopping:\r
- for (int wait = 0; countWaiting > 0 && wait <= m_maxKillTimeSec; wait++) {\r
- m_mutexThreads.lock();\r
- for (int ii = 0; ii < m_maxThreads; ii++) {\r
- ReThread* current = m_threads[ii];\r
- if (current != NULL && current->m_isStopped) {\r
- countWaiting--;\r
- }\r
- }\r
- m_mutexThreads.unlock();\r
- if (countWaiting > 0)\r
- millisecSleep(1000);\r
- }\r
- // now we kill:\r
- countWaiting = 0;\r
- for (int ii = 0; ii < m_maxThreads; ii++) {\r
- m_mutexThreads.lock();\r
- ReThread* current = m_threads[ii];\r
- if (current == NULL || current->m_isStopped)\r
- m_mutexThreads.unlock();\r
- else {\r
- current->kill();\r
- int id = current->m_threadId;\r
- m_mutexThreads.unlock();\r
- m_logger->sayF(LOG_WARNING | CAT_PROCESS, LC_KILL_ALL_THREADS_2,\r
- i18n("thread $1 must be killed")).arg(id).end();\r
- countWaiting++;\r
- }\r
- }\r
- if (countWaiting > 0)\r
- // wait 1 msec for end of kill:\r
- millisecSleep(1);\r
- // we destroy all threads marked with auto delete:\r
- m_mutexThreads.lock();\r
- for (int ii = 0; ii < m_maxThreads; ii++) {\r
- ReThread* current = m_threads[ii];\r
- if (current != NULL && current->m_autoDelete) {\r
- delete current;\r
- m_threads[ii] = NULL;\r
- }\r
- }\r
- m_mutexThreads.unlock();\r
-}\r
-\r
-/**\r
- * Starts a thread with very few prerequisites.\r
- *\r
- * Note: this method is threadsafe.\r
- *\r
- * @param processor the instance doing the actual task\r
- * @return <code>true</code>: successful\r
- */\r
-bool ReThreadPool::startSimpleThread(ReProcessor& processor) {\r
- // auto delete: the thread starter frees the instance\r
- ReSimpleThread* thread = new ReSimpleThread(&processor);\r
- bool rc = startThread(thread);\r
- return rc;\r
-}\r
-\r
-#if defined __linux__\r
-/**\r
- * The start routine of pthread_start\r
- *\r
- * This will handle the connection for each client (in an own thread).\r
- *\r
- * @param pConnection a void* pointer to the <code>ReThread</code> instance\r
- * */\r
-void* globalThreadStarterFunction(void *pConnection) {\r
- ReThread* thread = reinterpret_cast<ReThread*>(pConnection);\r
- thread->runAndFinish();\r
- return NULL;\r
-}\r
-#elif defined __WIN32__\r
-DWORD WINAPI globalThreadStarterFunction(_In_ LPVOID pConnection) {\r
- ReThread* thread = reinterpret_cast<ReThread*>(pConnection);\r
- thread->runAndFinish();\r
- return 0;\r
-}\r
-\r
-#endif\r
-\r
-/**\r
- * Starts a new thread.\r
- *\r
- * Note: this method is threadsafe.\r
- *\r
- * @param thread the\r
- */\r
-bool ReThreadPool::startThread(ReThread* thread) {\r
- bool ok = false;\r
- if (!insertThread(thread)) {\r
- if (thread->m_autoDelete) {\r
- delete thread;\r
- }\r
- } else {\r
- if (thread->prepareToRun(++m_nextId, m_logger, this)) {\r
-#if defined __linux__\r
- ok = pthread_create(&thread->m_threadInfo, NULL,\r
- globalThreadStarterFunction, reinterpret_cast<void*>(thread))\r
- >= 0;\r
-#elif defined __WIN32__\r
- ok = (thread->m_threadInfo = CreateThread(NULL, 0, globalThreadStarterFunction,\r
- reinterpret_cast<void*>(thread), 0, &thread->m_osThreadId)) != NULL;\r
-#endif\r
- if (!ok)\r
- m_logger->sayF(LOG_ERROR | CAT_PROCESS, LC_START_THREAD_1,\r
- i18n("cannot create a thread: $1")).arg(getLastOSError())\r
- .end();\r
- }\r
- }\r
- return ok;\r
-}\r
-\r
-/**\r
- * Waits for the end of all threads.\r
- *\r
- * Note: this method is threadsafe.\r
- *\r
- * @param timeoutSec maximal time for waiting\r
- * @return <code>true</code>: all threads are finished<br>\r
- * <code>false</code>: timeout reached\r
- */\r
-bool ReThreadPool::waitForAlmostAll(int mayResist, int timeoutSec) {\r
- bool rc = false;\r
- time_t start = time(NULL);\r
- time_t now;\r
- do {\r
- int countWaiting = 0;\r
- m_mutexThreads.lock();\r
- for (int ii = 0; ii < m_maxThreads; ii++) {\r
- ReThread* current = m_threads[ii];\r
- if (current != NULL && !current->m_isStopped) {\r
- countWaiting++;\r
- }\r
- }\r
- m_mutexThreads.unlock();\r
- if (countWaiting <= mayResist) {\r
- rc = true;\r
- break;\r
- }\r
- millisecSleep(200);\r
- now = time(NULL);\r
- } while (now < start + timeoutSec);\r
- return rc;\r
-}\r
-/**\r
- * Waits for the end of all threads.\r
- *\r
- * Note: this method is threadsafe.\r
- *\r
- * @param timeoutSec maximal time for waiting\r
- * @return <code>true</code>: all threads are finished<br>\r
- * <code>false</code>: timeout reached\r
- */\r
-bool ReThreadPool::waitForDone(int timeoutSec) {\r
- bool rc = waitForAlmostAll(0, timeoutSec);\r
- return rc;\r
-}\r
+/*
+ * ReThread.cpp
+ *
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
+ * Do what you want.
+ * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
+ * The latest sources: https://github.com/republib
+ */
+
+#include "base/rebase.hpp"
+
+enum RELOC_HASHLIST {
+ LC_PREPARE_TO_RUN_1 = LOC_FIRST_OF(LOC_THREAD), // 50601
+ LC_START_THREAD_1, // 50602
+ LC_INSERT_THREAD_1, // 50603
+ LC_KILL_ALL_THREADS_1, // 50604
+ LC_KILL_ALL_THREADS_2, // 50605
+ LC_MUTEX_THREADS, // 50606
+};
+
+/**
+ * Constructor.
+ *
+ * @param autodelete <code>true</code>: the <code>ReThreadStarter</code>
+ * deletes the instance when it is stopped
+ */
+ReThread::ReThread(bool autoDelete) :
+ m_threadId(-1),
+ m_threadLogger(false),
+ m_appender(NULL),
+ m_pool(NULL),
+#if defined __linux__
+ m_threadInfo(),
+#elif defined __WIN32__
+ m_threadInfo(NULL),
+ m_osThreadId(0),
+#endif
+ m_shouldStop(false),
+ m_isStopped(false),
+ m_autoDelete(autoDelete) {
+#if defined __linux__
+ memset(&m_threadInfo, 0, sizeof m_threadInfo);
+#endif
+}
+/**
+ * Destructor.
+ */
+ReThread::~ReThread() {
+ delete m_appender;
+ m_appender = NULL;
+}
+
+/**
+ * Returns the logger.
+ *
+ * @return the logger
+ */
+ReLogger* ReThread::logger() {
+ return &m_threadLogger;
+}
+
+/**
+ * Kills the thread.
+ *
+ * Note: This is very oppressive!
+ * The thread starter uses that only if <code>setShouldStop(true)</code>
+ * has no success.
+ */
+void ReThread::kill() {
+#if defined __linux__
+ pthread_kill(m_threadInfo, SIGKILL);
+#elif defined __WIN32__
+ TerminateThread(m_threadInfo, 254);
+#endif
+}
+/**
+ * Prepares the thread for running.
+ *
+ * Should only called by <code>ReThredStarter()</code>
+ *
+ * @param id the thread id
+ * @param masterLogger the logger for error handling
+ * @param pool the instance which has started the thread
+ */
+bool ReThread::prepareToRun(int id, ReLogger* masterLogger,
+ ReThreadPool* pool) {
+ bool rc = false;
+ if (m_pool != NULL) {
+ ReLogger* current =
+ masterLogger == NULL ? globalLogger() : masterLogger;
+ current->sayF(LOG_ERROR | CAT_LIB, LC_PREPARE_TO_RUN_1,
+ i18n("prepareToRun($1) is called multiple times")).arg(id).end();
+ } else {
+ m_threadId = id;
+ if (m_appender != NULL) {
+ m_appender->setMasterLogger(masterLogger);
+ } else {
+ m_appender = new ReSlaveAppender(masterLogger,
+ '@' + (id % ('z' - '@' + 1)));
+ m_threadLogger.addAppender(m_appender);
+ }
+ m_pool = pool;
+ rc = true;
+ }
+ return rc;
+}
+/**
+ * Runs the task and exits the thread.
+ */
+void ReThread::runAndFinish() {
+ run();
+ m_isStopped = true;
+#if defined __linux__
+ pthread_exit(NULL);
+#elif defined __WIN32__
+ // Nothing to do
+#endif
+}
+
+/**
+ * Constructor.
+ *
+ * @param processor the instance that performs the actual task
+ */
+ReSimpleThread::ReSimpleThread(ReProcessor* processor) :
+ ReThread(true),
+ m_processor(processor) {
+}
+/**
+ * Destructor.
+ */
+ReSimpleThread::~ReSimpleThread() {
+}
+/**
+ * This method does the actual task.
+ */
+void ReSimpleThread::run() {
+ m_processor->process();
+ m_isStopped = true;
+}
+
+/**
+ * Constructor.
+ *
+ * @param maxThreads the maximal number of threads
+ * @param logger the (master) logger for error handling
+ */
+ReThreadPool::ReThreadPool(int maxThreads, ReLogger* logger) :
+ m_nextId(0),
+ m_logger(logger),
+ m_maxThreads(maxThreads),
+ m_maxKillTimeSec(3),
+ m_mutexThreads(LC_MUTEX_THREADS) {
+ m_threads = new ReThread*[maxThreads];
+ memset(m_threads, 0, maxThreads * sizeof *m_threads);
+}
+
+/**
+ * Destructor.
+ */
+ReThreadPool::~ReThreadPool() {
+ killAllThreads();
+}
+
+/**
+ * Inserts a thread into the thread list.
+ *
+ * @param thread thread to insert
+ * @return <code>true</code>: success<br>
+ * <code>false</code>: too much threads (no space in list)
+ */
+bool ReThreadPool::insertThread(ReThread* thread) {
+ bool found = false;
+ m_mutexThreads.lock();
+ for (int ii = 0; ii < m_maxThreads; ii++) {
+ ReThread* current = m_threads[ii];
+ if (current == NULL) {
+ m_threads[ii] = thread;
+ found = true;
+ break;
+ } else if (current->m_isStopped) {
+ if (current->m_autoDelete)
+ delete current;
+ m_threads[ii] = thread;
+ found = true;
+ }
+ }
+ m_mutexThreads.unlock();
+ if (!found)
+ m_logger->sayF(LOG_ERROR | CAT_PROCESS, LC_INSERT_THREAD_1,
+ i18n("too much threads: $1")).arg(m_maxThreads).end();
+
+ return found;
+}
+/**
+ * Returns the number of active threads.
+ *
+ * Inactive threads will be removed.
+ *
+ * @return the number of running threads
+ */
+int ReThreadPool::countThreads() {
+ int count = 0;
+ m_mutexThreads.lock();
+ for (int ii = 0; ii < m_maxThreads; ii++) {
+ ReThread* current = m_threads[ii];
+ if (current != NULL) {
+ if (!current->m_isStopped)
+ count++;
+ else {
+ if (current->m_autoDelete)
+ delete current;
+ m_threads[ii] = NULL;
+ }
+ }
+ }
+ m_mutexThreads.unlock();
+ return count;
+}
+
+/**
+ * Stopps all running threads.
+ */
+void ReThreadPool::killAllThreads() {
+ // Orders stop for all threads:
+ int countWaiting = 0;
+ m_mutexThreads.lock();
+ for (int ii = 0; ii < m_maxThreads; ii++) {
+ ReThread* current = m_threads[ii];
+ if (current != NULL && !current->m_isStopped) {
+ countWaiting++;
+ current->setShouldStop(true);
+ }
+ }
+ m_mutexThreads.unlock();
+ if (countWaiting > 0)
+ m_logger->sayF(LOG_INFO | CAT_PROCESS, LC_KILL_ALL_THREADS_1,
+ i18n("$1 thread(s) still running")).arg(countWaiting).end();
+
+ // we observe the thread stopping:
+ for (int wait = 0; countWaiting > 0 && wait <= m_maxKillTimeSec; wait++) {
+ m_mutexThreads.lock();
+ for (int ii = 0; ii < m_maxThreads; ii++) {
+ ReThread* current = m_threads[ii];
+ if (current != NULL && current->m_isStopped) {
+ countWaiting--;
+ }
+ }
+ m_mutexThreads.unlock();
+ if (countWaiting > 0)
+ millisecSleep(1000);
+ }
+ // now we kill:
+ countWaiting = 0;
+ for (int ii = 0; ii < m_maxThreads; ii++) {
+ m_mutexThreads.lock();
+ ReThread* current = m_threads[ii];
+ if (current == NULL || current->m_isStopped)
+ m_mutexThreads.unlock();
+ else {
+ current->kill();
+ int id = current->m_threadId;
+ m_mutexThreads.unlock();
+ m_logger->sayF(LOG_WARNING | CAT_PROCESS, LC_KILL_ALL_THREADS_2,
+ i18n("thread $1 must be killed")).arg(id).end();
+ countWaiting++;
+ }
+ }
+ if (countWaiting > 0)
+ // wait 1 msec for end of kill:
+ millisecSleep(1);
+ // we destroy all threads marked with auto delete:
+ m_mutexThreads.lock();
+ for (int ii = 0; ii < m_maxThreads; ii++) {
+ ReThread* current = m_threads[ii];
+ if (current != NULL && current->m_autoDelete) {
+ delete current;
+ m_threads[ii] = NULL;
+ }
+ }
+ m_mutexThreads.unlock();
+}
+
+/**
+ * Starts a thread with very few prerequisites.
+ *
+ * Note: this method is threadsafe.
+ *
+ * @param processor the instance doing the actual task
+ * @return <code>true</code>: successful
+ */
+bool ReThreadPool::startSimpleThread(ReProcessor& processor) {
+ // auto delete: the thread starter frees the instance
+ ReSimpleThread* thread = new ReSimpleThread(&processor);
+ bool rc = startThread(thread);
+ return rc;
+}
+
+#if defined __linux__
+/**
+ * The start routine of pthread_start
+ *
+ * This will handle the connection for each client (in an own thread).
+ *
+ * @param pConnection a void* pointer to the <code>ReThread</code> instance
+ * */
+void* globalThreadStarterFunction(void *pConnection) {
+ ReThread* thread = reinterpret_cast<ReThread*>(pConnection);
+ thread->runAndFinish();
+ return NULL;
+}
+#elif defined __WIN32__
+DWORD WINAPI globalThreadStarterFunction(_In_ LPVOID pConnection) {
+ ReThread* thread = reinterpret_cast<ReThread*>(pConnection);
+ thread->runAndFinish();
+ return 0;
+}
+
+#endif
+
+/**
+ * Starts a new thread.
+ *
+ * Note: this method is threadsafe.
+ *
+ * @param thread the
+ */
+bool ReThreadPool::startThread(ReThread* thread) {
+ bool ok = false;
+ if (!insertThread(thread)) {
+ if (thread->m_autoDelete) {
+ delete thread;
+ }
+ } else {
+ if (thread->prepareToRun(++m_nextId, m_logger, this)) {
+#if defined __linux__
+ ok = pthread_create(&thread->m_threadInfo, NULL,
+ globalThreadStarterFunction, reinterpret_cast<void*>(thread))
+ >= 0;
+#elif defined __WIN32__
+ ok = (thread->m_threadInfo = CreateThread(NULL, 0, globalThreadStarterFunction,
+ reinterpret_cast<void*>(thread), 0, &thread->m_osThreadId)) != NULL;
+#endif
+ if (!ok)
+ m_logger->sayF(LOG_ERROR | CAT_PROCESS, LC_START_THREAD_1,
+ i18n("cannot create a thread: $1")).arg(getLastOSError())
+ .end();
+ }
+ }
+ return ok;
+}
+
+/**
+ * Waits for the end of all threads.
+ *
+ * Note: this method is threadsafe.
+ *
+ * @param timeoutSec maximal time for waiting
+ * @return <code>true</code>: all threads are finished<br>
+ * <code>false</code>: timeout reached
+ */
+bool ReThreadPool::waitForAlmostAll(int mayResist, int timeoutSec) {
+ bool rc = false;
+ time_t start = time(NULL);
+ time_t now;
+ do {
+ int countWaiting = 0;
+ m_mutexThreads.lock();
+ for (int ii = 0; ii < m_maxThreads; ii++) {
+ ReThread* current = m_threads[ii];
+ if (current != NULL && !current->m_isStopped) {
+ countWaiting++;
+ }
+ }
+ m_mutexThreads.unlock();
+ if (countWaiting <= mayResist) {
+ rc = true;
+ break;
+ }
+ millisecSleep(200);
+ now = time(NULL);
+ } while (now < start + timeoutSec);
+ return rc;
+}
+/**
+ * Waits for the end of all threads.
+ *
+ * Note: this method is threadsafe.
+ *
+ * @param timeoutSec maximal time for waiting
+ * @return <code>true</code>: all threads are finished<br>
+ * <code>false</code>: timeout reached
+ */
+bool ReThreadPool::waitForDone(int timeoutSec) {
+ bool rc = waitForAlmostAll(0, timeoutSec);
+ return rc;
+}
-/*\r
- * ReThreadStarter.hpp\r
- *\r
- * License: Public domain\r
- * Do what you want.\r
- * No warranties and disclaimer of any damages.\r
- * The latest sources: https://github.com/republib\r
- */\r
-\r
-#ifndef BASE_RETHREAD_HPP_\r
-#define BASE_RETHREAD_HPP_\r
-\r
-class ReThreadPool;\r
-class ReSlaveAppender;\r
-/**\r
- * Abstract base class for threads\r
- *\r
- * Starting is done with a <code>ReThreadStarter</code>.\r
- */\r
-class ReThread: public ReLoggerOwner {\r
-public:\r
- ReThread(bool autoDelete);\r
- virtual ~ReThread();\r
-public:\r
- /** Does the actual task.\r
- * This method must do a <code>m_isStopped = true;</code> at the end!\r
- */\r
- virtual void run() = 0;\r
- /** Returns if the thread is stopped.\r
- * @return <code>true</code>the thread is stopped\r
- */\r
- inline bool isStopped() const {\r
- return m_isStopped;\r
- }\r
- virtual ReLogger* logger();\r
- /** Sets the wish for stopping the thread.\r
- * @param value <code>true</code>: the thread should stop as soon as possible\r
- */\r
- inline void setShouldStop(bool value) {\r
- m_shouldStop = value;\r
- }\r
-private:\r
- friend class ReThreadPool;\r
- void kill();\r
- bool prepareToRun(int id, ReLogger* masterLogger, ReThreadPool* starter);\r
-#if defined __linux__\r
- friend void* globalThreadStarterFunction(void *pConnection);\r
-#elif defined __WIN32__\r
- friend DWORD WINAPI globalThreadStarterFunction(_In_ LPVOID pParameter);\r
-#endif\r
- void runAndFinish();\r
-protected:\r
- int m_threadId;\r
- ReLogger m_threadLogger;\r
- ReSlaveAppender* m_appender;\r
- ReThreadPool* m_pool;\r
-#if defined __linux__\r
- pthread_t m_threadInfo;\r
-#elif defined __WIN32__\r
- HANDLE m_threadInfo;\r
- DWORD m_osThreadId;\r
-#endif\r
- bool m_shouldStop;\r
- bool m_isStopped;\r
- bool m_autoDelete;\r
-};\r
-\r
-class ReSimpleThread: public ReThread {\r
-public:\r
- ReSimpleThread(ReProcessor* processor);\r
- virtual ~ReSimpleThread();\r
-public:\r
- virtual void run();\r
-private:\r
- ReProcessor* m_processor;\r
-};\r
-/**\r
- * Offers a portable way to start threads.\r
- */\r
-class ReThreadPool {\r
-public:\r
- ReThreadPool(int maxThreads, ReLogger* logger);\r
- virtual ~ReThreadPool();\r
-public:\r
- int countThreads();\r
- void killAllThreads();\r
- bool startSimpleThread(ReProcessor& processor);\r
- bool startThread(ReThread* thread);\r
- bool waitForAlmostAll(int mayResist, int timeoutSec);\r
- bool waitForDone(int timeoutSec);\r
- /** Returns whether the threads should stop.\r
- * @return <code>true</code>: all threads should stop\r
- */\r
- inline bool shouldStop() const {\r
- return m_shouldStop;\r
- }\r
- /** Sets that the threads should stop.\r
- */\r
- inline void setShouldStop() {\r
- m_shouldStop = true;\r
- }\r
-private:\r
- bool insertThread(ReThread* thread);\r
-private:\r
- int m_nextId;\r
- ReLogger* m_logger;\r
- int m_maxThreads;\r
- ReThread** m_threads;\r
- int m_maxKillTimeSec;\r
- ReMutex m_mutexThreads;\r
- bool m_shouldStop;\r
-};\r
-#endif /* BASE_RETHREAD_HPP_ */\r
+/*
+ * ReThread.hpp
+ *
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
+ * Do what you want.
+ * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
+ * The latest sources: https://github.com/republib
+ */
+
+#ifndef BASE_RETHREAD_HPP_
+#define BASE_RETHREAD_HPP_
+
+class ReThreadPool;
+class ReSlaveAppender;
+/**
+ * Abstract base class for threads
+ *
+ * Starting is done with a <code>ReThreadStarter</code>.
+ */
+class ReThread: public ReLoggerOwner {
+public:
+ ReThread(bool autoDelete);
+ virtual ~ReThread();
+public:
+ /** Does the actual task.
+ * This method must do a <code>m_isStopped = true;</code> at the end!
+ */
+ virtual void run() = 0;
+ /** Returns if the thread is stopped.
+ * @return <code>true</code>the thread is stopped
+ */
+ inline bool isStopped() const {
+ return m_isStopped;
+ }
+ virtual ReLogger* logger();
+ /** Sets the wish for stopping the thread.
+ * @param value <code>true</code>: the thread should stop as soon as possible
+ */
+ inline void setShouldStop(bool value) {
+ m_shouldStop = value;
+ }
+private:
+ friend class ReThreadPool;
+ void kill();
+ bool prepareToRun(int id, ReLogger* masterLogger, ReThreadPool* starter);
+#if defined __linux__
+ friend void* globalThreadStarterFunction(void *pConnection);
+#elif defined __WIN32__
+ friend DWORD WINAPI globalThreadStarterFunction(_In_ LPVOID pParameter);
+#endif
+ void runAndFinish();
+protected:
+ int m_threadId;
+ ReLogger m_threadLogger;
+ ReSlaveAppender* m_appender;
+ ReThreadPool* m_pool;
+#if defined __linux__
+ pthread_t m_threadInfo;
+#elif defined __WIN32__
+ HANDLE m_threadInfo;
+ DWORD m_osThreadId;
+#endif
+ bool m_shouldStop;
+ bool m_isStopped;
+ bool m_autoDelete;
+};
+
+class ReSimpleThread: public ReThread {
+public:
+ ReSimpleThread(ReProcessor* processor);
+ virtual ~ReSimpleThread();
+public:
+ virtual void run();
+private:
+ ReProcessor* m_processor;
+};
+/**
+ * Offers a portable way to start threads.
+ */
+class ReThreadPool {
+public:
+ ReThreadPool(int maxThreads, ReLogger* logger);
+ virtual ~ReThreadPool();
+public:
+ int countThreads();
+ void killAllThreads();
+ bool startSimpleThread(ReProcessor& processor);
+ bool startThread(ReThread* thread);
+ bool waitForAlmostAll(int mayResist, int timeoutSec);
+ bool waitForDone(int timeoutSec);
+ /** Returns whether the threads should stop.
+ * @return <code>true</code>: all threads should stop
+ */
+ inline bool shouldStop() const {
+ return m_shouldStop;
+ }
+ /** Sets that the threads should stop.
+ */
+ inline void setShouldStop() {
+ m_shouldStop = true;
+ }
+private:
+ bool insertThread(ReThread* thread);
+private:
+ int m_nextId;
+ ReLogger* m_logger;
+ int m_maxThreads;
+ ReThread** m_threads;
+ int m_maxKillTimeSec;
+ ReMutex m_mutexThreads;
+ bool m_shouldStop;
+};
+#endif /* BASE_RETHREAD_HPP_ */
/*
* 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
*/
/**
/*
* 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
*/
/*
* baselocations.hpp
- *
- * License: Public domain
+ *
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
* Do what you want.
* No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
* The latest sources: https://github.com/republib
*/
/** The files of the REal PUBlic LIB use the range from 50000 until 99999.
*/
-enum RELOC_LIB {
- LC_CONFIGFILE = 50000,
- LC_DIRTOOLS = 50100,
- LC_SEQARRAY = 50200,
- LC_HASHLIST = 50300,
- LC_TRAVERSER = 50400,
- LC_TCP = 50500,
- LC_THREAD = 50600,
- LC_LOGGER = 50700,
-};
-enum RELOC_UDPCONNECTION {
- LC_UDPCONNECTION_CONSTRUCT = 50101,
- LC_UDPCONNECTION_RECEIVE_1 = 50102,
- LC_UDPCONNECTION_RECEIVE_2 = 50103,
- LC_UDPCONNECTION_SEND_1 = 50104,
- LC_UDPCONNECTION_SEND_2 = 50105,
- LC_UDPCONNECTION_CLOSE_1 = 50106,
- LC_UDPCONNECTION_CLOSE_2 = 50107,
- LC_UDPCONNECTION_CONNECT_1 = 50108,
- LC_UDPCONNECTION_CONNECT_2 = 50109,
- LC_UDPCONNECTION_RUN_1 = 50110,
-};
-enum RELOC_LOGGER {
- LC_LOGGER_SAYF_OPEN = 50201,
-};
-enum RELOC_CSTRING {
- LC_CSTRING_REPLACE_1 = 50301,
+
+enum ReModules_t {
+ LOC_CONFIGFILE = 500,
+ LOC_DIRTOOLS,
+ LOC_SEQARRAY,
+ LOC_HASHLIST,
+ LOC_TRAVERSER,
+ LOC_TCP, /*505*/
+ LOC_THREAD,
+ LOC_LOGGER,
+ LOC_UDP,
+ LOC_CSTRING,
};
+#define LOC_FIRST_OF(moduleNo) (moduleNo*100+1)
+
#endif /* BASELOCATIONS_HPP_ */
-/*\r
- * rebase.hpp\r
- *\r
- * License: Public domain\r
- * Do what you want.\r
- * No warranties and disclaimer of any damages.\r
- * The latest sources: https://github.com/republib\r
- */\r
-#ifndef REBASE_HPP_\r
-#define min()\r
-#define max()\r
-#define REBASE_HPP_\r
-#include <stdlib.h>\r
-#include <string.h>\r
-#include <malloc.h>\r
-#include <stdio.h>\r
-#include <sys/types.h>\r
-#include <sys/stat.h>\r
-#include <time.h>\r
-#include <errno.h>\r
-#include <ctype.h>\r
-#include <assert.h>\r
-#include <stdarg.h>\r
-#include <limits.h>\r
-\r
-#define __LITTLE_ENDIAN__\r
-//#define __BIG_ENDIAN__\r
-\r
-#if defined __linux__\r
-\r
-# include <sys/time.h>\r
-# include <stddef.h>\r
-# include <dirent.h>\r
-# include <fnmatch.h>\r
-# include <regex.h>\r
-# include <unistd.h>\r
-# include <inttypes.h>\r
-# include <fcntl.h>\r
-#include <pthread.h>\r
-#include <semaphore.h>\r
-#include <signal.h>\r
-typedef u_int64_t uint64_t;\r
-typedef u_int8_t uint8_t;\r
-typedef __off_t ReFileSize_t;\r
-typedef timespec ReFileTime_t;\r
-# define _strdup strdup\r
-# define _unlink unlink\r
-# define _strnicmp(s1, s2, n) strncasecmp(s1, s2, n)\r
-# define _stricmp(s1, s2) strcasecmp(s1, s2)\r
-# define _snprintf snprintf\r
-# define _memcmp(t,s,n) memcmp(t,s,n)\r
-# define _mkdir(path, mode) mkdir(path, mode)\r
-# define _rmdir(path) rmdir(path)\r
-# define OS_SEPARATOR_CHAR '/'\r
-# define OS_SEPARATOR "/"\r
-# define millisecSleep(msec) usleep(msec*1000)\r
-inline int getLastOSError() {\r
- return errno;\r
-}\r
-#elif defined __WIN32__\r
-# include <direct.h>\r
-# include <WinSock2.h>\r
-# include <windows.h>\r
-# define _memcmp(t,s,n) memcmp(t,s,n)\r
-# define lstat stat\r
-# define millisecSleep(msec) Sleep(msec)\r
-# define OS_SEPARATOR_CHAR '\\'\r
-# define OS_SEPARATOR "\\"\r
-typedef _int64 int64_t;\r
-typedef unsigned long long uint64_t;\r
-typedef unsigned char uint8_t;\r
-typedef unsigned long uint32_t;\r
-typedef long int int32_t;\r
-typedef int64_t ReFileSize_t;\r
-typedef FILETIME ReFileTime_t;\r
-# define S_ISDIR(mode) (((mode) & _S_IFDIR) != 0)\r
-# define ALLPERMS 0\r
-//# define _mkdir(name, mode) (!CreateDirectory(name, NULL))\r
-# define _mkdir(name, mode) _mkdir(name)\r
-inline int getLastOSError() {\r
- return GetLastError();\r
-}\r
-#endif\r
-\r
-typedef int ReErrNo_t;\r
-/**\r
- * This base class allows to define classes with very common tasks.\r
- *\r
- * Example (a thread starter):\r
- * the class realizing the task is a sub class of <code>ReProcessor</code>.\r
- * The thread starter gets an instance of this task class and calls the\r
- * method <code>process()</code> inside the new thread.\r
- */\r
-class ReProcessor {\r
-public:\r
- /** Destructor.\r
- */\r
- virtual ~ReProcessor() {\r
- }\r
-public:\r
- virtual void process() = 0;\r
-};\r
-#undef min\r
-#undef max\r
-/** Returns the minimum of 2 values.\r
- * @param a first value\r
- * @param b 2nd value\r
- * @return minimum of a and b\r
- */\r
-inline int min(int a, int b) {\r
- return a < b ? a : b;\r
-}\r
-/** Returns the maximum of 2 values.\r
- * @param a first value\r
- * @param b 2nd value\r
- * @return maximum of a and b\r
- */\r
-inline int max(int a, int b) {\r
- return a < b ? a : b;\r
-}\r
-#include "base/ReClassId.hpp"\r
-#include "base/ReException.hpp"\r
-#include "base/ReMutex.hpp"\r
-#include "base/ReByteBuffer.hpp"\r
-#include "base/ReSerializable.hpp"\r
-#include "base/ReVarArgs.hpp"\r
-#include "base/ReLogger.hpp"\r
-#include "base/ReThread.hpp"\r
-#include "base/ReTestUnit.hpp"\r
-#include "base/ReCString.hpp"\r
-#include "base/ReStringUtils.hpp"\r
-#include "base/ReDirectory.hpp"\r
-#include "base/ReSeqArray.hpp"\r
-#include "base/ReAppenders.hpp"\r
-#include "base/ReStringList.hpp"\r
-#include "base/ReHashList.hpp"\r
-#include "base/ReConfigFile.hpp"\r
-#include "base/ReI18N.hpp"\r
-#include "base/ReProgramArgs.hpp"\r
-#include "base/ReBaseUtils.hpp"\r
-\r
-#include "base/baselocations.hpp"\r
-#endif /* REBASE_HPP_ */\r
+/*
+ * rebase.hpp
+ *
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
+ * Do what you want.
+ * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
+ * The latest sources: https://github.com/republib
+ */
+#ifndef REBASE_HPP_
+#define REBASE_HPP_
+#define min()
+#define max()
+#include <stdlib.h>
+#include <string.h>
+#include <malloc.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <errno.h>
+#include <ctype.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <limits.h>
+
+#define __LITTLE_ENDIAN__
+//#define __BIG_ENDIAN__
+
+#if defined __linux__
+
+# include <sys/time.h>
+# include <stddef.h>
+# include <dirent.h>
+# include <fnmatch.h>
+# include <regex.h>
+# include <unistd.h>
+# include <inttypes.h>
+# include <fcntl.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <signal.h>
+typedef u_int64_t uint64_t;
+typedef u_int8_t uint8_t;
+typedef __off_t ReFileSize_t;
+enum ReOSType {
+ OS_UNDEF,
+ OS_LINUX = 'l',
+ OS_WIN32 = 'w'
+};
+typedef struct timespec ReFileTime_t;
+# define _strdup strdup
+# define _unlink unlink
+# define _strnicmp(s1, s2, n) strncasecmp(s1, s2, n)
+# define _stricmp(s1, s2) strcasecmp(s1, s2)
+# define _snprintf snprintf
+# define _memcmp(t,s,n) memcmp(t,s,n)
+# define _mkdir(path, mode) mkdir(path, mode)
+# define _rmdir(path) rmdir(path)
+# define OS_SEPARATOR_CHAR '/'
+# define OS_SEPARATOR "/"
+# define millisecSleep(msec) usleep(msec*1000)
+inline int getLastOSError() {
+ return errno;
+}
+#elif defined __WIN32__
+# include <direct.h>
+# include <WinSock2.h>
+# include <windows.h>
+# define _memcmp(t,s,n) memcmp(t,s,n)
+# define lstat stat
+# define millisecSleep(msec) Sleep(msec)
+# define OS_SEPARATOR_CHAR '\\'
+# define OS_SEPARATOR "\\"
+typedef _int64 int64_t;
+typedef unsigned long long uint64_t;
+typedef unsigned char uint8_t;
+typedef unsigned long uint32_t;
+typedef long int int32_t;
+typedef int64_t ReFileSize_t;
+typedef FILETIME ReFileTime_t;
+# define S_ISDIR(mode) (((mode) & _S_IFDIR) != 0)
+# define ALLPERMS 0
+//# define _mkdir(name, mode) (!CreateDirectory(name, NULL))
+# define _mkdir(name, mode) _mkdir(name)
+inline int getLastOSError() {
+ return GetLastError();
+}
+#endif
+
+typedef int ReErrNo_t;
+/**
+ * This base class allows to define classes with very common tasks.
+ *
+ * Example (a thread starter):
+ * the class realizing the task is a sub class of <code>ReProcessor</code>.
+ * The thread starter gets an instance of this task class and calls the
+ * method <code>process()</code> inside the new thread.
+ */
+class ReProcessor {
+public:
+ /** Destructor.
+ */
+ virtual ~ReProcessor() {
+ }
+public:
+ virtual void process() = 0;
+};
+#undef min
+#undef max
+/** Returns the minimum of 2 values.
+ * @param a first value
+ * @param b 2nd value
+ * @return minimum of a and b
+ */
+inline int min(int a, int b) {
+ return a < b ? a : b;
+}
+/** Returns the maximum of 2 values.
+ * @param a first value
+ * @param b 2nd value
+ * @return maximum of a and b
+ */
+inline int max(int a, int b) {
+ return a < b ? a : b;
+}
+#include "base/ReClassId.hpp"
+#include "base/ReException.hpp"
+#include "base/ReMutex.hpp"
+#include "base/ReByteBuffer.hpp"
+#include "base/ReSerializable.hpp"
+#include "base/ReVarArgs.hpp"
+#include "base/ReLogger.hpp"
+#include "base/ReThread.hpp"
+#include "base/ReTestUnit.hpp"
+#include "base/ReCString.hpp"
+#include "base/ReStringUtils.hpp"
+#include "base/ReDirectory.hpp"
+#include "base/ReSeqArray.hpp"
+#include "base/ReAppenders.hpp"
+#include "base/ReStringList.hpp"
+#include "base/ReHashList.hpp"
+#include "base/ReConfigFile.hpp"
+#include "base/ReI18N.hpp"
+#include "base/ReProgramArgs.hpp"
+#include "base/ReBaseUtils.hpp"
+
+#include "base/baselocations.hpp"
+#endif /* REBASE_HPP_ */
/*
* 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
*/
/*
* 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"
/*
* 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"
/*
* 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"
-/*\r
- * cuReTraverser.cpp\r
- *\r
- * License: Public domain\r
- * Do what you want.\r
- * No warranties and disclaimer of any damages.\r
- * The latest sources: https://github.com/republib\r
- */\r
-\r
-#include "base/rebase.hpp"\r
-#include "os/reos.hpp"\r
-\r
-static const char* s_empty[] = { NULL };\r
-// count of seconds from 1.1.1970 until 1.1.1980:\r
-const int tenYear = (365 * 10 + 2) * 24 * 3600;\r
-\r
-const char* s_allFiles[] = { //\r
- " 1.txt", //\r
- "*dir1", //\r
- "*dir2", //\r
- "*dir1/cache", //\r
- "*dir1/dir1_1", //\r
- "*dir1/dir1_2", //\r
- "*dir1/dir1_2/dir1_2_1", //\r
- " dir1/dir1_2/dir1_2_1/x1.txt", //\r
- " dir1/dir1_2/dir1_2_1/x2.txt", //\r
- " dir2/2.x", //\r
- " dir1/cache/cache.txt", //\r
- NULL };\r
-\r
-class TestReDirTools: public ReTestUnit {\r
-public:\r
- TestReDirTools() :\r
- ReTestUnit("ReDirTools", __FILE__),\r
- // m_base\r
- // m_buffer\r
- m_testAll(true),\r
- m_logger(ReLogger::globalLogger()) {\r
- m_base = testDir();\r
- ReDirectory::deleteTree(m_base.str());\r
- m_base.append("dirtool");\r
- _mkdir(m_base.str(), ALLPERMS);\r
- m_base.append(OS_SEPARATOR, -1);\r
- m_testAll = true;\r
- run();\r
- ReDirectory::deleteTree(m_base.str(), true);\r
- }\r
-private:\r
- ReByteBuffer m_base;\r
- ReByteBuffer m_buffer;\r
- bool m_testAll;\r
- ReLogger* m_logger;\r
-private:\r
- void run() {\r
- testRandom();\r
- testDelete();\r
- testChecksumBuild();\r
- if (m_testAll) {\r
- testDirOptions();\r
- checkSetFilterFromProgramArgs();\r
- testList2();\r
- testToolStatistic();\r
- testBasic();\r
- testDirStatistic();\r
- testCopyFile();\r
- testList();\r
- testToolSync();\r
- testBatch();\r
- testChecksumBuild();\r
- testDelete();\r
- testRandom();\r
- }\r
- }\r
- void testRandom() {\r
- const char* argv[] = { "random", "-l20", "-s", "40", "50",\r
- NULL };\r
- ReDirRandom(m_logger).run(-1, argv);\r
- }\r
- void testDelete() {\r
- rebuildTree();\r
- ReByteBuffer nameCurrent;\r
- buildFilename("current", nameCurrent);\r
- ReByteBuffer optOutput("--output-file=");\r
- optOutput.append(nameCurrent);\r
- const char* argv[] = { "delete", "-p;x*.txt", optOutput.str(), m_base\r
- .str(), NULL };\r
- const char* existing[] = { //\r
- " 1.txt", //\r
- "*dir1", //\r
- "*dir2", //\r
- "*dir1/cache", //\r
- "*dir1/dir1_1", //\r
- "*dir1/dir1_2", //\r
- "*dir1/dir1_2/dir1_2_1", //\r
- " dir2/2.x", //\r
- " dir1/cache/cache.txt", //\r
- NULL };\r
- const char* notExisting[] = { //\r
- " dir1/dir1_2/dir1_2_1/x1.txt", //\r
- " dir1/dir1_2/dir1_2_1/x2.txt", //\r
- NULL };\r
- ReDirDelete(m_logger).run(-1, argv);\r
- mustExist(existing);\r
- mayNotExist(notExisting);\r
-\r
- const char* argv2[] = { "delete", optOutput.str(), m_base.str(), NULL };\r
- ReDirDelete(m_logger).run(-1, argv2);\r
- mayNotExist(s_allFiles);\r
- }\r
- void mustExist(const char** names) {\r
- ReByteBuffer name;\r
- struct stat info;\r
- for (int ix = 0; names[ix] != NULL; ix++) {\r
- const char* arg = names[ix];\r
- name = m_base;\r
- name.ensureLastChar(OS_SEPARATOR_CHAR);\r
- name.append(arg + 1);\r
- name.replaceAll("/", -1, OS_SEPARATOR, -1);\r
- if (stat(name.str(), &info) != 0)\r
- checkEqu("missing", name);\r
- bool isDir = arg[0] == '*';\r
- if (isDir && !S_ISDIR(info.st_mode))\r
- checkEqu("file not dir", name);\r
- if (!isDir && S_ISDIR(info.st_mode))\r
- checkEqu("dir not file", name);\r
- }\r
- }\r
- void mayNotExist(const char** names) {\r
- ReByteBuffer name;\r
- struct stat info;\r
- for (int ix = 0; names[ix] != NULL; ix++) {\r
- const char* arg = names[ix];\r
- name = m_base;\r
- name.ensureLastChar(OS_SEPARATOR_CHAR);\r
- name.append(arg + 1);\r
- name.replaceAll("/", -1, OS_SEPARATOR, -1);\r
- if (stat(name.str(), &info) == 0)\r
- checkEqu("exists", name);\r
- }\r
- }\r
- int secOfFileTime(ReFileTime_t data) {\r
-#if defined __linux__\r
- return data.tv_sec;\r
-#elif defined __WIN32__\r
- return (int) ReDirStatus_t::filetimeToTime(&data);\r
-#endif\r
- }\r
- const char* makeDir(const char* relPath) {\r
- m_buffer = m_base;\r
- m_buffer.append(relPath);\r
- m_buffer.replaceAll("/", 1, OS_SEPARATOR, -1);\r
- _mkdir(m_buffer.str(), ALLPERMS);\r
- struct stat info;\r
- if (stat(m_buffer.str(), &info) != 0) {\r
- logF(true, "cannot create dir %s", m_buffer.str());\r
- }\r
- return m_buffer.str();\r
- }\r
- void makeFile(const char* relPath) {\r
- ReByteBuffer path(m_base);\r
- path.append(relPath);\r
- path.replaceAll("/", 1, OS_SEPARATOR, -1);\r
- createFile(path.str(), relPath);\r
- struct stat info;\r
- if (stat(path.str(), &info) != 0) {\r
- logF(true, "cannot create file %s", path.str());\r
- }\r
- }\r
- void initTree() {\r
- for (int ix = 0; s_allFiles[ix] != 0; ix++) {\r
- const char* name = s_allFiles[ix];\r
- if (name[0] == '*')\r
- makeDir(name + 1);\r
- else\r
- makeFile(name + 1);\r
- }\r
- }\r
- void rebuildTree() {\r
- ReDirectory::deleteTree(m_base.str(), false);\r
- initTree();\r
- }\r
- void testChecksumBuild() {\r
- rebuildTree();\r
- ReByteBuffer nameCurrent;\r
- buildFilename("current", nameCurrent);\r
- ReByteBuffer optOutput("--output-file=");\r
- optOutput.append(nameCurrent);\r
- const char* argv[] = { "dt", "checksum", "-P;*;-cache", "-tr",\r
- "-p;*.txt", "-s4711", "-arpd64", "-cBuild", optOutput.str(), m_base\r
- .str(), NULL };\r
- ReDirTools tools;\r
- tools.main(-1, (char**) argv);\r
- // checkFile(nameCurrent, "");\r
- const char* argv2[] = { "dt", "checksum", "-P;*;-cache", "-tr",\r
- "-p;*.txt", "-s4711", "-arpd64", "-cCompare", optOutput.str(),\r
- m_base.str(), NULL };\r
- tools.main(-1, (char**) argv2);\r
- }\r
- // F:\temp\retestunit\dirtool\r
- void testList2() {\r
- rebuildTree();\r
- const char* argv[] = { "dt", "list", "-r", m_base.str(), NULL };\r
- ReDirTools tools;\r
- tools.main(4, (char**) argv);\r
- }\r
- void testList() {\r
- rebuildTree();\r
- const char* argv[] = { "list", m_base.str(), NULL };\r
- ReDirList(m_logger).run(2, argv);\r
- }\r
- void testCopyFile() {\r
- rebuildTree();\r
-#if defined __linux__\r
- ReByteBuffer src(m_base);\r
- src.append("dir1/dir1_2/dir1_2_1/x1.txt");\r
- ReByteBuffer trg(testDir());\r
- trg.append("copy_x1.txt");\r
- ReByteBuffer buffer;\r
- buffer.ensureSize(5);\r
- ReDirSync::copyFile(src.str(), NULL, trg.str(), buffer,\r
- ReLogger::globalLogger());\r
- checkFileEqu(src.str(), trg.str());\r
-#else\r
- log(false, "testCopyFile not implemented");\r
-#endif\r
- }\r
-\r
- void checkRelDate(ReFileTime_t absTime, int relTime) {\r
- int diffNow = int(time(NULL) - secOfFileTime(absTime));\r
- int diff =\r
- diffNow * relTime < 0 ? diffNow + relTime : diffNow - relTime;\r
- if (diff < 0)\r
- diff = -diff;\r
- checkT(diff < 2);\r
- }\r
- void testDirOptions() {\r
- class MyOptions: public ReDirOptions {\r
- public:\r
- MyOptions() :\r
- ReDirOptions(s_empty, s_empty) {\r
- }\r
- public:\r
- int count() {\r
- return m_countCompoundUsage;\r
- }\r
- const char** usage() {\r
- return m_compoundUsage;\r
- }\r
- };\r
- static const char* usage1[] = { "line1", "line2", NULL };\r
- static const char* usage2[] = { "x1", "x2", "x3", NULL };\r
- MyOptions opts;\r
- opts.initCompoundUsage(sizeof usage1 + sizeof usage2);\r
- opts.addCompoundUsage(usage1);\r
- opts.addCompoundUsage(usage2);\r
- checkEqu(7, opts.count());\r
- checkEqu("line1", opts.usage()[0]);\r
- checkEqu("line2", opts.usage()[1]);\r
- checkEqu("x1", opts.usage()[2]);\r
- checkEqu("x2", opts.usage()[3]);\r
- checkEqu("x3", opts.usage()[4]);\r
-\r
- // local time: +3600\r
- const int DIFF = 3600;\r
- checkEqu(tenYear + 24 * 60 * 60 - DIFF,\r
- secOfFileTime(opts.checkDate("1980.01.02")));\r
- checkEqu(tenYear + 24 * 60 * 60 + 3600 - DIFF,\r
- secOfFileTime(opts.checkDate("1980.01.02/1")));\r
- checkEqu(tenYear + 24 * 60 * 60 + 2 * 3600 + 33 * 60 - DIFF,\r
- secOfFileTime(opts.checkDate("1980.01.02/02:33")));\r
- checkRelDate(opts.checkDate("now-3m"), 3 * 60);\r
- checkRelDate(opts.checkDate("now-7h"), 7 * 60 * 60);\r
- checkRelDate(opts.checkDate("now-5d"), 5 * 24 * 60 * 60);\r
-\r
- checkEqu(125ll, opts.checkSize("125"));\r
- checkEqu(125ll, opts.checkSize("125b"));\r
- checkEqu(3000ll, opts.checkSize("3k"));\r
- checkEqu(3 * 1024ll, opts.checkSize("3K"));\r
- checkEqu(4 * 1000 * 1000ll, opts.checkSize("4m"));\r
- checkEqu(4 * 1024 * 1024ll, opts.checkSize("4M"));\r
- checkEqu(5 * 1000 * 1000 * 1000ll, opts.checkSize("5g"));\r
- checkEqu(5 * 1024 * 1024 * 1024ll, opts.checkSize("5G"));\r
-\r
- }\r
- void checkSetFilterFromProgramArgs() {\r
- ReDirOptions opts(s_empty, s_empty);\r
- opts.addStandardFilterOptions();\r
- const char* argv[] = { "x", "-y1980.01.02", "-o1980.01.03", "-D5",\r
- "-d1", "-z1k", "-Z2M", "-p;*;-*~" };\r
- ReDirEntryFilter filter;\r
- opts.programArgsChangeable().init(sizeof argv / sizeof argv[0], argv);\r
- opts.setFilterFromProgramArgs(filter);\r
- // local time: +3600\r
- const int DIFF = 3600;\r
- checkEqu(tenYear + 1 * 24 * 3600 - DIFF,\r
- secOfFileTime(filter.m_maxAge));\r
- checkEqu(tenYear + 2 * 24 * 3600 - DIFF,\r
- secOfFileTime(filter.m_minAge));\r
- checkEqu(5, (int ) filter.m_maxDepth);\r
- checkEqu(1, (int ) filter.m_minDepth);\r
- checkEqu(1000ll, filter.m_minSize);\r
- checkEqu(2 * 1024 * 1024ll, filter.m_maxSize);\r
- checkNN(filter.m_nodePatterns);\r
- checkEqu(";*;-*~", filter.m_nodePatterns->patternString());\r
- }\r
- void checkOneFile(const char* node, const char* parent,\r
- const ReHashList& hash) {\r
- ReByteBuffer path, expected;\r
- checkT(hash.get(ReByteBuffer(node), path));\r
- expected.set(parent, -1);\r
- if (!expected.endsWith(OS_SEPARATOR))\r
- expected.append(OS_SEPARATOR);\r
- if (!path.endsWith(expected.str(), -1))\r
- checkT(false);\r
- }\r
- void testBasic() {\r
- rebuildTree();\r
- ReTraverser traverser(m_base.str());\r
- RePatternList patterns;\r
- // exclude */cache/*\r
- ReByteBuffer buffer(";*;-cache");\r
- patterns.set(buffer.str());\r
- traverser.setDirPattern(&patterns);\r
- int level = 0;\r
- ReDirStatus_t* entry;\r
- ReHashList hash;\r
- while ((entry = traverser.rawNextFile(level)) != NULL) {\r
- hash.put(ReByteBuffer(entry->node(), -1), entry->m_path);\r
- logF(false, "%d: %-12s %2d %s", level, entry->node(),\r
- int(entry->fileSize()), entry->m_path.str());\r
- }\r
- checkOneFile("x1.txt", "dir1_2_1", hash);\r
- checkOneFile("x2.txt", "dir1_2_1", hash);\r
- checkOneFile("dir1_2_1", "dir1_2", hash);\r
- checkOneFile("dir1_1", "dir1", hash);\r
- checkOneFile("dir1_2", "dir1", hash);\r
- checkF(hash.get("cache.txt", buffer));\r
- }\r
- void testDirStatistic() {\r
- rebuildTree();\r
- ReDirStatistic stat(m_logger);\r
- const ReStringList& list = stat.calculate(m_base.str(), 1);\r
- ReByteBuffer buffer;\r
- ReByteBuffer expected;\r
- log(false, list.join("\n", buffer).str());\r
- checkEqu(3u, list.count());\r
- // "1 t:\temp\winfried\2\retestunit\dir1\n"\r
- buffer.set(list.strOf(0), list.strLengthOf(0));\r
- checkT(buffer.startsWith("1\t"));\r
- expected.set(m_base.str(), m_base.length()).append("dir1", -1).append(\r
- OS_SEPARATOR);\r
- // .append(OS_SEPARATOR, -1)\r
- checkT(buffer.endsWith(expected.str()));\r
-\r
- buffer.setLength(0);\r
- const ReStringList& list2 = stat.calculate(m_base.str(), 1,\r
- formatWithSizeFilesAndDirs);\r
- log(false, list2.join("\n", buffer).str());\r
-\r
- buffer.set(list.strOf(0), list.strLengthOf(0));\r
- checkT(buffer.startsWith(" 0.000074 MB 3 4\t"));\r
- expected.set(m_base.str(), m_base.length()).append("dir1", -1).append(\r
- OS_SEPARATOR);\r
- checkT(buffer.endsWith(expected.str()));\r
-\r
- buffer.set(list.strOf(1), list.strLengthOf(1));\r
- checkT(buffer.startsWith(" 0.000008 MB 1 0\t"));\r
- expected.set(m_base.str(), m_base.length()).append("dir2", -1).append(\r
- OS_SEPARATOR);\r
- checkT(buffer.endsWith(expected.str()));\r
-\r
- buffer.set(list.strOf(2), list.strLengthOf(2));\r
- checkT(buffer.startsWith(" 0.000087 MB 5 6\t"));\r
- expected.set(m_base.str(), m_base.length());\r
- }\r
- /**\r
- * Tests a file with a given context.\r
- *\r
- * @param filename name of the file to test\r
- * @param content expected content: each line must contain a '*',\r
- * separating the start of line and the end of line\r
- */\r
- void checkFile(ReByteBuffer& filename, const char* content) {\r
- ReStringList current;\r
- current.readFromFile(filename.str());\r
- ReStringList expected;\r
- expected.split(content, '\n');\r
- checkEqu(expected.count(), current.count());\r
- ReByteBuffer line;\r
- ReStringList cols;\r
- for (size_t ix = 0; ix < current.count(); ix++) {\r
- line.setLength(0).append(current.strOf(ix), -1);\r
-#if defined __WIN32__\r
- line.replaceAll("/", 1, OS_SEPARATOR, 1);\r
-#endif\r
- cols.split(expected.strOf(ix), '*');\r
- const char* col1 = cols.strOf(0);\r
- if (!line.startsWith(col1))\r
- checkEqu(col1, line);\r
- const char* col2 = cols.strOf(1);\r
- if (!line.endsWith(col2)) {\r
- checkEqu(col2, line);\r
- }\r
- }\r
- }\r
- void testTouch() {\r
- rebuildTree();\r
- ReByteBuffer nameCurrent;\r
- buildFilename("current", nameCurrent);\r
- ReByteBuffer optOutput("--output-file=");\r
- optOutput.append(nameCurrent);\r
- ReDirTools tools;\r
- const char* argv[] = { "dt", "touch", "-P;*;-cache", "-tr", "-p;*.txt",\r
- "-m2015.03.28/10:21:31", optOutput.str(), m_base.str(), NULL };\r
- tools.main(8, (char**) argv);\r
-\r
- checkFile(nameCurrent,\r
- "2015.03.28 10:21:31 *dirtool/1.txt\n"\r
- "2015.03.28 10:21:31 *dirtool/dir1/dir1_2/dir1_2_1/x1.txt\n"\r
- "2015.03.28 10:21:31 *dirtool/dir1/dir1_2/dir1_2_1/x2.txt\n"\r
- "=== filtered: 3 file(s) 0.000059 MByte 0 dirs(s) */sec\n"\r
- "=== total: 4 file(s) 0.000067 MByte 6 dirs(s) * sec");\r
-\r
- const char* argv2[] = { "dt", "list", "-P;*;-cache", "-tr", "-p;*.txt",\r
- "-h", optOutput.str(), m_base.str(), NULL };\r
- tools.main(8, (char**) argv2);\r
-\r
- checkFile(nameCurrent,\r
- " 0.000005 2015.03.28 10:21:31 *dirtool/1.txt\n"\r
- " 0.000027 2015.03.28 10:21:31 *dirtool/dir1/dir1_2/dir1_2_1/x1.txt\n"\r
- " 0.000027 2015.03.28 10:21:31 *dirtool/dir1/dir1_2/dir1_2_1/x2.txt\n"\r
- "=== filtered: 3 file(s) 0.000059 MByte 0 dirs(s) */sec\n"\r
- "=== total: 4 file(s) 0.000067 MByte 6 dirs(s) * sec");\r
-\r
- }\r
- void testBatch() {\r
- rebuildTree();\r
- ReByteBuffer nameCurrent;\r
- buildFilename("current", nameCurrent);\r
- ReByteBuffer optOutput("--output-file=");\r
- optOutput.append(nameCurrent);\r
- ReDirTools tools;\r
- const char* argv[] = { "dt", "batch", "-P;*;-cache", "-td",\r
- "-cmyscript", optOutput.str(), m_base.str(), NULL };\r
- tools.main(7, (char**) argv);\r
-#if defined __linux__\r
- static const char* content =\r
- "#! /bin/s*h\n"\r
- "myscript '*dirtool/dir1'\n"\r
- "myscript '*dirtool/dir2'\n"\r
- "myscript '*dirtool/dir1/dir1_1'\n"\r
- "myscript '*dirtool/dir1/dir1_2'\n"\r
- "myscript '*dirtool/dir1/cache'\n"\r
- "myscript '*dirtool/dir1/dir1_2/dir1_2_1'\n"\r
- "# === filtered: 0 file(s) 0.000000 MByte 6 dirs(s) */sec\n"\r
- "# === total: 4 file(s) 0.000067 MByte 6 dirs(s) * sec";\r
-#elif defined __WIN32__\r
- static const char* content = "rem this batch is created by*dirtool\n"\r
- "call myscript *dirtool\\dir1\"\n"\r
- "call myscript *dirtool\\dir2\"\n"\r
- "call myscript *dirtool\\dir1\\cache\"\n"\r
- "call myscript *dirtool\\dir1\\dir1_1\"\n"\r
- "call myscript *dirtool\\dir1\\dir1_2\"\n"\r
- "call myscript *dirtool\\dir1\\dir1_2\\dir1_2_1\"\n"\r
- "rem === filtered: 0 file(s) 0.000000 MByte 6 dirs(s) *sec\n"\r
- "rem === total: 4 file(s) 0.000067 MByte 6 dirs(s) * sec";\r
-#endif\r
- checkFile(nameCurrent, content);\r
- }\r
- void testToolStatistic() {\r
- rebuildTree();\r
- ReDirTools tools;\r
- const char* argv[] = { "dt", "stat", "-P;*;-cache", m_base.str(), "2" };\r
- tools.main(5, (char**) argv);\r
- }\r
- void testToolSync() {\r
- rebuildTree();\r
- ReDirTools tools;\r
- ReByteBuffer source(m_base);\r
- source.append("dir1");\r
- ReByteBuffer target(testDir());\r
- target.ensureLastChar(OS_SEPARATOR_CHAR);\r
- target.append("synctest", -1);\r
- _mkdir(target.str(), ALLPERMS);\r
- const char* argv[] = { "dt", "sync", "-P;*;-cache", "-p;*.txt", source\r
- .str(), target.str() };\r
- tools.main(6, (char**) argv);\r
- }\r
-};\r
-extern void testReDirTools(void);\r
-\r
-void testReDirTools(void) {\r
- TestReDirTools unit;\r
-}\r
+/*
+ * cuReDirTools.cpp
+ *
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
+ * Do what you want.
+ * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
+ * The latest sources: https://github.com/republib
+ */
+
+#include "base/rebase.hpp"
+#include "os/reos.hpp"
+
+static const char* s_empty[] = { NULL };
+// count of seconds from 1.1.1970 until 1.1.1980:
+const int tenYear = (365 * 10 + 2) * 24 * 3600;
+
+const char* s_allFiles[] = { //
+ " 1.txt", //
+ "*dir1", //
+ "*dir2", //
+ "*dir1/cache", //
+ "*dir1/dir1_1", //
+ "*dir1/dir1_2", //
+ "*dir1/dir1_2/dir1_2_1", //
+ " dir1/dir1_2/dir1_2_1/x1.txt", //
+ " dir1/dir1_2/dir1_2_1/x2.txt", //
+ " dir2/2.x", //
+ " dir1/cache/cache.txt", //
+ NULL };
+
+class TestReDirTools: public ReTestUnit {
+public:
+ TestReDirTools() :
+ ReTestUnit("ReDirTools", __FILE__),
+ // m_base
+ // m_buffer
+ m_testAll(true),
+ m_logger(ReLogger::globalLogger()) {
+ m_base = testDir();
+ ReDirectory::deleteTree(m_base.str());
+ m_base.append("dirtool");
+ _mkdir(m_base.str(), ALLPERMS);
+ m_base.append(OS_SEPARATOR, -1);
+ m_testAll = true;
+ run();
+ ReDirectory::deleteTree(m_base.str(), true);
+ }
+private:
+ ReByteBuffer m_base;
+ ReByteBuffer m_buffer;
+ bool m_testAll;
+ ReLogger* m_logger;
+private:
+ void run() {
+ testRandom();
+ testDelete();
+ testChecksumBuild();
+ if (m_testAll) {
+ testDirOptions();
+ checkSetFilterFromProgramArgs();
+ testList2();
+ testToolStatistic();
+ testBasic();
+ testDirStatistic();
+ testCopyFile();
+ testList();
+ testToolSync();
+ testBatch();
+ testChecksumBuild();
+ testDelete();
+ testRandom();
+ }
+ }
+ void testRandom() {
+ const char* argv[] = { "random", "-l20", "-s", "40", "50",
+ NULL };
+ ReDirRandom(m_logger).run(-1, argv);
+ }
+ void testDelete() {
+ rebuildTree();
+ ReByteBuffer nameCurrent;
+ buildFilename("current", nameCurrent);
+ ReByteBuffer optOutput("--output-file=");
+ optOutput.append(nameCurrent);
+ const char* argv[] = { "delete", "-p;x*.txt", optOutput.str(), m_base
+ .str(), NULL };
+ const char* existing[] = { //
+ " 1.txt", //
+ "*dir1", //
+ "*dir2", //
+ "*dir1/cache", //
+ "*dir1/dir1_1", //
+ "*dir1/dir1_2", //
+ "*dir1/dir1_2/dir1_2_1", //
+ " dir2/2.x", //
+ " dir1/cache/cache.txt", //
+ NULL };
+ const char* notExisting[] = { //
+ " dir1/dir1_2/dir1_2_1/x1.txt", //
+ " dir1/dir1_2/dir1_2_1/x2.txt", //
+ NULL };
+ ReDirDelete(m_logger).run(-1, argv);
+ mustExist(existing);
+ mayNotExist(notExisting);
+
+ const char* argv2[] = { "delete", optOutput.str(), m_base.str(), NULL };
+ ReDirDelete(m_logger).run(-1, argv2);
+ mayNotExist(s_allFiles);
+ }
+ void mustExist(const char** names) {
+ ReByteBuffer name;
+ struct stat info;
+ for (int ix = 0; names[ix] != NULL; ix++) {
+ const char* arg = names[ix];
+ name = m_base;
+ name.ensureLastChar(OS_SEPARATOR_CHAR);
+ name.append(arg + 1);
+ name.replaceAll("/", -1, OS_SEPARATOR, -1);
+ if (stat(name.str(), &info) != 0)
+ checkEqu("missing", name);
+ bool isDir = arg[0] == '*';
+ if (isDir && !S_ISDIR(info.st_mode))
+ checkEqu("file not dir", name);
+ if (!isDir && S_ISDIR(info.st_mode))
+ checkEqu("dir not file", name);
+ }
+ }
+ void mayNotExist(const char** names) {
+ ReByteBuffer name;
+ struct stat info;
+ for (int ix = 0; names[ix] != NULL; ix++) {
+ const char* arg = names[ix];
+ name = m_base;
+ name.ensureLastChar(OS_SEPARATOR_CHAR);
+ name.append(arg + 1);
+ name.replaceAll("/", -1, OS_SEPARATOR, -1);
+ if (stat(name.str(), &info) == 0)
+ checkEqu("exists", name);
+ }
+ }
+ int secOfFileTime(ReFileTime_t data) {
+#if defined __linux__
+ return data.tv_sec;
+#elif defined __WIN32__
+ return (int) ReDirStatus_t::filetimeToTime(&data);
+#endif
+ }
+ const char* makeDir(const char* relPath) {
+ m_buffer = m_base;
+ m_buffer.append(relPath);
+ m_buffer.replaceAll("/", 1, OS_SEPARATOR, -1);
+ _mkdir(m_buffer.str(), ALLPERMS);
+ struct stat info;
+ if (stat(m_buffer.str(), &info) != 0) {
+ logF(true, "cannot create dir %s", m_buffer.str());
+ }
+ return m_buffer.str();
+ }
+ void makeFile(const char* relPath) {
+ ReByteBuffer path(m_base);
+ path.append(relPath);
+ path.replaceAll("/", 1, OS_SEPARATOR, -1);
+ createFile(path.str(), relPath);
+ struct stat info;
+ if (stat(path.str(), &info) != 0) {
+ logF(true, "cannot create file %s", path.str());
+ }
+ }
+ void initTree() {
+ for (int ix = 0; s_allFiles[ix] != 0; ix++) {
+ const char* name = s_allFiles[ix];
+ if (name[0] == '*')
+ makeDir(name + 1);
+ else
+ makeFile(name + 1);
+ }
+ }
+ void rebuildTree() {
+ ReDirectory::deleteTree(m_base.str(), false);
+ initTree();
+ }
+ void testChecksumBuild() {
+ rebuildTree();
+ ReByteBuffer nameCurrent;
+ buildFilename("current", nameCurrent);
+ ReByteBuffer optOutput("--output-file=");
+ optOutput.append(nameCurrent);
+ const char* argv[] = { "dt", "checksum", "-P;*;-cache", "-tr",
+ "-p;*.txt", "-s4711", "-arpd64", "-cBuild", optOutput.str(), m_base
+ .str(), NULL };
+ ReDirTools tools;
+ tools.main(-1, (char**) argv);
+ // checkFile(nameCurrent, "");
+ const char* argv2[] = { "dt", "checksum", "-P;*;-cache", "-tr",
+ "-p;*.txt", "-s4711", "-arpd64", "-cCompare", optOutput.str(),
+ m_base.str(), NULL };
+ tools.main(-1, (char**) argv2);
+ }
+ // F:\temp\retestunit\dirtool
+ void testList2() {
+ rebuildTree();
+ const char* argv[] = { "dt", "list", "-r", m_base.str(), NULL };
+ ReDirTools tools;
+ tools.main(4, (char**) argv);
+ }
+ void testList() {
+ rebuildTree();
+ const char* argv[] = { "list", m_base.str(), NULL };
+ ReDirList(m_logger).run(2, argv);
+ }
+ void testCopyFile() {
+ rebuildTree();
+#if defined __linux__
+ ReByteBuffer src(m_base);
+ src.append("dir1/dir1_2/dir1_2_1/x1.txt");
+ ReByteBuffer trg(testDir());
+ trg.append("copy_x1.txt");
+ ReByteBuffer buffer;
+ buffer.ensureSize(5);
+ ReDirSync::copyFile(src.str(), NULL, trg.str(), buffer,
+ ReLogger::globalLogger());
+ checkFileEqu(src.str(), trg.str());
+#else
+ log(false, "testCopyFile not implemented");
+#endif
+ }
+
+ void checkRelDate(ReFileTime_t absTime, int relTime) {
+ int diffNow = int(time(NULL) - secOfFileTime(absTime));
+ int diff =
+ diffNow * relTime < 0 ? diffNow + relTime : diffNow - relTime;
+ if (diff < 0)
+ diff = -diff;
+ checkT(diff < 2);
+ }
+ void testDirOptions() {
+ class MyOptions: public ReDirOptions {
+ public:
+ MyOptions() :
+ ReDirOptions(s_empty, s_empty) {
+ }
+ public:
+ int count() {
+ return m_countCompoundUsage;
+ }
+ const char** usage() {
+ return m_compoundUsage;
+ }
+ };
+ static const char* usage1[] = { "line1", "line2", NULL };
+ static const char* usage2[] = { "x1", "x2", "x3", NULL };
+ MyOptions opts;
+ opts.initCompoundUsage(sizeof usage1 + sizeof usage2);
+ opts.addCompoundUsage(usage1);
+ opts.addCompoundUsage(usage2);
+ checkEqu(7, opts.count());
+ checkEqu("line1", opts.usage()[0]);
+ checkEqu("line2", opts.usage()[1]);
+ checkEqu("x1", opts.usage()[2]);
+ checkEqu("x2", opts.usage()[3]);
+ checkEqu("x3", opts.usage()[4]);
+
+ // local time: +3600
+ const int DIFF = 3600;
+ checkEqu(tenYear + 24 * 60 * 60 - DIFF,
+ secOfFileTime(opts.checkDate("1980.01.02")));
+ checkEqu(tenYear + 24 * 60 * 60 + 3600 - DIFF,
+ secOfFileTime(opts.checkDate("1980.01.02/1")));
+ checkEqu(tenYear + 24 * 60 * 60 + 2 * 3600 + 33 * 60 - DIFF,
+ secOfFileTime(opts.checkDate("1980.01.02/02:33")));
+ checkRelDate(opts.checkDate("now-3m"), 3 * 60);
+ checkRelDate(opts.checkDate("now-7h"), 7 * 60 * 60);
+ checkRelDate(opts.checkDate("now-5d"), 5 * 24 * 60 * 60);
+
+ checkEqu(125ll, opts.checkSize("125"));
+ checkEqu(125ll, opts.checkSize("125b"));
+ checkEqu(3000ll, opts.checkSize("3k"));
+ checkEqu(3 * 1024ll, opts.checkSize("3K"));
+ checkEqu(4 * 1000 * 1000ll, opts.checkSize("4m"));
+ checkEqu(4 * 1024 * 1024ll, opts.checkSize("4M"));
+ checkEqu(5 * 1000 * 1000 * 1000ll, opts.checkSize("5g"));
+ checkEqu(5 * 1024 * 1024 * 1024ll, opts.checkSize("5G"));
+
+ }
+ void checkSetFilterFromProgramArgs() {
+ ReDirOptions opts(s_empty, s_empty);
+ opts.addStandardFilterOptions();
+ const char* argv[] = { "x", "-y1980.01.02", "-o1980.01.03", "-D5",
+ "-d1", "-z1k", "-Z2M", "-p;*;-*~" };
+ ReDirEntryFilter filter;
+ opts.programArgsChangeable().init(sizeof argv / sizeof argv[0], argv);
+ opts.setFilterFromProgramArgs(filter);
+ // local time: +3600
+ const int DIFF = 3600;
+ checkEqu(tenYear + 1 * 24 * 3600 - DIFF,
+ secOfFileTime(filter.m_maxAge));
+ checkEqu(tenYear + 2 * 24 * 3600 - DIFF,
+ secOfFileTime(filter.m_minAge));
+ checkEqu(5, (int ) filter.m_maxDepth);
+ checkEqu(1, (int ) filter.m_minDepth);
+ checkEqu(1000ll, filter.m_minSize);
+ checkEqu(2 * 1024 * 1024ll, filter.m_maxSize);
+ checkNN(filter.m_nodePatterns);
+ checkEqu(";*;-*~", filter.m_nodePatterns->patternString());
+ }
+ void checkOneFile(const char* node, const char* parent,
+ const ReHashList& hash) {
+ ReByteBuffer path, expected;
+ checkT(hash.get(ReByteBuffer(node), path));
+ expected.set(parent, -1);
+ if (!expected.endsWith(OS_SEPARATOR))
+ expected.append(OS_SEPARATOR);
+ if (!path.endsWith(expected.str(), -1))
+ checkT(false);
+ }
+ void testBasic() {
+ rebuildTree();
+ ReTraverser traverser(m_base.str());
+ RePatternList patterns;
+ // exclude */cache/*
+ ReByteBuffer buffer(";*;-cache");
+ patterns.set(buffer.str());
+ traverser.setDirPattern(&patterns);
+ int level = 0;
+ ReDirStatus_t* entry;
+ ReHashList hash;
+ while ((entry = traverser.rawNextFile(level)) != NULL) {
+ hash.put(ReByteBuffer(entry->node(), -1), entry->m_path);
+ logF(false, "%d: %-12s %2d %s", level, entry->node(),
+ int(entry->fileSize()), entry->m_path.str());
+ }
+ checkOneFile("x1.txt", "dir1_2_1", hash);
+ checkOneFile("x2.txt", "dir1_2_1", hash);
+ checkOneFile("dir1_2_1", "dir1_2", hash);
+ checkOneFile("dir1_1", "dir1", hash);
+ checkOneFile("dir1_2", "dir1", hash);
+ checkF(hash.get("cache.txt", buffer));
+ }
+ void testDirStatistic() {
+ rebuildTree();
+ ReDirStatistic stat(m_logger);
+ const ReStringList& list = stat.calculate(m_base.str(), 1);
+ ReByteBuffer buffer;
+ ReByteBuffer expected;
+ log(false, list.join("\n", buffer).str());
+ checkEqu(3u, list.count());
+ // "1 t:\temp\winfried\2\retestunit\dir1\n"
+ buffer.set(list.strOf(0), list.strLengthOf(0));
+ checkT(buffer.startsWith("1\t"));
+ expected.set(m_base.str(), m_base.length()).append("dir1", -1).append(
+ OS_SEPARATOR);
+ // .append(OS_SEPARATOR, -1)
+ checkT(buffer.endsWith(expected.str()));
+
+ buffer.setLength(0);
+ const ReStringList& list2 = stat.calculate(m_base.str(), 1,
+ formatWithSizeFilesAndDirs);
+ log(false, list2.join("\n", buffer).str());
+
+ buffer.set(list.strOf(0), list.strLengthOf(0));
+ checkT(buffer.startsWith(" 0.000074 MB 3 4\t"));
+ expected.set(m_base.str(), m_base.length()).append("dir1", -1).append(
+ OS_SEPARATOR);
+ checkT(buffer.endsWith(expected.str()));
+
+ buffer.set(list.strOf(1), list.strLengthOf(1));
+ checkT(buffer.startsWith(" 0.000008 MB 1 0\t"));
+ expected.set(m_base.str(), m_base.length()).append("dir2", -1).append(
+ OS_SEPARATOR);
+ checkT(buffer.endsWith(expected.str()));
+
+ buffer.set(list.strOf(2), list.strLengthOf(2));
+ checkT(buffer.startsWith(" 0.000087 MB 5 6\t"));
+ expected.set(m_base.str(), m_base.length());
+ }
+ /**
+ * Tests a file with a given context.
+ *
+ * @param filename name of the file to test
+ * @param content expected content: each line must contain a '*',
+ * separating the start of line and the end of line
+ */
+ void checkFile(ReByteBuffer& filename, const char* content) {
+ ReStringList current;
+ current.readFromFile(filename.str());
+ ReStringList expected;
+ expected.split(content, '\n');
+ checkEqu(expected.count(), current.count());
+ ReByteBuffer line;
+ ReStringList cols;
+ for (size_t ix = 0; ix < current.count(); ix++) {
+ line.setLength(0).append(current.strOf(ix), -1);
+#if defined __WIN32__
+ line.replaceAll("/", 1, OS_SEPARATOR, 1);
+#endif
+ cols.split(expected.strOf(ix), '*');
+ const char* col1 = cols.strOf(0);
+ if (!line.startsWith(col1))
+ checkEqu(col1, line);
+ const char* col2 = cols.strOf(1);
+ if (!line.endsWith(col2)) {
+ checkEqu(col2, line);
+ }
+ }
+ }
+ void testTouch() {
+ rebuildTree();
+ ReByteBuffer nameCurrent;
+ buildFilename("current", nameCurrent);
+ ReByteBuffer optOutput("--output-file=");
+ optOutput.append(nameCurrent);
+ ReDirTools tools;
+ const char* argv[] = { "dt", "touch", "-P;*;-cache", "-tr", "-p;*.txt",
+ "-m2015.03.28/10:21:31", optOutput.str(), m_base.str(), NULL };
+ tools.main(8, (char**) argv);
+
+ checkFile(nameCurrent,
+ "2015.03.28 10:21:31 *dirtool/1.txt\n"
+ "2015.03.28 10:21:31 *dirtool/dir1/dir1_2/dir1_2_1/x1.txt\n"
+ "2015.03.28 10:21:31 *dirtool/dir1/dir1_2/dir1_2_1/x2.txt\n"
+ "=== filtered: 3 file(s) 0.000059 MByte 0 dirs(s) */sec\n"
+ "=== total: 4 file(s) 0.000067 MByte 6 dirs(s) * sec");
+
+ const char* argv2[] = { "dt", "list", "-P;*;-cache", "-tr", "-p;*.txt",
+ "-h", optOutput.str(), m_base.str(), NULL };
+ tools.main(8, (char**) argv2);
+
+ checkFile(nameCurrent,
+ " 0.000005 2015.03.28 10:21:31 *dirtool/1.txt\n"
+ " 0.000027 2015.03.28 10:21:31 *dirtool/dir1/dir1_2/dir1_2_1/x1.txt\n"
+ " 0.000027 2015.03.28 10:21:31 *dirtool/dir1/dir1_2/dir1_2_1/x2.txt\n"
+ "=== filtered: 3 file(s) 0.000059 MByte 0 dirs(s) */sec\n"
+ "=== total: 4 file(s) 0.000067 MByte 6 dirs(s) * sec");
+
+ }
+ void testBatch() {
+ rebuildTree();
+ ReByteBuffer nameCurrent;
+ buildFilename("current", nameCurrent);
+ ReByteBuffer optOutput("--output-file=");
+ optOutput.append(nameCurrent);
+ ReDirTools tools;
+ const char* argv[] = { "dt", "batch", "-P;*;-cache", "-td",
+ "-cmyscript", optOutput.str(), m_base.str(), NULL };
+ tools.main(7, (char**) argv);
+#if defined __linux__
+ static const char* content =
+ "#! /bin/s*h\n"
+ "myscript '*dirtool/dir1'\n"
+ "myscript '*dirtool/dir2'\n"
+ "myscript '*dirtool/dir1/dir1_1'\n"
+ "myscript '*dirtool/dir1/dir1_2'\n"
+ "myscript '*dirtool/dir1/cache'\n"
+ "myscript '*dirtool/dir1/dir1_2/dir1_2_1'\n"
+ "# === filtered: 0 file(s) 0.000000 MByte 6 dirs(s) */sec\n"
+ "# === total: 4 file(s) 0.000067 MByte 6 dirs(s) * sec";
+#elif defined __WIN32__
+ static const char* content = "rem this batch is created by*dirtool\n"
+ "call myscript *dirtool\\dir1\"\n"
+ "call myscript *dirtool\\dir2\"\n"
+ "call myscript *dirtool\\dir1\\cache\"\n"
+ "call myscript *dirtool\\dir1\\dir1_1\"\n"
+ "call myscript *dirtool\\dir1\\dir1_2\"\n"
+ "call myscript *dirtool\\dir1\\dir1_2\\dir1_2_1\"\n"
+ "rem === filtered: 0 file(s) 0.000000 MByte 6 dirs(s) *sec\n"
+ "rem === total: 4 file(s) 0.000067 MByte 6 dirs(s) * sec";
+#endif
+ checkFile(nameCurrent, content);
+ }
+ void testToolStatistic() {
+ rebuildTree();
+ ReDirTools tools;
+ const char* argv[] = { "dt", "stat", "-P;*;-cache", m_base.str(), "2" };
+ tools.main(5, (char**) argv);
+ }
+ void testToolSync() {
+ rebuildTree();
+ ReDirTools tools;
+ ReByteBuffer source(m_base);
+ source.append("dir1");
+ ReByteBuffer target(testDir());
+ target.ensureLastChar(OS_SEPARATOR_CHAR);
+ target.append("synctest", -1);
+ _mkdir(target.str(), ALLPERMS);
+ const char* argv[] = { "dt", "sync", "-P;*;-cache", "-p;*.txt", source
+ .str(), target.str() };
+ tools.main(6, (char**) argv);
+ }
+};
+extern void testReDirTools(void);
+
+void testReDirTools(void) {
+ TestReDirTools unit;
+}
/*
* 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"
/*
* 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"
/*
* 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"
/*
- * 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"
/*
* 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"
/*
- * 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
*/
/*
* 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
*/
/*
/*
* 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"
/*
- * 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
*/
/*
* 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"
/*
* 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"
/*
* 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"
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)
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));
}
};
/*
- * 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"
/*
* 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"
/*
* 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
*/
/*
* 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"
/*
* 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
*/
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);
/*
* 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"
/*
- * 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"
/*
* 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"
+/*
+ * dirtool.cpp
+ *
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
+ * Do what you want.
+ * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
+ * The latest sources: https://github.com/republib
+ */
// dirtool.cpp : Definiert den Einstiegspunkt für die Konsolenanwendung.\r
//\r
#include "base/rebase.hpp"\r
-/*\r
- * ReMD5.cpp\r
- *\r
- * Created on: 30.01.2015\r
- *\r
- */\r
-\r
-#include "base/rebase.hpp"\r
-#include "math/remath.hpp"\r
-\r
-const int ReMD5::m_s[RE_DIGEST_CHUNK_SIZE] = { 7, 12, 17, 22, 7, 12, 17, 22, 7,\r
- 12, 17, 22, 7, 12, 17, 22, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9,\r
- 14, 20, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 6, 10,\r
- 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21 };\r
-// for x in [1..64] : int(2**32 * sin(x))\r
-const uint32_t ReMD5::m_K[RE_DIGEST_CHUNK_SIZE] = { 0xd76aa478, 0xe8c7b756,\r
- 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,\r
- 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193,\r
- 0xa679438e, 0x49b40821, 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,\r
- 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, 0x21e1cde6, 0xc33707d6,\r
- 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,\r
- 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9,\r
- 0xf6bb4b60, 0xbebfbc70, 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,\r
- 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, 0xf4292244, 0x432aff97,\r
- 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,\r
- 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235,\r
- 0x2ad7d2bb, 0xeb86d391 };\r
-\r
-/**\r
- * Constructor.\r
- *\r
- * @param digest the buffer for the binary checksum\r
- * @param digestSize the length of <code>digest</code>\r
- * @param waiting a buffer for a chunk. Must have space for at least 2*chunksize bytes\r
- * NULL: an intrinsic buffer will be used\r
- * @param chunkSize the length of one full input block\r
- */\r
-ReDigest::ReDigest(uint8_t* digest, size_t digestSize, uint8_t* waiting,\r
- size_t chunkSize) :\r
- m_digest(digest),\r
- m_digestSize(digestSize),\r
- // m_waitingBuffer[RE_DIGEST_CHUNK_SIZE];\r
- m_waiting(waiting != NULL ? waiting : m_waitingBuffer),\r
- m_lengthWaiting(0),\r
- m_chunkSize(chunkSize),\r
- m_length(0),\r
- m_finalized(false),\r
- m_salt(0) {\r
-}\r
-/**\r
- * Destructor.\r
- */\r
-ReDigest::~ReDigest() {\r
-}\r
-/**\r
- * Returns the binary digest value.\r
- *\r
- * @return the binary digest (16 byte array)\r
- */\r
-const uint8_t* ReDigest::digest() {\r
- if (!m_finalized) {\r
- finalize();\r
- }\r
- return m_digest;\r
-}\r
-\r
-/**\r
- * Returns the binary digest value.\r
- *\r
- * @return the binary digest (16 byte array)\r
- */\r
-const ReByteBuffer& ReDigest::hexDigest() {\r
- if (m_hexDigest.length() == 0) {\r
- digest();\r
- for (size_t ix = 0; ix < m_digestSize; ix++) {\r
- m_hexDigest.appendInt(m_digest[ix], "%02x");\r
- }\r
- }\r
- return m_hexDigest;\r
-}\r
-\r
-/**\r
- * Processes a 64 byte block.\r
- *\r
- * @param block a block which should be added to the digest\r
- * @param blockLength the length of <code>block</code>\r
- */\r
-void ReDigest::update(const uint8_t* block, int blockLength) {\r
- if (blockLength == -1)\r
- blockLength = strlen((const char*) block);\r
- // process the "waiting" input (incomplete chunk):\r
- m_length += blockLength;\r
- if (m_lengthWaiting > 0) {\r
- int rest = m_chunkSize - m_lengthWaiting;\r
- if (rest > blockLength)\r
- rest = blockLength;\r
- memcpy(m_waiting + m_lengthWaiting, block, rest);\r
- blockLength -= rest;\r
- block += rest;\r
- m_lengthWaiting += rest;\r
- // Is the chunk complete?\r
- if (m_lengthWaiting == m_chunkSize) {\r
- processChunk(m_waiting);\r
- m_lengthWaiting = 0;\r
- }\r
- }\r
- // process full 512 bit chunks (64 byte blocks):\r
- for (int ix = blockLength / m_chunkSize; ix > 0; ix--) {\r
- processChunk(block);\r
- block += m_chunkSize;\r
- }\r
- blockLength %= m_chunkSize;\r
- if (blockLength != 0) {\r
- assert(m_lengthWaiting == 0);\r
- memcpy(m_waiting, block, blockLength);\r
- m_lengthWaiting = blockLength;\r
- }\r
-}\r
-\r
-/**\r
- * Sets the salt of the checksum algorithm.\r
- *\r
- * Important: set the salt before you make the first <code>update()</code>!\r
- *\r
- * The salt makes that the checksum of the same input is (extremly) different\r
- * to another salt.\r
- *\r
- * @param salt the salt to set\r
- */\r
-void ReDigest::setSalt(uint64_t salt) {\r
- m_salt = salt;\r
-}\r
-/**\r
- * Constructor.\r
- */\r
-ReMD5::ReMD5() :\r
- ReDigest(m_digest, sizeof m_digest),\r
- m_a0(0x67452301),\r
- m_b0(0xefcdab89),\r
- m_c0(0x98badcfe),\r
- m_d0(0x10325476) {\r
-}\r
-\r
-/**\r
- * Destructor.\r
- */\r
-ReMD5::~ReMD5() {\r
-}\r
-\r
-/**\r
- * Finalizes the digest.\r
- *\r
- * Handles the rest block (< 64 byte) and append a special tail: a "1" bit\r
- * and the length of the total input length (in bits).\r
- *\r
- * @param block the rest input (incomplete chunk)\r
- * @param blockLength the length of the block: 0..63\r
- */\r
-void ReMD5::finalize() {\r
- uint8_t* block = m_waiting;\r
- int blockLength = m_lengthWaiting;\r
- // append "1" bit to message\r
- // Notice: the input bytes are considered as bits strings,\r
- // where the first bit is the most significant bit of the byte.\r
- block[blockLength++] = 0x80;\r
- //Pre-processing: padding with zeros\r
- //append "0" bit until message length in bits ≡ 448 (mod 512)\r
- // fill the rest of the chunk with '\0'.\r
- // the last 8 bytes is set to the length in bits (length in bytes * 8)\r
- int restLength = (m_length + 1) % RE_DIGEST_CHUNK_SIZE;\r
- // 0 -> 56, 1 -> 55, 2 -> 54, ... 55 -> 1, 56 -> 0,\r
- // 57 -> 63, 58 -> 62, ... 63 -> 57\r
- int zeros = restLength <= 56 ? 56 - restLength : 120 - restLength;\r
- memset(block + blockLength, 0, zeros);\r
- blockLength += zeros;\r
- //append original length in bits mod (2 pow 64) to message\r
- uint64_t lengthBits = 8LL * m_length + m_salt;\r
-#if defined __LITTLE_ENDIAN__\r
- memcpy(block + blockLength, &lengthBits, 8);\r
- blockLength += 8;\r
-#else\r
- block[blockLength++] = lengthBits;\r
- lengthBits >>= 8;\r
- block[blockLength++] = lengthBits;\r
- lengthBits >>= 8;\r
- block[blockLength++] = lengthBits;\r
- lengthBits >>= 8;\r
- block[blockLength++] = lengthBits;\r
- lengthBits >>= 8;\r
- block[blockLength++] = lengthBits;\r
- lengthBits >>= 8;\r
- block[blockLength++] = lengthBits;\r
- lengthBits >>= 8;\r
- block[blockLength++] = lengthBits;\r
- lengthBits >>= 8;\r
- block[blockLength++] = lengthBits;\r
-#endif\r
- processChunk(block);\r
- if (blockLength > RE_DIGEST_CHUNK_SIZE)\r
- processChunk(block + RE_DIGEST_CHUNK_SIZE);\r
-#if defined __LITTLE_ENDIAN__\r
- memcpy(m_digest, &m_a0, 4);\r
- memcpy(m_digest + 4, &m_b0, 4);\r
- memcpy(m_digest + 8, &m_c0, 4);\r
- memcpy(m_digest + 12, &m_d0, 4);\r
-#else\r
+/*
+ * ReMD5.cpp
+ *
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
+ * Do what you want.
+ * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
+ * The latest sources: https://github.com/republib
+ */
+
+#include "base/rebase.hpp"
+#include "math/remath.hpp"
+
+const int ReMD5::m_s[RE_DIGEST_CHUNK_SIZE] = { 7, 12, 17, 22, 7, 12, 17, 22, 7,
+ 12, 17, 22, 7, 12, 17, 22, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9,
+ 14, 20, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 6, 10,
+ 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21 };
+// for x in [1..64] : int(2**32 * sin(x))
+const uint32_t ReMD5::m_K[RE_DIGEST_CHUNK_SIZE] = { 0xd76aa478, 0xe8c7b756,
+ 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
+ 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193,
+ 0xa679438e, 0x49b40821, 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
+ 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, 0x21e1cde6, 0xc33707d6,
+ 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
+ 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9,
+ 0xf6bb4b60, 0xbebfbc70, 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
+ 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, 0xf4292244, 0x432aff97,
+ 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
+ 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235,
+ 0x2ad7d2bb, 0xeb86d391 };
+
+/**
+ * Constructor.
+ *
+ * @param digest the buffer for the binary checksum
+ * @param digestSize the length of <code>digest</code>
+ * @param waiting a buffer for a chunk. Must have space for at least 2*chunksize bytes
+ * NULL: an intrinsic buffer will be used
+ * @param chunkSize the length of one full input block
+ */
+ReDigest::ReDigest(uint8_t* digest, size_t digestSize, uint8_t* waiting,
+ size_t chunkSize) :
+ m_digest(digest),
+ m_digestSize(digestSize),
+ // m_waitingBuffer[RE_DIGEST_CHUNK_SIZE];
+ m_waiting(waiting != NULL ? waiting : m_waitingBuffer),
+ m_lengthWaiting(0),
+ m_chunkSize(chunkSize),
+ m_length(0),
+ m_finalized(false),
+ m_salt(0) {
+}
+/**
+ * Destructor.
+ */
+ReDigest::~ReDigest() {
+}
+/**
+ * Returns the binary digest value.
+ *
+ * @return the binary digest (16 byte array)
+ */
+const uint8_t* ReDigest::digest() {
+ if (!m_finalized) {
+ finalize();
+ }
+ return m_digest;
+}
+
+/**
+ * Returns the binary digest value.
+ *
+ * @return the binary digest (16 byte array)
+ */
+const ReByteBuffer& ReDigest::hexDigest() {
+ if (m_hexDigest.length() == 0) {
+ digest();
+ for (size_t ix = 0; ix < m_digestSize; ix++) {
+ m_hexDigest.appendInt(m_digest[ix], "%02x");
+ }
+ }
+ return m_hexDigest;
+}
+
+/**
+ * Processes a 64 byte block.
+ *
+ * @param block a block which should be added to the digest
+ * @param blockLength the length of <code>block</code>
+ */
+void ReDigest::update(const uint8_t* block, int blockLength) {
+ if (blockLength == -1)
+ blockLength = strlen((const char*) block);
+ // process the "waiting" input (incomplete chunk):
+ m_length += blockLength;
+ if (m_lengthWaiting > 0) {
+ int rest = m_chunkSize - m_lengthWaiting;
+ if (rest > blockLength)
+ rest = blockLength;
+ memcpy(m_waiting + m_lengthWaiting, block, rest);
+ blockLength -= rest;
+ block += rest;
+ m_lengthWaiting += rest;
+ // Is the chunk complete?
+ if (m_lengthWaiting == m_chunkSize) {
+ processChunk(m_waiting);
+ m_lengthWaiting = 0;
+ }
+ }
+ // process full 512 bit chunks (64 byte blocks):
+ for (int ix = blockLength / m_chunkSize; ix > 0; ix--) {
+ processChunk(block);
+ block += m_chunkSize;
+ }
+ blockLength %= m_chunkSize;
+ if (blockLength != 0) {
+ assert(m_lengthWaiting == 0);
+ memcpy(m_waiting, block, blockLength);
+ m_lengthWaiting = blockLength;
+ }
+}
+
+/**
+ * Sets the salt of the checksum algorithm.
+ *
+ * Important: set the salt before you make the first <code>update()</code>!
+ *
+ * The salt makes that the checksum of the same input is (extremly) different
+ * to another salt.
+ *
+ * @param salt the salt to set
+ */
+void ReDigest::setSalt(uint64_t salt) {
+ m_salt = salt;
+}
+/**
+ * Constructor.
+ */
+ReMD5::ReMD5() :
+ ReDigest(m_digest, sizeof m_digest),
+ m_a0(0x67452301),
+ m_b0(0xefcdab89),
+ m_c0(0x98badcfe),
+ m_d0(0x10325476) {
+}
+
+/**
+ * Destructor.
+ */
+ReMD5::~ReMD5() {
+}
+
+/**
+ * Finalizes the digest.
+ *
+ * Handles the rest block (< 64 byte) and append a special tail: a "1" bit
+ * and the length of the total input length (in bits).
+ *
+ * @param block the rest input (incomplete chunk)
+ * @param blockLength the length of the block: 0..63
+ */
+void ReMD5::finalize() {
+ uint8_t* block = m_waiting;
+ int blockLength = m_lengthWaiting;
+ // append "1" bit to message
+ // Notice: the input bytes are considered as bits strings,
+ // where the first bit is the most significant bit of the byte.
+ block[blockLength++] = 0x80;
+ //Pre-processing: padding with zeros
+ //append "0" bit until message length in bits ≡ 448 (mod 512)
+ // fill the rest of the chunk with '\0'.
+ // the last 8 bytes is set to the length in bits (length in bytes * 8)
+ int restLength = (m_length + 1) % RE_DIGEST_CHUNK_SIZE;
+ // 0 -> 56, 1 -> 55, 2 -> 54, ... 55 -> 1, 56 -> 0,
+ // 57 -> 63, 58 -> 62, ... 63 -> 57
+ int zeros = restLength <= 56 ? 56 - restLength : 120 - restLength;
+ memset(block + blockLength, 0, zeros);
+ blockLength += zeros;
+ //append original length in bits mod (2 pow 64) to message
+ uint64_t lengthBits = 8LL * m_length + m_salt;
+#if defined __LITTLE_ENDIAN__
+ memcpy(block + blockLength, &lengthBits, 8);
+ blockLength += 8;
+#else
+ block[blockLength++] = lengthBits;
+ lengthBits >>= 8;
+ block[blockLength++] = lengthBits;
+ lengthBits >>= 8;
+ block[blockLength++] = lengthBits;
+ lengthBits >>= 8;
+ block[blockLength++] = lengthBits;
+ lengthBits >>= 8;
+ block[blockLength++] = lengthBits;
+ lengthBits >>= 8;
+ block[blockLength++] = lengthBits;
+ lengthBits >>= 8;
+ block[blockLength++] = lengthBits;
+ lengthBits >>= 8;
+ block[blockLength++] = lengthBits;
+#endif
+ processChunk(block);
+ if (blockLength > RE_DIGEST_CHUNK_SIZE)
+ processChunk(block + RE_DIGEST_CHUNK_SIZE);
+#if defined __LITTLE_ENDIAN__
+ memcpy(m_digest, &m_a0, 4);
+ memcpy(m_digest + 4, &m_b0, 4);
+ memcpy(m_digest + 8, &m_c0, 4);
+ memcpy(m_digest + 12, &m_d0, 4);
+#else
#define oneWord(word, ix) m_digest[ix] = word; word >>= 8; \\r
m_digest[ix + 1] = word; word >>= 8; m_digest[ix + 2] = word; word >>= 8; \\r
- m_digest[ix + 3] = word\r
- oneWord(m_a0, 0);\r
- oneWord(m_b0, 4);\r
- oneWord(m_c0, 8);\r
- oneWord(m_d0, 12);\r
-#endif\r
-}\r
-\r
-/**\r
- * Processes a 512 bit block ("chunk").\r
- *\r
- * This method is a direct programming of the algorithm described in wikipedia.\r
- */\r
-void ReMD5::processChunk2(const uint8_t block[RE_DIGEST_CHUNK_SIZE]) {\r
- uint32_t M[16];\r
- // break chunk into sixteen 32-bit words M[j], 0 ≤ j ≤ 15\r
- for (int ix = 0; ix < 16; ix++) {\r
- uint32_t x = block[3];\r
- for (int jj = 2; jj >= 0; jj--) {\r
- x = (x << 8) + block[jj];\r
- }\r
- M[ix] = x;\r
- block += 4;\r
- }\r
- //Initialize hash value for this chunk:\r
- uint32_t A = m_a0;\r
- uint32_t B = m_b0;\r
- uint32_t C = m_c0;\r
- uint32_t D = m_d0;\r
- //Main loop:\r
-\r
- int F, g;\r
-//#define TRACE_MD5\r
-#if defined TRACE_MD5\r
- printf("neu: (%s)\n", block);\r
-#endif\r
- for (int i = 0; i < RE_DIGEST_CHUNK_SIZE; i++) {\r
-#if defined TRACE_MD5\r
- if (i < 8)\r
- printf("%2d: A: %08x B: %08x C: %08x D%08x\n", i, A, B, C, D);\r
-#endif\r
- if (i < 16) {\r
-# define F1(B, C, D) ((B & C) | (~ B & D))\r
- // F := (B and C) or ((not B) and D)\r
- F = F1(B, C, D);\r
- g = i;\r
- } else if (i < 32) {\r
- // F := (D and B) or (C and (not D))\r
- // g := (5×i + 1) mod 16\r
-# define F2(B, C, D) ((D & B) | (C & ~ D))\r
- F = F2(B, C, D);\r
- g = (5 * i + 1) % 16;\r
- } else if (i < 48) {\r
- // F := B xor C xor D\r
- // g := (3×i + 5) mod 16\r
-# define F3(B, C, D) ((B ^ C) ^ D)\r
- F = F3(B, C, D);\r
- g = (3 * i + 5) % 16;\r
- } else {\r
- // F := C xor (B or (not D))\r
-# define F4(B, C, D) (C ^ (B | ~ D))\r
- // g := (7×i) mod 16\r
- F = F4(B, C, D);\r
- g = (7 * i) % 16;\r
- }\r
-#if defined TRACE_MD5\r
- if (i < 8)\r
- printf(" K[%2d]: %08x M[%2d]: %08x shift: %02d\n",\r
- i, m_K[i], g, M[g], m_s[i]);\r
-#endif\r
- uint32_t dTemp = D;\r
- D = C;\r
- C = B;\r
- // B := B + leftrotate((A + F + K[i] + M[g]), s[i])\r
- uint32_t x = (A + F + m_K[i] + M[g]);\r
- int shift = m_s[i];\r
- B += (x << shift) | (x >> (32 - shift));\r
- A = dTemp;\r
- }\r
- //Add this chunk's hash to result so far:\r
- m_a0 += A;\r
- m_b0 += B;\r
- m_c0 += C;\r
- m_d0 += D;\r
-}\r
-/** ----------------------\r
- */\r
-#if defined OPTIMIZER_WORKS_GREAT\r
-inline void rotate_left_and_add(uint32_t& rc, uint32_t data, int shift, uint32_t term) {\r
- rc = ((data << shift) | (data >> (32-shift))) + term;\r
-}\r
-//#define TRACE_MD5\r
-#if defined TRACE_MD5\r
-static int s_ix = 0;\r
-#endif\r
-inline void X1(uint32_t &var, uint32_t x, uint32_t y, uint32_t z,\r
- uint32_t data, uint32_t aConst, uint32_t shift) {\r
-#if defined TRACE_MD5\r
- printf("%2d: A: %08x B: %08x C: %08x D%08x\n", s_ix++ % 16, var, x, y, z);\r
- printf(" K[%2d]: %08x M[?]: %08x shift: %02d\n",\r
- s_ix - 1, aConst, data, shift);\r
-#endif\r
- rotate_left_and_add(var, var + F1(x, y, z) + data + aConst, shift, x);\r
-}\r
-inline void X2(uint32_t& var, uint32_t x, uint32_t y, uint32_t z,\r
- uint32_t data, uint32_t aConst, uint32_t shift) {\r
-#if defined TRACE_MD5\r
- printf("%2d: A: %08x B: %08x C: %08x D%08x\n", s_ix++ % 16, var, x, y, z);\r
- printf(" K[%2d]: %08x M[?]: %08x shift: %02d\n",\r
- s_ix - 1, aConst, data, shift);\r
-#endif\r
- rotate_left_and_add(var, var + F2(x, y, z) + data + aConst, shift, x);\r
-}\r
-\r
-inline void X3(uint32_t& var, uint32_t x, uint32_t y, uint32_t z,\r
- uint32_t data, uint32_t aConst, uint32_t shift) {\r
-#if defined TRACE_MD5\r
- printf("%2d: A: %08x B: %08x C: %08x D%08x\n", s_ix++ % 16, var, x, y, z);\r
- printf(" K[%2d]: %08x M[?]: %08x shift: %02d\n",\r
- s_ix - 1, aConst, data, shift);\r
-#endif\r
- rotate_left_and_add(var, var + F3(x, y, z) + data + aConst, shift, x);\r
-}\r
-\r
-inline void X4(uint32_t& var, uint32_t x, uint32_t y, uint32_t z,\r
- uint32_t data, uint32_t aConst, uint32_t shift) {\r
-#if defined TRACE_MD5\r
- printf("%2d: A: %08x B: %08x C: %08x D%08x\n", s_ix++ % 16, var, x, y, z);\r
- printf(" K[%2d]: %08x M[?]: %08x shift: %02d\n",\r
- s_ix - 1, aConst, data, shift);\r
-#endif\r
- rotate_left_and_add(var, var + F4(x, y, z) + data + aConst, shift, x);\r
-}\r
-#else\r
+ m_digest[ix + 3] = word
+ oneWord(m_a0, 0);
+ oneWord(m_b0, 4);
+ oneWord(m_c0, 8);
+ oneWord(m_d0, 12);
+#endif
+}
+
+/**
+ * Processes a 512 bit block ("chunk").
+ *
+ * This method is a direct programming of the algorithm described in wikipedia.
+ */
+void ReMD5::processChunk2(const uint8_t block[RE_DIGEST_CHUNK_SIZE]) {
+ uint32_t M[16];
+ // break chunk into sixteen 32-bit words M[j], 0 ≤ j ≤ 15
+ for (int ix = 0; ix < 16; ix++) {
+ uint32_t x = block[3];
+ for (int jj = 2; jj >= 0; jj--) {
+ x = (x << 8) + block[jj];
+ }
+ M[ix] = x;
+ block += 4;
+ }
+ //Initialize hash value for this chunk:
+ uint32_t A = m_a0;
+ uint32_t B = m_b0;
+ uint32_t C = m_c0;
+ uint32_t D = m_d0;
+ //Main loop:
+
+ int F, g;
+//#define TRACE_MD5
+#if defined TRACE_MD5
+ printf("neu: (%s)\n", block);
+#endif
+ for (int i = 0; i < RE_DIGEST_CHUNK_SIZE; i++) {
+#if defined TRACE_MD5
+ if (i < 8)
+ printf("%2d: A: %08x B: %08x C: %08x D%08x\n", i, A, B, C, D);
+#endif
+ if (i < 16) {
+# define F1(B, C, D) ((B & C) | (~ B & D))
+ // F := (B and C) or ((not B) and D)
+ F = F1(B, C, D);
+ g = i;
+ } else if (i < 32) {
+ // F := (D and B) or (C and (not D))
+ // g := (5×i + 1) mod 16
+# define F2(B, C, D) ((D & B) | (C & ~ D))
+ F = F2(B, C, D);
+ g = (5 * i + 1) % 16;
+ } else if (i < 48) {
+ // F := B xor C xor D
+ // g := (3×i + 5) mod 16
+# define F3(B, C, D) ((B ^ C) ^ D)
+ F = F3(B, C, D);
+ g = (3 * i + 5) % 16;
+ } else {
+ // F := C xor (B or (not D))
+# define F4(B, C, D) (C ^ (B | ~ D))
+ // g := (7×i) mod 16
+ F = F4(B, C, D);
+ g = (7 * i) % 16;
+ }
+#if defined TRACE_MD5
+ if (i < 8)
+ printf(" K[%2d]: %08x M[%2d]: %08x shift: %02d\n",
+ i, m_K[i], g, M[g], m_s[i]);
+#endif
+ uint32_t dTemp = D;
+ D = C;
+ C = B;
+ // B := B + leftrotate((A + F + K[i] + M[g]), s[i])
+ uint32_t x = (A + F + m_K[i] + M[g]);
+ int shift = m_s[i];
+ B += (x << shift) | (x >> (32 - shift));
+ A = dTemp;
+ }
+ //Add this chunk's hash to result so far:
+ m_a0 += A;
+ m_b0 += B;
+ m_c0 += C;
+ m_d0 += D;
+}
+/** ----------------------
+ */
+#if defined OPTIMIZER_WORKS_GREAT
+inline void rotate_left_and_add(uint32_t& rc, uint32_t data, int shift, uint32_t term) {
+ rc = ((data << shift) | (data >> (32-shift))) + term;
+}
+//#define TRACE_MD5
+#if defined TRACE_MD5
+static int s_ix = 0;
+#endif
+inline void X1(uint32_t &var, uint32_t x, uint32_t y, uint32_t z,
+ uint32_t data, uint32_t aConst, uint32_t shift) {
+#if defined TRACE_MD5
+ printf("%2d: A: %08x B: %08x C: %08x D%08x\n", s_ix++ % 16, var, x, y, z);
+ printf(" K[%2d]: %08x M[?]: %08x shift: %02d\n",
+ s_ix - 1, aConst, data, shift);
+#endif
+ rotate_left_and_add(var, var + F1(x, y, z) + data + aConst, shift, x);
+}
+inline void X2(uint32_t& var, uint32_t x, uint32_t y, uint32_t z,
+ uint32_t data, uint32_t aConst, uint32_t shift) {
+#if defined TRACE_MD5
+ printf("%2d: A: %08x B: %08x C: %08x D%08x\n", s_ix++ % 16, var, x, y, z);
+ printf(" K[%2d]: %08x M[?]: %08x shift: %02d\n",
+ s_ix - 1, aConst, data, shift);
+#endif
+ rotate_left_and_add(var, var + F2(x, y, z) + data + aConst, shift, x);
+}
+
+inline void X3(uint32_t& var, uint32_t x, uint32_t y, uint32_t z,
+ uint32_t data, uint32_t aConst, uint32_t shift) {
+#if defined TRACE_MD5
+ printf("%2d: A: %08x B: %08x C: %08x D%08x\n", s_ix++ % 16, var, x, y, z);
+ printf(" K[%2d]: %08x M[?]: %08x shift: %02d\n",
+ s_ix - 1, aConst, data, shift);
+#endif
+ rotate_left_and_add(var, var + F3(x, y, z) + data + aConst, shift, x);
+}
+
+inline void X4(uint32_t& var, uint32_t x, uint32_t y, uint32_t z,
+ uint32_t data, uint32_t aConst, uint32_t shift) {
+#if defined TRACE_MD5
+ printf("%2d: A: %08x B: %08x C: %08x D%08x\n", s_ix++ % 16, var, x, y, z);
+ printf(" K[%2d]: %08x M[?]: %08x shift: %02d\n",
+ s_ix - 1, aConst, data, shift);
+#endif
+ rotate_left_and_add(var, var + F4(x, y, z) + data + aConst, shift, x);
+}
+#else
#define rotate_left_and_add(var, data, shift, term) { \\r
uint32_t val = data; \\r
var = ((val << shift) | (val >> (32-shift))) + term; \\r
-}\r
+}
#define X1(var, x, y, z, data, aConst, shift) \\r
- rotate_left_and_add(var, var + F1(x, y, z) + data + aConst, shift, x)\r
+ rotate_left_and_add(var, var + F1(x, y, z) + data + aConst, shift, x)
#define X2(var, x, y, z, data, aConst, shift) \\r
- rotate_left_and_add(var, var + F2(x, y, z) + data + aConst, shift, x)\r
+ rotate_left_and_add(var, var + F2(x, y, z) + data + aConst, shift, x)
#define X3(var, x, y, z, data, aConst, shift) \\r
- rotate_left_and_add(var, var + F3(x, y, z) + data + aConst, shift, x)\r
+ rotate_left_and_add(var, var + F3(x, y, z) + data + aConst, shift, x)
#define X4(var, x, y, z, data, aConst, shift) \\r
- rotate_left_and_add(var, var + F4(x, y, z) + data + aConst, shift, x)\r
-#endif /* OPTIMIZER_WORKS_GREAT */\r
-\r
-/**\r
- * Processes a 512 bit block ("chunk").\r
- *\r
- * This is a optimized version, derived from the method above.\r
- * We unroll the loop, this brings speed with factor 2.\r
- * <pre>\r
- * B := B + leftrotate((A + F + K[i] + M[g]), s[i])\r
- * D := C;\r
- * C := B;\r
- * B := B + leftrotate((A + F + K[i] + M[g]), s[i])\r
- * A := D(old)\r
- * (D, C, B, A) = (C, B, B + leftrotate((A + F + K[i] + M[g]), s[i]), D)\r
- * ==> (A, B, C, D) = (D, B + leftrotate((A + F + K[i] + M[g]), s[i]), B, A)\r
- * The unrolled loop:\r
- * i = g = 0;\r
- * (A, B, C, D) = (D, B + leftrotate((A + F1(B, C, D) + K[0] + M[0]), s[0]), B, A)\r
- * only one var must be calculated, the other 3 are exchanged only.\r
- * i = g = 1;\r
- * (A, B, C, D) = (D, B + leftrotate((A + F1(B, C, D) + K[1] + M[1]), s[1]), B, A)\r
- * i = g = 2;\r
- * (A, B, C, D) = (D, B + leftrotate((A + F1(B, C, D) + K[2] + M[2]), s[2]), B, A)\r
- * i = g = 3;\r
- * (A, B, C, D) = (D, B + leftrotate((A + F1(B, C, D) + K[3] + M[3]), s[3]), B, A)\r
- * in each of the 4 statements another variable (of A, B, C and D) will be calculated\r
- * so we do not exchange in each step, we calculate in the end position\r
- * we define a function to do this:\r
- * void X1(uint32_t &var, uint32_t x, uint32_t y, uint32_t z, uint32_t data, uint32_t shift, uint32_t aConst){\r
- * var = rotate_left(var+ F1(x, y, z) + data + aConst, shift) + x;\r
- * }\r
- * Note: the input parameter of X1 must respect the exchange:\r
- * A -> D -> C -> B ...\r
- * X1(A, B, C, D, M[0], K[0], s[0]);\r
- * X1(D, A, B, C, M[1], K[1], s[1]);\r
- * X1(C, D, A, B, M[2], K[2], s[2]);\r
- * X1(B, C, D, A, M[3], K[3], s[3]);\r
- * ...\r
- * </pre>\r
- */\r
-void ReMD5::processChunk(const uint8_t block[RE_DIGEST_CHUNK_SIZE]) {\r
- uint32_t M[16];\r
- // break chunk into sixteen 32-bit words M[j], 0 ≤ j ≤ 15\r
-#ifdef __LITTLE_ENDIAN__\r
- for (int ix = 0; ix < 16; ix++) {\r
- //memcpy(&M[ix], block + ix * 4, 4);\r
- M[ix] = *(uint32_t*) (block + ix * 4);\r
- }\r
-#elif defined __BIG_ENDIAN__\r
- for (int ix = 0; ix < 16; ix++) {\r
- uint32_t x = block[3];\r
- for (int jj = 2; jj >= 0; jj--) {\r
- x = (x << 8) + block[jj];\r
- }\r
- M[ix] = x;\r
- block += 4;\r
- }\r
-#else\r
-# error "missing __LITTLE_ENDIAN__ or __BIG_ENDIAN__"\r
-#endif\r
- //Initialize hash value for this chunk:\r
- uint32_t A = m_a0;\r
- uint32_t B = m_b0;\r
- uint32_t C = m_c0;\r
- uint32_t D = m_d0;\r
-#if defined NeverAndNeverAndNeverAgain\r
- // Derivation of the optimization:\r
-\r
-#endif\r
- /* Round 1 */\r
- X1(A, B, C, D, M[0], 0xd76aa478, 7);\r
- X1(D, A, B, C, M[1], 0xe8c7b756, 12);\r
- X1(C, D, A, B, M[2], 0x242070db, 17);\r
- X1(B, C, D, A, M[3], 0xc1bdceee, 22);\r
- X1(A, B, C, D, M[4], 0xf57c0faf, 7);\r
- X1(D, A, B, C, M[5], 0x4787c62a, 12);\r
- X1(C, D, A, B, M[6], 0xa8304613, 17);\r
- X1(B, C, D, A, M[7], 0xfd469501, 22);\r
- X1(A, B, C, D, M[8], 0x698098d8, 7);\r
- X1(D, A, B, C, M[9], 0x8b44f7af, 12);\r
- X1(C, D, A, B, M[10], 0xffff5bb1, 17);\r
- X1(B, C, D, A, M[11], 0x895cd7be, 22);\r
- X1(A, B, C, D, M[12], 0x6b901122, 7);\r
- X1(D, A, B, C, M[13], 0xfd987193, 12);\r
- X1(C, D, A, B, M[14], 0xa679438e, 17);\r
- X1(B, C, D, A, M[15], 0x49b40821, 22);\r
-\r
- /* Round 2 */\r
- X2(A, B, C, D, M[1], 0xf61e2562, 5);\r
- X2(D, A, B, C, M[6], 0xc040b340, 9);\r
- X2(C, D, A, B, M[11], 0x265e5a51, 14);\r
- X2(B, C, D, A, M[0], 0xe9b6c7aa, 20);\r
- X2(A, B, C, D, M[5], 0xd62f105d, 5);\r
- X2(D, A, B, C, M[10], 0x02441453, 9);\r
- X2(C, D, A, B, M[15], 0xd8a1e681, 14);\r
- X2(B, C, D, A, M[4], 0xe7d3fbc8, 20);\r
- X2(A, B, C, D, M[9], 0x21e1cde6, 5);\r
- X2(D, A, B, C, M[14], 0xc33707d6, 9);\r
- X2(C, D, A, B, M[3], 0xf4d50d87, 14);\r
- X2(B, C, D, A, M[8], 0x455a14ed, 20);\r
- X2(A, B, C, D, M[13], 0xa9e3e905, 5);\r
- X2(D, A, B, C, M[2], 0xfcefa3f8, 9);\r
- X2(C, D, A, B, M[7], 0x676f02d9, 14);\r
- X2(B, C, D, A, M[12], 0x8d2a4c8a, 20);\r
-\r
- /* Round 3 */\r
- X3(A, B, C, D, M[5], 0xfffa3942, 4);\r
- X3(D, A, B, C, M[8], 0x8771f681, 11);\r
- X3(C, D, A, B, M[11], 0x6d9d6122, 16);\r
- X3(B, C, D, A, M[14], 0xfde5380c, 23);\r
- X3(A, B, C, D, M[1], 0xa4beea44, 4);\r
- X3(D, A, B, C, M[4], 0x4bdecfa9, 11);\r
- X3(C, D, A, B, M[7], 0xf6bb4b60, 16);\r
- X3(B, C, D, A, M[10], 0xbebfbc70, 23);\r
- X3(A, B, C, D, M[13], 0x289b7ec6, 4);\r
- X3(D, A, B, C, M[0], 0xeaa127fa, 11);\r
- X3(C, D, A, B, M[3], 0xd4ef3085, 16);\r
- X3(B, C, D, A, M[6], 0x04881d05, 23);\r
- X3(A, B, C, D, M[9], 0xd9d4d039, 4);\r
- X3(D, A, B, C, M[12], 0xe6db99e5, 11);\r
- X3(C, D, A, B, M[15], 0x1fa27cf8, 16);\r
- X3(B, C, D, A, M[2], 0xc4ac5665, 23);\r
-\r
- /* Round 4 */\r
- X4(A, B, C, D, M[0], 0xf4292244, 6);\r
- X4(D, A, B, C, M[7], 0x432aff97, 10);\r
- X4(C, D, A, B, M[14], 0xab9423a7, 15);\r
- X4(B, C, D, A, M[5], 0xfc93a039, 21);\r
- X4(A, B, C, D, M[12], 0x655b59c3, 6);\r
- X4(D, A, B, C, M[3], 0x8f0ccc92, 10);\r
- X4(C, D, A, B, M[10], 0xffeff47d, 15);\r
- X4(B, C, D, A, M[1], 0x85845dd1, 21);\r
- X4(A, B, C, D, M[8], 0x6fa87e4f, 6);\r
- X4(D, A, B, C, M[15], 0xfe2ce6e0, 10);\r
- X4(C, D, A, B, M[6], 0xa3014314, 15);\r
- X4(B, C, D, A, M[13], 0x4e0811a1, 21);\r
- X4(A, B, C, D, M[4], 0xf7537e82, 6);\r
- X4(D, A, B, C, M[11], 0xbd3af235, 10);\r
- X4(C, D, A, B, M[2], 0x2ad7d2bb, 15);\r
- X4(B, C, D, A, M[9], 0xeb86d391, 21);\r
-\r
- //Add this chunk's hash to result so far:\r
- m_a0 += A;\r
- m_b0 += B;\r
- m_c0 += C;\r
- m_d0 += D;\r
-}\r
-/**\r
- * Prepares the instance for a new checksum.\r
- */\r
-void ReMD5::reset() {\r
- m_a0 = 0x67452301;\r
- m_b0 = 0xefcdab89;\r
- m_c0 = 0x98badcfe;\r
- m_d0 = 0x10325476;\r
- memset(m_digest, 0, sizeof m_digest);\r
- memset(m_waiting, 0, m_chunkSize);\r
- m_lengthWaiting = 0;\r
- m_length = 0;\r
- m_hexDigest.setLength(0);\r
- m_finalized = false;\r
-}\r
+ rotate_left_and_add(var, var + F4(x, y, z) + data + aConst, shift, x)
+#endif /* OPTIMIZER_WORKS_GREAT */
+
+/**
+ * Processes a 512 bit block ("chunk").
+ *
+ * This is a optimized version, derived from the method above.
+ * We unroll the loop, this brings speed with factor 2.
+ * <pre>
+ * B := B + leftrotate((A + F + K[i] + M[g]), s[i])
+ * D := C;
+ * C := B;
+ * B := B + leftrotate((A + F + K[i] + M[g]), s[i])
+ * A := D(old)
+ * (D, C, B, A) = (C, B, B + leftrotate((A + F + K[i] + M[g]), s[i]), D)
+ * ==> (A, B, C, D) = (D, B + leftrotate((A + F + K[i] + M[g]), s[i]), B, A)
+ * The unrolled loop:
+ * i = g = 0;
+ * (A, B, C, D) = (D, B + leftrotate((A + F1(B, C, D) + K[0] + M[0]), s[0]), B, A)
+ * only one var must be calculated, the other 3 are exchanged only.
+ * i = g = 1;
+ * (A, B, C, D) = (D, B + leftrotate((A + F1(B, C, D) + K[1] + M[1]), s[1]), B, A)
+ * i = g = 2;
+ * (A, B, C, D) = (D, B + leftrotate((A + F1(B, C, D) + K[2] + M[2]), s[2]), B, A)
+ * i = g = 3;
+ * (A, B, C, D) = (D, B + leftrotate((A + F1(B, C, D) + K[3] + M[3]), s[3]), B, A)
+ * in each of the 4 statements another variable (of A, B, C and D) will be calculated
+ * so we do not exchange in each step, we calculate in the end position
+ * we define a function to do this:
+ * void X1(uint32_t &var, uint32_t x, uint32_t y, uint32_t z, uint32_t data, uint32_t shift, uint32_t aConst){
+ * var = rotate_left(var+ F1(x, y, z) + data + aConst, shift) + x;
+ * }
+ * Note: the input parameter of X1 must respect the exchange:
+ * A -> D -> C -> B ...
+ * X1(A, B, C, D, M[0], K[0], s[0]);
+ * X1(D, A, B, C, M[1], K[1], s[1]);
+ * X1(C, D, A, B, M[2], K[2], s[2]);
+ * X1(B, C, D, A, M[3], K[3], s[3]);
+ * ...
+ * </pre>
+ */
+void ReMD5::processChunk(const uint8_t block[RE_DIGEST_CHUNK_SIZE]) {
+ uint32_t M[16];
+ // break chunk into sixteen 32-bit words M[j], 0 ≤ j ≤ 15
+#ifdef __LITTLE_ENDIAN__
+ for (int ix = 0; ix < 16; ix++) {
+ //memcpy(&M[ix], block + ix * 4, 4);
+ M[ix] = *(uint32_t*) (block + ix * 4);
+ }
+#elif defined __BIG_ENDIAN__
+ for (int ix = 0; ix < 16; ix++) {
+ uint32_t x = block[3];
+ for (int jj = 2; jj >= 0; jj--) {
+ x = (x << 8) + block[jj];
+ }
+ M[ix] = x;
+ block += 4;
+ }
+#else
+# error "missing __LITTLE_ENDIAN__ or __BIG_ENDIAN__"
+#endif
+ //Initialize hash value for this chunk:
+ uint32_t A = m_a0;
+ uint32_t B = m_b0;
+ uint32_t C = m_c0;
+ uint32_t D = m_d0;
+#if defined NeverAndNeverAndNeverAgain
+ // Derivation of the optimization:
+
+#endif
+ /* Round 1 */
+ X1(A, B, C, D, M[0], 0xd76aa478, 7);
+ X1(D, A, B, C, M[1], 0xe8c7b756, 12);
+ X1(C, D, A, B, M[2], 0x242070db, 17);
+ X1(B, C, D, A, M[3], 0xc1bdceee, 22);
+ X1(A, B, C, D, M[4], 0xf57c0faf, 7);
+ X1(D, A, B, C, M[5], 0x4787c62a, 12);
+ X1(C, D, A, B, M[6], 0xa8304613, 17);
+ X1(B, C, D, A, M[7], 0xfd469501, 22);
+ X1(A, B, C, D, M[8], 0x698098d8, 7);
+ X1(D, A, B, C, M[9], 0x8b44f7af, 12);
+ X1(C, D, A, B, M[10], 0xffff5bb1, 17);
+ X1(B, C, D, A, M[11], 0x895cd7be, 22);
+ X1(A, B, C, D, M[12], 0x6b901122, 7);
+ X1(D, A, B, C, M[13], 0xfd987193, 12);
+ X1(C, D, A, B, M[14], 0xa679438e, 17);
+ X1(B, C, D, A, M[15], 0x49b40821, 22);
+
+ /* Round 2 */
+ X2(A, B, C, D, M[1], 0xf61e2562, 5);
+ X2(D, A, B, C, M[6], 0xc040b340, 9);
+ X2(C, D, A, B, M[11], 0x265e5a51, 14);
+ X2(B, C, D, A, M[0], 0xe9b6c7aa, 20);
+ X2(A, B, C, D, M[5], 0xd62f105d, 5);
+ X2(D, A, B, C, M[10], 0x02441453, 9);
+ X2(C, D, A, B, M[15], 0xd8a1e681, 14);
+ X2(B, C, D, A, M[4], 0xe7d3fbc8, 20);
+ X2(A, B, C, D, M[9], 0x21e1cde6, 5);
+ X2(D, A, B, C, M[14], 0xc33707d6, 9);
+ X2(C, D, A, B, M[3], 0xf4d50d87, 14);
+ X2(B, C, D, A, M[8], 0x455a14ed, 20);
+ X2(A, B, C, D, M[13], 0xa9e3e905, 5);
+ X2(D, A, B, C, M[2], 0xfcefa3f8, 9);
+ X2(C, D, A, B, M[7], 0x676f02d9, 14);
+ X2(B, C, D, A, M[12], 0x8d2a4c8a, 20);
+
+ /* Round 3 */
+ X3(A, B, C, D, M[5], 0xfffa3942, 4);
+ X3(D, A, B, C, M[8], 0x8771f681, 11);
+ X3(C, D, A, B, M[11], 0x6d9d6122, 16);
+ X3(B, C, D, A, M[14], 0xfde5380c, 23);
+ X3(A, B, C, D, M[1], 0xa4beea44, 4);
+ X3(D, A, B, C, M[4], 0x4bdecfa9, 11);
+ X3(C, D, A, B, M[7], 0xf6bb4b60, 16);
+ X3(B, C, D, A, M[10], 0xbebfbc70, 23);
+ X3(A, B, C, D, M[13], 0x289b7ec6, 4);
+ X3(D, A, B, C, M[0], 0xeaa127fa, 11);
+ X3(C, D, A, B, M[3], 0xd4ef3085, 16);
+ X3(B, C, D, A, M[6], 0x04881d05, 23);
+ X3(A, B, C, D, M[9], 0xd9d4d039, 4);
+ X3(D, A, B, C, M[12], 0xe6db99e5, 11);
+ X3(C, D, A, B, M[15], 0x1fa27cf8, 16);
+ X3(B, C, D, A, M[2], 0xc4ac5665, 23);
+
+ /* Round 4 */
+ X4(A, B, C, D, M[0], 0xf4292244, 6);
+ X4(D, A, B, C, M[7], 0x432aff97, 10);
+ X4(C, D, A, B, M[14], 0xab9423a7, 15);
+ X4(B, C, D, A, M[5], 0xfc93a039, 21);
+ X4(A, B, C, D, M[12], 0x655b59c3, 6);
+ X4(D, A, B, C, M[3], 0x8f0ccc92, 10);
+ X4(C, D, A, B, M[10], 0xffeff47d, 15);
+ X4(B, C, D, A, M[1], 0x85845dd1, 21);
+ X4(A, B, C, D, M[8], 0x6fa87e4f, 6);
+ X4(D, A, B, C, M[15], 0xfe2ce6e0, 10);
+ X4(C, D, A, B, M[6], 0xa3014314, 15);
+ X4(B, C, D, A, M[13], 0x4e0811a1, 21);
+ X4(A, B, C, D, M[4], 0xf7537e82, 6);
+ X4(D, A, B, C, M[11], 0xbd3af235, 10);
+ X4(C, D, A, B, M[2], 0x2ad7d2bb, 15);
+ X4(B, C, D, A, M[9], 0xeb86d391, 21);
+
+ //Add this chunk's hash to result so far:
+ m_a0 += A;
+ m_b0 += B;
+ m_c0 += C;
+ m_d0 += D;
+}
+/**
+ * Prepares the instance for a new checksum.
+ */
+void ReMD5::reset() {
+ m_a0 = 0x67452301;
+ m_b0 = 0xefcdab89;
+ m_c0 = 0x98badcfe;
+ m_d0 = 0x10325476;
+ memset(m_digest, 0, sizeof m_digest);
+ memset(m_waiting, 0, m_chunkSize);
+ m_lengthWaiting = 0;
+ m_length = 0;
+ m_hexDigest.setLength(0);
+ m_finalized = false;
+}
/*
* 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_
/*
* 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
*/
/*
* 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
*/
-/*\r
- * ReRPD64.cpp\r
- *\r
- * Created on: 30.01.2015\r
- *\r
- */\r
-\r
-#include "base/rebase.hpp"\r
-#include "math/remath.hpp"\r
-\r
-/**\r
- * Constructor.\r
- */\r
-ReRPD64::ReRPD64() :\r
- ReDigest(m_digest, sizeof m_digest),\r
- //m_digest\r
- m_a0(0),\r
- m_b0(0),\r
- m_c0(0),\r
- m_d0(0) {\r
- reset();\r
-}\r
-\r
-/**\r
- * Destructor.\r
- */\r
-ReRPD64::~ReRPD64() {\r
-}\r
-\r
-/**\r
- * Finalizes the digest.\r
- *\r
- * Handles the rest block (< 64 byte) and append a special tail: a "1" bit\r
- * and the length of the total input length (in bits).\r
- *\r
- * @param block the rest input (incomplete chunk)\r
- * @param blockLength the length of the block: 0..63\r
- */\r
-void ReRPD64::finalize() {\r
- uint8_t* block = m_waiting;\r
- int blockLength = m_lengthWaiting;\r
- // append "1" bit to message\r
- // Notice: the input bytes are considered as bits strings,\r
- // where the first bit is the most significant bit of the byte.\r
- block[blockLength++] = 0x80;\r
- //Pre-processing: padding with zeros\r
- //append "0" bit until message length in bits ≡ 448 (mod 512)\r
- // fill the rest of the chunk with '\0'.\r
- // the last 8 bytes is set to the length in bits (length in bytes * 8)\r
- int restLength = (m_length + 1) % 64;\r
- // 0 -> 56, 1 -> 55, 2 -> 54, ... 55 -> 1, 56 -> 0,\r
- // 57 -> 63, 58 -> 62, ... 63 -> 57\r
- int zeros = restLength <= 56 ? 56 - restLength : 120 - restLength;\r
- memset(block + blockLength, 0, zeros);\r
- blockLength += zeros;\r
- //append original length in bits mod (2 pow 64) to message\r
- uint64_t lengthBits = 8LL * m_length + m_salt;\r
-#if defined __LITTLE_ENDIAN__\r
- memcpy(block + blockLength, &lengthBits, 8);\r
- blockLength += 8;\r
-#else\r
- block[blockLength++] = lengthBits;\r
- lengthBits >>= 8;\r
- block[blockLength++] = lengthBits;\r
- lengthBits >>= 8;\r
- block[blockLength++] = lengthBits;\r
- lengthBits >>= 8;\r
- block[blockLength++] = lengthBits;\r
- lengthBits >>= 8;\r
- block[blockLength++] = lengthBits;\r
- lengthBits >>= 8;\r
- block[blockLength++] = lengthBits;\r
- lengthBits >>= 8;\r
- block[blockLength++] = lengthBits;\r
- lengthBits >>= 8;\r
- block[blockLength++] = lengthBits;\r
-#endif\r
- processChunk(block);\r
- if (blockLength > 64)\r
- processChunk(block + 64);\r
- m_a0 ^= m_b0;\r
- m_c0 ^= m_d0;\r
-#if defined __LITTLE_ENDIAN__\r
- memcpy(m_digest, &m_a0, 8);\r
- memcpy(m_digest + 8, &m_c0, 8);\r
-#else\r
+/*
+ * ReRPD64.cpp
+ *
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
+ * Do what you want.
+ * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
+ * The latest sources: https://github.com/republib
+ */
+
+#include "base/rebase.hpp"
+#include "math/remath.hpp"
+
+/**
+ * Constructor.
+ */
+ReRPD64::ReRPD64() :
+ ReDigest(m_digest, sizeof m_digest),
+ //m_digest
+ m_a0(0),
+ m_b0(0),
+ m_c0(0),
+ m_d0(0) {
+ reset();
+}
+
+/**
+ * Destructor.
+ */
+ReRPD64::~ReRPD64() {
+}
+
+/**
+ * Finalizes the digest.
+ *
+ * Handles the rest block (< 64 byte) and append a special tail: a "1" bit
+ * and the length of the total input length (in bits).
+ *
+ * @param block the rest input (incomplete chunk)
+ * @param blockLength the length of the block: 0..63
+ */
+void ReRPD64::finalize() {
+ uint8_t* block = m_waiting;
+ int blockLength = m_lengthWaiting;
+ // append "1" bit to message
+ // Notice: the input bytes are considered as bits strings,
+ // where the first bit is the most significant bit of the byte.
+ block[blockLength++] = 0x80;
+ //Pre-processing: padding with zeros
+ //append "0" bit until message length in bits ≡ 448 (mod 512)
+ // fill the rest of the chunk with '\0'.
+ // the last 8 bytes is set to the length in bits (length in bytes * 8)
+ int restLength = (m_length + 1) % 64;
+ // 0 -> 56, 1 -> 55, 2 -> 54, ... 55 -> 1, 56 -> 0,
+ // 57 -> 63, 58 -> 62, ... 63 -> 57
+ int zeros = restLength <= 56 ? 56 - restLength : 120 - restLength;
+ memset(block + blockLength, 0, zeros);
+ blockLength += zeros;
+ //append original length in bits mod (2 pow 64) to message
+ uint64_t lengthBits = 8LL * m_length + m_salt;
+#if defined __LITTLE_ENDIAN__
+ memcpy(block + blockLength, &lengthBits, 8);
+ blockLength += 8;
+#else
+ block[blockLength++] = lengthBits;
+ lengthBits >>= 8;
+ block[blockLength++] = lengthBits;
+ lengthBits >>= 8;
+ block[blockLength++] = lengthBits;
+ lengthBits >>= 8;
+ block[blockLength++] = lengthBits;
+ lengthBits >>= 8;
+ block[blockLength++] = lengthBits;
+ lengthBits >>= 8;
+ block[blockLength++] = lengthBits;
+ lengthBits >>= 8;
+ block[blockLength++] = lengthBits;
+ lengthBits >>= 8;
+ block[blockLength++] = lengthBits;
+#endif
+ processChunk(block);
+ if (blockLength > 64)
+ processChunk(block + 64);
+ m_a0 ^= m_b0;
+ m_c0 ^= m_d0;
+#if defined __LITTLE_ENDIAN__
+ memcpy(m_digest, &m_a0, 8);
+ memcpy(m_digest + 8, &m_c0, 8);
+#else
#define oneQWord(word, ix) m_digest[ix] = word; word >>= 8; \\r
m_digest[ix + 1] = word; word >>= 8; m_digest[ix + 2] = word; word >>= 8; \\r
m_digest[ix + 3] = word; word >>= 8; m_digest[ix + 4] = word; word >>= 8; \\r
m_digest[ix + 4] = word; word >>= 8; m_digest[ix + 5] = word; word >>= 8; \\r
- m_digest[ix + 6] = word; word >>= 8; m_digest[ix + 7] = word\r
- oneWord(m_a0, 0);\r
- oneWord(m_c0, 8);\r
-#endif\r
-}\r
-\r
-#define F1(B, C, D) ((B & C) | (~ B & D))\r
-#define F2(B, C, D) ((D & B) | (C & ~ D))\r
-#define F3(B, C, D) ((B ^ C) ^ D)\r
-#define F4(B, C, D) (C ^ (B | ~ D))\r
-\r
-#if defined OPTIMIZER_WORKS_GREAT\r
-inline void rotate_left_and_add(uint64_t& rc, uint64_t data, int shift, uint64_t term) {\r
- rc = ((data << shift) | (data >> (64-shift))) + term;\r
-}\r
-inline void X1(uint64_t &var, uint64_t x, uint64_t y, uint64_t z,\r
- uint64_t data, uint64_t aConst, uint64_t shift) {\r
- rotate_left_and_add(var, var + F1(x, y, z) + data + aConst, shift, x);\r
-}\r
-inline void X2(uint64_t& var, uint64_t x, uint64_t y, uint64_t z,\r
- uint64_t data, uint64_t aConst, uint64_t shift) {\r
- rotate_left_and_add(var, var + F2(x, y, z) + data + aConst, shift, x);\r
-}\r
-\r
-inline void X3(uint64_t& var, uint64_t x, uint64_t y, uint64_t z,\r
- uint64_t data, uint64_t aConst, uint64_t shift) {\r
- rotate_left_and_add(var, var + F3(x, y, z) + data + aConst, shift, x);\r
-}\r
-\r
-inline void X4(uint64_t& var, uint64_t x, uint64_t y, uint64_t z,\r
- uint64_t data, uint64_t aConst, uint64_t shift) {\r
- rotate_left_and_add(var, var + F4(x, y, z) + data + aConst, shift, x);\r
-}\r
-#else\r
+ m_digest[ix + 6] = word; word >>= 8; m_digest[ix + 7] = word
+ oneWord(m_a0, 0);
+ oneWord(m_c0, 8);
+#endif
+}
+
+#define F1(B, C, D) ((B & C) | (~ B & D))
+#define F2(B, C, D) ((D & B) | (C & ~ D))
+#define F3(B, C, D) ((B ^ C) ^ D)
+#define F4(B, C, D) (C ^ (B | ~ D))
+
+#if defined OPTIMIZER_WORKS_GREAT
+inline void rotate_left_and_add(uint64_t& rc, uint64_t data, int shift, uint64_t term) {
+ rc = ((data << shift) | (data >> (64-shift))) + term;
+}
+inline void X1(uint64_t &var, uint64_t x, uint64_t y, uint64_t z,
+ uint64_t data, uint64_t aConst, uint64_t shift) {
+ rotate_left_and_add(var, var + F1(x, y, z) + data + aConst, shift, x);
+}
+inline void X2(uint64_t& var, uint64_t x, uint64_t y, uint64_t z,
+ uint64_t data, uint64_t aConst, uint64_t shift) {
+ rotate_left_and_add(var, var + F2(x, y, z) + data + aConst, shift, x);
+}
+
+inline void X3(uint64_t& var, uint64_t x, uint64_t y, uint64_t z,
+ uint64_t data, uint64_t aConst, uint64_t shift) {
+ rotate_left_and_add(var, var + F3(x, y, z) + data + aConst, shift, x);
+}
+
+inline void X4(uint64_t& var, uint64_t x, uint64_t y, uint64_t z,
+ uint64_t data, uint64_t aConst, uint64_t shift) {
+ rotate_left_and_add(var, var + F4(x, y, z) + data + aConst, shift, x);
+}
+#else
#define rotate_left_and_add(var, data, shift, term) { \\r
uint64_t val = data; \\r
var = ((val << (shift)) | (val >> (64-(shift)))) + term; \\r
-}\r
+}
#define X1(var, x, y, z, data, aConst, shift) \\r
- rotate_left_and_add(var, var + F1(x, y, z) + data + aConst, shift, x)\r
+ rotate_left_and_add(var, var + F1(x, y, z) + data + aConst, shift, x)
#define X2(var, x, y, z, data, aConst, shift) \\r
- rotate_left_and_add(var, var + F2(x, y, z) + data + aConst, shift, x)\r
+ rotate_left_and_add(var, var + F2(x, y, z) + data + aConst, shift, x)
#define X3(var, x, y, z, data, aConst, shift) \\r
- rotate_left_and_add(var, var + F3(x, y, z) + data + aConst, shift, x)\r
+ rotate_left_and_add(var, var + F3(x, y, z) + data + aConst, shift, x)
#define X4(var, x, y, z, data, aConst, shift) \\r
- rotate_left_and_add(var, var + F4(x, y, z) + data + aConst, shift, x)\r
-#endif /* OPTIMIZER_WORKS_GREAT */\r
-\r
-/**\r
- * Processes a 512 bit block ("chunk").\r
- *\r
- */\r
-void ReRPD64::processChunk(const uint8_t block[64]) {\r
- uint64_t M[8];\r
- // break chunk into sixteen 32-bit words M[j], 0 ≤ j ≤ 15\r
-#ifdef __LITTLE_ENDIAN__\r
- for (int ix = 0; ix < 8; ix++) {\r
- //memcpy(&M[ix], block + ix * 8, 8);\r
- M[ix] = *(uint64_t*) (block + ix * 8);\r
- }\r
-#elif defined __BIG_ENDIAN__\r
- for (int ix = 0; ix < 16; ix++) {\r
- uint64_t x = block[8-1];\r
- for (int jj = 8 - 2; jj >= 0; jj--) {\r
- x = (x << 8) + block[jj];\r
- }\r
- M[ix] = x;\r
- block += 8;\r
- }\r
-#else\r
-# error "missing __LITTLE_ENDIAN__ or __BIG_ENDIAN__"\r
-#endif\r
- //Initialize hash value for this chunk:\r
- uint64_t A = m_a0;\r
- uint64_t B = m_b0;\r
- uint64_t C = m_c0;\r
- uint64_t D = m_d0;\r
- /* Round 1 */\r
- X1(A, B, C, D, M[0], 0xd76aa478698098d8ll, 7);\r
- X1(D, A, B, C, M[1], 0xe8c7b7568b44f7afll, 12);\r
- X1(C, D, A, B, M[2], 0x242070dbffff5bb1ll, 17);\r
- X1(B, C, D, A, M[3], 0xc1bdceee895cd7bell, 22);\r
- X1(A, B, C, D, M[4], 0xf57c0faf6b901122ll, 32 + 7);\r
- X1(D, A, B, C, M[5], 0x4787c62afd987193ll, 32 + 12);\r
- X1(C, D, A, B, M[6], 0xa8304613a679438ell, 32 + 17);\r
- X1(B, C, D, A, M[7], 0xfd46950149b40821ll, 32 + 22);\r
-\r
- /* Round 2 */\r
- X2(A, B, C, D, M[1], 0xf61e256221e1cde6ll, 5);\r
- X2(D, A, B, C, M[6], 0xc040b340c33707d6ll, 9);\r
- X2(C, D, A, B, M[3], 0x265e5a51f4d50d87ll, 14);\r
- X2(B, C, D, A, M[0], 0xe9b6c7aa455a14edll, 20);\r
- X2(A, B, C, D, M[5], 0xd62f105da9e3e905ll, 5 + 32);\r
- X2(D, A, B, C, M[2], 0x02441453fcefa3f8ll, 9 + 32);\r
- X2(C, D, A, B, M[7], 0xd8a1e681676f02d9ll, 14 + 32);\r
- X2(B, C, D, A, M[4], 0xe7d3fbc88d2a4c8all, 20 + 32);\r
-\r
- /* Round 3 */\r
- X3(A, B, C, D, M[5], 0xfffa3942289b7ec6ll, 4);\r
- X3(D, A, B, C, M[2], 0x8771f681eaa127fall, 11);\r
- X3(C, D, A, B, M[7], 0x6d9d6122d4ef3085ll, 16);\r
- X3(B, C, D, A, M[4], 0xfde5380c04881d05ll, 23);\r
- X3(A, B, C, D, M[1], 0xa4beea44d9d4d039ll, 4 + 32);\r
- X3(D, A, B, C, M[6], 0x4bdecfa9e6db99e5ll, 11 + 32);\r
- X3(C, D, A, B, M[3], 0xf6bb4b601fa27cf8ll, 16 + 32);\r
- X3(B, C, D, A, M[0], 0xbebfbc70c4ac5665ll, 23 + 32);\r
-\r
- /* Round 4 */\r
- X4(A, B, C, D, M[0], 0xf42922446fa87e4fll, 6);\r
- X4(D, A, B, C, M[7], 0x432aff97fe2ce6e0ll, 10);\r
- X4(C, D, A, B, M[4], 0xab9423a7a3014314ll, 15);\r
- X4(B, C, D, A, M[1], 0xfc93a0394e0811a1ll, 21);\r
- X4(A, B, C, D, M[6], 0x655b59c3f7537e82ll, 6 + 32);\r
- X4(D, A, B, C, M[3], 0x8f0ccc92bd3af235ll, 10 + 32);\r
- X4(C, D, A, B, M[0], 0xffeff47d2ad7d2bbll, 15 + 32);\r
- X4(B, C, D, A, M[5], 0x85845dd1eb86d391ll, 21 + 32);\r
-\r
- //Add this chunk's hash to result so far:\r
- m_a0 += A;\r
- m_b0 += B;\r
- m_c0 += C;\r
- m_d0 += D;\r
-}\r
-/**\r
- * Prepares the instance for a new checksum.\r
- */\r
-void ReRPD64::reset() {\r
- m_a0 = 0xd76aa478698098d8ll;\r
- m_b0 = 0xe8c7b7568b44f7afll;\r
- m_c0 = 0x242070dbffff5bb1ll;\r
- m_d0 = 0xc1bdceee895cd7bell;\r
- memset(m_digest, 0, sizeof m_digest);\r
- memset(m_waiting, 0, m_chunkSize * 2);\r
- m_lengthWaiting = 0;\r
- m_length = 0;\r
- m_hexDigest.setLength(0);\r
- m_finalized = false;\r
-}\r
-\r
+ rotate_left_and_add(var, var + F4(x, y, z) + data + aConst, shift, x)
+#endif /* OPTIMIZER_WORKS_GREAT */
+
+/**
+ * Processes a 512 bit block ("chunk").
+ *
+ */
+void ReRPD64::processChunk(const uint8_t block[64]) {
+ uint64_t M[8];
+ // break chunk into sixteen 32-bit words M[j], 0 ≤ j ≤ 15
+#ifdef __LITTLE_ENDIAN__
+ for (int ix = 0; ix < 8; ix++) {
+ //memcpy(&M[ix], block + ix * 8, 8);
+ M[ix] = *(uint64_t*) (block + ix * 8);
+ }
+#elif defined __BIG_ENDIAN__
+ for (int ix = 0; ix < 16; ix++) {
+ uint64_t x = block[8-1];
+ for (int jj = 8 - 2; jj >= 0; jj--) {
+ x = (x << 8) + block[jj];
+ }
+ M[ix] = x;
+ block += 8;
+ }
+#else
+# error "missing __LITTLE_ENDIAN__ or __BIG_ENDIAN__"
+#endif
+ //Initialize hash value for this chunk:
+ uint64_t A = m_a0;
+ uint64_t B = m_b0;
+ uint64_t C = m_c0;
+ uint64_t D = m_d0;
+ /* Round 1 */
+ X1(A, B, C, D, M[0], 0xd76aa478698098d8ll, 7);
+ X1(D, A, B, C, M[1], 0xe8c7b7568b44f7afll, 12);
+ X1(C, D, A, B, M[2], 0x242070dbffff5bb1ll, 17);
+ X1(B, C, D, A, M[3], 0xc1bdceee895cd7bell, 22);
+ X1(A, B, C, D, M[4], 0xf57c0faf6b901122ll, 32 + 7);
+ X1(D, A, B, C, M[5], 0x4787c62afd987193ll, 32 + 12);
+ X1(C, D, A, B, M[6], 0xa8304613a679438ell, 32 + 17);
+ X1(B, C, D, A, M[7], 0xfd46950149b40821ll, 32 + 22);
+
+ /* Round 2 */
+ X2(A, B, C, D, M[1], 0xf61e256221e1cde6ll, 5);
+ X2(D, A, B, C, M[6], 0xc040b340c33707d6ll, 9);
+ X2(C, D, A, B, M[3], 0x265e5a51f4d50d87ll, 14);
+ X2(B, C, D, A, M[0], 0xe9b6c7aa455a14edll, 20);
+ X2(A, B, C, D, M[5], 0xd62f105da9e3e905ll, 5 + 32);
+ X2(D, A, B, C, M[2], 0x02441453fcefa3f8ll, 9 + 32);
+ X2(C, D, A, B, M[7], 0xd8a1e681676f02d9ll, 14 + 32);
+ X2(B, C, D, A, M[4], 0xe7d3fbc88d2a4c8all, 20 + 32);
+
+ /* Round 3 */
+ X3(A, B, C, D, M[5], 0xfffa3942289b7ec6ll, 4);
+ X3(D, A, B, C, M[2], 0x8771f681eaa127fall, 11);
+ X3(C, D, A, B, M[7], 0x6d9d6122d4ef3085ll, 16);
+ X3(B, C, D, A, M[4], 0xfde5380c04881d05ll, 23);
+ X3(A, B, C, D, M[1], 0xa4beea44d9d4d039ll, 4 + 32);
+ X3(D, A, B, C, M[6], 0x4bdecfa9e6db99e5ll, 11 + 32);
+ X3(C, D, A, B, M[3], 0xf6bb4b601fa27cf8ll, 16 + 32);
+ X3(B, C, D, A, M[0], 0xbebfbc70c4ac5665ll, 23 + 32);
+
+ /* Round 4 */
+ X4(A, B, C, D, M[0], 0xf42922446fa87e4fll, 6);
+ X4(D, A, B, C, M[7], 0x432aff97fe2ce6e0ll, 10);
+ X4(C, D, A, B, M[4], 0xab9423a7a3014314ll, 15);
+ X4(B, C, D, A, M[1], 0xfc93a0394e0811a1ll, 21);
+ X4(A, B, C, D, M[6], 0x655b59c3f7537e82ll, 6 + 32);
+ X4(D, A, B, C, M[3], 0x8f0ccc92bd3af235ll, 10 + 32);
+ X4(C, D, A, B, M[0], 0xffeff47d2ad7d2bbll, 15 + 32);
+ X4(B, C, D, A, M[5], 0x85845dd1eb86d391ll, 21 + 32);
+
+ //Add this chunk's hash to result so far:
+ m_a0 += A;
+ m_b0 += B;
+ m_c0 += C;
+ m_d0 += D;
+}
+/**
+ * Prepares the instance for a new checksum.
+ */
+void ReRPD64::reset() {
+ m_a0 = 0xd76aa478698098d8ll;
+ m_b0 = 0xe8c7b7568b44f7afll;
+ m_c0 = 0x242070dbffff5bb1ll;
+ m_d0 = 0xc1bdceee895cd7bell;
+ memset(m_digest, 0, sizeof m_digest);
+ memset(m_waiting, 0, m_chunkSize * 2);
+ m_lengthWaiting = 0;
+ m_length = 0;
+ m_hexDigest.setLength(0);
+ m_finalized = false;
+}
+
/*
* 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_
/*
* 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
*/
/*
* 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
*/
/*
* 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
*/
-/*\r
- * ReTCP.cpp\r
- *\r
- * Created on: 04.03.2015\r
- * Author: hm\r
- */\r
-\r
-#include "base/rebase.hpp"\r
-#include "net/renet.hpp"\r
-\r
-enum LOCATION_DIRTOOL {\r
- LC_LISTEN_FOR_ALL_1 = LC_TCP + 1, // 50501\r
- LC_LISTEN_FOR_ALL_2, // 50502\r
- LC_LISTEN_FOR_ALL_3, // 50503\r
- LC_RECEIVE_5, // 50504\r
- LC_LISTEN_FOR_ALL_5, // 50505\r
- LC_LISTEN_FOR_ALL_6, // 50506\r
- LC_WRITE_1, // 50507\r
- LC_CONNECT_1, // 50508\r
- LC_SOCKET_ADDR_SET_1, // 50509\r
- LC_LISTEN_FOR_ALL_7, // 50510\r
- LC_HANDLE_CONNECTION_1, // 50511\r
- LC_RECEIVE_1, // 50512\r
- LC_RECEIVE_2, // 50513\r
- LC_RECEIVE_3, // 50514\r
- LC_RECEIVE_4, // 50515\r
- LC_CONNECT_2, // 50516\r
- LC_CONNECT_3, // 50517\r
- LC_TCP_CONNECTION_1, // 50518\r
- LC_WRITE_2, // 50519\r
- LC_SERVER_CONNECTION_RUN_1, // 50520\r
- LC_SERVER_CONNECTION_RUN_2, // 50521\r
- LC_SERVER_CONNECTION_RUN_3, // 50522\r
- LC_HANDLE_CONNECTION_4, // 50523\r
-};\r
-\r
-#if defined __WIN32__\r
-bool ReTCPConnection::isGlobalInitialized = false;\r
-#endif\r
-\r
-/**\r
- * Constructor.\r
- *\r
- * @param message the description of the exception\r
- */\r
-ReTCPException::ReTCPException(const char* message) {\r
-\r
-}\r
-;\r
-\r
-/**\r
- * Constructor.\r
- *\r
- * @param socket the socket which is disconnected\r
- */\r
-ReTCPDisconnectException::ReTCPDisconnectException(int socket) :\r
- ReTCPException(i18n("disconnected")),\r
- m_socket(socket) {\r
-}\r
-\r
-/**\r
- * Constructor.\r
- *\r
- * @param logger the logger for error handling\r
- */\r
-ReSocketAddress::ReSocketAddress(ReLoggerOwner* loggerOwner) :\r
- m_preferredFamily(AF_INET),\r
- m_family(-1),\r
- m_port(0),\r
- m_loggerOwner(loggerOwner),\r
- // m_ip\r
- m_name() {\r
- memset(&m_ip, 0, sizeof m_ip);\r
-}\r
-/**\r
- * Destructor.\r
- */\r
-ReSocketAddress::~ReSocketAddress() {\r
-}\r
-void addressToString(struct addrinfo& addr, char* ip, size_t ipSize) {\r
- if (addr.ai_family == AF_INET) {\r
- inet_ntop(addr.ai_family, (struct sockaddr_in *) addr.ai_addr, ip,\r
- ipSize);\r
- } else if (addr.ai_family == AF_INET6) {\r
- inet_ntop(addr.ai_family, (struct sockaddr_in6 *) addr.ai_addr, ip,\r
- ipSize);\r
- }\r
-}\r
-/**\r
- * Sets the data from symbolic values.\r
- */\r
-void ReSocketAddress::setAddress(const char* ip, int port) {\r
- struct addrinfo hints;\r
- struct addrinfo* infoList;\r
- int status;\r
- m_port = port;\r
- memset(&hints, 0, sizeof hints);\r
- hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version\r
- hints.ai_socktype = SOCK_STREAM;\r
-\r
- if ((status = getaddrinfo(ip, NULL, &hints, &infoList)) != 0) {\r
- m_loggerOwner->logger()->sayF(LOG_ERROR | CAT_NETWORK,\r
- LC_SOCKET_ADDR_SET_1, i18n("getaddrinfo($1) failed: $2")).arg(ip)\r
- .arg(errno).end();\r
- }\r
-\r
- struct addrinfo* ptr;\r
- bool hasIP4 = false;\r
- bool hasIP6 = false;\r
-\r
- // Search for a available ip address\r
- // if more than one are available, prefer m_preferredFamily:\r
- m_ip[0] = '\0';\r
- for (ptr = infoList; ptr != NULL; ptr = ptr->ai_next) {\r
- // different fields in IPv4 and IPv6:\r
- if (!hasIP4 && ptr->ai_family == AF_INET) {\r
- m_family = AF_INET;\r
- hasIP4 = true;\r
- } else if (!hasIP6 && ptr->ai_family == AF_INET6) {\r
- m_family = AF_INET6;\r
- hasIP6 = true;\r
- }\r
- addressToString(*ptr, m_ip, sizeof m_ip);\r
- if (ptr->ai_family == m_preferredFamily)\r
- break;\r
- }\r
- // free the linked list\r
- freeaddrinfo(infoList);\r
- if (m_ip[0] == '\0')\r
- m_name.setLength(0);\r
- else\r
- m_name.set(m_ip).appendChar(':').appendInt(port);\r
-}\r
-\r
-/**\r
- * Constructor.\r
- *\r
- * @param logger logger for the error handling\r
- */\r
-#pragma warning( push )\r
-#pragma warning( disable : 4355 )\r
-ReTCPClient::ReTCPClient(ReLogger* logger) :\r
- ReTCPConnection(-1, this),\r
- m_logger(logger) {\r
-#pragma warning( pop )\r
-}\r
-/**\r
- * Destructor.\r
- */\r
-ReTCPClient::~ReTCPClient() {\r
-}\r
-/**\r
- * Connects a client with the server.\r
- *\r
- * @param ip domain address ("denic.de") or ip address ("192.168.2.1")\r
- * @param port port number: 1..65535\r
- */\r
-bool ReTCPClient::connect(const char* ip, int port) {\r
- bool rc = false;\r
- struct addrinfo hints;\r
- struct addrinfo* addr = NULL;\r
-\r
- memset(&hints, 0, sizeof hints);\r
- hints.ai_family = AF_UNSPEC;\r
- hints.ai_socktype = SOCK_STREAM;\r
-\r
- getaddrinfo(ip, ReByteBuffer("").appendInt(port).str(), &hints, &addr);\r
- if (addr == NULL) {\r
- m_logger->sayF(LOG_ERROR | CAT_NETWORK, LC_CONNECT_1,\r
- i18n("ip not reachable: $1")).arg(ip).end();\r
- } else {\r
- addressToString(*addr, m_ip, sizeof m_ip);\r
- m_peerName.set(m_ip).appendChar(':').appendInt(port);\r
- m_port = port;\r
- if ((m_handleSocket = socket(addr->ai_family, addr->ai_socktype,\r
- addr->ai_protocol)) < 0)\r
- m_logger->sayF(LOG_ERROR | CAT_NETWORK, LC_CONNECT_2,\r
- i18n("socket() failed ($1): $2")).arg(errno).arg(m_peerName).end();\r
- else if (::connect(m_handleSocket, addr->ai_addr, addr->ai_addrlen)\r
- != 0)\r
- m_logger->sayF(LOG_ERROR | CAT_NETWORK, LC_CONNECT_3,\r
- i18n("connect() failed ($1): $2")).arg(errno).arg(m_peerName)\r
- .end();\r
- else\r
- rc = true;\r
- }\r
- return rc;\r
-}\r
-\r
-/**\r
- * Returns the logger.\r
- *\r
- * @param the logger for error handling\r
- */\r
-ReLogger* ReTCPClient::logger() {\r
- return m_logger;\r
-}\r
-\r
-/**\r
- * Constructor.\r
- *\r
- * @param id an identifier for logging\r
- * @param logger the logger for error handling\r
- */\r
-ReTCPConnection::ReTCPConnection(int id, ReLoggerOwner* loggerOwner) :\r
- ReSocketAddress(loggerOwner),\r
- m_peerName(),\r
- m_received(),\r
- m_handleSocket(-1),\r
- m_id(id),\r
- m_noSent(0),\r
- m_noReceived(0),\r
- m_logSendReceive(true) {\r
-#if defined __WIN32__\r
- if (! isGlobalInitialized){\r
- WSADATA wsaData;\r
- if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) {\r
- loggerOwner->logger()->sayF(LOG_ERROR | CAT_NETWORK, LC_TCP_CONNECTION_1,\r
- i18n("WSAStartup() failed: $1")).arg(errno).arg(getLastOSError()).end();\r
- throw ReException("WSAStartup() failed");\r
- }\r
- isGlobalInitialized = true;\r
- }\r
-#endif\r
-}\r
-\r
-/**\r
- * Destructor.\r
- */\r
-ReTCPConnection::~ReTCPConnection() {\r
-}\r
-/**\r
- * Finishes the connection (in both directions) and frees the resouces.\r
- */\r
-void ReTCPConnection::close() {\r
- if (m_handleSocket >= 0) {\r
- reCloseSocket(m_handleSocket);\r
- m_handleSocket = -1;\r
- }\r
-}\r
-\r
-/**\r
- * Frees the global resources.\r
- */\r
-void ReTCPConnection::globalClose() {\r
-#if defined __WIN32__\r
- if (isGlobalInitialized)\r
- WSACleanup();\r
-#endif\r
-}\r
-\r
-/**\r
- * Sets the address given by a family and a string like "192.168.0.1:22".\r
- *\r
- * @param family AF_INET or AF_INET6\r
- * @param ip the string describing the address, e.g. "192.168.0.1"\r
- * @param port the port of the peer\r
- */\r
-void ReTCPConnection::setConnectedAddress(int family, const char* ip,\r
- int port) {\r
- m_family = family;\r
- m_name = ip;\r
- m_port = port;\r
- int length = strlen(ip);\r
- memcpy(m_ip, ip, length);\r
- m_ip[length] = '\0';\r
- m_name.appendChar(':').appendInt(port);\r
-}\r
-\r
-/**\r
- * Receives a message.\r
- *\r
- * @param command OUT: the received command\r
- * @param data OUT: the received data\r
- *\r
- * @throws ReTCPBrokenPipeException\r
- */\r
-void ReTCPConnection::receive(ReByteBuffer& command, ReByteBuffer& data) {\r
- command.setLength(8);\r
- int received = recv(m_handleSocket, command.buffer(), 8, 0);\r
- if (received == 0) {\r
- // connection closed from the peer\r
- throw ReTCPDisconnectException(m_handleSocket);\r
- } else if (received != 8) {\r
- m_loggerOwner->logger()->sayF(LOG_ERROR | CAT_NETWORK, LC_RECEIVE_1,\r
- i18n("cannot receive ($1): $2 [$3]")).arg(errno).arg(received).arg(\r
- m_peerName).end();\r
- } else {\r
- int flags = 0;\r
- int length = 0;\r
- int found;\r
- if ((found = sscanf(command.str(), "%8x", &length)) != 1\r
- || (flags = (length >> 24)) > 256 || (length &= 0xffffff) < 8) {\r
- m_loggerOwner->logger()->sayF(LOG_ERROR | CAT_NETWORK, LC_RECEIVE_2,\r
- i18n("wrong format: $1 [$2]")).arg(command).arg(m_peerName).end();\r
- } else {\r
- data.setLength(length);\r
- int readBytes = 0;\r
- int rest = length;\r
- char* buf = data.buffer();\r
- int rounds = 0;\r
- while (readBytes < length) {\r
- rounds++;\r
- received = recv(m_handleSocket, buf, rest, 0);\r
- if (received > 0) {\r
- buf += received;\r
- rest -= received;\r
- readBytes += received;\r
- } else if (received == 0) {\r
- data.setLength(readBytes);\r
- break;\r
- } else {\r
- m_loggerOwner->logger()->sayF(LOG_ERROR | CAT_NETWORK,\r
- LC_RECEIVE_3, i18n("cannot receive ($1): $2 [$3]")).arg(\r
- errno).arg(received).arg(m_peerName).end();\r
- break;\r
- }\r
- }\r
- if (rounds)\r
- rounds += 0;\r
- if (readBytes < length) {\r
- m_loggerOwner->logger()->sayF(LOG_ERROR | CAT_NETWORK,\r
- LC_RECEIVE_4, i18n("too few bytes read: $1/$2 [$3]")).arg(\r
- readBytes).arg(received).arg(m_peerName).end();\r
- } else if (m_logSendReceive) {\r
- m_loggerOwner->logger()->sayF(LOG_DEBUG | CAT_NETWORK,\r
- LC_RECEIVE_5,\r
- i18n("received: $1 bytes in $2 round(s) [$3]: $4")).arg(\r
- length).arg(rounds).arg(m_peerName).arg(\r
- ReByteBuffer().appendDump(data.str(), data.length(), 80).str())\r
- .end();\r
- }\r
- command.setLength(0);\r
- if (readBytes >= 8) {\r
- command.append(data.str(), 8);\r
- data.remove(0, 8);\r
- }\r
- }\r
- }\r
-}\r
-/**\r
- * Sends a command with (or without) data to the peer.\r
- *\r
- * @param command the command to send\r
- * @param data the data to send\r
- */\r
-void ReTCPConnection::send(const char* command, const char* data, int length) {\r
- if (data == NULL)\r
- length = 0;\r
- else if (length < 0)\r
- length = strlen(data);\r
- m_toSend.ensureSize(length + 16);\r
- ++m_noSent;\r
- m_toSend.setLength(0);\r
- int flags = 0x7b;\r
- // add command length:\r
- int rest = length + 8;\r
- m_toSend.appendInt(rest | (flags << 24), "%08x");\r
- // we send the flag/length too:\r
- rest += 8;\r
- m_toSend.appendFix(command, -1, 8, 8, NULL);\r
- m_toSend.append(data, length);\r
- int sent = 0;\r
- const char* buf = m_toSend.str();\r
- int rounds = 0;\r
-\r
- while (rest > 0) {\r
- rounds++;\r
- if ((sent = ::send(m_handleSocket, buf, rest, 0)) > 0) {\r
- buf += sent;\r
- rest -= sent;\r
- } else {\r
- m_loggerOwner->logger()->sayF(LOG_ERROR | CAT_NETWORK, LC_WRITE_1,\r
- i18n("cannot send ($1): $2")).arg(errno).arg(m_peerName).end();\r
- break;\r
- }\r
- }\r
- if (rest == 0 && m_logSendReceive)\r
- m_loggerOwner->logger()->sayF(LOG_DEBUG | CAT_NETWORK, LC_WRITE_2,\r
- i18n("sent: $1 bytes in $2 round(s): $3 $4")).arg(length).arg(\r
- rounds).arg(command).arg(\r
- ReByteBuffer().appendDump(data, length, 80).str()).end();\r
-}\r
-\r
-/**\r
- * Constructor.\r
- *\r
- * @param id an identifier for logging\r
- * @param logger the logger for error handling\r
- */\r
-#pragma warning( push )\r
-#pragma warning( disable : 4355 )\r
-ReTCPServerConnection::ReTCPServerConnection(int id, ReTCPServer* server) :\r
- ReTCPConnection(id, this),\r
- ReThread(true),\r
- m_server(server) {\r
-#pragma warning( pop )\r
-}\r
-\r
-/**\r
- * Destructor.\r
- */\r
-ReTCPServerConnection::~ReTCPServerConnection() {\r
-}\r
-\r
-/**\r
- * Serves the commands of a single connection (in a single thread).\r
- */\r
-void ReTCPServerConnection::run() {\r
- ReByteBuffer command;\r
- ReNetCommandHandler::ProcessingState rc = ReNetCommandHandler::PS_UNDEF;\r
- m_loggerOwner->logger()->sayF(LOG_INFO | CAT_NETWORK,\r
- LC_SERVER_CONNECTION_RUN_1, i18n("new connection to $1")).arg(m_name)\r
- .end();\r
- try {\r
- do {\r
- receive(command, m_received);\r
- rc = m_server->handler().handleNetCommand(command, m_received,\r
- this);\r
- if (rc == ReNetCommandHandler::PS_UNKNOWN) {\r
- logger()->sayF(LOG_ERROR | CAT_NETWORK, LC_HANDLE_CONNECTION_1,\r
- i18n("unknown command: $1 length: $2")).arg(command).arg(\r
- m_received.length()).end();\r
- }\r
- } while (rc != ReNetCommandHandler::PS_STOP && !m_shouldStop);\r
- m_pool->setShouldStop();\r
- } catch (ReTCPDisconnectException& e) {\r
- rc = ReNetCommandHandler::PS_CLOSED;\r
- }\r
- close();\r
- if (rc == ReNetCommandHandler::PS_CLOSED)\r
- logger()->sayF(LOG_INFO | CAT_NETWORK, LC_HANDLE_CONNECTION_4,\r
- i18n("connection closed by the peer $1")).arg(m_peerName).end();\r
- else if (rc == ReNetCommandHandler::PS_STOP)\r
- m_loggerOwner->logger()->sayF(LOG_INFO | CAT_NETWORK,\r
- LC_SERVER_CONNECTION_RUN_2, i18n("stop signal received from $1"))\r
- .arg(m_name).end();\r
- else\r
- m_loggerOwner->logger()->say(LOG_INFO | CAT_NETWORK,\r
- LC_SERVER_CONNECTION_RUN_3, i18n("stop order received"));\r
- m_id = -1;\r
-}\r
-\r
-/**\r
- * Constructor.\r
- *\r
- * @param port the port for listening\r
- * @param commandHandler a handler which can process the incoming commands.\r
- * May be NULL\r
- * @param logger the logger for error handling\r
- * @param maxConnections maximal count of threads handling a connection\r
- */\r
-#pragma warning( push )\r
-#pragma warning( disable : 4355 )\r
-ReTCPServer::ReTCPServer(int port, class ReNetCommandHandler& commandHandler,\r
- ReLogger* logger, int maxConnections) :\r
- ReTCPConnection(0, this),\r
- m_maxConnections(maxConnections),\r
- m_handler(commandHandler),\r
- m_logger(logger),\r
- m_threadPool(maxConnections, logger) {\r
-#pragma warning( pop )\r
- m_port = port;\r
-}\r
-\r
-/**\r
- * Destructor.\r
- */\r
-ReTCPServer::~ReTCPServer() {\r
- // m_poolConnections does the things!\r
-}\r
-/**\r
- * Creates a server connection.\r
- *\r
- * @param id the connection identifier\r
- * @param handleSocket the handle of the read/write channel\r
- * @address the data about the client connection (ip, port)\r
- */\r
-ReTCPServerConnection* ReTCPServer::createConnection(int id, int handleSocket) {\r
- ReTCPServerConnection* rc = NULL;\r
- if (m_threadPool.countThreads() < m_maxConnections) {\r
- union {\r
- struct sockaddr address;\r
- uint8_t dummy[32];\r
- } u;\r
- struct sockaddr& address = u.address;\r
- memset(&u, -1, sizeof u);\r
- socklen_t sizeAddress = sizeof u; //.address;\r
- int rc2 = getpeername(handleSocket, &address, &sizeAddress);\r
- if (rc2 != 0)\r
- m_logger->sayF(LOG_ERROR | CAT_NETWORK, LC_LISTEN_FOR_ALL_1,\r
- i18n("getpeername() failed: $1")).arg(getLastOSError()).end();\r
- rc = new ReTCPServerConnection(id, this);\r
- rc->setId(id);\r
- rc->setHandleSocket(handleSocket);\r
- char ip[INET6_ADDRSTRLEN];\r
- inet_ntop(address.sa_family,\r
- address.sa_family == AF_INET ?\r
- (void *) &(((struct sockaddr_in*) &address)->sin_addr) :\r
- (void *) &(((struct sockaddr_in6*) &address)->sin6_addr), ip,\r
- sizeof ip);\r
- int port =\r
- address.sa_family == AF_INET ?\r
- (int) ntohs(((struct sockaddr_in*) &address)->sin_port) :\r
- (int) ntohl(((struct sockaddr_in6*) &address)->sin6_port);\r
- rc->setConnectedAddress(address.sa_family, ip, port);\r
- }\r
- return rc;\r
-}\r
-\r
-/**\r
- * Accepts connections and create a thread which will handle this connection.\r
- */\r
-bool ReTCPServer::listenForAll() {\r
- bool rc = false;\r
- struct addrinfo hints;\r
- struct addrinfo* addrInfo;\r
-\r
-// first, load up address structs with getaddrinfo():\r
- memset(&hints, 0, sizeof hints);\r
- hints.ai_family = AF_UNSPEC; // use IPv4 or IPv6, whichever\r
- hints.ai_socktype = SOCK_STREAM;\r
- hints.ai_flags = AI_PASSIVE; // fill in my IP for me\r
-\r
- memcpy(m_ip, "0.0.0.0", 8);\r
- m_name.set(m_ip).appendChar(':').appendInt(m_port);\r
- getaddrinfo(NULL, ReByteBuffer().appendInt(m_port).str(), &hints,\r
- &addrInfo);\r
- m_family = addrInfo->ai_family;\r
-// make a socket:\r
- m_handleSocket = socket(addrInfo->ai_family, addrInfo->ai_socktype,\r
- addrInfo->ai_protocol);\r
- if (m_handleSocket == -1) {\r
- m_logger->sayF(LOG_ERROR | CAT_NETWORK, LC_LISTEN_FOR_ALL_1,\r
- i18n("cannot create a socket: $1")).arg(errno).end();\r
- } else {\r
- int yes = 1;\r
- // Avoid the "Address already in use" error message of finished processes\r
- // that are still waiting for the release by the kernel:\r
- if (setsockopt(m_handleSocket, SOL_SOCKET, SO_REUSEADDR,\r
- reinterpret_cast<const char*>(&yes), sizeof(int)) == -1) {\r
- m_logger->sayF(LOG_WARNING | CAT_NETWORK, LC_LISTEN_FOR_ALL_7,\r
- i18n("setsockopt() failed: $1")).arg(errno).end();\r
- // this error is not fatal, continue!\r
- }\r
- // bind it to the port we passed in to getaddrinfo():\r
- if (bind(m_handleSocket, addrInfo->ai_addr, addrInfo->ai_addrlen) != 0)\r
- m_logger->sayF(LOG_ERROR | CAT_NETWORK, LC_LISTEN_FOR_ALL_2,\r
- i18n("cannot bind: $1")).arg(errno).end();\r
- else {\r
- //Listen\r
- listen(m_handleSocket,\r
- m_maxConnections < 16 ? m_maxConnections : 16);\r
-\r
- //Accept and incoming connection\r
- m_logger->sayF(LOG_INFO | CAT_NETWORK, LC_LISTEN_FOR_ALL_3,\r
- i18n("listening on $1...")).arg(m_port).end();\r
- int nextId = 1;\r
- //Accept and incoming connection\r
- int clientSocket;\r
- while ((clientSocket = accept(m_handleSocket,\r
- NULL, NULL)) > 0) {\r
- if (!m_threadPool.shouldStop()) {\r
- if (m_threadPool.countThreads() >= m_maxConnections) {\r
- // close the connection at once:\r
- m_logger->sayF(LOG_WARNING | CAT_NETWORK,\r
- LC_LISTEN_FOR_ALL_5,\r
- i18n(\r
- "connection refused (too many connections): $1"))\r
- .arg(m_port).end();\r
- reCloseSocket(clientSocket);\r
- } else {\r
- ReTCPServerConnection* connection = createConnection(\r
- nextId++, clientSocket);\r
- connection->setLogSendReceive(m_logSendReceive);\r
- if (!m_threadPool.startThread(connection)) {\r
- m_logger->sayF(LOG_ERROR | CAT_PROCESS,\r
- LC_LISTEN_FOR_ALL_6,\r
- i18n("cannot create a thread: $1")).arg(\r
- getLastOSError()).end();\r
- reCloseSocket(clientSocket);\r
- clientSocket = -1;\r
- } else {\r
-\r
- }\r
- }\r
- }\r
- }\r
- if (clientSocket < 0)\r
- m_logger->sayF(LOG_ERROR | CAT_PROCESS,\r
- LC_LISTEN_FOR_ALL_6,\r
- i18n("accept() failed: $1")).arg(\r
- getLastOSError()).end();\r
- if (m_threadPool.shouldStop()) {\r
- m_logger->say(LOG_INFO | CAT_PROCESS, LC_LISTEN_FOR_ALL_7,\r
- i18n("stop request received"));\r
- }\r
- }\r
- }\r
- return rc;\r
-}\r
-\r
-/**\r
- * Returns the logger.\r
- *\r
- * @param the logger for error handling\r
- */\r
-ReLogger* ReTCPServer::logger() {\r
- return m_logger;\r
-}\r
-\r
-/**\r
- * Constructor.\r
- */\r
-ReNetCommandHandler::ReNetCommandHandler() :\r
- m_nextHandler(NULL) {\r
-}\r
-\r
-/**\r
- * Adds a handler at the end of the handler chain.\r
- */\r
-void ReNetCommandHandler::addHandler(ReNetCommandHandler* handler) {\r
- if (m_nextHandler == NULL)\r
- m_nextHandler = handler;\r
- else\r
- m_nextHandler->addHandler(handler);\r
-}\r
-\r
-/**\r
- * Constructor.\r
- *\r
- * @param port port for listening\r
- * @param logger logger for error handling\r
- */\r
-#pragma warning( push )\r
-#pragma warning( disable : 4355 )\r
-ReTCPEchoServer::ReTCPEchoServer(int port, ReLogger* logger) :\r
- ReTCPServer(port, *this, logger),\r
- ReNetCommandHandler() {\r
-#pragma warning( pop )\r
-}\r
-/**\r
- * Destructor.\r
- */\r
-ReTCPEchoServer::~ReTCPEchoServer() {\r
-}\r
-\r
-/**\r
- * Handler for the commands "echo", "localtim" and "stop".\r
- *\r
- * @param command a string describing what do to\r
- * @param data data of the command, may be empty\r
- * @param connection the connection which can be used for answers\r
- * @result PS_UNKNOWN: command is not known<br>\r
- * PS_PROCESSED: command successfully processed<br>\r
- * PS_FAILED: command processed, error occurred<br>\r
- * PS_ABORT: connection should be finished\r
- */\r
-ReNetCommandHandler::ProcessingState ReTCPEchoServer::handleNetCommand(\r
- ReByteBuffer& command, ReByteBuffer& data, ReTCPConnection* connection) {\r
- ProcessingState rc = PS_UNDEF;\r
- if (command.equals("echo ")) {\r
- connection->send("Echo", data.str(), data.length());\r
- rc = PS_PROCESSED;\r
- } else if (command.equals("strlen ")) {\r
- m_toSend.setLength(0).appendInt(data.length());\r
- connection->send("Strlen ", m_toSend.str(), m_toSend.length());\r
- rc = PS_PROCESSED;\r
- } else if (command.equals("filldata")) {\r
- int length = atol(data.str());\r
- if (m_toSend.length() != length || !m_toSend.startsWith("xxxxx"))\r
- m_toSend.setLength(0).appendChar('x', length);\r
- connection->send("Filldata", m_toSend.str(), m_toSend.length());\r
- rc = PS_PROCESSED;\r
- } else if (command.equals("localtim")) {\r
- time_t now2 = time(NULL);\r
- struct tm* now = localtime(&now2);\r
- char buffer[128];\r
- strftime(buffer, sizeof buffer, "%y.%m.%d %H:%M:%S", now);\r
- connection->send("Localtim", buffer, strlen(buffer));\r
- rc = PS_PROCESSED;\r
- } else if (command.equals("stop ")) {\r
- rc = PS_STOP;\r
- } else {\r
- rc = PS_UNKNOWN;\r
- if (m_nextHandler != NULL)\r
- rc = m_nextHandler->handleNetCommand(command, data, connection);\r
- }\r
- return rc;\r
-}\r
-\r
-/**\r
- * Constructor.\r
- */\r
-ReTCPStopClient::ReTCPStopClient(ReLogger* logger) :\r
- ReTCPClient(logger) {\r
-}\r
-/**\r
- * Destructor.\r
- */\r
-ReTCPStopClient::~ReTCPStopClient() {\r
-}\r
-\r
-void ReTCPStopClient::stopServer(int port, const char* ip) {\r
- connect(ip, port);\r
- send("stop", NULL);\r
-}\r
+/*
+ * ReTCP.cpp
+ *
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
+ * Do what you want.
+ * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
+ * The latest sources: https://github.com/republib
+ */
+
+#include "base/rebase.hpp"
+#include "net/renet.hpp"
+
+enum LOCATION_DIRTOOL {
+ LC_LISTEN_FOR_ALL_1 = LOC_FIRST_OF(LOC_TCP), // 50501
+ LC_LISTEN_FOR_ALL_2, // 50502
+ LC_LISTEN_FOR_ALL_3, // 50503
+ LC_RECEIVE_5, // 50504
+ LC_LISTEN_FOR_ALL_5, // 50505
+ LC_LISTEN_FOR_ALL_6, // 50506
+ LC_WRITE_1, // 50507
+ LC_CONNECT_1, // 50508
+ LC_SOCKET_ADDR_SET_1, // 50509
+ LC_LISTEN_FOR_ALL_7, // 50510
+ LC_HANDLE_CONNECTION_1, // 50511
+ LC_RECEIVE_1, // 50512
+ LC_RECEIVE_2, // 50513
+ LC_RECEIVE_3, // 50514
+ LC_RECEIVE_4, // 50515
+ LC_CONNECT_2, // 50516
+ LC_CONNECT_3, // 50517
+ LC_TCP_CONNECTION_1, // 50518
+ LC_WRITE_2, // 50519
+ LC_SERVER_CONNECTION_RUN_1, // 50520
+ LC_SERVER_CONNECTION_RUN_2, // 50521
+ LC_SERVER_CONNECTION_RUN_3, // 50522
+ LC_HANDLE_CONNECTION_4, // 50523
+};
+
+#if defined __WIN32__
+bool ReTCPConnection::isGlobalInitialized = false;
+#endif
+
+/**
+ * Constructor.
+ *
+ * @param message the description of the exception
+ */
+ReTCPException::ReTCPException(const char* message) {
+
+}
+;
+
+/**
+ * Constructor.
+ *
+ * @param socket the socket which is disconnected
+ */
+ReTCPDisconnectException::ReTCPDisconnectException(int socket) :
+ ReTCPException(i18n("disconnected")),
+ m_socket(socket) {
+}
+
+/**
+ * Constructor.
+ *
+ * @param logger the logger for error handling
+ */
+ReSocketAddress::ReSocketAddress(ReLoggerOwner* loggerOwner) :
+ m_preferredFamily(AF_INET),
+ m_family(-1),
+ m_port(0),
+ m_loggerOwner(loggerOwner),
+ // m_ip
+ m_name() {
+ memset(&m_ip, 0, sizeof m_ip);
+}
+/**
+ * Destructor.
+ */
+ReSocketAddress::~ReSocketAddress() {
+}
+void addressToString(struct addrinfo& addr, char* ip, size_t ipSize) {
+ if (addr.ai_family == AF_INET) {
+ inet_ntop(addr.ai_family, (struct sockaddr_in *) addr.ai_addr, ip,
+ ipSize);
+ } else if (addr.ai_family == AF_INET6) {
+ inet_ntop(addr.ai_family, (struct sockaddr_in6 *) addr.ai_addr, ip,
+ ipSize);
+ }
+}
+/**
+ * Sets the data from symbolic values.
+ */
+void ReSocketAddress::setAddress(const char* ip, int port) {
+ struct addrinfo hints;
+ struct addrinfo* infoList;
+ int status;
+ m_port = port;
+ memset(&hints, 0, sizeof hints);
+ hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version
+ hints.ai_socktype = SOCK_STREAM;
+
+ if ((status = getaddrinfo(ip, NULL, &hints, &infoList)) != 0) {
+ m_loggerOwner->logger()->sayF(LOG_ERROR | CAT_NETWORK,
+ LC_SOCKET_ADDR_SET_1, i18n("getaddrinfo($1) failed: $2")).arg(ip)
+ .arg(errno).end();
+ }
+
+ struct addrinfo* ptr;
+ bool hasIP4 = false;
+ bool hasIP6 = false;
+
+ // Search for a available ip address
+ // if more than one are available, prefer m_preferredFamily:
+ m_ip[0] = '\0';
+ for (ptr = infoList; ptr != NULL; ptr = ptr->ai_next) {
+ // different fields in IPv4 and IPv6:
+ if (!hasIP4 && ptr->ai_family == AF_INET) {
+ m_family = AF_INET;
+ hasIP4 = true;
+ } else if (!hasIP6 && ptr->ai_family == AF_INET6) {
+ m_family = AF_INET6;
+ hasIP6 = true;
+ }
+ addressToString(*ptr, m_ip, sizeof m_ip);
+ if (ptr->ai_family == m_preferredFamily)
+ break;
+ }
+ // free the linked list
+ freeaddrinfo(infoList);
+ if (m_ip[0] == '\0')
+ m_name.setLength(0);
+ else
+ m_name.set(m_ip).appendChar(':').appendInt(port);
+}
+
+/**
+ * Constructor.
+ *
+ * @param logger logger for the error handling
+ */
+#pragma warning( push )
+#pragma warning( disable : 4355 )
+ReTCPClient::ReTCPClient(ReLogger* logger) :
+ ReTCPConnection(-1, this),
+ m_logger(logger) {
+#pragma warning( pop )
+}
+/**
+ * Destructor.
+ */
+ReTCPClient::~ReTCPClient() {
+}
+/**
+ * Connects a client with the server.
+ *
+ * @param ip domain address ("denic.de") or ip address ("192.168.2.1")
+ * @param port port number: 1..65535
+ */
+bool ReTCPClient::connect(const char* ip, int port) {
+ bool rc = false;
+ struct addrinfo hints;
+ struct addrinfo* addr = NULL;
+
+ memset(&hints, 0, sizeof hints);
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+
+ getaddrinfo(ip, ReByteBuffer("").appendInt(port).str(), &hints, &addr);
+ if (addr == NULL) {
+ m_logger->sayF(LOG_ERROR | CAT_NETWORK, LC_CONNECT_1,
+ i18n("ip not reachable: $1")).arg(ip).end();
+ } else {
+ addressToString(*addr, m_ip, sizeof m_ip);
+ m_peerName.set(m_ip).appendChar(':').appendInt(port);
+ m_port = port;
+ if ((m_handleSocket = socket(addr->ai_family, addr->ai_socktype,
+ addr->ai_protocol)) < 0)
+ m_logger->sayF(LOG_ERROR | CAT_NETWORK, LC_CONNECT_2,
+ i18n("socket() failed ($1): $2")).arg(errno).arg(m_peerName).end();
+ else if (::connect(m_handleSocket, addr->ai_addr, addr->ai_addrlen)
+ != 0)
+ m_logger->sayF(LOG_ERROR | CAT_NETWORK, LC_CONNECT_3,
+ i18n("connect() failed ($1): $2")).arg(errno).arg(m_peerName)
+ .end();
+ else
+ rc = true;
+ }
+ return rc;
+}
+
+/**
+ * Returns the logger.
+ *
+ * @param the logger for error handling
+ */
+ReLogger* ReTCPClient::logger() {
+ return m_logger;
+}
+
+/**
+ * Constructor.
+ *
+ * @param id an identifier for logging
+ * @param logger the logger for error handling
+ */
+ReTCPConnection::ReTCPConnection(int id, ReLoggerOwner* loggerOwner) :
+ ReSocketAddress(loggerOwner),
+ m_peerName(),
+ m_received(),
+ m_handleSocket(-1),
+ m_id(id),
+ m_noSent(0),
+ m_noReceived(0),
+ m_logSendReceive(true) {
+#if defined __WIN32__
+ if (! isGlobalInitialized) {
+ WSADATA wsaData;
+ if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) {
+ loggerOwner->logger()->sayF(LOG_ERROR | CAT_NETWORK, LC_TCP_CONNECTION_1,
+ i18n("WSAStartup() failed: $1")).arg(errno).arg(getLastOSError()).end();
+ throw ReException("WSAStartup() failed");
+ }
+ isGlobalInitialized = true;
+ }
+#endif
+}
+
+/**
+ * Destructor.
+ */
+ReTCPConnection::~ReTCPConnection() {
+}
+/**
+ * Finishes the connection (in both directions) and frees the resouces.
+ */
+void ReTCPConnection::close() {
+ if (m_handleSocket >= 0) {
+ reCloseSocket(m_handleSocket);
+ m_handleSocket = -1;
+ }
+}
+
+/**
+ * Frees the global resources.
+ */
+void ReTCPConnection::globalClose() {
+#if defined __WIN32__
+ if (isGlobalInitialized)
+ WSACleanup();
+#endif
+}
+
+/**
+ * Sets the address given by a family and a string like "192.168.0.1:22".
+ *
+ * @param family AF_INET or AF_INET6
+ * @param ip the string describing the address, e.g. "192.168.0.1"
+ * @param port the port of the peer
+ */
+void ReTCPConnection::setConnectedAddress(int family, const char* ip,
+ int port) {
+ m_family = family;
+ m_name = ip;
+ m_port = port;
+ int length = strlen(ip);
+ memcpy(m_ip, ip, length);
+ m_ip[length] = '\0';
+ m_name.appendChar(':').appendInt(port);
+}
+
+/**
+ * Receives a message.
+ *
+ * @param command OUT: the received command
+ * @param data OUT: the received data
+ *
+ * @throws ReTCPBrokenPipeException
+ */
+void ReTCPConnection::receive(ReByteBuffer& command, ReByteBuffer& data) {
+ command.setLength(8);
+ int received = recv(m_handleSocket, command.buffer(), 8, 0);
+ if (received == 0) {
+ // connection closed from the peer
+ throw ReTCPDisconnectException(m_handleSocket);
+ } else if (received != 8) {
+ m_loggerOwner->logger()->sayF(LOG_ERROR | CAT_NETWORK, LC_RECEIVE_1,
+ i18n("cannot receive ($1): $2 [$3]")).arg(errno).arg(received).arg(
+ m_peerName).end();
+ } else {
+ int flags = 0;
+ int length = 0;
+ int found;
+ if ((found = sscanf(command.str(), "%8x", &length)) != 1
+ || (flags = (length >> 24)) > 256 || (length &= 0xffffff) < 8) {
+ m_loggerOwner->logger()->sayF(LOG_ERROR | CAT_NETWORK, LC_RECEIVE_2,
+ i18n("wrong format: $1 [$2]")).arg(command).arg(m_peerName).end();
+ } else {
+ data.setLength(length);
+ int readBytes = 0;
+ int rest = length;
+ char* buf = data.buffer();
+ int rounds = 0;
+ while (readBytes < length) {
+ rounds++;
+ received = recv(m_handleSocket, buf, rest, 0);
+ if (received > 0) {
+ buf += received;
+ rest -= received;
+ readBytes += received;
+ } else if (received == 0) {
+ data.setLength(readBytes);
+ break;
+ } else {
+ m_loggerOwner->logger()->sayF(LOG_ERROR | CAT_NETWORK,
+ LC_RECEIVE_3, i18n("cannot receive ($1): $2 [$3]")).arg(
+ errno).arg(received).arg(m_peerName).end();
+ break;
+ }
+ }
+ if (rounds)
+ rounds += 0;
+ if (readBytes < length) {
+ m_loggerOwner->logger()->sayF(LOG_ERROR | CAT_NETWORK,
+ LC_RECEIVE_4, i18n("too few bytes read: $1/$2 [$3]")).arg(
+ readBytes).arg(received).arg(m_peerName).end();
+ } else if (m_logSendReceive) {
+ m_loggerOwner->logger()->sayF(LOG_DEBUG | CAT_NETWORK,
+ LC_RECEIVE_5,
+ i18n("received: $1 bytes in $2 round(s) [$3]: $4")).arg(
+ length).arg(rounds).arg(m_peerName).arg(
+ ReByteBuffer().appendDump(data.str(), data.length(), 80).str())
+ .end();
+ }
+ command.setLength(0);
+ if (readBytes >= 8) {
+ command.append(data.str(), 8);
+ data.remove(0, 8);
+ }
+ }
+ }
+}
+/**
+ * Sends a command with (or without) data to the peer.
+ *
+ * @param command the command to send
+ * @param data the data to send
+ */
+void ReTCPConnection::send(const char* command, const char* data, int length) {
+ if (data == NULL)
+ length = 0;
+ else if (length < 0)
+ length = strlen(data);
+ m_toSend.ensureSize(length + 16);
+ ++m_noSent;
+ m_toSend.setLength(0);
+ int flags = 0x7b;
+ // add command length:
+ int rest = length + 8;
+ m_toSend.appendInt(rest | (flags << 24), "%08x");
+ // we send the flag/length too:
+ rest += 8;
+ m_toSend.appendFix(command, -1, 8, 8, NULL);
+ m_toSend.append(data, length);
+ int sent = 0;
+ const char* buf = m_toSend.str();
+ int rounds = 0;
+
+ while (rest > 0) {
+ rounds++;
+ if ((sent = ::send(m_handleSocket, buf, rest, 0)) > 0) {
+ buf += sent;
+ rest -= sent;
+ } else {
+ m_loggerOwner->logger()->sayF(LOG_ERROR | CAT_NETWORK, LC_WRITE_1,
+ i18n("cannot send ($1): $2")).arg(errno).arg(m_peerName).end();
+ break;
+ }
+ }
+ if (rest == 0 && m_logSendReceive)
+ m_loggerOwner->logger()->sayF(LOG_DEBUG | CAT_NETWORK, LC_WRITE_2,
+ i18n("sent: $1 bytes in $2 round(s): $3 $4")).arg(length).arg(
+ rounds).arg(command).arg(
+ ReByteBuffer().appendDump(data, length, 80).str()).end();
+}
+
+/**
+ * Constructor.
+ *
+ * @param id an identifier for logging
+ * @param logger the logger for error handling
+ */
+#pragma warning( push )
+#pragma warning( disable : 4355 )
+ReTCPServerConnection::ReTCPServerConnection(int id, ReTCPServer* server) :
+ ReTCPConnection(id, this),
+ ReThread(true),
+ m_server(server) {
+#pragma warning( pop )
+}
+
+/**
+ * Destructor.
+ */
+ReTCPServerConnection::~ReTCPServerConnection() {
+}
+
+/**
+ * Serves the commands of a single connection (in a single thread).
+ */
+void ReTCPServerConnection::run() {
+ ReByteBuffer command;
+ ReNetCommandHandler::ProcessingState rc = ReNetCommandHandler::PS_UNDEF;
+ m_loggerOwner->logger()->sayF(LOG_INFO | CAT_NETWORK,
+ LC_SERVER_CONNECTION_RUN_1, i18n("new connection to $1")).arg(m_name)
+ .end();
+ try {
+ do {
+ receive(command, m_received);
+ rc = m_server->handler().handleNetCommand(command, m_received,
+ this);
+ if (rc == ReNetCommandHandler::PS_UNKNOWN) {
+ logger()->sayF(LOG_ERROR | CAT_NETWORK, LC_HANDLE_CONNECTION_1,
+ i18n("unknown command: $1 length: $2")).arg(command).arg(
+ m_received.length()).end();
+ }
+ } while (rc != ReNetCommandHandler::PS_STOP && !m_shouldStop);
+ m_pool->setShouldStop();
+ } catch (ReTCPDisconnectException& e) {
+ rc = ReNetCommandHandler::PS_CLOSED;
+ }
+ close();
+ if (rc == ReNetCommandHandler::PS_CLOSED)
+ logger()->sayF(LOG_INFO | CAT_NETWORK, LC_HANDLE_CONNECTION_4,
+ i18n("connection closed by the peer $1")).arg(m_peerName).end();
+ else if (rc == ReNetCommandHandler::PS_STOP)
+ m_loggerOwner->logger()->sayF(LOG_INFO | CAT_NETWORK,
+ LC_SERVER_CONNECTION_RUN_2, i18n("stop signal received from $1"))
+ .arg(m_name).end();
+ else
+ m_loggerOwner->logger()->say(LOG_INFO | CAT_NETWORK,
+ LC_SERVER_CONNECTION_RUN_3, i18n("stop order received"));
+ m_id = -1;
+}
+
+/**
+ * Constructor.
+ *
+ * @param port the port for listening
+ * @param commandHandler a handler which can process the incoming commands.
+ * May be NULL
+ * @param logger the logger for error handling
+ * @param maxConnections maximal count of threads handling a connection
+ */
+#pragma warning( push )
+#pragma warning( disable : 4355 )
+ReTCPServer::ReTCPServer(int port, class ReNetCommandHandler& commandHandler,
+ ReLogger* logger, int maxConnections) :
+ ReTCPConnection(0, this),
+ m_maxConnections(maxConnections),
+ m_handler(commandHandler),
+ m_logger(logger),
+ m_threadPool(maxConnections, logger) {
+#pragma warning( pop )
+ m_port = port;
+}
+
+/**
+ * Destructor.
+ */
+ReTCPServer::~ReTCPServer() {
+ // m_poolConnections does the things!
+}
+/**
+ * Creates a server connection.
+ *
+ * @param id the connection identifier
+ * @param handleSocket the handle of the read/write channel
+ * @address the data about the client connection (ip, port)
+ */
+ReTCPServerConnection* ReTCPServer::createConnection(int id, int handleSocket) {
+ ReTCPServerConnection* rc = NULL;
+ if (m_threadPool.countThreads() < m_maxConnections) {
+ union {
+ struct sockaddr address;
+ uint8_t dummy[32];
+ } u;
+ struct sockaddr& address = u.address;
+ memset(&u, -1, sizeof u);
+ socklen_t sizeAddress = sizeof u; //.address;
+ int rc2 = getpeername(handleSocket, &address, &sizeAddress);
+ if (rc2 != 0)
+ m_logger->sayF(LOG_ERROR | CAT_NETWORK, LC_LISTEN_FOR_ALL_1,
+ i18n("getpeername() failed: $1")).arg(getLastOSError()).end();
+ rc = new ReTCPServerConnection(id, this);
+ rc->setId(id);
+ rc->setHandleSocket(handleSocket);
+ char ip[INET6_ADDRSTRLEN];
+ inet_ntop(address.sa_family,
+ address.sa_family == AF_INET ?
+ (void *) &(((struct sockaddr_in*) &address)->sin_addr) :
+ (void *) &(((struct sockaddr_in6*) &address)->sin6_addr), ip,
+ sizeof ip);
+ int port =
+ address.sa_family == AF_INET ?
+ (int) ntohs(((struct sockaddr_in*) &address)->sin_port) :
+ (int) ntohl(((struct sockaddr_in6*) &address)->sin6_port);
+ rc->setConnectedAddress(address.sa_family, ip, port);
+ }
+ return rc;
+}
+
+/**
+ * Accepts connections and create a thread which will handle this connection.
+ */
+bool ReTCPServer::listenForAll() {
+ bool rc = false;
+ struct addrinfo hints;
+ struct addrinfo* addrInfo;
+
+// first, load up address structs with getaddrinfo():
+ memset(&hints, 0, sizeof hints);
+ hints.ai_family = AF_UNSPEC; // use IPv4 or IPv6, whichever
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_PASSIVE; // fill in my IP for me
+
+ memcpy(m_ip, "0.0.0.0", 8);
+ m_name.set(m_ip).appendChar(':').appendInt(m_port);
+ getaddrinfo(NULL, ReByteBuffer().appendInt(m_port).str(), &hints,
+ &addrInfo);
+ m_family = addrInfo->ai_family;
+// make a socket:
+ m_handleSocket = socket(addrInfo->ai_family, addrInfo->ai_socktype,
+ addrInfo->ai_protocol);
+ if (m_handleSocket == -1) {
+ m_logger->sayF(LOG_ERROR | CAT_NETWORK, LC_LISTEN_FOR_ALL_1,
+ i18n("cannot create a socket: $1")).arg(errno).end();
+ } else {
+ int yes = 1;
+ // Avoid the "Address already in use" error message of finished processes
+ // that are still waiting for the release by the kernel:
+ if (setsockopt(m_handleSocket, SOL_SOCKET, SO_REUSEADDR,
+ reinterpret_cast<const char*>(&yes), sizeof(int)) == -1) {
+ m_logger->sayF(LOG_WARNING | CAT_NETWORK, LC_LISTEN_FOR_ALL_7,
+ i18n("setsockopt() failed: $1")).arg(errno).end();
+ // this error is not fatal, continue!
+ }
+ // bind it to the port we passed in to getaddrinfo():
+ if (bind(m_handleSocket, addrInfo->ai_addr, addrInfo->ai_addrlen) != 0)
+ m_logger->sayF(LOG_ERROR | CAT_NETWORK, LC_LISTEN_FOR_ALL_2,
+ i18n("cannot bind: $1")).arg(errno).end();
+ else {
+ //Listen
+ listen(m_handleSocket,
+ m_maxConnections < 16 ? m_maxConnections : 16);
+
+ //Accept and incoming connection
+ m_logger->sayF(LOG_INFO | CAT_NETWORK, LC_LISTEN_FOR_ALL_3,
+ i18n("listening on $1...")).arg(m_port).end();
+ int nextId = 1;
+ //Accept and incoming connection
+ int clientSocket;
+ while ((clientSocket = accept(m_handleSocket,
+ NULL, NULL)) > 0) {
+ if (!m_threadPool.shouldStop()) {
+ if (m_threadPool.countThreads() >= m_maxConnections) {
+ // close the connection at once:
+ m_logger->sayF(LOG_WARNING | CAT_NETWORK,
+ LC_LISTEN_FOR_ALL_5,
+ i18n(
+ "connection refused (too many connections): $1"))
+ .arg(m_port).end();
+ reCloseSocket(clientSocket);
+ } else {
+ ReTCPServerConnection* connection = createConnection(
+ nextId++, clientSocket);
+ connection->setLogSendReceive(m_logSendReceive);
+ if (!m_threadPool.startThread(connection)) {
+ m_logger->sayF(LOG_ERROR | CAT_PROCESS,
+ LC_LISTEN_FOR_ALL_6,
+ i18n("cannot create a thread: $1")).arg(
+ getLastOSError()).end();
+ reCloseSocket(clientSocket);
+ clientSocket = -1;
+ } else {
+
+ }
+ }
+ }
+ }
+ if (clientSocket < 0)
+ m_logger->sayF(LOG_ERROR | CAT_PROCESS, LC_LISTEN_FOR_ALL_6,
+ i18n("accept() failed: $1")).arg(getLastOSError()).end();
+ if (m_threadPool.shouldStop()) {
+ m_logger->say(LOG_INFO | CAT_PROCESS, LC_LISTEN_FOR_ALL_7,
+ i18n("stop request received"));
+ }
+ }
+ }
+ return rc;
+}
+
+/**
+ * Returns the logger.
+ *
+ * @param the logger for error handling
+ */
+ReLogger* ReTCPServer::logger() {
+ return m_logger;
+}
+
+/**
+ * Constructor.
+ */
+ReNetCommandHandler::ReNetCommandHandler() :
+ m_nextHandler(NULL) {
+}
+
+/**
+ * Adds a handler at the end of the handler chain.
+ */
+void ReNetCommandHandler::addHandler(ReNetCommandHandler* handler) {
+ if (m_nextHandler == NULL)
+ m_nextHandler = handler;
+ else
+ m_nextHandler->addHandler(handler);
+}
+
+/**
+ * Constructor.
+ *
+ * @param port port for listening
+ * @param logger logger for error handling
+ */
+#pragma warning( push )
+#pragma warning( disable : 4355 )
+ReTCPEchoServer::ReTCPEchoServer(int port, ReLogger* logger) :
+ ReTCPServer(port, *this, logger),
+ ReNetCommandHandler() {
+#pragma warning( pop )
+}
+/**
+ * Destructor.
+ */
+ReTCPEchoServer::~ReTCPEchoServer() {
+}
+
+/**
+ * Handler for the commands "echo", "localtim" and "stop".
+ *
+ * @param command a string describing what do to
+ * @param data data of the command, may be empty
+ * @param connection the connection which can be used for answers
+ * @result PS_UNKNOWN: command is not known<br>
+ * PS_PROCESSED: command successfully processed<br>
+ * PS_FAILED: command processed, error occurred<br>
+ * PS_ABORT: connection should be finished
+ */
+ReNetCommandHandler::ProcessingState ReTCPEchoServer::handleNetCommand(
+ ReByteBuffer& command, ReByteBuffer& data, ReTCPConnection* connection) {
+ ProcessingState rc = PS_UNDEF;
+ if (command.equals("echo ")) {
+ connection->send("Echo", data.str(), data.length());
+ rc = PS_PROCESSED;
+ } else if (command.equals("strlen ")) {
+ m_toSend.setLength(0).appendInt(data.length());
+ connection->send("Strlen ", m_toSend.str(), m_toSend.length());
+ rc = PS_PROCESSED;
+ } else if (command.equals("filldata")) {
+ int length = atol(data.str());
+ if (m_toSend.length() != length || !m_toSend.startsWith("xxxxx"))
+ m_toSend.setLength(0).appendChar('x', length);
+ connection->send("Filldata", m_toSend.str(), m_toSend.length());
+ rc = PS_PROCESSED;
+ } else if (command.equals("localtim")) {
+ time_t now2 = time(NULL);
+ struct tm* now = localtime(&now2);
+ char buffer[128];
+ strftime(buffer, sizeof buffer, "%y.%m.%d %H:%M:%S", now);
+ connection->send("Localtim", buffer, strlen(buffer));
+ rc = PS_PROCESSED;
+ } else if (command.equals("stop ")) {
+ rc = PS_STOP;
+ } else {
+ rc = PS_UNKNOWN;
+ if (m_nextHandler != NULL)
+ rc = m_nextHandler->handleNetCommand(command, data, connection);
+ }
+ return rc;
+}
+
+/**
+ * Constructor.
+ */
+ReTCPStopClient::ReTCPStopClient(ReLogger* logger) :
+ ReTCPClient(logger) {
+}
+/**
+ * Destructor.
+ */
+ReTCPStopClient::~ReTCPStopClient() {
+}
+
+void ReTCPStopClient::stopServer(int port, const char* ip) {
+ connect(ip, port);
+ send("stop", NULL);
+}
/*
* 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
*/
/*
- * 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:
/*
- * 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_
-/*\r
- * renet.hpp\r
- *\r
- * License: Public domain\r
- * Do what you want.\r
- * No warranties and disclaimer of any damages.\r
- * The latest sources: https://github.com/republib\r
- *\r
- */\r
-\r
-#ifndef NET_RENET_HPP_\r
-#define NET_RENET_HPP_\r
-\r
-#if defined __linux__\r
-#include <arpa/inet.h>\r
-#include <netinet/in.h>\r
-#include <sys/socket.h>\r
-#include <netdb.h>\r
-#elif defined __WIN32__\r
-#include <ws2tcpip.h>\r
-#endif\r
-#include "net/ReUdpConnection.hpp"\r
-#include "net/ReTCP.hpp"\r
-#endif /* NET_RENET_HPP_ */\r
+/*
+ * renet.hpp
+ *
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
+ * Do what you want.
+ * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
+ * The latest sources: https://github.com/republib
+ */
+
+#ifndef NET_RENET_HPP_
+#define NET_RENET_HPP_
+
+#if defined __linux__
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#elif defined __WIN32__
+#include <ws2tcpip.h>
+#endif
+#include "net/ReUdpConnection.hpp"
+#include "net/ReTCP.hpp"
+#endif /* NET_RENET_HPP_ */
-/*\r
- * ReDirTools.cpp\r
- *\r
- * License: Public domain\r
- * Do what you want.\r
- * No warranties and disclaimer of any damages.\r
- * The latest sources: https://github.com/republib\r
- */\r
-\r
-#include "base/rebase.hpp"\r
-#include "math/remath.hpp"\r
-#include "os/reos.hpp"\r
-#include "net/renet.hpp"\r
-\r
-enum LOCATION_DIRTOOL {\r
- LC_COPY_FILE_1 = LC_DIRTOOLS + 1, // 50101\r
- LC_COPY_FILE_2, // 50102\r
- LC_COPY_FILE_3, // 50103\r
- LC_COPY_FILE_4, // 50104\r
- LC_COPY_FILE_5, // 50105\r
- LC_COPY_FILE_6, // 50106\r
- LC_MAKE_DIR_1, // 50107\r
- LC_MAKE_DIR_2, // 50108\r
- LC_SET_PROPERTIES_1, // 50109\r
- LC_SET_PROPERTIES_2, // 50110\r
- LC_SET_PROPERTIES_3, // 50111\r
- LC_TOUCH_1, // 50112\r
- LC_CALCULATE_CHECKSUM_1, // 50113\r
- LC_BUILD_DIRECTORY_1, // 50114\r
- LC_COMPARE_STORAGE_1, // 50115\r
- LC_COMPARE_STORAGE_2, // 50116\r
- LC_COMPARE_STORAGE_3, // 50117\r
- LC_COMPARE_STORAGE_4, // 50118\r
- LC_COMPARE_STORAGE_5, // 50119\r
- LC_COMPARE_STORAGE_6, // 50120\r
- LC_COMPARE_STORAGE_7, // 50121\r
- LC_COMPARE_DIR_1, // 50122\r
- LC_DELETE_1, // 50123\r
-};\r
-const char* ReDirTools::m_version = "2015.03.22";\r
-ReLogger* ReDirTools::m_logger = NULL;\r
-\r
-static const char* s_helpSummary[] = { "dirtool or dt <command> <opts>",\r
- " Useful commands around directory trees.",\r
- " Type 'dirtool help <command>' for more help.", "<command>:",\r
- "batch produce output to handle the found files with a script",\r
- "checksum shows the checksum (MD5 or other) of the selected files",\r
- "delete delete the selected files",\r
- "help shows info about the arguments/options",\r
- "list shows the meta data of the selected files",\r
- "random displays random numbers",\r
- "statistic shows statistics about a direcctory tree",\r
- "synchronize copies only modified or new files from",\r
- " from a source directory tre to a target",\r
- "touch sets the filetimes",\r
- "which finds a file in a path list (like PATH)",\r
- NULL };\r
-\r
-const char* s_batchUsage[] = {\r
- "<command>: batch [<opts>] <dir_or_file1> [<dir_or_file2> ...]",\r
- " produces output usable for a batch file (script)",\r
- " all found files can be processed with a given script",\r
- " each line starts (usually) with a script name (see -c)",\r
- " then it follows the full filename of the found file",\r
- " use --arguments or --script to configure the output line",\r
- NULL };\r
-const char* s_deleteExamples[] = {\r
- "dirtool delete --basename-pattern=;*~;*.bak e:\\data",\r
- "dirtool delete /tmp/log",\r
- NULL };\r
-\r
-const char* s_deleteUsage[] = {\r
- "<command>: delete [<opts>] <dir_or_file1> [<dir_or_file2> ...]",\r
- " delete the specified files",\r
- NULL };\r
-const char* s_batchExamples[] = {\r
- "dirtool batch -cbackup.bat --basename-pattern=;*.txt;*.doc e:\\data",\r
- "dirtool batch --type=r '--arguments=backup.sh !basename! !path!' usr etc",\r
- NULL };\r
-\r
-const char* s_listUsage[] = {\r
- "<command>: l(ist) [<opts>] <dir_or_file1> [<dir_or_file2> ...]",\r
- " lists the metadata (size, modification date ...) of the selected files",\r
- NULL };\r
-const char* s_listExamples[] =\r
- { "dirtool list --min-size=10M e:\\data",\r
- "dirtool li --type=f -y7d --size=10M -p;*.cpp;*.hpp;Makefile*;-*~ /home/data",\r
- NULL };\r
-\r
-const char* s_checksumUsage[] =\r
- { "<command>: c(hecksum) [<opts>] <dir_or_file1> [<dir_or_file2> ...]",\r
- " shows a check sum of the given files or manages a checksum storage file in each directory for observing",\r
- NULL };\r
-const char* s_checksumExamples[] = { "dirtool ch --buffer-size=512 e:\\data",\r
- "dirtool check -cList -p;*.iso /home/iso /down/debian.iso",\r
- "dirtool checksum -cBuild -aMD5 /home",\r
- "dirtool checksum --command=Compare /home",\r
- "dirtool checksum -cUpdate -O/var/log/checksum.log /home",\r
- NULL };\r
-const char* s_randomUsage[] = { "<command>: r(andom) <opts> count until [from]",\r
- " displays random numbers.", " <count> count of generated numbers",\r
- " <until> the maximum (including) value of the numbers",\r
- " <from> the minimum (including) value of the numbers. Default: 1",\r
- NULL };\r
-const char* s_randomExamples[] = { "dirtool ra --multiple -w3 10 33",\r
- "dirtool rand -s 6 49", "dirtool rand --sort --width=1 5 50",\r
- "dirtool rand --sort --width=1 2 10",\r
- NULL };\r
-\r
-static const char* s_statisticUsage[] =\r
- { "<command>: st(atistic) [<opts>] <path1> [<path2> ...] [<depth>]",\r
- " shows a statistic about a directory tree",\r
- "<path> a directory path: relative or absolute",\r
- "<depth> 0: only the summary of <path> will be shown",\r
- " 1: shows the summery of each subdir of <path> and the total",\r
- " n: shows the summery of each subdir until level <n> and the total",\r
- " default: 1", "<opts_stat>:",\r
- NULL };\r
-const char* s_statisticExamples[] =\r
- { "dirtool st -q -t0 e:\\windows",\r
- "dirtool statistic --quiet --kbyte --trace-interval=60 ../mail ../remember 2",\r
- "dirtool stat -q --kbyte d:data 2",\r
- NULL };\r
-\r
-const char* s_syncUsage[] =\r
- { "<command>: sy(nchronize) <opts> <source1> [<source2> ...] <target>",\r
- " Synchronizes the content of a directory tree with another.",\r
- " Newer or missing files will be copied",\r
- " If a source name ends with the separator (/ in linux, \\ in win) the target",\r
- " will be completed with the basename of the source.", " Example:",\r
- " 'sync home backup' copies the file home/x.txt to backup/home/x.txt",\r
- " 'sync data/ backup/data2' copies the file home/x.txt to backup/data2/x.txt",\r
- NULL };\r
-const char* s_syncExamples[] = {\r
- "dirtool sync --basename-pattern=;*.txt;*.doc e:\\data\\ d:\\backup\\data2",\r
- "dirtool sync --type=r --max-size=1G usr etc /media/backup",\r
- NULL };\r
-\r
-const char* s_tcpUsage[] = {\r
- "<command>: tcp [<opts>] <subcommand> [<param> ...]",\r
- " test tool for network test", "<subcommand>:", " server",\r
- " client <ip> [<direction> [<rounds> [<print_interval>]]]",\r
- " <ip>: URL of the server", " <rounds>: number of messages to send",\r
- " <direction>: 'upload', 'download' or 'mixed'",\r
- NULL };\r
-const char* s_tcpExamples[] = { "dirtool tcp -p 5555 server",\r
- "dirtool tcp -p 5555 client localhost download 10000 10",\r
- "dirtool tcp -p 5555 --buffer-size=1024 client 192.168.7.3 upload 10 25",\r
- NULL };\r
-\r
-const char* s_touchUsage[] =\r
- { "<command>: touch [<opts>] <dir_or_file1> [<dir_or_file2> ...]",\r
- " sets the filetimes (modification and/or access time) of the selected files",\r
- NULL };\r
-const char* s_touchExamples[] = {\r
- "dirtool touch -p;*.csv -m2014.12.1/8:12 e:\\data e:\\history\\readme.txt",\r
- "dirtool touch -p;*.cpp --modified=now-4d12H /home/data",\r
- "dirtool touch -p;*.csv -m+1m . e:\\data e:\\history\\readme.txt",\r
- NULL };\r
-\r
-const char* s_whichUsage[] = {\r
- "<command>: w(hicht) [<opts>] <file_or_pattern1> [<file_or_pattern2> ...]",\r
- " finds a file in a path list (like PATH)",\r
- NULL };\r
-const char* s_whichExamples[] = {\r
- "dirtool which --list=JAVA_CLASSPATH org.jdom.jar *.jdom*.jar",\r
- "dirtool which find.exe",\r
- NULL };\r
-/**\r
- * Constructor.\r
- *\r
- * @param usage a string vector with a message how to use the command\r
- * @param example a string vector with some examples how to use the command\r
- */\r
-ReDirOptions::ReDirOptions(const char* usage[], const char* examples[]) :\r
- ReTraceUnit(INT_MAX, INT_MAX),\r
- m_programArgs(usage, examples),\r
- m_nodePatterns(),\r
- m_pathPatterns(),\r
- m_compoundUsage(NULL),\r
- m_countCompoundUsage(0),\r
- m_output(stdout),\r
- m_verboseLevel(V_NORMAL) {\r
- m_nodePatterns.setIgnoreCase(true);\r
- m_pathPatterns.setIgnoreCase(true);\r
-}\r
-/**\r
- * Destructor.\r
- */\r
-ReDirOptions::~ReDirOptions() {\r
- close();\r
- delete[] m_compoundUsage;\r
-}\r
-/**\r
- * Adds a usage component to the compound usage message list.\r
- * @param usage a string vector containing a part of the usage message\r
- */\r
-void ReDirOptions::addCompoundUsage(const char** usage) {\r
- int start = 0;\r
- while (m_compoundUsage[start] != NULL) {\r
- if (++start >= m_countCompoundUsage)\r
- assert(false);\r
- }\r
- for (int ix = 0; usage[ix] != NULL; ix++) {\r
- if (start + ix > m_countCompoundUsage) {\r
- assert(false);\r
- break;\r
- }\r
- m_compoundUsage[start + ix] = usage[ix];\r
- }\r
-}\r
-\r
-/**\r
- * Adds the standard filter options.\r
- */\r
-void ReDirOptions::addStandardFilterOptions() {\r
- // standard short options: D d O o P p T t v y Z z\r
- m_programArgs.addInt("maxdepth",\r
- i18n("the depth of the subdirectory is lower or equal <number>\n"\r
- "0: search is done only in the base directory"), 'D', "max-depth",\r
- 512);\r
- m_programArgs.addInt("mindepth",\r
- i18n("the depth of the subdirectory is greater or equal <number>\n"\r
- "0: search is done in all subdirectories"), 'd', "min-depth", 0);\r
- m_programArgs.addString("output", i18n("the name of the output file.\n"\r
- "The output will written to this file instead of stdout"), 'O',\r
- "output-file", false, NULL);\r
- m_programArgs.addString("older",\r
- i18n(\r
- "the modification date is older than <string>\n"\r
- "<string> is a date (e.g. 2015.02.17) or number followed by an unit\n"\r
- "units: m(inutes) h(hours), d(days). Default: m(inutes)\n"\r
- "examples: -o25 --older-than=30d -o24h -o2009.3.2/12:00 -o1999.01.01"),\r
- 'o', "older-than", false, NULL);\r
- m_programArgs.addString("pathpattern",\r
- i18n(\r
- "a list of patterns for the path (without basename)\n"\r
- "the separator is the first character of the list\n"\r
- "Each pattern can contain '*' as wildcard\n"\r
- "If the first character is '-' the pattern is a 'not pattern':\n"\r
- "A directory will be entered if at least one of the positive patterns\n"\r
- "and none of the 'not patterns' matches\n"\r
- "examples:\n"\r
- "';music;pic*' enters music and xy/Music and PIC and pictures but not xy/pic and img\n"\r
- "';*;-.git;.hg' ignores .git and xy/z/.git and .ht"), 'P',\r
- "path-pattern", false, NULL);\r
- m_programArgs.addString("nodepattern",\r
- i18n(\r
- "a list of patterns for the basename (name without path) separated by ';'\n"\r
- "Each pattern can contain '*' as wildcard\n"\r
- "If the first character is '-' the pattern is a 'not pattern':\n"\r
- "A file will be found if at least one of the positive patterns and none\n"\r
- "of the 'not patterns' matches\n"\r
- "examples: '*.cpp;*.hpp;Make*' '*;-*.bak;-*~"), 'p',\r
- "basename-pattern", false, NULL);\r
- m_programArgs.addString("verbose",\r
- i18n(\r
- "verbose level: 0: no info, 1: summary only, 2: normal, 3: chatter mode, 4: debug"),\r
- 'v', "verbose", false, "1");\r
- m_programArgs.addInt("trace",\r
- i18n("all <number> seconds the current path will be traced\n"\r
- "0: no trace"), 'T', "trace-interval", 0);\r
- m_programArgs.addString("type", i18n("the file type\n"\r
- "<string> is a list of <v> values:\n"\r
- "<v>: b(lock) c(har) d(irectory) (l)i(nkdir) l(ink) o(ther)\n"\r
- " p(ipe) s(ocket) r(egular)\n"\r
- "<v>-sets: S(pecial)=bcspo N(ondir)=Slr\n"\r
- "examples: -td --type=dr -tNi"), 't', "type", false, NULL);\r
- m_programArgs.addString("younger",\r
- i18n(\r
- "the modification date is younger than <string>\n"\r
- "<string> is a date (e.g. 2015.02.17) or number followed by an unit\n"\r
- "units: m(inutes) h(hours), d(days). Default: m(inutes)"), 'y',\r
- "younger-than", false, NULL);\r
- m_programArgs.addString("maxsize",\r
- i18n(\r
- "the filesize is greater or equal <string>\n"\r
- "<string> is a number followed by an unit\n"\r
- "units: b(yte) k(Byte) K(iByte) m(Byte), M(iByte), g(Byte) G(iByte)\n"\r
- "examples: -Z50m --max-size=1G"), 'Z', "max-size", false, NULL);\r
- m_programArgs.addString("minsize",\r
- i18n(\r
- "the filesize is greater or equal <string>\n"\r
- "<string> is a number followed by an unit\n"\r
- "units: b(yte) k(Byte) K(iByte) m(Byte), M(iByte), g(Byte) G(iByte)\n"\r
- "examples: -z50m --min-size=1G"), 'z', "min-size", false, NULL);\r
-}\r
-\r
-/**\r
- * Checks whether the given value is a time expression.\r
- *\r
- * Possible: <date> <date_time> <n><unit>\r
- * Units: m(inutes) h(our) d(ays) s(econds)\r
- *\r
- * @param value value to check\r
- * @return the value converted into the absolute (or relative) time\r
- * @throws ReOptionExecption\r
- */\r
-ReFileTime_t ReDirOptions::checkDate(const char* value) {\r
- ReByteBuffer theValue(value, -1);\r
- time_t rcTime;\r
- if (theValue.count(".") == 2) {\r
- // a date:\r
- int year, month, day;\r
- int hour = 0;\r
- int minute = 0;\r
- int sec = 0;\r
- switch (sscanf(value, "%d.%d.%d/%d:%d:%d", &year, &month, &day, &hour,\r
- &minute, &sec)) {\r
- case 3:\r
- case 4:\r
- case 5:\r
- case 6: {\r
- if (year < 1980)\r
- throw ReOptionException(&m_programArgs,\r
- i18n("date < 1980.01.01: $1"), value);\r
- struct tm time;\r
- memset(&time, 0, sizeof time);\r
- time.tm_year = year - 1900;\r
- time.tm_mon = month - 1;\r
- time.tm_mday = day;\r
- time.tm_hour = hour;\r
- time.tm_min = minute;\r
- time.tm_sec = sec;\r
- rcTime = mktime(&time);\r
- break;\r
- }\r
- default:\r
- throw ReOptionException(&m_programArgs,\r
- i18n(\r
- "invalid date/date-time value: $1 yyyy.mm.dd/hh:MM expected"),\r
- value);\r
- }\r
- } else if (theValue.count(":") >= 1) {\r
- // a time:\r
- int hour = 0;\r
- int minute = 0;\r
- int sec = 0;\r
- switch (sscanf(value, "%d:%d:%d", &hour, &minute, &sec)) {\r
- case 2:\r
- case 3: {\r
- // the time (today)\r
- rcTime = time(NULL) / 86400 * 86400 + hour * 3600 + minute * 60\r
- + sec;\r
- break;\r
- }\r
- default:\r
- throw ReOptionException(&m_programArgs,\r
- i18n("invalid time value: $1. HH:MM:SS expected"), value);\r
- }\r
- } else {\r
- // a time distance value:\r
- char unit = 'm';\r
- int count = 0;\r
- int negativeFactor = 1;\r
- bool fromNow = theValue.startsWith("now");\r
- if (fromNow) {\r
- theValue.remove(0, 3);\r
- }\r
- char cc = theValue.at(0);\r
- if (cc == '-' || cc == '+') {\r
- if (cc == '-')\r
- negativeFactor = -1;\r
- theValue.remove(0, 1);\r
- }\r
- switch (sscanf(theValue.str(), "%d%c", &count, &unit)) {\r
- case 1:\r
- case 2:\r
- switch (unit) {\r
- case 's':\r
- break;\r
- case 'm':\r
- count *= 60;\r
- break;\r
- case 'h':\r
- count *= 60 * 60;\r
- break;\r
- case 'd':\r
- count *= 24 * 60 * 60;\r
- break;\r
- default:\r
- throw ReOptionException(&m_programArgs,\r
- i18n(\r
- "invalid unit $1. expected: s(econds) m(inutes) h(ours) d(ays)"),\r
- value);\r
- }\r
- rcTime = count * negativeFactor;\r
- break;\r
- default:\r
- throw ReOptionException(&m_programArgs,\r
- i18n(\r
- "invalid relative time value $1 (<number><unit> expected). <unit>: s m h d"),\r
- value);\r
- }\r
- if (fromNow)\r
- rcTime += time(NULL);\r
- }\r
- ReFileTime_t rc;\r
- ReDirStatus_t::timeToFiletime(rcTime, rc);\r
- return rc;\r
-}\r
-/**\r
- * Checks whether the given value is a time expression.\r
- *\r
- * Possible: <date> <date_time> <n><unit>\r
- * Units: m(inutes) h(our) d(ays)\r
- *\r
- * @param value value to check\r
- * @return the value (multiplied with the unit factor)\r
- * @throws ReOptionExecption\r
- */\r
-int64_t ReDirOptions::checkSize(const char* value) {\r
- int64_t rc = 0;\r
- char unit = 'b';\r
- switch (sscanf(value, "%lld%c", (long long int*) &rc, &unit)) {\r
- case 1:\r
- case 2:\r
- switch (unit) {\r
- case 'b':\r
- break;\r
- case 'k':\r
- rc *= 1000;\r
- break;\r
- case 'K':\r
- rc *= 1024;\r
- break;\r
- case 'm':\r
- rc *= 1000 * 1000;\r
- break;\r
- case 'M':\r
- rc *= 1024 * 1024;\r
- break;\r
- case 'g':\r
- rc *= 1000LL * 1000 * 1000;\r
- break;\r
- case 'G':\r
- rc *= 1024LL * 1024 * 1024;\r
- break;\r
- default:\r
- throw ReOptionException(&m_programArgs,\r
- i18n("invalid <unit>: $1. Expected: b k K m M g G"), value);\r
- }\r
- break;\r
- default:\r
- throw ReOptionException(&m_programArgs,\r
- i18n(\r
- "invalid size value: $1 expected: <number><unit> <unit>: b k K m M g G"),\r
- value);\r
- }\r
- return rc;\r
-}\r
-/**\r
- * Checks whether the given value is a valid pattern list.\r
- *\r
- * The first character must be a separator (not '*', '.' and not a letter)\r
- *\r
- * @param value value to check\r
- * @return the <cpde>value</code> (for chaining)\r
- * @throws ReOptionExecption\r
- */\r
-const char* ReDirOptions::checkPatternList(const char* value) {\r
- if (isalnum(*value) || *value == '_' || *value == '*' || *value == '.'\r
- || *value == '-')\r
- throw ReOptionException(&m_programArgs,\r
- i18n("invalid separator (first character): $1 use ';' instead"),\r
- value);\r
- if (strchr(value, OS_SEPARATOR_CHAR) != NULL)\r
- throw ReOptionException(&m_programArgs,\r
- i18n("slash not allowed in pattern list: $1"), value);\r
- return value;\r
-}\r
-/**\r
- * Checks whether the given value is a valid filetype list.\r
- *\r
- * @param value value to check\r
- * @return the bitmask\r
- * @throws ReOptionExecption\r
- */\r
-ReDirStatus_t::Type_t ReDirOptions::checkType(const char* value) {\r
- int rc = ReDirStatus_t::TF_UNDEF;\r
- while (*value != '\0') {\r
- switch (*value) {\r
- case 'b':\r
- rc |= ReDirStatus_t::TF_BLOCK;\r
- break;\r
- case 'c':\r
- rc |= ReDirStatus_t::TF_CHAR;\r
- break;\r
- case 'd':\r
- rc |= ReDirStatus_t::TF_SUBDIR;\r
- break;\r
- case 'i':\r
- rc |= ReDirStatus_t::TF_LINK_DIR;\r
- break;\r
- case 'l':\r
- rc |= ReDirStatus_t::TF_LINK;\r
- break;\r
- case 'o':\r
- rc |= ReDirStatus_t::TF_OTHER;\r
- break;\r
- case 'p':\r
- rc |= ReDirStatus_t::TF_PIPE;\r
- break;\r
- case 's':\r
- rc |= ReDirStatus_t::TF_SOCKET;\r
- break;\r
- case 'r':\r
- rc |= ReDirStatus_t::TF_REGULAR;\r
- break;\r
- case 'S':\r
- rc |= ReDirStatus_t::TC_SPECIAL;\r
- break;\r
- case 'N':\r
- rc |= ReDirStatus_t::TC_NON_DIR;\r
- break;\r
- case ' ':\r
- case ',':\r
- break;\r
- default:\r
- throw ReOptionException(&m_programArgs,\r
- i18n("invalid type: $1 Expected: b(lock) c(har) d(irectory)"\r
- " (l)i(nkdir) l(ink) o(ther) p(ipe) s(ocket) r(egular)"\r
- " S(pecial=bcspo) N(ondir=Slr)"), value);\r
- }\r
- value++;\r
- }\r
- return (ReDirStatus_t::Type_t) rc;\r
-}\r
-\r
-/**\r
- * Prints a help message, the error message and exits.\r
- *\r
- * @param errorMessage the error message.\r
- * @param message2 an additional message\r
- */\r
-\r
-void ReDirOptions::help(const char* errorMessage, const char* message2) const {\r
- ReByteBuffer msg;\r
- if (errorMessage != 0)\r
- msg.append(errorMessage, -1);\r
- if (message2 != NULL)\r
- msg.append(message2, -1);\r
- m_programArgs.help(msg.str(), false, stdout);\r
- exit(1);\r
-}\r
-\r
-/**\r
- * Checks the correctness of the standard filter options.\r
- * \r
- * @throws \r
- */\r
-void ReDirOptions::checkStandardFilterOptions() {\r
- ReByteBuffer buffer;\r
- if (m_programArgs.getString("older", buffer)[0] != '\0')\r
- checkDate(buffer.str());\r
- if (m_programArgs.getString("younger", buffer)[0] != '\0')\r
- checkDate(buffer.str());\r
- if (m_programArgs.getString("type", buffer)[0] != '\0')\r
- checkType(buffer.str());\r
- if (m_programArgs.getString("maxsize", buffer)[0] != '\0')\r
- checkSize(buffer.str());\r
- if (m_programArgs.getString("minsize", buffer)[0] != '\0')\r
- checkSize(buffer.str());\r
- if (m_programArgs.getString("nodepattern", buffer)[0] != '\0')\r
- checkPatternList(buffer.str());\r
- if (m_programArgs.getString("pathpattern", buffer)[0] != '\0')\r
- checkPatternList(buffer.str());\r
- if (m_programArgs.getString("verbose", buffer)[0] != '\0') {\r
- unsigned level = V_NORMAL;\r
- if (ReStringUtils::lengthOfUnsigned(buffer.str(), -1, &level)\r
- != (int) buffer.length())\r
- help(i18n("verbose level is not a number (or '')"), buffer.str());\r
- else\r
- m_verboseLevel = VerboseLevel(level);\r
- }\r
-}\r
-\r
-/**\r
- * Frees the resources.\r
- */\r
-void ReDirOptions::close() {\r
- if (m_output != stdout && m_output != stderr) {\r
- fclose(m_output);\r
- }\r
- m_output = stdout;\r
-}\r
-\r
-/**\r
- * Initializes the compound usage message array.\r
- *\r
- * @param size the size of the array: size = (field1 + field2 + ...) * sizeof(const char*)\r
- */\r
-void ReDirOptions::initCompoundUsage(size_t size) {\r
- delete[] m_compoundUsage;\r
- int count = size / sizeof m_compoundUsage[0];\r
- m_compoundUsage = new const char*[count];\r
- memset(m_compoundUsage, 0, size);\r
- m_countCompoundUsage = count;\r
-}\r
-\r
-/**\r
- * Optimizes the path patterns.\r
- *\r
- * For all patterns of the list:\r
- * <ul><li>remove a trailing "\" and "\*"</li>\r
- * <li>change "*\xy" to "*\xy" and "xy" (finds xy in the root directory)</li>\r
- * <li>replaces "/" with the os specific path separator</li>\r
- * </ul>\r
- *\r
- * @param buffer the pattern list as string, e.g. ";*;-cache"\r
- */\r
-void ReDirOptions::optimizePathPattern(ReByteBuffer& buffer) {\r
- ReStringList list;\r
- ReStringList rootList;\r
- list.split(buffer.str() + 1, buffer.str()[0]);\r
- buffer.replaceAll(OS_SEPARATOR, 1, "/", 1);\r
- ReByteBuffer item;\r
- for (int ix = 0; ix < (int) list.count(); ix++) {\r
- item.set(list.strOf(ix), -1);\r
- if (item.endsWith("/*"))\r
- item.setLength(item.length() - 2);\r
- item.removeLastChar('/');\r
- bool notAnchored = item.startsWith("*/") || item.startsWith("-*/");\r
- item.replaceAll("/", 1, OS_SEPARATOR, 1);\r
- list.replace(ix, item.str());\r
- if (notAnchored) {\r
- item.remove(item.str()[0] == '-' ? 1 : 0, 2);\r
- rootList.append(item.str(), 0);\r
- }\r
- }\r
- if (rootList.count() > 0) {\r
- list.append(rootList);\r
- }\r
- item.set(buffer.str(), 1);\r
- buffer.set(item);\r
- list.join(item.str(), buffer, true);\r
-}\r
-/**\r
- * Sets the standard filter options given by the program arguments.\r
- *\r
- * @param filter OUT: the filter to set\r
- */\r
-void ReDirOptions::setFilterFromProgramArgs(ReDirEntryFilter& filter) {\r
- ReByteBuffer buffer;\r
- if (m_programArgs.getString("younger", buffer)[0] != '\0')\r
- filter.m_maxAge = checkDate(buffer.str());\r
- if (m_programArgs.getString("older", buffer)[0] != '\0')\r
- filter.m_minAge = checkDate(buffer.str());\r
- if (m_programArgs.getString("maxsize", buffer)[0] != '\0')\r
- filter.m_maxSize = checkSize(buffer.str());\r
- if (m_programArgs.getString("minsize", buffer)[0] != '\0')\r
- filter.m_minSize = checkSize(buffer.str());\r
- if (m_programArgs.getString("type", buffer)[0] != '\0')\r
- filter.m_types = checkType(buffer.str());\r
- filter.m_minDepth = m_programArgs.getInt("mindepth");\r
- filter.m_maxDepth = m_programArgs.getInt("maxdepth");\r
- if (m_programArgs.getString("nodepattern", buffer)[0] != '\0') {\r
- checkPatternList(buffer.str());\r
- m_nodePatterns.set(buffer.str());\r
- filter.m_nodePatterns = &m_nodePatterns;\r
- }\r
- if (m_programArgs.getString("pathpattern", buffer)[0] != '\0') {\r
- checkPatternList(buffer.str());\r
- optimizePathPattern(buffer);\r
- m_pathPatterns.set(buffer.str());\r
- filter.m_pathPatterns = &m_pathPatterns;\r
- }\r
- if ((m_interval = m_programArgs.getInt("trace")) != 0)\r
- m_triggerCount = 10;\r
- if (m_programArgs.getString("output", buffer)[0] != '\0') {\r
- if ((m_output = fopen(buffer.str(), "w")) == NULL) {\r
- help("cannot open output file", buffer.str());\r
- m_output = stdout;\r
- }\r
- }\r
-}\r
-\r
-/**\r
- * Constructor.\r
- *\r
- * @param usage a string vector with a message how to use the command\r
- * @param example a string vector with some examples how to use the command\r
- * @param minArguments minimal count of non option arguments\r
- * @param reservedFirst count of arguments at the start which are not files\r
- * @param reservedLast count of arguments at the end which are not files\r
- * @param addCurrentDirIfNoArguments\r
- * <code>true</code>: if no arguments are given the current\r
- * directory will be added as argument\r
- * @param logger logger for error messages\r
- */\r
-#pragma warning( push )\r
-#pragma warning( disable : 4355 )\r
-ReTool::ReTool(const char* usage[], const char* example[], int minArguments,\r
- int reservedFirst, int reservedLast, bool addCurrentDirIfNoArguments,\r
- ReLogger* logger) :\r
- ReDirOptions(usage, example),\r
- ReDirTreeStatistic(),\r
- m_minArguments(minArguments),\r
- m_reservedFirst(reservedFirst),\r
- m_reservedLast(reservedLast),\r
- m_addCurrentDirIfNoArguments(addCurrentDirIfNoArguments),\r
- m_hasStandardArgs(true),\r
- m_traverser(NULL, this, logger),\r
- m_filter(),\r
- m_start(time(NULL)),\r
- m_logger(logger) {\r
-#pragma warning( pop )\r
-}\r
-\r
-/**\r
- * Destructor.\r
- */\r
-ReTool::~ReTool() {\r
-}\r
-\r
-/**\r
- * Prints a message.\r
- *\r
- * Often overwritten by a subclass.\r
- *\r
- * @param currentFile message for the trace\r
- * @return <code>true</code> (for chaining)\r
- */\r
-bool ReTool::trace(const char* currentFile) {\r
- ReByteBuffer buffer(" ");\r
- int duration = int(time(NULL) - m_startTime);\r
- buffer.appendInt(duration / 60).appendInt(duration % 60, ":%02d: ");\r
- buffer.appendInt(m_files).appendChar('/').appendInt(\r
- m_traverser.directories()).append(" dir(s)");\r
- buffer.appendInt(m_files).appendChar('/').appendInt(m_traverser.files())\r
- .append(" file(s)");\r
- buffer.append(currentFile);\r
- fputs(buffer.str(), stdout);\r
- return true;\r
-}\r
-\r
-/**\r
- * Evaluates the arguments and calls the main function.\r
- *\r
- * @param argc number of arguments. If -1: <code>argv</code> ends with <code>NULL</code>\r
- * @param argv program arguments\r
- * @param minArguments the command needs at least so many non option arguments\r
- */\r
-void ReTool::run(int argc, const char** argv) {\r
- try {\r
- if (argc < 0) {\r
- for (argc = 0; argv[argc] != NULL; argc++) {\r
- // nothing to do\r
- }\r
- }\r
- m_programArgs.init(argc, argv);\r
- if (m_programArgs.argCount() < m_minArguments)\r
- m_programArgs.help(i18n("too few arguments"), false, stdout);\r
- if (m_hasStandardArgs)\r
- setFilterFromProgramArgs(m_filter);\r
- doIt();\r
- if (m_output != stdout) {\r
- fclose(m_output);\r
- m_output = stdout;\r
- }\r
-\r
- } catch (ReOptionException& exc) {\r
- m_programArgs.help(exc.getMessage(), false, stdout);\r
- }\r
-}\r
-/**\r
- * Evaluates the non option arguments which are names of files or directories.\r
- *\r
- * For each non reserved argument it will be called either <code>processSingleFile()</code>\r
- * or <code>processTree()</code>.\r
- *\r
- */\r
-void ReTool::processFileArguments() {\r
- int max = m_programArgs.argCount() - m_reservedLast;\r
- // Test whether the arguments are files or directories:\r
- ReByteBuffer arg;\r
- for (int ii = m_reservedFirst; ii < max; ii++) {\r
- arg = m_programArgs.arg(ii);\r
- if (!exists(arg) != 0)\r
- m_programArgs.help(\r
- ReByteBuffer(i18n("not a file or a directory: ")).append(arg)\r
- .str(), false, stderr);\r
- }\r
- // process the files:\r
- for (int ii = m_reservedFirst; ii < max; ii++) {\r
- const char* arg = m_programArgs.arg(ii);\r
- if (S_ISDIR(m_statInfo.st_mode))\r
- processTree(arg);\r
- else\r
- processSingleFile(arg);\r
- }\r
-}\r
-/**\r
- * Processes a single file.\r
- *\r
- * Gets the file info and calls <code>processFile()</code>.\r
- *\r
- * @param filename the name of the file\r
- */\r
-void ReTool::processSingleFile(const char* filename) {\r
- ReByteBuffer protocol;\r
- ReByteBuffer path;\r
- ReByteBuffer name;\r
- ReByteBuffer ext;\r
- ReStringUtils::splitPath(filename, &protocol, &path, &name, &ext);\r
- protocol.append(path);\r
- if (protocol.length() == 0)\r
- protocol.append(".");\r
- else\r
- protocol.reduceLength();\r
- m_traverser.changeBase(protocol.str());\r
-\r
- name.append(ext);\r
- setFilterFromProgramArgs(m_filter);\r
- m_traverser.setPropertiesFromFilter(&m_filter);\r
- ReDirStatus_t entry(m_logger);\r
- entry.m_path = protocol;\r
- if (entry.findFirst()) {\r
- do {\r
-#if defined __linux__\r
- bool found = strcmp(entry.node(), name.str()) == 0;\r
-#elif defined __WIN32__\r
- bool found = _stricmp(entry.node(), name.str()) == 0;\r
-#endif\r
- if (found && m_filter.match(entry)) {\r
- processFile(&entry);\r
- break;\r
- }\r
- } while (entry.findNext());\r
- }\r
- entry.freeEntry();\r
-}\r
-/**\r
- * Processes a directory tree.\r
- *\r
- * Finds all filtered files in this directory and its subdirs and call <code>processFile()</code>\r
- * or <code>processDir()</code>.\r
- *\r
- * @param directory the name of the directory\r
- */\r
-void ReTool::processTree(const char* directory) {\r
- m_traverser.changeBase(directory);\r
- m_traverser.setPropertiesFromFilter(&m_filter);\r
- ReDirStatus_t* entry;\r
- int level;\r
- while ((entry = m_traverser.nextFile(level, &m_filter)) != NULL) {\r
- if (entry->isDirectory()) {\r
- processDir(entry);\r
- m_directories++;\r
- } else {\r
- processFile(entry);\r
- m_files++;\r
- m_sizes += entry->fileSize();\r
- }\r
- }\r
-}\r
-/**\r
- * Processes one file.\r
- *\r
- * Normally this method will be overwritten.\r
- *\r
- * @param entry the properties of the file to process\r
- */\r
-void ReTool::processFile(ReDirStatus_t* entry) {\r
- fprintf(m_output, "+++ ignored (not a directory): %s\n", entry->fullName());\r
-}\r
-/**\r
- * Issues a summary message if verbose level allows this.\r
- * \r
- * @param prefix NULL or a line prefix\r
- */\r
-void ReTool::printSummary(const char* prefix) {\r
- if (m_verboseLevel >= V_SUMMARY) {\r
- int duration = int(time(NULL) - m_start);\r
- ReByteBuffer line;\r
- ReByteBuffer line2;\r
- statisticAsString(line);\r
- double rate =\r
- duration == 0 ? 0.0 : (m_files + m_directories) / duration;\r
- line.append(rate, " %.1f").append(i18n("/sec"), -1);\r
- m_traverser.statisticAsString(line2);\r
- line2.appendChar(' ').appendTime(duration).append(" ", 1).append(\r
- i18n("sec"));\r
- fprintf(m_output, "%s=== filtered: %s\n", prefix == NULL ? "" : prefix,\r
- line.str());\r
- fprintf(m_output, "%s=== total: %s\n", prefix == NULL ? "" : prefix,\r
- line2.str());\r
- }\r
-\r
-}\r
-/**\r
- * Processes one directory.\r
- *\r
- * Normally this method will be overwritten.\r
- *\r
- * @param entry the properties of the directory to process\r
- */\r
-void ReTool::processDir(ReDirStatus_t* entry) {\r
- fprintf(m_output, "+++ ignored (not a file): %s\n", entry->fullName());\r
-}\r
-\r
-/**\r
- * Constructor.\r
- */\r
-ReDirStatisticData::ReDirStatisticData() :\r
- ReDirTreeStatistic(),\r
- m_path() {\r
-}\r
-/**\r
- * Copy constructor.\r
- *\r
- * @param source the source to copy\r
- */\r
-ReDirStatisticData::ReDirStatisticData(const ReDirStatisticData& source) :\r
- m_path(source.m_path) {\r
-}\r
-/**\r
- * Assignment operator.\r
- *\r
- * @param source the source to copy\r
- * @return the instance itself\r
- */\r
-ReDirStatisticData& ReDirStatisticData::operator =(\r
- const ReDirStatisticData& source) {\r
- m_sizes = source.m_sizes;\r
- m_files = source.m_files;\r
- m_directories = source.m_directories;\r
- m_path = source.m_path;\r
- return *this;\r
-}\r
-\r
-/**\r
- * Constructor.\r
- *\r
- * @param logger logger for error handling\r
- * @param deltaList increment if list must be increased\r
- * @param deltaBuffer increment if content buffer must be increased\r
- */\r
-ReDirStatistic::ReDirStatistic(ReLogger* logger, int deltaList, int deltaBuffer) :\r
- ReTool(s_statisticUsage, s_statisticExamples, 2, 0, 1, false, logger),\r
- m_list(deltaList, deltaBuffer),\r
- m_traceInterval(60),\r
- m_lastTrace(0) {\r
- // standard short options: D d O o P p T t v y Z z\r
- m_programArgs.addBool("kbyte",\r
- i18n("output format is '<kbyte> <path>' (like unix 'du' command)"), 'k',\r
- "kbyte", false);\r
- addStandardFilterOptions();\r
-}\r
-/**\r
- * Destructor.\r
- */\r
-ReDirStatistic::~ReDirStatistic() {\r
-}\r
-\r
-/**\r
- * Adds the data from another instance.\r
- *\r
- * @param source the other instance\r
- * @return the instance itself\r
- */\r
-ReDirStatisticData& ReDirStatisticData::add(const ReDirStatisticData& source) {\r
- m_sizes += source.m_sizes;\r
- m_files += source.m_files;\r
- m_directories += source.m_directories;\r
- return *this;\r
-}\r
-/**\r
- * Initializes the data of the instance.\r
- */\r
-void ReDirStatisticData::clear() {\r
- ReDirTreeStatistic::clear();\r
- m_path.setLength(0);\r
-}\r
-/**\r
- * Constructor.\r
- *\r
- * @param logger logger for error handling\r
- */\r
-ReDirBatch::ReDirBatch(ReLogger* logger) :\r
- ReTool(s_batchUsage, s_batchExamples, 0, 0, 0, true, logger),\r
- m_arguments(),\r
- m_script(),\r
- m_isExe(false) {\r
- // standard short options: D d O o P p T t v y Z z\r
- m_programArgs.addString("first",\r
- i18n("defines the first line of the output"), '1', "first-line", true,\r
-#if defined __linux__\r
- "#! /bin/sh"\r
-#elif defined __WIN32__\r
- "rem this batch is created by dirtool"\r
-#endif\r
-) ;\r
- m_programArgs.addString("arguments", i18n("template for the output line.\n"\r
- "Possible placeholders: (e.g. e:\\data\\sample.txt)\n"\r
- " !full!: e:\\data\\sample.txt\n"\r
- " !path!: e:\\data\\\n"\r
- " !basename!: sample.txt\n"\r
- " !name!: sample\n"\r
- " !ext!: .txt\n"\r
- "example: --arguments='echo !basename! in !path! found'"), 'a',\r
- "arguments", false, NULL);\r
- m_programArgs.addString("script",\r
- i18n("name of the script (starts each output line)"), 'c', "script",\r
- false, NULL);\r
-#if defined __WIN32__\r
- m_programArgs.addBool("isexe",\r
- i18n("supresses the starting 'call' of each output line"\r
- "neccessary if a *.exe will be called (instead of a *.bat)"),\r
- 'x', "is-exe", false);\r
-#endif\r
- addStandardFilterOptions();\r
-}\r
-\r
-static void replaceMakros(const char* arguments, ReDirStatus_t* entry,\r
- const char* delim, ReByteBuffer& line) {\r
- line.set(arguments, -1);\r
- // we prepare the removal of unwanted delimiters in constructed placeholders:\r
- // example: !path!!name!: without correction: "e:\\data\\""xxx"\r
- // We want: "e:\\data\\xxx"\r
- line.replaceAll("!!", 2, "!\x01!", 3);\r
- ReByteBuffer replacement;\r
- if (strstr(arguments, "!full!") != NULL) {\r
- replacement.set(delim, -1).append(entry->m_path);\r
- replacement.append(entry->node(), -1).append(delim, -1);\r
- line.replaceAll("!full!", 6, replacement.str(), replacement.length());\r
- }\r
- if (strstr(arguments, "!path!") != NULL) {\r
- replacement.set(delim, -1).append(entry->m_path).append(delim, -1);\r
- line.replaceAll("!path!", 6, replacement.str(), replacement.length());\r
- }\r
- if (strstr(arguments, "!basename!") != NULL) {\r
- replacement.set(delim, -1).append(entry->node(), -1).append(delim, -1);\r
- line.replaceAll("!basename!", 10, replacement.str(),\r
- replacement.length());\r
- }\r
- if (strstr(arguments, "!name!") != NULL) {\r
- replacement.set(delim, -1).append(entry->node(), -1);\r
- int ix = replacement.rindexOf(".", 1);\r
- if (ix > 1)\r
- replacement.setLength(ix);\r
- replacement.append(delim, -1);\r
- line.replaceAll("!name!", 6, replacement.str(), replacement.length());\r
- }\r
- if (strstr(arguments, "!ext!") != NULL) {\r
- replacement.set(delim, -1).append(entry->node(), -1);\r
- int ix = replacement.rindexOf(".", 1);\r
- if (ix > 1)\r
- replacement.remove(1, ix - 1);\r
- else\r
- replacement.setLength(1);\r
- replacement.append(delim, -1);\r
- line.replaceAll("!ext!", 5, replacement.str(), replacement.length());\r
- }\r
- // We remove the unwanted delimiters (see above):\r
- ReByteBuffer buffer;\r
- buffer.set(delim, -1).appendChar('\01').append(delim, -1);\r
- line.replaceAll(buffer.str(), buffer.length(), "", 0);\r
-}\r
-/**\r
- * Creates the batch file.\r
- */\r
-void ReDirBatch::doIt() {\r
- ReByteBuffer buffer;\r
- m_programArgs.getString("arguments", m_arguments);\r
- m_programArgs.getString("script", m_script);\r
- if (m_arguments.length() + m_script.length() == 0)\r
- help(\r
- i18n(\r
- "one of the option must be set: -a (--arguments) or -c (--script)"));\r
-#if defined __WIN32__\r
- m_isExe = m_programArgs.getBool("isexe");\r
-#endif\r
- m_programArgs.getString("first", buffer);\r
- if (buffer.length() > 0)\r
- fprintf(m_output, "%s\n", buffer.str());\r
- processFileArguments();\r
-#if defined __linux__\r
- static const char* prefix = "# ";\r
-#elif defined __WIN32__\r
- static const char* prefix = "rem ";\r
-#endif\r
- printSummary(prefix);\r
-}\r
-\r
-/**\r
- * Processes one directory.\r
- *\r
- * @param entry the properties of the directory to process\r
- */\r
-void ReDirBatch::processDir(ReDirStatus_t* entry) {\r
- processFile(entry);\r
-}\r
-\r
-/**\r
- * Processes one file.\r
- *\r
- * @param entry the properties of the file to process\r
- */\r
-void ReDirBatch::processFile(ReDirStatus_t* entry) {\r
- ReByteBuffer line;\r
-#if defined __linux__\r
- static const char* delim = "'";\r
-#elif defined __WIN32__\r
- static const char* delim = "\"";\r
-#endif\r
- if (m_script.length() > 0) {\r
-#if defined __WIN32__\r
- if (! m_isExe)\r
- line.append("call ");\r
-#endif\r
- line.append(m_script).appendChar(' ').appendChar(delim[0]);\r
- line.append(entry->m_path).append(entry->node(), -1);\r
- line.appendChar(delim[0]);\r
- } else {\r
- replaceMakros(m_arguments.str(), entry, delim, line);\r
- }\r
- fprintf(m_output, "%s\n", line.str());\r
-}\r
-\r
-/**\r
- * Constructor.\r
- *\r
- * @param logger logger for error handling\r
- */\r
-ReDirChecksum::ReDirChecksum(ReLogger* logger) :\r
- ReTool(s_checksumUsage, s_checksumExamples, 0, 0, 0, true, logger),\r
- m_command(CMD_LIST),\r
- m_digest(NULL),\r
- m_buffer() {\r
- // standard short options: D d O o P p T t v y Z z\r
- m_programArgs.addString("algorithm", i18n("algorithm: 'MD5' or 'RPD64'"),\r
- 'a', "--algorithm", false, "MD5");\r
- m_programArgs.addInt("buffersize",\r
- i18n("buffer size for file reading (in KiByte)"), 'b', "--buffer-size",\r
- 4 * 1024);\r
- m_programArgs.addString("command",\r
- i18n(\r
- "'build': builds in each directory a file '.dt.chksum' with sum and name\n"\r
- "'compare': shows differences between the content of '.dt.chksum' and the current files\n"\r
- "'list': shows checksum and filename\n"\r
- "'update': like compare, but update the content of '.dt.chksum'"),\r
- 'c', "--command", false, "list");\r
- m_programArgs.addInt("salt", i18n("a number which change the checksum"),\r
- 's', "--salt", 0);\r
- addStandardFilterOptions();\r
-}\r
-\r
-/**\r
- * Destructor.\r
- */\r
-ReDirChecksum::~ReDirChecksum() {\r
- delete m_digest;\r
- m_digest = NULL;\r
-}\r
-/**\r
- * Builds a checksum storage for a given directory.\r
- *\r
- * @param path the directory to process\r
- */\r
-void ReDirChecksum::buildStorage(const char* path, const char* storageFile) {\r
- ReTraverser traverser(path);\r
- traverser.setMaxLevel(0);\r
- ReDirStatus_t* entry;\r
- ReRPD64 digest2;\r
- digest2.setSalt(0x2004199111121989ll);\r
- ReByteBuffer line;\r
- FILE* fp = fopen(storageFile, "w");\r
- if (fp == NULL) {\r
- m_logger->sayF(LOG_ERROR | CAT_FILE, LC_BUILD_DIRECTORY_1,\r
- i18n("cannot open file: $1 (errno: $2)")).arg(storageFile).arg(\r
- errno).end();\r
-\r
- } else {\r
- int level;\r
- while ((entry = traverser.nextFile(level, &m_filter)) != NULL) {\r
- line.setLength(0);\r
- if (!entry->isDirectory()) {\r
- calculateChecksum(entry->fullName(), *m_digest, m_buffer,\r
- m_logger);\r
- line.append(m_digest->hexDigest());\r
- }\r
- line.appendChar('\t').append(entry->node());\r
- digest2.update(line);\r
- fprintf(fp, "%s\n", line.str());\r
- if (m_verboseLevel >= V_NORMAL)\r
- fprintf(m_output, "%16s\t%s",\r
- entry->isDirectory() ? "" : m_digest->hexDigest().str(),\r
- entry->fullName());\r
- }\r
- fprintf(fp, "%s\n", digest2.hexDigest().str());\r
- fclose(fp);\r
- }\r
-}\r
-\r
-/**\r
- * Compares the files of a directory with the files in the storage file\r
- *\r
- * @param path the directory\r
- * @param storageFile the file with the checksums\r
- */\r
-void ReDirChecksum::compareDir(const char* path, ReStringList& names,\r
- const char* storageFile) {\r
- // forget the last line (checksum):\r
- int count = names.count() - 1;\r
- names.remove(count);\r
- for (int ii = 0; ii < count; ii++) {\r
- const char* name = names.strOf(ii);\r
- const char* ptr = strchr(name, '\t');\r
- if (ptr != NULL)\r
- names.replaceString(ii, ptr + 1);\r
- }\r
- names.sort();\r
- names.setSorted(true);\r
- ReTraverser traverser(path);\r
- traverser.setMaxLevel(0);\r
- ReDirStatus_t* entry;\r
- int level;\r
- while ((entry = traverser.nextFile(level, &m_filter)) != NULL) {\r
- ReSeqArray::Index index;\r
- const char* node = entry->node();\r
- // the stringlist stores the string with trailing '\0'\r
- if (!names.binarySearch(node, strlen(node) + 1, index)) {\r
- m_logger->sayF(LOG_ERROR | CAT_SECURITY, LC_COMPARE_DIR_1,\r
- i18n("missing file $1 in storage $2")).arg(entry->fullName())\r
- .arg(storageFile).end();\r
- }\r
- }\r
-}\r
-\r
-/**\r
- * Compares a storage file with the files of a directory.\r
- *\r
- * @param path the directory\r
- * @param storageFile the file with the checksums\r
- */\r
-void ReDirChecksum::compareStorage(const char* path, const char* storageFile) {\r
- ReStringList storage;\r
- storage.readFromFile(storageFile);\r
- if (storage.count() <= 0) {\r
- m_logger->sayF(LOG_ERROR | CAT_SECURITY, LC_COMPARE_STORAGE_1,\r
- i18n("empty storage file: $1")).arg(storageFile).end();\r
- } else if (!isValid(storage)) {\r
- m_logger->sayF(LOG_ERROR | CAT_SECURITY, LC_COMPARE_STORAGE_2,\r
- i18n("corrupted storage file: $1")).arg(storageFile).end();\r
- } else {\r
- ReStringList cols;\r
- ReByteBuffer fullname(path);\r
- fullname.ensureLastChar(OS_SEPARATOR_CHAR);\r
- int pathLength = fullname.length();\r
- struct stat info;\r
- // test all files of the storage:\r
- for (size_t ii = 0; ii < storage.count() - 1; ii++) {\r
- cols.split(storage.strOf(ii), '\t');\r
- int colCount = cols.count();\r
- if (colCount != 2) {\r
- m_logger->sayF(LOG_ERROR | CAT_SECURITY, LC_COMPARE_STORAGE_3,\r
- i18n("wrong format ($1) in storage file $2-$3")).arg(\r
- colCount).arg(storageFile).arg(ii + 1).end();\r
- break;\r
- } else {\r
- fullname.setLength(pathLength).append(cols.strOf(1));\r
- if (cols.strLengthOf(0) == 0) {\r
- // a directory:\r
- if (stat(fullname.str(), &info) != 0) {\r
- m_logger->sayF(LOG_ERROR | CAT_SECURITY,\r
- LC_COMPARE_STORAGE_4,\r
- i18n("missing directory $1 ($2-$3)")).arg(fullname)\r
- .arg(storageFile).arg(ii + 1).end();\r
- } else if (!S_ISDIR(info.st_mode)) {\r
- m_logger->sayF(LOG_ERROR | CAT_SECURITY,\r
- LC_COMPARE_STORAGE_5,\r
- i18n("directory is now a file: $1 ($2-$3)")).arg(\r
- fullname).arg(storageFile).arg(ii + 1).end();\r
- }\r
- } else {\r
- // a directory:\r
- if (stat(fullname.str(), &info) != 0) {\r
- m_logger->sayF(LOG_ERROR | CAT_SECURITY,\r
- LC_COMPARE_STORAGE_6,\r
- i18n("missing file $1 ($2-$3)")).arg(fullname).arg(\r
- storageFile).arg(ii + 1).end();\r
- } else if (S_ISDIR(info.st_mode)) {\r
- m_logger->sayF(LOG_ERROR | CAT_SECURITY,\r
- LC_COMPARE_STORAGE_7,\r
- i18n("file is now a directory: $1 ($2-$3)")).arg(\r
- fullname).arg(storageFile).arg(ii + 1).end();\r
- } else {\r
- calculateChecksum(fullname.str(), *m_digest, m_buffer,\r
- m_logger);\r
- if (!m_digest->hexDigest().equals(cols.strOf(0))) {\r
- m_logger->sayF(LOG_ERROR | CAT_SECURITY,\r
- LC_COMPARE_STORAGE_7,\r
- i18n("checksum different: $1 ($2-$3)")).arg(\r
- fullname).arg(storageFile).arg(ii + 1).end();\r
- }\r
- }\r
- }\r
- }\r
- }\r
- compareDir(path, storage, storageFile);\r
- }\r
-}\r
-/**\r
- * Checks the validity of a storage file.\r
- *\r
- * The storage file is protected with a checksum.\r
- * The checksum has another salt as the checksums of the files. This makes it\r
- * much harder to manipulate the storage file.\r
- *\r
- * @param storage the storage file as string list.\r
- */\r
-bool ReDirChecksum::isValid(const ReStringList& storage) {\r
- int count = storage.count();\r
- ReRPD64 digest2;\r
- digest2.setSalt(0x2004199111121989ll);\r
-\r
- for (int ii = 0; ii < count - 1; ii++) {\r
- const char* line = storage.strOf(ii);\r
- digest2.update(line);\r
- }\r
- bool rc = true;\r
- const char* hex = storage.strOf(count - 1);\r
- if (!digest2.hexDigest().equals(hex))\r
- rc = false;\r
- return rc;\r
-}\r
-/**\r
- * Lists the metadata of the specified files.\r
- */\r
-void ReDirChecksum::doIt() {\r
- int size = m_programArgs.getInt("buffersize") * 1024;\r
- m_buffer.setLength(size);\r
- ReByteBuffer value;\r
- m_programArgs.getString("command", value);\r
- if (value.equals("list", -1, true))\r
- m_command = CMD_LIST;\r
- else if (value.equals("build", -1, true))\r
- m_command = CMD_BUILD;\r
- else if (value.equals("compare", -1, true))\r
- m_command = CMD_COMPARE;\r
- else if (value.equals("update", -1, true))\r
- m_command = CMD_UPDATE;\r
- else\r
- help(i18n("unknown command (expected: build compare list update): "),\r
- value.str());\r
-\r
- m_programArgs.getString("algorithm", value);\r
- if (value.equals("md5", -1, true))\r
- m_digest = new ReMD5();\r
- else if (value.equals("rpd64", -1, true))\r
- m_digest = new ReRPD64();\r
- else\r
- help(i18n("unknown algorithm (expected: MD5 RPD64): "), value.str());\r
- int salt = m_programArgs.getInt("salt");\r
- if (salt != 0)\r
- m_digest->setSalt(salt);\r
- m_filter.m_allDirectories = true;\r
- processFileArguments();\r
- printSummary();\r
-}\r
-\r
-/**\r
- * Processes one directory.\r
- *\r
- * @param entry the properties of the directory to process\r
- */\r
-void ReDirChecksum::processDir(ReDirStatus_t* entry) {\r
- ReByteBuffer storageFile;\r
- if (m_command != CMD_LIST) {\r
- storageFile.append(entry->fullName());\r
- storageFile.ensureLastChar(OS_SEPARATOR_CHAR);\r
- storageFile.append(".dt.chksum");\r
- }\r
- switch (m_command) {\r
- case CMD_BUILD:\r
- buildStorage(entry->fullName(), storageFile.str());\r
- break;\r
- case CMD_COMPARE:\r
- compareStorage(entry->fullName(), storageFile.str());\r
- break;\r
- case CMD_UPDATE:\r
- printf("update not implemented");\r
- break;\r
- default:\r
- break;\r
- }\r
-}\r
-/**\r
- * Processes one file.\r
- *\r
- * @param entry the properties of the file to process\r
- */\r
-void ReDirChecksum::processFile(ReDirStatus_t* entry) {\r
- const char* name = entry->fullName();\r
- switch (m_command) {\r
- case CMD_LIST:\r
- calculateChecksum(name, *m_digest, m_buffer, m_logger);\r
- fprintf(m_output, "%s %s\n", m_digest->hexDigest().str(), name);\r
- break;\r
- default:\r
- break;\r
- }\r
-}\r
-/**\r
- * Compares a storage file with the files of a directory.\r
- *\r
- * @param path the directory\r
- * @param storageFile the file with the checksums\r
- */\r
-void ReDirChecksum::updateStorage(const char* path, const char* storageFile) {\r
-\r
-}\r
-\r
-/**\r
- * Calculates the checksum of a given file.\r
- *\r
- * @param name the full filename\r
- * @param digest IN: defines the kind of checksum<br>\r
- * OUT: contains the calculated checksum\r
- * @param buffer IN/OUT: a buffer for file reading\r
- * @param logger logger for error processing\r
- * @return <code>digest</code> (for chaining)\r
- */\r
-ReDigest& ReDirChecksum::calculateChecksum(const char* name, ReDigest& digest,\r
- ReByteBuffer& buffer, ReLogger* logger) {\r
- FILE* fp = fopen(name, "rb");\r
- if (fp == NULL) {\r
- if (logger != NULL)\r
- logger->sayF(LOG_ERROR | CAT_FILE, LC_CALCULATE_CHECKSUM_1,\r
- i18n("cannot open file: $1 (errno: $2)")).arg(name).arg(\r
- errno).end();\r
- } else {\r
- ReMD5 digest;\r
- size_t readBytes;\r
- uint8_t* buf = reinterpret_cast<uint8_t*>(buffer.buffer());\r
- size_t blockSize = buffer.length();\r
-\r
- while ((readBytes = fread(buf, 1, blockSize, fp)) > 0) {\r
- digest.update(buf, readBytes);\r
- }\r
- fclose(fp);\r
- }\r
- return digest;\r
-}\r
-\r
-/**\r
- * Constructor.\r
- *\r
- * @param logger logger for error handling\r
- */\r
-ReDirDelete::ReDirDelete(ReLogger* logger) :\r
- ReTool(s_deleteUsage, s_deleteExamples, 0, 0, 0, true, logger) {\r
- addStandardFilterOptions();\r
-}\r
-\r
-/**\r
- * Deletes the specified files/directories.\r
- */\r
-void ReDirDelete::doIt() {\r
- m_traverser.setDepthFirst(true);\r
- processFileArguments();\r
- printSummary();\r
-}\r
-\r
-/**\r
- * Processes one directory.\r
- *\r
- * @param entry the properties of the directory to process\r
- */\r
-void ReDirDelete::processDir(ReDirStatus_t* entry) {\r
- _rmdir(entry->fullName());\r
-}\r
-/**\r
- * Processes one file.\r
- *\r
- * @param entry the properties of the file to process\r
- */\r
-void ReDirDelete::processFile(ReDirStatus_t* entry) {\r
- const char* name = entry->fullName();\r
- if (m_verboseLevel >= V_NORMAL) {\r
- fprintf(m_output, "%s\n", name);\r
- }\r
- if (_unlink(name) != 0)\r
- m_logger->sayF(LOG_ERROR | CAT_FILE, LC_DELETE_1,\r
- i18n("cannot delete ($1): $2")).arg(errno).arg(name).end();\r
-}\r
-\r
-/**\r
- * Constructor.\r
- *\r
- * @param logger logger for error handling\r
- */\r
-ReDirList::ReDirList(ReLogger* logger) :\r
- ReTool(s_listUsage, s_listExamples, 0, 0, 0, true, logger),\r
- m_widthOwner(13),\r
- m_shortFormat(false),\r
- m_withRights(false),\r
- m_numerical(false) {\r
- // standard short options: D d O o P p T t v y Z z\r
- m_programArgs.addBool("short", i18n("output is only path and basename"),\r
- '1', "--short", false);\r
- m_programArgs.addBool("rights", i18n("show the permission/right info"), 'r',\r
- "--rights", false);\r
- m_programArgs.addBool("numerical",\r
- i18n("the permission/right info is shown as numbers"), 'n',\r
- "--numerical", false);\r
- m_programArgs.addInt("owner", i18n("space reserved for owner/group"), 'w',\r
- "--width-owner", 13);\r
- addStandardFilterOptions();\r
-}\r
-\r
-/**\r
- * Lists the metadata of the specified files.\r
- */\r
-void ReDirList::doIt() {\r
- m_shortFormat = m_programArgs.getBool("short");\r
- m_withRights = m_programArgs.getBool("rights");\r
- m_numerical = m_programArgs.getBool("numerical");\r
- m_widthOwner = m_programArgs.getInt("owner");\r
- processFileArguments();\r
- printSummary();\r
-}\r
-\r
-/**\r
- * Processes one directory.\r
- *\r
- * @param entry the properties of the directory to process\r
- */\r
-void ReDirList::processDir(ReDirStatus_t* entry) {\r
- processFile(entry);\r
-}\r
-/**\r
- * Processes one file.\r
- *\r
- * @param entry the properties of the file to process\r
- */\r
-void ReDirList::processFile(ReDirStatus_t* entry) {\r
- ReByteBuffer bufferRights;\r
- ReByteBuffer bufferTime;\r
- if (m_shortFormat)\r
- fprintf(m_output, "%s%s\n", entry->m_path.str(), entry->node());\r
- else {\r
- if (m_withRights)\r
- entry->rightsAsString(bufferRights, m_numerical, m_widthOwner);\r
- fprintf(m_output, "%c%s %12.6f %s %s%s\n", entry->typeAsChar(),\r
- bufferRights.str(), entry->fileSize() / 1E6,\r
- entry->filetimeAsString(bufferTime), entry->m_path.str(),\r
- entry->node());\r
- }\r
-}\r
-\r
-/**\r
- * Constructor.\r
- *\r
- * @param logger logger for error handling\r
- */\r
-ReDirRandom::ReDirRandom(ReLogger* logger) :\r
- ReTool(s_randomUsage, s_randomExamples, 2, 0, 0, true, logger) {\r
- m_hasStandardArgs = false;\r
- m_programArgs.addBool("multiple",\r
- i18n("result can contain a value multiple times"), 'm', "--multiple",\r
- false);\r
- m_programArgs.addInt("perline", i18n("number of values per line"), 'l',\r
- "--values-per-line", 10);\r
- m_programArgs.addBool("sort", i18n("the result is sorted"), 's', "--sort",\r
- false);\r
- m_programArgs.addInt("width",\r
- i18n("output format: minimum width of the numbers"\r
- " 0: width of the maximum"), 'w', "--min-width", 0);\r
-}\r
-\r
-/**\r
- * Lists the metadata of the specified files.\r
- */\r
-void ReDirRandom::doIt() {\r
- bool multiple = m_programArgs.getBool("multiple");\r
- bool sort = m_programArgs.getBool("sort");\r
- int count = atol(m_programArgs.arg(0));\r
- int to = atol(m_programArgs.arg(1));\r
- int from = 1;\r
- if (m_programArgs.argCount() > 2)\r
- from = atol(m_programArgs.arg(2));\r
- ReShiftRandom rand;\r
- rand.setSeed(rand.nearTrueRandom());\r
- for (int ii = rand.nextInt(100); ii > 0; ii--)\r
- rand.nextInt();\r
- int factor = to <= 1000 ? 1000 * 1000 : 1;\r
- int numbersPerLine = m_programArgs.getInt("perline");\r
- int width = m_programArgs.getInt("width");\r
- if (width <= 0)\r
- width = ReByteBuffer("").appendInt(to).length();\r
- ReByteBuffer format("%");\r
- format.appendInt(width).append("d%s");\r
- int found = 0;\r
- ReSeqArray values(count);\r
- ReByteBuffer value;\r
- if (!multiple && count >= to - from + 1)\r
- help(\r
- i18n(\r
- "not realy random: all possible values requested. Do you mean '--multiple'?"));\r
- while (found < count) {\r
- value.setLength(0).appendInt(\r
- from + rand.nextInt((to - from + 1) * factor) / factor, "%010d");\r
- int ix = (int) values.find(value.str());\r
- if (!multiple && ix != -1)\r
- continue;\r
- found++;\r
- values.add(-1, value.str());\r
- }\r
- if (sort)\r
- values.sort();\r
- for (int ii = 0; ii < count; ii++) {\r
- values.get(ii, value);\r
- int val = atol(value.str());\r
- printf(format.str(), val,\r
- ii % numbersPerLine == numbersPerLine - 1 ? "\n" : " ");\r
- }\r
- if (count % numbersPerLine != 0)\r
- printf("\n");\r
-}\r
-\r
-/**\r
- * Calculates the statistic of a directory tree.\r
- *\r
- *\r
- */\r
-const ReStringList& ReDirStatistic::calculate(const char* base, int level,\r
- void (*formatter)(const ReDirStatisticData& data, ReDirStatistic& parent,\r
- ReByteBuffer& line)) {\r
- ReDirEntryFilter filter;\r
- ReTraverser traverser(base, this);\r
- setFilterFromProgramArgs(filter);\r
- traverser.setPropertiesFromFilter(&filter);\r
- if (level > 1024)\r
- level = 1024;\r
- else if (level < 0)\r
- level = 0;\r
- m_list.clear();\r
- ReDirStatisticData** dataStack = new ReDirStatisticData*[level + 1];\r
- memset(dataStack, 0, sizeof dataStack[0] * (level + 1));\r
- dataStack[0] = new ReDirStatisticData();\r
- int topOfStack = 0;\r
- ReDirStatus_t* entry;\r
- int currentDepth = -1;\r
- ReDirStatisticData* current = dataStack[0];\r
- current->m_path.set(base, -1);\r
- ReByteBuffer line;\r
- bool useFilter = filter.m_minSize > 0 || filter.m_maxSize != -1\r
- || !filetimeIsUndefined(filter.m_minAge)\r
- || !filetimeIsUndefined(filter.m_maxAge) || m_nodePatterns.count() > 0;\r
- while ((entry = traverser.rawNextFile(currentDepth))) {\r
- if (currentDepth <= level && !entry->m_path.equals(current->m_path)) {\r
- if (currentDepth <= topOfStack) {\r
- // close the entries of the stack above the new top level:\r
- while (topOfStack >= currentDepth) {\r
- // Add the data to the parent:\r
- if (topOfStack > 0)\r
- dataStack[topOfStack - 1]->add(*dataStack[topOfStack]);\r
- // Append it to the result:\r
- (*formatter)(*dataStack[topOfStack], *this, line);\r
- m_list.append(line);\r
- topOfStack--;\r
- }\r
- // We reuse the top of stack:\r
- topOfStack++;\r
- current = dataStack[topOfStack];\r
- // exchange the top of stack with the new found directory:\r
- current->clear();\r
- current->m_path.set(entry->m_path.str(),\r
- entry->m_path.length());\r
- ;\r
- } else {\r
- // set up a new stack entry:\r
- if (currentDepth != topOfStack + 1)\r
- assert(currentDepth == topOfStack + 1);\r
-\r
- topOfStack++;\r
- if (dataStack[topOfStack] == NULL)\r
- dataStack[topOfStack] = new ReDirStatisticData();\r
- else\r
- dataStack[topOfStack]->clear();\r
- current = dataStack[topOfStack];\r
- current->m_path.set(entry->m_path.str(),\r
- entry->m_path.length());\r
- }\r
- }\r
- if (entry->isDirectory()) {\r
- current->m_directories++;\r
- } else if (!useFilter || filter.match(*entry)) {\r
- current->m_sizes += entry->fileSize();\r
- current->m_files++;\r
- }\r
- }\r
- // close all dirs with parents:\r
- while (topOfStack > 0) {\r
- // Add the data to the parent:\r
- dataStack[topOfStack - 1]->add(*dataStack[topOfStack]);\r
- // Append it to the result:\r
- (*formatter)(*dataStack[topOfStack], *this, line);\r
- m_list.append(line);\r
- topOfStack--;\r
- }\r
- // ... and the overall summery:\r
- (*formatter)(*dataStack[0], *this, line);\r
- m_list.append(line);\r
- // free the resources:\r
- for (int ix = 0; ix <= level; ix++)\r
- delete dataStack[ix];\r
- delete[] dataStack;\r
- return m_list;\r
-}\r
-\r
-/**\r
- * Build the statistic and print the results.\r
- *\r
- * @param arc count of arguments in <code>argv</code>\r
- * @param argv the program arguments.\r
- */\r
-void ReDirStatistic::doIt() {\r
- int depth = 1;\r
- if (m_programArgs.argCount() > 1) {\r
- const char* arg1 = m_programArgs.arg(1);\r
- if (ReStringUtils::lengthOfUnsigned(arg1, -1, (unsigned *) &depth) == 0)\r
- m_programArgs.help("depth is not an integer", false, stdout);\r
- }\r
- void (*proc)(const ReDirStatisticData& data, ReDirStatistic& parent,\r
- ReByteBuffer& line) = &formatWithSizeFilesAndDirs;\r
- if (m_programArgs.getBool("kbyte"))\r
- proc = &formatLikeDu;\r
- const ReStringList& list = calculate(m_programArgs.arg(0), depth, proc);\r
- ReByteBuffer buffer;\r
- for (size_t ix = 0; ix < list.count(); ix++) {\r
- buffer.set(list.strOf(ix), list.strLengthOf(ix));\r
- fprintf(m_output, "%s\n", buffer.str());\r
- }\r
- if (m_verboseLevel >= V_SUMMARY) {\r
- int duration = int(time(NULL) - m_start);\r
- fprintf(m_output, "=== duration: ");\r
- if (duration >= 3600)\r
- fprintf(m_output, "%d:", duration / 3600);\r
- fprintf(m_output, "%02d:%02d\n", duration % 3600 / 60, duration % 60);\r
- }\r
-}\r
-\r
-/**\r
- * Formats a line like the du (disk usage) command.\r
- *\r
- * This is a possible parameter of <code>ReDirStatistic::calculate()</code>.\r
- *\r
- * @param data statistic data, including path name\r
- * @param parent the caller (<code>ReDirStatistic</code>). This allows to deliver\r
- * a context to this formatting routine (through derivation of\r
- * <code>ReDirStatistic</code>)\r
- * @param line OUT: the formatted line, the conclusion of the statistic data\r
- */\r
-void formatLikeDu(const ReDirStatisticData& data, ReDirStatistic& parent,\r
- ReByteBuffer& line) {\r
- line.setLength(0);\r
- // Round up to the next KiByte:\r
- line.appendInt(int((data.m_sizes + 1023) / 1024)).append("\t").append(\r
- data.m_path);\r
-}\r
-\r
-/**\r
- * Formats a line in a standard way: MBytes, file count and directory count.\r
- *\r
- * This is a possible parameter of <code>ReDirStatistic::calculate()</code>.\r
- *\r
- * @param data statistic data, including path name\r
- * @param parent the caller (<code>ReDirStatistic</code>). This allows to deliver\r
- * a context to this formatting routine (through derivation of\r
- * <code>ReDirStatistic</code>)\r
- * @param line OUT: the formatted line, the conclusion of the statistic data\r
- */\r
-void formatWithSizeFilesAndDirs(const ReDirStatisticData& data,\r
- ReDirStatistic& parent, ReByteBuffer& line) {\r
- line.setLength(0);\r
- // Round up to the next KiByte:\r
- char buffer[256];\r
- _snprintf(buffer, sizeof buffer, "%14.6f MB %7d %7d\t", data.m_sizes / 1E6,\r
- data.m_files, data.m_directories);\r
- line.append(buffer, -1).append(data.m_path);\r
-}\r
-/**\r
- * Constructor.\r
- *\r
- * @param logger logger for error handling\r
- */\r
-ReDirTouch::ReDirTouch(ReLogger* logger) :\r
- ReTool(s_touchUsage, s_touchExamples, 1, 0, 0, false, logger),\r
- m_buffer()\r
-//m_modified()\r
-//m_accessed()\r
-{\r
- setFiletimeUndef(m_modified);\r
- setFiletimeUndef(m_accessed);\r
- // standard short options: D d O o P p T t v y Z z\r
- m_programArgs.addString("accessed", i18n("the new access time.\n"\r
- "Formats: absolute, relative to now, relative to the current filetime\n"\r
- "[yyyy.mm.dd/]HH:MM:SS \n"\r
- "now-<count>{s|m|h|d}\n"\r
- "{+|-}<count>{s|m|h|d}"), 'a', "--accessed", false, NULL);\r
- m_programArgs.addString("modified", i18n("the new modification time.\n"\r
- "Formats: absolute, relative to now, relative to the current filetime\n"\r
- "[yyyy.mm.dd/]HH:MM:SS\n"\r
- "now-<count>{s|m|h|d}\n"\r
- "{+|-}<count>{s|m|h|d}"), 'm', "--modified", false, NULL);\r
- addStandardFilterOptions();\r
-}\r
-/**\r
- * Sets the filetime for the specified files.\r
- */\r
-void ReDirTouch::doIt() {\r
- ReByteBuffer buffer;\r
- if (m_programArgs.getString("modified", buffer)[0] != '\0')\r
- m_modified = checkDate(buffer.str());\r
- if (m_programArgs.getString("accessed", buffer)[0] != '\0')\r
- m_accessed = checkDate(buffer.str());\r
- if (filetimeIsUndefined(m_modified) && filetimeIsUndefined(m_accessed))\r
- help("missing --modified and/or --accessed");\r
- processFileArguments();\r
- printSummary();\r
-}\r
-\r
-/**\r
- * Processes one directory.\r
- *\r
- * @param entry the properties of the directory to process\r
- */\r
-void ReDirTouch::processDir(ReDirStatus_t* entry) {\r
- processFile(entry);\r
-}\r
-\r
-static bool isAbsoluteTime(ReFileTime_t& time) {\r
-#if defined __linux__\r
- static struct tm year1980 = { 0, 0, 0, 1, 1 - 1, 1980 - 1900 };\r
- static time_t time1980 = mktime(&year1980);\r
- return time.tv_sec >= time1980;\r
-#elif defined __WIN32__\r
- static ReFileTime_t filetime1980 = {2148603904, 27846551};\r
- bool rc = time > filetime1980;\r
- return rc;\r
-#endif\r
-}\r
-static void addRelativeTime(ReFileTime_t& absTime,\r
- const ReFileTime_t& relTime) {\r
-#if defined __linux__\r
- if ((absTime.tv_nsec += relTime.tv_nsec) >= 1000 * 1000 * 1000) {\r
- absTime.tv_nsec -= 1000 * 1000 * 1000;\r
- absTime.tv_sec += 1;\r
- }\r
- absTime.tv_sec += relTime.tv_sec;\r
-#elif defined __WIN32__\r
- uint64_t absValue = ((uint64_t) absTime.dwHighDateTime << 32) | absTime.dwLowDateTime;\r
- uint64_t relValue = ((uint64_t) relTime.dwHighDateTime << 32) | relTime.dwLowDateTime;\r
- absValue += relValue;\r
- absTime.dwHighDateTime = (uint32_t) (absValue >> 32);\r
- absTime.dwLowDateTime = (uint32_t) absValue;\r
-#endif\r
-}\r
-/**\r
- * Processes one file.\r
- *\r
- * @param entry the properties of the file to process\r
- */\r
-void ReDirTouch::processFile(ReDirStatus_t* entry) {\r
- ReFileTime_t modified = *entry->modified();\r
- int countTimes = 0;\r
- if (!filetimeIsUndefined(m_modified)) {\r
- countTimes++;\r
- if (isAbsoluteTime(m_modified))\r
- modified = m_modified;\r
- else\r
- addRelativeTime(modified, m_modified);\r
- }\r
- ReFileTime_t accessed = *entry->accessed();\r
- if (!filetimeIsUndefined(m_accessed)) {\r
- countTimes++;\r
- if (isAbsoluteTime(m_accessed))\r
- accessed = m_accessed;\r
- else\r
- addRelativeTime(accessed, m_accessed);\r
- }\r
- const char* name = entry->fullName();\r
- if (touch(name, modified, accessed, ReLogger::globalLogger()) == 0\r
- && m_verboseLevel >= V_NORMAL) {\r
- ReByteBuffer bufferTime;\r
- ReByteBuffer bufferTime2;\r
- if (m_verboseLevel == V_NORMAL) {\r
- if (countTimes == 2)\r
- fprintf(m_output, "%s | %s | %s\n",\r
- ReDirStatus_t::filetimeToString(&modified, bufferTime),\r
- ReDirStatus_t::filetimeToString(&accessed, bufferTime2),\r
- name);\r
- else {\r
- ReDirStatus_t::filetimeToString(\r
- filetimeIsUndefined(m_modified) ? &accessed : &modified,\r
- bufferTime);\r
- fprintf(m_output, "%s %s\n", bufferTime.str(), name);\r
- }\r
- } else {\r
- ReByteBuffer bufferTime3;\r
- ReByteBuffer bufferTime4;\r
- if (countTimes == 2)\r
- fprintf(m_output, "%s -> %s | %s -> %s | %s\n",\r
- ReDirStatus_t::filetimeToString(entry->modified(),\r
- bufferTime),\r
- ReDirStatus_t::filetimeToString(&modified, bufferTime2),\r
- ReDirStatus_t::filetimeToString(entry->accessed(),\r
- bufferTime3),\r
- ReDirStatus_t::filetimeToString(&accessed, bufferTime4),\r
- name);\r
- else {\r
- ReDirStatus_t::filetimeToString(\r
- filetimeIsUndefined(m_modified) ? &m_accessed : &m_modified,\r
- bufferTime);\r
- fprintf(m_output, "%s -> %s %s\n",\r
- ReDirStatus_t::filetimeToString(entry->modified(),\r
- bufferTime2), bufferTime.str(), (char*) name);\r
- }\r
- }\r
- }\r
-}\r
-\r
-/**\r
- * Sets file times.\r
- *\r
- * @param filename the name of the file\r
- * @param properties contains modification and access time\r
- * @param logger NULL or the logger\r
- */\r
-ReErrNo_t ReDirTouch::touch(const char* filename, const ReFileTime_t& modified,\r
- const ReFileTime_t& accessed, ReLogger* logger) {\r
-#if defined __linux__\r
- timeval times[2];\r
- times[0].tv_sec = accessed.tv_sec;\r
- times[0].tv_usec = accessed.tv_nsec / 1000;\r
- times[1].tv_sec = modified.tv_sec;\r
- times[1].tv_usec = modified.tv_nsec / 1000;\r
- ReErrNo_t rc = utimes(filename, times) == 0 ? 0 : errno;\r
-\r
-#elif defined __WIN32__\r
- ReErrNo_t rc = 0;\r
- HANDLE handle = CreateFile(filename, FILE_WRITE_ATTRIBUTES,\r
- FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING,\r
- FILE_ATTRIBUTE_NORMAL, NULL);\r
- if (handle == INVALID_HANDLE_VALUE)\r
- rc = GetLastError();\r
- else {\r
- if (! SetFileTime(handle, (LPFILETIME) NULL, &accessed, &modified))\r
- rc = GetLastError();\r
- CloseHandle(handle);\r
- }\r
-#endif\r
- if (rc != 0 && logger != NULL)\r
- logger->sayF(LOG_ERROR | CAT_FILE, LC_TOUCH_1,\r
- i18n("cannot change filetime: $1 (errno: $2)")).arg(filename).arg(\r
- errno).end();\r
- return rc;\r
-}\r
-\r
-/**\r
- * Prints a vector of lines.\r
- * \r
- * @param lines a vector of lines without newline ('\n')\r
- */\r
-static void printField(const char** lines) {\r
- for (int ix = 0; lines[ix] != NULL; ix++) {\r
- printf("%s\n", lines[ix]);\r
- }\r
-}\r
-\r
-/**\r
- * Prints a message how to use the module and exits.\r
- */\r
-void ReDirTools::usage(ReTool& tool) {\r
- tool.programArgs().help(NULL, false, stdout);\r
-}\r
-\r
-/**\r
- * Prints an message how to use the program and exits.\r
- *\r
- * @param msg an error message\r
- * @param msg2 an addition to the error message or NULL\r
- */\r
-void ReDirTools::usage(const char* msg, const char* msg2) {\r
- printf("Version: %s\n", m_version);\r
- printf("usage: dirtool <command> <opt>\n"\r
- "call 'dirtool help' for more info\n");\r
- if (msg != NULL)\r
- printf("+++ %s%s\n", msg, msg2 == NULL ? "" : msg2);\r
- exit(1);\r
-}\r
-\r
-/**\r
- * creates a subdirectory (and the parent directories if neccessary.\r
- *\r
- * @param path the name of the subdir to create\r
- */\r
-void ReDirSync::makeDirWithParents(ReByteBuffer& path, int minWidth,\r
- ReTraverser& traverser) {\r
- if (!exists(path)) {\r
- ReFileProperties_t* props = NULL;\r
-#if defined __linux__\r
- ReDirStatus_t* entry = traverser.topOfStack();\r
- props = entry == NULL ? NULL : &entry->m_status;\r
-#endif\r
- makeDirectory(path.str(), minWidth, props, ReLogger::globalLogger());\r
- }\r
-}\r
-/**\r
- * Constructor.\r
- *\r
- * @param logger logger for error handling\r
- */\r
-ReDirSync::ReDirSync(ReLogger* logger) :\r
- ReTool(s_syncUsage, s_syncExamples, 2, 0, 1, false, logger),\r
- m_buffer() {\r
- // standard short options: D d O o P p T t v y Z z\r
- m_buffer.ensureSize(4u * 1024u * 1024u);\r
- m_programArgs.addBool("add",\r
- i18n("copies only files which does not exist on the target"), 'a',\r
- "add", false);\r
- m_programArgs.addBool("dry",\r
- i18n("does nothing, but says what should be done"), 'Y', "dry", false);\r
- m_programArgs.addInt("timediff",\r
- i18n("filetime difference is considered to be equal\n"\r
- "if the difference is less than this value (in seconds)"), 'I',\r
- "time-delta", 2);\r
- m_programArgs.addBool("ignoredate",\r
- i18n(\r
- "the modification is recognized only by the different size (not time)"),\r
- 'i', "ignore-time", false);\r
- m_programArgs.addBool("mustexist",\r
- i18n("files which don't exist on the target will not be copied"), 'm',\r
- "must-exist", false);\r
- addStandardFilterOptions();\r
-}\r
-\r
-/**\r
- * Copies a file.\r
- *\r
- * @param entry the source file info\r
- * @param target the name of the target file\r
- */\r
-void ReDirSync::copyFile(ReDirStatus_t* entry, const char* target) {\r
- ReFileProperties_t* props;\r
-#ifdef __linux__\r
- props = &entry->m_status;\r
-#else\r
- ReFileProperties_t properties;\r
- properties.m_modified = *entry->modified();\r
- properties.m_accessed = *entry->accessed();\r
- properties.m_size = entry->fileSize();\r
- props = &properties;\r
-#endif\r
- copyFile(entry->fullName(), props, target, m_buffer,\r
- ReLogger::globalLogger());\r
-}\r
-\r
-/**\r
- * Copies a file.\r
- *\r
- * @param source the source file name\r
- * @param properties NULL or the properties of the source file\r
- * @param target the name of the target file\r
- * @param buffer OUT: the reading uses this buffer<br>\r
- * Set the capacity to make it more efficient\r
- * @param logger NULL or the logger for error messages\r
- * @return <code>true</code>success<br>\r
- * <code>false</code>error occurred\r
- */\r
-bool ReDirSync::copyFile(const char* source, ReFileProperties_t* properties,\r
- const char* target, ReByteBuffer& buffer, ReLogger* logger) {\r
- bool rc = false;\r
-#ifdef __linux__\r
- struct stat info;\r
- if (properties == NULL) {\r
- if (stat(source, &info) == 0)\r
- properties = &info;\r
- else {\r
- if (logger != NULL)\r
- logger->sayF(LOG_ERROR | CAT_FILE, LC_COPY_FILE_1,\r
- i18n("could not find: $1 (errno: $2)")).arg(source).arg(\r
- errno).end();\r
- }\r
- }\r
- FILE* fpSource = fopen(source, "rb");\r
- if (fpSource == NULL) {\r
- if (logger != NULL)\r
- logger->sayF(LOG_ERROR | CAT_FILE, LC_COPY_FILE_2,\r
- i18n("cannot open $1 (errno: $2)")).arg(source).arg(errno).end();\r
- } else {\r
- ReFileSize_t size =\r
- properties == NULL ? 0x7fffffff : properties->st_size;\r
- FILE* fpTarget = fopen(target, "w");\r
- if (fpTarget == NULL) {\r
- if (logger != NULL)\r
- logger->sayF(LOG_ERROR | CAT_FILE, LC_COPY_FILE_3,\r
- i18n("cannot open $1 (errno: $2)")).arg(target).arg(errno)\r
- .end();\r
- } else {\r
- while (size > 0) {\r
- size_t blockSize = buffer.capacity();\r
- if ((int) blockSize > size)\r
- blockSize = size;\r
- if (fread(buffer.buffer(), blockSize, 1, fpSource) != 1) {\r
- if (logger != NULL)\r
- logger->sayF(LOG_ERROR | CAT_FILE, LC_COPY_FILE_5,\r
- i18n("cannot read $1 (errno: $2)")).arg(source).arg(\r
- errno).end();\r
- break;\r
- }\r
- size_t written;\r
- if ((written = fwrite(buffer.buffer(), 1, blockSize, fpTarget))\r
- != blockSize) {\r
- if (logger != NULL)\r
- logger->sayF(LOG_ERROR | CAT_FILE, LC_COPY_FILE_6,\r
- i18n("cannot write $1 [$2] (errno: $3)")).arg(\r
- target).arg(written).arg(errno).end();\r
- break;\r
- }\r
- size -= blockSize;\r
- }\r
- rc = size == 0ll;\r
- fclose(fpTarget);\r
- if (properties != NULL)\r
- setProperties(target, properties, logger);\r
- }\r
- fclose(fpSource);\r
- }\r
-#elif defined __WIN32__\r
- BOOL cancel = false;\r
- rc = CopyFileExA(source, target, NULL, NULL, &cancel, COPY_FILE_NO_BUFFERING) != 0;\r
- int errNo = 0;\r
- if (! rc)\r
- errNo = GetLastError();\r
-#endif\r
- return rc;\r
-}\r
-/**\r
- * Sets the file properties.\r
- *\r
- * @param fullName the name of the file\r
- * @param properties the properties like times and rights\r
- * @param logger NULL or the logger for error messages\r
- * @return <code>true</code>: success<br>\r
- * <code>false</code>: error occurred\r
- */\r
-bool ReDirSync::setProperties(const char* fullName,\r
- ReFileProperties_t* properties, ReLogger* logger) {\r
- bool rc = true;\r
-#if defined __linux__\r
- timeval times[2];\r
- times[0].tv_sec = properties->st_atim.tv_sec;\r
- times[0].tv_usec = properties->st_atim.tv_nsec / 1000;\r
- times[1].tv_sec = properties->st_mtim.tv_sec;\r
- times[1].tv_usec = properties->st_mtim.tv_nsec / 1000;\r
- if (utimes(fullName, times) != 0) {\r
- if (logger != NULL)\r
- logger->sayF(LOG_ERROR | CAT_FILE, LC_SET_PROPERTIES_1,\r
- i18n("cannot change file times: $1 (errno: $2)")).arg(fullName)\r
- .arg(errno).end();\r
- rc = false;\r
- }\r
- int rights = properties->st_mode & (S_IRWXO | S_IRWXG | S_IRWXU);\r
- if (chmod(fullName, rights) != 0) {\r
- if (logger != NULL)\r
- logger->sayF(LOG_ERROR | CAT_FILE, LC_SET_PROPERTIES_2,\r
- i18n("cannot change file modes: $1 (errno: $2)")).arg(fullName)\r
- .arg(errno).end();\r
- rc = false;\r
- }\r
- if (chown(fullName, properties->st_uid, properties->st_gid) != 0) {\r
- if (logger != NULL)\r
- logger->sayF(LOG_ERROR | CAT_FILE, LC_SET_PROPERTIES_3,\r
- i18n("cannot change file owner: $1 (errno: $2)")).arg(fullName)\r
- .arg(errno).end();\r
- rc = false;\r
- }\r
-#endif\r
- return rc;\r
-}\r
-\r
-/**\r
- * Creates a directory and its parents (if neccessary).\r
- *\r
- * @param directory the full name of the directory\r
- * @param properties NULL or the properties of the new directory\r
- * @param logger NULL or the logger for error messages\r
- * @return <code>true</code>success<br>\r
- * <code>false</code>error occurred\r
- */\r
-bool ReDirSync::makeDirectory(const char* directory, int minLength,\r
- ReFileProperties_t* properties, ReLogger* logger) {\r
- bool rc = true;\r
- ReByteBuffer path(directory);\r
- int start = 0;\r
-#if defined __WIN32__\r
- start = path.indexOf(':');\r
-#endif\r
- path.ensureLastChar(OS_SEPARATOR_CHAR);\r
- int ixSlash = start < 0 ? 0 : start;\r
- struct stat info;\r
- // for all parents and the full path itself:\r
- while (ixSlash >= 0) {\r
- ixSlash = path.indexOf(OS_SEPARATOR_CHAR, ixSlash + 1);\r
- // is the slash in front of the first node, e.g. 'e:\'?\r
- if (ixSlash == start + 1)\r
- // not a real node: take the next node\r
- ixSlash = path.indexOf(OS_SEPARATOR_CHAR, ixSlash + 1);\r
- if (ixSlash >= 0) {\r
- // we handle only the next node:\r
- path.buffer()[ixSlash] = '\0';\r
- }\r
- // does the node exist?\r
- if (lstat(path.str(), &info) != 0) {\r
- // no, then we make it:\r
- if (_mkdir(path.str(), ALLPERMS) != 0) {\r
- if (logger != NULL)\r
- logger->sayF(LOG_ERROR | CAT_FILE, LC_MAKE_DIR_1,\r
- i18n("could not make directory $1 (errno: $2)")).arg(\r
- path.str()).arg(errno).end();\r
- rc = false;\r
- break;\r
- } else {\r
-#if defined __linux__\r
- setProperties(path.str(), properties);\r
-#endif\r
- }\r
- }\r
- if (ixSlash >= 0) {\r
- // restore the full path:\r
- path.buffer()[ixSlash] = OS_SEPARATOR_CHAR;\r
- }\r
- }\r
- return rc;\r
-}\r
-\r
-/**\r
- * Synchronizes two directory trees.\r
- */\r
-void ReDirSync::doIt() {\r
- ReDirEntryFilter filter;\r
- const char* sep = OS_SEPARATOR;\r
- ReByteBuffer buffer;\r
- ReByteBuffer target(m_programArgs.arg(m_programArgs.argCount() - 1));\r
- target.removeLastChar(OS_SEPARATOR_CHAR);\r
- if (!exists(target))\r
- help(i18n("target does not exist: $1"), target.str());\r
- else if (!S_ISDIR(m_statInfo.st_mode))\r
- help(i18n("target is not a directory: $1"), target.str());\r
- size_t lengthTargetBase = target.length();\r
- bool addOnly = m_programArgs.getBool("add");\r
- int maxFileTimeDiff = m_programArgs.getInt("timediff");\r
- bool dry = m_programArgs.getBool("dry");\r
- bool ignoreDate = m_programArgs.getBool("ignoredate");\r
- bool mustExist = m_programArgs.getBool("mustexist");\r
- setFilterFromProgramArgs(filter);\r
- int64_t sumSizes = 0;\r
- int files = 0;\r
- int treeFiles = 0;\r
- int treeDirs = 0;\r
- int64_t treeSumSizes = 0ll;\r
- ReByteBuffer source, targetFile;\r
- for (int ix = 0; ix < m_programArgs.argCount() - 1; ix++) {\r
- source.set(m_programArgs.arg(ix), -1);\r
- target.setLength(lengthTargetBase);\r
- bool endsWithSlash = source.endsWith(sep, 1);\r
- if (endsWithSlash)\r
- source.reduceLength();\r
- if (!exists(source))\r
- help(i18n("source does not exist: $1"), source.str());\r
- else if (!S_ISDIR(m_statInfo.st_mode))\r
- help(i18n("source is not a directory: $1"), source.str());\r
- if (!endsWithSlash) {\r
- // the basename of the source will be appended to the target:\r
- int startNode = source.rindexOf(sep, 1, 0, source.length() - 1);\r
- target.appendChar(OS_SEPARATOR_CHAR);\r
- target.append(source.str() + startNode + 1, -1);\r
- }\r
- size_t ixSourceRelative = source.length();\r
- size_t ixTargetRelative = target.length();\r
-\r
- m_traverser.changeBase(source.str());\r
- m_traverser.setPropertiesFromFilter(&filter);\r
- int level;\r
- ReDirStatus_t* entry;\r
- ReByteBuffer line;\r
- while ((entry = m_traverser.nextFile(level, &filter)) != NULL) {\r
- if (entry->isDirectory())\r
- continue;\r
- // append the new relative path from source to target:\r
- target.setLength(ixTargetRelative);\r
- target.append(entry->m_path.str() + ixSourceRelative, -1);\r
- if (!exists(target))\r
- makeDirWithParents(target, ixTargetRelative, m_traverser);\r
- targetFile.set(target).append(entry->node(), -1);\r
- const char* targetRelativePath = targetFile.str() + ixTargetRelative\r
- + 1;\r
- bool targetExists = exists(targetFile);\r
- if (!targetExists && mustExist) {\r
- if (m_verboseLevel == V_CHATTER)\r
- fprintf(m_output, "-ignored: %s does not exist\n",\r
- targetRelativePath);\r
- continue;\r
- }\r
- if (targetExists) {\r
- if (addOnly) {\r
- if (m_verboseLevel >= V_CHATTER)\r
- fprintf(m_output, "~ignored: %s exists\n",\r
- targetRelativePath);\r
- continue;\r
- }\r
- if (ignoreDate && entry->fileSize() == m_statInfo.st_size) {\r
- if (m_verboseLevel >= V_CHATTER)\r
- fprintf(m_output, "_ignored: %s same size\n",\r
- targetRelativePath);\r
- continue;\r
- }\r
- // target younger than source?\r
- int diff = int(\r
- m_statInfo.st_mtime\r
- - entry->filetimeToTime(entry->modified()));\r
- if (!ignoreDate && diff <= maxFileTimeDiff) {\r
- if (m_verboseLevel >= V_CHATTER)\r
- fprintf(m_output, "=ignored: %s same time\n",\r
- targetRelativePath);\r
- continue;\r
- }\r
- }\r
- files++;\r
- sumSizes += entry->fileSize();\r
- if (m_verboseLevel >= V_NORMAL)\r
- fprintf(m_output, "%c%s%s\n", targetExists ? '!' : '+',\r
- targetRelativePath, dry ? " would be copied" : "");\r
- if (!dry)\r
- copyFile(entry, targetFile.str());\r
- }\r
- treeFiles += m_traverser.files();\r
- treeDirs += m_traverser.directories();\r
- treeSumSizes += m_traverser.sizes();\r
- }\r
- if (m_verboseLevel >= V_SUMMARY) {\r
- int duration = int(time(NULL) - m_start);\r
- fprintf(m_output,\r
- i18n(\r
- "=== copied: %02d:%02d sec %7d file(s) %12.6f MByte (%.3f MB/sec).\n"\r
- "=== tree: %5d dir(s) %7d file(s) %12.6f MByte\n"),\r
- duration / 60, duration % 60, files, sumSizes / 1E6,\r
- sumSizes / 1E6 / (duration == 0 ? 1 : duration), treeDirs,\r
- treeFiles, treeSumSizes / 1E6);\r
- }\r
-}\r
-\r
-/**\r
- * Constructor.\r
- *\r
- * @param logger logger for error handling\r
- */\r
-ReDirTCP::ReDirTCP(ReLogger* logger) :\r
- ReTool(s_tcpUsage, s_tcpExamples, 1, 0, 0, true, logger) {\r
- m_hasStandardArgs = false;\r
- m_programArgs.addString("size", i18n("size of the message to send/receive\n"\r
- "<string> is a number followed by an unit\n"\r
- "units: b(yte) k(Byte) K(iByte) m(Byte), M(iByte)\n"\r
- "Note: maximum: 16M-32=16777184\n"\r
- "examples: -b1m --buffer-size=512K"), 'b', "size", false, "64K");\r
- m_programArgs.addInt("port", i18n("port of the server/client"), 'p',\r
- "--port", 58111);\r
-}\r
-\r
-/**\r
- * Lists the metadata of the specified files.\r
- */\r
-void ReDirTCP::doIt() {\r
- int port = m_programArgs.getInt("port");\r
- ReByteBuffer buffer;\r
- int64_t bufferSize = checkSize(m_programArgs.getString("size", buffer));\r
- // the protocol does not allow more than 16 MiByte because of the flags:\r
- if (bufferSize > 16LL * 1024 * 1024 - 64LL)\r
- help(i18n("buffersize exceeds 16777184 = 16MiByte - 32: "),\r
- buffer.str());\r
- ReByteBuffer command = m_programArgs.arg(0);\r
- if (command.isPrefixOf("server", -1, true)) {\r
- ReTCPEchoServer server(port, m_logger);\r
- server.setLogSendReceive(false);\r
- server.listenForAll();\r
- } else if (command.isPrefixOf("client", -1, true)) {\r
- const char* ip = m_programArgs.arg(1);\r
- ReByteBuffer direction("download");\r
- int rounds = 10;\r
- int interval = 5;\r
- if (m_programArgs.argCount() > 2) {\r
- direction = m_programArgs.arg(2);\r
- if (!direction.isPrefixOf("download", -1, true)\r
- && !direction.isPrefixOf("upload", -1, true)\r
- && !direction.isPrefixOf("mixed", -1, true))\r
- help(\r
- "unknown direction: $1 (use 'download', 'upload' or 'mixed')",\r
- direction.str());\r
- }\r
- if (m_programArgs.argCount() > 3)\r
- rounds = atoi(m_programArgs.arg(3));\r
- if (m_programArgs.argCount() > 4)\r
- interval = atoi(m_programArgs.arg(4));\r
- if (tolower(direction.at(0)) == 'm')\r
- runMixedClient(ip, port, rounds, interval, (int) bufferSize);\r
- else\r
- runOneThreadClient(ip, port, rounds, interval, (int) bufferSize,\r
- tolower(direction.at(0)) == 'u');\r
- } else\r
- help("unknown subcommand: $1", command.str());\r
-}\r
-void ReDirTCP::runMixedClient(const char* ip, int port, int rounds,\r
- int interval, int bufferSize) {\r
- help("not implemented: mixed");\r
-}\r
-void ReDirTCP::runOneThreadClient(const char* ip, int port, int rounds,\r
- int interval, int bufferSize, bool upload) {\r
- ReTCPClient client(m_logger);\r
- if (client.connect(ip, port)) {\r
- time_t start = time(NULL);\r
- const char* command = upload ? "strlen" : "filldata";\r
- ReByteBuffer message;\r
- if (upload)\r
- message.appendChar('x', bufferSize);\r
- else\r
- message.appendInt(bufferSize);\r
- time_t lastPrint = start;\r
- int64_t size = 0;\r
- int duration = 0;\r
- ReByteBuffer answer, data;\r
- client.setLogSendReceive(false);\r
- int64_t sizeCurrent = 0;\r
- for (int ii = 0; ii < rounds; ii++) {\r
- client.send(command, message.str(), message.length());\r
- client.receive(answer, data);\r
- size += message.length() + data.length();\r
- sizeCurrent += message.length() + data.length();\r
- time_t now = time(NULL);\r
- if (now >= lastPrint + interval) {\r
- duration = int(now - start);\r
- int durationCurrent = int(now - lastPrint);\r
- printf("%2d: %9.3f MiByte %8.3f / %8.3f kiByte/sec %s\n", ii,\r
- size / 1024.0 / 1024,\r
- (double) sizeCurrent / durationCurrent / 1024,\r
- (double) size / duration / 1024,\r
- upload ? "up" : "down");\r
- lastPrint = now;\r
- sizeCurrent = 0;\r
- }\r
- }\r
- duration = int(time(NULL) - start);\r
- if (duration == 0)\r
- duration = 1;\r
- printf("%2d: %9.3f MiByte %8.3f kiByte %s/sec %s\n", rounds,\r
- size / 1024.0 / 1024, (double) size / duration / 1024,\r
- upload ? "up" : "down");\r
-\r
- }\r
-}\r
-/**\r
- * Constructor.\r
- *\r
- * @param logger logger for error handling\r
- */\r
-ReDirWhich::ReDirWhich(ReLogger* logger) :\r
- ReTool(s_batchUsage, s_batchExamples, 0, 0, 0, true, logger) {\r
-// no standard options:\r
- m_programArgs.addBool("all",\r
- i18n("all files will be found, not only the first"), 'a', "all", false);\r
- m_programArgs.addString("list",\r
- i18n("a path list (separator see option 'separator'"), 'l', "list",\r
- false,\r
- NULL);\r
- m_programArgs.addString("separator",\r
- i18n("separator between the path elements"), 's', "separator", false,\r
-#if defined __linux__\r
- ":"\r
-#elif defined __WIN32__\r
- ";"\r
-#endif\r
-) ;\r
- m_programArgs.addString("variable", i18n("variable with the path list"),\r
- 'v', "variable", false, "PATH");\r
- m_hasStandardArgs = false;\r
-}\r
-\r
-/**\r
- * Creates the batch file.\r
- */\r
-void ReDirWhich::doIt() {\r
- ReByteBuffer value, path;\r
- bool all = false;\r
- ReStringList items;\r
- char sep = 0;\r
- m_programArgs.getString("list", path);\r
- if (path.length() == 0) {\r
- m_programArgs.getString("variable", value);\r
- if (getenv(value.str()) == NULL)\r
- help("Umgebungsvariable nicht definiert: ", value.str());\r
- else\r
- path = getenv(value.str());\r
- m_programArgs.getString("separator", value);\r
- sep = value.at(0);\r
- }\r
- items.split(path.str(), sep);\r
- struct stat info;\r
- ReByteBuffer full;\r
- for (int ix = 0; ix < m_programArgs.argCount(); ix++) {\r
- bool found = false;\r
-\r
- ReByteBuffer arg(m_programArgs.arg(ix));\r
- for (size_t ixItem = 0; ixItem < items.count(); ixItem++) {\r
- full.set(items.strOf(ixItem));\r
- if (arg.indexOf('*') < 0) {\r
- full.ensureLastChar(OS_SEPARATOR_CHAR);\r
- full.append(arg);\r
- found = stat(full.str(), &info) == 0;\r
- if (found)\r
- printf("%s\n", full.str());\r
- } else {\r
- ReDirectory dir(full.str());\r
- if (dir.findFirst(arg.str(), false)) {\r
- do {\r
- printf("%s%c%s\n", full.str(), OS_SEPARATOR_CHAR,\r
- dir.currentFile());\r
- } while (dir.findNext());\r
- }\r
- }\r
- if (found && !all)\r
- break;\r
- }\r
- }\r
-}\r
-\r
-/**\r
- * Tests whether a abrevation of an argument is given.\r
- * @param full the full name\r
- * @param part the part to test\r
- * @return <code>true</code>: part is a prefix of full\r
- */\r
-static bool isArg(const char* full, const char* part) {\r
- ReByteBuffer fullArg(full);\r
- bool rc = fullArg.startsWith(part, -1);\r
- return rc;\r
-}\r
-\r
-/**\r
- * Gets the arguments for the "help" command and execute this.\r
- *\r
- * @param argc the number of arguments\r
- * @param argav the argument vector\r
- */\r
-void ReDirTools::help(int argc, const char* argv[]) {\r
- if (argc <= 1)\r
- printField(s_helpSummary);\r
- else {\r
- argc--;\r
- argv++;\r
- const char* arg0 = argv[0];\r
- if (isArg("batch", arg0)) {\r
- ReDirBatch(m_logger).help(NULL);\r
- } else if (isArg("checksum", arg0)) {\r
- ReDirChecksum(m_logger).help(NULL);\r
- } else if (_stricmp("delete", arg0) == 0) {\r
- ReDirDelete(m_logger).help(NULL);\r
- } else if (isArg("list", arg0)) {\r
- ReDirList(m_logger).help(NULL);\r
- } else if (isArg("help", arg0)) {\r
- printField(s_helpSummary);\r
- } else if (isArg("random", arg0)) {\r
- ReDirRandom(m_logger).help(NULL);\r
- } else if (isArg("statistic", arg0)) {\r
- ReDirStatistic(m_logger).help(NULL);\r
- } else if (isArg("test", arg0)) {\r
- void testAll();\r
- testAll();\r
- } else if (isArg("tcp", arg0)) {\r
- ReDirTCP(m_logger).help(NULL);\r
- } else if (isArg("touch", arg0)) {\r
- ReDirTouch(m_logger).help(NULL);\r
- } else if (isArg("which", arg0)) {\r
- ReDirWhich(m_logger).help(NULL);\r
- } else\r
- printf("+++ unknown sub command: %s\n", arg0);\r
- }\r
-}\r
-\r
-/**\r
- * Executes a command.\r
- *\r
- * @param argc the number of arguments\r
- * @param argv the argument vector\r
- * @param tool the tool which realizes the command\r
- */\r
-void ReDirTools::run(int argc, const char* argv[], ReTool& tool) {\r
- tool.run(argc, argv);\r
-}\r
-/**\r
- * Gets the arguments for any command and execute this.\r
- *\r
- * @param argc the number of arguments\r
- * @param orgArgv the argument vector\r
- */\r
-int ReDirTools::main(int argc, char* orgArgv[]) {\r
- ReDirTools tools;\r
- if (argc < 0) {\r
- argc = 0;\r
- for (int ix = 0; orgArgv[ix] != NULL; ix++)\r
- argc++;\r
- }\r
- const char** argv = (const char**) orgArgv;\r
- if (argc < 2) {\r
- tools.help(0, argv);\r
- exit(1);\r
- }\r
- argc--;\r
- argv++;\r
- m_logger = ReLogger::globalLogger();\r
- const char* arg0 = argv[0];\r
- if (isArg("batch", arg0))\r
- ReDirBatch(m_logger).run(argc, argv);\r
- else if (isArg("checksum", arg0))\r
- ReDirChecksum(m_logger).run(argc, argv);\r
- else if (_stricmp("delete", arg0) == 0)\r
- ReDirDelete(m_logger).run(argc, argv);\r
- else if (isArg("help", arg0))\r
- tools.help(argc, argv);\r
- else if (isArg("list", arg0))\r
- ReDirList(m_logger).run(argc, argv);\r
- else if (isArg("random", arg0))\r
- ReDirRandom(m_logger).run(argc, argv);\r
- else if (isArg("statistic", arg0))\r
- ReDirStatistic(m_logger).run(argc, argv);\r
- else if (isArg("synchronize", arg0))\r
- ReDirSync(m_logger).run(argc, argv);\r
- else if (isArg("tcp", arg0))\r
- ReDirTCP(m_logger).run(argc, argv);\r
- else if (isArg("touch", arg0))\r
- ReDirTouch(m_logger).run(argc, argv);\r
- else if (isArg("which", arg0))\r
- ReDirWhich(m_logger).run(argc, argv);\r
- else if (isArg("test", arg0)) {\r
- void testAll();\r
- testAll();\r
- } else\r
- tools.usage("unknown command: ", argv[1]);\r
- ReTCPServer::globalClose();\r
- return 0;\r
-}\r
-\r
+/*
+ * ReDirTools.cpp
+ *
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
+ * Do what you want.
+ * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
+ * The latest sources: https://github.com/republib
+ */
+
+#include "base/rebase.hpp"
+#include "math/remath.hpp"
+#include "os/reos.hpp"
+#include "net/renet.hpp"
+
+enum LOCATION_DIRTOOL {
+ LC_COPY_FILE_1 = LOC_FIRST_OF(LOC_DIRTOOLS), // 50101
+ LC_COPY_FILE_2, // 50102
+ LC_COPY_FILE_3, // 50103
+ LC_COPY_FILE_4, // 50104
+ LC_COPY_FILE_5, // 50105
+ LC_COPY_FILE_6, // 50106
+ LC_MAKE_DIR_1, // 50107
+ LC_MAKE_DIR_2, // 50108
+ LC_SET_PROPERTIES_1, // 50109
+ LC_SET_PROPERTIES_2, // 50110
+ LC_SET_PROPERTIES_3, // 50111
+ LC_TOUCH_1, // 50112
+ LC_CALCULATE_CHECKSUM_1, // 50113
+ LC_BUILD_DIRECTORY_1, // 50114
+ LC_COMPARE_STORAGE_1, // 50115
+ LC_COMPARE_STORAGE_2, // 50116
+ LC_COMPARE_STORAGE_3, // 50117
+ LC_COMPARE_STORAGE_4, // 50118
+ LC_COMPARE_STORAGE_5, // 50119
+ LC_COMPARE_STORAGE_6, // 50120
+ LC_COMPARE_STORAGE_7, // 50121
+ LC_COMPARE_DIR_1, // 50122
+ LC_DELETE_1, // 50123
+};
+const char* ReDirTools::m_version = "2015.03.22";
+ReLogger* ReDirTools::m_logger = NULL;
+
+static const char* s_helpSummary[] = { "dirtool or dt <command> <opts>",
+ " Useful commands around directory trees.",
+ " Type 'dirtool help <command>' for more help.", "<command>:",
+ "batch produce output to handle the found files with a script",
+ "checksum shows the checksum (MD5 or other) of the selected files",
+ "delete delete the selected files",
+ "help shows info about the arguments/options",
+ "list shows the meta data of the selected files",
+ "random displays random numbers",
+ "statistic shows statistics about a direcctory tree",
+ "synchronize copies only modified or new files from",
+ " from a source directory tre to a target",
+ "touch sets the filetimes",
+ "which finds a file in a path list (like PATH)",
+ NULL };
+
+const char* s_batchUsage[] = {
+ "<command>: batch [<opts>] <dir_or_file1> [<dir_or_file2> ...]",
+ " produces output usable for a batch file (script)",
+ " all found files can be processed with a given script",
+ " each line starts (usually) with a script name (see -c)",
+ " then it follows the full filename of the found file",
+ " use --arguments or --script to configure the output line",
+ NULL };
+const char* s_deleteExamples[] = {
+ "dirtool delete --basename-pattern=;*~;*.bak e:\\data",
+ "dirtool delete /tmp/log",
+ NULL };
+
+const char* s_deleteUsage[] = {
+ "<command>: delete [<opts>] <dir_or_file1> [<dir_or_file2> ...]",
+ " delete the specified files",
+ NULL };
+const char* s_batchExamples[] = {
+ "dirtool batch -cbackup.bat --basename-pattern=;*.txt;*.doc e:\\data",
+ "dirtool batch --type=r '--arguments=backup.sh !basename! !path!' usr etc",
+ NULL };
+
+const char* s_listUsage[] = {
+ "<command>: l(ist) [<opts>] <dir_or_file1> [<dir_or_file2> ...]",
+ " lists the metadata (size, modification date ...) of the selected files",
+ NULL };
+const char* s_listExamples[] =
+ { "dirtool list --min-size=10M e:\\data",
+ "dirtool li --type=f -y7d --size=10M -p;*.cpp;*.hpp;Makefile*;-*~ /home/data",
+ NULL };
+
+const char* s_checksumUsage[] =
+ { "<command>: c(hecksum) [<opts>] <dir_or_file1> [<dir_or_file2> ...]",
+ " shows a check sum of the given files or manages a checksum storage file in each directory for observing",
+ NULL };
+const char* s_checksumExamples[] = { "dirtool ch --buffer-size=512 e:\\data",
+ "dirtool check -cList -p;*.iso /home/iso /down/debian.iso",
+ "dirtool checksum -cBuild -aMD5 /home",
+ "dirtool checksum --command=Compare /home",
+ "dirtool checksum -cUpdate -O/var/log/checksum.log /home",
+ NULL };
+const char* s_randomUsage[] = { "<command>: r(andom) <opts> count until [from]",
+ " displays random numbers.", " <count> count of generated numbers",
+ " <until> the maximum (including) value of the numbers",
+ " <from> the minimum (including) value of the numbers. Default: 1",
+ NULL };
+const char* s_randomExamples[] = { "dirtool ra --multiple -w3 10 33",
+ "dirtool rand -s 6 49", "dirtool rand --sort --width=1 5 50",
+ "dirtool rand --sort --width=1 2 10",
+ NULL };
+
+static const char* s_statisticUsage[] =
+ { "<command>: st(atistic) [<opts>] <path1> [<path2> ...] [<depth>]",
+ " shows a statistic about a directory tree",
+ "<path> a directory path: relative or absolute",
+ "<depth> 0: only the summary of <path> will be shown",
+ " 1: shows the summery of each subdir of <path> and the total",
+ " n: shows the summery of each subdir until level <n> and the total",
+ " default: 1", "<opts_stat>:",
+ NULL };
+const char* s_statisticExamples[] =
+ { "dirtool st -q -t0 e:\\windows",
+ "dirtool statistic --quiet --kbyte --trace-interval=60 ../mail ../remember 2",
+ "dirtool stat -q --kbyte d:data 2",
+ NULL };
+
+const char* s_syncUsage[] =
+ { "<command>: sy(nchronize) <opts> <source1> [<source2> ...] <target>",
+ " Synchronizes the content of a directory tree with another.",
+ " Newer or missing files will be copied",
+ " If a source name ends with the separator (/ in linux, \\ in win) the target",
+ " will be completed with the basename of the source.", " Example:",
+ " 'sync home backup' copies the file home/x.txt to backup/home/x.txt",
+ " 'sync data/ backup/data2' copies the file home/x.txt to backup/data2/x.txt",
+ NULL };
+const char* s_syncExamples[] = {
+ "dirtool sync --basename-pattern=;*.txt;*.doc e:\\data\\ d:\\backup\\data2",
+ "dirtool sync --type=r --max-size=1G usr etc /media/backup",
+ NULL };
+
+const char* s_tcpUsage[] = {
+ "<command>: tcp [<opts>] <subcommand> [<param> ...]",
+ " test tool for network test", "<subcommand>:", " server",
+ " client <ip> [<direction> [<rounds> [<print_interval>]]]",
+ " <ip>: URL of the server", " <rounds>: number of messages to send",
+ " <direction>: 'upload', 'download' or 'mixed'",
+ NULL };
+const char* s_tcpExamples[] = { "dirtool tcp -p 5555 server",
+ "dirtool tcp -p 5555 client localhost download 10000 10",
+ "dirtool tcp -p 5555 --buffer-size=1024 client 192.168.7.3 upload 10 25",
+ NULL };
+
+const char* s_touchUsage[] =
+ { "<command>: touch [<opts>] <dir_or_file1> [<dir_or_file2> ...]",
+ " sets the filetimes (modification and/or access time) of the selected files",
+ NULL };
+const char* s_touchExamples[] = {
+ "dirtool touch -p;*.csv -m2014.12.1/8:12 e:\\data e:\\history\\readme.txt",
+ "dirtool touch -p;*.cpp --modified=now-4d12H /home/data",
+ "dirtool touch -p;*.csv -m+1m . e:\\data e:\\history\\readme.txt",
+ NULL };
+
+const char* s_whichUsage[] = {
+ "<command>: w(hicht) [<opts>] <file_or_pattern1> [<file_or_pattern2> ...]",
+ " finds a file in a path list (like PATH)",
+ NULL };
+const char* s_whichExamples[] = {
+ "dirtool which --list=JAVA_CLASSPATH org.jdom.jar *.jdom*.jar",
+ "dirtool which find.exe",
+ NULL };
+/**
+ * Constructor.
+ *
+ * @param usage a string vector with a message how to use the command
+ * @param example a string vector with some examples how to use the command
+ */
+ReDirOptions::ReDirOptions(const char* usage[], const char* examples[]) :
+ ReTraceUnit(INT_MAX, INT_MAX),
+ m_programArgs(usage, examples),
+ m_nodePatterns(),
+ m_pathPatterns(),
+ m_compoundUsage(NULL),
+ m_countCompoundUsage(0),
+ m_output(stdout),
+ m_verboseLevel(V_NORMAL) {
+ m_nodePatterns.setIgnoreCase(true);
+ m_pathPatterns.setIgnoreCase(true);
+}
+/**
+ * Destructor.
+ */
+ReDirOptions::~ReDirOptions() {
+ close();
+ delete[] m_compoundUsage;
+}
+/**
+ * Adds a usage component to the compound usage message list.
+ * @param usage a string vector containing a part of the usage message
+ */
+void ReDirOptions::addCompoundUsage(const char** usage) {
+ int start = 0;
+ while (m_compoundUsage[start] != NULL) {
+ if (++start >= m_countCompoundUsage)
+ assert(false);
+ }
+ for (int ix = 0; usage[ix] != NULL; ix++) {
+ if (start + ix > m_countCompoundUsage) {
+ assert(false);
+ break;
+ }
+ m_compoundUsage[start + ix] = usage[ix];
+ }
+}
+
+/**
+ * Adds the standard filter options.
+ */
+void ReDirOptions::addStandardFilterOptions() {
+ // standard short options: D d O o P p T t v y Z z
+ m_programArgs.addInt("maxdepth",
+ i18n("the depth of the subdirectory is lower or equal <number>\n"
+ "0: search is done only in the base directory"), 'D', "max-depth",
+ 512);
+ m_programArgs.addInt("mindepth",
+ i18n("the depth of the subdirectory is greater or equal <number>\n"
+ "0: search is done in all subdirectories"), 'd', "min-depth", 0);
+ m_programArgs.addString("output", i18n("the name of the output file.\n"
+ "The output will written to this file instead of stdout"), 'O',
+ "output-file", false, NULL);
+ m_programArgs.addString("older",
+ i18n(
+ "the modification date is older than <string>\n"
+ "<string> is a date (e.g. 2015.02.17) or number followed by an unit\n"
+ "units: m(inutes) h(hours), d(days). Default: m(inutes)\n"
+ "examples: -o25 --older-than=30d -o24h -o2009.3.2/12:00 -o1999.01.01"),
+ 'o', "older-than", false, NULL);
+ m_programArgs.addString("pathpattern",
+ i18n(
+ "a list of patterns for the path (without basename)\n"
+ "the separator is the first character of the list\n"
+ "Each pattern can contain '*' as wildcard\n"
+ "If the first character is '-' the pattern is a 'not pattern':\n"
+ "A directory will be entered if at least one of the positive patterns\n"
+ "and none of the 'not patterns' matches\n"
+ "examples:\n"
+ "';music;pic*' enters music and xy/Music and PIC and pictures but not xy/pic and img\n"
+ "';*;-.git;.hg' ignores .git and xy/z/.git and .ht"), 'P',
+ "path-pattern", false, NULL);
+ m_programArgs.addString("nodepattern",
+ i18n(
+ "a list of patterns for the basename (name without path) separated by ';'\n"
+ "Each pattern can contain '*' as wildcard\n"
+ "If the first character is '-' the pattern is a 'not pattern':\n"
+ "A file will be found if at least one of the positive patterns and none\n"
+ "of the 'not patterns' matches\n"
+ "examples: '*.cpp;*.hpp;Make*' '*;-*.bak;-*~"), 'p',
+ "basename-pattern", false, NULL);
+ m_programArgs.addString("verbose",
+ i18n(
+ "verbose level: 0: no info, 1: summary only, 2: normal, 3: chatter mode, 4: debug"),
+ 'v', "verbose", false, "1");
+ m_programArgs.addInt("trace",
+ i18n("all <number> seconds the current path will be traced\n"
+ "0: no trace"), 'T', "trace-interval", 0);
+ m_programArgs.addString("type", i18n("the file type\n"
+ "<string> is a list of <v> values:\n"
+ "<v>: b(lock) c(har) d(irectory) (l)i(nkdir) l(ink) o(ther)\n"
+ " p(ipe) s(ocket) r(egular)\n"
+ "<v>-sets: S(pecial)=bcspo N(ondir)=Slr\n"
+ "examples: -td --type=dr -tNi"), 't', "type", false, NULL);
+ m_programArgs.addString("younger",
+ i18n(
+ "the modification date is younger than <string>\n"
+ "<string> is a date (e.g. 2015.02.17) or number followed by an unit\n"
+ "units: m(inutes) h(hours), d(days). Default: m(inutes)"), 'y',
+ "younger-than", false, NULL);
+ m_programArgs.addString("maxsize",
+ i18n(
+ "the filesize is greater or equal <string>\n"
+ "<string> is a number followed by an unit\n"
+ "units: b(yte) k(Byte) K(iByte) m(Byte), M(iByte), g(Byte) G(iByte)\n"
+ "examples: -Z50m --max-size=1G"), 'Z', "max-size", false, NULL);
+ m_programArgs.addString("minsize",
+ i18n(
+ "the filesize is greater or equal <string>\n"
+ "<string> is a number followed by an unit\n"
+ "units: b(yte) k(Byte) K(iByte) m(Byte), M(iByte), g(Byte) G(iByte)\n"
+ "examples: -z50m --min-size=1G"), 'z', "min-size", false, NULL);
+}
+
+/**
+ * Checks whether the given value is a time expression.
+ *
+ * Possible: <date> <date_time> <n><unit>
+ * Units: m(inutes) h(our) d(ays) s(econds)
+ *
+ * @param value value to check
+ * @return the value converted into the absolute (or relative) time
+ * @throws ReOptionExecption
+ */
+ReFileTime_t ReDirOptions::checkDate(const char* value) {
+ ReByteBuffer theValue(value, -1);
+ time_t rcTime;
+ if (theValue.count(".") == 2) {
+ // a date:
+ int year, month, day;
+ int hour = 0;
+ int minute = 0;
+ int sec = 0;
+ switch (sscanf(value, "%d.%d.%d/%d:%d:%d", &year, &month, &day, &hour,
+ &minute, &sec)) {
+ case 3:
+ case 4:
+ case 5:
+ case 6: {
+ if (year < 1980)
+ throw ReOptionException(&m_programArgs,
+ i18n("date < 1980.01.01: $1"), value);
+ struct tm time;
+ memset(&time, 0, sizeof time);
+ time.tm_year = year - 1900;
+ time.tm_mon = month - 1;
+ time.tm_mday = day;
+ time.tm_hour = hour;
+ time.tm_min = minute;
+ time.tm_sec = sec;
+ rcTime = mktime(&time);
+ break;
+ }
+ default:
+ throw ReOptionException(&m_programArgs,
+ i18n(
+ "invalid date/date-time value: $1 yyyy.mm.dd/hh:MM expected"),
+ value);
+ }
+ } else if (theValue.count(":") >= 1) {
+ // a time:
+ int hour = 0;
+ int minute = 0;
+ int sec = 0;
+ switch (sscanf(value, "%d:%d:%d", &hour, &minute, &sec)) {
+ case 2:
+ case 3: {
+ // the time (today)
+ rcTime = time(NULL) / 86400 * 86400 + hour * 3600 + minute * 60
+ + sec;
+ break;
+ }
+ default:
+ throw ReOptionException(&m_programArgs,
+ i18n("invalid time value: $1. HH:MM:SS expected"), value);
+ }
+ } else {
+ // a time distance value:
+ char unit = 'm';
+ int count = 0;
+ int negativeFactor = 1;
+ bool fromNow = theValue.startsWith("now");
+ if (fromNow) {
+ theValue.remove(0, 3);
+ }
+ char cc = theValue.at(0);
+ if (cc == '-' || cc == '+') {
+ if (cc == '-')
+ negativeFactor = -1;
+ theValue.remove(0, 1);
+ }
+ switch (sscanf(theValue.str(), "%d%c", &count, &unit)) {
+ case 1:
+ case 2:
+ switch (unit) {
+ case 's':
+ break;
+ case 'm':
+ count *= 60;
+ break;
+ case 'h':
+ count *= 60 * 60;
+ break;
+ case 'd':
+ count *= 24 * 60 * 60;
+ break;
+ default:
+ throw ReOptionException(&m_programArgs,
+ i18n(
+ "invalid unit $1. expected: s(econds) m(inutes) h(ours) d(ays)"),
+ value);
+ }
+ rcTime = count * negativeFactor;
+ break;
+ default:
+ throw ReOptionException(&m_programArgs,
+ i18n(
+ "invalid relative time value $1 (<number><unit> expected). <unit>: s m h d"),
+ value);
+ }
+ if (fromNow)
+ rcTime += time(NULL);
+ }
+ ReFileTime_t rc;
+ ReDirStatus_t::timeToFiletime(rcTime, rc);
+ return rc;
+}
+/**
+ * Checks whether the given value is a time expression.
+ *
+ * Possible: <date> <date_time> <n><unit>
+ * Units: m(inutes) h(our) d(ays)
+ *
+ * @param value value to check
+ * @return the value (multiplied with the unit factor)
+ * @throws ReOptionExecption
+ */
+int64_t ReDirOptions::checkSize(const char* value) {
+ int64_t rc = 0;
+ char unit = 'b';
+ switch (sscanf(value, "%lld%c", (long long int*) &rc, &unit)) {
+ case 1:
+ case 2:
+ switch (unit) {
+ case 'b':
+ break;
+ case 'k':
+ rc *= 1000;
+ break;
+ case 'K':
+ rc *= 1024;
+ break;
+ case 'm':
+ rc *= 1000 * 1000;
+ break;
+ case 'M':
+ rc *= 1024 * 1024;
+ break;
+ case 'g':
+ rc *= 1000LL * 1000 * 1000;
+ break;
+ case 'G':
+ rc *= 1024LL * 1024 * 1024;
+ break;
+ default:
+ throw ReOptionException(&m_programArgs,
+ i18n("invalid <unit>: $1. Expected: b k K m M g G"), value);
+ }
+ break;
+ default:
+ throw ReOptionException(&m_programArgs,
+ i18n(
+ "invalid size value: $1 expected: <number><unit> <unit>: b k K m M g G"),
+ value);
+ }
+ return rc;
+}
+/**
+ * Checks whether the given value is a valid pattern list.
+ *
+ * The first character must be a separator (not '*', '.' and not a letter)
+ *
+ * @param value value to check
+ * @return the <cpde>value</code> (for chaining)
+ * @throws ReOptionExecption
+ */
+const char* ReDirOptions::checkPatternList(const char* value) {
+ if (isalnum(*value) || *value == '_' || *value == '*' || *value == '.'
+ || *value == '-')
+ throw ReOptionException(&m_programArgs,
+ i18n("invalid separator (first character): $1 use ';' instead"),
+ value);
+ if (strchr(value, OS_SEPARATOR_CHAR) != NULL)
+ throw ReOptionException(&m_programArgs,
+ i18n("slash not allowed in pattern list: $1"), value);
+ return value;
+}
+/**
+ * Checks whether the given value is a valid filetype list.
+ *
+ * @param value value to check
+ * @return the bitmask
+ * @throws ReOptionExecption
+ */
+ReDirStatus_t::Type_t ReDirOptions::checkType(const char* value) {
+ int rc = ReDirStatus_t::TF_UNDEF;
+ while (*value != '\0') {
+ switch (*value) {
+ case 'b':
+ rc |= ReDirStatus_t::TF_BLOCK;
+ break;
+ case 'c':
+ rc |= ReDirStatus_t::TF_CHAR;
+ break;
+ case 'd':
+ rc |= ReDirStatus_t::TF_SUBDIR;
+ break;
+ case 'i':
+ rc |= ReDirStatus_t::TF_LINK_DIR;
+ break;
+ case 'l':
+ rc |= ReDirStatus_t::TF_LINK;
+ break;
+ case 'o':
+ rc |= ReDirStatus_t::TF_OTHER;
+ break;
+ case 'p':
+ rc |= ReDirStatus_t::TF_PIPE;
+ break;
+ case 's':
+ rc |= ReDirStatus_t::TF_SOCKET;
+ break;
+ case 'r':
+ rc |= ReDirStatus_t::TF_REGULAR;
+ break;
+ case 'S':
+ rc |= ReDirStatus_t::TC_SPECIAL;
+ break;
+ case 'N':
+ rc |= ReDirStatus_t::TC_NON_DIR;
+ break;
+ case ' ':
+ case ',':
+ break;
+ default:
+ throw ReOptionException(&m_programArgs,
+ i18n("invalid type: $1 Expected: b(lock) c(har) d(irectory)"
+ " (l)i(nkdir) l(ink) o(ther) p(ipe) s(ocket) r(egular)"
+ " S(pecial=bcspo) N(ondir=Slr)"), value);
+ }
+ value++;
+ }
+ return (ReDirStatus_t::Type_t) rc;
+}
+
+/**
+ * Prints a help message, the error message and exits.
+ *
+ * @param errorMessage the error message.
+ * @param message2 an additional message
+ */
+
+void ReDirOptions::help(const char* errorMessage, const char* message2) const {
+ ReByteBuffer msg;
+ if (errorMessage != 0)
+ msg.append(errorMessage, -1);
+ if (message2 != NULL)
+ msg.append(message2, -1);
+ m_programArgs.help(msg.str(), false, stdout);
+ exit(1);
+}
+
+/**
+ * Checks the correctness of the standard filter options.
+ *
+ * @throws
+ */
+void ReDirOptions::checkStandardFilterOptions() {
+ ReByteBuffer buffer;
+ if (m_programArgs.getString("older", buffer)[0] != '\0')
+ checkDate(buffer.str());
+ if (m_programArgs.getString("younger", buffer)[0] != '\0')
+ checkDate(buffer.str());
+ if (m_programArgs.getString("type", buffer)[0] != '\0')
+ checkType(buffer.str());
+ if (m_programArgs.getString("maxsize", buffer)[0] != '\0')
+ checkSize(buffer.str());
+ if (m_programArgs.getString("minsize", buffer)[0] != '\0')
+ checkSize(buffer.str());
+ if (m_programArgs.getString("nodepattern", buffer)[0] != '\0')
+ checkPatternList(buffer.str());
+ if (m_programArgs.getString("pathpattern", buffer)[0] != '\0')
+ checkPatternList(buffer.str());
+ if (m_programArgs.getString("verbose", buffer)[0] != '\0') {
+ unsigned level = V_NORMAL;
+ if (ReStringUtils::lengthOfUnsigned(buffer.str(), -1, &level)
+ != (int) buffer.length())
+ help(i18n("verbose level is not a number (or '')"), buffer.str());
+ else
+ m_verboseLevel = VerboseLevel(level);
+ }
+}
+
+/**
+ * Frees the resources.
+ */
+void ReDirOptions::close() {
+ if (m_output != stdout && m_output != stderr) {
+ fclose(m_output);
+ }
+ m_output = stdout;
+}
+
+/**
+ * Initializes the compound usage message array.
+ *
+ * @param size the size of the array: size = (field1 + field2 + ...) * sizeof(const char*)
+ */
+void ReDirOptions::initCompoundUsage(size_t size) {
+ delete[] m_compoundUsage;
+ int count = size / sizeof m_compoundUsage[0];
+ m_compoundUsage = new const char*[count];
+ memset(m_compoundUsage, 0, size);
+ m_countCompoundUsage = count;
+}
+
+/**
+ * Optimizes the path patterns.
+ *
+ * For all patterns of the list:
+ * <ul><li>remove a trailing "\" and "\*"</li>
+ * <li>change "*\xy" to "*\xy" and "xy" (finds xy in the root directory)</li>
+ * <li>replaces "/" with the os specific path separator</li>
+ * </ul>
+ *
+ * @param buffer the pattern list as string, e.g. ";*;-cache"
+ */
+void ReDirOptions::optimizePathPattern(ReByteBuffer& buffer) {
+ ReStringList list;
+ ReStringList rootList;
+ list.split(buffer.str() + 1, buffer.str()[0]);
+ buffer.replaceAll(OS_SEPARATOR, 1, "/", 1);
+ ReByteBuffer item;
+ for (int ix = 0; ix < (int) list.count(); ix++) {
+ item.set(list.strOf(ix), -1);
+ if (item.endsWith("/*"))
+ item.setLength(item.length() - 2);
+ item.removeLastChar('/');
+ bool notAnchored = item.startsWith("*/") || item.startsWith("-*/");
+ item.replaceAll("/", 1, OS_SEPARATOR, 1);
+ list.replace(ix, item.str());
+ if (notAnchored) {
+ item.remove(item.str()[0] == '-' ? 1 : 0, 2);
+ rootList.append(item.str(), 0);
+ }
+ }
+ if (rootList.count() > 0) {
+ list.append(rootList);
+ }
+ item.set(buffer.str(), 1);
+ buffer.set(item);
+ list.join(item.str(), buffer, true);
+}
+/**
+ * Sets the standard filter options given by the program arguments.
+ *
+ * @param filter OUT: the filter to set
+ */
+void ReDirOptions::setFilterFromProgramArgs(ReDirEntryFilter& filter) {
+ ReByteBuffer buffer;
+ if (m_programArgs.getString("younger", buffer)[0] != '\0')
+ filter.m_maxAge = checkDate(buffer.str());
+ if (m_programArgs.getString("older", buffer)[0] != '\0')
+ filter.m_minAge = checkDate(buffer.str());
+ if (m_programArgs.getString("maxsize", buffer)[0] != '\0')
+ filter.m_maxSize = checkSize(buffer.str());
+ if (m_programArgs.getString("minsize", buffer)[0] != '\0')
+ filter.m_minSize = checkSize(buffer.str());
+ if (m_programArgs.getString("type", buffer)[0] != '\0')
+ filter.m_types = checkType(buffer.str());
+ filter.m_minDepth = m_programArgs.getInt("mindepth");
+ filter.m_maxDepth = m_programArgs.getInt("maxdepth");
+ if (m_programArgs.getString("nodepattern", buffer)[0] != '\0') {
+ checkPatternList(buffer.str());
+ m_nodePatterns.set(buffer.str());
+ filter.m_nodePatterns = &m_nodePatterns;
+ }
+ if (m_programArgs.getString("pathpattern", buffer)[0] != '\0') {
+ checkPatternList(buffer.str());
+ optimizePathPattern(buffer);
+ m_pathPatterns.set(buffer.str());
+ filter.m_pathPatterns = &m_pathPatterns;
+ }
+ if ((m_interval = m_programArgs.getInt("trace")) != 0)
+ m_triggerCount = 10;
+ if (m_programArgs.getString("output", buffer)[0] != '\0') {
+ if ((m_output = fopen(buffer.str(), "w")) == NULL) {
+ help("cannot open output file", buffer.str());
+ m_output = stdout;
+ }
+ }
+}
+
+/**
+ * Constructor.
+ *
+ * @param usage a string vector with a message how to use the command
+ * @param example a string vector with some examples how to use the command
+ * @param minArguments minimal count of non option arguments
+ * @param reservedFirst count of arguments at the start which are not files
+ * @param reservedLast count of arguments at the end which are not files
+ * @param addCurrentDirIfNoArguments
+ * <code>true</code>: if no arguments are given the current
+ * directory will be added as argument
+ * @param logger logger for error messages
+ */
+#pragma warning( push )
+#pragma warning( disable : 4355 )
+ReTool::ReTool(const char* usage[], const char* example[], int minArguments,
+ int reservedFirst, int reservedLast, bool addCurrentDirIfNoArguments,
+ ReLogger* logger) :
+ ReDirOptions(usage, example),
+ ReDirTreeStatistic(),
+ m_minArguments(minArguments),
+ m_reservedFirst(reservedFirst),
+ m_reservedLast(reservedLast),
+ m_addCurrentDirIfNoArguments(addCurrentDirIfNoArguments),
+ m_hasStandardArgs(true),
+ m_traverser(NULL, this, logger),
+ m_filter(),
+ m_start(time(NULL)),
+ m_logger(logger) {
+#pragma warning( pop )
+}
+
+/**
+ * Destructor.
+ */
+ReTool::~ReTool() {
+}
+
+/**
+ * Prints a message.
+ *
+ * Often overwritten by a subclass.
+ *
+ * @param currentFile message for the trace
+ * @return <code>true</code> (for chaining)
+ */
+bool ReTool::trace(const char* currentFile) {
+ ReByteBuffer buffer(" ");
+ int duration = int(time(NULL) - m_startTime);
+ buffer.appendInt(duration / 60).appendInt(duration % 60, ":%02d: ");
+ buffer.appendInt(m_files).appendChar('/').appendInt(
+ m_traverser.directories()).append(" dir(s)");
+ buffer.appendInt(m_files).appendChar('/').appendInt(m_traverser.files())
+ .append(" file(s)");
+ buffer.append(currentFile);
+ fputs(buffer.str(), stdout);
+ return true;
+}
+
+/**
+ * Evaluates the arguments and calls the main function.
+ *
+ * @param argc number of arguments. If -1: <code>argv</code> ends with <code>NULL</code>
+ * @param argv program arguments
+ * @param minArguments the command needs at least so many non option arguments
+ */
+void ReTool::run(int argc, const char** argv) {
+ try {
+ if (argc < 0) {
+ for (argc = 0; argv[argc] != NULL; argc++) {
+ // nothing to do
+ }
+ }
+ m_programArgs.init(argc, argv);
+ if (m_programArgs.argCount() < m_minArguments)
+ m_programArgs.help(i18n("too few arguments"), false, stdout);
+ if (m_hasStandardArgs)
+ setFilterFromProgramArgs(m_filter);
+ doIt();
+ if (m_output != stdout) {
+ fclose(m_output);
+ m_output = stdout;
+ }
+
+ } catch (ReOptionException& exc) {
+ m_programArgs.help(exc.getMessage(), false, stdout);
+ }
+}
+/**
+ * Evaluates the non option arguments which are names of files or directories.
+ *
+ * For each non reserved argument it will be called either <code>processSingleFile()</code>
+ * or <code>processTree()</code>.
+ *
+ */
+void ReTool::processFileArguments() {
+ int max = m_programArgs.argCount() - m_reservedLast;
+ // Test whether the arguments are files or directories:
+ ReByteBuffer arg;
+ for (int ii = m_reservedFirst; ii < max; ii++) {
+ arg = m_programArgs.arg(ii);
+ if (!exists(arg) != 0)
+ m_programArgs.help(
+ ReByteBuffer(i18n("not a file or a directory: ")).append(arg)
+ .str(), false, stderr);
+ }
+ // process the files:
+ for (int ii = m_reservedFirst; ii < max; ii++) {
+ const char* arg = m_programArgs.arg(ii);
+ if (S_ISDIR(m_statInfo.st_mode))
+ processTree(arg);
+ else
+ processSingleFile(arg);
+ }
+}
+/**
+ * Processes a single file.
+ *
+ * Gets the file info and calls <code>processFile()</code>.
+ *
+ * @param filename the name of the file
+ */
+void ReTool::processSingleFile(const char* filename) {
+ ReByteBuffer protocol;
+ ReByteBuffer path;
+ ReByteBuffer name;
+ ReByteBuffer ext;
+ ReStringUtils::splitPath(filename, &protocol, &path, &name, &ext);
+ protocol.append(path);
+ if (protocol.length() == 0)
+ protocol.append(".");
+ else
+ protocol.reduceLength();
+ m_traverser.changeBase(protocol.str());
+
+ name.append(ext);
+ setFilterFromProgramArgs(m_filter);
+ m_traverser.setPropertiesFromFilter(&m_filter);
+ ReDirStatus_t entry(m_logger);
+ entry.m_path = protocol;
+ if (entry.findFirst()) {
+ do {
+#if defined __linux__
+ bool found = strcmp(entry.node(), name.str()) == 0;
+#elif defined __WIN32__
+ bool found = _stricmp(entry.node(), name.str()) == 0;
+#endif
+ if (found && m_filter.match(entry)) {
+ processFile(&entry);
+ break;
+ }
+ } while (entry.findNext());
+ }
+ entry.freeEntry();
+}
+/**
+ * Processes a directory tree.
+ *
+ * Finds all filtered files in this directory and its subdirs and call <code>processFile()</code>
+ * or <code>processDir()</code>.
+ *
+ * @param directory the name of the directory
+ */
+void ReTool::processTree(const char* directory) {
+ m_traverser.changeBase(directory);
+ m_traverser.setPropertiesFromFilter(&m_filter);
+ ReDirStatus_t* entry;
+ int level;
+ while ((entry = m_traverser.nextFile(level, &m_filter)) != NULL) {
+ if (entry->isDirectory()) {
+ processDir(entry);
+ m_directories++;
+ } else {
+ processFile(entry);
+ m_files++;
+ m_sizes += entry->fileSize();
+ }
+ }
+}
+/**
+ * Processes one file.
+ *
+ * Normally this method will be overwritten.
+ *
+ * @param entry the properties of the file to process
+ */
+void ReTool::processFile(ReDirStatus_t* entry) {
+ fprintf(m_output, "+++ ignored (not a directory): %s\n", entry->fullName());
+}
+/**
+ * Issues a summary message if verbose level allows this.
+ *
+ * @param prefix NULL or a line prefix
+ */
+void ReTool::printSummary(const char* prefix) {
+ if (m_verboseLevel >= V_SUMMARY) {
+ int duration = int(time(NULL) - m_start);
+ ReByteBuffer line;
+ ReByteBuffer line2;
+ statisticAsString(line);
+ double rate =
+ duration == 0 ? 0.0 : (m_files + m_directories) / duration;
+ line.append(rate, " %.1f").append(i18n("/sec"), -1);
+ m_traverser.statisticAsString(line2);
+ line2.appendChar(' ').appendTime(duration).append(" ", 1).append(
+ i18n("sec"));
+ fprintf(m_output, "%s=== filtered: %s\n", prefix == NULL ? "" : prefix,
+ line.str());
+ fprintf(m_output, "%s=== total: %s\n", prefix == NULL ? "" : prefix,
+ line2.str());
+ }
+
+}
+/**
+ * Processes one directory.
+ *
+ * Normally this method will be overwritten.
+ *
+ * @param entry the properties of the directory to process
+ */
+void ReTool::processDir(ReDirStatus_t* entry) {
+ fprintf(m_output, "+++ ignored (not a file): %s\n", entry->fullName());
+}
+
+/**
+ * Constructor.
+ */
+ReDirStatisticData::ReDirStatisticData() :
+ ReDirTreeStatistic(),
+ m_path() {
+}
+/**
+ * Copy constructor.
+ *
+ * @param source the source to copy
+ */
+ReDirStatisticData::ReDirStatisticData(const ReDirStatisticData& source) :
+ m_path(source.m_path) {
+}
+/**
+ * Assignment operator.
+ *
+ * @param source the source to copy
+ * @return the instance itself
+ */
+ReDirStatisticData& ReDirStatisticData::operator =(
+ const ReDirStatisticData& source) {
+ m_sizes = source.m_sizes;
+ m_files = source.m_files;
+ m_directories = source.m_directories;
+ m_path = source.m_path;
+ return *this;
+}
+
+/**
+ * Constructor.
+ *
+ * @param logger logger for error handling
+ * @param deltaList increment if list must be increased
+ * @param deltaBuffer increment if content buffer must be increased
+ */
+ReDirStatistic::ReDirStatistic(ReLogger* logger, int deltaList, int deltaBuffer) :
+ ReTool(s_statisticUsage, s_statisticExamples, 2, 0, 1, false, logger),
+ m_list(deltaList, deltaBuffer),
+ m_traceInterval(60),
+ m_lastTrace(0) {
+ // standard short options: D d O o P p T t v y Z z
+ m_programArgs.addBool("kbyte",
+ i18n("output format is '<kbyte> <path>' (like unix 'du' command)"), 'k',
+ "kbyte", false);
+ addStandardFilterOptions();
+}
+/**
+ * Destructor.
+ */
+ReDirStatistic::~ReDirStatistic() {
+}
+
+/**
+ * Adds the data from another instance.
+ *
+ * @param source the other instance
+ * @return the instance itself
+ */
+ReDirStatisticData& ReDirStatisticData::add(const ReDirStatisticData& source) {
+ m_sizes += source.m_sizes;
+ m_files += source.m_files;
+ m_directories += source.m_directories;
+ return *this;
+}
+/**
+ * Initializes the data of the instance.
+ */
+void ReDirStatisticData::clear() {
+ ReDirTreeStatistic::clear();
+ m_path.setLength(0);
+}
+/**
+ * Constructor.
+ *
+ * @param logger logger for error handling
+ */
+ReDirBatch::ReDirBatch(ReLogger* logger) :
+ ReTool(s_batchUsage, s_batchExamples, 0, 0, 0, true, logger),
+ m_arguments(),
+ m_script(),
+ m_isExe(false) {
+ // standard short options: D d O o P p T t v y Z z
+ m_programArgs.addString("first",
+ i18n("defines the first line of the output"), '1', "first-line", true,
+#if defined __linux__
+ "#! /bin/sh"
+#elif defined __WIN32__
+ "rem this batch is created by dirtool"
+#endif
+) ;
+ m_programArgs.addString("arguments", i18n("template for the output line.\n"
+ "Possible placeholders: (e.g. e:\\data\\sample.txt)\n"
+ " !full!: e:\\data\\sample.txt\n"
+ " !path!: e:\\data\\\n"
+ " !basename!: sample.txt\n"
+ " !name!: sample\n"
+ " !ext!: .txt\n"
+ "example: --arguments='echo !basename! in !path! found'"), 'a',
+ "arguments", false, NULL);
+ m_programArgs.addString("script",
+ i18n("name of the script (starts each output line)"), 'c', "script",
+ false, NULL);
+#if defined __WIN32__
+ m_programArgs.addBool("isexe",
+ i18n("supresses the starting 'call' of each output line"
+ "neccessary if a *.exe will be called (instead of a *.bat)"),
+ 'x', "is-exe", false);
+#endif
+ addStandardFilterOptions();
+}
+
+static void replaceMakros(const char* arguments, ReDirStatus_t* entry,
+ const char* delim, ReByteBuffer& line) {
+ line.set(arguments, -1);
+ // we prepare the removal of unwanted delimiters in constructed placeholders:
+ // example: !path!!name!: without correction: "e:\\data\\""xxx"
+ // We want: "e:\\data\\xxx"
+ line.replaceAll("!!", 2, "!\x01!", 3);
+ ReByteBuffer replacement;
+ if (strstr(arguments, "!full!") != NULL) {
+ replacement.set(delim, -1).append(entry->m_path);
+ replacement.append(entry->node(), -1).append(delim, -1);
+ line.replaceAll("!full!", 6, replacement.str(), replacement.length());
+ }
+ if (strstr(arguments, "!path!") != NULL) {
+ replacement.set(delim, -1).append(entry->m_path).append(delim, -1);
+ line.replaceAll("!path!", 6, replacement.str(), replacement.length());
+ }
+ if (strstr(arguments, "!basename!") != NULL) {
+ replacement.set(delim, -1).append(entry->node(), -1).append(delim, -1);
+ line.replaceAll("!basename!", 10, replacement.str(),
+ replacement.length());
+ }
+ if (strstr(arguments, "!name!") != NULL) {
+ replacement.set(delim, -1).append(entry->node(), -1);
+ int ix = replacement.rindexOf(".", 1);
+ if (ix > 1)
+ replacement.setLength(ix);
+ replacement.append(delim, -1);
+ line.replaceAll("!name!", 6, replacement.str(), replacement.length());
+ }
+ if (strstr(arguments, "!ext!") != NULL) {
+ replacement.set(delim, -1).append(entry->node(), -1);
+ int ix = replacement.rindexOf(".", 1);
+ if (ix > 1)
+ replacement.remove(1, ix - 1);
+ else
+ replacement.setLength(1);
+ replacement.append(delim, -1);
+ line.replaceAll("!ext!", 5, replacement.str(), replacement.length());
+ }
+ // We remove the unwanted delimiters (see above):
+ ReByteBuffer buffer;
+ buffer.set(delim, -1).appendChar('\01').append(delim, -1);
+ line.replaceAll(buffer.str(), buffer.length(), "", 0);
+}
+/**
+ * Creates the batch file.
+ */
+void ReDirBatch::doIt() {
+ ReByteBuffer buffer;
+ m_programArgs.getString("arguments", m_arguments);
+ m_programArgs.getString("script", m_script);
+ if (m_arguments.length() + m_script.length() == 0)
+ help(
+ i18n(
+ "one of the option must be set: -a (--arguments) or -c (--script)"));
+#if defined __WIN32__
+ m_isExe = m_programArgs.getBool("isexe");
+#endif
+ m_programArgs.getString("first", buffer);
+ if (buffer.length() > 0)
+ fprintf(m_output, "%s\n", buffer.str());
+ processFileArguments();
+#if defined __linux__
+ static const char* prefix = "# ";
+#elif defined __WIN32__
+ static const char* prefix = "rem ";
+#endif
+ printSummary(prefix);
+}
+
+/**
+ * Processes one directory.
+ *
+ * @param entry the properties of the directory to process
+ */
+void ReDirBatch::processDir(ReDirStatus_t* entry) {
+ processFile(entry);
+}
+
+/**
+ * Processes one file.
+ *
+ * @param entry the properties of the file to process
+ */
+void ReDirBatch::processFile(ReDirStatus_t* entry) {
+ ReByteBuffer line;
+#if defined __linux__
+ static const char* delim = "'";
+#elif defined __WIN32__
+ static const char* delim = "\"";
+#endif
+ if (m_script.length() > 0) {
+#if defined __WIN32__
+ if (! m_isExe)
+ line.append("call ");
+#endif
+ line.append(m_script).appendChar(' ').appendChar(delim[0]);
+ line.append(entry->m_path).append(entry->node(), -1);
+ line.appendChar(delim[0]);
+ } else {
+ replaceMakros(m_arguments.str(), entry, delim, line);
+ }
+ fprintf(m_output, "%s\n", line.str());
+}
+
+/**
+ * Constructor.
+ *
+ * @param logger logger for error handling
+ */
+ReDirChecksum::ReDirChecksum(ReLogger* logger) :
+ ReTool(s_checksumUsage, s_checksumExamples, 0, 0, 0, true, logger),
+ m_command(CMD_LIST),
+ m_digest(NULL),
+ m_buffer() {
+ // standard short options: D d O o P p T t v y Z z
+ m_programArgs.addString("algorithm", i18n("algorithm: 'MD5' or 'RPD64'"),
+ 'a', "--algorithm", false, "MD5");
+ m_programArgs.addInt("buffersize",
+ i18n("buffer size for file reading (in KiByte)"), 'b', "--buffer-size",
+ 4 * 1024);
+ m_programArgs.addString("command",
+ i18n(
+ "'build': builds in each directory a file '.dt.chksum' with sum and name\n"
+ "'compare': shows differences between the content of '.dt.chksum' and the current files\n"
+ "'list': shows checksum and filename\n"
+ "'update': like compare, but update the content of '.dt.chksum'"),
+ 'c', "--command", false, "list");
+ m_programArgs.addInt("salt", i18n("a number which change the checksum"),
+ 's', "--salt", 0);
+ addStandardFilterOptions();
+}
+
+/**
+ * Destructor.
+ */
+ReDirChecksum::~ReDirChecksum() {
+ delete m_digest;
+ m_digest = NULL;
+}
+/**
+ * Builds a checksum storage for a given directory.
+ *
+ * @param path the directory to process
+ */
+void ReDirChecksum::buildStorage(const char* path, const char* storageFile) {
+ ReTraverser traverser(path);
+ traverser.setMaxLevel(0);
+ ReDirStatus_t* entry;
+ ReRPD64 digest2;
+ digest2.setSalt(0x2004199111121989ll);
+ ReByteBuffer line;
+ FILE* fp = fopen(storageFile, "w");
+ if (fp == NULL) {
+ m_logger->sayF(LOG_ERROR | CAT_FILE, LC_BUILD_DIRECTORY_1,
+ i18n("cannot open file: $1 (errno: $2)")).arg(storageFile).arg(
+ errno).end();
+
+ } else {
+ int level;
+ while ((entry = traverser.nextFile(level, &m_filter)) != NULL) {
+ line.setLength(0);
+ if (!entry->isDirectory()) {
+ calculateChecksum(entry->fullName(), *m_digest, m_buffer,
+ m_logger);
+ line.append(m_digest->hexDigest());
+ }
+ line.appendChar('\t').append(entry->node());
+ digest2.update(line);
+ fprintf(fp, "%s\n", line.str());
+ if (m_verboseLevel >= V_NORMAL)
+ fprintf(m_output, "%16s\t%s",
+ entry->isDirectory() ? "" : m_digest->hexDigest().str(),
+ entry->fullName());
+ }
+ fprintf(fp, "%s\n", digest2.hexDigest().str());
+ fclose(fp);
+ }
+}
+
+/**
+ * Compares the files of a directory with the files in the storage file
+ *
+ * @param path the directory
+ * @param storageFile the file with the checksums
+ */
+void ReDirChecksum::compareDir(const char* path, ReStringList& names,
+ const char* storageFile) {
+ // forget the last line (checksum):
+ int count = names.count() - 1;
+ names.remove(count);
+ for (int ii = 0; ii < count; ii++) {
+ const char* name = names.strOf(ii);
+ const char* ptr = strchr(name, '\t');
+ if (ptr != NULL)
+ names.replaceString(ii, ptr + 1);
+ }
+ names.sort();
+ names.setSorted(true);
+ ReTraverser traverser(path);
+ traverser.setMaxLevel(0);
+ ReDirStatus_t* entry;
+ int level;
+ while ((entry = traverser.nextFile(level, &m_filter)) != NULL) {
+ ReSeqArray::Index index;
+ const char* node = entry->node();
+ // the stringlist stores the string with trailing '\0'
+ if (!names.binarySearch(node, strlen(node) + 1, index)) {
+ m_logger->sayF(LOG_ERROR | CAT_SECURITY, LC_COMPARE_DIR_1,
+ i18n("missing file $1 in storage $2")).arg(entry->fullName())
+ .arg(storageFile).end();
+ }
+ }
+}
+
+/**
+ * Compares a storage file with the files of a directory.
+ *
+ * @param path the directory
+ * @param storageFile the file with the checksums
+ */
+void ReDirChecksum::compareStorage(const char* path, const char* storageFile) {
+ ReStringList storage;
+ storage.readFromFile(storageFile);
+ if (storage.count() <= 0) {
+ m_logger->sayF(LOG_ERROR | CAT_SECURITY, LC_COMPARE_STORAGE_1,
+ i18n("empty storage file: $1")).arg(storageFile).end();
+ } else if (!isValid(storage)) {
+ m_logger->sayF(LOG_ERROR | CAT_SECURITY, LC_COMPARE_STORAGE_2,
+ i18n("corrupted storage file: $1")).arg(storageFile).end();
+ } else {
+ ReStringList cols;
+ ReByteBuffer fullname(path);
+ fullname.ensureLastChar(OS_SEPARATOR_CHAR);
+ int pathLength = fullname.length();
+ struct stat info;
+ // test all files of the storage:
+ for (size_t ii = 0; ii < storage.count() - 1; ii++) {
+ cols.split(storage.strOf(ii), '\t');
+ int colCount = cols.count();
+ if (colCount != 2) {
+ m_logger->sayF(LOG_ERROR | CAT_SECURITY, LC_COMPARE_STORAGE_3,
+ i18n("wrong format ($1) in storage file $2-$3")).arg(
+ colCount).arg(storageFile).arg(ii + 1).end();
+ break;
+ } else {
+ fullname.setLength(pathLength).append(cols.strOf(1));
+ if (cols.strLengthOf(0) == 0) {
+ // a directory:
+ if (stat(fullname.str(), &info) != 0) {
+ m_logger->sayF(LOG_ERROR | CAT_SECURITY,
+ LC_COMPARE_STORAGE_4,
+ i18n("missing directory $1 ($2-$3)")).arg(fullname)
+ .arg(storageFile).arg(ii + 1).end();
+ } else if (!S_ISDIR(info.st_mode)) {
+ m_logger->sayF(LOG_ERROR | CAT_SECURITY,
+ LC_COMPARE_STORAGE_5,
+ i18n("directory is now a file: $1 ($2-$3)")).arg(
+ fullname).arg(storageFile).arg(ii + 1).end();
+ }
+ } else {
+ // a directory:
+ if (stat(fullname.str(), &info) != 0) {
+ m_logger->sayF(LOG_ERROR | CAT_SECURITY,
+ LC_COMPARE_STORAGE_6,
+ i18n("missing file $1 ($2-$3)")).arg(fullname).arg(
+ storageFile).arg(ii + 1).end();
+ } else if (S_ISDIR(info.st_mode)) {
+ m_logger->sayF(LOG_ERROR | CAT_SECURITY,
+ LC_COMPARE_STORAGE_7,
+ i18n("file is now a directory: $1 ($2-$3)")).arg(
+ fullname).arg(storageFile).arg(ii + 1).end();
+ } else {
+ calculateChecksum(fullname.str(), *m_digest, m_buffer,
+ m_logger);
+ if (!m_digest->hexDigest().equals(cols.strOf(0))) {
+ m_logger->sayF(LOG_ERROR | CAT_SECURITY,
+ LC_COMPARE_STORAGE_7,
+ i18n("checksum different: $1 ($2-$3)")).arg(
+ fullname).arg(storageFile).arg(ii + 1).end();
+ }
+ }
+ }
+ }
+ }
+ compareDir(path, storage, storageFile);
+ }
+}
+/**
+ * Checks the validity of a storage file.
+ *
+ * The storage file is protected with a checksum.
+ * The checksum has another salt as the checksums of the files. This makes it
+ * much harder to manipulate the storage file.
+ *
+ * @param storage the storage file as string list.
+ */
+bool ReDirChecksum::isValid(const ReStringList& storage) {
+ int count = storage.count();
+ ReRPD64 digest2;
+ digest2.setSalt(0x2004199111121989ll);
+
+ for (int ii = 0; ii < count - 1; ii++) {
+ const char* line = storage.strOf(ii);
+ digest2.update(line);
+ }
+ bool rc = true;
+ const char* hex = storage.strOf(count - 1);
+ if (!digest2.hexDigest().equals(hex))
+ rc = false;
+ return rc;
+}
+/**
+ * Lists the metadata of the specified files.
+ */
+void ReDirChecksum::doIt() {
+ int size = m_programArgs.getInt("buffersize") * 1024;
+ m_buffer.setLength(size);
+ ReByteBuffer value;
+ m_programArgs.getString("command", value);
+ if (value.equals("list", -1, true))
+ m_command = CMD_LIST;
+ else if (value.equals("build", -1, true))
+ m_command = CMD_BUILD;
+ else if (value.equals("compare", -1, true))
+ m_command = CMD_COMPARE;
+ else if (value.equals("update", -1, true))
+ m_command = CMD_UPDATE;
+ else
+ help(i18n("unknown command (expected: build compare list update): "),
+ value.str());
+
+ m_programArgs.getString("algorithm", value);
+ if (value.equals("md5", -1, true))
+ m_digest = new ReMD5();
+ else if (value.equals("rpd64", -1, true))
+ m_digest = new ReRPD64();
+ else
+ help(i18n("unknown algorithm (expected: MD5 RPD64): "), value.str());
+ int salt = m_programArgs.getInt("salt");
+ if (salt != 0)
+ m_digest->setSalt(salt);
+ m_filter.m_allDirectories = true;
+ processFileArguments();
+ printSummary();
+}
+
+/**
+ * Processes one directory.
+ *
+ * @param entry the properties of the directory to process
+ */
+void ReDirChecksum::processDir(ReDirStatus_t* entry) {
+ ReByteBuffer storageFile;
+ if (m_command != CMD_LIST) {
+ storageFile.append(entry->fullName());
+ storageFile.ensureLastChar(OS_SEPARATOR_CHAR);
+ storageFile.append(".dt.chksum");
+ }
+ switch (m_command) {
+ case CMD_BUILD:
+ buildStorage(entry->fullName(), storageFile.str());
+ break;
+ case CMD_COMPARE:
+ compareStorage(entry->fullName(), storageFile.str());
+ break;
+ case CMD_UPDATE:
+ printf("update not implemented");
+ break;
+ default:
+ break;
+ }
+}
+/**
+ * Processes one file.
+ *
+ * @param entry the properties of the file to process
+ */
+void ReDirChecksum::processFile(ReDirStatus_t* entry) {
+ const char* name = entry->fullName();
+ switch (m_command) {
+ case CMD_LIST:
+ calculateChecksum(name, *m_digest, m_buffer, m_logger);
+ fprintf(m_output, "%s %s\n", m_digest->hexDigest().str(), name);
+ break;
+ default:
+ break;
+ }
+}
+/**
+ * Compares a storage file with the files of a directory.
+ *
+ * @param path the directory
+ * @param storageFile the file with the checksums
+ */
+void ReDirChecksum::updateStorage(const char* path, const char* storageFile) {
+
+}
+
+/**
+ * Calculates the checksum of a given file.
+ *
+ * @param name the full filename
+ * @param digest IN: defines the kind of checksum<br>
+ * OUT: contains the calculated checksum
+ * @param buffer IN/OUT: a buffer for file reading
+ * @param logger logger for error processing
+ * @return <code>digest</code> (for chaining)
+ */
+ReDigest& ReDirChecksum::calculateChecksum(const char* name, ReDigest& digest,
+ ReByteBuffer& buffer, ReLogger* logger) {
+ FILE* fp = fopen(name, "rb");
+ if (fp == NULL) {
+ if (logger != NULL)
+ logger->sayF(LOG_ERROR | CAT_FILE, LC_CALCULATE_CHECKSUM_1,
+ i18n("cannot open file: $1 (errno: $2)")).arg(name).arg(
+ errno).end();
+ } else {
+ ReMD5 digest;
+ size_t readBytes;
+ uint8_t* buf = reinterpret_cast<uint8_t*>(buffer.buffer());
+ size_t blockSize = buffer.length();
+
+ while ((readBytes = fread(buf, 1, blockSize, fp)) > 0) {
+ digest.update(buf, readBytes);
+ }
+ fclose(fp);
+ }
+ return digest;
+}
+
+/**
+ * Constructor.
+ *
+ * @param logger logger for error handling
+ */
+ReDirDelete::ReDirDelete(ReLogger* logger) :
+ ReTool(s_deleteUsage, s_deleteExamples, 0, 0, 0, true, logger) {
+ addStandardFilterOptions();
+}
+
+/**
+ * Deletes the specified files/directories.
+ */
+void ReDirDelete::doIt() {
+ m_traverser.setDepthFirst(true);
+ processFileArguments();
+ printSummary();
+}
+
+/**
+ * Processes one directory.
+ *
+ * @param entry the properties of the directory to process
+ */
+void ReDirDelete::processDir(ReDirStatus_t* entry) {
+ _rmdir(entry->fullName());
+}
+/**
+ * Processes one file.
+ *
+ * @param entry the properties of the file to process
+ */
+void ReDirDelete::processFile(ReDirStatus_t* entry) {
+ const char* name = entry->fullName();
+ if (m_verboseLevel >= V_NORMAL) {
+ fprintf(m_output, "%s\n", name);
+ }
+ if (_unlink(name) != 0)
+ m_logger->sayF(LOG_ERROR | CAT_FILE, LC_DELETE_1,
+ i18n("cannot delete ($1): $2")).arg(errno).arg(name).end();
+}
+
+/**
+ * Constructor.
+ *
+ * @param logger logger for error handling
+ */
+ReDirList::ReDirList(ReLogger* logger) :
+ ReTool(s_listUsage, s_listExamples, 0, 0, 0, true, logger),
+ m_widthOwner(13),
+ m_shortFormat(false),
+ m_withRights(false),
+ m_numerical(false) {
+ // standard short options: D d O o P p T t v y Z z
+ m_programArgs.addBool("short", i18n("output is only path and basename"),
+ '1', "--short", false);
+ m_programArgs.addBool("rights", i18n("show the permission/right info"), 'r',
+ "--rights", false);
+ m_programArgs.addBool("numerical",
+ i18n("the permission/right info is shown as numbers"), 'n',
+ "--numerical", false);
+ m_programArgs.addInt("owner", i18n("space reserved for owner/group"), 'w',
+ "--width-owner", 13);
+ addStandardFilterOptions();
+}
+
+/**
+ * Lists the metadata of the specified files.
+ */
+void ReDirList::doIt() {
+ m_shortFormat = m_programArgs.getBool("short");
+ m_withRights = m_programArgs.getBool("rights");
+ m_numerical = m_programArgs.getBool("numerical");
+ m_widthOwner = m_programArgs.getInt("owner");
+ processFileArguments();
+ printSummary();
+}
+
+/**
+ * Processes one directory.
+ *
+ * @param entry the properties of the directory to process
+ */
+void ReDirList::processDir(ReDirStatus_t* entry) {
+ processFile(entry);
+}
+/**
+ * Processes one file.
+ *
+ * @param entry the properties of the file to process
+ */
+void ReDirList::processFile(ReDirStatus_t* entry) {
+ ReByteBuffer bufferRights;
+ ReByteBuffer bufferTime;
+ if (m_shortFormat)
+ fprintf(m_output, "%s%s\n", entry->m_path.str(), entry->node());
+ else {
+ if (m_withRights)
+ entry->rightsAsString(bufferRights, m_numerical, m_widthOwner);
+ fprintf(m_output, "%c%s %12.6f %s %s%s\n", entry->typeAsChar(),
+ bufferRights.str(), entry->fileSize() / 1E6,
+ entry->filetimeAsString(bufferTime), entry->m_path.str(),
+ entry->node());
+ }
+}
+
+/**
+ * Constructor.
+ *
+ * @param logger logger for error handling
+ */
+ReDirRandom::ReDirRandom(ReLogger* logger) :
+ ReTool(s_randomUsage, s_randomExamples, 2, 0, 0, true, logger) {
+ m_hasStandardArgs = false;
+ m_programArgs.addBool("multiple",
+ i18n("result can contain a value multiple times"), 'm', "--multiple",
+ false);
+ m_programArgs.addInt("perline", i18n("number of values per line"), 'l',
+ "--values-per-line", 10);
+ m_programArgs.addBool("sort", i18n("the result is sorted"), 's', "--sort",
+ false);
+ m_programArgs.addInt("width",
+ i18n("output format: minimum width of the numbers"
+ " 0: width of the maximum"), 'w', "--min-width", 0);
+}
+
+/**
+ * Lists the metadata of the specified files.
+ */
+void ReDirRandom::doIt() {
+ bool multiple = m_programArgs.getBool("multiple");
+ bool sort = m_programArgs.getBool("sort");
+ int count = atol(m_programArgs.arg(0));
+ int to = atol(m_programArgs.arg(1));
+ int from = 1;
+ if (m_programArgs.argCount() > 2)
+ from = atol(m_programArgs.arg(2));
+ ReShiftRandom rand;
+ rand.setSeed(rand.nearTrueRandom());
+ for (int ii = rand.nextInt(100); ii > 0; ii--)
+ rand.nextInt();
+ int factor = to <= 1000 ? 1000 * 1000 : 1;
+ int numbersPerLine = m_programArgs.getInt("perline");
+ int width = m_programArgs.getInt("width");
+ if (width <= 0)
+ width = ReByteBuffer("").appendInt(to).length();
+ ReByteBuffer format("%");
+ format.appendInt(width).append("d%s");
+ int found = 0;
+ ReSeqArray values(count);
+ ReByteBuffer value;
+ if (!multiple && count >= to - from + 1)
+ help(
+ i18n(
+ "not realy random: all possible values requested. Do you mean '--multiple'?"));
+ while (found < count) {
+ value.setLength(0).appendInt(
+ from + rand.nextInt((to - from + 1) * factor) / factor, "%010d");
+ int ix = (int) values.find(value.str());
+ if (!multiple && ix != -1)
+ continue;
+ found++;
+ values.add(-1, value.str());
+ }
+ if (sort)
+ values.sort();
+ for (int ii = 0; ii < count; ii++) {
+ values.get(ii, value);
+ int val = atol(value.str());
+ printf(format.str(), val,
+ ii % numbersPerLine == numbersPerLine - 1 ? "\n" : " ");
+ }
+ if (count % numbersPerLine != 0)
+ printf("\n");
+}
+
+/**
+ * Calculates the statistic of a directory tree.
+ *
+ *
+ */
+const ReStringList& ReDirStatistic::calculate(const char* base, int level,
+ void (*formatter)(const ReDirStatisticData& data, ReDirStatistic& parent,
+ ReByteBuffer& line)) {
+ ReDirEntryFilter filter;
+ ReTraverser traverser(base, this);
+ setFilterFromProgramArgs(filter);
+ traverser.setPropertiesFromFilter(&filter);
+ if (level > 1024)
+ level = 1024;
+ else if (level < 0)
+ level = 0;
+ m_list.clear();
+ ReDirStatisticData** dataStack = new ReDirStatisticData*[level + 1];
+ memset(dataStack, 0, sizeof dataStack[0] * (level + 1));
+ dataStack[0] = new ReDirStatisticData();
+ int topOfStack = 0;
+ ReDirStatus_t* entry;
+ int currentDepth = -1;
+ ReDirStatisticData* current = dataStack[0];
+ current->m_path.set(base, -1);
+ ReByteBuffer line;
+ bool useFilter = filter.m_minSize > 0 || filter.m_maxSize != -1
+ || !filetimeIsUndefined(filter.m_minAge)
+ || !filetimeIsUndefined(filter.m_maxAge) || m_nodePatterns.count() > 0;
+ while ((entry = traverser.rawNextFile(currentDepth))) {
+ if (currentDepth <= level && !entry->m_path.equals(current->m_path)) {
+ if (currentDepth <= topOfStack) {
+ // close the entries of the stack above the new top level:
+ while (topOfStack >= currentDepth) {
+ // Add the data to the parent:
+ if (topOfStack > 0)
+ dataStack[topOfStack - 1]->add(*dataStack[topOfStack]);
+ // Append it to the result:
+ (*formatter)(*dataStack[topOfStack], *this, line);
+ m_list.append(line);
+ topOfStack--;
+ }
+ // We reuse the top of stack:
+ topOfStack++;
+ current = dataStack[topOfStack];
+ // exchange the top of stack with the new found directory:
+ current->clear();
+ current->m_path.set(entry->m_path.str(),
+ entry->m_path.length());
+ ;
+ } else {
+ // set up a new stack entry:
+ if (currentDepth != topOfStack + 1)
+ assert(currentDepth == topOfStack + 1);
+
+ topOfStack++;
+ if (dataStack[topOfStack] == NULL)
+ dataStack[topOfStack] = new ReDirStatisticData();
+ else
+ dataStack[topOfStack]->clear();
+ current = dataStack[topOfStack];
+ current->m_path.set(entry->m_path.str(),
+ entry->m_path.length());
+ }
+ }
+ if (entry->isDirectory()) {
+ current->m_directories++;
+ } else if (!useFilter || filter.match(*entry)) {
+ current->m_sizes += entry->fileSize();
+ current->m_files++;
+ }
+ }
+ // close all dirs with parents:
+ while (topOfStack > 0) {
+ // Add the data to the parent:
+ dataStack[topOfStack - 1]->add(*dataStack[topOfStack]);
+ // Append it to the result:
+ (*formatter)(*dataStack[topOfStack], *this, line);
+ m_list.append(line);
+ topOfStack--;
+ }
+ // ... and the overall summery:
+ (*formatter)(*dataStack[0], *this, line);
+ m_list.append(line);
+ // free the resources:
+ for (int ix = 0; ix <= level; ix++)
+ delete dataStack[ix];
+ delete[] dataStack;
+ return m_list;
+}
+
+/**
+ * Build the statistic and print the results.
+ *
+ * @param arc count of arguments in <code>argv</code>
+ * @param argv the program arguments.
+ */
+void ReDirStatistic::doIt() {
+ int depth = 1;
+ if (m_programArgs.argCount() > 1) {
+ const char* arg1 = m_programArgs.arg(1);
+ if (ReStringUtils::lengthOfUnsigned(arg1, -1, (unsigned *) &depth) == 0)
+ m_programArgs.help("depth is not an integer", false, stdout);
+ }
+ void (*proc)(const ReDirStatisticData& data, ReDirStatistic& parent,
+ ReByteBuffer& line) = &formatWithSizeFilesAndDirs;
+ if (m_programArgs.getBool("kbyte"))
+ proc = &formatLikeDu;
+ const ReStringList& list = calculate(m_programArgs.arg(0), depth, proc);
+ ReByteBuffer buffer;
+ for (size_t ix = 0; ix < list.count(); ix++) {
+ buffer.set(list.strOf(ix), list.strLengthOf(ix));
+ fprintf(m_output, "%s\n", buffer.str());
+ }
+ if (m_verboseLevel >= V_SUMMARY) {
+ int duration = int(time(NULL) - m_start);
+ fprintf(m_output, "=== duration: ");
+ if (duration >= 3600)
+ fprintf(m_output, "%d:", duration / 3600);
+ fprintf(m_output, "%02d:%02d\n", duration % 3600 / 60, duration % 60);
+ }
+}
+
+/**
+ * Formats a line like the du (disk usage) command.
+ *
+ * This is a possible parameter of <code>ReDirStatistic::calculate()</code>.
+ *
+ * @param data statistic data, including path name
+ * @param parent the caller (<code>ReDirStatistic</code>). This allows to deliver
+ * a context to this formatting routine (through derivation of
+ * <code>ReDirStatistic</code>)
+ * @param line OUT: the formatted line, the conclusion of the statistic data
+ */
+void formatLikeDu(const ReDirStatisticData& data, ReDirStatistic& parent,
+ ReByteBuffer& line) {
+ line.setLength(0);
+ // Round up to the next KiByte:
+ line.appendInt(int((data.m_sizes + 1023) / 1024)).append("\t").append(
+ data.m_path);
+}
+
+/**
+ * Formats a line in a standard way: MBytes, file count and directory count.
+ *
+ * This is a possible parameter of <code>ReDirStatistic::calculate()</code>.
+ *
+ * @param data statistic data, including path name
+ * @param parent the caller (<code>ReDirStatistic</code>). This allows to deliver
+ * a context to this formatting routine (through derivation of
+ * <code>ReDirStatistic</code>)
+ * @param line OUT: the formatted line, the conclusion of the statistic data
+ */
+void formatWithSizeFilesAndDirs(const ReDirStatisticData& data,
+ ReDirStatistic& parent, ReByteBuffer& line) {
+ line.setLength(0);
+ // Round up to the next KiByte:
+ char buffer[256];
+ _snprintf(buffer, sizeof buffer, "%14.6f MB %7d %7d\t", data.m_sizes / 1E6,
+ data.m_files, data.m_directories);
+ line.append(buffer, -1).append(data.m_path);
+}
+/**
+ * Constructor.
+ *
+ * @param logger logger for error handling
+ */
+ReDirTouch::ReDirTouch(ReLogger* logger) :
+ ReTool(s_touchUsage, s_touchExamples, 1, 0, 0, false, logger),
+ m_buffer()
+//m_modified()
+//m_accessed()
+{
+ setFiletimeUndef(m_modified);
+ setFiletimeUndef(m_accessed);
+ // standard short options: D d O o P p T t v y Z z
+ m_programArgs.addString("accessed", i18n("the new access time.\n"
+ "Formats: absolute, relative to now, relative to the current filetime\n"
+ "[yyyy.mm.dd/]HH:MM:SS \n"
+ "now-<count>{s|m|h|d}\n"
+ "{+|-}<count>{s|m|h|d}"), 'a', "--accessed", false, NULL);
+ m_programArgs.addString("modified", i18n("the new modification time.\n"
+ "Formats: absolute, relative to now, relative to the current filetime\n"
+ "[yyyy.mm.dd/]HH:MM:SS\n"
+ "now-<count>{s|m|h|d}\n"
+ "{+|-}<count>{s|m|h|d}"), 'm', "--modified", false, NULL);
+ addStandardFilterOptions();
+}
+/**
+ * Sets the filetime for the specified files.
+ */
+void ReDirTouch::doIt() {
+ ReByteBuffer buffer;
+ if (m_programArgs.getString("modified", buffer)[0] != '\0')
+ m_modified = checkDate(buffer.str());
+ if (m_programArgs.getString("accessed", buffer)[0] != '\0')
+ m_accessed = checkDate(buffer.str());
+ if (filetimeIsUndefined(m_modified) && filetimeIsUndefined(m_accessed))
+ help("missing --modified and/or --accessed");
+ processFileArguments();
+ printSummary();
+}
+
+/**
+ * Processes one directory.
+ *
+ * @param entry the properties of the directory to process
+ */
+void ReDirTouch::processDir(ReDirStatus_t* entry) {
+ processFile(entry);
+}
+
+static bool isAbsoluteTime(ReFileTime_t& time) {
+#if defined __linux__
+ static struct tm year1980 = { 0, 0, 0, 1, 1 - 1, 1980 - 1900 };
+ static time_t time1980 = mktime(&year1980);
+ return time.tv_sec >= time1980;
+#elif defined __WIN32__
+ static ReFileTime_t filetime1980 = {2148603904, 27846551};
+ bool rc = time > filetime1980;
+ return rc;
+#endif
+}
+static void addRelativeTime(ReFileTime_t& absTime,
+ const ReFileTime_t& relTime) {
+#if defined __linux__
+ if ((absTime.tv_nsec += relTime.tv_nsec) >= 1000 * 1000 * 1000) {
+ absTime.tv_nsec -= 1000 * 1000 * 1000;
+ absTime.tv_sec += 1;
+ }
+ absTime.tv_sec += relTime.tv_sec;
+#elif defined __WIN32__
+ uint64_t absValue = ((uint64_t) absTime.dwHighDateTime << 32) | absTime.dwLowDateTime;
+ uint64_t relValue = ((uint64_t) relTime.dwHighDateTime << 32) | relTime.dwLowDateTime;
+ absValue += relValue;
+ absTime.dwHighDateTime = (uint32_t) (absValue >> 32);
+ absTime.dwLowDateTime = (uint32_t) absValue;
+#endif
+}
+/**
+ * Processes one file.
+ *
+ * @param entry the properties of the file to process
+ */
+void ReDirTouch::processFile(ReDirStatus_t* entry) {
+ ReFileTime_t modified = *entry->modified();
+ int countTimes = 0;
+ if (!filetimeIsUndefined(m_modified)) {
+ countTimes++;
+ if (isAbsoluteTime(m_modified))
+ modified = m_modified;
+ else
+ addRelativeTime(modified, m_modified);
+ }
+ ReFileTime_t accessed = *entry->accessed();
+ if (!filetimeIsUndefined(m_accessed)) {
+ countTimes++;
+ if (isAbsoluteTime(m_accessed))
+ accessed = m_accessed;
+ else
+ addRelativeTime(accessed, m_accessed);
+ }
+ const char* name = entry->fullName();
+ if (touch(name, modified, accessed, ReLogger::globalLogger()) == 0
+ && m_verboseLevel >= V_NORMAL) {
+ ReByteBuffer bufferTime;
+ ReByteBuffer bufferTime2;
+ if (m_verboseLevel == V_NORMAL) {
+ if (countTimes == 2)
+ fprintf(m_output, "%s | %s | %s\n",
+ ReDirStatus_t::filetimeToString(&modified, bufferTime),
+ ReDirStatus_t::filetimeToString(&accessed, bufferTime2),
+ name);
+ else {
+ ReDirStatus_t::filetimeToString(
+ filetimeIsUndefined(m_modified) ? &accessed : &modified,
+ bufferTime);
+ fprintf(m_output, "%s %s\n", bufferTime.str(), name);
+ }
+ } else {
+ ReByteBuffer bufferTime3;
+ ReByteBuffer bufferTime4;
+ if (countTimes == 2)
+ fprintf(m_output, "%s -> %s | %s -> %s | %s\n",
+ ReDirStatus_t::filetimeToString(entry->modified(),
+ bufferTime),
+ ReDirStatus_t::filetimeToString(&modified, bufferTime2),
+ ReDirStatus_t::filetimeToString(entry->accessed(),
+ bufferTime3),
+ ReDirStatus_t::filetimeToString(&accessed, bufferTime4),
+ name);
+ else {
+ ReDirStatus_t::filetimeToString(
+ filetimeIsUndefined(m_modified) ? &m_accessed : &m_modified,
+ bufferTime);
+ fprintf(m_output, "%s -> %s %s\n",
+ ReDirStatus_t::filetimeToString(entry->modified(),
+ bufferTime2), bufferTime.str(), (char*) name);
+ }
+ }
+ }
+}
+
+/**
+ * Sets file times.
+ *
+ * @param filename the name of the file
+ * @param properties contains modification and access time
+ * @param logger NULL or the logger
+ */
+ReErrNo_t ReDirTouch::touch(const char* filename, const ReFileTime_t& modified,
+ const ReFileTime_t& accessed, ReLogger* logger) {
+#if defined __linux__
+ timeval times[2];
+ times[0].tv_sec = accessed.tv_sec;
+ times[0].tv_usec = accessed.tv_nsec / 1000;
+ times[1].tv_sec = modified.tv_sec;
+ times[1].tv_usec = modified.tv_nsec / 1000;
+ ReErrNo_t rc = utimes(filename, times) == 0 ? 0 : errno;
+
+#elif defined __WIN32__
+ ReErrNo_t rc = 0;
+ HANDLE handle = CreateFile(filename, FILE_WRITE_ATTRIBUTES,
+ FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL, NULL);
+ if (handle == INVALID_HANDLE_VALUE)
+ rc = GetLastError();
+ else {
+ if (! SetFileTime(handle, (LPFILETIME) NULL, &accessed, &modified))
+ rc = GetLastError();
+ CloseHandle(handle);
+ }
+#endif
+ if (rc != 0 && logger != NULL)
+ logger->sayF(LOG_ERROR | CAT_FILE, LC_TOUCH_1,
+ i18n("cannot change filetime: $1 (errno: $2)")).arg(filename).arg(
+ errno).end();
+ return rc;
+}
+
+/**
+ * Prints a vector of lines.
+ *
+ * @param lines a vector of lines without newline ('\n')
+ */
+static void printField(const char** lines) {
+ for (int ix = 0; lines[ix] != NULL; ix++) {
+ printf("%s\n", lines[ix]);
+ }
+}
+
+/**
+ * Prints a message how to use the module and exits.
+ */
+void ReDirTools::usage(ReTool& tool) {
+ tool.programArgs().help(NULL, false, stdout);
+}
+
+/**
+ * Prints an message how to use the program and exits.
+ *
+ * @param msg an error message
+ * @param msg2 an addition to the error message or NULL
+ */
+void ReDirTools::usage(const char* msg, const char* msg2) {
+ printf("Version: %s\n", m_version);
+ printf("usage: dirtool <command> <opt>\n"
+ "call 'dirtool help' for more info\n");
+ if (msg != NULL)
+ printf("+++ %s%s\n", msg, msg2 == NULL ? "" : msg2);
+ exit(1);
+}
+
+/**
+ * creates a subdirectory (and the parent directories if neccessary.
+ *
+ * @param path the name of the subdir to create
+ */
+void ReDirSync::makeDirWithParents(ReByteBuffer& path, int minWidth,
+ ReTraverser& traverser) {
+ if (!exists(path)) {
+ ReFileProperties_t* props = NULL;
+#if defined __linux__
+ ReDirStatus_t* entry = traverser.topOfStack();
+ props = entry == NULL ? NULL : &entry->m_status;
+#endif
+ makeDirectory(path.str(), minWidth, props, ReLogger::globalLogger());
+ }
+}
+/**
+ * Constructor.
+ *
+ * @param logger logger for error handling
+ */
+ReDirSync::ReDirSync(ReLogger* logger) :
+ ReTool(s_syncUsage, s_syncExamples, 2, 0, 1, false, logger),
+ m_buffer() {
+ // standard short options: D d O o P p T t v y Z z
+ m_buffer.ensureSize(4u * 1024u * 1024u);
+ m_programArgs.addBool("add",
+ i18n("copies only files which does not exist on the target"), 'a',
+ "add", false);
+ m_programArgs.addBool("dry",
+ i18n("does nothing, but says what should be done"), 'Y', "dry", false);
+ m_programArgs.addInt("timediff",
+ i18n("filetime difference is considered to be equal\n"
+ "if the difference is less than this value (in seconds)"), 'I',
+ "time-delta", 2);
+ m_programArgs.addBool("ignoredate",
+ i18n(
+ "the modification is recognized only by the different size (not time)"),
+ 'i', "ignore-time", false);
+ m_programArgs.addBool("mustexist",
+ i18n("files which don't exist on the target will not be copied"), 'm',
+ "must-exist", false);
+ addStandardFilterOptions();
+}
+
+/**
+ * Copies a file.
+ *
+ * @param entry the source file info
+ * @param target the name of the target file
+ */
+void ReDirSync::copyFile(ReDirStatus_t* entry, const char* target) {
+ ReFileProperties_t* props;
+#ifdef __linux__
+ props = &entry->m_status;
+#else
+ ReFileProperties_t properties;
+ properties.m_modified = *entry->modified();
+ properties.m_accessed = *entry->accessed();
+ properties.m_size = entry->fileSize();
+ props = &properties;
+#endif
+ copyFile(entry->fullName(), props, target, m_buffer,
+ ReLogger::globalLogger());
+}
+
+/**
+ * Copies a file.
+ *
+ * @param source the source file name
+ * @param properties NULL or the properties of the source file
+ * @param target the name of the target file
+ * @param buffer OUT: the reading uses this buffer<br>
+ * Set the capacity to make it more efficient
+ * @param logger NULL or the logger for error messages
+ * @return <code>true</code>success<br>
+ * <code>false</code>error occurred
+ */
+bool ReDirSync::copyFile(const char* source, ReFileProperties_t* properties,
+ const char* target, ReByteBuffer& buffer, ReLogger* logger) {
+ bool rc = false;
+#ifdef __linux__
+ struct stat info;
+ if (properties == NULL) {
+ if (stat(source, &info) == 0)
+ properties = &info;
+ else {
+ if (logger != NULL)
+ logger->sayF(LOG_ERROR | CAT_FILE, LC_COPY_FILE_1,
+ i18n("could not find: $1 (errno: $2)")).arg(source).arg(
+ errno).end();
+ }
+ }
+ FILE* fpSource = fopen(source, "rb");
+ if (fpSource == NULL) {
+ if (logger != NULL)
+ logger->sayF(LOG_ERROR | CAT_FILE, LC_COPY_FILE_2,
+ i18n("cannot open $1 (errno: $2)")).arg(source).arg(errno).end();
+ } else {
+ ReFileSize_t size =
+ properties == NULL ? 0x7fffffff : properties->st_size;
+ FILE* fpTarget = fopen(target, "w");
+ if (fpTarget == NULL) {
+ if (logger != NULL)
+ logger->sayF(LOG_ERROR | CAT_FILE, LC_COPY_FILE_3,
+ i18n("cannot open $1 (errno: $2)")).arg(target).arg(errno)
+ .end();
+ } else {
+ while (size > 0) {
+ size_t blockSize = buffer.capacity();
+ if ((int) blockSize > size)
+ blockSize = size;
+ if (fread(buffer.buffer(), blockSize, 1, fpSource) != 1) {
+ if (logger != NULL)
+ logger->sayF(LOG_ERROR | CAT_FILE, LC_COPY_FILE_5,
+ i18n("cannot read $1 (errno: $2)")).arg(source).arg(
+ errno).end();
+ break;
+ }
+ size_t written;
+ if ((written = fwrite(buffer.buffer(), 1, blockSize, fpTarget))
+ != blockSize) {
+ if (logger != NULL)
+ logger->sayF(LOG_ERROR | CAT_FILE, LC_COPY_FILE_6,
+ i18n("cannot write $1 [$2] (errno: $3)")).arg(
+ target).arg(written).arg(errno).end();
+ break;
+ }
+ size -= blockSize;
+ }
+ rc = size == 0ll;
+ fclose(fpTarget);
+ if (properties != NULL)
+ setProperties(target, properties, logger);
+ }
+ fclose(fpSource);
+ }
+#elif defined __WIN32__
+ BOOL cancel = false;
+ rc = CopyFileExA(source, target, NULL, NULL, &cancel, COPY_FILE_NO_BUFFERING) != 0;
+ int errNo = 0;
+ if (! rc)
+ errNo = GetLastError();
+#endif
+ return rc;
+}
+/**
+ * Sets the file properties.
+ *
+ * @param fullName the name of the file
+ * @param properties the properties like times and rights
+ * @param logger NULL or the logger for error messages
+ * @return <code>true</code>: success<br>
+ * <code>false</code>: error occurred
+ */
+bool ReDirSync::setProperties(const char* fullName,
+ ReFileProperties_t* properties, ReLogger* logger) {
+ bool rc = true;
+#if defined __linux__
+ timeval times[2];
+ times[0].tv_sec = properties->st_atim.tv_sec;
+ times[0].tv_usec = properties->st_atim.tv_nsec / 1000;
+ times[1].tv_sec = properties->st_mtim.tv_sec;
+ times[1].tv_usec = properties->st_mtim.tv_nsec / 1000;
+ if (utimes(fullName, times) != 0) {
+ if (logger != NULL)
+ logger->sayF(LOG_ERROR | CAT_FILE, LC_SET_PROPERTIES_1,
+ i18n("cannot change file times: $1 (errno: $2)")).arg(fullName)
+ .arg(errno).end();
+ rc = false;
+ }
+ int rights = properties->st_mode & (S_IRWXO | S_IRWXG | S_IRWXU);
+ if (chmod(fullName, rights) != 0) {
+ if (logger != NULL)
+ logger->sayF(LOG_ERROR | CAT_FILE, LC_SET_PROPERTIES_2,
+ i18n("cannot change file modes: $1 (errno: $2)")).arg(fullName)
+ .arg(errno).end();
+ rc = false;
+ }
+ if (chown(fullName, properties->st_uid, properties->st_gid) != 0) {
+ if (logger != NULL)
+ logger->sayF(LOG_ERROR | CAT_FILE, LC_SET_PROPERTIES_3,
+ i18n("cannot change file owner: $1 (errno: $2)")).arg(fullName)
+ .arg(errno).end();
+ rc = false;
+ }
+#endif
+ return rc;
+}
+
+/**
+ * Creates a directory and its parents (if neccessary).
+ *
+ * @param directory the full name of the directory
+ * @param properties NULL or the properties of the new directory
+ * @param logger NULL or the logger for error messages
+ * @return <code>true</code>success<br>
+ * <code>false</code>error occurred
+ */
+bool ReDirSync::makeDirectory(const char* directory, int minLength,
+ ReFileProperties_t* properties, ReLogger* logger) {
+ bool rc = true;
+ ReByteBuffer path(directory);
+ int start = 0;
+#if defined __WIN32__
+ start = path.indexOf(':');
+#endif
+ path.ensureLastChar(OS_SEPARATOR_CHAR);
+ int ixSlash = start < 0 ? 0 : start;
+ struct stat info;
+ // for all parents and the full path itself:
+ while (ixSlash >= 0) {
+ ixSlash = path.indexOf(OS_SEPARATOR_CHAR, ixSlash + 1);
+ // is the slash in front of the first node, e.g. 'e:\'?
+ if (ixSlash == start + 1)
+ // not a real node: take the next node
+ ixSlash = path.indexOf(OS_SEPARATOR_CHAR, ixSlash + 1);
+ if (ixSlash >= 0) {
+ // we handle only the next node:
+ path.buffer()[ixSlash] = '\0';
+ }
+ // does the node exist?
+ if (lstat(path.str(), &info) != 0) {
+ // no, then we make it:
+ if (_mkdir(path.str(), ALLPERMS) != 0) {
+ if (logger != NULL)
+ logger->sayF(LOG_ERROR | CAT_FILE, LC_MAKE_DIR_1,
+ i18n("could not make directory $1 (errno: $2)")).arg(
+ path.str()).arg(errno).end();
+ rc = false;
+ break;
+ } else {
+#if defined __linux__
+ setProperties(path.str(), properties);
+#endif
+ }
+ }
+ if (ixSlash >= 0) {
+ // restore the full path:
+ path.buffer()[ixSlash] = OS_SEPARATOR_CHAR;
+ }
+ }
+ return rc;
+}
+
+/**
+ * Synchronizes two directory trees.
+ */
+void ReDirSync::doIt() {
+ ReDirEntryFilter filter;
+ const char* sep = OS_SEPARATOR;
+ ReByteBuffer buffer;
+ ReByteBuffer target(m_programArgs.arg(m_programArgs.argCount() - 1));
+ target.removeLastChar(OS_SEPARATOR_CHAR);
+ if (!exists(target))
+ help(i18n("target does not exist: $1"), target.str());
+ else if (!S_ISDIR(m_statInfo.st_mode))
+ help(i18n("target is not a directory: $1"), target.str());
+ size_t lengthTargetBase = target.length();
+ bool addOnly = m_programArgs.getBool("add");
+ int maxFileTimeDiff = m_programArgs.getInt("timediff");
+ bool dry = m_programArgs.getBool("dry");
+ bool ignoreDate = m_programArgs.getBool("ignoredate");
+ bool mustExist = m_programArgs.getBool("mustexist");
+ setFilterFromProgramArgs(filter);
+ int64_t sumSizes = 0;
+ int files = 0;
+ int treeFiles = 0;
+ int treeDirs = 0;
+ int64_t treeSumSizes = 0ll;
+ ReByteBuffer source, targetFile;
+ for (int ix = 0; ix < m_programArgs.argCount() - 1; ix++) {
+ source.set(m_programArgs.arg(ix), -1);
+ target.setLength(lengthTargetBase);
+ bool endsWithSlash = source.endsWith(sep, 1);
+ if (endsWithSlash)
+ source.reduceLength();
+ if (!exists(source))
+ help(i18n("source does not exist: $1"), source.str());
+ else if (!S_ISDIR(m_statInfo.st_mode))
+ help(i18n("source is not a directory: $1"), source.str());
+ if (!endsWithSlash) {
+ // the basename of the source will be appended to the target:
+ int startNode = source.rindexOf(sep, 1, 0, source.length() - 1);
+ target.appendChar(OS_SEPARATOR_CHAR);
+ target.append(source.str() + startNode + 1, -1);
+ }
+ size_t ixSourceRelative = source.length();
+ size_t ixTargetRelative = target.length();
+
+ m_traverser.changeBase(source.str());
+ m_traverser.setPropertiesFromFilter(&filter);
+ int level;
+ ReDirStatus_t* entry;
+ ReByteBuffer line;
+ while ((entry = m_traverser.nextFile(level, &filter)) != NULL) {
+ if (entry->isDirectory())
+ continue;
+ // append the new relative path from source to target:
+ target.setLength(ixTargetRelative);
+ target.append(entry->m_path.str() + ixSourceRelative, -1);
+ if (!exists(target))
+ makeDirWithParents(target, ixTargetRelative, m_traverser);
+ targetFile.set(target).append(entry->node(), -1);
+ const char* targetRelativePath = targetFile.str() + ixTargetRelative
+ + 1;
+ bool targetExists = exists(targetFile);
+ if (!targetExists && mustExist) {
+ if (m_verboseLevel == V_CHATTER)
+ fprintf(m_output, "-ignored: %s does not exist\n",
+ targetRelativePath);
+ continue;
+ }
+ if (targetExists) {
+ if (addOnly) {
+ if (m_verboseLevel >= V_CHATTER)
+ fprintf(m_output, "~ignored: %s exists\n",
+ targetRelativePath);
+ continue;
+ }
+ if (ignoreDate && entry->fileSize() == m_statInfo.st_size) {
+ if (m_verboseLevel >= V_CHATTER)
+ fprintf(m_output, "_ignored: %s same size\n",
+ targetRelativePath);
+ continue;
+ }
+ // target younger than source?
+ int diff = int(
+ m_statInfo.st_mtime
+ - entry->filetimeToTime(entry->modified()));
+ if (!ignoreDate && diff <= maxFileTimeDiff) {
+ if (m_verboseLevel >= V_CHATTER)
+ fprintf(m_output, "=ignored: %s same time\n",
+ targetRelativePath);
+ continue;
+ }
+ }
+ files++;
+ sumSizes += entry->fileSize();
+ if (m_verboseLevel >= V_NORMAL)
+ fprintf(m_output, "%c%s%s\n", targetExists ? '!' : '+',
+ targetRelativePath, dry ? " would be copied" : "");
+ if (!dry)
+ copyFile(entry, targetFile.str());
+ }
+ treeFiles += m_traverser.files();
+ treeDirs += m_traverser.directories();
+ treeSumSizes += m_traverser.sizes();
+ }
+ if (m_verboseLevel >= V_SUMMARY) {
+ int duration = int(time(NULL) - m_start);
+ fprintf(m_output,
+ i18n(
+ "=== copied: %02d:%02d sec %7d file(s) %12.6f MByte (%.3f MB/sec).\n"
+ "=== tree: %5d dir(s) %7d file(s) %12.6f MByte\n"),
+ duration / 60, duration % 60, files, sumSizes / 1E6,
+ sumSizes / 1E6 / (duration == 0 ? 1 : duration), treeDirs,
+ treeFiles, treeSumSizes / 1E6);
+ }
+}
+
+/**
+ * Constructor.
+ *
+ * @param logger logger for error handling
+ */
+ReDirTCP::ReDirTCP(ReLogger* logger) :
+ ReTool(s_tcpUsage, s_tcpExamples, 1, 0, 0, true, logger) {
+ m_hasStandardArgs = false;
+ m_programArgs.addString("size", i18n("size of the message to send/receive\n"
+ "<string> is a number followed by an unit\n"
+ "units: b(yte) k(Byte) K(iByte) m(Byte), M(iByte)\n"
+ "Note: maximum: 16M-32=16777184\n"
+ "examples: -b1m --buffer-size=512K"), 'b', "size", false, "64K");
+ m_programArgs.addInt("port", i18n("port of the server/client"), 'p',
+ "--port", 58111);
+}
+
+/**
+ * Lists the metadata of the specified files.
+ */
+void ReDirTCP::doIt() {
+ int port = m_programArgs.getInt("port");
+ ReByteBuffer buffer;
+ int64_t bufferSize = checkSize(m_programArgs.getString("size", buffer));
+ // the protocol does not allow more than 16 MiByte because of the flags:
+ if (bufferSize > 16LL * 1024 * 1024 - 64LL)
+ help(i18n("buffersize exceeds 16777184 = 16MiByte - 32: "),
+ buffer.str());
+ ReByteBuffer command = m_programArgs.arg(0);
+ if (command.isPrefixOf("server", -1, true)) {
+ ReTCPEchoServer server(port, m_logger);
+ server.setLogSendReceive(false);
+ server.listenForAll();
+ } else if (command.isPrefixOf("client", -1, true)) {
+ const char* ip = m_programArgs.arg(1);
+ ReByteBuffer direction("download");
+ int rounds = 10;
+ int interval = 5;
+ if (m_programArgs.argCount() > 2) {
+ direction = m_programArgs.arg(2);
+ if (!direction.isPrefixOf("download", -1, true)
+ && !direction.isPrefixOf("upload", -1, true)
+ && !direction.isPrefixOf("mixed", -1, true))
+ help(
+ "unknown direction: $1 (use 'download', 'upload' or 'mixed')",
+ direction.str());
+ }
+ if (m_programArgs.argCount() > 3)
+ rounds = atoi(m_programArgs.arg(3));
+ if (m_programArgs.argCount() > 4)
+ interval = atoi(m_programArgs.arg(4));
+ if (tolower(direction.at(0)) == 'm')
+ runMixedClient(ip, port, rounds, interval, (int) bufferSize);
+ else
+ runOneThreadClient(ip, port, rounds, interval, (int) bufferSize,
+ tolower(direction.at(0)) == 'u');
+ } else
+ help("unknown subcommand: $1", command.str());
+}
+void ReDirTCP::runMixedClient(const char* ip, int port, int rounds,
+ int interval, int bufferSize) {
+ help("not implemented: mixed");
+}
+void ReDirTCP::runOneThreadClient(const char* ip, int port, int rounds,
+ int interval, int bufferSize, bool upload) {
+ ReTCPClient client(m_logger);
+ if (client.connect(ip, port)) {
+ time_t start = time(NULL);
+ const char* command = upload ? "strlen" : "filldata";
+ ReByteBuffer message;
+ if (upload)
+ message.appendChar('x', bufferSize);
+ else
+ message.appendInt(bufferSize);
+ time_t lastPrint = start;
+ int64_t size = 0;
+ int duration = 0;
+ ReByteBuffer answer, data;
+ client.setLogSendReceive(false);
+ int64_t sizeCurrent = 0;
+ for (int ii = 0; ii < rounds; ii++) {
+ client.send(command, message.str(), message.length());
+ client.receive(answer, data);
+ size += message.length() + data.length();
+ sizeCurrent += message.length() + data.length();
+ time_t now = time(NULL);
+ if (now >= lastPrint + interval) {
+ duration = int(now - start);
+ int durationCurrent = int(now - lastPrint);
+ printf("%2d: %9.3f MiByte %8.3f / %8.3f kiByte/sec %s\n", ii,
+ size / 1024.0 / 1024,
+ (double) sizeCurrent / durationCurrent / 1024,
+ (double) size / duration / 1024, upload ? "up" : "down");
+ lastPrint = now;
+ sizeCurrent = 0;
+ }
+ }
+ duration = int(time(NULL) - start);
+ if (duration == 0)
+ duration = 1;
+ printf("%2d: %9.3f MiByte %8.3f kiByte %s/sec %s\n", rounds,
+ size / 1024.0 / 1024, (double) size / duration / 1024,
+ upload ? "up" : "down");
+
+ }
+}
+/**
+ * Constructor.
+ *
+ * @param logger logger for error handling
+ */
+ReDirWhich::ReDirWhich(ReLogger* logger) :
+ ReTool(s_batchUsage, s_batchExamples, 0, 0, 0, true, logger) {
+// no standard options:
+ m_programArgs.addBool("all",
+ i18n("all files will be found, not only the first"), 'a', "all", false);
+ m_programArgs.addString("list",
+ i18n("a path list (separator see option 'separator'"), 'l', "list",
+ false,
+ NULL);
+ m_programArgs.addString("separator",
+ i18n("separator between the path elements"), 's', "separator", false,
+#if defined __linux__
+ ":"
+#elif defined __WIN32__
+ ";"
+#endif
+) ;
+ m_programArgs.addString("variable", i18n("variable with the path list"),
+ 'v', "variable", false, "PATH");
+ m_hasStandardArgs = false;
+}
+
+/**
+ * Creates the batch file.
+ */
+void ReDirWhich::doIt() {
+ ReByteBuffer value, path;
+ bool all = false;
+ ReStringList items;
+ char sep = 0;
+ m_programArgs.getString("list", path);
+ if (path.length() == 0) {
+ m_programArgs.getString("variable", value);
+ if (getenv(value.str()) == NULL)
+ help("Umgebungsvariable nicht definiert: ", value.str());
+ else
+ path = getenv(value.str());
+ m_programArgs.getString("separator", value);
+ sep = value.at(0);
+ }
+ items.split(path.str(), sep);
+ struct stat info;
+ ReByteBuffer full;
+ for (int ix = 0; ix < m_programArgs.argCount(); ix++) {
+ bool found = false;
+
+ ReByteBuffer arg(m_programArgs.arg(ix));
+ for (size_t ixItem = 0; ixItem < items.count(); ixItem++) {
+ full.set(items.strOf(ixItem));
+ if (arg.indexOf('*') < 0) {
+ full.ensureLastChar(OS_SEPARATOR_CHAR);
+ full.append(arg);
+ found = stat(full.str(), &info) == 0;
+ if (found)
+ printf("%s\n", full.str());
+ } else {
+ ReDirectory dir(full.str());
+ if (dir.findFirst(arg.str(), false)) {
+ do {
+ printf("%s%c%s\n", full.str(), OS_SEPARATOR_CHAR,
+ dir.currentFile());
+ } while (dir.findNext());
+ }
+ }
+ if (found && !all)
+ break;
+ }
+ }
+}
+
+/**
+ * Tests whether a abrevation of an argument is given.
+ * @param full the full name
+ * @param part the part to test
+ * @return <code>true</code>: part is a prefix of full
+ */
+static bool isArg(const char* full, const char* part) {
+ ReByteBuffer fullArg(full);
+ bool rc = fullArg.startsWith(part, -1);
+ return rc;
+}
+
+/**
+ * Gets the arguments for the "help" command and execute this.
+ *
+ * @param argc the number of arguments
+ * @param argav the argument vector
+ */
+void ReDirTools::help(int argc, const char* argv[]) {
+ if (argc <= 1)
+ printField(s_helpSummary);
+ else {
+ argc--;
+ argv++;
+ const char* arg0 = argv[0];
+ if (isArg("batch", arg0)) {
+ ReDirBatch(m_logger).help(NULL);
+ } else if (isArg("checksum", arg0)) {
+ ReDirChecksum(m_logger).help(NULL);
+ } else if (_stricmp("delete", arg0) == 0) {
+ ReDirDelete(m_logger).help(NULL);
+ } else if (isArg("list", arg0)) {
+ ReDirList(m_logger).help(NULL);
+ } else if (isArg("help", arg0)) {
+ printField(s_helpSummary);
+ } else if (isArg("random", arg0)) {
+ ReDirRandom(m_logger).help(NULL);
+ } else if (isArg("statistic", arg0)) {
+ ReDirStatistic(m_logger).help(NULL);
+ } else if (isArg("test", arg0)) {
+ void testAll();
+ testAll();
+ } else if (isArg("tcp", arg0)) {
+ ReDirTCP(m_logger).help(NULL);
+ } else if (isArg("touch", arg0)) {
+ ReDirTouch(m_logger).help(NULL);
+ } else if (isArg("which", arg0)) {
+ ReDirWhich(m_logger).help(NULL);
+ } else
+ printf("+++ unknown sub command: %s\n", arg0);
+ }
+}
+
+/**
+ * Executes a command.
+ *
+ * @param argc the number of arguments
+ * @param argv the argument vector
+ * @param tool the tool which realizes the command
+ */
+void ReDirTools::run(int argc, const char* argv[], ReTool& tool) {
+ tool.run(argc, argv);
+}
+/**
+ * Gets the arguments for any command and execute this.
+ *
+ * @param argc the number of arguments
+ * @param orgArgv the argument vector
+ */
+int ReDirTools::main(int argc, char* orgArgv[]) {
+ ReDirTools tools;
+ if (argc < 0) {
+ argc = 0;
+ for (int ix = 0; orgArgv[ix] != NULL; ix++)
+ argc++;
+ }
+ const char** argv = (const char**) orgArgv;
+ if (argc < 2) {
+ tools.help(0, argv);
+ exit(1);
+ }
+ argc--;
+ argv++;
+ m_logger = ReLogger::globalLogger();
+ const char* arg0 = argv[0];
+ if (isArg("batch", arg0))
+ ReDirBatch(m_logger).run(argc, argv);
+ else if (isArg("checksum", arg0))
+ ReDirChecksum(m_logger).run(argc, argv);
+ else if (_stricmp("delete", arg0) == 0)
+ ReDirDelete(m_logger).run(argc, argv);
+ else if (isArg("help", arg0))
+ tools.help(argc, argv);
+ else if (isArg("list", arg0))
+ ReDirList(m_logger).run(argc, argv);
+ else if (isArg("random", arg0))
+ ReDirRandom(m_logger).run(argc, argv);
+ else if (isArg("statistic", arg0))
+ ReDirStatistic(m_logger).run(argc, argv);
+ else if (isArg("synchronize", arg0))
+ ReDirSync(m_logger).run(argc, argv);
+ else if (isArg("tcp", arg0))
+ ReDirTCP(m_logger).run(argc, argv);
+ else if (isArg("touch", arg0))
+ ReDirTouch(m_logger).run(argc, argv);
+ else if (isArg("which", arg0))
+ ReDirWhich(m_logger).run(argc, argv);
+ else if (isArg("test", arg0)) {
+ void testAll();
+ testAll();
+ } else
+ tools.usage("unknown command: ", argv[1]);
+ ReTCPServer::globalClose();
+ return 0;
+}
+
/*
- * 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
*/
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);
};
/**
/*
* 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
*/
* 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) {
}
/**
* @param sequence IN/OUT: the serialized byte sequence
* @param length INT/OUT the length of <code>sequence</code>
*/
-void ReRemoteDir::deserialize(uint8_t*& sequence, size_t& length){
-
+void ReRemoteDir::deserialize(uint8_t*& sequence, size_t& length) {
}
/**
* Reads the info from a directory into the instance.
*
* @param path the full path name of the directory to read
*/
-void ReRemoteDir::populate(const char* path){
+void ReRemoteDir::populate(const char* path) {
ReTraverser dir(path);
}
*
* @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) {
}
/*
* 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_
/*
*
*/
-class ReRemoteDir : public ReSerializable {
+class ReRemoteDir: public ReSerializable {
public:
ReRemoteDir();
virtual ~ReRemoteDir();
/**
* A TCP server for file and directory processing.
*/
-class ReRemoteDirService : public ReNetCommandHandler {
+class ReRemoteDirService: public ReNetCommandHandler {
public:
ReRemoteDirService();
~ReRemoteDirService();
-/*\r
- * ReTraverser.cpp\r
- *\r
- * License: Public domain\r
- * Do what you want.\r
- * No warranties and disclaimer of any damages.\r
- * The latest sources: https://github.com/republib\r
- */\r
-\r
-#include "base/rebase.hpp"\r
-#include "os/reos.hpp"\r
-#if defined __WIN32__\r
-#include "accctrl.h"\r
-#include "aclapi.h"\r
-#pragma comment(lib, "advapi32.lib")\r
-#endif\r
-\r
-enum RELOC_TRAVERSER {\r
- LC_RIGHTS_AS_STRING_1 = LC_TRAVERSER + 1, // 50401\r
- LC_RIGHTS_AS_STRING_2, // 50402\r
- LC_RIGHTS_AS_STRING_3, // 50403\r
- LC_GET_PRIVILEGE_1, // 50404\r
- LC_GET_PRIVILEGE_2, // 50405\r
- LC_GET_PRIVILEGE_3, // 50406\r
- LC_GET_FILE_OWNER_1, // 50407\r
- LC_GET_FILE_OWNER_2, // 50408\r
-};\r
-\r
-int ReDirEntryFilter::m_serialId = buildSerialId(CLASSID_DIR_ENTRY_FILTER, 1);\r
-\r
-/**\r
- * Constructor.\r
- */\r
-ReDirStatus_t::ReDirStatus_t(ReLogger* logger) :\r
- m_path(),\r
- m_fullName(),\r
- m_passNo(0),\r
- m_logger(logger),\r
-#ifdef __linux__\r
- m_handle(NULL),\r
- m_data(NULL)\r
-//m_status;\r
-#elif defined WIN32\r
-m_handle(INVALID_HANDLE_VALUE),\r
-//m_data;\r
-m_getPrivilege(true)\r
-#endif\r
-{\r
-#ifdef __linux__\r
- memset(&m_status, 0, sizeof m_status);\r
-#elif defined WIN32\r
- memset(&m_data, 0, sizeof m_data);\r
-#endif\r
-}\r
-\r
-/**\r
- * Returns the last access time.\r
- *\r
- * @return the last access time\r
- */\r
-const ReFileTime_t* ReDirStatus_t::accessed() {\r
-#ifdef __linux__\r
- return &(getStatus()->st_atim);\r
-#elif defined __WIN32__\r
- return &m_data.ftLastAccessTime;\r
-#endif\r
-}\r
-\r
-/**\r
- * Returns the filesize.\r
- *\r
- * @return the filesize\r
- */\r
-ReFileSize_t ReDirStatus_t::fileSize() {\r
-#ifdef __linux__\r
- return getStatus()->st_size;\r
-#elif defined __WIN32__\r
- return ((int64_t) m_data.nFileSizeHigh << 32) + m_data.nFileSizeLow;\r
-#endif\r
-}\r
-\r
-/**\r
- * Returns the file time as a string.\r
- *\r
- * @param buffer OUT: the file time\r
- * @return <code>buffer.str()</code> (for chaining)\r
- */\r
-const char* ReDirStatus_t::filetimeAsString(ReByteBuffer& buffer) {\r
- return filetimeToString(modified(), buffer);\r
-}\r
-\r
-/**\r
- * Converts a filetime to a string.\r
- *\r
- * @param time the filetime to convert\r
- * @param buffer OUT: the buffer for the string\r
- * @return <code>buffer.str()</code>, e.g. "2014.01.07 02:59:43"\r
- */\r
-const char* ReDirStatus_t::filetimeToString(const ReFileTime_t* time,\r
- ReByteBuffer& buffer) {\r
- time_t time1 = filetimeToTime(time);\r
- struct tm* time2 = localtime(&time1);\r
- buffer.setLength(4 + 2 * 2 + 2 * 2 + 1 + 3 * 2 + 2 * 1);\r
- strftime(buffer.buffer(), buffer.length(), "%Y.%m.%d %H:%M:%S", time2);\r
- return buffer.str();\r
-}\r
-\r
-/**\r
- * Converts a filetime to a unix time (seconds since the Epoche).\r
- *\r
- * @param filetime the filetime to convert\r
- * @return the count of seconds since 1.1.1970\r
- */\r
-time_t ReDirStatus_t::filetimeToTime(const ReFileTime_t* filetime) {\r
-#ifdef __linux__\r
- return filetime->tv_sec;\r
-#elif defined __WIN32__\r
- // 64-bit arithmetic:\r
- LARGE_INTEGER date, adjust;\r
- date.HighPart = filetime->dwHighDateTime;\r
- date.LowPart = filetime->dwLowDateTime;\r
- // 100-nanoseconds = milliseconds * 10000\r
- adjust.QuadPart = 11644473600000 * 10000;\r
- // removes the diff between 1970 and 1601\r
- date.QuadPart -= adjust.QuadPart;\r
- // converts back from 100-nanoseconds to seconds\r
- time_t rc = (time_t) (date.QuadPart / 10000000);\r
- return rc;\r
-#endif\r
-}\r
-\r
-/**\r
- * Loads the info about the first file into the instance.\r
- *\r
- * @return <code>true</code>: success\r
- */\r
-bool ReDirStatus_t::findFirst() {\r
- bool rc = false;\r
-#if defined __linux__\r
- if (m_handle != NULL)\r
- closedir(m_handle);\r
- m_handle = opendir(m_path.str());\r
- rc = m_handle != NULL && (m_data = readdir(m_handle)) != NULL;\r
- m_status.st_ino = 0;\r
-#elif defined __WIN32__\r
- if (m_handle != INVALID_HANDLE_VALUE)\r
- FindClose(m_handle);\r
- ReByteBuffer thePath(m_path);\r
- thePath.append(m_path.lastChar() == '\\' ? "*" : "\\*");\r
- m_handle = FindFirstFileA(thePath.str(), &m_data);\r
- rc = m_handle != INVALID_HANDLE_VALUE;\r
-#endif\r
- m_fullName.setLength(0);\r
- return rc;\r
-}\r
-\r
-/**\r
- * Loads the info about the next file into the instance.\r
- *\r
- * @return <code>true</code>: success\r
- */\r
-bool ReDirStatus_t::findNext() {\r
-#if defined __linux__\r
- bool rc = m_handle != NULL && (m_data = readdir(m_handle)) != NULL;\r
- m_status.st_ino = 0;\r
-#elif defined __WIN32__\r
- bool rc = m_handle != INVALID_HANDLE_VALUE && FindNextFileA(m_handle, &m_data);\r
-#endif\r
- m_fullName.setLength(0);\r
- return rc;\r
-}\r
-\r
-/**\r
- * Frees the resources of an instance.\r
- */\r
-void ReDirStatus_t::freeEntry() {\r
-#if defined __linux__\r
- if (m_handle != NULL) {\r
- closedir(m_handle);\r
- m_handle = NULL;\r
- }\r
-#elif defined __WIN32__\r
- if (m_handle != INVALID_HANDLE_VALUE) {\r
- FindClose(m_handle);\r
- m_handle = INVALID_HANDLE_VALUE;\r
- }\r
-#endif\r
- m_path.setLength(0);\r
- m_fullName.setLength(0);\r
-}\r
-\r
-/**\r
- * Returns the full filename (with path).\r
- *\r
- * @return the filename with path\r
- */\r
-const char* ReDirStatus_t::fullName() {\r
- if (m_fullName.length() == 0)\r
- m_fullName.set(m_path).append(node(), -1);\r
- return m_fullName.str();\r
-}\r
-\r
-#if defined __WIN32__\r
-/** Gets the name of the file owner.\r
- *\r
- * @param handle file handle (see <code>CreateFile()</code>)\r
- * @param name OUT: the owner: [domain\\]name\r
- * @return <code>true</code>: success\r
- */\r
-bool ReDirStatus_t::getFileOwner(HANDLE handle, const char* file,\r
- ReByteBuffer& name, ReLogger* logger) {\r
- bool rc = false;\r
- PSID pSidOwner = NULL;\r
- PSECURITY_DESCRIPTOR pSD = NULL;\r
- if (GetSecurityInfo(handle, SE_FILE_OBJECT,\r
- OWNER_SECURITY_INFORMATION, &pSidOwner, NULL, NULL, NULL, &pSD) != ERROR_SUCCESS) {\r
- if (logger != NULL)\r
- logger->sayF(LOG_ERROR | CAT_FILE, LC_GET_FILE_OWNER_1,\r
- "GetSecurityInfo($1): $2").arg(file).arg((int) GetLastError()).end();\r
- } else {\r
- char accountName[128];\r
- char domainName[128];\r
- DWORD dwAcctName = sizeof accountName;\r
- DWORD dwDomainName = sizeof domainName;\r
- SID_NAME_USE eUse = SidTypeUnknown;\r
- if (! LookupAccountSid(NULL, pSidOwner, accountName, &dwAcctName, domainName,\r
- &dwDomainName, &eUse)) {\r
- if (logger != NULL)\r
- logger->sayF(LOG_ERROR | CAT_SECURITY, LC_GET_FILE_OWNER_2,\r
- "LookupAccountSid(): $1").arg((int) GetLastError()).end();\r
- } else {\r
- if (dwDomainName > 0)\r
- name.append(domainName).appendChar('\\');\r
- name.append(accountName);\r
- rc = true;\r
- }\r
- }\r
- return rc;\r
-}\r
-#endif /* __WIN32__ */\r
-\r
-#if defined __WIN32__\r
-/** Tries to get a privilege.\r
- *\r
- * @param privilege the name of the privilege, e.g. "SeBackup"\r
- * @param logger logger for error logging\r
- */\r
-bool ReDirStatus_t::getPrivilege(const char* privilege, ReLogger* logger) {\r
- bool rc = false;\r
- LUID luidPrivilege;\r
- HANDLE hAccessToken;\r
- if (! OpenProcessToken (GetCurrentProcess(),\r
- TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hAccessToken)) {\r
- if (logger != NULL)\r
- logger->sayF(LOG_ERROR | CAT_FILE, LC_GET_PRIVILEGE_1,\r
- "OpenProcessToken(): $1").arg((int) GetLastError()).end();\r
- } else if (! LookupPrivilegeValue (NULL, SE_BACKUP_NAME, &luidPrivilege)) {\r
- if (logger != NULL)\r
- logger->sayF(LOG_ERROR | CAT_FILE, LC_GET_PRIVILEGE_2,\r
- "LookupPrivilegeValue(): $1").arg((int) GetLastError()).end();\r
- } else {\r
- TOKEN_PRIVILEGES tpPrivileges;\r
- tpPrivileges.PrivilegeCount = 1;\r
- tpPrivileges.Privileges[0].Luid = luidPrivilege;\r
- tpPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;\r
- if (AdjustTokenPrivileges (hAccessToken, FALSE, &tpPrivileges,\r
- 0, NULL, NULL) == 0)\r
- rc = true;\r
- else {\r
- int error = GetLastError();\r
- if (error != 1300 && logger != NULL)\r
- logger->sayF(LOG_ERROR | CAT_FILE, LC_GET_PRIVILEGE_3,\r
- "AdjustTokenPrivileges(): $1").arg((int) GetLastError()).end();\r
- }\r
- }\r
- return rc;\r
-}\r
-#endif /* __WIN32__ */\r
-\r
-/**\r
- * Tests whether the instance is a directory.\r
- *\r
- * @return <code>true</code>: instance contains the data of a directory\r
- */\r
-bool ReDirStatus_t::isDirectory() {\r
-#ifdef __linux__\r
- return m_data->d_type == DT_DIR\r
- || (m_data->d_type == DT_UNKNOWN && S_ISDIR(getStatus()->st_mode));\r
-#elif defined __WIN32__\r
- return 0 != (m_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);\r
-#endif\r
-}\r
-\r
-/**\r
- * Tests whether the instance contains data about "." or "..".\r
- *\r
- * @return <code>true</code>: an ignorable entry has been found\r
- */\r
-bool ReDirStatus_t::isDotDir() const {\r
-#ifdef __linux__\r
- bool rc = m_data == NULL\r
- || (m_data->d_name[0] == '.'\r
- && (m_data->d_name[1] == '\0'\r
- || (m_data->d_name[1] == '.' && m_data->d_name[2] == '\0')));\r
-#elif defined __WIN32__\r
- bool rc = m_data.cFileName[0] == '.' && (m_data.cFileName[1] == '\0'\r
- || (m_data.cFileName[1] == '.' && m_data.cFileName[2] == '\0'));\r
-#endif\r
- return rc;\r
-}\r
-\r
-/**\r
- * Tests whether the instance is a symbolic link.\r
- *\r
- * Unter windows it tests whether the the instance is a reparse point.\r
- *\r
- * @return <code>true</code>: instance contains the data of a link\r
- */\r
-bool ReDirStatus_t::isLink() {\r
- bool rc;\r
-#ifdef __linux__\r
- rc = m_data->d_type == DT_LNK\r
- || (m_data->d_type == DT_UNKNOWN && S_ISLNK(getStatus()->st_mode));\r
-#elif defined __WIN32__\r
- rc = 0 != (m_data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT);\r
-#endif\r
- return rc;\r
-}\r
-/**\r
- * Tests whether the instance is a "normal" file.\r
- *\r
- * @return <code>true</code>: instance contains the data of a not special file\r
- */\r
-bool ReDirStatus_t::isRegular() {\r
-#ifdef __linux__\r
- return m_data->d_type == DT_REG\r
- || (m_data->d_type == DT_UNKNOWN && S_ISREG(getStatus()->st_mode));\r
-#elif defined __WIN32__\r
- return 0 == (m_data.dwFileAttributes & (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE));\r
-#endif\r
-}\r
-/**\r
- * Returns the modification time.\r
- *\r
- * @return the modification time\r
- */\r
-const ReFileTime_t* ReDirStatus_t::modified() {\r
-#ifdef __linux__\r
- return &(getStatus()->st_mtim);\r
-#elif defined __WIN32__\r
- return &m_data.ftLastWriteTime;\r
-#endif\r
-}\r
-\r
-/**\r
- * Returns the name of the current file (without path).\r
- *\r
- * @return the name of the current file.\r
- */\r
-const char* ReDirStatus_t::node() const {\r
-#ifdef __linux__\r
- return m_data->d_name;\r
-#elif defined __WIN32__\r
- return m_data.cFileName;\r
-#endif\r
-}\r
-\r
-inline void addRight(int mode, ReByteBuffer& buffer) {\r
- char right;\r
- switch (mode & 7) {\r
- case 1:\r
- right = 'x';\r
- break;\r
- case 2:\r
- right = 'w';\r
- break;\r
- case 3:\r
- right = 'X';\r
- break;\r
- case 4:\r
- right = 'r';\r
- break;\r
- case 5:\r
- right = 'R';\r
- break;\r
- case 6:\r
- right = 'W';\r
- break;\r
- case 7:\r
- right = 'A';\r
- break;\r
- default:\r
- right = '-';\r
- break;\r
- }\r
- buffer.appendChar(right);\r
-}\r
-inline void addId(const char* id, int maxLength, ReByteBuffer& buffer) {\r
- int length = strlen(id);\r
- if (length == maxLength)\r
- buffer.append(id, length);\r
- else if (length < maxLength)\r
- buffer.append(id, length).appendChar(' ', maxLength - length);\r
- else {\r
- buffer.append(id, 2);\r
- buffer.append(id + length - maxLength - 2, maxLength - 2);\r
- }\r
-}\r
-/**\r
- * Returns the file rights as a string.\r
- *\r
- * @param buffer OUT: the file rights\r
- * @param numerical <code>true</code>: the owner/group should be numerical (UID/GID)\r
- * @param ownerWidth the width for group/owner\r
- * @return <code>buffer.str()</code> (for chaining)\r
- */\r
-const char* ReDirStatus_t::rightsAsString(ReByteBuffer& buffer, bool numerical,\r
- int ownerWidth) {\r
- buffer.setLength(0);\r
-#if defined __linux__\r
- if (numerical) {\r
- buffer.appendInt(getStatus()->st_mode & ALLPERMS, "%04o");\r
- buffer.appendInt(getStatus()->st_uid, " %4d");\r
- buffer.appendInt(getStatus()->st_gid, " %4d");\r
- } else {\r
- int mode = getStatus()->st_mode & ALLPERMS;\r
- addRight(mode >> 6, buffer);\r
- addRight(mode >> 3, buffer);\r
- addRight(mode, buffer);\r
- buffer.appendChar(' ');\r
- struct passwd* passwd = getpwuid(getStatus()->st_uid);\r
- if (passwd == NULL)\r
- buffer.appendInt(getStatus()->st_uid, "%4d");\r
- else\r
- addId(passwd->pw_name, 5, buffer);\r
- buffer.appendChar(' ');\r
- struct group* group = getgrgid(getStatus()->st_gid);\r
- if (group == NULL)\r
- buffer.appendInt(getStatus()->st_gid, "%4d");\r
- else\r
- addId(group->gr_name, 5, buffer);\r
- buffer.appendChar(' ');\r
- }\r
-#elif defined __WIN32__\r
- const char* name = fullName();\r
- HANDLE handle = INVALID_HANDLE_VALUE;\r
- if (! isDirectory()) {\r
- if ( (handle = CreateFile(name, GENERIC_READ, FILE_SHARE_READ, NULL,\r
- OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)\r
- m_logger->sayF(LOG_ERROR | CAT_FILE, LC_RIGHTS_AS_STRING_1,\r
- "CreateFile($1): $2").arg(name).arg((int) GetLastError()).end();\r
- } else if (m_getPrivilege) {\r
- // we try only one time:\r
- m_getPrivilege = false;\r
- if (getPrivilege(SE_BACKUP_NAME, m_logger)) {\r
- if ( (handle = CreateFile(name, 0, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,\r
- NULL)) != INVALID_HANDLE_VALUE)\r
- m_logger->sayF(LOG_ERROR | CAT_FILE, LC_RIGHTS_AS_STRING_2,\r
- "CreateFile($1): $2").arg(name).arg((int) GetLastError()).end();\r
- }\r
-\r
- }\r
- ReByteBuffer owner;\r
- if (handle != INVALID_HANDLE_VALUE)\r
- getFileOwner(handle, name, owner, m_logger);\r
- CloseHandle(handle);\r
- buffer.appendFix(owner.str(), owner.length(), ownerWidth, ownerWidth);\r
-#endif\r
- return buffer.str();\r
-}\r
-\r
-/**\r
- * Converts the unix time (time_t) to the file time.\r
- *\r
- * @param time the unix time (secondes since 1.1.1970)\r
- * @param filetime OUT: the OS specific filetime\r
- */\r
-void ReDirStatus_t::timeToFiletime(time_t time, ReFileTime_t& filetime) {\r
-#ifdef __linux__\r
- filetime.tv_sec = time;\r
- filetime.tv_nsec = 0;\r
-#elif defined __WIN32__\r
- LONGLONG ll = Int32x32To64(time, 10000000) + 116444736000000000;\r
- filetime.dwLowDateTime = (DWORD)ll;\r
- filetime.dwHighDateTime = ll >> 32;\r
-#endif\r
-}\r
-/**\r
- * Returns the type of the entry.\r
- * return the file type, e.g. TF_REGULAR\r
- */\r
-ReDirStatus_t::Type_t ReDirStatus_t::type() {\r
- Type_t rc = TF_UNDEF;\r
-#if defined __linux__\r
- int flags = getStatus()->st_mode;\r
- if (S_ISDIR(flags))\r
- rc = TF_SUBDIR;\r
- else if (flags == 0 || S_ISREG(flags))\r
- rc = TF_REGULAR;\r
- else if (S_ISLNK(flags))\r
- rc = TF_LINK;\r
- else if (S_ISCHR(flags))\r
- rc = TF_CHAR;\r
- else if (S_ISBLK(flags))\r
- rc = TF_BLOCK;\r
- else if (S_ISFIFO(flags))\r
- rc = TF_PIPE;\r
- else if (S_ISSOCK(flags))\r
- rc = TF_SOCKET;\r
- else\r
- rc = TF_OTHER;\r
-#elif defined __WIN32__\r
- int flags = (m_data.dwFileAttributes & ~(FILE_ATTRIBUTE_READONLY\r
- | FILE_ATTRIBUTE_HIDDEN\r
- | FILE_ATTRIBUTE_SYSTEM\r
- | FILE_ATTRIBUTE_ARCHIVE\r
- | FILE_ATTRIBUTE_NORMAL\r
- | FILE_ATTRIBUTE_TEMPORARY\r
- | FILE_ATTRIBUTE_SPARSE_FILE\r
- | FILE_ATTRIBUTE_COMPRESSED\r
- | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED\r
- | FILE_ATTRIBUTE_ENCRYPTED\r
- | FILE_ATTRIBUTE_HIDDEN));\r
-\r
- if (0 == flags)\r
- rc = TF_REGULAR;\r
- else if (0 != (m_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {\r
- rc = (0 != (m_data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))\r
- ? TF_LINK_DIR : TF_SUBDIR;\r
- } else if (0 != (m_data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))\r
- rc = TF_LINK;\r
- else\r
- rc = TF_OTHER;\r
-#endif\r
- return rc;\r
-}\r
-\r
-/**\r
- * Returns the filetype as a single character.\r
- *\r
- * @return the filetype, e.g. 'd' for a directory\r
- */\r
-char ReDirStatus_t::typeAsChar() {\r
- char rc = ' ';\r
- switch (type()) {\r
- case TF_REGULAR:\r
- rc = ' ';\r
- break;\r
- case TF_LINK:\r
- rc = 'l';\r
- break;\r
- case TF_SUBDIR:\r
- rc = 'd';\r
- break;\r
- case TF_CHAR:\r
- rc = 'c';\r
- break;\r
- case TF_BLOCK:\r
- rc = 'b';\r
- break;\r
- case TF_PIPE:\r
- rc = 'p';\r
- break;\r
- case TF_SOCKET:\r
- rc = 's';\r
- break;\r
- default:\r
- rc = 'o';\r
- break;\r
- }\r
- return rc;\r
-}\r
-/**\r
- * Constructor.\r
- */\r
-ReDirEntryFilter::ReDirEntryFilter() :\r
- ReSerializable(m_serialId),\r
- m_types(ReDirStatus_t::TC_ALL),\r
- m_nodePatterns(NULL),\r
- m_pathPatterns(NULL),\r
- m_minSize(0),\r
- m_maxSize(-1),\r
- //m_minAge(0),\r
- //m_maxAge(0),\r
- m_minDepth(0),\r
- m_maxDepth(512),\r
- m_allDirectories(false) {\r
- setFiletimeUndef(m_minAge);\r
- setFiletimeUndef(m_maxAge);\r
-}\r
-\r
-/**\r
- * Destructor.\r
- */\r
-ReDirEntryFilter::~ReDirEntryFilter() {\r
-}\r
-\r
-/**\r
- * Sets the members of the instance from a byte sequence.\r
- *\r
- * @param sequence IN/OUT: the serialized byte sequence\r
- * @param length INT/OUT the length of <code>sequence</code>\r
- */\r
-void ReDirEntryFilter::deserialize(uint8_t*& sequence, size_t& length){\r
- /*ReDirStatus_t::Type_t m_types;\r
- RePatternList* m_nodePatterns;\r
- RePatternList* m_pathPatterns;\r
- ReFileSize_t m_minSize;\r
- ReFileSize_t m_maxSize;\r
- ReFileTime_t m_minAge;\r
- ReFileTime_t m_maxAge;\r
- int m_minDepth;\r
- int m_maxDepth;\r
- bool m_allDirectories; */\r
-\r
-\r
-}\r
-/**\r
- * Packs the members into a byte sequence.\r
- *\r
- * @param sequence IN/OUT: the place for the byte sequence\r
- */\r
-ReByteBuffer& ReDirEntryFilter::serialize(ReByteBuffer& sequence){\r
- /*ReDirStatus_t::Type_t m_types;\r
- RePatternList* m_nodePatterns;\r
- RePatternList* m_pathPatterns;\r
- ReFileSize_t m_minSize;\r
- ReFileSize_t m_maxSize;\r
- ReFileTime_t m_minAge;\r
- ReFileTime_t m_maxAge;\r
- int m_minDepth;\r
- int m_maxDepth;\r
- bool m_allDirectories; */\r
- sequence.appendBits24(m_serialId);\r
- packString255(sequence, m_nodePatterns->patternString());\r
-\r
-}\r
-\r
-\r
-/**\r
- * Tests whether an entry matches the conditions of the filter.\r
- *\r
- * @param entry entry to test\r
- * @return <true>: the entry matches the conditions of the filter<br>\r
- * <false>: otherwise\r
- */\r
-bool ReDirEntryFilter::match(ReDirStatus_t& entry) {\r
- bool rc = false;\r
- do {\r
- if (m_allDirectories && entry.isDirectory()) {\r
- rc = true;\r
- break;\r
- }\r
- if (0 == (entry.type() & m_types))\r
- break;\r
- int64_t size = entry.fileSize();\r
- if (m_minSize > 0 && size < m_minSize)\r
- break;\r
- if (m_maxSize >= 0 && size > m_maxSize)\r
- break;\r
- if (!filetimeIsUndefined(m_minAge) && *entry.modified() > m_minAge)\r
- break;\r
- if (!filetimeIsUndefined(m_maxAge) && m_maxAge > *entry.modified())\r
- break;\r
- const char* node = entry.node();\r
- if (m_nodePatterns != NULL && !m_nodePatterns->match(node))\r
- break;\r
- rc = true;\r
- } while (false);\r
- return rc;\r
-}\r
-;\r
-\r
-#ifdef __linux__\r
-/**\r
- * Returns the status of the current file (lazy loading).\r
- *\r
- * @return the status of the current file\r
- */\r
-struct stat* ReDirStatus_t::getStatus() {\r
- if (m_status.st_ino == 0) {\r
- if (stat(fullName(), &m_status) != 0)\r
- m_status.st_ino = 0;\r
- }\r
- return &m_status;\r
-}\r
-#endif\r
-\r
-/**\r
- * Constructor.\r
- */\r
-ReDirTreeStatistic::ReDirTreeStatistic() :\r
- m_directories(0),\r
- m_files(0),\r
- m_sizes(0ll) {\r
-}\r
-/**\r
- * Builds a string describing the data.\r
- *\r
- * @param buffer IN/OUT: a buffer for the result\r
- * @param append <code>true</code>: the string will be appended to the buffer<br>\r
- * <code>false</code>: the buffer will be cleared at the beginning\r
- * @param formatFiles the <code>sprintf</code> format for the file count, e.g. "%8d"\r
- * @param formatSized the <code>sprintf</code> format for the MByte format, e.g. "%12.6f"\r
- * @param formatFiles the <code>sprintf</code> format for the directory count, e.g. "%6d"\r
- * @return a human readable string\r
- */\r
-const char* ReDirTreeStatistic::statisticAsString(ReByteBuffer& buffer,\r
- bool append, const char* formatFiles, const char* formatSizes,\r
- const char* formatDirs) {\r
- if (!append)\r
- buffer.setLength(0);\r
- buffer.appendInt(m_files, formatFiles);\r
- buffer.append(i18n("file(s)")).appendChar(' ');\r
- buffer.append(m_sizes / 1000.0 / 1000, formatSizes);\r
- buffer.append(" ", 1).append(i18n("MByte")).appendChar(' ');\r
- buffer.appendInt(m_directories, formatDirs);\r
- buffer.append(i18n("dirs(s)"));\r
- return buffer.str();\r
-}\r
-\r
-/**\r
- * Constructor.\r
- *\r
- * @param triggerCount efficiency: only every N calls a time check takes place\r
- * @param interval the minimum number of seconds between two traces\r
- */\r
-ReTraceUnit::ReTraceUnit(int triggerCount, int interval) :\r
- m_count(0),\r
- m_triggerCount(triggerCount),\r
- m_lastTrace(0),\r
- m_interval(interval),\r
- m_startTime(time(NULL)) {\r
- m_lastTrace = m_startTime;\r
-}\r
-/**\r
- * Destructor.\r
- */\r
-ReTraceUnit::~ReTraceUnit() {\r
-}\r
-\r
-/**\r
- * Prints a message.\r
- *\r
- * Often overwritten by a subclass.\r
- *\r
- * @param message message for the trace\r
- * @return <code>true</code> (for chaining)\r
- */\r
-bool ReTraceUnit::trace(const char* message) {\r
- printf("%s\n", message);\r
- return true;\r
-}\r
-\r
-/**\r
- * Constructor.\r
- *\r
- * @param base the base directory. The traversal starts at this point\r
- */\r
-ReTraverser::ReTraverser(const char* base, ReTraceUnit* tracer,\r
- ReLogger* logger) :\r
- ReDirTreeStatistic(),\r
- m_minLevel(0),\r
- m_maxLevel(512),\r
- m_level(-1),\r
- m_base(base),\r
- // m_dirs\r
- m_passNoForDirSearch(2),\r
- m_dirPatterns(NULL),\r
- m_tracer(tracer),\r
- m_logger(logger) {\r
- memset(m_dirs, 0, sizeof m_dirs);\r
- m_dirs[0] = new ReDirStatus_t(m_logger);\r
- // remove a preceeding "./". This simplifies the pattern expressions:\r
- if (m_base.startsWith(\r
- ReByteBuffer(".").appendChar(OS_SEPARATOR_CHAR).str())) {\r
- m_base.remove(0, 2);\r
- }\r
-}\r
-\r
-/**\r
- * Destructor.\r
- */\r
-ReTraverser::~ReTraverser() {\r
- destroy();\r
-}\r
-\r
-/**\r
- * Initializes the instance to process a new base.\r
- *\r
- * @param base the base directory to search\r
- */\r
-void ReTraverser::changeBase(const char* base) {\r
- destroy();\r
- m_base.setLength(0).append(base);\r
- memset(m_dirs, 0, sizeof m_dirs);\r
- m_dirs[0] = new ReDirStatus_t(m_logger);\r
- // remove a preceeding "./". This simplifies the pattern expressions:\r
- if (m_base.startsWith(\r
- ReByteBuffer(".").appendChar(OS_SEPARATOR_CHAR).str())) {\r
- m_base.remove(0, 2);\r
- }\r
-}\r
-\r
-/**\r
- * Releases the resources.\r
- */\r
-void ReTraverser::destroy() {\r
- for (size_t ix = 0; ix < sizeof m_dirs / sizeof m_dirs[0]; ix++) {\r
- if (m_dirs[ix] != NULL) {\r
- m_dirs[ix]->freeEntry();\r
- delete m_dirs[ix];\r
- m_dirs[ix] = NULL;\r
- }\r
- }\r
-}\r
-/**\r
- * Returns the info about the next file in the directory tree traversal.\r
- *\r
- * @param level OUT: the level relative to the base.<br>\r
- * 0 means the file is inside the base.<br>\r
- * Not defined if the result is NULL\r
- * @return NULL no more files<br>\r
- * otherwise: the stack entry with the next file in the\r
- * directory tree. May be a directory too\r
- */\r
-ReDirStatus_t* ReTraverser::rawNextFile(int& level) {\r
- ReDirStatus_t* rc = NULL;\r
- bool alreadyRead = false;\r
- bool again;\r
- do {\r
- again = false;\r
- if (m_level < 0) {\r
- // Not yet initialized?\r
- if (m_dirs[0]->m_passNo == 2)\r
- rc = NULL;\r
- else {\r
- // first call:\r
- if (initEntry(m_base.str(), NULL, 0)) {\r
- m_directories++;\r
- if (1 != m_passNoForDirSearch)\r
- rc = m_dirs[0];\r
- else\r
- again = alreadyRead = true;\r
- }\r
- }\r
- } else {\r
- ReDirStatus_t* current = m_dirs[m_level];\r
- if (alreadyRead || current->findNext()) {\r
- alreadyRead = false;\r
- // a file or directory found:\r
- if (m_tracer != NULL && m_tracer->isCountTriggered()\r
- && m_tracer->isTimeTriggered())\r
- m_tracer->trace(current->fullName());\r
- if (current->m_passNo != m_passNoForDirSearch) {\r
- // we search for any file:\r
- rc = m_dirs[m_level];\r
- } else {\r
- // we are interested only in true subdirectories:\r
- again = true;\r
- if (m_level < m_maxLevel && current->isDirectory()\r
- && !current->isDotDir() && !current->isLink()\r
- && (m_dirPatterns == NULL\r
- || isAllowedDir(current->node()))) {\r
- // open a new level\r
- alreadyRead = initEntry(current->m_path,\r
- current->node(), m_level + 1);\r
- m_directories++;\r
- }\r
- }\r
- } else {\r
- // the current subdir does not have more files:\r
- if (current->m_passNo == 1) {\r
- // we start the second pass:\r
- alreadyRead = initEntry(current->m_path, NULL, -1);\r
- current->m_passNo = 2;\r
- again = true;\r
- } else {\r
- // this subdirectory is complete. We continue in the parent directory:\r
- current->freeEntry();\r
- if (--m_level >= 0) {\r
- again = true;\r
- }\r
- }\r
- }\r
- }\r
- if (rc != NULL && rc->isDotDir())\r
- again = true;\r
- } while (again);\r
- if (rc != NULL && !rc->isDirectory()) {\r
- m_files++;\r
- if (m_sizes >= 0)\r
- m_sizes += rc->fileSize();\r
- }\r
- level = m_level;\r
- return rc;\r
-}\r
-/**\r
- * Returns the info about the next file matching the filter options.\r
- *\r
- * @param level OUT: the level relative to the base.<br>\r
- * 0 means the file is inside the base.<br>\r
- * Not defined if the result is NULL\r
- * @param filter NULL: every file matches<br>\r
- * otherwise: each found file must match this filter conditions\r
- * @return NULL no more files<br>\r
- * otherwise: the info about the next file in the\r
- * directory tree\r
- */\r
-ReDirStatus_t* ReTraverser::nextFile(int& level, ReDirEntryFilter* filter) {\r
- ReDirStatus_t* rc = rawNextFile(level);\r
- while (rc != NULL) {\r
- if (filter == NULL || filter->match(*rc)) {\r
- break;\r
- }\r
- rc = rawNextFile(level);\r
- }\r
- return rc;\r
-}\r
-\r
-/**\r
- * Initializes an entry in the directory entry stack.\r
- *\r
- * @param parent the parent directory of the entry\r
- * @param node the name of the directory belonging to the entry (without path)\r
- * @param level the index of the entry in the stack.<br>\r
- * If < 0: m_levels and m_path will not be changed\r
- * @return <code>true</code>: a new file is available<br>\r
- * <cude>false/code>: findFirstEntry() signals: no entry.\r
- */\r
-bool ReTraverser::initEntry(const ReByteBuffer& parent, const char* node,\r
- int level) {\r
- bool rc = false;\r
- if (level < MAX_ENTRY_STACK_DEPTH) {\r
- if (level >= 0)\r
- m_level = level;\r
- if (m_dirs[m_level] == NULL)\r
- m_dirs[m_level] = new ReDirStatus_t(m_logger);\r
- ReDirStatus_t* current = m_dirs[m_level];\r
- current->m_passNo = 1;\r
- if (level >= 0) {\r
- current->m_path.set(parent.str(), parent.length());\r
- if (!parent.endsWith(OS_SEPARATOR))\r
- current->m_path.append(OS_SEPARATOR);\r
- if (node != NULL)\r
- current->m_path.append(node).append(OS_SEPARATOR);\r
- }\r
- rc = current->findFirst();\r
- }\r
- return rc;\r
-}\r
-\r
-/**\r
- * Sets some properties from a filter.\r
- *\r
- * @param filter the filter with the properties to set\r
- */\r
-void ReTraverser::setPropertiesFromFilter(ReDirEntryFilter* filter) {\r
- m_minLevel = filter->m_minDepth;\r
- m_maxLevel = filter->m_maxDepth;\r
- setDirPattern(filter->m_pathPatterns);\r
-}\r
-\r
-/**\r
- * Returns the info of an entry the directory stack.\r
- *\r
- * @param offsetFromTop 0: return the top of stack<br>\r
- * 1: returns the entry one below the top<br>\r
- * 2: ...\r
- * @return NULL: not available<br>\r
- * otherwise: the wanted entry\r
- */\r
-ReDirStatus_t* ReTraverser::topOfStack(int offsetFromTop) {\r
- ReDirStatus_t* rc = NULL;\r
- if (offsetFromTop >= 0 && m_level - 1 - offsetFromTop >= 0)\r
- rc = m_dirs[m_level - 1 - offsetFromTop];\r
- return rc;\r
-}\r
+/*
+ * ReTraverser.cpp
+ *
+ * License: Public Domain
+ * You can use and modify this file without any restriction.
+ * Do what you want.
+ * No warranties and disclaimer of any damages.
+ * You also can use this license: http://www.wtfpl.net
+ * The latest sources: https://github.com/republib
+ */
+
+#include "base/rebase.hpp"
+#include "os/reos.hpp"
+#if defined __WIN32__
+#include "accctrl.h"
+#include "aclapi.h"
+#pragma comment(lib, "advapi32.lib")
+#endif
+
+enum RELOC_TRAVERSER {
+ LC_RIGHTS_AS_STRING_1 = LOC_FIRST_OF(LOC_TRAVERSER), // 50401
+ LC_RIGHTS_AS_STRING_2, // 50402
+ LC_RIGHTS_AS_STRING_3, // 50403
+ LC_GET_PRIVILEGE_1, // 50404
+ LC_GET_PRIVILEGE_2, // 50405
+ LC_GET_PRIVILEGE_3, // 50406
+ LC_GET_FILE_OWNER_1, // 50407
+ LC_GET_FILE_OWNER_2, // 50408
+};
+
+int ReDirEntryFilter::m_serialId = buildSerialId(CLASSID_DIR_ENTRY_FILTER, 1);
+
+/**
+ * Constructor.
+ */
+ReDirStatus_t::ReDirStatus_t(ReLogger* logger) :
+ m_path(),
+ m_fullName(),
+ m_passNo(0),
+ m_logger(logger),
+#ifdef __linux__
+ m_handle(NULL),
+ m_data(NULL)
+//m_status;
+#elif defined WIN32
+m_handle(INVALID_HANDLE_VALUE),
+//m_data;
+m_getPrivilege(true)
+#endif
+{
+#ifdef __linux__
+ memset(&m_status, 0, sizeof m_status);
+#elif defined WIN32
+ memset(&m_data, 0, sizeof m_data);
+#endif
+}
+
+/**
+ * Returns the last access time.
+ *
+ * @return the last access time
+ */
+const ReFileTime_t* ReDirStatus_t::accessed() {
+#ifdef __linux__
+ return &(getStatus()->st_atim);
+#elif defined __WIN32__
+ return &m_data.ftLastAccessTime;
+#endif
+}
+
+/**
+ * Returns the filesize.
+ *
+ * @return the filesize
+ */
+ReFileSize_t ReDirStatus_t::fileSize() {
+#ifdef __linux__
+ return getStatus()->st_size;
+#elif defined __WIN32__
+ return ((int64_t) m_data.nFileSizeHigh << 32) + m_data.nFileSizeLow;
+#endif
+}
+
+/**
+ * Returns the file time as a string.
+ *
+ * @param buffer OUT: the file time
+ * @return <code>buffer.str()</code> (for chaining)
+ */
+const char* ReDirStatus_t::filetimeAsString(ReByteBuffer& buffer) {
+ return filetimeToString(modified(), buffer);
+}
+
+/**
+ * Converts a filetime to a string.
+ *
+ * @param time the filetime to convert
+ * @param buffer OUT: the buffer for the string
+ * @return <code>buffer.str()</code>, e.g. "2014.01.07 02:59:43"
+ */
+const char* ReDirStatus_t::filetimeToString(const ReFileTime_t* time,
+ ReByteBuffer& buffer) {
+ time_t time1 = filetimeToTime(time);
+ struct tm* time2 = localtime(&time1);
+ buffer.setLength(4 + 2 * 2 + 2 * 2 + 1 + 3 * 2 + 2 * 1);
+ strftime(buffer.buffer(), buffer.length(), "%Y.%m.%d %H:%M:%S", time2);
+ return buffer.str();
+}
+
+/**
+ * Converts a filetime to a unix time (seconds since the Epoche).
+ *
+ * @param filetime the filetime to convert
+ * @return the count of seconds since 1.1.1970
+ */
+time_t ReDirStatus_t::filetimeToTime(const ReFileTime_t* filetime) {
+#ifdef __linux__
+ return filetime->tv_sec;
+#elif defined __WIN32__
+ // 64-bit arithmetic:
+ LARGE_INTEGER date, adjust;
+ date.HighPart = filetime->dwHighDateTime;
+ date.LowPart = filetime->dwLowDateTime;
+ // 100-nanoseconds = milliseconds * 10000
+ adjust.QuadPart = 11644473600000 * 10000;
+ // removes the diff between 1970 and 1601
+ date.QuadPart -= adjust.QuadPart;
+ // converts back from 100-nanoseconds to seconds
+ time_t rc = (time_t) (date.QuadPart / 10000000);
+ return rc;
+#endif
+}
+
+/**
+ * Loads the info about the first file into the instance.
+ *
+ * @return <code>true</code>: success
+ */
+bool ReDirStatus_t::findFirst() {
+ bool rc = false;
+#if defined __linux__
+ if (m_handle != NULL)
+ closedir(m_handle);
+ m_handle = opendir(m_path.str());
+ rc = m_handle != NULL && (m_data = readdir(m_handle)) != NULL;
+ m_status.st_ino = 0;
+#elif defined __WIN32__
+ if (m_handle != INVALID_HANDLE_VALUE)
+ FindClose(m_handle);
+ ReByteBuffer thePath(m_path);
+ thePath.append(m_path.lastChar() == '\\' ? "*" : "\\*");
+ m_handle = FindFirstFileA(thePath.str(), &m_data);
+ rc = m_handle != INVALID_HANDLE_VALUE;
+#endif
+ m_fullName.setLength(0);
+ return rc;
+}
+
+/**
+ * Loads the info about the next file into the instance.
+ *
+ * @return <code>true</code>: success
+ */
+bool ReDirStatus_t::findNext() {
+#if defined __linux__
+ bool rc = m_handle != NULL && (m_data = readdir(m_handle)) != NULL;
+ m_status.st_ino = 0;
+#elif defined __WIN32__
+ bool rc = m_handle != INVALID_HANDLE_VALUE && FindNextFileA(m_handle, &m_data);
+#endif
+ m_fullName.setLength(0);
+ return rc;
+}
+
+/**
+ * Frees the resources of an instance.
+ */
+void ReDirStatus_t::freeEntry() {
+#if defined __linux__
+ if (m_handle != NULL) {
+ closedir(m_handle);
+ m_handle = NULL;
+ }
+#elif defined __WIN32__
+ if (m_handle != INVALID_HANDLE_VALUE) {
+ FindClose(m_handle);
+ m_handle = INVALID_HANDLE_VALUE;
+ }
+#endif
+ m_path.setLength(0);
+ m_fullName.setLength(0);
+}
+
+/**
+ * Returns the full filename (with path).
+ *
+ * @return the filename with path
+ */
+const char* ReDirStatus_t::fullName() {
+ if (m_fullName.length() == 0)
+ m_fullName.set(m_path).append(node(), -1);
+ return m_fullName.str();
+}
+
+#if defined __WIN32__
+/** Gets the name of the file owner.
+ *
+ * @param handle file handle (see <code>CreateFile()</code>)
+ * @param name OUT: the owner: [domain\\]name
+ * @return <code>true</code>: success
+ */
+bool ReDirStatus_t::getFileOwner(HANDLE handle, const char* file,
+ ReByteBuffer& name, ReLogger* logger) {
+ bool rc = false;
+ PSID pSidOwner = NULL;
+ PSECURITY_DESCRIPTOR pSD = NULL;
+ if (GetSecurityInfo(handle, SE_FILE_OBJECT,
+ OWNER_SECURITY_INFORMATION, &pSidOwner, NULL, NULL, NULL, &pSD) != ERROR_SUCCESS) {
+ if (logger != NULL)
+ logger->sayF(LOG_ERROR | CAT_FILE, LC_GET_FILE_OWNER_1,
+ "GetSecurityInfo($1): $2").arg(file).arg((int) GetLastError()).end();
+ } else {
+ char accountName[128];
+ char domainName[128];
+ DWORD dwAcctName = sizeof accountName;
+ DWORD dwDomainName = sizeof domainName;
+ SID_NAME_USE eUse = SidTypeUnknown;
+ if (! LookupAccountSid(NULL, pSidOwner, accountName, &dwAcctName, domainName,
+ &dwDomainName, &eUse)) {
+ if (logger != NULL)
+ logger->sayF(LOG_ERROR | CAT_SECURITY, LC_GET_FILE_OWNER_2,
+ "LookupAccountSid(): $1").arg((int) GetLastError()).end();
+ } else {
+ if (dwDomainName > 0)
+ name.append(domainName).appendChar('\\');
+ name.append(accountName);
+ rc = true;
+ }
+ }
+ return rc;
+}
+#endif /* __WIN32__ */
+
+#if defined __WIN32__
+/** Tries to get a privilege.
+ *
+ * @param privilege the name of the privilege, e.g. "SeBackup"
+ * @param logger logger for error logging
+ */
+bool ReDirStatus_t::getPrivilege(const char* privilege, ReLogger* logger) {
+ bool rc = false;
+ LUID luidPrivilege;
+ HANDLE hAccessToken;
+ if (! OpenProcessToken (GetCurrentProcess(),
+ TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hAccessToken)) {
+ if (logger != NULL)
+ logger->sayF(LOG_ERROR | CAT_FILE, LC_GET_PRIVILEGE_1,
+ "OpenProcessToken(): $1").arg((int) GetLastError()).end();
+ } else if (! LookupPrivilegeValue (NULL, SE_BACKUP_NAME, &luidPrivilege)) {
+ if (logger != NULL)
+ logger->sayF(LOG_ERROR | CAT_FILE, LC_GET_PRIVILEGE_2,
+ "LookupPrivilegeValue(): $1").arg((int) GetLastError()).end();
+ } else {
+ TOKEN_PRIVILEGES tpPrivileges;
+ tpPrivileges.PrivilegeCount = 1;
+ tpPrivileges.Privileges[0].Luid = luidPrivilege;
+ tpPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+ if (AdjustTokenPrivileges (hAccessToken, FALSE, &tpPrivileges,
+ 0, NULL, NULL) == 0)
+ rc = true;
+ else {
+ int error = GetLastError();
+ if (error != 1300 && logger != NULL)
+ logger->sayF(LOG_ERROR | CAT_FILE, LC_GET_PRIVILEGE_3,
+ "AdjustTokenPrivileges(): $1").arg((int) GetLastError()).end();
+ }
+ }
+ return rc;
+}
+#endif /* __WIN32__ */
+
+/**
+ * Tests whether the instance is a directory.
+ *
+ * @return <code>true</code>: instance contains the data of a directory
+ */
+bool ReDirStatus_t::isDirectory() {
+#ifdef __linux__
+ return m_data->d_type == DT_DIR
+ || (m_data->d_type == DT_UNKNOWN && S_ISDIR(getStatus()->st_mode));
+#elif defined __WIN32__
+ return 0 != (m_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
+#endif
+}
+
+/**
+ * Tests whether the instance contains data about "." or "..".
+ *
+ * @return <code>true</code>: an ignorable entry has been found
+ */
+bool ReDirStatus_t::isDotDir() const {
+#ifdef __linux__
+ bool rc = m_data == NULL
+ || (m_data->d_name[0] == '.'
+ && (m_data->d_name[1] == '\0'
+ || (m_data->d_name[1] == '.' && m_data->d_name[2] == '\0')));
+#elif defined __WIN32__
+ bool rc = m_data.cFileName[0] == '.' && (m_data.cFileName[1] == '\0'
+ || (m_data.cFileName[1] == '.' && m_data.cFileName[2] == '\0'));
+#endif
+ return rc;
+}
+
+/**
+ * Tests whether the instance is a symbolic link.
+ *
+ * Unter windows it tests whether the the instance is a reparse point.
+ *
+ * @return <code>true</code>: instance contains the data of a link
+ */
+bool ReDirStatus_t::isLink() {
+ bool rc;
+#ifdef __linux__
+ rc = m_data->d_type == DT_LNK
+ || (m_data->d_type == DT_UNKNOWN && S_ISLNK(getStatus()->st_mode));
+#elif defined __WIN32__
+ rc = 0 != (m_data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT);
+#endif
+ return rc;
+}
+/**
+ * Tests whether the instance is a "normal" file.
+ *
+ * @return <code>true</code>: instance contains the data of a not special file
+ */
+bool ReDirStatus_t::isRegular() {
+#ifdef __linux__
+ return m_data->d_type == DT_REG
+ || (m_data->d_type == DT_UNKNOWN && S_ISREG(getStatus()->st_mode));
+#elif defined __WIN32__
+ return 0 == (m_data.dwFileAttributes & (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE));
+#endif
+}
+/**
+ * Returns the modification time.
+ *
+ * @return the modification time
+ */
+const ReFileTime_t* ReDirStatus_t::modified() {
+#ifdef __linux__
+ return &(getStatus()->st_mtim);
+#elif defined __WIN32__
+ return &m_data.ftLastWriteTime;
+#endif
+}
+
+/**
+ * Returns the name of the current file (without path).
+ *
+ * @return the name of the current file.
+ */
+const char* ReDirStatus_t::node() const {
+#ifdef __linux__
+ return m_data->d_name;
+#elif defined __WIN32__
+ return m_data.cFileName;
+#endif
+}
+
+inline void addRight(int mode, ReByteBuffer& buffer) {
+ char right;
+ switch (mode & 7) {
+ case 1:
+ right = 'x';
+ break;
+ case 2:
+ right = 'w';
+ break;
+ case 3:
+ right = 'X';
+ break;
+ case 4:
+ right = 'r';
+ break;
+ case 5:
+ right = 'R';
+ break;
+ case 6:
+ right = 'W';
+ break;
+ case 7:
+ right = 'A';
+ break;
+ default:
+ right = '-';
+ break;
+ }
+ buffer.appendChar(right);
+}
+inline void addId(const char* id, int maxLength, ReByteBuffer& buffer) {
+ int length = strlen(id);
+ if (length == maxLength)
+ buffer.append(id, length);
+ else if (length < maxLength)
+ buffer.append(id, length).appendChar(' ', maxLength - length);
+ else {
+ buffer.append(id, 2);
+ buffer.append(id + length - maxLength - 2, maxLength - 2);
+ }
+}
+/**
+ * Returns the file rights as a string.
+ *
+ * @param buffer OUT: the file rights
+ * @param numerical <code>true</code>: the owner/group should be numerical (UID/GID)
+ * @param ownerWidth the width for group/owner
+ * @return <code>buffer.str()</code> (for chaining)
+ */
+const char* ReDirStatus_t::rightsAsString(ReByteBuffer& buffer, bool numerical,
+ int ownerWidth) {
+ buffer.setLength(0);
+#if defined __linux__
+ if (numerical) {
+ buffer.appendInt(getStatus()->st_mode & ALLPERMS, "%04o");
+ buffer.appendInt(getStatus()->st_uid, " %4d");
+ buffer.appendInt(getStatus()->st_gid, " %4d");
+ } else {
+ int mode = getStatus()->st_mode & ALLPERMS;
+ addRight(mode >> 6, buffer);
+ addRight(mode >> 3, buffer);
+ addRight(mode, buffer);
+ buffer.appendChar(' ');
+ struct passwd* passwd = getpwuid(getStatus()->st_uid);
+ if (passwd == NULL)
+ buffer.appendInt(getStatus()->st_uid, "%4d");
+ else
+ addId(passwd->pw_name, 5, buffer);
+ buffer.appendChar(' ');
+ struct group* group = getgrgid(getStatus()->st_gid);
+ if (group == NULL)
+ buffer.appendInt(getStatus()->st_gid, "%4d");
+ else
+ addId(group->gr_name, 5, buffer);
+ buffer.appendChar(' ');
+ }
+#elif defined __WIN32__
+ const char* name = fullName();
+ HANDLE handle = INVALID_HANDLE_VALUE;
+ if (! isDirectory()) {
+ if ( (handle = CreateFile(name, GENERIC_READ, FILE_SHARE_READ, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
+ m_logger->sayF(LOG_ERROR | CAT_FILE, LC_RIGHTS_AS_STRING_1,
+ "CreateFile($1): $2").arg(name).arg((int) GetLastError()).end();
+ } else if (m_getPrivilege) {
+ // we try only one time:
+ m_getPrivilege = false;
+ if (getPrivilege(SE_BACKUP_NAME, m_logger)) {
+ if ( (handle = CreateFile(name, 0, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
+ NULL)) != INVALID_HANDLE_VALUE)
+ m_logger->sayF(LOG_ERROR | CAT_FILE, LC_RIGHTS_AS_STRING_2,
+ "CreateFile($1): $2").arg(name).arg((int) GetLastError()).end();
+ }
+
+ }
+ ReByteBuffer owner;
+ if (handle != INVALID_HANDLE_VALUE)
+ getFileOwner(handle, name, owner, m_logger);
+ CloseHandle(handle);
+ buffer.appendFix(owner.str(), owner.length(), ownerWidth, ownerWidth);
+#endif
+ return buffer.str();
+}
+
+/**
+ * Converts the unix time (time_t) to the file time.
+ *
+ * @param time the unix time (secondes since 1.1.1970)
+ * @param filetime OUT: the OS specific filetime
+ */
+void ReDirStatus_t::timeToFiletime(time_t time, ReFileTime_t& filetime) {
+#ifdef __linux__
+ filetime.tv_sec = time;
+ filetime.tv_nsec = 0;
+#elif defined __WIN32__
+ LONGLONG ll = Int32x32To64(time, 10000000) + 116444736000000000;
+ filetime.dwLowDateTime = (DWORD)ll;
+ filetime.dwHighDateTime = ll >> 32;
+#endif
+}
+/**
+ * Returns the type of the entry.
+ * return the file type, e.g. TF_REGULAR
+ */
+ReDirStatus_t::Type_t ReDirStatus_t::type() {
+ Type_t rc = TF_UNDEF;
+#if defined __linux__
+ int flags = getStatus()->st_mode;
+ if (S_ISDIR(flags))
+ rc = TF_SUBDIR;
+ else if (flags == 0 || S_ISREG(flags))
+ rc = TF_REGULAR;
+ else if (S_ISLNK(flags))
+ rc = TF_LINK;
+ else if (S_ISCHR(flags))
+ rc = TF_CHAR;
+ else if (S_ISBLK(flags))
+ rc = TF_BLOCK;
+ else if (S_ISFIFO(flags))
+ rc = TF_PIPE;
+ else if (S_ISSOCK(flags))
+ rc = TF_SOCKET;
+ else
+ rc = TF_OTHER;
+#elif defined __WIN32__
+ int flags = (m_data.dwFileAttributes & ~(FILE_ATTRIBUTE_READONLY
+ | FILE_ATTRIBUTE_HIDDEN
+ | FILE_ATTRIBUTE_SYSTEM
+ | FILE_ATTRIBUTE_ARCHIVE
+ | FILE_ATTRIBUTE_NORMAL
+ | FILE_ATTRIBUTE_TEMPORARY
+ | FILE_ATTRIBUTE_SPARSE_FILE
+ | FILE_ATTRIBUTE_COMPRESSED
+ | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED
+ | FILE_ATTRIBUTE_ENCRYPTED
+ | FILE_ATTRIBUTE_HIDDEN));
+
+ if (0 == flags)
+ rc = TF_REGULAR;
+ else if (0 != (m_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
+ rc = (0 != (m_data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
+ ? TF_LINK_DIR : TF_SUBDIR;
+ } else if (0 != (m_data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
+ rc = TF_LINK;
+ else
+ rc = TF_OTHER;
+#endif
+ return rc;
+}
+
+/**
+ * Returns the filetype as a single character.
+ *
+ * @return the filetype, e.g. 'd' for a directory
+ */
+char ReDirStatus_t::typeAsChar() {
+ char rc = ' ';
+ switch (type()) {
+ case TF_REGULAR:
+ rc = ' ';
+ break;
+ case TF_LINK:
+ rc = 'l';
+ break;
+ case TF_SUBDIR:
+ rc = 'd';
+ break;
+ case TF_CHAR:
+ rc = 'c';
+ break;
+ case TF_BLOCK:
+ rc = 'b';
+ break;
+ case TF_PIPE:
+ rc = 'p';
+ break;
+ case TF_SOCKET:
+ rc = 's';
+ break;
+ default:
+ rc = 'o';
+ break;
+ }
+ return rc;
+}
+/**
+ * Constructor.
+ */
+ReDirEntryFilter::ReDirEntryFilter() :
+ ReSerializable(m_serialId),
+ m_types(ReDirStatus_t::TC_ALL),
+ m_nodePatterns(NULL),
+ m_pathPatterns(NULL),
+ m_minSize(0),
+ m_maxSize(-1),
+ //m_minAge(0),
+ //m_maxAge(0),
+ m_minDepth(0),
+ m_maxDepth(512),
+ m_allDirectories(false) {
+ setFiletimeUndef(m_minAge);
+ setFiletimeUndef(m_maxAge);
+}
+
+/**
+ * Destructor.
+ */
+ReDirEntryFilter::~ReDirEntryFilter() {
+}
+
+/**
+ * Sets the members of the instance from a byte sequence.
+ *
+ * @param sequence IN/OUT: the serialized byte sequence
+ * @param length INT/OUT the length of <code>sequence</code>
+ */
+void ReDirEntryFilter::deserialize(const uint8_t*& sequence, size_t& length) {
+ int id;
+ unpackInt24(sequence, length, id);
+ if (id != m_serialId)
+ throw ReSerializeFormatException("wrong serialId", this);
+ ReByteBuffer buffer;
+ unpackString64k(sequence, length, buffer);
+ bool ignoreCase;
+ unpackBool(sequence, length, ignoreCase);
+ m_nodePatterns->set(buffer.str(), ignoreCase);
+ unpackString64k(sequence, length, buffer);
+ unpackBool(sequence, length, ignoreCase);
+ unpackString64k(sequence, length, buffer);
+ m_pathPatterns->set(buffer.str(), ignoreCase);
+ unpackInt64(sequence, length, m_minSize);
+ unpackInt64(sequence, length, m_maxSize);
+ int64_t value;
+ unpackInt64(sequence, length, value);
+ unpackBool(sequence, length, m_allDirectories);
+}
+/**
+ * Packs the members into a byte sequence.
+ *
+ * @param sequence IN/OUT: the place for the byte sequence
+ */
+ReByteBuffer& ReDirEntryFilter::serialize(ReByteBuffer& sequence) {
+ sequence.appendBits24(m_serialId);
+ packString64k(sequence, m_nodePatterns->patternString());
+ packBool(sequence, m_nodePatterns->ignoreCase());
+ packString64k(sequence, m_pathPatterns->patternString());
+ packBool(sequence, m_pathPatterns->ignoreCase());
+ sequence.appendBits64(m_minSize);
+ sequence.appendBits64(m_maxSize);
+ uint64_t value;
+#if defined __linux__
+ value = (m_minAge.tv_sec << 32) + m_minAge.tv_nsec;
+ sequence.appendBits64(int64_t(value));
+ value = (m_minAge.tv_sec << 32) + m_minAge.tv_nsec;
+#elif defined __WIN32__
+#error "missing impl"
+#endif
+ sequence.appendBits64(int64_t(value));
+ packBool(sequence, m_allDirectories);
+}
+
+/**
+ * Tests whether an entry matches the conditions of the filter.
+ *
+ * @param entry entry to test
+ * @return <true>: the entry matches the conditions of the filter<br>
+ * <false>: otherwise
+ */
+bool ReDirEntryFilter::match(ReDirStatus_t& entry) {
+ bool rc = false;
+ do {
+ if (m_allDirectories && entry.isDirectory()) {
+ rc = true;
+ break;
+ }
+ if (0 == (entry.type() & m_types))
+ break;
+ int64_t size = entry.fileSize();
+ if (m_minSize > 0 && size < m_minSize)
+ break;
+ if (m_maxSize >= 0 && size > m_maxSize)
+ break;
+ if (!filetimeIsUndefined(m_minAge) && *entry.modified() > m_minAge)
+ break;
+ if (!filetimeIsUndefined(m_maxAge) && m_maxAge > *entry.modified())
+ break;
+ const char* node = entry.node();
+ if (m_nodePatterns != NULL && !m_nodePatterns->match(node))
+ break;
+ rc = true;
+ } while (false);
+ return rc;
+}
+;
+
+#ifdef __linux__
+/**
+ * Returns the status of the current file (lazy loading).
+ *
+ * @return the status of the current file
+ */
+struct stat* ReDirStatus_t::getStatus() {
+ if (m_status.st_ino == 0) {
+ if (stat(fullName(), &m_status) != 0)
+ m_status.st_ino = 0;
+ }
+ return &m_status;
+}
+#endif
+
+/**
+ * Constructor.
+ */
+ReDirTreeStatistic::ReDirTreeStatistic() :
+ m_directories(0),
+ m_files(0),
+ m_sizes(0ll) {
+}
+/**
+ * Builds a string describing the data.
+ *
+ * @param buffer IN/OUT: a buffer for the result
+ * @param append <code>true</code>: the string will be appended to the buffer<br>
+ * <code>false</code>: the buffer will be cleared at the beginning
+ * @param formatFiles the <code>sprintf</code> format for the file count, e.g. "%8d"
+ * @param formatSized the <code>sprintf</code> format for the MByte format, e.g. "%12.6f"
+ * @param formatFiles the <code>sprintf</code> format for the directory count, e.g. "%6d"
+ * @return a human readable string
+ */
+const char* ReDirTreeStatistic::statisticAsString(ReByteBuffer& buffer,
+ bool append, const char* formatFiles, const char* formatSizes,
+ const char* formatDirs) {
+ if (!append)
+ buffer.setLength(0);
+ buffer.appendInt(m_files, formatFiles);
+ buffer.append(i18n("file(s)")).appendChar(' ');
+ buffer.append(m_sizes / 1000.0 / 1000, formatSizes);
+ buffer.append(" ", 1).append(i18n("MByte")).appendChar(' ');
+ buffer.appendInt(m_directories, formatDirs);
+ buffer.append(i18n("dirs(s)"));
+ return buffer.str();
+}
+
+/**
+ * Constructor.
+ *
+ * @param triggerCount efficiency: only every N calls a time check takes place
+ * @param interval the minimum number of seconds between two traces
+ */
+ReTraceUnit::ReTraceUnit(int triggerCount, int interval) :
+ m_count(0),
+ m_triggerCount(triggerCount),
+ m_lastTrace(0),
+ m_interval(interval),
+ m_startTime(time(NULL)) {
+ m_lastTrace = m_startTime;
+}
+/**
+ * Destructor.
+ */
+ReTraceUnit::~ReTraceUnit() {
+}
+
+/**
+ * Prints a message.
+ *
+ * Often overwritten by a subclass.
+ *
+ * @param message message for the trace
+ * @return <code>true</code> (for chaining)
+ */
+bool ReTraceUnit::trace(const char* message) {
+ printf("%s\n", message);
+ return true;
+}
+
+/**
+ * Constructor.
+ *
+ * @param base the base directory. The traversal starts at this point
+ */
+ReTraverser::ReTraverser(const char* base, ReTraceUnit* tracer,
+ ReLogger* logger) :
+ ReDirTreeStatistic(),
+ m_minLevel(0),
+ m_maxLevel(512),
+ m_level(-1),
+ m_base(base),
+ // m_dirs
+ m_passNoForDirSearch(2),
+ m_dirPatterns(NULL),
+ m_tracer(tracer),
+ m_logger(logger) {
+ memset(m_dirs, 0, sizeof m_dirs);
+ m_dirs[0] = new ReDirStatus_t(m_logger);
+ // remove a preceeding "./". This simplifies the pattern expressions:
+ if (m_base.startsWith(
+ ReByteBuffer(".").appendChar(OS_SEPARATOR_CHAR).str())) {
+ m_base.remove(0, 2);
+ }
+}
+
+/**
+ * Destructor.
+ */
+ReTraverser::~ReTraverser() {
+ destroy();
+}
+
+/**
+ * Initializes the instance to process a new base.
+ *
+ * @param base the base directory to search
+ */
+void ReTraverser::changeBase(const char* base) {
+ destroy();
+ m_base.setLength(0).append(base);
+ memset(m_dirs, 0, sizeof m_dirs);
+ m_dirs[0] = new ReDirStatus_t(m_logger);
+ // remove a preceeding "./". This simplifies the pattern expressions:
+ if (m_base.startsWith(
+ ReByteBuffer(".").appendChar(OS_SEPARATOR_CHAR).str())) {
+ m_base.remove(0, 2);
+ }
+}
+
+/**
+ * Releases the resources.
+ */
+void ReTraverser::destroy() {
+ for (size_t ix = 0; ix < sizeof m_dirs / sizeof m_dirs[0]; ix++) {
+ if (m_dirs[ix] != NULL) {
+ m_dirs[ix]->freeEntry();
+ delete m_dirs[ix];
+ m_dirs[ix] = NULL;
+ }
+ }
+}
+/**
+ * Returns the info about the next file in the directory tree traversal.
+ *
+ * @param level OUT: the level relative to the base.<br>
+ * 0 means the file is inside the base.<br>
+ * Not defined if the result is NULL
+ * @return NULL no more files<br>
+ * otherwise: the stack entry with the next file in the
+ * directory tree. May be a directory too
+ */
+ReDirStatus_t* ReTraverser::rawNextFile(int& level) {
+ ReDirStatus_t* rc = NULL;
+ bool alreadyRead = false;
+ bool again;
+ do {
+ again = false;
+ if (m_level < 0) {
+ // Not yet initialized?
+ if (m_dirs[0]->m_passNo == 2)
+ rc = NULL;
+ else {
+ // first call:
+ if (initEntry(m_base.str(), NULL, 0)) {
+ m_directories++;
+ if (1 != m_passNoForDirSearch)
+ rc = m_dirs[0];
+ else
+ again = alreadyRead = true;
+ }
+ }
+ } else {
+ ReDirStatus_t* current = m_dirs[m_level];
+ if (alreadyRead || current->findNext()) {
+ alreadyRead = false;
+ // a file or directory found:
+ if (m_tracer != NULL && m_tracer->isCountTriggered()
+ && m_tracer->isTimeTriggered())
+ m_tracer->trace(current->fullName());
+ if (current->m_passNo != m_passNoForDirSearch) {
+ // we search for any file:
+ rc = m_dirs[m_level];
+ } else {
+ // we are interested only in true subdirectories:
+ again = true;
+ if (m_level < m_maxLevel && current->isDirectory()
+ && !current->isDotDir() && !current->isLink()
+ && (m_dirPatterns == NULL
+ || isAllowedDir(current->node()))) {
+ // open a new level
+ alreadyRead = initEntry(current->m_path,
+ current->node(), m_level + 1);
+ m_directories++;
+ }
+ }
+ } else {
+ // the current subdir does not have more files:
+ if (current->m_passNo == 1) {
+ // we start the second pass:
+ alreadyRead = initEntry(current->m_path, NULL, -1);
+ current->m_passNo = 2;
+ again = true;
+ } else {
+ // this subdirectory is complete. We continue in the parent directory:
+ current->freeEntry();
+ if (--m_level >= 0) {
+ again = true;
+ }
+ }
+ }
+ }
+ if (rc != NULL && rc->isDotDir())
+ again = true;
+ } while (again);
+ if (rc != NULL && !rc->isDirectory()) {
+ m_files++;
+ if (m_sizes >= 0)
+ m_sizes += rc->fileSize();
+ }
+ level = m_level;
+ return rc;
+}
+/**
+ * Returns the info about the next file matching the filter options.
+ *
+ * @param level OUT: the level relative to the base.<br>
+ * 0 means the file is inside the base.<br>
+ * Not defined if the result is NULL
+ * @param filter NULL: every file matches<br>
+ * otherwise: each found file must match this filter conditions
+ * @return NULL no more files<br>
+ * otherwise: the info about the next file in the
+ * directory tree
+ */
+ReDirStatus_t* ReTraverser::nextFile(int& level, ReDirEntryFilter* filter) {
+ ReDirStatus_t* rc = rawNextFile(level);
+ while (rc != NULL) {
+ if (filter == NULL || filter->match(*rc)) {
+ break;
+ }
+ rc = rawNextFile(level);
+ }
+ return rc;
+}
+
+/**
+ * Initializes an entry in the directory entry stack.
+ *
+ * @param parent the parent directory of the entry
+ * @param node the name of the directory belonging to the entry (without path)
+ * @param level the index of the entry in the stack.<br>
+ * If < 0: m_levels and m_path will not be changed
+ * @return <code>true</code>: a new file is available<br>
+ * <cude>false/code>: findFirstEntry() signals: no entry.
+ */
+bool ReTraverser::initEntry(const ReByteBuffer& parent, const char* node,
+ int level) {
+ bool rc = false;
+ if (level < MAX_ENTRY_STACK_DEPTH) {
+ if (level >= 0)
+ m_level = level;
+ if (m_dirs[m_level] == NULL)
+ m_dirs[m_level] = new ReDirStatus_t(m_logger);
+ ReDirStatus_t* current = m_dirs[m_level];
+ current->m_passNo = 1;
+ if (level >= 0) {
+ current->m_path.set(parent.str(), parent.length());
+ if (!parent.endsWith(OS_SEPARATOR))
+ current->m_path.append(OS_SEPARATOR);
+ if (node != NULL)
+ current->m_path.append(node).append(OS_SEPARATOR);
+ }
+ rc = current->findFirst();
+ }
+ return rc;
+}
+
+/**
+ * Sets some properties from a filter.
+ *
+ * @param filter the filter with the properties to set
+ */
+void ReTraverser::setPropertiesFromFilter(ReDirEntryFilter* filter) {
+ m_minLevel = filter->m_minDepth;
+ m_maxLevel = filter->m_maxDepth;
+ setDirPattern(filter->m_pathPatterns);
+}
+
+/**
+ * Returns the info of an entry the directory stack.
+ *
+ * @param offsetFromTop 0: return the top of stack<br>
+ * 1: returns the entry one below the top<br>
+ * 2: ...
+ * @return NULL: not available<br>
+ * otherwise: the wanted entry
+ */
+ReDirStatus_t* ReTraverser::topOfStack(int offsetFromTop) {
+ ReDirStatus_t* rc = NULL;
+ if (offsetFromTop >= 0 && m_level - 1 - offsetFromTop >= 0)
+ rc = m_dirs[m_level - 1 - offsetFromTop];
+ return rc;
+}
/*
- * 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
*/
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:
/*
* 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
*/
/*
* 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
*/
/*
* 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
*/
public:
virtual bool compile(const char* pattern);
void dump(FILE* fp, const char* prefix) const;
+ /** Returns whether the search is case insensitive.
+ * @return <code>true</code>: the search is case insensitive
+ */
+ inline bool ignoreCase() const {
+ return m_ignoreCase;
+ }
virtual bool match(const ReByteBuffer& toTest, ReHit* hit = NULL) const;
virtual bool search(const ReByteBuffer& toTest, ReHit* hit = NULL,
bool greedy = false) const;
}
void destroy();
void dump(FILE* fp, const char* prefix) const;
+ /** Tests whether the search is case insensitive
+ * @return <code>true</code>: the search is case insensitive
+ */
+ bool ignoreCase() const {
+ bool rc = m_count == 0 ? false : m_patterns[0]->ignoreCase();
+ return rc;
+ }
bool match(const ReByteBuffer& name);
/** @brief Tests whether a string matches the patterns.
* @param name the string to Test
/*
* 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
*/