diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/IFileOperation/file_op.cpp | 14 | ||||
-rw-r--r-- | lib/ShadowCopy/LockFile.cpp | 4 | ||||
-rw-r--r-- | lib/ShadowCopy/shadow.cpp | 4 | ||||
-rw-r--r-- | lib/Thumbnail/thumbnail.cpp | 66 | ||||
-rw-r--r-- | lib/Thumbnail/thumbnail.h | 4 | ||||
-rw-r--r-- | lib/db_file.cpp | 248 | ||||
-rw-r--r-- | lib/dir_lock.cpp | 124 | ||||
-rw-r--r-- | lib/dir_lock.h | 3 | ||||
-rw-r--r-- | lib/error_log.cpp | 101 | ||||
-rw-r--r-- | lib/error_log.h | 49 | ||||
-rw-r--r-- | lib/hard_filter.cpp | 193 | ||||
-rw-r--r-- | lib/hard_filter.h | 14 | ||||
-rw-r--r-- | lib/icon_buffer.cpp | 116 | ||||
-rw-r--r-- | lib/icon_buffer.h | 2 | ||||
-rw-r--r-- | lib/localization.cpp | 5 | ||||
-rw-r--r-- | lib/norm_filter.h | 2 | ||||
-rw-r--r-- | lib/parallel_scan.cpp | 16 | ||||
-rw-r--r-- | lib/parse_lng.h | 13 | ||||
-rw-r--r-- | lib/parse_plural.h | 10 | ||||
-rw-r--r-- | lib/process_xml.cpp | 56 | ||||
-rw-r--r-- | lib/process_xml.h | 2 | ||||
-rw-r--r-- | lib/recycler.cpp | 66 | ||||
-rw-r--r-- | lib/recycler.h | 8 | ||||
-rw-r--r-- | lib/resolve_path.cpp | 83 | ||||
-rw-r--r-- | lib/resolve_path.h | 1 | ||||
-rw-r--r-- | lib/resources.cpp | 7 | ||||
-rw-r--r-- | lib/shadow.cpp | 51 | ||||
-rw-r--r-- | lib/statistics.cpp | 13 | ||||
-rw-r--r-- | lib/status_handler.h | 60 | ||||
-rw-r--r-- | lib/status_handler_impl.h | 34 | ||||
-rw-r--r-- | lib/xml_base.cpp | 14 |
31 files changed, 587 insertions, 796 deletions
diff --git a/lib/IFileOperation/file_op.cpp b/lib/IFileOperation/file_op.cpp index 8024ab91..3d7717f4 100644 --- a/lib/IFileOperation/file_op.cpp +++ b/lib/IFileOperation/file_op.cpp @@ -24,7 +24,7 @@ void moveToRecycleBin(const wchar_t* fileNames[], //throw ComError { ComPtr<IFileOperation> fileOp; ZEN_CHECK_COM(::CoCreateInstance(CLSID_FileOperation, //throw ComError - NULL, + nullptr, CLSCTX_ALL, IID_PPV_ARGS(fileOp.init()))); @@ -45,7 +45,7 @@ void moveToRecycleBin(const wchar_t* fileNames[], //throw ComError //create file/folder item object ComPtr<IShellItem> psiFile; HRESULT hr = ::SHCreateItemFromParsingName(fileNames[i], - NULL, + nullptr, IID_PPV_ARGS(psiFile.init())); if (FAILED(hr)) { @@ -55,7 +55,7 @@ void moveToRecycleBin(const wchar_t* fileNames[], //throw ComError throw ComError(std::wstring(L"Error calling \"SHCreateItemFromParsingName\" for file:\n") + L"\"" + fileNames[i] + L"\".", hr); } - ZEN_CHECK_COM(fileOp->DeleteItem(psiFile.get(), NULL)); + ZEN_CHECK_COM(fileOp->DeleteItem(psiFile.get(), nullptr)); ++operationCount; } @@ -80,7 +80,7 @@ void copyFile(const wchar_t* sourceFile, //throw ComError { ComPtr<IFileOperation> fileOp; ZEN_CHECK_COM(::CoCreateInstance(CLSID_FileOperation, //throw ComError - NULL, + nullptr, CLSCTX_ALL, IID_PPV_ARGS(fileOp.init()))); @@ -96,7 +96,7 @@ void copyFile(const wchar_t* sourceFile, //throw ComError ComPtr<IShellItem> psiSourceFile; { HRESULT hr = ::SHCreateItemFromParsingName(sourceFile, - NULL, + nullptr, IID_PPV_ARGS(psiSourceFile.init())); if (FAILED(hr)) throw ComError(std::wstring(L"Error calling \"SHCreateItemFromParsingName\" for file:\n") + L"\"" + sourceFile + L"\".", hr); @@ -113,14 +113,14 @@ void copyFile(const wchar_t* sourceFile, //throw ComError ComPtr<IShellItem> psiTargetFolder; { HRESULT hr = ::SHCreateItemFromParsingName(targetFolder.c_str(), - NULL, + nullptr, IID_PPV_ARGS(psiTargetFolder.init())); if (FAILED(hr)) throw ComError(std::wstring(L"Error calling \"SHCreateItemFromParsingName\" for folder:\n") + L"\"" + targetFolder + L"\".", hr); } //schedule file copy operation - ZEN_CHECK_COM(fileOp->CopyItem(psiSourceFile.get(), psiTargetFolder.get(), targetFileNameShort.c_str(), NULL)); + ZEN_CHECK_COM(fileOp->CopyItem(psiSourceFile.get(), psiTargetFolder.get(), targetFileNameShort.c_str(), nullptr)); //perform actual operations ZEN_CHECK_COM(fileOp->PerformOperations()); diff --git a/lib/ShadowCopy/LockFile.cpp b/lib/ShadowCopy/LockFile.cpp index 523b01bb..b9008863 100644 --- a/lib/ShadowCopy/LockFile.cpp +++ b/lib/ShadowCopy/LockFile.cpp @@ -24,10 +24,10 @@ int wmain(int argc, wchar_t* argv[]) HANDLE hFile = ::CreateFile(filename.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, + nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, - NULL); + nullptr); if (hFile == INVALID_HANDLE_VALUE) { std::wcout << "Error obtaining exclusive lock on test file: " << filename << "\n\n"; diff --git a/lib/ShadowCopy/shadow.cpp b/lib/ShadowCopy/shadow.cpp index fc95381d..bc27e2c3 100644 --- a/lib/ShadowCopy/shadow.cpp +++ b/lib/ShadowCopy/shadow.cpp @@ -110,7 +110,7 @@ shadow::ShadowData createShadowCopy(const wchar_t* volumeName) //throw ComError ZEN_CHECK_COM(fut.Wait()); HRESULT hr = S_OK; - ZEN_CHECK_COM(fut.QueryStatus(&hr, NULL)); //check if the async operation succeeded... + ZEN_CHECK_COM(fut.QueryStatus(&hr, nullptr)); //check if the async operation succeeded... if (FAILED(hr)) throw ComError(L"Error calling \"fut->QueryStatus\".", hr); }; @@ -151,7 +151,7 @@ shadow::ShadowData createShadowCopy(const wchar_t* volumeName) //throw ComError VSS_SNAPSHOT_PROP props = {}; ZEN_CHECK_COM(backupComp->GetSnapshotProperties(SnapShotId, &props)); - ZEN_ON_BLOCK_EXIT(::VssFreeSnapshotProperties(&props)); + ZEN_ON_SCOPE_EXIT(::VssFreeSnapshotProperties(&props)); //finally: write volume name of newly created shadow copy return shadow::ShadowData(backupComp, props.m_pwszSnapshotDeviceObject); diff --git a/lib/Thumbnail/thumbnail.cpp b/lib/Thumbnail/thumbnail.cpp index 050251de..0176f89b 100644 --- a/lib/Thumbnail/thumbnail.cpp +++ b/lib/Thumbnail/thumbnail.cpp @@ -31,59 +31,59 @@ thumb::HICON thumb::getThumbnail(const wchar_t* filename, int requestedSize) //r { HRESULT hr = ::SHGetDesktopFolder(shellFolder.init()); if (FAILED(hr) || !shellFolder) - return NULL; + return nullptr; } - PIDLIST_RELATIVE pidlFolder = NULL; + PIDLIST_RELATIVE pidlFolder = nullptr; { const std::wstring& pathName = beforeLast(filenameStr, '\\'); - HRESULT hr = shellFolder->ParseDisplayName(NULL, // [in] HWND hwnd, - NULL, // [in] IBindCtx *pbc, + HRESULT hr = shellFolder->ParseDisplayName(nullptr, // [in] HWND hwnd, + nullptr, // [in] IBindCtx *pbc, const_cast<LPWSTR>(pathName.c_str()), // [in] LPWSTR pszDisplayName, - NULL, // [out] ULONG *pchEaten, - &pidlFolder, // [out] PIDLIST_RELATIVE* ppidl, - NULL); // [in, out] ULONG *pdwAttributes + nullptr, // [out] ULONG *pchEaten, + &pidlFolder, // [out] PIDLIST_RELATIVE* ppidl, + nullptr); // [in, out] ULONG *pdwAttributes if (FAILED(hr) || !pidlFolder) - return NULL; + return nullptr; } - ZEN_ON_BLOCK_EXIT(::ILFree(pidlFolder)); //older version: ::CoTaskMemFree + ZEN_ON_SCOPE_EXIT(::ILFree(pidlFolder)); //older version: ::CoTaskMemFree ComPtr<IShellFolder> imageFolder; { HRESULT hr = shellFolder->BindToObject(pidlFolder, // [in] PCUIDLIST_RELATIVE pidl, - NULL, + nullptr, IID_PPV_ARGS(imageFolder.init())); if (FAILED(hr) || !imageFolder) - return NULL; + return nullptr; } - PIDLIST_RELATIVE pidImage = NULL; + PIDLIST_RELATIVE pidImage = nullptr; { const std::wstring& shortName = afterLast(filenameStr, '\\'); - HRESULT hr = imageFolder->ParseDisplayName(NULL, // [in] HWND hwnd, - NULL, // [in] IBindCtx *pbc, + HRESULT hr = imageFolder->ParseDisplayName(nullptr, // [in] HWND hwnd, + nullptr, // [in] IBindCtx *pbc, const_cast<LPWSTR>(shortName.c_str()), // [in] LPWSTR pszDisplayName, - NULL, // [out] ULONG *pchEaten, - &pidImage, // [out] PIDLIST_RELATIVE *ppidl, - NULL); // [in, out] ULONG *pdwAttributes + nullptr, // [out] ULONG *pchEaten, + &pidImage, // [out] PIDLIST_RELATIVE *ppidl, + nullptr); // [in, out] ULONG *pdwAttributes if (FAILED(hr) || !pidImage) - return NULL; + return nullptr; } - ZEN_ON_BLOCK_EXIT(::ILFree(pidImage)); //older version: ::CoTaskMemFree + ZEN_ON_SCOPE_EXIT(::ILFree(pidImage)); //older version: ::CoTaskMemFree ComPtr<IExtractImage> extractImage; { PCUITEMID_CHILD_ARRAY pidlIn = reinterpret_cast<PCUITEMID_CHILD_ARRAY>(&pidImage); //this is where STRICT_TYPED_ITEMIDS gets us ;) - HRESULT hr = imageFolder->GetUIObjectOf(NULL, // [in] HWND hwndOwner, + HRESULT hr = imageFolder->GetUIObjectOf(nullptr, // [in] HWND hwndOwner, 1, // [in] UINT cidl, pidlIn, // [in] PCUITEMID_CHILD_ARRAY apidl, IID_IExtractImage, // [in] REFIID riid, - NULL, // [in, out] UINT *rgfReserved, + nullptr, // [in, out] UINT *rgfReserved, reinterpret_cast<void**>(extractImage.init())); // [out] void **ppv if (FAILED(hr) || !extractImage) - return NULL; + return nullptr; } { @@ -100,28 +100,28 @@ thumb::HICON thumb::getThumbnail(const wchar_t* filename, int requestedSize) //r clrDepth, // [in] DWORD dwRecClrDepth, &flags); // [in, out] DWORD *pdwFlags if (FAILED(hr)) - return NULL; + return nullptr; } - HBITMAP bitmap = NULL; + HBITMAP bitmap = nullptr; { HRESULT hr = extractImage->Extract(&bitmap); if (FAILED(hr) || !bitmap) - return NULL; + return nullptr; } - ZEN_ON_BLOCK_EXIT(::DeleteObject(bitmap)); + ZEN_ON_SCOPE_EXIT(::DeleteObject(bitmap)); BITMAP bmpInfo = {}; if (::GetObject(bitmap, //__in HGDIOBJ hgdiobj, sizeof(bmpInfo), //__in int cbBuffer, &bmpInfo) == 0) //__out LPVOID lpvObject - return NULL; + return nullptr; - HBITMAP bitmapMask = ::CreateCompatibleBitmap(::GetDC(NULL), bmpInfo.bmWidth, bmpInfo.bmHeight); + HBITMAP bitmapMask = ::CreateCompatibleBitmap(::GetDC(nullptr), bmpInfo.bmWidth, bmpInfo.bmHeight); if (bitmapMask == 0) - return NULL; - ZEN_ON_BLOCK_EXIT(::DeleteObject(bitmapMask)); + return nullptr; + ZEN_ON_SCOPE_EXIT(::DeleteObject(bitmapMask)); ICONINFO iconInfo = {}; iconInfo.fIcon = true; @@ -141,7 +141,7 @@ thumb::HICON thumb::getIconByIndex(int iconIndex, int shilIconType) //return 0 o HRESULT hr = ::SHGetImageList(shilIconType, //__in int iImageList, IID_PPV_ARGS(imageList.init())); if (FAILED(hr) || !imageList) - return NULL; + return nullptr; } bool hasAlpha = false; //perf: 0,14 µs @@ -153,7 +153,7 @@ thumb::HICON thumb::getIconByIndex(int iconIndex, int shilIconType) //return 0 o hasAlpha = flags & ILIF_ALPHA; } - ::HICON hIcon = NULL; //perf: 1,5 ms - the dominant block + ::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, @@ -161,7 +161,7 @@ thumb::HICON thumb::getIconByIndex(int iconIndex, int shilIconType) //return 0 o //other flags: http://msdn.microsoft.com/en-us/library/windows/desktop/bb775230(v=vs.85).aspx &hIcon); // [out] HICON *picon if (FAILED(hr) || !hIcon) - return NULL; + return nullptr; } return hIcon; diff --git a/lib/Thumbnail/thumbnail.h b/lib/Thumbnail/thumbnail.h index 7e11812c..b8b95d8f 100644 --- a/lib/Thumbnail/thumbnail.h +++ b/lib/Thumbnail/thumbnail.h @@ -21,8 +21,8 @@ namespace thumb /* PREREQUISITES: -1. COM must be initialized for the current thread via ::CoInitialize(NULL) or ::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED), - but NOT ::CoInitializeEx(NULL, COINIT_MULTITHREADED) -> internal access violation crash! +1. COM must be initialized for the current thread via ::CoInitialize(nullptr) or ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED), + but NOT ::CoInitializeEx(nullptr, COINIT_MULTITHREADED) -> internal access violation crash! 2. call ::FileIconInit() on app start to remedy obscure errors like SHELL_E_WRONG_BITDEPTH (0x80270102) for certain file types, e.g. lnk, mpg - required on Windows 7 see http://msdn.microsoft.com/en-us/library/ms683212(v=VS.85).aspx */ diff --git a/lib/db_file.cpp b/lib/db_file.cpp index 2d02d634..8ae8f94b 100644 --- a/lib/db_file.cpp +++ b/lib/db_file.cpp @@ -60,16 +60,36 @@ Zstring getDBFilename(const BaseDirMapping& baseMap, bool tempfile = false) } +class CheckedDbReader : public CheckedReader +{ +public: + CheckedDbReader(wxInputStream& stream, const Zstring& errorObjName) : CheckedReader(stream), errorObjName_(errorObjName) {} + +private: + virtual void throwException() const { throw FileError(_("Error reading from synchronization database:") + L" \n" + L"\"" + errorObjName_ + L"\""); } + + const Zstring errorObjName_; +}; + + +class CheckedDbWriter : public CheckedWriter +{ +public: + CheckedDbWriter(wxOutputStream& stream, const Zstring& errorObjName) : CheckedWriter(stream), errorObjName_(errorObjName) {} + + virtual void throwException() const { throw FileError(_("Error writing to synchronization database:") + L" \n" + L"\"" + errorObjName_ + L"\""); } + + const Zstring errorObjName_; +}; + + + StreamMapping loadStreams(const Zstring& filename) //throw FileError { - if (!zen::fileExists(filename)) - throw FileErrorDatabaseNotExisting(_("Initial synchronization:") + L" \n\n" + - _("One of the FreeFileSync database files is not yet existing:") + L" \n" + - L"\"" + filename + L"\""); try { //read format description (uncompressed) - FileInputStream rawStream(filename); //throw FileError + FileInputStream rawStream(filename); //throw FileError, ErrorNotExisting //read FreeFileSync file identifier char formatDescr[sizeof(FILE_FORMAT_DESCR)] = {}; @@ -80,26 +100,32 @@ StreamMapping loadStreams(const Zstring& filename) //throw FileError wxZlibInputStream decompressed(rawStream, wxZLIB_ZLIB); - CheckedReader cr(decompressed, filename); + CheckedDbReader cr(decompressed, filename); - std::int32_t version = cr.readNumberC<std::int32_t>(); + std::int32_t version = cr.readPOD<std::int32_t>(); if (version != FILE_FORMAT_VER) //read file format version# throw FileError(_("Incompatible synchronization database format:") + L" \n" + L"\"" + filename + L"\""); //read stream lists StreamMapping output; - std::uint32_t dbCount = cr.readNumberC<std::uint32_t>(); //number of databases: one for each sync-pair + std::uint32_t dbCount = cr.readPOD<std::uint32_t>(); //number of databases: one for each sync-pair while (dbCount-- != 0) { //DB id of partner databases - const std::string sessionID = cr.readStringC<std::string>(); - const MemoryStream stream = cr.readStringC<MemoryStream>(); //read db-entry stream (containing DirInformation) + const std::string sessionID = cr.readString<std::string>(); + const MemoryStream stream = cr.readString<MemoryStream>(); //read db-entry stream (containing DirInformation) output.insert(std::make_pair(sessionID, stream)); } return output; } + catch (ErrorNotExisting&) + { + throw FileErrorDatabaseNotExisting(_("Initial synchronization:") + L" \n\n" + + _("One of the FreeFileSync database files is not yet existing:") + L" \n" + + L"\"" + filename + L"\""); + } catch (const std::bad_alloc&) //this is most likely caused by a corrupted database file { throw FileError(_("Error reading from synchronization database:") + L" (bad alloc)"); @@ -107,7 +133,7 @@ StreamMapping loadStreams(const Zstring& filename) //throw FileError } -class StreamParser : private CheckedReader +class StreamParser : private CheckedDbReader { public: static DirInfoPtr execute(const MemoryStream& stream, const Zstring& fileName) //throw FileError -> return value always bound! @@ -127,14 +153,14 @@ public: } private: - StreamParser(wxInputStream& stream, const Zstring& errorObjName, DirInformation& dirInfo) : CheckedReader(stream, errorObjName) + StreamParser(wxInputStream& stream, const Zstring& errorObjName, DirInformation& dirInfo) : CheckedDbReader(stream, errorObjName) { recurse(dirInfo.baseDirContainer); } Zstring readStringUtf8() const { - return utf8CvrtTo<Zstring>(readStringC<Zbase<char>>()); + return utf8CvrtTo<Zstring>(readString<Zbase<char>>()); } FileId readFileId() const @@ -142,39 +168,39 @@ private: assert_static(sizeof(FileId().first ) <= sizeof(std::uint64_t)); assert_static(sizeof(FileId().second) <= sizeof(std::uint64_t)); - const auto devId = static_cast<decltype(FileId().first )>(readNumberC<std::uint64_t>()); // - const auto fId = static_cast<decltype(FileId().second)>(readNumberC<std::uint64_t>()); //silence "loss of precision" compiler warnings + const auto devId = static_cast<decltype(FileId().first )>(readPOD<std::uint64_t>()); // + const auto fId = static_cast<decltype(FileId().second)>(readPOD<std::uint64_t>()); //silence "loss of precision" compiler warnings return std::make_pair(devId, fId); } void recurse(DirContainer& dirCont) const { - while (readNumberC<bool>()) //files + while (readPOD<bool>()) //files { //attention: order of function argument evaluation is undefined! So do it one after the other... const Zstring shortName = readStringUtf8(); //file name - const std::int64_t modTime = readNumberC<std::int64_t>(); - const std::uint64_t fileSize = readNumberC<std::uint64_t>(); + const std::int64_t modTime = readPOD<std::int64_t>(); + const std::uint64_t fileSize = readPOD<std::uint64_t>(); const FileId fileID = readFileId(); dirCont.addSubFile(shortName, FileDescriptor(modTime, fileSize, fileID)); } - while (readNumberC<bool>()) //symlinks + while (readPOD<bool>()) //symlinks { //attention: order of function argument evaluation is undefined! So do it one after the other... const Zstring shortName = readStringUtf8(); //file name - const std::int64_t modTime = readNumberC<std::int64_t>(); + const std::int64_t modTime = readPOD<std::int64_t>(); const Zstring targetPath = readStringUtf8(); //file name - const LinkDescriptor::LinkType linkType = static_cast<LinkDescriptor::LinkType>(readNumberC<std::int32_t>()); + const LinkDescriptor::LinkType linkType = static_cast<LinkDescriptor::LinkType>(readPOD<std::int32_t>()); dirCont.addSubLink(shortName, LinkDescriptor(modTime, targetPath, linkType)); } - while (readNumberC<bool>()) //directories + while (readPOD<bool>()) //directories { const Zstring shortName = readStringUtf8(); //directory name DirContainer& subDir = dirCont.addSubDir(shortName); @@ -201,29 +227,28 @@ void saveFile(const StreamMapping& streamList, const Zstring& filename) //throw 6 1,77 MB - 613 ms 9 (maximal compression) 1,74 MB - 3330 ms */ - CheckedWriter cw(compressed, filename); + CheckedDbWriter cw(compressed, filename); //save file format version - cw.writeNumberC<std::int32_t>(FILE_FORMAT_VER); + cw.writePOD<std::int32_t>(FILE_FORMAT_VER); //save stream list - cw.writeNumberC<std::uint32_t>(static_cast<std::uint32_t>(streamList.size())); //number of database records: one for each sync-pair + cw.writePOD<std::uint32_t>(static_cast<std::uint32_t>(streamList.size())); //number of database records: one for each sync-pair for (auto iter = streamList.begin(); iter != streamList.end(); ++iter) { - cw.writeStringC<std::string >(iter->first ); //sync session id - cw.writeStringC<MemoryStream>(iter->second); //DirInformation stream + cw.writeString<std::string >(iter->first ); //sync session id + cw.writeString<MemoryStream>(iter->second); //DirInformation stream } } - //(try to) hide database file #ifdef FFS_WIN - ::SetFileAttributes(zen::applyLongPathPrefix(filename).c_str(), FILE_ATTRIBUTE_HIDDEN); + ::SetFileAttributes(applyLongPathPrefix(filename).c_str(), FILE_ATTRIBUTE_HIDDEN); //(try to) hide database file #endif } template <SelectedSide side> -class StreamGenerator : private CheckedWriter +class StreamGenerator : private CheckedDbWriter { public: static MemoryStream execute(const BaseDirMapping& baseMapping, const DirContainer* oldDirInfo, const Zstring& errorObjName) @@ -238,7 +263,7 @@ public: } private: - StreamGenerator(const BaseDirMapping& baseMapping, const DirContainer* oldDirInfo, const Zstring& errorObjName, wxOutputStream& stream) : CheckedWriter(stream, errorObjName) + StreamGenerator(const BaseDirMapping& baseMapping, const DirContainer* oldDirInfo, const Zstring& errorObjName, wxOutputStream& stream) : CheckedDbWriter(stream, errorObjName) { recurse(baseMapping, oldDirInfo); } @@ -247,32 +272,36 @@ private: { // for (const auto& fileMap : hierObj.refSubFiles()) { processFile(fileMap, oldDirInfo); }); ! - std::for_each(hierObj.refSubFiles().begin(), hierObj.refSubFiles().end(), [&](const FileMapping& fileMap) { this->processFile(fileMap, oldDirInfo); }); - writeNumberC<bool>(false); //mark last entry + std::for_each(hierObj.refSubFiles().begin(), hierObj.refSubFiles().end(), [&](const FileMapping& fileMap) { this->processFile(fileMap, oldDirInfo); }); + writePOD<bool>(false); //mark last entry std::for_each(hierObj.refSubLinks().begin(), hierObj.refSubLinks().end(), [&](const SymLinkMapping& linkObj) { this->processLink(linkObj, oldDirInfo); }); - writeNumberC<bool>(false); //mark last entry - std::for_each(hierObj.refSubDirs ().begin(), hierObj.refSubDirs ().end(), [&](const DirMapping& dirMap) { this->processDir(dirMap, oldDirInfo); }); - writeNumberC<bool>(false); //mark last entry + writePOD<bool>(false); //mark last entry + std::for_each(hierObj.refSubDirs ().begin(), hierObj.refSubDirs ().end(), [&](const DirMapping& dirMap) { this->processDir (dirMap, oldDirInfo); }); + writePOD<bool>(false); //mark last entry } - void writeStringUtf8(const Zstring& str) { writeStringC(utf8CvrtTo<Zbase<char>>(str)); } + void writeStringUtf8(const Zstring& str) { writeString(utf8CvrtTo<Zbase<char>>(str)); } void writeFileId(const FileId& id) { - writeNumberC<std::uint64_t>(id.first ); //device id - writeNumberC<std::uint64_t>(id.second); //file id + writePOD<std::uint64_t>(id.first ); //device id + writePOD<std::uint64_t>(id.second); //file id } +#ifdef _MSC_VER + warn_static("support multiple folder pairs that differ in hard filter only?") +#endif + void processFile(const FileMapping& fileMap, const DirContainer* oldParentDir) { if (fileMap.getCategory() == FILE_EQUAL) //data in sync: write current state { if (!fileMap.isEmpty<side>()) { - writeNumberC<bool>(true); //mark beginning of entry + writePOD<bool>(true); //mark beginning of entry writeStringUtf8(fileMap.getShortName<side>()); //save respecting case! (Windows) - writeNumberC<std:: int64_t>(to<std:: int64_t>(fileMap.getLastWriteTime<side>())); - writeNumberC<std::uint64_t>(to<std::uint64_t>(fileMap.getFileSize<side>())); + writePOD<std:: int64_t>(to<std:: int64_t>(fileMap.getLastWriteTime<side>())); + writePOD<std::uint64_t>(to<std::uint64_t>(fileMap.getFileSize<side>())); writeFileId(fileMap.getFileId<side>()); } } @@ -283,10 +312,10 @@ private: auto iter = oldParentDir->files.find(fileMap.getObjShortName()); if (iter != oldParentDir->files.end()) { - writeNumberC<bool>(true); //mark beginning of entry + writePOD<bool>(true); //mark beginning of entry writeStringUtf8(iter->first); //save respecting case! (Windows) - writeNumberC<std:: int64_t>(to<std:: int64_t>(iter->second.lastWriteTimeRaw)); - writeNumberC<std::uint64_t>(to<std::uint64_t>(iter->second.fileSize)); + writePOD<std:: int64_t>(to<std:: int64_t>(iter->second.lastWriteTimeRaw)); + writePOD<std::uint64_t>(to<std::uint64_t>(iter->second.fileSize)); writeFileId(iter->second.id); } } @@ -299,11 +328,11 @@ private: { if (!linkObj.isEmpty<side>()) { - writeNumberC<bool>(true); //mark beginning of entry + writePOD<bool>(true); //mark beginning of entry writeStringUtf8(linkObj.getShortName<side>()); //save respecting case! (Windows) - writeNumberC<std::int64_t>(to<std::int64_t>(linkObj.getLastWriteTime<side>())); + writePOD<std::int64_t>(to<std::int64_t>(linkObj.getLastWriteTime<side>())); writeStringUtf8(linkObj.getTargetPath<side>()); - writeNumberC<std::int32_t>(linkObj.getLinkType<side>()); + writePOD<std::int32_t>(linkObj.getLinkType<side>()); } } else //not in sync: reuse last synchronous state @@ -313,11 +342,11 @@ private: auto iter = oldParentDir->links.find(linkObj.getObjShortName()); if (iter != oldParentDir->links.end()) { - writeNumberC<bool>(true); //mark beginning of entry + writePOD<bool>(true); //mark beginning of entry writeStringUtf8(iter->first); //save respecting case! (Windows) - writeNumberC<std::int64_t>(to<std::int64_t>(iter->second.lastWriteTimeRaw)); + writePOD<std::int64_t>(to<std::int64_t>(iter->second.lastWriteTimeRaw)); writeStringUtf8(iter->second.targetPath); - writeNumberC<std::int32_t>(iter->second.type); + writePOD<std::int32_t>(iter->second.type); } } } @@ -325,8 +354,8 @@ private: void processDir(const DirMapping& dirMap, const DirContainer* oldParentDir) { - const DirContainer* oldDir = NULL; - const Zstring* oldDirName = NULL; + const DirContainer* oldDir = nullptr; + const Zstring* oldDirName = nullptr; if (oldParentDir) //no data is also a "synchronous state"! { auto iter = oldParentDir->dirs.find(dirMap.getObjShortName()); @@ -343,7 +372,7 @@ private: { if (!dirMap.isEmpty<side>()) { - writeNumberC<bool>(true); //mark beginning of entry + writePOD<bool>(true); //mark beginning of entry writeStringUtf8(dirMap.getShortName<side>()); //save respecting case! (Windows) recurse(dirMap, oldDir); } @@ -352,7 +381,7 @@ private: { if (oldDir) { - writeNumberC<bool>(true); //mark beginning of entry + writePOD<bool>(true); //mark beginning of entry writeStringUtf8(*oldDirName); //save respecting case! (Windows) recurse(dirMap, oldDir); return; @@ -372,7 +401,7 @@ private: assert(false); break; case DIR_DIFFERENT_METADATA: - writeNumberC<bool>(true); + writePOD<bool>(true); writeStringUtf8(dirMap.getShortName<side>()); //ATTENTION: strictly this is a violation of the principle of reporting last synchronous state! //however in this case this will result in "last sync unsuccessful" for this directory within <automatic> algorithm, which is fine @@ -396,30 +425,23 @@ std::pair<DirInfoPtr, DirInfoPtr> zen::loadFromDisk(const BaseDirMapping& baseMa const StreamMapping streamListRight = ::loadStreams(fileNameRight); //throw FileError //find associated session: there can be at most one session within intersection of left and right ids - StreamMapping::const_iterator streamLeft = streamListLeft .end(); - StreamMapping::const_iterator streamRight = streamListRight.end(); for (auto iterLeft = streamListLeft.begin(); iterLeft != streamListLeft.end(); ++iterLeft) { auto iterRight = streamListRight.find(iterLeft->first); if (iterRight != streamListRight.end()) { - streamLeft = iterLeft; - streamRight = iterRight; - break; + //read streams into DirInfo + DirInfoPtr dirInfoLeft = StreamParser::execute(iterLeft ->second, fileNameLeft); //throw FileError + DirInfoPtr dirInfoRight = StreamParser::execute(iterRight->second, fileNameRight); //throw FileError + + return std::make_pair(dirInfoLeft, dirInfoRight); } } - if (streamLeft == streamListLeft .end() || - streamRight == streamListRight.end()) - throw FileErrorDatabaseNotExisting(_("Initial synchronization:") + L" \n\n" + - _("Database files do not share a common synchronization session:") + L" \n" + - L"\"" + fileNameLeft + L"\"\n" + - L"\"" + fileNameRight + L"\""); - //read streams into DirInfo - DirInfoPtr dirInfoLeft = StreamParser::execute(streamLeft ->second, fileNameLeft); //throw FileError - DirInfoPtr dirInfoRight = StreamParser::execute(streamRight->second, fileNameRight); //throw FileError - - return std::make_pair(dirInfoLeft, dirInfoRight); + throw FileErrorDatabaseNotExisting(_("Initial synchronization:") + L" \n\n" + + _("Database files do not share a common synchronization session:") + L" \n" + + L"\"" + fileNameLeft + L"\"\n" + + L"\"" + fileNameRight + L"\""); } @@ -440,75 +462,65 @@ void zen::saveToDisk(const BaseDirMapping& baseMapping) //throw FileError StreamMapping streamListLeft; StreamMapping streamListRight; - try //read file data: list of session ID + DirInfo-stream - { - streamListLeft = ::loadStreams(dbNameLeft); - } - catch (FileError&) {} //if error occurs: just overwrite old file! User is already informed about issues right after comparing! - try - { - streamListRight = ::loadStreams(dbNameRight); - } + //read file data: list of session ID + DirInfo-stream + try { streamListLeft = ::loadStreams(dbNameLeft ); } catch (FileError&) {} + try { streamListRight = ::loadStreams(dbNameRight); } + catch (FileError&) {} + //if error occurs: just overwrite old file! User is already informed about issues right after comparing! //find associated session: there can be at most one session within intersection of left and right ids - StreamMapping::iterator streamLeft = streamListLeft .end(); - StreamMapping::iterator streamRight = streamListRight.end(); + auto streamLeftOld = streamListLeft .cend(); + auto streamRightOld = streamListRight.cend(); for (auto iterLeft = streamListLeft.begin(); iterLeft != streamListLeft.end(); ++iterLeft) { auto iterRight = streamListRight.find(iterLeft->first); if (iterRight != streamListRight.end()) { - streamLeft = iterLeft; - streamRight = iterRight; + streamLeftOld = iterLeft; + streamRightOld = iterRight; break; } } //(try to) read old DirInfo - DirInfoPtr oldDirInfoLeft; - DirInfoPtr oldDirInfoRight; - try - { - if (streamLeft != streamListLeft .end() && - streamRight != streamListRight.end()) + DirInfoPtr dirInfoLeftOld; + DirInfoPtr dirInfoRightOld; + if (streamLeftOld != streamListLeft .end() && + streamRightOld != streamListRight.end()) + try { - oldDirInfoLeft = StreamParser::execute(streamLeft ->second, dbNameLeft ); //throw FileError - oldDirInfoRight = StreamParser::execute(streamRight->second, dbNameRight); //throw FileError + dirInfoLeftOld = StreamParser::execute(streamLeftOld ->second, dbNameLeft ); //throw FileError + dirInfoRightOld = StreamParser::execute(streamRightOld->second, dbNameRight); //throw FileError + } + catch (FileError&) + { + //if error occurs: just overwrite old file! User is already informed about issues right after comparing! + dirInfoLeftOld .reset(); //read both or none! + dirInfoRightOld.reset(); // } - } - catch (FileError&) - { - //if error occurs: just overwrite old file! User is already informed about issues right after comparing! - oldDirInfoLeft .reset(); //read both or none! - oldDirInfoRight.reset(); // - } //create new database entries - MemoryStream newStreamLeft = StreamGenerator<LEFT_SIDE >::execute(baseMapping, oldDirInfoLeft .get() ? &oldDirInfoLeft ->baseDirContainer : NULL, dbNameLeft); - MemoryStream newStreamRight = StreamGenerator<RIGHT_SIDE>::execute(baseMapping, oldDirInfoRight.get() ? &oldDirInfoRight->baseDirContainer : NULL, dbNameRight); + MemoryStream rawStreamLeftNew = StreamGenerator<LEFT_SIDE >::execute(baseMapping, dirInfoLeftOld .get() ? &dirInfoLeftOld ->baseDirContainer : nullptr, dbNameLeft); + MemoryStream rawStreamRightNew = StreamGenerator<RIGHT_SIDE>::execute(baseMapping, dirInfoRightOld.get() ? &dirInfoRightOld->baseDirContainer : nullptr, dbNameRight); //check if there is some work to do at all - { - const bool updateRequiredLeft = streamLeft == streamListLeft .end() || newStreamLeft != streamLeft ->second; - const bool updateRequiredRight = streamRight == streamListRight.end() || newStreamRight != streamRight->second; - //some users monitor the *.ffs_db file with RTS => don't touch the file if it isnt't strictly needed - if (!updateRequiredLeft && !updateRequiredRight) - return; - } - - //create/update DirInfo-streams - std::string sessionID = zen::generateGUID(); + if (streamLeftOld != streamListLeft .end() && rawStreamLeftNew == streamLeftOld ->second && + streamRightOld != streamListRight.end() && rawStreamRightNew == streamRightOld->second) + return; //some users monitor the *.ffs_db file with RTS => don't touch the file if it isnt't strictly needed //erase old session data - if (streamLeft != streamListLeft.end()) - streamListLeft.erase(streamLeft); - if (streamRight != streamListRight.end()) - streamListRight.erase(streamRight); + if (streamLeftOld != streamListLeft.end()) + streamListLeft.erase(streamLeftOld); + if (streamRightOld != streamListRight.end()) + streamListRight.erase(streamRightOld); + + //create/update DirInfo-streams + const std::string sessionID = zen::generateGUID(); //fill in new - streamListLeft .insert(std::make_pair(sessionID, newStreamLeft)); - streamListRight.insert(std::make_pair(sessionID, newStreamRight)); + streamListLeft .insert(std::make_pair(sessionID, rawStreamLeftNew)); + streamListRight.insert(std::make_pair(sessionID, rawStreamRightNew)); //write (temp-) files... zen::ScopeGuard guardTempFileLeft = zen::makeGuard([&] {zen::removeFile(dbNameLeftTmp); }); diff --git a/lib/dir_lock.cpp b/lib/dir_lock.cpp index 88fb19b3..87864ce4 100644 --- a/lib/dir_lock.cpp +++ b/lib/dir_lock.cpp @@ -28,7 +28,6 @@ #elif defined FFS_LINUX #include <sys/stat.h> -#include <cerrno> #include <unistd.h> #endif @@ -76,24 +75,24 @@ public: const char buffer[1] = {' '}; #ifdef FFS_WIN - //ATTENTION: setting file pointer IS required! => use CreateFile/FILE_GENERIC_WRITE + SetFilePointerEx! + //ATTENTION: setting file pointer IS required! => use CreateFile/GENERIC_WRITE + SetFilePointerEx! //although CreateFile/FILE_APPEND_DATA without SetFilePointerEx works locally, it MAY NOT work on some network shares creating a 4 gig file!!! const HANDLE fileHandle = ::CreateFile(applyLongPathPrefix(lockfilename_).c_str(), GENERIC_READ | GENERIC_WRITE, //use both when writing over network, see comment in file_io.cpp FILE_SHARE_READ, - NULL, + nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, - NULL); + nullptr); if (fileHandle == INVALID_HANDLE_VALUE) return; - ZEN_ON_BLOCK_EXIT(::CloseHandle(fileHandle)); + ZEN_ON_SCOPE_EXIT(::CloseHandle(fileHandle)); const LARGE_INTEGER moveDist = {}; if (!::SetFilePointerEx(fileHandle, //__in HANDLE hFile, moveDist, //__in LARGE_INTEGER liDistanceToMove, - NULL, //__out_opt PLARGE_INTEGER lpNewFilePointer, + nullptr, //__out_opt PLARGE_INTEGER lpNewFilePointer, FILE_END)) //__in DWORD dwMoveMethod return; @@ -102,13 +101,13 @@ public: buffer, //__out LPVOID lpBuffer, 1, //__in DWORD nNumberOfBytesToRead, &bytesWritten, //__out_opt LPDWORD lpNumberOfBytesWritten, - NULL); //__inout_opt LPOVERLAPPED lpOverlapped + nullptr); //__inout_opt LPOVERLAPPED lpOverlapped #elif defined FFS_LINUX - const int fileHandle = ::open(lockfilename_.c_str(), O_WRONLY | O_APPEND); //O_EXCL contains a race condition on NFS file systems: http://linux.die.net/man/2/open + const int fileHandle = ::open(lockfilename_.c_str(), O_WRONLY | O_APPEND); if (fileHandle == -1) return; - ZEN_ON_BLOCK_EXIT(::close(fileHandle)); + ZEN_ON_SCOPE_EXIT(::close(fileHandle)); const ssize_t bytesWritten = ::write(fileHandle, buffer, 1); (void)bytesWritten; @@ -127,41 +126,25 @@ UInt64 getLockFileSize(const Zstring& filename) //throw FileError, ErrorNotExist #ifdef FFS_WIN WIN32_FIND_DATA fileInfo = {}; const HANDLE searchHandle = ::FindFirstFile(applyLongPathPrefix(filename).c_str(), &fileInfo); - if (searchHandle == INVALID_HANDLE_VALUE) + if (searchHandle != INVALID_HANDLE_VALUE) { - const DWORD lastError = ::GetLastError(); - - std::wstring errorMessage = _("Error reading file attributes:") + L"\n\"" + filename + L"\"" + L"\n\n" + getLastErrorFormatted(lastError); - - if (lastError == ERROR_FILE_NOT_FOUND || - lastError == ERROR_PATH_NOT_FOUND || - lastError == ERROR_BAD_NETPATH || - lastError == ERROR_NETNAME_DELETED) - throw ErrorNotExisting(errorMessage); - else - throw FileError(errorMessage); + ::FindClose(searchHandle); + return zen::UInt64(fileInfo.nFileSizeLow, fileInfo.nFileSizeHigh); } - ::FindClose(searchHandle); - - return zen::UInt64(fileInfo.nFileSizeLow, fileInfo.nFileSizeHigh); - #elif defined FFS_LINUX struct ::stat fileInfo = {}; - if (::stat(filename.c_str(), &fileInfo) != 0) //follow symbolic links - { - const int lastError = errno; - - std::wstring errorMessage = _("Error reading file attributes:") + L"\n\"" + filename + L"\"" + L"\n\n" + getLastErrorFormatted(lastError); + if (::stat(filename.c_str(), &fileInfo) == 0) //follow symbolic links + return zen::UInt64(fileInfo.st_size); +#endif - if (lastError == ENOENT) - throw ErrorNotExisting(errorMessage); - else - throw FileError(errorMessage); - } + const ErrorCode lastError = getLastError(); + const std::wstring errorMessage = _("Error reading file attributes:") + L"\n\"" + filename + L"\"" + L"\n\n" + getLastErrorFormatted(lastError); - return zen::UInt64(fileInfo.st_size); -#endif + if (errorCodeForNotExisting(lastError)) + throw ErrorNotExisting(errorMessage); + else + throw FileError(errorMessage); } @@ -177,29 +160,6 @@ Zstring deleteAbandonedLockName(const Zstring& lockfilename) //make sure to NOT namespace { -//read string from file stream -inline -std::string readString(wxInputStream& stream) //throw std::exception -{ - const auto strLength = readPOD<std::uint32_t>(stream); - std::string output; - if (strLength > 0) - { - output.resize(strLength); //throw std::bad_alloc - stream.Read(&output[0], strLength); - } - return output; -} - - -inline -void writeString(wxOutputStream& stream, const std::string& str) //write string to filestream -{ - writePOD(stream, static_cast<std::uint32_t>(str.length())); - stream.Write(str.c_str(), str.length()); -} - - std::string getComputerId() //returns empty string on error { const wxString fhn = ::wxGetFullHostName(); @@ -234,9 +194,9 @@ struct LockInformation //some format checking here? - lockId = readString(stream); + lockId = readString<std::string>(stream); procDescr.processId = static_cast<ProcessId>(readPOD<std::uint64_t>(stream)); //possible loss of precision (32/64 bit process) covered by buildId - procDescr.computerId = readString(stream); + procDescr.computerId = readString<std::string>(stream); } void toStream(wxOutputStream& stream) const //write @@ -282,7 +242,7 @@ ProcessStatus getProcessStatus(const LockInformation::ProcessDescription& procDe return PROC_STATUS_NO_IDEA; //lock owned by different computer #ifdef FFS_WIN - if (procDescr.processId == ::GetCurrentProcessId()) //may seem obscure, but it's possible: a lock file is "stolen" and put back while the program is running + if (procDescr.processId == ::GetCurrentProcessId()) //may seem obscure, but it's possible: deletion failed or a lock file is "stolen" and put back while the program is running return PROC_STATUS_ITS_US; //note: ::OpenProcess() is no option as it may successfully return for crashed processes! @@ -291,7 +251,7 @@ ProcessStatus getProcessStatus(const LockInformation::ProcessDescription& procDe 0); //__in DWORD th32ProcessID if (snapshot == INVALID_HANDLE_VALUE) return PROC_STATUS_NO_IDEA; - ZEN_ON_BLOCK_EXIT(::CloseHandle(snapshot)); + ZEN_ON_SCOPE_EXIT(::CloseHandle(snapshot)); PROCESSENTRY32 processEntry = {}; processEntry.dwSize = sizeof(processEntry); @@ -315,7 +275,7 @@ ProcessStatus getProcessStatus(const LockInformation::ProcessDescription& procDe if (procDescr.processId <= 0 || procDescr.processId >= 65536) return PROC_STATUS_NO_IDEA; //invalid process id - return zen::dirExists(Zstr("/proc/") + zen::toString<Zstring>(procDescr.processId)) ? PROC_STATUS_RUNNING : PROC_STATUS_NOT_RUNNING; + return zen::dirExists(Zstr("/proc/") + zen::numberTo<Zstring>(procDescr.processId)) ? PROC_STATUS_RUNNING : PROC_STATUS_NOT_RUNNING; #endif } @@ -374,9 +334,8 @@ void waitOnDirLock(const Zstring& lockfilename, DirLockCallback* callback) //thr const zen::UInt64 fileSizeNew = ::getLockFileSize(lockfilename); //throw FileError, ErrorNotExisting wxLongLong currentTime = wxGetLocalTimeMillis(); - if (fileSizeNew != fileSizeOld) + if (fileSizeNew != fileSizeOld) //received life sign from lock { - //received life sign from lock fileSizeOld = fileSizeNew; lockSilentStart = currentTime; } @@ -413,7 +372,7 @@ void waitOnDirLock(const Zstring& lockfilename, DirLockCallback* callback) //thr long remainingSeconds = ((DETECT_EXITUS_INTERVAL - (wxGetLocalTimeMillis() - lockSilentStart)) / 1000).ToLong(); remainingSeconds = std::max(0L, remainingSeconds); - const std::wstring remSecMsg = replaceCpy(_P("1 sec", "%x sec", remainingSeconds), L"%x", toString<std::wstring>(remainingSeconds)); + const std::wstring remSecMsg = replaceCpy(_P("1 sec", "%x sec", remainingSeconds), L"%x", numberTo<std::wstring>(remainingSeconds)); callback->reportInfo(infoMsg + L" " + remSecMsg); } @@ -446,10 +405,10 @@ bool tryLock(const Zstring& lockfilename) //throw FileError const HANDLE fileHandle = ::CreateFile(applyLongPathPrefix(lockfilename).c_str(), GENERIC_READ | GENERIC_WRITE, //use both when writing over network, see comment in file_io.cpp FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - NULL, + nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, - NULL); + nullptr); if (fileHandle == INVALID_HANDLE_VALUE) { if (::GetLastError() == ERROR_FILE_EXISTS) @@ -473,7 +432,7 @@ bool tryLock(const Zstring& lockfilename) //throw FileError ::close(fileHandle); #endif - ScopeGuard guardLockFile = zen::makeGuard([&] { zen::removeFile(lockfilename); }); + ScopeGuard guardLockFile = zen::makeGuard([&] { removeFile(lockfilename); }); //write UUID at the beginning of the file: this ID is a universal identifier for this lock (no matter what the path is, considering symlinks, etc.) writeLockInfo(lockfilename); //throw FileError @@ -487,7 +446,7 @@ bool tryLock(const Zstring& lockfilename) //throw FileError class DirLock::SharedDirLock { public: - SharedDirLock(const Zstring& lockfilename, DirLockCallback* callback = NULL) : //throw FileError + SharedDirLock(const Zstring& lockfilename, DirLockCallback* callback = nullptr) : //throw FileError lockfilename_(lockfilename) { while (!::tryLock(lockfilename)) //throw FileError @@ -530,17 +489,14 @@ public: FileToUuidMap::const_iterator iterUuid = fileToUuid.find(lockfilename); if (iterUuid != fileToUuid.end()) { - const std::shared_ptr<SharedDirLock>& activeLock = findActive(iterUuid->second); //returns null-lock if not found - if (activeLock) + if (const std::shared_ptr<SharedDirLock>& activeLock = findActive(iterUuid->second)) //returns null-lock if not found return activeLock; //SharedDirLock is still active -> enlarge circle of shared ownership } try //actual check based on lock UUID, deadlock prevention: "lockfilename" may be an alternative name for an already active lock { const std::string lockId = retrieveLockId(lockfilename); //throw FileError, ErrorNotExisting - - const std::shared_ptr<SharedDirLock>& activeLock = findActive(lockId); //returns null-lock if not found - if (activeLock) + if (const std::shared_ptr<SharedDirLock>& activeLock = findActive(lockId)) //returns null-lock if not found { fileToUuid[lockfilename] = lockId; //perf-optimization: update relation return activeLock; @@ -549,8 +505,8 @@ public: catch (FileError&) {} //catch everything, let SharedDirLock constructor deal with errors, e.g. 0-sized/corrupted lock file //not yet in buffer, so create a new directory lock - std::shared_ptr<SharedDirLock> newLock(new SharedDirLock(lockfilename, callback)); //throw FileError - const std::string newLockId = retrieveLockId(lockfilename); //throw FileError, ErrorNotExisting + auto newLock = std::make_shared<SharedDirLock>(lockfilename, callback); //throw FileError + const std::string& newLockId = retrieveLockId(lockfilename); //throw FileError, ErrorNotExisting //update registry fileToUuid[lockfilename] = newLockId; //throw() @@ -564,16 +520,14 @@ private: std::shared_ptr<SharedDirLock> findActive(const std::string& lockId) //returns null-lock if not found { - UuidToLockMap::const_iterator iterLock = uuidToLock.find(lockId); + auto iterLock = uuidToLock.find(lockId); return iterLock != uuidToLock.end() ? iterLock->second.lock() : nullptr; //try to get shared_ptr; throw() } - typedef std::weak_ptr<SharedDirLock> SharedLock; - typedef std::string UniqueId; - typedef std::map<Zstring, UniqueId, LessFilename> FileToUuidMap; //n:1 handle uppper/lower case correctly - typedef std::map<UniqueId, SharedLock> UuidToLockMap; //1:1 + typedef std::map<Zstring, UniqueId, LessFilename> FileToUuidMap; //n:1 handle uppper/lower case correctly + typedef std::map<UniqueId, std::weak_ptr<SharedDirLock>> UuidToLockMap; //1:1 FileToUuidMap fileToUuid; //lockname |-> UUID; locks can be referenced by a lockfilename or alternatively a UUID UuidToLockMap uuidToLock; //UUID |-> "shared lock ownership" @@ -583,7 +537,7 @@ private: DirLock::DirLock(const Zstring& lockfilename, DirLockCallback* callback) //throw FileError { #ifdef FFS_WIN - const DWORD bufferSize = std::max(lockfilename.size(), static_cast<size_t>(10000)); + const DWORD bufferSize = 10000; std::vector<wchar_t> volName(bufferSize); if (::GetVolumePathName(lockfilename.c_str(), //__in LPCTSTR lpszFileName, &volName[0], //__out LPTSTR lpszVolumePathName, diff --git a/lib/dir_lock.h b/lib/dir_lock.h index 9938d554..3e9f2a79 100644 --- a/lib/dir_lock.h +++ b/lib/dir_lock.h @@ -25,11 +25,12 @@ RAII structure to place a directory lock against other FFS processes: - detects and resolves abandoned locks (instantly if lock is associated with local pc, else after 30 seconds) - temporary locks created during abandoned lock resolution keep "lockfilename"'s extension - race-free (Windows, almost on Linux(NFS)) + - currently NOT thread-safe! (static LockAdmin) */ class DirLock { public: - DirLock(const Zstring& lockfilename, DirLockCallback* callback = NULL); //throw FileError, callback only used during construction + DirLock(const Zstring& lockfilename, DirLockCallback* callback = nullptr); //throw FileError, callback only used during construction private: class LockAdmin; diff --git a/lib/error_log.cpp b/lib/error_log.cpp deleted file mode 100644 index a71e72e1..00000000 --- a/lib/error_log.cpp +++ /dev/null @@ -1,101 +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 (zhnmju123 AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "error_log.h" -#include <zen/time.h> -#include <zen/i18n.h> -#include <algorithm> - -using namespace zen; - - -void ErrorLogging::logMsg(const wxString& message, zen::MessageType type) -{ - Entry newEntry; - newEntry.type = type; - newEntry.time = std::time(NULL); - newEntry.message = message; - - messages.push_back(newEntry); - - ++statistics[type]; -} - - -int ErrorLogging::typeCount(int typeFilter) const -{ - int count = 0; - - if (typeFilter & TYPE_INFO) - count += statistics[TYPE_INFO]; - if (typeFilter & TYPE_WARNING) - count += statistics[TYPE_WARNING]; - if (typeFilter & TYPE_ERROR) - count += statistics[TYPE_ERROR]; - if (typeFilter & TYPE_FATAL_ERROR) - count += statistics[TYPE_FATAL_ERROR]; - - return count; -} - - -std::vector<wxString> ErrorLogging::getFormattedMessages(int typeFilter) const -{ - std::vector<wxString> output; - - std::for_each(messages.begin(), messages.end(), - [&](const Entry& entry) - { - if (entry.type & typeFilter) - output.push_back(formatMessage(entry)); - }); - - return output; -} - - -wxString ErrorLogging::formatMessage(const Entry& msg) -{ - wxString typeName; - switch (msg.type) - { - case TYPE_INFO: - typeName = _("Info"); - break; - case TYPE_WARNING: - typeName = _("Warning"); - break; - case TYPE_ERROR: - typeName = _("Error"); - break; - case TYPE_FATAL_ERROR: - typeName = _("Fatal Error"); - break; - } - - const wxString prefix = L"[" + formatTime<wxString>(FORMAT_TIME, localTime(msg.time)) + L"] " + typeName + L": "; - - wxString formattedText = prefix; - for (auto iter = msg.message.begin(); iter != msg.message.end(); ) - if (*iter == L'\n') - { - formattedText += L'\n'; - - wxString blanks; - blanks.resize(prefix.size(), L' '); - formattedText += blanks; - - do //remove duplicate newlines - { - ++iter; - } - while (iter != msg.message.end() && *iter == L'\n'); - } - else - formattedText += *iter++; - - return formattedText; -} diff --git a/lib/error_log.h b/lib/error_log.h deleted file mode 100644 index 62aac70f..00000000 --- a/lib/error_log.h +++ /dev/null @@ -1,49 +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 (zhnmju123 AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef ERRORLOGGING_H_INCLUDED -#define ERRORLOGGING_H_INCLUDED - -#include <map> -#include <vector> -#include <wx/string.h> - -namespace zen -{ -enum MessageType -{ - TYPE_INFO = 1, - TYPE_WARNING = 2, - TYPE_ERROR = 4, - TYPE_FATAL_ERROR = 8, -}; - -class ErrorLogging -{ -public: - void logMsg(const wxString& message, MessageType type); - - int typeCount(int typeFilter = TYPE_INFO | TYPE_WARNING | TYPE_ERROR | TYPE_FATAL_ERROR) const; - - std::vector<wxString> getFormattedMessages(int typeFilter = TYPE_INFO | TYPE_WARNING | TYPE_ERROR | TYPE_FATAL_ERROR) const; - -private: - struct Entry - { - MessageType type; - time_t time; - wxString message; - }; - - static wxString formatMessage(const Entry& msg); - - std::vector<Entry> messages; //list of non-resolved errors and warnings - - mutable std::map<MessageType, int> statistics; -}; -} - -#endif // ERRORLOGGING_H_INCLUDED diff --git a/lib/hard_filter.cpp b/lib/hard_filter.cpp index fcef6a9f..603d27b0 100644 --- a/lib/hard_filter.cpp +++ b/lib/hard_filter.cpp @@ -57,8 +57,17 @@ HardFilter::FilterRef HardFilter::loadFilter(wxInputStream& stream) } -//-------------------------------------------------------------------------------------------------- -void addFilterEntry(const Zstring& filtername, std::set<Zstring>& fileFilter, std::set<Zstring>& directoryFilter) +namespace +{ +//constructing them in addFilterEntry becomes perf issue for large filter lists +const Zstring asterisk(Zstr('*')); +const Zstring sepAsterisk = FILE_NAME_SEPARATOR + asterisk; +const Zstring asteriskSep = asterisk + FILE_NAME_SEPARATOR; +const Zstring asteriskSepAsterisk = asteriskSep + asterisk; +} + + +void addFilterEntry(const Zstring& filtername, std::vector<Zstring>& fileFilter, std::vector<Zstring>& directoryFilter) { Zstring filterFormatted = filtername; @@ -68,70 +77,73 @@ void addFilterEntry(const Zstring& filtername, std::set<Zstring>& fileFilter, st #elif defined FFS_LINUX //Linux DOES distinguish between upper/lower-case: nothing to do here #endif + if (startsWith(filterFormatted, FILE_NAME_SEPARATOR)) // \abc + filterFormatted = afterFirst(filterFormatted, FILE_NAME_SEPARATOR); //leading separator is optional! - const Zstring sepAsterisk = Zstring(FILE_NAME_SEPARATOR) + Zchar('*'); - const Zstring sepQuestionMark = Zstring(FILE_NAME_SEPARATOR) + Zchar('?'); - const Zstring asteriskSep = Zstring(Zstr('*')) + FILE_NAME_SEPARATOR; - const Zstring questionMarkSep = Zstring(Zstr('?')) + FILE_NAME_SEPARATOR; - - //-------------------------------------------------------------------------------------------------- - //add some syntactic sugar: handle beginning of filtername - if (startsWith(filterFormatted, FILE_NAME_SEPARATOR)) + //some syntactic sugar: + if (filterFormatted == asteriskSepAsterisk) // *\* := match everything except files directly in base directory { - //remove leading separators (keep BEFORE test for Zstring::empty()!) - filterFormatted = afterFirst(filterFormatted, FILE_NAME_SEPARATOR); + fileFilter. push_back(filterFormatted); + directoryFilter.push_back(asterisk); + return; } - else if (startsWith(filterFormatted, asteriskSep) || // *\abc - startsWith(filterFormatted, questionMarkSep)) // ?\abc + //more syntactic sugar: handle beginning of filtername + else if (startsWith(filterFormatted, asteriskSep)) // *\abc { - addFilterEntry(Zstring(filterFormatted.c_str() + 1), fileFilter, directoryFilter); //prevent further recursion by prefix + addFilterEntry(filterFormatted.c_str() + 2, fileFilter, directoryFilter); //recursion is finite } - //-------------------------------------------------------------------------------------------------- //even more syntactic sugar: handle end of filtername if (endsWith(filterFormatted, FILE_NAME_SEPARATOR)) { const Zstring candidate = beforeLast(filterFormatted, FILE_NAME_SEPARATOR); if (!candidate.empty()) - directoryFilter.insert(candidate); //only relevant for directory filtering + directoryFilter.push_back(candidate); //only relevant for directory filtering } - else if (endsWith(filterFormatted, sepAsterisk) || // abc\* - endsWith(filterFormatted, sepQuestionMark)) // abc\? + else if (endsWith(filterFormatted, sepAsterisk)) // abc\* { - fileFilter.insert( filterFormatted); - directoryFilter.insert(filterFormatted); + fileFilter .push_back(filterFormatted); + directoryFilter.push_back(filterFormatted); const Zstring candidate = beforeLast(filterFormatted, FILE_NAME_SEPARATOR); if (!candidate.empty()) - directoryFilter.insert(candidate); //only relevant for directory filtering + directoryFilter.push_back(candidate); //only relevant for directory filtering } else if (!filterFormatted.empty()) { - fileFilter. insert(filterFormatted); - directoryFilter.insert(filterFormatted); + fileFilter. push_back(filterFormatted); + directoryFilter.push_back(filterFormatted); } } namespace { -template <class T> inline -const T* cStringFind(const T* str1, T ch) //strchr() +template <class Char> inline +const Char* cStringFind(const Char* str, Char ch) //strchr() { - while (*str1 != ch) //ch is allowed to be 0 by contract! must return end of string in this case + for (;;) { - if (*str1 == 0) - return NULL; - ++str1; + const Char s = *str; + if (s == ch) //ch is allowed to be 0 by contract! must return end of string in this case + return str; + + if (s == 0) + return nullptr; + ++str; } - return str1; } + bool matchesMask(const Zchar* str, const Zchar* mask) { - for (Zchar ch; (ch = *mask) != 0; ++mask, ++str) + for (;; ++mask, ++str) { - switch (ch) + Zchar m = *mask; + if (m == 0) + return *str == 0; + + switch (m) { case Zstr('?'): if (*str == 0) @@ -139,96 +151,91 @@ bool matchesMask(const Zchar* str, const Zchar* mask) break; case Zstr('*'): - //advance to next non-*/? char + //advance mask to next non-* char do { - ++mask; - ch = *mask; + m = *++mask; } - while (ch == Zstr('*') || ch == Zstr('?')); - //if mask ends with '*': - if (ch == 0) + while (m == Zstr('*')); + + if (m == 0) //mask ends with '*': return true; + //*? - pattern + if (m == Zstr('?')) + { + ++mask; + while (*str++ != 0) + if (matchesMask(str, mask)) + return true; + return false; + } + + //*[letter] - pattern ++mask; - while ((str = cStringFind(str, ch)) != NULL) + for (;;) { + str = cStringFind(str, m); + if (!str) + return false; + ++str; if (matchesMask(str, mask)) return true; } - return false; default: - if (*str != ch) + if (*str != m) return false; } } - return *str == 0; } + //returns true if string matches at least the beginning of mask inline bool matchesMaskBegin(const Zchar* str, const Zchar* mask) { - for (Zchar ch; (ch = *mask) != 0; ++mask, ++str) + for (;; ++mask, ++str) { - if (*str == 0) - return true; + const Zchar m = *mask; + if (m == 0) + return *str == 0; - switch (ch) + switch (m) { case Zstr('?'): + if (*str == 0) + return true; break; case Zstr('*'): return true; default: - if (*str != ch) - return false; + if (*str != m) + return *str == 0; } } - return *str == 0; } } -class MatchFound : public std::unary_function<Zstring, bool> -{ -public: - MatchFound(const Zstring& name) : name_(name) {} - - bool operator()(const Zstring& mask) const - { - return matchesMask(name_.c_str(), mask.c_str()); - } -private: - const Zstring& name_; -}; - - inline -bool matchesFilter(const Zstring& nameFormatted, const std::set<Zstring>& filter) +bool matchesFilter(const Zstring& name, const std::vector<Zstring>& filter) { - return std::find_if(filter.begin(), filter.end(), MatchFound(nameFormatted)) != filter.end(); + return std::any_of(filter.begin(), filter.end(), [&](const Zstring& mask) { return matchesMask(name.c_str(), mask.c_str()); }); } inline -bool matchesFilterBegin(const Zstring& nameFormatted, const std::set<Zstring>& filter) +bool matchesFilterBegin(const Zstring& name, const std::vector<Zstring>& filter) { - for (std::set<Zstring>::const_iterator i = filter.begin(); i != filter.end(); ++i) - if (matchesMaskBegin(nameFormatted.c_str(), i->c_str())) - return true; - return false; - - // return std::find_if(filter.begin(), filter.end(), - // boost::bind(matchesMaskBegin, nameFormatted.c_str(), _1)) != filter.end(); + return std::any_of(filter.begin(), filter.end(), [&](const Zstring& mask) { return matchesMaskBegin(name.c_str(), mask.c_str()); }); } -std::vector<Zstring> compoundStringToFilter(const Zstring& filterString) +std::vector<Zstring> splitByDelimiter(const Zstring& filterString) { //delimiters may be ';' or '\n' std::vector<Zstring> output; @@ -256,16 +263,29 @@ NameFilter::NameFilter(const Zstring& includeFilter, const Zstring& excludeFilte includeFilterTmp(includeFilter), //save constructor arguments for serialization excludeFilterTmp(excludeFilter) { - //no need for regular expressions! In tests wxRegex was by factor of 10 slower than wxString::Matches()!! + //no need for regular expressions: In tests wxRegex was by factor of 10 slower than wxString::Matches() //load filter into vectors of strings //delimiters may be ';' or '\n' - const std::vector<Zstring>& includeList = compoundStringToFilter(includeFilter); - const std::vector<Zstring>& excludeList = compoundStringToFilter(excludeFilter); + const std::vector<Zstring>& includeList = splitByDelimiter(includeFilter); + const std::vector<Zstring>& excludeList = splitByDelimiter(excludeFilter); //setup include/exclude filters for files and directories std::for_each(includeList.begin(), includeList.end(), [&](const Zstring& entry) { addFilterEntry(entry, filterFileIn, filterFolderIn); }); std::for_each(excludeList.begin(), excludeList.end(), [&](const Zstring& entry) { addFilterEntry(entry, filterFileEx, filterFolderEx); }); + + auto removeDuplicates = [](std::vector<Zstring>& cont) + { + std::vector<Zstring> output; + std::set<Zstring> used; + std::copy_if(cont.begin(), cont.end(), std::back_inserter(output), [&](const Zstring& item) { return used.insert(item).second; }); + output.swap(cont); + }; + + removeDuplicates(filterFileIn); + removeDuplicates(filterFolderIn); + removeDuplicates(filterFileEx); + removeDuplicates(filterFolderEx); } @@ -278,14 +298,14 @@ bool NameFilter::passFileFilter(const Zstring& relFilename) const const Zstring& nameFormatted = relFilename; //nothing to do here #endif - return matchesFilter(nameFormatted, filterFileIn) && //process include filters - !matchesFilter(nameFormatted, filterFileEx); //process exclude filters + return matchesFilter(nameFormatted, filterFileIn) && //process include filters + !matchesFilter(nameFormatted, filterFileEx); //process exclude filters } bool NameFilter::passDirFilter(const Zstring& relDirname, bool* subObjMightMatch) const { - assert(subObjMightMatch == NULL || *subObjMightMatch == true); //check correct usage + assert(!subObjMightMatch || *subObjMightMatch == true); //check correct usage #ifdef FFS_WIN //Windows does NOT distinguish between upper/lower-case Zstring nameFormatted = relDirname; @@ -317,6 +337,17 @@ bool NameFilter::passDirFilter(const Zstring& relDirname, bool* subObjMightMatch } +bool NameFilter::isNull(const Zstring& includeFilter, const Zstring& excludeFilter) +{ + Zstring include = includeFilter; + Zstring exclude = excludeFilter; + trim(include); + trim(exclude); + + return include == Zstr("*") && exclude.empty(); + //return NameFilter(includeFilter, excludeFilter).isNull(); -> very expensive for huge lists +} + bool NameFilter::isNull() const { static NameFilter output(Zstr("*"), Zstring()); diff --git a/lib/hard_filter.h b/lib/hard_filter.h index 476f5ac1..1a9943a3 100644 --- a/lib/hard_filter.h +++ b/lib/hard_filter.h @@ -7,7 +7,7 @@ #ifndef FFS_FILTER_H_INCLUDED #define FFS_FILTER_H_INCLUDED -#include <set> +#include <vector> #include <memory> #include <wx/stream.h> #include <zen/zstring.h> @@ -88,7 +88,9 @@ public: virtual bool passFileFilter(const Zstring& relFilename) const; virtual bool passDirFilter(const Zstring& relDirname, bool* subObjMightMatch) const; + virtual bool isNull() const; + static bool isNull(const Zstring& includeFilter, const Zstring& excludeFilter); //*fast* check without expensively constructing NameFilter instance! private: friend class HardFilter; @@ -97,10 +99,10 @@ private: static FilterRef load(wxInputStream& stream); //"serial constructor" virtual bool cmpLessSameType(const HardFilter& other) const; - std::set<Zstring> filterFileIn; //upper case (windows) - std::set<Zstring> filterFolderIn; // - std::set<Zstring> filterFileEx; // - std::set<Zstring> filterFolderEx; // + std::vector<Zstring> filterFileIn; // + std::vector<Zstring> filterFolderIn; //upper case (windows) + unique items by construction + std::vector<Zstring> filterFileEx; // + std::vector<Zstring> filterFolderEx; // const Zstring includeFilterTmp; //save constructor arguments for serialization const Zstring excludeFilterTmp; // @@ -162,7 +164,7 @@ bool NullFilter::passFileFilter(const Zstring& relFilename) const inline bool NullFilter::passDirFilter(const Zstring& relDirname, bool* subObjMightMatch) const { - assert(subObjMightMatch == NULL || *subObjMightMatch == true); //check correct usage + assert(!subObjMightMatch || *subObjMightMatch == true); //check correct usage return true; } diff --git a/lib/icon_buffer.cpp b/lib/icon_buffer.cpp index 647228e3..e1518d30 100644 --- a/lib/icon_buffer.cpp +++ b/lib/icon_buffer.cpp @@ -17,9 +17,7 @@ #include <zen/win_ver.h> #elif defined FFS_LINUX -#include <giomm/file.h> -#include <gtkmm/icontheme.h> -#include <gtkmm/main.h> +#include <gtk/gtk.h> #endif using namespace zen; @@ -59,10 +57,10 @@ public: typedef GdkPixbuf* HandleType; #endif - IconHolder(HandleType handle = 0) : handle_(handle) {} //take ownership! + explicit IconHolder(HandleType handle = 0) : handle_(handle) {} //take ownership! //icon holder has value semantics! - IconHolder(const IconHolder& other) : handle_(other.handle_ == NULL ? NULL : + IconHolder(const IconHolder& other) : handle_(other.handle_ == nullptr ? nullptr : #ifdef FFS_WIN ::CopyIcon(other.handle_) #elif defined FFS_LINUX @@ -70,7 +68,7 @@ public: #endif ) {} - IconHolder(IconHolder&& other) : handle_(other.handle_) { other.handle_ = NULL; } + IconHolder(IconHolder&& other) : handle_(other.handle_) { other.handle_ = nullptr; } IconHolder& operator=(IconHolder other) //unifying assignment: no need for r-value reference optimization! { @@ -80,11 +78,11 @@ public: ~IconHolder() { - if (handle_ != NULL) + if (handle_ != nullptr) #ifdef FFS_WIN ::DestroyIcon(handle_); #elif defined FFS_LINUX - ::g_object_unref(handle_); + ::g_object_unref(handle_); //superseedes "::gdk_pixbuf_unref"! #endif } @@ -92,7 +90,7 @@ public: wxIcon toWxIcon(int expectedSize) const //copy HandleType, caller needs to take ownership! { - if (handle_ == NULL) + if (handle_ == nullptr) return wxNullIcon; IconHolder clone(*this); @@ -107,7 +105,7 @@ public: if (::GetIconInfo(clone.handle_, &icoInfo)) { ::DeleteObject(icoInfo.hbmMask); //nice potential for a GDI leak! - ZEN_ON_BLOCK_EXIT(::DeleteObject(icoInfo.hbmColor)); // + ZEN_ON_SCOPE_EXIT(::DeleteObject(icoInfo.hbmColor)); // BITMAP bmpInfo = {}; if (::GetObject(icoInfo.hbmColor, //__in HGDIOBJ hgdiobj, @@ -130,7 +128,7 @@ public: #elif defined FFS_LINUX // newIcon.SetPixbuf(clone.handle_); // transfer ownership!! #endif // - clone.handle_ = NULL; // + clone.handle_ = nullptr; // return newIcon; } @@ -140,7 +138,7 @@ private: public: //use member pointer as implicit conversion to bool (C++ Templates - Vandevoorde/Josuttis; chapter 20) - operator int ConversionToBool::* () const { return handle_ != NULL ? &ConversionToBool::dummy : NULL; } + operator int ConversionToBool::* () const { return handle_ != nullptr ? &ConversionToBool::dummy : nullptr; } }; @@ -210,13 +208,13 @@ IconHolder getIconByAttribute(LPCWSTR pszPath, DWORD dwFileAttributes, IconBuffe SHGFI_SYSICONINDEX | SHGFI_USEFILEATTRIBUTES); //no need to IUnknown::Release() imgList! if (!imgList) - return NULL; + return IconHolder(); boost::call_once(initGetIconByIndexOnce, []() //thread-safe init { getIconByIndex = DllFun<thumb::GetIconByIndexFct>(thumb::getDllName(), thumb::getIconByIndexFctName); }); - return getIconByIndex ? static_cast<HICON>(getIconByIndex(fileInfo.iIcon, getShilIconType(sz))) : NULL; + return IconHolder(getIconByIndex ? static_cast<HICON>(getIconByIndex(fileInfo.iIcon, getShilIconType(sz))) : nullptr); } @@ -243,19 +241,11 @@ IconHolder getThumbnail(const Zstring& filename, int requestedSize) //return 0 o { getThumbnailIcon = DllFun<GetThumbnailFct>(getDllName(), getThumbnailFctName); }); - return getThumbnailIcon ? static_cast< ::HICON>(getThumbnailIcon(filename.c_str(), requestedSize)) : NULL; + return IconHolder(getThumbnailIcon ? static_cast< ::HICON>(getThumbnailIcon(filename.c_str(), requestedSize)) : nullptr); #elif defined FFS_LINUX - //call Gtk::Main::init_gtkmm_internals() on application startup!! - try - { - Glib::RefPtr<Gdk::Pixbuf> iconPixbuf = Gdk::Pixbuf::create_from_file(filename.c_str(), requestedSize, requestedSize); - if (iconPixbuf) - return IconHolder(iconPixbuf->gobj_copy()); //copy and pass icon ownership (may be 0) - } - catch (const Glib::Error&) {} - - return IconHolder(); + GdkPixbuf* pixBuf = gdk_pixbuf_new_from_file_at_size(filename.c_str(), requestedSize, requestedSize, nullptr); + return IconHolder(pixBuf); //pass ownership (may be 0) #endif } @@ -276,30 +266,11 @@ IconHolder getGenericFileIcon(IconBuffer::IconSize sz) #elif defined FFS_LINUX const int requestedSize = cvrtSize(sz); - try - { - Glib::RefPtr<Gtk::IconTheme> iconTheme = Gtk::IconTheme::get_default(); - if (iconTheme) - { - Glib::RefPtr<Gdk::Pixbuf> iconPixbuf; - std::find_if(mimeFileIcons, mimeFileIcons + sizeof(mimeFileIcons) / sizeof(mimeFileIcons[0]), - [&](const char* mimeName) -> bool - { - try - { - iconPixbuf = iconTheme->load_icon(mimeName, requestedSize, Gtk::ICON_LOOKUP_USE_BUILTIN); - } - catch (const Glib::Error&) { return false; } - - return iconPixbuf; - } - ); - if (iconPixbuf) - return IconHolder(iconPixbuf->gobj_copy()); // transfer ownership!! - } - } - catch (const Glib::Error&) {} + if (GtkIconTheme* defaultTheme = gtk_icon_theme_get_default()) //not owned! + for (auto iter = std::begin(mimeFileIcons); iter != std::end(mimeFileIcons); ++iter) + if (GdkPixbuf* pixBuf = gtk_icon_theme_load_icon(defaultTheme, *iter, requestedSize, GTK_ICON_LOOKUP_USE_BUILTIN, nullptr)) + return IconHolder(pixBuf); //pass ownership (may be nullptr) return IconHolder(); #endif } @@ -343,44 +314,35 @@ IconHolder getAssociatedIcon(const Zstring& filename, IconBuffer::IconSize sz) //http://msdn.microsoft.com/en-us/library/windows/desktop/bb762185(v=vs.85).aspx if (!imgList) - return NULL; + return IconHolder(); //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::GetIconByIndexFct>(thumb::getDllName(), thumb::getIconByIndexFctName); }); - return getIconByIndex ? static_cast<HICON>(getIconByIndex(fileInfo.iIcon, getShilIconType(sz))) : NULL; + return IconHolder(getIconByIndex ? static_cast<HICON>(getIconByIndex(fileInfo.iIcon, getShilIconType(sz))) : nullptr); #elif defined FFS_LINUX const int requestedSize = cvrtSize(sz); - //call Gtk::Main::init_gtkmm_internals() on application startup!! - try + + GFile* file = g_file_new_for_path(filename.c_str()); //never fails + ZEN_ON_SCOPE_EXIT(g_object_unref(file);) + + if (GFileInfo* fileInfo = g_file_query_info(file, G_FILE_ATTRIBUTE_STANDARD_ICON, G_FILE_QUERY_INFO_NONE, nullptr, nullptr)) { - Glib::RefPtr<Gio::File> fileObj = Gio::File::create_for_path(filename.c_str()); //never fails - Glib::RefPtr<Gio::FileInfo> fileInfo = fileObj->query_info(G_FILE_ATTRIBUTE_STANDARD_ICON); - if (fileInfo) - { - Glib::RefPtr<Gio::Icon> gicon = fileInfo->get_icon(); - if (gicon) - { - Glib::RefPtr<Gtk::IconTheme> iconTheme = Gtk::IconTheme::get_default(); - if (iconTheme) + ZEN_ON_SCOPE_EXIT(g_object_unref(fileInfo);) + + if (GIcon* gicon = g_file_info_get_icon(fileInfo)) //not owned! + if (GtkIconTheme* defaultTheme = gtk_icon_theme_get_default()) //not owned! + if (GtkIconInfo* iconInfo = gtk_icon_theme_lookup_by_gicon(defaultTheme, gicon, requestedSize, GTK_ICON_LOOKUP_USE_BUILTIN)) //this may fail if icon is not installed on system { - Gtk::IconInfo iconInfo = iconTheme->lookup_icon(gicon, requestedSize, Gtk::ICON_LOOKUP_USE_BUILTIN); //this may fail if icon is not installed on system - if (iconInfo) - { - Glib::RefPtr<Gdk::Pixbuf> iconPixbuf = iconInfo.load_icon(); //render icon into Pixbuf - if (iconPixbuf) - return IconHolder(iconPixbuf->gobj_copy()); //copy and pass icon ownership (may be 0) - } + ZEN_ON_SCOPE_EXIT(gtk_icon_info_free(iconInfo);) + if (GdkPixbuf* pixBuf = gtk_icon_info_load_icon(iconInfo, nullptr)) + return IconHolder(pixBuf); //pass ownership (may be nullptr) } - } - } } - catch (const Glib::Error&) {} - //fallback: icon lookup may fail because some icons are currently not present on system return ::getGenericFileIcon(sz); #endif @@ -450,14 +412,14 @@ typedef std::queue<Zstring> IconDbSequence; //entryName class Buffer { public: - bool requestFileIcon(const Zstring& fileName, IconHolder* icon = NULL) + bool requestFileIcon(const Zstring& fileName, IconHolder* icon = nullptr) { boost::lock_guard<boost::mutex> dummy(lockBuffer); auto iter = iconMappping.find(fileName); if (iter != iconMappping.end()) { - if (icon != NULL) + if (icon != nullptr) *icon = iter->second; return true; } @@ -518,8 +480,8 @@ void WorkerThread::operator()() //thread entry //Prerequisites, see thumbnail.h //1. Initialize COM - ::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); - ZEN_ON_BLOCK_EXIT(::CoUninitialize()); + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); + ZEN_ON_SCOPE_EXIT(::CoUninitialize()); //2. Initialize system image list typedef BOOL (WINAPI* FileIconInitFun)(BOOL fRestoreCache); diff --git a/lib/icon_buffer.h b/lib/icon_buffer.h index 3f77a520..f3a358ef 100644 --- a/lib/icon_buffer.h +++ b/lib/icon_buffer.h @@ -32,7 +32,7 @@ public: int getSize() const; //*maximum* icon size in pixel - bool requestFileIcon(const Zstring& filename, wxIcon* icon = NULL); //returns false if icon is not in buffer + 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; private: diff --git a/lib/localization.cpp b/lib/localization.cpp index e8867129..3c997074 100644 --- a/lib/localization.cpp +++ b/lib/localization.cpp @@ -183,7 +183,7 @@ ExistingTranslations::ExistingTranslations() for (auto i = lngFiles.begin(); i != lngFiles.end(); ++i) try { - std::string stream = loadStream(*i);; //throw XmlFileError + std::string stream = loadStream(*i); //throw XmlFileError try { lngfile::TransHeader lngHeader; @@ -193,8 +193,7 @@ ExistingTranslations::ExistingTranslations() There is some buggy behavior in wxWidgets which maps "zh_TW" to simplified chinese. Fortunately locales can be also entered as description. I changed to "Chinese (Traditional)" which works fine. */ - const wxLanguageInfo* locInfo = wxLocale::FindLanguageInfo(utf8CvrtTo<wxString>(lngHeader.localeName)); - if (locInfo) + if (const wxLanguageInfo* locInfo = wxLocale::FindLanguageInfo(utf8CvrtTo<wxString>(lngHeader.localeName))) { ExistingTranslations::Entry newEntry; newEntry.languageID = locInfo->Language; diff --git a/lib/norm_filter.h b/lib/norm_filter.h index a47fd910..dc73bc6b 100644 --- a/lib/norm_filter.h +++ b/lib/norm_filter.h @@ -29,7 +29,7 @@ NormalizedFilter normalizeFilters(const FilterConfig& global, const FilterConfig inline bool isNullFilter(const FilterConfig& filterCfg) { - return NameFilter(filterCfg.includeFilter, filterCfg.excludeFilter).isNull() && + return NameFilter::isNull(filterCfg.includeFilter, filterCfg.excludeFilter) && SoftFilter(filterCfg.timeSpan, filterCfg.unitTimeSpan, filterCfg.sizeMin, filterCfg.unitSizeMin, filterCfg.sizeMax, filterCfg.unitSizeMax).isNull(); diff --git a/lib/parallel_scan.cpp b/lib/parallel_scan.cpp index 774cbeb7..6c9fd3ee 100644 --- a/lib/parallel_scan.cpp +++ b/lib/parallel_scan.cpp @@ -88,22 +88,22 @@ DiskInfo retrieveDiskInfo(const Zstring& pathName) 0, OPEN_EXISTING, 0, - NULL); + nullptr); if (hVolume == INVALID_HANDLE_VALUE) return output; - ZEN_ON_BLOCK_EXIT(::CloseHandle(hVolume)); + ZEN_ON_SCOPE_EXIT(::CloseHandle(hVolume)); std::vector<char> buffer(sizeof(VOLUME_DISK_EXTENTS) + sizeof(DISK_EXTENT)); //reserve buffer for at most one disk! call below will then fail if volume spans multiple disks! DWORD bytesReturned = 0; if (!::DeviceIoControl(hVolume, // handle to device IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, // dwIoControlCode - NULL, // lpInBuffer + nullptr, // lpInBuffer 0, // nInBufferSize &buffer[0], // output buffer static_cast<DWORD>(buffer.size()), // size of output buffer &bytesReturned, // number of bytes returned - NULL)) // OVERLAPPED structure + nullptr)) // OVERLAPPED structure return output; const VOLUME_DISK_EXTENTS& volDisks = *reinterpret_cast<VOLUME_DISK_EXTENTS*>(&buffer[0]); @@ -254,7 +254,7 @@ public: if (activeCount >= 2) { statusText += L" " + _P("[1 Thread]", "[%x Threads]", activeCount); - replace(statusText, L"%x", toString<std::wstring>(activeCount)); + replace(statusText, L"%x", numberTo<std::wstring>(activeCount)); } statusText += std::wstring(L" \n") + L'\"' + filename + L'\"'; return statusText; @@ -481,7 +481,7 @@ public: void operator()() //thread entry { acb_->incActiveWorker(); - ZEN_ON_BLOCK_EXIT(acb_->decActiveWorker();); + ZEN_ON_SCOPE_EXIT(acb_->decActiveWorker();); std::for_each(workload_.begin(), workload_.end(), [&](std::pair<DirectoryKey, DirectoryValue*>& item) @@ -515,7 +515,7 @@ public: break; } - DstHackCallback* dstCallbackPtr = NULL; + DstHackCallback* dstCallbackPtr = nullptr; #ifdef FFS_WIN DstHackCallbackImpl dstCallback(*acb_, threadID_); dstCallbackPtr = &dstCallback; @@ -546,7 +546,7 @@ void zen::fillBuffer(const std::set<DirectoryKey>& keysToRead, //in std::vector<boost::thread> worker; //note: GCC doesn't allow to construct an array of empty threads since they would be initialized by const boost::thread& worker.reserve(buckets.size()); - zen::ScopeGuard guardWorker = zen::makeGuard([&]() + zen::ScopeGuard guardWorker = zen::makeGuard([&] { std::for_each(worker.begin(), worker.end(), [](boost::thread& wt) { wt.interrupt(); }); //interrupt all at once, then join std::for_each(worker.begin(), worker.end(), [](boost::thread& wt) { wt.join(); }); diff --git a/lib/parse_lng.h b/lib/parse_lng.h index e876c5a9..07932c3a 100644 --- a/lib/parse_lng.h +++ b/lib/parse_lng.h @@ -213,15 +213,6 @@ private: TokenMap tokens; }; -struct IsWhiteSpace : public std::unary_function<char, bool> -{ - bool operator()(char c) const - { - const unsigned char usc = c; //caveat 1: std::isspace() takes an int, but expects an unsigned char - return usc < 128 && //caveat 2: some parts of UTF-8 chars are erroneously seen as whitespace, e.g. the a0 from "\xec\x8b\a0" (MSVC) - std::isspace(usc) != 0; //[!] - } -}; class Scanner { @@ -231,7 +222,7 @@ public: Token nextToken() { //skip whitespace - pos = std::find_if(pos, stream.end(), std::not1(IsWhiteSpace())); + pos = std::find_if(pos, stream.end(), [](char c) { return !zen::isWhiteSpace(c); }); if (pos == stream.end()) return Token(Token::TK_END); @@ -289,7 +280,7 @@ private: static void normalize(std::string& text) { //remmove whitespace from end - while (!text.empty() && IsWhiteSpace()(*text.rbegin())) + while (!text.empty() && zen::isWhiteSpace(*text.rbegin())) text.resize(text.size() - 1); //ensure c-style line breaks diff --git a/lib/parse_plural.h b/lib/parse_plural.h index 297aaafc..3c5820d0 100644 --- a/lib/parse_plural.h +++ b/lib/parse_plural.h @@ -9,6 +9,7 @@ #include <list> #include <memory> +#include <functional> #include <zen/string_base.h> @@ -185,7 +186,7 @@ private: Token nextToken() { //skip whitespace - pos = std::find_if(pos, stream.end(), std::not1(std::ptr_fun(std::iswspace))); + pos = std::find_if(pos, stream.end(), [](char c) { return !zen::isWhiteSpace(c); }); if (pos == stream.end()) return Token(Token::TK_END); @@ -196,12 +197,12 @@ private: return Token(i->second); } - Wstring::const_iterator digitEnd = std::find_if(pos, stream.end(), std::not1(std::ptr_fun(std::iswdigit))); + Wstring::const_iterator digitEnd = std::find_if(pos, stream.end(), [](char c) { return !zen::isDigit(c); }); int digitCount = digitEnd - pos; if (digitCount != 0) { Token out(Token::TK_NUMBER); - out.number = zen::toNumber<int>(Wstring(&*pos, digitCount)); + out.number = zen::stringTo<int>(Wstring(&*pos, digitCount)); pos += digitCount; return out; } @@ -391,8 +392,7 @@ private: template <class T> const T& manageObj(const T& obj) { - std::shared_ptr<Expression> newEntry(new T(obj)); - dump_.push_back(newEntry); + dump_.push_back(std::make_shared<T>(obj)); return static_cast<T&>(*dump_.back()); } diff --git a/lib/process_xml.cpp b/lib/process_xml.cpp index 4ee215b9..7fc7b066 100644 --- a/lib/process_xml.cpp +++ b/lib/process_xml.cpp @@ -79,15 +79,15 @@ wxString xmlAccess::getGlobalConfigFile() void xmlAccess::OptionalDialogs::resetDialogs() { - warningDependentFolders = true; - warningMultiFolderWriteAccess = true; - warningSignificantDifference = true; - warningNotEnoughDiskSpace = true; - warningUnresolvedConflicts = true; - warningSyncDatabase = true; - warningRecyclerMissing = true; - popupOnConfigChange = true; - showSummaryBeforeSync = true; + warningDependentFolders = true; + warningMultiFolderWriteAccess = true; + warningSignificantDifference = true; + warningNotEnoughDiskSpace = true; + warningUnresolvedConflicts = true; + warningSyncDatabase = true; + warningRecyclerMissing = true; + popupOnConfigChange = true; + showSummaryBeforeSync = true; } @@ -496,21 +496,21 @@ void writeText(const UnitTime& value, std::string& output) case UTIME_NONE: output = "Inactive"; break; - // case UTIME_LAST_X_HOURS: - // output = "x-hours"; - // break; case UTIME_TODAY: output = "Today"; break; - case UTIME_THIS_WEEK: - output = "Week"; - break; + //case UTIME_THIS_WEEK: + // output = "Week"; + // break; case UTIME_THIS_MONTH: output = "Month"; break; case UTIME_THIS_YEAR: output = "Year"; break; + case UTIME_LAST_X_DAYS: + output = "x-days"; + break; } } @@ -521,16 +521,16 @@ bool readText(const std::string& input, UnitTime& value) zen::trim(tmp); if (tmp == "Inactive") value = UTIME_NONE; - // else if (tmp == "x-hours") - // value = UTIME_LAST_X_HOURS; else if (tmp == "Today") value = UTIME_TODAY; - else if (tmp == "Week") - value = UTIME_THIS_WEEK; + //else if (tmp == "Week") + // value = UTIME_THIS_WEEK; else if (tmp == "Month") value = UTIME_THIS_MONTH; else if (tmp == "Year") value = UTIME_THIS_YEAR; + else if (tmp == "x-days") + value = UTIME_LAST_X_DAYS; else return false; return true; @@ -540,13 +540,13 @@ bool readText(const std::string& input, UnitTime& value) template <> inline void writeText(const ColumnTypeRim& value, std::string& output) { - output = toString<std::string>(value); + output = numberTo<std::string>(value); } template <> inline bool readText(const std::string& input, ColumnTypeRim& value) { - value = static_cast<ColumnTypeRim>(toNumber<int>(input)); + value = static_cast<ColumnTypeRim>(stringTo<int>(input)); return true; } @@ -554,13 +554,13 @@ bool readText(const std::string& input, ColumnTypeRim& value) template <> inline void writeText(const ColumnTypeNavi& value, std::string& output) { - output = toString<std::string>(value); + output = numberTo<std::string>(value); } template <> inline bool readText(const std::string& input, ColumnTypeNavi& value) { - value = static_cast<ColumnTypeNavi>(toNumber<int>(input)); + value = static_cast<ColumnTypeNavi>(stringTo<int>(input)); return true; } @@ -740,8 +740,7 @@ void readConfig(const XmlIn& in, FolderPairEnh& enhPair) //########################################################### //alternate comp configuration (optional) - XmlIn inAltCmp = in["CompareConfig"]; - if (inAltCmp) + if (XmlIn inAltCmp = in["CompareConfig"]) { CompConfig altCmpCfg; readConfig(inAltCmp, altCmpCfg); @@ -750,8 +749,7 @@ void readConfig(const XmlIn& in, FolderPairEnh& enhPair) } //########################################################### //alternate sync configuration (optional) - XmlIn inAltSync = in["SyncConfig"]; - if (inAltSync) + if (XmlIn inAltSync = in["SyncConfig"]) { SyncConfig altSyncCfg; readConfig(inAltSync, altSyncCfg); @@ -874,6 +872,7 @@ void readConfig(const XmlIn& in, XmlGlobalSettings& config) inWnd["ManualDeletionUseRecycler"](config.gui.useRecyclerForManualDeletion); inWnd["RespectCaseOnSearch" ](config.gui.textSearchRespectCase); + inWnd["ShowIcons"](config.gui.showIcons); inWnd["IconSize"](config.gui.iconSize); //########################################################### @@ -929,7 +928,7 @@ void readConfig(const Zstring& filename, XmlType type, ConfigType& config) ::readConfig(in, config); if (in.errorsOccured()) - throw FfsXmlError(_("Error parsing configuration file:") + L"\n\"" + filename + L"\"\n\n" + + throw FfsXmlError(_("Configuration loaded partially only:") + L"\n\"" + filename + L"\"\n\n" + getErrorMessageFormatted(in), FfsXmlError::WARNING); } } @@ -1139,6 +1138,7 @@ void writeConfig(const XmlGlobalSettings& config, XmlOut& out) outWnd["ManualDeletionUseRecycler"](config.gui.useRecyclerForManualDeletion); outWnd["RespectCaseOnSearch" ](config.gui.textSearchRespectCase); + outWnd["ShowIcons"](config.gui.showIcons); outWnd["IconSize"](config.gui.iconSize); //########################################################### diff --git a/lib/process_xml.h b/lib/process_xml.h index 5abbfdc9..801deede 100644 --- a/lib/process_xml.h +++ b/lib/process_xml.h @@ -161,6 +161,7 @@ struct XmlGlobalSettings #elif defined FFS_LINUX textSearchRespectCase(true), #endif + showIcons(true), iconSize(ICON_SIZE_SMALL), lastUpdateCheck(0) { @@ -209,6 +210,7 @@ struct XmlGlobalSettings bool useRecyclerForManualDeletion; bool textSearchRespectCase; + bool showIcons; FileIconSize iconSize; long lastUpdateCheck; //time of last update check diff --git a/lib/recycler.cpp b/lib/recycler.cpp index 11ed77f7..1c743a0c 100644 --- a/lib/recycler.cpp +++ b/lib/recycler.cpp @@ -22,8 +22,9 @@ #include "IFileOperation/file_op.h" #elif defined FFS_LINUX +#include <zen/scope_guard.h> #include <sys/stat.h> -#include <giomm/file.h> +#include <gio/gio.h> #endif using namespace zen; @@ -52,7 +53,7 @@ void moveToWindowsRecycler(const std::vector<Zstring>& filesToDelete) //throw F static bool useIFileOperation = false; static boost::once_flag once = BOOST_ONCE_INIT; //caveat: function scope static initialization is not thread-safe in VS 2010! - boost::call_once(once, []() { useIFileOperation = vistaOrLater(); }); + boost::call_once(once, [] { useIFileOperation = vistaOrLater(); }); if (useIFileOperation) //new recycle bin usage: available since Vista { @@ -65,8 +66,8 @@ void moveToWindowsRecycler(const std::vector<Zstring>& filesToDelete) //throw F const DllFun<GetLastErrorFct> getLastError (getDllName(), getLastErrorFctName); if (!moveToRecycler || !getLastError) - throw FileError(_("Error moving to Recycle Bin:") + L"\n\"" + fileNames[0] + L"\"" + //report first file only... better than nothing - L"\n\n" + _("Could not load a required DLL:") + L" \"" + getDllName() + L"\""); + throw FileError(replaceCpy(_("Unable to move %x to the Recycle Bin!"), L"%x", std::wstring(L"\"") + fileNames[0] + L"\"") + L"\n\n" + //report first file only... better than nothing + replaceCpy(_("Cannot load file %x."), L"%x", std::wstring(L"\"") + getDllName() + L"\"")); //#warning moving long file paths to recycler does not work! clarify! // std::vector<Zstring> temp; @@ -76,10 +77,11 @@ void moveToWindowsRecycler(const std::vector<Zstring>& filesToDelete) //throw F if (!moveToRecycler(&fileNames[0], //array must not be empty fileNames.size())) { - wchar_t errorMessage[2000]; - getLastError(errorMessage, 2000); - throw FileError(_("Error moving to Recycle Bin:") + L"\n\"" + fileNames[0] + L"\"" + //report first file only... better than nothing - L"\n\n" + L"(" + errorMessage + L")"); + std::vector<wchar_t> msgBuffer(2000); + getLastError(&msgBuffer[0], msgBuffer.size()); + + throw FileError(replaceCpy(_("Unable to move %x to the Recycle Bin!"), L"%x", std::wstring(L"\"") + fileNames[0] + L"\"") + L"\n\n" + //report first file only... better than nothing + &msgBuffer[0]); } } else //regular recycle bin usage: available since XP @@ -95,18 +97,18 @@ void moveToWindowsRecycler(const std::vector<Zstring>& filesToDelete) //throw F } SHFILEOPSTRUCT fileOp = {}; - fileOp.hwnd = NULL; + fileOp.hwnd = nullptr; fileOp.wFunc = FO_DELETE; fileOp.pFrom = filenameDoubleNull.c_str(); - fileOp.pTo = NULL; + fileOp.pTo = nullptr; fileOp.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION | FOF_SILENT | FOF_NOERRORUI; fileOp.fAnyOperationsAborted = false; - fileOp.hNameMappings = NULL; - fileOp.lpszProgressTitle = NULL; + fileOp.hNameMappings = nullptr; + fileOp.lpszProgressTitle = nullptr; if (::SHFileOperation(&fileOp) != 0 || fileOp.fAnyOperationsAborted) { - throw FileError(_("Error moving to Recycle Bin:") + L"\n\"" + filenameDoubleNull + L"\""); //report first file only... better than nothing + throw FileError(replaceCpy(_("Unable to move %x to the Recycle Bin!"), L"%x", std::wstring(L"\"") + filenameDoubleNull.c_str() + L"\"")); //report first file only... better than nothing } } } @@ -128,17 +130,20 @@ bool zen::moveToRecycleBin(const Zstring& filename) //throw FileError ::moveToWindowsRecycler(fileNames); //throw FileError #elif defined FFS_LINUX - Glib::RefPtr<Gio::File> fileObj = Gio::File::create_for_path(filename.c_str()); //never fails - try - { - if (!fileObj->trash()) - throw FileError(_("Error moving to Recycle Bin:") + L"\n\"" + filename + L"\"" + - L"\n\n" + L"(unknown error)"); - } - catch (const Glib::Error& errorObj) + GFile* file = g_file_new_for_path(filename.c_str()); //never fails according to docu + ZEN_ON_SCOPE_EXIT(g_object_unref(file);) + + GError* error = nullptr; + ZEN_ON_SCOPE_EXIT(if (error) g_error_free(error);); + + if (!g_file_trash(file, nullptr, &error)) { + if (!error) + throw FileError(replaceCpy(_("Unable to move %x to the Recycle Bin!"), L"%x", L"\"" + filename + L"\"") + L"\n\n" + + L"Unknown error."); + //implement same behavior as in Windows: if recycler is not existing, delete permanently - if (errorObj.code() == G_IO_ERROR_NOT_SUPPORTED) + if (error->code == G_IO_ERROR_NOT_SUPPORTED) { struct stat fileInfo = {}; if (::lstat(filename.c_str(), &fileInfo) != 0) @@ -152,11 +157,11 @@ bool zen::moveToRecycleBin(const Zstring& filename) //throw FileError } //assemble error message - const std::wstring errorMessage = L"Glib Error Code " + toString<std::wstring>(errorObj.code()) + /* L", " + - g_quark_to_string(errorObj.domain()) + */ L": " + utf8CvrtTo<std::wstring>(errorObj.what()); + const std::wstring errorMessage = L"Glib Error Code " + numberTo<std::wstring>(error->code) + /* L", " + + g_quark_to_string(error->domain) + */ L": " + utf8CvrtTo<std::wstring>(error->message); - throw FileError(_("Error moving to Recycle Bin:") + L"\n\"" + filename + L"\"" + - L"\n\n" + L"(" + errorMessage + L")"); + throw FileError(replaceCpy(_("Unable to move %x to the Recycle Bin!"), L"%x", L"\"" + filename + L"\"") + L"\n\n" + + errorMessage); } #endif return true; @@ -166,10 +171,11 @@ bool zen::moveToRecycleBin(const Zstring& filename) //throw FileError #ifdef FFS_WIN zen::StatusRecycler zen::recycleBinStatus(const Zstring& pathName) { - std::vector<wchar_t> buffer(MAX_PATH + 1); - if (::GetVolumePathName(applyLongPathPrefix(pathName).c_str(), //__in LPCTSTR lpszFileName, - &buffer[0], //__out LPTSTR lpszVolumePathName, - static_cast<DWORD>(buffer.size()))) //__in DWORD cchBufferLength + const DWORD bufferSize = MAX_PATH + 1; + std::vector<wchar_t> buffer(bufferSize); + if (::GetVolumePathName(pathName.c_str(), //__in LPCTSTR lpszFileName, + &buffer[0], //__out LPTSTR lpszVolumePathName, + bufferSize)) //__in DWORD cchBufferLength { Zstring rootPath = &buffer[0]; if (!endsWith(rootPath, FILE_NAME_SEPARATOR)) //a trailing backslash is required diff --git a/lib/recycler.h b/lib/recycler.h index 37b9250e..8eab5b21 100644 --- a/lib/recycler.h +++ b/lib/recycler.h @@ -19,12 +19,14 @@ namespace zen Windows ------- -Recycler always available: during runtime either SHFileOperation or (since Vista) IFileOperation will be dynamically selected +Recycler API always available: during runtime either SHFileOperation or IFileOperation (since Vista) will be dynamically selected Linux ----- -Compiler flag: `pkg-config --cflags gtkmm-2.4` -Linker flag: `pkg-config --libs gtkmm-2.4` +Compiler flags: `pkg-config --cflags gio-2.0` +Linker flags: `pkg-config --libs gio-2.0` + +Already included in package "gtk+-2.0"! */ //move a file or folder to Recycle Bin (deletes permanently if recycle is not available) diff --git a/lib/resolve_path.cpp b/lib/resolve_path.cpp index 178e70e2..1b241956 100644 --- a/lib/resolve_path.cpp +++ b/lib/resolve_path.cpp @@ -30,17 +30,17 @@ namespace Zstring resolveRelativePath(Zstring relativeName) //note: ::GetFullPathName() is documented not threadsafe! { const DWORD bufferSize = 10000; - std::vector<Zchar> fullPath(bufferSize); + std::vector<wchar_t> buffer(bufferSize); - const DWORD rv = ::GetFullPathName(applyLongPathPrefix(relativeName).c_str(), //__in LPCTSTR lpFileName, - bufferSize, //__in DWORD nBufferLength, - &fullPath[0], //__out LPTSTR lpBuffer, - NULL); //__out LPTSTR *lpFilePart - if (rv == 0 || rv >= bufferSize) //theoretically, rv can never be == bufferSize + const DWORD charsWritten = ::GetFullPathName(applyLongPathPrefix(relativeName).c_str(), //__in LPCTSTR lpFileName, + bufferSize, //__in DWORD nBufferLength, + &buffer[0], //__out LPTSTR lpBuffer, + nullptr); //__out LPTSTR *lpFilePart + if (charsWritten == 0 || charsWritten >= bufferSize) //theoretically, charsWritten can never be == bufferSize //ERROR! Don't do anything return relativeName; - return Zstring(&fullPath[0], rv); + return Zstring(&buffer[0], charsWritten); } #elif defined FFS_LINUX @@ -52,7 +52,7 @@ Zstring resolveRelativePath(const Zstring& relativeName) if (!startsWith(relativeName, FILE_NAME_SEPARATOR)) //absolute names are exactly those starting with a '/' { std::vector<char> buffer(10000); - if (::getcwd(&buffer[0], buffer.size()) != NULL) + if (::getcwd(&buffer[0], buffer.size()) != nullptr) { Zstring workingDir = &buffer[0]; if (!endsWith(workingDir, FILE_NAME_SEPARATOR)) @@ -64,10 +64,10 @@ Zstring resolveRelativePath(const Zstring& relativeName) return relativeName; /* - char* absPath = ::realpath(relativeName.c_str(), NULL); + char* absPath = ::realpath(relativeName.c_str(), nullptr); if (!absPath) return relativeName; //ERROR! Don't do anything - ZEN_ON_BLOCK_EXIT(::free(absPath)); + ZEN_ON_SCOPE_EXIT(::free(absPath)); return Zstring(absPath); */ @@ -93,9 +93,9 @@ private: auto addCsidl = [&](int csidl, const Zstring& paramName) { wchar_t buffer[MAX_PATH]; - if (SUCCEEDED(::SHGetFolderPath(NULL, //__in HWND hwndOwner, + if (SUCCEEDED(::SHGetFolderPath(nullptr, //__in HWND hwndOwner, csidl | CSIDL_FLAG_DONT_VERIFY, //__in int nFolder, - NULL, //__in HANDLE hToken, + nullptr, //__in HANDLE hToken, 0 /* == SHGFP_TYPE_CURRENT*/, //__in DWORD dwFlags, buffer))) //__out LPTSTR pszPath { @@ -294,41 +294,44 @@ Zstring volumenNameToPath(const Zstring& volumeName) //return empty string on er HANDLE hVol = ::FindFirstVolume(&volGuid[0], static_cast<DWORD>(volGuid.size())); if (hVol != INVALID_HANDLE_VALUE) { - ZEN_ON_BLOCK_EXIT(::FindVolumeClose(hVol)); + ZEN_ON_SCOPE_EXIT(::FindVolumeClose(hVol)); do { std::vector<wchar_t> volName(MAX_PATH + 1); - if (::GetVolumeInformation(&volGuid[0], //__in_opt LPCTSTR lpRootPathName, - &volName[0], //__out LPTSTR lpVolumeNameBuffer, + if (::GetVolumeInformation(&volGuid[0], //__in_opt LPCTSTR lpRootPathName, + &volName[0], //__out LPTSTR lpVolumeNameBuffer, static_cast<DWORD>(volName.size()), //__in DWORD nVolumeNameSize, - NULL, //__out_opt LPDWORD lpVolumeSerialNumber, - NULL, //__out_opt LPDWORD lpMaximumComponentLength, - NULL, //__out_opt LPDWORD lpFileSystemFlags, - NULL, //__out LPTSTR lpFileSystemNameBuffer, - 0)) //__in DWORD nFileSystemNameSize + nullptr, //__out_opt LPDWORD lpVolumeSerialNumber, + nullptr, //__out_opt LPDWORD lpMaximumComponentLength, + nullptr, //__out_opt LPDWORD lpFileSystemFlags, + nullptr, //__out LPTSTR lpFileSystemNameBuffer, + 0)) //__in DWORD nFileSystemNameSize { if (EqualFilename()(volumeName, Zstring(&volName[0]))) { //GetVolumePathNamesForVolumeName is not available for Windows 2000! typedef BOOL (WINAPI* GetVolumePathNamesForVolumeNameWFunc)(LPCWSTR lpszVolumeName, - LPWCH lpszVolumePathNames, - DWORD cchBufferLength, + LPWCH lpszVolumePathNames, + DWORD cchBufferLength, PDWORD lpcchReturnLength); const SysDllFun<GetVolumePathNamesForVolumeNameWFunc> getVolumePathNamesForVolumeName(L"kernel32.dll", "GetVolumePathNamesForVolumeNameW"); if (getVolumePathNamesForVolumeName) { - std::vector<wchar_t> volPath(10000); - + std::vector<wchar_t> buffer(10000); DWORD returnedLen = 0; - if (getVolumePathNamesForVolumeName(&volGuid[0], //__in LPCTSTR lpszVolumeName, - &volPath[0], //__out LPTSTR lpszVolumePathNames, - static_cast<DWORD>(volPath.size()), //__in DWORD cchBufferLength, - &returnedLen)) //__out PDWORD lpcchReturnLength + if (getVolumePathNamesForVolumeName(&volGuid[0], //__in LPCTSTR lpszVolumeName, + &buffer[0], //__out LPTSTR lpszVolumePathNames, + static_cast<DWORD>(buffer.size()), //__in DWORD cchBufferLength, + &returnedLen)) //__out PDWORD lpcchReturnLength { - return &volPath[0]; //return first path name in double-null terminated list! + //Attention: in contrast to documentation, this function may write a *single* 0 into + //buffer if volGuid does not have any associated volume paths (e.g. a hidden volume) + const Zstring volPath(&buffer[0]); + if (!volPath.empty()) + return volPath; //return first path name in double-null terminated list! } } return &volGuid[0]; //GUID looks ugly, but should be working correctly @@ -364,10 +367,10 @@ Zstring volumePathToName(const Zstring& volumePath) //return empty string on err if (::GetVolumeInformation(volumePath.c_str(), //__in_opt LPCTSTR lpRootPathName, &volName[0], //__out LPTSTR lpVolumeNameBuffer, bufferSize, //__in DWORD nVolumeNameSize, - NULL, //__out_opt LPDWORD lpVolumeSerialNumber, - NULL, //__out_opt LPDWORD lpMaximumComponentLength, - NULL, //__out_opt LPDWORD lpFileSystemFlags, - NULL, //__out LPTSTR lpFileSystemNameBuffer, + nullptr, //__out_opt LPDWORD lpVolumeSerialNumber, + nullptr, //__out_opt LPDWORD lpMaximumComponentLength, + nullptr, //__out_opt LPDWORD lpFileSystemFlags, + nullptr, //__out LPTSTR lpFileSystemNameBuffer, 0)) //__in DWORD nFileSystemNameSize { return &volName[0]; @@ -612,8 +615,8 @@ void zen::loginNetworkShare(const Zstring& dirnameOrig, bool allowUserInteractio //note: following function call may block heavily if network is not reachable!!! DWORD rv2 = ::WNetAddConnection2(&trgRes, // __in LPNETRESOURCE lpNetResource, - NULL, // __in LPCTSTR lpPassword, - NULL, // __in LPCTSTR lpUsername, + nullptr, // __in LPCTSTR lpPassword, + nullptr, // __in LPCTSTR lpUsername, allowUserInteraction ? CONNECT_INTERACTIVE : 0); //__in DWORD dwFlags if (rv2 == NO_ERROR) return; //mapping reestablished @@ -647,8 +650,8 @@ void zen::loginNetworkShare(const Zstring& dirnameOrig, bool allowUserInteractio //note: following function call may block heavily if network is not reachable!!! DWORD rv2 = ::WNetAddConnection2(&trgRes, // __in LPNETRESOURCE lpNetResource, - NULL, // __in LPCTSTR lpPassword, - NULL, // __in LPCTSTR lpUsername, + nullptr, // __in LPCTSTR lpPassword, + nullptr, // __in LPCTSTR lpUsername, allowUserInteraction ? CONNECT_INTERACTIVE : 0); //__in DWORD dwFlags if (rv2 == NO_ERROR) return; //mapping reestablished @@ -661,7 +664,7 @@ void zen::loginNetworkShare(const Zstring& dirnameOrig, bool allowUserInteractio DWORD bufferSize = sizeof(NETRESOURCE) + 20000; std::vector<char> buffer(bufferSize); - LPTSTR relPath = NULL; + LPTSTR relPath = nullptr; //note: following function call may block heavily if network is not reachable!!! const DWORD rv = WNetGetResourceInformation(&nr, // __in LPNETRESOURCE lpNetResource, @@ -680,8 +683,8 @@ void zen::loginNetworkShare(const Zstring& dirnameOrig, bool allowUserInteractio { //note: following function call may block heavily if network is not reachable!!! DWORD rv2 = ::WNetAddConnection2(&trgRes, // __in LPNETRESOURCE lpNetResource, - NULL, // __in LPCTSTR lpPassword, - NULL, // __in LPCTSTR lpUsername, + nullptr, // __in LPCTSTR lpPassword, + nullptr, // __in LPCTSTR lpUsername, allowUserInteraction ? CONNECT_INTERACTIVE : 0); //__in DWORD dwFlags if (rv2 == NO_ERROR) return; //mapping reestablished diff --git a/lib/resolve_path.h b/lib/resolve_path.h index 2fd5008e..bc74441a 100644 --- a/lib/resolve_path.h +++ b/lib/resolve_path.h @@ -12,6 +12,7 @@ namespace zen { +//resolve environment variables, relative paths, ect. and append file name separator Zstring getFormattedDirectoryName(const Zstring& dirString); //throw() - non-blocking! no I/O! #ifdef FFS_WIN diff --git a/lib/resources.cpp b/lib/resources.cpp index e47065e6..f5def2c6 100644 --- a/lib/resources.cpp +++ b/lib/resources.cpp @@ -46,18 +46,19 @@ void loadAnimFromZip(wxZipInputStream& zipInput, wxAnimation& anim) GlobalResources::GlobalResources() { - wxFFileInputStream input(toWx(zen::getResourceDir()) + wxT("Resources.zip")); + wxFFileInputStream input(toWx(zen::getResourceDir()) + L"Resources.zip"); if (input.IsOk()) //if not... we don't want to react too harsh here { //activate support for .png files wxImage::AddHandler(new wxPNGHandler); //ownership passed - wxZipInputStream resourceFile(input); + wxZipInputStream resourceFile(input, wxConvUTF8); + //do NOT rely on wxConvLocal! May result in "Cannot convert from the charset 'Unknown encoding (-1)'!" while (true) { std::unique_ptr<wxZipEntry> entry(resourceFile.GetNextEntry()); //take ownership! - if (entry.get() == NULL) + if (entry.get() == nullptr) break; const wxString name = entry->GetName(); diff --git a/lib/shadow.cpp b/lib/shadow.cpp index d00aa2f3..0a34ae5a 100644 --- a/lib/shadow.cpp +++ b/lib/shadow.cpp @@ -20,7 +20,6 @@ namespace { bool runningWOW64() //test if process is running under WOW64 (reference http://msdn.microsoft.com/en-us/library/ms684139(VS.85).aspx) { - //dynamically load windows API function typedef BOOL (WINAPI* IsWow64ProcessFun)(HANDLE hProcess, PBOOL Wow64Process); const SysDllFun<IsWow64ProcessFun> isWow64Process(L"kernel32.dll", "IsWow64Process"); @@ -47,41 +46,36 @@ public: getShadowVolume (getDllName(), getShadowVolumeFctName), backupHandle(nullptr) { - //check if shadow copy dll was loaded correctly - if (!createShadowCopy || !releaseShadowCopy || !getShadowVolume) - throw FileError(_("Error accessing Volume Shadow Copy Service!") + L"\n" + - _("Could not load a required DLL:") + L" \"" + getDllName() + L"\""); - //VSS does not support running under WOW64 except for Windows XP and Windows Server 2003 //(Reference: http://msdn.microsoft.com/en-us/library/aa384627(VS.85).aspx) if (runningWOW64()) throw FileError(_("Error accessing Volume Shadow Copy Service!") + L"\n" + _("Making shadow copies on WOW64 is not supported. Please use FreeFileSync 64-bit version.")); + + //check if shadow copy dll was loaded correctly + if (!createShadowCopy || !releaseShadowCopy || !getShadowVolume) + throw FileError(_("Error accessing Volume Shadow Copy Service!") + L"\n" + + replaceCpy(_("Cannot load file %x."), L"%x", std::wstring(L"\"") + getDllName() + L"\"")); + //--------------------------------------------------------------------------------------------------------- //start shadow volume copy service: - wchar_t errorMessage[1000]; + const unsigned int BUFFER_SIZE = 10000; + std::vector<wchar_t> msgBuffer(BUFFER_SIZE); backupHandle = createShadowCopy(volumeNameFormatted.c_str(), - errorMessage, - 1000); + &msgBuffer[0], BUFFER_SIZE); if (!backupHandle) throw FileError(_("Error accessing Volume Shadow Copy Service!") + L"\n" + - L"(" + errorMessage + L" Volume: \"" + volumeNameFormatted + L"\")"); + &msgBuffer[0] + L" Volume: \"" + volumeNameFormatted + L"\""); - wchar_t shadowVolName[1000]; - getShadowVolume(backupHandle, shadowVolName, 1000); - shadowVol = Zstring(shadowVolName) + FILE_NAME_SEPARATOR; //shadowVolName NEVER has a trailing backslash + std::vector<wchar_t> volBuffer(BUFFER_SIZE); + getShadowVolume(backupHandle, &volBuffer[0], BUFFER_SIZE); + shadowVol = Zstring(&volBuffer[0]) + FILE_NAME_SEPARATOR; //shadowVolName NEVER has a trailing backslash } - ~ShadowVolume() - { - releaseShadowCopy(backupHandle); //fast! no performance optimization necessary - } + ~ShadowVolume() { releaseShadowCopy(backupHandle); } //fast! no performance optimization necessary - Zstring getShadowVolumeName() const //trailing path separator - { - return shadowVol; - } + Zstring getShadowVolumeName() const { return shadowVol; } //with trailing path separator private: ShadowVolume(const ShadowVolume&); @@ -100,14 +94,13 @@ private: Zstring ShadowCopy::makeShadowCopy(const Zstring& inputFile) { - wchar_t volumeNameRaw[1000]; - + std::vector<wchar_t> volBuffer(1000); if (!::GetVolumePathName(inputFile.c_str(), //__in LPCTSTR lpszFileName, - volumeNameRaw, //__out LPTSTR lpszVolumePathName, - 1000)) //__in DWORD cchBufferLength - throw FileError(_("Could not determine volume name for file:") + L"\n\"" + inputFile + L"\""); + &volBuffer[0], //__out LPTSTR lpszVolumePathName, + static_cast<DWORD>(volBuffer.size()))) //__in DWORD cchBufferLength + throw FileError(_("Cannot determine volume name for file:") + L"\n\"" + inputFile + L"\""); - Zstring volumeNameFormatted = volumeNameRaw; + Zstring volumeNameFormatted = &volBuffer[0]; if (!endsWith(volumeNameFormatted, FILE_NAME_SEPARATOR)) volumeNameFormatted += FILE_NAME_SEPARATOR; @@ -115,7 +108,7 @@ Zstring ShadowCopy::makeShadowCopy(const Zstring& inputFile) const size_t pos = inputFile.find(volumeNameFormatted); //inputFile needs NOT to begin with volumeNameFormatted: consider for example \\?\ prefix! if (pos == Zstring::npos) { - std::wstring msg = _("Volume name %x not part of filename %y!"); + std::wstring msg = _("Volume name %x not part of file name %y!"); replace(msg, L"%x", std::wstring(L"\"") + volumeNameFormatted + L"\"", false); replace(msg, L"%y", std::wstring(L"\"") + inputFile + L"\"", false); throw FileError(msg); @@ -125,7 +118,7 @@ Zstring ShadowCopy::makeShadowCopy(const Zstring& inputFile) VolNameShadowMap::const_iterator iter = shadowVol.find(volumeNameFormatted); if (iter == shadowVol.end()) { - std::shared_ptr<ShadowVolume> newEntry(new ShadowVolume(volumeNameFormatted)); + auto newEntry = std::make_shared<ShadowVolume>(volumeNameFormatted); iter = shadowVol.insert(std::make_pair(volumeNameFormatted, newEntry)).first; } diff --git a/lib/statistics.cpp b/lib/statistics.cpp index 9914d455..7b3bceae 100644 --- a/lib/statistics.cpp +++ b/lib/statistics.cpp @@ -6,14 +6,13 @@ #include "statistics.h" -#include <wx/ffile.h> -#include <zen/basic_math.h> -#include "status_handler.h" -#include <wx+/format_unit.h> #include <limits> +#include <wx/ffile.h> #include <wx/stopwatch.h> +#include <zen/basic_math.h> #include <zen/assert_static.h> #include <zen/i18n.h> +#include <wx+/format_unit.h> using namespace zen; @@ -29,11 +28,11 @@ RetrieveStatistics::~RetrieveStatistics() std::for_each(data.begin(), data.end(), [&](const StatEntry& entry) { - outputFile.Write(toString<wxString>(entry.time)); + outputFile.Write(numberTo<wxString>(entry.time)); outputFile.Write(wxT(";")); - outputFile.Write(toString<wxString>(entry.objects)); + outputFile.Write(numberTo<wxString>(entry.objects)); outputFile.Write(wxT(";")); - outputFile.Write(toString<wxString>(entry.value)); + outputFile.Write(numberTo<wxString>(entry.value)); outputFile.Write(wxT("\n")); }); } diff --git a/lib/status_handler.h b/lib/status_handler.h index a37d2257..b246e49c 100644 --- a/lib/status_handler.h +++ b/lib/status_handler.h @@ -7,6 +7,7 @@ #ifndef STATUSHANDLER_H_INCLUDED #define STATUSHANDLER_H_INCLUDED +#include "../process_callback.h" #include <string> #include <zen/int64.h> @@ -19,64 +20,9 @@ void updateUiNow(); //do the updating Updating GUI is fast! time per single call to ProcessCallback::forceUiRefresh() - Comparison 25 µs - - Synchronization 0.6 ms (despite complex graph!) + - Synchronization 0.6 ms (despite complex graph control!) */ -//interfaces for status updates (can be implemented by GUI or Batch mode) - - -//report status during comparison and synchronization -struct ProcessCallback -{ - virtual ~ProcessCallback() {} - - //identifiers of different phases - enum Process - { - PROCESS_NONE = 10, - PROCESS_SCANNING, - PROCESS_COMPARING_CONTENT, - PROCESS_SYNCHRONIZING - }; - - //these methods have to be implemented in the derived classes to handle error and status information - virtual void initNewProcess(int objectsTotal, zen::Int64 dataTotal, Process processID) = 0; //informs about the total amount of data that will be processed from now on - - //note: this one must NOT throw in order to properly allow undoing setting of statistics! - //it is in general paired with a call to requestUiRefresh() to compensate! - virtual void updateProcessedData(int objectsDelta, zen::Int64 dataDelta) = 0; //throw()!! - virtual void updateTotalData (int objectsDelta, zen::Int64 dataDelta) = 0; // - /*the estimated total may change *during* sync: - 1. move file -> fallback to copy + delete - 2. file copy, actual size changed after comparison - 3. auto-resolution for failed create operations due to missing source - 4. directory deletion: may contain more items than scanned by FFS: excluded by filter - 5. delete directory to recycler or move to user-defined dir on same volume: no matter how many sub-elements exist, this is only 1 object to process! - 6. user-defined deletion directory on different volume: full file copy required (instead of move) */ - - //opportunity to abort must be implemented in a frequently executed method like requestUiRefresh() - virtual void requestUiRefresh() = 0; //throw ? - virtual void forceUiRefresh () = 0; //throw ? - call before starting long running task which doesn't update regularly - - //called periodically after data was processed: expected(!) to request GUI update - virtual void reportStatus(const std::wstring& text) = 0; //status info only, should not be logged! - - //called periodically after data was processed: expected(!) to request GUI update - virtual void reportInfo(const std::wstring& text) = 0; - - virtual void reportWarning(const std::wstring& warningMessage, bool& warningActive) = 0; - - //error handling: - enum Response - { - IGNORE_ERROR = 10, - RETRY - }; - virtual Response reportError (const std::wstring& errorMessage) = 0; //recoverable error situation - virtual void reportFatalError(const std::wstring& errorMessage) = 0; //non-recoverable error situation -}; - - //gui may want to abort process struct AbortCallback { @@ -108,6 +54,4 @@ private: bool abortRequested; }; - - #endif // STATUSHANDLER_H_INCLUDED diff --git a/lib/status_handler_impl.h b/lib/status_handler_impl.h new file mode 100644 index 00000000..7141d75c --- /dev/null +++ b/lib/status_handler_impl.h @@ -0,0 +1,34 @@ +// ************************************************************************** +// * 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 (zhnmju123 AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef STATUSHANDLER_IMPL_H_INCLUDED +#define STATUSHANDLER_IMPL_H_INCLUDED + +#include <zen/file_error.h> +#include "status_handler.h" + +template <typename Function> inline +bool tryReportingError(Function cmd, ProcessCallback& handler) //return "true" on success, "false" if error was ignored +{ + for (;;) + try + { + cmd(); //throw FileError + return true; + } + catch (zen::FileError& error) + { + switch (handler.reportError(error.toString())) //may throw! + { + case ProcessCallback::IGNORE_ERROR: + return false; + case ProcessCallback::RETRY: + break; //continue with loop + } + } +} + +#endif //STATUSHANDLER_IMPL_H_INCLUDED diff --git a/lib/xml_base.cpp b/lib/xml_base.cpp index e6b1e840..10bb698a 100644 --- a/lib/xml_base.cpp +++ b/lib/xml_base.cpp @@ -63,13 +63,17 @@ void xmlAccess::loadXmlDocument(const Zstring& filename, XmlDoc& doc) //throw Ff const std::wstring xmlAccess::getErrorMessageFormatted(const XmlIn& in) { - std::wstring errorMessage = _("Could not read values for the following XML nodes:") + L"\n"; + std::wstring msg; - std::vector<std::wstring> failedNodes = in.getErrorsAs<std::wstring>(); - std::for_each(failedNodes.begin(), failedNodes.end(), - [&](const std::wstring& str) { errorMessage += str + L'\n'; }); + const auto& failedElements = in.getErrorsAs<std::wstring>(); + if (!failedElements.empty()) + { + msg = _("Cannot read the following XML elements:") + L"\n"; + std::for_each(failedElements.begin(), failedElements.end(), + [&](const std::wstring& str) { msg += str + L'\n'; }); + } - return errorMessage; + return msg; } |