From: hama Date: Sat, 8 Aug 2015 10:49:44 +0000 (+0200) Subject: ReDirectory, ReFileUtils, ReTestUnit X-Git-Url: https://gitweb.hamatoma.de/?a=commitdiff_plain;h=5d7d109e6f497f99f2598e0f4920564353dbb114;p=crepublib ReDirectory, ReFileUtils, ReTestUnit * +ReDirectory::currentSize(), ::currentModified() * deleteTree() returns static data * +ReFileUtils::makeDirectoryWithParents() * +ReTestMemoryAppender, checkLogContains(), checkLogContainsLocation() --- diff --git a/base/ReDirectory.cpp b/base/ReDirectory.cpp index 1a99650..73f7fce 100644 --- a/base/ReDirectory.cpp +++ b/base/ReDirectory.cpp @@ -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 lstat() */ -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 true: 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 true: 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); - bool rc = handle != INVALID_HANDLE_VALUE; - if (rc){ - if (GetFileTime(handle, created, accessed, modified) == 0) - rc = false; - CloseHandle(handle); + 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; diff --git a/base/ReDirectory.hpp b/base/ReDirectory.hpp index d8f8010..738296c 100644 --- a/base/ReDirectory.hpp +++ b/base/ReDirectory.hpp @@ -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 true: 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 true: 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 true: 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 true: 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 true: 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 true: 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_ */ diff --git a/base/ReTestUnit.cpp b/base/ReTestUnit.cpp index 315fd3b..eae8f0c 100644 --- a/base/ReTestUnit.cpp +++ b/base/ReTestUnit.cpp @@ -12,6 +12,75 @@ 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 true: the search is case insensitive + * @return true: the message is stored
+ * false: 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 true: the location is stored
+ * false: 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 null value will be logged. * * @param pointer this expression will be tested diff --git a/base/ReTestUnit.hpp b/base/ReTestUnit.hpp index 1025770..a3d3cb0 100644 --- a/base/ReTestUnit.hpp +++ b/base/ReTestUnit.hpp @@ -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_ */ diff --git a/cunit/cuReDirTools.cpp b/cunit/cuReDirTools.cpp index 6934c9f..b4be51c 100644 --- a/cunit/cuReDirTools.cpp +++ b/cunit/cuReDirTools.cpp @@ -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 }; */ }; diff --git a/cunit/cuReDirectory.cpp b/cunit/cuReDirectory.cpp index c547a9a..d2e1db1 100644 --- a/cunit/cuReDirectory.cpp +++ b/cunit/cuReDirectory.cpp @@ -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); diff --git a/cunit/cuReFileUtils.cpp b/cunit/cuReFileUtils.cpp index aafd43d..a3e1b6e 100644 --- a/cunit/cuReFileUtils.cpp +++ b/cunit/cuReFileUtils.cpp @@ -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); diff --git a/dirtool.cpp b/dirtool.cpp index 253827d..91cede8 100644 --- a/dirtool.cpp +++ b/dirtool.cpp @@ -12,10 +12,10 @@ // #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 diff --git a/os/ReFileUtils.cpp b/os/ReFileUtils.cpp index 737d37c..72bac70 100644 --- a/os/ReFileUtils.cpp +++ b/os/ReFileUtils.cpp @@ -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 true: success: the directory exists
+ * false: 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 true: success: the directory exists
+ * false: 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. * diff --git a/os/ReFileUtils.hpp b/os/ReFileUtils.hpp index 8ac5a44..4de7e29 100644 --- a/os/ReFileUtils.hpp +++ b/os/ReFileUtils.hpp @@ -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);