From: hama Date: Sat, 1 Aug 2015 23:04:58 +0000 (+0200) Subject: Tests in cuReFileUtils.cpp, dirtool X-Git-Url: https://gitweb.hamatoma.de/?a=commitdiff_plain;h=104aaffeb90c7474cd1f0c66022e827486c6d4b4;p=crepublib Tests in cuReFileUtils.cpp, dirtool * copy file times + owner/rights --- diff --git a/base/ReDirectory.cpp b/base/ReDirectory.cpp index 5b666c9..67912df 100644 --- a/base/ReDirectory.cpp +++ b/base/ReDirectory.cpp @@ -19,39 +19,42 @@ const char* ReDirectory::ALL_FILES = ""; ReDirectory::ReDirectory() : m_path(), m_pattern(), - m_currentFull(), m_stat(), + m_currentFull(), #if defined __linux__ m_dir(NULL), m_entry(NULL), -// m_regExpr + m_regExpr(), m_regExprFlags(0), m_regExprIsInitialized(false), m_isRegExpr(false), #elif defined __WIN32__ m_handle(INVALID_HANDLE_VALUE), -//m_data() + m_data(), #endif m_valid(false) { } + /** @brief Constructor. * * @param path The path of the directory. */ ReDirectory::ReDirectory(const char* path) : - m_path(), - m_pattern(), -#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(0), -//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); @@ -103,7 +106,7 @@ const char* ReDirectory::currentNode() { * @param the status info given by lstat() */ struct stat& ReDirectory::currentLStat(){ - if (m_stat.st_gid == -1 && filetimeIsUndefined(m_stat.st_mtim)){ + if (m_stat.st_gid == (mode_t)-1 && filetimeIsUndefined(m_stat.st_mtim)){ lstat(currentFull().str(), &m_stat); } return m_stat; diff --git a/base/baselocations.hpp b/base/baselocations.hpp index dca1361..10a27c4 100644 --- a/base/baselocations.hpp +++ b/base/baselocations.hpp @@ -26,6 +26,7 @@ enum ReModules_t { LOC_LOGGER, LOC_UDP, LOC_CSTRING, + LOC_FILEUTILS, /*510*/ }; #define LOC_FIRST_OF(moduleNo) (moduleNo*100+1) diff --git a/base/rebase.hpp b/base/rebase.hpp index 8bc8c07..48f6ea3 100644 --- a/base/rebase.hpp +++ b/base/rebase.hpp @@ -89,6 +89,9 @@ inline void bzero(void* ptr, size_t size){ memset(ptr, 0, size); } #endif +// avoids warning like "parameter not used" +#define reUseParam(var) (void) var + enum ReOSType { OS_UNDEF, OS_LINUX = 'l', @@ -129,7 +132,7 @@ inline int min(int a, int b) { * @return maximum of a and b */ inline int max(int a, int b) { - return a < b ? a : b; + return a > b ? a : b; } #include "base/ReClassId.hpp" #include "base/ReException.hpp" diff --git a/cunit/cuReDirTools.cpp b/cunit/cuReDirTools.cpp index d373a7a..ddfadd6 100644 --- a/cunit/cuReDirTools.cpp +++ b/cunit/cuReDirTools.cpp @@ -219,8 +219,8 @@ private: trg.append("copy_x1.txt"); ReByteArray buffer; buffer.ensureSize(5); - ReDirSync::copyFile(src.str(), NULL, trg.str(), buffer, - ReLogger::globalLogger()); + ReDirSync::copyFile(src.str(), false, NULL, trg.str(), buffer, + ReLogger::globalLogger()); checkFileEqu(src.str(), trg.str()); #else log(false, "testCopyFile not implemented"); @@ -495,7 +495,7 @@ private: _mkdir(target.str(), ALLPERMS); const char* argv[] = { "dt", "sync", "--delete=1h", "-p;*.txt", source .str(), target.str() }; - //tools.main(6, (char**) argv); + tools.main(6, (char**) argv); } /* " 1.txt", // diff --git a/cunit/cuReFileUtils.cpp b/cunit/cuReFileUtils.cpp new file mode 100644 index 0000000..68f5092 --- /dev/null +++ b/cunit/cuReFileUtils.cpp @@ -0,0 +1,86 @@ +/* + * cuReFileUtils.cpp + * + * License: Public Domain + * You can use and modify this file without any restriction. + * Do what you want. + * No warranties and disclaimer of any damages. + * You also can use the license from http://www.wtfpl.net/. + * The latest sources: https://github.com/republib + */ + +#include "base/rebase.hpp" +#include "os/reos.hpp" + +class TestReFileUtils: public ReTestUnit { +public: + TestReFileUtils() : + ReTestUnit("ReFileUtils", __FILE__) { + run(); + } +private: + void run() { + testReadWrite(); + testSetFiles(); + testTempFile(); + } + void testReadWrite(){ + ReByteArray name = ReFileUtils::tempFile("refiletest.$$$"); + const char* content = "123\nabc\n"; + ReFileUtils::writeString(name.str(), content); + ReByteArray buffer; + ReFileUtils::readString(name.str(), buffer); + checkEqu(content, buffer); + + // invalid filename: + ReFileUtils::readString("/./././.", buffer); + checkEqu("", buffer); + } + void testSetFiles(){ + ReByteArray name = ReFileUtils::tempFile("setfile_test.dat"); + struct stat info; + unlink(name.str()); + ReFileUtils::writeString(name.str(), ""); + checkEqu(0, stat(name.str(), &info)); + struct tm time1; + time1.tm_year = 2015 - 1900; + // March + time1.tm_mon = 2; + time1.tm_mday = 30; + time1.tm_hour = 11; + time1.tm_min = 33; + time1.tm_sec = 44; + time_t time2 = mktime(&time1); + ReFileTime_t time3; + ReFileUtils::timeToFiletime(time2, time3); + ReFileTime_t time4; + int diff = 86400 + 2 * 3600 + 3 * 60 + 2; + ReFileUtils::timeToFiletime(time2 + diff, time4); + ReFileUtils::setTimes(name.str(), time3, &time4, NULL); + checkEqu(0, stat(name.str(), &info)); + checkT(time2 == info.st_mtim.tv_sec); + checkT(time2 + diff == info.st_atim.tv_sec); + } + void testTempFile(){ + const char* subdir = "refileutiltest"; + const char* node = "test1.$$.txt"; + ReByteArray dir = ReFileUtils::tempDir(subdir); + ReByteArray fn = ReFileUtils::tempFile(node, subdir); + checkT(fn.startsWith(dir.str())); + checkT(fn.endsWith(node)); + unlink(fn.str()); + struct stat info; + checkF(0 == stat(fn.str(), &info)); + ReFileUtils::writeString(fn.str(), "hi"); + checkEqu(0, stat(fn.str(), &info)); + } +}; +extern void testReFileUtils(void); + +void testReFileUtils(void) { + TestReFileUtils unit; +} + + + + diff --git a/cunit/cuReRandomizer.cpp b/cunit/cuReRandomizer.cpp index f099365..4da00f2 100644 --- a/cunit/cuReRandomizer.cpp +++ b/cunit/cuReRandomizer.cpp @@ -26,7 +26,7 @@ private: void testNextInt64Repeater() { ReCongruentialGenerator rand; const int MAX = 16; - time_t start = time(NULL); + clock_t start = clock(); int64_t field[MAX]; for (int ix = 0; ix < MAX; ix++) field[ix] = rand.nextInt64(); @@ -36,11 +36,13 @@ private: if (field[ix] != current) checkEqu(field[ix], current); } + double duration = double(clock() - start) / CLOCKS_PER_SEC; + reUseParam(&duration); } void testNextInt64() { ReCongruentialGenerator rand; const int MAX = 16; - time_t start = time(NULL); + clock_t start = clock(); int64_t field[MAX]; for (int ix = 0; ix < MAX; ix++) field[ix] = rand.nextInt64(ix + 10); @@ -50,7 +52,8 @@ private: if (!(field[ix] >= 0 && field[ix] <= ix + 10)) checkT(false); } - time_t diff = time(NULL) - start; + int diff = (int) (clock() - start); + reUseParam(&diff); } }; extern void testReRandomizer(void); diff --git a/cunit/cuReTraverser.cpp b/cunit/cuReTraverser.cpp index 379b751..78c0c45 100644 --- a/cunit/cuReTraverser.cpp +++ b/cunit/cuReTraverser.cpp @@ -88,7 +88,7 @@ private: trg.append("copy_x1.txt"); ReByteArray buffer; buffer.ensureSize(5); - ReDirSync::copyFile(src.str(), NULL, trg.str(), buffer, + ReDirSync::copyFile(src.str(), false, NULL, trg.str(), buffer, ReLogger::globalLogger()); checkFileEqu(src.str(), trg.str()); #else diff --git a/cunit/testall.cpp b/cunit/testall.cpp index 0b17811..b400777 100644 --- a/cunit/testall.cpp +++ b/cunit/testall.cpp @@ -62,6 +62,8 @@ void testString() { testReMatcher(); } void testOs() { + void testReFileUtils(); + testReFileUtils(); void testReDirTools(); testReDirTools(); void testReTraverser(); diff --git a/math/ReRandomizer.cpp b/math/ReRandomizer.cpp index 8500e34..62b0aab 100644 --- a/math/ReRandomizer.cpp +++ b/math/ReRandomizer.cpp @@ -56,8 +56,6 @@ ReRandomizer::seed_t ReRandomizer::nearTrueRandom() { #if defined __linux__ int fh = open("/dev/urandom", O_RDONLY); char buffer[sizeof(seed_t)]; - size_t length = 0; - if (read(fh, buffer, sizeof buffer) > 0) rc ^= *(seed_t*) buffer; close(fh); @@ -131,7 +129,7 @@ int64_t ReRandomizer::nextInt64(int64_t maxValue, int64_t minValue) { } if (s_trace) { static int count = 0; - printf("%c %16llx ", count++ % 4 == 0 ? '\n' : ' ', rc); + printf("%c %16llx ", count++ % 4 == 0 ? '\n' : ' ', (long long) rc); } return rc; } @@ -297,7 +295,7 @@ void ReCongruentialGenerator::setIncrement(seed_t increment) { void ReCongruentialGenerator::setSeed(seed_t seed) { m_seed = m_lastSetSeed = seed; if (s_trace) - printf(" Seed: %llx ", seed); + printf(" Seed: %llx ", (long long) seed); } /** @brief Returns the next 64 bit pseudo random number. diff --git a/net/ReTCP.cpp b/net/ReTCP.cpp index 8abebb5..1b1059c 100644 --- a/net/ReTCP.cpp +++ b/net/ReTCP.cpp @@ -141,12 +141,16 @@ void ReSocketAddress::setAddress(const char* ip, int port) { * * @param logger logger for the error handling */ +#ifdef __WIN32__ #pragma warning( push ) #pragma warning( disable : 4355 ) +#endif ReTCPClient::ReTCPClient(ReLogger* logger) : ReTCPConnection(-1, this), m_logger(logger) { +#ifdef __WIN32__ #pragma warning( pop ) +#endif } /** * Destructor. @@ -392,13 +396,17 @@ void ReTCPConnection::send(const char* command, const char* data, int length) { * @param id an identifier for logging * @param logger the logger for error handling */ +#ifdef __WIN32__ #pragma warning( push ) #pragma warning( disable : 4355 ) +#endif ReTCPServerConnection::ReTCPServerConnection(int id, ReTCPServer* server) : ReTCPConnection(id, this), ReThread(true), m_server(server) { +#ifdef __WIN32__ #pragma warning( pop ) +#endif } /** @@ -454,8 +462,10 @@ void ReTCPServerConnection::run() { * @param logger the logger for error handling * @param maxConnections maximal count of threads handling a connection */ +#ifdef __WIN32__ #pragma warning( push ) #pragma warning( disable : 4355 ) +#endif ReTCPServer::ReTCPServer(int port, class ReNetCommandHandler& commandHandler, ReLogger* logger, int maxConnections) : ReTCPConnection(0, this), @@ -463,7 +473,9 @@ ReTCPServer::ReTCPServer(int port, class ReNetCommandHandler& commandHandler, m_handler(commandHandler), m_logger(logger), m_threadPool(maxConnections, logger) { +#ifdef __WIN32__ #pragma warning( pop ) +#endif m_port = port; } @@ -634,12 +646,16 @@ void ReNetCommandHandler::addHandler(ReNetCommandHandler* handler) { * @param port port for listening * @param logger logger for error handling */ +#ifdef __WIN32__ #pragma warning( push ) #pragma warning( disable : 4355 ) +#endif ReTCPEchoServer::ReTCPEchoServer(int port, ReLogger* logger) : ReTCPServer(port, *this, logger), ReNetCommandHandler() { +#ifdef __WIN32__ #pragma warning( pop ) +#endif } /** * Destructor. @@ -669,7 +685,7 @@ ReNetCommandHandler::ProcessingState ReTCPEchoServer::handleNetCommand( connection->send("Strlen ", m_toSend.str(), m_toSend.length()); rc = PS_PROCESSED; } else if (command.equals("filldata")) { - int length = atol(data.str()); + size_t length = (size_t) atol(data.str()); if (m_toSend.length() != length || !m_toSend.startsWith("xxxxx")) m_toSend.setLength(0).appendChar('x', length); connection->send("Filldata", m_toSend.str(), m_toSend.length()); diff --git a/os/ReDirTools.cpp b/os/ReDirTools.cpp index aef9a3b..6757543 100644 --- a/os/ReDirTools.cpp +++ b/os/ReDirTools.cpp @@ -40,6 +40,9 @@ enum LOCATION_DIRTOOL { LC_DELETE_1, // 50123 LC_DELETE_SUPERFLUOUS_1, // 50124 LC_SYNC_1, // 50125 + LC_COPY_FILE_7, // 50126 + LC_ADAPT_PROPERTIES_1, // 50127 + LC_ADAPT_PROPERTIES_2, // 50128 }; const char* ReDirTools::m_version = "2015.03.22"; ReLogger* ReDirTools::m_logger = NULL; @@ -401,7 +404,7 @@ ReFileTime_t ReDirOptions::checkDate(const char* value) { rcTime += time(NULL); } ReFileTime_t rc; - ReDirStatus_t::timeToFiletime(rcTime, rc); + ReFileUtils::timeToFiletime(rcTime, rc); return rc; } /** @@ -672,7 +675,7 @@ void ReDirOptions::setFilterFromProgramArgs(ReDirEntryFilter& filter) { m_pathPatterns.set(buffer.str()); filter.m_pathPatterns = &m_pathPatterns; } - if ((m_interval = m_programArgs.getInt("trace") * CLOCKS_PER_SEC) != 0){ + if ((m_interval = m_programArgs.getInt("trace") * CLOCKS_PER_SEC) != 0) { m_triggerCount = 10; } if (m_programArgs.getString("output", buffer)[0] != '\0') { @@ -686,8 +689,8 @@ void ReDirOptions::setFilterFromProgramArgs(ReDirEntryFilter& filter) { int length; if (buffer.empty()) m_verboseLevel = V_NORMAL; - else if ( (length = ReStringUtils::lengthOfUnsigned(buffer.str(), - buffer.length(), &nValue)) == 0 || length != buffer.length()) + else if ((length = ReStringUtils::lengthOfUnsigned(buffer.str(), + buffer.length(), &nValue)) == 0 || length != (int) buffer.length()) help("not a verbose level (0..5): ", buffer.str()); else m_verboseLevel = VerboseLevel(min(V_DEBUG, nValue)); @@ -721,7 +724,6 @@ ReTool::ReTool(const char* usage[], const char* example[], int minArguments, m_traverser(NULL, this, logger), m_filter(), m_start(clock()), - //m_statInfo(), m_logger(logger) { #pragma warning( pop ) } @@ -770,7 +772,7 @@ void ReTool::run(int argc, const char** argv) { m_programArgs.init(argc, argv); if (m_programArgs.argCount() < m_minArguments) m_programArgs.help(i18n("too few arguments"), false, stdout); - if (m_hasStandardArgs){ + if (m_hasStandardArgs) { checkStandardFilterOptions(); setFilterFromProgramArgs(m_filter); } @@ -795,20 +797,23 @@ void ReTool::processFileArguments() { int max = m_programArgs.argCount() - m_reservedLast; // Test whether the arguments are files or directories: ReByteArray arg; + struct stat info; for (int ii = m_reservedFirst; ii < max; ii++) { arg = m_programArgs.arg(ii); if (!exists(arg) != 0) m_programArgs.help( - ReByteArray(i18n("not a file or a directory: ")).append(arg) - .str(), false, stderr); + ReByteArray(i18n("not a file or a directory: ")).append(arg).str(), + false, stderr); } // process the files: for (int ii = m_reservedFirst; ii < max; ii++) { - const char* arg = m_programArgs.arg(ii); - if (S_ISDIR(m_statInfo.st_mode)) - processTree(arg); - else - processSingleFile(arg); + ReByteArray arg(m_programArgs.arg(ii)); + if (exists(arg, &info)) { + if (S_ISDIR(info.st_mode)) + processTree(arg.str()); + else + processSingleFile(arg.str()); + } } } /** @@ -900,8 +905,8 @@ void ReTool::printSummary(const char* prefix) { duration == 0.0 ? 0.0 : (m_files + m_directories) / duration; line.append(rate, " %.1f").append(i18n("/sec"), -1); m_traverser.statisticAsString(line2); - line2.appendChar(' ').appendTime(int(duration * 1000)).append(" ", 1).append( - i18n("sec")); + line2.appendChar(' ').appendTime(int(duration * 1000)).append(" ", 1) + .append(i18n("sec")); fprintf(m_output, "%s=== filtered: %s\n", prefix == NULL ? "" : prefix, line.str()); fprintf(m_output, "%s=== total: %s\n", prefix == NULL ? "" : prefix, @@ -1190,7 +1195,7 @@ void ReDirChecksum::buildStorage(const char* path, const char* storageFile) { if (fp == NULL) { m_logger->sayF(LOG_ERROR | CAT_FILE, LC_BUILD_DIRECTORY_1, i18n("cannot open file: $1 (errno: $2)")).arg(storageFile).arg( - errno).end(); + errno).end(); } else { int level; @@ -1449,8 +1454,8 @@ ReDigest& ReDirChecksum::calculateChecksum(const char* name, ReDigest& digest, if (fp == NULL) { if (logger != NULL) logger->sayF(LOG_ERROR | CAT_FILE, LC_CALCULATE_CHECKSUM_1, - i18n("cannot open file: $1 (errno: $2)")).arg(name).arg( - errno).end(); + i18n("cannot open file: $1 (errno: $2)")).arg(name).arg(errno) + .end(); } else { ReMD5 digest; size_t readBytes; @@ -1911,11 +1916,11 @@ void ReDirTouch::processFile(ReDirStatus_t* entry) { if (m_verboseLevel == V_NORMAL) { if (countTimes == 2) fprintf(m_output, "%s | %s | %s\n", - ReDirStatus_t::filetimeToString(&modified, bufferTime), - ReDirStatus_t::filetimeToString(&accessed, bufferTime2), + ReFileUtils::filetimeToString(&modified, bufferTime).str(), + ReFileUtils::filetimeToString(&accessed, bufferTime2).str(), name); else { - ReDirStatus_t::filetimeToString( + ReFileUtils::filetimeToString( filetimeIsUndefined(m_modified) ? &accessed : &modified, bufferTime); fprintf(m_output, "%s %s\n", bufferTime.str(), name); @@ -1925,20 +1930,20 @@ void ReDirTouch::processFile(ReDirStatus_t* entry) { ReByteArray bufferTime4; if (countTimes == 2) fprintf(m_output, "%s -> %s | %s -> %s | %s\n", - ReDirStatus_t::filetimeToString(entry->modified(), - bufferTime), - ReDirStatus_t::filetimeToString(&modified, bufferTime2), - ReDirStatus_t::filetimeToString(entry->accessed(), - bufferTime3), - ReDirStatus_t::filetimeToString(&accessed, bufferTime4), + ReFileUtils::filetimeToString(entry->modified(), bufferTime) + .str(), + ReFileUtils::filetimeToString(&modified, bufferTime2).str(), + ReFileUtils::filetimeToString(entry->accessed(), + bufferTime3).str(), + ReFileUtils::filetimeToString(&accessed, bufferTime4).str(), name); else { - ReDirStatus_t::filetimeToString( + ReFileUtils::filetimeToString( filetimeIsUndefined(m_modified) ? &m_accessed : &m_modified, bufferTime); fprintf(m_output, "%s -> %s %s\n", - ReDirStatus_t::filetimeToString(entry->modified(), - bufferTime2), bufferTime.str(), (char*) name); + ReFileUtils::filetimeToString(entry->modified(), + bufferTime2).str(), bufferTime.str(), (char*) name); } } } @@ -1977,7 +1982,7 @@ ReErrNo_t ReDirTouch::touch(const char* filename, const ReFileTime_t& modified, if (rc != 0 && logger != NULL) logger->sayF(LOG_ERROR | CAT_FILE, LC_TOUCH_1, i18n("cannot change filetime: $1 (errno: $2)")).arg(filename).arg( - errno).end(); + errno).end(); return rc; } @@ -2049,15 +2054,17 @@ ReDirSync::ReDirSync(ReLogger* logger) : i18n( "deletes the files/folders of the target directory not existing in the source directory.\n" "If a time expression is given only files will be deleted if they are older than this.\n" - "a time expression is a date, a time, a date/time or a relative time.\n" - "relative times are a number and a unit.\n" + "a time expression is a date, a time, a date/time or a relative time.\n" + "relative times are a number and a unit.\n" "units: m(inutes) h(hours), d(days). Default: m(inutes)\n" "examples: -E7d --delete=30d -E24h -E2009.3.2/12:00 -E1999.01.01"), 'E', "delete", true, NULL); m_programArgs.addBool("deletebefore", - i18n("deletes the superfluous files before copying.\n" - "This needs fewer space but it is more dangerous:\n" - "you have no time if the target is wrong: all files will be deleted"), 'B', "delete-before", false); + i18n( + "deletes the superfluous files before copying.\n" + "This needs fewer space but it is more dangerous:\n" + "you have no time if the target is wrong: all files will be deleted"), + 'B', "delete-before", false); m_programArgs.addString("editor", i18n( "this editor will be started with a file containing a summary message (signals the end)"), @@ -2079,13 +2086,17 @@ ReDirSync::ReDirSync(ReLogger* logger) : /** * Copies a file. * - * @param entry the source file info - * @param target the name of the target file + * @param dry logs the copy but do nothing (simulating mode) + * @param entry the source file info + * @param target the name of the target file + * @param targetName used for the logging + * @param trgProps NULL or the properties of the (existing) target file */ -void ReDirSync::copyFile(ReDirStatus_t* entry, const char* target) { +void ReDirSync::copyFile(bool dry, ReDirStatus_t* entry, const char* target, + const char* targetName, ReFileProperties_t* trgProps) { ReFileProperties_t* props; #ifdef __linux__ - props = &entry->m_status; + props = entry->getStatus(); #else ReFileProperties_t properties; properties.m_modified = *entry->modified(); @@ -2093,14 +2104,100 @@ void ReDirSync::copyFile(ReDirStatus_t* entry, const char* target) { properties.m_size = entry->fileSize(); props = &properties; #endif - copyFile(entry->fullName(), props, target, m_buffer, - ReLogger::globalLogger()); + if (dry + || copyFile(entry->fullName(), entry->isLink(), props, target, m_buffer, + ReLogger::globalLogger())) { + if (m_verboseLevel >= V_NORMAL) { + if (entry->isLink()) + //@ or use targetRelativePath + fprintf(m_output, "%c %s (symbolic link)%s\n", + trgProps != NULL ? '!' : '&', targetName, + dry ? " would be copied" : ""); + else + fprintf(m_output, "%c%s%s\n", trgProps != NULL ? '!' : '+', + targetName, dry ? " would be copied" : ""); + } + adaptProperties(entry->fullName(), props, target, NULL); + } } +/** + * Tries to make the target properties are the same as the source properties. + * + * @param source the name of the source file/directory + * @param srcProps the source properties + * @param target the name of the target file/directory + * @param trgProps the target properties. If NULL the file is new + * and all properties must be set + * @param verbose verbose level: determines the output messages + * @param prefix the first char of the output line + * @param output NULL or the output stream, e.g. stdout + * @param logger NULL or the logger for error messages + * @return true: success
+ * false: error occurred + */ +bool ReDirSync::adaptProperties(const char* source, + ReFileProperties_t* srcProps, const char* target, + ReFileProperties_t* trgProps, VerboseLevel verbose, char prefix, + FILE* output, ReLogger* logger) { + bool rc = true; +#ifdef __linux__ + ReByteArray changed; + if (trgProps == NULL || srcProps->st_uid != trgProps->st_uid + || srcProps->st_gid != trgProps->st_gid) { + if (chown(target, srcProps->st_uid, srcProps->st_gid) != 0) { + rc = false; + if (logger != NULL) + logger->sayF(LOG_ERROR | CAT_FILE, LC_ADAPT_PROPERTIES_1, + i18n("could not change owner/group: $1 (errno: $2)")).arg( + target).arg(errno).end(); + } else if (trgProps != NULL && verbose >= V_NORMAL) { + if (verbose >= V_CHATTER) { + if (srcProps->st_uid != trgProps->st_uid) + changed.append(" UID: ").appendInt(srcProps->st_uid).append( + "->").appendInt(trgProps->st_uid); + if (srcProps->st_gid != trgProps->st_gid) + changed.append(" GID: ").appendInt(srcProps->st_gid).append( + "->").appendInt(trgProps->st_gid); + } else { + if (srcProps->st_uid != trgProps->st_uid) + changed.append(" UID"); + if (srcProps->st_gid != trgProps->st_gid) + changed.append(" GID"); + } + } + } + if (trgProps == NULL + || (srcProps->st_mode & ALLPERMS) != (trgProps->st_mode & ALLPERMS)) { + int mode = srcProps->st_mode & ALLPERMS; + if (chmod(target, mode) != 0 && logger != NULL) { + rc = false; + if (logger != NULL) + logger->sayF(LOG_ERROR | CAT_FILE, LC_ADAPT_PROPERTIES_2, + i18n("could not change permissions: $1 (errno: $2)")).arg( + target).arg(errno).end(); + + } else if (verbose >= V_CHATTER) { + changed.append(" rights: ").appendInt( + (trgProps->st_mode & ALLPERMS), "%o").append("->").appendInt( + mode); + } else if (verbose == V_NORMAL) { + changed.append(" rights"); + } + } + if (trgProps != NULL && verbose >= V_NORMAL && output != NULL) { + fprintf(output, "%c%s%s\n", prefix, source, changed.str()); + } +#else +#error "not implemented" +#endif + return rc; +} /** * Copies a file. * * @param source the source file name + * @param isLink true: the source file is a symbolic link * @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
@@ -2109,8 +2206,9 @@ void ReDirSync::copyFile(ReDirStatus_t* entry, const char* target) { * @return truesuccess
* falseerror occurred */ -bool ReDirSync::copyFile(const char* source, ReFileProperties_t* properties, - const char* target, ReByteArray& buffer, ReLogger* logger) { +bool ReDirSync::copyFile(const char* source, bool isLink, + ReFileProperties_t* properties, const char* target, ReByteArray& buffer, + ReLogger* logger) { bool rc = false; #ifdef __linux__ struct stat info; @@ -2121,52 +2219,72 @@ bool ReDirSync::copyFile(const char* source, ReFileProperties_t* properties, if (logger != NULL) logger->sayF(LOG_ERROR | CAT_FILE, LC_COPY_FILE_1, i18n("could not find: $1 (errno: $2)")).arg(source).arg( - errno).end(); + 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 { - ReFileSize_t size = - properties == NULL ? 0x7fffffff : properties->st_size; - FILE* fpTarget = fopen(target, "w"); - if (fpTarget == NULL) { + if (isLink) { + char reference[512]; + int referenceLength = 0; + if ((referenceLength = readlink(source, reference, sizeof reference)) + < 0) { if (logger != NULL) + logger->sayF(LOG_ERROR | CAT_FILE, LC_COPY_FILE_2, + i18n("cannot read link $1 (errno: $2)")).arg(source).arg( + errno).end(); + } else { + reference[referenceLength] = '\0'; + unlink(target); + if (symlink(reference, target) != 0 && logger != NULL) logger->sayF(LOG_ERROR | CAT_FILE, LC_COPY_FILE_3, - i18n("cannot open $1 (errno: $2)")).arg(target).arg(errno) + i18n("cannot create link $1 [$2] (errno: $3)")).arg(target) + .arg(reference).arg(errno).end(); + } + } else { + FILE* fpSource = fopen(source, "rb"); + if (fpSource == NULL) { + if (logger != NULL) + logger->sayF(LOG_ERROR | CAT_FILE, LC_COPY_FILE_4, + i18n("cannot open $1 (errno: $2)")).arg(source).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; + ReFileSize_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_5, + 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_6, + 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_7, + i18n("cannot write $1 [$2] (errno: $3)")).arg( + target).arg(written).arg(errno).end(); + break; + } + size -= blockSize; } - size -= blockSize; + rc = size == 0ll; + fclose(fpTarget); + if (properties != NULL) + setProperties(target, properties, logger); } - rc = size == 0ll; - fclose(fpTarget); - if (properties != NULL) - setProperties(target, properties, logger); + fclose(fpSource); } - fclose(fpSource); } #elif defined __WIN32__ BOOL cancel = false; @@ -2184,41 +2302,44 @@ bool ReDirSync::copyFile(const char* source, ReFileProperties_t* properties, * @param target the target directory * @param deleteTime UNDEF or a time: only files older than this will be deleted */ -void ReDirSync::deleteSuperfluous(const ReByteArray& source, const ReByteArray& target, - const ReFileTime_t& deleteTime){ +void ReDirSync::deleteSuperfluous(const ReByteArray& source, + const ReByteArray& target, const ReFileTime_t& deleteTime) { ReTraceUnit& tracer = *this; ReDirectory trg(target.str()); if (trg.findFirst(ReDirectory::ALL_FILES, false)) { ReByteArray src, trg2; struct stat info; - ReFileTime_t modified; bool ignoreTime = filetimeIsUndefined(deleteTime); bool dry = m_programArgs.getBool("dry"); do { - if (strcmp(".", trg.currentNode()) == 0 || strcmp("..", trg.currentNode()) == 0) + if (strcmp(".", trg.currentNode()) == 0 + || strcmp("..", trg.currentNode()) == 0) continue; if (tracer.isCountTriggered() && tracer.isTimeTriggered()) tracer.trace(trg.currentFull().str()); src.set(source).append(trg.currentNode()); - if (lstat(src.str(), &info) == 0){ - if (S_ISDIR(info.st_mode)){ + if (lstat(src.str(), &info) == 0) { + if (S_ISDIR(info.st_mode)) { trg2.set(target).append(trg.currentNode()); deleteSuperfluous(src, trg2, deleteTime); } - } else if (ignoreTime || trg.currentModified() >= deleteTime){ - if (trg.currentIsDir()){ - if (! dry) + } else if (ignoreTime || trg.currentModified() >= deleteTime) { + if (trg.currentIsDir()) { + if (!dry) ReDirectory::deleteTree(trg.currentFull().str(), true); if (m_verboseLevel >= V_NORMAL) - fprintf(m_output, "-%s%s\n", trg.currentFull().str(), dry ? " would be deleted (dir)" : ""); + fprintf(m_output, "-%s%s\n", trg.currentFull().str(), + dry ? " would be deleted (dir)" : ""); } else { if (unlink(trg.currentFull().str()) != 0) - m_logger->sayF(LOG_ERROR | CAT_FILE, LC_DELETE_SUPERFLUOUS_1, - i18n("cannot delete file: $1 (errno: $2)")).arg(trg.currentFull()).arg( - errno).end(); + m_logger->sayF(LOG_ERROR | CAT_FILE, + LC_DELETE_SUPERFLUOUS_1, + i18n("cannot delete file: $1 (errno: $2)")).arg( + trg.currentFull()).arg(errno).end(); else if (m_verboseLevel >= V_NORMAL) - fprintf(m_output, "-%s%s\n", trg.currentFull().str(), dry ? " would be deleted" : ""); + fprintf(m_output, "-%s%s\n", trg.currentFull().str(), + dry ? " would be deleted" : ""); } } } while (trg.findNext()); @@ -2323,8 +2444,8 @@ bool ReDirSync::makeDirectory(const char* directory, int minLength, return rc; } -static void printStatus(FILE* fp, double duration, int files, int sumSizes, int treeDirs, - int treeFiles, int64_t treeSumSizes){ +static void printStatus(FILE* fp, double duration, int files, int sumSizes, + int treeDirs, int treeFiles, int64_t treeSumSizes) { fprintf(fp, i18n( "=== copied: %02d:%02d sec %7d file(s) %12.6f MByte (%.3f MB/sec).\n" @@ -2342,9 +2463,10 @@ void ReDirSync::doIt() { ReByteArray buffer; ReByteArray target(m_programArgs.arg(m_programArgs.argCount() - 1)); target.removeLastChar(OS_SEPARATOR_CHAR); - if (!exists(target)) + struct stat info; + if (!exists(target, &info)) help(i18n("target does not exist: "), target.str()); - else if (!S_ISDIR(m_statInfo.st_mode)) + 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"); @@ -2356,9 +2478,9 @@ void ReDirSync::doIt() { ReFileTime_t deleteDate; setFiletimeUndef(deleteDate); bool deleteBefore = m_programArgs.getBool("deletebefore"); - if (m_programArgs.getString("delete", buffer) != NULL){ + if (m_programArgs.getString("delete", buffer) != NULL) { deleteTarget = true; - if (! buffer.empty()) + if (!buffer.empty()) deleteDate = checkDate(buffer.str()); } @@ -2376,7 +2498,7 @@ void ReDirSync::doIt() { source.reduceLength(); if (!exists(source)) help(i18n("source does not exist: $1"), source.str()); - else if (!S_ISDIR(m_statInfo.st_mode)) + 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: @@ -2394,18 +2516,19 @@ void ReDirSync::doIt() { int level; ReDirStatus_t* entry; ReByteArray line; + struct stat targetInfo; while ((entry = m_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 (!exists(target) && ! dry) + if (!exists(target) && !dry) makeDirWithParents(target, ixTargetRelative, m_traverser); targetFile.set(target).append(entry->node(), -1); const char* targetRelativePath = targetFile.str() + ixTargetRelative + 1; - bool targetExists = exists(targetFile); + bool targetExists = exists(targetFile, &targetInfo); if (!targetExists && mustExist) { if (m_verboseLevel == V_CHATTER) fprintf(m_output, "-ignored: %s does not exist\n", @@ -2419,7 +2542,7 @@ void ReDirSync::doIt() { targetRelativePath); continue; } - if (ignoreDate && entry->fileSize() == m_statInfo.st_size) { + if (ignoreDate && entry->fileSize() == targetInfo.st_size) { if (m_verboseLevel >= V_CHATTER) fprintf(m_output, "_ignored: %s same size\n", targetRelativePath); @@ -2427,8 +2550,8 @@ void ReDirSync::doIt() { } // target younger than source? int diff = int( - m_statInfo.st_mtime - - entry->filetimeToTime(entry->modified())); + targetInfo.st_mtime + - ReFileUtils::filetimeToTime(entry->modified())); if (!ignoreDate && (diff <= maxFileTimeDiff || diff == 3600)) { if (m_verboseLevel >= V_CHATTER) fprintf(m_output, "=ignored: %s same time\n", @@ -2438,13 +2561,10 @@ void ReDirSync::doIt() { } files++; sumSizes += entry->fileSize(); - if (m_verboseLevel >= V_NORMAL) - fprintf(m_output, "%c%s%s\n", targetExists ? '!' : '+', - targetRelativePath, dry ? " would be copied" : ""); - if (!dry) - copyFile(entry, targetFile.str()); + copyFile(dry, entry, targetFile.str(), targetRelativePath, + &targetInfo); } - if (deleteTarget && ! deleteBefore) + if (deleteTarget && !deleteBefore) deleteSuperfluous(source, target, deleteDate); treeFiles += m_traverser.files(); treeDirs += m_traverser.directories(); @@ -2452,20 +2572,21 @@ void ReDirSync::doIt() { } double duration = double((clock() - m_start)) / CLOCKS_PER_SEC; if (m_verboseLevel >= V_SUMMARY) { - printStatus(m_output, duration, files, sumSizes, treeDirs, - treeFiles, treeSumSizes); + printStatus(m_output, duration, files, sumSizes, treeDirs, treeFiles, + treeSumSizes); } - if (m_programArgs.getString("editor", buffer) != NULL){ + if (m_programArgs.getString("editor", buffer) != NULL) { ReByteArray tempFile = ReFileUtils::tempDir("redirtool.status."); tempFile.appendInt(time(NULL), "%x").append(".txt"); FILE* fp = fopen(tempFile.str(), "w"); if (fp == NULL) m_logger->sayF(LOG_ERROR | CAT_FILE, LC_SYNC_1, - i18n("cannot open status file ($1): $2")).arg(errno).arg(tempFile).end(); + i18n("cannot open status file ($1): $2")).arg(errno).arg( + tempFile).end(); else { fprintf(fp, i18n("backup finished!\n\n")); - printStatus(fp, duration, files, sumSizes, treeDirs, - treeFiles, treeSumSizes); + printStatus(fp, duration, files, sumSizes, treeDirs, treeFiles, + treeSumSizes); fclose(fp); buffer.insert(0, "\"", -1); buffer.append("\" ").append(tempFile); @@ -2541,7 +2662,7 @@ void ReDirTCP::runOneThreadClient(const char* ip, int port, int rounds, int interval, int bufferSize, bool upload) { ReTCPClient client(m_logger); if (client.connect(ip, port)) { - time_t start = time(NULL); + clock_t start = clock(); const char* command = upload ? "strlen" : "filldata"; ReByteArray message; if (upload) @@ -2550,7 +2671,7 @@ void ReDirTCP::runOneThreadClient(const char* ip, int port, int rounds, message.appendInt(bufferSize); time_t lastPrint = start; int64_t size = 0; - int duration = 0; + double duration = 0; ReByteArray answer, data; client.setLogSendReceive(false); int64_t sizeCurrent = 0; @@ -2571,10 +2692,10 @@ void ReDirTCP::runOneThreadClient(const char* ip, int port, int rounds, sizeCurrent = 0; } } - duration = int(time(NULL) - start); - if (duration == 0) - duration = 1; - printf("%2d: %9.3f MiByte %8.3f kiByte %s/sec %s\n", rounds, + duration = int(clock() - start) / CLOCKS_PER_SEC; + if (duration == 0.0) + duration = 0.001; + printf("%2d: %9.3f MiByte %8.3f kiByte/sec %s\n", rounds, size / 1024.0 / 1024, (double) size / duration / 1024, upload ? "up" : "down"); diff --git a/os/ReDirTools.hpp b/os/ReDirTools.hpp index a792ec6..f50b218 100644 --- a/os/ReDirTools.hpp +++ b/os/ReDirTools.hpp @@ -102,21 +102,25 @@ protected: virtual void processTree(const char* filename); /** Tests whether a file or directory exists: @param name can be changed and recovered! + @param props NULL or the properties of the file @return true: a file with the given name exists */ - inline bool exists(ReByteArray& name) { + inline bool exists(ReByteArray& name, struct stat* props = NULL) { + struct stat info; + if (props == NULL) + props = &info; #if defined __linux__ // linux ignores a trailing slash: - return stat(name.str(), &m_statInfo) == 0; + return stat(name.str(), props) == 0; #elif defined __WIN32__ int ix = name.length() - 1; if (ix >= 0 && name.str()[ix] != OS_SEPARATOR_CHAR) - ix = -1; + ix = -1; else - name.setLength(ix); - bool rc = stat(name.str(), &m_statInfo) == 0; + name.setLength(ix); + bool rc = stat(name.str(), props) == 0; if (ix >= 0) - name.appendChar(OS_SEPARATOR_CHAR); + name.appendChar(OS_SEPARATOR_CHAR); return rc; #endif } @@ -129,7 +133,6 @@ protected: ReTraverser m_traverser; ReDirEntryFilter m_filter; clock_t m_start; - struct stat m_statInfo; ReLogger* m_logger; }; @@ -269,14 +272,20 @@ public: ReDirSync(ReLogger* logger); protected: virtual void doIt(); - void copyFile(ReDirStatus_t* entry, const char* target); + void copyFile(bool dry, ReDirStatus_t* entry, const char* target, + const char* targetRelative, ReFileProperties_t* trgProps); void deleteSuperfluous(const ReByteArray& source, const ReByteArray& target, - const ReFileTime_t& deleteTime); + const ReFileTime_t& deleteTime); void makeDirWithParents(ReByteArray& path, int minWidth, ReTraverser& traverser); public: - static bool copyFile(const char* source, ReFileProperties_t* properties, - const char* target, ReByteArray& buffer, ReLogger* logger = NULL); + static bool adaptProperties(const char* source, + ReFileProperties_t* srcProps, const char* target, struct stat* trgProps, + VerboseLevel verbose = V_QUIET, char prefix = ':', FILE* output = NULL, + ReLogger* logger = NULL); + static bool copyFile(const char* source, bool isLink, + ReFileProperties_t* properties, const char* target, ReByteArray& buffer, + ReLogger* logger = NULL); static bool makeDirectory(const char* directory, int minLength, ReFileProperties_t* properties, ReLogger* logger = NULL); static bool setProperties(const char* fullName, diff --git a/os/ReFileUtils.cpp b/os/ReFileUtils.cpp index 97c979b..737d37c 100644 --- a/os/ReFileUtils.cpp +++ b/os/ReFileUtils.cpp @@ -8,49 +8,203 @@ * You also can use this license: http://www.wtfpl.net * The latest sources: https://github.com/republib */ -#include "base/rebase.hpp" -#include "os/reos.hpp" - -/** - * Returns the name of a subdirectory in the temporary directory. - * - * If the directory does not exist it will be created. - * - * @param node the name of the subdirectory - * @return the full name of the subdirectory - */ -ReByteArray ReFileUtils::tempDir(const char* node){ - ReByteArray rc; - if (getenv("TMP") != NULL) { - rc = getenv("TMP"); - } else if (getenv("TEMP")) { - rc = getenv("TEMP"); - } else { -#if defined __linux__ - rc = "/tmp/"; -#elif defined __WIN32__ - rc = "c:\\temp"; -#endif - } - if (node != NULL){ - rc.ensureLastChar(OS_SEPARATOR_CHAR); - rc.append(node); - _mkdir(rc.str(), ALLPERMS); - } - return rc; -} -/** - * Returns the name of a file in a subdirectory in the temporary directory. - * - * If the directory does not exist it will be created. - * - * @param node the name of the subdirectory - * @param node the name of the subdirectory - * @param the full name of the subdirectory - */ -ReByteArray ReFileUtils::tempFile(const char* node, const char* subdir){ - ReByteArray rc = tempDir(subdir); - rc.ensureLastChar(OS_SEPARATOR_CHAR); - rc.append(node); - return rc; -} +#include "base/rebase.hpp" +#include "os/reos.hpp" + +enum LOCATION_DIRTOOL { + LC_SET_TIMES_1 = LOC_FIRST_OF(LOC_FILEUTILS), // 50101 +}; + +/** + * Converts a filetime to a string. + * + * @param time the filetime to convert + * @param buffer OUT: the buffer for the string + * @return buffer.str(), e.g. "2014.01.07 02:59:43" + */ +ReByteArray& ReFileUtils::filetimeToString(const ReFileTime_t* time, + ReByteArray& buffer) { + time_t time1 = filetimeToTime(time); + struct tm* time2 = localtime(&time1); + buffer.setLength(4 + 2 * 2 + 2 * 2 + 1 + 3 * 2 + 2 * 1); + strftime(buffer.buffer(), buffer.length(), "%Y.%m.%d %H:%M:%S", time2); + return buffer; +} + +/** + * Converts a filetime to a unix time (seconds since the Epoche). + * + * @param filetime the filetime to convert + * @return the count of seconds since 1.1.1970 + */ +time_t ReFileUtils::filetimeToTime(const ReFileTime_t* filetime) { +#ifdef __linux__ + return filetime->tv_sec; +#elif defined __WIN32__ + // 64-bit arithmetic: + LARGE_INTEGER date, adjust; + date.HighPart = filetime->dwHighDateTime; + date.LowPart = filetime->dwLowDateTime; + // 100-nanoseconds = milliseconds * 10000 + adjust.QuadPart = 11644473600000 * 10000; + // removes the diff between 1970 and 1601 + date.QuadPart -= adjust.QuadPart; + // converts back from 100-nanoseconds to seconds + time_t rc = (time_t) (date.QuadPart / 10000000); +#if defined __WIN32__ + static int s_diffTime = 0x7fffffff; + if (s_diffTime == 0x7fffffff) { + s_diffTime = 0; + ReByteArray tempFile = ReFileUtils::tempFile("$$redir$$.tmp", NULL); + const char* filename = tempFile.str(); + FILE* fp = fopen(filename, "w"); + if (fp != NULL) { + struct stat info; + int rcStat = stat(filename, &info); + fclose(fp); + if (rcStat == 0) { + WIN32_FIND_DATAA data; + HANDLE handle = FindFirstFile(filename, &data); + if (handle != INVALID_HANDLE_VALUE) { + time_t other = filetimeToTime(&data.ftLastWriteTime); + s_diffTime = info.st_mtime - other; + FindClose(handle); + } + } + } + } + rc += s_diffTime; +#endif + return rc; +#endif +} + +/** + * Reads the content of a file into a buffer. + * + * @param filename name of the file to read + * @param buffer OUT: the buffer for the content + * @return buffer (for chaining) + */ +ReByteArray& ReFileUtils::readString(const char* filename, + ReByteArray& buffer) { + struct stat info; + buffer.setLength(0); + if (stat(filename, &info) == 0 && !S_ISDIR(info.st_mode)) { + FILE* fp = fopen(filename, "rb"); + if (fp != NULL) { + int length = info.st_size; + buffer.setLength(length); + int readBytes; + readBytes = fread((void*) buffer.buffer(), 1, length, fp); + buffer.setLength(max(readBytes, 0)); + fclose(fp); + } + } + return buffer; +} +/** + * Sets the file time/times. + * + * @param name the filename + * @param modified the time of modification + * @param accessed NULL or the time of the last access + */ +void ReFileUtils::setTimes(const char* name, ReFileTime_t modified, + ReFileTime_t* accessed, ReLogger* logger) { +#if defined linux + struct timeval vals[2]; + if (accessed == NULL) { + vals[0].tv_sec = time(NULL); + vals[0].tv_usec = 0; + } else { + vals[0].tv_sec = accessed->tv_sec; + vals[0].tv_usec = accessed->tv_nsec / 1000; + } + vals[1].tv_sec = modified.tv_sec; + vals[1].tv_usec = modified.tv_nsec / 1000; + if (utimes(name, vals) == 0 && logger != NULL) + logger->sayF(LOG_ERROR | CAT_FILE, LC_SET_TIMES_1, + "cannot change times ($1): $2").arg(errno).arg(name).end(); + +#elif defined __WIN32__ +#error "not implemented" +#endif +} + +/** + * Returns the name of a subdirectory in the temporary directory. + * + * If the directory does not exist it will be created. + * + * @param node NULL or the name of the subdirectory. If NULL the OS specific + * temporary directory will returned + * @return the full name of the subdirectory + */ +ReByteArray ReFileUtils::tempDir(const char* node) { + ReByteArray rc; + if (getenv("TMP") != NULL) { + rc = getenv("TMP"); + } else if (getenv("TEMP")) { + rc = getenv("TEMP"); + } else { +#if defined __linux__ + rc = "/tmp/"; +#elif defined __WIN32__ + rc = "c:\\temp"; +#endif + } + if (node != NULL) { + rc.ensureLastChar(OS_SEPARATOR_CHAR); + rc.append(node); + _mkdir(rc.str(), ALLPERMS); + } + return rc; +} +/** + * Returns the name of a file in a subdirectory in the temporary directory. + * + * If the directory does not exist it will be created. + * + * @param node the name of the subdirectory + * @param node the name of the subdirectory + * @param the full name of the subdirectory + */ +ReByteArray ReFileUtils::tempFile(const char* node, const char* subdir) { + ReByteArray rc = tempDir(subdir); + rc.ensureLastChar(OS_SEPARATOR_CHAR); + rc.append(node); + return rc; +} +/** + * Converts the unix time (time_t) to the file time. + * + * @param time the unix time (secondes since 1.1.1970) + * @param filetime OUT: the OS specific filetime + */ +void ReFileUtils::timeToFiletime(time_t time, ReFileTime_t& filetime) { +#ifdef __linux__ + filetime.tv_sec = time; + filetime.tv_nsec = 0; +#elif defined __WIN32__ + LONGLONG ll = Int32x32To64(time, 10000000) + 116444736000000000; + filetime.dwLowDateTime = (DWORD)ll; + filetime.dwHighDateTime = ll >> 32; +#endif +} + +/** + * Writes a string into a file. + * + * @param filename the name of the file to write + * @param content the string to write + */ +void ReFileUtils::writeString(const char* filename, const char* content) { + FILE* fp = fopen(filename, "w"); + if (fp != NULL) { + int length = strlen(content); + int written = fwrite(content, 1, length, fp); + reUseParam(written); + fclose(fp); + } +} diff --git a/os/ReFileUtils.hpp b/os/ReFileUtils.hpp index f4b8e1b..8ac5a44 100644 --- a/os/ReFileUtils.hpp +++ b/os/ReFileUtils.hpp @@ -8,12 +8,20 @@ * You also can use this license: http://www.wtfpl.net * The latest sources: https://github.com/republib */ -#if ! defined REFILE_UTILS_HPP -#define REFILE_UTILS_HPP - -class ReFileUtils { -public: - static ReByteArray tempDir(const char* node); - static ReByteArray tempFile(const char* node, const char* subdir); -}; -#endif \ No newline at end of file +#if ! defined REFILE_UTILS_HPP +#define REFILE_UTILS_HPP + +class ReFileUtils { +public: + static ReByteArray& filetimeToString(const ReFileTime_t* time, + ReByteArray& buffer); + static time_t filetimeToTime(const ReFileTime_t* time); + static ReByteArray& readString(const char* filename, ReByteArray& buffer); + static void setTimes(const char* name, ReFileTime_t modified, + ReFileTime_t* accessed = NULL, ReLogger* logger = NULL); + static ReByteArray tempDir(const char* node = NULL); + static ReByteArray tempFile(const char* node, const char* subdir = NULL); + static void timeToFiletime(time_t time, ReFileTime_t& filetime); + static void writeString(const char* filename, const char* content); +}; +#endif diff --git a/os/ReTraverser.cpp b/os/ReTraverser.cpp index d09ad83..971d565 100644 --- a/os/ReTraverser.cpp +++ b/os/ReTraverser.cpp @@ -88,72 +88,9 @@ ReFileSize_t ReDirStatus_t::fileSize() { * @return buffer.str() (for chaining) */ const char* ReDirStatus_t::filetimeAsString(ReByteArray& buffer) { - return filetimeToString(modified(), buffer); + return ReFileUtils::filetimeToString(modified(), buffer).str(); } -/** - * Converts a filetime to a string. - * - * @param time the filetime to convert - * @param buffer OUT: the buffer for the string - * @return buffer.str(), e.g. "2014.01.07 02:59:43" - */ -const char* ReDirStatus_t::filetimeToString(const ReFileTime_t* time, - ReByteArray& buffer) { - time_t time1 = filetimeToTime(time); - struct tm* time2 = localtime(&time1); - buffer.setLength(4 + 2 * 2 + 2 * 2 + 1 + 3 * 2 + 2 * 1); - strftime(buffer.buffer(), buffer.length(), "%Y.%m.%d %H:%M:%S", time2); - return buffer.str(); -} - -/** - * Converts a filetime to a unix time (seconds since the Epoche). - * - * @param filetime the filetime to convert - * @return the count of seconds since 1.1.1970 - */ -time_t ReDirStatus_t::filetimeToTime(const ReFileTime_t* filetime) { -#ifdef __linux__ - return filetime->tv_sec; -#elif defined __WIN32__ - // 64-bit arithmetic: - LARGE_INTEGER date, adjust; - date.HighPart = filetime->dwHighDateTime; - date.LowPart = filetime->dwLowDateTime; - // 100-nanoseconds = milliseconds * 10000 - adjust.QuadPart = 11644473600000 * 10000; - // removes the diff between 1970 and 1601 - date.QuadPart -= adjust.QuadPart; - // converts back from 100-nanoseconds to seconds - time_t rc = (time_t) (date.QuadPart / 10000000); -#if defined __WIN32__ - static int s_diffTime = 0x7fffffff; - if (s_diffTime == 0x7fffffff){ - s_diffTime = 0; - ReByteArray tempFile = ReFileUtils::tempFile("$$redir$$.tmp", NULL); - const char* filename = tempFile.str(); - FILE* fp = fopen(filename, "w"); - if (fp != NULL){ - struct stat info; - int rcStat = stat(filename, &info); - fclose(fp); - if (rcStat == 0) { - WIN32_FIND_DATAA data; - HANDLE handle = FindFirstFile(filename, &data); - if (handle != INVALID_HANDLE_VALUE){ - time_t other = filetimeToTime(&data.ftLastWriteTime); - s_diffTime = info.st_mtime - other; - FindClose(handle); - } - } - } - } - rc += s_diffTime; -#endif - return rc; -#endif -} /** * Loads the info about the first file into the instance. @@ -495,22 +432,6 @@ const char* ReDirStatus_t::rightsAsString(ReByteArray& buffer, bool numerical, return buffer.str(); } -/** - * Converts the unix time (time_t) to the file time. - * - * @param time the unix time (secondes since 1.1.1970) - * @param filetime OUT: the OS specific filetime - */ -void ReDirStatus_t::timeToFiletime(time_t time, ReFileTime_t& filetime) { -#ifdef __linux__ - filetime.tv_sec = time; - filetime.tv_nsec = 0; -#elif defined __WIN32__ - LONGLONG ll = Int32x32To64(time, 10000000) + 116444736000000000; - filetime.dwLowDateTime = (DWORD)ll; - filetime.dwHighDateTime = ll >> 32; -#endif -} /** * Returns the type of the entry. * return the file type, e.g. TF_REGULAR diff --git a/os/ReTraverser.hpp b/os/ReTraverser.hpp index 865f2fd..5c934bb 100644 --- a/os/ReTraverser.hpp +++ b/os/ReTraverser.hpp @@ -61,15 +61,11 @@ public: Type_t type(); char typeAsChar(); public: - static const char* filetimeToString(const ReFileTime_t* time, - ReByteArray& buffer); - static time_t filetimeToTime(const ReFileTime_t* time); #if defined __WIN32__ static bool getFileOwner(HANDLE handle, const char* file, ReByteArray& name, ReLogger* logger = NULL); static bool getPrivilege(const char* privilege, ReLogger* logger); #endif - static void timeToFiletime(time_t time, ReFileTime_t& filetime); public: ReByteArray m_path; ReByteArray m_fullName;