From: hama Date: Thu, 1 Jan 2015 14:11:09 +0000 (+0100) Subject: Refactoring, valgrind clean, +ReDirTools X-Git-Url: https://gitweb.hamatoma.de/?a=commitdiff_plain;h=ebe447edd8ea2c886f10ffe68501f687d7073346;p=crepublib Refactoring, valgrind clean, +ReDirTools --- diff --git a/base/ReByteBuffer.cpp b/base/ReByteBuffer.cpp index 8168d5c..4194fa2 100644 --- a/base/ReByteBuffer.cpp +++ b/base/ReByteBuffer.cpp @@ -161,8 +161,8 @@ ReByteBuffer& ReByteBuffer::appendInt(int number, const char* format){ * * @return *this (for chaining). */ -ReByteBuffer& ReByteBuffer::append(ReByteBuffer& source){ - return append(source.buffer(), source.length()); +ReByteBuffer& ReByteBuffer::append(const ReByteBuffer& source){ + return append(source.str(), source.length()); } /** Converts a subsequence into an integer. diff --git a/base/ReByteBuffer.hpp b/base/ReByteBuffer.hpp index 883bfd0..f863a6d 100644 --- a/base/ReByteBuffer.hpp +++ b/base/ReByteBuffer.hpp @@ -43,7 +43,7 @@ public: ReByteBuffer& operator =(const ReByteBuffer& source); public: ReByteBuffer& append(const Byte* source, size_t length = -1); - ReByteBuffer& append(ReByteBuffer& source); + ReByteBuffer& append(const ReByteBuffer& source); ReByteBuffer& appendInt(int number, const char* format = "%d"); /** @brief Returns the n-th byte of the internal buffer. * @param index The index of the wanted byte. @@ -62,13 +62,21 @@ public: inline Byte* buffer() const{ return m_buffer; } + int count(const char* toCount, size_t lengthToCount = -1); /**@brief Returns the minimum allocation unit. * @return The minimum of bytes to allocate. */ inline size_t delta() const{ return m_delta; } - int count(const char* toCount, size_t lengthToCount = -1); + /** @brief Tests whether another instance is equal to this instance. + * @param buffer the buffer to compare + * @return true: the buffer's contents are equal + */ + inline bool equals(const ReByteBuffer& buffer){ + bool rc = buffer.length() == m_length && memcmp(buffer.str(), m_buffer, m_length) == 0; + return rc; + } /**@brief Returns the length of the buffer (the count of used bytes). * @return The count of the allocated bytes in the internal buffer. */ diff --git a/base/ReHashList.cpp b/base/ReHashList.cpp index 580e552..d8ef8aa 100644 --- a/base/ReHashList.cpp +++ b/base/ReHashList.cpp @@ -132,7 +132,7 @@ void ReHashList::clear(){ * @param true: An item was found. false: No more items. */ bool ReHashList::next(size_t& position, ReByteBuffer* key, ReByteBuffer* value){ - bool rc = position < m_keys.getCount(); + bool rc = position < m_keys.count(); if (rc){ ReSeqList::Sequence* seq = m_keys.getInfo(position++); if (key != NULL){ @@ -162,7 +162,7 @@ int ReHashList::find(const Byte* key, size_t length) const{ if (length == (size_t) -1) length = strlen(key) + 1; int rc = -1; - int count = m_keys.getCount(); + int count = m_keys.count(); for (int ii = 0; rc < 0 && ii < count; ii++){ ReSeqList::Sequence* seq = m_keys.getInfo(ii); if (seq->m_length == length){ diff --git a/base/ReLogger.cpp b/base/ReLogger.cpp index 8a9df3c..dd159f7 100644 --- a/base/ReLogger.cpp +++ b/base/ReLogger.cpp @@ -215,8 +215,10 @@ void ReFileAppender::setConfig(const char* pattern, int maxFiles, int maxSize){ changeFile(); else{ m_stream = fopen(fullname.str(), "a"); - if (m_stream == NULL) - assert(m_stream != NULL); + if (m_stream == NULL){ + fprintf (stderr, "+++ ReFileAppender::setConfig(): cannot open: '%s'\n", fullname.str()); + //assert(m_stream != NULL); + } } } } diff --git a/base/ReProgramArgs.cpp b/base/ReProgramArgs.cpp index b275e27..2996ef5 100644 --- a/base/ReProgramArgs.cpp +++ b/base/ReProgramArgs.cpp @@ -560,7 +560,7 @@ void ReProgramArgs::help(const char* message, bool issueLastError, ReStringList& line.set("\t", 1).append(properties.strOf(IxDescr), -1); lines.append(line.str()); } - if (m_examples.getCount() > 0){ + if (m_examples.count() > 0){ lines.append(i18n("Example:")); lines.append(m_examples); } @@ -578,7 +578,7 @@ void ReProgramArgs::help(const char* message, bool issueLastError, ReStringList& void ReProgramArgs::help(const char* message, bool issueLastError, FILE* stream){ ReStringList lines; help(message, issueLastError, lines); - for(size_t ii = 0; ii < lines.getCount(); ii++){ + for(size_t ii = 0; ii < lines.count(); ii++){ fputs(lines.strOf(ii), stream); fputc('\n', stream); } diff --git a/base/ReSeqList.cpp b/base/ReSeqList.cpp index 9f5e3a7..4e8a019 100644 --- a/base/ReSeqList.cpp +++ b/base/ReSeqList.cpp @@ -64,7 +64,7 @@ void ReSeqList::add(Index index, const Byte* source, size_t sourceLength, Tag ta seq.m_length = sourceLength; seq.m_tag = tag; m_content.append(source, sourceLength); - if (index >= getCount()){ + if (index >= count()){ m_list.append((Byte*) &seq, sizeof seq); }else{ m_list.insert(index * sizeof(Sequence), (Byte*) &seq, sizeof seq); @@ -81,7 +81,7 @@ void ReSeqList::add(Index index, const Byte* source, size_t sourceLength, Tag ta */ bool ReSeqList::get(Index index, ReByteBuffer& value, Tag* tag) const{ bool rc = false; - if (index < getCount()){ + if (index < count()){ Sequence* seq = ((Sequence*)m_list.buffer()) + index; value.set(m_content.buffer() + seq->m_index, seq->m_length); if (tag != NULL) @@ -98,7 +98,7 @@ bool ReSeqList::get(Index index, ReByteBuffer& value, Tag* tag) const{ * @param tag An additional info associated to the source. */ void ReSeqList::set(Index index, const Byte* source, size_t sourceLength, Tag tag){ - if (index >= getCount()) + if (index >= count()) add(index, source, sourceLength, tag); else { if (sourceLength == (size_t) -1) @@ -122,7 +122,7 @@ void ReSeqList::set(Index index, const Byte* source, size_t sourceLength, Tag ta * @param index The index of the entry to remove. */ void ReSeqList::remove(Index index){ - if (index <= getCount()){ + if (index <= count()){ Sequence* seq = getInfo(index); // Is this the last entry in m_content? if (seq->m_index + seq->m_length >= m_content.length()){ diff --git a/base/ReSeqList.hpp b/base/ReSeqList.hpp index 5299cec..afc3351 100644 --- a/base/ReSeqList.hpp +++ b/base/ReSeqList.hpp @@ -45,7 +45,7 @@ public: /** @brief Returns the count of defined entries in the list. * @return The number of defined entries in the list (array). */ - inline Index getCount() const { + inline Index count() const { return m_list.length() / sizeof (Sequence); } protected: diff --git a/base/ReStringList.cpp b/base/ReStringList.cpp index 37a66f5..bb10fb4 100644 --- a/base/ReStringList.cpp +++ b/base/ReStringList.cpp @@ -8,12 +8,13 @@ #include "base/rebase.hpp" /** @brief Constructor. + * + * @param deltaList list reservation will be incremented by this amount + * @param deltaBuffer buffer reservation will be incremented by this amount */ -ReStringList::ReStringList() - : - ReSeqList() +ReStringList::ReStringList(int deltaList, int deltaBuffer) : + ReSeqList(deltaList, deltaBuffer) { - } /** @brief Destructor. @@ -23,19 +24,35 @@ ReStringList::~ReStringList() { /** @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. - * T his class knows nothing about this. + * @param tagOf An item which will stored with the string. It can be retrieved + * by the same index.This class knows nothing about this. + * @return the instance itself (for chaining) */ -void ReStringList::append(const char* source, Tag tagOf){ +ReStringList& ReStringList::append(const char* source, Tag tagOf){ add(-1, source, -1, tagOf); + return *this; } +/** @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. + * @return the instance itself (for chaining) + */ +ReStringList& ReStringList::append(const ReByteBuffer& source, Tag tagOf){ + add(-1, source.str(), source.length(), tagOf); + return *this; +} + /** @brief Appends a stringlist at the end. * * @param source The new stringlist. + * @return the instance itself (for chaining) */ -void ReStringList::append(ReStringList& source){ - for (size_t ii = 0; ii < source.getCount(); ii++) +ReStringList& ReStringList::append(ReStringList& source){ + for (size_t ii = 0; ii < source.count(); ii++) add(-1, source.strOf(ii), -1, source.tagOf(ii)); + return *this; } /** @brief Inserts a string at a given index. * @@ -65,7 +82,7 @@ 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){ - if (index < getCount()){ + if (index < count()){ Sequence* seq = getInfo(index); set(index, source, -1, seq->m_tag); } @@ -78,7 +95,7 @@ void ReStringList::replaceString(Index index, const char* source){ * @param source The new string of the replaced element. */ void ReStringList::replaceTag(Index index, Tag tagOf){ - if (index < getCount()){ + if (index < count()){ Sequence* seq = getInfo(index); seq->m_tag = tagOf; } @@ -93,7 +110,7 @@ void ReStringList::replaceTag(Index index, Tag tagOf){ */ const char* ReStringList::strOf(Index index) const{ const char* rc = NULL; - if (index < getCount()){ + if (index < count()){ Sequence* seq = getInfo(index); rc = m_content.buffer() + seq->m_index; } @@ -110,7 +127,7 @@ const char* ReStringList::strOf(Index index) const{ */ ReSeqList::Tag ReStringList::tagOf(Index index) const{ Tag rc = -1; - if (index < getCount()){ + if (index < count()){ Sequence* seq = getInfo(index); rc = seq->m_tag; } @@ -126,7 +143,7 @@ ReSeqList::Tag ReStringList::tagOf(Index index) const{ */ size_t ReStringList::sizeOf(Index index) const{ size_t rc = 0; - if (index < getCount()){ + if (index < count()){ Sequence* seq = getInfo(index); rc = seq->m_length; } @@ -142,7 +159,7 @@ size_t ReStringList::sizeOf(Index index) const{ */ size_t ReStringList::strLengthOf(Index index) const{ size_t rc = 0; - if (index < getCount()){ + if (index < count()){ Sequence* seq = getInfo(index); rc = seq->m_length - 1; } @@ -155,7 +172,7 @@ size_t ReStringList::strLengthOf(Index index) const{ size_t ReStringList::sumOfSizes() const{ size_t rc = 0; - for (int ii = getCount() - 1; ii >= 0; ii--){ + for (int ii = count() - 1; ii >= 0; ii--){ Sequence* seq = getInfo(ii); rc += seq->m_length; } @@ -168,7 +185,7 @@ size_t ReStringList::sumOfSizes() const{ size_t ReStringList::sumOfStrLengths() const{ size_t rc = 0; - for (int ii = getCount() - 1; ii >= 0; ii--){ + for (int ii = count() - 1; ii >= 0; ii--){ Sequence* seq = getInfo(ii); rc += seq->m_length - 1; } @@ -186,9 +203,9 @@ size_t ReStringList::sumOfStrLengths() const{ ReSeqList::Index ReStringList::indexOf(const char* toFind, bool ignoreCase, Index start) const{ Index rc = (Index) -1; - Index count = getCount(); + Index theCount = count(); - for (; rc == (Index) -1 && start < count; start++){ + for (; rc == (Index) -1 && start < theCount; start++){ const char* item = strOf(start); int rc2; if (ignoreCase) @@ -212,10 +229,10 @@ ReSeqList::Index ReStringList::indexOf(const char* toFind, ReSeqList::Index ReStringList::nextStartingWith(Index start, const char* prefix, bool ignoreCase){ Index rc = (Index) -1; - Index count = getCount(); + Index theCount = count(); size_t length = strlen(prefix); - for (; rc == (Index) -1 && start < count; start++){ + for (; rc == (Index) -1 && start < theCount; start++){ const char* item = strOf(start); int rc2; if (ignoreCase) @@ -264,18 +281,20 @@ void ReStringList::split(const char* list, char separator, bool append){ * * @param separator This string was put between the substrings. May be NULL or "". * @param result Out: The result buffer. + * @return buffer (for chaining) */ -void ReStringList::join(const char* separator, ReByteBuffer& result){ - size_t count = getCount(); +ReByteBuffer& ReStringList::join(const char* separator, ReByteBuffer& result) const{ + size_t theCount = count(); result.setLength(0); size_t lengthSep = strlen(separator); - for (size_t ix = 0; ix < count; ix++){ + for (size_t ix = 0; ix < theCount; ix++){ result.append(strOf(ix), sizeOf(ix) - 1); - if (ix != count - 1 && separator != NULL) + if (ix != theCount - 1 && separator != NULL) result.append(separator, lengthSep); } + return result; } /** @brief Writes the stringlist to a file. * @@ -290,10 +309,10 @@ bool ReStringList::writeToFile(const char* filename, bool rc = false; FILE* fp = fopen(filename, mode); if (fp){ - size_t count = getCount(); - for (size_t ix = 0; ix < count; ix++){ + size_t aCount = count(); + for (size_t ix = 0; ix < aCount; ix++){ fputs(strOf(ix), fp); - if (ix != count - 1 && separator != NULL) + if (ix != aCount - 1 && separator != NULL) fputs(separator, fp); } fclose(fp); @@ -340,15 +359,15 @@ bool ReStringList::readFromFile(const char* filename, bool cutNewline){ */ int ReStringList::firstDiff(const ReStringList& toCompare) const{ int rc = -1; - for (size_t ix = 0; rc == -1 && ix < getCount(); ix++){ - if (ix >= toCompare.getCount()) + for (size_t ix = 0; rc == -1 && ix < count(); ix++){ + if (ix >= toCompare.count()) rc = (int) ix; else if (sizeOf(ix) != toCompare.sizeOf(ix) || strcmp(strOf(ix), toCompare.strOf(ix)) != 0) rc = (int) ix; } - if (rc == -1 && getCount() < toCompare.getCount()) - rc = getCount(); + if (rc == -1 && count() < toCompare.count()) + rc = count(); return rc; } /** @brief Tests the equality with another instance. @@ -362,6 +381,6 @@ int ReStringList::firstDiff(const ReStringList& toCompare) const{ * @return true: The other instance is equal. false: Otherwise. */ bool ReStringList::equal(const ReStringList& toCompare) const{ - bool rc = getCount() == toCompare.getCount() && firstDiff(toCompare) == -1; + bool rc = count() == toCompare.count() && firstDiff(toCompare) == -1; return rc; } diff --git a/base/ReStringList.hpp b/base/ReStringList.hpp index 3ead88d..dfde5ea 100644 --- a/base/ReStringList.hpp +++ b/base/ReStringList.hpp @@ -17,7 +17,7 @@ * * ReStringList listIn, listOut; * listIn.readFromFile("abc.csv"); - * for (int ii = 0; ii < listIn.getCount(); ii++){ + * for (int ii = 0; ii < listIn.count(); ii++){ * ReStringList cols; * cols.split(list.getCStr(ii), ','); * char number[20]; sprintf(number, "%d", ii + 1); @@ -30,14 +30,15 @@ */ class ReStringList : public ReSeqList { public: - ReStringList(); + ReStringList(int deltaList = 1024, int deltaBuffer = 1024); virtual ~ReStringList(); public: - void append(const char* source, Tag tag = 0); - void append(ReStringList& source); + ReStringList& append(const char* source, Tag tag = 0); + ReStringList& append(const ReByteBuffer& source, Tag tag = 0); + ReStringList& append(ReStringList& source); Index indexOf(const char* toFind, bool ignoreCase = false, Index start = 0) const; void insert(Index index, const char* source, Tag tag = 0); - void join(const char* separator, ReByteBuffer& result); + ReByteBuffer& join(const char* separator, ReByteBuffer& result) const; Index nextStartingWith(Index index, const char* prefix, bool ignoreCase = false); void replace(Index index, const char* source, Tag tag = 0); void replaceString(Index index, const char* source); diff --git a/base/ReTestUnit.cpp b/base/ReTestUnit.cpp index 8efab79..6fa2297 100644 --- a/base/ReTestUnit.cpp +++ b/base/ReTestUnit.cpp @@ -172,7 +172,7 @@ void ReTestUnit::createTestDir(){ #define ALLPERMS 0 #endif if (lstat(name, &info) != 0) - mkdir(name, ALLPERMS); + _mkdir(name); else{ char cmd[512 + 128]; snprintf(cmd, sizeof cmd, "rm -Rf %s*", name); @@ -205,7 +205,7 @@ void ReTestUnit::createFile(const char* filename, const char* content){ * @param content The content of the file. If NULL the file will be empty. */ void ReTestUnit::createDir(const char* filename){ - mkdir(filename, ALLPERMS); + _mkdir(filename); } diff --git a/base/rebase.hpp b/base/rebase.hpp index 8a6dd87..3deca3e 100644 --- a/base/rebase.hpp +++ b/base/rebase.hpp @@ -29,6 +29,7 @@ #define _strdup strdup #define _unlink unlink +#define _mkdir(path) mkdir(path, ALLPERMS) //#define _ #elif defined __WIN32__ diff --git a/cunit/cuReSeqList.cpp b/cunit/cuReSeqList.cpp index 8cee5f2..e09f42c 100644 --- a/cunit/cuReSeqList.cpp +++ b/cunit/cuReSeqList.cpp @@ -16,13 +16,13 @@ private: ReSeqList::Tag tag = 0; list.add(-1, "123", -1, 100); - checkEqu(1u, list.getCount()); + checkEqu(1u, list.count()); checkT(list.get(0, value, &tag)); checkEqu("123", value.str()); checkEqu((int64_t) 100, tag); list.add(-1, "ab", -1, 200); - checkEqu(2u, list.getCount()); + checkEqu(2u, list.count()); checkT(list.get(0, value)); checkEqu("123", value.str()); checkT(list.get(1, value, &tag)); @@ -30,7 +30,7 @@ private: checkEqu(200ll, tag); list.add(0, "xyz", -1, 300); - checkEqu(3u, list.getCount()); + checkEqu(3u, list.count()); checkT(list.get(0, value, &tag)); checkEqu("xyz", value.str()); checkT(list.get(1, value)); @@ -40,7 +40,7 @@ private: checkEqu(300ll, tag); list.add(1, "vw", -1, 400); - checkEqu(4u, list.getCount()); + checkEqu(4u, list.count()); checkT(list.get(0, value)); checkEqu("xyz", value.str()); checkT(list.get(1, value, &tag)); @@ -52,7 +52,7 @@ private: checkEqu(400ll, tag); list.clear(); - checkEqu(0u, list.getCount()); + checkEqu(0u, list.count()); checkF(list.get(0, value)); } void testRemove(){ @@ -66,7 +66,7 @@ private: list.add(-1, "jkl134", -1, 400); list.remove(3); - checkEqu(3u, list.getCount()); + checkEqu(3u, list.count()); list.get(0, value, &tag); checkEqu("abc", value.str()); checkEqu(100ll, tag); @@ -79,7 +79,7 @@ private: list.remove(1); - checkEqu(2u, list.getCount()); + checkEqu(2u, list.count()); list.get(0, value, &tag); checkEqu("abc", value.str()); checkEqu(100ll, tag); @@ -88,7 +88,7 @@ private: checkEqu(300ll, tag); list.remove(0); - checkEqu(1u, list.getCount()); + checkEqu(1u, list.count()); list.get(0, value, &tag); checkEqu("ghi", value.str()); checkEqu(300ll, tag); diff --git a/cunit/cuReStringList.cpp b/cunit/cuReStringList.cpp index 2bd4b66..23b527f 100644 --- a/cunit/cuReStringList.cpp +++ b/cunit/cuReStringList.cpp @@ -7,12 +7,25 @@ public: } private: void run(){ + testAppendByteBuffer(); testBase(); testReplace(); testJoin(); testEqu(); testFile(); } + void testAppendByteBuffer(){ + ReStringList list; + ReByteBuffer line; + line.set("Hi", -1); + list.append(line, 1ll).append(line.append("!", -1), -5ll); + checkEqu(2u, list.count()); + checkEqu(1ll, list.tagOf(0)); + checkEqu("Hi", list.strOf(0)); + + checkEqu(-5ll, list.tagOf(1)); + checkEqu("Hi!", list.strOf(1)); + } void testReplace(){ ReStringList list; @@ -104,7 +117,7 @@ private: ReStringList list; const char* str = "1;abc;xyz;4;;99"; list.split(str, ';'); - checkEqu(6U, list.getCount()); + checkEqu(6U, list.count()); checkEqu("1", list.strOf(0)); checkEqu("abc", list.strOf(1)); checkEqu("xyz", list.strOf(2)); @@ -116,13 +129,13 @@ private: checkEqu(str, value.str()); list.split("1\r\n2\n\r3", '\n'); - checkEqu(3U, list.getCount()); + checkEqu(3U, list.count()); checkEqu("1", list.strOf(0)); checkEqu("2", list.strOf(1)); checkEqu("3", list.strOf(2)); list.split("xyz\tXYZ", '\t', true); - checkEqu(5U, list.getCount()); + checkEqu(5U, list.count()); checkEqu("1", list.strOf(0)); checkEqu("2", list.strOf(1)); checkEqu("3", list.strOf(2)); @@ -155,7 +168,7 @@ private: list.append("a", 200); list.append("vwxyz", 300); - checkEqu(3U, list.getCount()); + checkEqu(3U, list.count()); int index = 0; checkEqu("123", list.strOf(index)); checkEqu(4U, list.sizeOf(index)); @@ -174,7 +187,7 @@ private: checkEqu(12U, list.sumOfSizes()); list.insert(0, "0", 50); - checkEqu(4U, list.getCount()); + checkEqu(4U, list.count()); checkEqu(14U, list.sumOfSizes()); index = 0; @@ -240,7 +253,7 @@ private: list3.append("y", 2000); list2.append(list3); - checkEqu(5u, list2.getCount()); + checkEqu(5u, list2.count()); checkEqu("a", list2.strOf(0)); checkEqu(100ll, list2.tagOf(0)); checkEqu("b", list2.strOf(1)); diff --git a/cunit/cuReTraverser.cpp b/cunit/cuReTraverser.cpp index 6869162..e4d448d 100644 --- a/cunit/cuReTraverser.cpp +++ b/cunit/cuReTraverser.cpp @@ -49,7 +49,8 @@ private: makeFile("dir1/dir1_2/dir1_2_1/x2.txt"); } void run(){ - initTree(); + initTree(); + testDirStatistic(); testBasic(); } void checkOneFile(const char* node, const char* parent, const ReHashList& hash){ @@ -64,23 +65,29 @@ private: void testBasic(){ ReTraverser traverser(m_base.str()); int level = 0; - DirStatus_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); - checkOneFile("dir1", m_base.str(), hash); - - //checkEqu("xy12.ab", nameOfEntry(entry)); + 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); + checkOneFile("dir1", m_base.str(), hash); + } + void testDirStatistic(){ + ReDirStatistic stat; + const ReStringList& list = stat.calculate(m_base.str(), 1); + ReByteBuffer buffer; + log(false, list.join("\n", buffer).str()); + checkEqu(3u, list.count()); + checkEqu("", list.strOf(0)); } }; extern void testReTraverser(void); diff --git a/os/DirTools.hpp b/os/DirTools.hpp new file mode 100644 index 0000000..9504115 --- /dev/null +++ b/os/DirTools.hpp @@ -0,0 +1,45 @@ +/* + * DirTools.hpp + * + * Created on: 30.12.2014 + * Author: hm + */ + +#ifndef OS_DIRTOOLS_HPP_ +#define OS_DIRTOOLS_HPP_ + +class ReDirStatisticData{ +public: + ReDirStatisticData(); + ReDirStatisticData(const ReDirStatisticData& source); + ReDirStatisticData& operator =(const ReDirStatisticData& source); +public: + void clear(); + ReDirStatisticData& add(const ReDirStatisticData& source); +public: + int64_t m_sizes; + int m_files; + int m_dirs; + ReByteBuffer m_path; +}; + +class ReDirStatistic; +extern void formatLikeDu(const ReDirStatisticData& data, ReDirStatistic& parent, + ReByteBuffer& line); +/** + * Calculates a statistic of a directory tree. + */ +class ReDirStatistic { +public: + ReDirStatistic(int deltaList = 512, int deltaBuffer = 0x10000); + ~ReDirStatistic(); +public: + const ReStringList& calculate(const char* base, int depth, + void (*format)(const ReDirStatisticData& data, ReDirStatistic& parent, + ReByteBuffer& line) = formatLikeDu); +private: + ReStringList m_list; +}; + + +#endif /* OS_DIRTOOLS_HPP_ */ diff --git a/os/ReDirTools.cpp b/os/ReDirTools.cpp new file mode 100644 index 0000000..cab4336 --- /dev/null +++ b/os/ReDirTools.cpp @@ -0,0 +1,165 @@ +/* + * DirTools.cpp + * + * Created on: 30.12.2014 + * Author: hm + */ + +#include "base/rebase.hpp" +#include "os/reos.hpp" + +/** + * Constructor. + */ +ReDirStatisticData::ReDirStatisticData() : + m_sizes(0), + m_files(0), + m_dirs(0), + m_path() +{ +} +/** + * Copy constructor. + * + * @param source the source to copy + */ +ReDirStatisticData::ReDirStatisticData(const ReDirStatisticData& source) : + m_sizes(source.m_sizes), + m_files(source.m_files), + m_dirs(source.m_dirs), + 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_dirs = source.m_dirs; + m_path = source.m_path; + return *this; +} +/** + * Initializes the data of the instance. + */ +void ReDirStatisticData::clear(){ + m_sizes = 0; + m_files = 0; + m_dirs = 0; + m_path.setLength(0); +} + +/** + * 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_dirs += source.m_dirs; + return *this; +} + +/** + * Constructor. + */ +ReDirStatistic::ReDirStatistic(int deltaList, int deltaBuffer) : + m_list(deltaList, deltaBuffer) +{ +} +/** + * Destructor. + */ +ReDirStatistic::~ReDirStatistic(){ + +} + +/** + * 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)){ + ReTraverser traverser(base); + if (level > 1024) + level = 1024; + else if (level < 0) + level = 0; + 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; + while( (entry = traverser.rawNextFile(currentDepth))){ + if (! entry->m_path.equals(current->m_path)){ + if (currentDepth <= topOfStack){ + // close the entries of the stack above the new current: + while(topOfStack > currentDepth){ + // Add the data to the parent: + dataStack[topOfStack - 1]->add(*dataStack[topOfStack]); + // Append it to the result: + (*formatter)(*dataStack[topOfStack - 1], *this, line); + m_list.append(line); + } + } else if (currentDepth < level){ + // 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_dirs++; + } else { + current->m_sizes += entry->fileSize(); + current->m_files++; + } + } + while(topOfStack > 0){ + // Add the data to the parent: + dataStack[topOfStack - 1]->add(*dataStack[topOfStack]); + // Append it to the result: + (*formatter)(*dataStack[topOfStack - 1], *this, line); + m_list.append(line); + } + // free the resources: + for (int ix = 0; ix <= level; ix++) + delete dataStack[ix]; + delete[] dataStack; + return m_list; +} + +/** + * Formats a line like the du (disk usage) command. + * + * This is a possible parameter of ReDirStatistic::calculate(). + * + * @param data statistic data, including path name + * @param parent the caller (ReDirStatistic). This allows to deliver + * a context to this formatting routine (through derivation of + * ReDirStatistic) + * @param line OUT: the formatted line, the conclusion of the statistic data + */ +void formatLikeDu(const ReDirStatisticData& data, + ReDirStatistic& parent, ReByteBuffer& line){ + line.setLength(0); + line.append(int(data.m_sizes / 1024)).append("\t").append(data.m_path); +} diff --git a/os/ReTraverser.cpp b/os/ReTraverser.cpp index 052d38c..c870d9c 100644 --- a/os/ReTraverser.cpp +++ b/os/ReTraverser.cpp @@ -17,37 +17,73 @@ const char* const ReTraverser::m_separatorStr = "\\"; #endif #ifdef __linux__ -#define isUndefHandle(handle) ((handle) == NULL) -#define findFirstEntry(path, data) opendir(path) -#define findNextEntry(handle,data) (((data) = readdir(handle)) != NULL) +//#define isUndefHandle(handle) ((handle) == NULL) +//#define findNextEntry(handle,data) (((data) = readdir(handle)) != NULL) #define closeDir(handle) closedir(handle) -#define initEntryBuffer(entry) -#define setHandleUndef(h) ((h) = NULL) +//#define setHandleUndef(h) ((h) = NULL) #else #define isUndefHandle(handle) ((handle) == INVALID_HANDLE_VALUE) #define setHandleUndef(h) ((h) = INVALID_HANDLE_VALUE) -HANDLE findFirstEntry(const char* path, DirInfoStruct_t* data){ - ReByteBuffer thePath(path); - thePath.append("\\*"); - HANDLE rc = FindFirstFileA(thePath.str(), data); - return rc; -} #define findNextEntry(handle, data) (FindNextFileA(handle, data) != 0) -#define closeDir(handle) FindClose(handle) #define initEntryBuffer(entry) ((entry)->m_data = &(entry)->m_dataBuffer) #endif -const char* DirStatus_t::node() const{ +ReDirStatus_t::ReDirStatus_t() : +#ifdef __linux__ + m_handle(NULL), + m_data(NULL) + //m_status; +#elif defined WIN32 + m_handle(INVALID_HANDLE_VALUE) + //m_data; +#endif +{ +#ifdef __linux__ + memset(&m_status, 0, sizeof m_status); +#elif defined WIN32 + memset(&m_data, 0, sizeof m_data); +#endif +} + +/** + * 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){ + CloseDir(m_handle); + m_handle = INVALID_HANDLE_VALUE; + } +#endif + m_path.setLength(0); +} + +/** + * 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 } -bool DirStatus_t::isDotDir() const{ +/** + * Tests whether the instance contains data about "." or "..". + * + * @return true: an ignorable entry has been found + */ +bool ReDirStatus_t::isDotDir() const{ #ifdef __linux__ - bool rc = m_data->d_name[0] == '.' && (m_data->d_name[1] == '\0' - || (m_data->d_name[1] == '.' && m_data->d_name[2] == '\0')); + 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')); @@ -55,14 +91,62 @@ bool DirStatus_t::isDotDir() const{ return rc; } -bool DirStatus_t::isDirectory() { +/** + * Loads the info about the first file into the instance. + * + * @return true: success + */ +bool ReDirStatus_t::findFirst(){ + bool rc = false; +#if defined __linux__ + if (m_handle != NULL) + closedir(m_handle); + m_handle = opendir(m_path.str()); + rc = m_handle != NULL && (m_data = readdir(m_handle)) != NULL; +#elif defined __WIN32__ + if (m_handle != INVALID_HANDLE_VALUE) + FindClose(m_handle); + ReByteBuffer thePath(m_path); + thePath.append("\\*"); + m_handle = FindFirstFileA(thePath.str(), &m_data); + rc = m_handle != INVALID_HANDLE_VALUE; +#endif + return rc; +} + +/** + * Loads the info about the next file into the instance. + * + * @return true: success + */ +bool ReDirStatus_t::findNext(){ +#if defined __linux__ + bool rc = m_handle != NULL && (m_data = readdir(m_handle)) != NULL; +#elif defined __WIN32__ + bool rc = m_handle != INVALID_HANDLE_VALUE && FindNextFileA(m_handle, &m_data); +#endif + return rc; +} +/** + * Tests whether the instance is a directory. + * + * @return true: instance contains the data of a directory + */ +bool ReDirStatus_t::isDirectory() { #ifdef __linux__ return (m_data->d_type != DT_UNKNOWN && m_data->d_type == DT_DIR) || S_ISDIR(getStatus()->st_mode); #elif defined __WIN32__ return 0 != (m_dataBuffer.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); #endif } -bool DirStatus_t::isLink() { +/** + * Tests whether the instance is a symbolic link. + * + * Unter windows it tests whether the the instance is a reparse point. + * + * @return true: instance contains the data of a link + */ +bool ReDirStatus_t::isLink() { bool rc; #ifdef __linux__ rc = (m_data->d_type != DT_UNKNOWN && m_data->d_type == DT_LNK) || S_ISLNK(getStatus()->st_mode); @@ -71,21 +155,36 @@ bool DirStatus_t::isLink() { #endif return rc; } -bool DirStatus_t::isRegular() { +/** + * Tests whether the instance is a "normal" file. + * + * @return true: instance contains the data of a not special file + */ +bool ReDirStatus_t::isRegular() { #ifdef __linux__ return (m_data->d_type != DT_UNKNOWN && m_data->d_type == DT_REG) || S_ISREG(getStatus()->st_mode); #elif defined __WIN32__ return 0 != (m_dataBuffer.dwFileAttributes & (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE)); #endif } -FileSize_t DirStatus_t::fileSize() { +/** + * Returns the filesize. + * + * @return the filesize + */ +FileSize_t ReDirStatus_t::fileSize() { #ifdef __linux__ return getStatus()->st_size; #elif defined __WIN32__ return ((int64_t) m_dataBuffer.nFileSizeHigh << 32) + m_dataBuffer.nFileSizeLow; #endif } -const FileTime_t* DirStatus_t::modified() { +/** + * Returns the modification time. + * + * @return the modification time + */ +const FileTime_t* ReDirStatus_t::modified() { #ifdef __linux__ return &(getStatus()->st_mtime); #elif defined __WIN32__ @@ -93,7 +192,7 @@ const FileTime_t* DirStatus_t::modified() { #endif } -time_t DirStatus_t::filetimeToTime(const FileTime_t* filetime){ +time_t ReDirStatus_t::filetimeToTime(const FileTime_t* filetime){ #ifdef __linux__ return *filetime; #elif defined __WIN32__ @@ -109,14 +208,19 @@ time_t DirStatus_t::filetimeToTime(const FileTime_t* filetime){ return (time_t) (date.QuadPart / 10000000); #endif } - -void DirStatus_t::timeToFiletime(time_t time, FileTime_t& filetime){ +/** + * 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, FileTime_t& filetime){ #ifdef __linux__ filetime = time; #elif defined __WIN32__ LONGLONG ll = Int32x32To64(time, 10000000) + 116444736000000000; filetime.dwLowDateTime = (DWORD)ll; - filetime.dwHighDateTime = ll >> 32;; + filetime.dwHighDateTime = ll >> 32; #endif } @@ -138,7 +242,7 @@ DirEntryFilter_t::DirEntryFilter_t() : /** * */ -bool DirEntryFilter_t::match(DirStatus_t& entry){ +bool DirEntryFilter_t::match(ReDirStatus_t& entry){ bool rc = false; do { if (! m_directories && entry.isDirectory()) @@ -151,9 +255,9 @@ bool DirEntryFilter_t::match(DirStatus_t& entry){ break; if (m_maxSize >= 0 && entry.fileSize() < m_maxSize) break; - if (m_minAge != 0 && DirStatus_t::filetimeToTime(entry.modified()) < m_minAge) + if (m_minAge != 0 && ReDirStatus_t::filetimeToTime(entry.modified()) < m_minAge) break; - if (m_maxAge != 0 && DirStatus_t::filetimeToTime(entry.modified()) > m_maxAge) + if (m_maxAge != 0 && ReDirStatus_t::filetimeToTime(entry.modified()) > m_maxAge) break; if (m_nodePatterns != NULL && ! m_nodePatterns->match(entry.node())) break; @@ -170,7 +274,7 @@ bool DirEntryFilter_t::match(DirStatus_t& entry){ * * @return the status of the current file */ -struct stat* DirStatus_t::getStatus() { +struct stat* ReDirStatus_t::getStatus() { if (m_status.st_ino == 0) if (stat(m_data->d_name, &m_status) != 0) memset((void*) &m_status, 0, sizeof m_status); @@ -189,12 +293,21 @@ ReTraverser::ReTraverser(const char* base) : // m_dirs m_passNoForDirSearch(2) { + memset(m_dirs, 0, sizeof m_dirs); + m_dirs[0] = new ReDirStatus_t(); } /** * Destructor. */ ReTraverser::~ReTraverser() { + 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; + } + } } /** @@ -207,9 +320,9 @@ ReTraverser::~ReTraverser() { * otherwise: the stack entry with the next file in the * directory tree. May be a directory too */ -DirStatus_t* ReTraverser::rawNextFile(int& level) +ReDirStatus_t* ReTraverser::rawNextFile(int& level) { - DirStatus_t* rc = NULL; + ReDirStatus_t* rc = NULL; bool alreadyRead = false; bool again; do{ @@ -218,18 +331,18 @@ DirStatus_t* ReTraverser::rawNextFile(int& level) // first call: if (initEntry(m_base.str(), NULL, 0)){ if (1 != m_passNoForDirSearch) - rc = &m_dirs[0]; + rc = m_dirs[0]; else again = alreadyRead = true; } } else { - DirStatus_t* current = &m_dirs[m_level]; - if (alreadyRead || findNextEntry(current->m_handle, current->m_data)){ - alreadyRead = false; + ReDirStatus_t* current = m_dirs[m_level]; + if (alreadyRead || current->findNext()){ + alreadyRead = false; // a file or directory found: if (current->m_passNo != m_passNoForDirSearch){ // we search for any file: - rc = &m_dirs[m_level]; + rc = m_dirs[m_level]; } else { // we are interested only in subdirectories: again = true; @@ -247,8 +360,7 @@ DirStatus_t* ReTraverser::rawNextFile(int& level) again = true; } else { // this subdirectory is complete. We continue in the parent directory: - closeDir(current->m_handle); - setHandleUndef(current->m_handle); + current->freeEntry(); if ( --m_level > 0){ again = true; } @@ -273,8 +385,8 @@ DirStatus_t* ReTraverser::rawNextFile(int& level) * otherwise: the info about the next file in the * directory tree */ -DirStatus_t* ReTraverser::nextFile(int& level, DirEntryFilter_t* filter){ - DirStatus_t* rc = rawNextFile(level); +ReDirStatus_t* ReTraverser::nextFile(int& level, DirEntryFilter_t* filter){ + ReDirStatus_t* rc = rawNextFile(level); while (rc != NULL){ if (filter == NULL || filter->match(*rc)){ break; @@ -299,8 +411,9 @@ bool ReTraverser::initEntry(const ReByteBuffer& parent, const char* node, int le if (level < MAX_ENTRY_STACK_DEPTH){ if (level >= 0) m_level = level; - DirStatus_t* current = &m_dirs[m_level]; - initEntryBuffer(current); + if (m_dirs[m_level] == NULL) + m_dirs[m_level] = new ReDirStatus_t(); + ReDirStatus_t* current = m_dirs[m_level]; current->m_passNo = 1; if (level >= 0){ current->m_path.set(parent.str(), parent.length()); @@ -309,25 +422,9 @@ bool ReTraverser::initEntry(const ReByteBuffer& parent, const char* node, int le if (node != NULL) current->m_path.append(node).append(m_separatorStr); } - current->m_handle = findFirstEntry(current->m_path.str(), current->m_data); - rc = ! isUndefHandle(current->m_handle); + rc = current->findFirst(); } return rc; } -/** - * Frees the resources of an entry of the directory entry stack. - * - * @param level the index of the entry in the stack - */ -void ReTraverser::freeEntry(int level){ - if (level < MAX_ENTRY_STACK_DEPTH){ - DirStatus_t* current = &m_dirs[level]; - if (! isUndefHandle(current->m_handle)){ - closeDir(current->m_handle); - setHandleUndef(current->m_handle); - } - current->m_path.setLength(0); - } -} diff --git a/os/ReTraverser.hpp b/os/ReTraverser.hpp index 0c928af..566f696 100644 --- a/os/ReTraverser.hpp +++ b/os/ReTraverser.hpp @@ -11,32 +11,24 @@ #include "string/ReMatcher.hpp" #ifdef __linux__ #include - #include +#include typedef DIR* FindFileHandle_t; typedef __off_t FileSize_t; -typedef struct dirent DirInfoStruct_t; typedef time_t FileTime_t; -#if 0 -#define nameOfEntry(entry) ((entry)->m_data->d_name) -#define isDirEntry(entry) (((entry)->m_data->d_type != DT_UNKNOWN && (entry)->m_data->d_type == DT_DIR) \ - || S_ISDIR((entry)->getStatus()->st_mode)) -#define isLinkEntry(entry) (((entry)->m_data->d_type != DT_UNKNOWN && (entry)->m_data->d_type == DT_LINK) \ - || S_ISLNK(entry.getStatus()->st_mode))) -#define isRegularEntry(entry) (((entry)->m_data->d_type != DT_UNKNOWN && (entry)->m_data->d_type == DT_REG) \ - || S_ISREG((entry)->getStatus()->st_mode)) -#define sizeOfEntry(entry) ((entry)->getStatus()->st_size) -#define modifiedOfEntry(entry) ((entry)->getStatus()->st_mtime) -#endif #else typedef int64_t FileSize_t; typedef FILETIME FileTime_t; -typedef HANDLE FindFileHandle_t; -typedef WIN32_FIND_DATAA DirInfoStruct_t; #endif -class DirStatus_t{ +class ReDirStatus_t{ +public: + ReDirStatus_t(); public: + void freeEntry(); const char* node() const; + bool findFirst(); + bool findNext(); + bool hasData() const; bool isDirectory(); bool isLink(); bool isRegular(); @@ -45,17 +37,16 @@ public: bool isDotDir() const; public: ReByteBuffer m_path; - // a pointer to the data: - DirInfoStruct_t* m_data; - FindFileHandle_t m_handle; int m_passNo; -#if defined __linux__ +#ifdef __linux__ + DIR* m_handle; + struct dirent* m_data; struct stat m_status; public: struct stat* getStatus(); #elif defined WIN32 - // the buffer for m_data: - DirInfoStruct_t m_dataBuffer; + HANDLE m_handle; + WIN32_FIND_DATAA m_data; #endif public: static time_t filetimeToTime(const FileTime_t* time); @@ -66,7 +57,7 @@ public: DirEntryFilter_t(); ~DirEntryFilter_t(); public: - bool match(DirStatus_t& entry); + bool match(ReDirStatus_t& entry); public: bool m_regulars; bool m_specials; @@ -84,8 +75,8 @@ public: ReTraverser(const char* base); virtual ~ReTraverser(); public: - DirStatus_t* rawNextFile(int& level); - DirStatus_t* nextFile(int& level, DirEntryFilter_t* filter = NULL); + ReDirStatus_t* rawNextFile(int& level); + ReDirStatus_t* nextFile(int& level, DirEntryFilter_t* filter = NULL); protected: bool initEntry(const ReByteBuffer& parent, const char* node, int level); void freeEntry(int level); @@ -93,7 +84,7 @@ protected: protected: int m_level; ReByteBuffer m_base; - DirStatus_t m_dirs[MAX_ENTRY_STACK_DEPTH]; + ReDirStatus_t* m_dirs[MAX_ENTRY_STACK_DEPTH]; /// each directory will be passed twice: for all files + for directories only /// 1: depth first 2: breadth first int m_passNoForDirSearch; diff --git a/os/reos.hpp b/os/reos.hpp index 4d513c7..e8ccc68 100644 --- a/os/reos.hpp +++ b/os/reos.hpp @@ -20,6 +20,6 @@ #endif #include "os/ReTraverser.hpp" - +#include "os/DirTools.hpp" #endif /* OS_REOS_HPP_ */ diff --git a/string/ReMatcher.cpp b/string/ReMatcher.cpp index 3e613ea..aafb33f 100644 --- a/string/ReMatcher.cpp +++ b/string/ReMatcher.cpp @@ -97,7 +97,7 @@ bool ReSimpleMatcher::match(const ReByteBuffer& toTest, ReHit* hit) const{ break; } // Does the tail match? - int last = m_tokens.getCount() - 1; + int last = m_tokens.count() - 1; if (last == 0){ rc = true; break; @@ -175,7 +175,7 @@ bool ReSimpleMatcher::searchTokens(const ReByteBuffer& toTest, int from, int to, bool ReSimpleMatcher::search(const ReByteBuffer& toTest, ReHit* hit, bool greedy) const{ bool rc = m_findAll; if (! rc){ - size_t last = m_tokens.getCount(); + size_t last = m_tokens.count(); size_t length0 = m_tokens.strLengthOf(0); size_t lengthLast = m_tokens.strLengthOf(last); rc = searchTokens(toTest, length0 == 0 ? 1 : 0,