diff options
author | Daniel Wilhelm <daniel@wili.li> | 2016-03-16 21:31:24 +0100 |
---|---|---|
committer | Daniel Wilhelm <daniel@wili.li> | 2016-03-16 21:31:24 +0100 |
commit | 89621addb4a7c87d2e3f3e7462e3c690cf71de71 (patch) | |
tree | 008b5dea7624ee1eeb57ff82c45fdf1afcab3b08 | |
parent | 7.5 (diff) | |
download | FreeFileSync-89621addb4a7c87d2e3f3e7462e3c690cf71de71.tar.gz FreeFileSync-89621addb4a7c87d2e3f3e7462e3c690cf71de71.tar.bz2 FreeFileSync-89621addb4a7c87d2e3f3e7462e3c690cf71de71.zip |
7.6
185 files changed, 4063 insertions, 4103 deletions
diff --git a/FreeFileSync/Build/Changelog.txt b/FreeFileSync/Build/Changelog.txt index cdc477f5..cb3a1188 100644 --- a/FreeFileSync/Build/Changelog.txt +++ b/FreeFileSync/Build/Changelog.txt @@ -1,3 +1,20 @@ +FreeFileSync 7.6 [2015-11-01] +----------------------------- +Create missing synchronization base folders only on demand +Improved main grid text search performance by 40% +Restore correct main dialog height after restart (Linux) +Default to standard main dialog size after unmaximize (Linux) +Prevent creation of irregular folder names (Windows) +Support MTP devices over WiFi with null modification times +Do not apply invalid vertical main dialog positions (OS X) +Support Yosemite full screen window mode (OS X) +Use buffered lock file I/O (Windows) +Correctly setup OpenSSL for multithreaded use +Added COM initialization for worker threads (Windows) +Forward focus to sync button after comparison +Streamlined file system abstraction layer interfaces + + FreeFileSync 7.5 [2015-10-01] ----------------------------- Detect moved files on source even for targets with no (SFTP) or unstable (FAT) file id support diff --git a/FreeFileSync/Build/Help/FreeFileSync.hhc b/FreeFileSync/Build/Help/FreeFileSync.hhc index d6f976d3..91bf0ec3 100644 --- a/FreeFileSync/Build/Help/FreeFileSync.hhc +++ b/FreeFileSync/Build/Help/FreeFileSync.hhc @@ -52,8 +52,8 @@ <param name="Local" value="html\schedule-a-batch-job.html"> </OBJECT> <LI> <OBJECT type="text/sitemap"> - <param name="Name" value="Synchronize with FTP"> - <param name="Local" value="html\synchronize-with-ftp.html"> + <param name="Name" value="Synchronize with SFTP"> + <param name="Local" value="html\synchronize-with-sftp.html"> </OBJECT> <LI> <OBJECT type="text/sitemap"> <param name="Name" value="Tips and Tricks"> diff --git a/FreeFileSync/Build/Help/FreeFileSync.hhp b/FreeFileSync/Build/Help/FreeFileSync.hhp index 9ab37838..a165eb37 100644 --- a/FreeFileSync/Build/Help/FreeFileSync.hhp +++ b/FreeFileSync/Build/Help/FreeFileSync.hhp @@ -19,7 +19,7 @@ html\external-applications.html html\freefilesync.html html\macros.html html\schedule-a-batch-job.html -html\synchronize-with-ftp.html +html\synchronize-with-sftp.html html\tips-and-tricks.html html\variable-drive-letters.html html\versioning.html diff --git a/FreeFileSync/Build/Help/html/command-line.html b/FreeFileSync/Build/Help/html/command-line.html index 3cfcf182..e7f89892 100644 --- a/FreeFileSync/Build/Help/html/command-line.html +++ b/FreeFileSync/Build/Help/html/command-line.html @@ -15,7 +15,7 @@ </p> <div class="box-outer"><div class="greybox"><div class="box-inner"> - <div class="command-line">FreeFileSync -h</div> + <div class="command-line">FreeFileSync.exe -h</div> </div></div></div> <br> <br> @@ -25,7 +25,7 @@ <p>In order to start synchronization in batch mode, supply the path of a ffs_batch configuration file as the first argument for FreeFileSync.exe:</p> <div class="box-outer"><div class="greybox"><div class="box-inner"> - <div class="command-line">FreeFileSync "D:\Backup Projects.ffs_batch"</div> + <div class="command-line">FreeFileSync.exe "D:\Backup Projects.ffs_batch"</div> </div></div></div> <p>After synchronization one of the following status codes is returned:</p> @@ -76,7 +76,7 @@ <p>If you pass a ffs_gui file, FreeFileSync will start in GUI mode and immediately start comparison (but only if all directories exist):</p> <div class="box-outer"><div class="greybox"><div class="box-inner"> - <div class="command-line">FreeFileSync "D:\Manual Backup.ffs_gui"</div> + <div class="command-line">FreeFileSync.exe "D:\Manual Backup.ffs_gui"</div> </div></div></div> <br> <br> @@ -89,7 +89,7 @@ </p> <div class="box-outer"><div class="greybox"><div class="box-inner"> - <div class="command-line">FreeFileSync "D:\Manual Backup.ffs_gui" -leftdir C:\NewSource -rightdir D:\NewTarget</div> + <div class="command-line">FreeFileSync.exe "D:\Manual Backup.ffs_gui" -leftdir C:\NewSource -rightdir D:\NewTarget</div> </div></div></div> <br> <br> @@ -102,7 +102,7 @@ </p> <div class="box-outer"><div class="greybox"><div class="box-inner"> - <div class="command-line">FreeFileSync "D:\Manual Backup.ffs_gui" "D:\Backup Projects.ffs_batch"</div> + <div class="command-line">FreeFileSync.exe "D:\Manual Backup.ffs_gui" "D:\Backup Projects.ffs_batch"</div> </div></div></div> </body> </html>
\ No newline at end of file diff --git a/FreeFileSync/Build/Help/html/synchronize-with-ftp.html b/FreeFileSync/Build/Help/html/synchronize-with-sftp.html index b36c316a..4d2f1016 100644 --- a/FreeFileSync/Build/Help/html/synchronize-with-ftp.html +++ b/FreeFileSync/Build/Help/html/synchronize-with-sftp.html @@ -3,16 +3,20 @@ <head> <meta charset="utf-8"> <link rel="stylesheet" type="text/css" href="base.css" > - <title>Synchronize with FTP</title> + <title>Synchronize with SFTP</title> </head> <body> <h1>Synchronize with SFTP <span style="font-weight: normal">(Windows)</span></h1> - <p>FreeFileSync support synchronization with SFTP natively.</p> - + <p> + FreeFileSync supports synchronization with SFTP natively. Just enter your SFTP login information into the dialog shown during folder selection:<br> + <br> + <img src="../images/sftp-login.png" alt="Enter SFTP login data"> + </p> + <div class="box-outer"><div class="bluebox"><div class="box-inner"> - <b>Note</b><br>In case the FTP server sets file modification times to the current time + <b>Note</b><br>In case the SFTP server sets file modification times to the current time you can do a <a href="expert-settings.html">Compare by File Size</a> as a workaround. </div></div></div> <br> diff --git a/FreeFileSync/Build/Help/images/sftp-login.png b/FreeFileSync/Build/Help/images/sftp-login.png Binary files differnew file mode 100644 index 00000000..df2a74cd --- /dev/null +++ b/FreeFileSync/Build/Help/images/sftp-login.png diff --git a/FreeFileSync/Build/Languages/german.lng b/FreeFileSync/Build/Languages/german.lng index 22c4c994..e109c58b 100644 --- a/FreeFileSync/Build/Languages/german.lng +++ b/FreeFileSync/Build/Languages/german.lng @@ -7,6 +7,18 @@ <plural_definition>n == 1 ? 0 : 1</plural_definition> </header> +<source>This FreeFileSync installer for donors has reached its installation limit. Download the regular version from the FreeFileSync homepage now?</source> +<target>Das FreeFileSync Installationsprogramm für Spender hat sein Installationslimit erreicht. Jetzt die normale Version von der FreeFileSync Homepage herunterladen?</target> + +<source>Thanks for your donation and support!</source> +<target>Danke für Ihre Spende und Unterstützung!</target> + +<source>%x is not a regular directory name.</source> +<target>%x ist kein regulärer Verzeichnisname.</target> + +<source>If you ignore this error the folders are considered empty. Missing folders are created automatically when needed.</source> +<target>Wird dieser Fehler ignoriert, werden die Ordner als leer angesehen. Fehlende Ordner werden bei Bedarf automatisch erstellt.</target> + <source>Both sides have changed since last synchronization.</source> <target>Beide Seiten wurden seit der letzten Synchronisation verändert.</target> @@ -109,9 +121,6 @@ <source>Cannot find the following folders:</source> <target>Die folgenden Ordner wurden nicht gefunden:</target> -<source>You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization.</source> -<target>Dieser Fehler kann ignoriert werden, um die Ordner als leer anzusehen und beim Synchronisieren automatisch zu erstellen.</target> - <source>A folder input field is empty.</source> <target>Ein Ordnereingabefeld ist leer.</target> @@ -255,9 +264,6 @@ Tatsächlich: %y bytes <source>Cannot find device %x.</source> <target>Das Gerät %x wurde nicht gefunden.</target> -<source>Cannot determine free disk space for %x.</source> -<target>Der freie Speicherplatz für %x konnte nicht ermittelt werden.</target> - <source>Cannot create directory %x.</source> <target>Das Verzeichnis %x kann nicht erstellt werden.</target> @@ -276,12 +282,15 @@ Tatsächlich: %y bytes <source>Cannot resolve symbolic link %x.</source> <target>Die symbolische Verknüpfung %x kann nicht aufgelöst werden.</target> -<source>Unable to move %x to the recycle bin.</source> -<target>%x kann nicht in den Papierkorb verschoben werden.</target> - <source>Cannot open directory %x.</source> <target>Das Verzeichnis %x kann nicht geöffnet werden.</target> +<source>Cannot determine free disk space for %x.</source> +<target>Der freie Speicherplatz für %x konnte nicht ermittelt werden.</target> + +<source>Unable to move %x to the recycle bin.</source> +<target>%x kann nicht in den Papierkorb verschoben werden.</target> + <source>Incorrect command line:</source> <target>Ungültige Befehlszeile:</target> diff --git a/FreeFileSync/Build/Resources.zip b/FreeFileSync/Build/Resources.zip Binary files differindex fc4cb51b..b3be68ad 100644 --- a/FreeFileSync/Build/Resources.zip +++ b/FreeFileSync/Build/Resources.zip diff --git a/FreeFileSync/Source/Makefile b/FreeFileSync/Source/Makefile index 07a56306..e7d21650 100644 --- a/FreeFileSync/Source/Makefile +++ b/FreeFileSync/Source/Makefile @@ -5,7 +5,7 @@ SHAREDIR = $(DESTDIR)$(prefix)/share APPSHAREDIR = $(SHAREDIR)/$(APPNAME) DOCSHAREDIR = $(SHAREDIR)/doc/$(APPNAME) -CXXFLAGS = -std=c++11 -pipe -DWXINTL_NO_GETTEXT_MACRO -I../.. -I../../zenXml -include "zen/i18n.h" -include "zen/warn_static.h" -Wall \ +CXXFLAGS = -std=c++14 -pipe -DWXINTL_NO_GETTEXT_MACRO -I../.. -I../../zenXml -include "zen/i18n.h" -include "zen/warn_static.h" -Wall \ -O3 -DNDEBUG `wx-config --cxxflags --debug=no` -DZEN_LINUX -pthread LINKFLAGS = -s `wx-config --libs std, aui --debug=no` -lboost_thread -lboost_chrono -lboost_system -lz -pthread diff --git a/FreeFileSync/Source/RealtimeSync/Makefile b/FreeFileSync/Source/RealtimeSync/Makefile index a83e711d..04b8856c 100644 --- a/FreeFileSync/Source/RealtimeSync/Makefile +++ b/FreeFileSync/Source/RealtimeSync/Makefile @@ -2,7 +2,7 @@ APPNAME = RealtimeSync prefix = /usr BINDIR = $(DESTDIR)$(prefix)/bin -CXXFLAGS = -std=c++11 -pipe -DWXINTL_NO_GETTEXT_MACRO -I../../.. -I../../../zenXml -include "zen/i18n.h" -include "zen/warn_static.h" -Wall \ +CXXFLAGS = -std=c++14 -pipe -DWXINTL_NO_GETTEXT_MACRO -I../../.. -I../../../zenXml -include "zen/i18n.h" -include "zen/warn_static.h" -Wall \ -O3 -DNDEBUG `wx-config --cxxflags --debug=no` -DZEN_LINUX -pthread LINKFLAGS = -s `wx-config --libs std, aui --debug=no` -lboost_thread -lboost_chrono -lboost_system -lz -pthread diff --git a/FreeFileSync/Source/RealtimeSync/app_icon.h b/FreeFileSync/Source/RealtimeSync/app_icon.h index 1a2a3e32..da9f8245 100644 --- a/FreeFileSync/Source/RealtimeSync/app_icon.h +++ b/FreeFileSync/Source/RealtimeSync/app_icon.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef RTS_APP_ICON_8914578394545 -#define RTS_APP_ICON_8914578394545 +#ifndef APP_ICON_H_8914578394545342 +#define APP_ICON_H_8914578394545342 #include <wx/icon.h> #include <wx+/image_resources.h> @@ -37,4 +37,4 @@ wxIcon getRtsIcon() } -#endif //RTS_APP_ICON_8914578394545 +#endif //APP_ICON_H_8914578394545342 diff --git a/FreeFileSync/Source/RealtimeSync/application.h b/FreeFileSync/Source/RealtimeSync/application.h index 2d2bd4c2..024200b8 100644 --- a/FreeFileSync/Source/RealtimeSync/application.h +++ b/FreeFileSync/Source/RealtimeSync/application.h @@ -4,11 +4,12 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef REALTIMESYNCAPP_H -#define REALTIMESYNCAPP_H +#ifndef APPLICATION_H_18506781708176342677 +#define APPLICATION_H_18506781708176342677 #include <wx/app.h> + class Application : public wxApp { public: @@ -23,4 +24,4 @@ private: //wxLayoutDirection GetLayoutDirection() const override { return wxLayout_LeftToRight; } }; -#endif // REALTIMESYNCAPP_H +#endif //APPLICATION_H_18506781708176342677 diff --git a/FreeFileSync/Source/RealtimeSync/folder_selector2.cpp b/FreeFileSync/Source/RealtimeSync/folder_selector2.cpp index 30e260fd..9eaff122 100644 --- a/FreeFileSync/Source/RealtimeSync/folder_selector2.cpp +++ b/FreeFileSync/Source/RealtimeSync/folder_selector2.cpp @@ -36,7 +36,7 @@ void setFolderPath(const Zstring& dirpath, wxTextCtrl* txtCtrl, wxWindow& toolti tooltipWnd.SetToolTip(toWx(folderPathFmt)); //who knows when the real bugfix reaches mere mortals via an official release... if (staticText) //change static box label only if there is a real difference to what is shown in wxTextCtrl anyway - staticText->SetLabel(EqualFilePath()(appendSeparator(trimCpy(dirpath)), appendSeparator(folderPathFmt)) ? wxString(_("Drag && drop")) : toWx(folderPathFmt)); + staticText->SetLabel(equalFilePath(appendSeparator(trimCpy(dirpath)), appendSeparator(folderPathFmt)) ? wxString(_("Drag && drop")) : toWx(folderPathFmt)); } } diff --git a/FreeFileSync/Source/RealtimeSync/folder_selector2.h b/FreeFileSync/Source/RealtimeSync/folder_selector2.h index fb748197..49e2c8b3 100644 --- a/FreeFileSync/Source/RealtimeSync/folder_selector2.h +++ b/FreeFileSync/Source/RealtimeSync/folder_selector2.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef DIR_NAME2_H_073246031245342566 -#define DIR_NAME2_H_073246031245342566 +#ifndef FOLDER_SELECTOR2_H_073246031245342566 +#define FOLDER_SELECTOR2_H_073246031245342566 #include <zen/zstring.h> #include <wx/stattext.h> @@ -43,4 +43,4 @@ private: }; } -#endif //DIR_NAME2_H_073246031245342566 +#endif //FOLDER_SELECTOR2_H_073246031245342566 diff --git a/FreeFileSync/Source/RealtimeSync/main_dlg.cpp b/FreeFileSync/Source/RealtimeSync/main_dlg.cpp index ade897ad..4ad5c5be 100644 --- a/FreeFileSync/Source/RealtimeSync/main_dlg.cpp +++ b/FreeFileSync/Source/RealtimeSync/main_dlg.cpp @@ -239,7 +239,7 @@ void MainDialog::OnConfigSave(wxCommandEvent& event) defaultFileName = beforeLast(defaultFileName, Zstr("."), IF_MISSING_RETURN_NONE) + Zstr(".ffs_real"); wxFileDialog filePicker(this, - wxEmptyString, + wxString(), //OS X really needs dir/file separated like this: utfCvrtTo<wxString>(beforeLast(defaultFileName, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)), //default dir utfCvrtTo<wxString>(afterLast (defaultFileName, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL)), //default file @@ -306,9 +306,9 @@ void MainDialog::setLastUsedConfig(const Zstring& filepath) void MainDialog::OnConfigLoad(wxCommandEvent& event) { wxFileDialog filePicker(this, - wxEmptyString, + wxString(), utfCvrtTo<wxString>(beforeLast(currentConfigFileName, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)), //default dir - wxEmptyString, + wxString(), wxString(L"RealtimeSync (*.ffs_real; *.ffs_batch)|*.ffs_real;*.ffs_batch") + L"|" +_("All files") + L" (*.*)|*", wxFD_OPEN); if (filePicker.ShowModal() == wxID_OK) diff --git a/FreeFileSync/Source/RealtimeSync/main_dlg.h b/FreeFileSync/Source/RealtimeSync/main_dlg.h index 768b6098..45eabdd8 100644 --- a/FreeFileSync/Source/RealtimeSync/main_dlg.h +++ b/FreeFileSync/Source/RealtimeSync/main_dlg.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef RTS_MAIN_DLG_H_2384790842252445 -#define RTS_MAIN_DLG_H_2384790842252445 +#ifndef MAIN_DLG_H_2384790842252445 +#define MAIN_DLG_H_2384790842252445 #include "gui_generated.h" #include <vector> @@ -16,6 +16,7 @@ #include <wx/timer.h> #include "folder_selector2.h" + namespace xmlAccess { struct XmlRealConfig; @@ -67,4 +68,4 @@ private: zen::AsyncGuiQueue guiQueue; //schedule and run long-running tasks asynchronously, but process results on GUI queue }; -#endif //RTS_MAIN_DLG_H_2384790842252445 +#endif //MAIN_DLG_H_2384790842252445 diff --git a/FreeFileSync/Source/RealtimeSync/monitor.cpp b/FreeFileSync/Source/RealtimeSync/monitor.cpp index cc218ef8..bfdfb93b 100644 --- a/FreeFileSync/Source/RealtimeSync/monitor.cpp +++ b/FreeFileSync/Source/RealtimeSync/monitor.cpp @@ -72,7 +72,7 @@ WaitResult waitForChanges(const std::vector<Zstring>& folderPathPhrases, //throw auto ftDirExists = runAsync([=] { return zen::dirExists(folderPathFmt); }); //we need to check dirExists(), not somethingExists(): it's not clear if DirWatcher detects a type clash (file instead of directory!) while (ftDirExists.wait_for(std::chrono::milliseconds(rts::UI_UPDATE_INTERVAL / 2)) != std::future_status::ready) - onRefreshGui(false); //may throw! + onRefreshGui(false /*readyForSync*/); //may throw! if (!ftDirExists.get()) return WaitResult(folderPathFmt); @@ -113,7 +113,7 @@ WaitResult waitForChanges(const std::vector<Zstring>& folderPathPhrases, //throw return WaitResult(folderPath); try { - std::vector<DirWatcher::Entry> changedItems = watcher.getChanges([&] { onRefreshGui(false); /*may throw!*/ }); //throw FileError + std::vector<DirWatcher::Entry> changedItems = watcher.getChanges([&] { onRefreshGui(false /*readyForSync*/); /*may throw!*/ }); //throw FileError //remove to be ignored changes erase_if(changedItems, [](const DirWatcher::Entry& e) @@ -140,7 +140,7 @@ WaitResult waitForChanges(const std::vector<Zstring>& folderPathPhrases, //throw } std::this_thread::sleep_for(std::chrono::milliseconds(rts::UI_UPDATE_INTERVAL / 2)); - onRefreshGui(true); //throw ?: may start sync at this presumably idle time + onRefreshGui(true /*readyForSync*/); //throw ?: may start sync at this presumably idle time } } diff --git a/FreeFileSync/Source/RealtimeSync/monitor.h b/FreeFileSync/Source/RealtimeSync/monitor.h index a5aee773..eca0d71e 100644 --- a/FreeFileSync/Source/RealtimeSync/monitor.h +++ b/FreeFileSync/Source/RealtimeSync/monitor.h @@ -4,12 +4,13 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef MONITOR_HEADER_345087425834253425 -#define MONITOR_HEADER_345087425834253425 +#ifndef MONITOR_H_345087425834253425 +#define MONITOR_H_345087425834253425 #include <functional> #include <zen/zstring.h> + namespace rts { const int UI_UPDATE_INTERVAL = 100; //unit: [ms]; perform ui updates not more often than necessary, 100 seems to be a good value with only a minimal performance loss @@ -35,4 +36,4 @@ void monitorDirectories(const std::vector<Zstring>& folderPathPhrases, MonitorCallback& callback); } -#endif //MONITOR_HEADER_345087425834253425 +#endif //MONITOR_H_345087425834253425 diff --git a/FreeFileSync/Source/RealtimeSync/tray_menu.h b/FreeFileSync/Source/RealtimeSync/tray_menu.h index 1b906c54..e8c1cf2d 100644 --- a/FreeFileSync/Source/RealtimeSync/tray_menu.h +++ b/FreeFileSync/Source/RealtimeSync/tray_menu.h @@ -4,12 +4,13 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef TRAY_583967857420987534253245 -#define TRAY_583967857420987534253245 +#ifndef TRAY_MENU_H_3967857420987534253245 +#define TRAY_MENU_H_3967857420987534253245 #include <wx/string.h> #include "xml_proc.h" + namespace rts { enum AbortReason @@ -20,4 +21,4 @@ enum AbortReason AbortReason startDirectoryMonitor(const xmlAccess::XmlRealConfig& config, const wxString& jobname); //jobname may be empty } -#endif //TRAY_583967857420987534253245 +#endif //TRAY_MENU_H_3967857420987534253245 diff --git a/FreeFileSync/Source/RealtimeSync/xml_proc.cpp b/FreeFileSync/Source/RealtimeSync/xml_proc.cpp index 68bcf5ba..b095daa7 100644 --- a/FreeFileSync/Source/RealtimeSync/xml_proc.cpp +++ b/FreeFileSync/Source/RealtimeSync/xml_proc.cpp @@ -103,7 +103,7 @@ xmlAccess::XmlRealConfig convertBatchToReal(const xmlAccess::XmlBatchConfig& bat xmlAccess::XmlRealConfig output; output.directories.assign(uniqueFolders.begin(), uniqueFolders.end()); - output.commandline = Zstr("\"") + zen::getFreeFileSyncLauncher() + Zstr("\" \"") + batchFilePath + Zstr("\""); + output.commandline = Zstr("\"") + zen::getFreeFileSyncLauncherPath() + Zstr("\" \"") + batchFilePath + Zstr("\""); return output; } } diff --git a/FreeFileSync/Source/RealtimeSync/xml_proc.h b/FreeFileSync/Source/RealtimeSync/xml_proc.h index e86ec5c0..9521b029 100644 --- a/FreeFileSync/Source/RealtimeSync/xml_proc.h +++ b/FreeFileSync/Source/RealtimeSync/xml_proc.h @@ -4,13 +4,14 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef XMLPROCESSING_H_0813748158321813490 -#define XMLPROCESSING_H_0813748158321813490 +#ifndef XML_PROC_H_0813748158321813490 +#define XML_PROC_H_0813748158321813490 #include <vector> #include <zen/xml_io.h> #include <zen/zstring.h> + namespace xmlAccess { struct XmlRealConfig @@ -31,4 +32,4 @@ void readRealOrBatchConfig(const Zstring& filepath, xmlAccess::XmlRealConfig& co int getProgramLanguage(); } -#endif //XMLPROCESSING_H_0813748158321813490 +#endif //XML_PROC_H_0813748158321813490 diff --git a/FreeFileSync/Source/algorithm.cpp b/FreeFileSync/Source/algorithm.cpp index 189d63ea..9a5379db 100644 --- a/FreeFileSync/Source/algorithm.cpp +++ b/FreeFileSync/Source/algorithm.cpp @@ -20,7 +20,7 @@ using namespace zen; void zen::swapGrids(const MainConfiguration& config, FolderComparison& folderCmp) { - std::for_each(begin(folderCmp), end(folderCmp), [](BaseDirPair& baseObj) { baseObj.flip(); }); + std::for_each(begin(folderCmp), end(folderCmp), [](BaseFolderPair& baseFolder) { baseFolder.flip(); }); redetermineSyncDirection(config, folderCmp, nullptr, //onReportWarning nullptr); //onUpdateStatus -> status update while loading db file @@ -40,119 +40,119 @@ private: void recurse(HierarchyObject& hierObj) const { - for (FilePair& fileObj : hierObj.refSubFiles()) - processFile(fileObj); - for (SymlinkPair& linkObj : hierObj.refSubLinks()) - processLink(linkObj); - for (DirPair& dirObj : hierObj.refSubDirs()) - processDir(dirObj); + for (FilePair& file : hierObj.refSubFiles()) + processFile(file); + for (SymlinkPair& link : hierObj.refSubLinks()) + processLink(link); + for (FolderPair& folder : hierObj.refSubFolders()) + processFolder(folder); } - void processFile(FilePair& fileObj) const + void processFile(FilePair& file) const { - const CompareFilesResult cat = fileObj.getCategory(); + const CompareFilesResult cat = file.getCategory(); //##################### schedule old temporary files for deletion #################### - if (cat == FILE_LEFT_SIDE_ONLY && endsWith(fileObj.getItemName<LEFT_SIDE>(), ABF::TEMP_FILE_ENDING)) - return fileObj.setSyncDir(SyncDirection::LEFT); - else if (cat == FILE_RIGHT_SIDE_ONLY && endsWith(fileObj.getItemName<RIGHT_SIDE>(), ABF::TEMP_FILE_ENDING)) - return fileObj.setSyncDir(SyncDirection::RIGHT); + if (cat == FILE_LEFT_SIDE_ONLY && endsWith(file.getItemName<LEFT_SIDE>(), 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)) + return file.setSyncDir(SyncDirection::RIGHT); //#################################################################################### switch (cat) { case FILE_LEFT_SIDE_ONLY: - fileObj.setSyncDir(dirCfg.exLeftSideOnly); + file.setSyncDir(dirCfg.exLeftSideOnly); break; case FILE_RIGHT_SIDE_ONLY: - fileObj.setSyncDir(dirCfg.exRightSideOnly); + file.setSyncDir(dirCfg.exRightSideOnly); break; case FILE_RIGHT_NEWER: - fileObj.setSyncDir(dirCfg.rightNewer); + file.setSyncDir(dirCfg.rightNewer); break; case FILE_LEFT_NEWER: - fileObj.setSyncDir(dirCfg.leftNewer); + file.setSyncDir(dirCfg.leftNewer); break; case FILE_DIFFERENT_CONTENT: - fileObj.setSyncDir(dirCfg.different); + file.setSyncDir(dirCfg.different); break; case FILE_CONFLICT: case FILE_DIFFERENT_METADATA: //use setting from "conflict/cannot categorize" if (dirCfg.conflict == SyncDirection::NONE) - fileObj.setSyncDirConflict(fileObj.getCatExtraDescription()); //take over category conflict + file.setSyncDirConflict(file.getCatExtraDescription()); //take over category conflict else - fileObj.setSyncDir(dirCfg.conflict); + file.setSyncDir(dirCfg.conflict); break; case FILE_EQUAL: - fileObj.setSyncDir(SyncDirection::NONE); + file.setSyncDir(SyncDirection::NONE); break; } } - void processLink(SymlinkPair& linkObj) const + void processLink(SymlinkPair& symlink) const { - switch (linkObj.getLinkCategory()) + switch (symlink.getLinkCategory()) { case SYMLINK_LEFT_SIDE_ONLY: - linkObj.setSyncDir(dirCfg.exLeftSideOnly); + symlink.setSyncDir(dirCfg.exLeftSideOnly); break; case SYMLINK_RIGHT_SIDE_ONLY: - linkObj.setSyncDir(dirCfg.exRightSideOnly); + symlink.setSyncDir(dirCfg.exRightSideOnly); break; case SYMLINK_LEFT_NEWER: - linkObj.setSyncDir(dirCfg.leftNewer); + symlink.setSyncDir(dirCfg.leftNewer); break; case SYMLINK_RIGHT_NEWER: - linkObj.setSyncDir(dirCfg.rightNewer); + symlink.setSyncDir(dirCfg.rightNewer); break; case SYMLINK_CONFLICT: case SYMLINK_DIFFERENT_METADATA: //use setting from "conflict/cannot categorize" if (dirCfg.conflict == SyncDirection::NONE) - linkObj.setSyncDirConflict(linkObj.getCatExtraDescription()); //take over category conflict + symlink.setSyncDirConflict(symlink.getCatExtraDescription()); //take over category conflict else - linkObj.setSyncDir(dirCfg.conflict); + symlink.setSyncDir(dirCfg.conflict); break; case SYMLINK_DIFFERENT_CONTENT: - linkObj.setSyncDir(dirCfg.different); + symlink.setSyncDir(dirCfg.different); break; case SYMLINK_EQUAL: - linkObj.setSyncDir(SyncDirection::NONE); + symlink.setSyncDir(SyncDirection::NONE); break; } } - void processDir(DirPair& dirObj) const + void processFolder(FolderPair& folder) const { - const CompareDirResult cat = dirObj.getDirCategory(); + const CompareDirResult cat = folder.getDirCategory(); //########### schedule abandoned temporary recycle bin directory for deletion ########## - if (cat == DIR_LEFT_SIDE_ONLY && endsWith(dirObj.getItemName<LEFT_SIDE>(), ABF::TEMP_FILE_ENDING)) - return setSyncDirectionRec(SyncDirection::LEFT, dirObj); // - else if (cat == DIR_RIGHT_SIDE_ONLY && endsWith(dirObj.getItemName<RIGHT_SIDE>(), ABF::TEMP_FILE_ENDING)) - return setSyncDirectionRec(SyncDirection::RIGHT, dirObj); //don't recurse below! + if (cat == DIR_LEFT_SIDE_ONLY && endsWith(folder.getItemName<LEFT_SIDE>(), 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)) + return setSyncDirectionRec(SyncDirection::RIGHT, folder); //don't recurse below! //####################################################################################### switch (cat) { case DIR_LEFT_SIDE_ONLY: - dirObj.setSyncDir(dirCfg.exLeftSideOnly); + folder.setSyncDir(dirCfg.exLeftSideOnly); break; case DIR_RIGHT_SIDE_ONLY: - dirObj.setSyncDir(dirCfg.exRightSideOnly); + folder.setSyncDir(dirCfg.exRightSideOnly); break; case DIR_EQUAL: - dirObj.setSyncDir(SyncDirection::NONE); + folder.setSyncDir(SyncDirection::NONE); break; case DIR_CONFLICT: case DIR_DIFFERENT_METADATA: //use setting from "conflict/cannot categorize" if (dirCfg.conflict == SyncDirection::NONE) - dirObj.setSyncDirConflict(dirObj.getCatExtraDescription()); //take over category conflict + folder.setSyncDirConflict(folder.getCatExtraDescription()); //take over category conflict else - dirObj.setSyncDir(dirCfg.conflict); + folder.setSyncDir(dirCfg.conflict); break; } - recurse(dirObj); + recurse(folder); } const DirectionSet dirCfg; @@ -164,22 +164,22 @@ private: bool allItemsCategoryEqual(const HierarchyObject& hierObj) { return std::all_of(hierObj.refSubFiles().begin(), hierObj.refSubFiles().end(), - [](const FilePair& fileObj) { return fileObj.getCategory() == FILE_EQUAL; })&& //files + [](const FilePair& file) { return file.getCategory() == FILE_EQUAL; })&& //files std::all_of(hierObj.refSubLinks().begin(), hierObj.refSubLinks().end(), - [](const SymlinkPair& linkObj) { return linkObj.getLinkCategory() == SYMLINK_EQUAL; })&& //symlinks + [](const SymlinkPair& link) { return link.getLinkCategory() == SYMLINK_EQUAL; })&& //symlinks - std::all_of(hierObj.refSubDirs(). begin(), hierObj.refSubDirs().end(), - [](const DirPair& dirObj) + std::all_of(hierObj.refSubFolders(). begin(), hierObj.refSubFolders().end(), + [](const FolderPair& folder) { - return dirObj.getDirCategory() == DIR_EQUAL && allItemsCategoryEqual(dirObj); //short circuit-behavior! + return folder.getDirCategory() == DIR_EQUAL && allItemsCategoryEqual(folder); //short circuit-behavior! }); //directories } } bool zen::allElementsEqual(const FolderComparison& folderCmp) { - return std::all_of(begin(folderCmp), end(folderCmp), [](const BaseDirPair& baseObj) { return allItemsCategoryEqual(baseObj); }); + return std::all_of(begin(folderCmp), end(folderCmp), [](const BaseFolderPair& baseFolder) { return allItemsCategoryEqual(baseFolder); }); } //--------------------------------------------------------------------------------------------------------------- @@ -194,9 +194,9 @@ const InSyncDescrFile& getDescriptor<RIGHT_SIDE>(const InSyncFile& dbFile) { ret template <SelectedSide side> inline -bool matchesDbEntry(const FilePair& fileObj, const InSyncDir::FileList::value_type* dbFile, unsigned int optTimeShiftHours) +bool matchesDbEntry(const FilePair& file, const InSyncFolder::FileList::value_type* dbFile, unsigned int optTimeShiftHours) { - if (fileObj.isEmpty<side>()) + if (file.isEmpty<side>()) return !dbFile; else if (!dbFile) return false; @@ -204,11 +204,11 @@ bool matchesDbEntry(const FilePair& fileObj, const InSyncDir::FileList::value_ty const Zstring& shortNameDb = dbFile->first; const InSyncDescrFile& descrDb = getDescriptor<side>(dbFile->second); - return fileObj.getItemName<side>() == shortNameDb && //detect changes in case (windows) + return file.getItemName<side>() == shortNameDb && //detect changes in case (windows) //respect 2 second FAT/FAT32 precision! copying a file to a FAT32 drive changes it's modification date by up to 2 seconds //we're not interested in "fileTimeTolerance" here! - sameFileTime(fileObj.getLastWriteTime<side>(), descrDb.lastWriteTimeRaw, 2, optTimeShiftHours) && - fileObj.getFileSize<side>() == dbFile->second.fileSize; + sameFileTime(file.getLastWriteTime<side>(), descrDb.lastWriteTimeRaw, 2, optTimeShiftHours) && + file.getFileSize<side>() == dbFile->second.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! } @@ -245,19 +245,19 @@ const InSyncDescrLink& getDescriptor<RIGHT_SIDE>(const InSyncSymlink& dbLink) { //check whether database entry and current item match: *irrespective* of current comparison settings template <SelectedSide side> inline -bool matchesDbEntry(const SymlinkPair& linkObj, const InSyncDir::LinkList::value_type* dbLink, unsigned int optTimeShiftHours) +bool matchesDbEntry(const SymlinkPair& symlink, const InSyncFolder::SymlinkList::value_type* dbSymlink, unsigned int optTimeShiftHours) { - if (linkObj.isEmpty<side>()) - return !dbLink; - else if (!dbLink) + if (symlink.isEmpty<side>()) + return !dbSymlink; + else if (!dbSymlink) return false; - const Zstring& shortNameDb = dbLink->first; - const InSyncDescrLink& descrDb = getDescriptor<side>(dbLink->second); + const Zstring& shortNameDb = dbSymlink->first; + const InSyncDescrLink& descrDb = getDescriptor<side>(dbSymlink->second); - return linkObj.getItemName<side>() == shortNameDb && + return symlink.getItemName<side>() == shortNameDb && //respect 2 second FAT/FAT32 precision! copying a file to a FAT32 drive changes its modification date by up to 2 seconds - sameFileTime(linkObj.getLastWriteTime<side>(), descrDb.lastWriteTimeRaw, 2, optTimeShiftHours); + sameFileTime(symlink.getLastWriteTime<side>(), descrDb.lastWriteTimeRaw, 2, optTimeShiftHours); } @@ -286,24 +286,24 @@ 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 -bool matchesDbEntry(const DirPair& dirObj, const InSyncDir::DirList::value_type* dbDir) +bool matchesDbEntry(const FolderPair& folder, const InSyncFolder::FolderList::value_type* dbFolder) { - if (dirObj.isEmpty<side>()) - return !dbDir || dbDir->second.status == InSyncDir::DIR_STATUS_STRAW_MAN; - else if (!dbDir || dbDir->second.status == InSyncDir::DIR_STATUS_STRAW_MAN) + if (folder.isEmpty<side>()) + return !dbFolder || dbFolder->second.status == InSyncFolder::DIR_STATUS_STRAW_MAN; + else if (!dbFolder || dbFolder->second.status == InSyncFolder::DIR_STATUS_STRAW_MAN) return false; - const Zstring& shortNameDb = dbDir->first; + const Zstring& shortNameDb = dbFolder->first; - return dirObj.getItemName<side>() == shortNameDb; + return folder.getItemName<side>() == shortNameDb; } inline -bool stillInSync(const InSyncDir& dbDir) +bool stillInSync(const InSyncFolder& dbFolder) { //case-sensitive short name match is a database invariant! - //InSyncDir::DIR_STATUS_STRAW_MAN considered + //InSyncFolder::DIR_STATUS_STRAW_MAN considered return true; } @@ -312,45 +312,45 @@ bool stillInSync(const InSyncDir& dbDir) class DetectMovedFiles { public: - static void execute(BaseDirPair& baseDirectory, const InSyncDir& dbFolder) { DetectMovedFiles(baseDirectory, dbFolder); } + static void execute(BaseFolderPair& baseFolder, const InSyncFolder& dbFolder) { DetectMovedFiles(baseFolder, dbFolder); } private: - DetectMovedFiles(BaseDirPair& baseDirectory, const InSyncDir& dbFolder) : - cmpVar (baseDirectory.getCompVariant()), - fileTimeTolerance(baseDirectory.getFileTimeTolerance()), - optTimeShiftHours(baseDirectory.getTimeShift()) + DetectMovedFiles(BaseFolderPair& baseFolder, const InSyncFolder& dbFolder) : + cmpVar (baseFolder.getCompVariant()), + fileTimeTolerance(baseFolder.getFileTimeTolerance()), + optTimeShiftHours(baseFolder.getTimeShift()) { - recurse(baseDirectory, &dbFolder); + recurse(baseFolder, &dbFolder); if ((!exLeftOnlyById .empty() || !exLeftOnlyByPath .empty()) && (!exRightOnlyById.empty() || !exRightOnlyByPath.empty())) detectMovePairs(dbFolder); } - void recurse(HierarchyObject& hierObj, const InSyncDir* dbFolder) + void recurse(HierarchyObject& hierObj, const InSyncFolder* dbFolder) { - for (FilePair& fileObj : hierObj.refSubFiles()) + for (FilePair& file : hierObj.refSubFiles()) { auto getDbFileEntry = [&]() -> const InSyncFile* //evaluate lazily! { if (dbFolder) { - auto it = dbFolder->files.find(fileObj.getPairShortName()); + auto it = dbFolder->files.find(file.getPairItemName()); if (it != dbFolder->files.end()) return &it->second; } return nullptr; }; - const CompareFilesResult cat = fileObj.getCategory(); + const CompareFilesResult cat = file.getCategory(); if (cat == FILE_LEFT_SIDE_ONLY) { if (const InSyncFile* dbFile = getDbFileEntry()) - exLeftOnlyByPath.emplace(dbFile, &fileObj); - else if (!fileObj.getFileId<LEFT_SIDE>().empty()) + exLeftOnlyByPath.emplace(dbFile, &file); + else if (!file.getFileId<LEFT_SIDE>().empty()) { - auto rv = exLeftOnlyById.emplace(fileObj.getFileId<LEFT_SIDE>(), &fileObj); + auto rv = exLeftOnlyById.emplace(file.getFileId<LEFT_SIDE>(), &file); if (!rv.second) //duplicate file ID! NTFS hard link/symlink? rv.first->second = nullptr; } @@ -358,44 +358,44 @@ private: else if (cat == FILE_RIGHT_SIDE_ONLY) { if (const InSyncFile* dbFile = getDbFileEntry()) - exRightOnlyByPath.emplace(dbFile, &fileObj); - else if (!fileObj.getFileId<RIGHT_SIDE>().empty()) + exRightOnlyByPath.emplace(dbFile, &file); + else if (!file.getFileId<RIGHT_SIDE>().empty()) { - auto rv = exRightOnlyById.emplace(fileObj.getFileId<RIGHT_SIDE>(), &fileObj); + auto rv = exRightOnlyById.emplace(file.getFileId<RIGHT_SIDE>(), &file); if (!rv.second) //duplicate file ID! NTFS hard link/symlink? rv.first->second = nullptr; } } } - for (DirPair& dirObj : hierObj.refSubDirs()) + for (FolderPair& folder : hierObj.refSubFolders()) { - const InSyncDir* dbSubFolder = nullptr; //try to find corresponding database entry + const InSyncFolder* dbSubFolder = nullptr; //try to find corresponding database entry if (dbFolder) { - auto it = dbFolder->dirs.find(dirObj.getPairShortName()); - if (it != dbFolder->dirs.end()) + auto it = dbFolder->folders.find(folder.getPairItemName()); + if (it != dbFolder->folders.end()) dbSubFolder = &it->second; } - recurse(dirObj, dbSubFolder); + recurse(folder, dbSubFolder); } } - void detectMovePairs(const InSyncDir& container) const + void detectMovePairs(const InSyncFolder& container) const { for (auto& dbFile : container.files) findAndSetMovePair(dbFile.second); - for (auto& dbDir : container.dirs) - detectMovePairs(dbDir.second); + for (auto& dbFolder : container.folders) + detectMovePairs(dbFolder.second); } template <SelectedSide side> - static bool sameSizeAndDate(const FilePair& fileObj, const InSyncFile& dbFile) + static bool sameSizeAndDate(const FilePair& file, const InSyncFile& dbFile) { - return fileObj.getFileSize<side>() == dbFile.fileSize && - sameFileTime(fileObj.getLastWriteTime<side>(), getDescriptor<side>(dbFile).lastWriteTimeRaw, 2, 0); + return file.getFileSize<side>() == dbFile.fileSize && + sameFileTime(file.getLastWriteTime<side>(), getDescriptor<side>(dbFile).lastWriteTimeRaw, 2, 0); //- respect 2 second FAT/FAT32 precision! //- a "optTimeShiftHours" != 0 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) @@ -406,7 +406,7 @@ private: template <SelectedSide side> static FilePair* getAssocFilePair(const InSyncFile& dbFile, - const std::unordered_map<ABF::FileId, FilePair*, StringHash>& exOneSideById, + const std::unordered_map<AFS::FileId, FilePair*, StringHash>& exOneSideById, const std::unordered_map<const InSyncFile*, FilePair*>& exOneSideByPath) { { @@ -418,7 +418,7 @@ private: //- note: exOneSideById isn't filled in this case, see recurse() } - const ABF::FileId fileId = getDescriptor<side>(dbFile).fileId; + const AFS::FileId fileId = getDescriptor<side>(dbFile).fileId; if (!fileId.empty()) { auto it = exOneSideById.find(fileId); @@ -447,8 +447,8 @@ private: const int fileTimeTolerance; const unsigned int optTimeShiftHours; - std::unordered_map<ABF::FileId, FilePair*, StringHash> exLeftOnlyById; //FilePair* == nullptr for duplicate ids! => consider aliasing through symlinks! - std::unordered_map<ABF::FileId, FilePair*, StringHash> exRightOnlyById; //=> avoid ambiguity for mixtures of files/symlinks on one side and allow 1-1 mapping only! + std::unordered_map<AFS::FileId, FilePair*, StringHash> exLeftOnlyById; //FilePair* == nullptr for duplicate ids! => consider aliasing through symlinks! + std::unordered_map<AFS::FileId, FilePair*, StringHash> 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::unordered_map<const InSyncFile*, FilePair*> exLeftOnlyByPath; //MSVC: only 4% faster than std::map for 1 million items! @@ -485,156 +485,156 @@ private: class RedetermineTwoWay { public: - static void execute(BaseDirPair& baseDirectory, const InSyncDir& dbFolder) { RedetermineTwoWay(baseDirectory, dbFolder); } + static void execute(BaseFolderPair& baseFolder, const InSyncFolder& dbFolder) { RedetermineTwoWay(baseFolder, dbFolder); } private: - RedetermineTwoWay(BaseDirPair& baseDirectory, const InSyncDir& dbFolder) : + RedetermineTwoWay(BaseFolderPair& baseFolder, const InSyncFolder& dbFolder) : txtBothSidesChanged(_("Both sides have changed since last synchronization.")), txtNoSideChanged(_("Cannot determine sync-direction:") + L" \n" + _("No change since last synchronization.")), txtDbNotInSync(_("Cannot determine sync-direction:") + L" \n" + _("The database entry is not in sync considering current settings.")), - cmpVar (baseDirectory.getCompVariant()), - fileTimeTolerance(baseDirectory.getFileTimeTolerance()), - optTimeShiftHours(baseDirectory.getTimeShift()) + cmpVar (baseFolder.getCompVariant()), + fileTimeTolerance(baseFolder.getFileTimeTolerance()), + optTimeShiftHours(baseFolder.getTimeShift()) { //-> considering filter not relevant: //if narrowing filter: all ok; if widening filter (if file ex on both sides -> conflict, fine; if file ex. on one side: copy to other side: fine) - recurse(baseDirectory, &dbFolder); + recurse(baseFolder, &dbFolder); } - void recurse(HierarchyObject& hierObj, const InSyncDir* dbFolder) const + void recurse(HierarchyObject& hierObj, const InSyncFolder* dbFolder) const { - for (FilePair& fileObj : hierObj.refSubFiles()) - processFile(fileObj, dbFolder); - for (SymlinkPair& linkObj : hierObj.refSubLinks()) - processSymlink(linkObj, dbFolder); - for (DirPair& dirObj : hierObj.refSubDirs()) - processDir(dirObj, dbFolder); + for (FilePair& file : hierObj.refSubFiles()) + processFile(file, dbFolder); + for (SymlinkPair& link : hierObj.refSubLinks()) + processSymlink(link, dbFolder); + for (FolderPair& folder : hierObj.refSubFolders()) + processDir(folder, dbFolder); } - void processFile(FilePair& fileObj, const InSyncDir* dbFolder) const + void processFile(FilePair& file, const InSyncFolder* dbFolder) const { - const CompareFilesResult cat = fileObj.getCategory(); + const CompareFilesResult cat = file.getCategory(); if (cat == FILE_EQUAL) return; //##################### schedule old temporary files for deletion #################### - if (cat == FILE_LEFT_SIDE_ONLY && endsWith(fileObj.getItemName<LEFT_SIDE>(), ABF::TEMP_FILE_ENDING)) - return fileObj.setSyncDir(SyncDirection::LEFT); - else if (cat == FILE_RIGHT_SIDE_ONLY && endsWith(fileObj.getItemName<RIGHT_SIDE>(), ABF::TEMP_FILE_ENDING)) - return fileObj.setSyncDir(SyncDirection::RIGHT); + if (cat == FILE_LEFT_SIDE_ONLY && endsWith(file.getItemName<LEFT_SIDE>(), 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)) + return file.setSyncDir(SyncDirection::RIGHT); //#################################################################################### //try to find corresponding database entry - const InSyncDir::FileList::value_type* dbEntry = nullptr; + const InSyncFolder::FileList::value_type* dbEntry = nullptr; if (dbFolder) { - auto it = dbFolder->files.find(fileObj.getPairShortName()); + auto it = dbFolder->files.find(file.getPairItemName()); if (it != dbFolder->files.end()) dbEntry = &*it; } //evaluation - const bool changeOnLeft = !matchesDbEntry<LEFT_SIDE >(fileObj, dbEntry, optTimeShiftHours); - const bool changeOnRight = !matchesDbEntry<RIGHT_SIDE>(fileObj, dbEntry, optTimeShiftHours); + const bool changeOnLeft = !matchesDbEntry<LEFT_SIDE >(file, dbEntry, optTimeShiftHours); + const bool changeOnRight = !matchesDbEntry<RIGHT_SIDE>(file, dbEntry, optTimeShiftHours); if (changeOnLeft != changeOnRight) { //if database entry not in sync according to current settings! -> do not set direction based on async status! if (dbEntry && !stillInSync(dbEntry->second, cmpVar, fileTimeTolerance, optTimeShiftHours)) - fileObj.setSyncDirConflict(txtDbNotInSync); + file.setSyncDirConflict(txtDbNotInSync); else - fileObj.setSyncDir(changeOnLeft ? SyncDirection::RIGHT : SyncDirection::LEFT); + file.setSyncDir(changeOnLeft ? SyncDirection::RIGHT : SyncDirection::LEFT); } else { if (changeOnLeft) - fileObj.setSyncDirConflict(txtBothSidesChanged); + file.setSyncDirConflict(txtBothSidesChanged); else - fileObj.setSyncDirConflict(txtNoSideChanged); + file.setSyncDirConflict(txtNoSideChanged); } } - void processSymlink(SymlinkPair& linkObj, const InSyncDir* dbFolder) const + void processSymlink(SymlinkPair& symlink, const InSyncFolder* dbFolder) const { - const CompareSymlinkResult cat = linkObj.getLinkCategory(); + const CompareSymlinkResult cat = symlink.getLinkCategory(); if (cat == SYMLINK_EQUAL) return; //try to find corresponding database entry - const InSyncDir::LinkList::value_type* dbEntry = nullptr; + const InSyncFolder::SymlinkList::value_type* dbEntry = nullptr; if (dbFolder) { - auto it = dbFolder->symlinks.find(linkObj.getPairShortName()); + auto it = dbFolder->symlinks.find(symlink.getPairItemName()); if (it != dbFolder->symlinks.end()) dbEntry = &*it; } //evaluation - const bool changeOnLeft = !matchesDbEntry<LEFT_SIDE >(linkObj, dbEntry, optTimeShiftHours); - const bool changeOnRight = !matchesDbEntry<RIGHT_SIDE>(linkObj, dbEntry, optTimeShiftHours); + const bool changeOnLeft = !matchesDbEntry<LEFT_SIDE >(symlink, dbEntry, optTimeShiftHours); + const bool changeOnRight = !matchesDbEntry<RIGHT_SIDE>(symlink, dbEntry, optTimeShiftHours); if (changeOnLeft != changeOnRight) { //if database entry not in sync according to current settings! -> do not set direction based on async status! if (dbEntry && !stillInSync(dbEntry->second, cmpVar, fileTimeTolerance, optTimeShiftHours)) - linkObj.setSyncDirConflict(txtDbNotInSync); + symlink.setSyncDirConflict(txtDbNotInSync); else - linkObj.setSyncDir(changeOnLeft ? SyncDirection::RIGHT : SyncDirection::LEFT); + symlink.setSyncDir(changeOnLeft ? SyncDirection::RIGHT : SyncDirection::LEFT); } else { if (changeOnLeft) - linkObj.setSyncDirConflict(txtBothSidesChanged); + symlink.setSyncDirConflict(txtBothSidesChanged); else - linkObj.setSyncDirConflict(txtNoSideChanged); + symlink.setSyncDirConflict(txtNoSideChanged); } } - void processDir(DirPair& dirObj, const InSyncDir* dbFolder) const + void processDir(FolderPair& folder, const InSyncFolder* dbFolder) const { - const CompareDirResult cat = dirObj.getDirCategory(); + const CompareDirResult cat = folder.getDirCategory(); //########### schedule abandoned temporary recycle bin directory for deletion ########## - if (cat == DIR_LEFT_SIDE_ONLY && endsWith(dirObj.getItemName<LEFT_SIDE>(), ABF::TEMP_FILE_ENDING)) - return setSyncDirectionRec(SyncDirection::LEFT, dirObj); // - else if (cat == DIR_RIGHT_SIDE_ONLY && endsWith(dirObj.getItemName<RIGHT_SIDE>(), ABF::TEMP_FILE_ENDING)) - return setSyncDirectionRec(SyncDirection::RIGHT, dirObj); //don't recurse below! + if (cat == DIR_LEFT_SIDE_ONLY && endsWith(folder.getItemName<LEFT_SIDE>(), 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)) + return setSyncDirectionRec(SyncDirection::RIGHT, folder); //don't recurse below! //####################################################################################### //try to find corresponding database entry - const InSyncDir::DirList::value_type* dbEntry = nullptr; + const InSyncFolder::FolderList::value_type* dbEntry = nullptr; if (dbFolder) { - auto it = dbFolder->dirs.find(dirObj.getPairShortName()); - if (it != dbFolder->dirs.end()) + auto it = dbFolder->folders.find(folder.getPairItemName()); + if (it != dbFolder->folders.end()) dbEntry = &*it; } if (cat != DIR_EQUAL) { //evaluation - const bool changeOnLeft = !matchesDbEntry<LEFT_SIDE >(dirObj, dbEntry); - const bool changeOnRight = !matchesDbEntry<RIGHT_SIDE>(dirObj, dbEntry); + const bool changeOnLeft = !matchesDbEntry<LEFT_SIDE >(folder, dbEntry); + const bool changeOnRight = !matchesDbEntry<RIGHT_SIDE>(folder, dbEntry); if (changeOnLeft != changeOnRight) { //if database entry not in sync according to current settings! -> do not set direction based on async status! if (dbEntry && !stillInSync(dbEntry->second)) - dirObj.setSyncDirConflict(txtDbNotInSync); + folder.setSyncDirConflict(txtDbNotInSync); else - dirObj.setSyncDir(changeOnLeft ? SyncDirection::RIGHT : SyncDirection::LEFT); + folder.setSyncDir(changeOnLeft ? SyncDirection::RIGHT : SyncDirection::LEFT); } else { if (changeOnLeft) - dirObj.setSyncDirConflict(txtBothSidesChanged); + folder.setSyncDirConflict(txtBothSidesChanged); else - dirObj.setSyncDirConflict(txtNoSideChanged); + folder.setSyncDirConflict(txtNoSideChanged); } } - recurse(dirObj, dbEntry ? &dbEntry->second : nullptr); + recurse(folder, dbEntry ? &dbEntry->second : nullptr); } const std::wstring txtBothSidesChanged; @@ -667,19 +667,19 @@ std::vector<DirectionConfig> zen::extractDirectionCfg(const MainConfiguration& m void zen::redetermineSyncDirection(const DirectionConfig& dirCfg, - BaseDirPair& baseDirectory, + BaseFolderPair& baseFolder, const std::function<void(const std::wstring& msg)>& reportWarning, const std::function<void(std::int64_t bytesDelta)>& onUpdateStatus) { //try to load sync-database files - std::shared_ptr<InSyncDir> lastSyncState; + std::shared_ptr<InSyncFolder> lastSyncState; if (dirCfg.var == DirectionConfig::TWOWAY || detectMovedFilesEnabled(dirCfg)) try { - if (allItemsCategoryEqual(baseDirectory)) + if (allItemsCategoryEqual(baseFolder)) return; //nothing to do: abort and don't even try to open db files - lastSyncState = loadLastSynchronousState(baseDirectory, onUpdateStatus); //throw FileError, FileErrorDatabaseNotExisting + lastSyncState = loadLastSynchronousState(baseFolder, onUpdateStatus); //throw FileError, FileErrorDatabaseNotExisting } catch (FileErrorDatabaseNotExisting&) {} //let's ignore this error, there's no value in reporting it other than confuse users catch (const FileError& e) //e.g. incompatible database version @@ -694,16 +694,16 @@ void zen::redetermineSyncDirection(const DirectionConfig& dirCfg, if (dirCfg.var == DirectionConfig::TWOWAY) { if (lastSyncState) - RedetermineTwoWay::execute(baseDirectory, *lastSyncState); + RedetermineTwoWay::execute(baseFolder, *lastSyncState); else //default fallback - Redetermine::execute(getTwoWayUpdateSet(), baseDirectory); + Redetermine::execute(getTwoWayUpdateSet(), baseFolder); } else - Redetermine::execute(extractDirections(dirCfg), baseDirectory); + Redetermine::execute(extractDirections(dirCfg), baseFolder); //detect renamed files if (lastSyncState) - DetectMovedFiles::execute(baseDirectory, *lastSyncState); + DetectMovedFiles::execute(baseFolder, *lastSyncState); } @@ -731,30 +731,30 @@ void zen::redetermineSyncDirection(const MainConfiguration& mainCfg, struct SetNewDirection { - static void execute(FilePair& fileObj, SyncDirection newDirection) + static void execute(FilePair& file, SyncDirection newDirection) { - if (fileObj.getCategory() != FILE_EQUAL) - fileObj.setSyncDir(newDirection); + if (file.getCategory() != FILE_EQUAL) + file.setSyncDir(newDirection); } - static void execute(SymlinkPair& linkObj, SyncDirection newDirection) + static void execute(SymlinkPair& symlink, SyncDirection newDirection) { - if (linkObj.getLinkCategory() != SYMLINK_EQUAL) - linkObj.setSyncDir(newDirection); + if (symlink.getLinkCategory() != SYMLINK_EQUAL) + symlink.setSyncDir(newDirection); } - static void execute(DirPair& dirObj, SyncDirection newDirection) + static void execute(FolderPair& folder, SyncDirection newDirection) { - if (dirObj.getDirCategory() != DIR_EQUAL) - dirObj.setSyncDir(newDirection); + if (folder.getDirCategory() != DIR_EQUAL) + folder.setSyncDir(newDirection); //recurse: - for (FilePair& fileObj : dirObj.refSubFiles()) - execute(fileObj, newDirection); - for (SymlinkPair& linkObj : dirObj.refSubLinks()) - execute(linkObj, newDirection); - for (DirPair& dirObj2 : dirObj.refSubDirs()) - execute(dirObj2, newDirection); + for (FilePair& file : folder.refSubFiles()) + execute(file, newDirection); + for (SymlinkPair& link : folder.refSubLinks()) + execute(link, newDirection); + for (FolderPair& subFolder : folder.refSubFolders()) + execute(subFolder, newDirection); } }; @@ -765,20 +765,20 @@ void zen::setSyncDirectionRec(SyncDirection newDirection, FileSystemObject& fsOb struct Recurse: public FSObjectVisitor { Recurse(SyncDirection newDir) : newDir_(newDir) {} - void visit(const FilePair& fileObj) override + void visit(const FilePair& file) override { - SetNewDirection::execute(const_cast<FilePair&>(fileObj), newDir_); //phyiscal object is not const in this method anyway + SetNewDirection::execute(const_cast<FilePair&>(file), newDir_); //phyiscal object is not const in this method anyway } - void visit(const SymlinkPair& linkObj) override + void visit(const SymlinkPair& symlink) override { - SetNewDirection::execute(const_cast<SymlinkPair&>(linkObj), newDir_); // + SetNewDirection::execute(const_cast<SymlinkPair&>(symlink), newDir_); // } - void visit(const DirPair& dirObj) override + void visit(const FolderPair& folder) override { - SetNewDirection::execute(const_cast<DirPair&>(dirObj), newDir_); // + SetNewDirection::execute(const_cast<FolderPair&>(folder), newDir_); // } private: - SyncDirection newDir_; + const SyncDirection newDir_; } setDirVisitor(newDirection); fsObj.accept(setDirVisitor); } @@ -790,14 +790,14 @@ namespace template <bool include> void inOrExcludeAllRows(zen::HierarchyObject& hierObj) { - for (FilePair& fileObj : hierObj.refSubFiles()) - fileObj.setActive(include); - for (SymlinkPair& linkObj : hierObj.refSubLinks()) - linkObj.setActive(include); - for (DirPair& dirObj : hierObj.refSubDirs()) + for (FilePair& file : hierObj.refSubFiles()) + file.setActive(include); + for (SymlinkPair& link : hierObj.refSubLinks()) + link.setActive(include); + for (FolderPair& folder : hierObj.refSubFolders()) { - dirObj.setActive(include); - inOrExcludeAllRows<include>(dirObj); //recurse + folder.setActive(include); + inOrExcludeAllRows<include>(folder); //recurse } } } @@ -806,9 +806,9 @@ void inOrExcludeAllRows(zen::HierarchyObject& hierObj) void zen::setActiveStatus(bool newStatus, zen::FolderComparison& folderCmp) { if (newStatus) - std::for_each(begin(folderCmp), end(folderCmp), [](BaseDirPair& baseDirObj) { inOrExcludeAllRows<true>(baseDirObj); }); //include all rows + std::for_each(begin(folderCmp), end(folderCmp), [](BaseFolderPair& baseFolder) { inOrExcludeAllRows<true>(baseFolder); }); //include all rows else - std::for_each(begin(folderCmp), end(folderCmp), [](BaseDirPair& baseDirObj) { inOrExcludeAllRows<false>(baseDirObj); }); //exclude all rows + std::for_each(begin(folderCmp), end(folderCmp), [](BaseFolderPair& baseFolder) { inOrExcludeAllRows<false>(baseFolder); }); //exclude all rows } @@ -820,14 +820,14 @@ void zen::setActiveStatus(bool newStatus, zen::FileSystemObject& fsObj) struct Recurse: public FSObjectVisitor { Recurse(bool newStat) : newStatus_(newStat) {} - void visit(const FilePair& fileObj) override {} - void visit(const SymlinkPair& linkObj) override {} - void visit(const DirPair& dirObj) override + void visit(const FilePair& file) override {} + void visit(const SymlinkPair& link) override {} + void visit(const FolderPair& folder) override { if (newStatus_) - inOrExcludeAllRows<true>(const_cast<DirPair&>(dirObj)); //object is not physically const here anyway + inOrExcludeAllRows<true>(const_cast<FolderPair&>(folder)); //object is not physically const here anyway else - inOrExcludeAllRows<false>(const_cast<DirPair&>(dirObj)); // + inOrExcludeAllRows<false>(const_cast<FolderPair&>(folder)); // } private: const bool newStatus_; @@ -872,41 +872,41 @@ private: void recurse(HierarchyObject& hierObj) const { - for (FilePair& fileObj : hierObj.refSubFiles()) - processFile(fileObj); - for (SymlinkPair& linkObj : hierObj.refSubLinks()) - processLink(linkObj); - for (DirPair& dirObj : hierObj.refSubDirs()) - processDir(dirObj); - }; + for (FilePair& file : hierObj.refSubFiles()) + processFile(file); + for (SymlinkPair& link : hierObj.refSubLinks()) + processLink(link); + for (FolderPair& folder : hierObj.refSubFolders()) + processDir(folder); + } - void processFile(FilePair& fileObj) const + void processFile(FilePair& file) const { - if (Eval<strategy>::process(fileObj)) - fileObj.setActive(filterProc.passFileFilter(fileObj.getPairRelativePath())); + if (Eval<strategy>::process(file)) + file.setActive(filterProc.passFileFilter(file.getPairRelativePath())); } - void processLink(SymlinkPair& linkObj) const + void processLink(SymlinkPair& symlink) const { - if (Eval<strategy>::process(linkObj)) - linkObj.setActive(filterProc.passFileFilter(linkObj.getPairRelativePath())); + if (Eval<strategy>::process(symlink)) + symlink.setActive(filterProc.passFileFilter(symlink.getPairRelativePath())); } - void processDir(DirPair& dirObj) const + void processDir(FolderPair& folder) const { bool childItemMightMatch = true; - const bool filterPassed = filterProc.passDirFilter(dirObj.getPairRelativePath(), &childItemMightMatch); + const bool filterPassed = filterProc.passDirFilter(folder.getPairRelativePath(), &childItemMightMatch); - if (Eval<strategy>::process(dirObj)) - dirObj.setActive(filterPassed); + if (Eval<strategy>::process(folder)) + folder.setActive(filterPassed); if (!childItemMightMatch) //use same logic like directory traversing here: evaluate filter in subdirs only if objects could match { - inOrExcludeAllRows<false>(dirObj); //exclude all files dirs in subfolders => incompatible with STRATEGY_OR! + inOrExcludeAllRows<false>(folder); //exclude all files dirs in subfolders => incompatible with STRATEGY_OR! return; } - recurse(dirObj); + recurse(folder); } const HardFilter& filterProc; @@ -924,24 +924,24 @@ private: void recurse(zen::HierarchyObject& hierObj) const { - for (FilePair& fileObj : hierObj.refSubFiles()) - processFile(fileObj); - for (SymlinkPair& linkObj : hierObj.refSubLinks()) - processLink(linkObj); - for (DirPair& dirObj : hierObj.refSubDirs()) - processDir(dirObj); - }; + for (FilePair& file : hierObj.refSubFiles()) + processFile(file); + for (SymlinkPair& link : hierObj.refSubLinks()) + processLink(link); + for (FolderPair& folder : hierObj.refSubFolders()) + processDir(folder); + } - void processFile(FilePair& fileObj) const + void processFile(FilePair& file) const { - if (Eval<strategy>::process(fileObj)) + if (Eval<strategy>::process(file)) { - if (fileObj.isEmpty<LEFT_SIDE>()) - fileObj.setActive(matchSize<RIGHT_SIDE>(fileObj) && - matchTime<RIGHT_SIDE>(fileObj)); - else if (fileObj.isEmpty<RIGHT_SIDE>()) - fileObj.setActive(matchSize<LEFT_SIDE>(fileObj) && - matchTime<LEFT_SIDE>(fileObj)); + 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)); else { //the only case with partially unclear semantics: @@ -959,34 +959,34 @@ private: ------------ */ //let's set ? := E - fileObj.setActive((matchSize<RIGHT_SIDE>(fileObj) && - matchTime<RIGHT_SIDE>(fileObj)) || - (matchSize<LEFT_SIDE>(fileObj) && - matchTime<LEFT_SIDE>(fileObj))); + file.setActive((matchSize<RIGHT_SIDE>(file) && + matchTime<RIGHT_SIDE>(file)) || + (matchSize<LEFT_SIDE>(file) && + matchTime<LEFT_SIDE>(file))); } } } - void processLink(SymlinkPair& linkObj) const + void processLink(SymlinkPair& symlink) const { - if (Eval<strategy>::process(linkObj)) + if (Eval<strategy>::process(symlink)) { - if (linkObj.isEmpty<LEFT_SIDE>()) - linkObj.setActive(matchTime<RIGHT_SIDE>(linkObj)); - else if (linkObj.isEmpty<RIGHT_SIDE>()) - linkObj.setActive(matchTime<LEFT_SIDE>(linkObj)); + if (symlink.isEmpty<LEFT_SIDE>()) + symlink.setActive(matchTime<RIGHT_SIDE>(symlink)); + else if (symlink.isEmpty<RIGHT_SIDE>()) + symlink.setActive(matchTime<LEFT_SIDE>(symlink)); else - linkObj.setActive(matchTime<RIGHT_SIDE>(linkObj) || - matchTime<LEFT_SIDE> (linkObj)); + symlink.setActive(matchTime<RIGHT_SIDE>(symlink) || + matchTime<LEFT_SIDE> (symlink)); } } - void processDir(DirPair& dirObj) const + void processDir(FolderPair& folder) const { - if (Eval<strategy>::process(dirObj)) - dirObj.setActive(timeSizeFilter_.matchFolder()); //if date filter is active we deactivate all folders: effectively gets rid of empty folders! + if (Eval<strategy>::process(folder)) + folder.setActive(timeSizeFilter_.matchFolder()); //if date filter is active we deactivate all folders: effectively gets rid of empty folders! - recurse(dirObj); + recurse(folder); } template <SelectedSide side, class T> @@ -1006,16 +1006,16 @@ private: } -void zen::addHardFiltering(BaseDirPair& baseDirObj, const Zstring& excludeFilter) +void zen::addHardFiltering(BaseFolderPair& baseFolder, const Zstring& excludeFilter) { - ApplyHardFilter<STRATEGY_AND>::execute(baseDirObj, NameFilter(FilterConfig().includeFilter, excludeFilter)); + ApplyHardFilter<STRATEGY_AND>::execute(baseFolder, NameFilter(FilterConfig().includeFilter, excludeFilter)); } -void zen::addSoftFiltering(BaseDirPair& baseDirObj, const SoftFilter& timeSizeFilter) +void zen::addSoftFiltering(BaseFolderPair& baseFolder, const SoftFilter& timeSizeFilter) { if (!timeSizeFilter.isNull()) //since we use STRATEGY_AND, we may skip a "null" filter - ApplySoftFilter<STRATEGY_AND>::execute(baseDirObj, timeSizeFilter); + ApplySoftFilter<STRATEGY_AND>::execute(baseFolder, timeSizeFilter); } @@ -1035,15 +1035,15 @@ void zen::applyFiltering(FolderComparison& folderCmp, const MainConfiguration& m for (auto it = allPairs.begin(); it != allPairs.end(); ++it) { - BaseDirPair& baseDirectory = *folderCmp[it - allPairs.begin()]; + BaseFolderPair& baseFolder = *folderCmp[it - allPairs.begin()]; const NormalizedFilter normFilter = normalizeFilters(mainCfg.globalFilter, it->localFilter); //"set" hard filter - ApplyHardFilter<STRATEGY_SET>::execute(baseDirectory, *normFilter.nameFilter); + ApplyHardFilter<STRATEGY_SET>::execute(baseFolder, *normFilter.nameFilter); //"and" soft filter - addSoftFiltering(baseDirectory, normFilter.timeSizeFilter); + addSoftFiltering(baseFolder, normFilter.timeSizeFilter); } } @@ -1062,40 +1062,40 @@ private: void recurse(HierarchyObject& hierObj) const { - for (FilePair& fileObj : hierObj.refSubFiles()) - processFile(fileObj); - for (SymlinkPair& linkObj : hierObj.refSubLinks()) - processLink(linkObj); - for (DirPair& dirObj : hierObj.refSubDirs()) - processDir(dirObj); - }; + for (FilePair& file : hierObj.refSubFiles()) + processFile(file); + for (SymlinkPair& link : hierObj.refSubLinks()) + processLink(link); + for (FolderPair& folder : hierObj.refSubFolders()) + processDir(folder); + } - void processFile(FilePair& fileObj) const + void processFile(FilePair& file) const { - if (fileObj.isEmpty<LEFT_SIDE>()) - fileObj.setActive(matchTime<RIGHT_SIDE>(fileObj)); - else if (fileObj.isEmpty<RIGHT_SIDE>()) - fileObj.setActive(matchTime<LEFT_SIDE>(fileObj)); + if (file.isEmpty<LEFT_SIDE>()) + file.setActive(matchTime<RIGHT_SIDE>(file)); + else if (file.isEmpty<RIGHT_SIDE>()) + file.setActive(matchTime<LEFT_SIDE>(file)); else - fileObj.setActive(matchTime<RIGHT_SIDE>(fileObj) || - matchTime<LEFT_SIDE >(fileObj)); + file.setActive(matchTime<RIGHT_SIDE>(file) || + matchTime<LEFT_SIDE>(file)); } - void processLink(SymlinkPair& linkObj) const + void processLink(SymlinkPair& link) const { - if (linkObj.isEmpty<LEFT_SIDE>()) - linkObj.setActive(matchTime<RIGHT_SIDE>(linkObj)); - else if (linkObj.isEmpty<RIGHT_SIDE>()) - linkObj.setActive(matchTime<LEFT_SIDE>(linkObj)); + if (link.isEmpty<LEFT_SIDE>()) + link.setActive(matchTime<RIGHT_SIDE>(link)); + else if (link.isEmpty<RIGHT_SIDE>()) + link.setActive(matchTime<LEFT_SIDE>(link)); else - linkObj.setActive(matchTime<RIGHT_SIDE>(linkObj) || - matchTime<LEFT_SIDE> (linkObj)); + link.setActive(matchTime<RIGHT_SIDE>(link) || + matchTime<LEFT_SIDE> (link)); } - void processDir(DirPair& dirObj) const + void processDir(FolderPair& folder) const { - dirObj.setActive(false); - recurse(dirObj); + folder.setActive(false); + recurse(folder); } template <SelectedSide side, class T> @@ -1112,7 +1112,7 @@ private: void zen::applyTimeSpanFilter(FolderComparison& folderCmp, std::int64_t timeFrom, std::int64_t timeTo) { - std::for_each(begin(folderCmp), end(folderCmp), [&](BaseDirPair& baseDirObj) { FilterByTimeSpan::execute(baseDirObj, timeFrom, timeTo); }); + std::for_each(begin(folderCmp), end(folderCmp), [&](BaseFolderPair& baseFolder) { FilterByTimeSpan::execute(baseFolder, timeFrom, timeTo); }); } //############################################################################################################ @@ -1127,14 +1127,14 @@ std::pair<std::wstring, int> zen::getSelectedItemsAsString(const std::vector<Fil for (const FileSystemObject* fsObj : selectionLeft) if (!fsObj->isEmpty<LEFT_SIDE>()) { - fileList += ABF::getDisplayPath(fsObj->getAbstractPath<LEFT_SIDE>()) + L'\n'; + fileList += AFS::getDisplayPath(fsObj->getAbstractPath<LEFT_SIDE>()) + L'\n'; ++totalDelCount; } for (const FileSystemObject* fsObj : selectionRight) if (!fsObj->isEmpty<RIGHT_SIDE>()) { - fileList += ABF::getDisplayPath(fsObj->getAbstractPath<RIGHT_SIDE>()) + L'\n'; + fileList += AFS::getDisplayPath(fsObj->getAbstractPath<RIGHT_SIDE>()) + L'\n'; ++totalDelCount; } @@ -1147,32 +1147,32 @@ namespace struct FSObjectLambdaVisitor : public FSObjectVisitor { static void visit(FileSystemObject& fsObj, - const std::function<void(const DirPair& dirObj )>& onDir, - const std::function<void(const FilePair& fileObj)>& onFile, - const std::function<void(const SymlinkPair& linkObj)>& onSymlink) + const std::function<void(const FolderPair& folder)>& onFolder, + const std::function<void(const FilePair& file )>& onFile, + const std::function<void(const SymlinkPair& link )>& onSymlink) { - FSObjectLambdaVisitor visitor(onDir, onFile, onSymlink); + FSObjectLambdaVisitor visitor(onFolder, onFile, onSymlink); fsObj.accept(visitor); } private: - FSObjectLambdaVisitor(const std::function<void(const DirPair& dirObj )>& onDir, - const std::function<void(const FilePair& fileObj)>& onFile, - const std::function<void(const SymlinkPair& linkObj)>& onSymlink) : onDir_(onDir), onFile_(onFile), onSymlink_(onSymlink) {} + FSObjectLambdaVisitor(const std::function<void(const FolderPair& folder)>& onFolder, + const std::function<void(const FilePair& file )>& onFile, + const std::function<void(const SymlinkPair& link )>& onSymlink) : onFolder_(onFolder), onFile_(onFile), onSymlink_(onSymlink) {} - void visit(const DirPair& dirObj ) override { if (onDir_) onDir_ (dirObj ); } - void visit(const FilePair& fileObj) override { if (onFile_) onFile_ (fileObj); } - void visit(const SymlinkPair& linkObj) override { if (onSymlink_) onSymlink_(linkObj); } + void visit(const FolderPair& folder) override { if (onFolder_ ) onFolder_ (folder); } + void visit(const FilePair& file ) override { if (onFile_ ) onFile_ (file); } + void visit(const SymlinkPair& link ) override { if (onSymlink_) onSymlink_(link); } - const std::function<void(const DirPair& dirObj )> onDir_; - const std::function<void(const FilePair& fileObj)> onFile_; - const std::function<void(const SymlinkPair& linkObj)> onSymlink_; + const std::function<void(const FolderPair& folder)> onFolder_; + const std::function<void(const FilePair& file )> onFile_; + const std::function<void(const SymlinkPair& link )> onSymlink_; }; template <SelectedSide side> void copyToAlternateFolderFrom(const std::vector<FileSystemObject*>& rowsToCopy, - ABF& abfTarget, + const AbstractPath& targetFolderPath, bool keepRelPaths, bool overwriteIfExists, ProcessCallback& callback) @@ -1186,65 +1186,62 @@ void copyToAlternateFolderFrom(const std::vector<FileSystemObject*>& rowsToCopy, const std::wstring txtCreatingFile (_("Creating file %x" )); const std::wstring txtCreatingLink (_("Creating symbolic link %x")); - auto copyItem = [&](FileSystemObject& fsObj, const Zstring& relPath) //throw FileError + auto copyItem = [&](FileSystemObject& fsObj, const AbstractPath& targetPath) //throw FileError { - const AbstractPathRef targetPath = abfTarget.getAbstractPath(relPath); - const std::function<void()> deleteTargetItem = [&] { if (overwriteIfExists) try { //file or (broken) file-symlink: - ABF::removeFile(targetPath); //throw FileError + AFS::removeFile(targetPath); //throw FileError } catch (FileError&) { //folder or folder-symlink: - if (ABF::folderExists(targetPath)) //directory or dir-symlink - ABF::removeFolderRecursively(targetPath, nullptr /*onBeforeFileDeletion*/, nullptr /*onBeforeFolderDeletion*/); //throw FileError + if (AFS::folderExists(targetPath)) //directory or dir-symlink + AFS::removeFolderRecursively(targetPath, nullptr /*onBeforeFileDeletion*/, nullptr /*onBeforeFolderDeletion*/); //throw FileError else throw; } }; - FSObjectLambdaVisitor::visit(fsObj, - [&](const DirPair& dirObj) + FSObjectLambdaVisitor::visit(fsObj, [&](const FolderPair& folder) { StatisticsReporter statReporter(1, 0, callback); - notifyItemCopy(txtCreatingFolder, ABF::getDisplayPath(targetPath)); + notifyItemCopy(txtCreatingFolder, AFS::getDisplayPath(targetPath)); try { //deleteTargetItem(); -> never delete pre-existing folders!!! => might delete child items we just copied! - ABF::copyNewFolder(dirObj.getAbstractPath<side>(), targetPath, false /*copyFilePermissions*/); //throw FileError + AFS::copyNewFolder(folder.getAbstractPath<side>(), targetPath, false /*copyFilePermissions*/); //throw FileError } - catch (const FileError&) { if (!ABF::folderExists(targetPath)) throw; } //might already exist: see creation of intermediate directories below + catch (const FileError&) { if (!AFS::folderExists(targetPath)) throw; } //might already exist: see creation of intermediate directories below statReporter.reportDelta(1, 0); statReporter.reportFinished(); }, - [&](const FilePair& fileObj) + [&](const FilePair& file) { - StatisticsReporter statReporter(1, fileObj.getFileSize<side>(), callback); - notifyItemCopy(txtCreatingFile, ABF::getDisplayPath(targetPath)); + StatisticsReporter statReporter(1, file.getFileSize<side>(), callback); + notifyItemCopy(txtCreatingFile, AFS::getDisplayPath(targetPath)); auto onNotifyCopyStatus = [&](std::int64_t bytesDelta) { statReporter.reportDelta(0, bytesDelta); }; - ABF::copyFileTransactional(fileObj.getAbstractPath<side>(), targetPath, //throw FileError, ErrorFileLocked + AFS::copyFileTransactional(file.getAbstractPath<side>(), targetPath, //throw FileError, ErrorFileLocked false /*copyFilePermissions*/, true /*transactionalCopy*/, deleteTargetItem, onNotifyCopyStatus); statReporter.reportDelta(1, 0); statReporter.reportFinished(); }, - [&](const SymlinkPair& linkObj) + [&](const SymlinkPair& symlink) { StatisticsReporter statReporter(1, 0, callback); - notifyItemCopy(txtCreatingLink, ABF::getDisplayPath(targetPath)); + notifyItemCopy(txtCreatingLink, AFS::getDisplayPath(targetPath)); deleteTargetItem(); - ABF::copySymlink(linkObj.getAbstractPath<side>(), targetPath, false /*copyFilePermissions*/); //throw FileError + AFS::copySymlink(symlink.getAbstractPath<side>(), targetPath, false /*copyFilePermissions*/); //throw FileError statReporter.reportDelta(1, 0); statReporter.reportFinished(); @@ -1255,19 +1252,21 @@ void copyToAlternateFolderFrom(const std::vector<FileSystemObject*>& rowsToCopy, tryReportingError([&] { const Zstring& relPath = keepRelPaths ? fsObj->getRelativePath<side>() : fsObj->getItemName<side>(); + const AbstractPath targetItemPath = AFS::appendRelPath(targetFolderPath, relPath); + try { - copyItem(*fsObj, relPath); //throw FileError + copyItem(*fsObj, targetItemPath); //throw FileError } catch (FileError&) { //create intermediate directories if missing - const AbstractPathRef targetParentPath = abfTarget.getAbstractPath(beforeLast(relPath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)); - if (!ABF::somethingExists(targetParentPath)) //->(minor) file system race condition! + const AbstractPath targetParentPath = AFS::appendRelPath(targetFolderPath, beforeLast(relPath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)); + if (!AFS::somethingExists(targetParentPath)) //->(minor) file system race condition! { - ABF::createFolderRecursively(targetParentPath); //throw FileError + AFS::createFolderRecursively(targetParentPath); //throw FileError //retry: this should work now! - copyItem(*fsObj, relPath); //throw FileError + copyItem(*fsObj, targetItemPath); //throw FileError } else throw; @@ -1294,18 +1293,18 @@ void zen::copyToAlternateFolder(const std::vector<FileSystemObject*>& rowsToCopy for (FileSystemObject* fsObj : itemSelectionLeft) FSObjectLambdaVisitor::visit(*fsObj, nullptr /*onDir*/, - [&](const FilePair& fileObj) {dataToProcess += static_cast<std::int64_t>(fileObj.getFileSize<LEFT_SIDE>()); }, nullptr /*onSymlink*/); + [&](const FilePair& file) {dataToProcess += static_cast<std::int64_t>(file.getFileSize<LEFT_SIDE>()); }, nullptr /*onSymlink*/); for (FileSystemObject* fsObj : itemSelectionRight) FSObjectLambdaVisitor::visit(*fsObj, nullptr /*onDir*/, - [&](const FilePair& fileObj) {dataToProcess += static_cast<std::int64_t>(fileObj.getFileSize<RIGHT_SIDE>()); }, nullptr /*onSymlink*/); + [&](const FilePair& file) {dataToProcess += static_cast<std::int64_t>(file.getFileSize<RIGHT_SIDE>()); }, nullptr /*onSymlink*/); callback.initNewPhase(itemCount, dataToProcess, ProcessCallback::PHASE_SYNCHRONIZING); //throw X - std::unique_ptr<ABF> abfTarget = createAbstractBaseFolder(targetFolderPathPhrase); + const AbstractPath targetFolderPath = createAbstractPath(targetFolderPathPhrase); - copyToAlternateFolderFrom<LEFT_SIDE >(itemSelectionLeft, *abfTarget, keepRelPaths, overwriteIfExists, callback); - copyToAlternateFolderFrom<RIGHT_SIDE>(itemSelectionRight, *abfTarget, keepRelPaths, overwriteIfExists, callback); + copyToAlternateFolderFrom<LEFT_SIDE >(itemSelectionLeft, targetFolderPath, keepRelPaths, overwriteIfExists, callback); + copyToAlternateFolderFrom<RIGHT_SIDE>(itemSelectionRight, targetFolderPath, keepRelPaths, overwriteIfExists, callback); } //############################################################################################################ @@ -1348,12 +1347,12 @@ void deleteFromGridAndHDOneSide(std::vector<FileSystemObject*>& rowsToDelete, if (!fsObj->isEmpty<side>()) //element may be implicitly deleted, e.g. if parent folder was deleted first { FSObjectLambdaVisitor::visit(*fsObj, - [&](const DirPair& dirObj) + [&](const FolderPair& folder) { if (useRecycleBin) { - notifyItemDeletion(txtRemovingDirectory, ABF::getDisplayPath(dirObj.getAbstractPath<side>())); - ABF::recycleItemDirectly(dirObj.getAbstractPath<side>()); //throw FileError + notifyItemDeletion(txtRemovingDirectory, AFS::getDisplayPath(folder.getAbstractPath<side>())); + AFS::recycleItemDirectly(folder.getAbstractPath<side>()); //throw FileError statReporter.reportDelta(1, 0); } else @@ -1369,33 +1368,33 @@ void deleteFromGridAndHDOneSide(std::vector<FileSystemObject*>& rowsToDelete, notifyItemDeletion(txtRemovingDirectory, displayPath); }; - ABF::removeFolderRecursively(dirObj.getAbstractPath<side>(), onBeforeFileDeletion, onBeforeDirDeletion); //throw FileError + AFS::removeFolderRecursively(folder.getAbstractPath<side>(), onBeforeFileDeletion, onBeforeDirDeletion); //throw FileError } }, - [&](const FilePair& fileObj) + [&](const FilePair& file) { - notifyItemDeletion(txtRemovingFile, ABF::getDisplayPath(fileObj.getAbstractPath<side>())); + notifyItemDeletion(txtRemovingFile, AFS::getDisplayPath(file.getAbstractPath<side>())); if (useRecycleBin) - ABF::recycleItemDirectly(fileObj.getAbstractPath<side>()); //throw FileError + AFS::recycleItemDirectly(file.getAbstractPath<side>()); //throw FileError else - ABF::removeFile(fileObj.getAbstractPath<side>()); //throw FileError + AFS::removeFile(file.getAbstractPath<side>()); //throw FileError statReporter.reportDelta(1, 0); }, - [&](const SymlinkPair& linkObj) + [&](const SymlinkPair& symlink) { - notifyItemDeletion(txtRemovingSymlink, ABF::getDisplayPath(linkObj.getAbstractPath<side>())); + notifyItemDeletion(txtRemovingSymlink, AFS::getDisplayPath(symlink.getAbstractPath<side>())); if (useRecycleBin) - ABF::recycleItemDirectly(linkObj.getAbstractPath<side>()); //throw FileError + AFS::recycleItemDirectly(symlink.getAbstractPath<side>()); //throw FileError else { - if (ABF::folderExists(linkObj.getAbstractPath<side>())) //dir symlink - ABF::removeFolderSimple(linkObj.getAbstractPath<side>()); //throw FileError + if (AFS::folderExists(symlink.getAbstractPath<side>())) //dir symlink + AFS::removeFolderSimple(symlink.getAbstractPath<side>()); //throw FileError else //file symlink, broken symlink - ABF::removeFile(linkObj.getAbstractPath<side>()); //throw FileError + AFS::removeFile(symlink.getAbstractPath<side>()); //throw FileError } statReporter.reportDelta(1, 0); }); @@ -1414,31 +1413,30 @@ void categorize(const std::vector<FileSystemObject*>& rows, std::vector<FileSystemObject*>& deletePermanent, std::vector<FileSystemObject*>& deleteRecyler, bool useRecycleBin, - std::map<const ABF*, bool, ABF::LessItemPath>& recyclerSupported, + std::map<AbstractPath, bool, AFS::LessAbstractPath>& recyclerSupported, ProcessCallback& callback) { - auto hasRecycler = [&](const ABF& baseFolder) -> bool + auto hasRecycler = [&](const AbstractPath& baseFolderPath) -> bool { - auto it = recyclerSupported.find(&baseFolder); //perf: avoid duplicate checks! + auto it = recyclerSupported.find(baseFolderPath); //perf: avoid duplicate checks! if (it != recyclerSupported.end()) return it->second; - const std::wstring msg = replaceCpy(_("Checking recycle bin availability for folder %x..."), L"%x", - fmtPath(ABF::getDisplayPath(baseFolder.getAbstractPath()))); + const std::wstring msg = replaceCpy(_("Checking recycle bin availability for folder %x..."), L"%x", fmtPath(AFS::getDisplayPath(baseFolderPath))); bool recSupported = false; tryReportingError([&]{ - recSupported = baseFolder.supportsRecycleBin([&] { callback.reportStatus(msg); /*may throw*/ }); //throw FileError + recSupported = AFS::supportsRecycleBin(baseFolderPath, [&] { callback.reportStatus(msg); /*may throw*/ }); //throw FileError }, callback); //throw X? - recyclerSupported.emplace(&baseFolder, recSupported); + recyclerSupported.emplace(baseFolderPath, recSupported); return recSupported; }; for (FileSystemObject* row : rows) if (!row->isEmpty<side>()) { - if (useRecycleBin && hasRecycler(row->root().getABF<side>())) //Windows' ::SHFileOperation() will delete permanently anyway, but we have a superior deletion routine + if (useRecycleBin && hasRecycler(row->base().getAbstractPath<side>())) //Windows' ::SHFileOperation() will delete permanently anyway, but we have a superior deletion routine deleteRecyler.push_back(row); else deletePermanent.push_back(row); @@ -1461,9 +1459,9 @@ void zen::deleteFromGridAndHD(const std::vector<FileSystemObject*>& rowsToDelete throw std::logic_error("Programming Error: Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__)); //build up mapping from base directory to corresponding direction config - std::unordered_map<const BaseDirPair*, DirectionConfig> baseDirCfgs; + std::unordered_map<const BaseFolderPair*, DirectionConfig> baseFolderCfgs; for (auto it = folderCmp.begin(); it != folderCmp.end(); ++it) - baseDirCfgs[&** it] = directCfgs[it - folderCmp.begin()]; + baseFolderCfgs[&** it] = directCfgs[it - folderCmp.begin()]; std::vector<FileSystemObject*> deleteLeft = rowsToDeleteOnLeft; std::vector<FileSystemObject*> deleteRight = rowsToDeleteOnRight; @@ -1489,9 +1487,9 @@ void zen::deleteFromGridAndHD(const std::vector<FileSystemObject*>& rowsToDelete if (fsObj.isEmpty<LEFT_SIDE>() != fsObj.isEmpty<RIGHT_SIDE>()) //make sure objects exists on one side only { - auto cfgIter = baseDirCfgs.find(&fsObj.root()); - assert(cfgIter != baseDirCfgs.end()); - if (cfgIter != baseDirCfgs.end()) + auto cfgIter = baseFolderCfgs.find(&fsObj.base()); + assert(cfgIter != baseFolderCfgs.end()); + if (cfgIter != baseFolderCfgs.end()) { SyncDirection newDir = SyncDirection::NONE; @@ -1509,7 +1507,7 @@ void zen::deleteFromGridAndHD(const std::vector<FileSystemObject*>& rowsToDelete } //last step: cleanup empty rows: this one invalidates all pointers! - std::for_each(begin(folderCmp), end(folderCmp), BaseDirPair::removeEmpty); + std::for_each(begin(folderCmp), end(folderCmp), BaseFolderPair::removeEmpty); }; ZEN_ON_SCOPE_EXIT(updateDirection()); //MSVC: assert is a macro and it doesn't play nice with ZEN_ON_SCOPE_EXIT, surprise... wasn't there something about macros being "evil"? @@ -1519,19 +1517,19 @@ void zen::deleteFromGridAndHD(const std::vector<FileSystemObject*>& rowsToDelete std::vector<FileSystemObject*> deleteRecylerLeft; std::vector<FileSystemObject*> deleteRecylerRight; - std::map<const ABF*, bool, ABF::LessItemPath> recyclerSupported; + std::map<AbstractPath, bool, AFS::LessAbstractPath> recyclerSupported; categorize<LEFT_SIDE >(deleteLeft, deletePermanentLeft, deleteRecylerLeft, useRecycleBin, recyclerSupported, callback); categorize<RIGHT_SIDE>(deleteRight, deletePermanentRight, deleteRecylerRight, useRecycleBin, recyclerSupported, callback); //windows: check if recycle bin really exists; if not, Windows will silently delete, which is wrong if (useRecycleBin && - std::any_of(recyclerSupported.begin(), recyclerSupported.end(), [](const decltype(recyclerSupported)::value_type& item) { return !item.second; })) + std::any_of(recyclerSupported.begin(), recyclerSupported.end(), [](const auto& item) { return !item.second; })) { std::wstring msg = _("The recycle bin is not available for the following folders. Files will be deleted permanently instead:") + L"\n"; for (const auto& item : recyclerSupported) if (!item.second) - msg += L"\n" + ABF::getDisplayPath(item.first->getAbstractPath()); + msg += L"\n" + AFS::getDisplayPath(item.first); callback.reportWarning(msg, warningRecyclerMissing); //throw? } diff --git a/FreeFileSync/Source/algorithm.h b/FreeFileSync/Source/algorithm.h index f35d9d23..f93811a5 100644 --- a/FreeFileSync/Source/algorithm.h +++ b/FreeFileSync/Source/algorithm.h @@ -20,7 +20,7 @@ void swapGrids(const MainConfiguration& config, FolderComparison& folderCmp); std::vector<DirectionConfig> extractDirectionCfg(const MainConfiguration& mainCfg); void redetermineSyncDirection(const DirectionConfig& directConfig, - BaseDirPair& baseDirectory, + BaseFolderPair& baseFolder, const std::function<void(const std::wstring& msg)>& reportWarning, const std::function<void(std::int64_t bytesDelta)>& onUpdateStatus); @@ -35,8 +35,8 @@ bool allElementsEqual(const FolderComparison& folderCmp); //filtering void applyFiltering (FolderComparison& folderCmp, const MainConfiguration& mainCfg); //full filter apply -void addHardFiltering(BaseDirPair& baseDirObj, const Zstring& excludeFilter); //exclude additional entries only -void addSoftFiltering(BaseDirPair& baseDirObj, const SoftFilter& timeSizeFilter); //exclude additional entries only +void addHardFiltering(BaseFolderPair& baseFolder, const Zstring& excludeFilter); //exclude additional entries only +void addSoftFiltering(BaseFolderPair& baseFolder, const SoftFilter& timeSizeFilter); //exclude additional entries only void applyTimeSpanFilter(FolderComparison& folderCmp, std::int64_t timeFrom, std::int64_t timeTo); //overwrite current active/inactive settings diff --git a/FreeFileSync/Source/application.cpp b/FreeFileSync/Source/application.cpp index ee1aac03..f3ceaa60 100644 --- a/FreeFileSync/Source/application.cpp +++ b/FreeFileSync/Source/application.cpp @@ -281,8 +281,6 @@ void Application::launch(const std::vector<Zstring>& commandArgs) raiseReturnCode(returnCode, FFS_RC_ABORTED); }; - auto equalNoCase = [](const Zstring& lhs, const Zstring& rhs) { return utfCvrtTo<wxString>(lhs).CmpNoCase(utfCvrtTo<wxString>(rhs)) == 0; }; - //parse command line arguments std::vector<Zstring> dirPathPhrasesLeft; std::vector<Zstring> dirPathPhrasesRight; @@ -413,7 +411,7 @@ void Application::launch(const std::vector<Zstring>& commandArgs) //distinguish sync scenarios: //--------------------------- - const Zstring globalConfigFilePath = globalConfigFile? *globalConfigFile : xmlAccess::getGlobalConfigFile(); + const Zstring globalConfigFilePath = globalConfigFile ? *globalConfigFile : xmlAccess::getGlobalConfigFile(); if (configFiles.empty()) { @@ -532,8 +530,11 @@ void showSyntaxHelp() showNotificationDialog(nullptr, DialogInfoType::INFO, PopupDialogCfg(). setTitle(_("Command line")). setDetailInstructions(_("Syntax:") + L"\n\n" + - +#ifdef ZEN_WIN L"FreeFileSync.exe " + L"\n" + +#elif defined ZEN_LINUX || defined ZEN_MAC + L"./FreeFileSync " + L"\n" + +#endif L" [" + _("global config file:") + L" GlobalSettings.xml]" + L"\n" + L" [" + _("config files:") + L" *.ffs_gui/*.ffs_batch]" + L"\n" + L" [-LeftDir " + _("directory") + L"] [-RightDir " + _("directory") + L"]" + L"\n" + diff --git a/FreeFileSync/Source/application.h b/FreeFileSync/Source/application.h index c866fe67..69250c32 100644 --- a/FreeFileSync/Source/application.h +++ b/FreeFileSync/Source/application.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef FREEFILESYNCAPP_H -#define FREEFILESYNCAPP_H +#ifndef APPLICATION_H_081568741942010985702395 +#define APPLICATION_H_081568741942010985702395 #include <vector> #include <zen/zstring.h> @@ -31,4 +31,4 @@ private: zen::FfsReturnCode returnCode; }; -#endif // FREEFILESYNCAPP_H +#endif //APPLICATION_H_081568741942010985702395 diff --git a/FreeFileSync/Source/comparison.cpp b/FreeFileSync/Source/comparison.cpp index 86c21a76..1a3cb6d5 100644 --- a/FreeFileSync/Source/comparison.cpp +++ b/FreeFileSync/Source/comparison.cpp @@ -46,20 +46,20 @@ namespace { struct ResolvedFolderPair { - ResolvedFolderPair(const std::shared_ptr<ABF>& left, - const std::shared_ptr<ABF>& right) : - abfLeft (left), - abfRight(right) {} + ResolvedFolderPair(const AbstractPath& left, + const AbstractPath& right) : + folderPathLeft (left), + folderPathRight(right) {} - std::shared_ptr<ABF> abfLeft; //always bound - std::shared_ptr<ABF> abfRight; // + AbstractPath folderPathLeft; + AbstractPath folderPathRight; }; struct ResolvedBaseFolders { std::vector<ResolvedFolderPair> resolvedPairs; - std::set<const ABF*, ABF::LessItemPath> existingBaseFolders; //references "resolvedPairs" variable!!! + std::set<AbstractPath, AFS::LessAbstractPath> existingBaseFolders; }; @@ -71,36 +71,36 @@ ResolvedBaseFolders initializeBaseFolders(const std::vector<FolderPairCfg>& cfgL tryReportingError([&] { - std::set<const ABF*, ABF::LessItemPath> uniqueBaseFolders; + std::set<AbstractPath, AFS::LessAbstractPath> uniqueBaseFolders; //support "retry" for environment variable and and variable driver letter resolution! output.resolvedPairs.clear(); for (const FolderPairCfg& fpCfg : cfgList) { - std::shared_ptr<ABF> abfLeft = createAbstractBaseFolder(fpCfg.folderPathPhraseLeft_); - std::shared_ptr<ABF> abfRight = createAbstractBaseFolder(fpCfg.folderPathPhraseRight_); + AbstractPath folderPathLeft = createAbstractPath(fpCfg.folderPathPhraseLeft_); + AbstractPath folderPathRight = createAbstractPath(fpCfg.folderPathPhraseRight_); - uniqueBaseFolders.insert(abfLeft .get()); - uniqueBaseFolders.insert(abfRight.get()); + uniqueBaseFolders.insert(folderPathLeft); + uniqueBaseFolders.insert(folderPathRight); - output.resolvedPairs.emplace_back(abfLeft, abfRight); + output.resolvedPairs.emplace_back(folderPathLeft, folderPathRight); } - const DirectoryStatus status = checkFolderExistenceUpdating(uniqueBaseFolders, allowUserInteraction, callback); //re-check *all* directories on each try! - output.existingBaseFolders = status.existingBaseFolder; + const FolderStatus status = getFolderStatusNonBlocking(uniqueBaseFolders, allowUserInteraction, callback); //re-check *all* directories on each try! + output.existingBaseFolders = status.existing; - if (!status.missingBaseFolder.empty() || !status.failedChecks.empty()) + if (!status.missing.empty() || !status.failedChecks.empty()) { std::wstring errorMsg = _("Cannot find the following folders:") + L"\n"; - for (const auto& baseFolder : status.missingBaseFolder) - errorMsg += L"\n" + ABF::getDisplayPath(baseFolder->getAbstractPath()); + for (const AbstractPath& folderPath : status.missing) + errorMsg += L"\n" + AFS::getDisplayPath(folderPath); for (const auto& fc : status.failedChecks) - errorMsg += L"\n" + ABF::getDisplayPath(fc.first->getAbstractPath()); + errorMsg += L"\n" + AFS::getDisplayPath(fc.first); errorMsg += L"\n\n"; - errorMsg += _("You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization."); + errorMsg += _("If you ignore this error the folders are considered empty. Missing folders are created automatically when needed."); if (!status.failedChecks.empty()) { @@ -123,9 +123,9 @@ void checkForIncompleteInput(const std::vector<ResolvedFolderPair>& folderPairs, bool haveFullPair = false; for (const ResolvedFolderPair& fp : folderPairs) - if (fp.abfLeft->emptyBaseFolderPath() != fp.abfRight->emptyBaseFolderPath()) + if (AFS::isNullPath(fp.folderPathLeft) != AFS::isNullPath(fp.folderPathRight)) havePartialPair = true; - else if (!fp.abfLeft->emptyBaseFolderPath()) + else if (!AFS::isNullPath(fp.folderPathLeft)) haveFullPair = true; if (havePartialPair == haveFullPair) //error if: all empty or exist both full and partial pairs -> support single-dir scenario @@ -141,9 +141,9 @@ void checkFolderDependency(const std::vector<ResolvedFolderPair>& folderPairs, b std::vector<ResolvedFolderPair> dependentFolderPairs; for (const ResolvedFolderPair& fp : folderPairs) - if (!fp.abfLeft->emptyBaseFolderPath() && !fp.abfRight->emptyBaseFolderPath()) //empty folders names may be accepted by user + if (!AFS::isNullPath(fp.folderPathLeft) && !AFS::isNullPath(fp.folderPathRight)) //empty folders names may be accepted by user //test wheter leftDirectory begins with rightDirectory or the other way round - if (ABF::havePathDependency(*fp.abfLeft, *fp.abfRight)) + if (AFS::havePathDependency(fp.folderPathLeft, fp.folderPathRight)) dependentFolderPairs.push_back(fp); if (!dependentFolderPairs.empty()) @@ -151,8 +151,8 @@ void checkFolderDependency(const std::vector<ResolvedFolderPair>& folderPairs, b std::wstring warningMsg = _("The following folder paths are dependent from each other:"); for (const ResolvedFolderPair& pair : dependentFolderPairs) warningMsg += L"\n\n" + - ABF::getDisplayPath(pair.abfLeft ->getAbstractPath()) + L"\n" + - ABF::getDisplayPath(pair.abfRight->getAbstractPath()); + AFS::getDisplayPath(pair.folderPathLeft) + L"\n" + + AFS::getDisplayPath(pair.folderPathRight); callback.reportWarning(warningMsg, warningDependentFolders); } @@ -165,18 +165,18 @@ class ComparisonBuffer public: ComparisonBuffer(const std::set<DirectoryKey>& keysToRead, ProcessCallback& callback); - //create comparison result table and fill category except for files existing on both sides: undefinedFiles and undefinedLinks are appended! - std::shared_ptr<BaseDirPair> compareByTimeSize(const ResolvedFolderPair& fp, const FolderPairCfg& fpConfig) const; - std::list<std::shared_ptr<BaseDirPair>> compareByContent(const std::vector<std::pair<ResolvedFolderPair, FolderPairCfg>>& workLoad) const; + //create comparison result table and fill category except for files existing on both sides: undefinedFiles and undefinedSymlinks are appended! + std::shared_ptr<BaseFolderPair> compareByTimeSize(const ResolvedFolderPair& fp, const FolderPairCfg& fpConfig) const; + std::list<std::shared_ptr<BaseFolderPair>> compareByContent(const std::vector<std::pair<ResolvedFolderPair, FolderPairCfg>>& workLoad) const; private: ComparisonBuffer (const ComparisonBuffer&) = delete; ComparisonBuffer& operator=(const ComparisonBuffer&) = delete; - std::shared_ptr<BaseDirPair> performComparison(const ResolvedFolderPair& fp, - const FolderPairCfg& fpCfg, - std::vector<FilePair*>& undefinedFiles, - std::vector<SymlinkPair*>& undefinedLinks) const; + std::shared_ptr<BaseFolderPair> performComparison(const ResolvedFolderPair& fp, + const FolderPairCfg& fpCfg, + std::vector<FilePair*>& undefinedFiles, + std::vector<SymlinkPair*>& undefinedSymlinks) const; std::map<DirectoryKey, DirectoryValue> directoryBuffer; //contains only *existing* directories ProcessCallback& callback_; @@ -243,17 +243,17 @@ std::wstring getConflictInvalidDate(const std::wstring& displayPath, std::int64_ //check for changed files with same modification date -std::wstring getConflictSameDateDiffSize(const FilePair& fileObj) +std::wstring getConflictSameDateDiffSize(const FilePair& file) { - return replaceCpy(_("Files %x have the same date but a different size."), L"%x", fmtPath(fileObj.getPairRelativePath())) + L"\n" + - L" " + arrowLeft + L" " + _("Date:") + L" " + utcToLocalTimeString(fileObj.getLastWriteTime<LEFT_SIDE >()) + L" " + _("Size:") + L" " + toGuiString(fileObj.getFileSize<LEFT_SIDE>()) + L"\n" + - L" " + arrowRight + L" " + _("Date:") + L" " + utcToLocalTimeString(fileObj.getLastWriteTime<RIGHT_SIDE>()) + L" " + _("Size:") + L" " + toGuiString(fileObj.getFileSize<RIGHT_SIDE>()); + return replaceCpy(_("Files %x have the same date but a different size."), L"%x", fmtPath(file.getPairRelativePath())) + L"\n" + + L" " + arrowLeft + L" " + _("Date:") + L" " + utcToLocalTimeString(file.getLastWriteTime<LEFT_SIDE >()) + L" " + _("Size:") + L" " + toGuiString(file.getFileSize<LEFT_SIDE>()) + L"\n" + + L" " + arrowRight + L" " + _("Date:") + L" " + utcToLocalTimeString(file.getLastWriteTime<RIGHT_SIDE>()) + L" " + _("Size:") + L" " + toGuiString(file.getFileSize<RIGHT_SIDE>()); } -std::wstring getConflictSkippedBinaryComparison(const FilePair& fileObj) +std::wstring getConflictSkippedBinaryComparison(const FilePair& file) { - return replaceCpy(_("Content comparison was skipped for excluded files %x."), L"%x", fmtPath(fileObj.getPairRelativePath())); + return replaceCpy(_("Content comparison was skipped for excluded files %x."), L"%x", fmtPath(file.getPairRelativePath())); } @@ -266,98 +266,98 @@ std::wstring getDescrDiffMetaShortnameCase(const FileSystemObject& fsObj) template <class FileOrLinkPair> -std::wstring getDescrDiffMetaDate(const FileOrLinkPair& fileObj) +std::wstring getDescrDiffMetaDate(const FileOrLinkPair& file) { return _("Items differ in attributes only") + L"\n" + - L" " + arrowLeft + L" " + _("Date:") + L" " + utcToLocalTimeString(fileObj.template getLastWriteTime<LEFT_SIDE >()) + L"\n" + - L" " + arrowRight + L" " + _("Date:") + L" " + utcToLocalTimeString(fileObj.template getLastWriteTime<RIGHT_SIDE>()); + L" " + arrowLeft + L" " + _("Date:") + L" " + utcToLocalTimeString(file.template getLastWriteTime<LEFT_SIDE >()) + L"\n" + + L" " + arrowRight + L" " + _("Date:") + L" " + utcToLocalTimeString(file.template getLastWriteTime<RIGHT_SIDE>()); } //----------------------------------------------------------------------------- -void categorizeSymlinkByTime(SymlinkPair& linkObj, int fileTimeTolerance, unsigned int optTimeShiftHours) +void categorizeSymlinkByTime(SymlinkPair& symlink, int fileTimeTolerance, unsigned int optTimeShiftHours) { //categorize symlinks that exist on both sides - switch (compareFileTime(linkObj.getLastWriteTime<LEFT_SIDE>(), - linkObj.getLastWriteTime<RIGHT_SIDE>(), fileTimeTolerance, optTimeShiftHours)) + switch (compareFileTime(symlink.getLastWriteTime<LEFT_SIDE>(), + symlink.getLastWriteTime<RIGHT_SIDE>(), fileTimeTolerance, optTimeShiftHours)) { case TimeResult::EQUAL: //Caveat: - //1. SYMLINK_EQUAL may only be set if short names match in case: InSyncDir's mapping tables use short name as a key! see db_file.cpp + //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 (linkObj.getItemName<LEFT_SIDE>() == linkObj.getItemName<RIGHT_SIDE>()) - linkObj.setCategory<FILE_EQUAL>(); + if (symlink.getItemName<LEFT_SIDE>() == symlink.getItemName<RIGHT_SIDE>()) + symlink.setCategory<FILE_EQUAL>(); else - linkObj.setCategoryDiffMetadata(getDescrDiffMetaShortnameCase(linkObj)); + symlink.setCategoryDiffMetadata(getDescrDiffMetaShortnameCase(symlink)); break; case TimeResult::LEFT_NEWER: - linkObj.setCategory<FILE_LEFT_NEWER>(); + symlink.setCategory<FILE_LEFT_NEWER>(); break; case TimeResult::RIGHT_NEWER: - linkObj.setCategory<FILE_RIGHT_NEWER>(); + symlink.setCategory<FILE_RIGHT_NEWER>(); break; case TimeResult::LEFT_INVALID: - linkObj.setCategoryConflict(getConflictInvalidDate(ABF::getDisplayPath(linkObj.getAbstractPath<LEFT_SIDE>()), linkObj.getLastWriteTime<LEFT_SIDE>())); + symlink.setCategoryConflict(getConflictInvalidDate(AFS::getDisplayPath(symlink.getAbstractPath<LEFT_SIDE>()), symlink.getLastWriteTime<LEFT_SIDE>())); break; case TimeResult::RIGHT_INVALID: - linkObj.setCategoryConflict(getConflictInvalidDate(ABF::getDisplayPath(linkObj.getAbstractPath<RIGHT_SIDE>()), linkObj.getLastWriteTime<RIGHT_SIDE>())); + symlink.setCategoryConflict(getConflictInvalidDate(AFS::getDisplayPath(symlink.getAbstractPath<RIGHT_SIDE>()), symlink.getLastWriteTime<RIGHT_SIDE>())); break; } } -std::shared_ptr<BaseDirPair> ComparisonBuffer::compareByTimeSize(const ResolvedFolderPair& fp, const FolderPairCfg& fpConfig) const +std::shared_ptr<BaseFolderPair> ComparisonBuffer::compareByTimeSize(const ResolvedFolderPair& fp, const FolderPairCfg& fpConfig) const { //do basis scan and retrieve files existing on both sides as "compareCandidates" std::vector<FilePair*> uncategorizedFiles; std::vector<SymlinkPair*> uncategorizedLinks; - std::shared_ptr<BaseDirPair> output = performComparison(fp, fpConfig, uncategorizedFiles, uncategorizedLinks); + std::shared_ptr<BaseFolderPair> output = performComparison(fp, fpConfig, uncategorizedFiles, uncategorizedLinks); //finish symlink categorization - for (SymlinkPair* linkObj : uncategorizedLinks) - categorizeSymlinkByTime(*linkObj, fpConfig.fileTimeTolerance, fpConfig.optTimeShiftHours); + for (SymlinkPair* symlink : uncategorizedLinks) + categorizeSymlinkByTime(*symlink, fpConfig.fileTimeTolerance, fpConfig.optTimeShiftHours); //categorize files that exist on both sides - for (FilePair* fileObj : uncategorizedFiles) + for (FilePair* file : uncategorizedFiles) { - switch (compareFileTime(fileObj->getLastWriteTime<LEFT_SIDE>(), - fileObj->getLastWriteTime<RIGHT_SIDE>(), fpConfig.fileTimeTolerance, fpConfig.optTimeShiftHours)) + switch (compareFileTime(file->getLastWriteTime<LEFT_SIDE>(), + file->getLastWriteTime<RIGHT_SIDE>(), fpConfig.fileTimeTolerance, fpConfig.optTimeShiftHours)) { case TimeResult::EQUAL: //Caveat: - //1. FILE_EQUAL may only be set if short names match in case: InSyncDir's mapping tables use short name as a key! see db_file.cpp + //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::syncTo() in file_hierarchy.cpp - if (fileObj->getFileSize<LEFT_SIDE>() == fileObj->getFileSize<RIGHT_SIDE>()) + if (file->getFileSize<LEFT_SIDE>() == file->getFileSize<RIGHT_SIDE>()) { - if (fileObj->getItemName<LEFT_SIDE>() == fileObj->getItemName<RIGHT_SIDE>()) - fileObj->setCategory<FILE_EQUAL>(); + if (file->getItemName<LEFT_SIDE>() == file->getItemName<RIGHT_SIDE>()) + file->setCategory<FILE_EQUAL>(); else - fileObj->setCategoryDiffMetadata(getDescrDiffMetaShortnameCase(*fileObj)); + file->setCategoryDiffMetadata(getDescrDiffMetaShortnameCase(*file)); } else - fileObj->setCategoryConflict(getConflictSameDateDiffSize(*fileObj)); //same date, different filesize + file->setCategoryConflict(getConflictSameDateDiffSize(*file)); //same date, different filesize break; case TimeResult::LEFT_NEWER: - fileObj->setCategory<FILE_LEFT_NEWER>(); + file->setCategory<FILE_LEFT_NEWER>(); break; case TimeResult::RIGHT_NEWER: - fileObj->setCategory<FILE_RIGHT_NEWER>(); + file->setCategory<FILE_RIGHT_NEWER>(); break; case TimeResult::LEFT_INVALID: - fileObj->setCategoryConflict(getConflictInvalidDate(ABF::getDisplayPath(fileObj->getAbstractPath<LEFT_SIDE>()), fileObj->getLastWriteTime<LEFT_SIDE>())); + file->setCategoryConflict(getConflictInvalidDate(AFS::getDisplayPath(file->getAbstractPath<LEFT_SIDE>()), file->getLastWriteTime<LEFT_SIDE>())); break; case TimeResult::RIGHT_INVALID: - fileObj->setCategoryConflict(getConflictInvalidDate(ABF::getDisplayPath(fileObj->getAbstractPath<RIGHT_SIDE>()), fileObj->getLastWriteTime<RIGHT_SIDE>())); + file->setCategoryConflict(getConflictInvalidDate(AFS::getDisplayPath(file->getAbstractPath<RIGHT_SIDE>()), file->getLastWriteTime<RIGHT_SIDE>())); break; } } @@ -365,55 +365,55 @@ std::shared_ptr<BaseDirPair> ComparisonBuffer::compareByTimeSize(const ResolvedF } -void categorizeSymlinkByContent(SymlinkPair& linkObj, int fileTimeTolerance, unsigned int optTimeShiftHours, ProcessCallback& callback) +void categorizeSymlinkByContent(SymlinkPair& symlink, int fileTimeTolerance, unsigned int optTimeShiftHours, ProcessCallback& callback) { //categorize symlinks that exist on both sides Zstring targetPathRawL; Zstring targetPathRawR; Opt<std::wstring> errMsg = tryReportingError([&] { - callback.reportStatus(replaceCpy(_("Resolving symbolic link %x"), L"%x", fmtPath(ABF::getDisplayPath(linkObj.getAbstractPath<LEFT_SIDE>())))); + callback.reportStatus(replaceCpy(_("Resolving symbolic link %x"), L"%x", fmtPath(AFS::getDisplayPath(symlink.getAbstractPath<LEFT_SIDE>())))); - targetPathRawL = ABF::getSymlinkContentBuffer(linkObj.getAbstractPath<LEFT_SIDE>()); //throw FileError + targetPathRawL = AFS::getSymlinkContentBuffer(symlink.getAbstractPath<LEFT_SIDE>()); //throw FileError - callback.reportStatus(replaceCpy(_("Resolving symbolic link %x"), L"%x", fmtPath(ABF::getDisplayPath(linkObj.getAbstractPath<RIGHT_SIDE>())))); - targetPathRawR = ABF::getSymlinkContentBuffer(linkObj.getAbstractPath<RIGHT_SIDE>()); //throw FileError + callback.reportStatus(replaceCpy(_("Resolving symbolic link %x"), L"%x", fmtPath(AFS::getDisplayPath(symlink.getAbstractPath<RIGHT_SIDE>())))); + targetPathRawR = AFS::getSymlinkContentBuffer(symlink.getAbstractPath<RIGHT_SIDE>()); //throw FileError }, callback); //throw X? if (errMsg) - linkObj.setCategoryConflict(*errMsg); + symlink.setCategoryConflict(*errMsg); else { if (targetPathRawL == targetPathRawR #ifdef ZEN_WIN //type of symbolic link is relevant for Windows only && - ABF::folderExists(linkObj.getAbstractPath<LEFT_SIDE >()) == //check if dir-symlink - ABF::folderExists(linkObj.getAbstractPath<RIGHT_SIDE>()) // + AFS::folderExists(symlink.getAbstractPath<LEFT_SIDE >()) == //check if dir-symlink + AFS::folderExists(symlink.getAbstractPath<RIGHT_SIDE>()) // #endif ) { //Caveat: - //1. SYMLINK_EQUAL may only be set if short names match in case: InSyncDir's mapping tables use short name as a key! see db_file.cpp + //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::syncTo() in file_hierarchy.cpp //symlinks have same "content" - if (linkObj.getItemName<LEFT_SIDE>() != linkObj.getItemName<RIGHT_SIDE>()) - linkObj.setCategoryDiffMetadata(getDescrDiffMetaShortnameCase(linkObj)); - else if (!sameFileTime(linkObj.getLastWriteTime<LEFT_SIDE>(), - linkObj.getLastWriteTime<RIGHT_SIDE>(), fileTimeTolerance, optTimeShiftHours)) - linkObj.setCategoryDiffMetadata(getDescrDiffMetaDate(linkObj)); + if (symlink.getItemName<LEFT_SIDE>() != symlink.getItemName<RIGHT_SIDE>()) + symlink.setCategoryDiffMetadata(getDescrDiffMetaShortnameCase(symlink)); + else if (!sameFileTime(symlink.getLastWriteTime<LEFT_SIDE>(), + symlink.getLastWriteTime<RIGHT_SIDE>(), fileTimeTolerance, optTimeShiftHours)) + symlink.setCategoryDiffMetadata(getDescrDiffMetaDate(symlink)); else - linkObj.setCategory<FILE_EQUAL>(); + symlink.setCategory<FILE_EQUAL>(); } else - linkObj.setCategory<FILE_DIFFERENT_CONTENT>(); + symlink.setCategory<FILE_DIFFERENT_CONTENT>(); } } -std::list<std::shared_ptr<BaseDirPair>> ComparisonBuffer::compareByContent(const std::vector<std::pair<ResolvedFolderPair, FolderPairCfg>>& workLoad) const +std::list<std::shared_ptr<BaseFolderPair>> ComparisonBuffer::compareByContent(const std::vector<std::pair<ResolvedFolderPair, FolderPairCfg>>& workLoad) const { - std::list<std::shared_ptr<BaseDirPair>> output; + std::list<std::shared_ptr<BaseFolderPair>> output; if (workLoad.empty()) return output; @@ -431,31 +431,31 @@ std::list<std::shared_ptr<BaseDirPair>> ComparisonBuffer::compareByContent(const //content comparison of file content happens AFTER finding corresponding files and AFTER filtering //in order to separate into two processes (scanning and comparing) - for (FilePair* fileObj : undefinedFiles) + for (FilePair* file : undefinedFiles) //pre-check: files have different content if they have a different filesize (must not be FILE_EQUAL: see InSyncFile) - if (fileObj->getFileSize<LEFT_SIDE>() != fileObj->getFileSize<RIGHT_SIDE>()) - fileObj->setCategory<FILE_DIFFERENT_CONTENT>(); + if (file->getFileSize<LEFT_SIDE>() != file->getFileSize<RIGHT_SIDE>()) + file->setCategory<FILE_DIFFERENT_CONTENT>(); else { //perf: skip binary comparison for excluded rows (e.g. via time span and size filter)! //both soft and hard filter were already applied in ComparisonBuffer::performComparison()! - if (!fileObj->isActive()) - fileObj->setCategoryConflict(getConflictSkippedBinaryComparison(*fileObj)); + if (!file->isActive()) + file->setCategoryConflict(getConflictSkippedBinaryComparison(*file)); else - filesToCompareBytewise.push_back(fileObj); + filesToCompareBytewise.push_back(file); } //finish symlink categorization - for (SymlinkPair* linkObj : uncategorizedLinks) - categorizeSymlinkByContent(*linkObj, w.second.fileTimeTolerance, w.second.optTimeShiftHours, callback_); + for (SymlinkPair* symlink : uncategorizedLinks) + categorizeSymlinkByContent(*symlink, w.second.fileTimeTolerance, w.second.optTimeShiftHours, callback_); } //finish categorization... const size_t objectsTotal = filesToCompareBytewise.size(); std::uint64_t bytesTotal = 0; //left and right filesizes are equal - for (FilePair* fileObj : filesToCompareBytewise) - bytesTotal += fileObj->getFileSize<LEFT_SIDE>(); + for (FilePair* file : filesToCompareBytewise) + bytesTotal += file->getFileSize<LEFT_SIDE>(); callback_.initNewPhase(static_cast<int>(objectsTotal), //may throw bytesTotal, @@ -466,46 +466,46 @@ std::list<std::shared_ptr<BaseDirPair>> ComparisonBuffer::compareByContent(const //PERF_START; //compare files (that have same size) bytewise... - for (FilePair* fileObj : filesToCompareBytewise) + for (FilePair* file : filesToCompareBytewise) { - callback_.reportStatus(replaceCpy(txtComparingContentOfFiles, L"%x", fmtPath(fileObj->getPairRelativePath()))); + callback_.reportStatus(replaceCpy(txtComparingContentOfFiles, L"%x", fmtPath(file->getPairRelativePath()))); //check files that exist in left and right model but have different content bool haveSameContent = false; Opt<std::wstring> errMsg = tryReportingError([&] { - StatisticsReporter statReporter(1, fileObj->getFileSize<LEFT_SIDE>(), callback_); + StatisticsReporter statReporter(1, file->getFileSize<LEFT_SIDE>(), callback_); auto onUpdateStatus = [&](std::int64_t bytesDelta) { statReporter.reportDelta(0, bytesDelta); }; - haveSameContent = filesHaveSameContent(fileObj->getAbstractPath<LEFT_SIDE>(), - fileObj->getAbstractPath<RIGHT_SIDE>(), onUpdateStatus); //throw FileError + haveSameContent = filesHaveSameContent(file->getAbstractPath<LEFT_SIDE>(), + file->getAbstractPath<RIGHT_SIDE>(), onUpdateStatus); //throw FileError statReporter.reportDelta(1, 0); statReporter.reportFinished(); }, callback_); //throw X? if (errMsg) - fileObj->setCategoryConflict(*errMsg); + file->setCategoryConflict(*errMsg); else { if (haveSameContent) { //Caveat: - //1. FILE_EQUAL may only be set if short names match in case: InSyncDir's mapping tables use short name as a key! see db_file.cpp + //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::syncTo() in file_hierarchy.cpp - if (fileObj->getItemName<LEFT_SIDE>() != fileObj->getItemName<RIGHT_SIDE>()) - fileObj->setCategoryDiffMetadata(getDescrDiffMetaShortnameCase(*fileObj)); - else if (!sameFileTime(fileObj->getLastWriteTime<LEFT_SIDE>(), - fileObj->getLastWriteTime<RIGHT_SIDE>(), fileObj->root().getFileTimeTolerance(), fileObj->root().getTimeShift())) - fileObj->setCategoryDiffMetadata(getDescrDiffMetaDate(*fileObj)); + if (file->getItemName<LEFT_SIDE>() != file->getItemName<RIGHT_SIDE>()) + file->setCategoryDiffMetadata(getDescrDiffMetaShortnameCase(*file)); + else if (!sameFileTime(file->getLastWriteTime<LEFT_SIDE>(), + file->getLastWriteTime<RIGHT_SIDE>(), file->base().getFileTimeTolerance(), file->base().getTimeShift())) + file->setCategoryDiffMetadata(getDescrDiffMetaDate(*file)); else - fileObj->setCategory<FILE_EQUAL>(); + file->setCategory<FILE_EQUAL>(); } else - fileObj->setCategory<FILE_DIFFERENT_CONTENT>(); + file->setCategory<FILE_DIFFERENT_CONTENT>(); } } return output; @@ -518,31 +518,31 @@ class MergeSides public: MergeSides(const std::map<Zstring, std::wstring, LessFilePath>& failedItemReads, std::vector<FilePair*>& undefinedFilesOut, - std::vector<SymlinkPair*>& undefinedLinksOut) : + std::vector<SymlinkPair*>& undefinedSymlinksOut) : failedItemReads_(failedItemReads), undefinedFiles(undefinedFilesOut), - undefinedLinks(undefinedLinksOut) {} + undefinedSymlinks(undefinedSymlinksOut) {} - void execute(const DirContainer& leftSide, const DirContainer& rightSide, HierarchyObject& output) + void execute(const FolderContainer& lhs, const FolderContainer& rhs, HierarchyObject& output) { auto it = failedItemReads_.find(Zstring()); //empty path if read-error for whole base directory - mergeTwoSides(leftSide, rightSide, + mergeTwoSides(lhs, rhs, it != failedItemReads_.end() ? &it->second : nullptr, output); } private: - void mergeTwoSides(const DirContainer& leftSide, const DirContainer& rightSide, const std::wstring* errorMsg, HierarchyObject& output); + void mergeTwoSides(const FolderContainer& lhs, const FolderContainer& rhs, const std::wstring* errorMsg, HierarchyObject& output); template <SelectedSide side> - void fillOneSide(const DirContainer& dirCont, const std::wstring* errorMsg, HierarchyObject& output); + void fillOneSide(const FolderContainer& folderCont, const std::wstring* errorMsg, HierarchyObject& output); const std::wstring* checkFailedRead(FileSystemObject& fsObj, const std::wstring* errorMsg); const std::map<Zstring, std::wstring, LessFilePath>& failedItemReads_; //base-relative paths or empty if read-error for whole base directory std::vector<FilePair*>& undefinedFiles; - std::vector<SymlinkPair*>& undefinedLinks; + std::vector<SymlinkPair*>& undefinedSymlinks; }; @@ -566,25 +566,25 @@ const std::wstring* MergeSides::checkFailedRead(FileSystemObject& fsObj, const s template <SelectedSide side> -void MergeSides::fillOneSide(const DirContainer& dirCont, const std::wstring* errorMsg, HierarchyObject& output) +void MergeSides::fillOneSide(const FolderContainer& folderCont, const std::wstring* errorMsg, HierarchyObject& output) { - for (const auto& file : dirCont.files) + for (const auto& file : folderCont.files) { FilePair& newItem = output.addSubFile<side>(file.first, file.second); checkFailedRead(newItem, errorMsg); } - for (const auto& link : dirCont.links) + for (const auto& symlink : folderCont.symlinks) { - SymlinkPair& newItem = output.addSubLink<side>(link.first, link.second); + SymlinkPair& newItem = output.addSubLink<side>(symlink.first, symlink.second); checkFailedRead(newItem, errorMsg); } - for (const auto& dir : dirCont.dirs) + for (const auto& dir : folderCont.folders) { - DirPair& newDir = output.addSubDir<side>(dir.first); - const std::wstring* errorMsgNew = checkFailedRead(newDir, errorMsg); - fillOneSide<side>(dir.second, errorMsgNew, newDir); //recurse + FolderPair& newFolder = output.addSubFolder<side>(dir.first); + const std::wstring* errorMsgNew = checkFailedRead(newFolder, errorMsg); + fillOneSide<side>(dir.second, errorMsgNew, newFolder); //recurse } } @@ -593,46 +593,46 @@ void MergeSides::fillOneSide(const DirContainer& dirCont, const std::wstring* er template <class MapType, class ProcessLeftOnly, class ProcessRightOnly, class ProcessBoth> inline void linearMerge(const MapType& mapLeft, const MapType& mapRight, ProcessLeftOnly lo, ProcessRightOnly ro, ProcessBoth bo) { - const auto lessVal = mapLeft.value_comp(); + const auto lessKey = typename MapType::key_compare(); - auto iterLeft = mapLeft .begin(); - auto iterRight = mapRight.begin(); + auto itL = mapLeft .begin(); + auto itR = mapRight.begin(); - auto finishLeft = [&] { std::for_each(iterLeft, mapLeft .end(), lo); }; - auto finishRight = [&] { std::for_each(iterRight, mapRight.end(), ro); }; + auto finishLeft = [&] { std::for_each(itL, mapLeft .end(), lo); }; + auto finishRight = [&] { std::for_each(itR, mapRight.end(), ro); }; - if (iterLeft == mapLeft .end()) return finishRight(); - if (iterRight == mapRight.end()) return finishLeft(); + if (itL == mapLeft .end()) return finishRight(); + if (itR == mapRight.end()) return finishLeft (); for (;;) - if (lessVal(*iterLeft, *iterRight)) + if (lessKey(itL->first, itR->first)) { - lo(*iterLeft); - if (++iterLeft == mapLeft.end()) + lo(*itL); + if (++itL == mapLeft.end()) return finishRight(); } - else if (lessVal(*iterRight, *iterLeft)) + else if (lessKey(itR->first, itL->first)) { - ro(*iterRight); - if (++iterRight == mapRight.end()) + ro(*itR); + if (++itR == mapRight.end()) return finishLeft(); } else { - bo(*iterLeft, *iterRight); - ++iterLeft; // - ++iterRight; //increment BOTH before checking for end of range! - if (iterLeft == mapLeft .end()) return finishRight(); - if (iterRight == mapRight.end()) return finishLeft(); + bo(*itL, *itR); + ++itL; // + ++itR; //increment BOTH before checking for end of range! + if (itL == mapLeft .end()) return finishRight(); + if (itR == mapRight.end()) return finishLeft (); } } -void MergeSides::mergeTwoSides(const DirContainer& leftSide, const DirContainer& rightSide, const std::wstring* errorMsg, HierarchyObject& output) +void MergeSides::mergeTwoSides(const FolderContainer& lhs, const FolderContainer& rhs, const std::wstring* errorMsg, HierarchyObject& output) { - typedef const DirContainer::FileList::value_type FileData; + typedef const FolderContainer::FileList::value_type FileData; - linearMerge(leftSide.files, rightSide.files, + linearMerge(lhs.files, rhs.files, [&](const FileData& fileLeft ) { FilePair& newItem = output.addSubFile<LEFT_SIDE >(fileLeft .first, fileLeft .second); checkFailedRead(newItem, errorMsg); }, //left only [&](const FileData& fileRight) { FilePair& newItem = output.addSubFile<RIGHT_SIDE>(fileRight.first, fileRight.second); checkFailedRead(newItem, errorMsg); }, //right only @@ -645,54 +645,54 @@ void MergeSides::mergeTwoSides(const DirContainer& leftSide, const DirContainer& fileRight.second); if (!checkFailedRead(newItem, errorMsg)) undefinedFiles.push_back(&newItem); - static_assert(IsSameType<HierarchyObject::SubFileVec, FixedList<FilePair>>::value, ""); //HierarchyObject::addSubFile() must NOT invalidate references used in "undefinedFiles"! + static_assert(IsSameType<HierarchyObject::FileList, FixedList<FilePair>>::value, ""); //HierarchyObject::addSubFile() must NOT invalidate references used in "undefinedFiles"! }); //----------------------------------------------------------------------------------------------- - typedef const DirContainer::LinkList::value_type LinkData; + typedef const FolderContainer::SymlinkList::value_type SymlinkData; - linearMerge(leftSide.links, rightSide.links, - [&](const LinkData& linkLeft) { SymlinkPair& newItem = output.addSubLink<LEFT_SIDE >(linkLeft .first, linkLeft .second); checkFailedRead(newItem, errorMsg); }, //left only - [&](const LinkData& linkRight) { SymlinkPair& newItem = output.addSubLink<RIGHT_SIDE>(linkRight.first, linkRight.second); checkFailedRead(newItem, errorMsg); }, //right only + linearMerge(lhs.symlinks, rhs.symlinks, + [&](const SymlinkData& symlinkLeft ) { SymlinkPair& newItem = output.addSubLink<LEFT_SIDE >(symlinkLeft .first, symlinkLeft .second); checkFailedRead(newItem, errorMsg); }, //left only + [&](const SymlinkData& symlinkRight) { SymlinkPair& newItem = output.addSubLink<RIGHT_SIDE>(symlinkRight.first, symlinkRight.second); checkFailedRead(newItem, errorMsg); }, //right only - [&](const LinkData& linkLeft, const LinkData& linkRight) //both sides + [&](const SymlinkData& symlinkLeft, const SymlinkData& symlinkRight) //both sides { - SymlinkPair& newItem = output.addSubLink(linkLeft.first, - linkLeft.second, + SymlinkPair& newItem = output.addSubLink(symlinkLeft.first, + symlinkLeft.second, SYMLINK_EQUAL, //dummy-value until categorization is finished later - linkRight.first, - linkRight.second); + symlinkRight.first, + symlinkRight.second); if (!checkFailedRead(newItem, errorMsg)) - undefinedLinks.push_back(&newItem); + undefinedSymlinks.push_back(&newItem); }); //----------------------------------------------------------------------------------------------- - typedef const DirContainer::DirList::value_type DirData; + typedef const FolderContainer::FolderList::value_type FolderData; - linearMerge(leftSide.dirs, rightSide.dirs, - [&](const DirData& dirLeft) //left only + linearMerge(lhs.folders, rhs.folders, + [&](const FolderData& dirLeft) //left only { - DirPair& newDir = output.addSubDir<LEFT_SIDE>(dirLeft.first); - const std::wstring* errorMsgNew = checkFailedRead(newDir, errorMsg); - this->fillOneSide<LEFT_SIDE>(dirLeft.second, errorMsgNew, newDir); //recurse + FolderPair& newFolder = output.addSubFolder<LEFT_SIDE>(dirLeft.first); + const std::wstring* errorMsgNew = checkFailedRead(newFolder, errorMsg); + this->fillOneSide<LEFT_SIDE>(dirLeft.second, errorMsgNew, newFolder); //recurse }, - [&](const DirData& dirRight) //right only + [&](const FolderData& dirRight) //right only { - DirPair& newDir = output.addSubDir<RIGHT_SIDE>(dirRight.first); - const std::wstring* errorMsgNew = checkFailedRead(newDir, errorMsg); - this->fillOneSide<RIGHT_SIDE>(dirRight.second, errorMsgNew, newDir); //recurse + FolderPair& newFolder = output.addSubFolder<RIGHT_SIDE>(dirRight.first); + const std::wstring* errorMsgNew = checkFailedRead(newFolder, errorMsg); + this->fillOneSide<RIGHT_SIDE>(dirRight.second, errorMsgNew, newFolder); //recurse }, - [&](const DirData& dirLeft, const DirData& dirRight) //both sides + [&](const FolderData& dirLeft, const FolderData& dirRight) //both sides { - DirPair& newDir = output.addSubDir(dirLeft.first, dirRight.first, DIR_EQUAL); - const std::wstring* errorMsgNew = checkFailedRead(newDir, errorMsg); + FolderPair& newFolder = output.addSubFolder(dirLeft.first, dirRight.first, DIR_EQUAL); + const std::wstring* errorMsgNew = checkFailedRead(newFolder, errorMsg); if (!errorMsgNew) if (dirLeft.first != dirRight.first) - newDir.setCategoryDiffMetadata(getDescrDiffMetaShortnameCase(newDir)); + newFolder.setCategoryDiffMetadata(getDescrDiffMetaShortnameCase(newFolder)); - mergeTwoSides(dirLeft.second, dirRight.second, errorMsgNew, newDir); //recurse + mergeTwoSides(dirLeft.second, dirRight.second, errorMsgNew, newFolder); //recurse }); } @@ -701,54 +701,54 @@ void MergeSides::mergeTwoSides(const DirContainer& leftSide, const DirContainer& //mark excluded directories (see fillBuffer()) + remove superfluous excluded subdirectories void stripExcludedDirectories(HierarchyObject& hierObj, const HardFilter& filterProc) { - for (DirPair& dirObj : hierObj.refSubDirs()) - stripExcludedDirectories(dirObj, filterProc); + for (FolderPair& folder : hierObj.refSubFolders()) + stripExcludedDirectories(folder, filterProc); //remove superfluous directories: // this does not invalidate "std::vector<FilePair*>& undefinedFiles", since we delete folders only // and there is no side-effect for memory positions of FilePair and SymlinkPair thanks to zen::FixedList! - static_assert(IsSameType<FixedList<DirPair>, HierarchyObject::SubDirVec>::value, ""); + static_assert(IsSameType<FixedList<FolderPair>, HierarchyObject::FolderList>::value, ""); - hierObj.refSubDirs().remove_if([&](DirPair& dirObj) + hierObj.refSubFolders().remove_if([&](FolderPair& folder) { - const bool included = filterProc.passDirFilter(dirObj.getPairRelativePath(), nullptr); //childItemMightMatch is false, child items were already excluded during scanning + const bool included = filterProc.passDirFilter(folder.getPairRelativePath(), nullptr); //childItemMightMatch is false, child items were already excluded during scanning if (!included) //falsify only! (e.g. might already be inactive due to read error!) - dirObj.setActive(false); + folder.setActive(false); return !included && //don't check active status, but eval filter directly! - dirObj.refSubDirs ().empty() && - dirObj.refSubLinks().empty() && - dirObj.refSubFiles().empty(); + folder.refSubFolders().empty() && + folder.refSubLinks ().empty() && + folder.refSubFiles ().empty(); }); } -//create comparison result table and fill category except for files existing on both sides: undefinedFiles and undefinedLinks are appended! -std::shared_ptr<BaseDirPair> ComparisonBuffer::performComparison(const ResolvedFolderPair& fp, - const FolderPairCfg& fpCfg, - std::vector<FilePair*>& undefinedFiles, - std::vector<SymlinkPair*>& undefinedLinks) const +//create comparison result table and fill category except for files existing on both sides: undefinedFiles and undefinedSymlinks are appended! +std::shared_ptr<BaseFolderPair> ComparisonBuffer::performComparison(const ResolvedFolderPair& fp, + const FolderPairCfg& fpCfg, + std::vector<FilePair*>& undefinedFiles, + std::vector<SymlinkPair*>& undefinedSymlinks) const { callback_.reportStatus(_("Generating file list...")); callback_.forceUiRefresh(); - auto getDirValue = [&](const ABF& baseFolder) -> const DirectoryValue* + auto getDirValue = [&](const AbstractPath& folderPath) -> const DirectoryValue* { - auto it = directoryBuffer.find(DirectoryKey(baseFolder, fpCfg.filter.nameFilter, fpCfg.handleSymlinks)); + auto it = directoryBuffer.find(DirectoryKey(folderPath, fpCfg.filter.nameFilter, fpCfg.handleSymlinks)); return it != directoryBuffer.end() ? &it->second : nullptr; }; - const DirectoryValue* bufValueLeft = getDirValue(*fp.abfLeft); - const DirectoryValue* bufValueRight = getDirValue(*fp.abfRight); + const DirectoryValue* bufValueLeft = getDirValue(fp.folderPathLeft); + const DirectoryValue* bufValueRight = getDirValue(fp.folderPathRight); std::map<Zstring, std::wstring, LessFilePath> failedReads; //base-relative paths or empty if read-error for whole base directory { - //mix failedDirReads with failedItemReads: + //mix failedFolderReads with failedItemReads: //mark directory errors already at directory-level (instead for child items only) to show on GUI! See "MergeSides" //=> minor pessimization for "excludefilterFailedRead" which needlessly excludes parent folders, too - if (bufValueLeft ) append(failedReads, bufValueLeft ->failedDirReads); - if (bufValueRight) append(failedReads, bufValueRight->failedDirReads); + if (bufValueLeft ) append(failedReads, bufValueLeft ->failedFolderReads); + if (bufValueRight) append(failedReads, bufValueRight->failedFolderReads); if (bufValueLeft ) append(failedReads, bufValueLeft ->failedItemReads); if (bufValueRight) append(failedReads, bufValueRight->failedItemReads); @@ -761,19 +761,19 @@ std::shared_ptr<BaseDirPair> ComparisonBuffer::performComparison(const ResolvedF for (const auto& item : failedReads) excludefilterFailedRead += item.first + Zstr("\n"); //exclude item AND (potential) child items! - std::shared_ptr<BaseDirPair> output = std::make_shared<BaseDirPair>(fp.abfLeft, - bufValueLeft != nullptr, //dir existence must be checked only once: available iff buffer entry exists! - fp.abfRight, - bufValueRight != nullptr, - fpCfg.filter.nameFilter->copyFilterAddingExclusion(excludefilterFailedRead), - fpCfg.compareVar, - fpCfg.fileTimeTolerance, - fpCfg.optTimeShiftHours); + std::shared_ptr<BaseFolderPair> output = std::make_shared<BaseFolderPair>(fp.folderPathLeft, + bufValueLeft != nullptr, //dir existence must be checked only once: available iff buffer entry exists! + fp.folderPathRight, + bufValueRight != nullptr, + fpCfg.filter.nameFilter->copyFilterAddingExclusion(excludefilterFailedRead), + fpCfg.compareVar, + fpCfg.fileTimeTolerance, + fpCfg.optTimeShiftHours); //PERF_START; - DirContainer emptyDirCont; //WTF!!! => using a temporary in the ternary conditional would implicitly call the DirContainer copy-constructor!!!!!! - MergeSides(failedReads, undefinedFiles, undefinedLinks).execute(bufValueLeft ? bufValueLeft ->dirCont : emptyDirCont, - bufValueRight ? bufValueRight->dirCont : emptyDirCont, *output); + FolderContainer emptyFolderCont; //WTF!!! => using a temporary in the ternary conditional would implicitly call the FolderContainer copy-constructor!!!!!! + MergeSides(failedReads, undefinedFiles, undefinedSymlinks).execute(bufValueLeft ? bufValueLeft ->folderCont : emptyFolderCont, + bufValueRight ? bufValueRight->folderCont : emptyFolderCont, *output); //PERF_STOP; //##################### in/exclude rows according to filtering ##################### @@ -845,7 +845,7 @@ FolderComparison zen::compare(xmlAccess::OptionalDialogs& warnings, //-------------------end of basic checks------------------------------------------ - auto basefolderExisting = [&](const ABF& baseFolder) { return resInfo.existingBaseFolders.find(&baseFolder) != resInfo.existingBaseFolders.end(); }; + auto basefolderExisting = [&](const AbstractPath& folderPath) { return resInfo.existingBaseFolders.find(folderPath) != resInfo.existingBaseFolders.end(); }; std::vector<std::pair<ResolvedFolderPair, FolderPairCfg>> totalWorkLoad; for (size_t i = 0; i < cfgList.size(); ++i) @@ -855,9 +855,9 @@ FolderComparison zen::compare(xmlAccess::OptionalDialogs& warnings, if (createDirLocks) { std::set<Zstring, LessFilePath> dirPathsExisting; - for (const ABF* baseFolder : resInfo.existingBaseFolders) - if (Opt<Zstring> folderPath = ABF::getNativeItemPath(baseFolder->getAbstractPath())) //restrict directory locking to native paths until further - dirPathsExisting.insert(*folderPath); + for (const AbstractPath& folderPath : resInfo.existingBaseFolders) + if (Opt<Zstring> nativePath = AFS::getNativeItemPath(folderPath)) //restrict directory locking to native paths until further + dirPathsExisting.insert(*nativePath); dirLocks = std::make_unique<LockHolder>(dirPathsExisting, warnings.warningDirectoryLockFailed, callback); } @@ -869,15 +869,15 @@ FolderComparison zen::compare(xmlAccess::OptionalDialogs& warnings, for (const auto& w : totalWorkLoad) { - if (basefolderExisting(*w.first.abfLeft)) //only traverse *currently existing* directories: at this point user is aware that non-ex + empty string are seen as empty folder! - dirsToRead.emplace(*w.first.abfLeft, w.second.filter.nameFilter, w.second.handleSymlinks); - if (basefolderExisting(*w.first.abfRight)) - dirsToRead.emplace(*w.first.abfRight, w.second.filter.nameFilter, w.second.handleSymlinks); + if (basefolderExisting(w.first.folderPathLeft)) //only traverse *currently existing* directories: at this point user is aware that non-ex + empty string are seen as empty folder! + dirsToRead.emplace(w.first.folderPathLeft, w.second.filter.nameFilter, w.second.handleSymlinks); + if (basefolderExisting(w.first.folderPathRight)) + dirsToRead.emplace(w.first.folderPathRight, w.second.filter.nameFilter, w.second.handleSymlinks); } FolderComparison output; - //reduce peak memory by restricting lifetime of ComparisonBuffer to have ended when loading potentially huge InSyncDir instance in redetermineSyncDirection() + //reduce peak memory by restricting lifetime of ComparisonBuffer to have ended when loading potentially huge InSyncFolder instance in redetermineSyncDirection() { //------------ traverse/read folders ----------------------------------------------------- //PERF_START; @@ -895,7 +895,7 @@ FolderComparison zen::compare(xmlAccess::OptionalDialogs& warnings, workLoadByContent.push_back(w); break; } - std::list<std::shared_ptr<BaseDirPair>> outputByContent = cmpBuff.compareByContent(workLoadByContent); + std::list<std::shared_ptr<BaseFolderPair>> outputByContent = cmpBuff.compareByContent(workLoadByContent); //write output in expected order for (const auto& w : totalWorkLoad) diff --git a/FreeFileSync/Source/comparison.h b/FreeFileSync/Source/comparison.h index 0a88ef8a..0614378b 100644 --- a/FreeFileSync/Source/comparison.h +++ b/FreeFileSync/Source/comparison.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef COMPARISON_H_INCLUDED -#define COMPARISON_H_INCLUDED +#ifndef COMPARISON_H_8032178534545426 +#define COMPARISON_H_8032178534545426 #include "file_hierarchy.h" #include "lib/process_xml.h" @@ -60,4 +60,4 @@ FolderComparison compare(xmlAccess::OptionalDialogs& warnings, ProcessCallback& callback); } -#endif // COMPARISON_H_INCLUDED +#endif //COMPARISON_H_8032178534545426 diff --git a/FreeFileSync/Source/file_hierarchy.cpp b/FreeFileSync/Source/file_hierarchy.cpp index 0cbd241a..3c044831 100644 --- a/FreeFileSync/Source/file_hierarchy.cpp +++ b/FreeFileSync/Source/file_hierarchy.cpp @@ -23,15 +23,15 @@ void HierarchyObject::removeEmptyRec() return objEmpty; }; - refSubFiles().remove_if(isEmpty); - refSubLinks().remove_if(isEmpty); - refSubDirs ().remove_if(isEmpty); + refSubFiles ().remove_if(isEmpty); + refSubLinks ().remove_if(isEmpty); + refSubFolders().remove_if(isEmpty); if (emptyExisting) //notify if actual deletion happened - notifySyncCfgChanged(); //mustn't call this in ~FileSystemObject(), since parent, usually a DirPair, may already be partially destroyed and existing as a pure HierarchyObject! + notifySyncCfgChanged(); //mustn't call this in ~FileSystemObject(), since parent, usually a FolderPair, may already be partially destroyed and existing as a pure HierarchyObject! - for (DirPair& subDir : refSubDirs()) - subDir.removeEmptyRec(); //recurse + for (FolderPair& folder : refSubFolders()) + folder.removeEmptyRec(); //recurse } @@ -134,9 +134,9 @@ SyncOperation getIsolatedSyncOperation(bool itemExistsLeft, template <class Predicate> inline bool hasDirectChild(const HierarchyObject& hierObj, Predicate p) { - return std::any_of(hierObj.refSubFiles().begin(), hierObj.refSubFiles().end(), p) || - std::any_of(hierObj.refSubLinks().begin(), hierObj.refSubLinks().end(), p) || - std::any_of(hierObj.refSubDirs(). begin(), hierObj.refSubDirs(). end(), p); + return std::any_of(hierObj.refSubFiles ().begin(), hierObj.refSubFiles ().end(), p) || + std::any_of(hierObj.refSubLinks ().begin(), hierObj.refSubLinks ().end(), p) || + std::any_of(hierObj.refSubFolders().begin(), hierObj.refSubFolders().end(), p); } } @@ -154,10 +154,10 @@ SyncOperation FileSystemObject::getSyncOperation() const } -//SyncOperation DirPair::testSyncOperation() const -> no recursion: we do NOT want to consider child elements when testing! +//SyncOperation FolderPair::testSyncOperation() const -> no recursion: we do NOT want to consider child elements when testing! -SyncOperation DirPair::getSyncOperation() const +SyncOperation FolderPair::getSyncOperation() const { if (!haveBufferedSyncOp) { @@ -408,8 +408,8 @@ std::wstring zen::getSyncOpDescription(const FileSystemObject& fsObj) const Zstring relTarget = getRelName(*targetFile, !onLeft); return getSyncOpDescription(op) + L"\n" + - (EqualFilePath()(beforeLast(relSource, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE), - beforeLast(relTarget, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)) ? + (equalFilePath(beforeLast(relSource, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE), + beforeLast(relTarget, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)) ? //detected pure "rename" fmtPath(afterLast(relSource, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL)) + L" ->\n" + //show short name only fmtPath(afterLast(relTarget, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL)) : diff --git a/FreeFileSync/Source/file_hierarchy.h b/FreeFileSync/Source/file_hierarchy.h index ca3974ff..9737652f 100644 --- a/FreeFileSync/Source/file_hierarchy.h +++ b/FreeFileSync/Source/file_hierarchy.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef FILEHIERARCHY_H_257235289645296 -#define FILEHIERARCHY_H_257235289645296 +#ifndef FILE_HIERARCHY_H_257235289645296 +#define FILE_HIERARCHY_H_257235289645296 #include <map> #include <cstddef> //required by GCC 4.8.1 to find ptrdiff_t @@ -20,38 +20,38 @@ #include <zen/file_id_def.h> #include "structures.h" #include "lib/hard_filter.h" -#include "fs/native.h" +#include "fs/abstract.h" namespace zen { -using ABF = AbstractBaseFolder; +using AFS = AbstractFileSystem; struct FileDescriptor { - FileDescriptor() : lastWriteTimeRaw(), fileSize(), fileId(), isFollowedSymlink() {} + FileDescriptor() {} FileDescriptor(std::int64_t lastWriteTimeRawIn, std::uint64_t fileSizeIn, - const ABF::FileId& idIn, + const AFS::FileId& idIn, bool isSymlink) : lastWriteTimeRaw(lastWriteTimeRawIn), fileSize(fileSizeIn), fileId(idIn), isFollowedSymlink(isSymlink) {} - std::int64_t lastWriteTimeRaw; //number of seconds since Jan. 1st 1970 UTC, same semantics like time_t (== signed long) - std::uint64_t fileSize; - ABF::FileId fileId; // optional! - bool isFollowedSymlink; + std::int64_t lastWriteTimeRaw = 0; //number of seconds since Jan. 1st 1970 UTC, same semantics like time_t (== signed long) + std::uint64_t fileSize = 0; + AFS::FileId fileId {}; // optional! + bool isFollowedSymlink = false; }; struct LinkDescriptor { - LinkDescriptor() : lastWriteTimeRaw() {} + LinkDescriptor() {} explicit LinkDescriptor(std::int64_t lastWriteTimeRawIn) : lastWriteTimeRaw(lastWriteTimeRawIn) {} - std::int64_t lastWriteTimeRaw; //number of seconds since Jan. 1st 1970 UTC, same semantics like time_t (== signed long) + std::int64_t lastWriteTimeRaw = 0; //number of seconds since Jan. 1st 1970 UTC, same semantics like time_t (== signed long) }; @@ -88,36 +88,29 @@ struct SelectParam<RIGHT_SIDE> static T& ref(T& left, T& right) { return right; } }; - -class BaseDirPair; -class DirPair; -class FilePair; -class SymlinkPair; -class FileSystemObject; - //------------------------------------------------------------------ -struct DirContainer +struct FolderContainer { //------------------------------------------------------------------ - typedef std::map<Zstring, DirContainer, LessFilePath> DirList; // - typedef std::map<Zstring, FileDescriptor, LessFilePath> FileList; //key: file name - typedef std::map<Zstring, LinkDescriptor, LessFilePath> LinkList; // + typedef std::map<Zstring, FolderContainer, LessFilePath> FolderList; // + typedef std::map<Zstring, FileDescriptor, LessFilePath> FileList; //key: file name + typedef std::map<Zstring, LinkDescriptor, LessFilePath> SymlinkList; // //------------------------------------------------------------------ - DirContainer() = default; - DirContainer (const DirContainer&) = delete; //catch accidental (and unnecessary) copying - DirContainer& operator=(const DirContainer&) = delete; // + FolderContainer() = default; + FolderContainer (const FolderContainer&) = delete; //catch accidental (and unnecessary) copying + FolderContainer& operator=(const FolderContainer&) = delete; // - DirList dirs; - FileList files; - LinkList links; //non-followed symlinks + FolderList folders; + FileList files; + SymlinkList symlinks; //non-followed symlinks //convenience - DirContainer& addSubDir(const Zstring& itemName) + FolderContainer& addSubFolder(const Zstring& itemName) { - return dirs[itemName]; //value default-construction is okay here - //return dirs.emplace(itemName, DirContainer()).first->second; + return folders[itemName]; //value default-construction is okay here + //return dirs.emplace(itemName, FolderContainer()).first->second; } void addSubFile(const Zstring& itemName, const FileDescriptor& fileData) @@ -129,12 +122,18 @@ struct DirContainer void addSubLink(const Zstring& itemName, const LinkDescriptor& linkData) { - auto rv = links.emplace(itemName, linkData); + auto rv = symlinks.emplace(itemName, linkData); if (!rv.second) rv.first->second = linkData; } }; +class BaseFolderPair; +class FolderPair; +class FilePair; +class SymlinkPair; +class FileSystemObject; + /*------------------------------------------------------------------ inheritance diagram: @@ -145,115 +144,115 @@ struct DirContainer /|\ /|\ _______________|_______________ ______|______ | | | | | - SymlinkPair FilePair DirPair BaseDirPair + SymlinkPair FilePair FolderPair BaseFolderPair ------------------------------------------------------------------*/ class HierarchyObject { - friend class DirPair; + friend class FolderPair; friend class FileSystemObject; public: - typedef FixedList<FilePair> SubFileVec; //MergeSides::execute() requires a structure that doesn't invalidate pointers after push_back() - typedef FixedList<SymlinkPair> SubLinkVec; //Note: deque<> has circular dependency in VCPP! - typedef FixedList<DirPair> SubDirVec; + typedef FixedList<FilePair> FileList; //MergeSides::execute() requires a structure that doesn't invalidate pointers after push_back() + typedef FixedList<SymlinkPair> SymlinkList; //Note: deque<> has circular dependency in VCPP! + typedef FixedList<FolderPair> FolderList; - DirPair& addSubDir(const Zstring& shortNameLeft, - const Zstring& shortNameRight, - CompareDirResult defaultCmpResult); + FolderPair& addSubFolder(const Zstring& itemNameLeft, + const Zstring& itemNameRight, + CompareDirResult defaultCmpResult); template <SelectedSide side> - DirPair& addSubDir(const Zstring& shortName); //dir exists on one side only + FolderPair& addSubFolder(const Zstring& itemName); //dir exists on one side only - FilePair& addSubFile(const Zstring& shortNameLeft, + FilePair& addSubFile(const Zstring& itemNameLeft, const FileDescriptor& left, //file exists on both sides CompareFilesResult defaultCmpResult, - const Zstring& shortNameRight, + const Zstring& itemNameRight, const FileDescriptor& right); template <SelectedSide side> - FilePair& addSubFile(const Zstring& shortName, //file exists on one side only + FilePair& addSubFile(const Zstring& itemName, //file exists on one side only const FileDescriptor& descr); - SymlinkPair& addSubLink(const Zstring& shortNameLeft, + SymlinkPair& addSubLink(const Zstring& itemNameLeft, const LinkDescriptor& left, //link exists on both sides CompareSymlinkResult defaultCmpResult, - const Zstring& shortNameRight, + const Zstring& itemNameRight, const LinkDescriptor& right); template <SelectedSide side> - SymlinkPair& addSubLink(const Zstring& shortName, //link exists on one side only + SymlinkPair& addSubLink(const Zstring& itemName, //link exists on one side only const LinkDescriptor& descr); - const SubFileVec& refSubFiles() const { return subFiles; } - /**/ SubFileVec& refSubFiles() { return subFiles; } + const FileList& refSubFiles() const { return subFiles; } + /**/ FileList& refSubFiles() { return subFiles; } - const SubLinkVec& refSubLinks() const { return subLinks; } - /**/ SubLinkVec& refSubLinks() { return subLinks; } + const SymlinkList& refSubLinks() const { return subLinks; } + /**/ SymlinkList& refSubLinks() { return subLinks; } - const SubDirVec& refSubDirs() const { return subDirs; } - /**/ SubDirVec& refSubDirs() { return subDirs; } + const FolderList& refSubFolders() const { return subFolders; } + /**/ FolderList& refSubFolders() { return subFolders; } - BaseDirPair& getRoot() { return root_; } + BaseFolderPair& getBase() { return base_; } const Zstring& getPairRelativePathPf() const { return pairRelPathPf; } //postfixed or empty! protected: HierarchyObject(const Zstring& relPathPf, - BaseDirPair& baseDirObj) : + BaseFolderPair& baseFolder) : pairRelPathPf(relPathPf), - root_(baseDirObj) {} + base_(baseFolder) {} - ~HierarchyObject() {} //don't need polymorphic deletion + virtual ~HierarchyObject() {} //don't need polymorphic deletion, but we have a vtable anyway virtual void flip(); void removeEmptyRec(); private: - virtual void notifySyncCfgChanged() {}; + virtual void notifySyncCfgChanged() {} HierarchyObject (const HierarchyObject&) = delete; //this class is referenced by it's child elements => make it non-copyable/movable! HierarchyObject& operator=(const HierarchyObject&) = delete; - SubFileVec subFiles; //contained file maps - SubLinkVec subLinks; //contained symbolic link maps - SubDirVec subDirs; //contained directory maps + FileList subFiles; //contained file maps + SymlinkList subLinks; //contained symbolic link maps + FolderList subFolders; //contained directory maps Zstring pairRelPathPf; //postfixed or empty - BaseDirPair& root_; + BaseFolderPair& base_; }; //------------------------------------------------------------------ -class BaseDirPair : public HierarchyObject //synchronization base directory +class BaseFolderPair : public HierarchyObject //synchronization base directory { public: - BaseDirPair(const std::shared_ptr<ABF>& abfLeftIn, - bool dirExistsLeft, - const std::shared_ptr<ABF>& abfRightIn, - bool dirExistsRight, - const HardFilter::FilterRef& filter, - CompareVariant cmpVar, - int fileTimeTolerance, - unsigned int optTimeShiftHours) : + BaseFolderPair(const AbstractPath& folderPathLeft, + bool dirExistsLeft, + const AbstractPath& folderPathRight, + bool dirExistsRight, + const HardFilter::FilterRef& filter, + CompareVariant cmpVar, + int fileTimeTolerance, + unsigned int optTimeShiftHours) : HierarchyObject(Zstring(), *this), filter_(filter), cmpVar_(cmpVar), fileTimeTolerance_(fileTimeTolerance), optTimeShiftHours_(optTimeShiftHours), dirExistsLeft_ (dirExistsLeft), dirExistsRight_(dirExistsRight), - abfLeft(abfLeftIn), - abfRight(abfRightIn) {} + folderPathLeft_(folderPathLeft), + folderPathRight_(folderPathRight) {} - template <SelectedSide side> ABF& getABF() const; + template <SelectedSide side> const AbstractPath& getAbstractPath() const; - static void removeEmpty(BaseDirPair& baseDir) { baseDir.removeEmptyRec(); }; //physically remove all invalid entries (where both sides are empty) recursively + static void removeEmpty(BaseFolderPair& baseFolder) { baseFolder.removeEmptyRec(); } //physically remove all invalid entries (where both sides are empty) recursively template <SelectedSide side> bool isExisting() const; //status of directory existence at the time of comparison! template <SelectedSide side> void setExisting(bool value); //update after creating the directory in FFS - //get settings which were used while creating BaseDirPair + //get settings which were used while creating BaseFolderPair const HardFilter& getFilter() const { return *filter_; } CompareVariant getCompVariant() const { return cmpVar_; } int getFileTimeTolerance() const { return fileTimeTolerance_; } @@ -270,16 +269,16 @@ private: bool dirExistsLeft_; bool dirExistsRight_; - std::shared_ptr<ABF> abfLeft; //always bound - std::shared_ptr<ABF> abfRight; // + AbstractPath folderPathLeft_; + AbstractPath folderPathRight_; }; template <> inline -ABF& BaseDirPair::getABF<LEFT_SIDE>() const { return *abfLeft; } +const AbstractPath& BaseFolderPair::getAbstractPath<LEFT_SIDE>() const { return folderPathLeft_; } template <> inline -ABF& BaseDirPair::getABF<RIGHT_SIDE>() const { return *abfRight; } +const AbstractPath& BaseFolderPair::getAbstractPath<RIGHT_SIDE>() const { return folderPathRight_; } //get rid of shared_ptr indirection @@ -289,36 +288,36 @@ class DerefIter : public std::iterator<std::bidirectional_iterator_tag, U> { public: DerefIter() {} - DerefIter(IterTy it) : iter(it) {} - DerefIter(const DerefIter& other) : iter(other.iter) {} - DerefIter& operator++() { ++iter; return *this; } - DerefIter& operator--() { --iter; return *this; } - DerefIter operator++(int) { DerefIter tmp(*this); operator++(); return tmp; } - DerefIter operator--(int) { DerefIter tmp(*this); operator--(); return tmp; } - inline friend ptrdiff_t operator-(const DerefIter& lhs, const DerefIter& rhs) { return lhs.iter - rhs.iter; } - inline friend bool operator==(const DerefIter& lhs, const DerefIter& rhs) { return lhs.iter == rhs.iter; } + DerefIter(IterTy it) : it_(it) {} + DerefIter(const DerefIter& other) : it_(other.it_) {} + DerefIter& operator++() { ++it_; return *this; } + DerefIter& operator--() { --it_; return *this; } + inline friend DerefIter operator++(DerefIter& it, int) { DerefIter tmp(it); ++it; return tmp; } + inline friend DerefIter operator--(DerefIter& it, int) { DerefIter tmp(it); --it; return tmp; } + inline friend ptrdiff_t operator-(const DerefIter& lhs, const DerefIter& rhs) { return lhs.it_ - rhs.it_; } + inline friend bool operator==(const DerefIter& lhs, const DerefIter& rhs) { return lhs.it_ == rhs.it_; } inline friend bool operator!=(const DerefIter& lhs, const DerefIter& rhs) { return !(lhs == rhs); } - U& operator* () { return **iter; } - U* operator->() { return &** iter; } + U& operator* () { return **it_; } + U* operator->() { return &** it_; } private: - IterTy iter; + IterTy it_; }; -typedef std::vector<std::shared_ptr<BaseDirPair>> FolderComparison; //make sure pointers to sub-elements remain valid -//don't change this back to std::vector<BaseDirPair> too easily: comparison uses push_back to add entries which may result in a full copy! +typedef std::vector<std::shared_ptr<BaseFolderPair>> FolderComparison; //make sure pointers to sub-elements remain valid +//don't change this back to std::vector<BaseFolderPair> too easily: comparison uses push_back to add entries which may result in a full copy! -DerefIter<typename FolderComparison::iterator, BaseDirPair> inline begin(FolderComparison& vect) { return vect.begin(); } -DerefIter<typename FolderComparison::iterator, BaseDirPair> inline end (FolderComparison& vect) { return vect.end (); } -DerefIter<typename FolderComparison::const_iterator, const BaseDirPair> inline begin(const FolderComparison& vect) { return vect.begin(); } -DerefIter<typename FolderComparison::const_iterator, const BaseDirPair> inline end (const FolderComparison& vect) { return vect.end (); } +DerefIter<typename FolderComparison::iterator, BaseFolderPair> inline begin( FolderComparison& vect) { return vect.begin(); } +DerefIter<typename FolderComparison::iterator, BaseFolderPair> inline end ( FolderComparison& vect) { return vect.end (); } +DerefIter<typename FolderComparison::const_iterator, const BaseFolderPair> inline begin(const FolderComparison& vect) { return vect.begin(); } +DerefIter<typename FolderComparison::const_iterator, const BaseFolderPair> inline end (const FolderComparison& vect) { return vect.end (); } //------------------------------------------------------------------ struct FSObjectVisitor { virtual ~FSObjectVisitor() {} - virtual void visit(const FilePair& fileObj) = 0; - virtual void visit(const SymlinkPair& linkObj) = 0; - virtual void visit(const DirPair& dirObj) = 0; + virtual void visit(const FilePair& file ) = 0; + virtual void visit(const SymlinkPair& symlink) = 0; + virtual void visit(const FolderPair& folder ) = 0; }; //inherit from this class to allow safe random access by id instead of unsafe raw pointer @@ -358,15 +357,14 @@ class FileSystemObject : public ObjectMgr<FileSystemObject> public: virtual void accept(FSObjectVisitor& visitor) const = 0; - Zstring getPairShortName () const; //like getItemName() but also returns value if either side is empty + Zstring getPairItemName () const; //like getItemName() but also returns value if either side is empty Zstring getPairRelativePath() const; //like getRelativePath() but also returns value if either side is empty template <SelectedSide side> bool isEmpty() const; template <SelectedSide side> const Zstring& getItemName() const; //case sensitive! template <SelectedSide side> Zstring getRelativePath() const; //get path relative to base sync dir without FILE_NAME_SEPARATOR prefix public: - template <SelectedSide side> ABF& getABF() const; - template <SelectedSide side> AbstractPathRef getAbstractPath() const; + template <SelectedSide side> AbstractPath getAbstractPath() const; //precondition: !isEmpty<side>() //comparison result CompareFilesResult getCategory() const { return cmpResult; } @@ -391,8 +389,8 @@ public: const HierarchyObject& parent() const { return parent_; } /**/ HierarchyObject& parent() { return parent_; } - const BaseDirPair& root() const { return parent_.getRoot(); } - /**/ BaseDirPair& root() { return parent_.getRoot(); } + const BaseFolderPair& base() const { return parent_.getBase(); } + /**/ BaseFolderPair& base() { return parent_.getBase(); } //for use during init in "CompareProcess" only: template <CompareFilesResult res> void setCategory(); @@ -400,26 +398,26 @@ public: void setCategoryDiffMetadata(const std::wstring& description); protected: - FileSystemObject(const Zstring& shortNameLeft, - const Zstring& shortNameRight, + FileSystemObject(const Zstring& itemNameLeft, + const Zstring& itemNameRight, HierarchyObject& parentObj, CompareFilesResult defaultCmpResult) : cmpResult(defaultCmpResult), - shortNameLeft_(shortNameLeft), - shortNameRight_(shortNameRight), - //shortNameRight_(shortNameRight == shortNameLeft ? shortNameLeft : shortNameRight), -> strangely doesn't seem to shrink peak memory consumption at all! + itemNameLeft_(itemNameLeft), + itemNameRight_(itemNameRight), + //itemNameRight_(itemNameRight == itemNameLeft ? itemNameLeft : itemNameRight), -> strangely doesn't seem to shrink peak memory consumption at all! parent_(parentObj) { parent_.notifySyncCfgChanged(); } - ~FileSystemObject() {} //don't need polymorphic deletion + virtual ~FileSystemObject() {} //don't need polymorphic deletion, but we have a vtable anyway //mustn't call parent here, it is already partially destroyed and nothing more than a pure HierarchyObject! virtual void flip(); virtual void notifySyncCfgChanged() { parent().notifySyncCfgChanged(); /*propagate!*/ } - void setSynced(const Zstring& shortName); + void setSynced(const Zstring& itemName); private: FileSystemObject (const FileSystemObject&) = delete; @@ -439,15 +437,15 @@ private: std::unique_ptr<std::wstring> syncDirectionConflict; //non-empty if we have a conflict setting sync-direction //get rid of std::wstring small string optimization (consumes 32/48 byte on VS2010 x86/x64!) - Zstring shortNameLeft_; //slightly redundant under linux, but on windows the "same" filepaths can differ in case - Zstring shortNameRight_; //use as indicator: an empty name means: not existing! + Zstring itemNameLeft_; //slightly redundant under linux, but on windows the "same" filepaths can differ in case + Zstring itemNameRight_; //use as indicator: an empty name means: not existing! HierarchyObject& parent_; }; //------------------------------------------------------------------ -class DirPair : public FileSystemObject, public HierarchyObject +class FolderPair : public FileSystemObject, public HierarchyObject { friend class HierarchyObject; @@ -456,16 +454,16 @@ public: CompareDirResult getDirCategory() const; //returns actually used subset of CompareFilesResult - DirPair(const Zstring& shortNameLeft, //use empty shortname if "not existing" - const Zstring& shortNameRight, // - HierarchyObject& parentObj, - CompareDirResult defaultCmpResult) : - FileSystemObject(shortNameLeft, shortNameRight, parentObj, static_cast<CompareFilesResult>(defaultCmpResult)), - HierarchyObject(getPairRelativePath() + FILE_NAME_SEPARATOR, parentObj.getRoot()) {} + FolderPair(const Zstring& itemNameLeft, //use empty itemName if "not existing" + const Zstring& itemNameRight, // + HierarchyObject& parentObj, + CompareDirResult defaultCmpResult) : + FileSystemObject(itemNameLeft, itemNameRight, parentObj, static_cast<CompareFilesResult>(defaultCmpResult)), + HierarchyObject(getPairRelativePath() + FILE_NAME_SEPARATOR, parentObj.getBase()) {} SyncOperation getSyncOperation() const override; - void setSyncedTo(const Zstring& shortName); //call after sync, sets DIR_EQUAL + void setSyncedTo(const Zstring& itemName); //call after sync, sets DIR_EQUAL private: void flip () override; @@ -486,19 +484,19 @@ class FilePair : public FileSystemObject public: void accept(FSObjectVisitor& visitor) const override; - FilePair(const Zstring& shortNameLeft, //use empty string if "not existing" + FilePair(const Zstring& itemNameLeft, //use empty string if "not existing" const FileDescriptor& left, CompareFilesResult defaultCmpResult, - const Zstring& shortNameRight, // + const Zstring& itemNameRight, // const FileDescriptor& right, HierarchyObject& parentObj) : - FileSystemObject(shortNameLeft, shortNameRight, parentObj, defaultCmpResult), + FileSystemObject(itemNameLeft, itemNameRight, parentObj, defaultCmpResult), dataLeft(left), dataRight(right) {} template <SelectedSide side> std::int64_t getLastWriteTime() const; template <SelectedSide side> std::uint64_t getFileSize() const; - template <SelectedSide side> ABF::FileId getFileId () const; + template <SelectedSide side> AFS::FileId getFileId () const; template <SelectedSide side> bool isFollowedSymlink() const; void setMoveRef(ObjectId refId) { moveFileRef = refId; } //reference to corresponding renamed file @@ -510,12 +508,12 @@ public: SyncOperation getSyncOperation() const override; template <SelectedSide sideTrg> - void setSyncedTo(const Zstring& shortName, //call after sync, sets FILE_EQUAL + void setSyncedTo(const Zstring& itemName, //call after sync, sets FILE_EQUAL std::uint64_t fileSize, std::int64_t lastWriteTimeTrg, std::int64_t lastWriteTimeSrc, - const ABF::FileId& fileIdTrg, - const ABF::FileId& fileIdSrc, + const AFS::FileId& fileIdTrg, + const AFS::FileId& fileIdSrc, bool isSymlinkTrg, bool isSymlinkSrc); @@ -523,8 +521,8 @@ private: SyncOperation applyMoveOptimization(SyncOperation op) const; void flip () override; - void removeObjectL() override; - void removeObjectR() override; + void removeObjectL() override { dataLeft = FileDescriptor(); } + void removeObjectR() override { dataRight = FileDescriptor(); } FileDescriptor dataLeft; FileDescriptor dataRight; @@ -534,7 +532,7 @@ private: //------------------------------------------------------------------ -class SymlinkPair : public FileSystemObject //this class models a TRUE symbolic link, i.e. one that is NEVER dereferenced: deref-links should be directly placed in class File/DirPair +class SymlinkPair : public FileSystemObject //this class models a TRUE symbolic link, i.e. one that is NEVER dereferenced: deref-links should be directly placed in class File/FolderPair { friend class HierarchyObject; //construction @@ -545,25 +543,25 @@ public: CompareSymlinkResult getLinkCategory() const; //returns actually used subset of CompareFilesResult - SymlinkPair(const Zstring& shortNameLeft, //use empty string if "not existing" + SymlinkPair(const Zstring& itemNameLeft, //use empty string if "not existing" const LinkDescriptor& left, CompareSymlinkResult defaultCmpResult, - const Zstring& shortNameRight, //use empty string if "not existing" + const Zstring& itemNameRight, //use empty string if "not existing" const LinkDescriptor& right, HierarchyObject& parentObj) : - FileSystemObject(shortNameLeft, shortNameRight, parentObj, static_cast<CompareFilesResult>(defaultCmpResult)), + FileSystemObject(itemNameLeft, itemNameRight, parentObj, static_cast<CompareFilesResult>(defaultCmpResult)), dataLeft(left), dataRight(right) {} template <SelectedSide sideTrg> - void setSyncedTo(const Zstring& shortName, //call after sync, sets SYMLINK_EQUAL + void setSyncedTo(const Zstring& itemName, //call after sync, sets SYMLINK_EQUAL std::int64_t lastWriteTimeTrg, std::int64_t lastWriteTimeSrc); private: void flip() override; - void removeObjectL() override; - void removeObjectR() override; + void removeObjectL() override { dataLeft = LinkDescriptor(); } + void removeObjectR() override { dataRight = LinkDescriptor(); } LinkDescriptor dataLeft; LinkDescriptor dataRight; @@ -606,7 +604,7 @@ std::wstring getSyncOpDescription (const FileSystemObject& fsObj); //inline virtual... admittedly its use may be limited inline void FilePair ::accept(FSObjectVisitor& visitor) const { visitor.visit(*this); } -inline void DirPair ::accept(FSObjectVisitor& visitor) const { visitor.visit(*this); } +inline void FolderPair ::accept(FSObjectVisitor& visitor) const { visitor.visit(*this); } inline void SymlinkPair::accept(FSObjectVisitor& visitor) const { visitor.visit(*this); } @@ -618,7 +616,7 @@ CompareFilesResult FilePair::getFileCategory() const inline -CompareDirResult DirPair::getDirCategory() const +CompareDirResult FolderPair::getDirCategory() const { return static_cast<CompareDirResult>(getCategory()); } @@ -682,7 +680,7 @@ void FileSystemObject::setActive(bool active) template <SelectedSide side> inline bool FileSystemObject::isEmpty() const { - return SelectParam<side>::ref(shortNameLeft_, shortNameRight_).empty(); + return SelectParam<side>::ref(itemNameLeft_, itemNameRight_).empty(); } @@ -696,7 +694,7 @@ bool FileSystemObject::isEmpty() const template <SelectedSide side> inline const Zstring& FileSystemObject::getItemName() const { - return SelectParam<side>::ref(shortNameLeft_, shortNameRight_); //empty if not existing + return SelectParam<side>::ref(itemNameLeft_, itemNameRight_); //empty if not existing } @@ -712,29 +710,23 @@ Zstring FileSystemObject::getRelativePath() const inline Zstring FileSystemObject::getPairRelativePath() const { - return parent_.getPairRelativePathPf() + getPairShortName(); + return parent_.getPairRelativePathPf() + getPairItemName(); } inline -Zstring FileSystemObject::getPairShortName() const +Zstring FileSystemObject::getPairItemName() const { return isEmpty<LEFT_SIDE>() ? getItemName<RIGHT_SIDE>() : getItemName<LEFT_SIDE>(); } template <SelectedSide side> inline -ABF& FileSystemObject::getABF() const -{ - return root().getABF<side>(); -} - - -template <SelectedSide side> inline -AbstractPathRef FileSystemObject::getAbstractPath() const +AbstractPath FileSystemObject::getAbstractPath() const { assert(!isEmpty<side>()); - return root().getABF<side>().getAbstractPath(parent_.getPairRelativePathPf() + getItemName<side>()); + const Zstring& itemName = isEmpty<side>() ? getItemName<OtherSide<side>::result>() : getItemName<side>(); + return AFS::appendRelPath(base().getAbstractPath<side>(), parent_.getPairRelativePathPf() + itemName); } @@ -742,7 +734,7 @@ template <> inline void FileSystemObject::removeObject<LEFT_SIDE>() { cmpResult = isEmpty<RIGHT_SIDE>() ? FILE_EQUAL : FILE_RIGHT_SIDE_ONLY; - shortNameLeft_.clear(); + itemNameLeft_.clear(); removeObjectL(); setSyncDir(SyncDirection::NONE); //calls notifySyncCfgChanged() @@ -753,7 +745,7 @@ template <> inline void FileSystemObject::removeObject<RIGHT_SIDE>() { cmpResult = isEmpty<LEFT_SIDE>() ? FILE_EQUAL : FILE_LEFT_SIDE_ONLY; - shortNameRight_.clear(); + itemNameRight_.clear(); removeObjectR(); setSyncDir(SyncDirection::NONE); //calls notifySyncCfgChanged() @@ -761,10 +753,10 @@ void FileSystemObject::removeObject<RIGHT_SIDE>() inline -void FileSystemObject::setSynced(const Zstring& shortName) +void FileSystemObject::setSynced(const Zstring& itemName) { assert(!isEmpty()); - shortNameRight_ = shortNameLeft_ = shortName; + itemNameRight_ = itemNameLeft_ = itemName; cmpResult = FILE_EQUAL; setSyncDir(SyncDirection::NONE); } @@ -776,7 +768,7 @@ void FileSystemObject::setCategory() cmpResult = res; } template <> void FileSystemObject::setCategory<FILE_CONFLICT>(); // -template <> void FileSystemObject::setCategory<FILE_DIFFERENT_METADATA>(); //not defined! +template <> void FileSystemObject::setCategory<FILE_DIFFERENT_METADATA>(); //deny use => not defined! template <> void FileSystemObject::setCategory<FILE_LEFT_SIDE_ONLY>(); // template <> void FileSystemObject::setCategory<FILE_RIGHT_SIDE_ONLY>(); // @@ -797,7 +789,7 @@ void FileSystemObject::setCategoryDiffMetadata(const std::wstring& description) inline void FileSystemObject::flip() { - std::swap(shortNameLeft_, shortNameRight_); + std::swap(itemNameLeft_, itemNameRight_); switch (cmpResult) { @@ -827,108 +819,108 @@ void FileSystemObject::flip() inline void HierarchyObject::flip() { - for (FilePair& fileObj : refSubFiles()) - fileObj.flip(); - for (SymlinkPair& linkObj : refSubLinks()) - linkObj.flip(); - for (DirPair& dirObj : refSubDirs()) - dirObj.flip(); + for (FilePair& file : refSubFiles()) + file.flip(); + for (SymlinkPair& link : refSubLinks()) + link.flip(); + for (FolderPair& folder : refSubFolders()) + folder.flip(); } inline -DirPair& HierarchyObject::addSubDir(const Zstring& shortNameLeft, - const Zstring& shortNameRight, - CompareDirResult defaultCmpResult) +FolderPair& HierarchyObject::addSubFolder(const Zstring& itemNameLeft, + const Zstring& itemNameRight, + CompareDirResult defaultCmpResult) { - subDirs.emplace_back(shortNameLeft, shortNameRight, *this, defaultCmpResult); - return subDirs.back(); + subFolders.emplace_back(itemNameLeft, itemNameRight, *this, defaultCmpResult); + return subFolders.back(); } template <> inline -DirPair& HierarchyObject::addSubDir<LEFT_SIDE>(const Zstring& shortName) +FolderPair& HierarchyObject::addSubFolder<LEFT_SIDE>(const Zstring& itemName) { - subDirs.emplace_back(shortName, Zstring(), *this, DIR_LEFT_SIDE_ONLY); - return subDirs.back(); + subFolders.emplace_back(itemName, Zstring(), *this, DIR_LEFT_SIDE_ONLY); + return subFolders.back(); } template <> inline -DirPair& HierarchyObject::addSubDir<RIGHT_SIDE>(const Zstring& shortName) +FolderPair& HierarchyObject::addSubFolder<RIGHT_SIDE>(const Zstring& itemName) { - subDirs.emplace_back(Zstring(), shortName, *this, DIR_RIGHT_SIDE_ONLY); - return subDirs.back(); + subFolders.emplace_back(Zstring(), itemName, *this, DIR_RIGHT_SIDE_ONLY); + return subFolders.back(); } inline -FilePair& HierarchyObject::addSubFile(const Zstring& shortNameLeft, +FilePair& HierarchyObject::addSubFile(const Zstring& itemNameLeft, const FileDescriptor& left, //file exists on both sides CompareFilesResult defaultCmpResult, - const Zstring& shortNameRight, + const Zstring& itemNameRight, const FileDescriptor& right) { - subFiles.emplace_back(shortNameLeft, left, defaultCmpResult, shortNameRight, right, *this); + subFiles.emplace_back(itemNameLeft, left, defaultCmpResult, itemNameRight, right, *this); return subFiles.back(); } template <> inline -FilePair& HierarchyObject::addSubFile<LEFT_SIDE>(const Zstring& shortName, const FileDescriptor& descr) +FilePair& HierarchyObject::addSubFile<LEFT_SIDE>(const Zstring& itemName, const FileDescriptor& descr) { - subFiles.emplace_back(shortName, descr, FILE_LEFT_SIDE_ONLY, Zstring(), FileDescriptor(), *this); + subFiles.emplace_back(itemName, descr, FILE_LEFT_SIDE_ONLY, Zstring(), FileDescriptor(), *this); return subFiles.back(); } template <> inline -FilePair& HierarchyObject::addSubFile<RIGHT_SIDE>(const Zstring& shortName, const FileDescriptor& descr) +FilePair& HierarchyObject::addSubFile<RIGHT_SIDE>(const Zstring& itemName, const FileDescriptor& descr) { - subFiles.emplace_back(Zstring(), FileDescriptor(), FILE_RIGHT_SIDE_ONLY, shortName, descr, *this); + subFiles.emplace_back(Zstring(), FileDescriptor(), FILE_RIGHT_SIDE_ONLY, itemName, descr, *this); return subFiles.back(); } inline -SymlinkPair& HierarchyObject::addSubLink(const Zstring& shortNameLeft, +SymlinkPair& HierarchyObject::addSubLink(const Zstring& itemNameLeft, const LinkDescriptor& left, //link exists on both sides CompareSymlinkResult defaultCmpResult, - const Zstring& shortNameRight, + const Zstring& itemNameRight, const LinkDescriptor& right) { - subLinks.emplace_back(shortNameLeft, left, defaultCmpResult, shortNameRight, right, *this); + subLinks.emplace_back(itemNameLeft, left, defaultCmpResult, itemNameRight, right, *this); return subLinks.back(); } template <> inline -SymlinkPair& HierarchyObject::addSubLink<LEFT_SIDE>(const Zstring& shortName, const LinkDescriptor& descr) +SymlinkPair& HierarchyObject::addSubLink<LEFT_SIDE>(const Zstring& itemName, const LinkDescriptor& descr) { - subLinks.emplace_back(shortName, descr, SYMLINK_LEFT_SIDE_ONLY, Zstring(), LinkDescriptor(), *this); + subLinks.emplace_back(itemName, descr, SYMLINK_LEFT_SIDE_ONLY, Zstring(), LinkDescriptor(), *this); return subLinks.back(); } template <> inline -SymlinkPair& HierarchyObject::addSubLink<RIGHT_SIDE>(const Zstring& shortName, const LinkDescriptor& descr) +SymlinkPair& HierarchyObject::addSubLink<RIGHT_SIDE>(const Zstring& itemName, const LinkDescriptor& descr) { - subLinks.emplace_back(Zstring(), LinkDescriptor(), SYMLINK_RIGHT_SIDE_ONLY, shortName, descr, *this); + subLinks.emplace_back(Zstring(), LinkDescriptor(), SYMLINK_RIGHT_SIDE_ONLY, itemName, descr, *this); return subLinks.back(); } inline -void BaseDirPair::flip() +void BaseFolderPair::flip() { HierarchyObject::flip(); std::swap(dirExistsLeft_, dirExistsRight_); - std::swap(abfLeft, abfRight); + std::swap(folderPathLeft_, folderPathRight_); } inline -void DirPair::flip() +void FolderPair::flip() { HierarchyObject ::flip(); //call base class versions FileSystemObject::flip(); // @@ -936,38 +928,38 @@ void DirPair::flip() inline -void DirPair::removeObjectL() -{ - for (FilePair& fileObj : refSubFiles()) - fileObj.removeObject<LEFT_SIDE>(); - for (SymlinkPair& linkObj : refSubLinks()) - linkObj.removeObject<LEFT_SIDE>(); - for (DirPair& dirObj : refSubDirs()) - dirObj.removeObject<LEFT_SIDE>(); +void FolderPair::removeObjectL() +{ + for (FilePair& file : refSubFiles()) + file.removeObject<LEFT_SIDE>(); + for (SymlinkPair& link : refSubLinks()) + link.removeObject<LEFT_SIDE>(); + for (FolderPair& folder : refSubFolders()) + folder.removeObject<LEFT_SIDE>(); } inline -void DirPair::removeObjectR() -{ - for (FilePair& fileObj : refSubFiles()) - fileObj.removeObject<RIGHT_SIDE>(); - for (SymlinkPair& linkObj : refSubLinks()) - linkObj.removeObject<RIGHT_SIDE>(); - for (DirPair& dirObj : refSubDirs()) - dirObj.removeObject<RIGHT_SIDE>(); +void FolderPair::removeObjectR() +{ + for (FilePair& file : refSubFiles()) + file.removeObject<RIGHT_SIDE>(); + for (SymlinkPair& link : refSubLinks()) + link.removeObject<RIGHT_SIDE>(); + for (FolderPair& folder : refSubFolders()) + folder.removeObject<RIGHT_SIDE>(); } template <SelectedSide side> inline -bool BaseDirPair::isExisting() const +bool BaseFolderPair::isExisting() const { return SelectParam<side>::ref(dirExistsLeft_, dirExistsRight_); } template <SelectedSide side> inline -void BaseDirPair::setExisting(bool value) +void BaseFolderPair::setExisting(bool value) { SelectParam<side>::ref(dirExistsLeft_, dirExistsRight_) = value; } @@ -981,20 +973,6 @@ void FilePair::flip() } -inline -void FilePair::removeObjectL() -{ - dataLeft = FileDescriptor(); -} - - -inline -void FilePair::removeObjectR() -{ - dataRight = FileDescriptor(); -} - - template <SelectedSide side> inline std::int64_t FilePair::getLastWriteTime() const { @@ -1010,7 +988,7 @@ std::uint64_t FilePair::getFileSize() const template <SelectedSide side> inline -ABF::FileId FilePair::getFileId() const +AFS::FileId FilePair::getFileId() const { return SelectParam<side>::ref(dataLeft, dataRight).fileId; } @@ -1024,12 +1002,12 @@ bool FilePair::isFollowedSymlink() const template <SelectedSide sideTrg> inline -void FilePair::setSyncedTo(const Zstring& shortName, +void FilePair::setSyncedTo(const Zstring& itemName, std::uint64_t fileSize, std::int64_t lastWriteTimeTrg, std::int64_t lastWriteTimeSrc, - const ABF::FileId& fileIdTrg, - const ABF::FileId& fileIdSrc, + const AFS::FileId& fileIdTrg, + const AFS::FileId& fileIdSrc, bool isSymlinkTrg, bool isSymlinkSrc) { @@ -1040,12 +1018,12 @@ void FilePair::setSyncedTo(const Zstring& shortName, SelectParam<sideSrc>::ref(dataLeft, dataRight) = FileDescriptor(lastWriteTimeSrc, fileSize, fileIdSrc, isSymlinkSrc); moveFileRef = nullptr; - FileSystemObject::setSynced(shortName); //set FileSystemObject specific part + FileSystemObject::setSynced(itemName); //set FileSystemObject specific part } template <SelectedSide sideTrg> inline -void SymlinkPair::setSyncedTo(const Zstring& shortName, +void SymlinkPair::setSyncedTo(const Zstring& itemName, std::int64_t lastWriteTimeTrg, std::int64_t lastWriteTimeSrc) { @@ -1054,14 +1032,14 @@ void SymlinkPair::setSyncedTo(const Zstring& shortName, SelectParam<sideTrg>::ref(dataLeft, dataRight) = LinkDescriptor(lastWriteTimeTrg); SelectParam<sideSrc>::ref(dataLeft, dataRight) = LinkDescriptor(lastWriteTimeSrc); - FileSystemObject::setSynced(shortName); //set FileSystemObject specific part + FileSystemObject::setSynced(itemName); //set FileSystemObject specific part } inline -void DirPair::setSyncedTo(const Zstring& shortName) +void FolderPair::setSyncedTo(const Zstring& itemName) { - FileSystemObject::setSynced(shortName); //set FileSystemObject specific part + FileSystemObject::setSynced(itemName); //set FileSystemObject specific part } @@ -1085,20 +1063,6 @@ void SymlinkPair::flip() FileSystemObject::flip(); //call base class versions std::swap(dataLeft, dataRight); } - - -inline -void SymlinkPair::removeObjectL() -{ - dataLeft = LinkDescriptor(); -} - - -inline -void SymlinkPair::removeObjectR() -{ - dataRight = LinkDescriptor(); -} } -#endif //FILEHIERARCHY_H_257235289645296 +#endif //FILE_HIERARCHY_H_257235289645296 diff --git a/FreeFileSync/Source/fs/abstract.cpp b/FreeFileSync/Source/fs/abstract.cpp index 283e3c17..20e86923 100644 --- a/FreeFileSync/Source/fs/abstract.cpp +++ b/FreeFileSync/Source/fs/abstract.cpp @@ -7,15 +7,15 @@ #include "abstract.h" using namespace zen; -using ABF = AbstractBaseFolder; +using AFS = AbstractFileSystem; -const Zchar* ABF::TEMP_FILE_ENDING = Zstr(".ffs_tmp"); +const Zchar* AFS::TEMP_FILE_ENDING = Zstr(".ffs_tmp"); -ABF::FileAttribAfterCopy ABF::copyFileAsStream(const AbstractPathRef& apSource, const AbstractPathRef& apTarget, //throw FileError, ErrorTargetExisting, ErrorFileLocked - const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus) +AFS::FileAttribAfterCopy AFS::copyFileAsStream(const Zstring& itemPathImplSource, const AbstractPath& apTarget, //throw FileError, ErrorTargetExisting, ErrorFileLocked + const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus) const { - auto streamIn = getInputStream(apSource); //throw FileError, ErrorFileLocked + auto streamIn = getInputStream(itemPathImplSource); //throw FileError, ErrorFileLocked if (onNotifyCopyStatus) onNotifyCopyStatus(0); //throw X! std::uint64_t bytesWritten = 0; @@ -46,46 +46,47 @@ ABF::FileAttribAfterCopy ABF::copyFileAsStream(const AbstractPathRef& apSource, //important check: catches corrupt sftp download with libssh2! if (bytesWritten != fileSizeExpected) - throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(ABF::getDisplayPath(apSource))), - replaceCpy(replaceCpy(_("Unexpected size of data stream.\nExpected: %x bytes\nActual: %y bytes"), L"%x", numberTo<std::wstring>(fileSizeExpected)), L"%y", numberTo<std::wstring>(bytesWritten))); + throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(getDisplayPath(itemPathImplSource))), + replaceCpy(replaceCpy(_("Unexpected size of data stream.\nExpected: %x bytes\nActual: %y bytes"), + L"%x", numberTo<std::wstring>(fileSizeExpected)), + L"%y", numberTo<std::wstring>(bytesWritten))); //modification time should be set here! targetFileId = streamOut->finalize([&] { if (onNotifyCopyStatus) onNotifyCopyStatus(0); /*throw X*/ }); //throw FileError - ABF::FileAttribAfterCopy attr = {}; + AFS::FileAttribAfterCopy attr; attr.fileSize = bytesWritten; attr.modificationTime = modificationTime; attr.sourceFileId = sourceFileId; attr.targetFileId = targetFileId; - return attr; } -ABF::FileAttribAfterCopy ABF::copyFileTransactional(const AbstractPathRef& apSource, const AbstractPathRef& apTarget, //throw FileError, ErrorFileLocked +AFS::FileAttribAfterCopy AFS::copyFileTransactional(const AbstractPath& apSource, const AbstractPath& apTarget, //throw FileError, ErrorFileLocked bool copyFilePermissions, bool transactionalCopy, const std::function<void()>& onDeleteTargetFile, const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus) { - auto copyFileBestEffort = [&](const AbstractPathRef& apTargetTmp) + auto copyFileBestEffort = [&](const AbstractPath& apTargetTmp) { //caveat: typeid returns static type for pointers, dynamic type for references!!! - if (typeid(*apSource.abf) == typeid(*apTarget.abf)) - return apSource.abf->copyFileForSameAbfType(apSource.itemPathImpl, apTargetTmp, copyFilePermissions, onNotifyCopyStatus); //throw FileError, ErrorTargetExisting, ErrorFileLocked + if (typeid(*apSource.afs) == typeid(*apTarget.afs)) + return apSource.afs->copyFileForSameAfsType(apSource.itemPathImpl, apTargetTmp, copyFilePermissions, onNotifyCopyStatus); //throw FileError, ErrorTargetExisting, ErrorFileLocked //fall back to stream-based file copy: if (copyFilePermissions) - throw FileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtPath(ABF::getDisplayPath(apTargetTmp))), + throw FileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtPath(AFS::getDisplayPath(apTargetTmp))), _("Operation not supported for different base folder types.")); - return copyFileAsStream(apSource, apTargetTmp, onNotifyCopyStatus); //throw FileError, ErrorTargetExisting, ErrorFileLocked + return AFS::copyFileAsStream(apSource, apTargetTmp, onNotifyCopyStatus); //throw FileError, ErrorTargetExisting, ErrorFileLocked }; if (transactionalCopy) { - AbstractPathRef apTargetTmp(*apTarget.abf, apTarget.itemPathImpl + TEMP_FILE_ENDING); - ABF::FileAttribAfterCopy attr = {}; + AbstractPath apTargetTmp(apTarget.afs, apTarget.itemPathImpl + TEMP_FILE_ENDING); + AFS::FileAttribAfterCopy attr; for (int i = 0;; ++i) try @@ -100,7 +101,8 @@ ABF::FileAttribAfterCopy ABF::copyFileTransactional(const AbstractPathRef& apSou } //transactional behavior: ensure cleanup; not needed before copyFileBestEffort() which is already transactional - zen::ScopeGuard guardTempFile = zen::makeGuard([&] { try { ABF::removeFile(apTargetTmp); } catch (FileError&) {} }); + ZEN_ON_SCOPE_FAIL( try { AFS::removeFile(apTargetTmp); } + catch (FileError&) {} ); //have target file deleted (after read access on source and target has been confirmed) => allow for almost transactional overwrite if (onDeleteTargetFile) @@ -116,8 +118,6 @@ ABF::FileAttribAfterCopy ABF::copyFileTransactional(const AbstractPathRef& apSou http://blogs.msdn.com/b/oldnewthing/archive/2005/07/15/439261.aspx http://support.microsoft.com/kb/172190/en-us */ - - guardTempFile.dismiss(); return attr; } else @@ -137,22 +137,22 @@ ABF::FileAttribAfterCopy ABF::copyFileTransactional(const AbstractPathRef& apSou } -void ABF::createFolderRecursively(const AbstractPathRef& ap) //throw FileError +void AFS::createFolderRecursively(const AbstractPath& ap) //throw FileError { try { - AbstractBaseFolder::createFolderSimple(ap); //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing + AFS::createFolderSimple(ap); //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing } catch (ErrorTargetExisting&) {} catch (ErrorTargetPathMissing&) { - if (std::unique_ptr<AbstractPathRef> parentPath = ABF::getParentFolderPath(ap)) + if (Opt<AbstractPath> parentPath = AFS::getParentFolderPath(ap)) { //recurse... createFolderRecursively(*parentPath); //throw FileError //now try again... - ABF::createFolderSimple(ap); //throw FileError, (ErrorTargetExisting), (ErrorTargetPathMissing) + AFS::createFolderSimple(ap); //throw FileError, (ErrorTargetExisting), (ErrorTargetPathMissing) return; } throw; @@ -162,15 +162,15 @@ void ABF::createFolderRecursively(const AbstractPathRef& ap) //throw FileError namespace { -struct FlatTraverserCallback: public ABF::TraverserCallback +struct FlatTraverserCallback: public AFS::TraverserCallback { - FlatTraverserCallback(const AbstractPathRef& folderPath) : folderPath_(folderPath) {} + FlatTraverserCallback(const AbstractPath& folderPath) : folderPath_(folderPath) {} void onFile (const FileInfo& fi) override { fileNames_ .push_back(fi.itemName); } std::unique_ptr<TraverserCallback> onDir (const DirInfo& di) override { folderNames_.push_back(di.itemName); return nullptr; } HandleLink onSymlink(const SymlinkInfo& si) override { - if (ABF::folderExists(ABF::appendRelPath(folderPath_, si.itemName))) //dir symlink + if (AFS::folderExists(AFS::appendRelPath(folderPath_, si.itemName))) //dir symlink folderLinkNames_.push_back(si.itemName); else //file symlink, broken symlink fileNames_.push_back(si.itemName); @@ -184,68 +184,68 @@ struct FlatTraverserCallback: public ABF::TraverserCallback const std::vector<Zstring>& refFolderLinkNames() const { return folderLinkNames_; } private: - const AbstractPathRef folderPath_; + const AbstractPath folderPath_; std::vector<Zstring> fileNames_; std::vector<Zstring> folderNames_; std::vector<Zstring> folderLinkNames_; }; -void removeFolderRecursivelyImpl(const AbstractPathRef& folderPath, //throw FileError +void removeFolderRecursivelyImpl(const AbstractPath& folderPath, //throw FileError const std::function<void (const std::wstring& displayPath)>& onBeforeFileDeletion, //optional const std::function<void (const std::wstring& displayPath)>& onBeforeFolderDeletion) //one call for each *existing* object! { - assert(!ABF::symlinkExists(folderPath)); //[!] no symlinks in this context!!! - assert(ABF::folderExists(folderPath)); //Do NOT traverse into it deleting contained files!!! + assert(!AFS::symlinkExists(folderPath)); //[!] no symlinks in this context!!! + assert(AFS::folderExists(folderPath)); //Do NOT traverse into it deleting contained files!!! FlatTraverserCallback ft(folderPath); //deferred recursion => save stack space and allow deletion of extremely deep hierarchies! - ABF::traverseFolder(folderPath, ft); //throw FileError + AFS::traverseFolder(folderPath, ft); //throw FileError for (const Zstring& fileName : ft.refFileNames()) { - const AbstractPathRef filePath = ABF::appendRelPath(folderPath, fileName); + const AbstractPath filePath = AFS::appendRelPath(folderPath, fileName); if (onBeforeFileDeletion) - onBeforeFileDeletion(ABF::getDisplayPath(filePath)); + onBeforeFileDeletion(AFS::getDisplayPath(filePath)); - ABF::removeFile(filePath); //throw FileError + AFS::removeFile(filePath); //throw FileError } for (const Zstring& folderLinkName : ft.refFolderLinkNames()) { - const AbstractPathRef linkPath = ABF::appendRelPath(folderPath, folderLinkName); + const AbstractPath linkPath = AFS::appendRelPath(folderPath, folderLinkName); if (onBeforeFolderDeletion) - onBeforeFolderDeletion(ABF::getDisplayPath(linkPath)); + onBeforeFolderDeletion(AFS::getDisplayPath(linkPath)); - ABF::removeFolderSimple(linkPath); //throw FileError + AFS::removeFolderSimple(linkPath); //throw FileError } for (const Zstring& folderName : ft.refFolderNames()) - removeFolderRecursivelyImpl(ABF::appendRelPath(folderPath, folderName), //throw FileError + removeFolderRecursivelyImpl(AFS::appendRelPath(folderPath, folderName), //throw FileError onBeforeFileDeletion, onBeforeFolderDeletion); if (onBeforeFolderDeletion) - onBeforeFolderDeletion(ABF::getDisplayPath(folderPath)); + onBeforeFolderDeletion(AFS::getDisplayPath(folderPath)); - ABF::removeFolderSimple(folderPath); //throw FileError + AFS::removeFolderSimple(folderPath); //throw FileError } } -void ABF::removeFolderRecursively(const AbstractPathRef& ap, //throw FileError +void AFS::removeFolderRecursively(const AbstractPath& ap, //throw FileError const std::function<void (const std::wstring& displayPath)>& onBeforeFileDeletion, //optional const std::function<void (const std::wstring& displayPath)>& onBeforeFolderDeletion) //one call for each *existing* object! { - if (ABF::symlinkExists(ap)) + if (AFS::symlinkExists(ap)) { if (onBeforeFolderDeletion) - onBeforeFolderDeletion(ABF::getDisplayPath(ap)); + onBeforeFolderDeletion(AFS::getDisplayPath(ap)); - ABF::removeFolderSimple(ap); //throw FileError + AFS::removeFolderSimple(ap); //throw FileError } else { //no error situation if directory is not existing! manual deletion relies on it! - if (ABF::somethingExists(ap)) + if (AFS::somethingExists(ap)) removeFolderRecursivelyImpl(ap, onBeforeFileDeletion, onBeforeFolderDeletion); //throw FileError } } diff --git a/FreeFileSync/Source/fs/abstract.h b/FreeFileSync/Source/fs/abstract.h index b54deecb..1b21757c 100644 --- a/FreeFileSync/Source/fs/abstract.h +++ b/FreeFileSync/Source/fs/abstract.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef ABSTRACT_FS_873450978453042524534234 -#define ABSTRACT_FS_873450978453042524534234 +#ifndef ABSTRACT_H_873450978453042524534234 +#define ABSTRACT_H_873450978453042524534234 #include <functional> #include <zen/file_error.h> @@ -16,99 +16,95 @@ namespace zen { -struct AbstractBaseFolder; +struct AbstractFileSystem; - -struct AbstractPathRef +//============================================================================================================== +class AbstractPath //THREAD-SAFETY: like an int! { - struct ItemId; - ItemId getUniqueId() const; //non-referencing identifier value +public: + template <class T1, class T2> + AbstractPath(T1&& afsIn, T2&& itemPathImplIn) : afs(std::forward<T1>(afsIn)), itemPathImpl(std::forward<T2>(itemPathImplIn)) {} private: - AbstractPathRef(const AbstractBaseFolder& abfIn, const Zstring& itemPathImplIn) : abf(&abfIn), itemPathImpl(itemPathImplIn) {} - friend struct AbstractBaseFolder; + friend struct AbstractFileSystem; - const AbstractBaseFolder* abf; //always bound - Zstring itemPathImpl; //valid only in context of a specific AbstractBaseFolder *instance*, e.g. FTP session! - //referenced item needs not exist; no file I/O!!! + std::shared_ptr<const AbstractFileSystem> afs; //always bound; "const AbstractFileSystem" => all accesses expected to be thread-safe!!! + Zstring itemPathImpl; //valid only in context of a specific AbstractFileSystem instance* }; //============================================================================================================== -//============================================================================================================== - -struct AbstractBaseFolder +struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model thread-safe access! { - virtual Zstring getInitPathPhrase() const = 0; //noexcept + struct LessAbstractPath; + static bool equalAbstractPath(const AbstractPath& lhs, const AbstractPath& rhs); - //copy this instance for life-time management and safe access on any method by a different thread: - virtual std::unique_ptr<AbstractBaseFolder> createIndependentCopy() const = 0; //noexcept + static Zstring getInitPathPhrase(const AbstractPath& ap) { return ap.afs->getInitPathPhrase(ap.itemPathImpl); } - static std::wstring getDisplayPath(const AbstractPathRef& ap) { return ap.abf->getDisplayPath(ap.itemPathImpl); } + static std::wstring getDisplayPath(const AbstractPath& ap) { return ap.afs->getDisplayPath(ap.itemPathImpl); } - static Zstring getFileShortName(const AbstractPathRef& ap) { return ap.abf->getFileShortName(ap.itemPathImpl); } + static bool isNullPath(const AbstractPath& ap) { return ap.afs->isNullPath(ap.itemPathImpl); } - struct LessItemPath; - static bool equalItemPath(const AbstractPathRef& lhs, const AbstractPathRef& rhs); + static AbstractPath appendRelPath(const AbstractPath& ap, const Zstring& relPath) + { +#ifdef ZEN_WIN + assert(!contains(relPath, L"/")); //relPath is expected to use FILE_NAME_SEPARATOR! +#endif + assert(relPath.empty() || (!startsWith(relPath, FILE_NAME_SEPARATOR) && !endsWith(relPath, FILE_NAME_SEPARATOR))); + return AbstractPath(ap.afs, ap.afs->appendRelPathToItemPathImpl(ap.itemPathImpl, relPath)); + } - static bool havePathDependency(const AbstractBaseFolder& lhs, const AbstractBaseFolder& rhs); + static Zstring getFileShortName(const AbstractPath& ap) { return ap.afs->getFileShortName(ap.itemPathImpl); } - static AbstractPathRef appendRelPath(const AbstractPathRef& ap, const Zstring& relPath) { return AbstractPathRef(*ap.abf, ap.abf->appendRelPathToItemPathImpl(ap.itemPathImpl, relPath)); } + static bool havePathDependency(const AbstractPath& lhs, const AbstractPath& rhs); - static Opt<Zstring> getNativeItemPath(const AbstractPathRef& ap) { return ap.abf->isNativeFileSystem() ? Opt<Zstring>(ap.itemPathImpl) : NoValue(); } + static Opt<Zstring> getNativeItemPath(const AbstractPath& ap) { return ap.afs->isNativeFileSystem() ? Opt<Zstring>(ap.itemPathImpl) : NoValue(); } - static std::unique_ptr<AbstractPathRef> getParentFolderPath(const AbstractPathRef& ap) + static Opt<AbstractPath> getParentFolderPath(const AbstractPath& ap) { - if (const Opt<Zstring> parentPathImpl = ap.abf->getParentFolderPathImpl(ap.itemPathImpl)) - return std::unique_ptr<AbstractPathRef>(new AbstractPathRef(*ap.abf, *parentPathImpl)); - return nullptr; + if (const Opt<Zstring> parentPathImpl = ap.afs->getParentFolderPathImpl(ap.itemPathImpl)) + return AbstractPath(ap.afs, *parentPathImpl); + return NoValue(); } - //limitation: zen::Opt requires default-constructibility => we need to use std::unique_ptr: //---------------------------------------------------------------------------------------------------------------- - static bool fileExists (const AbstractPathRef& ap) { return ap.abf->fileExists (ap.itemPathImpl); } //noexcept; check whether file or file-symlink exists - static bool folderExists (const AbstractPathRef& ap) { return ap.abf->folderExists (ap.itemPathImpl); } //noexcept; check whether directory or dir-symlink exists - static bool symlinkExists (const AbstractPathRef& ap) { return ap.abf->symlinkExists (ap.itemPathImpl); } //noexcept; check whether a symbolic link exists - static bool somethingExists(const AbstractPathRef& ap) { return ap.abf->somethingExists(ap.itemPathImpl); } //noexcept; check whether any object with this name exists + static bool fileExists (const AbstractPath& ap) { return ap.afs->fileExists (ap.itemPathImpl); } //noexcept; check whether file or file-symlink exists + static bool folderExists (const AbstractPath& ap) { return ap.afs->folderExists (ap.itemPathImpl); } //noexcept; check whether directory or dir-symlink exists + static bool symlinkExists (const AbstractPath& ap) { return ap.afs->symlinkExists (ap.itemPathImpl); } //noexcept; check whether a symbolic link exists + static bool somethingExists(const AbstractPath& ap) { return ap.afs->somethingExists(ap.itemPathImpl); } //noexcept; check whether any object with this name exists //---------------------------------------------------------------------------------------------------------------- //should provide for single ATOMIC folder creation! - static void createFolderSimple(const AbstractPathRef& ap) { ap.abf->createFolderSimple(ap.itemPathImpl); }; //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing + static void createFolderSimple(const AbstractPath& ap) { ap.afs->createFolderSimple(ap.itemPathImpl); } //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing //non-recursive folder deletion: - static void removeFolderSimple(const AbstractPathRef& ap) { ap.abf->removeFolderSimple(ap.itemPathImpl); }; //throw FileError + static void removeFolderSimple(const AbstractPath& ap) { ap.afs->removeFolderSimple(ap.itemPathImpl); } //throw FileError //- no error if already existing //- create recursively if parent directory is not existing - static void createFolderRecursively(const AbstractPathRef& ap); //throw FileError + static void createFolderRecursively(const AbstractPath& ap); //throw FileError - static void removeFolderRecursively(const AbstractPathRef& ap, //throw FileError + static void removeFolderRecursively(const AbstractPath& ap, //throw FileError const std::function<void (const std::wstring& displayPath)>& onBeforeFileDeletion, //optional const std::function<void (const std::wstring& displayPath)>& onBeforeFolderDeletion); //one call for each *existing* object! - static bool removeFile(const AbstractPathRef& ap) { return ap.abf->removeFile(ap.itemPathImpl); }; //throw FileError; return "false" if file is not existing + static bool removeFile(const AbstractPath& ap) { return ap.afs->removeFile(ap.itemPathImpl); } //throw FileError; return "false" if file is not existing //---------------------------------------------------------------------------------------------------------------- - static void setModTime (const AbstractPathRef& ap, std::int64_t modificationTime) { ap.abf->setModTime (ap.itemPathImpl, modificationTime); } //throw FileError, follows symlinks - static void setModTimeSymlink(const AbstractPathRef& ap, std::int64_t modificationTime) { ap.abf->setModTimeSymlink(ap.itemPathImpl, modificationTime); } //throw FileError + static void setModTime (const AbstractPath& ap, std::int64_t modificationTime) { ap.afs->setModTime (ap.itemPathImpl, modificationTime); } //throw FileError, follows symlinks + static void setModTimeSymlink(const AbstractPath& ap, std::int64_t modificationTime) { ap.afs->setModTimeSymlink(ap.itemPathImpl, modificationTime); } //throw FileError - static AbstractPathRef getResolvedSymlinkPath(const AbstractPathRef& ap) { return ap.abf->getResolvedSymlinkPath(ap.itemPathImpl); } //throw FileError + static AbstractPath getResolvedSymlinkPath(const AbstractPath& ap) { return AbstractPath(ap.afs, ap.afs->getResolvedSymlinkPath(ap.itemPathImpl)); } //throw FileError - static Zstring getSymlinkContentBuffer(const AbstractPathRef& ap) { return ap.abf->getSymlinkContentBuffer(ap.itemPathImpl); } //throw FileError + static Zstring getSymlinkContentBuffer(const AbstractPath& ap) { return ap.afs->getSymlinkContentBuffer(ap.itemPathImpl); } //throw FileError //---------------------------------------------------------------------------------------------------------------- + //noexcept; optional return value: + static ImageHolder getFileIcon (const AbstractPath& ap, int pixelSize) { return ap.afs->getFileIcon (ap.itemPathImpl, pixelSize); } + static ImageHolder getThumbnailImage(const AbstractPath& ap, int pixelSize) { return ap.afs->getThumbnailImage(ap.itemPathImpl, pixelSize); } - struct IconLoader - { - std::function<ImageHolder(int pixelSize)> getThumbnailImage; //optional, noexcept! - std::function<ImageHolder(int pixelSize)> getFileIcon; // - }; - - //Async operations: retrieve function objects for asynchronous loading - //- THREAD-SAFETY: must be thread-safe like an int! => no dangling references to this instance! - static IconLoader getAsyncIconLoader(const AbstractPathRef& ap) { return ap.abf->getAsyncIconLoader(ap.itemPathImpl); } //noexcept! - virtual std::function<void()> /*throw FileError*/ getAsyncConnectFolder(bool allowUserInteraction) const = 0; //noexcept, optional return value - static std::function<bool()> /*throw FileError*/ getAsyncCheckFolderExists(const AbstractPathRef& ap) { return ap.abf->getAsyncCheckFolderExists(ap.itemPathImpl); } //noexcept + static bool folderExistsThrowing(const AbstractPath& ap) { return ap.afs->folderExistsThrowing(ap.itemPathImpl); } //throw FileError + static void connectNetworkFolder(const AbstractPath& ap, bool allowUserInteraction) { return ap.afs->connectNetworkFolder(ap.itemPathImpl, allowUserInteraction); } //throw FileError //---------------------------------------------------------------------------------------------------------------- using FileId = Zbase<char>; @@ -118,49 +114,43 @@ struct AbstractBaseFolder { virtual ~InputStream() {} virtual size_t read(void* buffer, size_t bytesToRead) = 0; //throw FileError; returns "bytesToRead", unless end of file! - virtual FileId getFileId () = 0; //throw FileError - virtual std::int64_t getModificationTime() = 0; //throw FileError - virtual std::uint64_t getFileSize () = 0; //throw FileError - virtual size_t optimalBlockSize() const = 0; //non-zero block size is ABF contract! it's implementers job to always give a reasonable buffer size! - protected: - InputStream() {} - private: - InputStream (InputStream&) = delete; - InputStream& operator=(InputStream&) = delete; + virtual FileId getFileId () = 0; //throw FileError + virtual std::int64_t getModificationTime() = 0; //throw FileError + virtual std::uint64_t getFileSize () = 0; //throw FileError + virtual size_t optimalBlockSize() const = 0; //non-zero block size is AFS contract! it's implementers job to always give a reasonable buffer size! + }; + + struct OutputStreamImpl + { + virtual ~OutputStreamImpl() {} + virtual size_t optimalBlockSize() const = 0; //non-zero block size is AFS contract! + virtual void write (const void* buffer, size_t bytesToWrite ) = 0; //throw FileError + virtual FileId finalize(const std::function<void()>& onUpdateStatus) = 0; //throw FileError }; //TRANSACTIONAL output stream! => call finalize when done! - //- takes ownership and deletes on errors!!!! => transactionally create output stream first if not existing!! - //- AbstractPathRef member => implicit contract: ABF instance needs to out-live OutputStream! struct OutputStream { - virtual ~OutputStream(); + OutputStream(std::unique_ptr<OutputStreamImpl>&& outStream, const AbstractPath& filePath, const std::uint64_t* streamSize); + ~OutputStream(); + size_t optimalBlockSize() const { return outStream_->optimalBlockSize(); } //non-zero block size is AFS contract! void write(const void* buffer, size_t bytesToWrite); //throw FileError FileId finalize(const std::function<void()>& onUpdateStatus); //throw FileError - virtual size_t optimalBlockSize() const = 0; //non-zero block size is ABF contract! - protected: - OutputStream(const AbstractPathRef& filePath, const std::uint64_t* streamSize); - bool finalizeHasSucceeded() const { return finalizeSucceeded; } private: - virtual void writeImpl (const void* buffer, size_t bytesToWrite ) = 0; //throw FileError - virtual FileId finalizeImpl(const std::function<void()>& onUpdateStatus) = 0; //throw FileError - - OutputStream (OutputStream&) = delete; - OutputStream& operator=(OutputStream&) = delete; - - const AbstractPathRef filePath_; + std::unique_ptr<OutputStreamImpl> outStream_; //bound! + const AbstractPath filePath_; bool finalizeSucceeded = false; Opt<std::uint64_t> bytesExpected; std::uint64_t bytesWritten = 0; }; //return value always bound: - static std::unique_ptr<InputStream > getInputStream (const AbstractPathRef& ap) { return ap.abf->getInputStream (ap.itemPathImpl); } //throw FileError, ErrorFileLocked - static std::unique_ptr<OutputStream> getOutputStream(const AbstractPathRef& ap, + static std::unique_ptr<InputStream > getInputStream (const AbstractPath& ap) { return ap.afs->getInputStream (ap.itemPathImpl); } //throw FileError, ErrorFileLocked + static std::unique_ptr<OutputStream> getOutputStream(const AbstractPath& ap, const std::uint64_t* streamSize, //optional const std::int64_t* modificationTime) // - { return ap.abf->getOutputStream(ap.itemPathImpl, streamSize, modificationTime); } //throw FileError, ErrorTargetExisting + { return std::make_unique<OutputStream>(ap.afs->getOutputStream(ap.itemPathImpl, streamSize, modificationTime), ap, streamSize); } //throw FileError, ErrorTargetExisting //---------------------------------------------------------------------------------------------------------------- struct TraverserCallback @@ -209,29 +199,31 @@ struct AbstractBaseFolder }; //- client needs to handle duplicate file reports! (FilePlusTraverser fallback, retrying to read directory contents, ...) - static void traverseFolder(const AbstractPathRef& ap, TraverserCallback& sink) { ap.abf->traverseFolder(ap.itemPathImpl, sink); } + static void traverseFolder(const AbstractPath& ap, TraverserCallback& sink) { ap.afs->traverseFolder(ap.itemPathImpl, sink); } //---------------------------------------------------------------------------------------------------------------- - static bool supportPermissionCopy(const AbstractBaseFolder& baseFolderLeft, const AbstractBaseFolder& baseFolderRight); //throw FileError + static bool supportPermissionCopy(const AbstractPath& apSource, const AbstractPath& apTarget); //throw FileError struct FileAttribAfterCopy { - std::uint64_t fileSize; - std::int64_t modificationTime; //time_t UTC compatible + std::uint64_t fileSize = 0; + std::int64_t modificationTime = 0; //time_t UTC compatible FileId sourceFileId; FileId targetFileId; }; //return current attributes at the time of copy //symlink handling: dereference source - static FileAttribAfterCopy copyFileAsStream(const AbstractPathRef& apSource, const AbstractPathRef& apTarget, //throw FileError, ErrorTargetExisting, ErrorFileLocked + static FileAttribAfterCopy copyFileAsStream(const AbstractPath& apSource, const AbstractPath& apTarget, //throw FileError, ErrorTargetExisting, ErrorFileLocked //accummulated delta != file size! consider ADS, sparse, compressed files - const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus); //may be nullptr; throw X! + const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus) //may be nullptr; throw X! + { return apSource.afs->copyFileAsStream(apSource.itemPathImpl, apTarget, onNotifyCopyStatus); } + //Note: it MAY happen that copyFileTransactional() leaves temp files behind, e.g. temporary network drop. // => clean them up at an appropriate time (automatically set sync directions to delete them). They have the following ending: static const Zchar* TEMP_FILE_ENDING; //don't use Zstring as global constant: avoid static initialization order problem in global namespace! - static FileAttribAfterCopy copyFileTransactional(const AbstractPathRef& apSource, const AbstractPathRef& apTarget, //throw FileError, ErrorFileLocked + static FileAttribAfterCopy copyFileTransactional(const AbstractPath& apSource, const AbstractPath& apTarget, //throw FileError, ErrorFileLocked bool copyFilePermissions, bool transactionalCopy, //if target is existing user needs to implement deletion: copyFile() NEVER overwrites target if already existing! @@ -239,75 +231,61 @@ struct AbstractBaseFolder const std::function<void()>& onDeleteTargetFile, const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus); - static void copyNewFolder(const AbstractPathRef& apSource, const AbstractPathRef& apTarget, bool copyFilePermissions); //throw FileError - static void copySymlink (const AbstractPathRef& apSource, const AbstractPathRef& apTarget, bool copyFilePermissions); //throw FileError - static void renameItem (const AbstractPathRef& apSource, const AbstractPathRef& apTarget); //throw FileError, ErrorTargetExisting, ErrorDifferentVolume + static void copyNewFolder(const AbstractPath& apSource, const AbstractPath& apTarget, bool copyFilePermissions); //throw FileError + static void copySymlink (const AbstractPath& apSource, const AbstractPath& apTarget, bool copyFilePermissions); //throw FileError + static void renameItem (const AbstractPath& apSource, const AbstractPath& apTarget); //throw FileError, ErrorTargetExisting, ErrorDifferentVolume //---------------------------------------------------------------------------------------------------------------- - //---------------------------------------------------------------------------------------------------------------- - - virtual ~AbstractBaseFolder() {} - - AbstractPathRef getAbstractPath(const Zstring& relPath) const - { -#ifdef ZEN_WIN - assert(!contains(relPath, L"/")); //relPath is expected to use FILE_NAME_SEPARATOR! -#endif - assert(relPath.empty() || (!startsWith(relPath, FILE_NAME_SEPARATOR) && !endsWith(relPath, FILE_NAME_SEPARATOR))); - - return AbstractPathRef(*this, appendRelPathToItemPathImpl(getBasePathImpl(), relPath)); - } - AbstractPathRef getAbstractPath() const { return AbstractPathRef(*this, getBasePathImpl()); } - - //limitation: zen::Opt requires default-constructibility => we need to use std::unique_ptr: - std::unique_ptr<AbstractPathRef> getAbstractPathFromNativePath(const Zstring& nativePath) const { return isNativeFileSystem() ? std::unique_ptr<AbstractPathRef>(new AbstractPathRef(*this, nativePath)) : nullptr; } - - virtual bool emptyBaseFolderPath() const = 0; - virtual std::uint64_t getFreeDiskSpace() const = 0; //throw FileError, returns 0 if not available + static std::uint64_t getFreeDiskSpace(const AbstractPath& ap) { return ap.afs->getFreeDiskSpace(ap.itemPathImpl); } //throw FileError, returns 0 if not available - //---------------------------------------------------------------------------------------------------------------- - virtual bool supportsRecycleBin(const std::function<void ()>& onUpdateGui) const = 0; //throw FileError + static bool supportsRecycleBin(const AbstractPath& ap, const std::function<void ()>& onUpdateGui) { return ap.afs->supportsRecycleBin(ap.itemPathImpl, onUpdateGui); } //throw FileError struct RecycleSession { - virtual ~RecycleSession() {}; - virtual bool recycleItem(const AbstractPathRef& ap, const Zstring& logicalRelPath) = 0; //throw FileError; return true if item existed + virtual ~RecycleSession() {} + virtual bool recycleItem(const AbstractPath& itemPath, const Zstring& logicalRelPath) = 0; //throw FileError; return true if item existed virtual void tryCleanup(const std::function<void (const std::wstring& displayPath)>& notifyDeletionStatus /*optional; currentItem may be empty*/) = 0; //throw FileError }; - virtual std::unique_ptr<RecycleSession> createRecyclerSession() const = 0; //throw FileError, precondition: supportsRecycleBin(); return value must be bound! - static void recycleItemDirectly(const AbstractPathRef& ap) { ap.abf->recycleItemDirectly(ap.itemPathImpl); } //throw FileError + //precondition: supportsRecycleBin() must return true! + static std::unique_ptr<RecycleSession> createRecyclerSession(const AbstractPath& ap) { return ap.afs->createRecyclerSession(ap.itemPathImpl); } //throw FileError, return value must be bound! + + static void recycleItemDirectly(const AbstractPath& ap) { ap.afs->recycleItemDirectly(ap.itemPathImpl); } //throw FileError - //================================================================================================================ //================================================================================================================ //no need to protect access: - static const AbstractBaseFolder& getAbf(const AbstractPathRef& ap) { return *ap.abf; } - static Zstring appendPaths(const Zstring& basePath, const Zstring& relPath, Zchar pathSep); -protected: //grant derived classes access to AbstractPathRef: - static Zstring getItemPathImpl(const AbstractPathRef& ap) { return ap.itemPathImpl; } - static AbstractPathRef makeAbstractItem(const AbstractBaseFolder& abfIn, const Zstring& itemPathImplIn) { return AbstractPathRef(abfIn, itemPathImplIn); } + virtual ~AbstractFileSystem() {} + +protected: //grant derived classes access to AbstractPath: + static const AbstractFileSystem& getAfs (const AbstractPath& ap) { return *ap.afs; } + static Zstring getItemPathImpl(const AbstractPath& ap) { return ap.itemPathImpl; } + + FileAttribAfterCopy copyFileAsStream(const Zstring& itemPathImplSource, const AbstractPath& apTarget, //throw FileError, ErrorTargetExisting, ErrorFileLocked + const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus) const; //may be nullptr; throw X! private: - virtual bool isNativeFileSystem() const { return false; }; + virtual bool isNativeFileSystem() const { return false; } + + virtual Zstring getInitPathPhrase(const Zstring& itemPathImpl) const = 0; virtual std::wstring getDisplayPath(const Zstring& itemPathImpl) const = 0; - virtual Zstring appendRelPathToItemPathImpl(const Zstring& itemPathImpl, const Zstring& relPath) const = 0; + virtual bool isNullPath(const Zstring& itemPathImpl) const = 0; - virtual Zstring getBasePathImpl() const = 0; + virtual Zstring appendRelPathToItemPathImpl(const Zstring& itemPathImpl, const Zstring& relPath) const = 0; //used during folder creation if parent folder is missing virtual Opt<Zstring> getParentFolderPathImpl(const Zstring& itemPathImpl) const = 0; virtual Zstring getFileShortName(const Zstring& itemPathImpl) const = 0; - virtual bool lessItemPathSameAbfType(const Zstring& itemPathImplLhs, const AbstractPathRef& apRhs) const = 0; + virtual bool lessItemPathSameAfsType(const Zstring& itemPathImplLhs, const AbstractPath& apRhs) const = 0; - virtual bool havePathDependencySameAbfType(const AbstractBaseFolder& other) const = 0; + virtual bool havePathDependencySameAfsType(const Zstring& itemPathImplLhs, const AbstractPath& apRhs) const = 0; //---------------------------------------------------------------------------------------------------------------- virtual bool fileExists (const Zstring& itemPathImpl) const = 0; //noexcept @@ -328,41 +306,48 @@ private: virtual void setModTime (const Zstring& itemPathImpl, std::int64_t modificationTime) const = 0; //throw FileError, follows symlinks virtual void setModTimeSymlink(const Zstring& itemPathImpl, std::int64_t modificationTime) const = 0; //throw FileError - virtual AbstractPathRef getResolvedSymlinkPath(const Zstring& itemPathImpl) const = 0; //throw FileError + virtual Zstring getResolvedSymlinkPath(const Zstring& itemPathImpl) const = 0; //throw FileError virtual Zstring getSymlinkContentBuffer(const Zstring& itemPathImpl) const = 0; //throw FileError - virtual void recycleItemDirectly(const Zstring& itemPathImpl) const = 0; //throw FileError - - //---------------------------------------------------------------------------------------------------------------- - //- THREAD-SAFETY: must be thread-safe like an int! => no dangling references to this instance! - virtual IconLoader getAsyncIconLoader(const Zstring& itemPathImpl) const = 0; //noexcept! - virtual std::function<bool()> /*throw FileError*/ getAsyncCheckFolderExists(const Zstring& itemPathImpl) const = 0; //noexcept //---------------------------------------------------------------------------------------------------------------- - virtual std::unique_ptr<InputStream > getInputStream (const Zstring& itemPathImpl) const = 0; //throw FileError, ErrorFileLocked - virtual std::unique_ptr<OutputStream> getOutputStream(const Zstring& itemPathImpl, //throw FileError, ErrorTargetExisting - const std::uint64_t* streamSize, //optional - const std::int64_t* modificationTime) const = 0; // + virtual std::unique_ptr<InputStream > getInputStream (const Zstring& itemPathImpl) const = 0; //throw FileError, ErrorFileLocked + virtual std::unique_ptr<OutputStreamImpl> getOutputStream(const Zstring& itemPathImpl, //throw FileError, ErrorTargetExisting + const std::uint64_t* streamSize, //optional + const std::int64_t* modificationTime) const = 0; // //---------------------------------------------------------------------------------------------------------------- virtual void traverseFolder(const Zstring& itemPathImpl, TraverserCallback& sink) const = 0; //noexcept //---------------------------------------------------------------------------------------------------------------- //symlink handling: follow link! - virtual FileAttribAfterCopy copyFileForSameAbfType(const Zstring& itemPathImplSource, const AbstractPathRef& apTarget, bool copyFilePermissions, //throw FileError, ErrorTargetExisting, ErrorFileLocked + virtual FileAttribAfterCopy copyFileForSameAfsType(const Zstring& itemPathImplSource, const AbstractPath& apTarget, bool copyFilePermissions, //throw FileError, ErrorTargetExisting, ErrorFileLocked //accummulated delta != file size! consider ADS, sparse, compressed files const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus) const = 0; //may be nullptr; throw X! //symlink handling: follow link! - virtual void copyNewFolderForSameAbfType(const Zstring& itemPathImplSource, const AbstractPathRef& apTarget, bool copyFilePermissions) const = 0; //throw FileError - virtual void copySymlinkForSameAbfType (const Zstring& itemPathImplSource, const AbstractPathRef& apTarget, bool copyFilePermissions) const = 0; //throw FileError - virtual void renameItemForSameAbfType (const Zstring& itemPathImplSource, const AbstractPathRef& apTarget) const = 0; //throw FileError, ErrorTargetExisting, ErrorDifferentVolume - virtual bool supportsPermissions() const = 0; //throw FileError + virtual void copyNewFolderForSameAfsType(const Zstring& itemPathImplSource, const AbstractPath& apTarget, bool copyFilePermissions) const = 0; //throw FileError + virtual void copySymlinkForSameAfsType (const Zstring& itemPathImplSource, const AbstractPath& apTarget, bool copyFilePermissions) const = 0; //throw FileError + virtual void renameItemForSameAfsType (const Zstring& itemPathImplSource, const AbstractPath& apTarget) const = 0; //throw FileError, ErrorTargetExisting, ErrorDifferentVolume + virtual bool supportsPermissions(const Zstring& itemPathImpl) const = 0; //throw FileError + + //---------------------------------------------------------------------------------------------------------------- + virtual ImageHolder getFileIcon (const Zstring& itemPathImpl, int pixelSize) const = 0; //noexcept; optional return value + virtual ImageHolder getThumbnailImage(const Zstring& itemPathImpl, int pixelSize) const = 0; // + + virtual bool folderExistsThrowing(const Zstring& itemPathImpl) const = 0; //throw FileError + virtual void connectNetworkFolder(const Zstring& itemPathImpl, bool allowUserInteraction) const = 0; //throw FileError + //---------------------------------------------------------------------------------------------------------------- + + virtual std::uint64_t getFreeDiskSpace(const Zstring& itemPathImpl) const = 0; //throw FileError, returns 0 if not available + virtual bool supportsRecycleBin(const Zstring& itemPathImpl, const std::function<void ()>& onUpdateGui) const = 0; //throw FileError + virtual std::unique_ptr<RecycleSession> createRecyclerSession(const Zstring& itemPathImpl) const = 0; //throw FileError, return value must be bound! + virtual void recycleItemDirectly(const Zstring& itemPathImpl) const = 0; //throw FileError }; //implement "retry" in a generic way: template <class Command> inline //function object expecting to throw FileError if operation fails -bool tryReportingDirError(Command cmd, AbstractBaseFolder::TraverserCallback& callback) //return "true" on success, "false" if error was ignored +bool tryReportingDirError(Command cmd, AbstractFileSystem::TraverserCallback& callback) //return "true" on success, "false" if error was ignored { for (size_t retryNumber = 0;; ++retryNumber) try @@ -374,9 +359,9 @@ bool tryReportingDirError(Command cmd, AbstractBaseFolder::TraverserCallback& ca { switch (callback.reportDirError(e.toString(), retryNumber)) { - case AbstractBaseFolder::TraverserCallback::ON_ERROR_RETRY: + case AbstractFileSystem::TraverserCallback::ON_ERROR_RETRY: break; - case AbstractBaseFolder::TraverserCallback::ON_ERROR_IGNORE: + case AbstractFileSystem::TraverserCallback::ON_ERROR_IGNORE: return false; } } @@ -384,7 +369,7 @@ bool tryReportingDirError(Command cmd, AbstractBaseFolder::TraverserCallback& ca template <class Command> inline //function object expecting to throw FileError if operation fails -bool tryReportingItemError(Command cmd, AbstractBaseFolder::TraverserCallback& callback, const Zstring& itemName) //return "true" on success, "false" if error was ignored +bool tryReportingItemError(Command cmd, AbstractFileSystem::TraverserCallback& callback, const Zstring& itemName) //return "true" on success, "false" if error was ignored { for (size_t retryNumber = 0;; ++retryNumber) try @@ -396,9 +381,9 @@ bool tryReportingItemError(Command cmd, AbstractBaseFolder::TraverserCallback& c { switch (callback.reportItemError(e.toString(), retryNumber, itemName)) { - case AbstractBaseFolder::TraverserCallback::ON_ERROR_RETRY: + case AbstractFileSystem::TraverserCallback::ON_ERROR_RETRY: break; - case AbstractBaseFolder::TraverserCallback::ON_ERROR_IGNORE: + case AbstractFileSystem::TraverserCallback::ON_ERROR_IGNORE: return false; } } @@ -412,55 +397,33 @@ bool tryReportingItemError(Command cmd, AbstractBaseFolder::TraverserCallback& c //------------------------------------ implementation ----------------------------------------- -struct AbstractPathRef::ItemId -{ - ItemId(const AbstractBaseFolder* abfIn, const Zstring& itemPathImplIn) : abf(abfIn), itemPathImpl(itemPathImplIn) {} - - inline friend bool operator<(const ItemId& lhs, const ItemId& rhs) { return lhs.abf != rhs.abf ? lhs.abf < rhs.abf : lhs.itemPathImpl < rhs.itemPathImpl; } - //don't treat itemPathImpl like regular file path => no case-insensitive comparison on Windows/OS X! - -private: - const void* abf; - Zstring itemPathImpl; -}; - -inline -AbstractPathRef::ItemId AbstractPathRef::getUniqueId() const { return ItemId(abf, itemPathImpl); } - - -struct AbstractBaseFolder::LessItemPath +struct AbstractFileSystem::LessAbstractPath { - bool operator()(const AbstractPathRef& lhs, const AbstractPathRef& rhs) const + bool operator()(const AbstractPath& lhs, const AbstractPath& rhs) const { //note: in worst case, order is guaranteed to be stable only during each program run - return typeid(*lhs.abf) != typeid(*rhs.abf) ? typeid(*lhs.abf).before(typeid(*rhs.abf)) : lhs.abf->lessItemPathSameAbfType(lhs.itemPathImpl, rhs); + return typeid(*lhs.afs) != typeid(*rhs.afs) ? typeid(*lhs.afs).before(typeid(*rhs.afs)) : lhs.afs->lessItemPathSameAfsType(lhs.itemPathImpl, rhs); //caveat: typeid returns static type for pointers, dynamic type for references!!! } - bool operator()(const AbstractBaseFolder* lhs, const AbstractBaseFolder* rhs) const - { - return (*this)(lhs->getAbstractPath(), rhs->getAbstractPath()); - } - //bool operator()(const std::shared_ptr<AbstractBaseFolder>& lhs, const std::shared_ptr<AbstractBaseFolder>& rhs) const - //-> avoid overload ambiguity with "const ABF*" which may erroneously convert to std::shared_ptr!!! }; inline -bool AbstractBaseFolder::equalItemPath(const AbstractPathRef& lhs, const AbstractPathRef& rhs) +bool AbstractFileSystem::equalAbstractPath(const AbstractPath& lhs, const AbstractPath& rhs) { - return !LessItemPath()(lhs, rhs) && !LessItemPath()(rhs, lhs); + return !LessAbstractPath()(lhs, rhs) && !LessAbstractPath()(rhs, lhs); } inline -bool AbstractBaseFolder::havePathDependency(const AbstractBaseFolder& lhs, const AbstractBaseFolder& rhs) +bool AbstractFileSystem::havePathDependency(const AbstractPath& lhs, const AbstractPath& rhs) { - return typeid(lhs) != typeid(rhs) ? false : lhs.havePathDependencySameAbfType(rhs); + return typeid(*lhs.afs) != typeid(*rhs.afs) ? false : lhs.afs->havePathDependencySameAfsType(lhs.itemPathImpl, rhs); }; inline -Zstring AbstractBaseFolder::appendPaths(const Zstring& basePath, const Zstring& relPath, Zchar pathSep) +Zstring AbstractFileSystem::appendPaths(const Zstring& basePath, const Zstring& relPath, Zchar pathSep) { if (relPath.empty()) return basePath; @@ -485,7 +448,8 @@ Zstring AbstractBaseFolder::appendPaths(const Zstring& basePath, const Zstring& //-------------------------------------------------------------------------- inline -AbstractBaseFolder::OutputStream::OutputStream(const AbstractPathRef& filePath, const std::uint64_t* streamSize) : filePath_(filePath) +AbstractFileSystem::OutputStream::OutputStream(std::unique_ptr<OutputStreamImpl>&& outStream, const AbstractPath& filePath, const std::uint64_t* streamSize) : + outStream_(std::move(outStream)), filePath_(filePath) { if (streamSize) bytesExpected = *streamSize; @@ -493,30 +457,36 @@ AbstractBaseFolder::OutputStream::OutputStream(const AbstractPathRef& filePath, inline -AbstractBaseFolder::OutputStream::~OutputStream() +AbstractFileSystem::OutputStream::~OutputStream() { + //delete file on errors: => fail if already existing BEFORE creating OutputStream instance!! + + outStream_.reset(); //close file handle *before* remove! + if (!finalizeSucceeded) //transactional output stream! => clean up! - try { removeFile(filePath_); /*throw FileError*/ } + try { AbstractFileSystem::removeFile(filePath_); /*throw FileError*/ } catch (FileError& e) { (void)e; assert(false); } } inline -void AbstractBaseFolder::OutputStream::write(const void* buffer, size_t bytesToWrite) +void AbstractFileSystem::OutputStream::write(const void* buffer, size_t bytesToWrite) { bytesWritten += bytesToWrite; - writeImpl(buffer, bytesToWrite); //throw FileError + outStream_->write(buffer, bytesToWrite); //throw FileError } inline -AbstractBaseFolder::FileId AbstractBaseFolder::OutputStream::finalize(const std::function<void()>& onUpdateStatus) //throw FileError +AbstractFileSystem::FileId AbstractFileSystem::OutputStream::finalize(const std::function<void()>& onUpdateStatus) //throw FileError { if (bytesExpected && bytesWritten != *bytesExpected) throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(getDisplayPath(filePath_))), - replaceCpy(replaceCpy(_("Unexpected size of data stream.\nExpected: %x bytes\nActual: %y bytes"), L"%x", numberTo<std::wstring>(*bytesExpected)), L"%y", numberTo<std::wstring>(bytesWritten))); + replaceCpy(replaceCpy(_("Unexpected size of data stream.\nExpected: %x bytes\nActual: %y bytes"), + L"%x", numberTo<std::wstring>(*bytesExpected)), + L"%y", numberTo<std::wstring>(bytesWritten))); - const FileId fileId = finalizeImpl(onUpdateStatus); //throw FileError + const FileId fileId = outStream_->finalize(onUpdateStatus); //throw FileError finalizeSucceeded = true; return fileId; @@ -525,10 +495,10 @@ AbstractBaseFolder::FileId AbstractBaseFolder::OutputStream::finalize(const std: //-------------------------------------------------------------------------- inline -void AbstractBaseFolder::copyNewFolder(const AbstractPathRef& apSource, const AbstractPathRef& apTarget, bool copyFilePermissions) //throw FileError +void AbstractFileSystem::copyNewFolder(const AbstractPath& apSource, const AbstractPath& apTarget, bool copyFilePermissions) //throw FileError { - if (typeid(*apSource.abf) == typeid(*apTarget.abf)) - return apSource.abf->copyNewFolderForSameAbfType(apSource.itemPathImpl, apTarget, copyFilePermissions); //throw FileError + if (typeid(*apSource.afs) == typeid(*apTarget.afs)) + return apSource.afs->copyNewFolderForSameAfsType(apSource.itemPathImpl, apTarget, copyFilePermissions); //throw FileError //fall back: if (copyFilePermissions) @@ -540,37 +510,38 @@ void AbstractBaseFolder::copyNewFolder(const AbstractPathRef& apSource, const Ab inline -void AbstractBaseFolder::copySymlink(const AbstractPathRef& apSource, const AbstractPathRef& apTarget, bool copyFilePermissions) //throw FileError +void AbstractFileSystem::copySymlink(const AbstractPath& apSource, const AbstractPath& apTarget, bool copyFilePermissions) //throw FileError { - if (typeid(*apSource.abf) == typeid(*apTarget.abf)) - return apSource.abf->copySymlinkForSameAbfType(apSource.itemPathImpl, apTarget, copyFilePermissions); //throw FileError + if (typeid(*apSource.afs) == typeid(*apTarget.afs)) + return apSource.afs->copySymlinkForSameAfsType(apSource.itemPathImpl, apTarget, copyFilePermissions); //throw FileError - throw FileError(replaceCpy(replaceCpy(_("Cannot copy symbolic link %x to %y."), L"%x", - L"\n" + fmtPath(getDisplayPath(apSource))), L"%y", - L"\n" + fmtPath(getDisplayPath(apTarget))), _("Operation not supported for different base folder types.")); + throw FileError(replaceCpy(replaceCpy(_("Cannot copy symbolic link %x to %y."), + L"%x", L"\n" + fmtPath(getDisplayPath(apSource))), + L"%y", L"\n" + fmtPath(getDisplayPath(apTarget))), _("Operation not supported for different base folder types.")); } inline -void AbstractBaseFolder::renameItem(const AbstractPathRef& apSource, const AbstractPathRef& apTarget) //throw FileError, ErrorTargetExisting, ErrorDifferentVolume +void AbstractFileSystem::renameItem(const AbstractPath& apSource, const AbstractPath& apTarget) //throw FileError, ErrorTargetExisting, ErrorDifferentVolume { - if (typeid(*apSource.abf) == typeid(*apTarget.abf)) - return apSource.abf->renameItemForSameAbfType(apSource.itemPathImpl, apTarget); //throw FileError, ErrorTargetExisting, ErrorDifferentVolume + if (typeid(*apSource.afs) == typeid(*apTarget.afs)) + return apSource.afs->renameItemForSameAfsType(apSource.itemPathImpl, apTarget); //throw FileError, ErrorTargetExisting, ErrorDifferentVolume - throw ErrorDifferentVolume(replaceCpy(replaceCpy(_("Cannot move file %x to %y."), L"%x", - L"\n" + fmtPath(getDisplayPath(apSource))), L"%y", - L"\n" + fmtPath(getDisplayPath(apTarget))), _("Operation not supported for different base folder types.")); + throw ErrorDifferentVolume(replaceCpy(replaceCpy(_("Cannot move file %x to %y."), + L"%x", L"\n" + fmtPath(getDisplayPath(apSource))), + L"%y", L"\n" + fmtPath(getDisplayPath(apTarget))), _("Operation not supported for different base folder types.")); } inline -bool AbstractBaseFolder::supportPermissionCopy(const AbstractBaseFolder& baseFolderLeft, const AbstractBaseFolder& baseFolderRight) //throw FileError +bool AbstractFileSystem::supportPermissionCopy(const AbstractPath& apSource, const AbstractPath& apTarget) //throw FileError { - if (typeid(baseFolderLeft) == typeid(baseFolderRight)) - return baseFolderLeft .supportsPermissions() && //throw FileError - baseFolderRight.supportsPermissions(); - return false; + if (typeid(*apSource.afs) != typeid(*apTarget.afs)) + return false; + + return apSource.afs->supportsPermissions(apSource.itemPathImpl) && //throw FileError + apTarget.afs->supportsPermissions(apTarget.itemPathImpl); } } -#endif //ABSTRACT_FS_873450978453042524534234 +#endif //ABSTRACT_H_873450978453042524534234 diff --git a/FreeFileSync/Source/fs/concrete.cpp b/FreeFileSync/Source/fs/concrete.cpp index 67605d03..d9ab257d 100644 --- a/FreeFileSync/Source/fs/concrete.cpp +++ b/FreeFileSync/Source/fs/concrete.cpp @@ -12,24 +12,23 @@ #endif using namespace zen; -using ABF = AbstractBaseFolder; -std::unique_ptr<AbstractBaseFolder> zen::createAbstractBaseFolder(const Zstring& folderPathPhrase) //noexcept +AbstractPath zen::createAbstractPath(const Zstring& itemPathPhrase) //noexcept { //greedy: try native evaluation first - if (acceptsFolderPathPhraseNative(folderPathPhrase)) //noexcept - return createBaseFolderNative(folderPathPhrase); //noexcept + if (acceptsItemPathPhraseNative(itemPathPhrase)) //noexcept + return createItemPathNative(itemPathPhrase); //noexcept //then the rest: #ifdef ZEN_WIN_VISTA_AND_LATER - if (acceptsFolderPathPhraseMtp(folderPathPhrase)) //noexcept - return createBaseFolderMtp(folderPathPhrase); //noexcept + if (acceptsItemPathPhraseMtp(itemPathPhrase)) //noexcept + return createItemPathMtp(itemPathPhrase); //noexcept - if (acceptsFolderPathPhraseSftp(folderPathPhrase)) //noexcept - return createBaseFolderSftp(folderPathPhrase); //noexcept + if (acceptsItemPathPhraseSftp(itemPathPhrase)) //noexcept + return createItemPathSftp(itemPathPhrase); //noexcept #endif //no idea? => native! - return createBaseFolderNative(folderPathPhrase); + return createItemPathNative(itemPathPhrase); } diff --git a/FreeFileSync/Source/fs/concrete.h b/FreeFileSync/Source/fs/concrete.h index 7b3d171c..fdee2485 100644 --- a/FreeFileSync/Source/fs/concrete.h +++ b/FreeFileSync/Source/fs/concrete.h @@ -11,7 +11,7 @@ namespace zen { -std::unique_ptr<AbstractBaseFolder> createAbstractBaseFolder(const Zstring& folderPathPhrase); //noexcept +AbstractPath createAbstractPath(const Zstring& itemPathPhrase); //noexcept } #endif //FS_CONCRETE_348787329573243 diff --git a/FreeFileSync/Source/fs/native.cpp b/FreeFileSync/Source/fs/native.cpp index 5398b507..75c3de9c 100644 --- a/FreeFileSync/Source/fs/native.cpp +++ b/FreeFileSync/Source/fs/native.cpp @@ -9,7 +9,6 @@ #include <zen/symlink_target.h> #include <zen/file_io.h> #include <zen/file_id_def.h> -#include <zen/int64.h> #include <zen/stl_tools.h> #include <zen/recycler.h> #include "../lib/resolve_path.h" @@ -17,6 +16,7 @@ #include "native_traverser_impl.h" #ifdef ZEN_WIN + #include <zen/int64.h> #include <zen/com_tools.h> #elif defined ZEN_LINUX || defined ZEN_MAC @@ -44,12 +44,12 @@ void initComForThread() //throw FileError } -class RecycleSessionNative : public AbstractBaseFolder::RecycleSession +class RecycleSessionNative : public AbstractFileSystem::RecycleSession { public: - RecycleSessionNative(const Zstring baseDirPathPf) : baseDirPathPf_(baseDirPathPf) {} + RecycleSessionNative(const Zstring folderPathPf) : baseFolderPathPf_(folderPathPf) {} - bool recycleItem(const AbstractPathRef& ap, const Zstring& logicalRelPath) override; //throw FileError + bool recycleItem(const AbstractPath& itemPath, const Zstring& logicalRelPath) override; //throw FileError void tryCleanup(const std::function<void (const std::wstring& displayPath)>& notifyDeletionStatus) override; //throw FileError private: @@ -60,67 +60,11 @@ private: Zstring recyclerTmpDir; //temporary folder holding files/folders for *deferred* recycling #endif - const Zstring baseDirPathPf_; //ends with path separator + const Zstring baseFolderPathPf_; //ends with path separator }; //=========================================================================================================================== -template <class Function> inline -void evalAttributeByHandle(FileHandle fh, const Zstring& filePath, Function evalFileInfo) //throw FileError -{ -#ifdef ZEN_WIN - BY_HANDLE_FILE_INFORMATION fileInfo = {}; - if (!::GetFileInformationByHandle(fh, &fileInfo)) - THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(filePath)), L"GetFileInformationByHandle"); - -#elif defined ZEN_LINUX || defined ZEN_MAC - struct ::stat fileInfo = {}; - if (::fstat(fh, &fileInfo) != 0) - THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(filePath)), L"fstat"); -#endif - evalFileInfo(fileInfo); -} - - -inline -ABF::FileId getFileId(FileHandle fh, const Zstring& filePath) //throw FileError -{ - zen::FileId fid; -#ifdef ZEN_WIN - evalAttributeByHandle(fh, filePath, [&](const BY_HANDLE_FILE_INFORMATION& fileInfo) { fid = extractFileId(fileInfo); }); //throw FileError -#elif defined ZEN_LINUX || defined ZEN_MAC - evalAttributeByHandle(fh, filePath, [&](const struct ::stat& fileInfo) { fid = extractFileId(fileInfo); }); //throw FileError -#endif - return convertToAbstractFileId(fid); -} - - -inline -std::int64_t getModificationTime(FileHandle fh, const Zstring& filePath) //throw FileError -{ - std::int64_t modTime = 0; -#ifdef ZEN_WIN - evalAttributeByHandle(fh, filePath, [&](const BY_HANDLE_FILE_INFORMATION& fileInfo) { modTime = filetimeToTimeT(fileInfo.ftLastWriteTime); }); //throw FileError -#elif defined ZEN_LINUX || defined ZEN_MAC - evalAttributeByHandle(fh, filePath, [&](const struct ::stat& fileInfo) { modTime = fileInfo.st_mtime; }); //throw FileError -#endif - return modTime; -} - - -inline -std::uint64_t getFileSize(FileHandle fh, const Zstring& filePath) //throw FileError -{ - std::uint64_t fileSize = 0; -#ifdef ZEN_WIN - evalAttributeByHandle(fh, filePath, [&](const BY_HANDLE_FILE_INFORMATION& fileInfo) { fileSize = get64BitUInt(fileInfo.nFileSizeLow, fileInfo.nFileSizeHigh); }); //throw FileError -#elif defined ZEN_LINUX || defined ZEN_MAC - evalAttributeByHandle(fh, filePath, [&](const struct ::stat& fileInfo) { fileSize = makeUnsigned(fileInfo.st_size); }); //throw FileError -#endif - return fileSize; -} - - void preAllocateSpaceBestEffort(FileHandle fh, const std::uint64_t streamSize, const Zstring& displayPath) //throw FileError { #ifdef ZEN_WIN @@ -171,26 +115,88 @@ void preAllocateSpaceBestEffort(FileHandle fh, const std::uint64_t streamSize, c } -struct InputStreamNative : public AbstractBaseFolder::InputStream +#ifdef ZEN_WIN + using FileAttribs = BY_HANDLE_FILE_INFORMATION; +#elif defined ZEN_LINUX || defined ZEN_MAC + typedef struct ::stat FileAttribs; //GCC 5.2 fails when "::" is used in "using FileAttribs = struct ::stat" +#endif + + +inline +FileAttribs getFileAttributes(FileHandle fh, const Zstring& filePath) //throw FileError +{ +#ifdef ZEN_WIN + BY_HANDLE_FILE_INFORMATION fileAttr = {}; + if (!::GetFileInformationByHandle(fh, &fileAttr)) + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(filePath)), L"GetFileInformationByHandle"); + +#elif defined ZEN_LINUX || defined ZEN_MAC + struct ::stat fileAttr = {}; + if (::fstat(fh, &fileAttr) != 0) + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(filePath)), L"fstat"); +#endif + return fileAttr; +} + + +struct InputStreamNative : public AbstractFileSystem::InputStream { InputStreamNative(const Zstring& filePath) : fi(filePath) {} //throw FileError, ErrorFileLocked size_t read(void* buffer, size_t bytesToRead) override { return fi.read(buffer, bytesToRead); } //throw FileError; returns "bytesToRead", unless end of file! - ABF::FileId getFileId () override { return ::getFileId (fi.getHandle(), fi.getFilePath()); } //throw FileError - std::int64_t getModificationTime() override { return ::getModificationTime(fi.getHandle(), fi.getFilePath()); } //throw FileError - std::uint64_t getFileSize () override { return ::getFileSize (fi.getHandle(), fi.getFilePath()); } //throw FileError - size_t optimalBlockSize() const override { return fi.optimalBlockSize(); } //non-zero block size is ABF contract! + AFS::FileId getFileId () override; //throw FileError + std::int64_t getModificationTime() override; //throw FileError + std::uint64_t getFileSize () override; //throw FileError + size_t optimalBlockSize() const override { return fi.optimalBlockSize(); } //non-zero block size is AFS contract! private: + const FileAttribs& getBufferedAttributes() //throw FileError + { + if (!fileAttr) + fileAttr = getFileAttributes(fi.getHandle(), fi.getFilePath()); //throw FileError + return *fileAttr; + } + FileInput fi; + Opt<FileAttribs> fileAttr; }; -struct OutputStreamNative : public AbstractBaseFolder::OutputStream +inline +AFS::FileId InputStreamNative::getFileId() +{ + return convertToAbstractFileId(extractFileId(getBufferedAttributes())); //throw FileError +} + + +inline +std::int64_t InputStreamNative::getModificationTime() +{ +#ifdef ZEN_WIN + return filetimeToTimeT(getBufferedAttributes().ftLastWriteTime); //throw FileError +#elif defined ZEN_LINUX || defined ZEN_MAC + return getBufferedAttributes().st_mtime; //throw FileError +#endif +} + + +inline +std::uint64_t InputStreamNative::getFileSize() +{ + const FileAttribs& fa = getBufferedAttributes(); //throw FileError +#ifdef ZEN_WIN + return get64BitUInt(fa.nFileSizeLow, fa.nFileSizeHigh); +#elif defined ZEN_LINUX || defined ZEN_MAC + return makeUnsigned(fa.st_size); //throw FileError +#endif +} + +//=========================================================================================================================== + +struct OutputStreamNative : public AbstractFileSystem::OutputStreamImpl { - OutputStreamNative(FileOutput&& fop, const AbstractPathRef& abstractFilePath, const std::uint64_t* streamSize, const std::int64_t* modTime) : - OutputStream(abstractFilePath, streamSize), - fo(std::move(fop)) //OutputStream takes ownership and deletes on errors!!!! => transactionally create output stream first if not existing!! + OutputStreamNative(const Zstring& filePath, const std::uint64_t* streamSize, const std::int64_t* modTime) : + fo(filePath, FileOutput::ACC_CREATE_NEW) //throw FileError, ErrorTargetExisting { if (modTime) modTime_ = *modTime; @@ -199,14 +205,14 @@ struct OutputStreamNative : public AbstractBaseFolder::OutputStream preAllocateSpaceBestEffort(fo.getHandle(), *streamSize, fo.getFilePath()); //throw FileError } - size_t optimalBlockSize() const override { return fo.optimalBlockSize(); } //non-zero block size is ABF contract! + size_t optimalBlockSize() const override { return fo.optimalBlockSize(); } //non-zero block size is AFS contract! private: - void writeImpl(const void* buffer, size_t bytesToWrite) override { fo.write(buffer, bytesToWrite); } //throw FileError + void write(const void* buffer, size_t bytesToWrite) override { fo.write(buffer, bytesToWrite); } //throw FileError - ABF::FileId finalizeImpl(const std::function<void()>& onUpdateStatus) override //throw FileError + AFS::FileId finalize(const std::function<void()>& onUpdateStatus) override //throw FileError { - const ABF::FileId fileId = getFileId(fo.getHandle(), fo.getFilePath()); //throw FileError + const AFS::FileId fileId = convertToAbstractFileId(extractFileId(getFileAttributes(fo.getHandle(), fo.getFilePath()))); //throw FileError if (onUpdateStatus) onUpdateStatus(); //throw X! fo.close(); //throw FileError @@ -234,63 +240,28 @@ private: //=========================================================================================================================== -class NativeBaseFolder : public AbstractBaseFolder +class NativeFileSystem : public AbstractFileSystem { public: //itemPathImpl := native full item path as used by OS APIs - NativeBaseFolder(const Zstring& baseDirPathIn) : baseDirPath(baseDirPathIn) {} - - static Zstring getItemPathImplForRecycler(const AbstractPathRef& ap) + static Zstring getItemPathImplForRecycler(const AbstractPath& ap) { - if (typeid(getAbf(ap)) != typeid(NativeBaseFolder)) + if (typeid(getAfs(ap)) != typeid(NativeFileSystem)) throw std::logic_error("Programming Error: Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__)); return getItemPathImpl(ap); } private: - Zstring getInitPathPhrase() const override /*noexcept*/ { return baseDirPath; } - - //copy this instance for life-time management and safe access on any method by a different thread: - std::unique_ptr<AbstractBaseFolder> createIndependentCopy() const override //noexcept - { - return std::make_unique<NativeBaseFolder>(baseDirPath); //safe: Zstring uses atomic ref-count - } - - bool emptyBaseFolderPath() const override { return baseDirPath.empty(); } - - std::uint64_t getFreeDiskSpace() const override //throw FileError, returns 0 if not available - { - initComForThread(); //throw FileError - return zen::getFreeDiskSpace(baseDirPath); //throw FileError - } - - //---------------------------------------------------------------------------------------------------------------- - bool supportsRecycleBin(const std::function<void ()>& onUpdateGui) const override //throw FileError - { -#ifdef ZEN_WIN - initComForThread(); //throw FileError - return recycleBinExists(baseDirPath, onUpdateGui); //throw FileError - -#elif defined ZEN_LINUX || defined ZEN_MAC - return true; //truth be told: no idea!!! -#endif - } - - std::unique_ptr<RecycleSession> createRecyclerSession() const override //throw FileError, return value must be bound! - { - initComForThread(); //throw FileError - assert(supportsRecycleBin(nullptr)); - return std::make_unique<RecycleSessionNative>(appendSeparator(baseDirPath)); - } - bool isNativeFileSystem() const override { return true; } + Zstring getInitPathPhrase(const Zstring& itemPathImpl) const override { return itemPathImpl; } + std::wstring getDisplayPath(const Zstring& itemPathImpl) const override { return utfCvrtTo<std::wstring>(itemPathImpl); } - Zstring appendRelPathToItemPathImpl(const Zstring& itemPathImpl, const Zstring& relPath) const override { return appendPaths(itemPathImpl, relPath, FILE_NAME_SEPARATOR); } + bool isNullPath(const Zstring& itemPathImpl) const override { return itemPathImpl.empty(); } - Zstring getBasePathImpl() const override { return baseDirPath; } + Zstring appendRelPathToItemPathImpl(const Zstring& itemPathImpl, const Zstring& relPath) const override { return appendPaths(itemPathImpl, relPath, FILE_NAME_SEPARATOR); } //used during folder creation if parent folder is missing Opt<Zstring> getParentFolderPathImpl(const Zstring& itemPathImpl) const override @@ -320,12 +291,12 @@ private: Zstring getFileShortName(const Zstring& itemPathImpl) const override { return afterLast(itemPathImpl, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL); } - bool lessItemPathSameAbfType(const Zstring& itemPathImplLhs, const AbstractPathRef& apRhs) const override { return LessFilePath()(itemPathImplLhs, getItemPathImpl(apRhs)); } + bool lessItemPathSameAfsType(const Zstring& itemPathImplLhs, const AbstractPath& apRhs) const override { return LessFilePath()(itemPathImplLhs, getItemPathImpl(apRhs)); } - bool havePathDependencySameAbfType(const AbstractBaseFolder& other) const override + bool havePathDependencySameAfsType(const Zstring& itemPathImplLhs, const AbstractPath& apRhs) const override { - const Zstring& lhs = appendSeparator(baseDirPath); - const Zstring& rhs = appendSeparator(static_cast<const NativeBaseFolder&>(other).baseDirPath); + const Zstring& lhs = appendSeparator(itemPathImplLhs); + const Zstring& rhs = appendSeparator(getItemPathImpl(apRhs)); const size_t lenMin = std::min(lhs.length(), rhs.length()); @@ -372,10 +343,10 @@ private: zen::setFileTime(itemPathImpl, modificationTime, ProcSymlink::DIRECT); //throw FileError } - AbstractPathRef getResolvedSymlinkPath(const Zstring& itemPathImpl) const override //throw FileError + Zstring getResolvedSymlinkPath(const Zstring& itemPathImpl) const override //throw FileError { initComForThread(); //throw FileError - return makeAbstractItem(*this, zen::getResolvedSymlinkPath(itemPathImpl)); //throw FileError + return zen::getResolvedSymlinkPath(itemPathImpl); //throw FileError } Zstring getSymlinkContentBuffer(const Zstring& itemPathImpl) const override //throw FileError @@ -384,69 +355,6 @@ private: return getSymlinkTargetRaw(itemPathImpl); //throw FileError } - void recycleItemDirectly(const Zstring& itemPathImpl) const override //throw FileError - { - initComForThread(); //throw FileError - zen::recycleOrDelete(itemPathImpl); //throw FileError - } - - //---------------------------------------------------------------------------------------------------------------- - //- THREAD-SAFETY: must be thread-safe like an int! => no dangling references to this instance! - IconLoader getAsyncIconLoader(const Zstring& itemPathImpl) const override //noexcept! - { - IconLoader wl = {}; - wl.getFileIcon = [itemPathImpl](int pixelSize) //noexcept! - { - try - { - initComForThread(); //throw FileError - return getFileIcon(itemPathImpl, pixelSize); - } - catch (FileError&) { assert(false); return ImageHolder(); } - }; - - wl.getThumbnailImage = [itemPathImpl](int pixelSize) //noexcept! - { - try - { - initComForThread(); //throw FileError - return getThumbnailImage(itemPathImpl, pixelSize); - } - catch (FileError&) { assert(false); return ImageHolder(); } - }; - return wl; - } - - //- THREAD-SAFETY: must be thread-safe like an int! => no dangling references to this instance! - std::function<bool()> /*throw FileError*/ getAsyncCheckFolderExists(const Zstring& itemPathImpl) const override //noexcept - { - warn_static("finish file error detection") - - return [itemPathImpl] - { - initComForThread(); //throw FileError - return zen::dirExists(itemPathImpl); - }; - } - - //- THREAD-SAFETY: must be thread-safe like an int! => no dangling references to this instance! - std::function<void()> /*throw FileError*/ getAsyncConnectFolder(bool allowUserInteraction) const override //noexcept - { - warn_static("clean-up/remove/re-think the getAsyncConnectFolder() function") - - const Zstring dirPath = baseDirPath; //help lambda capture syntax... - - return [dirPath, allowUserInteraction]() - { -#ifdef ZEN_WIN - initComForThread(); //throw FileError - - //login to network share, if necessary - loginNetworkShare(dirPath, allowUserInteraction); -#endif - }; - } - //---------------------------------------------------------------------------------------------------------------- //return value always bound: std::unique_ptr<InputStream > getInputStream (const Zstring& itemPathImpl) const override //throw FileError, ErrorFileLocked @@ -455,15 +363,12 @@ private: return std::make_unique<InputStreamNative >(itemPathImpl); //throw FileError, ErrorFileLocked } - std::unique_ptr<OutputStream> getOutputStream(const Zstring& itemPathImpl, //throw FileError, ErrorTargetExisting - const std::uint64_t* streamSize, //optional - const std::int64_t* modificationTime) const override // + std::unique_ptr<OutputStreamImpl> getOutputStream(const Zstring& itemPathImpl, //throw FileError, ErrorTargetExisting + const std::uint64_t* streamSize, //optional + const std::int64_t* modificationTime) const override // { initComForThread(); //throw FileError - - //AbstractBaseFolder::OutputStream takes ownership and deletes on errors!!!! => transactionally create output stream first!! - auto&& fop = FileOutput(itemPathImpl, FileOutput::ACC_CREATE_NEW); //throw FileError, ErrorTargetExisting - return std::make_unique<OutputStreamNative>(std::move(fop), makeAbstractItem(*this, itemPathImpl), streamSize, modificationTime); //throw FileError + return std::make_unique<OutputStreamNative>(itemPathImpl, streamSize, modificationTime); //throw FileError, ErrorTargetExisting } //---------------------------------------------------------------------------------------------------------------- @@ -478,14 +383,14 @@ private: //---------------------------------------------------------------------------------------------------------------- //symlink handling: follow link! - FileAttribAfterCopy copyFileForSameAbfType(const Zstring& itemPathImplSource, const AbstractPathRef& apTarget, bool copyFilePermissions, //throw FileError, ErrorTargetExisting, ErrorFileLocked + FileAttribAfterCopy copyFileForSameAfsType(const Zstring& itemPathImplSource, const AbstractPath& apTarget, bool copyFilePermissions, //throw FileError, ErrorTargetExisting, ErrorFileLocked const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus) const override //may be nullptr; throw X! { initComForThread(); //throw FileError const InSyncAttributes attrNew = copyNewFile(itemPathImplSource, getItemPathImpl(apTarget), //throw FileError, ErrorTargetExisting, ErrorFileLocked copyFilePermissions, onNotifyCopyStatus); //may be nullptr; throw X! - FileAttribAfterCopy attrOut = {}; + FileAttribAfterCopy attrOut; attrOut.fileSize = attrNew.fileSize; attrOut.modificationTime = attrNew.modificationTime; attrOut.sourceFileId = convertToAbstractFileId(attrNew.sourceFileId); @@ -494,31 +399,102 @@ private: } //symlink handling: follow link! - void copyNewFolderForSameAbfType(const Zstring& itemPathImplSource, const AbstractPathRef& apTarget, bool copyFilePermissions) const override //throw FileError + void copyNewFolderForSameAfsType(const Zstring& itemPathImplSource, const AbstractPath& apTarget, bool copyFilePermissions) const override //throw FileError { initComForThread(); //throw FileError zen::copyNewDirectory(itemPathImplSource, getItemPathImpl(apTarget), copyFilePermissions); } - void copySymlinkForSameAbfType(const Zstring& itemPathImplSource, const AbstractPathRef& apTarget, bool copyFilePermissions) const override //throw FileError + void copySymlinkForSameAfsType(const Zstring& itemPathImplSource, const AbstractPath& apTarget, bool copyFilePermissions) const override //throw FileError { initComForThread(); //throw FileError zen::copySymlink(itemPathImplSource, getItemPathImpl(apTarget), copyFilePermissions); //throw FileError } - void renameItemForSameAbfType(const Zstring& itemPathImplSource, const AbstractPathRef& apTarget) const override //throw FileError, ErrorTargetExisting, ErrorDifferentVolume + void renameItemForSameAfsType(const Zstring& itemPathImplSource, const AbstractPath& apTarget) const override //throw FileError, ErrorTargetExisting, ErrorDifferentVolume { initComForThread(); //throw FileError zen::renameFile(itemPathImplSource, getItemPathImpl(apTarget)); //throw FileError, ErrorTargetExisting, ErrorDifferentVolume } - bool supportsPermissions() const override //throw FileError + bool supportsPermissions(const Zstring& itemPathImpl) const override //throw FileError { initComForThread(); //throw FileError - return zen::supportsPermissions(baseDirPath); + return zen::supportsPermissions(itemPathImpl); } - const Zstring baseDirPath; + //---------------------------------------------------------------------------------------------------------------- + ImageHolder getFileIcon(const Zstring& itemPathImpl, int pixelSize) const override //noexcept; optional return value + { + try + { + initComForThread(); //throw FileError + return zen::getFileIcon(itemPathImpl, pixelSize); + } + catch (FileError&) { assert(false); return ImageHolder(); } + } + + ImageHolder getThumbnailImage(const Zstring& itemPathImpl, int pixelSize) const override //noexcept; optional return value + { + try + { + initComForThread(); //throw FileError + return zen::getThumbnailImage(itemPathImpl, pixelSize); + } + catch (FileError&) { assert(false); return ImageHolder(); } + } + + bool folderExistsThrowing(const Zstring& itemPathImpl) const override //throw FileError + { + warn_static("finish file error detection") + + initComForThread(); //throw FileError + return zen::dirExists(itemPathImpl); + } + + void connectNetworkFolder(const Zstring& itemPathImpl, bool allowUserInteraction) const override //throw FileError + { + warn_static("clean-up/remove/re-think the getAsyncConnectFolder() function") + +#ifdef ZEN_WIN + initComForThread(); //throw FileError + + //login to network share, if necessary + loginNetworkShare(itemPathImpl, allowUserInteraction); +#endif + } + + //---------------------------------------------------------------------------------------------------------------- + + std::uint64_t getFreeDiskSpace(const Zstring& itemPathImpl) const override //throw FileError, returns 0 if not available + { + initComForThread(); //throw FileError + return zen::getFreeDiskSpace(itemPathImpl); //throw FileError + } + + bool supportsRecycleBin(const Zstring& itemPathImpl, const std::function<void ()>& onUpdateGui) const override //throw FileError + { +#ifdef ZEN_WIN + initComForThread(); //throw FileError + return recycleBinExists(itemPathImpl, onUpdateGui); //throw FileError + +#elif defined ZEN_LINUX || defined ZEN_MAC + return true; //truth be told: no idea!!! +#endif + } + + std::unique_ptr<RecycleSession> createRecyclerSession(const Zstring& itemPathImpl) const override //throw FileError, return value must be bound! + { + initComForThread(); //throw FileError + assert(supportsRecycleBin(itemPathImpl, nullptr)); + return std::make_unique<RecycleSessionNative>(appendSeparator(itemPathImpl)); + } + + void recycleItemDirectly(const Zstring& itemPathImpl) const override //throw FileError + { + initComForThread(); //throw FileError + zen::recycleOrDelete(itemPathImpl); //throw FileError + } }; //=========================================================================================================================== @@ -528,14 +504,14 @@ private: //to support later cleanup if automatic deletion fails for whatever reason Zstring RecycleSessionNative::getOrCreateRecyclerTempDirPf() //throw FileError { - assert(!baseDirPathPf_.empty()); - if (baseDirPathPf_.empty()) + assert(!baseFolderPathPf_.empty()); + if (baseFolderPathPf_.empty()) return Zstring(); if (recyclerTmpDir.empty()) recyclerTmpDir = [&] { - assert(endsWith(baseDirPathPf_, FILE_NAME_SEPARATOR)); + assert(endsWith(baseFolderPathPf_, FILE_NAME_SEPARATOR)); /* -> this naming convention is too cute and confusing for end users: @@ -558,7 +534,7 @@ Zstring RecycleSessionNative::getOrCreateRecyclerTempDirPf() //throw FileError */ //ensure unique ownership: - Zstring dirpath = baseDirPathPf_ + Zstr("RecycleBin") + ABF::TEMP_FILE_ENDING; + Zstring dirpath = baseFolderPathPf_ + Zstr("RecycleBin") + AFS::TEMP_FILE_ENDING; for (int i = 0;; ++i) try { @@ -573,7 +549,7 @@ Zstring RecycleSessionNative::getOrCreateRecyclerTempDirPf() //throw FileError catch (ErrorTargetExisting&) { if (i == 10) throw; //avoid endless recursion in pathological cases - dirpath = baseDirPathPf_ + Zstr("RecycleBin") + Zchar('_') + numberTo<Zstring>(i) + ABF::TEMP_FILE_ENDING; + dirpath = baseFolderPathPf_ + Zstr("RecycleBin") + Zchar('_') + numberTo<Zstring>(i) + AFS::TEMP_FILE_ENDING; } }(); @@ -583,27 +559,27 @@ Zstring RecycleSessionNative::getOrCreateRecyclerTempDirPf() //throw FileError #endif -bool RecycleSessionNative::recycleItem(const AbstractPathRef& ap, const Zstring& logicalRelPath) //throw FileError +bool RecycleSessionNative::recycleItem(const AbstractPath& itemPath, const Zstring& logicalRelPath) //throw FileError { - const Zstring itemPath = NativeBaseFolder::getItemPathImplForRecycler(ap); + const Zstring itemPathImpl = NativeFileSystem::getItemPathImplForRecycler(itemPath); assert(!startsWith(logicalRelPath, FILE_NAME_SEPARATOR)); #ifdef ZEN_WIN - const bool remnantRecyclerItem = [&itemPath] //clean-up of recycler temp directory failed during last sync + const bool isRemnantRecyclerItem = [&itemPathImpl] //clean-up of recycler temp directory failed during last sync { //search for path component named "RecycleBin.ffs_tmp" or "RecycleBin_<num>.ffs_tmp": - const size_t pos = itemPath.find(L"\\RecycleBin"); + const size_t pos = itemPathImpl.find(L"\\RecycleBin"); if (pos == Zstring::npos) return false; - const size_t pos2 = itemPath.find(L'\\', pos + 1); - return endsWith(StringRef<Zchar>(itemPath.begin(), pos2 == Zstring::npos ? itemPath.end() : itemPath.begin() + pos2), ABF::TEMP_FILE_ENDING); + const size_t pos2 = itemPathImpl.find(L'\\', pos + 1); + return endsWith(StringRef<const Zchar>(itemPathImpl.begin(), pos2 == Zstring::npos ? itemPathImpl.end() : itemPathImpl.begin() + pos2), AFS::TEMP_FILE_ENDING); }(); //do not create RecycleBin.ffs_tmp directories recursively if recycling a particular item fails forever! //=> 1. stack overflow crashes 2. paths longer than 260 chars, undeletable/viewable with Explorer - if (remnantRecyclerItem) - return recycleOrDelete(itemPath); //throw FileError + if (isRemnantRecyclerItem) + return recycleOrDelete(itemPathImpl); //throw FileError const Zstring tmpPath = getOrCreateRecyclerTempDirPf() + logicalRelPath; //throw FileError bool deleted = false; @@ -612,7 +588,7 @@ bool RecycleSessionNative::recycleItem(const AbstractPathRef& ap, const Zstring& { //perf: Instead of recycling each object separately, we rename them one by one // into a temporary directory and batch-recycle all at once after sync - renameFile(itemPath, tmpPath); //throw FileError, ErrorDifferentVolume + renameFile(itemPathImpl, tmpPath); //throw FileError, ErrorDifferentVolume this->toBeRecycled.push_back(tmpPath); deleted = true; }; @@ -626,7 +602,7 @@ bool RecycleSessionNative::recycleItem(const AbstractPathRef& ap, const Zstring& catch (ErrorDifferentVolume&) { throw; } catch (FileError&) { - if (somethingExists(itemPath)) + if (somethingExists(itemPathImpl)) { const Zstring tmpParentDir = beforeLast(tmpPath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE); //what if C:\ ? if (!somethingExists(tmpParentDir)) @@ -641,13 +617,13 @@ bool RecycleSessionNative::recycleItem(const AbstractPathRef& ap, const Zstring& } catch (ErrorDifferentVolume&) //MoveFileEx() returns ERROR_PATH_NOT_FOUND *before* considering ERROR_NOT_SAME_DEVICE! => we have to create tmpParentDir to find out! { - return recycleOrDelete(itemPath); //throw FileError + return recycleOrDelete(itemPathImpl); //throw FileError } return deleted; #elif defined ZEN_LINUX || defined ZEN_MAC - return recycleOrDelete(itemPath); //throw FileError + return recycleOrDelete(itemPathImpl); //throw FileError #endif } @@ -674,9 +650,9 @@ void RecycleSessionNative::tryCleanup(const std::function<void (const std::wstri //coordinate changes with getResolvedFilePath()! -bool zen::acceptsFolderPathPhraseNative(const Zstring& folderPathPhrase) //noexcept +bool zen::acceptsItemPathPhraseNative(const Zstring& itemPathPhrase) //noexcept { - Zstring path = folderPathPhrase; + Zstring path = itemPathPhrase; path = expandMacros(path); //expand before trimming! trim(path); @@ -713,8 +689,9 @@ bool zen::acceptsFolderPathPhraseNative(const Zstring& folderPathPhrase) //noexc } -std::unique_ptr<AbstractBaseFolder> zen::createBaseFolderNative(const Zstring& folderPathPhrase) //noexcept +AbstractPath zen::createItemPathNative(const Zstring& itemPathPhrase) //noexcept { - return std::make_unique<NativeBaseFolder>(getResolvedFilePath(folderPathPhrase)); - warn_static("get volume by name for idle HDD! => call async getFormattedDirectoryPath, but currently not thread-safe") + const Zstring itemPathImpl = getResolvedFilePath(itemPathPhrase); + return AbstractPath(std::make_shared<NativeFileSystem>(), itemPathImpl); + warn_static("get volume by name hangs for idle HDD! => run createItemPathNative during getFolderStatusNonBlocking() but getResolvedFilePath currently not thread-safe!") } diff --git a/FreeFileSync/Source/fs/native.h b/FreeFileSync/Source/fs/native.h index 64245be0..2765eace 100644 --- a/FreeFileSync/Source/fs/native.h +++ b/FreeFileSync/Source/fs/native.h @@ -11,9 +11,8 @@ namespace zen { -bool acceptsFolderPathPhraseNative(const Zstring& folderPathPhrase); //noexcept - -std::unique_ptr<AbstractBaseFolder> createBaseFolderNative(const Zstring& folderPathPhrase); //noexcept +bool acceptsItemPathPhraseNative (const Zstring& itemPathPhrase); //noexcept +AbstractPath createItemPathNative(const Zstring& itemPathPhrase); //noexcept } #endif //FS_NATIVE_183247018532434563465 diff --git a/FreeFileSync/Source/fs/native_traverser_impl.h b/FreeFileSync/Source/fs/native_traverser_impl.h index 1b6e2a6c..3c3efe2f 100644 --- a/FreeFileSync/Source/fs/native_traverser_impl.h +++ b/FreeFileSync/Source/fs/native_traverser_impl.h @@ -6,7 +6,6 @@ #include <zen/sys_error.h> #include <zen/symlink_target.h> -#include <zen/int64.h> #include <cstddef> //offsetof #include <sys/stat.h> @@ -17,16 +16,16 @@ namespace { using namespace zen; -using ABF = AbstractBaseFolder; +using AFS = AbstractFileSystem; inline -ABF::FileId convertToAbstractFileId(const zen::FileId& fid) +AFS::FileId convertToAbstractFileId(const zen::FileId& fid) { if (fid == zen::FileId()) - return ABF::FileId(); + return AFS::FileId(); - ABF::FileId out(reinterpret_cast<const char*>(&fid.first), sizeof(fid.first)); + AFS::FileId out(reinterpret_cast<const char*>(&fid.first), sizeof(fid.first)); out.append(reinterpret_cast<const char*>(&fid.second), sizeof(fid.second)); return out; } @@ -35,13 +34,13 @@ ABF::FileId convertToAbstractFileId(const zen::FileId& fid) class DirTraverser { public: - static void execute(const Zstring& baseDirectory, ABF::TraverserCallback& sink) + static void execute(const Zstring& baseDirectory, AFS::TraverserCallback& sink) { DirTraverser(baseDirectory, sink); } private: - DirTraverser(const Zstring& baseDirectory, ABF::TraverserCallback& sink) + DirTraverser(const Zstring& baseDirectory, AFS::TraverserCallback& sink) { /* quote: "Since POSIX.1 does not specify the size of the d_name field, and other nonstandard fields may precede that field within the dirent structure, portable applications that use readdir_r() should allocate @@ -57,7 +56,7 @@ private: DirTraverser (const DirTraverser&) = delete; DirTraverser& operator=(const DirTraverser&) = delete; - void traverse(const Zstring& dirPath, ABF::TraverserCallback& sink) + void traverse(const Zstring& dirPath, AFS::TraverserCallback& sink) { tryReportingDirError([&] { @@ -65,7 +64,7 @@ private: }, sink); } - void traverseWithException(const Zstring& dirPath, ABF::TraverserCallback& sink) //throw FileError + void traverseWithException(const Zstring& dirPath, AFS::TraverserCallback& sink) //throw FileError { //no need to check for endless recursion: Linux has a fixed limit on the number of symbolic links in a path @@ -104,11 +103,11 @@ private: if (S_ISLNK(statData.st_mode)) //on Linux there is no distinction between file and directory symlinks! { - const ABF::TraverserCallback::SymlinkInfo linkInfo = { itemName, statData.st_mtime }; + const AFS::TraverserCallback::SymlinkInfo linkInfo = { itemName, statData.st_mtime }; switch (sink.onSymlink(linkInfo)) { - case ABF::TraverserCallback::LINK_FOLLOW: + case AFS::TraverserCallback::LINK_FOLLOW: { //try to resolve symlink (and report error on failure!!!) struct ::stat statDataTrg = {}; @@ -123,12 +122,12 @@ private: { if (S_ISDIR(statDataTrg.st_mode)) //a directory { - if (std::unique_ptr<ABF::TraverserCallback> trav = sink.onDir({ itemName })) + if (std::unique_ptr<AFS::TraverserCallback> trav = sink.onDir({ itemName })) traverse(itemPath, *trav); } else //a file or named pipe, ect. { - ABF::TraverserCallback::FileInfo fi = { itemName, makeUnsigned(statDataTrg.st_size), statDataTrg.st_mtime, convertToAbstractFileId(extractFileId(statDataTrg)), &linkInfo }; + AFS::TraverserCallback::FileInfo fi = { itemName, makeUnsigned(statDataTrg.st_size), statDataTrg.st_mtime, convertToAbstractFileId(extractFileId(statDataTrg)), &linkInfo }; sink.onFile(fi); } } @@ -136,18 +135,18 @@ private: } break; - case ABF::TraverserCallback::LINK_SKIP: + case AFS::TraverserCallback::LINK_SKIP: break; } } else if (S_ISDIR(statData.st_mode)) //a directory { - if (std::unique_ptr<ABF::TraverserCallback> trav = sink.onDir({ itemName })) + if (std::unique_ptr<AFS::TraverserCallback> trav = sink.onDir({ itemName })) traverse(itemPath, *trav); } else //a file or named pipe, ect. { - ABF::TraverserCallback::FileInfo fi = { itemName, makeUnsigned(statData.st_size), statData.st_mtime, convertToAbstractFileId(extractFileId(statData)), nullptr /*symlinkInfo*/ }; + AFS::TraverserCallback::FileInfo fi = { itemName, makeUnsigned(statData.st_size), statData.st_mtime, convertToAbstractFileId(extractFileId(statData)), nullptr /*symlinkInfo*/ }; sink.onFile(fi); } /* diff --git a/FreeFileSync/Source/lib/binary.cpp b/FreeFileSync/Source/lib/binary.cpp index 899d45a8..8ec4a184 100644 --- a/FreeFileSync/Source/lib/binary.cpp +++ b/FreeFileSync/Source/lib/binary.cpp @@ -10,7 +10,7 @@ #include <zen/file_io.h> using namespace zen; -using ABF = AbstractBaseFolder; +using AFS = AbstractFileSystem; namespace { @@ -72,10 +72,10 @@ const std::int64_t TICKS_PER_SEC = ticksPerSec(); } -bool zen::filesHaveSameContent(const AbstractPathRef& filePath1, const AbstractPathRef& filePath2, const std::function<void(std::int64_t bytesDelta)>& onUpdateStatus) //throw FileError +bool zen::filesHaveSameContent(const AbstractPath& filePath1, const AbstractPath& filePath2, const std::function<void(std::int64_t bytesDelta)>& onUpdateStatus) //throw FileError { - const std::unique_ptr<ABF::InputStream> inStream1 = ABF::getInputStream(filePath1); //throw FileError, (ErrorFileLocked) - const std::unique_ptr<ABF::InputStream> inStream2 = ABF::getInputStream(filePath2); // + const std::unique_ptr<AFS::InputStream> inStream1 = AFS::getInputStream(filePath1); //throw FileError, (ErrorFileLocked) + const std::unique_ptr<AFS::InputStream> inStream2 = AFS::getInputStream(filePath2); // BufferSize dynamicBufSize(std::min(inStream1->optimalBlockSize(), inStream2->optimalBlockSize())); diff --git a/FreeFileSync/Source/lib/binary.h b/FreeFileSync/Source/lib/binary.h index 1c5a62fd..41e4f3df 100644 --- a/FreeFileSync/Source/lib/binary.h +++ b/FreeFileSync/Source/lib/binary.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef BINARY_H_INCLUDED_3941281398513241134 -#define BINARY_H_INCLUDED_3941281398513241134 +#ifndef BINARY_H_3941281398513241134 +#define BINARY_H_3941281398513241134 #include <functional> #include <zen/file_error.h> @@ -14,9 +14,9 @@ namespace zen { -bool filesHaveSameContent(const AbstractPathRef& filePath1, //throw FileError - const AbstractPathRef& filePath2, +bool filesHaveSameContent(const AbstractPath& filePath1, //throw FileError + const AbstractPath& filePath2, const std::function<void(std::int64_t bytesDelta)>& onUpdateStatus); //may be nullptr } -#endif //BINARY_H_INCLUDED_3941281398513241134 +#endif //BINARY_H_3941281398513241134 diff --git a/FreeFileSync/Source/lib/cmp_filetime.h b/FreeFileSync/Source/lib/cmp_filetime.h index 4d47a63b..fe1be035 100644 --- a/FreeFileSync/Source/lib/cmp_filetime.h +++ b/FreeFileSync/Source/lib/cmp_filetime.h @@ -1,5 +1,11 @@ -#ifndef CMP_FILETIME_H_INCLUDED -#define CMP_FILETIME_H_INCLUDED +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef CMP_FILETIME_H_032180451675845 +#define CMP_FILETIME_H_032180451675845 #include <ctime> #include <algorithm> @@ -75,4 +81,4 @@ TimeResult compareFileTime(std::int64_t lhs, std::int64_t rhs, int tolerance, un } } -#endif // CMP_FILETIME_H_INCLUDED +#endif //CMP_FILETIME_H_032180451675845 diff --git a/FreeFileSync/Source/lib/db_file.cpp b/FreeFileSync/Source/lib/db_file.cpp index f3f9177b..ea3d5efd 100644 --- a/FreeFileSync/Source/lib/db_file.cpp +++ b/FreeFileSync/Source/lib/db_file.cpp @@ -35,7 +35,7 @@ using MemStreamIn = MemoryStreamIn <ByteArray>; //----------------------------------------------------------------------------------- template <SelectedSide side> inline -AbstractPathRef getDatabaseFilePath(const BaseDirPair& baseDirObj, bool tempfile = false) +AbstractPath getDatabaseFilePath(const BaseFolderPair& baseFolder, bool tempfile = false) { //Linux and Windows builds are binary incompatible: different file id?, problem with case sensitivity? are UTC file times really compatible? //what about endianess!? @@ -43,18 +43,18 @@ AbstractPathRef getDatabaseFilePath(const BaseDirPair& baseDirObj, bool tempfile //Give db files different names. //make sure they end with ".ffs_db". These files will be excluded from comparison #ifdef ZEN_WIN - const Zstring dbname = Zstr("sync"); + const Zstring dbName = Zstr("sync"); #elif defined ZEN_LINUX || defined ZEN_MAC - 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 hidden e.g. in Nautilus #endif - const Zstring dbFileName = dbname + (tempfile ? Zstr(".tmp") : Zstr("")) + SYNC_DB_FILE_ENDING; + const Zstring dbFileName = dbName + (tempfile ? Zstr(".tmp") : Zstr("")) + SYNC_DB_FILE_ENDING; - return baseDirObj.getABF<side>().getAbstractPath(dbFileName); + return AFS::appendRelPath(baseFolder.getAbstractPath<side>(), dbFileName); } //####################################################################################################################################### -void saveStreams(const DbStreams& streamList, const AbstractPathRef& dbPath, const std::function<void(std::int64_t bytesDelta)>& onUpdateStatus) //throw FileError +void saveStreams(const DbStreams& streamList, const AbstractPath& dbPath, const std::function<void(std::int64_t bytesDelta)>& onUpdateStatus) //throw FileError { //perf? instead of writing to a file stream directly, collect data into memory first, then write to file block-wise MemStreamOut memStreamOut; @@ -74,13 +74,13 @@ void saveStreams(const DbStreams& streamList, const AbstractPathRef& dbPath, con writeContainer<ByteArray> (memStreamOut, stream.second); } - assert(!ABF::somethingExists(dbPath)); //orphan tmp files should have been cleaned up at this point! + assert(!AFS::somethingExists(dbPath)); //orphan tmp files should have been cleaned up at this point! //save memory stream to file (as a transaction!) { MemoryStreamIn<ByteArray> memStreamIn(memStreamOut.ref()); const std::uint64_t streamSize = memStreamOut.ref().size(); - const std::unique_ptr<ABF::OutputStream> fileStreamOut = ABF::getOutputStream(dbPath, &streamSize, nullptr /*modificationTime*/); //throw FileError, ErrorTargetExisting + const std::unique_ptr<AFS::OutputStream> fileStreamOut = AFS::getOutputStream(dbPath, &streamSize, nullptr /*modificationTime*/); //throw FileError, ErrorTargetExisting if (onUpdateStatus) onUpdateStatus(0); copyStream(memStreamIn, *fileStreamOut, fileStreamOut->optimalBlockSize(), onUpdateStatus); //throw FileError fileStreamOut->finalize([&] { if (onUpdateStatus) onUpdateStatus(0); }); //throw FileError @@ -88,20 +88,20 @@ void saveStreams(const DbStreams& streamList, const AbstractPathRef& dbPath, con } #ifdef ZEN_WIN - if (Opt<Zstring> nativeFilePath = ABF::getNativeItemPath(dbPath)) + if (Opt<Zstring> nativeFilePath = AFS::getNativeItemPath(dbPath)) ::SetFileAttributes(applyLongPathPrefix(*nativeFilePath).c_str(), FILE_ATTRIBUTE_HIDDEN); //(try to) hide database file #endif } -DbStreams loadStreams(const AbstractPathRef& dbPath, const std::function<void(std::int64_t bytesDelta)>& onUpdateStatus) //throw FileError, FileErrorDatabaseNotExisting +DbStreams loadStreams(const AbstractPath& dbPath, const std::function<void(std::int64_t bytesDelta)>& onUpdateStatus) //throw FileError, FileErrorDatabaseNotExisting { try { //load memory stream from file MemoryStreamOut<ByteArray> memStreamOut; { - const std::unique_ptr<ABF::InputStream> fileStreamIn = ABF::getInputStream(dbPath); //throw FileError, ErrorFileLocked + const std::unique_ptr<AFS::InputStream> fileStreamIn = AFS::getInputStream(dbPath); //throw FileError, ErrorFileLocked if (onUpdateStatus) onUpdateStatus(0); copyStream(*fileStreamIn, memStreamOut, fileStreamIn->optimalBlockSize(), onUpdateStatus); //throw FileError } //close file handle @@ -113,11 +113,11 @@ DbStreams loadStreams(const AbstractPathRef& dbPath, const std::function<void(st readArray(streamIn, formatDescr, sizeof(formatDescr)); //throw UnexpectedEndOfStreamError if (!std::equal(FILE_FORMAT_DESCR, FILE_FORMAT_DESCR + sizeof(FILE_FORMAT_DESCR), formatDescr)) - throw FileError(replaceCpy(_("Database file %x is incompatible."), L"%x", fmtPath(ABF::getDisplayPath(dbPath)))); + throw FileError(replaceCpy(_("Database file %x is incompatible."), L"%x", fmtPath(AFS::getDisplayPath(dbPath)))); const int version = readNumber<std::int32_t>(streamIn); //throw UnexpectedEndOfStreamError if (version != DB_FORMAT_CONTAINER) //read file format version number - throw FileError(replaceCpy(_("Database file %x is incompatible."), L"%x", fmtPath(ABF::getDisplayPath(dbPath)))); + throw FileError(replaceCpy(_("Database file %x is incompatible."), L"%x", fmtPath(AFS::getDisplayPath(dbPath)))); DbStreams output; @@ -135,18 +135,18 @@ DbStreams loadStreams(const AbstractPathRef& dbPath, const std::function<void(st } catch (FileError&) { - if (!ABF::somethingExists(dbPath)) //a benign(?) race condition with FileError + if (!AFS::somethingExists(dbPath)) //a benign(?) race condition with FileError throw FileErrorDatabaseNotExisting(_("Initial synchronization:") + L" \n" + - replaceCpy(_("Database file %x does not yet exist."), L"%x", fmtPath(ABF::getDisplayPath(dbPath)))); + replaceCpy(_("Database file %x does not yet exist."), L"%x", fmtPath(AFS::getDisplayPath(dbPath)))); throw; } catch (UnexpectedEndOfStreamError&) { - throw FileError(_("Database file is corrupt:") + L"\n" + fmtPath(ABF::getDisplayPath(dbPath))); + throw FileError(_("Database file is corrupt:") + L"\n" + fmtPath(AFS::getDisplayPath(dbPath))); } catch (const std::bad_alloc& e) //still required? { - throw FileError(_("Database file is corrupt:") + L"\n" + fmtPath(ABF::getDisplayPath(dbPath)), + throw FileError(_("Database file is corrupt:") + L"\n" + fmtPath(AFS::getDisplayPath(dbPath)), _("Out of memory.") + L" " + utfCvrtTo<std::wstring>(e.what())); } } @@ -156,7 +156,7 @@ DbStreams loadStreams(const AbstractPathRef& dbPath, const std::function<void(st class StreamGenerator //for db-file back-wards compatibility we stick with two output streams until further { public: - static void execute(const InSyncDir& dir, //throw FileError + static void execute(const InSyncFolder& dbFolder, //throw FileError const std::wstring& displayFilePathL, //used for diagnostics only const std::wstring& displayFilePathR, ByteArray& streamL, @@ -165,7 +165,7 @@ public: StreamGenerator generator; //PERF_START - generator.recurse(dir); + generator.recurse(dbFolder); //PERF_STOP auto compStream = [](const ByteArray& stream, const std::wstring& displayFilePath) -> ByteArray //throw FileError @@ -224,7 +224,7 @@ public: } private: - void recurse(const InSyncDir& container) + void recurse(const InSyncFolder& container) { writeNumber<std::uint32_t>(outputBoth, static_cast<std::uint32_t>(container.files.size())); for (const auto& dbFile : container.files) @@ -247,13 +247,13 @@ private: writeLink(outputRight, dbSymlink.second.right); } - writeNumber<std::uint32_t>(outputBoth, static_cast<std::uint32_t>(container.dirs.size())); - for (const auto& dbDir : container.dirs) + writeNumber<std::uint32_t>(outputBoth, static_cast<std::uint32_t>(container.folders.size())); + for (const auto& dbFolder : container.folders) { - writeUtf8(outputBoth, dbDir.first); - writeNumber<std::int32_t>(outputBoth, dbDir.second.status); + writeUtf8(outputBoth, dbFolder.first); + writeNumber<std::int32_t>(outputBoth, dbFolder.second.status); - recurse(dbDir.second); + recurse(dbFolder.second); } } @@ -280,10 +280,10 @@ private: class StreamParser { public: - static std::shared_ptr<InSyncDir> execute(const ByteArray& streamL, //throw FileError - const ByteArray& streamR, - const std::wstring& displayFilePathL, //used for diagnostics only - const std::wstring& displayFilePathR) + static std::shared_ptr<InSyncFolder> execute(const ByteArray& streamL, //throw FileError + const ByteArray& streamR, + const std::wstring& displayFilePathL, //used for diagnostics only + const std::wstring& displayFilePathR) { auto decompStream = [](const ByteArray& stream, const std::wstring& displayFilePath) -> ByteArray //throw FileError { @@ -333,7 +333,7 @@ public: const ByteArray tmpL = readContainer<ByteArray>(inL); const ByteArray tmpR = readContainer<ByteArray>(inR); - auto output = std::make_shared<InSyncDir>(InSyncDir::DIR_STATUS_IN_SYNC); + auto output = std::make_shared<InSyncFolder>(InSyncFolder::DIR_STATUS_IN_SYNC); StreamParser parser(streamVersionL, decompStream(tmpL, displayFilePathL), decompStream(tmpR, displayFilePathR), @@ -362,7 +362,7 @@ private: inputRight(bufferR), inputBoth (bufferB) {} - void recurse(InSyncDir& container) + void recurse(InSyncFolder& container) { size_t fileCount = readNumber<std::uint32_t>(inputBoth); while (fileCount-- != 0) @@ -389,10 +389,10 @@ private: while (dirCount-- != 0) { const Zstring itemName = readUtf8(inputBoth); - const auto status = static_cast<InSyncDir::InSyncStatus>(readNumber<std::int32_t>(inputBoth)); + const auto status = static_cast<InSyncFolder::InSyncStatus>(readNumber<std::int32_t>(inputBoth)); - InSyncDir& subDir = container.addDir(itemName, status); - recurse(subDir); + InSyncFolder& dbFolder = container.addFolder(itemName, status); + recurse(dbFolder); } } @@ -403,7 +403,7 @@ private: //attention: order of function argument evaluation is undefined! So do it one after the other... const auto lastWriteTimeRaw = readNumber<std::int64_t>(input); //throw UnexpectedEndOfStreamError - ABF::FileId fileId; + AFS::FileId fileId; warn_static("remove after migration! 2015-05-02") if (streamVersion_ == 1) { @@ -445,10 +445,10 @@ class UpdateLastSynchronousState => update all database entries! */ public: - static void execute(const BaseDirPair& baseDirObj, InSyncDir& dir) + static void execute(const BaseFolderPair& baseFolder, InSyncFolder& dbFolder) { - UpdateLastSynchronousState updater(baseDirObj.getCompVariant(), baseDirObj.getFilter()); - updater.recurse(baseDirObj, dir); + UpdateLastSynchronousState updater(baseFolder.getCompVariant(), baseFolder.getFilter()); + updater.recurse(baseFolder, dbFolder); } private: @@ -456,11 +456,11 @@ private: filter_(filter), activeCmpVar_(activeCmpVar) {} - void recurse(const HierarchyObject& hierObj, InSyncDir& dir) + void recurse(const HierarchyObject& hierObj, InSyncFolder& dbFolder) { - process(hierObj.refSubFiles(), hierObj.getPairRelativePathPf(), dir.files); - process(hierObj.refSubLinks(), hierObj.getPairRelativePathPf(), dir.symlinks); - process(hierObj.refSubDirs (), hierObj.getPairRelativePathPf(), dir.dirs); + process(hierObj.refSubFiles (), hierObj.getPairRelativePathPf(), dbFolder.files); + process(hierObj.refSubLinks (), hierObj.getPairRelativePathPf(), dbFolder.symlinks); + process(hierObj.refSubFolders(), hierObj.getPairRelativePathPf(), dbFolder.folders); } template <class M, class V> @@ -504,41 +504,41 @@ private: */ } - void process(const HierarchyObject::SubFileVec& currentFiles, const Zstring& parentRelPathPf, InSyncDir::FileList& dbFiles) + void process(const HierarchyObject::FileList& currentFiles, const Zstring& parentRelPathPf, InSyncFolder::FileList& dbFiles) { std::unordered_set<const InSyncFile*> toPreserve; //referencing fixed-in-memory std::map elements - for (const FilePair& fileObj : currentFiles) - if (!fileObj.isEmpty()) + for (const FilePair& file : currentFiles) + if (!file.isEmpty()) { - if (fileObj.getCategory() == FILE_EQUAL) //data in sync: write current state + if (file.getCategory() == FILE_EQUAL) //data in sync: write current state { - //Caveat: If FILE_EQUAL, we *implicitly* assume equal left and right short names matching case: InSyncDir's mapping tables use short name as a key! + //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(fileObj.getItemName<LEFT_SIDE>() == fileObj.getItemName<RIGHT_SIDE>()); + assert(file.getItemName<LEFT_SIDE>() == file.getItemName<RIGHT_SIDE>()); //this should be taken for granted: - assert(fileObj.getFileSize<LEFT_SIDE>() == fileObj.getFileSize<RIGHT_SIDE>()); + assert(file.getFileSize<LEFT_SIDE>() == file.getFileSize<RIGHT_SIDE>()); //create or update new "in-sync" state - InSyncFile& file = updateItem(dbFiles, fileObj.getPairShortName(), - InSyncFile(InSyncDescrFile(fileObj.getLastWriteTime<LEFT_SIDE >(), - fileObj.getFileId <LEFT_SIDE >()), - InSyncDescrFile(fileObj.getLastWriteTime<RIGHT_SIDE>(), - fileObj.getFileId <RIGHT_SIDE>()), - activeCmpVar_, - fileObj.getFileSize<LEFT_SIDE>())); - toPreserve.insert(&file); + InSyncFile& dbFile = updateItem(dbFiles, file.getPairItemName(), + InSyncFile(InSyncDescrFile(file.getLastWriteTime<LEFT_SIDE >(), + file.getFileId <LEFT_SIDE >()), + InSyncDescrFile(file.getLastWriteTime<RIGHT_SIDE>(), + file.getFileId <RIGHT_SIDE>()), + activeCmpVar_, + file.getFileSize<LEFT_SIDE>())); + toPreserve.insert(&dbFile); } else //not in sync: preserve last synchronous state { - auto it = dbFiles.find(fileObj.getPairShortName()); + auto it = dbFiles.find(file.getPairItemName()); if (it != dbFiles.end()) toPreserve.insert(&it->second); } } //delete removed items (= "in-sync") from database - erase_if(dbFiles, [&](const InSyncDir::FileList::value_type& v) -> bool + erase_if(dbFiles, [&](const InSyncFolder::FileList::value_type& v) -> bool { if (toPreserve.find(&v.second) != toPreserve.end()) return false; @@ -549,58 +549,58 @@ private: }); } - void process(const HierarchyObject::SubLinkVec& currentLinks, const Zstring& parentRelPathPf, InSyncDir::LinkList& dbLinks) + void process(const HierarchyObject::SymlinkList& currentSymlinks, const Zstring& parentRelPathPf, InSyncFolder::SymlinkList& dbSymlinks) { std::unordered_set<const InSyncSymlink*> toPreserve; - for (const SymlinkPair& linkObj : currentLinks) - if (!linkObj.isEmpty()) + for (const SymlinkPair& symlink : currentSymlinks) + if (!symlink.isEmpty()) { - if (linkObj.getLinkCategory() == SYMLINK_EQUAL) //data in sync: write current state + if (symlink.getLinkCategory() == SYMLINK_EQUAL) //data in sync: write current state { - assert(linkObj.getItemName<LEFT_SIDE>() == linkObj.getItemName<RIGHT_SIDE>()); + assert(symlink.getItemName<LEFT_SIDE>() == symlink.getItemName<RIGHT_SIDE>()); //create or update new "in-sync" state - InSyncSymlink& link = updateItem(dbLinks, linkObj.getPairShortName(), - InSyncSymlink(InSyncDescrLink(linkObj.getLastWriteTime<LEFT_SIDE>()), - InSyncDescrLink(linkObj.getLastWriteTime<RIGHT_SIDE>()), - activeCmpVar_)); - toPreserve.insert(&link); + InSyncSymlink& dbSymlink = updateItem(dbSymlinks, symlink.getPairItemName(), + InSyncSymlink(InSyncDescrLink(symlink.getLastWriteTime<LEFT_SIDE>()), + InSyncDescrLink(symlink.getLastWriteTime<RIGHT_SIDE>()), + activeCmpVar_)); + toPreserve.insert(&dbSymlink); } else //not in sync: preserve last synchronous state { - auto it = dbLinks.find(linkObj.getPairShortName()); - if (it != dbLinks.end()) + auto it = dbSymlinks.find(symlink.getPairItemName()); + if (it != dbSymlinks.end()) toPreserve.insert(&it->second); } } //delete removed items (= "in-sync") from database - erase_if(dbLinks, [&](const InSyncDir::LinkList::value_type& v) -> bool + erase_if(dbSymlinks, [&](const InSyncFolder::SymlinkList::value_type& v) -> bool { if (toPreserve.find(&v.second) != toPreserve.end()) return false; - //all items not existing in "currentLinks" have either been deleted meanwhile or been excluded via filter: + //all items not existing in "currentSymlinks" have either been deleted meanwhile or been excluded via filter: const Zstring& itemRelPath = parentRelPathPf + v.first; return filter_.passFileFilter(itemRelPath); }); } - void process(const HierarchyObject::SubDirVec& currentDirs, const Zstring& parentRelPathPf, InSyncDir::DirList& dbDirs) + void process(const HierarchyObject::FolderList& currentFolders, const Zstring& parentRelPathPf, InSyncFolder::FolderList& dbFolders) { - std::unordered_set<const InSyncDir*> toPreserve; + std::unordered_set<const InSyncFolder*> toPreserve; - for (const DirPair& dirObj : currentDirs) - if (!dirObj.isEmpty()) - switch (dirObj.getDirCategory()) + for (const FolderPair& folder : currentFolders) + if (!folder.isEmpty()) + switch (folder.getDirCategory()) { case DIR_EQUAL: { - assert(dirObj.getItemName<LEFT_SIDE>() == dirObj.getItemName<RIGHT_SIDE>()); + assert(folder.getItemName<LEFT_SIDE>() == folder.getItemName<RIGHT_SIDE>()); //update directory entry only (shallow), but do *not touch* exising child elements!!! - const Zstring& key = dirObj.getPairShortName(); - auto insertResult = dbDirs.emplace(key, InSyncDir(InSyncDir::DIR_STATUS_IN_SYNC)); //get or create + const Zstring& key = folder.getPairItemName(); + auto insertResult = dbFolders.emplace(key, InSyncFolder(InSyncFolder::DIR_STATUS_IN_SYNC)); //get or create auto it = insertResult.first; #if defined ZEN_WIN || defined ZEN_MAC //caveat: key might need to be updated, too, if there is a change in short name case!!! @@ -608,14 +608,14 @@ private: if (alreadyExisting && it->first != key) { auto oldValue = std::move(it->second); - dbDirs.erase(it); //don't fiddle with decrementing "it"! - you might lose while optimizing pointlessly - it = dbDirs.emplace(key, std::move(oldValue)).first; + dbFolders.erase(it); //don't fiddle with decrementing "it"! - you might lose while optimizing pointlessly + it = dbFolders.emplace(key, std::move(oldValue)).first; } #endif - InSyncDir& dir = it->second; - dir.status = InSyncDir::DIR_STATUS_IN_SYNC; //update immediate directory entry - toPreserve.insert(&dir); - recurse(dirObj, dir); + InSyncFolder& dbFolder = it->second; + dbFolder.status = InSyncFolder::DIR_STATUS_IN_SYNC; //update immediate directory entry + toPreserve.insert(&dbFolder); + recurse(folder, dbFolder); } break; @@ -626,9 +626,9 @@ private: //Example: directories on left and right differ in case while sub-files are equal { //reuse last "in-sync" if available or insert strawman entry (do not try to update and thereby remove child elements!!!) - InSyncDir& dir = dbDirs.emplace(dirObj.getPairShortName(), InSyncDir(InSyncDir::DIR_STATUS_STRAW_MAN)).first->second; - toPreserve.insert(&dir); - recurse(dirObj, dir); //unconditional recursion without filter check! => no problem since "childItemMightMatch" is optional!!! + InSyncFolder& dbFolder = dbFolders.emplace(folder.getPairItemName(), InSyncFolder(InSyncFolder::DIR_STATUS_STRAW_MAN)).first->second; + toPreserve.insert(&dbFolder); + recurse(folder, dbFolder); //unconditional recursion without filter check! => no problem since "childItemMightMatch" is optional!!! } break; @@ -636,18 +636,18 @@ private: case DIR_LEFT_SIDE_ONLY: case DIR_RIGHT_SIDE_ONLY: { - auto it = dbDirs.find(dirObj.getPairShortName()); - if (it != dbDirs.end()) + auto it = dbFolders.find(folder.getPairItemName()); + if (it != dbFolders.end()) { toPreserve.insert(&it->second); - recurse(dirObj, it->second); //although existing sub-items cannot be in sync, items deleted on both sides *are* in-sync!!! + recurse(folder, it->second); //although existing sub-items cannot be in sync, items deleted on both sides *are* in-sync!!! } } break; } //delete removed items (= "in-sync") from database - erase_if(dbDirs, [&](InSyncDir::DirList::value_type& v) -> bool + erase_if(dbFolders, [&](InSyncFolder::FolderList::value_type& v) -> bool { if (toPreserve.find(&v.second) != toPreserve.end()) return false; @@ -665,12 +665,12 @@ private: } //delete all entries for removed folder (= "in-sync") from database - void dbSetEmptyState(InSyncDir& dir, const Zstring& parentRelPathPf) + void dbSetEmptyState(InSyncFolder& dbFolder, const Zstring& parentRelPathPf) { - erase_if(dir.files, [&](const InSyncDir::FileList::value_type& v) { return filter_.passFileFilter(parentRelPathPf + v.first); }); - erase_if(dir.symlinks, [&](const InSyncDir::LinkList::value_type& v) { return filter_.passFileFilter(parentRelPathPf + v.first); }); + erase_if(dbFolder.files, [&](const InSyncFolder::FileList ::value_type& v) { return filter_.passFileFilter(parentRelPathPf + v.first); }); + erase_if(dbFolder.symlinks, [&](const InSyncFolder::SymlinkList::value_type& v) { return filter_.passFileFilter(parentRelPathPf + v.first); }); - erase_if(dir.dirs, [&](InSyncDir::DirList::value_type& v) + erase_if(dbFolder.folders, [&](InSyncFolder::FolderList::value_type& v) { const Zstring& itemRelPath = parentRelPathPf + v.first; @@ -689,20 +689,20 @@ private: //####################################################################################################################################### -std::shared_ptr<InSyncDir> zen::loadLastSynchronousState(const BaseDirPair& baseDirObj, //throw FileError, FileErrorDatabaseNotExisting -> return value always bound! - const std::function<void(std::int64_t bytesDelta)>& onUpdateStatus) +std::shared_ptr<InSyncFolder> zen::loadLastSynchronousState(const BaseFolderPair& baseFolder, //throw FileError, FileErrorDatabaseNotExisting -> return value always bound! + const std::function<void(std::int64_t bytesDelta)>& onUpdateStatus) { - const AbstractPathRef dbPathLeft = getDatabaseFilePath<LEFT_SIDE >(baseDirObj); - const AbstractPathRef dbPathRight = getDatabaseFilePath<RIGHT_SIDE>(baseDirObj); + const AbstractPath dbPathLeft = getDatabaseFilePath<LEFT_SIDE >(baseFolder); + const AbstractPath dbPathRight = getDatabaseFilePath<RIGHT_SIDE>(baseFolder); - if (!baseDirObj.isExisting<LEFT_SIDE >() || - !baseDirObj.isExisting<RIGHT_SIDE>()) + if (!baseFolder.isExisting<LEFT_SIDE >() || + !baseFolder.isExisting<RIGHT_SIDE>()) { //avoid race condition with directory existence check: reading sync.ffs_db may succeed although first dir check had failed => conflicts! //https://sourceforge.net/tracker/?func=detail&atid=1093080&aid=3531351&group_id=234430 - const AbstractPathRef filePath = !baseDirObj.isExisting<LEFT_SIDE>() ? dbPathLeft : dbPathRight; + const AbstractPath filePath = !baseFolder.isExisting<LEFT_SIDE>() ? dbPathLeft : dbPathRight; throw FileErrorDatabaseNotExisting(_("Initial synchronization:") + L" \n" + //it could be due to a to-be-created target directory not yet existing => FileErrorDatabaseNotExisting - replaceCpy(_("Database file %x does not yet exist."), L"%x", fmtPath(ABF::getDisplayPath(filePath)))); + replaceCpy(_("Database file %x does not yet exist."), L"%x", fmtPath(AFS::getDisplayPath(filePath)))); } //read file data: list of session ID + DirInfo-stream @@ -717,8 +717,8 @@ std::shared_ptr<InSyncDir> zen::loadLastSynchronousState(const BaseDirPair& base { return StreamParser::execute(streamLeft.second, //throw FileError itRight->second, - ABF::getDisplayPath(dbPathLeft), - ABF::getDisplayPath(dbPathRight)); + AFS::getDisplayPath(dbPathLeft), + AFS::getDisplayPath(dbPathRight)); } } throw FileErrorDatabaseNotExisting(_("Initial synchronization:") + L" \n" + @@ -726,18 +726,18 @@ std::shared_ptr<InSyncDir> zen::loadLastSynchronousState(const BaseDirPair& base } -void zen::saveLastSynchronousState(const BaseDirPair& baseDirObj, const std::function<void(std::int64_t bytesDelta)>& onUpdateStatus) //throw FileError +void zen::saveLastSynchronousState(const BaseFolderPair& baseFolder, const std::function<void(std::int64_t bytesDelta)>& onUpdateStatus) //throw FileError { //transactional behaviour! write to tmp files first - const AbstractPathRef dbPathLeft = getDatabaseFilePath<LEFT_SIDE >(baseDirObj); - const AbstractPathRef dbPathRight = getDatabaseFilePath<RIGHT_SIDE>(baseDirObj); + const AbstractPath dbPathLeft = getDatabaseFilePath<LEFT_SIDE >(baseFolder); + const AbstractPath dbPathRight = getDatabaseFilePath<RIGHT_SIDE>(baseFolder); - const AbstractPathRef dbPathLeftTmp = getDatabaseFilePath<LEFT_SIDE >(baseDirObj, true); - const AbstractPathRef dbPathRightTmp = getDatabaseFilePath<RIGHT_SIDE>(baseDirObj, true); + const AbstractPath dbPathLeftTmp = getDatabaseFilePath<LEFT_SIDE >(baseFolder, true); + const AbstractPath dbPathRightTmp = getDatabaseFilePath<RIGHT_SIDE>(baseFolder, true); //delete old tmp file, if necessary -> throws if deletion fails! - ABF::removeFile(dbPathLeftTmp); // - ABF::removeFile(dbPathRightTmp); //throw FileError + AFS::removeFile(dbPathLeftTmp); // + AFS::removeFile(dbPathRightTmp); //throw FileError //(try to) load old database files... DbStreams streamsLeft; //list of session ID + DirInfo-stream @@ -756,39 +756,39 @@ void zen::saveLastSynchronousState(const BaseDirPair& baseDirObj, const std::fun //find associated session: there can be at most one session within intersection of left and right ids auto itStreamLeftOld = streamsLeft .cend(); auto itStreamRightOld = streamsRight.cend(); - for (auto iterLeft = streamsLeft.begin(); iterLeft != streamsLeft.end(); ++iterLeft) + for (auto itL = streamsLeft.begin(); itL != streamsLeft.end(); ++itL) { - auto iterRight = streamsRight.find(iterLeft->first); - if (iterRight != streamsRight.end()) + auto itR = streamsRight.find(itL->first); + if (itR != streamsRight.end()) { - itStreamLeftOld = iterLeft; - itStreamRightOld = iterRight; + itStreamLeftOld = itL; + itStreamRightOld = itR; break; } } //load last synchrounous state - std::shared_ptr<InSyncDir> lastSyncState = std::make_shared<InSyncDir>(InSyncDir::DIR_STATUS_IN_SYNC); + std::shared_ptr<InSyncFolder> lastSyncState = std::make_shared<InSyncFolder>(InSyncFolder::DIR_STATUS_IN_SYNC); if (itStreamLeftOld != streamsLeft .end() && itStreamRightOld != streamsRight.end()) try { lastSyncState = StreamParser::execute(itStreamLeftOld ->second, //throw FileError itStreamRightOld->second, - ABF::getDisplayPath(dbPathLeft), - ABF::getDisplayPath(dbPathRight)); + AFS::getDisplayPath(dbPathLeft), + AFS::getDisplayPath(dbPathRight)); } catch (FileError&) {} //if error occurs: just overwrite old file! User is already informed about issues right after comparing! //update last synchrounous state - UpdateLastSynchronousState::execute(baseDirObj, *lastSyncState); + UpdateLastSynchronousState::execute(baseFolder, *lastSyncState); //serialize again ByteArray updatedStreamLeft; ByteArray updatedStreamRight; StreamGenerator::execute(*lastSyncState, //throw FileError - ABF::getDisplayPath(dbPathLeft), - ABF::getDisplayPath(dbPathRight), + AFS::getDisplayPath(dbPathLeft), + AFS::getDisplayPath(dbPathRight), updatedStreamLeft, updatedStreamRight); @@ -815,9 +815,9 @@ void zen::saveLastSynchronousState(const BaseDirPair& baseDirObj, const std::fun //operation finished: rename temp files -> this should work transactionally: //if there were no write access, creation of temp files would have failed - ABF::removeFile(dbPathLeft); //throw FileError - ABF::renameItem(dbPathLeftTmp, dbPathLeft); //throw FileError, (ErrorTargetExisting, ErrorDifferentVolume) + AFS::removeFile(dbPathLeft); //throw FileError + AFS::renameItem(dbPathLeftTmp, dbPathLeft); //throw FileError, (ErrorTargetExisting, ErrorDifferentVolume) - ABF::removeFile(dbPathRight); // - ABF::renameItem(dbPathRightTmp, dbPathRight); // + AFS::removeFile(dbPathRight); // + AFS::renameItem(dbPathRightTmp, dbPathRight); // } diff --git a/FreeFileSync/Source/lib/db_file.h b/FreeFileSync/Source/lib/db_file.h index 3f5e43ed..0876f7c2 100644 --- a/FreeFileSync/Source/lib/db_file.h +++ b/FreeFileSync/Source/lib/db_file.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef DBFILE_H_834275398588021574 -#define DBFILE_H_834275398588021574 +#ifndef DB_FILE_H_834275398588021574 +#define DB_FILE_H_834275398588021574 #include <zen/file_error.h> #include "../file_hierarchy.h" @@ -17,12 +17,12 @@ const Zchar SYNC_DB_FILE_ENDING[] = Zstr(".ffs_db"); //don't use Zstring as glob struct InSyncDescrFile //subset of FileDescriptor { - InSyncDescrFile(std::int64_t lastWriteTimeRawIn, const ABF::FileId& idIn) : + InSyncDescrFile(std::int64_t lastWriteTimeRawIn, const AFS::FileId& idIn) : lastWriteTimeRaw(lastWriteTimeRawIn), fileId(idIn) {} std::int64_t lastWriteTimeRaw; - ABF::FileId fileId; // == file id: optional! (however, always set on Linux, and *generally* available on Windows) + AFS::FileId fileId; // == file id: optional! (however, always set on Linux, and *generally* available on Windows) }; struct InSyncDescrLink @@ -51,7 +51,7 @@ struct InSyncSymlink CompareVariant cmpVar; }; -struct InSyncDir +struct InSyncFolder { //for directories we have a logical problem: we cannot have "not existent" as an indicator for //"no last synchronous state" since this precludes child elements that may be in sync! @@ -60,24 +60,24 @@ struct InSyncDir DIR_STATUS_IN_SYNC, DIR_STATUS_STRAW_MAN //there is no last synchronous state, but used as container only }; - InSyncDir(InSyncStatus statusIn) : status(statusIn) {} + InSyncFolder(InSyncStatus statusIn) : status(statusIn) {} InSyncStatus status; //------------------------------------------------------------------ - typedef std::map<Zstring, InSyncDir, LessFilePath> DirList; // - typedef std::map<Zstring, InSyncFile, LessFilePath> FileList; // key: file name - typedef std::map<Zstring, InSyncSymlink, LessFilePath> LinkList; // + typedef std::map<Zstring, InSyncFolder, LessFilePath> FolderList; // + typedef std::map<Zstring, InSyncFile, LessFilePath> FileList; // key: file name + typedef std::map<Zstring, InSyncSymlink, LessFilePath> SymlinkList; // //------------------------------------------------------------------ - DirList dirs; - FileList files; - LinkList symlinks; //non-followed symlinks + FolderList folders; + FileList files; + SymlinkList symlinks; //non-followed symlinks //convenience - InSyncDir& addDir(const Zstring& shortName, InSyncStatus st) + InSyncFolder& addFolder(const Zstring& shortName, InSyncStatus st) { - return dirs.emplace(shortName, InSyncDir(st)).first->second; + return folders.emplace(shortName, InSyncFolder(st)).first->second; } void addFile(const Zstring& shortName, const InSyncDescrFile& dataL, const InSyncDescrFile& dataR, CompareVariant cmpVar, std::uint64_t fileSize) @@ -94,11 +94,11 @@ struct InSyncDir DEFINE_NEW_FILE_ERROR(FileErrorDatabaseNotExisting); -std::shared_ptr<InSyncDir> loadLastSynchronousState(const BaseDirPair& baseDirObj, //throw FileError, FileErrorDatabaseNotExisting -> return value always bound! - const std::function<void(std::int64_t bytesDelta)>& onUpdateStatus); +std::shared_ptr<InSyncFolder> loadLastSynchronousState(const BaseFolderPair& baseDirObj, //throw FileError, FileErrorDatabaseNotExisting -> return value always bound! + const std::function<void(std::int64_t bytesDelta)>& onUpdateStatus); -void saveLastSynchronousState(const BaseDirPair& baseDirObj, //throw FileError +void saveLastSynchronousState(const BaseFolderPair& baseDirObj, //throw FileError const std::function<void(std::int64_t bytesDelta)>& onUpdateStatus); } -#endif //DBFILE_H_834275398588021574 +#endif //DB_FILE_H_834275398588021574 diff --git a/FreeFileSync/Source/lib/dir_exist_async.h b/FreeFileSync/Source/lib/dir_exist_async.h index 492522f4..38c595f3 100644 --- a/FreeFileSync/Source/lib/dir_exist_async.h +++ b/FreeFileSync/Source/lib/dir_exist_async.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef DIR_EXIST_HEADER_08173281673432158067342132467183267 -#define DIR_EXIST_HEADER_08173281673432158067342132467183267 +#ifndef DIR_EXIST_ASYNC_H_0817328167343215806734213 +#define DIR_EXIST_ASYNC_H_0817328167343215806734213 #include <list> #include <zen/thread.h> @@ -22,47 +22,38 @@ namespace //- check existence of all directories in parallel! (avoid adding up search times if multiple network drives are not reachable) //- add reasonable time-out time! //- avoid checking duplicate entries by design: std::set -struct DirectoryStatus +struct FolderStatus { - std::set<const ABF*, ABF::LessItemPath> existingBaseFolder; - std::set<const ABF*, ABF::LessItemPath> missingBaseFolder; - std::map<const ABF*, FileError, ABF::LessItemPath> failedChecks; + std::set<AbstractPath, AFS::LessAbstractPath> existing; + std::set<AbstractPath, AFS::LessAbstractPath> missing; + std::map<AbstractPath, FileError, AFS::LessAbstractPath> failedChecks; }; - -DirectoryStatus checkFolderExistenceUpdating(const std::set<const ABF*, ABF::LessItemPath>& baseFolders, bool allowUserInteraction, ProcessCallback& procCallback) +FolderStatus getFolderStatusNonBlocking(const std::set<AbstractPath, AFS::LessAbstractPath>& folderPaths, bool allowUserInteraction, ProcessCallback& procCallback) { using namespace zen; - DirectoryStatus output; + FolderStatus output; - std::list<std::pair<const ABF*, std::future<bool>>> futureInfo; + std::list<std::pair<AbstractPath, std::future<bool>>> futureInfo; - for (const ABF* baseFolder : baseFolders) - if (!baseFolder->emptyBaseFolderPath()) //skip empty dirs + for (const AbstractPath& folderPath : folderPaths) + if (!AFS::isNullPath(folderPath)) //skip empty dirs + futureInfo.emplace_back(folderPath, runAsync([folderPath, allowUserInteraction] //AbstractPath is thread-safe like an int! :) { - AbstractPathRef folderPath = baseFolder->getAbstractPath(); - - std::function<void()> connectFolder /*throw FileError*/ = baseFolder->getAsyncConnectFolder(allowUserInteraction); //noexcept - std::function<bool()> dirExists /*throw FileError*/ = ABF::getAsyncCheckFolderExists(folderPath); //noexcept + //1. login to network share, open FTP connection, ect. + AFS::connectNetworkFolder(folderPath, allowUserInteraction); //throw FileError - futureInfo.emplace_back(baseFolder, runAsync([connectFolder, dirExists] - { - //1. login to network share, open FTP connection, ect. - if (connectFolder) - connectFolder(); //throw FileError - - //2. check dir existence - return dirExists(); //throw FileError - })); - } + //2. check dir existence + return AFS::folderExistsThrowing(folderPath); //throw FileError + })); - //don't wait (almost) endlessly like win32 would on non-existing network shares: - std::chrono::steady_clock::time_point endTime = std::chrono::steady_clock::now() + std::chrono::seconds(20); //consider CD-rom insert or hard disk spin up time from sleep + //don't wait (almost) endlessly like Win32 would on non-existing network shares: + std::chrono::steady_clock::time_point endTime = std::chrono::steady_clock::now() + std::chrono::seconds(20); //consider CD-ROM insert or hard disk spin up time from sleep for (auto& fi : futureInfo) { - const std::wstring& displayPathFmt = fmtPath(ABF::getDisplayPath(fi.first->getAbstractPath())); + const std::wstring& displayPathFmt = fmtPath(AFS::getDisplayPath(fi.first)); procCallback.reportStatus(replaceCpy(_("Searching for folder %x..."), L"%x", displayPathFmt)); //may throw! @@ -76,9 +67,9 @@ DirectoryStatus checkFolderExistenceUpdating(const std::set<const ABF*, ABF::Les { //call future::get() only *once*! otherwise: undefined behavior! if (fi.second.get()) //throw FileError - output.existingBaseFolder.insert(fi.first); + output.existing.insert(fi.first); else - output.missingBaseFolder.insert(fi.first); + output.missing.insert(fi.first); } catch (const FileError& e) { output.failedChecks.emplace(fi.first, e); } } @@ -90,13 +81,13 @@ DirectoryStatus checkFolderExistenceUpdating(const std::set<const ABF*, ABF::Les } } -inline //also silences Clang "unused function" for compilation units depending from getExistingDirsUpdating() only -bool folderExistsUpdating(const ABF& baseFolder, bool allowUserInteraction, ProcessCallback& procCallback) +inline //also silences Clang "unused function" +bool folderExistsNonBlocking(const AbstractPath& folderPath, bool allowUserInteraction, ProcessCallback& procCallback) { - std::set<const ABF*, ABF::LessItemPath> baseFolders{ &baseFolder }; - const DirectoryStatus status = checkFolderExistenceUpdating(baseFolders, allowUserInteraction, procCallback); - return status.existingBaseFolder.find(&baseFolder) != status.existingBaseFolder.end(); + std::set<AbstractPath, AFS::LessAbstractPath> folderPaths{ folderPath}; + const FolderStatus status = getFolderStatusNonBlocking(folderPaths, allowUserInteraction, procCallback); + return status.existing.find(folderPath) != status.existing.end(); } } -#endif //DIR_EXIST_HEADER_08173281673432158067342132467183267 +#endif //DIR_EXIST_ASYNC_H_0817328167343215806734213 diff --git a/FreeFileSync/Source/lib/dir_lock.cpp b/FreeFileSync/Source/lib/dir_lock.cpp index 190378ad..5e837e15 100644 --- a/FreeFileSync/Source/lib/dir_lock.cpp +++ b/FreeFileSync/Source/lib/dir_lock.cpp @@ -12,12 +12,12 @@ #include <zen/scope_guard.h> #include <zen/guid.h> #include <zen/tick_count.h> -#include <zen/int64.h> #include <zen/file_access.h> #include <zen/serialize.h> #include <zen/optional.h> #ifdef ZEN_WIN + #include <zen/int64.h> #include <zen/win.h> //includes "windows.h" #include <zen/long_path_prefix.h> #include <zen/privilege.h> @@ -50,14 +50,22 @@ using MemStreamOut = MemoryStreamOut<ByteArray>; using MemStreamIn = MemoryStreamIn <ByteArray>; +#ifndef NDEBUG + const std::thread::id mainThreadId = std::this_thread::get_id(); +#endif + + //worker thread class LifeSigns { public: - LifeSigns(const Zstring& lockfilepath) : lockfilepath_(lockfilepath) {} + LifeSigns(const Zstring& lockFilePath) : lockFilePath_(lockFilePath) {} void operator()() const //throw ThreadInterruption { +#ifdef ZEN_WIN + setCurrentThreadName("Folder Lock Life Signs"); +#endif try { for (;;) @@ -74,7 +82,7 @@ public: } } - void emitLifeSign() const //try to append one byte...; throw() + void emitLifeSign() const //try to append one byte..., throw() { #ifdef ZEN_WIN try { activatePrivilege(SE_BACKUP_NAME); } @@ -82,12 +90,13 @@ public: try { activatePrivilege(SE_RESTORE_NAME); } catch (const FileError&) {} - const HANDLE fileHandle = ::CreateFile(applyLongPathPrefix(lockfilepath_).c_str(), //_In_ LPCTSTR lpFileName, + const HANDLE fileHandle = ::CreateFile(applyLongPathPrefix(lockFilePath_).c_str(), //_In_ LPCTSTR lpFileName, //use both when writing over network, see comment in file_io.cpp GENERIC_READ | GENERIC_WRITE, //_In_ DWORD dwDesiredAccess, FILE_SHARE_READ, //_In_ DWORD dwShareMode, nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes, OPEN_EXISTING, //_In_ DWORD dwCreationDisposition, + FILE_ATTRIBUTE_TEMPORARY | // FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, //_In_ DWORD dwFlagsAndAttributes, nullptr); //_In_opt_ HANDLE hTemplateFile if (fileHandle == INVALID_HANDLE_VALUE) @@ -95,7 +104,7 @@ public: ZEN_ON_SCOPE_EXIT(::CloseHandle(fileHandle)); //ATTENTION: setting file pointer IS required! => use CreateFile/GENERIC_WRITE + SetFilePointerEx! - //although CreateFile/FILE_APPEND_DATA without SetFilePointerEx works locally, it MAY NOT work on some network shares creating a 4 gig file!!! + //although CreateFile/FILE_APPEND_DATA without SetFilePointerEx works locally, it DOES NOT work on certain network shares creating a 4 gig file!!! const LARGE_INTEGER moveDist = {}; if (!::SetFilePointerEx(fileHandle, //__in HANDLE hFile, moveDist, //__in LARGE_INTEGER liDistanceToMove, @@ -112,7 +121,7 @@ public: return; #elif defined ZEN_LINUX || defined ZEN_MAC - const int fileHandle = ::open(lockfilepath_.c_str(), O_WRONLY | O_APPEND); + const int fileHandle = ::open(lockFilePath_.c_str(), O_WRONLY | O_APPEND); if (fileHandle == -1) return; ZEN_ON_SCOPE_EXIT(::close(fileHandle)); @@ -123,7 +132,7 @@ public: } private: - const Zstring lockfilepath_; //thread local! atomic ref-count => binary value-type semantics! + const Zstring lockFilePath_; //thread local! atomic ref-count => binary value-type semantics! }; @@ -148,13 +157,13 @@ std::uint64_t getLockFileSize(const Zstring& filepath) //throw FileError } -Zstring abandonedLockDeletionName(const Zstring& lockfilepath) //make sure to NOT change file ending! +Zstring abandonedLockDeletionName(const Zstring& lockFilePath) //make sure to NOT change file ending! { - const size_t pos = lockfilepath.rfind(FILE_NAME_SEPARATOR); //search from end - return pos == Zstring::npos ? Zstr("Del.") + lockfilepath : - Zstring(lockfilepath.c_str(), pos + 1) + //include path separator + const size_t pos = lockFilePath.rfind(FILE_NAME_SEPARATOR); //search from end + return pos == Zstring::npos ? Zstr("Del.") + lockFilePath : + Zstring(lockFilePath.c_str(), pos + 1) + //include path separator Zstr("Del.") + - afterLast(lockfilepath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL); + afterLast(lockFilePath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL); } @@ -369,24 +378,24 @@ struct LockInformation //throw FileError //wxGetFullHostName() is a performance killer and can hang for some users, so don't touch! -LockInformation retrieveLockInfo(const Zstring& lockfilepath) //throw FileError +LockInformation retrieveLockInfo(const Zstring& lockFilePath) //throw FileError { - MemStreamIn streamIn = loadBinStream<ByteArray>(lockfilepath, nullptr); //throw FileError + MemStreamIn streamIn = loadBinStream<ByteArray>(lockFilePath, nullptr); //throw FileError try { return LockInformation(streamIn); //throw UnexpectedEndOfStreamError } catch (UnexpectedEndOfStreamError&) { - throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(lockfilepath)), L"unexpected end of stream"); + throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(lockFilePath)), L"unexpected end of stream"); } } inline -std::string retrieveLockId(const Zstring& lockfilepath) //throw FileError +std::string retrieveLockId(const Zstring& lockFilePath) //throw FileError { - return retrieveLockInfo(lockfilepath).lockId; //throw FileError + return retrieveLockInfo(lockFilePath).lockId; //throw FileError } @@ -419,9 +428,9 @@ ProcessStatus getProcessStatus(const LockInformation& lockInfo) //throw FileErro const std::int64_t TICKS_PER_SEC = ticksPerSec(); //= 0 on error -void waitOnDirLock(const Zstring& lockfilepath, DirLockCallback* callback) //throw FileError +void waitOnDirLock(const Zstring& lockFilePath, DirLockCallback* callback) //throw FileError { - std::wstring infoMsg = _("Waiting while directory is locked:") + L' ' + fmtPath(lockfilepath); + std::wstring infoMsg = _("Waiting while directory is locked:") + L' ' + fmtPath(lockFilePath); if (callback) callback->reportStatus(infoMsg); @@ -433,7 +442,7 @@ void waitOnDirLock(const Zstring& lockfilepath, DirLockCallback* callback) //thr std::string originalLockId; //empty if it cannot be retrieved try { - const LockInformation& lockInfo = retrieveLockInfo(lockfilepath); //throw FileError + const LockInformation& lockInfo = retrieveLockInfo(lockFilePath); //throw FileError //enhance status message and show which user is holding the lock: infoMsg += L" | " + _("Lock owner:") + L' ' + utfCvrtTo<std::wstring>(lockInfo.userId); @@ -457,7 +466,7 @@ void waitOnDirLock(const Zstring& lockfilepath, DirLockCallback* callback) //thr for (;;) { const TickVal now = getTicks(); - const std::uint64_t fileSizeNew = getLockFileSize(lockfilepath); //throw FileError + const std::uint64_t fileSizeNew = getLockFileSize(lockFilePath); //throw FileError if (TICKS_PER_SEC <= 0 || !lastLifeSign.isValid() || !now.isValid()) throw FileError(L"System timer failed."); //no i18n: "should" never throw ;) @@ -471,18 +480,18 @@ void waitOnDirLock(const Zstring& lockfilepath, DirLockCallback* callback) //thr if (lockOwnderDead || //no need to wait any longer... dist(lastLifeSign, now) / TICKS_PER_SEC > DETECT_ABANDONED_INTERVAL) { - DirLock dummy(abandonedLockDeletionName(lockfilepath), callback); //throw FileError + DirLock dummy(abandonedLockDeletionName(lockFilePath), callback); //throw FileError //now that the lock is in place check existence again: meanwhile another process may have deleted and created a new lock! if (!originalLockId.empty()) - if (retrieveLockId(lockfilepath) != originalLockId) //throw FileError -> since originalLockId is filled, we are not expecting errors! + if (retrieveLockId(lockFilePath) != originalLockId) //throw FileError -> since originalLockId is filled, we are not expecting errors! return; //another process has placed a new lock, leave scope: the wait for the old lock is technically over... - if (getLockFileSize(lockfilepath) != fileSizeOld) //throw FileError + if (getLockFileSize(lockFilePath) != fileSizeOld) //throw FileError continue; //late life sign - removeFile(lockfilepath); //throw FileError + removeFile(lockFilePath); //throw FileError return; } @@ -510,24 +519,24 @@ void waitOnDirLock(const Zstring& lockfilepath, DirLockCallback* callback) //thr } catch (FileError&) { - if (!somethingExists(lockfilepath)) //a benign(?) race condition with FileError + if (!somethingExists(lockFilePath)) //a benign(?) race condition with FileError return; //what we are waiting for... throw; } } -void releaseLock(const Zstring& lockfilepath) //throw () +void releaseLock(const Zstring& lockFilePath) //throw () { try { - removeFile(lockfilepath); //throw FileError + removeFile(lockFilePath); //throw FileError } catch (FileError&) {} } -bool tryLock(const Zstring& lockfilepath) //throw FileError +bool tryLock(const Zstring& lockFilePath) //throw FileError { #ifdef ZEN_WIN try { activatePrivilege(SE_BACKUP_NAME); } @@ -535,13 +544,15 @@ bool tryLock(const Zstring& lockfilepath) //throw FileError try { activatePrivilege(SE_RESTORE_NAME); } catch (const FileError&) {} - const HANDLE fileHandle = ::CreateFile(applyLongPathPrefix(lockfilepath).c_str(), //_In_ LPCTSTR lpFileName, + const HANDLE fileHandle = ::CreateFile(applyLongPathPrefix(lockFilePath).c_str(), //_In_ LPCTSTR lpFileName, //use both when writing over network, see comment in file_io.cpp GENERIC_READ | GENERIC_WRITE, //_In_ DWORD dwDesiredAccess, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, //_In_ DWORD dwShareMode, nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes, CREATE_NEW, //_In_ DWORD dwCreationDisposition, - FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, //_In_ DWORD dwFlagsAndAttributes, + FILE_ATTRIBUTE_TEMPORARY | // + FILE_ATTRIBUTE_NORMAL | + FILE_FLAG_BACKUP_SEMANTICS, //_In_ DWORD dwFlagsAndAttributes, nullptr); //_In_opt_ HANDLE hTemplateFile if (fileHandle == INVALID_HANDLE_VALUE) { @@ -550,31 +561,28 @@ bool tryLock(const Zstring& lockfilepath) //throw FileError ec == ERROR_ALREADY_EXISTS) //comment on msdn claims, this one is used on Windows Mobile 6 return false; - throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(lockfilepath)), formatSystemError(L"CreateFile", ec)); + throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(lockFilePath)), formatSystemError(L"CreateFile", ec)); } - ScopeGuard guardLockFile = zen::makeGuard([&] { removeFile(lockfilepath); }); - FileOutput fileOut(fileHandle, lockfilepath); //pass handle ownership - //be careful to avoid CreateFile() + CREATE_ALWAYS on a hidden file -> see file_io.cpp - //=> we don't need it that badly //::SetFileAttributes(applyLongPathPrefix(lockfilepath).c_str(), FILE_ATTRIBUTE_HIDDEN); //(try to) hide it + //=> we don't need it that badly //::SetFileAttributes(applyLongPathPrefix(lockFilePath).c_str(), FILE_ATTRIBUTE_HIDDEN); //(try to) hide it #elif defined ZEN_LINUX || defined ZEN_MAC const mode_t oldMask = ::umask(0); //important: we want the lock file to have exactly the permissions specified ZEN_ON_SCOPE_EXIT(::umask(oldMask)); //O_EXCL contains a race condition on NFS file systems: http://linux.die.net/man/2/open - const int fileHandle = ::open(lockfilepath.c_str(), O_CREAT | O_EXCL | O_WRONLY, + const int fileHandle = ::open(lockFilePath.c_str(), O_CREAT | O_EXCL | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); if (fileHandle == -1) { if (errno == EEXIST) return false; else - THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(lockfilepath)), L"open"); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(lockFilePath)), L"open"); } - ScopeGuard guardLockFile = zen::makeGuard([&] { removeFile(lockfilepath); }); - FileOutput fileOut(fileHandle, lockfilepath); //pass handle ownership #endif + ZEN_ON_SCOPE_FAIL( removeFile(lockFilePath); ); + FileOutput fileOut(fileHandle, lockFilePath); //pass handle ownership //write housekeeping info: user, process info, lock GUID ByteArray binStream = [&] @@ -587,7 +595,6 @@ bool tryLock(const Zstring& lockfilepath) //throw FileError if (!binStream.empty()) fileOut.write(&*binStream.begin(), binStream.size()); //throw FileError - guardLockFile.dismiss(); //lockfile created successfully return true; } } @@ -596,13 +603,13 @@ bool tryLock(const Zstring& lockfilepath) //throw FileError class DirLock::SharedDirLock { public: - SharedDirLock(const Zstring& lockfilepath, DirLockCallback* callback) : //throw FileError - lockfilepath_(lockfilepath) + SharedDirLock(const Zstring& lockFilePath, DirLockCallback* callback) : //throw FileError + lockFilePath_(lockFilePath) { - while (!::tryLock(lockfilepath)) //throw FileError - ::waitOnDirLock(lockfilepath, callback); // + while (!::tryLock(lockFilePath)) //throw FileError + ::waitOnDirLock(lockFilePath, callback); // - lifeSignthread = InterruptibleThread(LifeSigns(lockfilepath)); + lifeSignthread = InterruptibleThread(LifeSigns(lockFilePath)); } ~SharedDirLock() @@ -610,14 +617,14 @@ public: lifeSignthread.interrupt(); //thread lifetime is subset of this instances's life lifeSignthread.join(); - ::releaseLock(lockfilepath_); //throw () + ::releaseLock(lockFilePath_); //throw () } private: SharedDirLock (const DirLock&) = delete; SharedDirLock& operator=(const DirLock&) = delete; - const Zstring lockfilepath_; + const Zstring lockFilePath_; InterruptibleThread lifeSignthread; }; @@ -632,33 +639,35 @@ public: } //create or retrieve a SharedDirLock - std::shared_ptr<SharedDirLock> retrieve(const Zstring& lockfilepath, DirLockCallback* callback) //throw FileError + std::shared_ptr<SharedDirLock> retrieve(const Zstring& lockFilePath, DirLockCallback* callback) //throw FileError { + assert(std::this_thread::get_id() == mainThreadId); //function is not thread-safe! + tidyUp(); //optimization: check if we already own a lock for this path - auto iterGuid = fileToGuid.find(lockfilepath); + auto iterGuid = fileToGuid.find(lockFilePath); if (iterGuid != fileToGuid.end()) if (const std::shared_ptr<SharedDirLock>& activeLock = getActiveLock(iterGuid->second)) //returns null-lock if not found return activeLock; //SharedDirLock is still active -> enlarge circle of shared ownership - try //check based on lock GUID, deadlock prevention: "lockfilepath" may be an alternative name for a lock already owned by this process + try //check based on lock GUID, deadlock prevention: "lockFilePath" may be an alternative name for a lock already owned by this process { - const std::string lockId = retrieveLockId(lockfilepath); //throw FileError + const std::string lockId = retrieveLockId(lockFilePath); //throw FileError if (const std::shared_ptr<SharedDirLock>& activeLock = getActiveLock(lockId)) //returns null-lock if not found { - fileToGuid[lockfilepath] = lockId; //found an alias for one of our active locks + fileToGuid[lockFilePath] = lockId; //found an alias for one of our active locks return activeLock; } } catch (FileError&) {} //catch everything, let SharedDirLock constructor deal with errors, e.g. 0-sized/corrupted lock files //lock not owned by us => create a new one - auto newLock = std::make_shared<SharedDirLock>(lockfilepath, callback); //throw FileError - const std::string& newLockGuid = retrieveLockId(lockfilepath); //throw FileError + auto newLock = std::make_shared<SharedDirLock>(lockFilePath, callback); //throw FileError + const std::string& newLockGuid = retrieveLockId(lockFilePath); //throw FileError //update registry - fileToGuid[lockfilepath] = newLockGuid; //throw() + fileToGuid[lockFilePath] = newLockGuid; //throw() guidToLock[newLockGuid] = newLock; // return newLock; @@ -675,8 +684,8 @@ private: std::shared_ptr<SharedDirLock> getActiveLock(const UniqueId& lockId) //returns null if none found { - auto iterLock = guidToLock.find(lockId); - return iterLock != guidToLock.end() ? iterLock->second.lock() : nullptr; //try to get shared_ptr; throw() + auto it = guidToLock.find(lockId); + return it != guidToLock.end() ? it->second.lock() : nullptr; //try to get shared_ptr; throw() } void tidyUp() //remove obsolete entries @@ -685,20 +694,20 @@ private: erase_if(fileToGuid, [&](const FileToGuidMap::value_type& v) { return guidToLock.find(v.second) == guidToLock.end(); }); } - FileToGuidMap fileToGuid; //lockname |-> GUID; locks can be referenced by a lockfilepath or alternatively a GUID + FileToGuidMap fileToGuid; //lockname |-> GUID; locks can be referenced by a lockFilePath or alternatively a GUID GuidToLockMap guidToLock; //GUID |-> "shared lock ownership" }; -DirLock::DirLock(const Zstring& lockfilepath, DirLockCallback* callback) //throw FileError +DirLock::DirLock(const Zstring& lockFilePath, DirLockCallback* callback) //throw FileError { if (callback) - callback->reportStatus(replaceCpy(_("Creating file %x"), L"%x", fmtPath(lockfilepath))); + callback->reportStatus(replaceCpy(_("Creating file %x"), L"%x", fmtPath(lockFilePath))); #ifdef ZEN_WIN const DWORD bufferSize = 10000; std::vector<wchar_t> volName(bufferSize); - if (::GetVolumePathName(lockfilepath.c_str(), //__in LPCTSTR lpszFileName, + if (::GetVolumePathName(lockFilePath.c_str(), //__in LPCTSTR lpszFileName, &volName[0], //__out LPTSTR lpszVolumePathName, bufferSize)) //__in DWORD cchBufferLength { @@ -708,5 +717,5 @@ DirLock::DirLock(const Zstring& lockfilepath, DirLockCallback* callback) //throw } #endif - sharedLock = LockAdmin::instance().retrieve(lockfilepath, callback); //throw FileError + sharedLock = LockAdmin::instance().retrieve(lockFilePath, callback); //throw FileError } diff --git a/FreeFileSync/Source/lib/dir_lock.h b/FreeFileSync/Source/lib/dir_lock.h index 00634b5d..6ababb12 100644 --- a/FreeFileSync/Source/lib/dir_lock.h +++ b/FreeFileSync/Source/lib/dir_lock.h @@ -3,8 +3,9 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 * // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef DIR_LOCK_H_INCLUDED -#define DIR_LOCK_H_INCLUDED + +#ifndef DIR_LOCK_H_81740832174954356 +#define DIR_LOCK_H_81740832174954356 #include <memory> #include <zen/file_error.h> @@ -26,14 +27,14 @@ RAII structure to place a directory lock against other FFS processes: - ownership shared between all object instances refering to a specific lock location(= GUID) - can be copied safely and efficiently! (ref-counting) - detects and resolves abandoned locks (instantly if lock is associated with local pc, else after 30 seconds) - - temporary locks created during abandoned lock resolution keep "lockfilepath"'s extension + - temporary locks created during abandoned lock resolution keep "lockFilePath"'s extension - race-free (Windows, almost on Linux(NFS)) - - NOT thread-safe! (1. static LockAdmin 2. directory name aliases must be resolved sequentially!) + - NOT thread-safe! (1. global LockAdmin 2. directory name aliases must be resolved sequentially!) */ class DirLock { public: - DirLock(const Zstring& lockfilepath, DirLockCallback* callback = nullptr); //throw FileError, callback only used during construction + DirLock(const Zstring& lockFilePath, DirLockCallback* callback = nullptr); //throw FileError, callback only used during construction private: class LockAdmin; @@ -42,4 +43,4 @@ private: }; } -#endif // DIR_LOCK_H_INCLUDED +#endif //DIR_LOCK_H_81740832174954356 diff --git a/FreeFileSync/Source/lib/error_log.h b/FreeFileSync/Source/lib/error_log.h index 465f2aa5..904f5946 100644 --- a/FreeFileSync/Source/lib/error_log.h +++ b/FreeFileSync/Source/lib/error_log.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef ERROR_LOG_89734181783491324134 -#define ERROR_LOG_89734181783491324134 +#ifndef ERROR_LOG_H_89734181783491324134 +#define ERROR_LOG_H_89734181783491324134 #include <cassert> #include <zen/serialize.h> @@ -40,4 +40,4 @@ void logError(const std::string& msg) //throw() } } -#endif //ERROR_LOG_89734181783491324134 +#endif //ERROR_LOG_H_89734181783491324134 diff --git a/FreeFileSync/Source/lib/ffs_paths.cpp b/FreeFileSync/Source/lib/ffs_paths.cpp index 818b3763..61f21b7a 100644 --- a/FreeFileSync/Source/lib/ffs_paths.cpp +++ b/FreeFileSync/Source/lib/ffs_paths.cpp @@ -114,7 +114,7 @@ Zstring zen::getConfigDir() //this function is called by RealtimeSync!!! -Zstring zen::getFreeFileSyncLauncher() +Zstring zen::getFreeFileSyncLauncherPath() { #ifdef ZEN_WIN return getInstallDir() + Zstr("FreeFileSync.exe"); diff --git a/FreeFileSync/Source/lib/ffs_paths.h b/FreeFileSync/Source/lib/ffs_paths.h index d4f2a0f8..65b26f4f 100644 --- a/FreeFileSync/Source/lib/ffs_paths.h +++ b/FreeFileSync/Source/lib/ffs_paths.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef STANDARDPATHS_H_84275908342534253425 -#define STANDARDPATHS_H_84275908342534253425 +#ifndef FFS_PATHS_H_842759083425342534253 +#define FFS_PATHS_H_842759083425342534253 #include <zen/zstring.h> @@ -18,8 +18,8 @@ Zstring getResourceDir(); //resource directory WITH path separator at end Zstring getConfigDir (); //config directory WITH path separator at end //------------------------------------------------------------------------------ -Zstring getFreeFileSyncLauncher(); //full path to application launcher C:\...\FreeFileSync.exe +Zstring getFreeFileSyncLauncherPath(); //full path to application launcher C:\...\FreeFileSync.exe bool manualProgramUpdateRequired(); } -#endif //STANDARDPATHS_H_84275908342534253425 +#endif //FFS_PATHS_H_842759083425342534253 diff --git a/FreeFileSync/Source/lib/generate_logfile.h b/FreeFileSync/Source/lib/generate_logfile.h index 608e0289..a27daf6a 100644 --- a/FreeFileSync/Source/lib/generate_logfile.h +++ b/FreeFileSync/Source/lib/generate_logfile.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef GEN_LOGFILE_H_93172643216748973216458732165415 -#define GEN_LOGFILE_H_93172643216748973216458732165415 +#ifndef GENERATE_LOGFILE_H_931726432167489732164 +#define GENERATE_LOGFILE_H_931726432167489732164 #include <zen/error_log.h> #include <zen/serialize.h> @@ -119,7 +119,7 @@ std::wstring generateLogHeader(const SummaryInfo& s) inline void saveLogToFile(const SummaryInfo& summary, //throw FileError const ErrorLog& log, - ABF::OutputStream& streamOut, + AFS::OutputStream& streamOut, const std::function<void(std::int64_t bytesDelta)>& onUpdateSaveStatus) { //write log items in blocks instead of creating one big string: memory allocation might fail; think 1 million entries! @@ -204,10 +204,10 @@ void saveToLastSyncsLog(const SummaryInfo& summary, //throw FileError if (newStream.size() > maxBytesToWrite) { //but do not cut in the middle of a row - auto iter = std::search(newStream.cbegin() + maxBytesToWrite, newStream.cend(), std::begin(LINE_BREAK), std::end(LINE_BREAK) - 1); - if (iter != newStream.cend()) + auto it = std::search(newStream.cbegin() + maxBytesToWrite, newStream.cend(), std::begin(LINE_BREAK), std::end(LINE_BREAK) - 1); + if (it != newStream.cend()) { - newStream.resize(iter - newStream.cbegin()); + newStream.resize(it - newStream.cbegin()); newStream += LINE_BREAK; newStream += "[...]"; @@ -221,4 +221,4 @@ void saveToLastSyncsLog(const SummaryInfo& summary, //throw FileError } } -#endif //GEN_LOGFILE_H_93172643216748973216458732165415 +#endif //GENERATE_LOGFILE_H_931726432167489732164 diff --git a/FreeFileSync/Source/lib/hard_filter.h b/FreeFileSync/Source/lib/hard_filter.h index 4a562ec3..a6b68d9c 100644 --- a/FreeFileSync/Source/lib/hard_filter.h +++ b/FreeFileSync/Source/lib/hard_filter.h @@ -21,7 +21,7 @@ Semantics of HardFilter: class hierarchy: - HardFilter (interface) + HardFilter (interface) /|\ _________|_____________ | | | diff --git a/FreeFileSync/Source/lib/help_provider.h b/FreeFileSync/Source/lib/help_provider.h index 8e42b87d..e84e8f34 100644 --- a/FreeFileSync/Source/lib/help_provider.h +++ b/FreeFileSync/Source/lib/help_provider.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef HELPPROVIDER_H_85930427583421563126 -#define HELPPROVIDER_H_85930427583421563126 +#ifndef HELP_PROVIDER_H_85930427583421563126 +#define HELP_PROVIDER_H_85930427583421563126 #ifdef ZEN_WIN #include <zen/zstring.h> @@ -126,4 +126,4 @@ void uninitializeHelp() } } -#endif //HELPPROVIDER_H_85930427583421563126 +#endif //HELP_PROVIDER_H_85930427583421563126 diff --git a/FreeFileSync/Source/lib/icon_buffer.cpp b/FreeFileSync/Source/lib/icon_buffer.cpp index b2d0162b..617c6489 100644 --- a/FreeFileSync/Source/lib/icon_buffer.cpp +++ b/FreeFileSync/Source/lib/icon_buffer.cpp @@ -18,7 +18,7 @@ #endif using namespace zen; -using ABF = AbstractBaseFolder; +using AFS = AbstractFileSystem; namespace @@ -45,8 +45,7 @@ wxBitmap extractWxBitmap(ImageHolder&& ih) if (!ih.getRgb()) return wxNullBitmap; - //let wxImage take ownership: - wxImage img(ih.getWidth(), ih.getHeight(), ih.releaseRgb(), false /*static_data*/); + wxImage img(ih.getWidth(), ih.getHeight(), ih.releaseRgb(), false /*static_data*/); //pass ownership if (ih.getAlpha()) img.SetAlpha(ih.releaseAlpha(), false); return wxBitmap(img); @@ -54,27 +53,18 @@ wxBitmap extractWxBitmap(ImageHolder&& ih) #ifdef ZEN_WIN -std::set<Zstring, LessFilePath> customIconExt //function-scope statics are not (yet) thread-safe in VC12 -{ - L"ani", - L"cur", - L"exe", - L"ico", - L"msc", - L"scr" -}; -std::set<Zstring, LessFilePath> linkExt -{ - L"lnk", - L"pif", - L"url", - L"website" -}; +const std::set<Zstring, LessFilePath> linkExt { L"lnk", L"pif", L"url", L"website" }; + //test for extension for non-thumbnail icons that can have a stock icon which does not have to be physically read from disc inline bool hasStandardIconExtension(const Zstring& filePath) { + static const std::set<Zstring, LessFilePath> customIconExt { L"ani", L"cur", L"exe", L"ico", L"msc", L"scr" }; //function-scope statics are not (yet) thread-safe in VC12 +#if defined _MSC_VER && _MSC_VER < 1900 +#error function scope static initialization is not yet thread-safe! +#endif + const Zstring extension(getFileExtension(filePath)); return customIconExt.find(extension) == customIconExt.end() && @@ -85,7 +75,7 @@ bool hasStandardIconExtension(const Zstring& filePath) //################################################################################################################################################ -ImageHolder getDisplayIcon(const ABF::IconLoader& iconLoader, const Zstring& templateName, IconBuffer::IconSize sz) +ImageHolder getDisplayIcon(const AbstractPath& itemPath, IconBuffer::IconSize sz) { //1. try to load thumbnails switch (sz) @@ -94,21 +84,21 @@ ImageHolder getDisplayIcon(const ABF::IconLoader& iconLoader, const Zstring& tem break; case IconBuffer::SIZE_MEDIUM: case IconBuffer::SIZE_LARGE: - if (iconLoader.getThumbnailImage) - if (ImageHolder img = iconLoader.getThumbnailImage(IconBuffer::getSize(sz))) - return img; + if (ImageHolder img = AFS::getThumbnailImage(itemPath, IconBuffer::getSize(sz))) + return img; //else: fallback to non-thumbnail icon break; } + const Zstring& templateName = AFS::getFileShortName(itemPath); + //2. retrieve file icons #ifdef ZEN_WIN - //result will be buffered under full filepath, not extension; this is okay: failure to load thumbnail is independent from extension in general! - if (!hasStandardIconExtension(templateName)) //"pricey" extensions are stored with full path and are read from disk, while cheap ones require just the extension + //result will be buffered with full path, not extension; this is okay: failure to load thumbnail is independent from extension in general! + if (!hasStandardIconExtension(templateName)) //perf: no need for physical disk access for standard icons #endif - if (iconLoader.getFileIcon) - if (ImageHolder ih = iconLoader.getFileIcon(IconBuffer::getSize(sz))) - return ih; + if (ImageHolder ih = AFS::getFileIcon(itemPath, IconBuffer::getSize(sz))) + return ih; //3. fallbacks if (ImageHolder ih = getIconByTemplatePath(templateName, IconBuffer::getSize(sz))) @@ -120,65 +110,51 @@ ImageHolder getDisplayIcon(const ABF::IconLoader& iconLoader, const Zstring& tem //################################################################################################################################################ //---------------------- Shared Data ------------------------- -struct WorkItem -{ - WorkItem(const AbstractPathRef::ItemId& id, const ABF::IconLoader& iconLoader, const Zstring& fileName) : id_(id), iconLoader_(iconLoader), fileName_(fileName) {} - - AbstractPathRef::ItemId id_; //async icon loading => avoid any dangling references!!! - ABF::IconLoader iconLoader_; //THREAD-SAFETY: thread-safe like an int! - Zstring fileName_; //template name for use as fallback icon -}; - - class WorkLoad { public: //context of worker thread, blocking: - WorkItem extractNextFile() //throw ThreadInterruption + AbstractPath extractNextFile() //throw ThreadInterruption { assert(std::this_thread::get_id() != mainThreadId); std::unique_lock<std::mutex> dummy(lockFiles); interruptibleWait(conditionNewWork, dummy, [this] { return !workLoad.empty(); }); //throw ThreadInterruption - WorkItem workItem = workLoad.back(); // - workLoad.pop_back(); //yes, not std::bad_alloc exception-safe, but bad_alloc is not relevant for us - return workItem; // + AbstractPath filePath = workLoad.back(); // + workLoad.pop_back(); //yes, no strong exception guarantee (std::bad_alloc) + return filePath; // } - void setWorkload(const std::vector<AbstractPathRef>& newLoad) //context of main thread + void setWorkload(const std::vector<AbstractPath>& newLoad) //context of main thread { assert(std::this_thread::get_id() == mainThreadId); { std::lock_guard<std::mutex> dummy(lockFiles); workLoad.clear(); - for (const AbstractPathRef& filePath : newLoad) - workLoad.emplace_back(filePath.getUniqueId(), - ABF::getAsyncIconLoader(filePath), //noexcept! - ABF::getFileShortName(filePath)); // + for (const AbstractPath& filePath : newLoad) + workLoad.emplace_back(filePath); } conditionNewWork.notify_all(); //instead of notify_one(); workaround bug: https://svn.boost.org/trac/boost/ticket/7796 //condition handling, see: http://www.boost.org/doc/libs/1_43_0/doc/html/thread/synchronization.html#thread.synchronization.condvar_ref } - void addToWorkload(const AbstractPathRef& filePath) //context of main thread + void addToWorkload(const AbstractPath& filePath) //context of main thread { assert(std::this_thread::get_id() == mainThreadId); { std::lock_guard<std::mutex> dummy(lockFiles); - - workLoad.emplace_back(filePath.getUniqueId(), //set as next item to retrieve - ABF::getAsyncIconLoader(filePath), //noexcept! - ABF::getFileShortName(filePath)); // + workLoad.emplace_back(filePath); //set as next item to retrieve } conditionNewWork.notify_all(); } private: - std::vector<WorkItem> workLoad; //processes last elements of vector first! - std::mutex lockFiles; - std::condition_variable conditionNewWork; //signal event: data for processing available + //AbstractPath is thread-safe like an int! + std::vector<AbstractPath> workLoad; //processes last elements of vector first! + std::mutex lockFiles; + std::condition_variable conditionNewWork; //signal event: data for processing available }; @@ -188,19 +164,19 @@ public: Buffer() : firstInsertPos(iconList.end()), lastInsertPos(iconList.end()) {} //called by main and worker thread: - bool hasIcon(const AbstractPathRef::ItemId& id) const + bool hasIcon(const AbstractPath& filePath) const { std::lock_guard<std::mutex> dummy(lockIconList); - return iconList.find(id) != iconList.end(); + return iconList.find(filePath) != iconList.end(); } //must be called by main thread only! => wxBitmap is NOT thread-safe like an int (non-atomic ref-count!!!) - Opt<wxBitmap> retrieve(const AbstractPathRef::ItemId& id) + Opt<wxBitmap> retrieve(const AbstractPath& filePath) { assert(std::this_thread::get_id() == mainThreadId); std::lock_guard<std::mutex> dummy(lockIconList); - auto it = iconList.find(id); + auto it = iconList.find(filePath); if (it == iconList.end()) return NoValue(); @@ -216,12 +192,12 @@ public: } //called by main and worker thread: - void insert(const AbstractPathRef::ItemId& id, ImageHolder&& icon) + void insert(const AbstractPath& filePath, ImageHolder&& icon) { std::lock_guard<std::mutex> dummy(lockIconList); //thread safety: moving ImageHolder is free from side effects, but ~wxBitmap() is NOT! => do NOT delete items from iconList here! - auto rc = iconList.emplace(id, makeValueObject()); + auto rc = iconList.emplace(filePath, makeValueObject()); assert(rc.second); //insertion took place if (rc.second) { @@ -249,11 +225,11 @@ private: struct IconData; #ifdef __clang__ //workaround libc++ limitation for incomplete types: http://llvm.org/bugs/show_bug.cgi?id=17701 - typedef std::map<AbstractPathRef::ItemId, std::unique_ptr<IconData>> FileIconMap; + typedef std::map<AbstractPath, std::unique_ptr<IconData>, AFS::LessAbstractPath> FileIconMap; static IconData& refData(FileIconMap::iterator it) { return *(it->second); } static std::unique_ptr<IconData> makeValueObject() { return std::make_unique<IconData>(); } #else - typedef std::map<AbstractPathRef::ItemId, IconData> FileIconMap; + typedef std::map<AbstractPath, IconData, AFS::LessAbstractPath> FileIconMap; IconData& refData(FileIconMap::iterator it) { return it->second; } static IconData makeValueObject() { return IconData(); } #endif @@ -385,6 +361,8 @@ public: void WorkerThread::operator()() const //thread entry { #ifdef ZEN_WIN + setCurrentThreadName("Icon Buffer Worker"); + try { //1. Initialize COM here due to the icon_loader.h dependency only, but NOT due to native.h, mtp.h's internal COM usage => this is not our responsibility! @@ -396,10 +374,10 @@ void WorkerThread::operator()() const //thread entry interruptionPoint(); //throw ThreadInterruption //start work: blocks until next icon to load is retrieved: - const WorkItem workItem = workload_->extractNextFile(); //throw ThreadInterruption + const AbstractPath itemPath = workload_->extractNextFile(); //throw ThreadInterruption - if (!buffer_->hasIcon(workItem.id_)) //perf: workload may contain duplicate entries? - buffer_->insert(workItem.id_, getDisplayIcon(workItem.iconLoader_, workItem.fileName_, iconSizeType)); + if (!buffer_->hasIcon(itemPath)) //perf: workload may contain duplicate entries? + buffer_->insert(itemPath, getDisplayIcon(itemPath, iconSizeType)); } #ifdef ZEN_WIN @@ -412,14 +390,13 @@ void WorkerThread::operator()() const //thread entry struct IconBuffer::Pimpl { - Pimpl() : - workload(std::make_shared<WorkLoad>()), - buffer (std::make_shared<Buffer>()) {} - - std::shared_ptr<WorkLoad> workload; - std::shared_ptr<Buffer> buffer; + std::shared_ptr<WorkLoad> workload = std::make_shared<WorkLoad>(); + std::shared_ptr<Buffer> buffer = std::make_shared<Buffer>(); InterruptibleThread worker; + + //------------------------- + std::map<Zstring, wxBitmap, LessFilePath> extensionIcons; }; @@ -462,28 +439,28 @@ int IconBuffer::getSize(IconSize sz) } -bool IconBuffer::readyForRetrieval(const AbstractPathRef& filePath) +bool IconBuffer::readyForRetrieval(const AbstractPath& filePath) { #ifdef ZEN_WIN if (iconSizeType == IconBuffer::SIZE_SMALL) - if (hasStandardIconExtension(ABF::getFileShortName(filePath))) + if (hasStandardIconExtension(AFS::getFileShortName(filePath))) return true; #endif - return pimpl->buffer->hasIcon(filePath.getUniqueId()); + return pimpl->buffer->hasIcon(filePath); } -Opt<wxBitmap> IconBuffer::retrieveFileIcon(const AbstractPathRef& filePath) +Opt<wxBitmap> IconBuffer::retrieveFileIcon(const AbstractPath& filePath) { #ifdef ZEN_WIN //perf: let's read icons which don't need file access right away! No async delay justified! - const Zstring fileName = ABF::getFileShortName(filePath); + const Zstring fileName = AFS::getFileShortName(filePath); if (iconSizeType == IconBuffer::SIZE_SMALL) //non-thumbnail view, we need file type icons only! if (hasStandardIconExtension(fileName)) return this->getIconByExtension(fileName); //buffered!!! #endif - if (Opt<wxBitmap> ico = pimpl->buffer->retrieve(filePath.getUniqueId())) + if (Opt<wxBitmap> ico = pimpl->buffer->retrieve(filePath)) return ico; //since this icon seems important right now, we don't want to wait until next setWorkload() to start retrieving @@ -493,7 +470,7 @@ Opt<wxBitmap> IconBuffer::retrieveFileIcon(const AbstractPathRef& filePath) } -void IconBuffer::setWorkload(const std::vector<AbstractPathRef>& load) +void IconBuffer::setWorkload(const std::vector<AbstractPath>& load) { assert(load.size() < BUFFER_SIZE_MAX / 2); @@ -504,26 +481,20 @@ void IconBuffer::setWorkload(const std::vector<AbstractPathRef>& load) wxBitmap IconBuffer::getIconByExtension(const Zstring& filePath) { - //comparison of ItemIds is currently case-sensitive: -#if defined ZEN_WIN || defined ZEN_MAC - const Zstring& extension = makeUpperCopy(getFileExtension(filePath)); -#elif defined ZEN_LINUX - const Zstring& extension = getFileExtension(filePath); -#endif - - const AbstractPathRef::ItemId extId(nullptr, extension); - - if (Opt<wxBitmap> ico = pimpl->buffer->retrieve(extId)) - return *ico; + const Zstring& extension = getFileExtension(filePath); - const Zstring& templateName(extension.empty() ? Zstr("file") : Zstr("file.") + extension); - //don't pass actual file name to getIconByTemplatePath(), e.g. "AUTHORS" has own mime type on Linux!!! - //=> we want to buffer by extension only to minimize buffer-misses! - pimpl->buffer->insert(extId, getIconByTemplatePath(templateName, IconBuffer::getSize(iconSizeType))); + assert(std::this_thread::get_id() == mainThreadId); - Opt<wxBitmap> ico = pimpl->buffer->retrieve(extId); - assert(ico); - return *ico; + auto it = pimpl->extensionIcons.find(extension); + if (it == pimpl->extensionIcons.end()) + { + const Zstring& templateName(extension.empty() ? Zstr("file") : Zstr("file.") + extension); + //don't pass actual file name to getIconByTemplatePath(), e.g. "AUTHORS" has own mime type on Linux!!! + //=> we want to buffer by extension only to minimize buffer-misses! + it = pimpl->extensionIcons.emplace(extension, extractWxBitmap(getIconByTemplatePath(templateName, IconBuffer::getSize(iconSizeType)))).first; + } + //need buffer size limit??? + return it->second; } diff --git a/FreeFileSync/Source/lib/icon_buffer.h b/FreeFileSync/Source/lib/icon_buffer.h index f149df2a..c3354808 100644 --- a/FreeFileSync/Source/lib/icon_buffer.h +++ b/FreeFileSync/Source/lib/icon_buffer.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef ICONBUFFER_H_INCLUDED_8425703245726394256 -#define ICONBUFFER_H_INCLUDED_8425703245726394256 +#ifndef ICON_BUFFER_H_8425703245726394256 +#define ICON_BUFFER_H_8425703245726394256 #include <vector> #include <memory> @@ -33,9 +33,9 @@ public: static int getSize(IconSize sz); //expected and *maximum* icon size in pixel int getSize() const { return getSize(iconSizeType); } // - bool readyForRetrieval(const AbstractPathRef& filePath); - Opt<wxBitmap> retrieveFileIcon (const AbstractPathRef& filePath); //... and mark as hot - void setWorkload (const std::vector<AbstractPathRef>& load); //(re-)set new workload of icons to be retrieved; + bool readyForRetrieval(const AbstractPath& filePath); + Opt<wxBitmap> retrieveFileIcon (const AbstractPath& filePath); //... and mark as hot + void setWorkload (const std::vector<AbstractPath>& load); //(re-)set new workload of icons to be retrieved; wxBitmap getIconByExtension(const Zstring& filePath); //...and add to buffer @@ -53,4 +53,4 @@ private: bool hasLinkExtension(const Zstring& filepath); } -#endif //ICONBUFFER_H_INCLUDED_8425703245726394256 +#endif //ICON_BUFFER_H_8425703245726394256 diff --git a/FreeFileSync/Source/lib/icon_holder.h b/FreeFileSync/Source/lib/icon_holder.h index b26deb1e..9bff11de 100644 --- a/FreeFileSync/Source/lib/icon_holder.h +++ b/FreeFileSync/Source/lib/icon_holder.h @@ -4,12 +4,12 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef IMAGE_HOLDER_284578426342567457 -#define IMAGE_HOLDER_284578426342567457 +#ifndef IMAGE_HOLDER_H_284578426342567457 +#define IMAGE_HOLDER_H_284578426342567457 #include <memory> -//used by fs_abstract.h => check carefully before adding dependencies! +//used by fs/abstract.h => check carefully before adding dependencies! namespace zen { @@ -48,4 +48,4 @@ private: }; } -#endif //IMAGE_HOLDER_284578426342567457
\ No newline at end of file +#endif //IMAGE_HOLDER_H_284578426342567457 diff --git a/FreeFileSync/Source/lib/icon_loader.h b/FreeFileSync/Source/lib/icon_loader.h index 6fe424c0..e37446c8 100644 --- a/FreeFileSync/Source/lib/icon_loader.h +++ b/FreeFileSync/Source/lib/icon_loader.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef ICON_LOADER_1348701985713445 -#define ICON_LOADER_1348701985713445 +#ifndef ICON_LOADER_H_1348701985713445 +#define ICON_LOADER_H_1348701985713445 #include <zen/zstring.h> #include "icon_holder.h" @@ -24,4 +24,4 @@ ImageHolder getFileIcon(const Zstring& filePath, int pixelSize); ImageHolder getThumbnailImage(const Zstring& filePath, int pixelSize); } -#endif //ICON_LOADER_1348701985713445 +#endif //ICON_LOADER_H_1348701985713445 diff --git a/FreeFileSync/Source/lib/localization.cpp b/FreeFileSync/Source/lib/localization.cpp index 040c26d2..12e50757 100644 --- a/FreeFileSync/Source/lib/localization.cpp +++ b/FreeFileSync/Source/lib/localization.cpp @@ -24,6 +24,7 @@ #include <wchar.h> //wcscasecmp #elif defined ZEN_MAC + #include <zen/osx_string.h> #include <CoreServices/CoreServices.h> #endif @@ -39,7 +40,7 @@ public: wxLanguage langId() const { return langId_; } - std::wstring translate(const std::wstring& text) override + std::wstring translate(const std::wstring& text) const override { //look for translation in buffer table auto it = transMapping.find(text); @@ -48,7 +49,7 @@ public: return text; //fallback } - std::wstring translate(const std::wstring& singular, const std::wstring& plural, std::int64_t n) override + std::wstring translate(const std::wstring& singular, const std::wstring& plural, std::int64_t n) const override { auto it = transMappingPl.find(std::make_pair(singular, plural)); if (it != transMappingPl.end()) @@ -131,22 +132,15 @@ struct LessTranslation #elif defined ZEN_LINUX return ::wcscasecmp(lhs.languageName.c_str(), rhs.languageName.c_str()) < 0; //ignores case; locale-dependent! - //return lhs.languageName.CmpNoCase(rhs.languageName) < 0; #elif defined ZEN_MAC - auto allocCFStringRef = [](const std::wstring& str) -> CFStringRef //output not owned! - { - return ::CFStringCreateWithCString(nullptr, //CFAllocatorRef alloc, - utfCvrtTo<std::string>(str).c_str(), //const char *cStr, - kCFStringEncodingUTF8); //CFStringEncoding encoding - }; - - CFStringRef langL = allocCFStringRef(lhs.languageName); + CFStringRef langL = osx::createCFString(utfCvrtTo<std::string>(lhs.languageName).c_str()); ZEN_ON_SCOPE_EXIT(::CFRelease(langL)); - CFStringRef langR = allocCFStringRef(rhs.languageName); + CFStringRef langR = osx::createCFString(utfCvrtTo<std::string>(rhs.languageName).c_str()); ZEN_ON_SCOPE_EXIT(::CFRelease(langR)); + //correctly position "czech" unlike wcscasecmp(): return::CFStringCompare(langL, langR, kCFCompareLocalized | kCFCompareCaseInsensitive) == kCFCompareLessThan; //no-fail #endif } diff --git a/FreeFileSync/Source/lib/norm_filter.h b/FreeFileSync/Source/lib/norm_filter.h index 3f2cd161..5b27464b 100644 --- a/FreeFileSync/Source/lib/norm_filter.h +++ b/FreeFileSync/Source/lib/norm_filter.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef NORM_FILTER_H_INCLUDED -#define NORM_FILTER_H_INCLUDED +#ifndef NORM_FILTER_H_974896787346251 +#define NORM_FILTER_H_974896787346251 #include "hard_filter.h" #include "soft_filter.h" @@ -66,4 +66,4 @@ NormalizedFilter normalizeFilters(const FilterConfig& global, const FilterConfig } } -#endif //NORM_FILTER_H_INCLUDED +#endif //NORM_FILTER_H_974896787346251 diff --git a/FreeFileSync/Source/lib/parallel_scan.cpp b/FreeFileSync/Source/lib/parallel_scan.cpp index e06c9dd6..2af42074 100644 --- a/FreeFileSync/Source/lib/parallel_scan.cpp +++ b/FreeFileSync/Source/lib/parallel_scan.cpp @@ -273,21 +273,21 @@ struct TraverserConfig { public: TraverserConfig(int threadID, - const ABF& abf, + const AbstractPath& baseFolderPath, const HardFilter::FilterRef& filter, SymLinkHandling handleSymlinks, - std::map<Zstring, std::wstring, LessFilePath>& failedDirReads, + std::map<Zstring, std::wstring, LessFilePath>& failedFolderReads, std::map<Zstring, std::wstring, LessFilePath>& failedItemReads, AsyncCallback& acb) : - baseFolder(abf), + baseFolderPath_(baseFolderPath), filter_(filter), handleSymlinks_(handleSymlinks), - failedDirReads_ (failedDirReads), + failedDirReads_ (failedFolderReads), failedItemReads_(failedItemReads), acb_(acb), threadID_(threadID) {} - const ABF& baseFolder; + const AbstractPath baseFolderPath_; const HardFilter::FilterRef filter_; //always bound! const SymLinkHandling handleSymlinks_; @@ -300,15 +300,15 @@ public: }; -class DirCallback : public ABF::TraverserCallback +class DirCallback : public AFS::TraverserCallback { public: DirCallback(TraverserConfig& config, - const Zstring& relNameParentPf, //postfixed with FILE_NAME_SEPARATOR! - DirContainer& output, + const Zstring& parentRelPathPf, //postfixed with FILE_NAME_SEPARATOR! + FolderContainer& output, int level) : cfg(config), - relNameParentPf_(relNameParentPf), + parentRelPathPf_(parentRelPathPf), output_(output), level_(level) {} @@ -321,8 +321,8 @@ public: private: TraverserConfig& cfg; - const Zstring relNameParentPf_; - DirContainer& output_; + const Zstring parentRelPathPf_; + FolderContainer& output_; const int level_; }; @@ -336,15 +336,15 @@ void DirCallback::onFile(const FileInfo& fi) //throw ThreadInterruption endsWith(fi.itemName, LOCK_FILE_ENDING)) return; - const Zstring relFilePath = relNameParentPf_ + fi.itemName; + const Zstring fileRelPath = parentRelPathPf_ + fi.itemName; //update status information no matter whether item is excluded or not! if (cfg.acb_.mayReportCurrentFile(cfg.threadID_, cfg.lastReportTime)) - cfg.acb_.reportCurrentFile(ABF::getDisplayPath(cfg.baseFolder.getAbstractPath(relFilePath))); + cfg.acb_.reportCurrentFile(AFS::getDisplayPath(AFS::appendRelPath(cfg.baseFolderPath_, fileRelPath))); //------------------------------------------------------------------------------------ //apply filter before processing (use relative name!) - if (!cfg.filter_->passFileFilter(relFilePath)) + if (!cfg.filter_->passFileFilter(fileRelPath)) return; // std::string fileId = details.fileSize >= 1024 * 1024U ? util::retrieveFileID(filepath) : std::string(); @@ -364,37 +364,37 @@ void DirCallback::onFile(const FileInfo& fi) //throw ThreadInterruption } -std::unique_ptr<ABF::TraverserCallback> DirCallback::onDir(const DirInfo& di) //throw ThreadInterruption +std::unique_ptr<AFS::TraverserCallback> DirCallback::onDir(const DirInfo& di) //throw ThreadInterruption { interruptionPoint(); //throw ThreadInterruption - const Zstring& relDirPath = relNameParentPf_ + di.itemName; + const Zstring& folderRelPath = parentRelPathPf_ + di.itemName; //update status information no matter whether item is excluded or not! if (cfg.acb_.mayReportCurrentFile(cfg.threadID_, cfg.lastReportTime)) - cfg.acb_.reportCurrentFile(ABF::getDisplayPath(cfg.baseFolder.getAbstractPath(relDirPath))); + cfg.acb_.reportCurrentFile(AFS::getDisplayPath(AFS::appendRelPath(cfg.baseFolderPath_, folderRelPath))); //------------------------------------------------------------------------------------ //apply filter before processing (use relative name!) bool childItemMightMatch = true; - const bool passFilter = cfg.filter_->passDirFilter(relDirPath, &childItemMightMatch); + const bool passFilter = cfg.filter_->passDirFilter(folderRelPath, &childItemMightMatch); if (!passFilter && !childItemMightMatch) return nullptr; //do NOT traverse subdirs //else: attention! ensure directory filtering is applied later to exclude actually filtered directories - DirContainer& subDir = output_.addSubDir(di.itemName); + FolderContainer& subFolder = output_.addSubFolder(di.itemName); if (passFilter) cfg.acb_.incItemsScanned(); //add 1 element to the progress indicator //------------------------------------------------------------------------------------ if (level_ > 100) //Win32 traverser: stack overflow approximately at level 1000 - if (!tryReportingItemError([&] //check after DirContainer::addSubDir() + if (!tryReportingItemError([&] //check after FolderContainer::addSubFolder() { - throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", ABF::getDisplayPath(cfg.baseFolder.getAbstractPath(relDirPath))), L"Endless recursion."); + throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", AFS::getDisplayPath(AFS::appendRelPath(cfg.baseFolderPath_, folderRelPath))), L"Endless recursion."); }, *this, di.itemName)) return nullptr; - return std::make_unique<DirCallback>(cfg, relDirPath + FILE_NAME_SEPARATOR, subDir, level_ + 1); //releaseDirTraverser() is guaranteed to be called in any case + return std::make_unique<DirCallback>(cfg, folderRelPath + FILE_NAME_SEPARATOR, subFolder, level_ + 1); //releaseDirTraverser() is guaranteed to be called in any case } @@ -402,11 +402,11 @@ DirCallback::HandleLink DirCallback::onSymlink(const SymlinkInfo& si) //throw Th { interruptionPoint(); //throw ThreadInterruption - const Zstring& relLinkPath = relNameParentPf_ + si.itemName; + const Zstring& linkRelPath = parentRelPathPf_ + si.itemName; //update status information no matter whether item is excluded or not! if (cfg.acb_.mayReportCurrentFile(cfg.threadID_, cfg.lastReportTime)) - cfg.acb_.reportCurrentFile(ABF::getDisplayPath(cfg.baseFolder.getAbstractPath(relLinkPath))); + cfg.acb_.reportCurrentFile(AFS::getDisplayPath(AFS::appendRelPath(cfg.baseFolderPath_, linkRelPath))); switch (cfg.handleSymlinks_) { @@ -414,7 +414,7 @@ DirCallback::HandleLink DirCallback::onSymlink(const SymlinkInfo& si) //throw Th return LINK_SKIP; case SYMLINK_DIRECT: - if (cfg.filter_->passFileFilter(relLinkPath)) //always use file filter: Link type may not be "stable" on Linux! + if (cfg.filter_->passFileFilter(linkRelPath)) //always use file filter: Link type may not be "stable" on Linux! { output_.addSubLink(si.itemName, LinkDescriptor(si.lastWriteTime)); cfg.acb_.incItemsScanned(); //add 1 element to the progress indicator @@ -424,10 +424,10 @@ DirCallback::HandleLink DirCallback::onSymlink(const SymlinkInfo& si) //throw Th case SYMLINK_FOLLOW: //filter symlinks before trying to follow them: handle user-excluded broken symlinks! //since we don't know yet what type the symlink will resolve to, only do this when both variants agree: - if (!cfg.filter_->passFileFilter(relLinkPath)) + if (!cfg.filter_->passFileFilter(linkRelPath)) { bool childItemMightMatch = true; - if (!cfg.filter_->passDirFilter(relLinkPath, &childItemMightMatch)) + if (!cfg.filter_->passDirFilter(linkRelPath, &childItemMightMatch)) if (!childItemMightMatch) return LINK_SKIP; } @@ -444,7 +444,7 @@ DirCallback::HandleError DirCallback::reportDirError(const std::wstring& msg, si switch (cfg.acb_.reportError(msg, retryNumber)) //throw ThreadInterruption { case FillBufferCallback::ON_ERROR_IGNORE: - cfg.failedDirReads_[beforeLast(relNameParentPf_, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)] = msg; + cfg.failedDirReads_[beforeLast(parentRelPathPf_, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)] = msg; return ON_ERROR_IGNORE; case FillBufferCallback::ON_ERROR_RETRY: @@ -460,7 +460,7 @@ DirCallback::HandleError DirCallback::reportItemError(const std::wstring& msg, s switch (cfg.acb_.reportError(msg, retryNumber)) //throw ThreadInterruption { case FillBufferCallback::ON_ERROR_IGNORE: - cfg.failedItemReads_[relNameParentPf_ + itemName] = msg; + cfg.failedItemReads_[parentRelPathPf_ + itemName] = msg; return ON_ERROR_IGNORE; case FillBufferCallback::ON_ERROR_RETRY: @@ -477,40 +477,40 @@ class WorkerThread public: WorkerThread(int threadID, const std::shared_ptr<AsyncCallback>& acb, - std::unique_ptr<ABF>&& baseFolder, //always bound! + const AbstractPath& baseFolderPath, //always bound! const HardFilter::FilterRef& filter, // SymLinkHandling handleSymlinks, DirectoryValue& dirOutput) : acb_(acb), - baseFolder_(std::move(baseFolder)), - outputContainer(dirOutput.dirCont), + outputContainer(dirOutput.folderCont), travCfg(threadID, - *baseFolder_, + baseFolderPath, filter, handleSymlinks, //shared by all(!) instances of DirCallback while traversing a folder hierarchy - dirOutput.failedDirReads, + dirOutput.failedFolderReads, dirOutput.failedItemReads, *acb_) {} void operator()() //thread entry { +#ifdef ZEN_WIN + setCurrentThreadName("Folder Traverser"); +#endif + acb_->incActiveWorker(); ZEN_ON_SCOPE_EXIT(acb_->decActiveWorker()); - const AbstractPathRef& baseFolderPath = baseFolder_->getAbstractPath(); - if (acb_->mayReportCurrentFile(travCfg.threadID_, travCfg.lastReportTime)) - acb_->reportCurrentFile(ABF::getDisplayPath(baseFolderPath)); //just in case first directory access is blocking + acb_->reportCurrentFile(AFS::getDisplayPath(travCfg.baseFolderPath_)); //just in case first directory access is blocking DirCallback cb(travCfg, Zstring(), outputContainer, 0); - ABF::traverseFolder(baseFolderPath, cb); //throw X + AFS::traverseFolder(travCfg.baseFolderPath_, cb); //throw X } private: std::shared_ptr<AsyncCallback> acb_; - std::unique_ptr<ABF> baseFolder_; //always bound! - DirContainer& outputContainer; + FolderContainer& outputContainer; TraverserConfig travCfg; }; } @@ -525,14 +525,13 @@ void zen::fillBuffer(const std::set<DirectoryKey>& keysToRead, //in FixedList<InterruptibleThread> worker; - zen::ScopeGuard guardWorker = zen::makeGuard([&] - { + ZEN_ON_SCOPE_FAIL( for (InterruptibleThread& wt : worker) - wt.interrupt(); //interrupt all at once first, then join + wt.interrupt(); //interrupt all at once first, then join for (InterruptibleThread& wt : worker) if (wt.joinable()) //= precondition of thread::join(), which throws an exception if violated! wt.join(); //in this context it is possible a thread is *not* joinable anymore due to the thread::try_join_for() below! - }); + ); auto acb = std::make_shared<AsyncCallback>(updateIntervalMs / 2 /*reportingIntervalMs*/); @@ -545,7 +544,7 @@ void zen::fillBuffer(const std::set<DirectoryKey>& keysToRead, //in const int threadId = static_cast<int>(worker.size()); worker.emplace_back(WorkerThread(threadId, acb, - key.baseFolder_->createIndependentCopy(), //copy instance for safe access on any method by a different thread! + key.folderPath_, //AbstractPath is thread-safe like an int! :) key.filter_, key.handleSymlinks_, dirOutput)); @@ -566,6 +565,4 @@ void zen::fillBuffer(const std::set<DirectoryKey>& keysToRead, //in acb->incrementNotifyingThreadId(); //process info messages of one thread at a time only } - - guardWorker.dismiss(); } diff --git a/FreeFileSync/Source/lib/parallel_scan.h b/FreeFileSync/Source/lib/parallel_scan.h index f75fab99..fdf962b9 100644 --- a/FreeFileSync/Source/lib/parallel_scan.h +++ b/FreeFileSync/Source/lib/parallel_scan.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef PARALLEL_SCAN_H_INCLUDED -#define PARALLEL_SCAN_H_INCLUDED +#ifndef PARALLEL_SCAN_H_924588904275284572857 +#define PARALLEL_SCAN_H_924588904275284572857 #include <map> #include <set> @@ -18,27 +18,28 @@ namespace zen { struct DirectoryKey { - DirectoryKey(const ABF& baseFolder, + DirectoryKey(const AbstractPath& folderPath, const HardFilter::FilterRef& filter, SymLinkHandling handleSymlinks) : - baseFolder_(&baseFolder), + folderPath_(folderPath), filter_(filter), handleSymlinks_(handleSymlinks) {} - const ABF* baseFolder_; //always bound! + const AbstractPath folderPath_; //always bound! HardFilter::FilterRef filter_; //filter interface: always bound by design! SymLinkHandling handleSymlinks_; }; + inline bool operator<(const DirectoryKey& lhs, const DirectoryKey& rhs) { if (lhs.handleSymlinks_ != rhs.handleSymlinks_) return lhs.handleSymlinks_ < rhs.handleSymlinks_; - if (ABF::LessItemPath()(lhs.baseFolder_, rhs.baseFolder_)) + if (AFS::LessAbstractPath()(lhs.folderPath_, rhs.folderPath_)) return true; - if (ABF::LessItemPath()(rhs.baseFolder_, lhs.baseFolder_)) + if (AFS::LessAbstractPath()(rhs.folderPath_, lhs.folderPath_)) return false; return *lhs.filter_ < *rhs.filter_; @@ -47,18 +48,17 @@ bool operator<(const DirectoryKey& lhs, const DirectoryKey& rhs) struct DirectoryValue { - DirContainer dirCont; + FolderContainer folderCont; //relative names (or empty string for root) for directories that could not be read (completely), e.g. access denied, or temporal network drop - std::map<Zstring, std::wstring, LessFilePath> failedDirReads; //with corresponding error message + std::map<Zstring, std::wstring, LessFilePath> failedFolderReads; //with corresponding error message //relative names (never empty) for failure to read single file/dir/symlink with corresponding error message std::map<Zstring, std::wstring, LessFilePath> failedItemReads; }; -class FillBufferCallback +struct FillBufferCallback { -public: virtual ~FillBufferCallback() {} enum HandleError @@ -78,4 +78,4 @@ void fillBuffer(const std::set<DirectoryKey>& keysToRead, //in size_t updateIntervalMs); //unit: [ms] } -#endif // PARALLEL_SCAN_H_INCLUDED +#endif //PARALLEL_SCAN_H_924588904275284572857 diff --git a/FreeFileSync/Source/lib/parse_lng.h b/FreeFileSync/Source/lib/parse_lng.h index a54d4683..cb7866ef 100644 --- a/FreeFileSync/Source/lib/parse_lng.h +++ b/FreeFileSync/Source/lib/parse_lng.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef PARSE_LNG_HEADER_INCLUDED -#define PARSE_LNG_HEADER_INCLUDED +#ifndef PARSE_LNG_H_46794693622675638 +#define PARSE_LNG_H_46794693622675638 #include <algorithm> #include <cctype> @@ -717,4 +717,4 @@ std::string generateLng(const TranslationUnorderedList& in, const TransHeader& h } } -#endif //PARSE_LNG_HEADER_INCLUDED +#endif //PARSE_LNG_H_46794693622675638 diff --git a/FreeFileSync/Source/lib/parse_plural.h b/FreeFileSync/Source/lib/parse_plural.h index a54eecea..e7e1e066 100644 --- a/FreeFileSync/Source/lib/parse_plural.h +++ b/FreeFileSync/Source/lib/parse_plural.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef PARSE_PLURAL_H_INCLUDED -#define PARSE_PLURAL_H_INCLUDED +#ifndef PARSE_PLURAL_H_180465845670839576 +#define PARSE_PLURAL_H_180465845670839576 #include <memory> #include <cstdint> @@ -20,8 +20,7 @@ struct Expression { virtual ~Expression() {} }; template <class T> struct Expr : public Expression { - typedef T ValueType; - virtual ValueType eval() const = 0; + virtual T eval() const = 0; }; @@ -35,7 +34,7 @@ public: private: std::shared_ptr<Expr<std::int64_t>> expr; - mutable std::int64_t n_; + mutable std::int64_t n_ = 0; }; @@ -54,9 +53,8 @@ public: private: struct FormInfo { - FormInfo() : count(0), firstNumber(0) {} - int count; - int firstNumber; //which maps to the plural form index position + int count = 0; + int firstNumber = 0; //which maps to the plural form index position }; std::vector<FormInfo> forms; }; @@ -114,31 +112,33 @@ pm-expression: namespace implementation { -//specific binary expression based on STL function objects -template <class StlOp> -struct BinaryExp : public Expr<typename StlOp::result_type> +template <class BinaryOp, class ParamType, class ResultType> +struct BinaryExp : public Expr<ResultType> { - typedef std::shared_ptr<Expr<typename StlOp::first_argument_type >> ExpLhs; - typedef std::shared_ptr<Expr<typename StlOp::second_argument_type>> ExpRhs; + using ExpLhs = std::shared_ptr<Expr<ParamType>>; + using ExpRhs = std::shared_ptr<Expr<ParamType>>; - BinaryExp(const ExpLhs& lhs, const ExpRhs& rhs, StlOp biop) : lhs_(lhs), rhs_(rhs), biop_(biop) { assert(lhs && rhs); } - typename StlOp::result_type eval() const override { return biop_(lhs_->eval(), rhs_->eval()); } + BinaryExp(const ExpLhs& lhs, const ExpRhs& rhs) : lhs_(lhs), rhs_(rhs) { assert(lhs && rhs); } + ResultType eval() const override { return BinaryOp()(lhs_->eval(), rhs_->eval()); } private: ExpLhs lhs_; ExpRhs rhs_; - StlOp biop_; }; -template <class StlOp> inline -std::shared_ptr<BinaryExp<StlOp>> makeBiExp(const std::shared_ptr<Expression>& lhs, const std::shared_ptr<Expression>& rhs, StlOp biop) //throw ParsingError + +template <class BinaryOp, class ParamType> inline +std::shared_ptr<Expression> makeBiExp(const std::shared_ptr<Expression>& lhs, const std::shared_ptr<Expression>& rhs) //throw ParsingError { - auto exLeft = std::dynamic_pointer_cast<Expr<typename StlOp::first_argument_type >>(lhs); - auto exRight = std::dynamic_pointer_cast<Expr<typename StlOp::second_argument_type>>(rhs); + auto exLeft = std::dynamic_pointer_cast<Expr<ParamType>>(lhs); + auto exRight = std::dynamic_pointer_cast<Expr<ParamType>>(rhs); if (!exLeft || !exRight) throw ParsingError(); - return std::make_shared<BinaryExp<StlOp>>(exLeft, exRight, biop); + + using ResultType = decltype(BinaryOp()(std::declval<ParamType>(), std::declval<ParamType>())); + return std::make_shared<BinaryExp<BinaryOp, ParamType, ResultType>>(exLeft, exRight); } + template <class T> struct ConditionalExp : public Expr<T> { @@ -146,13 +146,14 @@ struct ConditionalExp : public Expr<T> const std::shared_ptr<Expr<T>>& thenExp, const std::shared_ptr<Expr<T>>& elseExp) : ifExp_(ifExp), thenExp_(thenExp), elseExp_(elseExp) { assert(ifExp && thenExp && elseExp); } - typename Expr<T>::ValueType eval() const override { return ifExp_->eval() ? thenExp_->eval() : elseExp_->eval(); } + T eval() const override { return ifExp_->eval() ? thenExp_->eval() : elseExp_->eval(); } private: std::shared_ptr<Expr<bool>> ifExp_; std::shared_ptr<Expr<T>> thenExp_; std::shared_ptr<Expr<T>> elseExp_; }; + struct ConstNumberExp : public Expr<std::int64_t> { ConstNumberExp(std::int64_t n) : n_(n) {} @@ -161,6 +162,7 @@ private: std::int64_t n_; }; + struct VariableNumberNExp : public Expr<std::int64_t> { VariableNumberNExp(std::int64_t& n) : n_(n) {} @@ -193,11 +195,11 @@ struct Token TK_END }; - Token(Type t) : type(t), number(0) {} - Token(std::int64_t num) : type(TK_CONST_NUMBER), number(num) {} + Token(Type t) : type(t) {} + Token(std::int64_t num) : number(num) {} - Type type; - std::int64_t number; //if type == TK_CONST_NUMBER + Type type = TK_CONST_NUMBER; + std::int64_t number = 0; //if type == TK_CONST_NUMBER }; class Scanner @@ -230,11 +232,11 @@ public: if (pos == stream_.end()) return Token::TK_END; - for (auto iter = tokens.begin(); iter != tokens.end(); ++iter) - if (startsWith(iter->first)) + for (const auto& item : tokens) + if (startsWith(item.first)) { - pos += iter->first.size(); - return Token(iter->second); + pos += item.first.size(); + return Token(item.second); } auto digitEnd = std::find_if(pos, stream_.end(), [](char c) { return !zen::isDigit(c); }); @@ -316,7 +318,7 @@ private: nextToken(); std::shared_ptr<Expression> rhs = parseLogicalAnd(); - e = makeBiExp(e, rhs, std::logical_or<bool>()); //throw ParsingError + e = makeBiExp<std::logical_or<>, bool>(e, rhs); //throw ParsingError } return e; } @@ -329,7 +331,7 @@ private: nextToken(); std::shared_ptr<Expression> rhs = parseEquality(); - e = makeBiExp(e, rhs, std::logical_and<bool>()); //throw ParsingError + e = makeBiExp<std::logical_and<>, bool>(e, rhs); //throw ParsingError } return e; } @@ -345,8 +347,8 @@ private: nextToken(); std::shared_ptr<Expression> rhs = parseRelational(); - if (t == Token::TK_EQUAL) return makeBiExp(e, rhs, std::equal_to <std::int64_t>()); //throw ParsingError - if (t == Token::TK_NOT_EQUAL) return makeBiExp(e, rhs, std::not_equal_to<std::int64_t>()); // + if (t == Token::TK_EQUAL) return makeBiExp<std::equal_to<>, std::int64_t>(e, rhs); //throw ParsingError + if (t == Token::TK_NOT_EQUAL) return makeBiExp<std::not_equal_to<>, std::int64_t>(e, rhs); // } return e; } @@ -364,10 +366,10 @@ private: nextToken(); std::shared_ptr<Expression> rhs = parseMultiplicative(); - if (t == Token::TK_LESS) return makeBiExp(e, rhs, std::less <std::int64_t>()); // - if (t == Token::TK_LESS_EQUAL) return makeBiExp(e, rhs, std::less_equal <std::int64_t>()); //throw ParsingError - if (t == Token::TK_GREATER) return makeBiExp(e, rhs, std::greater <std::int64_t>()); // - if (t == Token::TK_GREATER_EQUAL) return makeBiExp(e, rhs, std::greater_equal<std::int64_t>()); // + if (t == Token::TK_LESS) return makeBiExp<std::less <>, std::int64_t>(e, rhs); // + if (t == Token::TK_LESS_EQUAL) return makeBiExp<std::less_equal <>, std::int64_t>(e, rhs); //throw ParsingError + if (t == Token::TK_GREATER) return makeBiExp<std::greater <>, std::int64_t>(e, rhs); // + if (t == Token::TK_GREATER_EQUAL) return makeBiExp<std::greater_equal<>, std::int64_t>(e, rhs); // } return e; } @@ -386,7 +388,7 @@ private: if (literal->eval() == 0) throw ParsingError(); - e = makeBiExp(e, rhs, std::modulus<std::int64_t>()); //throw ParsingError + e = makeBiExp<std::modulus<>, std::int64_t>(e, rhs); //throw ParsingError } return e; } @@ -475,4 +477,4 @@ inline PluralForm::PluralForm(const std::string& stream) : expr(implementation::Parser(stream, n_).parse()) {} //throw ParsingError } -#endif // PARSE_PLURAL_H_INCLUDED +#endif //PARSE_PLURAL_H_180465845670839576 diff --git a/FreeFileSync/Source/lib/perf_check.cpp b/FreeFileSync/Source/lib/perf_check.cpp index ed34b7ef..98e3b25d 100644 --- a/FreeFileSync/Source/lib/perf_check.cpp +++ b/FreeFileSync/Source/lib/perf_check.cpp @@ -78,13 +78,13 @@ zen::Opt<double> PerfCheck::getRemainingTimeSec(double dataRemaining) const const auto& itemBack = *blk.second; //----------------------------------------------------------------------------------------------- const std::int64_t timeDeltaMs = itemBack.first - itemFront.first; - const double dataDelta = itemBack.second.data_ - itemFront.second.data_; + const double bytesDelta = itemBack.second.bytes_ - itemFront.second.bytes_; //objects model logical operations *NOT* disk accesses, so we better play safe and use "bytes" only! //http://sourceforge.net/p/freefilesync/feature-requests/197/ - if (!numeric::isNull(dataDelta)) //sign(dataRemaining) != sign(dataDelta) usually an error, so show it! - return dataRemaining * timeDeltaMs / 1000.0 / dataDelta; + if (!numeric::isNull(bytesDelta)) //sign(dataRemaining) != sign(bytesDelta) usually an error, so show it! + return dataRemaining * timeDeltaMs / 1000.0 / bytesDelta; } return NoValue(); } @@ -99,10 +99,10 @@ zen::Opt<std::wstring> PerfCheck::getBytesPerSecond() const const auto& itemBack = *blk.second; //----------------------------------------------------------------------------------------------- const std::int64_t timeDeltaMs = itemBack.first - itemFront.first; - const double dataDelta = itemBack.second.data_ - itemFront.second.data_; + const double bytesDelta = itemBack.second.bytes_ - itemFront.second.bytes_; if (timeDeltaMs != 0) - return filesizeToShortString(static_cast<std::int64_t>(dataDelta * 1000.0 / timeDeltaMs)) + _("/sec"); + return filesizeToShortString(static_cast<std::int64_t>(bytesDelta * 1000.0 / timeDeltaMs)) + _("/sec"); } return NoValue(); } @@ -117,10 +117,10 @@ zen::Opt<std::wstring> PerfCheck::getItemsPerSecond() const const auto& itemBack = *blk.second; //----------------------------------------------------------------------------------------------- const int64_t timeDeltaMs = itemBack.first - itemFront.first; - const int itemsDelta = itemBack.second.itemCount_ - itemFront.second.itemCount_; + const int itemsDelta = itemBack.second.items_ - itemFront.second.items_; if (timeDeltaMs != 0) - return replaceCpy(_("%x items/sec"), L"%x", formatThreeDigitPrecision(itemsDelta * 1000.0 / timeDeltaMs)); + return replaceCpy(_("%x items/sec"), L"%x", formatTwoDigitPrecision(itemsDelta * 1000.0 / timeDeltaMs)); } return NoValue(); } diff --git a/FreeFileSync/Source/lib/perf_check.h b/FreeFileSync/Source/lib/perf_check.h index 16376f97..ad602e4f 100644 --- a/FreeFileSync/Source/lib/perf_check.h +++ b/FreeFileSync/Source/lib/perf_check.h @@ -4,14 +4,15 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef STATISTICS_H_INCLUDED -#define STATISTICS_H_INCLUDED +#ifndef PERF_CHECK_H_87804217589312454 +#define PERF_CHECK_H_87804217589312454 #include <cstdint> #include <map> #include <string> #include <zen/optional.h> + class PerfCheck { public: @@ -28,9 +29,9 @@ public: private: struct Record { - Record(int itemCount, double data) : itemCount_(itemCount), data_(data) {} - int itemCount_; - double data_; //unit: [bytes] + Record(int items, double bytes) : items_(items), bytes_(bytes) {} + const int items_; + const double bytes_; }; std::pair<const std::multimap<int64_t, Record>::value_type*, @@ -43,4 +44,4 @@ private: std::map<int64_t, Record> samples; //time, unit: [ms] }; -#endif // STATISTICS_H_INCLUDED +#endif //PERF_CHECK_H_87804217589312454 diff --git a/FreeFileSync/Source/lib/process_xml.h b/FreeFileSync/Source/lib/process_xml.h index e309978a..9d765130 100644 --- a/FreeFileSync/Source/lib/process_xml.h +++ b/FreeFileSync/Source/lib/process_xml.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef PROCESSXML_H_INCLUDED_28345825704254262435 -#define PROCESSXML_H_INCLUDED_28345825704254262435 +#ifndef PROCESS_XML_H_28345825704254262435 +#define PROCESS_XML_H_28345825704254262435 #include <zen/xml_io.h> #include <wx/gdicmn.h> @@ -14,6 +14,7 @@ #include "../ui/column_attr.h" #include "../ui/folder_history_types.h" + namespace xmlAccess { enum XmlType @@ -166,8 +167,8 @@ struct XmlGlobalSettings #endif } - wxPoint dlgPos { wxDefaultCoord, wxDefaultCoord }; - wxSize dlgSize { wxDefaultCoord, wxDefaultCoord }; + wxPoint dlgPos; + wxSize dlgSize; bool isMaximized = false; int sashOffset = 0; @@ -224,11 +225,8 @@ struct XmlGlobalSettings bool manualDeletionUseRecycler = true; -#if defined ZEN_WIN || defined ZEN_MAC - bool textSearchRespectCase = false; -#elif defined ZEN_LINUX - bool textSearchRespectCase = true; -#endif + bool textSearchRespectCase = false; //good default for Linux, too! + bool showIcons = true; FileIconSize iconSize = ICON_SIZE_SMALL; @@ -262,4 +260,4 @@ XmlBatchConfig convertGuiToBatch(const XmlGuiConfig& guiCfg, const XmlBatchCon std::wstring extractJobName(const Zstring& configFilename); } -#endif //PROCESSXML_H_INCLUDED_28345825704254262435 +#endif //PROCESS_XML_H_28345825704254262435 diff --git a/FreeFileSync/Source/lib/resolve_path.cpp b/FreeFileSync/Source/lib/resolve_path.cpp index f14078db..7cad395a 100644 --- a/FreeFileSync/Source/lib/resolve_path.cpp +++ b/FreeFileSync/Source/lib/resolve_path.cpp @@ -6,7 +6,6 @@ #include <zen/utf.h> #include <zen/optional.h> #include <zen/scope_guard.h> -#include <wx/utils.h> //wxGetEnv #ifdef ZEN_WIN #include <zen/long_path_prefix.h> @@ -28,8 +27,64 @@ using namespace zen; namespace { -Zstring resolveRelativePath(const Zstring& relativePath) //note: ::GetFullPathName() is documented to be NOT thread-safe! +#ifndef NDEBUG + const std::thread::id mainThreadId = std::this_thread::get_id(); +#endif + + +Opt<Zstring> getEnvironmentVar(const Zstring& name) +{ + assert(std::this_thread::get_id() == mainThreadId); //getenv() is not thread-safe! + +#ifdef ZEN_WIN + const DWORD bufferSize = 32767; //MSDN: "maximum buffer size" + std::vector<wchar_t> buffer(bufferSize); + + ::SetLastError(ERROR_SUCCESS); //GetEnvironmentVariable() does not touch global error code when successfully returning variable of zero length! + const DWORD rv = ::GetEnvironmentVariable(name.c_str(), //_In_opt_ LPCTSTR lpName, + &buffer[0], //_Out_opt_ LPTSTR lpBuffer, + bufferSize); //_In_ DWORD nSize + if (rv == 0) + { + const DWORD ec = ::GetLastError(); + + if (ec == ERROR_SUCCESS) //variable exists but is empty + return Zstring(); + + assert(ec == ERROR_ENVVAR_NOT_FOUND); + return NoValue(); + } + else if (rv >= bufferSize) + { + assert(false); + return NoValue(); + } + Zstring value(&buffer[0], rv); + +#elif defined ZEN_LINUX || defined ZEN_MAC + const char* buffer = ::getenv(name.c_str()); //no extended error reporting + if (!buffer) + return NoValue(); + Zstring value(buffer); +#endif + + //some postprocessing: + trim(value); //remove leading, trailing blanks + + //remove leading, trailing double-quotes + if (startsWith(value, Zstr("\"")) && + endsWith (value, Zstr("\"")) && + value.length() >= 2) + value = Zstring(value.c_str() + 1, value.length() - 2); + + return value; +} + + +Zstring resolveRelativePath(const Zstring& relativePath) { + assert(std::this_thread::get_id() == mainThreadId); //GetFullPathName() is documented to NOT be thread-safe! + #ifdef ZEN_WIN //- don't use long path prefix here! does not work with relative paths "." and ".." //- function also replaces "/" characters by "\" @@ -60,14 +115,14 @@ Zstring resolveRelativePath(const Zstring& relativePath) //note: ::GetFullPathNa */ if (startsWith(relativePath, "~/") || relativePath == "~") { - const char* homeDir = ::getenv("HOME"); + Opt<Zstring> homeDir = getEnvironmentVar("HOME"); if (!homeDir) return relativePath; //error! no further processing! if (startsWith(relativePath, "~/")) - return appendSeparator(homeDir) + afterFirst(relativePath, '/', IF_MISSING_RETURN_NONE); + return appendSeparator(*homeDir) + afterFirst(relativePath, '/', IF_MISSING_RETURN_NONE); else if (relativePath == "~") - return homeDir; + return *homeDir; } //we cannot use ::realpath() since it resolves *existing* relative paths only! @@ -205,30 +260,9 @@ private: #endif -Opt<Zstring> getEnvironmentVar(const Zstring& envName) //return null if not found -{ - wxString value; - if (!wxGetEnv(utfCvrtTo<wxString>(envName), &value)) - return NoValue(); - - //some postprocessing: - trim(value); //remove leading, trailing blanks - - //remove leading, trailing double-quotes - if (startsWith(value, L"\"") && - endsWith (value, L"\"") && - value.length() >= 2) - value = wxString(value.c_str() + 1, value.length() - 2); - - return utfCvrtTo<Zstring>(value); -} - - Opt<Zstring> resolveMacro(const Zstring& macro, //macro without %-characters const std::vector<std::pair<Zstring, Zstring>>& ext) //return nullptr if not resolved { - auto equalNoCase = [](const Zstring& lhs, const Zstring& rhs) { return utfCvrtTo<wxString>(lhs).CmpNoCase(utfCvrtTo<wxString>(rhs)) == 0; }; - //there exist environment variables named %TIME%, %DATE% so check for our internal macros first! if (equalNoCase(macro, Zstr("time"))) return formatTime<Zstring>(Zstr("%H%M%S")); @@ -239,24 +273,24 @@ Opt<Zstring> resolveMacro(const Zstring& macro, //macro without %-characters if (equalNoCase(macro, Zstr("timestamp"))) return formatTime<Zstring>(Zstr("%Y-%m-%d %H%M%S")); //e.g. "2012-05-15 131513" - Zstring cand; - auto processPhrase = [&](const Zchar* phrase, const Zchar* format) -> bool + Zstring timeStr; + auto resolveTimePhrase = [&](const Zchar* phrase, const Zchar* format) -> bool { if (!equalNoCase(macro, phrase)) return false; - cand = formatTime<Zstring>(format); + timeStr = formatTime<Zstring>(format); return true; }; - if (processPhrase(Zstr("weekday"), Zstr("%A"))) return cand; - if (processPhrase(Zstr("day" ), Zstr("%d"))) return cand; - if (processPhrase(Zstr("month" ), Zstr("%m"))) return cand; - if (processPhrase(Zstr("week" ), Zstr("%U"))) return cand; - if (processPhrase(Zstr("year" ), Zstr("%Y"))) return cand; - if (processPhrase(Zstr("hour" ), Zstr("%H"))) return cand; - if (processPhrase(Zstr("min" ), Zstr("%M"))) return cand; - if (processPhrase(Zstr("sec" ), Zstr("%S"))) return cand; + if (resolveTimePhrase(Zstr("weekday"), Zstr("%A"))) return timeStr; + if (resolveTimePhrase(Zstr("day" ), Zstr("%d"))) return timeStr; + if (resolveTimePhrase(Zstr("month" ), Zstr("%m"))) return timeStr; + if (resolveTimePhrase(Zstr("week" ), Zstr("%U"))) return timeStr; + if (resolveTimePhrase(Zstr("year" ), Zstr("%Y"))) return timeStr; + if (resolveTimePhrase(Zstr("hour" ), Zstr("%H"))) return timeStr; + if (resolveTimePhrase(Zstr("min" ), Zstr("%M"))) return timeStr; + if (resolveTimePhrase(Zstr("sec" ), Zstr("%S"))) return timeStr; //check domain-specific extensions { @@ -267,7 +301,7 @@ Opt<Zstring> resolveMacro(const Zstring& macro, //macro without %-characters //try to resolve as environment variable if (Opt<Zstring> value = getEnvironmentVar(macro)) - return value; + return *value; #ifdef ZEN_WIN //try to resolve as CSIDL value @@ -333,11 +367,11 @@ Opt<Zstring> getPathByVolumenName(const Zstring& volumeName) //return no value o { const Zstring path = it; - firstMatch.addJob([path, volumeName]() -> std::unique_ptr<Zstring> + firstMatch.addJob([path, volumeName]() -> Opt<Zstring> { UINT type = ::GetDriveType(appendSeparator(path).c_str()); //non-blocking call! if (type == DRIVE_REMOTE || type == DRIVE_CDROM) - return nullptr; + return NoValue(); //next call seriously blocks for non-existing network drives! std::vector<wchar_t> volName(MAX_PATH + 1); //docu says so @@ -350,9 +384,9 @@ Opt<Zstring> getPathByVolumenName(const Zstring& volumeName) //return no value o nullptr, //__out_opt LPDWORD lpFileSystemFlags, nullptr, //__out LPTSTR lpFileSystemNameBuffer, 0)) //__in DWORD nFileSystemNameSize - if (EqualFilePath()(volumeName, &volName[0])) - return std::make_unique<Zstring>(path); - return nullptr; + if (equalFilePath(volumeName, &volName[0])) + return path; + return NoValue(); }); } if (auto result = firstMatch.get()) //blocks until ready @@ -363,7 +397,7 @@ Opt<Zstring> getPathByVolumenName(const Zstring& volumeName) //return no value o } -//networks and cdrom excluded - this should not block +//networks and cdrom excluded - may still block while HDD is spinning up Zstring getVolumeName(const Zstring& volumePath) //return empty string on error { UINT rv = ::GetDriveType(appendSeparator(volumePath).c_str()); //non-blocking call! @@ -530,7 +564,7 @@ Zstring zen::getResolvedFilePath(const Zstring& pathPhrase) //noexcept //remove leading/trailing whitespace before allowing misinterpretation in applyLongPathPrefix() trim(path, true, false); - while (endsWith(path, Zstr(' '))) //don't remove all whitespace from right, e.g. 0xa0 may be used as part of dir name + while (endsWith(path, Zstr(' '))) //don't remove any whitespace from right, e.g. 0xa0 may be used as part of folder name path.resize(path.size() - 1); #ifdef ZEN_WIN @@ -544,10 +578,10 @@ Zstring zen::getResolvedFilePath(const Zstring& pathPhrase) //noexcept /* need to resolve relative paths: WINDOWS: - - \\?\-prefix which needs absolute names + - \\?\-prefix requires absolute names - Volume Shadow Copy: volume name needs to be part of each file path - file icon buffer (at least for extensions that are actually read from disk, like "exe") - - ::SHFileOperation(): Using relative path names is not thread safe + - Use of relative path names is not thread safe! (e.g. SHFileOperation) WINDOWS/LINUX: - detection of dependent directories, e.g. "\" and "C:\test" */ diff --git a/FreeFileSync/Source/lib/resolve_path.h b/FreeFileSync/Source/lib/resolve_path.h index fac1e3ff..34d50360 100644 --- a/FreeFileSync/Source/lib/resolve_path.h +++ b/FreeFileSync/Source/lib/resolve_path.h @@ -4,12 +4,13 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef RESOLVE_PATH_H_INCLUDED_817402834713454 -#define RESOLVE_PATH_H_INCLUDED_817402834713454 +#ifndef RESOLVE_PATH_H_817402834713454 +#define RESOLVE_PATH_H_817402834713454 #include <vector> #include <zen/zstring.h> + namespace zen { /* @@ -34,4 +35,4 @@ std::vector<Zstring> getDirectoryAliases(const Zstring& folderPathPhrase); //may #endif } -#endif //RESOLVE_PATH_H_INCLUDED_817402834713454 +#endif //RESOLVE_PATH_H_817402834713454 diff --git a/FreeFileSync/Source/lib/return_codes.h b/FreeFileSync/Source/lib/return_codes.h index 71da2f02..a3d45a25 100644 --- a/FreeFileSync/Source/lib/return_codes.h +++ b/FreeFileSync/Source/lib/return_codes.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef RETURN_CODES_H_INCLUDED -#define RETURN_CODES_H_INCLUDED +#ifndef RETURN_CODES_H_81307482137054156 +#define RETURN_CODES_H_81307482137054156 namespace zen { @@ -27,4 +27,4 @@ void raiseReturnCode(FfsReturnCode& rc, FfsReturnCode rcProposed) } } -#endif // RETURN_CODES_H_INCLUDED +#endif //RETURN_CODES_H_81307482137054156 diff --git a/FreeFileSync/Source/lib/soft_filter.h b/FreeFileSync/Source/lib/soft_filter.h index 879bfb60..262dd934 100644 --- a/FreeFileSync/Source/lib/soft_filter.h +++ b/FreeFileSync/Source/lib/soft_filter.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef SOFT_FILTER_H_INCLUDED -#define SOFT_FILTER_H_INCLUDED +#ifndef SOFT_FILTER_H_810457108534657 +#define SOFT_FILTER_H_810457108534657 #include <algorithm> #include <limits> @@ -108,8 +108,8 @@ bool SoftFilter::isNull() const //filter is equivalent to NullFilter, but may be return timeFrom_ == std::numeric_limits<std::int64_t>::min() && sizeMin_ == 0U && sizeMax_ == std::numeric_limits<std::uint64_t>::max() && - matchesFolder_ == true;; + matchesFolder_ == true; } } -#endif // SOFT_FILTER_H_INCLUDED +#endif //SOFT_FILTER_H_810457108534657 diff --git a/FreeFileSync/Source/lib/status_handler.h b/FreeFileSync/Source/lib/status_handler.h index 554d8226..7aa77d04 100644 --- a/FreeFileSync/Source/lib/status_handler.h +++ b/FreeFileSync/Source/lib/status_handler.h @@ -4,14 +4,15 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef STATUSHANDLER_H_INCLUDED -#define STATUSHANDLER_H_INCLUDED +#ifndef STATUS_HANDLER_H_81704805908341534 +#define STATUS_HANDLER_H_81704805908341534 #include "../process_callback.h" #include <vector> #include <string> #include <zen/i18n.h> + namespace zen { bool updateUiIsAllowed(); //test if a specific amount of time is over @@ -140,4 +141,4 @@ private: }; } -#endif // STATUSHANDLER_H_INCLUDED +#endif //STATUS_HANDLER_H_81704805908341534 diff --git a/FreeFileSync/Source/lib/status_handler_impl.h b/FreeFileSync/Source/lib/status_handler_impl.h index 489ef9d7..8cce3864 100644 --- a/FreeFileSync/Source/lib/status_handler_impl.h +++ b/FreeFileSync/Source/lib/status_handler_impl.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef STATUSHANDLER_IMPL_H_INCLUDED -#define STATUSHANDLER_IMPL_H_INCLUDED +#ifndef STATUS_HANDLER_IMPL_H_07682758976 +#define STATUS_HANDLER_IMPL_H_07682758976 #include <zen/optional.h> #include <zen/file_error.h> @@ -92,4 +92,4 @@ private: }; } -#endif //STATUSHANDLER_IMPL_H_INCLUDED +#endif //STATUS_HANDLER_IMPL_H_07682758976 diff --git a/FreeFileSync/Source/lib/versioning.cpp b/FreeFileSync/Source/lib/versioning.cpp index 4cfa84df..68561b21 100644 --- a/FreeFileSync/Source/lib/versioning.cpp +++ b/FreeFileSync/Source/lib/versioning.cpp @@ -2,7 +2,7 @@ #include <cstddef> //required by GCC 4.8.1 to find ptrdiff_t using namespace zen; -using ABF = AbstractBaseFolder; +using AFS = AbstractFileSystem; namespace { @@ -42,7 +42,7 @@ bool impl::isMatchingVersion(const Zstring& shortname, const Zstring& shortnameV }; auto nextStringI = [&](const Zstring& str) -> bool //windows: ignore case! { - if (itLast - it < static_cast<ptrdiff_t>(str.size()) || !EqualFilePath()(str, Zstring(&*it, str.size()))) + if (itLast - it < static_cast<ptrdiff_t>(str.size()) || !equalFilePath(str, Zstring(&*it, str.size()))) return false; it += str.size(); return true; @@ -65,8 +65,8 @@ bool impl::isMatchingVersion(const Zstring& shortname, const Zstring& shortnameV /* create target super directories if missing */ -void FileVersioner::moveItemToVersioning(const AbstractPathRef& itemPath, const Zstring& relativePath, //throw FileError - const std::function<void(const AbstractPathRef& sourcePath, const AbstractPathRef& targetPath)>& moveItem) //move source -> target; may throw FileError +void FileVersioner::moveItemToVersioning(const AbstractPath& itemPath, const Zstring& relativePath, //throw FileError + const std::function<void(const AbstractPath& sourcePath, const AbstractPath& targetPath)>& moveItem) //move source -> target; may throw FileError { assert(!startsWith(relativePath, FILE_NAME_SEPARATOR)); assert(!endsWith (relativePath, FILE_NAME_SEPARATOR)); @@ -75,8 +75,6 @@ void FileVersioner::moveItemToVersioning(const AbstractPathRef& itemPath, const Zstring versionedRelPath; switch (versioningStyle_) { - default: - assert(false); case VER_STYLE_REPLACE: versionedRelPath = relativePath; break; @@ -87,8 +85,7 @@ void FileVersioner::moveItemToVersioning(const AbstractPathRef& itemPath, const break; } - const AbstractPathRef versionedItemPath = versioningFolder_->getAbstractPath(versionedRelPath); - + const AbstractPath versionedItemPath = AFS::appendRelPath(versioningFolderPath_, versionedRelPath); try { moveItem(itemPath, versionedItemPath); //throw FileError @@ -96,10 +93,10 @@ void FileVersioner::moveItemToVersioning(const AbstractPathRef& itemPath, const catch (const FileError&) //expected to fail if target directory is not yet existing! { //create intermediate directories if missing - const AbstractPathRef versionedParentPath = versioningFolder_->getAbstractPath(beforeLast(versionedRelPath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)); - if (!ABF::somethingExists(versionedParentPath)) //->(minor) file system race condition! + const AbstractPath versionedParentPath = AFS::appendRelPath(versioningFolderPath_, beforeLast(versionedRelPath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)); + if (!AFS::somethingExists(versionedParentPath)) //->(minor) file system race condition! { - ABF::createFolderRecursively(versionedParentPath); //throw FileError + AFS::createFolderRecursively(versionedParentPath); //throw FileError //retry: this should work now! moveItem(itemPath, versionedItemPath); //throw FileError return; @@ -115,22 +112,22 @@ namespace //no need to check if super-directories of target exist: done by moveItemToVersioning() //if target already exists, it is overwritten, even if it is a different type, e.g. a directory! template <class Function> -void moveItem(const AbstractPathRef& sourcePath, //throw FileError - const AbstractPathRef& targetPath, +void moveItem(const AbstractPath& sourcePath, //throw FileError + const AbstractPath& targetPath, Function copyDelete) //throw FileError; fallback if move failed { - assert(ABF::fileExists(sourcePath) || ABF::symlinkExists(sourcePath) || !ABF::somethingExists(sourcePath)); //we process files and symlinks only + assert(AFS::fileExists(sourcePath) || AFS::symlinkExists(sourcePath) || !AFS::somethingExists(sourcePath)); //we process files and symlinks only //first try to move directly without copying try { - ABF::renameItem(sourcePath, targetPath); //throw FileError, ErrorTargetExisting, ErrorDifferentVolume + AFS::renameItem(sourcePath, targetPath); //throw FileError, ErrorTargetExisting, ErrorDifferentVolume return; //great, we get away cheaply! } catch (const FileError&) { //missing source item is not an error => check BEFORE calling removeTarget()! - if (!ABF::somethingExists(sourcePath)) + if (!AFS::somethingExists(sourcePath)) return; //object *not* processed auto removeTarget = [&] @@ -138,15 +135,15 @@ void moveItem(const AbstractPathRef& sourcePath, //throw FileError try { //file or (broken) file-symlink: - ABF::removeFile(targetPath); //throw FileError + AFS::removeFile(targetPath); //throw FileError } catch (FileError&) { //folder or folder-symlink: - if (ABF::folderExists(targetPath)) //directory or dir-symlink + if (AFS::folderExists(targetPath)) //directory or dir-symlink { - assert(ABF::symlinkExists(targetPath)); //we do not expect targetPath to be a directory in general (but possible!) - ABF::removeFolderRecursively(targetPath, nullptr /*onBeforeFileDeletion*/, nullptr /*onBeforeFolderDeletion*/); //throw FileError + assert(AFS::symlinkExists(targetPath)); //we do not expect targetPath to be a directory in general (but possible!) + AFS::removeFolderRecursively(targetPath, nullptr /*onBeforeFileDeletion*/, nullptr /*onBeforeFolderDeletion*/); //throw FileError } else throw; @@ -164,7 +161,7 @@ void moveItem(const AbstractPathRef& sourcePath, //throw FileError removeTarget(); //throw FileError try { - ABF::renameItem(sourcePath, targetPath); //throw FileError, (ErrorTargetExisting), ErrorDifferentVolume + AFS::renameItem(sourcePath, targetPath); //throw FileError, (ErrorTargetExisting), ErrorDifferentVolume } catch (const ErrorDifferentVolume&) { @@ -175,68 +172,68 @@ void moveItem(const AbstractPathRef& sourcePath, //throw FileError } -void moveFileOrSymlink(const AbstractPathRef& sourcePath, //throw FileError - const AbstractPathRef& targetPath, +void moveFileOrSymlink(const AbstractPath& sourcePath, //throw FileError + const AbstractPath& targetPath, const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus) //may be nullptr { auto copyDelete = [&] { - assert(!ABF::somethingExists(targetPath)); - if (ABF::symlinkExists(sourcePath)) - ABF::copySymlink(sourcePath, targetPath, false /*copy filesystem permissions*/); //throw FileError + assert(!AFS::somethingExists(targetPath)); + if (AFS::symlinkExists(sourcePath)) + AFS::copySymlink(sourcePath, targetPath, false /*copy filesystem permissions*/); //throw FileError else - ABF::copyFileTransactional(sourcePath, targetPath, //throw FileError, ErrorFileLocked + AFS::copyFileTransactional(sourcePath, targetPath, //throw FileError, ErrorFileLocked false /*copyFilePermissions*/, true /*transactionalCopy*/, nullptr /*onDeleteTargetFile*/, onNotifyCopyStatus); - ABF::removeFile(sourcePath); //throw FileError; newly copied file is NOT deleted if exception is thrown here! + AFS::removeFile(sourcePath); //throw FileError; newly copied file is NOT deleted if exception is thrown here! }; moveItem(sourcePath, targetPath, copyDelete); //throw FileError } -void moveFile(const AbstractPathRef& sourcePath, //throw FileError - const AbstractPathRef& targetPath, +void moveFile(const AbstractPath& sourcePath, //throw FileError + const AbstractPath& targetPath, const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus) //may be nullptr { auto copyDelete = [&] { - assert(!ABF::somethingExists(targetPath)); - ABF::copyFileTransactional(sourcePath, targetPath, //throw FileError, ErrorFileLocked + assert(!AFS::somethingExists(targetPath)); + AFS::copyFileTransactional(sourcePath, targetPath, //throw FileError, ErrorFileLocked false /*copyFilePermissions*/, true /*transactionalCopy*/, nullptr /*onDeleteTargetFile*/, onNotifyCopyStatus); - ABF::removeFile(sourcePath); //throw FileError; newly copied file is NOT deleted if exception is thrown here! + AFS::removeFile(sourcePath); //throw FileError; newly copied file is NOT deleted if exception is thrown here! }; moveItem(sourcePath, targetPath, copyDelete); //throw FileError } -void moveFileSymlink(const AbstractPathRef& sourcePath, const AbstractPathRef& targetPath) //throw FileError +void moveFileSymlink(const AbstractPath& sourcePath, const AbstractPath& targetPath) //throw FileError { auto copyDelete = [&] { - assert(!ABF::somethingExists(targetPath)); - ABF::copySymlink(sourcePath, targetPath, false /*copy filesystem permissions*/); //throw FileError - ABF::removeFile(sourcePath); //throw FileError; newly copied file is NOT deleted if exception is thrown here! + assert(!AFS::somethingExists(targetPath)); + AFS::copySymlink(sourcePath, targetPath, false /*copy filesystem permissions*/); //throw FileError + AFS::removeFile(sourcePath); //throw FileError; newly copied file is NOT deleted if exception is thrown here! }; moveItem(sourcePath, targetPath, copyDelete); //throw FileError } -void moveFolderSymlink(const AbstractPathRef& sourcePath, const AbstractPathRef& targetPath) //throw FileError +void moveFolderSymlink(const AbstractPath& sourcePath, const AbstractPath& targetPath) //throw FileError { auto copyDelete = [&] //throw FileError { - assert(!ABF::somethingExists(targetPath)); - ABF::copySymlink(sourcePath, targetPath, false /*copy filesystem permissions*/); //throw FileError - ABF::removeFolderSimple(sourcePath); //throw FileError; newly copied link is NOT deleted if exception is thrown here! + assert(!AFS::somethingExists(targetPath)); + AFS::copySymlink(sourcePath, targetPath, false /*copy filesystem permissions*/); //throw FileError + AFS::removeFolderSimple(sourcePath); //throw FileError; newly copied link is NOT deleted if exception is thrown here! }; moveItem(sourcePath, targetPath, copyDelete); } -struct FlatTraverserCallback: public ABF::TraverserCallback +struct FlatTraverserCallback: public AFS::TraverserCallback { - FlatTraverserCallback(const AbstractPathRef& folderPath) : folderPath_(folderPath) {} + FlatTraverserCallback(const AbstractPath& folderPath) : folderPath_(folderPath) {} const std::vector<Zstring>& refFileNames () const { return fileNames_; } const std::vector<Zstring>& refFolderNames () const { return folderNames_; } @@ -248,7 +245,7 @@ private: std::unique_ptr<TraverserCallback> onDir (const DirInfo& di) override { folderNames_.push_back(di.itemName); return nullptr; } HandleLink onSymlink(const SymlinkInfo& si) override { - if (ABF::folderExists(ABF::appendRelPath(folderPath_, si.itemName))) //dir symlink + if (AFS::folderExists(AFS::appendRelPath(folderPath_, si.itemName))) //dir symlink folderLinkNames_.push_back(si.itemName); else //file symlink, broken symlink fileLinkNames_.push_back(si.itemName); @@ -257,7 +254,7 @@ private: HandleError reportDirError (const std::wstring& msg, size_t retryNumber) override { throw FileError(msg); } HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zstring& itemName) override { throw FileError(msg); } - const AbstractPathRef folderPath_; + const AbstractPath folderPath_; std::vector<Zstring> fileNames_; std::vector<Zstring> folderNames_; std::vector<Zstring> fileLinkNames_; @@ -266,12 +263,12 @@ private: } -bool FileVersioner::revisionFile(const AbstractPathRef& filePath, const Zstring& relativePath, const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus) //throw FileError +bool FileVersioner::revisionFile(const AbstractPath& filePath, const Zstring& relativePath, const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus) //throw FileError { bool moveSuccessful = false; moveItemToVersioning(filePath, relativePath, //throw FileError - [&](const AbstractPathRef& sourcePath, const AbstractPathRef& targetPath) + [&](const AbstractPath& sourcePath, const AbstractPath& targetPath) { moveFileOrSymlink(sourcePath, targetPath, onNotifyCopyStatus); //throw FileError moveSuccessful = true; @@ -280,85 +277,86 @@ bool FileVersioner::revisionFile(const AbstractPathRef& filePath, const Zstring& } -void FileVersioner::revisionFolder(const AbstractPathRef& folderPath, const Zstring& relativePath, //throw FileError +void FileVersioner::revisionFolder(const AbstractPath& folderPath, const Zstring& relativePath, //throw FileError 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 std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus) { - if (ABF::symlinkExists(folderPath)) //on Linux there is just one type of symlink, and since we do revision file symlinks, we should revision dir symlinks as well! + if (AFS::symlinkExists(folderPath)) //on Linux there is just one type of symlink, and since we do revision file symlinks, we should revision dir symlinks as well! { moveItemToVersioning(folderPath, relativePath, //throw FileError - [&](const AbstractPathRef& sourcePath, const AbstractPathRef& targetPath) + [&](const AbstractPath& sourcePath, const AbstractPath& targetPath) { if (onBeforeFolderMove) - onBeforeFolderMove(ABF::getDisplayPath(sourcePath), ABF::getDisplayPath(targetPath)); + onBeforeFolderMove(AFS::getDisplayPath(sourcePath), AFS::getDisplayPath(targetPath)); moveFolderSymlink(sourcePath, targetPath); //throw FileError }); } else { //no error situation if directory is not existing! manual deletion relies on it! - if (ABF::somethingExists(folderPath)) + if (AFS::somethingExists(folderPath)) revisionFolderImpl(folderPath, relativePath, onBeforeFileMove, onBeforeFolderMove, onNotifyCopyStatus); //throw FileError } } -void FileVersioner::revisionFolderImpl(const AbstractPathRef& folderPath, const Zstring& relativePath, //throw FileError +void FileVersioner::revisionFolderImpl(const AbstractPath& folderPath, const Zstring& relativePath, //throw FileError 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 std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus) { - assert(!ABF::symlinkExists(folderPath)); //[!] no symlinks in this context!!! - assert(ABF::folderExists(folderPath)); //Do NOT traverse into it deleting contained files!!! + assert(!AFS::symlinkExists(folderPath)); //[!] no symlinks in this context!!! + assert(AFS::folderExists(folderPath)); //Do NOT traverse into it deleting contained files!!! //create target directories only when needed in moveFileToVersioning(): avoid empty directories! FlatTraverserCallback ft(folderPath); //traverse source directory one level deep - ABF::traverseFolder(folderPath, ft); + AFS::traverseFolder(folderPath, ft); const Zstring relPathPf = appendSeparator(relativePath); for (const Zstring& fileName : ft.refFileNames()) - moveItemToVersioning(ABF::appendRelPath(folderPath, fileName), //throw FileError + moveItemToVersioning(AFS::appendRelPath(folderPath, fileName), //throw FileError relPathPf + fileName, - [&](const AbstractPathRef& sourcePath, const AbstractPathRef& targetPath) + [&](const AbstractPath& sourcePath, const AbstractPath& targetPath) { if (onBeforeFileMove) - onBeforeFileMove(ABF::getDisplayPath(sourcePath), ABF::getDisplayPath(targetPath)); + onBeforeFileMove(AFS::getDisplayPath(sourcePath), AFS::getDisplayPath(targetPath)); moveFile(sourcePath, targetPath, onNotifyCopyStatus); //throw FileError }); for (const Zstring& fileLinkName : ft.refFileLinkNames()) - moveItemToVersioning(ABF::appendRelPath(folderPath, fileLinkName), //throw FileError + moveItemToVersioning(AFS::appendRelPath(folderPath, fileLinkName), //throw FileError relPathPf + fileLinkName, - [&](const AbstractPathRef& sourcePath, const AbstractPathRef& targetPath) + [&](const AbstractPath& sourcePath, const AbstractPath& targetPath) { if (onBeforeFileMove) - onBeforeFileMove(ABF::getDisplayPath(sourcePath), ABF::getDisplayPath(targetPath)); + onBeforeFileMove(AFS::getDisplayPath(sourcePath), AFS::getDisplayPath(targetPath)); moveFileSymlink(sourcePath, targetPath); //throw FileError }); //on Linux there is just one type of symlink, and since we do revision file symlinks, we should revision dir symlinks as well! for (const Zstring& folderLinkName : ft.refFolderLinkNames()) - moveItemToVersioning(ABF::appendRelPath(folderPath, folderLinkName), //throw FileError + moveItemToVersioning(AFS::appendRelPath(folderPath, folderLinkName), //throw FileError relPathPf + folderLinkName, - [&](const AbstractPathRef& sourcePath, const AbstractPathRef& targetPath) + [&](const AbstractPath& sourcePath, const AbstractPath& targetPath) { if (onBeforeFolderMove) - onBeforeFolderMove(ABF::getDisplayPath(sourcePath), ABF::getDisplayPath(targetPath)); + onBeforeFolderMove(AFS::getDisplayPath(sourcePath), AFS::getDisplayPath(targetPath)); moveFolderSymlink(sourcePath, targetPath); //throw FileError }); //move folders recursively for (const Zstring& folderName : ft.refFolderNames()) - revisionFolderImpl(ABF::appendRelPath(folderPath, folderName), //throw FileError + revisionFolderImpl(AFS::appendRelPath(folderPath, folderName), //throw FileError relPathPf + folderName, onBeforeFileMove, onBeforeFolderMove, onNotifyCopyStatus); //delete source if (onBeforeFolderMove) - onBeforeFolderMove(ABF::getDisplayPath(folderPath), ABF::getDisplayPath(versioningFolder_->getAbstractPath(relativePath))); - ABF::removeFolderSimple(folderPath); //throw FileError + onBeforeFolderMove(AFS::getDisplayPath(folderPath), AFS::getDisplayPath(AFS::appendRelPath(versioningFolderPath_, relativePath))); + + AFS::removeFolderSimple(folderPath); //throw FileError } diff --git a/FreeFileSync/Source/lib/versioning.h b/FreeFileSync/Source/lib/versioning.h index 7a9d0ea9..08426dba 100644 --- a/FreeFileSync/Source/lib/versioning.h +++ b/FreeFileSync/Source/lib/versioning.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef VERSIONING_HEADER_8760247652438056 -#define VERSIONING_HEADER_8760247652438056 +#ifndef VERSIONING_H_8760247652438056 +#define VERSIONING_H_8760247652438056 #include <functional> #include <zen/time.h> @@ -31,29 +31,27 @@ namespace zen class FileVersioner { public: - FileVersioner(std::unique_ptr<AbstractBaseFolder>&& versioningFolder, //must be bound! throw FileError! + FileVersioner(const AbstractPath& versioningFolderPath, //throw FileError VersioningStyle versioningStyle, const TimeComp& timeStamp) : + versioningFolderPath_(versioningFolderPath), versioningStyle_(versioningStyle), timeStamp_(formatTime<Zstring>(Zstr("%Y-%m-%d %H%M%S"), timeStamp)) //e.g. "2012-05-15 131513" { - if (!versioningFolder) + if (AbstractFileSystem::isNullPath(versioningFolderPath_)) throw std::logic_error("Programming Error: Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__)); - if (timeStamp_.size() != 17) //formatTime() returns empty string on error; unexpected length: e.g. problem in year 10000! + if (timeStamp_.size() != 17) //formatTime() returns empty string on error; unexpected length: e.g. problem in year 10,000! throw FileError(_("Unable to create time stamp for versioning:") + L" \"" + utfCvrtTo<std::wstring>(timeStamp_) + L"\""); - - //honor strong exception safety guarantee: - versioningFolder_ = std::move(versioningFolder); //noexcept } - bool revisionFile(const AbstractPathRef& filePath, //throw FileError; return "false" if file is not existing + bool revisionFile(const AbstractPath& filePath, //throw FileError; return "false" if file is not existing const Zstring& relativePath, //called frequently if move has to revert to copy + delete => see zen::copyFile for limitations when throwing exceptions! const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus); //may be nullptr - void revisionFolder(const AbstractPathRef& folderPath, const Zstring& relativePath, //throw FileError + void revisionFolder(const AbstractPath& folderPath, const Zstring& relativePath, //throw FileError //optional callbacks: may be nullptr const std::function<void(const std::wstring& displayPathFrom, const std::wstring& displayPathTo)>& onBeforeFileMove, //one call for each *existing* object! @@ -64,18 +62,18 @@ public: //void limitVersions(std::function<void()> updateUI); //throw FileError; call when done revisioning! private: - void revisionFolderImpl(const AbstractPathRef& folderPath, const Zstring& relativePath, + 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 std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus); //throw FileError - void moveItemToVersioning(const AbstractPathRef& itemPath, //throw FileError + void moveItemToVersioning(const AbstractPath& itemPath, //throw FileError const Zstring& relativePath, - const std::function<void(const AbstractPathRef& sourcePath, const AbstractPathRef& targetPath)>& moveItem); //may throw FileError + const std::function<void(const AbstractPath& sourcePath, const AbstractPath& targetPath)>& moveItem); //may throw FileError + const AbstractPath versioningFolderPath_; const VersioningStyle versioningStyle_; const Zstring timeStamp_; - std::unique_ptr<AbstractBaseFolder> versioningFolder_; //always bound! //std::vector<Zstring> fileRelNames; //store list of revisioned file and symlink relative names for limitVersions() }; @@ -86,4 +84,4 @@ bool isMatchingVersion(const Zstring& shortname, const Zstring& shortnameVersion } } -#endif //VERSIONING_HEADER_8760247652438056 +#endif //VERSIONING_H_8760247652438056 diff --git a/FreeFileSync/Source/process_callback.h b/FreeFileSync/Source/process_callback.h index eb50a571..93dc0554 100644 --- a/FreeFileSync/Source/process_callback.h +++ b/FreeFileSync/Source/process_callback.h @@ -4,12 +4,13 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef PROC_HEADER_48257827842345454545 -#define PROC_HEADER_48257827842345454545 +#ifndef PROCESS_CALLBACK_H_48257827842345454545 +#define PROCESS_CALLBACK_H_48257827842345454545 #include <string> #include <cstdint> + //interface for comparison and synchronization process status updates (used by GUI or Batch mode) const int UI_UPDATE_INTERVAL = 100; //unit: [ms]; perform ui updates not more often than necessary, //100 seems to be a good value with only a minimal performance loss; also used by Win 7 copy progress bar @@ -73,4 +74,4 @@ struct ProcessCallback virtual void abortProcessNow() = 0; //will throw an exception => don't call while in a C GUI callstack }; -#endif //PROC_HEADER_48257827842345454545 +#endif //PROCESS_CALLBACK_H_48257827842345454545 diff --git a/FreeFileSync/Source/structures.cpp b/FreeFileSync/Source/structures.cpp index f4a46dc6..0fc3e584 100644 --- a/FreeFileSync/Source/structures.cpp +++ b/FreeFileSync/Source/structures.cpp @@ -89,7 +89,7 @@ bool zen::detectMovedFilesSelectable(const DirectionConfig& cfg) const DirectionSet tmp = zen::extractDirections(cfg); return (tmp.exLeftSideOnly == SyncDirection::RIGHT && tmp.exRightSideOnly == SyncDirection::RIGHT) || - (tmp.exLeftSideOnly == SyncDirection::LEFT&& + (tmp.exLeftSideOnly == SyncDirection::LEFT && tmp.exRightSideOnly == SyncDirection::LEFT); } diff --git a/FreeFileSync/Source/synchronization.cpp b/FreeFileSync/Source/synchronization.cpp index 3d4ed798..3aec81ba 100644 --- a/FreeFileSync/Source/synchronization.cpp +++ b/FreeFileSync/Source/synchronization.cpp @@ -13,6 +13,7 @@ #include "lib/versioning.h" #include "lib/binary.h" #include "fs/concrete.h" +#include "fs/native.h" #ifdef ZEN_WIN #include <zen/long_path_prefix.h> @@ -31,43 +32,28 @@ namespace inline int getCUD(const SyncStatistics& stat) { - return stat.getCreate() + - stat.getUpdate() + - stat.getDelete(); + return stat.createCount() + + stat.updateCount() + + stat.deleteCount(); } } -void SyncStatistics::init() -{ - createLeft = 0; - createRight = 0; - updateLeft = 0; - updateRight = 0; - deleteLeft = 0; - deleteRight = 0; - dataToProcess = 0; - rowsTotal = 0; -} - SyncStatistics::SyncStatistics(const FolderComparison& folderCmp) { - init(); - std::for_each(begin(folderCmp), end(folderCmp), [&](const BaseDirPair& baseDirObj) { recurse(baseDirObj); }); + std::for_each(begin(folderCmp), end(folderCmp), [&](const BaseFolderPair& baseFolder) { recurse(baseFolder); }); } -SyncStatistics::SyncStatistics(const HierarchyObject& hierObj) +SyncStatistics::SyncStatistics(const HierarchyObject& hierObj) { - init(); recurse(hierObj); } -SyncStatistics::SyncStatistics(const FilePair& fileObj) +SyncStatistics::SyncStatistics(const FilePair& file) { - init(); - processFile(fileObj); + processFile(file); rowsTotal += 1; } @@ -75,32 +61,32 @@ SyncStatistics::SyncStatistics(const FilePair& fileObj) inline void SyncStatistics::recurse(const HierarchyObject& hierObj) { - for (const FilePair& fileObj : hierObj.refSubFiles()) - processFile(fileObj); - for (const SymlinkPair& linkObj : hierObj.refSubLinks()) - processLink(linkObj); - for (const DirPair& dirObj : hierObj.refSubDirs()) - processDir(dirObj); - - rowsTotal += hierObj.refSubDirs(). size(); - rowsTotal += hierObj.refSubFiles().size(); - rowsTotal += hierObj.refSubLinks().size(); + for (const FilePair& file : hierObj.refSubFiles()) + processFile(file); + for (const SymlinkPair& link : hierObj.refSubLinks()) + processLink(link); + for (const FolderPair& folder : hierObj.refSubFolders()) + processFolder(folder); + + rowsTotal += hierObj.refSubFolders().size(); + rowsTotal += hierObj.refSubFiles ().size(); + rowsTotal += hierObj.refSubLinks ().size(); } inline -void SyncStatistics::processFile(const FilePair& fileObj) +void SyncStatistics::processFile(const FilePair& file) { - switch (fileObj.getSyncOperation()) //evaluate comparison result and sync direction + switch (file.getSyncOperation()) //evaluate comparison result and sync direction { case SO_CREATE_NEW_LEFT: ++createLeft; - dataToProcess += static_cast<std::int64_t>(fileObj.getFileSize<RIGHT_SIDE>()); + dataToProcess += static_cast<std::int64_t>(file.getFileSize<RIGHT_SIDE>()); break; case SO_CREATE_NEW_RIGHT: ++createRight; - dataToProcess += static_cast<std::int64_t>(fileObj.getFileSize<LEFT_SIDE>()); + dataToProcess += static_cast<std::int64_t>(file.getFileSize<LEFT_SIDE>()); break; case SO_DELETE_LEFT: @@ -125,16 +111,16 @@ void SyncStatistics::processFile(const FilePair& fileObj) case SO_OVERWRITE_LEFT: ++updateLeft; - dataToProcess += static_cast<std::int64_t>(fileObj.getFileSize<RIGHT_SIDE>()); + dataToProcess += static_cast<std::int64_t>(file.getFileSize<RIGHT_SIDE>()); break; case SO_OVERWRITE_RIGHT: ++updateRight; - dataToProcess += static_cast<std::int64_t>(fileObj.getFileSize<LEFT_SIDE>()); + dataToProcess += static_cast<std::int64_t>(file.getFileSize<LEFT_SIDE>()); break; case SO_UNRESOLVED_CONFLICT: - conflictMsgs.emplace_back(fileObj.getPairRelativePath(), fileObj.getSyncOpConflict()); + conflictMsgs.emplace_back(file.getPairRelativePath(), file.getSyncOpConflict()); break; case SO_COPY_METADATA_TO_LEFT: @@ -153,9 +139,9 @@ void SyncStatistics::processFile(const FilePair& fileObj) inline -void SyncStatistics::processLink(const SymlinkPair& linkObj) +void SyncStatistics::processLink(const SymlinkPair& link) { - switch (linkObj.getSyncOperation()) //evaluate comparison result and sync direction + switch (link.getSyncOperation()) //evaluate comparison result and sync direction { case SO_CREATE_NEW_LEFT: ++createLeft; @@ -184,7 +170,7 @@ void SyncStatistics::processLink(const SymlinkPair& linkObj) break; case SO_UNRESOLVED_CONFLICT: - conflictMsgs.emplace_back(linkObj.getPairRelativePath(), linkObj.getSyncOpConflict()); + conflictMsgs.emplace_back(link.getPairRelativePath(), link.getSyncOpConflict()); break; case SO_MOVE_LEFT_SOURCE: @@ -200,9 +186,9 @@ void SyncStatistics::processLink(const SymlinkPair& linkObj) inline -void SyncStatistics::processDir(const DirPair& dirObj) +void SyncStatistics::processFolder(const FolderPair& folder) { - switch (dirObj.getSyncOperation()) //evaluate comparison result and sync direction + switch (folder.getSyncOperation()) //evaluate comparison result and sync direction { case SO_CREATE_NEW_LEFT: ++createLeft; @@ -221,7 +207,7 @@ void SyncStatistics::processDir(const DirPair& dirObj) break; case SO_UNRESOLVED_CONFLICT: - conflictMsgs.emplace_back(dirObj.getPairRelativePath(), dirObj.getSyncOpConflict()); + conflictMsgs.emplace_back(folder.getPairRelativePath(), folder.getSyncOpConflict()); break; case SO_OVERWRITE_LEFT: @@ -244,7 +230,7 @@ void SyncStatistics::processDir(const DirPair& dirObj) break; } - recurse(dirObj); //since we model logical stats, we recurse, even if deletion variant is "recycler" or "versioning + same volume", which is a single physical operation! + recurse(folder); //since we model logical stats, we recurse, even if deletion variant is "recycler" or "versioning + same volume", which is a single physical operation! } //----------------------------------------------------------------------------------------------------------- @@ -280,19 +266,19 @@ namespace bool significantDifferenceDetected(const SyncStatistics& folderPairStat) { //initial file copying shall not be detected as major difference - if ((folderPairStat.getCreate<LEFT_SIDE >() == 0 || - folderPairStat.getCreate<RIGHT_SIDE>() == 0) && - folderPairStat.getUpdate () == 0 && - folderPairStat.getDelete () == 0 && - folderPairStat.getConflict() == 0) + if ((folderPairStat.createCount<LEFT_SIDE >() == 0 || + folderPairStat.createCount<RIGHT_SIDE>() == 0) && + folderPairStat.updateCount () == 0 && + folderPairStat.deleteCount () == 0 && + folderPairStat.conflictCount() == 0) return false; - const int nonMatchingRows = folderPairStat.getCreate() + - folderPairStat.getDelete(); - //folderPairStat.getUpdate() + -> not relevant when testing for "wrong folder selected" - //folderPairStat.getConflict(); + const int nonMatchingRows = folderPairStat.createCount() + + folderPairStat.deleteCount(); + //folderPairStat.updateCount() + -> not relevant when testing for "wrong folder selected" + //folderPairStat.conflictCount(); - return nonMatchingRows >= 10 && nonMatchingRows > 0.5 * folderPairStat.getRowCount(); + return nonMatchingRows >= 10 && nonMatchingRows > 0.5 * folderPairStat.rowCount(); } //################################################################################################################# @@ -300,7 +286,7 @@ bool significantDifferenceDetected(const SyncStatistics& folderPairStat) class DeletionHandling //abstract deletion variants: permanently, recycle bin, user-defined directory { public: - DeletionHandling(ABF& baseFolder, + DeletionHandling(const AbstractPath& baseFolderPath, DeletionPolicy handleDel, //nothrow! const Zstring& versioningFolderPhrase, VersioningStyle versioningStyle, @@ -324,9 +310,9 @@ public: //clean-up temporary directory (recycle bin optimization) void tryCleanup(bool allowUserCallback); //throw FileError; throw X -> call this in non-exceptional coding, i.e. somewhere after sync! - template <class Function> void removeFileWithCallback (const AbstractPathRef& filePath, const Zstring& relativePath, Function onNotifyItemDeletion, const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus); // - template <class Function> void removeDirWithCallback (const AbstractPathRef& dirPath, const Zstring& relativePath, Function onNotifyItemDeletion, const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus); //throw FileError - template <class Function> void removeLinkWithCallback (const AbstractPathRef& linkPath, const Zstring& relativePath, Function onNotifyItemDeletion, const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus); // + template <class Function> void removeFileWithCallback (const AbstractPath& filePath, const Zstring& relativePath, Function onNotifyItemDeletion, const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus); // + template <class Function> void removeDirWithCallback (const AbstractPath& dirPath, const Zstring& relativePath, Function onNotifyItemDeletion, const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus); //throw FileError + template <class Function> void removeLinkWithCallback (const AbstractPath& linkPath, const Zstring& relativePath, Function onNotifyItemDeletion, const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus); // const std::wstring& getTxtRemovingFile () const { return txtRemovingFile; } // const std::wstring& getTxtRemovingSymLink() const { return txtRemovingSymlink; } //buffered status texts @@ -336,33 +322,33 @@ private: DeletionHandling (const DeletionHandling&) = delete; DeletionHandling& operator=(const DeletionHandling&) = delete; + AFS::RecycleSession& getOrCreateRecyclerSession() //throw FileError => dont create in constructor!!! + { + assert(deletionPolicy_ == DELETE_TO_RECYCLER); + if (!recyclerSession.get()) + recyclerSession = AFS::createRecyclerSession(baseFolderPath_); //throw FileError + return *recyclerSession; + } + FileVersioner& getOrCreateVersioner() //throw FileError => dont create in constructor!!! { assert(deletionPolicy_ == DELETE_TO_VERSIONING); if (!versioner.get()) - versioner = std::make_unique<FileVersioner>(std::move(versioningFolder), versioningStyle_, timeStamp_); //throw FileError + versioner = std::make_unique<FileVersioner>(versioningFolderPath, versioningStyle_, timeStamp_); //throw FileError return *versioner; - }; - - ABF::RecycleSession& getOrCreateRecyclerSession() //throw FileError => dont create in constructor!!! - { - assert(deletionPolicy_ == DELETE_TO_RECYCLER); - if (!recyclerSession.get()) - recyclerSession = baseFolder_.createRecyclerSession(); //throw FileError - return *recyclerSession; - }; + } ProcessCallback& procCallback_; const DeletionPolicy deletionPolicy_; //keep it invariant! e.g. consider getOrCreateVersioner() one-time construction! - ABF& baseFolder_; - std::unique_ptr<ABF::RecycleSession> recyclerSession; + const AbstractPath baseFolderPath_; + std::unique_ptr<AFS::RecycleSession> recyclerSession; //used only for DELETE_TO_VERSIONING: + const AbstractPath versioningFolderPath; const VersioningStyle versioningStyle_; const TimeComp timeStamp_; - std::unique_ptr<AbstractBaseFolder> versioningFolder; //bound until first call to getOrCreateVersioner()!!! std::unique_ptr<FileVersioner> versioner; //throw FileError in constructor => create on demand! //buffer status texts: @@ -375,7 +361,7 @@ private: }; -DeletionHandling::DeletionHandling(ABF& baseFolder, +DeletionHandling::DeletionHandling(const AbstractPath& baseFolderPath, DeletionPolicy handleDel, //nothrow! const Zstring& versioningFolderPhrase, VersioningStyle versioningStyle, @@ -383,7 +369,8 @@ DeletionHandling::DeletionHandling(ABF& baseFolder, ProcessCallback& procCallback) : procCallback_(procCallback), deletionPolicy_(handleDel), - baseFolder_(baseFolder), + baseFolderPath_(baseFolderPath), + versioningFolderPath(createAbstractPath(versioningFolderPhrase)), versioningStyle_(versioningStyle), timeStamp_(timeStamp), txtMovingFile (_("Moving file %x to %y")), @@ -404,15 +391,10 @@ DeletionHandling::DeletionHandling(ABF& baseFolder, break; case DELETE_TO_VERSIONING: - { - versioningFolder = createAbstractBaseFolder(versioningFolderPhrase); //noexcept - const std::wstring displayPathFmt = fmtPath(ABF::getDisplayPath(versioningFolder->getAbstractPath())); - - txtRemovingFile = replaceCpy(_("Moving file %x to %y" ), L"%y", displayPathFmt); - txtRemovingDirectory = replaceCpy(_("Moving folder %x to %y" ), L"%y", displayPathFmt); - txtRemovingSymlink = replaceCpy(_("Moving symbolic link %x to %y"), L"%y", displayPathFmt); - } - break; + txtRemovingFile = replaceCpy(_("Moving file %x to %y" ), L"%y", fmtPath(AFS::getDisplayPath(versioningFolderPath))); + txtRemovingDirectory = replaceCpy(_("Moving folder %x to %y" ), L"%y", fmtPath(AFS::getDisplayPath(versioningFolderPath))); + txtRemovingSymlink = replaceCpy(_("Moving symbolic link %x to %y"), L"%y", fmtPath(AFS::getDisplayPath(versioningFolderPath))); + break; } } @@ -460,7 +442,7 @@ void DeletionHandling::tryCleanup(bool allowUserCallback) //throw FileError; thr template <class Function> -void DeletionHandling::removeDirWithCallback(const AbstractPathRef& dirPath, +void DeletionHandling::removeDirWithCallback(const AbstractPath& folderPath, const Zstring& relativePath, Function onNotifyItemDeletion, const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus) //throw FileError @@ -477,12 +459,12 @@ void DeletionHandling::removeDirWithCallback(const AbstractPathRef& dirPath, auto onBeforeFileDeletion = [&](const std::wstring& displayPath) { notifyDeletion(txtRemovingFile, displayPath); }; auto onBeforeDirDeletion = [&](const std::wstring& displayPath) { notifyDeletion(txtRemovingDirectory, displayPath); }; - ABF::removeFolderRecursively(dirPath, onBeforeFileDeletion, onBeforeDirDeletion); //throw FileError + AFS::removeFolderRecursively(folderPath, onBeforeFileDeletion, onBeforeDirDeletion); //throw FileError } break; case DELETE_TO_RECYCLER: - if (getOrCreateRecyclerSession().recycleItem(dirPath, relativePath)) //throw FileError; return true if item existed + if (getOrCreateRecyclerSession().recycleItem(folderPath, relativePath)) //throw FileError; return true if item existed onNotifyItemDeletion(); //moving to recycler is ONE logical operation, irrespective of the number of child elements! break; @@ -496,7 +478,7 @@ void DeletionHandling::removeDirWithCallback(const AbstractPathRef& dirPath, auto onBeforeFileMove = [&](const std::wstring& displayPathFrom, const std::wstring& displayPathTo) { notifyMove(txtMovingFile, displayPathFrom, displayPathTo); }; auto onBeforeFolderMove = [&](const std::wstring& displayPathFrom, const std::wstring& displayPathTo) { notifyMove(txtMovingFolder, displayPathFrom, displayPathTo); }; - getOrCreateVersioner().revisionFolder(dirPath, relativePath, onBeforeFileMove, onBeforeFolderMove, onNotifyCopyStatus); //throw FileError + getOrCreateVersioner().revisionFolder(folderPath, relativePath, onBeforeFileMove, onBeforeFolderMove, onNotifyCopyStatus); //throw FileError } break; } @@ -504,20 +486,20 @@ void DeletionHandling::removeDirWithCallback(const AbstractPathRef& dirPath, template <class Function> -void DeletionHandling::removeFileWithCallback(const AbstractPathRef& filePath, +void DeletionHandling::removeFileWithCallback(const AbstractPath& filePath, const Zstring& relativePath, Function onNotifyItemDeletion, const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus) //throw FileError { bool deleted = false; - if (endsWith(relativePath, ABF::TEMP_FILE_ENDING)) //special rule for .ffs_tmp files: always delete permanently! - deleted = ABF::removeFile(filePath); //throw FileError + if (endsWith(relativePath, AFS::TEMP_FILE_ENDING)) //special rule for .ffs_tmp files: always delete permanently! + deleted = AFS::removeFile(filePath); //throw FileError else switch (deletionPolicy_) { case DELETE_PERMANENTLY: - deleted = ABF::removeFile(filePath); //throw FileError + deleted = AFS::removeFile(filePath); //throw FileError break; case DELETE_TO_RECYCLER: @@ -534,9 +516,9 @@ void DeletionHandling::removeFileWithCallback(const AbstractPathRef& filePath, template <class Function> inline -void DeletionHandling::removeLinkWithCallback(const AbstractPathRef& linkPath, const Zstring& relativePath, Function onNotifyItemDeletion, const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus) //throw FileError +void DeletionHandling::removeLinkWithCallback(const AbstractPath& linkPath, const Zstring& relativePath, Function onNotifyItemDeletion, const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus) //throw FileError { - if (ABF::folderExists(linkPath)) //dir symlink + if (AFS::folderExists(linkPath)) //dir symlink return removeDirWithCallback(linkPath, relativePath, onNotifyItemDeletion, onNotifyCopyStatus); //throw FileError else //file symlink, broken symlink return removeFileWithCallback(linkPath, relativePath, onNotifyItemDeletion, onNotifyCopyStatus); //throw FileError @@ -556,10 +538,10 @@ void DeletionHandling::removeLinkWithCallback(const AbstractPathRef& linkPath, c class MinimumDiskSpaceNeeded { public: - static std::pair<std::int64_t, std::int64_t> calculate(const BaseDirPair& baseObj) + static std::pair<std::int64_t, std::int64_t> calculate(const BaseFolderPair& baseFolder) { MinimumDiskSpaceNeeded inst; - inst.recurse(baseObj); + inst.recurse(baseFolder); return std::make_pair(inst.spaceNeededLeft, inst.spaceNeededRight); } @@ -571,37 +553,37 @@ private: //don't process directories //process files - for (const FilePair& fileObj : hierObj.refSubFiles()) - switch (fileObj.getSyncOperation()) //evaluate comparison result and sync direction + for (const FilePair& file : hierObj.refSubFiles()) + switch (file.getSyncOperation()) //evaluate comparison result and sync direction { case SO_CREATE_NEW_LEFT: - spaceNeededLeft += static_cast<std::int64_t>(fileObj.getFileSize<RIGHT_SIDE>()); + spaceNeededLeft += static_cast<std::int64_t>(file.getFileSize<RIGHT_SIDE>()); break; case SO_CREATE_NEW_RIGHT: - spaceNeededRight += static_cast<std::int64_t>(fileObj.getFileSize<LEFT_SIDE>()); + spaceNeededRight += static_cast<std::int64_t>(file.getFileSize<LEFT_SIDE>()); break; case SO_DELETE_LEFT: //if (freeSpaceDelLeft_) - spaceNeededLeft -= static_cast<std::int64_t>(fileObj.getFileSize<LEFT_SIDE>()); + spaceNeededLeft -= static_cast<std::int64_t>(file.getFileSize<LEFT_SIDE>()); break; case SO_DELETE_RIGHT: //if (freeSpaceDelRight_) - spaceNeededRight -= static_cast<std::int64_t>(fileObj.getFileSize<RIGHT_SIDE>()); + spaceNeededRight -= static_cast<std::int64_t>(file.getFileSize<RIGHT_SIDE>()); break; case SO_OVERWRITE_LEFT: //if (freeSpaceDelLeft_) - spaceNeededLeft -= static_cast<std::int64_t>(fileObj.getFileSize<LEFT_SIDE>()); - spaceNeededLeft += static_cast<std::int64_t>(fileObj.getFileSize<RIGHT_SIDE>()); + spaceNeededLeft -= static_cast<std::int64_t>(file.getFileSize<LEFT_SIDE>()); + spaceNeededLeft += static_cast<std::int64_t>(file.getFileSize<RIGHT_SIDE>()); break; case SO_OVERWRITE_RIGHT: //if (freeSpaceDelRight_) - spaceNeededRight -= static_cast<std::int64_t>(fileObj.getFileSize<RIGHT_SIDE>()); - spaceNeededRight += static_cast<std::int64_t>(fileObj.getFileSize<LEFT_SIDE>()); + spaceNeededRight -= static_cast<std::int64_t>(file.getFileSize<RIGHT_SIDE>()); + spaceNeededRight += static_cast<std::int64_t>(file.getFileSize<LEFT_SIDE>()); break; case SO_DO_NOTHING: @@ -620,8 +602,8 @@ private: //[...] //recurse into sub-dirs - for (auto& subDir : hierObj.refSubDirs()) - recurse(subDir); + for (const FolderPair& folder : hierObj.refSubFolders()) + recurse(folder); } std::int64_t spaceNeededLeft; @@ -661,11 +643,11 @@ public: txtMovingFile (_("Moving file %x to %y")) {} - void startSync(BaseDirPair& baseDirObj) + void startSync(BaseFolderPair& baseFolder) { - runZeroPass(baseDirObj); //first process file moves - runPass<PASS_ONE>(baseDirObj); //delete files (or overwrite big ones with smaller ones) - runPass<PASS_TWO>(baseDirObj); //copy rest + runZeroPass(baseFolder); //first process file moves + runPass<PASS_ONE>(baseFolder); //delete files (or overwrite big ones with smaller ones) + runPass<PASS_TWO>(baseFolder); //copy rest } private: @@ -676,13 +658,13 @@ private: PASS_NEVER //skip }; - static PassId getPass(const FilePair& fileObj); - static PassId getPass(const SymlinkPair& linkObj); - static PassId getPass(const DirPair& dirObj); + static PassId getPass(const FilePair& file); + static PassId getPass(const SymlinkPair& link); + static PassId getPass(const FolderPair& folder); template <SelectedSide side> void prepare2StepMove(FilePair& sourceObj, FilePair& targetObj); //throw FileError - bool createParentDir(FileSystemObject& fsObj); //throw FileError + bool createParentFolder(FileSystemObject& fsObj); //throw FileError template <SelectedSide side> void manageFileMove(FilePair& sourceObj, FilePair& targetObj); //throw FileError @@ -690,26 +672,26 @@ private: template <PassId pass> void runPass(HierarchyObject& hierObj); - void synchronizeFile(FilePair& fileObj); - template <SelectedSide side> void synchronizeFileInt(FilePair& fileObj, SyncOperation syncOp); + void synchronizeFile(FilePair& file); + template <SelectedSide side> void synchronizeFileInt(FilePair& file, SyncOperation syncOp); - void synchronizeLink(SymlinkPair& linkObj); - template <SelectedSide sideTrg> void synchronizeLinkInt(SymlinkPair& linkObj, SyncOperation syncOp); + void synchronizeLink(SymlinkPair& link); + template <SelectedSide sideTrg> void synchronizeLinkInt(SymlinkPair& link, SyncOperation syncOp); - void synchronizeFolder(DirPair& dirObj); - template <SelectedSide sideTrg> void synchronizeFolderInt(DirPair& dirObj, SyncOperation syncOp); + void synchronizeFolder(FolderPair& folder); + template <SelectedSide sideTrg> void synchronizeFolderInt(FolderPair& folder, SyncOperation syncOp); - void reportStatus(const std::wstring& rawText, const std::wstring& displayPath) const { procCallback_.reportStatus(replaceCpy(rawText, L"%x", fmtPath(displayPath))); }; - void reportInfo (const std::wstring& rawText, const std::wstring& displayPath) const { procCallback_.reportInfo (replaceCpy(rawText, L"%x", fmtPath(displayPath))); }; + void reportStatus(const std::wstring& rawText, const std::wstring& displayPath) const { procCallback_.reportStatus(replaceCpy(rawText, L"%x", fmtPath(displayPath))); } + void reportInfo (const std::wstring& rawText, const std::wstring& displayPath) const { procCallback_.reportInfo (replaceCpy(rawText, L"%x", fmtPath(displayPath))); } void reportInfo (const std::wstring& rawText, const std::wstring& displayPath1, const std::wstring& displayPath2) const { procCallback_.reportInfo(replaceCpy(replaceCpy(rawText, L"%x", L"\n" + fmtPath(displayPath1)), L"%y", L"\n" + fmtPath(displayPath2))); - }; + } - ABF::FileAttribAfterCopy copyFileWithCallback(const AbstractPathRef& sourcePath, - const AbstractPathRef& targetPath, + AFS::FileAttribAfterCopy copyFileWithCallback(const AbstractPath& sourcePath, + const AbstractPath& targetPath, const std::function<void()>& onDeleteTargetFile, const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus) const; //throw FileError @@ -757,7 +739,7 @@ __________________________ 3. create move target's parent directory recursively + execute move do we have name clash? - -> prepare a 2-step move operation: 1. move source to root and update "move target" accordingly 2. delay move until 2nd pass + -> prepare a 2-step move operation: 1. move source to base and update "move target" accordingly 2. delay move until 2nd pass 4. If any of the operations above did not succeed (even after retry), update statistics and revert to "copy + delete" Note: first pass may delete "move source"!!! @@ -779,7 +761,7 @@ template <class List> inline bool haveNameClash(const Zstring& shortname, List& m) { return std::any_of(m.begin(), m.end(), - [&](const typename List::value_type& obj) { return EqualFilePath()(obj.getPairShortName(), shortname); }); + [&](const typename List::value_type& obj) { return equalFilePath(obj.getPairItemName(), shortname); }); } @@ -787,7 +769,7 @@ template <SelectedSide side> void SynchronizeFolderPair::prepare2StepMove(FilePair& sourceObj, FilePair& targetObj) //throw FileError { - Zstring sourceRelPathTmp = sourceObj.getItemName<side>() + ABF::TEMP_FILE_ENDING; + Zstring sourceRelPathTmp = sourceObj.getItemName<side>() + AFS::TEMP_FILE_ENDING; //this could still lead to a name-clash in obscure cases, if some file exists on the other side with //the very same (.ffs_tmp) name and is copied before the second step of the move is executed //good news: even in this pathologic case, this may only prevent the copy of the other file, but not the move @@ -795,19 +777,19 @@ void SynchronizeFolderPair::prepare2StepMove(FilePair& sourceObj, for (int i = 0;; ++i) try { - AbstractPathRef sourcePathTmp = sourceObj.getABF<side>().getAbstractPath(sourceRelPathTmp); + AbstractPath sourcePathTmp = AFS::appendRelPath(sourceObj.base().getAbstractPath<side>(), sourceRelPathTmp); reportInfo(txtMovingFile, - ABF::getDisplayPath(sourceObj.getAbstractPath<side>()), - ABF::getDisplayPath(sourcePathTmp)); + AFS::getDisplayPath(sourceObj.getAbstractPath<side>()), + AFS::getDisplayPath(sourcePathTmp)); - ABF::renameItem(sourceObj.getAbstractPath<side>(), sourcePathTmp); //throw FileError, ErrorTargetExisting, (ErrorDifferentVolume) + AFS::renameItem(sourceObj.getAbstractPath<side>(), sourcePathTmp); //throw FileError, ErrorTargetExisting, (ErrorDifferentVolume) break; } catch (const ErrorTargetExisting&) //repeat until unique name found: no file system race condition! { if (i == 10) throw; //avoid endless recursion in pathological cases - sourceRelPathTmp = sourceObj.getItemName<side>() + Zchar('_') + numberTo<Zstring>(i) + ABF::TEMP_FILE_ENDING; + sourceRelPathTmp = sourceObj.getItemName<side>() + Zchar('_') + numberTo<Zstring>(i) + AFS::TEMP_FILE_ENDING; } warn_static("was wenn diff volume: symlink aliasing!") //throw FileError, ErrorDifferentVolume, ErrorTargetExisting @@ -818,8 +800,8 @@ void SynchronizeFolderPair::prepare2StepMove(FilePair& sourceObj, sourceObj.getFileId <side>(), sourceObj.isFollowedSymlink<side>()); - FilePair& tempFile = sourceObj.root().addSubFile<side>(afterLast(sourceRelPathTmp, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL), descrSource); - static_assert(IsSameType<FixedList<FilePair>, HierarchyObject::SubFileVec>::value, + FilePair& tempFile = sourceObj.base().addSubFile<side>(afterLast(sourceRelPathTmp, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL), descrSource); + static_assert(IsSameType<FixedList<FilePair>, HierarchyObject::FileList>::value, "ATTENTION: we're adding to the file list WHILE looping over it! This is only working because FixedList iterators are not invalidated by insertion!"); sourceObj.removeObject<side>(); //remove only *after* evaluating "sourceObj, side"! @@ -834,42 +816,42 @@ void SynchronizeFolderPair::prepare2StepMove(FilePair& sourceObj, } -bool SynchronizeFolderPair::createParentDir(FileSystemObject& fsObj) //throw FileError, "false" on name clash +bool SynchronizeFolderPair::createParentFolder(FileSystemObject& fsObj) //throw FileError, "false" on name clash { - if (DirPair* parentDir = dynamic_cast<DirPair*>(&fsObj.parent())) + if (auto parentFolder = dynamic_cast<FolderPair*>(&fsObj.parent())) { - if (!createParentDir(*parentDir)) + if (!createParentFolder(*parentFolder)) return false; //detect (and try to resolve) file type conflicts: 1. symlinks 2. files - const Zstring& shortname = parentDir->getPairShortName(); - if (haveNameClash(shortname, parentDir->parent().refSubLinks()) || - haveNameClash(shortname, parentDir->parent().refSubFiles())) + const Zstring& shortname = parentFolder->getPairItemName(); + if (haveNameClash(shortname, parentFolder->parent().refSubLinks()) || + haveNameClash(shortname, parentFolder->parent().refSubFiles())) return false; - //in this context "parentDir" cannot be scheduled for deletion since it contains a "move target"! - //note: if parentDir were deleted, we'd end up destroying "fsObj"! - assert(parentDir->getSyncOperation() != SO_DELETE_LEFT && - parentDir->getSyncOperation() != SO_DELETE_RIGHT); + //in this context "parentFolder" cannot be scheduled for deletion since it contains a "move target"! + //note: if parentFolder were deleted, we'd end up destroying "fsObj"! + assert(parentFolder->getSyncOperation() != SO_DELETE_LEFT && + parentFolder->getSyncOperation() != SO_DELETE_RIGHT); - synchronizeFolder(*parentDir); //throw FileError + synchronizeFolder(*parentFolder); //throw FileError } return true; } template <SelectedSide side> -void SynchronizeFolderPair::manageFileMove(FilePair& sourceObj, - FilePair& targetObj) //throw FileError +void SynchronizeFolderPair::manageFileMove(FilePair& sourceFile, + FilePair& targetFile) //throw FileError { - assert((sourceObj.getSyncOperation() == SO_MOVE_LEFT_SOURCE && targetObj.getSyncOperation() == SO_MOVE_LEFT_TARGET && side == LEFT_SIDE) || - (sourceObj.getSyncOperation() == SO_MOVE_RIGHT_SOURCE && targetObj.getSyncOperation() == SO_MOVE_RIGHT_TARGET && side == RIGHT_SIDE)); + assert((sourceFile.getSyncOperation() == SO_MOVE_LEFT_SOURCE && targetFile.getSyncOperation() == SO_MOVE_LEFT_TARGET && side == LEFT_SIDE) || + (sourceFile.getSyncOperation() == SO_MOVE_RIGHT_SOURCE && targetFile.getSyncOperation() == SO_MOVE_RIGHT_TARGET && side == RIGHT_SIDE)); const bool sourceWillBeDeleted = [&]() -> bool { - if (DirPair* parentDir = dynamic_cast<DirPair*>(&sourceObj.parent())) + if (auto parentFolder = dynamic_cast<const FolderPair*>(&sourceFile.parent())) { - switch (parentDir->getSyncOperation()) //evaluate comparison result and sync direction + switch (parentFolder->getSyncOperation()) //evaluate comparison result and sync direction { case SO_DELETE_LEFT: case SO_DELETE_RIGHT: @@ -893,24 +875,24 @@ void SynchronizeFolderPair::manageFileMove(FilePair& sourceObj, return false; }(); - auto haveNameClash = [](const FilePair& fileObj) + auto haveNameClash = [](const FilePair& file) { - return ::haveNameClash(fileObj.getPairShortName(), fileObj.parent().refSubLinks()) || - ::haveNameClash(fileObj.getPairShortName(), fileObj.parent().refSubDirs()); + return ::haveNameClash(file.getPairItemName(), file.parent().refSubLinks()) || + ::haveNameClash(file.getPairItemName(), file.parent().refSubFolders()); }; - if (sourceWillBeDeleted || haveNameClash(sourceObj)) + if (sourceWillBeDeleted || haveNameClash(sourceFile)) { //prepare for move now: - revert to 2-step move on name clashes - if (haveNameClash(targetObj) || - !createParentDir(targetObj)) //throw FileError - return prepare2StepMove<side>(sourceObj, targetObj); //throw FileError + if (haveNameClash(targetFile) || + !createParentFolder(targetFile)) //throw FileError + return prepare2StepMove<side>(sourceFile, targetFile); //throw FileError //finally start move! this should work now: - synchronizeFile(targetObj); //throw FileError - //SynchronizeFolderPair::synchronizeFileInt() is *not* expecting SO_MOVE_LEFT_SOURCE/SO_MOVE_RIGHT_SOURCE => start move from targetObj, not sourceObj! + synchronizeFile(targetFile); //throw FileError + //SynchronizeFolderPair::synchronizeFileInt() is *not* expecting SO_MOVE_LEFT_SOURCE/SO_MOVE_RIGHT_SOURCE => start move from targetFile, not sourceFile! } - //else: sourceObj will not be deleted, and is not standing in the way => delay to second pass + //else: sourceFile will not be deleted, and is not standing in the way => delay to second pass //note: this case may include new "move sources" from two-step sub-routine!!! } @@ -918,16 +900,16 @@ void SynchronizeFolderPair::manageFileMove(FilePair& sourceObj, //search for file move-operations void SynchronizeFolderPair::runZeroPass(HierarchyObject& hierObj) { - for (FilePair& fileObj : hierObj.refSubFiles()) + for (FilePair& file : hierObj.refSubFiles()) { - const SyncOperation syncOp = fileObj.getSyncOperation(); + const SyncOperation syncOp = file.getSyncOperation(); switch (syncOp) //evaluate comparison result and sync direction { case SO_MOVE_LEFT_SOURCE: case SO_MOVE_RIGHT_SOURCE: - if (FilePair* targetObj = dynamic_cast<FilePair*>(FileSystemObject::retrieve(fileObj.getMoveRef()))) + if (FilePair* targetObj = dynamic_cast<FilePair*>(FileSystemObject::retrieve(file.getMoveRef()))) { - FilePair* sourceObj = &fileObj; + FilePair* sourceObj = &file; assert(dynamic_cast<FilePair*>(FileSystemObject::retrieve(targetObj->getMoveRef())) == sourceObj); zen::Opt<std::wstring> errMsg = tryReportingError([&] @@ -979,8 +961,8 @@ void SynchronizeFolderPair::runZeroPass(HierarchyObject& hierObj) } } - for (DirPair& dirObj : hierObj.refSubDirs()) - runZeroPass(dirObj); //recurse + for (FolderPair& folder : hierObj.refSubFolders()) + runZeroPass(folder); //recurse } //--------------------------------------------------------------------------------------------------------------- @@ -990,19 +972,19 @@ void SynchronizeFolderPair::runZeroPass(HierarchyObject& hierObj) // - support change in type: overwrite file by directory, symlink by file, ect. inline -SynchronizeFolderPair::PassId SynchronizeFolderPair::getPass(const FilePair& fileObj) +SynchronizeFolderPair::PassId SynchronizeFolderPair::getPass(const FilePair& file) { - switch (fileObj.getSyncOperation()) //evaluate comparison result and sync direction + switch (file.getSyncOperation()) //evaluate comparison result and sync direction { case SO_DELETE_LEFT: case SO_DELETE_RIGHT: return PASS_ONE; case SO_OVERWRITE_LEFT: - return fileObj.getFileSize<LEFT_SIDE>() > fileObj.getFileSize<RIGHT_SIDE>() ? PASS_ONE : PASS_TWO; + return file.getFileSize<LEFT_SIDE>() > file.getFileSize<RIGHT_SIDE>() ? PASS_ONE : PASS_TWO; case SO_OVERWRITE_RIGHT: - return fileObj.getFileSize<LEFT_SIDE>() < fileObj.getFileSize<RIGHT_SIDE>() ? PASS_ONE : PASS_TWO; + return file.getFileSize<LEFT_SIDE>() < file.getFileSize<RIGHT_SIDE>() ? PASS_ONE : PASS_TWO; case SO_MOVE_LEFT_SOURCE: // case SO_MOVE_RIGHT_SOURCE: // [!] @@ -1028,9 +1010,9 @@ SynchronizeFolderPair::PassId SynchronizeFolderPair::getPass(const FilePair& fil inline -SynchronizeFolderPair::PassId SynchronizeFolderPair::getPass(const SymlinkPair& linkObj) +SynchronizeFolderPair::PassId SynchronizeFolderPair::getPass(const SymlinkPair& link) { - switch (linkObj.getSyncOperation()) //evaluate comparison result and sync direction + switch (link.getSyncOperation()) //evaluate comparison result and sync direction { case SO_DELETE_LEFT: case SO_DELETE_RIGHT: @@ -1060,9 +1042,9 @@ SynchronizeFolderPair::PassId SynchronizeFolderPair::getPass(const SymlinkPair& inline -SynchronizeFolderPair::PassId SynchronizeFolderPair::getPass(const DirPair& dirObj) +SynchronizeFolderPair::PassId SynchronizeFolderPair::getPass(const FolderPair& folder) { - switch (dirObj.getSyncOperation()) //evaluate comparison result and sync direction + switch (folder.getSyncOperation()) //evaluate comparison result and sync direction { case SO_DELETE_LEFT: case SO_DELETE_RIGHT: @@ -1095,22 +1077,22 @@ template <SynchronizeFolderPair::PassId pass> void SynchronizeFolderPair::runPass(HierarchyObject& hierObj) { //synchronize files: - for (FilePair& fileObj : hierObj.refSubFiles()) - if (pass == this->getPass(fileObj)) //"this->" required by two-pass lookup as enforced by GCC 4.7 - tryReportingError([&] { synchronizeFile(fileObj); }, procCallback_); //throw X? + for (FilePair& file : hierObj.refSubFiles()) + if (pass == this->getPass(file)) //"this->" required by two-pass lookup as enforced by GCC 4.7 + tryReportingError([&] { synchronizeFile(file); }, procCallback_); //throw X? //synchronize symbolic links: - for (SymlinkPair& linkObj : hierObj.refSubLinks()) - if (pass == this->getPass(linkObj)) - tryReportingError([&] { synchronizeLink(linkObj); }, procCallback_); //throw X? + for (SymlinkPair& symlink : hierObj.refSubLinks()) + if (pass == this->getPass(symlink)) + tryReportingError([&] { synchronizeLink(symlink); }, procCallback_); //throw X? //synchronize folders: - for (DirPair& dirObj : hierObj.refSubDirs()) + for (FolderPair& folder : hierObj.refSubFolders()) { - if (pass == this->getPass(dirObj)) - tryReportingError([&] { synchronizeFolder(dirObj); }, procCallback_); //throw X? + if (pass == this->getPass(folder)) + tryReportingError([&] { synchronizeFolder(folder); }, procCallback_); //throw X? - this->runPass<pass>(dirObj); //recurse + this->runPass<pass>(folder); //recurse } } @@ -1147,22 +1129,22 @@ Opt<SelectedSide> getTargetDirection(SyncOperation syncOp) inline -void SynchronizeFolderPair::synchronizeFile(FilePair& fileObj) +void SynchronizeFolderPair::synchronizeFile(FilePair& file) { - const SyncOperation syncOp = fileObj.getSyncOperation(); + const SyncOperation syncOp = file.getSyncOperation(); if (Opt<SelectedSide> sideTrg = getTargetDirection(syncOp)) { if (*sideTrg == LEFT_SIDE) - synchronizeFileInt<LEFT_SIDE>(fileObj, syncOp); + synchronizeFileInt<LEFT_SIDE>(file, syncOp); else - synchronizeFileInt<RIGHT_SIDE>(fileObj, syncOp); + synchronizeFileInt<RIGHT_SIDE>(file, syncOp); } } template <SelectedSide sideTrg> -void SynchronizeFolderPair::synchronizeFileInt(FilePair& fileObj, SyncOperation syncOp) +void SynchronizeFolderPair::synchronizeFileInt(FilePair& file, SyncOperation syncOp) { static const SelectedSide sideSrc = OtherSide<sideTrg>::result; @@ -1171,44 +1153,44 @@ void SynchronizeFolderPair::synchronizeFileInt(FilePair& fileObj, SyncOperation case SO_CREATE_NEW_LEFT: case SO_CREATE_NEW_RIGHT: { - if (const DirPair* parentDir = dynamic_cast<DirPair*>(&fileObj.parent())) - if (parentDir->isEmpty<sideTrg>()) //BaseDirPair OTOH is always non-empty and existing in this context => else: fatal error in zen::synchronize() + if (auto parentFolder = dynamic_cast<const FolderPair*>(&file.parent())) + if (parentFolder->isEmpty<sideTrg>()) //BaseFolderPair OTOH is always non-empty and existing in this context => else: fatal error in zen::synchronize() return; //if parent directory creation failed, there's no reason to show more errors! //can't use "getAbstractPath<sideTrg>()" as file name is not available! - const AbstractPathRef targetPath = fileObj.getABF<sideTrg>().getAbstractPath(fileObj.getRelativePath<sideSrc>()); - reportInfo(txtCreatingFile, ABF::getDisplayPath(targetPath)); + const AbstractPath targetPath = AFS::appendRelPath(file.base().getAbstractPath<sideTrg>(), file.getRelativePath<sideSrc>()); + reportInfo(txtCreatingFile, AFS::getDisplayPath(targetPath)); - StatisticsReporter statReporter(1, fileObj.getFileSize<sideSrc>(), procCallback_); + StatisticsReporter statReporter(1, file.getFileSize<sideSrc>(), procCallback_); try { auto onNotifyCopyStatus = [&](std::int64_t bytesDelta) { statReporter.reportDelta(0, bytesDelta); }; - const ABF::FileAttribAfterCopy newAttr = copyFileWithCallback(fileObj.getAbstractPath<sideSrc>(), + const AFS::FileAttribAfterCopy newAttr = copyFileWithCallback(file.getAbstractPath<sideSrc>(), targetPath, nullptr, //no target to delete onNotifyCopyStatus); //throw FileError statReporter.reportDelta(1, 0); //update FilePair - fileObj.setSyncedTo<sideTrg>(fileObj.getItemName<sideSrc>(), newAttr.fileSize, - newAttr.modificationTime, //target time set from source - newAttr.modificationTime, - newAttr.targetFileId, - newAttr.sourceFileId, - false, fileObj.isFollowedSymlink<sideSrc>()); + file.setSyncedTo<sideTrg>(file.getItemName<sideSrc>(), newAttr.fileSize, + newAttr.modificationTime, //target time set from source + newAttr.modificationTime, + newAttr.targetFileId, + newAttr.sourceFileId, + false, file.isFollowedSymlink<sideSrc>()); } catch (FileError&) { warn_static("still an error if base dir is missing!") - // const Zstring basedir = beforeLast(fileObj.getBaseDirPf<side>(), FILE_NAME_SEPARATOR); //what about C:\ ??? + // const Zstring basedir = beforeLast(file.getBaseDirPf<side>(), FILE_NAME_SEPARATOR); //what about C:\ ??? //if (!dirExists(basedir) || - if (!ABF::somethingExists(fileObj.getAbstractPath<sideSrc>())) //do not check on type (symlink, file, folder) -> if there is a type change, FFS should error out! + if (!AFS::somethingExists(file.getAbstractPath<sideSrc>())) //do not check on type (symlink, file, folder) -> if there is a type change, FFS should error out! { //source deleted meanwhile...nothing was done (logical point of view!) - fileObj.removeObject<sideSrc>(); //remove only *after* evaluating "fileObj, sideSrc"! + file.removeObject<sideSrc>(); //remove only *after* evaluating "file, sideSrc"! } else throw; @@ -1219,18 +1201,18 @@ void SynchronizeFolderPair::synchronizeFileInt(FilePair& fileObj, SyncOperation case SO_DELETE_LEFT: case SO_DELETE_RIGHT: - reportInfo(getDelHandling<sideTrg>().getTxtRemovingFile(), ABF::getDisplayPath(fileObj.getAbstractPath<sideTrg>())); + reportInfo(getDelHandling<sideTrg>().getTxtRemovingFile(), AFS::getDisplayPath(file.getAbstractPath<sideTrg>())); { StatisticsReporter statReporter(1, 0, procCallback_); auto onNotifyItemDeletion = [&] { statReporter.reportDelta(1, 0); }; auto onNotifyCopyStatus = [&](std::int64_t bytesDelta) { statReporter.reportDelta(0, bytesDelta); }; - getDelHandling<sideTrg>().removeFileWithCallback(fileObj.getAbstractPath<sideTrg>(), fileObj.getPairRelativePath(), onNotifyItemDeletion, onNotifyCopyStatus); //throw FileError + getDelHandling<sideTrg>().removeFileWithCallback(file.getAbstractPath<sideTrg>(), file.getPairRelativePath(), onNotifyItemDeletion, onNotifyCopyStatus); //throw FileError warn_static("what if item not found? still an error if base dir is missing; externally deleted otherwise!") - fileObj.removeObject<sideTrg>(); //update FilePair + file.removeObject<sideTrg>(); //update FilePair statReporter.reportFinished(); } @@ -1238,19 +1220,19 @@ void SynchronizeFolderPair::synchronizeFileInt(FilePair& fileObj, SyncOperation case SO_MOVE_LEFT_TARGET: case SO_MOVE_RIGHT_TARGET: - if (FilePair* moveSource = dynamic_cast<FilePair*>(FileSystemObject::retrieve(fileObj.getMoveRef()))) + if (FilePair* moveSource = dynamic_cast<FilePair*>(FileSystemObject::retrieve(file.getMoveRef()))) { - FilePair* moveTarget = &fileObj; + FilePair* moveTarget = &file; assert((moveSource->getSyncOperation() == SO_MOVE_LEFT_SOURCE && moveTarget->getSyncOperation() == SO_MOVE_LEFT_TARGET && sideTrg == LEFT_SIDE) || (moveSource->getSyncOperation() == SO_MOVE_RIGHT_SOURCE && moveTarget->getSyncOperation() == SO_MOVE_RIGHT_TARGET && sideTrg == RIGHT_SIDE)); - const AbstractPathRef oldItem = moveSource->getAbstractPath<sideTrg>(); - const AbstractPathRef newItem = moveTarget->getABF<sideTrg>().getAbstractPath(moveTarget->getRelativePath<sideSrc>()); + const AbstractPath oldPath = moveSource->getAbstractPath<sideTrg>(); + const AbstractPath newPath = AFS::appendRelPath(moveTarget->base().getAbstractPath<sideTrg>(), moveTarget->getRelativePath<sideSrc>()); - reportInfo(txtMovingFile, ABF::getDisplayPath(oldItem), ABF::getDisplayPath(newItem)); + reportInfo(txtMovingFile, AFS::getDisplayPath(oldPath), AFS::getDisplayPath(newPath)); warn_static("was wenn diff volume: symlink aliasing!") - ABF::renameItem(oldItem, newItem); //throw FileError, (ErrorTargetExisting, ErrorDifferentVolume) + AFS::renameItem(oldPath, newPath); //throw FileError, (ErrorTargetExisting, ErrorDifferentVolume) //update FilePair assert(moveSource->getFileSize<sideTrg>() == moveTarget->getFileSize<sideSrc>()); @@ -1271,50 +1253,52 @@ void SynchronizeFolderPair::synchronizeFileInt(FilePair& fileObj, SyncOperation case SO_OVERWRITE_LEFT: case SO_OVERWRITE_RIGHT: { - const AbstractPathRef resolvedTargetPath = fileObj.isFollowedSymlink<sideTrg>() ? //follow link when updating file rather than delete it and replace with regular file!!! - ABF::getResolvedSymlinkPath(fileObj.getAbstractPath<sideTrg>()) : //throw FileError - fileObj.getABF<sideTrg>().getAbstractPath(fileObj.getRelativePath<sideSrc>()); //respect differences in case of source object + //respect differences in case of source object: + const AbstractPath targetPathLogical = AFS::appendRelPath(file.base().getAbstractPath<sideTrg>(), file.getRelativePath<sideSrc>()); - reportInfo(txtOverwritingFile, ABF::getDisplayPath(resolvedTargetPath)); + const AbstractPath targetPathResolved = file.isFollowedSymlink<sideTrg>() ? //follow link when updating file rather than delete it and replace with regular file!!! + AFS::getResolvedSymlinkPath(file.getAbstractPath<sideTrg>()) : //throw FileError + targetPathLogical; //respect differences in case of source object - if (fileObj.isFollowedSymlink<sideTrg>()) //since we follow the link, we need to sync case sensitivity of the link manually! - if (fileObj.getItemName<sideTrg>() != fileObj.getItemName<sideSrc>()) //have difference in case? - ABF::renameItem(fileObj.getAbstractPath<sideTrg>(), //throw FileError, (ErrorTargetExisting, ErrorDifferentVolume) - fileObj.getABF<sideTrg>().getAbstractPath(fileObj.getRelativePath<sideSrc>())); + reportInfo(txtOverwritingFile, AFS::getDisplayPath(targetPathResolved)); - StatisticsReporter statReporter(1, fileObj.getFileSize<sideSrc>(), procCallback_); + if (file.isFollowedSymlink<sideTrg>()) //since we follow the link, we need to sync case sensitivity of the link manually! + if (file.getItemName<sideTrg>() != file.getItemName<sideSrc>()) //have difference in case? + AFS::renameItem(file.getAbstractPath<sideTrg>(), targetPathLogical); //throw FileError, (ErrorTargetExisting, ErrorDifferentVolume) + + StatisticsReporter statReporter(1, file.getFileSize<sideSrc>(), procCallback_); auto onNotifyCopyStatus = [&](std::int64_t bytesDelta) { statReporter.reportDelta(0, bytesDelta); }; auto onDeleteTargetFile = [&] //delete target at appropriate time { - reportStatus(this->getDelHandling<sideTrg>().getTxtRemovingFile(), ABF::getDisplayPath(resolvedTargetPath)); + reportStatus(this->getDelHandling<sideTrg>().getTxtRemovingFile(), AFS::getDisplayPath(targetPathResolved)); - this->getDelHandling<sideTrg>().removeFileWithCallback(resolvedTargetPath, fileObj.getPairRelativePath(), [] {}, onNotifyCopyStatus); //throw FileError; + this->getDelHandling<sideTrg>().removeFileWithCallback(targetPathResolved, file.getPairRelativePath(), [] {}, onNotifyCopyStatus); //throw FileError; //no (logical) item count update desired - but total byte count may change, e.g. move(copy) deleted file to versioning dir - //fileObj.removeObject<sideTrg>(); -> doesn't make sense for isFollowedSymlink(); "fileObj, sideTrg" evaluated below! + //file.removeObject<sideTrg>(); -> doesn't make sense for isFollowedSymlink(); "file, sideTrg" evaluated below! //if fail-safe file copy is active, then the next operation will be a simple "rename" //=> don't risk reportStatus() throwing GuiAbortProcess() leaving the target deleted rather than updated! if (!transactionalFileCopy_) - reportStatus(txtOverwritingFile, ABF::getDisplayPath(resolvedTargetPath)); //restore status text copy file + reportStatus(txtOverwritingFile, AFS::getDisplayPath(targetPathResolved)); //restore status text copy file }; - const ABF::FileAttribAfterCopy newAttr = copyFileWithCallback(fileObj.getAbstractPath<sideSrc>(), - resolvedTargetPath, + const AFS::FileAttribAfterCopy newAttr = copyFileWithCallback(file.getAbstractPath<sideSrc>(), + targetPathResolved, onDeleteTargetFile, onNotifyCopyStatus); //throw FileError statReporter.reportDelta(1, 0); //we model "delete + copy" as ONE logical operation //update FilePair - fileObj.setSyncedTo<sideTrg>(fileObj.getItemName<sideSrc>(), newAttr.fileSize, - newAttr.modificationTime, //target time set from source - newAttr.modificationTime, - newAttr.targetFileId, - newAttr.sourceFileId, - fileObj.isFollowedSymlink<sideTrg>(), - fileObj.isFollowedSymlink<sideSrc>()); + file.setSyncedTo<sideTrg>(file.getItemName<sideSrc>(), newAttr.fileSize, + newAttr.modificationTime, //target time set from source + newAttr.modificationTime, + newAttr.targetFileId, + newAttr.sourceFileId, + file.isFollowedSymlink<sideTrg>(), + file.isFollowedSymlink<sideSrc>()); statReporter.reportFinished(); } @@ -1324,26 +1308,26 @@ void SynchronizeFolderPair::synchronizeFileInt(FilePair& fileObj, SyncOperation case SO_COPY_METADATA_TO_RIGHT: //harmonize with file_hierarchy.cpp::getSyncOpDescription!! - reportInfo(txtWritingAttributes, ABF::getDisplayPath(fileObj.getAbstractPath<sideTrg>())); + reportInfo(txtWritingAttributes, AFS::getDisplayPath(file.getAbstractPath<sideTrg>())); - if (fileObj.getItemName<sideTrg>() != fileObj.getItemName<sideSrc>()) //have difference in case? - ABF::renameItem(fileObj.getAbstractPath<sideTrg>(), //throw FileError, (ErrorTargetExisting, ErrorDifferentVolume) - fileObj.getABF<sideTrg>().getAbstractPath(fileObj.getRelativePath<sideSrc>())); + if (file.getItemName<sideTrg>() != file.getItemName<sideSrc>()) //have difference in case? + AFS::renameItem(file.getAbstractPath<sideTrg>(), //throw FileError, (ErrorTargetExisting, ErrorDifferentVolume) + AFS::appendRelPath(file.base().getAbstractPath<sideTrg>(), file.getRelativePath<sideSrc>())); - if (fileObj.getLastWriteTime<sideTrg>() != fileObj.getLastWriteTime<sideSrc>()) + if (file.getLastWriteTime<sideTrg>() != file.getLastWriteTime<sideSrc>()) //- no need to call sameFileTime() or respect 2 second FAT/FAT32 precision in this comparison //- do NOT read *current* source file time, but use buffered value which corresponds to time of comparison! - ABF::setModTime(fileObj.getAbstractPath<sideTrg>(), fileObj.getLastWriteTime<sideSrc>()); //throw FileError + AFS::setModTime(file.getAbstractPath<sideTrg>(), file.getLastWriteTime<sideSrc>()); //throw FileError //-> both sides *should* be completely equal now... - assert(fileObj.getFileSize<sideTrg>() == fileObj.getFileSize<sideSrc>()); - fileObj.setSyncedTo<sideTrg>(fileObj.getItemName<sideSrc>(), fileObj.getFileSize<sideSrc>(), - fileObj.getLastWriteTime<sideSrc>(), //target time set from source - fileObj.getLastWriteTime<sideSrc>(), - fileObj.getFileId <sideTrg>(), - fileObj.getFileId <sideSrc>(), - fileObj.isFollowedSymlink<sideTrg>(), - fileObj.isFollowedSymlink<sideSrc>()); + assert(file.getFileSize<sideTrg>() == file.getFileSize<sideSrc>()); + file.setSyncedTo<sideTrg>(file.getItemName<sideSrc>(), file.getFileSize<sideSrc>(), + file.getLastWriteTime<sideSrc>(), //target time set from source + file.getLastWriteTime<sideSrc>(), + file.getFileId <sideTrg>(), + file.getFileId <sideSrc>(), + file.isFollowedSymlink<sideTrg>(), + file.isFollowedSymlink<sideSrc>()); procCallback_.updateProcessedData(1, 0); break; @@ -1362,22 +1346,22 @@ void SynchronizeFolderPair::synchronizeFileInt(FilePair& fileObj, SyncOperation inline -void SynchronizeFolderPair::synchronizeLink(SymlinkPair& linkObj) +void SynchronizeFolderPair::synchronizeLink(SymlinkPair& link) { - const SyncOperation syncOp = linkObj.getSyncOperation(); + const SyncOperation syncOp = link.getSyncOperation(); if (Opt<SelectedSide> sideTrg = getTargetDirection(syncOp)) { if (*sideTrg == LEFT_SIDE) - synchronizeLinkInt<LEFT_SIDE>(linkObj, syncOp); + synchronizeLinkInt<LEFT_SIDE>(link, syncOp); else - synchronizeLinkInt<RIGHT_SIDE>(linkObj, syncOp); + synchronizeLinkInt<RIGHT_SIDE>(link, syncOp); } } template <SelectedSide sideTrg> -void SynchronizeFolderPair::synchronizeLinkInt(SymlinkPair& linkObj, SyncOperation syncOp) +void SynchronizeFolderPair::synchronizeLinkInt(SymlinkPair& symlink, SyncOperation syncOp) { static const SelectedSide sideSrc = OtherSide<sideTrg>::result; @@ -1386,21 +1370,21 @@ void SynchronizeFolderPair::synchronizeLinkInt(SymlinkPair& linkObj, SyncOperati case SO_CREATE_NEW_LEFT: case SO_CREATE_NEW_RIGHT: { - if (const DirPair* parentDir = dynamic_cast<DirPair*>(&linkObj.parent())) - if (parentDir->isEmpty<sideTrg>()) //BaseDirPair OTOH is always non-empty and existing in this context => else: fatal error in zen::synchronize() + if (auto parentFolder = dynamic_cast<const FolderPair*>(&symlink.parent())) + if (parentFolder->isEmpty<sideTrg>()) //BaseFolderPair OTOH is always non-empty and existing in this context => else: fatal error in zen::synchronize() return; //if parent directory creation failed, there's no reason to show more errors! - const AbstractPathRef targetPath = linkObj.getABF<sideTrg>().getAbstractPath(linkObj.getRelativePath<sideSrc>()); - reportInfo(txtCreatingLink, ABF::getDisplayPath(targetPath)); + const AbstractPath targetPath = AFS::appendRelPath(symlink.base().getAbstractPath<sideTrg>(), symlink.getRelativePath<sideSrc>()); + reportInfo(txtCreatingLink, AFS::getDisplayPath(targetPath)); StatisticsReporter statReporter(1, 0, procCallback_); try { - ABF::copySymlink(linkObj.getAbstractPath<sideSrc>(), targetPath, copyFilePermissions_); //throw FileError + AFS::copySymlink(symlink.getAbstractPath<sideSrc>(), targetPath, copyFilePermissions_); //throw FileError //update SymlinkPair - linkObj.setSyncedTo<sideTrg>(linkObj.getItemName<sideSrc>(), - linkObj.getLastWriteTime<sideSrc>(), //target time set from source - linkObj.getLastWriteTime<sideSrc>()); + symlink.setSyncedTo<sideTrg>(symlink.getItemName<sideSrc>(), + symlink.getLastWriteTime<sideSrc>(), //target time set from source + symlink.getLastWriteTime<sideSrc>()); statReporter.reportDelta(1, 0); } @@ -1408,10 +1392,10 @@ void SynchronizeFolderPair::synchronizeLinkInt(SymlinkPair& linkObj, SyncOperati { warn_static("still an error if base dir is missing!") - if (ABF::somethingExists(linkObj.getAbstractPath<sideSrc>())) //do not check on type (symlink, file, folder) -> if there is a type change, FFS should not be quiet about it! + if (AFS::somethingExists(symlink.getAbstractPath<sideSrc>())) //do not check on type (symlink, file, folder) -> if there is a type change, FFS should not be quiet about it! throw; //source deleted meanwhile...nothing was done (logical point of view!) - linkObj.removeObject<sideSrc>(); + symlink.removeObject<sideSrc>(); } statReporter.reportFinished(); } @@ -1419,16 +1403,16 @@ void SynchronizeFolderPair::synchronizeLinkInt(SymlinkPair& linkObj, SyncOperati case SO_DELETE_LEFT: case SO_DELETE_RIGHT: - reportInfo(getDelHandling<sideTrg>().getTxtRemovingSymLink(), ABF::getDisplayPath(linkObj.getAbstractPath<sideTrg>())); + reportInfo(getDelHandling<sideTrg>().getTxtRemovingSymLink(), AFS::getDisplayPath(symlink.getAbstractPath<sideTrg>())); { StatisticsReporter statReporter(1, 0, procCallback_); auto onNotifyItemDeletion = [&] { statReporter.reportDelta(1, 0); }; auto onNotifyCopyStatus = [&](std::int64_t bytesDelta) { statReporter.reportDelta(0, bytesDelta); }; - getDelHandling<sideTrg>().removeLinkWithCallback(linkObj.getAbstractPath<sideTrg>(), linkObj.getPairRelativePath(), onNotifyItemDeletion, onNotifyCopyStatus); //throw FileError + getDelHandling<sideTrg>().removeLinkWithCallback(symlink.getAbstractPath<sideTrg>(), symlink.getPairRelativePath(), onNotifyItemDeletion, onNotifyCopyStatus); //throw FileError - linkObj.removeObject<sideTrg>(); //update SymlinkPair + symlink.removeObject<sideTrg>(); //update SymlinkPair statReporter.reportFinished(); } @@ -1436,30 +1420,30 @@ void SynchronizeFolderPair::synchronizeLinkInt(SymlinkPair& linkObj, SyncOperati case SO_OVERWRITE_LEFT: case SO_OVERWRITE_RIGHT: - reportInfo(txtOverwritingLink, ABF::getDisplayPath(linkObj.getAbstractPath<sideTrg>())); + reportInfo(txtOverwritingLink, AFS::getDisplayPath(symlink.getAbstractPath<sideTrg>())); { StatisticsReporter statReporter(1, 0, procCallback_); auto onNotifyCopyStatus = [&](std::int64_t bytesDelta) { statReporter.reportDelta(0, bytesDelta); }; - //reportStatus(getDelHandling<sideTrg>().getTxtRemovingSymLink(), ABF::getDisplayPath(linkObj.getAbstractPath<sideTrg>())); - getDelHandling<sideTrg>().removeLinkWithCallback(linkObj.getAbstractPath<sideTrg>(), linkObj.getPairRelativePath(), [] {}, onNotifyCopyStatus); //throw FileError + //reportStatus(getDelHandling<sideTrg>().getTxtRemovingSymLink(), AFS::getDisplayPath(symlink.getAbstractPath<sideTrg>())); + getDelHandling<sideTrg>().removeLinkWithCallback(symlink.getAbstractPath<sideTrg>(), symlink.getPairRelativePath(), [] {}, onNotifyCopyStatus); //throw FileError - //linkObj.removeObject<sideTrg>(); -> "linkObj, sideTrg" evaluated below! + //symlink.removeObject<sideTrg>(); -> "symlink, sideTrg" evaluated below! //=> don't risk reportStatus() throwing GuiAbortProcess() leaving the target deleted rather than updated: - //reportStatus(txtOverwritingLink, ABF::getDisplayPath(linkObj.getAbstractPath<sideTrg>())); //restore status text + //reportStatus(txtOverwritingLink, AFS::getDisplayPath(symlink.getAbstractPath<sideTrg>())); //restore status text - ABF::copySymlink(linkObj.getAbstractPath<sideSrc>(), - linkObj.getABF<sideTrg>().getAbstractPath(linkObj.getRelativePath<sideSrc>()), //respect differences in case of source object + AFS::copySymlink(symlink.getAbstractPath<sideSrc>(), + AFS::appendRelPath(symlink.base().getAbstractPath<sideTrg>(), symlink.getRelativePath<sideSrc>()), //respect differences in case of source object copyFilePermissions_); //throw FileError statReporter.reportDelta(1, 0); //we model "delete + copy" as ONE logical operation //update SymlinkPair - linkObj.setSyncedTo<sideTrg>(linkObj.getItemName<sideSrc>(), - linkObj.getLastWriteTime<sideSrc>(), //target time set from source - linkObj.getLastWriteTime<sideSrc>()); + symlink.setSyncedTo<sideTrg>(symlink.getItemName<sideSrc>(), + symlink.getLastWriteTime<sideSrc>(), //target time set from source + symlink.getLastWriteTime<sideSrc>()); statReporter.reportFinished(); } @@ -1467,21 +1451,21 @@ void SynchronizeFolderPair::synchronizeLinkInt(SymlinkPair& linkObj, SyncOperati case SO_COPY_METADATA_TO_LEFT: case SO_COPY_METADATA_TO_RIGHT: - reportInfo(txtWritingAttributes, ABF::getDisplayPath(linkObj.getAbstractPath<sideTrg>())); + reportInfo(txtWritingAttributes, AFS::getDisplayPath(symlink.getAbstractPath<sideTrg>())); - if (linkObj.getItemName<sideTrg>() != linkObj.getItemName<sideSrc>()) //have difference in case? - ABF::renameItem(linkObj.getAbstractPath<sideTrg>(), //throw FileError, (ErrorTargetExisting, ErrorDifferentVolume) - linkObj.getABF<sideTrg>().getAbstractPath(linkObj.getRelativePath<sideSrc>())); + if (symlink.getItemName<sideTrg>() != symlink.getItemName<sideSrc>()) //have difference in case? + AFS::renameItem(symlink.getAbstractPath<sideTrg>(), //throw FileError, (ErrorTargetExisting, ErrorDifferentVolume) + AFS::appendRelPath(symlink.base().getAbstractPath<sideTrg>(), symlink.getRelativePath<sideSrc>())); - if (linkObj.getLastWriteTime<sideTrg>() != linkObj.getLastWriteTime<sideSrc>()) + if (symlink.getLastWriteTime<sideTrg>() != symlink.getLastWriteTime<sideSrc>()) //- no need to call sameFileTime() or respect 2 second FAT/FAT32 precision in this comparison //- do NOT read *current* source file time, but use buffered value which corresponds to time of comparison! - ABF::setModTimeSymlink(linkObj.getAbstractPath<sideTrg>(), linkObj.getLastWriteTime<sideSrc>()); //throw FileError + AFS::setModTimeSymlink(symlink.getAbstractPath<sideTrg>(), symlink.getLastWriteTime<sideSrc>()); //throw FileError //-> both sides *should* be completely equal now... - linkObj.setSyncedTo<sideTrg>(linkObj.getItemName<sideSrc>(), - linkObj.getLastWriteTime<sideSrc>(), //target time set from source - linkObj.getLastWriteTime<sideSrc>()); + symlink.setSyncedTo<sideTrg>(symlink.getItemName<sideSrc>(), + symlink.getLastWriteTime<sideSrc>(), //target time set from source + symlink.getLastWriteTime<sideSrc>()); procCallback_.updateProcessedData(1, 0); break; @@ -1502,22 +1486,22 @@ void SynchronizeFolderPair::synchronizeLinkInt(SymlinkPair& linkObj, SyncOperati inline -void SynchronizeFolderPair::synchronizeFolder(DirPair& dirObj) +void SynchronizeFolderPair::synchronizeFolder(FolderPair& folder) { - const SyncOperation syncOp = dirObj.getSyncOperation(); + const SyncOperation syncOp = folder.getSyncOperation(); if (Opt<SelectedSide> sideTrg = getTargetDirection(syncOp)) { if (*sideTrg == LEFT_SIDE) - synchronizeFolderInt<LEFT_SIDE>(dirObj, syncOp); + synchronizeFolderInt<LEFT_SIDE>(folder, syncOp); else - synchronizeFolderInt<RIGHT_SIDE>(dirObj, syncOp); + synchronizeFolderInt<RIGHT_SIDE>(folder, syncOp); } } template <SelectedSide sideTrg> -void SynchronizeFolderPair::synchronizeFolderInt(DirPair& dirObj, SyncOperation syncOp) +void SynchronizeFolderPair::synchronizeFolderInt(FolderPair& folder, SyncOperation syncOp) { static const SelectedSide sideSrc = OtherSide<sideTrg>::result; @@ -1525,24 +1509,24 @@ void SynchronizeFolderPair::synchronizeFolderInt(DirPair& dirObj, SyncOperation { case SO_CREATE_NEW_LEFT: case SO_CREATE_NEW_RIGHT: - if (const DirPair* parentDir = dynamic_cast<DirPair*>(&dirObj.parent())) - if (parentDir->isEmpty<sideTrg>()) //BaseDirPair OTOH is always non-empty and existing in this context => else: fatal error in zen::synchronize() + if (auto parentFolder = dynamic_cast<const FolderPair*>(&folder.parent())) + if (parentFolder->isEmpty<sideTrg>()) //BaseFolderPair OTOH is always non-empty and existing in this context => else: fatal error in zen::synchronize() return; //if parent directory creation failed, there's no reason to show more errors! warn_static("save this file access?") - if (ABF::somethingExists(dirObj.getAbstractPath<sideSrc>())) //do not check on type (symlink, file, folder) -> if there is a type change, FFS should error out! + if (AFS::somethingExists(folder.getAbstractPath<sideSrc>())) //do not check on type (symlink, file, folder) -> if there is a type change, FFS should error out! { - const AbstractPathRef targetPath = dirObj.getABF<sideTrg>().getAbstractPath(dirObj.getRelativePath<sideSrc>()); - reportInfo(txtCreatingFolder, ABF::getDisplayPath(targetPath)); + const AbstractPath targetPath = AFS::appendRelPath(folder.base().getAbstractPath<sideTrg>(), folder.getRelativePath<sideSrc>()); + reportInfo(txtCreatingFolder, AFS::getDisplayPath(targetPath)); try { - ABF::copyNewFolder(dirObj.getAbstractPath<sideSrc>(), targetPath, copyFilePermissions_); //throw FileError + AFS::copyNewFolder(folder.getAbstractPath<sideSrc>(), targetPath, copyFilePermissions_); //throw FileError } - catch (const FileError&) { if (!ABF::folderExists(targetPath)) throw; } + catch (const FileError&) { if (!AFS::folderExists(targetPath)) throw; } - //update DirPair - dirObj.setSyncedTo(dirObj.getItemName<sideSrc>()); + //update FolderPair + folder.setSyncedTo(folder.getItemName<sideSrc>()); procCallback_.updateProcessedData(1, 0); } @@ -1550,34 +1534,34 @@ void SynchronizeFolderPair::synchronizeFolderInt(DirPair& dirObj, SyncOperation { warn_static("still an error if base dir is missing!") - const SyncStatistics subStats(dirObj); + const SyncStatistics subStats(folder); procCallback_.updateTotalData(-getCUD(subStats) - 1, -subStats.getDataToProcess()); - //remove only *after* evaluating dirObj!! - dirObj.refSubFiles().clear(); // - dirObj.refSubLinks().clear(); //update DirPair - dirObj.refSubDirs ().clear(); // - dirObj.removeObject<sideSrc>(); // + //remove only *after* evaluating folder!! + folder.refSubFiles ().clear(); // + folder.refSubLinks ().clear(); //update FolderPair + folder.refSubFolders().clear(); // + folder.removeObject<sideSrc>(); // } break; case SO_DELETE_LEFT: case SO_DELETE_RIGHT: - reportInfo(getDelHandling<sideTrg>().getTxtRemovingDir(), ABF::getDisplayPath(dirObj.getAbstractPath<sideTrg>())); + reportInfo(getDelHandling<sideTrg>().getTxtRemovingDir(), AFS::getDisplayPath(folder.getAbstractPath<sideTrg>())); { - const SyncStatistics subStats(dirObj); //counts sub-objects only! + const SyncStatistics subStats(folder); //counts sub-objects only! StatisticsReporter statReporter(1 + getCUD(subStats), subStats.getDataToProcess(), procCallback_); auto onNotifyItemDeletion = [&] { statReporter.reportDelta(1, 0); }; auto onNotifyCopyStatus = [&](std::int64_t bytesDelta) { statReporter.reportDelta(0, bytesDelta); }; - getDelHandling<sideTrg>().removeDirWithCallback(dirObj.getAbstractPath<sideTrg>(), dirObj.getPairRelativePath(), onNotifyItemDeletion, onNotifyCopyStatus); //throw FileError + getDelHandling<sideTrg>().removeDirWithCallback(folder.getAbstractPath<sideTrg>(), folder.getPairRelativePath(), onNotifyItemDeletion, onNotifyCopyStatus); //throw FileError - dirObj.refSubFiles().clear(); // - dirObj.refSubLinks().clear(); //update DirPair - dirObj.refSubDirs ().clear(); // - dirObj.removeObject<sideTrg>(); // + folder.refSubFiles ().clear(); // + folder.refSubLinks ().clear(); //update FolderPair + folder.refSubFolders().clear(); // + folder.removeObject<sideTrg>(); // statReporter.reportFinished(); } @@ -1587,15 +1571,15 @@ void SynchronizeFolderPair::synchronizeFolderInt(DirPair& dirObj, SyncOperation case SO_OVERWRITE_RIGHT: // case SO_COPY_METADATA_TO_LEFT: case SO_COPY_METADATA_TO_RIGHT: - reportInfo(txtWritingAttributes, ABF::getDisplayPath(dirObj.getAbstractPath<sideTrg>())); + reportInfo(txtWritingAttributes, AFS::getDisplayPath(folder.getAbstractPath<sideTrg>())); - if (dirObj.getItemName<sideTrg>() != dirObj.getItemName<sideSrc>()) //have difference in case? - ABF::renameItem(dirObj.getAbstractPath<sideTrg>(), //throw FileError, (ErrorTargetExisting, ErrorDifferentVolume) - dirObj.getABF<sideTrg>().getAbstractPath(dirObj.getRelativePath<sideSrc>())); + if (folder.getItemName<sideTrg>() != folder.getItemName<sideSrc>()) //have difference in case? + AFS::renameItem(folder.getAbstractPath<sideTrg>(), //throw FileError, (ErrorTargetExisting, ErrorDifferentVolume) + AFS::appendRelPath(folder.base().getAbstractPath<sideTrg>(), folder.getRelativePath<sideSrc>())); //copyFileTimes -> useless: modification time changes with each child-object creation/deletion //-> both sides *should* be completely equal now... - dirObj.setSyncedTo(dirObj.getItemName<sideSrc>()); + folder.setSyncedTo(folder.getItemName<sideSrc>()); procCallback_.updateProcessedData(1, 0); break; @@ -1617,13 +1601,13 @@ void SynchronizeFolderPair::synchronizeFolderInt(DirPair& dirObj, SyncOperation //########################################################################################### //--------------------- data verification ------------------------- -void verifyFiles(const AbstractPathRef& sourcePath, const AbstractPathRef& targetPath, const std::function<void(std::int64_t bytesDelta)>& onUpdateStatus) //throw FileError +void verifyFiles(const AbstractPath& sourcePath, const AbstractPath& targetPath, const std::function<void(std::int64_t bytesDelta)>& onUpdateStatus) //throw FileError { try { //do like "copy /v": 1. flush target file buffers, 2. read again as usual (using OS buffers) // => it seems OS buffered are not invalidated by this: snake oil??? - if (Opt<Zstring> nativeTargetPath = ABF::getNativeItemPath(targetPath)) + if (Opt<Zstring> nativeTargetPath = AFS::getNativeItemPath(targetPath)) { #ifdef ZEN_WIN const HANDLE fileHandle = ::CreateFile(applyLongPathPrefix(*nativeTargetPath).c_str(), //_In_ LPCTSTR lpFileName, @@ -1655,9 +1639,9 @@ void verifyFiles(const AbstractPathRef& sourcePath, const AbstractPathRef& targe if (onUpdateStatus) onUpdateStatus(0); if (!filesHaveSameContent(sourcePath, targetPath, onUpdateStatus)) //throw FileError - throw FileError(replaceCpy(replaceCpy(_("%x and %y have different content."), L"%x", L"\n" + - fmtPath(ABF::getDisplayPath(sourcePath))), L"%y", L"\n" + - fmtPath(ABF::getDisplayPath(targetPath)))); + throw FileError(replaceCpy(replaceCpy(_("%x and %y have different content."), + L"%x", L"\n" + fmtPath(AFS::getDisplayPath(sourcePath))), + L"%y", L"\n" + fmtPath(AFS::getDisplayPath(targetPath)))); } catch (const FileError& e) //add some context to error message { @@ -1666,14 +1650,14 @@ void verifyFiles(const AbstractPathRef& sourcePath, const AbstractPathRef& targe } -ABF::FileAttribAfterCopy SynchronizeFolderPair::copyFileWithCallback(const AbstractPathRef& sourcePath, //throw FileError - const AbstractPathRef& targetPath, +AFS::FileAttribAfterCopy SynchronizeFolderPair::copyFileWithCallback(const AbstractPath& sourcePath, //throw FileError + const AbstractPath& targetPath, const std::function<void()>& onDeleteTargetFile, const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus) const //returns current attributes of source file { - auto copyOperation = [this, &targetPath, &onDeleteTargetFile, &onNotifyCopyStatus](const AbstractPathRef& sourcePathTmp) + auto copyOperation = [this, &targetPath, &onDeleteTargetFile, &onNotifyCopyStatus](const AbstractPath& sourcePathTmp) { - ABF::FileAttribAfterCopy newAttr = ABF::copyFileTransactional(sourcePathTmp, targetPath, //throw FileError, ErrorFileLocked + AFS::FileAttribAfterCopy newAttr = AFS::copyFileTransactional(sourcePathTmp, targetPath, //throw FileError, ErrorFileLocked copyFilePermissions_, transactionalFileCopy_, onDeleteTargetFile, @@ -1682,12 +1666,10 @@ ABF::FileAttribAfterCopy SynchronizeFolderPair::copyFileWithCallback(const Abstr //#################### Verification ############################# if (verifyCopiedFiles_) { - auto guardTarget = makeGuard([&] { ABF::removeFile(targetPath); }); //delete target if verification fails + ZEN_ON_SCOPE_FAIL( AFS::removeFile(targetPath); ); //delete target if verification fails - procCallback_.reportInfo(replaceCpy(txtVerifying, L"%x", fmtPath(ABF::getDisplayPath(targetPath)))); + procCallback_.reportInfo(replaceCpy(txtVerifying, L"%x", fmtPath(AFS::getDisplayPath(targetPath)))); verifyFiles(sourcePathTmp, targetPath, [&](std::int64_t bytesDelta) { procCallback_.requestUiRefresh(); }); //throw FileError - - guardTarget.dismiss(); } //#################### /Verification ############################# @@ -1702,7 +1684,7 @@ ABF::FileAttribAfterCopy SynchronizeFolderPair::copyFileWithCallback(const Abstr catch (ErrorFileLocked& e1) { if (shadowCopyHandler_) //if file is locked (try to) use Windows Volume Shadow Copy Service: - if (Opt<Zstring> nativeSourcePath = ABF::getNativeItemPath(sourcePath)) + if (Opt<Zstring> nativeSourcePath = AFS::getNativeItemPath(sourcePath)) { Zstring nativeShadowPath; //contains prefix: E.g. "\\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Program Files\FFS\sample.dat" try @@ -1719,9 +1701,7 @@ ABF::FileAttribAfterCopy SynchronizeFolderPair::copyFileWithCallback(const Abstr } //now try again - if (std::unique_ptr<AbstractPathRef> shadowPath = ABF::getAbf(sourcePath).getAbstractPathFromNativePath(nativeShadowPath)) - return copyOperation(*shadowPath); - else assert(false); + return copyOperation(createItemPathNative(nativeShadowPath)); } throw; } @@ -1732,34 +1712,53 @@ ABF::FileAttribAfterCopy SynchronizeFolderPair::copyFileWithCallback(const Abstr //########################################################################################### +template <SelectedSide side> +bool baseFolderDrop(BaseFolderPair& baseFolder, ProcessCallback& callback) +{ + const AbstractPath folderPath = baseFolder.getAbstractPath<side>(); + + if (baseFolder.isExisting<side>()) + if (Opt<std::wstring> errMsg = tryReportingError([&] + { + if (!folderExistsNonBlocking(folderPath, false, callback)) + throw FileError(replaceCpy(_("Cannot find folder %x."), L"%x", fmtPath(AFS::getDisplayPath(folderPath)))); + //should really be logged as a "fatal error" if ignored by the user... + }, callback)) //throw X? + return true; + + return false; +} + + template <SelectedSide side> //create base directories first (if not yet existing) -> no symlink or attribute copying! -bool createBaseDirectory(BaseDirPair& baseDirObj, ProcessCallback& callback) //nothrow; return false if fatal error occurred +bool createBaseFolder(BaseFolderPair& baseFolder, ProcessCallback& callback) //nothrow; return false if fatal error occurred { - if (baseDirObj.getABF<side>().emptyBaseFolderPath()) + const AbstractPath baseFolderPath = baseFolder.getAbstractPath<side>(); + + if (AFS::isNullPath(baseFolderPath)) return true; - if (!baseDirObj.isExisting<side>()) //create target directory: user presumably ignored error "dir existing" in order to have it created automatically + if (!baseFolder.isExisting<side>()) //create target directory: user presumably ignored error "dir existing" in order to have it created automatically { bool temporaryNetworkDrop = false; zen::Opt<std::wstring> errMsg = tryReportingError([&] { - const AbstractPathRef baseFolderPath = baseDirObj.getABF<side>().getAbstractPath(); try { //a nice race-free check and set operation: - ABF::createFolderSimple(baseFolderPath); //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing - baseDirObj.setExisting<side>(true); //update our model! + AFS::createFolderSimple(baseFolderPath); //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing + baseFolder.setExisting<side>(true); //update our model! } catch (ErrorTargetPathMissing&) { - ABF::createFolderRecursively(baseFolderPath); //throw FileError - baseDirObj.setExisting<side>(true); //update our model! + AFS::createFolderRecursively(baseFolderPath); //throw FileError + baseFolder.setExisting<side>(true); //update our model! } catch (ErrorTargetExisting&) { //TEMPORARY network drop! base directory not found during comparison, but reappears during synchronization //=> sync-directions are based on false assumptions! Abort. - callback.reportFatalError(replaceCpy(_("Target folder %x already existing."), L"%x", fmtPath(ABF::getDisplayPath(baseFolderPath)))); + callback.reportFatalError(replaceCpy(_("Target folder %x already existing."), L"%x", fmtPath(AFS::getDisplayPath(baseFolderPath)))); temporaryNetworkDrop = true; //Is it possible we're catching a "false-positive" here, could FFS have created the directory indirectly after comparison? @@ -1774,13 +1773,14 @@ bool createBaseDirectory(BaseDirPair& baseDirObj, ProcessCallback& callback) //n return true; } + struct ReadWriteCount { - ReadWriteCount() : reads(), writes() {} - size_t reads; - size_t writes; + size_t reads = 0; + size_t writes = 0; }; + enum class FolderPairJobType { PROCESS, @@ -1829,91 +1829,91 @@ void zen::synchronize(const TimeComp& timeStamp, if (syncConfig.size() != folderCmp.size()) throw std::logic_error("Programming Error: Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__)); - //inform about the total amount of data that will be processed from now on - const SyncStatistics statisticsTotal(folderCmp); - - //keep at beginning so that all gui elements are initialized properly - callback.initNewPhase(getCUD(statisticsTotal), //may throw - statisticsTotal.getDataToProcess(), - ProcessCallback::PHASE_SYNCHRONIZING); + //aggregate basic information + std::vector<SyncStatistics> folderPairStats; + { + int objectsTotal = 0; + int64_t dataTotal = 0; + for (auto j = begin(folderCmp); j != end(folderCmp); ++j) + { + SyncStatistics fpStats(*j); + objectsTotal += getCUD(fpStats); + dataTotal += fpStats.getDataToProcess(); + folderPairStats.push_back(fpStats); + } + //inform about the total amount of data that will be processed from now on + //keep at beginning so that all gui elements are initialized properly + callback.initNewPhase(objectsTotal, //throw X + dataTotal, + ProcessCallback::PHASE_SYNCHRONIZING); + } std::vector<FolderPairJobType> jobType(folderCmp.size(), FolderPairJobType::PROCESS); //folder pairs may be skipped after fatal errors were found //-------------------execute basic checks all at once before starting sync-------------------------------------- - auto dirNotFoundAnymore = [&](const ABF& baseFolder, bool wasExisting) - { - if (wasExisting) - if (Opt<std::wstring> errMsg = tryReportingError([&] - { - if (!folderExistsUpdating(baseFolder, false, callback)) - throw FileError(replaceCpy(_("Cannot find folder %x."), L"%x", fmtPath(ABF::getDisplayPath(baseFolder.getAbstractPath())))); - //should be logged as a "fatal error" if ignored by the user... - }, callback)) //throw X? - return true; - - return false; - }; + std::vector<SyncStatistics::ConflictInfo> unresolvedConflicts; //aggregate information - std::map<const ABF*, ReadWriteCount, ABF::LessItemPath> dirReadWriteCount; //count read/write accesses + std::map<AbstractPath, ReadWriteCount, AFS::LessAbstractPath> dirReadWriteCount; //count read/write accesses for (auto j = begin(folderCmp); j != end(folderCmp); ++j) { - //create all entries first! otherwise counting accesses is too complex during later inserts! + //create all entries first! otherwise counting accesses would be too complex during later inserts! - if (!j->getABF<LEFT_SIDE >().emptyBaseFolderPath()) //empty folder is always dependent => exclude! - dirReadWriteCount[&j->getABF<LEFT_SIDE>()]; - if (!j->getABF<RIGHT_SIDE >().emptyBaseFolderPath()) - dirReadWriteCount[&j->getABF<RIGHT_SIDE>()]; + if (!AFS::isNullPath(j->getAbstractPath<LEFT_SIDE>())) //empty folder is always dependent => exclude! + dirReadWriteCount[j->getAbstractPath<LEFT_SIDE>()]; + if (!AFS::isNullPath(j->getAbstractPath<RIGHT_SIDE>())) + dirReadWriteCount[j->getAbstractPath<RIGHT_SIDE>()]; } - auto incReadCount = [&](const ABF& baseFolder) + auto incReadCount = [&](const AbstractPath& baseFolderPath) { - if (!baseFolder.emptyBaseFolderPath()) + if (!AFS::isNullPath(baseFolderPath)) for (auto& item : dirReadWriteCount) - if (ABF::havePathDependency(baseFolder, *item.first)) + if (AFS::havePathDependency(baseFolderPath, item.first)) ++item.second.reads; }; - auto incWriteCount = [&](const ABF& baseFolder) + auto incWriteCount = [&](const AbstractPath& baseFolderPath) { - if (!baseFolder.emptyBaseFolderPath()) + if (!AFS::isNullPath(baseFolderPath)) for (auto& item : dirReadWriteCount) - if (ABF::havePathDependency(baseFolder, *item.first)) + if (AFS::havePathDependency(baseFolderPath, item.first)) ++item.second.writes; }; - std::vector<std::pair<const ABF*, const ABF*>> significantDiffPairs; + std::vector<std::pair<AbstractPath, AbstractPath>> significantDiffPairs; - std::vector<std::pair<const ABF*, std::pair<std::int64_t, std::int64_t>>> diskSpaceMissing; //base folder / space required / space available + std::vector<std::pair<AbstractPath, std::pair<std::int64_t, std::int64_t>>> diskSpaceMissing; //base folder / space required / space available //status of base directories which are set to DELETE_TO_RECYCLER (and contain actual items to be deleted) - std::map<const ABF*, bool, ABF::LessItemPath> recyclerSupported; //expensive to determine on Win XP => buffer + check recycle bin existence only once per base folder! + std::map<AbstractPath, bool, AFS::LessAbstractPath> recyclerSupported; //expensive to determine on Win XP => buffer + check recycle bin existence only once per base folder! //start checking folder pairs for (auto j = begin(folderCmp); j != end(folderCmp); ++j) { const size_t folderIndex = j - begin(folderCmp); - const FolderPairSyncCfg& folderPairCfg = syncConfig[folderIndex]; + const FolderPairSyncCfg& folderPairCfg = syncConfig [folderIndex]; + const SyncStatistics& folderPairStat = folderPairStats[folderIndex]; + + //aggregate all conflicts: + append(unresolvedConflicts, folderPairStat.getConflicts()); - //exclude some pathological case (leftdir, rightdir are empty) - if (ABF::equalItemPath(j->getABF<LEFT_SIDE >().getAbstractPath(), - j->getABF<RIGHT_SIDE>().getAbstractPath())) + //exclude a few pathological cases (including empty left, right folders) + if (AFS::equalAbstractPath(j->getAbstractPath<LEFT_SIDE >(), + j->getAbstractPath<RIGHT_SIDE>())) { jobType[folderIndex] = FolderPairJobType::SKIP; continue; } - //aggregate basic information - const SyncStatistics folderPairStat(*j); + const bool writeLeft = folderPairStat.createCount<LEFT_SIDE>() + + folderPairStat.updateCount<LEFT_SIDE>() + + folderPairStat.deleteCount<LEFT_SIDE>() > 0; - const bool writeLeft = folderPairStat.getCreate<LEFT_SIDE>() + - folderPairStat.getUpdate<LEFT_SIDE>() + - folderPairStat.getDelete<LEFT_SIDE>() > 0; - - const bool writeRight = folderPairStat.getCreate<RIGHT_SIDE>() + - folderPairStat.getUpdate<RIGHT_SIDE>() + - folderPairStat.getDelete<RIGHT_SIDE>() > 0; + const bool writeRight = folderPairStat.createCount<RIGHT_SIDE>() + + folderPairStat.updateCount<RIGHT_SIDE>() + + folderPairStat.deleteCount<RIGHT_SIDE>() > 0; //skip folder pair if there is nothing to do (except for two-way mode and move-detection, where DB files need to be updated) //-> skip creating (not yet existing) base directories in particular if there's no need @@ -1924,33 +1924,27 @@ void zen::synchronize(const TimeComp& timeStamp, } //aggregate information of folders used by multiple pairs in read/write access - if (!ABF::havePathDependency(j->getABF<LEFT_SIDE>(), j->getABF<RIGHT_SIDE>())) //true in general + if (!AFS::havePathDependency(j->getAbstractPath<LEFT_SIDE>(), j->getAbstractPath<RIGHT_SIDE>())) { - if (writeLeft && writeRight) - { - incWriteCount(j->getABF<LEFT_SIDE>()); - incWriteCount(j->getABF<RIGHT_SIDE>()); - } - else if (writeLeft) - { - incWriteCount(j->getABF<LEFT_SIDE>()); - incReadCount (j->getABF<RIGHT_SIDE>()); - } + if (writeLeft) + incWriteCount(j->getAbstractPath<LEFT_SIDE>()); else if (writeRight) - { - incReadCount (j->getABF<LEFT_SIDE>()); - incWriteCount(j->getABF<RIGHT_SIDE>()); - } + incReadCount (j->getAbstractPath<LEFT_SIDE>()); + + if (writeRight) + incWriteCount(j->getAbstractPath<RIGHT_SIDE>()); + else if (writeLeft) + incReadCount (j->getAbstractPath<RIGHT_SIDE>()); } else //if folder pair contains two dependent folders, a warning was already issued after comparison; in this context treat as one write access at most { if (writeLeft || writeRight) - incWriteCount(j->getABF<LEFT_SIDE>()); + incWriteCount(j->getAbstractPath<LEFT_SIDE>()); } //check for empty target folder paths: this only makes sense if empty field is source (and no DB files need to be created) - if ((j->getABF<LEFT_SIDE >().emptyBaseFolderPath() && (writeLeft || folderPairCfg.saveSyncDB_)) || - (j->getABF<RIGHT_SIDE>().emptyBaseFolderPath() && (writeRight || folderPairCfg.saveSyncDB_))) + if ((AFS::isNullPath(j->getAbstractPath<LEFT_SIDE >()) && (writeLeft || folderPairCfg.saveSyncDB_)) || + (AFS::isNullPath(j->getAbstractPath<RIGHT_SIDE>()) && (writeRight || folderPairCfg.saveSyncDB_))) { callback.reportFatalError(_("Target folder input field must not be empty.")); jobType[folderIndex] = FolderPairJobType::SKIP; @@ -1960,30 +1954,30 @@ void zen::synchronize(const TimeComp& timeStamp, //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 (dirNotFoundAnymore(j->getABF<LEFT_SIDE >(), j->isExisting<LEFT_SIDE >()) || - dirNotFoundAnymore(j->getABF<RIGHT_SIDE>(), j->isExisting<RIGHT_SIDE>())) + if (baseFolderDrop<LEFT_SIDE >(*j, callback) || + baseFolderDrop<RIGHT_SIDE>(*j, callback)) { jobType[folderIndex] = FolderPairJobType::SKIP; continue; } - //the following scenario is covered by base directory creation below in case source directory exists (accessible or not), but latter doesn't cover source created after comparison, but before sync!!! - auto sourceDirNotFound = [&](const ABF& baseFolder, bool wasExisting) -> bool //avoid race-condition: we need to evaluate existence status from time of comparison! + //allow propagation of deletions only from *null-* or *existing* source folder: + auto sourceFolderMissing = [&](const AbstractPath& baseFolder, bool wasExisting) //we need to evaluate existence status from time of comparison! { - if (!baseFolder.emptyBaseFolderPath()) + if (!AFS::isNullPath(baseFolder)) //PERMANENT network drop: avoid data loss when source directory is not found AND user chose to ignore errors (else we wouldn't arrive here) - if (folderPairStat.getDelete() > 0) //check deletions only... (respect filtered items!) - //folderPairStat.getConflict() == 0 && -> there COULD be conflicts for <automatic> if directory existence check fails, but loading sync.ffs_db succeeds + if (folderPairStat.deleteCount() > 0) //check deletions only... (respect filtered items!) + //folderPairStat.conflictCount() == 0 && -> there COULD be conflicts for <automatic> if directory existence check fails, but loading sync.ffs_db succeeds //https://sourceforge.net/tracker/?func=detail&atid=1093080&aid=3531351&group_id=234430 -> fixed, but still better not consider conflicts! if (!wasExisting) //avoid race-condition: we need to evaluate existence status from time of comparison! { - callback.reportFatalError(replaceCpy(_("Source folder %x not found."), L"%x", fmtPath(ABF::getDisplayPath(baseFolder.getAbstractPath())))); + callback.reportFatalError(replaceCpy(_("Source folder %x not found."), L"%x", fmtPath(AFS::getDisplayPath(baseFolder)))); return true; } return false; }; - if (sourceDirNotFound(j->getABF<LEFT_SIDE >(), j->isExisting<LEFT_SIDE >()) || - sourceDirNotFound(j->getABF<RIGHT_SIDE>(), j->isExisting<RIGHT_SIDE>())) + if (sourceFolderMissing(j->getAbstractPath<LEFT_SIDE >(), j->isExisting<LEFT_SIDE >()) || + sourceFolderMissing(j->getAbstractPath<RIGHT_SIDE>(), j->isExisting<RIGHT_SIDE>())) { jobType[folderIndex] = FolderPairJobType::SKIP; continue; @@ -1991,11 +1985,11 @@ void zen::synchronize(const TimeComp& timeStamp, //check if user-defined directory for deletion was specified if (folderPairCfg.handleDeletion == zen::DELETE_TO_VERSIONING && - folderPairStat.getUpdate() + folderPairStat.getDelete() > 0) + folderPairStat.updateCount() + folderPairStat.deleteCount() > 0) { if (trimCpy(folderPairCfg.versioningFolderPhrase).empty()) { - //should not arrive here: already checked in SyncCfgDialog + //should never arrive here: already checked in SyncCfgDialog callback.reportFatalError(_("Please enter a target folder for versioning.")); jobType[folderIndex] = FolderPairJobType::SKIP; continue; @@ -2003,66 +1997,67 @@ void zen::synchronize(const TimeComp& timeStamp, } //check if more than 50% of total number of files/dirs are to be created/overwritten/deleted - if (!j->getABF<LEFT_SIDE >().emptyBaseFolderPath() && - !j->getABF<RIGHT_SIDE>().emptyBaseFolderPath()) + if (!AFS::isNullPath(j->getAbstractPath<LEFT_SIDE >()) && + !AFS::isNullPath(j->getAbstractPath<RIGHT_SIDE>())) if (significantDifferenceDetected(folderPairStat)) - significantDiffPairs.emplace_back(&j->getABF<LEFT_SIDE>(), &j->getABF<RIGHT_SIDE>()); + significantDiffPairs.emplace_back(j->getAbstractPath<LEFT_SIDE >(), + j->getAbstractPath<RIGHT_SIDE>()); //check for sufficient free diskspace - auto checkSpace = [&](const ABF& baseFolder, std::int64_t minSpaceNeeded) + auto checkSpace = [&](const AbstractPath& baseFolderPath, std::int64_t minSpaceNeeded) { - if (!baseFolder.emptyBaseFolderPath()) + if (!AFS::isNullPath(baseFolderPath)) try { - const std::int64_t freeSpace = baseFolder.getFreeDiskSpace(); //throw FileError, returns 0 if not available + const std::int64_t freeSpace = AFS::getFreeDiskSpace(baseFolderPath); //throw FileError, returns 0 if not available if (0 < freeSpace && //zero means "request not supported" (e.g. see WebDav) freeSpace < minSpaceNeeded) - diskSpaceMissing.emplace_back(&baseFolder, std::make_pair(minSpaceNeeded, freeSpace)); + diskSpaceMissing.emplace_back(baseFolderPath, std::make_pair(minSpaceNeeded, freeSpace)); } catch (FileError&) { assert(false); } //for warning only => no need for tryReportingError() }; const std::pair<std::int64_t, std::int64_t> spaceNeeded = MinimumDiskSpaceNeeded::calculate(*j); - checkSpace(j->getABF<LEFT_SIDE >(), spaceNeeded.first); - checkSpace(j->getABF<RIGHT_SIDE>(), spaceNeeded.second); + checkSpace(j->getAbstractPath<LEFT_SIDE >(), spaceNeeded.first); + checkSpace(j->getAbstractPath<RIGHT_SIDE>(), spaceNeeded.second); //windows: check if recycle bin really exists; if not, Windows will silently delete, which is wrong - auto checkRecycler = [&](const ABF& baseFolder) + auto checkRecycler = [&](const AbstractPath& baseFolderPath) { - assert(!baseFolder.emptyBaseFolderPath()); - if (!baseFolder.emptyBaseFolderPath()) - if (recyclerSupported.find(&baseFolder) == recyclerSupported.end()) //perf: avoid duplicate checks! + assert(!AFS::isNullPath(baseFolderPath)); + if (!AFS::isNullPath(baseFolderPath)) + if (recyclerSupported.find(baseFolderPath) == recyclerSupported.end()) //perf: avoid duplicate checks! { callback.reportStatus(replaceCpy(_("Checking recycle bin availability for folder %x..."), L"%x", - fmtPath(ABF::getDisplayPath(baseFolder.getAbstractPath())))); + fmtPath(AFS::getDisplayPath(baseFolderPath)))); bool recSupported = false; tryReportingError([&] { - recSupported = baseFolder.supportsRecycleBin([&]{ callback.requestUiRefresh(); /*may throw*/ }); //throw FileError + recSupported = AFS::supportsRecycleBin(baseFolderPath, [&]{ callback.requestUiRefresh(); /*may throw*/ }); //throw FileError }, callback); //throw X? - recyclerSupported.emplace(&baseFolder, recSupported); + recyclerSupported.emplace(baseFolderPath, recSupported); } }; if (folderPairCfg.handleDeletion == DELETE_TO_RECYCLER) { - if (folderPairStat.getUpdate<LEFT_SIDE>() + - folderPairStat.getDelete<LEFT_SIDE>() > 0) - checkRecycler(j->getABF<LEFT_SIDE>()); + if (folderPairStat.updateCount<LEFT_SIDE>() + + folderPairStat.deleteCount<LEFT_SIDE>() > 0) + checkRecycler(j->getAbstractPath<LEFT_SIDE>()); - if (folderPairStat.getUpdate<RIGHT_SIDE>() + - folderPairStat.getDelete<RIGHT_SIDE>() > 0) - checkRecycler(j->getABF<RIGHT_SIDE>()); + if (folderPairStat.updateCount<RIGHT_SIDE>() + + folderPairStat.deleteCount<RIGHT_SIDE>() > 0) + checkRecycler(j->getAbstractPath<RIGHT_SIDE>()); } } //check if unresolved conflicts exist - if (statisticsTotal.getConflict() > 0) + if (!unresolvedConflicts.empty()) { std::wstring msg = _("The following items have unresolved conflicts and will not be synchronized:"); - for (const auto& item : statisticsTotal.getConflictMessages()) //show *all* conflicts in warning message + for (const SyncStatistics::ConflictInfo& item : unresolvedConflicts) //show *all* conflicts in warning message msg += L"\n\n" + fmtPath(item.first) + L": " + item.second; callback.reportWarning(msg, warnings.warningUnresolvedConflicts); @@ -2075,8 +2070,8 @@ void zen::synchronize(const TimeComp& timeStamp, for (const auto& item : significantDiffPairs) msg += L"\n\n" + - ABF::getDisplayPath(item.first ->getAbstractPath()) + L" <-> " + L"\n" + - ABF::getDisplayPath(item.second->getAbstractPath()); + AFS::getDisplayPath(item.first) + L" <-> " + L"\n" + + AFS::getDisplayPath(item.second); callback.reportWarning(msg, warnings.warningSignificantDifference); } @@ -2087,7 +2082,7 @@ void zen::synchronize(const TimeComp& timeStamp, std::wstring msg = _("Not enough free disk space available in:"); for (const auto& item : diskSpaceMissing) - msg += L"\n\n" + ABF::getDisplayPath(item.first->getAbstractPath()) + L"\n" + + msg += L"\n\n" + AFS::getDisplayPath(item.first) + L"\n" + _("Required:") + L" " + filesizeToShortString(item.second.first) + L"\n" + _("Available:") + L" " + filesizeToShortString(item.second.second); @@ -2099,7 +2094,7 @@ void zen::synchronize(const TimeComp& timeStamp, std::wstring dirListMissingRecycler; for (const auto& item : recyclerSupported) if (!item.second) - dirListMissingRecycler += L"\n" + ABF::getDisplayPath(item.first->getAbstractPath()); + dirListMissingRecycler += L"\n" + AFS::getDisplayPath(item.first); if (!dirListMissingRecycler.empty()) callback.reportWarning(_("The recycle bin is not available for the following folders. Files will be deleted permanently instead:") + L"\n" + @@ -2108,7 +2103,7 @@ void zen::synchronize(const TimeComp& timeStamp, //check if folders are used by multiple pairs in read/write access { - std::vector<const ABF*> conflictFolders; + std::vector<AbstractPath> conflictFolders; for (const auto& item : dirReadWriteCount) if (item.second.reads + item.second.writes >= 2 && item.second.writes >= 1) //race condition := multiple accesses of which at least one is a write conflictFolders.push_back(item.first); @@ -2116,8 +2111,8 @@ void zen::synchronize(const TimeComp& timeStamp, if (!conflictFolders.empty()) { std::wstring msg = _("Multiple folder pairs write to a common subfolder. Please review your configuration.") + L"\n"; - for (const ABF* baseFolder : conflictFolders) - msg += L"\n" + ABF::getDisplayPath(baseFolder->getAbstractPath()); + for (const AbstractPath& folderPath : conflictFolders) + msg += L"\n" + AFS::getDisplayPath(folderPath); callback.reportWarning(msg, warnings.warningFolderPairRaceCondition); } @@ -2138,58 +2133,61 @@ void zen::synchronize(const TimeComp& timeStamp, for (auto j = begin(folderCmp); j != end(folderCmp); ++j) { const size_t folderIndex = j - begin(folderCmp); - const FolderPairSyncCfg& folderPairCfg = syncConfig[folderIndex]; + const FolderPairSyncCfg& folderPairCfg = syncConfig [folderIndex]; + const SyncStatistics& folderPairStat = folderPairStats[folderIndex]; if (jobType[folderIndex] == FolderPairJobType::SKIP) //folder pairs may be skipped after fatal errors were found continue; //------------------------------------------------------------------------------------------ callback.reportInfo(_("Synchronizing folder pair:") + L" [" + getVariantName(folderPairCfg.syncVariant_) + L"]\n" + - L" " + ABF::getDisplayPath(j->getABF<LEFT_SIDE >().getAbstractPath()) + L"\n" + - L" " + ABF::getDisplayPath(j->getABF<RIGHT_SIDE>().getAbstractPath())); + L" " + AFS::getDisplayPath(j->getAbstractPath<LEFT_SIDE >()) + L"\n" + + L" " + AFS::getDisplayPath(j->getAbstractPath<RIGHT_SIDE>())); //------------------------------------------------------------------------------------------ //checking a second time: (a long time may have passed since the intro checks!) - if (dirNotFoundAnymore(j->getABF<LEFT_SIDE >(), j->isExisting<LEFT_SIDE >()) || - dirNotFoundAnymore(j->getABF<RIGHT_SIDE>(), j->isExisting<RIGHT_SIDE>())) + if (baseFolderDrop<LEFT_SIDE >(*j, callback) || + baseFolderDrop<RIGHT_SIDE>(*j, callback)) continue; - //create base directories first (if not yet existing) -> no symlink or attribute copying! - if (!createBaseDirectory<LEFT_SIDE >(*j, callback) || - !createBaseDirectory<RIGHT_SIDE>(*j, 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 >(*j, callback) || //+ detect temporary network drop!! + !createBaseFolder<RIGHT_SIDE>(*j, callback)) // + continue; //------------------------------------------------------------------------------------------ //execute synchronization recursively - //update synchronization database (automatic sync only) - ScopeGuard guardUpdateDb = makeGuard([&] + //update synchronization database in case of errors: + ZEN_ON_SCOPE_FAIL(try { if (folderPairCfg.saveSyncDB_) - try { zen::saveLastSynchronousState(*j, nullptr); } //throw FileError - catch (FileError&) {} - }); + zen::saveLastSynchronousState(*j, nullptr); + } //throw FileError + catch (FileError&) {} + ); if (jobType[folderIndex] == FolderPairJobType::PROCESS) { //guarantee removal of invalid entries (where element is empty on both sides) - ZEN_ON_SCOPE_EXIT(BaseDirPair::removeEmpty(*j)); + ZEN_ON_SCOPE_EXIT(BaseFolderPair::removeEmpty(*j)); bool copyPermissionsFp = false; tryReportingError([&] { copyPermissionsFp = copyFilePermissions && //copy permissions only if asked for and supported by *both* sides! - !j->getABF<LEFT_SIDE >().emptyBaseFolderPath() && //scenario: directory selected on one side only - !j->getABF<RIGHT_SIDE>().emptyBaseFolderPath() && // - ABF::supportPermissionCopy(j->getABF<LEFT_SIDE>(), j->getABF<RIGHT_SIDE>()); //throw FileError + !AFS::isNullPath(j->getAbstractPath<LEFT_SIDE >()) && //scenario: directory selected on one side only + !AFS::isNullPath(j->getAbstractPath<RIGHT_SIDE>()) && // + AFS::supportPermissionCopy(j->getAbstractPath<LEFT_SIDE>(), j->getAbstractPath<RIGHT_SIDE>()); //throw FileError }, callback); //throw X? - auto getEffectiveDeletionPolicy = [&](const ABF& baseFolder) -> DeletionPolicy + auto getEffectiveDeletionPolicy = [&](const AbstractPath& baseFolderPath) -> DeletionPolicy { if (folderPairCfg.handleDeletion == DELETE_TO_RECYCLER) { - auto it = recyclerSupported.find(&baseFolder); + auto it = recyclerSupported.find(baseFolderPath); if (it != recyclerSupported.end()) //buffer filled during intro checks (but only if deletions are expected) if (!it->second) return DELETE_PERMANENTLY; //Windows' ::SHFileOperation() will do this anyway, but we have a better and faster deletion routine (e.g. on networks) @@ -2198,15 +2196,15 @@ void zen::synchronize(const TimeComp& timeStamp, }; - DeletionHandling delHandlerL(j->getABF<LEFT_SIDE>(), - getEffectiveDeletionPolicy(j->getABF<LEFT_SIDE>()), + DeletionHandling delHandlerL(j->getAbstractPath<LEFT_SIDE>(), + getEffectiveDeletionPolicy(j->getAbstractPath<LEFT_SIDE>()), folderPairCfg.versioningFolderPhrase, folderPairCfg.versioningStyle_, timeStamp, callback); - DeletionHandling delHandlerR(j->getABF<RIGHT_SIDE>(), - getEffectiveDeletionPolicy(j->getABF<RIGHT_SIDE>()), + DeletionHandling delHandlerR(j->getAbstractPath<RIGHT_SIDE>(), + getEffectiveDeletionPolicy(j->getAbstractPath<RIGHT_SIDE>()), folderPairCfg.versioningFolderPhrase, folderPairCfg.versioningStyle_, timeStamp, @@ -2242,8 +2240,6 @@ void zen::synchronize(const TimeComp& timeStamp, callback.reportStatus(dbUpdateMsg + L" (" + filesizeToShortString(bytesWritten) + L")"); //throw X }); }, callback); //throw X? - - guardUpdateDb.dismiss(); } } } diff --git a/FreeFileSync/Source/synchronization.h b/FreeFileSync/Source/synchronization.h index a932bf8d..8a29dda9 100644 --- a/FreeFileSync/Source/synchronization.h +++ b/FreeFileSync/Source/synchronization.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef SYNCHRONIZATION_H_INCLUDED -#define SYNCHRONIZATION_H_INCLUDED +#ifndef SYNCHRONIZATION_H_8913470815943295 +#define SYNCHRONIZATION_H_8913470815943295 #include <zen/time.h> #include "file_hierarchy.h" @@ -17,44 +17,48 @@ namespace zen { class SyncStatistics //this class counts *logical* operations, (create, update, delete + bytes), *not* disk accesses! { - //-> note the fundamental difference to counting disk accesses! + //-> note the fundamental difference compared to counting disk accesses! public: - SyncStatistics(const HierarchyObject& hierObj); SyncStatistics(const FolderComparison& folderCmp); - SyncStatistics(const FilePair& fileObj); + SyncStatistics(const HierarchyObject& hierObj); + SyncStatistics(const FilePair& file); - int getCreate() const; - template <SelectedSide side> int getCreate() const; + template <SelectedSide side> + int createCount() const { return SelectParam<side>::ref(createLeft, createRight); } + int createCount() const { return createLeft + createRight; } - int getUpdate() const; - template <SelectedSide side> int getUpdate() const; + template <SelectedSide side> + int updateCount() const { return SelectParam<side>::ref(updateLeft, updateRight); } + int updateCount() const { return updateLeft + updateRight; } - int getDelete() const; - template <SelectedSide side> int getDelete() const; + template <SelectedSide side> + int deleteCount() const { return SelectParam<side>::ref(deleteLeft, deleteRight); } + int deleteCount() const { return deleteLeft + deleteRight; } - int getConflict() const { return static_cast<int>(conflictMsgs.size()); } - - typedef std::vector<std::pair<Zstring, std::wstring>> ConflictTexts; // Pair(filepath/conflict text) - const ConflictTexts& getConflictMessages() const { return conflictMsgs; } + int conflictCount() const { return static_cast<int>(conflictMsgs.size()); } std::int64_t getDataToProcess() const { return dataToProcess; } - size_t getRowCount() const { return rowsTotal; } + size_t rowCount () const { return rowsTotal; } -private: - void init(); + using ConflictInfo = std::pair<Zstring, std::wstring>; //pair(filePath/conflict message) + const std::vector<ConflictInfo>& getConflicts() const { return conflictMsgs; } +private: void recurse(const HierarchyObject& hierObj); - void processFile(const FilePair& fileObj); - void processLink(const SymlinkPair& linkObj); - void processDir(const DirPair& dirObj); - - int createLeft, createRight; - int updateLeft, updateRight; - int deleteLeft, deleteRight; - ConflictTexts conflictMsgs; //conflict texts to display as a warning message - std::int64_t dataToProcess; - size_t rowsTotal; + void processFile(const FilePair& file); + void processLink(const SymlinkPair& link); + void processFolder(const FolderPair& folder); + + int createLeft = 0; + int createRight = 0; + int updateLeft = 0; + int updateRight = 0; + int deleteLeft = 0; + int deleteRight = 0; + std::vector<ConflictInfo> conflictMsgs; //conflict texts to display as a warning message + std::int64_t dataToProcess = 0; + size_t rowsTotal = 0; }; @@ -91,71 +95,6 @@ void synchronize(const TimeComp& timeStamp, const std::vector<FolderPairSyncCfg>& syncConfig, //CONTRACT: syncConfig and folderCmp correspond row-wise! FolderComparison& folderCmp, // ProcessCallback& callback); - - - - - - - - - - -// ----------- implementation ---------------- -template <> inline -int SyncStatistics::getCreate<LEFT_SIDE>() const -{ - return createLeft; -} - -template <> inline -int SyncStatistics::getCreate<RIGHT_SIDE>() const -{ - return createRight; -} - -inline -int SyncStatistics::getCreate() const -{ - return getCreate<LEFT_SIDE>() + getCreate<RIGHT_SIDE>(); -} - -template <> inline -int SyncStatistics::getUpdate<LEFT_SIDE>() const -{ - return updateLeft; -} - -template <> inline -int SyncStatistics::getUpdate<RIGHT_SIDE>() const -{ - return updateRight; -} - -inline -int SyncStatistics::getUpdate() const -{ - return getUpdate<LEFT_SIDE>() + getUpdate<RIGHT_SIDE>(); -} - - -template <> inline -int SyncStatistics::getDelete<LEFT_SIDE>() const -{ - return deleteLeft; -} - -template <> inline -int SyncStatistics::getDelete<RIGHT_SIDE>() const -{ - return deleteRight; -} - -inline -int SyncStatistics::getDelete() const -{ - return getDelete<LEFT_SIDE>() + getDelete<RIGHT_SIDE>(); -} } -#endif // SYNCHRONIZATION_H_INCLUDED +#endif //SYNCHRONIZATION_H_8913470815943295 diff --git a/FreeFileSync/Source/ui/app_icon.h b/FreeFileSync/Source/ui/app_icon.h index 03254f2d..7bca4290 100644 --- a/FreeFileSync/Source/ui/app_icon.h +++ b/FreeFileSync/Source/ui/app_icon.h @@ -4,12 +4,13 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef APP_ICON_H6748179634932174683214 -#define APP_ICON_H6748179634932174683214 +#ifndef APP_ICON_H_6748179634932174683214 +#define APP_ICON_H_6748179634932174683214 #include <wx/icon.h> #include <wx+/image_resources.h> + namespace zen { inline @@ -23,7 +24,6 @@ wxIcon getFfsIcon() return wxIcon(L"A_FFS_ICON"); #elif defined ZEN_LINUX - //attention: make sure to not implicitly call "instance()" again => deadlock on Linux wxIcon icon; icon.CopyFromBitmap(getResourceImage(L"FreeFileSync")); //use big logo bitmap for better quality return icon; @@ -37,4 +37,4 @@ wxIcon getFfsIcon() } -#endif //APP_ICON_H6748179634932174683214 +#endif //APP_ICON_H_6748179634932174683214 diff --git a/FreeFileSync/Source/ui/batch_config.h b/FreeFileSync/Source/ui/batch_config.h index ab3064ef..e611ee3b 100644 --- a/FreeFileSync/Source/ui/batch_config.h +++ b/FreeFileSync/Source/ui/batch_config.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef BATCHCONFIG_H_INCLUDED -#define BATCHCONFIG_H_INCLUDED +#ifndef BATCH_CONFIG_H_3921674832168945 +#define BATCH_CONFIG_H_3921674832168945 #include <wx/window.h> #include "../lib/process_xml.h" @@ -30,4 +30,4 @@ ReturnBatchConfig::ButtonPressed customizeBatchConfig(wxWindow* parent, size_t onCompletionHistoryMax); } -#endif // BATCHCONFIG_H_INCLUDED +#endif //BATCH_CONFIG_H_3921674832168945 diff --git a/FreeFileSync/Source/ui/batch_status_handler.cpp b/FreeFileSync/Source/ui/batch_status_handler.cpp index 90b3b627..0d6f40e0 100644 --- a/FreeFileSync/Source/ui/batch_status_handler.cpp +++ b/FreeFileSync/Source/ui/batch_status_handler.cpp @@ -24,15 +24,15 @@ namespace //"Backup FreeFileSync 2013-09-15 015052 [Error].log" //return value always bound! -std::pair<std::unique_ptr<ABF::OutputStream>, AbstractPathRef> prepareNewLogfile(ABF& abf, //throw FileError - const std::wstring& jobName, - const TimeComp& timeStamp, - const std::wstring& status) +std::pair<std::unique_ptr<AFS::OutputStream>, AbstractPath> prepareNewLogfile(const AbstractPath& logFolderPath, //throw FileError + const std::wstring& jobName, + const TimeComp& timeStamp, + const std::wstring& status) { assert(!jobName.empty()); - //create logfile directory if required - ABF::createFolderRecursively(abf.getAbstractPath()); //throw FileError + //create logfile folder if required + AFS::createFolderRecursively(logFolderPath); //throw FileError //const std::string colon = "\xcb\xb8"; //="modifier letter raised colon" => regular colon is forbidden in file names on Windows and OS X //=> too many issues, most notably cmd.exe is not Unicode-awere: http://sourceforge.net/p/freefilesync/discussion/open-discussion/thread/c559a5fb/ @@ -47,8 +47,8 @@ std::pair<std::unique_ptr<ABF::OutputStream>, AbstractPathRef> prepareNewLogfile for (int i = 0;; ++i) try { - const AbstractPathRef logFilePath = abf.getAbstractPath(logFileName); - auto outStream = ABF::getOutputStream(logFilePath, //throw FileError, ErrorTargetExisting + const AbstractPath logFilePath = AFS::appendRelPath(logFolderPath, logFileName); + auto outStream = AFS::getOutputStream(logFilePath, //throw FileError, ErrorTargetExisting nullptr, /*streamSize*/ nullptr /*modificationTime*/); return std::make_pair(std::move(outStream), logFilePath); @@ -61,7 +61,7 @@ std::pair<std::unique_ptr<ABF::OutputStream>, AbstractPathRef> prepareNewLogfile } -struct LogTraverserCallback: public ABF::TraverserCallback +struct LogTraverserCallback: public AFS::TraverserCallback { LogTraverserCallback(std::vector<Zstring>& logFileNames, const Zstring& prefix, const std::function<void()>& onUpdateStatus) : logFileNames_(logFileNames), @@ -88,14 +88,14 @@ private: }; -void limitLogfileCount(ABF& abf, const std::wstring& jobname, size_t maxCount, const std::function<void()>& onUpdateStatus) //noexcept +void limitLogfileCount(const AbstractPath& logFolderPath, const std::wstring& jobname, size_t maxCount, const std::function<void()>& onUpdateStatus) //noexcept { std::vector<Zstring> logFileNames; { const Zstring prefix = utfCvrtTo<Zstring>(jobname); LogTraverserCallback lt(logFileNames, prefix, onUpdateStatus); //traverse source directory one level deep - ABF::traverseFolder(abf.getAbstractPath(), lt); + AFS::traverseFolder(logFolderPath, lt); } if (logFileNames.size() <= maxCount) @@ -108,7 +108,7 @@ void limitLogfileCount(ABF& abf, const std::wstring& jobname, size_t maxCount, c { try { - ABF::removeFile(abf.getAbstractPath(logFileName)); //throw FileError + AFS::removeFile(AFS::appendRelPath(logFolderPath, logFileName)); //throw FileError } catch (FileError&) { assert(false); }; @@ -149,14 +149,12 @@ BatchStatusHandler::BatchStatusHandler(bool showProgress, logFolderPathPhrase_(logFolderPathPhrase) { //ATTENTION: "progressDlg" is an unmanaged resource!!! However, at this point we already consider construction complete! => - ScopeGuard constructorGuard = zen::makeGuard([&] { /*cleanup();*/ /*destructor call would lead to member double clean-up!!!*/ }); + //ZEN_ON_SCOPE_FAIL( cleanup(); ); //destructor call would lead to member double clean-up!!! //... //if (logFile) // ::wxSetEnv(L"logfile", utfCvrtTo<wxString>(logFile->getFilename())); - - constructorGuard.dismiss(); } @@ -250,16 +248,16 @@ BatchStatusHandler::~BatchStatusHandler() //create not before destruction: 1. avoid issues with FFS trying to sync open log file 2. simplify transactional retry on failure 3. no need to rename log file to include status if (logfilesCountLimit_ != 0) { - std::unique_ptr<AbstractBaseFolder> logFileAbf = createAbstractBaseFolder(trimCpy(logFolderPathPhrase_).empty() ? getConfigDir() + Zstr("Logs") : logFolderPathPhrase_); //noexcept + const AbstractPath logFolderPath = createAbstractPath(trimCpy(logFolderPathPhrase_).empty() ? getConfigDir() + Zstr("Logs") : logFolderPathPhrase_); //noexcept try { tryReportingError([&] //errors logged here do not impact final status calculation above! => not a problem! { - auto rv = prepareNewLogfile(*logFileAbf, jobName_, timeStamp_, status); //throw FileError; return value always bound! - ABF::OutputStream& logFileStream = *rv.first; - const AbstractPathRef logFilePath = rv.second; + auto rv = prepareNewLogfile(logFolderPath, jobName_, timeStamp_, status); //throw FileError; return value always bound! + AFS::OutputStream& logFileStream = *rv.first; + const AbstractPath logFilePath = rv.second; - saveLogToFile(summary, errorLog, logFileStream, OnUpdateLogfileStatusNoThrow(*this, ABF::getDisplayPath(logFilePath))); //throw FileError + saveLogToFile(summary, errorLog, logFileStream, OnUpdateLogfileStatusNoThrow(*this, AFS::getDisplayPath(logFilePath))); //throw FileError logFileStream.finalize([&] { try { requestUiRefresh(); } catch (...) {} }); //throw FileError }, *this); //throw X? } @@ -269,7 +267,7 @@ BatchStatusHandler::~BatchStatusHandler() { try { reportStatus(_("Cleaning up old log files...")); } catch (...) {} - limitLogfileCount(*logFileAbf, jobName_, logfilesCountLimit_, [&] { try { requestUiRefresh(); } catch (...) {} }); //noexcept + limitLogfileCount(logFolderPath, jobName_, logfilesCountLimit_, [&] { try { requestUiRefresh(); } catch (...) {} }); //noexcept } } //----------------- write results into LastSyncs.log------------------------ @@ -406,7 +404,7 @@ ProcessCallback::Response BatchStatusHandler::reportError(const std::wstring& er //always, except for "retry": - zen::ScopeGuard guardWriteLog = zen::makeGuard([&] { errorLog.logMsg(errorMessage, TYPE_ERROR); }); + auto guardWriteLog = zen::makeGuard<ScopeGuardRunMode::ON_EXIT>([&] { errorLog.logMsg(errorMessage, TYPE_ERROR); }); switch (handleError_) { diff --git a/FreeFileSync/Source/ui/batch_status_handler.h b/FreeFileSync/Source/ui/batch_status_handler.h index df1adc36..833d27ff 100644 --- a/FreeFileSync/Source/ui/batch_status_handler.h +++ b/FreeFileSync/Source/ui/batch_status_handler.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef BATCH_STATUS_HANDLER_857390451451234566 -#define BATCH_STATUS_HANDLER_857390451451234566 +#ifndef BATCH_STATUS_HANDLER_H_857390451451234566 +#define BATCH_STATUS_HANDLER_H_857390451451234566 #include <zen/error_log.h> #include <zen/time.h> @@ -74,4 +74,4 @@ private: const Zstring logFolderPathPhrase_; }; -#endif //BATCH_STATUS_HANDLER_857390451451234566 +#endif //BATCH_STATUS_HANDLER_H_857390451451234566 diff --git a/FreeFileSync/Source/ui/column_attr.h b/FreeFileSync/Source/ui/column_attr.h index eef2eeec..797c6136 100644 --- a/FreeFileSync/Source/ui/column_attr.h +++ b/FreeFileSync/Source/ui/column_attr.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef COL_ATTR_HEADER_189467891346732143214 -#define COL_ATTR_HEADER_189467891346732143214 +#ifndef COLUMN_ATTR_H_189467891346732143213 +#define COLUMN_ATTR_H_189467891346732143213 #include <vector> @@ -107,4 +107,4 @@ std::vector<ColumnAttributeNavi> getDefaultColumnAttributesNavi() } } -#endif // COL_ATTR_HEADER_189467891346732143214 +#endif //COLUMN_ATTR_H_189467891346732143213 diff --git a/FreeFileSync/Source/ui/custom_grid.cpp b/FreeFileSync/Source/ui/custom_grid.cpp index 060446bd..3eec8375 100644 --- a/FreeFileSync/Source/ui/custom_grid.cpp +++ b/FreeFileSync/Source/ui/custom_grid.cpp @@ -217,7 +217,7 @@ public: void setIconManager(const std::shared_ptr<IconManager>& iconMgr) { iconMgr_ = iconMgr; } - void updateNewAndGetUnbufferedIcons(std::vector<AbstractPathRef>& newLoad) //loads all not yet drawn icons + void updateNewAndGetUnbufferedIcons(std::vector<AbstractPath>& newLoad) //loads all not yet drawn icons { if (iconMgr_) { @@ -250,7 +250,7 @@ public: } } - void getUnbufferedIconsForPreload(std::vector<std::pair<ptrdiff_t, AbstractPathRef>>& newLoad) //return (priority, filepath) list + void getUnbufferedIconsForPreload(std::vector<std::pair<ptrdiff_t, AbstractPath>>& newLoad) //return (priority, filepath) list { if (iconMgr_) { @@ -370,12 +370,12 @@ private: { GetRowType(DisplayType& result) : result_(result) {} - void visit(const FilePair& fileObj) override {} - void visit(const SymlinkPair& linkObj) override + void visit(const FilePair& file) override {} + void visit(const SymlinkPair& symlink) override { result_ = DISP_TYPE_SYMLINK; } - void visit(const DirPair& dirObj) override + void visit(const FolderPair& folder) override { result_ = DISP_TYPE_FOLDER; } @@ -386,83 +386,83 @@ private: return output; } - wxString getValue(size_t row, ColumnType colType) const override + std::wstring getValue(size_t row, ColumnType colType) const override { if (const FileSystemObject* fsObj = getRawData(row)) { struct GetTextValue : public FSObjectVisitor { - GetTextValue(ColumnTypeRim colType) : colType_(colType) {} + GetTextValue(ColumnTypeRim ctr) : colType_(ctr) {} - void visit(const FilePair& fileObj) override + void visit(const FilePair& file) override { value = [&] { switch (colType_) { case COL_TYPE_FULL_PATH: - return fileObj.isEmpty<side>() ? std::wstring() : ABF::getDisplayPath(fileObj.getAbstractPath<side>()); + return file.isEmpty<side>() ? std::wstring() : AFS::getDisplayPath(file.getAbstractPath<side>()); case COL_TYPE_FILENAME: - return utfCvrtTo<std::wstring>(fileObj.getItemName<side>()); + return utfCvrtTo<std::wstring>(file.getItemName<side>()); case COL_TYPE_REL_FOLDER: - return utfCvrtTo<std::wstring>(beforeLast(fileObj.getPairRelativePath(), FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)); + return utfCvrtTo<std::wstring>(beforeLast(file.getPairRelativePath(), FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)); case COL_TYPE_BASE_DIRECTORY: - return ABF::getDisplayPath(fileObj.getABF<side>().getAbstractPath()); + return AFS::getDisplayPath(file.base().getAbstractPath<side>()); case COL_TYPE_SIZE: - //return fileObj.isEmpty<side>() ? std::wstring() : utfCvrtTo<std::wstring>(fileObj.getFileId<side>()); // -> test file id - return fileObj.isEmpty<side>() ? std::wstring() : toGuiString(fileObj.getFileSize<side>()); + //return file.isEmpty<side>() ? std::wstring() : utfCvrtTo<std::wstring>(file.getFileId<side>()); // -> test file id + return file.isEmpty<side>() ? std::wstring() : toGuiString(file.getFileSize<side>()); case COL_TYPE_DATE: - return fileObj.isEmpty<side>() ? std::wstring() : utcToLocalTimeString(fileObj.getLastWriteTime<side>()); + return file.isEmpty<side>() ? std::wstring() : utcToLocalTimeString(file.getLastWriteTime<side>()); case COL_TYPE_EXTENSION: - return utfCvrtTo<std::wstring>(getFileExtension(fileObj.getItemName<side>())); + return utfCvrtTo<std::wstring>(getFileExtension(file.getItemName<side>())); } assert(false); return std::wstring(); }(); } - void visit(const SymlinkPair& linkObj) override + void visit(const SymlinkPair& symlink) override { value = [&] { switch (colType_) { case COL_TYPE_FULL_PATH: - return linkObj.isEmpty<side>() ? std::wstring() : ABF::getDisplayPath(linkObj.getAbstractPath<side>()); + return symlink.isEmpty<side>() ? std::wstring() : AFS::getDisplayPath(symlink.getAbstractPath<side>()); case COL_TYPE_FILENAME: - return utfCvrtTo<std::wstring>(linkObj.getItemName<side>()); + return utfCvrtTo<std::wstring>(symlink.getItemName<side>()); case COL_TYPE_REL_FOLDER: - return utfCvrtTo<std::wstring>(beforeLast(linkObj.getPairRelativePath(), FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)); + return utfCvrtTo<std::wstring>(beforeLast(symlink.getPairRelativePath(), FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)); case COL_TYPE_BASE_DIRECTORY: - return ABF::getDisplayPath(linkObj.getABF<side>().getAbstractPath()); + return AFS::getDisplayPath(symlink.base().getAbstractPath<side>()); case COL_TYPE_SIZE: - return linkObj.isEmpty<side>() ? std::wstring() : L"<" + _("Symlink") + L">"; + return symlink.isEmpty<side>() ? std::wstring() : L"<" + _("Symlink") + L">"; case COL_TYPE_DATE: - return linkObj.isEmpty<side>() ? std::wstring() : utcToLocalTimeString(linkObj.getLastWriteTime<side>()); + return symlink.isEmpty<side>() ? std::wstring() : utcToLocalTimeString(symlink.getLastWriteTime<side>()); case COL_TYPE_EXTENSION: - return utfCvrtTo<std::wstring>(getFileExtension(linkObj.getItemName<side>())); + return utfCvrtTo<std::wstring>(getFileExtension(symlink.getItemName<side>())); } assert(false); return std::wstring(); }(); } - void visit(const DirPair& dirObj) override + void visit(const FolderPair& folder) override { value = [&] { switch (colType_) { case COL_TYPE_FULL_PATH: - return dirObj.isEmpty<side>() ? std::wstring() : ABF::getDisplayPath(dirObj.getAbstractPath<side>()); + return folder.isEmpty<side>() ? std::wstring() : AFS::getDisplayPath(folder.getAbstractPath<side>()); case COL_TYPE_FILENAME: - return utfCvrtTo<std::wstring>(dirObj.getItemName<side>()); + return utfCvrtTo<std::wstring>(folder.getItemName<side>()); case COL_TYPE_REL_FOLDER: - return utfCvrtTo<std::wstring>(beforeLast(dirObj.getPairRelativePath(), FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)); + return utfCvrtTo<std::wstring>(beforeLast(folder.getPairRelativePath(), FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)); case COL_TYPE_BASE_DIRECTORY: - return ABF::getDisplayPath(dirObj.getABF<side>().getAbstractPath()); + return AFS::getDisplayPath(folder.base().getAbstractPath<side>()); case COL_TYPE_SIZE: - return dirObj.isEmpty<side>() ? std::wstring() : L"<" + _("Folder") + L">"; + return folder.isEmpty<side>() ? std::wstring() : L"<" + _("Folder") + L">"; case COL_TYPE_DATE: return std::wstring(); case COL_TYPE_EXTENSION: @@ -479,7 +479,7 @@ private: return getVal.value; } //if data is not found: - return wxString(); + return std::wstring(); } static const int GAP_SIZE = 2; @@ -488,7 +488,7 @@ private: { wxRect rectTmp = rect; - const bool isActive = [&]() -> bool + const bool isActive = [&] { if (const FileSystemObject* fsObj = this->getRawData(row)) return fsObj->isActive(); @@ -601,7 +601,7 @@ private: return bestSize; // + 1 pix for cell border line -> not used anymore! } - wxString getColumnLabel(ColumnType colType) const override + std::wstring getColumnLabel(ColumnType colType) const override { switch (static_cast<ColumnTypeRim>(colType)) { @@ -620,7 +620,7 @@ private: case COL_TYPE_EXTENSION: return _("Extension"); } - return wxEmptyString; + return std::wstring(); } void renderColumnLabel(Grid& tree, wxDC& dc, const wxRect& rect, ColumnType colType, bool highlighted) override @@ -652,18 +652,18 @@ private: { enum IconType { - EMPTY = 0, //= default value! + EMPTY, FOLDER, ICON_PATH, }; - IconType type; - const FileSystemObject* fsObj; //only set if type != EMPTY - bool drawAsLink; + IconType type = EMPTY; + const FileSystemObject* fsObj = nullptr; //only set if type != EMPTY + bool drawAsLink = false; }; IconInfo getIconInfo(size_t row) const //return ICON_FILE_FOLDER if row points to a folder { - IconInfo out = {}; + IconInfo out; const FileSystemObject* fsObj = getRawData(row); if (fsObj && !fsObj->isEmpty<side>()) @@ -674,17 +674,17 @@ private: { GetIcon(IconInfo& ii) : ii_(ii) {} - void visit(const FilePair& fileObj) override + void visit(const FilePair& file) override { ii_.type = IconInfo::ICON_PATH; - ii_.drawAsLink = fileObj.isFollowedSymlink<side>() || hasLinkExtension(fileObj.getItemName<side>()); + ii_.drawAsLink = file.isFollowedSymlink<side>() || hasLinkExtension(file.getItemName<side>()); } - void visit(const SymlinkPair& linkObj) override + void visit(const SymlinkPair& symlink) override { ii_.type = IconInfo::ICON_PATH; ii_.drawAsLink = true; } - void visit(const DirPair& dirObj) override + void visit(const FolderPair& folder) override { ii_.type = IconInfo::FOLDER; //todo: if ("is followed symlink") ii_.drawAsLink = true; @@ -697,7 +697,7 @@ private: return out; } - wxString getToolTip(size_t row, ColumnType colType) const override + std::wstring getToolTip(size_t row, ColumnType colType) const override { std::wstring toolTip; @@ -705,27 +705,27 @@ private: if (!fsObj->isEmpty<side>()) { toolTip = getGridDataView() && getGridDataView()->getFolderPairCount() > 1 ? - ABF::getDisplayPath(fsObj->getAbstractPath<side>()) : + AFS::getDisplayPath(fsObj->getAbstractPath<side>()) : utfCvrtTo<std::wstring>(fsObj->getRelativePath<side>()); struct AssembleTooltip : public FSObjectVisitor { AssembleTooltip(std::wstring& tipMsg) : tipMsg_(tipMsg) {} - void visit(const FilePair& fileObj) override + void visit(const FilePair& file) override { tipMsg_ += L"\n" + - _("Size:") + L" " + zen::filesizeToShortString(fileObj.getFileSize<side>()) + L"\n" + - _("Date:") + L" " + zen::utcToLocalTimeString(fileObj.getLastWriteTime<side>()); + _("Size:") + L" " + zen::filesizeToShortString(file.getFileSize<side>()) + L"\n" + + _("Date:") + L" " + zen::utcToLocalTimeString(file.getLastWriteTime<side>()); } - void visit(const SymlinkPair& linkObj) override + void visit(const SymlinkPair& symlink) override { tipMsg_ += L"\n" + - _("Date:") + L" " + zen::utcToLocalTimeString(linkObj.getLastWriteTime<side>()); + _("Date:") + L" " + zen::utcToLocalTimeString(symlink.getLastWriteTime<side>()); } - void visit(const DirPair& dirObj) override {} + void visit(const FolderPair& folder) override {} std::wstring& tipMsg_; } assembler(toolTip); @@ -738,7 +738,7 @@ private: std::vector<char> failedLoads; //effectively a vector<bool> of size "number of rows" - std::unique_ptr<wxBitmap> buffer; //avoid costs of recreating this temporal variable + Opt<wxBitmap> buffer; //avoid costs of recreating this temporal variable }; @@ -762,16 +762,16 @@ private: //mark rows selected on navigation grid: if (enabled && !selected) { - const bool markRow = [&]() -> bool + const bool markRow = [&] { if (const FileSystemObject* fsObj = getRawData(row)) { if (markedFilesAndLinks_.find(fsObj) != markedFilesAndLinks_.end()) //mark files/links directly return true; - if (auto dirObj = dynamic_cast<const DirPair*>(fsObj)) + if (auto folder = dynamic_cast<const FolderPair*>(fsObj)) { - if (markedContainer_.find(dirObj) != markedContainer_.end()) //mark directories which *are* the given HierarchyObject* + if (markedContainer_.find(folder) != markedContainer_.end()) //mark directories which *are* the given HierarchyObject* return true; } @@ -782,8 +782,8 @@ private: if (markedContainer_.find(parent) != markedContainer_.end()) return true; - if (auto dirObj = dynamic_cast<const DirPair*>(parent)) - parent = &(dirObj->parent()); + if (auto folder = dynamic_cast<const FolderPair*>(parent)) + parent = &(folder->parent()); else break; } @@ -935,7 +935,7 @@ public: void highlightSyncAction(bool value) { highlightSyncAction_ = value; } private: - wxString getValue(size_t row, ColumnType colType) const override + std::wstring getValue(size_t row, ColumnType colType) const override { if (const FileSystemObject* fsObj = getRawData(row)) switch (static_cast<ColumnTypeMiddle>(colType)) @@ -947,7 +947,7 @@ private: case COL_TYPE_SYNC_ACTION: return getSymbol(fsObj->getSyncOperation()); } - return wxString(); + return std::wstring(); } void renderRowBackgound(wxDC& dc, const wxRect& rect, size_t row, bool enabled, bool selected) override @@ -1058,7 +1058,7 @@ private: } } - wxString getColumnLabel(ColumnType colType) const override + std::wstring getColumnLabel(ColumnType colType) const override { switch (static_cast<ColumnTypeMiddle>(colType)) { @@ -1069,10 +1069,10 @@ private: case COL_TYPE_SYNC_ACTION: return _("Action") + L" (F10)"; } - return wxEmptyString; + return std::wstring(); } - wxString getToolTip(ColumnType colType) const override { return getColumnLabel(colType); } + std::wstring getToolTip(ColumnType colType) const override { return getColumnLabel(colType); } void renderColumnLabel(Grid& tree, wxDC& dc, const wxRect& rect, ColumnType colType, bool highlighted) override { @@ -1320,7 +1320,7 @@ private: }; std::unique_ptr<MouseHighlight> highlight; //current mouse highlight std::unique_ptr<std::pair<size_t, BlockPosition>> dragSelection; //(row, block); area clicked when beginning selection - std::unique_ptr<wxBitmap> buffer; //avoid costs of recreating this temporal variable + Opt<wxBitmap> buffer; //avoid costs of recreating this temporal variable Tooltip toolTip; wxImage notch; }; @@ -1693,16 +1693,16 @@ private: void loadIconsAsynchronously(wxEvent& event) //loads all (not yet) drawn icons { - std::vector<std::pair<ptrdiff_t, AbstractPathRef>> prefetchLoad; + std::vector<std::pair<ptrdiff_t, AbstractPath>> prefetchLoad; provLeft_ .getUnbufferedIconsForPreload(prefetchLoad); provRight_.getUnbufferedIconsForPreload(prefetchLoad); //make sure least-important prefetch rows are inserted first into workload (=> processed last) - using Pft = decltype(prefetchLoad)::value_type; //priority index nicely considers both grids at the same time! - std::sort(prefetchLoad.begin(), prefetchLoad.end(), [](const Pft& lhs, const Pft& rhs) { return lhs.first < rhs.first; }); + //priority index nicely considers both grids at the same time! + std::sort(prefetchLoad.begin(), prefetchLoad.end(), [](const auto& lhs, const auto& rhs) { return lhs.first < rhs.first; }); //last inserted items are processed first in icon buffer: - std::vector<AbstractPathRef> newLoad; + std::vector<AbstractPath> newLoad; for (const auto& item : prefetchLoad) newLoad.push_back(item.second); @@ -1711,7 +1711,7 @@ private: iconBuffer_.setWorkload(newLoad); - if (newLoad.empty()) //let's only pay for iconupdater when needed + if (newLoad.empty()) //let's only pay for IconUpdater when needed stop(); } diff --git a/FreeFileSync/Source/ui/custom_grid.h b/FreeFileSync/Source/ui/custom_grid.h index add36fa5..3546a14a 100644 --- a/FreeFileSync/Source/ui/custom_grid.h +++ b/FreeFileSync/Source/ui/custom_grid.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef CUSTOMGRID_H_INCLUDED -#define CUSTOMGRID_H_INCLUDED +#ifndef CUSTOM_GRID_H_8405817408327894 +#define CUSTOM_GRID_H_8405817408327894 #include <wx+/grid.h> #include "grid_view.h" @@ -79,4 +79,4 @@ typedef void (wxEvtHandler::*SyncDirectionEventFunction)(SyncDirectionEvent&); (wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(SyncDirectionEventFunction, &func) } -#endif // CUSTOMGRID_H_INCLUDED +#endif //CUSTOM_GRID_H_8405817408327894 diff --git a/FreeFileSync/Source/ui/folder_history_box.cpp b/FreeFileSync/Source/ui/folder_history_box.cpp index b96a540b..919acd00 100644 --- a/FreeFileSync/Source/ui/folder_history_box.cpp +++ b/FreeFileSync/Source/ui/folder_history_box.cpp @@ -66,13 +66,13 @@ void FolderHistoryBox::OnRequireHistoryUpdate(wxEvent& event) } //set value and update list are technically entangled: see potential bug description below -void FolderHistoryBox::setValueAndUpdateList(const wxString& dirpath) +void FolderHistoryBox::setValueAndUpdateList(const wxString& folderPathPhrase) { //populate selection list.... std::vector<wxString> dirList; { //add some aliases to allow user changing to volume name and back, if possible - std::vector<Zstring> aliases = getDirectoryAliases(toZ(dirpath)); //may block when resolving [<volume name>] + std::vector<Zstring> aliases = getDirectoryAliases(toZ(folderPathPhrase)); //may block when resolving [<volume name>] std::transform(aliases.begin(), aliases.end(), std::back_inserter(dirList), [](const Zstring& str) { return utfCvrtTo<wxString>(str); }); } if (sharedHistory_.get()) @@ -91,8 +91,8 @@ void FolderHistoryBox::setValueAndUpdateList(const wxString& dirpath) //attention: if the target value is not part of the dropdown list, SetValue() will look for a string that *starts with* this value: //e.g. if the dropdown list contains "222" SetValue("22") will erroneously set and select "222" instead, while "111" would be set correctly! // -> by design on Windows! - if (std::find(dirList.begin(), dirList.end(), dirpath) == dirList.end()) - dirList.insert(dirList.begin(), dirpath); + if (std::find(dirList.begin(), dirList.end(), folderPathPhrase) == dirList.end()) + dirList.insert(dirList.begin(), folderPathPhrase); //this->Clear(); -> NO! emits yet another wxEVT_COMMAND_TEXT_UPDATED!!! wxItemContainer::Clear(); //suffices to clear the selection items only! @@ -100,7 +100,7 @@ void FolderHistoryBox::setValueAndUpdateList(const wxString& dirpath) for (const wxString& dir : dirList) this->Append(dir); //this->SetSelection(wxNOT_FOUND); //don't select anything - ChangeValue(dirpath); //preserve main text! + ChangeValue(folderPathPhrase); //preserve main text! } @@ -116,7 +116,7 @@ void FolderHistoryBox::OnKeyEvent(wxKeyEvent& event) if (0 <= pos && pos < static_cast<int>(this->GetCount()) && //what a mess...: (GetValue() != GetString(pos) || //avoid problems when a character shall be deleted instead of list item - GetValue() == wxEmptyString)) //exception: always allow removing empty entry + GetValue().empty())) //exception: always allow removing empty entry { //save old (selected) value: deletion seems to have influence on this const wxString currentVal = this->GetValue(); diff --git a/FreeFileSync/Source/ui/folder_history_box.h b/FreeFileSync/Source/ui/folder_history_box.h index 605f1444..4c220bd9 100644 --- a/FreeFileSync/Source/ui/folder_history_box.h +++ b/FreeFileSync/Source/ui/folder_history_box.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef CUSTOMCOMBOBOX_H_INCLUDED -#define CUSTOMCOMBOBOX_H_INCLUDED +#ifndef FOLDER_HISTORY_BOX_H_08170517045945 +#define FOLDER_HISTORY_BOX_H_08170517045945 #include <wx/combobox.h> #include <memory> @@ -21,39 +21,39 @@ class FolderHistory public: FolderHistory() : maxSize_(0) {} - FolderHistory(const std::vector<Zstring>& dirpaths, size_t maxSize) : + FolderHistory(const std::vector<Zstring>& folderPathPhrases, size_t maxSize) : maxSize_(maxSize), - dirpaths_(dirpaths) + folderPathPhrases_(folderPathPhrases) { - if (dirpaths_.size() > maxSize_) //keep maximal size of history list - dirpaths_.resize(maxSize_); + if (folderPathPhrases_.size() > maxSize_) //keep maximal size of history list + folderPathPhrases_.resize(maxSize_); } - const std::vector<Zstring>& getList() const { return dirpaths_; } + const std::vector<Zstring>& getList() const { return folderPathPhrases_; } static const wxString separationLine() { return L"---------------------------------------------------------------------------------------------------------------"; } - void addItem(const Zstring& dirpath) + void addItem(const Zstring& folderPathPhrase) { - if (dirpath.empty() || dirpath == zen::utfCvrtTo<Zstring>(separationLine())) + if (folderPathPhrase.empty() || folderPathPhrase == zen::utfCvrtTo<Zstring>(separationLine())) return; - const Zstring nameTmp = zen::trimCpy(dirpath); + const Zstring nameTmp = zen::trimCpy(folderPathPhrase); //insert new folder or put it to the front if already existing - zen::erase_if(dirpaths_, [&](const Zstring& item) { return ::EqualFilePath()(item, nameTmp); }); + zen::erase_if(folderPathPhrases_, [&](const Zstring& item) { return equalFilePath(item, nameTmp); }); - dirpaths_.insert(dirpaths_.begin(), nameTmp); + folderPathPhrases_.insert(folderPathPhrases_.begin(), nameTmp); - if (dirpaths_.size() > maxSize_) //keep maximal size of history list - dirpaths_.resize(maxSize_); + if (folderPathPhrases_.size() > maxSize_) //keep maximal size of history list + folderPathPhrases_.resize(maxSize_); } - void delItem(const Zstring& dirpath) { zen::erase_if(dirpaths_, [&](const Zstring& item) { return ::EqualFilePath()(item, dirpath); }); } + void delItem(const Zstring& folderPathPhrase) { zen::erase_if(folderPathPhrases_, [&](const Zstring& item) { return equalFilePath(item, folderPathPhrase); }); } private: size_t maxSize_; - std::vector<Zstring> dirpaths_; + std::vector<Zstring> folderPathPhrases_; }; @@ -62,7 +62,7 @@ class FolderHistoryBox : public wxComboBox public: FolderHistoryBox(wxWindow* parent, wxWindowID id, - const wxString& value = wxEmptyString, + const wxString& value = {}, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, int n = 0, @@ -73,9 +73,9 @@ public: void init(const std::shared_ptr<FolderHistory>& sharedHistory) { sharedHistory_ = sharedHistory; } - void setValue(const wxString& dirpath) + void setValue(const wxString& folderPathPhrase) { - setValueAndUpdateList(dirpath); //required for setting value correctly; Linux: ensure the dropdown is shown as being populated + setValueAndUpdateList(folderPathPhrase); //required for setting value correctly; Linux: ensure the dropdown is shown as being populated } // GetValue @@ -83,10 +83,10 @@ public: private: void OnKeyEvent(wxKeyEvent& event); void OnRequireHistoryUpdate(wxEvent& event); - void setValueAndUpdateList(const wxString& dirpath); + void setValueAndUpdateList(const wxString& folderPathPhrase); std::shared_ptr<FolderHistory> sharedHistory_; }; -#endif // CUSTOMCOMBOBOX_H_INCLUDED +#endif //FOLDER_HISTORY_BOX_H_08170517045945 diff --git a/FreeFileSync/Source/ui/folder_history_types.h b/FreeFileSync/Source/ui/folder_history_types.h index ce7e5446..42f58c79 100644 --- a/FreeFileSync/Source/ui/folder_history_types.h +++ b/FreeFileSync/Source/ui/folder_history_types.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef FOLDER_HIST_TYPES_32481457137432143214 -#define FOLDER_HIST_TYPES_32481457137432143214 +#ifndef FOLDER_HISTORY_TYPES_H_32481457137432143214 +#define FOLDER_HISTORY_TYPES_H_32481457137432143214 #include <zen/zstring.h> @@ -21,4 +21,4 @@ struct ConfigHistoryItem }; } -#endif //FOLDER_HIST_TYPES_32481457137432143214 +#endif //FOLDER_HISTORY_TYPES_H_32481457137432143214 diff --git a/FreeFileSync/Source/ui/folder_pair.h b/FreeFileSync/Source/ui/folder_pair.h index 721af19e..ccc1a57d 100644 --- a/FreeFileSync/Source/ui/folder_pair.h +++ b/FreeFileSync/Source/ui/folder_pair.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef FOLDERPAIR_H_89341750847252345 -#define FOLDERPAIR_H_89341750847252345 +#ifndef FOLDER_PAIR_H_89341750847252345 +#define FOLDER_PAIR_H_89341750847252345 #include <wx/event.h> #include <wx/menu.h> @@ -169,4 +169,4 @@ private: } -#endif //FOLDERPAIR_H_89341750847252345 +#endif //FOLDER_PAIR_H_89341750847252345 diff --git a/FreeFileSync/Source/ui/folder_selector.cpp b/FreeFileSync/Source/ui/folder_selector.cpp index 2c5e94d3..9cb5ce56 100644 --- a/FreeFileSync/Source/ui/folder_selector.cpp +++ b/FreeFileSync/Source/ui/folder_selector.cpp @@ -27,9 +27,10 @@ // #include <gtk/gtk.h> #endif - using namespace zen; -using ABF = AbstractBaseFolder; +#ifndef ZEN_WIN_VISTA_AND_LATER + using AFS = AbstractFileSystem; +#endif namespace @@ -39,7 +40,7 @@ void setFolderPathPhrase(const Zstring& folderPathPhrase, FolderHistoryBox* comb if (comboBox) comboBox->setValue(toWx(folderPathPhrase)); - const Zstring folderPathPhraseFmt = createAbstractBaseFolder(folderPathPhrase)->getInitPathPhrase(); //noexcept + const Zstring folderPathPhraseFmt = AFS::getInitPathPhrase(createAbstractPath(folderPathPhrase)); //noexcept //may block when resolving [<volume name>] tooltipWnd.SetToolTip(nullptr); //workaround wxComboBox bug http://trac.wxwidgets.org/ticket/10512 / http://trac.wxwidgets.org/ticket/12659 @@ -48,7 +49,7 @@ void setFolderPathPhrase(const Zstring& folderPathPhrase, FolderHistoryBox* comb if (staticText) { //change static box label only if there is a real difference to what is shown in wxTextCtrl anyway - staticText->SetLabel(EqualFilePath()(appendSeparator(trimCpy(folderPathPhrase)), appendSeparator(folderPathPhraseFmt)) ? + staticText->SetLabel(equalFilePath(appendSeparator(trimCpy(folderPathPhrase)), appendSeparator(folderPathPhraseFmt)) ? wxString(_("Drag && drop")) : toWx(folderPathPhraseFmt)); } } @@ -61,8 +62,8 @@ bool acceptShellItemPaths(const std::vector<Zstring>& shellItemPaths) //- MTP paths if (shellItemPaths.empty()) return false; - return acceptsFolderPathPhraseNative(shellItemPaths[0]) || // - acceptsFolderPathPhraseMtp (shellItemPaths[0]); //noexcept + return acceptsItemPathPhraseNative(shellItemPaths[0]) || // + acceptsItemPathPhraseMtp (shellItemPaths[0]); //noexcept } @@ -171,9 +172,8 @@ void FolderSelector::onFilesDropped(FileDropEvent& event) { auto fmtShellPath = [](const Zstring& shellItemPath) { - std::unique_ptr<ABF> abf = createAbstractBaseFolder(shellItemPath); - - if (!ABF::folderExists(abf->getAbstractPath())) + const AbstractPath itemPath = createAbstractPath(shellItemPath); + if (!AFS::folderExists(itemPath)) { Zstring parentShellPath = beforeLast(shellItemPath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE); if (!parentShellPath.empty()) @@ -182,15 +182,14 @@ void FolderSelector::onFilesDropped(FileDropEvent& event) if (endsWith(parentShellPath, L":")) //volume root parentShellPath += FILE_NAME_SEPARATOR; #endif - std::unique_ptr<ABF> abfParent = createAbstractBaseFolder(parentShellPath); - - if (ABF::folderExists(abfParent->getAbstractPath())) - return abfParent->getInitPathPhrase(); + const AbstractPath parentPath = createAbstractPath(parentShellPath); + if (AFS::folderExists(parentPath)) + return AFS::getInitPathPhrase(parentPath); //else: keep original name unconditionally: usecase: inactive mapped network shares } } //make sure FFS-specific explicit MTP-syntax is applied! - return abf->getInitPathPhrase(); + return AFS::getInitPathPhrase(itemPath); }; setPath(fmtShellPath(itemPaths[0])); @@ -225,35 +224,26 @@ void FolderSelector::onSelectFolder(wxCommandEvent& event) std::shared_ptr<const void> /*PCIDLIST_ABSOLUTE*/ defaultFolderPidl; #endif { - auto baseFolderExisting = [](const ABF& abf) + auto folderExistsTimed = [](const AbstractPath& folderPath) { - std::function<bool()> /*throw FileError*/ dirExists = ABF::getAsyncCheckFolderExists(abf.getAbstractPath()); //noexcept - - auto ft = runAsync([dirExists] - { - try - { - return dirExists(); //throw FileError - } - catch (FileError&) { return false; } - }); + auto ft = runAsync([folderPath] { return AFS::folderExists(folderPath); }); return ft.wait_for(std::chrono::milliseconds(200)) == std::future_status::ready && ft.get(); //potentially slow network access: wait 200ms at most }; const Zstring folderPathPhrase = getPath(); - if (acceptsFolderPathPhraseNative(folderPathPhrase)) //noexcept + if (acceptsItemPathPhraseNative(folderPathPhrase)) //noexcept { - std::unique_ptr<ABF> abf = createBaseFolderNative(folderPathPhrase); - if (baseFolderExisting(*abf)) - if (Opt<Zstring> nativeFolderPath = ABF::getNativeItemPath(abf->getAbstractPath())) + const AbstractPath folderPath = createItemPathNative(folderPathPhrase); + if (folderExistsTimed(folderPath)) + if (Opt<Zstring> nativeFolderPath = AFS::getNativeItemPath(folderPath)) defaultFolderPath = *nativeFolderPath; } #ifdef ZEN_WIN_VISTA_AND_LATER - else if (acceptsFolderPathPhraseMtp(folderPathPhrase)) //noexcept + else if (acceptsItemPathPhraseMtp(folderPathPhrase)) //noexcept { - std::unique_ptr<ABF> abf = createBaseFolderMtp(folderPathPhrase); - if (baseFolderExisting(*abf)) - defaultFolderPidl = geMtpItemAbsolutePidl(abf->getAbstractPath()); + const AbstractPath folderPath = createItemPathMtp(folderPathPhrase); + if (folderExistsTimed(folderPath)) + defaultFolderPidl = geMtpItemAbsolutePidl(folderPath); } #endif } @@ -275,7 +265,7 @@ void FolderSelector::onSelectFolder(wxCommandEvent& event) return; //make sure FFS-specific explicit MTP-syntax is applied! - newFolderPathPhrase = createAbstractBaseFolder(rv.first)->getInitPathPhrase(); //noexcept + newFolderPathPhrase = AFS::getInitPathPhrase(createAbstractPath(rv.first)); //noexcept } catch (const FileError& e) { showNotificationDialog(&dropWindow_, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString())); return; } #else diff --git a/FreeFileSync/Source/ui/folder_selector.h b/FreeFileSync/Source/ui/folder_selector.h index 961acbe8..6e7c7791 100644 --- a/FreeFileSync/Source/ui/folder_selector.h +++ b/FreeFileSync/Source/ui/folder_selector.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef DIR_NAME_H_24857842375234523463425 -#define DIR_NAME_H_24857842375234523463425 +#ifndef FOLDER_SELECTOR_H_24857842375234523463425 +#define FOLDER_SELECTOR_H_24857842375234523463425 #include <zen/zstring.h> #include <wx/stattext.h> @@ -47,7 +47,7 @@ public: void setPath(const Zstring& folderPathPhrase); private: - virtual bool canSetDroppedShellPaths(const std::vector<Zstring>& shellItemPaths) { return true; }; //return true if drop should be processed + virtual bool canSetDroppedShellPaths(const std::vector<Zstring>& shellItemPaths) { return true; } //return true if drop should be processed void onMouseWheel (wxMouseEvent& event); void onFilesDropped (FileDropEvent& event); @@ -65,4 +65,4 @@ private: }; } -#endif //DIR_NAME_H_24857842375234523463425 +#endif //FOLDER_SELECTOR_H_24857842375234523463425 diff --git a/FreeFileSync/Source/ui/grid_view.cpp b/FreeFileSync/Source/ui/grid_view.cpp index db7f64e9..04d1f277 100644 --- a/FreeFileSync/Source/ui/grid_view.cpp +++ b/FreeFileSync/Source/ui/grid_view.cpp @@ -19,35 +19,35 @@ void addNumbers(const FileSystemObject& fsObj, StatusResult& result) { GetValues(StatusResult& res) : result_(res) {} - void visit(const FilePair& fileObj) override + void visit(const FilePair& file) override { - if (!fileObj.isEmpty<LEFT_SIDE>()) + if (!file.isEmpty<LEFT_SIDE>()) { - result_.filesizeLeftView += fileObj.getFileSize<LEFT_SIDE>(); + result_.filesizeLeftView += file.getFileSize<LEFT_SIDE>(); ++result_.filesOnLeftView; } - if (!fileObj.isEmpty<RIGHT_SIDE>()) + if (!file.isEmpty<RIGHT_SIDE>()) { - result_.filesizeRightView += fileObj.getFileSize<RIGHT_SIDE>(); + result_.filesizeRightView += file.getFileSize<RIGHT_SIDE>(); ++result_.filesOnRightView; } } - void visit(const SymlinkPair& linkObj) override + void visit(const SymlinkPair& symlink) override { - if (!linkObj.isEmpty<LEFT_SIDE>()) + if (!symlink.isEmpty<LEFT_SIDE>()) ++result_.filesOnLeftView; - if (!linkObj.isEmpty<RIGHT_SIDE>()) + if (!symlink.isEmpty<RIGHT_SIDE>()) ++result_.filesOnRightView; } - void visit(const DirPair& dirObj) override + void visit(const FolderPair& folder) override { - if (!dirObj.isEmpty<LEFT_SIDE>()) + if (!folder.isEmpty<LEFT_SIDE>()) ++result_.foldersOnLeftView; - if (!dirObj.isEmpty<RIGHT_SIDE>()) + if (!folder.isEmpty<RIGHT_SIDE>()) ++result_.foldersOnRightView; } StatusResult& result_; @@ -69,11 +69,11 @@ void GridView::updateView(Predicate pred) if (const FileSystemObject* fsObj = FileSystemObject::retrieve(ref.objId)) if (pred(*fsObj)) { - //save row position for direct random access to FilePair or DirPair + //save row position for direct random access to FilePair or FolderPair this->rowPositions.emplace(ref.objId, viewRef.size()); //costs: 0.28 µs per call - MSVC based on std::set //"this->" required by two-pass lookup as enforced by GCC 4.7 - //save row position to identify first child *on sorted subview* of DirPair or BaseDirPair in case latter are filtered out + //save row position to identify first child *on sorted subview* of FolderPair or BaseFolderPair in case latter are filtered out const HierarchyObject* parent = &fsObj->parent(); for (;;) //map all yet unassociated parents to this row { @@ -81,8 +81,8 @@ void GridView::updateView(Predicate pred) if (!rv.second) break; - if (auto dirObj = dynamic_cast<const DirPair*>(parent)) - parent = &(dirObj->parent()); + if (auto folder = dynamic_cast<const FolderPair*>(parent)) + parent = &(folder->parent()); else break; } @@ -107,23 +107,6 @@ ptrdiff_t GridView::findRowFirstChild(const HierarchyObject* hierObj) const } -GridView::StatusCmpResult::StatusCmpResult() : - existsExcluded (false), - existsEqual (false), - existsConflict (false), - existsLeftOnly (false), - existsRightOnly (false), - existsLeftNewer (false), - existsRightNewer(false), - existsDifferent (false), - filesOnLeftView (0), - foldersOnLeftView (0), - filesOnRightView (0), - foldersOnRightView(0), - filesizeLeftView (0), - filesizeRightView (0) {} - - GridView::StatusCmpResult GridView::updateCmpResult(bool showExcluded, //maps sortedRef to viewRef bool leftOnlyFilesActive, bool rightOnlyFilesActive, @@ -185,25 +168,6 @@ GridView::StatusCmpResult GridView::updateCmpResult(bool showExcluded, //maps so } -GridView::StatusSyncPreview::StatusSyncPreview() : - existsExcluded (false), - existsEqual (false), - existsConflict (false), - existsSyncCreateLeft (false), - existsSyncCreateRight(false), - existsSyncDeleteLeft (false), - existsSyncDeleteRight(false), - existsSyncDirLeft (false), - existsSyncDirRight (false), - existsSyncDirNone (false), - filesOnLeftView (0), - foldersOnLeftView (0), - filesOnRightView (0), - foldersOnRightView(0), - filesizeLeftView (0), - filesizeRightView (0) {} - - GridView::StatusSyncPreview GridView::updateSyncPreview(bool showExcluded, //maps sortedRef to viewRef bool syncCreateLeftActive, bool syncCreateRightActive, @@ -319,14 +283,14 @@ private: void recurse(HierarchyObject& hierObj) { - for (FilePair& fileObj : hierObj.refSubFiles()) - sortedRef_.emplace_back(index_, fileObj.getId()); - for (SymlinkPair& linkObj : hierObj.refSubLinks()) - sortedRef_.emplace_back(index_, linkObj.getId()); - for (DirPair& dirObj : hierObj.refSubDirs()) + for (FilePair& file : hierObj.refSubFiles()) + sortedRef_.emplace_back(index_, file.getId()); + for (SymlinkPair& symlink : hierObj.refSubLinks()) + sortedRef_.emplace_back(index_, symlink.getId()); + for (FolderPair& folder : hierObj.refSubFolders()) { - sortedRef_.emplace_back(index_, dirObj.getId()); - recurse(dirObj); //add recursion here to list sub-objects directly below parent! + sortedRef_.emplace_back(index_, folder.getId()); + recurse(folder); //add recursion here to list sub-objects directly below parent! } } @@ -340,13 +304,13 @@ void GridView::setData(FolderComparison& folderCmp) //clear everything std::vector<FileSystemObject::ObjectId>().swap(viewRef); //free mem std::vector<RefIndex>().swap(sortedRef); // - currentSort.reset(); + currentSort = NoValue(); folderPairCount = std::count_if(begin(folderCmp), end(folderCmp), - [](const BaseDirPair& baseObj) //count non-empty pairs to distinguish single/multiple folder pair cases + [](const BaseFolderPair& baseObj) //count non-empty pairs to distinguish single/multiple folder pair cases { - return !baseObj.getABF<LEFT_SIDE >().emptyBaseFolderPath() || - !baseObj.getABF<RIGHT_SIDE>().emptyBaseFolderPath(); + return !AFS::isNullPath(baseObj.getAbstractPath<LEFT_SIDE >()) || + !AFS::isNullPath(baseObj.getAbstractPath<RIGHT_SIDE>()); }); for (auto it = begin(folderCmp); it != end(folderCmp); ++it) @@ -522,7 +486,7 @@ void GridView::sortView(ColumnTypeRim type, bool onLeft, bool ascending) viewRef.clear(); rowPositions.clear(); rowPositionsFirstChild.clear(); - currentSort = std::make_unique<SortInfo>(type, onLeft, ascending); + currentSort = SortInfo(type, onLeft, ascending); switch (type) { diff --git a/FreeFileSync/Source/ui/grid_view.h b/FreeFileSync/Source/ui/grid_view.h index 5a2fb293..17311890 100644 --- a/FreeFileSync/Source/ui/grid_view.h +++ b/FreeFileSync/Source/ui/grid_view.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef GRIDVIEW_H_INCLUDED -#define GRIDVIEW_H_INCLUDED +#ifndef GRID_VIEW_H_9285028345703475842569 +#define GRID_VIEW_H_9285028345703475842569 #include <vector> #include <unordered_map> @@ -19,7 +19,7 @@ namespace zen class GridView { public: - GridView() : folderPairCount(0) {} + GridView() {} //direct data access via row number const FileSystemObject* getObject(size_t row) const; //returns nullptr if object is not found; complexity: constant! @@ -33,25 +33,23 @@ public: struct StatusCmpResult { - StatusCmpResult(); - - bool existsExcluded; - bool existsEqual; - bool existsConflict; - - bool existsLeftOnly; - bool existsRightOnly; - bool existsLeftNewer; - bool existsRightNewer; - bool existsDifferent; - - unsigned int filesOnLeftView; - unsigned int foldersOnLeftView; - unsigned int filesOnRightView; - unsigned int foldersOnRightView; - - std::uint64_t filesizeLeftView; - std::uint64_t filesizeRightView; + bool existsExcluded = false; + bool existsEqual = false; + bool existsConflict = false; + + bool existsLeftOnly = false; + bool existsRightOnly = false; + bool existsLeftNewer = false; + bool existsRightNewer = false; + bool existsDifferent = false; + + unsigned int filesOnLeftView = 0; + unsigned int foldersOnLeftView = 0; + unsigned int filesOnRightView = 0; + unsigned int foldersOnRightView = 0; + + std::uint64_t filesizeLeftView = 0; + std::uint64_t filesizeRightView = 0; }; //comparison results view @@ -66,27 +64,25 @@ public: struct StatusSyncPreview { - StatusSyncPreview(); - - bool existsExcluded; - bool existsEqual; - bool existsConflict; - - bool existsSyncCreateLeft; - bool existsSyncCreateRight; - bool existsSyncDeleteLeft; - bool existsSyncDeleteRight; - bool existsSyncDirLeft; - bool existsSyncDirRight; - bool existsSyncDirNone; - - unsigned int filesOnLeftView; - unsigned int foldersOnLeftView; - unsigned int filesOnRightView; - unsigned int foldersOnRightView; - - std::uint64_t filesizeLeftView; - std::uint64_t filesizeRightView; + bool existsExcluded = false; + bool existsEqual = false; + bool existsConflict = false; + + bool existsSyncCreateLeft = false; + bool existsSyncCreateRight = false; + bool existsSyncDeleteLeft = false; + bool existsSyncDeleteRight = false; + bool existsSyncDirLeft = false; + bool existsSyncDirRight = false; + bool existsSyncDirNone = false; + + unsigned int filesOnLeftView = 0; + unsigned int foldersOnLeftView = 0; + unsigned int filesOnRightView = 0; + unsigned int foldersOnRightView = 0; + + std::uint64_t filesizeLeftView = 0; + std::uint64_t filesizeRightView = 0; }; //synchronization preview @@ -119,12 +115,15 @@ public: const SortInfo* getSortInfo() const { return currentSort.get(); } //return nullptr if currently not sorted ptrdiff_t findRowDirect(FileSystemObject::ObjectIdConst objId) const; // find an object's row position on view list directly, return < 0 if not found - ptrdiff_t findRowFirstChild(const HierarchyObject* hierObj) const; // find first child of DirPair or BaseDirPair *on sorted sub view* + ptrdiff_t findRowFirstChild(const HierarchyObject* hierObj) const; // find first child of FolderPair or BaseFolderPair *on sorted sub view* //"hierObj" may be invalid, it is NOT dereferenced, return < 0 if not found size_t getFolderPairCount() const { return folderPairCount; } //count non-empty pairs to distinguish single/multiple folder pair cases private: + GridView (const GridView&) = delete; + GridView& operator=(const GridView&) = delete; + struct RefIndex { RefIndex(size_t folderInd, FileSystemObject::ObjectId id) : @@ -150,7 +149,7 @@ private: | (setData...) | */ //std::shared_ptr<FolderComparison> folderCmp; //actual comparison data: owned by GridView! - size_t folderPairCount; //number of non-empty folder pairs + size_t folderPairCount = 0; //number of non-empty folder pairs class SerializeHierarchy; @@ -180,7 +179,7 @@ private: template <bool ascending> struct LessSyncDirection; - std::unique_ptr<SortInfo> currentSort; + Opt<SortInfo> currentSort; }; @@ -207,4 +206,4 @@ FileSystemObject* GridView::getObject(size_t row) } -#endif // GRIDVIEW_H_INCLUDED +#endif //GRID_VIEW_H_9285028345703475842569 diff --git a/FreeFileSync/Source/ui/gui_generated.cpp b/FreeFileSync/Source/ui/gui_generated.cpp index 1f912c77..f73d7d0f 100644 --- a/FreeFileSync/Source/ui/gui_generated.cpp +++ b/FreeFileSync/Source/ui/gui_generated.cpp @@ -126,10 +126,8 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const bSizerTopButtons = new wxBoxSizer( wxHORIZONTAL ); - m_bpButtonCmpConfig = new wxBitmapButton( m_panelTopButtons, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), wxBU_AUTODRAW ); - m_bpButtonCmpConfig->SetToolTip( _("dummy") ); - bSizerTopButtons->Add( m_bpButtonCmpConfig, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + bSizerTopButtons->Add( 0, 0, 1, wxALIGN_CENTER_VERTICAL, 5 ); m_buttonCancel = new zen::BitmapTextButton( m_panelTopButtons, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); m_buttonCancel->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); @@ -145,6 +143,11 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const bSizerTopButtons->Add( m_buttonCompare, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + m_bpButtonCmpConfig = new wxBitmapButton( m_panelTopButtons, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), wxBU_AUTODRAW ); + m_bpButtonCmpConfig->SetToolTip( _("dummy") ); + + bSizerTopButtons->Add( m_bpButtonCmpConfig, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + bSizerTopButtons->Add( 0, 0, 1, wxALIGN_CENTER_VERTICAL, 5 ); @@ -172,6 +175,9 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const bSizerTopButtons->Add( m_buttonSync, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + bSizerTopButtons->Add( 0, 0, 1, wxALIGN_CENTER_VERTICAL, 5 ); + + bSizer1791->Add( bSizerTopButtons, 1, wxEXPAND|wxALIGN_CENTER_HORIZONTAL, 5 ); @@ -935,9 +941,9 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const this->Connect( m_menuItemCheckVersionNow->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuCheckVersion ) ); this->Connect( m_menuItemCheckVersionAuto->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuCheckVersionAutomatically ) ); this->Connect( m_menuItemAbout->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuAbout ) ); + m_buttonCompare->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnCompare ), NULL, this ); m_bpButtonCmpConfig->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnCmpSettings ), NULL, this ); m_bpButtonCmpConfig->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnCompSettingsContext ), NULL, this ); - m_buttonCompare->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnCompare ), NULL, this ); m_bpButtonFilter->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnConfigureFilter ), NULL, this ); m_bpButtonFilter->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnGlobalFilterContext ), NULL, this ); m_bpButtonSyncConfig->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSyncSettings ), NULL, this ); diff --git a/FreeFileSync/Source/ui/gui_generated.h b/FreeFileSync/Source/ui/gui_generated.h index 309b86f1..c55ece18 100644 --- a/FreeFileSync/Source/ui/gui_generated.h +++ b/FreeFileSync/Source/ui/gui_generated.h @@ -28,8 +28,8 @@ namespace zen { class TripleSplitter; } #include <wx/font.h> #include <wx/colour.h> #include <wx/settings.h> -#include <wx/bmpbuttn.h> #include <wx/button.h> +#include <wx/bmpbuttn.h> #include <wx/sizer.h> #include <wx/panel.h> #include <wx/stattext.h> @@ -92,9 +92,9 @@ protected: wxBoxSizer* bSizerPanelHolder; wxPanel* m_panelTopButtons; wxBoxSizer* bSizerTopButtons; - wxBitmapButton* m_bpButtonCmpConfig; zen::BitmapTextButton* m_buttonCancel; zen::BitmapTextButton* m_buttonCompare; + wxBitmapButton* m_bpButtonCmpConfig; wxBitmapButton* m_bpButtonFilter; wxBitmapButton* m_bpButtonSyncConfig; zen::BitmapTextButton* m_buttonSync; diff --git a/FreeFileSync/Source/ui/gui_status_handler.cpp b/FreeFileSync/Source/ui/gui_status_handler.cpp index d3b9be5c..56b73506 100644 --- a/FreeFileSync/Source/ui/gui_status_handler.cpp +++ b/FreeFileSync/Source/ui/gui_status_handler.cpp @@ -400,7 +400,7 @@ ProcessCallback::Response StatusHandlerFloatingDialog::reportError(const std::ws //always, except for "retry": - zen::ScopeGuard guardWriteLog = zen::makeGuard([&] { errorLog.logMsg(errorMessage, TYPE_ERROR); }); + auto guardWriteLog = zen::makeGuard<ScopeGuardRunMode::ON_EXIT>([&] { errorLog.logMsg(errorMessage, TYPE_ERROR); }); switch (handleError_) { diff --git a/FreeFileSync/Source/ui/gui_status_handler.h b/FreeFileSync/Source/ui/gui_status_handler.h index 9674c448..1483856b 100644 --- a/FreeFileSync/Source/ui/gui_status_handler.h +++ b/FreeFileSync/Source/ui/gui_status_handler.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef GUISTATUSHANDLER_H_INCLUDED -#define GUISTATUSHANDLER_H_INCLUDED +#ifndef GUI_STATUS_HANDLER_H_0183247018545 +#define GUI_STATUS_HANDLER_H_0183247018545 #include <zen/error_log.h> #include <wx/event.h> @@ -84,4 +84,4 @@ private: }; -#endif // GUISTATUSHANDLER_H_INCLUDED +#endif //GUI_STATUS_HANDLER_H_0183247018545 diff --git a/FreeFileSync/Source/ui/main_dlg.cpp b/FreeFileSync/Source/ui/main_dlg.cpp index c392ebc7..a32b4870 100644 --- a/FreeFileSync/Source/ui/main_dlg.cpp +++ b/FreeFileSync/Source/ui/main_dlg.cpp @@ -401,7 +401,13 @@ void MainDialog::create(const Zstring& globalConfigFile) GetFirstResult<FalseType> firstMissingDir; for (const Zstring& filepath : filepaths) - firstMissingDir.addJob([filepath] { return filepath.empty() /*ever empty??*/ || !fileExists(filepath) ? std::make_unique<FalseType>() : nullptr; }); + firstMissingDir.addJob([filepath] () -> Opt<FalseType> + { + assert(!filepath.empty()); + if (filepath.empty() /*ever empty??*/ || !fileExists(filepath)) + return FalseType(); + return NoValue(); + }); //potentially slow network access: give all checks 500ms to finish const bool allFilesExist = firstMissingDir.timedWait(std::chrono::milliseconds(500)) && //false: time elapsed @@ -795,42 +801,38 @@ MainDialog::MainDialog(const Zstring& globalConfigFile, bool havePartialPair = false; bool haveFullPair = false; - std::vector<std::function<bool()>> asyncDirChecks; + std::vector<AbstractPath> folderPathsToCheck; - auto addDirCheck = [&](const FolderPairEnh& fp) + auto addFolderCheck = [&](const FolderPairEnh& fp) { - std::unique_ptr<ABF> abfL = createAbstractBaseFolder(fp.folderPathPhraseLeft_); - std::unique_ptr<ABF> abfR = createAbstractBaseFolder(fp.folderPathPhraseRight_); + const AbstractPath folderPathL = createAbstractPath(fp.folderPathPhraseLeft_); + const AbstractPath folderPathR = createAbstractPath(fp.folderPathPhraseRight_); - if (abfL->emptyBaseFolderPath() != abfR->emptyBaseFolderPath()) //only skip check if both sides are empty! + if (AFS::isNullPath(folderPathL) != AFS::isNullPath(folderPathR)) //only skip check if both sides are empty! havePartialPair = true; - else if (!abfL->emptyBaseFolderPath()) + else if (!AFS::isNullPath(folderPathL)) haveFullPair = true; - if (!abfL->emptyBaseFolderPath()) - asyncDirChecks.push_back(ABF::getAsyncCheckFolderExists(abfL->getAbstractPath())); //noexcept - if (!abfR->emptyBaseFolderPath()) - asyncDirChecks.push_back(ABF::getAsyncCheckFolderExists(abfR->getAbstractPath())); //noexcept + if (!AFS::isNullPath(folderPathL)) + folderPathsToCheck.push_back(folderPathL); //noexcept + if (!AFS::isNullPath(folderPathR)) + folderPathsToCheck.push_back(folderPathR); //noexcept }; - addDirCheck(currMainCfg.firstPair); - std::for_each(currMainCfg.additionalPairs.begin(), currMainCfg.additionalPairs.end(), addDirCheck); + addFolderCheck(currMainCfg.firstPair); + std::for_each(currMainCfg.additionalPairs.begin(), currMainCfg.additionalPairs.end(), addFolderCheck); //------------------------------------------------------------------------------------------ if (havePartialPair != haveFullPair) //either all pairs full or all half-filled -> validity check! { //check existence of all directories in parallel! GetFirstResult<FalseType> firstMissingDir; - for (const std::function<bool()>& dirExists : asyncDirChecks) - firstMissingDir.addJob([dirExists]() -> std::unique_ptr<FalseType> + for (const AbstractPath& folderPath : folderPathsToCheck) + firstMissingDir.addJob([folderPath]() -> Opt<FalseType> { - try - { - if (dirExists()) //throw FileError - return nullptr; - } - catch (FileError&) {} - return std::make_unique<FalseType>(); + if (!AFS::folderExists(folderPath)) + return FalseType(); + return NoValue(); }); const bool startComparisonNow = !firstMissingDir.timedWait(std::chrono::milliseconds(500)) || //= no result yet => start comparison anyway! @@ -918,18 +920,40 @@ void MainDialog::setGlobalCfgOnInit(const xmlAccess::XmlGlobalSettings& globalSe dialogAreaVisible = std::max(dialogAreaVisible, intersection.GetWidth() * intersection.GetHeight()); } - if (dialogAreaVisible > 0.1 * dialogAreaTotal) //at least 10% of the dialog should be visible! - SetSize(wxRect(globalSettings.gui.dlgPos, globalSettings.gui.dlgSize)); + //wxGTK's wxWindow::SetSize seems unreliable and behaves like a wxWindow::SetClientSize + //=> use wxWindow::SetClientSize instead (for the record: no such issue on Windows/OS X) + SetClientSize(globalSettings.gui.dlgSize); + + if (dialogAreaVisible > 0.1 * dialogAreaTotal //at least 10% of the dialog should be visible! +#ifdef ZEN_MAC + && globalSettings.gui.dlgPos.y > 0 //unlike Windows/Ubuntu, OS X does not correct invalid y-positions + //worse: OS X seems to treat them as client positions and sets the dialog so that the title bar is unreachable! +#endif + ) + SetPosition(globalSettings.gui.dlgPos); else - { - SetSize(wxRect(globalSettings.gui.dlgSize)); Center(); - } } else Center(); - Maximize(globalSettings.gui.isMaximized); +#ifdef ZEN_MAC + //OS X 10.10 and later: http://osxdaily.com/2014/10/28/maximize-zoom-windows-os-x-mac/ + //enlarging a window will set full screen by default, not maximize (latter is still available by holding ALT) + //=> no real need to support both maximize and full screen functions, we just follow the OS X 10.10 design: + const bool fullScreenApiSupported = EnableFullScreenView(true); //http://stackoverflow.com/questions/26500481/os-x-fullscreen-in-wxwidgets-3-0 + assert(fullScreenApiSupported); //available since 10.7 + if (fullScreenApiSupported) + { + if (globalSettings.gui.isMaximized) + ShowFullScreen(true); //once EnableFullScreenView() is set, this internally uses the new full screen API + } + else +#endif + if (globalSettings.gui.isMaximized) + Maximize(true); + + //Maximize(globalSettings.gui.isMaximized); //set column attributes m_gridMainL ->setColumnConfig(gridview::convertConfig(globalSettings.gui.columnAttribLeft)); @@ -1045,13 +1069,31 @@ xmlAccess::XmlGlobalSettings MainDialog::getGlobalCfgBeforeExit() Iconize(false); globalSettings.gui.isMaximized = IsMaximized(); //evaluate AFTER uniconizing! - - if (IsMaximized()) + if (globalSettings.gui.isMaximized) Maximize(false); - globalSettings.gui.dlgSize = GetSize(); +#ifdef ZEN_MAC + if (IsFullScreen()) + { + globalSettings.gui.isMaximized = true; + ShowFullScreen(false); + } +#endif + + globalSettings.gui.dlgSize = GetClientSize(); globalSettings.gui.dlgPos = GetPosition(); +#if defined ZEN_LINUX || defined ZEN_MAC //sometimes retrieving position and size afer un-maximize does not work: + //wxGTK: returns full screen size and strange position (65/-4) + //OS X 10.9 (but NO issue on 10.11!) returns full screen size and strange position (0/-22) + if (globalSettings.gui.isMaximized) + if (globalSettings.gui.dlgPos.y < 0) + { + globalSettings.gui.dlgSize = wxSize(); + globalSettings.gui.dlgPos = wxPoint(); + } +#endif + return globalSettings; } @@ -1163,11 +1205,11 @@ std::vector<FileSystemObject*> MainDialog::getTreeSelection() const if (auto root = dynamic_cast<const TreeView::RootNode*>(node.get())) { //selecting root means "select everything", *ignoring* current view filter! - BaseDirPair& baseDir = root->baseDirObj_; + BaseFolderPair& baseDir = root->baseFolder_; std::vector<FileSystemObject*> dirsFilesAndLinks; - for (FileSystemObject& fsObj : baseDir.refSubDirs()) //no need to explicitly add child elements! + for (FileSystemObject& fsObj : baseDir.refSubFolders()) //no need to explicitly add child elements! dirsFilesAndLinks.push_back(&fsObj); for (FileSystemObject& fsObj : baseDir.refSubFiles()) dirsFilesAndLinks.push_back(&fsObj); @@ -1177,7 +1219,7 @@ std::vector<FileSystemObject*> MainDialog::getTreeSelection() const append(output, dirsFilesAndLinks); } else if (auto dir = dynamic_cast<const TreeView::DirNode*>(node.get())) - output.push_back(&(dir->dirObj_)); + output.push_back(&(dir->folder_)); else if (auto file = dynamic_cast<const TreeView::FilesNode*>(node.get())) append(output, file->filesAndLinks_); else assert(false); @@ -1282,20 +1324,20 @@ void MainDialog::deleteSelectedFiles(const std::vector<FileSystemObject*>& selec namespace { template <SelectedSide side> -AbstractPathRef getExistingParentFolder(const FileSystemObject& fsObj) +AbstractPath getExistingParentFolder(const FileSystemObject& fsObj) { - const DirPair* dirObj = dynamic_cast<const DirPair*>(&fsObj); - if (!dirObj) - dirObj = dynamic_cast<const DirPair*>(&fsObj.parent()); + auto folder = dynamic_cast<const FolderPair*>(&fsObj); + if (!folder) + folder = dynamic_cast<const FolderPair*>(&fsObj.parent()); - while (dirObj) + while (folder) { - if (!dirObj->isEmpty<side>()) - return dirObj->getAbstractPath<side>(); + if (!folder->isEmpty<side>()) + return folder->getAbstractPath<side>(); - dirObj = dynamic_cast<const DirPair*>(&dirObj->parent()); + folder = dynamic_cast<const FolderPair*>(&folder->parent()); } - return fsObj.getABF<side>().getAbstractPath(); + return fsObj.base().getAbstractPath<side>(); } } @@ -1329,15 +1371,12 @@ void MainDialog::openExternalApplication(const wxString& commandline, const std: (leftSide && selectionTmp[0]->isEmpty<LEFT_SIDE >()) || (!leftSide && selectionTmp[0]->isEmpty<RIGHT_SIDE>())) { - auto abfL = createAbstractBaseFolder(firstFolderPair->getValues().folderPathPhraseLeft_); //keep AbstractPathRef valid! - auto abfR = createAbstractBaseFolder(firstFolderPair->getValues().folderPathPhraseRight_); // - - AbstractPathRef fallbackFolderPath = [&] + const AbstractPath fallbackFolderPath = [&] { if (selectionTmp.empty()) return leftSide ? - abfL->getAbstractPath() : - abfR->getAbstractPath(); + createAbstractPath(firstFolderPair->getValues().folderPathPhraseLeft_) : + createAbstractPath(firstFolderPair->getValues().folderPathPhraseRight_); else return leftSide ? getExistingParentFolder<LEFT_SIDE >(*selectionTmp[0]) : @@ -1349,14 +1388,14 @@ void MainDialog::openExternalApplication(const wxString& commandline, const std: #ifdef ZEN_WIN #ifdef ZEN_WIN_VISTA_AND_LATER if (std::shared_ptr<const void> /*PCIDLIST_ABSOLUTE*/ fallbackFolderPidl = geMtpItemAbsolutePidl(fallbackFolderPath)) - shellExecute(fallbackFolderPidl.get(), ABF::getDisplayPath(fallbackFolderPath), EXEC_TYPE_ASYNC); //throw FileError + shellExecute(fallbackFolderPidl.get(), AFS::getDisplayPath(fallbackFolderPath), EXEC_TYPE_ASYNC); //throw FileError else #endif - shellExecute(L"\"" + toZ(ABF::getDisplayPath(fallbackFolderPath)) + L"\"", EXEC_TYPE_ASYNC); //throw FileError + shellExecute(L"\"" + toZ(AFS::getDisplayPath(fallbackFolderPath)) + L"\"", EXEC_TYPE_ASYNC); //throw FileError #elif defined ZEN_LINUX - shellExecute("xdg-open \"" + toZ(ABF::getDisplayPath(fallbackFolderPath)) + "\"", EXEC_TYPE_ASYNC); // + shellExecute("xdg-open \"" + toZ(AFS::getDisplayPath(fallbackFolderPath)) + "\"", EXEC_TYPE_ASYNC); // #elif defined ZEN_MAC - shellExecute("open \"" + toZ(ABF::getDisplayPath(fallbackFolderPath)) + "\"", EXEC_TYPE_ASYNC); // + shellExecute("open \"" + toZ(AFS::getDisplayPath(fallbackFolderPath)) + "\"", EXEC_TYPE_ASYNC); // #endif } catch (const FileError& e) { showNotificationDialog(this, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString())); } @@ -1390,11 +1429,13 @@ void MainDialog::openExternalApplication(const wxString& commandline, const std: for (const FileSystemObject* fsObj : selectionTmp) //context menu calls this function only if selection is not empty! { const Zstring& relPath = fsObj->getPairRelativePath(); - Zstring path1 = toZ(ABF::getDisplayPath(fsObj->getABF<LEFT_SIDE>().getAbstractPath(relPath))); //full path, even if item is not existing! - Zstring dir1 = toZ(ABF::getDisplayPath(fsObj->getABF<LEFT_SIDE>().getAbstractPath(beforeLast(relPath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)))); + const Zstring& relPathParent = beforeLast(relPath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE); + + Zstring path1 = toZ(AFS::getDisplayPath(AFS::appendRelPath(fsObj->base().getAbstractPath<LEFT_SIDE>(), relPath))); //full path, even if item is not existing! + Zstring dir1 = toZ(AFS::getDisplayPath(AFS::appendRelPath(fsObj->base().getAbstractPath<LEFT_SIDE>(), relPathParent))); - Zstring path2 = toZ(ABF::getDisplayPath(fsObj->getABF<RIGHT_SIDE>().getAbstractPath(relPath))); - Zstring dir2 = toZ(ABF::getDisplayPath(fsObj->getABF<RIGHT_SIDE>().getAbstractPath(beforeLast(relPath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)))); + Zstring path2 = toZ(AFS::getDisplayPath(AFS::appendRelPath(fsObj->base().getAbstractPath<RIGHT_SIDE>(), relPath))); + Zstring dir2 = toZ(AFS::getDisplayPath(AFS::appendRelPath(fsObj->base().getAbstractPath<RIGHT_SIDE>(), relPathParent))); if (!leftSide) { @@ -1414,13 +1455,13 @@ void MainDialog::openExternalApplication(const wxString& commandline, const std: #ifdef ZEN_WIN_VISTA_AND_LATER if (openFileBrowserRequested || openWithDefaultAppRequested) { - const AbstractPathRef itemPath = leftSide ? fsObj->getAbstractPath<LEFT_SIDE>() : fsObj->getAbstractPath<RIGHT_SIDE>(); + const AbstractPath itemPath = leftSide ? fsObj->getAbstractPath<LEFT_SIDE>() : fsObj->getAbstractPath<RIGHT_SIDE>(); if (std::shared_ptr<const void> /*PCIDLIST_ABSOLUTE*/ shellItemPidl = geMtpItemAbsolutePidl(itemPath)) { if (openFileBrowserRequested) showShellItemInExplorer(shellItemPidl.get()); //throw FileError else - shellExecute(shellItemPidl.get(), ABF::getDisplayPath(itemPath), EXEC_TYPE_ASYNC); //throw FileError + shellExecute(shellItemPidl.get(), AFS::getDisplayPath(itemPath), EXEC_TYPE_ASYNC); //throw FileError continue; } } @@ -1958,12 +1999,12 @@ void MainDialog::onNaviSelection(GridRangeSelectEvent& event) if (std::unique_ptr<TreeView::Node> node = treeDataView->getLine(event.rowFirst_)) { if (const TreeView::RootNode* root = dynamic_cast<const TreeView::RootNode*>(node.get())) - leadRow = gridDataView->findRowFirstChild(&(root->baseDirObj_)); + leadRow = gridDataView->findRowFirstChild(&(root->baseFolder_)); else if (const TreeView::DirNode* dir = dynamic_cast<const TreeView::DirNode*>(node.get())) { - leadRow = gridDataView->findRowDirect(&(dir->dirObj_)); + leadRow = gridDataView->findRowDirect(&(dir->folder_)); if (leadRow < 0) //directory was filtered out! still on tree view (but NOT on grid view) - leadRow = gridDataView->findRowFirstChild(&(dir->dirObj_)); + leadRow = gridDataView->findRowFirstChild(&(dir->folder_)); } else if (const TreeView::FilesNode* files = dynamic_cast<const TreeView::FilesNode*>(node.get())) { @@ -1995,9 +2036,9 @@ void MainDialog::onNaviSelection(GridRangeSelectEvent& event) if (std::unique_ptr<TreeView::Node> node = treeDataView->getLine(row)) { if (const TreeView::RootNode* root = dynamic_cast<const TreeView::RootNode*>(node.get())) - markedContainer.insert(&(root->baseDirObj_)); + markedContainer.insert(&(root->baseFolder_)); else if (const TreeView::DirNode* dir = dynamic_cast<const TreeView::DirNode*>(node.get())) - markedContainer.insert(&(dir->dirObj_)); + markedContainer.insert(&(dir->folder_)); else if (const TreeView::FilesNode* files = dynamic_cast<const TreeView::FilesNode*>(node.get())) markedFilesAndLinks.insert(files->filesAndLinks_.begin(), files->filesAndLinks_.end()); } @@ -2048,17 +2089,17 @@ void MainDialog::onNaviGridContext(GridClickEvent& event) { ContextMenu submenu; - const bool isDir = dynamic_cast<const DirPair*>(selection[0]) != nullptr; + const bool isFolder = dynamic_cast<const FolderPair*>(selection[0]) != nullptr; //by short name - Zstring labelShort = Zstring(Zstr("*")) + FILE_NAME_SEPARATOR + selection[0]->getPairShortName(); - if (isDir) + Zstring labelShort = Zstring(Zstr("*")) + FILE_NAME_SEPARATOR + selection[0]->getPairItemName(); + if (isFolder) labelShort += FILE_NAME_SEPARATOR; submenu.addItem(utfCvrtTo<wxString>(labelShort), [this, &selection, include] { filterShortname(*selection[0], include); }); //by relative path Zstring labelRel = FILE_NAME_SEPARATOR + selection[0]->getPairRelativePath(); - if (isDir) + if (isFolder) labelRel += FILE_NAME_SEPARATOR; submenu.addItem(utfCvrtTo<wxString>(labelRel), [this, &selection, include] { filterItems(selection, include); }); @@ -2171,10 +2212,10 @@ void MainDialog::onMainGridContextRim(bool leftSide) { ContextMenu submenu; - const bool isDir = dynamic_cast<const DirPair*>(selection[0]) != nullptr; + const bool isFolder = dynamic_cast<const FolderPair*>(selection[0]) != nullptr; //by extension - if (!isDir) + if (!isFolder) { const Zstring extension = getFileExtension(selection[0]->getPairRelativePath()); if (!extension.empty()) @@ -2183,14 +2224,14 @@ void MainDialog::onMainGridContextRim(bool leftSide) } //by short name - Zstring labelShort = Zstring(Zstr("*")) + FILE_NAME_SEPARATOR + selection[0]->getPairShortName(); - if (isDir) + Zstring labelShort = Zstring(Zstr("*")) + FILE_NAME_SEPARATOR + selection[0]->getPairItemName(); + if (isFolder) labelShort += FILE_NAME_SEPARATOR; submenu.addItem(utfCvrtTo<wxString>(labelShort), [this, &selection, include] { filterShortname(*selection[0], include); }); //by relative path Zstring labelRel = FILE_NAME_SEPARATOR + selection[0]->getPairRelativePath(); - if (isDir) + if (isFolder) labelRel += FILE_NAME_SEPARATOR; submenu.addItem(utfCvrtTo<wxString>(labelRel), [this, &selection, include] { filterItems(selection, include); }); @@ -2305,7 +2346,7 @@ void MainDialog::filterPhrase(const Zstring& phrase, bool include, bool addNewLi applyFilterConfig(); //user's temporary exclusions lost! else //do not fully apply filter, just exclude new items: preserve user's temporary exclusions { - std::for_each(begin(folderCmp), end(folderCmp), [&](BaseDirPair& baseDirObj) { addHardFiltering(baseDirObj, phrase); }); + std::for_each(begin(folderCmp), end(folderCmp), [&](BaseFolderPair& baseFolder) { addHardFiltering(baseFolder, phrase); }); updateGui(); } } @@ -2320,9 +2361,9 @@ void MainDialog::filterExtension(const Zstring& extension, bool include) void MainDialog::filterShortname(const FileSystemObject& fsObj, bool include) { - Zstring phrase = Zstring(Zstr("*")) + FILE_NAME_SEPARATOR + fsObj.getPairShortName(); - const bool isDir = dynamic_cast<const DirPair*>(&fsObj) != nullptr; - if (isDir) + Zstring phrase = Zstring(Zstr("*")) + FILE_NAME_SEPARATOR + fsObj.getPairItemName(); + const bool isFolder = dynamic_cast<const FolderPair*>(&fsObj) != nullptr; + if (isFolder) phrase += FILE_NAME_SEPARATOR; filterPhrase(phrase, include, true); @@ -2344,8 +2385,8 @@ void MainDialog::filterItems(const std::vector<FileSystemObject*>& selection, bo //#pragma warning(suppress: 6011) -> fsObj bound in this context! phrase += FILE_NAME_SEPARATOR + fsObj->getPairRelativePath(); - const bool isDir = dynamic_cast<const DirPair*>(fsObj) != nullptr; - if (isDir) + const bool isFolder = dynamic_cast<const FolderPair*>(fsObj) != nullptr; + if (isFolder) phrase += FILE_NAME_SEPARATOR; } filterPhrase(phrase, include, true); @@ -2583,7 +2624,7 @@ void MainDialog::addFileToCfgHistory(const std::vector<Zstring>& filepaths) for (unsigned int i = 0; i < m_listBoxHistory->GetCount(); ++i) if (auto histData = dynamic_cast<wxClientHistoryData*>(m_listBoxHistory->GetClientObject(i))) { - if (EqualFilePath()(filepath, histData->cfgFile_)) + if (equalFilePath(filepath, histData->cfgFile_)) return std::make_pair(histData, i); } else @@ -2603,7 +2644,7 @@ void MainDialog::addFileToCfgHistory(const std::vector<Zstring>& filepaths) wxString label; unsigned int newPos = 0; - if (EqualFilePath()(filepath, lastRunConfigName())) + if (equalFilePath(filepath, lastRunConfigName())) label = lastSessionLabel; else { @@ -2637,19 +2678,17 @@ void MainDialog::addFileToCfgHistory(const std::vector<Zstring>& filepaths) } -void MainDialog::removeObsoleteCfgHistoryItems(const std::vector<Zstring>& filepaths) +void MainDialog::removeObsoleteCfgHistoryItems(const std::vector<Zstring>& filePaths) { //don't use wxString: NOT thread-safe! (e.g. non-atomic ref-count) - auto getMissingFilesAsync = [filepaths]() -> std::vector<Zstring> + auto getMissingFilesAsync = [filePaths] { - //std::this_thread::sleep_for(std::chrono::seconds(5)); - //check existence of all config files in parallel! std::list<std::future<bool>> fileEx; - for (const Zstring& filepath : filepaths) - fileEx.push_back(zen::runAsync([=] { return fileExists(filepath); })); + for (const Zstring& filePath : filePaths) + fileEx.push_back(zen::runAsync([=] { return somethingExists(filePath); })); //potentially slow network access => limit maximum wait time! wait_for_all_timed(fileEx.begin(), fileEx.end(), std::chrono::milliseconds(1000)); @@ -2657,7 +2696,7 @@ void MainDialog::removeObsoleteCfgHistoryItems(const std::vector<Zstring>& filep std::vector<Zstring> missingFiles; auto itFut = fileEx.begin(); - for (auto it = filepaths.begin(); it != filepaths.end(); ++it, ++itFut) + for (auto it = filePaths.begin(); it != filePaths.end(); ++it, ++itFut) if (isReady(*itFut) && !itFut->get()) //remove only files that are confirmed to be non-existent missingFiles.push_back(*it); @@ -2668,14 +2707,14 @@ void MainDialog::removeObsoleteCfgHistoryItems(const std::vector<Zstring>& filep } -void MainDialog::removeCfgHistoryItems(const std::vector<Zstring>& filepaths) +void MainDialog::removeCfgHistoryItems(const std::vector<Zstring>& filePaths) { - std::for_each(filepaths.begin(), filepaths.end(), [&](const Zstring& filepath) + std::for_each(filePaths.begin(), filePaths.end(), [&](const Zstring& filepath) { const int histSize = m_listBoxHistory->GetCount(); for (int i = 0; i < histSize; ++i) if (auto histData = dynamic_cast<wxClientHistoryData*>(m_listBoxHistory->GetClientObject(i))) - if (EqualFilePath()(filepath, histData->cfgFile_)) + if (equalFilePath(filepath, histData->cfgFile_)) { m_listBoxHistory->Delete(i); break; @@ -2686,7 +2725,7 @@ void MainDialog::removeCfgHistoryItems(const std::vector<Zstring>& filepaths) void MainDialog::updateUnsavedCfgStatus() { - const Zstring activeCfgFilename = activeConfigFiles.size() == 1 && !EqualFilePath()(activeConfigFiles[0], lastRunConfigName()) ? activeConfigFiles[0] : Zstring(); + const Zstring activeCfgFilename = activeConfigFiles.size() == 1 && !equalFilePath(activeConfigFiles[0], lastRunConfigName()) ? activeConfigFiles[0] : Zstring(); const bool haveUnsavedCfg = lastConfigurationSaved != getConfig(); @@ -2727,7 +2766,7 @@ void MainDialog::updateUnsavedCfgStatus() void MainDialog::OnConfigSave(wxCommandEvent& event) { - const Zstring activeCfgFilename = activeConfigFiles.size() == 1 && !EqualFilePath()(activeConfigFiles[0], lastRunConfigName()) ? activeConfigFiles[0] : Zstring(); + const Zstring activeCfgFilename = activeConfigFiles.size() == 1 && !equalFilePath(activeConfigFiles[0], lastRunConfigName()) ? activeConfigFiles[0] : Zstring(); using namespace xmlAccess; @@ -2783,13 +2822,13 @@ bool MainDialog::trySaveConfig(const Zstring* guiFilename) //return true if save } else { - Zstring defaultFileName = activeConfigFiles.size() == 1 && !EqualFilePath()(activeConfigFiles[0], lastRunConfigName()) ? activeConfigFiles[0] : Zstr("SyncSettings.ffs_gui"); + Zstring defaultFileName = activeConfigFiles.size() == 1 && !equalFilePath(activeConfigFiles[0], lastRunConfigName()) ? activeConfigFiles[0] : Zstr("SyncSettings.ffs_gui"); //attention: activeConfigFiles may be an imported *.ffs_batch file! We don't want to overwrite it with a GUI config! if (pathEndsWith(defaultFileName, Zstr(".ffs_batch"))) defaultFileName = beforeLast(defaultFileName, Zstr("."), IF_MISSING_RETURN_NONE) + Zstr(".ffs_gui"); wxFileDialog filePicker(this, //put modal dialog on stack: creating this on freestore leads to memleak! - wxEmptyString, + wxString(), //OS X really needs dir/file separated like this: utfCvrtTo<wxString>(beforeLast(defaultFileName, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)), //default dir utfCvrtTo<wxString>(afterLast (defaultFileName, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL)), //default file @@ -2824,7 +2863,7 @@ bool MainDialog::trySaveBatchConfig(const Zstring* batchFileToUpdate) //essentially behave like trySaveConfig(): the collateral damage of not saving GUI-only settings "m_bpButtonViewTypeSyncAction" is negliable - const Zstring activeCfgFilename = activeConfigFiles.size() == 1 && !EqualFilePath()(activeConfigFiles[0], lastRunConfigName()) ? activeConfigFiles[0] : Zstring(); + const Zstring activeCfgFilename = activeConfigFiles.size() == 1 && !equalFilePath(activeConfigFiles[0], lastRunConfigName()) ? activeConfigFiles[0] : Zstring(); const XmlGuiConfig guiCfg = getConfig(); //prepare batch config: reuse existing batch-specific settings from file if available @@ -2878,7 +2917,7 @@ bool MainDialog::trySaveBatchConfig(const Zstring* batchFileToUpdate) defaultFileName = beforeLast(defaultFileName, Zstr("."), IF_MISSING_RETURN_NONE) + Zstr(".ffs_batch"); wxFileDialog filePicker(this, //put modal dialog on stack: creating this on freestore leads to memleak! - wxEmptyString, + wxString(), //OS X really needs dir/file separated like this: utfCvrtTo<wxString>(beforeLast(defaultFileName, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)), //default dir utfCvrtTo<wxString>(afterLast (defaultFileName, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL)), //default file @@ -2909,7 +2948,7 @@ bool MainDialog::saveOldConfig() //return false on user abort { if (lastConfigurationSaved != getConfig()) { - const Zstring activeCfgFilename = activeConfigFiles.size() == 1 && !EqualFilePath()(activeConfigFiles[0], lastRunConfigName()) ? activeConfigFiles[0] : Zstring(); + const Zstring activeCfgFilename = activeConfigFiles.size() == 1 && !equalFilePath(activeConfigFiles[0], lastRunConfigName()) ? activeConfigFiles[0] : Zstring(); //notify user about changed settings if (globalCfg.optDialogs.popupOnConfigChange) @@ -2969,15 +3008,14 @@ bool MainDialog::saveOldConfig() //return false on user abort void MainDialog::OnConfigLoad(wxCommandEvent& event) { - const Zstring activeCfgFilename = activeConfigFiles.size() == 1 && !EqualFilePath()(activeConfigFiles[0], lastRunConfigName()) ? activeConfigFiles[0] : Zstring(); + const Zstring activeCfgFilename = activeConfigFiles.size() == 1 && !equalFilePath(activeConfigFiles[0], lastRunConfigName()) ? activeConfigFiles[0] : Zstring(); wxFileDialog filePicker(this, - wxEmptyString, + wxString(), utfCvrtTo<wxString>(beforeLast(activeCfgFilename, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)), //set default dir - wxEmptyString, + wxString(), wxString(L"FreeFileSync (*.ffs_gui; *.ffs_batch)|*.ffs_gui;*.ffs_batch") + L"|" +_("All files") + L" (*.*)|*", wxFD_OPEN | wxFD_MULTIPLE); - if (filePicker.ShowModal() == wxID_OK) { wxArrayString tmp; @@ -3290,9 +3328,9 @@ void MainDialog::showConfigDialog(SyncConfigPanel panelToShow, int localPairInde std::vector<LocalPairConfig> folderPairConfig; auto addPairCfg = [&](const FolderPairEnh& fp) { - LocalPairConfig fpCfg = {}; - fpCfg.folderPairName = getShortDisplayNameForFolderPair(ABF::getDisplayPath(createAbstractBaseFolder(fp.folderPathPhraseLeft_ )->getAbstractPath()), - ABF::getDisplayPath(createAbstractBaseFolder(fp.folderPathPhraseRight_)->getAbstractPath())); + LocalPairConfig fpCfg; + fpCfg.folderPairName = getShortDisplayNameForFolderPair(AFS::getDisplayPath(createAbstractPath(fp.folderPathPhraseLeft_ )), + AFS::getDisplayPath(createAbstractPath(fp.folderPathPhraseRight_))); fpCfg.altCmpConfig = fp.altCmpConfig; fpCfg.altSyncConfig = fp.altSyncConfig; fpCfg.localFilter = fp.localFilter; @@ -3600,12 +3638,10 @@ void MainDialog::updateGlobalFilterButton() void MainDialog::OnCompare(wxCommandEvent& event) { - //PERF_START; - //wxBusyCursor dummy; -> redundant: progress already shown in progress dialog! wxWindow* oldFocus = wxWindow::FindFocus(); - ZEN_ON_SCOPE_EXIT(if (oldFocus) oldFocus->SetFocus()); //e.g. keep focus on main grid after pressing F5 + ZEN_ON_SCOPE_EXIT(if (oldFocus && oldFocus->IsShownOnScreen()) oldFocus->SetFocus()); //e.g. keep focus on config panel after pressing F5 int scrollPosX = 0; int scrollPosY = 0; @@ -3642,7 +3678,6 @@ void MainDialog::OnCompare(wxCommandEvent& event) } catch (GuiAbortProcess&) { - // if (m_buttonCompare->IsShownOnScreen()) m_buttonCompare->SetFocus(); updateGui(); //refresh grid in ANY case! (also on abort) return; } @@ -3651,8 +3686,6 @@ void MainDialog::OnCompare(wxCommandEvent& event) treeDataView->setData(folderCmp); // updateGui(); - // if (m_buttonSync->IsShownOnScreen()) m_buttonSync->SetFocus(); - m_gridMainL->clearSelection(ALLOW_GRID_EVENT); m_gridMainC->clearSelection(ALLOW_GRID_EVENT); m_gridMainR->clearSelection(ALLOW_GRID_EVENT); @@ -3668,6 +3701,9 @@ void MainDialog::OnCompare(wxCommandEvent& event) folderHistoryLeft ->addItem(toZ(m_folderPathLeft ->GetValue())); folderHistoryRight->addItem(toZ(m_folderPathRight->GetValue())); + if (oldFocus == m_buttonCompare) + oldFocus = m_buttonSync; + //prepare status information if (allElementsEqual(folderCmp)) flashStatusInformation(_("All files are in sync")); @@ -3739,12 +3775,12 @@ void MainDialog::updateStatistics() const SyncStatistics st(folderCmp); setValue(*m_staticTextData, st.getDataToProcess() == 0, filesizeToShortString(st.getDataToProcess()), *m_bitmapData, L"data"); - setIntValue(*m_staticTextCreateLeft, st.getCreate<LEFT_SIDE >(), *m_bitmapCreateLeft, L"so_create_left_small"); - setIntValue(*m_staticTextUpdateLeft, st.getUpdate<LEFT_SIDE >(), *m_bitmapUpdateLeft, L"so_update_left_small"); - setIntValue(*m_staticTextDeleteLeft, st.getDelete<LEFT_SIDE >(), *m_bitmapDeleteLeft, L"so_delete_left_small"); - setIntValue(*m_staticTextCreateRight, st.getCreate<RIGHT_SIDE>(), *m_bitmapCreateRight, L"so_create_right_small"); - setIntValue(*m_staticTextUpdateRight, st.getUpdate<RIGHT_SIDE>(), *m_bitmapUpdateRight, L"so_update_right_small"); - setIntValue(*m_staticTextDeleteRight, st.getDelete<RIGHT_SIDE>(), *m_bitmapDeleteRight, L"so_delete_right_small"); + setIntValue(*m_staticTextCreateLeft, st.createCount<LEFT_SIDE >(), *m_bitmapCreateLeft, L"so_create_left_small"); + setIntValue(*m_staticTextUpdateLeft, st.updateCount<LEFT_SIDE >(), *m_bitmapUpdateLeft, L"so_update_left_small"); + setIntValue(*m_staticTextDeleteLeft, st.deleteCount<LEFT_SIDE >(), *m_bitmapDeleteLeft, L"so_delete_left_small"); + setIntValue(*m_staticTextCreateRight, st.createCount<RIGHT_SIDE>(), *m_bitmapCreateRight, L"so_create_right_small"); + setIntValue(*m_staticTextUpdateRight, st.updateCount<RIGHT_SIDE>(), *m_bitmapUpdateRight, L"so_update_right_small"); + setIntValue(*m_staticTextDeleteRight, st.deleteCount<RIGHT_SIDE>(), *m_bitmapDeleteRight, L"so_delete_right_small"); m_panelStatistics->Layout(); m_panelStatistics->Refresh(); //fix small mess up on RTL layout @@ -3799,7 +3835,7 @@ void MainDialog::OnStartSync(wxCommandEvent& event) try { //PERF_START; - const Zstring activeCfgFilename = activeConfigFiles.size() == 1 && !EqualFilePath()(activeConfigFiles[0], lastRunConfigName()) ? activeConfigFiles[0] : Zstring(); + const Zstring activeCfgFilename = activeConfigFiles.size() == 1 && !equalFilePath(activeConfigFiles[0], lastRunConfigName()) ? activeConfigFiles[0] : Zstring(); const auto& guiCfg = getConfig(); @@ -3826,11 +3862,11 @@ void MainDialog::OnStartSync(wxCommandEvent& event) for (auto it = begin(folderCmp); it != end(folderCmp); ++it) { if (it->isExisting<LEFT_SIDE>()) //do NOT check directory existence again! - if (Opt<Zstring> nativeFolderPath = ABF::getNativeItemPath(it->getABF<LEFT_SIDE >().getAbstractPath())) //restrict directory locking to native paths until further + if (Opt<Zstring> nativeFolderPath = AFS::getNativeItemPath(it->getAbstractPath<LEFT_SIDE>())) //restrict directory locking to native paths until further dirPathsExisting.insert(*nativeFolderPath); if (it->isExisting<RIGHT_SIDE>()) - if (Opt<Zstring> nativeFolderPath = ABF::getNativeItemPath(it->getABF<RIGHT_SIDE >().getAbstractPath())) + if (Opt<Zstring> nativeFolderPath = AFS::getNativeItemPath(it->getAbstractPath<RIGHT_SIDE>())) dirPathsExisting.insert(*nativeFolderPath); } dirLocks = std::make_unique<LockHolder>(dirPathsExisting, globalCfg.optDialogs.warningDirectoryLockFailed, statusHandler); @@ -4599,8 +4635,8 @@ void MainDialog::OnMenuExportFileList(wxCommandEvent& event) { //get a filepath wxFileDialog filePicker(this, //creating this on freestore leads to memleak! - wxEmptyString, - wxEmptyString, + wxString(), + wxString(), L"FileList.csv", //default file name _("Comma-separated values") + L" (*.csv)|*.csv" + L"|" +_("All files") + L" (*.*)|*", wxFD_SAVE | wxFD_OVERWRITE_PROMPT); @@ -4633,10 +4669,10 @@ void MainDialog::OnMenuExportFileList(wxCommandEvent& event) //base folders header += fmtValue(_("Folder Pairs")) + '\n' ; std::for_each(begin(folderCmp), end(folderCmp), - [&](BaseDirPair& baseDirObj) + [&](BaseFolderPair& baseFolder) { - header += utfCvrtTo<Utf8String>(ABF::getDisplayPath(baseDirObj.getABF<LEFT_SIDE >().getAbstractPath())) + CSV_SEP; - header += utfCvrtTo<Utf8String>(ABF::getDisplayPath(baseDirObj.getABF<RIGHT_SIDE>().getAbstractPath())) + '\n'; + header += utfCvrtTo<Utf8String>(AFS::getDisplayPath(baseFolder.getAbstractPath<LEFT_SIDE >())) + CSV_SEP; + header += utfCvrtTo<Utf8String>(AFS::getDisplayPath(baseFolder.getAbstractPath<RIGHT_SIDE>())) + '\n'; }); header += '\n'; @@ -4751,7 +4787,7 @@ void MainDialog::OnMenuCheckVersionAutomatically(wxCommandEvent& event) m_menuItemCheckVersionAuto->Check(updateCheckActive(globalCfg.gui.lastUpdateCheck)); - if (runPeriodicUpdateCheckNow(globalCfg.gui.lastUpdateCheck)) + if (shouldRunPeriodicUpdateCheck(globalCfg.gui.lastUpdateCheck)) { flashStatusInformation(_("Searching for program updates...")); //synchronous update check is sufficient here: @@ -4766,7 +4802,7 @@ void MainDialog::OnRegularUpdateCheck(wxIdleEvent& event) Disconnect(wxEVT_IDLE, wxIdleEventHandler(MainDialog::OnRegularUpdateCheck), nullptr, this); if (manualProgramUpdateRequired()) - if (runPeriodicUpdateCheckNow(globalCfg.gui.lastUpdateCheck)) + if (shouldRunPeriodicUpdateCheck(globalCfg.gui.lastUpdateCheck)) { flashStatusInformation(_("Searching for program updates...")); diff --git a/FreeFileSync/Source/ui/main_dlg.h b/FreeFileSync/Source/ui/main_dlg.h index 5d1aebf7..1624e8c6 100644 --- a/FreeFileSync/Source/ui/main_dlg.h +++ b/FreeFileSync/Source/ui/main_dlg.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef MAINDIALOG_H_891048132454564 -#define MAINDIALOG_H_891048132454564 +#ifndef MAIN_DLG_H_8910481324545644545 +#define MAIN_DLG_H_8910481324545644545 #include <map> #include <list> @@ -331,4 +331,4 @@ private: bool localKeyEventsEnabled = true; }; -#endif //MAINDIALOG_H_891048132454564 +#endif //MAIN_DLG_H_8910481324545644545 diff --git a/FreeFileSync/Source/ui/on_completion_box.cpp b/FreeFileSync/Source/ui/on_completion_box.cpp index 14487e21..6d8e8dd9 100644 --- a/FreeFileSync/Source/ui/on_completion_box.cpp +++ b/FreeFileSync/Source/ui/on_completion_box.cpp @@ -107,10 +107,10 @@ void OnCompletionBox::addItemHistory() //do not add built-in commands to history for (const auto& item : defaultCommands) if (command == utfCvrtTo<Zstring>(item.first) || - ::EqualFilePath()(command, item.second)) + equalFilePath(command, item.second)) return; - erase_if(history_, [&](const Zstring& item) { return ::EqualFilePath()(command, item); }); + erase_if(history_, [&](const Zstring& item) { return equalFilePath(command, item); }); history_.insert(history_.begin(), command); @@ -228,7 +228,7 @@ void OnCompletionBox::OnKeyEvent(wxKeyEvent& event) if (0 <= pos && pos < static_cast<int>(this->GetCount()) && //what a mess...: (GetValue() != GetString(pos) || //avoid problems when a character shall be deleted instead of list item - GetValue() == wxEmptyString)) //exception: always allow removing empty entry + GetValue().empty())) //exception: always allow removing empty entry { const auto selValue = utfCvrtTo<Zstring>(GetString(pos)); diff --git a/FreeFileSync/Source/ui/on_completion_box.h b/FreeFileSync/Source/ui/on_completion_box.h index 5d969b68..4b171377 100644 --- a/FreeFileSync/Source/ui/on_completion_box.h +++ b/FreeFileSync/Source/ui/on_completion_box.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef EXEC_FINISHED_BOX_18947773210473214 -#define EXEC_FINISHED_BOX_18947773210473214 +#ifndef ON_COMPLETION_BOX_H_18947773210473214 +#define ON_COMPLETION_BOX_H_18947773210473214 #include <vector> #include <string> @@ -25,7 +25,7 @@ class OnCompletionBox : public wxComboBox public: OnCompletionBox(wxWindow* parent, wxWindowID id, - const wxString& value = wxEmptyString, + const wxString& value = {}, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, int n = 0, @@ -59,4 +59,4 @@ private: }; -#endif //EXEC_FINISHED_BOX_18947773210473214 +#endif //ON_COMPLETION_BOX_H_18947773210473214 diff --git a/FreeFileSync/Source/ui/progress_indicator.cpp b/FreeFileSync/Source/ui/progress_indicator.cpp index 044be243..2ee83ba7 100644 --- a/FreeFileSync/Source/ui/progress_indicator.cpp +++ b/FreeFileSync/Source/ui/progress_indicator.cpp @@ -425,10 +425,10 @@ public: struct LogEntryView { - time_t time; - MessageType type; + time_t time = 0; + MessageType type = TYPE_INFO; MsgString messageLine; - bool firstLine; //if LogEntry::message spans multiple rows + bool firstLine = false; //if LogEntry::message spans multiple rows }; Opt<LogEntryView> getEntry(size_t row) const @@ -437,7 +437,7 @@ public: { const Line& line = viewRef[row]; - LogEntryView output = {}; + LogEntryView output; output.time = line.logIt_->time; output.type = line.logIt_->type; output.messageLine = extractLine(line.logIt_->message, line.rowNumber_); @@ -478,20 +478,20 @@ public: private: static MsgString extractLine(const MsgString& message, size_t textRow) { - auto iter1 = message.begin(); + auto it1 = message.begin(); for (;;) { - auto iter2 = std::find_if(iter1, message.end(), [](wchar_t c) { return c == L'\n'; }); + auto it2 = std::find_if(it1, message.end(), [](wchar_t c) { return c == L'\n'; }); if (textRow == 0) - return iter1 == message.end() ? MsgString() : MsgString(&*iter1, iter2 - iter1); //must not dereference iterator pointing to "end"! + return it1 == message.end() ? MsgString() : MsgString(&*it1, it2 - it1); //must not dereference iterator pointing to "end"! - if (iter2 == message.end()) + if (it2 == message.end()) { assert(false); return MsgString(); } - iter1 = iter2 + 1; //skip newline + it1 = it2 + 1; //skip newline --textRow; } } @@ -499,6 +499,7 @@ private: struct Line { Line(ErrorLog::const_iterator logIt, size_t rowNumber) : logIt_(logIt), rowNumber_(rowNumber) {} + ErrorLog::const_iterator logIt_; //always bound! size_t rowNumber_; //LogEntry::message may span multiple rows }; @@ -527,7 +528,7 @@ public: size_t getRowCount() const override { return msgView_ ? msgView_->rowsOnView() : 0; } - wxString getValue(size_t row, ColumnType colType) const override + std::wstring getValue(size_t row, ColumnType colType) const override { if (msgView_) if (Opt<MessageView::LogEntryView> entry = msgView_->getEntry(row)) @@ -535,7 +536,7 @@ public: { case COL_TYPE_MSG_TIME: if (entry->firstLine) - return formatTime<wxString>(FORMAT_TIME, localTime(entry->time)); + return formatTime<std::wstring>(FORMAT_TIME, localTime(entry->time)); break; case COL_TYPE_MSG_CATEGORY: @@ -554,9 +555,9 @@ public: break; case COL_TYPE_MSG_TEXT: - return copyStringTo<wxString>(entry->messageLine); + return copyStringTo<std::wstring>(entry->messageLine); } - return wxEmptyString; + return std::wstring(); } void renderCell(wxDC& dc, const wxRect& rect, size_t row, ColumnType colType, bool enabled, bool selected) override @@ -567,7 +568,7 @@ public: const wxColor colorGridLine = wxColour(192, 192, 192); //light grey wxDCPenChanger dummy2(dc, wxPen(colorGridLine, 1, wxSOLID)); - const bool drawBottomLine = [&]() -> bool //don't separate multi-line messages + const bool drawBottomLine = [&] //don't separate multi-line messages { if (msgView_) if (Opt<MessageView::LogEntryView> nextEntry = msgView_->getEntry(row + 1)) @@ -653,7 +654,7 @@ public: return std::max(getResourceImage(L"msg_info_small").GetHeight(), grid.getMainWin().GetCharHeight() + 2) + 1; //+ some space + bottom border } - wxString getToolTip(size_t row, ColumnType colType) const override + std::wstring getToolTip(size_t row, ColumnType colType) const override { switch (static_cast<ColumnTypeMsg>(colType)) { @@ -664,10 +665,10 @@ public: case COL_TYPE_MSG_CATEGORY: return getValue(row, colType); } - return wxEmptyString; + return std::wstring(); } - wxString getColumnLabel(ColumnType colType) const override { return wxEmptyString; } + std::wstring getColumnLabel(ColumnType colType) const override { return std::wstring(); } private: const std::shared_ptr<MessageView> msgView_; @@ -678,8 +679,7 @@ private: class LogPanel : public LogPanelGenerated { public: - LogPanel(wxWindow* parent, const ErrorLog& log) : LogPanelGenerated(parent), - msgView(std::make_shared<MessageView>(log)), processingKeyEventHandler(false) + LogPanel(wxWindow* parent, const ErrorLog& log) : LogPanelGenerated(parent), msgView(std::make_shared<MessageView>(log)) { const int errorCount = log.getItemCount(TYPE_ERROR | TYPE_FATAL_ERROR); const int warningCount = log.getItemCount(TYPE_WARNING); @@ -906,7 +906,7 @@ private: } std::shared_ptr<MessageView> msgView; //bound! - bool processingKeyEventHandler; + bool processingKeyEventHandler = false; }; //######################################################################################## @@ -1050,7 +1050,7 @@ struct LabelFormatterBytes : public LabelFormatter return e * numeric::nearMatch(a, std::begin(steps), std::end(steps)); } - wxString formatText(double value, double optimalBlockSize) const override { return filesizeToShortString(static_cast<std::int64_t>(value)); }; + wxString formatText(double value, double optimalBlockSize) const override { return filesizeToShortString(static_cast<std::int64_t>(value)); } }; @@ -1069,7 +1069,7 @@ struct LabelFormatterItemCount : public LabelFormatter wxString formatText(double value, double optimalBlockSize) const override { return toGuiString(numeric::round(value)); //not enough room for a "%x items" representation - }; + } }; @@ -1106,7 +1106,7 @@ struct LabelFormatterTimeElapsed : public LabelFormatter } private: - bool drawLabel_; + const bool drawLabel_; }; } diff --git a/FreeFileSync/Source/ui/progress_indicator.h b/FreeFileSync/Source/ui/progress_indicator.h index 92741ccd..dfc686c0 100644 --- a/FreeFileSync/Source/ui/progress_indicator.h +++ b/FreeFileSync/Source/ui/progress_indicator.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef PROGRESS_INDICATOR_H_INCLUDED_8037493452348 -#define PROGRESS_INDICATOR_H_INCLUDED_8037493452348 +#ifndef PROGRESS_INDICATOR_H_8037493452348 +#define PROGRESS_INDICATOR_H_8037493452348 #include <functional> #include <zen/error_log.h> @@ -88,4 +88,4 @@ private: }; -#endif //PROGRESS_INDICATOR_H_INCLUDED_8037493452348 +#endif //PROGRESS_INDICATOR_H_8037493452348 diff --git a/FreeFileSync/Source/ui/search.cpp b/FreeFileSync/Source/ui/search.cpp index 66244c80..d7adb70e 100644 --- a/FreeFileSync/Source/ui/search.cpp +++ b/FreeFileSync/Source/ui/search.cpp @@ -5,39 +5,35 @@ // ************************************************************************** #include "search.h" -#include <zen/string_tools.h> - +#include <zen/zstring.h> +#include <zen/perf.h> using namespace zen; + namespace { template <bool respectCase> -class ContainsMatch +class MatchFound { public: - ContainsMatch(const wxString& textToFind) : textToFind_(textToFind) {} - bool operator()(const wxString& phrase) const { return contains(phrase, textToFind_); } + MatchFound(const std::wstring& textToFind) : textToFind_(textToFind) {} + bool operator()(const std::wstring& phrase) const { return contains(phrase, textToFind_); } private: - const wxString textToFind_; + const std::wstring textToFind_; }; template <> -class ContainsMatch<false> +class MatchFound<false> { public: - ContainsMatch(const wxString& textToFind) : textToFind_(textToFind.Upper()) {} - bool operator()(wxString&& phrase) const - { - //wxWidgets::MakeUpper() is inefficient! But performance is not THAT important for this high-level search functionality - phrase.MakeUpper(); - return contains(phrase, textToFind_); - } + MatchFound(const std::wstring& textToFind) : textToFind_(makeUpperCopy(textToFind)) {} + bool operator()(std::wstring&& phrase) const { return contains(makeUpperCopy(phrase), textToFind_); } private: - const wxString textToFind_; + const std::wstring textToFind_; }; //########################################################################################### @@ -54,11 +50,11 @@ ptrdiff_t findRow(const Grid& grid, //return -1 if no matching row found erase_if(colAttr, [](const Grid::ColumnAttribute& ca) { return !ca.visible_; }); if (!colAttr.empty()) { - const ContainsMatch<respectCase> containsMatch(searchString); + const MatchFound<respectCase> matchFound(copyStringTo<std::wstring>(searchString)); for (size_t row = rowFirst; row < rowLast; ++row) for (auto iterCol = colAttr.begin(); iterCol != colAttr.end(); ++iterCol) - if (containsMatch(prov->getValue(row, iterCol->type_))) + if (matchFound(prov->getValue(row, iterCol->type_))) return row; } } @@ -69,6 +65,8 @@ ptrdiff_t findRow(const Grid& grid, //return -1 if no matching row found std::pair<const Grid*, ptrdiff_t> zen::findGridMatch(const Grid& grid1, const Grid& grid2, const wxString& searchString, bool respectCase) { + //PERF_START + const size_t rowCountL = grid1.getRowCount(); const size_t rowCountR = grid2.getRowCount(); @@ -96,4 +94,4 @@ std::pair<const Grid*, ptrdiff_t> zen::findGridMatch(const Grid& grid1, const Gr finishSearch(grid1, 0, cursorRowL + 1); } return result; -}
\ No newline at end of file +} diff --git a/FreeFileSync/Source/ui/small_dlgs.cpp b/FreeFileSync/Source/ui/small_dlgs.cpp index 4023d3ba..3517e3db 100644 --- a/FreeFileSync/Source/ui/small_dlgs.cpp +++ b/FreeFileSync/Source/ui/small_dlgs.cpp @@ -182,7 +182,7 @@ SftpSetupDlg::SftpSetupDlg(wxWindow* parent, Zstring& folderPathPhrase) : SftpSe m_checkBoxShowPassword->SetValue(false); m_textCtrlPasswordVisible->Hide(); - if (acceptsFolderPathPhraseSftp(folderPathPhrase)) //noexcept + if (acceptsItemPathPhraseSftp(folderPathPhrase)) //noexcept { const auto res = getResolvedSftpPath(folderPathPhrase); //noexcept const SftpLoginInfo login = res.first; @@ -223,7 +223,7 @@ void SftpSetupDlg::OnToggleShowPassword(wxCommandEvent& event) std::pair<SftpLoginInfo, Zstring /*host-relative path*/> SftpSetupDlg::getSftpLogin() const { - SftpLoginInfo login = {}; + SftpLoginInfo login; login.server = utfCvrtTo<Zstring>(m_textCtrlServer ->GetValue()); login.port = stringTo<int> (m_textCtrlPort ->GetValue()); //0 if empty login.username = utfCvrtTo<Zstring>(m_textCtrlUserName->GetValue()); @@ -586,12 +586,12 @@ SyncConfirmationDlg::SyncConfirmationDlg(wxWindow* parent, }; setValue(*m_staticTextData, st.getDataToProcess() == 0, filesizeToShortString(st.getDataToProcess()), *m_bitmapData, L"data"); - setIntValue(*m_staticTextCreateLeft, st.getCreate<LEFT_SIDE >(), *m_bitmapCreateLeft, L"so_create_left_small"); - setIntValue(*m_staticTextUpdateLeft, st.getUpdate<LEFT_SIDE >(), *m_bitmapUpdateLeft, L"so_update_left_small"); - setIntValue(*m_staticTextDeleteLeft, st.getDelete<LEFT_SIDE >(), *m_bitmapDeleteLeft, L"so_delete_left_small"); - setIntValue(*m_staticTextCreateRight, st.getCreate<RIGHT_SIDE>(), *m_bitmapCreateRight, L"so_create_right_small"); - setIntValue(*m_staticTextUpdateRight, st.getUpdate<RIGHT_SIDE>(), *m_bitmapUpdateRight, L"so_update_right_small"); - setIntValue(*m_staticTextDeleteRight, st.getDelete<RIGHT_SIDE>(), *m_bitmapDeleteRight, L"so_delete_right_small"); + setIntValue(*m_staticTextCreateLeft, st.createCount<LEFT_SIDE >(), *m_bitmapCreateLeft, L"so_create_left_small"); + setIntValue(*m_staticTextUpdateLeft, st.updateCount<LEFT_SIDE >(), *m_bitmapUpdateLeft, L"so_update_left_small"); + setIntValue(*m_staticTextDeleteLeft, st.deleteCount<LEFT_SIDE >(), *m_bitmapDeleteLeft, L"so_delete_left_small"); + setIntValue(*m_staticTextCreateRight, st.createCount<RIGHT_SIDE>(), *m_bitmapCreateRight, L"so_create_right_small"); + setIntValue(*m_staticTextUpdateRight, st.updateCount<RIGHT_SIDE>(), *m_bitmapUpdateRight, L"so_update_right_small"); + setIntValue(*m_staticTextDeleteRight, st.deleteCount<RIGHT_SIDE>(), *m_bitmapDeleteRight, L"so_delete_right_small"); m_panelStatistics->Layout(); @@ -792,7 +792,7 @@ void OptionsDlg::OnDefault(wxCommandEvent& event) void OptionsDlg::setExtApp(const xmlAccess::ExternalApps& extApp) { auto extAppTmp = extApp; - erase_if(extAppTmp, [](decltype(extAppTmp[0])& entry) { return entry.first.empty() && entry.second.empty(); }); + erase_if(extAppTmp, [](auto& entry) { return entry.first.empty() && entry.second.empty(); }); extAppTmp.resize(extAppTmp.size() + 1); //append empty row to facilitate insertions diff --git a/FreeFileSync/Source/ui/small_dlgs.h b/FreeFileSync/Source/ui/small_dlgs.h index d6db883f..9d95ab69 100644 --- a/FreeFileSync/Source/ui/small_dlgs.h +++ b/FreeFileSync/Source/ui/small_dlgs.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef SMALLDIALOGS_H_8321790875018750245 -#define SMALLDIALOGS_H_8321790875018750245 +#ifndef SMALL_DLGS_H_8321790875018750245 +#define SMALL_DLGS_H_8321790875018750245 #include <wx/window.h> #include "../lib/process_xml.h" @@ -59,4 +59,4 @@ ReturnSmallDlg::ButtonPressed showSelectTimespanDlg(wxWindow* parent, std::int64 #endif } -#endif //SMALLDIALOGS_H_8321790875018750245 +#endif //SMALL_DLGS_H_8321790875018750245 diff --git a/FreeFileSync/Source/ui/sorting.h b/FreeFileSync/Source/ui/sorting.h index 18c7cab2..27dd605e 100644 --- a/FreeFileSync/Source/ui/sorting.h +++ b/FreeFileSync/Source/ui/sorting.h @@ -17,9 +17,9 @@ namespace { struct CompileTimeReminder : public FSObjectVisitor { - void visit(const FilePair& fileObj) override {} - void visit(const SymlinkPair& linkObj) override {} - void visit(const DirPair& dirObj ) override {} + void visit(const FilePair& file ) override {} + void visit(const SymlinkPair& symlink) override {} + void visit(const FolderPair& folder ) override {} } checkDymanicCasts; //just a compile-time reminder to manually check dynamic casts in this file when needed } @@ -27,7 +27,7 @@ struct CompileTimeReminder : public FSObjectVisitor inline bool isDirectoryPair(const FileSystemObject& fsObj) { - return dynamic_cast<const DirPair*>(&fsObj) != nullptr; + return dynamic_cast<const FolderPair*>(&fsObj) != nullptr; } @@ -65,8 +65,8 @@ bool lessFullPath(const FileSystemObject& a, const FileSystemObject& b) else if (b.isEmpty<side>()) return true; - return makeSortDirection(LessFilePath(), Int2Type<ascending>())(ABF::getDisplayPath(a.getAbstractPath<side>()), - ABF::getDisplayPath(b.getAbstractPath<side>())); + return makeSortDirection(LessFilePath(), Int2Type<ascending>())(AFS::getDisplayPath(a.getAbstractPath<side>()), + AFS::getDisplayPath(b.getAbstractPath<side>())); } @@ -95,7 +95,7 @@ bool lessRelativeFolder(const FileSystemObject& a, const FileSystemObject& b) else if (isDirectoryA) return true; - return LessFilePath()(a.getPairShortName(), b.getPairShortName()); + return LessFilePath()(a.getPairItemName(), b.getPairItemName()); } @@ -114,17 +114,17 @@ bool lessFilesize(const FileSystemObject& a, const FileSystemObject& b) else if (isDirectoryPair(b)) return true; - const FilePair* fileObjA = dynamic_cast<const FilePair*>(&a); - const FilePair* fileObjB = dynamic_cast<const FilePair*>(&b); + const FilePair* fileA = dynamic_cast<const FilePair*>(&a); + const FilePair* fileB = dynamic_cast<const FilePair*>(&b); //then symlinks - if (!fileObjA) + if (!fileA) return false; - else if (!fileObjB) + else if (!fileB) return true; //return list beginning with largest files first - return makeSortDirection(std::less<std::uint64_t>(), Int2Type<ascending>())(fileObjA->getFileSize<side>(), fileObjB->getFileSize<side>()); + return makeSortDirection(std::less<>(), Int2Type<ascending>())(fileA->getFileSize<side>(), fileB->getFileSize<side>()); } @@ -136,22 +136,22 @@ bool lessFiletime(const FileSystemObject& a, const FileSystemObject& b) else if (b.isEmpty<side>()) return true; //empty rows always last - const FilePair* fileObjA = dynamic_cast<const FilePair*>(&a); - const FilePair* fileObjB = dynamic_cast<const FilePair*>(&b); + const FilePair* fileA = dynamic_cast<const FilePair*>(&a); + const FilePair* fileB = dynamic_cast<const FilePair*>(&b); - const SymlinkPair* linkObjA = dynamic_cast<const SymlinkPair*>(&a); - const SymlinkPair* linkObjB = dynamic_cast<const SymlinkPair*>(&b); + const SymlinkPair* symlinkA = dynamic_cast<const SymlinkPair*>(&a); + const SymlinkPair* symlinkB = dynamic_cast<const SymlinkPair*>(&b); - if (!fileObjA && !linkObjA) + if (!fileA && !symlinkA) return false; //directories last - else if (!fileObjB && !linkObjB) + else if (!fileB && !symlinkB) return true; //directories last - const std::int64_t dateA = fileObjA ? fileObjA->getLastWriteTime<side>() : linkObjA->getLastWriteTime<side>(); - const std::int64_t dateB = fileObjB ? fileObjB->getLastWriteTime<side>() : linkObjB->getLastWriteTime<side>(); + const std::int64_t dateA = fileA ? fileA->getLastWriteTime<side>() : symlinkA->getLastWriteTime<side>(); + const std::int64_t dateB = fileB ? fileB->getLastWriteTime<side>() : symlinkB->getLastWriteTime<side>(); //return list beginning with newest files first - return makeSortDirection(std::less<std::int64_t>(), Int2Type<ascending>())(dateA, dateB); + return makeSortDirection(std::less<>(), Int2Type<ascending>())(dateA, dateB); } @@ -163,16 +163,14 @@ bool lessExtension(const FileSystemObject& a, const FileSystemObject& b) else if (b.isEmpty<side>()) return true; //empty rows always last - if (dynamic_cast<const DirPair*>(&a)) + if (dynamic_cast<const FolderPair*>(&a)) return false; //directories last - else if (dynamic_cast<const DirPair*>(&b)) + else if (dynamic_cast<const FolderPair*>(&b)) return true; //directories last - auto getExtension = [&](const FileSystemObject& fsObj) -> Zstring + auto getExtension = [](const FileSystemObject& fsObj) { - const Zstring& shortName = fsObj.getItemName<side>(); - const size_t pos = shortName.rfind(Zchar('.')); - return pos == Zstring::npos ? Zstring() : Zstring(shortName.begin() + pos + 1, shortName.end()); + return afterLast(fsObj.getItemName<side>(), Zchar('.'), zen::IF_MISSING_RETURN_NONE); }; return makeSortDirection(LessFilePath(), Int2Type<ascending>())(getExtension(a), getExtension(b)); @@ -195,7 +193,7 @@ bool lessCmpResult(const FileSystemObject& a, const FileSystemObject& b) template <bool ascending> inline bool lessSyncDirection(const FileSystemObject& a, const FileSystemObject& b) { - return makeSortDirection(std::less<SyncOperation>(), Int2Type<ascending>())(a.getSyncOperation(), b.getSyncOperation()); + return makeSortDirection(std::less<>(), Int2Type<ascending>())(a.getSyncOperation(), b.getSyncOperation()); } } diff --git a/FreeFileSync/Source/ui/switch_to_gui.h b/FreeFileSync/Source/ui/switch_to_gui.h index 6684e8d9..7d55055e 100644 --- a/FreeFileSync/Source/ui/switch_to_gui.h +++ b/FreeFileSync/Source/ui/switch_to_gui.h @@ -4,12 +4,13 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef SWITCHTOGUI_H_132047815734845 -#define SWITCHTOGUI_H_132047815734845 +#ifndef SWITCH_TO_GUI_H_132047815734845 +#define SWITCH_TO_GUI_H_132047815734845 #include "../lib/process_xml.h" #include "main_dlg.h" //in "application.cpp" we have this dependency anyway! + namespace zen { //switch from FreeFileSync Batch to GUI modus: opens a new FreeFileSync GUI session asynchronously @@ -41,4 +42,4 @@ private: }; } -#endif //SWITCHTOGUI_H_132047815734845 +#endif //SWITCH_TO_GUI_H_132047815734845 diff --git a/FreeFileSync/Source/ui/sync_cfg.cpp b/FreeFileSync/Source/ui/sync_cfg.cpp index fee76629..d192401a 100644 --- a/FreeFileSync/Source/ui/sync_cfg.cpp +++ b/FreeFileSync/Source/ui/sync_cfg.cpp @@ -406,7 +406,7 @@ std::shared_ptr<const CompConfig> ConfigDialog::getCompConfig() const if (!m_checkBoxUseLocalCmpOptions->GetValue()) return nullptr; - CompConfig compCfg = {}; + CompConfig compCfg; compCfg.compareVar = localCmpVar; compCfg.handleSymlinks = !m_checkBoxSymlinksInclude->GetValue() ? SYMLINK_EXCLUDE : m_radioBtnSymlinksDirect->GetValue() ? SYMLINK_DIRECT : SYMLINK_FOLLOW; compCfg.optTimeShiftHours = m_checkBoxTimeShift->GetValue() ? m_spinCtrlTimeShift->GetValue() : 0; @@ -778,7 +778,7 @@ std::shared_ptr<const SyncConfig> ConfigDialog::getSyncConfig() const if (!m_checkBoxUseLocalSyncOptions->GetValue()) return nullptr; - SyncConfig syncCfg = {}; + SyncConfig syncCfg; syncCfg.directionCfg = directionCfg; syncCfg.handleDeletion = handleDeletion; syncCfg.versioningFolderPhrase = versioningFolder.getPath(); @@ -946,7 +946,7 @@ MiscSyncConfig ConfigDialog::getMiscSyncOptions() const { assert(selectedPairIndexToShow == -1); - MiscSyncConfig miscCfg = {}; + MiscSyncConfig miscCfg; miscCfg.handleError = onGuiError; miscCfg.onCompletionCommand = m_comboBoxOnCompletion->getValue(); miscCfg.onCompletionHistory = m_comboBoxOnCompletion->getHistory(); @@ -1097,7 +1097,7 @@ ReturnSyncConfig::ButtonPressed zen::showSyncConfigDlg(wxWindow* parent, size_t onCompletionHistoryMax) { - GlobalSyncConfig globalCfg = {}; + GlobalSyncConfig globalCfg; globalCfg.cmpConfig = globalCmpConfig; globalCfg.syncCfg = globalSyncCfg; globalCfg.filter = globalFilter; diff --git a/FreeFileSync/Source/ui/sync_cfg.h b/FreeFileSync/Source/ui/sync_cfg.h index b782772d..be777997 100644 --- a/FreeFileSync/Source/ui/sync_cfg.h +++ b/FreeFileSync/Source/ui/sync_cfg.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef SYNCCONFIG_H_INCLUDED_31289470134253425 -#define SYNCCONFIG_H_INCLUDED_31289470134253425 +#ifndef SYNC_CFG_H_31289470134253425 +#define SYNC_CFG_H_31289470134253425 #include <wx/window.h> #include "../lib/process_xml.h" @@ -40,7 +40,7 @@ struct LocalPairConfig struct MiscSyncConfig { - xmlAccess::OnGuiError handleError; + xmlAccess::OnGuiError handleError = xmlAccess::ON_GUIERROR_POPUP; Zstring onCompletionCommand; std::vector<Zstring> onCompletionHistory; }; @@ -72,4 +72,4 @@ ReturnSyncConfig::ButtonPressed showSyncConfigDlg(wxWindow* parent, size_t onCompletionHistoryMax); } -#endif //SYNCCONFIG_H_INCLUDED_31289470134253425 +#endif //SYNC_CFG_H_31289470134253425 diff --git a/FreeFileSync/Source/ui/taskbar.cpp b/FreeFileSync/Source/ui/taskbar.cpp index 73409419..ba7fa7c6 100644 --- a/FreeFileSync/Source/ui/taskbar.cpp +++ b/FreeFileSync/Source/ui/taskbar.cpp @@ -177,7 +177,7 @@ private: { osx::dockIconSetText(str); //throw SysError } - catch (const zen::SysError& e) { assert(false); } + catch (const zen::SysError&) { assert(false); } } }; diff --git a/FreeFileSync/Source/ui/taskbar.h b/FreeFileSync/Source/ui/taskbar.h index d8fd1ace..19d99a12 100644 --- a/FreeFileSync/Source/ui/taskbar.h +++ b/FreeFileSync/Source/ui/taskbar.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef TASKBARPROGRESS_H_INCLUDED -#define TASKBARPROGRESS_H_INCLUDED +#ifndef TASKBAR_H_98170845709124456 +#define TASKBAR_H_98170845709124456 #include <memory> #include <wx/frame.h> @@ -48,4 +48,4 @@ private: } -#endif // TASKBARPROGRESS_H_INCLUDED +#endif //TASKBAR_H_98170845709124456 diff --git a/FreeFileSync/Source/ui/tray_icon.h b/FreeFileSync/Source/ui/tray_icon.h index cdcad3b7..6d240300 100644 --- a/FreeFileSync/Source/ui/tray_icon.h +++ b/FreeFileSync/Source/ui/tray_icon.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef TRAYICON_H_84217830427534285 -#define TRAYICON_H_84217830427534285 +#ifndef TRAY_ICON_H_84217830427534285 +#define TRAY_ICON_H_84217830427534285 #include <functional> #include <wx/image.h> @@ -42,4 +42,4 @@ private: wxImage logo; }; -#endif //TRAYICON_H_84217830427534285 +#endif //TRAY_ICON_H_84217830427534285 diff --git a/FreeFileSync/Source/ui/tree_view.cpp b/FreeFileSync/Source/ui/tree_view.cpp index 981bbdb2..c74c836a 100644 --- a/FreeFileSync/Source/ui/tree_view.cpp +++ b/FreeFileSync/Source/ui/tree_view.cpp @@ -20,6 +20,11 @@ using namespace zen; +namespace +{ +const int WIDTH_PERCENTAGE_BAR = 60; +} + inline void TreeView::compressNode(Container& cont) //remove single-element sub-trees -> gain clarity + usability (call *after* inclusion check!!!) { @@ -40,57 +45,57 @@ void TreeView::extractVisibleSubtree(HierarchyObject& hierObj, //in TreeView::Container& cont, //out Function pred) { - auto getBytes = [](const FilePair& fileObj) //MSVC screws up miserably if we put this lambda into std::for_each + auto getBytes = [](const FilePair& file) //MSVC screws up miserably if we put this lambda into std::for_each { ////give accumulated bytes the semantics of a sync preview! - //if (fileObj.isActive()) - // switch (fileObj.getSyncDir()) + //if (file.isActive()) + // switch (file.getSyncDir()) // { // case SyncDirection::LEFT: - // return fileObj.getFileSize<RIGHT_SIDE>(); + // return file.getFileSize<RIGHT_SIDE>(); // case SyncDirection::RIGHT: - // return fileObj.getFileSize<LEFT_SIDE>(); + // return file.getFileSize<LEFT_SIDE>(); // case SyncDirection::NONE: // break; // } //prefer file-browser semantics over sync preview (=> always show useful numbers, even for SyncDirection::NONE) //discussion: https://sourceforge.net/p/freefilesync/discussion/open-discussion/thread/ba6b6a33 - return std::max(fileObj.getFileSize<LEFT_SIDE>(), fileObj.getFileSize<RIGHT_SIDE>()); + return std::max(file.getFileSize<LEFT_SIDE>(), file.getFileSize<RIGHT_SIDE>()); }; cont.firstFileId = nullptr; - for (FilePair& fileObj : hierObj.refSubFiles()) - if (pred(fileObj)) + for (FilePair& file : hierObj.refSubFiles()) + if (pred(file)) { - cont.bytesNet += getBytes(fileObj); + cont.bytesNet += getBytes(file); ++cont.itemCountNet; if (!cont.firstFileId) - cont.firstFileId = fileObj.getId(); + cont.firstFileId = file.getId(); } - for (SymlinkPair& linkObj : hierObj.refSubLinks()) - if (pred(linkObj)) + for (SymlinkPair& symlink : hierObj.refSubLinks()) + if (pred(symlink)) { ++cont.itemCountNet; if (!cont.firstFileId) - cont.firstFileId = linkObj.getId(); + cont.firstFileId = symlink.getId(); } cont.bytesGross += cont.bytesNet; cont.itemCountGross += cont.itemCountNet; - cont.subDirs.reserve(hierObj.refSubDirs().size()); //avoid expensive reallocations! + cont.subDirs.reserve(hierObj.refSubFolders().size()); //avoid expensive reallocations! - for (DirPair& subDirObj : hierObj.refSubDirs()) + for (FolderPair& folder : hierObj.refSubFolders()) { - const bool included = pred(subDirObj); + const bool included = pred(folder); cont.subDirs.emplace_back(); // auto& subDirCont = cont.subDirs.back(); - TreeView::extractVisibleSubtree(subDirObj, subDirCont, pred); + TreeView::extractVisibleSubtree(folder, subDirCont, pred); if (included) ++subDirCont.itemCountGross; @@ -101,7 +106,7 @@ void TreeView::extractVisibleSubtree(HierarchyObject& hierObj, //in cont.subDirs.pop_back(); else { - subDirCont.objId = subDirObj.getId(); + subDirCont.objId = folder.getId(); compressNode(subDirCont); } } @@ -151,8 +156,12 @@ void calcPercentage(std::vector<std::pair<std::uint64_t, int*>>& workList) std::wstring zen::getShortDisplayNameForFolderPair(const std::wstring& displayPathLeft, const std::wstring& displayPathRight) { const wchar_t sep = L'/'; - std::wstring fmtPathL = replaceCpy(displayPathLeft, L'\\', sep); //treat slash, backslash the same - std::wstring fmtPathR = replaceCpy(displayPathRight, L'\\', sep); // + std::wstring fmtPathL = displayPathLeft; + std::wstring fmtPathR = displayPathRight; +#ifdef ZEN_WIN + replace(fmtPathL, L'\\', sep); //treat slash, backslash the same + replace(fmtPathR, L'\\', sep); // +#endif if (!startsWith(fmtPathL, sep)) fmtPathL = sep + fmtPathL; if (!startsWith(fmtPathR, sep)) fmtPathR = sep + fmtPathR; @@ -183,8 +192,11 @@ std::wstring zen::getShortDisplayNameForFolderPair(const std::wstring& displayPa auto getLastComponent = [sep](const std::wstring& displayPath) -> std::wstring { +#ifdef ZEN_WIN const std::wstring fmtPath = replaceCpy(displayPath, L'\\', sep); - +#else + const std::wstring fmtPath = displayPath; +#endif auto itEnd = fmtPath.end(); if (endsWith(fmtPath, sep)) //preserve trailing separator, support "C:\" --itEnd; @@ -228,15 +240,15 @@ struct TreeView::LessShortName case TreeView::TYPE_DIRECTORY: { - const auto* dirObjL = dynamic_cast<const DirPair*>(FileSystemObject::retrieve(static_cast<const DirNodeImpl*>(lhs.node_)->objId)); - const auto* dirObjR = dynamic_cast<const DirPair*>(FileSystemObject::retrieve(static_cast<const DirNodeImpl*>(rhs.node_)->objId)); + const auto* folderL = dynamic_cast<const FolderPair*>(FileSystemObject::retrieve(static_cast<const DirNodeImpl*>(lhs.node_)->objId)); + const auto* folderR = dynamic_cast<const FolderPair*>(FileSystemObject::retrieve(static_cast<const DirNodeImpl*>(rhs.node_)->objId)); - if (!dirObjL) //might be pathologic, but it's covered + if (!folderL) //might be pathologic, but it's covered return false; - else if (!dirObjR) + else if (!folderR) return true; - return makeSortDirection(LessFilePath(), Int2Type<ascending>())(dirObjL->getPairShortName(), dirObjR->getPairShortName()); + return makeSortDirection(LessFilePath(), Int2Type<ascending>())(folderL->getPairItemName(), folderR->getPairItemName()); } case TreeView::TYPE_FILES: @@ -334,11 +346,11 @@ void TreeView::applySubView(std::vector<RootNodeImpl>&& newView) switch (tl.type_) { case TreeView::TYPE_ROOT: - return static_cast<const RootNodeImpl*>(tl.node_)->baseDirObj.get(); + return static_cast<const RootNodeImpl*>(tl.node_)->baseFolder.get(); case TreeView::TYPE_DIRECTORY: - if (auto dirObj = dynamic_cast<const DirPair*>(FileSystemObject::retrieve(static_cast<const DirNodeImpl*>(tl.node_)->objId))) - return dirObj; + if (auto folder = dynamic_cast<const FolderPair*>(FileSystemObject::retrieve(static_cast<const DirNodeImpl*>(tl.node_)->objId))) + return folder; break; case TreeView::TYPE_FILES: @@ -414,7 +426,7 @@ void TreeView::updateView(Predicate pred) std::vector<RootNodeImpl> newView; newView.reserve(folderCmp.size()); //avoid expensive reallocations! - for (const std::shared_ptr<BaseDirPair>& baseObj : folderCmp) + for (const std::shared_ptr<BaseFolderPair>& baseObj : folderCmp) { newView.emplace_back(); RootNodeImpl& root = newView.back(); @@ -426,9 +438,9 @@ void TreeView::updateView(Predicate pred) newView.pop_back(); else { - root.baseDirObj = baseObj; - root.displayName = getShortDisplayNameForFolderPair(ABF::getDisplayPath(baseObj->getABF<LEFT_SIDE >().getAbstractPath()), - ABF::getDisplayPath(baseObj->getABF<RIGHT_SIDE>().getAbstractPath())); + root.baseFolder = baseObj; + root.displayName = getShortDisplayNameForFolderPair(AFS::getDisplayPath(baseObj->getAbstractPath<LEFT_SIDE >()), + AFS::getDisplayPath(baseObj->getAbstractPath<RIGHT_SIDE>())); this->compressNode(root); //"this->" required by two-pass lookup as enforced by GCC 4.7 } @@ -661,10 +673,10 @@ void TreeView::setData(FolderComparison& newData) folderCmp = newData; //remove truly empty folder pairs as early as this: we want to distinguish single/multiple folder pair cases by looking at "folderCmp" - erase_if(folderCmp, [](const std::shared_ptr<BaseDirPair>& baseObj) + erase_if(folderCmp, [](const std::shared_ptr<BaseFolderPair>& baseObj) { - return baseObj->getABF<LEFT_SIDE >().emptyBaseFolderPath() && - baseObj->getABF<RIGHT_SIDE>().emptyBaseFolderPath(); + return AFS::isNullPath(baseObj->getAbstractPath<LEFT_SIDE >()) && + AFS::isNullPath(baseObj->getAbstractPath<RIGHT_SIDE>()); }); } @@ -681,15 +693,15 @@ std::unique_ptr<TreeView::Node> TreeView::getLine(size_t row) const case TreeView::TYPE_ROOT: { const auto& root = *static_cast<const TreeView::RootNodeImpl*>(flatTree[row].node_); - return std::make_unique<TreeView::RootNode>(percent, root.bytesGross, root.itemCountGross, getStatus(row), *root.baseDirObj, root.displayName); + return std::make_unique<TreeView::RootNode>(percent, root.bytesGross, root.itemCountGross, getStatus(row), *root.baseFolder, root.displayName); } break; case TreeView::TYPE_DIRECTORY: { const auto* dir = static_cast<const TreeView::DirNodeImpl*>(flatTree[row].node_); - if (auto dirObj = dynamic_cast<DirPair*>(FileSystemObject::retrieve(dir->objId))) - return std::make_unique<TreeView::DirNode>(percent, dir->bytesGross, dir->itemCountGross, level, getStatus(row), *dirObj); + if (auto folder = dynamic_cast<FolderPair*>(FileSystemObject::retrieve(dir->objId))) + return std::make_unique<TreeView::DirNode>(percent, dir->bytesGross, dir->itemCountGross, level, getStatus(row), *folder); } break; @@ -753,14 +765,11 @@ class GridDataNavi : private wxEvtHandler, public GridData { public: GridDataNavi(Grid& grid, const std::shared_ptr<TreeView>& treeDataView) : treeDataView_(treeDataView), - fileIcon(IconBuffer::genericFileIcon(IconBuffer::SIZE_SMALL)), - dirIcon (IconBuffer::genericDirIcon (IconBuffer::SIZE_SMALL)), rootBmp(getResourceImage(L"rootFolder").ConvertToImage().Scale(iconSizeSmall, iconSizeSmall, wxIMAGE_QUALITY_HIGH)), widthNodeIcon(iconSizeSmall), widthLevelStep(widthNodeIcon), widthNodeStatus(getResourceImage(L"nodeExpanded").GetWidth()), - grid_(grid), - showPercentBar(true) + grid_(grid) { grid.getMainWin().Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(GridDataNavi::onKeyDown), nullptr, this); grid.Connect(EVENT_GRID_MOUSE_LEFT_DOWN, GridClickEventHandler(GridDataNavi::onMouseLeft ), nullptr, this); @@ -775,7 +784,7 @@ public: private: size_t getRowCount() const override { return treeDataView_ ? treeDataView_->linesTotal() : 0; } - wxString getToolTip(size_t row, ColumnType colType) const override + std::wstring getToolTip(size_t row, ColumnType colType) const override { switch (static_cast<ColumnTypeNavi>(colType)) { @@ -788,8 +797,8 @@ private: if (std::unique_ptr<TreeView::Node> node = treeDataView_->getLine(row)) if (const TreeView::RootNode* root = dynamic_cast<const TreeView::RootNode*>(node.get())) { - const wxString& dirLeft = ABF::getDisplayPath(root->baseDirObj_.getABF<LEFT_SIDE >().getAbstractPath()); - const wxString& dirRight = ABF::getDisplayPath(root->baseDirObj_.getABF<RIGHT_SIDE>().getAbstractPath()); + const std::wstring& dirLeft = AFS::getDisplayPath(root->baseFolder_.getAbstractPath<LEFT_SIDE >()); + const std::wstring& dirRight = AFS::getDisplayPath(root->baseFolder_.getAbstractPath<RIGHT_SIDE>()); if (dirLeft.empty()) return dirRight; else if (dirRight.empty()) @@ -798,10 +807,10 @@ private: } break; } - return wxString(); + return std::wstring(); } - wxString getValue(size_t row, ColumnType colType) const override + std::wstring getValue(size_t row, ColumnType colType) const override { if (treeDataView_) { @@ -813,9 +822,9 @@ private: case COL_TYPE_NAVI_DIRECTORY: if (const TreeView::RootNode* root = dynamic_cast<const TreeView::RootNode*>(node.get())) - return utfCvrtTo<wxString>(root->displayName_); + return root->displayName_; else if (const TreeView::DirNode* dir = dynamic_cast<const TreeView::DirNode*>(node.get())) - return utfCvrtTo<wxString>(dir->dirObj_.getPairShortName()); + return utfCvrtTo<std::wstring>(dir->folder_.getPairItemName()); else if (dynamic_cast<const TreeView::FilesNode*>(node.get())) return _("Files"); break; @@ -824,7 +833,7 @@ private: return toGuiString(node->itemCount_); } } - return wxString(); + return std::wstring(); } void renderColumnLabel(Grid& tree, wxDC& dc, const wxRect& rect, ColumnType colType, bool highlighted) override @@ -882,7 +891,7 @@ private: ////clear first secion: //clearArea(dc, wxRect(rect.GetTopLeft(), wxSize( // node->level_ * widthLevelStep + GAP_SIZE + //width - // (showPercentBar ? widthPercentBar + 2 * GAP_SIZE : 0) + // + // (showPercentBar ? WIDTH_PERCENTAGE_BAR + 2 * GAP_SIZE : 0) + // // widthNodeStatus + GAP_SIZE + widthNodeIcon + GAP_SIZE, // // rect.height)), wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); @@ -929,7 +938,7 @@ private: } }(); - const wxRect areaPerc(rectTmp.x, rectTmp.y + 2, widthPercentBar, rectTmp.height - 4); + const wxRect areaPerc(rectTmp.x, rectTmp.y + 2, WIDTH_PERCENTAGE_BAR, rectTmp.height - 4); { //clear background wxDCPenChanger dummy (dc, COLOR_PERCENTAGE_BORDER); @@ -949,8 +958,8 @@ private: wxDCTextColourChanger dummy3(dc, *wxBLACK); //accessibility: always set both foreground AND background colors! dc.DrawLabel(numberTo<wxString>(node->percent_) + L"%", areaPerc, wxALIGN_CENTER); - rectTmp.x += widthPercentBar + 2 * GAP_SIZE; - rectTmp.width -= widthPercentBar + 2 * GAP_SIZE; + rectTmp.x += WIDTH_PERCENTAGE_BAR + 2 * GAP_SIZE; + rectTmp.width -= WIDTH_PERCENTAGE_BAR + 2 * GAP_SIZE; } if (rectTmp.width > 0) { @@ -991,7 +1000,7 @@ private: else if (auto dir = dynamic_cast<const TreeView::DirNode*>(node.get())) { nodeIcon = dirIcon; - isActive = dir->dirObj_.isActive(); + isActive = dir->folder_.isActive(); } else if (dynamic_cast<const TreeView::FilesNode*>(node.get())) nodeIcon = fileIcon; @@ -1041,7 +1050,7 @@ private: if (static_cast<ColumnTypeNavi>(colType) == COL_TYPE_NAVI_DIRECTORY && treeDataView_) { if (std::unique_ptr<TreeView::Node> node = treeDataView_->getLine(row)) - return node->level_ * widthLevelStep + GAP_SIZE + (showPercentBar ? widthPercentBar + 2 * GAP_SIZE : 0) + widthNodeStatus + GAP_SIZE + return node->level_ * widthLevelStep + GAP_SIZE + (showPercentBar ? WIDTH_PERCENTAGE_BAR + 2 * GAP_SIZE : 0) + widthNodeStatus + GAP_SIZE + widthNodeIcon + GAP_SIZE + dc.GetTextExtent(getValue(row, colType)).GetWidth() + GAP_SIZE; //additional gap from right else @@ -1052,7 +1061,7 @@ private: 2 * GAP_SIZE; //include gap from right! } - wxString getColumnLabel(ColumnType colType) const override + std::wstring getColumnLabel(ColumnType colType) const override { switch (static_cast<ColumnTypeNavi>(colType)) { @@ -1063,7 +1072,7 @@ private: case COL_TYPE_NAVI_ITEM_COUNT: return _("Items"); } - return wxEmptyString; + return std::wstring(); } void onMouseLeft(GridClickEvent& event) @@ -1079,7 +1088,7 @@ private: if (cellArea.width > 0 && cellArea.height > 0) { const int tolerance = 1; - const int xNodeStatusFirst = -tolerance + cellArea.x + static_cast<int>(node->level_) * widthLevelStep + GAP_SIZE + (showPercentBar ? widthPercentBar + 2 * GAP_SIZE : 0); + const int xNodeStatusFirst = -tolerance + cellArea.x + static_cast<int>(node->level_) * widthLevelStep + GAP_SIZE + (showPercentBar ? WIDTH_PERCENTAGE_BAR + 2 * GAP_SIZE : 0); const int xNodeStatusLast = (xNodeStatusFirst + tolerance) + widthNodeStatus + tolerance; // -> synchronize renderCell() <-> getBestSize() <-> onMouseLeft() @@ -1255,16 +1264,16 @@ private: } std::shared_ptr<TreeView> treeDataView_; - const wxBitmap fileIcon; - const wxBitmap dirIcon; + const wxBitmap fileIcon = IconBuffer::genericFileIcon(IconBuffer::SIZE_SMALL); + const wxBitmap dirIcon = IconBuffer::genericDirIcon (IconBuffer::SIZE_SMALL); + const wxBitmap rootBmp; - std::unique_ptr<wxBitmap> buffer; //avoid costs of recreating this temporal variable + Opt<wxBitmap> buffer; //avoid costs of recreating this temporal variable const int widthNodeIcon; const int widthLevelStep; const int widthNodeStatus; - static const int widthPercentBar = 60; Grid& grid_; - bool showPercentBar; + bool showPercentBar = true; }; } diff --git a/FreeFileSync/Source/ui/tree_view.h b/FreeFileSync/Source/ui/tree_view.h index 6754edb8..d24b37dd 100644 --- a/FreeFileSync/Source/ui/tree_view.h +++ b/FreeFileSync/Source/ui/tree_view.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef TREE_H_INCLUDED_841703190201835280256673425 -#define TREE_H_INCLUDED_841703190201835280256673425 +#ifndef TREE_VIEW_H_841703190201835280256673425 +#define TREE_VIEW_H_841703190201835280256673425 #include <functional> #include <zen/optional.h> @@ -79,16 +79,16 @@ public: struct DirNode : public Node { - DirNode(int percent, std::uint64_t bytes, int itemCount, unsigned int level, NodeStatus status, DirPair& dirObj) : Node(percent, bytes, itemCount, level, status), dirObj_(dirObj) {} - DirPair& dirObj_; + DirNode(int percent, std::uint64_t bytes, int itemCount, unsigned int level, NodeStatus status, FolderPair& folder) : Node(percent, bytes, itemCount, level, status), folder_(folder) {} + FolderPair& folder_; }; struct RootNode : public Node { - RootNode(int percent, std::uint64_t bytes, int itemCount, NodeStatus status, BaseDirPair& baseDirObj, const std::wstring& displayName) : - Node(percent, bytes, itemCount, 0, status), baseDirObj_(baseDirObj), displayName_(displayName) {} + RootNode(int percent, std::uint64_t bytes, int itemCount, NodeStatus status, BaseFolderPair& baseFolder, const std::wstring& displayName) : + Node(percent, bytes, itemCount, 0, status), baseFolder_(baseFolder), displayName_(displayName) {} - BaseDirPair& baseDirObj_; + BaseFolderPair& baseFolder_; const std::wstring displayName_; }; @@ -131,13 +131,13 @@ private: struct DirNodeImpl : public Container { DirNodeImpl() : objId(nullptr) {} - FileSystemObject::ObjectId objId; //weak pointer to DirPair + FileSystemObject::ObjectId objId; //weak pointer to FolderPair }; struct RootNodeImpl : public Container { RootNodeImpl() {} - std::shared_ptr<BaseDirPair> baseDirObj; + std::shared_ptr<BaseFolderPair> baseFolder; std::wstring displayName; }; @@ -177,7 +177,7 @@ private: /* /|\ | (update...) | */ - std::vector<std::shared_ptr<BaseDirPair>> folderCmp; //full raw data + std::vector<std::shared_ptr<BaseFolderPair>> folderCmp; //full raw data ColumnTypeNavi sortColumn; bool sortAscending; @@ -199,4 +199,4 @@ std::vector<ColumnAttributeNavi> convertConfig(const std::vector<Grid::ColumnA } } -#endif //TREE_H_INCLUDED_841703190201835280256673425 +#endif //TREE_VIEW_H_841703190201835280256673425 diff --git a/FreeFileSync/Source/ui/triple_splitter.h b/FreeFileSync/Source/ui/triple_splitter.h index 7acae675..9bc54acc 100644 --- a/FreeFileSync/Source/ui/triple_splitter.h +++ b/FreeFileSync/Source/ui/triple_splitter.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef TRIPPLE_SPLIT_HEADER_8257804292846842573942534254 -#define TRIPPLE_SPLIT_HEADER_8257804292846842573942534254 +#ifndef TRIPLE_SPLITTER_H_8257804292846842573942534254 +#define TRIPLE_SPLITTER_H_8257804292846842573942534254 #include <cassert> #include <memory> @@ -86,4 +86,4 @@ private: }; } -#endif //TRIPPLE_SPLIT_HEADER_8257804292846842573942534254 +#endif //TRIPLE_SPLITTER_H_8257804292846842573942534254 diff --git a/FreeFileSync/Source/ui/version_check.cpp b/FreeFileSync/Source/ui/version_check.cpp index 691cf896..91df1f17 100644 --- a/FreeFileSync/Source/ui/version_check.cpp +++ b/FreeFileSync/Source/ui/version_check.cpp @@ -20,22 +20,31 @@ #ifdef ZEN_WIN #include <zen/win.h> //tame wininet include #include <zen/win_ver.h> +#include <zen/com_tools.h> #include <wininet.h> -#elif defined ZEN_LINUX - #include <wx/protocol/http.h> - // #include <wx/utils.h> - #elif defined ZEN_MAC - #include <wx/protocol/http.h> #include <CoreServices/CoreServices.h> //Gestalt() #endif +#if defined ZEN_LINUX || defined ZEN_MAC + #include <wx/protocol/http.h> + #include <wx/app.h> +#endif + + using namespace zen; namespace { +#ifndef ZEN_WIN + #ifndef NDEBUG + const std::thread::id mainThreadId = std::this_thread::get_id(); + #endif +#endif + + std::wstring getIso639Language() { //respect thread-safety for WinInetAccess => don't use wxWidgets in the Windows build here!!! @@ -52,6 +61,8 @@ std::wstring getIso639Language() assert(false); return std::wstring(); #else + assert(std::this_thread::get_id() == mainThreadId); + const std::wstring localeName(wxLocale::GetLanguageCanonicalName(wxLocale::GetSystemLanguage())); if (localeName.empty()) return std::wstring(); @@ -78,6 +89,8 @@ std::wstring getIso3166Country() assert(false); return std::wstring(); #else + assert(std::this_thread::get_id() == mainThreadId); + const std::wstring localeName(wxLocale::GetLanguageCanonicalName(wxLocale::GetSystemLanguage())); if (localeName.empty()) return std::wstring(); @@ -100,6 +113,8 @@ std::wstring getUserAgentName() const auto osvMinor = getOsVersion().minor; #elif defined ZEN_LINUX + assert(std::this_thread::get_id() == mainThreadId); + const wxLinuxDistributionInfo distribInfo = wxGetLinuxDistributionInfo(); assert(contains(distribInfo.Release, L'.')); std::vector<wxString> digits = split<wxString>(distribInfo.Release, L'.'); //e.g. "15.04" @@ -144,7 +159,7 @@ std::wstring getUserAgentName() class InternetConnectionError {}; #ifdef ZEN_WIN -//WinInet: 1. uses IE proxy settings! :) 2. follows HTTP redirects by default 3. swallows https +//WinInet: 1. uses IE proxy settings! :) 2. follows HTTP redirects by default 3. swallows HTTPS std::string readBytesFromUrl(const wchar_t* url) //throw InternetConnectionError { //::InternetAttemptConnect(0) -> not working as expected: succeeds even when there is no internet connection! @@ -215,10 +230,6 @@ bool internetIsAlive() //noexcept } #else -#ifndef NDEBUG - const std::thread::id mainThreadId = std::this_thread::get_id(); -#endif - std::string readBytesFromUrl(const wxString& url, int level = 0) //throw InternetConnectionError { assert(std::this_thread::get_id() == mainThreadId); @@ -262,7 +273,7 @@ std::string readBytesFromUrl(const wxString& url, int level = 0) //throw Interne std::string buffer; int newValue = 0; while ((newValue = httpStream->GetC()) != wxEOF) - buffer.push_back(newValue); + buffer.push_back(static_cast<char>(newValue)); return buffer; } @@ -406,7 +417,7 @@ void zen::checkForUpdateNow(wxWindow* parent, std::wstring& lastOnlineVersion) } -bool zen::runPeriodicUpdateCheckNow(time_t lastUpdateCheck) +bool zen::shouldRunPeriodicUpdateCheck(time_t lastUpdateCheck) { if (updateCheckActive(lastUpdateCheck)) { @@ -430,9 +441,15 @@ struct zen::UpdateCheckResult std::shared_ptr<UpdateCheckResult> zen::retrieveOnlineVersion() { #ifdef ZEN_WIN - auto result = std::make_shared<UpdateCheckResult>(); - result->versionStatus = getOnlineVersion(result->onlineVersion); //access is thread-safe on Windows only! - return result; + try + { + ComInitializer ci; //throw SysError + + auto result = std::make_shared<UpdateCheckResult>(); + result->versionStatus = getOnlineVersion(result->onlineVersion); //access is thread-safe on Windows only! + return result; + } + catch (SysError&) { assert(false); return nullptr; } #else return nullptr; #endif diff --git a/FreeFileSync/Source/ui/version_check.h b/FreeFileSync/Source/ui/version_check.h index dbc945e8..06bb79f6 100644 --- a/FreeFileSync/Source/ui/version_check.h +++ b/FreeFileSync/Source/ui/version_check.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef VERSION_CHECK_HEADER_324872374893274983275 -#define VERSION_CHECK_HEADER_324872374893274983275 +#ifndef VERSION_CHECK_H_324872374893274983275 +#define VERSION_CHECK_H_324872374893274983275 #include <functional> #include <memory> @@ -18,17 +18,18 @@ bool updateCheckActive(time_t lastUpdateCheck); void disableUpdateCheck(time_t& lastUpdateCheck); bool haveNewerVersionOnline(const std::wstring& onlineVersion); -void checkForUpdateNow(wxWindow* parent, std::wstring& lastOnlineVersion); - //periodic update check: -bool runPeriodicUpdateCheckNow(time_t lastUpdateCheck); +bool shouldRunPeriodicUpdateCheck(time_t lastUpdateCheck); //long-runing part of the check: thread-safe => run asynchronously struct UpdateCheckResult; std::shared_ptr<UpdateCheckResult> retrieveOnlineVersion(); -//eval on gui thread: +//eval on main thread: void evalPeriodicUpdateCheck(wxWindow* parent, time_t& lastUpdateCheck, std::wstring& lastOnlineVersion, const UpdateCheckResult* result); + +//call from main thread: +void checkForUpdateNow(wxWindow* parent, std::wstring& lastOnlineVersion); } -#endif //VERSION_CHECK_HEADER_324872374893274983275 +#endif //VERSION_CHECK_H_324872374893274983275 diff --git a/FreeFileSync/Source/version/version.h b/FreeFileSync/Source/version/version.h index c0cc4452..955f886f 100644 --- a/FreeFileSync/Source/version/version.h +++ b/FreeFileSync/Source/version/version.h @@ -3,7 +3,7 @@ namespace zen { -const wchar_t ffsVersion[] = L"7.5"; //internal linkage! +const wchar_t ffsVersion[] = L"7.6"; //internal linkage! const wchar_t FFS_VERSION_SEPARATOR = L'.'; } diff --git a/wx+/app_main.h b/wx+/app_main.h index b97a25ef..73306cbc 100644 --- a/wx+/app_main.h +++ b/wx+/app_main.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef APPMAIN_H_INCLUDED -#define APPMAIN_H_INCLUDED +#ifndef APP_MAIN_H_08215601837818347575856 +#define APP_MAIN_H_08215601837818347575856 #include <wx/window.h> #include <wx/app.h> @@ -47,4 +47,4 @@ void setMainWindow(wxWindow* window) inline bool mainWindowWasSet() { return refMainWndStatus(); } } -#endif // APPMAIN_H_INCLUDED +#endif //APP_MAIN_H_08215601837818347575856 diff --git a/wx+/async_task.h b/wx+/async_task.h index b23f6948..dc699829 100644 --- a/wx+/async_task.h +++ b/wx+/async_task.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef ASYNC_JOB_839147839170432143214321 -#define ASYNC_JOB_839147839170432143214321 +#ifndef ASYNC_TASK_H_839147839170432143214321 +#define ASYNC_TASK_H_839147839170432143214321 #include <functional> #include <zen/thread.h> @@ -144,4 +144,4 @@ private: } -#endif //ASYNC_JOB_839147839170432143214321 +#endif //ASYNC_TASK_H_839147839170432143214321 diff --git a/wx+/bitmap_button.h b/wx+/bitmap_button.h index 960e4579..4aef3d36 100644 --- a/wx+/bitmap_button.h +++ b/wx+/bitmap_button.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef BUTTON_HEADER_83415718945878341563415 -#define BUTTON_HEADER_83415718945878341563415 +#ifndef BITMAP_BUTTON_H_83415718945878341563415 +#define BITMAP_BUTTON_H_83415718945878341563415 #include <wx/bmpbuttn.h> #include "image_tools.h" @@ -85,4 +85,4 @@ void setImage(wxBitmapButton& button, const wxBitmap& bmp) } } -#endif //BUTTON_HEADER_83415718945878341563415 +#endif //BITMAP_BUTTON_H_83415718945878341563415 diff --git a/wx+/choice_enum.h b/wx+/choice_enum.h index 9b1575ee..86daf213 100644 --- a/wx+/choice_enum.h +++ b/wx+/choice_enum.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef WX_CHOICE_ENUM_H_INCLUDED -#define WX_CHOICE_ENUM_H_INCLUDED +#ifndef CHOICE_ENUM_H_132413545345687 +#define CHOICE_ENUM_H_132413545345687 #include <vector> #include <wx/choice.h> @@ -39,7 +39,7 @@ namespace zen template <class Enum> struct EnumDescrList { - EnumDescrList& add(Enum value, const wxString& text, const wxString& tooltip = wxEmptyString) + EnumDescrList& add(Enum value, const wxString& text, const wxString& tooltip = {}) { descrList.emplace_back(value, std::make_pair(text, tooltip)); return *this; @@ -112,4 +112,4 @@ template <class Enum> void updateTooltipEnumVal(const EnumDescrList<Enum>& mappi } -#endif //WX_CHOICE_ENUM_H_INCLUDED +#endif //CHOICE_ENUM_H_132413545345687 diff --git a/wx+/context_menu.h b/wx+/context_menu.h index ec62852d..27c00c81 100644 --- a/wx+/context_menu.h +++ b/wx+/context_menu.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef CONTEXT_HEADER_18047302153418174632141234 -#define CONTEXT_HEADER_18047302153418174632141234 +#ifndef CONTEXT_MENU_H_18047302153418174632141234 +#define CONTEXT_MENU_H_18047302153418174632141234 #include <map> #include <vector> @@ -73,8 +73,8 @@ public: void popup(wxWindow& wnd) //show popup menu + process lambdas { //eventually all events from submenu items will be received by this menu - for (auto iter = commandList.begin(); iter != commandList.end(); ++iter) - menu->Connect(iter->first, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(ContextMenu::onSelection), new GenericCommand(iter->second) /*pass ownership*/, this); + for (const auto& item : commandList) + menu->Connect(item.first, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(ContextMenu::onSelection), new GenericCommand(item.second) /*pass ownership*/, this); wnd.PopupMenu(menu.get()); wxTheApp->ProcessPendingEvents(); //make sure lambdas are evaluated before going out of scope; @@ -99,4 +99,4 @@ private: }; } -#endif //CONTEXT_HEADER_18047302153418174632141234 +#endif //CONTEXT_MENU_H_18047302153418174632141234 @@ -4,10 +4,11 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef DC_3813704987123956832143243214 -#define DC_3813704987123956832143243214 +#ifndef DC_H_4987123956832143243214 +#define DC_H_4987123956832143243214 #include <unordered_map> +#include <zen/optional.h> #include <wx/dcbuffer.h> //for macro: wxALWAYS_NATIVE_DOUBLE_BUFFER namespace zen @@ -48,7 +49,7 @@ public: auto it = refDcToAreaMap().find(&dc); if (it != refDcToAreaMap().end()) { - oldRect = std::make_unique<wxRect>(it->second); + oldRect = it->second; wxRect tmp = r; tmp.Intersect(*oldRect); //better safe than sorry @@ -65,7 +66,7 @@ public: ~RecursiveDcClipper() { dc_.DestroyClippingRegion(); - if (oldRect.get() != nullptr) + if (oldRect) { dc_.SetClippingRegion(*oldRect); refDcToAreaMap()[&dc_] = *oldRect; @@ -78,7 +79,7 @@ private: //associate "active" clipping area with each DC static std::unordered_map<wxDC*, wxRect>& refDcToAreaMap() { static std::unordered_map<wxDC*, wxRect> clippingAreas; return clippingAreas; } - std::unique_ptr<wxRect> oldRect; + Opt<wxRect> oldRect; wxDC& dc_; }; @@ -88,17 +89,17 @@ private: #endif #if wxALWAYS_NATIVE_DOUBLE_BUFFER -struct BufferedPaintDC : public wxPaintDC { BufferedPaintDC(wxWindow& wnd, std::unique_ptr<wxBitmap>& buffer) : wxPaintDC(&wnd) {} }; +struct BufferedPaintDC : public wxPaintDC { BufferedPaintDC(wxWindow& wnd, Opt<wxBitmap>& buffer) : wxPaintDC(&wnd) {} }; #else class BufferedPaintDC : public wxMemoryDC { public: - BufferedPaintDC(wxWindow& wnd, std::unique_ptr<wxBitmap>& buffer) : buffer_(buffer), paintDc(&wnd) + BufferedPaintDC(wxWindow& wnd, Opt<wxBitmap>& buffer) : buffer_(buffer), paintDc(&wnd) { const wxSize clientSize = wnd.GetClientSize(); if (!buffer_ || clientSize != wxSize(buffer->GetWidth(), buffer->GetHeight())) - buffer = std::make_unique<wxBitmap>(clientSize.GetWidth(), clientSize.GetHeight()); + buffer = wxBitmap(clientSize.GetWidth(), clientSize.GetHeight()); SelectObject(*buffer); @@ -119,10 +120,10 @@ public: } private: - std::unique_ptr<wxBitmap>& buffer_; + Opt<wxBitmap>& buffer_; wxPaintDC paintDc; }; #endif } -#endif //DC_3813704987123956832143243214 +#endif //DC_H_4987123956832143243214 diff --git a/wx+/font_size.h b/wx+/font_size.h index c1ea47cd..2302e056 100644 --- a/wx+/font_size.h +++ b/wx+/font_size.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef FONT_SIZE_HEADER_23849632846734343234532 -#define FONT_SIZE_HEADER_23849632846734343234532 +#ifndef FONT_SIZE_H_23849632846734343234532 +#define FONT_SIZE_H_23849632846734343234532 #include <zen/basic_math.h> #include <wx/window.h> @@ -16,6 +16,7 @@ #include <vssym32.h> //TMT_COLOR #endif + namespace zen { //set portable font size in multiples of the operating system's default font size @@ -31,8 +32,6 @@ void setMainInstructionFont(wxWindow& control); //following Windows/Gnome/OS X g - - //###################### implementation ##################### inline void setRelativeFontSize(wxWindow& control, double factor) @@ -84,4 +83,4 @@ void setMainInstructionFont(wxWindow& control) }; } -#endif //FONT_SIZE_HEADER_23849632846734343234532 +#endif //FONT_SIZE_H_23849632846734343234532 diff --git a/wx+/graph.cpp b/wx+/graph.cpp index 601c4913..bf1f1567 100644 --- a/wx+/graph.cpp +++ b/wx+/graph.cpp @@ -280,9 +280,10 @@ struct GetIntersectionX const double deltaX = to.x - from.x; const double deltaY = to.y - from.y; return numeric::isNull(deltaX) ? to : CurvePoint(x_, from.y + (x_ - from.x) / deltaX * deltaY); - }; + } + private: - double x_; + const double x_; }; struct GetIntersectionY @@ -293,9 +294,10 @@ struct GetIntersectionY const double deltaX = to.x - from.x; const double deltaY = to.y - from.y; return numeric::isNull(deltaY) ? to : CurvePoint(from.x + (y_ - from.y) / deltaY * deltaX, y_); - }; + } + private: - double y_; + const double y_; }; void cutPointsOutsideX(std::vector<CurvePoint>& curvePoints, std::vector<char>& oobMarker, double minX, double maxX) diff --git a/wx+/graph.h b/wx+/graph.h index ea05709d..7b61858d 100644 --- a/wx+/graph.h +++ b/wx+/graph.h @@ -4,14 +4,15 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef WX_PLOT_HEADER_2344252459 -#define WX_PLOT_HEADER_2344252459 +#ifndef GRAPH_H_234425245936567345799 +#define GRAPH_H_234425245936567345799 #include <map> #include <vector> #include <memory> #include <wx/panel.h> #include <wx/settings.h> +#include <wx/bitmap.h> #include <zen/string_tools.h> #include <zen/optional.h> @@ -177,7 +178,7 @@ public: class CurveAttributes { public: - CurveAttributes() {} //required by GCC + CurveAttributes() {} //required by GCC CurveAttributes& setColor (const wxColour& col) { color = col; autoColor = false; return *this; } CurveAttributes& fillCurveArea(const wxColour& col) { fillColor = col; drawCurveArea = true; return *this; } CurveAttributes& setLineWidth(size_t width) { lineWidth = static_cast<int>(width); return *this; } @@ -276,7 +277,7 @@ public: PosLabelX labelposX = X_LABEL_BOTTOM; int xLabelHeight = 25; - std::shared_ptr<LabelFormatter> labelFmtX = std::make_shared<DecimalNumberFormatter>(); + std::shared_ptr<LabelFormatter> labelFmtX = std::make_shared<DecimalNumberFormatter>(); PosLabelY labelposY = Y_LABEL_LEFT; int yLabelWidth = 60; @@ -333,7 +334,7 @@ private: MainAttributes attr; //global attributes - std::unique_ptr<wxBitmap> doubleBuffer; + Opt<wxBitmap> doubleBuffer; typedef std::vector<std::pair<std::shared_ptr<CurveData>, CurveAttributes>> CurveList; CurveList curves_; @@ -341,4 +342,4 @@ private: }; } -#endif //WX_PLOT_HEADER_2344252459 +#endif //GRAPH_H_234425245936567345799 diff --git a/wx+/grid.cpp b/wx+/grid.cpp index c819763f..26186a09 100644 --- a/wx+/grid.cpp +++ b/wx+/grid.cpp @@ -126,12 +126,12 @@ namespace const wchar_t ELLIPSIS = L'\u2026'; //... template <class Function> inline -wxString getTruncatedText(const wxString& text, Function textFits) +std::wstring getTruncatedText(const std::wstring& text, Function textFits) { if (textFits(text)) return text; - //unlike Windows 7 Explorer, we truncate UTF-16 correctly: e.g. CJK-Ideogramm encodes to TWO wchar_t: utfCvrtTo<wxString>("\xf0\xa4\xbd\x9c"); + //unlike Windows 7 Explorer, we truncate UTF-16 correctly: e.g. CJK-Ideogramm encodes to TWO wchar_t: utfCvrtTo<std::wstring>("\xf0\xa4\xbd\x9c"); size_t low = 0; //number of unicode chars! size_t high = unicodeLength(text); // @@ -139,7 +139,7 @@ wxString getTruncatedText(const wxString& text, Function textFits) { const size_t middle = (low + high) / 2; - wxString candidate(strBegin(text), findUnicodePos(text, middle)); + std::wstring candidate(strBegin(text), findUnicodePos(text, middle)); candidate += ELLIPSIS; if (high - low <= 1) @@ -152,7 +152,8 @@ wxString getTruncatedText(const wxString& text, Function textFits) } } -void drawTextLabelFitting(wxDC& dc, const wxString& text, const wxRect& rect, int alignment) + +void drawTextLabelFitting(wxDC& dc, const std::wstring& text, const wxRect& rect, int alignment) { RecursiveDcClipper clip(dc, rect); //wxDC::DrawLabel doesn't care about width, WTF? @@ -169,13 +170,13 @@ void drawTextLabelFitting(wxDC& dc, const wxString& text, const wxRect& rect, in */ //truncate large texts and add ellipsis - auto textFits = [&](const wxString& phrase) { return dc.GetTextExtent(phrase).GetWidth() <= rect.GetWidth(); }; + auto textFits = [&](const std::wstring& phrase) { return dc.GetTextExtent(phrase).GetWidth() <= rect.GetWidth(); }; dc.DrawLabel(getTruncatedText(text, textFits), rect, alignment); } } -void GridData::drawCellText(wxDC& dc, const wxRect& rect, const wxString& text, bool enabled, int alignment) +void GridData::drawCellText(wxDC& dc, const wxRect& rect, const std::wstring& text, bool enabled, int alignment) { wxDCTextColourChanger dummy(dc, enabled ? dc.GetTextForeground() : wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT)); drawTextLabelFitting(dc, text, rect, alignment); @@ -221,7 +222,7 @@ void GridData::drawColumnLabelBackground(wxDC& dc, const wxRect& rect, bool high } -void GridData::drawColumnLabelText(wxDC& dc, const wxRect& rect, const wxString& text) +void GridData::drawColumnLabelText(wxDC& dc, const wxRect& rect, const std::wstring& text) { wxDCTextColourChanger dummy(dc, getColorLabelText()); //accessibility: always set both foreground AND background colors! drawTextLabelFitting(dc, text, rect, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL); @@ -283,14 +284,14 @@ public: } protected: - void setToolTip(const wxString& text) //proper fix for wxWindow + void setToolTip(const std::wstring& text) //proper fix for wxWindow { wxToolTip* tt = GetToolTip(); const wxString oldText = tt ? tt->GetTip() : wxString(); if (text != oldText) { - if (text.IsEmpty()) + if (text.empty()) SetToolTip(nullptr); //wxGTK doesn't allow wxToolTip with empty text! else { @@ -368,7 +369,7 @@ private: void onEraseBackGround(wxEraseEvent& event) {} Grid& parent_; - std::unique_ptr<wxBitmap> doubleBuffer; + Opt<wxBitmap> doubleBuffer; }; //---------------------------------------------------------------------------------------------------------------- @@ -466,7 +467,7 @@ public: } private: - static wxString formatRow(size_t row) { return toGuiString(row + 1); } //convert number to std::wstring including thousands separator + static std::wstring formatRow(size_t row) { return toGuiString(row + 1); } //convert number to std::wstring including thousands separator bool AcceptsFocus() const override { return false; } @@ -791,7 +792,7 @@ private: { if (const Opt<ColAction> action = refParent().clientPosToColumnAction(event.GetPosition())) { - highlightCol = std::make_unique<size_t>(action->col); + highlightCol = action->col; if (action->wantResize) SetCursor(wxCURSOR_SIZEWE); //set window-local only! :) @@ -800,19 +801,19 @@ private: } else { - highlightCol.reset(); + highlightCol = NoValue(); SetCursor(*wxSTANDARD_CURSOR); } } //update tooltip - const wxString toolTip = [&]() -> wxString + const std::wstring toolTip = [&] { const wxPoint absPos = refParent().CalcUnscrolledPosition(event.GetPosition()); if (const Opt<ColumnType> ct = refParent().getColumnAtPos(absPos.x)) if (auto prov = refParent().getDataProvider()) return prov->getToolTip(*ct); - return wxString(); + return std::wstring(); }(); setToolTip(toolTip); @@ -822,7 +823,7 @@ private: void onLeaveWindow(wxMouseEvent& event) override { - highlightCol.reset(); //wxEVT_LEAVE_WINDOW does not respect mouse capture! -> however highlight is drawn unconditionally during move/resize! + highlightCol = NoValue(); //wxEVT_LEAVE_WINDOW does not respect mouse capture! -> however highlight is drawn unconditionally during move/resize! Refresh(); event.Skip(); } @@ -845,7 +846,7 @@ private: std::unique_ptr<ColumnResizing> activeResizing; std::unique_ptr<ColumnMove> activeMove; - std::unique_ptr<size_t> highlightCol; //column during mouse-over + Opt<size_t> highlightCol; //column during mouse-over }; //---------------------------------------------------------------------------------------------------------------- @@ -863,7 +864,7 @@ public: ColLabelWin& colLabelWin) : SubWindow(parent), rowLabelWin_(rowLabelWin), colLabelWin_(colLabelWin) - { + { Connect(EVENT_GRID_HAS_SCROLLED, wxEventHandler(MainWin::onRequestWindowUpdate), nullptr, this); } @@ -1068,7 +1069,7 @@ private: activeSelection->evalMousePos(); //eval on both mouse movement + timer event! //change tooltip - const wxString toolTip = [&]() -> wxString + const std::wstring toolTip = [&] { const ptrdiff_t rowCount = refParent().getRowCount(); const wxPoint absPos = refParent().CalcUnscrolledPosition(event.GetPosition()); @@ -1078,7 +1079,7 @@ private: if (ct && 0 <= row && row < rowCount) if (auto prov = refParent().getDataProvider()) return prov->getToolTip(row, *ct); - return wxString(); + return std::wstring(); }(); setToolTip(toolTip); @@ -1837,14 +1838,14 @@ Opt<Grid::ColAction> Grid::clientPosToColumnAction(const wxPoint& pos) const accuWidth += absWidths[col].width_; if (std::abs(absPosX - accuWidth) < resizeTolerance) { - ColAction out = {}; + ColAction out; out.wantResize = true; out.col = col; return out; } else if (absPosX < accuWidth) { - ColAction out = {}; + ColAction out; out.wantResize = false; out.col = col; return out; @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef GENERIC_GRID_HEADER_83470213483173 -#define GENERIC_GRID_HEADER_83470213483173 +#ifndef GRID_H_834702134831734869987 +#define GRID_H_834702134831734869987 #include <memory> #include <numeric> @@ -91,27 +91,27 @@ public: virtual size_t getRowCount() const = 0; //grid area - virtual wxString getValue(size_t row, ColumnType colType) const = 0; - virtual void renderRowBackgound(wxDC& dc, const wxRect& rect, size_t row, bool enabled, bool selected); //default implementation - virtual void renderCell (wxDC& dc, const wxRect& rect, size_t row, ColumnType colType, bool enabled, bool selected); // - virtual int getBestSize (wxDC& dc, size_t row, ColumnType colType ); //must correspond to renderCell()! - virtual wxString getToolTip (size_t row, ColumnType colType) const { return wxString(); } + virtual std::wstring getValue(size_t row, ColumnType colType) const = 0; + virtual void renderRowBackgound(wxDC& dc, const wxRect& rect, size_t row, bool enabled, bool selected); //default implementation + virtual void renderCell (wxDC& dc, const wxRect& rect, size_t row, ColumnType colType, bool enabled, bool selected); // + virtual int getBestSize (wxDC& dc, size_t row, ColumnType colType ); //must correspond to renderCell()! + virtual std::wstring getToolTip (size_t row, ColumnType colType) const { return std::wstring(); } //label area - virtual wxString getColumnLabel(ColumnType colType) const = 0; + virtual std::wstring getColumnLabel(ColumnType colType) const = 0; virtual void renderColumnLabel(Grid& grid, wxDC& dc, const wxRect& rect, ColumnType colType, bool highlighted); //default implementation - virtual wxString getToolTip(ColumnType colType) const { return wxString(); } + virtual std::wstring getToolTip(ColumnType colType) const { return std::wstring(); } static const int COLUMN_GAP_LEFT; //for left-aligned text protected: //optional helper routines static wxRect drawCellBorder (wxDC& dc, const wxRect& rect); //returns inner rectangle static void drawCellBackground(wxDC& dc, const wxRect& rect, bool enabled, bool selected, const wxColor& backgroundColor); - static void drawCellText (wxDC& dc, const wxRect& rect, const wxString& text, bool enabled, int alignment = wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL); + static void drawCellText (wxDC& dc, const wxRect& rect, const std::wstring& text, bool enabled, int alignment = wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL); static wxRect drawColumnLabelBorder (wxDC& dc, const wxRect& rect); //returns inner rectangle static void drawColumnLabelBackground(wxDC& dc, const wxRect& rect, bool highlighted); - static void drawColumnLabelText (wxDC& dc, const wxRect& rect, const wxString& text); + static void drawColumnLabelText (wxDC& dc, const wxRect& rect, const std::wstring& text); }; enum GridEventPolicy @@ -300,8 +300,8 @@ private: struct ColAction { - bool wantResize; //"!wantResize" means "move" or "single click" - size_t col; + bool wantResize = false; //"!wantResize" means "move" or "single click" + size_t col = 0; }; Opt<ColAction> clientPosToColumnAction(const wxPoint& pos) const; void moveColumn(size_t colFrom, size_t colTo); @@ -341,4 +341,4 @@ private: }; } -#endif //GENERIC_GRID_HEADER_83470213483173 +#endif //GRID_H_834702134831734869987 diff --git a/wx+/image_resources.h b/wx+/image_resources.h index 8d7c9b49..30f89848 100644 --- a/wx+/image_resources.h +++ b/wx+/image_resources.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef RESOURCES_H_8740257825342532457 -#define RESOURCES_H_8740257825342532457 +#ifndef IMAGE_RESOURCES_H_8740257825342532457 +#define IMAGE_RESOURCES_H_8740257825342532457 #include <wx/bitmap.h> #include <wx/animate.h> @@ -19,4 +19,4 @@ const wxBitmap& getResourceImage (const wxString& name); const wxAnimation& getResourceAnimation(const wxString& name); } -#endif //RESOURCES_H_8740257825342532457 +#endif //IMAGE_RESOURCES_H_8740257825342532457 diff --git a/wx+/image_tools.cpp b/wx+/image_tools.cpp index 4b76fac0..52ce45e3 100644 --- a/wx+/image_tools.cpp +++ b/wx+/image_tools.cpp @@ -230,4 +230,4 @@ void zen::convertToVanillaImage(wxImage& img) { assert(!img.HasMask()); } -}
\ No newline at end of file +} diff --git a/wx+/image_tools.h b/wx+/image_tools.h index 43c1a625..5e99653c 100644 --- a/wx+/image_tools.h +++ b/wx+/image_tools.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef IMAGE_TOOLS_HEADER_45782456427634254 -#define IMAGE_TOOLS_HEADER_45782456427634254 +#ifndef IMAGE_TOOLS_H_45782456427634254 +#define IMAGE_TOOLS_H_45782456427634254 #include <numeric> #include <wx/bitmap.h> @@ -13,6 +13,7 @@ #include <wx/dcmemory.h> #include <zen/basic_math.h> + namespace zen { enum class ImageStackLayout @@ -109,8 +110,8 @@ double getAvgBrightness(const wxImage& img) //calculate average weighted by alpha channel double dividend = 0; - for (auto iter = pixBegin; iter != pixEnd; ++iter) - dividend += *iter * static_cast<double>(alphaFirst[(iter - pixBegin) / 3]); + for (auto it = pixBegin; it != pixEnd; ++it) + dividend += *it * static_cast<double>(alphaFirst[(it - pixBegin) / 3]); const double divisor = 3.0 * std::accumulate(alphaFirst, alphaFirst + pixelCount, 0.0); @@ -132,9 +133,9 @@ void brighten(wxImage& img, int level) { auto pixEnd = pixBegin + 3 * pixelCount; //RGB if (level > 0) - std::for_each(pixBegin, pixEnd, [&](unsigned char& c) { c = std::min(255, c + level); }); + std::for_each(pixBegin, pixEnd, [&](unsigned char& c) { c = static_cast<unsigned char>(std::min(255, c + level)); }); else - std::for_each(pixBegin, pixEnd, [&](unsigned char& c) { c = std::max(0, c + level); }); + std::for_each(pixBegin, pixEnd, [&](unsigned char& c) { c = static_cast<unsigned char>(std::max(0, c + level)); }); } } @@ -171,8 +172,7 @@ bool isEqual(const wxBitmap& lhs, const wxBitmap& rhs) if (!lhs.IsOk()) return true; - const int pixelCount = lhs.GetWidth() * lhs.GetHeight(); - if (pixelCount != rhs.GetWidth() * rhs.GetHeight()) + if (lhs.GetSize() != rhs.GetSize()) return false; wxImage imLhs = lhs.ConvertToImage(); @@ -181,6 +181,8 @@ bool isEqual(const wxBitmap& lhs, const wxBitmap& rhs) if (imLhs.HasAlpha() != imRhs.HasAlpha()) return false; + const int pixelCount = lhs.GetWidth() * lhs.GetHeight(); + if (!std::equal(imLhs.GetData(), imLhs.GetData() + pixelCount * 3, imRhs.GetData())) return false; @@ -254,4 +256,4 @@ wxColour hsvColor(double h, double s, double v) //h within [0, 360), s, v within */ } -#endif //IMAGE_TOOLS_HEADER_45782456427634254 +#endif //IMAGE_TOOLS_H_45782456427634254 diff --git a/wx+/no_flicker.h b/wx+/no_flicker.h index fb0585ca..cff5e654 100644 --- a/wx+/no_flicker.h +++ b/wx+/no_flicker.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef NO_FLICKER_HEADER_893421590321532 -#define NO_FLICKER_HEADER_893421590321532 +#ifndef NO_FLICKER_H_893421590321532 +#define NO_FLICKER_H_893421590321532 #include <wx/textctrl.h> #include <wx/stattext.h> @@ -40,4 +40,4 @@ void setText(wxStaticText& control, wxString newText, bool* additionalLayoutChan } } -#endif //NO_FLICKER_HEADER_893421590321532 +#endif //NO_FLICKER_H_893421590321532 diff --git a/wx+/popup_dlg.h b/wx+/popup_dlg.h index eef7ab79..99fe2de1 100644 --- a/wx+/popup_dlg.h +++ b/wx+/popup_dlg.h @@ -4,12 +4,13 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef MESSAGEPOPUP_H_820780154723456 -#define MESSAGEPOPUP_H_820780154723456 +#ifndef POPUP_DLG_H_820780154723456 +#define POPUP_DLG_H_820780154723456 #include <wx/window.h> #include <wx/string.h> + namespace zen { //parent window, optional: support correct dialog placement above parent on multiple monitor systems @@ -88,4 +89,4 @@ private: }; } -#endif //MESSAGEPOPUP_H_820780154723456 +#endif //POPUP_DLG_H_820780154723456 @@ -7,7 +7,8 @@ #ifndef RTL_H_0183487180058718273432148 #define RTL_H_0183487180058718273432148 -#include <memory> +//#include <memory> +#include <zen/optional.h> #include <wx/dcmemory.h> #include <wx/dcmirror.h> #include <wx/image.h> @@ -22,18 +23,18 @@ void drawBitmapRtlMirror(wxDC& dc, const wxBitmap& image, const wxRect& rect, int alignment, - std::unique_ptr<wxBitmap>& buffer); //mirror image if layout is RTL + fix some strange wxDC::Blit bug on RTL + Opt<wxBitmap>& buffer); //mirror image if layout is RTL + fix some strange wxDC::Blit bug on RTL void drawBitmapRtlNoMirror(wxDC& dc, //wxDC::DrawLabel does already NOT mirror by default (but does a crappy job at it, surprise) const wxBitmap& image, const wxRect& rect, int alignment, - std::unique_ptr<wxBitmap>& buffer); + Opt<wxBitmap>& buffer); void drawIconRtlNoMirror(wxDC& dc, //wxDC::DrawIcon DOES mirror by default const wxIcon& icon, const wxPoint& pt, - std::unique_ptr<wxBitmap>& buffer); + Opt<wxBitmap>& buffer); wxBitmap mirrorIfRtl(const wxBitmap& bmp); @@ -50,12 +51,12 @@ wxBitmap mirrorIfRtl(const wxBitmap& bmp); namespace { template <class DrawImageFun> -void drawRtlImpl(wxDC& dc, const wxRect& rect, std::unique_ptr<wxBitmap>& buffer, bool doMirror, DrawImageFun draw) +void drawRtlImpl(wxDC& dc, const wxRect& rect, Opt<wxBitmap>& buffer, bool doMirror, DrawImageFun draw) { if (dc.GetLayoutDirection() == wxLayout_RightToLeft) { if (!buffer || buffer->GetWidth() != rect.width || buffer->GetHeight() < rect.height) //[!] since we do a mirror, width needs to match exactly! - buffer = std::make_unique<wxBitmap>(rect.width, rect.height); + buffer = wxBitmap(rect.width, rect.height); wxMemoryDC memDc(*buffer); memDc.Blit(wxPoint(0, 0), rect.GetSize(), &dc, rect.GetTopLeft()); //blit in: background is mirrored due to memDc, dc having different layout direction! @@ -84,13 +85,13 @@ void drawRtlImpl(wxDC& dc, const wxRect& rect, std::unique_ptr<wxBitmap>& buffer inline -void drawBitmapRtlMirror(wxDC& dc, const wxBitmap& image, const wxRect& rect, int alignment, std::unique_ptr<wxBitmap>& buffer) +void drawBitmapRtlMirror(wxDC& dc, const wxBitmap& image, const wxRect& rect, int alignment, Opt<wxBitmap>& buffer) { return drawRtlImpl(dc, rect, buffer, true, [&](wxDC& dc2, const wxRect& rect2) { dc2.DrawLabel(wxString(), image, rect2, alignment); }); } inline -void drawBitmapRtlNoMirror(wxDC& dc, const wxBitmap& image, const wxRect& rect, int alignment, std::unique_ptr<wxBitmap>& buffer) +void drawBitmapRtlNoMirror(wxDC& dc, const wxBitmap& image, const wxRect& rect, int alignment, Opt<wxBitmap>& buffer) { if (dc.GetLayoutDirection() == wxLayout_RightToLeft) if ((alignment & wxALIGN_CENTER_HORIZONTAL) == 0) //we still *do* want to mirror alignment! @@ -100,7 +101,7 @@ void drawBitmapRtlNoMirror(wxDC& dc, const wxBitmap& image, const wxRect& rect, } inline -void drawIconRtlNoMirror(wxDC& dc, const wxIcon& icon, const wxPoint& pt, std::unique_ptr<wxBitmap>& buffer) +void drawIconRtlNoMirror(wxDC& dc, const wxIcon& icon, const wxPoint& pt, Opt<wxBitmap>& buffer) { wxRect rect(pt.x, pt.y, icon.GetWidth(), icon.GetHeight()); return drawRtlImpl(dc, rect, buffer, false, [&](wxDC& dc2, const wxRect& rect2) { dc2.DrawIcon(icon, rect2.GetTopLeft()); }); diff --git a/wx+/std_button_layout.h b/wx+/std_button_layout.h index f497de2c..fd0db8ce 100644 --- a/wx+/std_button_layout.h +++ b/wx+/std_button_layout.h @@ -4,13 +4,14 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef STD_BUTTON_ORDER_H_18347032147831732143214 -#define STD_BUTTON_ORDER_H_18347032147831732143214 +#ifndef STD_BUTTON_LAYOUT_H_183470321478317214 +#define STD_BUTTON_LAYOUT_H_183470321478317214 #include <algorithm> #include <wx/sizer.h> #include <wx/button.h> + namespace zen { struct StdButtons @@ -143,4 +144,4 @@ void setStandardButtonLayout(wxBoxSizer& sizer, const StdButtons& buttons) } } -#endif //STD_BUTTON_ORDER_H_18347032147831732143214 +#endif //STD_BUTTON_LAYOUT_H_183470321478317214 diff --git a/wx+/string_conv.h b/wx+/string_conv.h index 229a9825..270f6442 100644 --- a/wx+/string_conv.h +++ b/wx+/string_conv.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef STRINGCONV_H_INCLUDED -#define STRINGCONV_H_INCLUDED +#ifndef STRING_CONV_H_893217450815743 +#define STRING_CONV_H_893217450815743 #include <zen/utf.h> #include <wx/string.h> @@ -25,4 +25,4 @@ inline std::vector<Zstring> toZ(const std::vector<wxString>& strList) } } -#endif // STRINGCONV_H_INCLUDED +#endif //STRING_CONV_H_893217450815743 diff --git a/wx+/timespan.h b/wx+/timespan.h index 09469ccb..fd1c24c3 100644 --- a/wx+/timespan.h +++ b/wx+/timespan.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef WX_TIMESPAN_CTRL_HEADER_INCLUDED -#define WX_TIMESPAN_CTRL_HEADER_INCLUDED +#ifndef TIMESPAN_H_254783756533456 +#define TIMESPAN_H_254783756533456 #include <wx/textctrl.h> #include <wx/datetime.h> @@ -31,7 +31,7 @@ class TimeSpanCtrl : public wxPanel { public: TimeSpanCtrl(wxWindow* parent, wxWindowID id, - const wxString& value = wxEmptyString, + const wxString& value = {}, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = 0, @@ -42,7 +42,7 @@ public: { wxBoxSizer* bSizer27 = new wxBoxSizer( wxHORIZONTAL ); - m_textCtrl = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_CENTRE ); + m_textCtrl = new wxTextCtrl(this, wxID_ANY, wxString(), wxDefaultPosition, wxDefaultSize, wxTE_CENTRE ); bSizer27->Add(m_textCtrl, 1, wxALIGN_CENTER_VERTICAL | wxEXPAND, 5 ); m_spinBtn = new wxSpinButton(this, wxID_ANY, wxDefaultPosition, wxSize( 20, -1 ), wxSP_ARROW_KEYS ); @@ -163,4 +163,4 @@ private: } -#endif //WX_TIMESPAN_CTRL_HEADER_INCLUDED +#endif //TIMESPAN_H_254783756533456 diff --git a/wx+/toggle_button.h b/wx+/toggle_button.h index faa72f66..a440a76d 100644 --- a/wx+/toggle_button.h +++ b/wx+/toggle_button.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef TOGGLEBUTTON_H_INCLUDED -#define TOGGLEBUTTON_H_INCLUDED +#ifndef TOGGLE_BUTTON_H_8173024810574556 +#define TOGGLE_BUTTON_H_8173024810574556 #include <wx/bmpbuttn.h> #include <wx+/bitmap_button.h> @@ -20,9 +20,7 @@ public: const wxSize& size = wxDefaultSize, long style = 0, const wxValidator& validator = wxDefaultValidator, - const wxString& name = wxButtonNameStr) : - wxBitmapButton(parent, id, bitmap, pos, size, style, validator, name), - active(false) + const wxString& name = wxButtonNameStr) : wxBitmapButton(parent, id, bitmap, pos, size, style, validator, name) { SetLayoutDirection(wxLayout_LeftToRight); //avoid mirroring RTL languages like Hebrew or Arabic } @@ -35,7 +33,7 @@ public: void toggle() { setActive(!active); } private: - bool active; + bool active = false; wxBitmap activeBmp_; wxBitmap inactiveBmp_; @@ -47,12 +45,6 @@ private: - - - - - - //######################## implementation ######################## inline void ToggleButton::init(const wxBitmap& activeBmp, @@ -72,4 +64,4 @@ void ToggleButton::setActive(bool value) zen::setImage(*this, active ? activeBmp_ : inactiveBmp_); } -#endif // TOGGLEBUTTON_H_INCLUDED +#endif //TOGGLE_BUTTON_H_8173024810574556 diff --git a/wx+/tooltip.cpp b/wx+/tooltip.cpp index ea9852d3..4b51d407 100644 --- a/wx+/tooltip.cpp +++ b/wx+/tooltip.cpp @@ -21,7 +21,7 @@ class Tooltip::TooltipDialogGenerated : public wxDialog public: TooltipDialogGenerated(wxWindow* parent, wxWindowID id = wxID_ANY, - const wxString& title = wxEmptyString, + const wxString& title = {}, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = 0) : wxDialog(parent, id, title, pos, size, style) @@ -36,7 +36,7 @@ public: m_bitmapLeft = new wxStaticBitmap(this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0); bSizer158->Add(m_bitmapLeft, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5); - m_staticTextMain = new wxStaticText(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); + m_staticTextMain = new wxStaticText(this, wxID_ANY, wxString(), wxDefaultPosition, wxDefaultSize, 0); bSizer158->Add(m_staticTextMain, 0, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5); this->SetSizer(bSizer158); diff --git a/wx+/tooltip.h b/wx+/tooltip.h index d4236186..8ddba819 100644 --- a/wx+/tooltip.h +++ b/wx+/tooltip.h @@ -4,11 +4,12 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef CUSTOMTOOLTIP_H_INCLUDED -#define CUSTOMTOOLTIP_H_INCLUDED +#ifndef TOOLTIP_H_8912740832170515 +#define TOOLTIP_H_8912740832170515 #include <wx/window.h> + namespace zen { class Tooltip @@ -29,4 +30,4 @@ private: }; } -#endif // CUSTOMTOOLTIP_H_INCLUDED +#endif //TOOLTIP_H_8912740832170515 diff --git a/wx+/zlib_wrap.h b/wx+/zlib_wrap.h index 815d8361..aaedd847 100644 --- a/wx+/zlib_wrap.h +++ b/wx+/zlib_wrap.h @@ -4,11 +4,12 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef ZLIB_H_INCLUDED_428597064566 -#define ZLIB_H_INCLUDED_428597064566 +#ifndef ZLIB_WRAP_H_428597064566 +#define ZLIB_WRAP_H_428597064566 #include <zen/serialize.h> + namespace zen { class ZlibInternalError {}; @@ -110,4 +111,4 @@ BinContainer decompress(const BinContainer& stream) //throw ZlibInternalError } } -#endif //ZLIB_H_INCLUDED_428597064566 +#endif //ZLIB_WRAP_H_428597064566 diff --git a/zen/basic_math.h b/zen/basic_math.h index 14fcae9c..8b745caf 100644 --- a/zen/basic_math.h +++ b/zen/basic_math.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef BASIC_MATH_HEADER_34726398432 -#define BASIC_MATH_HEADER_34726398432 +#ifndef BASIC_MATH_H_3472639843265675 +#define BASIC_MATH_H_3472639843265675 #include <algorithm> #include <iterator> @@ -14,6 +14,7 @@ #include <functional> #include <cassert> + namespace numeric { template <class T> @@ -26,10 +27,10 @@ template <class T> int sign(T value); //returns -1/0/1 template <class T> -const T& min(const T& a, const T& b, const T& c); +T min(T a, T b, T c); template <class T> -const T& max(const T& a, const T& b, const T& c); +T max(T a, T b, T c); template <class T> void clamp(T& val, const T& minVal, const T& maxVal); //make sure minVal <= val && val <= maxVal @@ -114,14 +115,14 @@ int sign(T value) //returns -1/0/1 template <class T> inline -const T& min(const T& a, const T& b, const T& c) +T min(T a, T b, T c) //don't follow std::min's "const T&(const T&, const T&)" API { return std::min(std::min(a, b), c); } template <class T> inline -const T& max(const T& a, const T& b, const T& c) +T max(T a, T b, T c) { return std::max(std::max(a, b), c); } @@ -396,4 +397,4 @@ double norm2(InputIterator first, InputIterator last) } } -#endif //BASIC_MATH_HEADER_34726398432 +#endif //BASIC_MATH_H_3472639843265675 diff --git a/zen/build_info.h b/zen/build_info.h index 7c738847..09e1c5a7 100644 --- a/zen/build_info.h +++ b/zen/build_info.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef BUILDINFO_H_5928539285603428657 -#define BUILDINFO_H_5928539285603428657 +#ifndef BUILD_INFO_H_5928539285603428657 +#define BUILD_INFO_H_5928539285603428657 namespace zen { @@ -35,4 +35,4 @@ namespace zen #endif } -#endif //BUILDINFO_H_5928539285603428657 +#endif //BUILD_INFO_H_5928539285603428657 diff --git a/zen/deprecate.h b/zen/deprecate.h index 49d8386b..5cac14c3 100644 --- a/zen/deprecate.h +++ b/zen/deprecate.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef DEPRECATE_HEADER_2348970348 -#define DEPRECATE_HEADER_2348970348 +#ifndef DEPRECATE_H_234897087787348 +#define DEPRECATE_H_234897087787348 //compiler macros: http://predef.sourceforge.net/precomp.html #ifdef __GNUC__ @@ -18,4 +18,4 @@ #error add your platform here! #endif -#endif //DEPRECATE_HEADER_2348970348 +#endif //DEPRECATE_H_234897087787348 diff --git a/zen/dir_watcher.cpp b/zen/dir_watcher.cpp index 3bab8d34..97ecafe5 100644 --- a/zen/dir_watcher.cpp +++ b/zen/dir_watcher.cpp @@ -14,6 +14,7 @@ #include "device_notify.h" #include "win.h" //includes "windows.h" #include "long_path_prefix.h" + #include "optional.h" #elif defined ZEN_LINUX #include <map> @@ -119,9 +120,7 @@ public: void reportError(const std::wstring& msg, const std::wstring& description, DWORD errorCode) //throw() { std::lock_guard<std::mutex> dummy(lockAccess); - - ErrorInfo newInfo = { copyStringTo<BasicWString>(msg), copyStringTo<BasicWString>(description), errorCode }; - errorInfo = std::make_unique<ErrorInfo>(newInfo); + errorInfo = ErrorInfo({ copyStringTo<BasicWString>(msg), copyStringTo<BasicWString>(description), errorCode }); } private: @@ -136,7 +135,7 @@ private: BasicWString descr; DWORD errorCode; }; - std::unique_ptr<ErrorInfo> errorInfo; //non-empty if errors occurred in thread + Opt<ErrorInfo> errorInfo; //non-empty if errors occurred in thread }; @@ -147,8 +146,7 @@ public: ReadChangesAsync(const Zstring& directory, //make sure to not leak-in thread-unsafe types! const std::shared_ptr<SharedData>& shared) : shared_(shared), - dirpathPf(appendSeparator(directory)), - hDir(INVALID_HANDLE_VALUE) + dirpathPf(appendSeparator(directory)) { hDir = ::CreateFile(applyLongPathPrefix(dirpathPf).c_str(), //_In_ LPCTSTR lpFileName, FILE_LIST_DIRECTORY, //_In_ DWORD dwDesiredAccess, @@ -218,7 +216,7 @@ public: } //async I/O is a resource that needs to be guarded since it will write to local variable "buffer"! - zen::ScopeGuard guardAio = zen::makeGuard([&] + auto guardAio = zen::makeGuard<ScopeGuardRunMode::ON_EXIT>([&] { //Canceling Pending I/O Operations: http://msdn.microsoft.com/en-us/library/aa363789(v=vs.85).aspx #ifdef ZEN_WIN_VISTA_AND_LATER @@ -265,7 +263,7 @@ private: std::shared_ptr<SharedData> shared_; //worker thread only: Zstring dirpathPf; //thread safe! - HANDLE hDir; + HANDLE hDir = INVALID_HANDLE_VALUE; }; @@ -277,11 +275,9 @@ public: InterruptibleThread& worker) : notificationHandle(registerFolderRemovalNotification(hDir, //throw FileError displayPath, - [this] { this->onRequestRemoval (); }, //noexcept! + [this]{ this->onRequestRemoval (); }, //noexcept! [this](bool successful) { this->onRemovalFinished(); })), // - worker_(worker), - removalRequested(false), - operationComplete(false) {} + worker_(worker) {} ~HandleVolumeRemoval() { @@ -311,8 +307,8 @@ private: DeviceNotificationHandle* notificationHandle; InterruptibleThread& worker_; - bool removalRequested; - bool operationComplete; + bool removalRequested = false; + bool operationComplete = false; }; } @@ -375,9 +371,7 @@ std::vector<DirWatcher::Entry> DirWatcher::getChanges(const std::function<void() #elif defined ZEN_LINUX struct DirWatcher::Pimpl { - Pimpl() : notifDescr() {} - - int notifDescr; + int notifDescr = 0; std::map<int, Zstring> watchDescrs; //watch descriptor and (sub-)directory name (postfixed with separator) -> owned by "notifDescr" }; @@ -387,14 +381,14 @@ DirWatcher::DirWatcher(const Zstring& dirPath) : //throw FileError pimpl_(std::make_unique<Pimpl>()) { //get all subdirectories - std::vector<Zstring> fullDirList { baseDirPath }; + std::vector<Zstring> fullFolderList { baseDirPath }; { std::function<void (const Zstring& path)> traverse; - traverse = [&traverse, &fullDirList](const Zstring& path) + traverse = [&traverse, &fullFolderList](const Zstring& path) { traverseFolder(path, nullptr, - [&](const DirInfo& di ) { fullDirList.push_back(di.fullPath); traverse(di.fullPath); }, + [&](const DirInfo& di ) { fullFolderList.push_back(di.fullPath); traverse(di.fullPath); }, nullptr, //don't traverse into symlinks (analog to windows build) [&](const std::wstring& errorMsg) { throw FileError(errorMsg); }); }; @@ -407,7 +401,7 @@ DirWatcher::DirWatcher(const Zstring& dirPath) : //throw FileError if (pimpl_->notifDescr == -1) THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(baseDirPath)), L"inotify_init"); - zen::ScopeGuard guardDescr = zen::makeGuard([&] { ::close(pimpl_->notifDescr); }); + ZEN_ON_SCOPE_FAIL( ::close(pimpl_->notifDescr); ); //set non-blocking mode bool initSuccess = false; @@ -420,7 +414,7 @@ DirWatcher::DirWatcher(const Zstring& dirPath) : //throw FileError THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(baseDirPath)), L"fcntl"); //add watches - for (const Zstring& subDirPath : fullDirList) + for (const Zstring& subDirPath : fullFolderList) { int wd = ::inotify_add_watch(pimpl_->notifDescr, subDirPath.c_str(), IN_ONLYDIR | //"Only watch pathname if it is a directory." @@ -445,8 +439,6 @@ DirWatcher::DirWatcher(const Zstring& dirPath) : //throw FileError pimpl_->watchDescrs.emplace(wd, appendSeparator(subDirPath)); } - - guardDescr.dismiss(); } @@ -557,8 +549,7 @@ void eventCallback(ConstFSEventStreamRef streamRef, struct DirWatcher::Pimpl { - Pimpl() : eventStream() {} - FSEventStreamRef eventStream; + FSEventStreamRef eventStream = nullptr; std::vector<DirWatcher::Entry> changedFiles; }; @@ -583,6 +574,7 @@ DirWatcher::DirWatcher(const Zstring& dirPath) : FSEventStreamContext context = {}; context.info = &pimpl_->changedFiles; + //can this fail?? not documented! pimpl_->eventStream = ::FSEventStreamCreate(nullptr, //CFAllocatorRef allocator, &eventCallback, //FSEventStreamCallback callback, &context, //FSEventStreamContext* context, @@ -591,22 +583,16 @@ DirWatcher::DirWatcher(const Zstring& dirPath) : 0, //CFTimeInterval latency, in seconds kFSEventStreamCreateFlagWatchRoot | kFSEventStreamCreateFlagFileEvents); //FSEventStreamCreateFlags flags - //can this fail?? not documented! - - zen::ScopeGuard guardCreate = zen::makeGuard([&] { ::FSEventStreamRelease(pimpl_->eventStream); }); + ZEN_ON_SCOPE_FAIL( ::FSEventStreamRelease(pimpl_->eventStream); ); + //no-fail: ::FSEventStreamScheduleWithRunLoop(pimpl_->eventStream, //FSEventStreamRef streamRef, ::CFRunLoopGetCurrent(), //CFRunLoopRef runLoop; CFRunLoopGetCurrent(): failure not documented! kCFRunLoopDefaultMode); //CFStringRef runLoopMode - //no-fail - - zen::ScopeGuard guardRunloop = zen::makeGuard([&] { ::FSEventStreamInvalidate(pimpl_->eventStream); }); + ZEN_ON_SCOPE_FAIL( ::FSEventStreamInvalidate(pimpl_->eventStream); ); if (!::FSEventStreamStart(pimpl_->eventStream)) throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(baseDirPath)), L"Function call failed: FSEventStreamStart"); //no error code documented! - - guardCreate .dismiss(); - guardRunloop.dismiss(); } diff --git a/zen/error_log.h b/zen/error_log.h index 81892a25..d282ea4b 100644 --- a/zen/error_log.h +++ b/zen/error_log.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef ERRORLOGGING_H_INCLUDED -#define ERRORLOGGING_H_INCLUDED +#ifndef ERROR_LOG_H_8917590832147915 +#define ERROR_LOG_H_8917590832147915 #include <cassert> #include <algorithm> @@ -86,7 +86,7 @@ namespace template <class String> String formatMessageImpl(const LogEntry& entry) //internal linkage { - auto getTypeName = [&]() -> std::wstring + auto getTypeName = [&] { switch (entry.type) { @@ -132,4 +132,4 @@ template <class String> inline String formatMessage(const LogEntry& entry) { return formatMessageImpl<String>(entry); } } -#endif //ERRORLOGGING_H_INCLUDED +#endif //ERROR_LOG_H_8917590832147915 diff --git a/zen/file_access.cpp b/zen/file_access.cpp index e04673d3..2ca373aa 100644 --- a/zen/file_access.cpp +++ b/zen/file_access.cpp @@ -8,7 +8,6 @@ #include <map> #include <algorithm> #include <stdexcept> -#include "int64.h" #include "file_traverser.h" #include "scope_guard.h" #include "symlink_target.h" @@ -17,6 +16,7 @@ #ifdef ZEN_WIN #include <Aclapi.h> + #include "int64.h" #include "privilege.h" #include "long_path_prefix.h" #include "win_ver.h" @@ -24,7 +24,6 @@ #include <zen/vista_file_op.h> //requires COM initialization! #endif - #elif defined ZEN_LINUX #include <sys/vfs.h> //statfs #include <sys/time.h> //lutimes @@ -135,7 +134,7 @@ bool zen::somethingExists(const Zstring& itemPath) namespace { #ifdef ZEN_WIN -bool isFatDrive(const Zstring& filePath) //throw() +bool isFatDrive(const Zstring& filePath) //noexcept { const DWORD bufferSize = MAX_PATH + 1; std::vector<wchar_t> buffer(bufferSize); @@ -202,12 +201,17 @@ std::uint64_t zen::getFilesize(const Zstring& filePath) //throw FileError THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(filePath)), L"CreateFile"); ZEN_ON_SCOPE_EXIT(::CloseHandle(hFile)); - //why not use ::GetFileSizeEx() instead??? - BY_HANDLE_FILE_INFORMATION fileInfoHnd = {}; - if (!::GetFileInformationByHandle(hFile, &fileInfoHnd)) - THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(filePath)), L"GetFileInformationByHandle"); + LARGE_INTEGER fileSize = {}; + if (!::GetFileSizeEx(hFile, //_In_ HANDLE hFile, + &fileSize)) //_Out_ PLARGE_INTEGER lpFileSize + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(filePath)), L"GetFileSizeEx"); + return fileSize.QuadPart; - return get64BitUInt(fileInfoHnd.nFileSizeLow, fileInfoHnd.nFileSizeHigh); + //alternative: + //BY_HANDLE_FILE_INFORMATION fileInfoHnd = {}; + //if (!::GetFileInformationByHandle(hFile, &fileInfoHnd)) + // THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(filePath)), L"GetFileInformationByHandle"); + //return get64BitUInt(fileInfoHnd.nFileSizeLow, fileInfoHnd.nFileSizeHigh); #elif defined ZEN_LINUX || defined ZEN_MAC struct ::stat fileInfo = {}; @@ -312,53 +316,56 @@ void zen::removeDirectorySimple(const Zstring& dirPath) //throw FileError return; } #endif - throw FileError(replaceCpy(_("Cannot delete directory %x."), L"%x", fmtPath(dirPath)), formatSystemError(functionName, ec)); } - //may spuriously fail with ERROR_DIR_NOT_EMPTY(145) even though all child items have - //successfully been *marked* for deletion, but some application still has a handle open! - //e.g. Open "C:\Test\Dir1\Dir2" (filled with lots of files) in Explorer, then delete "C:\Test\Dir1" via ::RemoveDirectory() => Error 145 - //Sample code: http://us.generation-nt.com/answer/createfile-directory-handles-removing-parent-help-29126332.html + /* + Windows: may spuriously fail with ERROR_DIR_NOT_EMPTY(145) even though all child items have + successfully been *marked* for deletion, but some application still has a handle open! + e.g. Open "C:\Test\Dir1\Dir2" (filled with lots of files) in Explorer, then delete "C:\Test\Dir1" via ::RemoveDirectory() => Error 145 + Sample code: http://us.generation-nt.com/answer/createfile-directory-handles-removing-parent-help-29126332.html + Alternatives: 1. move file/empty folder to some other location, then DeleteFile()/RemoveDirectory() + 2. use CreateFile/FILE_FLAG_DELETE_ON_CLOSE *without* FILE_SHARE_DELETE instead of DeleteFile() => early failure + */ } namespace { -void removeDirectoryImpl(const Zstring& dirPath) //throw FileError +void removeDirectoryImpl(const Zstring& folderPath) //throw FileError { - assert(dirExists(dirPath)); //[!] no symlinks in this context!!! - //attention: check if dirPath is a symlink! Do NOT traverse into it deleting contained files!!! + assert(dirExists(folderPath)); //[!] no symlinks in this context!!! + //attention: check if folderPath is a symlink! Do NOT traverse into it deleting contained files!!! - std::vector<Zstring> fileList; - std::vector<Zstring> dirLinkList; - std::vector<Zstring> dirList; + std::vector<Zstring> filePaths; + std::vector<Zstring> folderSymlinkPaths; + std::vector<Zstring> folderPaths; //get all files and directories from current directory (WITHOUT subdirectories!) - traverseFolder(dirPath, - [&](const FileInfo& fi) { fileList.push_back(fi.fullPath); }, - [&](const DirInfo& di) { dirList .push_back(di.fullPath); }, //defer recursion => save stack space and allow deletion of extremely deep hierarchies! + traverseFolder(folderPath, + [&](const FileInfo& fi) { filePaths.push_back(fi.fullPath); }, + [&](const DirInfo& di) { folderPaths .push_back(di.fullPath); }, //defer recursion => save stack space and allow deletion of extremely deep hierarchies! [&](const SymlinkInfo& si) { #ifdef ZEN_WIN if (dirExists(si.fullPath)) //dir symlink - dirLinkList.push_back(si.fullPath); + folderSymlinkPaths.push_back(si.fullPath); else //file symlink, broken symlink #endif - fileList.push_back(si.fullPath); + filePaths.push_back(si.fullPath); }, [](const std::wstring& errorMsg) { throw FileError(errorMsg); }); - for (const Zstring& filePath : fileList) + for (const Zstring& filePath : filePaths) removeFile(filePath); //throw FileError - for (const Zstring& dirLinkPath : dirLinkList) - removeDirectorySimple(dirLinkPath); //throw FileError + for (const Zstring& symlinkPath : folderSymlinkPaths) + removeDirectorySimple(symlinkPath); //throw FileError //delete directories recursively - for (const Zstring& subDirPath : dirList) - removeDirectoryImpl(subDirPath); //throw FileError; call recursively to correctly handle symbolic links + for (const Zstring& subFolderPath : folderPaths) + removeDirectoryImpl(subFolderPath); //throw FileError; call recursively to correctly handle symbolic links - removeDirectorySimple(dirPath); //throw FileError + removeDirectorySimple(folderPath); //throw FileError } } @@ -442,7 +449,7 @@ void renameFile_sub(const Zstring& pathSource, const Zstring& pathTarget) //thro } #elif defined ZEN_LINUX || defined ZEN_MAC - //rename() will never fail with EEXIST, but always overwrite! + //rename() will never fail with EEXIST, but always (atomically) overwrite! => equivalent to SetFileInformationByHandle() + FILE_RENAME_INFO::ReplaceIfExists //=> Linux: renameat2() with RENAME_NOREPLACE -> still new, probably buggy //=> OS X: no solution @@ -458,7 +465,7 @@ void renameFile_sub(const Zstring& pathSource, const Zstring& pathTarget) //thro throw FileError(errorMsg, errorDescr); }; - if (!EqualFilePath()(pathSource, pathTarget)) //OS X: changing file name case is not an "already exists" error! + if (!equalFilePath(pathSource, pathTarget)) //OS X: changing file name case is not an "already exists" error! if (somethingExists(pathTarget)) throwException(EEXIST); @@ -511,7 +518,7 @@ Zstring findUnused8Dot3Name(const Zstring& filePath) //find a unique 8.3 short n if (!somethingExists(output)) //ensure uniqueness return output; } - throw std::runtime_error(std::string("100000000 files, one for each number, exist in this directory? You're kidding...") + utfCvrtTo<std::string>(pathPrefix) + + throw std::runtime_error(std::string("100,000,000 files, one for each number, exist in this directory? You're kidding...") + utfCvrtTo<std::string>(pathPrefix) + "\n" + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__)); } @@ -529,8 +536,8 @@ bool have8dot3NameClash(const Zstring& filePath) if (!shortName.empty() && !longName .empty() && - EqualFilePath()(origName, shortName) && - !EqualFilePath()(shortName, longName)) + equalFilePath(origName, shortName) && + !equalFilePath(shortName, longName)) { //for filePath short and long file name are equal and another unrelated file happens to have the same short name //e.g. filePath == "TESTWE~1", but another file is existing named "TestWeb" with short name ""TESTWE~1" @@ -896,9 +903,9 @@ void setFileTimeRaw(const Zstring& filePath, const struct ::timespec& modTime, P #elif defined ZEN_MAC struct AttrBufFileTimes { - std::uint32_t length; - struct ::timespec createTime; //keep order; see docs! - struct ::timespec writeTime; // + std::uint32_t length = 0; + struct ::timespec createTime = {}; //keep order; see docs! + struct ::timespec writeTime = {}; // } __attribute__((aligned(4), packed)); @@ -915,7 +922,7 @@ void setFileTimeRaw(const Zstring& filePath, attribs.bitmapcount = ATTR_BIT_MAP_COUNT; attribs.commonattr = (createTime ? ATTR_CMN_CRTIME : 0) | ATTR_CMN_MODTIME; - AttrBufFileTimes newTimes = {}; + AttrBufFileTimes newTimes; if (createTime) { newTimes.createTime.tv_sec = createTime->tv_sec; @@ -944,7 +951,7 @@ void getFileTimeRaw(int fd, //throw FileError attribs.bitmapcount = ATTR_BIT_MAP_COUNT; attribs.commonattr = ATTR_CMN_CRTIME | ATTR_CMN_MODTIME; - AttrBufFileTimes fileTimes = {}; + AttrBufFileTimes fileTimes; const int rv = ::fgetattrlist(fd, //int fd, &attribs, //struct ::attrlist* attrList, @@ -1080,7 +1087,7 @@ void copySecurityContext(const Zstring& source, const Zstring& target, ProcSymli if (rv3 < 0) THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write security context of %x."), L"%x", fmtPath(target)), L"setfilecon"); } -#endif //HAVE_SELINUX +#endif //copy permissions for files, directories or symbolic links: requires admin rights @@ -1340,7 +1347,9 @@ void zen::copyNewDirectory(const Zstring& sourcePath, const Zstring& targetPath, bool copyFilePermissions) { #ifdef ZEN_WIN - //special handling for volume root: trying to create existing root directory results in ERROR_ACCESS_DENIED rather than ERROR_ALREADY_EXISTS! +auto getErrorMsg = [](const Zstring& path){ return replaceCpy(_("Cannot create directory %x."), L"%x", fmtPath(path)); }; + +//special handling for volume root: trying to create existing root directory results in ERROR_ACCESS_DENIED rather than ERROR_ALREADY_EXISTS! Zstring dirTmp = removeLongPathPrefix(endsWith(targetPath, FILE_NAME_SEPARATOR) ? beforeLast(targetPath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE) : targetPath); @@ -1349,16 +1358,19 @@ void zen::copyNewDirectory(const Zstring& sourcePath, const Zstring& targetPath, { dirTmp += FILE_NAME_SEPARATOR; //we do not support "C:" to represent a relative path! - const ErrorCode lastError = somethingExists(dirTmp) ? ERROR_ALREADY_EXISTS : ERROR_PATH_NOT_FOUND; //don't use dirExists() => harmonize with ErrorTargetExisting! - - const std::wstring errorMsg = replaceCpy(_("Cannot create directory %x."), L"%x", fmtPath(dirTmp)); - const std::wstring errorDescr = formatSystemError(L"CreateDirectory", lastError); + const DWORD ec = somethingExists(dirTmp) ? ERROR_ALREADY_EXISTS : ERROR_PATH_NOT_FOUND; //don't use dirExists() => harmonize with ErrorTargetExisting! + const std::wstring errorDescr = formatSystemError(L"CreateDirectory", ec); - if (lastError == ERROR_ALREADY_EXISTS) - throw ErrorTargetExisting(errorMsg, errorDescr); - throw FileError(errorMsg, errorDescr); //[!] this is NOT a ErrorTargetPathMissing case! + if (ec == ERROR_ALREADY_EXISTS) + throw ErrorTargetExisting(getErrorMsg(dirTmp), errorDescr); + throw FileError(getErrorMsg(dirTmp), errorDescr); //[!] this is NOT a ErrorTargetPathMissing case! } + //deliberately don't support creating irregular folders like "...." https://social.technet.microsoft.com/Forums/windows/en-US/ffee2322-bb6b-4fdf-86f9-8f93cf1fa6cb/ + if (endsWith(targetPath, L' ') || + endsWith(targetPath, L'.')) + throw FileError(getErrorMsg(targetPath), replaceCpy(_("%x is not a regular directory name."), L"%x", fmtPath(afterLast(targetPath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL)))); + //don't use ::CreateDirectoryEx: //- it may fail with "wrong parameter (error code 87)" when source is on mapped online storage //- automatically copies symbolic links if encountered: unfortunately it doesn't copy symlinks over network shares but silently creates empty folders instead (on XP)! @@ -1383,14 +1395,13 @@ void zen::copyNewDirectory(const Zstring& sourcePath, const Zstring& targetPath, if (ec != ERROR_SUCCESS) { - const std::wstring errorMsg = replaceCpy(_("Cannot create directory %x."), L"%x", fmtPath(targetPath)); const std::wstring errorDescr = formatSystemError(L"CreateDirectory", ec); if (ec == ERROR_ALREADY_EXISTS) - throw ErrorTargetExisting(errorMsg, errorDescr); + throw ErrorTargetExisting(getErrorMsg(targetPath), errorDescr); else if (ec == ERROR_PATH_NOT_FOUND) - throw ErrorTargetPathMissing(errorMsg, errorDescr); - throw FileError(errorMsg, errorDescr); + throw ErrorTargetPathMissing(getErrorMsg(targetPath), errorDescr); + throw FileError(getErrorMsg(targetPath), errorDescr); } } @@ -1489,13 +1500,12 @@ void zen::copyNewDirectory(const Zstring& sourcePath, const Zstring& targetPath, /*int rv =*/ ::copyfile(sourcePath.c_str(), targetPath.c_str(), 0, COPYFILE_XATTR); #endif - zen::ScopeGuard guardNewDir = zen::makeGuard([&] { try { removeDirectorySimple(targetPath); } catch (FileError&) {} }); //ensure cleanup: + ZEN_ON_SCOPE_FAIL(try { removeDirectorySimple(targetPath); } + catch (FileError&) {}); //ensure cleanup: //enforce copying file permissions: it's advertized on GUI... if (copyFilePermissions) copyItemPermissions(sourcePath, targetPath, ProcSymlink::FOLLOW); //throw FileError - - guardNewDir.dismiss(); //target has been created successfully! } } @@ -1529,7 +1539,8 @@ void zen::copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool THROW_LAST_FILE_ERROR(replaceCpy(replaceCpy(_("Cannot copy symbolic link %x to %y."), L"%x", L"\n" + fmtPath(sourceLink)), L"%y", L"\n" + fmtPath(targetLink)), functionName); //allow only consistent objects to be created -> don't place before ::symlink, targetLink may already exist! - zen::ScopeGuard guardNewLink = zen::makeGuard([&] + + auto cleanUp = [&] { try { @@ -1541,7 +1552,8 @@ void zen::copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool removeFile(targetLink); //throw FileError } catch (FileError&) {} - }); + }; + ZEN_ON_SCOPE_FAIL(cleanUp()); //file times: essential for sync'ing a symlink: enforce this! (don't just try!) #ifdef ZEN_WIN @@ -1573,8 +1585,6 @@ void zen::copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool if (copyFilePermissions) copyItemPermissions(sourceLink, targetLink, ProcSymlink::DIRECT); //throw FileError - - guardNewLink.dismiss(); //target has been created successfully! } @@ -1798,7 +1808,8 @@ InSyncAttributes copyFileWindowsBackupStream(const Zstring& sourceFile, //throw throw FileError(errorMsg, errorDescr); } - ScopeGuard guardTarget = makeGuard([&] { try { removeFile(targetFile); } catch (FileError&) {} }); //transactional behavior: guard just after opening target and before managing hFileTarget + ZEN_ON_SCOPE_FAIL(try { removeFile(targetFile); } + catch (FileError&) {} ); //transactional behavior: guard just after opening target and before managing hFileTarget ZEN_ON_SCOPE_EXIT(::CloseHandle(hFileTarget)); //---------------------------------------------------------------------- @@ -1807,7 +1818,7 @@ InSyncAttributes copyFileWindowsBackupStream(const Zstring& sourceFile, //throw THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(targetFile)), L"GetFileInformationByHandle"); //return up-to-date file attributes - InSyncAttributes newAttrib = {}; + InSyncAttributes newAttrib; newAttrib.fileSize = get64BitUInt(fileInfoSource.nFileSizeLow, fileInfoSource.nFileSizeHigh); newAttrib.modificationTime = filetimeToTimeT(fileInfoSource.ftLastWriteTime); //no DST hack (yet) newAttrib.sourceFileId = extractFileId(fileInfoSource); @@ -1923,7 +1934,6 @@ InSyncAttributes copyFileWindowsBackupStream(const Zstring& sourceFile, //throw &fileInfoSource.ftLastWriteTime)) THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(targetFile)), L"SetFileTime"); - guardTarget.dismiss(); return newAttrib; } @@ -1938,20 +1948,17 @@ struct CallbackData const Zstring& targetFile) : sourceFile_(sourceFile), targetFile_(targetFile), - onUpdateCopyStatus_(onUpdateCopyStatus), - fileInfoSrc(), - fileInfoTrg(), - bytesReported() {} + onUpdateCopyStatus_(onUpdateCopyStatus) {} const Zstring& sourceFile_; const Zstring& targetFile_; const std::function<void(std::int64_t bytesDelta)>& onUpdateCopyStatus_; //optional std::exception_ptr exception; //out - BY_HANDLE_FILE_INFORMATION fileInfoSrc; //out: modified by CopyFileEx() at beginning - BY_HANDLE_FILE_INFORMATION fileInfoTrg; // + BY_HANDLE_FILE_INFORMATION fileInfoSrc{}; //out: modified by CopyFileEx() at beginning + BY_HANDLE_FILE_INFORMATION fileInfoTrg{}; // - std::int64_t bytesReported; //used internally to calculate bytes transferred delta + std::int64_t bytesReported = 0; //used internally to calculate bytes transferred delta }; @@ -2060,7 +2067,7 @@ InSyncAttributes copyFileWindowsDefault(const Zstring& sourceFile, //throw FileE try { activatePrivilege(SE_RESTORE_NAME); } catch (const FileError&) { backupPrivilegesActive = false; } - zen::ScopeGuard guardTarget = zen::makeGuard([&] { try { removeFile(targetFile); } catch (FileError&) {} }); + auto guardTarget = zen::makeGuard<ScopeGuardRunMode::ON_FAIL>([&] { try { removeFile(targetFile); } catch (FileError&) {} }); //transactional behavior: guard just before starting copy, we don't trust ::CopyFileEx(), do we? ;) DWORD copyFlags = COPY_FILE_FAIL_IF_EXISTS; @@ -2152,9 +2159,7 @@ InSyncAttributes copyFileWindowsDefault(const Zstring& sourceFile, //throw FileE // - perf: recent measurements show no slow down at all for buffered USB sticks! setFileTimeRaw(targetFile, &cbd.fileInfoSrc.ftCreationTime, cbd.fileInfoSrc.ftLastWriteTime, ProcSymlink::FOLLOW); //throw FileError - guardTarget.dismiss(); //target has been created successfully! - - InSyncAttributes newAttrib = {}; + InSyncAttributes newAttrib; newAttrib.fileSize = get64BitUInt(cbd.fileInfoSrc.nFileSizeLow, cbd.fileInfoSrc.nFileSizeHigh); newAttrib.modificationTime = filetimeToTimeT(cbd.fileInfoSrc.ftLastWriteTime); newAttrib.sourceFileId = extractFileId(cbd.fileInfoSrc); @@ -2228,8 +2233,9 @@ InSyncAttributes copyFileOsSpecific(const Zstring& sourceFile, //throw FileError } if (onUpdateCopyStatus) onUpdateCopyStatus(0); //throw X! - InSyncAttributes newAttrib = {}; - zen::ScopeGuard guardTarget = zen::makeGuard([&] { try { removeFile(targetFile); } catch (FileError&) {} }); + InSyncAttributes newAttrib; + ZEN_ON_SCOPE_FAIL( try { removeFile(targetFile); } + catch (FileError&) {} ); //transactional behavior: place guard after ::open() and before lifetime of FileOutput: //=> don't delete file that existed previously!!! { @@ -2278,7 +2284,6 @@ InSyncAttributes copyFileOsSpecific(const Zstring& sourceFile, //throw FileError setFileTime(targetFile, sourceInfo.st_mtime, ProcSymlink::FOLLOW); //throw FileError #endif - guardTarget.dismiss(); //target has been created successfully! return newAttrib; } #endif @@ -2303,15 +2308,12 @@ InSyncAttributes zen::copyNewFile(const Zstring& sourceFile, const Zstring& targ { const InSyncAttributes attr = copyFileOsSpecific(sourceFile, targetFile, onUpdateCopyStatus); //throw FileError, ErrorTargetExisting, ErrorFileLocked - if (copyFilePermissions) - { - //at this point we know we created a new file, so it's fine to delete it for cleanup! - zen::ScopeGuard guardTargetFile = zen::makeGuard([&] { try { removeFile(targetFile); } catch (FileError&) {}}); + //at this point we know we created a new file, so it's fine to delete it for cleanup! + ZEN_ON_SCOPE_FAIL(try { removeFile(targetFile); } + catch (FileError&) {}); + if (copyFilePermissions) copyItemPermissions(sourceFile, targetFile, ProcSymlink::FOLLOW); //throw FileError - guardTargetFile.dismiss(); //target has been created successfully! - } - return attr; } diff --git a/zen/file_access.h b/zen/file_access.h index 3588f79b..ec5bda66 100644 --- a/zen/file_access.h +++ b/zen/file_access.h @@ -55,8 +55,8 @@ void copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool copy struct InSyncAttributes { - std::uint64_t fileSize; - std::int64_t modificationTime; //time_t UTC compatible + std::uint64_t fileSize = 0; + std::int64_t modificationTime = 0; //time_t UTC compatible FileId sourceFileId; FileId targetFileId; }; diff --git a/zen/file_error.h b/zen/file_error.h index 7be52282..f41a878a 100644 --- a/zen/file_error.h +++ b/zen/file_error.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef FILEERROR_H_INCLUDED_839567308565656789 -#define FILEERROR_H_INCLUDED_839567308565656789 +#ifndef FILE_ERROR_H_839567308565656789 +#define FILE_ERROR_H_839567308565656789 #include <string> #include "zstring.h" @@ -49,7 +49,7 @@ DEFINE_NEW_FILE_ERROR(ErrorDifferentVolume); __pragma(warning(suppress: 4127)) /*"conditional expression is constant"*/ \ } while (false) -#else //variant witout "__pragma": +#else //same thing witout "__pragma": #define THROW_LAST_FILE_ERROR(msg, functionName) \ do { const ErrorCode ecInternal = getLastError(); throw FileError(msg, formatSystemError(functionName, ecInternal)); } while (false) #endif @@ -66,4 +66,4 @@ inline std::wstring fmtPath(const Zstring& displayPath) { return fmtPath(utfCvrt inline std::wstring fmtPath(const wchar_t* displayPath) { return fmtPath(std::wstring(displayPath)); } } -#endif //FILEERROR_H_INCLUDED_839567308565656789 +#endif //FILE_ERROR_H_839567308565656789 diff --git a/zen/file_id_def.h b/zen/file_id_def.h index c33edf81..24e45795 100644 --- a/zen/file_id_def.h +++ b/zen/file_id_def.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef FILE_ID_INTERNAL_HEADER_013287632486321493 -#define FILE_ID_INTERNAL_HEADER_013287632486321493 +#ifndef FILE_ID_DEF_H_013287632486321493 +#define FILE_ID_DEF_H_013287632486321493 #include <utility> @@ -66,4 +66,4 @@ FileId extractFileId(const struct ::stat& fileInfo) #endif } -#endif //FILE_ID_INTERNAL_HEADER_013287632486321493 +#endif //FILE_ID_DEF_H_013287632486321493 diff --git a/zen/file_io.cpp b/zen/file_io.cpp index 68e852da..5e8b7a1d 100644 --- a/zen/file_io.cpp +++ b/zen/file_io.cpp @@ -150,14 +150,11 @@ FileInput::FileInput(const Zstring& filepath) : //throw FileError, ErrorFileLock //------------------------------------------------------------------------------------------------------ - ScopeGuard constructorGuard = zen::makeGuard([&] //destructor call would lead to member double clean-up!!! - { -#ifdef ZEN_WIN - ::CloseHandle(fileHandle); +#ifdef ZEN_WIN //destructor call would lead to member double clean-up!!! + ZEN_ON_SCOPE_FAIL(::CloseHandle(fileHandle)); #elif defined ZEN_LINUX || defined ZEN_MAC - ::close(fileHandle); + ZEN_ON_SCOPE_FAIL(::close(fileHandle)); #endif - }); #ifdef ZEN_LINUX //handle still un-owned => need constructor guard //optimize read-ahead on input file: @@ -167,8 +164,6 @@ FileInput::FileInput(const Zstring& filepath) : //throw FileError, ErrorFileLock #elif defined ZEN_MAC //"dtruss" doesn't show use of "fcntl() F_RDAHEAD/F_RDADVISE" for "cp") #endif - - constructorGuard.dismiss(); } diff --git a/zen/file_io.h b/zen/file_io.h index 52be7f95..5bcf4189 100644 --- a/zen/file_io.h +++ b/zen/file_io.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef FILEIO_89578342758342572345 -#define FILEIO_89578342758342572345 +#ifndef FILE_IO_H_89578342758342572345 +#define FILE_IO_H_89578342758342572345 #include "file_error.h" @@ -88,4 +88,4 @@ private: }; } -#endif //FILEIO_89578342758342572345 +#endif //FILE_IO_H_89578342758342572345 diff --git a/zen/file_traverser.cpp b/zen/file_traverser.cpp index facce75c..7fd6c596 100644 --- a/zen/file_traverser.cpp +++ b/zen/file_traverser.cpp @@ -6,9 +6,9 @@ #include "file_traverser.h" #include "file_error.h" -#include "int64.h" #ifdef ZEN_WIN + #include "int64.h" #include "long_path_prefix.h" #include "file_access.h" #include "symlink_target.h" @@ -68,8 +68,8 @@ void zen::traverseFolder(const Zstring& dirPath, //skip "." and ".." const wchar_t* const itemNameRaw = findData.cFileName; - if (itemNameRaw[0] == 0) - throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtPath(dirPath)), L"FindNextFile: Data corruption; item with empty name."); + if (itemNameRaw[0] == 0) + throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtPath(dirPath)), L"FindNextFile: Data corruption; item with empty name."); if (itemNameRaw[0] == L'.' && (itemNameRaw[1] == 0 || (itemNameRaw[1] == L'.' && itemNameRaw[2] == 0))) @@ -106,15 +106,15 @@ void zen::traverseFolder(const Zstring& dirPath, std::vector<char> bufferUtfDecomposed; #endif - DIR* dirObj = ::opendir(dirPath.c_str()); //directory must NOT end with path separator, except "/" - if (!dirObj) + DIR* folder = ::opendir(dirPath.c_str()); //directory must NOT end with path separator, except "/" + if (!folder) THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot open directory %x."), L"%x", fmtPath(dirPath)), L"opendir"); - ZEN_ON_SCOPE_EXIT(::closedir(dirObj)); //never close nullptr handles! -> crash + ZEN_ON_SCOPE_EXIT(::closedir(folder)); //never close nullptr handles! -> crash for (;;) { struct ::dirent* dirEntry = nullptr; - if (::readdir_r(dirObj, reinterpret_cast< ::dirent*>(&buffer[0]), &dirEntry) != 0) + if (::readdir_r(folder, reinterpret_cast< ::dirent*>(&buffer[0]), &dirEntry) != 0) THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtPath(dirPath)), L"readdir_r"); //don't retry but restart dir traversal on error! http://blogs.msdn.com/b/oldnewthing/archive/2014/06/12/10533529.aspx @@ -125,7 +125,7 @@ void zen::traverseFolder(const Zstring& dirPath, const char* itemNameRaw = dirEntry->d_name; if (itemNameRaw[0] == 0) - throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtPath(dirPath)), L"readdir_r: Data corruption; item with empty name."); + throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtPath(dirPath)), L"readdir_r: Data corruption; item with empty name."); if (itemNameRaw[0] == '.' && (itemNameRaw[1] == 0 || (itemNameRaw[1] == '.' && itemNameRaw[2] == 0))) diff --git a/zen/file_traverser.h b/zen/file_traverser.h index 75c7660c..9f850e01 100644 --- a/zen/file_traverser.h +++ b/zen/file_traverser.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef FOLDER_TRAVERSER_H_INCLUDED_3127463214871234 -#define FOLDER_TRAVERSER_H_INCLUDED_3127463214871234 +#ifndef FILER_TRAVERSER_H_127463214871234 +#define FILER_TRAVERSER_H_127463214871234 #include <cstdint> #include <functional> @@ -41,4 +41,4 @@ void traverseFolder(const Zstring& dirPath, //noexcept const std::function<void (const std::wstring& errorMsg)>& onError); // } -#endif //FOLDER_TRAVERSER_H_INCLUDED_3127463214871234 +#endif //FILER_TRAVERSER_H_127463214871234 diff --git a/zen/fixed_list.h b/zen/fixed_list.h index 61ce3f16..362577d5 100644 --- a/zen/fixed_list.h +++ b/zen/fixed_list.h @@ -4,12 +4,13 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef FIXED_LIST_01238467085684139453534 -#define FIXED_LIST_01238467085684139453534 +#ifndef FIXED_LIST_H_01238467085684139453534 +#define FIXED_LIST_H_01238467085684139453534 #include <cassert> #include <iterator> + namespace zen { //std::list(C++11)-like class for inplace element construction supporting non-copyable/movable types @@ -20,9 +21,9 @@ class FixedList struct Node { template <class... Args> - Node(Args&& ... args) : next(nullptr), val(std::forward<Args>(args)...) {} + Node(Args&& ... args) : val(std::forward<Args>(args)...) {} - Node* next; //singly linked list is sufficient + Node* next = nullptr; //singly linked list is sufficient T val; }; @@ -35,14 +36,14 @@ public: class ListIterator : public std::iterator<std::forward_iterator_tag, U> { public: - ListIterator(NodeT* it = nullptr) : iter(it) {} - ListIterator& operator++() { iter = iter->next; return *this; } - inline friend bool operator==(const ListIterator& lhs, const ListIterator& rhs) { return lhs.iter == rhs.iter; } + ListIterator(NodeT* it = nullptr) : it_(it) {} + ListIterator& operator++() { it_ = it_->next; return *this; } + inline friend bool operator==(const ListIterator& lhs, const ListIterator& rhs) { return lhs.it_ == rhs.it_; } inline friend bool operator!=(const ListIterator& lhs, const ListIterator& rhs) { return !(lhs == rhs); } - U& operator* () const { return iter->val; } - U* operator->() const { return &iter->val; } + U& operator* () const { return it_->val; } + U* operator->() const { return &it_->val; } private: - NodeT* iter; + NodeT* it_; }; typedef T value_type; @@ -154,4 +155,4 @@ private: }; } -#endif //FIXED_LIST_01238467085684139453534 +#endif //FIXED_LIST_H_01238467085684139453534 diff --git a/zen/format_unit.cpp b/zen/format_unit.cpp index 9624458c..e9c686aa 100644 --- a/zen/format_unit.cpp +++ b/zen/format_unit.cpp @@ -8,12 +8,12 @@ #include "basic_math.h" #include "i18n.h" #include "time.h" -#include "int64.h" #include <cwchar> //swprintf #include <ctime> #include <cstdio> #ifdef ZEN_WIN + #include "int64.h" #include "win.h" //includes "windows.h" #include "win_ver.h" @@ -25,9 +25,18 @@ using namespace zen; +std::wstring zen::formatTwoDigitPrecision(double value) +{ + //print two digits: 0,1 | 1,1 | 11 + if (numeric::abs(value) < 9.95) //9.99 must not be formatted as "10.0" + return printNumber<std::wstring>(L"%.1f", value); + return numberTo<std::wstring>(numeric::round(value)); +} + + std::wstring zen::formatThreeDigitPrecision(double value) { - //print at least three digits: 0,01 | 0,11 | 1,11 | 11,1 | 111 + //print three digits: 0,01 | 0,11 | 1,11 | 11,1 | 111 if (numeric::abs(value) < 9.995) //9.999 must not be formatted as "10.00" return printNumber<std::wstring>(L"%.2f", value); if (numeric::abs(value) < 99.95) //99.99 must not be formatted as "100.0" @@ -197,7 +206,7 @@ private: return inst; } - IntegerFormat() : fmt(), valid_(false) + IntegerFormat() { //all we want is default NUMBERFMT, but set NumDigits to 0 fmt.NumDigits = 0; @@ -224,10 +233,10 @@ private: } } - NUMBERFMT fmt; + NUMBERFMT fmt = {}; std::wstring thousandSep; std::wstring decimalSep; - bool valid_; + bool valid_ = false; }; } #endif diff --git a/zen/format_unit.h b/zen/format_unit.h index d50baa32..eacb8d46 100644 --- a/zen/format_unit.h +++ b/zen/format_unit.h @@ -19,7 +19,8 @@ std::wstring remainingTimeToString(double timeInSec); std::wstring fractionToString(double fraction); //within [0, 1] std::wstring utcToLocalTimeString(std::int64_t utcTime); //like Windows Explorer would... -std::wstring formatThreeDigitPrecision(double value); //= *at least* three digits +std::wstring formatTwoDigitPrecision (double value); //format with fixed number of digits +std::wstring formatThreeDigitPrecision(double value); //(unless value is too large) template <class NumberType> std::wstring toGuiString(NumberType number); //format integer number including thousands separator @@ -4,16 +4,15 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef GUID_H_INCLUDED_80425780237502345 -#define GUID_H_INCLUDED_80425780237502345 +#ifndef GUID_H_80425780237502345 +#define GUID_H_80425780237502345 #include <string> #include <boost/uuid/uuid.hpp> #ifdef __GNUC__ //boost should clean this mess up #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wshadow" - #pragma GCC diagnostic ignored "-Wuninitialized" + #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif #include <boost/uuid/uuid_generators.hpp> @@ -35,4 +34,4 @@ std::string generateGUID() //creates a 16 byte GUID } } -#endif //GUID_H_INCLUDED_80425780237502345 +#endif //GUID_H_80425780237502345 @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef I18_N_HEADER_3843489325045 -#define I18_N_HEADER_3843489325045 +#ifndef I18_N_H_3843489325044253425456 +#define I18_N_H_3843489325044253425456 #include <string> #include <memory> @@ -28,18 +28,24 @@ namespace zen { -//implement handler to enable program wide localizations: implement THREAD-SAFE ACCESS! +//implement handler to enable program wide localizations: struct TranslationHandler { + //THREAD-SAFETY: "const" member must model thread-safe access! + TranslationHandler() {} virtual ~TranslationHandler() {} //C++11: std::wstring should be thread-safe like an int - virtual std::wstring translate(const std::wstring& text) = 0; //simple translation - virtual std::wstring translate(const std::wstring& singular, const std::wstring& plural, std::int64_t n) = 0; + virtual std::wstring translate(const std::wstring& text) const = 0; //simple translation + virtual std::wstring translate(const std::wstring& singular, const std::wstring& plural, std::int64_t n) const = 0; + +private: + TranslationHandler (const TranslationHandler&) = delete; + TranslationHandler& operator=(const TranslationHandler&) = delete; }; -void setTranslator(std::unique_ptr<TranslationHandler>&& newHandler = nullptr); //take ownership -TranslationHandler* getTranslator(); +void setTranslator(std::unique_ptr<const TranslationHandler>&& newHandler = nullptr); //take ownership +const TranslationHandler* getTranslator(); @@ -59,12 +65,13 @@ namespace implementation inline std::wstring translate(const std::wstring& text) { - if (TranslationHandler* t = getTranslator()) + if (const TranslationHandler* t = getTranslator()) return t->translate(text); return text; } + //translate plural forms: "%x day" "%x days" //returns "1 day" if n == 1; "123 days" if n == 123 for english language inline @@ -72,7 +79,7 @@ std::wstring translate(const std::wstring& singular, const std::wstring& plural, { assert(contains(plural, L"%x")); - if (TranslationHandler* t = getTranslator()) + if (const TranslationHandler* t = getTranslator()) { std::wstring translation = t->translate(singular, plural, n); assert(!contains(translation, L"%x")); @@ -82,6 +89,7 @@ std::wstring translate(const std::wstring& singular, const std::wstring& plural, return replaceCpy(std::abs(n) == 1 ? singular : plural, L"%x", toGuiString(n)); } + template <class T> inline std::wstring translate(const std::wstring& singular, const std::wstring& plural, T n) { @@ -89,19 +97,22 @@ std::wstring translate(const std::wstring& singular, const std::wstring& plural, return translate(singular, plural, static_cast<std::int64_t>(n)); } + inline -std::unique_ptr<TranslationHandler>& globalHandler() +std::unique_ptr<const TranslationHandler>& globalHandler() { - static std::unique_ptr<TranslationHandler> inst; //external linkage even in header! + static std::unique_ptr<const TranslationHandler> inst; //external linkage even in header! return inst; } } + inline -void setTranslator(std::unique_ptr<TranslationHandler>&& newHandler) { implementation::globalHandler() = std::move(newHandler); } +void setTranslator(std::unique_ptr<const TranslationHandler>&& newHandler) { implementation::globalHandler() = std::move(newHandler); } + inline -TranslationHandler* getTranslator() { return implementation::globalHandler().get(); } +const TranslationHandler* getTranslator() { return implementation::globalHandler().get(); } } -#endif //I18_N_HEADER_3843489325045 +#endif //I18_N_H_3843489325044253425456 diff --git a/zen/int64.h b/zen/int64.h deleted file mode 100644 index bc01a4c2..00000000 --- a/zen/int64.h +++ /dev/null @@ -1,71 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef FFS_LARGE_64_BIT_INTEGER_H_INCLUDED -#define FFS_LARGE_64_BIT_INTEGER_H_INCLUDED - -#include <cstdint> - -#ifdef ZEN_WIN - #include "win.h" -#endif - - -namespace zen -{ -#ifdef ZEN_WIN -inline -std::int64_t get64BitInt(DWORD low, LONG high) -{ - static_assert(sizeof(low) + sizeof(high) == sizeof(std::int64_t), ""); - - LARGE_INTEGER cvt = {}; - cvt.LowPart = low; - cvt.HighPart = high; - return cvt.QuadPart; -} - -std::int64_t get64BitInt(std::uint64_t low, std::int64_t high) = delete; - - -inline -std::uint64_t get64BitUInt(DWORD low, DWORD high) -{ - static_assert(sizeof(low) + sizeof(high) == sizeof(std::uint64_t), ""); - - ULARGE_INTEGER cvt = {}; - cvt.LowPart = low; - cvt.HighPart = high; - return cvt.QuadPart; -} - -std::int64_t get64BitUInt(std::uint64_t low, std::uint64_t high) = delete; - - -//convert FILETIME (number of 100-nanosecond intervals since January 1, 1601 UTC) -// to time_t (number of seconds since Jan. 1st 1970 UTC) -// -//FAT32 time is preserved exactly: FAT32 -> toTimeT -> tofiletime -> FAT32 -inline -std::int64_t filetimeToTimeT(const FILETIME& ft) -{ - return static_cast<std::int64_t>(get64BitUInt(ft.dwLowDateTime, ft.dwHighDateTime) / 10000000U) - get64BitInt(3054539008UL, 2); //caveat: signed/unsigned arithmetics! - //timeshift between ansi C time and FILETIME in seconds == 11644473600s -} - -inline -FILETIME timetToFileTime(std::int64_t utcTime) -{ - ULARGE_INTEGER cvt = {}; - cvt.QuadPart = (utcTime + get64BitInt(3054539008UL, 2)) * 10000000U; //caveat: signed/unsigned arithmetics! - - const FILETIME output = { cvt.LowPart, cvt.HighPart }; - return output; -} -#endif -} - -#endif //FFS_LARGE_64_BIT_INTEGER_H_INCLUDED diff --git a/zen/long_path_prefix.h b/zen/long_path_prefix.h index 6f6ebfdc..ef8e5dc8 100644 --- a/zen/long_path_prefix.h +++ b/zen/long_path_prefix.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef LONGPATHPREFIX_H_INCLUDED -#define LONGPATHPREFIX_H_INCLUDED +#ifndef LONG_PATH_PREFIX_H_3984678473567247567 +#define LONG_PATH_PREFIX_H_3984678473567247567 #include "win.h" #include "zstring.h" @@ -60,7 +60,7 @@ Zstring applyLongPathPrefixImpl(const Zstring& path) assert(!path.empty()); //nicely check almost all WinAPI accesses! assert(!zen::isWhiteSpace(path[0])); - //http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx#naming_conventions)) + //http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx#naming_conventions /* - special names like ".NUL" create all kinds of trouble (e.g. CreateDirectory() reports success, but does nothing) unless prefix is supplied => accept as limitation @@ -131,4 +131,4 @@ Zstring zen::ntPathToWin32Path(const Zstring& path) //noexcept return path; } -#endif //LONGPATHPREFIX_H_INCLUDED +#endif //LONG_PATH_PREFIX_H_3984678473567247567 diff --git a/zen/optional.h b/zen/optional.h index d65820c2..c01d95ff 100644 --- a/zen/optional.h +++ b/zen/optional.h @@ -8,6 +8,7 @@ #define OPTIONAL_H_2857428578342203589 #include <cassert> +#include <type_traits> namespace zen { @@ -36,36 +37,52 @@ template <class T> class Opt { public: - Opt() : value() , valid(false) {} - Opt(NoValue) : value() , valid(false) {} - Opt(const T& val) : value(val), valid(true ) {} + Opt() {} + Opt(NoValue) {} + Opt(const T& val) : valid(true) { new (&rawMem) T(val); } //throw X - Opt(const Opt& tmp) : value(tmp.valid ? tmp.value : T()), valid(tmp.valid) {} + Opt(const Opt& other) : valid(other.valid) + { + if (const T* val = other.get()) + new (&rawMem) T(*val); //throw X + } - Opt& operator=(const Opt& tmp) + ~Opt() { if (T* val = get()) val->~T(); } + + Opt& operator=(const Opt& other) //strong exception-safety iff T::operator=() is strongly exception-safe { - if (tmp.valid) - value = tmp.value; - valid = tmp.valid; + if (T* val = get()) + { + if (const T* valOther = other.get()) + *val = *valOther; //throw X + else + { + valid = false; + val->~T(); + } + } + else if (const T* valOther = other.get()) + { + new (&rawMem) T(*valOther); //throw X + valid = true; + } return *this; } - ////rvalue optimization: only basic exception safety: - // Opt(Opt&& tmp) : value(std::move(tmp.value)), valid(tmp.valid) {} - explicit operator bool() const { return valid; } //thank you C++11!!! - const T& operator*() const { assert(valid); return value; } - /**/ T& operator*() { assert(valid); return value; } + const T* get() const { return valid ? reinterpret_cast<const T*>(&rawMem) : nullptr; } + T* get() { return valid ? reinterpret_cast< T*>(&rawMem) : nullptr; } - const T* operator->() const { assert(valid); return &value; } - /**/ T* operator->() { assert(valid); return &value; } + const T& operator*() const { return *get(); } + /**/ T& operator*() { return *get(); } - void reset() { valid = false; } + const T* operator->() const { return get(); } + /**/ T* operator->() { return get(); } private: - T value; - bool valid; + std::aligned_storage_t<sizeof(T), alignof(T)> rawMem; //don't require T to be default-constructible! + bool valid = false; }; } @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef DEBUG_PERF_HEADER_83947184145342652456 -#define DEBUG_PERF_HEADER_83947184145342652456 +#ifndef PERF_H_83947184145342652456 +#define PERF_H_83947184145342652456 #include "deprecate.h" #include "tick_count.h" @@ -17,6 +17,7 @@ #include <iostream> #endif + //############# two macros for quick performance measurements ############### #define PERF_START zen::PerfTimer perfTest; #define PERF_STOP perfTest.showResult(); @@ -30,19 +31,14 @@ public: class TimerError {}; ZEN_DEPRECATE - PerfTimer() : //throw TimerError - ticksPerSec_(ticksPerSec()), - resultShown(false), - startTime(getTicksNow()), - paused(false), - elapsedUntilPause(0) + PerfTimer() : startTime(getTicksNow()) //throw TimerError { //std::clock() - "counts CPU time in Linux GCC and wall time in VC++" - WTF!??? if (ticksPerSec_ == 0) throw TimerError(); } - ~PerfTimer() { if (!resultShown) try { showResult(); } catch (TimerError&) {} } + ~PerfTimer() { if (!resultShown) try { showResult(); } catch (TimerError&) { assert(false); } } void pause() { @@ -102,12 +98,12 @@ private: return now; } - const std::int64_t ticksPerSec_; - bool resultShown; + const std::int64_t ticksPerSec_ = ticksPerSec(); //return 0 on error + bool resultShown = false; TickVal startTime; - bool paused; - int64_t elapsedUntilPause; + bool paused = false; + int64_t elapsedUntilPause = 0; }; } -#endif //DEBUG_PERF_HEADER_83947184145342652456 +#endif //PERF_H_83947184145342652456 diff --git a/zen/process_priority.cpp b/zen/process_priority.cpp index 017eaa8b..2d1abafa 100644 --- a/zen/process_priority.cpp +++ b/zen/process_priority.cpp @@ -30,11 +30,6 @@ PreventStandby::~PreventStandby() } -#ifndef PROCESS_MODE_BACKGROUND_BEGIN - #define PROCESS_MODE_BACKGROUND_BEGIN 0x00100000 // Windows Server 2003 and Windows XP/2000: This value is not supported! - #define PROCESS_MODE_BACKGROUND_END 0x00200000 // -#endif - struct ScheduleForBackgroundProcessing::Pimpl {}; diff --git a/zen/process_priority.h b/zen/process_priority.h index 72ce972c..48b95c9e 100644 --- a/zen/process_priority.h +++ b/zen/process_priority.h @@ -3,8 +3,8 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 * // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef PREVENTSTANDBY_H_83421759082143245 -#define PREVENTSTANDBY_H_83421759082143245 +#ifndef PROCESS_PRIORITY_H_83421759082143245 +#define PROCESS_PRIORITY_H_83421759082143245 #include <memory> #include "file_error.h" @@ -35,4 +35,4 @@ private: }; } -#endif //PREVENTSTANDBY_H_83421759082143245 +#endif //PROCESS_PRIORITY_H_83421759082143245 diff --git a/zen/recycler.cpp b/zen/recycler.cpp index d49e686c..59d2729a 100644 --- a/zen/recycler.cpp +++ b/zen/recycler.cpp @@ -12,6 +12,8 @@ #ifdef ZEN_WIN_VISTA_AND_LATER #include "vista_file_op.h" + #else + #include "com_tools.h" #endif #elif defined ZEN_LINUX @@ -179,12 +181,18 @@ bool zen::recycleBinExists(const Zstring& dirPath, const std::function<void ()>& #else //excessive runtime if recycle bin exists, is full and drive is slow: - auto ft = runAsync([dirPath]() + auto ft = runAsync([dirPath]() -> HRESULT { - SHQUERYRBINFO recInfo = {}; - recInfo.cbSize = sizeof(recInfo); - return ::SHQueryRecycleBin(dirPath.c_str(), //__in_opt LPCTSTR pszRootPath, - &recInfo); //__inout LPSHQUERYRBINFO pSHQueryRBInfo + try + { + ComInitializer ci; //throw SysError + + SHQUERYRBINFO recInfo = {}; + recInfo.cbSize = sizeof(recInfo); + return ::SHQueryRecycleBin(dirPath.c_str(), //__in_opt LPCTSTR pszRootPath, + &recInfo); //__inout LPSHQUERYRBINFO pSHQueryRBInfo + } + catch (SysError&) { assert(false); return ERROR_GEN_FAILURE; } }); while (ft.wait_for(std::chrono::milliseconds(50)) != std::future_status::ready) diff --git a/zen/recycler.h b/zen/recycler.h index 3df7fda2..4b5658cb 100644 --- a/zen/recycler.h +++ b/zen/recycler.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef RECYCLER_H_INCLUDED_18345067341545 -#define RECYCLER_H_INCLUDED_18345067341545 +#ifndef RECYCLER_H_18345067341545 +#define RECYCLER_H_18345067341545 #include <vector> #include <functional> @@ -48,4 +48,4 @@ void recycleOrDelete(const std::vector<Zstring>& filePaths, //throw FileError, r #endif } -#endif //RECYCLER_H_INCLUDED_18345067341545 +#endif //RECYCLER_H_18345067341545 diff --git a/zen/scope_guard.h b/zen/scope_guard.h index b5564c9b..791764de 100644 --- a/zen/scope_guard.h +++ b/zen/scope_guard.h @@ -4,79 +4,116 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef ZEN_SCOPEGUARD_8971632487321434 -#define ZEN_SCOPEGUARD_8971632487321434 +#ifndef SCOPE_GUARD_H_8971632487321434 +#define SCOPE_GUARD_H_8971632487321434 #include <cassert> +#include <exception> #include <type_traits> //std::decay -//best of Zen, Loki and C++11 +//best of Zen, Loki and C++17 + + +#ifdef ZEN_WIN +inline int getUncaughtExceptionCount() { return std::uncaught_exceptions(); } + +#elif defined ZEN_LINUX || defined ZEN_MAC +//std::uncaught_exceptions() currently unsupported on GCC and Clang => clean up ASAP +#ifdef ZEN_LINUX + static_assert(__GNUC__ < 5 || (__GNUC__ == 5 && (__GNUC_MINOR__ < 2 || (__GNUC_MINOR__ == 2 && __GNUC_PATCHLEVEL__ <= 1))), "check std::uncaught_exceptions support"); +#else + static_assert(__clang_major__ < 7 || (__clang_major__ == 7 && __clang_minor__ <= 0), "check std::uncaught_exceptions support"); +#endif + +namespace __cxxabiv1 +{ +struct __cxa_eh_globals; +extern "C" __cxa_eh_globals* __cxa_get_globals() noexcept; +} + +inline int getUncaughtExceptionCount() +{ + return *(reinterpret_cast<unsigned int*>(static_cast<char*>(static_cast<void*>(__cxxabiv1::__cxa_get_globals())) + sizeof(void*))); +} +#endif + namespace zen { //Scope Guard /* - zen::ScopeGuard lockAio = zen::makeGuard([&] { ::CloseHandle(hDir); }); + auto guardAio = zen::makeGuard<ScopeGuardRunMode::ON_EXIT>([&] { ::CloseHandle(hDir); }); ... - lockAio.dismiss(); -*/ + guardAio.dismiss(); -//Scope Exit -/* +Scope Exit: ZEN_ON_SCOPE_EXIT(::CloseHandle(hDir)); + ZEN_ON_SCOPE_FAIL(UndoPreviousWork()); + ZEN_ON_SCOPE_SUCCESS(NotifySuccess()); */ -class ScopeGuardBase +enum class ScopeGuardRunMode { -public: - void dismiss() { dismissed_ = true; } - -protected: - ScopeGuardBase() {} - ScopeGuardBase(ScopeGuardBase&& other) : dismissed_(other.dismissed_) { other.dismiss(); } //take over responsibility - ~ScopeGuardBase() {} //[!] protected non-virtual base class destructor - - bool isDismissed() const { return dismissed_; } - -private: - ScopeGuardBase (const ScopeGuardBase&) = delete; - ScopeGuardBase& operator=(const ScopeGuardBase&) = delete; - - bool dismissed_ = false; + ON_EXIT, + ON_SUCCESS, + ON_FAIL }; -template <typename F> -class ScopeGuardImpl : public ScopeGuardBase +template <ScopeGuardRunMode runMode, typename F> +class ScopeGuard { public: - explicit ScopeGuardImpl(const F& fun) : fun_(fun) {} - explicit ScopeGuardImpl( F&& fun) : fun_(std::move(fun)) {} - ScopeGuardImpl(ScopeGuardImpl&& other) : ScopeGuardBase(std::move(other)), fun_(std::move(other.fun_)) {} + explicit ScopeGuard(const F& fun) : fun_(fun) {} + explicit ScopeGuard( F&& fun) : fun_(std::move(fun)) {} + + ScopeGuard(ScopeGuard&& other) : fun_(std::move(other.fun_)), + exeptionCount(other.exeptionCount), + dismissed(other.dismissed) { other.dismissed = true; } - ~ScopeGuardImpl() + ~ScopeGuard() noexcept(runMode != ScopeGuardRunMode::ON_SUCCESS) { - if (!this->isDismissed()) - try +#ifdef _MSC_VER + #pragma warning(suppress: 4127) //"conditional expression is constant" +#endif + if (!dismissed) + { + if (runMode != ScopeGuardRunMode::ON_EXIT) { - fun_(); + const bool failed = getUncaughtExceptionCount() > exeptionCount; + if ((runMode == ScopeGuardRunMode::ON_FAIL) != failed) + return; } - catch (...) { assert(false); } + + if (runMode == ScopeGuardRunMode::ON_SUCCESS) + fun_(); //throw X + else + try { fun_(); } + catch (...) { assert(false); } //consistency: don't expect exceptions for ON_EXIT even if "!failed"! + } } + void dismiss() { dismissed = true; } + private: + ScopeGuard (const ScopeGuard&) = delete; + ScopeGuard& operator=(const ScopeGuard&) = delete; + F fun_; + const int exeptionCount = getUncaughtExceptionCount(); + bool dismissed = false; }; -typedef ScopeGuardBase&& ScopeGuard; -template <class F> inline -ScopeGuardImpl<typename std::decay<F>::type> makeGuard(F&& fun) { return ScopeGuardImpl<typename std::decay<F>::type>(std::forward<F>(fun)); } +template <ScopeGuardRunMode runMode, class F> inline +auto makeGuard(F&& fun) { return ScopeGuard<runMode, std::decay_t<F>>(std::forward<F>(fun)); } } #define ZEN_CONCAT_SUB(X, Y) X ## Y #define ZEN_CONCAT(X, Y) ZEN_CONCAT_SUB(X, Y) -#define ZEN_ON_SCOPE_EXIT(X) auto ZEN_CONCAT(dummy, __LINE__) = zen::makeGuard([&]{ X; }); (void)ZEN_CONCAT(dummy, __LINE__); +#define ZEN_ON_SCOPE_EXIT(X) auto ZEN_CONCAT(dummy, __LINE__) = zen::makeGuard<zen::ScopeGuardRunMode::ON_EXIT >([&]{ X; }); (void)ZEN_CONCAT(dummy, __LINE__); +#define ZEN_ON_SCOPE_FAIL(X) auto ZEN_CONCAT(dummy, __LINE__) = zen::makeGuard<zen::ScopeGuardRunMode::ON_FAIL >([&]{ X; }); (void)ZEN_CONCAT(dummy, __LINE__); +#define ZEN_ON_SCOPE_SUCCESS(X) auto ZEN_CONCAT(dummy, __LINE__) = zen::makeGuard<zen::ScopeGuardRunMode::ON_SUCCESS>([&]{ X; }); (void)ZEN_CONCAT(dummy, __LINE__); -#endif //ZEN_SCOPEGUARD_8971632487321434 +#endif //SCOPE_GUARD_H_8971632487321434 diff --git a/zen/serialize.h b/zen/serialize.h index ff2871b1..77cf657e 100644 --- a/zen/serialize.h +++ b/zen/serialize.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef SERIALIZE_H_INCLUDED_83940578357 -#define SERIALIZE_H_INCLUDED_83940578357 +#ifndef SERIALIZE_H_839405783574356 +#define SERIALIZE_H_839405783574356 #include <functional> #include <cstdint> @@ -250,4 +250,4 @@ C readContainer(BinInputStream& stream) //throw UnexpectedEndOfStreamError } } -#endif //SERIALIZE_H_INCLUDED_83940578357 +#endif //SERIALIZE_H_839405783574356 diff --git a/zen/shell_execute.h b/zen/shell_execute.h index 2adce179..9b8e00f9 100644 --- a/zen/shell_execute.h +++ b/zen/shell_execute.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef EXECUTE_HEADER_23482134578134134 -#define EXECUTE_HEADER_23482134578134134 +#ifndef SHELL_EXECUTE_H_23482134578134134 +#define SHELL_EXECUTE_H_23482134578134134 #include "file_error.h" @@ -94,9 +94,9 @@ void shellExecute(const Zstring& command, ExecutionType type) //throw FileError if (!argv.empty()) { filepath = argv[0]; - for (auto iter = argv.begin() + 1; iter != argv.end(); ++iter) - arguments += (iter != argv.begin() ? L" " : L"") + - (iter->empty() || std::any_of(iter->begin(), iter->end(), &isWhiteSpace<wchar_t>) ? L"\"" + *iter + L"\"" : *iter); + for (auto it = argv.begin() + 1; it != argv.end(); ++it) + arguments += (it != argv.begin() ? L" " : L"") + + (it->empty() || std::any_of(it->begin(), it->end(), &isWhiteSpace<wchar_t>) ? L"\"" + *it + L"\"" : *it); } auto fillExecInfo = [&](SHELLEXECUTEINFO& execInfo) @@ -130,4 +130,4 @@ void shellExecute(const Zstring& command, ExecutionType type) //throw FileError } } -#endif //EXECUTE_HEADER_23482134578134134 +#endif //SHELL_EXECUTE_H_23482134578134134 diff --git a/zen/stl_tools.h b/zen/stl_tools.h index 1e38f1b0..b78dd5dd 100644 --- a/zen/stl_tools.h +++ b/zen/stl_tools.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef STL_TOOLS_HEADER_84567184321434 -#define STL_TOOLS_HEADER_84567184321434 +#ifndef STL_TOOLS_H_84567184321434 +#define STL_TOOLS_H_84567184321434 #include <set> #include <map> @@ -15,6 +15,7 @@ #include "type_tools.h" #include "build_info.h" + //enhancements for <algorithm> namespace zen { @@ -93,11 +94,11 @@ namespace impl template <class S, class Predicate> inline void set_or_map_erase_if(S& s, Predicate p) { - for (auto iter = s.begin(); iter != s.end();) - if (p(*iter)) - s.erase(iter++); + for (auto it = s.begin(); it != s.end();) + if (p(*it)) + s.erase(it++); else - ++iter; + ++it; } } @@ -125,14 +126,14 @@ void append(std::map<KeyType, ValueType, LessType, Alloc>& m, const C& c) { m.in template <class M, class K, class V> inline V& map_add_or_update(M& map, const K& key, const V& value) //efficient add or update without "default-constructible" requirement (Effective STL, item 24) { - auto iter = map.lower_bound(key); - if (iter != map.end() && !(map.key_comp()(key, iter->first))) + auto it = map.lower_bound(key); + if (it != map.end() && !(map.key_comp()(key, it->first))) { - iter->second = value; - return iter->second; + it->second = value; + return it->second; } else - return map.insert(iter, typename M::value_type(key, value))->second; + return map.insert(it, typename M::value_type(key, value))->second; } @@ -158,12 +159,12 @@ ForwardIterator binary_search(ForwardIterator first, ForwardIterator last, const template <class BidirectionalIterator, class T> inline BidirectionalIterator find_last(const BidirectionalIterator first, const BidirectionalIterator last, const T& value) { - for (BidirectionalIterator iter = last; iter != first;) //reverse iteration: 1. check 2. decrement 3. evaluate + for (BidirectionalIterator it = last; it != first;) //reverse iteration: 1. check 2. decrement 3. evaluate { - --iter; // + --it; // - if (*iter == value) - return iter; + if (*it == value) + return it; } return last; } @@ -173,7 +174,7 @@ template <class BidirectionalIterator1, class BidirectionalIterator2> inline BidirectionalIterator1 search_last(const BidirectionalIterator1 first1, BidirectionalIterator1 last1, const BidirectionalIterator2 first2, const BidirectionalIterator2 last2) { - const BidirectionalIterator1 iterNotFound = last1; + const BidirectionalIterator1 itNotFound = last1; //reverse iteration: 1. check 2. decrement 3. evaluate for (;;) @@ -184,7 +185,7 @@ BidirectionalIterator1 search_last(const BidirectionalIterator1 first1, Bi for (;;) { if (it2 == first2) return it1; - if (it1 == first1) return iterNotFound; + if (it1 == first1) return itNotFound; --it1; --it2; @@ -231,4 +232,4 @@ size_t hashBytes(const unsigned char* ptr, size_t len) } } -#endif //STL_TOOLS_HEADER_84567184321434 +#endif //STL_TOOLS_H_84567184321434 diff --git a/zen/string_base.h b/zen/string_base.h index 224797e8..96d46fc4 100644 --- a/zen/string_base.h +++ b/zen/string_base.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef Z_BASE_H_INCLUDED_08321745456 -#define Z_BASE_H_INCLUDED_08321745456 +#ifndef STRING_BASE_H_083217454562342526 +#define STRING_BASE_H_083217454562342526 #include <algorithm> #include <cassert> @@ -249,8 +249,8 @@ public: //std::string functions size_t length() const; size_t size () const { return length(); } - const Char* c_str() const { return rawStr; }; //C-string format with 0-termination - const Char* data() const { return rawStr; }; //internal representation, 0-termination not guaranteed + const Char* c_str() const { return rawStr; } //C-string format with 0-termination + const Char* data() const { return rawStr; } //internal representation, 0-termination not guaranteed const Char operator[](size_t pos) const; bool empty() const { return length() == 0; } void clear(); @@ -686,4 +686,4 @@ Zbase<Char, SP, AP>& Zbase<Char, SP, AP>::operator+=(Char ch) } } -#endif //Z_BASE_H_INCLUDED_08321745456 +#endif //STRING_BASE_H_083217454562342526 diff --git a/zen/string_tools.h b/zen/string_tools.h index 8f83b9cd..fc9fe806 100644 --- a/zen/string_tools.h +++ b/zen/string_tools.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef STRING_TOOLS_HEADER_213458973046 -#define STRING_TOOLS_HEADER_213458973046 +#ifndef STRING_TOOLS_H_213458973046 +#define STRING_TOOLS_H_213458973046 #include <cctype> //isspace #include <cwctype> //iswspace @@ -42,8 +42,8 @@ template <class S, class T> S afterFirst (const S& str, const T& term, FailureRe template <class S, class T> S beforeFirst(const S& str, const T& term, FailureReturnVal rv); template <class S, class T> std::vector<S> split(const S& str, const T& delimiter); -template <class S> void trim ( S& str, bool fromLeft = true, bool fromRight = true); -template <class S> S trimCpy(const S& str, bool fromLeft = true, bool fromRight = true); +template <class S> void trim (S& str, bool fromLeft = true, bool fromRight = true); +template <class S> S trimCpy(S str, bool fromLeft = true, bool fromRight = true); template <class S, class T, class U> void replace ( S& str, const T& oldTerm, const U& newTerm, bool replaceAll = true); template <class S, class T, class U> S replaceCpy(const S& str, const T& oldTerm, const U& newTerm, bool replaceAll = true); @@ -54,7 +54,7 @@ template <class Num, class S > Num stringTo(const S& str); template <class S, class T, class Num> S printNumber(const T& format, const Num& number); //format a single number using std::snprintf() //string to string conversion: converts string-like type into char-compatible target string class -template <class T, class S> T copyStringTo(const S& str); +template <class T, class S> T copyStringTo(S&& str); @@ -94,6 +94,7 @@ bool isDigit(Char ch) //similar to implmenetation of std::::isdigit()! return static_cast<Char>('0') <= ch && ch <= static_cast<Char>('9'); } + template <> bool isAlpha(char ch) = delete; //probably not a good idea with UTF-8 anyway... template <> inline bool isAlpha(wchar_t ch) { return std::iswalpha(ch) != 0; } @@ -252,7 +253,7 @@ std::vector<S> split(const S& str, const T& delimiter) } -namespace implementation +namespace impl { ZEN_INIT_DETECT_MEMBER(append); @@ -295,8 +296,8 @@ S replaceCpy(const S& str, const T& oldTerm, const U& newTerm, bool replaceAll) for (;;) { - implementation::stringAppend(output, strPos, strMatch - strPos); - implementation::stringAppend(output, newBegin, newLen); + impl::stringAppend(output, strPos, strMatch - strPos); + impl::stringAppend(output, newBegin, newLen); strPos = strMatch + oldLen; @@ -308,7 +309,7 @@ S replaceCpy(const S& str, const T& oldTerm, const U& newTerm, bool replaceAll) if (strMatch == strEnd) break; } - implementation::stringAppend(output, strPos, strEnd - strPos); + impl::stringAppend(output, strPos, strEnd - strPos); return output; } @@ -346,16 +347,15 @@ void trim(S& str, bool fromLeft, bool fromRight) template <class S> inline -S trimCpy(const S& str, bool fromLeft, bool fromRight) +S trimCpy(S str, bool fromLeft, bool fromRight) { //implementing trimCpy() in terms of trim(), instead of the other way round, avoids memory allocations when trimming from right! - S tmp = str; - trim(tmp, fromLeft, fromRight); - return tmp; + trim(str, fromLeft, fromRight); + return std::move(str); //"str" is an l-value parameter => no copy elision! } -namespace implementation +namespace impl { template <class S, class T> struct CopyStringToString @@ -363,18 +363,19 @@ struct CopyStringToString T copy(const S& src) const { return T(strBegin(src), strLength(src)); } }; -template <class S> -struct CopyStringToString<S, S> //perf: we don't need a deep copy if string types match +template <class T> +struct CopyStringToString<T, T> //perf: we don't need a deep copy if string types match { - const S& copy(const S& src) const { return src; } + template <class S> + T copy(S&& str) const { return std::forward<S>(str); } }; } template <class T, class S> inline -T copyStringTo(const S& str) { return implementation::CopyStringToString<S, T>().copy(str); } +T copyStringTo(S&& str) { return impl::CopyStringToString<std::decay_t<S>, T>().copy(std::forward<S>(str)); } -namespace implementation +namespace impl { template <class Num> inline int saferPrintf(char* buffer, size_t bufferSize, const char* format, const Num& number) //there is no such thing as a "safe" printf ;) @@ -400,17 +401,18 @@ int saferPrintf(wchar_t* buffer, size_t bufferSize, const wchar_t* format, const template <class S, class T, class Num> inline S printNumber(const T& format, const Num& number) //format a single number using ::sprintf { - typedef typename GetCharType<S>::Type CharType; + static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, ""); + using CharType = typename GetCharType<S>::Type; const int BUFFER_SIZE = 128; - CharType buffer[BUFFER_SIZE]; - const int charsWritten = implementation::saferPrintf(buffer, BUFFER_SIZE, strBegin(format), number); + CharType buffer[BUFFER_SIZE]; //zero-initialize? + const int charsWritten = impl::saferPrintf(buffer, BUFFER_SIZE, strBegin(format), number); return charsWritten > 0 ? S(buffer, charsWritten) : S(); } -namespace implementation +namespace impl { enum NumberType { @@ -424,7 +426,7 @@ enum NumberType template <class S, class Num> inline S numberTo(const Num& number, Int2Type<NUM_TYPE_OTHER>) //default number to string conversion using streams: convenient, but SLOW, SLOW, SLOW!!!! (~ factor of 20) { - typedef typename GetCharType<S>::Type CharType; + using CharType = typename GetCharType<S>::Type; std::basic_ostringstream<CharType> ss; ss << number; @@ -453,7 +455,7 @@ template <class OutputIterator, class Num> inline void formatNegativeInteger(Num n, OutputIterator& it) { assert(n < 0); - typedef typename std::iterator_traits<OutputIterator>::value_type CharType; + using CharType = typename std::iterator_traits<OutputIterator>::value_type; do { const Num tmp = n / 10; @@ -469,7 +471,7 @@ template <class OutputIterator, class Num> inline void formatPositiveInteger(Num n, OutputIterator& it) { assert(n >= 0); - typedef typename std::iterator_traits<OutputIterator>::value_type CharType; + using CharType = typename std::iterator_traits<OutputIterator>::value_type; do { const Num tmp = n / 10; @@ -483,8 +485,9 @@ void formatPositiveInteger(Num n, OutputIterator& it) template <class S, class Num> inline S numberTo(const Num& number, Int2Type<NUM_TYPE_SIGNED_INT>) { - typedef typename GetCharType<S>::Type CharType; - CharType buffer[2 + sizeof(Num) * 241 / 100]; //it's generally faster to use a buffer than to rely on String::operator+=() (in)efficiency + using CharType = typename GetCharType<S>::Type; + CharType buffer[2 + sizeof(Num) * 241 / 100]; //zero-initialize? + //it's generally faster to use a buffer than to rely on String::operator+=() (in)efficiency //required chars (+ sign char): 1 + ceil(ln_10(256^sizeof(n) / 2 + 1)) -> divide by 2 for signed half-range; second +1 since one half starts with 1! // <= 1 + ceil(ln_10(256^sizeof(n))) =~ 1 + ceil(sizeof(n) * 2.4082) <= 2 + floor(sizeof(n) * 2.41) @@ -503,8 +506,8 @@ S numberTo(const Num& number, Int2Type<NUM_TYPE_SIGNED_INT>) template <class S, class Num> inline S numberTo(const Num& number, Int2Type<NUM_TYPE_UNSIGNED_INT>) { - typedef typename GetCharType<S>::Type CharType; - CharType buffer[1 + sizeof(Num) * 241 / 100]; + using CharType = typename GetCharType<S>::Type; + CharType buffer[1 + sizeof(Num) * 241 / 100]; //zero-initialize? //required chars: ceil(ln_10(256^sizeof(n))) =~ ceil(sizeof(n) * 2.4082) <= 1 + floor(sizeof(n) * 2.41) auto it = std::end(buffer); @@ -519,7 +522,7 @@ S numberTo(const Num& number, Int2Type<NUM_TYPE_UNSIGNED_INT>) template <class Num, class S> inline Num stringTo(const S& str, Int2Type<NUM_TYPE_OTHER>) //default string to number conversion using streams: convenient, but SLOW { - typedef typename GetCharType<S>::Type CharType; + using CharType = typename GetCharType<S>::Type; Num number = 0; std::basic_istringstream<CharType>(copyStringTo<std::basic_string<CharType>>(str)) >> number; return number; @@ -538,8 +541,8 @@ Num stringTo(const S& str, Int2Type<NUM_TYPE_FLOATING_POINT>) template <class Num, class S> Num extractInteger(const S& str, bool& hasMinusSign) //very fast conversion to integers: slightly faster than std::atoi, but more importantly: generic { - typedef typename GetCharType<S>::Type CharType; - + using CharType = typename GetCharType<S>::Type; + const CharType* first = strBegin(str); const CharType* last = first + strLength(str); @@ -606,28 +609,28 @@ Num stringTo(const S& str, Int2Type<NUM_TYPE_UNSIGNED_INT>) //very fast conversi template <class S, class Num> inline S numberTo(const Num& number) { - typedef Int2Type< - IsSignedInt <Num>::value ? implementation::NUM_TYPE_SIGNED_INT : - IsUnsignedInt<Num>::value ? implementation::NUM_TYPE_UNSIGNED_INT : - IsFloat <Num>::value ? implementation::NUM_TYPE_FLOATING_POINT : - implementation::NUM_TYPE_OTHER> TypeTag; + using TypeTag = Int2Type< + IsSignedInt <Num>::value ? impl::NUM_TYPE_SIGNED_INT : + IsUnsignedInt<Num>::value ? impl::NUM_TYPE_UNSIGNED_INT : + IsFloat <Num>::value ? impl::NUM_TYPE_FLOATING_POINT : + impl::NUM_TYPE_OTHER>; - return implementation::numberTo<S>(number, TypeTag()); + return impl::numberTo<S>(number, TypeTag()); } template <class Num, class S> inline Num stringTo(const S& str) { - typedef Int2Type< - IsSignedInt <Num>::value ? implementation::NUM_TYPE_SIGNED_INT : - IsUnsignedInt<Num>::value ? implementation::NUM_TYPE_UNSIGNED_INT : - IsFloat <Num>::value ? implementation::NUM_TYPE_FLOATING_POINT : - implementation::NUM_TYPE_OTHER> TypeTag; + using TypeTag = Int2Type< + IsSignedInt <Num>::value ? impl::NUM_TYPE_SIGNED_INT : + IsUnsignedInt<Num>::value ? impl::NUM_TYPE_UNSIGNED_INT : + IsFloat <Num>::value ? impl::NUM_TYPE_FLOATING_POINT : + impl::NUM_TYPE_OTHER>; - return implementation::stringTo<Num>(str, TypeTag()); + return impl::stringTo<Num>(str, TypeTag()); } } -#endif //STRING_TOOLS_HEADER_213458973046 +#endif //STRING_TOOLS_H_213458973046 diff --git a/zen/string_traits.h b/zen/string_traits.h index 61fa2625..12701dc3 100644 --- a/zen/string_traits.h +++ b/zen/string_traits.h @@ -4,12 +4,13 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef STRING_TRAITS_HEADER_813274321443234 -#define STRING_TRAITS_HEADER_813274321443234 +#ifndef STRING_TRAITS_H_813274321443234 +#define STRING_TRAITS_H_813274321443234 #include <cstring> //strlen #include "type_tools.h" + //uniform access to string-like types, both classes and character arrays namespace zen { @@ -42,12 +43,12 @@ public: StringRef(Iterator first, Iterator last) : len_(last - first), str_(first != last ? &*first : nullptr) {} //StringRef(const Char* str, size_t len) : str_(str), len_(len) {} -> needless constraint! Char* not available for empty range! - const Char* data() const { return str_; } //1. no null-termination! 2. may be nullptr! + Char* data () const { return str_; } //1. no null-termination! 2. may be nullptr! size_t length() const { return len_; } private: - size_t len_; - const Char* str_; + const size_t len_; + Char* str_; }; @@ -67,16 +68,14 @@ namespace implementation template<class S, class Char> //test if result of S::c_str() can convert to const Char* class HasConversion { - typedef char Yes[1]; - typedef char No [2]; + using Yes = char[1]; + using No = char[2]; static Yes& hasConversion(const Char*); static No& hasConversion(...); - static S& createInstance(); - public: - enum { value = sizeof(hasConversion(createInstance().c_str())) == sizeof(Yes) }; + enum { value = sizeof(hasConversion(std::declval<S>().c_str())) == sizeof(Yes) }; }; @@ -89,7 +88,7 @@ struct GetCharTypeImpl<S, true> : typename SelectIf<HasConversion<S, char >::value, char, NullType>::Type >::Type> { - //typedef typename S::value_type Type; + //using Type = typename S::value_type; /*DON'T use S::value_type: 1. support Glib::ustring: value_type is "unsigned int" but c_str() returns "const char*" 2. wxString, wxWidgets v2.9, has some questionable string design: wxString::c_str() returns a proxy (wxCStrData) which @@ -100,8 +99,10 @@ struct GetCharTypeImpl<S, true> : template <> struct GetCharTypeImpl<char, false> : ResultType<char > {}; template <> struct GetCharTypeImpl<wchar_t, false> : ResultType<wchar_t> {}; -template <> struct GetCharTypeImpl<StringRef<char >, false> : ResultType<char > {}; -template <> struct GetCharTypeImpl<StringRef<wchar_t>, false> : ResultType<wchar_t> {}; +template <> struct GetCharTypeImpl<StringRef<char >, false> : ResultType<char > {}; +template <> struct GetCharTypeImpl<StringRef<wchar_t >, false> : ResultType<wchar_t> {}; +template <> struct GetCharTypeImpl<StringRef<const char >, false> : ResultType<char > {}; +template <> struct GetCharTypeImpl<StringRef<const wchar_t>, false> : ResultType<wchar_t> {}; ZEN_INIT_DETECT_MEMBER_TYPE(value_type); @@ -111,11 +112,11 @@ ZEN_INIT_DETECT_MEMBER(length); // template <class S> class StringTraits { - typedef typename RemoveRef <S >::Type NonRefType; - typedef typename RemoveConst <NonRefType >::Type NonConstType; - typedef typename RemoveArray <NonConstType>::Type NonArrayType; - typedef typename RemovePointer<NonArrayType>::Type NonPtrType; - typedef typename RemoveConst <NonPtrType >::Type UndecoratedType; //handle "const char* const" + using NonRefType = typename RemoveRef <S >::Type; + using NonConstType = typename RemoveConst <NonRefType >::Type; + using NonArrayType = typename RemoveArray <NonConstType>::Type; + using NonPtrType = typename RemovePointer<NonArrayType>::Type; + using UndecoratedType = typename RemoveConst <NonPtrType >::Type ; //handle "const char* const" public: enum @@ -125,7 +126,7 @@ public: HasMember_length <NonConstType>::value }; - typedef typename GetCharTypeImpl<UndecoratedType, isStringClass>::Type CharType; + using CharType = typename GetCharTypeImpl<UndecoratedType, isStringClass>::Type; enum { @@ -171,8 +172,11 @@ inline const char* strBegin(const char* str) { return str; } inline const wchar_t* strBegin(const wchar_t* str) { return str; } inline const char* strBegin(const char& ch) { return &ch; } inline const wchar_t* strBegin(const wchar_t& ch) { return &ch; } -inline const char* strBegin(const StringRef<char >& ref) { return ref.data(); } -inline const wchar_t* strBegin(const StringRef<wchar_t>& ref) { return ref.data(); } + +inline const char* strBegin(const StringRef<char >& ref) { return ref.data(); } +inline const wchar_t* strBegin(const StringRef<wchar_t >& ref) { return ref.data(); } +inline const char* strBegin(const StringRef<const char >& ref) { return ref.data(); } +inline const wchar_t* strBegin(const StringRef<const wchar_t>& ref) { return ref.data(); } template <class S, typename = typename EnableIf<implementation::StringTraits<S>::isStringClass>::Type> inline @@ -185,8 +189,11 @@ inline size_t strLength(const char* str) { return cStringLength(str); } inline size_t strLength(const wchar_t* str) { return cStringLength(str); } inline size_t strLength(char) { return 1; } inline size_t strLength(wchar_t) { return 1; } -inline size_t strLength(const StringRef<char >& ref) { return ref.length(); } -inline size_t strLength(const StringRef<wchar_t>& ref) { return ref.length(); } + +inline size_t strLength(const StringRef<char >& ref) { return ref.length(); } +inline size_t strLength(const StringRef<wchar_t >& ref) { return ref.length(); } +inline size_t strLength(const StringRef<const char >& ref) { return ref.length(); } +inline size_t strLength(const StringRef<const wchar_t>& ref) { return ref.length(); } } @@ -206,4 +213,4 @@ size_t strLength(S&& str) } } -#endif //STRING_TRAITS_HEADER_813274321443234 +#endif //STRING_TRAITS_H_813274321443234 diff --git a/zen/symlink_target.h b/zen/symlink_target.h index b5ca8191..f50d1806 100644 --- a/zen/symlink_target.h +++ b/zen/symlink_target.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef SYMLINK_80178347019835748321473214 -#define SYMLINK_80178347019835748321473214 +#ifndef SYMLINK_TARGET_H_801783470198357483 +#define SYMLINK_TARGET_H_801783470198357483 #include "scope_guard.h" #include "file_error.h" @@ -240,4 +240,4 @@ bool isSymlink(const WIN32_FIND_DATA& data) #endif } -#endif //SYMLINK_80178347019835748321473214 +#endif //SYMLINK_TARGET_H_801783470198357483 diff --git a/zen/thread.h b/zen/thread.h index b10dd342..bb6e7901 100644 --- a/zen/thread.h +++ b/zen/thread.h @@ -4,19 +4,23 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef STD_THREAD_WRAP_H_7896323423432 -#define STD_THREAD_WRAP_H_7896323423432 +#ifndef THREAD_H_7896323423432235246427 +#define THREAD_H_7896323423432235246427 #include <thread> #include <future> -#include <zen/scope_guard.h> -#include <zen/type_traits.h> +#include "scope_guard.h" +#include "type_traits.h" +#include "optional.h" +#ifdef ZEN_WIN +#include "win.h" +#endif + namespace zen { class InterruptionStatus; - class InterruptibleThread { public: @@ -58,6 +62,9 @@ void interruptibleWait(std::condition_variable& cv, std::unique_lock<std::mutex> template <class Rep, class Period> void interruptibleSleep(const std::chrono::duration<Rep, Period>& relTime); //throw ThreadInterruption +#ifdef ZEN_WIN +void setCurrentThreadName(const char* threadName); +#endif //------------------------------------------------------------------------------------------ /* @@ -72,7 +79,7 @@ Example: //dir exising */ template <class Function> -auto runAsync(Function&& fun) -> std::future<decltype(fun())>; +auto runAsync(Function&& fun); //wait for all with a time limit: return true if *all* results are available! template<class InputIterator, class Duration> @@ -90,18 +97,18 @@ public: GetFirstResult(); template <class Fun> - void addJob(Fun&& f); //f must return a std::unique_ptr<T> containing a value if successful + void addJob(Fun&& f); //f must return a zen::Opt<T> containing a value if successful template <class Duration> bool timedWait(const Duration& duration) const; //true: "get()" is ready, false: time elapsed //return first value or none if all jobs failed; blocks until result is ready! - std::unique_ptr<T> get() const; //may be called only once! + Opt<T> get() const; //may be called only once! private: class AsyncResult; std::shared_ptr<AsyncResult> asyncResult_; - size_t jobsTotal_; + size_t jobsTotal_ = 0; }; //------------------------------------------------------------------------------------------ @@ -111,7 +118,7 @@ template <class T> class Protected { public: - Protected() : value_() {} + Protected() {} Protected(const T& value) : value_(value) {} template <class Function> @@ -126,7 +133,7 @@ private: Protected& operator=(const Protected&) = delete; std::mutex lockValue; - T value_; + T value_{}; }; @@ -142,7 +149,7 @@ private: namespace impl { template <class Function> inline -auto runAsync(Function&& fun, TrueType /*copy-constructible*/) -> std::future<decltype(fun())> +auto runAsync(Function&& fun, TrueType /*copy-constructible*/) { typedef decltype(fun()) ResultType; @@ -155,17 +162,17 @@ auto runAsync(Function&& fun, TrueType /*copy-constructible*/) -> std::future<de template <class Function> inline -auto runAsync(Function&& fun, FalseType /*copy-constructible*/) -> std::future<decltype(fun())> +auto runAsync(Function&& fun, FalseType /*copy-constructible*/) { //support move-only function objects! auto sharedFun = std::make_shared<Function>(std::forward<Function>(fun)); - return runAsync([sharedFun]() { return (*sharedFun)(); }, TrueType()); + return runAsync([sharedFun] { return (*sharedFun)(); }, TrueType()); } } template <class Function> inline -auto runAsync(Function&& fun) -> std::future<decltype(fun())> +auto runAsync(Function&& fun) { return impl::runAsync(std::forward<Function>(fun), StaticBool<std::is_copy_constructible<Function>::value>()); } @@ -187,7 +194,7 @@ class GetFirstResult<T>::AsyncResult { public: //context: worker threads - void reportFinished(std::unique_ptr<T>&& result) + void reportFinished(Opt<T>&& result) { { std::lock_guard<std::mutex> dummy(lockResult); @@ -206,7 +213,7 @@ public: return conditionJobDone.wait_for(dummy, duration, [&] { return this->jobDone(jobsTotal); }); } - std::unique_ptr<T> getResult(size_t jobsTotal) + Opt<T> getResult(size_t jobsTotal) { std::unique_lock<std::mutex> dummy(lockResult); conditionJobDone.wait(dummy, [&] { return this->jobDone(jobsTotal); }); @@ -226,20 +233,20 @@ private: #endif std::mutex lockResult; - size_t jobsFinished = 0; // - std::unique_ptr<T> result_; //our condition is: "have result" or "jobsFinished == jobsTotal" + size_t jobsFinished = 0; // + Opt<T> result_; //our condition is: "have result" or "jobsFinished == jobsTotal" std::condition_variable conditionJobDone; }; template <class T> inline -GetFirstResult<T>::GetFirstResult() : asyncResult_(std::make_shared<AsyncResult>()), jobsTotal_(0) {} +GetFirstResult<T>::GetFirstResult() : asyncResult_(std::make_shared<AsyncResult>()) {} template <class T> template <class Fun> inline -void GetFirstResult<T>::addJob(Fun&& f) //f must return a std::unique_ptr<T> containing a value on success +void GetFirstResult<T>::addJob(Fun&& f) //f must return a zen::Opt<T> containing a value on success { std::thread t([asyncResult = this->asyncResult_, f = std::forward<Fun>(f)] { asyncResult->reportFinished(f()); }); ++jobsTotal_; @@ -253,7 +260,7 @@ bool GetFirstResult<T>::timedWait(const Duration& duration) const { return async template <class T> inline -std::unique_ptr<T> GetFirstResult<T>::get() const { return asyncResult_->getResult(jobsTotal_); } +Opt<T> GetFirstResult<T>::get() const { return asyncResult_->getResult(jobsTotal_); } //------------------------------------------------------------------------------------------ @@ -263,7 +270,7 @@ std::unique_ptr<T> GetFirstResult<T>::get() const { return asyncResult_->getResu #elif defined __GNUC__ || defined __clang__ #define ZEN_THREAD_LOCAL_SPECIFIER __thread #else - #error "game over" + #error "Game over!" #endif @@ -316,7 +323,7 @@ public: void interruptibleSleep(const std::chrono::duration<Rep, Period>& relTime) //throw ThreadInterruption { std::unique_lock<std::mutex> lock(lockSleep); - if (conditionSleepInterruption.wait_for(lock, relTime, [&] { return static_cast<bool>(this->interrupted); })) + if (conditionSleepInterruption.wait_for(lock, relTime, [this] { return static_cast<bool>(this->interrupted); })) throw ThreadInterruption(); } @@ -406,6 +413,39 @@ InterruptibleThread::InterruptibleThread(Function&& f) : intStatus_(std::make_sh inline void InterruptibleThread::interrupt() { intStatus_->interrupt(); } + + +#ifdef ZEN_WIN +//https://randomascii.wordpress.com/2015/10/26/thread-naming-in-windows-time-for-something-better/ + +#pragma pack(push,8) +struct THREADNAME_INFO +{ + DWORD dwType; // Must be 0x1000. + LPCSTR szName; // Pointer to name (in user addr space). + DWORD dwThreadID; // Thread ID (-1=caller thread). + DWORD dwFlags; // Reserved for future use, must be zero. +}; +#pragma pack(pop) + + +inline +void setCurrentThreadName(const char* threadName) +{ +const DWORD MS_VC_EXCEPTION = 0x406D1388; + +THREADNAME_INFO info = {}; + info.dwType = 0x1000; + info.szName = threadName; + info.dwThreadID = GetCurrentThreadId(); + + __try + { + ::RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), reinterpret_cast<ULONG_PTR*>(&info)); + } + __except(EXCEPTION_EXECUTE_HANDLER){} +} +#endif } -#endif //STD_THREAD_WRAP_H_7896323423432 +#endif //THREAD_H_7896323423432235246427 diff --git a/zen/tick_count.h b/zen/tick_count.h index b5667c6c..647876fb 100644 --- a/zen/tick_count.h +++ b/zen/tick_count.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef ZEN_TICK_COUNT_HEADER_3807326 -#define ZEN_TICK_COUNT_HEADER_3807326 +#ifndef TICK_COUNT_H_3807326223463457 +#define TICK_COUNT_H_3807326223463457 #include <cstdint> #include "type_traits.h" @@ -13,18 +13,12 @@ #ifdef ZEN_WIN #include "win.h" //includes "windows.h" - #elif defined ZEN_LINUX #include <time.h> //Posix ::clock_gettime() - #elif defined ZEN_MAC #include <mach/mach_time.h> #endif -//template <class T> inline -//T dist(T a, T b) -//{ -// return a > b ? a - b : b - a; -//} + namespace zen { @@ -55,7 +49,7 @@ public: typedef uint64_t NativeVal; #endif - TickVal() : val_() {} + TickVal() {} explicit TickVal(const NativeVal& val) : val_(val) {} inline friend @@ -92,7 +86,7 @@ public: bool isValid() const { return dist(*this, TickVal()) != 0; } private: - NativeVal val_; + NativeVal val_ {}; }; @@ -144,4 +138,4 @@ TickVal getTicks() //return !isValid() on error } } -#endif //ZEN_TICK_COUNT_HEADER_3807326 +#endif //TICK_COUNT_H_3807326223463457 @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef ZEN_TIME_HEADER_845709281432434 -#define ZEN_TIME_HEADER_845709281432434 +#ifndef TIME_H_8457092814324342453627 +#define TIME_H_8457092814324342453627 #include <ctime> #include "string_tools.h" @@ -13,16 +13,14 @@ namespace zen { -struct TimeComp //replaces "struct std::tm" and SYSTEMTIME +struct TimeComp //replaces std::tm and SYSTEMTIME { - TimeComp() : year(0), month(0), day(0), hour(0), minute(0), second(0) {} - - int year; // - - int month; //1-12 - int day; //1-31 - int hour; //0-23 - int minute; //0-59 - int second; //0-61 + int year = 0; // - + int month = 0; //1-12 + int day = 0; //1-31 + int hour = 0; //0-23 + int minute = 0; //0-59 + int second = 0; //0-60 (including leap second) }; TimeComp localTime (time_t utc = std::time(nullptr)); //convert time_t (UTC) to local time components @@ -32,9 +30,9 @@ time_t localToTimeT(const TimeComp& comp); //convert local time com /* format (current) date and time; example: - formatTime<std::wstring>(L"%Y*%m*%d"); -> "2011*10*29" - formatTime<std::wstring>(FORMAT_DATE); -> "2011-10-29" - formatTime<std::wstring>(FORMAT_TIME); -> "17:55:34" + formatTime<std::wstring>(L"%Y|%m|%d"); -> "2011|10|29" + formatTime<std::wstring>(FORMAT_DATE); -> "2011-10-29" + formatTime<std::wstring>(FORMAT_TIME); -> "17:55:34" */ template <class String, class String2> String formatTime(const String2& format, const TimeComp& comp = localTime()); //format as specified by "std::strftime", returns empty string on failure @@ -74,7 +72,7 @@ bool parseTime(const String& format, const String2& str, TimeComp& comp); //simi namespace implementation { inline -struct std::tm toClibTimeComponents(const TimeComp& comp) +std::tm toClibTimeComponents(const TimeComp& comp) { assert(1 <= comp.month && comp.month <= 12 && 1 <= comp.day && comp.day <= 31 && @@ -82,19 +80,21 @@ struct std::tm toClibTimeComponents(const TimeComp& comp) 0 <= comp.minute && comp.minute <= 59 && 0 <= comp.second && comp.second <= 61); - struct std::tm ctc = {}; + std::tm ctc = {}; ctc.tm_year = comp.year - 1900; //years since 1900 ctc.tm_mon = comp.month - 1; //0-11 ctc.tm_mday = comp.day; //1-31 ctc.tm_hour = comp.hour; //0-23 ctc.tm_min = comp.minute; //0-59 - ctc.tm_sec = comp.second; //0-61 + ctc.tm_sec = comp.second; //0-60 (including leap second) ctc.tm_isdst = -1; //> 0 if DST is active, == 0 if DST is not active, < 0 if the information is not available + //ctc.tm_wday + //ctc.tm_yday return ctc; } inline -TimeComp toZenTimeComponents(const struct ::tm& ctc) +TimeComp toZenTimeComponents(const std::tm& ctc) { TimeComp comp; comp.year = ctc.tm_year + 1900; @@ -157,21 +157,21 @@ struct GetFormat<FormatIsoDateTimeTag> //%Y-%m-%d %H:%M:%S - e.g. 2001-08-23 14: // VS 2010: CRASH unless "_invalid_parameter_handler" is set: http://msdn.microsoft.com/en-us/library/ksazx244.aspx // GCC: returns 0, apparently no crash. Still, considering some clib maintainer's comments, we should expect the worst! inline -size_t strftimeWrap_impl(char* buffer, size_t bufferSize, const char* format, const struct std::tm* timeptr) +size_t strftimeWrap_impl(char* buffer, size_t bufferSize, const char* format, const std::tm* timeptr) { return std::strftime(buffer, bufferSize, format, timeptr); } inline -size_t strftimeWrap_impl(wchar_t* buffer, size_t bufferSize, const wchar_t* format, const struct std::tm* timeptr) +size_t strftimeWrap_impl(wchar_t* buffer, size_t bufferSize, const wchar_t* format, const std::tm* timeptr) { return std::wcsftime(buffer, bufferSize, format, timeptr); } /* inline -bool isValid(const struct std::tm& t) +bool isValid(const std::tm& t) { -> not enough! MSCRT has different limits than the C standard which even seem to change with different versions: _VALIDATE_RETURN((( timeptr->tm_sec >=0 ) && ( timeptr->tm_sec <= 59 ) ), EINVAL, FALSE) @@ -194,7 +194,7 @@ bool isValid(const struct std::tm& t) */ template <class CharType> inline -size_t strftimeWrap(CharType* buffer, size_t bufferSize, const CharType* format, const struct std::tm* timeptr) +size_t strftimeWrap(CharType* buffer, size_t bufferSize, const CharType* format, const std::tm* timeptr) { #if defined _MSC_VER && !defined NDEBUG //there's no way around: application init must register an invalid parameter handler that does nothing !!! @@ -215,7 +215,7 @@ template <class String, class String2> inline String formatTime(const String2& format, const TimeComp& comp, UserDefinedFormatTag) //format as specified by "std::strftime", returns empty string on failure { typedef typename GetCharType<String>::Type CharType; - struct std::tm ctc = toClibTimeComponents(comp); + std::tm ctc = toClibTimeComponents(comp); std::mktime(&ctc); // unfortunately std::strftime() needs all elements of "struct tm" filled, e.g. tm_wday, tm_yday //note: although std::mktime() explicitly expects "local time", calculating weekday and day of year *should* be time-zone and DST independent @@ -236,10 +236,9 @@ String formatTime(FormatType, const TimeComp& comp, PredefinedFormatTag) inline TimeComp localTime(time_t utc) { - struct ::tm lt = {}; + std::tm lt = {}; - //use thread-safe variants of localtime()! -#ifdef ZEN_WIN +#ifdef ZEN_WIN //use thread-safe variants of std::localtime()! if (::localtime_s(<, &utc) != 0) #else if (::localtime_r(&utc, <) == nullptr) @@ -253,7 +252,7 @@ TimeComp localTime(time_t utc) inline time_t localToTimeT(const TimeComp& comp) //returns -1 on error { - struct std::tm ctc = implementation::toClibTimeComponents(comp); + std::tm ctc = implementation::toClibTimeComponents(comp); return std::mktime(&ctc); } @@ -279,36 +278,36 @@ bool parseTime(const String& format, const String2& str, TimeComp& comp) //retur typedef typename GetCharType<String>::Type CharType; static_assert(IsSameType<CharType, typename GetCharType<String2>::Type>::value, ""); - const CharType* iterFmt = strBegin(format); - const CharType* const fmtLast = iterFmt + strLength(format); + const CharType* itFmt = strBegin(format); + const CharType* const fmtLast = itFmt + strLength(format); - const CharType* iterStr = strBegin(str); - const CharType* const strLast = iterStr + strLength(str); + const CharType* itStr = strBegin(str); + const CharType* const strLast = itStr + strLength(str); auto extractNumber = [&](int& result, size_t digitCount) -> bool { - if (strLast - iterStr < makeSigned(digitCount)) + if (strLast - itStr < makeSigned(digitCount)) return false; - if (std::any_of(iterStr, iterStr + digitCount, [](CharType c) { return !isDigit(c); })) + if (std::any_of(itStr, itStr + digitCount, [](CharType c) { return !isDigit(c); })) return false; - result = zen::stringTo<int>(StringRef<CharType>(iterStr, iterStr + digitCount)); - iterStr += digitCount; + result = zen::stringTo<int>(StringRef<const CharType>(itStr, itStr + digitCount)); + itStr += digitCount; return true; }; - for (; iterFmt != fmtLast; ++iterFmt) + for (; itFmt != fmtLast; ++itFmt) { - const CharType fmt = *iterFmt; + const CharType fmt = *itFmt; if (fmt == '%') { - ++iterFmt; - if (iterFmt == fmtLast) + ++itFmt; + if (itFmt == fmtLast) return false; - switch (*iterFmt) + switch (*itFmt) { case 'Y': if (!extractNumber(comp.year, 4)) @@ -340,19 +339,19 @@ bool parseTime(const String& format, const String2& str, TimeComp& comp) //retur } else if (isWhiteSpace(fmt)) //single whitespace in format => skip 0..n whitespace chars { - while (iterStr != strLast && isWhiteSpace(*iterStr)) - ++iterStr; + while (itStr != strLast && isWhiteSpace(*itStr)) + ++itStr; } else { - if (iterStr == strLast || *iterStr != fmt) + if (itStr == strLast || *itStr != fmt) return false; - ++iterStr; + ++itStr; } } - return iterStr == strLast; + return itStr == strLast; } } -#endif //ZEN_TIME_HEADER_845709281432434 +#endif //TIME_H_8457092814324342453627 diff --git a/zen/type_tools.h b/zen/type_tools.h index 31384d4c..a1e628a1 100644 --- a/zen/type_tools.h +++ b/zen/type_tools.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef TYPE_TOOLS_HEADER_45237590734254545 -#define TYPE_TOOLS_HEADER_45237590734254545 +#ifndef TYPE_TOOLS_H_45237590734254545 +#define TYPE_TOOLS_H_45237590734254545 #include "type_traits.h" @@ -100,4 +100,4 @@ template <class Predicate> inline LessDescending<Predicate> makeDescending(Predicate pred) { return pred; } } -#endif //TYPE_TOOLS_HEADER_45237590734254545 +#endif //TYPE_TOOLS_H_45237590734254545 diff --git a/zen/type_traits.h b/zen/type_traits.h index 3ec90f49..ac7f1a42 100644 --- a/zen/type_traits.h +++ b/zen/type_traits.h @@ -4,11 +4,12 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef TYPE_TRAITS_HEADER_3425628658765467 -#define TYPE_TRAITS_HEADER_3425628658765467 +#ifndef TYPE_TRAITS_H_3425628658765467 +#define TYPE_TRAITS_H_3425628658765467 #include <type_traits> //all we need is std::is_class!! + namespace zen { //################# TMP compile time return values: "inherit to return compile-time result" ############## @@ -21,8 +22,8 @@ struct StaticInt template <bool b> struct StaticBool : StaticInt<b> {}; -typedef StaticBool<true> TrueType; -typedef StaticBool<false> FalseType; +using TrueType = StaticBool<true>; +using FalseType = StaticBool<false>; template <class EnumType, EnumType val> struct StaticEnum @@ -33,15 +34,15 @@ struct StaticEnum template <class T> struct ResultType { - typedef T Type; + using Type = T; }; //Herb Sutter's signedness conversion helpers: http://herbsutter.com/2013/06/13/gotw-93-solution-auto-variables-part-2/ template<class T> inline -typename std::make_signed<T>::type makeSigned(T t) { return static_cast<typename std::make_signed<T>::type>(t); } +typename std::make_signed<T>::type makeSigned(T t) { return static_cast<std::make_signed_t<T>>(t); } template<class T> inline -typename std::make_unsigned<T>::type makeUnsigned(T t) { return static_cast<typename std::make_unsigned<T>::type>(t); } +typename std::make_unsigned<T>::type makeUnsigned(T t) { return static_cast<std::make_unsigned_t<T>>(t); } //################# Built-in Types ######################## //Example: "IsSignedInt<int>::value" evaluates to "true" @@ -136,8 +137,8 @@ struct IsArithmetic : StaticBool<IsInteger<T>::value || IsFloat<T>::value> {}; struct HasMemberImpl_##NAME \ { \ private: \ - typedef char Yes[1]; \ - typedef char No [2]; \ + using Yes = char[1]; \ + using No = char[2]; \ \ template <typename U, U t> \ class Helper {}; \ @@ -165,8 +166,8 @@ struct IsArithmetic : StaticBool<IsInteger<T>::value || IsFloat<T>::value> {}; template<typename U> \ class HasMember_##NAME \ { \ - typedef char Yes[1]; \ - typedef char No [2]; \ + using Yes = char[1]; \ + using No = char[2]; \ \ template <typename T, T t> class Helper {}; \ \ @@ -182,8 +183,8 @@ struct IsArithmetic : StaticBool<IsInteger<T>::value || IsFloat<T>::value> {}; template<typename T> \ class HasMemberType_##TYPENAME \ { \ - typedef char Yes[1]; \ - typedef char No [2]; \ + using Yes = char[1]; \ + using No = char[2]; \ \ template <typename U> class Helper {}; \ \ @@ -194,4 +195,4 @@ struct IsArithmetic : StaticBool<IsInteger<T>::value || IsFloat<T>::value> {}; }; } -#endif //TYPE_TRAITS_HEADER_3425628658765467 +#endif //TYPE_TRAITS_H_3425628658765467 @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef STRING_UTF8_HEADER_01832479146991573473545 -#define STRING_UTF8_HEADER_01832479146991573473545 +#ifndef UTF_H_01832479146991573473545 +#define UTF_H_01832479146991573473545 #include <cstdint> #include <iterator> @@ -455,4 +455,4 @@ TargetString utfCvrtTo(const SourceString& str) } } -#endif //STRING_UTF8_HEADER_01832479146991573473545 +#endif //UTF_H_01832479146991573473545 diff --git a/zen/warn_static.h b/zen/warn_static.h index 3f268ec3..bba17cb7 100644 --- a/zen/warn_static.h +++ b/zen/warn_static.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef WARN_STATIC_HEADER_08724567834560832745 -#define WARN_STATIC_HEADER_08724567834560832745 +#ifndef WARN_STATIC_H_08724567834560832745 +#define WARN_STATIC_H_08724567834560832745 /* Portable Compile-Time Warning @@ -30,4 +30,4 @@ Usage: enum { STATIC_WARNING_CONCAT(warn_static_dummy_value, __LINE__) = sizeof(STATIC_WARNING_87903124) }; #endif -#endif //WARN_STATIC_HEADER_08724567834560832745 +#endif //WARN_STATIC_H_08724567834560832745 @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef YAWFWH_YET_ANOTHER_WRAPPER_FOR_WINDOWS_H -#define YAWFWH_YET_ANOTHER_WRAPPER_FOR_WINDOWS_H +#ifndef WIN_H_8701570183560183247891346363457 +#define WIN_H_8701570183560183247891346363457 #ifndef _WINSOCKAPI_ //prevent inclusion of winsock.h in windows.h: obsoleted by and conflicting with winsock2.h #define _WINSOCKAPI_ @@ -30,4 +30,4 @@ #endif //------------------------------------------------------ -#endif //YAWFWH_YET_ANOTHER_WRAPPER_FOR_WINDOWS_H +#endif //WIN_H_8701570183560183247891346363457 diff --git a/zen/xml_io.h b/zen/xml_io.h index 16e01aff..4876ac55 100644 --- a/zen/xml_io.h +++ b/zen/xml_io.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef XMLBASE_H_INCLUDED -#define XMLBASE_H_INCLUDED +#ifndef XML_IO_H_8914759321263879 +#define XML_IO_H_8914759321263879 #include <zenxml/xml.h> #include "file_error.h" @@ -23,4 +23,4 @@ void checkForMappingErrors(const XmlIn& xmlInput, const Zstring& filepath); //th void saveXmlDocument(const XmlDoc& doc, const Zstring& filepath); //throw FileError } -#endif // XMLBASE_H_INCLUDED +#endif //XML_IO_H_8914759321263879 diff --git a/zen/zstring.cpp b/zen/zstring.cpp index 3b29e664..59f15f19 100644 --- a/zen/zstring.cpp +++ b/zen/zstring.cpp @@ -6,14 +6,10 @@ #include "zstring.h" #include <stdexcept> -#include <unordered_map> #ifdef ZEN_WIN #include "dll.h" - #include "win_ver.h" - -#elif defined ZEN_MAC - #include <ctype.h> //toupper() + //#include "win_ver.h" #endif using namespace zen; @@ -52,7 +48,7 @@ const SysDllFun<CompareStringOrdinalFunc> compareStringOrdinal = SysDllFun<Compa } -int cmpFilePath(const Zchar* lhs, size_t lhsLen, const Zchar* rhs, size_t rhsLen) +int cmpStringNoCase(const wchar_t* lhs, size_t lhsLen, const wchar_t* rhs, size_t rhsLen) { assert(std::find(lhs, lhs + lhsLen, 0) == lhs + lhsLen); //don't expect embedded nulls! assert(std::find(rhs, rhs + rhsLen, 0) == rhs + rhsLen); // @@ -79,7 +75,7 @@ int cmpFilePath(const Zchar* lhs, size_t lhsLen, const Zchar* rhs, size_t rhsLen if (minSize == 0) //LCMapString does not allow input sizes of 0! return static_cast<int>(lhsLen) - static_cast<int>(rhsLen); - auto copyToUpperCase = [&](const wchar_t* strIn, wchar_t* strOut) + auto copyToUpperCase = [minSize](const wchar_t* strIn, wchar_t* strOut) { //faster than CharUpperBuff + wmemcpy or CharUpper + wmemcpy and same speed like ::CompareString() if (::LCMapString(LOCALE_INVARIANT, //__in LCID Locale, @@ -118,43 +114,18 @@ int cmpFilePath(const Zchar* lhs, size_t lhsLen, const Zchar* rhs, size_t rhsLen } -Zstring makeUpperCopy(const Zstring& str) -{ - const int len = static_cast<int>(str.size()); - - if (len == 0) //LCMapString does not allow input sizes of 0! - return str; - - Zstring output = str; - - //LOCALE_INVARIANT is NOT available with Windows 2000 -> ok - - //use Windows' upper case conversion: faster than ::CharUpper() - if (::LCMapString(LOCALE_INVARIANT, //__in LCID Locale, - LCMAP_UPPERCASE, //__in DWORD dwMapFlags, - str.c_str(), //__in LPCTSTR lpSrcStr, - len, //__in int cchSrc, - &*output.begin(), //__out LPTSTR lpDestStr, - len) == 0) //__in int cchDest - throw std::runtime_error("Error comparing strings (LCMapString). " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__)); - - return output; -} - - -#elif defined ZEN_MAC -Zstring makeUpperCopy(const Zstring& str) +void makeUpperInPlace(wchar_t* str, size_t strLen) { - const size_t len = str.size(); - - Zstring output; - output.resize(len); - - std::transform(str.begin(), str.end(), output.begin(), [](char c) { return static_cast<char>(::toupper(static_cast<unsigned char>(c))); }); //locale-dependent! - - //result of toupper() is an unsigned char mapped to int range, so the char representation is in the last 8 bits and we need not care about signedness! - //this should work for UTF-8, too: all chars >= 128 are mapped upon themselves! - - return output; + //- use Windows' upper case conversion: faster than ::CharUpper() + //- LOCALE_INVARIANT is NOT available with Windows 2000 -> ok + //- MSDN: "The destination string can be the same as the source string only if LCMAP_UPPERCASE or LCMAP_LOWERCASE is set" + if (strLen != 0) //LCMapString does not allow input sizes of 0! + if (::LCMapString(LOCALE_INVARIANT, //__in LCID Locale, + LCMAP_UPPERCASE, //__in DWORD dwMapFlags, + str, //__in LPCTSTR lpSrcStr, + static_cast<int>(strLen), //__in int cchSrc, + str, //__out LPTSTR lpDestStr, + static_cast<int>(strLen)) == 0) //__in int cchDest + throw std::runtime_error("Error comparing strings (LCMapString). " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__)); } #endif diff --git a/zen/zstring.h b/zen/zstring.h index 462ea39c..3a431ea7 100644 --- a/zen/zstring.h +++ b/zen/zstring.h @@ -4,15 +4,11 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef ZSTRING_H_INCLUDED_73425873425789 -#define ZSTRING_H_INCLUDED_73425873425789 +#ifndef ZSTRING_H_73425873425789 +#define ZSTRING_H_73425873425789 #include "string_base.h" -#ifdef ZEN_LINUX - #include <cstring> //strncmp -#endif - #ifdef ZEN_WIN //Windows encodes Unicode as UTF-16 wchar_t typedef wchar_t Zchar; @@ -30,33 +26,33 @@ typedef zen::Zbase<Zchar, zen::StorageRefCountThreadSafe, zen::AllocatorOptimalSpeed> Zstring; +int cmpStringNoCase(const wchar_t* lhs, size_t lhsLen, const wchar_t* rhs, size_t rhsLen); +#if defined ZEN_LINUX || defined ZEN_MAC + int cmpStringNoCase(const char* lhs, size_t lhsLen, const char* rhs, size_t rhsLen); +#endif + +template <class S, class T> inline +bool equalNoCase(const S& lhs, const T& rhs) { using namespace zen; return cmpStringNoCase(strBegin(lhs), strLength(lhs), strBegin(rhs), strLength(rhs)) == 0; } + +template <class S> +S makeUpperCopy(S str); -//Compare filepaths: Windows does NOT distinguish between upper/lower-case, while Linux DOES -int cmpFilePath(const wchar_t* lhs, size_t lhsLen, const wchar_t* rhs, size_t rhsLen); +//Compare filepaths: Windows/OS X does NOT distinguish between upper/lower-case, while Linux DOES +int cmpFilePath(const wchar_t* lhs, size_t lhsLen, const wchar_t* rhs, size_t rhsLen); #if defined ZEN_LINUX || defined ZEN_MAC int cmpFilePath(const char* lhs, size_t lhsLen, const char* rhs, size_t rhsLen); #endif +template <class S, class T> inline +bool equalFilePath(const S& lhs, const T& rhs) { using namespace zen; return cmpFilePath(strBegin(lhs), strLength(lhs), strBegin(rhs), strLength(rhs)) == 0; } - - -struct LessFilePath //case-insensitive on Windows, case-sensitive on Linux +struct LessFilePath { template <class S, class T> bool operator()(const S& lhs, const T& rhs) const { using namespace zen; return cmpFilePath(strBegin(lhs), strLength(lhs), strBegin(rhs), strLength(rhs)) < 0; } }; -struct EqualFilePath //case-insensitive on Windows, case-sensitive on Linux -{ - template <class S, class T> - bool operator()(const S& lhs, const T& rhs) const { using namespace zen; return cmpFilePath(strBegin(lhs), strLength(lhs), strBegin(rhs), strLength(rhs)) == 0; } -}; - - -#if defined ZEN_WIN || defined ZEN_MAC - Zstring makeUpperCopy(const Zstring& str); -#endif inline @@ -103,38 +99,98 @@ bool pathEndsWith(const S& str, const T& postfix) //################################# inline implementation ######################################## +#ifdef ZEN_WIN +void makeUpperInPlace(wchar_t* str, size_t strLen); -#if defined ZEN_LINUX || defined ZEN_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC inline -int cmpFilePath(const char* lhs, size_t lhsLen, const char* rhs, size_t rhsLen) +void makeUpperInPlace(wchar_t* str, size_t strLen) +{ + std::for_each(str, str + strLen, [](wchar_t& c) { c = std::towupper(c); }); //locale-dependent! +} + + +inline +void makeUpperInPlace(char* str, size_t strLen) +{ + std::for_each(str, str + strLen, [](char& c) { c = std::toupper(static_cast<unsigned char>(c)); }); //locale-dependent! + //result of toupper() is an unsigned char mapped to int range, so the char representation is in the last 8 bits and we need not care about signedness! + //this should work for UTF-8, too: all chars >= 128 are mapped upon themselves! +} + + +inline +int cmpStringNoCase(const wchar_t* lhs, size_t lhsLen, const wchar_t* rhs, size_t rhsLen) +{ + assert(std::find(lhs, lhs + lhsLen, 0) == lhs + lhsLen); //don't expect embedded nulls! + assert(std::find(rhs, rhs + rhsLen, 0) == rhs + rhsLen); // + + const int rv = ::wcsncasecmp(lhs, rhs, std::min(lhsLen, rhsLen)); //locale-dependent! + if (rv != 0) + return rv; + return static_cast<int>(lhsLen) - static_cast<int>(rhsLen); +} + + +inline +int cmpStringNoCase(const char* lhs, size_t lhsLen, const char* rhs, size_t rhsLen) { assert(std::find(lhs, lhs + lhsLen, 0) == lhs + lhsLen); //don't expect embedded nulls! assert(std::find(rhs, rhs + rhsLen, 0) == rhs + rhsLen); // -#if defined ZEN_LINUX - const int rv = std::strncmp(lhs, rhs, std::min(lhsLen, rhsLen)); -#elif defined ZEN_MAC const int rv = ::strncasecmp(lhs, rhs, std::min(lhsLen, rhsLen)); //locale-dependent! -#endif if (rv != 0) return rv; return static_cast<int>(lhsLen) - static_cast<int>(rhsLen); } +#endif + + +template <class S> inline +S makeUpperCopy(S str) +{ + const size_t len = str.length(); //we assert S is a string type! + if (len > 0) + makeUpperInPlace(&*str.begin(), len); + + return std::move(str); //"str" is an l-value parameter => no copy elision! +} + inline int cmpFilePath(const wchar_t* lhs, size_t lhsLen, const wchar_t* rhs, size_t rhsLen) { +#if defined ZEN_WIN || defined ZEN_MAC + return cmpStringNoCase(lhs, lhsLen, rhs, rhsLen); + +#elif defined ZEN_LINUX assert(std::find(lhs, lhs + lhsLen, 0) == lhs + lhsLen); //don't expect embedded nulls! assert(std::find(rhs, rhs + rhsLen, 0) == rhs + rhsLen); // -#if defined ZEN_LINUX const int rv = std::wcsncmp(lhs, rhs, std::min(lhsLen, rhsLen)); -#elif defined ZEN_MAC - const int rv = ::wcsncasecmp(lhs, rhs, std::min(lhsLen, rhsLen)); //locale-dependent! + if (rv != 0) + return rv; + return static_cast<int>(lhsLen) - static_cast<int>(rhsLen); #endif +} + + +#if defined ZEN_LINUX || defined ZEN_MAC +inline +int cmpFilePath(const char* lhs, size_t lhsLen, const char* rhs, size_t rhsLen) +{ +#if defined ZEN_MAC + return cmpStringNoCase(lhs, lhsLen, rhs, rhsLen); + +#elif defined ZEN_LINUX + assert(std::find(lhs, lhs + lhsLen, 0) == lhs + lhsLen); //don't expect embedded nulls! + assert(std::find(rhs, rhs + rhsLen, 0) == rhs + rhsLen); // + + const int rv = std::strncmp(lhs, rhs, std::min(lhsLen, rhsLen)); if (rv != 0) return rv; return static_cast<int>(lhsLen) - static_cast<int>(rhsLen); +#endif } #endif @@ -172,4 +228,4 @@ int cmpFilePath(const wchar_t* lhs, size_t lhsLen, const wchar_t* rhs, size_t rh #error no target platform defined #endif -#endif //ZSTRING_H_INCLUDED_73425873425789 +#endif //ZSTRING_H_73425873425789 |