summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorDaniel Wilhelm <daniel@wili.li>2014-04-18 17:23:19 +0200
committerDaniel Wilhelm <daniel@wili.li>2014-04-18 17:23:19 +0200
commit0887aee8c54d0ed51bb2031431e2bcdafebb4c6e (patch)
tree69537ceb9787bb25ac363cc4e6cdaf0804d78363 /lib
parent5.12 (diff)
downloadFreeFileSync-0887aee8c54d0ed51bb2031431e2bcdafebb4c6e.tar.gz
FreeFileSync-0887aee8c54d0ed51bb2031431e2bcdafebb4c6e.tar.bz2
FreeFileSync-0887aee8c54d0ed51bb2031431e2bcdafebb4c6e.zip
5.13
Diffstat (limited to 'lib')
-rw-r--r--lib/Batch.icobin117121 -> 103260 bytes
-rw-r--r--lib/FreeFileSync.icobin132428 -> 114124 bytes
-rw-r--r--lib/ShadowCopy/LockFile.cpp43
-rw-r--r--lib/ShadowCopy/shadow.cpp4
-rw-r--r--lib/SyncDB.icobin133875 -> 111496 bytes
-rw-r--r--lib/Thumbnail/Thumbnail.vcxproj18
-rw-r--r--lib/Thumbnail/thumbnail.cpp11
-rw-r--r--lib/db_file.cpp10
-rw-r--r--lib/dir_exist_async.h55
-rw-r--r--lib/dir_lock.cpp201
-rw-r--r--lib/dir_lock.h2
-rw-r--r--lib/ffs_paths.cpp146
-rw-r--r--lib/ffs_paths.h106
-rw-r--r--lib/hard_filter.cpp6
-rw-r--r--lib/help_provider.h28
-rw-r--r--lib/icon_buffer.cpp108
-rw-r--r--lib/icon_buffer.h6
-rw-r--r--lib/localization.cpp29
-rw-r--r--lib/lock_holder.h21
-rw-r--r--lib/parallel_scan.cpp37
-rw-r--r--lib/perf_check.cpp43
-rw-r--r--lib/perf_check.h6
-rw-r--r--lib/process_xml.cpp6
-rw-r--r--lib/process_xml.h20
-rw-r--r--lib/resolve_path.cpp86
-rw-r--r--lib/resources.cpp10
-rw-r--r--lib/shadow.cpp4
-rw-r--r--lib/status_handler.cpp2
-rw-r--r--lib/xml_base.cpp2
29 files changed, 521 insertions, 489 deletions
diff --git a/lib/Batch.ico b/lib/Batch.ico
index f742b9a3..d27f35d1 100644
--- a/lib/Batch.ico
+++ b/lib/Batch.ico
Binary files differ
diff --git a/lib/FreeFileSync.ico b/lib/FreeFileSync.ico
index 02925b10..88f656ee 100644
--- a/lib/FreeFileSync.ico
+++ b/lib/FreeFileSync.ico
Binary files differ
diff --git a/lib/ShadowCopy/LockFile.cpp b/lib/ShadowCopy/LockFile.cpp
deleted file mode 100644
index a09e7f94..00000000
--- a/lib/ShadowCopy/LockFile.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-// **************************************************************************
-// * 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 *
-// **************************************************************************
-
-
-#include <string>
-#include <iostream>
-#define WIN32_LEAN_AND_MEAN
-#include "windows.h"
-
-int wmain(int argc, wchar_t* argv[])
-{
- if (argc <= 1)
- {
- std::wcout << "Please enter the filename to be locked as %1 parameter!" << "\n\n";
- system("pause");
- return -1;
- }
- std::wstring filename = argv[1];
-
- HANDLE hFile = ::CreateFile(filename.c_str(),
- GENERIC_READ,
- 0, //obtain *exclusive* lock on test file
- nullptr,
- OPEN_EXISTING,
- FILE_FLAG_BACKUP_SEMANTICS,
- nullptr);
- if (hFile == INVALID_HANDLE_VALUE)
- {
- std::wcout << "Error obtaining exclusive lock on test file: " << filename << "\n\n";
- system("pause");
- return -1;
- }
-
- std::wcout << "File " << filename << " is locked! Press a key to unlock." << "\n\n";
- system("pause");
-
- ::CloseHandle(hFile);
- return 0;
-}
-
diff --git a/lib/ShadowCopy/shadow.cpp b/lib/ShadowCopy/shadow.cpp
index 93026039..65387f3b 100644
--- a/lib/ShadowCopy/shadow.cpp
+++ b/lib/ShadowCopy/shadow.cpp
@@ -151,8 +151,8 @@ shadow::ShadowHandle shadow::createShadowCopy(const wchar_t* volumeName)
{
try
{
- ShadowData result = ::createShadowCopy(volumeName); //shadow handle owned by caller! throw ComError
- return new ShadowData(result); //std::bad_alloc?
+ ShadowData result = ::createShadowCopy(volumeName); //throw ComError
+ return new ShadowData(result); //shadow handle owned by caller! std::bad_alloc?
}
catch (const zen::ComError& e)
{
diff --git a/lib/SyncDB.ico b/lib/SyncDB.ico
index 6fc5d264..9740a338 100644
--- a/lib/SyncDB.ico
+++ b/lib/SyncDB.ico
Binary files differ
diff --git a/lib/Thumbnail/Thumbnail.vcxproj b/lib/Thumbnail/Thumbnail.vcxproj
index 3baa2d61..de9b22ae 100644
--- a/lib/Thumbnail/Thumbnail.vcxproj
+++ b/lib/Thumbnail/Thumbnail.vcxproj
@@ -87,7 +87,7 @@
</BuildLog>
<ClCompile>
<Optimization>Disabled</Optimization>
- <PreprocessorDefinitions>_DEBUG;_WINDOWS;_USRDLL;THUMBNAIL_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>FFS_WIN;_DEBUG;_WINDOWS;_USRDLL;THUMBNAIL_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>true</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
@@ -155,7 +155,7 @@
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>NDEBUG;_WINDOWS;_USRDLL;THUMBNAIL_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>FFS_WIN;NDEBUG;_WINDOWS;_USRDLL;THUMBNAIL_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeader>
@@ -221,20 +221,6 @@
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\..\zen\debug_memory_leaks.cpp" />
- <ClCompile Include="dll_main.cpp">
- <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
- </PrecompiledHeader>
- <CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</CompileAsManaged>
- <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
- </PrecompiledHeader>
- <CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</CompileAsManaged>
- <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
- </PrecompiledHeader>
- <CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</CompileAsManaged>
- <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
- </PrecompiledHeader>
- <CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</CompileAsManaged>
- </ClCompile>
<ClCompile Include="thumbnail.cpp" />
</ItemGroup>
<ItemGroup>
diff --git a/lib/Thumbnail/thumbnail.cpp b/lib/Thumbnail/thumbnail.cpp
index 0c7162b9..284d1093 100644
--- a/lib/Thumbnail/thumbnail.cpp
+++ b/lib/Thumbnail/thumbnail.cpp
@@ -118,8 +118,13 @@ thumb::HICON thumb::getThumbnail(const wchar_t* filename, int requestedSize) //r
&bmpInfo) == 0) //__out LPVOID lpvObject
return nullptr;
- HBITMAP bitmapMask = ::CreateCompatibleBitmap(::GetDC(nullptr), bmpInfo.bmWidth, bmpInfo.bmHeight);
- if (bitmapMask == 0)
+ HDC hDC = ::GetDC(nullptr);
+ if (!hDC)
+ return nullptr;
+ ZEN_ON_SCOPE_EXIT(::ReleaseDC(nullptr, hDC));
+
+ HBITMAP bitmapMask = ::CreateCompatibleBitmap(hDC, bmpInfo.bmWidth, bmpInfo.bmHeight);
+ if (!bitmapMask)
return nullptr;
ZEN_ON_SCOPE_EXIT(::DeleteObject(bitmapMask));
@@ -156,7 +161,7 @@ thumb::HICON thumb::getIconByIndex(int iconIndex, int shilIconType) //return 0 o
::HICON hIcon = nullptr; //perf: 1,5 ms - the dominant block
{
HRESULT hr = imageList->GetIcon(iconIndex, // [in] int i,
- hasAlpha ? ILD_IMAGE : ILD_NORMAL, // [in] UINT flags,
+ (hasAlpha ? ILD_IMAGE : ILD_NORMAL), // [in] UINT flags,
//ILD_IMAGE -> do not draw mask - fixes glitch with ILD_NORMAL where both mask *and* alpha channel are applied, e.g. for ffs_batch and folder icon
//other flags: http://msdn.microsoft.com/en-us/library/windows/desktop/bb775230(v=vs.85).aspx
&hIcon); // [out] HICON *picon
diff --git a/lib/db_file.cpp b/lib/db_file.cpp
index 7f8da45a..368cf56b 100644
--- a/lib/db_file.cpp
+++ b/lib/db_file.cpp
@@ -44,7 +44,7 @@ Zstring getDBFilename(const BaseDirMapping& baseMap, bool tempfile = false)
//make sure they end with ".ffs_db". These files will be excluded from comparison
#ifdef FFS_WIN
Zstring dbname = Zstring(Zstr("sync")) + (tempfile ? Zstr(".tmp") : Zstr("")) + SYNC_DB_FILE_ENDING;
-#elif defined FFS_LINUX
+#elif defined FFS_LINUX || defined FFS_MAC
//files beginning with dots are hidden e.g. in Nautilus
Zstring dbname = Zstring(Zstr(".sync")) + (tempfile ? Zstr(".tmp") : Zstr("")) + SYNC_DB_FILE_ENDING;
#endif
@@ -452,7 +452,7 @@ private:
auto it = map.lower_bound(key);
if (it != map.end() && !(map.key_comp()(key, it->first)))
{
-#ifdef FFS_WIN //caveat: key might need to be updated, too, if there is a change in short name case!!!
+#if defined FFS_WIN || defined FFS_MAC //caveat: key might need to be updated, too, if there is a change in short name case!!!
if (it->first != key)
{
map.erase(it); //don't fiddle with decrementing "it"! - you might lose while optimizing pointlessly
@@ -567,7 +567,7 @@ private:
auto insertResult = dbDirs.insert(std::make_pair(key, InSyncDir(InSyncDir::STATUS_IN_SYNC))); //get or create
auto it = insertResult.first;
-#ifdef FFS_WIN //caveat: key might need to be updated, too, if there is a change in short name case!!!
+#if defined FFS_WIN || defined FFS_MAC //caveat: key might need to be updated, too, if there is a change in short name case!!!
const bool alreadyExisting = !insertResult.second;
if (alreadyExisting && it->first != key)
{
@@ -620,7 +620,9 @@ private:
const Zstring& shortName = v.first;
return filter_.passDirFilter(parentRelativeNamePf + shortName, nullptr);
//if directory is not included in "currentDirs", it is either not existing anymore, in which case it should be deleted from database
- //or it was excluded via filter, in which case the database entry should be preserved -> we can't tell and need to preserve the old db entry
+ //or it was excluded via filter, in which case the database entry should be preserved
+ //-> we can't tell and need to preserve the old db entry -> all child db elements are preserved since they are not recursed in the loop above!!!
+ //-> no problem with filter logic of excluding complete directory subtrees, if top folder is excluded directly!
});
}
diff --git a/lib/dir_exist_async.h b/lib/dir_exist_async.h
index 306c9ab9..33af8fa8 100644
--- a/lib/dir_exist_async.h
+++ b/lib/dir_exist_async.h
@@ -9,31 +9,62 @@
#include <zen/thread.h>
#include <zen/file_handling.h>
-#include "process_callback.h"
#include <zen/file_error.h>
+#include "process_callback.h"
#include "resolve_path.h"
-//dir existence checking may hang for non-existent network drives => run asynchronously and update UI!
namespace
{
-bool dirExistsUpdating(const Zstring& dirname, bool allowUserInteraction, ProcessCallback& procCallback)
+//directory existence checking may hang for non-existent network drives => run asynchronously and update UI!
+//- check existence of all directories in parallel! (avoid adding up search times if multiple network drives are not reachable)
+//- add reasonable time-out time!
+std::set<Zstring, LessFilename> getExistingDirsUpdating(const std::vector<Zstring>& dirnames, bool allowUserInteraction, ProcessCallback& procCallback)
{
using namespace zen;
- procCallback.reportStatus(replaceCpy(_("Searching for folder %x..."), L"%x", fmtFileName(dirname), false));
+ std::list<boost::unique_future<bool>> dirEx;
- auto ft = async([=]() -> bool
+ std::for_each(dirnames.begin(), dirnames.end(),
+ [&](const Zstring& dirname)
{
+ dirEx.push_back(zen::async2<bool>([=]() -> bool
+ {
+ if (dirname.empty())
+ return false;
#ifdef FFS_WIN
- //1. login to network share, if necessary
- loginNetworkShare(dirname, allowUserInteraction);
+ //1. login to network share, if necessary
+ loginNetworkShare(dirname, allowUserInteraction);
#endif
- //2. check dir existence
- return zen::dirExists(dirname);
+ //2. check dir existence
+ return dirExists(dirname);
+ }));
});
- while (!ft.timed_wait(boost::posix_time::milliseconds(UI_UPDATE_INTERVAL / 2)))
- procCallback.requestUiRefresh(); //may throw!
- return ft.get();
+
+ std::set<Zstring, LessFilename> output;
+ const boost::system_time endTime = boost::get_system_time() + boost::posix_time::seconds(10); //10 sec should be enough even if Win32 waits much longer
+
+ auto itDirname = dirnames.begin();
+ for (auto it = dirEx.begin(); it != dirEx.end(); ++it, ++itDirname)
+ {
+ procCallback.reportStatus(replaceCpy(_("Searching for folder %x..."), L"%x", fmtFileName(*itDirname), false)); //may throw!
+
+ while (boost::get_system_time() < endTime &&
+ !it->timed_wait(boost::posix_time::milliseconds(UI_UPDATE_INTERVAL / 2)))
+ procCallback.requestUiRefresh(); //may throw!
+
+ if (it->is_ready() && it->get())
+ output.insert(*itDirname);
+ }
+ return output;
+}
+
+
+bool dirExistsUpdating(const Zstring& dirname, bool allowUserInteraction, ProcessCallback& procCallback)
+{
+ std::vector<Zstring> dirnames;
+ dirnames.push_back(dirname);
+ std::set<Zstring, LessFilename> dirsEx = getExistingDirsUpdating(dirnames, allowUserInteraction, procCallback);
+ return dirsEx.find(dirname) != dirsEx.end();
}
}
diff --git a/lib/dir_lock.cpp b/lib/dir_lock.cpp
index 5041b37e..f263d4ac 100644
--- a/lib/dir_lock.cpp
+++ b/lib/dir_lock.cpp
@@ -16,6 +16,7 @@
#include <zen/int64.h>
#include <zen/file_handling.h>
#include <zen/serialize.h>
+#include <zen/optional.h>
#ifdef FFS_WIN
#include <tlhelp32.h>
@@ -24,10 +25,12 @@
#include <Sddl.h> //login sid
#include <Lmcons.h> //UNLEN
-#elif defined FFS_LINUX
-#include <fcntl.h> //::open()
+#elif defined FFS_LINUX || defined FFS_MAC
+#include <fcntl.h> //open()
#include <sys/stat.h> //
-#include <unistd.h>
+#include <unistd.h> //getsid()
+#include <signal.h> //kill()
+#include <pwd.h> //getpwuid_r()
#endif
using namespace zen;
@@ -103,7 +106,7 @@ public:
&bytesWritten, //__out_opt LPDWORD lpNumberOfBytesWritten,
nullptr); //__inout_opt LPOVERLAPPED lpOverlapped
-#elif defined FFS_LINUX
+#elif defined FFS_LINUX || defined FFS_MAC
const int fileHandle = ::open(lockfilename_.c_str(), O_WRONLY | O_APPEND);
if (fileHandle < 0)
return;
@@ -132,7 +135,7 @@ UInt64 getLockFileSize(const Zstring& filename) //throw FileError, ErrorNotExist
return UInt64(fileInfo.nFileSizeLow, fileInfo.nFileSizeHigh);
}
-#elif defined FFS_LINUX
+#elif defined FFS_LINUX || defined FFS_MAC
struct ::stat fileInfo = {};
if (::stat(filename.c_str(), &fileInfo) == 0) //follow symbolic links
return UInt64(fileInfo.st_size);
@@ -159,13 +162,13 @@ Zstring deleteAbandonedLockName(const Zstring& lockfilename) //make sure to NOT
#ifdef FFS_WIN
-std::wstring getLoginSid() //throw FileError
+Zstring getLoginSid() //throw FileError
{
HANDLE hToken = 0;
if (!::OpenProcessToken(::GetCurrentProcess(), //__in HANDLE ProcessHandle,
TOKEN_ALL_ACCESS, //__in DWORD DesiredAccess,
&hToken)) //__out PHANDLE TokenHandle
- throw FileError(_("Cannot get process information.") + L"\n\n" + getLastErrorFormatted());
+ throw FileError(_("Cannot get process information.") + L" (OpenProcessToken)" + L"\n\n" + getLastErrorFormatted());
ZEN_ON_SCOPE_EXIT(::CloseHandle(hToken));
DWORD bufferSize = 0;
@@ -177,7 +180,7 @@ std::wstring getLoginSid() //throw FileError
&buffer[0], //__out_opt LPVOID TokenInformation,
bufferSize, //__in DWORD TokenInformationLength,
&bufferSize)) //__out PDWORD ReturnLength
- throw FileError(_("Cannot get process information.") + L"\n\n" + getLastErrorFormatted());
+ throw FileError(_("Cannot get process information.") + L" (GetTokenInformation)" + L"\n\n" + getLastErrorFormatted());
auto groups = reinterpret_cast<const TOKEN_GROUPS*>(&buffer[0]);
@@ -187,67 +190,126 @@ std::wstring getLoginSid() //throw FileError
LPTSTR sidStr = nullptr;
if (!::ConvertSidToStringSid(groups->Groups[i].Sid, //__in PSID Sid,
&sidStr)) //__out LPTSTR *StringSid
- throw FileError(_("Cannot get process information.") + L"\n\n" + getLastErrorFormatted());
+ throw FileError(_("Cannot get process information.") + L" (ConvertSidToStringSid)" + L"\n\n" + getLastErrorFormatted());
ZEN_ON_SCOPE_EXIT(::LocalFree(sidStr));
return sidStr;
}
- throw FileError(_("Cannot get process information.") + L"\n\n" + getLastErrorFormatted() + L"(no login found)"); //shouldn't happen
+ throw FileError(_("Cannot get process information.") + L" (no login found)" + L"\n\n" + getLastErrorFormatted()); //shouldn't happen
}
#endif
+#ifdef FFS_WIN
+typedef DWORD ProcessId;
+typedef DWORD SessionId;
+#elif defined FFS_LINUX || defined FFS_MAC
+typedef pid_t ProcessId;
+typedef pid_t SessionId;
+#endif
+
+//return ppid on Windows, sid on Linux/Mac, "no value" if process corresponding to "processId" is not existing
+Opt<SessionId> getSessionId(ProcessId processId) //throw FileError
+{
+#ifdef FFS_WIN
+ //note: ::OpenProcess() is no alternative as it may successfully return for crashed processes! -> remark: "WaitForSingleObject" may identify this case!
+ HANDLE snapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, //__in DWORD dwFlags,
+ 0); //__in DWORD th32ProcessID
+ if (snapshot == INVALID_HANDLE_VALUE)
+ throw FileError(_("Cannot get process information.") + L" (CreateToolhelp32Snapshot)" + L"\n\n" + getLastErrorFormatted());
+ ZEN_ON_SCOPE_EXIT(::CloseHandle(snapshot));
+
+ PROCESSENTRY32 processEntry = {};
+ processEntry.dwSize = sizeof(processEntry);
+
+ if (!::Process32First(snapshot, //__in HANDLE hSnapshot,
+ &processEntry)) //__inout LPPROCESSENTRY32 lppe
+ throw FileError(_("Cannot get process information.") + L" (Process32First)" + L"\n\n" + getLastErrorFormatted()); //ERROR_NO_MORE_FILES not possible
+ do
+ {
+ if (processEntry.th32ProcessID == processId) //yes, MSDN says this is the way: http://msdn.microsoft.com/en-us/library/windows/desktop/ms684868(v=vs.85).aspx
+ return processEntry.th32ParentProcessID; //parent id is stable, even if parent process has already terminated!
+ }
+ while (::Process32Next(snapshot, &processEntry));
+ if (::GetLastError() != ERROR_NO_MORE_FILES) //yes, they call it "files"
+ throw FileError(_("Cannot get process information.") + L" (Process32Next)" + L"\n\n" + getLastErrorFormatted());
+
+ return NoValue();
+
+#elif defined FFS_LINUX || defined FFS_MAC
+ if (::kill(processId, 0) != 0) //sig == 0: no signal sent, just existence check
+ return NoValue();
+
+ pid_t procSid = ::getsid(processId); //NOT to be confused with "login session", e.g. not stable on OS X!!!
+ if (procSid == -1)
+ throw FileError(_("Cannot get process information.") + L" (getsid)" + L"\n\n" + getLastErrorFormatted());
+
+ return procSid;
+#endif
+}
+
+
class FromCurrentProcess {}; //tag
struct LockInformation //throw FileError
{
explicit LockInformation(FromCurrentProcess) :
lockId(zen::generateGUID()),
+ sessionId(), //dummy value
#ifdef FFS_WIN
- sessionId(utfCvrtTo<std::string>(getLoginSid())), //throw FileError
processId(::GetCurrentProcessId()) //never fails
{
DWORD bufferSize = 0;
::GetComputerNameEx(ComputerNameDnsFullyQualified, nullptr, &bufferSize); //get required buffer size
std::vector<wchar_t> buffer(bufferSize);
- if (!GetComputerNameEx(ComputerNameDnsFullyQualified, //__in COMPUTER_NAME_FORMAT NameType,
- &buffer[0], //__out LPTSTR lpBuffer,
- &bufferSize)) //__inout LPDWORD lpnSize
- throw FileError(_("Cannot get process information.") + L"\n\n" + getLastErrorFormatted());
+ if (!::GetComputerNameEx(ComputerNameDnsFullyQualified, //__in COMPUTER_NAME_FORMAT NameType,
+ &buffer[0], //__out LPTSTR lpBuffer,
+ &bufferSize)) //__inout LPDWORD lpnSize
+ throw FileError(_("Cannot get process information.") + L" (GetComputerNameEx)" + L"\n\n" + getLastErrorFormatted());
computerName = "Windows." + utfCvrtTo<std::string>(&buffer[0]);
bufferSize = UNLEN + 1;
buffer.resize(bufferSize);
if (!::GetUserName(&buffer[0], //__out LPTSTR lpBuffer,
&bufferSize)) //__inout LPDWORD lpnSize
- throw FileError(_("Cannot get process information.") + L"\n\n" + getLastErrorFormatted());
+ throw FileError(_("Cannot get process information.") + L" (GetUserName)" + L"\n\n" + getLastErrorFormatted());
userId = utfCvrtTo<std::string>(&buffer[0]);
- }
-#elif defined FFS_LINUX
+
+#elif defined FFS_LINUX || defined FFS_MAC
processId(::getpid()) //never fails
{
std::vector<char> buffer(10000);
- if (::gethostname(&buffer[0], buffer.size()) != 0)
- throw FileError(_("Cannot get process information.") + L"\n\n" + getLastErrorFormatted());
+ if (::gethostname(&buffer[0], buffer.size()) != 0)
+ throw FileError(_("Cannot get process information.") + L" (gethostname)" + L"\n\n" + getLastErrorFormatted());
computerName += "Linux."; //distinguish linux/windows lock files
computerName += &buffer[0];
if (::getdomainname(&buffer[0], buffer.size()) != 0)
- throw FileError(_("Cannot get process information.") + L"\n\n" + getLastErrorFormatted());
+ throw FileError(_("Cannot get process information.") + L" (getdomainname)" + L"\n\n" + getLastErrorFormatted());
computerName += ".";
computerName += &buffer[0];
- uid_t userIdTmp = ::getuid(); //never fails
- userId.assign(reinterpret_cast<const char*>(&userIdTmp), sizeof(userIdTmp));
+ const uid_t userIdNo = ::getuid(); //never fails
+ userId.assign(reinterpret_cast<const char*>(&userIdNo), sizeof(userIdNo));
+
+ //the id alone is not very distinctive, e.g. often 1000 on Ubuntu => add name
+ buffer.resize(std::max<long>(buffer.size(), ::sysconf(_SC_GETPW_R_SIZE_MAX))); //::sysconf may return long(-1)
+ struct passwd buffer2 = {};
+ struct passwd* pwsEntry = nullptr;
+ if (::getpwuid_r(userIdNo, &buffer2, &buffer[0], buffer.size(), &pwsEntry) != 0) //getlogin() is deprecated and not working on Ubuntu at all!!!
+ throw FileError(_("Cannot get process information.") + L" (getpwuid_r)" + L"\n\n" + getLastErrorFormatted());
+ if (!pwsEntry)
+ throw FileError(_("Cannot get process information.") + L" (no login found)" + L"\n\n" + getLastErrorFormatted());
+ userId += pwsEntry->pw_name;
+#endif
- pid_t sessionIdTmp = ::getsid(0); //new after each restart!
- if (sessionIdTmp == -1)
- throw FileError(_("Cannot get process information.") + L"\n\n" + getLastErrorFormatted());
- sessionId.assign(reinterpret_cast<const char*>(&sessionIdTmp), sizeof(sessionIdTmp));
+ Opt<SessionId> sessionIdTmp = getSessionId(processId); //throw FileError
+ if (!sessionIdTmp)
+ throw FileError(_("Cannot get process information.") + L" (getSessionId)" + L"\n\n" + getLastErrorFormatted());
+ sessionId = *sessionIdTmp;
}
-#endif
explicit LockInformation(BinStreamIn& stream) //throw UnexpectedEndOfStreamError
{
@@ -262,8 +324,8 @@ struct LockInformation //throw FileError
lockId = readContainer<std::string>(stream); //
computerName = readContainer<std::string>(stream); //UnexpectedEndOfStreamError
userId = readContainer<std::string>(stream); //
- sessionId = readContainer<std::string>(stream); //
- processId = static_cast<decltype(processId)>(readNumber<std::uint64_t>(stream)); //[!] conversion
+ sessionId = static_cast<SessionId>(readNumber<std::uint64_t>(stream)); //[!] conversion
+ processId = static_cast<ProcessId>(readNumber<std::uint64_t>(stream)); //[!] conversion
}
void toStream(BinStreamOut& stream) const //throw ()
@@ -271,25 +333,25 @@ struct LockInformation //throw FileError
writeArray(stream, LOCK_FORMAT_DESCR, sizeof(LOCK_FORMAT_DESCR));
writeNumber<boost::int32_t>(stream, LOCK_FORMAT_VER);
- assert_static(sizeof(processId) <= sizeof(std::uint64_t)); //ensure portability
+ assert_static(sizeof(processId) <= sizeof(std::uint64_t)); //ensure cross-platform compatibility!
+ assert_static(sizeof(sessionId) <= sizeof(std::uint64_t)); //
writeContainer(stream, lockId);
writeContainer(stream, computerName);
writeContainer(stream, userId);
- writeContainer(stream, sessionId);
+ writeNumber<std::uint64_t>(stream, sessionId);
writeNumber<std::uint64_t>(stream, processId);
}
std::string lockId; //16 byte GUID - a universal identifier for this lock (no matter what the path is, considering symlinks, distributed network, etc.)
+ //identify local computer
std::string computerName; //format: HostName.DomainName
- std::string userId; //non-readable!
- std::string sessionId; //
-#ifdef FFS_WIN
- DWORD processId;
-#elif defined FFS_LINUX
- pid_t processId;
-#endif
+ std::string userId;
+
+ //identify running process
+ SessionId sessionId; //Windows: parent process id; Linux/OS X: session of the process, NOT the user
+ ProcessId processId;
};
@@ -325,71 +387,40 @@ std::string retrieveLockId(const Zstring& lockfilename) //throw FileError, Error
}
-//true: process not available, false: cannot say anything
enum ProcessStatus
{
PROC_STATUS_NOT_RUNNING,
PROC_STATUS_RUNNING,
PROC_STATUS_ITS_US,
- PROC_STATUS_NO_IDEA
+ PROC_STATUS_CANT_TELL
};
+
ProcessStatus getProcessStatus(const LockInformation& lockInfo) //throw FileError
{
const LockInformation localInfo((FromCurrentProcess())); //throw FileError
if (lockInfo.computerName != localInfo.computerName ||
lockInfo.userId != localInfo.userId) //another user may run a session right now!
- return PROC_STATUS_NO_IDEA; //lock owned by different computer in this network
+ return PROC_STATUS_CANT_TELL; //lock owned by different computer in this network
- if (lockInfo.sessionId != localInfo.sessionId)
- return PROC_STATUS_NOT_RUNNING; //different session but same user? there can be only one
-
- if (lockInfo.processId == localInfo.processId) //obscure, but possible: deletion failed or a lock file is "stolen" and put back while the program is running
+ if (lockInfo.sessionId == localInfo.sessionId &&
+ lockInfo.processId == localInfo.processId) //obscure, but possible: deletion failed or a lock file is "stolen" and put back while the program is running
return PROC_STATUS_ITS_US;
-#ifdef FFS_WIN
- //note: ::OpenProcess() is no alternative as it may successfully return for crashed processes! -> remark: "WaitForSingleObject" may identify this case!
- HANDLE snapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, //__in DWORD dwFlags,
- 0); //__in DWORD th32ProcessID
- if (snapshot == INVALID_HANDLE_VALUE)
- return PROC_STATUS_NO_IDEA;
- ZEN_ON_SCOPE_EXIT(::CloseHandle(snapshot));
-
- PROCESSENTRY32 processEntry = {};
- processEntry.dwSize = sizeof(processEntry);
-
- if (!::Process32First(snapshot, //__in HANDLE hSnapshot,
- &processEntry)) //__inout LPPROCESSENTRY32 lppe
- return PROC_STATUS_NO_IDEA; //ERROR_NO_MORE_FILES not possible
- do
- {
- if (processEntry.th32ProcessID == lockInfo.processId)
- return PROC_STATUS_RUNNING; //process still running
- }
- while (::Process32Next(snapshot, &processEntry));
- if (::GetLastError() != ERROR_NO_MORE_FILES) //yes, they call it "files"
- return PROC_STATUS_NO_IDEA;
-
+ if (Opt<SessionId> sessionId = getSessionId(lockInfo.processId)) //throw FileError
+ return *sessionId == lockInfo.sessionId ? PROC_STATUS_RUNNING : PROC_STATUS_NOT_RUNNING;
return PROC_STATUS_NOT_RUNNING;
-
-#elif defined FFS_LINUX
- if (lockInfo.processId <= 0 || lockInfo.processId >= 65536)
- return PROC_STATUS_NO_IDEA; //invalid process id
-
- return zen::dirExists("/proc/" + zen::numberTo<Zstring>(lockInfo.processId)) ? PROC_STATUS_RUNNING : PROC_STATUS_NOT_RUNNING;
-#endif
}
const std::int64_t TICKS_PER_SEC = ticksPerSec(); //= 0 on error
-
void waitOnDirLock(const Zstring& lockfilename, DirLockCallback* callback) //throw FileError
{
const std::wstring infoMsg = replaceCpy(_("Waiting while directory is locked (%x)..."), L"%x", fmtFileName(lockfilename));
if (callback)
- callback->reportInfo(infoMsg);
+ callback->reportStatus(infoMsg);
//---------------------------------------------------------------
try
{
@@ -407,7 +438,7 @@ void waitOnDirLock(const Zstring& lockfilename, DirLockCallback* callback) //thr
lockOwnderDead = true;
break;
case PROC_STATUS_RUNNING:
- case PROC_STATUS_NO_IDEA:
+ case PROC_STATUS_CANT_TELL:
break;
}
}
@@ -462,10 +493,10 @@ void waitOnDirLock(const Zstring& lockfilename, DirLockCallback* callback) //thr
{
const int remainingSeconds = std::max<int>(0, DETECT_ABANDONED_INTERVAL - dist(lastLifeSign, getTicks()) / TICKS_PER_SEC);
const std::wstring remSecMsg = replaceCpy(_P("1 sec", "%x sec", remainingSeconds), L"%x", numberTo<std::wstring>(remainingSeconds));
- callback->reportInfo(infoMsg + L" " + remSecMsg);
+ callback->reportStatus(infoMsg + L" " + remSecMsg);
}
else
- callback->reportInfo(infoMsg); //emit a message in any case (might clear other one)
+ callback->reportStatus(infoMsg); //emit a message in any case (might clear other one)
}
}
}
@@ -510,7 +541,7 @@ bool tryLock(const Zstring& lockfilename) //throw FileError
::SetFileAttributes(applyLongPathPrefix(lockfilename).c_str(), FILE_ATTRIBUTE_HIDDEN); //(try to) hide it
-#elif defined FFS_LINUX
+#elif defined FFS_LINUX || defined FFS_MAC
//O_EXCL contains a race condition on NFS file systems: http://linux.die.net/man/2/open
::umask(0); //important! -> why?
const int fileHandle = ::open(lockfilename.c_str(), O_CREAT | O_WRONLY | O_EXCL, S_IRWXU | S_IRWXG | S_IRWXO);
@@ -538,7 +569,7 @@ bool tryLock(const Zstring& lockfilename) //throw FileError
class DirLock::SharedDirLock
{
public:
- SharedDirLock(const Zstring& lockfilename, DirLockCallback* callback = nullptr) : //throw FileError
+ SharedDirLock(const Zstring& lockfilename, DirLockCallback* callback) : //throw FileError
lockfilename_(lockfilename)
{
while (!::tryLock(lockfilename)) //throw FileError
@@ -560,7 +591,6 @@ private:
SharedDirLock& operator=(const DirLock&);
const Zstring lockfilename_;
-
boost::thread threadObj;
};
@@ -633,6 +663,9 @@ private:
DirLock::DirLock(const Zstring& lockfilename, DirLockCallback* callback) //throw FileError
{
+ if (callback)
+ callback->reportStatus(replaceCpy(_("Creating file %x"), L"%x", fmtFileName(lockfilename)));
+
#ifdef FFS_WIN
const DWORD bufferSize = 10000;
std::vector<wchar_t> volName(bufferSize);
diff --git a/lib/dir_lock.h b/lib/dir_lock.h
index 85b9058e..ec2a431a 100644
--- a/lib/dir_lock.h
+++ b/lib/dir_lock.h
@@ -17,7 +17,7 @@ struct DirLockCallback //while waiting for the lock
{
virtual ~DirLockCallback() {}
virtual void requestUiRefresh() = 0; //allowed to throw exceptions
- virtual void reportInfo(const std::wstring& text) = 0;
+ virtual void reportStatus(const std::wstring& text) = 0;
};
/*
diff --git a/lib/ffs_paths.cpp b/lib/ffs_paths.cpp
new file mode 100644
index 00000000..3a0b557d
--- /dev/null
+++ b/lib/ffs_paths.cpp
@@ -0,0 +1,146 @@
+// **************************************************************************
+// * 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 *
+// **************************************************************************
+
+#include "ffs_paths.h"
+#include <zen/file_handling.h>
+#include <wx/stdpaths.h>
+#include <wx+/string_conv.h>
+
+#ifdef FFS_MAC
+#include <vector>
+#include <zen/scope_guard.h>
+#include <CoreServices/CoreServices.h> //keep in .cpp file to not pollute global namespace! e.g. with UInt64
+#endif
+
+using namespace zen;
+
+
+namespace
+{
+#if defined FFS_WIN || defined FFS_LINUX
+inline
+Zstring getExecutableDir() //directory containing executable WITH path separator at end
+{
+ return appendSeparator(beforeLast(utfCvrtTo<Zstring>(wxStandardPaths::Get().GetExecutablePath()), FILE_NAME_SEPARATOR));
+}
+#endif
+
+#ifdef FFS_WIN
+inline
+Zstring getInstallDir() //root install directory WITH path separator at end
+{
+ return appendSeparator(beforeLast(beforeLast(getExecutableDir(), FILE_NAME_SEPARATOR), FILE_NAME_SEPARATOR));
+}
+#endif
+
+
+#ifdef FFS_WIN
+inline
+bool isPortableVersion() { return !fileExists(getInstallDir() + L"uninstall.exe"); } //this check is a bit lame...
+#elif defined FFS_LINUX
+inline
+bool isPortableVersion() { return !endsWith(getExecutableDir(), "/bin/"); } //this check is a bit lame...
+#endif
+}
+
+
+bool zen::manualProgramUpdateRequired()
+{
+#if defined FFS_WIN || defined FFS_MAC
+ return true;
+#elif defined FFS_LINUX
+ return isPortableVersion(); //locally installed version is updated by system
+#endif
+}
+
+
+Zstring zen::getResourceDir()
+{
+#ifdef FFS_WIN
+ return getInstallDir();
+#elif defined FFS_LINUX
+ if (isPortableVersion())
+ return getExecutableDir();
+ else //use OS' standard paths
+ return appendSeparator(toZ(wxStandardPathsBase::Get().GetResourcesDir()));
+#elif defined FFS_MAC
+ return appendSeparator(toZ(wxStandardPathsBase::Get().GetResourcesDir()));
+#endif
+}
+
+
+Zstring zen::getConfigDir()
+{
+#ifdef FFS_WIN
+ if (isPortableVersion())
+ return getInstallDir();
+#elif defined FFS_LINUX
+ if (isPortableVersion())
+ return getExecutableDir();
+#elif defined FFS_MAC
+ //portable apps do not seem common on OS - fine with me: http://theocacao.com/document.page/319
+#endif
+ //use OS' standard paths
+ Zstring userDirectory = toZ(wxStandardPathsBase::Get().GetUserDataDir());
+
+ if (!dirExists(userDirectory))
+ try
+ {
+ makeDirectory(userDirectory); //throw FileError
+ }
+ catch (const FileError&) {}
+
+ return appendSeparator(userDirectory);
+}
+
+
+//this function is called by RealtimeSync!!!
+Zstring zen::getFreeFileSyncLauncher()
+{
+#ifdef FFS_WIN
+ return getInstallDir() + Zstr("FreeFileSync.exe");
+#elif defined FFS_LINUX
+ return getExecutableDir() + Zstr("FreeFileSync");
+#elif defined FFS_MAC
+ auto CFStringRefToUtf8 = [](const CFStringRef& cfs) -> Zstring
+ {
+ if (cfs)
+ {
+ CFIndex length = ::CFStringGetLength(cfs);
+ if (length > 0)
+ {
+ CFIndex bufferSize = ::CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8);
+ std::vector<char> buffer(bufferSize);
+
+ if (::CFStringGetCString(cfs, &buffer[0], bufferSize, kCFStringEncodingUTF8))
+ return Zstring(&buffer[0]);
+ }
+ }
+ return Zstring();
+ };
+
+ CFURLRef appURL = nullptr;
+ ZEN_ON_SCOPE_EXIT(if (appURL) ::CFRelease(appURL));
+
+ if (::LSFindApplicationForInfo(kLSUnknownCreator, // OSType inCreator,
+ CFSTR("net.SourceForge.FreeFileSync"),//CFStringRef inBundleID,
+ nullptr, //CFStringRef inName,
+ nullptr, //FSRef *outAppRef,
+ &appURL) == noErr) //CFURLRef *outAppURL
+ if (appURL)
+ if (CFURLRef absUrl = ::CFURLCopyAbsoluteURL(appURL))
+ {
+ ZEN_ON_SCOPE_EXIT(::CFRelease(absUrl));
+
+ if (CFStringRef path = ::CFURLCopyFileSystemPath(absUrl, kCFURLPOSIXPathStyle))
+ {
+ ZEN_ON_SCOPE_EXIT(::CFRelease(path));
+ return appendSeparator(CFStringRefToUtf8(path)) + "Contents/MacOS/FreeFileSync";
+ }
+ }
+ return Zstr("./FreeFileSync"); //fallback: at least give some hint...
+#endif
+}
diff --git a/lib/ffs_paths.h b/lib/ffs_paths.h
index 2aef2322..cb0b9c3c 100644
--- a/lib/ffs_paths.h
+++ b/lib/ffs_paths.h
@@ -7,10 +7,7 @@
#ifndef STANDARDPATHS_H_INCLUDED
#define STANDARDPATHS_H_INCLUDED
-#include <wx/stdpaths.h>
#include <zen/zstring.h>
-#include <zen/file_handling.h>
-#include <wx+/string_conv.h>
namespace zen
{
@@ -18,108 +15,11 @@ namespace zen
//global program directories
//------------------------------------------------------------------------------
Zstring getResourceDir(); //resource directory WITH path separator at end
-Zstring getConfigDir(); //config directory WITH path separator at end
+Zstring getConfigDir (); //config directory WITH path separator at end
//------------------------------------------------------------------------------
-Zstring getLauncher(); //full path to application launcher C:\...\FreeFileSync.exe
-bool isPortableVersion();
-
-
-
-
-
-
-
-
-
-
-
-
-
-//---------------- implementation ----------------
-namespace impl
-{
-inline
-const Zstring& getBinaryDir() //directory containing executable WITH path separator at end
-{
- static Zstring instance = beforeLast(utfCvrtTo<Zstring>(wxStandardPaths::Get().GetExecutablePath()), FILE_NAME_SEPARATOR) + Zstring(FILE_NAME_SEPARATOR); //extern linkage!
- return instance;
-}
-
-#ifdef FFS_WIN
-inline
-Zstring getInstallDir() //root install directory WITH path separator at end
-{
- return beforeLast(beforeLast(getBinaryDir(), FILE_NAME_SEPARATOR), FILE_NAME_SEPARATOR) + FILE_NAME_SEPARATOR;
-}
-#endif
-}
-
-
-inline
-bool isPortableVersion()
-{
-#ifdef FFS_WIN
- static const bool isPortable = !fileExists(impl::getInstallDir() + L"uninstall.exe"); //this check is a bit lame...
-
-#elif defined FFS_LINUX
- static const bool isPortable = !endsWith(impl::getBinaryDir(), "/bin/"); //this check is a bit lame...
-#endif
- return isPortable;
-}
-
-
-inline
-Zstring getResourceDir()
-{
-#ifdef FFS_WIN
- return impl::getInstallDir();
-
-#elif defined FFS_LINUX
- if (isPortableVersion())
- return impl::getBinaryDir();
- else //use OS' standard paths
- return appendSeparator(toZ(wxStandardPathsBase::Get().GetResourcesDir()));
-#endif
-}
-
-
-inline
-Zstring getConfigDir()
-{
- if (isPortableVersion())
-#ifdef FFS_WIN
- return impl::getInstallDir();
-#elif defined FFS_LINUX
- //wxString(wxT(".")) + zToWx(FILE_NAME_SEPARATOR) -> don't use current working directory
- //avoid surprises with GlobalSettings.xml being newly created in each working directory
- return impl::getBinaryDir();
-#endif
- else //use OS' standard paths
- {
- Zstring userDirectory = toZ(wxStandardPathsBase::Get().GetUserDataDir());
-
- if (!dirExists(userDirectory))
- try
- {
- makeDirectory(userDirectory); //only top directory needs to be created: no recursion necessary
- }
- catch (const FileError&) {}
-
- return appendSeparator(userDirectory);
- }
-}
-
-
-inline
-Zstring getLauncher()
-{
-#ifdef FFS_WIN
- return impl::getInstallDir() + Zstr("FreeFileSync.exe");
-#elif defined FFS_LINUX
- return impl::getBinaryDir() + Zstr("FreeFileSync");
-#endif
-}
+Zstring getFreeFileSyncLauncher(); //full path to application launcher C:\...\FreeFileSync.exe
+bool manualProgramUpdateRequired();
}
#endif // STANDARDPATHS_H_INCLUDED
diff --git a/lib/hard_filter.cpp b/lib/hard_filter.cpp
index 5664da83..bb94b25d 100644
--- a/lib/hard_filter.cpp
+++ b/lib/hard_filter.cpp
@@ -69,7 +69,7 @@ void addFilterEntry(const Zstring& filtername, std::vector<Zstring>& fileFilter,
{
Zstring filterFormatted = filtername;
-#ifdef FFS_WIN
+#if defined FFS_WIN || defined FFS_MAC
//Windows does NOT distinguish between upper/lower-case
makeUpper(filterFormatted);
#elif defined FFS_LINUX
@@ -289,7 +289,7 @@ NameFilter::NameFilter(const Zstring& includeFilter, const Zstring& excludeFilte
bool NameFilter::passFileFilter(const Zstring& relFilename) const
{
-#ifdef FFS_WIN //Windows does NOT distinguish between upper/lower-case
+#if defined FFS_WIN || defined FFS_MAC //Windows does NOT distinguish between upper/lower-case
Zstring nameFormatted = relFilename;
makeUpper(nameFormatted);
#elif defined FFS_LINUX //Linux DOES distinguish between upper/lower-case
@@ -305,7 +305,7 @@ bool NameFilter::passDirFilter(const Zstring& relDirname, bool* subObjMightMatch
{
assert(!subObjMightMatch || *subObjMightMatch == true); //check correct usage
-#ifdef FFS_WIN //Windows does NOT distinguish between upper/lower-case
+#if defined FFS_WIN || defined FFS_MAC //Windows does NOT distinguish between upper/lower-case
Zstring nameFormatted = relDirname;
makeUpper(nameFormatted);
#elif defined FFS_LINUX //Linux DOES distinguish between upper/lower-case
diff --git a/lib/help_provider.h b/lib/help_provider.h
index 15ae4f60..215b7dac 100644
--- a/lib/help_provider.h
+++ b/lib/help_provider.h
@@ -8,12 +8,14 @@
#define HELPPROVIDER_H_INCLUDED
#include <wx/help.h>
-#include "ffs_paths.h"
#include <zen/zstring.h>
+#include "ffs_paths.h"
namespace zen
{
-void displayHelpEntry(const wxString& section = wxEmptyString); //use '/' as path separator!
+//use '/' as path separator!
+void displayHelpEntry(wxWindow* parent);
+void displayHelpEntry(const wxString& section, wxWindow* parent);
@@ -36,10 +38,10 @@ wxHelpController& getHelpCtrl()
if (!initialized)
{
initialized = true;
- controller.Initialize(toWx(zen::getResourceDir()) +
+ controller.Initialize(utfCvrtTo<wxString>(zen::getResourceDir()) +
#ifdef FFS_WIN
L"FreeFileSync.chm");
-#elif defined FFS_LINUX
+#elif defined FFS_LINUX || defined FFS_MAC
L"Help/FreeFileSync.hhp");
#endif
}
@@ -48,12 +50,20 @@ wxHelpController& getHelpCtrl()
inline
-void displayHelpEntry(const wxString& section)
+void displayHelpEntry(const wxString& section, wxWindow* parent)
+{
+ getHelpCtrl().SetParentWindow(parent); //this nicely solves modal issues on OSX with help file going to the background
+ getHelpCtrl().DisplaySection(replaceCpy(section, L'/', utfCvrtTo<wxString>(FILE_NAME_SEPARATOR)));
+ getHelpCtrl().SetParentWindow(nullptr);
+}
+
+
+inline
+void displayHelpEntry(wxWindow* parent)
{
- if (section.empty())
- getHelpCtrl().DisplayContents();
- else
- getHelpCtrl().DisplaySection(replaceCpy(section, L'/', utfCvrtTo<std::wstring>(FILE_NAME_SEPARATOR)));
+ getHelpCtrl().SetParentWindow(parent);
+ getHelpCtrl().DisplayContents();
+ getHelpCtrl().SetParentWindow(nullptr);
}
}
diff --git a/lib/icon_buffer.cpp b/lib/icon_buffer.cpp
index 1d391dfc..44440be8 100644
--- a/lib/icon_buffer.cpp
+++ b/lib/icon_buffer.cpp
@@ -21,32 +21,22 @@
using namespace zen;
+warn_static("mac")
+
+#if defined FFS_MAC
+struct IconBuffer::Pimpl {};
+IconBuffer::IconBuffer(IconSize sz): pimpl(), icoSize(sz), genDirIcon(), genFileIcon() {}
+IconBuffer::~IconBuffer() {}
+int IconBuffer::getSize(IconSize icoSize) {return 16; }
+bool IconBuffer::requestFileIcon(const Zstring& filename, wxIcon* icon) { return false; }
+void IconBuffer::setWorkload(const std::vector<Zstring>& load) {}
+#else
+
namespace
{
const size_t BUFFER_SIZE_MAX = 800; //maximum number of icons to buffer
-inline
-int cvrtSize(IconBuffer::IconSize sz) //get size in pixel
-{
- switch (sz)
- {
- case IconBuffer::SIZE_SMALL:
-#ifdef FFS_WIN
- return 16;
-#elif defined FFS_LINUX
- return 24;
-#endif
- case IconBuffer::SIZE_MEDIUM:
- return 48;
- case IconBuffer::SIZE_LARGE:
- return 128;
- }
- assert(false);
- return 0;
-}
-
-
class IconHolder //handle HICON/GdkPixbuf ownership WITHOUT ref-counting to allow thread-safe usage (in contrast to wxIcon)
{
public:
@@ -259,23 +249,22 @@ IconHolder getThumbnail(const Zstring& filename, int requestedSize) //return 0 o
}
-const char* mimeFileIcons[] =
-{
- "application-x-zerosize", //Kubuntu: /usr/share/icons/oxygen/48x48/mimetypes
- "text-x-generic", //http://live.gnome.org/GnomeArt/Tutorials/IconThemes
- "empty", //
- "gtk-file", //Ubuntu: /usr/share/icons/Humanity/mimes/48
- "gnome-fs-regular", //
-};
-
-
IconHolder getGenericFileIcon(IconBuffer::IconSize sz)
{
#ifdef FFS_WIN
return getIconByAttribute(L"dummy", FILE_ATTRIBUTE_NORMAL, sz);
#elif defined FFS_LINUX
- const int requestedSize = cvrtSize(sz);
+ const char* mimeFileIcons[] =
+ {
+ "application-x-zerosize", //Kubuntu: /usr/share/icons/oxygen/48x48/mimetypes
+ "text-x-generic", //http://live.gnome.org/GnomeArt/Tutorials/IconThemes
+ "empty", //
+ "gtk-file", //Ubuntu: /usr/share/icons/Humanity/mimes/48
+ "gnome-fs-regular", //
+ };
+
+ const int requestedSize = IconBuffer::getSize(sz);
if (GtkIconTheme* defaultTheme = gtk_icon_theme_get_default()) //not owned!
for (auto it = std::begin(mimeFileIcons); it != std::end(mimeFileIcons); ++it)
@@ -296,7 +285,7 @@ IconHolder getAssociatedIcon(const Zstring& filename, IconBuffer::IconSize sz)
case IconBuffer::SIZE_MEDIUM:
case IconBuffer::SIZE_LARGE:
{
- IconHolder ico = getThumbnail(filename, cvrtSize(sz));
+ IconHolder ico = getThumbnail(filename, IconBuffer::getSize(sz));
if (ico)
return ico;
//else: fallback to non-thumbnail icon
@@ -314,11 +303,12 @@ IconHolder getAssociatedIcon(const Zstring& filename, IconBuffer::IconSize sz)
//which means the access to get thumbnail failed: thumbnail failure is not dependent from extension in general!
SHFILEINFO fileInfo = {};
- DWORD_PTR imgList = ::SHGetFileInfo(filename.c_str(), //zen::removeLongPathPrefix(fileName), //::SHGetFileInfo() can't handle \\?\-prefix!
- 0,
- &fileInfo,
- sizeof(fileInfo),
- SHGFI_SYSICONINDEX);
+ DWORD_PTR imgList = ::SHGetFileInfo(filename.c_str(), //_In_ LPCTSTR pszPath, -> note: ::SHGetFileInfo() can't handle \\?\-prefix!
+ 0, //DWORD dwFileAttributes,
+ &fileInfo, //_Inout_ SHFILEINFO *psfi,
+ sizeof(fileInfo), //UINT cbFileInfo,
+ SHGFI_SYSICONINDEX); //UINT uFlags
+
//Quote: "The IImageList pointer type, such as that returned in the ppv parameter, can be cast as an HIMAGELIST as
// needed; for example, for use in a list view. Conversely, an HIMAGELIST can be cast as a pointer to an IImageList."
//http://msdn.microsoft.com/en-us/library/windows/desktop/bb762185(v=vs.85).aspx
@@ -328,14 +318,14 @@ IconHolder getAssociatedIcon(const Zstring& filename, IconBuffer::IconSize sz)
//imgList->Release(); //empiric study: crash on XP if we release this! Seems we do not own it... -> also no GDI leak on Win7 -> okay
//another comment on http://msdn.microsoft.com/en-us/library/bb762179(v=VS.85).aspx describes exact same behavior on Win7/XP
- boost::call_once(initGetIconByIndexOnce, [] //thread-safe init
+ boost::call_once(initGetIconByIndexOnce, [] //thread-safe init
{
getIconByIndex = DllFun<thumb::FunType_getIconByIndex>(thumb::getDllName(), thumb::funName_getIconByIndex);
});
return IconHolder(getIconByIndex ? static_cast<HICON>(getIconByIndex(fileInfo.iIcon, getShilIconType(sz))) : nullptr);
#elif defined FFS_LINUX
- const int requestedSize = cvrtSize(sz);
+ const int requestedSize = IconBuffer::getSize(sz);
GFile* file = g_file_new_for_path(filename.c_str()); //never fails
ZEN_ON_SCOPE_EXIT(g_object_unref(file);)
@@ -403,7 +393,6 @@ public:
boost::unique_lock<boost::mutex> dummy(lockFiles);
filesToLoad = newLoad;
}
-
conditionNewFiles.notify_all(); //instead of notify_one(); workaround bug: https://svn.boost.org/trac/boost/ticket/7796
//condition handling, see: http://www.boost.org/doc/libs/1_43_0/doc/html/thread/synchronization.html#thread.synchronization.condvar_ref
}
@@ -428,7 +417,7 @@ public:
auto it = iconMappping.find(fileName);
if (it != iconMappping.end())
{
- if (icon != nullptr)
+ if (icon)
*icon = it->second;
return true;
}
@@ -460,7 +449,7 @@ private:
NameIconMap iconMappping; //use synchronisation when accessing this!
IconDbSequence iconSequence; //save sequence of buffer entry to delete oldest elements
};
-//-------------------------------------------------------------
+
//################################################################################################################################################
class WorkerThread //lifetime is part of icon buffer
@@ -516,8 +505,7 @@ void WorkerThread::operator()() //thread entry
struct IconBuffer::Pimpl
{
- Pimpl() :
- workload(std::make_shared<WorkLoad>()),
+ Pimpl() : workload(std::make_shared<WorkLoad>()),
buffer(std::make_shared<Buffer>()) {}
std::shared_ptr<WorkLoad> workload;
@@ -527,11 +515,10 @@ struct IconBuffer::Pimpl
};
-IconBuffer::IconBuffer(IconSize sz) :
- pimpl(new Pimpl),
+IconBuffer::IconBuffer(IconSize sz) : pimpl(make_unique<Pimpl>()),
icoSize(sz),
- genDirIcon(::getGenericDirectoryIcon(sz).toWxIcon(cvrtSize(icoSize))),
- genFileIcon(::getGenericFileIcon(sz).toWxIcon(cvrtSize(icoSize)))
+ genDirIcon(::getGenericDirectoryIcon(sz).toWxIcon(IconBuffer::getSize(icoSize))),
+ genFileIcon(::getGenericFileIcon(sz).toWxIcon(IconBuffer::getSize(icoSize)))
{
pimpl->worker = boost::thread(WorkerThread(pimpl->workload, pimpl->buffer, sz));
}
@@ -545,9 +532,23 @@ IconBuffer::~IconBuffer()
}
-int IconBuffer::getSize() const
+int IconBuffer::getSize(IconSize icoSize)
{
- return cvrtSize(icoSize);
+ switch (icoSize)
+ {
+ case IconBuffer::SIZE_SMALL:
+#if defined FFS_WIN || defined FFS_MAC
+ return 16;
+#elif defined FFS_LINUX
+ return 24;
+#endif
+ case IconBuffer::SIZE_MEDIUM:
+ return 48;
+ case IconBuffer::SIZE_LARGE:
+ return 128;
+ }
+ assert(false);
+ return 0;
}
@@ -561,7 +562,7 @@ bool IconBuffer::requestFileIcon(const Zstring& filename, wxIcon* icon)
IconHolder heldIcon;
if (!pimpl->buffer->requestFileIcon(entryName, &heldIcon))
return false;
- *icon = heldIcon.toWxIcon(cvrtSize(icoSize));
+ *icon = heldIcon.toWxIcon(IconBuffer::getSize(icoSize));
return true;
};
@@ -577,7 +578,7 @@ bool IconBuffer::requestFileIcon(const Zstring& filename, wxIcon* icon)
IconHolder heldIcon = getAssociatedIconByExt(extension, icoSize); //fast!
pimpl->buffer->insertIntoBuffer(extension, heldIcon);
if (icon)
- *icon = heldIcon.toWxIcon(cvrtSize(icoSize));
+ *icon = heldIcon.toWxIcon(IconBuffer::getSize(icoSize));
}
return true;
}
@@ -588,3 +589,4 @@ bool IconBuffer::requestFileIcon(const Zstring& filename, wxIcon* icon)
}
void IconBuffer::setWorkload(const std::vector<Zstring>& load) { pimpl->workload->setWorkload(load); }
+#endif
diff --git a/lib/icon_buffer.h b/lib/icon_buffer.h
index 75bf54c8..ba34faa2 100644
--- a/lib/icon_buffer.h
+++ b/lib/icon_buffer.h
@@ -8,6 +8,7 @@
#define ICONBUFFER_H_INCLUDED
#include <memory>
+#include <wx/bitmap.h>
#include <wx/icon.h>
#include <zen/zstring.h>
@@ -27,11 +28,12 @@ public:
IconBuffer(IconSize sz);
~IconBuffer();
+ static int getSize(IconSize icoSize); //*maximum* icon size in pixel
+ int getSize() const { return getSize(icoSize); } //
+
const wxIcon& genericFileIcon() { return genFileIcon; }
const wxIcon& genericDirIcon () { return genDirIcon; }
- int getSize() const; //*maximum* icon size in pixel
-
bool requestFileIcon(const Zstring& filename, wxIcon* icon = nullptr); //returns false if icon is not in buffer
void setWorkload(const std::vector<Zstring>& load); //(re-)set new workload of icons to be retrieved;
diff --git a/lib/localization.cpp b/lib/localization.cpp
index 78e03545..33494cf4 100644
--- a/lib/localization.cpp
+++ b/lib/localization.cpp
@@ -20,6 +20,10 @@
#include "parse_lng.h"
#include "ffs_paths.h"
+#ifdef FFS_MAC
+#include <CoreServices/CoreServices.h>
+#endif
+
using namespace zen;
@@ -129,8 +133,8 @@ struct LessTranslation : public std::binary_function<ExistingTranslations::Entry
{
bool operator()(const ExistingTranslations::Entry& lhs, const ExistingTranslations::Entry& rhs) const
{
+ //use a more "natural" sort: ignore case and diacritics
#ifdef FFS_WIN
- //use a more "natural" sort, that is ignore case and diacritics
const int rv = ::CompareString(LOCALE_USER_DEFAULT, //__in LCID Locale,
NORM_IGNORECASE, //__in DWORD dwCmpFlags,
lhs.languageName.c_str(), //__in LPCTSTR lpString1,
@@ -141,8 +145,25 @@ struct LessTranslation : public std::binary_function<ExistingTranslations::Entry
throw std::runtime_error("Error comparing strings!");
else
return rv == CSTR_LESS_THAN; //convert to C-style string compare result
-#else
- return lhs.languageName < rhs.languageName;
+
+#elif defined FFS_LINUX
+ return lhs.languageName.CmpNoCase(rhs.languageName) < 0;
+
+#elif defined FFS_MAC
+ auto allocCFStringRef = [](const wxString& str) -> CFStringRef //output not owned!
+ {
+ return ::CFStringCreateWithCString(nullptr, //CFAllocatorRef alloc,
+ utfCvrtTo<std::string>(str).c_str(), //const char *cStr,
+ kCFStringEncodingUTF8); //CFStringEncoding encoding
+ };
+
+ CFStringRef langL = allocCFStringRef(lhs.languageName);
+ ZEN_ON_SCOPE_EXIT(::CFRelease(langL));
+
+ CFStringRef langR = allocCFStringRef(rhs.languageName);
+ ZEN_ON_SCOPE_EXIT(::CFRelease(langR));
+
+ return::CFStringCompare(langL, langR, kCFCompareLocalized | kCFCompareCaseInsensitive) == kCFCompareLessThan; //no-fail
#endif
}
};
@@ -405,7 +426,7 @@ void zen::setLanguage(int language) //throw FileError
catch (lngfile::ParsingError& e)
{
throw FileError(replaceCpy(replaceCpy(replaceCpy(_("Error parsing file %x, row %y, column %z."),
- L"%x", fmtFileName(toZ(languageFile))),
+ L"%x", fmtFileName(utfCvrtTo<Zstring>(languageFile))),
L"%y", numberTo<std::wstring>(e.row + 1)),
L"%z", numberTo<std::wstring>(e.col + 1)));
}
diff --git a/lib/lock_holder.h b/lib/lock_holder.h
index d4fe27a9..9cde59a7 100644
--- a/lib/lock_holder.h
+++ b/lib/lock_holder.h
@@ -18,20 +18,14 @@ class LockHolder
public:
LockHolder(const std::vector<Zstring>& dirnamesFmt, //resolved dirname ending with path separator
ProcessCallback& procCallback,
- bool allowUserInteraction) : allowUserInteraction_(allowUserInteraction)
+ bool allowUserInteraction)
{
- std::vector<Zstring> dirs = dirnamesFmt;
- vector_remove_if(dirs, [](const Zstring& dir) { return dir.empty(); });
+ std::set<Zstring, LessFilename> existingDirs = getExistingDirsUpdating(dirnamesFmt, allowUserInteraction, procCallback);
- for (auto it = dirs.begin(); it != dirs.end(); ++it)
+ for (auto it = existingDirs.begin(); it != existingDirs.end(); ++it)
{
const Zstring& dirnameFmt = *it;
- if (!dirExistsUpdating(dirnameFmt, allowUserInteraction_, procCallback))
- continue;
-
- if (lockHolder.find(dirnameFmt) != lockHolder.end())
- continue;
assert(endsWith(dirnameFmt, FILE_NAME_SEPARATOR)); //this is really the contract, formatting does other things as well, e.g. macro substitution
class WaitOnLockHandler : public DirLockCallback
@@ -39,7 +33,7 @@ public:
public:
WaitOnLockHandler(ProcessCallback& pc) : pc_(pc) {}
virtual void requestUiRefresh() { pc_.requestUiRefresh(); } //allowed to throw exceptions
- virtual void reportInfo(const std::wstring& text) { pc_.reportStatus(text); }
+ virtual void reportStatus(const std::wstring& text) { pc_.reportStatus(text); }
private:
ProcessCallback& pc_;
} callback(procCallback);
@@ -47,8 +41,7 @@ public:
try
{
//lock file creation is synchronous and may block noticeably for very slow devices (usb sticks, mapped cloud storages)
- procCallback.forceUiRefresh(); //=> make sure the right folder name is shown on GUI during this time!
- lockHolder.insert(std::make_pair(dirnameFmt, DirLock(dirnameFmt + Zstr("sync") + LOCK_FILE_ENDING, &callback)));
+ lockHolder.push_back(DirLock(dirnameFmt + Zstr("sync") + LOCK_FILE_ENDING, &callback)); //throw FileError
}
catch (const FileError& e)
{
@@ -59,9 +52,7 @@ public:
}
private:
- typedef std::map<Zstring, DirLock, LessFilename> DirnameLockMap;
- DirnameLockMap lockHolder;
- const bool allowUserInteraction_;
+ std::vector<DirLock> lockHolder;
};
}
diff --git a/lib/parallel_scan.cpp b/lib/parallel_scan.cpp
index 94f6b0f4..37dd350e 100644
--- a/lib/parallel_scan.cpp
+++ b/lib/parallel_scan.cpp
@@ -13,11 +13,11 @@
#include <zen/thread.h> //includes <boost/thread.hpp>
#include <zen/scope_guard.h>
#include <zen/fixed_list.h>
+#include <boost/detail/atomic_count.hpp>
using namespace zen;
-
namespace
{
/*
@@ -111,8 +111,6 @@ DiskInfo retrieveDiskInfo(const Zstring& pathName)
return output;
}
-
-#elif defined FFS_LINUX
#endif
*/
@@ -195,16 +193,16 @@ public:
if (!errorMsg.empty() && !errorResponse.get())
{
FillBufferCallback::HandleError rv = callback.reportError(copyStringTo<std::wstring>(errorMsg)); //throw!
- errorResponse.reset(new FillBufferCallback::HandleError(rv));
+ errorResponse = make_unique<FillBufferCallback::HandleError>(rv);
dummy.unlock(); //optimization for condition_variable::notify_one()
conditionGotResponse.notify_all(); //instead of notify_one(); workaround bug: https://svn.boost.org/trac/boost/ticket/7796
}
}
- void setNotifyingThread(size_t threadID) { notifyingThreadID = threadID; } //context of main thread
+ void incrementNotifyingThreadId() { ++notifyingThreadID; } //context of main thread
- void reportCurrentFile(const Zstring& filename, size_t threadID) //context of worker thread
+ void reportCurrentFile(const Zstring& filename, long threadID) //context of worker thread
{
if (threadID != notifyingThreadID) return; //only one thread at a time may report status
@@ -213,7 +211,7 @@ public:
currentStatus.clear();
}
- void reportCurrentStatus(const std::wstring& status, size_t threadID) //context of worker thread
+ void reportCurrentStatus(const std::wstring& status, long threadID) //context of worker thread
{
if (threadID != notifyingThreadID) return; //only one thread may report status
@@ -264,7 +262,7 @@ private:
std::unique_ptr<FillBufferCallback::HandleError> errorResponse;
//---- status updates ----
- volatile size_t notifyingThreadID; //theoretically racy, but there is nothing that could go wrong...
+ boost::detail::atomic_count notifyingThreadID;
//CAVEAT: do NOT use boost::thread::id as long as this showstopper exists: https://svn.boost.org/trac/boost/ticket/5754
boost::mutex lockCurrentStatus; //use a different lock for current file: continue traversing while some thread may process an error
Zstring currentFile; //only one of these two is filled at a time!
@@ -282,7 +280,7 @@ private:
struct TraverserShared
{
public:
- TraverserShared(size_t threadID,
+ TraverserShared(long threadID,
SymLinkHandling handleSymlinks,
const HardFilter::FilterRef& filter,
std::set<Zstring>& failedReads,
@@ -299,7 +297,7 @@ public:
std::set<Zstring>& failedReads_; //relative postfixed names of directories that could not be read (empty for root)
AsyncCallback& acb_;
- size_t threadID_;
+ long threadID_;
};
@@ -445,7 +443,7 @@ DirCallback::HandleError DirCallback::onError(const std::wstring& msg)
class DstHackCallbackImpl : public DstHackCallback
{
public:
- DstHackCallbackImpl(AsyncCallback& acb, size_t threadID) :
+ DstHackCallbackImpl(AsyncCallback& acb, long threadID) :
acb_(acb),
threadID_(threadID),
textApplyingDstHack(replaceCpy(_("Encoding extended time information: %x"), L"%x", L"\n%x")) {}
@@ -457,7 +455,7 @@ private:
}
AsyncCallback& acb_;
- size_t threadID_;
+ long threadID_;
const std::wstring textApplyingDstHack;
};
#endif
@@ -467,7 +465,7 @@ private:
class WorkerThread
{
public:
- WorkerThread(size_t threadID,
+ WorkerThread(long threadID,
const std::shared_ptr<AsyncCallback>& acb,
const DirectoryKey& dirKey,
DirectoryValue& dirOutput) :
@@ -504,7 +502,7 @@ public:
}
private:
- size_t threadID_;
+ long threadID_;
std::shared_ptr<AsyncCallback> acb_;
const DirectoryKey dirKey_;
DirectoryValue& dirOutput_;
@@ -531,7 +529,7 @@ void zen::fillBuffer(const std::set<DirectoryKey>& keysToRead, //in
});
});
- std::shared_ptr<AsyncCallback> acb = std::make_shared<AsyncCallback>();
+ auto acb = std::make_shared<AsyncCallback>();
//init worker threads
std::for_each(keysToRead.begin(), keysToRead.end(),
@@ -540,18 +538,15 @@ void zen::fillBuffer(const std::set<DirectoryKey>& keysToRead, //in
assert(buf.find(key) == buf.end());
DirectoryValue& dirOutput = buf[key];
- const size_t threadId = worker.size();
+ const long threadId = static_cast<long>(worker.size());
worker.emplace_back(WorkerThread(threadId, acb, key, dirOutput));
});
//wait until done
- size_t threadId = 0;
- for (auto it = worker.begin(); it != worker.end(); ++it, ++threadId)
+ for (auto it = worker.begin(); it != worker.end(); ++it)
{
boost::thread& wt = *it;
- acb->setNotifyingThread(threadId); //process info messages of first (active) thread only
-
do
{
//update status
@@ -561,6 +556,8 @@ void zen::fillBuffer(const std::set<DirectoryKey>& keysToRead, //in
acb->processErrors(callback);
}
while (!wt.timed_join(boost::posix_time::milliseconds(updateInterval)));
+
+ acb->incrementNotifyingThreadId(); //process info messages of one thread at a time only
}
guardWorker.dismiss();
diff --git a/lib/perf_check.cpp b/lib/perf_check.cpp
index 85a98910..33361201 100644
--- a/lib/perf_check.cpp
+++ b/lib/perf_check.cpp
@@ -49,57 +49,54 @@ void PerfCheck::addSample(int objectsCurrent, double dataCurrent, long timeMs)
//remove all records earlier than "now - windowMax"
const long newBegin = timeMs - windowMax;
- auto iterWindowBegin = samples.upper_bound(newBegin);
- if (iterWindowBegin != samples.begin())
- samples.erase(samples.begin(), --iterWindowBegin); //keep one point before newBegin in order to handle "measurement holes"
+ auto it = samples.upper_bound(newBegin);
+ if (it != samples.begin())
+ samples.erase(samples.begin(), --it); //keep one point before newBegin in order to handle "measurement holes"
}
-wxString PerfCheck::getRemainingTime(double dataRemaining) const
+std::wstring PerfCheck::getRemainingTime(double dataRemaining) const
{
if (!samples.empty())
{
const auto& recordBack = *samples.rbegin();
//find start of records "window"
- auto iterFront = samples.upper_bound(recordBack.first - windowSizeRemTime);
- if (iterFront != samples.begin())
- --iterFront; //one point before window begin in order to handle "measurement holes"
+ auto itFront = samples.upper_bound(recordBack.first - windowSizeRemTime);
+ if (itFront != samples.begin())
+ --itFront; //one point before window begin in order to handle "measurement holes"
- const auto& recordFront = *iterFront;
+ const auto& recordFront = *itFront;
//-----------------------------------------------------------------------------------------------
- const double timeDelta = recordBack.first - recordFront.first;
+ const long timeDelta = recordBack.first - recordFront.first;
const double dataDelta = recordBack.second.data_ - recordFront.second.data_;
- //objects do *NOT* correspond to disk accesses, so we better play safe and use "bytes" only!
- //https://sourceforge.net/tracker/index.php?func=detail&aid=3452469&group_id=234430&atid=1093083
+ //objects model logical operations *NOT* disk accesses, so we better play safe and use "bytes" only!
+ //http://sourceforge.net/p/freefilesync/feature-requests/197/
if (!numeric::isNull(dataDelta)) //sign(dataRemaining) != sign(dataDelta) usually an error, so show it!
- {
- const double remTimeSec = dataRemaining * timeDelta / (1000.0 * dataDelta);
- return remainingTimeToShortString(remTimeSec);
- }
+ return remainingTimeToString(dataRemaining * timeDelta / (1000.0 * dataDelta));
}
return L"-"; //fallback
}
-wxString PerfCheck::getBytesPerSecond() const
+std::wstring PerfCheck::getBytesPerSecond() const
{
if (!samples.empty())
{
const auto& recordBack = *samples.rbegin();
//find start of records "window"
- auto iterFront = samples.upper_bound(recordBack.first - windowSizeBPS);
- if (iterFront != samples.begin())
- --iterFront; //one point before window begin in order to handle "measurement holes"
+ auto itFront = samples.upper_bound(recordBack.first - windowSizeBPS);
+ if (itFront != samples.begin())
+ --itFront; //one point before window begin in order to handle "measurement holes"
- const auto& recordFront = *iterFront;
+ const auto& recordFront = *itFront;
//-----------------------------------------------------------------------------------------------
- const double timeDelta = recordBack.first - recordFront.first;
+ const long timeDelta = recordBack.first - recordFront.first;
const double dataDelta = recordBack.second.data_ - recordFront.second.data_;
- if (!numeric::isNull(timeDelta) && dataDelta > 0)
- return filesizeToShortString(zen::Int64(dataDelta * 1000 / timeDelta)) + _("/sec");
+ if (timeDelta != 0 && dataDelta > 0)
+ return filesizeToShortString(zen::Int64(dataDelta * 1000 / timeDelta)) + _("/sec");
}
return L"-"; //fallback
}
diff --git a/lib/perf_check.h b/lib/perf_check.h
index f314f842..3e04b778 100644
--- a/lib/perf_check.h
+++ b/lib/perf_check.h
@@ -8,7 +8,7 @@
#define STATISTICS_H_INCLUDED
#include <map>
-#include <wx/string.h>
+#include <string>
class PerfCheck
{
@@ -19,8 +19,8 @@ public:
void addSample(int objectsCurrent, double dataCurrent, long timeMs); //timeMs must be ascending!
- wxString getRemainingTime(double dataRemaining) const;
- wxString getBytesPerSecond() const; //for window
+ std::wstring getRemainingTime(double dataRemaining) const;
+ std::wstring getBytesPerSecond() const; //for window
private:
const long windowSizeRemTime; //unit: [ms]
diff --git a/lib/process_xml.cpp b/lib/process_xml.cpp
index e38749f9..0c5b5581 100644
--- a/lib/process_xml.cpp
+++ b/lib/process_xml.cpp
@@ -75,7 +75,7 @@ void setXmlType(XmlDoc& doc, XmlType type) //throw()
wxString xmlAccess::getGlobalConfigFile()
{
- return toWx(zen::getConfigDir()) + L"GlobalSettings.xml";
+ return utfCvrtTo<wxString>(zen::getConfigDir()) + L"GlobalSettings.xml";
}
@@ -1177,7 +1177,7 @@ void xmlAccess::readConfig(const Zstring& filename, xmlAccess::XmlBatchConfig& c
void xmlAccess::readConfig(xmlAccess::XmlGlobalSettings& config)
{
- ::readConfig(toZ(getGlobalConfigFile()), XML_TYPE_GLOBAL, config);
+ ::readConfig(utfCvrtTo<Zstring>(getGlobalConfigFile()), XML_TYPE_GLOBAL, config);
}
@@ -1448,7 +1448,7 @@ void xmlAccess::writeConfig(const XmlBatchConfig& config, const Zstring& filenam
void xmlAccess::writeConfig(const XmlGlobalSettings& config)
{
- ::writeConfig(config, XML_TYPE_GLOBAL, toZ(getGlobalConfigFile())); //throw FfsXmlError
+ ::writeConfig(config, XML_TYPE_GLOBAL, utfCvrtTo<Zstring>(getGlobalConfigFile())); //throw FfsXmlError
}
diff --git a/lib/process_xml.h b/lib/process_xml.h
index d0396d6e..9dda330e 100644
--- a/lib/process_xml.h
+++ b/lib/process_xml.h
@@ -175,7 +175,7 @@ struct XmlGlobalSettings
onCompletionHistoryMax(8),
//deleteOnBothSides(false),
useRecyclerForManualDeletion(true), //enable if OS supports it; else user will have to activate first and then get an error message
-#ifdef FFS_WIN
+#if defined FFS_WIN || defined FFS_MAC
textSearchRespectCase(false),
#elif defined FFS_LINUX
textSearchRespectCase(true),
@@ -186,15 +186,17 @@ struct XmlGlobalSettings
{
//default external apps will be translated "on the fly"!!! First entry will be used for [Enter] or mouse double-click!
#ifdef FFS_WIN
- externelApplications.push_back(std::make_pair(L"Show in Explorer", //mark for extraction: _("Show in Explorer")
- L"explorer /select, \"%item_path%\""));
- externelApplications.push_back(std::make_pair(L"Open with default application", //mark for extraction: _("Open with default application")
- L"\"%item_path%\""));
+ externelApplications.push_back(std::make_pair(L"Show in Explorer", L"explorer /select, \"%item_path%\""));
+ externelApplications.push_back(std::make_pair(L"Open with default application", L"\"%item_path%\""));
+ //mark for extraction: _("Show in Explorer")
+ //mark for extraction: _("Open with default application")
#elif defined FFS_LINUX
- externelApplications.push_back(std::make_pair(L"Browse directory", //mark for extraction: _("Browse directory") Linux doesn't use the term "folder"
- L"xdg-open \"%item_folder%\""));
- externelApplications.push_back(std::make_pair(L"Open with default application", //mark for extraction: _("Open with default application")
- L"xdg-open \"%item_path%\""));
+ externelApplications.push_back(std::make_pair(L"Browse directory", L"xdg-open \"%item_folder%\""));
+ externelApplications.push_back(std::make_pair(L"Open with default application", L"xdg-open \"%item_path%\""));
+ //mark for extraction: _("Browse directory") Linux doesn't use the term "folder"
+#elif defined FFS_MAC
+ externelApplications.push_back(std::make_pair(L"Browse directory", L"open -R \"%item_path%\""));
+ externelApplications.push_back(std::make_pair(L"Open with default application", L"open \"%item_path%\""));
#endif
}
diff --git a/lib/resolve_path.cpp b/lib/resolve_path.cpp
index 9c690c9a..b735d1f9 100644
--- a/lib/resolve_path.cpp
+++ b/lib/resolve_path.cpp
@@ -1,25 +1,21 @@
#include "resolve_path.h"
+#include <set> //not necessarily included by <map>!
#include <map>
-#include <set>
#include <zen/time.h>
-#include <zen/scope_guard.h>
#include <zen/thread.h>
-#include <wx/utils.h>
-#include <wx+/string_conv.h>
+#include <zen/utf.h>
+#include <wx/utils.h> //wxGetEnv
#ifdef FFS_WIN
-#include <zen/dll.h>
-#include <Shlobj.h>
#include <zen/win.h> //includes "windows.h"
+#include <Shlobj.h>
#include <zen/long_path_prefix.h>
#include <zen/file_handling.h>
#ifdef _MSC_VER
#pragma comment(lib, "Mpr.lib")
#endif
-#elif defined FFS_LINUX
-#include <zen/file_traverser.h>
-#include <unistd.h>
+#elif defined FFS_LINUX || defined FFS_MAC
#include <stdlib.h> //getenv()
#endif
@@ -44,7 +40,7 @@ Zstring resolveRelativePath(const Zstring& relativeName) //note: ::GetFullPathNa
return removeLongPathPrefix(Zstring(&buffer[0], charsWritten)); //GetFullPathName() preserves long path prefix -> a low-level detail we don't want to leak out!
}
-#elif defined FFS_LINUX
+#elif defined FFS_LINUX || defined FFS_MAC
Zstring resolveRelativePath(const Zstring& relativeName)
{
//http://linux.die.net/man/2/path_resolution
@@ -70,21 +66,12 @@ Zstring resolveRelativePath(const Zstring& relativeName)
return homeDir;
}
- //unfortunately ::realpath only resolves *existing* relative paths, so we need to do it by ourselves
+ //we cannot use ::realpath() since it resolves *existing* relative paths only!
std::vector<char> buffer(10000);
if (::getcwd(&buffer[0], buffer.size()) != nullptr)
return appendSeparator(&buffer[0]) + relativeName;
}
return relativeName;
-
- /*
- char* absPath = ::realpath(relativeName.c_str(), nullptr);
- if (!absPath)
- return relativeName; //ERROR! Don't do anything
- ZEN_ON_SCOPE_EXIT(::free(absPath));
-
- return Zstring(absPath);
- */
}
#endif
@@ -288,33 +275,10 @@ Zstring zen::expandMacros(const Zstring& text) { return ::expandMacros(text, std
namespace
{
-#ifdef FFS_LINUX
-class TraverseMedia : public zen::TraverseCallback
-{
-public:
- typedef std::map<Zstring, Zstring> DeviceList; //device name -> device path mapping
-
- TraverseMedia(DeviceList& devices) : devices_(devices) {}
-
- virtual void onFile(const Zchar* shortName, const Zstring& fullName, const FileInfo& details) {}
- virtual HandleLink onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details) { return LINK_SKIP; }
- virtual std::shared_ptr<TraverseCallback> onDir(const Zchar* shortName, const Zstring& fullName)
- {
- devices_.insert(std::make_pair(shortName, fullName));
- return nullptr; //DON'T traverse into subdirs
- }
- virtual HandleError onError(const std::wstring& msg) { assert(false); return ON_ERROR_IGNORE; }
-
-private:
- DeviceList& devices_;
-};
-#endif
-
-
+#ifdef FFS_WIN
//networks and cdrom excluded - this should not block
Zstring getPathByVolumenName(const Zstring& volumeName) //return empty string on error
{
-#ifdef FFS_WIN
//FindFirstVolume(): traverses volumes on local hard disks only!
//GetLogicalDriveStrings(): traverses all *logical* volumes, including CD-ROM, FreeOTFE virtual volumes
@@ -335,8 +299,7 @@ Zstring getPathByVolumenName(const Zstring& volumeName) //return empty string on
findFirstMatch.addJob([path, volumeName]() -> std::unique_ptr<Zstring>
{
UINT type = ::GetDriveType(path.c_str()); //non-blocking call!
- if (type == DRIVE_REMOTE ||
- type == DRIVE_CDROM)
+ if (type == DRIVE_REMOTE || type == DRIVE_CDROM)
return nullptr;
//next call seriously blocks for non-existing network drives!
@@ -359,29 +322,15 @@ Zstring getPathByVolumenName(const Zstring& volumeName) //return empty string on
return *result;
}
-#elif defined FFS_LINUX
- //due to the naming convention on Linux /media/<volume name> this function is not that useful, but...
-
- TraverseMedia::DeviceList deviceList;
-
- TraverseMedia traverser(deviceList);
- traverseFolder("/media", traverser); //traverse one level
-
- TraverseMedia::DeviceList::const_iterator it = deviceList.find(volumeName);
- if (it != deviceList.end())
- return it->second;
-#endif
return Zstring();
}
-#ifdef FFS_WIN
//networks and cdrom excluded - this should not block
Zstring getVolumeName(const Zstring& volumePath) //return empty string on error
{
UINT rv = ::GetDriveType(volumePath.c_str()); //non-blocking call!
- if (rv != DRIVE_REMOTE &&
- rv != DRIVE_CDROM)
+ if (rv != DRIVE_REMOTE && rv != DRIVE_CDROM)
{
std::vector<wchar_t> buffer(MAX_PATH + 1);
if (::GetVolumeInformation(volumePath.c_str(), //__in_opt LPCTSTR lpRootPathName,
@@ -420,7 +369,7 @@ Zstring expandVolumeName(const Zstring& text) // [volname]:\folder [volna
rest = afterFirst(rest, Zstr(':'));
if (startsWith(rest, FILE_NAME_SEPARATOR))
rest = afterFirst(rest, FILE_NAME_SEPARATOR);
-
+#ifdef FFS_WIN
//[.*] pattern was found...
if (!volname.empty())
{
@@ -435,11 +384,10 @@ Zstring expandVolumeName(const Zstring& text) // [volname]:\folder [volna
?:\[FFS USB]\FreeFileSync\ - Windows
/.../[FFS USB]/FreeFileSync/ - Linux
instead of:
- C:\Program Files\FreeFileSync\[FFS USB]\FreeFileSync\
- */
-#ifdef FFS_WIN
+ C:\Program Files\FreeFileSync\[FFS USB]\FreeFileSync\ */
return L"?:\\[" + volname + L"]\\" + rest;
-#elif defined FFS_LINUX
+
+#elif defined FFS_LINUX || defined FFS_MAC //neither supported nor needed
return "/.../[" + volname + "]/" + rest;
#endif
}
@@ -500,13 +448,13 @@ void getDirectoryAliasesRecursive(const Zstring& dirname, std::set<Zstring, Less
const auto& csidlMap = CsidlConstants::get();
envToDir.insert(csidlMap.begin(), csidlMap.end());
-#elif defined FFS_LINUX
- addEnvVar("HOME"); // /home/zenju
+#elif defined FFS_LINUX || defined FFS_MAC
+ addEnvVar("HOME"); //Linux: /home/zenju Mac: /Users/zenju
#endif
//substitute paths by symbolic names
auto pathStartsWith = [](const Zstring& path, const Zstring& prefix) -> bool
{
-#ifdef FFS_WIN
+#if defined FFS_WIN || defined FFS_MAC
Zstring tmp = path;
Zstring tmp2 = prefix;
::makeUpper(tmp);
diff --git a/lib/resources.cpp b/lib/resources.cpp
index 2f7daeaf..8a021475 100644
--- a/lib/resources.cpp
+++ b/lib/resources.cpp
@@ -10,6 +10,7 @@
#include <wx/zipstrm.h>
#include <wx/image.h>
#include <wx/mstream.h>
+#include <zen/utf.h>
#include "ffs_paths.h"
using namespace zen;
@@ -45,7 +46,7 @@ void loadAnimFromZip(wxZipInputStream& zipInput, wxAnimation& anim)
GlobalResources::GlobalResources()
{
- wxFFileInputStream input(toWx(zen::getResourceDir()) + L"Resources.zip");
+ wxFFileInputStream input(utfCvrtTo<wxString>(zen::getResourceDir()) + L"Resources.zip");
if (input.IsOk()) //if not... we don't want to react too harsh here
{
//activate support for .png files
@@ -57,7 +58,7 @@ GlobalResources::GlobalResources()
while (true)
{
std::unique_ptr<wxZipEntry> entry(resourceFile.GetNextEntry()); //take ownership!
- if (entry.get() == nullptr)
+ if (!entry)
break;
const wxString name = entry->GetName();
@@ -75,9 +76,10 @@ GlobalResources::GlobalResources()
#ifdef FFS_WIN
//for compatibility it seems we need to stick with a "real" icon
programIcon = wxIcon(L"A_PROGRAM_ICON");
-#else
+
+#elif defined FFS_LINUX || defined FFS_MAC
//use big logo bitmap for better quality
- programIcon.CopyFromBitmap(getImageInt(L"FreeFileSync.png"));
+ programIcon.CopyFromBitmap(getImageInt(L"FreeFileSync"));
//attention: this is the reason we need a member getImage -> it must not implicitly create static object instance!!!
//erroneously calling static object constructor twice will deadlock on Linux!!
#endif
diff --git a/lib/shadow.cpp b/lib/shadow.cpp
index 6dae97b1..52b40a9e 100644
--- a/lib/shadow.cpp
+++ b/lib/shadow.cpp
@@ -38,7 +38,7 @@ bool runningWOW64() //test if process is running under WOW64 (reference http://m
class ShadowCopy::ShadowVolume
{
public:
- ShadowVolume(const Zstring& volumeNamePf) : //throw(FileError)
+ ShadowVolume(const Zstring& volumeNamePf) : //throw FileError
createShadowCopy (getDllName(), funName_createShadowCopy),
releaseShadowCopy(getDllName(), funName_releaseShadowCopy),
getShadowVolume (getDllName(), funName_getShadowVolume),
@@ -111,7 +111,7 @@ Zstring ShadowCopy::makeShadowCopy(const Zstring& inputFile)
VolNameShadowMap::const_iterator it = shadowVol.find(volumeNamePf);
if (it == shadowVol.end())
{
- auto newEntry = std::make_shared<ShadowVolume>(volumeNamePf);
+ auto newEntry = std::make_shared<ShadowVolume>(volumeNamePf); //throw FileError
it = shadowVol.insert(std::make_pair(volumeNamePf, newEntry)).first;
}
diff --git a/lib/status_handler.cpp b/lib/status_handler.cpp
index fd3b2d96..c24c6f50 100644
--- a/lib/status_handler.cpp
+++ b/lib/status_handler.cpp
@@ -22,7 +22,7 @@ void zen::updateUiNow()
namespace
{
-const std::int64_t TICKS_UPDATE_INTERVAL = UI_UPDATE_INTERVAL * ticksPerSec() / 1000;
+const std::int64_t TICKS_UPDATE_INTERVAL = UI_UPDATE_INTERVAL* ticksPerSec() / 1000;
TickVal lastExec = getTicks();
};
diff --git a/lib/xml_base.cpp b/lib/xml_base.cpp
index 814b9bdb..e26f73c2 100644
--- a/lib/xml_base.cpp
+++ b/lib/xml_base.cpp
@@ -71,7 +71,7 @@ const std::wstring xmlAccess::getErrorMessageFormatted(const XmlIn& in)
{
msg = _("Cannot read the following XML elements:") + L"\n";
std::for_each(failedElements.begin(), failedElements.end(),
- [&](const std::wstring& str) { msg += str + L'\n'; });
+ [&](const std::wstring& elem) { msg += L"\n" + elem; });
}
return msg;
bgstack15