]> gitweb.hamatoma.de Git - crepublib/commitdiff
ReDirectory, ReFileUtils, ReTestUnit master
authorhama <hama@siduction.net>
Sat, 8 Aug 2015 10:49:44 +0000 (12:49 +0200)
committerhama <hama@siduction.net>
Sat, 8 Aug 2015 10:49:44 +0000 (12:49 +0200)
* +ReDirectory::currentSize(), ::currentModified()
* deleteTree() returns static data
* +ReFileUtils::makeDirectoryWithParents()
* +ReTestMemoryAppender, checkLogContains(), checkLogContainsLocation()

base/ReDirectory.cpp
base/ReDirectory.hpp
base/ReTestUnit.cpp
base/ReTestUnit.hpp
cunit/cuReDirTools.cpp
cunit/cuReDirectory.cpp
cunit/cuReFileUtils.cpp
dirtool.cpp
os/ReFileUtils.cpp
os/ReFileUtils.hpp

index 1a99650263ce183164b44728d43d9070a1564664..73f7fce5e9189880c0dee272d5259334e6f8ccee 100644 (file)
@@ -40,21 +40,21 @@ ReDirectory::ReDirectory() :
  * @param path         The path of the directory.
  */
 ReDirectory::ReDirectory(const char* path) :
-                   m_path(),
-                   m_pattern(),
-                   m_stat(),
-                   m_currentFull(),
-       #if defined __linux__
-                   m_dir(NULL),
-                   m_entry(NULL),
-                   m_regExpr(),
-                   m_regExprFlags(0),
-                   m_regExprIsInitialized(false),
-                   m_isRegExpr(false),
-       #elif defined __WIN32__
-                   m_handle(INVALID_HANDLE_VALUE),
-                   m_data(),
-       #endif
+           m_path(),
+           m_pattern(),
+           m_stat(),
+           m_currentFull(),
+#if defined __linux__
+           m_dir(NULL),
+           m_entry(NULL),
+           m_regExpr(),
+           m_regExprFlags(0),
+           m_regExprIsInitialized(false),
+           m_isRegExpr(false),
+#elif defined __WIN32__
+           m_handle(INVALID_HANDLE_VALUE),
+           m_data(),
+#endif
            m_valid(false) {
 #if defined __linux__
        memset(&m_regExpr, 0, sizeof m_regExpr);
@@ -105,13 +105,13 @@ const char* ReDirectory::currentNode() {
  *
  * @param the status info given by <code>lstat()</code>
  */
-struct stat& ReDirectory::currentLStat(){
+struct stat& ReDirectory::currentLStat() {
 #if defined linux
-       if (m_stat.st_gid == (mode_t)-1 && filetimeIsUndefined(m_stat.st_mtim)){
+       if (m_stat.st_gid == (mode_t) -1 && filetimeIsUndefined(m_stat.st_mtim)) {
                lstat(currentFull().str(), &m_stat);
        }
 #elif defined __WIN32__
-       if (m_stat.st_mtime == (time_t) -1){
+       if (m_stat.st_mtime == (time_t) -1) {
                stat(currentFull().str(), &m_stat);
        }
 #endif
@@ -123,7 +123,7 @@ struct stat& ReDirectory::currentLStat(){
  *
  * @return     <code>true</code>: the current file is a directory
  */
-bool ReDirectory::currentIsDir(){
+bool ReDirectory::currentIsDir() {
 #if defined __linux__
        return S_ISDIR(currentLStat().st_mode);
 #elif defined __WIN32__
@@ -132,11 +132,11 @@ bool ReDirectory::currentIsDir(){
 }
 
 /**
- * Returns whether the current file is a directory.
+ * Returns the modification date of the current entry.
  *
- * @return     <code>true</code>: the current file is a directory
+ * @return     the modification date of the current entry
  */
-ReFileTime_t ReDirectory::currentModified(){
+ReFileTime_t ReDirectory::currentModified() {
 #if defined __linux__
        return currentLStat().st_mtim;
 #elif defined __WIN32__
@@ -144,6 +144,19 @@ ReFileTime_t ReDirectory::currentModified(){
 #endif
 }
 
+/**
+ * Returns the size of the current entry.
+ *
+ * @return     the filesize of the current entry
+ */
+int64_t ReDirectory::currentSize() {
+#if defined __linux__
+       return currentLStat().st_size;
+#elif defined __WIN32__
+       return m_data.ftLastWriteTime;
+#endif
+}
+
 /**
  * Deletes a directory tree.
  *
@@ -151,7 +164,8 @@ ReFileTime_t ReDirectory::currentModified(){
  * @param deleteBaseToo        true: the directory itself will be deleted
  *                                             false: only the files and subdirs in the dir will be deleted
  */
-void ReDirectory::deleteTree(const char* base, bool deleteBaseToo) {
+void ReDirectory::deleteTree(const char* base, bool deleteBaseToo,
+    int* deletedDirs, int* deletedFiles, int64_t* deletedSize) {
        if (true) {
                ReDirectory dir(base);
                if (dir.findFirst(ALL_FILES, false)) {
@@ -162,16 +176,17 @@ void ReDirectory::deleteTree(const char* base, bool deleteBaseToo) {
                                    && (node[1] == '\0' || (node[1] == '.' && node[2] == '\0'))))
                                    && stat(dir.currentFull().str(), &info) == 0) {
                                        if (S_ISDIR(info.st_mode))
-                                               deleteTree(dir.currentFull().str(), true);
-                                       else
-                                               _unlink(dir.currentFull().str());
+                                               deleteTree(dir.currentFull().str(), true, deletedDirs,
+                                                   deletedFiles, deletedSize);
+                                       else if (unlink(dir.currentFull().str())
+                                           == 0&& deletedFiles != NULL)
+                                               ++*deletedFiles;
                                }
                        } while (dir.findNext());
                }
                dir.close();
-               if (deleteBaseToo) {
-                       _rmdir(base);
-               }
+               if (deleteBaseToo && _rmdir(base) && deletedDirs != NULL)
+                       ++*deletedDirs;
        }
 }
 
@@ -179,30 +194,30 @@ void ReDirectory::deleteTree(const char* base, bool deleteBaseToo) {
  * Returns the filetime of a given file.
  *
  * @param filename     filename with path
- * @param modified     OUT: the modification date. May be NULL
- * @param created      OUT: the modification date. May be NULL
- * @param accessed     OUT: the modification date. May be NULL
+ * @param modified     OUT: the modification time. May be NULL
+ * @param created      OUT: the file creation time. May be NULL
+ * @param accessed     OUT: the last access time. May be NULL
  */
 bool ReDirectory::filetime(const char* filename, ReFileTime_t* modified,
-       ReFileTime_t* created, ReFileTime_t* accessed){
+    ReFileTime_t* created, ReFileTime_t* accessed) {
 #if defined __linux__
        struct stat info;
        bool rc = stat(filename, &info) == 0;
-       if (rc){
+       if (rc) {
                if (modified != NULL)
-                       *m_modified = info.st_mtim;
+                       *modified = info.st_mtim;
                if (created != NULL)
                        *created = info.st_ctim;
-               if (accessed != NULL
+               if (accessed != NULL)
                        *accessed = info.st_atim;
        }
 #elif defined __WIN32__
-       HANDLE handle = CreateFile(filename, GENERIC_READ, FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);\r
-       bool rc = handle != INVALID_HANDLE_VALUE;\r
-       if (rc){\r
-               if (GetFileTime(handle, created, accessed, modified) == 0)\r
-                       rc = false;\r
-               CloseHandle(handle);\r
+       HANDLE handle = CreateFile(filename, GENERIC_READ, FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
+       bool rc = handle != INVALID_HANDLE_VALUE;
+       if (rc) {
+               if (GetFileTime(handle, created, accessed, modified) == 0)
+               rc = false;
+               CloseHandle(handle);
        }
 #endif
        return rc;
index d8f80109fb3f7816c5d204a7f8e6fdadaf97d568..738296c37dd2d9dd8e93eae11a7eeb1dcf83f412 100644 (file)
@@ -23,7 +23,7 @@ public:
        /** @brief Returns the filename of the current file with path.
         * @return      the full filename with path
         */
-       const ReByteArray& currentFull(){
+       const ReByteArray& currentFull() {
                if (m_currentFull.empty())
                        m_currentFull.set(m_path).append(currentNode());
                return m_currentFull;
@@ -32,6 +32,7 @@ public:
        bool currentIsDir();
        struct stat& currentLStat();
        ReFileTime_t currentModified();
+       int64_t currentSize();
        bool findFirst(const char* pattern, bool isRegExpr);
        bool findNext();
        bool findYoungest(ReByteArray& filename);
@@ -41,9 +42,11 @@ public:
        void setDir(const char* path);
        void setRegExprFlags(int flags);
 public:
-       static void deleteTree(const char* base, bool deleteBaseToo = false);
+       static void deleteTree(const char* base, bool deleteBaseToo = false,
+           int* deletedDirs = NULL, int* deletedFiles = NULL,
+           int64_t* deletedSize = NULL);
        static bool filetime(const char* filename, ReFileTime_t* modified,
-               ReFileTime_t* created = NULL, ReFileTime_t* accessed = NULL);
+           ReFileTime_t* created = NULL, ReFileTime_t* accessed = NULL);
 public:
        static const char* ALL_FILES;
 private:
@@ -94,12 +97,12 @@ inline void setFiletimeUndef(ReFileTime_t& time) {
  * @param op2  second operand
  * @return             <code>true</code>: op1 == op2
  */
-inline bool operator ==(const ReFileTime_t& op1, const ReFileTime_t& op2){
+inline bool operator ==(const ReFileTime_t& op1, const ReFileTime_t& op2) {
 #if defined __linux__
        return op1.tv_sec == op2.tv_sec && op1.tv_nsec == op2.tv_nsec;
 #else
        return op1.dwHighDateTime == op2.dwHighDateTime
-                       && op1.dwLowDateTime == op2.dwLowDateTime;
+       && op1.dwLowDateTime == op2.dwLowDateTime;
 #endif
 }
 /** Returns whether a filetime is equal than another.
@@ -107,21 +110,22 @@ inline bool operator ==(const ReFileTime_t& op1, const ReFileTime_t& op2){
  * @param op2  second operand
  * @return             <code>true</code>: op1 == op2
  */
-inline bool operator !=(const ReFileTime_t& op1, const ReFileTime_t& op2){
-       return ! (op1 == op2);
+inline bool operator !=(const ReFileTime_t& op1, const ReFileTime_t& op2) {
+       return !(op1 == op2);
 }
 /** Returns whether a filetime is greater (younger) than another.
  * @param op1  first operand
  * @param op2  second operand
  * @return             <code>true</code>: op1 > op2
  */
-inline bool operator >(const ReFileTime_t& op1, const ReFileTime_t& op2){
+inline bool operator >(const ReFileTime_t& op1, const ReFileTime_t& op2) {
 #if defined __linux__
-       return op1.tv_sec > op2.tv_sec || op1.tv_sec == op2.tv_sec && op1.tv_nsec > op2.tv_nsec;
+       return op1.tv_sec > op2.tv_sec
+           || op1.tv_sec == op2.tv_sec && op1.tv_nsec > op2.tv_nsec;
 #else
        return op1.dwHighDateTime > op2.dwHighDateTime
-               || (op1.dwHighDateTime == op2.dwHighDateTime
-                       && op1.dwLowDateTime > op2.dwLowDateTime);
+       || (op1.dwHighDateTime == op2.dwHighDateTime
+               && op1.dwLowDateTime > op2.dwLowDateTime);
 #endif
 }
 /** Returns whether a filetime is greater (younger) or equal than another.
@@ -129,9 +133,10 @@ inline bool operator >(const ReFileTime_t& op1, const ReFileTime_t& op2){
  * @param op2  second operand
  * @return             <code>true</code>: op1 > op2
  */
-inline bool operator >=(const ReFileTime_t& op1, const ReFileTime_t& op2){
+inline bool operator >=(const ReFileTime_t& op1, const ReFileTime_t& op2) {
 #if defined __linux__
-       return op1.tv_sec > op2.tv_sec || op1.tv_sec == op2.tv_sec && op1.tv_nsec >= op2.tv_nsec;
+       return op1.tv_sec > op2.tv_sec
+           || op1.tv_sec == op2.tv_sec && op1.tv_nsec >= op2.tv_nsec;
 #else
        return op1.dwHighDateTime > op2.dwHighDateTime
        || (op1.dwHighDateTime == op2.dwHighDateTime
@@ -143,17 +148,16 @@ inline bool operator >=(const ReFileTime_t& op1, const ReFileTime_t& op2){
  * @param op2  second operand
  * @return             <code>true</code>: op1 > op2
  */
-inline bool operator <(const ReFileTime_t& op1, const ReFileTime_t& op2){
-       return ! (op1 >= op2);
+inline bool operator <(const ReFileTime_t& op1, const ReFileTime_t& op2) {
+       return !(op1 >= op2);
 }
 /** Returns whether a filetime is lower (older) or equal than another.
  * @param op1  first operand
  * @param op2  second operand
  * @return             <code>true</code>: op1 > op2
  */
-inline bool operator <=(const ReFileTime_t& op1, const ReFileTime_t& op2){
+inline bool operator <=(const ReFileTime_t& op1, const ReFileTime_t& op2) {
        return op1 == op2 || op1 < op2;
 }
 
-
 #endif /* REDIRECTORY_H_ */
index 315fd3b8fdea9d394dd190d70cf3034cfff0267f..eae8f0cc9fc083b2af024e29f2c49514cad9808c 100644 (file)
 
 ReByteArray ReTestUnit::m_tempDir;
 
+/**
+ * Stores the logging messages in a line sequence, inclusive the location as tag.
+ * This allows the retrieval whether a given error message is stored.
+ */
+class ReTestMemoryAppender: public ReMemoryAppender {
+public:
+       ReTestMemoryAppender() :
+                   ReMemoryAppender(1000) {
+               // with 24 bit tag, 16-bit linecount
+               setSizes(3, 2);
+       }
+public:
+       bool contains(const char* message, bool ignoreCase = true);
+       bool containsLocation(int location);
+       virtual void say(ReLogger* logger, const char* message);
+};
+/**
+ * Tests whether the stored logging messages contains a given message.
+ *
+ * @param message              message to search
+ * @param ignoreCase   <code>true</code>: the search is case insensitive
+ * @return                             <code>true</code>: the message is stored<br>
+ *                                             <code>false</code>: otherwise
+ */
+bool ReTestMemoryAppender::contains(const char* message, bool ignoreCase) {
+       ReByteArray line;
+       bool rc = false;
+       for (size_t ix = 0; !rc && ix < count(); ix++) {
+               get(ix, line);
+               rc = line.indexOf(message, -1, 0, -1, ignoreCase) >= 0;
+       }
+       return rc;
+}
+/**
+ * Tests whether the stored logging messages contains a given location.
+ *
+ * @param location     location to search
+ * @return                     <code>true</code>: the location is stored<br>
+ *                                     <code>false</code>: otherwise
+ */
+bool ReTestMemoryAppender::containsLocation(int location) {
+       ReByteArray line;
+       ReSeqArray::Tag tag;
+       bool rc = false;
+       for (size_t ix = 0; !rc && ix < count(); ix++) {
+               get(ix, line, &tag);
+               rc = tag == location;
+       }
+       return rc;
+}
+
+/**
+ * Stores a logging message.
+ *
+ * Distinction to ReMemoryAppender::say(): the location is stored as tag.
+ * This enables an easy retrieval.
+ *
+ * @param logger       the caller
+ * @param message      the logging message to store
+ */
+void ReTestMemoryAppender::say(ReLogger* logger, const char* message) {
+       int theCount = count();
+       if (theCount >= m_maxLines)
+               remove(theCount - 1);
+       // we store in reverse order:
+       add(0, message != NULL ? message : logger->asCString(), -1,
+           logger->currentLocation());
+}
+
 /** @brief Constructor.
  *
  * @param name                 the name of the test class
@@ -23,7 +92,7 @@ ReTestUnit::ReTestUnit(const char* name, const char* sourceFile) :
            m_name(name),
            m_sourceFile(sourceFile),
            m_buffer(),
-           m_memoryAppender(new ReMemoryAppender(1024)),
+           m_memoryAppender(new ReTestMemoryAppender()),
            m_silentLogger() {
        m_silentLogger.addAppender(m_memoryAppender);
        int ix = m_sourceFile.rindexOf(OS_SEPARATOR, 1);
@@ -227,6 +296,41 @@ void ReTestUnit::assertNotNull(void* pointer, int lineNo) {
        }
 }
 
+/**
+ * Checks whether the silent logger contains a given error message.
+ *
+ * @param location     the location of the error message
+ * @param lineNo       the line number of the test (for the error message)
+ */
+void ReTestUnit::assertLogContainsLocation(int location, int lineNo) {
+       bool rc = m_memoryAppender->containsLocation(location);
+       if (!rc) {
+               logF(true, "%s-%d: location not found: %d", m_sourceFile.str(), lineNo,
+                   location);
+               ReByteArray logMsg;
+               logF(false, "log messages:\n%s--- end of log messages",
+                   m_memoryAppender->join(logMsg).str());
+       }
+}
+
+/**
+ * Checks whether the silent logger contains a given part of an logging message.
+ *
+ * @param partOfMessage        the string to search
+ * @param lineNo       the line number of the test (for the error message)
+ */
+void ReTestUnit::assertLogContains(const char* partOfMessage, bool ignoreCase,
+    int lineNo) {
+       bool rc = m_memoryAppender->contains(partOfMessage, ignoreCase);
+       if (!rc) {
+               logF(true, "%s-%d: message not found: %s", m_sourceFile.str(), lineNo,
+                   partOfMessage);
+               ReByteArray logMsg;
+               logF(false, "log messages:\n%s--- end of log messages",
+                   m_memoryAppender->join(logMsg).str());
+       }
+}
+
 /** @brief Checks a pointer expression. A not <code>null</code> value will be logged.
  *
  * @param pointer              this expression will be tested
index 1025770c36fd1d7daa1b9229fb75db876f0f3dab..a3d3cb0c9d54767fe4b1ff2d33b063a8a66b9dad 100644 (file)
@@ -12,7 +12,7 @@
 #ifndef RETESTUNIT_H_
 #define RETESTUNIT_H_
 
-class ReMemoryAppender;
+class ReTestMemoryAppender;
 class ReTestUnit {
 public:
        ReTestUnit(const char* name, const char* sourcefile);
@@ -37,6 +37,9 @@ public:
        void assertEqualFiles(const char* name1, const char* name2, int lineNo);
        void assertFileExists(const char* name, int lineNo);
        void assertFalse(bool conditon, int lineNo);
+       void assertLogContainsLocation(int location, int lineNo);
+       void assertLogContains(const char* partOfMessage, bool ignoreCase,
+           int lineNo);
        void assertNotNull(void* pointer, int lineNo);
        void assertNull(void* pointer, int lineNo);
        void assertTrue(bool conditon, int lineNo);
@@ -58,7 +61,7 @@ protected:
        ReByteArray m_name;
        ReByteArray m_sourceFile;
        ReByteArray m_buffer;
-       ReMemoryAppender* m_memoryAppender;
+       ReTestMemoryAppender* m_memoryAppender;
        ReLogger m_silentLogger;
 protected:
        static ReByteArray m_tempDir;
@@ -72,4 +75,6 @@ protected:
 #define checkFileExists(fn) assertFileExists(fn, __LINE__)
 #define checkDirExists(fn) assertDirExists(fn, __LINE__)
 #define checkFileEqu(f1, f2) assertEqualFiles(f1, f2, __LINE__)
+#define checkLogContains(msg, ignoreCase) assertLogContains(msg, ignoreCase, __LINE__)
+#define checkLogContainsLocation(location) assertLogContainsLocation(location, __LINE__)
 #endif /* RETESTUNIT_H_ */
index 6934c9f839995d79a96a40567c24a81e484b9f3a..b4be51c10dbc520f5dd7c0ff41491a8b359a363a 100644 (file)
@@ -109,6 +109,44 @@ private:
                ReDirDelete(m_logger).run(-1, argv2);
                mayNotExist(s_allFiles);
        }
+       void buildFile() {
+
+       }
+       void testSyncDelete() {
+               /*
+                rebuildTree();
+                ReByteArray srcDir(ReFileUtils::tempDir("src"));
+                ReByteArray trgDir(ReFileUtils::tempDir("trg"));
+                ReByteArray optOutput("--output-file=");
+                ReByteArray nameCurrent;
+                buildFilename("current", nameCurrent);
+                optOutput.append(nameCurrent);
+                const char* argv[] = { "sync", "--delete", optOutput.str(), m_base
+                .str(), NULL };
+                const char* existing[] = { //
+                " 1.txt", //
+                "*dir1", //
+                "*dir2", //
+                "*dir1/cache", //
+                "*dir1/dir1_1", //
+                "*dir1/dir1_2", //
+                "*dir1/dir1_2/dir1_2_1", //
+                " dir2/2.x", //
+                " dir1/cache/cache.txt", //
+                NULL };
+                const char* notExisting[] = { //
+                " dir1/dir1_2/dir1_2_1/x1.txt", //
+                " dir1/dir1_2/dir1_2_1/x2.txt", //
+                NULL };
+                ReDirDelete(m_logger).run(-1, argv);
+                mustExist(existing);
+                mayNotExist(notExisting);
+
+                const char* argv2[] = { "delete", optOutput.str(), m_base.str(), NULL };
+                ReDirDelete(m_logger).run(-1, argv2);
+                mayNotExist(s_allFiles);
+                */
+       }
        void mustExist(const char** names) {
                ReByteArray name;
                struct stat info;
@@ -220,7 +258,7 @@ private:
                ReByteArray buffer;
                buffer.ensureSize(5);
                ReDirSync::copyFile(src.str(), false, NULL, trg.str(), buffer,
-                       ReLogger::globalLogger());
+                   ReLogger::globalLogger());
                checkFileEqu(src.str(), trg.str());
 #else
                log(false, "testCopyFile not implemented");
@@ -460,26 +498,27 @@ private:
                target.append("synctest", -1);
                _mkdir(target.str(), ALLPERMS);
                ReDirectory::deleteTree(target.str(), false);
-               const char* argv[] = { "dt", "sync", "-P;*;-cache", "-p;*.txt", "-v3", optOutput.str(), source
-                   .str(), target.str() };
+               const char* argv[] = { "dt", "sync", "-P;*;-cache", "-p;*.txt", "-v3",
+                   optOutput.str(), source.str(), target.str() };
                tools.main(8, (char**) argv);
                target.appendChar(OS_SEPARATOR_CHAR);
-               for(const char** pFile = s_allFiles; *pFile != NULL; pFile++){
+               for (const char** pFile = s_allFiles; *pFile != NULL; pFile++) {
                        ReByteArray fullTarget(target);
                        fullTarget.append(*pFile);
-                       if (fullTarget.endsWith(".txt") || ! fullTarget.contains("cache")){
-                               checkT(stat(fullTarget.str(), &info) != 0);
+                       if (fullTarget.endsWith(".txt") || !fullTarget.contains("cache")) {
+                               checkT(stat(fullTarget.str(), &info) == 0);
                        } else {
-                               checkF(stat(fullTarget.str(), &info) != 0);
+                               checkF(stat(fullTarget.str(), &info) == 0);
                        }
                }
                static const char* content =
                    "+dir1_2/dir1_2_1/x1.txt\n"
-                       "+dir1_2/dir1_2_1/x2.txt\n"
-                       "+cache/cache.txt\n"
-                       "-/tmp/retestunit/synctest/dir1/cache/cache.txt\n"
-                       "=== copied:    * sec        3 file(s)     0.000074 MByte (* MB/sec).\n"
-                       "===   tree:      5 dir(s)       3 file(s)     0.000074 MByte";
+                           "+dir1_2/dir1_2_1/x2.txt\n"
+                           "+cache/cache.txt\n"
+                           "-/tmp/retestunit/synctest/dir1/cache/cache.txt\n"
+                           "===  copied:    * sec        3 file(s)     0.000074 MByte (* MB/sec).\n"
+                           "===    tree:      5 dir(s)       3 file(s)     0.000074 MByte\n"
+                           "=== deleted:      1 dir(s)       1 file(s)     0.000020 MByte";
                ReByteArray content2(content);
                content2.replace('/', OS_SEPARATOR_CHAR);
                testFileContentWithWildcards(nameCurrent, content2.str());
@@ -498,18 +537,18 @@ private:
                tools.main(6, (char**) argv);
        }
        /*
-           " 1.txt", //
-               "*dir1", //
-               "*dir2", //
-               "*dir1/cache", //
-               "*dir1/dir1_1", //
-               "*dir1/dir1_2", //
-               "*dir1/dir1_2/dir1_2_1", //
-               " dir1/dir1_2/dir1_2_1/x1.txt", //
-               " dir1/dir1_2/dir1_2_1/x2.txt", //
-               " dir2/2.x", //
-               " dir1/cache/cache.txt", //
-               NULL };
+        " 1.txt", //
+        "*dir1", //
+        "*dir2", //
+        "*dir1/cache", //
+        "*dir1/dir1_1", //
+        "*dir1/dir1_2", //
+        "*dir1/dir1_2/dir1_2_1", //
+        " dir1/dir1_2/dir1_2_1/x1.txt", //
+        " dir1/dir1_2/dir1_2_1/x2.txt", //
+        " dir2/2.x", //
+        " dir1/cache/cache.txt", //
+        NULL };
 
         */
 };
index c547a9a91f368f0ec18d18416f5b6d4af3aa5629..d2e1db1db031d03fc5479795287eb2e3de681339 100644 (file)
@@ -9,6 +9,7 @@
  * The latest sources: https://github.com/republib
  */
 #include "base/rebase.hpp"
+#include "os/reos.hpp"
 
 class TestReDirectory: public ReTestUnit {
 public:
@@ -18,9 +19,39 @@ public:
        }
 private:
        void run() {
+               testDeleteTree();
                testFileTime();
                testBase();
        }
+       void testDeleteTree() {
+               const char* base = "s4712";
+               const char* subDir = "s1";
+               ReByteArray dir(ReFileUtils::tempDir(base));
+               ReByteArray dir2(ReFileUtils::tempFile(subDir, base));
+               ReByteArray fn(dir2);
+               fn.appendChar(OS_SEPARATOR_CHAR).append("f1");
+               ReFileUtils::writeString(fn.str(), "xyz");
+               struct stat info;
+               checkEqu(0, stat(fn.str(), &info));
+               // delete exclusive base:
+               int files, dirs;
+               int64_t sizes;
+               ReDirectory::deleteTree(dir.str(), false, &dirs, &files, &sizes);
+               checkEqu(1, dirs);
+               checkEqu(1, files);
+               checkEqu(3L, sizes);
+               checkF(0 == stat(dir2.str(), &info));
+               // delete inclusive base:
+               _mkdir(dir2.str(), ALLPERMS);
+               checkEqu(0, stat(dir2.str(), &info));
+               ReFileUtils::writeString(fn.str(), "xyz0");
+               checkEqu(0, stat(fn.str(), &info));
+               ReDirectory::deleteTree(dir.str(), true, &dirs, &files, &sizes);
+               checkEqu(2, dirs);
+               checkEqu(1, files);
+               checkEqu(4L, sizes);
+               checkF(0 == stat(dir2.str(), &info));
+       }
        void checkOrder(ReFileTime_t& time1, ReFileTime_t& time2,
            ReFileTime_t& time3) {
                checkT(time1 < time2);
index aafd43db5ab0aa62bba1ad68e9c41eb8839b6dde..a3e1b6eb944087aed92b7c549b805db806570414 100644 (file)
@@ -20,6 +20,7 @@ public:
        }
 private:
        void run() {
+               testMakeDirectoryWithParents();
                testReadWrite();
                testSetFiles();
                testTempFile();
@@ -79,6 +80,37 @@ private:
                ReFileUtils::writeString(fn.str(), "hi");
                checkEqu(0, stat(fn.str(), &info));
        }
+       void testMakeDirectoryWithParents() {
+               ReByteArray base(ReFileUtils::tempDir("s4711"));
+               ReByteArray dir(base);
+               dir.appendChar(OS_SEPARATOR_CHAR).append("d1").appendChar(
+                   OS_SEPARATOR_CHAR).append("d2");
+               ReDirectory::deleteTree(base.str(), true);
+               struct stat info;
+               checkF(0 == stat(base.str(), &info));
+               // create 3 directories, not ending with a separator:
+               checkT(ReFileUtils::makeDirWithParents(dir.str()));
+               checkEqu(0, stat(dir.str(), &info));
+               checkEqu(0, _rmdir(dir.str()));
+               checkF(0 == stat(dir.str(), &info));
+               dir.appendChar(OS_SEPARATOR_CHAR);
+               // create the last directory, with an ending separator
+               checkT(ReFileUtils::makeDirWithParents(dir.str()));
+               checkEqu(0, stat(dir.str(), &info));
+
+               // Error: a node is a normal file
+               // Remove trailing separator
+               dir.setLength(dir.length() - 1);
+               checkEqu(0, _rmdir(dir.str()));
+               checkF(0 == stat(dir.str(), &info));
+               ReFileUtils::writeString(dir.str(), "x");
+               checkEqu(0, stat(dir.str(), &info));
+               checkF(S_ISDIR(info.st_mode));
+               checkF(
+                   ReFileUtils::makeDirWithParents(dir.str(), &m_silentLogger));
+               checkLogContainsLocation(51003);
+               checkLogContains(dir.str(), false);
+       }
 };
 extern void testReFileUtils(void);
 
index 253827dd7cfd42e445e0650665aca8561e2ce10a..91cede827db3be8219a99f7a12874147e0d75388 100644 (file)
 //
 #include "base/rebase.hpp"
 #include "os/reos.hpp"
-
+#if defined __WIN32__
 int _tmain(int argc, _TCHAR* argv[]) {
        ReDirTools tool;
        tool.main(argc, argv);
        return 0;
 }
-
+#endif
index 737d37c840b65c49bd17ae29287c884af6881b6b..72bac7044b0782065fe623e69c447813e33a17aa 100644 (file)
@@ -12,7 +12,9 @@
 #include "os/reos.hpp"
 
 enum LOCATION_DIRTOOL {
-       LC_SET_TIMES_1 = LOC_FIRST_OF(LOC_FILEUTILS), // 50101
+       LC_SET_TIMES_1 = LOC_FIRST_OF(LOC_FILEUTILS), // 51001
+       LC_MK_DIR_1,            // 51002
+       LC_MK_DIR_2,            // 51003
 };
 
 /**
@@ -79,6 +81,60 @@ time_t ReFileUtils::filetimeToTime(const ReFileTime_t* filetime) {
 #endif
 }
 
+/**
+ * Creates a directory (if not it does not exist) and logs errors.
+ *
+ * @param path         the full name
+ * @param logger       NULL or the logger
+ * @return                     <code>true</code>: success: the directory exists<br>
+ *                                     <code>false</code>: error occurred
+ */
+static bool ReFileUtils::makeDir(const char* path, ReLogger* logger) {
+       struct stat info;
+       bool rc = true;
+       if (stat(path, &info) != 0) {
+               rc = _mkdir(path, ALLPERMS) == 0;
+               if (!rc && logger != NULL)
+                       logger->sayF(LOG_ERROR | CAT_FILE, LC_MK_DIR_1,
+                           "can't create directory ($1): $2").arg(errno).arg(path).end();
+       } else if (!S_ISDIR(info.st_mode)) {
+               rc = false;
+               if (logger != NULL)
+                       logger->sayF(LOG_ERROR | CAT_FILE, LC_MK_DIR_2,
+                           "can't create directory (is a file): $1").arg(path).end();
+       }
+       return rc;
+}
+/**
+ * Creates a directory and (if necessary) its parent directories.
+ *
+ * @param path         the full name
+ * @param logger       NULL or the logger
+ * @return                     <code>true</code>: success: the directory exists<br>
+ *                                     <code>false</code>: error occurred
+ */
+bool ReFileUtils::makeDirWithParents(const char* path, ReLogger* logger) {
+       const char* end;
+       const char* start = path;
+       bool rc = true;
+       ReByteArray dir;
+       while (rc && (end = strchr(start, OS_SEPARATOR_CHAR)) != NULL) {
+               if (end == path) {
+                       start++;
+                       dir.appendChar(OS_SEPARATOR_CHAR);
+               } else {
+                       dir.append(start, end - start);
+                       start = end + 1;
+                       rc = makeDir(dir.str(), logger);
+                       dir.appendChar(OS_SEPARATOR_CHAR);
+               }
+       }
+       if (rc && start[0] != '\0') {
+               dir.append(start);
+               rc = makeDir(dir.str(), logger);
+       }
+       return rc;
+}
 /**
  * Reads the content of a file into a buffer.
  *
index 8ac5a44ab5f5b7497fff06d48c2a5813f0634879..4de7e293c81237ee228695570627035994053868 100644 (file)
@@ -16,6 +16,9 @@ public:
        static ReByteArray& filetimeToString(const ReFileTime_t* time,
            ReByteArray& buffer);
        static time_t filetimeToTime(const ReFileTime_t* time);
+       static bool makeDir(const char* path, ReLogger* logger = NULL);
+       static bool makeDirWithParents(const char* path, ReLogger* logger =
+           NULL);
        static ReByteArray& readString(const char* filename, ReByteArray& buffer);
        static void setTimes(const char* name, ReFileTime_t modified,
            ReFileTime_t* accessed = NULL, ReLogger* logger = NULL);