diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Batch.ico | bin | 117121 -> 103260 bytes | |||
-rw-r--r-- | lib/FreeFileSync.ico | bin | 132428 -> 114124 bytes | |||
-rw-r--r-- | lib/ShadowCopy/LockFile.cpp | 43 | ||||
-rw-r--r-- | lib/ShadowCopy/shadow.cpp | 4 | ||||
-rw-r--r-- | lib/SyncDB.ico | bin | 133875 -> 111496 bytes | |||
-rw-r--r-- | lib/Thumbnail/Thumbnail.vcxproj | 18 | ||||
-rw-r--r-- | lib/Thumbnail/thumbnail.cpp | 11 | ||||
-rw-r--r-- | lib/db_file.cpp | 10 | ||||
-rw-r--r-- | lib/dir_exist_async.h | 55 | ||||
-rw-r--r-- | lib/dir_lock.cpp | 201 | ||||
-rw-r--r-- | lib/dir_lock.h | 2 | ||||
-rw-r--r-- | lib/ffs_paths.cpp | 146 | ||||
-rw-r--r-- | lib/ffs_paths.h | 106 | ||||
-rw-r--r-- | lib/hard_filter.cpp | 6 | ||||
-rw-r--r-- | lib/help_provider.h | 28 | ||||
-rw-r--r-- | lib/icon_buffer.cpp | 108 | ||||
-rw-r--r-- | lib/icon_buffer.h | 6 | ||||
-rw-r--r-- | lib/localization.cpp | 29 | ||||
-rw-r--r-- | lib/lock_holder.h | 21 | ||||
-rw-r--r-- | lib/parallel_scan.cpp | 37 | ||||
-rw-r--r-- | lib/perf_check.cpp | 43 | ||||
-rw-r--r-- | lib/perf_check.h | 6 | ||||
-rw-r--r-- | lib/process_xml.cpp | 6 | ||||
-rw-r--r-- | lib/process_xml.h | 20 | ||||
-rw-r--r-- | lib/resolve_path.cpp | 86 | ||||
-rw-r--r-- | lib/resources.cpp | 10 | ||||
-rw-r--r-- | lib/shadow.cpp | 4 | ||||
-rw-r--r-- | lib/status_handler.cpp | 2 | ||||
-rw-r--r-- | lib/xml_base.cpp | 2 |
29 files changed, 521 insertions, 489 deletions
diff --git a/lib/Batch.ico b/lib/Batch.ico Binary files differindex f742b9a3..d27f35d1 100644 --- a/lib/Batch.ico +++ b/lib/Batch.ico diff --git a/lib/FreeFileSync.ico b/lib/FreeFileSync.ico Binary files differindex 02925b10..88f656ee 100644 --- a/lib/FreeFileSync.ico +++ b/lib/FreeFileSync.ico 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 Binary files differindex 6fc5d264..9740a338 100644 --- a/lib/SyncDB.ico +++ b/lib/SyncDB.ico 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; |