summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorB Stack <bgstack15@gmail.com>2021-03-03 01:18:05 +0000
committerB Stack <bgstack15@gmail.com>2021-03-03 01:18:05 +0000
commit320f1ae680d73da35a0cfe4846eb687d8616bcac (patch)
tree6fb17404841b30822a2d9204e3e0932e55f05ebb
parentMerge branch '11.6' into 'master' (diff)
parentadd upstream 11.7 (diff)
downloadFreeFileSync-11.7.tar.gz
FreeFileSync-11.7.tar.bz2
FreeFileSync-11.7.zip
Merge branch '11.7' into 'master'11.7
add upstream 11.7 See merge request opensource-tracking/FreeFileSync!31
-rw-r--r--Changelog.txt20
-rw-r--r--FreeFileSync/Build/Resources/Languages.zipbin507047 -> 507670 bytes
-rw-r--r--FreeFileSync/Source/RealTimeSync/app_icon.h2
-rw-r--r--FreeFileSync/Source/RealTimeSync/application.cpp14
-rw-r--r--FreeFileSync/Source/RealTimeSync/main_dlg.cpp2
-rw-r--r--FreeFileSync/Source/RealTimeSync/monitor.cpp8
-rw-r--r--FreeFileSync/Source/afs/abstract.cpp23
-rw-r--r--FreeFileSync/Source/afs/abstract.h42
-rw-r--r--FreeFileSync/Source/afs/abstract_impl.h12
-rw-r--r--FreeFileSync/Source/afs/ftp.cpp180
-rw-r--r--FreeFileSync/Source/afs/gdrive.cpp388
-rw-r--r--FreeFileSync/Source/afs/gdrive.h3
-rw-r--r--FreeFileSync/Source/afs/native.cpp193
-rw-r--r--FreeFileSync/Source/afs/native.h5
-rw-r--r--FreeFileSync/Source/afs/sftp.cpp68
-rw-r--r--FreeFileSync/Source/application.cpp41
-rw-r--r--FreeFileSync/Source/base/algorithm.cpp413
-rw-r--r--FreeFileSync/Source/base/binary.cpp4
-rw-r--r--FreeFileSync/Source/base/binary.h2
-rw-r--r--FreeFileSync/Source/base/comparison.cpp141
-rw-r--r--FreeFileSync/Source/base/db_file.cpp150
-rw-r--r--FreeFileSync/Source/base/db_file.h6
-rw-r--r--FreeFileSync/Source/base/dir_exist_async.h3
-rw-r--r--FreeFileSync/Source/base/dir_lock.cpp22
-rw-r--r--FreeFileSync/Source/base/dir_lock.h2
-rw-r--r--FreeFileSync/Source/base/file_hierarchy.cpp31
-rw-r--r--FreeFileSync/Source/base/file_hierarchy.h222
-rw-r--r--FreeFileSync/Source/base/icon_loader.cpp70
-rw-r--r--FreeFileSync/Source/base/icon_loader.h2
-rw-r--r--FreeFileSync/Source/base/lock_holder.h2
-rw-r--r--FreeFileSync/Source/base/parallel_scan.cpp39
-rw-r--r--FreeFileSync/Source/base/parallel_scan.h6
-rw-r--r--FreeFileSync/Source/base/process_callback.h9
-rw-r--r--FreeFileSync/Source/base/status_handler_impl.h54
-rw-r--r--FreeFileSync/Source/base/synchronization.cpp328
-rw-r--r--FreeFileSync/Source/base/synchronization.h8
-rw-r--r--FreeFileSync/Source/base/versioning.cpp20
-rw-r--r--FreeFileSync/Source/base/versioning.h8
-rw-r--r--FreeFileSync/Source/base_tools.cpp4
-rw-r--r--FreeFileSync/Source/config.cpp8
-rw-r--r--FreeFileSync/Source/config.h7
-rw-r--r--FreeFileSync/Source/fatal_error.h6
-rw-r--r--FreeFileSync/Source/ffs_paths.cpp52
-rw-r--r--FreeFileSync/Source/ffs_paths.h6
-rw-r--r--FreeFileSync/Source/icon_buffer.cpp2
-rw-r--r--FreeFileSync/Source/localization.cpp8
-rw-r--r--FreeFileSync/Source/log_file.cpp10
-rw-r--r--FreeFileSync/Source/log_file.h4
-rw-r--r--FreeFileSync/Source/parse_lng.h122
-rw-r--r--FreeFileSync/Source/parse_plural.h30
-rw-r--r--FreeFileSync/Source/perf_check.cpp4
-rw-r--r--FreeFileSync/Source/status_handler.cpp45
-rw-r--r--FreeFileSync/Source/status_handler.h24
-rw-r--r--FreeFileSync/Source/ui/abstract_folder_picker.cpp14
-rw-r--r--FreeFileSync/Source/ui/batch_config.cpp2
-rw-r--r--FreeFileSync/Source/ui/batch_status_handler.cpp17
-rw-r--r--FreeFileSync/Source/ui/batch_status_handler.h2
-rw-r--r--FreeFileSync/Source/ui/cfg_grid.cpp14
-rw-r--r--FreeFileSync/Source/ui/cfg_grid.h8
-rw-r--r--FreeFileSync/Source/ui/command_box.cpp4
-rw-r--r--FreeFileSync/Source/ui/file_grid.cpp58
-rw-r--r--FreeFileSync/Source/ui/file_grid_attr.h8
-rw-r--r--FreeFileSync/Source/ui/file_view.cpp96
-rw-r--r--FreeFileSync/Source/ui/folder_pair.h6
-rw-r--r--FreeFileSync/Source/ui/folder_selector.cpp8
-rw-r--r--FreeFileSync/Source/ui/gui_status_handler.cpp18
-rw-r--r--FreeFileSync/Source/ui/gui_status_handler.h4
-rw-r--r--FreeFileSync/Source/ui/log_panel.cpp8
-rw-r--r--FreeFileSync/Source/ui/main_dlg.cpp421
-rw-r--r--FreeFileSync/Source/ui/main_dlg.h9
-rw-r--r--FreeFileSync/Source/ui/progress_indicator.cpp70
-rw-r--r--FreeFileSync/Source/ui/search_grid.cpp2
-rw-r--r--FreeFileSync/Source/ui/small_dlgs.cpp14
-rw-r--r--FreeFileSync/Source/ui/status_handler_impl.h63
-rw-r--r--FreeFileSync/Source/ui/tree_grid.cpp54
-rw-r--r--FreeFileSync/Source/ui/tree_grid_attr.h6
-rw-r--r--FreeFileSync/Source/ui/version_check.cpp8
-rw-r--r--FreeFileSync/Source/version/version.h2
-rw-r--r--libcurl/rest.cpp2
-rw-r--r--wx+/choice_enum.h2
-rw-r--r--wx+/graph.cpp22
-rw-r--r--wx+/graph.h4
-rw-r--r--wx+/grid.cpp17
-rw-r--r--wx+/grid.h4
-rw-r--r--wx+/image_resources.cpp36
-rw-r--r--wx+/image_tools.cpp58
-rw-r--r--wx+/image_tools.h3
-rw-r--r--xBRZ/src/xbrz.cpp60
-rw-r--r--xBRZ/src/xbrz.h33
-rw-r--r--xBRZ/src/xbrz_tools.h33
-rw-r--r--zen/basic_math.h22
-rw-r--r--zen/dir_watcher.cpp15
-rw-r--r--zen/error_log.h2
-rw-r--r--zen/file_access.cpp98
-rw-r--r--zen/file_access.h34
-rw-r--r--zen/file_id_def.h46
-rw-r--r--zen/file_io.cpp29
-rw-r--r--zen/file_io.h23
-rw-r--r--zen/file_traverser.cpp8
-rw-r--r--zen/format_unit.cpp8
-rw-r--r--zen/http.cpp12
-rw-r--r--zen/http.h6
-rw-r--r--zen/json.h2
-rw-r--r--zen/open_ssl.cpp132
-rw-r--r--zen/perf.h23
-rw-r--r--zen/process_exec.cpp8
-rw-r--r--zen/resolve_path.cpp9
-rw-r--r--zen/ring_buffer.h8
-rw-r--r--zen/serialize.h14
-rw-r--r--zen/stl_tools.h12
-rw-r--r--zen/string_tools.h16
-rw-r--r--zen/string_traits.h11
-rw-r--r--zen/symlink_target.h4
-rw-r--r--zen/sys_info.cpp48
-rw-r--r--zen/sys_info.h1
-rw-r--r--zen/thread.cpp2
-rw-r--r--zen/thread.h37
-rw-r--r--zen/time.h2
-rw-r--r--zen/type_traits.h60
-rw-r--r--zenXml/zenxml/cvrt_text.h2
-rw-r--r--zenXml/zenxml/dom.h17
-rw-r--r--zenXml/zenxml/parser.h18
122 files changed, 2500 insertions, 2294 deletions
diff --git a/Changelog.txt b/Changelog.txt
index dd6e9d4e..e4199c8d 100644
--- a/Changelog.txt
+++ b/Changelog.txt
@@ -1,3 +1,23 @@
+FreeFileSync 11.7 [2021-03-01]
+------------------------------
+Detect moved files on FTP (if server supports MLSD)
+Allow installation only for current or all user(s) (Linux)
+Added application uninstaller: uninstall.sh (Linux)
+Use login user config path when running as root (macOS, Linux)
+Fixed detection of moved files with unstable device IDs (macOS, Linux)
+Strict checking for duplicate file IDs
+Avoid EINVAL invalid argument error when using F_PREALLOCATE (macOS)
+Restore input focus after closing log panel
+Double-click on file to open Google Drive web interface
+Fixed alpha channel image scaling glitch
+Fixed recycle bin folders being created recursively
+Fixed thread count status message fluctuation
+Don't quit FreeFileSync when parent terminal is closed (SIGHUP)
+Fixed "Operation not supported" error when setting directory locks
+Show folder picker despite SHCreateItemFromParsingName() error
+Work around "OLE received a packet with an invalid header" error
+
+
FreeFileSync 11.6 [2021-02-01]
------------------------------
New FreeFileSync installer (Linux)
diff --git a/FreeFileSync/Build/Resources/Languages.zip b/FreeFileSync/Build/Resources/Languages.zip
index ed898768..1329f10b 100644
--- a/FreeFileSync/Build/Resources/Languages.zip
+++ b/FreeFileSync/Build/Resources/Languages.zip
Binary files differ
diff --git a/FreeFileSync/Source/RealTimeSync/app_icon.h b/FreeFileSync/Source/RealTimeSync/app_icon.h
index 6352cc7a..55ae348b 100644
--- a/FreeFileSync/Source/RealTimeSync/app_icon.h
+++ b/FreeFileSync/Source/RealTimeSync/app_icon.h
@@ -16,7 +16,7 @@ inline
wxIcon getRtsIcon() //see FFS/app_icon.h
{
assert(loadImage("RealTimeSync").GetWidth () == loadImage("RealTimeSync").GetHeight() &&
- loadImage("RealTimeSync").GetWidth() == 128);
+ loadImage("RealTimeSync").GetWidth() == fastFromDIP(128));
wxIcon icon;
icon.CopyFromBitmap(loadImage("RealTimeSync", fastFromDIP(64)));
return icon;
diff --git a/FreeFileSync/Source/RealTimeSync/application.cpp b/FreeFileSync/Source/RealTimeSync/application.cpp
index 729b0e49..87df96ba 100644
--- a/FreeFileSync/Source/RealTimeSync/application.cpp
+++ b/FreeFileSync/Source/RealTimeSync/application.cpp
@@ -87,6 +87,18 @@ bool Application::OnInit()
#error unknown GTK version!
#endif
+ try
+ {
+ /* we're a GUI app: ignore SIGHUP when the parent terminal quits! (or process is killed!)
+ => the FFS launcher will still be killed => fine
+ => macOS: apparently not needed! interestingly the FFS launcher does receive SIGHUP and *is* killed */
+ if (sighandler_t oldHandler = ::signal(SIGHUP, SIG_IGN);
+ oldHandler == SIG_ERR)
+ THROW_LAST_SYS_ERROR("signal(SIGHUP)");
+ else assert(!oldHandler);
+ }
+ catch (const SysError& e) { std::cerr << utfTo<std::string>(e.toString()) << '\n'; }
+
//Windows User Experience Interaction Guidelines: tool tips should have 5s timeout, info tips no timeout => compromise:
wxToolTip::Enable(true); //yawn, a wxWidgets screw-up: wxToolTip::SetAutoPop is no-op if global tooltip window is not yet constructed: wxToolTip::Enable creates it
@@ -177,7 +189,7 @@ void Application::OnUnhandledException() //handles both wxApp::OnInit() + wxApp:
}
catch (const std::bad_alloc& e) //the only kind of exception we don't want crash dumps for
{
- fff::logFatalError(e.what()); //it's not always possible to display a message box, e.g. corrupted stack, however low-level file output works!
+ fff::logFatalError(utfTo<std::wstring>(e.what())); //it's not always possible to display a message box, e.g. corrupted stack, however low-level file output works!
const auto titleFmt = copyStringTo<std::wstring>(wxTheApp->GetAppDisplayName()) + SPACED_DASH + _("An exception occurred");
std::cerr << utfTo<std::string>(titleFmt + SPACED_DASH) << e.what() << '\n';
diff --git a/FreeFileSync/Source/RealTimeSync/main_dlg.cpp b/FreeFileSync/Source/RealTimeSync/main_dlg.cpp
index fc9e1acb..f308d0fa 100644
--- a/FreeFileSync/Source/RealTimeSync/main_dlg.cpp
+++ b/FreeFileSync/Source/RealTimeSync/main_dlg.cpp
@@ -376,7 +376,7 @@ void MainDialog::onAddFolder(wxCommandEvent& event)
//clear existing top folder first
firstFolderPanel_->setPath(Zstring());
- insertAddFolder({ topFolder }, 0);
+ insertAddFolder({topFolder}, 0);
}
diff --git a/FreeFileSync/Source/RealTimeSync/monitor.cpp b/FreeFileSync/Source/RealTimeSync/monitor.cpp
index 1866b764..e0dabf2c 100644
--- a/FreeFileSync/Source/RealTimeSync/monitor.cpp
+++ b/FreeFileSync/Source/RealTimeSync/monitor.cpp
@@ -26,7 +26,7 @@ std::set<Zstring, LessNativePath> waitForMissingDirs(const std::vector<Zstring>&
const std::function<void(const Zstring& folderPath)>& requestUiUpdate, std::chrono::milliseconds cbInterval)
{
//early failure! check for unsupported folder paths:
- for (const std::string& protoName : { "ftp", "sftp", "mtp", "gdrive" })
+ for (const std::string& protoName : {"ftp", "sftp", "mtp", "gdrive"})
for (const Zstring& phrase : folderPathPhrases)
//hopefully clear enough now: https://freefilesync.org/forum/viewtopic.php?t=4302
if (startsWithAsciiNoCase(trimCpy(phrase), protoName + ':'))
@@ -118,7 +118,7 @@ DirWatcher::Change waitForChanges(const std::set<Zstring, LessNativePath>& folde
catch (FileError&)
{
if (!dirAvailable(folderPath)) //folder not existing or can't access
- return { DirWatcher::ChangeType::baseFolderUnavailable, folderPath };
+ return {DirWatcher::ChangeType::baseFolderUnavailable, folderPath};
throw;
}
@@ -141,7 +141,7 @@ DirWatcher::Change waitForChanges(const std::set<Zstring, LessNativePath>& folde
//IMPORTANT CHECK: DirWatcher has problems detecting removal of top watched directories!
if (checkDirNow)
if (!dirAvailable(folderPath)) //catch errors related to directory removal, e.g. ERROR_NETNAME_DELETED
- return { DirWatcher::ChangeType::baseFolderUnavailable, folderPath };
+ return {DirWatcher::ChangeType::baseFolderUnavailable, folderPath};
try
{
std::vector<DirWatcher::Change> changes = watcher->fetchChanges([&] { requestUiUpdate(false /*readyForSync*/); /*throw X*/ },
@@ -167,7 +167,7 @@ DirWatcher::Change waitForChanges(const std::set<Zstring, LessNativePath>& folde
catch (FileError&)
{
if (!dirAvailable(folderPath)) //a benign(?) race condition with FileError
- return { DirWatcher::ChangeType::baseFolderUnavailable, folderPath };
+ return {DirWatcher::ChangeType::baseFolderUnavailable, folderPath};
throw;
}
}
diff --git a/FreeFileSync/Source/afs/abstract.cpp b/FreeFileSync/Source/afs/abstract.cpp
index 08d5bda0..614f1290 100644
--- a/FreeFileSync/Source/afs/abstract.cpp
+++ b/FreeFileSync/Source/afs/abstract.cpp
@@ -21,7 +21,7 @@ bool fff::isValidRelPath(const Zstring& relPath)
if constexpr (FILE_NAME_SEPARATOR != Zstr('/' )) if (contains(relPath, Zstr('/' ))) return false;
if constexpr (FILE_NAME_SEPARATOR != Zstr('\\')) if (contains(relPath, Zstr('\\'))) return false;
- const Zchar doubleSep[] = { FILE_NAME_SEPARATOR, FILE_NAME_SEPARATOR, 0 };
+ const Zchar doubleSep[] = {FILE_NAME_SEPARATOR, FILE_NAME_SEPARATOR, 0};
return !startsWith(relPath, FILE_NAME_SEPARATOR)&& !endsWith(relPath, FILE_NAME_SEPARATOR)&&
!contains(relPath, doubleSep);
}
@@ -80,7 +80,7 @@ struct FlatTraverserCallback : public AFS::TraverserCallback
private:
void onFile (const AFS::FileInfo& fi) override { if (onFile_) onFile_ (fi); }
std::shared_ptr<TraverserCallback> onFolder (const AFS::FolderInfo& fi) override { if (onFolder_) onFolder_ (fi); return nullptr; }
- HandleLink onSymlink(const AFS::SymlinkInfo& si) override { if (onSymlink_) onSymlink_(si); return TraverserCallback::LINK_SKIP; }
+ HandleLink onSymlink(const AFS::SymlinkInfo& si) override { if (onSymlink_) onSymlink_(si); return TraverserCallback::HandleLink::skip; }
HandleError reportDirError (const ErrorInfo& errorInfo) override { throw FileError(errorInfo.msg); }
HandleError reportItemError(const ErrorInfo& errorInfo, const Zstring& itemName) override { throw FileError(errorInfo.msg); }
@@ -98,13 +98,13 @@ void AFS::traverseFolderFlat(const AfsPath& afsPath, //throw FileError
const std::function<void (const SymlinkInfo& si)>& onSymlink) const
{
auto ft = std::make_shared<FlatTraverserCallback>(onFile, onFolder, onSymlink); //throw FileError
- traverseFolderRecursive({{ afsPath, ft }}, 1 /*parallelOps*/); //throw FileError
+ traverseFolderRecursive({{afsPath, ft}}, 1 /*parallelOps*/); //throw FileError
}
//already existing: undefined behavior! (e.g. fail/overwrite/auto-rename)
AFS::FileCopyResult AFS::copyFileAsStream(const AfsPath& afsSource, const StreamAttributes& attrSource, //throw FileError, ErrorFileLocked, X
- const AbstractPath& apTarget, const IOCallback& notifyUnbufferedIO /*throw X*/) const
+ const AbstractPath& apTarget, const IoCallback& notifyUnbufferedIO /*throw X*/) const
{
int64_t totalUnbufferedIO = 0;
IOCallbackDivider cbd(notifyUnbufferedIO, totalUnbufferedIO);
@@ -148,14 +148,13 @@ AFS::FileCopyResult AFS::copyFileAsStream(const AfsPath& afsSource, const Stream
replaceCpy(replaceCpy(_("Unexpected size of data stream.\nExpected: %x bytes\nActual: %y bytes"),
L"%x", numberTo<std::wstring>(totalBytesRead)),
L"%y", numberTo<std::wstring>(totalBytesWritten)) + L" [notifyUnbufferedWrite]");
-
FileCopyResult cpResult;
- cpResult.fileSize = attrSourceNew.fileSize;
- cpResult.modTime = attrSourceNew.modTime;
- cpResult.sourceFileId = attrSourceNew.fileId;
- cpResult.targetFileId = finResult.fileId;
- cpResult.errorModTime = finResult.errorModTime;
- /* Failing to set modification time is not a serious problem from synchronization perspective (treated like external update)
+ cpResult.fileSize = attrSourceNew.fileSize;
+ cpResult.modTime = attrSourceNew.modTime;
+ cpResult.sourceFilePrint = attrSourceNew.filePrint;
+ cpResult.targetFilePrint = finResult.filePrint;
+ cpResult.errorModTime = finResult.errorModTime;
+ /* Failing to set modification time is not a serious problem from synchronization perspective (treat like external update)
=> Support additional scenarios:
- GVFS failing to set modTime for FTP: https://freefilesync.org/forum/viewtopic.php?t=2372
- GVFS failing to set modTime for MTP: https://freefilesync.org/forum/viewtopic.php?t=2803
@@ -171,7 +170,7 @@ AFS::FileCopyResult AFS::copyFileTransactional(const AbstractPath& apSource, con
bool copyFilePermissions,
bool transactionalCopy,
const std::function<void()>& onDeleteTargetFile,
- const IOCallback& notifyUnbufferedIO /*throw X*/)
+ const IoCallback& notifyUnbufferedIO /*throw X*/)
{
auto copyFilePlain = [&](const AbstractPath& apTargetTmp)
diff --git a/FreeFileSync/Source/afs/abstract.h b/FreeFileSync/Source/afs/abstract.h
index c37c5f8a..691fd464 100644
--- a/FreeFileSync/Source/afs/abstract.h
+++ b/FreeFileSync/Source/afs/abstract.h
@@ -69,8 +69,6 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t
static Zstring getInitPathPhrase(const AbstractPath& ap) { return ap.afsDevice.ref().getInitPathPhrase(ap.afsPath); }
- static std::optional<Zstring> getNativeItemPath(const AbstractPath& ap) { return ap.afsDevice.ref().getNativeItemPath(ap.afsPath); }
-
//----------------------------------------------------------------------------------------------------------------
static void authenticateAccess(const AfsDevice& afsDevice, bool allowUserInteraction) //throw FileError
{ return afsDevice.ref().authenticateAccess(allowUserInteraction); }
@@ -82,7 +80,7 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t
static bool hasNativeTransactionalCopy(const AbstractPath& ap) { return ap.afsDevice.ref().hasNativeTransactionalCopy(); }
//----------------------------------------------------------------------------------------------------------------
- using FileId = std::string; //AfsDevice-dependent unique ID
+ using FingerPrint = uint64_t; //AfsDevice-dependent persistent unique ID
enum class ItemType : unsigned char
{
@@ -135,7 +133,7 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t
{
time_t modTime; //number of seconds since Jan. 1st 1970 UTC
uint64_t fileSize;
- FileId fileId; //optional!
+ FingerPrint filePrint; //optional
};
//----------------------------------------------------------------------------------------------------------------
@@ -149,13 +147,13 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t
virtual std::optional<StreamAttributes> getAttributesBuffered() = 0; //throw FileError
};
//return value always bound:
- static std::unique_ptr<InputStream> getInputStream(const AbstractPath& ap, const zen::IOCallback& notifyUnbufferedIO /*throw X*/) //throw FileError, ErrorFileLocked
+ static std::unique_ptr<InputStream> getInputStream(const AbstractPath& ap, const zen::IoCallback& notifyUnbufferedIO /*throw X*/) //throw FileError, ErrorFileLocked
{ return ap.afsDevice.ref().getInputStream(ap.afsPath, notifyUnbufferedIO); }
struct FinalizeResult
{
- FileId fileId;
+ FingerPrint filePrint; //optional
std::optional<zen::FileError> errorModTime;
};
@@ -184,14 +182,14 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t
static std::unique_ptr<OutputStream> getOutputStream(const AbstractPath& ap, //throw FileError
std::optional<uint64_t> streamSize,
std::optional<time_t> modTime,
- const zen::IOCallback& notifyUnbufferedIO /*throw X*/)
+ const zen::IoCallback& notifyUnbufferedIO /*throw X*/)
{ return std::make_unique<OutputStream>(ap.afsDevice.ref().getOutputStream(ap.afsPath, streamSize, modTime, notifyUnbufferedIO), ap, streamSize); }
//----------------------------------------------------------------------------------------------------------------
struct SymlinkInfo
{
Zstring itemName;
- time_t modTime; //number of seconds since Jan. 1st 1970 UTC
+ time_t modTime;
};
struct FileInfo
@@ -199,7 +197,7 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t
Zstring itemName;
uint64_t fileSize; //unit: bytes!
time_t modTime; //number of seconds since Jan. 1st 1970 UTC
- FileId fileId; //optional: empty if not supported!
+ FingerPrint filePrint; //optional; persistent + unique (relative to device) or 0!
bool isFollowedSymlink;
};
@@ -213,16 +211,16 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t
{
virtual ~TraverserCallback() {}
- enum HandleLink
+ enum class HandleLink
{
- LINK_FOLLOW, //dereferences link, then calls "onFolder()" or "onFile()"
- LINK_SKIP
+ follow, //follows link, then calls "onFolder()" or "onFile()"
+ skip
};
- enum HandleError
+ enum class HandleError
{
- ON_ERROR_RETRY,
- ON_ERROR_CONTINUE
+ retry,
+ ignore
};
virtual void onFile (const FileInfo& fi) = 0; //
@@ -264,8 +262,8 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t
{
uint64_t fileSize = 0;
time_t modTime = 0; //number of seconds since Jan. 1st 1970 UTC
- FileId sourceFileId;
- FileId targetFileId;
+ FingerPrint sourceFilePrint; //optional
+ FingerPrint targetFilePrint; //
std::optional<zen::FileError> errorModTime; //failure to set modification time
};
@@ -280,7 +278,7 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t
//if transactionalCopy == true, full read access on source had been proven at this point, so it's safe to delete it.
const std::function<void()>& onDeleteTargetFile /*throw X*/,
//accummulated delta != file size! consider ADS, sparse, compressed files
- const zen::IOCallback& notifyUnbufferedIO /*throw X*/);
+ const zen::IoCallback& notifyUnbufferedIO /*throw X*/);
//already existing: fail
//symlink handling: follow
@@ -331,7 +329,7 @@ protected:
//already existing: undefined behavior! (e.g. fail/overwrite/auto-rename)
FileCopyResult copyFileAsStream(const AfsPath& afsSource, const StreamAttributes& attrSource, //throw FileError, ErrorFileLocked, X
- const AbstractPath& apTarget, const zen::IOCallback& notifyUnbufferedIO /*throw X*/) const;
+ const AbstractPath& apTarget, const zen::IoCallback& notifyUnbufferedIO /*throw X*/) const;
private:
virtual std::optional<Zstring> getNativeItemPath(const AfsPath& afsPath) const { return {}; };
@@ -363,13 +361,13 @@ private:
virtual bool equalSymlinkContentForSameAfsType(const AfsPath& afsLhs, const AbstractPath& apRhs) const = 0; //throw FileError
//----------------------------------------------------------------------------------------------------------------
- virtual std::unique_ptr<InputStream> getInputStream(const AfsPath& afsPath, const zen::IOCallback& notifyUnbufferedIO /*throw X*/) const = 0; //throw FileError, ErrorFileLocked
+ virtual std::unique_ptr<InputStream> getInputStream(const AfsPath& afsPath, const zen::IoCallback& notifyUnbufferedIO /*throw X*/) const = 0; //throw FileError, ErrorFileLocked
//already existing: undefined behavior! (e.g. fail/overwrite/auto-rename)
virtual std::unique_ptr<OutputStreamImpl> getOutputStream(const AfsPath& afsPath, //throw FileError
std::optional<uint64_t> streamSize,
std::optional<time_t> modTime,
- const zen::IOCallback& notifyUnbufferedIO /*throw X*/) const = 0;
+ const zen::IoCallback& notifyUnbufferedIO /*throw X*/) const = 0;
//----------------------------------------------------------------------------------------------------------------
virtual void traverseFolderRecursive(const TraverserWorkload& workload /*throw X*/, size_t parallelOps) const = 0;
//----------------------------------------------------------------------------------------------------------------
@@ -383,7 +381,7 @@ private:
virtual FileCopyResult copyFileForSameAfsType(const AfsPath& afsSource, const StreamAttributes& attrSource, //throw FileError, ErrorFileLocked, X
const AbstractPath& apTarget, bool copyFilePermissions,
//accummulated delta != file size! consider ADS, sparse, compressed files
- const zen::IOCallback& notifyUnbufferedIO /*throw X*/) const = 0;
+ const zen::IoCallback& notifyUnbufferedIO /*throw X*/) const = 0;
//symlink handling: follow
diff --git a/FreeFileSync/Source/afs/abstract_impl.h b/FreeFileSync/Source/afs/abstract_impl.h
index e7ab071e..4ecc2614 100644
--- a/FreeFileSync/Source/afs/abstract_impl.h
+++ b/FreeFileSync/Source/afs/abstract_impl.h
@@ -28,9 +28,9 @@ std::wstring tryReportingDirError(Function cmd /*throw FileError*/, AbstractFile
assert(!e.toString().empty());
switch (cb.reportDirError({e.toString(), std::chrono::steady_clock::now(), retryNumber})) //throw X
{
- case AbstractFileSystem::TraverserCallback::ON_ERROR_CONTINUE:
+ case AbstractFileSystem::TraverserCallback::HandleError::ignore:
return e.toString();
- case AbstractFileSystem::TraverserCallback::ON_ERROR_RETRY:
+ case AbstractFileSystem::TraverserCallback::HandleError::retry:
break; //continue with loop
}
}
@@ -49,9 +49,9 @@ bool tryReportingItemError(Command cmd, AbstractFileSystem::TraverserCallback& c
{
switch (callback.reportItemError({e.toString(), std::chrono::steady_clock::now(), retryNumber}, itemName)) //throw X
{
- case AbstractFileSystem::TraverserCallback::ON_ERROR_RETRY:
+ case AbstractFileSystem::TraverserCallback::HandleError::retry:
break;
- case AbstractFileSystem::TraverserCallback::ON_ERROR_CONTINUE:
+ case AbstractFileSystem::TraverserCallback::HandleError::ignore:
return false;
}
}
@@ -197,8 +197,8 @@ private:
std::condition_variable conditionBytesWritten_;
std::condition_variable conditionBytesRead_;
- std::atomic<uint64_t> totalBytesWritten_{ 0 }; //std:atomic is uninitialized by default!
- std::atomic<uint64_t> totalBytesRead_ { 0 }; //
+ std::atomic<uint64_t> totalBytesWritten_{0}; //std:atomic is uninitialized by default!
+ std::atomic<uint64_t> totalBytesRead_ {0}; //
};
//==========================================================================================
diff --git a/FreeFileSync/Source/afs/ftp.cpp b/FreeFileSync/Source/afs/ftp.cpp
index 88afa144..0685066f 100644
--- a/FreeFileSync/Source/afs/ftp.cpp
+++ b/FreeFileSync/Source/afs/ftp.cpp
@@ -93,7 +93,7 @@ Zstring ansiToUtfEncoding(const std::string& str) //throw SysError
throw SysError(formatGlibError("g_convert(" + utfTo<std::string>(str) + ')', error));
ZEN_ON_SCOPE_EXIT(::g_free(utfStr));
- return { utfStr, bytesWritten };
+ return {utfStr, bytesWritten};
}
@@ -117,7 +117,7 @@ std::string utfToAnsiEncoding(const Zstring& str) //throw SysError
throw SysError(formatGlibError("g_convert(" + utfTo<std::string>(str) + ')', error));
ZEN_ON_SCOPE_EXIT(::g_free(ansiStr));
- return { ansiStr, bytesWritten };
+ return {ansiStr, bytesWritten};
}
@@ -494,8 +494,8 @@ public:
return perform(AfsPath(), true /*isDir*/, CURLFTPMETHOD_NOCWD /*avoid needless CWDs*/,
{
- { CURLOPT_NOBODY, 1L },
- { CURLOPT_QUOTE, quote },
+ {CURLOPT_NOBODY, 1L},
+ {CURLOPT_QUOTE, quote},
}, requiresUtf8, timeoutSec); //throw SysError
}
@@ -510,7 +510,7 @@ public:
=> are there servers supporting neither FEAT nor HELP? only time will tell...
... and it tells! FUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU https://freefilesync.org/forum/viewtopic.php?t=8041 */
- //=> * to the rescue: as long as we get an FTP response, *any* FTP response, including 550, the connection itself is fine!
+ //=> * to the rescue: as long as we get an FTP response - *any* FTP response (including 550) - the connection itself is fine!
const std::string& featBuf = runSingleFtpCommand("*FEAT", false /*requiresUtf8*/, timeoutSec); //throw SysError
for (const std::string& line : splitFtpResponse(featBuf))
@@ -563,7 +563,7 @@ public:
++it; //skip double quote
else
{
- const std::string homePathRaw = replaceCpy<std::string>({ itBegin, it }, "\"\"", '"');
+ const std::string homePathRaw = replaceCpy(std::string{itBegin, it}, "\"\"", '"');
const ServerEncoding enc = getServerEncoding(timeoutSec); //throw SysError
const Zstring homePathUtf = serverToUtfEncoding(homePathRaw, enc); //throw SysError
return sanitizeDeviceRelativePath(homePathUtf);
@@ -917,6 +917,7 @@ struct FtpItem
Zstring itemName;
uint64_t fileSize = 0;
time_t modTime = 0;
+ AFS::FingerPrint filePrint = 0; //optional
};
@@ -1023,8 +1024,8 @@ public:
{
std::vector<CurlOption> options =
{
- { CURLOPT_WRITEDATA, &rawListing },
- { CURLOPT_WRITEFUNCTION, onBytesReceived },
+ {CURLOPT_WRITEDATA, &rawListing},
+ {CURLOPT_WRITEFUNCTION, onBytesReceived},
};
curl_ftpmethod pathMethod = CURLFTPMETHOD_SINGLECWD;
@@ -1142,16 +1143,22 @@ private:
}
else if (startsWithAsciiNoCase(fact, "unique="))
{
- warn_static("reevaluate: https://tools.ietf.org/html/rfc3659#section-7.5.2")
- //note: as far as the RFC goes, the "unique" fact is not required to act like a persistent file id!
- auto fileId
- /*item.fileId */= afterFirst(fact, '=', IfNotFoundReturn::none);
+ /* https://tools.ietf.org/html/rfc3659#section-7.5.2
+ "The mapping between files, and unique fact tokens should be maintained, [...] for
+ *at least* the lifetime of the control connection from user-PI to server-PI."
+
+ => not necessarily *persistent* as far as the RFC goes!
+ BUT: practially this will be the inode ID/file index, so we can assume persistence */
+ const std::string uniqueId = afterFirst(fact, '=', IfNotFoundReturn::none);
+ assert(!uniqueId.empty());
+ item.filePrint = hashArray<AFS::FingerPrint>(uniqueId.begin(), uniqueId.end());
+ //other metadata to hash e.g. create fact? => not available on Linux-hosted FTP!
}
if (equalAsciiNoCase(typeFact, "cdir"))
- return { AFS::ItemType::folder, Zstr("."), 0, 0 };
+ return {AFS::ItemType::folder, Zstr("."), 0, 0};
if (equalAsciiNoCase(typeFact, "pdir"))
- return { AFS::ItemType::folder, Zstr(".."), 0, 0 };
+ return {AFS::ItemType::folder, Zstr(".."), 0, 0};
if (equalAsciiNoCase(typeFact, "dir"))
item.type = AFS::ItemType::folder;
@@ -1238,33 +1245,33 @@ private:
static FtpItem parseUnixLine(const std::string& rawLine, time_t utcTimeNow, int utcCurrentYear, bool haveGroup, ServerEncoding enc) //throw SysError
{
- try
- {
- FtpLineParser parser(rawLine);
- /* total 4953 <- optional first line
- drwxr-xr-x 1 root root 4096 Jan 10 11:58 version
- -rwxr-xr-x 1 root root 1084 Sep 2 01:17 Unit Test.vcxproj.user
- -rwxr-xr-x 1 1000 300 2217 Feb 28 2016 win32.manifest
- lrwxr-xr-x 1 root root 18 Apr 26 15:17 Projects -> /mnt/hgfs/Projects
+ /* total 4953 <- optional first line
+ drwxr-xr-x 1 root root 4096 Jan 10 11:58 version
+ -rwxr-xr-x 1 root root 1084 Sep 2 01:17 Unit Test.vcxproj.user
+ -rwxr-xr-x 1 1000 300 2217 Feb 28 2016 win32.manifest
+ lrwxr-xr-x 1 root root 18 Apr 26 15:17 Projects -> /mnt/hgfs/Projects
- file type: -:file l:symlink d:directory b:block device p:named pipe c:char device s:socket
+ file type: -:file l:symlink d:directory b:block device p:named pipe c:char device s:socket
- permissions: (r|-)(w|-)(x|s|S|-) user
- (r|-)(w|-)(x|s|S|-) group s := S + x S = Setgid
- (r|-)(w|-)(x|t|T|-) others t := T + x T = sticky bit
+ permissions: (r|-)(w|-)(x|s|S|-) user
+ (r|-)(w|-)(x|s|S|-) group s := S + x S = Setgid
+ (r|-)(w|-)(x|t|T|-) others t := T + x T = sticky bit
- Alternative formats:
- Unix, no group ("ls -alG") https://freefilesync.org/forum/viewtopic.php?t=4306
- dr-xr-xr-x 2 root 512 Apr 8 1994 etc
+ Alternative formats:
+ Unix, no group ("ls -alG") https://freefilesync.org/forum/viewtopic.php?t=4306
+ dr-xr-xr-x 2 root 512 Apr 8 1994 etc
- Yet to be seen in the wild:
- Netware:
- d [R----F--] supervisor 512 Jan 16 18:53 login
- - [R----F--] rhesus 214059 Oct 20 15:27 cx.exe
+ Yet to be seen in the wild:
+ Netware:
+ d [R----F--] supervisor 512 Jan 16 18:53 login
+ - [R----F--] rhesus 214059 Oct 20 15:27 cx.exe
- NetPresenz for the Mac:
- -------r-- 326 1391972 1392298 Nov 22 1995 MegaPhone.sit
- drwxrwxr-x folder 2 May 10 1996 network */
+ NetPresenz for the Mac:
+ -------r-- 326 1391972 1392298 Nov 22 1995 MegaPhone.sit
+ drwxrwxr-x folder 2 May 10 1996 network */
+ try
+ {
+ FtpLineParser parser(rawLine);
const std::string typeTag = parser.readRange(1, [](char c) //throw SysError
{
@@ -1300,7 +1307,7 @@ private:
const std::string monthStr = parser.readRange(std::not_fn(isWhiteSpace<char>)); //throw SysError
parser.readRange(&isWhiteSpace<char>); //throw SysError
- const char* months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+ const char* months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
auto itMonth = std::find_if(std::begin(months), std::end(months), [&](const char* name) { return equalAsciiNoCase(name, monthStr); });
if (itMonth == std::end(months))
throw SysError(L"Failed to parse month name.");
@@ -1344,8 +1351,8 @@ private:
else
throw SysError(L"Failed to parse file time.");
- warn_static("reevaluate: can't we detect the server offset via MDTM!?")
//let's pretend the time listing is UTC (same behavior as FileZilla): hopefully MLSD will make this mess obsolete soon...
+ // => find exact offset with some MDTM hackery? yes, could do that, but this doesn't solve the bigger problem of imprecise LIST file times, so why bother?
time_t utcTime = utcToTimeT(timeComp); //returns -1 on error
if (utcTime == -1)
{
@@ -1366,7 +1373,7 @@ private:
throw SysError(L"Item name not available.");
if (itemName == "." || itemName == "..") //sometimes returned, e.g. by freefilesync.org
- return { AFS::ItemType::folder, utfTo<Zstring>(itemName), 0, 0 };
+ return {AFS::ItemType::folder, utfTo<Zstring>(itemName), 0, 0};
//------------------------------------------------------------------------------------
FtpItem item;
if (typeTag == "d")
@@ -1391,29 +1398,28 @@ private:
//"dir"
static std::vector<FtpItem> parseWindows(const std::string& buf, ServerEncoding enc) //throw SysError
{
- /*
- Test server: test.rebex.net username:demo pw:password useTls = true
+ /* Test server: test.rebex.net username:demo pw:password useTls = true
+
+ listing supported by libcurl (US server)
+ 10-27-15 03:46AM <DIR> pub
+ 04-08-14 03:09PM 11,399 readme.txt
- listing supported by libcurl (US server)
- 10-27-15 03:46AM <DIR> pub
- 04-08-14 03:09PM 11,399 readme.txt
+ Datalogic Windows CE 5.0
+ 01-01-98 13:00 <DIR> Storage Card
- Datalogic Windows CE 5.0
- 01-01-98 13:00 <DIR> Storage Card
+ IIS option "four-digit years"
+ 06-22-2017 04:25PM <DIR> test
+ 06-20-2017 12:50PM 1875499 zstring.obj
- IIS option "four-digit years"
- 06-22-2017 04:25PM <DIR> test
- 06-20-2017 12:50PM 1875499 zstring.obj
+ Alternative formats (yet to be seen in the wild)
+ "dir" on Windows, US:
+ 10/27/2015 03:46 AM <DIR> pub
+ 04/08/2014 03:09 PM 11,399 readme.txt
- Alternative formats (yet to be seen in the wild)
- "dir" on Windows, US:
- 10/27/2015 03:46 AM <DIR> pub
- 04/08/2014 03:09 PM 11,399 readme.txt
+ "dir" on Windows, German:
+ 21.09.2016 18:31 <DIR> Favorites
+ 12.01.2017 19:57 11.399 gsview64.ini */
- "dir" on Windows, German:
- 21.09.2016 18:31 <DIR> Favorites
- 12.01.2017 19:57 11.399 gsview64.ini
- */
const TimeComp tc = getUtcTime();
if (tc == TimeComp())
throw SysError(L"Failed to determine current time: " + numberTo<std::wstring>(std::time(nullptr)));
@@ -1473,8 +1479,8 @@ private:
timeComp.day = day;
timeComp.hour = hour;
timeComp.minute = minute;
- warn_static("reevaluate: can't we detect the server offset via MDTM!?")
//let's pretend the time listing is UTC (same behavior as FileZilla): hopefully MLSD will make this mess obsolete soon...
+ // => find exact offset with some MDTM hackery? yes, could do that, but this doesn't solve the bigger problem of imprecise LIST file times, so why bother?
time_t utcTime = utcToTimeT(timeComp); //returns -1 on error
if (utcTime == -1)
{
@@ -1561,18 +1567,18 @@ private:
switch (item.type)
{
case AFS::ItemType::file:
- cb.onFile({ item.itemName, item.fileSize, item.modTime, AFS::FileId(), false /*isFollowedSymlink*/ }); //throw X
+ cb.onFile({item.itemName, item.fileSize, item.modTime, item.filePrint, false /*isFollowedSymlink*/}); //throw X
break;
case AFS::ItemType::folder:
- if (std::shared_ptr<AFS::TraverserCallback> cbSub = cb.onFolder({ item.itemName, false /*isFollowedSymlink*/ })) //throw X
- workload_.push_back({ itemPath, std::move(cbSub) });
+ if (std::shared_ptr<AFS::TraverserCallback> cbSub = cb.onFolder({item.itemName, false /*isFollowedSymlink*/})) //throw X
+ workload_.push_back({itemPath, std::move(cbSub)});
break;
case AFS::ItemType::symlink:
- switch (cb.onSymlink({ item.itemName, item.modTime })) //throw X
+ switch (cb.onSymlink({item.itemName, item.modTime})) //throw X
{
- case AFS::TraverserCallback::LINK_FOLLOW:
+ case AFS::TraverserCallback::HandleLink::follow:
{
FtpItem target = {};
if (!tryReportingItemError([&] //throw X
@@ -1583,15 +1589,15 @@ private:
if (target.type == AFS::ItemType::folder)
{
- if (std::shared_ptr<AFS::TraverserCallback> cbSub = cb.onFolder({ item.itemName, true /*isFollowedSymlink*/ })) //throw X
- workload_.push_back({ itemPath, std::move(cbSub) });
+ if (std::shared_ptr<AFS::TraverserCallback> cbSub = cb.onFolder({item.itemName, true /*isFollowedSymlink*/})) //throw X
+ workload_.push_back({itemPath, std::move(cbSub)});
}
else //a file or named pipe, etc.
- cb.onFile({ item.itemName, target.fileSize, target.modTime, AFS::FileId(), true /*isFollowedSymlink*/ }); //throw X
+ cb.onFile({item.itemName, target.fileSize, target.modTime, item.filePrint, true /*isFollowedSymlink*/}); //throw X
}
break;
- case AFS::TraverserCallback::LINK_SKIP:
+ case AFS::TraverserCallback::HandleLink::skip:
break;
}
break;
@@ -1643,9 +1649,9 @@ void ftpFileDownload(const FtpLogin& login, const AfsPath& afsFilePath, //throw
{
session.perform(afsFilePath, false /*isDir*/, CURLFTPMETHOD_NOCWD, //are there any servers that require CURLFTPMETHOD_SINGLECWD? let's find out
{
- { CURLOPT_WRITEDATA, &onBytesReceived },
- { CURLOPT_WRITEFUNCTION, onBytesReceivedWrapper },
- { CURLOPT_IGNORE_CONTENT_LENGTH, 1L }, //skip FTP "SIZE" command before download (=> download until actual EOF if file size changes)
+ {CURLOPT_WRITEDATA, &onBytesReceived},
+ {CURLOPT_WRITEFUNCTION, onBytesReceivedWrapper},
+ {CURLOPT_IGNORE_CONTENT_LENGTH, 1L}, //skip FTP "SIZE" command before download (=> download until actual EOF if file size changes)
}, true /*requiresUtf8*/, login.timeoutSec); //throw SysError
});
}
@@ -1706,15 +1712,15 @@ void ftpFileUpload(const FtpLogin& login, const AfsPath& afsFilePath, //throw Fi
*/
session.perform(afsFilePath, false /*isDir*/, CURLFTPMETHOD_NOCWD, //are there any servers that require CURLFTPMETHOD_SINGLECWD? let's find out
{
- { CURLOPT_UPLOAD, 1L },
- { CURLOPT_READDATA, &getBytesToSend },
- { CURLOPT_READFUNCTION, getBytesToSendWrapper },
+ {CURLOPT_UPLOAD, 1L},
+ {CURLOPT_READDATA, &getBytesToSend},
+ {CURLOPT_READFUNCTION, getBytesToSendWrapper},
- //{ CURLOPT_INFILESIZE_LARGE, static_cast<curl_off_t>(inputBuffer.size()) },
+ //{CURLOPT_INFILESIZE_LARGE, static_cast<curl_off_t>(inputBuffer.size())},
//=> CURLOPT_INFILESIZE_LARGE does not issue a specific FTP command, but is used by libcurl only!
- //{ CURLOPT_PREQUOTE, quote },
- //{ CURLOPT_POSTQUOTE, quote },
+ //{CURLOPT_PREQUOTE, quote},
+ //{CURLOPT_POSTQUOTE, quote},
}, true /*requiresUtf8*/, login.timeoutSec); //throw SysError
});
}
@@ -1733,7 +1739,7 @@ struct InputStreamFtp : public AFS::InputStream
{
InputStreamFtp(const FtpLogin& login,
const AfsPath& afsPath,
- const IOCallback& notifyUnbufferedIO /*throw X*/) :
+ const IoCallback& notifyUnbufferedIO /*throw X*/) :
notifyUnbufferedIO_(notifyUnbufferedIO)
{
worker_ = InterruptibleThread([asyncStreamOut = this->asyncStreamIn_, login, afsPath]
@@ -1784,7 +1790,7 @@ private:
totalBytesReported_ = totalBytesDownloaded;
}
- const IOCallback notifyUnbufferedIO_; //throw X
+ const IoCallback notifyUnbufferedIO_; //throw X
int64_t totalBytesReported_ = 0;
std::shared_ptr<AsyncStreamBuffer> asyncStreamIn_ = std::make_shared<AsyncStreamBuffer>(FTP_STREAM_BUFFER_SIZE);
InterruptibleThread worker_;
@@ -1799,7 +1805,7 @@ struct OutputStreamFtp : public AFS::OutputStreamImpl
OutputStreamFtp(const FtpLogin& login,
const AfsPath& afsPath,
std::optional<time_t> modTime,
- const IOCallback& notifyUnbufferedIO /*throw X*/) :
+ const IoCallback& notifyUnbufferedIO /*throw X*/) :
login_(login),
afsPath_(afsPath),
modTime_(modTime),
@@ -1861,7 +1867,7 @@ struct OutputStreamFtp : public AFS::OutputStreamImpl
futUploadDone_.get(); //throw FileError
AFS::FinalizeResult result;
- //result.fileId = ... -> not supported by FTP
+ //result.filePrint = ... -> yet unknown at this point
try
{
setModTimeIfAvailable(); //throw FileError, follows symlinks
@@ -1910,7 +1916,7 @@ private:
const FtpLogin login_;
const AfsPath afsPath_;
const std::optional<time_t> modTime_;
- const IOCallback notifyUnbufferedIO_; //throw X
+ const IoCallback notifyUnbufferedIO_; //throw X
int64_t totalBytesReported_ = 0;
std::shared_ptr<AsyncStreamBuffer> asyncStreamOut_ = std::make_shared<AsyncStreamBuffer>(FTP_STREAM_BUFFER_SIZE);
InterruptibleThread worker_;
@@ -2110,7 +2116,7 @@ private:
//----------------------------------------------------------------------------------------------------------------
//return value always bound:
- std::unique_ptr<InputStream> getInputStream(const AfsPath& afsPath, const IOCallback& notifyUnbufferedIO /*throw X*/) const override //throw FileError, (ErrorFileLocked)
+ std::unique_ptr<InputStream> getInputStream(const AfsPath& afsPath, const IoCallback& notifyUnbufferedIO /*throw X*/) const override //throw FileError, (ErrorFileLocked)
{
return std::make_unique<InputStreamFtp>(login_, afsPath, notifyUnbufferedIO);
}
@@ -2120,7 +2126,7 @@ private:
std::unique_ptr<OutputStreamImpl> getOutputStream(const AfsPath& afsPath, //throw FileError
std::optional<uint64_t> streamSize,
std::optional<time_t> modTime,
- const IOCallback& notifyUnbufferedIO /*throw X*/) const override
+ const IoCallback& notifyUnbufferedIO /*throw X*/) const override
{
/* most FTP servers overwrite, but some (e.g. IIS) can be configured to fail, others (pureFTP) can be configured to auto-rename:
https://download.pureftpd.org/pub/pure-ftpd/doc/README
@@ -2140,7 +2146,7 @@ private:
//symlink handling: follow
//already existing: undefined behavior! (e.g. fail/overwrite/auto-rename)
FileCopyResult copyFileForSameAfsType(const AfsPath& afsSource, const StreamAttributes& attrSource, //throw FileError, (ErrorFileLocked), X
- const AbstractPath& apTarget, bool copyFilePermissions, const IOCallback& notifyUnbufferedIO /*throw X*/) const override
+ const AbstractPath& apTarget, bool copyFilePermissions, const IoCallback& notifyUnbufferedIO /*throw X*/) const override
{
//no native FTP file copy => use stream-based file copy:
if (copyFilePermissions)
@@ -2195,8 +2201,8 @@ private:
session.perform(AfsPath(), true /*isDir*/, CURLFTPMETHOD_NOCWD, //avoid needless CWDs
{
- { CURLOPT_NOBODY, 1L },
- { CURLOPT_QUOTE, quote },
+ {CURLOPT_NOBODY, 1L},
+ {CURLOPT_QUOTE, quote},
}, true /*requiresUtf8*/, login_.timeoutSec); //throw SysError
});
}
@@ -2361,7 +2367,7 @@ AbstractPath fff::createItemPathFtp(const Zstring& itemPathPhrase) //noexcept
auto it = std::find_if(fullPath.begin(), fullPath.end(), [](Zchar c) { return c == '/' || c == '\\'; });
const Zstring serverPort(fullPath.begin(), it);
- const AfsPath serverRelPath = sanitizeDeviceRelativePath({ it, fullPath.end() });
+ const AfsPath serverRelPath = sanitizeDeviceRelativePath({it, fullPath.end()});
login.server = beforeLast(serverPort, Zstr(':'), IfNotFoundReturn::all);
const Zstring port = afterLast(serverPort, Zstr(':'), IfNotFoundReturn::none);
diff --git a/FreeFileSync/Source/afs/gdrive.cpp b/FreeFileSync/Source/afs/gdrive.cpp
index b731477b..863fad43 100644
--- a/FreeFileSync/Source/afs/gdrive.cpp
+++ b/FreeFileSync/Source/afs/gdrive.cpp
@@ -169,6 +169,14 @@ std::wstring formatGdriveErrorRaw(std::string serverResponse)
return utfTo<std::wstring>(serverResponse);
}
+
+AFS::FingerPrint getGdriveFilePrint(const std::string& itemId)
+{
+ assert(!itemId.empty());
+ //Google Drive item ID is persistent and globally unique! :)
+ return hashArray<AFS::FingerPrint>(itemId.begin(), itemId.end());
+}
+
//----------------------------------------------------------------------------------------------------------------
constinit2 Global<UniSessionCounter> httpSessionCount;
@@ -180,7 +188,8 @@ UniInitializer startupInitHttp(*httpSessionCount.get());
class HttpSessionManager //reuse (healthy) HTTP sessions globally
{
public:
- explicit HttpSessionManager(const Zstring& caCertFilePath) : caCertFilePath_(caCertFilePath),
+ explicit HttpSessionManager(const Zstring& caCertFilePath) :
+ caCertFilePath_(caCertFilePath),
sessionCleaner_([this]
{
setCurrentThreadName(Zstr("Session Cleaner[HTTP]"));
@@ -317,7 +326,7 @@ HttpSession::Result gdriveHttpsRequest(const std::string& serverRelPath, //throw
{
//https://developers.google.com/drive/api/v3/performance
//"In order to receive a gzip-encoded response you must do two things: Set an Accept-Encoding header, ["gzip" automatically set by HttpSession]
- { CURLOPT_USERAGENT, "FreeFileSync (gzip)" }, //and modify your user agent to contain the string gzip."
+ {CURLOPT_USERAGENT, "FreeFileSync (gzip)"}, //and modify your user agent to contain the string gzip."
};
append(options, extraOptions);
@@ -338,10 +347,10 @@ GdriveUser getGdriveUser(const std::string& accessToken) //throw SysError
//https://developers.google.com/drive/api/v3/reference/about
const std::string& queryParams = xWwwFormUrlEncode(
{
- { "fields", "user/displayName,user/emailAddress" },
+ {"fields", "user/displayName,user/emailAddress"},
});
std::string response;
- gdriveHttpsRequest("/drive/v3/about?" + queryParams, { "Authorization: Bearer " + accessToken }, {} /*extraOptions*/, //throw SysError
+ gdriveHttpsRequest("/drive/v3/about?" + queryParams, {"Authorization: Bearer " + accessToken}, {} /*extraOptions*/, //throw SysError
[&](const void* buffer, size_t bytesToWrite) { response.append(static_cast<const char*>(buffer), bytesToWrite); }, nullptr /*readRequest*/);
JsonValue jresponse;
@@ -353,7 +362,7 @@ GdriveUser getGdriveUser(const std::string& accessToken) //throw SysError
const std::optional<std::string> displayName = getPrimitiveFromJsonObject(*user, "displayName");
const std::optional<std::string> email = getPrimitiveFromJsonObject(*user, "emailAddress");
if (displayName && email)
- return { utfTo<std::wstring>(*displayName), *email };
+ return {utfTo<std::wstring>(*displayName), *email};
}
throw SysError(formatGdriveErrorRaw(response));
@@ -385,15 +394,15 @@ GdriveAccessInfo gdriveExchangeAuthCode(const GdriveAuthCode& authCode) //throw
//https://developers.google.com/identity/protocols/OAuth2InstalledApp#exchange-authorization-code
const std::string postBuf = xWwwFormUrlEncode(
{
- { "code", authCode.code },
- { "client_id", getGdriveClientId() },
- { "client_secret", getGdriveClientSecret() },
- { "redirect_uri", authCode.redirectUrl },
- { "grant_type", "authorization_code" },
- { "code_verifier", authCode.codeChallenge },
+ {"code", authCode.code},
+ {"client_id", getGdriveClientId()},
+ {"client_secret", getGdriveClientSecret()},
+ {"redirect_uri", authCode.redirectUrl},
+ {"grant_type", "authorization_code"},
+ {"code_verifier", authCode.codeChallenge},
});
std::string response;
- gdriveHttpsRequest("/oauth2/v4/token", {} /*extraHeaders*/, { { CURLOPT_POSTFIELDS, postBuf.c_str() } }, //throw SysError
+ gdriveHttpsRequest("/oauth2/v4/token", {} /*extraHeaders*/, {{ CURLOPT_POSTFIELDS, postBuf.c_str()}}, //throw SysError
[&](const void* buffer, size_t bytesToWrite) { response.append(static_cast<const char*>(buffer), bytesToWrite); }, nullptr /*readRequest*/);
JsonValue jresponse;
@@ -408,7 +417,7 @@ GdriveAccessInfo gdriveExchangeAuthCode(const GdriveAuthCode& authCode) //throw
const GdriveUser userInfo = getGdriveUser(*accessToken); //throw SysError
- return { { *accessToken, std::time(nullptr) + stringTo<time_t>(*expiresIn) }, *refreshToken, userInfo };
+ return {{*accessToken, std::time(nullptr) + stringTo<time_t>(*expiresIn)}, *refreshToken, userInfo};
}
@@ -426,9 +435,9 @@ GdriveAccessInfo gdriveAuthorizeAccess(const std::string& gdriveLoginHint, const
ZEN_ON_SCOPE_EXIT(if (servinfo) ::freeaddrinfo(servinfo));
//ServiceName == "0" => open the next best free port
- const int rcGai = ::getaddrinfo(nullptr, //_In_opt_ PCSTR pNodeName,
- "0", //_In_opt_ PCSTR pServiceName,
- &hints, //_In_opt_ const ADDRINFOA* pHints,
+ const int rcGai = ::getaddrinfo(nullptr, //_In_opt_ PCSTR pNodeName
+ "0", //_In_opt_ PCSTR pServiceName
+ &hints, //_In_opt_ const ADDRINFOA* pHints
&servinfo); //_Outptr_ PADDRINFOA* ppResult
if (rcGai != 0)
throw SysError(formatSystemError("getaddrinfo", replaceCpy(_("Error code %x"), L"%x", numberTo<std::wstring>(rcGai)), utfTo<std::wstring>(::gai_strerror(rcGai))));
@@ -493,13 +502,13 @@ GdriveAccessInfo gdriveAuthorizeAccess(const std::string& gdriveLoginHint, const
//authenticate Google Drive via browser: https://developers.google.com/identity/protocols/OAuth2InstalledApp#step-2-send-a-request-to-googles-oauth-20-server
const std::string oauthUrl = "https://accounts.google.com/o/oauth2/v2/auth?" + xWwwFormUrlEncode(
{
- { "client_id", getGdriveClientId() },
- { "redirect_uri", redirectUrl },
- { "response_type", "code" },
- { "scope", "https://www.googleapis.com/auth/drive" },
- { "code_challenge", codeChallenge },
- { "code_challenge_method", "plain" },
- { "login_hint", gdriveLoginHint },
+ {"client_id", getGdriveClientId()},
+ {"redirect_uri", redirectUrl},
+ {"response_type", "code"},
+ {"scope", "https://www.googleapis.com/auth/drive"},
+ {"code_challenge", codeChallenge},
+ {"code_challenge_method", "plain"},
+ {"login_hint", gdriveLoginHint},
});
try
{
@@ -519,7 +528,7 @@ GdriveAccessInfo gdriveAuthorizeAccess(const std::string& gdriveLoginHint, const
FD_SET(socket, &rfd);
fd_set* readfds = &rfd;
- struct ::timeval tv = {};
+ timeval tv = {};
tv.tv_usec = static_cast<long>(100 /*ms*/) * 1000;
//WSAPoll broken, even ::poll() on OS X? https://daniel.haxx.se/blog/2012/10/10/wsapoll-is-broken/
@@ -613,7 +622,7 @@ GdriveAccessInfo gdriveAuthorizeAccess(const std::string& gdriveLoginHint, const
//do as many login-related tasks as possible while we have the browser as an error output device!
//see AFS::connectNetworkFolder() => errors will be lost after time out in dir_exist_async.h!
- authResult = gdriveExchangeAuthCode({ code, redirectUrl, codeChallenge }); //throw SysError
+ authResult = gdriveExchangeAuthCode({code, redirectUrl, codeChallenge}); //throw SysError
replace(htmlMsg, "TITLE_PLACEHOLDER", utfTo<std::string>(_("Authentication completed.")));
replace(htmlMsg, "MESSAGE_PLACEHOLDER", utfTo<std::string>(_("You may close this page now and continue with FreeFileSync.")));
}
@@ -651,14 +660,14 @@ GdriveAccessToken gdriveRefreshAccess(const std::string& refreshToken) //throw S
//https://developers.google.com/identity/protocols/OAuth2InstalledApp#offline
const std::string postBuf = xWwwFormUrlEncode(
{
- { "refresh_token", refreshToken },
- { "client_id", getGdriveClientId() },
- { "client_secret", getGdriveClientSecret() },
- { "grant_type", "refresh_token" },
+ {"refresh_token", refreshToken},
+ {"client_id", getGdriveClientId()},
+ {"client_secret", getGdriveClientSecret()},
+ {"grant_type", "refresh_token"},
});
std::string response;
- gdriveHttpsRequest("/oauth2/v4/token", {} /*extraHeaders*/, { { CURLOPT_POSTFIELDS, postBuf.c_str() } }, //throw SysError
+ gdriveHttpsRequest("/oauth2/v4/token", {} /*extraHeaders*/, {{CURLOPT_POSTFIELDS, postBuf.c_str()}}, //throw SysError
[&](const void* buffer, size_t bytesToWrite) { response.append(static_cast<const char*>(buffer), bytesToWrite); }, nullptr /*readRequest*/);
JsonValue jresponse;
@@ -670,7 +679,7 @@ GdriveAccessToken gdriveRefreshAccess(const std::string& refreshToken) //throw S
if (!accessToken || !expiresIn)
throw SysError(formatGdriveErrorRaw(response));
- return { *accessToken, std::time(nullptr) + stringTo<time_t>(*expiresIn) };
+ return {*accessToken, std::time(nullptr) + stringTo<time_t>(*expiresIn)};
}
@@ -686,7 +695,7 @@ void gdriveRevokeAccess(const std::string& accessToken) //throw SysError
mgr->access(HttpSessionId(Zstr("accounts.google.com")), [&](HttpSession& session) //throw SysError
{
- httpResult = session.perform("/o/oauth2/revoke?token=" + accessToken, { "Content-Type: application/x-www-form-urlencoded" }, {} /*extraOptions*/,
+ httpResult = session.perform("/o/oauth2/revoke?token=" + accessToken, {"Content-Type: application/x-www-form-urlencoded"}, {} /*extraOptions*/,
[&](const void* buffer, size_t bytesToWrite) { response.append(static_cast<const char*>(buffer), bytesToWrite); }, nullptr /*readRequest*/); //throw SysError
});
@@ -699,7 +708,7 @@ int64_t gdriveGetMyDriveFreeSpace(const std::string& accessToken) //throw SysErr
{
//https://developers.google.com/drive/api/v3/reference/about
std::string response;
- gdriveHttpsRequest("/drive/v3/about?fields=storageQuota", { "Authorization: Bearer " + accessToken }, {} /*extraOptions*/, //throw SysError
+ gdriveHttpsRequest("/drive/v3/about?fields=storageQuota", {"Authorization: Bearer " + accessToken}, {} /*extraOptions*/, //throw SysError
[&](const void* buffer, size_t bytesToWrite) { response.append(static_cast<const char*>(buffer), bytesToWrite); }, nullptr /*readRequest*/);
JsonValue jresponse;
@@ -741,14 +750,14 @@ std::vector<DriveDetails> getSharedDrives(const std::string& accessToken) //thro
{
std::string queryParams = xWwwFormUrlEncode(
{
- { "pageSize", "100" }, //"[1, 100] Default: 10"
- { "fields", "nextPageToken,drives(id,name)" },
+ {"pageSize", "100"}, //"[1, 100] Default: 10"
+ {"fields", "nextPageToken,drives(id,name)"},
});
if (nextPageToken)
- queryParams += '&' + xWwwFormUrlEncode({ { "pageToken", *nextPageToken } });
+ queryParams += '&' + xWwwFormUrlEncode({{"pageToken", *nextPageToken}});
std::string response;
- gdriveHttpsRequest("/drive/v3/drives?" + queryParams, { "Authorization: Bearer " + accessToken }, {} /*extraOptions*/, //throw SysError
+ gdriveHttpsRequest("/drive/v3/drives?" + queryParams, {"Authorization: Bearer " + accessToken}, {} /*extraOptions*/, //throw SysError
[&](const void* buffer, size_t bytesToWrite) { response.append(static_cast<const char*>(buffer), bytesToWrite); }, nullptr /*readRequest*/);
JsonValue jresponse;
@@ -767,7 +776,7 @@ std::vector<DriveDetails> getSharedDrives(const std::string& accessToken) //thro
if (!driveId || !driveName)
throw SysError(formatGdriveErrorRaw(serializeJson(driveVal)));
- sharedDrives.push_back({ std::move(*driveId), utfTo<Zstring>(*driveName) });
+ sharedDrives.push_back({std::move(*driveId), utfTo<Zstring>(*driveName)});
}
}
while (nextPageToken);
@@ -865,7 +874,7 @@ GdriveItemDetails extractItemDetails(JsonValue jvalue) //throw SysError
//evaluate "targetMimeType" ? don't bother: "The MIME type of a shortcut can become stale"!
}
- return { utfTo<Zstring>(*itemName), fileSize, modTime, type, owner, std::move(targetId), std::move(parentIds) };
+ return {utfTo<Zstring>(*itemName), fileSize, modTime, type, owner, std::move(targetId), std::move(parentIds)};
}
@@ -874,11 +883,11 @@ GdriveItemDetails getItemDetails(const std::string& itemId, const std::string& a
//https://developers.google.com/drive/api/v3/reference/files/get
const std::string& queryParams = xWwwFormUrlEncode(
{
- { "fields", "trashed,name,mimeType,ownedByMe,size,modifiedTime,parents,shortcutDetails(targetId)" },
- { "supportsAllDrives", "true" },
+ {"fields", "trashed,name,mimeType,ownedByMe,size,modifiedTime,parents,shortcutDetails(targetId)"},
+ {"supportsAllDrives", "true"},
});
std::string response;
- gdriveHttpsRequest("/drive/v3/files/" + itemId + '?' + queryParams, { "Authorization: Bearer " + accessToken }, {} /*extraOptions*/, //throw SysError
+ gdriveHttpsRequest("/drive/v3/files/" + itemId + '?' + queryParams, {"Authorization: Bearer " + accessToken}, {} /*extraOptions*/, //throw SysError
[&](const void* buffer, size_t bytesToWrite) { response.append(static_cast<const char*>(buffer), bytesToWrite); }, nullptr /*readRequest*/);
try
{
@@ -912,19 +921,19 @@ std::vector<GdriveItem> readFolderContent(const std::string& folderId, const std
{
std::string queryParams = xWwwFormUrlEncode(
{
- { "corpora", "allDrives" }, //"The 'user' corpus includes all files in "My Drive" and "Shared with me" https://developers.google.com/drive/api/v3/reference/files/list
- { "includeItemsFromAllDrives", "true" },
- { "pageSize", "1000" }, //"[1, 1000] Default: 100"
- { "q", "trashed=false and '" + folderId + "' in parents" },
- { "spaces", "drive" },
- { "supportsAllDrives", "true" },
- { "fields", "nextPageToken,incompleteSearch,files(id,name,mimeType,ownedByMe,size,modifiedTime,parents,shortcutDetails(targetId))" }, //https://developers.google.com/drive/api/v3/reference/files
+ {"corpora", "allDrives"}, //"The 'user' corpus includes all files in "My Drive" and "Shared with me" https://developers.google.com/drive/api/v3/reference/files/list
+ {"includeItemsFromAllDrives", "true"},
+ {"pageSize", "1000"}, //"[1, 1000] Default: 100"
+ {"q", "trashed=false and '" + folderId + "' in parents"},
+ {"spaces", "drive"},
+ {"supportsAllDrives", "true"},
+ {"fields", "nextPageToken,incompleteSearch,files(id,name,mimeType,ownedByMe,size,modifiedTime,parents,shortcutDetails(targetId))"}, //https://developers.google.com/drive/api/v3/reference/files
});
if (nextPageToken)
- queryParams += '&' + xWwwFormUrlEncode({ { "pageToken", *nextPageToken } });
+ queryParams += '&' + xWwwFormUrlEncode({{"pageToken", *nextPageToken}});
std::string response;
- gdriveHttpsRequest("/drive/v3/files?" + queryParams, { "Authorization: Bearer " + accessToken }, {} /*extraOptions*/, //throw SysError
+ gdriveHttpsRequest("/drive/v3/files?" + queryParams, {"Authorization: Bearer " + accessToken}, {} /*extraOptions*/, //throw SysError
[&](const void* buffer, size_t bytesToWrite) { response.append(static_cast<const char*>(buffer), bytesToWrite); }, nullptr /*readRequest*/);
JsonValue jresponse;
@@ -946,7 +955,7 @@ std::vector<GdriveItem> readFolderContent(const std::string& folderId, const std
GdriveItemDetails itemDetails(extractItemDetails(childVal)); //throw SysError
assert(std::find(itemDetails.parentIds.begin(), itemDetails.parentIds.end(), folderId) != itemDetails.parentIds.end());
- childItems.push_back({ std::move(*itemId), std::move(itemDetails) });
+ childItems.push_back({std::move(*itemId), std::move(itemDetails)});
}
}
while (nextPageToken);
@@ -980,16 +989,16 @@ ChangesDelta getChangesDelta(const std::string& startPageToken, const std::strin
{
const std::string& queryParams = xWwwFormUrlEncode(
{
- { "pageToken", *nextPageToken },
- { "fields", "kind,nextPageToken,newStartPageToken,changes(kind,changeType,removed,fileId,file(trashed,name,mimeType,ownedByMe,size,modifiedTime,parents,shortcutDetails(targetId)),driveId,drive(name))" },
- { "includeItemsFromAllDrives", "true" },
- { "pageSize", "1000" }, //"[1, 1000] Default: 100"
- { "spaces", "drive" },
- { "supportsAllDrives", "true" },
+ {"pageToken", *nextPageToken},
+ {"fields", "kind,nextPageToken,newStartPageToken,changes(kind,changeType,removed,fileId,file(trashed,name,mimeType,ownedByMe,size,modifiedTime,parents,shortcutDetails(targetId)),driveId,drive(name))"},
+ {"includeItemsFromAllDrives", "true"},
+ {"pageSize", "1000"}, //"[1, 1000] Default: 100"
+ {"spaces", "drive"},
+ {"supportsAllDrives", "true"},
//do NOT "restrictToMyDrive": we're also interested in "Shared with me" items, which might be referenced by a shortcut in "My Drive"
});
std::string response;
- gdriveHttpsRequest("/drive/v3/changes?" + queryParams, { "Authorization: Bearer " + accessToken }, {} /*extraOptions*/, //throw SysError
+ gdriveHttpsRequest("/drive/v3/changes?" + queryParams, {"Authorization: Bearer " + accessToken}, {} /*extraOptions*/, //throw SysError
[&](const void* buffer, size_t bytesToWrite) { response.append(static_cast<const char*>(buffer), bytesToWrite); }, nullptr /*readRequest*/);
JsonValue jresponse;
@@ -1076,10 +1085,10 @@ std::string /*startPageToken*/ getChangesCurrentToken(const std::string& accessT
//https://developers.google.com/drive/api/v3/reference/changes/getStartPageToken
const std::string& queryParams = xWwwFormUrlEncode(
{
- { "supportsAllDrives", "true" },
+ {"supportsAllDrives", "true"},
});
std::string response;
- gdriveHttpsRequest("/drive/v3/changes/startPageToken?" + queryParams, { "Authorization: Bearer " + accessToken }, {} /*extraOptions*/, //throw SysError
+ gdriveHttpsRequest("/drive/v3/changes/startPageToken?" + queryParams, {"Authorization: Bearer " + accessToken}, {} /*extraOptions*/, //throw SysError
[&](const void* buffer, size_t bytesToWrite) { response.append(static_cast<const char*>(buffer), bytesToWrite); }, nullptr /*readRequest*/);
JsonValue jresponse;
@@ -1101,11 +1110,11 @@ void gdriveDeleteItem(const std::string& itemId, const std::string& accessToken)
//https://developers.google.com/drive/api/v3/reference/files/delete
const std::string& queryParams = xWwwFormUrlEncode(
{
- { "supportsAllDrives", "true" },
+ {"supportsAllDrives", "true"},
});
std::string response;
- const HttpSession::Result httpResult = gdriveHttpsRequest("/drive/v3/files/" + itemId + '?' + queryParams, { "Authorization: Bearer " + accessToken }, //throw SysError
- { { CURLOPT_CUSTOMREQUEST, "DELETE" } }, [&](const void* buffer, size_t bytesToWrite) { response.append(static_cast<const char*>(buffer), bytesToWrite); }, nullptr /*readRequest*/);
+ const HttpSession::Result httpResult = gdriveHttpsRequest("/drive/v3/files/" + itemId + '?' + queryParams, {"Authorization: Bearer " + accessToken}, //throw SysError
+ {{CURLOPT_CUSTOMREQUEST, "DELETE"}}, [&](const void* buffer, size_t bytesToWrite) { response.append(static_cast<const char*>(buffer), bytesToWrite); }, nullptr /*readRequest*/);
if (response.empty() && httpResult.statusCode == 204)
return; //"If successful, this method returns an empty response body"
@@ -1120,14 +1129,14 @@ void gdriveUnlinkParent(const std::string& itemId, const std::string& parentId,
//https://developers.google.com/drive/api/v3/reference/files/update
const std::string& queryParams = xWwwFormUrlEncode(
{
- { "removeParents", parentId },
- { "supportsAllDrives", "true" },
- { "fields", "id,parents" }, //for test if operation was successful
+ {"removeParents", parentId},
+ {"supportsAllDrives", "true"},
+ {"fields", "id,parents"}, //for test if operation was successful
});
std::string response;
const HttpSession::Result httpResult = gdriveHttpsRequest("/drive/v3/files/" + itemId + '?' + queryParams, //throw SysError
- { "Authorization: Bearer " + accessToken, "Content-Type: application/json; charset=UTF-8" },
- { { CURLOPT_CUSTOMREQUEST, "PATCH" }, { CURLOPT_POSTFIELDS, "{}" } },
+ {"Authorization: Bearer " + accessToken, "Content-Type: application/json; charset=UTF-8"},
+ {{ CURLOPT_CUSTOMREQUEST, "PATCH"}, { CURLOPT_POSTFIELDS, "{}"}},
[&](const void* buffer, size_t bytesToWrite) { response.append(static_cast<const char*>(buffer), bytesToWrite); }, nullptr /*readRequest*/);
if (response.empty() && httpResult.statusCode == 204)
@@ -1157,14 +1166,14 @@ void gdriveMoveToTrash(const std::string& itemId, const std::string& accessToken
//https://developers.google.com/drive/api/v3/reference/files/update
const std::string& queryParams = xWwwFormUrlEncode(
{
- { "supportsAllDrives", "true" },
- { "fields", "trashed" },
+ {"supportsAllDrives", "true"},
+ {"fields", "trashed"},
});
const std::string postBuf = R"({ "trashed": true })";
std::string response;
- gdriveHttpsRequest("/drive/v3/files/" + itemId + '?' + queryParams, { "Authorization: Bearer " + accessToken, "Content-Type: application/json; charset=UTF-8" }, //throw SysError
- { { CURLOPT_CUSTOMREQUEST, "PATCH" }, { CURLOPT_POSTFIELDS, postBuf.c_str() } },
+ gdriveHttpsRequest("/drive/v3/files/" + itemId + '?' + queryParams, {"Authorization: Bearer " + accessToken, "Content-Type: application/json; charset=UTF-8"}, //throw SysError
+ {{CURLOPT_CUSTOMREQUEST, "PATCH"}, {CURLOPT_POSTFIELDS, postBuf.c_str()}},
[&](const void* buffer, size_t bytesToWrite) { response.append(static_cast<const char*>(buffer), bytesToWrite); }, nullptr /*readRequest*/);
JsonValue jresponse;
@@ -1183,18 +1192,18 @@ std::string /*folderId*/ gdriveCreateFolderPlain(const Zstring& folderName, cons
//https://developers.google.com/drive/api/v3/folder#creating_a_folder
const std::string& queryParams = xWwwFormUrlEncode(
{
- { "supportsAllDrives", "true" },
- { "fields", "id" },
+ {"supportsAllDrives", "true"},
+ {"fields", "id"},
});
JsonValue postParams(JsonValue::Type::object);
postParams.objectVal.emplace("mimeType", gdriveFolderMimeType);
postParams.objectVal.emplace("name", utfTo<std::string>(folderName));
- postParams.objectVal.emplace("parents", std::vector<JsonValue> { JsonValue(parentId) });
+ postParams.objectVal.emplace("parents", std::vector<JsonValue> {JsonValue(parentId)});
const std::string& postBuf = serializeJson(postParams, "" /*lineBreak*/, "" /*indent*/);
std::string response;
- gdriveHttpsRequest("/drive/v3/files?" + queryParams, { "Authorization: Bearer " + accessToken, "Content-Type: application/json; charset=UTF-8" },
- { { CURLOPT_POSTFIELDS, postBuf.c_str() } },
+ gdriveHttpsRequest("/drive/v3/files?" + queryParams, {"Authorization: Bearer " + accessToken, "Content-Type: application/json; charset=UTF-8"},
+ {{CURLOPT_POSTFIELDS, postBuf.c_str()}},
[&](const void* buffer, size_t bytesToWrite) { response.append(static_cast<const char*>(buffer), bytesToWrite); }, nullptr /*readRequest*/); //throw SysError
JsonValue jresponse;
@@ -1216,8 +1225,8 @@ std::string /*shortcutId*/ gdriveCreateShortcutPlain(const Zstring& shortcutName
- creating shortcuts to shortcuts fails with "Internal Error" */
const std::string& queryParams = xWwwFormUrlEncode(
{
- { "supportsAllDrives", "true" },
- { "fields", "id" },
+ {"supportsAllDrives", "true"},
+ {"fields", "id"},
});
JsonValue shortcutDetails(JsonValue::Type::object);
shortcutDetails.objectVal.emplace("targetId", targetId);
@@ -1225,13 +1234,13 @@ std::string /*shortcutId*/ gdriveCreateShortcutPlain(const Zstring& shortcutName
JsonValue postParams(JsonValue::Type::object);
postParams.objectVal.emplace("mimeType", gdriveShortcutMimeType);
postParams.objectVal.emplace("name", utfTo<std::string>(shortcutName));
- postParams.objectVal.emplace("parents", std::vector<JsonValue> { JsonValue(parentId) });
+ postParams.objectVal.emplace("parents", std::vector<JsonValue> {JsonValue(parentId)});
postParams.objectVal.emplace("shortcutDetails", std::move(shortcutDetails));
const std::string& postBuf = serializeJson(postParams, "" /*lineBreak*/, "" /*indent*/);
std::string response;
- gdriveHttpsRequest("/drive/v3/files?" + queryParams, { "Authorization: Bearer " + accessToken, "Content-Type: application/json; charset=UTF-8" },
- { { CURLOPT_POSTFIELDS, postBuf.c_str() } },
+ gdriveHttpsRequest("/drive/v3/files?" + queryParams, {"Authorization: Bearer " + accessToken, "Content-Type: application/json; charset=UTF-8"},
+ {{CURLOPT_POSTFIELDS, postBuf.c_str()}},
[&](const void* buffer, size_t bytesToWrite) { response.append(static_cast<const char*>(buffer), bytesToWrite); }, nullptr /*readRequest*/); //throw SysError
JsonValue jresponse;
@@ -1252,8 +1261,8 @@ std::string /*fileId*/ gdriveCopyFile(const std::string& fileId, const std::stri
//https://developers.google.com/drive/api/v3/reference/files/copy
const std::string queryParams = xWwwFormUrlEncode(
{
- { "supportsAllDrives", "true" },
- { "fields", "id" },
+ {"supportsAllDrives", "true"},
+ {"fields", "id"},
});
//more Google Drive peculiarities: changing the file name changes modifiedTime!!! => workaround:
@@ -1265,13 +1274,13 @@ std::string /*fileId*/ gdriveCopyFile(const std::string& fileId, const std::stri
JsonValue postParams(JsonValue::Type::object);
postParams.objectVal.emplace("name", utfTo<std::string>(newName));
- postParams.objectVal.emplace("parents", std::vector<JsonValue> { JsonValue(parentIdTo) });
+ postParams.objectVal.emplace("parents", std::vector<JsonValue> {JsonValue(parentIdTo)});
postParams.objectVal.emplace("modifiedTime", modTimeRfc);
const std::string& postBuf = serializeJson(postParams, "" /*lineBreak*/, "" /*indent*/);
std::string response;
gdriveHttpsRequest("/drive/v3/files/" + fileId + "/copy?" + queryParams, //throw SysError
- { "Authorization: Bearer " + accessToken, "Content-Type: application/json; charset=UTF-8" }, { { CURLOPT_POSTFIELDS, postBuf.c_str() } },
+ {"Authorization: Bearer " + accessToken, "Content-Type: application/json; charset=UTF-8"}, {{CURLOPT_POSTFIELDS, postBuf.c_str()}},
[&](const void* buffer, size_t bytesToWrite) { response.append(static_cast<const char*>(buffer), bytesToWrite); }, nullptr /*readRequest*/);
JsonValue jresponse;
@@ -1294,15 +1303,15 @@ void gdriveMoveAndRenameItem(const std::string& itemId, const std::string& paren
//https://developers.google.com/drive/api/v3/folder#moving_files_between_folders
std::string queryParams = xWwwFormUrlEncode(
{
- { "supportsAllDrives", "true" },
- { "fields", "name,parents" }, //for test if operation was successful
+ {"supportsAllDrives", "true"},
+ {"fields", "name,parents"}, //for test if operation was successful
});
if (parentIdFrom != parentIdTo)
queryParams += '&' + xWwwFormUrlEncode(
{
- { "removeParents", parentIdFrom },
- { "addParents", parentIdTo },
+ {"removeParents", parentIdFrom},
+ {"addParents", parentIdTo},
});
//more Google Drive peculiarities: changing the file name changes modifiedTime!!! => workaround:
@@ -1319,8 +1328,8 @@ void gdriveMoveAndRenameItem(const std::string& itemId, const std::string& paren
std::string response;
gdriveHttpsRequest("/drive/v3/files/" + itemId + '?' + queryParams, //throw SysError
- { "Authorization: Bearer " + accessToken, "Content-Type: application/json; charset=UTF-8" },
- { { CURLOPT_CUSTOMREQUEST, "PATCH" }, { CURLOPT_POSTFIELDS, postBuf.c_str() } },
+ {"Authorization: Bearer " + accessToken, "Content-Type: application/json; charset=UTF-8"},
+ {{CURLOPT_CUSTOMREQUEST, "PATCH"}, {CURLOPT_POSTFIELDS, postBuf.c_str()}},
[&](const void* buffer, size_t bytesToWrite) { response.append(static_cast<const char*>(buffer), bytesToWrite); }, nullptr /*readRequest*/);
JsonValue jresponse;
@@ -1350,14 +1359,14 @@ void setModTime(const std::string& itemId, time_t modTime, const std::string& ac
const std::string& queryParams = xWwwFormUrlEncode(
{
- { "supportsAllDrives", "true" },
- { "fields", "modifiedTime" },
+ {"supportsAllDrives", "true"},
+ {"fields", "modifiedTime"},
});
const std::string postBuf = R"({ "modifiedTime": ")" + modTimeRfc + "\" }";
std::string response;
- gdriveHttpsRequest("/drive/v3/files/" + itemId + '?' + queryParams, { "Authorization: Bearer " + accessToken, "Content-Type: application/json; charset=UTF-8" }, //throw SysError
- { { CURLOPT_CUSTOMREQUEST, "PATCH" }, { CURLOPT_POSTFIELDS, postBuf.c_str() } },
+ gdriveHttpsRequest("/drive/v3/files/" + itemId + '?' + queryParams, {"Authorization: Bearer " + accessToken, "Content-Type: application/json; charset=UTF-8"}, //throw SysError
+ {{CURLOPT_CUSTOMREQUEST, "PATCH"}, {CURLOPT_POSTFIELDS, postBuf.c_str()}},
[&](const void* buffer, size_t bytesToWrite) { response.append(static_cast<const char*>(buffer), bytesToWrite); }, nullptr /*readRequest*/);
JsonValue jresponse;
@@ -1382,17 +1391,17 @@ void gdriveDownloadFileImpl(const std::string& fileId, const std::function<void(
std::string queryParams = xWwwFormUrlEncode(
{
- { "supportsAllDrives", "true" },
- { "alt", "media" },
+ {"supportsAllDrives", "true"},
+ {"alt", "media"},
});
if (acknowledgeAbuse) //apply on demand only! https://freefilesync.org/forum/viewtopic.php?t=7520")
- queryParams += '&' + xWwwFormUrlEncode({ { "acknowledgeAbuse", "true" } });
+ queryParams += '&' + xWwwFormUrlEncode({{"acknowledgeAbuse", "true"}});
std::string responseHead; //save front part of the response in case we get an error
bool headFlushed = false;
const HttpSession::Result httpResult = gdriveHttpsRequest("/drive/v3/files/" + fileId + '?' + queryParams, //throw SysError, X
- { "Authorization: Bearer " + accessToken }, {} /*extraOptions*/,
+ {"Authorization: Bearer " + accessToken}, {} /*extraOptions*/,
[&](const void* buffer, size_t bytesToWrite)
{
if (responseHead.size() < 10000) //don't access writeBlock() in case of error! (=> support acknowledgeAbuse retry handling)
@@ -1456,7 +1465,7 @@ std::string /*itemId*/ gdriveUploadSmallFile(const Zstring& fileName, const std:
JsonValue postParams(JsonValue::Type::object);
postParams.objectVal.emplace("name", utfTo<std::string>(fileName));
- postParams.objectVal.emplace("parents", std::vector<JsonValue> { JsonValue(parentId) });
+ postParams.objectVal.emplace("parents", std::vector<JsonValue> {JsonValue(parentId)});
if (modTime) //convert to RFC 3339 date-time: e.g. "2018-09-29T08:39:12.053Z"
{
const std::string& modTimeRfc = utfTo<std::string>(formatTime(Zstr("%Y-%m-%dT%H:%M:%S.000Z"), getUtcTime(*modTime))); //returns empty string on failure
@@ -1521,8 +1530,8 @@ TODO:
const std::string& queryParams = xWwwFormUrlEncode(
{
- { "supportsAllDrives", "true" },
- { "uploadType", "multipart" },
+ {"supportsAllDrives", "true"},
+ {"uploadType", "multipart"},
});
std::string response;
const HttpSession::Result httpResult = gdriveHttpsRequest("/upload/drive/v3/files?" + queryParams, //throw SysError, X
@@ -1531,7 +1540,7 @@ TODO:
"Content-Type: multipart/related; boundary=" + boundaryString,
"Content-Length: " + numberTo<std::string>(postBufHead.size() + streamSize + postBufTail.size())
},
- { { CURLOPT_POST, 1 } }, //otherwise HttpSession::perform() will PUT
+ {{CURLOPT_POST, 1}}, //otherwise HttpSession::perform() will PUT
[&](const void* buffer, size_t bytesToWrite) { response.append(static_cast<const char*>(buffer), bytesToWrite); }, readMultipartBlock );
JsonValue jresponse;
@@ -1561,12 +1570,12 @@ std::string /*itemId*/ gdriveUploadFile(const Zstring& fileName, const std::stri
{
const std::string& queryParams = xWwwFormUrlEncode(
{
- { "supportsAllDrives", "true" },
- { "uploadType", "resumable" },
+ {"supportsAllDrives", "true"},
+ {"uploadType", "resumable"},
});
JsonValue postParams(JsonValue::Type::object);
postParams.objectVal.emplace("name", utfTo<std::string>(fileName));
- postParams.objectVal.emplace("parents", std::vector<JsonValue> { JsonValue(parentId) });
+ postParams.objectVal.emplace("parents", std::vector<JsonValue> {JsonValue(parentId)});
if (modTime) //convert to RFC 3339 date-time: e.g. "2018-09-29T08:39:12.053Z"
{
const std::string& modTimeRfc = utfTo<std::string>(formatTime(Zstr("%Y-%m-%dT%H:%M:%S.000Z"), getUtcTime(*modTime))); //returns empty string on failure
@@ -1601,8 +1610,8 @@ std::string /*itemId*/ gdriveUploadFile(const Zstring& fileName, const std::stri
std::string response;
const HttpSession::Result httpResult = gdriveHttpsRequest("/upload/drive/v3/files?" + queryParams, //throw SysError
- { "Authorization: Bearer " + accessToken, "Content-Type: application/json; charset=UTF-8" },
- { { CURLOPT_POSTFIELDS, postBuf.c_str() }, { CURLOPT_HEADERDATA, &onBytesReceived }, { CURLOPT_HEADERFUNCTION, onBytesReceivedWrapper } },
+ {"Authorization: Bearer " + accessToken, "Content-Type: application/json; charset=UTF-8"},
+ {{CURLOPT_POSTFIELDS, postBuf.c_str()}, {CURLOPT_HEADERDATA, &onBytesReceived}, {CURLOPT_HEADERFUNCTION, onBytesReceivedWrapper}},
[&](const void* buffer, size_t bytesToWrite) { response.append(static_cast<const char*>(buffer), bytesToWrite); }, nullptr /*readRequest*/);
if (httpResult.statusCode != 200)
@@ -1644,11 +1653,11 @@ std::string /*itemId*/ getMyDriveId(const std::string& accessToken) //throw SysE
//https://developers.google.com/drive/api/v3/reference/files/get
const std::string& queryParams = xWwwFormUrlEncode(
{
- { "supportsAllDrives", "true" },
- { "fields", "id" },
+ {"supportsAllDrives", "true"},
+ {"fields", "id"},
});
std::string response;
- gdriveHttpsRequest("/drive/v3/files/root?" + queryParams, { "Authorization: Bearer " + accessToken }, {} /*extraOptions*/, //throw SysError
+ gdriveHttpsRequest("/drive/v3/files/root?" + queryParams, {"Authorization: Bearer " + accessToken}, {} /*extraOptions*/, //throw SysError
[&](const void* buffer, size_t bytesToWrite) { response.append(static_cast<const char*>(buffer), bytesToWrite); }, nullptr /*readRequest*/);
JsonValue jresponse;
@@ -1721,7 +1730,7 @@ class GdriveFileState //per-user-session! => serialize access (perf: amortized f
{
public:
GdriveFileState(GdriveAccessBuffer& accessBuf) : //throw SysError
- /* issue getChangesCurrentToken() as the very first Google Drive query!*/
+ /* issue getChangesCurrentToken() as the very first Google Drive query! */
lastSyncToken_(getChangesCurrentToken(accessBuf.getAccessToken())), //throw SysError
myDriveId_ (getMyDriveId (accessBuf.getAccessToken())), //
accessBuf_(accessBuf)
@@ -1863,20 +1872,20 @@ public:
{
if (itFound != sharedDrives_.end())
throw SysError(replaceCpy(_("Cannot find %x."), L"%x",
- fmtPath(getGdriveDisplayPath({{ accessBuf_.getUserEmail(), sharedDriveName}, AfsPath() }))) + L' ' +
+ fmtPath(getGdriveDisplayPath({{accessBuf_.getUserEmail(), sharedDriveName}, AfsPath()}))) + L' ' +
replaceCpy(_("The name %x is used by more than one item in the folder."), L"%x", fmtPath(sharedDriveName)));
itFound = it;
}
if (itFound == sharedDrives_.end())
throw SysError(replaceCpy(_("Cannot find %x."), L"%x",
- fmtPath(getGdriveDisplayPath({{ accessBuf_.getUserEmail(), sharedDriveName}, AfsPath() }))));
+ fmtPath(getGdriveDisplayPath({{accessBuf_.getUserEmail(), sharedDriveName}, AfsPath()}))));
return itFound->first;
}();
const std::vector<Zstring> relPath = split(afsPath.value, FILE_NAME_SEPARATOR, SplitOnEmpty::skip);
if (relPath.empty())
- return { driveId, GdriveItemType::folder, AfsPath(), {} };
+ return {driveId, GdriveItemType::folder, AfsPath(), {}};
else
return getPathStatusSub(driveId, sharedDriveName, AfsPath(), relPath, followLeafShortcut); //throw SysError
}
@@ -1888,7 +1897,7 @@ public:
return ps.existingItemId;
const AfsPath afsPathMissingChild(nativeAppendPaths(ps.existingPath.value, ps.relPath.front()));
- throw SysError(replaceCpy(_("Cannot find %x."), L"%x", fmtPath(getGdriveDisplayPath({ { accessBuf_.getUserEmail(), sharedDriveName }, afsPathMissingChild }))));
+ throw SysError(replaceCpy(_("Cannot find %x."), L"%x", fmtPath(getGdriveDisplayPath({{accessBuf_.getUserEmail(), sharedDriveName}, afsPathMissingChild}))));
}
std::pair<std::string /*itemId*/, GdriveItemDetails> getFileAttributes(const Zstring& sharedDriveName, const AfsPath& afsPath, bool followLeafShortcut) //throw SysError
@@ -1903,7 +1912,7 @@ public:
{
rootDetails.itemName = Zstr("My Drive");
rootDetails.owner = FileOwner::me;
- return { itemId, std::move(rootDetails) };
+ return {itemId, std::move(rootDetails)};
}
if (auto it = sharedDrives_.find(itemId);
@@ -1911,7 +1920,7 @@ public:
{
rootDetails.itemName = it->second;
rootDetails.owner = FileOwner::none;
- return { itemId, std::move(rootDetails) };
+ return {itemId, std::move(rootDetails)};
}
}
else if (auto it = itemDetails_.find(itemId);
@@ -1940,7 +1949,7 @@ public:
for (auto itChild : it->second.childItems)
{
const auto& [childId, childDetails] = *itChild;
- childItems.push_back({ childId, childDetails });
+ childItems.push_back({childId, childDetails});
}
return std::move(childItems); //[!] need std::move!
}
@@ -2116,14 +2125,14 @@ private:
{
if (itFound != itemDetails_.end())
throw SysError(replaceCpy(_("Cannot find %x."), L"%x",
- fmtPath(getGdriveDisplayPath({{ accessBuf_.getUserEmail(), sharedDriveName}, AfsPath(nativeAppendPaths(folderPath.value, relPath.front())) }))) + L' ' +
+ fmtPath(getGdriveDisplayPath({{accessBuf_.getUserEmail(), sharedDriveName}, AfsPath(nativeAppendPaths(folderPath.value, relPath.front()))}))) + L' ' +
replaceCpy(_("The name %x is used by more than one item in the folder."), L"%x", fmtPath(relPath.front())));
itFound = itChild;
}
if (itFound == itemDetails_.end())
- return { folderId, GdriveItemType::folder, folderPath, relPath }; //always a folder, see check before recursion above
+ return {folderId, GdriveItemType::folder, folderPath, relPath}; //always a folder, see check before recursion above
else
{
auto getItemDetailsBuffered = [&](const std::string& itemId) -> const GdriveItemDetails&
@@ -2131,7 +2140,7 @@ private:
auto it = itemDetails_.find(itemId);
if (it == itemDetails_.end())
{
- notifyItemUpdated(registerFileStateDelta(), { itemId, getItemDetails(itemId, accessBuf_.getAccessToken()) }); //throw SysError
+ notifyItemUpdated(registerFileStateDelta(), {itemId, getItemDetails(itemId, accessBuf_.getAccessToken())}); //throw SysError
//perf: always buffered, except for direct, first-time folder access!
it = itemDetails_.find(itemId);
assert(it != itemDetails_.end());
@@ -2146,15 +2155,15 @@ private:
if (childRelPath.empty())
{
if (childDetails.type == GdriveItemType::shortcut && followLeafShortcut)
- return { childDetails.targetId, getItemDetailsBuffered(childDetails.targetId).type, childItemPath, childRelPath };
+ return {childDetails.targetId, getItemDetailsBuffered(childDetails.targetId).type, childItemPath, childRelPath};
else
- return { childId, childDetails.type, childItemPath, childRelPath };
+ return {childId, childDetails.type, childItemPath, childRelPath};
}
switch (childDetails.type)
{
case GdriveItemType::file: //parent/file/child-rel-path... => obscure, but possible (and not an error)
- return { childId, childDetails.type, childItemPath, childRelPath };
+ return {childId, childDetails.type, childItemPath, childRelPath};
case GdriveItemType::folder:
return getPathStatusSub(childId, sharedDriveName, childItemPath, childRelPath, followLeafShortcut); //throw SysError
@@ -2163,14 +2172,14 @@ private:
switch (getItemDetailsBuffered(childDetails.targetId).type)
{
case GdriveItemType::file: //parent/file-symlink/child-rel-path... => obscure, but possible (and not an error)
- return { childDetails.targetId, GdriveItemType::file, childItemPath, childRelPath }; //resolve symlinks if in the *middle* of a path!
+ return {childDetails.targetId, GdriveItemType::file, childItemPath, childRelPath}; //resolve symlinks if in the *middle* of a path!
case GdriveItemType::folder: //parent/folder-symlink/child-rel-path... => always follow
return getPathStatusSub(childDetails.targetId, sharedDriveName, childItemPath, childRelPath, followLeafShortcut); //throw SysError
case GdriveItemType::shortcut: //should never happen: creating shortcuts to shortcuts fails with "Internal Error"
throw SysError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x",
- fmtPath(getGdriveDisplayPath({{ accessBuf_.getUserEmail(), sharedDriveName}, AfsPath(nativeAppendPaths(folderPath.value, relPath.front())) }))) + L' ' +
+ fmtPath(getGdriveDisplayPath({{accessBuf_.getUserEmail(), sharedDriveName}, AfsPath(nativeAppendPaths(folderPath.value, relPath.front()))}))) + L' ' +
L"Google Drive Shortcut points to another Shortcut.");
}
break;
@@ -2343,7 +2352,7 @@ public:
{
auto accessBuf = makeSharedRef<GdriveAccessBuffer>(accessInfo);
auto fileState = makeSharedRef<GdriveFileState >(accessBuf.ref()); //throw SysError
- userSession = { accessBuf, fileState };
+ userSession = {accessBuf, fileState};
}
});
@@ -2449,7 +2458,7 @@ public:
useFileState(userSession->fileState.ref()); //throw X
});
- return { accessToken, stateDelta };
+ return {accessToken, stateDelta};
}
private:
@@ -2550,7 +2559,7 @@ private:
makeSharedRef<GdriveFileState>( accessBuf.ref()) : //throw SysError
makeSharedRef<GdriveFileState>(streamIn2, accessBuf.ref()); //
- return UserSession{ accessBuf, fileState };
+ return UserSession{accessBuf, fileState};
}
else
{
@@ -2566,7 +2575,7 @@ private:
auto accessBuf = makeSharedRef<GdriveAccessBuffer>(streamInBody); //throw SysError
auto fileState = makeSharedRef<GdriveFileState >(streamInBody, accessBuf.ref()); //throw SysError
- return UserSession{ accessBuf, fileState };
+ return UserSession{accessBuf, fileState};
}
}
catch (const SysError& e)
@@ -2647,7 +2656,7 @@ struct GetDirDetails
if (item.details.itemName.empty())
throw SysError(L"Folder contains an item without name."); //mostly an issue for FFS's folder traversal, but NOT for globalGdriveSessions!
- return { std::move(*childItemsBuf), folderPath_ };
+ return {std::move(*childItemsBuf), folderPath_};
}
catch (const SysError& e) { throw FileError(replaceCpy(_("Cannot read directory %x."), L"%x", fmtPath(getGdriveDisplayPath(folderPath_))), e.toString()); }
}
@@ -2683,14 +2692,15 @@ struct GetShortcutTargetDetails
//buffer new file state ASAP
accessGlobalFileState(shortcutPath_.gdriveLogin.email, [&](GdriveFileState& fileState) //throw SysError
{
- fileState.notifyItemUpdated(aai.stateDelta, { shortcutDetails_.targetId, *targetDetailsBuf });
+ fileState.notifyItemUpdated(aai.stateDelta, {shortcutDetails_.targetId, *targetDetailsBuf});
});
}
+ assert(targetDetailsBuf->targetId.empty());
if (targetDetailsBuf->type == GdriveItemType::shortcut) //should never happen: creating shortcuts to shortcuts fails with "Internal Error"
throw SysError(L"Google Drive Shortcut points to another Shortcut.");
- return { std::move(*targetDetailsBuf), shortcutDetails_, shortcutPath_ };
+ return {std::move(*targetDetailsBuf), shortcutDetails_, shortcutPath_};
}
catch (const SysError& e) { throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtPath(getGdriveDisplayPath(shortcutPath_))), e.toString()); }
}
@@ -2726,7 +2736,7 @@ private:
void traverseWithException(const AfsPath& folderPath, AFS::TraverserCallback& cb) //throw FileError, X
{
- const std::vector<GdriveItem>& childItems = GetDirDetails({ gdriveLogin_, folderPath })().childItems; //throw FileError
+ const std::vector<GdriveItem>& childItems = GetDirDetails({gdriveLogin_, folderPath})().childItems; //throw FileError
for (const GdriveItem& item : childItems)
{
@@ -2735,42 +2745,42 @@ private:
switch (item.details.type)
{
case GdriveItemType::file:
- cb.onFile({ itemName, item.details.fileSize, item.details.modTime, item.itemId, false /*isFollowedSymlink*/ }); //throw X
+ cb.onFile({itemName, item.details.fileSize, item.details.modTime, getGdriveFilePrint(item.itemId), false /*isFollowedSymlink*/}); //throw X
break;
case GdriveItemType::folder:
- if (std::shared_ptr<AFS::TraverserCallback> cbSub = cb.onFolder({ itemName, false /*isFollowedSymlink*/ })) //throw X
+ if (std::shared_ptr<AFS::TraverserCallback> cbSub = cb.onFolder({itemName, false /*isFollowedSymlink*/})) //throw X
{
const AfsPath afsItemPath(nativeAppendPaths(folderPath.value, itemName));
- workload_.push_back({ afsItemPath, std::move(cbSub) });
+ workload_.push_back({afsItemPath, std::move(cbSub)});
}
break;
case GdriveItemType::shortcut:
- switch (cb.onSymlink({ itemName, item.details.modTime })) //throw X
+ switch (cb.onSymlink({itemName, item.details.modTime})) //throw X
{
- case AFS::TraverserCallback::LINK_FOLLOW:
+ case AFS::TraverserCallback::HandleLink::follow:
{
const AfsPath afsItemPath(nativeAppendPaths(folderPath.value, itemName));
GdriveItemDetails targetDetails = {};
if (!tryReportingItemError([&] //throw X
{
- targetDetails = GetShortcutTargetDetails({ gdriveLogin_, afsItemPath }, item.details)().target; //throw FileError
+ targetDetails = GetShortcutTargetDetails({gdriveLogin_, afsItemPath}, item.details)().target; //throw FileError
}, cb, itemName))
continue;
if (targetDetails.type == GdriveItemType::folder)
{
- if (std::shared_ptr<AFS::TraverserCallback> cbSub = cb.onFolder({ itemName, true /*isFollowedSymlink*/ })) //throw X
- workload_.push_back({ afsItemPath, std::move(cbSub) });
+ if (std::shared_ptr<AFS::TraverserCallback> cbSub = cb.onFolder({itemName, true /*isFollowedSymlink*/})) //throw X
+ workload_.push_back({afsItemPath, std::move(cbSub)});
}
else //a file or named pipe, etc.
- cb.onFile({ itemName, targetDetails.fileSize, targetDetails.modTime, item.details.targetId, true /*isFollowedSymlink*/ }); //throw X
+ cb.onFile({itemName, targetDetails.fileSize, targetDetails.modTime, getGdriveFilePrint(item.details.targetId), true /*isFollowedSymlink*/}); //throw X
}
break;
- case AFS::TraverserCallback::LINK_SKIP:
+ case AFS::TraverserCallback::HandleLink::skip:
break;
}
break;
@@ -2792,7 +2802,7 @@ void gdriveTraverseFolderRecursive(const GdriveLogin& gdriveLogin, const std::ve
struct InputStreamGdrive : public AFS::InputStream
{
- InputStreamGdrive(const GdrivePath& gdrivePath, const IOCallback& notifyUnbufferedIO /*throw X*/) :
+ InputStreamGdrive(const GdrivePath& gdrivePath, const IoCallback& notifyUnbufferedIO /*throw X*/) :
gdrivePath_(gdrivePath),
notifyUnbufferedIO_(notifyUnbufferedIO)
{
@@ -2854,7 +2864,7 @@ struct InputStreamGdrive : public AFS::InputStream
const auto& [itemId, itemDetails] = fileState.getFileAttributes(gdrivePath_.gdriveLogin.sharedDriveName, gdrivePath_.itemPath, true /*followLeafShortcut*/); //throw SysError
attr.modTime = itemDetails.modTime;
attr.fileSize = itemDetails.fileSize;
- attr.fileId = itemId;
+ attr.filePrint = getGdriveFilePrint(itemId);
});
}
catch (const SysError& e) { throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(getGdriveDisplayPath(gdrivePath_))), e.toString()); }
@@ -2870,7 +2880,7 @@ private:
}
const GdrivePath gdrivePath_;
- const IOCallback notifyUnbufferedIO_; //throw X
+ const IoCallback notifyUnbufferedIO_; //throw X
int64_t totalBytesReported_ = 0;
std::shared_ptr<AsyncStreamBuffer> asyncStreamIn_ = std::make_shared<AsyncStreamBuffer>(GDRIVE_STREAM_BUFFER_SIZE);
InterruptibleThread worker_;
@@ -2884,12 +2894,12 @@ struct OutputStreamGdrive : public AFS::OutputStreamImpl
OutputStreamGdrive(const GdrivePath& gdrivePath, //throw SysError
std::optional<uint64_t> /*streamSize*/,
std::optional<time_t> modTime,
- const IOCallback& notifyUnbufferedIO /*throw X*/,
+ const IoCallback& notifyUnbufferedIO /*throw X*/,
std::unique_ptr<PathAccessLock>&& pal) :
notifyUnbufferedIO_(notifyUnbufferedIO)
{
- std::promise<AFS::FileId> pFileId;
- futFileId_ = pFileId.get_future();
+ std::promise<AFS::FingerPrint> pFilePrint;
+ futFilePrint_ = pFilePrint.get_future();
//CAVEAT: if file is already existing, OutputStreamGdrive *constructor* must fail, not OutputStreamGdrive::write(),
// otherwise ~OutputStreamImpl() will delete the already existing file! => don't check asynchronously!
@@ -2908,10 +2918,10 @@ struct OutputStreamGdrive : public AFS::OutputStreamImpl
});
worker_ = InterruptibleThread([gdrivePath, modTime, fileName, asyncStreamIn = this->asyncStreamOut_,
- pFileId = std::move(pFileId),
- parentId = std::move(parentId),
- aai = std::move(aai),
- pal = std::move(pal)]() mutable
+ pFilePrint = std::move(pFilePrint),
+ parentId = std::move(parentId),
+ aai = std::move(aai),
+ pal = std::move(pal)]() mutable
{
assert(pal); //bind life time to worker thread!
setCurrentThreadName(Zstr("Ostream[Gdrive] ") + utfTo<Zstring>(getGdriveDisplayPath(gdrivePath)));
@@ -2946,14 +2956,14 @@ struct OutputStreamGdrive : public AFS::OutputStreamImpl
fileState.notifyItemCreated(aai.stateDelta, newFileItem);
});
- pFileId.set_value(fileIdNew);
+ pFilePrint.set_value(getGdriveFilePrint(fileIdNew));
}
catch (const SysError& e)
{
FileError fe(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(getGdriveDisplayPath(gdrivePath))), e.toString());
const std::exception_ptr exptr = std::make_exception_ptr(std::move(fe));
asyncStreamIn->setReadError(exptr); //set both!
- pFileId.set_exception(exptr); //
+ pFilePrint.set_exception(exptr); //
}
//let ThreadStopRequest pass through!
});
@@ -2974,7 +2984,7 @@ struct OutputStreamGdrive : public AFS::OutputStreamImpl
{
asyncStreamOut_->closeStream();
- while (futFileId_.wait_for(std::chrono::milliseconds(50)) == std::future_status::timeout)
+ while (futFilePrint_.wait_for(std::chrono::milliseconds(50)) == std::future_status::timeout)
reportBytesProcessed(); //throw X
reportBytesProcessed(); //[!] once more, now that *all* bytes were written
@@ -2982,8 +2992,8 @@ struct OutputStreamGdrive : public AFS::OutputStreamImpl
//--------------------------------------------------------------------
AFS::FinalizeResult result;
- assert(isReady(futFileId_));
- result.fileId = futFileId_.get(); //throw FileError
+ assert(isReady(futFilePrint_));
+ result.filePrint = futFilePrint_.get(); //throw FileError
//result.errorModTime -> already (successfully) set during file creation
return result;
}
@@ -2996,11 +3006,11 @@ private:
totalBytesReported_ = totalBytesUploaded;
}
- const IOCallback notifyUnbufferedIO_; //throw X
+ const IoCallback notifyUnbufferedIO_; //throw X
int64_t totalBytesReported_ = 0;
std::shared_ptr<AsyncStreamBuffer> asyncStreamOut_ = std::make_shared<AsyncStreamBuffer>(GDRIVE_STREAM_BUFFER_SIZE);
InterruptibleThread worker_;
- std::future<AFS::FileId> futFileId_;
+ std::future<AFS::FingerPrint> futFilePrint_;
};
//==========================================================================================
@@ -3012,8 +3022,29 @@ public:
const GdriveLogin& getGdriveLogin() const { return gdriveLogin_; }
+ Zstring getFolderUrl(const AfsPath& folderPath) const //throw FileError
+ {
+ try
+ {
+ GdriveFileState::PathStatus ps;
+ accessGlobalFileState(gdriveLogin_.email, [&](GdriveFileState& fileState) //throw SysError
+ {
+ ps = fileState.getPathStatus(gdriveLogin_.sharedDriveName, folderPath, true /*followLeafShortcut*/); //throw SysError
+ });
+
+ if (!ps.relPath.empty())
+ throw FileError(replaceCpy(_("Cannot find %x."), L"%x", fmtPath(getDisplayPath(folderPath))));
+
+ if (ps.existingType != GdriveItemType::folder)
+ throw SysError(replaceCpy<std::wstring>(L"%x is not a folder.", L"%x", fmtPath(getItemName(folderPath))));
+
+ return Zstr("https://drive.google.com/drive/folders/") + utfTo<Zstring>(ps.existingItemId);
+ }
+ catch (const SysError& e) { throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(getDisplayPath(folderPath))), e.toString()); }
+ }
+
private:
- GdrivePath getGdrivePath(const AfsPath& afsPath) const { return { gdriveLogin_, afsPath }; }
+ GdrivePath getGdrivePath(const AfsPath& afsPath) const { return {gdriveLogin_, afsPath}; }
GdriveRawPath getGdriveRawPath(const AfsPath& afsPath) const //throw SysError
{
@@ -3224,7 +3255,7 @@ private:
//----------------------------------------------------------------------------------------------------------------
//return value always bound:
- std::unique_ptr<InputStream> getInputStream(const AfsPath& afsPath, const IOCallback& notifyUnbufferedIO /*throw X*/) const override //throw FileError, (ErrorFileLocked)
+ std::unique_ptr<InputStream> getInputStream(const AfsPath& afsPath, const IoCallback& notifyUnbufferedIO /*throw X*/) const override //throw FileError, (ErrorFileLocked)
{
return std::make_unique<InputStreamGdrive>(getGdrivePath(afsPath), notifyUnbufferedIO);
}
@@ -3234,7 +3265,7 @@ private:
std::unique_ptr<OutputStreamImpl> getOutputStream(const AfsPath& afsPath, //throw FileError
std::optional<uint64_t> streamSize,
std::optional<time_t> modTime,
- const IOCallback& notifyUnbufferedIO /*throw X*/) const override
+ const IoCallback& notifyUnbufferedIO /*throw X*/) const override
{
try
{
@@ -3262,7 +3293,7 @@ private:
//already existing: undefined behavior! (e.g. fail/overwrite/auto-rename)
//=> actual behavior: 1. fails or 2. creates duplicate (unlikely)
FileCopyResult copyFileForSameAfsType(const AfsPath& afsSource, const StreamAttributes& attrSource, //throw FileError, (ErrorFileLocked), (X)
- const AbstractPath& apTarget, bool copyFilePermissions, const IOCallback& notifyUnbufferedIO /*throw X*/) const override
+ const AbstractPath& apTarget, bool copyFilePermissions, const IoCallback& notifyUnbufferedIO /*throw X*/) const override
{
//no native Google Drive file copy => use stream-based file copy:
if (copyFilePermissions)
@@ -3325,10 +3356,10 @@ private:
});
FileCopyResult result;
- result.fileSize = fileSize;
- result.modTime = modTime;
- result.sourceFileId = itemIdSrc;
- result.targetFileId = fileIdTrg;
+ result.fileSize = fileSize;
+ result.modTime = modTime;
+ result.sourceFilePrint = getGdriveFilePrint(itemIdSrc);
+ result.targetFilePrint = getGdriveFilePrint(fileIdTrg);
/*result.errorModTime = */
return result;
}
@@ -3597,7 +3628,7 @@ void fff::gdriveRemoveUser(const std::string& accountEmail) //throw FileError
throw SysError(formatSystemError("gdriveRemoveUser", L"", L"Function call not allowed during init/shutdown."));
}
- catch (const SysError& e) { throw FileError(replaceCpy(_("Unable to disconnect from %x."), L"%x", fmtPath(getGdriveDisplayPath({{ accountEmail, Zstr("")}, AfsPath() }))), e.toString()); }
+ catch (const SysError& e) { throw FileError(replaceCpy(_("Unable to disconnect from %x."), L"%x", fmtPath(getGdriveDisplayPath({{accountEmail, Zstr("")}, AfsPath()}))), e.toString()); }
}
@@ -3625,7 +3656,7 @@ std::vector<Zstring /*sharedDriveName*/> fff::gdriveListSharedDrives(const std::
});
return sharedDriveNames;
}
- catch (const SysError& e) { throw FileError(replaceCpy(_("Unable to access %x."), L"%x", fmtPath(getGdriveDisplayPath({{ accountEmail, Zstr("")}, AfsPath() }))), e.toString()); }
+ catch (const SysError& e) { throw FileError(replaceCpy(_("Unable to access %x."), L"%x", fmtPath(getGdriveDisplayPath({{accountEmail, Zstr("")}, AfsPath()}))), e.toString()); }
}
@@ -3649,6 +3680,15 @@ GdriveLogin fff::extractGdriveLogin(const AfsDevice& afsDevice) //noexcept
}
+Zstring fff::getGoogleDriveFolderUrl(const AbstractPath& folderPath) //throw FileError
+{
+ if (const auto gdriveDevice = dynamic_cast<const GdriveFileSystem*>(&folderPath.afsDevice.ref()))
+ return gdriveDevice->getFolderUrl(folderPath.afsPath); //throw FileError
+
+ return {};
+}
+
+
bool fff::acceptsItemPathPhraseGdrive(const Zstring& itemPathPhrase) //noexcept
{
Zstring path = expandMacros(itemPathPhrase); //expand before trimming!
diff --git a/FreeFileSync/Source/afs/gdrive.h b/FreeFileSync/Source/afs/gdrive.h
index abca18ad..75bb4a80 100644
--- a/FreeFileSync/Source/afs/gdrive.h
+++ b/FreeFileSync/Source/afs/gdrive.h
@@ -33,6 +33,9 @@ struct GdriveLogin
};
AfsDevice condenseToGdriveDevice(const GdriveLogin& login); //noexcept; potentially messy user input
GdriveLogin extractGdriveLogin(const AfsDevice& afsDevice); //noexcept
+
+//return empty, if not a Google Drive path
+Zstring getGoogleDriveFolderUrl(const AbstractPath& folderPath); //throw FileError
}
#endif //FS_GDRIVE_9238425018342701356
diff --git a/FreeFileSync/Source/afs/native.cpp b/FreeFileSync/Source/afs/native.cpp
index 2e078378..9f677d36 100644
--- a/FreeFileSync/Source/afs/native.cpp
+++ b/FreeFileSync/Source/afs/native.cpp
@@ -8,7 +8,6 @@
#include <zen/file_access.h>
#include <zen/symlink_target.h>
#include <zen/file_io.h>
-#include <zen/file_id_def.h>
#include <zen/stl_tools.h>
#include <zen/resolve_path.h>
#include <zen/recycler.h>
@@ -18,6 +17,7 @@
#include "abstract_impl.h"
#include "../base/icon_loader.h"
+ #include <sys/vfs.h> //statfs
#include <sys/stat.h>
#include <dirent.h>
@@ -30,6 +30,7 @@ using AFS = AbstractFileSystem;
namespace
{
+
void initComForThread() //throw FileError
{
}
@@ -37,34 +38,59 @@ void initComForThread() //throw FileError
//====================================================================================================
//====================================================================================================
+//persistent + unique (relative to volume) or 0!
inline
-AFS::FileId convertToAbstractFileId(const zen::FileId& fid)
+AFS::FingerPrint getFileFingerprint(FileIndex fileIndex)
{
- if (fid == zen::FileId())
- return AFS::FileId();
-
- AFS::FileId out(reinterpret_cast<const char*>(&fid.volumeId), sizeof(fid.volumeId));
- return out.append(reinterpret_cast<const char*>(&fid.fileIndex), sizeof(fid.fileIndex));
+ static_assert(sizeof(fileIndex) == sizeof(AFS::FingerPrint));
+ return fileIndex; //== 0 if not supported
+ /* File details
+ ------------
+ st_mtim (Linux)
+ st_mtimespec (macOS): nanosecond-precision for improved uniqueness?
+ => essentially unknown after file copy (e.g. to FAT) without extra directory traversal :(
+
+ macOS st_birthtimespec: "if not supported by file system, holds the ctime instead"
+ ctime: inode modification time => changed on* rename*! => FU...
+
+ Volume details
+ --------------
+ st_dev: "st_dev value is not necessarily consistent across reboots or system crashes" https://freefilesync.org/forum/viewtopic.php?t=8054
+ only locally unique and depends on device mount point! => FU...
+
+ f_fsid: "Some operating systems use the device number..." => fuck!
+ "Several OSes restrict giving out the f_fsid field to the superuser only"
+
+ f_bsize macOS: "fundamental file system block size"
+ Linux: "optimal transfer block size" -> no! for all intents and purposes this *is* the "fundamental file system block size": https://stackoverflow.com/a/54835515
+ f_blocks => meh...
+
+ f_type Linux: documented values, nice! https://linux.die.net/man/2/statfs
+ macOS: - not stable between macOS releases: https://developer.apple.com/forums/thread/87745
+ - Apple docs say: "generally not a useful value"
+ - f_fstypename can be used as alternative
+
+ DADiskGetBSDName(): macOS only */
}
struct NativeFileInfo
{
- time_t modTime;
+ FileTimeNative modTime;
uint64_t fileSize;
- FileId fileId; //optional
+ FileIndex fileIndex;
};
NativeFileInfo getFileAttributes(FileBase::FileHandle fh) //throw SysError
{
- struct ::stat fileAttr = {};
- if (::fstat(fh, &fileAttr) != 0)
+ struct stat fileInfo = {};
+ if (::fstat(fh, &fileInfo) != 0)
THROW_LAST_SYS_ERROR("fstat");
return
{
- fileAttr.st_mtime,
- makeUnsigned(fileAttr.st_size),
- generateFileId(fileAttr)
+ fileInfo.st_mtim,
+ fileInfo.st_ino,
+ makeUnsigned(fileInfo.st_size)
};
}
@@ -94,7 +120,7 @@ std::vector<FsItem> getDirContentFlat(const Zstring& dirPath) //throw FileError
macOS: - libc: readdir thread-safe already in code from 2000: https://opensource.apple.com/source/Libc/Libc-166/gen.subproj/readdir.c.auto.html
- and in the latest version from 2017: https://opensource.apple.com/source/Libc/Libc-1244.30.3/gen/FreeBSD/readdir.c.auto.html */
errno = 0;
- const struct ::dirent* dirEntry = ::readdir(folder);
+ const dirent* dirEntry = ::readdir(folder);
if (!dirEntry)
{
if (errno == 0) //errno left unchanged => no more items
@@ -114,7 +140,7 @@ std::vector<FsItem> getDirContentFlat(const Zstring& dirPath) //throw FileError
if (itemNameRaw[0] == 0) //show error instead of endless recursion!!!
throw FileError(replaceCpy(_("Cannot read directory %x."), L"%x", fmtPath(dirPath)), formatSystemError("readdir", L"", L"Folder contains an item without name."));
- output.push_back({ itemNameRaw });
+ output.push_back({itemNameRaw});
/* Unicode normalization is file-system-dependent:
@@ -150,32 +176,42 @@ struct FsItemDetails
ItemType type;
time_t modTime; //number of seconds since Jan. 1st 1970 UTC
uint64_t fileSize; //unit: bytes!
- FileId fileId;
+ AFS::FingerPrint filePrint;
};
FsItemDetails getItemDetails(const Zstring& itemPath) //throw FileError
{
- struct ::stat statData = {};
- if (::lstat(itemPath.c_str(), &statData) != 0) //lstat() does not resolve symlinks
+ struct stat itemInfo = {};
+ if (::lstat(itemPath.c_str(), &itemInfo) != 0) //lstat() does not resolve symlinks
THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(itemPath)), "lstat");
- return { S_ISLNK(statData.st_mode) ? ItemType::symlink : //on Linux there is no distinction between file and directory symlinks!
- /**/ (S_ISDIR(statData.st_mode) ? ItemType::folder : ItemType::file), //a file or named pipe, etc. => dont't check using S_ISREG(): see comment in file_traverser.cpp
- statData.st_mtime,
- makeUnsigned(statData.st_size),
- generateFileId(statData) };
+ return {S_ISLNK(itemInfo.st_mode) ? ItemType::symlink : //on Linux there is no distinction between file and directory symlinks!
+ /**/ (S_ISDIR(itemInfo.st_mode) ? ItemType::folder : ItemType::file), //a file or named pipe, etc. => dont't check using S_ISREG(): see comment in file_traverser.cpp
+ itemInfo.st_mtime,
+ makeUnsigned(itemInfo.st_size),
+ getFileFingerprint(itemInfo.st_ino)};
}
FsItemDetails getSymlinkTargetDetails(const Zstring& linkPath) //throw FileError
{
- struct ::stat statData = {};
- if (::stat(linkPath.c_str(), &statData) != 0)
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtPath(linkPath)), "stat");
-
- return { S_ISDIR(statData.st_mode) ? ItemType::folder : ItemType::file,
- statData.st_mtime,
- makeUnsigned(statData.st_size),
- generateFileId(statData) };
+ try
+ {
+ struct stat itemInfo = {};
+ if (::stat(linkPath.c_str(), &itemInfo) != 0)
+ THROW_LAST_SYS_ERROR("stat");
+
+ const ItemType targetType = S_ISDIR(itemInfo.st_mode) ? ItemType::folder : ItemType::file;
+
+ const AFS::FingerPrint filePrint = targetType == ItemType::folder ? 0 : getFileFingerprint(itemInfo.st_ino);
+ return {targetType,
+ itemInfo.st_mtime,
+ makeUnsigned(itemInfo.st_size),
+ filePrint};
+ }
+ catch (const SysError& e)
+ {
+ throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtPath(linkPath)), e.toString());
+ }
}
@@ -185,7 +221,7 @@ public:
SingleFolderTraverser(const std::vector<std::pair<Zstring, std::shared_ptr<AFS::TraverserCallback>>>& workload /*throw X*/)
{
for (const auto& [folderPath, cb] : workload)
- workload_.push_back({ folderPath, cb });
+ workload_.push_back({folderPath, cb});
while (!workload_.empty())
{
@@ -219,18 +255,18 @@ private:
switch (itemDetails.type)
{
case ItemType::file:
- cb.onFile({ itemName, itemDetails.fileSize, itemDetails.modTime, convertToAbstractFileId(itemDetails.fileId), false /*isFollowedSymlink*/ }); //throw X
+ cb.onFile({itemName, itemDetails.fileSize, itemDetails.modTime, itemDetails.filePrint, false /*isFollowedSymlink*/}); //throw X
break;
case ItemType::folder:
- if (std::shared_ptr<AFS::TraverserCallback> cbSub = cb.onFolder({ itemName, false /*isFollowedSymlink*/ })) //throw X
- workload_.push_back({ itemPath, std::move(cbSub) });
+ if (std::shared_ptr<AFS::TraverserCallback> cbSub = cb.onFolder({itemName, false /*isFollowedSymlink*/})) //throw X
+ workload_.push_back({itemPath, std::move(cbSub)});
break;
case ItemType::symlink:
- switch (cb.onSymlink({ itemName, itemDetails.modTime })) //throw X
+ switch (cb.onSymlink({itemName, itemDetails.modTime})) //throw X
{
- case AFS::TraverserCallback::LINK_FOLLOW:
+ case AFS::TraverserCallback::HandleLink::follow:
{
FsItemDetails targetDetails = {};
if (!tryReportingItemError([&] //throw X
@@ -241,15 +277,15 @@ private:
if (targetDetails.type == ItemType::folder)
{
- if (std::shared_ptr<AFS::TraverserCallback> cbSub = cb.onFolder({ itemName, true /*isFollowedSymlink*/ })) //throw X
- workload_.push_back({ itemPath, std::move(cbSub) });
+ if (std::shared_ptr<AFS::TraverserCallback> cbSub = cb.onFolder({itemName, true /*isFollowedSymlink*/})) //throw X
+ workload_.push_back({itemPath, std::move(cbSub)}); //symlink may link to different volume!
}
else //a file or named pipe, etc.
- cb.onFile({ itemName, targetDetails.fileSize, targetDetails.modTime, convertToAbstractFileId(targetDetails.fileId), true /*isFollowedSymlink*/ }); //throw X
+ cb.onFile({itemName, targetDetails.fileSize, targetDetails.modTime, targetDetails.filePrint, true /*isFollowedSymlink*/}); //throw X
}
break;
- case AFS::TraverserCallback::LINK_SKIP:
+ case AFS::TraverserCallback::HandleLink::skip:
break;
}
break;
@@ -289,7 +325,7 @@ private:
struct InputStreamNative : public AFS::InputStream
{
- InputStreamNative(const Zstring& filePath, const IOCallback& notifyUnbufferedIO /*throw X*/) : fi_(filePath, notifyUnbufferedIO) {} //throw FileError, ErrorFileLocked
+ InputStreamNative(const Zstring& filePath, const IoCallback& notifyUnbufferedIO /*throw X*/) : fi_(filePath, notifyUnbufferedIO) {} //throw FileError, ErrorFileLocked
size_t read(void* buffer, size_t bytesToRead) override { return fi_.read(buffer, bytesToRead); } //throw FileError, ErrorFileLocked, X; return "bytesToRead" bytes unless end of stream!
size_t getBlockSize() const override { return fi_.getBlockSize(); } //non-zero block size is AFS contract!
@@ -297,13 +333,11 @@ struct InputStreamNative : public AFS::InputStream
{
try
{
- const NativeFileInfo fileInfo = getFileAttributes(fi_.getHandle()); //throw SysError
- return AFS::StreamAttributes(
- {
- fileInfo.modTime,
- fileInfo.fileSize,
- convertToAbstractFileId(fileInfo.fileId)
- });
+ const NativeFileInfo& fileInfo = getFileAttributes(fi_.getHandle()); //throw SysError
+
+ return AFS::StreamAttributes({nativeFileTimeToTimeT(fileInfo.modTime),
+ fileInfo.fileSize,
+ getFileFingerprint(fileInfo.fileIndex)});
}
catch (const SysError& e) { throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(fi_.getFilePath())), e.toString()); }
}
@@ -319,7 +353,7 @@ struct OutputStreamNative : public AFS::OutputStreamImpl
OutputStreamNative(const Zstring& filePath,
std::optional<uint64_t> streamSize,
std::optional<time_t> modTime,
- const IOCallback& notifyUnbufferedIO /*throw X*/) :
+ const IoCallback& notifyUnbufferedIO /*throw X*/) :
fo_(filePath, notifyUnbufferedIO), //throw FileError, ErrorTargetExisting
modTime_(modTime)
{
@@ -332,20 +366,22 @@ struct OutputStreamNative : public AFS::OutputStreamImpl
AFS::FinalizeResult finalize() override //throw FileError, X
{
AFS::FinalizeResult result;
- try
- {
- result.fileId = convertToAbstractFileId(getFileAttributes(fo_.getHandle()).fileId); //throw SysError
- }
- catch (const SysError& e) { throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(fo_.getFilePath())), e.toString()); }
+ if (modTime_)
+ try
+ {
+ const FileIndex fileIndex = getFileAttributes(fo_.getHandle()).fileIndex; //throw SysError
+ result.filePrint = getFileFingerprint(fileIndex);
+ }
+ catch (const SysError& e) { throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(fo_.getFilePath())), e.toString()); }
fo_.finalize(); //throw FileError, X
try
{
if (modTime_)
- setFileTime(fo_.getFilePath(), *modTime_, ProcSymlink::FOLLOW); //throw FileError
+ setFileTime(fo_.getFilePath(), *modTime_, ProcSymlink::follow); //throw FileError
/* is setting modtime after closing the file handle a pessimization?
- Native: no, needed for functional correctness, see file_access.cpp */
+ no, needed for functional correctness, see file_access.cpp */
}
catch (const FileError& e) { result.errorModTime = FileError(e.toString()); /*avoid slicing*/ }
@@ -364,11 +400,9 @@ class NativeFileSystem : public AbstractFileSystem
public:
NativeFileSystem(const Zstring& rootPath) : rootPath_(rootPath) {}
-private:
- Zstring getNativePath(const AfsPath& afsPath) const { return nativeAppendPaths(rootPath_, afsPath.value); }
-
- std::optional<Zstring> getNativeItemPath(const AfsPath& afsPath) const override { return getNativePath(afsPath); }
+ Zstring getNativePath(const AfsPath& afsPath) const { return isNullFileSystem() ? Zstring() : nativeAppendPaths(rootPath_, afsPath.value); }
+private:
Zstring getInitPathPhrase(const AfsPath& afsPath) const override
{
Zstring initPathPhrase = getNativePath(afsPath);
@@ -475,7 +509,7 @@ private:
//----------------------------------------------------------------------------------------------------------------
//return value always bound:
- std::unique_ptr<InputStream> getInputStream(const AfsPath& afsPath, const IOCallback& notifyUnbufferedIO /*throw X*/) const override //throw FileError, ErrorFileLocked
+ std::unique_ptr<InputStream> getInputStream(const AfsPath& afsPath, const IoCallback& notifyUnbufferedIO /*throw X*/) const override //throw FileError, ErrorFileLocked
{
initComForThread(); //throw FileError
return std::make_unique<InputStreamNative>(getNativePath(afsPath), notifyUnbufferedIO); //throw FileError, ErrorFileLocked
@@ -486,7 +520,7 @@ private:
std::unique_ptr<OutputStreamImpl> getOutputStream(const AfsPath& afsPath, //throw FileError
std::optional<uint64_t> streamSize,
std::optional<time_t> modTime,
- const IOCallback& notifyUnbufferedIO /*throw X*/) const override
+ const IoCallback& notifyUnbufferedIO /*throw X*/) const override
{
initComForThread(); //throw FileError
return std::make_unique<OutputStreamNative>(getNativePath(afsPath), streamSize, modTime, notifyUnbufferedIO); //throw FileError
@@ -509,7 +543,7 @@ private:
//already existing: undefined behavior! (e.g. fail/overwrite/auto-rename)
//=> actual behavior: fail with clear error message
FileCopyResult copyFileForSameAfsType(const AfsPath& afsSource, const StreamAttributes& attrSource, //throw FileError, ErrorFileLocked, X
- const AbstractPath& apTarget, bool copyFilePermissions, const IOCallback& notifyUnbufferedIO /*throw X*/) const override
+ const AbstractPath& apTarget, bool copyFilePermissions, const IoCallback& notifyUnbufferedIO /*throw X*/) const override
{
const Zstring nativePathTarget = static_cast<const NativeFileSystem&>(apTarget.afsDevice.ref()).getNativePath(apTarget.afsPath);
@@ -522,13 +556,14 @@ private:
catch (FileError&) {});
if (copyFilePermissions)
- copyItemPermissions(getNativePath(afsSource), nativePathTarget, ProcSymlink::FOLLOW); //throw FileError
+ copyItemPermissions(getNativePath(afsSource), nativePathTarget, ProcSymlink::follow); //throw FileError
FileCopyResult result;
- result.fileSize = nativeResult.fileSize;
- result.modTime = nativeResult.modTime;
- result.sourceFileId = convertToAbstractFileId(nativeResult.sourceFileId);
- result.targetFileId = convertToAbstractFileId(nativeResult.targetFileId);
+ result.fileSize = nativeResult.fileSize;
+ //caveat: modTime will be incorrect for file systems with imprecise file times, e.g. see FAT_FILE_TIME_PRECISION_SEC
+ result.modTime = nativeFileTimeToTimeT(nativeResult.sourceModTime);
+ result.sourceFilePrint = getFileFingerprint(nativeResult.sourceFileIdx);
+ result.targetFilePrint = getFileFingerprint(nativeResult.targetFileIdx);
result.errorModTime = nativeResult.errorModTime;
return result;
}
@@ -553,7 +588,7 @@ private:
tryCopyDirectoryAttributes(sourcePath, targetPath); //throw FileError
if (copyFilePermissions)
- copyItemPermissions(sourcePath, targetPath, ProcSymlink::FOLLOW); //throw FileError
+ copyItemPermissions(sourcePath, targetPath, ProcSymlink::follow); //throw FileError
}
//already existing: fail
@@ -568,7 +603,7 @@ private:
catch (FileError&) {});
if (copyFilePermissions)
- copyItemPermissions(getNativePath(afsSource), nativePathTarget, ProcSymlink::DIRECT); //throw FileError
+ copyItemPermissions(getNativePath(afsSource), nativePathTarget, ProcSymlink::direct); //throw FileError
}
//already existing: undefined behavior! (e.g. fail/overwrite)
@@ -662,11 +697,11 @@ void RecycleSessionNative::recycleItemIfExists(const AbstractPath& itemPath, con
{
assert(!startsWith(logicalRelPath, FILE_NAME_SEPARATOR));
- std::optional<Zstring> itemPathNative = AFS::getNativeItemPath(itemPath);
- if (!itemPathNative)
+ const Zstring& itemPathNative = getNativeItemPath(itemPath);
+ if (itemPathNative.empty())
throw std::logic_error("Contract violation! " + std::string(__FILE__) + ':' + numberTo<std::string>(__LINE__));
- recycleOrDeleteIfExists(*itemPathNative); //throw FileError
+ recycleOrDeleteIfExists(itemPathNative); //throw FileError
}
@@ -710,3 +745,11 @@ AbstractPath fff::createItemPathNativeNoFormatting(const Zstring& nativePath) //
else //broken path syntax
return AbstractPath(makeSharedRef<NativeFileSystem>(nativePath), AfsPath());
}
+
+
+Zstring fff::getNativeItemPath(const AbstractPath& ap)
+{
+ if (const auto nativeDevice = dynamic_cast<const NativeFileSystem*>(&ap.afsDevice.ref()))
+ return nativeDevice->getNativePath(ap.afsPath);
+ return {};
+}
diff --git a/FreeFileSync/Source/afs/native.h b/FreeFileSync/Source/afs/native.h
index 77896e91..24270e9f 100644
--- a/FreeFileSync/Source/afs/native.h
+++ b/FreeFileSync/Source/afs/native.h
@@ -14,7 +14,12 @@ namespace fff
bool acceptsItemPathPhraseNative(const Zstring& itemPathPhrase); //noexcept
AbstractPath createItemPathNative(const Zstring& itemPathPhrase); //noexcept
+//-------------------------------------------------------
+
AbstractPath createItemPathNativeNoFormatting(const Zstring& nativePath); //noexcept
+
+//return empty, if not a native path
+Zstring getNativeItemPath(const AbstractPath& ap);
}
#endif //FS_NATIVE_183247018532434563465
diff --git a/FreeFileSync/Source/afs/sftp.cpp b/FreeFileSync/Source/afs/sftp.cpp
index 325aa412..74dc1657 100644
--- a/FreeFileSync/Source/afs/sftp.cpp
+++ b/FreeFileSync/Source/afs/sftp.cpp
@@ -478,7 +478,7 @@ public:
int rc = LIBSSH2_ERROR_NONE;
try
{
- rc = sftpCommand({ sshSession_, sftpChannel }); //noexcept
+ rc = sftpCommand({sshSession_, sftpChannel}); //noexcept
}
catch (...) { assert(false); rc = LIBSSH2_ERROR_BAD_USE; }
@@ -563,16 +563,16 @@ public:
return; //time-out! => let next tryNonBlocking() call fail with detailed error!
const auto waitTimeMs = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - now).count();
- struct ::timeval tv = {};
+ timeval tv = {};
tv.tv_sec = static_cast<long>(waitTimeMs / 1000);
tv.tv_usec = static_cast<long>(waitTimeMs - tv.tv_sec * 1000) * 1000;
//WSAPoll is broken, ::poll() on macOS even worse: https://daniel.haxx.se/blog/2012/10/10/wsapoll-is-broken/
//perf: no significant difference compared to ::WSAPoll()
- const int rc = ::select(nfds + 1, //int nfds,
- readCount > 0 ? &readfds : nullptr, //fd_set* readfds,
- writeCount > 0 ? &writefds : nullptr, //fd_set* writefds,
- nullptr, //fd_set* exceptfds,
+ const int rc = ::select(nfds + 1, //int nfds
+ readCount > 0 ? &readfds : nullptr, //fd_set* readfds
+ writeCount > 0 ? &writefds : nullptr, //fd_set* writefds
+ nullptr, //fd_set* exceptfds
&tv); //struct timeval* timeout
if (rc == 0)
return; //time-out! => let next tryNonBlocking() call fail with detailed error!
@@ -738,7 +738,7 @@ public:
void init() //throw SysError, FatalSshError
{
if (session_->getSftpChannelCount() == 0) //make sure the SSH session contains at least one SFTP channel
- SshSession::addSftpChannel({ session_.get() }, timeoutSec_); //throw SysError, FatalSshError
+ SshSession::addSftpChannel({session_.get()}, timeoutSec_); //throw SysError, FatalSshError
}
//bool isHealthy() const { return session_->isHealthy(); }
@@ -753,7 +753,7 @@ public:
if (session_->tryNonBlocking(0, sftpCommandStartTime, functionName, sftpCommand, timeoutSec_)) //throw SysError, FatalSshError
return;
else //pending
- SshSession::waitForTraffic({ session_.get() }, timeoutSec_); //throw FatalSshError
+ SshSession::waitForTraffic({session_.get()}, timeoutSec_); //throw FatalSshError
}
private:
@@ -783,7 +783,7 @@ public:
if (session_->tryNonBlocking(channelNo, commandStartTime, functionName, sftpCommand, timeoutSec_)) //throw SysError, FatalSshError
return;
else //pending
- SshSession::waitForTraffic({ session_.get() }, timeoutSec_); //throw FatalSshError
+ SshSession::waitForTraffic({session_.get()}, timeoutSec_); //throw FatalSshError
}
catch (const SysError& ) { return; }
catch (const FatalSshError&) { return; }
@@ -1087,17 +1087,17 @@ std::vector<SftpItem> getDirContentFlat(const SftpLogin& login, const AfsPath& d
{
if ((attribs.flags & LIBSSH2_SFTP_ATTR_ACMODTIME) == 0) //server probably does not support these attributes => fail at folder level
throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(getSftpDisplayPath(login.server, itemPath))), L"Modification time not supported.");
- output.push_back({ itemName, { AFS::ItemType::symlink, 0, static_cast<time_t>(attribs.mtime) }});
+ output.push_back({itemName, {AFS::ItemType::symlink, 0, static_cast<time_t>(attribs.mtime)}});
}
else if (LIBSSH2_SFTP_S_ISDIR(attribs.permissions))
- output.push_back({ itemName, { AFS::ItemType::folder, 0, static_cast<time_t>(attribs.mtime) }});
+ output.push_back({itemName, {AFS::ItemType::folder, 0, static_cast<time_t>(attribs.mtime)}});
else //a file or named pipe, ect: LIBSSH2_SFTP_S_ISREG, LIBSSH2_SFTP_S_ISCHR, LIBSSH2_SFTP_S_ISBLK, LIBSSH2_SFTP_S_ISFIFO, LIBSSH2_SFTP_S_ISSOCK
{
if ((attribs.flags & LIBSSH2_SFTP_ATTR_ACMODTIME) == 0) //server probably does not support these attributes => fail at folder level
throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(getSftpDisplayPath(login.server, itemPath))), L"Modification time not supported.");
if ((attribs.flags & LIBSSH2_SFTP_ATTR_SIZE) == 0)
throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(getSftpDisplayPath(login.server, itemPath))), L"File size not supported.");
- output.push_back({ itemName, { AFS::ItemType::file, attribs.filesize, static_cast<time_t>(attribs.mtime) }});
+ output.push_back({itemName, {AFS::ItemType::file, attribs.filesize, static_cast<time_t>(attribs.mtime)}});
}
}
}
@@ -1117,7 +1117,7 @@ SftpItemDetails getSymlinkTargetDetails(const SftpLogin& login, const AfsPath& l
throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(getSftpDisplayPath(login.server, linkPath))), L"File attributes not available.");
if (LIBSSH2_SFTP_S_ISDIR(attribsTrg.permissions))
- return { AFS::ItemType::folder, 0, static_cast<time_t>(attribsTrg.mtime) };
+ return {AFS::ItemType::folder, 0, static_cast<time_t>(attribsTrg.mtime)};
else
{
if ((attribsTrg.flags & LIBSSH2_SFTP_ATTR_ACMODTIME) == 0) //server probably does not support these attributes => should fail at folder level!
@@ -1125,7 +1125,7 @@ SftpItemDetails getSymlinkTargetDetails(const SftpLogin& login, const AfsPath& l
if ((attribsTrg.flags & LIBSSH2_SFTP_ATTR_SIZE) == 0)
throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(getSftpDisplayPath(login.server, linkPath))), L"File size not supported.");
- return { AFS::ItemType::file, attribsTrg.filesize, static_cast<time_t>(attribsTrg.mtime) };
+ return {AFS::ItemType::file, attribsTrg.filesize, static_cast<time_t>(attribsTrg.mtime)};
}
}
@@ -1167,18 +1167,18 @@ private:
switch (item.details.type)
{
case AFS::ItemType::file:
- cb.onFile({ item.itemName, item.details.fileSize, item.details.modTime, AFS::FileId(), false /*isFollowedSymlink*/ }); //throw X
+ cb.onFile({item.itemName, item.details.fileSize, item.details.modTime, AFS::FingerPrint() /*not supported by SFTP*/, false /*isFollowedSymlink*/}); //throw X
break;
case AFS::ItemType::folder:
- if (std::shared_ptr<AFS::TraverserCallback> cbSub = cb.onFolder({ item.itemName, false /*isFollowedSymlink*/ })) //throw X
- workload_.push_back(WorkItem{ itemPath, std::move(cbSub) });
+ if (std::shared_ptr<AFS::TraverserCallback> cbSub = cb.onFolder({item.itemName, false /*isFollowedSymlink*/})) //throw X
+ workload_.push_back(WorkItem{itemPath, std::move(cbSub)});
break;
case AFS::ItemType::symlink:
- switch (cb.onSymlink({ item.itemName, item.details.modTime })) //throw X
+ switch (cb.onSymlink({item.itemName, item.details.modTime})) //throw X
{
- case AFS::TraverserCallback::LINK_FOLLOW:
+ case AFS::TraverserCallback::HandleLink::follow:
{
SftpItemDetails targetDetails = {};
if (!tryReportingItemError([&] //throw X
@@ -1189,15 +1189,15 @@ private:
if (targetDetails.type == AFS::ItemType::folder)
{
- if (std::shared_ptr<AFS::TraverserCallback> cbSub = cb.onFolder({ item.itemName, true /*isFollowedSymlink*/ })) //throw X
- workload_.push_back(WorkItem{ itemPath, std::move(cbSub) });
+ if (std::shared_ptr<AFS::TraverserCallback> cbSub = cb.onFolder({item.itemName, true /*isFollowedSymlink*/})) //throw X
+ workload_.push_back(WorkItem{itemPath, std::move(cbSub)});
}
else //a file or named pipe, etc.
- cb.onFile({ item.itemName, targetDetails.fileSize, targetDetails.modTime, AFS::FileId(), true /*isFollowedSymlink*/ }); //throw X
+ cb.onFile({item.itemName, targetDetails.fileSize, targetDetails.modTime, AFS::FingerPrint() /*not supported by SFTP*/, true /*isFollowedSymlink*/}); //throw X
}
break;
- case AFS::TraverserCallback::LINK_SKIP:
+ case AFS::TraverserCallback::HandleLink::skip:
break;
}
break;
@@ -1219,7 +1219,7 @@ void traverseFolderRecursiveSftp(const SftpLogin& login, const std::vector<std::
struct InputStreamSftp : public AFS::InputStream
{
- InputStreamSftp(const SftpLogin& login, const AfsPath& filePath, const IOCallback& notifyUnbufferedIO /*throw X*/) : //throw FileError
+ InputStreamSftp(const SftpLogin& login, const AfsPath& filePath, const IoCallback& notifyUnbufferedIO /*throw X*/) : //throw FileError
displayPath_(getSftpDisplayPath(login.server, filePath)),
notifyUnbufferedIO_(notifyUnbufferedIO)
{
@@ -1318,7 +1318,7 @@ private:
const std::wstring displayPath_;
LIBSSH2_SFTP_HANDLE* fileHandle_ = nullptr;
- const IOCallback notifyUnbufferedIO_; //throw X
+ const IoCallback notifyUnbufferedIO_; //throw X
std::shared_ptr<SftpSessionManager::SshSessionShared> session_;
std::vector<std::byte> memBuf_ = std::vector<std::byte>(getBlockSize());
@@ -1334,7 +1334,7 @@ struct OutputStreamSftp : public AFS::OutputStreamImpl
OutputStreamSftp(const SftpLogin& login, //throw FileError
const AfsPath& filePath,
std::optional<time_t> modTime,
- const IOCallback& notifyUnbufferedIO /*throw X*/) :
+ const IoCallback& notifyUnbufferedIO /*throw X*/) :
filePath_(filePath),
displayPath_(getSftpDisplayPath(login.server, filePath)),
modTime_(modTime),
@@ -1422,12 +1422,12 @@ struct OutputStreamSftp : public AFS::OutputStreamImpl
//it seems libssh2_sftp_fsetstat() triggers bugs on synology server => set mtime by path! https://freefilesync.org/forum/viewtopic.php?t=1281
AFS::FinalizeResult result;
- //result.fileId = ... -> not supported by SFTP
+ //result.filePrint = ... -> not supported by SFTP
try
{
setModTimeIfAvailable(); //throw FileError, follows symlinks
/* is setting modtime after closing the file handle a pessimization?
- SFTP: no, needed for functional correctness (synology server), just as for Native */
+ SFTP: no, needed for functional correctness (synology server), same as for Native */
}
catch (const FileError& e) { result.errorModTime = e; /*slicing?*/ }
@@ -1502,7 +1502,7 @@ private:
const std::wstring displayPath_;
LIBSSH2_SFTP_HANDLE* fileHandle_ = nullptr;
const std::optional<time_t> modTime_;
- const IOCallback notifyUnbufferedIO_; //throw X
+ const IoCallback notifyUnbufferedIO_; //throw X
std::shared_ptr<SftpSessionManager::SshSessionShared> session_;
std::vector<std::byte> memBuf_ = std::vector<std::byte>(getBlockSize());
@@ -1702,7 +1702,7 @@ private:
//----------------------------------------------------------------------------------------------------------------
//return value always bound:
- std::unique_ptr<InputStream> getInputStream(const AfsPath& afsPath, const IOCallback& notifyUnbufferedIO /*throw X*/) const override //throw FileError, (ErrorFileLocked)
+ std::unique_ptr<InputStream> getInputStream(const AfsPath& afsPath, const IoCallback& notifyUnbufferedIO /*throw X*/) const override //throw FileError, (ErrorFileLocked)
{
return std::make_unique<InputStreamSftp>(login_, afsPath, notifyUnbufferedIO); //throw FileError
}
@@ -1712,7 +1712,7 @@ private:
std::unique_ptr<OutputStreamImpl> getOutputStream(const AfsPath& afsPath, //throw FileError
std::optional<uint64_t> streamSize,
std::optional<time_t> modTime,
- const IOCallback& notifyUnbufferedIO /*throw X*/) const override
+ const IoCallback& notifyUnbufferedIO /*throw X*/) const override
{
return std::make_unique<OutputStreamSftp>(login_, afsPath, modTime, notifyUnbufferedIO); //throw FileError
}
@@ -1727,7 +1727,7 @@ private:
//symlink handling: follow
//already existing: undefined behavior! (e.g. fail/overwrite/auto-rename)
FileCopyResult copyFileForSameAfsType(const AfsPath& afsSource, const StreamAttributes& attrSource, //throw FileError, (ErrorFileLocked), X
- const AbstractPath& apTarget, bool copyFilePermissions, const IOCallback& notifyUnbufferedIO /*throw X*/) const override
+ const AbstractPath& apTarget, bool copyFilePermissions, const IoCallback& notifyUnbufferedIO /*throw X*/) const override
{
//no native SFTP file copy => use stream-based file copy:
if (copyFilePermissions)
@@ -1961,7 +1961,7 @@ int fff::getServerMaxChannelsPerConnection(const SftpLogin& login) //throw FileE
{
try
{
- SftpSessionManager::SshSessionExclusive::addSftpChannel({ exSession.get() }); //throw SysError, FatalSshError
+ SftpSessionManager::SshSessionExclusive::addSftpChannel({exSession.get()}); //throw SysError, FatalSshError
}
catch (const SysError& ) { if (exSession->getSftpChannelCount() == 0) throw; return static_cast<int>(exSession->getSftpChannelCount()); }
catch (const FatalSshError& e) { if (exSession->getSftpChannelCount() == 0) throw SysError(e.toString()); return static_cast<int>(exSession->getSftpChannelCount()); }
@@ -2012,7 +2012,7 @@ AbstractPath fff::createItemPathSftp(const Zstring& itemPathPhrase) //noexcept
auto it = std::find_if(fullPath.begin(), fullPath.end(), [](Zchar c) { return c == '/' || c == '\\'; });
const Zstring serverPort(fullPath.begin(), it);
- const AfsPath serverRelPath = sanitizeDeviceRelativePath({ it, fullPath.end() });
+ const AfsPath serverRelPath = sanitizeDeviceRelativePath({it, fullPath.end()});
login.server = beforeLast(serverPort, Zstr(':'), IfNotFoundReturn::all);
const Zstring port = afterLast(serverPort, Zstr(':'), IfNotFoundReturn::none);
diff --git a/FreeFileSync/Source/application.cpp b/FreeFileSync/Source/application.cpp
index c1ccd1d2..dc24ad19 100644
--- a/FreeFileSync/Source/application.cpp
+++ b/FreeFileSync/Source/application.cpp
@@ -92,8 +92,8 @@ bool Application::OnInit()
if (error)
throw SysError(formatGlibError("gtk_css_provider_load_from_path", error));
- ::gtk_style_context_add_provider_for_screen(::gdk_screen_get_default(), //GdkScreen* screen,
- GTK_STYLE_PROVIDER(provider), //GtkStyleProvider* provider,
+ ::gtk_style_context_add_provider_for_screen(::gdk_screen_get_default(), //GdkScreen* screen
+ GTK_STYLE_PROVIDER(provider), //GtkStyleProvider* provider
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); //guint priority
};
try
@@ -113,6 +113,18 @@ bool Application::OnInit()
#error unknown GTK version!
#endif
+ try
+ {
+ /* we're a GUI app: ignore SIGHUP when the parent terminal quits! (or process is killed!)
+ => the FFS launcher will still be killed => fine
+ => macOS: apparently not needed! interestingly the FFS launcher does receive SIGHUP and *is* killed */
+ if (sighandler_t oldHandler = ::signal(SIGHUP, SIG_IGN);
+ oldHandler == SIG_ERR)
+ THROW_LAST_SYS_ERROR("signal(SIGHUP)");
+ else assert(!oldHandler);
+ }
+ catch (const SysError& e) { std::cerr << utfTo<std::string>(e.toString()) << '\n'; }
+
//Windows User Experience Interaction Guidelines: tool tips should have 5s timeout, info tips no timeout => compromise:
wxToolTip::Enable(true); //yawn, a wxWidgets screw-up: wxToolTip::SetAutoPop is no-op if global tooltip window is not yet constructed: wxToolTip::Enable creates it
@@ -127,7 +139,7 @@ bool Application::OnInit()
}
catch (FileError&) { assert(false); }
- initAfs({ getResourceDirPf(), getConfigDirPathPf() }); //bonus: using FTP Gdrive implicitly inits OpenSSL (used in runSanityChecks() on Linux) alredy during globals init
+ initAfs({getResourceDirPf(), getConfigDirPathPf()}); //bonus: using FTP Gdrive implicitly inits OpenSSL (used in runSanityChecks() on Linux) alredy during globals init
Bind(wxEVT_QUERY_END_SESSION, [this](wxCloseEvent& event) { onQueryEndSession(event); }); //can veto
@@ -178,7 +190,7 @@ void Application::OnUnhandledException() //handles both wxApp::OnInit() + wxApp:
}
catch (const std::bad_alloc& e) //the only kind of exception we don't want crash dumps for
{
- logFatalError(e.what()); //it's not always possible to display a message box, e.g. corrupted stack, however low-level file output works!
+ logFatalError(utfTo<std::wstring>(e.what())); //it's not always possible to display a message box, e.g. corrupted stack, however low-level file output works!
const auto& titleFmt = copyStringTo<std::wstring>(wxTheApp->GetAppDisplayName()) + SPACED_DASH + _("An exception occurred");
std::cerr << utfTo<std::string>(titleFmt + SPACED_DASH) << e.what() << '\n';
@@ -212,7 +224,7 @@ void Application::launch(const std::vector<Zstring>& commandArgs)
auto notifyFatalError = [&](const std::wstring& msg, const std::wstring& title)
{
- logFatalError(utfTo<std::string>(msg));
+ logFatalError(msg);
//error handling strategy unknown and no sync log output available at this point!
auto titleFmt = copyStringTo<std::wstring>(wxTheApp->GetAppDisplayName()) + SPACED_DASH + title;
@@ -347,9 +359,9 @@ void Application::launch(const std::vector<Zstring>& commandArgs)
auto hasNonDefaultConfig = [](const LocalPairConfig& lpc)
{
- return lpc != LocalPairConfig{ lpc.folderPathPhraseLeft,
- lpc.folderPathPhraseRight,
- std::nullopt, std::nullopt, FilterConfig() };
+ return lpc != LocalPairConfig{lpc.folderPathPhraseLeft,
+ lpc.folderPathPhraseRight,
+ std::nullopt, std::nullopt, FilterConfig()};
};
auto replaceDirectories = [&](MainConfiguration& mainCfg)
@@ -371,8 +383,8 @@ void Application::launch(const std::vector<Zstring>& commandArgs)
mainCfg.firstPair.folderPathPhraseRight = dirPathPhrasePairs[0].second;
}
else
- mainCfg.additionalPairs.push_back({ dirPathPhrasePairs[i].first, dirPathPhrasePairs[i].second,
- std::nullopt, std::nullopt, FilterConfig() });
+ mainCfg.additionalPairs.push_back({dirPathPhrasePairs[i].first, dirPathPhrasePairs[i].second,
+ std::nullopt, std::nullopt, FilterConfig()});
}
return true;
};
@@ -516,8 +528,11 @@ void runBatchMode(const Zstring& globalConfigFilePath, const XmlBatchConfig& bat
{
if (showPopupAllowed)
showNotificationDialog(nullptr, DialogInfoType::error, PopupDialogCfg().setDetailInstructions(msg));
- else //"exit" or "ignore"
- logFatalError(utfTo<std::string>(msg));
+ else //"ignoreErrors" or BatchErrorHandling::cancel
+ {
+ logFatalError(msg);
+ std::cerr << utfTo<std::string>(wxTheApp->GetAppDisplayName() + SPACED_DASH + msg) << '\n'; //Linux, macOS
+ }
raiseExitCode(exitCode, rc);
};
@@ -669,7 +684,7 @@ void runBatchMode(const Zstring& globalConfigFilePath, const XmlBatchConfig& bat
case FinalRequest::none:
break;
case FinalRequest::switchGui: //open new top-level window *after* progress dialog is gone => run on main event loop
- MainDialog::create(globalConfigFilePath, &globalCfg, convertBatchToGui(batchCfg), { cfgFilePath }, true /*startComparison*/);
+ MainDialog::create(globalConfigFilePath, &globalCfg, convertBatchToGui(batchCfg), {cfgFilePath}, true /*startComparison*/);
break;
case FinalRequest::shutdown: //run *after* last sync stats were updated and saved! https://freefilesync.org/forum/viewtopic.php?t=5761
try
diff --git a/FreeFileSync/Source/base/algorithm.cpp b/FreeFileSync/Source/base/algorithm.cpp
index bcbba259..cf4a0ce9 100644
--- a/FreeFileSync/Source/base/algorithm.cpp
+++ b/FreeFileSync/Source/base/algorithm.cpp
@@ -120,9 +120,9 @@ private:
const CompareFileResult cat = file.getCategory();
//##################### schedule old temporary files for deletion ####################
- if (cat == FILE_LEFT_SIDE_ONLY && endsWith(file.getItemName<LEFT_SIDE>(), AFS::TEMP_FILE_ENDING))
+ if (cat == FILE_LEFT_SIDE_ONLY && endsWith(file.getItemName<SelectSide::left>(), AFS::TEMP_FILE_ENDING))
return file.setSyncDir(SyncDirection::left);
- else if (cat == FILE_RIGHT_SIDE_ONLY && endsWith(file.getItemName<RIGHT_SIDE>(), AFS::TEMP_FILE_ENDING))
+ else if (cat == FILE_RIGHT_SIDE_ONLY && endsWith(file.getItemName<SelectSide::right>(), AFS::TEMP_FILE_ENDING))
return file.setSyncDir(SyncDirection::right);
//####################################################################################
@@ -193,9 +193,9 @@ private:
const CompareDirResult cat = folder.getDirCategory();
//########### schedule abandoned temporary recycle bin directory for deletion ##########
- if (cat == DIR_LEFT_SIDE_ONLY && endsWith(folder.getItemName<LEFT_SIDE>(), AFS::TEMP_FILE_ENDING))
+ if (cat == DIR_LEFT_SIDE_ONLY && endsWith(folder.getItemName<SelectSide::left>(), AFS::TEMP_FILE_ENDING))
return setSyncDirectionRec(SyncDirection::left, folder); //
- else if (cat == DIR_RIGHT_SIDE_ONLY && endsWith(folder.getItemName<RIGHT_SIDE>(), AFS::TEMP_FILE_ENDING))
+ else if (cat == DIR_RIGHT_SIDE_ONLY && endsWith(folder.getItemName<SelectSide::right>(), AFS::TEMP_FILE_ENDING))
return setSyncDirectionRec(SyncDirection::right, folder); //don't recurse below!
//#######################################################################################
@@ -252,7 +252,7 @@ bool fff::allElementsEqual(const FolderComparison& folderCmp)
namespace
{
-template <SelectedSide side> inline
+template <SelectSide side> inline
bool matchesDbEntry(const FilePair& file, const InSyncFile* dbFile, const std::vector<unsigned int>& ignoreTimeShiftMinutes)
{
if (file.isEmpty<side>())
@@ -262,11 +262,10 @@ bool matchesDbEntry(const FilePair& file, const InSyncFile* dbFile, const std::v
const InSyncDescrFile& descrDb = SelectParam<side>::ref(dbFile->left, dbFile->right);
- //respect 2 second FAT/FAT32 precision! copying a file to a FAT32 drive changes it's modification date by up to 2 seconds
return //we're not interested in "fileTimeTolerance" here!
- sameFileTime(file.getLastWriteTime<side>(), descrDb.modTime, 2, ignoreTimeShiftMinutes) &&
+ sameFileTime(file.getLastWriteTime<side>(), descrDb.modTime, FAT_FILE_TIME_PRECISION_SEC, ignoreTimeShiftMinutes) &&
file.getFileSize<side>() == dbFile->fileSize;
- //note: we do *not* consider FileId here, but are only interested in *visual* changes. Consider user moving data to some other medium, this is not a change!
+ //note: we do *not* consider file ID here, but are only interested in *visual* changes. Consider user moving data to some other medium, this is not a change!
}
@@ -297,7 +296,7 @@ bool stillInSync(const InSyncFile& dbFile, CompareVariant compareVar, int fileTi
//--------------------------------------------------------------------
//check whether database entry and current item match: *irrespective* of current comparison settings
-template <SelectedSide side> inline
+template <SelectSide side> inline
bool matchesDbEntry(const SymlinkPair& symlink, const InSyncSymlink* dbSymlink, const std::vector<unsigned int>& ignoreTimeShiftMinutes)
{
if (symlink.isEmpty<side>())
@@ -307,8 +306,7 @@ bool matchesDbEntry(const SymlinkPair& symlink, const InSyncSymlink* dbSymlink,
const InSyncDescrLink& descrDb = SelectParam<side>::ref(dbSymlink->left, dbSymlink->right);
- //respect 2 second FAT/FAT32 precision! copying a file to a FAT32 drive changes its modification date by up to 2 seconds
- return sameFileTime(symlink.getLastWriteTime<side>(), descrDb.modTime, 2, ignoreTimeShiftMinutes);
+ return sameFileTime(symlink.getLastWriteTime<side>(), descrDb.modTime, FAT_FILE_TIME_PRECISION_SEC, ignoreTimeShiftMinutes);
}
@@ -337,7 +335,7 @@ bool stillInSync(const InSyncSymlink& dbLink, CompareVariant compareVar, int fil
//--------------------------------------------------------------------
//check whether database entry and current item match: *irrespective* of current comparison settings
-template <SelectedSide side> inline
+template <SelectSide side> inline
bool matchesDbEntry(const FolderPair& folder, const InSyncFolder* dbFolder)
{
const bool haveDbEntry = dbFolder && dbFolder->status != InSyncFolder::DIR_STATUS_STRAW_MAN;
@@ -368,6 +366,9 @@ private:
{
recurse(baseFolder, &dbFolder, &dbFolder);
+ purgeDuplicates< SelectSide::left>(filesL_, exLeftOnlyById_);
+ purgeDuplicates<SelectSide::right>(filesR_, exRightOnlyById_);
+
if ((!exLeftOnlyById_ .empty() || !exLeftOnlyByPath_ .empty()) &&
(!exRightOnlyById_.empty() || !exRightOnlyByPath_.empty()))
detectMovePairs(dbFolder);
@@ -377,40 +378,31 @@ private:
{
for (FilePair& file : hierObj.refSubFiles())
{
+ const AFS::FingerPrint filePrintL = file.getFilePrint< SelectSide::left>();
+ const AFS::FingerPrint filePrintR = file.getFilePrint<SelectSide::right>();
+
+ if (filePrintL != 0) filesL_.push_back(&file); //collect *all* prints for uniqueness check!
+ if (filePrintR != 0) filesR_.push_back(&file); //
+
auto getDbEntry = [](const InSyncFolder* dbFolder, const Zstring& fileName) -> const InSyncFile*
{
if (dbFolder)
- {
- auto it = dbFolder->files.find(fileName);
- if (it != dbFolder->files.end())
+ if (const auto it = dbFolder->files.find(fileName);
+ it != dbFolder->files.end())
return &it->second;
- }
return nullptr;
};
- const CompareFileResult cat = file.getCategory();
-
- if (cat == FILE_LEFT_SIDE_ONLY)
+ if (const CompareFileResult cat = file.getCategory();
+ cat == FILE_LEFT_SIDE_ONLY)
{
- if (const InSyncFile* dbEntry = getDbEntry(dbFolderL, file.getItemName<LEFT_SIDE>()))
+ if (const InSyncFile* dbEntry = getDbEntry(dbFolderL, file.getItemName<SelectSide::left>()))
exLeftOnlyByPath_.emplace(dbEntry, &file);
- else if (!file.getFileId<LEFT_SIDE>().empty())
- {
- const auto [it, inserted] = exLeftOnlyById_.emplace(file.getFileId<LEFT_SIDE>(), &file);
- if (!inserted) //duplicate file ID! NTFS hard link/symlink?
- it->second = nullptr;
- }
}
else if (cat == FILE_RIGHT_SIDE_ONLY)
{
- if (const InSyncFile* dbEntry = getDbEntry(dbFolderR, file.getItemName<RIGHT_SIDE>()))
+ if (const InSyncFile* dbEntry = getDbEntry(dbFolderR, file.getItemName<SelectSide::right>()))
exRightOnlyByPath_.emplace(dbEntry, &file);
- else if (!file.getFileId<RIGHT_SIDE>().empty())
- {
- const auto [it, inserted] = exRightOnlyById_.emplace(file.getFileId<RIGHT_SIDE>(), &file);
- if (!inserted) //duplicate file ID! NTFS hard link/symlink?
- it->second = nullptr;
- }
}
}
@@ -419,22 +411,59 @@ private:
auto getDbEntry = [](const InSyncFolder* dbFolder, const Zstring& folderName) -> const InSyncFolder*
{
if (dbFolder)
- {
- auto it = dbFolder->folders.find(folderName);
- if (it != dbFolder->folders.end())
+ if (const auto it = dbFolder->folders.find(folderName);
+ it != dbFolder->folders.end())
return &it->second;
- }
return nullptr;
};
- const InSyncFolder* dbEntryL = getDbEntry(dbFolderL, folder.getItemName<LEFT_SIDE>());
+ const InSyncFolder* dbEntryL = getDbEntry(dbFolderL, folder.getItemName<SelectSide::left>());
const InSyncFolder* dbEntryR = dbEntryL;
- if (dbFolderL != dbFolderR || getUnicodeNormalForm(folder.getItemName<LEFT_SIDE>()) != getUnicodeNormalForm(folder.getItemName<RIGHT_SIDE>()))
- dbEntryR = getDbEntry(dbFolderR, folder.getItemName<RIGHT_SIDE>());
+ if (dbFolderL != dbFolderR ||
+ getUnicodeNormalForm(folder.getItemName< SelectSide::left>()) !=
+ getUnicodeNormalForm(folder.getItemName<SelectSide::right>()))
+ dbEntryR = getDbEntry(dbFolderR, folder.getItemName<SelectSide::right>());
recurse(folder, dbEntryL, dbEntryR);
}
}
+ template <SelectSide side>
+ static void purgeDuplicates(std::vector<FilePair*>& files,
+ std::unordered_map<AFS::FingerPrint, FilePair*>& exOneSideById)
+ {
+ if (!files.empty())
+ {
+ std::sort(files.begin(), files.end(), [](const FilePair* lhs, const FilePair* rhs)
+ { return lhs->getFilePrint<side>() < rhs->getFilePrint<side>(); });
+
+ AFS::FingerPrint prevPrint = files[0]->getFilePrint<side>();
+
+ for (auto it = files.begin() + 1; it != files.end(); ++it)
+ if (const AFS::FingerPrint filePrint = (*it)->getFilePrint<side>();
+ prevPrint != filePrint)
+ prevPrint = filePrint;
+ else //duplicate file ID! NTFS hard link/symlink?
+ {
+ const auto dupFirst = it - 1;
+ const auto dupLast = std::find_if(it + 1, files.end(), [prevPrint](const FilePair* file)
+ { return file->getFilePrint<side>() != prevPrint; });
+
+ //remove from model: do *not* store invalid file prints in sync.ffs_db!
+ std::for_each(dupFirst, dupLast, [](FilePair* file) { file->clearFilePrint<side>(); });
+ it = dupLast - 1;
+ }
+
+ //collect unique file prints for files existing on one side only:
+ constexpr CompareFileResult oneSideOnlyTag = side == SelectSide::left ? FILE_LEFT_SIDE_ONLY : FILE_RIGHT_SIDE_ONLY;
+
+ for (FilePair* file : files)
+ if (file->getCategory() == oneSideOnlyTag)
+ if (const AFS::FingerPrint filePrint = file->getFilePrint<side>();
+ filePrint != 0) //skip duplicates marked by clearFilePrint()
+ exOneSideById.emplace(filePrint, file);
+ }
+ }
+
void detectMovePairs(const InSyncFolder& container) const
{
for (const auto& [fileName, dbAttrib] : container.files)
@@ -444,93 +473,99 @@ private:
detectMovePairs(subFolder);
}
- template <SelectedSide side>
+ template <SelectSide side>
static bool sameSizeAndDate(const FilePair& file, const InSyncFile& dbFile)
{
return file.getFileSize<side>() == dbFile.fileSize &&
- sameFileTime(file.getLastWriteTime<side>(), SelectParam<side>::ref(dbFile.left, dbFile.right).modTime, 2, {});
- //- respect 2 second FAT/FAT32 precision! not user-configurable!
- //- "ignoreTimeShiftMinutes" may lead to false positive move detections => let's be conservative and not allow it
- // (time shift is only ever required during FAT DST switches)
-
- //PS: *never* allow 2 sec tolerance as container predicate!!
- // => no strict weak ordering relation! reason: no transitivity of equivalence!
+ file.getLastWriteTime<side>() == SelectParam<side>::ref(dbFile.left, dbFile.right).modTime;
+ /* do NOT consider FAT_FILE_TIME_PRECISION_SEC:
+ 1. if DB contains file metadata collected during folder comparison we can be as precise as we want here
+ 2. if DB contains file metadata *estimated* directly after file copy:
+ - most file systems store file times with sub-second precision...
+ - ...except for FAT, but FAT does not have stable file IDs after file copy anyway (see comment below)
+ => file time comparison with seconds precision is fine!
+
+ PS: *never* allow a tolerance as container predicate!!
+ => no strict weak ordering relation! reason: no transitivity of equivalence! */
}
- template <SelectedSide side>
+ template <SelectSide side>
FilePair* getAssocFilePair(const InSyncFile& dbFile) const
{
- const std::unordered_map<AFS::FileId, FilePair*>& exOneSideById = SelectParam<side>::ref(exLeftOnlyById_, exRightOnlyById_);
const std::unordered_map<const InSyncFile*, FilePair*>& exOneSideByPath = SelectParam<side>::ref(exLeftOnlyByPath_, exRightOnlyByPath_);
- {
- auto it = exOneSideByPath.find(&dbFile);
- if (it != exOneSideByPath.end())
- return it->second; //if there is an association by path, don't care if there is also an association by id,
- //even if the association by path doesn't match time and size while the association by id does!
- //- there doesn't seem to be (any?) value in allowing this!
- //- note: exOneSideById isn't filled in this case, see recurse()
- }
+ const std::unordered_map<AFS::FingerPrint, FilePair*>& exOneSideById = SelectParam<side>::ref(exLeftOnlyById_, exRightOnlyById_);
+
+ if (const auto it = exOneSideByPath.find(&dbFile);
+ it != exOneSideByPath.end())
+ return it->second; //if there is an association by path, don't care if there is also an association by ID,
+ //even if the association by path doesn't match time and size while the association by ID does!
+ //there doesn't seem to be (any?) value in allowing this!
+
+ if (const AFS::FingerPrint filePrint = SelectParam<side>::ref(dbFile.left, dbFile.right).filePrint;
+ filePrint != 0)
+ if (const auto it = exOneSideById.find(filePrint);
+ it != exOneSideById.end())
+ return it->second;
- const AFS::FileId fileId = SelectParam<side>::ref(dbFile.left, dbFile.right).fileId;
- if (!fileId.empty())
- {
- auto it = exOneSideById.find(fileId);
- if (it != exOneSideById.end())
- return it->second; //= nullptr, if duplicate ID!
- }
return nullptr;
}
void findAndSetMovePair(const InSyncFile& dbFile) const
{
if (stillInSync(dbFile, cmpVar_, fileTimeTolerance_, ignoreTimeShiftMinutes_))
- if (FilePair* fileLeftOnly = getAssocFilePair<LEFT_SIDE>(dbFile))
- if (sameSizeAndDate<LEFT_SIDE>(*fileLeftOnly, dbFile))
- if (FilePair* fileRightOnly = getAssocFilePair<RIGHT_SIDE>(dbFile))
- if (sameSizeAndDate<RIGHT_SIDE>(*fileRightOnly, dbFile))
- if (fileLeftOnly ->getMoveRef() == nullptr && //don't let a row participate in two move pairs!
+ if (FilePair* fileLeftOnly = getAssocFilePair<SelectSide::left>(dbFile))
+ if (sameSizeAndDate<SelectSide::left>(*fileLeftOnly, dbFile))
+ if (FilePair* fileRightOnly = getAssocFilePair<SelectSide::right>(dbFile))
+ if (sameSizeAndDate<SelectSide::right>(*fileRightOnly, dbFile))
+ {
+ assert((!fileLeftOnly ->getMoveRef() &&
+ !fileRightOnly->getMoveRef()) ||
+ (fileLeftOnly ->getMoveRef() == fileRightOnly->getId() &&
+ fileRightOnly->getMoveRef() == fileLeftOnly ->getId()));
+
+ if (fileLeftOnly ->getMoveRef() == nullptr && //needless check!? file prints are unique in this context!
fileRightOnly->getMoveRef() == nullptr) //
{
fileLeftOnly ->setMoveRef(fileRightOnly->getId()); //found a pair, mark it!
fileRightOnly->setMoveRef(fileLeftOnly ->getId()); //
}
+ }
}
const CompareVariant cmpVar_;
const int fileTimeTolerance_;
const std::vector<unsigned int> ignoreTimeShiftMinutes_;
- std::unordered_map<AFS::FileId, FilePair*> exLeftOnlyById_; //FilePair* == nullptr for duplicate ids! => consider aliasing through symlinks!
- std::unordered_map<AFS::FileId, FilePair*> exRightOnlyById_; //=> avoid ambiguity for mixtures of files/symlinks on one side and allow 1-1 mapping only!
- //MSVC: std::unordered_map: about twice as fast as std::map for 1 million items!
+ std::vector<FilePair*> filesL_; //collection of *all* file items (with non-null filePrint)
+ std::vector<FilePair*> filesR_; // => detect duplicate file IDs
+
+ std::unordered_map<AFS::FingerPrint, FilePair*> exLeftOnlyById_; //MSVC: twice as fast as std::map for 1 million items!
+ std::unordered_map<AFS::FingerPrint, FilePair*> exRightOnlyById_;
- std::unordered_map<const InSyncFile*, FilePair*> exLeftOnlyByPath_; //MSVC: only 4% faster than std::map for 1 million items!
+ std::unordered_map<const InSyncFile*, FilePair*> exLeftOnlyByPath_; //MSVC: only 4% faster than std::map for 1 million items!
std::unordered_map<const InSyncFile*, FilePair*> exRightOnlyByPath_;
- /*
- detect renamed files:
-
- X -> |_| Create right
- |_| -> Y Delete right
-
- is detected as:
-
- Rename Y to X on right
-
- Algorithm:
- ----------
- DB-file left <--- (name, size, date) ---> DB-file right
- | |
- | (file ID, size, date) | (file ID, size, date)
- | or | or
- | (file path, size, date) | (file path, size, date)
- \|/ \|/
- file left only file right only
-
- FAT caveat: File Ids are generally not stable when file is either moved or renamed!
- => 1. Move/rename operations on FAT cannot be detected reliably.
- => 2. database generally contains wrong file ID on FAT after renaming from .ffs_tmp files => correct file IDs in database only after next sync
- => 3. even exFAT screws up (but less than FAT) and changes IDs after file move. Did they learn nothing from the past?
- */
+
+ /* Detect Renamed Files:
+
+ X -> |_| Create right
+ |_| -> Y Delete right
+
+ resolve as: Rename Y to X on right
+
+ Algorithm:
+ ----------
+ DB-file left <--- (name, size, date) ---> DB-file right
+ | |
+ | (file ID, size, date) | (file ID, size, date)
+ | or | or
+ | (file path, size, date) | (file path, size, date)
+ \|/ \|/
+ file left only file right only
+
+ FAT caveat: file IDs are generally not stable when file is either moved or renamed!
+ 1. Move/rename operations on FAT cannot be detected reliably.
+ 2. database generally contains wrong file ID on FAT after renaming from .ffs_tmp files => correct file IDs in database only after next sync
+ 3. even exFAT screws up (but less than FAT) and changes IDs after file move. Did they learn nothing from the past? */
};
//----------------------------------------------------------------------------------------------
@@ -569,9 +604,9 @@ private:
return;
//##################### schedule old temporary files for deletion ####################
- if (cat == FILE_LEFT_SIDE_ONLY && endsWith(file.getItemName<LEFT_SIDE>(), AFS::TEMP_FILE_ENDING))
+ if (cat == FILE_LEFT_SIDE_ONLY && endsWith(file.getItemName<SelectSide::left>(), AFS::TEMP_FILE_ENDING))
return file.setSyncDir(SyncDirection::left);
- else if (cat == FILE_RIGHT_SIDE_ONLY && endsWith(file.getItemName<RIGHT_SIDE>(), AFS::TEMP_FILE_ENDING))
+ else if (cat == FILE_RIGHT_SIDE_ONLY && endsWith(file.getItemName<SelectSide::right>(), AFS::TEMP_FILE_ENDING))
return file.setSyncDir(SyncDirection::right);
//####################################################################################
@@ -586,14 +621,14 @@ private:
}
return nullptr;
};
- const InSyncFile* dbEntryL = getDbEntry(dbFolderL, file.getItemName<LEFT_SIDE>());
+ const InSyncFile* dbEntryL = getDbEntry(dbFolderL, file.getItemName<SelectSide::left>());
const InSyncFile* dbEntryR = dbEntryL;
- if (dbFolderL != dbFolderR || getUnicodeNormalForm(file.getItemName<LEFT_SIDE>()) != getUnicodeNormalForm(file.getItemName<RIGHT_SIDE>()))
- dbEntryR = getDbEntry(dbFolderR, file.getItemName<RIGHT_SIDE>());
+ if (dbFolderL != dbFolderR || getUnicodeNormalForm(file.getItemName<SelectSide::left>()) != getUnicodeNormalForm(file.getItemName<SelectSide::right>()))
+ dbEntryR = getDbEntry(dbFolderR, file.getItemName<SelectSide::right>());
//evaluation
- const bool changeOnLeft = !matchesDbEntry< LEFT_SIDE>(file, dbEntryL, ignoreTimeShiftMinutes_);
- const bool changeOnRight = !matchesDbEntry<RIGHT_SIDE>(file, dbEntryR, ignoreTimeShiftMinutes_);
+ const bool changeOnLeft = !matchesDbEntry< SelectSide::left>(file, dbEntryL, ignoreTimeShiftMinutes_);
+ const bool changeOnRight = !matchesDbEntry<SelectSide::right>(file, dbEntryR, ignoreTimeShiftMinutes_);
if (changeOnLeft != changeOnRight)
{
@@ -630,14 +665,14 @@ private:
}
return nullptr;
};
- const InSyncSymlink* dbEntryL = getDbEntry(dbFolderL, symlink.getItemName<LEFT_SIDE>());
+ const InSyncSymlink* dbEntryL = getDbEntry(dbFolderL, symlink.getItemName<SelectSide::left>());
const InSyncSymlink* dbEntryR = dbEntryL;
- if (dbFolderL != dbFolderR || getUnicodeNormalForm(symlink.getItemName<LEFT_SIDE>()) != getUnicodeNormalForm(symlink.getItemName<RIGHT_SIDE>()))
- dbEntryR = getDbEntry(dbFolderR, symlink.getItemName<RIGHT_SIDE>());
+ if (dbFolderL != dbFolderR || getUnicodeNormalForm(symlink.getItemName<SelectSide::left>()) != getUnicodeNormalForm(symlink.getItemName<SelectSide::right>()))
+ dbEntryR = getDbEntry(dbFolderR, symlink.getItemName<SelectSide::right>());
//evaluation
- const bool changeOnLeft = !matchesDbEntry< LEFT_SIDE>(symlink, dbEntryL, ignoreTimeShiftMinutes_);
- const bool changeOnRight = !matchesDbEntry<RIGHT_SIDE>(symlink, dbEntryR, ignoreTimeShiftMinutes_);
+ const bool changeOnLeft = !matchesDbEntry< SelectSide::left>(symlink, dbEntryL, ignoreTimeShiftMinutes_);
+ const bool changeOnRight = !matchesDbEntry<SelectSide::right>(symlink, dbEntryR, ignoreTimeShiftMinutes_);
if (changeOnLeft != changeOnRight)
{
@@ -662,9 +697,9 @@ private:
const CompareDirResult cat = folder.getDirCategory();
//########### schedule abandoned temporary recycle bin directory for deletion ##########
- if (cat == DIR_LEFT_SIDE_ONLY && endsWith(folder.getItemName<LEFT_SIDE>(), AFS::TEMP_FILE_ENDING))
+ if (cat == DIR_LEFT_SIDE_ONLY && endsWith(folder.getItemName<SelectSide::left>(), AFS::TEMP_FILE_ENDING))
return setSyncDirectionRec(SyncDirection::left, folder); //
- else if (cat == DIR_RIGHT_SIDE_ONLY && endsWith(folder.getItemName<RIGHT_SIDE>(), AFS::TEMP_FILE_ENDING))
+ else if (cat == DIR_RIGHT_SIDE_ONLY && endsWith(folder.getItemName<SelectSide::right>(), AFS::TEMP_FILE_ENDING))
return setSyncDirectionRec(SyncDirection::right, folder); //don't recurse below!
//#######################################################################################
@@ -679,16 +714,16 @@ private:
}
return nullptr;
};
- const InSyncFolder* dbEntryL = getDbEntry(dbFolderL, folder.getItemName<LEFT_SIDE>());
+ const InSyncFolder* dbEntryL = getDbEntry(dbFolderL, folder.getItemName<SelectSide::left>());
const InSyncFolder* dbEntryR = dbEntryL;
- if (dbFolderL != dbFolderR || getUnicodeNormalForm(folder.getItemName<LEFT_SIDE>()) != getUnicodeNormalForm(folder.getItemName<RIGHT_SIDE>()))
- dbEntryR = getDbEntry(dbFolderR, folder.getItemName<RIGHT_SIDE>());
+ if (dbFolderL != dbFolderR || getUnicodeNormalForm(folder.getItemName<SelectSide::left>()) != getUnicodeNormalForm(folder.getItemName<SelectSide::right>()))
+ dbEntryR = getDbEntry(dbFolderR, folder.getItemName<SelectSide::right>());
if (cat != DIR_EQUAL)
{
//evaluation
- const bool changeOnLeft = !matchesDbEntry< LEFT_SIDE>(folder, dbEntryL);
- const bool changeOnRight = !matchesDbEntry<RIGHT_SIDE>(folder, dbEntryR);
+ const bool changeOnLeft = !matchesDbEntry< SelectSide::left>(folder, dbEntryL);
+ const bool changeOnRight = !matchesDbEntry<SelectSide::right>(folder, dbEntryR);
if (changeOnLeft != changeOnRight)
{
@@ -778,10 +813,10 @@ void fff::redetermineSyncDirection(const std::vector<std::pair<BaseFolderPair*,
{
std::wstring msg = _("Setting directions for first synchronization: Old files will be overwritten with newer files.");
if (directCfgs.size() > 1)
- msg += L'\n' + AFS::getDisplayPath(baseFolder->getAbstractPath< LEFT_SIDE>()) + L' ' + getVariantNameWithSymbol(dirCfg.var) + L' ' +
- AFS::getDisplayPath(baseFolder->getAbstractPath<RIGHT_SIDE>());
+ msg += L'\n' + AFS::getDisplayPath(baseFolder->getAbstractPath< SelectSide::left>()) + L' ' + getVariantNameWithSymbol(dirCfg.var) + L' ' +
+ AFS::getDisplayPath(baseFolder->getAbstractPath<SelectSide::right>());
- try { callback.reportInfo(msg); /*throw X*/} catch (...) {};
+ try { callback.logInfo(msg); /*throw X*/} catch (...) {};
Redetermine::execute(getTwoWayUpdateSet(), *baseFolder);
}
@@ -1000,12 +1035,12 @@ private:
{
if (Eval<strategy>::process(file))
{
- if (file.isEmpty<LEFT_SIDE>())
- file.setActive(matchSize<RIGHT_SIDE>(file) &&
- matchTime<RIGHT_SIDE>(file));
- else if (file.isEmpty<RIGHT_SIDE>())
- file.setActive(matchSize<LEFT_SIDE>(file) &&
- matchTime<LEFT_SIDE>(file));
+ if (file.isEmpty<SelectSide::left>())
+ file.setActive(matchSize<SelectSide::right>(file) &&
+ matchTime<SelectSide::right>(file));
+ else if (file.isEmpty<SelectSide::right>())
+ file.setActive(matchSize<SelectSide::left>(file) &&
+ matchTime<SelectSide::left>(file));
else
{
//the only case with partially unclear semantics:
@@ -1023,10 +1058,10 @@ private:
------------
*/
//let's set ? := E
- file.setActive((matchSize<RIGHT_SIDE>(file) &&
- matchTime<RIGHT_SIDE>(file)) ||
- (matchSize<LEFT_SIDE>(file) &&
- matchTime<LEFT_SIDE>(file)));
+ file.setActive((matchSize<SelectSide::right>(file) &&
+ matchTime<SelectSide::right>(file)) ||
+ (matchSize<SelectSide::left>(file) &&
+ matchTime<SelectSide::left>(file)));
}
}
}
@@ -1035,13 +1070,13 @@ private:
{
if (Eval<strategy>::process(symlink))
{
- if (symlink.isEmpty<LEFT_SIDE>())
- symlink.setActive(matchTime<RIGHT_SIDE>(symlink));
- else if (symlink.isEmpty<RIGHT_SIDE>())
- symlink.setActive(matchTime<LEFT_SIDE>(symlink));
+ if (symlink.isEmpty<SelectSide::left>())
+ symlink.setActive(matchTime<SelectSide::right>(symlink));
+ else if (symlink.isEmpty<SelectSide::right>())
+ symlink.setActive(matchTime<SelectSide::left>(symlink));
else
- symlink.setActive(matchTime<RIGHT_SIDE>(symlink) ||
- matchTime<LEFT_SIDE> (symlink));
+ symlink.setActive(matchTime<SelectSide::right>(symlink) ||
+ matchTime<SelectSide::left> (symlink));
}
}
@@ -1053,13 +1088,13 @@ private:
recurse(folder);
}
- template <SelectedSide side, class T>
+ template <SelectSide side, class T>
bool matchTime(const T& obj) const
{
return timeSizeFilter_.matchTime(obj.template getLastWriteTime<side>());
}
- template <SelectedSide side, class T>
+ template <SelectSide side, class T>
bool matchSize(const T& obj) const
{
return timeSizeFilter_.matchSize(obj.template getFileSize<side>());
@@ -1136,24 +1171,24 @@ private:
void processFile(FilePair& file) const
{
- if (file.isEmpty<LEFT_SIDE>())
- file.setActive(matchTime<RIGHT_SIDE>(file));
- else if (file.isEmpty<RIGHT_SIDE>())
- file.setActive(matchTime<LEFT_SIDE>(file));
+ if (file.isEmpty<SelectSide::left>())
+ file.setActive(matchTime<SelectSide::right>(file));
+ else if (file.isEmpty<SelectSide::right>())
+ file.setActive(matchTime<SelectSide::left>(file));
else
- file.setActive(matchTime<RIGHT_SIDE>(file) ||
- matchTime<LEFT_SIDE>(file));
+ file.setActive(matchTime<SelectSide::right>(file) ||
+ matchTime<SelectSide::left>(file));
}
void processLink(SymlinkPair& link) const
{
- if (link.isEmpty<LEFT_SIDE>())
- link.setActive(matchTime<RIGHT_SIDE>(link));
- else if (link.isEmpty<RIGHT_SIDE>())
- link.setActive(matchTime<LEFT_SIDE>(link));
+ if (link.isEmpty<SelectSide::left>())
+ link.setActive(matchTime<SelectSide::right>(link));
+ else if (link.isEmpty<SelectSide::right>())
+ link.setActive(matchTime<SelectSide::left>(link));
else
- link.setActive(matchTime<RIGHT_SIDE>(link) ||
- matchTime<LEFT_SIDE> (link));
+ link.setActive(matchTime<SelectSide::right>(link) ||
+ matchTime<SelectSide::left> (link));
}
void processDir(FolderPair& folder) const
@@ -1162,7 +1197,7 @@ private:
recurse(folder);
}
- template <SelectedSide side, class T>
+ template <SelectSide side, class T>
bool matchTime(const T& obj) const
{
return timeFrom_ <= obj.template getLastWriteTime<side>() &&
@@ -1212,7 +1247,7 @@ std::optional<PathDependency> fff::getPathDependency(const AbstractPath& basePat
// - user may have manually excluded the conflicting items or changed the filter settings without running a re-compare
bool childItemMightMatch = true;
if (relDirPath.empty() || filterP.passDirFilter(relDirPath, &childItemMightMatch) || childItemMightMatch)
- return PathDependency({ basePathP, basePathC, relDirPath });
+ return PathDependency({basePathP, basePathC, relDirPath});
}
}
}
@@ -1229,26 +1264,26 @@ std::pair<std::wstring, int> fff::getSelectedItemsAsString(std::span<const FileS
int totalDelCount = 0;
for (const FileSystemObject* fsObj : selectionLeft)
- if (!fsObj->isEmpty<LEFT_SIDE>())
+ if (!fsObj->isEmpty<SelectSide::left>())
{
- fileList += AFS::getDisplayPath(fsObj->getAbstractPath<LEFT_SIDE>()) + L'\n';
+ fileList += AFS::getDisplayPath(fsObj->getAbstractPath<SelectSide::left>()) + L'\n';
++totalDelCount;
}
for (const FileSystemObject* fsObj : selectionRight)
- if (!fsObj->isEmpty<RIGHT_SIDE>())
+ if (!fsObj->isEmpty<SelectSide::right>())
{
- fileList += AFS::getDisplayPath(fsObj->getAbstractPath<RIGHT_SIDE>()) + L'\n';
+ fileList += AFS::getDisplayPath(fsObj->getAbstractPath<SelectSide::right>()) + L'\n';
++totalDelCount;
}
- return { fileList, totalDelCount };
+ return {fileList, totalDelCount};
}
namespace
{
-template <SelectedSide side>
+template <SelectSide side>
void copyToAlternateFolderFrom(const std::vector<const FileSystemObject*>& rowsToCopy,
const AbstractPath& targetFolderPath,
bool keepRelPaths,
@@ -1257,7 +1292,9 @@ void copyToAlternateFolderFrom(const std::vector<const FileSystemObject*>& rowsT
{
auto notifyItemCopy = [&](const std::wstring& statusText, const std::wstring& displayPath)
{
- callback.reportInfo(replaceCpy(statusText, L"%x", fmtPath(displayPath))); //throw X
+ std::wstring msg = replaceCpy(statusText, L"%x", fmtPath(displayPath));
+ callback.logInfo(msg); //throw X
+ callback.updateStatus(std::move(msg)); //
};
const std::wstring txtCreatingFile (_("Creating file %x" ));
const std::wstring txtCreatingFolder(_("Creating folder %x" ));
@@ -1319,7 +1356,7 @@ void copyToAlternateFolderFrom(const std::vector<const FileSystemObject*>& rowsT
visitFSObject(*fsObj, [&](const FolderPair& folder)
{
- ItemStatReporter<> statReporter(1, 0, callback);
+ ItemStatReporter statReporter(1, 0, callback);
notifyItemCopy(txtCreatingFolder, AFS::getDisplayPath(targetPath));
AFS::createFolderIfMissingRecursion(targetPath); //throw FileError
@@ -1333,7 +1370,7 @@ void copyToAlternateFolderFrom(const std::vector<const FileSystemObject*>& rowsT
notifyItemCopy(txtCreatingFile, AFS::getDisplayPath(targetPath));
const FileAttributes attr = file.getAttributes<side>();
- const AFS::StreamAttributes sourceAttr{ attr.modTime, attr.fileSize, attr.fileId };
+ const AFS::StreamAttributes sourceAttr{attr.modTime, attr.fileSize, attr.filePrint};
copyItem(targetPath, statReporter, [&](const std::function<void()>& deleteTargetItem) //throw FileError
{
@@ -1380,19 +1417,19 @@ void fff::copyToAlternateFolder(std::span<const FileSystemObject* const> rowsToC
{
std::vector<const FileSystemObject*> itemSelectionLeft (rowsToCopyOnLeft .begin(), rowsToCopyOnLeft .end());
std::vector<const FileSystemObject*> itemSelectionRight(rowsToCopyOnRight.begin(), rowsToCopyOnRight.end());
- std::erase_if(itemSelectionLeft, [](const FileSystemObject* fsObj) { return fsObj->isEmpty< LEFT_SIDE>(); }); //needed for correct stats!
- std::erase_if(itemSelectionRight, [](const FileSystemObject* fsObj) { return fsObj->isEmpty<RIGHT_SIDE>(); }); //
+ std::erase_if(itemSelectionLeft, [](const FileSystemObject* fsObj) { return fsObj->isEmpty< SelectSide::left>(); }); //needed for correct stats!
+ std::erase_if(itemSelectionRight, [](const FileSystemObject* fsObj) { return fsObj->isEmpty<SelectSide::right>(); }); //
const int itemTotal = static_cast<int>(itemSelectionLeft.size() + itemSelectionRight.size());
int64_t bytesTotal = 0;
for (const FileSystemObject* fsObj : itemSelectionLeft)
visitFSObject(*fsObj, [](const FolderPair& folder) {},
- [&](const FilePair& file) { bytesTotal += static_cast<int64_t>(file.getFileSize<LEFT_SIDE>()); }, [](const SymlinkPair& symlink) {});
+ [&](const FilePair& file) { bytesTotal += static_cast<int64_t>(file.getFileSize<SelectSide::left>()); }, [](const SymlinkPair& symlink) {});
for (const FileSystemObject* fsObj : itemSelectionRight)
visitFSObject(*fsObj, [](const FolderPair& folder) {},
- [&](const FilePair& file) { bytesTotal += static_cast<int64_t>(file.getFileSize<RIGHT_SIDE>()); }, [](const SymlinkPair& symlink) {});
+ [&](const FilePair& file) { bytesTotal += static_cast<int64_t>(file.getFileSize<SelectSide::right>()); }, [](const SymlinkPair& symlink) {});
callback.initNewPhase(itemTotal, bytesTotal, ProcessPhase::none); //throw X
@@ -1400,22 +1437,24 @@ void fff::copyToAlternateFolder(std::span<const FileSystemObject* const> rowsToC
const AbstractPath targetFolderPath = createAbstractPath(targetFolderPathPhrase);
- copyToAlternateFolderFrom< LEFT_SIDE>(itemSelectionLeft, targetFolderPath, keepRelPaths, overwriteIfExists, callback);
- copyToAlternateFolderFrom<RIGHT_SIDE>(itemSelectionRight, targetFolderPath, keepRelPaths, overwriteIfExists, callback);
+ copyToAlternateFolderFrom< SelectSide::left>(itemSelectionLeft, targetFolderPath, keepRelPaths, overwriteIfExists, callback);
+ copyToAlternateFolderFrom<SelectSide::right>(itemSelectionRight, targetFolderPath, keepRelPaths, overwriteIfExists, callback);
}
//############################################################################################################
namespace
{
-template <SelectedSide side>
+template <SelectSide side>
void deleteFromGridAndHDOneSide(std::vector<FileSystemObject*>& rowsToDelete,
bool useRecycleBin,
PhaseCallback& callback)
{
auto notifyItemDeletion = [&](const std::wstring& statusText, const std::wstring& displayPath)
{
- callback.reportInfo(replaceCpy(statusText, L"%x", fmtPath(displayPath))); //throw X
+ std::wstring msg = replaceCpy(statusText, L"%x", fmtPath(displayPath));
+ callback.logInfo(msg); //throw X
+ callback.updateStatus(std::move(msg)); //
};
std::wstring txtRemovingFile;
@@ -1501,7 +1540,7 @@ void deleteFromGridAndHDOneSide(std::vector<FileSystemObject*>& rowsToDelete,
}
-template <SelectedSide side>
+template <SelectSide side>
void categorize(const std::vector<FileSystemObject*>& rows,
std::vector<FileSystemObject*>& deletePermanent,
std::vector<FileSystemObject*>& deleteRecyler,
@@ -1556,8 +1595,8 @@ void fff::deleteFromGridAndHD(const std::vector<FileSystemObject*>& rowsToDelete
std::vector<FileSystemObject*> deleteLeft = rowsToDeleteOnLeft;
std::vector<FileSystemObject*> deleteRight = rowsToDeleteOnRight;
- std::erase_if(deleteLeft, [](const FileSystemObject* fsObj) { return fsObj->isEmpty< LEFT_SIDE>(); }); //needed?
- std::erase_if(deleteRight, [](const FileSystemObject* fsObj) { return fsObj->isEmpty<RIGHT_SIDE>(); }); //yes, for correct stats:
+ std::erase_if(deleteLeft, [](const FileSystemObject* fsObj) { return fsObj->isEmpty< SelectSide::left>(); }); //needed?
+ std::erase_if(deleteRight, [](const FileSystemObject* fsObj) { return fsObj->isEmpty<SelectSide::right>(); }); //yes, for correct stats:
const int itemCount = static_cast<int>(deleteLeft.size() + deleteRight.size());
callback.initNewPhase(itemCount, 0, ProcessPhase::none); //throw X
@@ -1577,7 +1616,7 @@ void fff::deleteFromGridAndHD(const std::vector<FileSystemObject*>& rowsToDelete
{
FileSystemObject& fsObj = **it; //all pointers are required(!) to be bound
- if (fsObj.isEmpty<LEFT_SIDE>() != fsObj.isEmpty<RIGHT_SIDE>()) //make sure objects exists on one side only
+ if (fsObj.isEmpty<SelectSide::left>() != fsObj.isEmpty<SelectSide::right>()) //make sure objects exists on one side only
{
auto cfgIter = baseFolderCfgs.find(&fsObj.base());
assert(cfgIter != baseFolderCfgs.end());
@@ -1586,11 +1625,11 @@ void fff::deleteFromGridAndHD(const std::vector<FileSystemObject*>& rowsToDelete
SyncDirection newDir = SyncDirection::none;
if (cfgIter->second.var == SyncVariant::twoWay)
- newDir = fsObj.isEmpty<LEFT_SIDE>() ? SyncDirection::right : SyncDirection::left;
+ newDir = fsObj.isEmpty<SelectSide::left>() ? SyncDirection::right : SyncDirection::left;
else
{
const DirectionSet& dirCfg = extractDirections(cfgIter->second);
- newDir = fsObj.isEmpty<LEFT_SIDE>() ? dirCfg.exRightSideOnly : dirCfg.exLeftSideOnly;
+ newDir = fsObj.isEmpty<SelectSide::left>() ? dirCfg.exRightSideOnly : dirCfg.exLeftSideOnly;
}
setSyncDirectionRec(newDir, fsObj); //set new direction (recursively)
@@ -1611,8 +1650,8 @@ void fff::deleteFromGridAndHD(const std::vector<FileSystemObject*>& rowsToDelete
std::vector<FileSystemObject*> deleteRecylerRight;
std::map<AbstractPath, bool> recyclerSupported;
- categorize< LEFT_SIDE>(deleteLeft, deletePermanentLeft, deleteRecylerLeft, useRecycleBin, recyclerSupported, callback); //throw X
- categorize<RIGHT_SIDE>(deleteRight, deletePermanentRight, deleteRecylerRight, useRecycleBin, recyclerSupported, callback); //
+ categorize< SelectSide::left>(deleteLeft, deletePermanentLeft, deleteRecylerLeft, useRecycleBin, recyclerSupported, callback); //throw X
+ categorize<SelectSide::right>(deleteRight, deletePermanentRight, deleteRecylerRight, useRecycleBin, recyclerSupported, callback); //
//windows: check if recycle bin really exists; if not, Windows will silently delete, which is wrong
if (useRecycleBin &&
@@ -1627,11 +1666,11 @@ void fff::deleteFromGridAndHD(const std::vector<FileSystemObject*>& rowsToDelete
callback.reportWarning(msg, warnRecyclerMissing); //throw?
}
- deleteFromGridAndHDOneSide<LEFT_SIDE>(deleteRecylerLeft, true, callback);
- deleteFromGridAndHDOneSide<LEFT_SIDE>(deletePermanentLeft, false, callback);
+ deleteFromGridAndHDOneSide<SelectSide::left>(deleteRecylerLeft, true, callback);
+ deleteFromGridAndHDOneSide<SelectSide::left>(deletePermanentLeft, false, callback);
- deleteFromGridAndHDOneSide<RIGHT_SIDE>(deleteRecylerRight, true, callback);
- deleteFromGridAndHDOneSide<RIGHT_SIDE>(deletePermanentRight, false, callback);
+ deleteFromGridAndHDOneSide<SelectSide::right>(deleteRecylerRight, true, callback);
+ deleteFromGridAndHDOneSide<SelectSide::right>(deletePermanentRight, false, callback);
}
//############################################################################################################
@@ -1693,7 +1732,7 @@ void TempFileBuffer::createTempFiles(const std::set<FileDescriptor>& workLoad, P
MemoryStreamOut<std::string> cookie; //create hash to distinguish different versions and file locations
writeNumber (cookie, descr.attr.modTime);
writeNumber (cookie, descr.attr.fileSize);
- writeContainer(cookie, descr.attr.fileId);
+ writeNumber (cookie, descr.attr.filePrint);
writeNumber (cookie, descr.attr.isFollowedSymlink);
writeContainer(cookie, AFS::getInitPathPhrase(descr.path));
@@ -1706,13 +1745,15 @@ void TempFileBuffer::createTempFiles(const std::set<FileDescriptor>& workLoad, P
const Zstring tempFileName = Zstring(fileName.begin(), it) + Zstr('~') + descrHash + Zstring(it, fileName.end());
const Zstring tempFilePath = appendSeparator(tempFolderPath_) + tempFileName;
- const AFS::StreamAttributes sourceAttr{ descr.attr.modTime, descr.attr.fileSize, descr.attr.fileId };
+ const AFS::StreamAttributes sourceAttr{descr.attr.modTime, descr.attr.fileSize, descr.attr.filePrint};
tryReportingError([&]
{
- ItemStatReporter<> statReporter(1, descr.attr.fileSize, callback);
+ ItemStatReporter statReporter(1, descr.attr.fileSize, callback);
- callback.reportInfo(replaceCpy(_("Creating file %x"), L"%x", fmtPath(tempFilePath))); //throw X
+ std::wstring msg = replaceCpy(_("Creating file %x"), L"%x", fmtPath(tempFilePath)); //throw X
+ callback.logInfo(msg); //throw X
+ callback.updateStatus(std::move(msg)); //
auto notifyUnbufferedIO = [&](int64_t bytesDelta)
{
diff --git a/FreeFileSync/Source/base/binary.cpp b/FreeFileSync/Source/base/binary.cpp
index f3199f67..49bdd6eb 100644
--- a/FreeFileSync/Source/base/binary.cpp
+++ b/FreeFileSync/Source/base/binary.cpp
@@ -39,7 +39,7 @@ const size_t BLOCK_SIZE_MAX = 16 * 1024 * 1024;
struct StreamReader
{
- StreamReader(const AbstractPath& filePath, const IOCallback& notifyUnbufferedIO) : //throw FileError
+ StreamReader(const AbstractPath& filePath, const IoCallback& notifyUnbufferedIO) : //throw FileError
stream_(AFS::getInputStream(filePath, notifyUnbufferedIO)), //throw FileError, ErrorFileLocked
defaultBlockSize_(stream_->getBlockSize()),
dynamicBlockSize_(defaultBlockSize_) { assert(defaultBlockSize_ > 0); }
@@ -94,7 +94,7 @@ private:
}
-bool fff::filesHaveSameContent(const AbstractPath& filePath1, const AbstractPath& filePath2, const IOCallback& notifyUnbufferedIO /*throw X*/) //throw FileError, X
+bool fff::filesHaveSameContent(const AbstractPath& filePath1, const AbstractPath& filePath2, const IoCallback& notifyUnbufferedIO /*throw X*/) //throw FileError, X
{
int64_t totalUnbufferedIO = 0;
diff --git a/FreeFileSync/Source/base/binary.h b/FreeFileSync/Source/base/binary.h
index 3c2db11a..1223cb05 100644
--- a/FreeFileSync/Source/base/binary.h
+++ b/FreeFileSync/Source/base/binary.h
@@ -14,7 +14,7 @@ namespace fff
{
bool filesHaveSameContent(const AbstractPath& filePath1, //throw FileError, X
const AbstractPath& filePath2,
- const zen::IOCallback& notifyUnbufferedIO /*throw X*/);
+ const zen::IoCallback& notifyUnbufferedIO /*throw X*/);
}
#endif //BINARY_H_3941281398513241134
diff --git a/FreeFileSync/Source/base/comparison.cpp b/FreeFileSync/Source/base/comparison.cpp
index 87055154..bf1afafa 100644
--- a/FreeFileSync/Source/base/comparison.cpp
+++ b/FreeFileSync/Source/base/comparison.cpp
@@ -17,6 +17,7 @@
#include "cmp_filetime.h"
#include "status_handler_impl.h"
#include "../afs/concrete.h"
+#include "../afs/native.h"
using namespace zen;
using namespace fff;
@@ -25,7 +26,7 @@ using namespace fff;
std::vector<FolderPairCfg> fff::extractCompareCfg(const MainConfiguration& mainCfg)
{
//merge first and additional pairs
- std::vector<LocalPairConfig> localCfgs = { mainCfg.firstPair };
+ std::vector<LocalPairConfig> localCfgs = {mainCfg.firstPair};
append(localCfgs, mainCfg.additionalPairs);
std::vector<FolderPairCfg> output;
@@ -206,9 +207,9 @@ ComparisonBuffer::ComparisonBuffer(const std::set<DirectoryKey>& foldersToRead,
const int64_t totalTimeSec = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now() - compareStartTime).count();
- callback.reportInfo(_("Comparison finished:") + L' ' +
- _P("1 item found", "%x items found", itemsReported) + L" | " +
- _("Time elapsed:") + L' ' + copyStringTo<std::wstring>(wxTimeSpan::Seconds(totalTimeSec).Format())); //throw X
+ callback.logInfo(_("Comparison finished:") + L' ' +
+ _P("1 item found", "%x items found", itemsReported) + L" | " +
+ _("Time elapsed:") + L' ' + copyStringTo<std::wstring>(wxTimeSpan::Seconds(totalTimeSec).Format())); //throw X
}
@@ -222,7 +223,7 @@ const wchar_t arrowRight[] = L"->";
//NOTE: conflict texts are NOT expected to contain additional path info (already implicit through associated item!)
// => only add path info if information is relevant, e.g. conflict is specific to left/right side only
-template <SelectedSide side, class FileOrLinkPair> inline
+template <SelectSide side, class FileOrLinkPair> inline
Zstringc getConflictInvalidDate(const FileOrLinkPair& file)
{
return utfTo<Zstringc>(replaceCpy(_("File %x has an invalid date."), L"%x", fmtPath(AFS::getDisplayPath(file.template getAbstractPath<side>()))) + L'\n' +
@@ -233,8 +234,8 @@ Zstringc getConflictInvalidDate(const FileOrLinkPair& file)
Zstringc getConflictSameDateDiffSize(const FilePair& file)
{
return utfTo<Zstringc>(_("Files have the same date but a different size.") + L'\n' +
- arrowLeft + L' ' + _("Date:") + L' ' + formatUtcToLocalTime(file.getLastWriteTime< LEFT_SIDE>()) + L" " + _("Size:") + L' ' + formatNumber(file.getFileSize<LEFT_SIDE>()) + L'\n' +
- arrowRight + L' ' + _("Date:") + L' ' + formatUtcToLocalTime(file.getLastWriteTime<RIGHT_SIDE>()) + L" " + _("Size:") + L' ' + formatNumber(file.getFileSize<RIGHT_SIDE>()));
+ arrowLeft + L' ' + _("Date:") + L' ' + formatUtcToLocalTime(file.getLastWriteTime< SelectSide::left>()) + L" " + _("Size:") + L' ' + formatNumber(file.getFileSize<SelectSide::left>()) + L'\n' +
+ arrowRight + L' ' + _("Date:") + L' ' + formatUtcToLocalTime(file.getLastWriteTime<SelectSide::right>()) + L" " + _("Size:") + L' ' + formatNumber(file.getFileSize<SelectSide::right>()));
}
@@ -247,8 +248,8 @@ Zstringc getConflictSkippedBinaryComparison()
Zstringc getDescrDiffMetaShortnameCase(const FileSystemObject& fsObj)
{
return utfTo<Zstringc>(_("Items differ in attributes only") + L'\n' +
- arrowLeft + L' ' + fmtPath(fsObj.getItemName< LEFT_SIDE>()) + L'\n' +
- arrowRight + L' ' + fmtPath(fsObj.getItemName<RIGHT_SIDE>()));
+ arrowLeft + L' ' + fmtPath(fsObj.getItemName< SelectSide::left>()) + L'\n' +
+ arrowRight + L' ' + fmtPath(fsObj.getItemName<SelectSide::right>()));
}
@@ -257,8 +258,8 @@ template <class FileOrLinkPair>
Zstringc getDescrDiffMetaData(const FileOrLinkPair& file)
{
return utfTo<Zstringc>(_("Items differ in attributes only") + L'\n' +
- arrowLeft + L' ' + _("Date:") + L' ' + formatUtcToLocalTime(file.template getLastWriteTime< LEFT_SIDE>()) + L'\n' +
- arrowRight + L' ' + _("Date:") + L' ' + formatUtcToLocalTime(file.template getLastWriteTime<RIGHT_SIDE>()));
+ arrowLeft + L' ' + _("Date:") + L' ' + formatUtcToLocalTime(file.template getLastWriteTime< SelectSide::left>()) + L'\n' +
+ arrowRight + L' ' + _("Date:") + L' ' + formatUtcToLocalTime(file.template getLastWriteTime<SelectSide::right>()));
}
#endif
@@ -273,16 +274,16 @@ Zstringc getConflictAmbiguousItemName(const Zstring& itemName)
void categorizeSymlinkByTime(SymlinkPair& symlink)
{
//categorize symlinks that exist on both sides
- switch (compareFileTime(symlink.getLastWriteTime<LEFT_SIDE>(),
- symlink.getLastWriteTime<RIGHT_SIDE>(), symlink.base().getFileTimeTolerance(), symlink.base().getIgnoredTimeShift()))
+ switch (compareFileTime(symlink.getLastWriteTime<SelectSide::left>(),
+ symlink.getLastWriteTime<SelectSide::right>(), symlink.base().getFileTimeTolerance(), symlink.base().getIgnoredTimeShift()))
{
case TimeResult::equal:
//Caveat:
//1. SYMLINK_EQUAL may only be set if short names match in case: InSyncFolder's mapping tables use short name as a key! see db_file.cpp
//2. harmonize with "bool stillInSync()" in algorithm.cpp
- if (getUnicodeNormalForm(symlink.getItemName< LEFT_SIDE>()) ==
- getUnicodeNormalForm(symlink.getItemName<RIGHT_SIDE>()))
+ if (getUnicodeNormalForm(symlink.getItemName< SelectSide::left>()) ==
+ getUnicodeNormalForm(symlink.getItemName<SelectSide::right>()))
symlink.setCategory<FILE_EQUAL>();
else
symlink.setCategoryDiffMetadata(getDescrDiffMetaShortnameCase(symlink));
@@ -297,11 +298,11 @@ void categorizeSymlinkByTime(SymlinkPair& symlink)
break;
case TimeResult::leftInvalid:
- symlink.setCategoryConflict(getConflictInvalidDate<LEFT_SIDE>(symlink));
+ symlink.setCategoryConflict(getConflictInvalidDate<SelectSide::left>(symlink));
break;
case TimeResult::rightInvalid:
- symlink.setCategoryConflict(getConflictInvalidDate<RIGHT_SIDE>(symlink));
+ symlink.setCategoryConflict(getConflictInvalidDate<SelectSide::right>(symlink));
break;
}
}
@@ -321,18 +322,18 @@ std::shared_ptr<BaseFolderPair> ComparisonBuffer::compareByTimeSize(const Resolv
//categorize files that exist on both sides
for (FilePair* file : uncategorizedFiles)
{
- switch (compareFileTime(file->getLastWriteTime<LEFT_SIDE>(),
- file->getLastWriteTime<RIGHT_SIDE>(), fileTimeTolerance_, fpConfig.ignoreTimeShiftMinutes))
+ switch (compareFileTime(file->getLastWriteTime<SelectSide::left>(),
+ file->getLastWriteTime<SelectSide::right>(), fileTimeTolerance_, fpConfig.ignoreTimeShiftMinutes))
{
case TimeResult::equal:
//Caveat:
//1. FILE_EQUAL may only be set if short names match in case: InSyncFolder's mapping tables use short name as a key! see db_file.cpp
//2. FILE_EQUAL is expected to mean identical file sizes! See InSyncFile
//3. harmonize with "bool stillInSync()" in algorithm.cpp, FilePair::setSyncedTo() in file_hierarchy.h
- if (file->getFileSize<LEFT_SIDE>() == file->getFileSize<RIGHT_SIDE>())
+ if (file->getFileSize<SelectSide::left>() == file->getFileSize<SelectSide::right>())
{
- if (getUnicodeNormalForm(file->getItemName< LEFT_SIDE>()) ==
- getUnicodeNormalForm(file->getItemName<RIGHT_SIDE>()))
+ if (getUnicodeNormalForm(file->getItemName< SelectSide::left>()) ==
+ getUnicodeNormalForm(file->getItemName<SelectSide::right>()))
file->setCategory<FILE_EQUAL>();
else
file->setCategoryDiffMetadata(getDescrDiffMetaShortnameCase(*file));
@@ -350,11 +351,11 @@ std::shared_ptr<BaseFolderPair> ComparisonBuffer::compareByTimeSize(const Resolv
break;
case TimeResult::leftInvalid:
- file->setCategoryConflict(getConflictInvalidDate<LEFT_SIDE>(*file));
+ file->setCategoryConflict(getConflictInvalidDate<SelectSide::left>(*file));
break;
case TimeResult::rightInvalid:
- file->setCategoryConflict(getConflictInvalidDate<RIGHT_SIDE>(*file));
+ file->setCategoryConflict(getConflictInvalidDate<SelectSide::right>(*file));
break;
}
}
@@ -367,14 +368,14 @@ namespace
void categorizeSymlinkByContent(SymlinkPair& symlink, PhaseCallback& callback)
{
//categorize symlinks that exist on both sides
- callback.updateStatus(replaceCpy(_("Resolving symbolic link %x"), L"%x", fmtPath(AFS::getDisplayPath(symlink.getAbstractPath< LEFT_SIDE>())))); //throw X
- callback.updateStatus(replaceCpy(_("Resolving symbolic link %x"), L"%x", fmtPath(AFS::getDisplayPath(symlink.getAbstractPath<RIGHT_SIDE>())))); //throw X
+ callback.updateStatus(replaceCpy(_("Resolving symbolic link %x"), L"%x", fmtPath(AFS::getDisplayPath(symlink.getAbstractPath< SelectSide::left>())))); //throw X
+ callback.updateStatus(replaceCpy(_("Resolving symbolic link %x"), L"%x", fmtPath(AFS::getDisplayPath(symlink.getAbstractPath<SelectSide::right>())))); //throw X
bool equalContent = false;
const std::wstring errMsg = tryReportingError([&]
{
- equalContent = AFS::equalSymlinkContent(symlink.getAbstractPath< LEFT_SIDE>(),
- symlink.getAbstractPath<RIGHT_SIDE>()); //throw FileError
+ equalContent = AFS::equalSymlinkContent(symlink.getAbstractPath< SelectSide::left>(),
+ symlink.getAbstractPath<SelectSide::right>()); //throw FileError
}, callback); //throw X
if (!errMsg.empty())
@@ -387,11 +388,11 @@ void categorizeSymlinkByContent(SymlinkPair& symlink, PhaseCallback& callback)
//1. SYMLINK_EQUAL may only be set if short names match in case: InSyncFolder's mapping tables use short name as a key! see db_file.cpp
//2. harmonize with "bool stillInSync()" in algorithm.cpp, FilePair::setSyncedTo() in file_hierarchy.h
- if (getUnicodeNormalForm(symlink.getItemName< LEFT_SIDE>()) !=
- getUnicodeNormalForm(symlink.getItemName<RIGHT_SIDE>()))
+ if (getUnicodeNormalForm(symlink.getItemName< SelectSide::left>()) !=
+ getUnicodeNormalForm(symlink.getItemName<SelectSide::right>()))
symlink.setCategoryDiffMetadata(getDescrDiffMetaShortnameCase(symlink));
- //else if (!sameFileTime(symlink.getLastWriteTime<LEFT_SIDE>(),
- // symlink.getLastWriteTime<RIGHT_SIDE>(), symlink.base().getFileTimeTolerance(), symlink.base().getIgnoredTimeShift()))
+ //else if (!sameFileTime(symlink.getLastWriteTime<SelectSide::left>(),
+ // symlink.getLastWriteTime<SelectSide::right>(), symlink.base().getFileTimeTolerance(), symlink.base().getIgnoredTimeShift()))
// symlink.setCategoryDiffMetadata(getDescrDiffMetaData(symlink));
else
symlink.setCategory<FILE_EQUAL>();
@@ -422,10 +423,10 @@ std::shared_ptr<BaseFolderPair> ComparisonBuffer::compareBySize(const ResolvedFo
//1. FILE_EQUAL may only be set if short names match in case: InSyncFolder's mapping tables use short name as a key! see db_file.cpp
//2. FILE_EQUAL is expected to mean identical file sizes! See InSyncFile
//3. harmonize with "bool stillInSync()" in algorithm.cpp, FilePair::setSyncedTo() in file_hierarchy.h
- if (file->getFileSize<LEFT_SIDE>() == file->getFileSize<RIGHT_SIDE>())
+ if (file->getFileSize<SelectSide::left>() == file->getFileSize<SelectSide::right>())
{
- if (getUnicodeNormalForm(file->getItemName< LEFT_SIDE>()) ==
- getUnicodeNormalForm(file->getItemName<RIGHT_SIDE>()))
+ if (getUnicodeNormalForm(file->getItemName< SelectSide::left>()) ==
+ getUnicodeNormalForm(file->getItemName<SelectSide::right>()))
file->setCategory<FILE_EQUAL>();
else
file->setCategoryDiffMetadata(getDescrDiffMetaShortnameCase(*file));
@@ -444,7 +445,7 @@ namespace parallel
//--------------------------------------------------------------
inline
bool filesHaveSameContent(const AbstractPath& filePath1, const AbstractPath& filePath2, //throw FileError, X
- const IOCallback& notifyUnbufferedIO /*throw X*/,
+ const IoCallback& notifyUnbufferedIO /*throw X*/,
std::mutex& singleThread)
{ return parallelScope([=] { return filesHaveSameContent(filePath1, filePath2, notifyUnbufferedIO); /*throw FileError, X*/ }, singleThread); }
}
@@ -459,7 +460,7 @@ void categorizeFileByContent(FilePair& file, const std::wstring& txtComparingCon
bool haveSameContent = false;
const std::wstring errMsg = tryReportingError([&]
{
- AsyncItemStatReporter statReporter(1, file.getFileSize<LEFT_SIDE>(), acb);
+ AsyncItemStatReporter statReporter(1, file.getFileSize<SelectSide::left>(), acb);
//callbacks run *outside* singleThread_ lock! => fine
auto notifyUnbufferedIO = [&statReporter](int64_t bytesDelta)
@@ -468,8 +469,8 @@ void categorizeFileByContent(FilePair& file, const std::wstring& txtComparingCon
interruptionPoint(); //throw ThreadStopRequest
};
- haveSameContent = parallel::filesHaveSameContent(file.getAbstractPath< LEFT_SIDE>(),
- file.getAbstractPath<RIGHT_SIDE>(), notifyUnbufferedIO, singleThread); //throw FileError, ThreadStopRequest
+ haveSameContent = parallel::filesHaveSameContent(file.getAbstractPath< SelectSide::left>(),
+ file.getAbstractPath<SelectSide::right>(), notifyUnbufferedIO, singleThread); //throw FileError, ThreadStopRequest
statReporter.reportDelta(1, 0);
}, acb); //throw ThreadStopRequest
@@ -483,12 +484,12 @@ void categorizeFileByContent(FilePair& file, const std::wstring& txtComparingCon
//1. FILE_EQUAL may only be set if short names match in case: InSyncFolder's mapping tables use short name as a key! see db_file.cpp
//2. FILE_EQUAL is expected to mean identical file sizes! See InSyncFile
//3. harmonize with "bool stillInSync()" in algorithm.cpp, FilePair::setSyncedTo() in file_hierarchy.h
- if (getUnicodeNormalForm(file.getItemName< LEFT_SIDE>()) !=
- getUnicodeNormalForm(file.getItemName<RIGHT_SIDE>()))
+ if (getUnicodeNormalForm(file.getItemName< SelectSide::left>()) !=
+ getUnicodeNormalForm(file.getItemName<SelectSide::right>()))
file.setCategoryDiffMetadata(getDescrDiffMetaShortnameCase(file));
#if 0 //don't synchronize modtime only see FolderPairSyncer::synchronizeFileInt(), SO_COPY_METADATA_TO_*
- else if (!sameFileTime(file.getLastWriteTime<LEFT_SIDE>(),
- file.getLastWriteTime<RIGHT_SIDE>(), file.base().getFileTimeTolerance(), file.base().getIgnoredTimeShift()))
+ else if (!sameFileTime(file.getLastWriteTime<SelectSide::left>(),
+ file.getLastWriteTime<SelectSide::right>(), file.base().getFileTimeTolerance(), file.base().getIgnoredTimeShift()))
file.setCategoryDiffMetadata(getDescrDiffMetaData(file));
#endif
else
@@ -521,7 +522,7 @@ std::list<std::shared_ptr<BaseFolderPair>> ComparisonBuffer::compareByContent(co
{
ParallelOps& posL = parallelOpsStatus[basePathL.afsDevice];
ParallelOps& posR = parallelOpsStatus[basePathR.afsDevice];
- fpWorkload.push_back({ posL, posR, std::move(filesToCompareBytewise) });
+ fpWorkload.push_back({posL, posR, std::move(filesToCompareBytewise)});
};
//PERF_START;
@@ -541,7 +542,7 @@ std::list<std::shared_ptr<BaseFolderPair>> ComparisonBuffer::compareByContent(co
//in order to separate into two processes (scanning and comparing)
for (FilePair* file : undefinedFiles)
//pre-check: files have different content if they have a different file size (must not be FILE_EQUAL: see InSyncFile)
- if (file->getFileSize<LEFT_SIDE>() != file->getFileSize<RIGHT_SIDE>())
+ if (file->getFileSize<SelectSide::left>() != file->getFileSize<SelectSide::right>())
file->setCategory<FILE_DIFFERENT_CONTENT>();
else
{
@@ -553,8 +554,8 @@ std::list<std::shared_ptr<BaseFolderPair>> ComparisonBuffer::compareByContent(co
filesToCompareBytewise.push_back(file);
}
if (!filesToCompareBytewise.empty())
- addToBinaryWorkload(output.back()->getAbstractPath< LEFT_SIDE>(),
- output.back()->getAbstractPath<RIGHT_SIDE>(), std::move(filesToCompareBytewise));
+ addToBinaryWorkload(output.back()->getAbstractPath< SelectSide::left>(),
+ output.back()->getAbstractPath<SelectSide::right>(), std::move(filesToCompareBytewise));
//finish symlink categorization
for (SymlinkPair* symlink : uncategorizedLinks)
@@ -571,7 +572,7 @@ std::list<std::shared_ptr<BaseFolderPair>> ComparisonBuffer::compareByContent(co
itemsTotal += bwl.filesToCompareBytewise.size();
for (const FilePair* file : bwl.filesToCompareBytewise)
- bytesTotal += file->getFileSize<LEFT_SIDE>(); //left and right file sizes are equal
+ bytesTotal += file->getFileSize<SelectSide::left>(); //left and right file sizes are equal
}
cb_.initNewPhase(itemsTotal, bytesTotal, ProcessPhase::comparingContent); //throw X
@@ -594,7 +595,7 @@ std::list<std::shared_ptr<BaseFolderPair>> ComparisonBuffer::compareByContent(co
BinaryWorkload& bwl = fpWorkload[j];
ParallelOps& posL = bwl.parallelOpsL;
ParallelOps& posR = bwl.parallelOpsR;
- const size_t newTaskCount = std::min<size_t>({ 1 - posL.current, 1 - posR.current, bwl.filesToCompareBytewise.size() });
+ const size_t newTaskCount = std::min<size_t>({1 - posL.current, 1 - posR.current, bwl.filesToCompareBytewise.size()});
if (&posL != &posR)
posL.current += newTaskCount; //
posR.current += newTaskCount; //consider aliasing!
@@ -659,7 +660,7 @@ public:
private:
void mergeTwoSides(const FolderContainer& lhs, const FolderContainer& rhs, const Zstringc* errorMsg, ContainerObject& output);
- template <SelectedSide side>
+ template <SelectSide side>
void fillOneSide(const FolderContainer& folderCont, const Zstringc* errorMsg, ContainerObject& output);
const Zstringc* checkFailedRead(FileSystemObject& fsObj, const Zstringc* errorMsg);
@@ -690,7 +691,7 @@ const Zstringc* MergeSides::checkFailedRead(FileSystemObject& fsObj, const Zstri
}
-template <SelectedSide side>
+template <SelectSide side>
void MergeSides::fillOneSide(const FolderContainer& folderCont, const Zstringc* errorMsg, ContainerObject& output)
{
for (const auto& [fileName, attrib] : folderCont.files)
@@ -726,8 +727,8 @@ void matchFolders(const MapType& mapLeft, const MapType& mapRight, ProcessLeftOn
std::vector<FileRef> fileList;
fileList.reserve(mapLeft.size() + mapRight.size()); //perf: ~5% shorter runtime
- for (const auto& item : mapLeft ) fileList.push_back({ getUpperCase(item.first), &item, true });
- for (const auto& item : mapRight) fileList.push_back({ getUpperCase(item.first), &item, false });
+ for (const auto& item : mapLeft ) fileList.push_back({getUpperCase(item.first), &item, true});
+ for (const auto& item : mapRight) fileList.push_back({getUpperCase(item.first), &item, false});
//primary sort: ignore unicode normal form and case
//bonus: natural default sequence on file guid UI
@@ -792,12 +793,12 @@ void MergeSides::mergeTwoSides(const FolderContainer& lhs, const FolderContainer
matchFolders(lhs.files, rhs.files, [&](const FileData& fileLeft, const Zstringc* conflictMsg)
{
- FilePair& newItem = output.addSubFile< LEFT_SIDE>(fileLeft .first, fileLeft .second);
+ FilePair& newItem = output.addSubFile< SelectSide::left>(fileLeft .first, fileLeft .second);
checkFailedRead(newItem, conflictMsg ? conflictMsg : errorMsg);
},
[&](const FileData& fileRight, const Zstringc* conflictMsg)
{
- FilePair& newItem = output.addSubFile<RIGHT_SIDE>(fileRight.first, fileRight.second);
+ FilePair& newItem = output.addSubFile<SelectSide::right>(fileRight.first, fileRight.second);
checkFailedRead(newItem, conflictMsg ? conflictMsg : errorMsg);
},
[&](const FileData& fileLeft, const FileData& fileRight)
@@ -817,12 +818,12 @@ void MergeSides::mergeTwoSides(const FolderContainer& lhs, const FolderContainer
matchFolders(lhs.symlinks, rhs.symlinks, [&](const SymlinkData& symlinkLeft, const Zstringc* conflictMsg)
{
- SymlinkPair& newItem = output.addSubLink< LEFT_SIDE>(symlinkLeft .first, symlinkLeft .second);
+ SymlinkPair& newItem = output.addSubLink< SelectSide::left>(symlinkLeft .first, symlinkLeft .second);
checkFailedRead(newItem, conflictMsg ? conflictMsg : errorMsg);
},
[&](const SymlinkData& symlinkRight, const Zstringc* conflictMsg)
{
- SymlinkPair& newItem = output.addSubLink<RIGHT_SIDE>(symlinkRight.first, symlinkRight.second);
+ SymlinkPair& newItem = output.addSubLink<SelectSide::right>(symlinkRight.first, symlinkRight.second);
checkFailedRead(newItem, conflictMsg ? conflictMsg : errorMsg);
},
[&](const SymlinkData& symlinkLeft, const SymlinkData& symlinkRight) //both sides
@@ -841,15 +842,15 @@ void MergeSides::mergeTwoSides(const FolderContainer& lhs, const FolderContainer
matchFolders(lhs.folders, rhs.folders, [&](const FolderData& dirLeft, const Zstringc* conflictMsg)
{
- FolderPair& newFolder = output.addSubFolder<LEFT_SIDE>(dirLeft.first, dirLeft.second.first);
+ FolderPair& newFolder = output.addSubFolder<SelectSide::left>(dirLeft.first, dirLeft.second.first);
const Zstringc* errorMsgNew = checkFailedRead(newFolder, conflictMsg ? conflictMsg : errorMsg);
- this->fillOneSide<LEFT_SIDE>(dirLeft.second.second, errorMsgNew, newFolder); //recurse
+ this->fillOneSide<SelectSide::left>(dirLeft.second.second, errorMsgNew, newFolder); //recurse
},
[&](const FolderData& dirRight, const Zstringc* conflictMsg)
{
- FolderPair& newFolder = output.addSubFolder<RIGHT_SIDE>(dirRight.first, dirRight.second.first);
+ FolderPair& newFolder = output.addSubFolder<SelectSide::right>(dirRight.first, dirRight.second.first);
const Zstringc* errorMsgNew = checkFailedRead(newFolder, conflictMsg ? conflictMsg : errorMsg);
- this->fillOneSide<RIGHT_SIDE>(dirRight.second.second, errorMsgNew, newFolder); //recurse
+ this->fillOneSide<SelectSide::right>(dirRight.second.second, errorMsgNew, newFolder); //recurse
},
[&](const FolderData& dirLeft, const FolderData& dirRight)
{
@@ -904,7 +905,7 @@ std::shared_ptr<BaseFolderPair> ComparisonBuffer::performComparison(const Resolv
auto getDirValue = [&](const AbstractPath& folderPath) -> const DirectoryValue*
{
- auto it = directoryBuffer_.find({ folderPath, fpCfg.filter.nameFilter, fpCfg.handleSymlinks });
+ auto it = directoryBuffer_.find({folderPath, fpCfg.filter.nameFilter, fpCfg.handleSymlinks});
return it != directoryBuffer_.end() ? &it->second : nullptr;
};
@@ -986,7 +987,7 @@ FolderComparison fff::compare(WarningDialogs& warnings,
//indicator at the very beginning of the log to make sense of "total time"
//init process: keep at beginning so that all gui elements are initialized properly
callback.initNewPhase(-1, -1, ProcessPhase::scanning); //throw X; it's unknown how many files will be scanned => -1 objects
- //callback.reportInfo(Comparison started")); -> still useful?
+ //callback.logInfo(Comparison started")); -> still useful?
//-------------------------------------------------------------------------------
@@ -1006,7 +1007,7 @@ FolderComparison fff::compare(WarningDialogs& warnings,
}
catch (const FileError& e) //failure is not critical => log only
{
- callback.reportInfo(e.toString()); //throw X
+ callback.logInfo(e.toString()); //throw X
}
const ResolvedBaseFolders& resInfo = initializeBaseFolders(fpCfgList,
@@ -1067,8 +1068,10 @@ FolderComparison fff::compare(WarningDialogs& warnings,
{
std::set<Zstring> folderPathsToLock;
for (const AbstractPath& folderPath : resInfo.existingBaseFolders)
- if (std::optional<Zstring> nativePath = AFS::getNativeItemPath(folderPath)) //restrict directory locking to native paths until further
- folderPathsToLock.insert(*nativePath);
+
+ if (const Zstring& nativePath = getNativeItemPath(folderPath); //restrict directory locking to native paths until further
+ !nativePath.empty())
+ folderPathsToLock.insert(nativePath);
dirLocks = std::make_unique<LockHolder>(folderPathsToLock, warnings.warnDirectoryLockFailed, callback);
}
@@ -1081,9 +1084,9 @@ FolderComparison fff::compare(WarningDialogs& warnings,
for (const auto& [folderPair, fpCfg] : workLoad)
{
if (basefolderExisting(folderPair.folderPathLeft)) //only traverse *currently existing* folders: at this point user is aware that non-ex + empty string are seen as empty folder!
- foldersToRead.emplace(DirectoryKey({ folderPair.folderPathLeft, fpCfg.filter.nameFilter, fpCfg.handleSymlinks }));
+ foldersToRead.emplace(DirectoryKey({folderPair.folderPathLeft, fpCfg.filter.nameFilter, fpCfg.handleSymlinks}));
if (basefolderExisting(folderPair.folderPathRight))
- foldersToRead.emplace(DirectoryKey({ folderPair.folderPathRight, fpCfg.filter.nameFilter, fpCfg.handleSymlinks }));
+ foldersToRead.emplace(DirectoryKey({folderPair.folderPathRight, fpCfg.filter.nameFilter, fpCfg.handleSymlinks}));
}
FolderComparison output;
@@ -1100,7 +1103,7 @@ FolderComparison fff::compare(WarningDialogs& warnings,
std::vector<std::pair<ResolvedFolderPair, FolderPairCfg>> workLoadByContent;
for (const auto& [folderPair, fpCfg] : workLoad)
if (fpCfg.compareVar == CompareVariant::content)
- workLoadByContent.push_back({ folderPair, fpCfg });
+ workLoadByContent.push_back({folderPair, fpCfg});
std::list<std::shared_ptr<BaseFolderPair>> outputByContent = cmpBuff.compareByContent(workLoadByContent);
diff --git a/FreeFileSync/Source/base/db_file.cpp b/FreeFileSync/Source/base/db_file.cpp
index ed32e884..27f47565 100644
--- a/FreeFileSync/Source/base/db_file.cpp
+++ b/FreeFileSync/Source/base/db_file.cpp
@@ -11,6 +11,7 @@
#include <zen/build_info.h>
#include <zen/zlib_wrap.h>
#include "../afs/concrete.h"
+#include "../afs/native.h"
#include "status_handler_impl.h"
@@ -23,7 +24,7 @@ namespace
//-------------------------------------------------------------------------------------------------------------------------------
const char DB_FILE_DESCR[] = "FreeFileSync";
const int DB_FILE_VERSION = 11; //2020-02-07
-const int DB_STREAM_VERSION = 3; //2017-02-01
+const int DB_STREAM_VERSION = 4; //2021-02-14
//-------------------------------------------------------------------------------------------------------------------------------
DEFINE_NEW_FILE_ERROR(FileErrorDatabaseNotExisting)
@@ -44,7 +45,7 @@ using DbStreams = std::unordered_map<UniqueId, SessionData>; //list of streams b
| ensure 32/64 bit portability: use fixed size data types only e.g. uint32_t |
------------------------------------------------------------------------------*/
-template <SelectedSide side> inline
+template <SelectSide side> inline
AbstractPath getDatabaseFilePath(const BaseFolderPair& baseFolder)
{
static_assert(std::endian::native == std::endian::little);
@@ -57,13 +58,13 @@ AbstractPath getDatabaseFilePath(const BaseFolderPair& baseFolder)
- 32 vs 64-bit: already handled
=> give db files different names: */
- const Zstring dbName = Zstr(".sync"); //files beginning with dots are hidden e.g. in Nautilus
+ const Zstring dbName = Zstr(".sync"); //files beginning with dots are usually hidden
return AFS::appendRelPath(baseFolder.getAbstractPath<side>(), dbName + SYNC_DB_FILE_ENDING);
}
//#######################################################################################################################################
-void saveStreams(const DbStreams& streamList, const AbstractPath& dbPath, const IOCallback& notifyUnbufferedIO /*throw X*/) //throw FileError, X
+void saveStreams(const DbStreams& streamList, const AbstractPath& dbPath, const IoCallback& notifyUnbufferedIO /*throw X*/) //throw FileError, X
{
MemoryStreamOut<std::string> memStreamOut;
@@ -98,7 +99,7 @@ void saveStreams(const DbStreams& streamList, const AbstractPath& dbPath, const
}
-DbStreams loadStreams(const AbstractPath& dbPath, const IOCallback& notifyUnbufferedIO /*throw X*/) //throw FileError, FileErrorDatabaseNotExisting, X
+DbStreams loadStreams(const AbstractPath& dbPath, const IoCallback& notifyUnbufferedIO /*throw X*/) //throw FileError, FileErrorDatabaseNotExisting, X
{
std::string byteStream;
try
@@ -133,7 +134,7 @@ DbStreams loadStreams(const AbstractPath& dbPath, const IOCallback& notifyUnbuff
if (version == 9 || //TODO: remove migration code at some time! v9 used until 2017-02-01
version == 10) //TODO: remove migration code at some time! v10 used until 2020-02-07
;
- else if (version == DB_FILE_VERSION)//catch data corruption ASAP + don't rely on std::bad_alloc for consistency checking
+ else if (version == DB_FILE_VERSION) //catch data corruption ASAP + don't rely on std::bad_alloc for consistency checking
// => only "partially" useful for container/stream metadata since the streams data is zlib-compressed
{
assert(byteStream.size() >= sizeof(uint32_t)); //obviously in this context!
@@ -273,8 +274,8 @@ private:
writeItemName(itemName);
writeNumber(streamOutSmallNum_, static_cast<int32_t>(inSyncData.cmpVar));
- writeLinkDescr(inSyncData.left);
- writeLinkDescr(inSyncData.right);
+ writeNumber<int64_t>(streamOutBigNum_, inSyncData.left .modTime);
+ writeNumber<int64_t>(streamOutBigNum_, inSyncData.right.modTime);
}
writeNumber<uint32_t>(streamOutSmallNum_, static_cast<uint32_t>(container.folders.size()));
@@ -291,13 +292,11 @@ private:
void writeFileDescr(const InSyncDescrFile& descr)
{
- writeNumber<int64_t>(streamOutBigNum_, descr.modTime);
- writeContainer<std::string>(streamOutBigNum_, descr.fileId);
+ writeNumber<int64_t >(streamOutBigNum_, descr.modTime);
+ writeNumber<AFS::FingerPrint>(streamOutBigNum_, descr.filePrint);
static_assert(sizeof(descr.modTime) <= sizeof(int64_t)); //ensure cross-platform compatibility!
}
- void writeLinkDescr(const InSyncDescrLink& descr) { writeNumber<int64_t>(streamOutBigNum_, descr.modTime); }
-
/* maximize zlib compression by grouping similar data (=> 20% size reduction!)
-> further ~5% reduction possible by having one container per data type
@@ -364,7 +363,8 @@ public:
parser.recurse(output.ref()); //throw SysError
return output;
}
- else if (streamVersion == DB_STREAM_VERSION)
+ else if (streamVersion == 3 || //TODO: remove migration code at some time! 2021-02-14
+ streamVersion == DB_STREAM_VERSION)
{
MemoryStreamIn<std::string>& streamInPart1 = leadStreamLeft ? streamInL : streamInR;
MemoryStreamIn<std::string>& streamInPart2 = leadStreamLeft ? streamInR : streamInL;
@@ -387,9 +387,9 @@ public:
decompress(bufSmallNum), //throw SysError
decompress(bufBigNum)); //
if (leadStreamLeft)
- parser.recurse<LEFT_SIDE>(output.ref()); //throw SysError
+ parser.recurse<SelectSide::left>(output.ref()); //throw SysError
else
- parser.recurse<RIGHT_SIDE>(output.ref()); //throw SysError
+ parser.recurse<SelectSide::right>(output.ref()); //throw SysError
return output;
}
else
@@ -406,23 +406,20 @@ private:
streamVersion_(streamVersion),
streamInText_(bufText),
streamInSmallNum_(bufSmallNumbers),
- streamInBigNum_(bufBigNumbers)
- {
- (void)streamVersion_; //clang: -Wunused-private-field
- }
+ streamInBigNum_(bufBigNumbers) {}
- template <SelectedSide leadSide>
+ template <SelectSide leadSide>
void recurse(InSyncFolder& container) //throw SysError
{
- size_t fileCount = readNumber<uint32_t>(streamInSmallNum_);
+ size_t fileCount = readNumber<uint32_t>(streamInSmallNum_); //throw SysErrorUnexpectedEos
while (fileCount-- != 0)
{
- const Zstring itemName = readItemName();
- const auto cmpVar = static_cast<CompareVariant>(readNumber<int32_t>(streamInSmallNum_));
- const uint64_t fileSize = readNumber<uint64_t>(streamInSmallNum_);
+ const Zstring itemName = readItemName(); //
+ const auto cmpVar = static_cast<CompareVariant>(readNumber<int32_t>(streamInSmallNum_)); //
+ const uint64_t fileSize = readNumber<uint64_t>(streamInSmallNum_); //
- const InSyncDescrFile dataL = readFileDescr();
- const InSyncDescrFile dataT = readFileDescr();
+ const InSyncDescrFile dataL = readFileDescr(); //throw SysErrorUnexpectedEos
+ const InSyncDescrFile dataT = readFileDescr(); //
container.addFile(itemName,
SelectParam<leadSide>::ref(dataL, dataT),
@@ -432,22 +429,22 @@ private:
size_t linkCount = readNumber<uint32_t>(streamInSmallNum_);
while (linkCount-- != 0)
{
- const Zstring itemName = readItemName();
- const auto cmpVar = static_cast<CompareVariant>(readNumber<int32_t>(streamInSmallNum_));
+ const Zstring itemName = readItemName(); //
+ const auto cmpVar = static_cast<CompareVariant>(readNumber<int32_t>(streamInSmallNum_)); //
- const InSyncDescrLink dataL = readLinkDescr();
- const InSyncDescrLink dataT = readLinkDescr();
+ const InSyncDescrLink dataL(readNumber<int64_t>(streamInBigNum_)); //throw SysErrorUnexpectedEos
+ const InSyncDescrLink dataT(readNumber<int64_t>(streamInBigNum_)); //
container.addSymlink(itemName,
SelectParam<leadSide>::ref(dataL, dataT),
SelectParam<leadSide>::ref(dataT, dataL), cmpVar);
}
- size_t dirCount = readNumber<uint32_t>(streamInSmallNum_);
+ size_t dirCount = readNumber<uint32_t>(streamInSmallNum_); //
while (dirCount-- != 0)
{
- const Zstring itemName = readItemName();
- const auto status = static_cast<InSyncFolder::InSyncStatus>(readNumber<int32_t>(streamInSmallNum_));
+ const Zstring itemName = readItemName(); //
+ const auto status = static_cast<InSyncFolder::InSyncStatus>(readNumber<int32_t>(streamInSmallNum_)); //
InSyncFolder& dbFolder = container.addFolder(itemName, status);
recurse<leadSide>(dbFolder);
@@ -458,17 +455,24 @@ private:
InSyncDescrFile readFileDescr() //throw SysErrorUnexpectedEos
{
- //attention: order of function argument evaluation is undefined! So do it one after the other...
- const auto modTime = readNumber<int64_t>(streamInBigNum_); //throw SysErrorUnexpectedEos
- const auto fileId = readContainer<std::string>(streamInBigNum_); //
+ const auto modTime = readNumber<int64_t>(streamInBigNum_); //throw SysErrorUnexpectedEos
- return InSyncDescrFile(modTime, fileId);
- }
+ AFS::FingerPrint filePrint = 0;
+ if (streamVersion_ == 3) //TODO: remove migration code at some time! 2021-02-14
+ {
+ const auto& devFileId = readContainer<std::string>(streamInBigNum_); //throw SysErrorUnexpectedEos
+ ino_t fileIndex = 0;
+ if (devFileId.size() == sizeof(dev_t) + sizeof(fileIndex))
+ {
+ std::memcpy(&fileIndex, &devFileId[devFileId.size() - sizeof(fileIndex)], sizeof(fileIndex));
+ filePrint = fileIndex;
+ }
+ else assert(devFileId.empty());
+ }
+ else
+ filePrint = readNumber<AFS::FingerPrint>(streamInBigNum_); //throw SysErrorUnexpectedEos
- InSyncDescrLink readLinkDescr() //throw SysErrorUnexpectedEos
- {
- const auto modTime = readNumber<int64_t>(streamInBigNum_); //throw SysErrorUnexpectedEos
- return InSyncDescrLink(modTime);
+ return InSyncDescrFile(modTime, filePrint);
}
//TODO: remove migration code at some time! 2017-02-01
@@ -491,10 +495,10 @@ private:
const auto cmpVar = static_cast<CompareVariant>(readNumber<int32_t>(inputBoth_));
const uint64_t fileSize = readNumber<uint64_t>(inputBoth_);
const auto modTimeL = readNumber<int64_t>(inputLeft_);
- const auto fileIdL = readContainer<std::string>(inputLeft_);
+ /*const auto fileIdL =*/ readContainer<std::string>(inputLeft_);
const auto modTimeR = readNumber<int64_t>(inputRight_);
- const auto fileIdR = readContainer<std::string>(inputRight_);
- container.addFile(itemName, InSyncDescrFile(modTimeL, fileIdL), InSyncDescrFile(modTimeR, fileIdR), cmpVar, fileSize);
+ /*const auto fileIdR =*/ readContainer<std::string>(inputRight_);
+ container.addFile(itemName, InSyncDescrFile(modTimeL, AFS::FingerPrint()), InSyncDescrFile(modTimeR, AFS::FingerPrint()), cmpVar, fileSize);
}
size_t linkCount = readNumber<uint32_t>(inputBoth_);
@@ -568,23 +572,23 @@ private:
{
//Caveat: If FILE_EQUAL, we *implicitly* assume equal left and right short names matching case: InSyncFolder's mapping tables use short name as a key!
//This makes us silently dependent from code in algorithm.h!!!
- assert(getUnicodeNormalForm(file.getItemName<LEFT_SIDE>()) == getUnicodeNormalForm(file.getItemName<RIGHT_SIDE>()));
- assert(file.getFileSize<LEFT_SIDE>() == file.getFileSize<RIGHT_SIDE>());
+ assert(getUnicodeNormalForm(file.getItemName<SelectSide::left>()) == getUnicodeNormalForm(file.getItemName<SelectSide::right>()));
+ assert(file.getFileSize<SelectSide::left>() == file.getFileSize<SelectSide::right>());
//create or update new "in-sync" state
dbFiles.insert_or_assign(file.getItemNameAny(),
- InSyncFile(InSyncDescrFile(file.getLastWriteTime< LEFT_SIDE>(),
- file.getFileId < LEFT_SIDE>()),
- InSyncDescrFile(file.getLastWriteTime<RIGHT_SIDE>(),
- file.getFileId <RIGHT_SIDE>()),
+ InSyncFile(InSyncDescrFile(file.getLastWriteTime< SelectSide::left>(),
+ file.getFilePrint < SelectSide::left>()),
+ InSyncDescrFile(file.getLastWriteTime<SelectSide::right>(),
+ file.getFilePrint <SelectSide::right>()),
activeCmpVar_,
- file.getFileSize<LEFT_SIDE>()));
+ file.getFileSize<SelectSide::left>()));
toPreserve.insert(file.getItemNameAny());
}
else //not in sync: preserve last synchronous state
{
- toPreserve.insert(file.getItemName< LEFT_SIDE>()); //left/right may differ in case!
- toPreserve.insert(file.getItemName<RIGHT_SIDE>()); //
+ toPreserve.insert(file.getItemName< SelectSide::left>()); //left/right may differ in case!
+ toPreserve.insert(file.getItemName<SelectSide::right>()); //
}
}
@@ -609,19 +613,19 @@ private:
{
if (symlink.getLinkCategory() == SYMLINK_EQUAL) //data in sync: write current state
{
- assert(getUnicodeNormalForm(symlink.getItemName<LEFT_SIDE>()) == getUnicodeNormalForm(symlink.getItemName<RIGHT_SIDE>()));
+ assert(getUnicodeNormalForm(symlink.getItemName<SelectSide::left>()) == getUnicodeNormalForm(symlink.getItemName<SelectSide::right>()));
//create or update new "in-sync" state
dbSymlinks.insert_or_assign(symlink.getItemNameAny(),
- InSyncSymlink(InSyncDescrLink(symlink.getLastWriteTime< LEFT_SIDE>()),
- InSyncDescrLink(symlink.getLastWriteTime<RIGHT_SIDE>()),
+ InSyncSymlink(InSyncDescrLink(symlink.getLastWriteTime< SelectSide::left>()),
+ InSyncDescrLink(symlink.getLastWriteTime<SelectSide::right>()),
activeCmpVar_));
toPreserve.insert(symlink.getItemNameAny());
}
else //not in sync: preserve last synchronous state
{
- toPreserve.insert(symlink.getItemName< LEFT_SIDE>()); //left/right may differ in case!
- toPreserve.insert(symlink.getItemName<RIGHT_SIDE>()); //
+ toPreserve.insert(symlink.getItemName< SelectSide::left>()); //left/right may differ in case!
+ toPreserve.insert(symlink.getItemName<SelectSide::right>()); //
}
}
@@ -645,7 +649,7 @@ private:
{
if (folder.getDirCategory() == DIR_EQUAL)
{
- assert(getUnicodeNormalForm(folder.getItemName<LEFT_SIDE>()) == getUnicodeNormalForm(folder.getItemName<RIGHT_SIDE>()));
+ assert(getUnicodeNormalForm(folder.getItemName<SelectSide::left>()) == getUnicodeNormalForm(folder.getItemName<SelectSide::right>()));
//update directory entry only (shallow), but do *not touch* existing child elements!!!
InSyncFolder& dbFolder = dbFolders.emplace(folder.getItemNameAny(), InSyncFolder(InSyncFolder::DIR_STATUS_IN_SYNC)).first->second; //get or create
@@ -655,8 +659,8 @@ private:
}
else //not in sync: preserve last synchronous state
{
- toPreserve.emplace(folder.getItemName< LEFT_SIDE>(), &folder); //names differing in case? => treat like any other folder rename
- toPreserve.emplace(folder.getItemName<RIGHT_SIDE>(), &folder); //=> no *new* database entries even if child items are in sync
+ toPreserve.emplace(folder.getItemName< SelectSide::left>(), &folder); //names differing in case? => treat like any other folder rename
+ toPreserve.emplace(folder.getItemName<SelectSide::right>(), &folder); //=> no *new* database entries even if child items are in sync
}
}
@@ -749,7 +753,7 @@ std::pair<DbStreams::const_iterator,
}
}
- return { itCommonL, itCommonR };
+ return {itCommonL, itCommonR};
}
}
@@ -762,11 +766,11 @@ std::unordered_map<const BaseFolderPair*, SharedRef<const InSyncFolder>> fff::lo
for (const BaseFolderPair* baseFolder : baseFolders)
//avoid race condition with directory existence check: reading sync.ffs_db may succeed although first dir check had failed => conflicts!
- if (baseFolder->isAvailable< LEFT_SIDE>() &&
- baseFolder->isAvailable<RIGHT_SIDE>())
+ if (baseFolder->isAvailable< SelectSide::left>() &&
+ baseFolder->isAvailable<SelectSide::right>())
{
- dbFilePaths.insert(getDatabaseFilePath< LEFT_SIDE>(*baseFolder));
- dbFilePaths.insert(getDatabaseFilePath<RIGHT_SIDE>(*baseFolder));
+ dbFilePaths.insert(getDatabaseFilePath< SelectSide::left>(*baseFolder));
+ dbFilePaths.insert(getDatabaseFilePath<SelectSide::right>(*baseFolder));
}
//else: ignore; there's no value in reporting it other than to confuse users
@@ -801,11 +805,11 @@ std::unordered_map<const BaseFolderPair*, SharedRef<const InSyncFolder>> fff::lo
std::unordered_map<const BaseFolderPair*, SharedRef<const InSyncFolder>> output;
for (const BaseFolderPair* baseFolder : baseFolders)
- if (baseFolder->isAvailable< LEFT_SIDE>() &&
- baseFolder->isAvailable<RIGHT_SIDE>())
+ if (baseFolder->isAvailable< SelectSide::left>() &&
+ baseFolder->isAvailable<SelectSide::right>())
{
- const AbstractPath dbPathL = getDatabaseFilePath< LEFT_SIDE>(*baseFolder);
- const AbstractPath dbPathR = getDatabaseFilePath<RIGHT_SIDE>(*baseFolder);
+ const AbstractPath dbPathL = getDatabaseFilePath< SelectSide::left>(*baseFolder);
+ const AbstractPath dbPathR = getDatabaseFilePath<SelectSide::right>(*baseFolder);
auto itL = dbStreamsByPath.find(dbPathL);
auto itR = dbStreamsByPath.find(dbPathR);
@@ -842,8 +846,8 @@ std::unordered_map<const BaseFolderPair*, SharedRef<const InSyncFolder>> fff::lo
void fff::saveLastSynchronousState(const BaseFolderPair& baseFolder, bool transactionalCopy,
PhaseCallback& callback /*throw X*/) //throw X
{
- const AbstractPath dbPathL = getDatabaseFilePath< LEFT_SIDE>(baseFolder);
- const AbstractPath dbPathR = getDatabaseFilePath<RIGHT_SIDE>(baseFolder);
+ const AbstractPath dbPathL = getDatabaseFilePath< SelectSide::left>(baseFolder);
+ const AbstractPath dbPathR = getDatabaseFilePath<SelectSide::right>(baseFolder);
//------------ (try to) load DB files in parallel -------------------------
DbStreams streamsL; //list of session ID + DirInfo-stream
diff --git a/FreeFileSync/Source/base/db_file.h b/FreeFileSync/Source/base/db_file.h
index f9e0dabb..49d14813 100644
--- a/FreeFileSync/Source/base/db_file.h
+++ b/FreeFileSync/Source/base/db_file.h
@@ -19,12 +19,12 @@ const Zchar SYNC_DB_FILE_ENDING[] = Zstr(".ffs_db"); //don't use Zstring as glob
struct InSyncDescrFile //subset of FileAttributes
{
- InSyncDescrFile(time_t modTimeIn, const AFS::FileId& idIn) :
+ InSyncDescrFile(time_t modTimeIn, AFS::FingerPrint filePrintIn) :
modTime(modTimeIn),
- fileId(idIn) {}
+ filePrint(filePrintIn) {}
time_t modTime = 0;
- AFS::FileId fileId; // == file id: optional! (however, always set on Linux, and *generally* available on Windows)
+ AFS::FingerPrint filePrint = 0; //optional!
};
struct InSyncDescrLink
diff --git a/FreeFileSync/Source/base/dir_exist_async.h b/FreeFileSync/Source/base/dir_exist_async.h
index 98430b2e..593dc3b9 100644
--- a/FreeFileSync/Source/base/dir_exist_async.h
+++ b/FreeFileSync/Source/base/dir_exist_async.h
@@ -53,7 +53,8 @@ FolderStatus getFolderStatusNonBlocking(const std::set<AbstractPath>& folderPath
threadGroup.detach(); //don't wait on threads hanging longer than "folderAccessTimeout"
//1. login to network share, connect with Google Drive, etc.
- std::shared_future<void> ftAuth = runAsync([afsDevice /*clang bug*/= afsDevice, allowUserInteraction] { AFS::authenticateAccess(afsDevice, allowUserInteraction); /*throw FileError*/ });
+ std::shared_future<void> ftAuth = runAsync([afsDevice /*clang bug*/= afsDevice, allowUserInteraction]
+ { AFS::authenticateAccess(afsDevice, allowUserInteraction); /*throw FileError*/ });
for (const AbstractPath& folderPath : deviceFolderPaths)
{
diff --git a/FreeFileSync/Source/base/dir_lock.cpp b/FreeFileSync/Source/base/dir_lock.cpp
index de392fe9..f70cf513 100644
--- a/FreeFileSync/Source/base/dir_lock.cpp
+++ b/FreeFileSync/Source/base/dir_lock.cpp
@@ -96,12 +96,13 @@ private:
{
try
{
+#if 1
const int fdLockFile = ::open(lockFilePath_.c_str(), O_WRONLY | O_APPEND | O_CLOEXEC);
if (fdLockFile == -1)
THROW_LAST_SYS_ERROR("open");
ZEN_ON_SCOPE_EXIT(::close(fdLockFile));
-#if 0//alternative using lseek => no apparent benefit https://freefilesync.org/forum/viewtopic.php?t=7553#p25505
+#else //alternative using lseek => no apparent benefit https://freefilesync.org/forum/viewtopic.php?t=7553#p25505
const int fdLockFile = ::open(lockFilePath_.c_str(), O_WRONLY | O_CLOEXEC);
if (fdLockFile == -1)
THROW_LAST_SYS_ERROR("open");
@@ -302,7 +303,7 @@ ProcessStatus getProcessStatus(const LockInformation& lockInfo) //throw FileErro
DEFINE_NEW_FILE_ERROR(ErrorFileNotExisting)
uint64_t getLockFileSize(const Zstring& filePath) //throw FileError, ErrorFileNotExisting
{
- struct ::stat fileInfo = {};
+ struct stat fileInfo = {};
if (::stat(filePath.c_str(), &fileInfo) == 0)
return fileInfo.st_size;
@@ -316,7 +317,7 @@ void waitOnDirLock(const Zstring& lockFilePath, const DirLockCallback& notifySta
{
std::wstring infoMsg = _("Waiting while directory is in use:") + L' ' + fmtPath(lockFilePath);
- if (notifyStatus) notifyStatus(infoMsg); //throw X
+ if (notifyStatus) notifyStatus(std::wstring(infoMsg)); //throw X
//convenience optimization only: if we know the owning process crashed, we needn't wait DETECT_ABANDONED_INTERVAL sec
bool lockOwnderDead = false;
@@ -399,7 +400,7 @@ void waitOnDirLock(const Zstring& lockFilePath, const DirLockCallback& notifySta
notifyStatus(infoMsg + L" | " + _("Detecting abandoned lock...") + L' ' + _P("1 sec", "%x sec", remainingSeconds)); //throw X
}
else
- notifyStatus(infoMsg); //throw X; emit a message in any case (might clear other one)
+ notifyStatus(std::wstring(infoMsg)); //throw X; emit a message in any case (might clear other one)
}
std::this_thread::sleep_for(cbInterval);
}
@@ -419,10 +420,17 @@ void releaseLock(const Zstring& lockFilePath) //noexcept
bool tryLock(const Zstring& lockFilePath) //throw FileError
{
+ //important: we want the lock file to have exactly the permissions specified
+ //=> yes, disabling umask() is messy (per-process!), but fchmod() may not be supported: https://freefilesync.org/forum/viewtopic.php?t=8096
+ const mode_t oldMask = ::umask(0); //always succeeds
+ ZEN_ON_SCOPE_EXIT(::umask(oldMask));
+
const mode_t lockFileMode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; //0666
//O_EXCL contains a race condition on NFS file systems: https://linux.die.net/man/2/open
- const int hFile = ::open(lockFilePath.c_str(), O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, lockFileMode);
+ const int hFile = ::open(lockFilePath.c_str(), //const char* pathname
+ O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, //int flags
+ lockFileMode); //mode_t mode
if (hFile == -1)
{
if (errno == EEXIST)
@@ -432,10 +440,6 @@ bool tryLock(const Zstring& lockFilePath) //throw FileError
}
FileOutput fileOut(hFile, lockFilePath, nullptr /*notifyUnbufferedIO*/); //pass handle ownership
- //consider umask! we want the lock file to have exactly the permissions specified
- if (::fchmod(hFile, lockFileMode) != 0)
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtPath(lockFilePath)), "fchmod");
-
//write housekeeping info: user, process info, lock GUID
const std::string byteStream = serialize(getLockInfoFromCurrentProcess()); //throw FileError
diff --git a/FreeFileSync/Source/base/dir_lock.h b/FreeFileSync/Source/base/dir_lock.h
index 269dc7d1..87b3a7e6 100644
--- a/FreeFileSync/Source/base/dir_lock.h
+++ b/FreeFileSync/Source/base/dir_lock.h
@@ -25,7 +25,7 @@ namespace fff
- NOT thread-safe! (1. global LockAdmin 2. locks for directory aliases should be created sequentially to detect duplicate locks!) */
//while waiting for the lock
-using DirLockCallback = std::function<void(const std::wstring& msg)>; //throw X
+using DirLockCallback = std::function<void(std::wstring&& msg)>; //throw X
class DirLock
{
diff --git a/FreeFileSync/Source/base/file_hierarchy.cpp b/FreeFileSync/Source/base/file_hierarchy.cpp
index 6e184b97..0aa176cb 100644
--- a/FreeFileSync/Source/base/file_hierarchy.cpp
+++ b/FreeFileSync/Source/base/file_hierarchy.cpp
@@ -186,13 +186,13 @@ bool hasDirectChild(const ContainerObject& hierObj, Predicate p)
SyncOperation FileSystemObject::testSyncOperation(SyncDirection testSyncDir) const //semantics: "what if"! assumes "active, no conflict, no recursion (directory)!
{
- return getIsolatedSyncOperation(!isEmpty<LEFT_SIDE>(), !isEmpty<RIGHT_SIDE>(), getCategory(), true, testSyncDir, false);
+ return getIsolatedSyncOperation(!isEmpty<SelectSide::left>(), !isEmpty<SelectSide::right>(), getCategory(), true, testSyncDir, false);
}
SyncOperation FileSystemObject::getSyncOperation() const
{
- return getIsolatedSyncOperation(!isEmpty<LEFT_SIDE>(), !isEmpty<RIGHT_SIDE>(), getCategory(), selectedForSync_, getSyncDir(), !syncDirectionConflict_.empty());
+ return getIsolatedSyncOperation(!isEmpty<SelectSide::left>(), !isEmpty<SelectSide::right>(), getCategory(), selectedForSync_, getSyncDir(), !syncDirectionConflict_.empty());
//do *not* make a virtual call to testSyncOperation()! See FilePair::testSyncOperation()! <- better not implement one in terms of the other!!!
}
@@ -228,7 +228,7 @@ SyncOperation FolderPair::getSyncOperation() const
case SO_DELETE_RIGHT:
case SO_DO_NOTHING:
case SO_UNRESOLVED_CONFLICT:
- if (isEmpty<LEFT_SIDE>())
+ if (isEmpty<SelectSide::left>())
{
//1. if at least one child-element is to be created, make sure parent folder is created also
//note: this automatically fulfills "create parent folders even if excluded"
@@ -253,7 +253,7 @@ SyncOperation FolderPair::getSyncOperation() const
}))
syncOpBuffered_ = SO_DO_NOTHING;
}
- else if (isEmpty<RIGHT_SIDE>())
+ else if (isEmpty<SelectSide::right>())
{
if (hasDirectChild(*this,
[](const FileSystemObject& fsObj)
@@ -285,10 +285,8 @@ SyncOperation FolderPair::getSyncOperation() const
inline //called by private only!
SyncOperation FilePair::applyMoveOptimization(SyncOperation op) const
{
- /*
- check whether we can optimize "create + delete" via "move":
- note: as long as we consider "create + delete" cases only, detection of renamed files, should be fine even for "binary" comparison variant!
- */
+ /* check whether we can optimize "create + delete" via "move":
+ note: as long as we consider "create + delete" cases only, detection of renamed files, should be fine even for "binary" comparison variant! */
if (moveFileRef_)
if (auto refFile = dynamic_cast<const FilePair*>(FileSystemObject::retrieve(moveFileRef_))) //we expect a "FilePair", but only need a "FileSystemObject" here
if (refFile->moveFileRef_ == getId()) //both ends should agree...
@@ -379,14 +377,14 @@ std::wstring fff::getCategoryDescription(const FileSystemObject& fsObj)
[&](const FilePair& file)
{
descr += std::wstring(L"\n") +
- arrowLeft + L' ' + formatUtcToLocalTime(file.getLastWriteTime< LEFT_SIDE>()) + L'\n' +
- arrowRight + L' ' + formatUtcToLocalTime(file.getLastWriteTime<RIGHT_SIDE>());
+ arrowLeft + L' ' + formatUtcToLocalTime(file.getLastWriteTime< SelectSide::left>()) + L'\n' +
+ arrowRight + L' ' + formatUtcToLocalTime(file.getLastWriteTime<SelectSide::right>());
},
[&](const SymlinkPair& symlink)
{
descr += std::wstring(L"\n") +
- arrowLeft + L' ' + formatUtcToLocalTime(symlink.getLastWriteTime< LEFT_SIDE>()) + L'\n' +
- arrowRight + L' ' + formatUtcToLocalTime(symlink.getLastWriteTime<RIGHT_SIDE>());
+ arrowLeft + L' ' + formatUtcToLocalTime(symlink.getLastWriteTime< SelectSide::left>()) + L'\n' +
+ arrowRight + L' ' + formatUtcToLocalTime(symlink.getLastWriteTime<SelectSide::right>());
});
return descr + footer;
}
@@ -459,8 +457,8 @@ std::wstring fff::getSyncOpDescription(const FileSystemObject& fsObj)
case SO_COPY_METADATA_TO_RIGHT:
//harmonize with synchronization.cpp::FolderPairSyncer::synchronizeFileInt, ect!!
{
- Zstring itemNameOld = fsObj.getItemName<RIGHT_SIDE>();
- Zstring itemNameNew = fsObj.getItemName< LEFT_SIDE>();
+ Zstring itemNameOld = fsObj.getItemName<SelectSide::right>();
+ Zstring itemNameNew = fsObj.getItemName< SelectSide::left>();
if (op == SO_COPY_METADATA_TO_LEFT)
std::swap(itemNameOld, itemNameNew);
@@ -486,7 +484,7 @@ std::wstring fff::getSyncOpDescription(const FileSystemObject& fsObj)
if (!isMoveSource)
std::swap(fileFrom, fileTo);
- auto getRelName = [&](const FileSystemObject& fso, bool leftSide) { return leftSide ? fso.getRelativePath<LEFT_SIDE>() : fso.getRelativePath<RIGHT_SIDE>(); };
+ auto getRelName = [&](const FileSystemObject& fso, bool leftSide) { return leftSide ? fso.getRelativePath<SelectSide::left>() : fso.getRelativePath<SelectSide::right>(); };
const Zstring relPathFrom = getRelName(*fileFrom, onLeft);
const Zstring relPathTo = getRelName(*fileTo, onLeft);
@@ -511,6 +509,3 @@ std::wstring fff::getSyncOpDescription(const FileSystemObject& fsObj)
assert(false);
return std::wstring();
}
-
-
-warn_static(" FileSystemObject::isEmpty => rename: exists()!?")
diff --git a/FreeFileSync/Source/base/file_hierarchy.h b/FreeFileSync/Source/base/file_hierarchy.h
index 52000203..45c672f6 100644
--- a/FreeFileSync/Source/base/file_hierarchy.h
+++ b/FreeFileSync/Source/base/file_hierarchy.h
@@ -15,7 +15,6 @@
#include <unordered_set>
#include <zen/zstring.h>
#include <zen/stl_tools.h>
-#include <zen/file_id_def.h>
#include "structures.h"
#include "path_filter.h"
#include "../afs/abstract.h"
@@ -28,19 +27,19 @@ struct FileAttributes
FileAttributes() {}
FileAttributes(time_t modTimeIn,
uint64_t fileSizeIn,
- const AFS::FileId& idIn,
- bool isSymlink) :
+ AFS::FingerPrint filePrintIn,
+ bool followedSymlink) :
modTime(modTimeIn),
fileSize(fileSizeIn),
- fileId(idIn),
- isFollowedSymlink(isSymlink)
+ filePrint(filePrintIn),
+ isFollowedSymlink(followedSymlink)
{
static_assert(std::is_signed_v<time_t>, "... and signed!");
}
time_t modTime = 0; //number of seconds since Jan. 1st 1970 UTC
uint64_t fileSize = 0;
- AFS::FileId fileId; //optional!
+ AFS::FingerPrint filePrint = 0; //optional
bool isFollowedSymlink = false;
std::strong_ordering operator<=>(const FileAttributes&) const = default;
@@ -66,34 +65,34 @@ struct FolderAttributes
};
-enum SelectedSide
+enum class SelectSide
{
- LEFT_SIDE,
- RIGHT_SIDE
+ left,
+ right
};
-template <SelectedSide side>
+template <SelectSide side>
struct OtherSide;
template <>
-struct OtherSide<LEFT_SIDE> { static const SelectedSide value = RIGHT_SIDE; };
+struct OtherSide<SelectSide::left> { static const SelectSide value = SelectSide::right; };
template <>
-struct OtherSide<RIGHT_SIDE> { static const SelectedSide value = LEFT_SIDE; };
+struct OtherSide<SelectSide::right> { static const SelectSide value = SelectSide::left; };
-template <SelectedSide side>
+template <SelectSide side>
struct SelectParam;
template <>
-struct SelectParam<LEFT_SIDE>
+struct SelectParam<SelectSide::left>
{
template <class T>
static T& ref(T& left, T& right) { return left; }
};
template <>
-struct SelectParam<RIGHT_SIDE>
+struct SelectParam<SelectSide::right>
{
template <class T>
static T& ref(T& left, T& right) { return right; }
@@ -173,8 +172,8 @@ struct PathInformation //diamond-shaped inheritence!
{
virtual ~PathInformation() {}
- template <SelectedSide side> AbstractPath getAbstractPath() const;
- template <SelectedSide side> Zstring getRelativePath() const; //get path relative to base sync dir (without leading/trailing FILE_NAME_SEPARATOR)
+ template <SelectSide side> AbstractPath getAbstractPath() const;
+ template <SelectSide side> Zstring getRelativePath() const; //get path relative to base sync dir (without leading/trailing FILE_NAME_SEPARATOR)
Zstring getRelativePathAny() const { return getRelativePathL(); } //side doesn't matter
private:
@@ -185,11 +184,11 @@ private:
virtual Zstring getRelativePathR() const = 0; //
};
-template <> inline AbstractPath PathInformation::getAbstractPath< LEFT_SIDE>() const { return getAbstractPathL(); }
-template <> inline AbstractPath PathInformation::getAbstractPath<RIGHT_SIDE>() const { return getAbstractPathR(); }
+template <> inline AbstractPath PathInformation::getAbstractPath< SelectSide::left>() const { return getAbstractPathL(); }
+template <> inline AbstractPath PathInformation::getAbstractPath<SelectSide::right>() const { return getAbstractPathR(); }
-template <> inline Zstring PathInformation::getRelativePath< LEFT_SIDE>() const { return getRelativePathL(); }
-template <> inline Zstring PathInformation::getRelativePath<RIGHT_SIDE>() const { return getRelativePathR(); }
+template <> inline Zstring PathInformation::getRelativePath< SelectSide::left>() const { return getRelativePathL(); }
+template <> inline Zstring PathInformation::getRelativePath<SelectSide::right>() const { return getRelativePathR(); }
//------------------------------------------------------------------
@@ -209,7 +208,7 @@ public:
const Zstring& itemNameR,
const FolderAttributes& right);
- template <SelectedSide side>
+ template <SelectSide side>
FolderPair& addSubFolder(const Zstring& itemName, //dir exists on one side only
const FolderAttributes& attr);
@@ -219,7 +218,7 @@ public:
const Zstring& itemNameR,
const FileAttributes& right);
- template <SelectedSide side>
+ template <SelectSide side>
FilePair& addSubFile(const Zstring& itemName, //file exists on one side only
const FileAttributes& attr);
@@ -229,7 +228,7 @@ public:
const Zstring& itemNameR,
const LinkAttributes& right);
- template <SelectedSide side>
+ template <SelectSide side>
SymlinkPair& addSubLink(const Zstring& itemName, //link exists on one side only
const LinkAttributes& attr);
@@ -256,7 +255,7 @@ protected:
void removeEmptyRec();
- template <SelectedSide side>
+ template <SelectSide side>
void updateRelPathsRecursion(const FileSystemObject& fsAlias);
private:
@@ -300,8 +299,8 @@ public:
static void removeEmpty(BaseFolderPair& baseFolder) { baseFolder.removeEmptyRec(); } //physically remove all invalid entries (where both sides are empty) recursively
- template <SelectedSide side> bool isAvailable() const; //base folder status at the time of comparison!
- template <SelectedSide side> void setAvailable(bool value); //update after creating the directory in FFS
+ template <SelectSide side> bool isAvailable() const; //base folder status at the time of comparison!
+ template <SelectSide side> void setAvailable(bool value); //update after creating the directory in FFS
//get settings which were used while creating BaseFolderPair
const PathFilter& getFilter() const { return filter_.ref(); }
@@ -400,7 +399,6 @@ private:
ObjectMgr (const ObjectMgr& rhs) = delete;
ObjectMgr& operator=(const ObjectMgr& rhs) = delete; //it's not well-defined what copying an objects means regarding object-identity in this context
-
//our global ObjectMgr is not thread-safe (and currently does not need to be!)
//assert(runningOnMainThread()); -> still, may be accessed by synchronization worker threads, one thread at a time
static inline std::unordered_set<const ObjectMgr*> activeObjects_; //external linkage!
@@ -414,11 +412,11 @@ public:
virtual void accept(FSObjectVisitor& visitor) const = 0;
bool isPairEmpty() const; //true, if both sides are empty
- template <SelectedSide side> bool isEmpty() const;
+ template <SelectSide side> bool isEmpty() const;
//path getters always return valid values, even if isEmpty<side>()!
Zstring getItemNameAny() const; //like getItemName() but without bias to which side is returned
- template <SelectedSide side> Zstring getItemName() const; //case sensitive!
+ template <SelectSide side> Zstring getItemName() const; //case sensitive!
//comparison result
CompareFileResult getCategory() const { return cmpResult_; }
@@ -437,7 +435,7 @@ public:
virtual SyncOperation getSyncOperation() const;
std::wstring getSyncOpConflict() const; //return conflict when determining sync direction or (still unresolved) conflict during categorization
- template <SelectedSide side> void removeObject(); //removes file or directory (recursively!) without physically removing the element: used by manual deletion
+ template <SelectSide side> void removeObject(); //removes file or directory (recursively!) without physically removing the element: used by manual deletion
const ContainerObject& parent() const { return parent_; }
/**/ ContainerObject& parent() { return parent_; }
@@ -475,13 +473,13 @@ private:
FileSystemObject (const FileSystemObject&) = delete;
FileSystemObject& operator=(const FileSystemObject&) = delete;
- AbstractPath getAbstractPathL() const override { return AFS::appendRelPath(base().getAbstractPath< LEFT_SIDE>(), getRelativePath< LEFT_SIDE>()); }
- AbstractPath getAbstractPathR() const override { return AFS::appendRelPath(base().getAbstractPath<RIGHT_SIDE>(), getRelativePath<RIGHT_SIDE>()); }
+ AbstractPath getAbstractPathL() const override { return AFS::appendRelPath(base().getAbstractPath< SelectSide::left>(), getRelativePath< SelectSide::left>()); }
+ AbstractPath getAbstractPathR() const override { return AFS::appendRelPath(base().getAbstractPath<SelectSide::right>(), getRelativePath<SelectSide::right>()); }
virtual void removeObjectL() = 0;
virtual void removeObjectR() = 0;
- template <SelectedSide side>
+ template <SelectSide side>
void propagateChangedItemName(const Zstring& itemNameOld); //required after any itemName changes
//categorization
@@ -525,11 +523,11 @@ public:
attrL_(attrL),
attrR_(attrR) {}
- template <SelectedSide side> bool isFollowedSymlink() const;
+ template <SelectSide side> bool isFollowedSymlink() const;
SyncOperation getSyncOperation() const override;
- template <SelectedSide sideTrg>
+ template <SelectSide sideTrg>
void setSyncedTo(const Zstring& itemName, bool isSymlinkTrg, bool isSymlinkSrc); //call after sync, sets DIR_EQUAL
private:
@@ -564,11 +562,12 @@ public:
attrL_(attrL),
attrR_(attrR) {}
- template <SelectedSide side> time_t getLastWriteTime() const;
- template <SelectedSide side> uint64_t getFileSize() const;
- template <SelectedSide side> AFS::FileId getFileId() const;
- template <SelectedSide side> bool isFollowedSymlink() const;
- template <SelectedSide side> FileAttributes getAttributes() const;
+ template <SelectSide side> time_t getLastWriteTime() const;
+ template <SelectSide side> uint64_t getFileSize() const;
+ template <SelectSide side> bool isFollowedSymlink() const;
+ template <SelectSide side> FileAttributes getAttributes() const;
+ template <SelectSide side> AFS::FingerPrint getFilePrint() const;
+ template <SelectSide side> void clearFilePrint();
void setMoveRef(ObjectId refId) { moveFileRef_ = refId; } //reference to corresponding renamed file
ObjectId getMoveRef() const { return moveFileRef_; } //may be nullptr
@@ -578,19 +577,19 @@ public:
SyncOperation testSyncOperation(SyncDirection testSyncDir) const override; //semantics: "what if"! assumes "active, no conflict, no recursion (directory)!
SyncOperation getSyncOperation() const override;
- template <SelectedSide sideTrg>
+ template <SelectSide sideTrg>
void setSyncedTo(const Zstring& itemName, //call after sync, sets FILE_EQUAL
uint64_t fileSize,
int64_t lastWriteTimeTrg,
int64_t lastWriteTimeSrc,
- const AFS::FileId& fileIdTrg,
- const AFS::FileId& fileIdSrc,
+ AFS::FingerPrint filePrintTrg,
+ AFS::FingerPrint filePrintSrc,
bool isSymlinkTrg,
bool isSymlinkSrc);
private:
- Zstring getRelativePathL() const override { return nativeAppendPaths(parent().getRelativePath< LEFT_SIDE>(), getItemName< LEFT_SIDE>()); }
- Zstring getRelativePathR() const override { return nativeAppendPaths(parent().getRelativePath<RIGHT_SIDE>(), getItemName<RIGHT_SIDE>()); }
+ Zstring getRelativePathL() const override { return nativeAppendPaths(parent().getRelativePath< SelectSide::left>(), getItemName< SelectSide::left>()); }
+ Zstring getRelativePathR() const override { return nativeAppendPaths(parent().getRelativePath<SelectSide::right>(), getItemName<SelectSide::right>()); }
SyncOperation applyMoveOptimization(SyncOperation op) const;
@@ -613,7 +612,7 @@ class SymlinkPair : public FileSystemObject //this class models a TRUE symbolic
public:
void accept(FSObjectVisitor& visitor) const override;
- template <SelectedSide side> time_t getLastWriteTime() const; //write time of the link, NOT target!
+ template <SelectSide side> time_t getLastWriteTime() const; //write time of the link, NOT target!
CompareSymlinkResult getLinkCategory() const; //returns actually used subset of CompareFileResult
@@ -627,14 +626,14 @@ public:
attrL_(attrL),
attrR_(attrR) {}
- template <SelectedSide sideTrg>
+ template <SelectSide sideTrg>
void setSyncedTo(const Zstring& itemName, //call after sync, sets SYMLINK_EQUAL
int64_t lastWriteTimeTrg,
int64_t lastWriteTimeSrc);
private:
- Zstring getRelativePathL() const override { return nativeAppendPaths(parent().getRelativePath< LEFT_SIDE>(), getItemName< LEFT_SIDE>()); }
- Zstring getRelativePathR() const override { return nativeAppendPaths(parent().getRelativePath<RIGHT_SIDE>(), getItemName<RIGHT_SIDE>()); }
+ Zstring getRelativePathL() const override { return nativeAppendPaths(parent().getRelativePath< SelectSide::left>(), getItemName< SelectSide::left>()); }
+ Zstring getRelativePathR() const override { return nativeAppendPaths(parent().getRelativePath<SelectSide::right>(), getItemName<SelectSide::right>()); }
void flip() override;
void removeObjectL() override { attrL_ = LinkAttributes(); }
@@ -765,7 +764,7 @@ void FileSystemObject::setActive(bool active)
}
-template <SelectedSide side> inline
+template <SelectSide side> inline
bool FileSystemObject::isEmpty() const
{
return SelectParam<side>::ref(itemNameL_, itemNameR_).empty();
@@ -775,11 +774,11 @@ bool FileSystemObject::isEmpty() const
inline
bool FileSystemObject::isPairEmpty() const
{
- return isEmpty<LEFT_SIDE>() && isEmpty<RIGHT_SIDE>();
+ return isEmpty<SelectSide::left>() && isEmpty<SelectSide::right>();
}
-template <SelectedSide side> inline
+template <SelectSide side> inline
Zstring FileSystemObject::getItemName() const
{
//assert(!itemNameL_.empty() || !itemNameR_.empty()); -> file pair might be empty (until removed after sync)
@@ -794,51 +793,51 @@ Zstring FileSystemObject::getItemName() const
inline
Zstring FileSystemObject::getItemNameAny() const
{
- return getItemName<LEFT_SIDE>(); //side doesn't matter
+ return getItemName<SelectSide::left>(); //side doesn't matter
}
template <> inline
-void FileSystemObject::removeObject<LEFT_SIDE>()
+void FileSystemObject::removeObject<SelectSide::left>()
{
- const Zstring itemNameOld = getItemName<LEFT_SIDE>();
+ const Zstring itemNameOld = getItemName<SelectSide::left>();
- cmpResult_ = isEmpty<RIGHT_SIDE>() ? FILE_EQUAL : FILE_RIGHT_SIDE_ONLY;
+ cmpResult_ = isEmpty<SelectSide::right>() ? FILE_EQUAL : FILE_RIGHT_SIDE_ONLY;
itemNameL_.clear();
removeObjectL();
setSyncDir(SyncDirection::none); //calls notifySyncCfgChanged()
- propagateChangedItemName<LEFT_SIDE>(itemNameOld);
+ propagateChangedItemName<SelectSide::left>(itemNameOld);
}
template <> inline
-void FileSystemObject::removeObject<RIGHT_SIDE>()
+void FileSystemObject::removeObject<SelectSide::right>()
{
- const Zstring itemNameOld = getItemName<RIGHT_SIDE>();
+ const Zstring itemNameOld = getItemName<SelectSide::right>();
- cmpResult_ = isEmpty<LEFT_SIDE>() ? FILE_EQUAL : FILE_LEFT_SIDE_ONLY;
+ cmpResult_ = isEmpty<SelectSide::left>() ? FILE_EQUAL : FILE_LEFT_SIDE_ONLY;
itemNameR_.clear();
removeObjectR();
setSyncDir(SyncDirection::none); //calls notifySyncCfgChanged()
- propagateChangedItemName<RIGHT_SIDE>(itemNameOld);
+ propagateChangedItemName<SelectSide::right>(itemNameOld);
}
inline
void FileSystemObject::setSynced(const Zstring& itemName)
{
- const Zstring itemNameOldL = getItemName<LEFT_SIDE>();
- const Zstring itemNameOldR = getItemName<RIGHT_SIDE>();
+ const Zstring itemNameOldL = getItemName<SelectSide::left>();
+ const Zstring itemNameOldR = getItemName<SelectSide::right>();
assert(!isPairEmpty());
itemNameR_ = itemNameL_ = itemName;
cmpResult_ = FILE_EQUAL;
setSyncDir(SyncDirection::none);
- propagateChangedItemName< LEFT_SIDE>(itemNameOldL);
- propagateChangedItemName<RIGHT_SIDE>(itemNameOldR);
+ propagateChangedItemName< SelectSide::left>(itemNameOldL);
+ propagateChangedItemName<SelectSide::right>(itemNameOldR);
}
@@ -898,7 +897,7 @@ void FileSystemObject::flip()
}
-template <SelectedSide side> inline
+template <SelectSide side> inline
void FileSystemObject::propagateChangedItemName(const Zstring& itemNameOld)
{
if (itemNameL_.empty() && itemNameR_.empty()) return; //both sides might just have been deleted by removeObject<>
@@ -909,7 +908,7 @@ void FileSystemObject::propagateChangedItemName(const Zstring& itemNameOld)
}
-template <SelectedSide side> inline
+template <SelectSide side> inline
void ContainerObject::updateRelPathsRecursion(const FileSystemObject& fsAlias)
{
assert(SelectParam<side>::ref(relPathL_, relPathR_) != //perf: only call if actual item name changed!
@@ -924,14 +923,14 @@ void ContainerObject::updateRelPathsRecursion(const FileSystemObject& fsAlias)
inline
ContainerObject::ContainerObject(const FileSystemObject& fsAlias) :
- relPathL_(nativeAppendPaths(fsAlias.parent().relPathL_, fsAlias.getItemName<LEFT_SIDE>())),
+ relPathL_(nativeAppendPaths(fsAlias.parent().relPathL_, fsAlias.getItemName<SelectSide::left>())),
relPathR_(
fsAlias.parent().relPathL_.c_str() == //
fsAlias.parent().relPathR_.c_str() && //take advantage of FileSystemObject's Zstring reuse:
- fsAlias.getItemName< LEFT_SIDE>().c_str() == //=> perf: 12% faster merge phase; -4% peak memory
- fsAlias.getItemName<RIGHT_SIDE>().c_str() ? //
+ fsAlias.getItemName< SelectSide::left>().c_str() == //=> perf: 12% faster merge phase; -4% peak memory
+ fsAlias.getItemName<SelectSide::right>().c_str() ? //
relPathL_ : //ternary-WTF! (implicit copy-constructor call!!) => no big deal for a Zstring
- nativeAppendPaths(fsAlias.parent().relPathR_, fsAlias.getItemName<RIGHT_SIDE>())),
+ nativeAppendPaths(fsAlias.parent().relPathR_, fsAlias.getItemName<SelectSide::right>())),
base_(fsAlias.parent().base_)
{
assert(relPathL_.c_str() == relPathR_.c_str() || relPathL_ != relPathR_);
@@ -965,7 +964,7 @@ FolderPair& ContainerObject::addSubFolder(const Zstring& itemNameL,
template <> inline
-FolderPair& ContainerObject::addSubFolder<LEFT_SIDE>(const Zstring& itemName, const FolderAttributes& attr)
+FolderPair& ContainerObject::addSubFolder<SelectSide::left>(const Zstring& itemName, const FolderAttributes& attr)
{
subFolders_.emplace_back(itemName, attr, DIR_LEFT_SIDE_ONLY, Zstring(), FolderAttributes(), *this);
return subFolders_.back();
@@ -973,7 +972,7 @@ FolderPair& ContainerObject::addSubFolder<LEFT_SIDE>(const Zstring& itemName, co
template <> inline
-FolderPair& ContainerObject::addSubFolder<RIGHT_SIDE>(const Zstring& itemName, const FolderAttributes& attr)
+FolderPair& ContainerObject::addSubFolder<SelectSide::right>(const Zstring& itemName, const FolderAttributes& attr)
{
subFolders_.emplace_back(Zstring(), FolderAttributes(), DIR_RIGHT_SIDE_ONLY, itemName, attr, *this);
return subFolders_.back();
@@ -993,7 +992,7 @@ FilePair& ContainerObject::addSubFile(const Zstring& itemNameL,
template <> inline
-FilePair& ContainerObject::addSubFile<LEFT_SIDE>(const Zstring& itemName, const FileAttributes& attr)
+FilePair& ContainerObject::addSubFile<SelectSide::left>(const Zstring& itemName, const FileAttributes& attr)
{
subFiles_.emplace_back(itemName, attr, FILE_LEFT_SIDE_ONLY, Zstring(), FileAttributes(), *this);
return subFiles_.back();
@@ -1001,7 +1000,7 @@ FilePair& ContainerObject::addSubFile<LEFT_SIDE>(const Zstring& itemName, const
template <> inline
-FilePair& ContainerObject::addSubFile<RIGHT_SIDE>(const Zstring& itemName, const FileAttributes& attr)
+FilePair& ContainerObject::addSubFile<SelectSide::right>(const Zstring& itemName, const FileAttributes& attr)
{
subFiles_.emplace_back(Zstring(), FileAttributes(), FILE_RIGHT_SIDE_ONLY, itemName, attr, *this);
return subFiles_.back();
@@ -1021,7 +1020,7 @@ SymlinkPair& ContainerObject::addSubLink(const Zstring& itemNameL,
template <> inline
-SymlinkPair& ContainerObject::addSubLink<LEFT_SIDE>(const Zstring& itemName, const LinkAttributes& attr)
+SymlinkPair& ContainerObject::addSubLink<SelectSide::left>(const Zstring& itemName, const LinkAttributes& attr)
{
subLinks_.emplace_back(itemName, attr, SYMLINK_LEFT_SIDE_ONLY, Zstring(), LinkAttributes(), *this);
return subLinks_.back();
@@ -1029,7 +1028,7 @@ SymlinkPair& ContainerObject::addSubLink<LEFT_SIDE>(const Zstring& itemName, con
template <> inline
-SymlinkPair& ContainerObject::addSubLink<RIGHT_SIDE>(const Zstring& itemName, const LinkAttributes& attr)
+SymlinkPair& ContainerObject::addSubLink<SelectSide::right>(const Zstring& itemName, const LinkAttributes& attr)
{
subLinks_.emplace_back(Zstring(), LinkAttributes(), SYMLINK_RIGHT_SIDE_ONLY, itemName, attr, *this);
return subLinks_.back();
@@ -1058,11 +1057,11 @@ inline
void FolderPair::removeObjectL()
{
for (FilePair& file : refSubFiles())
- file.removeObject<LEFT_SIDE>();
+ file.removeObject<SelectSide::left>();
for (SymlinkPair& link : refSubLinks())
- link.removeObject<LEFT_SIDE>();
+ link.removeObject<SelectSide::left>();
for (FolderPair& folder : refSubFolders())
- folder.removeObject<LEFT_SIDE>();
+ folder.removeObject<SelectSide::left>();
attrL_ = FolderAttributes();
}
@@ -1072,24 +1071,24 @@ inline
void FolderPair::removeObjectR()
{
for (FilePair& file : refSubFiles())
- file.removeObject<RIGHT_SIDE>();
+ file.removeObject<SelectSide::right>();
for (SymlinkPair& link : refSubLinks())
- link.removeObject<RIGHT_SIDE>();
+ link.removeObject<SelectSide::right>();
for (FolderPair& folder : refSubFolders())
- folder.removeObject<RIGHT_SIDE>();
+ folder.removeObject<SelectSide::right>();
attrR_ = FolderAttributes();
}
-template <SelectedSide side> inline
+template <SelectSide side> inline
bool BaseFolderPair::isAvailable() const
{
return SelectParam<side>::ref(folderAvailableLeft_, folderAvailableRight_);
}
-template <SelectedSide side> inline
+template <SelectSide side> inline
void BaseFolderPair::setAvailable(bool value)
{
SelectParam<side>::ref(folderAvailableLeft_, folderAvailableRight_) = value;
@@ -1104,75 +1103,82 @@ void FilePair::flip()
}
-template <SelectedSide side> inline
+template <SelectSide side> inline
FileAttributes FilePair::getAttributes() const
{
return SelectParam<side>::ref(attrL_, attrR_);
}
-template <SelectedSide side> inline
+template <SelectSide side> inline
time_t FilePair::getLastWriteTime() const
{
return SelectParam<side>::ref(attrL_, attrR_).modTime;
}
-template <SelectedSide side> inline
+template <SelectSide side> inline
uint64_t FilePair::getFileSize() const
{
return SelectParam<side>::ref(attrL_, attrR_).fileSize;
}
-template <SelectedSide side> inline
-AFS::FileId FilePair::getFileId() const
+template <SelectSide side> inline
+bool FilePair::isFollowedSymlink() const
{
- return SelectParam<side>::ref(attrL_, attrR_).fileId;
+ return SelectParam<side>::ref(attrL_, attrR_).isFollowedSymlink;
}
-template <SelectedSide side> inline
-bool FilePair::isFollowedSymlink() const
+template <SelectSide side> inline
+bool FolderPair::isFollowedSymlink() const
{
return SelectParam<side>::ref(attrL_, attrR_).isFollowedSymlink;
}
-template <SelectedSide side> inline
-bool FolderPair::isFollowedSymlink() const
+template <SelectSide side> inline
+AFS::FingerPrint FilePair::getFilePrint() const
{
- return SelectParam<side>::ref(attrL_, attrR_).isFollowedSymlink;
+ return SelectParam<side>::ref(attrL_, attrR_).filePrint;
+}
+
+
+template <SelectSide side> inline
+void FilePair::clearFilePrint()
+{
+ SelectParam<side>::ref(attrL_, attrR_).filePrint = 0;
}
-template <SelectedSide sideTrg> inline
+template <SelectSide sideTrg> inline
void FilePair::setSyncedTo(const Zstring& itemName,
uint64_t fileSize,
int64_t lastWriteTimeTrg,
int64_t lastWriteTimeSrc,
- const AFS::FileId& fileIdTrg,
- const AFS::FileId& fileIdSrc,
+ AFS::FingerPrint filePrintTrg,
+ AFS::FingerPrint filePrintSrc,
bool isSymlinkTrg,
bool isSymlinkSrc)
{
//FILE_EQUAL is only allowed for same short name and file size: enforced by this method!
- constexpr SelectedSide sideSrc = OtherSide<sideTrg>::value;
+ constexpr SelectSide sideSrc = OtherSide<sideTrg>::value;
- SelectParam<sideTrg>::ref(attrL_, attrR_) = FileAttributes(lastWriteTimeTrg, fileSize, fileIdTrg, isSymlinkTrg);
- SelectParam<sideSrc>::ref(attrL_, attrR_) = FileAttributes(lastWriteTimeSrc, fileSize, fileIdSrc, isSymlinkSrc);
+ SelectParam<sideTrg>::ref(attrL_, attrR_) = FileAttributes(lastWriteTimeTrg, fileSize, filePrintTrg, isSymlinkTrg);
+ SelectParam<sideSrc>::ref(attrL_, attrR_) = FileAttributes(lastWriteTimeSrc, fileSize, filePrintSrc, isSymlinkSrc);
moveFileRef_ = nullptr;
FileSystemObject::setSynced(itemName); //set FileSystemObject specific part
}
-template <SelectedSide sideTrg> inline
+template <SelectSide sideTrg> inline
void SymlinkPair::setSyncedTo(const Zstring& itemName,
int64_t lastWriteTimeTrg,
int64_t lastWriteTimeSrc)
{
- constexpr SelectedSide sideSrc = OtherSide<sideTrg>::value;
+ constexpr SelectSide sideSrc = OtherSide<sideTrg>::value;
SelectParam<sideTrg>::ref(attrL_, attrR_) = LinkAttributes(lastWriteTimeTrg);
SelectParam<sideSrc>::ref(attrL_, attrR_) = LinkAttributes(lastWriteTimeSrc);
@@ -1181,12 +1187,12 @@ void SymlinkPair::setSyncedTo(const Zstring& itemName,
}
-template <SelectedSide sideTrg> inline
+template <SelectSide sideTrg> inline
void FolderPair::setSyncedTo(const Zstring& itemName,
bool isSymlinkTrg,
bool isSymlinkSrc)
{
- constexpr SelectedSide sideSrc = OtherSide<sideTrg>::value;
+ constexpr SelectSide sideSrc = OtherSide<sideTrg>::value;
SelectParam<sideTrg>::ref(attrL_, attrR_) = FolderAttributes(isSymlinkTrg);
SelectParam<sideSrc>::ref(attrL_, attrR_) = FolderAttributes(isSymlinkSrc);
@@ -1195,7 +1201,7 @@ void FolderPair::setSyncedTo(const Zstring& itemName,
}
-template <SelectedSide side> inline
+template <SelectSide side> inline
time_t SymlinkPair::getLastWriteTime() const
{
return SelectParam<side>::ref(attrL_, attrR_).modTime;
diff --git a/FreeFileSync/Source/base/icon_loader.cpp b/FreeFileSync/Source/base/icon_loader.cpp
index 684569ed..495793be 100644
--- a/FreeFileSync/Source/base/icon_loader.cpp
+++ b/FreeFileSync/Source/base/icon_loader.cpp
@@ -36,8 +36,7 @@ ImageHolder copyToImageHolder(const GdkPixbuf& pixBuf, int maxSize) //throw SysE
if (channels != 3 && channels != 4)
throw SysError(formatSystemError("gdk_pixbuf_get_n_channels", L"", L"Unexpected number of channels: " + numberTo<std::wstring>(channels)));
- const bool withAlpha = channels == 4;
- assert(::gdk_pixbuf_get_has_alpha(&pixBuf) == withAlpha);
+ assert(::gdk_pixbuf_get_has_alpha(&pixBuf) == (channels == 4));
const unsigned char* srcBytes = ::gdk_pixbuf_read_pixels(&pixBuf);
const int srcWidth = ::gdk_pixbuf_get_width (&pixBuf);
@@ -54,10 +53,10 @@ ImageHolder copyToImageHolder(const GdkPixbuf& pixBuf, int maxSize) //throw SysE
targetWidth = numeric::intDivRound(targetWidth * maxSize, maxExtent);
targetHeight = numeric::intDivRound(targetHeight * maxSize, maxExtent);
-#if 0 //alternative to xbrz::bilinearScale()
- GdkPixbuf* pixBufShrinked = ::gdk_pixbuf_scale_simple(pixBuf, //const GdkPixbuf* src,
- targetWidth, //int dest_width,
- targetHeight, //int dest_height,
+#if 0 //alternative to xbrz::bilinearScaleSimple()? does it support alpha-channel?
+ GdkPixbuf* pixBufShrinked = ::gdk_pixbuf_scale_simple(pixBuf, //const GdkPixbuf* src
+ targetWidth, //int dest_width
+ targetHeight, //int dest_height
GDK_INTERP_BILINEAR); //GdkInterpType interp_type
if (!pixBufShrinked)
throw SysError(formatSystemError("gdk_pixbuf_scale_simple", L"", L"Not enough memory."));
@@ -67,32 +66,38 @@ ImageHolder copyToImageHolder(const GdkPixbuf& pixBuf, int maxSize) //throw SysE
const auto imgReader = [srcBytes, srcStride, channels](int x, int y, xbrz::BytePixel& pix)
{
- std::memcpy(pix, srcBytes + y * srcStride + channels * x, channels);
+ const unsigned char* const ptr = srcBytes + y * srcStride + channels * x;
+
+ const unsigned char a = channels == 4 ? ptr[3] : 255;
+ pix[0] = a;
+ pix[1] = xbrz::premultiply(ptr[0], a); //r
+ pix[2] = xbrz::premultiply(ptr[1], a); //g
+ pix[3] = xbrz::premultiply(ptr[2], a); //b
};
- ImageHolder imgOut(targetWidth, targetHeight, withAlpha);
+ ImageHolder imgOut(targetWidth, targetHeight, true /*withAlpha*/);
- const auto imgWriter = [rgbPtr = imgOut.getRgb(), alphaPtr = imgOut.getAlpha()](const xbrz::BytePixel& pix) mutable
+ const auto imgWriter = [rgb = imgOut.getRgb(), alpha = imgOut.getAlpha()](const xbrz::BytePixel& pix) mutable
{
- *rgbPtr++ = pix[0]; //r
- *rgbPtr++ = pix[1]; //g
- *rgbPtr++ = pix[2]; //b
- if (alphaPtr)
- * alphaPtr++ = pix[3]; //a
+ const unsigned char a = pix[0];
+ *alpha++ = a;
+ *rgb++ = xbrz::demultiply(pix[1], a); //r
+ *rgb++ = xbrz::demultiply(pix[2], a); //g
+ *rgb++ = xbrz::demultiply(pix[3], a); //b
};
if (srcWidth == targetWidth &&
srcHeight == targetHeight)
xbrz::unscaledCopy(imgReader, imgWriter, srcWidth, srcHeight); //perf: going overboard?
else
- xbrz::bilinearScale(imgReader, //PixReader srcReader,
- srcWidth, //int srcWidth,
- srcHeight, //int srcHeight,
- imgWriter, //PixWriter trgWriter
- targetWidth, //int trgWidth,
- targetHeight, //int trgHeight,
- 0, //int yFirst,
- targetHeight); //int yLast,
+ xbrz::bilinearScaleSimple(imgReader, //PixReader srcReader
+ srcWidth, //int srcWidth
+ srcHeight, //int srcHeight
+ imgWriter, //PixWriter trgWriter
+ targetWidth, //int trgWidth
+ targetHeight, //int trgHeight
+ 0, //int yFirst
+ targetHeight); //int yLast
return imgOut;
}
@@ -105,9 +110,9 @@ ImageHolder imageHolderFromGicon(GIcon& gicon, int maxSize) //throw SysError
GtkIconTheme* const defaultTheme = ::gtk_icon_theme_get_default(); //not owned!
ASSERT_SYSERROR(defaultTheme); //no more error details
- GtkIconInfo* const iconInfo = ::gtk_icon_theme_lookup_by_gicon(defaultTheme, //GtkIconTheme* icon_theme,
- &gicon, //GIcon* icon,
- maxSize, //gint size,
+ GtkIconInfo* const iconInfo = ::gtk_icon_theme_lookup_by_gicon(defaultTheme, //GtkIconTheme* icon_theme
+ &gicon, //GIcon* icon
+ maxSize, //gint size
GTK_ICON_LOOKUP_USE_BUILTIN); //GtkIconLookupFlags flags
if (!iconInfo)
throw SysError(formatSystemError("gtk_icon_theme_lookup_by_gicon", L"", L"Icon not available."));
@@ -135,9 +140,9 @@ ImageHolder imageHolderFromGicon(GIcon& gicon, int maxSize) //throw SysError
FileIconHolder fff::getIconByTemplatePath(const Zstring& templatePath, int maxSize) //throw SysError
{
//uses full file name, e.g. "AUTHORS" has own mime type on Linux:
- gchar* const contentType = ::g_content_type_guess(templatePath.c_str(), //const gchar* filename,
- nullptr, //const guchar* data,
- 0, //gsize data_size,
+ gchar* const contentType = ::g_content_type_guess(templatePath.c_str(), //const gchar* filename
+ nullptr, //const guchar* data
+ 0, //gsize data_size
nullptr); //gboolean* result_uncertain
if (!contentType)
throw SysError(formatSystemError("g_content_type_guess(" + copyStringTo<std::string>(templatePath) + ')', L"", L"Unknown content type."));
@@ -218,7 +223,7 @@ FileIconHolder fff::getFileIcon(const Zstring& filePath, int maxSize) //throw Sy
ImageHolder fff::getThumbnailImage(const Zstring& filePath, int maxSize) //throw SysError
{
- struct ::stat fileInfo = {};
+ struct stat fileInfo = {};
if (::stat(filePath.c_str(), &fileInfo) != 0)
THROW_LAST_SYS_ERROR("stat");
@@ -241,13 +246,18 @@ ImageHolder fff::getThumbnailImage(const Zstring& filePath, int maxSize) //throw
wxImage fff::extractWxImage(ImageHolder&& ih)
{
assert(runningOnMainThread());
-
if (!ih.getRgb())
return wxNullImage;
wxImage img(ih.getWidth(), ih.getHeight(), ih.releaseRgb(), false /*static_data*/); //pass ownership
if (ih.getAlpha())
img.SetAlpha(ih.releaseAlpha(), false /*static_data*/);
+ else
+ {
+ assert(false);
+ img.SetAlpha();
+ ::memset(img.GetAlpha(), wxIMAGE_ALPHA_OPAQUE, ih.getWidth() * ih.getHeight());
+ }
return img;
}
diff --git a/FreeFileSync/Source/base/icon_loader.h b/FreeFileSync/Source/base/icon_loader.h
index ae4b7b43..754cffbc 100644
--- a/FreeFileSync/Source/base/icon_loader.h
+++ b/FreeFileSync/Source/base/icon_loader.h
@@ -16,7 +16,7 @@ namespace fff
{
//=> all functions are safe to call from multiple threads!
//COM needs to be initialized before calling any of these functions! CoInitializeEx/CoUninitialize
-//=> don't call from WM_PAINT handler! https://blogs.msdn.microsoft.com/yvesdolc/2009/08/06/do-you-receive-wm_paint-when-waiting-for-a-com-call-to-return/
+//=> don't call from WM_PAINT handler! https://docs.microsoft.com/en-us/archive/blogs/yvesdolc/do-you-receive-wm_paint-when-waiting-for-a-com-call-to-return
zen::FileIconHolder getIconByTemplatePath(const Zstring& templatePath, int maxSize); //throw SysError
zen::FileIconHolder genericFileIcon(int maxSize); //throw SysError
diff --git a/FreeFileSync/Source/base/lock_holder.h b/FreeFileSync/Source/base/lock_holder.h
index 87c075f9..68d8215e 100644
--- a/FreeFileSync/Source/base/lock_holder.h
+++ b/FreeFileSync/Source/base/lock_holder.h
@@ -31,7 +31,7 @@ public:
{
//lock file creation is synchronous and may block noticeably for very slow devices (USB sticks, mapped cloud storage)
lockHolder_.emplace_back(appendSeparator(folderPath) + Zstr("sync") + LOCK_FILE_ENDING,
- [&](const std::wstring& msg) { pcb.updateStatus(msg); /*throw X*/ },
+ [&](std::wstring&& msg) { pcb.updateStatus(std::move(msg)); /*throw X*/ },
UI_UPDATE_INTERVAL / 2); //throw FileError
}
catch (const FileError& e) { failedLocks.emplace_back(folderPath, e); }
diff --git a/FreeFileSync/Source/base/parallel_scan.cpp b/FreeFileSync/Source/base/parallel_scan.cpp
index 340bf0d8..d61ec28b 100644
--- a/FreeFileSync/Source/base/parallel_scan.cpp
+++ b/FreeFileSync/Source/base/parallel_scan.cpp
@@ -64,7 +64,7 @@ public:
errorResponse_ = std::nullopt;
dummy.unlock(); //optimization for condition_variable::notify_all()
- conditionReadyForNewRequest_.notify_all(); //instead of notify_one(); workaround bug: https://svn.boost.org/trac/boost/ticket/7796
+ conditionReadyForNewRequest_.notify_all(); //instead of notify_one(); work around bug: https://svn.boost.org/trac/boost/ticket/7796
return rv;
}
@@ -89,14 +89,14 @@ public:
switch (onError({errorRequest_->msg, errorRequest_->failTime, errorRequest_->retryNumber})) //throw X
{
case PhaseCallback::ignore:
- errorResponse_ = AFS::TraverserCallback::ON_ERROR_CONTINUE;
+ errorResponse_ = AFS::TraverserCallback::HandleError::ignore;
break;
case PhaseCallback::retry:
- errorResponse_ = AFS::TraverserCallback::ON_ERROR_RETRY;
+ errorResponse_ = AFS::TraverserCallback::HandleError::retry;
break;
}
- conditionHaveResponse_.notify_all(); //instead of notify_one(); workaround bug: https://svn.boost.org/trac/boost/ticket/7796
+ conditionHaveResponse_.notify_all(); //instead of notify_one(); work around bug: https://svn.boost.org/trac/boost/ticket/7796
}
if (threadsToFinish_ == 0)
{
@@ -303,7 +303,7 @@ void DirCallback::onFile(const AFS::FileInfo& fi) //throw ThreadStopRequest
Linux: retrieveFileID takes about 50% longer in VM! (avoidable because of redundant stat() call!) */
- output_.addSubFile(fi.itemName, FileAttributes(fi.modTime, fi.fileSize, fi.fileId, fi.isFollowedSymlink ));
+ output_.addSubFile(fi.itemName, FileAttributes(fi.modTime, fi.fileSize, fi.filePrint, fi.isFollowedSymlink));
cfg_.acb.incItemsScanned(); //add 1 element to the progress indicator
}
@@ -332,16 +332,15 @@ std::shared_ptr<AFS::TraverserCallback> DirCallback::onFolder(const AFS::FolderI
cfg_.acb.incItemsScanned(); //add 1 element to the progress indicator
//------------------------------------------------------------------------------------
- warn_static("FIX: this error cannot be retried!")
if (level_ > FOLDER_TRAVERSAL_LEVEL_MAX) //Win32 traverser: stack overflow approximately at level 1000
//check after FolderContainer::addSubFolder()
for (size_t retryNumber = 0;; ++retryNumber)
switch (reportItemError({replaceCpy(_("Cannot read directory %x."), L"%x", AFS::getDisplayPath(AFS::appendRelPath(cfg_.baseFolderPath, relPath))) +
L"\n\n" L"Endless recursion.", std::chrono::steady_clock::now(), retryNumber}, fi.itemName)) //throw ThreadStopRequest
{
- case AFS::TraverserCallback::ON_ERROR_RETRY:
+ case AFS::TraverserCallback::HandleError::retry:
break;
- case AFS::TraverserCallback::ON_ERROR_CONTINUE:
+ case AFS::TraverserCallback::HandleError::ignore:
return nullptr;
}
@@ -362,7 +361,7 @@ DirCallback::HandleLink DirCallback::onSymlink(const AFS::SymlinkInfo& si) //thr
switch (cfg_.handleSymlinks)
{
case SymLinkHandling::exclude:
- return LINK_SKIP;
+ return HandleLink::skip;
case SymLinkHandling::direct:
if (cfg_.filter.ref().passFileFilter(relPath)) //always use file filter: Link type may not be "stable" on Linux!
@@ -370,7 +369,7 @@ DirCallback::HandleLink DirCallback::onSymlink(const AFS::SymlinkInfo& si) //thr
output_.addSubLink(si.itemName, LinkAttributes(si.modTime));
cfg_.acb.incItemsScanned(); //add 1 element to the progress indicator
}
- return LINK_SKIP;
+ return HandleLink::skip;
case SymLinkHandling::follow:
//filter symlinks before trying to follow them: handle user-excluded broken symlinks!
@@ -380,32 +379,32 @@ DirCallback::HandleLink DirCallback::onSymlink(const AFS::SymlinkInfo& si) //thr
bool childItemMightMatch = true;
if (!cfg_.filter.ref().passDirFilter(relPath, &childItemMightMatch))
if (!childItemMightMatch)
- return LINK_SKIP;
+ return HandleLink::skip;
}
- return LINK_FOLLOW;
+ return HandleLink::follow;
}
assert(false);
- return LINK_SKIP;
+ return HandleLink::skip;
}
DirCallback::HandleError DirCallback::reportError(const ErrorInfo& errorInfo, const Zstring& itemName /*optional*/) //throw ThreadStopRequest
{
- switch (cfg_.acb.reportError(errorInfo)) //throw ThreadStopRequest
+ const HandleError handleErr = cfg_.acb.reportError(errorInfo); //throw ThreadStopRequest
+ switch (handleErr)
{
- case ON_ERROR_CONTINUE:
+ case HandleError::ignore:
if (itemName.empty())
cfg_.failedDirReads.emplace(beforeLast(parentRelPathPf_, FILE_NAME_SEPARATOR, IfNotFoundReturn::none), utfTo<Zstringc>(errorInfo.msg));
else
cfg_.failedItemReads.emplace(parentRelPathPf_ + itemName, utfTo<Zstringc>(errorInfo.msg));
- return ON_ERROR_CONTINUE;
+ break;
- case ON_ERROR_RETRY:
- return ON_ERROR_RETRY;
+ case HandleError::retry:
+ break;
}
- assert(false);
- return ON_ERROR_CONTINUE;
+ return handleErr;
}
}
diff --git a/FreeFileSync/Source/base/parallel_scan.h b/FreeFileSync/Source/base/parallel_scan.h
index 4675bbc4..4fe46643 100644
--- a/FreeFileSync/Source/base/parallel_scan.h
+++ b/FreeFileSync/Source/base/parallel_scan.h
@@ -33,10 +33,10 @@ struct DirectoryValue
FolderContainer folderCont;
//relative paths (or empty string for root) for directories that could not be read (completely), e.g. access denied, or temporary network drop
- std::map<Zstring, Zstringc> failedFolderReads; //with corresponding error message
+ std::map<Zstring, Zstringc /*error message*/> failedFolderReads;
- //relative paths (never empty) for failure to read single file/dir/symlink with corresponding error message
- std::map<Zstring, Zstringc> failedItemReads;
+ //relative paths (never empty) for failure to read single file/dir/symlink
+ std::map<Zstring, Zstringc /*error message*/> failedItemReads;
};
diff --git a/FreeFileSync/Source/base/process_callback.h b/FreeFileSync/Source/base/process_callback.h
index b5b015d6..8540f872 100644
--- a/FreeFileSync/Source/base/process_callback.h
+++ b/FreeFileSync/Source/base/process_callback.h
@@ -37,11 +37,11 @@ struct PhaseCallback
//opportunity to abort must be implemented in a frequently-executed method like requestUiUpdate()
virtual void requestUiUpdate(bool force = false) = 0; //throw X
- //UI info only, should not be logged: called periodically after data was processed: expected(!) to request GUI update
- virtual void updateStatus(const std::wstring& msg) = 0; //throw X
+ //UI info only, should *not* be logged: called periodically after data was processed: expected(!) to request GUI update
+ virtual void updateStatus(std::wstring&& msg) = 0; //throw X
- //like updateStatus() but should be logged:
- virtual void reportInfo(const std::wstring& msg) = 0; //throw X
+ //log only; must *not* call updateStatus()!
+ virtual void logInfo(const std::wstring& msg) = 0; //throw X
virtual void reportWarning(const std::wstring& msg, bool& warningActive) = 0; //throw X
@@ -51,7 +51,6 @@ struct PhaseCallback
std::chrono::steady_clock::time_point failTime;
size_t retryNumber = 0;
};
-
enum Response
{
ignore,
diff --git a/FreeFileSync/Source/base/status_handler_impl.h b/FreeFileSync/Source/base/status_handler_impl.h
index 0841a752..611c45ee 100644
--- a/FreeFileSync/Source/base/status_handler_impl.h
+++ b/FreeFileSync/Source/base/status_handler_impl.h
@@ -32,35 +32,39 @@ public:
}
//context of worker thread
- void updateStatus(const std::wstring& msg) //throw ThreadStopRequest
+ void updateStatus(std::wstring&& msg) //throw ThreadStopRequest
{
assert(!zen::runningOnMainThread());
{
std::lock_guard dummy(lockCurrentStatus_);
if (ThreadStatus* ts = getThreadStatus()) //call while holding "lockCurrentStatus_" lock!!
- ts->statusMsg = msg;
+ ts->statusMsg = std::move(msg);
else assert(false);
}
zen::interruptionPoint(); //throw ThreadStopRequest
}
//blocking call: context of worker thread
- //=> indirect support for "pause": reportInfo() is called under singleThread lock,
+ //=> indirect support for "pause": logInfo() is called under singleThread lock,
// so all other worker threads will wait when coming out of parallel I/O (trying to lock singleThread)
- void reportInfo(const std::wstring& msg) //throw ThreadStopRequest
+ void logInfo(const std::wstring& msg) //throw ThreadStopRequest
{
- updateStatus(msg); //throw ThreadStopRequest
-
assert(!zen::runningOnMainThread());
std::unique_lock dummy(lockRequest_);
- zen::interruptibleWait(conditionReadyForNewRequest_, dummy, [this] { return !reportInfoRequest_; }); //throw ThreadStopRequest
+ zen::interruptibleWait(conditionReadyForNewRequest_, dummy, [this] { return !logInfoRequest_; }); //throw ThreadStopRequest
- reportInfoRequest_ = /*std::move(taskPrefix) + */ msg;
+ logInfoRequest_ = /*std::move(taskPrefix) + */ msg;
dummy.unlock(); //optimization for condition_variable::notify_all()
conditionNewRequest.notify_all();
}
+ void reportInfo(std::wstring&& msg) //throw ThreadStopRequest
+ {
+ logInfo(msg); //throw ThreadStopRequest
+ updateStatus(std::move(msg)); //
+ }
+
//blocking call: context of worker thread
PhaseCallback::Response reportError(const PhaseCallback::ErrorInfo& errorInfo) //throw ThreadStopRequest
{
@@ -79,21 +83,21 @@ public:
errorResponse_ = std::nullopt;
dummy.unlock(); //optimization for condition_variable::notify_all()
- conditionReadyForNewRequest_.notify_all(); //=> spurious wake-up for AsyncCallback::reportInfo()
+ conditionReadyForNewRequest_.notify_all(); //=> spurious wake-up for AsyncCallback::logInfo()
return rv;
}
//context of main thread
- void waitUntilDone(std::chrono::milliseconds duration, PhaseCallback& cb) //throw X
+ void waitUntilDone(std::chrono::milliseconds cbInterval, PhaseCallback& cb) //throw X
{
assert(zen::runningOnMainThread());
for (;;)
{
- const std::chrono::steady_clock::time_point callbackTime = std::chrono::steady_clock::now() + duration;
+ const std::chrono::steady_clock::time_point callbackTime = std::chrono::steady_clock::now() + cbInterval;
for (std::unique_lock dummy(lockRequest_);;) //process all errors without delay
{
- const bool rv = conditionNewRequest.wait_until(dummy, callbackTime, [this] { return (errorRequest_ && !errorResponse_) || reportInfoRequest_ || finishNowRequest_; });
+ const bool rv = conditionNewRequest.wait_until(dummy, callbackTime, [this] { return (errorRequest_ && !errorResponse_) || logInfoRequest_ || finishNowRequest_; });
if (!rv) //time-out + condition not met
break;
@@ -101,12 +105,12 @@ public:
{
assert(!finishNowRequest_);
errorResponse_ = cb.reportError(*errorRequest_); //throw X
- conditionHaveResponse_.notify_all(); //instead of notify_one(); workaround bug: https://svn.boost.org/trac/boost/ticket/7796
+ conditionHaveResponse_.notify_all(); //instead of notify_one(); work around bug: https://svn.boost.org/trac/boost/ticket/7796
}
- if (reportInfoRequest_)
+ if (logInfoRequest_)
{
- cb.reportInfo(*reportInfoRequest_); //throw X
- reportInfoRequest_ = {};
+ cb.logInfo(*logInfoRequest_); //throw X
+ logInfoRequest_ = {};
conditionReadyForNewRequest_.notify_all(); //=> spurious wake-up for AsyncCallback::reportError()
}
if (finishNowRequest_)
@@ -117,7 +121,7 @@ public:
}
}
- //call member functions outside of mutex scope:
+ //call back outside of mutex scope:
cb.updateStatus(getCurrentStatus()); //throw X
reportStats(cb);
}
@@ -146,7 +150,7 @@ public:
if (statusByPriority_.size() < prio + 1)
statusByPriority_.resize(prio + 1);
- statusByPriority_[prio].push_back({ threadId, /*taskIdx,*/ std::wstring() });
+ statusByPriority_[prio].push_back({threadId, /*taskIdx,*/ std::wstring()});
}
void notifyTaskEnd() //noexcept
@@ -264,7 +268,7 @@ private:
std::condition_variable conditionHaveResponse_;
std::optional<PhaseCallback::ErrorInfo> errorRequest_;
std::optional<PhaseCallback::Response > errorResponse_;
- std::optional<std::wstring> reportInfoRequest_;
+ std::optional<std::wstring> logInfoRequest_;
bool finishNowRequest_ = false;
//---- status updates ----
@@ -275,10 +279,10 @@ private:
//std::vector<char/*bool*/> usedIndexNums_; //keep info for human-readable task index numbers
//---- status updates II (lock-free) ----
- std::atomic<int> itemsDeltaProcessed_{ 0 }; //
- std::atomic<int64_t> bytesDeltaProcessed_{ 0 }; //std:atomic is uninitialized by default!
- std::atomic<int> itemsDeltaTotal_ { 0 }; //
- std::atomic<int64_t> bytesDeltaTotal_ { 0 }; //
+ std::atomic<int> itemsDeltaProcessed_{0}; //
+ std::atomic<int64_t> bytesDeltaProcessed_{0}; //std:atomic is uninitialized by default!
+ std::atomic<int> itemsDeltaTotal_ {0}; //
+ std::atomic<int64_t> bytesDeltaTotal_ {0}; //
};
@@ -303,7 +307,7 @@ public:
cb_.updateDataTotal(itemsReported_ - itemsExpected_, bytesReported_ - bytesExpected_); //noexcept!
}
- void updateStatus(const std::wstring& msg) { cb_.updateStatus(msg); } //throw ThreadStopRequest
+ void updateStatus(std::wstring&& msg) { cb_.updateStatus(std::move(msg)); } //throw ThreadStopRequest
void reportDelta(int itemsDelta, int64_t bytesDelta) //noexcept!
{
@@ -404,7 +408,7 @@ void massParallelExecute(const std::vector<std::pair<AbstractPath, ParallelWorkI
acb.notifyTaskBegin(statusPrio);
ZEN_ON_SCOPE_EXIT(acb.notifyTaskEnd());
- ParallelContext pctx{ itemPath, acb };
+ ParallelContext pctx{itemPath, acb};
task(pctx); //throw ThreadStopRequest
});
diff --git a/FreeFileSync/Source/base/synchronization.cpp b/FreeFileSync/Source/base/synchronization.cpp
index 5350aee5..f0464ca1 100644
--- a/FreeFileSync/Source/base/synchronization.cpp
+++ b/FreeFileSync/Source/base/synchronization.cpp
@@ -84,12 +84,12 @@ void SyncStatistics::processFile(const FilePair& file)
{
case SO_CREATE_NEW_LEFT:
++createLeft_;
- bytesToProcess_ += static_cast<int64_t>(file.getFileSize<RIGHT_SIDE>());
+ bytesToProcess_ += static_cast<int64_t>(file.getFileSize<SelectSide::right>());
break;
case SO_CREATE_NEW_RIGHT:
++createRight_;
- bytesToProcess_ += static_cast<int64_t>(file.getFileSize<LEFT_SIDE>());
+ bytesToProcess_ += static_cast<int64_t>(file.getFileSize<SelectSide::left>());
break;
case SO_DELETE_LEFT:
@@ -117,20 +117,20 @@ void SyncStatistics::processFile(const FilePair& file)
case SO_OVERWRITE_LEFT:
++updateLeft_;
- bytesToProcess_ += static_cast<int64_t>(file.getFileSize<RIGHT_SIDE>());
+ bytesToProcess_ += static_cast<int64_t>(file.getFileSize<SelectSide::right>());
physicalDeleteLeft_ = true;
break;
case SO_OVERWRITE_RIGHT:
++updateRight_;
- bytesToProcess_ += static_cast<int64_t>(file.getFileSize<LEFT_SIDE>());
+ bytesToProcess_ += static_cast<int64_t>(file.getFileSize<SelectSide::left>());
physicalDeleteRight_ = true;
break;
case SO_UNRESOLVED_CONFLICT:
++conflictCount_;
if (conflictsPreview_.size() < CONFLICTS_PREVIEW_MAX)
- conflictsPreview_.push_back({ file.getRelativePathAny(), file.getSyncOpConflict() });
+ conflictsPreview_.push_back({file.getRelativePathAny(), file.getSyncOpConflict()});
break;
case SO_COPY_METADATA_TO_LEFT:
@@ -186,7 +186,7 @@ void SyncStatistics::processLink(const SymlinkPair& link)
case SO_UNRESOLVED_CONFLICT:
++conflictCount_;
if (conflictsPreview_.size() < CONFLICTS_PREVIEW_MAX)
- conflictsPreview_.push_back({ link.getRelativePathAny(), link.getSyncOpConflict() });
+ conflictsPreview_.push_back({link.getRelativePathAny(), link.getSyncOpConflict()});
break;
case SO_MOVE_LEFT_FROM:
@@ -228,7 +228,7 @@ void SyncStatistics::processFolder(const FolderPair& folder)
case SO_UNRESOLVED_CONFLICT:
++conflictCount_;
if (conflictsPreview_.size() < CONFLICTS_PREVIEW_MAX)
- conflictsPreview_.push_back({ folder.getRelativePathAny(), folder.getSyncOpConflict() });
+ conflictsPreview_.push_back({folder.getRelativePathAny(), folder.getSyncOpConflict()});
break;
case SO_OVERWRITE_LEFT:
@@ -272,7 +272,7 @@ public:
{
MinimumDiskSpaceNeeded inst;
inst.recurse(baseFolder);
- return { inst.spaceNeededLeft_, inst.spaceNeededRight_ };
+ return {inst.spaceNeededLeft_, inst.spaceNeededRight_};
}
private:
@@ -283,33 +283,33 @@ private:
switch (file.getSyncOperation()) //evaluate comparison result and sync direction
{
case SO_CREATE_NEW_LEFT:
- spaceNeededLeft_ += static_cast<int64_t>(file.getFileSize<RIGHT_SIDE>());
+ spaceNeededLeft_ += static_cast<int64_t>(file.getFileSize<SelectSide::right>());
break;
case SO_CREATE_NEW_RIGHT:
- spaceNeededRight_ += static_cast<int64_t>(file.getFileSize<LEFT_SIDE>());
+ spaceNeededRight_ += static_cast<int64_t>(file.getFileSize<SelectSide::left>());
break;
case SO_DELETE_LEFT:
- if (!file.isFollowedSymlink<LEFT_SIDE>())
- spaceNeededLeft_ -= static_cast<int64_t>(file.getFileSize<LEFT_SIDE>());
+ if (!file.isFollowedSymlink<SelectSide::left>())
+ spaceNeededLeft_ -= static_cast<int64_t>(file.getFileSize<SelectSide::left>());
break;
case SO_DELETE_RIGHT:
- if (!file.isFollowedSymlink<RIGHT_SIDE>())
- spaceNeededRight_ -= static_cast<int64_t>(file.getFileSize<RIGHT_SIDE>());
+ if (!file.isFollowedSymlink<SelectSide::right>())
+ spaceNeededRight_ -= static_cast<int64_t>(file.getFileSize<SelectSide::right>());
break;
case SO_OVERWRITE_LEFT:
- if (!file.isFollowedSymlink<LEFT_SIDE>())
- spaceNeededLeft_ -= static_cast<int64_t>(file.getFileSize<LEFT_SIDE>());
- spaceNeededLeft_ += static_cast<int64_t>(file.getFileSize<RIGHT_SIDE>());
+ if (!file.isFollowedSymlink<SelectSide::left>())
+ spaceNeededLeft_ -= static_cast<int64_t>(file.getFileSize<SelectSide::left>());
+ spaceNeededLeft_ += static_cast<int64_t>(file.getFileSize<SelectSide::right>());
break;
case SO_OVERWRITE_RIGHT:
- if (!file.isFollowedSymlink<RIGHT_SIDE>())
- spaceNeededRight_ -= static_cast<int64_t>(file.getFileSize<RIGHT_SIDE>());
- spaceNeededRight_ += static_cast<int64_t>(file.getFileSize<LEFT_SIDE>());
+ if (!file.isFollowedSymlink<SelectSide::right>())
+ spaceNeededRight_ -= static_cast<int64_t>(file.getFileSize<SelectSide::right>());
+ spaceNeededRight_ += static_cast<int64_t>(file.getFileSize<SelectSide::left>());
break;
case SO_DO_NOTHING:
@@ -332,11 +332,11 @@ private:
switch (folder.getSyncOperation())
{
case SO_DELETE_LEFT:
- if (!folder.isFollowedSymlink<LEFT_SIDE>())
+ if (!folder.isFollowedSymlink<SelectSide::left>())
recurse(folder); //not 100% correct: in fact more that what our model contains may be deleted (consider file filter!)
break;
case SO_DELETE_RIGHT:
- if (!folder.isFollowedSymlink<RIGHT_SIDE>())
+ if (!folder.isFollowedSymlink<SelectSide::right>())
recurse(folder);
break;
@@ -369,7 +369,7 @@ private:
std::vector<FolderPairSyncCfg> fff::extractSyncCfg(const MainConfiguration& mainCfg)
{
//merge first and additional pairs
- std::vector<LocalPairConfig> localCfgs = { mainCfg.firstPair };
+ std::vector<LocalPairConfig> localCfgs = {mainCfg.firstPair};
append(localCfgs, mainCfg.additionalPairs);
std::vector<FolderPairSyncCfg> output;
@@ -400,7 +400,7 @@ std::vector<FolderPairSyncCfg> fff::extractSyncCfg(const MainConfiguration& main
namespace
{
inline
-std::optional<SelectedSide> getTargetDirection(SyncOperation syncOp)
+std::optional<SelectSide> getTargetDirection(SyncOperation syncOp)
{
switch (syncOp)
{
@@ -410,7 +410,7 @@ std::optional<SelectedSide> getTargetDirection(SyncOperation syncOp)
case SO_COPY_METADATA_TO_LEFT:
case SO_MOVE_LEFT_FROM:
case SO_MOVE_LEFT_TO:
- return LEFT_SIDE;
+ return SelectSide::left;
case SO_CREATE_NEW_RIGHT:
case SO_DELETE_RIGHT:
@@ -418,7 +418,7 @@ std::optional<SelectedSide> getTargetDirection(SyncOperation syncOp)
case SO_COPY_METADATA_TO_RIGHT:
case SO_MOVE_RIGHT_FROM:
case SO_MOVE_RIGHT_TO:
- return RIGHT_SIDE;
+ return SelectSide::right;
case SO_DO_NOTHING:
case SO_EQUAL:
@@ -433,8 +433,8 @@ std::optional<SelectedSide> getTargetDirection(SyncOperation syncOp)
bool significantDifferenceDetected(const SyncStatistics& folderPairStat)
{
//initial file copying shall not be detected as major difference
- if ((folderPairStat.createCount< LEFT_SIDE>() == 0 ||
- folderPairStat.createCount<RIGHT_SIDE>() == 0) &&
+ if ((folderPairStat.createCount< SelectSide::left>() == 0 ||
+ folderPairStat.createCount<SelectSide::right>() == 0) &&
folderPairStat.updateCount () == 0 &&
folderPairStat.deleteCount () == 0 &&
folderPairStat.conflictCount() == 0)
@@ -463,14 +463,15 @@ void flushFileBuffers(const Zstring& nativeFilePath) //throw FileError
}
-void verifyFiles(const AbstractPath& sourcePath, const AbstractPath& targetPath, const IOCallback& notifyUnbufferedIO /*throw X*/) //throw FileError, X
+void verifyFiles(const AbstractPath& sourcePath, const AbstractPath& targetPath, const IoCallback& notifyUnbufferedIO /*throw X*/) //throw FileError, X
{
try
{
//do like "copy /v": 1. flush target file buffers, 2. read again as usual (using OS buffers)
// => it seems OS buffers are not invalidated by this: snake oil???
- if (std::optional<Zstring> nativeTargetPath = AFS::getNativeItemPath(targetPath))
- flushFileBuffers(*nativeTargetPath); //throw FileError
+ if (const Zstring& targetPathNative = getNativeItemPath(targetPath);
+ !targetPathNative.empty())
+ flushFileBuffers(targetPathNative); //throw FileError
if (!filesHaveSameContent(sourcePath, targetPath, notifyUnbufferedIO)) //throw FileError, X
throw FileError(replaceCpy(replaceCpy(_("%x and %y have different content."),
@@ -546,7 +547,7 @@ AFS::FileCopyResult copyFileTransactional(const AbstractPath& apSource, const AF
bool copyFilePermissions,
bool transactionalCopy,
const std::function<void()>& onDeleteTargetFile /*throw X*/,
- const IOCallback& notifyUnbufferedIO /*throw X*/,
+ const IoCallback& notifyUnbufferedIO /*throw X*/,
std::mutex& singleThread)
{
return parallelScope([=]
@@ -560,7 +561,7 @@ void recycleItemIfExists(AFS::RecycleSession& recyclerSession, const AbstractPat
{ parallelScope([=, &recyclerSession] { return recyclerSession.recycleItemIfExists(ap, logicalRelPath); /*throw FileError*/ }, singleThread); }
inline //FileVersioner::revisionFile() is internally synchronized!
-void revisionFile(FileVersioner& versioner, const FileDescriptor& fileDescr, const Zstring& relativePath, const IOCallback& notifyUnbufferedIO /*throw X*/, std::mutex& singleThread) //throw FileError, X
+void revisionFile(FileVersioner& versioner, const FileDescriptor& fileDescr, const Zstring& relativePath, const IoCallback& notifyUnbufferedIO /*throw X*/, std::mutex& singleThread) //throw FileError, X
{ parallelScope([=, &versioner] { return versioner.revisionFile(fileDescr, relativePath, notifyUnbufferedIO); /*throw FileError, X*/ }, singleThread); }
inline //FileVersioner::revisionSymlink() is internally synchronized!
@@ -572,12 +573,12 @@ void revisionFolder(FileVersioner& versioner,
const AbstractPath& folderPath, const Zstring& relativePath,
const std::function<void(const std::wstring& displayPathFrom, const std::wstring& displayPathTo)>& onBeforeFileMove /*throw X*/,
const std::function<void(const std::wstring& displayPathFrom, const std::wstring& displayPathTo)>& onBeforeFolderMove /*throw X*/,
- const IOCallback& notifyUnbufferedIO /*throw X*/,
+ const IoCallback& notifyUnbufferedIO /*throw X*/,
std::mutex& singleThread) //throw FileError, X
{ parallelScope([=, &versioner] { versioner.revisionFolder(folderPath, relativePath, onBeforeFileMove, onBeforeFolderMove, notifyUnbufferedIO); /*throw FileError, X*/ }, singleThread); }
inline
-void verifyFiles(const AbstractPath& apSource, const AbstractPath& apTarget, const IOCallback& notifyUnbufferedIO /*throw X*/, std::mutex& singleThread) //throw FileError, X
+void verifyFiles(const AbstractPath& apSource, const AbstractPath& apTarget, const IoCallback& notifyUnbufferedIO /*throw X*/, std::mutex& singleThread) //throw FileError, X
{ parallelScope([=] { ::verifyFiles(apSource, apTarget, notifyUnbufferedIO); /*throw FileError, X*/ }, singleThread); }
}
@@ -970,18 +971,24 @@ private:
static bool containsMoveTarget(const FolderPair& parent);
void executeFileMove(FilePair& file); //throw ThreadStopRequest
- template <SelectedSide side> void executeFileMoveImpl(FilePair& fileFrom, FilePair& fileTo); //throw ThreadStopRequest
+ template <SelectSide side> void executeFileMoveImpl(FilePair& fileFrom, FilePair& fileTo); //throw ThreadStopRequest
- void synchronizeFile(FilePair& file); //
- template <SelectedSide side> void synchronizeFileInt(FilePair& file, SyncOperation syncOp); //throw FileError, ErrorMoveUnsupported, ThreadStopRequest
+ void synchronizeFile(FilePair& file); //
+ template <SelectSide side> void synchronizeFileInt(FilePair& file, SyncOperation syncOp); //throw FileError, ErrorMoveUnsupported, ThreadStopRequest
- void synchronizeLink(SymlinkPair& link); //
- template <SelectedSide sideTrg> void synchronizeLinkInt(SymlinkPair& link, SyncOperation syncOp); //throw FileError, ThreadStopRequest
+ void synchronizeLink(SymlinkPair& link); //
+ template <SelectSide sideTrg> void synchronizeLinkInt(SymlinkPair& link, SyncOperation syncOp); //throw FileError, ThreadStopRequest
- void synchronizeFolder(FolderPair& folder); //
- template <SelectedSide sideTrg> void synchronizeFolderInt(FolderPair& folder, SyncOperation syncOp); //throw FileError, ThreadStopRequest
+ void synchronizeFolder(FolderPair& folder); //
+ template <SelectSide sideTrg> void synchronizeFolderInt(FolderPair& folder, SyncOperation syncOp); //throw FileError, ThreadStopRequest
+ void logInfo(const std::wstring& rawText, const std::wstring& displayPath) { acb_.logInfo (replaceCpy(rawText, L"%x", fmtPath(displayPath))); }
void reportInfo(const std::wstring& rawText, const std::wstring& displayPath) { acb_.reportInfo(replaceCpy(rawText, L"%x", fmtPath(displayPath))); }
+
+ void logInfo(const std::wstring& rawText, const std::wstring& displayPath1, const std::wstring& displayPath2) //throw ThreadStopRequest
+ {
+ acb_.logInfo(replaceCpy(replaceCpy(rawText, L"%x", L'\n' + fmtPath(displayPath1)), L"%y", L'\n' + fmtPath(displayPath2))); //throw ThreadStopRequest
+ }
void reportInfo(const std::wstring& rawText, const std::wstring& displayPath1, const std::wstring& displayPath2) //throw ThreadStopRequest
{
acb_.reportInfo(replaceCpy(replaceCpy(rawText, L"%x", L'\n' + fmtPath(displayPath1)), L"%y", L'\n' + fmtPath(displayPath2))); //throw ThreadStopRequest
@@ -1146,10 +1153,9 @@ RingBuffer<Workload::WorkItems> FolderPairSyncer::getFolderLevelWorkItems(PassNo
}
-/*
- __________________________
- |Move algorithm, 0th pass|
- --------------------------
+/* __________________________
+ |Move algorithm, 0th pass|
+ --------------------------
1. loop over hierarchy and find "move targets" => remember required parent folders
2. create required folders hierarchically:
@@ -1174,7 +1180,7 @@ RingBuffer<Workload::WorkItems> FolderPairSyncer::getFolderLevelWorkItems(PassNo
b -> c/b
a -> b/a */
-template <SelectedSide side>
+template <SelectSide side>
void FolderPairSyncer::executeFileMoveImpl(FilePair& fileFrom, FilePair& fileTo) //throw ThreadStopRequest
{
const bool fallBackCopyDelete = [&]
@@ -1186,10 +1192,10 @@ void FolderPairSyncer::executeFileMoveImpl(FilePair& fileFrom, FilePair& fileTo)
if (parentMissing)
{
- reportInfo(_("Cannot move file %x to %y.") + L"\n\n" +
- replaceCpy(_("Parent folder %x is not existing."), L"%x", fmtPath(AFS::getDisplayPath(parentMissing->getAbstractPath<side>()))),
- AFS::getDisplayPath(fileFrom.getAbstractPath<side>()),
- AFS::getDisplayPath(fileTo .getAbstractPath<side>())); //throw ThreadStopRequest
+ logInfo(_("Cannot move file %x to %y.") + L"\n\n" +
+ replaceCpy(_("Parent folder %x is not existing."), L"%x", fmtPath(AFS::getDisplayPath(parentMissing->getAbstractPath<side>()))),
+ AFS::getDisplayPath(fileFrom.getAbstractPath<side>()),
+ AFS::getDisplayPath(fileTo .getAbstractPath<side>())); //throw ThreadStopRequest
return true;
}
@@ -1197,9 +1203,9 @@ void FolderPairSyncer::executeFileMoveImpl(FilePair& fileFrom, FilePair& fileTo)
if (haveNameClash(fileTo.getItemNameAny(), fileTo.parent().refSubFolders()) ||
haveNameClash(fileTo.getItemNameAny(), fileTo.parent().refSubLinks ()))
{
- reportInfo(_("Cannot move file %x to %y.") + L"\n\n" + replaceCpy(_("The name %x is already used by another item."), L"%x", fmtPath(fileTo.getItemNameAny())),
- AFS::getDisplayPath(fileFrom.getAbstractPath<side>()),
- AFS::getDisplayPath(fileTo .getAbstractPath<side>())); //throw ThreadStopRequest
+ logInfo(_("Cannot move file %x to %y.") + L"\n\n" + replaceCpy(_("The name %x is already used by another item."), L"%x", fmtPath(fileTo.getItemNameAny())),
+ AFS::getDisplayPath(fileFrom.getAbstractPath<side>()),
+ AFS::getDisplayPath(fileTo .getAbstractPath<side>())); //throw ThreadStopRequest
return true;
}
@@ -1212,7 +1218,7 @@ void FolderPairSyncer::executeFileMoveImpl(FilePair& fileFrom, FilePair& fileTo)
}
catch (const ErrorMoveUnsupported& e)
{
- acb_.reportInfo(e.toString()); //let user know that move operation is not supported, then fall back:
+ acb_.logInfo(e.toString()); //let user know that move operation is not supported, then fall back:
moveSupported = false;
}
}, acb_);
@@ -1226,7 +1232,7 @@ void FolderPairSyncer::executeFileMoveImpl(FilePair& fileFrom, FilePair& fileTo)
{
SyncStatistics statSrc(fileFrom);
SyncStatistics statTrg(fileTo);
- return { getCUD(statSrc) + getCUD(statTrg), statSrc.getBytesToProcess() + statTrg.getBytesToProcess() };
+ return {getCUD(statSrc) + getCUD(statTrg), statSrc.getBytesToProcess() + statTrg.getBytesToProcess()};
};
const auto [itemsBefore, bytesBefore] = getStats();
fileFrom.setMoveRef(nullptr);
@@ -1251,9 +1257,9 @@ void FolderPairSyncer::executeFileMove(FilePair& file) //throw ThreadStopRequest
assert(fileFrom->getMoveRef() == file.getId());
if (syncOp == SO_MOVE_LEFT_TO)
- executeFileMoveImpl<LEFT_SIDE>(*fileFrom, file); //throw ThreadStopRequest
+ executeFileMoveImpl<SelectSide::left>(*fileFrom, file); //throw ThreadStopRequest
else
- executeFileMoveImpl<RIGHT_SIDE>(*fileFrom, file); //throw ThreadStopRequest
+ executeFileMoveImpl<SelectSide::right>(*fileFrom, file); //throw ThreadStopRequest
}
else assert(false);
break;
@@ -1307,7 +1313,7 @@ bool FolderPairSyncer::needZeroPass(const FolderPair& folder)
case SO_OVERWRITE_RIGHT: //
case SO_COPY_METADATA_TO_LEFT:
case SO_COPY_METADATA_TO_RIGHT:
- assert((!folder.isEmpty<LEFT_SIDE>() && !folder.isEmpty<RIGHT_SIDE>()) || !containsMoveTarget(folder));
+ assert((!folder.isEmpty<SelectSide::left>() && !folder.isEmpty<SelectSide::right>()) || !containsMoveTarget(folder));
//we're good to move contained items
break;
case SO_DELETE_LEFT: //not possible in the context of planning to move a child item, see FolderPair::getSyncOperation()
@@ -1367,10 +1373,10 @@ FolderPairSyncer::PassNo FolderPairSyncer::getPass(const FilePair& file)
return PassNo::one;
case SO_OVERWRITE_LEFT:
- return file.getFileSize<LEFT_SIDE>() > file.getFileSize<RIGHT_SIDE>() ? PassNo::one : PassNo::two;
+ return file.getFileSize<SelectSide::left>() > file.getFileSize<SelectSide::right>() ? PassNo::one : PassNo::two;
case SO_OVERWRITE_RIGHT:
- return file.getFileSize<LEFT_SIDE>() < file.getFileSize<RIGHT_SIDE>() ? PassNo::one : PassNo::two;
+ return file.getFileSize<SelectSide::left>() < file.getFileSize<SelectSide::right>() ? PassNo::one : PassNo::two;
case SO_MOVE_LEFT_FROM: //
case SO_MOVE_RIGHT_FROM: // [!]
@@ -1467,20 +1473,20 @@ void FolderPairSyncer::synchronizeFile(FilePair& file) //throw FileError, ErrorM
{
const SyncOperation syncOp = file.getSyncOperation();
- if (std::optional<SelectedSide> sideTrg = getTargetDirection(syncOp))
+ if (std::optional<SelectSide> sideTrg = getTargetDirection(syncOp))
{
- if (*sideTrg == LEFT_SIDE)
- synchronizeFileInt<LEFT_SIDE>(file, syncOp);
+ if (*sideTrg == SelectSide::left)
+ synchronizeFileInt<SelectSide::left>(file, syncOp);
else
- synchronizeFileInt<RIGHT_SIDE>(file, syncOp);
+ synchronizeFileInt<SelectSide::right>(file, syncOp);
}
}
-template <SelectedSide sideTrg>
+template <SelectSide sideTrg>
void FolderPairSyncer::synchronizeFileInt(FilePair& file, SyncOperation syncOp) //throw FileError, ErrorMoveUnsupported, ThreadStopRequest
{
- constexpr SelectedSide sideSrc = OtherSide<sideTrg>::value;
+ constexpr SelectSide sideSrc = OtherSide<sideTrg>::value;
DeletionHandler& delHandlerTrg = SelectParam<sideTrg>::ref(delHandlerLeft_, delHandlerRight_);
switch (syncOp)
@@ -1498,7 +1504,7 @@ void FolderPairSyncer::synchronizeFileInt(FilePair& file, SyncOperation syncOp)
AsyncItemStatReporter statReporter(1, file.getFileSize<sideSrc>(), acb_);
try
{
- const AFS::FileCopyResult result = copyFileWithCallback({ file.getAbstractPath<sideSrc>(), file.getAttributes<sideSrc>() },
+ const AFS::FileCopyResult result = copyFileWithCallback({file.getAbstractPath<sideSrc>(), file.getAttributes<sideSrc>()},
targetPath,
nullptr, //onDeleteTargetFile: nothing to delete
//if existing: undefined behavior! (e.g. fail/overwrite/auto-rename)
@@ -1509,8 +1515,8 @@ void FolderPairSyncer::synchronizeFileInt(FilePair& file, SyncOperation syncOp)
file.setSyncedTo<sideTrg>(file.getItemName<sideSrc>(), result.fileSize,
result.modTime, //target time set from source
result.modTime,
- result.targetFileId,
- result.sourceFileId,
+ result.targetFilePrint,
+ result.sourceFilePrint,
false, file.isFollowedSymlink<sideSrc>());
if (result.errorModTime)
@@ -1521,7 +1527,7 @@ void FolderPairSyncer::synchronizeFileInt(FilePair& file, SyncOperation syncOp)
break;
case CompareVariant::content: //just log, no warning:
case CompareVariant::size: //e.g. FTP server not supporting MFMT command
- acb_.reportInfo(result.errorModTime->toString());
+ acb_.logInfo(result.errorModTime->toString());
break;
}
}
@@ -1539,7 +1545,7 @@ void FolderPairSyncer::synchronizeFileInt(FilePair& file, SyncOperation syncOp)
statReporter.reportDelta(1, 0); //even if the source item does not exist anymore, significant I/O work was done => report
file.removeObject<sideSrc>(); //source deleted meanwhile...nothing was done (logical point of view!)
- reportInfo(txtSourceItemNotFound_, AFS::getDisplayPath(file.getAbstractPath<sideSrc>())); //throw ThreadStopRequest
+ logInfo(txtSourceItemNotFound_, AFS::getDisplayPath(file.getAbstractPath<sideSrc>())); //throw ThreadStopRequest
}
else
throw;
@@ -1553,7 +1559,7 @@ void FolderPairSyncer::synchronizeFileInt(FilePair& file, SyncOperation syncOp)
{
AsyncItemStatReporter statReporter(1, 0, acb_);
- delHandlerTrg.removeFileWithCallback({ file.getAbstractPath<sideTrg>(), file.getAttributes<sideTrg>() },
+ delHandlerTrg.removeFileWithCallback({file.getAbstractPath<sideTrg>(), file.getAttributes<sideTrg>()},
file.getRelativePath<sideTrg>(), statReporter, singleThread_); //throw FileError, X
file.removeObject<sideTrg>(); //update FilePair
}
@@ -1566,8 +1572,8 @@ void FolderPairSyncer::synchronizeFileInt(FilePair& file, SyncOperation syncOp)
FilePair* fileTo = &file;
assert(fileFrom->getMoveRef() == fileTo->getId());
- assert((fileFrom->getSyncOperation() == SO_MOVE_LEFT_FROM && fileTo->getSyncOperation() == SO_MOVE_LEFT_TO && sideTrg == LEFT_SIDE) ||
- (fileFrom->getSyncOperation() == SO_MOVE_RIGHT_FROM && fileTo->getSyncOperation() == SO_MOVE_RIGHT_TO && sideTrg == RIGHT_SIDE));
+ assert((fileFrom->getSyncOperation() == SO_MOVE_LEFT_FROM && fileTo->getSyncOperation() == SO_MOVE_LEFT_TO && sideTrg == SelectSide::left) ||
+ (fileFrom->getSyncOperation() == SO_MOVE_RIGHT_FROM && fileTo->getSyncOperation() == SO_MOVE_RIGHT_TO && sideTrg == SelectSide::right));
const AbstractPath pathFrom = fileFrom->getAbstractPath<sideTrg>();
const AbstractPath pathTo = fileTo ->getAbstractPath<sideTrg>();
@@ -1587,8 +1593,8 @@ void FolderPairSyncer::synchronizeFileInt(FilePair& file, SyncOperation syncOp)
fileTo ->getFileSize<sideSrc>(),
fileFrom->getLastWriteTime<sideTrg>(),
fileTo ->getLastWriteTime<sideSrc>(),
- fileFrom->getFileId<sideTrg>(),
- fileTo ->getFileId<sideSrc>(),
+ fileFrom->getFilePrint<sideTrg>(),
+ fileTo ->getFilePrint<sideSrc>(),
fileFrom->isFollowedSymlink<sideTrg>(),
fileTo ->isFollowedSymlink<sideSrc>());
fileFrom->removeObject<sideTrg>(); //remove only *after* evaluating "fileFrom, sideTrg"!
@@ -1624,7 +1630,7 @@ void FolderPairSyncer::synchronizeFileInt(FilePair& file, SyncOperation syncOp)
FileAttributes followedTargetAttr = file.getAttributes<sideTrg>();
followedTargetAttr.isFollowedSymlink = false;
- delHandlerTrg.removeFileWithCallback({ targetPathResolvedOld, followedTargetAttr }, file.getRelativePath<sideTrg>(), statReporter, singleThread_); //throw FileError, X
+ delHandlerTrg.removeFileWithCallback({targetPathResolvedOld, followedTargetAttr}, file.getRelativePath<sideTrg>(), statReporter, singleThread_); //throw FileError, X
//no (logical) item count update desired - but total byte count may change, e.g. move(copy) old file to versioning dir
statReporter.reportDelta(-1, 0); //undo item stats reporting within DeletionHandler::removeFileWithCallback()
@@ -1635,7 +1641,7 @@ void FolderPairSyncer::synchronizeFileInt(FilePair& file, SyncOperation syncOp)
//=> if failSafeFileCopy_ : don't run callbacks that could throw
};
- const AFS::FileCopyResult result = copyFileWithCallback({ file.getAbstractPath<sideSrc>(), file.getAttributes<sideSrc>() },
+ const AFS::FileCopyResult result = copyFileWithCallback({file.getAbstractPath<sideSrc>(), file.getAttributes<sideSrc>()},
targetPathResolvedNew,
onDeleteTargetFile,
statReporter); //throw FileError, ThreadStopRequest, X
@@ -1645,8 +1651,8 @@ void FolderPairSyncer::synchronizeFileInt(FilePair& file, SyncOperation syncOp)
file.setSyncedTo<sideTrg>(file.getItemName<sideSrc>(), result.fileSize,
result.modTime, //target time set from source
result.modTime,
- result.targetFileId,
- result.sourceFileId,
+ result.targetFilePrint,
+ result.sourceFilePrint,
file.isFollowedSymlink<sideTrg>(),
file.isFollowedSymlink<sideSrc>());
@@ -1658,7 +1664,7 @@ void FolderPairSyncer::synchronizeFileInt(FilePair& file, SyncOperation syncOp)
break;
case CompareVariant::content: //just log, no warning:
case CompareVariant::size: //e.g. FTP server not supporting MFMT command
- acb_.reportInfo(result.errorModTime->toString());
+ acb_.logInfo(result.errorModTime->toString());
break;
}
}
@@ -1691,10 +1697,10 @@ void FolderPairSyncer::synchronizeFileInt(FilePair& file, SyncOperation syncOp)
//-> both sides *should* be completely equal now...
assert(file.getFileSize<sideTrg>() == file.getFileSize<sideSrc>());
file.setSyncedTo<sideTrg>(file.getItemName<sideSrc>(), file.getFileSize<sideSrc>(),
- file.getLastWriteTime<sideTrg>(),
- file.getLastWriteTime<sideSrc>(),
- file.getFileId <sideTrg>(),
- file.getFileId <sideSrc>(),
+ file.getLastWriteTime <sideTrg>(),
+ file.getLastWriteTime <sideSrc>(),
+ file.getFilePrint <sideTrg>(),
+ file.getFilePrint <sideSrc>(),
file.isFollowedSymlink<sideTrg>(),
file.isFollowedSymlink<sideSrc>());
}
@@ -1716,20 +1722,20 @@ void FolderPairSyncer::synchronizeLink(SymlinkPair& link) //throw FileError, Thr
{
const SyncOperation syncOp = link.getSyncOperation();
- if (std::optional<SelectedSide> sideTrg = getTargetDirection(syncOp))
+ if (std::optional<SelectSide> sideTrg = getTargetDirection(syncOp))
{
- if (*sideTrg == LEFT_SIDE)
- synchronizeLinkInt<LEFT_SIDE>(link, syncOp);
+ if (*sideTrg == SelectSide::left)
+ synchronizeLinkInt<SelectSide::left>(link, syncOp);
else
- synchronizeLinkInt<RIGHT_SIDE>(link, syncOp);
+ synchronizeLinkInt<SelectSide::right>(link, syncOp);
}
}
-template <SelectedSide sideTrg>
+template <SelectSide sideTrg>
void FolderPairSyncer::synchronizeLinkInt(SymlinkPair& symlink, SyncOperation syncOp) //throw FileError, ThreadStopRequest
{
- constexpr SelectedSide sideSrc = OtherSide<sideTrg>::value;
+ constexpr SelectSide sideSrc = OtherSide<sideTrg>::value;
DeletionHandler& delHandlerTrg = SelectParam<sideTrg>::ref(delHandlerLeft_, delHandlerRight_);
switch (syncOp)
@@ -1771,7 +1777,7 @@ void FolderPairSyncer::synchronizeLinkInt(SymlinkPair& symlink, SyncOperation sy
statReporter.reportDelta(1, 0);
symlink.removeObject<sideSrc>(); //source deleted meanwhile...nothing was done (logical point of view!)
- reportInfo(txtSourceItemNotFound_, AFS::getDisplayPath(symlink.getAbstractPath<sideSrc>())); //throw ThreadStopRequest
+ logInfo(txtSourceItemNotFound_, AFS::getDisplayPath(symlink.getAbstractPath<sideSrc>())); //throw ThreadStopRequest
}
else
throw;
@@ -1865,20 +1871,20 @@ void FolderPairSyncer::synchronizeFolder(FolderPair& folder) //throw FileError,
{
const SyncOperation syncOp = folder.getSyncOperation();
- if (std::optional<SelectedSide> sideTrg = getTargetDirection(syncOp))
+ if (std::optional<SelectSide> sideTrg = getTargetDirection(syncOp))
{
- if (*sideTrg == LEFT_SIDE)
- synchronizeFolderInt<LEFT_SIDE>(folder, syncOp);
+ if (*sideTrg == SelectSide::left)
+ synchronizeFolderInt<SelectSide::left>(folder, syncOp);
else
- synchronizeFolderInt<RIGHT_SIDE>(folder, syncOp);
+ synchronizeFolderInt<SelectSide::right>(folder, syncOp);
}
}
-template <SelectedSide sideTrg>
+template <SelectSide sideTrg>
void FolderPairSyncer::synchronizeFolderInt(FolderPair& folder, SyncOperation syncOp) //throw FileError, ThreadStopRequest
{
- constexpr SelectedSide sideSrc = OtherSide<sideTrg>::value;
+ constexpr SelectSide sideSrc = OtherSide<sideTrg>::value;
DeletionHandler& delHandlerTrg = SelectParam<sideTrg>::ref(delHandlerLeft_, delHandlerRight_);
switch (syncOp)
@@ -1932,7 +1938,7 @@ void FolderPairSyncer::synchronizeFolderInt(FolderPair& folder, SyncOperation sy
acb_.updateDataProcessed(1, 0); //even if the source item does not exist anymore, significant I/O work was done => report
acb_.updateDataTotal(getCUD(statsAfter) - getCUD(statsBefore) + 1, statsAfter.getBytesToProcess() - statsBefore.getBytesToProcess()); //noexcept
- reportInfo(txtSourceItemNotFound_, AFS::getDisplayPath(folder.getAbstractPath<sideSrc>())); //throw ThreadStopRequest
+ logInfo(txtSourceItemNotFound_, AFS::getDisplayPath(folder.getAbstractPath<sideSrc>())); //throw ThreadStopRequest
}
}
break;
@@ -2002,7 +2008,7 @@ AFS::FileCopyResult FolderPairSyncer::copyFileWithCallback(const FileDescriptor&
AsyncItemStatReporter& statReporter)
{
const AbstractPath& sourcePath = sourceDescr.path;
- const AFS::StreamAttributes sourceAttr{ sourceDescr.attr.modTime, sourceDescr.attr.fileSize, sourceDescr.attr.fileId };
+ const AFS::StreamAttributes sourceAttr{sourceDescr.attr.modTime, sourceDescr.attr.fileSize, sourceDescr.attr.filePrint};
auto copyOperation = [this, &sourceAttr, &targetPath, &onDeleteTargetFile, &statReporter](const AbstractPath& sourcePathTmp)
{
@@ -2048,7 +2054,7 @@ AFS::FileCopyResult FolderPairSyncer::copyFileWithCallback(const FileDescriptor&
//###########################################################################################
-template <SelectedSide side>
+template <SelectSide side>
bool baseFolderDrop(BaseFolderPair& baseFolder, PhaseCallback& callback)
{
const AbstractPath folderPath = baseFolder.getAbstractPath<side>();
@@ -2057,7 +2063,7 @@ bool baseFolderDrop(BaseFolderPair& baseFolder, PhaseCallback& callback)
{
const std::wstring errMsg = tryReportingError([&]
{
- const FolderStatus status = getFolderStatusNonBlocking({ folderPath },
+ const FolderStatus status = getFolderStatusNonBlocking({folderPath},
false /*allowUserInteraction*/, callback);
static_assert(std::is_same_v<decltype(status.failedChecks.begin()->second), FileError>);
@@ -2076,10 +2082,10 @@ bool baseFolderDrop(BaseFolderPair& baseFolder, PhaseCallback& callback)
}
-template <SelectedSide side> //create base directories first (if not yet existing) -> no symlink or attribute copying!
+template <SelectSide side> //create base directories first (if not yet existing) -> no symlink or attribute copying!
bool createBaseFolder(BaseFolderPair& baseFolder, bool copyFilePermissions, PhaseCallback& callback) //return false if fatal error occurred
{
- static const SelectedSide sideSrc = OtherSide<side>::value;
+ static const SelectSide sideSrc = OtherSide<side>::value;
const AbstractPath baseFolderPath = baseFolder.getAbstractPath<side>();
if (AFS::isNullPath(baseFolderPath))
@@ -2090,7 +2096,7 @@ bool createBaseFolder(BaseFolderPair& baseFolder, bool copyFilePermissions, Phas
bool temporaryNetworkDrop = false;
const std::wstring errMsg = tryReportingError([&]
{
- const FolderStatus status = getFolderStatusNonBlocking({ baseFolderPath },
+ const FolderStatus status = getFolderStatusNonBlocking({baseFolderPath},
false /*allowUserInteraction*/, callback);
static_assert(std::is_same_v<decltype(status.failedChecks.begin()->second), FileError>);
@@ -2196,7 +2202,7 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime
}
catch (const FileError& e) //failure is not critical => log only
{
- callback.reportInfo(e.toString()); //throw X
+ callback.logInfo(e.toString()); //throw X
}
//-------------------execute basic checks all at once BEFORE starting sync--------------------------------------
@@ -2243,8 +2249,8 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime
//===============================================================================
//exclude a few pathological cases (including empty left, right folders)
- if (baseFolder.getAbstractPath< LEFT_SIDE>() ==
- baseFolder.getAbstractPath<RIGHT_SIDE>())
+ if (baseFolder.getAbstractPath< SelectSide::left>() ==
+ baseFolder.getAbstractPath<SelectSide::right>())
{
jobType[folderIndex] = FolderPairJobType::SKIP;
continue;
@@ -2258,17 +2264,17 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime
continue;
}
- const bool writeLeft = folderPairStat.createCount<LEFT_SIDE>() +
- folderPairStat.updateCount<LEFT_SIDE>() +
- folderPairStat.deleteCount<LEFT_SIDE>() > 0;
+ const bool writeLeft = folderPairStat.createCount<SelectSide::left>() +
+ folderPairStat.updateCount<SelectSide::left>() +
+ folderPairStat.deleteCount<SelectSide::left>() > 0;
- const bool writeRight = folderPairStat.createCount<RIGHT_SIDE>() +
- folderPairStat.updateCount<RIGHT_SIDE>() +
- folderPairStat.deleteCount<RIGHT_SIDE>() > 0;
+ const bool writeRight = folderPairStat.createCount<SelectSide::right>() +
+ folderPairStat.updateCount<SelectSide::right>() +
+ folderPairStat.deleteCount<SelectSide::right>() > 0;
//check for empty target folder paths: this only makes sense if empty field is source (and no DB files need to be created)
- if ((AFS::isNullPath(baseFolder.getAbstractPath< LEFT_SIDE>()) && (writeLeft || folderPairCfg.saveSyncDB)) ||
- (AFS::isNullPath(baseFolder.getAbstractPath<RIGHT_SIDE>()) && (writeRight || folderPairCfg.saveSyncDB)))
+ if ((AFS::isNullPath(baseFolder.getAbstractPath< SelectSide::left>()) && (writeLeft || folderPairCfg.saveSyncDB)) ||
+ (AFS::isNullPath(baseFolder.getAbstractPath<SelectSide::right>()) && (writeRight || folderPairCfg.saveSyncDB)))
{
callback.reportFatalError(_("Target folder input field must not be empty."));
jobType[folderIndex] = FolderPairJobType::SKIP;
@@ -2278,8 +2284,8 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime
//check for network drops after comparison
// - convenience: exit sync right here instead of showing tons of errors during file copy
// - early failure! there's no point in evaluating subsequent warnings
- if (baseFolderDrop< LEFT_SIDE>(baseFolder, callback) ||
- baseFolderDrop<RIGHT_SIDE>(baseFolder, callback))
+ if (baseFolderDrop< SelectSide::left>(baseFolder, callback) ||
+ baseFolderDrop<SelectSide::right>(baseFolder, callback))
{
jobType[folderIndex] = FolderPairJobType::SKIP;
continue;
@@ -2300,8 +2306,8 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime
}
return false;
};
- if (sourceFolderMissing(baseFolder.getAbstractPath< LEFT_SIDE>(), baseFolder.isAvailable< LEFT_SIDE>()) ||
- sourceFolderMissing(baseFolder.getAbstractPath<RIGHT_SIDE>(), baseFolder.isAvailable<RIGHT_SIDE>()))
+ if (sourceFolderMissing(baseFolder.getAbstractPath< SelectSide::left>(), baseFolder.isAvailable< SelectSide::left>()) ||
+ sourceFolderMissing(baseFolder.getAbstractPath<SelectSide::right>(), baseFolder.isAvailable<SelectSide::right>()))
{
jobType[folderIndex] = FolderPairJobType::SKIP;
continue;
@@ -2323,20 +2329,20 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime
//prepare: check if versioning path itself will be synchronized (and was not excluded via filter)
checkVersioningPaths.insert(versioningFolderPath);
- checkVersioningBasePaths.emplace_back(baseFolder.getAbstractPath< LEFT_SIDE>(), &baseFolder.getFilter());
- checkVersioningBasePaths.emplace_back(baseFolder.getAbstractPath<RIGHT_SIDE>(), &baseFolder.getFilter());
+ checkVersioningBasePaths.emplace_back(baseFolder.getAbstractPath< SelectSide::left>(), &baseFolder.getFilter());
+ checkVersioningBasePaths.emplace_back(baseFolder.getAbstractPath<SelectSide::right>(), &baseFolder.getFilter());
}
//prepare: check if folders are used by multiple pairs in read/write access
- checkReadWriteBaseFolders.emplace_back(baseFolder.getAbstractPath< LEFT_SIDE>(), &baseFolder.getFilter(), writeLeft);
- checkReadWriteBaseFolders.emplace_back(baseFolder.getAbstractPath<RIGHT_SIDE>(), &baseFolder.getFilter(), writeRight);
+ checkReadWriteBaseFolders.emplace_back(baseFolder.getAbstractPath< SelectSide::left>(), &baseFolder.getFilter(), writeLeft);
+ checkReadWriteBaseFolders.emplace_back(baseFolder.getAbstractPath<SelectSide::right>(), &baseFolder.getFilter(), writeRight);
//check if more than 50% of total number of files/dirs are to be created/overwritten/deleted
- if (!AFS::isNullPath(baseFolder.getAbstractPath< LEFT_SIDE>()) &&
- !AFS::isNullPath(baseFolder.getAbstractPath<RIGHT_SIDE>()))
+ if (!AFS::isNullPath(baseFolder.getAbstractPath< SelectSide::left>()) &&
+ !AFS::isNullPath(baseFolder.getAbstractPath<SelectSide::right>()))
if (significantDifferenceDetected(folderPairStat))
- checkSignificantDiffPairs.emplace_back(baseFolder.getAbstractPath< LEFT_SIDE>(),
- baseFolder.getAbstractPath<RIGHT_SIDE>());
+ checkSignificantDiffPairs.emplace_back(baseFolder.getAbstractPath< SelectSide::left>(),
+ baseFolder.getAbstractPath<SelectSide::right>());
//check for sufficient free diskspace
auto checkSpace = [&](const AbstractPath& baseFolderPath, int64_t minSpaceNeeded)
@@ -2348,16 +2354,16 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime
if (0 <= freeSpace &&
freeSpace < minSpaceNeeded)
- checkDiskSpaceMissing.push_back({ baseFolderPath, { minSpaceNeeded, freeSpace } });
+ checkDiskSpaceMissing.push_back({baseFolderPath, {minSpaceNeeded, freeSpace}});
}
catch (const FileError& e) //failure is not critical => log only
{
- callback.reportInfo(e.toString()); //throw X
+ callback.logInfo(e.toString()); //throw X
}
};
const std::pair<int64_t, int64_t> spaceNeeded = MinimumDiskSpaceNeeded::calculate(baseFolder);
- checkSpace(baseFolder.getAbstractPath< LEFT_SIDE>(), spaceNeeded.first);
- checkSpace(baseFolder.getAbstractPath<RIGHT_SIDE>(), spaceNeeded.second);
+ checkSpace(baseFolder.getAbstractPath< SelectSide::left>(), spaceNeeded.first);
+ checkSpace(baseFolder.getAbstractPath<SelectSide::right>(), spaceNeeded.second);
//Windows: check if recycle bin really exists; if not, Windows will silently delete, which is just wrong
auto checkRecycler = [&](const AbstractPath& baseFolderPath)
@@ -2379,11 +2385,11 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime
};
if (folderPairCfg.handleDeletion == DeletionPolicy::recycler)
{
- if (folderPairStat.expectPhysicalDeletion<LEFT_SIDE>())
- checkRecycler(baseFolder.getAbstractPath<LEFT_SIDE>());
+ if (folderPairStat.expectPhysicalDeletion<SelectSide::left>())
+ checkRecycler(baseFolder.getAbstractPath<SelectSide::left>());
- if (folderPairStat.expectPhysicalDeletion<RIGHT_SIDE>())
- checkRecycler(baseFolder.getAbstractPath<RIGHT_SIDE>());
+ if (folderPairStat.expectPhysicalDeletion<SelectSide::right>())
+ checkRecycler(baseFolder.getAbstractPath<SelectSide::right>());
}
}
//-----------------------------------------------------------------
@@ -2399,8 +2405,8 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime
if (conflictCount > 0)
{
msg += L"\n\n" + _("Folder pair:") + L' ' +
- AFS::getDisplayPath(baseFolder->getAbstractPath< LEFT_SIDE>()) + L" <-> " +
- AFS::getDisplayPath(baseFolder->getAbstractPath<RIGHT_SIDE>());
+ AFS::getDisplayPath(baseFolder->getAbstractPath< SelectSide::left>()) + L" <-> " +
+ AFS::getDisplayPath(baseFolder->getAbstractPath<SelectSide::right>());
for (const SyncStatistics::ConflictInfo& item : conflictPreview)
msg += L'\n' + utfTo<std::wstring>(item.relPath) + L": " + item.msg;
@@ -2567,7 +2573,7 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime
if (!scopeFail)
callback.reportWarning(msg, warnings.warnModificationTimeError); //throw X
else //at least log warnings when sync is cancelled
- try { callback.reportInfo(msg); /*throw X*/} catch (...) {};
+ try { callback.logInfo(msg); /*throw X*/} catch (...) {};
}
//*INDENT-ON*
);
@@ -2575,19 +2581,19 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime
class PcbNoThrow : public PhaseCallback
{
public:
- PcbNoThrow(ProcessCallback& cb) : cb_(cb) {}
+ explicit PcbNoThrow(ProcessCallback& cb) : cb_(cb) {}
void updateDataProcessed(int itemsDelta, int64_t bytesDelta) override {} //sync DB/del-handler: logically not part of sync data, so let's ignore
void updateDataTotal (int itemsDelta, int64_t bytesDelta) override {} //
void requestUiUpdate(bool force) override { try { cb_.requestUiUpdate(force); /*throw X*/} catch (...) {}; }
- void updateStatus(const std::wstring& msg) override { try { cb_.updateStatus(msg); /*throw X*/} catch (...) {}; }
- void reportInfo (const std::wstring& msg) override { try { cb_.reportInfo (msg); /*throw X*/} catch (...) {}; }
+ void updateStatus(std::wstring&& msg) override { try { cb_.updateStatus(std::move(msg)); /*throw X*/} catch (...) {}; }
+ void logInfo(const std::wstring& msg) override { try { cb_.logInfo(msg); /*throw X*/} catch (...) {}; }
- void reportWarning(const std::wstring& msg, bool& warningActive) override { reportInfo(msg); /*ignore*/ }
- Response reportError (const ErrorInfo& errorInfo) override { reportInfo(errorInfo.msg); return Response::ignore; }
- void reportFatalError(const std::wstring& msg) override { reportInfo(msg); /*ignore*/ }
+ void reportWarning(const std::wstring& msg, bool& warningActive) override { logInfo(msg); /*ignore*/ }
+ Response reportError (const ErrorInfo& errorInfo) override { logInfo(errorInfo.msg); return Response::ignore; }
+ void reportFatalError(const std::wstring& msg) override { logInfo(msg); /*ignore*/ }
private:
ProcessCallback& cb_;
@@ -2608,20 +2614,20 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime
//------------------------------------------------------------------------------------------
if (folderCmp.size() > 1)
- callback.reportInfo(_("Synchronizing folder pair:") + L' ' + getVariantNameWithSymbol(folderPairCfg.syncVar) + L'\n' + //throw X
- L" " + AFS::getDisplayPath(baseFolder.getAbstractPath< LEFT_SIDE>()) + L'\n' +
- L" " + AFS::getDisplayPath(baseFolder.getAbstractPath<RIGHT_SIDE>()));
+ callback.logInfo(_("Synchronizing folder pair:") + L' ' + getVariantNameWithSymbol(folderPairCfg.syncVar) + L'\n' + //throw X
+ L" " + AFS::getDisplayPath(baseFolder.getAbstractPath< SelectSide::left>()) + L'\n' +
+ L" " + AFS::getDisplayPath(baseFolder.getAbstractPath<SelectSide::right>()));
//------------------------------------------------------------------------------------------
//checking a second time: (a long time may have passed since syncing the previous folder pairs!)
- if (baseFolderDrop< LEFT_SIDE>(baseFolder, callback) ||
- baseFolderDrop<RIGHT_SIDE>(baseFolder, callback))
+ if (baseFolderDrop< SelectSide::left>(baseFolder, callback) ||
+ baseFolderDrop<SelectSide::right>(baseFolder, callback))
continue;
//create base folders if not yet existing
if (folderPairStat.createCount() > 0 || folderPairCfg.saveSyncDB) //else: temporary network drop leading to deletions already caught by "sourceFolderMissing" check!
- if (!createBaseFolder< LEFT_SIDE>(baseFolder, copyFilePermissions, callback) || //+ detect temporary network drop!!
- !createBaseFolder<RIGHT_SIDE>(baseFolder, copyFilePermissions, callback)) //
+ if (!createBaseFolder< SelectSide::left>(baseFolder, copyFilePermissions, callback) || //+ detect temporary network drop!!
+ !createBaseFolder<SelectSide::right>(baseFolder, copyFilePermissions, callback)) //
continue;
//------------------------------------------------------------------------------------------
@@ -2644,10 +2650,10 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime
tryReportingError([&]
{
copyPermissionsFp = copyFilePermissions && //copy permissions only if asked for and supported by *both* sides!
- !AFS::isNullPath(baseFolder.getAbstractPath< LEFT_SIDE>()) && //scenario: directory selected on one side only
- !AFS::isNullPath(baseFolder.getAbstractPath<RIGHT_SIDE>()) && //
- AFS::supportPermissionCopy(baseFolder.getAbstractPath<LEFT_SIDE>(),
- baseFolder.getAbstractPath<RIGHT_SIDE>()); //throw FileError
+ !AFS::isNullPath(baseFolder.getAbstractPath< SelectSide::left>()) && //scenario: directory selected on one side only
+ !AFS::isNullPath(baseFolder.getAbstractPath<SelectSide::right>()) && //
+ AFS::supportPermissionCopy(baseFolder.getAbstractPath<SelectSide::left>(),
+ baseFolder.getAbstractPath<SelectSide::right>()); //throw FileError
}, callback); //throw X
@@ -2664,14 +2670,14 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime
};
const AbstractPath versioningFolderPath = createAbstractPath(folderPairCfg.versioningFolderPhrase);
- DeletionHandler delHandlerL(baseFolder.getAbstractPath<LEFT_SIDE>(),
- getEffectiveDeletionPolicy(baseFolder.getAbstractPath<LEFT_SIDE>()),
+ DeletionHandler delHandlerL(baseFolder.getAbstractPath<SelectSide::left>(),
+ getEffectiveDeletionPolicy(baseFolder.getAbstractPath<SelectSide::left>()),
versioningFolderPath,
folderPairCfg.versioningStyle,
std::chrono::system_clock::to_time_t(syncStartTime));
- DeletionHandler delHandlerR(baseFolder.getAbstractPath<RIGHT_SIDE>(),
- getEffectiveDeletionPolicy(baseFolder.getAbstractPath<RIGHT_SIDE>()),
+ DeletionHandler delHandlerR(baseFolder.getAbstractPath<SelectSide::right>(),
+ getEffectiveDeletionPolicy(baseFolder.getAbstractPath<SelectSide::right>()),
versioningFolderPath,
folderPairCfg.versioningStyle,
std::chrono::system_clock::to_time_t(syncStartTime));
diff --git a/FreeFileSync/Source/base/synchronization.h b/FreeFileSync/Source/base/synchronization.h
index cb66a4ad..592760ce 100644
--- a/FreeFileSync/Source/base/synchronization.h
+++ b/FreeFileSync/Source/base/synchronization.h
@@ -23,19 +23,19 @@ public:
SyncStatistics(const ContainerObject& hierObj);
SyncStatistics(const FilePair& file);
- template <SelectedSide side>
+ template <SelectSide side>
int createCount() const { return SelectParam<side>::ref(createLeft_, createRight_); }
int createCount() const { return createLeft_ + createRight_; }
- template <SelectedSide side>
+ template <SelectSide side>
int updateCount() const { return SelectParam<side>::ref(updateLeft_, updateRight_); }
int updateCount() const { return updateLeft_ + updateRight_; }
- template <SelectedSide side>
+ template <SelectSide side>
int deleteCount() const { return SelectParam<side>::ref(deleteLeft_, deleteRight_); }
int deleteCount() const { return deleteLeft_ + deleteRight_; }
- template <SelectedSide side>
+ template <SelectSide side>
bool expectPhysicalDeletion() const { return SelectParam<side>::ref(physicalDeleteLeft_, physicalDeleteRight_); }
int64_t getBytesToProcess() const { return bytesToProcess_; }
diff --git a/FreeFileSync/Source/base/versioning.cpp b/FreeFileSync/Source/base/versioning.cpp
index ffd98261..adb8f106 100644
--- a/FreeFileSync/Source/base/versioning.cpp
+++ b/FreeFileSync/Source/base/versioning.cpp
@@ -55,7 +55,7 @@ std::pair<time_t, Zstring> fff::impl::parseVersionedFileName(const Zstring& file
if (fileNameOrig.empty())
return {};
- return { t, std::move(fileNameOrig) };
+ return {t, std::move(fileNameOrig)};
}
@@ -179,7 +179,7 @@ void moveExistingItemToVersioning(const AbstractPath& sourcePath, const Abstract
}
-void FileVersioner::revisionFile(const FileDescriptor& fileDescr, const Zstring& relativePath, const IOCallback& notifyUnbufferedIO /*throw X*/) const //throw FileError, X
+void FileVersioner::revisionFile(const FileDescriptor& fileDescr, const Zstring& relativePath, const IoCallback& notifyUnbufferedIO /*throw X*/) const //throw FileError, X
{
if (std::optional<AFS::ItemType> type = AFS::itemStillExists(fileDescr.path)) //throw FileError
{
@@ -194,12 +194,12 @@ void FileVersioner::revisionFile(const FileDescriptor& fileDescr, const Zstring&
void FileVersioner::revisionFileImpl(const FileDescriptor& fileDescr, const Zstring& relativePath, //throw FileError, X
const std::function<void(const std::wstring& displayPathFrom, const std::wstring& displayPathTo)>& onBeforeMove,
- const IOCallback& notifyUnbufferedIO /*throw X*/) const
+ const IoCallback& notifyUnbufferedIO /*throw X*/) const
{
const AbstractPath& filePath = fileDescr.path;
const AbstractPath targetPath = generateVersionedPath(relativePath);
- const AFS::StreamAttributes fileAttr{ fileDescr.attr.modTime, fileDescr.attr.fileSize, fileDescr.attr.fileId };
+ const AFS::StreamAttributes fileAttr{fileDescr.attr.modTime, fileDescr.attr.fileSize, fileDescr.attr.filePrint};
if (onBeforeMove)
onBeforeMove(AFS::getDisplayPath(filePath), AFS::getDisplayPath(targetPath));
@@ -242,7 +242,7 @@ void FileVersioner::revisionSymlinkImpl(const AbstractPath& linkPath, const Zstr
void FileVersioner::revisionFolder(const AbstractPath& folderPath, const Zstring& relativePath, //throw FileError, X
const std::function<void(const std::wstring& displayPathFrom, const std::wstring& displayPathTo)>& onBeforeFileMove /*throw X*/,
const std::function<void(const std::wstring& displayPathFrom, const std::wstring& displayPathTo)>& onBeforeFolderMove /*throw X*/,
- const IOCallback& notifyUnbufferedIO /*throw X*/) const
+ const IoCallback& notifyUnbufferedIO /*throw X*/) const
{
//no error situation if directory is not existing! manual deletion relies on it!
if (std::optional<AFS::ItemType> type = AFS::itemStillExists(folderPath)) //throw FileError
@@ -260,7 +260,7 @@ void FileVersioner::revisionFolder(const AbstractPath& folderPath, const Zstring
void FileVersioner::revisionFolderImpl(const AbstractPath& folderPath, const Zstring& relativePath, //throw FileError, X
const std::function<void(const std::wstring& displayPathFrom, const std::wstring& displayPathTo)>& onBeforeFileMove,
const std::function<void(const std::wstring& displayPathFrom, const std::wstring& displayPathTo)>& onBeforeFolderMove,
- const IOCallback& notifyUnbufferedIO /*throw X*/) const
+ const IoCallback& notifyUnbufferedIO /*throw X*/) const
{
//create target directories only when needed in moveFileToVersioning(): avoid empty directories!
@@ -277,8 +277,8 @@ void FileVersioner::revisionFolderImpl(const AbstractPath& folderPath, const Zst
for (const AFS::FileInfo& fileInfo : files)
{
- const FileDescriptor fileDescr{ AFS::appendRelPath(folderPath, fileInfo.itemName),
- FileAttributes(fileInfo.modTime, fileInfo.fileSize, fileInfo.fileId, false /*isSymlink*/)};
+ const FileDescriptor fileDescr{AFS::appendRelPath(folderPath, fileInfo.itemName),
+ FileAttributes(fileInfo.modTime, fileInfo.fileSize, fileInfo.filePrint, false /*isFollowedSymlink*/)};
revisionFileImpl(fileDescr, relPathPf + fileInfo.itemName, onBeforeFileMove, notifyUnbufferedIO); //throw FileError, X
}
@@ -325,7 +325,7 @@ void findFileVersions(VersionInfoMap& versions,
const Zstring& relPathOrig = nativeAppendPaths(relPathOrigParent, fileNameOrig);
const AbstractPath& filePath = AFS::appendRelPath(parentFolderPath, fileName);
- versions[relPathOrig].push_back(VersionInfo{ versionTime, filePath, isSymlink });
+ versions[relPathOrig].push_back(VersionInfo{versionTime, filePath, isSymlink});
};
auto extractFileVersion = [&](const Zstring& fileName, bool isSymlink)
@@ -423,7 +423,7 @@ void fff::applyVersioningLimit(const std::set<VersioningLimitFolder>& folderLimi
false /*allowUserInteraction*/, callback); //throw X
foldersToRead.clear();
for (const AbstractPath& folderPath : status.existing)
- foldersToRead.insert(DirectoryKey({ folderPath, makeSharedRef<NullFilter>(), SymLinkHandling::direct }));
+ foldersToRead.insert(DirectoryKey({folderPath, makeSharedRef<NullFilter>(), SymLinkHandling::direct}));
if (!status.failedChecks.empty())
{
diff --git a/FreeFileSync/Source/base/versioning.h b/FreeFileSync/Source/base/versioning.h
index 7e707300..6c5d667b 100644
--- a/FreeFileSync/Source/base/versioning.h
+++ b/FreeFileSync/Source/base/versioning.h
@@ -53,7 +53,7 @@ public:
void revisionFile(const FileDescriptor& fileDescr, //throw FileError, X
const Zstring& relativePath,
//called frequently if move has to revert to copy + delete => see zen::copyFile for limitations when throwing exceptions!
- const zen::IOCallback& notifyUnbufferedIO /*throw X*/) const;
+ const zen::IoCallback& notifyUnbufferedIO /*throw X*/) const;
void revisionSymlink(const AbstractPath& linkPath, const Zstring& relativePath) const; //throw FileError
@@ -61,7 +61,7 @@ public:
const std::function<void(const std::wstring& displayPathFrom, const std::wstring& displayPathTo)>& onBeforeFileMove, /*throw X*/
const std::function<void(const std::wstring& displayPathFrom, const std::wstring& displayPathTo)>& onBeforeFolderMove, /*throw X*/
//called frequently if move has to revert to copy + delete => see zen::copyFile for limitations when throwing exceptions!
- const zen::IOCallback& notifyUnbufferedIO /*throw X*/) const;
+ const zen::IoCallback& notifyUnbufferedIO /*throw X*/) const;
private:
FileVersioner (const FileVersioner&) = delete;
@@ -69,7 +69,7 @@ private:
void revisionFileImpl(const FileDescriptor& fileDescr, const Zstring& relativePath, //throw FileError, X
const std::function<void(const std::wstring& displayPathFrom, const std::wstring& displayPathTo)>& onBeforeMove,
- const zen::IOCallback& notifyUnbufferedIO) const;
+ const zen::IoCallback& notifyUnbufferedIO) const;
void revisionSymlinkImpl(const AbstractPath& linkPath, const Zstring& relativePath, //throw FileError
const std::function<void(const std::wstring& displayPathFrom, const std::wstring& displayPathTo)>& onBeforeMove) const;
@@ -77,7 +77,7 @@ private:
void revisionFolderImpl(const AbstractPath& folderPath, const Zstring& relativePath,
const std::function<void(const std::wstring& displayPathFrom, const std::wstring& displayPathTo)>& onBeforeFileMove,
const std::function<void(const std::wstring& displayPathFrom, const std::wstring& displayPathTo)>& onBeforeFolderMove,
- const zen::IOCallback& notifyUnbufferedIO) const; //throw FileError, X
+ const zen::IoCallback& notifyUnbufferedIO) const; //throw FileError, X
AbstractPath generateVersionedPath(const Zstring& relativePath) const;
diff --git a/FreeFileSync/Source/base_tools.cpp b/FreeFileSync/Source/base_tools.cpp
index 8860ecef..73246dde 100644
--- a/FreeFileSync/Source/base_tools.cpp
+++ b/FreeFileSync/Source/base_tools.cpp
@@ -29,7 +29,7 @@ std::vector<unsigned int> fff::fromTimeShiftPhrase(const std::wstring& timeShift
}
minutes.erase(0);
- return { minutes.begin(), minutes.end() };
+ return {minutes.begin(), minutes.end()};
}
@@ -76,7 +76,7 @@ void fff::logNonDefaultSettings(const XmlGlobalSettings& activeSettings, PhaseCa
changedSettingsMsg += L"\n " + _("Verify copied files") + L" - " + (activeSettings.verifyFileCopy ? _("Enabled") : _("Disabled"));
if (!changedSettingsMsg.empty())
- callback.reportInfo(_("Using non-default global settings:") + changedSettingsMsg); //throw X
+ callback.logInfo(_("Using non-default global settings:") + changedSettingsMsg); //throw X
}
diff --git a/FreeFileSync/Source/config.cpp b/FreeFileSync/Source/config.cpp
index 06f4abb6..f4d2850b 100644
--- a/FreeFileSync/Source/config.cpp
+++ b/FreeFileSync/Source/config.cpp
@@ -13,6 +13,7 @@
#include <wx/intl.h>
#include "ffs_paths.h"
#include "base_tools.h"
+#include "afs/native.h"
using namespace zen;
@@ -1099,8 +1100,9 @@ void writeStruc(const ConfigFileItem& value, XmlElement& output)
out.attribute("Config", substituteFreeFileSyncDriveLetter(value.cfgFilePath));
out.attribute("LastSync", value.lastSyncTime);
- if (std::optional<Zstring> nativePath = AFS::getNativeItemPath(value.logFilePath))
- out.attribute("Log", substituteFreeFileSyncDriveLetter(*nativePath));
+ if (const Zstring& nativePath = getNativeItemPath(value.logFilePath);
+ !nativePath.empty())
+ out.attribute("Log", substituteFreeFileSyncDriveLetter(nativePath));
else
out.attribute("Log", AFS::getInitPathPhrase(value.logFilePath));
@@ -1109,7 +1111,7 @@ void writeStruc(const ConfigFileItem& value, XmlElement& output)
const auto& [highR, lowR] = hexify(value.backColor.Red ());
const auto& [highG, lowG] = hexify(value.backColor.Green());
const auto& [highB, lowB] = hexify(value.backColor.Blue ());
- out.attribute("Color", std::string({ highR, lowR, highG, lowG, highB, lowB }));
+ out.attribute("Color", std::string({highR, lowR, highG, lowG, highB, lowB}));
}
}
diff --git a/FreeFileSync/Source/config.h b/FreeFileSync/Source/config.h
index b5995318..dc2c6bd6 100644
--- a/FreeFileSync/Source/config.h
+++ b/FreeFileSync/Source/config.h
@@ -8,6 +8,7 @@
#define PROCESS_XML_H_28345825704254262435
#include <wx/gdicmn.h>
+#include <zen/file_access.h>
#include "localization.h"
#include "base/structures.h"
#include "ui/file_grid_attr.h"
@@ -130,7 +131,7 @@ struct XmlGlobalSettings
bool copyLockedFiles = false; //safer default: avoid copies of partially written files
bool copyFilePermissions = false;
- int fileTimeTolerance = 2; //max. allowed file time deviation; < 0 means unlimited tolerance; default 2s: FAT vs NTFS
+ int fileTimeTolerance = zen::FAT_FILE_TIME_PRECISION_SEC; //max. allowed file time deviation; < 0 means unlimited tolerance; default 2s: FAT vs NTFS
bool runWithBackgroundPriority = false;
bool createLockFile = true;
bool verifyFileCopy = false;
@@ -227,8 +228,8 @@ struct XmlGlobalSettings
/* CONTRACT: first entry: show item in file browser
default external app descriptions will be translated "on the fly"!!! */
//"xdg-open \"%parent_path%\"" -> not good enough: we need %local_path% for proper MTP/Google Drive handling
- { L"Browse directory", "xdg-open \"$(dirname \"%local_path%\")\"" },
- { L"Open with default application", "xdg-open \"%local_path%\"" },
+ {L"Browse directory", "xdg-open \"$(dirname \"%local_path%\")\""},
+ {L"Open with default application", "xdg-open \"%local_path%\"" },
//mark for extraction: _("Browse directory") Linux doesn't use the term "folder"
};
diff --git a/FreeFileSync/Source/fatal_error.h b/FreeFileSync/Source/fatal_error.h
index eb025472..4749ac75 100644
--- a/FreeFileSync/Source/fatal_error.h
+++ b/FreeFileSync/Source/fatal_error.h
@@ -16,7 +16,7 @@
namespace fff
{
//write error message to a file (even with corrupted stack)- call in desperate situations when no other means of error handling is available
-void logFatalError(const std::string& msg); //noexcept
+void logFatalError(const std::wstring& msg); //noexcept
@@ -28,12 +28,12 @@ void logFatalError(const std::string& msg); //noexcept
//##################### implementation ############################
inline
-void logFatalError(const std::string& msg) //noexcept
+void logFatalError(const std::wstring& msg) //noexcept
{
using namespace zen;
assert(false); //this is stuff we like to debug
- const std::string logEntry = '[' + utfTo<std::string>(formatTime(formatDateTimeTag)) + "] " + msg;
+ const std::string logEntry = '[' + utfTo<std::string>(formatTime(formatDateTimeTag)) + "] " + utfTo<std::string>(msg);
try
{
setFileContent(getConfigDirPathPf() + Zstr("LastError.log"), logEntry, nullptr /*notifyUnbufferedIO*/); //throw FileError
diff --git a/FreeFileSync/Source/ffs_paths.cpp b/FreeFileSync/Source/ffs_paths.cpp
index 6a81257e..44b80f6f 100644
--- a/FreeFileSync/Source/ffs_paths.cpp
+++ b/FreeFileSync/Source/ffs_paths.cpp
@@ -8,8 +8,6 @@
#include <zen/file_access.h>
#include <zen/thread.h>
#include <zen/sys_info.h>
-#include <wx/stdpaths.h>
-#include <wx/app.h>
#include <iostream> //std::cerr
@@ -48,21 +46,6 @@ Zstring fff::getInstallDirPath()
}
-//getFfsVolumeId() might be called during static destruction, e.g. async update check
-VolumeId fff::getFfsVolumeId() //throw FileError
-{
- static VolumeId volumeId; //POD => no "magic static" code gen
- static constinit2 std::once_flag onceFlagGetFfsVolumeId; //=> no "magic static" code gen
- std::call_once(onceFlagGetFfsVolumeId, [] { volumeId = getVolumeId(getRealProcessPath()); }); //throw FileError
- return volumeId;
-}
-
-
-bool fff::isPortableVersion()
-{
- return false; //users want local installation type: https://freefilesync.org/forum/viewtopic.php?t=5750
-
-}
Zstring fff::getResourceDirPf()
@@ -73,34 +56,37 @@ Zstring fff::getResourceDirPf()
Zstring fff::getConfigDirPathPf()
{
- warn_static("Linux/macOS TODO: consider getuid() == 0 as request for elevation, NOT impersonation")
-
//note: compiler generates magic-statics code => fine, we don't expect accesses during shutdown
- static const Zstring cfgFolderPathPf = []
+ static const Zstring ffsConfigPathPf = []
{
- //make independent from wxWidgets global variable "appname"; support being called by RealTimeSync
- auto appName = wxTheApp->GetAppName();
- wxTheApp->SetAppName(L"FreeFileSync");
- ZEN_ON_SCOPE_EXIT(wxTheApp->SetAppName(appName));
-
- //OS standard path (XDG layout): ~/.config/FreeFileSync
- //wxBug: wxStandardPaths::GetUserDataDir() does not honor FileLayout_XDG flag
- wxStandardPaths::Get().SetFileLayout(wxStandardPaths::FileLayout_XDG);
- const Zstring cfgFolderPath = appendSeparator(utfTo<Zstring>(wxStandardPaths::Get().GetUserConfigDir())) + "FreeFileSync";
+ /* Windows: %AppData%\FreeFileSync
+ macOS: ~/Library/Application Support/FreeFileSync
+ Linux (XDG layout): ~/.config/FreeFileSync */
+ const Zstring& ffsConfigPath = []
+ {
+ try
+ {
+ return
+ appendSeparator(getUserDataPath()) + Zstr("FreeFileSync"); //throw FileError
+ }
+ catch (const FileError& e)
+ {
+ throw std::runtime_error(std::string(__FILE__) + '[' + numberTo<std::string>(__LINE__) + "] " + utfTo<std::string>(e.toString()));
+ }
+ }();
try //create the config folder if not existing + create "Logs" subfolder while we're at it
{
- createDirectoryIfMissingRecursion(appendSeparator(cfgFolderPath) + Zstr("Logs")); //throw FileError
+ createDirectoryIfMissingRecursion(appendSeparator(ffsConfigPath) + Zstr("Logs")); //throw FileError
}
catch (const FileError& e)
{
assert(false);
std::cerr << utfTo<std::string>(e.toString()) << '\n';
}
-
- return appendSeparator(cfgFolderPath);
+ return appendSeparator(ffsConfigPath);
}();
- return cfgFolderPathPf;
+ return ffsConfigPathPf;
}
diff --git a/FreeFileSync/Source/ffs_paths.h b/FreeFileSync/Source/ffs_paths.h
index cbf4edb4..a2310a14 100644
--- a/FreeFileSync/Source/ffs_paths.h
+++ b/FreeFileSync/Source/ffs_paths.h
@@ -8,7 +8,6 @@
#define FFS_PATHS_H_842759083425342534253
#include <zen/zstring.h>
-#include <zen/file_id_def.h>
namespace fff
@@ -20,11 +19,8 @@ Zstring getResourceDirPf (); //resource directory WITH trailing path separator
Zstring getConfigDirPathPf(); // config directory WITH trailing path separator
//------------------------------------------------------------------------------
-bool isPortableVersion();
- Zstring getInstallDirPath();
-
-zen::VolumeId getFfsVolumeId(); //throw FileError
+Zstring getInstallDirPath();
Zstring getFreeFileSyncLauncherPath(); //full path to application launcher C:\...\FreeFileSync.exe
}
diff --git a/FreeFileSync/Source/icon_buffer.cpp b/FreeFileSync/Source/icon_buffer.cpp
index bdd5eab2..30ad3173 100644
--- a/FreeFileSync/Source/icon_buffer.cpp
+++ b/FreeFileSync/Source/icon_buffer.cpp
@@ -76,7 +76,7 @@ public:
std::lock_guard dummy(lockFiles_);
workLoad_ = newLoad;
}
- conditionNewWork_.notify_all(); //instead of notify_one(); workaround bug: https://svn.boost.org/trac/boost/ticket/7796
+ conditionNewWork_.notify_all(); //instead of notify_one(); work around bug: https://svn.boost.org/trac/boost/ticket/7796
//condition handling, see: https://www.boost.org/doc/libs/1_43_0/doc/html/thread/synchronization.html#thread.synchronization.condvar_ref
}
diff --git a/FreeFileSync/Source/localization.cpp b/FreeFileSync/Source/localization.cpp
index 40c7b890..b04bc3b5 100644
--- a/FreeFileSync/Source/localization.cpp
+++ b/FreeFileSync/Source/localization.cpp
@@ -49,7 +49,7 @@ public:
std::wstring translate(const std::wstring& singular, const std::wstring& plural, int64_t n) const override
{
- auto it = transMappingPl_.find({ singular, plural });
+ auto it = transMappingPl_.find({singular, plural});
if (it != transMappingPl_.end())
{
const size_t formNo = pluralParser_->getForm(n);
@@ -89,13 +89,11 @@ FFSTranslation::FFSTranslation(const std::string& lngStream) //throw lng::Parsin
for (const std::string& pf : pluralForms)
transPluralForms.push_back(utfTo<std::wstring>(pf));
- transMappingPl_.insert(
- {
- {
+ transMappingPl_.insert({{
utfTo<std::wstring>(singAndPlural.first),
utfTo<std::wstring>(singAndPlural.second)
},
- std::move(transPluralForms) });
+ std::move(transPluralForms)});
}
}
diff --git a/FreeFileSync/Source/log_file.cpp b/FreeFileSync/Source/log_file.cpp
index 9ab69203..90eddeb9 100644
--- a/FreeFileSync/Source/log_file.cpp
+++ b/FreeFileSync/Source/log_file.cpp
@@ -399,7 +399,7 @@ void saveNewLogFile(const AbstractPath& logFilePath, //throw FileError, X
LogFileFormat logFormat,
const ProcessSummary& summary,
const ErrorLog& log,
- const std::function<void(const std::wstring& msg)>& notifyStatus /*throw X*/)
+ const std::function<void(std::wstring&& msg)>& notifyStatus /*throw X*/)
{
//create logfile folder if required
if (const std::optional<AbstractPath> parentPath = AFS::getParentPath(logFilePath))
@@ -479,7 +479,7 @@ std::vector<LogFileInfo> getLogFiles(const AbstractPath& logFolderPath) //throw
jobNames.pop_back();
}
- logfiles.push_back({ AFS::appendRelPath(logFolderPath, fi.itemName), t, utfTo<std::wstring>(jobNames) });
+ logfiles.push_back({AFS::appendRelPath(logFolderPath, fi.itemName), t, utfTo<std::wstring>(jobNames)});
}
}
}
@@ -494,7 +494,7 @@ std::vector<LogFileInfo> getLogFiles(const AbstractPath& logFolderPath) //throw
void limitLogfileCount(const AbstractPath& logFolderPath, //throw FileError, X
int logfilesMaxAgeDays, //<= 0 := no limit
const std::set<AbstractPath>& logFilePathsToKeep,
- const std::function<void(const std::wstring& msg)>& notifyStatus /*throw X*/)
+ const std::function<void(std::wstring&& msg)>& notifyStatus /*throw X*/)
{
if (logfilesMaxAgeDays > 0)
{
@@ -613,7 +613,7 @@ void fff::saveLogFile(const AbstractPath& logFilePath, //throw FileError, X
int logfilesMaxAgeDays,
LogFileFormat logFormat,
const std::set<AbstractPath>& logFilePathsToKeep,
- const std::function<void(const std::wstring& msg)>& notifyStatus /*throw X*/)
+ const std::function<void(std::wstring&& msg)>& notifyStatus /*throw X*/)
{
std::exception_ptr firstError;
try
@@ -642,7 +642,7 @@ void fff::sendLogAsEmail(const std::string& email, //throw FileError, X
const ProcessSummary& summary,
const ErrorLog& log,
const AbstractPath& logFilePath,
- const std::function<void(const std::wstring& msg)>& notifyStatus /*throw X*/)
+ const std::function<void(std::wstring&& msg)>& notifyStatus /*throw X*/)
{
try
{
diff --git a/FreeFileSync/Source/log_file.h b/FreeFileSync/Source/log_file.h
index 031be320..50dcd4c7 100644
--- a/FreeFileSync/Source/log_file.h
+++ b/FreeFileSync/Source/log_file.h
@@ -32,13 +32,13 @@ void saveLogFile(const AbstractPath& logFilePath, //throw FileError, X
int logfilesMaxAgeDays,
LogFileFormat logFormat,
const std::set<AbstractPath>& logFilePathsToKeep,
- const std::function<void(const std::wstring& msg)>& notifyStatus /*throw X*/);
+ const std::function<void(std::wstring&& msg)>& notifyStatus /*throw X*/);
void sendLogAsEmail(const std::string& email, //throw FileError, X
const ProcessSummary& summary,
const zen::ErrorLog& log,
const AbstractPath& logFilePath,
- const std::function<void(const std::wstring& msg)>& notifyStatus /*throw X*/);
+ const std::function<void(std::wstring&& msg)>& notifyStatus /*throw X*/);
}
#endif //GENERATE_LOGFILE_H_931726432167489732164
diff --git a/FreeFileSync/Source/parse_lng.h b/FreeFileSync/Source/parse_lng.h
index 7b8e09ec..4ab4e0f7 100644
--- a/FreeFileSync/Source/parse_lng.h
+++ b/FreeFileSync/Source/parse_lng.h
@@ -211,28 +211,28 @@ private:
const TokenMap tokens_ =
{
//header information
- { Token::TK_HEADER_BEGIN, "<header>" },
- { Token::TK_HEADER_END, "</header>" },
- { Token::TK_LANG_NAME_BEGIN, "<language>" },
- { Token::TK_LANG_NAME_END, "</language>" },
- { Token::TK_TRANS_NAME_BEGIN, "<translator>" },
- { Token::TK_TRANS_NAME_END, "</translator>" },
- { Token::TK_LOCALE_NAME_BEGIN, "<locale>" },
- { Token::TK_LOCALE_NAME_END, "</locale>" },
- { Token::TK_FLAG_FILE_BEGIN, "<image>" },
- { Token::TK_FLAG_FILE_END, "</image>" },
- { Token::TK_PLURAL_COUNT_BEGIN, "<plural_count>" },
- { Token::TK_PLURAL_COUNT_END, "</plural_count>" },
- { Token::TK_PLURAL_DEF_BEGIN, "<plural_definition>" },
- { Token::TK_PLURAL_DEF_END, "</plural_definition>" },
+ {Token::TK_HEADER_BEGIN, "<header>"},
+ {Token::TK_HEADER_END, "</header>"},
+ {Token::TK_LANG_NAME_BEGIN, "<language>"},
+ {Token::TK_LANG_NAME_END, "</language>"},
+ {Token::TK_TRANS_NAME_BEGIN, "<translator>"},
+ {Token::TK_TRANS_NAME_END, "</translator>"},
+ {Token::TK_LOCALE_NAME_BEGIN, "<locale>"},
+ {Token::TK_LOCALE_NAME_END, "</locale>"},
+ {Token::TK_FLAG_FILE_BEGIN, "<image>"},
+ {Token::TK_FLAG_FILE_END, "</image>"},
+ {Token::TK_PLURAL_COUNT_BEGIN, "<plural_count>"},
+ {Token::TK_PLURAL_COUNT_END, "</plural_count>"},
+ {Token::TK_PLURAL_DEF_BEGIN, "<plural_definition>"},
+ {Token::TK_PLURAL_DEF_END, "</plural_definition>"},
//item level
- { Token::TK_SRC_BEGIN, "<source>" },
- { Token::TK_SRC_END, "</source>" },
- { Token::TK_TRG_BEGIN, "<target>" },
- { Token::TK_TRG_END, "</target>" },
- { Token::TK_PLURAL_BEGIN, "<pluralform>" },
- { Token::TK_PLURAL_END, "</pluralform>" },
+ {Token::TK_SRC_BEGIN, "<source>"},
+ {Token::TK_SRC_END, "</source>"},
+ {Token::TK_TRG_BEGIN, "<target>"},
+ {Token::TK_TRG_END, "</target>"},
+ {Token::TK_PLURAL_BEGIN, "<pluralform>"},
+ {Token::TK_PLURAL_END, "</pluralform>"},
};
};
@@ -349,7 +349,7 @@ public:
}
catch (const plural::InvalidPluralForm&)
{
- throw ParsingError({ L"Invalid plural form definition", scn_.posRow(), scn_.posCol() });
+ throw ParsingError({L"Invalid plural form definition", scn_.posRow(), scn_.posCol()});
}
}
@@ -454,12 +454,12 @@ private:
using namespace zen;
if (original.empty())
- throw ParsingError({ L"Translation source text is empty", scn_.posRow(), scn_.posCol() });
+ throw ParsingError({L"Translation source text is empty", scn_.posRow(), scn_.posCol()});
if (!isValidUtf(original))
- throw ParsingError({ L"Translation source text contains UTF-8 encoding error", scn_.posRow(), scn_.posCol() });
+ throw ParsingError({L"Translation source text contains UTF-8 encoding error", scn_.posRow(), scn_.posCol()});
if (!isValidUtf(translation))
- throw ParsingError({ L"Translation text contains UTF-8 encoding error", scn_.posRow(), scn_.posCol() });
+ throw ParsingError({L"Translation text contains UTF-8 encoding error", scn_.posRow(), scn_.posCol()});
if (!translation.empty())
{
@@ -468,7 +468,7 @@ private:
{
if (contains(original, placeholder) &&
!contains(translation, placeholder))
- throw ParsingError({ replaceCpy<std::wstring>(L"Placeholder %x missing in translation", L"%x", utfTo<std::wstring>(placeholder)), scn_.posRow(), scn_.posCol() });
+ throw ParsingError({replaceCpy<std::wstring>(L"Placeholder %x missing in translation", L"%x", utfTo<std::wstring>(placeholder)), scn_.posRow(), scn_.posCol()});
};
checkPlaceholder("%x");
checkPlaceholder("%y");
@@ -476,41 +476,41 @@ private:
//if source is a one-liner, so should be the translation
if (!contains(original, '\n') && contains(translation, '\n'))
- throw ParsingError({ L"Source text is a one-liner, but translation consists of multiple lines", scn_.posRow(), scn_.posCol() });
+ throw ParsingError({L"Source text is a one-liner, but translation consists of multiple lines", scn_.posRow(), scn_.posCol()});
//if source contains ampersand to mark menu accellerator key, so must translation
const size_t ampCount = ampersandTokenCount(original);
if (ampCount > 1 || ampCount != ampersandTokenCount(translation))
- throw ParsingError({ L"Source and translation both need exactly one & character to mark a menu item access key or none at all", scn_.posRow(), scn_.posCol() });
+ throw ParsingError({L"Source and translation both need exactly one & character to mark a menu item access key or none at all", scn_.posRow(), scn_.posCol()});
//ampersand at the end makes buggy wxWidgets crash miserably
if (endsWithSingleAmp(original) || endsWithSingleAmp(translation))
- throw ParsingError({ L"The & character to mark a menu item access key must not occur at the end of a string", scn_.posRow(), scn_.posCol() });
+ throw ParsingError({L"The & character to mark a menu item access key must not occur at the end of a string", scn_.posRow(), scn_.posCol()});
//if source ends with colon, so must translation (note: character seems to be universally used, even for asian and arabic languages)
if (endsWith(original, ':') && !endsWithColon(translation))
- throw ParsingError({ L"Source text ends with a colon character \":\", but translation does not", scn_.posRow(), scn_.posCol() });
+ throw ParsingError({L"Source text ends with a colon character \":\", but translation does not", scn_.posRow(), scn_.posCol()});
//if source ends with a period, so must translation (note: character seems to be universally used, even for asian and arabic languages)
if (endsWithSingleDot(original) && !endsWithSingleDot(translation))
- throw ParsingError({ L"Source text ends with a punctuation mark character \".\", but translation does not", scn_.posRow(), scn_.posCol() });
+ throw ParsingError({L"Source text ends with a punctuation mark character \".\", but translation does not", scn_.posRow(), scn_.posCol()});
//if source ends with an ellipsis, so must translation (note: character seems to be universally used, even for asian and arabic languages)
if (endsWithEllipsis(original) && !endsWithEllipsis(translation))
- throw ParsingError({ L"Source text ends with an ellipsis \"...\", but translation does not", scn_.posRow(), scn_.posCol() });
+ throw ParsingError({L"Source text ends with an ellipsis \"...\", but translation does not", scn_.posRow(), scn_.posCol()});
//check for not-to-be-translated texts
- for (const char* fixedStr : { "FreeFileSync", "RealTimeSync", "ffs_gui", "ffs_batch", "ffs_tmp", "GlobalSettings.xml" })
+ for (const char* fixedStr : {"FreeFileSync", "RealTimeSync", "ffs_gui", "ffs_batch", "ffs_tmp", "GlobalSettings.xml"})
if (contains(original, fixedStr) && !contains(translation, fixedStr))
- throw ParsingError({ replaceCpy<std::wstring>(L"Misspelled \"%x\" in translation", L"%x", utfTo<std::wstring>(fixedStr)), scn_.posRow(), scn_.posCol() });
+ throw ParsingError({replaceCpy<std::wstring>(L"Misspelled \"%x\" in translation", L"%x", utfTo<std::wstring>(fixedStr)), scn_.posRow(), scn_.posCol()});
//some languages (French!) put a space before punctuation mark => must be a no-brake space!
for (const char punctChar : std::string(".!?:;$#"))
if (contains(original, std::string(" ") + punctChar) ||
contains(translation, std::string(" ") + punctChar))
- throw ParsingError({ replaceCpy<std::wstring>(L"Text contains a space before the \"%x\" character. Are line-breaks really allowed here?"
- " Maybe this should be a \"non-breaking space\" (Windows: Alt 0160 UTF8: 0xC2 0xA0)?",
- L"%x", utfTo<std::wstring>(punctChar)), scn_.posRow(), scn_.posCol() });
+ throw ParsingError({replaceCpy<std::wstring>(L"Text contains a space before the \"%x\" character. Are line-breaks really allowed here?"
+ " Maybe this should be a \"non-breaking space\" (Windows: Alt 0160 UTF8: 0xC2 0xA0)?",
+ L"%x", utfTo<std::wstring>(punctChar)), scn_.posRow(), scn_.posCol()});
}
}
@@ -519,30 +519,30 @@ private:
using namespace zen;
if (original.first.empty() || original.second.empty())
- throw ParsingError({ L"Translation source text is empty", scn_.posRow(), scn_.posCol() });
+ throw ParsingError({L"Translation source text is empty", scn_.posRow(), scn_.posCol()});
const std::vector<std::string> allTexts = [&]
{
- std::vector<std::string> at{ original.first, original.second };
+ std::vector<std::string> at{original.first, original.second};
at.insert(at.end(), translation.begin(), translation.end());
return at;
}();
for (const std::string& str : allTexts)
if (!isValidUtf(str))
- throw ParsingError({ L"Text contains UTF-8 encoding error", scn_.posRow(), scn_.posCol() });
+ throw ParsingError({L"Text contains UTF-8 encoding error", scn_.posRow(), scn_.posCol()});
//check the primary placeholder is existing at least for the second english text
if (!contains(original.second, "%x"))
- throw ParsingError({ L"Plural form source text does not contain %x placeholder", scn_.posRow(), scn_.posCol() });
+ throw ParsingError({L"Plural form source text does not contain %x placeholder", scn_.posRow(), scn_.posCol()});
if (!translation.empty())
{
//check for invalid number of plural forms
if (pluralInfo.getCount() != translation.size())
- throw ParsingError({ replaceCpy(replaceCpy<std::wstring>(L"Invalid number of plural forms; actual: %x, expected: %y",
- L"%x", numberTo<std::wstring>(translation.size())),
- L"%y", numberTo<std::wstring>(pluralInfo.getCount())), scn_.posRow(), scn_.posCol() });
+ throw ParsingError({replaceCpy(replaceCpy<std::wstring>(L"Invalid number of plural forms; actual: %x, expected: %y",
+ L"%x", numberTo<std::wstring>(translation.size())),
+ L"%y", numberTo<std::wstring>(pluralInfo.getCount())), scn_.posRow(), scn_.posCol()});
//check for duplicate plural form translations (catch copy & paste errors for single-number form translations)
for (auto it = translation.begin(); it != translation.end(); ++it)
@@ -550,8 +550,8 @@ private:
{
auto it2 = std::find(it + 1, translation.end(), *it);
if (it2 != translation.end())
- throw ParsingError({ replaceCpy<std::wstring>(L"Duplicate plural form translation at index position %x",
- L"%x", numberTo<std::wstring>(it2 - translation.begin())), scn_.posRow(), scn_.posCol() });
+ throw ParsingError({replaceCpy<std::wstring>(L"Duplicate plural form translation at index position %x",
+ L"%x", numberTo<std::wstring>(it2 - translation.begin())), scn_.posRow(), scn_.posCol()});
}
for (size_t pos = 0; pos < translation.size(); ++pos)
@@ -564,15 +564,15 @@ private:
const int firstNumber = pluralInfo.getFirstNumber(pos);
if (!contains(translation[pos], "%x") &&
!contains(translation[pos], numberTo<std::string>(firstNumber)))
- throw ParsingError({ replaceCpy<std::wstring>(replaceCpy<std::wstring>(L"Plural form translation at index position %y needs to use the decimal number %z or the %x placeholder",
- L"%y", numberTo<std::wstring>(pos)), L"%z", numberTo<std::wstring>(firstNumber)), scn_.posRow(), scn_.posCol() });
+ throw ParsingError({replaceCpy<std::wstring>(replaceCpy<std::wstring>(L"Plural form translation at index position %y needs to use the decimal number %z or the %x placeholder",
+ L"%y", numberTo<std::wstring>(pos)), L"%z", numberTo<std::wstring>(firstNumber)), scn_.posRow(), scn_.posCol()});
}
}
else
{
//ensure the placeholder is used when needed
if (!contains(translation[pos], "%x"))
- throw ParsingError({ replaceCpy<std::wstring>(L"Plural form at index position %y is missing the %x placeholder", L"%y", numberTo<std::wstring>(pos)), scn_.posRow(), scn_.posCol() });
+ throw ParsingError({replaceCpy<std::wstring>(L"Plural form at index position %y is missing the %x placeholder", L"%y", numberTo<std::wstring>(pos)), scn_.posRow(), scn_.posCol()});
}
auto checkSecondaryPlaceholder = [&](const std::string& placeholder)
@@ -582,7 +582,7 @@ private:
contains(original.second, placeholder))
for (const std::string& str : allTexts)
if (!contains(str, placeholder))
- throw ParsingError({ zen::replaceCpy<std::wstring>(L"Placeholder %x missing in text", L"%x", zen::utfTo<std::wstring>(placeholder)), scn_.posRow(), scn_.posCol() });
+ throw ParsingError({zen::replaceCpy<std::wstring>(L"Placeholder %x missing in text", L"%x", zen::utfTo<std::wstring>(placeholder)), scn_.posRow(), scn_.posCol()});
};
checkSecondaryPlaceholder("%y");
checkSecondaryPlaceholder("%z");
@@ -590,51 +590,51 @@ private:
//if source is a one-liner, so should be the translation
if (!contains(original.first, '\n') && !contains(original.second, '\n') &&
/**/std::any_of(translation.begin(), translation.end(), [](const std::string& pform) { return contains(pform, '\n'); }))
- /**/throw ParsingError({ L"Source text is a one-liner, but at least one plural form translation consists of multiple lines", scn_.posRow(), scn_.posCol() });
+ /**/throw ParsingError({L"Source text is a one-liner, but at least one plural form translation consists of multiple lines", scn_.posRow(), scn_.posCol()});
//if source contains ampersand to mark menu accellerator key, so must translation
const size_t ampCount = ampersandTokenCount(original.first);
for (const std::string& str : allTexts)
if (ampCount > 1 || ampersandTokenCount(str) != ampCount)
- throw ParsingError({ L"Source and translation both need exactly one & character to mark a menu item access key or none at all", scn_.posRow(), scn_.posCol() });
+ throw ParsingError({L"Source and translation both need exactly one & character to mark a menu item access key or none at all", scn_.posRow(), scn_.posCol()});
//ampersand at the end makes buggy wxWidgets crash miserably
for (const std::string& str : allTexts)
if (endsWithSingleAmp(str))
- throw ParsingError({ L"The & character to mark a menu item access key must not occur at the end of a string", scn_.posRow(), scn_.posCol() });
+ throw ParsingError({L"The & character to mark a menu item access key must not occur at the end of a string", scn_.posRow(), scn_.posCol()});
//if source ends with colon, so must translation (note: character seems to be universally used, even for asian and arabic languages)
if (endsWith(original.first, ':') || endsWith(original.second, ':'))
for (const std::string& str : allTexts)
if (!endsWithColon(str))
- throw ParsingError({ L"Source text ends with a colon character \":\", but translation does not", scn_.posRow(), scn_.posCol() });
+ throw ParsingError({L"Source text ends with a colon character \":\", but translation does not", scn_.posRow(), scn_.posCol()});
//if source ends with a period, so must translation (note: character seems to be universally used, even for asian and arabic languages)
if (endsWithSingleDot(original.first) || endsWithSingleDot(original.second))
for (const std::string& str : allTexts)
if (!endsWithSingleDot(str))
- throw ParsingError({ L"Source text ends with a punctuation mark character \".\", but translation does not", scn_.posRow(), scn_.posCol() });
+ throw ParsingError({L"Source text ends with a punctuation mark character \".\", but translation does not", scn_.posRow(), scn_.posCol()});
//if source ends with an ellipsis, so must translation (note: character seems to be universally used, even for asian and arabic languages)
if (endsWithEllipsis(original.first) || endsWithEllipsis(original.second))
for (const std::string& str : allTexts)
if (!endsWithEllipsis(str))
- throw ParsingError({ L"Source text ends with an ellipsis \"...\", but translation does not", scn_.posRow(), scn_.posCol() });
+ throw ParsingError({L"Source text ends with an ellipsis \"...\", but translation does not", scn_.posRow(), scn_.posCol()});
//check for not-to-be-translated texts
- for (const char* fixedStr : { "FreeFileSync", "RealTimeSync", "ffs_gui", "ffs_batch", "ffs_tmp", "GlobalSettings.xml" })
+ for (const char* fixedStr : {"FreeFileSync", "RealTimeSync", "ffs_gui", "ffs_batch", "ffs_tmp", "GlobalSettings.xml"})
if (contains(original.first, fixedStr) || contains(original.second, fixedStr))
for (const std::string& str : allTexts)
if (!contains(str, fixedStr))
- throw ParsingError({ replaceCpy<std::wstring>(L"Misspelled \"%x\" in translation", L"%x", utfTo<std::wstring>(fixedStr)), scn_.posRow(), scn_.posCol() });
+ throw ParsingError({replaceCpy<std::wstring>(L"Misspelled \"%x\" in translation", L"%x", utfTo<std::wstring>(fixedStr)), scn_.posRow(), scn_.posCol()});
//some languages (French!) put a space before punctuation mark => must be a no-brake space!
for (const char punctChar : std::string(".!?:;$#"))
for (const std::string& str : allTexts)
if (contains(str, std::string(" ") + punctChar))
- throw ParsingError({ replaceCpy<std::wstring>(L"Text contains a space before the \"%x\" character. Are line-breaks really allowed here?"
- " Maybe this should be a \"non-breaking space\" (Windows: Alt 0160 UTF8: 0xC2 0xA0)?",
- L"%x", utfTo<std::wstring>(punctChar)), scn_.posRow(), scn_.posCol() });
+ throw ParsingError({replaceCpy<std::wstring>(L"Text contains a space before the \"%x\" character. Are line-breaks really allowed here?"
+ " Maybe this should be a \"non-breaking space\" (Windows: Alt 0160 UTF8: 0xC2 0xA0)?",
+ L"%x", utfTo<std::wstring>(punctChar)), scn_.posRow(), scn_.posCol()});
}
}
@@ -686,7 +686,7 @@ private:
void expectToken(Token::Type t) //throw ParsingError
{
if (token().type != t)
- throw ParsingError({ L"Unexpected token", scn_.posRow(), scn_.posCol() });
+ throw ParsingError({L"Unexpected token", scn_.posRow(), scn_.posCol()});
}
void consumeToken(Token::Type t) //throw ParsingError
diff --git a/FreeFileSync/Source/parse_plural.h b/FreeFileSync/Source/parse_plural.h
index bbf635f1..67f70b27 100644
--- a/FreeFileSync/Source/parse_plural.h
+++ b/FreeFileSync/Source/parse_plural.h
@@ -241,21 +241,21 @@ private:
using TokenList = std::vector<std::pair<std::string, Token::Type>>;
const TokenList tokens_
{
- { "?", Token::TK_TERNARY_QUEST },
- { ":", Token::TK_TERNARY_COLON },
- { "||", Token::TK_OR },
- { "&&", Token::TK_AND },
- { "==", Token::TK_EQUAL },
- { "!=", Token::TK_NOT_EQUAL },
- { "<=", Token::TK_LESS_EQUAL },
- { "<", Token::TK_LESS },
- { ">=", Token::TK_GREATER_EQUAL },
- { ">", Token::TK_GREATER },
- { "%", Token::TK_MODULUS },
- { "n", Token::TK_VARIABLE_N },
- { "N", Token::TK_VARIABLE_N },
- { "(", Token::TK_BRACKET_LEFT },
- { ")", Token::TK_BRACKET_RIGHT },
+ {"?", Token::TK_TERNARY_QUEST},
+ {":", Token::TK_TERNARY_COLON},
+ {"||", Token::TK_OR },
+ {"&&", Token::TK_AND },
+ {"==", Token::TK_EQUAL },
+ {"!=", Token::TK_NOT_EQUAL },
+ {"<=", Token::TK_LESS_EQUAL },
+ {"<", Token::TK_LESS },
+ {">=", Token::TK_GREATER_EQUAL},
+ {">", Token::TK_GREATER },
+ {"%", Token::TK_MODULUS },
+ {"n", Token::TK_VARIABLE_N },
+ {"N", Token::TK_VARIABLE_N },
+ {"(", Token::TK_BRACKET_LEFT },
+ {")", Token::TK_BRACKET_RIGHT},
};
const std::string stream_;
diff --git a/FreeFileSync/Source/perf_check.cpp b/FreeFileSync/Source/perf_check.cpp
index 9905cc7d..7a0f43ab 100644
--- a/FreeFileSync/Source/perf_check.cpp
+++ b/FreeFileSync/Source/perf_check.cpp
@@ -22,7 +22,7 @@ PerfCheck::PerfCheck(std::chrono::milliseconds windowSizeRemTime,
void PerfCheck::addSample(std::chrono::nanoseconds timeElapsed, int itemsCurrent, int64_t bytesCurrent)
{
- samples_.insert(samples_.end(), { timeElapsed, { itemsCurrent, bytesCurrent }}); //use fact that time is monotonously ascending
+ samples_.insert(samples_.end(), {timeElapsed, { itemsCurrent, bytesCurrent}}); //use fact that time is monotonously ascending
//remove all records earlier than "now - windowMax"
auto it = samples_.upper_bound(timeElapsed - windowMax_);
@@ -45,7 +45,7 @@ std::tuple<double /*timeDelta*/, int /*itemsDelta*/, int64_t /*bytesDelta*/> Per
const int itemsDelta = itBack->second.items - itFront->second.items;
const int64_t bytesDelta = itBack->second.bytes - itFront->second.bytes;
- return { timeDelta, itemsDelta, bytesDelta };
+ return {timeDelta, itemsDelta, bytesDelta};
}
diff --git a/FreeFileSync/Source/status_handler.cpp b/FreeFileSync/Source/status_handler.cpp
index b86c104d..38c488f8 100644
--- a/FreeFileSync/Source/status_handler.cpp
+++ b/FreeFileSync/Source/status_handler.cpp
@@ -5,8 +5,10 @@
// *****************************************************************************
#include "status_handler.h"
-#include <chrono>
-//#include <zen/basic_math.h>
+#include <zen/basic_math.h>
+#include <zen/process_exec.h>
+
+using namespace zen;
namespace
@@ -26,3 +28,42 @@ bool fff::uiUpdateDue(bool force)
}
return false;
}
+
+
+void fff::runCommandAndLogErrors(const Zstring& cmdLine, ErrorLog& errorLog)
+{
+ try
+ {
+ //give consoleExecute() some "time to fail", but not too long to hang our process
+ const int DEFAULT_APP_TIMEOUT_MS = 100;
+
+ if (const auto& [exitCode, output] = consoleExecute(cmdLine, DEFAULT_APP_TIMEOUT_MS); //throw SysError, SysErrorTimeOut
+ exitCode != 0)
+ throw SysError(formatSystemError("", replaceCpy(_("Exit code %x"), L"%x", numberTo<std::wstring>(exitCode)), utfTo<std::wstring>(output)));
+
+ errorLog.logMsg(_("Executing command:") + L' ' + utfTo<std::wstring>(cmdLine) + L" [" + replaceCpy(_("Exit code %x"), L"%x", L"0") + L']', MSG_TYPE_INFO);
+ }
+ catch (SysErrorTimeOut&) //child process not failed yet => probably fine :>
+ {
+ errorLog.logMsg(_("Executing command:") + L' ' + utfTo<std::wstring>(cmdLine), MSG_TYPE_INFO);
+ }
+ catch (const SysError& e)
+ {
+ errorLog.logMsg(replaceCpy(_("Command %x failed."), L"%x", fmtPath(cmdLine)) + L"\n\n" + e.toString(), MSG_TYPE_ERROR);
+ }
+}
+
+
+void fff::delayAndCountDown(std::chrono::steady_clock::time_point delayUntil, const std::function<void(const std::wstring& timeRemMsg)>& notifyStatus)
+{
+ for (auto now = std::chrono::steady_clock::now(); now < delayUntil; now = std::chrono::steady_clock::now())
+ {
+ if (notifyStatus)
+ {
+ const auto timeRemMs = std::chrono::duration_cast<std::chrono::milliseconds>(delayUntil - now).count();
+ notifyStatus(_P("1 sec", "%x sec", numeric::intDivCeil(timeRemMs, 1000)));
+ }
+
+ std::this_thread::sleep_for(UI_UPDATE_INTERVAL / 2);
+ }
+}
diff --git a/FreeFileSync/Source/status_handler.h b/FreeFileSync/Source/status_handler.h
index faac4e99..36a74a2e 100644
--- a/FreeFileSync/Source/status_handler.h
+++ b/FreeFileSync/Source/status_handler.h
@@ -7,21 +7,19 @@
#ifndef STATUS_HANDLER_H_81704805908341534
#define STATUS_HANDLER_H_81704805908341534
-#include <vector>
+#include <thread>
+#include <functional>
+#include <zen/error_log.h>
#include "base/process_callback.h"
#include "return_codes.h"
-
namespace fff
{
bool uiUpdateDue(bool force = false); //test if a specific amount of time is over
-/*
-Updating GUI is fast!
- time per single call to ProcessCallback::forceUiRefresh()
+/* Updating GUI is fast! time per call to ProcessCallback::forceUiRefresh()
- Comparison 0.025 ms
- - Synchronization 0.74 ms (despite complex graph control!)
-*/
+ - Synchronization 0.74 ms (despite complex graph control!) */
//Exception class used to abort the "compare" and "sync" process
class AbortProcess {};
@@ -88,7 +86,7 @@ public:
assert((itemsTotal < 0) == (bytesTotal < 0));
currentPhase_ = phase;
statsCurrent_ = {};
- statsTotal_ = { itemsTotal, bytesTotal };
+ statsTotal_ = {itemsTotal, bytesTotal};
}
void updateDataProcessed(int itemsDelta, int64_t bytesDelta) override { updateData(statsCurrent_, itemsDelta, bytesDelta); } //note: these methods MUST NOT throw in order
@@ -116,10 +114,10 @@ public:
virtual void forceUiUpdateNoThrow() = 0; //noexcept
- void updateStatus(const std::wstring& msg) final //throw AbortProcess
+ void updateStatus(std::wstring&& msg) final //throw AbortProcess
{
//assert(!msg.empty()); -> possible, e.g. start of parallel scan
- statusText_ = msg; //update *before* running operations that can throw
+ statusText_ = std::move(msg); //update *before* running operations that can throw
requestUiUpdate(false /*force*/); //throw AbortProcess
}
@@ -160,11 +158,15 @@ private:
ProcessPhase currentPhase_ = ProcessPhase::none;
ProgressStats statsCurrent_;
- ProgressStats statsTotal_ { -1, -1 };
+ ProgressStats statsTotal_ {-1, -1};
std::wstring statusText_;
std::optional<AbortTrigger> abortRequested_;
};
+
+
+void delayAndCountDown(std::chrono::steady_clock::time_point delayUntil, const std::function<void(const std::wstring& timeRemMsg)>& notifyStatus);
+void runCommandAndLogErrors(const Zstring& cmdLine, zen::ErrorLog& errorLog);
}
#endif //STATUS_HANDLER_H_81704805908341534
diff --git a/FreeFileSync/Source/ui/abstract_folder_picker.cpp b/FreeFileSync/Source/ui/abstract_folder_picker.cpp
index 1465d8b1..914b2358 100644
--- a/FreeFileSync/Source/ui/abstract_folder_picker.cpp
+++ b/FreeFileSync/Source/ui/abstract_folder_picker.cpp
@@ -74,7 +74,7 @@ private:
error
};
- AsyncGuiQueue guiQueue_{ 25 /*polling [ms]*/ }; //schedule and run long-running tasks asynchronously, but process results on GUI queue
+ AsyncGuiQueue guiQueue_{25 /*polling [ms]*/}; //schedule and run long-running tasks asynchronously, but process results on GUI queue
//output-only parameters:
AbstractPath& folderPathOut_;
@@ -157,9 +157,9 @@ struct FlatTraverserCallback : public AFS::TraverserCallback
private:
void onFile (const AFS::FileInfo& fi) override {}
std::shared_ptr<TraverserCallback> onFolder (const AFS::FolderInfo& fi) override { result_.folderNames.emplace(fi.itemName, fi.isFollowedSymlink); return nullptr; }
- HandleLink onSymlink(const AFS::SymlinkInfo& si) override { return LINK_FOLLOW; }
- HandleError reportDirError (const ErrorInfo& errorInfo) override { logError(errorInfo.msg); return ON_ERROR_CONTINUE; }
- HandleError reportItemError(const ErrorInfo& errorInfo, const Zstring& itemName) override { logError(errorInfo.msg); return ON_ERROR_CONTINUE; }
+ HandleLink onSymlink(const AFS::SymlinkInfo& si) override { return HandleLink::follow; }
+ HandleError reportDirError (const ErrorInfo& errorInfo) override { logError(errorInfo.msg); return HandleError::ignore; }
+ HandleError reportItemError(const ErrorInfo& errorInfo, const Zstring& itemName) override { logError(errorInfo.msg); return HandleError::ignore; }
void logError(const std::wstring& msg)
{
@@ -189,7 +189,7 @@ void AbstractFolderPickerDlg::populateNodeThen(const wxTreeItemId& itemId, const
guiQueue_.processAsync([folderPath = itemData->folderPath] //AbstractPath is thread-safe like an int!
{
auto ft = std::make_shared<FlatTraverserCallback>(); //noexcept, traverse directory one level deep
- AFS::traverseFolderRecursive(folderPath.afsDevice, {{ folderPath.afsPath, ft }}, 1 /*parallelOps*/);
+ AFS::traverseFolderRecursive(folderPath.afsDevice, {{folderPath.afsPath, ft}}, 1 /*parallelOps*/);
return ft->getResult();
},
@@ -290,8 +290,8 @@ void AbstractFolderPickerDlg::navigateToExistingPath(const wxTreeItemId& itemId,
populateNodeThen(itemId, [this, itemId, nodeRelPath, leafType]
{
- const Zstring childFolderName = nodeRelPath.front();
- const std::vector<Zstring> childFolderRelPath{ nodeRelPath.begin() + 1, nodeRelPath.end() };
+ const Zstring childFolderName = nodeRelPath.front();
+ const std::vector<Zstring> childFolderRelPath{nodeRelPath.begin() + 1, nodeRelPath.end()};
wxTreeItemId childIdMatch;
size_t insertPos = 0; //let's not use the wxTreeCtrl::OnCompareItems() abomination to implement sorting
diff --git a/FreeFileSync/Source/ui/batch_config.cpp b/FreeFileSync/Source/ui/batch_config.cpp
index 77ce2f9e..a2351cc9 100644
--- a/FreeFileSync/Source/ui/batch_config.cpp
+++ b/FreeFileSync/Source/ui/batch_config.cpp
@@ -167,7 +167,7 @@ ConfirmationButton fff::showBatchConfigDialog(wxWindow* parent,
BatchExclusiveConfig& batchExCfg,
bool& ignoreErrors)
{
- BatchDialogConfig dlgCfg = { batchExCfg, ignoreErrors };
+ BatchDialogConfig dlgCfg = {batchExCfg, ignoreErrors};
BatchDialog batchDlg(parent, dlgCfg);
diff --git a/FreeFileSync/Source/ui/batch_status_handler.cpp b/FreeFileSync/Source/ui/batch_status_handler.cpp
index a79b894e..7950f76d 100644
--- a/FreeFileSync/Source/ui/batch_status_handler.cpp
+++ b/FreeFileSync/Source/ui/batch_status_handler.cpp
@@ -5,7 +5,6 @@
// *****************************************************************************
#include "batch_status_handler.h"
-#include <zen/process_exec.h>
#include <zen/shutdown.h>
#include <zen/resolve_path.h>
#include <wx+/popup_dlg.h>
@@ -13,7 +12,7 @@
#include <wx/sound.h>
#include "../afs/concrete.h"
#include "../log_file.h"
-#include "status_handler_impl.h"
+#include "../fatal_error.h"
using namespace zen;
using namespace fff;
@@ -36,7 +35,7 @@ BatchStatusHandler::BatchStatusHandler(bool showProgress,
autoRetryDelay_(autoRetryDelay),
soundFileSyncComplete_(soundFileSyncComplete),
progressDlg_(SyncProgressDialog::create(progressDlgSize, dlgMaximize, [this] { userRequestAbort(); }, *this, nullptr /*parentWindow*/, showProgress, autoCloseDialog,
-{ jobName }, startTime, ignoreErrors, autoRetryCount, [&]
+{jobName}, startTime, ignoreErrors, autoRetryCount, [&]
{
switch (postSyncAction)
{
@@ -96,7 +95,7 @@ BatchStatusHandler::Result BatchStatusHandler::reportResults(const Zstring& post
const ProcessSummary summary
{
- startTime_, syncResult, { jobName_ },
+ startTime_, syncResult, {jobName_},
getStatsCurrent(),
getStatsTotal (),
totalTime
@@ -105,7 +104,7 @@ BatchStatusHandler::Result BatchStatusHandler::reportResults(const Zstring& post
const AbstractPath logFilePath = generateLogFilePath(logFormat, summary, altLogFolderPathPhrase);
//e.g. %AppData%\FreeFileSync\Logs\Backup FreeFileSync 2013-09-15 015052.123 [Error].log
- auto notifyStatusNoThrow = [&](const std::wstring& msg) { try { updateStatus(msg); /*throw AbortProcess*/ } catch (AbortProcess&) {} };
+ auto notifyStatusNoThrow = [&](std::wstring&& msg) { try { updateStatus(std::move(msg)); /*throw AbortProcess*/ } catch (AbortProcess&) {} };
bool autoClose = false;
FinalRequest finalRequest = FinalRequest::none;
@@ -212,7 +211,7 @@ BatchStatusHandler::Result BatchStatusHandler::reportResults(const Zstring& post
//do NOT use tryReportingError()! saving log files should not be cancellable!
saveLogFile(logFilePath, summary, errorLog_, logfilesMaxAgeDays, logFormat, logFilePathsToKeep, notifyStatusNoThrow); //throw FileError
}
- catch (const FileError& e) { errorLog_.logMsg(e.toString(), MSG_TYPE_ERROR); }
+ catch (const FileError& e) { errorLog_.logMsg(e.toString(), MSG_TYPE_ERROR); logFatalError(e.toString()); }
//----------------------------------------------------------
@@ -236,7 +235,7 @@ BatchStatusHandler::Result BatchStatusHandler::reportResults(const Zstring& post
syncResult, errorLogFinal);
progressDlg_ = nullptr;
- return { syncResult, errorLogFinal.ref().getStats(), finalRequest, logFilePath, dlgSize, dlgIsMaximized };
+ return {syncResult, errorLogFinal.ref().getStats(), finalRequest, logFilePath, dlgSize, dlgIsMaximized};
}
@@ -260,10 +259,10 @@ void BatchStatusHandler::updateDataProcessed(int itemsDelta, int64_t bytesDelta)
}
-void BatchStatusHandler::reportInfo(const std::wstring& msg)
+void BatchStatusHandler::logInfo(const std::wstring& msg)
{
errorLog_.logMsg(msg, MSG_TYPE_INFO);
- updateStatus(msg); //throw AbortProcess
+ requestUiUpdate(false /*force*/); //throw AbortProcess
}
diff --git a/FreeFileSync/Source/ui/batch_status_handler.h b/FreeFileSync/Source/ui/batch_status_handler.h
index edea7480..91c1d78d 100644
--- a/FreeFileSync/Source/ui/batch_status_handler.h
+++ b/FreeFileSync/Source/ui/batch_status_handler.h
@@ -34,7 +34,7 @@ public:
~BatchStatusHandler();
void initNewPhase (int itemsTotal, int64_t bytesTotal, ProcessPhase phaseID) override; //
- void reportInfo (const std::wstring& msg) override; //
+ void logInfo (const std::wstring& msg) override; //
void reportWarning (const std::wstring& msg, bool& warningActive) override; //throw AbortProcess
Response reportError (const ErrorInfo& errorInfo) override; //
void reportFatalError(const std::wstring& msg) override; //
diff --git a/FreeFileSync/Source/ui/cfg_grid.cpp b/FreeFileSync/Source/ui/cfg_grid.cpp
index c0d153c1..98a8d50b 100644
--- a/FreeFileSync/Source/ui/cfg_grid.cpp
+++ b/FreeFileSync/Source/ui/cfg_grid.cpp
@@ -16,6 +16,7 @@
#include <wx/settings.h>
#include "../icon_buffer.h"
#include "../ffs_paths.h"
+#include "../afs/native.h"
using namespace zen;
using namespace fff;
@@ -199,7 +200,7 @@ void ConfigView::sortListViewImpl()
if (lhs->second.isLastRunCfg != rhs->second.isLastRunCfg)
return lhs->second.isLastRunCfg < rhs->second.isLastRunCfg; //"last session" label should be (always) last
- return makeSortDirection(std::greater<>(), std::bool_constant<ascending>())(lhs->second.cfgItem.lastSyncTime, rhs->second.cfgItem.lastSyncTime);
+ return makeSortDirection(std::greater(), std::bool_constant<ascending>())(lhs->second.cfgItem.lastSyncTime, rhs->second.cfgItem.lastSyncTime);
//[!] ascending lastSync shows lowest "days past" first <=> highest lastSyncTime first
};
@@ -215,7 +216,7 @@ void ConfigView::sortListViewImpl()
//primary sort order
if (hasLogL && lhs->second.cfgItem.logResult != rhs->second.cfgItem.logResult)
- return makeSortDirection(std::greater<>(), std::bool_constant<ascending>())(lhs->second.cfgItem.logResult, rhs->second.cfgItem.logResult);
+ return makeSortDirection(std::greater(), std::bool_constant<ascending>())(lhs->second.cfgItem.logResult, rhs->second.cfgItem.logResult);
//secondary sort order
return LessNaturalSort()(lhs->second.name, rhs->second.name);
@@ -480,9 +481,7 @@ private:
break;
case ColumnTypeCfg::lastLog:
- if (!item->isLastRunCfg &&
- !AFS::isNullPath(item->cfgItem.logFilePath) &&
- AFS::getNativeItemPath(item->cfgItem.logFilePath))
+ if (!item->isLastRunCfg && !getNativeItemPath(item->cfgItem.logFilePath).empty())
return static_cast<HoverArea>(HoverAreaLog::link);
break;
}
@@ -588,8 +587,9 @@ private:
case HoverAreaLog::link:
try
{
- if (std::optional<Zstring> nativePath = AFS::getNativeItemPath(item->cfgItem.logFilePath))
- openWithDefaultApp(*nativePath); //throw FileError
+ if (const Zstring& nativePath = getNativeItemPath(item->cfgItem.logFilePath);
+ !nativePath.empty())
+ openWithDefaultApp(nativePath); //throw FileError
else
assert(false);
assert(!AFS::isNullPath(item->cfgItem.logFilePath)); //see getMouseHover()
diff --git a/FreeFileSync/Source/ui/cfg_grid.h b/FreeFileSync/Source/ui/cfg_grid.h
index 19a7b427..84364584 100644
--- a/FreeFileSync/Source/ui/cfg_grid.h
+++ b/FreeFileSync/Source/ui/cfg_grid.h
@@ -59,9 +59,9 @@ std::vector<ColAttributesCfg> getCfgGridDefaultColAttribs()
using namespace zen;
return
{
- { ColumnTypeCfg::name, -fastFromDIP(75) - fastFromDIP(42), 1, true },
- { ColumnTypeCfg::lastSync, fastFromDIP(75), 0, true },
- { ColumnTypeCfg::lastLog, fastFromDIP(42), 0, true }, //leave some room for the sort direction indicator
+ {ColumnTypeCfg::name, -fastFromDIP(75) - fastFromDIP(42), 1, true},
+ {ColumnTypeCfg::lastSync, fastFromDIP(75), 0, true},
+ {ColumnTypeCfg::lastLog, fastFromDIP(42), 0, true}, //leave some room for the sort direction indicator
};
}
@@ -126,7 +126,7 @@ public:
size_t getRowCount() const { assert(cfgList_.size() == cfgListView_.size()); return cfgListView_.size(); }
void setSortDirection(ColumnTypeCfg colType, bool ascending);
- std::pair<ColumnTypeCfg, bool> getSortDirection() { return { sortColumn_, sortAscending_ }; }
+ std::pair<ColumnTypeCfg, bool> getSortDirection() { return {sortColumn_, sortAscending_}; }
private:
ConfigView (const ConfigView&) = delete;
diff --git a/FreeFileSync/Source/ui/command_box.cpp b/FreeFileSync/Source/ui/command_box.cpp
index 4c1b177f..c0637b10 100644
--- a/FreeFileSync/Source/ui/command_box.cpp
+++ b/FreeFileSync/Source/ui/command_box.cpp
@@ -112,9 +112,9 @@ void CommandBox::setValueAndUpdateList(const wxString& value)
if (std::find(items.begin(), items.end(), value) == items.end())
{
if (!items.empty() && !value.empty())
- items.insert(items.begin(), { value, getSeparationLine() });
+ items.insert(items.begin(), {value, getSeparationLine()});
else
- items.insert(items.begin(), { value });
+ items.insert(items.begin(), {value});
}
//this->Clear(); -> NO! emits yet another wxEVT_COMMAND_TEXT_UPDATED!!!
diff --git a/FreeFileSync/Source/ui/file_grid.cpp b/FreeFileSync/Source/ui/file_grid.cpp
index b65786d1..d54a9368 100644
--- a/FreeFileSync/Source/ui/file_grid.cpp
+++ b/FreeFileSync/Source/ui/file_grid.cpp
@@ -34,17 +34,17 @@ wxDEFINE_EVENT(EVENT_GRID_SYNC_DIRECTION, SyncDirectionEvent);
namespace
{
//let's NOT create wxWidgets objects statically:
-inline wxColor getColorSyncBlue (bool faint) { if (faint) return { 0xed, 0xee, 0xff }; return { 185, 188, 255 }; }
-inline wxColor getColorSyncGreen(bool faint) { if (faint) return { 0xf1, 0xff, 0xed }; return { 196, 255, 185 }; }
+inline wxColor getColorSyncBlue (bool faint) { if (faint) return {0xed, 0xee, 0xff}; return {185, 188, 255}; }
+inline wxColor getColorSyncGreen(bool faint) { if (faint) return {0xf1, 0xff, 0xed}; return {196, 255, 185}; }
-inline wxColor getColorConflictBackground (bool faint) { if (faint) return { 0xfe, 0xfe, 0xda }; return { 247, 252, 62 }; } //yellow
-inline wxColor getColorDifferentBackground(bool faint) { if (faint) return { 0xff, 0xed, 0xee }; return { 255, 185, 187 }; } //red
+inline wxColor getColorConflictBackground (bool faint) { if (faint) return {0xfe, 0xfe, 0xda}; return {247, 252, 62}; } //yellow
+inline wxColor getColorDifferentBackground(bool faint) { if (faint) return {0xff, 0xed, 0xee}; return {255, 185, 187}; } //red
-inline wxColor getColorSymlinkBackground() { return { 238, 201, 0 }; } //orange
-//inline wxColor getColorItemMissing() { return { 212, 208, 200 }; } //medium grey
+inline wxColor getColorSymlinkBackground() { return {238, 201, 0}; } //orange
+//inline wxColor getColorItemMissing() { return {212, 208, 200}; } //medium grey
-inline wxColor getColorInactiveBack(bool faint) { if (faint) return { 0xf6, 0xf6, 0xf6}; return { 0xe4, 0xe4, 0xe4 }; } //light grey
-inline wxColor getColorInactiveText() { return { 0x40, 0x40, 0x40 }; } //dark grey
+inline wxColor getColorInactiveBack(bool faint) { if (faint) return {0xf6, 0xf6, 0xf6}; return {0xe4, 0xe4, 0xe4}; } //light grey
+inline wxColor getColorInactiveText() { return {0x40, 0x40, 0x40}; } //dark grey
inline wxColor getColorGridLine() { return wxSystemSettings::GetColour(wxSYS_COLOUR_BTNSHADOW); }
@@ -73,7 +73,7 @@ std::pair<ptrdiff_t, ptrdiff_t> getVisibleRows(const Grid& grid) //returns range
const ptrdiff_t rowFrom = grid.getRowAtPos(topLeft.y); //return -1 for invalid position, rowCount if out of range
const ptrdiff_t rowTo = grid.getRowAtPos(bottom.y);
if (rowFrom >= 0 && rowTo >= 0)
- return { rowFrom, std::min(rowTo + 1, rowCount) };
+ return {rowFrom, std::min(rowTo + 1, rowCount)};
}
return {};
}
@@ -125,36 +125,36 @@ enum class CudAction
update,
destroy,
};
-std::pair<CudAction, SelectedSide> getCudAction(SyncOperation so)
+std::pair<CudAction, SelectSide> getCudAction(SyncOperation so)
{
switch (so)
{
//*INDENT-OFF*
case SO_CREATE_NEW_LEFT:
- case SO_MOVE_LEFT_TO: return {CudAction::create, LEFT_SIDE};
+ case SO_MOVE_LEFT_TO: return {CudAction::create, SelectSide::left};
case SO_CREATE_NEW_RIGHT:
- case SO_MOVE_RIGHT_TO: return {CudAction::create, RIGHT_SIDE};
+ case SO_MOVE_RIGHT_TO: return {CudAction::create, SelectSide::right};
case SO_DELETE_LEFT:
- case SO_MOVE_LEFT_FROM: return {CudAction::destroy, LEFT_SIDE};
+ case SO_MOVE_LEFT_FROM: return {CudAction::destroy, SelectSide::left};
case SO_DELETE_RIGHT:
- case SO_MOVE_RIGHT_FROM: return {CudAction::destroy, RIGHT_SIDE};
+ case SO_MOVE_RIGHT_FROM: return {CudAction::destroy, SelectSide::right};
case SO_OVERWRITE_LEFT:
- case SO_COPY_METADATA_TO_LEFT: return {CudAction::update, LEFT_SIDE};
+ case SO_COPY_METADATA_TO_LEFT: return {CudAction::update, SelectSide::left};
case SO_OVERWRITE_RIGHT:
- case SO_COPY_METADATA_TO_RIGHT: return {CudAction::update, RIGHT_SIDE};
+ case SO_COPY_METADATA_TO_RIGHT: return {CudAction::update, SelectSide::right};
case SO_DO_NOTHING:
case SO_EQUAL:
- case SO_UNRESOLVED_CONFLICT: return {CudAction::doNothing, LEFT_SIDE};
+ case SO_UNRESOLVED_CONFLICT: return {CudAction::doNothing, SelectSide::left};
//*INDENT-ON*
}
assert(false);
- return {CudAction::doNothing, LEFT_SIDE};
+ return {CudAction::doNothing, SelectSide::left};
}
@@ -375,7 +375,7 @@ private:
//########################################################################################################
-template <SelectedSide side>
+template <SelectSide side>
class GridDataRim : public GridDataBase
{
public:
@@ -503,7 +503,7 @@ private:
case ColumnTypeRim::size:
visitFSObject(*fsObj, [&](const FolderPair& folder) { value = L'<' + _("Folder") + L'>'; },
[&](const FilePair& file) { value = formatNumber(file.getFileSize<side>()); },
- //[&](const FilePair& file) { value = utfTo<std::wstring>(formatAsHexString(file.getFileId<side>())); }, // -> test file id
+ //[&](const FilePair& file) { value = numberTo<std::wstring>(file.getFilePrint<side>()); }, // -> test file id
[&](const SymlinkPair& symlink) { value = L'<' + _("Symlink") + L'>'; });
break;
@@ -1194,7 +1194,7 @@ private:
//draw sort marker
if (auto sortInfo = getDataView().getSortConfig())
if (const ColumnTypeRim* sortType = std::get_if<ColumnTypeRim>(&sortInfo->sortCol))
- if (*sortType == static_cast<ColumnTypeRim>(colType) && sortInfo->onLeft == (side == LEFT_SIDE))
+ if (*sortType == static_cast<ColumnTypeRim>(colType) && sortInfo->onLeft == (side == SelectSide::left))
{
const wxImage sortMarker = loadImage(sortInfo->ascending ? "sort_ascending" : "sort_descending");
drawBitmapRtlNoMirror(dc, enabled ? sortMarker : sortMarker.ConvertToDisabled(), rectInner, wxALIGN_CENTER_HORIZONTAL);
@@ -1293,16 +1293,16 @@ private:
};
-class GridDataLeft : public GridDataRim<LEFT_SIDE>
+class GridDataLeft : public GridDataRim<SelectSide::left>
{
public:
- GridDataLeft(Grid& grid, const SharedRef<SharedComponents>& sharedComp) : GridDataRim<LEFT_SIDE>(grid, sharedComp) {}
+ GridDataLeft(Grid& grid, const SharedRef<SharedComponents>& sharedComp) : GridDataRim<SelectSide::left>(grid, sharedComp) {}
};
-class GridDataRight : public GridDataRim<RIGHT_SIDE>
+class GridDataRight : public GridDataRim<SelectSide::right>
{
public:
- GridDataRight(Grid& grid, const SharedRef<SharedComponents>& sharedComp) : GridDataRim<RIGHT_SIDE>(grid, sharedComp) {}
+ GridDataRight(Grid& grid, const SharedRef<SharedComponents>& sharedComp) : GridDataRim<SelectSide::right>(grid, sharedComp) {}
};
//########################################################################################################
@@ -1480,7 +1480,7 @@ private:
{
//draw notch on left side
if (notch_.GetHeight() != rectTmp.height)
- notch_.Rescale(notch_.GetWidth(), rectTmp.height);
+ notch_ = notch_.Scale(notch_.GetWidth(), rectTmp.height);
//wxWidgets screws up again and has wxALIGN_RIGHT off by one pixel! -> use wxALIGN_LEFT instead
const wxRect rectNotch(rectTmp.x + rectTmp.width - notch_.GetWidth(), rectTmp.y, notch_.GetWidth(), rectTmp.height);
@@ -2008,9 +2008,9 @@ void filegrid::init(Grid& gridLeft, Grid& gridCenter, Grid& gridRight)
gridCenter.setColumnConfig(
{
- { static_cast<ColumnType>(ColumnTypeCenter::checkbox), widthCheckbox, 0, true },
- { static_cast<ColumnType>(ColumnTypeCenter::difference), widthDifference, 0, true },
- { static_cast<ColumnType>(ColumnTypeCenter::action), widthAction, 0, true },
+ {static_cast<ColumnType>(ColumnTypeCenter::checkbox), widthCheckbox, 0, true},
+ {static_cast<ColumnType>(ColumnTypeCenter::difference), widthDifference, 0, true},
+ {static_cast<ColumnType>(ColumnTypeCenter::action), widthAction, 0, true},
});
}
diff --git a/FreeFileSync/Source/ui/file_grid_attr.h b/FreeFileSync/Source/ui/file_grid_attr.h
index 13c4dab9..012724ad 100644
--- a/FreeFileSync/Source/ui/file_grid_attr.h
+++ b/FreeFileSync/Source/ui/file_grid_attr.h
@@ -42,10 +42,10 @@ std::vector<ColAttributesRim> getFileGridDefaultColAttribsLeft()
using namespace zen;
return //harmonize with main_dlg.cpp::onGridLabelContextRim() => expects stretched path and non-stretched other columns!
{
- { ColumnTypeRim::path, -fastFromDIP(100), 1, true },
- { ColumnTypeRim::extension, fastFromDIP( 60), 0, false },
- { ColumnTypeRim::date, fastFromDIP( 140), 0, false },
- { ColumnTypeRim::size, fastFromDIP( 100), 0, true },
+ {ColumnTypeRim::path, -fastFromDIP(100), 1, true },
+ {ColumnTypeRim::extension, fastFromDIP( 60), 0, false},
+ {ColumnTypeRim::date, fastFromDIP( 140), 0, false},
+ {ColumnTypeRim::size, fastFromDIP( 100), 0, true },
};
}
diff --git a/FreeFileSync/Source/ui/file_view.cpp b/FreeFileSync/Source/ui/file_view.cpp
index 8d06b266..fbea502e 100644
--- a/FreeFileSync/Source/ui/file_view.cpp
+++ b/FreeFileSync/Source/ui/file_view.cpp
@@ -62,8 +62,8 @@ FileView::FileView(FolderComparison& folderCmp)
serializeHierarchy(baseObj, sortedRef_);
folderPairs_.emplace_back(&baseObj,
- baseObj.getAbstractPath< LEFT_SIDE>(),
- baseObj.getAbstractPath<RIGHT_SIDE>());
+ baseObj.getAbstractPath< SelectSide::left>(),
+ baseObj.getAbstractPath<SelectSide::right>());
});
}
@@ -123,7 +123,7 @@ void FileView::updateView(Predicate pred)
assert(!groupDetails_.empty());
const size_t groupIdx = groupDetails_.size() - 1;
//-----------------------------------------------------------
- viewRef_.push_back({ objId, groupIdx });
+ viewRef_.push_back({objId, groupIdx});
}
}
@@ -149,33 +149,33 @@ void addNumbers(const FileSystemObject& fsObj, ViewStats& stats)
{
visitFSObject(fsObj, [&](const FolderPair& folder)
{
- if (!folder.isEmpty<LEFT_SIDE>())
+ if (!folder.isEmpty<SelectSide::left>())
++stats.fileStatsLeft.folderCount;
- if (!folder.isEmpty<RIGHT_SIDE>())
+ if (!folder.isEmpty<SelectSide::right>())
++stats.fileStatsRight.folderCount;
},
[&](const FilePair& file)
{
- if (!file.isEmpty<LEFT_SIDE>())
+ if (!file.isEmpty<SelectSide::left>())
{
- stats.fileStatsLeft.bytes += file.getFileSize<LEFT_SIDE>();
+ stats.fileStatsLeft.bytes += file.getFileSize<SelectSide::left>();
++stats.fileStatsLeft.fileCount;
}
- if (!file.isEmpty<RIGHT_SIDE>())
+ if (!file.isEmpty<SelectSide::right>())
{
- stats.fileStatsRight.bytes += file.getFileSize<RIGHT_SIDE>();
+ stats.fileStatsRight.bytes += file.getFileSize<SelectSide::right>();
++stats.fileStatsRight.fileCount;
}
},
[&](const SymlinkPair& symlink)
{
- if (!symlink.isEmpty<LEFT_SIDE>())
+ if (!symlink.isEmpty<SelectSide::left>())
++stats.fileStatsLeft.fileCount;
- if (!symlink.isEmpty<RIGHT_SIDE>())
+ if (!symlink.isEmpty<SelectSide::right>())
++stats.fileStatsRight.fileCount;
});
}
@@ -345,7 +345,7 @@ FileView::PathDrawInfo FileView::getDrawInfo(size_t row)
if (fsObj && !folderGroupObj)
folderGroupObj = dynamic_cast<FolderPair*>(&fsObj->parent());
- return { groupFirstRow, groupLastRow, groupIdx, viewUpdateId_, folderGroupObj, fsObj };
+ return {groupFirstRow, groupLastRow, groupIdx, viewUpdateId_, folderGroupObj, fsObj};
}
assert(false); //unexpected: check rowsOnView()!
return {};
@@ -394,7 +394,7 @@ bool isDirectoryPair(const FileSystemObject& fsObj)
}
-template <bool ascending, SelectedSide side> inline
+template <bool ascending, SelectSide side> inline
bool lessFileName(const FileSystemObject& lhs, const FileSystemObject& rhs)
{
//sort order: first files/symlinks, then directories then empty rows
@@ -445,7 +445,7 @@ bool lessFilePath(const FileSystemObject::ObjectId& lhs, const FileSystemObject:
const size_t basePosR = itR->second;
if (basePosL != basePosR)
- return zen::makeSortDirection(std::less<>(), std::bool_constant<ascending>())(basePosL, basePosR);
+ return zen::makeSortDirection(std::less(), std::bool_constant<ascending>())(basePosL, basePosR);
}
//------- sort component-wise ----------
@@ -507,7 +507,7 @@ bool lessFilePath(const FileSystemObject::ObjectId& lhs, const FileSystemObject:
else
return std::is_gt(cmp);
}
- //return zen::makeSortDirection(std::less<>(), std::bool_constant<ascending>())(rv, 0);
+ //return zen::makeSortDirection(std::less(), std::bool_constant<ascending>())(rv, 0);
/*...with equivalent names:
1. functional correctness => must not compare equal! e.g. a/a/x and a/A/y
@@ -516,7 +516,7 @@ bool lessFilePath(const FileSystemObject::ObjectId& lhs, const FileSystemObject:
}
-template <bool ascending, SelectedSide side> inline
+template <bool ascending, SelectSide side> inline
bool lessFilesize(const FileSystemObject& lhs, const FileSystemObject& rhs)
{
//empty rows always last
@@ -541,11 +541,11 @@ bool lessFilesize(const FileSystemObject& lhs, const FileSystemObject& rhs)
return true;
//return list beginning with largest files first
- return zen::makeSortDirection(std::less<>(), std::bool_constant<ascending>())(fileL->getFileSize<side>(), fileR->getFileSize<side>());
+ return zen::makeSortDirection(std::less(), std::bool_constant<ascending>())(fileL->getFileSize<side>(), fileR->getFileSize<side>());
}
-template <bool ascending, SelectedSide side> inline
+template <bool ascending, SelectSide side> inline
bool lessFiletime(const FileSystemObject& lhs, const FileSystemObject& rhs)
{
if (lhs.isEmpty<side>())
@@ -568,11 +568,11 @@ bool lessFiletime(const FileSystemObject& lhs, const FileSystemObject& rhs)
const int64_t dateR = fileR ? fileR->getLastWriteTime<side>() : symlinkR->getLastWriteTime<side>();
//return list beginning with newest files first
- return zen::makeSortDirection(std::less<>(), std::bool_constant<ascending>())(dateL, dateR);
+ return zen::makeSortDirection(std::less(), std::bool_constant<ascending>())(dateL, dateR);
}
-template <bool ascending, SelectedSide side> inline
+template <bool ascending, SelectSide side> inline
bool lessExtension(const FileSystemObject& lhs, const FileSystemObject& rhs)
{
if (lhs.isEmpty<side>())
@@ -613,11 +613,11 @@ bool lessCmpResult(const FileSystemObject& lhs, const FileSystemObject& rhs)
template <bool ascending> inline
bool lessSyncDirection(const FileSystemObject& lhs, const FileSystemObject& rhs)
{
- return zen::makeSortDirection(std::less<>(), std::bool_constant<ascending>())(lhs.getSyncOperation(), rhs.getSyncOperation());
+ return zen::makeSortDirection(std::less(), std::bool_constant<ascending>())(lhs.getSyncOperation(), rhs.getSyncOperation());
}
-template <bool ascending, SelectedSide side>
+template <bool ascending, SelectSide side>
struct LessFullPath
{
LessFullPath(std::vector<std::tuple<const void* /*BaseFolderPair*/, AbstractPath, AbstractPath>> folderPairs)
@@ -673,7 +673,7 @@ private:
};
-template <bool ascending, SelectedSide side>
+template <bool ascending, SelectSide side>
struct LessFileName
{
bool operator()(const FileSystemObject::ObjectId& lhs, const FileSystemObject::ObjectId& rhs) const
@@ -690,7 +690,7 @@ struct LessFileName
};
-template <bool ascending, SelectedSide side>
+template <bool ascending, SelectSide side>
struct LessFilesize
{
bool operator()(const FileSystemObject::ObjectId& lhs, const FileSystemObject::ObjectId& rhs) const
@@ -707,7 +707,7 @@ struct LessFilesize
};
-template <bool ascending, SelectedSide side>
+template <bool ascending, SelectSide side>
struct LessFiletime
{
bool operator()(const FileSystemObject::ObjectId& lhs, const FileSystemObject::ObjectId& rhs) const
@@ -724,7 +724,7 @@ struct LessFiletime
};
-template <bool ascending, SelectedSide side>
+template <bool ascending, SelectSide side>
struct LessExtension
{
bool operator()(const FileSystemObject::ObjectId& lhs, const FileSystemObject::ObjectId& rhs) const
@@ -783,7 +783,7 @@ void FileView::sortView(ColumnTypeRim type, ItemPathFormat pathFmt, bool onLeft,
groupDetails_ .clear();
rowPositions_ .clear();
rowPositionsFirstChild_.clear();
- currentSort_ = SortInfo({ type, onLeft, ascending });
+ currentSort_ = SortInfo({type, onLeft, ascending});
switch (type)
{
@@ -791,10 +791,10 @@ void FileView::sortView(ColumnTypeRim type, ItemPathFormat pathFmt, bool onLeft,
switch (pathFmt)
{
case ItemPathFormat::name:
- if ( ascending && onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessFileName<true, LEFT_SIDE>());
- else if ( ascending && !onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessFileName<true, RIGHT_SIDE>());
- else if (!ascending && onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessFileName<false, LEFT_SIDE>());
- else if (!ascending && !onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessFileName<false, RIGHT_SIDE>());
+ if ( ascending && onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessFileName<true, SelectSide::left>());
+ else if ( ascending && !onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessFileName<true, SelectSide::right>());
+ else if (!ascending && onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessFileName<false, SelectSide::left>());
+ else if (!ascending && !onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessFileName<false, SelectSide::right>());
break;
case ItemPathFormat::relative:
@@ -803,31 +803,31 @@ void FileView::sortView(ColumnTypeRim type, ItemPathFormat pathFmt, bool onLeft,
break;
case ItemPathFormat::full:
- if ( ascending && onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessFullPath<true, LEFT_SIDE>(folderPairs_));
- else if ( ascending && !onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessFullPath<true, RIGHT_SIDE>(folderPairs_));
- else if (!ascending && onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessFullPath<false, LEFT_SIDE>(folderPairs_));
- else if (!ascending && !onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessFullPath<false, RIGHT_SIDE>(folderPairs_));
+ if ( ascending && onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessFullPath<true, SelectSide::left>(folderPairs_));
+ else if ( ascending && !onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessFullPath<true, SelectSide::right>(folderPairs_));
+ else if (!ascending && onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessFullPath<false, SelectSide::left>(folderPairs_));
+ else if (!ascending && !onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessFullPath<false, SelectSide::right>(folderPairs_));
break;
}
break;
case ColumnTypeRim::size:
- if ( ascending && onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessFilesize<true, LEFT_SIDE>());
- else if ( ascending && !onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessFilesize<true, RIGHT_SIDE>());
- else if (!ascending && onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessFilesize<false, LEFT_SIDE>());
- else if (!ascending && !onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessFilesize<false, RIGHT_SIDE>());
+ if ( ascending && onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessFilesize<true, SelectSide::left>());
+ else if ( ascending && !onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessFilesize<true, SelectSide::right>());
+ else if (!ascending && onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessFilesize<false, SelectSide::left>());
+ else if (!ascending && !onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessFilesize<false, SelectSide::right>());
break;
case ColumnTypeRim::date:
- if ( ascending && onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessFiletime<true, LEFT_SIDE>());
- else if ( ascending && !onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessFiletime<true, RIGHT_SIDE>());
- else if (!ascending && onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessFiletime<false, LEFT_SIDE>());
- else if (!ascending && !onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessFiletime<false, RIGHT_SIDE>());
+ if ( ascending && onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessFiletime<true, SelectSide::left>());
+ else if ( ascending && !onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessFiletime<true, SelectSide::right>());
+ else if (!ascending && onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessFiletime<false, SelectSide::left>());
+ else if (!ascending && !onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessFiletime<false, SelectSide::right>());
break;
case ColumnTypeRim::extension:
- if ( ascending && onLeft) std::stable_sort(sortedRef_.begin(), sortedRef_.end(), LessExtension<true, LEFT_SIDE>());
- else if ( ascending && !onLeft) std::stable_sort(sortedRef_.begin(), sortedRef_.end(), LessExtension<true, RIGHT_SIDE>());
- else if (!ascending && onLeft) std::stable_sort(sortedRef_.begin(), sortedRef_.end(), LessExtension<false, LEFT_SIDE>());
- else if (!ascending && !onLeft) std::stable_sort(sortedRef_.begin(), sortedRef_.end(), LessExtension<false, RIGHT_SIDE>());
+ if ( ascending && onLeft) std::stable_sort(sortedRef_.begin(), sortedRef_.end(), LessExtension<true, SelectSide::left>());
+ else if ( ascending && !onLeft) std::stable_sort(sortedRef_.begin(), sortedRef_.end(), LessExtension<true, SelectSide::right>());
+ else if (!ascending && onLeft) std::stable_sort(sortedRef_.begin(), sortedRef_.end(), LessExtension<false, SelectSide::left>());
+ else if (!ascending && !onLeft) std::stable_sort(sortedRef_.begin(), sortedRef_.end(), LessExtension<false, SelectSide::right>());
break;
}
}
@@ -839,7 +839,7 @@ void FileView::sortView(ColumnTypeCenter type, bool ascending)
groupDetails_ .clear();
rowPositions_ .clear();
rowPositionsFirstChild_.clear();
- currentSort_ = SortInfo({ type, false, ascending });
+ currentSort_ = SortInfo({type, false, ascending});
switch (type)
{
diff --git a/FreeFileSync/Source/ui/folder_pair.h b/FreeFileSync/Source/ui/folder_pair.h
index 5940e25e..0357ae4c 100644
--- a/FreeFileSync/Source/ui/folder_pair.h
+++ b/FreeFileSync/Source/ui/folder_pair.h
@@ -84,7 +84,7 @@ private:
zen::ContextMenu menu;
menu.addItem(_("Remove local settings"), removeLocalCompCfg, wxNullImage, static_cast<bool>(localCmpCfg_));
- menu.popup(*basicPanel_.m_bpButtonLocalCompCfg, { basicPanel_.m_bpButtonLocalCompCfg->GetSize().x, 0 });
+ menu.popup(*basicPanel_.m_bpButtonLocalCompCfg, {basicPanel_.m_bpButtonLocalCompCfg->GetSize().x, 0});
}
void onLocalSyncCfgContext(wxEvent& event)
@@ -98,7 +98,7 @@ private:
zen::ContextMenu menu;
menu.addItem(_("Remove local settings"), removeLocalSyncCfg, wxNullImage, static_cast<bool>(localSyncCfg_));
- menu.popup(*basicPanel_.m_bpButtonLocalSyncCfg, { basicPanel_.m_bpButtonLocalSyncCfg->GetSize().x, 0 });
+ menu.popup(*basicPanel_.m_bpButtonLocalSyncCfg, {basicPanel_.m_bpButtonLocalSyncCfg->GetSize().x, 0});
}
void onLocalFilterCfgContext(wxEvent& event)
@@ -128,7 +128,7 @@ private:
menu.addSeparator();
menu.addItem( _("Copy"), copyFilter, wxNullImage, !isNullFilter(localFilter_));
menu.addItem( _("Paste"), pasteFilter, wxNullImage, filterCfgOnClipboard.get() != nullptr);
- menu.popup(*basicPanel_.m_bpButtonLocalFilter, { basicPanel_.m_bpButtonLocalFilter->GetSize().x, 0 });
+ menu.popup(*basicPanel_.m_bpButtonLocalFilter, {basicPanel_.m_bpButtonLocalFilter->GetSize().x, 0});
}
diff --git a/FreeFileSync/Source/ui/folder_selector.cpp b/FreeFileSync/Source/ui/folder_selector.cpp
index 061b603f..99c0bbe4 100644
--- a/FreeFileSync/Source/ui/folder_selector.cpp
+++ b/FreeFileSync/Source/ui/folder_selector.cpp
@@ -201,6 +201,9 @@ void FolderSelector::onSelectFolder(wxCommandEvent& event)
//make sure default folder exists: don't let folder picker hang on non-existing network share!
auto folderExistsTimed = [waitEndTime = std::chrono::steady_clock::now() + FOLDER_SELECTED_EXISTENCE_CHECK_TIME_MAX](const AbstractPath& folderPath)
{
+ if (AFS::isNullPath(folderPath))
+ return false;
+
auto ft = runAsync([folderPath]
{
try
@@ -218,8 +221,9 @@ void FolderSelector::onSelectFolder(wxCommandEvent& event)
{
const AbstractPath folderPath = createItemPathNative(folderPathPhrase);
if (folderExistsTimed(folderPath))
- if (std::optional<Zstring> nativeFolderPath = AFS::getNativeItemPath(folderPath))
- defaultFolderNative = *nativeFolderPath;
+ if (const Zstring& nativePath = getNativeItemPath(folderPath);
+ !nativePath.empty())
+ defaultFolderNative = nativePath;
}
};
const Zstring& currentFolderPath = getPath();
diff --git a/FreeFileSync/Source/ui/gui_status_handler.cpp b/FreeFileSync/Source/ui/gui_status_handler.cpp
index ef82679f..861ea810 100644
--- a/FreeFileSync/Source/ui/gui_status_handler.cpp
+++ b/FreeFileSync/Source/ui/gui_status_handler.cpp
@@ -15,7 +15,7 @@
#include "main_dlg.h"
#include "../afs/concrete.h"
#include "../log_file.h"
-#include "status_handler_impl.h"
+#include "../fatal_error.h"
using namespace zen;
using namespace fff;
@@ -172,7 +172,7 @@ StatusHandlerTemporaryPanel::Result StatusHandlerTemporaryPanel::reportResults()
auto errorLogFinal = std::make_shared<const ErrorLog>(std::move(errorLog_));
errorLog_ = ErrorLog(); //see check in ~StatusHandlerTemporaryPanel()
- return { summary, errorLogFinal };
+ return {summary, errorLogFinal};
}
@@ -187,10 +187,10 @@ void StatusHandlerTemporaryPanel::initNewPhase(int itemsTotal, int64_t bytesTota
}
-void StatusHandlerTemporaryPanel::reportInfo(const std::wstring& msg)
+void StatusHandlerTemporaryPanel::logInfo(const std::wstring& msg)
{
errorLog_.logMsg(msg, MSG_TYPE_INFO);
- updateStatus(msg); //throw AbortProcess
+ requestUiUpdate(false /*force*/); //throw AbortProcess
}
@@ -405,7 +405,7 @@ StatusHandlerFloatingDialog::Result StatusHandlerFloatingDialog::reportResults(c
const AbstractPath logFilePath = generateLogFilePath(logFormat, summary, altLogFolderPathPhrase);
//e.g. %AppData%\FreeFileSync\Logs\Backup FreeFileSync 2013-09-15 015052.123 [Error].log
- auto notifyStatusNoThrow = [&](const std::wstring& msg) { try { updateStatus(msg); /*throw AbortProcess*/ } catch (AbortProcess&) {} };
+ auto notifyStatusNoThrow = [&](std::wstring&& msg) { try { updateStatus(std::move(msg)); /*throw AbortProcess*/ } catch (AbortProcess&) {} };
bool autoClose = false;
FinalRequest finalRequest = FinalRequest::none;
@@ -513,7 +513,7 @@ StatusHandlerFloatingDialog::Result StatusHandlerFloatingDialog::reportResults(c
//do NOT use tryReportingError()! saving log files should not be cancellable!
saveLogFile(logFilePath, summary, errorLog_, logfilesMaxAgeDays, logFormat, logFilePathsToKeep, notifyStatusNoThrow); //throw FileError
}
- catch (const FileError& e) { errorLog_.logMsg(e.toString(), MSG_TYPE_ERROR); }
+ catch (const FileError& e) { errorLog_.logMsg(e.toString(), MSG_TYPE_ERROR); logFatalError(e.toString()); }
//----------------------------------------------------------
@@ -532,7 +532,7 @@ StatusHandlerFloatingDialog::Result StatusHandlerFloatingDialog::reportResults(c
syncResult, errorLogFinal);
progressDlg_ = nullptr;
- return { summary, errorLogFinal, finalRequest, logFilePath, dlgSize, dlgIsMaximized, autoCloseFinal };
+ return {summary, errorLogFinal, finalRequest, logFilePath, dlgSize, dlgIsMaximized, autoCloseFinal};
}
@@ -547,10 +547,10 @@ void StatusHandlerFloatingDialog::initNewPhase(int itemsTotal, int64_t bytesTota
}
-void StatusHandlerFloatingDialog::reportInfo(const std::wstring& msg)
+void StatusHandlerFloatingDialog::logInfo(const std::wstring& msg)
{
errorLog_.logMsg(msg, MSG_TYPE_INFO);
- updateStatus(msg); //throw AbortProcess
+ requestUiUpdate(false /*force*/); //throw AbortProcess
}
diff --git a/FreeFileSync/Source/ui/gui_status_handler.h b/FreeFileSync/Source/ui/gui_status_handler.h
index 643714a2..8f74e77d 100644
--- a/FreeFileSync/Source/ui/gui_status_handler.h
+++ b/FreeFileSync/Source/ui/gui_status_handler.h
@@ -26,7 +26,7 @@ public:
~StatusHandlerTemporaryPanel();
void initNewPhase (int itemsTotal, int64_t bytesTotal, ProcessPhase phaseID) override; //
- void reportInfo (const std::wstring& msg) override; //
+ void logInfo (const std::wstring& msg) override; //
void reportWarning (const std::wstring& msg, bool& warningActive) override; //throw AbortProcess
Response reportError (const ErrorInfo& errorInfo) override; //
void reportFatalError(const std::wstring& msg) override; //
@@ -71,7 +71,7 @@ public:
~StatusHandlerFloatingDialog();
void initNewPhase (int itemsTotal, int64_t bytesTotal, ProcessPhase phaseID) override; //
- void reportInfo (const std::wstring& msg) override; //
+ void logInfo (const std::wstring& msg) override; //
void reportWarning (const std::wstring& msg, bool& warningActive) override; //throw AbortProcess
Response reportError (const ErrorInfo& errorInfo) override; //
void reportFatalError(const std::wstring& msg) override; //
diff --git a/FreeFileSync/Source/ui/log_panel.cpp b/FreeFileSync/Source/ui/log_panel.cpp
index 3bea6506..cd61b9bd 100644
--- a/FreeFileSync/Source/ui/log_panel.cpp
+++ b/FreeFileSync/Source/ui/log_panel.cpp
@@ -18,7 +18,7 @@ using namespace fff;
namespace
{
-inline wxColor getColorGridLine() { return { 192, 192, 192 }; } //light grey
+inline wxColor getColorGridLine() { return {192, 192, 192}; } //light grey
inline
@@ -320,9 +320,9 @@ LogPanel::LogPanel(wxWindow* parent) : LogPanelGenerated(parent)
m_gridMessages->setRowHeight(rowHeight);
m_gridMessages->setColumnConfig(
{
- { static_cast<ColumnType>(ColumnTypeLog::time ), colMsgTimeWidth, 0, true },
- { static_cast<ColumnType>(ColumnTypeLog::severity), colMsgSeverityWidth, 0, true },
- { static_cast<ColumnType>(ColumnTypeLog::text ), -colMsgTimeWidth - colMsgSeverityWidth, 1, true },
+ {static_cast<ColumnType>(ColumnTypeLog::time ), colMsgTimeWidth, 0, true},
+ {static_cast<ColumnType>(ColumnTypeLog::severity), colMsgSeverityWidth, 0, true},
+ {static_cast<ColumnType>(ColumnTypeLog::text ), -colMsgTimeWidth - colMsgSeverityWidth, 1, true},
});
//support for CTRL + C
diff --git a/FreeFileSync/Source/ui/main_dlg.cpp b/FreeFileSync/Source/ui/main_dlg.cpp
index be302be0..3fe046fb 100644
--- a/FreeFileSync/Source/ui/main_dlg.cpp
+++ b/FreeFileSync/Source/ui/main_dlg.cpp
@@ -49,6 +49,7 @@
#include "../ffs_paths.h"
#include "../localization.h"
#include "../version/version.h"
+#include "../afs/gdrive.h"
using namespace zen;
using namespace fff;
@@ -1350,8 +1351,8 @@ std::vector<FileSystemObject*> MainDialog::getTreeSelection() const
void MainDialog::copyToAlternateFolder(const std::vector<FileSystemObject*>& selectionLeft,
const std::vector<FileSystemObject*>& selectionRight)
{
- if (std::all_of(selectionLeft .begin(), selectionLeft .end(), [](const FileSystemObject* fsObj) { return fsObj->isEmpty< LEFT_SIDE>(); }) &&
- /**/std::all_of(selectionRight.begin(), selectionRight.end(), [](const FileSystemObject* fsObj) { return fsObj->isEmpty<RIGHT_SIDE>(); }))
+ if (std::all_of(selectionLeft .begin(), selectionLeft .end(), [](const FileSystemObject* fsObj) { return fsObj->isEmpty< SelectSide::left>(); }) &&
+ /**/std::all_of(selectionRight.begin(), selectionRight.end(), [](const FileSystemObject* fsObj) { return fsObj->isEmpty<SelectSide::right>(); }))
/**/return; //harmonize with onGridContextRim(): this function should be a no-op iff context menu option is disabled!
FocusPreserver fp;
@@ -1399,8 +1400,8 @@ void MainDialog::copyToAlternateFolder(const std::vector<FileSystemObject*>& sel
void MainDialog::deleteSelectedFiles(const std::vector<FileSystemObject*>& selectionLeft,
const std::vector<FileSystemObject*>& selectionRight, bool moveToRecycler)
{
- if (std::all_of(selectionLeft .begin(), selectionLeft .end(), [](const FileSystemObject* fsObj) { return fsObj->isEmpty< LEFT_SIDE>(); }) &&
- /**/std::all_of(selectionRight.begin(), selectionRight.end(), [](const FileSystemObject* fsObj) { return fsObj->isEmpty<RIGHT_SIDE>(); }))
+ if (std::all_of(selectionLeft .begin(), selectionLeft .end(), [](const FileSystemObject* fsObj) { return fsObj->isEmpty< SelectSide::left>(); }) &&
+ /**/std::all_of(selectionRight.begin(), selectionRight.end(), [](const FileSystemObject* fsObj) { return fsObj->isEmpty<SelectSide::right>(); }))
/**/return; //harmonize with onGridContextRim(): this function should be a no-op iff context menu option is disabled!
FocusPreserver fp;
@@ -1444,7 +1445,7 @@ void MainDialog::deleteSelectedFiles(const std::vector<FileSystemObject*>& selec
namespace
{
-template <SelectedSide side>
+template <SelectSide side>
AbstractPath getExistingParentFolder(const FileSystemObject& fsObj)
{
auto folder = dynamic_cast<const FolderPair*>(&fsObj);
@@ -1462,41 +1463,40 @@ AbstractPath getExistingParentFolder(const FileSystemObject& fsObj)
}
-template <SelectedSide side, class Function>
+template <SelectSide side, class Function>
void extractFileDescriptor(const FileSystemObject& fsObj, Function onDescriptor)
{
if (!fsObj.isEmpty<side>())
visitFSObject(fsObj, [](const FolderPair& folder) {},
[&](const FilePair& file)
{
- const FileDescriptor descr = { file.getAbstractPath<side>(), file.getAttributes<side>() };
- onDescriptor(descr);
+ onDescriptor(FileDescriptor{file.getAbstractPath<side>(), file.getAttributes<side>()});
},
[](const SymlinkPair& symlink) {});
}
-template <SelectedSide side>
+template <SelectSide side>
void collectNonNativeFiles(const std::vector<FileSystemObject*>& selectedRows, const TempFileBuffer& tempFileBuf,
std::set<FileDescriptor>& workLoad)
{
for (const FileSystemObject* fsObj : selectedRows)
extractFileDescriptor<side>(*fsObj, [&](const FileDescriptor& descr)
{
- if (!AFS::getNativeItemPath(descr.path))
- if (tempFileBuf.getTempPath(descr).empty()) //TempFileBuffer::createTempFiles() contract!
- workLoad.insert(descr);
+ if (getNativeItemPath(descr.path).empty() &&
+ tempFileBuf.getTempPath(descr).empty()) //TempFileBuffer::createTempFiles() contract!
+ workLoad.insert(descr);
});
}
-template <SelectedSide side>
+template <SelectSide side>
void invokeCommandLine(const Zstring& commandLinePhrase, //throw FileError
bool openWithDefaultAppRequested,
const std::vector<FileSystemObject*>& selection,
const TempFileBuffer& tempFileBuf)
{
- constexpr SelectedSide side2 = OtherSide<side>::value;
+ constexpr SelectSide side2 = OtherSide<side>::value;
for (const FileSystemObject* fsObj : selection) //context menu calls this function only if selection is not empty!
{
@@ -1514,13 +1514,15 @@ void invokeCommandLine(const Zstring& commandLinePhrase, //throw FileError
Zstring localPath;
Zstring localPath2;
- if (AFS::getNativeItemPath(basePath))
- localPath = itemPath; //no matter if item exists or not
+ if (const Zstring& nativePath = getNativeItemPath(fsObj->getAbstractPath<side>());
+ !nativePath.empty())
+ localPath = nativePath; //no matter if item exists or not
else //returns empty if not available (item not existing, error during copy):
extractFileDescriptor<side>(*fsObj, [&](const FileDescriptor& descr) { localPath = tempFileBuf.getTempPath(descr); });
- if (AFS::getNativeItemPath(basePath2))
- localPath2 = itemPath2;
+ if (const Zstring& nativePath = getNativeItemPath(fsObj->getAbstractPath<side2>());
+ !nativePath.empty())
+ localPath2 = nativePath;
else
extractFileDescriptor<side2>(*fsObj, [&](const FileDescriptor& descr) { localPath2 = tempFileBuf.getTempPath(descr); });
@@ -1568,113 +1570,124 @@ void MainDialog::openExternalApplication(const Zstring& commandLinePhrase, bool
const bool showInFileBrowserRequested = commandLinePhrase == defaultCfg.externalApps[0].cmdLine;
const bool openWithDefaultAppRequested = commandLinePhrase == defaultCfg.externalApps[1].cmdLine;
- auto openFolderInFileBrowser = [this](const AbstractPath& folderPath)
+ try
{
- try
+ auto openFolderInFileBrowser = [](const AbstractPath& folderPath) //throw FileError
{
- openWithDefaultApp(utfTo<Zstring>(AFS::getDisplayPath(folderPath))); //throw FileError
- }
- catch (const FileError& e) { showNotificationDialog(this, DialogInfoType::error, PopupDialogCfg().setDetailInstructions(e.toString())); }
- };
+ if (const Zstring& gdriveUrl = getGoogleDriveFolderUrl(folderPath); //throw FileError
+ !gdriveUrl.empty())
+ return openWithDefaultApp(gdriveUrl); //throw FileError
+ else
+ openWithDefaultApp(utfTo<Zstring>(AFS::getDisplayPath(folderPath))); //throw FileError
+ };
- //support fallback instead of an error in this special case
- if (showInFileBrowserRequested)
- {
- if (selectionLeft.size() + selectionRight.size() > 1) //do not open more than one Explorer instance!
+ //support fallback instead of an error in this special case
+ if (showInFileBrowserRequested)
{
- if ((leftSide && !selectionLeft .empty()) ||
- (!leftSide && selectionRight.empty()))
- return openExternalApplication(commandLinePhrase, leftSide, { selectionLeft[0] }, {});
+ if (selectionLeft.size() + selectionRight.size() > 1) //do not open more than one Explorer instance!
+ {
+ if ((leftSide && !selectionLeft .empty()) ||
+ (!leftSide && selectionRight.empty()))
+ return openExternalApplication(commandLinePhrase, leftSide, {selectionLeft[0]}, {});
+ else
+ return openExternalApplication(commandLinePhrase, leftSide, {}, {selectionRight[0]});
+ }
+
+ //either left or right selection is filled with exactly one item (or no selection at all)
+ AbstractPath itemPath = getNullPath();
+ if (!selectionLeft.empty())
+ {
+ if (selectionLeft[0]->isEmpty<SelectSide::left>())
+ return openFolderInFileBrowser(getExistingParentFolder<SelectSide::left>(*selectionLeft[0])); //throw FileError
+
+ itemPath = selectionLeft[0]->getAbstractPath<SelectSide::left>();
+ }
+ else if (!selectionRight.empty())
+ {
+ if (selectionRight[0]->isEmpty<SelectSide::right>())
+ return openFolderInFileBrowser(getExistingParentFolder<SelectSide::right>(*selectionRight[0])); //throw FileError
+
+ itemPath = selectionRight[0]->getAbstractPath<SelectSide::right>();
+ }
else
- return openExternalApplication(commandLinePhrase, leftSide, {}, { selectionRight[0] });
+ return openFolderInFileBrowser(leftSide ? //throw FileError
+ createAbstractPath(firstFolderPair_->getValues().folderPathPhraseLeft) :
+ createAbstractPath(firstFolderPair_->getValues().folderPathPhraseRight));
+
+ //itemPath != base folder in this context
+ if (const Zstring& gdriveUrl = getGoogleDriveFolderUrl(*AFS::getParentPath(itemPath)); //throw FileError
+ !gdriveUrl.empty())
+ return openWithDefaultApp(gdriveUrl); //throw FileError
}
- if (selectionLeft.empty() && selectionRight.empty())
- return openFolderInFileBrowser(leftSide ?
- createAbstractPath(firstFolderPair_->getValues().folderPathPhraseLeft) :
- createAbstractPath(firstFolderPair_->getValues().folderPathPhraseRight));
- //in this context either left or right selection is filled with exactly one item
- if (!selectionLeft.empty())
+ //regular command evaluation:
+ const size_t invokeCount = selectionLeft.size() + selectionRight.size();
+ if (invokeCount > EXT_APP_MASS_INVOKE_THRESHOLD)
+ if (globalCfg_.confirmDlgs.confirmCommandMassInvoke)
+ {
+ bool dontAskAgain = false;
+ switch (showConfirmationDialog(this, DialogInfoType::warning, PopupDialogCfg().
+ setTitle(_("Confirm")).
+ setMainInstructions(replaceCpy(_P("Do you really want to execute the command %y for one item?",
+ "Do you really want to execute the command %y for %x items?", invokeCount),
+ L"%y", fmtPath(commandLinePhrase))).
+ setCheckBox(dontAskAgain, _("&Don't show this warning again")),
+ _("&Execute")))
+ {
+ case ConfirmationButton::accept:
+ globalCfg_.confirmDlgs.confirmCommandMassInvoke = !dontAskAgain;
+ break;
+ case ConfirmationButton::cancel:
+ return;
+ }
+ }
+
+ std::set<FileDescriptor> nonNativeFiles;
+ if (contains(commandLinePhrase, Zstr("%local_path%")))
{
- if (selectionLeft[0]->isEmpty<LEFT_SIDE>())
- return openFolderInFileBrowser(getExistingParentFolder<LEFT_SIDE>(*selectionLeft[0]));
+ collectNonNativeFiles< SelectSide::left>(selectionLeft, tempFileBuf_, nonNativeFiles);
+ collectNonNativeFiles<SelectSide::right>(selectionRight, tempFileBuf_, nonNativeFiles);
}
- else
+ if (contains(commandLinePhrase, Zstr("%local_path2%")))
{
- if (selectionRight[0]->isEmpty<RIGHT_SIDE>())
- return openFolderInFileBrowser(getExistingParentFolder<RIGHT_SIDE>(*selectionRight[0]));
+ collectNonNativeFiles<SelectSide::right>(selectionLeft, tempFileBuf_, nonNativeFiles);
+ collectNonNativeFiles< SelectSide::left>(selectionRight, tempFileBuf_, nonNativeFiles);
}
- }
- //regular command evaluation:
- const size_t invokeCount = selectionLeft.size() + selectionRight.size();
- if (invokeCount > EXT_APP_MASS_INVOKE_THRESHOLD)
- if (globalCfg_.confirmDlgs.confirmCommandMassInvoke)
+ //##################### create temporary files for non-native paths ######################
+ if (!nonNativeFiles.empty())
{
- bool dontAskAgain = false;
- switch (showConfirmationDialog(this, DialogInfoType::warning, PopupDialogCfg().
- setTitle(_("Confirm")).
- setMainInstructions(replaceCpy(_P("Do you really want to execute the command %y for one item?",
- "Do you really want to execute the command %y for %x items?", invokeCount),
- L"%y", fmtPath(commandLinePhrase))).
- setCheckBox(dontAskAgain, _("&Don't show this warning again")),
- _("&Execute")))
- {
- case ConfirmationButton::accept:
- globalCfg_.confirmDlgs.confirmCommandMassInvoke = !dontAskAgain;
- break;
- case ConfirmationButton::cancel:
- return;
- }
- }
+ FocusPreserver fp;
- std::set<FileDescriptor> nonNativeFiles;
- if (contains(commandLinePhrase, Zstr("%local_path%")))
- {
- collectNonNativeFiles< LEFT_SIDE>(selectionLeft, tempFileBuf_, nonNativeFiles);
- collectNonNativeFiles<RIGHT_SIDE>(selectionRight, tempFileBuf_, nonNativeFiles);
- }
- if (contains(commandLinePhrase, Zstr("%local_path2%")))
- {
- collectNonNativeFiles<RIGHT_SIDE>(selectionLeft, tempFileBuf_, nonNativeFiles);
- collectNonNativeFiles< LEFT_SIDE>(selectionRight, tempFileBuf_, nonNativeFiles);
- }
+ disableGuiElements(true /*enableAbort*/); //StatusHandlerTemporaryPanel will internally process Window messages, so avoid unexpected callbacks!
+ auto app = wxTheApp; //fix lambda/wxWigets/VC fuck up
+ ZEN_ON_SCOPE_EXIT(app->Yield(); enableGuiElements()); //ui update before enabling buttons again: prevent strange behaviour of delayed button clicks
- //##################### create temporary files for non-native paths ######################
- if (!nonNativeFiles.empty())
- {
- FocusPreserver fp;
+ const auto& guiCfg = getConfig();
- disableGuiElements(true /*enableAbort*/); //StatusHandlerTemporaryPanel will internally process Window messages, so avoid unexpected callbacks!
- auto app = wxTheApp; //fix lambda/wxWigets/VC fuck up
- ZEN_ON_SCOPE_EXIT(app->Yield(); enableGuiElements()); //ui update before enabling buttons again: prevent strange behaviour of delayed button clicks
+ StatusHandlerTemporaryPanel statusHandler(*this, std::chrono::system_clock::now() /*startTime*/,
+ false /*ignoreErrors*/,
+ guiCfg.mainCfg.autoRetryCount,
+ guiCfg.mainCfg.autoRetryDelay); //handle status display and error messages
+ try
+ {
+ tempFileBuf_.createTempFiles(nonNativeFiles, statusHandler); //throw AbortProcess
+ //"clearSelection" not needed/desired
+ }
+ catch (AbortProcess&) {}
- const auto& guiCfg = getConfig();
+ const StatusHandlerTemporaryPanel::Result r = statusHandler.reportResults(); //noexcept
+ setLastOperationLog(r.summary, r.errorLog);
- StatusHandlerTemporaryPanel statusHandler(*this, std::chrono::system_clock::now() /*startTime*/,
- false /*ignoreErrors*/,
- guiCfg.mainCfg.autoRetryCount,
- guiCfg.mainCfg.autoRetryDelay); //handle status display and error messages
- try
- {
- tempFileBuf_.createTempFiles(nonNativeFiles, statusHandler); //throw AbortProcess
- //"clearSelection" not needed/desired
- }
- catch (AbortProcess&) {}
-
- const StatusHandlerTemporaryPanel::Result r = statusHandler.reportResults(); //noexcept
- setLastOperationLog(r.summary, r.errorLog);
+ if (r.summary.syncResult == SyncResult::aborted)
+ return;
- if (r.summary.syncResult == SyncResult::aborted)
- return;
+ //updateGui(); -> not needed
+ }
+ //########################################################################################
- //updateGui(); -> not needed
- }
- //########################################################################################
- try
- {
- invokeCommandLine< LEFT_SIDE>(commandLinePhrase, openWithDefaultAppRequested, selectionLeft, tempFileBuf_); //throw FileError
- invokeCommandLine<RIGHT_SIDE>(commandLinePhrase, openWithDefaultAppRequested, selectionRight, tempFileBuf_); //
+ invokeCommandLine< SelectSide::left>(commandLinePhrase, openWithDefaultAppRequested, selectionLeft, tempFileBuf_); //throw FileError
+ invokeCommandLine<SelectSide::right>(commandLinePhrase, openWithDefaultAppRequested, selectionRight, tempFileBuf_); //
}
catch (const FileError& e) { showNotificationDialog(this, DialogInfoType::error, PopupDialogCfg().setDetailInstructions(e.toString())); }
}
@@ -1898,7 +1911,7 @@ void MainDialog::onTreeKeyEvent(wxKeyEvent& event)
{
case 'C':
case WXK_INSERT: //CTRL + C || CTRL + INS
- copySelectionToClipboard({ m_gridOverview });
+ copySelectionToClipboard({m_gridOverview});
return;
}
@@ -2142,16 +2155,13 @@ void MainDialog::onLocalKeyEvent(wxKeyEvent& event) //process key events without
break;
case WXK_ESCAPE: //let's do something useful and hide the log panel
- {
- const wxWindow* focus = wxWindow::FindFocus();
- if (!isComponentOf(focus, m_panelSearch) && //search panel also handles ESC!
+ if (!isComponentOf(wxWindow::FindFocus(), m_panelSearch) && //search panel also handles ESC!
m_panelLog->IsEnabled())
{
if (auiMgr_.GetPane(m_panelLog).IsShown()) //else: let it "ding"
return showLogPanel(false /*show*/);
}
- }
- break;
+ break;
}
event.Skip();
@@ -2308,7 +2318,7 @@ void MainDialog::onTreeGridContext(GridContextMenuEvent& event)
menu.addSeparator();
menu.addItem(_("&Synchronize selection") + L"\tEnter", [&] { startSyncForSelecction(selection); }, loadImage("start_sync_selection_sicon"), selectionContainsItemsToSync);
//----------------------------------------------------------------------------------------------------
- const bool haveNonEmptyItems = std::any_of(selection.begin(), selection.end(), [](const FileSystemObject* fsObj) { return !fsObj->isEmpty<LEFT_SIDE>() || !fsObj->isEmpty<RIGHT_SIDE>(); });
+ const bool haveNonEmptyItems = std::any_of(selection.begin(), selection.end(), [](const FileSystemObject* fsObj) { return !fsObj->isEmpty<SelectSide::left>() || !fsObj->isEmpty<SelectSide::right>(); });
//menu.addSeparator();
//menu.addItem(_("&Copy to...") + L"\tCtrl+T", [&] { copyToAlternateFolder(selection, selection); }, wxNullImage, haveNonEmptyItems);
//----------------------------------------------------------------------------------------------------
@@ -2485,8 +2495,8 @@ void MainDialog::onGridContextRim(const std::vector<FileSystemObject*>& selectio
}
}
//----------------------------------------------------------------------------------------------------
- const bool haveNonEmptyItemsL = std::any_of(selectionLeft .begin(), selectionLeft .end(), [](const FileSystemObject* fsObj) { return !fsObj->isEmpty<LEFT_SIDE >(); });
- const bool haveNonEmptyItemsR = std::any_of(selectionRight.begin(), selectionRight.end(), [](const FileSystemObject* fsObj) { return !fsObj->isEmpty<RIGHT_SIDE>(); });
+ const bool haveNonEmptyItemsL = std::any_of(selectionLeft .begin(), selectionLeft .end(), [](const FileSystemObject* fsObj) { return !fsObj->isEmpty< SelectSide::left>(); });
+ const bool haveNonEmptyItemsR = std::any_of(selectionRight.begin(), selectionRight.end(), [](const FileSystemObject* fsObj) { return !fsObj->isEmpty<SelectSide::right>(); });
menu.addSeparator();
menu.addItem(_("&Copy to...") + L"\tCtrl+T", [&] { copyToAlternateFolder(selectionLeft, selectionRight); }, wxNullImage, haveNonEmptyItemsL || haveNonEmptyItemsR);
@@ -2594,7 +2604,7 @@ void MainDialog::onGridLabelContextC(GridLabelClickEvent& event)
const GridViewType viewType = m_bpButtonViewType->isActive() ? GridViewType::action : GridViewType::difference;
menu.addItem(_("Difference") + (viewType != GridViewType::difference ? L"\tF11" : L""), [&] { setGridViewType(GridViewType::difference); }, greyScaleIfDisabled(loadImage("compare_sicon" ), viewType == GridViewType::difference));
menu.addItem(_("Action") + (viewType != GridViewType::action ? L"\tF11" : L""), [&] { setGridViewType(GridViewType::action ); }, greyScaleIfDisabled(loadImage("start_sync_sicon"), viewType == GridViewType::action));
- menu.popup(*m_gridMainC, { m_gridMainC->GetSize().x, 0 });
+ menu.popup(*m_gridMainC, {m_gridMainC->GetSize().x, 0});
}
@@ -2703,7 +2713,7 @@ void MainDialog::onGridLabelContextRim(GridLabelClickEvent& event, bool leftSide
menu.addItem(_("Select time span..."), selectTimeSpan);
}
//--------------------------------------------------------------------------------------------------------
- menu.popup(grid, { event.mousePos_.x, grid.getColumnLabelHeight() });
+ menu.popup(grid, {event.mousePos_.x, grid.getColumnLabelHeight()});
//event.Skip();
}
@@ -2800,7 +2810,7 @@ void MainDialog::onCompSettingsContext(wxEvent& event)
addVariantItem(CompareVariant::content, "cmp_content");
addVariantItem(CompareVariant::size, "cmp_size");
- menu.popup(*m_bpButtonCmpContext, { m_bpButtonCmpContext->GetSize().x, 0 });
+ menu.popup(*m_bpButtonCmpContext, {m_bpButtonCmpContext->GetSize().x, 0});
}
@@ -2827,7 +2837,7 @@ void MainDialog::onSyncSettingsContext(wxEvent& event)
addVariantItem(SyncVariant::update, "sync_update");
addVariantItem(SyncVariant::custom, "sync_custom");
- menu.popup(*m_bpButtonSyncContext, { m_bpButtonSyncContext->GetSize().x, 0 });
+ menu.popup(*m_bpButtonSyncContext, {m_bpButtonSyncContext->GetSize().x, 0});
}
@@ -3004,7 +3014,7 @@ bool MainDialog::trySaveConfig(const Zstring* guiCfgPath) //return true if saved
try
{
writeConfig(guiCfg, cfgFilePath); //throw FileError
- setLastUsedConfig(guiCfg, { cfgFilePath });
+ setLastUsedConfig(guiCfg, {cfgFilePath});
flashStatusInformation(_("Configuration saved"));
return true;
@@ -3086,7 +3096,7 @@ bool MainDialog::trySaveBatchConfig(const Zstring* batchCfgPath)
try
{
writeConfig(batchCfg, cfgFilePath); //throw FileError
- setLastUsedConfig(guiCfg, { cfgFilePath }); //[!] behave as if we had saved guiCfg
+ setLastUsedConfig(guiCfg, {cfgFilePath}); //[!] behave as if we had saved guiCfg
flashStatusInformation(_("Configuration saved"));
return true;
@@ -3351,7 +3361,7 @@ void MainDialog::renameSelectedCfgHistoryItem()
//FIRST: 1. consolidate unsaved changes using the *old* config file name, if any!
//2. get rid of multiple-selection if exists 3. load cfg to allow non-failing(!) setLastUsedConfig() below
- if (!loadConfiguration({ cfgPathOld }))
+ if (!loadConfiguration({cfgPathOld}))
return; //error/cancel
const Zstring fileName = afterLast(cfgPathOld, FILE_NAME_SEPARATOR, IfNotFoundReturn::all);
@@ -3394,11 +3404,11 @@ void MainDialog::renameSelectedCfgHistoryItem()
continue;
}
- cfggrid::getDataView(*m_gridCfgHistory).removeItems({ cfgPathOld });
+ cfggrid::getDataView(*m_gridCfgHistory).removeItems({cfgPathOld});
m_gridCfgHistory->Refresh(); //grid size changed => clears selection!
- //keep current cfg and just swap the file name: see previous "loadConfiguration({ cfgPathOld }"!
- setLastUsedConfig(lastSavedCfg_, { cfgPathNew });
+ //keep current cfg and just swap the file name: see previous "loadConfiguration({cfgPathOld}"!
+ setLastUsedConfig(lastSavedCfg_, {cfgPathNew});
return;
}
}
@@ -3478,13 +3488,13 @@ void MainDialog::onCfgGridContext(GridContextMenuEvent& event)
submenu.addItem(name, applyBackColor, bmpSquare.ConvertToImage(), !selectedRows.empty());
};
addColorOption(wxNullColour, L'(' + _("&Default") + L')'); //meta options should be enclosed in parentheses
- addColorOption({ 0xff, 0xd8, 0xcb }, _("Red"));
- addColorOption({ 0xff, 0xf9, 0x99 }, _("Yellow"));
- addColorOption({ 0xcc, 0xff, 0x99 }, _("Green"));
- addColorOption({ 0xcc, 0xff, 0xff }, _("Cyan"));
- addColorOption({ 0xcc, 0xcc, 0xff }, _("Blue"));
- addColorOption({ 0xf2, 0xcb, 0xff }, _("Purple"));
- addColorOption({ 0xdd, 0xdd, 0xdd }, _("Grey"));
+ addColorOption({0xff, 0xd8, 0xcb}, _("Red"));
+ addColorOption({0xff, 0xf9, 0x99}, _("Yellow"));
+ addColorOption({0xcc, 0xff, 0x99}, _("Green"));
+ addColorOption({0xcc, 0xff, 0xff}, _("Cyan"));
+ addColorOption({0xcc, 0xcc, 0xff}, _("Blue"));
+ addColorOption({0xf2, 0xcb, 0xff}, _("Purple"));
+ addColorOption({0xdd, 0xdd, 0xdd}, _("Grey"));
menu.addSubmenu(_("Background color"), submenu, loadImage("color_sicon"), !selectedRows.empty());
menu.addSeparator();
@@ -3586,7 +3596,7 @@ void MainDialog::onCfgGridLabelContext(GridLabelClickEvent& event)
menu.addItem(_("Highlight..."), setCfgHighlight);
//--------------------------------------------------------------------------------------------------------
- menu.popup(*m_gridCfgHistory, { event.mousePos_.x, m_gridCfgHistory->getColumnLabelHeight() });
+ menu.popup(*m_gridCfgHistory, {event.mousePos_.x, m_gridCfgHistory->getColumnLabelHeight()});
//event.Skip();
}
@@ -3867,7 +3877,7 @@ void MainDialog::onGlobalFilterContext(wxEvent& event)
menu.addItem( _("Copy"), copyFilter, wxNullImage, !isNullFilter(currentCfg_.mainCfg.globalFilter));
menu.addItem( _("Paste"), pasteFilter, wxNullImage, filterCfgOnClipboard_.get() != nullptr);
- menu.popup(*m_bpButtonFilterContext, { m_bpButtonFilterContext->GetSize().x, 0 });
+ menu.popup(*m_bpButtonFilterContext, {m_bpButtonFilterContext->GetSize().x, 0});
}
@@ -3922,7 +3932,7 @@ void MainDialog::onViewTypeContextMouse(wxMouseEvent& event)
menu.addItem(_("Difference") + (viewType != GridViewType::difference ? L"\tF11" : L""), [&] { setGridViewType(GridViewType::difference); }, greyScaleIfDisabled(loadImage("compare_sicon" ), viewType == GridViewType::difference));
menu.addItem(_("Action") + (viewType != GridViewType::action ? L"\tF11" : L""), [&] { setGridViewType(GridViewType::action ); }, greyScaleIfDisabled(loadImage("start_sync_sicon"), viewType == GridViewType::action));
- menu.popup(*m_bpButtonViewType, { m_bpButtonViewType->GetSize().x, 0 });
+ menu.popup(*m_bpButtonViewType, {m_bpButtonViewType->GetSize().x, 0});
}
@@ -3961,7 +3971,7 @@ void MainDialog::onViewFilterContext(wxEvent& event)
};
menu.addItem( _("Save as default"), saveDefault, loadImage("cfg_save_sicon"));
- menu.popup(*m_bpButtonViewFilterContext, { m_bpButtonViewFilterContext->GetSize().x, 0 });
+ menu.popup(*m_bpButtonViewFilterContext, {m_bpButtonViewFilterContext->GetSize().x, 0});
}
@@ -4166,12 +4176,12 @@ void MainDialog::updateStatistics()
const SyncStatistics st(folderCmp_);
setValue(*m_staticTextData, st.getBytesToProcess() == 0, formatFilesizeShort(st.getBytesToProcess()), *m_bitmapData, "data");
- setIntValue(*m_staticTextCreateLeft, st.createCount< LEFT_SIDE>(), *m_bitmapCreateLeft, "so_create_left_sicon");
- setIntValue(*m_staticTextUpdateLeft, st.updateCount< LEFT_SIDE>(), *m_bitmapUpdateLeft, "so_update_left_sicon");
- setIntValue(*m_staticTextDeleteLeft, st.deleteCount< LEFT_SIDE>(), *m_bitmapDeleteLeft, "so_delete_left_sicon");
- setIntValue(*m_staticTextCreateRight, st.createCount<RIGHT_SIDE>(), *m_bitmapCreateRight, "so_create_right_sicon");
- setIntValue(*m_staticTextUpdateRight, st.updateCount<RIGHT_SIDE>(), *m_bitmapUpdateRight, "so_update_right_sicon");
- setIntValue(*m_staticTextDeleteRight, st.deleteCount<RIGHT_SIDE>(), *m_bitmapDeleteRight, "so_delete_right_sicon");
+ setIntValue(*m_staticTextCreateLeft, st.createCount< SelectSide::left>(), *m_bitmapCreateLeft, "so_create_left_sicon");
+ setIntValue(*m_staticTextUpdateLeft, st.updateCount< SelectSide::left>(), *m_bitmapUpdateLeft, "so_update_left_sicon");
+ setIntValue(*m_staticTextDeleteLeft, st.deleteCount< SelectSide::left>(), *m_bitmapDeleteLeft, "so_delete_left_sicon");
+ setIntValue(*m_staticTextCreateRight, st.createCount<SelectSide::right>(), *m_bitmapCreateRight, "so_create_right_sicon");
+ setIntValue(*m_staticTextUpdateRight, st.updateCount<SelectSide::right>(), *m_bitmapUpdateRight, "so_update_right_sicon");
+ setIntValue(*m_staticTextDeleteRight, st.deleteCount<SelectSide::right>(), *m_bitmapDeleteRight, "so_delete_right_sicon");
m_panelStatistics->Layout();
m_panelStatistics->Refresh(); //fix small mess up on RTL layout
@@ -4270,13 +4280,15 @@ void MainDialog::onStartSync(wxCommandEvent& event)
std::set<Zstring> folderPathsToLock;
for (auto it = begin(folderCmp_); it != end(folderCmp_); ++it)
{
- if (it->isAvailable<LEFT_SIDE>()) //do NOT check directory existence again!
- if (std::optional<Zstring> nativeFolderPath = AFS::getNativeItemPath(it->getAbstractPath<LEFT_SIDE>())) //restrict directory locking to native paths until further
- folderPathsToLock.insert(*nativeFolderPath);
-
- if (it->isAvailable<RIGHT_SIDE>())
- if (std::optional<Zstring> nativeFolderPath = AFS::getNativeItemPath(it->getAbstractPath<RIGHT_SIDE>()))
- folderPathsToLock.insert(*nativeFolderPath);
+ if (it->isAvailable<SelectSide::left>()) //do NOT check directory existence again!
+ if (const Zstring& nativePath = getNativeItemPath(it->getAbstractPath<SelectSide::left>()); //restrict directory locking to native paths until further
+ !nativePath.empty())
+ folderPathsToLock.insert(nativePath);
+
+ if (it->isAvailable<SelectSide::right>())
+ if (const Zstring& nativePath = getNativeItemPath(it->getAbstractPath<SelectSide::right>());
+ !nativePath.empty())
+ folderPathsToLock.insert(nativePath);
}
dirLocks = std::make_unique<LockHolder>(folderPathsToLock, globalCfg_.warnDlgs.warnDirectoryLockFailed, statusHandler); //throw AbortProcess
}
@@ -4497,7 +4509,7 @@ void MainDialog::startSyncForSelecction(const std::vector<FileSystemObject*>& se
void MainDialog::updateConfigLastRunStats(time_t lastRunTime, SyncResult result, const AbstractPath& logFilePath)
{
- cfggrid::getDataView(*m_gridCfgHistory).setLastRunStats(activeConfigFiles_, { lastRunTime, result, logFilePath });
+ cfggrid::getDataView(*m_gridCfgHistory).setLastRunStats(activeConfigFiles_, {lastRunTime, result, logFilePath});
//re-apply selection: sort order changed if sorted by last sync time
cfggrid::addAndSelect(*m_gridCfgHistory, activeConfigFiles_, false /*scrollToSelection*/);
@@ -4576,21 +4588,14 @@ void MainDialog::setLastOperationLog(const ProcessSummary& summary, const std::s
void MainDialog::onToggleLog(wxCommandEvent& event)
{
- const bool show = !auiMgr_.GetPane(m_panelLog).IsShown();
- showLogPanel(show);
- if (show)
- logPanel_->SetFocus();
+ showLogPanel(!auiMgr_.GetPane(m_panelLog).IsShown());
}
void MainDialog::showLogPanel(bool show)
{
- warn_static("add 'FocusPreserver fp' when showing andclosing log!? similar implementation like focusIdAfterSearch_")
-
-
-
- wxAuiPaneInfo& logPane = auiMgr_.GetPane(m_panelLog);
- if (show != logPane.IsShown())
+ if (wxAuiPaneInfo& logPane = auiMgr_.GetPane(m_panelLog);
+ logPane.IsShown() != show)
{
if (!show)
{
@@ -4602,7 +4607,23 @@ void MainDialog::showLogPanel(bool show)
logPane.Show(show);
auiMgr_.Update();
- m_panelLog->Refresh(); //macOS: fix background corruption for the statistics boxes (call *after* wxAuiManager::Update()
+ m_panelLog->Refresh(); //macOS: fix background corruption for the statistics boxes; call *after* wxAuiManager::Update()
+ }
+
+ if (show)
+ {
+ if (wxWindow* focus = wxWindow::FindFocus()) //restore when closing panel!
+ if (!isComponentOf(focus, m_panelLog))
+ focusAfterCloseLog_ = focus->GetId();
+
+ logPanel_->SetFocus();
+ }
+ else
+ {
+ if (isComponentOf(wxWindow::FindFocus(), m_panelLog))
+ if (wxWindow* oldFocusWin = wxWindow::FindWindowById(focusAfterCloseLog_))
+ oldFocusWin->SetFocus();
+ focusAfterCloseLog_ = wxID_ANY;
}
}
@@ -4614,7 +4635,7 @@ void MainDialog::onGridDoubleClickRim(GridClickEvent& event, bool leftSide)
std::vector<FileSystemObject*> selectionLeft;
std::vector<FileSystemObject*> selectionRight;
if (FileSystemObject* fsObj = filegrid::getDataView(*m_gridMainC).getFsObject(event.row_)) //selection must be a list of BOUND pointers!
- (leftSide ? selectionLeft : selectionRight) = { fsObj };
+ (leftSide ? selectionLeft : selectionRight) = {fsObj};
openExternalApplication(globalCfg_.externalApps[0].cmdLine, leftSide, selectionLeft, selectionRight);
}
@@ -5007,7 +5028,7 @@ void MainDialog::onSearchGridEnter(wxCommandEvent& event)
void MainDialog::onHideSearchPanel(wxCommandEvent& event)
{
- hideFindPanel();
+ showFindPanel(false /*show*/);
}
@@ -5020,43 +5041,39 @@ void MainDialog::onSearchPanelKeyPressed(wxKeyEvent& event)
startFindNext(true /*searchAscending*/);
return;
case WXK_ESCAPE:
- hideFindPanel();
+ showFindPanel(false /*show*/);
return;
}
event.Skip();
}
-void MainDialog::showFindPanel() //CTRL + F or F3 with empty search phrase
+void MainDialog::showFindPanel(bool show) //CTRL + F or F3 with empty search phrase
{
- if (!auiMgr_.GetPane(m_panelSearch).IsShown())
+ if (auiMgr_.GetPane(m_panelSearch).IsShown() != show)
{
- auiMgr_.GetPane(m_panelSearch).Show();
+ auiMgr_.GetPane(m_panelSearch).Show(show);
auiMgr_.Update();
}
- m_textCtrlSearchTxt->SelectAll();
-
- if (wxWindow* focus = wxWindow::FindFocus()) //restore when closing panel!
- if (!isComponentOf(focus, m_panelSearch))
- focusIdAfterSearch_ = focus->GetId();
- //don't save wxWindow* to arbitrary window: it might not exist anymore when hideFindPanel() uses it!!! (e.g. some folder pair panel)
-
- m_textCtrlSearchTxt->SetFocus();
-}
+ if (show)
+ {
+ m_textCtrlSearchTxt->SelectAll();
+ if (wxWindow* focus = wxWindow::FindFocus()) //restore when closing panel!
+ if (!isComponentOf(focus, m_panelSearch))
+ focusAfterCloseSearch_ = focus->GetId();
-void MainDialog::hideFindPanel()
-{
- if (auiMgr_.GetPane(m_panelSearch).IsShown())
- {
- auiMgr_.GetPane(m_panelSearch).Hide();
- auiMgr_.Update();
+ m_textCtrlSearchTxt->SetFocus();
}
+ else
+ {
+ if (isComponentOf(wxWindow::FindFocus(), m_panelSearch))
+ if (wxWindow* oldFocusWin = wxWindow::FindWindowById(focusAfterCloseSearch_))
+ oldFocusWin->SetFocus();
- if (wxWindow* oldFocusWin = wxWindow::FindWindowById(focusIdAfterSearch_))
- oldFocusWin->SetFocus();
- focusIdAfterSearch_ = wxID_ANY;
+ focusAfterCloseSearch_ = wxID_ANY;
+ }
}
@@ -5065,14 +5082,14 @@ void MainDialog::startFindNext(bool searchAscending) //F3 or ENTER in m_textCtrl
const std::wstring& searchString = utfTo<std::wstring>(trimCpy(m_textCtrlSearchTxt->GetValue()));
if (searchString.empty())
- showFindPanel();
+ showFindPanel(true /*show*/);
else
{
Grid* grid1 = m_gridMainL;
Grid* grid2 = m_gridMainR;
wxWindow* focus = wxWindow::FindFocus();
- if ((isComponentOf(focus, m_panelSearch) ? focusIdAfterSearch_ : focus->GetId()) == m_gridMainR->getMainWin().GetId())
+ if ((isComponentOf(focus, m_panelSearch) ? focusAfterCloseSearch_ : focus->GetId()) == m_gridMainR->getMainWin().GetId())
std::swap(grid1, grid2); //select side to start search at grid cursor position
wxBeginBusyCursor(wxHOURGLASS_CURSOR);
@@ -5080,21 +5097,21 @@ void MainDialog::startFindNext(bool searchAscending) //F3 or ENTER in m_textCtrl
m_checkBoxMatchCase->GetValue(), searchAscending); //parameter owned by GUI, *not* globalCfg structure! => we should better implement a getGlocalCfg()!
wxEndBusyCursor();
- if (Grid* grid = const_cast<Grid*>(result.first)) //grid wasn't const when passing to findAndSelectNext(), so this is safe
+ if (Grid* grid = const_cast<Grid*>(result.first)) //grid wasn't const when passing to findAndSelectNext(), so this is legal
{
assert(result.second >= 0);
filegrid::setScrollMaster(*grid);
grid->setGridCursor(result.second, GridEventPolicy::allow);
- focusIdAfterSearch_ = grid->getMainWin().GetId();
+ focusAfterCloseSearch_ = grid->getMainWin().GetId();
if (!isComponentOf(wxWindow::FindFocus(), m_panelSearch))
grid->getMainWin().SetFocus();
}
else
{
- showFindPanel();
+ showFindPanel(true /*show*/);
showNotificationDialog(this, DialogInfoType::info, PopupDialogCfg().
setTitle(_("Find")).
setMainInstructions(replaceCpy(_("Cannot find %x"), L"%x", fmtPath(searchString))));
@@ -5105,7 +5122,7 @@ void MainDialog::startFindNext(bool searchAscending) //F3 or ENTER in m_textCtrl
void MainDialog::onTopFolderPairAdd(wxCommandEvent& event)
{
- insertAddFolderPair({ LocalPairConfig() }, 0);
+ insertAddFolderPair({LocalPairConfig()}, 0);
moveAddFolderPairUp(0);
}
@@ -5178,12 +5195,12 @@ void MainDialog::onShowFolderPairOptions(wxEvent& event)
const ptrdiff_t pos = it - additionalFolderPairs_.begin();
ContextMenu menu;
- menu.addItem(_("Add folder pair"), [this, pos] { insertAddFolderPair({ LocalPairConfig() }, pos); }, loadImage("item_add_sicon"));
+ menu.addItem(_("Add folder pair"), [this, pos] { insertAddFolderPair({LocalPairConfig()}, pos); }, loadImage("item_add_sicon"));
menu.addSeparator();
menu.addItem(_("Move up" ) + L"\tAlt+Page Up", [this, pos] { moveAddFolderPairUp(pos); }, loadImage("move_up_sicon"));
menu.addItem(_("Move down") + L"\tAlt+Page Down", [this, pos] { moveAddFolderPairUp(pos + 1); }, loadImage("move_down_sicon"), pos + 1 < makeSigned(additionalFolderPairs_.size()));
- menu.popup(*(*it)->m_bpButtonFolderPairOptions, { (*it)->m_bpButtonFolderPairOptions->GetSize().x, 0 });
+ menu.popup(*(*it)->m_bpButtonFolderPairOptions, {(*it)->m_bpButtonFolderPairOptions->GetSize().x, 0});
break;
}
}
@@ -5228,27 +5245,23 @@ void MainDialog::onAddFolderPairKeyEvent(wxKeyEvent& event)
{
case WXK_PAGEUP: //Alt + Page Up
case WXK_NUMPAD_PAGEUP:
- {
- const ptrdiff_t pos = getAddFolderPairPos();
- if (pos >= 0)
+ if (const ptrdiff_t pos = getAddFolderPairPos();
+ pos >= 0)
{
moveAddFolderPairUp(pos);
(pos == 0 ? m_folderPathLeft : additionalFolderPairs_[pos - 1]->m_folderPathLeft)->SetFocus();
}
- }
- return;
+ return;
case WXK_PAGEDOWN: //Alt + Page Down
case WXK_NUMPAD_PAGEDOWN:
- {
- const ptrdiff_t pos = getAddFolderPairPos();
- if (0 <= pos && pos + 1 < makeSigned(additionalFolderPairs_.size()))
+ if (const ptrdiff_t pos = getAddFolderPairPos();
+ 0 <= pos && pos + 1 < makeSigned(additionalFolderPairs_.size()))
{
moveAddFolderPairUp(pos + 1);
additionalFolderPairs_[pos + 1]->m_folderPathLeft->SetFocus();
}
- }
- return;
+ return;
}
event.Skip();
@@ -5507,8 +5520,8 @@ void MainDialog::onMenuExportFileList(wxCommandEvent& event)
header += fmtValue(_("Folder Pairs")) + LINE_BREAK;
std::for_each(begin(folderCmp_), end(folderCmp_), [&](BaseFolderPair& baseFolder)
{
- header += fmtValue(AFS::getDisplayPath(baseFolder.getAbstractPath< LEFT_SIDE>())) + CSV_SEP;
- header += fmtValue(AFS::getDisplayPath(baseFolder.getAbstractPath<RIGHT_SIDE>())) + LINE_BREAK;
+ header += fmtValue(AFS::getDisplayPath(baseFolder.getAbstractPath< SelectSide::left>())) + CSV_SEP;
+ header += fmtValue(AFS::getDisplayPath(baseFolder.getAbstractPath<SelectSide::right>())) + LINE_BREAK;
});
header += LINE_BREAK;
diff --git a/FreeFileSync/Source/ui/main_dlg.h b/FreeFileSync/Source/ui/main_dlg.h
index e22bad6f..dc0ea8d1 100644
--- a/FreeFileSync/Source/ui/main_dlg.h
+++ b/FreeFileSync/Source/ui/main_dlg.h
@@ -251,8 +251,7 @@ private:
void applyFilterConfig();
void applySyncDirections();
- void showFindPanel(); //CTRL + F
- void hideFindPanel();
+ void showFindPanel(bool show); //CTRL + F
void startFindNext(bool searchAscending); //F3
void resetLayout();
@@ -266,7 +265,7 @@ private:
void onMenuOptions (wxCommandEvent& event) override;
void onMenuExportFileList (wxCommandEvent& event) override;
void onMenuResetLayout (wxCommandEvent& event) override { resetLayout(); }
- void onMenuFindItem (wxCommandEvent& event) override { showFindPanel(); } //CTRL + F
+ void onMenuFindItem (wxCommandEvent& event) override { showFindPanel(true /*show*/); } //CTRL + F
void onMenuCheckVersion (wxCommandEvent& event) override;
void onMenuCheckVersionAutomatically(wxCommandEvent& event) override;
void onMenuAbout (wxCommandEvent& event) override;
@@ -347,7 +346,9 @@ private:
std::unique_ptr<FilterConfig> filterCfgOnClipboard_; //copy/paste of filter config
- wxWindowID focusIdAfterSearch_ = wxID_ANY; //used to restore focus after search panel is closed
+ wxWindowID focusAfterCloseLog_ = wxID_ANY; //
+ wxWindowID focusAfterCloseSearch_ = wxID_ANY; //restore focus after panel is closed
+ //don't save wxWindow* to arbitrary window: might not exist anymore when hideFindPanel() uses it!!! (e.g. some folder pair panel)
//mitigate reentrancy:
bool localKeyEventsEnabled_ = true;
diff --git a/FreeFileSync/Source/ui/progress_indicator.cpp b/FreeFileSync/Source/ui/progress_indicator.cpp
index 0ab39dab..fc97858e 100644
--- a/FreeFileSync/Source/ui/progress_indicator.cpp
+++ b/FreeFileSync/Source/ui/progress_indicator.cpp
@@ -47,17 +47,17 @@ constexpr std::chrono::seconds GRAPH_TOTAL_TIME_UPDATE_INTERVAL(2);
const size_t PROGRESS_GRAPH_SAMPLE_SIZE_MAX = 2'500'000; //sizeof(single node) worst case ~ 3 * 8 byte ptr + 16 byte key/value = 40 byte
-inline wxColor getColorBytes() { return { 111, 255, 99 }; } //light green
-inline wxColor getColorItems() { return { 127, 147, 255 }; } //light blue
+inline wxColor getColorBytes() { return {111, 255, 99}; } //light green
+inline wxColor getColorItems() { return {127, 147, 255}; } //light blue
-inline wxColor getColorBytesRim() { return { 20, 200, 0 }; } //medium green
-inline wxColor getColorItemsRim() { return { 90, 120, 255 }; } //medium blue
+inline wxColor getColorBytesRim() { return {20, 200, 0}; } //medium green
+inline wxColor getColorItemsRim() { return {90, 120, 255}; } //medium blue
-//inline wxColor getColorBytesFaint() { return { 205, 255, 202 }; } //faint green
-//inline wxColor getColorItemsFaint() { return { 198, 206, 255 }; } //faint blue
+//inline wxColor getColorBytesFaint() { return {205, 255, 202}; } //faint green
+//inline wxColor getColorItemsFaint() { return {198, 206, 255}; } //faint blue
-inline wxColor getColorBytesDark() { return { 12, 128, 0 }; } //dark green
-inline wxColor getColorItemsDark() { return { 53, 25, 255 }; } //dark blue
+inline wxColor getColorBytesDark() { return {12, 128, 0}; } //dark green
+inline wxColor getColorItemsDark() { return {53, 25, 255}; } //dark blue
inline wxColor getColorLightGrey() { return {0xf2, 0xf2, 0xf2}; }
inline wxColor getColorDarkGrey () { return {0x8f, 0x8f, 0x8f}; }
@@ -95,7 +95,7 @@ public:
void setFraction(double fraction) { fraction_ = fraction; } //value between [0, 1]
private:
- std::pair<double, double> getRangeX() const override { return { 0, 1 }; }
+ std::pair<double, double> getRangeX() const override { return {0, 1}; }
std::vector<CurvePoint> getPoints(double minX, double maxX, const wxSize& areaSizePx) const override
{
@@ -104,10 +104,10 @@ private:
return
{
- { 0, yHigh },
- { fraction_, yHigh },
- { fraction_, yLow },
- { 0, yLow },
+ {0, yHigh},
+ {fraction_, yHigh},
+ {fraction_, yLow },
+ {0, yLow },
};
}
@@ -117,14 +117,14 @@ private:
class CurveDataProgressSeparatorLine : public CurveData
{
- std::pair<double, double> getRangeX() const override { return { 0, 1 }; }
+ std::pair<double, double> getRangeX() const override { return {0, 1}; }
std::vector<CurvePoint> getPoints(double minX, double maxX, const wxSize& areaSizePx) const override
{
return
{
- { 0, 1 },
- { 1, 1 },
+ {0, 1},
+ {1, 1},
};
}
};
@@ -168,13 +168,13 @@ private:
const Statistics* syncStat_ = nullptr; //only bound while sync is running
std::unique_ptr<Taskbar> taskbar_;
- PerfCheck perf_{ WINDOW_REMAINING_TIME, WINDOW_BYTES_PER_SEC }; //estimate remaining time
+ PerfCheck perf_{WINDOW_REMAINING_TIME, WINDOW_BYTES_PER_SEC}; //estimate remaining time
std::chrono::nanoseconds timeLastSpeedEstimate_ = std::chrono::seconds(-100); //used for calculating intervals between showing and collecting perf samples
//initial value: just some big number
- std::shared_ptr<CurveDataProgressBar> curveDataBytes_{ std::make_shared<CurveDataProgressBar>(true /*drawTop*/) };
- std::shared_ptr<CurveDataProgressBar> curveDataItems_{ std::make_shared<CurveDataProgressBar>(false /*drawTop*/) };
+ std::shared_ptr<CurveDataProgressBar> curveDataBytes_{std::make_shared<CurveDataProgressBar>(true /*drawTop*/)};
+ std::shared_ptr<CurveDataProgressBar> curveDataItems_{std::make_shared<CurveDataProgressBar>(false /*drawTop*/)};
bool ignoreErrors_ = false;
};
@@ -422,14 +422,14 @@ public:
{
assert(!samples_.empty() || (lastSample_ == std::pair<std::chrono::nanoseconds, double>()));
- lastSample_ = { timeElapsed, value };
+ lastSample_ = {timeElapsed, value};
//allow for at most one sample per 100ms (handles duplicate inserts, too!) => this is unrelated to UI_UPDATE_INTERVAL!
if (!samples_.empty()) //always unconditionally insert first sample!
if (numeric::dist(timeElapsed, samples_.rbegin()->first) < std::chrono::milliseconds(100))
return;
- samples_.insert(samples_.end(), { timeElapsed, value }); //time is "expected" to be monotonously ascending
+ samples_.insert(samples_.end(), {timeElapsed, value}); //time is "expected" to be monotonously ascending
//documentation differs about whether "hint" should be before or after the to be inserted element!
//however "std::map<>::end()" is interpreted correctly by GCC and VS2010
@@ -452,8 +452,8 @@ private:
upperEndMs += 0.05 *(upperEndMs - samples.begin()->first);
*/
- return { std::chrono::duration<double>(samples_.begin()->first).count(), //need not start with 0, e.g. "binary comparison, graph reset, followed by sync"
- std::chrono::duration<double>(upperEnd).count() };
+ return {std::chrono::duration<double>(samples_.begin()->first).count(), //need not start with 0, e.g. "binary comparison, graph reset, followed by sync"
+ std::chrono::duration<double>(upperEnd).count()};
}
std::optional<CurvePoint> getLessEq(double x) const override //x: seconds since begin
@@ -504,14 +504,14 @@ public:
double getTotalTime() const { return x2_; }
private:
- std::pair<double, double> getRangeX() const override { return { x1_, x2_ }; }
+ std::pair<double, double> getRangeX() const override { return {x1_, x2_}; }
std::vector<CurvePoint> getPoints(double minX, double maxX, const wxSize& areaSizePx) const override
{
return
{
- { x1_, y1_ },
- { x2_, y2_ },
+ {x1_, y1_},
+ {x2_, y2_},
};
}
@@ -529,14 +529,14 @@ public:
void setTime(double x) { x_ = x; }
private:
- std::pair<double, double> getRangeX() const override { return { x_, x_ }; }
+ std::pair<double, double> getRangeX() const override { return {x_, x_}; }
std::vector<CurvePoint> getPoints(double minX, double maxX, const wxSize& areaSizePx) const override
{
return
{
- { x_, y_ },
- { x_, 0 },
+ {x_, y_},
+ {x_, 0 },
};
}
@@ -564,7 +564,7 @@ struct LabelFormatterBytes : public LabelFormatter
return 0;
const double a = bytesProposed / e; //bytesProposed = a * 2^k with a in [1, 2)
assert(1 <= a && a < 2);
- const double steps[] = { 1, 2 };
+ const double steps[] = {1, 2};
return e * numeric::nearMatch(a, std::begin(steps), std::end(steps));
}
@@ -578,7 +578,7 @@ struct LabelFormatterItemCount : public LabelFormatter
{
itemsProposed *= stretchDefaultBlockSize; //enlarge block default size
- const double steps[] = { 1, 2, 5, 10 };
+ const double steps[] = {1, 2, 5, 10};
if (itemsProposed <= 10)
return numeric::nearMatch(itemsProposed, std::begin(steps), std::end(steps)); //like nextNiceNumber(), but without the 2.5 step!
return nextNiceNumber(itemsProposed);
@@ -598,11 +598,11 @@ struct LabelFormatterTimeElapsed : public LabelFormatter
double getOptimalBlockSize(double secProposed) const override
{
//5 sec minimum block size
- const double stepsSec[] = { 5, 10, 20, 30, 60 }; //nice numbers for seconds
+ const double stepsSec[] = {5, 10, 20, 30, 60}; //nice numbers for seconds
if (secProposed <= 60)
return numeric::nearMatch(secProposed, std::begin(stepsSec), std::end(stepsSec));
- const double stepsMin[] = { 1, 2, 5, 10, 15, 20, 30, 60 }; //nice numbers for minutes
+ const double stepsMin[] = {1, 2, 5, 10, 15, 20, 30, 60}; //nice numbers for minutes
if (secProposed <= 3600)
return 60 * numeric::nearMatch(secProposed / 60, std::begin(stepsMin), std::end(stepsMin));
@@ -716,7 +716,7 @@ private:
bool closePressed_ = false;
//remaining time
- PerfCheck perf_{ WINDOW_REMAINING_TIME, WINDOW_BYTES_PER_SEC };
+ PerfCheck perf_{WINDOW_REMAINING_TIME, WINDOW_BYTES_PER_SEC};
std::chrono::nanoseconds timeLastSpeedEstimate_ = std::chrono::seconds(-100); //used for calculating intervals between collecting perf samples
std::chrono::nanoseconds timeLastGraphTotalUpdate_ = std::chrono::seconds(-100);
@@ -1513,7 +1513,7 @@ auto SyncProgressDialogImpl<TopLevelDialog>::destroy(bool autoClose, bool restor
this->Destroy(); //wxWidgets macOS: simple "delete"!!!!!!!
- return { autoCloseDialog, dlgSizeBuf_, isMaximized };
+ return {autoCloseDialog, dlgSizeBuf_, isMaximized};
}
diff --git a/FreeFileSync/Source/ui/search_grid.cpp b/FreeFileSync/Source/ui/search_grid.cpp
index 8c102d97..c3f459f4 100644
--- a/FreeFileSync/Source/ui/search_grid.cpp
+++ b/FreeFileSync/Source/ui/search_grid.cpp
@@ -123,7 +123,7 @@ std::pair<const Grid*, ptrdiff_t> fff::findGridMatch(const Grid& grid1, const Gr
findRow<false>(grid, searchString, searchAscending, rowFirst, rowLast);
if (targetRow >= 0)
{
- result = { &grid, targetRow };
+ result = {&grid, targetRow};
return true;
}
return false;
diff --git a/FreeFileSync/Source/ui/small_dlgs.cpp b/FreeFileSync/Source/ui/small_dlgs.cpp
index fbd0cbf9..1f569467 100644
--- a/FreeFileSync/Source/ui/small_dlgs.cpp
+++ b/FreeFileSync/Source/ui/small_dlgs.cpp
@@ -1082,12 +1082,12 @@ SyncConfirmationDlg::SyncConfirmationDlg(wxWindow* parent,
};
setValue(*m_staticTextData, st.getBytesToProcess() == 0, formatFilesizeShort(st.getBytesToProcess()), *m_bitmapData, "data");
- setIntValue(*m_staticTextCreateLeft, st.createCount< LEFT_SIDE>(), *m_bitmapCreateLeft, "so_create_left_sicon");
- setIntValue(*m_staticTextUpdateLeft, st.updateCount< LEFT_SIDE>(), *m_bitmapUpdateLeft, "so_update_left_sicon");
- setIntValue(*m_staticTextDeleteLeft, st.deleteCount< LEFT_SIDE>(), *m_bitmapDeleteLeft, "so_delete_left_sicon");
- setIntValue(*m_staticTextCreateRight, st.createCount<RIGHT_SIDE>(), *m_bitmapCreateRight, "so_create_right_sicon");
- setIntValue(*m_staticTextUpdateRight, st.updateCount<RIGHT_SIDE>(), *m_bitmapUpdateRight, "so_update_right_sicon");
- setIntValue(*m_staticTextDeleteRight, st.deleteCount<RIGHT_SIDE>(), *m_bitmapDeleteRight, "so_delete_right_sicon");
+ setIntValue(*m_staticTextCreateLeft, st.createCount< SelectSide::left>(), *m_bitmapCreateLeft, "so_create_left_sicon");
+ setIntValue(*m_staticTextUpdateLeft, st.updateCount< SelectSide::left>(), *m_bitmapUpdateLeft, "so_update_left_sicon");
+ setIntValue(*m_staticTextDeleteLeft, st.deleteCount< SelectSide::left>(), *m_bitmapDeleteLeft, "so_delete_left_sicon");
+ setIntValue(*m_staticTextCreateRight, st.createCount<SelectSide::right>(), *m_bitmapCreateRight, "so_create_right_sicon");
+ setIntValue(*m_staticTextUpdateRight, st.updateCount<SelectSide::right>(), *m_bitmapUpdateRight, "so_update_right_sicon");
+ setIntValue(*m_staticTextDeleteRight, st.deleteCount<SelectSide::right>(), *m_bitmapDeleteRight, "so_delete_right_sicon");
GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize()
//=> works like a charm for GTK2 with window resizing problems and title bar corruption; e.g. Debian!!!
@@ -1426,7 +1426,7 @@ std::vector<ExternalApp> OptionsDlg::getExtApp() const
description = it->second;
if (!description.empty() || !commandline.empty())
- output.push_back({ description, commandline });
+ output.push_back({description, commandline});
}
return output;
}
diff --git a/FreeFileSync/Source/ui/status_handler_impl.h b/FreeFileSync/Source/ui/status_handler_impl.h
deleted file mode 100644
index c52df76e..00000000
--- a/FreeFileSync/Source/ui/status_handler_impl.h
+++ /dev/null
@@ -1,63 +0,0 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef STATUS_HANDLER_IMPL_H_145234543248059083415565
-#define STATUS_HANDLER_IMPL_H_145234543248059083415565
-
-#include <chrono>
-#include <thread>
-#include <zen/basic_math.h>
-#include <zen/zstring.h>
-#include <zen/i18n.h>
-
-
-namespace fff
-{
-namespace
-{
-void delayAndCountDown(std::chrono::steady_clock::time_point delayUntil, const std::function<void(const std::wstring& timeRemMsg)>& notifyStatus)
-{
- for (auto now = std::chrono::steady_clock::now(); now < delayUntil; now = std::chrono::steady_clock::now())
- {
- if (notifyStatus)
- {
- const auto timeRemMs = std::chrono::duration_cast<std::chrono::milliseconds>(delayUntil - now).count();
- notifyStatus(_P("1 sec", "%x sec", numeric::intDivCeil(timeRemMs, 1000)));
- }
-
- std::this_thread::sleep_for(UI_UPDATE_INTERVAL / 2);
- }
-}
-
-
-void runCommandAndLogErrors(const Zstring& cmdLine, zen::ErrorLog& errorLog)
-{
- using namespace zen;
-
- try
- {
- //give consoleExecute() some "time to fail", but not too long to hang our process
- const int DEFAULT_APP_TIMEOUT_MS = 100;
-
- if (const auto& [exitCode, output] = consoleExecute(cmdLine, DEFAULT_APP_TIMEOUT_MS); //throw SysError, SysErrorTimeOut
- exitCode != 0)
- throw SysError(formatSystemError("", replaceCpy(_("Exit code %x"), L"%x", numberTo<std::wstring>(exitCode)), utfTo<std::wstring>(output)));
-
- errorLog.logMsg(_("Executing command:") + L' ' + utfTo<std::wstring>(cmdLine) + L" [" + replaceCpy(_("Exit code %x"), L"%x", L"0") + L']', MSG_TYPE_INFO);
- }
- catch (SysErrorTimeOut&) //child process not failed yet => probably fine :>
- {
- errorLog.logMsg(_("Executing command:") + L' ' + utfTo<std::wstring>(cmdLine), MSG_TYPE_INFO);
- }
- catch (const SysError& e)
- {
- errorLog.logMsg(replaceCpy(_("Command %x failed."), L"%x", fmtPath(cmdLine)) + L"\n\n" + e.toString(), MSG_TYPE_ERROR);
- }
-}
-}
-}
-
-#endif //STATUS_HANDLER_IMPL_H_145234543248059083415565
diff --git a/FreeFileSync/Source/ui/tree_grid.cpp b/FreeFileSync/Source/ui/tree_grid.cpp
index 9e6ec23c..ff261ef4 100644
--- a/FreeFileSync/Source/ui/tree_grid.cpp
+++ b/FreeFileSync/Source/ui/tree_grid.cpp
@@ -29,8 +29,8 @@ namespace
const int PERCENTAGE_BAR_WIDTH_DIP = 60;
const int TREE_GRID_GAP_SIZE_DIP = 2;
-inline wxColor getColorPercentBorder () { return { 198, 198, 198 }; }
-inline wxColor getColorPercentBackground() { return { 0xf8, 0xf8, 0xf8 }; }
+inline wxColor getColorPercentBorder () { return {198, 198, 198}; }
+inline wxColor getColorPercentBackground() { return {0xf8, 0xf8, 0xf8}; }
}
@@ -39,8 +39,8 @@ TreeView::TreeView(FolderComparison& folderCmp, const SortInfo& si) : folderCmp_
//remove truly empty folder pairs as early as this: we want to distinguish single/multiple folder pair cases by looking at "folderCmp"
std::erase_if(folderCmp_, [](const std::shared_ptr<BaseFolderPair>& baseObj)
{
- return AFS::isNullPath(baseObj->getAbstractPath< LEFT_SIDE>()) &&
- AFS::isNullPath(baseObj->getAbstractPath<RIGHT_SIDE>());
+ return AFS::isNullPath(baseObj->getAbstractPath< SelectSide::left>()) &&
+ AFS::isNullPath(baseObj->getAbstractPath<SelectSide::right>());
});
}
@@ -73,16 +73,16 @@ void TreeView::extractVisibleSubtree(ContainerObject& hierObj, //in
// switch (file.getSyncDir())
// {
// case SyncDirection::left:
- // return file.getFileSize<RIGHT_SIDE>();
+ // return file.getFileSize<SelectSide::right>();
// case SyncDirection::right:
- // return file.getFileSize<LEFT_SIDE>();
+ // return file.getFileSize<SelectSide::left>();
// case SyncDirection::none:
// break;
// }
//prefer file-browser semantics over sync preview (=> always show useful numbers, even for SyncDirection::none)
//discussion: https://freefilesync.org/forum/viewtopic.php?t=1595
- return std::max(file.getFileSize<LEFT_SIDE>(), file.getFileSize<RIGHT_SIDE>());
+ return std::max(file.getFileSize<SelectSide::left>(), file.getFileSize<SelectSide::right>());
};
cont.firstFileId = nullptr;
@@ -275,13 +275,13 @@ void TreeView::getChildren(const Container& cont, unsigned int level, std::vecto
for (const DirNodeImpl& subDir : cont.subDirs)
{
- output.push_back({ level, 0, &subDir, NodeType::folder});
+ output.push_back({level, 0, &subDir, NodeType::folder});
workList.emplace_back(subDir.bytesGross, &output.back().percent);
}
if (cont.firstFileId)
{
- output.push_back({ level, 0, &cont, NodeType::files });
+ output.push_back({level, 0, &cont, NodeType::files});
workList.emplace_back(cont.bytesNet, &output.back().percent);
}
calcPercentage(workList);
@@ -345,7 +345,7 @@ void TreeView::applySubView(std::vector<RootNodeImpl>&& newView)
for (const RootNodeImpl& root : folderCmpView_)
{
- flatTree_.push_back({ 0, 0, &root, NodeType::root });
+ flatTree_.push_back({0, 0, &root, NodeType::root});
workList.emplace_back(root.bytesGross, &flatTree_.back().percent);
}
@@ -394,8 +394,8 @@ void TreeView::updateView(Predicate pred)
else
{
root.baseFolder = baseObj;
- root.displayName = getShortDisplayNameForFolderPair(baseObj->getAbstractPath< LEFT_SIDE>(),
- baseObj->getAbstractPath<RIGHT_SIDE>());
+ root.displayName = getShortDisplayNameForFolderPair(baseObj->getAbstractPath< SelectSide::left>(),
+ baseObj->getAbstractPath<SelectSide::right>());
this->compressNode(root); //"this->" required by two-pass lookup as enforced by GCC 4.7
}
@@ -663,18 +663,18 @@ wxColor getColorForLevel(size_t level)
switch (level % 12)
{
//*INDENT-OFF*
- case 0: return { 0xcc, 0xcc, 0xff };
- case 1: return { 0xcc, 0xff, 0xcc };
- case 2: return { 0xff, 0xff, 0x99 };
- case 3: return { 0xdd, 0xdd, 0xdd };
- case 4: return { 0xff, 0xcc, 0xff };
- case 5: return { 0x99, 0xff, 0xcc };
- case 6: return { 0xcc, 0xcc, 0x99 };
- case 7: return { 0xff, 0xcc, 0xcc };
- case 8: return { 0xcc, 0xff, 0x99 };
- case 9: return { 0xff, 0xff, 0xcc };
- case 10: return { 0xcc, 0xff, 0xff };
- case 11: return { 0xff, 0xcc, 0x99 };
+ case 0: return {0xcc, 0xcc, 0xff};
+ case 1: return {0xcc, 0xff, 0xcc};
+ case 2: return {0xff, 0xff, 0x99};
+ case 3: return {0xdd, 0xdd, 0xdd};
+ case 4: return {0xff, 0xcc, 0xff};
+ case 5: return {0x99, 0xff, 0xcc};
+ case 6: return {0xcc, 0xcc, 0x99};
+ case 7: return {0xff, 0xcc, 0xcc};
+ case 8: return {0xcc, 0xff, 0x99};
+ case 9: return {0xff, 0xff, 0xcc};
+ case 10: return {0xcc, 0xff, 0xff};
+ case 11: return {0xff, 0xcc, 0x99};
//*INDENT-ON*
}
assert(false);
@@ -724,8 +724,8 @@ private:
if (std::unique_ptr<TreeView::Node> node = getDataView().getLine(row))
if (const TreeView::RootNode* root = dynamic_cast<const TreeView::RootNode*>(node.get()))
{
- const std::wstring& dirLeft = AFS::getDisplayPath(root->baseFolder.getAbstractPath< LEFT_SIDE>());
- const std::wstring& dirRight = AFS::getDisplayPath(root->baseFolder.getAbstractPath<RIGHT_SIDE>());
+ const std::wstring& dirLeft = AFS::getDisplayPath(root->baseFolder.getAbstractPath< SelectSide::left>());
+ const std::wstring& dirRight = AFS::getDisplayPath(root->baseFolder.getAbstractPath<SelectSide::right>());
if (dirLeft.empty())
return dirRight;
else if (dirRight.empty())
@@ -1131,7 +1131,7 @@ private:
menu.addItem(_("&Default"), setDefaultColumns); //'&' -> reuse text from "default" buttons elsewhere
//--------------------------------------------------------------------------------------------------------
- menu.popup(grid_, { event.mousePos_.x, grid_.getColumnLabelHeight() });
+ menu.popup(grid_, {event.mousePos_.x, grid_.getColumnLabelHeight()});
//event.Skip();
}
diff --git a/FreeFileSync/Source/ui/tree_grid_attr.h b/FreeFileSync/Source/ui/tree_grid_attr.h
index eff14c4e..4cd0f2f1 100644
--- a/FreeFileSync/Source/ui/tree_grid_attr.h
+++ b/FreeFileSync/Source/ui/tree_grid_attr.h
@@ -36,9 +36,9 @@ std::vector<ColAttributesTree> getTreeGridDefaultColAttribs()
using namespace zen;
return //harmonize with tree_view.cpp::onGridLabelContext() => expects stretched folder and non-stretched other columns!
{
- { ColumnTypeTree::folder, - 2 * fastFromDIP(60), 1, true }, //stretch to full width and substract sum of fixed size widths
- { ColumnTypeTree::itemCount, fastFromDIP(60), 0, true },
- { ColumnTypeTree::bytes, fastFromDIP(60), 0, true }, //GTK needs a few pixels more width
+ {ColumnTypeTree::folder, - 2 * fastFromDIP(60), 1, true}, //stretch to full width and substract sum of fixed size widths
+ {ColumnTypeTree::itemCount, fastFromDIP(60), 0, true},
+ {ColumnTypeTree::bytes, fastFromDIP(60), 0, true}, //GTK needs a few pixels more width
};
}
diff --git a/FreeFileSync/Source/ui/version_check.cpp b/FreeFileSync/Source/ui/version_check.cpp
index c81c0582..087582ee 100644
--- a/FreeFileSync/Source/ui/version_check.cpp
+++ b/FreeFileSync/Source/ui/version_check.cpp
@@ -10,7 +10,7 @@
#include <zen/string_tools.h>
#include <zen/i18n.h>
#include <zen/utf.h>
-#include <zen/file_access.h>
+//#include <zen/file_access.h>
#include <zen/scope_guard.h>
#include <zen/build_info.h>
#include <zen/basic_math.h>
@@ -27,6 +27,7 @@
#include "../version/version.h"
#include "small_dlgs.h"
+ #include <zen/symlink_target.h>
using namespace zen;
@@ -117,11 +118,10 @@ std::wstring getIso3166Country()
//coordinate with get_latest_version_number.php
std::vector<std::pair<std::string, std::string>> geHttpPostParameters(wxWindow& parent) //throw SysError
{
- assert(runningOnMainThread()); //this function is not thread-safe, e.g. consider wxWidgets usage in isPortableVersion()
+ assert(runningOnMainThread()); //this function is not thread-safe, e.g. consider wxWidgets usage in getIso639Language()
std::vector<std::pair<std::string, std::string>> params;
params.emplace_back("ffs_version", ffsVersion);
- params.emplace_back("installation_type", isPortableVersion() ? "Portable" : "Local");
params.emplace_back("os_name", "Linux");
@@ -159,7 +159,7 @@ void showUpdateAvailableDialog(wxWindow* parent, const std::string& onlineVersio
std::wstring updateDetailsMsg;
try
{
- updateDetailsMsg = utfTo<std::wstring>(sendHttpGet(utfTo<Zstring>("https://api.freefilesync.org/latest_changes?" + xWwwFormUrlEncode({ { "since", ffsVersion } })),
+ updateDetailsMsg = utfTo<std::wstring>(sendHttpGet(utfTo<Zstring>("https://api.freefilesync.org/latest_changes?" + xWwwFormUrlEncode({{"since", ffsVersion}})),
ffsUpdateCheckUserAgent, nullptr /*caCertFilePath*/, nullptr /*notifyUnbufferedIO*/).readAll()); //throw SysError
}
catch (const SysError& e) { updateDetailsMsg = _("Failed to retrieve update information.") + + L"\n\n" + e.toString(); }
diff --git a/FreeFileSync/Source/version/version.h b/FreeFileSync/Source/version/version.h
index e75db779..c621b68c 100644
--- a/FreeFileSync/Source/version/version.h
+++ b/FreeFileSync/Source/version/version.h
@@ -3,7 +3,7 @@
namespace fff
{
-const char ffsVersion[] = "11.6"; //internal linkage!
+const char ffsVersion[] = "11.7"; //internal linkage!
const char FFS_VERSION_SEPARATOR = '.';
}
diff --git a/libcurl/rest.cpp b/libcurl/rest.cpp
index 60fcb8eb..0964f63a 100644
--- a/libcurl/rest.cpp
+++ b/libcurl/rest.cpp
@@ -181,5 +181,5 @@ HttpSession::Result HttpSession::perform(const std::string& serverRelPath,
}
lastSuccessfulUseTime_ = std::chrono::steady_clock::now();
- return { static_cast<int>(httpStatus) /*, contentType ? contentType : ""*/ };
+ return {static_cast<int>(httpStatus) /*, contentType ? contentType : ""*/};
}
diff --git a/wx+/choice_enum.h b/wx+/choice_enum.h
index 626aa39a..a590861a 100644
--- a/wx+/choice_enum.h
+++ b/wx+/choice_enum.h
@@ -41,7 +41,7 @@ struct EnumDescrList
{
EnumDescrList& add(Enum value, const wxString& text, const wxString& tooltip = {})
{
- descrList.push_back({ value, { text, tooltip } });
+ descrList.push_back({value, {text, tooltip}});
return *this;
}
diff --git a/wx+/graph.cpp b/wx+/graph.cpp
index ba87299e..be75ac4d 100644
--- a/wx+/graph.cpp
+++ b/wx+/graph.cpp
@@ -36,7 +36,7 @@ double zen::nextNiceNumber(double blockSize) //round to next number which is a c
assert(1 <= a && a < 10);
//have a look at leading two digits: "nice" numbers start with 1, 2, 2.5 and 5
- const double steps[] = { 1, 2, 2.5, 5, 10 };
+ const double steps[] = {1, 2, 2.5, 5, 10};
return e * numeric::nearMatch(a, std::begin(steps), std::end(steps));
}
@@ -48,16 +48,16 @@ wxColor getDefaultColor(size_t pos)
switch (pos % 10)
{
//*INDENT-OFF*
- case 0: return { 0, 69, 134 }; //blue
- case 1: return { 255, 66, 14 }; //red
- case 2: return { 255, 211, 32 }; //yellow
- case 3: return { 87, 157, 28 }; //green
- case 4: return { 126, 0, 33 }; //royal
- case 5: return { 131, 202, 255 }; //light blue
- case 6: return { 49, 64, 4 }; //dark green
- case 7: return { 174, 207, 0 }; //light green
- case 8: return { 75, 31, 111 }; //purple
- case 9: return { 255, 149, 14 }; //orange
+ case 0: return { 0, 69, 134}; //blue
+ case 1: return {255, 66, 14}; //red
+ case 2: return {255, 211, 32}; //yellow
+ case 3: return { 87, 157, 28}; //green
+ case 4: return {126, 0, 33}; //royal
+ case 5: return {131, 202, 255}; //light blue
+ case 6: return { 49, 64, 4}; //dark green
+ case 7: return {174, 207, 0}; //light green
+ case 8: return { 75, 31, 111}; //purple
+ case 9: return {255, 149, 14}; //orange
//*INDENT-ON*
}
assert(false);
diff --git a/wx+/graph.h b/wx+/graph.h
index c843b09b..288ef9e7 100644
--- a/wx+/graph.h
+++ b/wx+/graph.h
@@ -218,7 +218,7 @@ public:
void addCurve(const std::shared_ptr<CurveData>& data, const CurveAttributes& ca = CurveAttributes());
void clearCurves() { curves_.clear(); }
- static wxColor getBorderColor() { return { 130, 135, 144 }; } //medium grey, the same Win7 uses for other frame borders => not accessible! but no big deal...
+ static wxColor getBorderColor() { return {130, 135, 144}; } //medium grey, the same Win7 uses for other frame borders => not accessible! but no big deal...
class MainAttributes
{
@@ -330,7 +330,7 @@ private:
CurveList curves_;
//perf!!! generating the font is *very* expensive! => buffer for Graph2D::render()!
- const wxFont labelFont_ { wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, L"Arial" };
+ const wxFont labelFont_{wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, L"Arial"};
};
}
diff --git a/wx+/grid.cpp b/wx+/grid.cpp
index cd91b1af..0111ccf7 100644
--- a/wx+/grid.cpp
+++ b/wx+/grid.cpp
@@ -27,8 +27,8 @@ using namespace zen;
//let's NOT create wxWidgets objects statically:
-wxColor GridData::getColorSelectionGradientFrom() { return { 137, 172, 255 }; } //blue: HSL: 158, 255, 196 HSV: 222, 0.46, 1
-wxColor GridData::getColorSelectionGradientTo () { return { 225, 234, 255 }; } // HSL: 158, 255, 240 HSV: 222, 0.12, 1
+wxColor GridData::getColorSelectionGradientFrom() { return {137, 172, 255}; } //blue: HSL: 158, 255, 196 HSV: 222, 0.46, 1
+wxColor GridData::getColorSelectionGradientTo () { return {225, 234, 255}; } // HSL: 158, 255, 240 HSV: 222, 0.12, 1
int GridData::getColumnGapLeft() { return fastFromDIP(4); }
@@ -493,8 +493,8 @@ public:
const int yFrom = refParent().CalcUnscrolledPosition(clientRect.GetTopLeft ()).y;
const int yTo = refParent().CalcUnscrolledPosition(clientRect.GetBottomRight()).y;
- return { std::max(yFrom / rowHeight_, 0),
- std::min<ptrdiff_t>((yTo / rowHeight_) + 1, refParent().getRowCount()) };
+ return {std::max(yFrom / rowHeight_, 0),
+ std::min<ptrdiff_t>((yTo / rowHeight_) + 1, refParent().getRowCount())};
}
private:
@@ -1351,6 +1351,7 @@ private:
return;
const double mouseDragSpeedIncScrollU = MOUSE_DRAG_ACCELERATION_DIP * wnd_.rowLabelWin_.getRowHeight() / pixelsPerUnitY; //unit: [scroll units / (DIP * sec)]
+ //design alternative: "Dynamic autoscroll based on escape velocity": https://devblogs.microsoft.com/oldnewthing/20210128-00/?p=104768
auto autoScroll = [&](int overlapPix, double& toScroll)
{
@@ -1986,7 +1987,7 @@ void Grid::setColumnConfig(const std::vector<Grid::ColAttributes>& attr)
assert(ca.type != ColumnType::none);
if (ca.visible)
- visCols.push_back({ ca.type, ca.offset, std::max(ca.stretch, 0) });
+ visCols.push_back({ca.type, ca.offset, std::max(ca.stretch, 0)});
}
//"ownership" of visible columns is now within Grid
@@ -2101,10 +2102,10 @@ Grid::ColumnPosInfo Grid::getColumnAtPos(int posX) const
{
accWidth += cw.width;
if (posX < accWidth)
- return { cw.type, posX + cw.width - accWidth, cw.width };
+ return {cw.type, posX + cw.width - accWidth, cw.width};
}
}
- return { ColumnType::none, 0, 0 };
+ return {ColumnType::none, 0, 0};
}
@@ -2386,7 +2387,7 @@ std::vector<Grid::ColumnWidth> Grid::getColWidths(int mainWinWidth) const //eval
else
width = std::max(width, 0); //support smaller width than COLUMN_MIN_WIDTH_DIP if set via configuration
- output.push_back({ vc.type, width });
+ output.push_back({vc.type, width});
}
return output;
}
diff --git a/wx+/grid.h b/wx+/grid.h
index 8d68ffd7..4de76b66 100644
--- a/wx+/grid.h
+++ b/wx+/grid.h
@@ -387,7 +387,7 @@ std::vector<Grid::ColAttributes> convertColAttributes(const std::vector<ColAttrR
{
std::vector<Grid::ColAttributes> output;
for (const ColAttrReal& ca : makeConsistent(attribs, defaults))
- output.push_back({ static_cast<ColumnType>(ca.type), ca.offset, ca.stretch, ca.visible });
+ output.push_back({static_cast<ColumnType>(ca.type), ca.offset, ca.stretch, ca.visible});
return output;
}
@@ -399,7 +399,7 @@ std::vector<ColAttrReal> convertColAttributes(const std::vector<Grid::ColAttribu
std::vector<ColAttrReal> output;
for (const Grid::ColAttributes& ca : attribs)
- output.push_back({ static_cast<ColTypeReal>(ca.type), ca.offset, ca.stretch, ca.visible });
+ output.push_back({static_cast<ColTypeReal>(ca.type), ca.offset, ca.stretch, ca.visible});
return output;
}
}
diff --git a/wx+/image_resources.cpp b/wx+/image_resources.cpp
index 42114ebb..fbfeb0d8 100644
--- a/wx+/image_resources.cpp
+++ b/wx+/image_resources.cpp
@@ -50,27 +50,23 @@ ImageHolder xbrzScale(int width, int height, const unsigned char* imageRgb, cons
*out++ = xbrz::makePixel(*alpha++, rgb[0], rgb[1], rgb[2]);
}
//-----------------------------------------------------
- xbrz::scale(hqScale, //size_t factor, //valid range: 2 - SCALE_FACTOR_MAX
- argbSrc, //const uint32_t* src,
- xbrTrg, //uint32_t* trg,
- width, height, //int srcWidth, int srcHeight,
- xbrz::ColorFormat::ARGB_UNBUFFERED); //ColorFormat colFmt,
- //test: total xBRZ scaling time with ARGB: 300ms, ARGB_UNBUFFERED: 50ms
+ xbrz::scale(hqScale, //size_t factor - valid range: 2 - SCALE_FACTOR_MAX
+ argbSrc, //const uint32_t* src
+ xbrTrg, //uint32_t* trg
+ width, height, //int srcWidth, int srcHeight
+ xbrz::ColorFormat::argbUnbuffered); //ColorFormat colFmt
+ //test: total xBRZ scaling time with ARGB: 300ms, ARGB unbuffered: 50ms
//-----------------------------------------------------
//convert BGRA to RGB + alpha
ImageHolder trgImg(hqWidth, hqHeight, true /*withAlpha*/);
- {
- unsigned char* rgb = trgImg.getRgb();
- unsigned char* alpha = trgImg.getAlpha();
- std::for_each(xbrTrg, xbrTrg + hqWidth * hqHeight, [&](uint32_t col)
- {
- *alpha++ = xbrz::getAlpha(col);
- *rgb++ = xbrz::getRed (col);
- *rgb++ = xbrz::getGreen(col);
- *rgb++ = xbrz::getBlue (col);
- });
- }
+ std::for_each(xbrTrg, xbrTrg + hqWidth * hqHeight, [rgb = trgImg.getRgb(), alpha = trgImg.getAlpha()](uint32_t col) mutable
+ {
+ *alpha++ = xbrz::getAlpha(col);
+ *rgb++ = xbrz::getRed (col);
+ *rgb++ = xbrz::getGreen(col);
+ *rgb++ = xbrz::getBlue (col);
+ });
return trgImg;
}
@@ -128,7 +124,7 @@ private:
Protected<std::vector<std::pair<std::string, ImageHolder>>> result_;
using TaskType = FunctionReturnTypeT<decltype(&getScalerTask)>;
- std::optional<ThreadGroup<TaskType>> threadGroup_{ ThreadGroup<TaskType>(std::max<int>(std::thread::hardware_concurrency(), 1), Zstr("xBRZ Scaler")) };
+ std::optional<ThreadGroup<TaskType>> threadGroup_{ThreadGroup<TaskType>(std::max<int>(std::thread::hardware_concurrency(), 1), Zstr("xBRZ Scaler"))};
//hardware_concurrency() == 0 if "not computable or well defined"
};
@@ -296,8 +292,8 @@ const wxImage& ImageBuffer::getImage(const std::string& name, int maxWidth /*opt
if (rawImg.GetHeight() >= outHeight) //=> skip needless xBRZ upscaling
it = imagesOut_.emplace(imkey, shrinkImage(rawImg, -1 /*maxWidth*/, outHeight)).first;
else if (rawImg.GetHeight() >= 0.9 * outHeight) //almost there: also no need for xBRZ-scale
- //however: for 125% DPI scaling, "2xBRZ + bilinear downscale" gives a better result than mere "125% bilinear upscale"!
- it = imagesOut_.emplace(imkey, rawImg.Scale(numeric::intDivRound(outHeight * rawImg.GetWidth(), rawImg.GetHeight()), outHeight, wxIMAGE_QUALITY_BILINEAR)).first;
+ it = imagesOut_.emplace(imkey, bilinearScale(rawImg, numeric::intDivRound(outHeight * rawImg.GetWidth(), rawImg.GetHeight()), outHeight)).first;
+ //however: for 125% DPI scaling, "2xBRZ + bilinear downscale" gives a better result than mere "125% bilinear upscale"
else
it = imagesOut_.emplace(imkey, shrinkImage(getScaledImage(name), -1 /*maxWidth*/, outHeight)).first;
}
diff --git a/wx+/image_tools.cpp b/wx+/image_tools.cpp
index 6ba95c5e..bbc8cee7 100644
--- a/wx+/image_tools.cpp
+++ b/wx+/image_tools.cpp
@@ -8,6 +8,7 @@
#include <zen/string_tools.h>
#include <zen/zstring.h>
#include <wx/app.h>
+#include <xBRZ/src/xbrz_tools.h>
using namespace zen;
@@ -91,12 +92,12 @@ void copyImageLayover(const wxImage& src,
for (int x = 0; x < srcWidth; ++x)
{
const int w1 = *srcAlpha; //alpha-composition interpreted as weighted average
- const int w2 = *trgAlpha * (255 - w1) / 255;
+ const int w2 = numeric::intDivRound(*trgAlpha * (255 - w1), 255);
const int wSum = w1 + w2;
auto calcColor = [w1, w2, wSum](unsigned char colsrc, unsigned char colTrg)
{
- return static_cast<unsigned char>(wSum == 0 ? 0 : (colsrc * w1 + colTrg * w2) / wSum);
+ return static_cast<unsigned char>(wSum == 0 ? 0 : numeric::intDivRound(colsrc * w1 + colTrg * w2, wSum));
};
trgRgb[0] = calcColor(srcRgb[0], trgRgb[0]);
trgRgb[1] = calcColor(srcRgb[1], trgRgb[1]);
@@ -138,7 +139,7 @@ wxImage zen::stackImages(const wxImage& img1, const wxImage& img2, ImageStackLay
const int img2Height = img2.GetHeight();
const wxSize newSize = dir == ImageStackLayout::horizontal ?
- wxSize(img1Width + gap + img2Width, std::max(img1Height, img2Height)) :
+ wxSize(img1Width + gap + img2Width, std::max(img1Height, img2Height)) :
wxSize(std::max(img1Width, img2Width), img1Height + gap + img2Height);
wxImage output(newSize);
@@ -181,7 +182,7 @@ wxImage zen::createImageFromText(const wxString& text, const wxFont& font, const
//assert(!contains(text, L"&")); //accelerator keys not supported here
wxString textFmt = replaceCpy(text, L"&", L"", false);
- const std::vector<std::pair<wxString, wxSize>> lineInfo = getTextExtentInfo(textFmt, font);
+ const std::vector<std::pair<wxString, wxSize>> lineInfo = getTextExtentInfo(textFmt, font);
int maxWidth = 0;
int lineHeight = 0;
@@ -197,8 +198,8 @@ wxImage zen::createImageFromText(const wxString& text, const wxFont& font, const
{
wxMemoryDC dc(newBitmap);
- if (wxTheApp->GetLayoutDirection() == wxLayout_RightToLeft)
- dc.SetLayoutDirection(wxLayout_RightToLeft); //handle e.g. "weak" bidi characters: -> arrows in hebrew/arabic
+ if (wxTheApp->GetLayoutDirection() == wxLayout_RightToLeft)
+ dc.SetLayoutDirection(wxLayout_RightToLeft); //handle e.g. "weak" bidi characters: -> arrows in hebrew/arabic
dc.SetBackground(*wxWHITE_BRUSH);
dc.Clear();
@@ -238,7 +239,7 @@ wxImage zen::createImageFromText(const wxString& text, const wxFont& font, const
for (int i = 0; i < pixelCount; ++i)
{
//black(0,0,0) becomes wxIMAGE_ALPHA_OPAQUE(255), while white(255,255,255) becomes wxIMAGE_ALPHA_TRANSPARENT(0)
- *alpha++ = static_cast<unsigned char>((255 - rgb[0] + 255 - rgb[1] + 255 - rgb[2]) / 3); //mixed-mode arithmetics!
+ *alpha++ = static_cast<unsigned char>(numeric::intDivRound(3 * 255 - rgb[0] - rgb[1] - rgb[2], 3)); //mixed-mode arithmetics!
*rgb++ = col.Red (); //
*rgb++ = col.Green(); //apply actual text color
@@ -312,6 +313,46 @@ wxImage zen::resizeCanvas(const wxImage& img, wxSize newSize, int alignment)
}
+wxImage zen::bilinearScale(const wxImage& img, int width, int height)
+{
+ assert(img.HasAlpha());
+ const auto imgReader = [rgb = img.GetData(), alpha = img.GetAlpha(), srcWidth = img.GetSize().x](int x, int y, xbrz::BytePixel& pix)
+ {
+ const int idx = y * srcWidth + x;
+ const unsigned char* const ptr = rgb + idx * 3;
+
+ const unsigned char a = alpha[idx];
+ pix[0] = a;
+ pix[1] = xbrz::premultiply(ptr[0], a); //r
+ pix[2] = xbrz::premultiply(ptr[1], a); //g
+ pix[3] = xbrz::premultiply(ptr[2], a); //b
+ };
+
+ wxImage imgOut(width, height);
+ imgOut.SetAlpha();
+
+ const auto imgWriter = [rgb = imgOut.GetData(), alpha = imgOut.GetAlpha()](const xbrz::BytePixel& pix) mutable
+ {
+ const unsigned char a = pix[0];
+ * alpha++ = a;
+ * rgb++ = xbrz::demultiply(pix[1], a); //r
+ *rgb++ = xbrz::demultiply(pix[2], a); //g
+ *rgb++ = xbrz::demultiply(pix[3], a); //b
+ };
+
+ xbrz::bilinearScaleSimple(imgReader, //PixReader srcReader
+ img.GetSize().x, //int srcWidth
+ img.GetSize().y, //int srcHeight
+ imgWriter, //PixWriter trgWriter
+ width, //int trgWidth
+ height, //int trgHeight
+ 0, //int yFirst
+ height); //int yLast
+ return imgOut;
+ //return img.Scale(width, height, wxIMAGE_QUALITY_BILINEAR);
+}
+
+
wxImage zen::shrinkImage(const wxImage& img, int maxWidth /*optional*/, int maxHeight /*optional*/)
{
wxSize newSize = img.GetSize();
@@ -330,8 +371,7 @@ wxImage zen::shrinkImage(const wxImage& img, int maxWidth /*optional*/, int maxH
if (newSize == img.GetSize())
return img;
- return img.Scale(newSize.x, newSize.y, wxIMAGE_QUALITY_BILINEAR); //looks sharper than wxIMAGE_QUALITY_HIGH!
- //perf: use xbrz::bilinearScale instead? only about 10% shorter runtime
+ return bilinearScale(img, newSize.x, newSize.y); //looks sharper than wxIMAGE_QUALITY_HIGH!
}
diff --git a/wx+/image_tools.h b/wx+/image_tools.h
index 0f0fb9c2..c2fed4c1 100644
--- a/wx+/image_tools.h
+++ b/wx+/image_tools.h
@@ -50,6 +50,9 @@ void convertToVanillaImage(wxImage& img); //add alpha channel if missing + remov
//wxColor hsvColor(double h, double s, double v); //h within [0, 360), s, v within [0, 1]
+//does *not* fuck up alpha channel like naive bilinear implementations, e.g. wxImage::Scale()
+wxImage bilinearScale(const wxImage& img, int width, int height);
+
wxImage shrinkImage(const wxImage& img, int maxWidth /*optional*/, int maxHeight /*optional*/);
inline wxImage shrinkImage(const wxImage& img, int maxSize) { return shrinkImage(img, maxSize, maxSize); }
diff --git a/xBRZ/src/xbrz.cpp b/xBRZ/src/xbrz.cpp
index 50660b84..6c015aa1 100644
--- a/xBRZ/src/xbrz.cpp
+++ b/xBRZ/src/xbrz.cpp
@@ -32,7 +32,10 @@ uint32_t gradientRGB(uint32_t pixFront, uint32_t pixBack) //blend front color wi
{
static_assert(0 < M && M < N && N <= 1000);
- auto calcColor = [](unsigned char colFront, unsigned char colBack) -> unsigned char { return (colFront * M + colBack * (N - M)) / N; };
+ auto calcColor = [](unsigned char colFront, unsigned char colBack)
+ {
+ return static_cast<unsigned char>(uintDivRound(colFront * M + colBack * (N - M), N));
+ };
return makePixel(calcColor(getRed (pixFront), getRed (pixBack)),
calcColor(getGreen(pixFront), getGreen(pixBack)),
@@ -53,10 +56,10 @@ uint32_t gradientARGB(uint32_t pixFront, uint32_t pixBack) //find intermediate c
auto calcColor = [=](unsigned char colFront, unsigned char colBack)
{
- return static_cast<unsigned char>((colFront * weightFront + colBack * weightBack) / weightSum);
+ return static_cast<unsigned char>(uintDivRound(colFront * weightFront + colBack * weightBack, weightSum));
};
- return makePixel(static_cast<unsigned char>(weightSum / N),
+ return makePixel(static_cast<unsigned char>(uintDivRound(weightSum, N)),
calcColor(getRed (pixFront), getRed (pixBack)),
calcColor(getGreen(pixFront), getGreen(pixBack)),
calcColor(getBlue (pixFront), getBlue (pixBack)));
@@ -154,7 +157,7 @@ double distYCbCr(uint32_t pix1, uint32_t pix2, double lumaWeight)
{
//https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion
//YCbCr conversion is a matrix multiplication => take advantage of linearity by subtracting first!
- const int r_diff = static_cast<int>(getRed (pix1)) - getRed (pix2); //we may delay division by 255 to after matrix multiplication
+ const int r_diff = static_cast<int>(getRed (pix1)) - getRed (pix2); //defer division by 255 to after matrix multiplication
const int g_diff = static_cast<int>(getGreen(pix1)) - getGreen(pix2); //
const int b_diff = static_cast<int>(getBlue (pix1)) - getBlue (pix2); //substraction for int is noticeable faster than for double!
@@ -1094,23 +1097,23 @@ struct ColorDistanceARGB
{
const double a1 = getAlpha(pix1) / 255.0 ;
const double a2 = getAlpha(pix2) / 255.0 ;
- /*
- Requirements for a color distance handling alpha channel: with a1, a2 in [0, 1]
+
+ /* Requirements for a color distance handling alpha channel: with a1, a2 in [0, 1]
1. if a1 = a2, distance should be: a1 * distYCbCr()
2. if a1 = 0, distance should be: a2 * distYCbCr(black, white) = a2 * 255
3. if a1 = 1, ??? maybe: 255 * (1 - a2) + a2 * distYCbCr()
- */
- //return std::min(a1, a2) * distYCbCrBuffered(pix1, pix2) + 255 * abs(a1 - a2);
+ std::min(a1, a2) * distYCbCrBuffered(pix1, pix2) + 255 * abs(a1 - a2);
+
+ alternative? std::sqrt(a1 * a2 * square(distYCbCrBuffered(pix1, pix2)) + square(255 * (a1 - a2))); */
+
//=> following code is 15% faster:
const double d = distYCbCrBuffered(pix1, pix2);
if (a1 < a2)
return a1 * d + 255 * (a2 - a1);
else
return a2 * d + 255 * (a1 - a2);
-
- //alternative? return std::sqrt(a1 * a2 * square(distYCbCrBuffered(pix1, pix2)) + square(255 * (a1 - a2)));
}
};
@@ -1163,7 +1166,7 @@ void xbrz::scale(size_t factor, const uint32_t* src, uint32_t* trg, int srcWidth
switch (colFmt)
{
//*INDENT-OFF*
- case ColorFormat::RGB:
+ case ColorFormat::rgb:
switch (factor)
{
case 2: return scaleImage<Scaler2x<ColorGradientRGB>, ColorDistanceRGB, OobReaderDuplicate>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
@@ -1174,7 +1177,7 @@ void xbrz::scale(size_t factor, const uint32_t* src, uint32_t* trg, int srcWidth
}
break;
- case ColorFormat::ARGB:
+ case ColorFormat::argb:
switch (factor)
{
case 2: return scaleImage<Scaler2x<ColorGradientARGB>, ColorDistanceARGB, OobReaderTransparent>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
@@ -1185,7 +1188,7 @@ void xbrz::scale(size_t factor, const uint32_t* src, uint32_t* trg, int srcWidth
}
break;
- case ColorFormat::ARGB_UNBUFFERED:
+ case ColorFormat::argbUnbuffered:
switch (factor)
{
case 2: return scaleImage<Scaler2x<ColorGradientARGB>, ColorDistanceUnbufferedARGB, OobReaderTransparent>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
@@ -1205,11 +1208,11 @@ bool xbrz::equalColorTest(uint32_t col1, uint32_t col2, ColorFormat colFmt, doub
{
switch (colFmt)
{
- case ColorFormat::RGB:
+ case ColorFormat::rgb:
return ColorDistanceRGB::dist(col1, col2, luminanceWeight) < equalColorTolerance;
- case ColorFormat::ARGB:
+ case ColorFormat::argb:
return ColorDistanceARGB::dist(col1, col2, luminanceWeight) < equalColorTolerance;
- case ColorFormat::ARGB_UNBUFFERED:
+ case ColorFormat::argbUnbuffered:
return ColorDistanceUnbufferedARGB::dist(col1, col2, luminanceWeight) < equalColorTolerance;
}
assert(false);
@@ -1223,13 +1226,26 @@ void xbrz::bilinearScale(const uint32_t* src, int srcWidth, int srcHeight,
const auto imgReader = [src, srcWidth](int x, int y, BytePixel& pix)
{
static_assert(sizeof(pix) == sizeof(uint32_t));
- std::memcpy(pix, src + y * srcWidth + x, sizeof(pix));
+ const uint32_t pixSrc = src[y * srcWidth + x];
+
+ const unsigned char a = getAlpha(pixSrc);
+ pix[0] = a;
+ pix[1] = xbrz::premultiply(getRed (pixSrc), a); //r
+ pix[2] = xbrz::premultiply(getGreen(pixSrc), a); //g
+ pix[3] = xbrz::premultiply(getBlue (pixSrc), a); //b
};
- const auto imgWriter = [trg](const xbrz::BytePixel& pix) mutable { std::memcpy(trg++, pix, sizeof(pix)); };
+ const auto imgWriter = [trg](const xbrz::BytePixel& pix) mutable
+ {
+ const unsigned char a = pix[0];
+ * trg++ = makePixel(a,
+ xbrz::demultiply(pix[1], a), //r
+ xbrz::demultiply(pix[2], a), //g
+ xbrz::demultiply(pix[3], a)); //b
+ };
- bilinearScale(imgReader, srcWidth, srcHeight,
- imgWriter, trgWidth, trgHeight, 0, trgHeight);
+ bilinearScaleSimple(imgReader, srcWidth, srcHeight,
+ imgWriter, trgWidth, trgHeight, 0, trgHeight);
}
@@ -1262,8 +1278,8 @@ void bilinearScaleCpu(const uint32_t* src, int srcWidth, int srcHeight,
tg.run([=]
{
const int iLast = std::min(i + TASK_GRANULARITY, trgHeight);
- xbrz::bilinearScale(src, srcWidth, srcHeight, srcWidth * sizeof(uint32_t),
- trg, trgWidth, trgHeight, trgWidth * sizeof(uint32_t),
+ xbrz::bilinearScaleSimple(src, srcWidth, srcHeight, srcWidth * sizeof(uint32_t),
+ trg, trgWidth, trgHeight, trgWidth * sizeof(uint32_t),
i, iLast, [](uint32_t pix) { return pix; });
});
tg.wait();
diff --git a/xBRZ/src/xbrz.h b/xBRZ/src/xbrz.h
index c0778cf1..b3a496ba 100644
--- a/xBRZ/src/xbrz.h
+++ b/xBRZ/src/xbrz.h
@@ -26,26 +26,24 @@
namespace xbrz
{
-/*
--------------------------------------------------------------------------
-| xBRZ: "Scale by rules" - high quality image upscaling filter by Zenju |
--------------------------------------------------------------------------
-using a modified approach of xBR:
-http://board.byuu.org/viewtopic.php?f=10&t=2248
-- new rule set preserving small image features
-- highly optimized for performance
-- support alpha channel
-- support multithreading
-- support 64-bit architectures
-- support processing image slices
-- support scaling up to 6xBRZ
-*/
+/* -------------------------------------------------------------------------
+ | xBRZ: "Scale by rules" - high quality image upscaling filter by Zenju |
+ -------------------------------------------------------------------------
+ using a modified approach of xBR:
+ http://board.byuu.org/viewtopic.php?f=10&t=2248
+ - new rule set preserving small image features
+ - highly optimized for performance
+ - support alpha channel
+ - support multithreading
+ - support 64-bit architectures
+ - support processing image slices
+ - support scaling up to 6xBRZ */
enum class ColorFormat //from high bits -> low bits, 8 bit per channel
{
- RGB, //8 bit for each red, green, blue, upper 8 bits unused
- ARGB, //including alpha channel, BGRA byte order on little-endian machines
- ARGB_UNBUFFERED, //like ARGB, but without the one-time buffer creation overhead (ca. 100 - 300 ms) at the expense of a slightly slower scaling time
+ rgb, //8 bit for each red, green, blue, upper 8 bits unused
+ argb, //including alpha channel, BGRA byte order on little-endian machines
+ argbUnbuffered, //like ARGB, but without the one-time buffer creation overhead (ca. 100 - 300 ms) at the expense of a slightly slower scaling time
};
const int SCALE_FACTOR_MAX = 6;
@@ -66,6 +64,7 @@ void scale(size_t factor, //valid range: 2 - SCALE_FACTOR_MAX
const ScalerCfg& cfg = ScalerCfg(),
int yFirst = 0, int yLast = std::numeric_limits<int>::max()); //slice of source image
+//BGRA byte order
void bilinearScale(const uint32_t* src, int srcWidth, int srcHeight,
/**/ uint32_t* trg, int trgWidth, int trgHeight);
diff --git a/xBRZ/src/xbrz_tools.h b/xBRZ/src/xbrz_tools.h
index 98164678..d6e48c0c 100644
--- a/xBRZ/src/xbrz_tools.h
+++ b/xBRZ/src/xbrz_tools.h
@@ -87,10 +87,35 @@ void nearestNeighborScale(PixReader srcReader /* (int x, int y, BytePixel& pix)
}
+inline
+unsigned int uintDivRound(unsigned int num, unsigned int den)
+{
+ assert(den != 0);
+ return (num + den / 2) / den;
+}
+
+
+inline
+unsigned char premultiply(unsigned char c, unsigned char alpha)
+{
+ return static_cast<unsigned char>(uintDivRound(static_cast<unsigned int>(c) * alpha, 255));
+ //premultiply/demultiply using int div round is more accurate than int div floor/ceil pair
+}
+
+
+inline
+unsigned char demultiply(unsigned char c, unsigned char alpha)
+{
+ return static_cast<unsigned char>(alpha == 0 ? 0 :
+ std::clamp(uintDivRound(static_cast<unsigned int>(c) * 255, alpha), 0U, 255U));
+}
+
+
+//caveat: treats alpha channel like regular color! => caller needs to pre/de-multiply alpha!
template <class PixReader, class PixWriter>
-void bilinearScale(PixReader srcReader /* (int x, int y, BytePixel& pix) */, int srcWidth, int srcHeight,
- PixWriter trgWriter /* (const BytePixel& pix) */, int trgWidth, int trgHeight,
- int yFirst, int yLast)
+void bilinearScaleSimple(PixReader srcReader /* (int x, int y, BytePixel& pix) */, int srcWidth, int srcHeight,
+ PixWriter trgWriter /* (const BytePixel& pix) */, int trgWidth, int trgHeight,
+ int yFirst, int yLast)
{
yFirst = std::max(yFirst, 0);
yLast = std::min(yLast, trgHeight);
@@ -121,7 +146,7 @@ void bilinearScale(PixReader srcReader /* (int x, int y, BytePixel& pix) */, int
const double xx1 = x / scaleX - x1;
const double x2x = 1 - xx1;
- buf[x] = { x1, x2, xx1, x2x };
+ buf[x] = {x1, x2, xx1, x2x};
}
for (int y = yFirst; y < yLast; ++y)
diff --git a/zen/basic_math.h b/zen/basic_math.h
index a4feb83e..944a0f53 100644
--- a/zen/basic_math.h
+++ b/zen/basic_math.h
@@ -111,14 +111,14 @@ std::pair<InputIterator, InputIterator> minMaxElement(InputIterator first, Input
}
}
}
- return { lowest, largest };
+ return {lowest, largest};
}
template <class InputIterator> inline
std::pair<InputIterator, InputIterator> minMaxElement(InputIterator first, InputIterator last)
{
- return minMaxElement(first, last, std::less<typename std::iterator_traits<InputIterator>::value_type>());
+ return minMaxElement(first, last, std::less());
}
*/
@@ -152,10 +152,10 @@ template <class N, class D> inline
auto intDivRound(N num, D den)
{
using namespace zen;
- static_assert(IsInteger<N>::value && IsInteger<D>::value);
- static_assert(IsSignedInt<N>::value == IsSignedInt<D>::value); //until further
+ static_assert(IsIntegerV<N>&& IsIntegerV<D>);
+ static_assert(IsSignedIntV<N> == IsSignedIntV<D>); //until further
assert(den != 0);
- if constexpr (IsSignedInt<N>::value)
+ if constexpr (IsSignedIntV<N>)
{
if ((num < 0) != (den < 0))
return (num - den / 2) / den;
@@ -168,10 +168,10 @@ template <class N, class D> inline
auto intDivCeil(N num, D den)
{
using namespace zen;
- static_assert(IsInteger<N>::value && IsInteger<D>::value);
- static_assert(IsSignedInt<N>::value == IsSignedInt<D>::value); //until further
+ static_assert(IsIntegerV<N>&& IsIntegerV<D>);
+ static_assert(IsSignedIntV<N> == IsSignedIntV<D>); //until further
assert(den != 0);
- if constexpr (IsSignedInt<N>::value)
+ if constexpr (IsSignedIntV<N>)
{
if ((num < 0) != (den < 0))
return num / den;
@@ -187,10 +187,10 @@ template <class N, class D> inline
auto intDivFloor(N num, D den)
{
using namespace zen;
- static_assert(IsInteger<N>::value && IsInteger<D>::value);
- static_assert(IsSignedInt<N>::value == IsSignedInt<D>::value); //until further
+ static_assert(IsIntegerV<N>&& IsIntegerV<D>);
+ static_assert(IsSignedIntV<N> == IsSignedIntV<D>); //until further
assert(den != 0);
- if constexpr (IsSignedInt<N>::value)
+ if constexpr (IsSignedIntV<N>)
{
if ((num < 0) != (den < 0))
{
diff --git a/zen/dir_watcher.cpp b/zen/dir_watcher.cpp
index dc416b34..191ffd64 100644
--- a/zen/dir_watcher.cpp
+++ b/zen/dir_watcher.cpp
@@ -9,7 +9,6 @@
#include <set>
#include "thread.h"
#include "scope_guard.h"
-//#include "basic_math.h"
#include <map>
#include <sys/inotify.h>
@@ -34,7 +33,7 @@ DirWatcher::DirWatcher(const Zstring& dirPath) : //throw FileError
pimpl_(std::make_unique<Impl>())
{
//get all subdirectories
- std::vector<Zstring> fullFolderList { baseDirPath_ };
+ std::vector<Zstring> fullFolderList {baseDirPath_};
{
std::function<void (const Zstring& path)> traverse;
@@ -102,7 +101,7 @@ DirWatcher::~DirWatcher()
std::vector<DirWatcher::Change> DirWatcher::fetchChanges(const std::function<void()>& requestUiUpdate, std::chrono::milliseconds cbInterval) //throw FileError
{
- std::vector<std::byte> buffer(512 * (sizeof(struct ::inotify_event) + NAME_MAX + 1));
+ std::vector<std::byte> buffer(512 * (sizeof(inotify_event) + NAME_MAX + 1));
ssize_t bytesRead = 0;
do
@@ -125,7 +124,7 @@ std::vector<DirWatcher::Change> DirWatcher::fetchChanges(const std::function<voi
ssize_t bytePos = 0;
while (bytePos < bytesRead)
{
- struct ::inotify_event& evt = reinterpret_cast<struct ::inotify_event&>(buffer[bytePos]);
+ inotify_event& evt = reinterpret_cast<inotify_event&>(buffer[bytePos]);
if (evt.len != 0) //exclude case: deletion of "self", already reported by parent directory watch
{
@@ -138,18 +137,18 @@ std::vector<DirWatcher::Change> DirWatcher::fetchChanges(const std::function<voi
if ((evt.mask & IN_CREATE) ||
(evt.mask & IN_MOVED_TO))
- output.push_back({ ChangeType::create, itemPath });
+ output.push_back({ChangeType::create, itemPath});
else if ((evt.mask & IN_MODIFY) ||
(evt.mask & IN_CLOSE_WRITE))
- output.push_back({ ChangeType::update, itemPath });
+ output.push_back({ChangeType::update, itemPath});
else if ((evt.mask & IN_DELETE ) ||
(evt.mask & IN_DELETE_SELF) ||
(evt.mask & IN_MOVE_SELF ) ||
(evt.mask & IN_MOVED_FROM))
- output.push_back({ ChangeType::remove, itemPath });
+ output.push_back({ChangeType::remove, itemPath});
}
}
- bytePos += sizeof(struct ::inotify_event) + evt.len;
+ bytePos += sizeof(inotify_event) + evt.len;
}
return output;
diff --git a/zen/error_log.h b/zen/error_log.h
index 6d9f80ae..a24dfe5a 100644
--- a/zen/error_log.h
+++ b/zen/error_log.h
@@ -68,7 +68,7 @@ private:
inline
void ErrorLog::logMsg(const std::wstring& msg, MessageType type)
{
- entries_.push_back({ std::time(nullptr), type, utfTo<Zstringc>(msg) });
+ entries_.push_back({std::time(nullptr), type, utfTo<Zstringc>(msg)});
}
diff --git a/zen/file_access.cpp b/zen/file_access.cpp
index 4c9af652..fb770f19 100644
--- a/zen/file_access.cpp
+++ b/zen/file_access.cpp
@@ -12,7 +12,6 @@
#include "file_traverser.h"
#include "scope_guard.h"
#include "symlink_target.h"
-#include "file_id_def.h"
#include "file_io.h"
#include "crc.h"
#include "guid.h"
@@ -45,7 +44,7 @@ std::optional<PathComponents> zen::parsePathComponents(const Zstring& itemPath)
Zstring relPath(it + 1, itemPathFmt.end());
trim(relPath, true, true, [](Zchar c) { return c == FILE_NAME_SEPARATOR; });
- return PathComponents({ rootPath, relPath });
+ return PathComponents({rootPath, relPath});
}
return {};
};
@@ -100,7 +99,7 @@ std::optional<Zstring> zen::getParentFolderPath(const Zstring& itemPath)
ItemType zen::getItemType(const Zstring& itemPath) //throw FileError
{
- struct ::stat itemInfo = {};
+ struct stat itemInfo = {};
if (::lstat(itemPath.c_str(), &itemInfo) != 0)
THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(itemPath)), "lstat");
@@ -153,7 +152,7 @@ std::optional<ItemType> zen::itemStillExists(const Zstring& itemPath) //throw Fi
bool zen::fileAvailable(const Zstring& filePath) //noexcept
{
//symbolic links (broken or not) are also treated as existing files!
- struct ::stat fileInfo = {};
+ struct stat fileInfo = {};
if (::stat(filePath.c_str(), &fileInfo) == 0) //follow symlinks!
return S_ISREG(fileInfo.st_mode);
return false;
@@ -163,7 +162,7 @@ bool zen::fileAvailable(const Zstring& filePath) //noexcept
bool zen::dirAvailable(const Zstring& dirPath) //noexcept
{
//symbolic links (broken or not) are also treated as existing directories!
- struct ::stat dirInfo = {};
+ struct stat dirInfo = {};
if (::stat(dirPath.c_str(), &dirInfo) == 0) //follow symlinks!
return S_ISDIR(dirInfo.st_mode);
return false;
@@ -177,29 +176,22 @@ namespace
int64_t zen::getFreeDiskSpace(const Zstring& path) //throw FileError, returns < 0 if not available
{
- struct ::statfs info = {};
- if (::statfs(path.c_str(), &info) != 0)
+ struct statfs info = {};
+ if (::statfs(path.c_str(), &info) != 0) //follows symlinks!
THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot determine free disk space for %x."), L"%x", fmtPath(path)), "statfs");
+ //Linux: "Fields that are undefined for a particular file system are set to 0."
+ //macOS: "Fields that are undefined for a particular file system are set to -1." - mkay :>
+ if (makeSigned(info.f_bsize) <= 0 ||
+ makeSigned(info.f_bavail) <= 0)
+ return -1;
return static_cast<int64_t>(info.f_bsize) * info.f_bavail;
}
-VolumeId zen::getVolumeId(const Zstring& itemPath) //throw FileError
-{
- struct ::stat fileInfo = {};
- if (::stat(itemPath.c_str(), &fileInfo) != 0)
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(itemPath)), "stat");
-
- warn_static("NOT STABLE!")
-
- return fileInfo.st_dev;
-}
-
-
uint64_t zen::getFileSize(const Zstring& filePath) //throw FileError
{
- struct ::stat fileInfo = {};
+ struct stat fileInfo = {};
if (::stat(filePath.c_str(), &fileInfo) != 0)
THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(filePath)), "stat");
@@ -207,6 +199,8 @@ uint64_t zen::getFileSize(const Zstring& filePath) //throw FileError
}
+
+
Zstring zen::getTempFolderPath() //throw FileError
{
if (const char* tempPath = ::getenv("TMPDIR")) //no extended error reporting
@@ -336,15 +330,15 @@ void moveAndRenameFileSub(const Zstring& pathFrom, const Zstring& pathTo, bool r
//macOS: no solution https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man2/rename.2.html
if (!replaceExisting)
{
- struct ::stat infoSrc = {};
- if (::lstat(pathFrom.c_str(), &infoSrc) != 0)
+ struct stat sourceInfo = {};
+ if (::lstat(pathFrom.c_str(), &sourceInfo) != 0)
THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(pathFrom)), "stat");
- struct ::stat infoTrg = {};
- if (::lstat(pathTo.c_str(), &infoTrg) == 0)
+ struct stat targetInfo = {};
+ if (::lstat(pathTo.c_str(), &targetInfo) == 0)
{
- if (infoSrc.st_dev != infoTrg.st_dev ||
- infoSrc.st_ino != infoTrg.st_ino)
+ if (sourceInfo.st_dev != targetInfo.st_dev ||
+ sourceInfo.st_ino != targetInfo.st_ino)
throwException(EEXIST); //that's what we're really here for
//else: continue with a rename in case
//caveat: if we have a hardlink referenced by two different paths, the source one will be unlinked => fine, but not exactly a "rename"...
@@ -376,7 +370,7 @@ void zen::moveAndRenameItem(const Zstring& pathFrom, const Zstring& pathTo, bool
namespace
{
-void setWriteTimeNative(const Zstring& itemPath, const struct ::timespec& modTime, ProcSymlink procSl) //throw FileError
+void setWriteTimeNative(const Zstring& itemPath, const timespec& modTime, ProcSymlink procSl) //throw FileError
{
/*
[2013-05-01] sigh, we can't use utimensat() on NTFS volumes on Ubuntu: silent failure!!! what morons are programming this shit???
@@ -388,12 +382,12 @@ void setWriteTimeNative(const Zstring& itemPath, const struct ::timespec& modTim
=> let's give utimensat another chance:
using open()/futimens() for regular files and utimensat(AT_SYMLINK_NOFOLLOW) for symlinks is consistent with "cp" and "touch"!
*/
- struct ::timespec newTimes[2] = {};
+ timespec newTimes[2] = {};
newTimes[0].tv_sec = ::time(nullptr); //access time; using UTIME_OMIT for tv_nsec would trigger even more bugs: https://freefilesync.org/forum/viewtopic.php?t=1701
newTimes[1] = modTime; //modification time
//test: even modTime == 0 is correctly applied (no NOOP!) test2: same behavior for "utime()"
- if (procSl == ProcSymlink::FOLLOW)
+ if (procSl == ProcSymlink::follow)
{
//hell knows why files on gvfs-mounted Samba shares fail to open(O_WRONLY) returning EOPNOTSUPP:
//https://freefilesync.org/forum/viewtopic.php?t=2803 => utimensat() works (but not for gvfs SFTP)
@@ -422,10 +416,8 @@ void setWriteTimeNative(const Zstring& itemPath, const struct ::timespec& modTim
void zen::setFileTime(const Zstring& filePath, time_t modTime, ProcSymlink procSl) //throw FileError
{
- struct ::timespec writeTime = {};
- writeTime.tv_sec = modTime;
- setWriteTimeNative(filePath, writeTime, procSl); //throw FileError
-
+ setWriteTimeNative(filePath, timetToNativeFileTime(modTime),
+ procSl); //throw FileError
}
@@ -442,7 +434,7 @@ namespace
void copySecurityContext(const Zstring& source, const Zstring& target, ProcSymlink procSl) //throw FileError
{
security_context_t contextSource = nullptr;
- const int rv = procSl == ProcSymlink::FOLLOW ?
+ const int rv = procSl == ProcSymlink::follow ?
::getfilecon (source.c_str(), &contextSource) :
::lgetfilecon(source.c_str(), &contextSource);
if (rv < 0)
@@ -457,7 +449,7 @@ void copySecurityContext(const Zstring& source, const Zstring& target, ProcSymli
{
security_context_t contextTarget = nullptr;
- const int rv2 = procSl == ProcSymlink::FOLLOW ?
+ const int rv2 = procSl == ProcSymlink::follow ?
::getfilecon(target.c_str(), &contextTarget) :
::lgetfilecon(target.c_str(), &contextTarget);
if (rv2 < 0)
@@ -475,7 +467,7 @@ void copySecurityContext(const Zstring& source, const Zstring& target, ProcSymli
}
}
- const int rv3 = procSl == ProcSymlink::FOLLOW ?
+ const int rv3 = procSl == ProcSymlink::follow ?
::setfilecon(target.c_str(), contextSource) :
::lsetfilecon(target.c_str(), contextSource);
if (rv3 < 0)
@@ -493,8 +485,8 @@ void zen::copyItemPermissions(const Zstring& sourcePath, const Zstring& targetPa
copySecurityContext(sourcePath, targetPath, procSl); //throw FileError
#endif
- struct ::stat fileInfo = {};
- if (procSl == ProcSymlink::FOLLOW)
+ struct stat fileInfo = {};
+ if (procSl == ProcSymlink::follow)
{
if (::stat(sourcePath.c_str(), &fileInfo) != 0)
THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtPath(sourcePath)), "stat");
@@ -614,22 +606,22 @@ void zen::copySymlink(const Zstring& sourcePath, const Zstring& targetPath) //th
catch (FileError&) {});
//file times: essential for syncing a symlink: enforce this! (don't just try!)
- struct ::stat sourceInfo = {};
+ struct stat sourceInfo = {};
if (::lstat(sourcePath.c_str(), &sourceInfo) != 0)
THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(sourcePath)), "lstat");
- setWriteTimeNative(targetPath, sourceInfo.st_mtim, ProcSymlink::DIRECT); //throw FileError
+ setWriteTimeNative(targetPath, sourceInfo.st_mtim, ProcSymlink::direct); //throw FileError
}
FileCopyResult zen::copyNewFile(const Zstring& sourceFile, const Zstring& targetFile, //throw FileError, ErrorTargetExisting, (ErrorFileLocked), X
- const IOCallback& notifyUnbufferedIO /*throw X*/)
+ const IoCallback& notifyUnbufferedIO /*throw X*/)
{
int64_t totalUnbufferedIO = 0;
FileInput fileIn(sourceFile, IOCallbackDivider(notifyUnbufferedIO, totalUnbufferedIO)); //throw FileError, (ErrorFileLocked -> Windows-only)
- struct ::stat sourceInfo = {};
+ struct stat sourceInfo = {};
if (::fstat(fileIn.getHandle(), &sourceInfo) != 0)
THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(sourceFile)), "fstat");
@@ -637,7 +629,7 @@ FileCopyResult zen::copyNewFile(const Zstring& sourceFile, const Zstring& target
//it seems we don't need S_IWUSR, not even for the setFileTime() below! (tested with source file having different user/group!)
//=> need copyItemPermissions() only for "chown" and umask-agnostic permissions
- const int fdTarget = ::open(targetFile.c_str(), O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode);
+ const int fdTarget = ::open(targetFile.c_str(), O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, mode);
if (fdTarget == -1)
{
const int ec = errno; //copy before making other system calls!
@@ -659,7 +651,7 @@ FileCopyResult zen::copyNewFile(const Zstring& sourceFile, const Zstring& target
//flush intermediate buffers before fiddling with the raw file handle
fileOut.flushBuffers(); //throw FileError, X
- struct ::stat targetInfo = {};
+ struct stat targetInfo = {};
if (::fstat(fileOut.getHandle(), &targetInfo) != 0)
THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(targetFile)), "fstat");
@@ -673,12 +665,12 @@ FileCopyResult zen::copyNewFile(const Zstring& sourceFile, const Zstring& target
std::optional<FileError> errorModTime;
try
{
- //we cannot set the target file times (::futimes) while the file descriptor is still open after a write operation:
- //this triggers bugs on samba shares where the modification time is set to current time instead.
- //Linux: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=340236
- // http://comments.gmane.org/gmane.linux.file-systems.cifs/2854
- //OS X: https://freefilesync.org/forum/viewtopic.php?t=356
- setWriteTimeNative(targetFile, sourceInfo.st_mtim, ProcSymlink::FOLLOW); //throw FileError
+ /* we cannot set the target file times (::futimes) while the file descriptor is still open after a write operation:
+ this triggers bugs on Samba shares where the modification time is set to current time instead.
+ Linux: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=340236
+ http://comments.gmane.org/gmane.linux.file-systems.cifs/2854
+ macOS: https://freefilesync.org/forum/viewtopic.php?t=356 */
+ setWriteTimeNative(targetFile, sourceInfo.st_mtim, ProcSymlink::follow); //throw FileError
}
catch (const FileError& e)
{
@@ -687,9 +679,9 @@ FileCopyResult zen::copyNewFile(const Zstring& sourceFile, const Zstring& target
FileCopyResult result;
result.fileSize = sourceInfo.st_size;
- result.modTime = sourceInfo.st_mtim.tv_sec; //
- result.sourceFileId = generateFileId(sourceInfo);
- result.targetFileId = generateFileId(targetInfo);
+ result.sourceModTime = sourceInfo.st_mtim;
+ result.sourceFileIdx = sourceInfo.st_ino;
+ result.targetFileIdx = targetInfo.st_ino;
result.errorModTime = errorModTime;
return result;
}
diff --git a/zen/file_access.h b/zen/file_access.h
index a3fa56d7..f3ea6c00 100644
--- a/zen/file_access.h
+++ b/zen/file_access.h
@@ -10,9 +10,8 @@
#include <functional>
#include "zstring.h"
#include "file_error.h"
-#include "file_id_def.h"
-#include "serialize.h"
-
+#include "serialize.h" //IoCallback
+ #include <sys/stat.h>
namespace zen
{
@@ -31,6 +30,21 @@ std::optional<Zstring> getParentFolderPath(const Zstring& itemPath);
bool fileAvailable(const Zstring& filePath); //noexcept
bool dirAvailable (const Zstring& dirPath ); //
+//FAT/FAT32: "Why does the timestamp of a file *increase* by up to 2 seconds when I copy it to a USB thumb drive?"
+const int FAT_FILE_TIME_PRECISION_SEC = 2; //https://devblogs.microsoft.com/oldnewthing/?p=83
+//https://web.archive.org/web/20141127143832/http://support.microsoft.com/kb/127830
+
+using FileIndex = ino_t;
+using FileTimeNative = timespec;
+
+inline time_t nativeFileTimeToTimeT(const timespec& ft) { return ft.tv_sec; } //follow Windows Explorer and always round down!
+inline timespec timetToNativeFileTime(time_t utcTime)
+{
+ timespec natTime = {};
+ natTime.tv_sec = utcTime;
+ return natTime;
+}
+
enum class ItemType
{
file,
@@ -47,14 +61,13 @@ std::optional<ItemType> itemStillExists(const Zstring& itemPath); //throw FileEr
enum class ProcSymlink
{
- DIRECT,
- FOLLOW
+ direct,
+ follow
};
void setFileTime(const Zstring& filePath, time_t modTime, ProcSymlink procSl); //throw FileError
//symlink handling: follow
int64_t getFreeDiskSpace(const Zstring& path); //throw FileError, returns < 0 if not available
-VolumeId getVolumeId(const Zstring& itemPath); //throw FileError
uint64_t getFileSize(const Zstring& filePath); //throw FileError
//get per-user directory designated for temporary files:
@@ -87,16 +100,15 @@ void copySymlink(const Zstring& sourcePath, const Zstring& targetPath); //throw
struct FileCopyResult
{
uint64_t fileSize = 0;
- time_t modTime = 0; //number of seconds since Jan. 1st 1970 UTC
- FileId sourceFileId;
- FileId targetFileId;
+ FileTimeNative sourceModTime = {};
+ FileIndex sourceFileIdx = 0;
+ FileIndex targetFileIdx = 0;
std::optional<FileError> errorModTime; //failure to set modification time
};
FileCopyResult copyNewFile(const Zstring& sourceFile, const Zstring& targetFile, //throw FileError, ErrorTargetExisting, ErrorFileLocked, X
//accummulated delta != file size! consider ADS, sparse, compressed files
- const IOCallback& notifyUnbufferedIO /*throw X*/);
-
+ const IoCallback& notifyUnbufferedIO /*throw X*/);
}
#endif //FILE_ACCESS_H_8017341345614857
diff --git a/zen/file_id_def.h b/zen/file_id_def.h
deleted file mode 100644
index d2d104d5..00000000
--- a/zen/file_id_def.h
+++ /dev/null
@@ -1,46 +0,0 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef FILE_ID_DEF_H_013287632486321493
-#define FILE_ID_DEF_H_013287632486321493
-
- #include <sys/stat.h>
-
-
-namespace zen
-{
-namespace impl { typedef struct ::stat StatDummy; } //sigh...
-
-using VolumeId = decltype(impl::StatDummy::st_dev);
-using FileIndex = decltype(impl::StatDummy::st_ino);
-
-
-struct FileId //always available on Linux, and *generally* available on Windows)
-{
- FileId() {}
- FileId(VolumeId volId, FileIndex fIdx) : volumeId(volId), fileIndex(fIdx)
- {
- if (volId == 0 || fIdx == 0)
- {
- volumeId = 0;
- fileIndex = 0;
- }
- }
- VolumeId volumeId = 0;
- FileIndex fileIndex = 0;
-
- bool operator==(const FileId&) const = default;
-};
-
-
-inline
-FileId generateFileId(const struct ::stat& fileInfo)
-{
- return FileId(fileInfo.st_dev, fileInfo.st_ino);
-}
-}
-
-#endif //FILE_ID_DEF_H_013287632486321493
diff --git a/zen/file_io.cpp b/zen/file_io.cpp
index e081335d..f575a366 100644
--- a/zen/file_io.cpp
+++ b/zen/file_io.cpp
@@ -41,7 +41,7 @@ namespace
FileBase::FileHandle openHandleForRead(const Zstring& filePath) //throw FileError, ErrorFileLocked
{
//caveat: check for file types that block during open(): character device, block device, named pipe
- struct ::stat fileInfo = {};
+ struct stat fileInfo = {};
if (::stat(filePath.c_str(), &fileInfo) == 0) //follows symlinks
{
if (!S_ISREG(fileInfo.st_mode) &&
@@ -74,11 +74,11 @@ FileBase::FileHandle openHandleForRead(const Zstring& filePath) //throw FileErro
}
-FileInput::FileInput(FileHandle handle, const Zstring& filePath, const IOCallback& notifyUnbufferedIO) :
+FileInput::FileInput(FileHandle handle, const Zstring& filePath, const IoCallback& notifyUnbufferedIO) :
FileBase(handle, filePath), notifyUnbufferedIO_(notifyUnbufferedIO) {}
-FileInput::FileInput(const Zstring& filePath, const IOCallback& notifyUnbufferedIO) :
+FileInput::FileInput(const Zstring& filePath, const IoCallback& notifyUnbufferedIO) :
FileBase(openHandleForRead(filePath), filePath), //throw FileError, ErrorFileLocked
notifyUnbufferedIO_(notifyUnbufferedIO)
{
@@ -166,8 +166,13 @@ FileBase::FileHandle openHandleForWrite(const Zstring& filePath) //throw FileErr
{
//checkForUnsupportedType(filePath); -> not needed, open() + O_WRONLY should fail fast
- const int fdFile = ::open(filePath.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC | /*access == FileOutput::ACC_OVERWRITE ? O_TRUNC : */ O_EXCL,
- S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); //0666 => umask will be applied implicitly!
+ const mode_t lockFileMode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; //0666 => umask will be applied implicitly!
+
+ //O_EXCL contains a race condition on NFS file systems: https://linux.die.net/man/2/open
+ const int fdFile = ::open(filePath.c_str(), //const char* pathname
+ O_CREAT | //int flags
+ /*access == FileOutput::ACC_OVERWRITE ? O_TRUNC : */ O_EXCL | O_WRONLY | O_CLOEXEC,
+ lockFileMode); //mode_t mode
if (fdFile == -1)
{
const int ec = errno; //copy before making other system calls!
@@ -185,13 +190,13 @@ FileBase::FileHandle openHandleForWrite(const Zstring& filePath) //throw FileErr
}
-FileOutput::FileOutput(FileHandle handle, const Zstring& filePath, const IOCallback& notifyUnbufferedIO) :
+FileOutput::FileOutput(FileHandle handle, const Zstring& filePath, const IoCallback& notifyUnbufferedIO) :
FileBase(handle, filePath), notifyUnbufferedIO_(notifyUnbufferedIO)
{
}
-FileOutput::FileOutput(const Zstring& filePath, const IOCallback& notifyUnbufferedIO) :
+FileOutput::FileOutput(const Zstring& filePath, const IoCallback& notifyUnbufferedIO) :
FileBase(openHandleForWrite(filePath), filePath), notifyUnbufferedIO_(notifyUnbufferedIO) {} //throw FileError, ErrorTargetExisting
@@ -298,8 +303,8 @@ void FileOutput::reserveSpace(uint64_t expectedSize) //throw FileError
//don't use ::posix_fallocate which uses horribly inefficient fallback if FS doesn't support it (EOPNOTSUPP) and changes files size!
//FALLOC_FL_KEEP_SIZE => allocate only, file size is NOT changed!
- if (::fallocate(getHandle(), //int fd,
- FALLOC_FL_KEEP_SIZE, //int mode,
+ if (::fallocate(getHandle(), //int fd
+ FALLOC_FL_KEEP_SIZE, //int mode
0, //off_t offset
expectedSize) != 0) //off_t len
if (errno != EOPNOTSUPP) //possible, unlike with posix_fallocate()
@@ -308,19 +313,19 @@ void FileOutput::reserveSpace(uint64_t expectedSize) //throw FileError
}
-std::string zen::getFileContent(const Zstring& filePath, const IOCallback& notifyUnbufferedIO /*throw X*/) //throw FileError, X
+std::string zen::getFileContent(const Zstring& filePath, const IoCallback& notifyUnbufferedIO /*throw X*/) //throw FileError, X
{
FileInput streamIn(filePath, notifyUnbufferedIO); //throw FileError, ErrorFileLocked
return bufferedLoad<std::string>(streamIn); //throw FileError, X
}
-void zen::setFileContent(const Zstring& filePath, const std::string& byteStream, const IOCallback& notifyUnbufferedIO /*throw X*/) //throw FileError, X
+void zen::setFileContent(const Zstring& filePath, const std::string& byteStream, const IoCallback& notifyUnbufferedIO /*throw X*/) //throw FileError, X
{
TempFileOutput fileOut(filePath, notifyUnbufferedIO); //throw FileError
if (!byteStream.empty())
{
- //preallocate disk space + reduce fragmentation
+ //preallocate disk space & reduce fragmentation
fileOut.reserveSpace(byteStream.size()); //throw FileError
fileOut.write(&byteStream[0], byteStream.size()); //throw FileError, X
}
diff --git a/zen/file_io.h b/zen/file_io.h
index a7385241..3d1dfee7 100644
--- a/zen/file_io.h
+++ b/zen/file_io.h
@@ -32,8 +32,7 @@ public:
FileHandle getHandle() { return hFile_; }
//Windows: use 64kB ?? https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-2000-server/cc938632%28v=technet.10%29
- //Linux: use st_blksize?
- //macOS: use f_iosize?
+ //macOS, Linux: use st_blksize?
static size_t getBlockSize() { return 128 * 1024; };
const Zstring& getFilePath() const { return filePath_; }
@@ -57,15 +56,15 @@ private:
class FileInput : public FileBase
{
public:
- FileInput( const Zstring& filePath, const IOCallback& notifyUnbufferedIO /*throw X*/); //throw FileError, ErrorFileLocked
- FileInput(FileHandle handle, const Zstring& filePath, const IOCallback& notifyUnbufferedIO /*throw X*/); //takes ownership!
+ FileInput( const Zstring& filePath, const IoCallback& notifyUnbufferedIO /*throw X*/); //throw FileError, ErrorFileLocked
+ FileInput(FileHandle handle, const Zstring& filePath, const IoCallback& notifyUnbufferedIO /*throw X*/); //takes ownership!
size_t read(void* buffer, size_t bytesToRead); //throw FileError, ErrorFileLocked, X; return "bytesToRead" bytes unless end of stream!
private:
size_t tryRead(void* buffer, size_t bytesToRead); //throw FileError, ErrorFileLocked; may return short, only 0 means EOF! => CONTRACT: bytesToRead > 0!
- const IOCallback notifyUnbufferedIO_; //throw X
+ const IoCallback notifyUnbufferedIO_; //throw X
std::vector<std::byte> memBuf_ = std::vector<std::byte>(getBlockSize());
size_t bufPos_ = 0;
@@ -76,8 +75,8 @@ private:
class FileOutput : public FileBase
{
public:
- FileOutput( const Zstring& filePath, const IOCallback& notifyUnbufferedIO /*throw X*/); //throw FileError, ErrorTargetExisting
- FileOutput(FileHandle handle, const Zstring& filePath, const IOCallback& notifyUnbufferedIO /*throw X*/); //takes ownership!
+ FileOutput( const Zstring& filePath, const IoCallback& notifyUnbufferedIO /*throw X*/); //throw FileError, ErrorTargetExisting
+ FileOutput(FileHandle handle, const Zstring& filePath, const IoCallback& notifyUnbufferedIO /*throw X*/); //takes ownership!
~FileOutput();
void reserveSpace(uint64_t expectedSize); //throw FileError
@@ -91,7 +90,7 @@ public:
private:
size_t tryWrite(const void* buffer, size_t bytesToWrite); //throw FileError; may return short! CONTRACT: bytesToWrite > 0
- IOCallback notifyUnbufferedIO_; //throw X
+ IoCallback notifyUnbufferedIO_; //throw X
std::vector<std::byte> memBuf_ = std::vector<std::byte>(getBlockSize());
size_t bufPos_ = 0;
size_t bufPosEnd_ = 0;
@@ -102,7 +101,7 @@ private:
class TempFileOutput
{
public:
- TempFileOutput( const Zstring& filePath, const IOCallback& notifyUnbufferedIO /*throw X*/) : //throw FileError
+ TempFileOutput( const Zstring& filePath, const IoCallback& notifyUnbufferedIO /*throw X*/) : //throw FileError
filePath_(filePath),
tmpFile_(tmpFilePath_, notifyUnbufferedIO) {} //throw FileError, (ErrorTargetExisting)
@@ -110,7 +109,7 @@ public:
void write(const void* buffer, size_t bytesToWrite) { tmpFile_.write(buffer, bytesToWrite); } //throw FileError, X
- FileOutput& refTempFile() { return tmpFile_; }
+ FileOutput& refTempFile() { return tmpFile_; }
void commit() //throw FileError, X
{
@@ -133,10 +132,10 @@ private:
};
-[[nodiscard]] std::string getFileContent(const Zstring& filePath, const IOCallback& notifyUnbufferedIO /*throw X*/); //throw FileError, X
+[[nodiscard]] std::string getFileContent(const Zstring& filePath, const IoCallback& notifyUnbufferedIO /*throw X*/); //throw FileError, X
//overwrites if existing + transactional! :)
-void setFileContent(const Zstring& filePath, const std::string& bytes, const IOCallback& notifyUnbufferedIO /*throw X*/); //throw FileError, X
+void setFileContent(const Zstring& filePath, const std::string& bytes, const IoCallback& notifyUnbufferedIO /*throw X*/); //throw FileError, X
}
#endif //FILE_IO_H_89578342758342572345
diff --git a/zen/file_traverser.cpp b/zen/file_traverser.cpp
index aa48cb85..f1b5519b 100644
--- a/zen/file_traverser.cpp
+++ b/zen/file_traverser.cpp
@@ -31,7 +31,7 @@ void zen::traverseFolder(const Zstring& dirPath,
for (;;)
{
errno = 0;
- const struct ::dirent* dirEntry = ::readdir(folder); //don't use readdir_r(), see comment in native.cpp
+ const dirent* dirEntry = ::readdir(folder); //don't use readdir_r(), see comment in native.cpp
if (!dirEntry)
{
if (errno == 0) //errno left unchanged => no more items
@@ -54,7 +54,7 @@ void zen::traverseFolder(const Zstring& dirPath,
const Zstring& itemPath = appendSeparator(dirPath) + itemName;
- struct ::stat statData = {};
+ struct stat statData = {};
try
{
if (::lstat(itemPath.c_str(), &statData) != 0) //lstat() does not resolve symlinks
@@ -75,12 +75,12 @@ void zen::traverseFolder(const Zstring& dirPath,
else if (S_ISDIR(statData.st_mode)) //a directory
{
if (onFolder)
- onFolder({ itemName, itemPath });
+ onFolder({itemName, itemPath});
}
else //a file or named pipe, etc.
{
if (onFile)
- onFile({ itemName, itemPath, makeUnsigned(statData.st_size), statData.st_mtime });
+ onFile({itemName, itemPath, makeUnsigned(statData.st_size), statData.st_mtime});
}
/* It may be a good idea to not check "S_ISREG(statData.st_mode)" explicitly and to not issue an error message on other types to support these scenarios:
diff --git a/zen/format_unit.cpp b/zen/format_unit.cpp
index 28943de7..b2d1b59a 100644
--- a/zen/format_unit.cpp
+++ b/zen/format_unit.cpp
@@ -128,9 +128,9 @@ std::wstring roundToBlock(double timeInHigh,
std::wstring zen::formatRemainingTime(double timeInSec)
{
- const int steps10[] = { 1, 2, 5, 10 };
- const int steps24[] = { 1, 2, 3, 4, 6, 8, 12, 24 };
- const int steps60[] = { 1, 2, 5, 10, 15, 20, 30, 60 };
+ const int steps10[] = {1, 2, 5, 10};
+ const int steps24[] = {1, 2, 3, 4, 6, 8, 12, 24};
+ const int steps60[] = {1, 2, 5, 10, 15, 20, 30, 60};
//determine preferred unit
double timeInUnit = timeInSec;
@@ -178,7 +178,7 @@ std::wstring zen::formatUtcToLocalTime(time_t utcTime)
{
auto errorMsg = [&] { return _("Error") + L" (time_t: " + numberTo<std::wstring>(utcTime) + L')'; };
- TimeComp loc = getLocalTime(utcTime);
+ const TimeComp& loc = getLocalTime(utcTime);
std::wstring dateString = utfTo<std::wstring>(formatTime(Zstr("%x %X"), loc));
return !dateString.empty() ? dateString : errorMsg();
diff --git a/zen/http.cpp b/zen/http.cpp
index 5d389719..f8fb24a3 100644
--- a/zen/http.cpp
+++ b/zen/http.cpp
@@ -23,7 +23,7 @@ public:
bool disableGetCache /*not relevant for POST (= never cached)*/,
const Zstring& userAgent,
const Zstring* caCertFilePath /*optional: enable certificate validation*/,
- const IOCallback& notifyUnbufferedIO) : //throw SysError, X
+ const IoCallback& notifyUnbufferedIO) : //throw SysError, X
notifyUnbufferedIO_(notifyUnbufferedIO)
{
ZEN_ON_SCOPE_FAIL(cleanup(); /*destructor call would lead to member double clean-up!!!*/);
@@ -214,7 +214,7 @@ private:
int64_t contentRemaining_ = -1; //consider "Content-Length" if available
- const IOCallback notifyUnbufferedIO_; //throw X
+ const IoCallback notifyUnbufferedIO_; //throw X
std::vector<std::byte> memBuf_ = std::vector<std::byte>(getBlockSize());
size_t bufPos_ = 0; //buffered I/O; see file_io.cpp
@@ -240,7 +240,7 @@ std::unique_ptr<HttpInputStream::Impl> sendHttpRequestImpl(const Zstring& url,
const std::string& contentType, //required for POST
const Zstring& userAgent,
const Zstring* caCertFilePath /*optional: enable certificate validation*/,
- const IOCallback& notifyUnbufferedIO) //throw SysError, X
+ const IoCallback& notifyUnbufferedIO) //throw SysError, X
{
Zstring urlRed = url;
//"A user agent should not automatically redirect a request more than five times, since such redirections usually indicate an infinite loop."
@@ -339,14 +339,14 @@ std::vector<std::pair<std::string, std::string>> zen::xWwwFormUrlDecode(const st
}
-HttpInputStream zen::sendHttpGet(const Zstring& url, const Zstring& userAgent, const Zstring* caCertFilePath, const IOCallback& notifyUnbufferedIO) //throw SysError, X
+HttpInputStream zen::sendHttpGet(const Zstring& url, const Zstring& userAgent, const Zstring* caCertFilePath, const IoCallback& notifyUnbufferedIO) //throw SysError, X
{
return sendHttpRequestImpl(url, nullptr /*postBuf*/, "" /*contentType*/, userAgent, caCertFilePath, notifyUnbufferedIO); //throw SysError, X, X
}
HttpInputStream zen::sendHttpPost(const Zstring& url, const std::vector<std::pair<std::string, std::string>>& postParams,
- const Zstring& userAgent, const Zstring* caCertFilePath, const IOCallback& notifyUnbufferedIO) //throw SysError, X
+ const Zstring& userAgent, const Zstring* caCertFilePath, const IoCallback& notifyUnbufferedIO) //throw SysError, X
{
return sendHttpPost(url, xWwwFormUrlEncode(postParams), "application/x-www-form-urlencoded", userAgent, caCertFilePath, notifyUnbufferedIO); //throw SysError, X
}
@@ -354,7 +354,7 @@ HttpInputStream zen::sendHttpPost(const Zstring& url, const std::vector<std::pai
HttpInputStream zen::sendHttpPost(const Zstring& url, const std::string& postBuf, const std::string& contentType,
- const Zstring& userAgent, const Zstring* caCertFilePath, const IOCallback& notifyUnbufferedIO) //throw SysError, X
+ const Zstring& userAgent, const Zstring* caCertFilePath, const IoCallback& notifyUnbufferedIO) //throw SysError, X
{
return sendHttpRequestImpl(url, &postBuf, contentType, userAgent, caCertFilePath, notifyUnbufferedIO); //throw SysError, X
}
diff --git a/zen/http.h b/zen/http.h
index 6cb107bf..07c3f28c 100644
--- a/zen/http.h
+++ b/zen/http.h
@@ -39,19 +39,19 @@ private:
HttpInputStream sendHttpGet(const Zstring& url,
const Zstring& userAgent,
const Zstring* caCertFilePath /*optional: enable certificate validation*/,
- const IOCallback& notifyUnbufferedIO /*throw X*/); //throw SysError, X
+ const IoCallback& notifyUnbufferedIO /*throw X*/); //throw SysError, X
HttpInputStream sendHttpPost(const Zstring& url,
const std::vector<std::pair<std::string, std::string>>& postParams,
const Zstring& userAgent,
const Zstring* caCertFilePath /*optional: enable certificate validation*/,
- const IOCallback& notifyUnbufferedIO /*throw X*/); //throw SysError, X
+ const IoCallback& notifyUnbufferedIO /*throw X*/); //throw SysError, X
HttpInputStream sendHttpPost(const Zstring& url,
const std::string& postBuf, const std::string& contentType,
const Zstring& userAgent,
const Zstring* caCertFilePath /*optional: enable certificate validation*/,
- const IOCallback& notifyUnbufferedIO /*throw X*/); //throw SysError, X
+ const IoCallback& notifyUnbufferedIO /*throw X*/); //throw SysError, X
bool internetIsAlive(); //noexcept
std::wstring formatHttpError(int httpStatus);
diff --git a/zen/json.h b/zen/json.h
index a3740664..f6458d6a 100644
--- a/zen/json.h
+++ b/zen/json.h
@@ -372,7 +372,7 @@ public:
if (*it == '"')
{
Token tk(Token::Type::string);
- tk.primVal = jsonUnescape({ pos_, it });
+ tk.primVal = jsonUnescape({pos_, it});
pos_ = ++it;
return tk;
}
diff --git a/zen/open_ssl.cpp b/zen/open_ssl.cpp
index ea77db43..7c94263a 100644
--- a/zen/open_ssl.cpp
+++ b/zen/open_ssl.cpp
@@ -79,7 +79,7 @@ std::wstring formatLastOpenSSLError(const char* functionName)
std::shared_ptr<EVP_PKEY> generateRsaKeyPair(int bits) //throw SysError
{
- EVP_PKEY_CTX* keyCtx = ::EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, //int id,
+ EVP_PKEY_CTX* keyCtx = ::EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, //int id
nullptr); //ENGINE* e
if (!keyCtx)
throw SysError(formatLastOpenSSLError("EVP_PKEY_CTX_new_id"));
@@ -110,9 +110,9 @@ std::shared_ptr<EVP_PKEY> streamToEvpKey(const std::string& keyStream, BioToEvpF
throw SysError(formatLastOpenSSLError("BIO_new_mem_buf"));
ZEN_ON_SCOPE_EXIT(::BIO_free_all(bio));
- if (EVP_PKEY* evp = bioToEvp(bio, //BIO* bp,
- nullptr, //EVP_PKEY** x,
- nullptr, //pem_password_cb* cb,
+ if (EVP_PKEY* evp = bioToEvp(bio, //BIO* bp
+ nullptr, //EVP_PKEY** x
+ nullptr, //pem_password_cb* cb
nullptr)) //void* u
return std::shared_ptr<EVP_PKEY>(evp, ::EVP_PKEY_free);
throw SysError(formatLastOpenSSLError(functionName));
@@ -128,9 +128,9 @@ std::shared_ptr<EVP_PKEY> streamToEvpKey(const std::string& keyStream, BioToRsaF
throw SysError(formatLastOpenSSLError("BIO_new_mem_buf"));
ZEN_ON_SCOPE_EXIT(::BIO_free_all(bio));
- RSA* rsa = bioToRsa(bio, //BIO* bp,
- nullptr, //RSA** x,
- nullptr, //pem_password_cb* cb,
+ RSA* rsa = bioToRsa(bio, //BIO* bp
+ nullptr, //RSA** x
+ nullptr, //pem_password_cb* cb
nullptr); //void* u
if (!rsa)
throw SysError(formatLastOpenSSLError(functionName));
@@ -168,9 +168,9 @@ std::shared_ptr<EVP_PKEY> streamToKey(const std::string& keyStream, RsaStreamTyp
}
auto tmp = reinterpret_cast<const unsigned char*>(keyStream.c_str());
- EVP_PKEY* evp = (publicKey ? ::d2i_PublicKey : ::d2i_PrivateKey)(EVP_PKEY_RSA, //int type,
- nullptr, //EVP_PKEY** a,
- &tmp, /*changes tmp pointer itself!*/ //const unsigned char** pp,
+ EVP_PKEY* evp = (publicKey ? ::d2i_PublicKey : ::d2i_PrivateKey)(EVP_PKEY_RSA, //int type
+ nullptr, //EVP_PKEY** a
+ &tmp, /*changes tmp pointer itself!*/ //const unsigned char** pp
static_cast<long>(keyStream.size())); //long length
if (!evp)
throw SysError(formatLastOpenSSLError(publicKey ? "d2i_PublicKey" : "d2i_PrivateKey"));
@@ -238,23 +238,23 @@ std::string evpKeyToStream(EVP_PKEY* evp, RsaToBioFunc rsaToBio, const char* fun
//fix OpenSSL API inconsistencies:
int PEM_write_bio_PrivateKey2(BIO* bio, EVP_PKEY* key)
{
- return ::PEM_write_bio_PrivateKey(bio, //BIO* bp,
- key, //EVP_PKEY* x,
- nullptr, //const EVP_CIPHER* enc,
- nullptr, //unsigned char* kstr,
- 0, //int klen,
- nullptr, //pem_password_cb* cb,
+ return ::PEM_write_bio_PrivateKey(bio, //BIO* bp
+ key, //EVP_PKEY* x
+ nullptr, //const EVP_CIPHER* enc
+ nullptr, //unsigned char* kstr
+ 0, //int klen
+ nullptr, //pem_password_cb* cb
nullptr); //void* u
}
int PEM_write_bio_RSAPrivateKey2(BIO* bio, RSA* rsa)
{
- return ::PEM_write_bio_RSAPrivateKey(bio, //BIO* bp,
- rsa, //RSA* x,
- nullptr, //const EVP_CIPHER* enc,
- nullptr, //unsigned char* kstr,
- 0, //int klen,
- nullptr, //pem_password_cb* cb,
+ return ::PEM_write_bio_RSAPrivateKey(bio, //BIO* bp
+ rsa, //RSA* x
+ nullptr, //const EVP_CIPHER* enc
+ nullptr, //unsigned char* kstr
+ 0, //int klen
+ nullptr, //pem_password_cb* cb
nullptr); //void* u
}
@@ -286,7 +286,7 @@ std::string keyToStream(EVP_PKEY* evp, RsaStreamType streamType, bool publicKey)
throw SysError(formatLastOpenSSLError(publicKey ? "i2d_PublicKey" : "i2d_PrivateKey"));
ZEN_ON_SCOPE_EXIT(::OPENSSL_free(buf)); //memory is only allocated for bufSize > 0
- return { reinterpret_cast<const char*>(buf), static_cast<size_t>(bufSize) };
+ return {reinterpret_cast<const char*>(buf), static_cast<size_t>(bufSize)};
}
//================================================================================
@@ -299,29 +299,29 @@ std::string createSignature(const std::string& message, EVP_PKEY* privateKey) //
throw SysError(formatSystemError("EVP_MD_CTX_create", L"", L"Unexpected failure.")); //no more error details
ZEN_ON_SCOPE_EXIT(::EVP_MD_CTX_destroy(mdctx));
- if (::EVP_DigestSignInit(mdctx, //EVP_MD_CTX* ctx,
- nullptr, //EVP_PKEY_CTX** pctx,
- EVP_sha256(), //const EVP_MD* type,
- nullptr, //ENGINE* e,
+ if (::EVP_DigestSignInit(mdctx, //EVP_MD_CTX* ctx
+ nullptr, //EVP_PKEY_CTX** pctx
+ EVP_sha256(), //const EVP_MD* type
+ nullptr, //ENGINE* e
privateKey) != 1) //EVP_PKEY* pkey
throw SysError(formatLastOpenSSLError("EVP_DigestSignInit"));
- if (::EVP_DigestSignUpdate(mdctx, //EVP_MD_CTX* ctx,
- message.c_str(), //const void* d,
+ if (::EVP_DigestSignUpdate(mdctx, //EVP_MD_CTX* ctx
+ message.c_str(), //const void* d
message.size()) != 1) //size_t cnt
throw SysError(formatLastOpenSSLError("EVP_DigestSignUpdate"));
size_t sigLenMax = 0; //"first call to EVP_DigestSignFinal returns the maximum buffer size required"
- if (::EVP_DigestSignFinal(mdctx, //EVP_MD_CTX* ctx,
- nullptr, //unsigned char* sigret,
+ if (::EVP_DigestSignFinal(mdctx, //EVP_MD_CTX* ctx
+ nullptr, //unsigned char* sigret
&sigLenMax) != 1) //size_t* siglen
throw SysError(formatLastOpenSSLError("EVP_DigestSignFinal"));
std::string signature(sigLenMax, '\0');
size_t sigLen = sigLenMax;
- if (::EVP_DigestSignFinal(mdctx, //EVP_MD_CTX* ctx,
- reinterpret_cast<unsigned char*>(&signature[0]), //unsigned char* sigret,
+ if (::EVP_DigestSignFinal(mdctx, //EVP_MD_CTX* ctx
+ reinterpret_cast<unsigned char*>(&signature[0]), //unsigned char* sigret
&sigLen) != 1) //size_t* siglen
throw SysError(formatLastOpenSSLError("EVP_DigestSignFinal"));
@@ -338,20 +338,20 @@ void verifySignature(const std::string& message, const std::string& signature, E
throw SysError(formatSystemError("EVP_MD_CTX_create", L"", L"Unexpected failure.")); //no more error details
ZEN_ON_SCOPE_EXIT(::EVP_MD_CTX_destroy(mdctx));
- if (::EVP_DigestVerifyInit(mdctx, //EVP_MD_CTX* ctx,
- nullptr, //EVP_PKEY_CTX** pctx,
- EVP_sha256(), //const EVP_MD* type,
- nullptr, //ENGINE* e,
+ if (::EVP_DigestVerifyInit(mdctx, //EVP_MD_CTX* ctx
+ nullptr, //EVP_PKEY_CTX** pctx
+ EVP_sha256(), //const EVP_MD* type
+ nullptr, //ENGINE* e
publicKey) != 1) //EVP_PKEY* pkey
throw SysError(formatLastOpenSSLError("EVP_DigestVerifyInit"));
- if (::EVP_DigestVerifyUpdate(mdctx, //EVP_MD_CTX* ctx,
- message.c_str(), //const void* d,
+ if (::EVP_DigestVerifyUpdate(mdctx, //EVP_MD_CTX* ctx
+ message.c_str(), //const void* d
message.size()) != 1) //size_t cnt
throw SysError(formatLastOpenSSLError("EVP_DigestVerifyUpdate"));
- if (::EVP_DigestVerifyFinal(mdctx, //EVP_MD_CTX* ctx,
- reinterpret_cast<const unsigned char*>(signature.c_str()), //const unsigned char* sig,
+ if (::EVP_DigestVerifyFinal(mdctx, //EVP_MD_CTX* ctx
+ reinterpret_cast<const unsigned char*>(signature.c_str()), //const unsigned char* sig
signature.size()) != 1) //size_t siglen
throw SysError(formatLastOpenSSLError("EVP_DigestVerifyFinal"));
}
@@ -735,10 +735,10 @@ std::string zen::convertPuttyKeyToPkix(const std::string& keyStream, const std::
throw SysError(formatSystemError("EVP_CIPHER_CTX_new", L"", L"Unexpected failure.")); //no more error details
ZEN_ON_SCOPE_EXIT(::EVP_CIPHER_CTX_free(cipCtx));
- if (::EVP_DecryptInit_ex(cipCtx, //EVP_CIPHER_CTX* ctx,
- EVP_aes_256_cbc(), //const EVP_CIPHER* type,
- nullptr, //ENGINE* impl,
- key, //const unsigned char* key, => implied length of 256 bit!
+ if (::EVP_DecryptInit_ex(cipCtx, //EVP_CIPHER_CTX* ctx
+ EVP_aes_256_cbc(), //const EVP_CIPHER* type
+ nullptr, //ENGINE* impl
+ key, //const unsigned char* key => implied length of 256 bit!
nullptr) != 1) //const unsigned char* iv
throw SysError(formatLastOpenSSLError("EVP_DecryptInit_ex"));
@@ -749,16 +749,16 @@ std::string zen::convertPuttyKeyToPkix(const std::string& keyStream, const std::
//"EVP_DecryptUpdate() should have room for (inl + cipher_block_size) bytes"
int decLen1 = 0;
- if (::EVP_DecryptUpdate(cipCtx, //EVP_CIPHER_CTX* ctx,
- reinterpret_cast<unsigned char*>(&privateBlob[0]), //unsigned char* out,
- &decLen1, //int* outl,
- reinterpret_cast<const unsigned char*>(privateBlobEnc.c_str()), //const unsigned char* in,
+ if (::EVP_DecryptUpdate(cipCtx, //EVP_CIPHER_CTX* ctx
+ reinterpret_cast<unsigned char*>(&privateBlob[0]), //unsigned char* out
+ &decLen1, //int* outl
+ reinterpret_cast<const unsigned char*>(privateBlobEnc.c_str()), //const unsigned char* in
static_cast<int>(privateBlobEnc.size())) != 1) //int inl
throw SysError(formatLastOpenSSLError("EVP_DecryptUpdate"));
int decLen2 = 0;
- if (::EVP_DecryptFinal_ex(cipCtx, //EVP_CIPHER_CTX* ctx,
- reinterpret_cast<unsigned char*>(&privateBlob[decLen1]), //unsigned char* outm,
+ if (::EVP_DecryptFinal_ex(cipCtx, //EVP_CIPHER_CTX* ctx
+ reinterpret_cast<unsigned char*>(&privateBlob[decLen1]), //unsigned char* outm
&decLen2) != 1) //int* outl
throw SysError(formatLastOpenSSLError("EVP_DecryptFinal_ex"));
@@ -777,7 +777,7 @@ std::string zen::convertPuttyKeyToPkix(const std::string& keyStream, const std::
{
static_assert(std::endian::native == std::endian::little&& sizeof(n) >= 4);
const char* numStr = reinterpret_cast<const char*>(&n);
- return { numStr[3], numStr[2], numStr[1], numStr[0] }; //big endian!
+ return {numStr[3], numStr[2], numStr[1], numStr[0]}; //big endian!
};
const std::string macData = numToBeString(algorithm .size()) + algorithm +
@@ -787,13 +787,13 @@ std::string zen::convertPuttyKeyToPkix(const std::string& keyStream, const std::
numToBeString(privateBlob .size()) + privateBlob;
char md[EVP_MAX_MD_SIZE] = {};
unsigned int mdLen = 0;
- if (!::HMAC(EVP_sha1(), //const EVP_MD* evp_md,
- macKey, //const void* key,
- sizeof(macKey), //int key_len,
- reinterpret_cast<const unsigned char*>(macData.c_str()), //const unsigned char* d,
- static_cast<int>(macData.size()), //int n,
- reinterpret_cast<unsigned char*>(md), //unsigned char* md,
- &mdLen)) //unsigned int* md_len
+ if (!::HMAC(EVP_sha1(), //const EVP_MD* evp_md
+ macKey, //const void* key
+ sizeof(macKey), //int key_len
+ reinterpret_cast<const unsigned char*>(macData.c_str()), //const unsigned char* d
+ static_cast<int>(macData.size()), //int n
+ reinterpret_cast<unsigned char*>(md), //unsigned char* md
+ &mdLen)) //unsigned int* md_len
throw SysError(formatSystemError("HMAC", L"", L"Unexpected failure.")); //no more error details
const bool hashValid = mac == std::string_view(md, mdLen);
@@ -979,10 +979,10 @@ std::string zen::convertPuttyKeyToPkix(const std::string& keyStream, const std::
throw SysError(formatLastOpenSSLError("EC_POINT_new"));
ZEN_ON_SCOPE_EXIT(::EC_POINT_free(ecPoint));
- if (::EC_POINT_oct2point(ecGroup, //const EC_GROUP* group,
- ecPoint, //EC_POINT* p,
- reinterpret_cast<const unsigned char*>(&pointStream[0]), //const unsigned char* buf,
- pointStream.size(), //size_t len,
+ if (::EC_POINT_oct2point(ecGroup, //const EC_GROUP* group
+ ecPoint, //EC_POINT* p
+ reinterpret_cast<const unsigned char*>(&pointStream[0]), //const unsigned char* buf
+ pointStream.size(), //size_t len
nullptr) != 1) //BN_CTX* ctx
throw SysError(formatLastOpenSSLError("EC_POINT_oct2point"));
@@ -1008,9 +1008,9 @@ std::string zen::convertPuttyKeyToPkix(const std::string& keyStream, const std::
//const std::string pubStream = extractStringPub(); -> we don't need the public key
const std::string priStream = extractStringPriv();
- EVP_PKEY* evpPriv = ::EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, //int type,
- nullptr, //ENGINE* e,
- reinterpret_cast<const unsigned char*>(&priStream[0]), //const unsigned char* priv,
+ EVP_PKEY* evpPriv = ::EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, //int type
+ nullptr, //ENGINE* e
+ reinterpret_cast<const unsigned char*>(&priStream[0]), //const unsigned char* priv
priStream.size()); //size_t len
if (!evpPriv)
throw SysError(formatLastOpenSSLError("EVP_PKEY_new_raw_private_key"));
diff --git a/zen/perf.h b/zen/perf.h
index 6bc328bb..2ebf1955 100644
--- a/zen/perf.h
+++ b/zen/perf.h
@@ -23,21 +23,20 @@
static zen::PerfTimer perfTest(true); //startPaused
perfTest.resume();
- ZEN_ON_SCOPE_EXIT(perfTest.pause());
-*/
+ ZEN_ON_SCOPE_EXIT(perfTest.pause()); */
namespace zen
{
-//issue with wxStopWatch? https://freefilesync.org/forum/viewtopic.php?t=1426
-// => wxStopWatch implementation uses QueryPerformanceCounter: https://github.com/wxWidgets/wxWidgets/blob/17d72a48ffd4d8ff42eed070ac48ee2de50ceabd/src/common/stopwatch.cpp
-// => whatever the problem was, it's almost certainly not caused by QueryPerformanceCounter():
-// MSDN: "How often does QPC roll over? Not less than 100 years from the most recent system boot"
-// https://docs.microsoft.com/en-us/windows/win32/sysinfo/acquiring-high-resolution-time-stamps#general-faq-about-qpc-and-tsc
-//
-// => using the system clock is problematic: https://freefilesync.org/forum/viewtopic.php?t=5280
-//
-// std::chrono::system_clock wraps ::GetSystemTimePreciseAsFileTime()
-// std::chrono::steady_clock wraps ::QueryPerformanceCounter()
+/* issue with wxStopWatch? https://freefilesync.org/forum/viewtopic.php?t=1426
+ - wxStopWatch implementation uses QueryPerformanceCounter: https://github.com/wxWidgets/wxWidgets/blob/17d72a48ffd4d8ff42eed070ac48ee2de50ceabd/src/common/stopwatch.cpp
+ - whatever the problem was, it's almost certainly not caused by QueryPerformanceCounter():
+ MSDN: "How often does QPC roll over? Not less than 100 years from the most recent system boot"
+ https://docs.microsoft.com/en-us/windows/win32/sysinfo/acquiring-high-resolution-time-stamps#general-faq-about-qpc-and-tsc
+
+ - using the system clock is problematic: https://freefilesync.org/forum/viewtopic.php?t=5280
+
+ std::chrono::system_clock wraps ::GetSystemTimePreciseAsFileTime()
+ std::chrono::steady_clock wraps ::QueryPerformanceCounter() */
class StopWatch
{
public:
diff --git a/zen/process_exec.cpp b/zen/process_exec.cpp
index bbc87c51..b82c2565 100644
--- a/zen/process_exec.cpp
+++ b/zen/process_exec.cpp
@@ -117,7 +117,7 @@ std::pair<int /*exit code*/, std::string> processExecuteImpl(const Zstring& file
if (::dup(fdLifeSignW) == -1) //O_CLOEXEC does NOT propagate with dup()
THROW_LAST_SYS_ERROR("dup(fdLifeSignW)");
- std::vector<const char*> argv{ filePath.c_str() };
+ std::vector<const char*> argv{filePath.c_str()};
for (const Zstring& arg : arguments)
argv.push_back(arg.c_str());
argv.push_back(nullptr);
@@ -147,6 +147,8 @@ std::pair<int /*exit code*/, std::string> processExecuteImpl(const Zstring& file
if (flags == -1)
THROW_LAST_SYS_ERROR("fcntl(F_GETFL)");
+ //fcntl() success: Linux: 0
+ // macOS: "Value other than -1."
if (::fcntl(fdLifeSignR, F_SETFL, flags | O_NONBLOCK) == -1)
THROW_LAST_SYS_ERROR("fcntl(F_SETFL, O_NONBLOCK)");
@@ -174,7 +176,7 @@ std::pair<int /*exit code*/, std::string> processExecuteImpl(const Zstring& file
const auto waitTimeMs = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - now).count();
- struct ::timeval tv = {};
+ timeval tv = {};
tv.tv_sec = static_cast<long>(waitTimeMs / 1000);
tv.tv_usec = static_cast<long>(waitTimeMs - tv.tv_sec * 1000) * 1000;
@@ -219,7 +221,7 @@ std::pair<int /*exit code*/, std::string> processExecuteImpl(const Zstring& file
exitCode == 127) //details should have been streamed to STDERR: used by /bin/sh, e.g. failure to execute due to missing .so file
throw SysError(utfTo<std::wstring>(trimCpy(output)));
- return { exitCode, output };
+ return {exitCode, output};
}
}
diff --git a/zen/resolve_path.cpp b/zen/resolve_path.cpp
index 76999500..0e714528 100644
--- a/zen/resolve_path.cpp
+++ b/zen/resolve_path.cpp
@@ -5,17 +5,12 @@
// *****************************************************************************
#include "resolve_path.h"
-//#include <set> //not necessarily included by <map>!
-//#include <map>
#include "time.h"
#include "thread.h"
-//#include "utf.h"
-//#include "scope_guard.h"
-//#include "globals.h"
#include "file_access.h"
#include <stdlib.h> //getenv()
- #include <unistd.h> //getcwd
+ #include <unistd.h> //getcwd()
using namespace zen;
@@ -251,7 +246,7 @@ std::vector<Zstring> zen::getFolderPathAliases(const Zstring& folderPathPhrase)
tmp.erase(dirPath);
tmp.erase(Zstring());
- return { tmp.begin(), tmp.end() };
+ return {tmp.begin(), tmp.end()};
}
diff --git a/zen/ring_buffer.h b/zen/ring_buffer.h
index 240262fa..ae2377d8 100644
--- a/zen/ring_buffer.h
+++ b/zen/ring_buffer.h
@@ -196,11 +196,11 @@ public:
using iterator = Iterator< RingBuffer, T>;
using const_iterator = Iterator<const RingBuffer, const T>;
- iterator begin() { return { *this, 0 }; }
- iterator end () { return { *this, size_ }; }
+ iterator begin() { return {*this, 0 }; }
+ iterator end () { return {*this, size_}; }
- const_iterator begin() const { return { *this, 0 }; }
- const_iterator end () const { return { *this, size_ }; }
+ const_iterator begin() const { return {*this, 0 }; }
+ const_iterator end () const { return {*this, size_}; }
const_iterator cbegin() const { return begin(); }
const_iterator cend () const { return end (); }
diff --git a/zen/serialize.h b/zen/serialize.h
index 6c57e4ee..f9677630 100644
--- a/zen/serialize.h
+++ b/zen/serialize.h
@@ -35,7 +35,7 @@ struct BufferedInputStream
Optional: support stream-copying
--------------------------------
size_t getBlockSize() const;
- const IOCallback& notifyUnbufferedIO
+ const IoCallback& notifyUnbufferedIO
};
--------------------------------
@@ -47,9 +47,9 @@ struct BufferedOutputStream
Optional: support stream-copying
--------------------------------
- const IOCallback& notifyUnbufferedIO
+ const IoCallback& notifyUnbufferedIO
}; */
-using IOCallback = std::function<void(int64_t bytesDelta)>; //throw X
+using IoCallback = std::function<void(int64_t bytesDelta)>; //throw X
//functions based on buffered stream abstraction
@@ -75,7 +75,7 @@ template < class BufferedInputStream> void readArray (BufferedInputSt
struct IOCallbackDivider
{
- IOCallbackDivider(const IOCallback& notifyUnbufferedIO, int64_t& totalUnbufferedIO) : totalUnbufferedIO_(totalUnbufferedIO), notifyUnbufferedIO_(notifyUnbufferedIO) {}
+ IOCallbackDivider(const IoCallback& notifyUnbufferedIO, int64_t& totalUnbufferedIO) : totalUnbufferedIO_(totalUnbufferedIO), notifyUnbufferedIO_(notifyUnbufferedIO) {}
void operator()(int64_t bytesDelta)
{
@@ -85,7 +85,7 @@ struct IOCallbackDivider
private:
int64_t& totalUnbufferedIO_;
- const IOCallback& notifyUnbufferedIO_;
+ const IoCallback& notifyUnbufferedIO_;
};
@@ -206,7 +206,7 @@ void writeArray(BufferedOutputStream& stream, const void* buffer, size_t len)
template <class N, class BufferedOutputStream> inline
void writeNumber(BufferedOutputStream& stream, const N& num)
{
- static_assert(IsArithmetic<N>::value || std::is_same_v<N, bool> || std::is_enum_v<N>);
+ static_assert(IsArithmeticV<N> || std::is_same_v<N, bool> || std::is_enum_v<N>);
writeArray(stream, &num, sizeof(N));
}
@@ -234,7 +234,7 @@ void readArray(BufferedInputStream& stream, void* buffer, size_t len) //throw Sy
template <class N, class BufferedInputStream> inline
N readNumber(BufferedInputStream& stream) //throw SysErrorUnexpectedEos
{
- static_assert(IsArithmetic<N>::value || std::is_same_v<N, bool> || std::is_enum_v<N>);
+ static_assert(IsArithmeticV<N> || std::is_same_v<N, bool> || std::is_enum_v<N>);
N num{};
readArray(stream, &num, sizeof(N)); //throw SysErrorUnexpectedEos
return num;
diff --git a/zen/stl_tools.h b/zen/stl_tools.h
index 495ff8d1..53b95241 100644
--- a/zen/stl_tools.h
+++ b/zen/stl_tools.h
@@ -128,7 +128,7 @@ void removeDuplicates(std::vector<T, Alloc>& v, CompLess less)
template <class T, class Alloc> inline
void removeDuplicates(std::vector<T, Alloc>& v)
{
- removeDuplicates(v, std::less<T>(), std::equal_to<T>());
+ removeDuplicates(v, std::less(), std::equal_to());
}
@@ -233,6 +233,7 @@ class FNV1aHash
{
public:
FNV1aHash() {}
+ explicit FNV1aHash(Num startVal) : hashVal_(startVal) {}
void add(Num n)
{
@@ -243,8 +244,8 @@ public:
Num get() const { return hashVal_; }
private:
- static_assert(IsUnsignedInt<Num>::value);
- static_assert(sizeof(Num) == 4 || sizeof(Num) == 8); //macOS: size_t is "unsigned long"
+ static_assert(IsUnsignedIntV<Num>);
+ static_assert(sizeof(Num) == 4 || sizeof(Num) == 8);
static constexpr Num base_ = sizeof(Num) == 4 ? 2166136261U : 14695981039346656037ULL;
static constexpr Num prime_ = sizeof(Num) == 4 ? 16777619U : 1099511628211ULL;
@@ -257,7 +258,7 @@ Num hashArray(ByteIterator first, ByteIterator last)
{
using ValType = typename std::iterator_traits<ByteIterator>::value_type;
static_assert(sizeof(ValType) <= sizeof(Num));
- static_assert(IsInteger<ValType>::value || std::is_same_v<ValType, char> || std::is_same_v<ValType, wchar_t>);
+ static_assert(IsIntegerV<ValType> || std::is_same_v<ValType, char> || std::is_same_v<ValType, wchar_t>);
FNV1aHash<Num> hash;
std::for_each(first, last, [&hash](ValType v) { hash.add(v); });
@@ -265,8 +266,7 @@ Num hashArray(ByteIterator first, ByteIterator last)
}
-//support for custom string classes in std::unordered_set/map
-struct StringHash
+struct StringHash //support for custom string classes with std::unordered_set/map
{
template <class String>
size_t operator()(const String& str) const
diff --git a/zen/string_tools.h b/zen/string_tools.h
index 883c45b8..8150df05 100644
--- a/zen/string_tools.h
+++ b/zen/string_tools.h
@@ -395,7 +395,7 @@ std::vector<S> split(const S& str, const T& delimiter, SplitOnEmpty soe)
{
if (str.empty() && soe == SplitOnEmpty::skip)
return {};
- return { str };
+ return {str};
}
const auto* const delimFirst = strBegin(delimiter);
@@ -800,9 +800,9 @@ template <class S, class Num> inline
S numberTo(const Num& number)
{
using TypeTag = std::integral_constant<impl::NumberType,
- IsSignedInt <Num>::value ? impl::NumberType::signedInt :
- IsUnsignedInt<Num>::value ? impl::NumberType::unsignedInt :
- IsFloat <Num>::value ? impl::NumberType::floatingPoint :
+ IsSignedIntV <Num> ? impl::NumberType::signedInt :
+ IsUnsignedIntV<Num> ? impl::NumberType::unsignedInt :
+ IsFloatV <Num> ? impl::NumberType::floatingPoint :
impl::NumberType::other>;
return impl::numberTo<S>(number, TypeTag());
@@ -813,9 +813,9 @@ template <class Num, class S> inline
Num stringTo(const S& str)
{
using TypeTag = std::integral_constant<impl::NumberType,
- IsSignedInt <Num>::value ? impl::NumberType::signedInt :
- IsUnsignedInt<Num>::value ? impl::NumberType::unsignedInt :
- IsFloat <Num>::value ? impl::NumberType::floatingPoint :
+ IsSignedIntV <Num> ? impl::NumberType::signedInt :
+ IsUnsignedIntV<Num> ? impl::NumberType::unsignedInt :
+ IsFloatV <Num> ? impl::NumberType::floatingPoint :
impl::NumberType::other>;
return impl::stringTo<Num>(str, TypeTag());
@@ -836,7 +836,7 @@ std::pair<char, char> hexify(unsigned char c, bool upperCase)
else
return static_cast<char>('a' + (num - 10));
};
- return { hexifyDigit(c / 16), hexifyDigit(c % 16) };
+ return {hexifyDigit(c / 16), hexifyDigit(c % 16)};
}
diff --git a/zen/string_traits.h b/zen/string_traits.h
index d9ce589c..ca40f7d6 100644
--- a/zen/string_traits.h
+++ b/zen/string_traits.h
@@ -120,19 +120,12 @@ public:
};
}
-template <class T>
-struct IsStringLike : std::bool_constant<impl::StringTraits<T>::isStringLike> {};
-template <class T>
-struct GetCharType { using Type = typename impl::StringTraits<T>::CharType; };
-
-
-//template alias helpers:
template<class T>
-constexpr bool IsStringLikeV = IsStringLike<T>::value;
+constexpr bool IsStringLikeV = impl::StringTraits<T>::isStringLike;
template<class T>
-using GetCharTypeT = typename GetCharType<T>::Type;
+using GetCharTypeT = typename impl::StringTraits<T>::CharType;
namespace impl
diff --git a/zen/symlink_target.h b/zen/symlink_target.h
index 42010fd2..32b1211d 100644
--- a/zen/symlink_target.h
+++ b/zen/symlink_target.h
@@ -70,11 +70,11 @@ Zstring getResolvedSymlinkPath_impl(const Zstring& linkPath) //throw FileError
namespace zen
{
inline
-SymlinkRawContent getSymlinkRawContent(const Zstring& linkPath) { return getSymlinkRawContent_impl(linkPath); }
+SymlinkRawContent getSymlinkRawContent(const Zstring& linkPath) { return getSymlinkRawContent_impl(linkPath); } //throw FileError
inline
-Zstring getSymlinkResolvedPath(const Zstring& linkPath) { return getResolvedSymlinkPath_impl(linkPath); }
+Zstring getSymlinkResolvedPath(const Zstring& linkPath) { return getResolvedSymlinkPath_impl(linkPath); } //throw FileError
}
diff --git a/zen/sys_info.cpp b/zen/sys_info.cpp
index f6045f7e..1a0d18f5 100644
--- a/zen/sys_info.cpp
+++ b/zen/sys_info.cpp
@@ -25,17 +25,14 @@ using namespace zen;
std::wstring zen::getUserName() //throw FileError
{
- const uid_t userIdNo = ::getuid(); //"real user ID"; never fails
-
- std::vector<char> buffer(std::max<long>(10000, ::sysconf(_SC_GETPW_R_SIZE_MAX))); //::sysconf may return long(-1)
- struct passwd buffer2 = {};
- struct passwd* pwsEntry = nullptr;
- if (::getpwuid_r(userIdNo, &buffer2, &buffer[0], buffer.size(), &pwsEntry) != 0) //getlogin() is deprecated and not working on Ubuntu at all!!!
- THROW_LAST_FILE_ERROR(_("Cannot get process information."), "getpwuid_r");
- if (!pwsEntry)
- throw FileError(_("Cannot get process information."), L"no login found"); //should not happen?
-
- return utfTo<std::wstring>(pwsEntry->pw_name);
+ //https://linux.die.net/man/3/getlogin
+ //https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/getlogin.2.html
+ const char* loginUser = ::getlogin();
+ if (!loginUser)
+ THROW_LAST_FILE_ERROR(_("Cannot get process information."), "getlogin");
+ //getlogin() is smarter than simply evaluating $LOGNAME! even in contexts without
+ //$LOGNAME, e.g. "sudo su" on Ubuntu, it returns the correct non-root user!
+ return utfTo<std::wstring>(loginUser);
}
@@ -64,7 +61,7 @@ ComputerModel zen::getComputerModel() //throw FileError
cm.vendor = tryGetInfo("/sys/devices/virtual/dmi/id/sys_vendor"); //
//clean up:
- cm.model = beforeFirst(cm.model, L'\u00ff', IfNotFoundReturn::all); //fix broken BIOS entries:
+ cm.model = beforeFirst(cm.model, L'\u00ff', IfNotFoundReturn::all); //fix broken BIOS entries:
cm.vendor = beforeFirst(cm.vendor, L'\u00ff', IfNotFoundReturn::all); //0xff can be considered 0
for (const char* dummyModel :
@@ -119,21 +116,25 @@ Zstring zen::getRealProcessPath() //throw FileError
}
+Zstring zen::getUserDataPath() //throw FileError
+{
+ if (::getuid() != 0) //nofail; root(0) => consider as request for elevation, NOT impersonation
+ if (const char* xdgCfgPath = ::getenv("XDG_CONFIG_HOME"); //no extended error reporting
+ xdgCfgPath && xdgCfgPath[0] != 0)
+ return xdgCfgPath;
+
+ return Zstring("/home/") + utfTo<Zstring>(getUserName()) + "/.config"; //throw FileError
+}
+
+
Zstring zen::getUserDownloadsPath() //throw FileError
{
try
{
- Zstring cmdLine;
- if (getuid() == 0) //nofail; root(0) => consider as request for elevation, NOT impersonation
- {
- const char* loginUser = getlogin(); //https://linux.die.net/man/3/getlogin
- if (!loginUser)
- THROW_LAST_SYS_ERROR("getlogin");
-
- cmdLine = Zstring("sudo -u ") + loginUser + " xdg-user-dir DOWNLOAD"; //sudo better be installed :>
- }
- else
- cmdLine = "xdg-user-dir DOWNLOAD";
+ const Zstring cmdLine = ::getuid() == 0 ? //nofail; root(0) => consider as request for elevation, NOT impersonation
+ //sudo better be installed :>
+ "sudo -u " + utfTo<Zstring>(getUserName()) + " xdg-user-dir DOWNLOAD" : //throw FileError
+ "xdg-user-dir DOWNLOAD";
const auto& [exitCode, output] = consoleExecute(cmdLine, std::nullopt /*timeoutMs*/); //throw SysError
if (exitCode != 0)
@@ -145,4 +146,3 @@ Zstring zen::getUserDownloadsPath() //throw FileError
}
catch (const SysError& e) { throw FileError(_("Cannot get process information."), e.toString()); }
}
-
diff --git a/zen/sys_info.h b/zen/sys_info.h
index 1b046fb6..4f83a9a3 100644
--- a/zen/sys_info.h
+++ b/zen/sys_info.h
@@ -31,6 +31,7 @@ std::wstring getOsDescription(); //throw FileError
Zstring getRealProcessPath(); //throw FileError
Zstring getUserDownloadsPath(); //throw FileError
+Zstring getUserDataPath(); //throw FileError
}
diff --git a/zen/thread.cpp b/zen/thread.cpp
index 89fa0233..e14afac7 100644
--- a/zen/thread.cpp
+++ b/zen/thread.cpp
@@ -28,7 +28,7 @@ const std::thread::id globalMainThreadId = std::this_thread::get_id();
bool zen::runningOnMainThread()
{
- if (globalMainThreadId == std::thread::id()) //called during static initialization!
+ if (globalMainThreadId == std::thread::id()) //if called during static initialization!
return true;
return std::this_thread::get_id() == globalMainThreadId;
diff --git a/zen/thread.h b/zen/thread.h
index 1bea95ea..136c7a5c 100644
--- a/zen/thread.h
+++ b/zen/thread.h
@@ -173,18 +173,18 @@ public:
void run(Function&& wi /*should throw ThreadStopRequest when needed*/, bool insertFront = false)
{
{
- std::lock_guard dummy(workLoad_->lock);
+ std::lock_guard dummy(workLoad_.ref().lock);
if (insertFront)
- workLoad_->tasks.push_front(std::move(wi));
+ workLoad_.ref().tasks.push_front(std::move(wi));
else
- workLoad_->tasks.push_back(std::move(wi));
- const size_t tasksPending = ++(workLoad_->tasksPending);
+ workLoad_.ref().tasks.push_back(std::move(wi));
+ const size_t tasksPending = ++(workLoad_.ref().tasksPending);
if (worker_.size() < std::min(tasksPending, threadCountMax_))
addWorkerThread();
}
- workLoad_->conditionNewTask.notify_all();
+ workLoad_.ref().conditionNewTask.notify_all();
}
//context of controlling thread, blocking:
@@ -203,12 +203,12 @@ public:
//non-blocking wait()-alternative: context of controlling thread:
void notifyWhenDone(const std::function<void()>& onCompletion /*noexcept! runs on worker thread!*/)
{
- std::lock_guard dummy(workLoad_->lock);
+ std::lock_guard dummy(workLoad_.ref().lock);
- if (workLoad_->tasksPending == 0)
+ if (workLoad_.ref().tasksPending == 0)
onCompletion();
else
- workLoad_->onCompletionCallbacks.push_back(onCompletion);
+ workLoad_.ref().onCompletionCallbacks.push_back(onCompletion);
}
//context of controlling thread:
@@ -222,27 +222,28 @@ private:
{
Zstring threadName = groupName_ + Zstr('[') + numberTo<Zstring>(worker_.size() + 1) + Zstr('/') + numberTo<Zstring>(threadCountMax_) + Zstr(']');
- worker_.emplace_back([wl = workLoad_, threadName = std::move(threadName)] //don't capture "this"! consider detach() and move operations
+ worker_.emplace_back([workLoad_ /*clang bug*/= workLoad_ /*share ownership!*/, threadName = std::move(threadName)]() mutable //don't capture "this"! consider detach() and move operations
{
setCurrentThreadName(threadName);
+ WorkLoad& workLoad = workLoad_.ref();
- std::unique_lock dummy(wl->lock);
+ std::unique_lock dummy(workLoad.lock);
for (;;)
{
- interruptibleWait(wl->conditionNewTask, dummy, [&tasks = wl->tasks] { return !tasks.empty(); }); //throw ThreadStopRequest
+ interruptibleWait(workLoad.conditionNewTask, dummy, [&tasks = workLoad.tasks] { return !tasks.empty(); }); //throw ThreadStopRequest
- Function task = std::move(wl->tasks. front()); //noexcept thanks to move
- /**/ wl->tasks.pop_front(); //
+ Function task = std::move(workLoad.tasks. front()); //noexcept thanks to move
+ /**/ workLoad.tasks.pop_front(); //
dummy.unlock();
task(); //throw ThreadStopRequest?
dummy.lock();
- if (--(wl->tasksPending) == 0)
- if (!wl->onCompletionCallbacks.empty())
+ if (--(workLoad.tasksPending) == 0)
+ if (!workLoad.onCompletionCallbacks.empty())
{
std::vector<std::function<void()>> callbacks;
- callbacks.swap(wl->onCompletionCallbacks);
+ callbacks.swap(workLoad.onCompletionCallbacks);
dummy.unlock();
for (const auto& cb : callbacks)
@@ -263,7 +264,7 @@ private:
};
std::vector<InterruptibleThread> worker_;
- std::shared_ptr<WorkLoad> workLoad_ = std::make_shared<WorkLoad>();
+ SharedRef<WorkLoad> workLoad_ = makeSharedRef<WorkLoad>();
bool detach_ = false;
size_t threadCountMax_;
Zstring groupName_;
@@ -446,7 +447,7 @@ private:
activeCondition_ = cv;
}
- std::atomic<bool> stopRequested_{ false }; //std:atomic is uninitialized by default!!!
+ std::atomic<bool> stopRequested_{false}; //std:atomic is uninitialized by default!!!
//"The default constructor is trivial: no initialization takes place other than zero initialization of static and thread-local objects."
std::condition_variable* activeCondition_ = nullptr;
diff --git a/zen/time.h b/zen/time.h
index aaf36983..903b2e87 100644
--- a/zen/time.h
+++ b/zen/time.h
@@ -271,7 +271,7 @@ TimeComp parseTime(const String& format, const String2& str)
if (strLast - itStr < 3)
return TimeComp();
- const char* months[] = { "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec" };
+ const char* months[] = {"jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"};
auto itMonth = std::find_if(std::begin(months), std::end(months), [&](const char* month)
{
return equalAsciiNoCase(makeStringView(itStr, 3), month);
diff --git a/zen/type_traits.h b/zen/type_traits.h
index 03fbd768..a4194c05 100644
--- a/zen/type_traits.h
+++ b/zen/type_traits.h
@@ -20,6 +20,7 @@ struct GetFirstOf
};
template<class... T> using GetFirstOfT = typename GetFirstOf<T...>::Type;
+
template <class F>
class FunctionReturnType
{
@@ -48,40 +49,40 @@ template<class T> inline auto makeSigned (T t) { return static_cast<std::make_s
template<class T> inline auto makeUnsigned(T t) { return static_cast<std::make_unsigned_t<T>>(t); }
//################# Built-in Types ########################
-//Example: "IsSignedInt<int>::value" evaluates to "true"
-
//unfortunate standardized nonsense: std::is_integral<> includes bool, char, wchar_t! => roll our own:
-template <class T> struct IsUnsignedInt;
-template <class T> struct IsSignedInt;
-
-template <class T> using IsFloat = std::is_floating_point<T>;
-template <class T> using IsInteger = std::bool_constant<IsUnsignedInt<T>::value || IsSignedInt<T>::value>;
-template <class T> using IsArithmetic = std::bool_constant<IsInteger <T>::value || IsFloat <T>::value>;
-
-//remaining non-arithmetic types: bool, char, wchar_t
-
-
-//optional: specialize new types like:
-//template <> struct IsUnsignedInt<UInt64> : std::true_type {};
+template<class T> constexpr bool IsUnsignedIntV = std::is_same_v<std::remove_cv_t<T>, unsigned char> ||
+ std::is_same_v<std::remove_cv_t<T>, unsigned short int> ||
+ std::is_same_v<std::remove_cv_t<T>, unsigned int> ||
+ std::is_same_v<std::remove_cv_t<T>, unsigned long int> ||
+ std::is_same_v<std::remove_cv_t<T>, unsigned long long int>;
+
+template<class T> constexpr bool IsSignedIntV = std::is_same_v<std::remove_cv_t<T>, signed char> ||
+ std::is_same_v<std::remove_cv_t<T>, short int> ||
+ std::is_same_v<std::remove_cv_t<T>, int> ||
+ std::is_same_v<std::remove_cv_t<T>, long int> ||
+ std::is_same_v<std::remove_cv_t<T>, long long int>;
+
+template<class T> constexpr bool IsIntegerV = IsUnsignedIntV<T> || IsSignedIntV<T>;
+template<class T> constexpr bool IsFloatV = std::is_floating_point_v<T>;
+template<class T> constexpr bool IsArithmeticV = IsIntegerV<T> || IsFloatV<T>;
//################# Class Members ########################
/* Detect data or function members of a class by name: ZEN_INIT_DETECT_MEMBER + HasMember_
Example: 1. ZEN_INIT_DETECT_MEMBER(c_str);
2. HasMemberV_c_str<T> -> use boolean
-*/
-/* Detect data or function members of a class by name *and* type: ZEN_INIT_DETECT_MEMBER2 + HasMember_
+
+ Detect data or function members of a class by name *and* type: ZEN_INIT_DETECT_MEMBER2 + HasMember_
Example: 1. ZEN_INIT_DETECT_MEMBER2(size, size_t (T::*)() const);
2. HasMember_size<T>::value -> use as boolean
-*/
-/* Detect member type of a class: ZEN_INIT_DETECT_MEMBER_TYPE + HasMemberType_
+
+ Detect member type of a class: ZEN_INIT_DETECT_MEMBER_TYPE + HasMemberType_
Example: 1. ZEN_INIT_DETECT_MEMBER_TYPE(value_type);
- 2. HasMemberTypeV_value_type<T> -> use as boolean
-*/
+ 2. HasMemberTypeV_value_type<T> -> use as boolean */
//########## Sorting ##############################
/*
@@ -114,25 +115,6 @@ LessDescending<Predicate> makeSortDirection(Predicate pred, std::false_type) { r
//################ implementation ######################
-template <class T>
-struct IsUnsignedInt : std::false_type {};
-
-template <> struct IsUnsignedInt<unsigned char > : std::true_type {};
-template <> struct IsUnsignedInt<unsigned short int > : std::true_type {};
-template <> struct IsUnsignedInt<unsigned int > : std::true_type {};
-template <> struct IsUnsignedInt<unsigned long int > : std::true_type {};
-template <> struct IsUnsignedInt<unsigned long long int> : std::true_type {};
-
-template <class T>
-struct IsSignedInt : std::false_type {};
-
-template <> struct IsSignedInt<signed char > : std::true_type {};
-template <> struct IsSignedInt<short int > : std::true_type {};
-template <> struct IsSignedInt<int > : std::true_type {};
-template <> struct IsSignedInt<long int > : std::true_type {};
-template <> struct IsSignedInt<long long int> : std::true_type {};
-//####################################################################
-
#define ZEN_INIT_DETECT_MEMBER(NAME) \
\
template<bool isClass, class T> \
diff --git a/zenXml/zenxml/cvrt_text.h b/zenXml/zenxml/cvrt_text.h
index 51b23173..058ffa30 100644
--- a/zenXml/zenxml/cvrt_text.h
+++ b/zenXml/zenxml/cvrt_text.h
@@ -131,7 +131,7 @@ template <class T>
struct GetTextType : std::integral_constant<TextType,
std::is_same_v<T, bool> ? TEXT_TYPE_BOOL :
IsStringLikeV<T> ? TEXT_TYPE_STRING : //string before number to correctly handle char/wchar_t -> this was an issue with Loki only!
- IsArithmetic<T>::value ? TEXT_TYPE_NUMBER : //
+ IsArithmeticV<T> ? TEXT_TYPE_NUMBER : //
IsChronoDuration<T>::value ? TEXT_TYPE_CHRONO :
TEXT_TYPE_OTHER> {};
diff --git a/zenXml/zenxml/dom.h b/zenXml/zenxml/dom.h
index cfbd14c9..e77509bf 100644
--- a/zenXml/zenxml/dom.h
+++ b/zenXml/zenxml/dom.h
@@ -85,7 +85,7 @@ public:
it->second->value = std::move(attrValue);
else
{
- auto itBack = attributes_.insert(attributes_.end(), { attrName, std::move(attrValue) });
+ auto itBack = attributes_.insert(attributes_.end(), {attrName, std::move(attrValue)});
attributesSorted_.emplace(std::move(attrName), itBack);
}
}
@@ -207,10 +207,10 @@ public:
\endcode
\return A pair of STL begin/end iterators to access all child elements sequentially.
*/
- std::pair<ChildIterConst, ChildIterConst> getChildren() const { return { childElements_.begin(), childElements_.end() }; }
+ std::pair<ChildIterConst, ChildIterConst> getChildren() const { return {childElements_.begin(), childElements_.end()}; }
///\sa getChildren
- std::pair<ChildIter, ChildIter> getChildren() { return { childElements_.begin(), childElements_.end() }; }
+ std::pair<ChildIter, ChildIter> getChildren() { return {childElements_.begin(), childElements_.end()}; }
///Get parent XML element, may be nullptr for root element
XmlElement* parent() { return parent_; }
@@ -231,9 +231,8 @@ public:
for (auto it = iterPair.first; it != iterPair.second; ++it)
std::cout << "name: " << it->name << " value: " << it->value << '\n';
\endcode
- \return A pair of STL begin/end iterators to access all attributes sequentially as a list of name/value pairs of std::string.
- */
- std::pair<AttrIter, AttrIter> getAttributes() const { return { attributes_.begin(), attributes_.end() }; }
+ \return A pair of STL begin/end iterators to access all attributes sequentially as a list of name/value pairs of std::string. */
+ std::pair<AttrIter, AttrIter> getAttributes() const { return {attributes_.begin(), attributes_.end()}; }
//swap two elements while keeping references to parent. -> disabled documentation extraction
void swapSubtree(XmlElement& other) noexcept
@@ -329,11 +328,11 @@ private:
XmlDoc (const XmlDoc&) = delete; //not implemented, thanks to XmlElement::parent_
XmlDoc& operator=(const XmlDoc&) = delete;
- std::string version_ { "1.0" };
- std::string encoding_{ "utf-8" };
+ std::string version_ {"1.0"};
+ std::string encoding_{"utf-8"};
std::string standalone_;
- XmlElement root_{ "Root" };
+ XmlElement root_{"Root"};
};
}
diff --git a/zenXml/zenxml/parser.h b/zenXml/zenxml/parser.h
index 7ec2433b..a4800ab3 100644
--- a/zenXml/zenxml/parser.h
+++ b/zenXml/zenxml/parser.h
@@ -423,15 +423,15 @@ private:
using TokenList = std::vector<std::pair<std::string, Token::Type>>;
const TokenList tokens_
{
- { "<?xml", Token::TK_DECL_BEGIN },
- { "?>", Token::TK_DECL_END },
- { "</", Token::TK_LESS_SLASH },
- { "/>", Token::TK_SLASH_GREATER },
- { "<", Token::TK_LESS }, //evaluate after TK_DECL_BEGIN!
- { ">", Token::TK_GREATER },
- { "=", Token::TK_EQUAL },
- { "\"", Token::TK_QUOTE },
- { "\'", Token::TK_QUOTE },
+ {"<?xml", Token::TK_DECL_BEGIN },
+ {"?>", Token::TK_DECL_END },
+ {"</", Token::TK_LESS_SLASH },
+ {"/>", Token::TK_SLASH_GREATER},
+ {"<", Token::TK_LESS }, //evaluate after TK_DECL_BEGIN!
+ {">", Token::TK_GREATER },
+ {"=", Token::TK_EQUAL },
+ {"\"", Token::TK_QUOTE },
+ {"\'", Token::TK_QUOTE },
};
const std::string xmlCommentBegin_ = "<!--";
bgstack15