From 893334d23ebd67652fd1b90e7a9455fbac741cc3 Mon Sep 17 00:00:00 2001 From: Hamatoma Date: Wed, 4 Feb 2015 12:35:53 +0100 Subject: [PATCH] bugfixing (win) --- base/ReByteBuffer.cpp | 2 +- base/ReCString.cpp | 1 + base/ReStringList.cpp | 44 ++++--- base/ReStringUtils.cpp | 212 ++++++++++++++++++--------------- base/ReStringUtils.hpp | 9 +- cunit/cuReDirTools.cpp | 248 +++++++++++++++++++++++++++++++++++++++ cunit/cuReStringList.cpp | 4 +- cunit/testall.cpp | 13 +- os/ReDirTools.cpp | 50 ++++---- os/ReDirTools.hpp | 12 +- os/ReTraverser.cpp | 53 ++++++--- os/ReTraverser.hpp | 41 ++++++- 12 files changed, 519 insertions(+), 170 deletions(-) create mode 100644 cunit/cuReDirTools.cpp diff --git a/base/ReByteBuffer.cpp b/base/ReByteBuffer.cpp index dde837e..724d7ef 100644 --- a/base/ReByteBuffer.cpp +++ b/base/ReByteBuffer.cpp @@ -239,7 +239,7 @@ ReByteBuffer& ReByteBuffer::appendHexDump(const char* data, size_t length, append(" ", 1); } append("\n", 1); - length = length <= (int) bytesPerLine ? 0 : length - bytesPerLine; + length = length <= (size_t) bytesPerLine ? 0 : length - bytesPerLine; data += bytesPerLine; offset += bytesPerLine; } diff --git a/base/ReCString.cpp b/base/ReCString.cpp index 1d10205..a81d72a 100644 --- a/base/ReCString.cpp +++ b/base/ReCString.cpp @@ -73,3 +73,4 @@ int _memicmp(const void* region1, const void* region2, int size) return rc; } #endif + diff --git a/base/ReStringList.cpp b/base/ReStringList.cpp index fab756a..e09bd6d 100644 --- a/base/ReStringList.cpp +++ b/base/ReStringList.cpp @@ -39,12 +39,11 @@ ReStringList& ReStringList::append(const char* source, Tag tagOf){ /** @brief Appends a string at the end. * * @param source the new string - * @param tagOf An item which will stored with the string. It can be retrieved - * by the same index.This class knows nothing about this + * @param tag a number which will stored with the string. Not interpreted by the instance * @return the instance itself (for chaining) */ -ReStringList& ReStringList::append(const ReByteBuffer& source, Tag tagOf){ - int ix = add(-1, source.str(), source.length() + 1, tagOf); +ReStringList& ReStringList::append(const ReByteBuffer& source, Tag tag){ + int ix = add(-1, source.str(), source.length() + 1, tag); return *this; } @@ -126,9 +125,11 @@ void ReStringList::replace(Index index, const char* source, Tag tagOf){ * @param source the new string of the replaced element */ void ReStringList::replaceString(Index index, const char* source){ + Tag tag; if (index < count()){ Sequence* seq = getInfo(index); - set(index, source, strlen(source) + 1, seq->m_tag); + getLengthAndTag(seq, tag); + set(index, source, -1, tag); } } /** @brief Replaces a tagOf in the internal array. @@ -136,13 +137,12 @@ void ReStringList::replaceString(Index index, const char* source){ * The string of the element remains unchanged. * * @param index the element with this index will be replaced - * @param source the new string of the replaced element + * @param tag the new tag */ -void ReStringList::replaceTag(Index index, Tag tagOf){ - if (index < count()){ - Sequence* seq = getInfo(index); - seq->m_tag = tagOf; - } +void ReStringList::replaceTag(Index index, Tag tag){ + ReByteBuffer buffer; + if (get(index, buffer)) + set(index, buffer.str(), buffer.length(), tag); } /** @brief Returns the C string given by an index. @@ -171,9 +171,9 @@ const char* ReStringList::strOf(Index index) const{ */ ReSeqArray::Tag ReStringList::tagOf(Index index) const{ Tag rc = -1; - if (index < count()){ - Sequence* seq = getInfo(index); - rc = seq->m_tag; + ReByteBuffer buffer; + if (! get(index, buffer, &rc)){ + rc = -1; } return rc; } @@ -189,7 +189,6 @@ size_t ReStringList::sizeOf(Index index) const{ size_t rc = 0; if (index < count()){ Sequence* seq = getInfo(index); - //@ToDo rc = getLength(seq); } return rc; @@ -204,22 +203,21 @@ size_t ReStringList::sizeOf(Index index) const{ */ size_t ReStringList::strLengthOf(Index index) const{ size_t rc = 0; - if (index < count()){ - Sequence* seq = getInfo(index); - rc = rc = getLength(seq) - 1; - } + ReByteBuffer buffer; + if (get(index, buffer)) + rc = buffer.length() - 1; return rc; } /** @brief Returns the sum of all string lengths stored in the array. * - * @return the sum of all string lengths stored in the array + * @return the sum of all string lengths stored in the array (including trailing '\0') */ size_t ReStringList::sumOfSizes() const{ size_t rc = 0; - + ReByteBuffer buffer; for (int ii = count() - 1; ii >= 0; ii--){ - Sequence* seq = getInfo(ii); - rc += rc = getLength(seq); + get(ii, buffer); + rc += buffer.length(); } return rc; } diff --git a/base/ReStringUtils.cpp b/base/ReStringUtils.cpp index d24bb94..ba09ce7 100644 --- a/base/ReStringUtils.cpp +++ b/base/ReStringUtils.cpp @@ -11,69 +11,42 @@ const char ReStringUtils::AUTO_SEPARATOR = '\0'; -/** @brief Splits a filename into its parts. - * - * Example: file:/etc/samba/smb.conf - * The 4 parts are: - * - * All parts exclusive the name can be empty. - * - *
Example:
- * ReByteBuffer protocol, path, name, ext;
- * ReByteBuffer fnBackup;
- * ReStringUtils::splitPath("/etc/sambe/smb.conf", &protocol, &path, &name, NULL);
- * ext.append(".bak");
- * ReStringUtils::joinPath(fnBackup, &protocol, &path, &name, &ext);
- * assert("/etc/sambe/smb.bak", fnBackup.getBuffer());
- * 
+/** @brief Tests whether a phrase is in a phrase list. * - * @param fullname The full name to split. - * @param protocol Out: The protocol part of the filename. May be NULL. - * @param path Out: The path of the filename. May be NULL. - * @param name Out: The name part of the filename. May be NULL. - * @param ext Out: The extension. May be NULL. + * @param phrase The word to search. + * @param list The list to search. All phrases of the list are separated by separator. + * @param ignoreCase true: The search is case insensitive. false: The search is case sensitive. + * @param separator The separator in list. If AUTO_SEPARATOR the separator will + * be taken from the list itself (the first character). */ -void ReStringUtils::splitPath(const char* fullname, - ReByteBuffer* protocol, ReByteBuffer* path, ReByteBuffer* name, ReByteBuffer* ext){ - const char currentSlash = strchr(fullname, '/') != NULL ? '/' : OS_SEPARATOR_CHAR; - const char* start = strchr(fullname, ':'); - if (protocol != NULL){ - protocol->setLength(0); - if (start != NULL) - protocol->append(fullname, start - fullname + 1); - } - if (start == NULL) - start = fullname; - else - start++; - - const char* end = strrchr(start, currentSlash); - - if (path != 0){ - path->setLength(0); - if (end != NULL) - path->append(start, end - start + 1); +bool ReStringUtils::isInList(const char* phrase, const char* list, + bool ignoreCase, char separator){ + if (separator == AUTO_SEPARATOR) + separator = *list++; + const char* end = strchr(list, separator); + int phraseLength = strlen(phrase); + bool rc = false; + while(! rc && end != NULL){ + if (end - list == phraseLength){ + if (ignoreCase) + rc = strnicmp(list, phrase, phraseLength) == 0; + else + rc = strncmp(list, phrase, end - list) == 0; + if (rc) + break; + } + list = end + 1; + end = strchr(list, separator); } - if (end != NULL) - start = end + 1; - end = strrchr(start, '.'); - if (name != NULL){ - name->setLength(0); - if (end == NULL) - name->append(start, strlen(start)); + if (! rc){ + if (ignoreCase) + rc = strnicmp(list, phrase, end - list) == 0; else - name->append(start, end - start); - } - if (ext != NULL){ - ext->setLength(0); - if (end != NULL) - ext->append(end, strlen(end)); + rc = strncmp(list, phrase, end - list) == 0; } + return rc; } + /** Joins a filename from parts. * *
Example:
@@ -138,6 +111,95 @@ ReByteBuffer& ReStringUtils::joinPath(ReByteBuffer& fullpath,
 	return fullpath;
 }
 
+
+/**
+ * Returns the length of an integer in a given text.
+ *
+ * @param text		the number is expected in this text
+ * @param length	the length of the text. -1: strlen(text)
+ * @param value		OUT: the value of the integer. May be NULL
+ * @return			the number of digits at the start of the text
+ */
+int ReStringUtils::lengthOfUnsigned(const char* text, int length, 
+	unsigned int* value){
+	int rc = 0;
+	unsigned int val = 0;
+	char cc;
+	if (length < 0)
+		// the '\0' is automatically a stopper, no need to call strlen()
+		length = INT_MAX;
+	while(rc < length && isdigit(cc = *text++)){
+		rc++;
+		val = 10*val + cc - '0'; 
+	}
+	if (value != NULL && rc != 0)
+		*value = val;
+	return rc;
+}
+
+/** @brief Splits a filename into its parts.
+ *
+ * Example: file:/etc/samba/smb.conf
+ * The 4 parts are:
+ * 
  • A protocol: file:
  • + *
  • The path. /etc/samba/
  • + *
  • The name: smb
  • + *
  • The extension: .conf
  • + *
+ * All parts exclusive the name can be empty. + * + *
Example:
+ * ReByteBuffer protocol, path, name, ext;
+ * ReByteBuffer fnBackup;
+ * ReStringUtils::splitPath("/etc/sambe/smb.conf", &protocol, &path, &name, NULL);
+ * ext.append(".bak");
+ * ReStringUtils::joinPath(fnBackup, &protocol, &path, &name, &ext);
+ * assert("/etc/sambe/smb.bak", fnBackup.getBuffer());
+ * 
+ * + * @param fullname The full name to split. + * @param protocol Out: The protocol part of the filename. May be NULL. + * @param path Out: The path of the filename. May be NULL. + * @param name Out: The name part of the filename. May be NULL. + * @param ext Out: The extension. May be NULL. + */ +void ReStringUtils::splitPath(const char* fullname, + ReByteBuffer* protocol, ReByteBuffer* path, ReByteBuffer* name, ReByteBuffer* ext){ + const char currentSlash = strchr(fullname, '/') != NULL ? '/' : OS_SEPARATOR_CHAR; + const char* start = strchr(fullname, ':'); + if (protocol != NULL){ + protocol->setLength(0); + if (start != NULL) + protocol->append(fullname, start - fullname + 1); + } + if (start == NULL) + start = fullname; + else + start++; + + const char* end = strrchr(start, currentSlash); + + if (path != 0){ + path->setLength(0); + if (end != NULL) + path->append(start, end - start + 1); + } + if (end != NULL) + start = end + 1; + end = strrchr(start, '.'); + if (name != NULL){ + name->setLength(0); + if (end == NULL) + name->append(start, strlen(start)); + else + name->append(start, end - start); + } + if (ext != NULL){ + ext->setLength(0); + if (end != NULL) + ext->append(end, strlen(end)); + } +} /** @brief Compares two strings case insensitive. * * @param string1 The first C string to compare. @@ -158,39 +220,3 @@ int ReStringUtils::strnicmp(const char* string1, const char* string2, size_t len } return rc; } - -/** @brief Tests whether a phrase is in a phrase list. - * - * @param phrase The word to search. - * @param list The list to search. All phrases of the list are separated by separator. - * @param ignoreCase true: The search is case insensitive. false: The search is case sensitive. - * @param separator The separator in list. If AUTO_SEPARATOR the separator will - * be taken from the list itself (the first character). - */ -bool ReStringUtils::isInList(const char* phrase, const char* list, - bool ignoreCase, char separator){ - if (separator == AUTO_SEPARATOR) - separator = *list++; - const char* end = strchr(list, separator); - int phraseLength = strlen(phrase); - bool rc = false; - while(! rc && end != NULL){ - if (end - list == phraseLength){ - if (ignoreCase) - rc = strnicmp(list, phrase, phraseLength) == 0; - else - rc = strncmp(list, phrase, end - list) == 0; - if (rc) - break; - } - list = end + 1; - end = strchr(list, separator); - } - if (! rc){ - if (ignoreCase) - rc = strnicmp(list, phrase, end - list) == 0; - else - rc = strncmp(list, phrase, end - list) == 0; - } - return rc; -} diff --git a/base/ReStringUtils.hpp b/base/ReStringUtils.hpp index 2c42f27..2b29ff9 100644 --- a/base/ReStringUtils.hpp +++ b/base/ReStringUtils.hpp @@ -19,15 +19,16 @@ public: //@ If used in isInList() the first character of the list will be the separator. static const char AUTO_SEPARATOR; public: - static void splitPath(const char* fullname, - ReByteBuffer* protocol, ReByteBuffer* path, ReByteBuffer* name, ReByteBuffer* ext); + static bool isInList(const char* phrase, const char* list, + bool ignoreCase = true, char separator = AUTO_SEPARATOR); static ReByteBuffer& joinPath(ReByteBuffer& result, ReByteBuffer* protocol, ReByteBuffer* path, ReByteBuffer* name, ReByteBuffer* ext); static ReByteBuffer& joinPath(ReByteBuffer& result, const char* protocol, const char* path, const char* name, const char* ext); + static int lengthOfUnsigned(const char* text, int length = -1, unsigned int* value = NULL); + static void splitPath(const char* fullname, + ReByteBuffer* protocol, ReByteBuffer* path, ReByteBuffer* name, ReByteBuffer* ext); static int strnicmp(const char* string1, const char* string2, size_t length); - static bool isInList(const char* phrase, const char* list, - bool ignoreCase = true, char separator = AUTO_SEPARATOR); }; #endif /* RESTRINGUTILS_H_ */ diff --git a/cunit/cuReDirTools.cpp b/cunit/cuReDirTools.cpp new file mode 100644 index 0000000..032ddd8 --- /dev/null +++ b/cunit/cuReDirTools.cpp @@ -0,0 +1,248 @@ +/* + * cuReTraverser.cpp + * + * License: Public domain + * Do what you want. + * No warranties and disclaimer of any damages. + * The latest sources: https://github.com/republib + */ + +#include "base/rebase.hpp" +#include "os/reos.hpp" + +static const char* s_empty[] = { NULL }; + +class TestReDirTools : public ReTestUnit { +public: + TestReDirTools() : ReTestUnit("ReTraverser", __FILE__){ + m_base = testDir(); + m_base.append("traverser").append(OS_SEPARATOR, -1); + _mkdir(m_base.str(), ALLPERMS); + run(); + } +private: + ReByteBuffer m_base; + ReByteBuffer m_buffer; +private: + 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 %1$s", m_buffer.str()); + } + return m_buffer.str(); + } + void makeFile(const char* relPath){ + ReByteBuffer path(m_base); + path.append("/").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 %1$s", path.str()); + } + } + void initTree(){ + makeFile("1.txt"); + makeDir("dir1"); + makeDir("dir2"); + makeDir("dir1/dir1_1"); + makeDir("dir1/dir1_2"); + makeDir("dir1/dir1_2/dir1_2_1"); + makeDir("dir1/cache"); + makeFile("dir1/dir1_2/dir1_2_1/x1.txt"); + makeFile("dir1/dir1_2/dir1_2_1/x2.txt"); + makeFile("dir2/2.x"); + makeFile("dir1/cache/cache.txt"); + } + void run(){ + initTree(); + + testToolSync(); + testToolStatistic(); + testBasic(); + testDirOptions(); + checkSetFilterFromProgramArgs(); + testDirStatistic(); + testCopyFile(); + testList(); + } + void testList(){ + ReDirTools tools; + const char* argv[] = { "list", m_base.str(), NULL }; + tools.list(2, argv); + } + void testCopyFile(){ +#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(time_t absTime, int relTime){ + int diff = int(time(NULL) - relTime - absTime); + 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(24*60*60 - DIFF, (int) opts.checkDate("1970.01.02")); + checkEqu(24*60*60+3600-DIFF, (int) opts.checkDate("1970.01.02/1")); + checkEqu(24*60*60 + 2*3600 + 33*60 - DIFF, (int) opts.checkDate("1970.01.02/02:33")); + checkRelDate(opts.checkDate("3m"), 3*60); + checkRelDate(opts.checkDate("7h"), 7*60*60); + checkRelDate(opts.checkDate("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", "-y1970.01.02", "-o1970.01.03", + "-D5", "-d1", "-z1k", "-Z2M", "-p;*;-*~" + }; + ReDirEntryFilter_t filter; + opts.programArgsChangeable().init(sizeof argv / sizeof argv[0], argv); + opts.setFilterFromProgramArgs(filter); + // local time: +3600 + const int DIFF = 3600; + checkEqu(1*24*3600 - DIFF, (int) filter.m_maxAge); + checkEqu(2*24*3600 - DIFF, (int) 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(){ + 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(){ + ReDirStatistic stat; + const ReStringList& list = stat.calculate(m_base.str(), 1); + ReByteBuffer buffer; + ReByteBuffer expected; + log(false, list.join("\n", buffer).str()); + checkEqu(4u, 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.000054 MB 2 3\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()); + } + void testToolStatistic(){ + ReDirTools tools; + const char* argv[] = { "dt", "stat", "-P;*;-*/cache/*", m_base.str(), "2" }; + tools.main(5, (char**) argv); + } + void testToolSync(){ + ReDirTools tools; + ReByteBuffer source(m_base); + source.append("dir1"); + ReByteBuffer target(makeDir("synctest")); + const char* argv[] = { "dt", "sync", "-P;*;-*/cache/*", "-p;*.txt", + source.str(), target.str() }; + tools.main(6, (char**) argv); + } +}; +extern void testReDirTools(void); + +void testReDirTools(void){ + TestReDirTools unit; +} diff --git a/cunit/cuReStringList.cpp b/cunit/cuReStringList.cpp index 6b7266c..1a58df5 100644 --- a/cunit/cuReStringList.cpp +++ b/cunit/cuReStringList.cpp @@ -26,9 +26,9 @@ private: ReStringList list; ReByteBuffer line; line.set("Hi", -1); - list.append(line, 99ll); + list.append(line, 99); line.append("!", -1); - list.append(line, -5ll); + list.append(line, -5); checkEqu(2u, list.count()); checkEqu(99ll, list.tagOf(0)); checkEqu("Hi", list.strOf(0)); diff --git a/cunit/testall.cpp b/cunit/testall.cpp index 2634bb6..8960425 100644 --- a/cunit/testall.cpp +++ b/cunit/testall.cpp @@ -41,10 +41,8 @@ void testBase(){ } void testString(){ - void testReCString(); - testReCString(); - extern void testReStringList(void); - testReStringList(); + void testReStringUtils(void); + testReStringUtils(); extern void testReI18N(void); testReI18N(); @@ -58,8 +56,13 @@ void testString(){ testReMatcher(); } void testOs(){ + void testReDirTools(); + testReDirTools(); + void testReTraverser(); testReTraverser(); + void testReDirTools(); + testReDirTools(); } void testMath(){ extern void testReMD5(); @@ -70,7 +73,7 @@ void testMath(){ void testAll(){ try { - testBase(); + testString(); testOs(); testBase(); diff --git a/os/ReDirTools.cpp b/os/ReDirTools.cpp index 0805df4..7742d5c 100644 --- a/os/ReDirTools.cpp +++ b/os/ReDirTools.cpp @@ -109,12 +109,14 @@ const char* s_syncExamples[] = { * @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_output(stdout), + m_verboseLevel(V_NORMAL) { } /** @@ -147,7 +149,7 @@ void ReDirOptions::addCompoundUsage(const char** usage){ * Adds the standard filter options. */ void ReDirOptions::addStandardFilterOptions(){ - // standard short options: D d O o P p q T t y Z z + // standard short options: D d O o P p T t v y Z z m_programArgs.addInt("maxdepth", i18n("the depth of the subdirectory is lower or equal \n" "0: search is done only in the base directory"), @@ -185,9 +187,9 @@ void ReDirOptions::addStandardFilterOptions(){ "of the 'not patterns' matches\n" "examples: '*.cpp;*.hpp;Make*' '*;-*.bak;-*~"), 'p', "basename-pattern", false, NULL); - m_programArgs.addBool("quiet", - i18n("no additional information like runtime"), - 'q', "quiet", false); + m_programArgs.addString("verbose", + i18n("verbose level: 0: no info, 1: summary only, 2: normal, 3: chatter mode, 4: debug"), + 'v', "verbose", false, "1"); m_programArgs.addInt("trace", i18n("all seconds the current path will be traced\n" "0: no trace"), @@ -446,7 +448,15 @@ void ReDirOptions::checkStandardFilterOptions(){ checkSize(m_programArgs.getString("maxsize", buffer)); checkSize(m_programArgs.getString("minsize", buffer)); checkPatternList(m_programArgs.getString("nodepattern", buffer)); - checkPatternList(m_programArgs.getString("pathpattern", buffer)); + checkPatternList(m_programArgs.getString("pathpattern", buffer)); + if (m_programArgs.getString("verbose", buffer)[0] != '\0'){ + unsigned level = V_NORMAL; + if (ReStringUtils::lengthOfUnsigned(buffer.str(), -1, &level) + != buffer.length()) + help(i18n("verbose level is not a number (or '')"), buffer.str()); + else + m_verboseLevel = VerboseLevel(level); + } } /** @@ -540,7 +550,8 @@ void ReDirOptions::setFilterFromProgramArgs(ReDirEntryFilter_t& filter){ m_pathPatterns.set(buffer.str()); filter.m_pathPatterns = &m_pathPatterns; } - filter.m_traceInterval = m_programArgs.getInt("trace"); + 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()); @@ -705,7 +716,7 @@ const ReStringList& ReDirStatistic::calculate(const char* base, int level, time_t now = time(NULL); int diff = int(now - m_lastTrace); if (diff >= m_traceInterval){ - fprintf(stderr, "%s\n", current->m_path.str()); + fprintf(m_output, "%s\n", current->m_path.str()); m_lastTrace = now; } } @@ -914,7 +925,7 @@ void ReDirList::list(int argc, const char* argv[]){ ReDirBatch::ReDirBatch() : ReDirOptions(s_batchUsage, s_batchExamples) { - // standard short options: D d O o P p q T t y Z z + // 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, @@ -1094,7 +1105,7 @@ ReDirSync::ReDirSync() : ReDirOptions(s_syncUsage, s_syncExamples), m_buffer() { - // standard short options: D d O o P p q T t y Z z + // 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"), @@ -1109,9 +1120,6 @@ ReDirSync::ReDirSync() : m_programArgs.addBool("ignoredate", i18n("the modification is recognized only by the different size (not time)"), 'i', "ignore-time", false); - m_programArgs.addBool("chatter", - i18n("comments the action of each file"), - 'h', "chatter", false); m_programArgs.addBool("mustexist", i18n("files which don't exist on the target will not be copied"), 'm', "must-exist", false); @@ -1342,12 +1350,10 @@ void ReDirSync::synchronize(int argc, const char* argv[]){ help(i18n("target is not a directory: $1"), target.str()); size_t lengthTargetBase = target.length(); bool addOnly = m_programArgs.getBool("add"); - bool chatterMode = m_programArgs.getBool("chatter"); int maxFileTimeDiff = m_programArgs.getInt("timediff"); bool dry = m_programArgs.getBool("dry"); bool ignoreDate = m_programArgs.getBool("ignoredate"); bool mustExist = m_programArgs.getBool("mustexist"); - bool verbose = ! m_programArgs.getBool("quiet"); setFilterFromProgramArgs(filter); int64_t sumSizes = 0; int files = 0; @@ -1391,33 +1397,33 @@ void ReDirSync::synchronize(int argc, const char* argv[]){ const char* targetRelativePath = targetFile.str() + ixTargetRelative + 1; bool exists = stat(targetFile.str(), &info) == 0; if (! exists && mustExist){ - if (chatterMode) + if (m_verboseLevel == V_CHATTER) fprintf(m_output, "-ignored: %s does not exist\n", targetRelativePath); continue; } if (exists){ if (addOnly){ - if (chatterMode) + if (m_verboseLevel >= V_CHATTER) fprintf(m_output, "~ignored: %s exists\n", targetRelativePath); continue; } if (ignoreDate && entry->fileSize() == info.st_size){ - if (chatterMode) + if (m_verboseLevel >= V_CHATTER) fprintf(m_output, "_ignored: %s same size\n", targetRelativePath); continue; } // target younger than source? - int diff = info.st_mtime - entry->filetimeToTime(entry->modified()); + int diff = int(info.st_mtime - entry->filetimeToTime(entry->modified())); if (! ignoreDate && info.st_mtime - entry->filetimeToTime(entry->modified()) <= maxFileTimeDiff) { - if (chatterMode) + if (m_verboseLevel >= V_CHATTER) fprintf(m_output, "=ignored: %s same time\n", targetRelativePath); continue; } } files++; sumSizes += entry->fileSize(); - if (verbose || chatterMode) + if (m_verboseLevel >= V_NORMAL) fprintf(m_output, "%c%s%s\n", exists ? '!' : '+', targetRelativePath, dry ? " would be copied" : ""); if (! dry) @@ -1427,7 +1433,7 @@ void ReDirSync::synchronize(int argc, const char* argv[]){ treeDirs += traverser.directories(); treeSumSizes+= traverser.sizes(); } - if (verbose){ + if (m_verboseLevel >= V_SUMMARY){ int duration = int(time(NULL) - start); fprintf(m_output, i18n( "=== copied: %02d:%02d sec %7d file(s) %12.6f MByte (%.3f MB/sec).\n" diff --git a/os/ReDirTools.hpp b/os/ReDirTools.hpp index 266593e..d5053a1 100644 --- a/os/ReDirTools.hpp +++ b/os/ReDirTools.hpp @@ -10,7 +10,16 @@ #ifndef OS_DIRTOOLS_HPP_ #define OS_DIRTOOLS_HPP_ -class ReDirOptions { +class ReDirOptions : ReTraceUnit{ +public: + enum VerboseLevel { + V_UNDEF, + V_QUIET, + V_SUMMARY, + V_NORMAL, + V_CHATTER, + V_DEBUG + }; public: ReDirOptions(const char* usage[], const char* example[]); ~ReDirOptions(); @@ -50,6 +59,7 @@ protected: const char** m_compoundUsage; int m_countCompoundUsage; FILE* m_output; + VerboseLevel m_verboseLevel; }; #if defined __linux__ diff --git a/os/ReTraverser.cpp b/os/ReTraverser.cpp index d3620d7..8d38ce8 100644 --- a/os/ReTraverser.cpp +++ b/os/ReTraverser.cpp @@ -335,10 +335,7 @@ ReDirEntryFilter_t::ReDirEntryFilter_t() : m_minAge(0), m_maxAge(0), m_minDepth(0), - m_maxDepth(512), - m_traceInterval(0), - m_lastTrace(0), - m_traceCounter(0) + m_maxDepth(512) { } @@ -353,13 +350,6 @@ ReDirEntryFilter_t::~ReDirEntryFilter_t(){ bool ReDirEntryFilter_t::match(ReDirStatus_t& entry){ bool rc = false; do { - if (m_traceCounter++ % 100 == 0 && m_traceInterval > 0){ - time_t now = time(NULL); - if (int(now - m_lastTrace) > m_traceInterval){ - m_lastTrace = now; - fprintf(stderr, "%s%s\n", entry.m_path.str(), entry.node()); - } - } if (0 == (entry.type() & m_types)) break; int64_t size = entry.fileSize(); @@ -393,13 +383,45 @@ struct stat* ReDirStatus_t::getStatus() { return &m_status; } #endif + +/** +* 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(time(NULL)), + m_interval(interval) +{ +} +/** + * Destructor. + */ +ReTraceUnit::~ReTraceUnit(){ +} + +/** + * Prints a message. + * + * Often overwritten by a subclass. + * + * @param message message for the trace + * @return true (for chaining) + */ +bool ReTraceUnit::trace(const char* message){ + printf("%d\n", message); + return true; +} /** * Constructor. * * @param base the base directory. The traversal starts at this point */ -ReTraverser::ReTraverser(const char* base) : +ReTraverser::ReTraverser(const char* base, ReTraceUnit* tracer) : m_minLevel(0), m_maxLevel(512), m_level(-1), @@ -409,8 +431,8 @@ ReTraverser::ReTraverser(const char* base) : m_dirPatterns(NULL), m_directories(0), m_files(0), - m_sizes(0ll) - + m_sizes(0ll), + m_tracer(tracer) { memset(m_dirs, 0, sizeof m_dirs); m_dirs[0] = new ReDirStatus_t(); @@ -469,6 +491,9 @@ ReDirStatus_t* ReTraverser::rawNextFile(int& 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]; diff --git a/os/ReTraverser.hpp b/os/ReTraverser.hpp index 9c4244b..cb4b1b6 100644 --- a/os/ReTraverser.hpp +++ b/os/ReTraverser.hpp @@ -79,7 +79,7 @@ public: static time_t filetimeToTime(const FileTime_t* time); static void timeToFiletime(time_t time, FileTime_t& filetime); }; -class ReDirEntryFilter_t { +class ReDirEntryFilter_t{ public: ReDirEntryFilter_t(); ~ReDirEntryFilter_t(); @@ -95,14 +95,44 @@ public: time_t m_maxAge; int m_minDepth; int m_maxDepth; - int m_traceInterval; - time_t m_lastTrace; - int m_traceCounter; +}; +class ReTraceUnit{ +public: + ReTraceUnit(int m_triggerCount = 10, int interval = 60); + virtual ~ReTraceUnit(); +public: + /** Returns whether the the instance is triggered. + * To avoid too the not cheap call of time() the trace unit uses a counter. + * If the counter reaches a given level the time check should be done. + * @return true: the counter has reached m_triggerCount + */ + inline bool isCountTriggered(){ + bool rc = ++m_count % m_triggerCount == 0; + return rc; + } + /** Tests whether a given waiting time has been gone since the last trace. + * @return true: the last trace has been done after + * at least m_interval seconds + */ + inline bool isTimeTriggered(){ + time_t now = time(NULL); + bool rc = now - m_lastTrace > m_interval; + if (rc){ + m_lastTrace = now; + } + return rc; + } + virtual bool trace(const char* message); +protected: + int m_count; + int m_triggerCount; + time_t m_lastTrace; + int m_interval; }; #define MAX_ENTRY_STACK_DEPTH 256 class ReTraverser { public: - ReTraverser(const char* base); + ReTraverser(const char* base, ReTraceUnit* tracer = NULL); virtual ~ReTraverser(); public: /** @@ -176,6 +206,7 @@ protected: int m_directories; int m_files; int64_t m_sizes; + ReTraceUnit* m_tracer; }; #endif /* OS_RETRAVERSER_HPP_ */ -- 2.39.5