From 744df3fc526622a7781e3f8401e90551cb02120c Mon Sep 17 00:00:00 2001 From: hama Date: Mon, 16 Feb 2015 00:40:56 +0100 Subject: [PATCH] Heavy refactoring ReDirTools --- base/ReByteBuffer.cpp | 168 +++++- base/ReByteBuffer.hpp | 5 + cunit/cuReByteBuffer.cpp | 24 + cunit/cuReDirTools.cpp | 3 +- cunit/cuReTraverser.cpp | 3 +- cunit/testall.cpp | 2 +- math/md5.cpp | 2 +- os/ReDirTools.cpp | 1072 ++++++++++++++++++++++---------------- os/ReDirTools.hpp | 127 +++-- os/ReTraverser.cpp | 62 ++- os/ReTraverser.hpp | 32 +- 11 files changed, 963 insertions(+), 537 deletions(-) diff --git a/base/ReByteBuffer.cpp b/base/ReByteBuffer.cpp index ded7ee8..65a01f0 100644 --- a/base/ReByteBuffer.cpp +++ b/base/ReByteBuffer.cpp @@ -44,7 +44,8 @@ ReByteBuffer::ReByteBuffer(const char* source) : m_reallocation(0) { m_buffer[0] = '\0'; - append(source); + if (source != NULL) + append(source); } /** @brief Constructor. @@ -124,49 +125,40 @@ ReByteBuffer& ReByteBuffer::append(const Byte* source, size_t length){ m_buffer[m_length] = '\0'; return *this; } -/** @brief Appends an integer as string at the end of the buffer. +/** @brief Appends the content of another ReByteBuffer instance at the end of the buffer. * * If the space is not enough it will be allocated. * *
Example:
- * ReByteBuffer buf;
- * buf.appendInt(33, 4);
- * assert(strcmp("33  ", buf.getBuffer()) == 0);
+ * ReByteBuffer name; name.append("mydoc");
+ * ReByteBuffer ext; ext.append(".txt");
+ * name.append(ext);
+ * assert("mydoc.txt", name.getBuffer());
  * 
* - * @param number the number to append - * @param format the format of the number used by sprintf() - * * @return *this (for chaining) */ -ReByteBuffer& ReByteBuffer::appendInt(int number, const char* format){ - char buffer [128]; - - _snprintf(buffer, sizeof buffer, format, number); - size_t length = strlen(buffer); - - ensureSize(m_length + length); - memcpy(m_buffer + m_length, buffer, length); - m_length += length; - m_buffer[m_length] = '\0'; - return *this; +ReByteBuffer& ReByteBuffer::append(const ReByteBuffer& source){ + return append(source.str(), source.length()); } -/** @brief Appends the content of another ReByteBuffer instance at the end of the buffer. +/** @brief Appends a floaating point number at the end of the buffer. * * If the space is not enough it will be allocated. * *
Example:
- * ReByteBuffer name; name.append("mydoc");
- * ReByteBuffer ext; ext.append(".txt");
- * name.append(ext);
- * assert("mydoc.txt", name.getBuffer());
+ * ReByteBuffer buf; buf.append("amount: ").append(377 / 100.0, "%5.2f");
+ * assert("amount:   3.77", buf.str());
  * 
* * @return *this (for chaining) */ -ReByteBuffer& ReByteBuffer::append(const ReByteBuffer& source){ - return append(source.str(), source.length()); +ReByteBuffer& ReByteBuffer::append(double value, const char* format){ + char buffer [256]; + + _snprintf(buffer, sizeof buffer, format, value); + append(buffer, -1); + return *this; } /** @@ -214,7 +206,7 @@ ReByteBuffer& ReByteBuffer::appendHexDump(const char* data, size_t length, int ix; for (ix = 0; ix < bytesPerLine; ix++){ if (ix < (int) length) - appendInt((unsigned) data[ix] % 0xff, "%02x"); + appendInt((unsigned char) data[ix], "%02x"); else append(" "); if (ix < bytesPerLine - 1 @@ -246,6 +238,94 @@ ReByteBuffer& ReByteBuffer::appendHexDump(const char* data, size_t length, return *this; } +/** @brief Appends an integer as string at the end of the buffer. + * + * If the space is not enough it will be allocated. + * + *
Example:
+ * ReByteBuffer buf; buf.appendInt(33, "%-4d");
+ * assert(strcmp("33  ", buf.str()) == 0);
+ * 
+ * + * @param number the number to append + * @param format the format of the number used by sprintf() + * + * @return *this (for chaining) + */ +ReByteBuffer& ReByteBuffer::appendInt(int number, const char* format){ + char buffer [128]; + + _snprintf(buffer, sizeof buffer, format, number); + append(buffer, -1); + return *this; +} + +/** @brief Appends an integer as string at the end of the buffer. + * + * If the space is not enough it will be allocated. + * + *
Example:
+ * ReByteBuffer buf; buf.appendInt(33U, "%x");
+ * assert(strcmp("21  ", buf.str()) == 0);
+ * 
+ * + * @param number the number to append + * @param format the format of the number used by sprintf() + * + * @return *this (for chaining) + */ +ReByteBuffer& ReByteBuffer::appendInt(unsigned int number, const char* format){ + char buffer [128]; + + _snprintf(buffer, sizeof buffer, format, number); + append(buffer, -1); + return *this; +} + +/** @brief Appends an integer as string at the end of the buffer. + * + * If the space is not enough it will be allocated. + * + *
Example:
+ * ReByteBuffer buf; buf.appendInt(12345678901ll, "%12d");
+ * assert(strcmp(" 12345678901", buf.str()) == 0);
+ * 
+ * + * @param number the number to append + * @param format the format of the number used by sprintf() + * + * @return *this (for chaining) + */ +ReByteBuffer& ReByteBuffer::appendInt(int64_t number, const char* format){ + char buffer [256]; + + _snprintf(buffer, sizeof buffer, format, number); + append(buffer, -1); + return *this; +} + +/** @brief Appends an integer as string at the end of the buffer. + * + * If the space is not enough it will be allocated. + * + *
Example:
+ * ReByteBuffer buf; buf.appendInt((uint64_t) 0x12345678901ll, "%12x");
+ * assert(strcmp(" 12345678901", buf.str()) == 0);
+ * 
+ * + * @param number the number to append + * @param format the format of the number used by sprintf() + * + * @return *this (for chaining) + */ +ReByteBuffer& ReByteBuffer::appendInt(uint64_t number, const char* format){ + char buffer [256]; + + _snprintf(buffer, sizeof buffer, format, number); + append(buffer, -1); + return *this; +} + /** @brief Appends a time (given in milli seconds) as 'dd:HH:MM.mmm'. * * @param time the time (duration) in msec @@ -282,6 +362,40 @@ ReByteBuffer& ReByteBuffer::appendMilliSec(int time, int minLength){ return *this; } +/** @brief Appends a time (given in milli seconds) as 'dd:HH:MM.mmm'. + * + * @param time the time (duration) in msec + * @param minLength > 3: seconds will be 2 digits (preceeding '0' if needed)
+ * > 4: at least MM:SS.sss
+ * > 7: at least HH:MM:SS.sss
+ * > 10: at least dd:HH:MM:SS
+ * @return buffer (for chaining) + */ +ReByteBuffer& ReByteBuffer::appendTime(int time, int minLength){ + int days = time / (24 * 3600); + time %= 24 * 3600; + int expectedLength = days > 0 ? 10 : 0; + int hours = time / 3600; + time %= 3600; + if (hours > 0 && expectedLength == 0) + expectedLength = 7; + int minutes = time / 60; + time %= 60; + if (minutes > 0 && expectedLength == 0) + expectedLength = 3; + int minimum = minLength > expectedLength ? minLength : expectedLength; + int sec = time; + static const char* stdFormat = "%02d"; + if (days > 0 || minimum > 10) + appendInt(days).append(":"); + if (hours > 0 || minimum > 7) + appendInt(hours, stdFormat).append(":"); + if (minutes > 0 || minimum > 4) + appendInt(minutes, stdFormat).append(":"); + appendInt(sec, sec > 10 || minimum > 3 ? stdFormat : "%d"); + return *this; +} + /** Converts a subsequence into an integer. * * The number may be a decimal or a hexadecimal number. Hex numbers start with 0x. diff --git a/base/ReByteBuffer.hpp b/base/ReByteBuffer.hpp index dcf1f01..69feda2 100644 --- a/base/ReByteBuffer.hpp +++ b/base/ReByteBuffer.hpp @@ -46,12 +46,17 @@ public: public: ReByteBuffer& append(const Byte* source, size_t length = -1); ReByteBuffer& append(const ReByteBuffer& source); + ReByteBuffer& append(double, const char* format = "%f"); ReByteBuffer& appendHexDump(const char* data, size_t length = -1, int offset = 0, int bytePerLine = 16, const char* offsetFormat = "%04x: ", bool withAscii = true, int groupWidth = 1, int gapBehind = -1, const char* separator = " | "); ReByteBuffer& appendInt(int number, const char* format = "%d"); + ReByteBuffer& appendInt(unsigned int number, const char* format = "%d"); + ReByteBuffer& appendInt(int64_t number, const char* format = "%lld"); + ReByteBuffer& appendInt(uint64_t number, const char* format = "%lld"); ReByteBuffer& appendMilliSec(int time, int minLength = 5); + ReByteBuffer& appendTime(int time, int minLength = 1); /** @brief Returns the n-th byte of the internal buffer. * @param index The index of the wanted byte. * @return 0: Wrong index. Otherwise: The byte from the wanted position. diff --git a/cunit/cuReByteBuffer.cpp b/cunit/cuReByteBuffer.cpp index 63b8171..b4fe8be 100644 --- a/cunit/cuReByteBuffer.cpp +++ b/cunit/cuReByteBuffer.cpp @@ -15,6 +15,8 @@ public: } private: void run(){ + testAppendFloat(); + testAppendInt64(); testEnsureSize2(); testHexDump(); testFill(); @@ -38,6 +40,22 @@ private: testSplice(); testReplace(); } + void testAppendFloat(){ + ReByteBuffer buffer; + + buffer.append(125 / 100.0); + checkEqu("1.250000", buffer.str()); + buffer.append(0.333, "%.2f"); + checkEqu("1.2500000.33", buffer.str()); + } + void testAppendInt64(){ + ReByteBuffer buffer; + buffer.appendInt((int64_t) 12345678901ll); + checkEqu("12345678901", buffer.str()); + buffer.appendInt((uint64_t) 0x123456789ll, "%llx"); + checkEqu("12345678901123456789", buffer.str()); + + } void testEnsureSize2(){ ReByteBuffer buffer; buffer.setLength(PRIMARY_BUFFER_SIZE - 1); @@ -301,6 +319,12 @@ private: buffer.appendInt(9, "%03d"); checkEqu("-1009", buffer.str()); checkEqu(5u, buffer.length()); + + buffer.setLength(0).appendInt((unsigned int) 123); + checkEqu("123", buffer.str()); + buffer.appendInt((unsigned int) 0x87654321, "%x"); + checkEqu("12387654321", buffer.str()); + } void testOpAssignCopyConstructor() { ReByteBuffer buf1; diff --git a/cunit/cuReDirTools.cpp b/cunit/cuReDirTools.cpp index 0ee0a79..6810241 100644 --- a/cunit/cuReDirTools.cpp +++ b/cunit/cuReDirTools.cpp @@ -73,9 +73,8 @@ private: testToolSync(); } void testList(){ - ReDirTools tools; const char* argv[] = { "list", m_base.str(), NULL }; - tools.list(2, argv); + ReDirList().run(2, argv); } void testCopyFile(){ #if defined __linux__ diff --git a/cunit/cuReTraverser.cpp b/cunit/cuReTraverser.cpp index abddd2b..39b740a 100644 --- a/cunit/cuReTraverser.cpp +++ b/cunit/cuReTraverser.cpp @@ -67,9 +67,8 @@ private: testList(); } void testList(){ - ReDirTools tools; const char* argv[] = { "list", m_base.str(), NULL }; - tools.list(2, argv); + ReDirList().run(2, argv); } void testCopyFile(){ #if defined __linux__ diff --git a/cunit/testall.cpp b/cunit/testall.cpp index cabbccf..f9ca28a 100644 --- a/cunit/testall.cpp +++ b/cunit/testall.cpp @@ -73,7 +73,7 @@ void testMath(){ void testAll(){ try { - testMath(); + testOs(); testBase(); testString(); diff --git a/math/md5.cpp b/math/md5.cpp index 9584c7f..7423820 100644 --- a/math/md5.cpp +++ b/math/md5.cpp @@ -83,7 +83,7 @@ static int s_ix = 0; // FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. // Rotation is separate from addition to prevent recomputation. inline void MD5::FF(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac) { -#define TRACE_MD5 +//#define TRACE_MD5 #if defined TRACE_MD5 printf("%2d: A: %08x B: %08x C: %08x D%08x\n", s_ix++ % 16, a, b, c, d); printf(" K[%2d]: %08x M[?]: %08x shift: %02d\n", diff --git a/os/ReDirTools.cpp b/os/ReDirTools.cpp index 10553eb..ee02528 100644 --- a/os/ReDirTools.cpp +++ b/os/ReDirTools.cpp @@ -36,11 +36,12 @@ static const char* s_helpSummary[] = { "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", NULL }; const char* s_batchUsage[] = { - ": batch", + ": batch [] [ ...]", " produces output usable for a batch file (script)", " all found files can be processed with a given script", " each line starts (usually) with a script name (see -c)", @@ -55,19 +56,18 @@ const char* s_batchExamples[] = { }; const char* s_listUsage[] = { - ": list", + ": l(ist) [] [ ...]", " lists the metadata (size, modification date ...) of the selected files", NULL }; const char* s_listExamples[] = { "dirtool list --min-size=10M e:\\data", - "dirtool list --type=f -y7d --size=10M -p;*.cpp;*.hpp;Makefile*;-*~ /home/data" , + "dirtool li --type=f -y7d --size=10M -p;*.cpp;*.hpp;Makefile*;-*~ /home/data", NULL }; static const char* s_statisticUsage[] = { - ":" - "st(atistic) [] []", + ": st(atistic) [] [ ...] []", " shows a statistic about a directory tree", " a directory path: relative or absolute", " 0: only the summary of will be shown", @@ -79,14 +79,13 @@ static const char* s_statisticUsage[] = { }; const char* s_statisticExamples[] = { "dirtool st -q -t0 e:\\windows", - "dirtool statistic --quiet --kbyte --trace-interval=60 ../mail 2", + "dirtool statistic --quiet --kbyte --trace-interval=60 ../mail ../remember 2", "dirtool stat -q --kbyte d:data 2", NULL }; const char* s_syncUsage[] = { - ":", - "sy(nchronize) [ ...] ", + ": sy(nchronize) [ ...] ", " Synchronizes the content of a directory tree with another.", " Newer or missing files will be copied", " If a source name ends with the separator (/ in linux, \\ in win) the target", @@ -102,6 +101,18 @@ const char* s_syncExamples[] = { NULL }; +const char* s_touchUsage[] = { + ": touch [] [ ...]", + " sets the filetimes (modification and/or access time) of the selected files", + NULL +}; +const char* s_touchExamples[] = { + "dirtool touch -p;*.csv -m2014.12.1/8:12 e:\\data e:\\history\\readme.txt", + "dirtool touch -p;*.cpp --modified=now-4d12H /home/data" , + "dirtool touch -p;*.csv -m+1m . e:\\data e:\\history\\readme.txt", + NULL +}; + /** * Constructor. * @@ -227,10 +238,10 @@ void ReDirOptions::addStandardFilterOptions(){ * Checks whether the given value is a time expression. * * Possible: - * Units: m(inutes) h(our) d(ays) + * Units: m(inutes) h(our) d(ays) s(econds) * * @param value value to check - * @return the value converted into the absolute time + * @return the value converted into the absolute (or relative) time * @throws ReOptionExecption */ time_t ReDirOptions::checkDate(const char* value){ @@ -241,10 +252,12 @@ time_t ReDirOptions::checkDate(const char* value){ int year, month, day; int hour = 0; int minute = 0; - switch (sscanf(value, "%d.%d.%d/%d:%dc", &year, &month, &day, &hour, &minute)){ + 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 5: + case 6: { if (year < 1970) throw ReOptionException(&m_programArgs, @@ -263,14 +276,42 @@ time_t ReDirOptions::checkDate(const char* value){ throw ReOptionException(&m_programArgs, i18n("invalid date/date-time value. 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) + rc = time(NULL) / 86400 * 86400 + hour * 3600 + minute * 60 + sec; + break; + } + default: + throw ReOptionException(&m_programArgs, + i18n("invalid time value. HH:MM:SS expected"), value); + } } else { // a time distance value: char unit = 'm'; int count = 0; - switch(sscanf(value, "%d%c", &count, &unit)){ + int isNegative = false; + if (theValue.startsWith("now")){ + rc = time(NULL); + theValue.remove(0, 3); + } + if (theValue.str()[0] == '-') + isNegative = true; + else if (theValue.startsWith("+")) + 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; @@ -282,15 +323,19 @@ time_t ReDirOptions::checkDate(const char* value){ break; default: throw ReOptionException(&m_programArgs, - i18n("invalid unit. expected: m(inutes) h(ours) d(ays)"), value); + i18n("invalid unit. expected: s(econds) m(inutes) h(ours) d(ays)"), value); } rc = time(NULL) - count; break; default: throw ReOptionException(&m_programArgs, - i18n("invalid relative time value expected. : m h d"), + i18n("invalid relative time value expected. : s m h d"), value); } + if (isNegative) + rc -= count; + else + rc += count; } return rc; } @@ -575,13 +620,27 @@ void ReDirOptions::setFilterFromProgramArgs(ReDirEntryFilter_t& filter){ /** * 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 usage a string vector with a message how to use the command + * @param example a string vector with some examples how to use the command + * @param minArguments minimal count of non option arguments + * @param reservedFirst count of arguments at the start which are not files + * @param reservedLast count of arguments at the end which are not files + * @param addCurrentDirIfNoArguments + * true: if no arguments are given the current + * directory will be added as argument */ -ReTool::ReTool(const char* usage[], const char* example[]) : +ReTool::ReTool(const char* usage[], const char* example[], + int minArguments, int reservedFirst, int reservedLast, + bool addCurrentDirIfNoArguments) : ReDirOptions(usage, example), - ReDirStatisticData(), - m_traverser(NULL) + ReDirTreeStatistic(), + m_minArguments(minArguments), + m_reservedFirst(reservedFirst), + m_reservedLast(reservedLast), + m_addCurrentDirIfNoArguments(addCurrentDirIfNoArguments), + m_traverser(NULL, this), + m_filter(), + m_start(time(NULL)) { } @@ -603,19 +662,144 @@ 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).append("/", 1).appendInt(m_traverser->directories()).append(" dir(s)"); - buffer.appendInt(m_files).append("/", 1).appendInt(m_traverser->files()).append(" dir(s)"); + buffer.appendInt(m_files).append("/", 1).appendInt(m_traverser.directories()).append(" dir(s)"); + buffer.appendInt(m_files).append("/", 1).appendInt(m_traverser.files()).append(" dir(s)"); buffer.append(currentFile); fputs(buffer.str(), stdout); return true; } + +/** + * Evaluates the arguments and calls the main function. + * + * @param argc number of arguments + * @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 { + m_programArgs.init(argc, argv); + if (m_programArgs.getArgCount() < m_minArguments) + m_programArgs.help(i18n("too few arguments"), false, stdout); + doIt(); + } catch (ReOptionException& exc) { + m_programArgs.help(exc.getMessage(), false, stdout); + } +} +/** + * Evaluates the non option arguments which are names of files or directories. + * + * For each non reserved argument it will be called either processSingleFile() + * or processTree(). + * + */ +void ReTool::processFileArguments(){ + int max = m_programArgs.getArgCount() - m_reservedLast; + struct stat info; + // Test whether the arguments are files or directories: + for (int ii = m_reservedFirst; ii < max; ii++){ + const char* arg = m_programArgs.getArg(ii); + if (stat(arg, &info) != 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.getArg(ii); + if (S_ISDIR(info.st_mode)) + processTree(arg); + else + processSingleFile(arg); + } +} +/** + * Processes a single file. + * + * Gets the file info and calls processFile(). + * + * @param filename the name of the file + */ +void ReTool::processSingleFile(const char* filename){ + ReByteBuffer protocol; + ReByteBuffer path; + ReByteBuffer name; + ReByteBuffer ext; + ReStringUtils::splitPath(filename, &protocol, &path, &name, &ext); + protocol.append(path); + if (protocol.length() == 0) + protocol.append("."); + else + protocol.setLength(protocol.length() - 1); + m_traverser.changeBase(protocol.str()); + + name.append(ext); + setFilterFromProgramArgs(m_filter); + m_traverser.setPropertiesFromFilter(&m_filter); + ReDirStatus_t entry; + entry.m_path = protocol; + if (entry.findFirst()){ + do { +#if defined __linux__ + bool found = strcmp(entry.node(), name.str()) == 0; +#elif defined __WIN32__ + bool found = stricmp(entry.node(), name.str()) == 0; +#endif + if (found && m_filter.match(entry)){ + processFile(&entry); + break; + } + } while(entry.findNext()); + } + entry.freeEntry(); +} +/** + * Processes a directory tree. + * + * Finds all filtered files in this directory and its subdirs and call processFile() + * or processDir(). + * + * @param directory the name of the directory + */ +void ReTool::processTree(const char* directory){ + m_traverser.changeBase(directory); + setFilterFromProgramArgs(m_filter); + m_traverser.setPropertiesFromFilter(&m_filter); + ReDirStatus_t* entry; + int level; + while( (entry = m_traverser.nextFile(level, &m_filter)) != NULL){ + if (entry->isDirectory()) + processDir(entry); + else + processFile(entry); + } +} +/** + * 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()); +} +/** + * 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() : - m_sizes(0), - m_files(0), - m_dirs(0), + ReDirTreeStatistic(), m_path() { } @@ -625,9 +809,6 @@ ReDirStatisticData::ReDirStatisticData() : * @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) { } @@ -640,7 +821,7 @@ ReDirStatisticData::ReDirStatisticData(const ReDirStatisticData& source) : ReDirStatisticData& ReDirStatisticData::operator =(const ReDirStatisticData& source){ m_sizes = source.m_sizes; m_files = source.m_files; - m_dirs = source.m_dirs; + m_directories = source.m_directories; m_path = source.m_path; return *this; } @@ -649,10 +830,8 @@ ReDirStatisticData& ReDirStatisticData::operator =(const ReDirStatisticData& sou * Constructor. */ ReDirStatistic::ReDirStatistic(int deltaList, int deltaBuffer) : - ReDirOptions(s_statisticUsage, s_statisticExamples), - m_list(deltaList, deltaBuffer), - m_traceInterval(0), - m_lastTrace(0) + ReTool(s_statisticUsage, s_statisticExamples, 2, 0, 1, false), + m_list(deltaList, deltaBuffer) { // standard short options: D d O o P p T t v y Z z m_programArgs.addBool("kbyte", @@ -675,18 +854,215 @@ ReDirStatistic::~ReDirStatistic(){ ReDirStatisticData& ReDirStatisticData::add(const ReDirStatisticData& source){ m_sizes += source.m_sizes; m_files += source.m_files; - m_dirs += source.m_dirs; + m_directories += source.m_directories; return *this; } /** * Initializes the data of the instance. */ void ReDirStatisticData::clear(){ - m_sizes = 0; - m_files = 0; - m_dirs = 0; + ReDirTreeStatistic::clear(); m_path.setLength(0); } +/** + * Constructor. + */ +ReDirBatch::ReDirBatch() : + ReTool(s_batchUsage, s_batchExamples, 0, 0, 0, true), + 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).append("\x01", 1).append(delim, -1); + line.replaceAll(buffer.str(), buffer.length(), "", 0); +} +/** + * Creates the batch file. + */ +void ReDirBatch::doIt(){ + ReByteBuffer buffer; + m_arguments.append(m_programArgs.getString("arguments", buffer), -1); + m_script.append(m_programArgs.getString("script", buffer), -1); + 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 + processFileArguments(); + if (m_verboseLevel >= V_SUMMARY){ + int duration = int(time(NULL) - m_start); +#if defined __linux__ + const char* prefix = "#"; +#elif defined __WIN32__ + const char* prefix = "rem"; +#endif + toString(buffer); + buffer.append(" ").appendTime(duration); + fprintf(m_output, "%s %s\n", buffer.str()); + } +} + +/** + * 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).append(" ").append(delim, -1); + line.append(entry->m_path).append(entry->node(), -1); + line.append(delim, -1); + } else { + replaceMakros(m_arguments.str(), entry, delim, line); + } + fprintf(m_output, "%s\n", line.str()); +} + +/** + * Constructor. + */ +ReDirList::ReDirList() : + ReTool(s_listUsage, s_listExamples, 0, 0, 0, true), + m_shortFormat(false) +{ + m_programArgs.addBool("short", i18n("output is only path and basename"), + '1', "--short", false); + addStandardFilterOptions(); +} + +/** + * Lists the metadata of the specified files. + */ +void ReDirList::doIt(){ + m_shortFormat = m_programArgs.getBool("short"); + processFileArguments(); + if (m_verboseLevel >= V_SUMMARY){ + int duration = int(time(NULL) - m_start); + ReByteBuffer line; + toString(line); + line.appendTime(duration).append(" ", 1).append(i18n("sec")); + fprintf(m_output, "=== %s\n", line.str()); + } +} + +/** + * 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 + fprintf(m_output, "%s %12.6f %s %02x %s%s\n", + entry->rightsAsString(bufferRights), + entry->fileSize() / 1E6, + entry->filetimeAsString(bufferTime), + entry->type(), + entry->m_path.str(), entry->node()); +} + /** * Calculates the statistic of a directory tree. @@ -751,7 +1127,7 @@ const ReStringList& ReDirStatistic::calculate(const char* base, int level, } } if (entry->isDirectory()){ - current->m_dirs++; + current->m_directories++; } else if (! useFilter || filter.match(*entry)){ current->m_sizes += entry->fileSize(); current->m_files++; @@ -782,39 +1158,32 @@ const ReStringList& ReDirStatistic::calculate(const char* base, int level, * @param arc count of arguments in argv * @param argv the program arguments. */ -void ReDirStatistic::run(int argc, const char* argv[]){ - time_t start = time(NULL); - try { - m_programArgs.init(argc, argv); - if (m_programArgs.getArgCount() < 1) - m_programArgs.help("statistic: missing path", false, stdout); - int depth = 1; - if (m_programArgs.getArgCount() > 1){ - const char* arg1 = m_programArgs.getArg(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.getArg(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) - 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); - } - } catch (ReOptionException& exc) { - m_programArgs.help(exc.getMessage(), false, stdout); - } +void ReDirStatistic::doIt(){ + int depth = 1; + if (m_programArgs.getArgCount() > 1){ + const char* arg1 = m_programArgs.getArg(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.getArg(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. * @@ -850,9 +1219,73 @@ void formatWithSizeFilesAndDirs(const ReDirStatisticData& data, // 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_dirs); + data.m_sizes / 1E6, data.m_files, data.m_directories); line.append(buffer, -1).append(data.m_path); } +/** + * Constructor. + */ +ReDirTouch::ReDirTouch() : + ReTool(s_touchUsage, s_touchExamples, 2, 1, 0, false), + m_buffer(), + m_properties() +{ + // standard short options: D d O o P p T t v y Z z + m_programArgs.addString("accessed", + i18n("the new access time.\n" + "Formats: absolute, relative to now, relative to the current filetime\n" + "[yyyy.mm.dd/]HH:MM:SS \n" + "now-{s|m|h|d}\n" + "{+|-}{s|m|h|d}"), + 'a', "--accessed", false, NULL); + m_programArgs.addString("modified", + i18n("the new modification time.\n" + "Formats: absolute, relative to now, relative to the current filetime\n" + "[yyyy.mm.dd/]HH:MM:SS\n" + "now-{s|m|h|d}\n" + "{+|-}{s|m|h|d}"), + 'm', "--modified", false, NULL); + addStandardFilterOptions(); +} +/** + * Sets the filetime for the specified files. + */ +void ReDirTouch::doIt(){ + ReByteBuffer buffer; + if (m_programArgs.getString("modified", buffer)[0] != '\0') + m_modified = checkDate(buffer.str()); + if (m_programArgs.getString("accessed", buffer)[0] != '\0') + m_accessed = checkDate(buffer.str()); + processFileArguments(); + if (m_verboseLevel >= V_SUMMARY){ + int duration = int(time(NULL) - m_start); + ReByteBuffer line; + toString(line); + line.appendTime(duration).append(" ", 1).append(i18n("sec")); + fprintf(m_output, "=== %s\n", line.str()); + } +} + +void ReDirTouch::processDir(ReDirStatus_t* entry){ + processFile(entry); +} +void ReDirTouch::processFile(ReDirStatus_t* entry){ + +} + +/** + * Sets file times. + * + * @param filename the name of the file + * @param properties contains modification and access time + * @param logger NULL or the logger + */ +bool ReDirTouch::touch(const char* filename, ReFileProperties_t* properties, + ReLogger* logger){ +} + + + /** * Prints a vector of lines. * @@ -865,11 +1298,10 @@ static void printField(const char** lines){ } /** - * Prints an message how to use the statistic module and exits. + * Prints a message how to use the module and exits. */ -void ReDirTools::statisticUsage(){ - ReDirStatistic statistic; - statistic.programArgs().help(NULL, false, stdout); +void ReDirTools::usage(ReTool& tool){ + tool.programArgs().help(NULL, false, stdout); } /** @@ -880,236 +1312,13 @@ void ReDirTools::statisticUsage(){ */ void ReDirTools::usage(const char* msg, const char* msg2){ printf ("Version: %s\n", m_version); - printf ("usage: dirtool \n"); - statisticUsage(); + printf ("usage: dirtool \n" + "call 'dirtool help' for more info\n"); if (msg != NULL) printf ("+++ %s%s\n", msg, msg2 == NULL ? "" : msg2); exit(1); } -/** - * Constructor. - */ -ReDirList::ReDirList() : - ReTool(s_listUsage, s_listExamples) -{ - m_programArgs.addBool("short", i18n("output is only path and basename"), - '1', "--short", false); - addStandardFilterOptions(); -} - -/** - * Gets the arguments for the "list" command and execute this. - * - * @param argc the number of arguments - * @param argav the argument vector - */ -void ReDirList::list(int argc, const char* argv[]){ - ReDirEntryFilter_t filter; - try { - time_t start = time(NULL); - m_programArgs.init(argc, argv); - bool shortOutput = m_programArgs.getBool("short"); - setFilterFromProgramArgs(filter); - bool noPath = m_programArgs.getArgCount() == 0; - ReByteBuffer bufferRights; - ReByteBuffer bufferTime; - int64_t sumSizes = 0; - int files = 0; - int dirs = 0; - - for (int ix = 0; noPath || ix < m_programArgs.getArgCount(); ix++){ - ReTraverser traverser(noPath ? "." : m_programArgs.getArg(ix)); - noPath = false; - traverser.setPropertiesFromFilter(&filter); - int level; - ReDirStatus_t* entry; - while( (entry = traverser.nextFile(level, &filter)) != NULL){ - if (entry->isDirectory()) - dirs++; - else{ - files++; - sumSizes += entry->fileSize(); - } - if (! printOneFile(entry)){ - if (shortOutput) - fprintf(m_output, "%s%s\n", entry->m_path.str(), entry->node()); - else - fprintf(m_output, "%s %12.6f %s %02x %s%s\n", - entry->rightsAsString(bufferRights), - entry->fileSize() / 1E6, - entry->filetimeAsString(bufferTime), - entry->type(), - entry->m_path.str(), entry->node()); - } - } - } - if (m_verboseLevel >= V_SUMMARY){ - int duration = int(time(NULL) - start); - fprintf(m_output, "+++ %d dirs and %d file(s) with %.6f MByte in %02d:%02d sec\n", - dirs, files, sumSizes / 1E6, duration / 60, duration % 60); - } - } catch(ReOptionException& exc){ - help(exc.getMessage()); - } -} - -/** - * Constructor. - */ -ReDirBatch::ReDirBatch() : - ReTool(s_batchUsage, s_batchExamples) -{ - // 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).append("\x01", 1).append(delim, -1); - line.replaceAll(buffer.str(), buffer.length(), "", 0); -} -/** - * Gets the arguments for the "list" command and execute this. - * - * @param argc the number of arguments - * @param argav the argument vector - */ -void ReDirBatch::createBatch(int argc, const char* argv[]){ - ReDirEntryFilter_t filter; - try { - time_t start = time(NULL); - m_programArgs.init(argc, argv); - ReByteBuffer buffer; - ReByteBuffer arguments(m_programArgs.getString("arguments", buffer), -1); - ReByteBuffer script(m_programArgs.getString("script", buffer), -1); - if (arguments.length() + script.length() == 0) - help(i18n("one of the option must be set: -a (--arguments) or -c (--script)")); -#if defined __WIN32__ - bool isExe = m_programArgs.getBool("isexe"); -#endif - setFilterFromProgramArgs(filter); - if (m_programArgs.getArgCount() == 0) - help(i18n("no arguments given (missing path)")); - if (m_programArgs.getString("first", buffer)[0] != '\0') - printf("%s\n", buffer.str()); - int64_t sumSizes = 0; - int files = 0; - int dirs = 0; -#if defined __linux__ - const char* delim = "'"; -#elif defined __WIN32__ - const char* delim = "\""; -#endif - for (int ix = 0; ix < m_programArgs.getArgCount(); ix++){ - ReTraverser traverser(m_programArgs.getArg(ix)); - traverser.setPropertiesFromFilter(&filter); - int level; - ReDirStatus_t* entry; - ReByteBuffer line; - while( (entry = traverser.nextFile(level, &filter)) != NULL){ - if (entry->isDirectory()) - dirs++; - else{ - files++; - sumSizes += entry->fileSize(); - } - if (script.length() > 0){ - line.setLength(0); -#if defined __WIN32__ - if (! isExe) - line.append("call "); -#endif - line.append(script).append(" ").append(delim, -1); - line.append(entry->m_path).append(entry->node(), -1); - line.append(delim, -1); - } else { - replaceMakros(arguments.str(), entry, delim, line); - } - fprintf(m_output, "%s\n", line.str()); - } - } - if (m_verboseLevel >= V_SUMMARY){ - int duration = int(time(NULL) - start); -#if defined __linux__ - const char* prefix = "#"; -#elif defined __WIN32__ - const char* prefix = "rem"; -#endif - fprintf(m_output, "%s %d dir(s) and %d file(s) with %.6f MByte in %02d:%02d sec\n", - prefix, dirs, files, sumSizes / 1E6, duration / 60, duration % 60); - } - } catch(ReOptionException& exc){ - help(exc.getMessage()); - } -} - /** * creates a subdirectory (and the parent directories if neccessary. * @@ -1136,7 +1345,7 @@ void ReDirSync::makeDirWithParents(ReByteBuffer& path, int minWidth, * Constructor. */ ReDirSync::ReDirSync() : - ReTool(s_syncUsage, s_syncExamples), + ReTool(s_syncUsage, s_syncExamples, 2, 0, 1, false), m_buffer() { // standard short options: D d O o P p T t v y Z z @@ -1360,136 +1569,114 @@ bool ReDirSync::makeDirectory(const char* directory, int minLength, } /** - * Gets the arguments for the "list" command and execute this. - * - * @param argc the number of arguments - * @param argav the argument vector + * Synchronizes two directory trees. */ -void ReDirSync::synchronize(int argc, const char* argv[]){ +void ReDirSync::doIt(){ ReDirEntryFilter_t filter; const char* sep = OS_SEPARATOR; struct stat info; - try { - time_t start = time(NULL); - m_programArgs.init(argc, argv); - ReByteBuffer buffer; - if (m_programArgs.getArgCount() < 2) - help(i18n("missing argument(s) (source / target)")); - ReByteBuffer target(m_programArgs.getArg(m_programArgs.getArgCount() - 1)); - if (target.endsWith(sep, 1)) - target.setLength(target.length() - 1); - if (stat(target.str(), &info) != 0) - help(i18n("target does not exist: $1"), target.str()); - else if (! S_ISDIR(info.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.getArgCount() - 1; ix++){ - source.set(m_programArgs.getArg(ix), -1); - target.setLength(lengthTargetBase); - bool endsWithSlash = source.endsWith(sep, 1); - if (endsWithSlash) - source.setLength(source.length() - 1); - if (stat(source.str(), &info) != 0) - help(i18n("source does not exist: $1"), source.str()); - else if (! S_ISDIR(info.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.append(OS_SEPARATOR, 1); - target.append(source.str() + startNode + 1, -1); - } - size_t ixSourceRelative = source.length(); - size_t ixTargetRelative = target.length(); + ReByteBuffer buffer; + ReByteBuffer target(m_programArgs.getArg(m_programArgs.getArgCount() - 1)); + if (target.endsWith(sep, 1)) + target.setLength(target.length() - 1); + if (stat(target.str(), &info) != 0) + help(i18n("target does not exist: $1"), target.str()); + else if (! S_ISDIR(info.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.getArgCount() - 1; ix++){ + source.set(m_programArgs.getArg(ix), -1); + target.setLength(lengthTargetBase); + bool endsWithSlash = source.endsWith(sep, 1); + if (endsWithSlash) + source.setLength(source.length() - 1); + if (stat(source.str(), &info) != 0) + help(i18n("source does not exist: $1"), source.str()); + else if (! S_ISDIR(info.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.append(OS_SEPARATOR, 1); + target.append(source.str() + startNode + 1, -1); + } + size_t ixSourceRelative = source.length(); + size_t ixTargetRelative = target.length(); - ReTraverser traverser(source.str()); - traverser.setPropertiesFromFilter(&filter); - int level; - ReDirStatus_t* entry; - ReByteBuffer line; - while( (entry = traverser.nextFile(level, &filter)) != NULL){ - if (entry->isDirectory()) + ReTraverser traverser(source.str()); + traverser.setPropertiesFromFilter(&filter); + int level; + ReDirStatus_t* entry; + ReByteBuffer line; + while( (entry = 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 (stat(target.str(), &info) != 0) + makeDirWithParents(target, ixTargetRelative, traverser); + targetFile.set(target).append(entry->node(), -1); + const char* targetRelativePath = targetFile.str() + ixTargetRelative + 1; + bool exists = stat(targetFile.str(), &info) == 0; + if (! exists && mustExist){ + if (m_verboseLevel == V_CHATTER) + fprintf(m_output, "-ignored: %s does not exist\n", targetRelativePath); + continue; + } + if (exists){ + if (addOnly){ + if (m_verboseLevel >= V_CHATTER) + fprintf(m_output, "~ignored: %s exists\n", targetRelativePath); continue; - // append the new relative path from source to target: - target.setLength(ixTargetRelative); - target.append(entry->m_path.str() + ixSourceRelative, -1); - if (stat(target.str(), &info) != 0) - makeDirWithParents(target, ixTargetRelative, traverser); - targetFile.set(target).append(entry->node(), -1); - const char* targetRelativePath = targetFile.str() + ixTargetRelative + 1; - bool exists = stat(targetFile.str(), &info) == 0; - if (! exists && mustExist){ - if (m_verboseLevel == V_CHATTER) - fprintf(m_output, "-ignored: %s does not exist\n", targetRelativePath); + } + if (ignoreDate && entry->fileSize() == info.st_size){ + if (m_verboseLevel >= V_CHATTER) + fprintf(m_output, "_ignored: %s same size\n", targetRelativePath); continue; } - if (exists){ - if (addOnly){ - if (m_verboseLevel >= V_CHATTER) - fprintf(m_output, "~ignored: %s exists\n", targetRelativePath); - continue; - } - if (ignoreDate && entry->fileSize() == info.st_size){ - if (m_verboseLevel >= V_CHATTER) - fprintf(m_output, "_ignored: %s same size\n", targetRelativePath); - continue; - } - // target younger than source? - int diff = int(info.st_mtime - entry->filetimeToTime(entry->modified())); - if (! ignoreDate && info.st_mtime - entry->filetimeToTime(entry->modified()) - <= maxFileTimeDiff) { - if (m_verboseLevel >= V_CHATTER) - fprintf(m_output, "=ignored: %s same time\n", targetRelativePath); - continue; - } + // target younger than source? + int diff = int(info.st_mtime - entry->filetimeToTime(entry->modified())); + if (! ignoreDate && info.st_mtime - entry->filetimeToTime(entry->modified()) + <= 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", exists ? '!' : '+', targetRelativePath, - dry ? " would be copied" : ""); - if (! dry) - copyFile(entry, targetFile.str()); } - treeFiles += traverser.files(); - treeDirs += traverser.directories(); - treeSumSizes+= traverser.sizes(); - } - 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" - "=== 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); - } - } catch(ReOptionException& exc){ - help(exc.getMessage()); - } -} - -/** - * Gets the arguments for the "batch" command and execute this. - * - * @param argc the number of arguments - * @param argav the argument vector - */ -void ReDirTools::batch(int argc, const char* argv[]){ - ReDirBatch batch; - batch.createBatch(argc, argv); + files++; + sumSizes += entry->fileSize(); + if (m_verboseLevel >= V_NORMAL) + fprintf(m_output, "%c%s%s\n", exists ? '!' : '+', targetRelativePath, + dry ? " would be copied" : ""); + if (! dry) + copyFile(entry, targetFile.str()); + } + treeFiles += traverser.files(); + treeDirs += traverser.directories(); + treeSumSizes+= 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); + } } /** @@ -1537,16 +1724,15 @@ void ReDirTools::help(int argc, const char* argv[]){ } /** - * Gets the arguments for the "statistic" command and execute this. + * Executes a command. * * @param argc the number of arguments - * @param argav the argument vector + * @param argv the argument vector + * @param tool the tool which realizes the command */ -void ReDirTools::list(int argc, const char* argv[]){ - ReDirList lister; - lister.list(argc, argv); +void ReDirTools::run(int argc, const char* argv[], ReTool& tool){ + tool.run(argc, argv); } - /** * Gets the arguments for any command and execute this. * @@ -1564,15 +1750,17 @@ int ReDirTools::main(int argc, char* orgArgv[]){ argv++; const char* arg0 = argv[0]; if (isArg("batch", arg0)) - tools.batch(argc, argv); + ReDirBatch().run(argc, argv); else if (isArg("list", arg0)) - tools.list(argc, argv); + ReDirList().run(argc, argv); else if (isArg("help", arg0)) tools.help(argc, argv); else if (isArg("statistic", arg0)) - tools.statistic(argc, argv); + ReDirStatistic().run(argc, argv); else if (isArg("synchronize", arg0)) - tools.synchronize(argc, argv); + ReDirSync().run(argc, argv); + else if (isArg("touch", arg0)) + ReDirTouch().run(argc, argv); else if (isArg("test", arg0)){ void testAll(); testAll(); @@ -1582,25 +1770,3 @@ int ReDirTools::main(int argc, char* orgArgv[]){ return 0; } -/** - * Gets the arguments for the "statistic" command and execute this. - * - * @param argc the number of arguments - * @param argav the argument vector - */ -void ReDirTools::statistic(int argc, const char* argv[]){ - ReDirStatistic statistic; - statistic.run(argc, argv); -} - -/** - * Gets the arguments for the "synchronize" command and execute this. - * - * @param argc the number of arguments - * @param argav the argument vector - */ -void ReDirTools::synchronize(int argc, const char* argv[]){ - ReDirSync sync; - sync.synchronize(argc, argv); -} - diff --git a/os/ReDirTools.hpp b/os/ReDirTools.hpp index 0cd22aa..87a49ca 100644 --- a/os/ReDirTools.hpp +++ b/os/ReDirTools.hpp @@ -31,10 +31,10 @@ public: int64_t checkSize(const char* value); void checkStandardFilterOptions(); ReDirStatus_t::Type_t checkType(const char* value); + void close(); /** Returns the compound usage message. * @return the compound usage message */ - void close(); const char** compoundUsage() const { return m_compoundUsage; } void initCompoundUsage(size_t size); @@ -72,46 +72,74 @@ typedef struct { } ReFileProperties_t; #endif -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 ReTool : public ReDirOptions, public ReDirStatisticData { +/** + * Base class of directory tools. + */ +class ReTool : public ReDirOptions, public ReDirTreeStatistic { public: - ReTool(const char* usage[], const char* example[]); + ReTool(const char* usage[], const char* example[], + int minArguments, int reservedFirst, int reservedLast, + bool addCurrentDirIfNoArguments); virtual ~ReTool(); public: virtual bool trace(const char* currentFile); + virtual void run(int argc, const char** argv); +protected: + /** The task specific method. + * Normally it calls processFileArguments() after some initializations. + */ + virtual void doIt() = 0; + virtual void processDir(ReDirStatus_t* status); + virtual void processFile(ReDirStatus_t* status); + void processFileArguments(); + virtual void processSingleFile(const char* filename); + virtual void processTree(const char* filename); protected: - ReTraverser* m_traverser; + int m_minArguments; + int m_reservedFirst; + int m_reservedLast; + bool m_addCurrentDirIfNoArguments; + ReTraverser m_traverser; + ReDirEntryFilter_t m_filter; + int64_t m_start; }; class ReDirBatch : public ReTool { public: ReDirBatch(); +protected: + virtual void doIt(); + virtual void processDir(ReDirStatus_t* entry); + virtual void processFile(ReDirStatus_t* entry); +protected: + ReByteBuffer m_arguments; + ReByteBuffer m_script; + bool m_isExe; + +}; + +/** + * List file attributes of files. + */ +class ReDirList : public ReTool { public: - void createBatch(int argc, const char* argv[]); - virtual bool printOneFile(ReDirStatus_t* entry) - { return false; } + ReDirList(); +protected: + virtual void doIt(); + virtual void processDir(ReDirStatus_t* entry); + virtual void processFile(ReDirStatus_t* entry); +protected: + bool m_shortFormat; }; +/** + * Sychronizes two directory trees. + */ class ReDirSync : public ReTool { public: ReDirSync(); -public: - void synchronize(int argc, const char* argv[]); protected: + virtual void doIt(); void copyFile(ReDirStatus_t* entry, const char* target); void makeDirWithParents(ReByteBuffer& path, int minWidth, ReTraverser& traverser); @@ -126,17 +154,18 @@ protected: ReByteBuffer m_buffer; }; -class ReDirList : public ReTool { +class ReDirStatisticData : public ReDirTreeStatistic { public: - ReDirList(); + ReDirStatisticData(); + ReDirStatisticData(const ReDirStatisticData& source); + ReDirStatisticData& operator =(const ReDirStatisticData& source); +public: + void clear(); + ReDirStatisticData& add(const ReDirStatisticData& source); public: - void list(int argc, const char* argv[]); - virtual bool printOneFile(ReDirStatus_t* entry) - { return false; } + ReByteBuffer m_path; }; - - class ReDirStatistic; extern void formatLikeDu(const ReDirStatisticData& data, ReDirStatistic& parent, ReByteBuffer& line); @@ -146,7 +175,7 @@ extern void formatWithSizeFilesAndDirs(const ReDirStatisticData& data, /** * Calculates a statistic of a directory tree. */ -class ReDirStatistic : public ReDirOptions { +class ReDirStatistic : public ReTool { public: ReDirStatistic(int deltaList = 512, int deltaBuffer = 0x10000); ~ReDirStatistic(); @@ -154,29 +183,43 @@ public: const ReStringList& calculate(const char* base, int depth, void (*format)(const ReDirStatisticData& data, ReDirStatistic& parent, ReByteBuffer& line) = formatLikeDu); - void run(int argc, const char* argv[]); - void setTraceInterval(int interval){ - m_traceInterval = interval; - } + void doIt(); private: ReStringList m_list; int m_traceInterval; time_t m_lastTrace; }; +/** + * Changes the filetime of files. + */ +class ReDirTouch : public ReTool { +public: + ReDirTouch(); +protected: + virtual void doIt(); + virtual void processDir(ReDirStatus_t* entry); + virtual void processFile(ReDirStatus_t* entry); +public: + static bool touch(const char* filename, ReFileProperties_t* properties, + ReLogger* logger = NULL); +protected: + ReByteBuffer m_buffer; + time_t m_modified; + time_t m_accessed; + ReFileProperties_t m_properties; +}; + class ReDirTools { public: virtual void usage(const char* msg, const char* msg2 = NULL); - void dirListUsage(); - void batch(int argc, const char* argv[]); void help(int argc, const char* argv[]); - void list(int argc, const char* argv[]); - void statisticUsage(); - void statistic(int argc, const char* argv[]); - void synchronize(int argc, const char* argv[]); public: static int main(int argc, char* argv[]); +protected: + void run(int argc, const char* argv[], ReTool& tool); + void usage(ReTool& tool); public: static const char* m_version; }; diff --git a/os/ReTraverser.cpp b/os/ReTraverser.cpp index 2194d27..6513a80 100644 --- a/os/ReTraverser.cpp +++ b/os/ReTraverser.cpp @@ -384,6 +384,40 @@ struct stat* ReDirStatus_t::getStatus() { } #endif +/** + * Constructor. + */ +ReDirTreeStatistic::ReDirTreeStatistic() : + m_directories(0), + m_files(0), + m_sizes(0ll) +{ +} +/** + * Builds a string describing the data. + * + * @param buffer IN/OUT: a buffer for the result + * @param append true: the string will be appended to the buffer
+ * false: the buffer will be cleared at the beginning + * @param formatFiles the sprintf format for the file count, e.g. "%8d" + * @param formatSized the sprintf format for the MByte format, e.g. "%12.6f" + * @param formatFiles the sprintf format for the directory count, e.g. "%6d" + * @return a human readable string + */ +const char* ReDirTreeStatistic::toString(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)")).append(" ", 1); + buffer.append(m_sizes / 1000.0 / 1000, formatSizes); + buffer.append(" ", 1).append(i18n("MByte")).append(" ", 1); + buffer.appendInt(m_directories, formatDirs); + buffer.append(i18n("dirs(s)")); + return buffer.str(); +} + /** * Constructor. * @@ -424,6 +458,7 @@ bool ReTraceUnit::trace(const char* message){ * @param base the base directory. The traversal starts at this point */ ReTraverser::ReTraverser(const char* base, ReTraceUnit* tracer) : + ReDirTreeStatistic(), m_minLevel(0), m_maxLevel(512), m_level(-1), @@ -431,9 +466,6 @@ ReTraverser::ReTraverser(const char* base, ReTraceUnit* tracer) : // m_dirs m_passNoForDirSearch(2), m_dirPatterns(NULL), - m_directories(0), - m_files(0), - m_sizes(0ll), m_tracer(tracer) { memset(m_dirs, 0, sizeof m_dirs); @@ -448,6 +480,29 @@ ReTraverser::ReTraverser(const char* base, ReTraceUnit* tracer) : * 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(); + // remove a preceeding "./". This simplifies the pattern expressions: + if (m_base.startsWith(ReByteBuffer(".").append(OS_SEPARATOR, 1).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(); @@ -456,7 +511,6 @@ ReTraverser::~ReTraverser() { } } } - /** * Returns the info about the next file in the directory tree traversal. * diff --git a/os/ReTraverser.hpp b/os/ReTraverser.hpp index 9f6ff78..5057674 100644 --- a/os/ReTraverser.hpp +++ b/os/ReTraverser.hpp @@ -130,12 +130,35 @@ protected: int m_interval; time_t m_startTime; }; + +class ReDirTreeStatistic { +public: + ReDirTreeStatistic(); +public: + const char* toString(ReByteBuffer& buffer, bool append = false, + const char* formatFiles = "%8d", const char* formatSizes = "%12.6f", + const char* formatDirs = "%7d"); + /** + * Resets the counters. + */ + inline void clear(){ + m_files = m_directories = 0; + m_sizes = 0ll; + } +public: + int m_directories; + int m_files; + int64_t m_sizes; +}; + + #define MAX_ENTRY_STACK_DEPTH 256 -class ReTraverser { +class ReTraverser : public ReDirTreeStatistic { public: ReTraverser(const char* base, ReTraceUnit* tracer = NULL); virtual ~ReTraverser(); public: + void changeBase(const char* base); /** * Return the number of entered directories . * @return the number of directories entered until now @@ -180,8 +203,9 @@ public: } ReDirStatus_t* topOfStack(int offset = 0); protected: - bool initEntry(const ReByteBuffer& parent, const char* node, int level); + void destroy(); void freeEntry(int level); + bool initEntry(const ReByteBuffer& parent, const char* node, int level); /** * Tests whether a directory should be processed. * @param node the base name of the subdir @@ -204,9 +228,7 @@ protected: /// a subdirectory will be entered only if this pattern list matches /// if NULL any directory will be entered RePatternList* m_dirPatterns; - int m_directories; - int m_files; - int64_t m_sizes; + ReDirTreeStatistic m_statistic; ReTraceUnit* m_tracer; }; -- 2.39.5