]> gitweb.hamatoma.de Git - crepublib/commitdiff
bugfixing
authorHamatoma <git.tortouse@hm.f-r-e-i.de>
Fri, 6 Feb 2015 00:02:50 +0000 (01:02 +0100)
committerHamatoma <git.tortouse@hm.f-r-e-i.de>
Fri, 6 Feb 2015 00:02:50 +0000 (01:02 +0100)
base/ReByteBuffer.cpp
base/ReHashList.cpp
base/ReHashList.hpp
base/ReProgramArgs.cpp
base/ReSeqArray.cpp
os/ReDirTools.cpp

index 724d7ef4f1d1f642f25598b1bf5f1343932be420..ded7ee8e2f195da85c29a0e1f4bcbcea0c6ec86c 100644 (file)
@@ -57,7 +57,7 @@ ReByteBuffer::ReByteBuffer(const Byte* source, size_t sourceLength) :
        // m_primaryBuffer
        m_buffer(m_primaryBuffer),
        m_length(0),
-       m_capacity(sizeof m_primaryBuffer),
+       m_capacity(sizeof m_primaryBuffer - 1),
        m_reallocation(0)
 {
        m_buffer[0] = '\0';
@@ -227,21 +227,21 @@ ReByteBuffer& ReByteBuffer::appendHexDump(const char* data, size_t length,
                if (withAscii){
                        if (separator != NULL)
                                append(separator, -1);
+                       char cc;
+                       for (ix = 0; ix < bytesPerLine; ix++){
+                               if (ix < (int) length)
+                                       appendInt( (cc = data[ix]) < ' ' || cc > 127 ? '.' : cc, "%c");
+                               else
+                                       append(" ");
+                               if (ix < bytesPerLine - 1 && gapBehind > 0
+                                               && (gapBehind == 1 || ix % gapBehind == gapBehind - 1))
+                                       append(" ", 1);
+                       }
+                       append("\n", 1);
+                       length = length <= (size_t) bytesPerLine ? 0 : length - bytesPerLine;
+                       data += bytesPerLine;
+                       offset += bytesPerLine;
                }
-               char cc;
-               for (ix = 0; ix < bytesPerLine; ix++){
-                       if (ix < (int) length)
-                               appendInt( (cc = data[ix]) < ' ' || cc > 127 ? '.' : cc, "%c");
-                       else
-                               append(" ");
-                       if (ix < bytesPerLine - 1 && gapBehind > 0
-                                       && (gapBehind == 1 || ix % gapBehind == gapBehind - 1))
-                               append(" ", 1);
-               }
-               append("\n", 1);
-               length = length <= (size_t) bytesPerLine ? 0 : length - bytesPerLine;
-               data += bytesPerLine;
-               offset += bytesPerLine;
        }
     return *this;
 }
index 123c3f9d3cccec3625746fb7189dd5c56cf63d8b..a2172775b651bb097ffad74d0828ad6424366b97 100644 (file)
@@ -206,6 +206,20 @@ void ReHashList::setCapacity(int maxKeys, int keySpace, int contentSpace){
        m_keys.setCapacity(maxKeys, keySpace);
        m_values.setCapacity(maxKeys, contentSpace);
 }
+/**
+ * Sets the sizes (maximal length) of the strings.
+ *
+ * @param sizeOfKeyString      storage size of the key string:<br>
+ *                                                     1: maximal string length = 255,<br>
+ *                                                     2: maximal string length = 64k ...
+ * @param sizeOfValueString    storage size of the key string:<br>
+ *                                                     1: maximal string length = 255,<br>
+ *                                                     2: maximal string length = 64k ...
+ */
+void ReHashList::setSizes(int sizeOfKeyString, int sizeOfValueString){
+       m_keys.setSizes(4, sizeOfKeyString);
+       m_values.setSizes(0, sizeOfValueString);
+}
 
 /**
  * Appends the status data of the instance onto the buffer.
index bd0618a1e4fede9c7243ed7cd1c5ee57cd372fa1..f9cb18353d9ddedeaa72848af3a3df30159fca07 100644 (file)
@@ -44,6 +44,7 @@ public:
        void put(const char* key, const char* value);
        void put(const ReByteBuffer& key, const ReByteBuffer& value);
        void setCapacity(int maxKeys, int keySpace, int contentSpace);
+       void setSizes(int sizeOfKeyString, int sizeOfValueString);
        const char* status(ReByteBuffer& buffer, const char* prefix = NULL) const;
 protected:
        int find(const Byte* key, size_t length) const;
index 7a2c2e703a1eddd7166b53b9ef885d58c57d4550..1ef93f9a18c7d70f9a60d8be77baec4c57b113b9 100644 (file)
@@ -93,6 +93,7 @@ ReProgramArgs::ReProgramArgs(const char* usageString, const char* examples)
     m_program("?"),
        m_lastError()
 {
+       m_values.setSizes(1, 2);
        m_usage.split(usageString, '\n');
        if (examples != NULL){
                if (strstr(examples, "$0") == NULL)
index 8b73fbbda6481d30863aca11a4e2266982fc9216..efe1f8fdbf5d2c5c0782db2bb0ef9e4e19b705c0 100644 (file)
@@ -66,7 +66,7 @@ ReSeqArray::ReSeqArray(int deltaList, int deltaBuffer) :
        m_content(deltaBuffer),
        m_list(deltaList),
        m_lost(0),
-       m_entrySize(sizeof(Index) + 1 + 8),
+       m_entrySize(sizeof(Index) + 1 + sizeof (void*)),
        m_commonSize(INDIVIDUAL_SIZE),
        m_sizeOfTag(sizeof (void*)),
        m_sizeOfLength(1),
@@ -462,7 +462,13 @@ void ReSeqArray::set(Index index, const Byte* source,
                        indexContent = m_content.length();
                        m_content.append(source, sourceLength);
                }
+               printf("vor:\n%s\n", ReByteBuffer("")
+                       .appendHexDump(m_list.str(), m_entrySize*(1+index), 0,
+                               m_entrySize*2, "%4d: ", false, m_entrySize, m_entrySize).str());
                setSequence(seq, indexContent, sourceLength, tag);
+               printf("dann:\n%s\n", ReByteBuffer("")
+                       .appendHexDump(m_list.str(), m_entrySize*(1+index), 0,
+                               m_entrySize*2, "%4d: ", false, m_entrySize, m_entrySize).str());
        }
 }
 
@@ -684,6 +690,7 @@ void ReSeqArray::setSizes(int sizeOfTag, int sizeOfLength, size_t constantSize){
                constantSize = 0;
        }
        m_commonSize = constantSize;
+       m_entrySize = sizeof(Index) + m_sizeOfLength + m_sizeOfTag;
 }
 
 /** @brief Sorts the list.
index f125b6b3b4c49cd335fc02bbb7b0c9a29b3e438f..11d9bce8c35f46ab6f733045bd7cf4fd2002b0b5 100644 (file)
-/*
- * ReDirTools.cpp
- *
- * License: Public domain
- * Do what you want.
- * No warranties and disclaimer of any damages.
- * The latest sources: https://github.com/republib
- */
-
-#include "base/rebase.hpp"
-#include "os/reos.hpp"
-
-enum LOCATION_DIRTOOL {
-       LC_COPY_FILE_1 = LC_DIRTOOLS + 1, // 50101
-       LC_COPY_FILE_2, // 50102
-       LC_COPY_FILE_3, // 50103
-       LC_COPY_FILE_4, // 50104
-       LC_COPY_FILE_5, // 50105
-       LC_COPY_FILE_6, // 50106
-       LC_MAKE_DIR_1,  // 50107
-       LC_MAKE_DIR_2,  // 50108
-       LC_SET_PROPERTIES_1,    // 50109
-       LC_SET_PROPERTIES_2,    // 50110
-       LC_SET_PROPERTIES_3,    // 50111
-};
-const char* ReDirTools::m_version = "2015.02.04";
-
-static const char* s_helpSummary[] = {
-    "dirtool or dt <command> <opts>",
-    "   Useful commands around directory trees.",
-    "   Type 'dirtool help <command>' for more help.",
-    "<command>:",
-    "batch         produce output to handle the found files with a script",
-    "help          shows info about the arguments/options",
-    "list          shows the meta data of the selected files",
-    "statistic     shows statistics about a direcctory tree",
-    "synchronize   copies only modified or new files from",
-    "              from a source directory tre to a target",
-    NULL
-};
-
-const char* s_batchUsage[] = {
-    "<command>: 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)",
-    "   then it follows the full filename of the found file",
-    "   use --arguments or --script to configure the output line",
-    NULL
-};
-const char* s_batchExamples[] = {
-    "dirtool batch -cbackup.bat --basename-pattern=;*.txt;*.doc e:\\data",
-    "dirtool batch --type=r '--arguments=backup.sh !basename! !path!' usr etc",
-    NULL
-};
-
-const char* s_listUsage[] = {
-    "<command>: list",
-    "   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" ,
-    NULL
-};
-
-static const char* s_statisticUsage[] = {
-    "<command>:"
-    "st(atistic)  [<opts_stat>] <path> [<depth>]",
-    "          shows a statistic about a directory tree",
-    "<path>    a directory path: relative or absolute",
-    "<depth>   0: only the summary of <path> will be shown",
-    "          1: shows the summery of each subdir of <path> and the total",
-    "          n: shows the summery of each subdir until level <n> and the total",
-    "          default: 1",
-    "<opts_stat>:",
-    NULL
-};
-const char* s_statisticExamples[] = {
-    "dirtool st -q -t0 e:\\windows",  
-    "dirtool statistic --quiet --kbyte --trace-interval=60 ../mail 2",  
-    "dirtool stat -q --kbyte d:data 2",  
-    NULL
-};
-
-const char* s_syncUsage[] = {
-    "<command>:",
-    "sy(nchronize) <opts> <source1> [<source2> ...] <target>",
-    "   Synchronizes the content of a directory tree with another.",
-    "   Newer or missing files will be copied",
-    "   If a source name ends with the separator (/ in linux, \\ in win) the target",
-    "   will be completed with the basename of the source.",
-    "   Example:",
-    "   'sync home backup' copies the file home/x.txt to backup/home/x.txt",
-    "   'sync data/ backup/data2' copies the file home/x.txt to backup/data2/x.txt",
-    NULL
-};
-const char* s_syncExamples[] = {
-    "dirtool sync --basename-pattern=;*.txt;*.doc e:\\data\\ d:\\backup\\data2",
-    "dirtool sync --type=r --max-size=1G usr etc /media/backup",
-    NULL
-};
-
-/**
- * Constructor.
- *
- * @param usage     a string vector with a message how to use the command
- * @param example   a string vector with some examples how to use the command
- */
-ReDirOptions::ReDirOptions(const char* usage[], const char* examples[]) :
-       ReTraceUnit(INT_MAX, INT_MAX),
-    m_programArgs(usage, examples),
-    m_nodePatterns(),
-    m_pathPatterns(),
-    m_compoundUsage(NULL),
-    m_countCompoundUsage(0),
-    m_output(stdout),
-       m_verboseLevel(V_NORMAL)
-{
-       m_nodePatterns.setIgnoreCase(true);
-       m_pathPatterns.setIgnoreCase(true);
-}
-/**
- * Destructor.
- */
-ReDirOptions::~ReDirOptions(){
-       close();
-       delete[] m_compoundUsage;
-}
-/**
- * Adds a usage component to the compound usage message list.
- * @param usage     a string vector containing a part of the usage message
- */
-void ReDirOptions::addCompoundUsage(const char** usage){
-    int start = 0;
-    while(m_compoundUsage[start] != NULL){
-        if (++start >= m_countCompoundUsage)
-            assert(false);
-    }
-    for (int ix = 0; usage[ix] != NULL; ix++){
-        if (start + ix > m_countCompoundUsage){
-            assert(false);
-            break;
-        }
-        m_compoundUsage[start + ix] = usage[ix];
-    }
-}
-
-/**
- * Adds the standard filter options.
- */
-void ReDirOptions::addStandardFilterOptions(){
-       // standard short options: D d O o P p T t v y Z z
-    m_programArgs.addInt("maxdepth",
-        i18n("the depth of the subdirectory is lower or equal <number>\n"
-        "0: search is done only in the base directory"),
-        'D', "max-depth", 512);
-    m_programArgs.addInt("mindepth", 
-        i18n("the depth of the subdirectory is greater or equal <number>\n"
-            "0: search is done in all subdirectories"),
-        'd', "min-depth", 0);
-    m_programArgs.addString("output",
-        i18n("the name of the output file.\n"
-            "The output will written to this file instead of stdout"),
-        'O', "output-file", false, NULL);
-    m_programArgs.addString("older",
-        i18n("the modification date is older than <string>\n"
-            "<string> is a date (e.g. 2015.02.17) or number followed by an unit\n"
-            "units: m(inutes) h(hours), d(days). Default: m(inutes)\n"
-            "examples: -o25 --older-than=30d -o24h -o2009.3.2/12:00 -o1999.01.01"),
-        'o', "older-than", false, NULL);
-    m_programArgs.addString("pathpattern", 
-        i18n("a list of patterns for the path (without basename)\n"
-            "the separator is the first character of the list\n"
-            "Each pattern can contain '*' as wildcard\n"
-            "If the first character is '-' the pattern is a 'not pattern':\n"
-            "A directory will be entered if at least one of the positive patterns\n"
-            "and none of the 'not patterns' matches\n"
-            "examples:\n"
-            "';music;pic*' enters music and xy/Music and PIC and pictures but not xy/pic and img\n"
-                       "';*;-.git;.hg' ignores .git and xy/z/.git and .ht"),
-               'P', "path-pattern", false, NULL);
-    m_programArgs.addString("nodepattern",
-        i18n("a list of patterns for the basename (name without path) separated by ';'\n"
-            "Each pattern can contain '*' as wildcard\n"
-            "If the first character is '-' the pattern is a 'not pattern':\n"
-            "A file will be found if at least one of the positive patterns and none\n"
-            "of the 'not patterns' matches\n"
-            "examples: '*.cpp;*.hpp;Make*' '*;-*.bak;-*~"),
-        'p', "basename-pattern", false, NULL);
-    m_programArgs.addString("verbose",
-        i18n("verbose level: 0: no info, 1: summary only, 2: normal, 3: chatter mode, 4: debug"),
-        'v', "verbose", false, "1");
-    m_programArgs.addInt("trace",
-        i18n("all <number> seconds the current path will be traced\n"
-            "0: no trace"),
-        'T', "trace-interval", 0);
-    m_programArgs.addString("type",
-        i18n("the file type\n"
-            "<string> is a list of <v> values:\n"
-            "<v>: b(lock) c(har) d(irectory) (l)i(nkdir) l(ink) o(ther)\n"
-            "     p(ipe) s(ocket) r(egular)\n"
-            "<v>-sets: S(pecial)=bcspo N(ondir)=Slr\n"
-            "examples: -td --type=dr -tNi"), 
-        't', "type", false, NULL);
-    m_programArgs.addString("younger",
-        i18n("the modification date is younger than <string>\n"
-            "<string> is a date (e.g. 2015.02.17) or number followed by an unit\n"
-            "units: m(inutes) h(hours), d(days). Default: m(inutes)"),
-        'y', "younger-than", false, NULL);
-    m_programArgs.addString("maxsize",
-        i18n("the filesize is greater or equal <string>\n"
-            "<string> is a number followed by an unit\n"
-            "units: b(yte) k(Byte) K(iByte) m(Byte), M(iByte), g(Byte) G(iByte)\n"
-            "examples: -Z50m --max-size=1G"),
-        'Z', "max-size", false, NULL);
-    m_programArgs.addString("minsize",
-        i18n("the filesize is greater or equal <string>\n"
-            "<string> is a number followed by an unit\n"
-            "units: b(yte) k(Byte) K(iByte) m(Byte), M(iByte), g(Byte) G(iByte)\n"
-            "examples: -z50m --min-size=1G"),
-        'z', "min-size", false, NULL);
-}
-
-/**
- * Checks whether the given value is a time expression.
- *
- * Possible: <date> <date_time> <n><unit>
- * Units: m(inutes) h(our) d(ays)
- *
- * @param value     value to check
- * @return          the value converted into the absolute time
- * @throws          ReOptionExecption
- */
-time_t ReDirOptions::checkDate(const char* value){
-    ReByteBuffer theValue(value, -1);
-    time_t rc = 0;
-    if (theValue.count(".") == 2){
-        // a date:
-        int year, month, day;
-        int hour = 0;
-        int minute = 0;
-        switch (sscanf(value, "%d.%d.%d/%d:%dc", &year, &month, &day, &hour, &minute)){
-        case 3:
-        case 4:
-        case 5:
-        {
-            if (year < 1970)
-                throw ReOptionException(&m_programArgs, 
-                    i18n("date < 1970.01.01: "), value);
-            struct tm time;
-            memset(&time, 0, sizeof time);
-            time.tm_year = year - 1900;
-            time.tm_mon = month - 1;
-            time.tm_mday = day;
-            time.tm_hour = hour;
-            time.tm_min = minute;
-            rc = mktime(&time);
-            break;
-        }
-        default:
-            throw ReOptionException(&m_programArgs, 
-                i18n("invalid date/date-time value. yyyy.mm.dd/hh:MM expected"), value);
-        }
-    } else {
-        // a time distance value:
-        char unit = 'm';
-        int count = 0;
-        switch(sscanf(value, "%d%c", &count, &unit)){
-        case 1:
-        case 2:
-            switch(unit){
-            case 'm':
-                count *= 60;
-                break;
-            case 'h':
-                count *= 60*60;
-                break;
-            case 'd':
-                count *= 24*60*60;
-                break;
-            default:
-                throw ReOptionException(&m_programArgs, 
-                    i18n("invalid unit. expected: m(inutes) h(ours) d(ays)"), value);
-            }
-            rc = time(NULL) - count;
-            break;
-        default:
-            throw ReOptionException(&m_programArgs, 
-                i18n("invalid relative time value <number><unit> expected. <unit>: m h d"), 
-                value);
-        }
-    }
-    return rc;
-}
-/**
- * Checks whether the given value is a time expression.
- *
- * Possible: <date> <date_time> <n><unit>
- * Units: m(inutes) h(our) d(ays)
- *
- * @param value     value to check
- * @return          the value (multiplied with the unit factor)
- * @throws          ReOptionExecption
- */
-time_t ReDirOptions::checkSize(const char* value){
-    int64_t rc = 0;
-    char unit = 'b';
-    switch (sscanf(value, "%lld%c", (long long int*) &rc, &unit)){
-    case 1:
-    case 2:
-        switch(unit){
-        case 'b':
-            break;
-        case 'k':
-            rc *= 1000;
-            break;
-        case 'K':
-            rc *= 1024;
-            break;
-        case 'm':
-            rc *= 1000*1000;
-            break;
-        case 'M':
-            rc *= 1024*1024;
-            break;
-        case 'g':
-            rc *= 1000LL*1000*1000;
-            break;
-        case 'G':
-            rc *= 1024LL*1024*1024;
-            break;
-        default:
-             throw ReOptionException(&m_programArgs, 
-                i18n("invalid <unit>. Expected: b k K m M g G"), 
-                value);
-        }
-        break;
-    default:
-        throw ReOptionException(&m_programArgs, 
-            i18n("invalid size value: <number><unit> <unit>: b k K m M g G"), 
-            value);
-    }
-    return rc;
-}
-/**
- * Checks whether the given value is a valid pattern list.
- *
- * The first character must be a separator (not '*', '.'  and not a letter)
- *
- * @param value     value to check
- * @return          the <cpde>value</code> (for chaining)
- * @throws          ReOptionExecption
- */
-const char* ReDirOptions::checkPatternList(const char* value){
-    if (isalnum(*value) || *value == '_' || *value == '*'
-                        || *value == '.' || *value == '-')
-         throw ReOptionException(&m_programArgs, 
-            i18n("invalid separator (first character): $1 use ';' instead"), 
-            value);
-       if (strchr(value, OS_SEPARATOR_CHAR) != NULL)
-               throw ReOptionException(&m_programArgs, 
-            i18n("slash not allowed in pattern list: $2"), value);
-    return value;
-}
-/**
- * Checks whether the given value is a valid filetype list.
- *
- * @param value     value to check
- * @return          the bitmask
- * @throws          ReOptionExecption
- */
-ReDirStatus_t::Type_t ReDirOptions::checkType(const char* value){
-    int rc = ReDirStatus_t::TF_UNDEF;
-    while (*value != '\0'){
-        switch(*value){
-        case 'b':
-            rc |= ReDirStatus_t::TF_BLOCK;
-            break;
-        case 'c':
-            rc |= ReDirStatus_t::TF_CHAR;
-            break;
-        case 'd':
-            rc |= ReDirStatus_t::TF_SUBDIR;
-            break;
-        case 'i':
-            rc |= ReDirStatus_t::TF_LINK_DIR;
-            break;
-        case 'l':
-            rc |= ReDirStatus_t::TF_LINK;
-            break;
-        case 'o':
-            rc |= ReDirStatus_t::TF_OTHER;
-            break;
-        case 'p':
-            rc |= ReDirStatus_t::TF_PIPE;
-            break;
-        case 's':
-            rc |= ReDirStatus_t::TF_SOCKET;
-            break;
-        case 'r':
-            rc |= ReDirStatus_t::TF_REGULAR;
-            break;
-        case 'S':
-            rc |= ReDirStatus_t::TC_SPECIAL;
-            break;
-        case 'N':
-            rc |= ReDirStatus_t::TC_NON_DIR;
-            break;
-        case ' ':
-        case ',':
-            break;
-       default:
-           throw ReOptionException(&m_programArgs, 
-                i18n("invalid type: $1 Expected: b(lock) c(har) d(irectory)"
-                " (l)i(nkdir) l(ink) o(ther) p(ipe) s(ocket) r(egular)"
-                " S(pecial=bcspo) N(ondir=Slr)"), 
-                value);
-        }
-        value++;
-    }
-    return (ReDirStatus_t::Type_t) rc;
-}
-
-/**
- * Prints a help message, the error message and exits.
- *
- * @param errorMessage  the error message.
- * @param message2      an additional message
- */
-
-void ReDirOptions::help(const char* errorMessage, const char* message2) const{
-    ReByteBuffer msg;
-    if (errorMessage != 0)
-        msg.append(errorMessage, -1);
-    if (message2 != NULL)
-        msg.append(message2, -1);
-    m_programArgs.help(msg.str(), false, stdout);
-    exit(1);
-}
-
-/**
- * Checks the correctness of the standard filter options.
- * 
- * @throws 
- */
-void ReDirOptions::checkStandardFilterOptions(){
-    ReByteBuffer buffer;
-    checkDate(m_programArgs.getString("older", buffer));
-    checkDate(m_programArgs.getString("younger", buffer));
-    checkType(m_programArgs.getString("types", buffer));
-    checkSize(m_programArgs.getString("maxsize", buffer));
-    checkSize(m_programArgs.getString("minsize", buffer));
-    checkPatternList(m_programArgs.getString("nodepattern", buffer));    
-    checkPatternList(m_programArgs.getString("pathpattern", buffer));
-       if (m_programArgs.getString("verbose", buffer)[0] != '\0'){
-               unsigned level = V_NORMAL;
-               if (ReStringUtils::lengthOfUnsigned(buffer.str(), -1, &level)
-                               != buffer.length())
-                       help(i18n("verbose level is not a number (or '')"), buffer.str());
-               else
-                       m_verboseLevel = VerboseLevel(level);
-       }
-}
-
-/**
- * Frees the resources.
- */
-void ReDirOptions::close(){
-       if (m_output != stdout){
-               fclose(m_output);
-               m_output = stdout;
-       }
-}
-
-/**
- * Initializes the compound usage message array.
- *
- * @param size      the size of the array: size = (field1 + field2 + ...) * sizeof(const char*)
- */
-void ReDirOptions::initCompoundUsage(size_t size){
-    delete[] m_compoundUsage;
-    int count = size / sizeof m_compoundUsage[0];
-    m_compoundUsage = new const char*[count];
-    memset(m_compoundUsage, 0, size);
-    m_countCompoundUsage = count;
-}
-
-/**
- * Optimizes the path patterns.
- *
- * For all patterns of the list:
- * <ul><li>remove a trailing "\" and "\*"</li>
- * <li>change "*\xy" to "*\xy" and "xy" (finds xy in the root directory)</li>
- * <li>replaces "/" with the os specific path separator</li>
- * </ul>
- *
- * @param buffer       the pattern list as string, e.g. ";*;-cache"
- */
-void ReDirOptions::optimizePathPattern(ReByteBuffer& buffer){
-       ReStringList list;
-       ReStringList rootList;
-       list.split(buffer.str() + 1, buffer.str()[0]);
-       buffer.replaceAll(OS_SEPARATOR, 1, "/", 1);
-       ReByteBuffer item;
-       for (int ix = 0; ix < (int) list.count(); ix++){
-               item.set(list.strOf(ix), -1);
-               if (item.endsWith("/*"))
-                       item.setLength(item.length() - 2);
-               if (item.endsWith("/"))
-                       item.setLength(item.length() - 1);
-               bool notAnchored = item.startsWith("*/") || item.startsWith("-*/");
-               item.replaceAll("/", 1, OS_SEPARATOR, 1);
-               list.replace(ix, item.str());
-               if (notAnchored){
-                       item.remove(item.str()[0] == '-' ? 1 : 0, 2);
-                       rootList.append(item.str(), 0);
-               }
-       }
-       if (rootList.count() > 0){
-               list.append(rootList);
-       }
-       item.set(buffer.str(), 1);
-       buffer.set(item);
-       list.join(item.str(), buffer, true);
-}
-/**
- * Sets the standard filter options given by the program arguments.
- *
- * @param filter    OUT: the filter to set
- */
-void ReDirOptions::setFilterFromProgramArgs(ReDirEntryFilter_t& filter){
-    ReByteBuffer buffer;
-    if (m_programArgs.getString("younger", buffer)[0] != '\0')
-        filter.m_maxAge = checkDate(buffer.str());
-    if (m_programArgs.getString("older", buffer)[0] != '\0')
-        filter.m_minAge = checkDate(buffer.str());
-    if (m_programArgs.getString("maxsize", buffer)[0] != '\0')
-        filter.m_maxSize = checkSize(buffer.str());
-    if (m_programArgs.getString("minsize", buffer)[0] != '\0')
-        filter.m_minSize = checkSize(buffer.str());
-    if (m_programArgs.getString("type", buffer)[0] != '\0')
-        filter.m_types = checkType(buffer.str());
-    filter.m_minDepth = m_programArgs.getInt("mindepth");
-    filter.m_maxDepth = m_programArgs.getInt("maxdepth");
-    if (m_programArgs.getString("nodepattern", buffer)[0] != '\0'){
-        checkPatternList(buffer.str());
-        m_nodePatterns.set(buffer.str());
-        filter.m_nodePatterns = &m_nodePatterns;
-    }
-    if (m_programArgs.getString("pathpattern", buffer)[0] != '\0'){
-        checkPatternList(buffer.str());
-               optimizePathPattern(buffer);
-        m_pathPatterns.set(buffer.str());
-        filter.m_pathPatterns = &m_pathPatterns;
-    }
-    if ( (m_interval = m_programArgs.getInt("trace")) != 0)
-               m_triggerCount = 10;
-       if (m_programArgs.getString("output", buffer)[0] != '\0'){
-               if ( (m_output = fopen(buffer.str(), "w")) == NULL){
-                       help("cannot open output file", buffer.str());
-                       m_output = stdout;
-               }
-       }
-}
-
-/**
- * Constructor.
- *
- * @param usage     a string vector with a message how to use the command
- * @param example   a string vector with some examples how to use the command
- */
-ReTool::ReTool(const char* usage[], const char* example[]) :
-       ReDirOptions(usage, example),
-       ReDirStatisticData(),
-       m_traverser(NULL)
-{
-}
-
-/**
- * Destructor.
- */
-ReTool::~ReTool(){
-}
-
+/*\r
+ * ReDirTools.cpp\r
+ *\r
+ * License: Public domain\r
+ * Do what you want.\r
+ * No warranties and disclaimer of any damages.\r
+ * The latest sources: https://github.com/republib\r
+ */\r
+\r
+#include "base/rebase.hpp"\r
+#include "os/reos.hpp"\r
+\r
+enum LOCATION_DIRTOOL {\r
+       LC_COPY_FILE_1 = LC_DIRTOOLS + 1, // 50101\r
+       LC_COPY_FILE_2, // 50102\r
+       LC_COPY_FILE_3, // 50103\r
+       LC_COPY_FILE_4, // 50104\r
+       LC_COPY_FILE_5, // 50105\r
+       LC_COPY_FILE_6, // 50106\r
+       LC_MAKE_DIR_1,  // 50107\r
+       LC_MAKE_DIR_2,  // 50108\r
+       LC_SET_PROPERTIES_1,    // 50109\r
+       LC_SET_PROPERTIES_2,    // 50110\r
+       LC_SET_PROPERTIES_3,    // 50111\r
+};\r
+const char* ReDirTools::m_version = "2015.02.04";\r
+\r
+static const char* s_helpSummary[] = {\r
+    "dirtool or dt <command> <opts>",\r
+    "   Useful commands around directory trees.",\r
+    "   Type 'dirtool help <command>' for more help.",\r
+    "<command>:",\r
+    "batch         produce output to handle the found files with a script",\r
+    "help          shows info about the arguments/options",\r
+    "list          shows the meta data of the selected files",\r
+    "statistic     shows statistics about a direcctory tree",\r
+    "synchronize   copies only modified or new files from",\r
+    "              from a source directory tre to a target",\r
+    NULL\r
+};\r
+\r
+const char* s_batchUsage[] = {\r
+    "<command>: batch",\r
+    "   produces output usable for a batch file (script)",\r
+    "   all found files can be processed with a given script",\r
+    "   each line starts (usually) with a script name (see -c)",\r
+    "   then it follows the full filename of the found file",\r
+    "   use --arguments or --script to configure the output line",\r
+    NULL\r
+};\r
+const char* s_batchExamples[] = {\r
+    "dirtool batch -cbackup.bat --basename-pattern=;*.txt;*.doc e:\\data",\r
+    "dirtool batch --type=r '--arguments=backup.sh !basename! !path!' usr etc",\r
+    NULL\r
+};\r
+\r
+const char* s_listUsage[] = {\r
+    "<command>: list",\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
+    NULL\r
+};\r
+\r
+static const char* s_statisticUsage[] = {\r
+    "<command>:"\r
+    "st(atistic)  [<opts_stat>] <path> [<depth>]",\r
+    "          shows a statistic about a directory tree",\r
+    "<path>    a directory path: relative or absolute",\r
+    "<depth>   0: only the summary of <path> will be shown",\r
+    "          1: shows the summery of each subdir of <path> and the total",\r
+    "          n: shows the summery of each subdir until level <n> and the total",\r
+    "          default: 1",\r
+    "<opts_stat>:",\r
+    NULL\r
+};\r
+const char* s_statisticExamples[] = {\r
+    "dirtool st -q -t0 e:\\windows",  \r
+    "dirtool statistic --quiet --kbyte --trace-interval=60 ../mail 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
+    "   Synchronizes the content of a directory tree with another.",\r
+    "   Newer or missing files will be copied",\r
+    "   If a source name ends with the separator (/ in linux, \\ in win) the target",\r
+    "   will be completed with the basename of the source.",\r
+    "   Example:",\r
+    "   'sync home backup' copies the file home/x.txt to backup/home/x.txt",\r
+    "   'sync data/ backup/data2' copies the file home/x.txt to backup/data2/x.txt",\r
+    NULL\r
+};\r
+const char* s_syncExamples[] = {\r
+    "dirtool sync --basename-pattern=;*.txt;*.doc e:\\data\\ d:\\backup\\data2",\r
+    "dirtool sync --type=r --max-size=1G usr etc /media/backup",\r
+    NULL\r
+};\r
+\r
+/**\r
+ * Constructor.\r
+ *\r
+ * @param usage     a string vector with a message how to use the command\r
+ * @param example   a string vector with some examples how to use the command\r
+ */\r
+ReDirOptions::ReDirOptions(const char* usage[], const char* examples[]) :\r
+       ReTraceUnit(INT_MAX, INT_MAX),\r
+    m_programArgs(usage, examples),\r
+    m_nodePatterns(),\r
+    m_pathPatterns(),\r
+    m_compoundUsage(NULL),\r
+    m_countCompoundUsage(0),\r
+    m_output(stdout),\r
+       m_verboseLevel(V_NORMAL)\r
+{\r
+       m_nodePatterns.setIgnoreCase(true);\r
+       m_pathPatterns.setIgnoreCase(true);\r
+}\r
+/**\r
+ * Destructor.\r
+ */\r
+ReDirOptions::~ReDirOptions(){\r
+       close();\r
+       delete[] m_compoundUsage;\r
+}\r
+/**\r
+ * Adds a usage component to the compound usage message list.\r
+ * @param usage     a string vector containing a part of the usage message\r
+ */\r
+void ReDirOptions::addCompoundUsage(const char** usage){\r
+    int start = 0;\r
+    while(m_compoundUsage[start] != NULL){\r
+        if (++start >= m_countCompoundUsage)\r
+            assert(false);\r
+    }\r
+    for (int ix = 0; usage[ix] != NULL; ix++){\r
+        if (start + ix > m_countCompoundUsage){\r
+            assert(false);\r
+            break;\r
+        }\r
+        m_compoundUsage[start + ix] = usage[ix];\r
+    }\r
+}\r
+\r
+/**\r
+ * Adds the standard filter options.\r
+ */\r
+void ReDirOptions::addStandardFilterOptions(){\r
+       // standard short options: D d O o P p T t v y Z z\r
+    m_programArgs.addInt("maxdepth",\r
+        i18n("the depth of the subdirectory is lower or equal <number>\n"\r
+        "0: search is done only in the base directory"),\r
+        'D', "max-depth", 512);\r
+    m_programArgs.addInt("mindepth", \r
+        i18n("the depth of the subdirectory is greater or equal <number>\n"\r
+            "0: search is done in all subdirectories"),\r
+        'd', "min-depth", 0);\r
+    m_programArgs.addString("output",\r
+        i18n("the name of the output file.\n"\r
+            "The output will written to this file instead of stdout"),\r
+        'O', "output-file", false, NULL);\r
+    m_programArgs.addString("older",\r
+        i18n("the modification date is older than <string>\n"\r
+            "<string> is a date (e.g. 2015.02.17) or number followed by an unit\n"\r
+            "units: m(inutes) h(hours), d(days). Default: m(inutes)\n"\r
+            "examples: -o25 --older-than=30d -o24h -o2009.3.2/12:00 -o1999.01.01"),\r
+        'o', "older-than", false, NULL);\r
+    m_programArgs.addString("pathpattern", \r
+        i18n("a list of patterns for the path (without basename)\n"\r
+            "the separator is the first character of the list\n"\r
+            "Each pattern can contain '*' as wildcard\n"\r
+            "If the first character is '-' the pattern is a 'not pattern':\n"\r
+            "A directory will be entered if at least one of the positive patterns\n"\r
+            "and none of the 'not patterns' matches\n"\r
+            "examples:\n"\r
+            "';music;pic*' enters music and xy/Music and PIC and pictures but not xy/pic and img\n"\r
+                       "';*;-.git;.hg' ignores .git and xy/z/.git and .ht"),\r
+               'P', "path-pattern", false, NULL);\r
+    m_programArgs.addString("nodepattern",\r
+        i18n("a list of patterns for the basename (name without path) separated by ';'\n"\r
+            "Each pattern can contain '*' as wildcard\n"\r
+            "If the first character is '-' the pattern is a 'not pattern':\n"\r
+            "A file will be found if at least one of the positive patterns and none\n"\r
+            "of the 'not patterns' matches\n"\r
+            "examples: '*.cpp;*.hpp;Make*' '*;-*.bak;-*~"),\r
+        'p', "basename-pattern", false, NULL);\r
+    m_programArgs.addString("verbose",\r
+        i18n("verbose level: 0: no info, 1: summary only, 2: normal, 3: chatter mode, 4: debug"),\r
+        'v', "verbose", false, "1");\r
+    m_programArgs.addInt("trace",\r
+        i18n("all <number> seconds the current path will be traced\n"\r
+            "0: no trace"),\r
+        'T', "trace-interval", 0);\r
+    m_programArgs.addString("type",\r
+        i18n("the file type\n"\r
+            "<string> is a list of <v> values:\n"\r
+            "<v>: b(lock) c(har) d(irectory) (l)i(nkdir) l(ink) o(ther)\n"\r
+            "     p(ipe) s(ocket) r(egular)\n"\r
+            "<v>-sets: S(pecial)=bcspo N(ondir)=Slr\n"\r
+            "examples: -td --type=dr -tNi"), \r
+        't', "type", false, NULL);\r
+    m_programArgs.addString("younger",\r
+        i18n("the modification date is younger than <string>\n"\r
+            "<string> is a date (e.g. 2015.02.17) or number followed by an unit\n"\r
+            "units: m(inutes) h(hours), d(days). Default: m(inutes)"),\r
+        'y', "younger-than", false, NULL);\r
+    m_programArgs.addString("maxsize",\r
+        i18n("the filesize is greater or equal <string>\n"\r
+            "<string> is a number followed by an unit\n"\r
+            "units: b(yte) k(Byte) K(iByte) m(Byte), M(iByte), g(Byte) G(iByte)\n"\r
+            "examples: -Z50m --max-size=1G"),\r
+        'Z', "max-size", false, NULL);\r
+    m_programArgs.addString("minsize",\r
+        i18n("the filesize is greater or equal <string>\n"\r
+            "<string> is a number followed by an unit\n"\r
+            "units: b(yte) k(Byte) K(iByte) m(Byte), M(iByte), g(Byte) G(iByte)\n"\r
+            "examples: -z50m --min-size=1G"),\r
+        'z', "min-size", false, NULL);\r
+}\r
+\r
+/**\r
+ * Checks whether the given value is a time expression.\r
+ *\r
+ * Possible: <date> <date_time> <n><unit>\r
+ * Units: m(inutes) h(our) d(ays)\r
+ *\r
+ * @param value     value to check\r
+ * @return          the value converted into the absolute time\r
+ * @throws          ReOptionExecption\r
+ */\r
+time_t ReDirOptions::checkDate(const char* value){\r
+    ReByteBuffer theValue(value, -1);\r
+    time_t rc = 0;\r
+    if (theValue.count(".") == 2){\r
+        // a date:\r
+        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
+        case 3:\r
+        case 4:\r
+        case 5:\r
+        {\r
+            if (year < 1970)\r
+                throw ReOptionException(&m_programArgs, \r
+                    i18n("date < 1970.01.01: "), value);\r
+            struct tm time;\r
+            memset(&time, 0, sizeof time);\r
+            time.tm_year = year - 1900;\r
+            time.tm_mon = month - 1;\r
+            time.tm_mday = day;\r
+            time.tm_hour = hour;\r
+            time.tm_min = minute;\r
+            rc = mktime(&time);\r
+            break;\r
+        }\r
+        default:\r
+            throw ReOptionException(&m_programArgs, \r
+                i18n("invalid date/date-time value. yyyy.mm.dd/hh:MM 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
+        case 1:\r
+        case 2:\r
+            switch(unit){\r
+            case 'm':\r
+                count *= 60;\r
+                break;\r
+            case 'h':\r
+                count *= 60*60;\r
+                break;\r
+            case 'd':\r
+                count *= 24*60*60;\r
+                break;\r
+            default:\r
+                throw ReOptionException(&m_programArgs, \r
+                    i18n("invalid unit. expected: 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
+                value);\r
+        }\r
+    }\r
+    return rc;\r
+}\r
+/**\r
+ * Checks whether the given value is a time expression.\r
+ *\r
+ * Possible: <date> <date_time> <n><unit>\r
+ * Units: m(inutes) h(our) d(ays)\r
+ *\r
+ * @param value     value to check\r
+ * @return          the value (multiplied with the unit factor)\r
+ * @throws          ReOptionExecption\r
+ */\r
+time_t ReDirOptions::checkSize(const char* value){\r
+    int64_t rc = 0;\r
+    char unit = 'b';\r
+    switch (sscanf(value, "%lld%c", (long long int*) &rc, &unit)){\r
+    case 1:\r
+    case 2:\r
+        switch(unit){\r
+        case 'b':\r
+            break;\r
+        case 'k':\r
+            rc *= 1000;\r
+            break;\r
+        case 'K':\r
+            rc *= 1024;\r
+            break;\r
+        case 'm':\r
+            rc *= 1000*1000;\r
+            break;\r
+        case 'M':\r
+            rc *= 1024*1024;\r
+            break;\r
+        case 'g':\r
+            rc *= 1000LL*1000*1000;\r
+            break;\r
+        case 'G':\r
+            rc *= 1024LL*1024*1024;\r
+            break;\r
+        default:\r
+             throw ReOptionException(&m_programArgs, \r
+                i18n("invalid <unit>. Expected: b k K m M g G"), \r
+                value);\r
+        }\r
+        break;\r
+    default:\r
+        throw ReOptionException(&m_programArgs, \r
+            i18n("invalid size value: <number><unit> <unit>: b k K m M g G"), \r
+            value);\r
+    }\r
+    return rc;\r
+}\r
+/**\r
+ * Checks whether the given value is a valid pattern list.\r
+ *\r
+ * The first character must be a separator (not '*', '.'  and not a letter)\r
+ *\r
+ * @param value     value to check\r
+ * @return          the <cpde>value</code> (for chaining)\r
+ * @throws          ReOptionExecption\r
+ */\r
+const char* ReDirOptions::checkPatternList(const char* value){\r
+    if (isalnum(*value) || *value == '_' || *value == '*'\r
+                        || *value == '.' || *value == '-')\r
+         throw ReOptionException(&m_programArgs, \r
+            i18n("invalid separator (first character): $1 use ';' instead"), \r
+            value);\r
+       if (strchr(value, OS_SEPARATOR_CHAR) != NULL)\r
+               throw ReOptionException(&m_programArgs, \r
+            i18n("slash not allowed in pattern list: $2"), value);\r
+    return value;\r
+}\r
+/**\r
+ * Checks whether the given value is a valid filetype list.\r
+ *\r
+ * @param value     value to check\r
+ * @return          the bitmask\r
+ * @throws          ReOptionExecption\r
+ */\r
+ReDirStatus_t::Type_t ReDirOptions::checkType(const char* value){\r
+    int rc = ReDirStatus_t::TF_UNDEF;\r
+    while (*value != '\0'){\r
+        switch(*value){\r
+        case 'b':\r
+            rc |= ReDirStatus_t::TF_BLOCK;\r
+            break;\r
+        case 'c':\r
+            rc |= ReDirStatus_t::TF_CHAR;\r
+            break;\r
+        case 'd':\r
+            rc |= ReDirStatus_t::TF_SUBDIR;\r
+            break;\r
+        case 'i':\r
+            rc |= ReDirStatus_t::TF_LINK_DIR;\r
+            break;\r
+        case 'l':\r
+            rc |= ReDirStatus_t::TF_LINK;\r
+            break;\r
+        case 'o':\r
+            rc |= ReDirStatus_t::TF_OTHER;\r
+            break;\r
+        case 'p':\r
+            rc |= ReDirStatus_t::TF_PIPE;\r
+            break;\r
+        case 's':\r
+            rc |= ReDirStatus_t::TF_SOCKET;\r
+            break;\r
+        case 'r':\r
+            rc |= ReDirStatus_t::TF_REGULAR;\r
+            break;\r
+        case 'S':\r
+            rc |= ReDirStatus_t::TC_SPECIAL;\r
+            break;\r
+        case 'N':\r
+            rc |= ReDirStatus_t::TC_NON_DIR;\r
+            break;\r
+        case ' ':\r
+        case ',':\r
+            break;\r
+       default:\r
+           throw ReOptionException(&m_programArgs, \r
+                i18n("invalid type: $1 Expected: b(lock) c(har) d(irectory)"\r
+                " (l)i(nkdir) l(ink) o(ther) p(ipe) s(ocket) r(egular)"\r
+                " S(pecial=bcspo) N(ondir=Slr)"), \r
+                value);\r
+        }\r
+        value++;\r
+    }\r
+    return (ReDirStatus_t::Type_t) rc;\r
+}\r
+\r
+/**\r
+ * Prints a help message, the error message and exits.\r
+ *\r
+ * @param errorMessage  the error message.\r
+ * @param message2      an additional message\r
+ */\r
+\r
+void ReDirOptions::help(const char* errorMessage, const char* message2) const{\r
+    ReByteBuffer msg;\r
+    if (errorMessage != 0)\r
+        msg.append(errorMessage, -1);\r
+    if (message2 != NULL)\r
+        msg.append(message2, -1);\r
+    m_programArgs.help(msg.str(), false, stdout);\r
+    exit(1);\r
+}\r
+\r
+/**\r
+ * Checks the correctness of the standard filter options.\r
+ * \r
+ * @throws \r
+ */\r
+void ReDirOptions::checkStandardFilterOptions(){\r
+    ReByteBuffer buffer;\r
+    checkDate(m_programArgs.getString("older", buffer));\r
+    checkDate(m_programArgs.getString("younger", buffer));\r
+    checkType(m_programArgs.getString("types", buffer));\r
+    checkSize(m_programArgs.getString("maxsize", buffer));\r
+    checkSize(m_programArgs.getString("minsize", buffer));\r
+    checkPatternList(m_programArgs.getString("nodepattern", buffer));    \r
+    checkPatternList(m_programArgs.getString("pathpattern", buffer));\r
+       if (m_programArgs.getString("verbose", buffer)[0] != '\0'){\r
+               unsigned level = V_NORMAL;\r
+               if (ReStringUtils::lengthOfUnsigned(buffer.str(), -1, &level)\r
+                               != buffer.length())\r
+                       help(i18n("verbose level is not a number (or '')"), buffer.str());\r
+               else\r
+                       m_verboseLevel = VerboseLevel(level);\r
+       }\r
+}\r
+\r
+/**\r
+ * Frees the resources.\r
+ */\r
+void ReDirOptions::close(){\r
+       if (m_output != stdout){\r
+               fclose(m_output);\r
+               m_output = stdout;\r
+       }\r
+}\r
+\r
+/**\r
+ * Initializes the compound usage message array.\r
+ *\r
+ * @param size      the size of the array: size = (field1 + field2 + ...) * sizeof(const char*)\r
+ */\r
+void ReDirOptions::initCompoundUsage(size_t size){\r
+    delete[] m_compoundUsage;\r
+    int count = size / sizeof m_compoundUsage[0];\r
+    m_compoundUsage = new const char*[count];\r
+    memset(m_compoundUsage, 0, size);\r
+    m_countCompoundUsage = count;\r
+}\r
+\r
+/**\r
+ * Optimizes the path patterns.\r
+ *\r
+ * For all patterns of the list:\r
+ * <ul><li>remove a trailing "\" and "\*"</li>\r
+ * <li>change "*\xy" to "*\xy" and "xy" (finds xy in the root directory)</li>\r
+ * <li>replaces "/" with the os specific path separator</li>\r
+ * </ul>\r
+ *\r
+ * @param buffer       the pattern list as string, e.g. ";*;-cache"\r
+ */\r
+void ReDirOptions::optimizePathPattern(ReByteBuffer& buffer){\r
+       ReStringList list;\r
+       ReStringList rootList;\r
+       list.split(buffer.str() + 1, buffer.str()[0]);\r
+       buffer.replaceAll(OS_SEPARATOR, 1, "/", 1);\r
+       ReByteBuffer item;\r
+       for (int ix = 0; ix < (int) list.count(); ix++){\r
+               item.set(list.strOf(ix), -1);\r
+               if (item.endsWith("/*"))\r
+                       item.setLength(item.length() - 2);\r
+               if (item.endsWith("/"))\r
+                       item.setLength(item.length() - 1);\r
+               bool notAnchored = item.startsWith("*/") || item.startsWith("-*/");\r
+               item.replaceAll("/", 1, OS_SEPARATOR, 1);\r
+               list.replace(ix, item.str());\r
+               if (notAnchored){\r
+                       item.remove(item.str()[0] == '-' ? 1 : 0, 2);\r
+                       rootList.append(item.str(), 0);\r
+               }\r
+       }\r
+       if (rootList.count() > 0){\r
+               list.append(rootList);\r
+       }\r
+       item.set(buffer.str(), 1);\r
+       buffer.set(item);\r
+       list.join(item.str(), buffer, true);\r
+}\r
+/**\r
+ * Sets the standard filter options given by the program arguments.\r
+ *\r
+ * @param filter    OUT: the filter to set\r
+ */\r
+void ReDirOptions::setFilterFromProgramArgs(ReDirEntryFilter_t& filter){\r
+    ReByteBuffer buffer;\r
+    if (m_programArgs.getString("younger", buffer)[0] != '\0')\r
+        filter.m_maxAge = checkDate(buffer.str());\r
+    if (m_programArgs.getString("older", buffer)[0] != '\0')\r
+        filter.m_minAge = checkDate(buffer.str());\r
+    if (m_programArgs.getString("maxsize", buffer)[0] != '\0')\r
+        filter.m_maxSize = checkSize(buffer.str());\r
+    if (m_programArgs.getString("minsize", buffer)[0] != '\0')\r
+        filter.m_minSize = checkSize(buffer.str());\r
+    if (m_programArgs.getString("type", buffer)[0] != '\0')\r
+        filter.m_types = checkType(buffer.str());\r
+    filter.m_minDepth = m_programArgs.getInt("mindepth");\r
+    filter.m_maxDepth = m_programArgs.getInt("maxdepth");\r
+    if (m_programArgs.getString("nodepattern", buffer)[0] != '\0'){\r
+        checkPatternList(buffer.str());\r
+        m_nodePatterns.set(buffer.str());\r
+        filter.m_nodePatterns = &m_nodePatterns;\r
+    }\r
+    if (m_programArgs.getString("pathpattern", buffer)[0] != '\0'){\r
+        checkPatternList(buffer.str());\r
+               optimizePathPattern(buffer);\r
+        m_pathPatterns.set(buffer.str());\r
+        filter.m_pathPatterns = &m_pathPatterns;\r
+    }\r
+    if ( (m_interval = m_programArgs.getInt("trace")) != 0)\r
+               m_triggerCount = 10;\r
+       if (m_programArgs.getString("output", buffer)[0] != '\0'){\r
+               if ( (m_output = fopen(buffer.str(), "w")) == NULL){\r
+                       help("cannot open output file", buffer.str());\r
+                       m_output = stdout;\r
+               }\r
+       }\r
+}\r
+\r
+/**\r
+ * Constructor.\r
+ *\r
+ * @param usage     a string vector with a message how to use the command\r
+ * @param example   a string vector with some examples how to use the command\r
+ */\r
+ReTool::ReTool(const char* usage[], const char* example[]) :\r
+       ReDirOptions(usage, example),\r
+       ReDirStatisticData(),\r
+       m_traverser(NULL)\r
+{\r
+}\r
+\r
+/**\r
+ * Destructor.\r
+ */\r
+ReTool::~ReTool(){\r
+}\r
+\r
 /**\r
  * Prints a message.\r
  *\r
@@ -592,1007 +592,1009 @@ ReTool::~ReTool(){
  * @param currentFile  message for the trace\r
  * @return                             <code>true</code> (for chaining)\r
  */\r
-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.append(currentFile);
-       fputs(buffer.str(), stdout);
-       return true;
-}
-/**
- * Constructor.
- */
-ReDirStatisticData::ReDirStatisticData() :
-       m_sizes(0),
-       m_files(0),
-       m_dirs(0),
-       m_path()
-{
-}
-/**
- * Copy constructor.
- *
- * @param source       the source to copy
- */
-ReDirStatisticData::ReDirStatisticData(const ReDirStatisticData& source) :
-       m_sizes(source.m_sizes),
-       m_files(source.m_files),
-       m_dirs(source.m_dirs),
-       m_path(source.m_path)
-{
-}
-/**
- * Assignment operator.
- *
- * @param source       the source to copy
- * @return                     the instance itself
- */
-ReDirStatisticData& ReDirStatisticData::operator =(const ReDirStatisticData& source){
-       m_sizes = source.m_sizes;
-       m_files = source.m_files;
-       m_dirs = source.m_dirs;
-       m_path = source.m_path;
-       return *this;
-}
-
-/**
- * Constructor.
- */
-ReDirStatistic::ReDirStatistic(int deltaList, int deltaBuffer) :
-    ReDirOptions(s_statisticUsage, s_statisticExamples),
-       m_list(deltaList, deltaBuffer),
-    m_traceInterval(0),
-    m_lastTrace(0)
-{
-       // standard short options: D d O o P p T t v y Z z
-    m_programArgs.addBool("kbyte", 
-        i18n("output format is '<kbyte> <path>' (like unix 'du' command)"),
-        'k', "kbyte", false);
-    addStandardFilterOptions();
-}
-/**
- * Destructor.
- */
-ReDirStatistic::~ReDirStatistic(){
-}
-
-/**
- * Adds the data from another instance.
- *
- * @param source               the other instance
- * @return                     the instance itself
- */
-ReDirStatisticData& ReDirStatisticData::add(const ReDirStatisticData& source){
-       m_sizes += source.m_sizes;
-       m_files += source.m_files;
-       m_dirs += source.m_dirs;
-       return *this;
-}
-/**
- * Initializes the data of the instance.
- */
-void ReDirStatisticData::clear(){
-       m_sizes = 0;
-       m_files = 0;
-       m_dirs = 0;
-       m_path.setLength(0);
-}
-
-/**
- * Calculates the statistic of a directory tree.
- *
- *
- */
-const ReStringList& ReDirStatistic::calculate(const char* base, int level,
-                       void (*formatter)(const ReDirStatisticData& data, ReDirStatistic& parent,
-                               ReByteBuffer& line)){
-       ReDirEntryFilter_t filter;
-       ReTraverser traverser(base);
-       setFilterFromProgramArgs(filter);
-       traverser.setPropertiesFromFilter(&filter);
-       if (level > 1024)
-               level = 1024;
-       else if (level < 0)
-               level = 0;
-    m_list.clear();
-       ReDirStatisticData** dataStack = new ReDirStatisticData*[level + 1];
-       memset(dataStack, 0, sizeof dataStack[0] * (level + 1));
-       dataStack[0] = new ReDirStatisticData();
-       int topOfStack = 0;
-       ReDirStatus_t* entry;
-       int currentDepth = -1;
-       ReDirStatisticData* current = dataStack[0];
-       current->m_path.set(base, -1);
-       ReByteBuffer line;
-       bool useFilter = filter.m_minSize > 0 || filter.m_maxSize != -1
-                               || filter.m_minAge != 0 || filter.m_maxAge != 0
-                               || m_nodePatterns.count() > 0;
-       while( (entry = traverser.rawNextFile(currentDepth))){
-               if (currentDepth <= level && ! entry->m_path.equals(current->m_path)){
-                       if (currentDepth <= topOfStack){
-                               // close the entries of the stack above the new top level:
-                               while(topOfStack >= currentDepth){
-                                       // Add the data to the parent:
-                    if (topOfStack > 0)
-                        dataStack[topOfStack - 1]->add(*dataStack[topOfStack]);
-                                       // Append it to the result:
-                                       (*formatter)(*dataStack[topOfStack], *this, line);
-                                       m_list.append(line);
-                    topOfStack--;
-                               }
-                // We reuse the top of stack:
-                topOfStack++;
-                current = dataStack[topOfStack];
-                // exchange the top of stack with the new found directory:
-                current->clear();
-                               current->m_path.set(entry->m_path.str(), entry->m_path.length());;
-                       } else {
-                               // set up a new stack entry:
-                               if (currentDepth != topOfStack + 1)
-                                       assert(currentDepth == topOfStack + 1);
-
-                               topOfStack++;
-                               if (dataStack[topOfStack] == NULL)
-                                       dataStack[topOfStack] = new ReDirStatisticData();
-                               else
-                                       dataStack[topOfStack]->clear();
-                               current = dataStack[topOfStack];
-                               current->m_path.set(entry->m_path.str(), entry->m_path.length());
-                       }
-               }
-               if (entry->isDirectory()){
-                       current->m_dirs++;
-               } else if (! useFilter || filter.match(*entry)){
-                       current->m_sizes += entry->fileSize();
-                       current->m_files++;
-               }
-       }
-    // close all dirs with parents:
-       while(topOfStack > 0){
-               // Add the data to the parent:
-               dataStack[topOfStack - 1]->add(*dataStack[topOfStack]);
-               // Append it to the result:
-               (*formatter)(*dataStack[topOfStack], *this, line);
-               m_list.append(line);
-        topOfStack--;
-       }
-    // ... and the overall summery:
-    (*formatter)(*dataStack[0], *this, line);
-       m_list.append(line);
-       // free the resources:
-       for (int ix = 0; ix <= level; ix++)
-               delete dataStack[ix];
-       delete[] dataStack;
-       return m_list;
-}
-
-/**
- * Build the statistic and print the results.
- *
- * @param arc   count of arguments in <code>argv</code>
- * @param argv  the program arguments.
- */
-void ReDirStatistic::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)
-            depth = atol(m_programArgs.getArg(1));
-        ReDirStatistic statistic;
-        statistic.setTraceInterval(m_programArgs.getInt("trace"));
-        void (*proc) (const ReDirStatisticData& data,
-                           ReDirStatistic& parent, ReByteBuffer& line) = &formatWithSizeFilesAndDirs;
-        if (m_programArgs.getBool("kbyte"))
-            proc = &formatLikeDu;
-        const ReStringList& list = statistic.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);
-    }
-}
-/**
- * Formats a line like the du (disk usage) command.
- *
- * This is a possible parameter of <code>ReDirStatistic::calculate()</code>.
- *
- * @param data         statistic data, including path name
- * @param parent       the caller (<code>ReDirStatistic</code>). This allows to deliver
- *                                     a context to this formatting routine (through derivation of
- *                                     <code>ReDirStatistic</code>)
- * @param line         OUT: the formatted line, the conclusion of the statistic data
- */
-void formatLikeDu(const ReDirStatisticData& data,
-                       ReDirStatistic& parent, ReByteBuffer& line){
-       line.setLength(0);
-    // Round up to the next KiByte:
-       line.appendInt(int((data.m_sizes + 1023) / 1024)).append("\t").append(data.m_path);
-}
-
-/**
- * Formats a line in a standard way: MBytes, file count and directory count.
- *
- * This is a possible parameter of <code>ReDirStatistic::calculate()</code>.
- *
- * @param data         statistic data, including path name
- * @param parent       the caller (<code>ReDirStatistic</code>). This allows to deliver
- *                                     a context to this formatting routine (through derivation of
- *                                     <code>ReDirStatistic</code>)
- * @param line         OUT: the formatted line, the conclusion of the statistic data
- */
-void formatWithSizeFilesAndDirs(const ReDirStatisticData& data,
-                       ReDirStatistic& parent, ReByteBuffer& line){
-       line.setLength(0);
-    // Round up to the next KiByte:
-    char buffer[256];
-    _snprintf(buffer, sizeof buffer, "%14.6f MB %7d %7d\t",
-        data.m_sizes / 1E6, data.m_files, data.m_dirs);
-       line.append(buffer, -1).append(data.m_path);
-}
-/**
- * Prints a vector of lines.
- * 
- * @param lines     a vector of lines without newline ('\n')
- */
-static void printField(const char** lines){
-    for (int ix = 0; lines[ix] != NULL; ix++){
-        printf("%s\n", lines[ix]);
-    }
-}
-
-/**
- * Prints an message how to use the statistic module and exits.
- */
-void ReDirTools::statisticUsage(){
-    ReDirStatistic statistic;
-    statistic.programArgs().help(NULL, false, stdout);
-}
-
-/**
- * Prints an message how to use the program and exits.
- *
- * @param msg       an error message
- * @param msg2      an addition to the error message or NULL
- */
-void ReDirTools::usage(const char* msg, const char* msg2){
-    printf ("Version: %s\n", m_version);
-    printf ("usage: dirtool <command> <opt>\n");
-    statisticUsage();
-    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.
- *
- * @param path      the name of the subdir to create
- */
-void ReDirSync::makeDirWithParents(ReByteBuffer& path, int minWidth,
-                       ReTraverser& traverser){
-    struct stat info;
-       bool endsWithSlash = path.str()[path.length() - 1] == OS_SEPARATOR_CHAR;
-       if (endsWithSlash)
-               path.setLength(path.length() - 1);
-       if (stat(path.str(), &info) != 0){
-               ReFileProperties_t* props = NULL;
-#if defined __linux__
-               ReDirStatus_t* entry = traverser.topOfStack();
-               props = entry == NULL ? NULL : &entry->m_status;
-#endif
-               makeDirectory(path.str(), minWidth, props, ReLogger::globalLogger());
-       }
-       if (endsWithSlash)
-               path.append(OS_SEPARATOR, 1);
-}
-/**
- * Constructor.
- */
-ReDirSync::ReDirSync() :
-    ReTool(s_syncUsage, s_syncExamples),
-    m_buffer()
-{
-       // standard short options: D d O o P p T t v y Z z
-       m_buffer.ensureSize(4u*1024u*1024u);
-    m_programArgs.addBool("add",
-        i18n("copies only files which does not exist on the target"),
-        'a', "add", false);
-    m_programArgs.addBool("dry",
-        i18n("does nothing, but says what should be done"),
-        'Y', "dry", false);
-    m_programArgs.addInt("timediff",
-        i18n("filetime difference is considered to be equal\n"
-                       "if the difference is less than this value (in seconds)"),
-        'I', "time-delta", 2);
-    m_programArgs.addBool("ignoredate",
-                       i18n("the modification is recognized only by the different size (not time)"),
-        'i', "ignore-time", false);
-    m_programArgs.addBool("mustexist",
-                       i18n("files which don't exist on the target will not be copied"),
-        'm', "must-exist", false);
-    addStandardFilterOptions();
-}
-
-/**
- * Copies a file.
- *
- * @param entry                the source file info
- * @param target       the name of the target file
- */
-void ReDirSync::copyFile(ReDirStatus_t* entry, const char* target){
-       ReFileProperties_t* props;
-#ifdef __linux__
-       props = &entry->m_status;
-#else
-       ReFileProperties_t properties;
-       properties.m_modified = *entry->modified();
-       properties.m_accessed = *entry->accessed();
-       properties.m_size = entry->fileSize();
-       props = &properties;
-#endif
-       copyFile(entry->fullName(), props, target, m_buffer,
-               ReLogger::globalLogger());
-}
-
-/**
- * Copies a file.
- *
- * @param source               the source file name
- * @param properties   NULL or the properties of the source file
- * @param target               the name of the target file
- * @param buffer               OUT: the reading uses this buffer<br>
- *                                             Set the capacity to make it more efficient
- * @param logger               NULL or the logger for error messages
- * @return                             <code>true</code>success<br>
- *                                             <code>false</code>error occurred
- */
-bool ReDirSync::copyFile(const char* source, ReFileProperties_t* properties,
-               const char* target, ReByteBuffer& buffer, ReLogger* logger){
-       bool rc = false;
-#ifdef __linux__
-       struct stat info;
-       if (properties == NULL){
-               if (stat(source, &info) == 0)
-                       properties = &info;
-               else {
-                       if (logger != NULL)
-                               logger->sayF(LOG_ERROR | CAT_FILE, LC_COPY_FILE_1,
-                                       i18n("could not find: $ (errno: $2)")).arg(source)
-                                               .arg(errno).end();
-               }
-       }
-       FILE* fpSource = fopen(source, "rb");
-       if (fpSource == NULL){
-               if (logger != NULL)
-                       logger->sayF(LOG_ERROR | CAT_FILE, LC_COPY_FILE_2,
-                                       i18n("cannot open $1 (errno: $2)"))
-                               .arg(source).arg(errno).end();
-       } else {
-               FileSize_t size = properties == NULL ? 0x7fffffff : properties->st_size;
-               FILE* fpTarget = fopen(target, "w");
-               if (fpTarget == NULL){
-                       if (logger != NULL)
-                               logger->sayF(LOG_ERROR | CAT_FILE, LC_COPY_FILE_3,
-                                               i18n("cannot open $1 (errno: $2)"))
-                                       .arg(target).arg(errno).end();
-               } else{
-                       while(size > 0){
-                               size_t blockSize = buffer.capacity();
-                               if ((int) blockSize > size)
-                                       blockSize = size;
-                               if (fread(buffer.buffer(), blockSize, 1, fpSource) != 1){
-                                       if (logger != NULL)
-                                               logger->sayF(LOG_ERROR | CAT_FILE, LC_COPY_FILE_5,
-                                                               i18n("cannot read $1 (errno: $2)"))
-                                                       .arg(source).arg(errno).end();
-                                       break;
-                               }
-                               size_t written;
-                               if ((written = fwrite(buffer.buffer(), 1, blockSize,
-                                               fpTarget)) != blockSize){
-                                       if (logger != NULL)
-                                               logger->sayF(LOG_ERROR | CAT_FILE, LC_COPY_FILE_6,
-                                                               i18n("cannot write $1 [$2] (errno: $3)"))
-                                                       .arg(target).arg(written).arg(errno).end();
-                                       break;
-                               }
-                               size -= blockSize;
-                       }
-                       rc = size == 0ll;
-                       fclose(fpTarget);
-                       if (properties != NULL)
-                               setProperties(target, properties, logger);
-               }
-               fclose(fpSource);
-       }
-#elif defined __WIN32__
-    BOOL cancel = false;
-    rc = CopyFileExA(source, target, NULL, NULL, &cancel, COPY_FILE_NO_BUFFERING) != 0;
-    int errNo = 0;
-    if (! rc)
-        errNo = GetLastError();
-#endif
-       return rc;
-}
-/**
- * Sets the file properties.
- *
- * @param fullName             the name of the file
- * @param properties   the properties like times and rights
- * @param logger               NULL or the logger for error messages
- * @return                             <code>true</code>: success<br>
- *                                             <code>false</code>: error occurred
- */
-bool ReDirSync::setProperties(const char* fullName,
-               ReFileProperties_t* properties, ReLogger* logger){
-       bool rc = true;
-#if defined __linux__
-       struct utimbuf times;
-       times.actime = properties->st_atime;
-       times.modtime = properties->st_mtime;
-       if (utime(fullName, &times) != 0){
-               if (logger != NULL)
-                       logger->sayF(LOG_ERROR | CAT_FILE, LC_SET_PROPERTIES_1,
-                               i18n("cannot change file times: $1 (errno: $2)"))
-                               .arg(fullName).arg(errno).end();
-               rc = false;
-       }
-       int rights = properties->st_mode & (S_IRWXO | S_IRWXG | S_IRWXU);
-       if (chmod(fullName, rights) != 0) {
-               if (logger != NULL)
-                       logger->sayF(LOG_ERROR | CAT_FILE, LC_SET_PROPERTIES_2,
-                               i18n("cannot change file modes: $1 (errno: $2)"))
-                               .arg(fullName).arg(errno).end();
-               rc = false;
-       }
-       if (chown(fullName, properties->st_uid, properties->st_gid) != 0){
-               if (logger != NULL)
-                       logger->sayF(LOG_ERROR | CAT_FILE, LC_SET_PROPERTIES_3,
-                               i18n("cannot change file owner: $1 (errno: $2)"))
-                               .arg(fullName).arg(errno).end();
-               rc = false;
-       }
-#endif
-       return rc;
-}
-
-/**
- * Creates a directory and its parents (if neccessary).
- *
- * @param directory            the full name of the directory
- * @param properties   NULL or the properties of the new directory
- * @param logger               NULL or the logger for error messages
- * @return                             <code>true</code>success<br>
- *                                             <code>false</code>error occurred
- */
-bool ReDirSync::makeDirectory(const char* directory, int minLength,
-               ReFileProperties_t* properties, ReLogger* logger){
-       bool rc = true;
-       ReByteBuffer path(directory);
-       int start = 0;
-#if defined __WIN32__
-       start = path.indexOf(':');
-#endif
-       int ixSlash = start < 0 ? 0 : start;
-       struct stat info;
-       // for all parents and the full path itself:
-       while(ixSlash >= 0){
-               ixSlash = path.indexOf(OS_SEPARATOR_CHAR, ixSlash + 1);
-        if (ixSlash >= (int) path.length() - 1)
-            break;
-               // is the slash in front of the first node, e.g. 'e:\'?
-               if (ixSlash == start + 1)
-                       // not a real node: take the next node
-                       ixSlash = path.indexOf(OS_SEPARATOR_CHAR, ixSlash + 1);
-               if (ixSlash >= 0){
-                       // we handle only the next node:
-                       path.buffer()[ixSlash] = '\0';
-               }
-               // does the node exist?
-               if (lstat(path.str(), &info) != 0){
-                       // no, then we make it:
-                       if (_mkdir(path.str(), ALLPERMS) != 0){
-                               if (logger != NULL)
-                                       logger->sayF(LOG_ERROR | CAT_FILE, LC_MAKE_DIR_1,
-                                               i18n("could not make directory $1 (errno: $2)"))
-                                                       .arg(path.str()).arg(errno).end();
-                               rc = false;
-                               break;
-                       } else {
-#if defined __linux__
-                               setProperties(path.str(), properties);
-#endif
-                       }
-               }
-               if (ixSlash >= 0){
-                       // restore the full path:
-                       path.buffer()[ixSlash] = OS_SEPARATOR_CHAR;
-               }
-       }
-       return rc;
-}
-
-/**
- * Gets the arguments for the "list" command and execute this.
- *
- * @param argc      the number of arguments
- * @param argav     the argument vector
- */
-void ReDirSync::synchronize(int argc, const char* argv[]){
-    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();
-
-            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;
-                                       }
-                                       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;
-                                       }
-                               }
-                               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);
-}
-
-/**
- * Tests whether a abrevation of an argument is given.
- * @param full      the full name
- * @param part      the part to test
- * @return          <code>true</code>: part is a prefix of full
- */
-static bool isArg(const char* full, const char* part){
-    ReByteBuffer fullArg(full);
-    bool rc = fullArg.startsWith(part, -1);
-    return rc;
-}
-
-/**
- * Gets the arguments for the "help" command and execute this.
- *
- * @param argc      the number of arguments
- * @param argav     the argument vector
- */
-void ReDirTools::help(int argc, const char* argv[]){
-    if (argc <= 1)
-        printField(s_helpSummary);
-    else {
-        argc--;
-        argv++;
-        const char* arg0 = argv[0];
-        if (isArg("batch", arg0)){
-            ReDirBatch batch;
-            batch.help(NULL);
-        } else if (isArg("list", arg0)){
-            ReDirList list;
-            list.help(NULL);
-        } else if (isArg("help", arg0))
-            printField(s_helpSummary);
-        else if (isArg("statistic", arg0)){
-            ReDirStatistic stat;
-            stat.help(NULL);
-        } else if (isArg("test", arg0)){
-            void testAll();
-            testAll();
-        } else
-            printf("+++ unknown sub command: %s\n", arg0);
-    }
-}
-
-/**
- * Gets the arguments for the "statistic" command and execute this.
- *
- * @param argc      the number of arguments
- * @param argav     the argument vector
- */
-void ReDirTools::list(int argc, const char* argv[]){
-    ReDirList lister;
-    lister.list(argc, argv);
-}
-
-/**
- * Gets the arguments for any command and execute this.
- *
- * @param argc      the number of arguments
- * @param orgArgv   the argument vector
- */
-int ReDirTools::main(int argc, char* orgArgv[]){
-    ReDirTools tools;
-    const char** argv = (const char**) orgArgv;
-    if (argc < 2){
-        tools.help(0, argv);
-        exit(1);
-    }
-    argc--;
-    argv++;
-    const char* arg0 = argv[0];
-    if (isArg("batch", arg0))
-        tools.batch(argc, argv);
-    else if (isArg("list", arg0))
-        tools.list(argc, argv);
-    else if (isArg("help", arg0))
-        tools.help(argc, argv);
-    else if (isArg("statistic", arg0))
-        tools.statistic(argc, argv);
-    else if (isArg("synchronize", arg0))
-        tools.synchronize(argc, argv);
-    else if (isArg("test", arg0)){
-        void testAll();
-        testAll();
-    }else
-        tools.usage("unknown command: ", argv[1]);
-       ReLogger::freeGlobalLogger();
-    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);
-}
-
+bool ReTool::trace(const char* currentFile){\r
+       ReByteBuffer buffer(" ");\r
+       int duration = int(time(NULL) - m_startTime);\r
+       buffer.appendInt(duration / 60).appendInt(duration % 60, ":%02d: ");\r
+       buffer.appendInt(m_files).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
+ * Constructor.\r
+ */\r
+ReDirStatisticData::ReDirStatisticData() :\r
+       m_sizes(0),\r
+       m_files(0),\r
+       m_dirs(0),\r
+       m_path()\r
+{\r
+}\r
+/**\r
+ * Copy constructor.\r
+ *\r
+ * @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
+/**\r
+ * Assignment operator.\r
+ *\r
+ * @param source       the source to copy\r
+ * @return                     the instance itself\r
+ */\r
+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_path = source.m_path;\r
+       return *this;\r
+}\r
+\r
+/**\r
+ * 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
+{\r
+       // standard short options: D d O o P p T t v y Z z\r
+    m_programArgs.addBool("kbyte", \r
+        i18n("output format is '<kbyte> <path>' (like unix 'du' command)"),\r
+        'k', "kbyte", false);\r
+    addStandardFilterOptions();\r
+}\r
+/**\r
+ * Destructor.\r
+ */\r
+ReDirStatistic::~ReDirStatistic(){\r
+}\r
+\r
+/**\r
+ * Adds the data from another instance.\r
+ *\r
+ * @param source               the other instance\r
+ * @return                     the instance itself\r
+ */\r
+ReDirStatisticData& ReDirStatisticData::add(const ReDirStatisticData& source){\r
+       m_sizes += source.m_sizes;\r
+       m_files += source.m_files;\r
+       m_dirs += source.m_dirs;\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
+       m_path.setLength(0);\r
+}\r
+\r
+/**\r
+ * Calculates the statistic of a directory tree.\r
+ *\r
+ *\r
+ */\r
+const ReStringList& ReDirStatistic::calculate(const char* base, int level,\r
+                       void (*formatter)(const ReDirStatisticData& data, ReDirStatistic& parent,\r
+                               ReByteBuffer& line)){\r
+       ReDirEntryFilter_t filter;\r
+       ReTraverser traverser(base, this);\r
+       setFilterFromProgramArgs(filter);\r
+       traverser.setPropertiesFromFilter(&filter);\r
+       if (level > 1024)\r
+               level = 1024;\r
+       else if (level < 0)\r
+               level = 0;\r
+    m_list.clear();\r
+       ReDirStatisticData** dataStack = new ReDirStatisticData*[level + 1];\r
+       memset(dataStack, 0, sizeof dataStack[0] * (level + 1));\r
+       dataStack[0] = new ReDirStatisticData();\r
+       int topOfStack = 0;\r
+       ReDirStatus_t* entry;\r
+       int currentDepth = -1;\r
+       ReDirStatisticData* current = dataStack[0];\r
+       current->m_path.set(base, -1);\r
+       ReByteBuffer line;\r
+       bool useFilter = filter.m_minSize > 0 || filter.m_maxSize != -1\r
+                               || filter.m_minAge != 0 || filter.m_maxAge != 0\r
+                               || m_nodePatterns.count() > 0;\r
+       while( (entry = traverser.rawNextFile(currentDepth))){\r
+               if (currentDepth <= level && ! entry->m_path.equals(current->m_path)){\r
+                       if (currentDepth <= topOfStack){\r
+                               // close the entries of the stack above the new top level:\r
+                               while(topOfStack >= currentDepth){\r
+                                       // Add the data to the parent:\r
+                    if (topOfStack > 0)\r
+                        dataStack[topOfStack - 1]->add(*dataStack[topOfStack]);\r
+                                       // Append it to the result:\r
+                                       (*formatter)(*dataStack[topOfStack], *this, line);\r
+                                       m_list.append(line);\r
+                    topOfStack--;\r
+                               }\r
+                // We reuse the top of stack:\r
+                topOfStack++;\r
+                current = dataStack[topOfStack];\r
+                // exchange the top of stack with the new found directory:\r
+                current->clear();\r
+                               current->m_path.set(entry->m_path.str(), entry->m_path.length());;\r
+                       } else {\r
+                               // set up a new stack entry:\r
+                               if (currentDepth != topOfStack + 1)\r
+                                       assert(currentDepth == topOfStack + 1);\r
+\r
+                               topOfStack++;\r
+                               if (dataStack[topOfStack] == NULL)\r
+                                       dataStack[topOfStack] = new ReDirStatisticData();\r
+                               else\r
+                                       dataStack[topOfStack]->clear();\r
+                               current = dataStack[topOfStack];\r
+                               current->m_path.set(entry->m_path.str(), entry->m_path.length());\r
+                       }\r
+               }\r
+               if (entry->isDirectory()){\r
+                       current->m_dirs++;\r
+               } else if (! useFilter || filter.match(*entry)){\r
+                       current->m_sizes += entry->fileSize();\r
+                       current->m_files++;\r
+               }\r
+       }\r
+    // close all dirs with parents:\r
+       while(topOfStack > 0){\r
+               // Add the data to the parent:\r
+               dataStack[topOfStack - 1]->add(*dataStack[topOfStack]);\r
+               // Append it to the result:\r
+               (*formatter)(*dataStack[topOfStack], *this, line);\r
+               m_list.append(line);\r
+        topOfStack--;\r
+       }\r
+    // ... and the overall summery:\r
+    (*formatter)(*dataStack[0], *this, line);\r
+       m_list.append(line);\r
+       // free the resources:\r
+       for (int ix = 0; ix <= level; ix++)\r
+               delete dataStack[ix];\r
+       delete[] dataStack;\r
+       return m_list;\r
+}\r
+\r
+/**\r
+ * Build the statistic and print the results.\r
+ *\r
+ * @param arc   count of arguments in <code>argv</code>\r
+ * @param argv  the program arguments.\r
+ */\r
+void ReDirStatistic::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
+        ReDirStatistic statistic;\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 = statistic.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
+}\r
+/**\r
+ * Formats a line like the du (disk usage) command.\r
+ *\r
+ * This is a possible parameter of <code>ReDirStatistic::calculate()</code>.\r
+ *\r
+ * @param data         statistic data, including path name\r
+ * @param parent       the caller (<code>ReDirStatistic</code>). This allows to deliver\r
+ *                                     a context to this formatting routine (through derivation of\r
+ *                                     <code>ReDirStatistic</code>)\r
+ * @param line         OUT: the formatted line, the conclusion of the statistic data\r
+ */\r
+void formatLikeDu(const ReDirStatisticData& data,\r
+                       ReDirStatistic& parent, ReByteBuffer& line){\r
+       line.setLength(0);\r
+    // Round up to the next KiByte:\r
+       line.appendInt(int((data.m_sizes + 1023) / 1024)).append("\t").append(data.m_path);\r
+}\r
+\r
+/**\r
+ * Formats a line in a standard way: MBytes, file count and directory count.\r
+ *\r
+ * This is a possible parameter of <code>ReDirStatistic::calculate()</code>.\r
+ *\r
+ * @param data         statistic data, including path name\r
+ * @param parent       the caller (<code>ReDirStatistic</code>). This allows to deliver\r
+ *                                     a context to this formatting routine (through derivation of\r
+ *                                     <code>ReDirStatistic</code>)\r
+ * @param line         OUT: the formatted line, the conclusion of the statistic data\r
+ */\r
+void formatWithSizeFilesAndDirs(const ReDirStatisticData& data,\r
+                       ReDirStatistic& parent, ReByteBuffer& line){\r
+       line.setLength(0);\r
+    // Round up to the next KiByte:\r
+    char buffer[256];\r
+    _snprintf(buffer, sizeof buffer, "%14.6f MB %7d %7d\t",\r
+        data.m_sizes / 1E6, data.m_files, data.m_dirs);\r
+       line.append(buffer, -1).append(data.m_path);\r
+}\r
+/**\r
+ * Prints a vector of lines.\r
+ * \r
+ * @param lines     a vector of lines without newline ('\n')\r
+ */\r
+static void printField(const char** lines){\r
+    for (int ix = 0; lines[ix] != NULL; ix++){\r
+        printf("%s\n", lines[ix]);\r
+    }\r
+}\r
+\r
+/**\r
+ * Prints an message how to use the statistic module and exits.\r
+ */\r
+void ReDirTools::statisticUsage(){\r
+    ReDirStatistic statistic;\r
+    statistic.programArgs().help(NULL, false, stdout);\r
+}\r
+\r
+/**\r
+ * Prints an message how to use the program and exits.\r
+ *\r
+ * @param msg       an error message\r
+ * @param msg2      an addition to the error message or NULL\r
+ */\r
+void ReDirTools::usage(const char* msg, const char* msg2){\r
+    printf ("Version: %s\n", m_version);\r
+    printf ("usage: dirtool <command> <opt>\n");\r
+    statisticUsage();\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
+ * @param path      the name of the subdir to create\r
+ */\r
+void ReDirSync::makeDirWithParents(ReByteBuffer& path, int minWidth,\r
+                       ReTraverser& traverser){\r
+    struct stat info;\r
+       bool endsWithSlash = path.str()[path.length() - 1] == OS_SEPARATOR_CHAR;\r
+       if (endsWithSlash)\r
+               path.setLength(path.length() - 1);\r
+       if (stat(path.str(), &info) != 0){\r
+               ReFileProperties_t* props = NULL;\r
+#if defined __linux__\r
+               ReDirStatus_t* entry = traverser.topOfStack();\r
+               props = entry == NULL ? NULL : &entry->m_status;\r
+#endif\r
+               makeDirectory(path.str(), minWidth, props, ReLogger::globalLogger());\r
+       }\r
+       if (endsWithSlash)\r
+               path.append(OS_SEPARATOR, 1);\r
+}\r
+/**\r
+ * Constructor.\r
+ */\r
+ReDirSync::ReDirSync() :\r
+    ReTool(s_syncUsage, s_syncExamples),\r
+    m_buffer()\r
+{\r
+       // standard short options: D d O o P p T t v y Z z\r
+       m_buffer.ensureSize(4u*1024u*1024u);\r
+    m_programArgs.addBool("add",\r
+        i18n("copies only files which does not exist on the target"),\r
+        'a', "add", false);\r
+    m_programArgs.addBool("dry",\r
+        i18n("does nothing, but says what should be done"),\r
+        'Y', "dry", false);\r
+    m_programArgs.addInt("timediff",\r
+        i18n("filetime difference is considered to be equal\n"\r
+                       "if the difference is less than this value (in seconds)"),\r
+        'I', "time-delta", 2);\r
+    m_programArgs.addBool("ignoredate",\r
+                       i18n("the modification is recognized only by the different size (not time)"),\r
+        'i', "ignore-time", false);\r
+    m_programArgs.addBool("mustexist",\r
+                       i18n("files which don't exist on the target will not be copied"),\r
+        'm', "must-exist", false);\r
+    addStandardFilterOptions();\r
+}\r
+\r
+/**\r
+ * Copies a file.\r
+ *\r
+ * @param entry                the source file info\r
+ * @param target       the name of the target file\r
+ */\r
+void ReDirSync::copyFile(ReDirStatus_t* entry, const char* target){\r
+       ReFileProperties_t* props;\r
+#ifdef __linux__\r
+       props = &entry->m_status;\r
+#else\r
+       ReFileProperties_t properties;\r
+       properties.m_modified = *entry->modified();\r
+       properties.m_accessed = *entry->accessed();\r
+       properties.m_size = entry->fileSize();\r
+       props = &properties;\r
+#endif\r
+       copyFile(entry->fullName(), props, target, m_buffer,\r
+               ReLogger::globalLogger());\r
+}\r
+\r
+/**\r
+ * Copies a file.\r
+ *\r
+ * @param source               the source file name\r
+ * @param properties   NULL or the properties of the source file\r
+ * @param target               the name of the target file\r
+ * @param buffer               OUT: the reading uses this buffer<br>\r
+ *                                             Set the capacity to make it more efficient\r
+ * @param logger               NULL or the logger for error messages\r
+ * @return                             <code>true</code>success<br>\r
+ *                                             <code>false</code>error occurred\r
+ */\r
+bool ReDirSync::copyFile(const char* source, ReFileProperties_t* properties,\r
+               const char* target, ReByteBuffer& buffer, ReLogger* logger){\r
+       bool rc = false;\r
+#ifdef __linux__\r
+       struct stat info;\r
+       if (properties == NULL){\r
+               if (stat(source, &info) == 0)\r
+                       properties = &info;\r
+               else {\r
+                       if (logger != NULL)\r
+                               logger->sayF(LOG_ERROR | CAT_FILE, LC_COPY_FILE_1,\r
+                                       i18n("could not find: $ (errno: $2)")).arg(source)\r
+                                               .arg(errno).end();\r
+               }\r
+       }\r
+       FILE* fpSource = fopen(source, "rb");\r
+       if (fpSource == NULL){\r
+               if (logger != NULL)\r
+                       logger->sayF(LOG_ERROR | CAT_FILE, LC_COPY_FILE_2,\r
+                                       i18n("cannot open $1 (errno: $2)"))\r
+                               .arg(source).arg(errno).end();\r
+       } else {\r
+               FileSize_t size = properties == NULL ? 0x7fffffff : properties->st_size;\r
+               FILE* fpTarget = fopen(target, "w");\r
+               if (fpTarget == NULL){\r
+                       if (logger != NULL)\r
+                               logger->sayF(LOG_ERROR | CAT_FILE, LC_COPY_FILE_3,\r
+                                               i18n("cannot open $1 (errno: $2)"))\r
+                                       .arg(target).arg(errno).end();\r
+               } else{\r
+                       while(size > 0){\r
+                               size_t blockSize = buffer.capacity();\r
+                               if ((int) blockSize > size)\r
+                                       blockSize = size;\r
+                               if (fread(buffer.buffer(), blockSize, 1, fpSource) != 1){\r
+                                       if (logger != NULL)\r
+                                               logger->sayF(LOG_ERROR | CAT_FILE, LC_COPY_FILE_5,\r
+                                                               i18n("cannot read $1 (errno: $2)"))\r
+                                                       .arg(source).arg(errno).end();\r
+                                       break;\r
+                               }\r
+                               size_t written;\r
+                               if ((written = fwrite(buffer.buffer(), 1, blockSize,\r
+                                               fpTarget)) != blockSize){\r
+                                       if (logger != NULL)\r
+                                               logger->sayF(LOG_ERROR | CAT_FILE, LC_COPY_FILE_6,\r
+                                                               i18n("cannot write $1 [$2] (errno: $3)"))\r
+                                                       .arg(target).arg(written).arg(errno).end();\r
+                                       break;\r
+                               }\r
+                               size -= blockSize;\r
+                       }\r
+                       rc = size == 0ll;\r
+                       fclose(fpTarget);\r
+                       if (properties != NULL)\r
+                               setProperties(target, properties, logger);\r
+               }\r
+               fclose(fpSource);\r
+       }\r
+#elif defined __WIN32__\r
+    BOOL cancel = false;\r
+    rc = CopyFileExA(source, target, NULL, NULL, &cancel, COPY_FILE_NO_BUFFERING) != 0;\r
+    int errNo = 0;\r
+    if (! rc)\r
+        errNo = GetLastError();\r
+#endif\r
+       return rc;\r
+}\r
+/**\r
+ * Sets the file properties.\r
+ *\r
+ * @param fullName             the name of the file\r
+ * @param properties   the properties like times and rights\r
+ * @param logger               NULL or the logger for error messages\r
+ * @return                             <code>true</code>: success<br>\r
+ *                                             <code>false</code>: error occurred\r
+ */\r
+bool ReDirSync::setProperties(const char* fullName,\r
+               ReFileProperties_t* properties, ReLogger* logger){\r
+       bool rc = true;\r
+#if defined __linux__\r
+       struct utimbuf times;\r
+       times.actime = properties->st_atime;\r
+       times.modtime = properties->st_mtime;\r
+       if (utime(fullName, &times) != 0){\r
+               if (logger != NULL)\r
+                       logger->sayF(LOG_ERROR | CAT_FILE, LC_SET_PROPERTIES_1,\r
+                               i18n("cannot change file times: $1 (errno: $2)"))\r
+                               .arg(fullName).arg(errno).end();\r
+               rc = false;\r
+       }\r
+       int rights = properties->st_mode & (S_IRWXO | S_IRWXG | S_IRWXU);\r
+       if (chmod(fullName, rights) != 0) {\r
+               if (logger != NULL)\r
+                       logger->sayF(LOG_ERROR | CAT_FILE, LC_SET_PROPERTIES_2,\r
+                               i18n("cannot change file modes: $1 (errno: $2)"))\r
+                               .arg(fullName).arg(errno).end();\r
+               rc = false;\r
+       }\r
+       if (chown(fullName, properties->st_uid, properties->st_gid) != 0){\r
+               if (logger != NULL)\r
+                       logger->sayF(LOG_ERROR | CAT_FILE, LC_SET_PROPERTIES_3,\r
+                               i18n("cannot change file owner: $1 (errno: $2)"))\r
+                               .arg(fullName).arg(errno).end();\r
+               rc = false;\r
+       }\r
+#endif\r
+       return rc;\r
+}\r
+\r
+/**\r
+ * Creates a directory and its parents (if neccessary).\r
+ *\r
+ * @param directory            the full name of the directory\r
+ * @param properties   NULL or the properties of the new directory\r
+ * @param logger               NULL or the logger for error messages\r
+ * @return                             <code>true</code>success<br>\r
+ *                                             <code>false</code>error occurred\r
+ */\r
+bool ReDirSync::makeDirectory(const char* directory, int minLength,\r
+               ReFileProperties_t* properties, ReLogger* logger){\r
+       bool rc = true;\r
+       ReByteBuffer path(directory);\r
+       int start = 0;\r
+#if defined __WIN32__\r
+       start = path.indexOf(':');\r
+#endif\r
+       int ixSlash = start < 0 ? 0 : start;\r
+       struct stat info;\r
+       // for all parents and the full path itself:\r
+       while(ixSlash >= 0){\r
+               ixSlash = path.indexOf(OS_SEPARATOR_CHAR, ixSlash + 1);\r
+        if (ixSlash >= (int) path.length() - 1)\r
+            break;\r
+               // is the slash in front of the first node, e.g. 'e:\'?\r
+               if (ixSlash == start + 1)\r
+                       // not a real node: take the next node\r
+                       ixSlash = path.indexOf(OS_SEPARATOR_CHAR, ixSlash + 1);\r
+               if (ixSlash >= 0){\r
+                       // we handle only the next node:\r
+                       path.buffer()[ixSlash] = '\0';\r
+               }\r
+               // does the node exist?\r
+               if (lstat(path.str(), &info) != 0){\r
+                       // no, then we make it:\r
+                       if (_mkdir(path.str(), ALLPERMS) != 0){\r
+                               if (logger != NULL)\r
+                                       logger->sayF(LOG_ERROR | CAT_FILE, LC_MAKE_DIR_1,\r
+                                               i18n("could not make directory $1 (errno: $2)"))\r
+                                                       .arg(path.str()).arg(errno).end();\r
+                               rc = false;\r
+                               break;\r
+                       } else {\r
+#if defined __linux__\r
+                               setProperties(path.str(), properties);\r
+#endif\r
+                       }\r
+               }\r
+               if (ixSlash >= 0){\r
+                       // restore the full path:\r
+                       path.buffer()[ixSlash] = OS_SEPARATOR_CHAR;\r
+               }\r
+       }\r
+       return rc;\r
+}\r
+\r
+/**\r
+ * 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 ReDirSync::synchronize(int argc, const char* argv[]){\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
+\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
+                                       }\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
+                               }\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
+}\r
+\r
+/**\r
+ * Tests whether a abrevation of an argument is given.\r
+ * @param full      the full name\r
+ * @param part      the part to test\r
+ * @return          <code>true</code>: part is a prefix of full\r
+ */\r
+static bool isArg(const char* full, const char* part){\r
+    ReByteBuffer fullArg(full);\r
+    bool rc = fullArg.startsWith(part, -1);\r
+    return rc;\r
+}\r
+\r
+/**\r
+ * Gets the arguments for the "help" command and execute this.\r
+ *\r
+ * @param argc      the number of arguments\r
+ * @param argav     the argument vector\r
+ */\r
+void ReDirTools::help(int argc, const char* argv[]){\r
+    if (argc <= 1)\r
+        printField(s_helpSummary);\r
+    else {\r
+        argc--;\r
+        argv++;\r
+        const char* arg0 = argv[0];\r
+        if (isArg("batch", arg0)){\r
+            ReDirBatch batch;\r
+            batch.help(NULL);\r
+        } else if (isArg("list", arg0)){\r
+            ReDirList list;\r
+            list.help(NULL);\r
+        } else if (isArg("help", arg0))\r
+            printField(s_helpSummary);\r
+        else if (isArg("statistic", arg0)){\r
+            ReDirStatistic stat;\r
+            stat.help(NULL);\r
+        } else if (isArg("test", arg0)){\r
+            void testAll();\r
+            testAll();\r
+        } else\r
+            printf("+++ unknown sub command: %s\n", arg0);\r
+    }\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::list(int argc, const char* argv[]){\r
+    ReDirList lister;\r
+    lister.list(argc, argv);\r
+}\r
+\r
+/**\r
+ * Gets the arguments for any command and execute this.\r
+ *\r
+ * @param argc      the number of arguments\r
+ * @param orgArgv   the argument vector\r
+ */\r
+int ReDirTools::main(int argc, char* orgArgv[]){\r
+    ReDirTools tools;\r
+    const char** argv = (const char**) orgArgv;\r
+    if (argc < 2){\r
+        tools.help(0, argv);\r
+        exit(1);\r
+    }\r
+    argc--;\r
+    argv++;\r
+    const char* arg0 = argv[0];\r
+    if (isArg("batch", arg0))\r
+        tools.batch(argc, argv);\r
+    else if (isArg("list", arg0))\r
+        tools.list(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
+    else if (isArg("synchronize", arg0))\r
+        tools.synchronize(argc, argv);\r
+    else if (isArg("test", arg0)){\r
+        void testAll();\r
+        testAll();\r
+    }else\r
+        tools.usage("unknown command: ", argv[1]);\r
+       ReLogger::freeGlobalLogger();\r
+    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