]> gitweb.hamatoma.de Git - crepublib/commitdiff
Heavy refactoring ReDirTools
authorhama <hama@siduction.net>
Sun, 15 Feb 2015 23:40:56 +0000 (00:40 +0100)
committerhama <hama@siduction.net>
Sun, 15 Feb 2015 23:40:56 +0000 (00:40 +0100)
base/ReByteBuffer.cpp
base/ReByteBuffer.hpp
cunit/cuReByteBuffer.cpp
cunit/cuReDirTools.cpp
cunit/cuReTraverser.cpp
cunit/testall.cpp
math/md5.cpp
os/ReDirTools.cpp
os/ReDirTools.hpp
os/ReTraverser.cpp
os/ReTraverser.hpp

index ded7ee8e2f195da85c29a0e1f4bcbcea0c6ec86c..65a01f0c037a5d062f715b413c84927c36a80fe7 100644 (file)
@@ -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 <code>ReByteBuffer</code> instance at the end of the buffer.
  *
  * If the space is not enough it will be allocated.
  *
  * <pre><code>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());
  * </code></pre>
  *
- * @param number               the number to append
- * @param format               the format of the number used by <code>sprintf()</code>
- *
  * @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 <code>ReByteBuffer</code> 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.
  *
  * <pre><code>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());
  * </code></pre>
  *
  * @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.
+ *
+ * <pre><code>Example:
+ * ReByteBuffer buf; buf.appendInt(33, "%-4d");
+ * assert(strcmp("33  ", buf.str()) == 0);
+ * </code></pre>
+ *
+ * @param number               the number to append
+ * @param format               the format of the number used by <code>sprintf()</code>
+ *
+ * @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.
+ *
+ * <pre><code>Example:
+ * ReByteBuffer buf; buf.appendInt(33U, "%x");
+ * assert(strcmp("21  ", buf.str()) == 0);
+ * </code></pre>
+ *
+ * @param number               the number to append
+ * @param format               the format of the number used by <code>sprintf()</code>
+ *
+ * @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.
+ *
+ * <pre><code>Example:
+ * ReByteBuffer buf; buf.appendInt(12345678901ll, "%12d");
+ * assert(strcmp(" 12345678901", buf.str()) == 0);
+ * </code></pre>
+ *
+ * @param number               the number to append
+ * @param format               the format of the number used by <code>sprintf()</code>
+ *
+ * @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.
+ *
+ * <pre><code>Example:
+ * ReByteBuffer buf; buf.appendInt((uint64_t) 0x12345678901ll, "%12x");
+ * assert(strcmp(" 12345678901", buf.str()) == 0);
+ * </code></pre>
+ *
+ * @param number               the number to append
+ * @param format               the format of the number used by <code>sprintf()</code>
+ *
+ * @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)<br>
+ *                                     > 4: at least MM:SS.sss<br>
+ *                                     > 7: at least HH:MM:SS.sss<br>
+ *                                     > 10: at least dd:HH:MM:SS<br>
+ * @return                     <code>buffer</code> (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.
index dcf1f01daddd3bcca98d34ad05675437c8c66a38..69feda25aaa32ac21877286bc1d60656a2aa422b 100644 (file)
@@ -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.
index 63b8171aec45c5ecd78329cfbfd8efb56edb3b53..b4fe8be2e80f91b44cabfa2574c00b5591a0af96 100644 (file)
@@ -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;
index 0ee0a797084a256f2187367976a74d3a0190bb93..68102414aac472ccd33be438d45234a9aae72303 100644 (file)
@@ -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__
index abddd2bf26ab4496af4732c6a136266a0bbe2e96..39b740a7c48e98b82f257094aac77be433ee6a7d 100644 (file)
@@ -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__
index cabbccff2d50d9c3af14b62bcfb7e31e37b86e1c..f9ca28a4fb2ecb07b2cdaf63c125adb79810c6ce 100644 (file)
@@ -73,7 +73,7 @@ void testMath(){
 void testAll(){
        try
        {
-               testMath();
+               testOs();
 
                testBase();
                testString();
index 9584c7fb318cb80f11e4a9abdf2c576e0b5ec072..7423820ba791ad445122fd7ab6703e54128b06ae 100644 (file)
@@ -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",
index 10553ebcccf97acaf2164126fdc99ac38ca5d79e..ee0252859d4f19343844a958c4d2a39da6e6af89 100644 (file)
@@ -36,11 +36,12 @@ static const char* s_helpSummary[] = {
     "statistic     shows statistics about a direcctory tree",\r
     "synchronize   copies only modified or new files from",\r
     "              from a source directory tre to a target",\r
+       "touch         sets the filetimes",\r
     NULL\r
 };\r
 \r
 const char* s_batchUsage[] = {\r
-    "<command>: batch",\r
+    "<command>: batch [<opts>] <dir_or_file1> [<dir_or_file2> ...]",\r
     "   produces output usable for a batch file (script)",\r
     "   all found files can be processed with a given script",\r
     "   each line starts (usually) with a script name (see -c)",\r
@@ -55,19 +56,18 @@ const char* s_batchExamples[] = {
 };\r
 \r
 const char* s_listUsage[] = {\r
-    "<command>: list",\r
+    "<command>: l(ist) [<opts>] <dir_or_file1> [<dir_or_file2> ...]",\r
     "   lists the metadata (size, modification date ...) of the selected files",\r
     NULL\r
 };\r
 const char* s_listExamples[] = {\r
     "dirtool list --min-size=10M e:\\data",\r
-    "dirtool list --type=f -y7d --size=10M -p;*.cpp;*.hpp;Makefile*;-*~ /home/data" ,\r
+    "dirtool li --type=f -y7d --size=10M -p;*.cpp;*.hpp;Makefile*;-*~ /home/data",\r
     NULL\r
 };\r
 \r
 static const char* s_statisticUsage[] = {\r
-    "<command>:"\r
-    "st(atistic)  [<opts_stat>] <path> [<depth>]",\r
+    "<command>: st(atistic)  [<opts>] <path1> [<path2> ...] [<depth>]",\r
     "          shows a statistic about a directory tree",\r
     "<path>    a directory path: relative or absolute",\r
     "<depth>   0: only the summary of <path> will be shown",\r
@@ -79,14 +79,13 @@ static const char* s_statisticUsage[] = {
 };\r
 const char* s_statisticExamples[] = {\r
     "dirtool st -q -t0 e:\\windows",  \r
-    "dirtool statistic --quiet --kbyte --trace-interval=60 ../mail 2",  \r
+    "dirtool statistic --quiet --kbyte --trace-interval=60 ../mail ../remember 2",\r
     "dirtool stat -q --kbyte d:data 2",  \r
     NULL\r
 };\r
 \r
 const char* s_syncUsage[] = {\r
-    "<command>:",\r
-    "sy(nchronize) <opts> <source1> [<source2> ...] <target>",\r
+    "<command>: sy(nchronize) <opts> <source1> [<source2> ...] <target>",\r
     "   Synchronizes the content of a directory tree with another.",\r
     "   Newer or missing files will be copied",\r
     "   If a source name ends with the separator (/ in linux, \\ in win) the target",\r
@@ -102,6 +101,18 @@ const char* s_syncExamples[] = {
     NULL\r
 };\r
 \r
+const char* s_touchUsage[] = {\r
+    "<command>: touch [<opts>] <dir_or_file1> [<dir_or_file2> ...]",\r
+    "   sets the filetimes (modification and/or access time) of the selected files",\r
+    NULL\r
+};\r
+const char* s_touchExamples[] = {\r
+    "dirtool touch -p;*.csv -m2014.12.1/8:12 e:\\data e:\\history\\readme.txt",\r
+    "dirtool touch -p;*.cpp --modified=now-4d12H /home/data" ,\r
+       "dirtool touch -p;*.csv -m+1m . e:\\data e:\\history\\readme.txt",\r
+    NULL\r
+};\r
+\r
 /**\r
  * Constructor.\r
  *\r
@@ -227,10 +238,10 @@ void ReDirOptions::addStandardFilterOptions(){
  * Checks whether the given value is a time expression.\r
  *\r
  * Possible: <date> <date_time> <n><unit>\r
- * Units: m(inutes) h(our) d(ays)\r
+ * Units: m(inutes) h(our) d(ays) s(econds)\r
  *\r
  * @param value     value to check\r
- * @return          the value converted into the absolute time\r
+ * @return          the value converted into the absolute (or relative) time\r
  * @throws          ReOptionExecption\r
  */\r
 time_t ReDirOptions::checkDate(const char* value){\r
@@ -241,10 +252,12 @@ time_t ReDirOptions::checkDate(const char* value){
         int year, month, day;\r
         int hour = 0;\r
         int minute = 0;\r
-        switch (sscanf(value, "%d.%d.%d/%d:%dc", &year, &month, &day, &hour, &minute)){\r
+               int sec = 0;\r
+        switch (sscanf(value, "%d.%d.%d/%d:%d:%d", &year, &month, &day, &hour, &minute, &sec)){\r
         case 3:\r
         case 4:\r
-        case 5:\r
+               case 5:\r
+               case 6:\r
         {\r
             if (year < 1970)\r
                 throw ReOptionException(&m_programArgs, \r
@@ -263,14 +276,42 @@ time_t ReDirOptions::checkDate(const char* value){
             throw ReOptionException(&m_programArgs, \r
                 i18n("invalid date/date-time value. yyyy.mm.dd/hh:MM expected"), value);\r
         }\r
+       } else if (theValue.count(":") >= 1){\r
+        // a time:\r
+        int hour = 0;\r
+        int minute = 0;\r
+               int sec = 0;\r
+               switch (sscanf(value, "%d:%d:%d", &hour, &minute, &sec)){\r
+        case 2:\r
+        case 3:\r
+        {\r
+                       // the time (today)\r
+                       rc = time(NULL) / 86400 * 86400 + hour * 3600 + minute * 60 + sec;\r
+            break;\r
+        }\r
+        default:\r
+            throw ReOptionException(&m_programArgs,\r
+                i18n("invalid time value. HH:MM:SS expected"), value);\r
+        }\r
     } else {\r
         // a time distance value:\r
         char unit = 'm';\r
         int count = 0;\r
-        switch(sscanf(value, "%d%c", &count, &unit)){\r
+               int isNegative = false;\r
+               if (theValue.startsWith("now")){\r
+                       rc = time(NULL);\r
+                       theValue.remove(0, 3);\r
+               }\r
+               if (theValue.str()[0] == '-')\r
+                       isNegative = true;\r
+               else if (theValue.startsWith("+"))\r
+                       theValue.remove(0, 1);\r
+        switch(sscanf(theValue.str(), "%d%c", &count, &unit)){\r
         case 1:\r
         case 2:\r
             switch(unit){\r
+                       case 's':\r
+                               break;\r
             case 'm':\r
                 count *= 60;\r
                 break;\r
@@ -282,15 +323,19 @@ time_t ReDirOptions::checkDate(const char* value){
                 break;\r
             default:\r
                 throw ReOptionException(&m_programArgs, \r
-                    i18n("invalid unit. expected: m(inutes) h(ours) d(ays)"), value);\r
+                    i18n("invalid unit. expected: s(econds) m(inutes) h(ours) d(ays)"), value);\r
             }\r
             rc = time(NULL) - count;\r
             break;\r
         default:\r
             throw ReOptionException(&m_programArgs, \r
-                i18n("invalid relative time value <number><unit> expected. <unit>: m h d"), \r
+                i18n("invalid relative time value <number><unit> expected. <unit>: s m h d"),\r
                 value);\r
         }\r
+        if (isNegative)\r
+                       rc -= count;\r
+               else\r
+                       rc += count;\r
     }\r
     return rc;\r
 }\r
@@ -575,13 +620,27 @@ void ReDirOptions::setFilterFromProgramArgs(ReDirEntryFilter_t& filter){
 /**\r
  * Constructor.\r
  *\r
- * @param usage     a string vector with a message how to use the command\r
- * @param example   a string vector with some examples how to use the command\r
+ * @param usage        a string vector with a message how to use the command\r
+ * @param example      a string vector with some examples how to use the command\r
+ * @param minArguments minimal count of non option arguments\r
+ * @param reservedFirst        count of arguments at the start which are not files\r
+ * @param reservedLast count of arguments at the end which are not files\r
+ * @param addCurrentDirIfNoArguments\r
+ *                                             <code>true</code>: if no arguments are given the current\r
+ *                                             directory will be added as argument\r
  */\r
-ReTool::ReTool(const char* usage[], const char* example[]) :\r
+ReTool::ReTool(const char* usage[], const char* example[],\r
+               int minArguments, int reservedFirst, int reservedLast,\r
+               bool addCurrentDirIfNoArguments) :\r
        ReDirOptions(usage, example),\r
-       ReDirStatisticData(),\r
-       m_traverser(NULL)\r
+       ReDirTreeStatistic(),\r
+       m_minArguments(minArguments),\r
+       m_reservedFirst(reservedFirst),\r
+       m_reservedLast(reservedLast),\r
+       m_addCurrentDirIfNoArguments(addCurrentDirIfNoArguments),\r
+       m_traverser(NULL, this),\r
+       m_filter(),\r
+       m_start(time(NULL))\r
 {\r
 }\r
 \r
@@ -603,19 +662,144 @@ bool ReTool::trace(const char* currentFile){
        ReByteBuffer buffer(" ");\r
        int duration = int(time(NULL) - m_startTime);\r
        buffer.appendInt(duration / 60).appendInt(duration % 60, ":%02d: ");\r
-       buffer.appendInt(m_files).append("/", 1).appendInt(m_traverser->directories()).append(" dir(s)");\r
-       buffer.appendInt(m_files).append("/", 1).appendInt(m_traverser->files()).append(" dir(s)");\r
+       buffer.appendInt(m_files).append("/", 1).appendInt(m_traverser.directories()).append(" dir(s)");\r
+       buffer.appendInt(m_files).append("/", 1).appendInt(m_traverser.files()).append(" dir(s)");\r
        buffer.append(currentFile);\r
        fputs(buffer.str(), stdout);\r
        return true;\r
 }\r
+\r
+/**\r
+ * Evaluates the arguments and calls the main function.\r
+ *\r
+ * @param argc                 number of arguments\r
+ * @param argv                 program arguments\r
+ * @param minArguments the command needs at least so many non option arguments\r
+ */\r
+void ReTool::run(int argc, const char** argv){\r
+       try {\r
+               m_programArgs.init(argc, argv);\r
+               if (m_programArgs.getArgCount() < m_minArguments)\r
+                       m_programArgs.help(i18n("too few arguments"), false, stdout);\r
+               doIt();\r
+    } catch (ReOptionException& exc) {\r
+        m_programArgs.help(exc.getMessage(), false, stdout);\r
+    }\r
+}\r
+/**\r
+ * Evaluates the non option arguments which are names of files or directories.\r
+ *\r
+ * For each non reserved argument it will be called either <code>processSingleFile()</code>\r
+ * or <code>processTree()</code>.\r
+ *\r
+ */\r
+void ReTool::processFileArguments(){\r
+       int max = m_programArgs.getArgCount() - m_reservedLast;\r
+       struct stat info;\r
+       // Test whether the arguments are files or directories:\r
+       for (int ii = m_reservedFirst; ii < max; ii++){\r
+               const char* arg = m_programArgs.getArg(ii);\r
+               if (stat(arg, &info) != 0)\r
+                       m_programArgs.help(\r
+                               ReByteBuffer(i18n("not a file or a directory: ")).append(arg).str(),\r
+                               false, stderr);\r
+       }\r
+       // process the files:\r
+       for (int ii = m_reservedFirst; ii < max; ii++){\r
+               const char* arg = m_programArgs.getArg(ii);\r
+               if (S_ISDIR(info.st_mode))\r
+                       processTree(arg);\r
+               else\r
+                       processSingleFile(arg);\r
+       }\r
+}\r
+/**\r
+ * Processes a single file.\r
+ *\r
+ * Gets the file info and calls <code>processFile()</code>.\r
+ *\r
+ * @param filename             the name of the file\r
+ */\r
+void ReTool::processSingleFile(const char* filename){\r
+       ReByteBuffer protocol;\r
+       ReByteBuffer path;\r
+       ReByteBuffer name;\r
+       ReByteBuffer ext;\r
+       ReStringUtils::splitPath(filename, &protocol, &path, &name, &ext);\r
+       protocol.append(path);\r
+       if (protocol.length() == 0)\r
+               protocol.append(".");\r
+       else\r
+               protocol.setLength(protocol.length() - 1);\r
+       m_traverser.changeBase(protocol.str());\r
+\r
+       name.append(ext);\r
+       setFilterFromProgramArgs(m_filter);\r
+       m_traverser.setPropertiesFromFilter(&m_filter);\r
+       ReDirStatus_t entry;\r
+       entry.m_path = protocol;\r
+       if (entry.findFirst()){\r
+               do {\r
+#if defined    __linux__\r
+                       bool found = strcmp(entry.node(), name.str()) == 0;\r
+#elif defined __WIN32__\r
+                       bool found = stricmp(entry.node(), name.str()) == 0;\r
+#endif\r
+                       if (found && m_filter.match(entry)){\r
+                               processFile(&entry);\r
+                               break;\r
+                       }\r
+               } while(entry.findNext());\r
+       }\r
+       entry.freeEntry();\r
+}\r
+/**\r
+ * Processes a directory tree.\r
+ *\r
+ * Finds all filtered files in this directory and its subdirs and call <code>processFile()</code>\r
+ * or <code>processDir()</code>.\r
+ *\r
+ * @param directory            the name of the directory\r
+ */\r
+void ReTool::processTree(const char* directory){\r
+       m_traverser.changeBase(directory);\r
+       setFilterFromProgramArgs(m_filter);\r
+       m_traverser.setPropertiesFromFilter(&m_filter);\r
+       ReDirStatus_t* entry;\r
+       int level;\r
+       while( (entry = m_traverser.nextFile(level, &m_filter)) != NULL){\r
+               if (entry->isDirectory())\r
+                       processDir(entry);\r
+               else\r
+                       processFile(entry);\r
+       }\r
+}\r
+/**\r
+ * Processes one file.\r
+ *\r
+ * Normally this method will be overwritten.\r
+ *\r
+ * @param entry        the properties of the file to process\r
+ */\r
+void ReTool::processFile(ReDirStatus_t* entry){\r
+       fprintf(m_output, "+++ ignored (not a directory): %s\n", entry->fullName());\r
+}\r
+/**\r
+ * Processes one directory.\r
+ *\r
+ * Normally this method will be overwritten.\r
+ *\r
+ * @param entry        the properties of the directory to process\r
+ */\r
+void ReTool::processDir(ReDirStatus_t* entry){\r
+       fprintf(m_output, "+++ ignored (not a file): %s\n", entry->fullName());\r
+}\r
+\r
 /**\r
  * Constructor.\r
  */\r
 ReDirStatisticData::ReDirStatisticData() :\r
-       m_sizes(0),\r
-       m_files(0),\r
-       m_dirs(0),\r
+       ReDirTreeStatistic(),\r
        m_path()\r
 {\r
 }\r
@@ -625,9 +809,6 @@ ReDirStatisticData::ReDirStatisticData() :
  * @param source       the source to copy\r
  */\r
 ReDirStatisticData::ReDirStatisticData(const ReDirStatisticData& source) :\r
-       m_sizes(source.m_sizes),\r
-       m_files(source.m_files),\r
-       m_dirs(source.m_dirs),\r
        m_path(source.m_path)\r
 {\r
 }\r
@@ -640,7 +821,7 @@ ReDirStatisticData::ReDirStatisticData(const ReDirStatisticData& source) :
 ReDirStatisticData& ReDirStatisticData::operator =(const ReDirStatisticData& source){\r
        m_sizes = source.m_sizes;\r
        m_files = source.m_files;\r
-       m_dirs = source.m_dirs;\r
+       m_directories = source.m_directories;\r
        m_path = source.m_path;\r
        return *this;\r
 }\r
@@ -649,10 +830,8 @@ ReDirStatisticData& ReDirStatisticData::operator =(const ReDirStatisticData& sou
  * Constructor.\r
  */\r
 ReDirStatistic::ReDirStatistic(int deltaList, int deltaBuffer) :\r
-    ReDirOptions(s_statisticUsage, s_statisticExamples),\r
-       m_list(deltaList, deltaBuffer),\r
-    m_traceInterval(0),\r
-    m_lastTrace(0)\r
+    ReTool(s_statisticUsage, s_statisticExamples, 2, 0, 1, false),\r
+       m_list(deltaList, deltaBuffer)\r
 {\r
        // standard short options: D d O o P p T t v y Z z\r
     m_programArgs.addBool("kbyte", \r
@@ -675,18 +854,215 @@ ReDirStatistic::~ReDirStatistic(){
 ReDirStatisticData& ReDirStatisticData::add(const ReDirStatisticData& source){\r
        m_sizes += source.m_sizes;\r
        m_files += source.m_files;\r
-       m_dirs += source.m_dirs;\r
+       m_directories += source.m_directories;\r
        return *this;\r
 }\r
 /**\r
  * Initializes the data of the instance.\r
  */\r
 void ReDirStatisticData::clear(){\r
-       m_sizes = 0;\r
-       m_files = 0;\r
-       m_dirs = 0;\r
+       ReDirTreeStatistic::clear();\r
        m_path.setLength(0);\r
 }\r
+/**\r
+ * Constructor.\r
+ */\r
+ReDirBatch::ReDirBatch() :\r
+    ReTool(s_batchUsage, s_batchExamples, 0, 0, 0, true),\r
+       m_arguments(),\r
+    m_script(),\r
+    m_isExe(false)\r
+{\r
+       // standard short options: D d O o P p T t v y Z z\r
+    m_programArgs.addString("first",\r
+        i18n("defines the first line of the output"),\r
+        '1', "first-line", true,\r
+#if defined __linux__\r
+        "#! /bin/sh"\r
+#elif defined __WIN32__\r
+        "rem this batch is created by dirtool"\r
+#endif\r
+        );\r
+    m_programArgs.addString("arguments",\r
+        i18n("template for the output line.\n"\r
+            "Possible placeholders: (e.g. e:\\data\\sample.txt)\n"\r
+            "   !full!: e:\\data\\sample.txt\n"\r
+            "   !path!: e:\\data\\\n"\r
+            "   !basename!: sample.txt\n"\r
+            "   !name!: sample\n"\r
+            "   !ext!: .txt\n"\r
+            "example: --arguments='echo !basename! in !path! found'"),\r
+        'a', "arguments", false, NULL);\r
+    m_programArgs.addString("script",\r
+        i18n("name of the script (starts each output line)"),\r
+        'c', "script", false, NULL);\r
+#if defined __WIN32__\r
+    m_programArgs.addBool("isexe",\r
+        i18n("supresses the starting 'call' of each output line"\r
+        "neccessary if a *.exe will be called (instead of a *.bat)"),\r
+        'x', "is-exe", false);\r
+#endif\r
+    addStandardFilterOptions();\r
+}\r
+\r
+static void replaceMakros(const char* arguments, ReDirStatus_t* entry, const char* delim, ReByteBuffer& line){\r
+    line.set(arguments, -1);\r
+    // we prepare the removal of unwanted delimiters in constructed placeholders:\r
+    // example: !path!!name!: without correction: "e:\\data\\""xxx"\r
+    // We want: "e:\\data\\xxx"\r
+    line.replaceAll("!!", 2, "!\x01!", 3);\r
+    ReByteBuffer replacement;\r
+    if (strstr(arguments, "!full!") != NULL){\r
+        replacement.set(delim, -1).append(entry->m_path);\r
+        replacement.append(entry->node(), -1).append(delim, -1);\r
+        line.replaceAll("!full!", 6, replacement.str(), replacement.length());\r
+    }\r
+    if (strstr(arguments, "!path!") != NULL){\r
+        replacement.set(delim, -1).append(entry->m_path).append(delim, -1);\r
+        line.replaceAll("!path!", 6, replacement.str(), replacement.length());\r
+    }\r
+    if (strstr(arguments, "!basename!") != NULL){\r
+        replacement.set(delim, -1).append(entry->node(), -1).append(delim, -1);\r
+        line.replaceAll("!basename!", 10, replacement.str(), replacement.length());\r
+    }\r
+    if (strstr(arguments, "!name!") != NULL){\r
+        replacement.set(delim, -1).append(entry->node(), -1);\r
+        int ix = replacement.rindexOf(".", 1);\r
+        if (ix > 1)\r
+            replacement.setLength(ix);\r
+        replacement.append(delim, -1);\r
+        line.replaceAll("!name!", 6, replacement.str(), replacement.length());\r
+    }\r
+    if (strstr(arguments, "!ext!") != NULL){\r
+        replacement.set(delim, -1).append(entry->node(), -1);\r
+        int ix = replacement.rindexOf(".", 1);\r
+        if (ix > 1)\r
+            replacement.remove(1, ix - 1);\r
+        else\r
+            replacement.setLength(1);\r
+        replacement.append(delim, -1);\r
+        line.replaceAll("!ext!", 5, replacement.str(), replacement.length());\r
+    }\r
+    // We remove the unwanted delimiters (see above):\r
+    ReByteBuffer buffer;\r
+    buffer.set(delim, -1).append("\x01", 1).append(delim, -1);\r
+    line.replaceAll(buffer.str(), buffer.length(), "", 0);\r
+}\r
+/**\r
+ * Creates the batch file.\r
+ */\r
+void ReDirBatch::doIt(){\r
+       ReByteBuffer buffer;\r
+       m_arguments.append(m_programArgs.getString("arguments", buffer), -1);\r
+       m_script.append(m_programArgs.getString("script", buffer), -1);\r
+       if (m_arguments.length() + m_script.length() == 0)\r
+               help(i18n("one of the option must be set: -a (--arguments) or -c (--script)"));\r
+#if defined __WIN32__\r
+       m_isExe = m_programArgs.getBool("isexe");\r
+#endif\r
+       processFileArguments();\r
+       if (m_verboseLevel >= V_SUMMARY){\r
+               int duration = int(time(NULL) - m_start);\r
+#if defined __linux__\r
+               const char* prefix = "#";\r
+#elif defined __WIN32__\r
+               const char* prefix = "rem";\r
+#endif\r
+               toString(buffer);\r
+               buffer.append(" ").appendTime(duration);\r
+               fprintf(m_output, "%s %s\n", buffer.str());\r
+       }\r
+}\r
+\r
+/**\r
+ * Processes one directory.\r
+ *\r
+ * @param entry        the properties of the directory to process\r
+ */\r
+void ReDirBatch::processDir(ReDirStatus_t* entry){\r
+       processFile(entry);\r
+}\r
+\r
+/**\r
+ * Processes one file.\r
+ *\r
+ * @param entry        the properties of the file to process\r
+ */\r
+void ReDirBatch::processFile(ReDirStatus_t* entry){\r
+       ReByteBuffer line;\r
+#if defined __linux__\r
+    static const char* delim = "'";\r
+#elif defined __WIN32__\r
+       static const char* delim = "\"";\r
+#endif\r
+       if (m_script.length() > 0){\r
+#if defined __WIN32__\r
+               if (! m_isExe)\r
+                       line.append("call ");\r
+#endif\r
+               line.append(m_script).append(" ").append(delim, -1);\r
+               line.append(entry->m_path).append(entry->node(), -1);\r
+               line.append(delim, -1);\r
+       } else {\r
+               replaceMakros(m_arguments.str(), entry, delim, line);\r
+       }\r
+       fprintf(m_output, "%s\n", line.str());\r
+}\r
+\r
+/**\r
+ * Constructor.\r
+ */\r
+ReDirList::ReDirList() :\r
+    ReTool(s_listUsage, s_listExamples, 0, 0, 0, true),\r
+    m_shortFormat(false)\r
+{\r
+    m_programArgs.addBool("short", i18n("output is only path and basename"),\r
+        '1', "--short", false);\r
+    addStandardFilterOptions();\r
+}\r
+\r
+/**\r
+ * Lists the metadata of the specified files.\r
+ */\r
+void ReDirList::doIt(){\r
+       m_shortFormat = m_programArgs.getBool("short");\r
+       processFileArguments();\r
+       if (m_verboseLevel >= V_SUMMARY){\r
+               int duration = int(time(NULL) - m_start);\r
+               ReByteBuffer line;\r
+               toString(line);\r
+               line.appendTime(duration).append(" ", 1).append(i18n("sec"));\r
+               fprintf(m_output, "=== %s\n", line.str());\r
+       }\r
+}\r
+\r
+/**\r
+ * Processes one directory.\r
+ *\r
+ * @param entry        the properties of the directory to process\r
+ */\r
+void ReDirList::processDir(ReDirStatus_t* entry){\r
+       processFile(entry);\r
+}\r
+/**\r
+ * Processes one file.\r
+ *\r
+ * @param entry        the properties of the file to process\r
+ */\r
+void ReDirList::processFile(ReDirStatus_t* entry){\r
+       ReByteBuffer bufferRights;\r
+       ReByteBuffer bufferTime;\r
+       if (m_shortFormat)\r
+               fprintf(m_output, "%s%s\n", entry->m_path.str(), entry->node());\r
+       else\r
+               fprintf(m_output, "%s %12.6f %s %02x %s%s\n",\r
+                       entry->rightsAsString(bufferRights),\r
+                       entry->fileSize() / 1E6,\r
+                       entry->filetimeAsString(bufferTime),\r
+                       entry->type(),\r
+                       entry->m_path.str(), entry->node());\r
+}\r
+\r
 \r
 /**\r
  * Calculates the statistic of a directory tree.\r
@@ -751,7 +1127,7 @@ const ReStringList& ReDirStatistic::calculate(const char* base, int level,
                        }\r
                }\r
                if (entry->isDirectory()){\r
-                       current->m_dirs++;\r
+                       current->m_directories++;\r
                } else if (! useFilter || filter.match(*entry)){\r
                        current->m_sizes += entry->fileSize();\r
                        current->m_files++;\r
@@ -782,39 +1158,32 @@ const ReStringList& ReDirStatistic::calculate(const char* base, int level,
  * @param arc   count of arguments in <code>argv</code>\r
  * @param argv  the program arguments.\r
  */\r
-void ReDirStatistic::run(int argc, const char* argv[]){\r
-    time_t start = time(NULL);\r
-    try {\r
-        m_programArgs.init(argc, argv);\r
-        if (m_programArgs.getArgCount() < 1)\r
-            m_programArgs.help("statistic: missing path", false, stdout);\r
-        int depth = 1;\r
-        if (m_programArgs.getArgCount() > 1){\r
-                       const char* arg1 = m_programArgs.getArg(1);\r
-                       if (ReStringUtils::lengthOfUnsigned(arg1, -1, (unsigned *) &depth) == 0)\r
-                               m_programArgs.help("depth is not an integer", false, stdout);\r
-               }\r
-        void (*proc) (const ReDirStatisticData& data,\r
-                           ReDirStatistic& parent, ReByteBuffer& line) = &formatWithSizeFilesAndDirs;\r
-        if (m_programArgs.getBool("kbyte"))\r
-            proc = &formatLikeDu;\r
-        const ReStringList& list = calculate(m_programArgs.getArg(0), depth, proc);\r
-        ReByteBuffer buffer;\r
-        for (size_t ix = 0; ix < list.count(); ix++){\r
-            buffer.set(list.strOf(ix), list.strLengthOf(ix));\r
-            fprintf(m_output, "%s\n", buffer.str());\r
-        }\r
-        if (m_verboseLevel >= V_SUMMARY){\r
-            int duration = int(time(NULL) - start);\r
-            fprintf(m_output, "=== duration: ");\r
-            if (duration >= 3600)\r
-                fprintf(m_output, "%d:", duration / 3600);\r
-            fprintf(m_output, "%02d:%02d\n", duration % 3600 / 60, duration % 60);\r
-        }\r
-    } catch (ReOptionException& exc) {\r
-        m_programArgs.help(exc.getMessage(), false, stdout);\r
-    }\r
+void ReDirStatistic::doIt(){\r
+       int depth = 1;\r
+       if (m_programArgs.getArgCount() > 1){\r
+               const char* arg1 = m_programArgs.getArg(1);\r
+               if (ReStringUtils::lengthOfUnsigned(arg1, -1, (unsigned *) &depth) == 0)\r
+                       m_programArgs.help("depth is not an integer", false, stdout);\r
+       }\r
+       void (*proc) (const ReDirStatisticData& data,\r
+                       ReDirStatistic& parent, ReByteBuffer& line) = &formatWithSizeFilesAndDirs;\r
+       if (m_programArgs.getBool("kbyte"))\r
+               proc = &formatLikeDu;\r
+       const ReStringList& list = calculate(m_programArgs.getArg(0), depth, proc);\r
+       ReByteBuffer buffer;\r
+       for (size_t ix = 0; ix < list.count(); ix++){\r
+               buffer.set(list.strOf(ix), list.strLengthOf(ix));\r
+               fprintf(m_output, "%s\n", buffer.str());\r
+       }\r
+       if (m_verboseLevel >= V_SUMMARY){\r
+               int duration = int(time(NULL) - m_start);\r
+               fprintf(m_output, "=== duration: ");\r
+               if (duration >= 3600)\r
+                       fprintf(m_output, "%d:", duration / 3600);\r
+               fprintf(m_output, "%02d:%02d\n", duration % 3600 / 60, duration % 60);\r
+       }\r
 }\r
+\r
 /**\r
  * Formats a line like the du (disk usage) command.\r
  *\r
@@ -850,9 +1219,73 @@ void formatWithSizeFilesAndDirs(const ReDirStatisticData& data,
     // Round up to the next KiByte:\r
     char buffer[256];\r
     _snprintf(buffer, sizeof buffer, "%14.6f MB %7d %7d\t",\r
-        data.m_sizes / 1E6, data.m_files, data.m_dirs);\r
+        data.m_sizes / 1E6, data.m_files, data.m_directories);\r
        line.append(buffer, -1).append(data.m_path);\r
 }\r
+/**\r
+ * Constructor.\r
+ */\r
+ReDirTouch::ReDirTouch() :\r
+       ReTool(s_touchUsage, s_touchExamples, 2, 1, 0, false),\r
+       m_buffer(),\r
+       m_properties()\r
+{\r
+       // standard short options: D d O o P p T t v y Z z\r
+    m_programArgs.addString("accessed",\r
+               i18n("the new access time.\n"\r
+                       "Formats: absolute, relative to now, relative to the current filetime\n"\r
+                       "[yyyy.mm.dd/]HH:MM:SS \n"\r
+                       "now-<count>{s|m|h|d}\n"\r
+                       "{+|-}<count>{s|m|h|d}"),\r
+        'a', "--accessed", false, NULL);\r
+    m_programArgs.addString("modified",\r
+               i18n("the new modification time.\n"\r
+                       "Formats: absolute, relative to now, relative to the current filetime\n"\r
+                       "[yyyy.mm.dd/]HH:MM:SS\n"\r
+                       "now-<count>{s|m|h|d}\n"\r
+                       "{+|-}<count>{s|m|h|d}"),\r
+        'm', "--modified", false, NULL);\r
+    addStandardFilterOptions();\r
+}\r
+/**\r
+ * Sets the filetime for the specified files.\r
+ */\r
+void ReDirTouch::doIt(){\r
+       ReByteBuffer buffer;\r
+       if (m_programArgs.getString("modified", buffer)[0] != '\0')\r
+               m_modified = checkDate(buffer.str());\r
+       if (m_programArgs.getString("accessed", buffer)[0] != '\0')\r
+               m_accessed = checkDate(buffer.str());\r
+       processFileArguments();\r
+       if (m_verboseLevel >= V_SUMMARY){\r
+               int duration = int(time(NULL) - m_start);\r
+               ReByteBuffer line;\r
+               toString(line);\r
+               line.appendTime(duration).append(" ", 1).append(i18n("sec"));\r
+               fprintf(m_output, "=== %s\n", line.str());\r
+       }\r
+}\r
+\r
+void ReDirTouch::processDir(ReDirStatus_t* entry){\r
+       processFile(entry);\r
+}\r
+void ReDirTouch::processFile(ReDirStatus_t* entry){\r
+\r
+}\r
+\r
+/**\r
+ * Sets file times.\r
+ *\r
+ * @param filename             the name of the file\r
+ * @param properties   contains modification and access time\r
+ * @param logger               NULL or the logger\r
+ */\r
+bool ReDirTouch::touch(const char* filename, ReFileProperties_t* properties,\r
+               ReLogger* logger){\r
+}\r
+\r
+\r
+\r
 /**\r
  * Prints a vector of lines.\r
  * \r
@@ -865,11 +1298,10 @@ static void printField(const char** lines){
 }\r
 \r
 /**\r
- * Prints an message how to use the statistic module and exits.\r
+ * Prints a message how to use the module and exits.\r
  */\r
-void ReDirTools::statisticUsage(){\r
-    ReDirStatistic statistic;\r
-    statistic.programArgs().help(NULL, false, stdout);\r
+void ReDirTools::usage(ReTool& tool){\r
+    tool.programArgs().help(NULL, false, stdout);\r
 }\r
 \r
 /**\r
@@ -880,236 +1312,13 @@ void ReDirTools::statisticUsage(){
  */\r
 void ReDirTools::usage(const char* msg, const char* msg2){\r
     printf ("Version: %s\n", m_version);\r
-    printf ("usage: dirtool <command> <opt>\n");\r
-    statisticUsage();\r
+    printf ("usage: dirtool <command> <opt>\n"\r
+               "call 'dirtool help' for more info\n");\r
     if (msg != NULL)\r
         printf ("+++ %s%s\n", msg, msg2 == NULL ? "" : msg2);\r
     exit(1);\r
 }\r
 \r
-/**\r
- * Constructor.\r
- */\r
-ReDirList::ReDirList() :\r
-    ReTool(s_listUsage, s_listExamples)\r
-{\r
-    m_programArgs.addBool("short", i18n("output is only path and basename"),\r
-        '1', "--short", false);\r
-    addStandardFilterOptions();\r
-}\r
-\r
-/**\r
- * Gets the arguments for the "list" command and execute this.\r
- *\r
- * @param argc      the number of arguments\r
- * @param argav     the argument vector\r
- */\r
-void ReDirList::list(int argc, const char* argv[]){\r
-    ReDirEntryFilter_t filter;\r
-    try {\r
-        time_t start = time(NULL);\r
-        m_programArgs.init(argc, argv);\r
-        bool shortOutput = m_programArgs.getBool("short");\r
-        setFilterFromProgramArgs(filter);\r
-        bool noPath = m_programArgs.getArgCount() == 0;\r
-        ReByteBuffer bufferRights;\r
-        ReByteBuffer bufferTime;\r
-        int64_t sumSizes = 0;\r
-        int files = 0;\r
-        int dirs = 0;\r
-        \r
-        for (int ix = 0; noPath || ix < m_programArgs.getArgCount(); ix++){\r
-            ReTraverser traverser(noPath ? "." : m_programArgs.getArg(ix));\r
-            noPath = false;\r
-                       traverser.setPropertiesFromFilter(&filter);\r
-            int level;\r
-            ReDirStatus_t* entry;\r
-            while( (entry = traverser.nextFile(level, &filter)) != NULL){\r
-                if (entry->isDirectory())\r
-                    dirs++;\r
-                else{\r
-                    files++;\r
-                    sumSizes += entry->fileSize();\r
-                }\r
-                if (! printOneFile(entry)){\r
-                    if (shortOutput)\r
-                        fprintf(m_output, "%s%s\n", entry->m_path.str(), entry->node());\r
-                    else\r
-                        fprintf(m_output, "%s %12.6f %s %02x %s%s\n",\r
-                            entry->rightsAsString(bufferRights), \r
-                            entry->fileSize() / 1E6, \r
-                            entry->filetimeAsString(bufferTime), \r
-                            entry->type(),\r
-                            entry->m_path.str(), entry->node());\r
-                }\r
-            }\r
-        }\r
-               if (m_verboseLevel >= V_SUMMARY){\r
-            int duration = int(time(NULL) - start);\r
-            fprintf(m_output, "+++ %d dirs and %d file(s) with %.6f MByte in %02d:%02d sec\n",\r
-                dirs, files, sumSizes / 1E6, duration / 60, duration % 60);\r
-        }\r
-    } catch(ReOptionException& exc){\r
-        help(exc.getMessage());\r
-    }\r
-}\r
-\r
-/**\r
- * Constructor.\r
- */\r
-ReDirBatch::ReDirBatch() :\r
-    ReTool(s_batchUsage, s_batchExamples)\r
-{\r
-       // standard short options: D d O o P p T t v y Z z\r
-    m_programArgs.addString("first",\r
-        i18n("defines the first line of the output"),\r
-        '1', "first-line", true,\r
-#if defined __linux__\r
-        "#! /bin/sh"\r
-#elif defined __WIN32__\r
-        "rem this batch is created by dirtool"\r
-#endif\r
-        );\r
-    m_programArgs.addString("arguments", \r
-        i18n("template for the output line.\n"\r
-            "Possible placeholders: (e.g. e:\\data\\sample.txt)\n"\r
-            "   !full!: e:\\data\\sample.txt\n"\r
-            "   !path!: e:\\data\\\n"\r
-            "   !basename!: sample.txt\n"\r
-            "   !name!: sample\n"\r
-            "   !ext!: .txt\n"\r
-            "example: --arguments='echo !basename! in !path! found'"),\r
-        'a', "arguments", false, NULL);\r
-    m_programArgs.addString("script",\r
-        i18n("name of the script (starts each output line)"),\r
-        'c', "script", false, NULL);\r
-#if defined __WIN32__\r
-    m_programArgs.addBool("isexe",\r
-        i18n("supresses the starting 'call' of each output line"\r
-        "neccessary if a *.exe will be called (instead of a *.bat)"),\r
-        'x', "is-exe", false);\r
-#endif\r
-    addStandardFilterOptions();\r
-}\r
-\r
-static void replaceMakros(const char* arguments, ReDirStatus_t* entry, const char* delim, ReByteBuffer& line){\r
-    line.set(arguments, -1);\r
-    // we prepare the removal of unwanted delimiters in constructed placeholders:\r
-    // example: !path!!name!: without correction: "e:\\data\\""xxx"\r
-    // We want: "e:\\data\\xxx"\r
-    line.replaceAll("!!", 2, "!\x01!", 3);\r
-    ReByteBuffer replacement;\r
-    if (strstr(arguments, "!full!") != NULL){\r
-        replacement.set(delim, -1).append(entry->m_path);\r
-        replacement.append(entry->node(), -1).append(delim, -1);\r
-        line.replaceAll("!full!", 6, replacement.str(), replacement.length());\r
-    }\r
-    if (strstr(arguments, "!path!") != NULL){\r
-        replacement.set(delim, -1).append(entry->m_path).append(delim, -1);\r
-        line.replaceAll("!path!", 6, replacement.str(), replacement.length());\r
-    }\r
-    if (strstr(arguments, "!basename!") != NULL){\r
-        replacement.set(delim, -1).append(entry->node(), -1).append(delim, -1);\r
-        line.replaceAll("!basename!", 10, replacement.str(), replacement.length());\r
-    }\r
-    if (strstr(arguments, "!name!") != NULL){\r
-        replacement.set(delim, -1).append(entry->node(), -1);\r
-        int ix = replacement.rindexOf(".", 1);\r
-        if (ix > 1)\r
-            replacement.setLength(ix);\r
-        replacement.append(delim, -1);\r
-        line.replaceAll("!name!", 6, replacement.str(), replacement.length());\r
-    }\r
-    if (strstr(arguments, "!ext!") != NULL){\r
-        replacement.set(delim, -1).append(entry->node(), -1);\r
-        int ix = replacement.rindexOf(".", 1);\r
-        if (ix > 1)\r
-            replacement.remove(1, ix - 1);\r
-        else\r
-            replacement.setLength(1);\r
-        replacement.append(delim, -1);\r
-        line.replaceAll("!ext!", 5, replacement.str(), replacement.length());\r
-    }\r
-    // We remove the unwanted delimiters (see above):\r
-    ReByteBuffer buffer;\r
-    buffer.set(delim, -1).append("\x01", 1).append(delim, -1);\r
-    line.replaceAll(buffer.str(), buffer.length(), "", 0);\r
-}\r
-/**\r
- * Gets the arguments for the "list" command and execute this.\r
- *\r
- * @param argc      the number of arguments\r
- * @param argav     the argument vector\r
- */\r
-void ReDirBatch::createBatch(int argc, const char* argv[]){\r
-    ReDirEntryFilter_t filter;\r
-    try {\r
-        time_t start = time(NULL);\r
-        m_programArgs.init(argc, argv);\r
-        ReByteBuffer buffer;\r
-        ReByteBuffer arguments(m_programArgs.getString("arguments", buffer), -1);\r
-        ReByteBuffer script(m_programArgs.getString("script", buffer), -1);\r
-        if (arguments.length() + script.length() == 0)\r
-            help(i18n("one of the option must be set: -a (--arguments) or -c (--script)"));\r
-#if defined __WIN32__\r
-        bool isExe = m_programArgs.getBool("isexe");\r
-#endif\r
-        setFilterFromProgramArgs(filter);\r
-        if (m_programArgs.getArgCount() == 0)\r
-            help(i18n("no arguments given (missing path)"));\r
-        if (m_programArgs.getString("first", buffer)[0] != '\0')\r
-            printf("%s\n", buffer.str());\r
-        int64_t sumSizes = 0;\r
-        int files = 0;\r
-        int dirs = 0;\r
-#if defined __linux__\r
-        const char* delim = "'";\r
-#elif defined __WIN32__\r
-        const char* delim = "\"";\r
-#endif\r
-        for (int ix = 0; ix < m_programArgs.getArgCount(); ix++){\r
-            ReTraverser traverser(m_programArgs.getArg(ix));\r
-            traverser.setPropertiesFromFilter(&filter);\r
-            int level;\r
-            ReDirStatus_t* entry;\r
-            ReByteBuffer line;\r
-            while( (entry = traverser.nextFile(level, &filter)) != NULL){\r
-                if (entry->isDirectory())\r
-                    dirs++;\r
-                else{\r
-                    files++;\r
-                    sumSizes += entry->fileSize();\r
-                }\r
-                if (script.length() > 0){\r
-                    line.setLength(0);\r
-#if defined __WIN32__\r
-                    if (! isExe)\r
-                        line.append("call ");\r
-#endif\r
-                    line.append(script).append(" ").append(delim, -1);\r
-                    line.append(entry->m_path).append(entry->node(), -1);\r
-                    line.append(delim, -1);\r
-                } else {\r
-                    replaceMakros(arguments.str(), entry, delim, line);\r
-                }\r
-                fprintf(m_output, "%s\n", line.str());\r
-            }\r
-        }\r
-        if (m_verboseLevel >= V_SUMMARY){\r
-            int duration = int(time(NULL) - start);\r
-#if defined __linux__\r
-            const char* prefix = "#";\r
-#elif defined __WIN32__\r
-            const char* prefix = "rem";\r
-#endif\r
-            fprintf(m_output, "%s %d dir(s) and %d file(s) with %.6f MByte in %02d:%02d sec\n",\r
-                prefix, dirs, files, sumSizes / 1E6, duration / 60, duration % 60);\r
-        }\r
-    } catch(ReOptionException& exc){\r
-        help(exc.getMessage());\r
-    }\r
-}\r
-\r
 /**\r
  * creates a subdirectory (and the parent directories if neccessary.\r
  *\r
@@ -1136,7 +1345,7 @@ void ReDirSync::makeDirWithParents(ReByteBuffer& path, int minWidth,
  * Constructor.\r
  */\r
 ReDirSync::ReDirSync() :\r
-    ReTool(s_syncUsage, s_syncExamples),\r
+    ReTool(s_syncUsage, s_syncExamples, 2, 0, 1, false),\r
     m_buffer()\r
 {\r
        // standard short options: D d O o P p T t v y Z z\r
@@ -1360,136 +1569,114 @@ bool ReDirSync::makeDirectory(const char* directory, int minLength,
 }\r
 \r
 /**\r
- * Gets the arguments for the "list" command and execute this.\r
- *\r
- * @param argc      the number of arguments\r
- * @param argav     the argument vector\r
+ * Synchronizes two directory trees.\r
  */\r
-void ReDirSync::synchronize(int argc, const char* argv[]){\r
+void ReDirSync::doIt(){\r
     ReDirEntryFilter_t filter;\r
     const char* sep = OS_SEPARATOR;\r
     struct stat info;\r
-    try {\r
-        time_t start = time(NULL);\r
-        m_programArgs.init(argc, argv);\r
-        ReByteBuffer buffer;\r
-        if (m_programArgs.getArgCount() < 2)\r
-            help(i18n("missing argument(s) (source / target)"));\r
-        ReByteBuffer target(m_programArgs.getArg(m_programArgs.getArgCount() - 1));\r
-        if (target.endsWith(sep, 1))\r
-             target.setLength(target.length() - 1);\r
-        if (stat(target.str(), &info) != 0)\r
-            help(i18n("target does not exist: $1"), target.str());\r
-        else if (! S_ISDIR(info.st_mode))\r
-            help(i18n("target is not a directory: $1"), target.str());\r
-        size_t lengthTargetBase = target.length();\r
-        bool addOnly = m_programArgs.getBool("add");\r
-               int maxFileTimeDiff = m_programArgs.getInt("timediff");\r
-               bool dry = m_programArgs.getBool("dry");\r
-               bool ignoreDate = m_programArgs.getBool("ignoredate");\r
-        bool mustExist = m_programArgs.getBool("mustexist");\r
-        setFilterFromProgramArgs(filter);\r
-        int64_t sumSizes = 0;\r
-        int files = 0;\r
-               int treeFiles = 0;\r
-               int treeDirs = 0;\r
-               int64_t treeSumSizes = 0ll;\r
-        ReByteBuffer source, targetFile;\r
-        for (int ix = 0; ix < m_programArgs.getArgCount() - 1; ix++){\r
-            source.set(m_programArgs.getArg(ix), -1);\r
-            target.setLength(lengthTargetBase);\r
-            bool endsWithSlash = source.endsWith(sep, 1);\r
-            if (endsWithSlash)\r
-                source.setLength(source.length() - 1);\r
-            if (stat(source.str(), &info) != 0)\r
-                help(i18n("source does not exist: $1"), source.str());\r
-            else if (! S_ISDIR(info.st_mode))\r
-                help(i18n("source is not a directory: $1"), source.str());\r
-            if (! endsWithSlash){\r
-                // the basename of the source will be appended to the target:\r
-                int startNode = source.rindexOf(sep, 1, 0, source.length() - 1);\r
-                target.append(OS_SEPARATOR, 1);\r
-                target.append(source.str() + startNode + 1, -1);\r
-            }\r
-            size_t ixSourceRelative = source.length();\r
-            size_t ixTargetRelative = target.length();\r
+       ReByteBuffer buffer;\r
+       ReByteBuffer target(m_programArgs.getArg(m_programArgs.getArgCount() - 1));\r
+       if (target.endsWith(sep, 1))\r
+                       target.setLength(target.length() - 1);\r
+       if (stat(target.str(), &info) != 0)\r
+               help(i18n("target does not exist: $1"), target.str());\r
+       else if (! S_ISDIR(info.st_mode))\r
+               help(i18n("target is not a directory: $1"), target.str());\r
+       size_t lengthTargetBase = target.length();\r
+       bool addOnly = m_programArgs.getBool("add");\r
+       int maxFileTimeDiff = m_programArgs.getInt("timediff");\r
+       bool dry = m_programArgs.getBool("dry");\r
+       bool ignoreDate = m_programArgs.getBool("ignoredate");\r
+       bool mustExist = m_programArgs.getBool("mustexist");\r
+       setFilterFromProgramArgs(filter);\r
+       int64_t sumSizes = 0;\r
+       int files = 0;\r
+       int treeFiles = 0;\r
+       int treeDirs = 0;\r
+       int64_t treeSumSizes = 0ll;\r
+       ReByteBuffer source, targetFile;\r
+       for (int ix = 0; ix < m_programArgs.getArgCount() - 1; ix++){\r
+               source.set(m_programArgs.getArg(ix), -1);\r
+               target.setLength(lengthTargetBase);\r
+               bool endsWithSlash = source.endsWith(sep, 1);\r
+               if (endsWithSlash)\r
+                       source.setLength(source.length() - 1);\r
+               if (stat(source.str(), &info) != 0)\r
+                       help(i18n("source does not exist: $1"), source.str());\r
+               else if (! S_ISDIR(info.st_mode))\r
+                       help(i18n("source is not a directory: $1"), source.str());\r
+               if (! endsWithSlash){\r
+                       // the basename of the source will be appended to the target:\r
+                       int startNode = source.rindexOf(sep, 1, 0, source.length() - 1);\r
+                       target.append(OS_SEPARATOR, 1);\r
+                       target.append(source.str() + startNode + 1, -1);\r
+               }\r
+               size_t ixSourceRelative = source.length();\r
+               size_t ixTargetRelative = target.length();\r
 \r
-            ReTraverser traverser(source.str());\r
-            traverser.setPropertiesFromFilter(&filter);\r
-            int level;\r
-            ReDirStatus_t* entry;\r
-            ReByteBuffer line;\r
-            while( (entry = traverser.nextFile(level, &filter)) != NULL){\r
-                               if (entry->isDirectory())\r
+               ReTraverser traverser(source.str());\r
+               traverser.setPropertiesFromFilter(&filter);\r
+               int level;\r
+               ReDirStatus_t* entry;\r
+               ReByteBuffer line;\r
+               while( (entry = traverser.nextFile(level, &filter)) != NULL){\r
+                       if (entry->isDirectory())\r
+                               continue;\r
+                       // append the new relative path from source to target:\r
+                       target.setLength(ixTargetRelative);\r
+                       target.append(entry->m_path.str() + ixSourceRelative, -1);\r
+                       if (stat(target.str(), &info) != 0)\r
+                               makeDirWithParents(target, ixTargetRelative, traverser);\r
+                       targetFile.set(target).append(entry->node(), -1);\r
+                       const char* targetRelativePath = targetFile.str() + ixTargetRelative + 1;\r
+                       bool exists = stat(targetFile.str(), &info) == 0;\r
+                       if (! exists && mustExist){\r
+                               if (m_verboseLevel == V_CHATTER)\r
+                                       fprintf(m_output, "-ignored: %s does not exist\n", targetRelativePath);\r
+                               continue;\r
+                       }\r
+                       if (exists){\r
+                               if (addOnly){\r
+                                       if (m_verboseLevel >= V_CHATTER)\r
+                                               fprintf(m_output, "~ignored: %s exists\n", targetRelativePath);\r
                                        continue;\r
-                // append the new relative path from source to target:\r
-                target.setLength(ixTargetRelative);\r
-                target.append(entry->m_path.str() + ixSourceRelative, -1);\r
-                if (stat(target.str(), &info) != 0)\r
-                    makeDirWithParents(target, ixTargetRelative, traverser);\r
-                targetFile.set(target).append(entry->node(), -1);\r
-                               const char* targetRelativePath = targetFile.str() + ixTargetRelative + 1;\r
-                bool exists = stat(targetFile.str(), &info) == 0;\r
-                if (! exists && mustExist){\r
-                                       if (m_verboseLevel == V_CHATTER)\r
-                                               fprintf(m_output, "-ignored: %s does not exist\n", targetRelativePath);\r
+                               }\r
+                               if (ignoreDate && entry->fileSize() == info.st_size){\r
+                                       if (m_verboseLevel >= V_CHATTER)\r
+                                               fprintf(m_output, "_ignored: %s same size\n", targetRelativePath);\r
                                        continue;\r
                                }\r
-                               if (exists){\r
-                                       if (addOnly){\r
-                                               if (m_verboseLevel >= V_CHATTER)\r
-                                                       fprintf(m_output, "~ignored: %s exists\n", targetRelativePath);\r
-                                               continue;\r
-                                       }\r
-                                       if (ignoreDate && entry->fileSize() == info.st_size){\r
-                                               if (m_verboseLevel >= V_CHATTER)\r
-                                                       fprintf(m_output, "_ignored: %s same size\n", targetRelativePath);\r
-                                               continue;\r
-                                       }\r
-                                       // target younger than source?\r
-                                       int diff = int(info.st_mtime - entry->filetimeToTime(entry->modified()));\r
-                                       if (! ignoreDate && info.st_mtime - entry->filetimeToTime(entry->modified())\r
-                                                       <= maxFileTimeDiff) {\r
-                                               if (m_verboseLevel >= V_CHATTER)\r
-                                                       fprintf(m_output, "=ignored: %s same time\n", targetRelativePath);\r
-                                               continue;\r
-                                       }\r
+                               // target younger than source?\r
+                               int diff = int(info.st_mtime - entry->filetimeToTime(entry->modified()));\r
+                               if (! ignoreDate && info.st_mtime - entry->filetimeToTime(entry->modified())\r
+                                               <= maxFileTimeDiff) {\r
+                                       if (m_verboseLevel >= V_CHATTER)\r
+                                               fprintf(m_output, "=ignored: %s same time\n", targetRelativePath);\r
+                                       continue;\r
                                }\r
-                               files++;\r
-                               sumSizes += entry->fileSize();\r
-                               if (m_verboseLevel >= V_NORMAL)\r
-                                       fprintf(m_output, "%c%s%s\n", exists ? '!' : '+', targetRelativePath,\r
-                                               dry ? " would be copied" : "");\r
-                               if (! dry)\r
-                                       copyFile(entry, targetFile.str());\r
                        }\r
-                       treeFiles += traverser.files();\r
-                       treeDirs += traverser.directories();\r
-                       treeSumSizes+= traverser.sizes();\r
-        }\r
-        if (m_verboseLevel >= V_SUMMARY){\r
-            int duration = int(time(NULL) - start);\r
-            fprintf(m_output, i18n(\r
-                               "=== copied:    %02d:%02d sec  %7d file(s) %12.6f MByte (%.3f MB/sec).\n"\r
-                               "=== tree  :  %5d dir(s) %7d file(s) %12.6f MByte\n"),\r
-                duration / 60, duration % 60, files, sumSizes / 1E6, \r
-                               sumSizes / 1E6 / (duration == 0 ? 1 : duration),\r
-                               treeDirs, treeFiles, treeSumSizes / 1E6);\r
-        }\r
-    } catch(ReOptionException& exc){\r
-        help(exc.getMessage());\r
-    }\r
-}\r
-\r
-/**\r
- * Gets the arguments for the "batch" command and execute this.\r
- *\r
- * @param argc      the number of arguments\r
- * @param argav     the argument vector\r
- */\r
-void ReDirTools::batch(int argc, const char* argv[]){\r
-    ReDirBatch batch;\r
-    batch.createBatch(argc, argv);\r
+                       files++;\r
+                       sumSizes += entry->fileSize();\r
+                       if (m_verboseLevel >= V_NORMAL)\r
+                               fprintf(m_output, "%c%s%s\n", exists ? '!' : '+', targetRelativePath,\r
+                                       dry ? " would be copied" : "");\r
+                       if (! dry)\r
+                               copyFile(entry, targetFile.str());\r
+               }\r
+               treeFiles += traverser.files();\r
+               treeDirs += traverser.directories();\r
+               treeSumSizes+= traverser.sizes();\r
+       }\r
+       if (m_verboseLevel >= V_SUMMARY){\r
+               int duration = int(time(NULL) - m_start);\r
+               fprintf(m_output, i18n(\r
+                       "=== copied:    %02d:%02d sec  %7d file(s) %12.6f MByte (%.3f MB/sec).\n"\r
+                       "=== tree  :  %5d dir(s) %7d file(s) %12.6f MByte\n"),\r
+                       duration / 60, duration % 60, files, sumSizes / 1E6,\r
+                       sumSizes / 1E6 / (duration == 0 ? 1 : duration),\r
+                       treeDirs, treeFiles, treeSumSizes / 1E6);\r
+       }\r
 }\r
 \r
 /**\r
@@ -1537,16 +1724,15 @@ void ReDirTools::help(int argc, const char* argv[]){
 }\r
 \r
 /**\r
- * Gets the arguments for the "statistic" command and execute this.\r
+ * Executes a command.\r
  *\r
  * @param argc      the number of arguments\r
- * @param argav     the argument vector\r
+ * @param argv         the argument vector\r
+ * @param tool         the tool which realizes the command\r
  */\r
-void ReDirTools::list(int argc, const char* argv[]){\r
-    ReDirList lister;\r
-    lister.list(argc, argv);\r
+void ReDirTools::run(int argc, const char* argv[], ReTool& tool){\r
+       tool.run(argc, argv);\r
 }\r
-\r
 /**\r
  * Gets the arguments for any command and execute this.\r
  *\r
@@ -1564,15 +1750,17 @@ int ReDirTools::main(int argc, char* orgArgv[]){
     argv++;\r
     const char* arg0 = argv[0];\r
     if (isArg("batch", arg0))\r
-        tools.batch(argc, argv);\r
+        ReDirBatch().run(argc, argv);\r
     else if (isArg("list", arg0))\r
-        tools.list(argc, argv);\r
+        ReDirList().run(argc, argv);\r
     else if (isArg("help", arg0))\r
         tools.help(argc, argv);\r
     else if (isArg("statistic", arg0))\r
-        tools.statistic(argc, argv);\r
+        ReDirStatistic().run(argc, argv);\r
     else if (isArg("synchronize", arg0))\r
-        tools.synchronize(argc, argv);\r
+        ReDirSync().run(argc, argv);\r
+    else if (isArg("touch", arg0))\r
+        ReDirTouch().run(argc, argv);\r
     else if (isArg("test", arg0)){\r
         void testAll();\r
         testAll();\r
@@ -1582,25 +1770,3 @@ int ReDirTools::main(int argc, char* orgArgv[]){
     return 0;\r
 }\r
 \r
-/**\r
- * Gets the arguments for the "statistic" command and execute this.\r
- *\r
- * @param argc      the number of arguments\r
- * @param argav     the argument vector\r
- */\r
-void ReDirTools::statistic(int argc, const char* argv[]){\r
-    ReDirStatistic statistic;\r
-    statistic.run(argc, argv);\r
-}\r
-\r
-/**\r
- * Gets the arguments for the "synchronize" command and execute this.\r
- *\r
- * @param argc      the number of arguments\r
- * @param argav     the argument vector\r
- */\r
-void ReDirTools::synchronize(int argc, const char* argv[]){\r
-    ReDirSync sync;\r
-    sync.synchronize(argc, argv);\r
-}\r
-\r
index 0cd22aac6e0f97d3306305f2af583024a6ade08c..87a49cac022548c5cd428f84439c80065471da02 100644 (file)
@@ -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 <code>processFileArguments()</code> 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;
 };
index 2194d27310ecc3b289a7c77601e8b00a5eb8b96a..6513a801f7e63dd4e95a8fe41bc1e1113397fd2e 100644 (file)
@@ -384,6 +384,40 @@ struct stat* ReDirStatus_t::getStatus() {
 }\r
 #endif\r
 \r
+/**\r
+ * Constructor.\r
+ */\r
+ReDirTreeStatistic::ReDirTreeStatistic() :\r
+       m_directories(0),\r
+       m_files(0),\r
+       m_sizes(0ll)\r
+{\r
+}\r
+/**\r
+ * Builds a string describing the data.\r
+ *\r
+ * @param buffer               IN/OUT: a buffer for the result\r
+ * @param append               <code>true</code>: the string will be appended to the buffer<br>\r
+ *                                             <code>false</code>: the buffer will be cleared at the beginning\r
+ * @param formatFiles  the <code>sprintf</code> format for the file count, e.g. "%8d"\r
+ * @param formatSized  the <code>sprintf</code> format for the MByte format, e.g. "%12.6f"\r
+ * @param formatFiles  the <code>sprintf</code> format for the directory count, e.g. "%6d"\r
+ * @return                             a human readable string\r
+ */\r
+const char* ReDirTreeStatistic::toString(ReByteBuffer& buffer, bool append,\r
+       const char* formatFiles, const char* formatSizes, const char* formatDirs)\r
+{\r
+       if (! append)\r
+               buffer.setLength(0);\r
+       buffer.appendInt(m_files, formatFiles);\r
+       buffer.append(i18n("file(s)")).append(" ", 1);\r
+       buffer.append(m_sizes / 1000.0 / 1000, formatSizes);\r
+       buffer.append(" ", 1).append(i18n("MByte")).append(" ", 1);\r
+       buffer.appendInt(m_directories, formatDirs);\r
+       buffer.append(i18n("dirs(s)"));\r
+       return buffer.str();\r
+}\r
+\r
 /**\r
 * Constructor.\r
 *\r
@@ -424,6 +458,7 @@ bool ReTraceUnit::trace(const char* message){
  * @param base         the base directory. The traversal starts at this point\r
  */\r
 ReTraverser::ReTraverser(const char* base, ReTraceUnit* tracer) :\r
+       ReDirTreeStatistic(),\r
     m_minLevel(0),\r
     m_maxLevel(512),\r
        m_level(-1),\r
@@ -431,9 +466,6 @@ ReTraverser::ReTraverser(const char* base, ReTraceUnit* tracer) :
     // m_dirs\r
     m_passNoForDirSearch(2),\r
     m_dirPatterns(NULL),\r
-       m_directories(0),\r
-       m_files(0),\r
-       m_sizes(0ll),\r
        m_tracer(tracer)\r
 {\r
        memset(m_dirs, 0, sizeof m_dirs);\r
@@ -448,6 +480,29 @@ ReTraverser::ReTraverser(const char* base, ReTraceUnit* tracer) :
  * Destructor.\r
  */\r
 ReTraverser::~ReTraverser() {\r
+       destroy();\r
+}\r
+\r
+/**\r
+ * Initializes the instance to process a new base.\r
+ *\r
+ * @param base the base directory to search\r
+ */\r
+void ReTraverser::changeBase(const char* base){\r
+       destroy();\r
+       m_base.setLength(0).append(base);\r
+       memset(m_dirs, 0, sizeof m_dirs);\r
+       m_dirs[0] = new ReDirStatus_t();\r
+       // remove a preceeding "./". This simplifies the pattern expressions:\r
+       if (m_base.startsWith(ReByteBuffer(".").append(OS_SEPARATOR, 1).str())){\r
+               m_base.remove(0, 2);\r
+       }\r
+}\r
+\r
+/**\r
+ * Releases the resources.\r
+ */\r
+void ReTraverser::destroy() {\r
        for (size_t ix = 0; ix < sizeof m_dirs / sizeof m_dirs[0]; ix++){\r
                if (m_dirs[ix] != NULL){\r
                        m_dirs[ix]->freeEntry();\r
@@ -456,7 +511,6 @@ ReTraverser::~ReTraverser() {
                }\r
        }\r
 }\r
-\r
 /**\r
  * Returns the info about the next file in the directory tree traversal.\r
  *\r
index 9f6ff782ddb2ce684ee03b50a411f387224b2cc3..50576746df6c05deaed048c1be43e35827ff3f7e 100644 (file)
@@ -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;
 };