summaryrefslogtreecommitdiff
path: root/zen
diff options
context:
space:
mode:
authorDaniel Wilhelm <daniel@wili.li>2014-04-18 17:22:18 +0200
committerDaniel Wilhelm <daniel@wili.li>2014-04-18 17:22:18 +0200
commitbcc5cc28c6dc5178e8f4fd0cc521034ae5def388 (patch)
treebacc60d27b435d32172f97643576c5e4e953177d /zen
parent5.9 (diff)
downloadFreeFileSync-bcc5cc28c6dc5178e8f4fd0cc521034ae5def388.tar.gz
FreeFileSync-bcc5cc28c6dc5178e8f4fd0cc521034ae5def388.tar.bz2
FreeFileSync-bcc5cc28c6dc5178e8f4fd0cc521034ae5def388.zip
5.10
Diffstat (limited to 'zen')
-rw-r--r--zen/FindFilePlus/FindFilePlus.vcxproj7
-rw-r--r--zen/FindFilePlus/find_file_plus.cpp2
-rw-r--r--zen/IFileOperation/FileOperation_Vista.vcxproj7
-rw-r--r--zen/IFileOperation/file_op.cpp14
-rw-r--r--zen/basic_math.h14
-rw-r--r--zen/debug_memory_leaks.cpp31
-rw-r--r--zen/debug_minidump.cpp (renamed from zen/debug_new.cpp)10
-rw-r--r--zen/debug_minidump.h (renamed from zen/debug_new.h)0
-rw-r--r--zen/dir_watcher.cpp50
-rw-r--r--zen/error_log.h44
-rw-r--r--zen/file_handling.cpp98
-rw-r--r--zen/file_io.cpp170
-rw-r--r--zen/file_io.h69
-rw-r--r--zen/file_io_base.h64
-rw-r--r--zen/file_traverser.cpp14
-rw-r--r--zen/perf.h2
-rw-r--r--zen/scroll_window_under_cursor.cpp15
-rw-r--r--zen/serialize.h2
-rw-r--r--zen/string_base.h20
-rw-r--r--zen/string_tools.h33
-rw-r--r--zen/thread.h27
-rw-r--r--zen/tick_count.h30
-rw-r--r--zen/time.h9
-rw-r--r--zen/zstring.h2
24 files changed, 514 insertions, 220 deletions
diff --git a/zen/FindFilePlus/FindFilePlus.vcxproj b/zen/FindFilePlus/FindFilePlus.vcxproj
index a50239ab..b94174ac 100644
--- a/zen/FindFilePlus/FindFilePlus.vcxproj
+++ b/zen/FindFilePlus/FindFilePlus.vcxproj
@@ -102,6 +102,7 @@
<DebugInformationFormat>EditAndContinue</DebugInformationFormat>
<DisableSpecificWarnings>4100</DisableSpecificWarnings>
<AdditionalIncludeDirectories>../..</AdditionalIncludeDirectories>
+ <SmallerTypeCheck>true</SmallerTypeCheck>
</ClCompile>
<Link>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
@@ -113,6 +114,7 @@
</ProfileGuidedDatabase>
<ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
<TargetMachine>MachineX86</TargetMachine>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
@@ -135,6 +137,7 @@
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<DisableSpecificWarnings>4100</DisableSpecificWarnings>
<AdditionalIncludeDirectories>../..</AdditionalIncludeDirectories>
+ <SmallerTypeCheck>true</SmallerTypeCheck>
</ClCompile>
<Link>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
@@ -146,6 +149,7 @@
</ProfileGuidedDatabase>
<ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
<TargetMachine>MachineX64</TargetMachine>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
@@ -179,6 +183,7 @@
</ProfileGuidedDatabase>
<ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
<TargetMachine>MachineX86</TargetMachine>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
@@ -215,9 +220,11 @@
</ProfileGuidedDatabase>
<ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
<TargetMachine>MachineX64</TargetMachine>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
+ <ClCompile Include="..\debug_memory_leaks.cpp" />
<ClCompile Include="dll_main.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
</PrecompiledHeader>
diff --git a/zen/FindFilePlus/find_file_plus.cpp b/zen/FindFilePlus/find_file_plus.cpp
index 5fc1a538..ec56b0bc 100644
--- a/zen/FindFilePlus/find_file_plus.cpp
+++ b/zen/FindFilePlus/find_file_plus.cpp
@@ -321,7 +321,7 @@ void FileSearcher::readDirImpl(FileInformation& output) //throw NtFileError
nextEntryOffset += dirInfo.NextEntryOffset;
- auto toFileTime = [](const LARGE_INTEGER & rawTime) -> FILETIME
+ auto toFileTime = [](const LARGE_INTEGER& rawTime) -> FILETIME
{
FILETIME tmp = { rawTime.LowPart, rawTime.HighPart };
return tmp;
diff --git a/zen/IFileOperation/FileOperation_Vista.vcxproj b/zen/IFileOperation/FileOperation_Vista.vcxproj
index 4bd5b509..73bfd56a 100644
--- a/zen/IFileOperation/FileOperation_Vista.vcxproj
+++ b/zen/IFileOperation/FileOperation_Vista.vcxproj
@@ -99,6 +99,7 @@
<DebugInformationFormat>EditAndContinue</DebugInformationFormat>
<DisableSpecificWarnings>4100;4996</DisableSpecificWarnings>
<AdditionalIncludeDirectories>../..;C:\Program Files\C++\boost</AdditionalIncludeDirectories>
+ <SmallerTypeCheck>true</SmallerTypeCheck>
</ClCompile>
<Link>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
@@ -111,6 +112,7 @@
<ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
<TargetMachine>MachineX86</TargetMachine>
<AdditionalLibraryDirectories>C:\Program Files\C++\Boost\stage\lib</AdditionalLibraryDirectories>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
@@ -133,6 +135,7 @@
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<DisableSpecificWarnings>4100;4996</DisableSpecificWarnings>
<AdditionalIncludeDirectories>../..;C:\Program Files\C++\boost</AdditionalIncludeDirectories>
+ <SmallerTypeCheck>true</SmallerTypeCheck>
</ClCompile>
<Link>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
@@ -145,6 +148,7 @@
<ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
<TargetMachine>MachineX64</TargetMachine>
<AdditionalLibraryDirectories>C:\Program Files\C++\Boost\stage_x64\lib</AdditionalLibraryDirectories>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
@@ -179,6 +183,7 @@
<ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
<TargetMachine>MachineX86</TargetMachine>
<AdditionalLibraryDirectories>C:\Program Files\C++\Boost\stage\lib</AdditionalLibraryDirectories>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
@@ -216,9 +221,11 @@
<ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
<TargetMachine>MachineX64</TargetMachine>
<AdditionalLibraryDirectories>C:\Program Files\C++\Boost\stage_x64\lib</AdditionalLibraryDirectories>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
+ <ClCompile Include="..\debug_memory_leaks.cpp" />
<ClCompile Include="dll_main.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
</PrecompiledHeader>
diff --git a/zen/IFileOperation/file_op.cpp b/zen/IFileOperation/file_op.cpp
index 3591de12..5d4cfdc9 100644
--- a/zen/IFileOperation/file_op.cpp
+++ b/zen/IFileOperation/file_op.cpp
@@ -173,16 +173,16 @@ void moveToRecycleBin(const wchar_t* fileNames[], //throw ComError
// Set the operation flags. Turn off all UI from being shown to the user during the
// operation. This includes error, confirmation and progress dialogs.
- ZEN_CHECK_COM(fileOp->SetOperationFlags(FOF_ALLOWUNDO |
+ ZEN_CHECK_COM(fileOp->SetOperationFlags(FOF_ALLOWUNDO |
FOF_NOCONFIRMATION |
FOF_SILENT | //no progress dialog box
FOF_NOERRORUI |
- FOFX_EARLYFAILURE |
- //without FOFX_EARLYFAILURE, IFileOperationProgressSink::PostDeleteItem() will always report success, even if deletion failed!!? WTF!?
- //PerformOperations() will still succeed but set the uselessly generic GetAnyOperationsAborted() instead :(((
- //=> always set FOFX_EARLYFAILURE since we prefer good error messages over "doing as much as possible"
- //luckily for FreeFileSync we don't expect failures on individual files anyway: FreeFileSync moves files to be
- //deleted to a temporary folder first, so there is no reason why a second move (the recycling itself) should fail
+ FOFX_EARLYFAILURE |
+ //without FOFX_EARLYFAILURE, IFileOperationProgressSink::PostDeleteItem() will always report success, even if deletion failed!!? WTF!?
+ //PerformOperations() will still succeed but set the uselessly generic GetAnyOperationsAborted() instead :(((
+ //=> always set FOFX_EARLYFAILURE since we prefer good error messages over "doing as much as possible"
+ //luckily for FreeFileSync we don't expect failures on individual files anyway: FreeFileSync moves files to be
+ //deleted to a temporary folder first, so there is no reason why a second move (the recycling itself) should fail
FOF_NO_CONNECTED_ELEMENTS));
//use FOFX_RECYCLEONDELETE when Windows 8 is available!?
diff --git a/zen/basic_math.h b/zen/basic_math.h
index 7923dc5d..bd416d19 100644
--- a/zen/basic_math.h
+++ b/zen/basic_math.h
@@ -32,6 +32,8 @@ const T& max(const T& a, const T& b, const T& c);
template <class T>
void confine(T& val, const T& minVal, const T& maxVal); //make sure minVal <= val && val <= maxVal
+template <class T>
+T confineCpy(const T& val, const T& minVal, const T& maxVal);
template <class InputIterator>
std::pair<InputIterator, InputIterator> minMaxElement(InputIterator first, InputIterator last);
@@ -97,6 +99,7 @@ const double ln2 = 0.693147180559945309417;
template <class T> inline
T abs(T value)
{
+ //static_assert(std::is_signed<T>::value, ""); might not compile for non-built-in arithmetic types; anyway "-value" should emit compiler error or warning for unsigned types
if (value < 0)
return -value; // operator "?:" caveat: may be different type than "value"
else
@@ -132,6 +135,17 @@ const T& max(const T& a, const T& b, const T& c)
template <class T> inline
+T confineCpy(const T& val, const T& minVal, const T& maxVal)
+{
+ assert(minVal <= maxVal);
+ if (val < minVal)
+ return minVal;
+ else if (val > maxVal)
+ return maxVal;
+ return val;
+}
+
+template <class T> inline
void confine(T& val, const T& minVal, const T& maxVal) //name trim?
{
assert(minVal <= maxVal);
diff --git a/zen/debug_memory_leaks.cpp b/zen/debug_memory_leaks.cpp
new file mode 100644
index 00000000..8774d16f
--- /dev/null
+++ b/zen/debug_memory_leaks.cpp
@@ -0,0 +1,31 @@
+// **************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
+// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
+// **************************************************************************
+
+//-----------Memory Leak Detection--------------------------
+//Usage: just include this file into a Visual Studio project
+
+
+#ifndef NDEBUG
+#define _CRTDBG_MAP_ALLOC //
+#include <stdlib.h> //keep this order: "The #include statements must be in the order shown here. If you change the order, the functions you use may not work properly."
+#include <crtdbg.h> //overwrites "operator new" ect; no need to include this in every compilation unit!
+//http://msdn.microsoft.com/en-us/library/e5ewb1h3(v=vs.80).aspx
+
+namespace
+{
+struct OnStartup
+{
+ OnStartup()
+ {
+ //note: wxWidgets also "activates" leak detection in the usual buggy way: it sets incomplete flags and incorrectly overwrites them rather than appending -> luckily it still seems to work!
+ int flags = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
+ flags |= _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF;
+ _CrtSetDbgFlag(flags);
+ }
+
+} dummy;
+}
+#endif \ No newline at end of file
diff --git a/zen/debug_new.cpp b/zen/debug_minidump.cpp
index 40fb88c7..9429819f 100644
--- a/zen/debug_new.cpp
+++ b/zen/debug_minidump.cpp
@@ -4,9 +4,10 @@
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// **************************************************************************
-#include "debug_new.h"
+#include "debug_minidump.h"
#include <string>
#include <sstream>
+#include <cassert>
#include <cstdlib> //malloc(), free()
#include "win.h" //includes "windows.h"
#include "DbgHelp.h" //available for MSC only
@@ -28,7 +29,7 @@ LONG WINAPI writeDumpOnException(EXCEPTION_POINTERS* pExceptionInfo)
MINIDUMP_EXCEPTION_INFORMATION* exceptParam = pExceptionInfo ? &exInfo : nullptr;
/*bool rv = */
- ::MiniDumpWriteDump(::GetCurrentProcess(), //__in HANDLE hProcess,
+ ::MiniDumpWriteDump(::GetCurrentProcess (), //__in HANDLE hProcess,
::GetCurrentProcessId(), //__in DWORD ProcessId,
hFile, //__in HANDLE hFile,
MiniDumpWithDataSegs, //__in MINIDUMP_TYPE DumpType, ->Standard: MiniDumpNormal, Medium: MiniDumpWithDataSegs, Full: MiniDumpWithFullMemory
@@ -38,11 +39,12 @@ LONG WINAPI writeDumpOnException(EXCEPTION_POINTERS* pExceptionInfo)
::CloseHandle(hFile);
}
+ assert(false);
return EXCEPTION_EXECUTE_HANDLER;
}
//ensure that a dump-file is written for uncaught exceptions
-struct Dummy { Dummy() { ::SetUnhandledExceptionFilter(writeDumpOnException); }} dummy;
+struct OnStartup { OnStartup() { ::SetUnhandledExceptionFilter(writeDumpOnException); }} dummy;
}
@@ -105,7 +107,7 @@ void* operator new(size_t size)
return ptr;
debug_tools::writeMinidump();
- throw debug_tools::BadAllocDetailed(size);
+ throw ::BadAllocDetailed(size);
}
void operator delete(void* ptr) { ::free(ptr); }
diff --git a/zen/debug_new.h b/zen/debug_minidump.h
index 4ef0106e..4ef0106e 100644
--- a/zen/debug_new.h
+++ b/zen/debug_minidump.h
diff --git a/zen/dir_watcher.cpp b/zen/dir_watcher.cpp
index c02453c6..2d249af8 100644
--- a/zen/dir_watcher.cpp
+++ b/zen/dir_watcher.cpp
@@ -97,7 +97,7 @@ public:
{
boost::lock_guard<boost::mutex> dummy(lockAccess);
- //first check whether errors occured in thread
+ //first check whether errors occurred in thread
if (!errorMsg.first.empty())
{
const std::wstring msg = errorMsg.first.c_str();
@@ -125,7 +125,7 @@ private:
boost::mutex lockAccess;
std::vector<DirWatcher::Entry> changedFiles;
- std::pair<BasicWString, DWORD> errorMsg; //non-empty if errors occured in thread
+ std::pair<BasicWString, DWORD> errorMsg; //non-empty if errors occurred in thread
};
@@ -279,9 +279,12 @@ private:
virtual void onRequestRemoval(HANDLE hnd)
{
//must release hDir immediately => stop monitoring!
- worker_.interrupt();
- worker_.join(); //we assume precondition "worker.joinable()"!!!
- //now hDir should have been released
+ if (worker_.joinable()) //= join() precondition: play safe; can't trust Windows to only call-back once
+ {
+ worker_.interrupt();
+ worker_.join(); //we assume precondition "worker.joinable()"!!!
+ //now hDir should have been released
+ }
removalRequested = true;
} //don't throw!
@@ -319,8 +322,13 @@ DirWatcher::DirWatcher(const Zstring& directory) : //throw FileError
DirWatcher::~DirWatcher()
{
- pimpl_->worker.interrupt();
- //pimpl_->worker.join(); -> we don't have time to wait... will take ~50ms anyway
+ if (pimpl_->worker.joinable()) //= thread::detach() precondition! -> may already be joined by HandleVolumeRemoval::onRequestRemoval()
+ {
+ pimpl_->worker.interrupt();
+ //if (pimpl_->worker.joinable()) pimpl_->worker.join(); -> we don't have time to wait... will take ~50ms anyway
+ pimpl_->worker.detach(); //we have to be explicit since C++11: [thread.thread.destr] ~thread() calls std::terminate() if joinable()!!!
+ }
+
//caveat: exitting the app may simply kill this thread!
}
@@ -409,10 +417,11 @@ DirWatcher::DirWatcher(const Zstring& directory) : //throw FileError
//set non-blocking mode
bool initSuccess = false;
- int flags = ::fcntl(pimpl_->notifDescr, F_GETFL);
- if (flags != -1)
- initSuccess = ::fcntl(pimpl_->notifDescr, F_SETFL, flags | O_NONBLOCK) != -1;
-
+ {
+ int flags = ::fcntl(pimpl_->notifDescr, F_GETFL);
+ if (flags != -1)
+ initSuccess = ::fcntl(pimpl_->notifDescr, F_SETFL, flags | O_NONBLOCK) != -1;
+ }
if (!initSuccess)
throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(dirname)) + L"\n\n" + getLastErrorFormatted());
@@ -455,13 +464,18 @@ DirWatcher::~DirWatcher()
std::vector<DirWatcher::Entry> DirWatcher::getChanges(const std::function<void()>&) //throw FileError
{
//non-blocking call, see O_NONBLOCK
- std::vector<char> buffer(1024 * (sizeof(struct inotify_event) + 16));
- ssize_t bytesRead = ::read(pimpl_->notifDescr, &buffer[0], buffer.size());
+ std::vector<char> buffer(1024 * (sizeof(struct ::inotify_event) + 16));
+
+ ssize_t bytesRead = 0;
+ do
+ {
+ bytesRead = ::read(pimpl_->notifDescr, &buffer[0], buffer.size());
+ }
+ while (bytesRead < 0 && errno == EINTR); //"Interrupted function call; When this happens, you should try the call again."
- if (bytesRead == -1)
+ if (bytesRead < 0)
{
- if (errno == EINTR || //Interrupted function call; When this happens, you should try the call again.
- errno == EAGAIN) //Non-blocking I/O has been selected using O_NONBLOCK and no data was immediately available for reading
+ if (errno == EAGAIN) //this error is ignored in all inotify wrappers I found
return std::vector<Entry>();
throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(pimpl_->dirname)) + L"\n\n" + getLastErrorFormatted());
@@ -472,7 +486,7 @@ std::vector<DirWatcher::Entry> DirWatcher::getChanges(const std::function<void()
ssize_t bytePos = 0;
while (bytePos < bytesRead)
{
- struct inotify_event& evt = reinterpret_cast<struct inotify_event&>(buffer[bytePos]);
+ struct ::inotify_event& evt = reinterpret_cast<struct ::inotify_event&>(buffer[bytePos]);
if (evt.len != 0) //exclude case: deletion of "self", already reported by parent directory watch
{
@@ -497,7 +511,7 @@ std::vector<DirWatcher::Entry> DirWatcher::getChanges(const std::function<void()
}
}
- bytePos += sizeof(struct inotify_event) + evt.len;
+ bytePos += sizeof(struct ::inotify_event) + evt.len;
}
return output;
diff --git a/zen/error_log.h b/zen/error_log.h
index 401581d7..490bb1f4 100644
--- a/zen/error_log.h
+++ b/zen/error_log.h
@@ -25,30 +25,35 @@ enum MessageType
TYPE_FATAL_ERROR = 0x8,
};
-typedef Zbase<wchar_t> MsgString; //std::wstring may employ small string optimization: we cannot accept bloating the "logEntries" memory block below (think 1 million entries)
+typedef Zbase<wchar_t> MsgString; //std::wstring may employ small string optimization: we cannot accept bloating the "ErrorLog::entries" memory block below (think 1 million items)
struct LogEntry
{
time_t time;
MessageType type;
- MsgString message;
+ MsgString message;
};
-MsgString formatMessage(const LogEntry& msg);
+template <class String>
+String formatMessage(const LogEntry& entry);
class ErrorLog
{
public:
- template <class String>
- void logMsg(const String& message, MessageType type);
+ template <class String> //a wchar_t-based string!
+ void logMsg(const String& text, MessageType type);
int getItemCount(int typeFilter = TYPE_INFO | TYPE_WARNING | TYPE_ERROR | TYPE_FATAL_ERROR) const;
- const std::vector<LogEntry>& getEntries() const { return logEntries; }
+ //subset of std::vector<> interface:
+ typedef std::vector<LogEntry>::const_iterator const_iterator;
+ const_iterator begin() const { return entries.begin(); }
+ const_iterator end () const { return entries.end (); }
+ bool empty() const { return entries.empty(); }
private:
- std::vector<LogEntry> logEntries; //list of non-resolved errors and warnings
+ std::vector<LogEntry> entries; //list of non-resolved errors and warnings
};
@@ -59,29 +64,26 @@ private:
-
-
-
-
//######################## implementation ##########################
template <class String> inline
-void ErrorLog::logMsg(const String& message, zen::MessageType type)
+void ErrorLog::logMsg(const String& text, zen::MessageType type)
{
- const LogEntry newEntry = { std::time(nullptr), type, copyStringTo<MsgString>(message) };
- logEntries.push_back(newEntry);
+ const LogEntry newEntry = { std::time(nullptr), type, copyStringTo<MsgString>(text) };
+ entries.push_back(newEntry);
}
inline
int ErrorLog::getItemCount(int typeFilter) const
{
- return static_cast<int>(std::count_if(logEntries.begin(), logEntries.end(), [&](const LogEntry& e) { return e.type & typeFilter; }));
+ return static_cast<int>(std::count_if(entries.begin(), entries.end(), [&](const LogEntry& e) { return e.type & typeFilter; }));
}
namespace
{
-MsgString formatMessageImpl(const LogEntry& entry) //internal linkage
+template <class String>
+String formatMessageImpl(const LogEntry& entry) //internal linkage
{
auto getTypeName = [&]() -> std::wstring
{
@@ -96,11 +98,11 @@ MsgString formatMessageImpl(const LogEntry& entry) //internal linkage
case TYPE_FATAL_ERROR:
return _("Fatal Error");
}
- assert(false);
+ assert(false);
return std::wstring();
};
- MsgString formattedText = L"[" + formatTime<MsgString>(FORMAT_TIME, localTime(entry.time)) + L"] " + copyStringTo<MsgString>(getTypeName()) + L": ";
+ String formattedText = L"[" + formatTime<String>(FORMAT_TIME, localTime(entry.time)) + L"] " + copyStringTo<String>(getTypeName()) + L": ";
const size_t prefixLen = formattedText.size();
for (auto iter = entry.message.begin(); iter != entry.message.end(); )
@@ -108,7 +110,7 @@ MsgString formatMessageImpl(const LogEntry& entry) //internal linkage
{
formattedText += L'\n';
- MsgString blanks;
+ String blanks;
blanks.resize(prefixLen, L' ');
formattedText += blanks;
@@ -125,8 +127,8 @@ MsgString formatMessageImpl(const LogEntry& entry) //internal linkage
}
}
-inline
-MsgString formatMessage(const LogEntry& entry) { return formatMessageImpl(entry); }
+template <class String> inline
+String formatMessage(const LogEntry& entry) { return formatMessageImpl<String>(entry); }
}
#endif //ERRORLOGGING_H_INCLUDED
diff --git a/zen/file_handling.cpp b/zen/file_handling.cpp
index 589057ad..fce85bcd 100644
--- a/zen/file_handling.cpp
+++ b/zen/file_handling.cpp
@@ -28,9 +28,8 @@
#elif defined FFS_LINUX
#include <sys/stat.h>
-#include <time.h>
#include <utime.h>
-#include <sys/time.h>
+#include <sys/time.h> //futimes
#include <sys/vfs.h>
#ifdef HAVE_SELINUX
@@ -926,7 +925,7 @@ void zen::setFileTime(const Zstring& filename, const Int64& modificationTime, Pr
#elif defined FFS_LINUX
if (procSl == SYMLINK_FOLLOW)
{
- struct utimbuf newTimes = {};
+ struct ::utimbuf newTimes = {};
newTimes.actime = ::time(nullptr);
newTimes.modtime = to<time_t>(modificationTime);
@@ -936,12 +935,9 @@ void zen::setFileTime(const Zstring& filename, const Int64& modificationTime, Pr
}
else
{
- struct timeval newTimes[2] = {};
- newTimes[0].tv_sec = ::time(nullptr); //seconds
- newTimes[0].tv_usec = 0; //microseconds
-
- newTimes[1].tv_sec = to<time_t>(modificationTime);
- newTimes[1].tv_usec = 0;
+ struct ::timeval newTimes[2] = {};
+ newTimes[0].tv_sec = ::time(nullptr); //access time (seconds)
+ newTimes[1].tv_sec = to<time_t>(modificationTime); //modification time (seconds)
if (::lutimes(filename.c_str(), newTimes) != 0)
throw FileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted());
@@ -1315,8 +1311,8 @@ void createDirectoryStraight(const Zstring& directory, //throw FileError, ErrorT
::SetFileAttributes(applyLongPathPrefix(directory).c_str(), sourceAttr);
//copy "read-only and system attributes": http://blogs.msdn.com/b/oldnewthing/archive/2003/09/30/55100.aspx
- const bool isCompressed = (sourceAttr & FILE_ATTRIBUTE_COMPRESSED) != 0;
- const bool isEncrypted = (sourceAttr & FILE_ATTRIBUTE_ENCRYPTED) != 0;
+ const bool isCompressed = (sourceAttr & FILE_ATTRIBUTE_COMPRESSED) != 0;
+ const bool isEncrypted = (sourceAttr & FILE_ATTRIBUTE_ENCRYPTED) != 0;
if (isEncrypted)
::EncryptFile(directory.c_str()); //seems no long path is required (check passed!)
@@ -1492,7 +1488,7 @@ void zen::copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool
catch (...) {}
});
- //file times: essential for a symlink: enforce this! (don't just try!)
+ //file times: essential for sync'ing a symlink: enforce this! (don't just try!)
{
const Int64 modTime = getFileTime(sourceLink, SYMLINK_DIRECT); //throw FileError
setFileTime(targetLink, modTime, SYMLINK_DIRECT); //throw FileError
@@ -1741,11 +1737,8 @@ void copyFileWindowsSparse(const Zstring& sourceFile,
}
//----------------------------------------------------------------------
- const DWORD BUFFER_SIZE = 512 * 1024; //512 kb seems to be a reasonable buffer size - must be greater than sizeof(WIN32_STREAM_ID)
- static boost::thread_specific_ptr<std::vector<BYTE>> cpyBuf;
- if (!cpyBuf.get())
- cpyBuf.reset(new std::vector<BYTE>(BUFFER_SIZE));
- std::vector<BYTE>& buffer = *cpyBuf;
+ const DWORD BUFFER_SIZE = 128 * 1024; //must be greater than sizeof(WIN32_STREAM_ID)
+ std::vector<BYTE> buffer(BUFFER_SIZE);
LPVOID contextRead = nullptr; //manage context for BackupRead()/BackupWrite()
LPVOID contextWrite = nullptr; //
@@ -2172,23 +2165,21 @@ void copyFileLinux(const Zstring& sourceFile,
FileAttrib* newAttrib) //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting
{
zen::ScopeGuard guardTarget = zen::makeGuard([&] { try { removeFile(targetFile); } catch (...) {} }); //transactional behavior: place guard before lifetime of FileOutput
+
+ //open sourceFile for reading
+ FileInputUnbuffered fileIn(sourceFile); //throw FileError, ErrorNotExisting
+
+ struct ::stat sourceInfo = {};
+ if (::fstat(fileIn.getDescriptor(), &sourceInfo) != 0) //read file attributes from source
+ throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(sourceFile)) + L"\n\n" + getLastErrorFormatted());
+
try
{
- //open sourceFile for reading
- FileInput fileIn(sourceFile); //throw FileError
-
//create targetFile and open it for writing
- FileOutput fileOut(targetFile, FileOutput::ACC_CREATE_NEW); //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting
-
- std::vector<char>& buffer = []() -> std::vector<char>&
- {
- static boost::thread_specific_ptr<std::vector<char>> cpyBuf;
- if (!cpyBuf.get())
- cpyBuf.reset(new std::vector<char>(512 * 1024)); //512 kb seems to be a reasonable buffer size
- return *cpyBuf;
- }();
+ FileOutputUnbuffered fileOut(targetFile, sourceInfo.st_mode); //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting
//copy contents of sourceFile to targetFile
+ std::vector<char> buffer(128 * 1024); //see comment in FileInputUnbuffered::read
do
{
const size_t bytesRead = fileIn.read(&buffer[0], buffer.size()); //throw FileError
@@ -2200,39 +2191,34 @@ void copyFileLinux(const Zstring& sourceFile,
callback->updateCopyStatus(Int64(bytesRead)); //throw X!
}
while (!fileIn.eof());
- }
- catch (ErrorTargetExisting&)
- {
- guardTarget.dismiss(); //don't delete file that existed previously!
- throw;
- }
-
- //adapt file modification time:
- {
- struct ::stat srcInfo = {};
- if (::stat(sourceFile.c_str(), &srcInfo) != 0) //read file attributes from source directory
- throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(sourceFile)) + L"\n\n" + getLastErrorFormatted());
- struct ::utimbuf newTimes = {};
- newTimes.actime = srcInfo.st_atime;
- newTimes.modtime = srcInfo.st_mtime;
-
- //set new "last write time"
- if (::utime(targetFile.c_str(), &newTimes) != 0)
- throw FileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(targetFile)) + L"\n\n" + getLastErrorFormatted());
-
- if (newAttrib)
+ //adapt target file modification time:
{
- struct ::stat trgInfo = {};
- if (::stat(targetFile.c_str(), &trgInfo) != 0) //read file attributes from source directory
+ struct ::timeval newTimes[2] = {};
+ newTimes[0].tv_sec = sourceInfo.st_atime;
+ newTimes[1].tv_sec = sourceInfo.st_mtime;
+ if (::futimes(fileOut.getDescriptor(), newTimes) != 0) //by using the already open file handle, we avoid issues like: https://sourceforge.net/p/freefilesync/bugs/230/
+ throw FileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(targetFile)) + L"\n\n" + getLastErrorFormatted());
+
+ //read and return file statistics
+ struct ::stat targetInfo = {};
+ if (::fstat(fileOut.getDescriptor(), &targetInfo) != 0)
throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(targetFile)) + L"\n\n" + getLastErrorFormatted());
- newAttrib->fileSize = UInt64(srcInfo.st_size);
- newAttrib->modificationTime = srcInfo.st_mtime;
- newAttrib->sourceFileId = extractFileID(srcInfo);
- newAttrib->targetFileId = extractFileID(trgInfo);
+ if (newAttrib)
+ {
+ newAttrib->fileSize = UInt64(sourceInfo.st_size);
+ newAttrib->modificationTime = sourceInfo.st_mtime;
+ newAttrib->sourceFileId = extractFileID(sourceInfo);
+ newAttrib->targetFileId = extractFileID(targetInfo);
+ }
}
}
+ catch (ErrorTargetExisting&)
+ {
+ guardTarget.dismiss(); //don't delete file that existed previously!
+ throw;
+ }
guardTarget.dismiss(); //target has been created successfully!
}
diff --git a/zen/file_io.cpp b/zen/file_io.cpp
index 0b5586b0..4880f6cc 100644
--- a/zen/file_io.cpp
+++ b/zen/file_io.cpp
@@ -8,20 +8,46 @@
#ifdef FFS_WIN
#include "long_path_prefix.h"
+#elif defined FFS_LINUX
+#include <fcntl.h> //open, close
+#include <unistd.h> //read, write
#endif
using namespace zen;
-FileInput::FileInput(FileHandle handle, const Zstring& filename) :
- eofReached(false),
- fileHandle(handle),
- filename_(filename) {}
+FileInput::FileInput(FileHandle handle, const Zstring& filename) : FileInputBase(filename), fileHandle(handle) {}
+
+#ifdef FFS_LINUX
+//"filename" could be a named pipe which *blocks* forever during "open()"! https://sourceforge.net/p/freefilesync/bugs/221/
+void checkForUnsupportedType(const Zstring& filename) //throw FileError
+{
+ struct ::stat fileInfo = {};
+ if (::stat(filename.c_str(), &fileInfo) != 0) //follows symlinks
+ return; //let the caller handle errors like "not existing"
+
+ if (!S_ISREG(fileInfo.st_mode) &&
+ !S_ISLNK(fileInfo.st_mode) &&
+ !S_ISDIR(fileInfo.st_mode))
+ {
+ auto getTypeName = [](mode_t m) -> std::wstring
+ {
+ const wchar_t* name =
+ S_ISCHR (m) ? L"character device":
+ S_ISBLK (m) ? L"block device" :
+ S_ISFIFO(m) ? L"FIFO, named pipe" :
+ S_ISSOCK(m) ? L"socket" : nullptr;
+ const std::wstring numFmt = printNumber<std::wstring>(L"0%06o", m & __S_IFMT);
+ return name ? numFmt + L", " + name : numFmt;
+ };
+ throw FileError(replaceCpy(_("Type of item %x is not supported:"), L"%x", fmtFileName(filename)) + L" " + getTypeName(fileInfo.st_mode));
+ }
+}
+#endif
FileInput::FileInput(const Zstring& filename) : //throw FileError, ErrorNotExisting
- eofReached(false),
- filename_(filename)
+ FileInputBase(filename)
{
#ifdef FFS_WIN
fileHandle = ::CreateFile(applyLongPathPrefix(filename).c_str(),
@@ -57,6 +83,7 @@ FileInput::FileInput(const Zstring& filename) : //throw FileError, ErrorNotExis
nullptr);
if (fileHandle == INVALID_HANDLE_VALUE)
#elif defined FFS_LINUX
+ checkForUnsupportedType(filename); //throw FileError; reading a named pipe would block forever!
fileHandle = ::fopen(filename.c_str(), "r,type=record,noseek"); //utilize UTF-8 filename
if (!fileHandle)
#endif
@@ -64,9 +91,9 @@ FileInput::FileInput(const Zstring& filename) : //throw FileError, ErrorNotExis
const ErrorCode lastError = getLastError();
if (errorCodeForNotExisting(lastError))
- throw ErrorNotExisting(replaceCpy(_("Cannot find file %x."), L"%x", fmtFileName(filename_)) + L"\n\n" + getLastErrorFormatted(lastError));
+ throw ErrorNotExisting(replaceCpy(_("Cannot find file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + getLastErrorFormatted(lastError));
- throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(filename_)) + L"\n\n" + getLastErrorFormatted(lastError) + L" (open)");
+ throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + getLastErrorFormatted(lastError) + L" (open)");
}
}
@@ -83,6 +110,8 @@ FileInput::~FileInput()
size_t FileInput::read(void* buffer, size_t bytesToRead) //returns actual number of bytes read; throw FileError
{
+ assert(!eof());
+ if (bytesToRead == 0) return 0;
#ifdef FFS_WIN
DWORD bytesRead = 0;
if (!::ReadFile(fileHandle, //__in HANDLE hFile,
@@ -94,33 +123,33 @@ size_t FileInput::read(void* buffer, size_t bytesToRead) //returns actual number
const size_t bytesRead = ::fread(buffer, 1, bytesToRead, fileHandle);
if (::ferror(fileHandle) != 0) //checks status of stream, not fread()!
#endif
- throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(filename_)) + L"\n\n" + getLastErrorFormatted() + L" (read)");
+ throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + getLastErrorFormatted() + L" (read)");
#ifdef FFS_WIN
if (bytesRead < bytesToRead) //verify only!
- eofReached = true;
+ setEof();
#elif defined FFS_LINUX
if (::feof(fileHandle) != 0)
- eofReached = true;
+ setEof();
if (bytesRead < bytesToRead)
- if (!eofReached) //pathologic!?
- throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(filename_)) + L"\n\n" + L"Incomplete read!");
+ if (!eof()) //pathologic!?
+ throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + L"Incomplete read!");
#endif
if (bytesRead > bytesToRead)
- throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(filename_)) + L"\n\n" + L"buffer overflow");
+ throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + L"buffer overflow");
return bytesRead;
}
-FileOutput::FileOutput(FileHandle handle, const Zstring& filename) : fileHandle(handle), filename_(filename) {}
+FileOutput::FileOutput(FileHandle handle, const Zstring& filename) : FileOutputBase(filename), fileHandle(handle) {}
FileOutput::FileOutput(const Zstring& filename, AccessFlag access) : //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting
- filename_(filename)
+ FileOutputBase(filename)
{
#ifdef FFS_WIN
const DWORD dwCreationDisposition = access == FileOutput::ACC_OVERWRITE ? CREATE_ALWAYS : CREATE_NEW;
@@ -160,7 +189,7 @@ FileOutput::FileOutput(const Zstring& filename, AccessFlag access) : //throw Fil
//"regular" error handling
if (fileHandle == INVALID_HANDLE_VALUE)
{
- const std::wstring errorMessage = replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(filename_)) + L"\n\n" + zen::getLastErrorFormatted(lastError);
+ const std::wstring errorMessage = replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + zen::getLastErrorFormatted(lastError);
if (lastError == ERROR_FILE_EXISTS || //confirmed to be used
lastError == ERROR_ALREADY_EXISTS) //comment on msdn claims, this one is used on Windows Mobile 6
@@ -174,13 +203,14 @@ FileOutput::FileOutput(const Zstring& filename, AccessFlag access) : //throw Fil
}
#elif defined FFS_LINUX
+ checkForUnsupportedType(filename); //throw FileError; writing a named pipe would block forever!
fileHandle = ::fopen(filename.c_str(),
//GNU extension: https://www.securecoding.cert.org/confluence/display/cplusplus/FIO03-CPP.+Do+not+make+assumptions+about+fopen()+and+file+creation
access == ACC_OVERWRITE ? "w,type=record,noseek" : "wx,type=record,noseek");
if (!fileHandle)
{
const int lastError = errno;
- const std::wstring errorMessage = replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(filename_)) + L"\n\n" + zen::getLastErrorFormatted(lastError);
+ const std::wstring errorMessage = replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + zen::getLastErrorFormatted(lastError);
if (lastError == EEXIST)
throw ErrorTargetExisting(errorMessage);
@@ -216,8 +246,108 @@ void FileOutput::write(const void* buffer, size_t bytesToWrite) //throw FileErro
const size_t bytesWritten = ::fwrite(buffer, 1, bytesToWrite, fileHandle);
if (::ferror(fileHandle) != 0) //checks status of stream, not fwrite()!
#endif
- throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(filename_)) + L"\n\n" + getLastErrorFormatted() + L" (w)"); //w -> distinguish from fopen error message!
+ throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + getLastErrorFormatted() + L" (w)"); //w -> distinguish from fopen error message!
if (bytesWritten != bytesToWrite) //must be fulfilled for synchronous writes!
- throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(filename_)) + L"\n\n" + L"Incomplete write!");
+ throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + L"Incomplete write!");
+}
+
+
+#ifdef FFS_LINUX
+//Compare copy_reg() in copy.c: ftp://ftp.gnu.org/gnu/coreutils/coreutils-5.0.tar.gz
+
+FileInputUnbuffered::FileInputUnbuffered(const Zstring& filename) : FileInputBase(filename) //throw FileError, ErrorNotExisting
+{
+ checkForUnsupportedType(filename); //throw FileError; reading a named pipe would block forever!
+
+ fdFile = ::open(filename.c_str(), O_RDONLY);
+ if (fdFile < 0)
+ {
+ const ErrorCode lastError = getLastError();
+
+ if (errorCodeForNotExisting(lastError))
+ throw ErrorNotExisting(replaceCpy(_("Cannot find file %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted(lastError));
+
+ throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted(lastError) + L" (open)");
+ }
+}
+
+
+FileInputUnbuffered::~FileInputUnbuffered() { ::close(fdFile); }
+
+
+size_t FileInputUnbuffered::read(void* buffer, size_t bytesToRead) //throw FileError; returns actual number of bytes read
+{
+ assert(!eof());
+ if (bytesToRead == 0) return 0; //[!]
+
+ ssize_t bytesRead = 0;
+ do
+ {
+ bytesRead = ::read(fdFile, buffer, bytesToRead);
+ }
+ while (bytesRead < 0 && errno == EINTR);
+
+ if (bytesRead < 0)
+ throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + getLastErrorFormatted() + L" (read)");
+ else if (bytesRead == 0) //"zero indicates end of file"
+ setEof();
+ else if (bytesRead > static_cast<ssize_t>(bytesToRead)) //better safe than sorry
+ throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + L"buffer overflow");
+ //if ::read is interrupted (EINTR) right in the middle, it will return successfully with "bytesRead < bytesToRead"!
+
+ return bytesRead;
+}
+
+
+FileOutputUnbuffered::FileOutputUnbuffered(const Zstring& filename, mode_t mode) : FileOutputBase(filename) //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting
+{
+ //checkForUnsupportedType(filename); -> not needed, open() + O_EXCL shoul fail fast
+
+ //overwrite is: O_CREAT | O_WRONLY | O_TRUNC
+ fdFile = ::open(filename.c_str(), O_CREAT | O_WRONLY | O_EXCL, mode & (S_IRWXU | S_IRWXG | S_IRWXO));
+ if (fdFile < 0)
+ {
+ const int lastError = errno;
+ const std::wstring errorMessage = replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(filename)) + L"\n\n" + zen::getLastErrorFormatted(lastError);
+ if (lastError == EEXIST)
+ throw ErrorTargetExisting(errorMessage);
+
+ if (lastError == ENOENT)
+ throw ErrorTargetPathMissing(errorMessage);
+
+ throw FileError(errorMessage);
+ }
}
+
+
+FileOutputUnbuffered::~FileOutputUnbuffered() { ::close(fdFile); }
+
+
+void FileOutputUnbuffered::write(const void* buffer, size_t bytesToWrite) //throw FileError
+{
+ while (bytesToWrite > 0)
+ {
+ ssize_t bytesWritten = 0;
+ do
+ {
+ bytesWritten = ::write(fdFile, buffer, bytesToWrite);
+ }
+ while (bytesWritten < 0 && errno == EINTR);
+
+ if (bytesWritten <= 0)
+ {
+ if (bytesWritten == 0) //comment in safe-read.c suggests to treat this as an error due to buggy drivers
+ errno = ENOSPC;
+
+ throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + getLastErrorFormatted() + L" (w)");
+ }
+ if (bytesWritten > static_cast<ssize_t>(bytesToWrite)) //better safe than sorry
+ throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + L"buffer overflow");
+
+ //if ::write is interrupted (EINTR) right in the middle, it will return successfully with "bytesWritten < bytesToWrite"!
+ buffer = static_cast<const char*>(buffer) + bytesWritten; //suppress warning about pointer arithmetics on void*
+ bytesToWrite -= bytesWritten;
+ }
+}
+#endif
diff --git a/zen/file_io.h b/zen/file_io.h
index b4d58ea4..31373857 100644
--- a/zen/file_io.h
+++ b/zen/file_io.h
@@ -7,15 +7,16 @@
#ifndef FILEIO_H_INCLUDED
#define FILEIO_H_INCLUDED
+#include "file_io_base.h"
+#include "file_error.h"
+
#ifdef FFS_WIN
#include "win.h" //includes "windows.h"
-
#elif defined FFS_LINUX
#include <cstdio>
+#include <sys/stat.h>
#endif
-#include "zstring.h"
-#include "file_error.h"
namespace zen
{
@@ -25,7 +26,7 @@ static const char LINE_BREAK[] = "\r\n";
static const char LINE_BREAK[] = "\n";
#endif
-//file IO optimized for sequential read/write accesses + better error reporting + long path support (following symlinks)
+//buffered file IO optimized for sequential read/write accesses + better error reporting + long path support (following symlinks)
#ifdef FFS_WIN
typedef HANDLE FileHandle;
@@ -33,52 +34,66 @@ typedef HANDLE FileHandle;
typedef FILE* FileHandle;
#endif
-class FileInput
+class FileInput : public FileInputBase
{
public:
FileInput(const Zstring& filename); //throw FileError, ErrorNotExisting
FileInput(FileHandle handle, const Zstring& filename); //takes ownership!
~FileInput();
- size_t read(void* buffer, size_t bytesToRead); //throw FileError; returns actual number of bytes read
- bool eof() { return eofReached; } //end of file reached
-
- const Zstring& getFilename() const { return filename_; }
+ virtual size_t read(void* buffer, size_t bytesToRead); //throw FileError; returns actual number of bytes read
+ //expected to fill buffer completely unless "end of file"
private:
- FileInput(const FileInput&);
- FileInput& operator=(const FileInput&);
-
- bool eofReached;
FileHandle fileHandle;
- const Zstring filename_;
};
-class FileOutput
+class FileOutput : public FileOutputBase
{
public:
- enum AccessFlag
- {
- ACC_OVERWRITE,
- ACC_CREATE_NEW
- };
FileOutput(const Zstring& filename, AccessFlag access); //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting
FileOutput(FileHandle handle, const Zstring& filename); //takes ownership!
~FileOutput();
- void write(const void* buffer, size_t bytesToWrite); //throw FileError
-
- const Zstring& getFilename() const { return filename_; }
+ virtual void write(const void* buffer, size_t bytesToWrite); //throw FileError
private:
- FileOutput(const FileOutput&);
- FileOutput& operator=(const FileOutput&);
-
FileHandle fileHandle;
- const Zstring filename_;
};
+
+#ifdef FFS_LINUX
+class FileInputUnbuffered : public FileInputBase
+{
+public:
+ FileInputUnbuffered(const Zstring& filename); //throw FileError, ErrorNotExisting
+ ~FileInputUnbuffered();
+
+ //considering safe-read.c it seems buffer size should be a multiple of 8192
+ virtual size_t read(void* buffer, size_t bytesToRead); //throw FileError; returns actual number of bytes read
+ //we should not rely on buffer being filled completely!
+
+ int getDescriptor() { return fdFile;}
+
+private:
+ int fdFile;
+};
+
+class FileOutputUnbuffered : public FileOutputBase
+{
+public:
+ //creates a new file (no overwrite allowed!)
+ FileOutputUnbuffered(const Zstring& filename, mode_t mode); //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting
+ ~FileOutputUnbuffered();
+
+ virtual void write(const void* buffer, size_t bytesToWrite); //throw FileError
+ int getDescriptor() { return fdFile;}
+
+private:
+ int fdFile;
+};
+#endif
}
#endif // FILEIO_H_INCLUDED
diff --git a/zen/file_io_base.h b/zen/file_io_base.h
new file mode 100644
index 00000000..f26cd8c2
--- /dev/null
+++ b/zen/file_io_base.h
@@ -0,0 +1,64 @@
+// **************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
+// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
+// **************************************************************************
+
+#ifndef FILEIO_BASE_H_INCLUDED_23432431789615314
+#define FILEIO_BASE_H_INCLUDED_23432431789615314
+
+#include "zstring.h"
+
+namespace zen
+{
+class FileBase
+{
+public:
+ const Zstring& getFilename() const { return filename_; }
+
+protected:
+ FileBase(const Zstring& filename) : filename_(filename) {}
+ ~FileBase() {}
+
+private:
+ FileBase(const FileBase&);
+ FileBase& operator=(const FileBase&);
+
+ const Zstring filename_;
+};
+
+
+class FileInputBase : public FileBase
+{
+public:
+ virtual size_t read(void* buffer, size_t bytesToRead) = 0; //throw FileError; returns actual number of bytes read
+ bool eof() const { return eofReached; } //end of file reached
+
+protected:
+ FileInputBase(const Zstring& filename) : FileBase(filename), eofReached(false) {}
+ ~FileInputBase() {}
+ void setEof() { eofReached = true; }
+
+private:
+ bool eofReached;
+};
+
+
+class FileOutputBase : public FileBase
+{
+public:
+ enum AccessFlag
+ {
+ ACC_OVERWRITE,
+ ACC_CREATE_NEW
+ };
+ virtual void write(const void* buffer, size_t bytesToWrite) = 0; //throw FileError
+
+protected:
+ FileOutputBase(const Zstring& filename) : FileBase(filename) {}
+ ~FileOutputBase() {}
+};
+
+}
+
+#endif //FILEIO_BASE_H_INCLUDED_23432431789615314
diff --git a/zen/file_traverser.cpp b/zen/file_traverser.cpp
index a95f5dee..2cea74d8 100644
--- a/zen/file_traverser.cpp
+++ b/zen/file_traverser.cpp
@@ -547,7 +547,6 @@ private:
if (!dirEntry) //no more items or ignored error
return;
-
//don't return "." and ".."
const char* const shortName = dirEntry->d_name; //evaluate dirEntry *before* going into recursion => we use a single "buffer"!
if (shortName[0] == '.' &&
@@ -597,12 +596,12 @@ private:
if (const std::shared_ptr<TraverseCallback>& rv = sink.onDir(shortName, fullName))
traverse(fullName, *rv, level + 1);
}
- else //a file
+ else //a file or named pipe, ect.
{
TraverseCallback::FileInfo fileInfo;
fileInfo.fileSize = zen::UInt64(statDataTrg.st_size);
fileInfo.lastWriteTimeRaw = statDataTrg.st_mtime; //UTC time (time_t format); unit: 1 second
- //fileInfo.id = extractFileID(statDataTrg); -> id from dereferenced symlink is problematic, since renaming will consider the link, not the target!
+ //fileInfo.id = extractFileID(statDataTrg); -> id from dereferenced symlink is problematic, since renaming would consider the link, not the target!
sink.onFile(shortName, fullName, fileInfo);
}
}
@@ -619,7 +618,7 @@ private:
if (const std::shared_ptr<TraverseCallback>& rv = sink.onDir(shortName, fullName))
traverse(fullName, *rv, level + 1);
}
- else //a file
+ else //a file or named pipe, ect.
{
TraverseCallback::FileInfo fileInfo;
fileInfo.fileSize = zen::UInt64(statData.st_size);
@@ -628,6 +627,13 @@ private:
sink.onFile(shortName, fullName, fileInfo);
}
+ /*
+ It may be a good idea to not check "S_ISREG(statData.st_mode)" explicitly and to not issue an error message on other types to support these scenarios:
+ - RTS setup watch (essentially wants to read directories only)
+ - removeDirectory (wants to delete everything; pipes can be deleted just like files via "unlink")
+
+ However an "open" on a pipe will block (https://sourceforge.net/p/freefilesync/bugs/221/), so the copy routines need to be smarter!!
+ */
}
}
diff --git a/zen/perf.h b/zen/perf.h
index c8a14950..92350602 100644
--- a/zen/perf.h
+++ b/zen/perf.h
@@ -48,7 +48,7 @@ public:
if (!now.isValid())
throw TimerError();
- const auto delta = static_cast<long>(1000.0 * (now - startTime) / ticksPerSec_);
+ const auto delta = static_cast<long>(1000.0 * dist(startTime, now) / ticksPerSec_);
#ifdef FFS_WIN
std::ostringstream ss;
ss << delta << " ms";
diff --git a/zen/scroll_window_under_cursor.cpp b/zen/scroll_window_under_cursor.cpp
index 6031cd88..f201bb04 100644
--- a/zen/scroll_window_under_cursor.cpp
+++ b/zen/scroll_window_under_cursor.cpp
@@ -4,10 +4,6 @@
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// **************************************************************************
-#include <cassert>
-#include "win.h" //includes "windows.h"
-#include "Windowsx.h" //WM_MOUSEWHEEL
-
//redirect mouse wheel events directly to window under cursor rather than window having input focus
//implementing new Windows Vista UI guidelines: http://msdn.microsoft.com/en-us/library/bb545459.aspx#wheel
//this is confirmed to be required for at least Windows 2000 to Windows 8
@@ -15,6 +11,11 @@
//Usage: just include this file into a Windows project
+#include <cassert>
+#include "win.h" //includes "windows.h"
+#include "Windowsx.h" //WM_MOUSEWHEEL
+
+
namespace
{
#ifndef WM_MOUSEHWHEEL //MinGW is clueless...
@@ -25,7 +26,7 @@ LRESULT CALLBACK mouseInputHook(int nCode, WPARAM wParam, LPARAM lParam)
{
//"if nCode is less than zero, the hook procedure must pass the message to the CallNextHookEx function
//without further processing and should return the value returned by CallNextHookEx"
- if (nCode >= 0)
+ if (nCode == HC_ACTION) //the only valid value for this hook type
{
MSG& msgInfo = *reinterpret_cast<MSG*>(lParam);
@@ -37,8 +38,7 @@ LRESULT CALLBACK mouseInputHook(int nCode, WPARAM wParam, LPARAM lParam)
pt.y = GET_Y_LPARAM(msgInfo.lParam); //
//visible child window directly under cursor; attention: not necessarily from our process!
- //http://blogs.msdn.com/b/oldnewthing/archive/2010/12/30/10110077.aspx
- if (HWND hWin = ::WindowFromPoint(pt))
+ if (HWND hWin = ::WindowFromPoint(pt)) //http://blogs.msdn.com/b/oldnewthing/archive/2010/12/30/10110077.aspx
if (msgInfo.hwnd != hWin && ::GetCapture() == nullptr)
{
DWORD winProcessId = 0;
@@ -50,7 +50,6 @@ LRESULT CALLBACK mouseInputHook(int nCode, WPARAM wParam, LPARAM lParam)
}
}
}
-
return ::CallNextHookEx(nullptr, nCode, wParam, lParam);
}
diff --git a/zen/serialize.h b/zen/serialize.h
index d22e3cea..a9238359 100644
--- a/zen/serialize.h
+++ b/zen/serialize.h
@@ -162,7 +162,7 @@ BinContainer loadBinStream(const Zstring& filename) //throw FileError, ErrorNotE
FileInput fileIn(filename); //throw FileError, ErrorNotExisting
BinContainer contOut;
- const size_t blockSize = 64 * 1024;
+ const size_t blockSize = 128 * 1024;
do
{
contOut.resize(contOut.size() + blockSize);
diff --git a/zen/string_base.h b/zen/string_base.h
index c3ddde36..05e5935e 100644
--- a/zen/string_base.h
+++ b/zen/string_base.h
@@ -142,7 +142,8 @@ protected:
static void destroy(Char* ptr)
{
- if (--descr(ptr)->refCount == 0)
+ assert(descr(ptr)->refCount > 0);
+ if (--descr(ptr)->refCount == 0) //operator--() is overloaded to decrement and evaluate in a single atomic operation!
{
descr(ptr)->~Descriptor();
AP::deallocate(descr(ptr));
@@ -213,8 +214,8 @@ public:
Char* end ();
const Char* begin() const;
const Char* end () const;
- const Char* cbegin() const { return begin(); }
- const Char* cend () const { return end(); }
+ const Char* cbegin() const { return begin(); }
+ const Char* cend () const { return end(); }
//std::string functions
size_t length() const;
@@ -268,10 +269,15 @@ template <class Char, template <class, class> class SP, class AP> bool operator<
template <class Char, template <class, class> class SP, class AP> bool operator<(const Zbase<Char, SP, AP>& lhs, const Char* rhs);
template <class Char, template <class, class> class SP, class AP> bool operator<(const Char* lhs, const Zbase<Char, SP, AP>& rhs);
-//rvalue references: unified first argument!
-template <class Char, template <class, class> class SP, class AP> inline Zbase<Char, SP, AP> operator+(Zbase<Char, SP, AP> lhs, const Zbase<Char, SP, AP>& rhs) { return lhs += rhs; }
-template <class Char, template <class, class> class SP, class AP> inline Zbase<Char, SP, AP> operator+(Zbase<Char, SP, AP> lhs, const Char* rhs) { return lhs += rhs; }
-template <class Char, template <class, class> class SP, class AP> inline Zbase<Char, SP, AP> operator+(Zbase<Char, SP, AP> lhs, Char rhs) { return lhs += rhs; }
+template <class Char, template <class, class> class SP, class AP> inline Zbase<Char, SP, AP> operator+(const Zbase<Char, SP, AP>& lhs, const Zbase<Char, SP, AP>& rhs) { return Zbase<Char, SP, AP>(lhs) += rhs; }
+template <class Char, template <class, class> class SP, class AP> inline Zbase<Char, SP, AP> operator+(const Zbase<Char, SP, AP>& lhs, const Char* rhs) { return Zbase<Char, SP, AP>(lhs) += rhs; }
+template <class Char, template <class, class> class SP, class AP> inline Zbase<Char, SP, AP> operator+(const Zbase<Char, SP, AP>& lhs, Char rhs) { return Zbase<Char, SP, AP>(lhs) += rhs; }
+
+//don't use unified first argument but save one move-construction in the r-value case instead!
+template <class Char, template <class, class> class SP, class AP> inline Zbase<Char, SP, AP> operator+(Zbase<Char, SP, AP>&& lhs, const Zbase<Char, SP, AP>& rhs) { return std::move(lhs += rhs); } //is the move really needed?
+template <class Char, template <class, class> class SP, class AP> inline Zbase<Char, SP, AP> operator+(Zbase<Char, SP, AP>&& lhs, const Char* rhs) { return std::move(lhs += rhs); } //lhs, is an l-vlaue in the function body...
+template <class Char, template <class, class> class SP, class AP> inline Zbase<Char, SP, AP> operator+(Zbase<Char, SP, AP>&& lhs, Char rhs) { return std::move(lhs += rhs); } //and not a local variable => no copy elision
+
template <class Char, template <class, class> class SP, class AP> inline Zbase<Char, SP, AP> operator+( Char lhs, const Zbase<Char, SP, AP>& rhs) { return Zbase<Char, SP, AP>(lhs) += rhs; }
template <class Char, template <class, class> class SP, class AP> inline Zbase<Char, SP, AP> operator+(const Char* lhs, const Zbase<Char, SP, AP>& rhs) { return Zbase<Char, SP, AP>(lhs) += rhs; }
diff --git a/zen/string_tools.h b/zen/string_tools.h
index 32d12119..c0bb1039 100644
--- a/zen/string_tools.h
+++ b/zen/string_tools.h
@@ -286,34 +286,45 @@ template <class S, class T, class U> inline
S replaceCpy(const S& str, const T& oldTerm, const U& newTerm, bool replaceAll)
{
assert_static(IsStringLike<T>::value && IsStringLike<U>::value);
-
typedef typename GetCharType<S>::Type CharType;
const size_t oldLen = strLength(oldTerm);
- const size_t newLen = strLength(newTerm);
-
- S output;
+ if (oldLen == 0)
+ {
+ assert(false);
+ return str;
+ }
const CharType* strPos = strBegin(str);
const CharType* const strEnd = strPos + strLength(str);
const CharType* const oldBegin = strBegin(oldTerm);
+ const CharType* const oldEnd = oldBegin + oldLen;
+
+ //optimize "oldTerm not found"
+ const CharType* strMatch = std::search(strPos, strEnd,
+ oldBegin, oldEnd);
+ if (strMatch == strEnd)
+ return str;
+
+ const size_t newLen = strLength(newTerm);
const CharType* const newBegin = strBegin(newTerm);
+ S output;
for (;;)
{
- const CharType* ptr = std::search(strPos, strEnd,
- oldBegin, oldBegin + oldLen);
- if (ptr == strEnd)
- break;
-
- implementation::stringAppend(output, strPos, ptr - strPos);
+ implementation::stringAppend(output, strPos, strMatch - strPos);
implementation::stringAppend(output, newBegin, newLen);
- strPos = ptr + oldLen;
+ strPos = strMatch + oldLen;
if (!replaceAll)
break;
+
+ strMatch = std::search(strPos, strEnd,
+ oldBegin, oldEnd);
+ if (strMatch == strEnd)
+ break;
}
implementation::stringAppend(output, strPos, strEnd - strPos);
diff --git a/zen/thread.h b/zen/thread.h
index 432a521e..f00c0298 100644
--- a/zen/thread.h
+++ b/zen/thread.h
@@ -9,7 +9,6 @@
//temporary solution until C++11 thread becomes fully available
#include <memory>
-#include "fixed_list.h"
//fix this pathetic boost thread warning mess
#ifdef __MINGW32__
@@ -64,12 +63,12 @@ public:
bool timedWait(const Duration& duration) const; //true: "get()" is ready, false: time elapsed
//return first value or none if all jobs failed; blocks until result is ready!
- std::unique_ptr<T> get() const; //must be called only once!
+ std::unique_ptr<T> get() const; //may be called only once!
private:
class AsyncResult;
- FixedList<boost::thread> workload; //note: we cannot use std::vector<boost::thread>: compiler error on GCC 4.7, probably a boost screw-up
std::shared_ptr<AsyncResult> result;
+ size_t jobsTotal;
};
@@ -93,11 +92,12 @@ private:
#endif
template <class T, class Function> inline
-auto async2(Function fun) -> boost::unique_future<T> //workaround VS2010 bug: bool (*fun)(); decltype(fun()) == int!
+auto async2(Function fun) -> boost::unique_future<T> //support for workaround of VS2010 bug: bool (*fun)(); decltype(fun()) == int!
{
- boost::packaged_task<T> pt([=] { return fun(); });
+ boost::packaged_task<T> pt(fun);
auto fut = pt.get_future();
- boost::thread(std::move(pt));
+ boost::thread t(std::move(pt));
+ t.detach(); //we have to be explicit since C++11: [thread.thread.destr] ~thread() calls std::terminate() if joinable()!!!
return std::move(fut); //compiler error without "move", why needed???
}
@@ -181,28 +181,27 @@ private:
template <class T> inline
-RunUntilFirstHit<T>::RunUntilFirstHit() : result(std::make_shared<AsyncResult>()) {}
+RunUntilFirstHit<T>::RunUntilFirstHit() : result(std::make_shared<AsyncResult>()), jobsTotal(0) {}
template <class T>
template <class Fun> inline
void RunUntilFirstHit<T>::addJob(Fun f) //f must return a std::unique_ptr<T> containing a value on success
{
- auto result2 = result; //VC11: this is ridiculous!!!
- workload.emplace_back([result2, f]
- {
- result2->reportFinished(f());
- });
+ auto result2 = result; //MSVC2010: this is ridiculous!!!
+ boost::thread t([result2, f] { result2->reportFinished(f()); });
+ ++jobsTotal;
+ t.detach(); //we have to be explicit since C++11: [thread.thread.destr] ~thread() calls std::terminate() if joinable()!!!
}
template <class T>
template <class Duration> inline
-bool RunUntilFirstHit<T>::timedWait(const Duration& duration) const { return result->waitForResult(workload.size(), duration); }
+bool RunUntilFirstHit<T>::timedWait(const Duration& duration) const { return result->waitForResult(jobsTotal, duration); }
template <class T> inline
-std::unique_ptr<T> RunUntilFirstHit<T>::get() const { return result->getResult(workload.size()); }
+std::unique_ptr<T> RunUntilFirstHit<T>::get() const { return result->getResult(jobsTotal); }
}
#endif //BOOST_THREAD_WRAP_H
diff --git a/zen/tick_count.h b/zen/tick_count.h
index 4f1a047e..98e59ae5 100644
--- a/zen/tick_count.h
+++ b/zen/tick_count.h
@@ -8,8 +8,10 @@
#define ZEN_TICK_COUNT_HEADER_3807326
#include <cstdint>
+#include <algorithm>
#include "type_traits.h"
#include "assert_static.h"
+#include <cmath>
#ifdef FFS_WIN
#include "win.h" //includes "windows.h"
@@ -21,7 +23,7 @@ namespace zen
{
//a portable "GetTickCount()" using "wall time equivalent" - e.g. no jumps due to ntp time corrections
class TickVal;
-std::int64_t operator-(const TickVal& lhs, const TickVal& rhs);
+std::int64_t dist(const TickVal& lhs, const TickVal& rhs); //use absolute difference for paranoid security: even QueryPerformanceCounter "wraps-around" at *some* time
std::int64_t ticksPerSec(); //return 0 on error
TickVal getTicks(); //return invalid value on error: !TickVal::isValid()
@@ -40,10 +42,6 @@ TickVal getTicks(); //return invalid value on error: !TickVal::isValid()
-
-
-
-
//############################ implementation ##############################
class TickVal
{
@@ -58,19 +56,31 @@ public:
explicit TickVal(const NativeVal& val) : val_(val) {}
inline friend
- std::int64_t operator-(const TickVal& lhs, const TickVal& rhs)
+ std::int64_t dist(const TickVal& lhs, const TickVal& rhs)
{
#ifdef FFS_WIN
assert_static(IsSignedInt<decltype(lhs.val_.QuadPart)>::value);
- return lhs.val_.QuadPart - rhs.val_.QuadPart;
+ return std::abs(lhs.val_.QuadPart - rhs.val_.QuadPart);
#elif defined FFS_LINUX
assert_static(IsSignedInt<decltype(lhs.val_.tv_sec)>::value);
assert_static(IsSignedInt<decltype(lhs.val_.tv_nsec)>::value);
- return static_cast<std::int64_t>(lhs.val_.tv_sec - rhs.val_.tv_sec) * 1000000000.0 + lhs.val_.tv_nsec - rhs.val_.tv_nsec;
+ return std::abs(static_cast<std::int64_t>(lhs.val_.tv_sec - rhs.val_.tv_sec) * 1000000000.0 + (lhs.val_.tv_nsec - rhs.val_.tv_nsec));
+#endif
+ }
+
+ inline friend
+ bool operator<(const TickVal& lhs, const TickVal& rhs) //evaluate directly rather than reuse operator-
+ {
+#ifdef FFS_WIN
+ return lhs.val_.QuadPart < rhs.val_.QuadPart;
+#elif defined FFS_LINUX
+ if (lhs.val_.tv_sec != rhs.val_.tv_sec)
+ return lhs.val_.tv_sec < rhs.val_.tv_sec;
+ return lhs.val_.tv_nsec < rhs.val_.tv_nsec;
#endif
}
- bool isValid() const { return *this - TickVal() != 0; }
+ bool isValid() const { return dist(*this, TickVal()) != 0; }
private:
NativeVal val_;
@@ -82,7 +92,7 @@ std::int64_t ticksPerSec() //return 0 on error
{
#ifdef FFS_WIN
LARGE_INTEGER frequency = {};
- if (!::QueryPerformanceFrequency(&frequency))
+ if (!::QueryPerformanceFrequency(&frequency)) //MSDN promises: "The frequency cannot change while the system is running."
return 0;
assert_static(sizeof(std::int64_t) >= sizeof(frequency.QuadPart));
return frequency.QuadPart;
diff --git a/zen/time.h b/zen/time.h
index aa1613cc..474c48c5 100644
--- a/zen/time.h
+++ b/zen/time.h
@@ -69,15 +69,6 @@ bool parseTime(const String& format, const String& str, TimeComp& comp); //simil
-
-
-
-
-
-
-
-
-
//############################ implementation ##############################
namespace implementation
{
diff --git a/zen/zstring.h b/zen/zstring.h
index 9d93d2d3..df96df7b 100644
--- a/zen/zstring.h
+++ b/zen/zstring.h
@@ -7,8 +7,8 @@
#ifndef ZSTRING_H_INCLUDED
#define ZSTRING_H_INCLUDED
-#include "string_base.h"
#include <cstring> //strcmp()
+#include "string_base.h"
#ifndef NDEBUG
#include "thread.h" //includes <boost/thread.hpp>
bgstack15